@open-mercato/core 0.6.7-develop.6828.1.ab1620a63e → 0.6.7-develop.6842.1.8cd8a8883c
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/.turbo/turbo-build.log +1 -1
- package/dist/modules/attachments/fields/attachment.js +1 -1
- package/dist/modules/attachments/fields/attachment.js.map +2 -2
- package/dist/modules/auth/components/UserConsentsPanel.js +9 -9
- package/dist/modules/auth/components/UserConsentsPanel.js.map +2 -2
- package/dist/modules/auth/frontend/reset.js +2 -2
- package/dist/modules/auth/frontend/reset.js.map +2 -2
- package/dist/modules/business_rules/components/ActionRow.js +1 -1
- package/dist/modules/business_rules/components/ActionRow.js.map +1 -1
- package/dist/modules/business_rules/components/ConditionRow.js +8 -4
- package/dist/modules/business_rules/components/ConditionRow.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js +4 -4
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/create/page.js +5 -5
- package/dist/modules/catalog/backend/catalog/products/create/page.js.map +2 -2
- package/dist/modules/catalog/components/PriceKindSettings.js +3 -3
- package/dist/modules/catalog/components/PriceKindSettings.js.map +2 -2
- package/dist/modules/catalog/components/products/ProductCategorizeSection.js +3 -3
- package/dist/modules/catalog/components/products/ProductCategorizeSection.js.map +2 -2
- package/dist/modules/catalog/components/products/ProductMediaManager.js +1 -1
- package/dist/modules/catalog/components/products/ProductMediaManager.js.map +2 -2
- package/dist/modules/catalog/components/products/VariantBuilder.js +5 -5
- package/dist/modules/catalog/components/products/VariantBuilder.js.map +2 -2
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.js +1 -1
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.js.map +2 -2
- package/dist/modules/customers/components/detail/ActivitiesDayStrip.js +2 -3
- package/dist/modules/customers/components/detail/ActivitiesDayStrip.js.map +2 -2
- package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js +1 -1
- package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js.map +1 -1
- package/dist/modules/dashboards/api/widgets/data/schema.js +26 -17
- package/dist/modules/dashboards/api/widgets/data/schema.js.map +2 -2
- package/dist/modules/dashboards/lib/aggregations.js +20 -6
- package/dist/modules/dashboards/lib/aggregations.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/detail.js +8 -8
- package/dist/modules/design_system/gallery/entries/detail.js.map +1 -1
- package/dist/modules/design_system/gallery/entries/overlays.js +2 -2
- package/dist/modules/design_system/gallery/entries/overlays.js.map +2 -2
- package/dist/modules/design_system/gallery/entries/scaffolding.js +8 -8
- package/dist/modules/design_system/gallery/entries/scaffolding.js.map +1 -1
- package/dist/modules/dictionaries/components/DictionaryEntrySelect.js +1 -1
- package/dist/modules/dictionaries/components/DictionaryEntrySelect.js.map +2 -2
- package/dist/modules/inbox_ops/backend/inbox-ops/log/page.js +6 -6
- package/dist/modules/inbox_ops/backend/inbox-ops/log/page.js.map +2 -2
- package/dist/modules/inbox_ops/components/proposals/EditActionDialog.js +3 -3
- package/dist/modules/inbox_ops/components/proposals/EditActionDialog.js.map +2 -2
- package/dist/modules/messages/components/message-detail/panels/dialogs.js +1 -1
- package/dist/modules/messages/components/message-detail/panels/dialogs.js.map +1 -1
- package/dist/modules/planner/components/AvailabilityRulesEditor.js +4 -4
- package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
- package/dist/modules/shipping_carriers/lib/shipment-wizard/components/PackageEditor.js +1 -1
- package/dist/modules/shipping_carriers/lib/shipment-wizard/components/PackageEditor.js.map +1 -1
- package/dist/modules/wms/components/backend/ReleaseReservationDialog.js +1 -1
- package/dist/modules/wms/components/backend/ReleaseReservationDialog.js.map +1 -1
- package/dist/modules/workflows/backend/definitions/page.js +1 -1
- package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js +1 -1
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
- package/dist/modules/workflows/backend/instances/page.js +8 -8
- package/dist/modules/workflows/backend/instances/page.js.map +2 -2
- package/dist/modules/workflows/backend/tasks/page.js +6 -6
- package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
- package/dist/modules/workflows/components/ActivitiesEditor.js +2 -2
- package/dist/modules/workflows/components/ActivitiesEditor.js.map +2 -2
- package/dist/modules/workflows/components/BusinessRulesSelector.js +1 -1
- package/dist/modules/workflows/components/BusinessRulesSelector.js.map +2 -2
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js +2 -2
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +2 -2
- package/dist/modules/workflows/components/EdgeEditDialogCrudForm.js +1 -2
- package/dist/modules/workflows/components/EdgeEditDialogCrudForm.js.map +2 -2
- package/dist/modules/workflows/components/NodeEditDialogCrudForm.js +1 -2
- package/dist/modules/workflows/components/NodeEditDialogCrudForm.js.map +2 -2
- package/dist/modules/workflows/components/StepsEditor.js +2 -2
- package/dist/modules/workflows/components/StepsEditor.js.map +2 -2
- package/dist/modules/workflows/components/TransitionsEditor.js +3 -3
- package/dist/modules/workflows/components/TransitionsEditor.js.map +2 -2
- package/dist/modules/workflows/components/WorkflowLegend.js +1 -1
- package/dist/modules/workflows/components/WorkflowLegend.js.map +2 -2
- package/dist/modules/workflows/components/WorkflowSelector.js +1 -1
- package/dist/modules/workflows/components/WorkflowSelector.js.map +2 -2
- package/dist/modules/workflows/components/fields/BusinessRuleConditionsEditor.js +1 -1
- package/dist/modules/workflows/components/fields/BusinessRuleConditionsEditor.js.map +2 -2
- package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js +1 -1
- package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js.map +2 -2
- package/dist/modules/workflows/components/fields/MappingArrayEditor.js +1 -1
- package/dist/modules/workflows/components/fields/MappingArrayEditor.js.map +2 -2
- package/dist/modules/workflows/components/fields/StartPreConditionsEditor.js +1 -1
- package/dist/modules/workflows/components/fields/StartPreConditionsEditor.js.map +2 -2
- package/dist/modules/workflows/components/fields/WorkflowSelectorField.js +4 -4
- package/dist/modules/workflows/components/fields/WorkflowSelectorField.js.map +2 -2
- package/package.json +8 -8
- package/src/modules/api_keys/i18n/ko.json +42 -0
- package/src/modules/attachments/fields/attachment.tsx +1 -1
- package/src/modules/attachments/i18n/ko.json +172 -0
- package/src/modules/audit_logs/i18n/ko.json +66 -0
- package/src/modules/auth/components/UserConsentsPanel.tsx +9 -9
- package/src/modules/auth/frontend/reset.tsx +2 -2
- package/src/modules/auth/i18n/ko.json +255 -0
- package/src/modules/business_rules/components/ActionRow.tsx +1 -1
- package/src/modules/business_rules/components/ConditionRow.tsx +9 -6
- package/src/modules/business_rules/i18n/ko.json +396 -0
- package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +4 -4
- package/src/modules/catalog/backend/catalog/products/create/page.tsx +5 -5
- package/src/modules/catalog/components/PriceKindSettings.tsx +3 -3
- package/src/modules/catalog/components/products/ProductCategorizeSection.tsx +3 -3
- package/src/modules/catalog/components/products/ProductMediaManager.tsx +1 -1
- package/src/modules/catalog/components/products/VariantBuilder.tsx +5 -5
- package/src/modules/catalog/i18n/ko.json +723 -0
- package/src/modules/communication_channels/backend/profile/communication-channels/page.tsx +1 -1
- package/src/modules/communication_channels/i18n/ko.json +138 -0
- package/src/modules/configs/i18n/ko.json +197 -0
- package/src/modules/currencies/i18n/de.json +1 -0
- package/src/modules/currencies/i18n/en.json +1 -0
- package/src/modules/currencies/i18n/es.json +1 -0
- package/src/modules/currencies/i18n/ko.json +160 -0
- package/src/modules/currencies/i18n/pl.json +1 -0
- package/src/modules/customer_accounts/i18n/ko.json +354 -0
- package/src/modules/customers/components/detail/ActivitiesDayStrip.tsx +2 -3
- package/src/modules/customers/components/detail/ConfirmDealLostDialog.tsx +1 -1
- package/src/modules/customers/i18n/de.json +10 -0
- package/src/modules/customers/i18n/en.json +10 -0
- package/src/modules/customers/i18n/es.json +10 -0
- package/src/modules/customers/i18n/ko.json +2855 -0
- package/src/modules/customers/i18n/pl.json +10 -0
- package/src/modules/dashboards/api/widgets/data/schema.ts +48 -19
- package/src/modules/dashboards/i18n/ko.json +118 -0
- package/src/modules/dashboards/lib/aggregations.ts +42 -6
- package/src/modules/data_sync/i18n/ko.json +125 -0
- package/src/modules/design_system/gallery/entries/detail.tsx +8 -8
- package/src/modules/design_system/gallery/entries/overlays.tsx +2 -2
- package/src/modules/design_system/gallery/entries/scaffolding.tsx +8 -8
- package/src/modules/design_system/i18n/de.json +12 -0
- package/src/modules/design_system/i18n/en.json +12 -0
- package/src/modules/design_system/i18n/es.json +12 -0
- package/src/modules/design_system/i18n/ko.json +58 -0
- package/src/modules/design_system/i18n/pl.json +12 -0
- package/src/modules/dictionaries/components/DictionaryEntrySelect.tsx +1 -1
- package/src/modules/dictionaries/i18n/ko.json +130 -0
- package/src/modules/directory/i18n/ko.json +105 -0
- package/src/modules/entities/i18n/ko.json +260 -0
- package/src/modules/feature_toggles/i18n/ko.json +96 -0
- package/src/modules/inbox_ops/backend/inbox-ops/log/page.tsx +6 -6
- package/src/modules/inbox_ops/components/proposals/EditActionDialog.tsx +3 -3
- package/src/modules/inbox_ops/i18n/ko.json +196 -0
- package/src/modules/integrations/i18n/ko.json +129 -0
- package/src/modules/messages/components/message-detail/panels/dialogs.tsx +1 -1
- package/src/modules/messages/i18n/ko.json +263 -0
- package/src/modules/notifications/i18n/ko.json +55 -0
- package/src/modules/payment_gateways/i18n/ko.json +87 -0
- package/src/modules/planner/components/AvailabilityRulesEditor.tsx +4 -4
- package/src/modules/planner/i18n/ko.json +565 -0
- package/src/modules/portal/i18n/ko.json +143 -0
- package/src/modules/progress/i18n/ko.json +20 -0
- package/src/modules/query_index/i18n/ko.json +69 -0
- package/src/modules/resources/i18n/ko.json +770 -0
- package/src/modules/sales/i18n/ko.json +1442 -0
- package/src/modules/shipping_carriers/i18n/ko.json +85 -0
- package/src/modules/shipping_carriers/lib/shipment-wizard/components/PackageEditor.tsx +1 -1
- package/src/modules/staff/i18n/ko.json +1162 -0
- package/src/modules/sync_excel/i18n/ko.json +107 -0
- package/src/modules/translations/i18n/ko.json +45 -0
- package/src/modules/wms/components/backend/ReleaseReservationDialog.tsx +1 -1
- package/src/modules/wms/i18n/ko.json +1053 -0
- package/src/modules/workflows/backend/definitions/page.tsx +1 -1
- package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +1 -1
- package/src/modules/workflows/backend/instances/page.tsx +8 -8
- package/src/modules/workflows/backend/tasks/page.tsx +6 -6
- package/src/modules/workflows/components/ActivitiesEditor.tsx +2 -2
- package/src/modules/workflows/components/BusinessRulesSelector.tsx +1 -1
- package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +2 -2
- package/src/modules/workflows/components/EdgeEditDialogCrudForm.tsx +1 -2
- package/src/modules/workflows/components/NodeEditDialogCrudForm.tsx +1 -2
- package/src/modules/workflows/components/StepsEditor.tsx +2 -2
- package/src/modules/workflows/components/TransitionsEditor.tsx +3 -3
- package/src/modules/workflows/components/WorkflowLegend.tsx +1 -1
- package/src/modules/workflows/components/WorkflowSelector.tsx +1 -1
- package/src/modules/workflows/components/fields/BusinessRuleConditionsEditor.tsx +1 -1
- package/src/modules/workflows/components/fields/FormFieldArrayEditor.tsx +1 -1
- package/src/modules/workflows/components/fields/MappingArrayEditor.tsx +1 -1
- package/src/modules/workflows/components/fields/StartPreConditionsEditor.tsx +1 -1
- package/src/modules/workflows/components/fields/WorkflowSelectorField.tsx +4 -4
- package/src/modules/workflows/i18n/ko.json +1203 -0
|
@@ -303,12 +303,12 @@ function PriceKindSettings() {
|
|
|
303
303
|
{
|
|
304
304
|
id: "promotion",
|
|
305
305
|
header: tableLabels.promotion,
|
|
306
|
-
cell: ({ row }) => row.original.isPromotion ? /* @__PURE__ */ jsx("span", { className: "inline-flex items-center rounded-full border border-
|
|
306
|
+
cell: ({ row }) => row.original.isPromotion ? /* @__PURE__ */ jsx("span", { className: "inline-flex items-center rounded-full border border-status-warning-border bg-status-warning-bg px-2 py-0.5 text-xs font-medium text-status-warning-text", children: tableLabels.promotionYes }) : /* @__PURE__ */ jsx("span", { className: "inline-flex items-center rounded-full border px-2 py-0.5 text-xs font-medium", children: tableLabels.promotionNo })
|
|
307
307
|
},
|
|
308
308
|
{
|
|
309
309
|
id: "active",
|
|
310
310
|
header: tableLabels.active,
|
|
311
|
-
cell: ({ row }) => row.original.isActive ? /* @__PURE__ */ jsx("span", { className: "inline-flex items-center rounded-full border border-
|
|
311
|
+
cell: ({ row }) => row.original.isActive ? /* @__PURE__ */ jsx("span", { className: "inline-flex items-center rounded-full border border-status-success-border bg-status-success-bg px-2 py-0.5 text-xs font-medium text-status-success-text", children: tableLabels.activeYes }) : /* @__PURE__ */ jsx("span", { className: "inline-flex items-center rounded-full border px-2 py-0.5 text-xs font-medium", children: tableLabels.activeNo })
|
|
312
312
|
}
|
|
313
313
|
], [displayModeLabels, tableLabels]);
|
|
314
314
|
const filteredItems = React.useMemo(() => {
|
|
@@ -471,7 +471,7 @@ function PriceKindSettings() {
|
|
|
471
471
|
t("catalog.priceKinds.form.activeLabel", "Active")
|
|
472
472
|
] })
|
|
473
473
|
] }),
|
|
474
|
-
error ? /* @__PURE__ */ jsx("p", { className: "text-sm text-
|
|
474
|
+
error ? /* @__PURE__ */ jsx("p", { className: "text-sm text-status-error-text", children: error }) : null
|
|
475
475
|
] }),
|
|
476
476
|
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
477
477
|
/* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: closeDialog, children: t("catalog.priceKinds.actions.cancel", "Cancel") }),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/catalog/components/PriceKindSettings.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { RadioGroup, Radio } from '@open-mercato/ui/primitives/radio'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { DictionaryEntrySelect } from '@open-mercato/core/modules/dictionaries/components/DictionaryEntrySelect'\nimport { useCurrencyDictionary } from '@open-mercato/core/modules/customers/components/detail/hooks/useCurrencyDictionary'\nimport type { DictionaryOption } from '@open-mercato/core/modules/dictionaries/components/DictionaryEntrySelect'\nimport type { CatalogPriceDisplayMode } from '../data/types'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('catalog')\n\ntype PriceKind = {\n id: string\n code: string\n title: string\n displayMode: CatalogPriceDisplayMode\n currencyCode: string | null\n isPromotion: boolean\n isActive: boolean\n createdAt: string\n updatedAt: string\n}\n\ntype DialogState =\n | { mode: 'create' }\n | { mode: 'edit'; entry: PriceKind }\n\ntype PriceKindApiPayload = Partial<PriceKind> & {\n display_mode?: PriceKind['displayMode']\n currency_code?: string | null\n is_promotion?: boolean\n is_active?: boolean\n created_at?: string\n updated_at?: string\n}\n\nconst DISPLAY_MODES: Array<{ value: 'including-tax' | 'excluding-tax'; label: string }> = [\n { value: 'excluding-tax', label: 'Excluding tax' },\n { value: 'including-tax', label: 'Including tax' },\n]\n\nconst PAGE_SIZE = 100\n\ntype PriceKindFormState = {\n code: string\n title: string\n displayMode: CatalogPriceDisplayMode\n currencyCode: string\n isPromotion: boolean\n isActive: boolean\n}\n\nconst DEFAULT_FORM: PriceKindFormState = {\n code: '',\n title: '',\n displayMode: 'excluding-tax' as const,\n currencyCode: '',\n isPromotion: false,\n isActive: true,\n}\n\nconst normalizePriceKind = (input: PriceKindApiPayload | null | undefined): PriceKind => {\n const raw = input ?? {}\n const toStringValue = (value: unknown): string | null => {\n if (typeof value === 'string') return value\n if (typeof value === 'number' || typeof value === 'bigint') return String(value)\n return null\n }\n const toBooleanValue = (value: unknown): boolean | null => (typeof value === 'boolean' ? value : null)\n const resolveDisplayMode = (value: string | null): PriceKind['displayMode'] =>\n value === 'including-tax' ? 'including-tax' : 'excluding-tax'\n\n const displayMode = resolveDisplayMode(\n toStringValue(raw.displayMode) ?? toStringValue(raw.display_mode),\n )\n const currencyCode = toStringValue(raw.currencyCode) ?? toStringValue(raw.currency_code)\n const isPromotion = toBooleanValue(raw.isPromotion) ?? toBooleanValue(raw.is_promotion)\n const isActive = toBooleanValue(raw.isActive) ?? toBooleanValue(raw.is_active)\n\n return {\n id: toStringValue(raw.id) ?? '',\n code: toStringValue(raw.code) ?? '',\n title: toStringValue(raw.title) ?? '',\n displayMode,\n currencyCode: currencyCode ?? null,\n isPromotion: isPromotion ?? false,\n isActive: isActive ?? true,\n createdAt: toStringValue(raw.createdAt) ?? toStringValue(raw.created_at) ?? '',\n updatedAt: toStringValue(raw.updatedAt) ?? toStringValue(raw.updated_at) ?? '',\n }\n}\n\nexport function PriceKindSettings() {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const scopeVersion = useOrganizationScopeVersion()\n const [items, setItems] = React.useState<PriceKind[]>([])\n const [loading, setLoading] = React.useState(false)\n const [search, setSearch] = React.useState('')\n const [dialog, setDialog] = React.useState<DialogState | null>(null)\n const [form, setForm] = React.useState<PriceKindFormState>(DEFAULT_FORM)\n const [submitting, setSubmitting] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n const { data: currencyDictionary, refetch: refetchCurrencyDictionary } = useCurrencyDictionary()\n\n const currencyOptionsLoader = React.useCallback(async (): Promise<DictionaryOption[]> => {\n if (currencyDictionary && Array.isArray(currencyDictionary.entries)) {\n return currencyDictionary.entries.map((entry) => ({\n value: entry.value,\n label: entry.label,\n color: entry.color ?? null,\n icon: entry.icon ?? null,\n }))\n }\n const payload = await refetchCurrencyDictionary()\n return payload.entries.map((entry) => ({\n value: entry.value,\n label: entry.label,\n color: entry.color ?? null,\n icon: entry.icon ?? null,\n }))\n }, [currencyDictionary, refetchCurrencyDictionary])\n\n const loadItems = React.useCallback(async () => {\n setLoading(true)\n const loadErrorMessage = t('catalog.priceKinds.errors.load', 'Failed to load price kinds.')\n try {\n const payload = await readApiResultOrThrow<{ items?: PriceKindApiPayload[] }>(\n `/api/catalog/price-kinds?pageSize=${PAGE_SIZE}`,\n undefined,\n { errorMessage: loadErrorMessage },\n )\n const normalized = Array.isArray(payload.items) ? payload.items.map((item) => normalizePriceKind(item)) : []\n setItems(normalized)\n } catch (err) {\n logger.error('catalog.price-kinds.list failed', { err })\n flash(loadErrorMessage, 'error')\n } finally {\n setLoading(false)\n }\n }, [t])\n\n React.useEffect(() => {\n loadItems().catch(() => {})\n }, [loadItems, scopeVersion])\n\n const openDialog = React.useCallback((state: DialogState) => {\n if (state.mode === 'edit') {\n setForm({\n code: state.entry.code,\n title: state.entry.title,\n displayMode: state.entry.displayMode,\n currencyCode: state.entry.currencyCode ?? '',\n isPromotion: state.entry.isPromotion,\n isActive: state.entry.isActive,\n })\n } else {\n setForm(DEFAULT_FORM)\n }\n setError(null)\n setDialog(state)\n }, [])\n\n const closeDialog = React.useCallback(() => {\n setDialog(null)\n setError(null)\n setSubmitting(false)\n setForm(DEFAULT_FORM)\n }, [])\n\n const handleSubmit = React.useCallback(async () => {\n if (!dialog) return\n const trimmedCode = form.code.trim().toLowerCase()\n const trimmedTitle = form.title.trim()\n if (!trimmedCode || !trimmedTitle) {\n setError(t('catalog.priceKinds.errors.required', 'Code and title are required.'))\n return\n }\n setSubmitting(true)\n setError(null)\n try {\n const payload = {\n code: trimmedCode,\n title: trimmedTitle,\n displayMode: form.displayMode,\n currencyCode: form.currencyCode.trim() || undefined,\n isPromotion: form.isPromotion,\n isActive: form.isActive,\n }\n const path = '/api/catalog/price-kinds'\n const method = dialog.mode === 'create' ? 'POST' : 'PUT'\n const body =\n dialog.mode === 'edit'\n ? JSON.stringify({ id: dialog.entry.id, ...payload })\n : JSON.stringify(payload)\n const savePriceKind = () => apiCall(path, {\n method,\n headers: { 'content-type': 'application/json' },\n body,\n })\n const optimisticLockHeader = buildOptimisticLockHeader(\n dialog.mode === 'edit' ? dialog.entry.updatedAt : null,\n )\n const call = Object.keys(optimisticLockHeader).length > 0\n ? await withScopedApiRequestHeaders(optimisticLockHeader, savePriceKind)\n : await savePriceKind()\n if (!call.ok) {\n await raiseCrudError(call.response, t('catalog.priceKinds.errors.save', 'Failed to save price kind.'))\n }\n flash(\n dialog.mode === 'create'\n ? t('catalog.priceKinds.messages.created', 'Price kind created.')\n : t('catalog.priceKinds.messages.updated', 'Price kind updated.'),\n 'success',\n )\n closeDialog()\n await loadItems()\n } catch (err) {\n logger.error('catalog.price-kinds.save failed', { err })\n // Route a concurrent-edit 409 through the single conflict surface (unified\n // conflict bar, or the enterprise merge dialog when its handler is mounted)\n // and close the editor so that surface owns the resolution. Other errors\n // stay inline in the dialog so the user can correct and resubmit.\n if (surfaceRecordConflict(err, t, { onRefresh: () => { void loadItems() } })) {\n closeDialog()\n return\n }\n const message = err instanceof Error ? err.message : t('catalog.priceKinds.errors.save', 'Failed to save price kind.')\n setError(message)\n } finally {\n setSubmitting(false)\n }\n }, [dialog, form, t, closeDialog, loadItems])\n\n const handleDelete = React.useCallback(\n async (entry: PriceKind) => {\n const confirmMessage = t('catalog.priceKinds.confirm.delete', 'Delete price kind \"{{code}}\"?').replace('{{code}}', entry.code)\n const confirmed = await confirm({\n title: confirmMessage,\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n const deletePriceKind = () => apiCall('/api/catalog/price-kinds', {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: entry.id }),\n })\n const optimisticLockHeader = buildOptimisticLockHeader(entry.updatedAt)\n const call = Object.keys(optimisticLockHeader).length > 0\n ? await withScopedApiRequestHeaders(optimisticLockHeader, deletePriceKind)\n : await deletePriceKind()\n if (!call.ok) {\n await raiseCrudError(call.response, t('catalog.priceKinds.errors.delete', 'Failed to delete price kind.'))\n }\n flash(t('catalog.priceKinds.messages.deleted', 'Price kind deleted.'), 'success')\n await loadItems()\n } catch (err) {\n logger.error('catalog.price-kinds.delete failed', { err })\n // Route a concurrent-edit 409 through the single conflict surface; fall\n // back to a flash for any other delete failure.\n if (surfaceRecordConflict(err, t, { onRefresh: () => { void loadItems() } })) return\n const message = err instanceof Error ? err.message : t('catalog.priceKinds.errors.delete', 'Failed to delete price kind.')\n flash(message, 'error')\n }\n },\n [confirm, loadItems, t],\n )\n\n const formKeyHandler = React.useCallback(\n (event: React.KeyboardEvent<HTMLFormElement>) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n void handleSubmit()\n }\n },\n [handleSubmit],\n )\n\n const displayModeLabels = React.useMemo(() => ({\n 'including-tax': t('catalog.priceKinds.form.displayMode.include', 'Including tax'),\n 'excluding-tax': t('catalog.priceKinds.form.displayMode.exclude', 'Excluding tax'),\n }), [t])\n\n const displayModeOptions = React.useMemo(\n () =>\n DISPLAY_MODES.map((mode) => ({\n ...mode,\n label: displayModeLabels[mode.value],\n })),\n [displayModeLabels],\n )\n\n const currencyLabels = React.useMemo(() => ({\n placeholder: t('catalog.priceKinds.form.currency.placeholder', 'Select currency\u2026'),\n addLabel: t('catalog.priceKinds.form.currency.add', 'Add currency'),\n addPrompt: t('catalog.priceKinds.form.currency.addPrompt', 'Provide a currency code.'),\n dialogTitle: t('catalog.priceKinds.form.currency.dialogTitle', 'Add currency'),\n valueLabel: t('catalog.priceKinds.form.currency.valueLabel', 'Currency code'),\n valuePlaceholder: t('catalog.priceKinds.form.currency.valuePlaceholder', 'e.g. USD'),\n labelLabel: t('catalog.priceKinds.form.currency.labelLabel', 'Display label (optional)'),\n labelPlaceholder: t('catalog.priceKinds.form.currency.labelPlaceholder', 'e.g. US Dollar'),\n emptyError: t('catalog.priceKinds.form.currency.required', 'Currency code is required.'),\n cancelLabel: t('catalog.priceKinds.form.currency.cancel', 'Cancel'),\n saveLabel: t('catalog.priceKinds.form.currency.save', 'Save'),\n saveShortcutHint: t('catalog.priceKinds.form.currency.saveShortcut', 'Press Enter to save'),\n successCreateLabel: t('catalog.priceKinds.form.currency.success', 'Currency added.'),\n errorLoad: t('catalog.priceKinds.form.currency.loadError', 'Unable to load currencies.'),\n errorSave: t('catalog.priceKinds.form.currency.createError', 'Unable to add currency.'),\n loadingLabel: t('catalog.priceKinds.form.currency.loading', 'Loading currencies\u2026'),\n manageTitle: t('catalog.priceKinds.form.currency.manage', 'Manage currencies'),\n }), [t])\n\n const tableLabels = React.useMemo(() => ({\n code: t('catalog.priceKinds.table.code', 'Code'),\n title: t('catalog.priceKinds.table.title', 'Title'),\n displayMode: t('catalog.priceKinds.table.displayMode', 'Display mode'),\n currency: t('catalog.priceKinds.table.currency', 'Currency'),\n promotion: t('catalog.priceKinds.table.promotion', 'Promotion'),\n promotionYes: t('catalog.priceKinds.table.promotionYes', 'Yes'),\n promotionNo: t('catalog.priceKinds.table.promotionNo', 'No'),\n active: t('catalog.priceKinds.table.active', 'Active'),\n activeYes: t('catalog.priceKinds.table.activeYes', 'Active'),\n activeNo: t('catalog.priceKinds.table.activeNo', 'Inactive'),\n search: t('catalog.priceKinds.search.placeholder', 'Search by code or title\u2026'),\n empty: t('catalog.priceKinds.table.empty', 'No price kinds yet.'),\n }), [t])\n\n const columns = React.useMemo<ColumnDef<PriceKind>[]>(() => [\n {\n accessorKey: 'code',\n header: tableLabels.code,\n cell: ({ row }) => <span className=\"font-mono uppercase\">{row.original.code}</span>,\n },\n {\n accessorKey: 'title',\n header: tableLabels.title,\n cell: ({ row }) => <span className=\"font-medium\">{row.original.title}</span>,\n },\n {\n accessorKey: 'displayMode',\n header: tableLabels.displayMode,\n cell: ({ row }) => displayModeLabels[row.original.displayMode] ?? row.original.displayMode,\n },\n {\n accessorKey: 'currencyCode',\n header: tableLabels.currency,\n cell: ({ row }) => (row.original.currencyCode ? row.original.currencyCode.toUpperCase() : '\u2014'),\n },\n {\n id: 'promotion',\n header: tableLabels.promotion,\n cell: ({ row }) =>\n row.original.isPromotion ? (\n <span className=\"inline-flex items-center rounded-full border border-amber-200 bg-amber-50 px-2 py-0.5 text-xs font-medium text-amber-900 dark:border-amber-500/50 dark:bg-amber-500/10 dark:text-amber-100\">\n {tableLabels.promotionYes}\n </span>\n ) : (\n <span className=\"inline-flex items-center rounded-full border px-2 py-0.5 text-xs font-medium\">\n {tableLabels.promotionNo}\n </span>\n ),\n },\n {\n id: 'active',\n header: tableLabels.active,\n cell: ({ row }) =>\n row.original.isActive ? (\n <span className=\"inline-flex items-center rounded-full border border-emerald-200 bg-emerald-50 px-2 py-0.5 text-xs font-medium text-emerald-900 dark:border-emerald-500/50 dark:bg-emerald-500/10 dark:text-emerald-100\">\n {tableLabels.activeYes}\n </span>\n ) : (\n <span className=\"inline-flex items-center rounded-full border px-2 py-0.5 text-xs font-medium\">\n {tableLabels.activeNo}\n </span>\n ),\n },\n ], [displayModeLabels, tableLabels])\n\n const filteredItems = React.useMemo(() => {\n const term = search.trim().toLowerCase()\n if (!term) return items\n return items.filter((item) => {\n const currency = (item.currencyCode ?? '').toLowerCase()\n const modeLabel = (displayModeLabels[item.displayMode] ?? item.displayMode).toLowerCase()\n return (\n item.code.toLowerCase().includes(term) ||\n item.title.toLowerCase().includes(term) ||\n currency.includes(term) ||\n modeLabel.includes(term)\n )\n })\n }, [displayModeLabels, items, search])\n\n const handleRowClick = React.useCallback((entry: PriceKind) => {\n openDialog({ mode: 'edit', entry })\n }, [openDialog])\n\n return (\n <>\n <section className=\"border bg-card text-card-foreground shadow-sm\">\n <div className=\"border-b px-6 py-4 space-y-1\">\n <h2 className=\"text-lg font-semibold\">{t('catalog.priceKinds.title', 'Price kinds')}</h2>\n <p className=\"text-sm text-muted-foreground\">\n {t('catalog.priceKinds.description', 'Configure reusable price kinds that control pricing columns and tax display.')}\n </p>\n </div>\n <div className=\"px-2 py-4 sm:px-4\">\n <DataTable<PriceKind>\n data={filteredItems}\n columns={columns}\n embedded\n isLoading={loading}\n searchValue={search}\n onSearchChange={setSearch}\n searchPlaceholder={tableLabels.search}\n emptyState={<p className=\"py-8 text-center text-sm text-muted-foreground\">{tableLabels.empty}</p>}\n actions={(\n <Button size=\"sm\" onClick={() => openDialog({ mode: 'create' })}>\n {t('catalog.priceKinds.actions.add', 'Add price kind')}\n </Button>\n )}\n refreshButton={{\n label: t('catalog.priceKinds.actions.refresh', 'Refresh'),\n onRefresh: () => { void loadItems() },\n isRefreshing: loading,\n }}\n rowActions={(entry) => (\n <RowActions\n items={[\n {\n id: 'edit',\n label: t('catalog.priceKinds.actions.edit', 'Edit'),\n onSelect: () => openDialog({ mode: 'edit', entry }),\n },\n {\n id: 'delete',\n label: t('catalog.priceKinds.actions.delete', 'Delete'),\n destructive: true,\n onSelect: () => { void handleDelete(entry) },\n },\n ]}\n />\n )}\n onRowClick={handleRowClick}\n />\n </div>\n <Dialog open={dialog !== null} onOpenChange={(open) => { if (!open) closeDialog(); }}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>\n {dialog?.mode === 'edit'\n ? t('catalog.priceKinds.dialog.editTitle', 'Edit price kind')\n : t('catalog.priceKinds.dialog.createTitle', 'Create price kind')}\n </DialogTitle>\n <DialogDescription>\n {dialog?.mode === 'edit'\n ? t('catalog.priceKinds.dialog.editDescription', 'Update labels or tax behavior for this price kind.')\n : t('catalog.priceKinds.dialog.createDescription', 'Define a reusable price kind for product pricing.')}\n </DialogDescription>\n </DialogHeader>\n <form className=\"space-y-4\" onKeyDown={formKeyHandler} onSubmit={(event) => { event.preventDefault(); void handleSubmit() }}>\n <div className=\"space-y-2\">\n <Label htmlFor=\"price-kind-code\">{t('catalog.priceKinds.form.codeLabel', 'Code')}</Label>\n <Input\n id=\"price-kind-code\"\n value={form.code}\n onChange={(event) => setForm((prev) => ({ ...prev, code: event.target.value }))}\n placeholder={t('catalog.priceKinds.form.codePlaceholder', 'e.g. regular')}\n className=\"font-mono uppercase\"\n disabled={dialog?.mode === 'edit'}\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"price-kind-title\">{t('catalog.priceKinds.form.titleLabel', 'Title')}</Label>\n <Input\n id=\"price-kind-title\"\n value={form.title}\n onChange={(event) => setForm((prev) => ({ ...prev, title: event.target.value }))}\n placeholder={t('catalog.priceKinds.form.titlePlaceholder', 'e.g. Regular price')}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>{t('catalog.priceKinds.form.displayModeLabel', 'Display mode')}</Label>\n <RadioGroup\n className=\"grid gap-2 md:grid-cols-2\"\n name=\"displayMode\"\n value={form.displayMode}\n onValueChange={(next) => setForm((prev) => ({ ...prev, displayMode: next as CatalogPriceDisplayMode }))}\n >\n {displayModeOptions.map((mode) => (\n <label\n key={mode.value}\n className={`flex cursor-pointer items-center gap-2 rounded-md border p-3 text-sm ${\n form.displayMode === mode.value ? 'border-primary bg-primary/5' : 'border-border'\n }`}\n >\n <Radio value={mode.value} />\n <span>{mode.label}</span>\n </label>\n ))}\n </RadioGroup>\n </div>\n <div className=\"space-y-2\">\n <Label>{t('catalog.priceKinds.form.currencyLabel', 'Currency (optional)')}</Label>\n <DictionaryEntrySelect\n value={form.currencyCode || undefined}\n onChange={(value) => setForm((prev) => ({ ...prev, currencyCode: value ?? '' }))}\n fetchOptions={currencyOptionsLoader}\n labels={currencyLabels}\n allowInlineCreate={false}\n sortOptions=\"none\"\n />\n </div>\n <div className=\"flex flex-col gap-2\">\n <label className=\"flex items-center gap-2 text-sm font-medium\">\n <input\n type=\"checkbox\"\n className=\"h-4 w-4 rounded border\"\n checked={form.isPromotion}\n onChange={(event) => setForm((prev) => ({ ...prev, isPromotion: event.target.checked }))}\n />\n {t('catalog.priceKinds.form.promotionLabel', 'Mark as promotion')}\n </label>\n <label className=\"flex items-center gap-2 text-sm font-medium\">\n <input\n type=\"checkbox\"\n className=\"h-4 w-4 rounded border\"\n checked={form.isActive}\n onChange={(event) => setForm((prev) => ({ ...prev, isActive: event.target.checked }))}\n />\n {t('catalog.priceKinds.form.activeLabel', 'Active')}\n </label>\n </div>\n {error ? <p className=\"text-sm text-red-600\">{error}</p> : null}\n </form>\n <DialogFooter>\n <Button variant=\"ghost\" onClick={closeDialog}>\n {t('catalog.priceKinds.actions.cancel', 'Cancel')}\n </Button>\n <Button onClick={() => void handleSubmit()} disabled={submitting}>\n {dialog?.mode === 'edit'\n ? t('catalog.priceKinds.actions.saveChanges', 'Save changes')\n : t('catalog.priceKinds.actions.create', 'Create')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </section>\n {ConfirmDialogElement}\n </>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { RadioGroup, Radio } from '@open-mercato/ui/primitives/radio'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { DictionaryEntrySelect } from '@open-mercato/core/modules/dictionaries/components/DictionaryEntrySelect'\nimport { useCurrencyDictionary } from '@open-mercato/core/modules/customers/components/detail/hooks/useCurrencyDictionary'\nimport type { DictionaryOption } from '@open-mercato/core/modules/dictionaries/components/DictionaryEntrySelect'\nimport type { CatalogPriceDisplayMode } from '../data/types'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('catalog')\n\ntype PriceKind = {\n id: string\n code: string\n title: string\n displayMode: CatalogPriceDisplayMode\n currencyCode: string | null\n isPromotion: boolean\n isActive: boolean\n createdAt: string\n updatedAt: string\n}\n\ntype DialogState =\n | { mode: 'create' }\n | { mode: 'edit'; entry: PriceKind }\n\ntype PriceKindApiPayload = Partial<PriceKind> & {\n display_mode?: PriceKind['displayMode']\n currency_code?: string | null\n is_promotion?: boolean\n is_active?: boolean\n created_at?: string\n updated_at?: string\n}\n\nconst DISPLAY_MODES: Array<{ value: 'including-tax' | 'excluding-tax'; label: string }> = [\n { value: 'excluding-tax', label: 'Excluding tax' },\n { value: 'including-tax', label: 'Including tax' },\n]\n\nconst PAGE_SIZE = 100\n\ntype PriceKindFormState = {\n code: string\n title: string\n displayMode: CatalogPriceDisplayMode\n currencyCode: string\n isPromotion: boolean\n isActive: boolean\n}\n\nconst DEFAULT_FORM: PriceKindFormState = {\n code: '',\n title: '',\n displayMode: 'excluding-tax' as const,\n currencyCode: '',\n isPromotion: false,\n isActive: true,\n}\n\nconst normalizePriceKind = (input: PriceKindApiPayload | null | undefined): PriceKind => {\n const raw = input ?? {}\n const toStringValue = (value: unknown): string | null => {\n if (typeof value === 'string') return value\n if (typeof value === 'number' || typeof value === 'bigint') return String(value)\n return null\n }\n const toBooleanValue = (value: unknown): boolean | null => (typeof value === 'boolean' ? value : null)\n const resolveDisplayMode = (value: string | null): PriceKind['displayMode'] =>\n value === 'including-tax' ? 'including-tax' : 'excluding-tax'\n\n const displayMode = resolveDisplayMode(\n toStringValue(raw.displayMode) ?? toStringValue(raw.display_mode),\n )\n const currencyCode = toStringValue(raw.currencyCode) ?? toStringValue(raw.currency_code)\n const isPromotion = toBooleanValue(raw.isPromotion) ?? toBooleanValue(raw.is_promotion)\n const isActive = toBooleanValue(raw.isActive) ?? toBooleanValue(raw.is_active)\n\n return {\n id: toStringValue(raw.id) ?? '',\n code: toStringValue(raw.code) ?? '',\n title: toStringValue(raw.title) ?? '',\n displayMode,\n currencyCode: currencyCode ?? null,\n isPromotion: isPromotion ?? false,\n isActive: isActive ?? true,\n createdAt: toStringValue(raw.createdAt) ?? toStringValue(raw.created_at) ?? '',\n updatedAt: toStringValue(raw.updatedAt) ?? toStringValue(raw.updated_at) ?? '',\n }\n}\n\nexport function PriceKindSettings() {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const scopeVersion = useOrganizationScopeVersion()\n const [items, setItems] = React.useState<PriceKind[]>([])\n const [loading, setLoading] = React.useState(false)\n const [search, setSearch] = React.useState('')\n const [dialog, setDialog] = React.useState<DialogState | null>(null)\n const [form, setForm] = React.useState<PriceKindFormState>(DEFAULT_FORM)\n const [submitting, setSubmitting] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n const { data: currencyDictionary, refetch: refetchCurrencyDictionary } = useCurrencyDictionary()\n\n const currencyOptionsLoader = React.useCallback(async (): Promise<DictionaryOption[]> => {\n if (currencyDictionary && Array.isArray(currencyDictionary.entries)) {\n return currencyDictionary.entries.map((entry) => ({\n value: entry.value,\n label: entry.label,\n color: entry.color ?? null,\n icon: entry.icon ?? null,\n }))\n }\n const payload = await refetchCurrencyDictionary()\n return payload.entries.map((entry) => ({\n value: entry.value,\n label: entry.label,\n color: entry.color ?? null,\n icon: entry.icon ?? null,\n }))\n }, [currencyDictionary, refetchCurrencyDictionary])\n\n const loadItems = React.useCallback(async () => {\n setLoading(true)\n const loadErrorMessage = t('catalog.priceKinds.errors.load', 'Failed to load price kinds.')\n try {\n const payload = await readApiResultOrThrow<{ items?: PriceKindApiPayload[] }>(\n `/api/catalog/price-kinds?pageSize=${PAGE_SIZE}`,\n undefined,\n { errorMessage: loadErrorMessage },\n )\n const normalized = Array.isArray(payload.items) ? payload.items.map((item) => normalizePriceKind(item)) : []\n setItems(normalized)\n } catch (err) {\n logger.error('catalog.price-kinds.list failed', { err })\n flash(loadErrorMessage, 'error')\n } finally {\n setLoading(false)\n }\n }, [t])\n\n React.useEffect(() => {\n loadItems().catch(() => {})\n }, [loadItems, scopeVersion])\n\n const openDialog = React.useCallback((state: DialogState) => {\n if (state.mode === 'edit') {\n setForm({\n code: state.entry.code,\n title: state.entry.title,\n displayMode: state.entry.displayMode,\n currencyCode: state.entry.currencyCode ?? '',\n isPromotion: state.entry.isPromotion,\n isActive: state.entry.isActive,\n })\n } else {\n setForm(DEFAULT_FORM)\n }\n setError(null)\n setDialog(state)\n }, [])\n\n const closeDialog = React.useCallback(() => {\n setDialog(null)\n setError(null)\n setSubmitting(false)\n setForm(DEFAULT_FORM)\n }, [])\n\n const handleSubmit = React.useCallback(async () => {\n if (!dialog) return\n const trimmedCode = form.code.trim().toLowerCase()\n const trimmedTitle = form.title.trim()\n if (!trimmedCode || !trimmedTitle) {\n setError(t('catalog.priceKinds.errors.required', 'Code and title are required.'))\n return\n }\n setSubmitting(true)\n setError(null)\n try {\n const payload = {\n code: trimmedCode,\n title: trimmedTitle,\n displayMode: form.displayMode,\n currencyCode: form.currencyCode.trim() || undefined,\n isPromotion: form.isPromotion,\n isActive: form.isActive,\n }\n const path = '/api/catalog/price-kinds'\n const method = dialog.mode === 'create' ? 'POST' : 'PUT'\n const body =\n dialog.mode === 'edit'\n ? JSON.stringify({ id: dialog.entry.id, ...payload })\n : JSON.stringify(payload)\n const savePriceKind = () => apiCall(path, {\n method,\n headers: { 'content-type': 'application/json' },\n body,\n })\n const optimisticLockHeader = buildOptimisticLockHeader(\n dialog.mode === 'edit' ? dialog.entry.updatedAt : null,\n )\n const call = Object.keys(optimisticLockHeader).length > 0\n ? await withScopedApiRequestHeaders(optimisticLockHeader, savePriceKind)\n : await savePriceKind()\n if (!call.ok) {\n await raiseCrudError(call.response, t('catalog.priceKinds.errors.save', 'Failed to save price kind.'))\n }\n flash(\n dialog.mode === 'create'\n ? t('catalog.priceKinds.messages.created', 'Price kind created.')\n : t('catalog.priceKinds.messages.updated', 'Price kind updated.'),\n 'success',\n )\n closeDialog()\n await loadItems()\n } catch (err) {\n logger.error('catalog.price-kinds.save failed', { err })\n // Route a concurrent-edit 409 through the single conflict surface (unified\n // conflict bar, or the enterprise merge dialog when its handler is mounted)\n // and close the editor so that surface owns the resolution. Other errors\n // stay inline in the dialog so the user can correct and resubmit.\n if (surfaceRecordConflict(err, t, { onRefresh: () => { void loadItems() } })) {\n closeDialog()\n return\n }\n const message = err instanceof Error ? err.message : t('catalog.priceKinds.errors.save', 'Failed to save price kind.')\n setError(message)\n } finally {\n setSubmitting(false)\n }\n }, [dialog, form, t, closeDialog, loadItems])\n\n const handleDelete = React.useCallback(\n async (entry: PriceKind) => {\n const confirmMessage = t('catalog.priceKinds.confirm.delete', 'Delete price kind \"{{code}}\"?').replace('{{code}}', entry.code)\n const confirmed = await confirm({\n title: confirmMessage,\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n const deletePriceKind = () => apiCall('/api/catalog/price-kinds', {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: entry.id }),\n })\n const optimisticLockHeader = buildOptimisticLockHeader(entry.updatedAt)\n const call = Object.keys(optimisticLockHeader).length > 0\n ? await withScopedApiRequestHeaders(optimisticLockHeader, deletePriceKind)\n : await deletePriceKind()\n if (!call.ok) {\n await raiseCrudError(call.response, t('catalog.priceKinds.errors.delete', 'Failed to delete price kind.'))\n }\n flash(t('catalog.priceKinds.messages.deleted', 'Price kind deleted.'), 'success')\n await loadItems()\n } catch (err) {\n logger.error('catalog.price-kinds.delete failed', { err })\n // Route a concurrent-edit 409 through the single conflict surface; fall\n // back to a flash for any other delete failure.\n if (surfaceRecordConflict(err, t, { onRefresh: () => { void loadItems() } })) return\n const message = err instanceof Error ? err.message : t('catalog.priceKinds.errors.delete', 'Failed to delete price kind.')\n flash(message, 'error')\n }\n },\n [confirm, loadItems, t],\n )\n\n const formKeyHandler = React.useCallback(\n (event: React.KeyboardEvent<HTMLFormElement>) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n void handleSubmit()\n }\n },\n [handleSubmit],\n )\n\n const displayModeLabels = React.useMemo(() => ({\n 'including-tax': t('catalog.priceKinds.form.displayMode.include', 'Including tax'),\n 'excluding-tax': t('catalog.priceKinds.form.displayMode.exclude', 'Excluding tax'),\n }), [t])\n\n const displayModeOptions = React.useMemo(\n () =>\n DISPLAY_MODES.map((mode) => ({\n ...mode,\n label: displayModeLabels[mode.value],\n })),\n [displayModeLabels],\n )\n\n const currencyLabels = React.useMemo(() => ({\n placeholder: t('catalog.priceKinds.form.currency.placeholder', 'Select currency\u2026'),\n addLabel: t('catalog.priceKinds.form.currency.add', 'Add currency'),\n addPrompt: t('catalog.priceKinds.form.currency.addPrompt', 'Provide a currency code.'),\n dialogTitle: t('catalog.priceKinds.form.currency.dialogTitle', 'Add currency'),\n valueLabel: t('catalog.priceKinds.form.currency.valueLabel', 'Currency code'),\n valuePlaceholder: t('catalog.priceKinds.form.currency.valuePlaceholder', 'e.g. USD'),\n labelLabel: t('catalog.priceKinds.form.currency.labelLabel', 'Display label (optional)'),\n labelPlaceholder: t('catalog.priceKinds.form.currency.labelPlaceholder', 'e.g. US Dollar'),\n emptyError: t('catalog.priceKinds.form.currency.required', 'Currency code is required.'),\n cancelLabel: t('catalog.priceKinds.form.currency.cancel', 'Cancel'),\n saveLabel: t('catalog.priceKinds.form.currency.save', 'Save'),\n saveShortcutHint: t('catalog.priceKinds.form.currency.saveShortcut', 'Press Enter to save'),\n successCreateLabel: t('catalog.priceKinds.form.currency.success', 'Currency added.'),\n errorLoad: t('catalog.priceKinds.form.currency.loadError', 'Unable to load currencies.'),\n errorSave: t('catalog.priceKinds.form.currency.createError', 'Unable to add currency.'),\n loadingLabel: t('catalog.priceKinds.form.currency.loading', 'Loading currencies\u2026'),\n manageTitle: t('catalog.priceKinds.form.currency.manage', 'Manage currencies'),\n }), [t])\n\n const tableLabels = React.useMemo(() => ({\n code: t('catalog.priceKinds.table.code', 'Code'),\n title: t('catalog.priceKinds.table.title', 'Title'),\n displayMode: t('catalog.priceKinds.table.displayMode', 'Display mode'),\n currency: t('catalog.priceKinds.table.currency', 'Currency'),\n promotion: t('catalog.priceKinds.table.promotion', 'Promotion'),\n promotionYes: t('catalog.priceKinds.table.promotionYes', 'Yes'),\n promotionNo: t('catalog.priceKinds.table.promotionNo', 'No'),\n active: t('catalog.priceKinds.table.active', 'Active'),\n activeYes: t('catalog.priceKinds.table.activeYes', 'Active'),\n activeNo: t('catalog.priceKinds.table.activeNo', 'Inactive'),\n search: t('catalog.priceKinds.search.placeholder', 'Search by code or title\u2026'),\n empty: t('catalog.priceKinds.table.empty', 'No price kinds yet.'),\n }), [t])\n\n const columns = React.useMemo<ColumnDef<PriceKind>[]>(() => [\n {\n accessorKey: 'code',\n header: tableLabels.code,\n cell: ({ row }) => <span className=\"font-mono uppercase\">{row.original.code}</span>,\n },\n {\n accessorKey: 'title',\n header: tableLabels.title,\n cell: ({ row }) => <span className=\"font-medium\">{row.original.title}</span>,\n },\n {\n accessorKey: 'displayMode',\n header: tableLabels.displayMode,\n cell: ({ row }) => displayModeLabels[row.original.displayMode] ?? row.original.displayMode,\n },\n {\n accessorKey: 'currencyCode',\n header: tableLabels.currency,\n cell: ({ row }) => (row.original.currencyCode ? row.original.currencyCode.toUpperCase() : '\u2014'),\n },\n {\n id: 'promotion',\n header: tableLabels.promotion,\n cell: ({ row }) =>\n row.original.isPromotion ? (\n <span className=\"inline-flex items-center rounded-full border border-status-warning-border bg-status-warning-bg px-2 py-0.5 text-xs font-medium text-status-warning-text\">\n {tableLabels.promotionYes}\n </span>\n ) : (\n <span className=\"inline-flex items-center rounded-full border px-2 py-0.5 text-xs font-medium\">\n {tableLabels.promotionNo}\n </span>\n ),\n },\n {\n id: 'active',\n header: tableLabels.active,\n cell: ({ row }) =>\n row.original.isActive ? (\n <span className=\"inline-flex items-center rounded-full border border-status-success-border bg-status-success-bg px-2 py-0.5 text-xs font-medium text-status-success-text\">\n {tableLabels.activeYes}\n </span>\n ) : (\n <span className=\"inline-flex items-center rounded-full border px-2 py-0.5 text-xs font-medium\">\n {tableLabels.activeNo}\n </span>\n ),\n },\n ], [displayModeLabels, tableLabels])\n\n const filteredItems = React.useMemo(() => {\n const term = search.trim().toLowerCase()\n if (!term) return items\n return items.filter((item) => {\n const currency = (item.currencyCode ?? '').toLowerCase()\n const modeLabel = (displayModeLabels[item.displayMode] ?? item.displayMode).toLowerCase()\n return (\n item.code.toLowerCase().includes(term) ||\n item.title.toLowerCase().includes(term) ||\n currency.includes(term) ||\n modeLabel.includes(term)\n )\n })\n }, [displayModeLabels, items, search])\n\n const handleRowClick = React.useCallback((entry: PriceKind) => {\n openDialog({ mode: 'edit', entry })\n }, [openDialog])\n\n return (\n <>\n <section className=\"border bg-card text-card-foreground shadow-sm\">\n <div className=\"border-b px-6 py-4 space-y-1\">\n <h2 className=\"text-lg font-semibold\">{t('catalog.priceKinds.title', 'Price kinds')}</h2>\n <p className=\"text-sm text-muted-foreground\">\n {t('catalog.priceKinds.description', 'Configure reusable price kinds that control pricing columns and tax display.')}\n </p>\n </div>\n <div className=\"px-2 py-4 sm:px-4\">\n <DataTable<PriceKind>\n data={filteredItems}\n columns={columns}\n embedded\n isLoading={loading}\n searchValue={search}\n onSearchChange={setSearch}\n searchPlaceholder={tableLabels.search}\n emptyState={<p className=\"py-8 text-center text-sm text-muted-foreground\">{tableLabels.empty}</p>}\n actions={(\n <Button size=\"sm\" onClick={() => openDialog({ mode: 'create' })}>\n {t('catalog.priceKinds.actions.add', 'Add price kind')}\n </Button>\n )}\n refreshButton={{\n label: t('catalog.priceKinds.actions.refresh', 'Refresh'),\n onRefresh: () => { void loadItems() },\n isRefreshing: loading,\n }}\n rowActions={(entry) => (\n <RowActions\n items={[\n {\n id: 'edit',\n label: t('catalog.priceKinds.actions.edit', 'Edit'),\n onSelect: () => openDialog({ mode: 'edit', entry }),\n },\n {\n id: 'delete',\n label: t('catalog.priceKinds.actions.delete', 'Delete'),\n destructive: true,\n onSelect: () => { void handleDelete(entry) },\n },\n ]}\n />\n )}\n onRowClick={handleRowClick}\n />\n </div>\n <Dialog open={dialog !== null} onOpenChange={(open) => { if (!open) closeDialog(); }}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>\n {dialog?.mode === 'edit'\n ? t('catalog.priceKinds.dialog.editTitle', 'Edit price kind')\n : t('catalog.priceKinds.dialog.createTitle', 'Create price kind')}\n </DialogTitle>\n <DialogDescription>\n {dialog?.mode === 'edit'\n ? t('catalog.priceKinds.dialog.editDescription', 'Update labels or tax behavior for this price kind.')\n : t('catalog.priceKinds.dialog.createDescription', 'Define a reusable price kind for product pricing.')}\n </DialogDescription>\n </DialogHeader>\n <form className=\"space-y-4\" onKeyDown={formKeyHandler} onSubmit={(event) => { event.preventDefault(); void handleSubmit() }}>\n <div className=\"space-y-2\">\n <Label htmlFor=\"price-kind-code\">{t('catalog.priceKinds.form.codeLabel', 'Code')}</Label>\n <Input\n id=\"price-kind-code\"\n value={form.code}\n onChange={(event) => setForm((prev) => ({ ...prev, code: event.target.value }))}\n placeholder={t('catalog.priceKinds.form.codePlaceholder', 'e.g. regular')}\n className=\"font-mono uppercase\"\n disabled={dialog?.mode === 'edit'}\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"price-kind-title\">{t('catalog.priceKinds.form.titleLabel', 'Title')}</Label>\n <Input\n id=\"price-kind-title\"\n value={form.title}\n onChange={(event) => setForm((prev) => ({ ...prev, title: event.target.value }))}\n placeholder={t('catalog.priceKinds.form.titlePlaceholder', 'e.g. Regular price')}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>{t('catalog.priceKinds.form.displayModeLabel', 'Display mode')}</Label>\n <RadioGroup\n className=\"grid gap-2 md:grid-cols-2\"\n name=\"displayMode\"\n value={form.displayMode}\n onValueChange={(next) => setForm((prev) => ({ ...prev, displayMode: next as CatalogPriceDisplayMode }))}\n >\n {displayModeOptions.map((mode) => (\n <label\n key={mode.value}\n className={`flex cursor-pointer items-center gap-2 rounded-md border p-3 text-sm ${\n form.displayMode === mode.value ? 'border-primary bg-primary/5' : 'border-border'\n }`}\n >\n <Radio value={mode.value} />\n <span>{mode.label}</span>\n </label>\n ))}\n </RadioGroup>\n </div>\n <div className=\"space-y-2\">\n <Label>{t('catalog.priceKinds.form.currencyLabel', 'Currency (optional)')}</Label>\n <DictionaryEntrySelect\n value={form.currencyCode || undefined}\n onChange={(value) => setForm((prev) => ({ ...prev, currencyCode: value ?? '' }))}\n fetchOptions={currencyOptionsLoader}\n labels={currencyLabels}\n allowInlineCreate={false}\n sortOptions=\"none\"\n />\n </div>\n <div className=\"flex flex-col gap-2\">\n <label className=\"flex items-center gap-2 text-sm font-medium\">\n <input\n type=\"checkbox\"\n className=\"h-4 w-4 rounded border\"\n checked={form.isPromotion}\n onChange={(event) => setForm((prev) => ({ ...prev, isPromotion: event.target.checked }))}\n />\n {t('catalog.priceKinds.form.promotionLabel', 'Mark as promotion')}\n </label>\n <label className=\"flex items-center gap-2 text-sm font-medium\">\n <input\n type=\"checkbox\"\n className=\"h-4 w-4 rounded border\"\n checked={form.isActive}\n onChange={(event) => setForm((prev) => ({ ...prev, isActive: event.target.checked }))}\n />\n {t('catalog.priceKinds.form.activeLabel', 'Active')}\n </label>\n </div>\n {error ? <p className=\"text-sm text-status-error-text\">{error}</p> : null}\n </form>\n <DialogFooter>\n <Button variant=\"ghost\" onClick={closeDialog}>\n {t('catalog.priceKinds.actions.cancel', 'Cancel')}\n </Button>\n <Button onClick={() => void handleSubmit()} disabled={submitting}>\n {dialog?.mode === 'edit'\n ? t('catalog.priceKinds.actions.saveChanges', 'Save changes')\n : t('catalog.priceKinds.actions.create', 'Create')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </section>\n {ConfirmDialogElement}\n </>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -173,7 +173,7 @@ function ProductCategorizeSection({
|
|
|
173
173
|
}
|
|
174
174
|
),
|
|
175
175
|
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("catalog.products.create.organize.categoriesHelp", "Assign products to one or more taxonomy nodes.") }),
|
|
176
|
-
errors.categoryIds ? /* @__PURE__ */ jsx("p", { className: "text-xs text-
|
|
176
|
+
errors.categoryIds ? /* @__PURE__ */ jsx("p", { className: "text-xs text-status-error-text", children: errors.categoryIds }) : null
|
|
177
177
|
] }),
|
|
178
178
|
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
179
179
|
/* @__PURE__ */ jsx(Label, { children: t("catalog.products.create.organize.channelsLabel", "Sales channels") }),
|
|
@@ -191,7 +191,7 @@ function ProductCategorizeSection({
|
|
|
191
191
|
}
|
|
192
192
|
),
|
|
193
193
|
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("catalog.products.create.organize.channelsHelp", "Selected channels will receive default offers for this product.") }),
|
|
194
|
-
errors.channelIds ? /* @__PURE__ */ jsx("p", { className: "text-xs text-
|
|
194
|
+
errors.channelIds ? /* @__PURE__ */ jsx("p", { className: "text-xs text-status-error-text", children: errors.channelIds }) : null
|
|
195
195
|
] }),
|
|
196
196
|
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
197
197
|
/* @__PURE__ */ jsx(Label, { children: t("catalog.products.create.organize.tagsLabel", "Tags") }),
|
|
@@ -207,7 +207,7 @@ function ProductCategorizeSection({
|
|
|
207
207
|
}
|
|
208
208
|
),
|
|
209
209
|
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("catalog.products.create.organize.tagsHelp", "Describe products with shared labels to build quick filters.") }),
|
|
210
|
-
errors.tags ? /* @__PURE__ */ jsx("p", { className: "text-xs text-
|
|
210
|
+
errors.tags ? /* @__PURE__ */ jsx("p", { className: "text-xs text-status-error-text", children: errors.tags }) : null
|
|
211
211
|
] })
|
|
212
212
|
] });
|
|
213
213
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/catalog/components/products/ProductCategorizeSection.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { TagsInput } from '@open-mercato/ui/backend/inputs/TagsInput'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport type { ProductFormValues } from './productForm'\n\nexport type ProductCategorizePickerOption = {\n value: string\n label: string\n description?: string | null\n}\n\nconst formatCategoryLabel = (name: string | null | undefined, fallback: string, parentName?: string | null) => {\n const base = typeof name === 'string' && name.trim().length ? name.trim() : fallback\n const parent = typeof parentName === 'string' && parentName.trim().length ? parentName.trim() : null\n return parent ? `${base} / ${parent}` : base\n}\n\ntype ProductCategorizeSectionProps = {\n values: ProductFormValues\n setValue: (id: string, value: unknown) => void\n errors: Record<string, string>\n initialCategoryOptions?: ProductCategorizePickerOption[]\n initialChannelOptions?: ProductCategorizePickerOption[]\n initialTagOptions?: ProductCategorizePickerOption[]\n}\n\nexport function ProductCategorizeSection({\n values,\n setValue,\n errors,\n initialCategoryOptions,\n initialChannelOptions,\n initialTagOptions,\n}: ProductCategorizeSectionProps) {\n const t = useT()\n const [categoryOptionsMap, setCategoryOptionsMap] = React.useState<Record<string, ProductCategorizePickerOption>>({})\n const [channelOptionsMap, setChannelOptionsMap] = React.useState<Record<string, ProductCategorizePickerOption>>({})\n const [tagOptionsMap, setTagOptionsMap] = React.useState<Record<string, ProductCategorizePickerOption>>({})\n\n const registerPickerOptions = React.useCallback(\n (\n setter: React.Dispatch<React.SetStateAction<Record<string, ProductCategorizePickerOption>>>,\n options: ProductCategorizePickerOption[],\n ) => {\n setter((prev) => {\n const next = { ...prev }\n options.forEach((option) => {\n if (option.value) next[option.value] = option\n })\n return next\n })\n },\n [],\n )\n\n const categorySuggestions = React.useMemo(() => Object.values(categoryOptionsMap), [categoryOptionsMap])\n const channelSuggestions = React.useMemo(() => Object.values(channelOptionsMap), [channelOptionsMap])\n const tagSuggestions = React.useMemo(() => Object.values(tagOptionsMap), [tagOptionsMap])\n\n React.useEffect(() => {\n if (initialCategoryOptions?.length) {\n registerPickerOptions(setCategoryOptionsMap, initialCategoryOptions)\n }\n }, [initialCategoryOptions, registerPickerOptions])\n\n React.useEffect(() => {\n if (initialChannelOptions?.length) {\n registerPickerOptions(setChannelOptionsMap, initialChannelOptions)\n }\n }, [initialChannelOptions, registerPickerOptions])\n\n React.useEffect(() => {\n if (initialTagOptions?.length) {\n registerPickerOptions(setTagOptionsMap, initialTagOptions)\n }\n }, [initialTagOptions, registerPickerOptions])\n\n const resolveCategoryLabel = React.useCallback(\n (id: string) => categoryOptionsMap[id]?.label ?? id,\n [categoryOptionsMap],\n )\n const resolveCategoryDescription = React.useCallback(\n (id: string) => categoryOptionsMap[id]?.description ?? null,\n [categoryOptionsMap],\n )\n const resolveChannelLabel = React.useCallback(\n (id: string) => channelOptionsMap[id]?.label ?? id,\n [channelOptionsMap],\n )\n const resolveChannelDescription = React.useCallback(\n (id: string) => channelOptionsMap[id]?.description ?? null,\n [channelOptionsMap],\n )\n const resolveTagLabel = React.useCallback((id: string) => tagOptionsMap[id]?.label ?? id, [tagOptionsMap])\n\n const loadCategorySuggestions = React.useCallback(\n async (term?: string) => {\n try {\n const params = new URLSearchParams({ pageSize: '200', view: 'manage' })\n if (term && term.trim().length) params.set('search', term.trim())\n const payload = await readApiResultOrThrow<{ items?: Array<{ id?: string; name?: string; parentName?: string | null }> }>(\n `/api/catalog/categories?${params.toString()}`,\n undefined,\n { errorMessage: t('catalog.products.filters.categoriesLoadError', 'Failed to load categories') },\n )\n const items = Array.isArray(payload?.items) ? payload.items : []\n const options = items\n .map((entry) => {\n const value = typeof entry.id === 'string' ? entry.id : null\n if (!value) return null\n const parentName =\n typeof entry.parentName === 'string' && entry.parentName.trim().length ? entry.parentName : null\n const label = formatCategoryLabel(\n typeof entry.name === 'string' ? entry.name : null,\n value,\n parentName,\n )\n const description =\n parentName && !label.toLowerCase().includes(parentName.toLowerCase()) ? parentName : null\n return { value, label, description }\n })\n .filter(\n (\n option: { value: string; label: string; description: string | null } | null,\n ): option is { value: string; label: string; description: string | null } => !!option,\n )\n registerPickerOptions(setCategoryOptionsMap, options)\n return options\n } catch {\n return []\n }\n },\n [registerPickerOptions, t],\n )\n\n const loadChannelSuggestions = React.useCallback(\n async (term?: string) => {\n try {\n const params = new URLSearchParams({ pageSize: '100', isActive: 'true' })\n if (term && term.trim().length) params.set('search', term.trim())\n const payload = await readApiResultOrThrow<{ items?: Array<{ id?: string; name?: string; code?: string }> }>(\n `/api/sales/channels?${params.toString()}`,\n undefined,\n { errorMessage: t('catalog.products.filters.channelsLoadError', 'Failed to load channels') },\n )\n const items = Array.isArray(payload?.items) ? payload.items : []\n const options = items\n .map((entry) => {\n const value = typeof entry.id === 'string' ? entry.id : null\n if (!value) return null\n const label =\n typeof entry.name === 'string' && entry.name.trim().length\n ? entry.name\n : typeof entry.code === 'string' && entry.code.trim().length\n ? entry.code\n : value\n const description = typeof entry.code === 'string' && entry.code.trim().length ? entry.code : null\n return { value, label, description }\n })\n .filter(\n (\n option: { value: string; label: string; description: string | null } | null,\n ): option is { value: string; label: string; description: string | null } => !!option,\n )\n registerPickerOptions(setChannelOptionsMap, options)\n return options\n } catch {\n return []\n }\n },\n [registerPickerOptions, t],\n )\n\n const loadTagSuggestions = React.useCallback(\n async (term?: string) => {\n try {\n const params = new URLSearchParams({ pageSize: '100' })\n if (term && term.trim().length) params.set('search', term.trim())\n const payload = await readApiResultOrThrow<{ items?: Array<{ label?: string }> }>(\n `/api/catalog/tags?${params.toString()}`,\n undefined,\n { errorMessage: t('catalog.products.filters.tagsLoadError', 'Failed to load tags') },\n )\n const items = Array.isArray(payload?.items) ? payload.items : []\n const options = items\n .map((entry) => {\n const rawLabel = typeof entry.label === 'string' ? entry.label.trim() : ''\n if (!rawLabel) return null\n return { value: rawLabel, label: rawLabel }\n })\n .filter(\n (option: { value: string; label: string } | null): option is { value: string; label: string } => !!option,\n )\n registerPickerOptions(setTagOptionsMap, options)\n return options\n } catch {\n return []\n }\n },\n [registerPickerOptions, t],\n )\n\n return (\n <div className=\"space-y-6\">\n <div className=\"space-y-2\">\n <Label>{t('catalog.products.create.organize.categoriesLabel', 'Categories')}</Label>\n <TagsInput\n value={Array.isArray(values.categoryIds) ? values.categoryIds : []}\n onChange={(next) => setValue('categoryIds', next)}\n suggestions={categorySuggestions}\n loadSuggestions={loadCategorySuggestions}\n allowCustomValues={false}\n resolveLabel={resolveCategoryLabel}\n resolveDescription={resolveCategoryDescription}\n placeholder={t('catalog.products.create.organize.categoriesPlaceholder', 'Search categories')}\n />\n <p className=\"text-xs text-muted-foreground\">\n {t('catalog.products.create.organize.categoriesHelp', 'Assign products to one or more taxonomy nodes.')}\n </p>\n {errors.categoryIds ? <p className=\"text-xs text-red-600\">{errors.categoryIds}</p> : null}\n </div>\n\n <div className=\"space-y-2\">\n <Label>{t('catalog.products.create.organize.channelsLabel', 'Sales channels')}</Label>\n <TagsInput\n value={Array.isArray(values.channelIds) ? values.channelIds : []}\n onChange={(next) => setValue('channelIds', next)}\n suggestions={channelSuggestions}\n loadSuggestions={loadChannelSuggestions}\n allowCustomValues={false}\n resolveLabel={resolveChannelLabel}\n resolveDescription={resolveChannelDescription}\n placeholder={t('catalog.products.create.organize.channelsPlaceholder', 'Pick channels')}\n />\n <p className=\"text-xs text-muted-foreground\">\n {t('catalog.products.create.organize.channelsHelp', 'Selected channels will receive default offers for this product.')}\n </p>\n {errors.channelIds ? <p className=\"text-xs text-red-600\">{errors.channelIds}</p> : null}\n </div>\n\n <div className=\"space-y-2\">\n <Label>{t('catalog.products.create.organize.tagsLabel', 'Tags')}</Label>\n <TagsInput\n value={Array.isArray(values.tags) ? values.tags : []}\n onChange={(next) => setValue('tags', next)}\n suggestions={tagSuggestions}\n loadSuggestions={loadTagSuggestions}\n resolveLabel={resolveTagLabel}\n placeholder={t('catalog.products.create.organize.tagsPlaceholder', 'Add tag and press Enter')}\n />\n <p className=\"text-xs text-muted-foreground\">\n {t('catalog.products.create.organize.tagsHelp', 'Describe products with shared labels to build quick filters.')}\n </p>\n {errors.tags ? <p className=\"text-xs text-red-600\">{errors.tags}</p> : null}\n </div>\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { TagsInput } from '@open-mercato/ui/backend/inputs/TagsInput'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport type { ProductFormValues } from './productForm'\n\nexport type ProductCategorizePickerOption = {\n value: string\n label: string\n description?: string | null\n}\n\nconst formatCategoryLabel = (name: string | null | undefined, fallback: string, parentName?: string | null) => {\n const base = typeof name === 'string' && name.trim().length ? name.trim() : fallback\n const parent = typeof parentName === 'string' && parentName.trim().length ? parentName.trim() : null\n return parent ? `${base} / ${parent}` : base\n}\n\ntype ProductCategorizeSectionProps = {\n values: ProductFormValues\n setValue: (id: string, value: unknown) => void\n errors: Record<string, string>\n initialCategoryOptions?: ProductCategorizePickerOption[]\n initialChannelOptions?: ProductCategorizePickerOption[]\n initialTagOptions?: ProductCategorizePickerOption[]\n}\n\nexport function ProductCategorizeSection({\n values,\n setValue,\n errors,\n initialCategoryOptions,\n initialChannelOptions,\n initialTagOptions,\n}: ProductCategorizeSectionProps) {\n const t = useT()\n const [categoryOptionsMap, setCategoryOptionsMap] = React.useState<Record<string, ProductCategorizePickerOption>>({})\n const [channelOptionsMap, setChannelOptionsMap] = React.useState<Record<string, ProductCategorizePickerOption>>({})\n const [tagOptionsMap, setTagOptionsMap] = React.useState<Record<string, ProductCategorizePickerOption>>({})\n\n const registerPickerOptions = React.useCallback(\n (\n setter: React.Dispatch<React.SetStateAction<Record<string, ProductCategorizePickerOption>>>,\n options: ProductCategorizePickerOption[],\n ) => {\n setter((prev) => {\n const next = { ...prev }\n options.forEach((option) => {\n if (option.value) next[option.value] = option\n })\n return next\n })\n },\n [],\n )\n\n const categorySuggestions = React.useMemo(() => Object.values(categoryOptionsMap), [categoryOptionsMap])\n const channelSuggestions = React.useMemo(() => Object.values(channelOptionsMap), [channelOptionsMap])\n const tagSuggestions = React.useMemo(() => Object.values(tagOptionsMap), [tagOptionsMap])\n\n React.useEffect(() => {\n if (initialCategoryOptions?.length) {\n registerPickerOptions(setCategoryOptionsMap, initialCategoryOptions)\n }\n }, [initialCategoryOptions, registerPickerOptions])\n\n React.useEffect(() => {\n if (initialChannelOptions?.length) {\n registerPickerOptions(setChannelOptionsMap, initialChannelOptions)\n }\n }, [initialChannelOptions, registerPickerOptions])\n\n React.useEffect(() => {\n if (initialTagOptions?.length) {\n registerPickerOptions(setTagOptionsMap, initialTagOptions)\n }\n }, [initialTagOptions, registerPickerOptions])\n\n const resolveCategoryLabel = React.useCallback(\n (id: string) => categoryOptionsMap[id]?.label ?? id,\n [categoryOptionsMap],\n )\n const resolveCategoryDescription = React.useCallback(\n (id: string) => categoryOptionsMap[id]?.description ?? null,\n [categoryOptionsMap],\n )\n const resolveChannelLabel = React.useCallback(\n (id: string) => channelOptionsMap[id]?.label ?? id,\n [channelOptionsMap],\n )\n const resolveChannelDescription = React.useCallback(\n (id: string) => channelOptionsMap[id]?.description ?? null,\n [channelOptionsMap],\n )\n const resolveTagLabel = React.useCallback((id: string) => tagOptionsMap[id]?.label ?? id, [tagOptionsMap])\n\n const loadCategorySuggestions = React.useCallback(\n async (term?: string) => {\n try {\n const params = new URLSearchParams({ pageSize: '200', view: 'manage' })\n if (term && term.trim().length) params.set('search', term.trim())\n const payload = await readApiResultOrThrow<{ items?: Array<{ id?: string; name?: string; parentName?: string | null }> }>(\n `/api/catalog/categories?${params.toString()}`,\n undefined,\n { errorMessage: t('catalog.products.filters.categoriesLoadError', 'Failed to load categories') },\n )\n const items = Array.isArray(payload?.items) ? payload.items : []\n const options = items\n .map((entry) => {\n const value = typeof entry.id === 'string' ? entry.id : null\n if (!value) return null\n const parentName =\n typeof entry.parentName === 'string' && entry.parentName.trim().length ? entry.parentName : null\n const label = formatCategoryLabel(\n typeof entry.name === 'string' ? entry.name : null,\n value,\n parentName,\n )\n const description =\n parentName && !label.toLowerCase().includes(parentName.toLowerCase()) ? parentName : null\n return { value, label, description }\n })\n .filter(\n (\n option: { value: string; label: string; description: string | null } | null,\n ): option is { value: string; label: string; description: string | null } => !!option,\n )\n registerPickerOptions(setCategoryOptionsMap, options)\n return options\n } catch {\n return []\n }\n },\n [registerPickerOptions, t],\n )\n\n const loadChannelSuggestions = React.useCallback(\n async (term?: string) => {\n try {\n const params = new URLSearchParams({ pageSize: '100', isActive: 'true' })\n if (term && term.trim().length) params.set('search', term.trim())\n const payload = await readApiResultOrThrow<{ items?: Array<{ id?: string; name?: string; code?: string }> }>(\n `/api/sales/channels?${params.toString()}`,\n undefined,\n { errorMessage: t('catalog.products.filters.channelsLoadError', 'Failed to load channels') },\n )\n const items = Array.isArray(payload?.items) ? payload.items : []\n const options = items\n .map((entry) => {\n const value = typeof entry.id === 'string' ? entry.id : null\n if (!value) return null\n const label =\n typeof entry.name === 'string' && entry.name.trim().length\n ? entry.name\n : typeof entry.code === 'string' && entry.code.trim().length\n ? entry.code\n : value\n const description = typeof entry.code === 'string' && entry.code.trim().length ? entry.code : null\n return { value, label, description }\n })\n .filter(\n (\n option: { value: string; label: string; description: string | null } | null,\n ): option is { value: string; label: string; description: string | null } => !!option,\n )\n registerPickerOptions(setChannelOptionsMap, options)\n return options\n } catch {\n return []\n }\n },\n [registerPickerOptions, t],\n )\n\n const loadTagSuggestions = React.useCallback(\n async (term?: string) => {\n try {\n const params = new URLSearchParams({ pageSize: '100' })\n if (term && term.trim().length) params.set('search', term.trim())\n const payload = await readApiResultOrThrow<{ items?: Array<{ label?: string }> }>(\n `/api/catalog/tags?${params.toString()}`,\n undefined,\n { errorMessage: t('catalog.products.filters.tagsLoadError', 'Failed to load tags') },\n )\n const items = Array.isArray(payload?.items) ? payload.items : []\n const options = items\n .map((entry) => {\n const rawLabel = typeof entry.label === 'string' ? entry.label.trim() : ''\n if (!rawLabel) return null\n return { value: rawLabel, label: rawLabel }\n })\n .filter(\n (option: { value: string; label: string } | null): option is { value: string; label: string } => !!option,\n )\n registerPickerOptions(setTagOptionsMap, options)\n return options\n } catch {\n return []\n }\n },\n [registerPickerOptions, t],\n )\n\n return (\n <div className=\"space-y-6\">\n <div className=\"space-y-2\">\n <Label>{t('catalog.products.create.organize.categoriesLabel', 'Categories')}</Label>\n <TagsInput\n value={Array.isArray(values.categoryIds) ? values.categoryIds : []}\n onChange={(next) => setValue('categoryIds', next)}\n suggestions={categorySuggestions}\n loadSuggestions={loadCategorySuggestions}\n allowCustomValues={false}\n resolveLabel={resolveCategoryLabel}\n resolveDescription={resolveCategoryDescription}\n placeholder={t('catalog.products.create.organize.categoriesPlaceholder', 'Search categories')}\n />\n <p className=\"text-xs text-muted-foreground\">\n {t('catalog.products.create.organize.categoriesHelp', 'Assign products to one or more taxonomy nodes.')}\n </p>\n {errors.categoryIds ? <p className=\"text-xs text-status-error-text\">{errors.categoryIds}</p> : null}\n </div>\n\n <div className=\"space-y-2\">\n <Label>{t('catalog.products.create.organize.channelsLabel', 'Sales channels')}</Label>\n <TagsInput\n value={Array.isArray(values.channelIds) ? values.channelIds : []}\n onChange={(next) => setValue('channelIds', next)}\n suggestions={channelSuggestions}\n loadSuggestions={loadChannelSuggestions}\n allowCustomValues={false}\n resolveLabel={resolveChannelLabel}\n resolveDescription={resolveChannelDescription}\n placeholder={t('catalog.products.create.organize.channelsPlaceholder', 'Pick channels')}\n />\n <p className=\"text-xs text-muted-foreground\">\n {t('catalog.products.create.organize.channelsHelp', 'Selected channels will receive default offers for this product.')}\n </p>\n {errors.channelIds ? <p className=\"text-xs text-status-error-text\">{errors.channelIds}</p> : null}\n </div>\n\n <div className=\"space-y-2\">\n <Label>{t('catalog.products.create.organize.tagsLabel', 'Tags')}</Label>\n <TagsInput\n value={Array.isArray(values.tags) ? values.tags : []}\n onChange={(next) => setValue('tags', next)}\n suggestions={tagSuggestions}\n loadSuggestions={loadTagSuggestions}\n resolveLabel={resolveTagLabel}\n placeholder={t('catalog.products.create.organize.tagsPlaceholder', 'Add tag and press Enter')}\n />\n <p className=\"text-xs text-muted-foreground\">\n {t('catalog.products.create.organize.tagsHelp', 'Describe products with shared labels to build quick filters.')}\n </p>\n {errors.tags ? <p className=\"text-xs text-status-error-text\">{errors.tags}</p> : null}\n </div>\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -158,7 +158,7 @@ function ProductMediaManager({
|
|
|
158
158
|
]
|
|
159
159
|
}
|
|
160
160
|
),
|
|
161
|
-
error ? /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-
|
|
161
|
+
error ? /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-status-error-text", children: error }) : null,
|
|
162
162
|
items.length ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-3 sm:grid-cols-3", children: items.map((item) => {
|
|
163
163
|
const isDefault = defaultMediaId === item.id;
|
|
164
164
|
const slug = slugifyAttachmentFileName(item.fileName);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/catalog/components/products/ProductMediaManager.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Upload, Image as ImageIcon, Trash2, Star } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { buildAttachmentImageUrl, slugifyAttachmentFileName } from '@open-mercato/core/modules/attachments/lib/imageUrls'\n\nexport type ProductMediaItem = {\n id: string\n url: string\n fileName: string\n fileSize: number\n thumbnailUrl?: string | null\n}\n\ntype Props = {\n entityId: string\n draftRecordId: string\n items: ProductMediaItem[]\n defaultMediaId: string | null\n onItemsChange: (items: ProductMediaItem[]) => void\n onDefaultChange: (attachmentId: string | null) => void\n}\n\nfunction humanFileSize(size: number): string {\n if (!Number.isFinite(size)) return `${size}`\n const units = ['B', 'KB', 'MB', 'GB']\n let idx = 0\n let value = size\n while (value >= 1024 && idx < units.length - 1) {\n value /= 1024\n idx++\n }\n return `${value.toFixed(idx === 0 ? 0 : 1)} ${units[idx]}`\n}\n\nexport function ProductMediaManager({\n entityId,\n draftRecordId,\n items,\n defaultMediaId,\n onItemsChange,\n onDefaultChange,\n}: Props) {\n const t = useT()\n const fileInputRef = React.useRef<HTMLInputElement | null>(null)\n const [error, setError] = React.useState<string | null>(null)\n const [isUploading, setUploading] = React.useState(false)\n const [isDragOver, setDragOver] = React.useState(false)\n\n const acceptFiles = React.useCallback(\n async (files: FileList | null) => {\n if (!files || !files.length || !draftRecordId) return\n setError(null)\n setUploading(true)\n let nextItems = items\n let defaultId = defaultMediaId\n try {\n for (const file of Array.from(files)) {\n if (!file.type.startsWith('image/')) {\n setError(t('catalog.products.media.errors.imagesOnly', 'Only image files are supported.'))\n continue\n }\n const fd = new FormData()\n fd.set('entityId', entityId)\n fd.set('recordId', draftRecordId)\n fd.set('file', file)\n // optimistic-lock-exempt: media attach/detach/reorder\n const call = await apiCall<{ ok?: boolean; item?: ProductMediaItem; error?: string }>(\n '/api/attachments',\n { method: 'POST', body: fd },\n { fallback: null },\n )\n if (!call.ok || !call.result?.item) {\n const message = call.result?.error ?? t('catalog.products.media.errors.uploadFailed', 'Upload failed.')\n setError(message)\n break\n }\n const existingIds = new Set(nextItems.map((entry) => entry.id))\n if (existingIds.has(call.result.item.id)) continue\n const slug = slugifyAttachmentFileName(call.result.item.fileName)\n const uploaded = {\n ...call.result.item,\n thumbnailUrl:\n call.result.item.thumbnailUrl ??\n buildAttachmentImageUrl(call.result.item.id, { width: 360, height: 360, slug }),\n }\n nextItems = [...nextItems, uploaded]\n if (!defaultId) {\n defaultId = uploaded.id\n }\n }\n if (nextItems !== items) {\n onItemsChange(nextItems)\n }\n if (!defaultMediaId && defaultId) {\n onDefaultChange(defaultId)\n }\n } finally {\n setUploading(false)\n if (fileInputRef.current) {\n fileInputRef.current.value = ''\n }\n }\n },\n [defaultMediaId, draftRecordId, entityId, items, onDefaultChange, onItemsChange, t],\n )\n\n const handleRemove = React.useCallback(\n async (attachmentId: string) => {\n setError(null)\n // optimistic-lock-exempt: media attach/detach/reorder\n const call = await apiCall<{ ok?: boolean; error?: string }>(\n `/api/attachments?id=${encodeURIComponent(attachmentId)}`,\n { method: 'DELETE' },\n { fallback: null },\n )\n if (!call.ok) {\n setError(call.result?.error ?? t('catalog.products.media.errors.deleteFailed', 'Failed to delete media.'))\n return\n }\n const next = items.filter((item) => item.id !== attachmentId)\n onItemsChange(next)\n if (defaultMediaId === attachmentId) {\n onDefaultChange(next[0]?.id ?? null)\n }\n },\n [defaultMediaId, items, onDefaultChange, onItemsChange, t],\n )\n\n const onDrop = React.useCallback(\n (event: React.DragEvent<HTMLDivElement>) => {\n event.preventDefault()\n event.stopPropagation()\n setDragOver(false)\n void acceptFiles(event.dataTransfer?.files ?? null)\n },\n [acceptFiles],\n )\n\n const onDragOver = React.useCallback((event: React.DragEvent<HTMLDivElement>) => {\n event.preventDefault()\n event.stopPropagation()\n setDragOver(true)\n }, [])\n\n const onDragLeave = React.useCallback((event: React.DragEvent<HTMLDivElement>) => {\n event.preventDefault()\n event.stopPropagation()\n setDragOver(false)\n }, [])\n\n const pickFiles = React.useCallback(() => {\n fileInputRef.current?.click()\n }, [])\n\n return (\n <div className=\"space-y-3\">\n <label className=\"text-sm font-medium\">{t('catalog.products.media.label', 'Media')}</label>\n <div\n className={cn(\n 'flex flex-col items-center justify-center rounded-lg border border-dashed p-6 text-center transition-colors',\n isDragOver ? 'border-primary bg-primary/5' : 'border-muted-foreground/30',\n )}\n onDrop={onDrop}\n onDragOver={onDragOver}\n onDragLeave={onDragLeave}\n role=\"presentation\"\n >\n <Upload className=\"mx-auto h-6 w-6 text-muted-foreground\" />\n <p className=\"mt-2 text-sm text-muted-foreground\">\n {t('catalog.products.media.dropHint', 'Drag and drop images here or click to upload.')}\n </p>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" className=\"mt-4\" onClick={pickFiles} disabled={isUploading}>\n {isUploading ? t('catalog.products.media.uploading', 'Uploading\u2026') : t('catalog.products.media.choose', 'Choose files')}\n </Button>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n multiple\n className=\"hidden\"\n onChange={(event) => void acceptFiles(event.target.files)}\n />\n </div>\n {error ? <p className=\"text-xs font-medium text-
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Upload, Image as ImageIcon, Trash2, Star } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { buildAttachmentImageUrl, slugifyAttachmentFileName } from '@open-mercato/core/modules/attachments/lib/imageUrls'\n\nexport type ProductMediaItem = {\n id: string\n url: string\n fileName: string\n fileSize: number\n thumbnailUrl?: string | null\n}\n\ntype Props = {\n entityId: string\n draftRecordId: string\n items: ProductMediaItem[]\n defaultMediaId: string | null\n onItemsChange: (items: ProductMediaItem[]) => void\n onDefaultChange: (attachmentId: string | null) => void\n}\n\nfunction humanFileSize(size: number): string {\n if (!Number.isFinite(size)) return `${size}`\n const units = ['B', 'KB', 'MB', 'GB']\n let idx = 0\n let value = size\n while (value >= 1024 && idx < units.length - 1) {\n value /= 1024\n idx++\n }\n return `${value.toFixed(idx === 0 ? 0 : 1)} ${units[idx]}`\n}\n\nexport function ProductMediaManager({\n entityId,\n draftRecordId,\n items,\n defaultMediaId,\n onItemsChange,\n onDefaultChange,\n}: Props) {\n const t = useT()\n const fileInputRef = React.useRef<HTMLInputElement | null>(null)\n const [error, setError] = React.useState<string | null>(null)\n const [isUploading, setUploading] = React.useState(false)\n const [isDragOver, setDragOver] = React.useState(false)\n\n const acceptFiles = React.useCallback(\n async (files: FileList | null) => {\n if (!files || !files.length || !draftRecordId) return\n setError(null)\n setUploading(true)\n let nextItems = items\n let defaultId = defaultMediaId\n try {\n for (const file of Array.from(files)) {\n if (!file.type.startsWith('image/')) {\n setError(t('catalog.products.media.errors.imagesOnly', 'Only image files are supported.'))\n continue\n }\n const fd = new FormData()\n fd.set('entityId', entityId)\n fd.set('recordId', draftRecordId)\n fd.set('file', file)\n // optimistic-lock-exempt: media attach/detach/reorder\n const call = await apiCall<{ ok?: boolean; item?: ProductMediaItem; error?: string }>(\n '/api/attachments',\n { method: 'POST', body: fd },\n { fallback: null },\n )\n if (!call.ok || !call.result?.item) {\n const message = call.result?.error ?? t('catalog.products.media.errors.uploadFailed', 'Upload failed.')\n setError(message)\n break\n }\n const existingIds = new Set(nextItems.map((entry) => entry.id))\n if (existingIds.has(call.result.item.id)) continue\n const slug = slugifyAttachmentFileName(call.result.item.fileName)\n const uploaded = {\n ...call.result.item,\n thumbnailUrl:\n call.result.item.thumbnailUrl ??\n buildAttachmentImageUrl(call.result.item.id, { width: 360, height: 360, slug }),\n }\n nextItems = [...nextItems, uploaded]\n if (!defaultId) {\n defaultId = uploaded.id\n }\n }\n if (nextItems !== items) {\n onItemsChange(nextItems)\n }\n if (!defaultMediaId && defaultId) {\n onDefaultChange(defaultId)\n }\n } finally {\n setUploading(false)\n if (fileInputRef.current) {\n fileInputRef.current.value = ''\n }\n }\n },\n [defaultMediaId, draftRecordId, entityId, items, onDefaultChange, onItemsChange, t],\n )\n\n const handleRemove = React.useCallback(\n async (attachmentId: string) => {\n setError(null)\n // optimistic-lock-exempt: media attach/detach/reorder\n const call = await apiCall<{ ok?: boolean; error?: string }>(\n `/api/attachments?id=${encodeURIComponent(attachmentId)}`,\n { method: 'DELETE' },\n { fallback: null },\n )\n if (!call.ok) {\n setError(call.result?.error ?? t('catalog.products.media.errors.deleteFailed', 'Failed to delete media.'))\n return\n }\n const next = items.filter((item) => item.id !== attachmentId)\n onItemsChange(next)\n if (defaultMediaId === attachmentId) {\n onDefaultChange(next[0]?.id ?? null)\n }\n },\n [defaultMediaId, items, onDefaultChange, onItemsChange, t],\n )\n\n const onDrop = React.useCallback(\n (event: React.DragEvent<HTMLDivElement>) => {\n event.preventDefault()\n event.stopPropagation()\n setDragOver(false)\n void acceptFiles(event.dataTransfer?.files ?? null)\n },\n [acceptFiles],\n )\n\n const onDragOver = React.useCallback((event: React.DragEvent<HTMLDivElement>) => {\n event.preventDefault()\n event.stopPropagation()\n setDragOver(true)\n }, [])\n\n const onDragLeave = React.useCallback((event: React.DragEvent<HTMLDivElement>) => {\n event.preventDefault()\n event.stopPropagation()\n setDragOver(false)\n }, [])\n\n const pickFiles = React.useCallback(() => {\n fileInputRef.current?.click()\n }, [])\n\n return (\n <div className=\"space-y-3\">\n <label className=\"text-sm font-medium\">{t('catalog.products.media.label', 'Media')}</label>\n <div\n className={cn(\n 'flex flex-col items-center justify-center rounded-lg border border-dashed p-6 text-center transition-colors',\n isDragOver ? 'border-primary bg-primary/5' : 'border-muted-foreground/30',\n )}\n onDrop={onDrop}\n onDragOver={onDragOver}\n onDragLeave={onDragLeave}\n role=\"presentation\"\n >\n <Upload className=\"mx-auto h-6 w-6 text-muted-foreground\" />\n <p className=\"mt-2 text-sm text-muted-foreground\">\n {t('catalog.products.media.dropHint', 'Drag and drop images here or click to upload.')}\n </p>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" className=\"mt-4\" onClick={pickFiles} disabled={isUploading}>\n {isUploading ? t('catalog.products.media.uploading', 'Uploading\u2026') : t('catalog.products.media.choose', 'Choose files')}\n </Button>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n multiple\n className=\"hidden\"\n onChange={(event) => void acceptFiles(event.target.files)}\n />\n </div>\n {error ? <p className=\"text-xs font-medium text-status-error-text\">{error}</p> : null}\n {items.length ? (\n <div className=\"grid grid-cols-2 gap-3 sm:grid-cols-3\">\n {items.map((item) => {\n const isDefault = defaultMediaId === item.id\n const slug = slugifyAttachmentFileName(item.fileName)\n const thumbnail =\n item.thumbnailUrl ||\n buildAttachmentImageUrl(item.id, { width: 360, height: 360, slug })\n return (\n <div key={item.id} className=\"flex flex-col rounded-md border bg-card\">\n <div className=\"relative aspect-square overflow-hidden rounded-t-md bg-muted\">\n {thumbnail ? (\n <img src={thumbnail} alt={item.fileName} className=\"h-full w-full object-cover\" />\n ) : (\n <div className=\"flex h-full items-center justify-center text-muted-foreground\">\n <ImageIcon className=\"h-8 w-8\" />\n </div>\n )}\n <div className=\"absolute right-1 top-1 flex gap-1\">\n <Button\n type=\"button\"\n variant={isDefault ? 'default' : 'secondary'}\n size=\"icon\"\n className={cn('h-7 w-7', isDefault ? '' : 'opacity-80')}\n onClick={() => onDefaultChange(item.id)}\n >\n <Star className=\"h-3.5 w-3.5\" />\n </Button>\n <Button type=\"button\" variant=\"secondary\" size=\"icon\" className=\"h-7 w-7\" onClick={() => void handleRemove(item.id)}>\n <Trash2 className=\"h-3.5 w-3.5\" />\n </Button>\n </div>\n </div>\n <div className=\"p-2\">\n <p className=\"line-clamp-1 text-sm font-medium\">{item.fileName}</p>\n <p className=\"text-xs text-muted-foreground\">{humanFileSize(item.fileSize)}</p>\n {isDefault ? (\n <p className=\"text-xs font-semibold text-primary\">\n {t('catalog.products.media.default', 'Default preview')}\n </p>\n ) : null}\n </div>\n </div>\n )\n })}\n </div>\n ) : (\n <p className=\"text-xs text-muted-foreground\">\n {t('catalog.products.media.empty', 'No media uploaded yet.')}\n </p>\n )}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -41,7 +41,7 @@ function VariantBasicsSection({ values, setValue, errors }) {
|
|
|
41
41
|
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
42
42
|
/* @__PURE__ */ jsxs(Label, { className: "flex items-center gap-1", children: [
|
|
43
43
|
t("catalog.variants.form.nameLabel", "Name"),
|
|
44
|
-
/* @__PURE__ */ jsx("span", { className: "text-
|
|
44
|
+
/* @__PURE__ */ jsx("span", { className: "text-status-error-text", children: "*" })
|
|
45
45
|
] }),
|
|
46
46
|
/* @__PURE__ */ jsx(
|
|
47
47
|
Input,
|
|
@@ -51,7 +51,7 @@ function VariantBasicsSection({ values, setValue, errors }) {
|
|
|
51
51
|
placeholder: t("catalog.variants.form.namePlaceholder", "e.g., Blue / Small")
|
|
52
52
|
}
|
|
53
53
|
),
|
|
54
|
-
errors.name ? /* @__PURE__ */ jsx("p", { className: "text-xs text-
|
|
54
|
+
errors.name ? /* @__PURE__ */ jsx("p", { className: "text-xs text-status-error-text", children: errors.name }) : null
|
|
55
55
|
] }),
|
|
56
56
|
/* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [
|
|
57
57
|
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
@@ -75,7 +75,7 @@ function VariantBasicsSection({ values, setValue, errors }) {
|
|
|
75
75
|
placeholder: t("catalog.variants.form.barcodePlaceholder", "EAN, UPC, etc.")
|
|
76
76
|
}
|
|
77
77
|
),
|
|
78
|
-
errors.barcode ? /* @__PURE__ */ jsx("p", { className: "text-xs text-
|
|
78
|
+
errors.barcode ? /* @__PURE__ */ jsx("p", { className: "text-xs text-status-error-text", children: errors.barcode }) : null
|
|
79
79
|
] })
|
|
80
80
|
] }),
|
|
81
81
|
/* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [
|
|
@@ -99,7 +99,7 @@ function VariantBasicsSection({ values, setValue, errors }) {
|
|
|
99
99
|
"catalog.variants.form.gtinTypeHint",
|
|
100
100
|
"Typed barcodes are validated and kept unique per organization."
|
|
101
101
|
) }),
|
|
102
|
-
errors.gtinType ? /* @__PURE__ */ jsx("p", { className: "text-xs text-
|
|
102
|
+
errors.gtinType ? /* @__PURE__ */ jsx("p", { className: "text-xs text-status-error-text", children: errors.gtinType }) : null
|
|
103
103
|
] }),
|
|
104
104
|
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
105
105
|
/* @__PURE__ */ jsx(Label, { htmlFor: "catalog-variant-hs-code", children: t("catalog.variants.form.hsCodeLabel", "HS code (customs tariff)") }),
|
|
@@ -111,7 +111,7 @@ function VariantBasicsSection({ values, setValue, errors }) {
|
|
|
111
111
|
onChange: (event) => setValue("hsCode", event.target.value)
|
|
112
112
|
}
|
|
113
113
|
),
|
|
114
|
-
errors.hsCode ? /* @__PURE__ */ jsx("p", { className: "text-xs text-
|
|
114
|
+
errors.hsCode ? /* @__PURE__ */ jsx("p", { className: "text-xs text-status-error-text", children: errors.hsCode }) : null
|
|
115
115
|
] })
|
|
116
116
|
] }),
|
|
117
117
|
/* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [
|