Skip to content

Commit 1d5ac53

Browse files
#605 add secondary ebay catehory logic
1 parent eb052df commit 1d5ac53

File tree

12 files changed

+802
-290
lines changed

12 files changed

+802
-290
lines changed

src/core/products/products/product-show/containers/tabs/ebay/EbayView.vue

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,12 @@ const loadingViews = ref(false);
2222
const views = ref<any[]>([]);
2323
const selectedViewId = ref<string | null>(null);
2424
const categoriesByView = ref<
25-
Record<string, { id: string | null; remoteId: string | null; salesChannelId: string | null }>
25+
Record<string, {
26+
id: string | null;
27+
remoteId: string | null;
28+
secondaryCategoryId: string | null;
29+
salesChannelId: string | null;
30+
}>
2631
>({});
2732
const categorySectionRef = ref<InstanceType<typeof EbayCategorySection> | null>(null);
2833
const defaultCategoriesByView = ref<
@@ -64,7 +69,12 @@ const fetchEbayProductCategories = async (fetchPolicy: FetchPolicy = 'cache-firs
6469
},
6570
fetchPolicy,
6671
});
67-
const map: Record<string, { id: string | null; remoteId: string | null; salesChannelId: string | null }> = {};
72+
const map: Record<string, {
73+
id: string | null;
74+
remoteId: string | null;
75+
secondaryCategoryId: string | null;
76+
salesChannelId: string | null;
77+
}> = {};
6878
const edges = data?.ebayProductCategories?.edges || [];
6979
edges.forEach((edge: any) => {
7080
const node = edge?.node;
@@ -73,6 +83,7 @@ const fetchEbayProductCategories = async (fetchPolicy: FetchPolicy = 'cache-firs
7383
map[viewId] = {
7484
id: node.id,
7585
remoteId: node.remoteId,
86+
secondaryCategoryId: node.secondaryCategoryId || null,
7687
salesChannelId: node.salesChannel?.id || null,
7788
};
7889
}
@@ -196,14 +207,15 @@ const selectedDefaultCategory = computed(() => {
196207
});
197208
198209
const handleCategorySaved = (
199-
payload: { id: string; remoteId: string; salesChannelId: string | null }
210+
payload: { id: string; remoteId: string; secondaryCategoryId: string | null; salesChannelId: string | null }
200211
) => {
201212
if (!selectedView.value) return;
202213
categoriesByView.value = {
203214
...categoriesByView.value,
204215
[selectedView.value.id]: {
205216
id: payload.id,
206217
remoteId: payload.remoteId,
218+
secondaryCategoryId: payload.secondaryCategoryId,
207219
salesChannelId: payload.salesChannelId || selectedView.value.salesChannel?.id || null,
208220
},
209221
};
@@ -213,7 +225,12 @@ const handleCategoryDeleted = () => {
213225
if (!selectedView.value) return;
214226
categoriesByView.value = {
215227
...categoriesByView.value,
216-
[selectedView.value.id]: { id: null, remoteId: null, salesChannelId: null },
228+
[selectedView.value.id]: {
229+
id: null,
230+
remoteId: null,
231+
secondaryCategoryId: null,
232+
salesChannelId: null,
233+
},
217234
};
218235
};
219236
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
<script setup lang="ts">
2+
import { computed } from 'vue';
3+
import { useI18n } from 'vue-i18n';
4+
import { Icon } from '../../../../../../../../shared/components/atoms/icon';
5+
import type { EbayCategoryNode, EbayCategoryTarget } from './ebayCategoryUtils';
6+
7+
const props = withDefaults(defineProps<{
8+
mainCategory: EbayCategoryNode | null;
9+
secondaryCategory: EbayCategoryNode | null;
10+
previousMainCategory?: EbayCategoryNode | null;
11+
previousSecondaryCategory?: EbayCategoryNode | null;
12+
selectedTarget: EbayCategoryTarget;
13+
secondaryDisabled: boolean;
14+
mainError?: string | null;
15+
secondaryError?: string | null;
16+
defaultCategory?: { remoteId: string | null; name: string | null } | null;
17+
readOnly?: boolean;
18+
}>(), {
19+
previousMainCategory: null,
20+
previousSecondaryCategory: null,
21+
mainError: null,
22+
secondaryError: null,
23+
defaultCategory: null,
24+
readOnly: false,
25+
});
26+
27+
const emit = defineEmits<{
28+
(e: 'target-change', target: EbayCategoryTarget): void;
29+
}>();
30+
31+
const { t } = useI18n();
32+
33+
const mainDisplay = computed(() => {
34+
if (props.mainCategory) {
35+
return {
36+
title: props.mainCategory.fullName || props.mainCategory.name || props.mainCategory.remoteId,
37+
remoteId: props.mainCategory.remoteId,
38+
isDefault: false,
39+
};
40+
}
41+
if (props.defaultCategory?.remoteId) {
42+
return {
43+
title: props.defaultCategory.name || props.defaultCategory.remoteId,
44+
remoteId: props.defaultCategory.remoteId,
45+
isDefault: true,
46+
};
47+
}
48+
return null;
49+
});
50+
51+
const selectTarget = (target: EbayCategoryTarget) => {
52+
if (props.readOnly) return;
53+
if (target === 'secondary' && props.secondaryDisabled) return;
54+
emit('target-change', target);
55+
};
56+
57+
const getCategoryLabel = (category: EbayCategoryNode | null | undefined) => {
58+
if (!category?.remoteId) {
59+
return t('products.products.ebay.noSelection');
60+
}
61+
return category.fullName || category.name || category.remoteId;
62+
};
63+
64+
const hasMainChanged = computed(
65+
() => (props.previousMainCategory?.remoteId || null) !== (props.mainCategory?.remoteId || null),
66+
);
67+
68+
const hasSecondaryChanged = computed(
69+
() => (props.previousSecondaryCategory?.remoteId || null) !== (props.secondaryCategory?.remoteId || null),
70+
);
71+
72+
const hasChanges = computed(
73+
() => hasMainChanged.value || hasSecondaryChanged.value,
74+
);
75+
76+
const getChangeText = (target: EbayCategoryTarget) => {
77+
const previous = target === 'main' ? props.previousMainCategory : props.previousSecondaryCategory;
78+
const current = target === 'main' ? props.mainCategory : props.secondaryCategory;
79+
return t('products.products.ebay.selectionSlots.changePattern', {
80+
from: getCategoryLabel(previous),
81+
to: getCategoryLabel(current),
82+
});
83+
};
84+
85+
const getCardClass = (target: EbayCategoryTarget) => {
86+
const isSelected = props.selectedTarget === target;
87+
const isDisabled = target === 'secondary' && props.secondaryDisabled;
88+
const hasError = target === 'main' ? Boolean(props.mainError) : Boolean(props.secondaryError);
89+
return [
90+
'rounded-lg border border-dashed p-3 transition-colors',
91+
hasError ? 'border-danger bg-danger-light/20' : (isSelected ? 'border-blue-500 bg-blue-50' : 'border-gray-300 bg-gray-50'),
92+
!props.readOnly && !isDisabled ? 'cursor-pointer hover:border-blue-400' : '',
93+
isDisabled ? 'opacity-60 cursor-not-allowed' : '',
94+
hasError ? 'blink-animation' : '',
95+
];
96+
};
97+
</script>
98+
99+
<template>
100+
<div class="space-y-3">
101+
<p v-if="!readOnly" class="text-xs text-gray-500">
102+
{{ t('products.products.ebay.selectionSlots.targetHint') }}
103+
</p>
104+
105+
<div
106+
:class="getCardClass('main')"
107+
role="button"
108+
tabindex="0"
109+
@click="selectTarget('main')"
110+
@keyup.enter="selectTarget('main')"
111+
@keyup.space.prevent="selectTarget('main')"
112+
>
113+
<div class="flex items-center justify-between gap-2">
114+
<h6 class="font-semibold text-sm text-gray-800">
115+
{{ t('products.products.ebay.selectionSlots.mainTitle') }}
116+
</h6>
117+
<Icon
118+
v-if="selectedTarget === 'main'"
119+
name="check-circle"
120+
class="w-4 h-4 text-blue-500"
121+
/>
122+
</div>
123+
124+
<div v-if="mainDisplay" class="mt-2">
125+
<div class="text-sm text-gray-900">
126+
{{ mainDisplay.title }}
127+
</div>
128+
<div class="text-xs text-gray-500">
129+
{{ t('products.products.ebay.defaultCategoryInfo', { id: mainDisplay.remoteId }) }}
130+
</div>
131+
<div v-if="mainDisplay.isDefault" class="text-xs text-amber-700 mt-1">
132+
{{ t('products.products.ebay.defaultCategoryTitle') }}
133+
</div>
134+
</div>
135+
<div v-else class="mt-2 text-sm text-gray-500">
136+
{{ t('products.products.ebay.noSelection') }}
137+
</div>
138+
<div v-if="mainError" class="mt-2 text-danger text-small blink-animation">
139+
{{ mainError }}
140+
</div>
141+
</div>
142+
143+
<div
144+
:class="getCardClass('secondary')"
145+
role="button"
146+
tabindex="0"
147+
@click="selectTarget('secondary')"
148+
@keyup.enter="selectTarget('secondary')"
149+
@keyup.space.prevent="selectTarget('secondary')"
150+
>
151+
<div class="flex items-center justify-between gap-2">
152+
<h6 class="font-semibold text-sm text-gray-800">
153+
{{ t('products.products.ebay.selectionSlots.secondaryTitle') }}
154+
</h6>
155+
<Icon
156+
v-if="selectedTarget === 'secondary'"
157+
name="check-circle"
158+
class="w-4 h-4 text-blue-500"
159+
/>
160+
</div>
161+
162+
<div v-if="secondaryCategory" class="mt-2">
163+
<div class="text-sm text-gray-900">
164+
{{ secondaryCategory.fullName || secondaryCategory.name || secondaryCategory.remoteId }}
165+
</div>
166+
<div class="text-xs text-gray-500">
167+
{{ t('products.products.ebay.defaultCategoryInfo', { id: secondaryCategory.remoteId }) }}
168+
</div>
169+
</div>
170+
<div v-else class="mt-2 text-sm text-gray-500">
171+
{{ t('products.products.ebay.noSelection') }}
172+
</div>
173+
174+
<div v-if="secondaryDisabled" class="mt-2 text-xs text-gray-500">
175+
{{ t('products.products.ebay.selectionSlots.secondaryDisabled') }}
176+
</div>
177+
<div v-if="secondaryError" class="mt-2 text-danger text-small blink-animation">
178+
{{ secondaryError }}
179+
</div>
180+
</div>
181+
182+
<div v-if="!readOnly && hasChanges" class="rounded border border-gray-200 bg-white p-3">
183+
<div class="text-xs font-semibold text-gray-700 mb-2">
184+
{{ t('products.products.ebay.selectionSlots.changeTitle') }}
185+
</div>
186+
<div v-if="hasMainChanged" class="text-xs text-gray-700">
187+
<span class="font-semibold">{{ t('products.products.ebay.selectionSlots.mainTitle') }}:</span>
188+
<span class="ml-1">{{ getChangeText('main') }}</span>
189+
</div>
190+
<div v-if="hasSecondaryChanged" class="text-xs text-gray-700 mt-1">
191+
<span class="font-semibold">{{ t('products.products.ebay.selectionSlots.secondaryTitle') }}:</span>
192+
<span class="ml-1">{{ getChangeText('secondary') }}</span>
193+
</div>
194+
</div>
195+
</div>
196+
</template>
197+
198+
<style scoped>
199+
.blink-animation {
200+
animation: blink-border 0.9s step-start infinite;
201+
}
202+
203+
@keyframes blink-border {
204+
50% {
205+
opacity: 0.45;
206+
}
207+
}
208+
</style>

0 commit comments

Comments
 (0)