@deenruv/admin-dashboard 1.0.15 → 1.0.17-dev.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/DeenruvAdminPanel.js +4 -4
- package/dist/components/GlobalSearch.js +5 -5
- package/dist/components/Menu/ActiveAdmins.js +1 -1
- package/dist/components/Menu/ChannelSwitcher.js +1 -1
- package/dist/components/Menu/LanguagesDropdown.js +1 -1
- package/dist/components/Menu/Navigation.js +4 -4
- package/dist/components/Menu/NavigationFooter.js +2 -2
- package/dist/components/Menu/Notifications.js +1 -1
- package/dist/components/Menu/index.js +12 -14
- package/dist/index.css +54 -44
- package/dist/locales/en/collections.json +7 -0
- package/dist/locales/en/common.json +1 -0
- package/dist/locales/en/orders.json +8 -0
- package/dist/locales/en/products.json +14 -2
- package/dist/locales/en/promotions.json +4 -0
- package/dist/locales/pl/collections.json +7 -0
- package/dist/locales/pl/common.json +1 -0
- package/dist/locales/pl/orders.json +8 -0
- package/dist/locales/pl/products.json +14 -2
- package/dist/locales/pl/promotions.json +4 -0
- package/dist/pages/Root.js +1 -1
- package/dist/pages/collections/Detail.js +20 -5
- package/dist/pages/collections/_components/CollectionDetailView.js +15 -2
- package/dist/pages/collections/_components/FiltersCard.js +51 -11
- package/dist/pages/customers/_components/PersonalDataCard.js +2 -1
- package/dist/pages/extensions/Extensions.js +3 -7
- package/dist/pages/orders/Detail.js +38 -5
- package/dist/pages/orders/List.js +1 -16
- package/dist/pages/orders/_components/CancelAndRefundDialog.js +1 -2
- package/dist/pages/orders/_components/OrderLineCustomFields.js +1 -1
- package/dist/pages/orders/_components/ProductsTable.js +1 -2
- package/dist/pages/orders/_components/RefundPaymentCard.js +1 -2
- package/dist/pages/products/Detail.js +58 -4
- package/dist/pages/products/_components/AssetsCard.js +15 -2
- package/dist/pages/products/_components/OptionsCard.js +7 -5
- package/dist/pages/products/_components/PriceCard.js +21 -8
- package/dist/pages/products/_components/ProductDetailView.js +7 -3
- package/dist/pages/products/_components/Variant.js +40 -19
- package/dist/pages/products/_components/VariantsTab.js +1 -1
- package/dist/pages/promotions/Detail.js +34 -3
- package/dist/pages/promotions/_components/EnabledCard.js +2 -2
- package/dist/pages/promotions/_components/PromotionDetailSidebar.js +2 -1
- package/dist/pages/roles/_components/PermissionsTable.js +0 -1
- package/dist/pages/shipping-methods/Detail.js +9 -5
- package/dist/pages/shipping-methods/_components/CalculatorCard.js +27 -11
- package/dist/pages/shipping-methods/_components/CheckerCard.js +28 -12
- package/dist/version.js +1 -1
- package/package.json +68 -68
|
@@ -42,6 +42,14 @@
|
|
|
42
42
|
"removeVariant": "Usuń wariant",
|
|
43
43
|
"removeVariantFromChannel": "Usuń wariant z kanału"
|
|
44
44
|
},
|
|
45
|
+
"initialVariant": {
|
|
46
|
+
"title": "Początkowy wariant",
|
|
47
|
+
"description": "Utwórz jeden domyślny wariant razem z tym produktem. Jeśli nazwa wariantu jest pusta, zostanie użyta nazwa produktu.",
|
|
48
|
+
"sku": "SKU",
|
|
49
|
+
"price": "Cena",
|
|
50
|
+
"name": "Nazwa wariantu",
|
|
51
|
+
"namePlaceholder": "Użyj nazwy produktu"
|
|
52
|
+
},
|
|
45
53
|
"optionsTab": {
|
|
46
54
|
"placeholder": "Wpisz, aby dodać nową wartość opcji",
|
|
47
55
|
"groupName": "Nazwa grupy",
|
|
@@ -53,6 +61,7 @@
|
|
|
53
61
|
"noOptionValues": "Dodaj opcje powyżej, aby znaleźć je tutaj"
|
|
54
62
|
},
|
|
55
63
|
"variantsTab": {
|
|
64
|
+
"unnamedVariant": "Nienazwany wariant",
|
|
56
65
|
"emptyState": {
|
|
57
66
|
"title": "Nic tutaj nie ma",
|
|
58
67
|
"description": "Nie ma jeszcze żadnych wariantów. Użyj przycisku powyżej, aby je dodać, pod warunkiem, że dodałeś już jakieś opcje. Jeśli nie, utwórz je najpierw w zakładce Opcje."
|
|
@@ -128,6 +137,7 @@
|
|
|
128
137
|
"noAssets": "Nie ma jeszcze żadnych aktywów",
|
|
129
138
|
"setAsFeatured": "Ustaw jako polecane",
|
|
130
139
|
"editAsset": "Edytuj zdjęcie",
|
|
140
|
+
"uploadAsset": "Prześlij obraz",
|
|
131
141
|
"removeAsset": "Usuń zasób",
|
|
132
142
|
"usedForColorsDesc": "Użyj tego atrybutu dla facetów, którzy są kolorami",
|
|
133
143
|
"colorsCollectionDesc": "Użyj tego dla ścieżek produktu w KOLORZE (BLAT/CORPUS)",
|
|
@@ -186,9 +196,11 @@
|
|
|
186
196
|
"new": "Nowy wariant",
|
|
187
197
|
"selectOption": "Wybierz opcję",
|
|
188
198
|
"createBasic": "Dane podstawowe",
|
|
189
|
-
"createOptions": "Opcje"
|
|
199
|
+
"createOptions": "Opcje",
|
|
200
|
+
"createOptionsHint": "Dodaj grupę opcji w tym panelu, aby rozpocząć wybieranie opcji wariantu. Wartości opcji możesz później dopracować w zakładce Opcje."
|
|
190
201
|
},
|
|
191
202
|
"validation": {
|
|
192
|
-
"nameSlugRequired": "Pola nazwy i slug są wymagane"
|
|
203
|
+
"nameSlugRequired": "Pola nazwy i slug są wymagane",
|
|
204
|
+
"initialVariantSkuRequired": "SKU wariantu jest wymagane"
|
|
193
205
|
}
|
|
194
206
|
}
|
package/dist/pages/Root.js
CHANGED
|
@@ -248,5 +248,5 @@ export const Root = ({ allPaths }) => {
|
|
|
248
248
|
await init();
|
|
249
249
|
});
|
|
250
250
|
}, []);
|
|
251
|
-
return (_jsx(_Fragment, { children: _jsxs("div", { className: "flex max-h-screen w-full max-w-full overflow-hidden bg-background text-foreground", children: [_jsx(Menu, { children: loaded ? _jsx(Outlet, {}) : _jsx(ContentAreaSkeleton, {}) }), loaded && (_jsxs(_Fragment, { children: [_jsx(GlobalSearch, {}), _jsx(CanLeaveRouteDialog, {}), isLocalhost ? _jsx(DeenruvDeveloperIndicator, {}) : null] }))] }) }));
|
|
251
|
+
return (_jsx(_Fragment, { children: _jsxs("div", { className: "flex max-h-screen w-full max-w-full overflow-hidden bg-background text-foreground antialiased", children: [_jsx(Menu, { children: loaded ? _jsx(Outlet, {}) : _jsx(ContentAreaSkeleton, {}) }), loaded && (_jsxs(_Fragment, { children: [_jsx(GlobalSearch, {}), _jsx(CanLeaveRouteDialog, {}), isLocalhost ? _jsx(DeenruvDeveloperIndicator, {}) : null] }))] }) }));
|
|
252
252
|
};
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback } from 'react';
|
|
3
3
|
import { useParams } from 'react-router';
|
|
4
|
-
import { useValidators, DetailView, createDeenruvForm, getMutation, useMutation } from '@deenruv/react-ui-devkit';
|
|
4
|
+
import { useValidators, DetailView, createDeenruvForm, getMutation, useMutation, useSettings, } from '@deenruv/react-ui-devkit';
|
|
5
5
|
import { CollectionsDetailView } from "./_components/CollectionDetailView.js";
|
|
6
6
|
const CreateCollectionMutation = getMutation('createCollection');
|
|
7
7
|
const EditCollectionMutation = getMutation('updateCollection');
|
|
8
8
|
const DeleteCollectionMutation = getMutation('deleteCollection');
|
|
9
|
+
const AssignCollectionsToChannelMutation = getMutation('assignCollectionsToChannel');
|
|
9
10
|
export const CollectionsDetailPage = () => {
|
|
10
11
|
const { id } = useParams();
|
|
11
12
|
const [update] = useMutation(EditCollectionMutation);
|
|
12
13
|
const [create] = useMutation(CreateCollectionMutation);
|
|
13
14
|
const [remove] = useMutation(DeleteCollectionMutation);
|
|
15
|
+
const [assignCollectionsToChannel] = useMutation(AssignCollectionsToChannelMutation);
|
|
16
|
+
const selectedChannel = useSettings((p) => p.selectedChannel);
|
|
14
17
|
const { configurableOperationArrayValidator, translationsValidator } = useValidators();
|
|
15
|
-
const onSubmitHandler = useCallback((data) => {
|
|
18
|
+
const onSubmitHandler = useCallback(async (data) => {
|
|
16
19
|
const translations = data.translations;
|
|
17
20
|
const inputData = {
|
|
18
21
|
assetIds: data.assetIds,
|
|
@@ -29,9 +32,16 @@ export const CollectionsDetailPage = () => {
|
|
|
29
32
|
};
|
|
30
33
|
if (id)
|
|
31
34
|
return update({ input: { id, ...inputData } });
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
const response = await create({ input: inputData });
|
|
36
|
+
const collectionId = response.createCollection.id;
|
|
37
|
+
const additionalChannelIds = Array.isArray(data.additionalChannelIds)
|
|
38
|
+
? data.additionalChannelIds.filter((channelId) => typeof channelId === 'string' && channelId !== selectedChannel?.id)
|
|
39
|
+
: [];
|
|
40
|
+
for (const channelId of additionalChannelIds) {
|
|
41
|
+
await assignCollectionsToChannel({ input: { collectionIds: [collectionId], channelId } });
|
|
42
|
+
}
|
|
43
|
+
return response;
|
|
44
|
+
}, [id, update, create, assignCollectionsToChannel, selectedChannel?.id]);
|
|
35
45
|
const onDeleteHandler = useCallback(() => {
|
|
36
46
|
if (!id) {
|
|
37
47
|
throw new Error('Could not find the id.');
|
|
@@ -52,6 +62,11 @@ export const CollectionsDetailPage = () => {
|
|
|
52
62
|
inheritFilters: {
|
|
53
63
|
initialValue: true,
|
|
54
64
|
},
|
|
65
|
+
...{
|
|
66
|
+
additionalChannelIds: {
|
|
67
|
+
initialValue: [],
|
|
68
|
+
},
|
|
69
|
+
},
|
|
55
70
|
translations: translationsValidator,
|
|
56
71
|
filters: configurableOperationArrayValidator(),
|
|
57
72
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useRef } from 'react';
|
|
3
|
-
import { EntityCustomFields, Input, Label, Switch, CustomCard, CardIcons, useDetailView, useSettings, RichTextEditor, useTranslation, setInArrayBy, EntityChannelManager, Routes, normalizeString, } from '@deenruv/react-ui-devkit';
|
|
3
|
+
import { EntityCustomFields, Input, Label, Switch, Checkbox, CustomCard, CardIcons, useDetailView, useSettings, useServer, RichTextEditor, useTranslation, setInArrayBy, EntityChannelManager, Routes, normalizeString, } from '@deenruv/react-ui-devkit';
|
|
4
4
|
import { FiltersCard } from "./FiltersCard";
|
|
5
5
|
import { ContentsCard } from "./ContentsCard";
|
|
6
6
|
import { AssetsCard } from "../../products/_components/AssetsCard.js";
|
|
@@ -9,6 +9,7 @@ export const CollectionsDetailView = () => {
|
|
|
9
9
|
const { t } = useTranslation('collections');
|
|
10
10
|
const contentLng = useSettings((p) => p.translationsLanguage);
|
|
11
11
|
const selectedChannel = useSettings((p) => p.selectedChannel);
|
|
12
|
+
const channels = useServer((p) => p.channels);
|
|
12
13
|
const navigate = useNavigate();
|
|
13
14
|
const { form, fetchEntity, entity, id } = useDetailView('collections-detail-view', 'CreateCollectionInput', 'translations', 'assetIds', 'featuredAssetId', 'isPrivate', 'inheritFilters', 'filters', 'customFields');
|
|
14
15
|
const { base } = form;
|
|
@@ -40,7 +41,15 @@ export const CollectionsDetailView = () => {
|
|
|
40
41
|
})();
|
|
41
42
|
}, [contentLng, selectedChannel?.id]);
|
|
42
43
|
const translations = base.watch('translations') || [];
|
|
44
|
+
const additionalChannelIds = base.watch('additionalChannelIds') ?? [];
|
|
43
45
|
const currentTranslationValue = translations.find((v) => v.languageCode === contentLng);
|
|
46
|
+
const toggleAdditionalChannel = useCallback((channelId, checked) => {
|
|
47
|
+
if (channelId === selectedChannel?.id)
|
|
48
|
+
return;
|
|
49
|
+
const currentIds = base.watch('additionalChannelIds') ?? [];
|
|
50
|
+
const nextIds = checked ? [...new Set([...currentIds, channelId])] : currentIds.filter((id) => id !== channelId);
|
|
51
|
+
base.setField('additionalChannelIds', nextIds);
|
|
52
|
+
}, [base, selectedChannel?.id]);
|
|
44
53
|
const handleAddAsset = useCallback((newId) => {
|
|
45
54
|
if (!newId)
|
|
46
55
|
return;
|
|
@@ -85,7 +94,11 @@ export const CollectionsDetailView = () => {
|
|
|
85
94
|
: undefined, required: true }) }), _jsx("div", { className: "flex basis-full md:basis-1/3", children: _jsx(Input, { label: t('details.basic.slug'), value: currentTranslationValue?.slug ?? '', onChange: (e) => {
|
|
86
95
|
handleSlugManualEdit();
|
|
87
96
|
setTranslationField('slug', e.target.value);
|
|
88
|
-
}, required: true }) }), _jsxs("div", { className: "mt-7 flex basis-full items-center gap-3 md:basis-1/3", children: [_jsx(Switch, { checked: base.watch('isPrivate') ?? false, onCheckedChange: (e) => base.setField('isPrivate', e) }), _jsx(Label, { children: t('details.basic.isPrivate') })] })] }), _jsxs("div", { className: "flex basis-full flex-col", children: [_jsx(Label, { className: "mb-2", children: t('details.basic.description') }), _jsx(RichTextEditor, { content: currentTranslationValue?.description ?? '', onContentChanged: (e) => setTranslationField('description', e) })] })] }) }), _jsx(AssetsCard, { onAddAsset: handleAddAsset, featuredAssetId: base.watch('featuredAssetId') ?? undefined, assetsIds: base.watch('assetIds') ?? undefined, onFeaturedAssetChange: (id) => base.setField('featuredAssetId', id), onAssetsChange: (ids) => base.setField('assetIds', ids) }), _jsx(EntityChannelManager, { entity: "collection", entityId: id, entityChannels: [{ id: selectedChannel?.id, code: selectedChannel?.code }], onRemoveSuccess: () => navigate(Routes.collections.list) })
|
|
97
|
+
}, required: true }) }), _jsxs("div", { className: "mt-7 flex basis-full items-center gap-3 md:basis-1/3", children: [_jsx(Switch, { checked: base.watch('isPrivate') ?? false, onCheckedChange: (e) => base.setField('isPrivate', e) }), _jsx(Label, { children: t('details.basic.isPrivate') })] })] }), _jsxs("div", { className: "flex basis-full flex-col", children: [_jsx(Label, { className: "mb-2", children: t('details.basic.description') }), _jsx(RichTextEditor, { content: currentTranslationValue?.description ?? '', onContentChanged: (e) => setTranslationField('description', e) })] })] }) }), _jsx(AssetsCard, { onAddAsset: handleAddAsset, featuredAssetId: base.watch('featuredAssetId') ?? undefined, assetsIds: base.watch('assetIds') ?? undefined, onFeaturedAssetChange: (id) => base.setField('featuredAssetId', id), onAssetsChange: (ids) => base.setField('assetIds', ids) }), id ? (_jsx(EntityChannelManager, { entity: "collection", entityId: id, entityChannels: [{ id: selectedChannel?.id, code: selectedChannel?.code }], onRemoveSuccess: () => navigate(Routes.collections.list) })) : (_jsx(CustomCard, { title: t('details.channels.title'), icon: _jsx(CardIcons.shipping, {}), color: "green", children: _jsxs("div", { className: "flex flex-col gap-4 pt-4", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: t('details.channels.description') }), _jsx("div", { className: "grid gap-3 md:grid-cols-2", children: channels.map((channel) => {
|
|
98
|
+
const isCurrentChannel = channel.id === selectedChannel?.id;
|
|
99
|
+
const checked = isCurrentChannel || additionalChannelIds.includes(channel.id);
|
|
100
|
+
return (_jsxs("label", { className: "flex items-center gap-3 rounded-md border p-3 text-sm transition-colors hover:bg-muted/50", children: [_jsx(Checkbox, { checked: checked, disabled: isCurrentChannel, onCheckedChange: (value) => toggleAdditionalChannel(channel.id, !!value) }), _jsx("span", { className: "font-medium", children: channel.code }), isCurrentChannel && (_jsx("span", { className: "ml-auto rounded-full bg-muted px-2 py-1 text-xs text-muted-foreground", children: t('details.channels.currentChannel') }))] }, channel.id));
|
|
101
|
+
}) }), !channels.length && _jsx("p", { className: "text-sm text-muted-foreground", children: t('details.channels.noChannels') })] }) })), _jsx(FiltersCard, { currentFiltersValue: base.watch('filters') ?? undefined, onFiltersValueChange: (filters) => base.setField('filters', filters ?? []), inheritValue: base.watch('inheritFilters') ?? false, onInheritChange: (e) => base.setField('inheritFilters', e), errors: base.formState.errors?.filters?.message ? [base.formState.errors.filters.message] : undefined }), _jsx(EntityCustomFields, { entityName: "collection", id: id, hideButton: true, onChange: (customFields, translations) => {
|
|
89
102
|
base.setField('customFields', customFields);
|
|
90
103
|
if (translations)
|
|
91
104
|
base.setField('translations', translations);
|
|
@@ -5,6 +5,44 @@ import { X } from 'lucide-react';
|
|
|
5
5
|
import { FacetsSelector } from "./FacetsSelector";
|
|
6
6
|
import { CombinationMode } from "./CombinationMode";
|
|
7
7
|
import { VariantsSelector } from "./VariantsSelector";
|
|
8
|
+
const getArgumentComponent = (argument) => argument?.ui?.component;
|
|
9
|
+
const getArgumentOptions = (argument) => argument?.ui?.options ?? [];
|
|
10
|
+
const stringifyDefaultValue = (value) => {
|
|
11
|
+
if (Array.isArray(value) || (typeof value === 'object' && value !== null)) {
|
|
12
|
+
return JSON.stringify(value);
|
|
13
|
+
}
|
|
14
|
+
if (typeof value === 'boolean' || typeof value === 'number' || typeof value === 'string') {
|
|
15
|
+
return String(value);
|
|
16
|
+
}
|
|
17
|
+
return '';
|
|
18
|
+
};
|
|
19
|
+
const getDefaultArgumentValue = (argument) => {
|
|
20
|
+
if (argument.defaultValue !== undefined && argument.defaultValue !== null) {
|
|
21
|
+
return stringifyDefaultValue(argument.defaultValue);
|
|
22
|
+
}
|
|
23
|
+
const component = getArgumentComponent(argument);
|
|
24
|
+
if (argument.list || component === 'facet-value-form-input' || component === 'product-multi-form-input') {
|
|
25
|
+
return '[]';
|
|
26
|
+
}
|
|
27
|
+
if (component === 'select-form-input') {
|
|
28
|
+
return getArgumentOptions(argument)[0]?.value ?? '';
|
|
29
|
+
}
|
|
30
|
+
if (argument.type === 'boolean') {
|
|
31
|
+
return 'false';
|
|
32
|
+
}
|
|
33
|
+
return '';
|
|
34
|
+
};
|
|
35
|
+
const parseStringArrayValue = (value) => {
|
|
36
|
+
if (!value)
|
|
37
|
+
return [];
|
|
38
|
+
try {
|
|
39
|
+
const parsed = JSON.parse(value);
|
|
40
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === 'string') : [];
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
};
|
|
8
46
|
export const FiltersCard = ({ currentFiltersValue, onFiltersValueChange, inheritValue, onInheritChange, errors, }) => {
|
|
9
47
|
const { t } = useTranslation('collections');
|
|
10
48
|
const codesTranslations = t('details.filters.labels.codes', { returnObjects: true });
|
|
@@ -27,14 +65,14 @@ export const FiltersCard = ({ currentFiltersValue, onFiltersValueChange, inherit
|
|
|
27
65
|
}, [fetchOptions]);
|
|
28
66
|
const handleFiltersValueChange = useCallback((index, code, args) => {
|
|
29
67
|
const correspondingFilter = filters.find((h) => h?.code === code);
|
|
30
|
-
if (correspondingFilter
|
|
31
|
-
const newFiltersValue = [...currentFiltersValue];
|
|
68
|
+
if (correspondingFilter) {
|
|
69
|
+
const newFiltersValue = [...(currentFiltersValue || [])];
|
|
32
70
|
newFiltersValue[index] = {
|
|
33
71
|
code: correspondingFilter.code,
|
|
34
72
|
arguments: args ||
|
|
35
73
|
correspondingFilter.args.map((a) => ({
|
|
36
74
|
name: a.name,
|
|
37
|
-
value:
|
|
75
|
+
value: getDefaultArgumentValue(a),
|
|
38
76
|
})),
|
|
39
77
|
};
|
|
40
78
|
onFiltersValueChange(newFiltersValue);
|
|
@@ -49,38 +87,40 @@ export const FiltersCard = ({ currentFiltersValue, onFiltersValueChange, inherit
|
|
|
49
87
|
}, [onFiltersValueChange, currentFiltersValue]);
|
|
50
88
|
const handleAddFilter = useCallback(() => {
|
|
51
89
|
const defaultFilter = filters[0];
|
|
90
|
+
if (!defaultFilter)
|
|
91
|
+
return;
|
|
52
92
|
const newFiltersValue = [
|
|
53
93
|
...(currentFiltersValue || []),
|
|
54
94
|
{
|
|
55
95
|
code: defaultFilter.code,
|
|
56
96
|
arguments: defaultFilter.args.map((a) => ({
|
|
57
97
|
name: a.name,
|
|
58
|
-
value:
|
|
98
|
+
value: getDefaultArgumentValue(a),
|
|
59
99
|
})),
|
|
60
100
|
},
|
|
61
101
|
];
|
|
62
102
|
onFiltersValueChange(newFiltersValue);
|
|
63
|
-
}, [onFiltersValueChange, currentFiltersValue]);
|
|
64
|
-
return (_jsx(CustomCard, { title: t('details.filters.title'), upperRight: _jsx(ErrorMessage, { errors: errors }), color: "orange", icon: _jsx(CardIcons.filter, {}), children: _jsx("div", { className: "flex- flex flex-wrap gap-4 xl:flex-nowrap", children: _jsxs("div", { className: "flex basis-full flex-col gap-4", children: [_jsxs("div", { className: "mb-2 flex basis-full", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Switch, { checked: inheritValue, onCheckedChange: onInheritChange }), _jsx(Label, { children: t('details.filters.inherit') })] }), _jsx(Button, { className: "ml-auto", onClick: handleAddFilter, children: t('details.filters.addCondition') })] }), currentFiltersValue?.map((filter, index) => {
|
|
103
|
+
}, [filters, onFiltersValueChange, currentFiltersValue]);
|
|
104
|
+
return (_jsx(CustomCard, { title: t('details.filters.title'), upperRight: _jsx(ErrorMessage, { errors: errors }), color: "orange", icon: _jsx(CardIcons.filter, {}), children: _jsx("div", { className: "flex- flex flex-wrap gap-4 xl:flex-nowrap", children: _jsxs("div", { className: "flex basis-full flex-col gap-4", children: [_jsxs("div", { className: "mb-2 flex basis-full", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Switch, { checked: inheritValue, onCheckedChange: onInheritChange }), _jsx(Label, { children: t('details.filters.inherit') })] }), _jsx(Button, { className: "ml-auto", onClick: handleAddFilter, disabled: filters.length === 0, title: filters.length === 0 ? t('details.filters.noAvailableConditions') : undefined, children: t('details.filters.addCondition') })] }), currentFiltersValue?.map((filter, index) => {
|
|
65
105
|
return (_jsxs("div", { className: "flex flex-col gap-4", children: [_jsx(Separator, {}), _jsxs("div", { className: "flex w-1/2 items-end gap-1", children: [_jsx(SimpleSelect, { label: t('details.filters.condition'), value: filter ? filter.code : '', onValueChange: (e) => handleFiltersValueChange(index, e), options: allFiltersOptions }), filter?.code && (_jsx(Button, { variant: 'secondary', className: "p-2", onClick: (e) => {
|
|
66
106
|
e.preventDefault();
|
|
67
107
|
handleRemoveFilter(filter.code);
|
|
68
108
|
}, children: _jsx(X, { size: 20 }) }))] }), _jsx("div", { className: "flex items-start justify-center gap-4", children: filter?.arguments.map((e, i) => {
|
|
69
109
|
const _filter = filters?.find((f) => f.code === filter.code);
|
|
70
110
|
const argument = _filter?.args.find((a) => a.name === e.name);
|
|
71
|
-
return argument
|
|
111
|
+
return getArgumentComponent(argument) === 'facet-value-form-input' ? (_jsx(FacetsSelector, { value: parseStringArrayValue(filter?.arguments[i].value), onChange: (e) => {
|
|
72
112
|
filter.arguments[i] = { name: argument?.name || '', value: JSON.stringify(e) };
|
|
73
113
|
handleFiltersValueChange(index, filter?.code, filter.arguments);
|
|
74
|
-
} })) : argument
|
|
114
|
+
} })) : getArgumentComponent(argument) === 'select-form-input' ? (_jsx(SimpleSelect, { label: argument?.label || argument?.name || '', value: filter?.arguments[i].value, onValueChange: (e) => {
|
|
75
115
|
filter.arguments[i] = { name: argument?.name || '', value: e };
|
|
76
116
|
handleFiltersValueChange(index, filter?.code, filter.arguments);
|
|
77
|
-
}, options: (argument
|
|
117
|
+
}, options: getArgumentOptions(argument).map((o) => ({
|
|
78
118
|
label: o.value,
|
|
79
119
|
value: o.value,
|
|
80
|
-
})) || [] }, i)) : argument
|
|
120
|
+
})) || [] }, i)) : getArgumentComponent(argument) === 'combination-mode-form-input' ? (_jsx(CombinationMode, { label: t('details.filters.labels.arguments.combination-mode'), value: filter?.arguments[i].value, onChange: (e) => {
|
|
81
121
|
filter.arguments[i] = { name: argument?.name || '', value: e };
|
|
82
122
|
handleFiltersValueChange(index, filter?.code, filter.arguments);
|
|
83
|
-
} })) : argument
|
|
123
|
+
} })) : getArgumentComponent(argument) === 'product-multi-form-input' ? (_jsx(VariantsSelector, { type: argument?.ui?.selectionMode, label: argument?.label || argument?.name || '', value: parseStringArrayValue(filter?.arguments[i].value), onChange: (e) => {
|
|
84
124
|
filter.arguments[i] = { name: argument?.name || '', value: JSON.stringify(e) };
|
|
85
125
|
handleFiltersValueChange(index, filter?.code, filter.arguments);
|
|
86
126
|
} })) : argument?.type === 'int' ? (_jsx("div", { className: "flex basis-full", children: _jsx(Input, { type: "number", step: 0.01, label: argument?.label || argument.name, value: filter?.arguments[i].value, onChange: (e) => {
|
|
@@ -2,5 +2,6 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useTranslation, CardIcons, CustomCard, Input } from '@deenruv/react-ui-devkit';
|
|
3
3
|
export const PersonalDataCard = ({ setField, state }) => {
|
|
4
4
|
const { t } = useTranslation('customers');
|
|
5
|
-
|
|
5
|
+
const getFieldErrors = (fieldName) => state.formState.errors?.[fieldName]?.message ? [state.formState.errors[fieldName].message] : undefined;
|
|
6
|
+
return (_jsx(CustomCard, { title: t('personalData.header'), icon: _jsx(CardIcons.basic, {}), color: "blue", children: _jsxs("div", { className: "flex flex-col gap-4", children: [_jsx("div", { className: "flex w-1/4 gap-3", children: _jsx(Input, { label: t('personalData.title'), value: state.watch('title') ?? undefined, onChange: (e) => setField('title', e.target.value) }) }), _jsxs("div", { className: "flex items-start gap-3", children: [_jsx(Input, { label: t('personalData.firstName'), value: state.watch('firstName') ?? undefined, onChange: (e) => setField('firstName', e.target.value), errors: getFieldErrors('firstName'), required: true }), _jsx(Input, { label: t('personalData.lastName'), value: state.watch('lastName') ?? undefined, onChange: (e) => setField('lastName', e.target.value), errors: getFieldErrors('lastName'), required: true })] }), _jsxs("div", { className: "flex items-start gap-3", children: [_jsx(Input, { label: t('personalData.emailAddress'), value: state.watch('emailAddress') ?? undefined, onChange: (e) => setField('emailAddress', e.target.value), errors: getFieldErrors('emailAddress'), required: true }), _jsx(Input, { label: t('personalData.phoneNumber'), value: state.watch('phoneNumber') ?? undefined, onChange: (e) => setField('phoneNumber', e.target.value) })] })] }) }));
|
|
6
7
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useMemo, useState } from 'react';
|
|
3
|
-
import { Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, Input, PageBlock, Separator,
|
|
3
|
+
import { Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, Input, PageBlock, Separator, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, usePluginStore, } from '@deenruv/react-ui-devkit';
|
|
4
4
|
import { BarChart3, Box, Check, ChevronDown, Code2, Cog, FileCode, Filter, Grid3X3, Info, LayoutDashboard, List, MessageSquare, Puzzle, RefreshCw, Search, Settings, Table, X, } from 'lucide-react';
|
|
5
5
|
const getCategoryIcon = (category) => {
|
|
6
6
|
switch (category) {
|
|
@@ -33,7 +33,7 @@ const getStatusBadge = (status) => {
|
|
|
33
33
|
}
|
|
34
34
|
};
|
|
35
35
|
export const Extensions = () => {
|
|
36
|
-
const { allPlugins: _plugins
|
|
36
|
+
const { allPlugins: _plugins } = usePluginStore();
|
|
37
37
|
const [searchTerm, setSearchTerm] = useState('');
|
|
38
38
|
const [categoryFilter, setCategoryFilter] = useState('all');
|
|
39
39
|
const [statusFilter, setStatusFilter] = useState('all');
|
|
@@ -65,9 +65,5 @@ export const Extensions = () => {
|
|
|
65
65
|
setCategoryFilter('all');
|
|
66
66
|
setStatusFilter('all');
|
|
67
67
|
setSearchTerm('');
|
|
68
|
-
}, children: "Reset filters" })] })) : view === 'grid' ? (_jsx("div", { className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3", children: filteredPlugins.map((plugin) => (_jsxs(Card, { className: "overflow-hidden", children: [_jsxs(CardHeader, { className: "pb-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "rounded-md bg-primary/10 p-2", children: getCategoryIcon(plugin.category) }), _jsx(CardTitle, { className: "text-lg", children: plugin.name })] }), getStatusBadge(plugin.status)] }), _jsx(CardDescription, { className: "mt-2", children: plugin.description })] }), _jsxs(CardContent, { className: "pb-2", children: [_jsxs("div", { className: "grid grid-cols-2 gap-2 text-sm", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-muted-foreground", children: "Version:" }), _jsx("span", { children: plugin.version })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-muted-foreground", children: "Author:" }), _jsx("span", { children: plugin.author })] })] }), _jsx(Separator, { className: "my-3" }), _jsxs("div", { className: "grid grid-cols-4 gap-2 text-center text-sm", children: [_jsxs("div", { children: [_jsx("div", { className: "font-medium", children: plugin.tables?.length || 0 }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Tables" })] }), _jsxs("div", { children: [_jsx("div", { className: "font-medium", children: plugin.tabs?.length || 0 }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Tabs" })] }), _jsxs("div", { children: [_jsx("div", { className: "font-medium", children: plugin.components?.length || 0 }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Components" })] }), _jsxs("div", { children: [_jsx("div", { className: "font-medium", children: plugin.widgets?.length || 0 }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Widgets" })] })] })] }),
|
|
69
|
-
changePluginStatus(plugin.name, checked ? 'active' : 'inactive');
|
|
70
|
-
} }) })] })] }, plugin.name))) })) : (_jsxs("div", { className: "rounded-md border", children: [_jsxs("div", { className: "grid grid-cols-12 gap-4 p-4 font-medium", children: [_jsx("div", { className: "col-span-4", children: "Name" }), _jsx("div", { className: "col-span-3", children: "Author" }), _jsx("div", { className: "col-span-1 text-center", children: "Version" }), _jsx("div", { className: "col-span-1 text-center", children: "Status" }), _jsx("div", { className: "col-span-3 text-right", children: "Actions" })] }), _jsx(Separator, {}), filteredPlugins.map((plugin, index) => (_jsxs("div", { children: [_jsxs("div", { className: "grid grid-cols-12 items-center gap-4 p-4", children: [_jsx("div", { className: "col-span-4", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "rounded-md bg-primary/10 p-2", children: getCategoryIcon(plugin.category) }), _jsxs("div", { children: [_jsx("div", { className: "font-medium", children: plugin.name }), _jsx("div", { className: "text-xs text-muted-foreground", children: plugin.description })] })] }) }), _jsx("div", { className: "col-span-3", children: plugin.author }), _jsx("div", { className: "col-span-1 text-center", children: plugin.version }), _jsx("div", { className: "col-span-1 text-center", children: getStatusBadge(plugin.status) }), _jsxs("div", { className: "col-span-3 flex justify-end gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", children: [_jsx(Settings, { className: "mr-2 size-3" }), "Configure"] }), _jsx(Switch, { checked: plugin.status === 'active', onCheckedChange: (checked) => {
|
|
71
|
-
changePluginStatus(plugin.name, checked ? 'active' : 'inactive');
|
|
72
|
-
} })] })] }), index < filteredPlugins.length - 1 && _jsx(Separator, {})] }, plugin.name)))] }))] }) }));
|
|
68
|
+
}, children: "Reset filters" })] })) : view === 'grid' ? (_jsx("div", { className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3", children: filteredPlugins.map((plugin) => (_jsxs(Card, { className: "overflow-hidden", children: [_jsxs(CardHeader, { className: "pb-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "rounded-md bg-primary/10 p-2", children: getCategoryIcon(plugin.category) }), _jsx(CardTitle, { className: "text-lg", children: plugin.name })] }), getStatusBadge(plugin.status)] }), _jsx(CardDescription, { className: "mt-2", children: plugin.description })] }), _jsxs(CardContent, { className: "pb-2", children: [_jsxs("div", { className: "grid grid-cols-2 gap-2 text-sm", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-muted-foreground", children: "Version:" }), _jsx("span", { children: plugin.version })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-muted-foreground", children: "Author:" }), _jsx("span", { children: plugin.author })] })] }), _jsx(Separator, { className: "my-3" }), _jsxs("div", { className: "grid grid-cols-4 gap-2 text-center text-sm", children: [_jsxs("div", { children: [_jsx("div", { className: "font-medium", children: plugin.tables?.length || 0 }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Tables" })] }), _jsxs("div", { children: [_jsx("div", { className: "font-medium", children: plugin.tabs?.length || 0 }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Tabs" })] }), _jsxs("div", { children: [_jsx("div", { className: "font-medium", children: plugin.components?.length || 0 }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Components" })] }), _jsxs("div", { children: [_jsx("div", { className: "font-medium", children: plugin.widgets?.length || 0 }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Widgets" })] })] })] }), _jsx(CardFooter, { className: "flex justify-between pt-2", children: _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", className: "size-8", children: _jsx(Info, { className: "size-4" }) }) }), _jsx(TooltipContent, { children: "View details" })] }) }) })] }, plugin.name))) })) : (_jsxs("div", { className: "rounded-md border", children: [_jsxs("div", { className: "grid grid-cols-12 gap-4 p-4 font-medium", children: [_jsx("div", { className: "col-span-4", children: "Name" }), _jsx("div", { className: "col-span-3", children: "Author" }), _jsx("div", { className: "col-span-1 text-center", children: "Version" }), _jsx("div", { className: "col-span-1 text-center", children: "Status" }), _jsx("div", { className: "col-span-3 text-right", children: "Actions" })] }), _jsx(Separator, {}), filteredPlugins.map((plugin, index) => (_jsxs("div", { children: [_jsxs("div", { className: "grid grid-cols-12 items-center gap-4 p-4", children: [_jsx("div", { className: "col-span-4", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "rounded-md bg-primary/10 p-2", children: getCategoryIcon(plugin.category) }), _jsxs("div", { children: [_jsx("div", { className: "font-medium", children: plugin.name }), _jsx("div", { className: "text-xs text-muted-foreground", children: plugin.description })] })] }) }), _jsx("div", { className: "col-span-3", children: plugin.author }), _jsx("div", { className: "col-span-1 text-center", children: plugin.version }), _jsx("div", { className: "col-span-1 text-center", children: getStatusBadge(plugin.status) }), _jsx("div", { className: "col-span-3 flex justify-end gap-2", children: _jsxs(Button, { variant: "outline", size: "sm", children: [_jsx(Settings, { className: "mr-2 size-3" }), "Configure"] }) })] }), index < filteredPlugins.length - 1 && _jsx(Separator, {})] }, plugin.name)))] }))] }) }));
|
|
73
69
|
};
|
|
@@ -1,17 +1,50 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect } from 'react';
|
|
3
|
-
import { useParams } from 'react-router';
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { useNavigate, useParams } from 'react-router';
|
|
4
4
|
import { OrderSummary, RealizationCard, CustomerSelectCard, AddressCard, ShippingMethod, TaxSummary, OrderHistory, ProductsCard, TopActions, Payments, SurchargeCard, } from "./_components";
|
|
5
|
-
import { useTranslation, useOrder, ORDER_STATE, EntityCustomFields, LoadingMask, DetailViewMarker, } from '@deenruv/react-ui-devkit';
|
|
5
|
+
import { useTranslation, useOrder, ORDER_STATE, EntityCustomFields, LoadingMask, DetailViewMarker, apiClient, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Routes, } from '@deenruv/react-ui-devkit';
|
|
6
6
|
import { PromotionsList } from "./_components/PromotionsList.js";
|
|
7
|
+
import { toast } from 'sonner';
|
|
8
|
+
const createDraftOrder = async () => {
|
|
9
|
+
const response = await apiClient('mutation')({
|
|
10
|
+
createDraftOrder: { id: true },
|
|
11
|
+
});
|
|
12
|
+
return response.createDraftOrder.id;
|
|
13
|
+
};
|
|
14
|
+
const DraftOrderCreateStart = () => {
|
|
15
|
+
const { t } = useTranslation('orders');
|
|
16
|
+
const navigate = useNavigate();
|
|
17
|
+
const [isCreating, setIsCreating] = useState(false);
|
|
18
|
+
const onCreate = async () => {
|
|
19
|
+
try {
|
|
20
|
+
setIsCreating(true);
|
|
21
|
+
const id = await createDraftOrder();
|
|
22
|
+
if (!id) {
|
|
23
|
+
toast.error(t('createDraftOrder.error'));
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
navigate(Routes.orders.to(id), { viewTransition: true });
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
toast.error(t('createDraftOrder.error'));
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
setIsCreating(false);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
return (_jsx("main", { className: "my-4", children: _jsx("div", { className: "mx-auto flex min-h-[70vh] w-full max-w-[720px] items-center px-4 2xl:px-8", children: _jsxs(Card, { className: "w-full", children: [_jsxs(CardHeader, { className: "space-y-2 text-center sm:text-left", children: [_jsx(CardTitle, { children: t('createDraftOrder.title') }), _jsx(CardDescription, { children: t('createDraftOrder.description') })] }), _jsx(CardContent, { children: _jsxs("div", { className: "flex flex-col-reverse gap-3 sm:flex-row sm:justify-end", children: [_jsx(Button, { variant: "outline", onClick: () => navigate(Routes.orders.list, { viewTransition: true }), disabled: isCreating, children: t('createDraftOrder.cancelButton') }), _jsx(Button, { onClick: onCreate, disabled: isCreating, children: isCreating ? (_jsxs("span", { className: "flex items-center gap-2", children: [_jsx("span", { className: "size-4 animate-spin rounded-full border-2 border-current border-r-transparent" }), t('createDraftOrder.creating')] })) : (t('createDraftOrder.startButton')) })] }) })] }) }) }));
|
|
36
|
+
};
|
|
7
37
|
export const OrdersDetailPage = () => {
|
|
8
38
|
const { id } = useParams();
|
|
9
39
|
const { t } = useTranslation('orders');
|
|
10
40
|
const { order, mode, fetchOrder, loading } = useOrder();
|
|
41
|
+
const isCreateRoute = id === 'new';
|
|
11
42
|
useEffect(() => {
|
|
12
|
-
if (id)
|
|
43
|
+
if (id && !isCreateRoute)
|
|
13
44
|
fetchOrder(id);
|
|
14
|
-
}, [id]);
|
|
45
|
+
}, [id, isCreateRoute]);
|
|
46
|
+
if (isCreateRoute)
|
|
47
|
+
return _jsx(DraftOrderCreateStart, {});
|
|
15
48
|
if (loading)
|
|
16
49
|
return _jsx(LoadingMask, {});
|
|
17
50
|
if (!order || !mode) {
|
|
@@ -4,15 +4,8 @@ import { Routes, apiClient, DetailList, deepMerge, OrderStateBadge, priceFormatt
|
|
|
4
4
|
import { useCallback } from 'react';
|
|
5
5
|
import { useShallow } from 'zustand/react/shallow';
|
|
6
6
|
import { useNavigate } from 'react-router';
|
|
7
|
-
import { toast } from 'sonner';
|
|
8
7
|
const tableId = 'orders-list-view';
|
|
9
8
|
const { selector } = ListLocations[tableId];
|
|
10
|
-
const createDraftOrder = async () => {
|
|
11
|
-
const response = await apiClient('mutation')({
|
|
12
|
-
createDraftOrder: { id: true },
|
|
13
|
-
});
|
|
14
|
-
return response.createDraftOrder.id;
|
|
15
|
-
};
|
|
16
9
|
const fetch = async ({ page, perPage, filter, filterOperator, sort }, additionalSelector) => {
|
|
17
10
|
const response = await apiClient('query')({
|
|
18
11
|
['orders']: [
|
|
@@ -71,15 +64,7 @@ export const OrdersListPage = () => {
|
|
|
71
64
|
},
|
|
72
65
|
},
|
|
73
66
|
], detailLinkColumn: "id", searchFields: ['code', 'customerLastName'], entityName: "Order", route: {
|
|
74
|
-
create: () => {
|
|
75
|
-
createDraftOrder()
|
|
76
|
-
.then((id) => {
|
|
77
|
-
if (!id)
|
|
78
|
-
toast.error('Failed to create draft order');
|
|
79
|
-
navigate(Routes.orders.to(id), { viewTransition: true });
|
|
80
|
-
})
|
|
81
|
-
.catch(() => toast.error('Failed to create draft order'));
|
|
82
|
-
},
|
|
67
|
+
create: () => navigate(Routes.orders.new, { viewTransition: true }),
|
|
83
68
|
edit: (id) => navigate(Routes.orders.to(id), { viewTransition: true }),
|
|
84
69
|
}, tableId: tableId, fetch: fetch, createPermissions: [Permission.CreateOrder], deletePermissions: [Permission.DeleteOrder], hideColumns: ['active', 'totalQuantity', 'currencyCode'], additionalColumns: [
|
|
85
70
|
{
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState } from 'react';
|
|
3
|
-
import { Button, Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, DropdownMenuItem, useOrder, } from '@deenruv/react-ui-devkit';
|
|
3
|
+
import { Button, Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, DropdownMenuItem, useOrder, useTranslation, } from '@deenruv/react-ui-devkit';
|
|
4
4
|
import { Undo2 } from 'lucide-react';
|
|
5
|
-
import { useTranslation } from 'react-i18next';
|
|
6
5
|
import { ProductsTable } from "./";
|
|
7
6
|
import { RefundPaymentCard } from "./RefundPaymentCard.js";
|
|
8
7
|
export const CancelAndRefundDialog = ({ refundReason, setRefundReason, onConfirm, }) => {
|
|
@@ -3,7 +3,7 @@ import { Button, Dialog, DialogContent, DialogTitle, ScrollArea, useOrder, useSe
|
|
|
3
3
|
import { useState } from 'react';
|
|
4
4
|
import { useShallow } from 'zustand/react/shallow';
|
|
5
5
|
export const OrderLineCustomFields = ({ line, order, mode }) => {
|
|
6
|
-
const
|
|
6
|
+
const setModifiedOrder = useOrder(({ setModifiedOrder }) => setModifiedOrder);
|
|
7
7
|
const orderLineCustomFields = useServer(useShallow((p) => p.serverConfig?.entityCustomFields?.find((el) => el.entityName === 'OrderLine')?.customFields || []));
|
|
8
8
|
const { t } = useTranslation('common');
|
|
9
9
|
const [open, setOpen] = useState(false);
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { OrderLineCustomFields } from "./OrderLineCustomFields.js";
|
|
3
|
-
import { Checkbox, ImageWithPreview, Input, Label, priceFormatter, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, useOrder, } from '@deenruv/react-ui-devkit';
|
|
3
|
+
import { Checkbox, ImageWithPreview, Input, Label, priceFormatter, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, useOrder, useTranslation, } from '@deenruv/react-ui-devkit';
|
|
4
4
|
import { Tag } from 'lucide-react';
|
|
5
5
|
import { useCallback } from 'react';
|
|
6
|
-
import { useTranslation } from 'react-i18next';
|
|
7
6
|
export const ProductsTable = ({ setRefundLines, refundLines }) => {
|
|
8
7
|
const { t } = useTranslation('orders');
|
|
9
8
|
const { mode, currentOrder } = useOrder();
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Checkbox, CustomCard, Input, Label, priceFormatter, Textarea, useOrder } from '@deenruv/react-ui-devkit';
|
|
3
|
-
import { useTranslation } from 'react-i18next';
|
|
2
|
+
import { Checkbox, CustomCard, Input, Label, priceFormatter, Textarea, useOrder, useTranslation, } from '@deenruv/react-ui-devkit';
|
|
4
3
|
export const RefundPaymentCard = ({ refundReason, setRefundReason, cancelShipping, setCancelShipping, refundAmount, setRefundAmount, }) => {
|
|
5
4
|
const { t } = useTranslation('orders');
|
|
6
5
|
const { modifiedOrder } = useOrder();
|
|
@@ -4,7 +4,7 @@ import { VariantsTab } from "./_components/VariantsTab";
|
|
|
4
4
|
import { OptionsTab } from "./_components/OptionsTab";
|
|
5
5
|
import { ProductDetailView } from './_components/ProductDetailView';
|
|
6
6
|
import { ProductDetailSidebar } from './_components/ProductDetailSidebar';
|
|
7
|
-
import { useTranslation, createDeenruvForm, DetailView, useMutation } from '@deenruv/react-ui-devkit';
|
|
7
|
+
import { DEFAULT_CHANNEL_CODE, useTranslation, createDeenruvForm, DetailView, useMutation, useSettings, } from '@deenruv/react-ui-devkit';
|
|
8
8
|
import { $, Permission, scalars, typedGql } from '@deenruv/admin-types';
|
|
9
9
|
import { useMemo } from 'react';
|
|
10
10
|
const EditProductMutation = typedGql('mutation', { scalars })({
|
|
@@ -13,6 +13,12 @@ const EditProductMutation = typedGql('mutation', { scalars })({
|
|
|
13
13
|
const CreateProductMutation = typedGql('mutation', { scalars })({
|
|
14
14
|
createProduct: [{ input: $('input', 'CreateProductInput!') }, { id: true }],
|
|
15
15
|
});
|
|
16
|
+
const CreateProductVariantsMutation = typedGql('mutation', { scalars })({
|
|
17
|
+
createProductVariants: [{ input: $('input', '[CreateProductVariantInput!]!') }, { id: true }],
|
|
18
|
+
});
|
|
19
|
+
const AssignProductVariantsToChannelMutation = typedGql('mutation', { scalars })({
|
|
20
|
+
assignProductVariantsToChannel: [{ input: $('input', 'AssignProductVariantsToChannelInput!') }, { id: true }],
|
|
21
|
+
});
|
|
16
22
|
const DeleteProductMutation = typedGql('mutation', { scalars })({
|
|
17
23
|
deleteProduct: [{ id: $('id', 'ID!') }, { result: true }],
|
|
18
24
|
});
|
|
@@ -21,7 +27,10 @@ export const ProductsDetailPage = () => {
|
|
|
21
27
|
const { t } = useTranslation('products');
|
|
22
28
|
const [update] = useMutation(EditProductMutation);
|
|
23
29
|
const [create] = useMutation(CreateProductMutation);
|
|
30
|
+
const [createVariants] = useMutation(CreateProductVariantsMutation);
|
|
31
|
+
const [assignProductVariantsToChannel] = useMutation(AssignProductVariantsToChannelMutation);
|
|
24
32
|
const [remove] = useMutation(DeleteProductMutation);
|
|
33
|
+
const selectedChannel = useSettings((p) => p.selectedChannel);
|
|
25
34
|
const defaultTabs = useMemo(() => {
|
|
26
35
|
const tabs = [];
|
|
27
36
|
if (id) {
|
|
@@ -48,19 +57,64 @@ export const ProductsDetailPage = () => {
|
|
|
48
57
|
return [t('validation.nameSlugRequired')];
|
|
49
58
|
},
|
|
50
59
|
},
|
|
60
|
+
...{
|
|
61
|
+
initialVariantSku: {
|
|
62
|
+
validate: (v) => {
|
|
63
|
+
if (!id && (!v || typeof v !== 'string' || !v.trim())) {
|
|
64
|
+
return [t('validation.initialVariantSkuRequired')];
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
initialVariantPrice: {
|
|
69
|
+
initialValue: 0,
|
|
70
|
+
},
|
|
71
|
+
initialVariantName: {},
|
|
72
|
+
},
|
|
51
73
|
},
|
|
52
|
-
onSubmitted: (data) => {
|
|
74
|
+
onSubmitted: async (data) => {
|
|
53
75
|
if (!data.translations)
|
|
54
76
|
throw new Error('Name is required.');
|
|
77
|
+
const translations = data.translations;
|
|
55
78
|
const input = {
|
|
56
|
-
translations
|
|
79
|
+
translations,
|
|
57
80
|
assetIds: data.assetIds,
|
|
58
81
|
featuredAssetId: data.featuredAssetId,
|
|
59
82
|
facetValueIds: data.facetValueIds,
|
|
60
83
|
enabled: data.enabled,
|
|
61
84
|
...(data.customFields ? { customFields: data.customFields } : {}),
|
|
62
85
|
};
|
|
63
|
-
|
|
86
|
+
if (id)
|
|
87
|
+
return update({ input: { id, ...input } });
|
|
88
|
+
const response = await create({ input });
|
|
89
|
+
const initialVariantName = typeof data.initialVariantName === 'string' ? data.initialVariantName.trim() : '';
|
|
90
|
+
const initialVariantSku = typeof data.initialVariantSku === 'string' ? data.initialVariantSku.trim() : '';
|
|
91
|
+
const initialVariantPrice = Number(data.initialVariantPrice ?? 0);
|
|
92
|
+
const variantsResponse = await createVariants({
|
|
93
|
+
input: [
|
|
94
|
+
{
|
|
95
|
+
productId: response.createProduct.id,
|
|
96
|
+
translations: translations.map((translation) => ({
|
|
97
|
+
languageCode: translation.languageCode,
|
|
98
|
+
name: initialVariantName || translation.name || initialVariantSku,
|
|
99
|
+
})),
|
|
100
|
+
sku: initialVariantSku,
|
|
101
|
+
price: Number.isFinite(initialVariantPrice) ? initialVariantPrice : 0,
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
});
|
|
105
|
+
const createdVariantId = variantsResponse.createProductVariants?.[0]?.id;
|
|
106
|
+
if (createdVariantId &&
|
|
107
|
+
selectedChannel?.id &&
|
|
108
|
+
selectedChannel.code &&
|
|
109
|
+
selectedChannel.code !== DEFAULT_CHANNEL_CODE) {
|
|
110
|
+
await assignProductVariantsToChannel({
|
|
111
|
+
input: {
|
|
112
|
+
productVariantIds: [createdVariantId],
|
|
113
|
+
channelId: selectedChannel.id,
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
return response;
|
|
64
118
|
},
|
|
65
119
|
onDeleted: () => {
|
|
66
120
|
if (id)
|