@open-mercato/core 0.6.6-develop.6229.1.ebe6706732 → 0.6.6-develop.6241.1.47c01ad4ed

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 (125) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/auth/commands/users.js +1 -0
  3. package/dist/modules/auth/commands/users.js.map +2 -2
  4. package/dist/modules/communication_channels/data/enrichers.js +9 -1
  5. package/dist/modules/communication_channels/data/enrichers.js.map +2 -2
  6. package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.js +3 -5
  7. package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.js.map +2 -2
  8. package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.js.map +2 -2
  9. package/dist/modules/customers/api/pipeline-stages/reorder/route.js +39 -1
  10. package/dist/modules/customers/api/pipeline-stages/reorder/route.js.map +2 -2
  11. package/dist/modules/customers/api/pipeline-stages/route.js +107 -12
  12. package/dist/modules/customers/api/pipeline-stages/route.js.map +2 -2
  13. package/dist/modules/customers/api/pipelines/route.js +107 -12
  14. package/dist/modules/customers/api/pipelines/route.js.map +2 -2
  15. package/dist/modules/customers/api/settings/address-format/route.js +33 -1
  16. package/dist/modules/customers/api/settings/address-format/route.js.map +2 -2
  17. package/dist/modules/customers/api/tags/assign/route.js +37 -0
  18. package/dist/modules/customers/api/tags/assign/route.js.map +2 -2
  19. package/dist/modules/customers/api/tags/unassign/route.js +37 -0
  20. package/dist/modules/customers/api/tags/unassign/route.js.map +2 -2
  21. package/dist/modules/entities/api/encryption.js +109 -38
  22. package/dist/modules/entities/api/encryption.js.map +2 -2
  23. package/dist/modules/entities/components/EncryptionManager.js +22 -9
  24. package/dist/modules/entities/components/EncryptionManager.js.map +2 -2
  25. package/dist/modules/integrations/api/[id]/credentials/route.js +23 -1
  26. package/dist/modules/integrations/api/[id]/credentials/route.js.map +2 -2
  27. package/dist/modules/integrations/api/[id]/route.js +10 -3
  28. package/dist/modules/integrations/api/[id]/route.js.map +2 -2
  29. package/dist/modules/integrations/api/[id]/state/route.js +20 -5
  30. package/dist/modules/integrations/api/[id]/state/route.js.map +2 -2
  31. package/dist/modules/integrations/api/[id]/version/route.js +17 -1
  32. package/dist/modules/integrations/api/[id]/version/route.js.map +2 -2
  33. package/dist/modules/integrations/api/route.js +2 -0
  34. package/dist/modules/integrations/api/route.js.map +2 -2
  35. package/dist/modules/integrations/backend/integrations/[id]/page.js +36 -19
  36. package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
  37. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +13 -9
  38. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +2 -2
  39. package/dist/modules/integrations/backend/integrations/page.js +4 -3
  40. package/dist/modules/integrations/backend/integrations/page.js.map +2 -2
  41. package/dist/modules/integrations/data/entities.js +2 -2
  42. package/dist/modules/integrations/data/entities.js.map +2 -2
  43. package/dist/modules/integrations/lib/credentials-service.js +32 -0
  44. package/dist/modules/integrations/lib/credentials-service.js.map +2 -2
  45. package/dist/modules/integrations/lib/state-service.js +4 -2
  46. package/dist/modules/integrations/lib/state-service.js.map +2 -2
  47. package/dist/modules/notifications/lib/notificationService.js +42 -10
  48. package/dist/modules/notifications/lib/notificationService.js.map +2 -2
  49. package/dist/modules/payment_gateways/api/status/route.js +91 -6
  50. package/dist/modules/payment_gateways/api/status/route.js.map +2 -2
  51. package/dist/modules/payment_gateways/backend/payment-gateways/page.js +25 -11
  52. package/dist/modules/payment_gateways/backend/payment-gateways/page.js.map +2 -2
  53. package/dist/modules/sales/backend/sales/channels/offers/page.js +25 -7
  54. package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
  55. package/dist/modules/sales/backend/sales/channels/page.js +25 -7
  56. package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
  57. package/dist/modules/sales/components/AdjustmentKindSettings.js +52 -24
  58. package/dist/modules/sales/components/AdjustmentKindSettings.js.map +2 -2
  59. package/dist/modules/sales/components/DocumentNumberSettings.js +26 -9
  60. package/dist/modules/sales/components/DocumentNumberSettings.js.map +2 -2
  61. package/dist/modules/sales/components/OrderEditingSettings.js +26 -9
  62. package/dist/modules/sales/components/OrderEditingSettings.js.map +2 -2
  63. package/dist/modules/sales/components/StatusSettings.js +66 -31
  64. package/dist/modules/sales/components/StatusSettings.js.map +2 -2
  65. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js +25 -7
  66. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
  67. package/dist/modules/shipping_carriers/api/cancel/route.js +38 -0
  68. package/dist/modules/shipping_carriers/api/cancel/route.js.map +2 -2
  69. package/dist/modules/shipping_carriers/api/shipments/route.js +12 -5
  70. package/dist/modules/shipping_carriers/api/shipments/route.js.map +2 -2
  71. package/dist/modules/staff/lib/timesheets-ui/TimerBar.js +7 -8
  72. package/dist/modules/staff/lib/timesheets-ui/TimerBar.js.map +2 -2
  73. package/dist/modules/staff/lib/timesheets-ui/timerErrors.js +12 -0
  74. package/dist/modules/staff/lib/timesheets-ui/timerErrors.js.map +7 -0
  75. package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.client.js +4 -3
  76. package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.client.js.map +2 -2
  77. package/dist/modules/workflows/backend/definitions/page.js +19 -10
  78. package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
  79. package/package.json +7 -7
  80. package/src/modules/auth/commands/users.ts +1 -0
  81. package/src/modules/communication_channels/data/enrichers.ts +25 -0
  82. package/src/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.tsx +7 -6
  83. package/src/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.ts +5 -2
  84. package/src/modules/customers/api/pipeline-stages/reorder/route.ts +41 -1
  85. package/src/modules/customers/api/pipeline-stages/route.ts +112 -13
  86. package/src/modules/customers/api/pipelines/route.ts +112 -13
  87. package/src/modules/customers/api/settings/address-format/route.ts +36 -1
  88. package/src/modules/customers/api/tags/assign/route.ts +39 -0
  89. package/src/modules/customers/api/tags/unassign/route.ts +39 -0
  90. package/src/modules/customers/i18n/de.json +1 -0
  91. package/src/modules/customers/i18n/en.json +1 -0
  92. package/src/modules/customers/i18n/es.json +1 -0
  93. package/src/modules/customers/i18n/pl.json +1 -0
  94. package/src/modules/entities/api/encryption.ts +122 -39
  95. package/src/modules/entities/components/EncryptionManager.tsx +28 -9
  96. package/src/modules/integrations/api/[id]/credentials/route.ts +23 -0
  97. package/src/modules/integrations/api/[id]/route.ts +8 -1
  98. package/src/modules/integrations/api/[id]/state/route.ts +20 -4
  99. package/src/modules/integrations/api/[id]/version/route.ts +18 -1
  100. package/src/modules/integrations/api/route.ts +3 -0
  101. package/src/modules/integrations/backend/integrations/[id]/page.tsx +39 -20
  102. package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +19 -11
  103. package/src/modules/integrations/backend/integrations/page.tsx +8 -5
  104. package/src/modules/integrations/data/entities.ts +2 -2
  105. package/src/modules/integrations/lib/credentials-service.ts +35 -0
  106. package/src/modules/integrations/lib/state-service.ts +3 -0
  107. package/src/modules/notifications/lib/notificationService.ts +74 -11
  108. package/src/modules/payment_gateways/api/status/route.ts +97 -5
  109. package/src/modules/payment_gateways/backend/payment-gateways/page.tsx +27 -11
  110. package/src/modules/sales/backend/sales/channels/offers/page.tsx +31 -7
  111. package/src/modules/sales/backend/sales/channels/page.tsx +31 -7
  112. package/src/modules/sales/components/AdjustmentKindSettings.tsx +60 -24
  113. package/src/modules/sales/components/DocumentNumberSettings.tsx +32 -10
  114. package/src/modules/sales/components/OrderEditingSettings.tsx +32 -10
  115. package/src/modules/sales/components/StatusSettings.tsx +74 -33
  116. package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +30 -6
  117. package/src/modules/shipping_carriers/api/cancel/route.ts +46 -0
  118. package/src/modules/shipping_carriers/api/shipments/route.ts +21 -6
  119. package/src/modules/staff/i18n/de.json +5 -5
  120. package/src/modules/staff/i18n/es.json +5 -5
  121. package/src/modules/staff/i18n/pl.json +5 -5
  122. package/src/modules/staff/lib/timesheets-ui/TimerBar.tsx +7 -8
  123. package/src/modules/staff/lib/timesheets-ui/timerErrors.ts +23 -0
  124. package/src/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.client.tsx +4 -3
  125. package/src/modules/workflows/backend/definitions/page.tsx +19 -10
@@ -17,6 +17,7 @@ import { Label } from "@open-mercato/ui/primitives/label";
17
17
  import { flash } from "@open-mercato/ui/backend/FlashMessages";
18
18
  import { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from "@open-mercato/ui/backend/utils/apiCall";
19
19
  import { buildOptimisticLockHeader } from "@open-mercato/ui/backend/utils/optimisticLock";
20
+ import { useGuardedMutation } from "@open-mercato/ui/backend/injection/useGuardedMutation";
20
21
  import { raiseCrudError } from "@open-mercato/ui/backend/utils/serverErrors";
21
22
  import { useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
22
23
  import { AppearanceSelector } from "@open-mercato/core/modules/dictionaries/components/AppearanceSelector";
@@ -48,10 +49,23 @@ const normalizeEntry = (raw) => {
48
49
  updatedAt: typeof raw.updatedAt === "string" ? raw.updatedAt : null
49
50
  };
50
51
  };
52
+ const SAVE_CONTEXT_ID = "sales-adjustment-kind-settings";
51
53
  function AdjustmentKindSettings() {
52
54
  const t = useT();
53
55
  const scopeVersion = useOrganizationScopeVersion();
54
56
  const { confirm, ConfirmDialogElement } = useConfirmDialog();
57
+ const { runMutation, retryLastMutation } = useGuardedMutation({
58
+ contextId: SAVE_CONTEXT_ID,
59
+ blockedMessage: t("ui.forms.flash.saveBlocked", "Save blocked by validation")
60
+ });
61
+ const mutationContext = React.useMemo(
62
+ () => ({
63
+ formId: SAVE_CONTEXT_ID,
64
+ resourceKind: "sales.adjustmentKinds",
65
+ retryLastMutation
66
+ }),
67
+ [retryLastMutation]
68
+ );
55
69
  const [items, setItems] = React.useState([]);
56
70
  const [search, setSearch] = React.useState("");
57
71
  const [loading, setLoading] = React.useState(false);
@@ -180,17 +194,24 @@ function AdjustmentKindSettings() {
180
194
  const method = dialog.mode === "create" ? "POST" : "PUT";
181
195
  const body = dialog.mode === "create" ? JSON.stringify(payload) : JSON.stringify({ id: dialog.entry.id, ...payload });
182
196
  const lockHeader = dialog.mode === "edit" ? buildOptimisticLockHeader(dialog.entry.updatedAt) : {};
183
- const call = await withScopedApiRequestHeaders(
184
- lockHeader,
185
- () => apiCall("/api/sales/adjustment-kinds", {
186
- method,
187
- headers: { "content-type": "application/json" },
188
- body
189
- })
190
- );
191
- if (!call.ok) {
192
- await raiseCrudError(call.response, labels.saveError);
193
- }
197
+ await runMutation({
198
+ operation: async () => {
199
+ const call = await withScopedApiRequestHeaders(
200
+ lockHeader,
201
+ () => apiCall("/api/sales/adjustment-kinds", {
202
+ method,
203
+ headers: { "content-type": "application/json" },
204
+ body
205
+ })
206
+ );
207
+ if (!call.ok) {
208
+ await raiseCrudError(call.response, labels.saveError);
209
+ }
210
+ return call;
211
+ },
212
+ context: mutationContext,
213
+ mutationPayload: dialog.mode === "create" ? { action: "create", ...payload } : { action: "update", id: dialog.entry.id, ...payload }
214
+ });
194
215
  flash(dialog.mode === "create" ? labels.created : labels.updated, "success");
195
216
  closeDialog();
196
217
  await loadItems();
@@ -201,7 +222,7 @@ function AdjustmentKindSettings() {
201
222
  } finally {
202
223
  setSubmitting(false);
203
224
  }
204
- }, [closeDialog, dialog, form.color, form.icon, form.label, form.value, labels, loadItems]);
225
+ }, [closeDialog, dialog, form.color, form.icon, form.label, form.value, labels, loadItems, mutationContext, runMutation]);
205
226
  const handleDelete = React.useCallback(
206
227
  async (entry) => {
207
228
  const message = labels.deleteConfirm.replace("{{code}}", entry.label || entry.value);
@@ -211,17 +232,24 @@ function AdjustmentKindSettings() {
211
232
  });
212
233
  if (!confirmed) return;
213
234
  try {
214
- const call = await withScopedApiRequestHeaders(
215
- buildOptimisticLockHeader(entry.updatedAt),
216
- () => apiCall("/api/sales/adjustment-kinds", {
217
- method: "DELETE",
218
- headers: { "content-type": "application/json" },
219
- body: JSON.stringify({ id: entry.id })
220
- })
221
- );
222
- if (!call.ok) {
223
- await raiseCrudError(call.response, labels.deleteError);
224
- }
235
+ await runMutation({
236
+ operation: async () => {
237
+ const call = await withScopedApiRequestHeaders(
238
+ buildOptimisticLockHeader(entry.updatedAt),
239
+ () => apiCall("/api/sales/adjustment-kinds", {
240
+ method: "DELETE",
241
+ headers: { "content-type": "application/json" },
242
+ body: JSON.stringify({ id: entry.id })
243
+ })
244
+ );
245
+ if (!call.ok) {
246
+ await raiseCrudError(call.response, labels.deleteError);
247
+ }
248
+ return call;
249
+ },
250
+ context: mutationContext,
251
+ mutationPayload: { action: "delete", id: entry.id }
252
+ });
225
253
  flash(labels.deleted, "success");
226
254
  await loadItems();
227
255
  } catch (err) {
@@ -230,7 +258,7 @@ function AdjustmentKindSettings() {
230
258
  flash(message2, "error");
231
259
  }
232
260
  },
233
- [confirm, labels, loadItems]
261
+ [confirm, labels, loadItems, mutationContext, runMutation]
234
262
  );
235
263
  const formKeyHandler = React.useCallback(
236
264
  (event) => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/sales/components/AdjustmentKindSettings.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Label } from '@open-mercato/ui/primitives/label'\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 { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { AppearanceSelector } from '@open-mercato/core/modules/dictionaries/components/AppearanceSelector'\nimport { renderDictionaryColor, renderDictionaryIcon } from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype AdjustmentKind = {\n id: string\n value: string\n label: string\n color: string | null\n icon: string | null\n createdAt: string | null\n updatedAt: string | null\n}\n\ntype DialogState =\n | { mode: 'create' }\n | { mode: 'edit'; entry: AdjustmentKind }\n\ntype FormState = {\n value: string\n label: string\n color: string | null\n icon: string | null\n}\n\nconst DEFAULT_FORM: FormState = {\n value: '',\n label: '',\n color: null,\n icon: null,\n}\n\nconst PAGE_SIZE = 100\n\nconst normalizeEntry = (raw: any): AdjustmentKind | null => {\n if (!raw || typeof raw !== 'object') return null\n const value = typeof raw.value === 'string' ? raw.value.trim() : ''\n const id = typeof raw.id === 'string' ? raw.id : ''\n if (!value || !id) return null\n const label = typeof raw.label === 'string' && raw.label.trim().length ? raw.label.trim() : value\n const color =\n typeof raw.color === 'string' && /^#([0-9a-fA-F]{6})$/.test(raw.color) ? `#${raw.color.slice(1).toLowerCase()}` : null\n const icon = typeof raw.icon === 'string' && raw.icon.trim().length ? raw.icon.trim() : null\n return {\n id,\n value,\n label,\n color,\n icon,\n createdAt: typeof raw.createdAt === 'string' ? raw.createdAt : null,\n updatedAt: typeof raw.updatedAt === 'string' ? raw.updatedAt : null,\n }\n}\n\nexport function AdjustmentKindSettings() {\n const t = useT()\n const scopeVersion = useOrganizationScopeVersion()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const [items, setItems] = React.useState<AdjustmentKind[]>([])\n const [search, setSearch] = React.useState('')\n const [loading, setLoading] = React.useState(false)\n const [dialog, setDialog] = React.useState<DialogState | null>(null)\n const [form, setForm] = React.useState<FormState>(DEFAULT_FORM)\n const [submitting, setSubmitting] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n\n const labels = React.useMemo(\n () => ({\n title: t('sales.config.adjustmentKinds.title', 'Adjustment kinds'),\n description: t(\n 'sales.config.adjustmentKinds.description',\n 'Manage reusable adjustment kinds for taxes, surcharges, shipping, and discounts.'\n ),\n add: t('sales.config.adjustmentKinds.actions.add', 'Add adjustment kind'),\n refresh: t('sales.config.adjustmentKinds.actions.refresh', 'Refresh'),\n edit: t('sales.config.adjustmentKinds.actions.edit', 'Edit'),\n delete: t('sales.config.adjustmentKinds.actions.delete', 'Delete'),\n deleteConfirm: t('sales.config.adjustmentKinds.confirm.delete', 'Delete adjustment kind \"{{code}}\"?'),\n createTitle: t('sales.config.adjustmentKinds.dialog.createTitle', 'Create adjustment kind'),\n editTitle: t('sales.config.adjustmentKinds.dialog.editTitle', 'Edit adjustment kind'),\n createDescription: t(\n 'sales.config.adjustmentKinds.dialog.createDescription',\n 'Define a reusable adjustment kind shown in document adjustment dialogs.'\n ),\n editDescription: t(\n 'sales.config.adjustmentKinds.dialog.editDescription',\n 'Update the code, label, or appearance for this adjustment kind.'\n ),\n codeLabel: t('sales.config.adjustmentKinds.form.codeLabel', 'Code'),\n codePlaceholder: t('sales.config.adjustmentKinds.form.codePlaceholder', 'e.g. discount'),\n labelLabel: t('sales.config.adjustmentKinds.form.labelLabel', 'Label'),\n labelPlaceholder: t('sales.config.adjustmentKinds.form.labelPlaceholder', 'e.g. Discount'),\n appearanceLabel: t('sales.config.adjustmentKinds.form.appearanceLabel', 'Appearance'),\n saveCreate: t('sales.config.adjustmentKinds.actions.create', 'Create'),\n saveEdit: t('sales.config.adjustmentKinds.actions.save', 'Save changes'),\n cancel: t('ui.actions.cancel', 'Cancel'),\n required: t('sales.config.adjustmentKinds.errors.required', 'Code is required.'),\n loadError: t('sales.config.adjustmentKinds.errors.load', 'Failed to load adjustment kinds.'),\n saveError: t('sales.config.adjustmentKinds.errors.save', 'Failed to save adjustment kind.'),\n deleteError: t('sales.config.adjustmentKinds.errors.delete', 'Failed to delete adjustment kind.'),\n created: t('sales.config.adjustmentKinds.messages.created', 'Adjustment kind created.'),\n updated: t('sales.config.adjustmentKinds.messages.updated', 'Adjustment kind updated.'),\n deleted: t('sales.config.adjustmentKinds.messages.deleted', 'Adjustment kind deleted.'),\n empty: t('sales.config.adjustmentKinds.table.empty', 'No adjustment kinds yet.'),\n searchPlaceholder: t('sales.config.adjustmentKinds.search.placeholder', 'Search by code or label\u2026'),\n columns: {\n code: t('sales.config.adjustmentKinds.table.code', 'Code'),\n label: t('sales.config.adjustmentKinds.table.label', 'Label'),\n appearance: t('sales.config.adjustmentKinds.table.appearance', 'Appearance'),\n },\n appearanceLabels: {\n colorLabel: t('dictionaries.form.colorLabel', 'Color'),\n colorHelp: t('dictionaries.form.colorHelp', 'Pick a highlight color.'),\n colorClearLabel: t('dictionaries.form.colorClear', 'Remove color'),\n iconLabel: t('dictionaries.form.iconLabel', 'Icon or emoji'),\n iconPlaceholder: t('dictionaries.form.iconPlaceholder', 'Type an emoji or icon token.'),\n iconPickerTriggerLabel: t('dictionaries.form.iconPickerTriggerLabel', 'Browse icons and emoji'),\n iconSearchPlaceholder: t('dictionaries.form.iconSearchPlaceholder', 'Search icons or emojis\u2026'),\n iconSearchEmptyLabel: t('dictionaries.form.iconSearchEmptyLabel', 'No icons match your search.'),\n iconSuggestionsLabel: t('dictionaries.form.iconSuggestionsLabel', 'Suggestions'),\n iconClearLabel: t('dictionaries.form.iconClearLabel', 'Remove icon'),\n previewEmptyLabel: t('dictionaries.form.previewEmptyLabel', 'No appearance selected'),\n },\n }),\n [t]\n )\n\n const loadItems = React.useCallback(async () => {\n setLoading(true)\n try {\n const payload = await readApiResultOrThrow<{ items?: any[] }>(\n `/api/sales/adjustment-kinds?pageSize=${PAGE_SIZE}`,\n undefined,\n { errorMessage: labels.loadError }\n )\n const mapped = Array.isArray(payload.items)\n ? payload.items\n .map((entry) => normalizeEntry(entry))\n .filter((entry): entry is AdjustmentKind => Boolean(entry))\n : []\n setItems(mapped)\n } catch (err) {\n console.error('sales.adjustment-kinds.list failed', err)\n flash(labels.loadError, 'error')\n } finally {\n setLoading(false)\n }\n }, [labels.loadError])\n\n React.useEffect(() => {\n loadItems().catch(() => {})\n }, [loadItems, scopeVersion])\n\n const openDialog = React.useCallback((state: DialogState) => {\n if (state.mode === 'edit') {\n setForm({\n value: state.entry.value,\n label: state.entry.label,\n color: state.entry.color,\n icon: state.entry.icon,\n })\n } else {\n setForm(DEFAULT_FORM)\n }\n setError(null)\n setDialog(state)\n }, [])\n\n const closeDialog = React.useCallback(() => {\n setDialog(null)\n setSubmitting(false)\n setError(null)\n setForm(DEFAULT_FORM)\n }, [])\n\n const handleSubmit = React.useCallback(async () => {\n if (!dialog) return\n const trimmedValue = form.value.trim()\n const trimmedLabel = form.label.trim()\n if (!trimmedValue) {\n setError(labels.required)\n return\n }\n setSubmitting(true)\n setError(null)\n try {\n const payload: Record<string, unknown> = {\n value: trimmedValue,\n }\n if (trimmedLabel) payload.label = trimmedLabel\n if (form.color !== undefined) payload.color = form.color\n if (form.icon !== undefined) payload.icon = form.icon\n\n const method = dialog.mode === 'create' ? 'POST' : 'PUT'\n const body =\n dialog.mode === 'create'\n ? JSON.stringify(payload)\n : JSON.stringify({ id: dialog.entry.id, ...payload })\n\n const lockHeader =\n dialog.mode === 'edit' ? buildOptimisticLockHeader(dialog.entry.updatedAt) : {}\n const call = await withScopedApiRequestHeaders(lockHeader, () =>\n apiCall('/api/sales/adjustment-kinds', {\n method,\n headers: { 'content-type': 'application/json' },\n body,\n })\n )\n if (!call.ok) {\n await raiseCrudError(call.response, labels.saveError)\n }\n flash(dialog.mode === 'create' ? labels.created : labels.updated, 'success')\n closeDialog()\n await loadItems()\n } catch (err) {\n console.error('sales.adjustment-kinds.save failed', err)\n const message = err instanceof Error ? err.message : labels.saveError\n setError(message)\n } finally {\n setSubmitting(false)\n }\n }, [closeDialog, dialog, form.color, form.icon, form.label, form.value, labels, loadItems])\n\n const handleDelete = React.useCallback(\n async (entry: AdjustmentKind) => {\n const message = labels.deleteConfirm.replace('{{code}}', entry.label || entry.value)\n const confirmed = await confirm({\n title: message,\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n const call = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(entry.updatedAt),\n () =>\n apiCall('/api/sales/adjustment-kinds', {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: entry.id }),\n })\n )\n if (!call.ok) {\n await raiseCrudError(call.response, labels.deleteError)\n }\n flash(labels.deleted, 'success')\n await loadItems()\n } catch (err) {\n console.error('sales.adjustment-kinds.delete failed', err)\n const message = err instanceof Error ? err.message : labels.deleteError\n flash(message, 'error')\n }\n },\n [confirm, labels, loadItems]\n )\n\n const formKeyHandler = React.useCallback(\n (event: React.KeyboardEvent<HTMLFormElement>) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n void handleSubmit()\n }\n },\n [handleSubmit]\n )\n\n const columns = React.useMemo<ColumnDef<AdjustmentKind>[]>(() => [\n {\n accessorKey: 'value',\n header: labels.columns.code,\n cell: ({ row }) => <span className=\"font-mono uppercase\">{row.original.value}</span>,\n },\n {\n accessorKey: 'label',\n header: labels.columns.label,\n cell: ({ row }) => <span className=\"font-medium\">{row.original.label}</span>,\n },\n {\n id: 'appearance',\n header: labels.columns.appearance,\n cell: ({ row }) => (\n <div className=\"flex items-center gap-2\">\n {renderDictionaryIcon(row.original.icon, 'h-4 w-4')}\n {renderDictionaryColor(row.original.color, 'h-4 w-4 rounded-full')}\n </div>\n ),\n },\n ], [labels.columns.appearance, labels.columns.code, labels.columns.label])\n\n const filteredItems = React.useMemo(() => {\n const term = search.trim().toLowerCase()\n if (!term) return items\n return items.filter((item) => item.value.toLowerCase().includes(term) || item.label.toLowerCase().includes(term))\n }, [items, search])\n\n const onRowClick = React.useCallback((entry: AdjustmentKind) => {\n openDialog({ mode: 'edit', entry })\n }, [openDialog])\n\n return (\n <section className=\"border bg-card text-card-foreground shadow-sm\" id=\"adjustment-kinds\">\n <div className=\"border-b px-6 py-4 space-y-1\">\n <h2 className=\"text-lg font-semibold\">{labels.title}</h2>\n <p className=\"text-sm text-muted-foreground\">{labels.description}</p>\n </div>\n <div className=\"px-2 py-4 sm:px-4\">\n <DataTable<AdjustmentKind>\n data={filteredItems}\n columns={columns}\n embedded\n isLoading={loading}\n searchValue={search}\n onSearchChange={setSearch}\n searchPlaceholder={labels.searchPlaceholder}\n emptyState={<p className=\"py-8 text-center text-sm text-muted-foreground\">{labels.empty}</p>}\n actions={(\n <Button size=\"sm\" onClick={() => openDialog({ mode: 'create' })}>\n {labels.add}\n </Button>\n )}\n refreshButton={{\n label: labels.refresh,\n onRefresh: () => { void loadItems() },\n isRefreshing: loading,\n }}\n rowActions={(entry) => (\n <RowActions\n items={[\n { id: 'edit', label: labels.edit, onSelect: () => openDialog({ mode: 'edit', entry }) },\n { id: 'delete', label: labels.delete, destructive: true, onSelect: () => { void handleDelete(entry) } },\n ]}\n />\n )}\n onRowClick={onRowClick}\n />\n </div>\n <Dialog open={dialog !== null} onOpenChange={(open) => { if (!open) closeDialog() }}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>\n {dialog?.mode === 'edit' ? labels.editTitle : labels.createTitle}\n </DialogTitle>\n <DialogDescription>\n {dialog?.mode === 'edit' ? labels.editDescription : labels.createDescription}\n </DialogDescription>\n </DialogHeader>\n <form\n className=\"space-y-4\"\n onKeyDown={formKeyHandler}\n onSubmit={(event) => {\n event.preventDefault()\n void handleSubmit()\n }}\n >\n <div className=\"space-y-2\">\n <Label htmlFor=\"adjustment-kind-code\">{labels.codeLabel}</Label>\n <Input\n id=\"adjustment-kind-code\"\n value={form.value}\n onChange={(event) => setForm((prev) => ({ ...prev, value: event.target.value }))}\n placeholder={labels.codePlaceholder}\n className=\"font-mono uppercase\"\n disabled={dialog?.mode === 'edit'}\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"adjustment-kind-label\">{labels.labelLabel}</Label>\n <Input\n id=\"adjustment-kind-label\"\n value={form.label}\n onChange={(event) => setForm((prev) => ({ ...prev, label: event.target.value }))}\n placeholder={labels.labelPlaceholder}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>{labels.appearanceLabel}</Label>\n <AppearanceSelector\n color={form.color}\n icon={form.icon}\n onColorChange={(next) => setForm((prev) => ({ ...prev, color: next }))}\n onIconChange={(next) => setForm((prev) => ({ ...prev, icon: next }))}\n labels={labels.appearanceLabels}\n />\n </div>\n {error ? <p className=\"text-sm text-destructive\">{error}</p> : null}\n </form>\n <DialogFooter>\n <Button variant=\"ghost\" onClick={closeDialog}>\n {labels.cancel}\n </Button>\n <Button onClick={() => void handleSubmit()} disabled={submitting}>\n {dialog?.mode === 'edit' ? labels.saveEdit : labels.saveCreate}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n {ConfirmDialogElement}\n </section>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Label } from '@open-mercato/ui/primitives/label'\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 { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { AppearanceSelector } from '@open-mercato/core/modules/dictionaries/components/AppearanceSelector'\nimport { renderDictionaryColor, renderDictionaryIcon } from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype AdjustmentKind = {\n id: string\n value: string\n label: string\n color: string | null\n icon: string | null\n createdAt: string | null\n updatedAt: string | null\n}\n\ntype DialogState =\n | { mode: 'create' }\n | { mode: 'edit'; entry: AdjustmentKind }\n\ntype FormState = {\n value: string\n label: string\n color: string | null\n icon: string | null\n}\n\nconst DEFAULT_FORM: FormState = {\n value: '',\n label: '',\n color: null,\n icon: null,\n}\n\nconst PAGE_SIZE = 100\n\nconst normalizeEntry = (raw: any): AdjustmentKind | null => {\n if (!raw || typeof raw !== 'object') return null\n const value = typeof raw.value === 'string' ? raw.value.trim() : ''\n const id = typeof raw.id === 'string' ? raw.id : ''\n if (!value || !id) return null\n const label = typeof raw.label === 'string' && raw.label.trim().length ? raw.label.trim() : value\n const color =\n typeof raw.color === 'string' && /^#([0-9a-fA-F]{6})$/.test(raw.color) ? `#${raw.color.slice(1).toLowerCase()}` : null\n const icon = typeof raw.icon === 'string' && raw.icon.trim().length ? raw.icon.trim() : null\n return {\n id,\n value,\n label,\n color,\n icon,\n createdAt: typeof raw.createdAt === 'string' ? raw.createdAt : null,\n updatedAt: typeof raw.updatedAt === 'string' ? raw.updatedAt : null,\n }\n}\n\nconst SAVE_CONTEXT_ID = 'sales-adjustment-kind-settings'\n\nexport function AdjustmentKindSettings() {\n const t = useT()\n const scopeVersion = useOrganizationScopeVersion()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\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.adjustmentKinds',\n retryLastMutation,\n }),\n [retryLastMutation],\n )\n const [items, setItems] = React.useState<AdjustmentKind[]>([])\n const [search, setSearch] = React.useState('')\n const [loading, setLoading] = React.useState(false)\n const [dialog, setDialog] = React.useState<DialogState | null>(null)\n const [form, setForm] = React.useState<FormState>(DEFAULT_FORM)\n const [submitting, setSubmitting] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n\n const labels = React.useMemo(\n () => ({\n title: t('sales.config.adjustmentKinds.title', 'Adjustment kinds'),\n description: t(\n 'sales.config.adjustmentKinds.description',\n 'Manage reusable adjustment kinds for taxes, surcharges, shipping, and discounts.'\n ),\n add: t('sales.config.adjustmentKinds.actions.add', 'Add adjustment kind'),\n refresh: t('sales.config.adjustmentKinds.actions.refresh', 'Refresh'),\n edit: t('sales.config.adjustmentKinds.actions.edit', 'Edit'),\n delete: t('sales.config.adjustmentKinds.actions.delete', 'Delete'),\n deleteConfirm: t('sales.config.adjustmentKinds.confirm.delete', 'Delete adjustment kind \"{{code}}\"?'),\n createTitle: t('sales.config.adjustmentKinds.dialog.createTitle', 'Create adjustment kind'),\n editTitle: t('sales.config.adjustmentKinds.dialog.editTitle', 'Edit adjustment kind'),\n createDescription: t(\n 'sales.config.adjustmentKinds.dialog.createDescription',\n 'Define a reusable adjustment kind shown in document adjustment dialogs.'\n ),\n editDescription: t(\n 'sales.config.adjustmentKinds.dialog.editDescription',\n 'Update the code, label, or appearance for this adjustment kind.'\n ),\n codeLabel: t('sales.config.adjustmentKinds.form.codeLabel', 'Code'),\n codePlaceholder: t('sales.config.adjustmentKinds.form.codePlaceholder', 'e.g. discount'),\n labelLabel: t('sales.config.adjustmentKinds.form.labelLabel', 'Label'),\n labelPlaceholder: t('sales.config.adjustmentKinds.form.labelPlaceholder', 'e.g. Discount'),\n appearanceLabel: t('sales.config.adjustmentKinds.form.appearanceLabel', 'Appearance'),\n saveCreate: t('sales.config.adjustmentKinds.actions.create', 'Create'),\n saveEdit: t('sales.config.adjustmentKinds.actions.save', 'Save changes'),\n cancel: t('ui.actions.cancel', 'Cancel'),\n required: t('sales.config.adjustmentKinds.errors.required', 'Code is required.'),\n loadError: t('sales.config.adjustmentKinds.errors.load', 'Failed to load adjustment kinds.'),\n saveError: t('sales.config.adjustmentKinds.errors.save', 'Failed to save adjustment kind.'),\n deleteError: t('sales.config.adjustmentKinds.errors.delete', 'Failed to delete adjustment kind.'),\n created: t('sales.config.adjustmentKinds.messages.created', 'Adjustment kind created.'),\n updated: t('sales.config.adjustmentKinds.messages.updated', 'Adjustment kind updated.'),\n deleted: t('sales.config.adjustmentKinds.messages.deleted', 'Adjustment kind deleted.'),\n empty: t('sales.config.adjustmentKinds.table.empty', 'No adjustment kinds yet.'),\n searchPlaceholder: t('sales.config.adjustmentKinds.search.placeholder', 'Search by code or label\u2026'),\n columns: {\n code: t('sales.config.adjustmentKinds.table.code', 'Code'),\n label: t('sales.config.adjustmentKinds.table.label', 'Label'),\n appearance: t('sales.config.adjustmentKinds.table.appearance', 'Appearance'),\n },\n appearanceLabels: {\n colorLabel: t('dictionaries.form.colorLabel', 'Color'),\n colorHelp: t('dictionaries.form.colorHelp', 'Pick a highlight color.'),\n colorClearLabel: t('dictionaries.form.colorClear', 'Remove color'),\n iconLabel: t('dictionaries.form.iconLabel', 'Icon or emoji'),\n iconPlaceholder: t('dictionaries.form.iconPlaceholder', 'Type an emoji or icon token.'),\n iconPickerTriggerLabel: t('dictionaries.form.iconPickerTriggerLabel', 'Browse icons and emoji'),\n iconSearchPlaceholder: t('dictionaries.form.iconSearchPlaceholder', 'Search icons or emojis\u2026'),\n iconSearchEmptyLabel: t('dictionaries.form.iconSearchEmptyLabel', 'No icons match your search.'),\n iconSuggestionsLabel: t('dictionaries.form.iconSuggestionsLabel', 'Suggestions'),\n iconClearLabel: t('dictionaries.form.iconClearLabel', 'Remove icon'),\n previewEmptyLabel: t('dictionaries.form.previewEmptyLabel', 'No appearance selected'),\n },\n }),\n [t]\n )\n\n const loadItems = React.useCallback(async () => {\n setLoading(true)\n try {\n const payload = await readApiResultOrThrow<{ items?: any[] }>(\n `/api/sales/adjustment-kinds?pageSize=${PAGE_SIZE}`,\n undefined,\n { errorMessage: labels.loadError }\n )\n const mapped = Array.isArray(payload.items)\n ? payload.items\n .map((entry) => normalizeEntry(entry))\n .filter((entry): entry is AdjustmentKind => Boolean(entry))\n : []\n setItems(mapped)\n } catch (err) {\n console.error('sales.adjustment-kinds.list failed', err)\n flash(labels.loadError, 'error')\n } finally {\n setLoading(false)\n }\n }, [labels.loadError])\n\n React.useEffect(() => {\n loadItems().catch(() => {})\n }, [loadItems, scopeVersion])\n\n const openDialog = React.useCallback((state: DialogState) => {\n if (state.mode === 'edit') {\n setForm({\n value: state.entry.value,\n label: state.entry.label,\n color: state.entry.color,\n icon: state.entry.icon,\n })\n } else {\n setForm(DEFAULT_FORM)\n }\n setError(null)\n setDialog(state)\n }, [])\n\n const closeDialog = React.useCallback(() => {\n setDialog(null)\n setSubmitting(false)\n setError(null)\n setForm(DEFAULT_FORM)\n }, [])\n\n const handleSubmit = React.useCallback(async () => {\n if (!dialog) return\n const trimmedValue = form.value.trim()\n const trimmedLabel = form.label.trim()\n if (!trimmedValue) {\n setError(labels.required)\n return\n }\n setSubmitting(true)\n setError(null)\n try {\n const payload: Record<string, unknown> = {\n value: trimmedValue,\n }\n if (trimmedLabel) payload.label = trimmedLabel\n if (form.color !== undefined) payload.color = form.color\n if (form.icon !== undefined) payload.icon = form.icon\n\n const method = dialog.mode === 'create' ? 'POST' : 'PUT'\n const body =\n dialog.mode === 'create'\n ? JSON.stringify(payload)\n : JSON.stringify({ id: dialog.entry.id, ...payload })\n\n const lockHeader =\n dialog.mode === 'edit' ? buildOptimisticLockHeader(dialog.entry.updatedAt) : {}\n await runMutation({\n operation: async () => {\n const call = await withScopedApiRequestHeaders(lockHeader, () =>\n apiCall('/api/sales/adjustment-kinds', {\n method,\n headers: { 'content-type': 'application/json' },\n body,\n })\n )\n if (!call.ok) {\n await raiseCrudError(call.response, labels.saveError)\n }\n return call\n },\n context: mutationContext,\n mutationPayload:\n dialog.mode === 'create'\n ? { action: 'create', ...payload }\n : { action: 'update', id: dialog.entry.id, ...payload },\n })\n flash(dialog.mode === 'create' ? labels.created : labels.updated, 'success')\n closeDialog()\n await loadItems()\n } catch (err) {\n console.error('sales.adjustment-kinds.save failed', err)\n const message = err instanceof Error ? err.message : labels.saveError\n setError(message)\n } finally {\n setSubmitting(false)\n }\n }, [closeDialog, dialog, form.color, form.icon, form.label, form.value, labels, loadItems, mutationContext, runMutation])\n\n const handleDelete = React.useCallback(\n async (entry: AdjustmentKind) => {\n const message = labels.deleteConfirm.replace('{{code}}', entry.label || entry.value)\n const confirmed = await confirm({\n title: message,\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n await runMutation({\n operation: async () => {\n const call = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(entry.updatedAt),\n () =>\n apiCall('/api/sales/adjustment-kinds', {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: entry.id }),\n })\n )\n if (!call.ok) {\n await raiseCrudError(call.response, labels.deleteError)\n }\n return call\n },\n context: mutationContext,\n mutationPayload: { action: 'delete', id: entry.id },\n })\n flash(labels.deleted, 'success')\n await loadItems()\n } catch (err) {\n console.error('sales.adjustment-kinds.delete failed', err)\n const message = err instanceof Error ? err.message : labels.deleteError\n flash(message, 'error')\n }\n },\n [confirm, labels, loadItems, mutationContext, runMutation]\n )\n\n const formKeyHandler = React.useCallback(\n (event: React.KeyboardEvent<HTMLFormElement>) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n void handleSubmit()\n }\n },\n [handleSubmit]\n )\n\n const columns = React.useMemo<ColumnDef<AdjustmentKind>[]>(() => [\n {\n accessorKey: 'value',\n header: labels.columns.code,\n cell: ({ row }) => <span className=\"font-mono uppercase\">{row.original.value}</span>,\n },\n {\n accessorKey: 'label',\n header: labels.columns.label,\n cell: ({ row }) => <span className=\"font-medium\">{row.original.label}</span>,\n },\n {\n id: 'appearance',\n header: labels.columns.appearance,\n cell: ({ row }) => (\n <div className=\"flex items-center gap-2\">\n {renderDictionaryIcon(row.original.icon, 'h-4 w-4')}\n {renderDictionaryColor(row.original.color, 'h-4 w-4 rounded-full')}\n </div>\n ),\n },\n ], [labels.columns.appearance, labels.columns.code, labels.columns.label])\n\n const filteredItems = React.useMemo(() => {\n const term = search.trim().toLowerCase()\n if (!term) return items\n return items.filter((item) => item.value.toLowerCase().includes(term) || item.label.toLowerCase().includes(term))\n }, [items, search])\n\n const onRowClick = React.useCallback((entry: AdjustmentKind) => {\n openDialog({ mode: 'edit', entry })\n }, [openDialog])\n\n return (\n <section className=\"border bg-card text-card-foreground shadow-sm\" id=\"adjustment-kinds\">\n <div className=\"border-b px-6 py-4 space-y-1\">\n <h2 className=\"text-lg font-semibold\">{labels.title}</h2>\n <p className=\"text-sm text-muted-foreground\">{labels.description}</p>\n </div>\n <div className=\"px-2 py-4 sm:px-4\">\n <DataTable<AdjustmentKind>\n data={filteredItems}\n columns={columns}\n embedded\n isLoading={loading}\n searchValue={search}\n onSearchChange={setSearch}\n searchPlaceholder={labels.searchPlaceholder}\n emptyState={<p className=\"py-8 text-center text-sm text-muted-foreground\">{labels.empty}</p>}\n actions={(\n <Button size=\"sm\" onClick={() => openDialog({ mode: 'create' })}>\n {labels.add}\n </Button>\n )}\n refreshButton={{\n label: labels.refresh,\n onRefresh: () => { void loadItems() },\n isRefreshing: loading,\n }}\n rowActions={(entry) => (\n <RowActions\n items={[\n { id: 'edit', label: labels.edit, onSelect: () => openDialog({ mode: 'edit', entry }) },\n { id: 'delete', label: labels.delete, destructive: true, onSelect: () => { void handleDelete(entry) } },\n ]}\n />\n )}\n onRowClick={onRowClick}\n />\n </div>\n <Dialog open={dialog !== null} onOpenChange={(open) => { if (!open) closeDialog() }}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>\n {dialog?.mode === 'edit' ? labels.editTitle : labels.createTitle}\n </DialogTitle>\n <DialogDescription>\n {dialog?.mode === 'edit' ? labels.editDescription : labels.createDescription}\n </DialogDescription>\n </DialogHeader>\n <form\n className=\"space-y-4\"\n onKeyDown={formKeyHandler}\n onSubmit={(event) => {\n event.preventDefault()\n void handleSubmit()\n }}\n >\n <div className=\"space-y-2\">\n <Label htmlFor=\"adjustment-kind-code\">{labels.codeLabel}</Label>\n <Input\n id=\"adjustment-kind-code\"\n value={form.value}\n onChange={(event) => setForm((prev) => ({ ...prev, value: event.target.value }))}\n placeholder={labels.codePlaceholder}\n className=\"font-mono uppercase\"\n disabled={dialog?.mode === 'edit'}\n />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"adjustment-kind-label\">{labels.labelLabel}</Label>\n <Input\n id=\"adjustment-kind-label\"\n value={form.label}\n onChange={(event) => setForm((prev) => ({ ...prev, label: event.target.value }))}\n placeholder={labels.labelPlaceholder}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>{labels.appearanceLabel}</Label>\n <AppearanceSelector\n color={form.color}\n icon={form.icon}\n onColorChange={(next) => setForm((prev) => ({ ...prev, color: next }))}\n onIconChange={(next) => setForm((prev) => ({ ...prev, icon: next }))}\n labels={labels.appearanceLabels}\n />\n </div>\n {error ? <p className=\"text-sm text-destructive\">{error}</p> : null}\n </form>\n <DialogFooter>\n <Button variant=\"ghost\" onClick={closeDialog}>\n {labels.cancel}\n </Button>\n <Button onClick={() => void handleSubmit()} disabled={submitting}>\n {dialog?.mode === 'edit' ? labels.saveEdit : labels.saveCreate}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n {ConfirmDialogElement}\n </section>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["message"]
7
7
  }
@@ -6,6 +6,7 @@ import { Button } from "@open-mercato/ui/primitives/button";
6
6
  import { Input } from "@open-mercato/ui/primitives/input";
7
7
  import { Badge } from "@open-mercato/ui/primitives/badge";
8
8
  import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
9
+ import { useGuardedMutation } from "@open-mercato/ui/backend/injection/useGuardedMutation";
9
10
  import { flash } from "@open-mercato/ui/backend/FlashMessages";
10
11
  import { useOrganizationScopeVersion } from "@open-mercato/shared/lib/frontend/useOrganizationScope";
11
12
  import { useT } from "@open-mercato/shared/lib/i18n/context";
@@ -26,6 +27,7 @@ const normalizeState = (payload) => ({
26
27
  orderNextNumber: typeof payload?.nextOrderNumber === "number" && Number.isFinite(payload.nextOrderNumber) ? String(payload.nextOrderNumber) : "1",
27
28
  quoteNextNumber: typeof payload?.nextQuoteNumber === "number" && Number.isFinite(payload.nextQuoteNumber) ? String(payload.nextQuoteNumber) : "1"
28
29
  });
30
+ const SAVE_CONTEXT_ID = "sales-document-number-settings";
29
31
  function DocumentNumberSettings() {
30
32
  const t = useT();
31
33
  const scopeVersion = useOrganizationScopeVersion();
@@ -33,6 +35,10 @@ function DocumentNumberSettings() {
33
35
  const [loading, setLoading] = React.useState(false);
34
36
  const [saving, setSaving] = React.useState(false);
35
37
  const [tokens, setTokens] = React.useState(DOCUMENT_NUMBER_TOKENS);
38
+ const { runMutation, retryLastMutation } = useGuardedMutation({
39
+ contextId: SAVE_CONTEXT_ID,
40
+ blockedMessage: t("ui.forms.flash.saveBlocked", "Save blocked by validation")
41
+ });
36
42
  const translations = React.useMemo(() => ({
37
43
  title: t("sales.config.numbering.title", "Document numbers"),
38
44
  description: t(
@@ -93,15 +99,26 @@ function DocumentNumberSettings() {
93
99
  orderNextNumber: Number.parseInt(formState.orderNextNumber, 10) || void 0,
94
100
  quoteNextNumber: Number.parseInt(formState.quoteNextNumber, 10) || void 0
95
101
  };
96
- const call = await apiCall("/api/sales/settings/document-numbers", {
97
- method: "PUT",
98
- headers: { "content-type": "application/json" },
99
- body: JSON.stringify(payload)
102
+ const call = await runMutation({
103
+ // optimistic-lock-exempt: single-row tenant numbering settings blob — no per-record version / concurrent record edit
104
+ operation: async () => {
105
+ const response = await apiCall("/api/sales/settings/document-numbers", {
106
+ method: "PUT",
107
+ headers: { "content-type": "application/json" },
108
+ body: JSON.stringify(payload)
109
+ });
110
+ if (!response.ok) {
111
+ throw new Error(translations.errors.save);
112
+ }
113
+ return response;
114
+ },
115
+ context: {
116
+ formId: SAVE_CONTEXT_ID,
117
+ resourceKind: "sales.settings",
118
+ retryLastMutation
119
+ },
120
+ mutationPayload: payload
100
121
  });
101
- if (!call.ok) {
102
- flash(translations.errors.save, "error");
103
- return;
104
- }
105
122
  setFormState(normalizeState(call.result));
106
123
  setTokens(Array.isArray(call.result?.tokens) && call.result.tokens.length ? call.result.tokens : DOCUMENT_NUMBER_TOKENS);
107
124
  flash(translations.messages.saved, "success");
@@ -111,7 +128,7 @@ function DocumentNumberSettings() {
111
128
  } finally {
112
129
  setSaving(false);
113
130
  }
114
- }, [formState.orderNextNumber, formState.orderNumberFormat, formState.quoteNextNumber, formState.quoteNumberFormat, translations.errors.save, translations.messages.saved]);
131
+ }, [formState.orderNextNumber, formState.orderNumberFormat, formState.quoteNextNumber, formState.quoteNumberFormat, retryLastMutation, runMutation, translations.errors.save, translations.messages.saved]);
115
132
  const handleReset = React.useCallback(() => {
116
133
  setFormState(DEFAULT_STATE);
117
134
  void handleLoad();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/sales/components/DocumentNumberSettings.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Loader2, RefreshCw } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n DOCUMENT_NUMBER_TOKENS,\n DEFAULT_ORDER_NUMBER_FORMAT,\n DEFAULT_QUOTE_NUMBER_FORMAT,\n} from '../lib/documentNumberTokens'\n\ntype SettingsResponse = {\n orderNumberFormat: string\n quoteNumberFormat: string\n nextOrderNumber: number\n nextQuoteNumber: number\n tokens?: typeof DOCUMENT_NUMBER_TOKENS\n}\n\ntype FormState = {\n orderNumberFormat: string\n quoteNumberFormat: string\n orderNextNumber: string\n quoteNextNumber: string\n}\n\nconst DEFAULT_STATE: FormState = {\n orderNumberFormat: DEFAULT_ORDER_NUMBER_FORMAT,\n quoteNumberFormat: DEFAULT_QUOTE_NUMBER_FORMAT,\n orderNextNumber: '1',\n quoteNextNumber: '1',\n}\n\nconst normalizeState = (payload?: Partial<SettingsResponse> | null): FormState => ({\n orderNumberFormat:\n typeof payload?.orderNumberFormat === 'string' && payload.orderNumberFormat.trim().length\n ? payload.orderNumberFormat\n : DEFAULT_ORDER_NUMBER_FORMAT,\n quoteNumberFormat:\n typeof payload?.quoteNumberFormat === 'string' && payload.quoteNumberFormat.trim().length\n ? payload.quoteNumberFormat\n : DEFAULT_QUOTE_NUMBER_FORMAT,\n orderNextNumber:\n typeof payload?.nextOrderNumber === 'number' && Number.isFinite(payload.nextOrderNumber)\n ? String(payload.nextOrderNumber)\n : '1',\n quoteNextNumber:\n typeof payload?.nextQuoteNumber === 'number' && Number.isFinite(payload.nextQuoteNumber)\n ? String(payload.nextQuoteNumber)\n : '1',\n})\n\nexport function DocumentNumberSettings() {\n const t = useT()\n const scopeVersion = useOrganizationScopeVersion()\n const [formState, setFormState] = React.useState<FormState>(DEFAULT_STATE)\n const [loading, setLoading] = React.useState(false)\n const [saving, setSaving] = React.useState(false)\n const [tokens, setTokens] = React.useState(DOCUMENT_NUMBER_TOKENS)\n\n const translations = React.useMemo(() => ({\n title: t('sales.config.numbering.title', 'Document numbers'),\n description: t(\n 'sales.config.numbering.description',\n 'Control how sales orders and quotes are numbered. Formats can include dates, sequences, and random fragments.'\n ),\n orderLabel: t('sales.config.numbering.orderFormat', 'Order number format'),\n quoteLabel: t('sales.config.numbering.quoteFormat', 'Quote number format'),\n orderNext: t('sales.config.numbering.orderNext', 'Next order sequence'),\n quoteNext: t('sales.config.numbering.quoteNext', 'Next quote sequence'),\n save: t('sales.config.numbering.actions.save', 'Save settings'),\n reset: t('sales.config.numbering.actions.reset', 'Reset changes'),\n refresh: t('sales.config.numbering.actions.refresh', 'Refresh'),\n refreshing: t('sales.config.numbering.actions.refreshing', 'Refreshing\u2026'),\n tokensTitle: t('sales.config.numbering.tokensTitle', 'Available tokens'),\n tokensHint: t(\n 'sales.config.numbering.tokensHint',\n 'Combine tokens to shape your identifiers, e.g. ORDER/{yyyy}/{seq:5}.'\n ),\n errors: {\n load: t('sales.config.numbering.errors.load', 'Failed to load numbering settings.'),\n save: t('sales.config.numbering.errors.save', 'Failed to save numbering settings.'),\n },\n messages: {\n saved: t('sales.config.numbering.messages.saved', 'Numbering settings saved.'),\n },\n }), [t])\n\n const handleLoad = React.useCallback(async () => {\n setLoading(true)\n try {\n const call = await apiCall<SettingsResponse>('/api/sales/settings/document-numbers')\n if (call.ok) {\n setFormState(normalizeState(call.result))\n setTokens(Array.isArray(call.result?.tokens) && call.result.tokens.length ? call.result.tokens : DOCUMENT_NUMBER_TOKENS)\n } else {\n flash(translations.errors.load, 'error')\n }\n } catch (err) {\n console.error('sales.document-number-settings.load failed', err)\n flash(translations.errors.load, 'error')\n } finally {\n setLoading(false)\n }\n }, [translations.errors.load])\n\n React.useEffect(() => {\n void handleLoad()\n }, [handleLoad, scopeVersion])\n\n const handleChange = (key: keyof FormState) => (event: React.ChangeEvent<HTMLInputElement>) => {\n setFormState((prev) => ({ ...prev, [key]: event.target.value }))\n }\n\n const handleSubmit = React.useCallback(async (event: React.FormEvent) => {\n event.preventDefault()\n setSaving(true)\n try {\n const payload = {\n orderNumberFormat: formState.orderNumberFormat.trim(),\n quoteNumberFormat: formState.quoteNumberFormat.trim(),\n orderNextNumber: Number.parseInt(formState.orderNextNumber, 10) || undefined,\n quoteNextNumber: Number.parseInt(formState.quoteNextNumber, 10) || undefined,\n }\n // optimistic-lock-exempt: single-row tenant numbering settings blob \u2014 no per-record version / concurrent record edit\n const call = await apiCall<SettingsResponse>('/api/sales/settings/document-numbers', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!call.ok) {\n flash(translations.errors.save, 'error')\n return\n }\n setFormState(normalizeState(call.result))\n setTokens(Array.isArray(call.result?.tokens) && call.result.tokens.length ? call.result.tokens : DOCUMENT_NUMBER_TOKENS)\n flash(translations.messages.saved, 'success')\n } catch (err) {\n console.error('sales.document-number-settings.save failed', err)\n flash(translations.errors.save, 'error')\n } finally {\n setSaving(false)\n }\n }, [formState.orderNextNumber, formState.orderNumberFormat, formState.quoteNextNumber, formState.quoteNumberFormat, translations.errors.save, translations.messages.saved])\n\n const handleReset = React.useCallback(() => {\n setFormState(DEFAULT_STATE)\n void handleLoad()\n }, [handleLoad])\n\n return (\n <section className=\"rounded-none border bg-card/30 p-5 shadow-sm\">\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"space-y-1\">\n <h2 className=\"text-lg font-semibold\">{translations.title}</h2>\n <p className=\"text-sm text-muted-foreground\">{translations.description}</p>\n </div>\n <div className=\"flex gap-2\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n type=\"button\"\n className=\"rounded-none border-0 shadow-none hover:bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0\"\n onClick={handleLoad}\n disabled={loading || saving}\n aria-label={translations.refresh}\n >\n {loading ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCw className=\"h-4 w-4\" />}\n <span className=\"sr-only\">{loading ? translations.refreshing : translations.refresh}</span>\n </Button>\n </div>\n </div>\n <form className=\"mt-4 space-y-5\" onSubmit={handleSubmit}>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <label className=\"space-y-2\">\n <div className=\"text-sm font-medium\">{translations.orderLabel}</div>\n <Input\n value={formState.orderNumberFormat}\n onChange={handleChange('orderNumberFormat')}\n disabled={loading || saving}\n spellCheck={false}\n />\n <p className=\"text-xs text-muted-foreground\">\n {t('sales.config.numbering.orderHint', 'Example: ORDER/{yyyy}/{seq:5}')}\n </p>\n </label>\n <label className=\"space-y-2\">\n <div className=\"text-sm font-medium\">{translations.quoteLabel}</div>\n <Input\n value={formState.quoteNumberFormat}\n onChange={handleChange('quoteNumberFormat')}\n disabled={loading || saving}\n spellCheck={false}\n />\n <p className=\"text-xs text-muted-foreground\">\n {t('sales.config.numbering.quoteHint', 'Example: QUOTE/{yy}/{mm}/{seq:4}')}\n </p>\n </label>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <label className=\"space-y-2\">\n <div className=\"text-sm font-medium\">{translations.orderNext}</div>\n <Input\n type=\"number\"\n min={1}\n value={formState.orderNextNumber}\n onChange={handleChange('orderNextNumber')}\n disabled={loading || saving}\n />\n <p className=\"text-xs text-muted-foreground\">\n {t('sales.config.numbering.sequenceHint', 'Applied on the next generated number for this type.')}\n </p>\n </label>\n <label className=\"space-y-2\">\n <div className=\"text-sm font-medium\">{translations.quoteNext}</div>\n <Input\n type=\"number\"\n min={1}\n value={formState.quoteNextNumber}\n onChange={handleChange('quoteNextNumber')}\n disabled={loading || saving}\n />\n <p className=\"text-xs text-muted-foreground\">\n {t('sales.config.numbering.sequenceHintQuote', 'Resets the quote counter for the next document.')}\n </p>\n </label>\n </div>\n <div className=\"space-y-2 rounded-lg border bg-muted/50 p-3\">\n <div className=\"flex items-center gap-2 text-sm font-medium\">\n {translations.tokensTitle}\n <span className=\"text-xs font-normal text-muted-foreground\">{translations.tokensHint}</span>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n {tokens.map((entry) => (\n <div key={entry.token} className=\"flex items-center gap-2 rounded border bg-background px-2 py-1\">\n <Badge variant=\"secondary\" className=\"font-mono text-xs\">\n {entry.token}\n </Badge>\n <span className=\"text-xs text-muted-foreground\">{entry.description}</span>\n </div>\n ))}\n </div>\n </div>\n <div className=\"flex items-center justify-end gap-2\">\n <Button type=\"button\" variant=\"ghost\" onClick={handleReset} disabled={loading || saving}>\n {translations.reset}\n </Button>\n <Button type=\"submit\" disabled={saving}>\n {saving ? t('sales.config.numbering.actions.saving', 'Saving\u2026') : translations.save}\n </Button>\n </div>\n </form>\n </section>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Loader2, RefreshCw } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n DOCUMENT_NUMBER_TOKENS,\n DEFAULT_ORDER_NUMBER_FORMAT,\n DEFAULT_QUOTE_NUMBER_FORMAT,\n} from '../lib/documentNumberTokens'\n\ntype SettingsResponse = {\n orderNumberFormat: string\n quoteNumberFormat: string\n nextOrderNumber: number\n nextQuoteNumber: number\n tokens?: typeof DOCUMENT_NUMBER_TOKENS\n}\n\ntype FormState = {\n orderNumberFormat: string\n quoteNumberFormat: string\n orderNextNumber: string\n quoteNextNumber: string\n}\n\nconst DEFAULT_STATE: FormState = {\n orderNumberFormat: DEFAULT_ORDER_NUMBER_FORMAT,\n quoteNumberFormat: DEFAULT_QUOTE_NUMBER_FORMAT,\n orderNextNumber: '1',\n quoteNextNumber: '1',\n}\n\nconst normalizeState = (payload?: Partial<SettingsResponse> | null): FormState => ({\n orderNumberFormat:\n typeof payload?.orderNumberFormat === 'string' && payload.orderNumberFormat.trim().length\n ? payload.orderNumberFormat\n : DEFAULT_ORDER_NUMBER_FORMAT,\n quoteNumberFormat:\n typeof payload?.quoteNumberFormat === 'string' && payload.quoteNumberFormat.trim().length\n ? payload.quoteNumberFormat\n : DEFAULT_QUOTE_NUMBER_FORMAT,\n orderNextNumber:\n typeof payload?.nextOrderNumber === 'number' && Number.isFinite(payload.nextOrderNumber)\n ? String(payload.nextOrderNumber)\n : '1',\n quoteNextNumber:\n typeof payload?.nextQuoteNumber === 'number' && Number.isFinite(payload.nextQuoteNumber)\n ? String(payload.nextQuoteNumber)\n : '1',\n})\n\nconst SAVE_CONTEXT_ID = 'sales-document-number-settings'\n\nexport function DocumentNumberSettings() {\n const t = useT()\n const scopeVersion = useOrganizationScopeVersion()\n const [formState, setFormState] = React.useState<FormState>(DEFAULT_STATE)\n const [loading, setLoading] = React.useState(false)\n const [saving, setSaving] = React.useState(false)\n const [tokens, setTokens] = React.useState(DOCUMENT_NUMBER_TOKENS)\n\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\n const translations = React.useMemo(() => ({\n title: t('sales.config.numbering.title', 'Document numbers'),\n description: t(\n 'sales.config.numbering.description',\n 'Control how sales orders and quotes are numbered. Formats can include dates, sequences, and random fragments.'\n ),\n orderLabel: t('sales.config.numbering.orderFormat', 'Order number format'),\n quoteLabel: t('sales.config.numbering.quoteFormat', 'Quote number format'),\n orderNext: t('sales.config.numbering.orderNext', 'Next order sequence'),\n quoteNext: t('sales.config.numbering.quoteNext', 'Next quote sequence'),\n save: t('sales.config.numbering.actions.save', 'Save settings'),\n reset: t('sales.config.numbering.actions.reset', 'Reset changes'),\n refresh: t('sales.config.numbering.actions.refresh', 'Refresh'),\n refreshing: t('sales.config.numbering.actions.refreshing', 'Refreshing\u2026'),\n tokensTitle: t('sales.config.numbering.tokensTitle', 'Available tokens'),\n tokensHint: t(\n 'sales.config.numbering.tokensHint',\n 'Combine tokens to shape your identifiers, e.g. ORDER/{yyyy}/{seq:5}.'\n ),\n errors: {\n load: t('sales.config.numbering.errors.load', 'Failed to load numbering settings.'),\n save: t('sales.config.numbering.errors.save', 'Failed to save numbering settings.'),\n },\n messages: {\n saved: t('sales.config.numbering.messages.saved', 'Numbering settings saved.'),\n },\n }), [t])\n\n const handleLoad = React.useCallback(async () => {\n setLoading(true)\n try {\n const call = await apiCall<SettingsResponse>('/api/sales/settings/document-numbers')\n if (call.ok) {\n setFormState(normalizeState(call.result))\n setTokens(Array.isArray(call.result?.tokens) && call.result.tokens.length ? call.result.tokens : DOCUMENT_NUMBER_TOKENS)\n } else {\n flash(translations.errors.load, 'error')\n }\n } catch (err) {\n console.error('sales.document-number-settings.load failed', err)\n flash(translations.errors.load, 'error')\n } finally {\n setLoading(false)\n }\n }, [translations.errors.load])\n\n React.useEffect(() => {\n void handleLoad()\n }, [handleLoad, scopeVersion])\n\n const handleChange = (key: keyof FormState) => (event: React.ChangeEvent<HTMLInputElement>) => {\n setFormState((prev) => ({ ...prev, [key]: event.target.value }))\n }\n\n const handleSubmit = React.useCallback(async (event: React.FormEvent) => {\n event.preventDefault()\n setSaving(true)\n try {\n const payload = {\n orderNumberFormat: formState.orderNumberFormat.trim(),\n quoteNumberFormat: formState.quoteNumberFormat.trim(),\n orderNextNumber: Number.parseInt(formState.orderNextNumber, 10) || undefined,\n quoteNextNumber: Number.parseInt(formState.quoteNextNumber, 10) || undefined,\n }\n const call = await runMutation({\n // optimistic-lock-exempt: single-row tenant numbering settings blob \u2014 no per-record version / concurrent record edit\n operation: async () => {\n const response = await apiCall<SettingsResponse>('/api/sales/settings/document-numbers', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!response.ok) {\n throw new Error(translations.errors.save)\n }\n return response\n },\n context: {\n formId: SAVE_CONTEXT_ID,\n resourceKind: 'sales.settings',\n retryLastMutation,\n },\n mutationPayload: payload,\n })\n setFormState(normalizeState(call.result))\n setTokens(Array.isArray(call.result?.tokens) && call.result.tokens.length ? call.result.tokens : DOCUMENT_NUMBER_TOKENS)\n flash(translations.messages.saved, 'success')\n } catch (err) {\n console.error('sales.document-number-settings.save failed', err)\n flash(translations.errors.save, 'error')\n } finally {\n setSaving(false)\n }\n }, [formState.orderNextNumber, formState.orderNumberFormat, formState.quoteNextNumber, formState.quoteNumberFormat, retryLastMutation, runMutation, translations.errors.save, translations.messages.saved])\n\n const handleReset = React.useCallback(() => {\n setFormState(DEFAULT_STATE)\n void handleLoad()\n }, [handleLoad])\n\n return (\n <section className=\"rounded-none border bg-card/30 p-5 shadow-sm\">\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"space-y-1\">\n <h2 className=\"text-lg font-semibold\">{translations.title}</h2>\n <p className=\"text-sm text-muted-foreground\">{translations.description}</p>\n </div>\n <div className=\"flex gap-2\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n type=\"button\"\n className=\"rounded-none border-0 shadow-none hover:bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0\"\n onClick={handleLoad}\n disabled={loading || saving}\n aria-label={translations.refresh}\n >\n {loading ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCw className=\"h-4 w-4\" />}\n <span className=\"sr-only\">{loading ? translations.refreshing : translations.refresh}</span>\n </Button>\n </div>\n </div>\n <form className=\"mt-4 space-y-5\" onSubmit={handleSubmit}>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <label className=\"space-y-2\">\n <div className=\"text-sm font-medium\">{translations.orderLabel}</div>\n <Input\n value={formState.orderNumberFormat}\n onChange={handleChange('orderNumberFormat')}\n disabled={loading || saving}\n spellCheck={false}\n />\n <p className=\"text-xs text-muted-foreground\">\n {t('sales.config.numbering.orderHint', 'Example: ORDER/{yyyy}/{seq:5}')}\n </p>\n </label>\n <label className=\"space-y-2\">\n <div className=\"text-sm font-medium\">{translations.quoteLabel}</div>\n <Input\n value={formState.quoteNumberFormat}\n onChange={handleChange('quoteNumberFormat')}\n disabled={loading || saving}\n spellCheck={false}\n />\n <p className=\"text-xs text-muted-foreground\">\n {t('sales.config.numbering.quoteHint', 'Example: QUOTE/{yy}/{mm}/{seq:4}')}\n </p>\n </label>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <label className=\"space-y-2\">\n <div className=\"text-sm font-medium\">{translations.orderNext}</div>\n <Input\n type=\"number\"\n min={1}\n value={formState.orderNextNumber}\n onChange={handleChange('orderNextNumber')}\n disabled={loading || saving}\n />\n <p className=\"text-xs text-muted-foreground\">\n {t('sales.config.numbering.sequenceHint', 'Applied on the next generated number for this type.')}\n </p>\n </label>\n <label className=\"space-y-2\">\n <div className=\"text-sm font-medium\">{translations.quoteNext}</div>\n <Input\n type=\"number\"\n min={1}\n value={formState.quoteNextNumber}\n onChange={handleChange('quoteNextNumber')}\n disabled={loading || saving}\n />\n <p className=\"text-xs text-muted-foreground\">\n {t('sales.config.numbering.sequenceHintQuote', 'Resets the quote counter for the next document.')}\n </p>\n </label>\n </div>\n <div className=\"space-y-2 rounded-lg border bg-muted/50 p-3\">\n <div className=\"flex items-center gap-2 text-sm font-medium\">\n {translations.tokensTitle}\n <span className=\"text-xs font-normal text-muted-foreground\">{translations.tokensHint}</span>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n {tokens.map((entry) => (\n <div key={entry.token} className=\"flex items-center gap-2 rounded border bg-background px-2 py-1\">\n <Badge variant=\"secondary\" className=\"font-mono text-xs\">\n {entry.token}\n </Badge>\n <span className=\"text-xs text-muted-foreground\">{entry.description}</span>\n </div>\n ))}\n </div>\n </div>\n <div className=\"flex items-center justify-end gap-2\">\n <Button type=\"button\" variant=\"ghost\" onClick={handleReset} disabled={loading || saving}>\n {translations.reset}\n </Button>\n <Button type=\"submit\" disabled={saving}>\n {saving ? t('sales.config.numbering.actions.saving', 'Saving\u2026') : translations.save}\n </Button>\n </div>\n </form>\n </section>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -6,6 +6,7 @@ import { Button } from "@open-mercato/ui/primitives/button";
6
6
  import { Switch } from "@open-mercato/ui/primitives/switch";
7
7
  import { flash } from "@open-mercato/ui/backend/FlashMessages";
8
8
  import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
9
+ import { useGuardedMutation } from "@open-mercato/ui/backend/injection/useGuardedMutation";
9
10
  import { useOrganizationScopeVersion } from "@open-mercato/shared/lib/frontend/useOrganizationScope";
10
11
  import { useT } from "@open-mercato/shared/lib/i18n/context";
11
12
  import { Badge } from "@open-mercato/ui/primitives/badge";
@@ -20,6 +21,7 @@ const normalizeStatusList = (list) => {
20
21
  });
21
22
  return Array.from(set);
22
23
  };
24
+ const SAVE_CONTEXT_ID = "sales-order-editing-settings";
23
25
  function OrderEditingSettings() {
24
26
  const t = useT();
25
27
  const scopeVersion = useOrganizationScopeVersion();
@@ -28,6 +30,10 @@ function OrderEditingSettings() {
28
30
  const [options, setOptions] = React.useState([]);
29
31
  const [customerStatuses, setCustomerStatuses] = React.useState(null);
30
32
  const [addressStatuses, setAddressStatuses] = React.useState(null);
33
+ const { runMutation, retryLastMutation } = useGuardedMutation({
34
+ contextId: SAVE_CONTEXT_ID,
35
+ blockedMessage: t("ui.forms.flash.saveBlocked", "Save blocked by validation")
36
+ });
31
37
  const translations = React.useMemo(
32
38
  () => ({
33
39
  title: t("sales.config.orderEditing.title", "Order editing guards"),
@@ -100,15 +106,26 @@ function OrderEditingSettings() {
100
106
  orderCustomerEditableStatuses: customerStatuses,
101
107
  orderAddressEditableStatuses: addressStatuses
102
108
  };
103
- const call = await apiCall("/api/sales/settings/order-editing", {
104
- method: "PUT",
105
- headers: { "content-type": "application/json" },
106
- body: JSON.stringify(payload)
109
+ const call = await runMutation({
110
+ // optimistic-lock-exempt: single-row tenant order-editing settings blob — no per-record version / concurrent record edit
111
+ operation: async () => {
112
+ const response = await apiCall("/api/sales/settings/order-editing", {
113
+ method: "PUT",
114
+ headers: { "content-type": "application/json" },
115
+ body: JSON.stringify(payload)
116
+ });
117
+ if (!response.ok) {
118
+ throw new Error(translations.messages.saveError);
119
+ }
120
+ return response;
121
+ },
122
+ context: {
123
+ formId: SAVE_CONTEXT_ID,
124
+ resourceKind: "sales.settings",
125
+ retryLastMutation
126
+ },
127
+ mutationPayload: payload
107
128
  });
108
- if (!call.ok) {
109
- flash(translations.messages.saveError, "error");
110
- return;
111
- }
112
129
  setCustomerStatuses(normalizeStatusList(call.result?.orderCustomerEditableStatuses));
113
130
  setAddressStatuses(normalizeStatusList(call.result?.orderAddressEditableStatuses));
114
131
  setOptions(Array.isArray(call.result?.orderStatuses) ? call.result.orderStatuses : []);
@@ -119,7 +136,7 @@ function OrderEditingSettings() {
119
136
  } finally {
120
137
  setSaving(false);
121
138
  }
122
- }, [addressStatuses, customerStatuses, translations.messages.saveError, translations.messages.saved]);
139
+ }, [addressStatuses, customerStatuses, retryLastMutation, runMutation, translations.messages.saveError, translations.messages.saved]);
123
140
  const renderStatusList = React.useCallback(
124
141
  (kind, values, label) => {
125
142
  const allowedAny = values === null;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/sales/components/OrderEditingSettings.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Loader2, RefreshCw } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Switch } from '@open-mercato/ui/primitives/switch'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\nimport { Badge } from '@open-mercato/ui/primitives/badge'\n\ntype OrderStatusOption = {\n id: string\n value: string\n label: string\n}\n\ntype SettingsResponse = {\n orderCustomerEditableStatuses: string[] | null\n orderAddressEditableStatuses: string[] | null\n orderStatuses: OrderStatusOption[]\n}\n\nconst normalizeStatusList = (list: unknown): string[] | null => {\n if (list === null) return null\n if (!Array.isArray(list)) return []\n const set = new Set<string>()\n list.forEach((value) => {\n if (typeof value === 'string' && value.trim().length) {\n set.add(value.trim())\n }\n })\n return Array.from(set)\n}\n\nexport function OrderEditingSettings() {\n const t = useT()\n const scopeVersion = useOrganizationScopeVersion()\n const [loading, setLoading] = React.useState(false)\n const [saving, setSaving] = React.useState(false)\n const [options, setOptions] = React.useState<OrderStatusOption[]>([])\n const [customerStatuses, setCustomerStatuses] = React.useState<string[] | null>(null)\n const [addressStatuses, setAddressStatuses] = React.useState<string[] | null>(null)\n\n const translations = React.useMemo(\n () => ({\n title: t('sales.config.orderEditing.title', 'Order editing guards'),\n description: t(\n 'sales.config.orderEditing.description',\n 'Control when customers and addresses can be changed on an order.'\n ),\n customerLabel: t('sales.config.orderEditing.customerLabel', 'Edit customer allowed at'),\n addressLabel: t('sales.config.orderEditing.addressLabel', 'Edit addresses allowed at'),\n allowAny: t('sales.config.orderEditing.allowAny', 'Allow at any status'),\n note: t(\n 'sales.config.orderEditing.note',\n 'Changing the customer clears assigned addresses to avoid mixing contact data.'\n ),\n actions: {\n refresh: t('sales.config.orderEditing.actions.refresh', 'Refresh'),\n refreshing: t('sales.config.orderEditing.actions.refreshing', 'Refreshing\u2026'),\n save: t('sales.config.orderEditing.actions.save', 'Save settings'),\n },\n messages: {\n loadError: t('sales.config.orderEditing.errors.load', 'Failed to load order editing settings.'),\n saveError: t('sales.config.orderEditing.errors.save', 'Failed to save order editing settings.'),\n saved: t('sales.config.orderEditing.success.save', 'Order editing settings saved.'),\n },\n }),\n [t]\n )\n\n const loadSettings = React.useCallback(async () => {\n setLoading(true)\n try {\n const call = await apiCall<SettingsResponse>('/api/sales/settings/order-editing')\n if (!call.ok) {\n flash(translations.messages.loadError, 'error')\n return\n }\n setOptions(Array.isArray(call.result?.orderStatuses) ? call.result.orderStatuses : [])\n setCustomerStatuses(normalizeStatusList(call.result?.orderCustomerEditableStatuses))\n setAddressStatuses(normalizeStatusList(call.result?.orderAddressEditableStatuses))\n } catch (err) {\n console.error('sales.order-editing-settings.load failed', err)\n flash(translations.messages.loadError, 'error')\n } finally {\n setLoading(false)\n }\n }, [translations.messages.loadError])\n\n React.useEffect(() => {\n void loadSettings()\n }, [loadSettings, scopeVersion])\n\n const toggleAny = React.useCallback((kind: 'customer' | 'address') => {\n if (kind === 'customer') {\n setCustomerStatuses((prev) => (prev === null ? [] : null))\n } else {\n setAddressStatuses((prev) => (prev === null ? [] : null))\n }\n }, [])\n\n const toggleStatus = React.useCallback((kind: 'customer' | 'address', value: string) => {\n const updater = kind === 'customer' ? setCustomerStatuses : setAddressStatuses\n updater((prev) => {\n const current = prev ?? []\n if (current.includes(value)) {\n return current.filter((entry) => entry !== value)\n }\n return [...current, value]\n })\n }, [])\n\n const handleSubmit = React.useCallback(async () => {\n setSaving(true)\n try {\n const payload = {\n orderCustomerEditableStatuses: customerStatuses,\n orderAddressEditableStatuses: addressStatuses,\n }\n // optimistic-lock-exempt: single-row tenant order-editing settings blob \u2014 no per-record version / concurrent record edit\n const call = await apiCall<SettingsResponse>('/api/sales/settings/order-editing', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!call.ok) {\n flash(translations.messages.saveError, 'error')\n return\n }\n setCustomerStatuses(normalizeStatusList(call.result?.orderCustomerEditableStatuses))\n setAddressStatuses(normalizeStatusList(call.result?.orderAddressEditableStatuses))\n setOptions(Array.isArray(call.result?.orderStatuses) ? call.result.orderStatuses : [])\n flash(translations.messages.saved, 'success')\n } catch (err) {\n console.error('sales.order-editing-settings.save failed', err)\n flash(translations.messages.saveError, 'error')\n } finally {\n setSaving(false)\n }\n }, [addressStatuses, customerStatuses, translations.messages.saveError, translations.messages.saved])\n\n const renderStatusList = React.useCallback(\n (kind: 'customer' | 'address', values: string[] | null, label: string) => {\n const allowedAny = values === null\n return (\n <div className=\"space-y-3 rounded-none border bg-card/30 p-4\">\n <div className=\"flex items-center justify-between gap-2\">\n <div className=\"space-y-1\">\n <p className=\"text-sm font-medium\">{label}</p>\n <p className=\"text-xs text-muted-foreground\">\n {allowedAny\n ? t('sales.config.orderEditing.anyStatus', 'Allowed at any status')\n : t('sales.config.orderEditing.pickStatuses', 'Select statuses that allow this change')}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <Switch checked={allowedAny} onCheckedChange={() => toggleAny(kind)} disabled={loading || saving} />\n <span className=\"text-xs text-muted-foreground\">{translations.allowAny}</span>\n </div>\n </div>\n {!allowedAny && (\n <div className=\"grid gap-2 sm:grid-cols-2 lg:grid-cols-3\">\n {options.map((status) => {\n const checked = values?.includes(status.value) ?? false\n return (\n <label\n key={status.id}\n className=\"flex items-center gap-2 rounded-none border bg-background p-2 text-sm\"\n >\n <input\n type=\"checkbox\"\n className=\"h-4 w-4 rounded border\"\n checked={checked}\n onChange={() => toggleStatus(kind, status.value)}\n disabled={loading || saving}\n />\n <span className=\"truncate\" title={status.label || status.value}>\n {status.label || status.value}\n </span>\n <Badge variant=\"outline\" className=\"ml-auto text-overline uppercase tracking-wide\">\n {status.value}\n </Badge>\n </label>\n )\n })}\n </div>\n )}\n </div>\n )\n },\n [loading, options, saving, t, toggleAny, toggleStatus, translations.allowAny]\n )\n\n return (\n <section className=\"space-y-4 rounded-none border bg-card/30 p-5 shadow-sm !rounded-none\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <h2 className=\"text-lg font-semibold\">{translations.title}</h2>\n <p className=\"text-sm text-muted-foreground\">{translations.description}</p>\n </div>\n <div className=\"flex gap-2\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n type=\"button\"\n className=\"rounded-none border-0 shadow-none hover:bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0\"\n onClick={() => void loadSettings()}\n disabled={loading || saving}\n aria-label={translations.actions.refresh}\n >\n {loading ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCw className=\"h-4 w-4\" />}\n <span className=\"sr-only\">{loading ? translations.actions.refreshing : translations.actions.refresh}</span>\n </Button>\n </div>\n </div>\n <p className=\"text-xs text-muted-foreground\">{translations.note}</p>\n <div className=\"space-y-3\">\n {renderStatusList('customer', customerStatuses, translations.customerLabel)}\n {renderStatusList('address', addressStatuses, translations.addressLabel)}\n </div>\n <div className=\"flex justify-end gap-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"rounded-none border-0 shadow-none hover:bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0\"\n onClick={() => void loadSettings()}\n disabled={loading || saving}\n aria-label={translations.actions.refresh}\n >\n {loading ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCw className=\"h-4 w-4\" />}\n <span className=\"sr-only\">{loading ? translations.actions.refreshing : translations.actions.refresh}</span>\n </Button>\n <Button type=\"button\" onClick={() => void handleSubmit()} disabled={loading || saving}>\n {translations.actions.save}\n </Button>\n </div>\n </section>\n )\n}\n"],
5
- "mappings": ";AAuJY,SACE,KADF;AArJZ,YAAY,WAAW;AACvB,SAAS,SAAS,iBAAiB;AACnC,SAAS,cAAc;AACvB,SAAS,cAAc;AACvB,SAAS,aAAa;AACtB,SAAS,eAAe;AACxB,SAAS,mCAAmC;AAC5C,SAAS,YAAY;AAErB,SAAS,aAAa;AActB,MAAM,sBAAsB,CAAC,SAAmC;AAC9D,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI,CAAC,MAAM,QAAQ,IAAI,EAAG,QAAO,CAAC;AAClC,QAAM,MAAM,oBAAI,IAAY;AAC5B,OAAK,QAAQ,CAAC,UAAU;AACtB,QAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,QAAQ;AACpD,UAAI,IAAI,MAAM,KAAK,CAAC;AAAA,IACtB;AAAA,EACF,CAAC;AACD,SAAO,MAAM,KAAK,GAAG;AACvB;AAEO,SAAS,uBAAuB;AACrC,QAAM,IAAI,KAAK;AACf,QAAM,eAAe,4BAA4B;AACjD,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,KAAK;AAClD,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAS,KAAK;AAChD,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAA8B,CAAC,CAAC;AACpE,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,MAAM,SAA0B,IAAI;AACpF,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,MAAM,SAA0B,IAAI;AAElF,QAAM,eAAe,MAAM;AAAA,IACzB,OAAO;AAAA,MACL,OAAO,EAAE,mCAAmC,sBAAsB;AAAA,MAClE,aAAa;AAAA,QACX;AAAA,QACA;AAAA,MACF;AAAA,MACA,eAAe,EAAE,2CAA2C,0BAA0B;AAAA,MACtF,cAAc,EAAE,0CAA0C,2BAA2B;AAAA,MACrF,UAAU,EAAE,sCAAsC,qBAAqB;AAAA,MACvE,MAAM;AAAA,QACJ;AAAA,QACA;AAAA,MACF;AAAA,MACA,SAAS;AAAA,QACP,SAAS,EAAE,6CAA6C,SAAS;AAAA,QACjE,YAAY,EAAE,gDAAgD,kBAAa;AAAA,QAC3E,MAAM,EAAE,0CAA0C,eAAe;AAAA,MACnE;AAAA,MACA,UAAU;AAAA,QACR,WAAW,EAAE,yCAAyC,wCAAwC;AAAA,QAC9F,WAAW,EAAE,yCAAyC,wCAAwC;AAAA,QAC9F,OAAO,EAAE,0CAA0C,+BAA+B;AAAA,MACpF;AAAA,IACF;AAAA,IACA,CAAC,CAAC;AAAA,EACJ;AAEA,QAAM,eAAe,MAAM,YAAY,YAAY;AACjD,eAAW,IAAI;AACf,QAAI;AACF,YAAM,OAAO,MAAM,QAA0B,mCAAmC;AAChF,UAAI,CAAC,KAAK,IAAI;AACZ,cAAM,aAAa,SAAS,WAAW,OAAO;AAC9C;AAAA,MACF;AACA,iBAAW,MAAM,QAAQ,KAAK,QAAQ,aAAa,IAAI,KAAK,OAAO,gBAAgB,CAAC,CAAC;AACrF,0BAAoB,oBAAoB,KAAK,QAAQ,6BAA6B,CAAC;AACnF,yBAAmB,oBAAoB,KAAK,QAAQ,4BAA4B,CAAC;AAAA,IACnF,SAAS,KAAK;AACZ,cAAQ,MAAM,4CAA4C,GAAG;AAC7D,YAAM,aAAa,SAAS,WAAW,OAAO;AAAA,IAChD,UAAE;AACA,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF,GAAG,CAAC,aAAa,SAAS,SAAS,CAAC;AAEpC,QAAM,UAAU,MAAM;AACpB,SAAK,aAAa;AAAA,EACpB,GAAG,CAAC,cAAc,YAAY,CAAC;AAE/B,QAAM,YAAY,MAAM,YAAY,CAAC,SAAiC;AACpE,QAAI,SAAS,YAAY;AACvB,0BAAoB,CAAC,SAAU,SAAS,OAAO,CAAC,IAAI,IAAK;AAAA,IAC3D,OAAO;AACL,yBAAmB,CAAC,SAAU,SAAS,OAAO,CAAC,IAAI,IAAK;AAAA,IAC1D;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,eAAe,MAAM,YAAY,CAAC,MAA8B,UAAkB;AACtF,UAAM,UAAU,SAAS,aAAa,sBAAsB;AAC5D,YAAQ,CAAC,SAAS;AAChB,YAAM,UAAU,QAAQ,CAAC;AACzB,UAAI,QAAQ,SAAS,KAAK,GAAG;AAC3B,eAAO,QAAQ,OAAO,CAAC,UAAU,UAAU,KAAK;AAAA,MAClD;AACA,aAAO,CAAC,GAAG,SAAS,KAAK;AAAA,IAC3B,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AAEL,QAAM,eAAe,MAAM,YAAY,YAAY;AACjD,cAAU,IAAI;AACd,QAAI;AACF,YAAM,UAAU;AAAA,QACd,+BAA+B;AAAA,QAC/B,8BAA8B;AAAA,MAChC;AAEA,YAAM,OAAO,MAAM,QAA0B,qCAAqC;AAAA,QAChF,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,OAAO;AAAA,MAC9B,CAAC;AACD,UAAI,CAAC,KAAK,IAAI;AACZ,cAAM,aAAa,SAAS,WAAW,OAAO;AAC9C;AAAA,MACF;AACA,0BAAoB,oBAAoB,KAAK,QAAQ,6BAA6B,CAAC;AACnF,yBAAmB,oBAAoB,KAAK,QAAQ,4BAA4B,CAAC;AACjF,iBAAW,MAAM,QAAQ,KAAK,QAAQ,aAAa,IAAI,KAAK,OAAO,gBAAgB,CAAC,CAAC;AACrF,YAAM,aAAa,SAAS,OAAO,SAAS;AAAA,IAC9C,SAAS,KAAK;AACZ,cAAQ,MAAM,4CAA4C,GAAG;AAC7D,YAAM,aAAa,SAAS,WAAW,OAAO;AAAA,IAChD,UAAE;AACA,gBAAU,KAAK;AAAA,IACjB;AAAA,EACF,GAAG,CAAC,iBAAiB,kBAAkB,aAAa,SAAS,WAAW,aAAa,SAAS,KAAK,CAAC;AAEpG,QAAM,mBAAmB,MAAM;AAAA,IAC7B,CAAC,MAA8B,QAAyB,UAAkB;AACxE,YAAM,aAAa,WAAW;AAC9B,aACE,qBAAC,SAAI,WAAU,gDACb;AAAA,6BAAC,SAAI,WAAU,2CACb;AAAA,+BAAC,SAAI,WAAU,aACb;AAAA,gCAAC,OAAE,WAAU,uBAAuB,iBAAM;AAAA,YAC1C,oBAAC,OAAE,WAAU,iCACV,uBACG,EAAE,uCAAuC,uBAAuB,IAChE,EAAE,0CAA0C,wCAAwC,GAC1F;AAAA,aACF;AAAA,UACA,qBAAC,SAAI,WAAU,2BACb;AAAA,gCAAC,UAAO,SAAS,YAAY,iBAAiB,MAAM,UAAU,IAAI,GAAG,UAAU,WAAW,QAAQ;AAAA,YAClG,oBAAC,UAAK,WAAU,iCAAiC,uBAAa,UAAS;AAAA,aACzE;AAAA,WACF;AAAA,QACC,CAAC,cACA,oBAAC,SAAI,WAAU,4CACZ,kBAAQ,IAAI,CAAC,WAAW;AACvB,gBAAM,UAAU,QAAQ,SAAS,OAAO,KAAK,KAAK;AAClD,iBACE;AAAA,YAAC;AAAA;AAAA,cAEC,WAAU;AAAA,cAEV;AAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,WAAU;AAAA,oBACV;AAAA,oBACA,UAAU,MAAM,aAAa,MAAM,OAAO,KAAK;AAAA,oBAC/C,UAAU,WAAW;AAAA;AAAA,gBACvB;AAAA,gBACA,oBAAC,UAAK,WAAU,YAAW,OAAO,OAAO,SAAS,OAAO,OACtD,iBAAO,SAAS,OAAO,OAC1B;AAAA,gBACA,oBAAC,SAAM,SAAQ,WAAU,WAAU,iDAChC,iBAAO,OACV;AAAA;AAAA;AAAA,YAfK,OAAO;AAAA,UAgBd;AAAA,QAEJ,CAAC,GACH;AAAA,SAEJ;AAAA,IAEJ;AAAA,IACA,CAAC,SAAS,SAAS,QAAQ,GAAG,WAAW,cAAc,aAAa,QAAQ;AAAA,EAC9E;AAEA,SACE,qBAAC,aAAQ,WAAU,wEACjB;AAAA,yBAAC,SAAI,WAAU,0CACb;AAAA,2BAAC,SAAI,WAAU,aACb;AAAA,4BAAC,QAAG,WAAU,yBAAyB,uBAAa,OAAM;AAAA,QAC1D,oBAAC,OAAE,WAAU,iCAAiC,uBAAa,aAAY;AAAA,SACzE;AAAA,MACA,oBAAC,SAAI,WAAU,cACb;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,MAAK;AAAA,UACL,MAAK;AAAA,UACL,WAAU;AAAA,UACV,SAAS,MAAM,KAAK,aAAa;AAAA,UACjC,UAAU,WAAW;AAAA,UACrB,cAAY,aAAa,QAAQ;AAAA,UAEhC;AAAA,sBAAU,oBAAC,WAAQ,WAAU,wBAAuB,IAAK,oBAAC,aAAU,WAAU,WAAU;AAAA,YACzF,oBAAC,UAAK,WAAU,WAAW,oBAAU,aAAa,QAAQ,aAAa,aAAa,QAAQ,SAAQ;AAAA;AAAA;AAAA,MACtG,GACF;AAAA,OACF;AAAA,IACA,oBAAC,OAAE,WAAU,iCAAiC,uBAAa,MAAK;AAAA,IAChE,qBAAC,SAAI,WAAU,aACZ;AAAA,uBAAiB,YAAY,kBAAkB,aAAa,aAAa;AAAA,MACzE,iBAAiB,WAAW,iBAAiB,aAAa,YAAY;AAAA,OACzE;AAAA,IACA,qBAAC,SAAI,WAAU,0BACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAQ;AAAA,UACR,MAAK;AAAA,UACL,WAAU;AAAA,UACV,SAAS,MAAM,KAAK,aAAa;AAAA,UACjC,UAAU,WAAW;AAAA,UACrB,cAAY,aAAa,QAAQ;AAAA,UAEhC;AAAA,sBAAU,oBAAC,WAAQ,WAAU,wBAAuB,IAAK,oBAAC,aAAU,WAAU,WAAU;AAAA,YACzF,oBAAC,UAAK,WAAU,WAAW,oBAAU,aAAa,QAAQ,aAAa,aAAa,QAAQ,SAAQ;AAAA;AAAA;AAAA,MACtG;AAAA,MACA,oBAAC,UAAO,MAAK,UAAS,SAAS,MAAM,KAAK,aAAa,GAAG,UAAU,WAAW,QAC5E,uBAAa,QAAQ,MACxB;AAAA,OACF;AAAA,KACF;AAEJ;",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Loader2, RefreshCw } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Switch } from '@open-mercato/ui/primitives/switch'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\nimport { Badge } from '@open-mercato/ui/primitives/badge'\n\ntype OrderStatusOption = {\n id: string\n value: string\n label: string\n}\n\ntype SettingsResponse = {\n orderCustomerEditableStatuses: string[] | null\n orderAddressEditableStatuses: string[] | null\n orderStatuses: OrderStatusOption[]\n}\n\nconst normalizeStatusList = (list: unknown): string[] | null => {\n if (list === null) return null\n if (!Array.isArray(list)) return []\n const set = new Set<string>()\n list.forEach((value) => {\n if (typeof value === 'string' && value.trim().length) {\n set.add(value.trim())\n }\n })\n return Array.from(set)\n}\n\nconst SAVE_CONTEXT_ID = 'sales-order-editing-settings'\n\nexport function OrderEditingSettings() {\n const t = useT()\n const scopeVersion = useOrganizationScopeVersion()\n const [loading, setLoading] = React.useState(false)\n const [saving, setSaving] = React.useState(false)\n const [options, setOptions] = React.useState<OrderStatusOption[]>([])\n const [customerStatuses, setCustomerStatuses] = React.useState<string[] | null>(null)\n const [addressStatuses, setAddressStatuses] = React.useState<string[] | null>(null)\n\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\n const translations = React.useMemo(\n () => ({\n title: t('sales.config.orderEditing.title', 'Order editing guards'),\n description: t(\n 'sales.config.orderEditing.description',\n 'Control when customers and addresses can be changed on an order.'\n ),\n customerLabel: t('sales.config.orderEditing.customerLabel', 'Edit customer allowed at'),\n addressLabel: t('sales.config.orderEditing.addressLabel', 'Edit addresses allowed at'),\n allowAny: t('sales.config.orderEditing.allowAny', 'Allow at any status'),\n note: t(\n 'sales.config.orderEditing.note',\n 'Changing the customer clears assigned addresses to avoid mixing contact data.'\n ),\n actions: {\n refresh: t('sales.config.orderEditing.actions.refresh', 'Refresh'),\n refreshing: t('sales.config.orderEditing.actions.refreshing', 'Refreshing\u2026'),\n save: t('sales.config.orderEditing.actions.save', 'Save settings'),\n },\n messages: {\n loadError: t('sales.config.orderEditing.errors.load', 'Failed to load order editing settings.'),\n saveError: t('sales.config.orderEditing.errors.save', 'Failed to save order editing settings.'),\n saved: t('sales.config.orderEditing.success.save', 'Order editing settings saved.'),\n },\n }),\n [t]\n )\n\n const loadSettings = React.useCallback(async () => {\n setLoading(true)\n try {\n const call = await apiCall<SettingsResponse>('/api/sales/settings/order-editing')\n if (!call.ok) {\n flash(translations.messages.loadError, 'error')\n return\n }\n setOptions(Array.isArray(call.result?.orderStatuses) ? call.result.orderStatuses : [])\n setCustomerStatuses(normalizeStatusList(call.result?.orderCustomerEditableStatuses))\n setAddressStatuses(normalizeStatusList(call.result?.orderAddressEditableStatuses))\n } catch (err) {\n console.error('sales.order-editing-settings.load failed', err)\n flash(translations.messages.loadError, 'error')\n } finally {\n setLoading(false)\n }\n }, [translations.messages.loadError])\n\n React.useEffect(() => {\n void loadSettings()\n }, [loadSettings, scopeVersion])\n\n const toggleAny = React.useCallback((kind: 'customer' | 'address') => {\n if (kind === 'customer') {\n setCustomerStatuses((prev) => (prev === null ? [] : null))\n } else {\n setAddressStatuses((prev) => (prev === null ? [] : null))\n }\n }, [])\n\n const toggleStatus = React.useCallback((kind: 'customer' | 'address', value: string) => {\n const updater = kind === 'customer' ? setCustomerStatuses : setAddressStatuses\n updater((prev) => {\n const current = prev ?? []\n if (current.includes(value)) {\n return current.filter((entry) => entry !== value)\n }\n return [...current, value]\n })\n }, [])\n\n const handleSubmit = React.useCallback(async () => {\n setSaving(true)\n try {\n const payload = {\n orderCustomerEditableStatuses: customerStatuses,\n orderAddressEditableStatuses: addressStatuses,\n }\n const call = await runMutation({\n // optimistic-lock-exempt: single-row tenant order-editing settings blob \u2014 no per-record version / concurrent record edit\n operation: async () => {\n const response = await apiCall<SettingsResponse>('/api/sales/settings/order-editing', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!response.ok) {\n throw new Error(translations.messages.saveError)\n }\n return response\n },\n context: {\n formId: SAVE_CONTEXT_ID,\n resourceKind: 'sales.settings',\n retryLastMutation,\n },\n mutationPayload: payload,\n })\n setCustomerStatuses(normalizeStatusList(call.result?.orderCustomerEditableStatuses))\n setAddressStatuses(normalizeStatusList(call.result?.orderAddressEditableStatuses))\n setOptions(Array.isArray(call.result?.orderStatuses) ? call.result.orderStatuses : [])\n flash(translations.messages.saved, 'success')\n } catch (err) {\n console.error('sales.order-editing-settings.save failed', err)\n flash(translations.messages.saveError, 'error')\n } finally {\n setSaving(false)\n }\n }, [addressStatuses, customerStatuses, retryLastMutation, runMutation, translations.messages.saveError, translations.messages.saved])\n\n const renderStatusList = React.useCallback(\n (kind: 'customer' | 'address', values: string[] | null, label: string) => {\n const allowedAny = values === null\n return (\n <div className=\"space-y-3 rounded-none border bg-card/30 p-4\">\n <div className=\"flex items-center justify-between gap-2\">\n <div className=\"space-y-1\">\n <p className=\"text-sm font-medium\">{label}</p>\n <p className=\"text-xs text-muted-foreground\">\n {allowedAny\n ? t('sales.config.orderEditing.anyStatus', 'Allowed at any status')\n : t('sales.config.orderEditing.pickStatuses', 'Select statuses that allow this change')}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <Switch checked={allowedAny} onCheckedChange={() => toggleAny(kind)} disabled={loading || saving} />\n <span className=\"text-xs text-muted-foreground\">{translations.allowAny}</span>\n </div>\n </div>\n {!allowedAny && (\n <div className=\"grid gap-2 sm:grid-cols-2 lg:grid-cols-3\">\n {options.map((status) => {\n const checked = values?.includes(status.value) ?? false\n return (\n <label\n key={status.id}\n className=\"flex items-center gap-2 rounded-none border bg-background p-2 text-sm\"\n >\n <input\n type=\"checkbox\"\n className=\"h-4 w-4 rounded border\"\n checked={checked}\n onChange={() => toggleStatus(kind, status.value)}\n disabled={loading || saving}\n />\n <span className=\"truncate\" title={status.label || status.value}>\n {status.label || status.value}\n </span>\n <Badge variant=\"outline\" className=\"ml-auto text-overline uppercase tracking-wide\">\n {status.value}\n </Badge>\n </label>\n )\n })}\n </div>\n )}\n </div>\n )\n },\n [loading, options, saving, t, toggleAny, toggleStatus, translations.allowAny]\n )\n\n return (\n <section className=\"space-y-4 rounded-none border bg-card/30 p-5 shadow-sm !rounded-none\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <h2 className=\"text-lg font-semibold\">{translations.title}</h2>\n <p className=\"text-sm text-muted-foreground\">{translations.description}</p>\n </div>\n <div className=\"flex gap-2\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n type=\"button\"\n className=\"rounded-none border-0 shadow-none hover:bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0\"\n onClick={() => void loadSettings()}\n disabled={loading || saving}\n aria-label={translations.actions.refresh}\n >\n {loading ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCw className=\"h-4 w-4\" />}\n <span className=\"sr-only\">{loading ? translations.actions.refreshing : translations.actions.refresh}</span>\n </Button>\n </div>\n </div>\n <p className=\"text-xs text-muted-foreground\">{translations.note}</p>\n <div className=\"space-y-3\">\n {renderStatusList('customer', customerStatuses, translations.customerLabel)}\n {renderStatusList('address', addressStatuses, translations.addressLabel)}\n </div>\n <div className=\"flex justify-end gap-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"rounded-none border-0 shadow-none hover:bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0\"\n onClick={() => void loadSettings()}\n disabled={loading || saving}\n aria-label={translations.actions.refresh}\n >\n {loading ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCw className=\"h-4 w-4\" />}\n <span className=\"sr-only\">{loading ? translations.actions.refreshing : translations.actions.refresh}</span>\n </Button>\n <Button type=\"button\" onClick={() => void handleSubmit()} disabled={loading || saving}>\n {translations.actions.save}\n </Button>\n </div>\n </section>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }