@open-mercato/core 0.6.8-develop.6910.1.560e304de2 → 0.6.8-develop.6912.1.36161dc9ac

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.
Files changed (140) hide show
  1. package/dist/modules/api_keys/backend/api-keys/page.js.map +1 -1
  2. package/dist/modules/attachments/components/AttachmentLibrary.js.map +2 -2
  3. package/dist/modules/attachments/components/AttachmentPartitionSettings.js.map +1 -1
  4. package/dist/modules/audit_logs/components/AccessLogsTable.js.map +1 -1
  5. package/dist/modules/audit_logs/components/AuditLogsActions.js.map +1 -1
  6. package/dist/modules/auth/backend/roles/page.js.map +2 -2
  7. package/dist/modules/auth/backend/users/page.js.map +2 -2
  8. package/dist/modules/business_rules/backend/logs/page.js.map +1 -1
  9. package/dist/modules/business_rules/backend/rules/page.js.map +1 -1
  10. package/dist/modules/business_rules/backend/sets/page.js.map +1 -1
  11. package/dist/modules/catalog/components/PriceKindSettings.js.map +1 -1
  12. package/dist/modules/catalog/components/categories/CategoriesDataTable.js.map +1 -1
  13. package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
  14. package/dist/modules/communication_channels/backend/communication_channels/channels/page.js.map +1 -1
  15. package/dist/modules/communication_channels/backend/profile/communication-channels/page.js.map +1 -1
  16. package/dist/modules/currencies/backend/currencies/page.js.map +1 -1
  17. package/dist/modules/currencies/backend/exchange-rates/page.js.map +1 -1
  18. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js.map +1 -1
  19. package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js.map +1 -1
  20. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  21. package/dist/modules/customers/backend/customers/deals/page.js.map +1 -1
  22. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  23. package/dist/modules/customers/components/CustomerTodosTable.js.map +1 -1
  24. package/dist/modules/data_sync/backend/data-sync/page.js.map +1 -1
  25. package/dist/modules/dictionaries/components/DictionaryTable.js.map +2 -2
  26. package/dist/modules/directory/backend/directory/organizations/page.js.map +1 -1
  27. package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
  28. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
  29. package/dist/modules/entities/components/SystemEntitiesTable.js.map +2 -2
  30. package/dist/modules/entities/components/UserEntitiesTable.js.map +2 -2
  31. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js.map +2 -2
  32. package/dist/modules/feature_toggles/components/OverridesTable.js.map +2 -2
  33. package/dist/modules/inbox_ops/backend/inbox-ops/log/page.js.map +1 -1
  34. package/dist/modules/inbox_ops/backend/inbox-ops/page.js.map +1 -1
  35. package/dist/modules/messages/components/MessagesInboxPageClient.js.map +1 -1
  36. package/dist/modules/payment_gateways/backend/payment-gateways/page.js.map +1 -1
  37. package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
  38. package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
  39. package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
  40. package/dist/modules/resources/backend/resources/resources/page.js.map +1 -1
  41. package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
  42. package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
  43. package/dist/modules/sales/components/AdjustmentKindSettings.js.map +1 -1
  44. package/dist/modules/sales/components/PaymentMethodsSettings.js.map +1 -1
  45. package/dist/modules/sales/components/ShippingMethodsSettings.js.map +1 -1
  46. package/dist/modules/sales/components/TaxRatesSettings.js.map +1 -1
  47. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
  48. package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +1 -1
  49. package/dist/modules/sales/components/documents/PaymentsSection.js.map +1 -1
  50. package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
  51. package/dist/modules/sales/components/documents/SalesOrderDraftLines.js.map +1 -1
  52. package/dist/modules/staff/backend/staff/leave-requests/page.js.map +2 -2
  53. package/dist/modules/staff/backend/staff/my-leave-requests/page.js.map +2 -2
  54. package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
  55. package/dist/modules/staff/backend/staff/team-roles/page.js.map +1 -1
  56. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  57. package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
  58. package/dist/modules/staff/backend/staff/timesheets/projects/page.js.map +2 -2
  59. package/dist/modules/wms/components/backend/WmsConfigurationPage.js.map +2 -2
  60. package/dist/modules/wms/components/backend/WmsInventoryConsolePage.js.map +2 -2
  61. package/dist/modules/wms/components/backend/WmsLocationDetailPage.js.map +1 -1
  62. package/dist/modules/wms/components/backend/WmsLotDetailPage.js.map +1 -1
  63. package/dist/modules/wms/components/backend/WmsLotsListPage.js.map +2 -2
  64. package/dist/modules/wms/components/backend/WmsOperationalDashboardPage.js.map +1 -1
  65. package/dist/modules/wms/components/backend/WmsSkuDetailPage.js.map +1 -1
  66. package/dist/modules/workflows/backend/definitions/page.js.map +1 -1
  67. package/dist/modules/workflows/backend/events/page.js.map +1 -1
  68. package/dist/modules/workflows/backend/instances/page.js.map +1 -1
  69. package/dist/modules/workflows/backend/tasks/page.js.map +1 -1
  70. package/jest.config.cjs +1 -1
  71. package/package.json +7 -7
  72. package/src/modules/api_keys/backend/api-keys/page.tsx +1 -1
  73. package/src/modules/attachments/components/AttachmentLibrary.tsx +2 -1
  74. package/src/modules/attachments/components/AttachmentPartitionSettings.tsx +1 -1
  75. package/src/modules/audit_logs/components/AccessLogsTable.tsx +1 -1
  76. package/src/modules/audit_logs/components/AuditLogsActions.tsx +1 -1
  77. package/src/modules/auth/backend/roles/page.tsx +2 -1
  78. package/src/modules/auth/backend/users/page.tsx +2 -1
  79. package/src/modules/business_rules/backend/logs/page.tsx +1 -1
  80. package/src/modules/business_rules/backend/rules/page.tsx +1 -1
  81. package/src/modules/business_rules/backend/sets/page.tsx +1 -1
  82. package/src/modules/catalog/components/PriceKindSettings.tsx +1 -1
  83. package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +1 -1
  84. package/src/modules/catalog/components/products/ProductsDataTable.tsx +2 -1
  85. package/src/modules/communication_channels/backend/communication_channels/channels/page.tsx +1 -1
  86. package/src/modules/communication_channels/backend/profile/communication-channels/page.tsx +1 -1
  87. package/src/modules/currencies/backend/currencies/page.tsx +1 -1
  88. package/src/modules/currencies/backend/exchange-rates/page.tsx +1 -1
  89. package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +1 -1
  90. package/src/modules/customer_accounts/backend/customer_accounts/users/page.tsx +1 -1
  91. package/src/modules/customers/backend/customers/companies/page.tsx +2 -1
  92. package/src/modules/customers/backend/customers/deals/page.tsx +1 -1
  93. package/src/modules/customers/backend/customers/people/page.tsx +2 -1
  94. package/src/modules/customers/components/CustomerTodosTable.tsx +1 -1
  95. package/src/modules/data_sync/backend/data-sync/page.tsx +1 -1
  96. package/src/modules/dictionaries/components/DictionaryTable.tsx +2 -1
  97. package/src/modules/directory/backend/directory/organizations/page.tsx +1 -1
  98. package/src/modules/directory/backend/directory/tenants/page.tsx +2 -1
  99. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +2 -1
  100. package/src/modules/entities/components/SystemEntitiesTable.tsx +2 -1
  101. package/src/modules/entities/components/UserEntitiesTable.tsx +2 -1
  102. package/src/modules/feature_toggles/components/FeatureTogglesTable.tsx +2 -1
  103. package/src/modules/feature_toggles/components/OverridesTable.tsx +2 -1
  104. package/src/modules/inbox_ops/backend/inbox-ops/log/page.tsx +1 -1
  105. package/src/modules/inbox_ops/backend/inbox-ops/page.tsx +1 -1
  106. package/src/modules/messages/components/MessagesInboxPageClient.tsx +1 -1
  107. package/src/modules/payment_gateways/backend/payment-gateways/page.tsx +1 -1
  108. package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +2 -1
  109. package/src/modules/query_index/components/QueryIndexesTable.tsx +2 -1
  110. package/src/modules/resources/backend/resources/resource-types/page.tsx +2 -1
  111. package/src/modules/resources/backend/resources/resources/page.tsx +1 -1
  112. package/src/modules/sales/backend/sales/channels/offers/page.tsx +2 -1
  113. package/src/modules/sales/backend/sales/channels/page.tsx +2 -1
  114. package/src/modules/sales/components/AdjustmentKindSettings.tsx +1 -1
  115. package/src/modules/sales/components/PaymentMethodsSettings.tsx +1 -1
  116. package/src/modules/sales/components/ShippingMethodsSettings.tsx +1 -1
  117. package/src/modules/sales/components/TaxRatesSettings.tsx +1 -1
  118. package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +2 -1
  119. package/src/modules/sales/components/documents/AdjustmentsSection.tsx +1 -1
  120. package/src/modules/sales/components/documents/PaymentsSection.tsx +1 -1
  121. package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +2 -1
  122. package/src/modules/sales/components/documents/SalesOrderDraftLines.tsx +1 -1
  123. package/src/modules/staff/backend/staff/leave-requests/page.tsx +2 -1
  124. package/src/modules/staff/backend/staff/my-leave-requests/page.tsx +2 -1
  125. package/src/modules/staff/backend/staff/team-members/page.tsx +3 -2
  126. package/src/modules/staff/backend/staff/team-roles/page.tsx +1 -1
  127. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +2 -1
  128. package/src/modules/staff/backend/staff/teams/page.tsx +2 -1
  129. package/src/modules/staff/backend/staff/timesheets/projects/page.tsx +2 -1
  130. package/src/modules/wms/components/backend/WmsConfigurationPage.tsx +2 -1
  131. package/src/modules/wms/components/backend/WmsInventoryConsolePage.tsx +4 -3
  132. package/src/modules/wms/components/backend/WmsLocationDetailPage.tsx +1 -1
  133. package/src/modules/wms/components/backend/WmsLotDetailPage.tsx +1 -1
  134. package/src/modules/wms/components/backend/WmsLotsListPage.tsx +2 -1
  135. package/src/modules/wms/components/backend/WmsOperationalDashboardPage.tsx +1 -1
  136. package/src/modules/wms/components/backend/WmsSkuDetailPage.tsx +1 -1
  137. package/src/modules/workflows/backend/definitions/page.tsx +1 -1
  138. package/src/modules/workflows/backend/events/page.tsx +1 -1
  139. package/src/modules/workflows/backend/instances/page.tsx +1 -1
  140. package/src/modules/workflows/backend/tasks/page.tsx +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/sales/components/ShippingMethodsSettings.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { z } from 'zod'\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 {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { SwitchField } from '@open-mercato/ui/primitives/switch-field'\nimport { CrudForm, type CrudCustomFieldRenderProps, type CrudField } from '@open-mercato/ui/backend/CrudForm'\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 {\n listShippingProviders,\n type ShippingProvider,\n} from '../lib/providers'\nimport { isRecord } from '@open-mercato/shared/lib/utils'\nimport { renderProviderFieldInput } from './ProviderFieldInput'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('sales')\n\ntype ShippingMethodRow = {\n id: string\n name: string\n code: string\n providerKey: string | null\n providerLabel: string | null\n carrierCode: string | null\n description: string | null\n serviceLevel: string | null\n estimatedTransitDays: number | null\n baseRateNet: number\n baseRateGross: number\n currencyCode: string | null\n isActive: boolean\n updatedAt: string | null\n providerSettings: Record<string, unknown> | null\n}\n\ntype DialogState =\n | { mode: 'create' }\n | { mode: 'edit'; entry: ShippingMethodRow }\n\nconst shippingFormSchema = z.object({\n name: z.string().trim().min(1),\n code: z.string().trim().min(1),\n providerKey: z.string().trim().optional(),\n description: z.string().optional(),\n carrierCode: z.string().optional(),\n serviceLevel: z.string().optional(),\n estimatedTransitDays: z.coerce.number().min(0).optional(),\n baseRateNet: z.coerce.number().min(0),\n baseRateGross: z.coerce.number().min(0).optional(),\n currencyCode: z.string().trim().optional(),\n isActive: z.boolean().optional(),\n providerSettings: z.record(z.string(), z.unknown()).optional(),\n})\n\ntype ShippingFormValues = z.infer<typeof shippingFormSchema>\n\nconst DEFAULT_FORM: ShippingFormValues = {\n name: '',\n code: '',\n providerKey: '',\n description: '',\n carrierCode: '',\n serviceLevel: '',\n estimatedTransitDays: undefined,\n baseRateNet: 0,\n baseRateGross: 0,\n currencyCode: '',\n isActive: true,\n providerSettings: {},\n}\n\ntype RateRule = {\n id?: string\n name?: string\n metric?: string\n min?: number | null\n max?: number | null\n amountNet?: number | null\n amountGross?: number | null\n currencyCode?: string | null\n}\n\ntype FlatRateLabels = {\n title: string\n add: string\n metric: string\n min: string\n max: string\n amountNet: string\n amountGross: string\n currency: string\n remove: string\n applyBaseRate: string\n}\n\nfunction FlatRateSettingsEditor(props: {\n value: Record<string, unknown>\n onChange: (next: Record<string, unknown>) => void\n translations: FlatRateLabels\n currencyCode?: string | null\n}) {\n const { value, onChange, translations, currencyCode } = props\n const rates = Array.isArray(value.rates) ? (value.rates as RateRule[]) : []\n const applyBaseRate = value.applyBaseRate !== false\n const metrics: Array<{ value: string; label: string }> = [\n { value: 'item_count', label: 'Item count' },\n { value: 'weight', label: 'Total weight' },\n { value: 'volume', label: 'Total volume' },\n { value: 'subtotal', label: 'Subtotal' },\n ]\n\n const updateRates = (nextRates: RateRule[]) => {\n onChange({ ...value, rates: nextRates })\n }\n\n const updateRate = (index: number, key: keyof RateRule, nextValue: unknown) => {\n const next = rates.map((rate, idx) =>\n idx === index ? { ...rate, [key]: nextValue } : rate\n )\n updateRates(next)\n }\n\n const addRate = () => {\n updateRates([\n ...rates,\n {\n id: crypto.randomUUID?.() ?? String(Date.now()),\n name: '',\n metric: 'item_count',\n min: 0,\n max: null,\n amountNet: 0,\n amountGross: 0,\n currencyCode: currencyCode ?? '',\n },\n ])\n }\n\n const removeRate = (index: number) => {\n updateRates(rates.filter((_, idx) => idx !== index))\n }\n\n return (\n <div className=\"space-y-3 rounded-md border border-dashed border-border p-3\">\n <div className=\"flex items-center justify-between\">\n <p className=\"text-sm font-medium\">{translations.title}</p>\n <Button size=\"sm\" variant=\"outline\" type=\"button\" onClick={addRate}>\n {translations.add}\n </Button>\n </div>\n <div className=\"space-y-3\">\n {rates.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">{translations.add}</p>\n ) : (\n rates.map((rate, index) => (\n <div key={rate.id ?? index} className=\"rounded-md border border-border p-3 space-y-2\">\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3\">\n <div className=\"space-y-1\">\n <Label className=\"text-xs uppercase text-muted-foreground\">{translations.metric}</Label>\n <Select\n value={rate.metric ?? 'item_count'}\n onValueChange={(value) => updateRate(index, 'metric', value)}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {metrics.map((opt) => (\n <SelectItem key={opt.value} value={opt.value}>\n {opt.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-1\">\n <Label className=\"text-xs uppercase text-muted-foreground\">{translations.min}</Label>\n <Input\n type=\"number\"\n value={rate.min ?? ''}\n onChange={(evt) => updateRate(index, 'min', evt.target.value === '' ? null : Number(evt.target.value))}\n />\n </div>\n <div className=\"space-y-1\">\n <Label className=\"text-xs uppercase text-muted-foreground\">{translations.max}</Label>\n <Input\n type=\"number\"\n value={rate.max ?? ''}\n onChange={(evt) => updateRate(index, 'max', evt.target.value === '' ? null : Number(evt.target.value))}\n />\n </div>\n <div className=\"space-y-1\">\n <Label className=\"text-xs uppercase text-muted-foreground\">{translations.amountNet}</Label>\n <Input\n type=\"number\"\n value={rate.amountNet ?? ''}\n onChange={(evt) =>\n updateRate(index, 'amountNet', evt.target.value === '' ? null : Number(evt.target.value))\n }\n />\n </div>\n <div className=\"space-y-1\">\n <Label className=\"text-xs uppercase text-muted-foreground\">{translations.amountGross}</Label>\n <Input\n type=\"number\"\n value={rate.amountGross ?? ''}\n onChange={(evt) =>\n updateRate(index, 'amountGross', evt.target.value === '' ? null : Number(evt.target.value))\n }\n />\n </div>\n <div className=\"space-y-1\">\n <Label className=\"text-xs uppercase text-muted-foreground\">{translations.currency}</Label>\n <Input\n type=\"text\"\n value={rate.currencyCode ?? currencyCode ?? ''}\n onChange={(evt) =>\n updateRate(index, 'currencyCode', evt.target.value?.toUpperCase?.() ?? '')\n }\n />\n </div>\n </div>\n <div className=\"flex justify-end\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => removeRate(index)}>\n {translations.remove}\n </Button>\n </div>\n </div>\n ))\n )}\n </div>\n <SwitchField\n id=\"apply-base-rate\"\n label={translations.applyBaseRate}\n flip\n checked={applyBaseRate}\n onCheckedChange={(checked) => onChange({ ...value, applyBaseRate: checked })}\n />\n </div>\n )\n}\n\nfunction createShippingProviderSettingsRenderer(params: {\n providers: ShippingProvider[]\n selectPrompt: string\n noFieldsLabel: string\n flatRateLabels: FlatRateLabels\n}) {\n const { providers, selectPrompt, noFieldsLabel, flatRateLabels } = params\n return function ShippingProviderSettingsField({\n value,\n setValue,\n values,\n }: CrudCustomFieldRenderProps) {\n const providerKey =\n values && typeof values.providerKey === 'string' && values.providerKey.trim().length\n ? values.providerKey\n : null\n const provider = providers.find((entry) => entry.key === providerKey)\n const previousKey = React.useRef<string | null>(providerKey)\n\n React.useEffect(() => {\n if (providerKey !== previousKey.current) {\n const defaults = provider?.settings?.defaults ?? null\n setValue(defaults && typeof defaults === 'object' ? defaults : {})\n previousKey.current = providerKey\n }\n }, [providerKey, provider, setValue])\n\n if (!provider) {\n return <p className=\"text-sm text-muted-foreground\">{selectPrompt}</p>\n }\n\n const settings = isRecord(value) ? value : {}\n const fields = provider.settings?.fields ?? []\n if (provider.key === 'flat-rate') {\n return (\n <FlatRateSettingsEditor\n value={settings}\n onChange={(next) => setValue(next)}\n translations={flatRateLabels}\n currencyCode={typeof values?.currencyCode === 'string' ? values.currencyCode : ''}\n />\n )\n }\n if (!fields.length) {\n return <p className=\"text-sm text-muted-foreground\">{noFieldsLabel}</p>\n }\n return (\n <div className=\"space-y-4\">\n {fields.map((field) => {\n const fieldValue = settings[field.key]\n return (\n <div key={field.key} className=\"space-y-1.5\">\n <Label htmlFor={field.key} className=\"text-sm font-medium\">\n {field.label}\n {field.required ? ' *' : ''}\n </Label>\n {field.description ? (\n <p className=\"text-xs text-muted-foreground\">{field.description}</p>\n ) : null}\n {renderProviderFieldInput({\n field,\n value: fieldValue,\n onChange: (next) => setValue({ ...settings, [field.key]: next }),\n })}\n </div>\n )\n })}\n </div>\n )\n }\n}\n\nexport function ShippingMethodsSettings() {\n const t = useT()\n const scopeVersion = useOrganizationScopeVersion()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const providers = React.useMemo(() => listShippingProviders(), [])\n const providerOptions = React.useMemo(\n () => providers.map((provider) => ({ value: provider.key, label: provider.label })),\n [providers]\n )\n const flatRateLabels = React.useMemo(() => ({\n title: t('sales.config.shippingMethods.form.ratesTitle', 'Rates'),\n add: t('sales.config.shippingMethods.form.addRate', 'Add rate'),\n metric: t('sales.config.shippingMethods.form.metric', 'Metric'),\n min: t('sales.config.shippingMethods.form.min', 'Min'),\n max: t('sales.config.shippingMethods.form.max', 'Max'),\n amountNet: t('sales.config.shippingMethods.form.amountNet', 'Amount (net)'),\n amountGross: t('sales.config.shippingMethods.form.amountGross', 'Amount (gross)'),\n currency: t('sales.config.shippingMethods.form.currency', 'Currency'),\n remove: t('sales.config.shippingMethods.form.removeRate', 'Remove'),\n applyBaseRate: t(\n 'sales.config.shippingMethods.form.applyBaseRate',\n 'Always include the base rate'\n ),\n }), [t])\n const ProviderSettingsField = React.useMemo(\n () =>\n createShippingProviderSettingsRenderer({\n providers,\n selectPrompt: t(\n 'sales.config.shippingMethods.form.selectProvider',\n 'Choose a provider to configure settings.'\n ),\n noFieldsLabel: t('sales.config.shippingMethods.form.noSettings', 'No configurable settings.'),\n flatRateLabels,\n }),\n [flatRateLabels, providers, t]\n )\n\n const translations = React.useMemo(() => ({\n title: t('sales.config.shippingMethods.title', 'Shipping methods'),\n description: t(\n 'sales.config.shippingMethods.description',\n 'Manage carriers, providers, and default shipping prices.'\n ),\n actions: {\n add: t('sales.config.shippingMethods.actions.add', 'Add shipping method'),\n edit: t('sales.config.shippingMethods.actions.edit', 'Edit'),\n delete: t('sales.config.shippingMethods.actions.delete', 'Delete'),\n deleteConfirm: t('sales.config.shippingMethods.actions.deleteConfirm', 'Delete \"{{name}}\"?'),\n refresh: t('sales.config.shippingMethods.actions.refresh', 'Refresh'),\n },\n table: {\n name: t('sales.config.shippingMethods.table.name', 'Name'),\n code: t('sales.config.shippingMethods.table.code', 'Code'),\n provider: t('sales.config.shippingMethods.table.provider', 'Provider'),\n rate: t('sales.config.shippingMethods.table.rate', 'Base rate'),\n currency: t('sales.config.shippingMethods.table.currency', 'Currency'),\n active: t('sales.config.shippingMethods.table.active', 'Active'),\n statusActive: t('sales.config.shippingMethods.table.statusActive', 'Active'),\n statusInactive: t('sales.config.shippingMethods.table.statusInactive', 'Inactive'),\n updatedAt: t('sales.config.shippingMethods.table.updatedAt', 'Updated'),\n empty: t('sales.config.shippingMethods.table.empty', 'No shipping methods yet.'),\n search: t('sales.config.shippingMethods.table.search', 'Search shipping methods\u2026'),\n },\n form: {\n createTitle: t('sales.config.shippingMethods.form.createTitle', 'Add shipping method'),\n editTitle: t('sales.config.shippingMethods.form.editTitle', 'Edit shipping method'),\n name: t('sales.config.shippingMethods.form.name', 'Name'),\n code: t('sales.config.shippingMethods.form.code', 'Code'),\n provider: t('sales.config.shippingMethods.form.provider', 'Provider'),\n description: t('sales.config.shippingMethods.form.description', 'Description'),\n carrierCode: t('sales.config.shippingMethods.form.carrierCode', 'Carrier'),\n serviceLevel: t('sales.config.shippingMethods.form.serviceLevel', 'Service level'),\n estimatedTransitDays: t(\n 'sales.config.shippingMethods.form.estimatedTransitDays',\n 'Transit days'\n ),\n baseRateNet: t('sales.config.shippingMethods.form.baseRateNet', 'Base rate (net)'),\n baseRateGross: t('sales.config.shippingMethods.form.baseRateGross', 'Base rate (gross)'),\n currencyCode: t('sales.config.shippingMethods.form.currencyCode', 'Currency'),\n isActive: t('sales.config.shippingMethods.form.isActive', 'Active'),\n providerSettings: t('sales.config.shippingMethods.form.providerSettings', 'Provider settings'),\n save: t('sales.config.shippingMethods.form.save', 'Save'),\n cancel: t('sales.config.shippingMethods.form.cancel', 'Cancel'),\n },\n messages: {\n saved: t('sales.config.shippingMethods.messages.saved', 'Shipping method saved.'),\n deleted: t('sales.config.shippingMethods.messages.deleted', 'Shipping method deleted.'),\n },\n errors: {\n load: t('sales.config.shippingMethods.errors.load', 'Failed to load shipping methods.'),\n save: t('sales.config.shippingMethods.errors.save', 'Failed to save shipping method.'),\n delete: t('sales.config.shippingMethods.errors.delete', 'Failed to delete shipping method.'),\n },\n }), [t])\n\n const [entries, setEntries] = React.useState<ShippingMethodRow[]>([])\n const [loading, setLoading] = React.useState(false)\n const [dialog, setDialog] = React.useState<DialogState | null>(null)\n const [formValues, setFormValues] = React.useState<ShippingFormValues>(DEFAULT_FORM)\n const [search, setSearch] = React.useState('')\n const [submitting, setSubmitting] = React.useState(false)\n\n const loadEntries = React.useCallback(async () => {\n setLoading(true)\n try {\n const payload = await readApiResultOrThrow<{ items?: Array<Record<string, unknown>> }>(\n '/api/sales/shipping-methods?pageSize=100',\n undefined,\n { errorMessage: translations.errors.load, fallback: { items: [] } },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n setEntries(\n items.map((item) => {\n const providerKey = typeof (item as any).providerKey === 'string'\n ? (item as any).providerKey\n : typeof (item as any)?.provider_key === 'string'\n ? (item as any).provider_key\n : null\n const provider = providers.find((entry) => entry.key === providerKey)\n const baseRateGross = typeof item.baseRateGross === 'number'\n ? item.baseRateGross\n : Number((item as any)?.base_rate_gross ?? item.baseRateGross ?? 0) || 0\n const baseRateNet = typeof item.baseRateNet === 'number'\n ? item.baseRateNet\n : Number((item as any)?.base_rate_net ?? item.baseRateNet ?? 0) || 0\n const currency =\n typeof item.currencyCode === 'string'\n ? item.currencyCode\n : typeof (item as any)?.currency_code === 'string'\n ? (item as any).currency_code\n : null\n return {\n id: String(item.id ?? ''),\n name: typeof item.name === 'string' && item.name.length ? item.name : '\u2014',\n code: typeof item.code === 'string' ? item.code : '',\n providerKey,\n providerLabel: provider?.label ?? null,\n carrierCode: typeof item.carrierCode === 'string' ? item.carrierCode : null,\n description: typeof item.description === 'string' ? item.description : null,\n serviceLevel: typeof item.serviceLevel === 'string' ? item.serviceLevel : null,\n estimatedTransitDays:\n typeof (item as any)?.estimatedTransitDays === 'number'\n ? (item as any).estimatedTransitDays\n : typeof (item as any)?.estimated_transit_days === 'number'\n ? (item as any).estimated_transit_days\n : null,\n baseRateNet,\n baseRateGross,\n currencyCode: currency,\n isActive:\n typeof item.isActive === 'boolean'\n ? item.isActive\n : typeof (item as any)?.is_active === 'boolean'\n ? (item as any).is_active\n : false,\n updatedAt: typeof item.updatedAt === 'string' ? item.updatedAt : null,\n providerSettings:\n item && typeof (item as any).providerSettings === 'object'\n ? ((item as any).providerSettings as Record<string, unknown>)\n : null,\n }\n })\n )\n } catch (err) {\n logger.error('sales.shipping-methods.list failed', { err })\n flash(translations.errors.load, 'error')\n } finally {\n setLoading(false)\n }\n }, [providers, translations.errors.load])\n\n React.useEffect(() => {\n void loadEntries()\n }, [loadEntries, scopeVersion])\n\n const openCreate = React.useCallback(() => {\n const defaultProvider = providerOptions[0]?.value ?? ''\n setFormValues({\n ...DEFAULT_FORM,\n providerKey: defaultProvider,\n providerSettings: {},\n })\n setDialog({ mode: 'create' })\n }, [providerOptions])\n\n const openEdit = React.useCallback((entry: ShippingMethodRow) => {\n setFormValues({\n name: entry.name,\n code: entry.code,\n providerKey: entry.providerKey ?? '',\n description: entry.description ?? '',\n carrierCode: entry.carrierCode ?? '',\n serviceLevel: entry.serviceLevel ?? '',\n estimatedTransitDays: entry.estimatedTransitDays ?? undefined,\n baseRateNet: entry.baseRateNet,\n baseRateGross: entry.baseRateGross,\n currencyCode: entry.currencyCode ?? '',\n isActive: entry.isActive,\n providerSettings: entry.providerSettings ?? {},\n })\n setDialog({ mode: 'edit', entry })\n }, [])\n\n const closeDialog = React.useCallback(() => {\n setDialog(null)\n }, [])\n\n const deleteEntry = React.useCallback(async (entry: ShippingMethodRow) => {\n const message = translations.actions.deleteConfirm.replace('{{name}}', entry.name)\n const confirmed = await confirm({\n title: message,\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n const headers = buildOptimisticLockHeader(entry.updatedAt)\n const call = await withScopedApiRequestHeaders(headers, () => apiCall('/api/sales/shipping-methods', {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: entry.id }),\n }))\n if (!call.ok) {\n if (surfaceRecordConflict({ status: call.status, body: call.result }, t)) return\n await raiseCrudError(call.response, translations.errors.delete)\n }\n flash(translations.messages.deleted, 'success')\n await loadEntries()\n } catch (err) {\n logger.error('sales.shipping-methods.delete failed', { err })\n const message = err instanceof Error ? err.message : translations.errors.delete\n flash(message, 'error')\n }\n }, [confirm, loadEntries, translations.actions.deleteConfirm, translations.errors.delete, translations.messages.deleted])\n\n const columns = React.useMemo<ColumnDef<ShippingMethodRow>[]>(() => [\n {\n accessorKey: 'name',\n header: translations.table.name,\n cell: ({ row }) => (\n <div className=\"flex flex-col\">\n <span className=\"font-medium\">{row.original.name}</span>\n <span className=\"text-xs text-muted-foreground\">{row.original.code}</span>\n </div>\n ),\n },\n {\n accessorKey: 'providerKey',\n header: translations.table.provider,\n cell: ({ row }) => (\n <span className=\"text-sm\">\n {row.original.providerLabel ?? row.original.providerKey ?? '\u2014'}\n </span>\n ),\n },\n {\n accessorKey: 'rate',\n header: translations.table.rate,\n cell: ({ row }) => (\n <span className=\"text-sm\">\n {row.original.baseRateGross.toFixed(2)} {row.original.currencyCode ?? ''}\n </span>\n ),\n },\n {\n accessorKey: 'isActive',\n header: translations.table.active,\n cell: ({ row }) => (\n <span className=\"text-sm\">\n {row.original.isActive ? translations.table.statusActive : translations.table.statusInactive}\n </span>\n ),\n },\n {\n accessorKey: 'updatedAt',\n header: translations.table.updatedAt,\n cell: ({ row }) => (\n <span className=\"text-xs text-muted-foreground\">\n {row.original.updatedAt ? new Date(row.original.updatedAt).toLocaleString() : '\u2014'}\n </span>\n ),\n },\n ], [translations])\n\n const handleSubmit = React.useCallback(async (values: ShippingFormValues) => {\n if (!dialog) return\n setSubmitting(true)\n const payload: Record<string, unknown> = {\n name: values.name.trim(),\n code: values.code.trim().toLowerCase(),\n description: values.description?.trim() || undefined,\n carrierCode: values.carrierCode?.trim() || undefined,\n providerKey: values.providerKey?.trim() || undefined,\n serviceLevel: values.serviceLevel?.trim() || undefined,\n estimatedTransitDays:\n values.estimatedTransitDays === undefined || values.estimatedTransitDays === null\n ? undefined\n : Number(values.estimatedTransitDays),\n baseRateNet: Number(values.baseRateNet ?? 0),\n baseRateGross:\n values.baseRateGross === undefined || values.baseRateGross === null\n ? Number(values.baseRateNet ?? 0)\n : Number(values.baseRateGross),\n currencyCode: values.currencyCode?.trim().toUpperCase() || undefined,\n isActive: values.isActive ?? true,\n providerSettings: isRecord(values.providerSettings) ? values.providerSettings : undefined,\n }\n const path = '/api/sales/shipping-methods'\n const method = dialog.mode === 'create' ? 'POST' : 'PUT'\n if (dialog.mode === 'edit') payload.id = dialog.entry.id\n try {\n const saveShippingMethod = () => apiCall(path, {\n method,\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n const headers = buildOptimisticLockHeader(dialog.mode === 'edit' ? dialog.entry.updatedAt : null)\n const call = await withScopedApiRequestHeaders(headers, saveShippingMethod)\n if (!call.ok) {\n if (surfaceRecordConflict({ status: call.status, body: call.result }, t)) return\n await raiseCrudError(call.response, translations.errors.save)\n }\n flash(translations.messages.saved, 'success')\n await loadEntries()\n closeDialog()\n } catch (err) {\n logger.error('sales.shipping-methods.save failed', { err })\n const message = err instanceof Error ? err.message : translations.errors.save\n flash(message, 'error')\n } finally {\n setSubmitting(false)\n }\n }, [closeDialog, dialog, loadEntries, translations.errors.save, translations.messages.saved])\n\n const fields = React.useMemo<CrudField[]>(() => [\n { id: 'name', label: translations.form.name, type: 'text', required: true },\n { id: 'code', label: translations.form.code, type: 'text', required: true },\n {\n id: 'providerKey',\n label: translations.form.provider,\n type: 'select',\n required: false,\n options: providerOptions,\n },\n { id: 'carrierCode', label: translations.form.carrierCode, type: 'text' },\n { id: 'serviceLevel', label: translations.form.serviceLevel, type: 'text' },\n {\n id: 'estimatedTransitDays',\n label: translations.form.estimatedTransitDays,\n type: 'number',\n layout: 'half',\n },\n {\n id: 'baseRateNet',\n label: translations.form.baseRateNet,\n type: 'number',\n required: true,\n layout: 'half',\n },\n {\n id: 'baseRateGross',\n label: translations.form.baseRateGross,\n type: 'number',\n required: false,\n layout: 'half',\n },\n {\n id: 'currencyCode',\n label: translations.form.currencyCode,\n type: 'text',\n layout: 'half',\n },\n { id: 'description', label: translations.form.description, type: 'textarea' },\n { id: 'isActive', label: translations.form.isActive, type: 'checkbox' },\n {\n id: 'providerSettings',\n label: translations.form.providerSettings,\n type: 'custom',\n component: ProviderSettingsField,\n },\n ], [ProviderSettingsField, providerOptions, translations.form.baseRateGross, translations.form.baseRateNet, translations.form.carrierCode, translations.form.code, translations.form.currencyCode, translations.form.description, translations.form.estimatedTransitDays, translations.form.isActive, translations.form.name, translations.form.provider, translations.form.providerSettings, translations.form.serviceLevel])\n\n const filteredEntries = React.useMemo(() => {\n if (!search.trim()) return entries\n const term = search.trim().toLowerCase()\n return entries.filter(\n (entry) =>\n entry.name.toLowerCase().includes(term) ||\n entry.code.toLowerCase().includes(term) ||\n (entry.providerLabel ?? '').toLowerCase().includes(term) ||\n (entry.providerKey ?? '').toLowerCase().includes(term)\n )\n }, [entries, search])\n\n return (\n <section className=\"rounded 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-medium\">{translations.title}</h2>\n <p className=\"text-sm text-muted-foreground\">{translations.description}</p>\n </div>\n <div className=\"px-2 py-4 sm:px-4\">\n <DataTable\n embedded\n isLoading={loading}\n columns={columns}\n data={filteredEntries}\n searchValue={search}\n onSearchChange={setSearch}\n searchPlaceholder={translations.table.search}\n emptyState={<p className=\"py-8 text-center text-sm text-muted-foreground\">{translations.table.empty}</p>}\n actions={(\n <Button size=\"sm\" onClick={openCreate}>\n {translations.actions.add}\n </Button>\n )}\n refreshButton={{\n label: translations.actions.refresh,\n onRefresh: () => { void loadEntries() },\n isRefreshing: loading,\n }}\n rowActions={(row) => (\n <RowActions\n items={[\n { id: 'edit', label: translations.actions.edit, onSelect: () => openEdit(row) },\n { id: 'delete', label: translations.actions.delete, destructive: true, onSelect: () => deleteEntry(row) },\n ]}\n />\n )}\n />\n </div>\n\n <Dialog open={dialog !== null} onOpenChange={(next) => !next && closeDialog()}>\n <DialogContent className=\"max-w-4xl\">\n <DialogHeader>\n <DialogTitle>\n {dialog?.mode === 'edit' ? translations.form.editTitle : translations.form.createTitle}\n </DialogTitle>\n </DialogHeader>\n <CrudForm\n schema={shippingFormSchema}\n fields={fields}\n initialValues={formValues}\n optimisticLockUpdatedAt={dialog?.mode === 'edit' ? dialog.entry.updatedAt : null}\n submitLabel={translations.form.save}\n cancelHref={undefined}\n embedded\n onSubmit={handleSubmit}\n isLoading={submitting}\n twoColumn\n extraActions={(\n <Button type=\"button\" variant=\"ghost\" onClick={closeDialog}>\n {translations.form.cancel}\n </Button>\n )}\n />\n </DialogContent>\n </Dialog>\n {ConfirmDialogElement}\n </section>\n )\n}\n"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { z } from 'zod'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\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 {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { SwitchField } from '@open-mercato/ui/primitives/switch-field'\nimport { CrudForm, type CrudCustomFieldRenderProps, type CrudField } from '@open-mercato/ui/backend/CrudForm'\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 {\n listShippingProviders,\n type ShippingProvider,\n} from '../lib/providers'\nimport { isRecord } from '@open-mercato/shared/lib/utils'\nimport { renderProviderFieldInput } from './ProviderFieldInput'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('sales')\n\ntype ShippingMethodRow = {\n id: string\n name: string\n code: string\n providerKey: string | null\n providerLabel: string | null\n carrierCode: string | null\n description: string | null\n serviceLevel: string | null\n estimatedTransitDays: number | null\n baseRateNet: number\n baseRateGross: number\n currencyCode: string | null\n isActive: boolean\n updatedAt: string | null\n providerSettings: Record<string, unknown> | null\n}\n\ntype DialogState =\n | { mode: 'create' }\n | { mode: 'edit'; entry: ShippingMethodRow }\n\nconst shippingFormSchema = z.object({\n name: z.string().trim().min(1),\n code: z.string().trim().min(1),\n providerKey: z.string().trim().optional(),\n description: z.string().optional(),\n carrierCode: z.string().optional(),\n serviceLevel: z.string().optional(),\n estimatedTransitDays: z.coerce.number().min(0).optional(),\n baseRateNet: z.coerce.number().min(0),\n baseRateGross: z.coerce.number().min(0).optional(),\n currencyCode: z.string().trim().optional(),\n isActive: z.boolean().optional(),\n providerSettings: z.record(z.string(), z.unknown()).optional(),\n})\n\ntype ShippingFormValues = z.infer<typeof shippingFormSchema>\n\nconst DEFAULT_FORM: ShippingFormValues = {\n name: '',\n code: '',\n providerKey: '',\n description: '',\n carrierCode: '',\n serviceLevel: '',\n estimatedTransitDays: undefined,\n baseRateNet: 0,\n baseRateGross: 0,\n currencyCode: '',\n isActive: true,\n providerSettings: {},\n}\n\ntype RateRule = {\n id?: string\n name?: string\n metric?: string\n min?: number | null\n max?: number | null\n amountNet?: number | null\n amountGross?: number | null\n currencyCode?: string | null\n}\n\ntype FlatRateLabels = {\n title: string\n add: string\n metric: string\n min: string\n max: string\n amountNet: string\n amountGross: string\n currency: string\n remove: string\n applyBaseRate: string\n}\n\nfunction FlatRateSettingsEditor(props: {\n value: Record<string, unknown>\n onChange: (next: Record<string, unknown>) => void\n translations: FlatRateLabels\n currencyCode?: string | null\n}) {\n const { value, onChange, translations, currencyCode } = props\n const rates = Array.isArray(value.rates) ? (value.rates as RateRule[]) : []\n const applyBaseRate = value.applyBaseRate !== false\n const metrics: Array<{ value: string; label: string }> = [\n { value: 'item_count', label: 'Item count' },\n { value: 'weight', label: 'Total weight' },\n { value: 'volume', label: 'Total volume' },\n { value: 'subtotal', label: 'Subtotal' },\n ]\n\n const updateRates = (nextRates: RateRule[]) => {\n onChange({ ...value, rates: nextRates })\n }\n\n const updateRate = (index: number, key: keyof RateRule, nextValue: unknown) => {\n const next = rates.map((rate, idx) =>\n idx === index ? { ...rate, [key]: nextValue } : rate\n )\n updateRates(next)\n }\n\n const addRate = () => {\n updateRates([\n ...rates,\n {\n id: crypto.randomUUID?.() ?? String(Date.now()),\n name: '',\n metric: 'item_count',\n min: 0,\n max: null,\n amountNet: 0,\n amountGross: 0,\n currencyCode: currencyCode ?? '',\n },\n ])\n }\n\n const removeRate = (index: number) => {\n updateRates(rates.filter((_, idx) => idx !== index))\n }\n\n return (\n <div className=\"space-y-3 rounded-md border border-dashed border-border p-3\">\n <div className=\"flex items-center justify-between\">\n <p className=\"text-sm font-medium\">{translations.title}</p>\n <Button size=\"sm\" variant=\"outline\" type=\"button\" onClick={addRate}>\n {translations.add}\n </Button>\n </div>\n <div className=\"space-y-3\">\n {rates.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">{translations.add}</p>\n ) : (\n rates.map((rate, index) => (\n <div key={rate.id ?? index} className=\"rounded-md border border-border p-3 space-y-2\">\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3\">\n <div className=\"space-y-1\">\n <Label className=\"text-xs uppercase text-muted-foreground\">{translations.metric}</Label>\n <Select\n value={rate.metric ?? 'item_count'}\n onValueChange={(value) => updateRate(index, 'metric', value)}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {metrics.map((opt) => (\n <SelectItem key={opt.value} value={opt.value}>\n {opt.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-1\">\n <Label className=\"text-xs uppercase text-muted-foreground\">{translations.min}</Label>\n <Input\n type=\"number\"\n value={rate.min ?? ''}\n onChange={(evt) => updateRate(index, 'min', evt.target.value === '' ? null : Number(evt.target.value))}\n />\n </div>\n <div className=\"space-y-1\">\n <Label className=\"text-xs uppercase text-muted-foreground\">{translations.max}</Label>\n <Input\n type=\"number\"\n value={rate.max ?? ''}\n onChange={(evt) => updateRate(index, 'max', evt.target.value === '' ? null : Number(evt.target.value))}\n />\n </div>\n <div className=\"space-y-1\">\n <Label className=\"text-xs uppercase text-muted-foreground\">{translations.amountNet}</Label>\n <Input\n type=\"number\"\n value={rate.amountNet ?? ''}\n onChange={(evt) =>\n updateRate(index, 'amountNet', evt.target.value === '' ? null : Number(evt.target.value))\n }\n />\n </div>\n <div className=\"space-y-1\">\n <Label className=\"text-xs uppercase text-muted-foreground\">{translations.amountGross}</Label>\n <Input\n type=\"number\"\n value={rate.amountGross ?? ''}\n onChange={(evt) =>\n updateRate(index, 'amountGross', evt.target.value === '' ? null : Number(evt.target.value))\n }\n />\n </div>\n <div className=\"space-y-1\">\n <Label className=\"text-xs uppercase text-muted-foreground\">{translations.currency}</Label>\n <Input\n type=\"text\"\n value={rate.currencyCode ?? currencyCode ?? ''}\n onChange={(evt) =>\n updateRate(index, 'currencyCode', evt.target.value?.toUpperCase?.() ?? '')\n }\n />\n </div>\n </div>\n <div className=\"flex justify-end\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => removeRate(index)}>\n {translations.remove}\n </Button>\n </div>\n </div>\n ))\n )}\n </div>\n <SwitchField\n id=\"apply-base-rate\"\n label={translations.applyBaseRate}\n flip\n checked={applyBaseRate}\n onCheckedChange={(checked) => onChange({ ...value, applyBaseRate: checked })}\n />\n </div>\n )\n}\n\nfunction createShippingProviderSettingsRenderer(params: {\n providers: ShippingProvider[]\n selectPrompt: string\n noFieldsLabel: string\n flatRateLabels: FlatRateLabels\n}) {\n const { providers, selectPrompt, noFieldsLabel, flatRateLabels } = params\n return function ShippingProviderSettingsField({\n value,\n setValue,\n values,\n }: CrudCustomFieldRenderProps) {\n const providerKey =\n values && typeof values.providerKey === 'string' && values.providerKey.trim().length\n ? values.providerKey\n : null\n const provider = providers.find((entry) => entry.key === providerKey)\n const previousKey = React.useRef<string | null>(providerKey)\n\n React.useEffect(() => {\n if (providerKey !== previousKey.current) {\n const defaults = provider?.settings?.defaults ?? null\n setValue(defaults && typeof defaults === 'object' ? defaults : {})\n previousKey.current = providerKey\n }\n }, [providerKey, provider, setValue])\n\n if (!provider) {\n return <p className=\"text-sm text-muted-foreground\">{selectPrompt}</p>\n }\n\n const settings = isRecord(value) ? value : {}\n const fields = provider.settings?.fields ?? []\n if (provider.key === 'flat-rate') {\n return (\n <FlatRateSettingsEditor\n value={settings}\n onChange={(next) => setValue(next)}\n translations={flatRateLabels}\n currencyCode={typeof values?.currencyCode === 'string' ? values.currencyCode : ''}\n />\n )\n }\n if (!fields.length) {\n return <p className=\"text-sm text-muted-foreground\">{noFieldsLabel}</p>\n }\n return (\n <div className=\"space-y-4\">\n {fields.map((field) => {\n const fieldValue = settings[field.key]\n return (\n <div key={field.key} className=\"space-y-1.5\">\n <Label htmlFor={field.key} className=\"text-sm font-medium\">\n {field.label}\n {field.required ? ' *' : ''}\n </Label>\n {field.description ? (\n <p className=\"text-xs text-muted-foreground\">{field.description}</p>\n ) : null}\n {renderProviderFieldInput({\n field,\n value: fieldValue,\n onChange: (next) => setValue({ ...settings, [field.key]: next }),\n })}\n </div>\n )\n })}\n </div>\n )\n }\n}\n\nexport function ShippingMethodsSettings() {\n const t = useT()\n const scopeVersion = useOrganizationScopeVersion()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const providers = React.useMemo(() => listShippingProviders(), [])\n const providerOptions = React.useMemo(\n () => providers.map((provider) => ({ value: provider.key, label: provider.label })),\n [providers]\n )\n const flatRateLabels = React.useMemo(() => ({\n title: t('sales.config.shippingMethods.form.ratesTitle', 'Rates'),\n add: t('sales.config.shippingMethods.form.addRate', 'Add rate'),\n metric: t('sales.config.shippingMethods.form.metric', 'Metric'),\n min: t('sales.config.shippingMethods.form.min', 'Min'),\n max: t('sales.config.shippingMethods.form.max', 'Max'),\n amountNet: t('sales.config.shippingMethods.form.amountNet', 'Amount (net)'),\n amountGross: t('sales.config.shippingMethods.form.amountGross', 'Amount (gross)'),\n currency: t('sales.config.shippingMethods.form.currency', 'Currency'),\n remove: t('sales.config.shippingMethods.form.removeRate', 'Remove'),\n applyBaseRate: t(\n 'sales.config.shippingMethods.form.applyBaseRate',\n 'Always include the base rate'\n ),\n }), [t])\n const ProviderSettingsField = React.useMemo(\n () =>\n createShippingProviderSettingsRenderer({\n providers,\n selectPrompt: t(\n 'sales.config.shippingMethods.form.selectProvider',\n 'Choose a provider to configure settings.'\n ),\n noFieldsLabel: t('sales.config.shippingMethods.form.noSettings', 'No configurable settings.'),\n flatRateLabels,\n }),\n [flatRateLabels, providers, t]\n )\n\n const translations = React.useMemo(() => ({\n title: t('sales.config.shippingMethods.title', 'Shipping methods'),\n description: t(\n 'sales.config.shippingMethods.description',\n 'Manage carriers, providers, and default shipping prices.'\n ),\n actions: {\n add: t('sales.config.shippingMethods.actions.add', 'Add shipping method'),\n edit: t('sales.config.shippingMethods.actions.edit', 'Edit'),\n delete: t('sales.config.shippingMethods.actions.delete', 'Delete'),\n deleteConfirm: t('sales.config.shippingMethods.actions.deleteConfirm', 'Delete \"{{name}}\"?'),\n refresh: t('sales.config.shippingMethods.actions.refresh', 'Refresh'),\n },\n table: {\n name: t('sales.config.shippingMethods.table.name', 'Name'),\n code: t('sales.config.shippingMethods.table.code', 'Code'),\n provider: t('sales.config.shippingMethods.table.provider', 'Provider'),\n rate: t('sales.config.shippingMethods.table.rate', 'Base rate'),\n currency: t('sales.config.shippingMethods.table.currency', 'Currency'),\n active: t('sales.config.shippingMethods.table.active', 'Active'),\n statusActive: t('sales.config.shippingMethods.table.statusActive', 'Active'),\n statusInactive: t('sales.config.shippingMethods.table.statusInactive', 'Inactive'),\n updatedAt: t('sales.config.shippingMethods.table.updatedAt', 'Updated'),\n empty: t('sales.config.shippingMethods.table.empty', 'No shipping methods yet.'),\n search: t('sales.config.shippingMethods.table.search', 'Search shipping methods\u2026'),\n },\n form: {\n createTitle: t('sales.config.shippingMethods.form.createTitle', 'Add shipping method'),\n editTitle: t('sales.config.shippingMethods.form.editTitle', 'Edit shipping method'),\n name: t('sales.config.shippingMethods.form.name', 'Name'),\n code: t('sales.config.shippingMethods.form.code', 'Code'),\n provider: t('sales.config.shippingMethods.form.provider', 'Provider'),\n description: t('sales.config.shippingMethods.form.description', 'Description'),\n carrierCode: t('sales.config.shippingMethods.form.carrierCode', 'Carrier'),\n serviceLevel: t('sales.config.shippingMethods.form.serviceLevel', 'Service level'),\n estimatedTransitDays: t(\n 'sales.config.shippingMethods.form.estimatedTransitDays',\n 'Transit days'\n ),\n baseRateNet: t('sales.config.shippingMethods.form.baseRateNet', 'Base rate (net)'),\n baseRateGross: t('sales.config.shippingMethods.form.baseRateGross', 'Base rate (gross)'),\n currencyCode: t('sales.config.shippingMethods.form.currencyCode', 'Currency'),\n isActive: t('sales.config.shippingMethods.form.isActive', 'Active'),\n providerSettings: t('sales.config.shippingMethods.form.providerSettings', 'Provider settings'),\n save: t('sales.config.shippingMethods.form.save', 'Save'),\n cancel: t('sales.config.shippingMethods.form.cancel', 'Cancel'),\n },\n messages: {\n saved: t('sales.config.shippingMethods.messages.saved', 'Shipping method saved.'),\n deleted: t('sales.config.shippingMethods.messages.deleted', 'Shipping method deleted.'),\n },\n errors: {\n load: t('sales.config.shippingMethods.errors.load', 'Failed to load shipping methods.'),\n save: t('sales.config.shippingMethods.errors.save', 'Failed to save shipping method.'),\n delete: t('sales.config.shippingMethods.errors.delete', 'Failed to delete shipping method.'),\n },\n }), [t])\n\n const [entries, setEntries] = React.useState<ShippingMethodRow[]>([])\n const [loading, setLoading] = React.useState(false)\n const [dialog, setDialog] = React.useState<DialogState | null>(null)\n const [formValues, setFormValues] = React.useState<ShippingFormValues>(DEFAULT_FORM)\n const [search, setSearch] = React.useState('')\n const [submitting, setSubmitting] = React.useState(false)\n\n const loadEntries = React.useCallback(async () => {\n setLoading(true)\n try {\n const payload = await readApiResultOrThrow<{ items?: Array<Record<string, unknown>> }>(\n '/api/sales/shipping-methods?pageSize=100',\n undefined,\n { errorMessage: translations.errors.load, fallback: { items: [] } },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n setEntries(\n items.map((item) => {\n const providerKey = typeof (item as any).providerKey === 'string'\n ? (item as any).providerKey\n : typeof (item as any)?.provider_key === 'string'\n ? (item as any).provider_key\n : null\n const provider = providers.find((entry) => entry.key === providerKey)\n const baseRateGross = typeof item.baseRateGross === 'number'\n ? item.baseRateGross\n : Number((item as any)?.base_rate_gross ?? item.baseRateGross ?? 0) || 0\n const baseRateNet = typeof item.baseRateNet === 'number'\n ? item.baseRateNet\n : Number((item as any)?.base_rate_net ?? item.baseRateNet ?? 0) || 0\n const currency =\n typeof item.currencyCode === 'string'\n ? item.currencyCode\n : typeof (item as any)?.currency_code === 'string'\n ? (item as any).currency_code\n : null\n return {\n id: String(item.id ?? ''),\n name: typeof item.name === 'string' && item.name.length ? item.name : '\u2014',\n code: typeof item.code === 'string' ? item.code : '',\n providerKey,\n providerLabel: provider?.label ?? null,\n carrierCode: typeof item.carrierCode === 'string' ? item.carrierCode : null,\n description: typeof item.description === 'string' ? item.description : null,\n serviceLevel: typeof item.serviceLevel === 'string' ? item.serviceLevel : null,\n estimatedTransitDays:\n typeof (item as any)?.estimatedTransitDays === 'number'\n ? (item as any).estimatedTransitDays\n : typeof (item as any)?.estimated_transit_days === 'number'\n ? (item as any).estimated_transit_days\n : null,\n baseRateNet,\n baseRateGross,\n currencyCode: currency,\n isActive:\n typeof item.isActive === 'boolean'\n ? item.isActive\n : typeof (item as any)?.is_active === 'boolean'\n ? (item as any).is_active\n : false,\n updatedAt: typeof item.updatedAt === 'string' ? item.updatedAt : null,\n providerSettings:\n item && typeof (item as any).providerSettings === 'object'\n ? ((item as any).providerSettings as Record<string, unknown>)\n : null,\n }\n })\n )\n } catch (err) {\n logger.error('sales.shipping-methods.list failed', { err })\n flash(translations.errors.load, 'error')\n } finally {\n setLoading(false)\n }\n }, [providers, translations.errors.load])\n\n React.useEffect(() => {\n void loadEntries()\n }, [loadEntries, scopeVersion])\n\n const openCreate = React.useCallback(() => {\n const defaultProvider = providerOptions[0]?.value ?? ''\n setFormValues({\n ...DEFAULT_FORM,\n providerKey: defaultProvider,\n providerSettings: {},\n })\n setDialog({ mode: 'create' })\n }, [providerOptions])\n\n const openEdit = React.useCallback((entry: ShippingMethodRow) => {\n setFormValues({\n name: entry.name,\n code: entry.code,\n providerKey: entry.providerKey ?? '',\n description: entry.description ?? '',\n carrierCode: entry.carrierCode ?? '',\n serviceLevel: entry.serviceLevel ?? '',\n estimatedTransitDays: entry.estimatedTransitDays ?? undefined,\n baseRateNet: entry.baseRateNet,\n baseRateGross: entry.baseRateGross,\n currencyCode: entry.currencyCode ?? '',\n isActive: entry.isActive,\n providerSettings: entry.providerSettings ?? {},\n })\n setDialog({ mode: 'edit', entry })\n }, [])\n\n const closeDialog = React.useCallback(() => {\n setDialog(null)\n }, [])\n\n const deleteEntry = React.useCallback(async (entry: ShippingMethodRow) => {\n const message = translations.actions.deleteConfirm.replace('{{name}}', entry.name)\n const confirmed = await confirm({\n title: message,\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n const headers = buildOptimisticLockHeader(entry.updatedAt)\n const call = await withScopedApiRequestHeaders(headers, () => apiCall('/api/sales/shipping-methods', {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: entry.id }),\n }))\n if (!call.ok) {\n if (surfaceRecordConflict({ status: call.status, body: call.result }, t)) return\n await raiseCrudError(call.response, translations.errors.delete)\n }\n flash(translations.messages.deleted, 'success')\n await loadEntries()\n } catch (err) {\n logger.error('sales.shipping-methods.delete failed', { err })\n const message = err instanceof Error ? err.message : translations.errors.delete\n flash(message, 'error')\n }\n }, [confirm, loadEntries, translations.actions.deleteConfirm, translations.errors.delete, translations.messages.deleted])\n\n const columns = React.useMemo<ColumnDef<ShippingMethodRow>[]>(() => [\n {\n accessorKey: 'name',\n header: translations.table.name,\n cell: ({ row }) => (\n <div className=\"flex flex-col\">\n <span className=\"font-medium\">{row.original.name}</span>\n <span className=\"text-xs text-muted-foreground\">{row.original.code}</span>\n </div>\n ),\n },\n {\n accessorKey: 'providerKey',\n header: translations.table.provider,\n cell: ({ row }) => (\n <span className=\"text-sm\">\n {row.original.providerLabel ?? row.original.providerKey ?? '\u2014'}\n </span>\n ),\n },\n {\n accessorKey: 'rate',\n header: translations.table.rate,\n cell: ({ row }) => (\n <span className=\"text-sm\">\n {row.original.baseRateGross.toFixed(2)} {row.original.currencyCode ?? ''}\n </span>\n ),\n },\n {\n accessorKey: 'isActive',\n header: translations.table.active,\n cell: ({ row }) => (\n <span className=\"text-sm\">\n {row.original.isActive ? translations.table.statusActive : translations.table.statusInactive}\n </span>\n ),\n },\n {\n accessorKey: 'updatedAt',\n header: translations.table.updatedAt,\n cell: ({ row }) => (\n <span className=\"text-xs text-muted-foreground\">\n {row.original.updatedAt ? new Date(row.original.updatedAt).toLocaleString() : '\u2014'}\n </span>\n ),\n },\n ], [translations])\n\n const handleSubmit = React.useCallback(async (values: ShippingFormValues) => {\n if (!dialog) return\n setSubmitting(true)\n const payload: Record<string, unknown> = {\n name: values.name.trim(),\n code: values.code.trim().toLowerCase(),\n description: values.description?.trim() || undefined,\n carrierCode: values.carrierCode?.trim() || undefined,\n providerKey: values.providerKey?.trim() || undefined,\n serviceLevel: values.serviceLevel?.trim() || undefined,\n estimatedTransitDays:\n values.estimatedTransitDays === undefined || values.estimatedTransitDays === null\n ? undefined\n : Number(values.estimatedTransitDays),\n baseRateNet: Number(values.baseRateNet ?? 0),\n baseRateGross:\n values.baseRateGross === undefined || values.baseRateGross === null\n ? Number(values.baseRateNet ?? 0)\n : Number(values.baseRateGross),\n currencyCode: values.currencyCode?.trim().toUpperCase() || undefined,\n isActive: values.isActive ?? true,\n providerSettings: isRecord(values.providerSettings) ? values.providerSettings : undefined,\n }\n const path = '/api/sales/shipping-methods'\n const method = dialog.mode === 'create' ? 'POST' : 'PUT'\n if (dialog.mode === 'edit') payload.id = dialog.entry.id\n try {\n const saveShippingMethod = () => apiCall(path, {\n method,\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n const headers = buildOptimisticLockHeader(dialog.mode === 'edit' ? dialog.entry.updatedAt : null)\n const call = await withScopedApiRequestHeaders(headers, saveShippingMethod)\n if (!call.ok) {\n if (surfaceRecordConflict({ status: call.status, body: call.result }, t)) return\n await raiseCrudError(call.response, translations.errors.save)\n }\n flash(translations.messages.saved, 'success')\n await loadEntries()\n closeDialog()\n } catch (err) {\n logger.error('sales.shipping-methods.save failed', { err })\n const message = err instanceof Error ? err.message : translations.errors.save\n flash(message, 'error')\n } finally {\n setSubmitting(false)\n }\n }, [closeDialog, dialog, loadEntries, translations.errors.save, translations.messages.saved])\n\n const fields = React.useMemo<CrudField[]>(() => [\n { id: 'name', label: translations.form.name, type: 'text', required: true },\n { id: 'code', label: translations.form.code, type: 'text', required: true },\n {\n id: 'providerKey',\n label: translations.form.provider,\n type: 'select',\n required: false,\n options: providerOptions,\n },\n { id: 'carrierCode', label: translations.form.carrierCode, type: 'text' },\n { id: 'serviceLevel', label: translations.form.serviceLevel, type: 'text' },\n {\n id: 'estimatedTransitDays',\n label: translations.form.estimatedTransitDays,\n type: 'number',\n layout: 'half',\n },\n {\n id: 'baseRateNet',\n label: translations.form.baseRateNet,\n type: 'number',\n required: true,\n layout: 'half',\n },\n {\n id: 'baseRateGross',\n label: translations.form.baseRateGross,\n type: 'number',\n required: false,\n layout: 'half',\n },\n {\n id: 'currencyCode',\n label: translations.form.currencyCode,\n type: 'text',\n layout: 'half',\n },\n { id: 'description', label: translations.form.description, type: 'textarea' },\n { id: 'isActive', label: translations.form.isActive, type: 'checkbox' },\n {\n id: 'providerSettings',\n label: translations.form.providerSettings,\n type: 'custom',\n component: ProviderSettingsField,\n },\n ], [ProviderSettingsField, providerOptions, translations.form.baseRateGross, translations.form.baseRateNet, translations.form.carrierCode, translations.form.code, translations.form.currencyCode, translations.form.description, translations.form.estimatedTransitDays, translations.form.isActive, translations.form.name, translations.form.provider, translations.form.providerSettings, translations.form.serviceLevel])\n\n const filteredEntries = React.useMemo(() => {\n if (!search.trim()) return entries\n const term = search.trim().toLowerCase()\n return entries.filter(\n (entry) =>\n entry.name.toLowerCase().includes(term) ||\n entry.code.toLowerCase().includes(term) ||\n (entry.providerLabel ?? '').toLowerCase().includes(term) ||\n (entry.providerKey ?? '').toLowerCase().includes(term)\n )\n }, [entries, search])\n\n return (\n <section className=\"rounded 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-medium\">{translations.title}</h2>\n <p className=\"text-sm text-muted-foreground\">{translations.description}</p>\n </div>\n <div className=\"px-2 py-4 sm:px-4\">\n <DataTable\n embedded\n isLoading={loading}\n columns={columns}\n data={filteredEntries}\n searchValue={search}\n onSearchChange={setSearch}\n searchPlaceholder={translations.table.search}\n emptyState={<p className=\"py-8 text-center text-sm text-muted-foreground\">{translations.table.empty}</p>}\n actions={(\n <Button size=\"sm\" onClick={openCreate}>\n {translations.actions.add}\n </Button>\n )}\n refreshButton={{\n label: translations.actions.refresh,\n onRefresh: () => { void loadEntries() },\n isRefreshing: loading,\n }}\n rowActions={(row) => (\n <RowActions\n items={[\n { id: 'edit', label: translations.actions.edit, onSelect: () => openEdit(row) },\n { id: 'delete', label: translations.actions.delete, destructive: true, onSelect: () => deleteEntry(row) },\n ]}\n />\n )}\n />\n </div>\n\n <Dialog open={dialog !== null} onOpenChange={(next) => !next && closeDialog()}>\n <DialogContent className=\"max-w-4xl\">\n <DialogHeader>\n <DialogTitle>\n {dialog?.mode === 'edit' ? translations.form.editTitle : translations.form.createTitle}\n </DialogTitle>\n </DialogHeader>\n <CrudForm\n schema={shippingFormSchema}\n fields={fields}\n initialValues={formValues}\n optimisticLockUpdatedAt={dialog?.mode === 'edit' ? dialog.entry.updatedAt : null}\n submitLabel={translations.form.save}\n cancelHref={undefined}\n embedded\n onSubmit={handleSubmit}\n isLoading={submitting}\n twoColumn\n extraActions={(\n <Button type=\"button\" variant=\"ghost\" onClick={closeDialog}>\n {translations.form.cancel}\n </Button>\n )}\n />\n </DialogContent>\n </Dialog>\n {ConfirmDialogElement}\n </section>\n )\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": ["value", "message"]
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/sales/components/TaxRatesSettings.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { z } from 'zod'\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 { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Dialog, DialogContent, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport { CrudForm, type CrudField } from '@open-mercato/ui/backend/CrudForm'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { readApiResultOrThrow, apiCall, 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 { formatDateTime } from '@open-mercato/shared/lib/time'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('sales')\n\ntype TaxRateRow = {\n id: string\n name: string\n code: string | null\n rate: number | null\n isDefault: boolean\n countryCode: string | null\n regionCode: string | null\n postalCode: string | null\n city: string | null\n updatedAt: string | null\n}\n\ntype DialogState =\n | { mode: 'create' }\n | { mode: 'edit'; entry: TaxRateRow }\n\nconst taxRateFormSchema = z.object({\n name: z.string().trim().min(1),\n code: z.string().trim().min(1),\n rate: z.coerce.number().min(0).max(100),\n countryCode: z.string().trim().optional(),\n regionCode: z.string().trim().optional(),\n postalCode: z.string().trim().optional(),\n city: z.string().trim().optional(),\n isDefault: z.boolean().optional(),\n})\n\ntype TaxRateFormValues = z.infer<typeof taxRateFormSchema>\n\nconst DEFAULT_FORM_VALUES: TaxRateFormValues = {\n name: '',\n code: '',\n rate: 0,\n countryCode: '',\n regionCode: '',\n postalCode: '',\n city: '',\n isDefault: false,\n}\n\nexport function TaxRatesSettings() {\n const t = useT()\n const scopeVersion = useOrganizationScopeVersion()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const [entries, setEntries] = React.useState<TaxRateRow[]>([])\n const [loading, setLoading] = React.useState(false)\n const [dialog, setDialog] = React.useState<DialogState | null>(null)\n const [search, setSearch] = React.useState('')\n\n const translations = React.useMemo(() => ({\n title: t('sales.config.taxRates.title', 'Tax rates'),\n description: t('sales.config.taxRates.description', 'Maintain VAT classes applied to catalog pricing.'),\n actions: {\n add: t('sales.config.taxRates.actions.add', 'Add tax rate'),\n edit: t('sales.config.taxRates.actions.edit', 'Edit'),\n delete: t('sales.config.taxRates.actions.delete', 'Delete'),\n deleteConfirm: t('sales.config.taxRates.actions.deleteConfirm', 'Delete tax rate \"{{name}}\"?'),\n refresh: t('sales.config.taxRates.actions.refresh', 'Refresh'),\n },\n table: {\n name: t('sales.config.taxRates.table.name', 'Name'),\n code: t('sales.config.taxRates.table.code', 'Code'),\n rate: t('sales.config.taxRates.table.rate', 'Rate'),\n location: t('sales.config.taxRates.table.location', 'Location'),\n updatedAt: t('sales.config.taxRates.table.updatedAt', 'Updated'),\n empty: t('sales.config.taxRates.table.empty', 'No tax rates yet.'),\n search: t('sales.config.taxRates.table.search', 'Search tax rates\u2026'),\n defaultBadge: t('sales.config.taxRates.table.defaultBadge', 'Default'),\n },\n form: {\n createTitle: t('sales.config.taxRates.form.createTitle', 'Add tax rate'),\n editTitle: t('sales.config.taxRates.form.editTitle', 'Edit tax rate'),\n name: t('sales.config.taxRates.form.name', 'Name'),\n code: t('sales.config.taxRates.form.code', 'Code'),\n rate: t('sales.config.taxRates.form.rate', 'Rate (%)'),\n countryCode: t('sales.config.taxRates.form.countryCode', 'Country code'),\n regionCode: t('sales.config.taxRates.form.regionCode', 'Region code'),\n postalCode: t('sales.config.taxRates.form.postalCode', 'Postal code'),\n city: t('sales.config.taxRates.form.city', 'City'),\n isDefault: t('sales.config.taxRates.form.isDefault', 'Default tax class'),\n isDefaultHelp: t(\n 'sales.config.taxRates.form.isDefaultHelp',\n 'Preselect this class for new catalog products.'\n ),\n save: t('sales.config.taxRates.form.save', 'Save'),\n cancel: t('sales.config.taxRates.form.cancel', 'Cancel'),\n codeHelp: t('sales.config.taxRates.form.codeHelp', 'Lowercase letters, numbers, and dashes.'),\n },\n messages: {\n saved: t('sales.config.taxRates.messages.saved', 'Tax rate saved.'),\n deleted: t('sales.config.taxRates.messages.deleted', 'Tax rate deleted.'),\n },\n errors: {\n load: t('sales.config.taxRates.errors.load', 'Failed to load tax rates.'),\n save: t('sales.config.taxRates.errors.save', 'Failed to save tax rate.'),\n delete: t('sales.config.taxRates.errors.delete', 'Failed to delete tax rate.'),\n },\n }), [t])\n\n const fields = React.useMemo<CrudField[]>(() => [\n { id: 'name', label: translations.form.name, type: 'text', required: true },\n { id: 'code', label: translations.form.code, type: 'text', required: true, description: translations.form.codeHelp },\n { id: 'rate', label: translations.form.rate, type: 'number', required: true },\n { id: 'countryCode', label: translations.form.countryCode, type: 'text', layout: 'half' },\n { id: 'regionCode', label: translations.form.regionCode, type: 'text', layout: 'half' },\n { id: 'postalCode', label: translations.form.postalCode, type: 'text', layout: 'half' },\n { id: 'city', label: translations.form.city, type: 'text', layout: 'half' },\n {\n id: 'isDefault',\n label: translations.form.isDefault,\n type: 'checkbox',\n description: translations.form.isDefaultHelp,\n },\n ], [translations.form])\n\n const loadEntries = React.useCallback(async () => {\n setLoading(true)\n try {\n const payload = await readApiResultOrThrow<{ items?: Array<Record<string, unknown>> }>(\n '/api/sales/tax-rates?pageSize=200',\n undefined,\n { errorMessage: translations.errors.load, fallback: { items: [] } },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n setEntries(\n items.map((item) => {\n const rawRate = typeof item.rate === 'number' ? item.rate : Number(item.rate ?? Number.NaN)\n return {\n id: String(item.id),\n name: typeof item.name === 'string' && item.name.length ? item.name : '\u2014',\n code: typeof item.code === 'string' && item.code.length ? item.code : null,\n rate: Number.isFinite(rawRate) ? rawRate : null,\n isDefault: Boolean(\n typeof item.isDefault === 'boolean'\n ? item.isDefault\n : typeof item.is_default === 'boolean'\n ? item.is_default\n : false,\n ),\n countryCode: typeof item.countryCode === 'string' && item.countryCode.length ? item.countryCode : null,\n regionCode: typeof item.regionCode === 'string' && item.regionCode.length ? item.regionCode : null,\n postalCode: typeof item.postalCode === 'string' && item.postalCode.length ? item.postalCode : null,\n city: typeof item.city === 'string' && item.city.length ? item.city : null,\n updatedAt: typeof item.updatedAt === 'string' ? item.updatedAt : null,\n }\n }),\n )\n } catch (err) {\n logger.error('sales.tax-rates.list failed', { err })\n flash(translations.errors.load, 'error')\n } finally {\n setLoading(false)\n }\n }, [translations.errors.load])\n\n React.useEffect(() => {\n void loadEntries()\n }, [loadEntries, scopeVersion])\n\n const openCreate = React.useCallback(() => {\n setDialog({ mode: 'create' })\n }, [])\n\n const openEdit = React.useCallback((entry: TaxRateRow) => {\n setDialog({ mode: 'edit', entry })\n }, [])\n\n const closeDialog = React.useCallback(() => {\n setDialog(null)\n }, [])\n\n const handleSubmit = React.useCallback(async (values: TaxRateFormValues) => {\n if (!dialog) return\n const payload: Record<string, unknown> = {\n name: values.name.trim(),\n code: values.code.trim().toLowerCase(),\n rate: Number(values.rate),\n countryCode: values.countryCode?.trim() ? values.countryCode.trim().toUpperCase() : undefined,\n regionCode: values.regionCode?.trim() || undefined,\n postalCode: values.postalCode?.trim() || undefined,\n city: values.city?.trim() || undefined,\n isDefault: Boolean(values.isDefault),\n }\n const method = dialog.mode === 'create' ? 'POST' : 'PUT'\n if (dialog.mode === 'edit') {\n payload.id = dialog.entry.id\n }\n try {\n const saveTaxRate = () => apiCall('/api/sales/tax-rates', {\n method,\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n const headers = buildOptimisticLockHeader(dialog.mode === 'edit' ? dialog.entry.updatedAt : null)\n const call = await withScopedApiRequestHeaders(headers, saveTaxRate)\n if (!call.ok) {\n if (surfaceRecordConflict({ status: call.status, body: call.result }, t)) return\n await raiseCrudError(call.response, translations.errors.save)\n return\n }\n flash(translations.messages.saved, 'success')\n closeDialog()\n await loadEntries()\n } catch (err) {\n logger.error('sales.tax-rates.save failed', { err })\n flash(translations.errors.save, 'error')\n }\n }, [dialog, translations.errors.save, translations.messages.saved, closeDialog, loadEntries])\n\n const deleteEntry = React.useCallback(async (entry: TaxRateRow) => {\n const message = translations.actions.deleteConfirm.replace('{{name}}', entry.name || entry.code || entry.id)\n const confirmed = await confirm({\n title: message,\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n const headers = buildOptimisticLockHeader(entry.updatedAt)\n const call = await withScopedApiRequestHeaders(headers, () => apiCall('/api/sales/tax-rates', {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: entry.id }),\n }))\n if (!call.ok) {\n if (surfaceRecordConflict({ status: call.status, body: call.result }, t)) return\n await raiseCrudError(call.response, translations.errors.delete)\n return\n }\n flash(translations.messages.deleted, 'success')\n await loadEntries()\n } catch (err) {\n logger.error('sales.tax-rates.delete failed', { err })\n flash(translations.errors.delete, 'error')\n }\n }, [confirm, translations.actions.deleteConfirm, translations.errors.delete, translations.messages.deleted, loadEntries])\n\n const columns = React.useMemo<ColumnDef<TaxRateRow>[]>(() => [\n {\n header: translations.table.name,\n accessorKey: 'name',\n cell: ({ row }) => (\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium\">{row.original.name}</span>\n {row.original.isDefault ? (\n <Badge variant=\"outline\" className=\"text-xs uppercase tracking-wide\">\n {translations.table.defaultBadge}\n </Badge>\n ) : null}\n </div>\n ),\n },\n {\n header: translations.table.code,\n accessorKey: 'code',\n cell: ({ row }) => row.original.code ?? '\u2014',\n },\n {\n header: translations.table.rate,\n accessorKey: 'rate',\n cell: ({ row }) => (typeof row.original.rate === 'number' ? `${row.original.rate}%` : '\u2014'),\n },\n {\n header: translations.table.location,\n cell: ({ row }) => formatLocation(row.original),\n },\n {\n header: translations.table.updatedAt,\n accessorKey: 'updatedAt',\n cell: ({ row }) => (row.original.updatedAt ? formatDateTime(row.original.updatedAt) : '\u2014'),\n },\n ], [translations.table])\n\n const dialogValues: TaxRateFormValues = React.useMemo(() => {\n if (!dialog || dialog.mode === 'create') return { ...DEFAULT_FORM_VALUES }\n return {\n name: dialog.entry.name ?? '',\n code: dialog.entry.code ?? '',\n rate: dialog.entry.rate ?? 0,\n countryCode: dialog.entry.countryCode ?? '',\n regionCode: dialog.entry.regionCode ?? '',\n postalCode: dialog.entry.postalCode ?? '',\n city: dialog.entry.city ?? '',\n isDefault: Boolean(dialog.entry.isDefault),\n }\n }, [dialog])\n\n const filteredEntries = React.useMemo(() => {\n const term = search.trim().toLowerCase()\n if (!term) return entries\n return entries.filter((entry) => {\n const location = formatLocation(entry).toLowerCase()\n return (\n entry.name.toLowerCase().includes(term) ||\n (entry.code ?? '').toLowerCase().includes(term) ||\n location.includes(term)\n )\n })\n }, [entries, search])\n\n return (\n <section className=\"rounded 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-medium\">{translations.title}</h2>\n <p className=\"text-sm text-muted-foreground\">{translations.description}</p>\n </div>\n\n <div className=\"px-2 py-4 sm:px-4\">\n <DataTable<TaxRateRow>\n data={filteredEntries}\n columns={columns}\n isLoading={loading}\n embedded\n searchValue={search}\n onSearchChange={setSearch}\n searchPlaceholder={translations.table.search}\n emptyState={<p className=\"py-8 text-center text-sm text-muted-foreground\">{translations.table.empty}</p>}\n actions={(\n <Button onClick={openCreate} size=\"sm\">\n {translations.actions.add}\n </Button>\n )}\n refreshButton={{\n label: translations.actions.refresh,\n onRefresh: () => { void loadEntries() },\n isRefreshing: loading,\n }}\n rowActions={(row) => (\n <RowActions\n items={[\n { id: 'edit', label: translations.actions.edit, onSelect: () => openEdit(row) },\n { id: 'delete', label: translations.actions.delete, destructive: true, onSelect: () => deleteEntry(row) },\n ]}\n />\n )}\n />\n </div>\n\n <Dialog open={Boolean(dialog)} onOpenChange={(open) => { if (!open) closeDialog() }}>\n <DialogContent className=\"max-h-[90vh] overflow-y-auto sm:max-w-md\">\n <DialogHeader>\n <DialogTitle>\n {dialog?.mode === 'edit' ? translations.form.editTitle : translations.form.createTitle}\n </DialogTitle>\n </DialogHeader>\n <CrudForm<TaxRateFormValues>\n schema={taxRateFormSchema}\n fields={fields}\n initialValues={dialogValues}\n optimisticLockUpdatedAt={dialog?.mode === 'edit' ? dialog.entry.updatedAt : null}\n submitLabel={translations.form.save}\n cancelHref={undefined}\n embedded\n onSubmit={handleSubmit}\n extraActions={(\n <Button type=\"button\" variant=\"ghost\" onClick={closeDialog}>\n {translations.form.cancel}\n </Button>\n )}\n />\n </DialogContent>\n </Dialog>\n {ConfirmDialogElement}\n </section>\n )\n}\n\nfunction formatLocation(entry: TaxRateRow): string {\n const parts: string[] = []\n if (entry.city) parts.push(entry.city)\n if (entry.regionCode) parts.push(entry.regionCode)\n if (entry.postalCode) parts.push(entry.postalCode)\n if (entry.countryCode) parts.push(entry.countryCode.toUpperCase())\n return parts.length ? parts.join(', ') : '\u2014'\n}\n\n"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { z } from 'zod'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\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 { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Dialog, DialogContent, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport { CrudForm, type CrudField } from '@open-mercato/ui/backend/CrudForm'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { readApiResultOrThrow, apiCall, 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 { formatDateTime } from '@open-mercato/shared/lib/time'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('sales')\n\ntype TaxRateRow = {\n id: string\n name: string\n code: string | null\n rate: number | null\n isDefault: boolean\n countryCode: string | null\n regionCode: string | null\n postalCode: string | null\n city: string | null\n updatedAt: string | null\n}\n\ntype DialogState =\n | { mode: 'create' }\n | { mode: 'edit'; entry: TaxRateRow }\n\nconst taxRateFormSchema = z.object({\n name: z.string().trim().min(1),\n code: z.string().trim().min(1),\n rate: z.coerce.number().min(0).max(100),\n countryCode: z.string().trim().optional(),\n regionCode: z.string().trim().optional(),\n postalCode: z.string().trim().optional(),\n city: z.string().trim().optional(),\n isDefault: z.boolean().optional(),\n})\n\ntype TaxRateFormValues = z.infer<typeof taxRateFormSchema>\n\nconst DEFAULT_FORM_VALUES: TaxRateFormValues = {\n name: '',\n code: '',\n rate: 0,\n countryCode: '',\n regionCode: '',\n postalCode: '',\n city: '',\n isDefault: false,\n}\n\nexport function TaxRatesSettings() {\n const t = useT()\n const scopeVersion = useOrganizationScopeVersion()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const [entries, setEntries] = React.useState<TaxRateRow[]>([])\n const [loading, setLoading] = React.useState(false)\n const [dialog, setDialog] = React.useState<DialogState | null>(null)\n const [search, setSearch] = React.useState('')\n\n const translations = React.useMemo(() => ({\n title: t('sales.config.taxRates.title', 'Tax rates'),\n description: t('sales.config.taxRates.description', 'Maintain VAT classes applied to catalog pricing.'),\n actions: {\n add: t('sales.config.taxRates.actions.add', 'Add tax rate'),\n edit: t('sales.config.taxRates.actions.edit', 'Edit'),\n delete: t('sales.config.taxRates.actions.delete', 'Delete'),\n deleteConfirm: t('sales.config.taxRates.actions.deleteConfirm', 'Delete tax rate \"{{name}}\"?'),\n refresh: t('sales.config.taxRates.actions.refresh', 'Refresh'),\n },\n table: {\n name: t('sales.config.taxRates.table.name', 'Name'),\n code: t('sales.config.taxRates.table.code', 'Code'),\n rate: t('sales.config.taxRates.table.rate', 'Rate'),\n location: t('sales.config.taxRates.table.location', 'Location'),\n updatedAt: t('sales.config.taxRates.table.updatedAt', 'Updated'),\n empty: t('sales.config.taxRates.table.empty', 'No tax rates yet.'),\n search: t('sales.config.taxRates.table.search', 'Search tax rates\u2026'),\n defaultBadge: t('sales.config.taxRates.table.defaultBadge', 'Default'),\n },\n form: {\n createTitle: t('sales.config.taxRates.form.createTitle', 'Add tax rate'),\n editTitle: t('sales.config.taxRates.form.editTitle', 'Edit tax rate'),\n name: t('sales.config.taxRates.form.name', 'Name'),\n code: t('sales.config.taxRates.form.code', 'Code'),\n rate: t('sales.config.taxRates.form.rate', 'Rate (%)'),\n countryCode: t('sales.config.taxRates.form.countryCode', 'Country code'),\n regionCode: t('sales.config.taxRates.form.regionCode', 'Region code'),\n postalCode: t('sales.config.taxRates.form.postalCode', 'Postal code'),\n city: t('sales.config.taxRates.form.city', 'City'),\n isDefault: t('sales.config.taxRates.form.isDefault', 'Default tax class'),\n isDefaultHelp: t(\n 'sales.config.taxRates.form.isDefaultHelp',\n 'Preselect this class for new catalog products.'\n ),\n save: t('sales.config.taxRates.form.save', 'Save'),\n cancel: t('sales.config.taxRates.form.cancel', 'Cancel'),\n codeHelp: t('sales.config.taxRates.form.codeHelp', 'Lowercase letters, numbers, and dashes.'),\n },\n messages: {\n saved: t('sales.config.taxRates.messages.saved', 'Tax rate saved.'),\n deleted: t('sales.config.taxRates.messages.deleted', 'Tax rate deleted.'),\n },\n errors: {\n load: t('sales.config.taxRates.errors.load', 'Failed to load tax rates.'),\n save: t('sales.config.taxRates.errors.save', 'Failed to save tax rate.'),\n delete: t('sales.config.taxRates.errors.delete', 'Failed to delete tax rate.'),\n },\n }), [t])\n\n const fields = React.useMemo<CrudField[]>(() => [\n { id: 'name', label: translations.form.name, type: 'text', required: true },\n { id: 'code', label: translations.form.code, type: 'text', required: true, description: translations.form.codeHelp },\n { id: 'rate', label: translations.form.rate, type: 'number', required: true },\n { id: 'countryCode', label: translations.form.countryCode, type: 'text', layout: 'half' },\n { id: 'regionCode', label: translations.form.regionCode, type: 'text', layout: 'half' },\n { id: 'postalCode', label: translations.form.postalCode, type: 'text', layout: 'half' },\n { id: 'city', label: translations.form.city, type: 'text', layout: 'half' },\n {\n id: 'isDefault',\n label: translations.form.isDefault,\n type: 'checkbox',\n description: translations.form.isDefaultHelp,\n },\n ], [translations.form])\n\n const loadEntries = React.useCallback(async () => {\n setLoading(true)\n try {\n const payload = await readApiResultOrThrow<{ items?: Array<Record<string, unknown>> }>(\n '/api/sales/tax-rates?pageSize=200',\n undefined,\n { errorMessage: translations.errors.load, fallback: { items: [] } },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n setEntries(\n items.map((item) => {\n const rawRate = typeof item.rate === 'number' ? item.rate : Number(item.rate ?? Number.NaN)\n return {\n id: String(item.id),\n name: typeof item.name === 'string' && item.name.length ? item.name : '\u2014',\n code: typeof item.code === 'string' && item.code.length ? item.code : null,\n rate: Number.isFinite(rawRate) ? rawRate : null,\n isDefault: Boolean(\n typeof item.isDefault === 'boolean'\n ? item.isDefault\n : typeof item.is_default === 'boolean'\n ? item.is_default\n : false,\n ),\n countryCode: typeof item.countryCode === 'string' && item.countryCode.length ? item.countryCode : null,\n regionCode: typeof item.regionCode === 'string' && item.regionCode.length ? item.regionCode : null,\n postalCode: typeof item.postalCode === 'string' && item.postalCode.length ? item.postalCode : null,\n city: typeof item.city === 'string' && item.city.length ? item.city : null,\n updatedAt: typeof item.updatedAt === 'string' ? item.updatedAt : null,\n }\n }),\n )\n } catch (err) {\n logger.error('sales.tax-rates.list failed', { err })\n flash(translations.errors.load, 'error')\n } finally {\n setLoading(false)\n }\n }, [translations.errors.load])\n\n React.useEffect(() => {\n void loadEntries()\n }, [loadEntries, scopeVersion])\n\n const openCreate = React.useCallback(() => {\n setDialog({ mode: 'create' })\n }, [])\n\n const openEdit = React.useCallback((entry: TaxRateRow) => {\n setDialog({ mode: 'edit', entry })\n }, [])\n\n const closeDialog = React.useCallback(() => {\n setDialog(null)\n }, [])\n\n const handleSubmit = React.useCallback(async (values: TaxRateFormValues) => {\n if (!dialog) return\n const payload: Record<string, unknown> = {\n name: values.name.trim(),\n code: values.code.trim().toLowerCase(),\n rate: Number(values.rate),\n countryCode: values.countryCode?.trim() ? values.countryCode.trim().toUpperCase() : undefined,\n regionCode: values.regionCode?.trim() || undefined,\n postalCode: values.postalCode?.trim() || undefined,\n city: values.city?.trim() || undefined,\n isDefault: Boolean(values.isDefault),\n }\n const method = dialog.mode === 'create' ? 'POST' : 'PUT'\n if (dialog.mode === 'edit') {\n payload.id = dialog.entry.id\n }\n try {\n const saveTaxRate = () => apiCall('/api/sales/tax-rates', {\n method,\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n const headers = buildOptimisticLockHeader(dialog.mode === 'edit' ? dialog.entry.updatedAt : null)\n const call = await withScopedApiRequestHeaders(headers, saveTaxRate)\n if (!call.ok) {\n if (surfaceRecordConflict({ status: call.status, body: call.result }, t)) return\n await raiseCrudError(call.response, translations.errors.save)\n return\n }\n flash(translations.messages.saved, 'success')\n closeDialog()\n await loadEntries()\n } catch (err) {\n logger.error('sales.tax-rates.save failed', { err })\n flash(translations.errors.save, 'error')\n }\n }, [dialog, translations.errors.save, translations.messages.saved, closeDialog, loadEntries])\n\n const deleteEntry = React.useCallback(async (entry: TaxRateRow) => {\n const message = translations.actions.deleteConfirm.replace('{{name}}', entry.name || entry.code || entry.id)\n const confirmed = await confirm({\n title: message,\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n const headers = buildOptimisticLockHeader(entry.updatedAt)\n const call = await withScopedApiRequestHeaders(headers, () => apiCall('/api/sales/tax-rates', {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: entry.id }),\n }))\n if (!call.ok) {\n if (surfaceRecordConflict({ status: call.status, body: call.result }, t)) return\n await raiseCrudError(call.response, translations.errors.delete)\n return\n }\n flash(translations.messages.deleted, 'success')\n await loadEntries()\n } catch (err) {\n logger.error('sales.tax-rates.delete failed', { err })\n flash(translations.errors.delete, 'error')\n }\n }, [confirm, translations.actions.deleteConfirm, translations.errors.delete, translations.messages.deleted, loadEntries])\n\n const columns = React.useMemo<ColumnDef<TaxRateRow>[]>(() => [\n {\n header: translations.table.name,\n accessorKey: 'name',\n cell: ({ row }) => (\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium\">{row.original.name}</span>\n {row.original.isDefault ? (\n <Badge variant=\"outline\" className=\"text-xs uppercase tracking-wide\">\n {translations.table.defaultBadge}\n </Badge>\n ) : null}\n </div>\n ),\n },\n {\n header: translations.table.code,\n accessorKey: 'code',\n cell: ({ row }) => row.original.code ?? '\u2014',\n },\n {\n header: translations.table.rate,\n accessorKey: 'rate',\n cell: ({ row }) => (typeof row.original.rate === 'number' ? `${row.original.rate}%` : '\u2014'),\n },\n {\n header: translations.table.location,\n cell: ({ row }) => formatLocation(row.original),\n },\n {\n header: translations.table.updatedAt,\n accessorKey: 'updatedAt',\n cell: ({ row }) => (row.original.updatedAt ? formatDateTime(row.original.updatedAt) : '\u2014'),\n },\n ], [translations.table])\n\n const dialogValues: TaxRateFormValues = React.useMemo(() => {\n if (!dialog || dialog.mode === 'create') return { ...DEFAULT_FORM_VALUES }\n return {\n name: dialog.entry.name ?? '',\n code: dialog.entry.code ?? '',\n rate: dialog.entry.rate ?? 0,\n countryCode: dialog.entry.countryCode ?? '',\n regionCode: dialog.entry.regionCode ?? '',\n postalCode: dialog.entry.postalCode ?? '',\n city: dialog.entry.city ?? '',\n isDefault: Boolean(dialog.entry.isDefault),\n }\n }, [dialog])\n\n const filteredEntries = React.useMemo(() => {\n const term = search.trim().toLowerCase()\n if (!term) return entries\n return entries.filter((entry) => {\n const location = formatLocation(entry).toLowerCase()\n return (\n entry.name.toLowerCase().includes(term) ||\n (entry.code ?? '').toLowerCase().includes(term) ||\n location.includes(term)\n )\n })\n }, [entries, search])\n\n return (\n <section className=\"rounded 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-medium\">{translations.title}</h2>\n <p className=\"text-sm text-muted-foreground\">{translations.description}</p>\n </div>\n\n <div className=\"px-2 py-4 sm:px-4\">\n <DataTable<TaxRateRow>\n data={filteredEntries}\n columns={columns}\n isLoading={loading}\n embedded\n searchValue={search}\n onSearchChange={setSearch}\n searchPlaceholder={translations.table.search}\n emptyState={<p className=\"py-8 text-center text-sm text-muted-foreground\">{translations.table.empty}</p>}\n actions={(\n <Button onClick={openCreate} size=\"sm\">\n {translations.actions.add}\n </Button>\n )}\n refreshButton={{\n label: translations.actions.refresh,\n onRefresh: () => { void loadEntries() },\n isRefreshing: loading,\n }}\n rowActions={(row) => (\n <RowActions\n items={[\n { id: 'edit', label: translations.actions.edit, onSelect: () => openEdit(row) },\n { id: 'delete', label: translations.actions.delete, destructive: true, onSelect: () => deleteEntry(row) },\n ]}\n />\n )}\n />\n </div>\n\n <Dialog open={Boolean(dialog)} onOpenChange={(open) => { if (!open) closeDialog() }}>\n <DialogContent className=\"max-h-[90vh] overflow-y-auto sm:max-w-md\">\n <DialogHeader>\n <DialogTitle>\n {dialog?.mode === 'edit' ? translations.form.editTitle : translations.form.createTitle}\n </DialogTitle>\n </DialogHeader>\n <CrudForm<TaxRateFormValues>\n schema={taxRateFormSchema}\n fields={fields}\n initialValues={dialogValues}\n optimisticLockUpdatedAt={dialog?.mode === 'edit' ? dialog.entry.updatedAt : null}\n submitLabel={translations.form.save}\n cancelHref={undefined}\n embedded\n onSubmit={handleSubmit}\n extraActions={(\n <Button type=\"button\" variant=\"ghost\" onClick={closeDialog}>\n {translations.form.cancel}\n </Button>\n )}\n />\n </DialogContent>\n </Dialog>\n {ConfirmDialogElement}\n </section>\n )\n}\n\nfunction formatLocation(entry: TaxRateRow): string {\n const parts: string[] = []\n if (entry.city) parts.push(entry.city)\n if (entry.regionCode) parts.push(entry.regionCode)\n if (entry.postalCode) parts.push(entry.postalCode)\n if (entry.countryCode) parts.push(entry.countryCode.toUpperCase())\n return parts.length ? parts.join(', ') : '\u2014'\n}\n\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/sales/components/channels/SalesChannelOffersPanel.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport type { ColumnDef, SortingState } 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 { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { mapOfferRow, renderOfferPriceSummary, type OfferRow } from './offerTableUtils'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('sales')\n\ntype OffersResponse = {\n items?: Array<Record<string, unknown>>\n total?: number\n totalPages?: number\n}\n\nconst PAGE_SIZE = 25\n\nconst SAVE_CONTEXT_ID = 'sales-channel-offers-panel'\n\nexport function SalesChannelOffersPanel({ channelId, channelName }: { channelId: string; channelName?: string }) {\n const t = useT()\n const router = useRouter()\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: SAVE_CONTEXT_ID,\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n const mutationContext = React.useMemo(\n () => ({\n formId: SAVE_CONTEXT_ID,\n resourceKind: 'sales.channels.offers',\n retryLastMutation,\n }),\n [retryLastMutation],\n )\n const [rows, setRows] = React.useState<OfferRow[]>([])\n const [page, setPage] = React.useState(1)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const [isLoading, setLoading] = React.useState(true)\n const [sorting, setSorting] = React.useState<SortingState>([])\n const [reloadToken, setReloadToken] = React.useState(0)\n\n const columns = React.useMemo<ColumnDef<OfferRow>[]>(() => [\n {\n accessorKey: 'title',\n header: t('sales.channels.offers.table.offer', 'Offer'),\n cell: ({ row }) => (\n <div className=\"flex items-center gap-3\">\n {row.original.productMediaUrl ? (\n <img\n src={row.original.productMediaUrl}\n alt={row.original.productTitle ?? row.original.title}\n className=\"h-12 w-12 rounded border object-cover\"\n />\n ) : (\n <div className=\"h-12 w-12 rounded border bg-muted\" />\n )}\n <div className=\"flex flex-col gap-1\">\n <div>\n <span className=\"font-medium\">{row.original.title}</span>\n <div className=\"text-xs text-muted-foreground\">\n {row.original.productTitle ?? t('sales.channels.offers.table.emptyProduct', 'Unlinked product')}\n </div>\n </div>\n </div>\n </div>\n ),\n meta: { sticky: true },\n },\n {\n accessorKey: 'pricing',\n header: t('sales.channels.offers.table.pricing', 'Prices'),\n cell: ({ row }) => (\n <div className=\"text-sm\">{renderOfferPriceSummary(row.original, t as any)}</div>\n ),\n },\n {\n accessorKey: 'isActive',\n header: t('sales.channels.offers.table.active', 'Active'),\n cell: ({ row }) => <BooleanIcon value={row.original.isActive} />,\n },\n {\n accessorKey: 'updatedAt',\n header: t('sales.channels.offers.table.updated', 'Updated'),\n cell: ({ row }) =>\n row.original.updatedAt\n ? <span className=\"text-xs text-muted-foreground\">{new Date(row.original.updatedAt).toLocaleDateString()}</span>\n : <span className=\"text-xs text-muted-foreground\">\u2014</span>,\n },\n ], [t])\n\n const loadOffers = React.useCallback(async () => {\n if (!channelId) return\n setLoading(true)\n try {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: String(PAGE_SIZE),\n channelId,\n })\n if (search.trim().length) {\n params.set('search', search.trim())\n }\n const sort = sorting[0]\n if (sort?.id) {\n params.set('sortField', sort.id)\n params.set('sortDir', sort.desc ? 'desc' : 'asc')\n }\n const payload = await readApiResultOrThrow<OffersResponse>(\n `/api/catalog/offers?${params.toString()}`,\n undefined,\n { errorMessage: t('sales.channels.offers.errors.load', 'Failed to load offers.') },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n setRows(items.map(mapOfferRow))\n setTotal(typeof payload.total === 'number' ? payload.total : items.length)\n setTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : Math.max(1, Math.ceil(items.length / PAGE_SIZE)))\n } catch (err) {\n logger.error('sales.channels.offers', { err })\n flash(t('sales.channels.offers.errors.load', 'Failed to load offers.'), 'error')\n } finally {\n setLoading(false)\n }\n }, [channelId, page, search, sorting, t])\n\n React.useEffect(() => {\n void loadOffers()\n }, [loadOffers, reloadToken])\n\n const handleDelete = React.useCallback(async (row: OfferRow) => {\n try {\n await runMutation({\n operation: () =>\n withScopedApiRequestHeaders(buildOptimisticLockHeader(row.updatedAt), () =>\n deleteCrud('catalog/offers', row.id, {\n errorMessage: t('sales.channels.offers.errors.delete', 'Failed to delete offer.'),\n }),\n ),\n context: mutationContext,\n mutationPayload: { action: 'delete', id: row.id },\n })\n flash(t('sales.channels.offers.messages.deleted', 'Offer deleted.'), 'success')\n setReloadToken((token) => token + 1)\n } catch (err) {\n if (surfaceRecordConflict(err, t)) return\n logger.error('sales.channels.offers.delete', { err })\n flash(t('sales.channels.offers.errors.delete', 'Failed to delete offer.'), 'error')\n }\n }, [mutationContext, runMutation, t])\n\n const handleSearchChange = React.useCallback((value: string) => {\n setSearch(value)\n setPage(1)\n }, [])\n\n const tableTitle = (\n <div>\n <div className=\"text-lg font-semibold\">\n {t('sales.channels.offers.heading', 'Offers for {{name}}', { name: channelName || t('sales.channels.nav.title', 'Sales channels') })}\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {t('sales.channels.offers.subtitle', 'Override product presentation and pricing per channel.')}\n </p>\n </div>\n )\n\n const actions = (\n <Button asChild>\n <Link href={`/backend/sales/channels/${channelId}/offers/create`}>\n {t('sales.channels.offers.actions.create', 'Add offer')}\n </Link>\n </Button>\n )\n\n return (\n <DataTable<OfferRow>\n title={tableTitle}\n actions={actions}\n columns={columns}\n data={rows}\n isLoading={isLoading}\n sorting={sorting}\n onSortingChange={setSorting}\n searchValue={search}\n onSearchChange={handleSearchChange}\n searchPlaceholder={t('sales.channels.offers.table.search', 'Search offers\u2026')}\n pagination={{\n page,\n pageSize: PAGE_SIZE,\n total,\n totalPages,\n onPageChange: setPage,\n }}\n refreshButton={{\n label: t('sales.channels.offers.table.refresh', 'Refresh'),\n onRefresh: () => setReloadToken((token) => token + 1),\n isRefreshing: isLoading,\n }}\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'edit',\n label: t('sales.channels.offers.actions.edit', 'Edit'),\n href: `/backend/sales/channels/${channelId}/offers/${row.id}/edit`,\n },\n {\n id: 'delete',\n label: t('sales.channels.offers.actions.delete', 'Delete'),\n onSelect: () => handleDelete(row),\n destructive: true,\n },\n ]}\n />\n )}\n onRowClick={(row) => router.push(`/backend/sales/channels/${channelId}/offers/${row.id}/edit`)}\n emptyState={\n <div className=\"py-10 text-center text-sm text-muted-foreground\">\n {t('sales.channels.offers.table.empty', 'No offers for this channel yet.')}\n </div>\n }\n />\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport type { SortingState } 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 { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { mapOfferRow, renderOfferPriceSummary, type OfferRow } from './offerTableUtils'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('sales')\n\ntype OffersResponse = {\n items?: Array<Record<string, unknown>>\n total?: number\n totalPages?: number\n}\n\nconst PAGE_SIZE = 25\n\nconst SAVE_CONTEXT_ID = 'sales-channel-offers-panel'\n\nexport function SalesChannelOffersPanel({ channelId, channelName }: { channelId: string; channelName?: string }) {\n const t = useT()\n const router = useRouter()\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: SAVE_CONTEXT_ID,\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n const mutationContext = React.useMemo(\n () => ({\n formId: SAVE_CONTEXT_ID,\n resourceKind: 'sales.channels.offers',\n retryLastMutation,\n }),\n [retryLastMutation],\n )\n const [rows, setRows] = React.useState<OfferRow[]>([])\n const [page, setPage] = React.useState(1)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const [isLoading, setLoading] = React.useState(true)\n const [sorting, setSorting] = React.useState<SortingState>([])\n const [reloadToken, setReloadToken] = React.useState(0)\n\n const columns = React.useMemo<ColumnDef<OfferRow>[]>(() => [\n {\n accessorKey: 'title',\n header: t('sales.channels.offers.table.offer', 'Offer'),\n cell: ({ row }) => (\n <div className=\"flex items-center gap-3\">\n {row.original.productMediaUrl ? (\n <img\n src={row.original.productMediaUrl}\n alt={row.original.productTitle ?? row.original.title}\n className=\"h-12 w-12 rounded border object-cover\"\n />\n ) : (\n <div className=\"h-12 w-12 rounded border bg-muted\" />\n )}\n <div className=\"flex flex-col gap-1\">\n <div>\n <span className=\"font-medium\">{row.original.title}</span>\n <div className=\"text-xs text-muted-foreground\">\n {row.original.productTitle ?? t('sales.channels.offers.table.emptyProduct', 'Unlinked product')}\n </div>\n </div>\n </div>\n </div>\n ),\n meta: { sticky: true },\n },\n {\n accessorKey: 'pricing',\n header: t('sales.channels.offers.table.pricing', 'Prices'),\n cell: ({ row }) => (\n <div className=\"text-sm\">{renderOfferPriceSummary(row.original, t as any)}</div>\n ),\n },\n {\n accessorKey: 'isActive',\n header: t('sales.channels.offers.table.active', 'Active'),\n cell: ({ row }) => <BooleanIcon value={row.original.isActive} />,\n },\n {\n accessorKey: 'updatedAt',\n header: t('sales.channels.offers.table.updated', 'Updated'),\n cell: ({ row }) =>\n row.original.updatedAt\n ? <span className=\"text-xs text-muted-foreground\">{new Date(row.original.updatedAt).toLocaleDateString()}</span>\n : <span className=\"text-xs text-muted-foreground\">\u2014</span>,\n },\n ], [t])\n\n const loadOffers = React.useCallback(async () => {\n if (!channelId) return\n setLoading(true)\n try {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: String(PAGE_SIZE),\n channelId,\n })\n if (search.trim().length) {\n params.set('search', search.trim())\n }\n const sort = sorting[0]\n if (sort?.id) {\n params.set('sortField', sort.id)\n params.set('sortDir', sort.desc ? 'desc' : 'asc')\n }\n const payload = await readApiResultOrThrow<OffersResponse>(\n `/api/catalog/offers?${params.toString()}`,\n undefined,\n { errorMessage: t('sales.channels.offers.errors.load', 'Failed to load offers.') },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n setRows(items.map(mapOfferRow))\n setTotal(typeof payload.total === 'number' ? payload.total : items.length)\n setTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : Math.max(1, Math.ceil(items.length / PAGE_SIZE)))\n } catch (err) {\n logger.error('sales.channels.offers', { err })\n flash(t('sales.channels.offers.errors.load', 'Failed to load offers.'), 'error')\n } finally {\n setLoading(false)\n }\n }, [channelId, page, search, sorting, t])\n\n React.useEffect(() => {\n void loadOffers()\n }, [loadOffers, reloadToken])\n\n const handleDelete = React.useCallback(async (row: OfferRow) => {\n try {\n await runMutation({\n operation: () =>\n withScopedApiRequestHeaders(buildOptimisticLockHeader(row.updatedAt), () =>\n deleteCrud('catalog/offers', row.id, {\n errorMessage: t('sales.channels.offers.errors.delete', 'Failed to delete offer.'),\n }),\n ),\n context: mutationContext,\n mutationPayload: { action: 'delete', id: row.id },\n })\n flash(t('sales.channels.offers.messages.deleted', 'Offer deleted.'), 'success')\n setReloadToken((token) => token + 1)\n } catch (err) {\n if (surfaceRecordConflict(err, t)) return\n logger.error('sales.channels.offers.delete', { err })\n flash(t('sales.channels.offers.errors.delete', 'Failed to delete offer.'), 'error')\n }\n }, [mutationContext, runMutation, t])\n\n const handleSearchChange = React.useCallback((value: string) => {\n setSearch(value)\n setPage(1)\n }, [])\n\n const tableTitle = (\n <div>\n <div className=\"text-lg font-semibold\">\n {t('sales.channels.offers.heading', 'Offers for {{name}}', { name: channelName || t('sales.channels.nav.title', 'Sales channels') })}\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {t('sales.channels.offers.subtitle', 'Override product presentation and pricing per channel.')}\n </p>\n </div>\n )\n\n const actions = (\n <Button asChild>\n <Link href={`/backend/sales/channels/${channelId}/offers/create`}>\n {t('sales.channels.offers.actions.create', 'Add offer')}\n </Link>\n </Button>\n )\n\n return (\n <DataTable<OfferRow>\n title={tableTitle}\n actions={actions}\n columns={columns}\n data={rows}\n isLoading={isLoading}\n sorting={sorting}\n onSortingChange={setSorting}\n searchValue={search}\n onSearchChange={handleSearchChange}\n searchPlaceholder={t('sales.channels.offers.table.search', 'Search offers\u2026')}\n pagination={{\n page,\n pageSize: PAGE_SIZE,\n total,\n totalPages,\n onPageChange: setPage,\n }}\n refreshButton={{\n label: t('sales.channels.offers.table.refresh', 'Refresh'),\n onRefresh: () => setReloadToken((token) => token + 1),\n isRefreshing: isLoading,\n }}\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'edit',\n label: t('sales.channels.offers.actions.edit', 'Edit'),\n href: `/backend/sales/channels/${channelId}/offers/${row.id}/edit`,\n },\n {\n id: 'delete',\n label: t('sales.channels.offers.actions.delete', 'Delete'),\n onSelect: () => handleDelete(row),\n destructive: true,\n },\n ]}\n />\n )}\n onRowClick={(row) => router.push(`/backend/sales/channels/${channelId}/offers/${row.id}/edit`)}\n emptyState={\n <div className=\"py-10 text-center text-sm text-muted-foreground\">\n {t('sales.channels.offers.table.empty', 'No offers for this channel yet.')}\n </div>\n }\n />\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/sales/components/documents/AdjustmentsSection.tsx"],
4
- "sourcesContent": ["// @ts-nocheck\n\n\"use client\"\n\nimport * as React from 'react'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { ErrorMessage, LoadingMessage, TabEmptyState } from '@open-mercato/ui/backend/detail'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { createCrud, deleteCrud, updateCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { handleSectionMutationError } from './optimisticLock'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { type DictionaryOption } from '@open-mercato/core/modules/dictionaries/components/DictionaryEntrySelect'\nimport {\n emitSalesDocumentTotalsRefresh,\n subscribeSalesDocumentTotalsRefresh,\n} from '@open-mercato/core/modules/sales/lib/frontend/documentTotalsEvents'\nimport type { SectionAction } from '@open-mercato/core/modules/customers/components/detail/types'\nimport type { SalesAdjustmentKind } from '../../data/entities'\nimport { PriceWithCurrency } from '../PriceWithCurrency'\nimport { AdjustmentDialog, type AdjustmentRowData, type AdjustmentSubmitPayload } from './AdjustmentDialog'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeDetail } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { extractCustomFieldValues } from './customFieldHelpers'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('sales')\n\ntype AdjustmentRow = AdjustmentRowData\n\ntype SalesDocumentAdjustmentsSectionProps = {\n documentId: string\n kind: 'order' | 'quote'\n currencyCode: string | null | undefined\n documentUpdatedAt?: string | null\n organizationId?: string | null\n tenantId?: string | null\n onActionChange?: (action: SectionAction | null) => void\n onRowsChange?: (rows: AdjustmentRow[]) => void\n}\n\nconst FALLBACK_ADJUSTMENT_KIND_VALUES: SalesAdjustmentKind[] = [\n 'discount',\n 'tax',\n 'shipping',\n 'surcharge',\n 'custom',\n]\n\nfunction normalizeNumber(value: unknown, fallback = 0): number {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string' && value.trim().length) {\n const parsed = Number(value)\n if (!Number.isNaN(parsed)) return parsed\n }\n return fallback\n}\n\nfunction formatPercent(value: number | null | undefined): string {\n if (value === null || value === undefined) return '\u2014'\n const formatter = new Intl.NumberFormat(undefined, { style: 'percent', maximumFractionDigits: 2 })\n return formatter.format(value / 100)\n}\n\nexport function SalesDocumentAdjustmentsSection({\n documentId,\n kind,\n currencyCode,\n documentUpdatedAt,\n organizationId: orgFromProps,\n tenantId: tenantFromProps,\n onActionChange,\n onRowsChange,\n}: SalesDocumentAdjustmentsSectionProps) {\n const t = useT()\n const { organizationId, tenantId } = useOrganizationScopeDetail()\n const resolvedOrganizationId = orgFromProps ?? organizationId ?? null\n const resolvedTenantId = tenantFromProps ?? tenantId ?? null\n const [rows, setRows] = React.useState<AdjustmentRow[]>([])\n const [loading, setLoading] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n const [dialogOpen, setDialogOpen] = React.useState(false)\n const [activeAdjustment, setActiveAdjustment] = React.useState<AdjustmentRow | null>(null)\n const [kindOptions, setKindOptions] = React.useState<DictionaryOption[]>([])\n const kindLoadingRef = React.useRef(false)\n\n const apiResourcePath = React.useMemo(\n () => (kind === 'order' ? '/api/sales/order-adjustments' : '/api/sales/quote-adjustments'),\n [kind]\n )\n const crudResourcePath = React.useMemo(\n () => (kind === 'order' ? 'sales/order-adjustments' : 'sales/quote-adjustments'),\n [kind]\n )\n const documentKey = kind === 'order' ? 'orderId' : 'quoteId'\n const fallbackKindOptions = React.useMemo<DictionaryOption[]>(\n () =>\n FALLBACK_ADJUSTMENT_KIND_VALUES.map((value) => ({\n value,\n label: t(`sales.documents.adjustments.kindLabels.${value}`, value),\n color: null,\n icon: null,\n })),\n [t]\n )\n const kindOptionMap = React.useMemo(\n () =>\n kindOptions.reduce<Record<string, DictionaryOption>>((acc, entry) => {\n acc[entry.value] = entry\n return acc\n }, {}),\n [kindOptions]\n )\n const loadKindOptions = React.useCallback(async (): Promise<DictionaryOption[]> => {\n if (kindOptions.length) return kindOptions\n if (kindLoadingRef.current) return kindOptions.length ? kindOptions : fallbackKindOptions\n kindLoadingRef.current = true\n try {\n const params = new URLSearchParams({ page: '1', pageSize: '100' })\n const response = await apiCall<{ items?: Array<Record<string, unknown>> }>(\n `/api/sales/adjustment-kinds?${params.toString()}`,\n undefined,\n { fallback: { items: [] } }\n )\n const items = Array.isArray(response.result?.items) ? response.result.items : []\n const parsed = items\n .map((item) => {\n const value = typeof (item as any).value === 'string' ? (item as any).value.trim() : ''\n if (!value) return null\n const label =\n typeof (item as any).label === 'string' && (item as any).label.trim().length\n ? (item as any).label.trim()\n : value\n const color =\n typeof (item as any).color === 'string' && /^#([0-9a-fA-F]{6})$/.test((item as any).color)\n ? `#${(item as any).color.slice(1).toLowerCase()}`\n : null\n const icon =\n typeof (item as any).icon === 'string' && (item as any).icon.trim().length\n ? (item as any).icon.trim()\n : null\n return { value, label, color, icon }\n })\n .filter((entry): entry is DictionaryOption => Boolean(entry))\n const merged = new Map<string, DictionaryOption>()\n fallbackKindOptions.forEach((entry) => merged.set(entry.value, entry))\n parsed.forEach((entry) => merged.set(entry.value, entry))\n const options = Array.from(merged.values()).sort((a, b) =>\n a.label.localeCompare(b.label, undefined, { sensitivity: 'base' })\n )\n setKindOptions(options)\n kindLoadingRef.current = false\n return options\n } catch (err) {\n logger.error('sales.adjustment-kinds.fetch', { err })\n kindLoadingRef.current = false\n setKindOptions(fallbackKindOptions)\n return fallbackKindOptions\n }\n }, [fallbackKindOptions, kindOptions])\n\n React.useEffect(() => {\n loadKindOptions().catch(() => {})\n }, [loadKindOptions, resolvedOrganizationId, resolvedTenantId])\n\n const loadAdjustments = React.useCallback(async () => {\n setLoading(true)\n setError(null)\n try {\n const params = new URLSearchParams({ page: '1', pageSize: '100', [documentKey]: documentId })\n const response = await apiCall<{ items?: Array<Record<string, unknown>> }>(\n `${apiResourcePath}?${params.toString()}`,\n undefined,\n { fallback: { items: [] } }\n )\n const items = Array.isArray(response.result?.items) ? response.result.items : []\n const mapped: AdjustmentRow[] = items\n .map((item) => {\n const id = typeof item.id === 'string' ? item.id : null\n if (!id) return null\n const amountNet = normalizeNumber(\n (item as any).amount_net ?? (item as any).amountNet ?? (item as any).amount_net_amount,\n NaN\n )\n const amountGross = normalizeNumber(\n (item as any).amount_gross ?? (item as any).amountGross ?? (item as any).amount_gross_amount,\n NaN\n )\n const rateRaw = normalizeNumber((item as any).rate, NaN)\n const kindValue =\n typeof item.kind === 'string' && item.kind.trim().length\n ? (item.kind.trim() as SalesAdjustmentKind)\n : 'custom'\n const currency =\n typeof (item as any).currency_code === 'string'\n ? (item as any).currency_code\n : typeof (item as any).currencyCode === 'string'\n ? (item as any).currencyCode\n : currencyCode ?? null\n const customFields = extractCustomFieldValues(item as Record<string, unknown>)\n const customFieldSetId =\n typeof (item as any).custom_field_set_id === 'string'\n ? (item as any).custom_field_set_id\n : typeof (item as any).customFieldSetId === 'string'\n ? (item as any).customFieldSetId\n : null\n const metadata =\n typeof (item as any).metadata === 'object' && (item as any).metadata\n ? ((item as any).metadata as Record<string, unknown>)\n : null\n return {\n id,\n label: typeof item.label === 'string' ? item.label : null,\n code: typeof item.code === 'string' ? item.code : null,\n kind: kindValue,\n calculatorKey:\n typeof (item as any).calculator_key === 'string'\n ? (item as any).calculator_key\n : typeof (item as any).calculatorKey === 'string'\n ? (item as any).calculatorKey\n : null,\n rate: Number.isFinite(rateRaw) ? rateRaw : null,\n amountNet: Number.isFinite(amountNet) ? amountNet : null,\n amountGross: Number.isFinite(amountGross) ? amountGross : null,\n currencyCode: currency,\n position:\n typeof item.position === 'number'\n ? item.position\n : typeof (item as any).position === 'string'\n ? Number((item as any).position)\n : 0,\n customFields: Object.keys(customFields).length ? customFields : null,\n customFieldSetId,\n metadata,\n }\n })\n .filter((entry): entry is AdjustmentRow => Boolean(entry))\n const ordered = [...mapped].sort((a, b) => (a.position ?? 0) - (b.position ?? 0))\n setRows(ordered)\n if (onRowsChange) onRowsChange(ordered)\n } catch (err) {\n logger.error('sales.document.adjustments.load', { err })\n setError(t('sales.documents.adjustments.errorLoad', 'Failed to load adjustments.'))\n } finally {\n setLoading(false)\n }\n }, [apiResourcePath, currencyCode, documentId, documentKey, onRowsChange, t])\n\n React.useEffect(() => {\n void loadAdjustments()\n }, [loadAdjustments])\n\n React.useEffect(\n () =>\n subscribeSalesDocumentTotalsRefresh((detail) => {\n if (detail.documentId !== documentId) return\n if (detail.kind && detail.kind !== kind) return\n void loadAdjustments()\n }),\n [documentId, kind, loadAdjustments],\n )\n\n const resolveKindLabel = React.useCallback(\n (kindValue: SalesAdjustmentKind) => {\n const option = kindOptionMap[kindValue]\n if (option) return option.label\n return t(`sales.documents.adjustments.kindLabels.${kindValue}`, typeof kindValue === 'string' ? kindValue : String(kindValue))\n },\n [kindOptionMap, t]\n )\n\n const handleOpenCreate = React.useCallback(() => {\n setActiveAdjustment(null)\n setDialogOpen(true)\n }, [])\n\n const handleEdit = React.useCallback((row: AdjustmentRow) => {\n setActiveAdjustment(row)\n setDialogOpen(true)\n }, [])\n\n const handleCloseDialog = React.useCallback(() => {\n setDialogOpen(false)\n setActiveAdjustment(null)\n }, [])\n\n React.useEffect(() => {\n if (!onActionChange) return\n onActionChange({\n label: t('sales.documents.adjustments.add', 'Add adjustment'),\n onClick: handleOpenCreate,\n disabled: false,\n })\n return () => onActionChange(null)\n }, [handleOpenCreate, onActionChange, t])\n\n const handleFormSubmit = React.useCallback(\n async (values: AdjustmentSubmitPayload) => {\n if (!resolvedOrganizationId || !resolvedTenantId) {\n throw createCrudFormError(\n t('sales.documents.adjustments.errorScope', 'Organization and tenant are required.')\n )\n }\n const payload: Record<string, unknown> = {\n [documentKey]: String(documentId),\n organizationId: String(resolvedOrganizationId),\n tenantId: String(resolvedTenantId),\n scope: 'order',\n kind: values.kind ?? 'custom',\n code: values.code ?? undefined,\n label: values.label ?? undefined,\n calculatorKey: values.calculatorKey ?? undefined,\n rate: Number.isFinite(values.rate) ? values.rate : undefined,\n amountNet: Number.isFinite(values.amountNet) ? values.amountNet : undefined,\n amountGross: Number.isFinite(values.amountGross) ? values.amountGross : undefined,\n currencyCode: (values.currencyCode ?? currencyCode ?? '').toUpperCase(),\n position: Number.isFinite(values.position) ? values.position : undefined,\n customFields: values.customFields ?? undefined,\n metadata: values.metadata ?? undefined,\n }\n\n const action = values.id ? updateCrud : createCrud\n try {\n const result = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(documentUpdatedAt),\n () =>\n action(\n crudResourcePath,\n values.id ? { id: values.id, ...payload } : payload,\n {\n successMessage: values.id\n ? t('sales.documents.adjustments.updated', 'Adjustment updated.')\n : t('sales.documents.adjustments.created', 'Adjustment added.'),\n errorMessage: t('sales.documents.adjustments.errorSave', 'Failed to save adjustment.'),\n }\n )\n )\n if (result.ok) {\n await loadAdjustments()\n emitSalesDocumentTotalsRefresh({ documentId, kind })\n setDialogOpen(false)\n setActiveAdjustment(null)\n }\n } catch (err) {\n if (\n handleSectionMutationError(err, t, () => void loadAdjustments())\n ) {\n setDialogOpen(false)\n setActiveAdjustment(null)\n return\n }\n throw err\n }\n },\n [\n currencyCode,\n documentId,\n documentKey,\n documentUpdatedAt,\n kind,\n loadAdjustments,\n crudResourcePath,\n resolvedOrganizationId,\n resolvedTenantId,\n t,\n ]\n )\n\n const handleDelete = React.useCallback(\n async (row: AdjustmentRow) => {\n try {\n const result = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(documentUpdatedAt),\n () =>\n deleteCrud(crudResourcePath, {\n body: {\n id: row.id,\n [documentKey]: documentId,\n organizationId: resolvedOrganizationId ?? undefined,\n tenantId: resolvedTenantId ?? undefined,\n },\n errorMessage: t('sales.documents.adjustments.errorDelete', 'Failed to delete adjustment.'),\n })\n )\n if (result.ok) {\n await loadAdjustments()\n emitSalesDocumentTotalsRefresh({ documentId, kind })\n }\n } catch (err) {\n if (handleSectionMutationError(err, t, () => void loadAdjustments())) return\n logger.error('sales.document.adjustments.delete', { err })\n flash(t('sales.documents.adjustments.errorDelete', 'Failed to delete adjustment.'), 'error')\n }\n },\n [crudResourcePath, documentId, documentKey, documentUpdatedAt, kind, loadAdjustments, resolvedOrganizationId, resolvedTenantId, t]\n )\n\n const columns = React.useMemo<ColumnDef<AdjustmentRow>[]>(\n () => [\n {\n id: 'label',\n header: t('sales.documents.adjustments.label', 'Label'),\n cell: ({ row }) => (\n <div className=\"flex flex-col\">\n <span className=\"font-medium text-foreground\">{row.original.label ?? t('sales.documents.adjustments.untitled', 'Untitled')}</span>\n {row.original.code ? <span className=\"text-xs text-muted-foreground\">{row.original.code}</span> : null}\n </div>\n ),\n },\n {\n id: 'kind',\n header: t('sales.documents.adjustments.kindLabel', 'Kind'),\n cell: ({ row }) => <Badge variant=\"outline\">{resolveKindLabel(row.original.kind)}</Badge>,\n },\n {\n id: 'amountNet',\n header: t('sales.documents.adjustments.amountNet', 'Net amount'),\n cell: ({ row }) => (\n <PriceWithCurrency amount={row.original.amountNet} currency={row.original.currencyCode ?? currencyCode} className=\"font-mono text-sm\" />\n ),\n },\n {\n id: 'amountGross',\n header: t('sales.documents.adjustments.amountGross', 'Gross amount'),\n cell: ({ row }) => (\n <PriceWithCurrency amount={row.original.amountGross} currency={row.original.currencyCode ?? currencyCode} className=\"font-mono text-sm\" />\n ),\n },\n {\n id: 'rate',\n header: t('sales.documents.adjustments.rate', 'Rate'),\n cell: ({ row }) => <span className=\"font-mono text-sm text-muted-foreground\">{formatPercent(row.original.rate)}</span>,\n },\n {\n id: 'position',\n header: t('sales.documents.adjustments.position', 'Position'),\n cell: ({ row }) => <span className=\"text-sm text-muted-foreground\">{row.original.position}</span>,\n },\n ],\n [currencyCode, resolveKindLabel, t]\n )\n\n const showLoadingState = loading && rows.length === 0\n\n const renderRowActions = React.useCallback(\n (row: AdjustmentRow) => (\n <RowActions\n items={[\n {\n id: 'edit',\n label: t('ui.actions.edit', 'Edit'),\n onSelect: () => handleEdit(row),\n },\n {\n id: 'delete',\n label: t('ui.actions.delete', 'Delete'),\n destructive: true,\n onSelect: () => handleDelete(row),\n },\n ]}\n />\n ),\n [handleDelete, handleEdit, t]\n )\n\n return (\n <div className=\"space-y-4\">\n {error ? (\n <ErrorMessage title={t('sales.documents.adjustments.errorLoad', 'Failed to load adjustments.')} description={error} onRetry={() => void loadAdjustments()} />\n ) : null}\n {showLoadingState ? (\n <LoadingMessage\n label={t('sales.documents.adjustments.loading', 'Loading adjustments\u2026')}\n className=\"border-0 bg-transparent p-0 py-8 justify-center\"\n />\n ) : (\n <DataTable<AdjustmentRow>\n data={rows}\n columns={columns}\n isLoading={loading && rows.length > 0}\n embedded\n onRowClick={handleEdit}\n rowActions={renderRowActions}\n emptyState={\n <TabEmptyState\n title={t('sales.documents.empty.adjustments.title', 'No adjustments yet.')}\n description={t('sales.documents.empty.adjustments.description', 'Add discounts, fees, or taxes to refine totals.')}\n actionLabel={t('sales.documents.adjustments.add', 'Add adjustment')}\n onAction={handleOpenCreate}\n />\n }\n />\n )}\n\n <AdjustmentDialog\n open={dialogOpen}\n onOpenChange={(open) => {\n if (!open) {\n handleCloseDialog()\n } else {\n setDialogOpen(true)\n }\n }}\n kind={kind}\n currencyCode={currencyCode ?? null}\n kindOptions={kindOptions.length ? kindOptions : fallbackKindOptions}\n loadKindOptions={loadKindOptions}\n labels={{\n addTitle: t('sales.documents.adjustments.addTitle', 'Add adjustment'),\n editTitle: t('sales.documents.adjustments.editTitle', 'Edit adjustment'),\n submitCreate: t('sales.documents.adjustments.submitCreate', 'Add adjustment'),\n submitUpdate: t('sales.documents.adjustments.submitUpdate', 'Save changes'),\n }}\n initialAdjustment={activeAdjustment}\n onSubmit={handleFormSubmit}\n />\n </div>\n )\n}\n"],
4
+ "sourcesContent": ["// @ts-nocheck\n\n\"use client\"\n\nimport * as React from 'react'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { ErrorMessage, LoadingMessage, TabEmptyState } from '@open-mercato/ui/backend/detail'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { createCrud, deleteCrud, updateCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { handleSectionMutationError } from './optimisticLock'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { type DictionaryOption } from '@open-mercato/core/modules/dictionaries/components/DictionaryEntrySelect'\nimport {\n emitSalesDocumentTotalsRefresh,\n subscribeSalesDocumentTotalsRefresh,\n} from '@open-mercato/core/modules/sales/lib/frontend/documentTotalsEvents'\nimport type { SectionAction } from '@open-mercato/core/modules/customers/components/detail/types'\nimport type { SalesAdjustmentKind } from '../../data/entities'\nimport { PriceWithCurrency } from '../PriceWithCurrency'\nimport { AdjustmentDialog, type AdjustmentRowData, type AdjustmentSubmitPayload } from './AdjustmentDialog'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeDetail } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { extractCustomFieldValues } from './customFieldHelpers'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('sales')\n\ntype AdjustmentRow = AdjustmentRowData\n\ntype SalesDocumentAdjustmentsSectionProps = {\n documentId: string\n kind: 'order' | 'quote'\n currencyCode: string | null | undefined\n documentUpdatedAt?: string | null\n organizationId?: string | null\n tenantId?: string | null\n onActionChange?: (action: SectionAction | null) => void\n onRowsChange?: (rows: AdjustmentRow[]) => void\n}\n\nconst FALLBACK_ADJUSTMENT_KIND_VALUES: SalesAdjustmentKind[] = [\n 'discount',\n 'tax',\n 'shipping',\n 'surcharge',\n 'custom',\n]\n\nfunction normalizeNumber(value: unknown, fallback = 0): number {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string' && value.trim().length) {\n const parsed = Number(value)\n if (!Number.isNaN(parsed)) return parsed\n }\n return fallback\n}\n\nfunction formatPercent(value: number | null | undefined): string {\n if (value === null || value === undefined) return '\u2014'\n const formatter = new Intl.NumberFormat(undefined, { style: 'percent', maximumFractionDigits: 2 })\n return formatter.format(value / 100)\n}\n\nexport function SalesDocumentAdjustmentsSection({\n documentId,\n kind,\n currencyCode,\n documentUpdatedAt,\n organizationId: orgFromProps,\n tenantId: tenantFromProps,\n onActionChange,\n onRowsChange,\n}: SalesDocumentAdjustmentsSectionProps) {\n const t = useT()\n const { organizationId, tenantId } = useOrganizationScopeDetail()\n const resolvedOrganizationId = orgFromProps ?? organizationId ?? null\n const resolvedTenantId = tenantFromProps ?? tenantId ?? null\n const [rows, setRows] = React.useState<AdjustmentRow[]>([])\n const [loading, setLoading] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n const [dialogOpen, setDialogOpen] = React.useState(false)\n const [activeAdjustment, setActiveAdjustment] = React.useState<AdjustmentRow | null>(null)\n const [kindOptions, setKindOptions] = React.useState<DictionaryOption[]>([])\n const kindLoadingRef = React.useRef(false)\n\n const apiResourcePath = React.useMemo(\n () => (kind === 'order' ? '/api/sales/order-adjustments' : '/api/sales/quote-adjustments'),\n [kind]\n )\n const crudResourcePath = React.useMemo(\n () => (kind === 'order' ? 'sales/order-adjustments' : 'sales/quote-adjustments'),\n [kind]\n )\n const documentKey = kind === 'order' ? 'orderId' : 'quoteId'\n const fallbackKindOptions = React.useMemo<DictionaryOption[]>(\n () =>\n FALLBACK_ADJUSTMENT_KIND_VALUES.map((value) => ({\n value,\n label: t(`sales.documents.adjustments.kindLabels.${value}`, value),\n color: null,\n icon: null,\n })),\n [t]\n )\n const kindOptionMap = React.useMemo(\n () =>\n kindOptions.reduce<Record<string, DictionaryOption>>((acc, entry) => {\n acc[entry.value] = entry\n return acc\n }, {}),\n [kindOptions]\n )\n const loadKindOptions = React.useCallback(async (): Promise<DictionaryOption[]> => {\n if (kindOptions.length) return kindOptions\n if (kindLoadingRef.current) return kindOptions.length ? kindOptions : fallbackKindOptions\n kindLoadingRef.current = true\n try {\n const params = new URLSearchParams({ page: '1', pageSize: '100' })\n const response = await apiCall<{ items?: Array<Record<string, unknown>> }>(\n `/api/sales/adjustment-kinds?${params.toString()}`,\n undefined,\n { fallback: { items: [] } }\n )\n const items = Array.isArray(response.result?.items) ? response.result.items : []\n const parsed = items\n .map((item) => {\n const value = typeof (item as any).value === 'string' ? (item as any).value.trim() : ''\n if (!value) return null\n const label =\n typeof (item as any).label === 'string' && (item as any).label.trim().length\n ? (item as any).label.trim()\n : value\n const color =\n typeof (item as any).color === 'string' && /^#([0-9a-fA-F]{6})$/.test((item as any).color)\n ? `#${(item as any).color.slice(1).toLowerCase()}`\n : null\n const icon =\n typeof (item as any).icon === 'string' && (item as any).icon.trim().length\n ? (item as any).icon.trim()\n : null\n return { value, label, color, icon }\n })\n .filter((entry): entry is DictionaryOption => Boolean(entry))\n const merged = new Map<string, DictionaryOption>()\n fallbackKindOptions.forEach((entry) => merged.set(entry.value, entry))\n parsed.forEach((entry) => merged.set(entry.value, entry))\n const options = Array.from(merged.values()).sort((a, b) =>\n a.label.localeCompare(b.label, undefined, { sensitivity: 'base' })\n )\n setKindOptions(options)\n kindLoadingRef.current = false\n return options\n } catch (err) {\n logger.error('sales.adjustment-kinds.fetch', { err })\n kindLoadingRef.current = false\n setKindOptions(fallbackKindOptions)\n return fallbackKindOptions\n }\n }, [fallbackKindOptions, kindOptions])\n\n React.useEffect(() => {\n loadKindOptions().catch(() => {})\n }, [loadKindOptions, resolvedOrganizationId, resolvedTenantId])\n\n const loadAdjustments = React.useCallback(async () => {\n setLoading(true)\n setError(null)\n try {\n const params = new URLSearchParams({ page: '1', pageSize: '100', [documentKey]: documentId })\n const response = await apiCall<{ items?: Array<Record<string, unknown>> }>(\n `${apiResourcePath}?${params.toString()}`,\n undefined,\n { fallback: { items: [] } }\n )\n const items = Array.isArray(response.result?.items) ? response.result.items : []\n const mapped: AdjustmentRow[] = items\n .map((item) => {\n const id = typeof item.id === 'string' ? item.id : null\n if (!id) return null\n const amountNet = normalizeNumber(\n (item as any).amount_net ?? (item as any).amountNet ?? (item as any).amount_net_amount,\n NaN\n )\n const amountGross = normalizeNumber(\n (item as any).amount_gross ?? (item as any).amountGross ?? (item as any).amount_gross_amount,\n NaN\n )\n const rateRaw = normalizeNumber((item as any).rate, NaN)\n const kindValue =\n typeof item.kind === 'string' && item.kind.trim().length\n ? (item.kind.trim() as SalesAdjustmentKind)\n : 'custom'\n const currency =\n typeof (item as any).currency_code === 'string'\n ? (item as any).currency_code\n : typeof (item as any).currencyCode === 'string'\n ? (item as any).currencyCode\n : currencyCode ?? null\n const customFields = extractCustomFieldValues(item as Record<string, unknown>)\n const customFieldSetId =\n typeof (item as any).custom_field_set_id === 'string'\n ? (item as any).custom_field_set_id\n : typeof (item as any).customFieldSetId === 'string'\n ? (item as any).customFieldSetId\n : null\n const metadata =\n typeof (item as any).metadata === 'object' && (item as any).metadata\n ? ((item as any).metadata as Record<string, unknown>)\n : null\n return {\n id,\n label: typeof item.label === 'string' ? item.label : null,\n code: typeof item.code === 'string' ? item.code : null,\n kind: kindValue,\n calculatorKey:\n typeof (item as any).calculator_key === 'string'\n ? (item as any).calculator_key\n : typeof (item as any).calculatorKey === 'string'\n ? (item as any).calculatorKey\n : null,\n rate: Number.isFinite(rateRaw) ? rateRaw : null,\n amountNet: Number.isFinite(amountNet) ? amountNet : null,\n amountGross: Number.isFinite(amountGross) ? amountGross : null,\n currencyCode: currency,\n position:\n typeof item.position === 'number'\n ? item.position\n : typeof (item as any).position === 'string'\n ? Number((item as any).position)\n : 0,\n customFields: Object.keys(customFields).length ? customFields : null,\n customFieldSetId,\n metadata,\n }\n })\n .filter((entry): entry is AdjustmentRow => Boolean(entry))\n const ordered = [...mapped].sort((a, b) => (a.position ?? 0) - (b.position ?? 0))\n setRows(ordered)\n if (onRowsChange) onRowsChange(ordered)\n } catch (err) {\n logger.error('sales.document.adjustments.load', { err })\n setError(t('sales.documents.adjustments.errorLoad', 'Failed to load adjustments.'))\n } finally {\n setLoading(false)\n }\n }, [apiResourcePath, currencyCode, documentId, documentKey, onRowsChange, t])\n\n React.useEffect(() => {\n void loadAdjustments()\n }, [loadAdjustments])\n\n React.useEffect(\n () =>\n subscribeSalesDocumentTotalsRefresh((detail) => {\n if (detail.documentId !== documentId) return\n if (detail.kind && detail.kind !== kind) return\n void loadAdjustments()\n }),\n [documentId, kind, loadAdjustments],\n )\n\n const resolveKindLabel = React.useCallback(\n (kindValue: SalesAdjustmentKind) => {\n const option = kindOptionMap[kindValue]\n if (option) return option.label\n return t(`sales.documents.adjustments.kindLabels.${kindValue}`, typeof kindValue === 'string' ? kindValue : String(kindValue))\n },\n [kindOptionMap, t]\n )\n\n const handleOpenCreate = React.useCallback(() => {\n setActiveAdjustment(null)\n setDialogOpen(true)\n }, [])\n\n const handleEdit = React.useCallback((row: AdjustmentRow) => {\n setActiveAdjustment(row)\n setDialogOpen(true)\n }, [])\n\n const handleCloseDialog = React.useCallback(() => {\n setDialogOpen(false)\n setActiveAdjustment(null)\n }, [])\n\n React.useEffect(() => {\n if (!onActionChange) return\n onActionChange({\n label: t('sales.documents.adjustments.add', 'Add adjustment'),\n onClick: handleOpenCreate,\n disabled: false,\n })\n return () => onActionChange(null)\n }, [handleOpenCreate, onActionChange, t])\n\n const handleFormSubmit = React.useCallback(\n async (values: AdjustmentSubmitPayload) => {\n if (!resolvedOrganizationId || !resolvedTenantId) {\n throw createCrudFormError(\n t('sales.documents.adjustments.errorScope', 'Organization and tenant are required.')\n )\n }\n const payload: Record<string, unknown> = {\n [documentKey]: String(documentId),\n organizationId: String(resolvedOrganizationId),\n tenantId: String(resolvedTenantId),\n scope: 'order',\n kind: values.kind ?? 'custom',\n code: values.code ?? undefined,\n label: values.label ?? undefined,\n calculatorKey: values.calculatorKey ?? undefined,\n rate: Number.isFinite(values.rate) ? values.rate : undefined,\n amountNet: Number.isFinite(values.amountNet) ? values.amountNet : undefined,\n amountGross: Number.isFinite(values.amountGross) ? values.amountGross : undefined,\n currencyCode: (values.currencyCode ?? currencyCode ?? '').toUpperCase(),\n position: Number.isFinite(values.position) ? values.position : undefined,\n customFields: values.customFields ?? undefined,\n metadata: values.metadata ?? undefined,\n }\n\n const action = values.id ? updateCrud : createCrud\n try {\n const result = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(documentUpdatedAt),\n () =>\n action(\n crudResourcePath,\n values.id ? { id: values.id, ...payload } : payload,\n {\n successMessage: values.id\n ? t('sales.documents.adjustments.updated', 'Adjustment updated.')\n : t('sales.documents.adjustments.created', 'Adjustment added.'),\n errorMessage: t('sales.documents.adjustments.errorSave', 'Failed to save adjustment.'),\n }\n )\n )\n if (result.ok) {\n await loadAdjustments()\n emitSalesDocumentTotalsRefresh({ documentId, kind })\n setDialogOpen(false)\n setActiveAdjustment(null)\n }\n } catch (err) {\n if (\n handleSectionMutationError(err, t, () => void loadAdjustments())\n ) {\n setDialogOpen(false)\n setActiveAdjustment(null)\n return\n }\n throw err\n }\n },\n [\n currencyCode,\n documentId,\n documentKey,\n documentUpdatedAt,\n kind,\n loadAdjustments,\n crudResourcePath,\n resolvedOrganizationId,\n resolvedTenantId,\n t,\n ]\n )\n\n const handleDelete = React.useCallback(\n async (row: AdjustmentRow) => {\n try {\n const result = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(documentUpdatedAt),\n () =>\n deleteCrud(crudResourcePath, {\n body: {\n id: row.id,\n [documentKey]: documentId,\n organizationId: resolvedOrganizationId ?? undefined,\n tenantId: resolvedTenantId ?? undefined,\n },\n errorMessage: t('sales.documents.adjustments.errorDelete', 'Failed to delete adjustment.'),\n })\n )\n if (result.ok) {\n await loadAdjustments()\n emitSalesDocumentTotalsRefresh({ documentId, kind })\n }\n } catch (err) {\n if (handleSectionMutationError(err, t, () => void loadAdjustments())) return\n logger.error('sales.document.adjustments.delete', { err })\n flash(t('sales.documents.adjustments.errorDelete', 'Failed to delete adjustment.'), 'error')\n }\n },\n [crudResourcePath, documentId, documentKey, documentUpdatedAt, kind, loadAdjustments, resolvedOrganizationId, resolvedTenantId, t]\n )\n\n const columns = React.useMemo<ColumnDef<AdjustmentRow>[]>(\n () => [\n {\n id: 'label',\n header: t('sales.documents.adjustments.label', 'Label'),\n cell: ({ row }) => (\n <div className=\"flex flex-col\">\n <span className=\"font-medium text-foreground\">{row.original.label ?? t('sales.documents.adjustments.untitled', 'Untitled')}</span>\n {row.original.code ? <span className=\"text-xs text-muted-foreground\">{row.original.code}</span> : null}\n </div>\n ),\n },\n {\n id: 'kind',\n header: t('sales.documents.adjustments.kindLabel', 'Kind'),\n cell: ({ row }) => <Badge variant=\"outline\">{resolveKindLabel(row.original.kind)}</Badge>,\n },\n {\n id: 'amountNet',\n header: t('sales.documents.adjustments.amountNet', 'Net amount'),\n cell: ({ row }) => (\n <PriceWithCurrency amount={row.original.amountNet} currency={row.original.currencyCode ?? currencyCode} className=\"font-mono text-sm\" />\n ),\n },\n {\n id: 'amountGross',\n header: t('sales.documents.adjustments.amountGross', 'Gross amount'),\n cell: ({ row }) => (\n <PriceWithCurrency amount={row.original.amountGross} currency={row.original.currencyCode ?? currencyCode} className=\"font-mono text-sm\" />\n ),\n },\n {\n id: 'rate',\n header: t('sales.documents.adjustments.rate', 'Rate'),\n cell: ({ row }) => <span className=\"font-mono text-sm text-muted-foreground\">{formatPercent(row.original.rate)}</span>,\n },\n {\n id: 'position',\n header: t('sales.documents.adjustments.position', 'Position'),\n cell: ({ row }) => <span className=\"text-sm text-muted-foreground\">{row.original.position}</span>,\n },\n ],\n [currencyCode, resolveKindLabel, t]\n )\n\n const showLoadingState = loading && rows.length === 0\n\n const renderRowActions = React.useCallback(\n (row: AdjustmentRow) => (\n <RowActions\n items={[\n {\n id: 'edit',\n label: t('ui.actions.edit', 'Edit'),\n onSelect: () => handleEdit(row),\n },\n {\n id: 'delete',\n label: t('ui.actions.delete', 'Delete'),\n destructive: true,\n onSelect: () => handleDelete(row),\n },\n ]}\n />\n ),\n [handleDelete, handleEdit, t]\n )\n\n return (\n <div className=\"space-y-4\">\n {error ? (\n <ErrorMessage title={t('sales.documents.adjustments.errorLoad', 'Failed to load adjustments.')} description={error} onRetry={() => void loadAdjustments()} />\n ) : null}\n {showLoadingState ? (\n <LoadingMessage\n label={t('sales.documents.adjustments.loading', 'Loading adjustments\u2026')}\n className=\"border-0 bg-transparent p-0 py-8 justify-center\"\n />\n ) : (\n <DataTable<AdjustmentRow>\n data={rows}\n columns={columns}\n isLoading={loading && rows.length > 0}\n embedded\n onRowClick={handleEdit}\n rowActions={renderRowActions}\n emptyState={\n <TabEmptyState\n title={t('sales.documents.empty.adjustments.title', 'No adjustments yet.')}\n description={t('sales.documents.empty.adjustments.description', 'Add discounts, fees, or taxes to refine totals.')}\n actionLabel={t('sales.documents.adjustments.add', 'Add adjustment')}\n onAction={handleOpenCreate}\n />\n }\n />\n )}\n\n <AdjustmentDialog\n open={dialogOpen}\n onOpenChange={(open) => {\n if (!open) {\n handleCloseDialog()\n } else {\n setDialogOpen(true)\n }\n }}\n kind={kind}\n currencyCode={currencyCode ?? null}\n kindOptions={kindOptions.length ? kindOptions : fallbackKindOptions}\n loadKindOptions={loadKindOptions}\n labels={{\n addTitle: t('sales.documents.adjustments.addTitle', 'Add adjustment'),\n editTitle: t('sales.documents.adjustments.editTitle', 'Edit adjustment'),\n submitCreate: t('sales.documents.adjustments.submitCreate', 'Add adjustment'),\n submitUpdate: t('sales.documents.adjustments.submitUpdate', 'Save changes'),\n }}\n initialAdjustment={activeAdjustment}\n onSubmit={handleFormSubmit}\n />\n </div>\n )\n}\n"],
5
5
  "mappings": ";AAuZU,SACE,KADF;AAnZV,YAAY,WAAW;AAEvB,SAAS,aAAa;AACtB,SAAS,iBAAiB;AAC1B,SAAS,cAAc,gBAAgB,qBAAqB;AAC5D,SAAS,aAAa;AACtB,SAAS,SAAS,mCAAmC;AACrD,SAAS,iCAAiC;AAC1C,SAAS,YAAY,YAAY,kBAAkB;AACnD,SAAS,2BAA2B;AACpC,SAAS,kCAAkC;AAC3C,SAAS,kBAAkB;AAE3B;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAGP,SAAS,yBAAyB;AAClC,SAAS,wBAA8E;AACvF,SAAS,YAAY;AACrB,SAAS,kCAAkC;AAC3C,SAAS,gCAAgC;AACzC,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,OAAO;AAenC,MAAM,kCAAyD;AAAA,EAC7D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,gBAAgB,OAAgB,WAAW,GAAW;AAC7D,MAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,EAAG,QAAO;AAChE,MAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,QAAQ;AACpD,UAAM,SAAS,OAAO,KAAK;AAC3B,QAAI,CAAC,OAAO,MAAM,MAAM,EAAG,QAAO;AAAA,EACpC;AACA,SAAO;AACT;AAEA,SAAS,cAAc,OAA0C;AAC/D,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,QAAM,YAAY,IAAI,KAAK,aAAa,QAAW,EAAE,OAAO,WAAW,uBAAuB,EAAE,CAAC;AACjG,SAAO,UAAU,OAAO,QAAQ,GAAG;AACrC;AAEO,SAAS,gCAAgC;AAAA,EAC9C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB,UAAU;AAAA,EACV;AAAA,EACA;AACF,GAAyC;AACvC,QAAM,IAAI,KAAK;AACf,QAAM,EAAE,gBAAgB,SAAS,IAAI,2BAA2B;AAChE,QAAM,yBAAyB,gBAAgB,kBAAkB;AACjE,QAAM,mBAAmB,mBAAmB,YAAY;AACxD,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAA0B,CAAC,CAAC;AAC1D,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,KAAK;AAClD,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAwB,IAAI;AAC5D,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,KAAK;AACxD,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,MAAM,SAA+B,IAAI;AACzF,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAA6B,CAAC,CAAC;AAC3E,QAAM,iBAAiB,MAAM,OAAO,KAAK;AAEzC,QAAM,kBAAkB,MAAM;AAAA,IAC5B,MAAO,SAAS,UAAU,iCAAiC;AAAA,IAC3D,CAAC,IAAI;AAAA,EACP;AACA,QAAM,mBAAmB,MAAM;AAAA,IAC7B,MAAO,SAAS,UAAU,4BAA4B;AAAA,IACtD,CAAC,IAAI;AAAA,EACP;AACA,QAAM,cAAc,SAAS,UAAU,YAAY;AACnD,QAAM,sBAAsB,MAAM;AAAA,IAChC,MACE,gCAAgC,IAAI,CAAC,WAAW;AAAA,MAC9C;AAAA,MACA,OAAO,EAAE,0CAA0C,KAAK,IAAI,KAAK;AAAA,MACjE,OAAO;AAAA,MACP,MAAM;AAAA,IACR,EAAE;AAAA,IACJ,CAAC,CAAC;AAAA,EACJ;AACA,QAAM,gBAAgB,MAAM;AAAA,IAC1B,MACE,YAAY,OAAyC,CAAC,KAAK,UAAU;AACnE,UAAI,MAAM,KAAK,IAAI;AACnB,aAAO;AAAA,IACT,GAAG,CAAC,CAAC;AAAA,IACP,CAAC,WAAW;AAAA,EACd;AACA,QAAM,kBAAkB,MAAM,YAAY,YAAyC;AACjF,QAAI,YAAY,OAAQ,QAAO;AAC/B,QAAI,eAAe,QAAS,QAAO,YAAY,SAAS,cAAc;AACtE,mBAAe,UAAU;AACzB,QAAI;AACF,YAAM,SAAS,IAAI,gBAAgB,EAAE,MAAM,KAAK,UAAU,MAAM,CAAC;AACjE,YAAM,WAAW,MAAM;AAAA,QACrB,+BAA+B,OAAO,SAAS,CAAC;AAAA,QAChD;AAAA,QACA,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE;AAAA,MAC5B;AACA,YAAM,QAAQ,MAAM,QAAQ,SAAS,QAAQ,KAAK,IAAI,SAAS,OAAO,QAAQ,CAAC;AAC/E,YAAM,SAAS,MACZ,IAAI,CAAC,SAAS;AACb,cAAM,QAAQ,OAAQ,KAAa,UAAU,WAAY,KAAa,MAAM,KAAK,IAAI;AACrF,YAAI,CAAC,MAAO,QAAO;AACnB,cAAM,QACJ,OAAQ,KAAa,UAAU,YAAa,KAAa,MAAM,KAAK,EAAE,SACjE,KAAa,MAAM,KAAK,IACzB;AACN,cAAM,QACJ,OAAQ,KAAa,UAAU,YAAY,sBAAsB,KAAM,KAAa,KAAK,IACrF,IAAK,KAAa,MAAM,MAAM,CAAC,EAAE,YAAY,CAAC,KAC9C;AACN,cAAM,OACJ,OAAQ,KAAa,SAAS,YAAa,KAAa,KAAK,KAAK,EAAE,SAC/D,KAAa,KAAK,KAAK,IACxB;AACN,eAAO,EAAE,OAAO,OAAO,OAAO,KAAK;AAAA,MACrC,CAAC,EACA,OAAO,CAAC,UAAqC,QAAQ,KAAK,CAAC;AAC9D,YAAM,SAAS,oBAAI,IAA8B;AACjD,0BAAoB,QAAQ,CAAC,UAAU,OAAO,IAAI,MAAM,OAAO,KAAK,CAAC;AACrE,aAAO,QAAQ,CAAC,UAAU,OAAO,IAAI,MAAM,OAAO,KAAK,CAAC;AACxD,YAAM,UAAU,MAAM,KAAK,OAAO,OAAO,CAAC,EAAE;AAAA,QAAK,CAAC,GAAG,MACnD,EAAE,MAAM,cAAc,EAAE,OAAO,QAAW,EAAE,aAAa,OAAO,CAAC;AAAA,MACnE;AACA,qBAAe,OAAO;AACtB,qBAAe,UAAU;AACzB,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,aAAO,MAAM,gCAAgC,EAAE,IAAI,CAAC;AACpD,qBAAe,UAAU;AACzB,qBAAe,mBAAmB;AAClC,aAAO;AAAA,IACT;AAAA,EACF,GAAG,CAAC,qBAAqB,WAAW,CAAC;AAErC,QAAM,UAAU,MAAM;AACpB,oBAAgB,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EAClC,GAAG,CAAC,iBAAiB,wBAAwB,gBAAgB,CAAC;AAE9D,QAAM,kBAAkB,MAAM,YAAY,YAAY;AACpD,eAAW,IAAI;AACf,aAAS,IAAI;AACb,QAAI;AACF,YAAM,SAAS,IAAI,gBAAgB,EAAE,MAAM,KAAK,UAAU,OAAO,CAAC,WAAW,GAAG,WAAW,CAAC;AAC5F,YAAM,WAAW,MAAM;AAAA,QACrB,GAAG,eAAe,IAAI,OAAO,SAAS,CAAC;AAAA,QACvC;AAAA,QACA,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE;AAAA,MAC5B;AACA,YAAM,QAAQ,MAAM,QAAQ,SAAS,QAAQ,KAAK,IAAI,SAAS,OAAO,QAAQ,CAAC;AAC/E,YAAM,SAA0B,MAC7B,IAAI,CAAC,SAAS;AACb,cAAM,KAAK,OAAO,KAAK,OAAO,WAAW,KAAK,KAAK;AACnD,YAAI,CAAC,GAAI,QAAO;AAChB,cAAM,YAAY;AAAA,UACf,KAAa,cAAe,KAAa,aAAc,KAAa;AAAA,UACrE;AAAA,QACF;AACA,cAAM,cAAc;AAAA,UACjB,KAAa,gBAAiB,KAAa,eAAgB,KAAa;AAAA,UACzE;AAAA,QACF;AACA,cAAM,UAAU,gBAAiB,KAAa,MAAM,GAAG;AACvD,cAAM,YACJ,OAAO,KAAK,SAAS,YAAY,KAAK,KAAK,KAAK,EAAE,SAC7C,KAAK,KAAK,KAAK,IAChB;AACN,cAAM,WACJ,OAAQ,KAAa,kBAAkB,WAClC,KAAa,gBACd,OAAQ,KAAa,iBAAiB,WACnC,KAAa,eACd,gBAAgB;AACxB,cAAM,eAAe,yBAAyB,IAA+B;AAC7E,cAAM,mBACJ,OAAQ,KAAa,wBAAwB,WACxC,KAAa,sBACd,OAAQ,KAAa,qBAAqB,WACvC,KAAa,mBACd;AACR,cAAM,WACJ,OAAQ,KAAa,aAAa,YAAa,KAAa,WACtD,KAAa,WACf;AACN,eAAO;AAAA,UACL;AAAA,UACA,OAAO,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ;AAAA,UACrD,MAAM,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO;AAAA,UAClD,MAAM;AAAA,UACN,eACE,OAAQ,KAAa,mBAAmB,WACnC,KAAa,iBACd,OAAQ,KAAa,kBAAkB,WACpC,KAAa,gBACd;AAAA,UACR,MAAM,OAAO,SAAS,OAAO,IAAI,UAAU;AAAA,UAC3C,WAAW,OAAO,SAAS,SAAS,IAAI,YAAY;AAAA,UACpD,aAAa,OAAO,SAAS,WAAW,IAAI,cAAc;AAAA,UAC1D,cAAc;AAAA,UACd,UACE,OAAO,KAAK,aAAa,WACrB,KAAK,WACL,OAAQ,KAAa,aAAa,WAChC,OAAQ,KAAa,QAAQ,IAC7B;AAAA,UACR,cAAc,OAAO,KAAK,YAAY,EAAE,SAAS,eAAe;AAAA,UAChE;AAAA,UACA;AAAA,QACF;AAAA,MACF,CAAC,EACA,OAAO,CAAC,UAAkC,QAAQ,KAAK,CAAC;AAC3D,YAAM,UAAU,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,GAAG,OAAO,EAAE,YAAY,MAAM,EAAE,YAAY,EAAE;AAChF,cAAQ,OAAO;AACf,UAAI,aAAc,cAAa,OAAO;AAAA,IACxC,SAAS,KAAK;AACZ,aAAO,MAAM,mCAAmC,EAAE,IAAI,CAAC;AACvD,eAAS,EAAE,yCAAyC,6BAA6B,CAAC;AAAA,IACpF,UAAE;AACA,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF,GAAG,CAAC,iBAAiB,cAAc,YAAY,aAAa,cAAc,CAAC,CAAC;AAE5E,QAAM,UAAU,MAAM;AACpB,SAAK,gBAAgB;AAAA,EACvB,GAAG,CAAC,eAAe,CAAC;AAEpB,QAAM;AAAA,IACJ,MACE,oCAAoC,CAAC,WAAW;AAC9C,UAAI,OAAO,eAAe,WAAY;AACtC,UAAI,OAAO,QAAQ,OAAO,SAAS,KAAM;AACzC,WAAK,gBAAgB;AAAA,IACvB,CAAC;AAAA,IACH,CAAC,YAAY,MAAM,eAAe;AAAA,EACpC;AAEA,QAAM,mBAAmB,MAAM;AAAA,IAC7B,CAAC,cAAmC;AAClC,YAAM,SAAS,cAAc,SAAS;AACtC,UAAI,OAAQ,QAAO,OAAO;AAC1B,aAAO,EAAE,0CAA0C,SAAS,IAAI,OAAO,cAAc,WAAW,YAAY,OAAO,SAAS,CAAC;AAAA,IAC/H;AAAA,IACA,CAAC,eAAe,CAAC;AAAA,EACnB;AAEA,QAAM,mBAAmB,MAAM,YAAY,MAAM;AAC/C,wBAAoB,IAAI;AACxB,kBAAc,IAAI;AAAA,EACpB,GAAG,CAAC,CAAC;AAEL,QAAM,aAAa,MAAM,YAAY,CAAC,QAAuB;AAC3D,wBAAoB,GAAG;AACvB,kBAAc,IAAI;AAAA,EACpB,GAAG,CAAC,CAAC;AAEL,QAAM,oBAAoB,MAAM,YAAY,MAAM;AAChD,kBAAc,KAAK;AACnB,wBAAoB,IAAI;AAAA,EAC1B,GAAG,CAAC,CAAC;AAEL,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,eAAgB;AACrB,mBAAe;AAAA,MACb,OAAO,EAAE,mCAAmC,gBAAgB;AAAA,MAC5D,SAAS;AAAA,MACT,UAAU;AAAA,IACZ,CAAC;AACD,WAAO,MAAM,eAAe,IAAI;AAAA,EAClC,GAAG,CAAC,kBAAkB,gBAAgB,CAAC,CAAC;AAExC,QAAM,mBAAmB,MAAM;AAAA,IAC7B,OAAO,WAAoC;AACzC,UAAI,CAAC,0BAA0B,CAAC,kBAAkB;AAChD,cAAM;AAAA,UACJ,EAAE,0CAA0C,uCAAuC;AAAA,QACrF;AAAA,MACF;AACA,YAAM,UAAmC;AAAA,QACvC,CAAC,WAAW,GAAG,OAAO,UAAU;AAAA,QAChC,gBAAgB,OAAO,sBAAsB;AAAA,QAC7C,UAAU,OAAO,gBAAgB;AAAA,QACjC,OAAO;AAAA,QACP,MAAM,OAAO,QAAQ;AAAA,QACrB,MAAM,OAAO,QAAQ;AAAA,QACrB,OAAO,OAAO,SAAS;AAAA,QACvB,eAAe,OAAO,iBAAiB;AAAA,QACvC,MAAM,OAAO,SAAS,OAAO,IAAI,IAAI,OAAO,OAAO;AAAA,QACnD,WAAW,OAAO,SAAS,OAAO,SAAS,IAAI,OAAO,YAAY;AAAA,QAClE,aAAa,OAAO,SAAS,OAAO,WAAW,IAAI,OAAO,cAAc;AAAA,QACxE,eAAe,OAAO,gBAAgB,gBAAgB,IAAI,YAAY;AAAA,QACtE,UAAU,OAAO,SAAS,OAAO,QAAQ,IAAI,OAAO,WAAW;AAAA,QAC/D,cAAc,OAAO,gBAAgB;AAAA,QACrC,UAAU,OAAO,YAAY;AAAA,MAC/B;AAEA,YAAM,SAAS,OAAO,KAAK,aAAa;AACxC,UAAI;AACF,cAAM,SAAS,MAAM;AAAA,UACnB,0BAA0B,iBAAiB;AAAA,UAC3C,MACE;AAAA,YACE;AAAA,YACA,OAAO,KAAK,EAAE,IAAI,OAAO,IAAI,GAAG,QAAQ,IAAI;AAAA,YAC5C;AAAA,cACE,gBAAgB,OAAO,KACnB,EAAE,uCAAuC,qBAAqB,IAC9D,EAAE,uCAAuC,mBAAmB;AAAA,cAChE,cAAc,EAAE,yCAAyC,4BAA4B;AAAA,YACvF;AAAA,UACF;AAAA,QACJ;AACA,YAAI,OAAO,IAAI;AACb,gBAAM,gBAAgB;AACtB,yCAA+B,EAAE,YAAY,KAAK,CAAC;AACnD,wBAAc,KAAK;AACnB,8BAAoB,IAAI;AAAA,QAC1B;AAAA,MACF,SAAS,KAAK;AACZ,YACE,2BAA2B,KAAK,GAAG,MAAM,KAAK,gBAAgB,CAAC,GAC/D;AACA,wBAAc,KAAK;AACnB,8BAAoB,IAAI;AACxB;AAAA,QACF;AACA,cAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,eAAe,MAAM;AAAA,IACzB,OAAO,QAAuB;AAC5B,UAAI;AACF,cAAM,SAAS,MAAM;AAAA,UACnB,0BAA0B,iBAAiB;AAAA,UAC3C,MACE,WAAW,kBAAkB;AAAA,YAC3B,MAAM;AAAA,cACJ,IAAI,IAAI;AAAA,cACR,CAAC,WAAW,GAAG;AAAA,cACf,gBAAgB,0BAA0B;AAAA,cAC1C,UAAU,oBAAoB;AAAA,YAChC;AAAA,YACA,cAAc,EAAE,2CAA2C,8BAA8B;AAAA,UAC3F,CAAC;AAAA,QACL;AACA,YAAI,OAAO,IAAI;AACb,gBAAM,gBAAgB;AACtB,yCAA+B,EAAE,YAAY,KAAK,CAAC;AAAA,QACrD;AAAA,MACF,SAAS,KAAK;AACZ,YAAI,2BAA2B,KAAK,GAAG,MAAM,KAAK,gBAAgB,CAAC,EAAG;AACtE,eAAO,MAAM,qCAAqC,EAAE,IAAI,CAAC;AACzD,cAAM,EAAE,2CAA2C,8BAA8B,GAAG,OAAO;AAAA,MAC7F;AAAA,IACF;AAAA,IACA,CAAC,kBAAkB,YAAY,aAAa,mBAAmB,MAAM,iBAAiB,wBAAwB,kBAAkB,CAAC;AAAA,EACnI;AAEA,QAAM,UAAU,MAAM;AAAA,IACpB,MAAM;AAAA,MACJ;AAAA,QACE,IAAI;AAAA,QACJ,QAAQ,EAAE,qCAAqC,OAAO;AAAA,QACtD,MAAM,CAAC,EAAE,IAAI,MACX,qBAAC,SAAI,WAAU,iBACb;AAAA,8BAAC,UAAK,WAAU,+BAA+B,cAAI,SAAS,SAAS,EAAE,wCAAwC,UAAU,GAAE;AAAA,UAC1H,IAAI,SAAS,OAAO,oBAAC,UAAK,WAAU,iCAAiC,cAAI,SAAS,MAAK,IAAU;AAAA,WACpG;AAAA,MAEJ;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,QAAQ,EAAE,yCAAyC,MAAM;AAAA,QACzD,MAAM,CAAC,EAAE,IAAI,MAAM,oBAAC,SAAM,SAAQ,WAAW,2BAAiB,IAAI,SAAS,IAAI,GAAE;AAAA,MACnF;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,QAAQ,EAAE,yCAAyC,YAAY;AAAA,QAC/D,MAAM,CAAC,EAAE,IAAI,MACX,oBAAC,qBAAkB,QAAQ,IAAI,SAAS,WAAW,UAAU,IAAI,SAAS,gBAAgB,cAAc,WAAU,qBAAoB;AAAA,MAE1I;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,QAAQ,EAAE,2CAA2C,cAAc;AAAA,QACnE,MAAM,CAAC,EAAE,IAAI,MACX,oBAAC,qBAAkB,QAAQ,IAAI,SAAS,aAAa,UAAU,IAAI,SAAS,gBAAgB,cAAc,WAAU,qBAAoB;AAAA,MAE5I;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,QAAQ,EAAE,oCAAoC,MAAM;AAAA,QACpD,MAAM,CAAC,EAAE,IAAI,MAAM,oBAAC,UAAK,WAAU,2CAA2C,wBAAc,IAAI,SAAS,IAAI,GAAE;AAAA,MACjH;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,QAAQ,EAAE,wCAAwC,UAAU;AAAA,QAC5D,MAAM,CAAC,EAAE,IAAI,MAAM,oBAAC,UAAK,WAAU,iCAAiC,cAAI,SAAS,UAAS;AAAA,MAC5F;AAAA,IACF;AAAA,IACA,CAAC,cAAc,kBAAkB,CAAC;AAAA,EACpC;AAEA,QAAM,mBAAmB,WAAW,KAAK,WAAW;AAEpD,QAAM,mBAAmB,MAAM;AAAA,IAC7B,CAAC,QACC;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,UACL;AAAA,YACE,IAAI;AAAA,YACJ,OAAO,EAAE,mBAAmB,MAAM;AAAA,YAClC,UAAU,MAAM,WAAW,GAAG;AAAA,UAChC;AAAA,UACA;AAAA,YACE,IAAI;AAAA,YACJ,OAAO,EAAE,qBAAqB,QAAQ;AAAA,YACtC,aAAa;AAAA,YACb,UAAU,MAAM,aAAa,GAAG;AAAA,UAClC;AAAA,QACF;AAAA;AAAA,IACF;AAAA,IAEF,CAAC,cAAc,YAAY,CAAC;AAAA,EAC9B;AAEA,SACE,qBAAC,SAAI,WAAU,aACZ;AAAA,YACC,oBAAC,gBAAa,OAAO,EAAE,yCAAyC,6BAA6B,GAAG,aAAa,OAAO,SAAS,MAAM,KAAK,gBAAgB,GAAG,IACzJ;AAAA,IACH,mBACC;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,uCAAuC,2BAAsB;AAAA,QACtE,WAAU;AAAA;AAAA,IACZ,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN;AAAA,QACA,WAAW,WAAW,KAAK,SAAS;AAAA,QACpC,UAAQ;AAAA,QACR,YAAY;AAAA,QACZ,YAAY;AAAA,QACZ,YACE;AAAA,UAAC;AAAA;AAAA,YACC,OAAO,EAAE,2CAA2C,qBAAqB;AAAA,YACzE,aAAa,EAAE,iDAAiD,iDAAiD;AAAA,YACjH,aAAa,EAAE,mCAAmC,gBAAgB;AAAA,YAClE,UAAU;AAAA;AAAA,QACZ;AAAA;AAAA,IAEJ;AAAA,IAGF;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN,cAAc,CAAC,SAAS;AACtB,cAAI,CAAC,MAAM;AACT,8BAAkB;AAAA,UACpB,OAAO;AACL,0BAAc,IAAI;AAAA,UACpB;AAAA,QACF;AAAA,QACA;AAAA,QACA,cAAc,gBAAgB;AAAA,QAC9B,aAAa,YAAY,SAAS,cAAc;AAAA,QAChD;AAAA,QACA,QAAQ;AAAA,UACN,UAAU,EAAE,wCAAwC,gBAAgB;AAAA,UACpE,WAAW,EAAE,yCAAyC,iBAAiB;AAAA,UACvE,cAAc,EAAE,4CAA4C,gBAAgB;AAAA,UAC5E,cAAc,EAAE,4CAA4C,cAAc;AAAA,QAC5E;AAAA,QACA,mBAAmB;AAAA,QACnB,UAAU;AAAA;AAAA,IACZ;AAAA,KACF;AAEJ;",
6
6
  "names": []
7
7
  }