@open-mercato/core 0.6.6-develop.6205.1.109e4b6a84 → 0.6.6-develop.6227.1.2695efff30

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 (198) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/currencies/backend/currencies/[id]/page.js +33 -8
  3. package/dist/modules/currencies/backend/currencies/[id]/page.js.map +2 -2
  4. package/dist/modules/currencies/backend/currencies/page.js +63 -27
  5. package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
  6. package/dist/modules/currencies/backend/exchange-rates/page.js +35 -14
  7. package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
  8. package/dist/modules/currencies/components/CurrencyFetchingConfig.js +104 -41
  9. package/dist/modules/currencies/components/CurrencyFetchingConfig.js.map +2 -2
  10. package/dist/modules/customer_accounts/api/admin/roles/[id]/acl.js +57 -8
  11. package/dist/modules/customer_accounts/api/admin/roles/[id]/acl.js.map +2 -2
  12. package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js +5 -2
  13. package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js.map +2 -2
  14. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js +30 -14
  15. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js.map +2 -2
  16. package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js +29 -19
  17. package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js.map +2 -2
  18. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js +166 -64
  19. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js.map +2 -2
  20. package/dist/modules/customers/components/AddressFormatSettings.js +31 -16
  21. package/dist/modules/customers/components/AddressFormatSettings.js.map +2 -2
  22. package/dist/modules/customers/components/DictionarySettings.js +57 -31
  23. package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
  24. package/dist/modules/customers/components/PipelineSettings.js +156 -76
  25. package/dist/modules/customers/components/PipelineSettings.js.map +2 -2
  26. package/dist/modules/customers/components/calendar/AgendaList.js +10 -4
  27. package/dist/modules/customers/components/calendar/AgendaList.js.map +2 -2
  28. package/dist/modules/customers/components/calendar/CalendarSettingsModal.js +18 -2
  29. package/dist/modules/customers/components/calendar/CalendarSettingsModal.js.map +2 -2
  30. package/dist/modules/customers/components/calendar/MonthGrid.js +5 -2
  31. package/dist/modules/customers/components/calendar/MonthGrid.js.map +2 -2
  32. package/dist/modules/customers/components/calendar/editor/PeopleField.js +6 -0
  33. package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +2 -2
  34. package/dist/modules/customers/components/calendar/editor/RelatedToField.js +2 -0
  35. package/dist/modules/customers/components/calendar/editor/RelatedToField.js.map +2 -2
  36. package/dist/modules/customers/components/calendar/editor/ScheduleSection.js +4 -1
  37. package/dist/modules/customers/components/calendar/editor/ScheduleSection.js.map +2 -2
  38. package/dist/modules/customers/lib/calendar/labels.js +33 -0
  39. package/dist/modules/customers/lib/calendar/labels.js.map +7 -0
  40. package/dist/modules/dashboards/components/WidgetVisibilityEditor.js +32 -11
  41. package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +2 -2
  42. package/dist/modules/data_sync/api/mappings/[id]/route.js +59 -0
  43. package/dist/modules/data_sync/api/mappings/[id]/route.js.map +2 -2
  44. package/dist/modules/data_sync/api/mappings/route.js +44 -0
  45. package/dist/modules/data_sync/api/mappings/route.js.map +2 -2
  46. package/dist/modules/data_sync/api/run.js +31 -0
  47. package/dist/modules/data_sync/api/run.js.map +2 -2
  48. package/dist/modules/data_sync/api/runs/[id]/cancel.js +31 -0
  49. package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
  50. package/dist/modules/data_sync/api/runs/[id]/retry.js +31 -0
  51. package/dist/modules/data_sync/api/runs/[id]/retry.js.map +2 -2
  52. package/dist/modules/data_sync/api/schedules/[id]/route.js +59 -3
  53. package/dist/modules/data_sync/api/schedules/[id]/route.js.map +2 -2
  54. package/dist/modules/data_sync/api/schedules/route.js +33 -4
  55. package/dist/modules/data_sync/api/schedules/route.js.map +2 -2
  56. package/dist/modules/directory/api/organization-branding/route.js +54 -2
  57. package/dist/modules/directory/api/organization-branding/route.js.map +2 -2
  58. package/dist/modules/directory/backend/directory/branding/page.js +15 -9
  59. package/dist/modules/directory/backend/directory/branding/page.js.map +2 -2
  60. package/dist/modules/directory/backend/directory/organizations/page.js +28 -9
  61. package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
  62. package/dist/modules/directory/backend/directory/tenants/page.js +29 -13
  63. package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
  64. package/dist/modules/entities/api/definitions.batch.js +15 -0
  65. package/dist/modules/entities/api/definitions.batch.js.map +2 -2
  66. package/dist/modules/entities/api/definitions.js +26 -0
  67. package/dist/modules/entities/api/definitions.js.map +2 -2
  68. package/dist/modules/entities/api/definitions.mutation-guard.js +57 -0
  69. package/dist/modules/entities/api/definitions.mutation-guard.js.map +7 -0
  70. package/dist/modules/entities/api/definitions.restore.js +15 -0
  71. package/dist/modules/entities/api/definitions.restore.js.map +2 -2
  72. package/dist/modules/entities/api/entities.js +31 -3
  73. package/dist/modules/entities/api/entities.js.map +2 -2
  74. package/dist/modules/entities/api/records.js +18 -0
  75. package/dist/modules/entities/api/records.js.map +2 -2
  76. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +28 -10
  77. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
  78. package/dist/modules/feature_toggles/api/overrides/route.js +38 -1
  79. package/dist/modules/feature_toggles/api/overrides/route.js.map +2 -2
  80. package/dist/modules/inbox_ops/api/emails/[id]/route.js +30 -0
  81. package/dist/modules/inbox_ops/api/emails/[id]/route.js.map +2 -2
  82. package/dist/modules/inbox_ops/api/proposals/[id]/accept-all/route.js +30 -0
  83. package/dist/modules/inbox_ops/api/proposals/[id]/accept-all/route.js.map +2 -2
  84. package/dist/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.js +31 -0
  85. package/dist/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.js.map +2 -2
  86. package/dist/modules/inbox_ops/api/proposals/[id]/categorize/route.js +31 -0
  87. package/dist/modules/inbox_ops/api/proposals/[id]/categorize/route.js.map +2 -2
  88. package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsActions.js +68 -39
  89. package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsActions.js.map +2 -2
  90. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +14 -4
  91. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +2 -2
  92. package/dist/modules/payment_gateways/api/cancel/route.js +37 -0
  93. package/dist/modules/payment_gateways/api/cancel/route.js.map +2 -2
  94. package/dist/modules/payment_gateways/api/capture/route.js +37 -0
  95. package/dist/modules/payment_gateways/api/capture/route.js.map +2 -2
  96. package/dist/modules/payment_gateways/api/guards.js +31 -0
  97. package/dist/modules/payment_gateways/api/guards.js.map +7 -0
  98. package/dist/modules/payment_gateways/api/refund/route.js +37 -0
  99. package/dist/modules/payment_gateways/api/refund/route.js.map +2 -2
  100. package/dist/modules/payment_gateways/api/sessions/route.js +37 -0
  101. package/dist/modules/payment_gateways/api/sessions/route.js.map +2 -2
  102. package/dist/modules/planner/api/availability-date-specific.js +11 -1
  103. package/dist/modules/planner/api/availability-date-specific.js.map +2 -2
  104. package/dist/modules/planner/backend/planner/availability-rulesets/page.js +20 -3
  105. package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
  106. package/dist/modules/planner/commands/availability-date-specific.js +16 -0
  107. package/dist/modules/planner/commands/availability-date-specific.js.map +2 -2
  108. package/dist/modules/planner/components/AvailabilityRulesEditor.js +109 -40
  109. package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
  110. package/dist/modules/query_index/api/purge.js +35 -3
  111. package/dist/modules/query_index/api/purge.js.map +2 -2
  112. package/dist/modules/query_index/api/reindex.js +41 -3
  113. package/dist/modules/query_index/api/reindex.js.map +2 -2
  114. package/dist/modules/query_index/components/QueryIndexesTable.js +57 -24
  115. package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
  116. package/dist/modules/shipping_carriers/api/shipments/route.js +31 -0
  117. package/dist/modules/shipping_carriers/api/shipments/route.js.map +2 -2
  118. package/dist/modules/shipping_carriers/api/tracking/refresh/route.js +55 -0
  119. package/dist/modules/shipping_carriers/api/tracking/refresh/route.js.map +7 -0
  120. package/dist/modules/shipping_carriers/data/validators.js +6 -1
  121. package/dist/modules/shipping_carriers/data/validators.js.map +2 -2
  122. package/dist/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.js +28 -8
  123. package/dist/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.js.map +2 -2
  124. package/dist/modules/shipping_carriers/lib/shipping-service.js +37 -7
  125. package/dist/modules/shipping_carriers/lib/shipping-service.js.map +2 -2
  126. package/dist/modules/shipping_carriers/workers/status-poller.js +1 -1
  127. package/dist/modules/shipping_carriers/workers/status-poller.js.map +2 -2
  128. package/dist/modules/translations/components/TranslationManager.js +49 -20
  129. package/dist/modules/translations/components/TranslationManager.js.map +2 -2
  130. package/package.json +7 -7
  131. package/src/modules/currencies/backend/currencies/[id]/page.tsx +40 -10
  132. package/src/modules/currencies/backend/currencies/page.tsx +68 -29
  133. package/src/modules/currencies/backend/exchange-rates/page.tsx +40 -15
  134. package/src/modules/currencies/components/CurrencyFetchingConfig.tsx +110 -41
  135. package/src/modules/customer_accounts/api/admin/roles/[id]/acl.ts +69 -7
  136. package/src/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.tsx +16 -8
  137. package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +32 -14
  138. package/src/modules/customer_accounts/widgets/injection/account-status/widget.client.tsx +32 -20
  139. package/src/modules/customers/backend/config/customers/pipeline-stages/page.tsx +171 -64
  140. package/src/modules/customers/components/AddressFormatSettings.tsx +39 -19
  141. package/src/modules/customers/components/DictionarySettings.tsx +63 -29
  142. package/src/modules/customers/components/PipelineSettings.tsx +165 -80
  143. package/src/modules/customers/components/calendar/AgendaList.tsx +13 -8
  144. package/src/modules/customers/components/calendar/CalendarSettingsModal.tsx +16 -2
  145. package/src/modules/customers/components/calendar/MonthGrid.tsx +5 -2
  146. package/src/modules/customers/components/calendar/editor/PeopleField.tsx +6 -0
  147. package/src/modules/customers/components/calendar/editor/RelatedToField.tsx +2 -0
  148. package/src/modules/customers/components/calendar/editor/ScheduleSection.tsx +7 -0
  149. package/src/modules/customers/i18n/de.json +11 -7
  150. package/src/modules/customers/i18n/en.json +6 -2
  151. package/src/modules/customers/i18n/es.json +11 -7
  152. package/src/modules/customers/i18n/pl.json +11 -7
  153. package/src/modules/customers/lib/calendar/labels.ts +42 -0
  154. package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +39 -11
  155. package/src/modules/data_sync/api/mappings/[id]/route.ts +63 -0
  156. package/src/modules/data_sync/api/mappings/route.ts +48 -0
  157. package/src/modules/data_sync/api/run.ts +33 -0
  158. package/src/modules/data_sync/api/runs/[id]/cancel.ts +34 -0
  159. package/src/modules/data_sync/api/runs/[id]/retry.ts +33 -0
  160. package/src/modules/data_sync/api/schedules/[id]/route.ts +64 -2
  161. package/src/modules/data_sync/api/schedules/route.ts +36 -4
  162. package/src/modules/directory/api/organization-branding/route.ts +61 -0
  163. package/src/modules/directory/backend/directory/branding/page.tsx +16 -10
  164. package/src/modules/directory/backend/directory/organizations/page.tsx +35 -8
  165. package/src/modules/directory/backend/directory/tenants/page.tsx +37 -13
  166. package/src/modules/entities/api/definitions.batch.ts +17 -0
  167. package/src/modules/entities/api/definitions.mutation-guard.ts +80 -0
  168. package/src/modules/entities/api/definitions.restore.ts +17 -0
  169. package/src/modules/entities/api/definitions.ts +30 -0
  170. package/src/modules/entities/api/entities.ts +35 -3
  171. package/src/modules/entities/api/records.ts +20 -0
  172. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +33 -10
  173. package/src/modules/feature_toggles/api/overrides/route.ts +44 -1
  174. package/src/modules/inbox_ops/api/emails/[id]/route.ts +32 -0
  175. package/src/modules/inbox_ops/api/proposals/[id]/accept-all/route.ts +32 -0
  176. package/src/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.ts +33 -0
  177. package/src/modules/inbox_ops/api/proposals/[id]/categorize/route.ts +33 -0
  178. package/src/modules/messages/components/message-detail/hooks/useMessageDetailsActions.ts +80 -42
  179. package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +21 -4
  180. package/src/modules/payment_gateways/api/cancel/route.ts +39 -0
  181. package/src/modules/payment_gateways/api/capture/route.ts +39 -0
  182. package/src/modules/payment_gateways/api/guards.ts +59 -0
  183. package/src/modules/payment_gateways/api/refund/route.ts +39 -0
  184. package/src/modules/payment_gateways/api/sessions/route.ts +40 -0
  185. package/src/modules/planner/api/availability-date-specific.ts +10 -0
  186. package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +26 -5
  187. package/src/modules/planner/commands/availability-date-specific.ts +24 -0
  188. package/src/modules/planner/components/AvailabilityRulesEditor.tsx +122 -41
  189. package/src/modules/query_index/api/purge.ts +37 -3
  190. package/src/modules/query_index/api/reindex.ts +43 -3
  191. package/src/modules/query_index/components/QueryIndexesTable.tsx +66 -24
  192. package/src/modules/shipping_carriers/api/shipments/route.ts +31 -0
  193. package/src/modules/shipping_carriers/api/tracking/refresh/route.ts +53 -0
  194. package/src/modules/shipping_carriers/data/validators.ts +5 -0
  195. package/src/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.ts +29 -8
  196. package/src/modules/shipping_carriers/lib/shipping-service.ts +43 -7
  197. package/src/modules/shipping_carriers/workers/status-poller.ts +1 -1
  198. package/src/modules/translations/components/TranslationManager.tsx +65 -21
@@ -16,6 +16,7 @@ import {
16
16
  import { LoadingMessage, ErrorMessage } from "@open-mercato/ui/backend/detail";
17
17
  import { apiCall, apiCallOrThrow, withScopedApiRequestHeaders } from "@open-mercato/ui/backend/utils/apiCall";
18
18
  import { createCrud, deleteCrud, updateCrud } from "@open-mercato/ui/backend/utils/crud";
19
+ import { useGuardedMutation } from "@open-mercato/ui/backend/injection/useGuardedMutation";
19
20
  import { buildOptimisticLockHeader } from "@open-mercato/ui/backend/utils/optimisticLock";
20
21
  import { flash } from "@open-mercato/ui/backend/FlashMessages";
21
22
  import { Spinner } from "@open-mercato/ui/primitives/spinner";
@@ -28,6 +29,7 @@ import {
28
29
  import { useT } from "@open-mercato/shared/lib/i18n/context";
29
30
  import { useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
30
31
  import { normalizeCrudServerError } from "@open-mercato/ui/backend/utils/serverErrors";
32
+ import { surfaceRecordConflict } from "@open-mercato/ui/backend/conflicts";
31
33
  import { parseAvailabilityRuleWindow } from "@open-mercato/core/modules/planner/lib/availabilitySchedule";
32
34
  import { deleteAvailabilityRuleSet } from "@open-mercato/core/modules/planner/lib/deleteAvailabilityRuleSet";
33
35
  import { CrudForm } from "@open-mercato/ui/backend/CrudForm";
@@ -43,6 +45,22 @@ function withOptimisticLockForRule(rule, mutation) {
43
45
  function withOptimisticLockForRuleSet(ruleSet, mutation) {
44
46
  return withScopedApiRequestHeaders(buildOptimisticLockHeader(ruleSet?.updatedAt ?? ruleSet?.updated_at ?? null), mutation);
45
47
  }
48
+ function resolveDateSpecificLockToken(rules) {
49
+ let latestMs = Number.NEGATIVE_INFINITY;
50
+ let latestIso = null;
51
+ for (const rule of rules) {
52
+ const raw = rule.updatedAt ?? rule.updated_at ?? null;
53
+ if (!raw) continue;
54
+ const ms = Date.parse(raw);
55
+ if (!Number.isFinite(ms) || ms <= latestMs) continue;
56
+ latestMs = ms;
57
+ latestIso = raw;
58
+ }
59
+ return latestIso;
60
+ }
61
+ function withOptimisticLockForDateSpecific(rules, mutation) {
62
+ return withScopedApiRequestHeaders(buildOptimisticLockHeader(resolveDateSpecificLockToken(rules)), mutation);
63
+ }
46
64
  const DAY_LABELS = [
47
65
  { code: "SU", short: "S", nameKey: "schedule.weekday.sunday", fallback: "Sunday" },
48
66
  { code: "MO", short: "M", nameKey: "schedule.weekday.monday", fallback: "Monday" },
@@ -289,6 +307,19 @@ function AvailabilityRulesEditor({
289
307
  }) {
290
308
  const t = useT();
291
309
  const { confirm, ConfirmDialogElement } = useConfirmDialog();
310
+ const mutationContextId = "planner-availability-rules-editor";
311
+ const { runMutation, retryLastMutation } = useGuardedMutation({
312
+ contextId: mutationContextId,
313
+ blockedMessage: t("ui.forms.flash.saveBlocked", "Save blocked by validation")
314
+ });
315
+ const mutationContext = React.useMemo(
316
+ () => ({
317
+ formId: mutationContextId,
318
+ resourceKind: "planner.availability",
319
+ retryLastMutation
320
+ }),
321
+ [mutationContextId, retryLastMutation]
322
+ );
292
323
  const isReadOnly = Boolean(readOnly);
293
324
  const canManageUnavailability = allowUnavailability ?? true;
294
325
  const dialogRef = React.useRef(null);
@@ -692,16 +723,20 @@ function AvailabilityRulesEditor({
692
723
  setIsWeeklyAutoSaving(options?.silentSuccess === true);
693
724
  try {
694
725
  const windows = buildWeeklyPayload(normalizeWeeklyWindows(weeklyWindowsRef.current));
695
- await apiCallOrThrow("/api/planner/availability-weekly", {
696
- method: "POST",
697
- headers: { "Content-Type": "application/json" },
698
- body: JSON.stringify({
699
- subjectType: subjectForRules,
700
- subjectId: subjectIdForRules,
701
- timezone,
702
- windows
703
- })
704
- }, { errorMessage: listLabels.saveWeeklyError });
726
+ await runMutation({
727
+ operation: () => apiCallOrThrow("/api/planner/availability-weekly", {
728
+ method: "POST",
729
+ headers: { "Content-Type": "application/json" },
730
+ body: JSON.stringify({
731
+ subjectType: subjectForRules,
732
+ subjectId: subjectIdForRules,
733
+ timezone,
734
+ windows
735
+ })
736
+ }, { errorMessage: listLabels.saveWeeklyError }),
737
+ context: mutationContext,
738
+ mutationPayload: { action: "save-weekly", subjectType: subjectForRules, subjectId: subjectIdForRules }
739
+ });
705
740
  lastSavedWeeklyKeyRef.current = weeklyKey;
706
741
  weeklyDirtyRef.current = false;
707
742
  if (!options?.silentSuccess) {
@@ -729,7 +764,9 @@ function AvailabilityRulesEditor({
729
764
  usingRuleSet,
730
765
  weeklyHasErrors,
731
766
  weeklyKey,
732
- isReadOnly
767
+ isReadOnly,
768
+ mutationContext,
769
+ runMutation
733
770
  ]);
734
771
  React.useEffect(() => {
735
772
  saveWeeklyRef.current = saveWeeklyHours;
@@ -795,7 +832,11 @@ function AvailabilityRulesEditor({
795
832
  })));
796
833
  }
797
834
  if (updates.length) {
798
- await Promise.all(updates);
835
+ await runMutation({
836
+ operation: () => Promise.all(updates),
837
+ context: mutationContext,
838
+ mutationPayload: { action: "update-timezone", timezone: trimmedTimezone }
839
+ });
799
840
  await refreshAvailability();
800
841
  await refreshRuleSetRules();
801
842
  }
@@ -816,7 +857,9 @@ function AvailabilityRulesEditor({
816
857
  effectiveRulesetId,
817
858
  subjectId,
818
859
  subjectType,
819
- isReadOnly
860
+ isReadOnly,
861
+ mutationContext,
862
+ runMutation
820
863
  ]);
821
864
  React.useEffect(() => {
822
865
  if (!timezoneDirty) return;
@@ -853,14 +896,18 @@ function AvailabilityRulesEditor({
853
896
  kind: rule.kind ?? "availability",
854
897
  note: rule.note ?? null
855
898
  }));
856
- await Promise.all(creations);
899
+ await runMutation({
900
+ operation: () => Promise.all(creations),
901
+ context: mutationContext,
902
+ mutationPayload: { action: "customize", count: creations.length }
903
+ });
857
904
  setCustomOverridesEnabled(true);
858
905
  await refreshAvailability();
859
906
  } catch (error2) {
860
907
  const message = error2 instanceof Error ? error2.message : listLabels.saveWeeklyError;
861
908
  flash(message, "error");
862
909
  }
863
- }, [effectiveRulesetId, listLabels.saveWeeklyError, refreshAvailability, rulesetRules, subjectId, subjectType, isReadOnly]);
910
+ }, [effectiveRulesetId, listLabels.saveWeeklyError, refreshAvailability, rulesetRules, subjectId, subjectType, isReadOnly, mutationContext, runMutation]);
864
911
  const handleResetToRuleSet = React.useCallback(async () => {
865
912
  if (isReadOnly) return;
866
913
  if (!effectiveRulesetId) return;
@@ -874,31 +921,39 @@ function AvailabilityRulesEditor({
874
921
  try {
875
922
  const idsToDelete = selectCustomRuleIdsToDelete("reset", availabilityRules);
876
923
  const idsToDeleteSet = new Set(idsToDelete);
877
- await Promise.all(
878
- availabilityRules.filter((rule) => idsToDeleteSet.has(rule.id)).map((rule) => withOptimisticLockForRule(
879
- rule,
880
- () => deleteCrud("planner/availability", rule.id, { errorMessage: listLabels.saveWeeklyError })
881
- ))
882
- );
924
+ await runMutation({
925
+ operation: () => Promise.all(
926
+ availabilityRules.filter((rule) => idsToDeleteSet.has(rule.id)).map((rule) => withOptimisticLockForRule(
927
+ rule,
928
+ () => deleteCrud("planner/availability", rule.id, { errorMessage: listLabels.saveWeeklyError })
929
+ ))
930
+ ),
931
+ context: mutationContext,
932
+ mutationPayload: { action: "reset-to-ruleset", ids: idsToDelete }
933
+ });
883
934
  setCustomOverridesEnabled(false);
884
935
  await refreshAvailability();
885
936
  } catch (error2) {
886
937
  const message = error2 instanceof Error ? error2.message : listLabels.saveWeeklyError;
887
938
  flash(message, "error");
888
939
  }
889
- }, [availabilityRules, confirm, effectiveRulesetId, listLabels.ruleSetConfirm, listLabels.saveWeeklyError, refreshAvailability, isReadOnly]);
940
+ }, [availabilityRules, confirm, effectiveRulesetId, listLabels.ruleSetConfirm, listLabels.saveWeeklyError, refreshAvailability, isReadOnly, mutationContext, runMutation]);
890
941
  const handleRuleSetChange = React.useCallback(async (nextId) => {
891
942
  if (isReadOnly) return;
892
943
  if (!onRulesetChange) return;
893
944
  const idsToDelete = selectCustomRuleIdsToDelete("switch", availabilityRules);
894
945
  if (idsToDelete.length) {
895
946
  const idsToDeleteSet = new Set(idsToDelete);
896
- await Promise.all(
897
- availabilityRules.filter((rule) => idsToDeleteSet.has(rule.id)).map((rule) => withOptimisticLockForRule(
898
- rule,
899
- () => deleteCrud("planner/availability", rule.id, { errorMessage: listLabels.saveWeeklyError })
900
- ))
901
- );
947
+ await runMutation({
948
+ operation: () => Promise.all(
949
+ availabilityRules.filter((rule) => idsToDeleteSet.has(rule.id)).map((rule) => withOptimisticLockForRule(
950
+ rule,
951
+ () => deleteCrud("planner/availability", rule.id, { errorMessage: listLabels.saveWeeklyError })
952
+ ))
953
+ ),
954
+ context: mutationContext,
955
+ mutationPayload: { action: "switch-ruleset", ids: idsToDelete }
956
+ });
902
957
  }
903
958
  setSelectedRulesetId(nextId);
904
959
  setCustomOverridesEnabled(false);
@@ -915,7 +970,9 @@ function AvailabilityRulesEditor({
915
970
  listLabels.saveWeeklyError,
916
971
  onRulesetChange,
917
972
  refreshAvailability,
918
- isReadOnly
973
+ isReadOnly,
974
+ mutationContext,
975
+ runMutation
919
976
  ]);
920
977
  const handleDeleteRuleSet = React.useCallback(async () => {
921
978
  if (isReadOnly || !onRulesetChange || !rulesetId) return;
@@ -929,7 +986,11 @@ function AvailabilityRulesEditor({
929
986
  variant: "destructive"
930
987
  }),
931
988
  deleteRuleSet: async (id) => {
932
- await withOptimisticLockForRuleSet(selected, () => deleteCrud("planner/availability-rule-sets", id, { errorMessage: listLabels.ruleSetDeleteError }));
989
+ await runMutation({
990
+ operation: () => withOptimisticLockForRuleSet(selected, () => deleteCrud("planner/availability-rule-sets", id, { errorMessage: listLabels.ruleSetDeleteError })),
991
+ context: mutationContext,
992
+ mutationPayload: { action: "delete-ruleset", id }
993
+ });
933
994
  },
934
995
  clearAssignment: async () => {
935
996
  setCustomOverridesEnabled(false);
@@ -955,7 +1016,9 @@ function AvailabilityRulesEditor({
955
1016
  refreshAvailability,
956
1017
  refreshRuleSets,
957
1018
  ruleSets,
958
- rulesetId
1019
+ rulesetId,
1020
+ mutationContext,
1021
+ runMutation
959
1022
  ]);
960
1023
  const ruleSetFormSchema = React.useMemo(
961
1024
  () => z.object({
@@ -1134,11 +1197,11 @@ function AvailabilityRulesEditor({
1134
1197
  unavailabilityReasonEntryId: editorUnavailable ? reasonEntryId : null,
1135
1198
  unavailabilityReasonValue: editorUnavailable ? reasonValue : null
1136
1199
  };
1137
- await apiCallOrThrow("/api/planner/availability-date-specific", {
1200
+ await withOptimisticLockForDateSpecific(editorRules, () => apiCallOrThrow("/api/planner/availability-date-specific", {
1138
1201
  method: "POST",
1139
1202
  headers: { "Content-Type": "application/json" },
1140
1203
  body: JSON.stringify(payload)
1141
- }, { errorMessage: listLabels.saveDateError });
1204
+ }, { errorMessage: listLabels.saveDateError }));
1142
1205
  } else {
1143
1206
  const rulesToDelete = editorRules;
1144
1207
  const uniqueRulesById = new Map(rulesToDelete.map((rule) => [rule.id, rule]));
@@ -1172,6 +1235,7 @@ function AvailabilityRulesEditor({
1172
1235
  await refreshAvailability();
1173
1236
  await refreshRuleSetRules();
1174
1237
  } catch (error2) {
1238
+ if (surfaceRecordConflict(error2, t)) return;
1175
1239
  const message = error2 instanceof Error ? error2.message : listLabels.saveDateError;
1176
1240
  flash(message, "error");
1177
1241
  }
@@ -1197,7 +1261,8 @@ function AvailabilityRulesEditor({
1197
1261
  timezone,
1198
1262
  usingRuleSet,
1199
1263
  canManageUnavailability,
1200
- isReadOnly
1264
+ isReadOnly,
1265
+ t
1201
1266
  ]);
1202
1267
  const handleSlotClick = React.useCallback((slot) => {
1203
1268
  if (usingRuleSet || isReadOnly) return;
@@ -1453,12 +1518,16 @@ function AvailabilityRulesEditor({
1453
1518
  variant: "outline",
1454
1519
  size: "icon",
1455
1520
  onClick: async () => {
1456
- await Promise.all(
1457
- rules.map((rule) => withOptimisticLockForRule(
1458
- rule,
1459
- () => deleteCrud("planner/availability", rule.id)
1460
- ))
1461
- );
1521
+ await runMutation({
1522
+ operation: () => Promise.all(
1523
+ rules.map((rule) => withOptimisticLockForRule(
1524
+ rule,
1525
+ () => deleteCrud("planner/availability", rule.id)
1526
+ ))
1527
+ ),
1528
+ context: mutationContext,
1529
+ mutationPayload: { action: "delete-date-rules", count: rules.length }
1530
+ });
1462
1531
  await refreshAvailability();
1463
1532
  await refreshRuleSetRules();
1464
1533
  },