@open-mercato/core 0.6.4-develop.4360.1.568bf6e32b → 0.6.4-develop.4368.1.2f7e9a7002
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/modules/catalog/backend/catalog/categories/[id]/edit/page.js +66 -44
- package/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js +124 -103
- package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js.map +2 -2
- package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js +26 -4
- package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js.map +2 -2
- package/dist/modules/directory/backend/directory/tenants/[id]/edit/page.js +24 -4
- package/dist/modules/directory/backend/directory/tenants/[id]/edit/page.js.map +2 -2
- package/dist/modules/planner/backend/planner/availability-rulesets/[id]/page.js +35 -6
- package/dist/modules/planner/backend/planner/availability-rulesets/[id]/page.js.map +3 -3
- package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js +67 -47
- package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js +29 -6
- package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js +29 -6
- package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js +33 -4
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js +33 -4
- package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +37 -8
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +3 -3
- package/dist/modules/workflows/backend/definitions/[id]/page.js +24 -6
- package/dist/modules/workflows/backend/definitions/[id]/page.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/catalog/backend/catalog/categories/[id]/edit/page.tsx +39 -8
- package/src/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.tsx +38 -6
- package/src/modules/catalog/i18n/de.json +2 -0
- package/src/modules/catalog/i18n/en.json +2 -0
- package/src/modules/catalog/i18n/es.json +2 -0
- package/src/modules/catalog/i18n/pl.json +2 -0
- package/src/modules/directory/backend/directory/organizations/[id]/edit/page.tsx +30 -4
- package/src/modules/directory/backend/directory/tenants/[id]/edit/page.tsx +28 -6
- package/src/modules/directory/i18n/de.json +2 -0
- package/src/modules/directory/i18n/en.json +2 -0
- package/src/modules/directory/i18n/es.json +2 -0
- package/src/modules/directory/i18n/pl.json +2 -0
- package/src/modules/planner/backend/planner/availability-rulesets/[id]/page.tsx +44 -4
- package/src/modules/planner/i18n/de.json +1 -0
- package/src/modules/planner/i18n/en.json +1 -0
- package/src/modules/planner/i18n/es.json +1 -0
- package/src/modules/planner/i18n/pl.json +1 -0
- package/src/modules/sales/backend/sales/channels/[channelId]/edit/page.tsx +36 -7
- package/src/modules/sales/i18n/de.json +1 -0
- package/src/modules/sales/i18n/en.json +1 -0
- package/src/modules/sales/i18n/es.json +1 -0
- package/src/modules/sales/i18n/pl.json +1 -0
- package/src/modules/staff/backend/staff/leave-requests/[id]/page.tsx +33 -6
- package/src/modules/staff/backend/staff/my-leave-requests/[id]/page.tsx +33 -6
- package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +44 -4
- package/src/modules/staff/backend/staff/team-roles/[id]/edit/page.tsx +44 -4
- package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +44 -4
- package/src/modules/staff/i18n/de.json +5 -0
- package/src/modules/staff/i18n/en.json +5 -0
- package/src/modules/staff/i18n/es.json +5 -0
- package/src/modules/staff/i18n/pl.json +5 -0
- package/src/modules/workflows/backend/definitions/[id]/page.tsx +26 -3
- package/src/modules/workflows/i18n/de.json +1 -0
- package/src/modules/workflows/i18n/en.json +1 -0
- package/src/modules/workflows/i18n/es.json +1 -0
- package/src/modules/workflows/i18n/pl.json +1 -0
|
@@ -7,6 +7,7 @@ import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
|
|
|
7
7
|
import { updateCrud, deleteCrud } from "@open-mercato/ui/backend/utils/crud";
|
|
8
8
|
import { createCrudFormError } from "@open-mercato/ui/backend/utils/serverErrors";
|
|
9
9
|
import { collectCustomFieldValues } from "@open-mercato/ui/backend/utils/customFieldValues";
|
|
10
|
+
import { ErrorMessage, RecordNotFoundState } from "@open-mercato/ui/backend/detail";
|
|
10
11
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
11
12
|
import { extractCustomFieldEntries } from "@open-mercato/shared/lib/crud/custom-fields-client";
|
|
12
13
|
import { E } from "../../../../../../../generated/entities.ids.generated.js";
|
|
@@ -46,19 +47,30 @@ function EditCatalogCategoryPage({ params }) {
|
|
|
46
47
|
const [pathLabel, setPathLabel] = React.useState("");
|
|
47
48
|
const [loading, setLoading] = React.useState(true);
|
|
48
49
|
const [error, setError] = React.useState(null);
|
|
50
|
+
const [isNotFound, setIsNotFound] = React.useState(false);
|
|
49
51
|
React.useEffect(() => {
|
|
50
52
|
if (!categoryId) return;
|
|
51
53
|
let cancelled = false;
|
|
52
54
|
async function load() {
|
|
53
55
|
setLoading(true);
|
|
54
56
|
setError(null);
|
|
57
|
+
setIsNotFound(false);
|
|
55
58
|
try {
|
|
56
|
-
const { ok, result } = await apiCall(
|
|
59
|
+
const { ok, status, result } = await apiCall(
|
|
57
60
|
`/api/catalog/categories?view=manage&ids=${encodeURIComponent(categoryId)}&status=all&page=1&pageSize=1`
|
|
58
61
|
);
|
|
59
|
-
if (!ok)
|
|
62
|
+
if (!ok) {
|
|
63
|
+
if (status === 404) {
|
|
64
|
+
if (!cancelled) setIsNotFound(true);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
throw new Error(t("catalog.categories.form.errors.load", "Failed to load category"));
|
|
68
|
+
}
|
|
60
69
|
const record = Array.isArray(result?.items) ? result.items?.[0] : null;
|
|
61
|
-
if (!record)
|
|
70
|
+
if (!record) {
|
|
71
|
+
if (!cancelled) setIsNotFound(true);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
62
74
|
if (cancelled) return;
|
|
63
75
|
const customValues = extractCustomFieldEntries(record);
|
|
64
76
|
setInitialValues({
|
|
@@ -144,50 +156,60 @@ function EditCatalogCategoryPage({ params }) {
|
|
|
144
156
|
if (!categoryId) {
|
|
145
157
|
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: t("catalog.categories.form.errors.idRequired", "Category identifier is required.") }) }) });
|
|
146
158
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
CrudForm,
|
|
159
|
+
if (isNotFound) {
|
|
160
|
+
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(
|
|
161
|
+
RecordNotFoundState,
|
|
151
162
|
{
|
|
152
|
-
|
|
163
|
+
label: t("catalog.categories.form.errors.notFound", "Category not found"),
|
|
153
164
|
backHref: "/backend/catalog/categories",
|
|
154
|
-
|
|
155
|
-
fields,
|
|
156
|
-
groups,
|
|
157
|
-
entityId: E.catalog.catalog_product_category,
|
|
158
|
-
initialValues: initialValues ?? { id: categoryId, name: "", slug: "", description: "", parentId: "", isActive: true },
|
|
159
|
-
isLoading: loading,
|
|
160
|
-
loadingMessage: t("catalog.categories.form.loading", "Loading category..."),
|
|
161
|
-
submitLabel: t("catalog.categories.form.action.save", "Save"),
|
|
162
|
-
cancelHref: "/backend/catalog/categories",
|
|
163
|
-
successRedirect: `/backend/catalog/categories?flash=${encodeURIComponent(t("catalog.categories.flash.updated", "Category updated"))}&type=success`,
|
|
164
|
-
extraActions: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
165
|
-
/* @__PURE__ */ jsx(
|
|
166
|
-
SendObjectMessageDialog,
|
|
167
|
-
{
|
|
168
|
-
object: {
|
|
169
|
-
entityModule: "catalog",
|
|
170
|
-
entityType: "category",
|
|
171
|
-
entityId: categoryId,
|
|
172
|
-
previewData: { title: initialValues?.name ?? categoryId }
|
|
173
|
-
},
|
|
174
|
-
viewHref: `/backend/catalog/categories/${categoryId}/edit`
|
|
175
|
-
}
|
|
176
|
-
),
|
|
177
|
-
pathLabel ? /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: t("catalog.categories.form.pathLabel", { path: pathLabel }) }) : null
|
|
178
|
-
] }),
|
|
179
|
-
onSubmit: async (values) => {
|
|
180
|
-
await submitCategoryUpdate(categoryId, values, t);
|
|
181
|
-
},
|
|
182
|
-
onDelete: async () => {
|
|
183
|
-
await deleteCrud("catalog/categories", categoryId, {
|
|
184
|
-
errorMessage: t("catalog.categories.form.errors.delete", "Failed to delete category")
|
|
185
|
-
});
|
|
186
|
-
},
|
|
187
|
-
deleteRedirect: `/backend/catalog/categories?flash=${encodeURIComponent(t("catalog.categories.flash.deleted", "Category archived"))}&type=success`
|
|
165
|
+
backLabel: t("catalog.categories.form.actions.backToList", "Back to categories")
|
|
188
166
|
}
|
|
189
|
-
)
|
|
190
|
-
|
|
167
|
+
) }) });
|
|
168
|
+
}
|
|
169
|
+
if (error && !loading) {
|
|
170
|
+
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(ErrorMessage, { label: error }) }) });
|
|
171
|
+
}
|
|
172
|
+
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(
|
|
173
|
+
CrudForm,
|
|
174
|
+
{
|
|
175
|
+
title: t("catalog.categories.form.editTitle", "Edit category"),
|
|
176
|
+
backHref: "/backend/catalog/categories",
|
|
177
|
+
versionHistory: { resourceKind: "catalog.category", resourceId: categoryId ? String(categoryId) : "" },
|
|
178
|
+
fields,
|
|
179
|
+
groups,
|
|
180
|
+
entityId: E.catalog.catalog_product_category,
|
|
181
|
+
initialValues: initialValues ?? { id: categoryId, name: "", slug: "", description: "", parentId: "", isActive: true },
|
|
182
|
+
isLoading: loading,
|
|
183
|
+
loadingMessage: t("catalog.categories.form.loading", "Loading category..."),
|
|
184
|
+
submitLabel: t("catalog.categories.form.action.save", "Save"),
|
|
185
|
+
cancelHref: "/backend/catalog/categories",
|
|
186
|
+
successRedirect: `/backend/catalog/categories?flash=${encodeURIComponent(t("catalog.categories.flash.updated", "Category updated"))}&type=success`,
|
|
187
|
+
extraActions: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
188
|
+
/* @__PURE__ */ jsx(
|
|
189
|
+
SendObjectMessageDialog,
|
|
190
|
+
{
|
|
191
|
+
object: {
|
|
192
|
+
entityModule: "catalog",
|
|
193
|
+
entityType: "category",
|
|
194
|
+
entityId: categoryId,
|
|
195
|
+
previewData: { title: initialValues?.name ?? categoryId }
|
|
196
|
+
},
|
|
197
|
+
viewHref: `/backend/catalog/categories/${categoryId}/edit`
|
|
198
|
+
}
|
|
199
|
+
),
|
|
200
|
+
pathLabel ? /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: t("catalog.categories.form.pathLabel", { path: pathLabel }) }) : null
|
|
201
|
+
] }),
|
|
202
|
+
onSubmit: async (values) => {
|
|
203
|
+
await submitCategoryUpdate(categoryId, values, t);
|
|
204
|
+
},
|
|
205
|
+
onDelete: async () => {
|
|
206
|
+
await deleteCrud("catalog/categories", categoryId, {
|
|
207
|
+
errorMessage: t("catalog.categories.form.errors.delete", "Failed to delete category")
|
|
208
|
+
});
|
|
209
|
+
},
|
|
210
|
+
deleteRedirect: `/backend/catalog/categories?flash=${encodeURIComponent(t("catalog.categories.flash.deleted", "Category archived"))}&type=success`
|
|
211
|
+
}
|
|
212
|
+
) }) });
|
|
191
213
|
}
|
|
192
214
|
export {
|
|
193
215
|
EditCatalogCategoryPage as default
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../../src/modules/catalog/backend/catalog/categories/%5Bid%5D/edit/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm, type CrudField, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { extractCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields-client'\nimport { E } from '#generated/entities.ids.generated'\nimport { CategorySelect } from '../../../../../components/categories/CategorySelect'\nimport { CategorySlugFieldSync } from '../../../../../components/categories/CategorySlugFieldSync'\nimport { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'\n\ntype CategoryRow = {\n id: string\n name: string\n slug: string | null\n description: string | null\n parentId: string | null\n isActive: boolean\n pathLabel?: string\n}\n\ntype CategoryResponse = {\n items?: CategoryRow[]\n}\n\ntype CategoryFormValues = {\n id?: string\n name: string\n slug?: string\n description?: string\n parentId?: string | null\n isActive?: boolean\n}\n\nasync function submitCategoryUpdate(\n categoryId: string,\n values: CategoryFormValues,\n t: (key: string, fallback?: string) => string,\n) {\n const resolvedId = typeof values.id === 'string' && values.id.length ? values.id : categoryId\n if (!resolvedId) {\n const message = t('catalog.categories.form.errors.idRequired', 'Category identifier is required.')\n throw createCrudFormError(message, { id: message })\n }\n const name = typeof values.name === 'string' ? values.name.trim() : ''\n if (!name) {\n const message = t('catalog.categories.form.errors.name', 'Provide the category name.')\n throw createCrudFormError(message, { name: message })\n }\n const slug = typeof values.slug === 'string' && values.slug.trim().length ? values.slug.trim() : undefined\n const description =\n typeof values.description === 'string' && values.description.trim().length\n ? values.description.trim()\n : undefined\n const parentId =\n typeof values.parentId === 'string' && values.parentId.trim().length\n ? values.parentId.trim()\n : null\n const customFields = collectCustomFieldValues(values as Record<string, unknown>)\n const payload: Record<string, unknown> = {\n id: resolvedId,\n name,\n slug,\n description,\n parentId,\n isActive: values.isActive !== false,\n }\n if (Object.keys(customFields).length > 0) payload.customFields = customFields\n await updateCrud('catalog/categories', payload)\n}\n\nexport default function EditCatalogCategoryPage({ params }: { params?: { id?: string } }) {\n const categoryId = params?.id ?? ''\n const t = useT()\n const [initialValues, setInitialValues] = React.useState<CategoryFormValues | null>(null)\n const [pathLabel, setPathLabel] = React.useState<string>('')\n const [loading, setLoading] = React.useState<boolean>(true)\n const [error, setError] = React.useState<string | null>(null)\n\n React.useEffect(() => {\n if (!categoryId) return\n let cancelled = false\n async function load() {\n setLoading(true)\n setError(null)\n try {\n const { ok, result } = await apiCall<CategoryResponse>(\n `/api/catalog/categories?view=manage&ids=${encodeURIComponent(categoryId)}&status=all&page=1&pageSize=1`,\n )\n if (!ok) throw new Error(t('catalog.categories.form.errors.load', 'Failed to load category'))\n const record = Array.isArray(result?.items) ? result.items?.[0] : null\n if (!record) throw new Error(t('catalog.categories.form.errors.notFound', 'Category not found'))\n if (cancelled) return\n const customValues = extractCustomFieldEntries(record as Record<string, unknown>)\n setInitialValues({\n id: record.id,\n name: record.name,\n slug: record.slug ?? '',\n description: record.description ?? '',\n parentId: record.parentId ?? '',\n isActive: record.isActive,\n ...customValues,\n })\n setPathLabel(record.pathLabel ?? '')\n } catch (err) {\n if (!cancelled) {\n const fallback = t('catalog.categories.form.errors.load', 'Failed to load category')\n const message = err instanceof Error ? err.message : fallback\n setError(message)\n }\n } finally {\n if (!cancelled) setLoading(false)\n }\n }\n load()\n return () => {\n cancelled = true\n }\n }, [categoryId, t])\n\n const fields = React.useMemo<CrudField[]>(() => [\n {\n id: 'name',\n label: t('catalog.categories.form.field.name', 'Name'),\n type: 'text',\n required: true,\n placeholder: t('catalog.categories.form.field.namePlaceholder', 'e.g., Footwear'),\n },\n {\n id: 'slug',\n label: t('catalog.categories.form.field.slug', 'Slug'),\n type: 'text',\n description: t('catalog.categories.form.field.slugHelp', 'Lowercase identifier for URLs or imports.'),\n },\n {\n id: 'description',\n label: t('catalog.categories.form.field.description', 'Description'),\n type: 'textarea',\n },\n {\n id: 'parentId',\n label: t('catalog.categories.form.field.parent', 'Parent'),\n type: 'custom',\n component: ({ id, value, setValue }) => (\n <CategorySelect\n id={id}\n value={typeof value === 'string' ? value : null}\n onChange={(next) => setValue(next ?? '')}\n includeEmptyOption\n className=\"w-full h-9 rounded border px-2 text-sm\"\n />\n ),\n },\n {\n id: 'isActive',\n label: t('catalog.categories.form.field.isActive', 'Active'),\n type: 'checkbox',\n },\n ], [t])\n\n const groups = React.useMemo<CrudFormGroup[]>(() => [\n {\n id: 'details',\n title: t('catalog.categories.form.group.details', 'Details'),\n column: 1,\n fields: ['name', 'slug', 'description', 'parentId', 'isActive'],\n component: (ctx) => <CategorySlugFieldSync {...ctx} />,\n },\n {\n id: 'custom',\n title: t('catalog.categories.form.group.custom', 'Custom data'),\n column: 2,\n kind: 'customFields',\n },\n ], [t])\n\n if (!categoryId) {\n return (\n <Page>\n <PageBody>\n <p className=\"text-sm text-destructive\">\n {t('catalog.categories.form.errors.idRequired', 'Category identifier is required.')}\n </p>\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody>\n {error ? (\n <div className=\"mb-4 rounded border border-destructive/50 bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n {error}\n </div>\n ) : null}\n <CrudForm<CategoryFormValues>\n title={t('catalog.categories.form.editTitle', 'Edit category')}\n backHref=\"/backend/catalog/categories\"\n versionHistory={{ resourceKind: 'catalog.category', resourceId: categoryId ? String(categoryId) : '' }}\n fields={fields}\n groups={groups}\n entityId={E.catalog.catalog_product_category}\n initialValues={initialValues ?? { id: categoryId, name: '', slug: '', description: '', parentId: '', isActive: true }}\n isLoading={loading}\n loadingMessage={t('catalog.categories.form.loading', 'Loading category...')}\n submitLabel={t('catalog.categories.form.action.save', 'Save')}\n cancelHref=\"/backend/catalog/categories\"\n successRedirect={`/backend/catalog/categories?flash=${encodeURIComponent(t('catalog.categories.flash.updated', 'Category updated'))}&type=success`}\n extraActions={(\n <>\n <SendObjectMessageDialog\n object={{\n entityModule: 'catalog',\n entityType: 'category',\n entityId: categoryId,\n previewData: { title: initialValues?.name ?? categoryId },\n }}\n viewHref={`/backend/catalog/categories/${categoryId}/edit`}\n />\n {pathLabel ? (\n <span className=\"text-xs text-muted-foreground\">\n {t('catalog.categories.form.pathLabel', { path: pathLabel })}\n </span>\n ) : null}\n </>\n )}\n onSubmit={async (values) => {\n await submitCategoryUpdate(categoryId, values, t)\n }}\n onDelete={async () => {\n await deleteCrud('catalog/categories', categoryId, {\n errorMessage: t('catalog.categories.form.errors.delete', 'Failed to delete category'),\n })\n }}\n deleteRedirect={`/backend/catalog/categories?flash=${encodeURIComponent(t('catalog.categories.flash.deleted', 'Category archived'))}&type=success`}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm, type CrudField, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'\nimport { ErrorMessage, RecordNotFoundState } from '@open-mercato/ui/backend/detail'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { extractCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields-client'\nimport { E } from '#generated/entities.ids.generated'\nimport { CategorySelect } from '../../../../../components/categories/CategorySelect'\nimport { CategorySlugFieldSync } from '../../../../../components/categories/CategorySlugFieldSync'\nimport { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'\n\ntype CategoryRow = {\n id: string\n name: string\n slug: string | null\n description: string | null\n parentId: string | null\n isActive: boolean\n pathLabel?: string\n}\n\ntype CategoryResponse = {\n items?: CategoryRow[]\n}\n\ntype CategoryFormValues = {\n id?: string\n name: string\n slug?: string\n description?: string\n parentId?: string | null\n isActive?: boolean\n}\n\nasync function submitCategoryUpdate(\n categoryId: string,\n values: CategoryFormValues,\n t: (key: string, fallback?: string) => string,\n) {\n const resolvedId = typeof values.id === 'string' && values.id.length ? values.id : categoryId\n if (!resolvedId) {\n const message = t('catalog.categories.form.errors.idRequired', 'Category identifier is required.')\n throw createCrudFormError(message, { id: message })\n }\n const name = typeof values.name === 'string' ? values.name.trim() : ''\n if (!name) {\n const message = t('catalog.categories.form.errors.name', 'Provide the category name.')\n throw createCrudFormError(message, { name: message })\n }\n const slug = typeof values.slug === 'string' && values.slug.trim().length ? values.slug.trim() : undefined\n const description =\n typeof values.description === 'string' && values.description.trim().length\n ? values.description.trim()\n : undefined\n const parentId =\n typeof values.parentId === 'string' && values.parentId.trim().length\n ? values.parentId.trim()\n : null\n const customFields = collectCustomFieldValues(values as Record<string, unknown>)\n const payload: Record<string, unknown> = {\n id: resolvedId,\n name,\n slug,\n description,\n parentId,\n isActive: values.isActive !== false,\n }\n if (Object.keys(customFields).length > 0) payload.customFields = customFields\n await updateCrud('catalog/categories', payload)\n}\n\nexport default function EditCatalogCategoryPage({ params }: { params?: { id?: string } }) {\n const categoryId = params?.id ?? ''\n const t = useT()\n const [initialValues, setInitialValues] = React.useState<CategoryFormValues | null>(null)\n const [pathLabel, setPathLabel] = React.useState<string>('')\n const [loading, setLoading] = React.useState<boolean>(true)\n const [error, setError] = React.useState<string | null>(null)\n const [isNotFound, setIsNotFound] = React.useState<boolean>(false)\n\n React.useEffect(() => {\n if (!categoryId) return\n let cancelled = false\n async function load() {\n setLoading(true)\n setError(null)\n setIsNotFound(false)\n try {\n const { ok, status, result } = await apiCall<CategoryResponse>(\n `/api/catalog/categories?view=manage&ids=${encodeURIComponent(categoryId)}&status=all&page=1&pageSize=1`,\n )\n if (!ok) {\n if (status === 404) {\n if (!cancelled) setIsNotFound(true)\n return\n }\n throw new Error(t('catalog.categories.form.errors.load', 'Failed to load category'))\n }\n const record = Array.isArray(result?.items) ? result.items?.[0] : null\n if (!record) {\n if (!cancelled) setIsNotFound(true)\n return\n }\n if (cancelled) return\n const customValues = extractCustomFieldEntries(record as Record<string, unknown>)\n setInitialValues({\n id: record.id,\n name: record.name,\n slug: record.slug ?? '',\n description: record.description ?? '',\n parentId: record.parentId ?? '',\n isActive: record.isActive,\n ...customValues,\n })\n setPathLabel(record.pathLabel ?? '')\n } catch (err) {\n if (!cancelled) {\n const fallback = t('catalog.categories.form.errors.load', 'Failed to load category')\n const message = err instanceof Error ? err.message : fallback\n setError(message)\n }\n } finally {\n if (!cancelled) setLoading(false)\n }\n }\n load()\n return () => {\n cancelled = true\n }\n }, [categoryId, t])\n\n const fields = React.useMemo<CrudField[]>(() => [\n {\n id: 'name',\n label: t('catalog.categories.form.field.name', 'Name'),\n type: 'text',\n required: true,\n placeholder: t('catalog.categories.form.field.namePlaceholder', 'e.g., Footwear'),\n },\n {\n id: 'slug',\n label: t('catalog.categories.form.field.slug', 'Slug'),\n type: 'text',\n description: t('catalog.categories.form.field.slugHelp', 'Lowercase identifier for URLs or imports.'),\n },\n {\n id: 'description',\n label: t('catalog.categories.form.field.description', 'Description'),\n type: 'textarea',\n },\n {\n id: 'parentId',\n label: t('catalog.categories.form.field.parent', 'Parent'),\n type: 'custom',\n component: ({ id, value, setValue }) => (\n <CategorySelect\n id={id}\n value={typeof value === 'string' ? value : null}\n onChange={(next) => setValue(next ?? '')}\n includeEmptyOption\n className=\"w-full h-9 rounded border px-2 text-sm\"\n />\n ),\n },\n {\n id: 'isActive',\n label: t('catalog.categories.form.field.isActive', 'Active'),\n type: 'checkbox',\n },\n ], [t])\n\n const groups = React.useMemo<CrudFormGroup[]>(() => [\n {\n id: 'details',\n title: t('catalog.categories.form.group.details', 'Details'),\n column: 1,\n fields: ['name', 'slug', 'description', 'parentId', 'isActive'],\n component: (ctx) => <CategorySlugFieldSync {...ctx} />,\n },\n {\n id: 'custom',\n title: t('catalog.categories.form.group.custom', 'Custom data'),\n column: 2,\n kind: 'customFields',\n },\n ], [t])\n\n if (!categoryId) {\n return (\n <Page>\n <PageBody>\n <p className=\"text-sm text-destructive\">\n {t('catalog.categories.form.errors.idRequired', 'Category identifier is required.')}\n </p>\n </PageBody>\n </Page>\n )\n }\n\n if (isNotFound) {\n return (\n <Page>\n <PageBody>\n <RecordNotFoundState\n label={t('catalog.categories.form.errors.notFound', 'Category not found')}\n backHref=\"/backend/catalog/categories\"\n backLabel={t('catalog.categories.form.actions.backToList', 'Back to categories')}\n />\n </PageBody>\n </Page>\n )\n }\n\n if (error && !loading) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage label={error} />\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <CrudForm<CategoryFormValues>\n title={t('catalog.categories.form.editTitle', 'Edit category')}\n backHref=\"/backend/catalog/categories\"\n versionHistory={{ resourceKind: 'catalog.category', resourceId: categoryId ? String(categoryId) : '' }}\n fields={fields}\n groups={groups}\n entityId={E.catalog.catalog_product_category}\n initialValues={initialValues ?? { id: categoryId, name: '', slug: '', description: '', parentId: '', isActive: true }}\n isLoading={loading}\n loadingMessage={t('catalog.categories.form.loading', 'Loading category...')}\n submitLabel={t('catalog.categories.form.action.save', 'Save')}\n cancelHref=\"/backend/catalog/categories\"\n successRedirect={`/backend/catalog/categories?flash=${encodeURIComponent(t('catalog.categories.flash.updated', 'Category updated'))}&type=success`}\n extraActions={(\n <>\n <SendObjectMessageDialog\n object={{\n entityModule: 'catalog',\n entityType: 'category',\n entityId: categoryId,\n previewData: { title: initialValues?.name ?? categoryId },\n }}\n viewHref={`/backend/catalog/categories/${categoryId}/edit`}\n />\n {pathLabel ? (\n <span className=\"text-xs text-muted-foreground\">\n {t('catalog.categories.form.pathLabel', { path: pathLabel })}\n </span>\n ) : null}\n </>\n )}\n onSubmit={async (values) => {\n await submitCategoryUpdate(categoryId, values, t)\n }}\n onDelete={async () => {\n await deleteCrud('catalog/categories', categoryId, {\n errorMessage: t('catalog.categories.form.errors.delete', 'Failed to delete category'),\n })\n }}\n deleteRedirect={`/backend/catalog/categories?flash=${encodeURIComponent(t('catalog.categories.flash.deleted', 'Category archived'))}&type=success`}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { useRouter } from "next/navigation";
|
|
5
5
|
import { Page, PageBody } from "@open-mercato/ui/backend/Page";
|
|
@@ -9,7 +9,7 @@ import { createCrudFormError } from "@open-mercato/ui/backend/utils/serverErrors
|
|
|
9
9
|
import { collectCustomFieldValues } from "@open-mercato/ui/backend/utils/customFieldValues";
|
|
10
10
|
import { apiCall, readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
|
|
11
11
|
import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
12
|
-
import { ErrorMessage } from "@open-mercato/ui/backend/detail";
|
|
12
|
+
import { ErrorMessage, RecordNotFoundState } from "@open-mercato/ui/backend/detail";
|
|
13
13
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
14
14
|
import { extractCustomFieldEntries } from "@open-mercato/shared/lib/crud/custom-fields-client";
|
|
15
15
|
import { E } from "../../../../../../../../generated/entities.ids.generated.js";
|
|
@@ -59,6 +59,7 @@ function EditVariantPage({ params }) {
|
|
|
59
59
|
const [existingPriceIds, setExistingPriceIds] = React.useState({});
|
|
60
60
|
const [loading, setLoading] = React.useState(true);
|
|
61
61
|
const [error, setError] = React.useState(null);
|
|
62
|
+
const [isNotFound, setIsNotFound] = React.useState(false);
|
|
62
63
|
const [currentProductId, setCurrentProductId] = React.useState(productId);
|
|
63
64
|
const [productTitle, setProductTitle] = React.useState("");
|
|
64
65
|
const [productTaxRateId, setProductTaxRateId] = React.useState(null);
|
|
@@ -118,13 +119,23 @@ function EditVariantPage({ params }) {
|
|
|
118
119
|
async function load() {
|
|
119
120
|
setLoading(true);
|
|
120
121
|
setError(null);
|
|
122
|
+
setIsNotFound(false);
|
|
121
123
|
try {
|
|
122
124
|
const variantRes = await apiCall(
|
|
123
125
|
`/api/catalog/variants?id=${encodeURIComponent(variantId)}&page=1&pageSize=1`
|
|
124
126
|
);
|
|
125
|
-
if (!variantRes.ok)
|
|
127
|
+
if (!variantRes.ok) {
|
|
128
|
+
if (variantRes.status === 404) {
|
|
129
|
+
if (!cancelled) setIsNotFound(true);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
throw new Error(t("catalog.variants.form.errors.load", "Failed to load variant."));
|
|
133
|
+
}
|
|
126
134
|
const record = Array.isArray(variantRes.result?.items) ? variantRes.result?.items?.[0] : void 0;
|
|
127
|
-
if (!record)
|
|
135
|
+
if (!record) {
|
|
136
|
+
if (!cancelled) setIsNotFound(true);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
128
139
|
const resolvedProductId = typeof record.product_id === "string" ? record.product_id : typeof record.productId === "string" ? record.productId : currentProductId;
|
|
129
140
|
if (resolvedProductId) setCurrentProductId(resolvedProductId);
|
|
130
141
|
const metadata = typeof record.metadata === "object" && record.metadata ? { ...record.metadata } : {};
|
|
@@ -291,108 +302,118 @@ function EditVariantPage({ params }) {
|
|
|
291
302
|
}
|
|
292
303
|
const formTitle = productTitle ? t("catalog.variants.form.editTitleFor", "Edit variant \u2022 {{title}}").replace("{{title}}", productTitle) : t("catalog.variants.form.editTitle", "Edit variant");
|
|
293
304
|
const productVariantsHref = `/backend/catalog/products/${currentProductId}#variants`;
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
CrudForm,
|
|
305
|
+
if (isNotFound) {
|
|
306
|
+
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(
|
|
307
|
+
RecordNotFoundState,
|
|
298
308
|
{
|
|
299
|
-
|
|
309
|
+
label: t("catalog.variants.form.errors.notFound", "Variant not found."),
|
|
300
310
|
backHref: productVariantsHref,
|
|
301
|
-
|
|
302
|
-
extraActions: /* @__PURE__ */ jsx(
|
|
303
|
-
SendObjectMessageDialog,
|
|
304
|
-
{
|
|
305
|
-
object: {
|
|
306
|
-
entityModule: "catalog",
|
|
307
|
-
entityType: "variant",
|
|
308
|
-
entityId: variantId,
|
|
309
|
-
previewData: {
|
|
310
|
-
title: typeof initialValues?.name === "string" && initialValues.name.trim().length ? initialValues.name : variantId,
|
|
311
|
-
metadata: {
|
|
312
|
-
[t("catalog.variants.form.skuLabel")]: typeof initialValues?.sku === "string" && initialValues.sku.trim().length ? initialValues.sku : "-",
|
|
313
|
-
[t("catalog.variants.form.pricesLabel")]: resolveVariantPriceLabel(initialValues?.prices) ?? "-"
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
},
|
|
317
|
-
viewHref: `/backend/catalog/products/${currentProductId}/variants/${variantId}`
|
|
318
|
-
}
|
|
319
|
-
),
|
|
320
|
-
fields: [],
|
|
321
|
-
groups,
|
|
322
|
-
entityId: E.catalog.catalog_product_variant,
|
|
323
|
-
customFieldsetBindings: { [E.catalog.catalog_product_variant]: { valueKey: "customFieldsetCode" } },
|
|
324
|
-
initialValues: initialValues ?? void 0,
|
|
325
|
-
isLoading: loading,
|
|
326
|
-
loadingMessage: t("catalog.variants.form.loading", "Loading variant..."),
|
|
327
|
-
submitLabel: t("catalog.variants.form.save", "Save changes"),
|
|
328
|
-
cancelHref: productVariantsHref,
|
|
329
|
-
onSubmit: async (values) => {
|
|
330
|
-
const name = values.name?.trim();
|
|
331
|
-
if (!name) {
|
|
332
|
-
const message = t("catalog.variants.form.errors.nameRequired", "Provide the variant name.");
|
|
333
|
-
throw createCrudFormError(message, { name: message });
|
|
334
|
-
}
|
|
335
|
-
const invalidPriceKinds = findInvalidVariantPriceKinds(priceKinds, values.prices);
|
|
336
|
-
if (invalidPriceKinds.length) {
|
|
337
|
-
const message = t("catalog.variants.form.errors.invalidPrice", "Provide a valid non-negative price.");
|
|
338
|
-
throw createCrudFormError(message, { prices: message });
|
|
339
|
-
}
|
|
340
|
-
const resolveTaxRateValue = (taxRateId) => {
|
|
341
|
-
if (!taxRateId) return null;
|
|
342
|
-
const match = taxRates.find((rate) => rate.id === taxRateId);
|
|
343
|
-
return typeof match?.rate === "number" && Number.isFinite(match.rate) ? match.rate : null;
|
|
344
|
-
};
|
|
345
|
-
const resolvedTaxRateId = values.taxRateId ?? productTaxRateId ?? null;
|
|
346
|
-
const resolvedTaxRateValue = values.taxRateId && resolvedTaxRateId ? resolveTaxRateValue(resolvedTaxRateId) : productTaxRateId ? resolveTaxRateValue(productTaxRateId) ?? productTaxRate : productTaxRate ?? null;
|
|
347
|
-
const metadata = typeof values.metadata === "object" && values.metadata ? { ...values.metadata } : {};
|
|
348
|
-
const defaultMediaEntry = values.defaultMediaId ? (Array.isArray(values.mediaItems) ? values.mediaItems : []).find((item) => item.id === values.defaultMediaId) : null;
|
|
349
|
-
const defaultMediaUrl = defaultMediaEntry ? buildAttachmentImageUrl(defaultMediaEntry.id, {
|
|
350
|
-
slug: slugifyAttachmentFileName(defaultMediaEntry.fileName)
|
|
351
|
-
}) : null;
|
|
352
|
-
const payload = {
|
|
353
|
-
id: variantId,
|
|
354
|
-
productId: currentProductId,
|
|
355
|
-
name,
|
|
356
|
-
sku: values.sku?.trim() || void 0,
|
|
357
|
-
barcode: values.barcode?.trim() || void 0,
|
|
358
|
-
isDefault: Boolean(values.isDefault),
|
|
359
|
-
isActive: values.isActive !== false,
|
|
360
|
-
optionValues: Object.keys(values.optionValues ?? {}).length ? values.optionValues : void 0,
|
|
361
|
-
metadata,
|
|
362
|
-
defaultMediaId: values.defaultMediaId ?? void 0,
|
|
363
|
-
defaultMediaUrl: defaultMediaUrl ?? void 0,
|
|
364
|
-
customFieldsetCode: values.customFieldsetCode?.trim().length ? values.customFieldsetCode : void 0,
|
|
365
|
-
taxRateId: resolvedTaxRateId,
|
|
366
|
-
taxRate: resolvedTaxRateValue
|
|
367
|
-
};
|
|
368
|
-
const customFields = collectCustomFieldValues(values);
|
|
369
|
-
if (Object.keys(customFields).length) payload.customFields = customFields;
|
|
370
|
-
await updateCrud("catalog/variants", payload);
|
|
371
|
-
await syncVariantPricesUpdate({
|
|
372
|
-
priceKinds,
|
|
373
|
-
priceDrafts: values.prices ?? {},
|
|
374
|
-
existingPriceIds,
|
|
375
|
-
productId: currentProductId,
|
|
376
|
-
variantId,
|
|
377
|
-
taxRates,
|
|
378
|
-
taxRateId: values.taxRateId,
|
|
379
|
-
productTaxRateId,
|
|
380
|
-
productTaxRate
|
|
381
|
-
});
|
|
382
|
-
flash(t("catalog.variants.form.updated", "Variant updated."), "success");
|
|
383
|
-
router.push(productVariantsHref);
|
|
384
|
-
},
|
|
385
|
-
onDelete: async () => {
|
|
386
|
-
await deleteCrud("catalog/variants", variantId, {
|
|
387
|
-
errorMessage: t("catalog.variants.form.deleteError", "Failed to delete variant.")
|
|
388
|
-
});
|
|
389
|
-
flash(t("catalog.variants.form.deleted", "Variant deleted."), "success");
|
|
390
|
-
router.push(productVariantsHref);
|
|
391
|
-
},
|
|
392
|
-
deleteRedirect: productVariantsHref
|
|
311
|
+
backLabel: t("catalog.variants.form.actions.backToProduct", "Back to product variants")
|
|
393
312
|
}
|
|
394
|
-
)
|
|
395
|
-
|
|
313
|
+
) }) });
|
|
314
|
+
}
|
|
315
|
+
if (error && !loading) {
|
|
316
|
+
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(ErrorMessage, { label: error }) }) });
|
|
317
|
+
}
|
|
318
|
+
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(
|
|
319
|
+
CrudForm,
|
|
320
|
+
{
|
|
321
|
+
title: formTitle,
|
|
322
|
+
backHref: productVariantsHref,
|
|
323
|
+
versionHistory: { resourceKind: "catalog.variant", resourceId: variantId ? String(variantId) : "" },
|
|
324
|
+
extraActions: /* @__PURE__ */ jsx(
|
|
325
|
+
SendObjectMessageDialog,
|
|
326
|
+
{
|
|
327
|
+
object: {
|
|
328
|
+
entityModule: "catalog",
|
|
329
|
+
entityType: "variant",
|
|
330
|
+
entityId: variantId,
|
|
331
|
+
previewData: {
|
|
332
|
+
title: typeof initialValues?.name === "string" && initialValues.name.trim().length ? initialValues.name : variantId,
|
|
333
|
+
metadata: {
|
|
334
|
+
[t("catalog.variants.form.skuLabel")]: typeof initialValues?.sku === "string" && initialValues.sku.trim().length ? initialValues.sku : "-",
|
|
335
|
+
[t("catalog.variants.form.pricesLabel")]: resolveVariantPriceLabel(initialValues?.prices) ?? "-"
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
viewHref: `/backend/catalog/products/${currentProductId}/variants/${variantId}`
|
|
340
|
+
}
|
|
341
|
+
),
|
|
342
|
+
fields: [],
|
|
343
|
+
groups,
|
|
344
|
+
entityId: E.catalog.catalog_product_variant,
|
|
345
|
+
customFieldsetBindings: { [E.catalog.catalog_product_variant]: { valueKey: "customFieldsetCode" } },
|
|
346
|
+
initialValues: initialValues ?? void 0,
|
|
347
|
+
isLoading: loading,
|
|
348
|
+
loadingMessage: t("catalog.variants.form.loading", "Loading variant..."),
|
|
349
|
+
submitLabel: t("catalog.variants.form.save", "Save changes"),
|
|
350
|
+
cancelHref: productVariantsHref,
|
|
351
|
+
onSubmit: async (values) => {
|
|
352
|
+
const name = values.name?.trim();
|
|
353
|
+
if (!name) {
|
|
354
|
+
const message = t("catalog.variants.form.errors.nameRequired", "Provide the variant name.");
|
|
355
|
+
throw createCrudFormError(message, { name: message });
|
|
356
|
+
}
|
|
357
|
+
const invalidPriceKinds = findInvalidVariantPriceKinds(priceKinds, values.prices);
|
|
358
|
+
if (invalidPriceKinds.length) {
|
|
359
|
+
const message = t("catalog.variants.form.errors.invalidPrice", "Provide a valid non-negative price.");
|
|
360
|
+
throw createCrudFormError(message, { prices: message });
|
|
361
|
+
}
|
|
362
|
+
const resolveTaxRateValue = (taxRateId) => {
|
|
363
|
+
if (!taxRateId) return null;
|
|
364
|
+
const match = taxRates.find((rate) => rate.id === taxRateId);
|
|
365
|
+
return typeof match?.rate === "number" && Number.isFinite(match.rate) ? match.rate : null;
|
|
366
|
+
};
|
|
367
|
+
const resolvedTaxRateId = values.taxRateId ?? productTaxRateId ?? null;
|
|
368
|
+
const resolvedTaxRateValue = values.taxRateId && resolvedTaxRateId ? resolveTaxRateValue(resolvedTaxRateId) : productTaxRateId ? resolveTaxRateValue(productTaxRateId) ?? productTaxRate : productTaxRate ?? null;
|
|
369
|
+
const metadata = typeof values.metadata === "object" && values.metadata ? { ...values.metadata } : {};
|
|
370
|
+
const defaultMediaEntry = values.defaultMediaId ? (Array.isArray(values.mediaItems) ? values.mediaItems : []).find((item) => item.id === values.defaultMediaId) : null;
|
|
371
|
+
const defaultMediaUrl = defaultMediaEntry ? buildAttachmentImageUrl(defaultMediaEntry.id, {
|
|
372
|
+
slug: slugifyAttachmentFileName(defaultMediaEntry.fileName)
|
|
373
|
+
}) : null;
|
|
374
|
+
const payload = {
|
|
375
|
+
id: variantId,
|
|
376
|
+
productId: currentProductId,
|
|
377
|
+
name,
|
|
378
|
+
sku: values.sku?.trim() || void 0,
|
|
379
|
+
barcode: values.barcode?.trim() || void 0,
|
|
380
|
+
isDefault: Boolean(values.isDefault),
|
|
381
|
+
isActive: values.isActive !== false,
|
|
382
|
+
optionValues: Object.keys(values.optionValues ?? {}).length ? values.optionValues : void 0,
|
|
383
|
+
metadata,
|
|
384
|
+
defaultMediaId: values.defaultMediaId ?? void 0,
|
|
385
|
+
defaultMediaUrl: defaultMediaUrl ?? void 0,
|
|
386
|
+
customFieldsetCode: values.customFieldsetCode?.trim().length ? values.customFieldsetCode : void 0,
|
|
387
|
+
taxRateId: resolvedTaxRateId,
|
|
388
|
+
taxRate: resolvedTaxRateValue
|
|
389
|
+
};
|
|
390
|
+
const customFields = collectCustomFieldValues(values);
|
|
391
|
+
if (Object.keys(customFields).length) payload.customFields = customFields;
|
|
392
|
+
await updateCrud("catalog/variants", payload);
|
|
393
|
+
await syncVariantPricesUpdate({
|
|
394
|
+
priceKinds,
|
|
395
|
+
priceDrafts: values.prices ?? {},
|
|
396
|
+
existingPriceIds,
|
|
397
|
+
productId: currentProductId,
|
|
398
|
+
variantId,
|
|
399
|
+
taxRates,
|
|
400
|
+
taxRateId: values.taxRateId,
|
|
401
|
+
productTaxRateId,
|
|
402
|
+
productTaxRate
|
|
403
|
+
});
|
|
404
|
+
flash(t("catalog.variants.form.updated", "Variant updated."), "success");
|
|
405
|
+
router.push(productVariantsHref);
|
|
406
|
+
},
|
|
407
|
+
onDelete: async () => {
|
|
408
|
+
await deleteCrud("catalog/variants", variantId, {
|
|
409
|
+
errorMessage: t("catalog.variants.form.deleteError", "Failed to delete variant.")
|
|
410
|
+
});
|
|
411
|
+
flash(t("catalog.variants.form.deleted", "Variant deleted."), "success");
|
|
412
|
+
router.push(productVariantsHref);
|
|
413
|
+
},
|
|
414
|
+
deleteRedirect: productVariantsHref
|
|
415
|
+
}
|
|
416
|
+
) }) });
|
|
396
417
|
}
|
|
397
418
|
function reconcileOptionValues(optionValues, optionDefinitions) {
|
|
398
419
|
if (!optionValues || !optionDefinitions.length) {
|