@open-mercato/core 0.6.6-develop.6205.1.109e4b6a84 → 0.6.6-develop.6228.1.cf483c68b5

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 (205) 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/dictionaries/fields/dictionary.js +29 -4
  57. package/dist/modules/dictionaries/fields/dictionary.js.map +2 -2
  58. package/dist/modules/directory/api/organization-branding/route.js +54 -2
  59. package/dist/modules/directory/api/organization-branding/route.js.map +2 -2
  60. package/dist/modules/directory/backend/directory/branding/page.js +15 -9
  61. package/dist/modules/directory/backend/directory/branding/page.js.map +2 -2
  62. package/dist/modules/directory/backend/directory/organizations/page.js +28 -9
  63. package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
  64. package/dist/modules/directory/backend/directory/tenants/page.js +29 -13
  65. package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
  66. package/dist/modules/entities/api/definitions.batch.js +15 -0
  67. package/dist/modules/entities/api/definitions.batch.js.map +2 -2
  68. package/dist/modules/entities/api/definitions.js +26 -0
  69. package/dist/modules/entities/api/definitions.js.map +2 -2
  70. package/dist/modules/entities/api/definitions.mutation-guard.js +57 -0
  71. package/dist/modules/entities/api/definitions.mutation-guard.js.map +7 -0
  72. package/dist/modules/entities/api/definitions.restore.js +15 -0
  73. package/dist/modules/entities/api/definitions.restore.js.map +2 -2
  74. package/dist/modules/entities/api/entities.js +31 -3
  75. package/dist/modules/entities/api/entities.js.map +2 -2
  76. package/dist/modules/entities/api/records.js +18 -0
  77. package/dist/modules/entities/api/records.js.map +2 -2
  78. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +28 -10
  79. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
  80. package/dist/modules/feature_toggles/api/overrides/route.js +38 -1
  81. package/dist/modules/feature_toggles/api/overrides/route.js.map +2 -2
  82. package/dist/modules/inbox_ops/api/emails/[id]/route.js +30 -0
  83. package/dist/modules/inbox_ops/api/emails/[id]/route.js.map +2 -2
  84. package/dist/modules/inbox_ops/api/proposals/[id]/accept-all/route.js +30 -0
  85. package/dist/modules/inbox_ops/api/proposals/[id]/accept-all/route.js.map +2 -2
  86. package/dist/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.js +31 -0
  87. package/dist/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.js.map +2 -2
  88. package/dist/modules/inbox_ops/api/proposals/[id]/categorize/route.js +31 -0
  89. package/dist/modules/inbox_ops/api/proposals/[id]/categorize/route.js.map +2 -2
  90. package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsActions.js +68 -39
  91. package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsActions.js.map +2 -2
  92. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +14 -4
  93. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +2 -2
  94. package/dist/modules/payment_gateways/api/cancel/route.js +37 -0
  95. package/dist/modules/payment_gateways/api/cancel/route.js.map +2 -2
  96. package/dist/modules/payment_gateways/api/capture/route.js +37 -0
  97. package/dist/modules/payment_gateways/api/capture/route.js.map +2 -2
  98. package/dist/modules/payment_gateways/api/guards.js +31 -0
  99. package/dist/modules/payment_gateways/api/guards.js.map +7 -0
  100. package/dist/modules/payment_gateways/api/refund/route.js +37 -0
  101. package/dist/modules/payment_gateways/api/refund/route.js.map +2 -2
  102. package/dist/modules/payment_gateways/api/sessions/route.js +37 -0
  103. package/dist/modules/payment_gateways/api/sessions/route.js.map +2 -2
  104. package/dist/modules/planner/api/availability-date-specific.js +11 -1
  105. package/dist/modules/planner/api/availability-date-specific.js.map +2 -2
  106. package/dist/modules/planner/backend/planner/availability-rulesets/page.js +20 -3
  107. package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
  108. package/dist/modules/planner/commands/availability-date-specific.js +16 -0
  109. package/dist/modules/planner/commands/availability-date-specific.js.map +2 -2
  110. package/dist/modules/planner/components/AvailabilityRulesEditor.js +109 -40
  111. package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
  112. package/dist/modules/query_index/api/purge.js +35 -3
  113. package/dist/modules/query_index/api/purge.js.map +2 -2
  114. package/dist/modules/query_index/api/reindex.js +41 -3
  115. package/dist/modules/query_index/api/reindex.js.map +2 -2
  116. package/dist/modules/query_index/components/QueryIndexesTable.js +57 -24
  117. package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
  118. package/dist/modules/shipping_carriers/api/shipments/route.js +31 -0
  119. package/dist/modules/shipping_carriers/api/shipments/route.js.map +2 -2
  120. package/dist/modules/shipping_carriers/api/tracking/refresh/route.js +55 -0
  121. package/dist/modules/shipping_carriers/api/tracking/refresh/route.js.map +7 -0
  122. package/dist/modules/shipping_carriers/data/validators.js +6 -1
  123. package/dist/modules/shipping_carriers/data/validators.js.map +2 -2
  124. package/dist/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.js +28 -8
  125. package/dist/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.js.map +2 -2
  126. package/dist/modules/shipping_carriers/lib/shipping-service.js +37 -7
  127. package/dist/modules/shipping_carriers/lib/shipping-service.js.map +2 -2
  128. package/dist/modules/shipping_carriers/workers/status-poller.js +1 -1
  129. package/dist/modules/shipping_carriers/workers/status-poller.js.map +2 -2
  130. package/dist/modules/translations/components/TranslationManager.js +49 -20
  131. package/dist/modules/translations/components/TranslationManager.js.map +2 -2
  132. package/package.json +7 -7
  133. package/src/modules/currencies/backend/currencies/[id]/page.tsx +40 -10
  134. package/src/modules/currencies/backend/currencies/page.tsx +68 -29
  135. package/src/modules/currencies/backend/exchange-rates/page.tsx +40 -15
  136. package/src/modules/currencies/components/CurrencyFetchingConfig.tsx +110 -41
  137. package/src/modules/customer_accounts/api/admin/roles/[id]/acl.ts +69 -7
  138. package/src/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.tsx +16 -8
  139. package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +32 -14
  140. package/src/modules/customer_accounts/widgets/injection/account-status/widget.client.tsx +32 -20
  141. package/src/modules/customers/backend/config/customers/pipeline-stages/page.tsx +171 -64
  142. package/src/modules/customers/components/AddressFormatSettings.tsx +39 -19
  143. package/src/modules/customers/components/DictionarySettings.tsx +63 -29
  144. package/src/modules/customers/components/PipelineSettings.tsx +165 -80
  145. package/src/modules/customers/components/calendar/AgendaList.tsx +13 -8
  146. package/src/modules/customers/components/calendar/CalendarSettingsModal.tsx +16 -2
  147. package/src/modules/customers/components/calendar/MonthGrid.tsx +5 -2
  148. package/src/modules/customers/components/calendar/editor/PeopleField.tsx +6 -0
  149. package/src/modules/customers/components/calendar/editor/RelatedToField.tsx +2 -0
  150. package/src/modules/customers/components/calendar/editor/ScheduleSection.tsx +7 -0
  151. package/src/modules/customers/i18n/de.json +11 -7
  152. package/src/modules/customers/i18n/en.json +6 -2
  153. package/src/modules/customers/i18n/es.json +11 -7
  154. package/src/modules/customers/i18n/pl.json +11 -7
  155. package/src/modules/customers/lib/calendar/labels.ts +42 -0
  156. package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +39 -11
  157. package/src/modules/data_sync/api/mappings/[id]/route.ts +63 -0
  158. package/src/modules/data_sync/api/mappings/route.ts +48 -0
  159. package/src/modules/data_sync/api/run.ts +33 -0
  160. package/src/modules/data_sync/api/runs/[id]/cancel.ts +34 -0
  161. package/src/modules/data_sync/api/runs/[id]/retry.ts +33 -0
  162. package/src/modules/data_sync/api/schedules/[id]/route.ts +64 -2
  163. package/src/modules/data_sync/api/schedules/route.ts +36 -4
  164. package/src/modules/dictionaries/fields/dictionary.tsx +31 -8
  165. package/src/modules/dictionaries/i18n/de.json +1 -0
  166. package/src/modules/dictionaries/i18n/en.json +1 -0
  167. package/src/modules/dictionaries/i18n/es.json +1 -0
  168. package/src/modules/dictionaries/i18n/pl.json +1 -0
  169. package/src/modules/directory/api/organization-branding/route.ts +61 -0
  170. package/src/modules/directory/backend/directory/branding/page.tsx +16 -10
  171. package/src/modules/directory/backend/directory/organizations/page.tsx +35 -8
  172. package/src/modules/directory/backend/directory/tenants/page.tsx +37 -13
  173. package/src/modules/entities/api/definitions.batch.ts +17 -0
  174. package/src/modules/entities/api/definitions.mutation-guard.ts +80 -0
  175. package/src/modules/entities/api/definitions.restore.ts +17 -0
  176. package/src/modules/entities/api/definitions.ts +30 -0
  177. package/src/modules/entities/api/entities.ts +35 -3
  178. package/src/modules/entities/api/records.ts +20 -0
  179. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +33 -10
  180. package/src/modules/feature_toggles/api/overrides/route.ts +44 -1
  181. package/src/modules/inbox_ops/api/emails/[id]/route.ts +32 -0
  182. package/src/modules/inbox_ops/api/proposals/[id]/accept-all/route.ts +32 -0
  183. package/src/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.ts +33 -0
  184. package/src/modules/inbox_ops/api/proposals/[id]/categorize/route.ts +33 -0
  185. package/src/modules/messages/components/message-detail/hooks/useMessageDetailsActions.ts +80 -42
  186. package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +21 -4
  187. package/src/modules/payment_gateways/api/cancel/route.ts +39 -0
  188. package/src/modules/payment_gateways/api/capture/route.ts +39 -0
  189. package/src/modules/payment_gateways/api/guards.ts +59 -0
  190. package/src/modules/payment_gateways/api/refund/route.ts +39 -0
  191. package/src/modules/payment_gateways/api/sessions/route.ts +40 -0
  192. package/src/modules/planner/api/availability-date-specific.ts +10 -0
  193. package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +26 -5
  194. package/src/modules/planner/commands/availability-date-specific.ts +24 -0
  195. package/src/modules/planner/components/AvailabilityRulesEditor.tsx +122 -41
  196. package/src/modules/query_index/api/purge.ts +37 -3
  197. package/src/modules/query_index/api/reindex.ts +43 -3
  198. package/src/modules/query_index/components/QueryIndexesTable.tsx +66 -24
  199. package/src/modules/shipping_carriers/api/shipments/route.ts +31 -0
  200. package/src/modules/shipping_carriers/api/tracking/refresh/route.ts +53 -0
  201. package/src/modules/shipping_carriers/data/validators.ts +5 -0
  202. package/src/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.ts +29 -8
  203. package/src/modules/shipping_carriers/lib/shipping-service.ts +43 -7
  204. package/src/modules/shipping_carriers/workers/status-poller.ts +1 -1
  205. package/src/modules/translations/components/TranslationManager.tsx +65 -21
@@ -16,6 +16,7 @@ import { flash } from "@open-mercato/ui/backend/FlashMessages";
16
16
  import { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from "@open-mercato/ui/backend/utils/apiCall";
17
17
  import { buildOptimisticLockHeader } from "@open-mercato/ui/backend/utils/optimisticLock";
18
18
  import { raiseCrudError } from "@open-mercato/ui/backend/utils/serverErrors";
19
+ import { useGuardedMutation } from "@open-mercato/ui/backend/injection/useGuardedMutation";
19
20
  import { useOrganizationScopeVersion } from "@open-mercato/shared/lib/frontend/useOrganizationScope";
20
21
  import { useT } from "@open-mercato/shared/lib/i18n/context";
21
22
  import { useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
@@ -69,6 +70,26 @@ function PipelineSettings() {
69
70
  const [stageDialog, setStageDialog] = React.useState(null);
70
71
  const [stageForm, setStageForm] = React.useState({ label: "", color: null, icon: null });
71
72
  const [submittingStage, setSubmittingStage] = React.useState(false);
73
+ const { runMutation, retryLastMutation } = useGuardedMutation({
74
+ contextId: "customers-pipeline-settings",
75
+ blockedMessage: t("ui.forms.flash.saveBlocked", "Save blocked by validation")
76
+ });
77
+ const pipelineMutationContext = React.useMemo(
78
+ () => ({
79
+ formId: "customers-pipeline-settings",
80
+ resourceKind: "customers.pipeline",
81
+ retryLastMutation
82
+ }),
83
+ [retryLastMutation]
84
+ );
85
+ const stageMutationContext = React.useMemo(
86
+ () => ({
87
+ formId: "customers-pipeline-settings",
88
+ resourceKind: "customers.pipeline_stage",
89
+ retryLastMutation
90
+ }),
91
+ [retryLastMutation]
92
+ );
72
93
  const loadPipelines = React.useCallback(async () => {
73
94
  setLoadingPipelines(true);
74
95
  try {
@@ -124,37 +145,49 @@ function PipelineSettings() {
124
145
  setSubmittingPipeline(true);
125
146
  try {
126
147
  if (pipelineDialog?.mode === "create") {
127
- const res = await apiCall("/api/customers/pipelines", {
128
- method: "POST",
129
- headers: { "content-type": "application/json" },
130
- body: JSON.stringify({ name: pipelineForm.name.trim(), isDefault: pipelineForm.isDefault })
148
+ await runMutation({
149
+ operation: async () => {
150
+ const res = await apiCall("/api/customers/pipelines", {
151
+ method: "POST",
152
+ headers: { "content-type": "application/json" },
153
+ body: JSON.stringify({ name: pipelineForm.name.trim(), isDefault: pipelineForm.isDefault })
154
+ });
155
+ if (!res.ok) {
156
+ await raiseCrudError(res.response, t("customers.pipelines.errors.createFailed", "Failed to create pipeline"));
157
+ }
158
+ },
159
+ context: pipelineMutationContext,
160
+ mutationPayload: { action: "create", name: pipelineForm.name.trim(), isDefault: pipelineForm.isDefault }
131
161
  });
132
- if (!res.ok) {
133
- await raiseCrudError(res.response, t("customers.pipelines.errors.createFailed", "Failed to create pipeline"));
134
- return;
135
- }
136
162
  flash(t("customers.pipelines.flash.created", "Pipeline created"), "success");
137
163
  } else if (pipelineDialog?.mode === "edit") {
138
- const res = await withScopedApiRequestHeaders(
139
- buildOptimisticLockHeader(pipelineDialog.entry.updatedAt),
140
- () => apiCall("/api/customers/pipelines", {
141
- method: "PUT",
142
- headers: { "content-type": "application/json" },
143
- body: JSON.stringify({ id: pipelineDialog.entry.id, name: pipelineForm.name.trim(), isDefault: pipelineForm.isDefault })
144
- })
145
- );
146
- if (!res.ok) {
147
- await raiseCrudError(res.response, t("customers.pipelines.errors.updateFailed", "Failed to update pipeline"));
148
- return;
149
- }
164
+ await runMutation({
165
+ operation: async () => {
166
+ const res = await withScopedApiRequestHeaders(
167
+ buildOptimisticLockHeader(pipelineDialog.entry.updatedAt),
168
+ () => apiCall("/api/customers/pipelines", {
169
+ method: "PUT",
170
+ headers: { "content-type": "application/json" },
171
+ body: JSON.stringify({ id: pipelineDialog.entry.id, name: pipelineForm.name.trim(), isDefault: pipelineForm.isDefault })
172
+ })
173
+ );
174
+ if (!res.ok) {
175
+ await raiseCrudError(res.response, t("customers.pipelines.errors.updateFailed", "Failed to update pipeline"));
176
+ }
177
+ },
178
+ context: pipelineMutationContext,
179
+ mutationPayload: { action: "update", id: pipelineDialog.entry.id, name: pipelineForm.name.trim(), isDefault: pipelineForm.isDefault }
180
+ });
150
181
  flash(t("customers.pipelines.flash.updated", "Pipeline updated"), "success");
151
182
  }
152
183
  setPipelineDialog(null);
153
184
  await loadPipelines();
185
+ } catch {
186
+ return;
154
187
  } finally {
155
188
  setSubmittingPipeline(false);
156
189
  }
157
- }, [pipelineDialog, pipelineForm, loadPipelines, t]);
190
+ }, [pipelineDialog, pipelineForm, loadPipelines, pipelineMutationContext, runMutation, t]);
158
191
  const handleDeletePipeline = React.useCallback(async (pipeline) => {
159
192
  const confirmed = await confirm({
160
193
  title: t("customers.pipelines.confirm.deleteTitle", "Delete pipeline"),
@@ -163,24 +196,35 @@ function PipelineSettings() {
163
196
  variant: "destructive"
164
197
  });
165
198
  if (!confirmed) return;
166
- const res = await withScopedApiRequestHeaders(
167
- buildOptimisticLockHeader(pipeline.updatedAt),
168
- () => apiCall("/api/customers/pipelines", {
169
- method: "DELETE",
170
- headers: { "content-type": "application/json" },
171
- body: JSON.stringify({ id: pipeline.id })
172
- })
173
- );
174
- if (!res.ok) {
175
- const body = res.result ?? {};
176
- const msg = typeof body.error === "string" ? body.error : t("customers.pipelines.errors.deleteFailed", "Failed to delete pipeline");
199
+ try {
200
+ await runMutation({
201
+ operation: async () => {
202
+ const res = await withScopedApiRequestHeaders(
203
+ buildOptimisticLockHeader(pipeline.updatedAt),
204
+ () => apiCall("/api/customers/pipelines", {
205
+ method: "DELETE",
206
+ headers: { "content-type": "application/json" },
207
+ body: JSON.stringify({ id: pipeline.id })
208
+ })
209
+ );
210
+ if (!res.ok) {
211
+ const body = res.result ?? {};
212
+ const msg = typeof body.error === "string" ? body.error : t("customers.pipelines.errors.deleteFailed", "Failed to delete pipeline");
213
+ throw new Error(msg);
214
+ }
215
+ },
216
+ context: pipelineMutationContext,
217
+ mutationPayload: { action: "delete", id: pipeline.id }
218
+ });
219
+ } catch (err) {
220
+ const msg = err instanceof Error && err.message ? err.message : t("customers.pipelines.errors.deleteFailed", "Failed to delete pipeline");
177
221
  flash(msg, "error");
178
222
  return;
179
223
  }
180
224
  flash(t("customers.pipelines.flash.deleted", "Pipeline deleted"), "success");
181
225
  if (expandedPipelineId === pipeline.id) setExpandedPipelineId(null);
182
226
  await loadPipelines();
183
- }, [confirm, expandedPipelineId, loadPipelines, t]);
227
+ }, [confirm, expandedPipelineId, loadPipelines, pipelineMutationContext, runMutation, t]);
184
228
  const openCreateStage = React.useCallback((pipelineId) => {
185
229
  setStageForm({ label: "", color: null, icon: null });
186
230
  setStageDialog({ mode: "create", pipelineId });
@@ -200,38 +244,52 @@ function PipelineSettings() {
200
244
  if (stageForm.color) appearance.color = stageForm.color;
201
245
  if (stageForm.icon) appearance.icon = stageForm.icon;
202
246
  if (stageDialog?.mode === "create") {
203
- const res = await apiCall("/api/customers/pipeline-stages", {
204
- method: "POST",
205
- headers: { "content-type": "application/json" },
206
- body: JSON.stringify({ pipelineId: stageDialog.pipelineId, label: stageForm.label.trim(), ...appearance })
247
+ const targetPipelineId = stageDialog.pipelineId;
248
+ await runMutation({
249
+ operation: async () => {
250
+ const res = await apiCall("/api/customers/pipeline-stages", {
251
+ method: "POST",
252
+ headers: { "content-type": "application/json" },
253
+ body: JSON.stringify({ pipelineId: targetPipelineId, label: stageForm.label.trim(), ...appearance })
254
+ });
255
+ if (!res.ok) {
256
+ await raiseCrudError(res.response, t("customers.pipelines.errors.stageCreateFailed", "Failed to create stage"));
257
+ }
258
+ },
259
+ context: stageMutationContext,
260
+ mutationPayload: { action: "create", pipelineId: targetPipelineId, label: stageForm.label.trim(), ...appearance }
207
261
  });
208
- if (!res.ok) {
209
- await raiseCrudError(res.response, t("customers.pipelines.errors.stageCreateFailed", "Failed to create stage"));
210
- return;
211
- }
212
262
  flash(t("customers.pipelines.flash.stageCreated", "Stage created"), "success");
213
- await loadStages(stageDialog.pipelineId);
263
+ await loadStages(targetPipelineId);
214
264
  } else if (stageDialog?.mode === "edit") {
215
- const res = await withScopedApiRequestHeaders(
216
- buildOptimisticLockHeader(stageDialog.entry.updatedAt),
217
- () => apiCall("/api/customers/pipeline-stages", {
218
- method: "PUT",
219
- headers: { "content-type": "application/json" },
220
- body: JSON.stringify({ id: stageDialog.entry.id, label: stageForm.label.trim(), ...appearance })
221
- })
222
- );
223
- if (!res.ok) {
224
- await raiseCrudError(res.response, t("customers.pipelines.errors.stageUpdateFailed", "Failed to update stage"));
225
- return;
226
- }
265
+ const targetStage = stageDialog.entry;
266
+ await runMutation({
267
+ operation: async () => {
268
+ const res = await withScopedApiRequestHeaders(
269
+ buildOptimisticLockHeader(targetStage.updatedAt),
270
+ () => apiCall("/api/customers/pipeline-stages", {
271
+ method: "PUT",
272
+ headers: { "content-type": "application/json" },
273
+ body: JSON.stringify({ id: targetStage.id, label: stageForm.label.trim(), ...appearance })
274
+ })
275
+ );
276
+ if (!res.ok) {
277
+ await raiseCrudError(res.response, t("customers.pipelines.errors.stageUpdateFailed", "Failed to update stage"));
278
+ }
279
+ },
280
+ context: stageMutationContext,
281
+ mutationPayload: { action: "update", id: targetStage.id, label: stageForm.label.trim(), ...appearance }
282
+ });
227
283
  flash(t("customers.pipelines.flash.stageUpdated", "Stage updated"), "success");
228
- await loadStages(stageDialog.entry.pipelineId);
284
+ await loadStages(targetStage.pipelineId);
229
285
  }
230
286
  setStageDialog(null);
287
+ } catch {
288
+ return;
231
289
  } finally {
232
290
  setSubmittingStage(false);
233
291
  }
234
- }, [stageDialog, stageForm, loadStages, t]);
292
+ }, [stageDialog, stageForm, loadStages, runMutation, stageMutationContext, t]);
235
293
  const handleDeleteStage = React.useCallback(async (stage) => {
236
294
  const confirmed = await confirm({
237
295
  title: t("customers.pipelines.confirm.stageDeleteTitle", "Delete stage"),
@@ -240,23 +298,34 @@ function PipelineSettings() {
240
298
  variant: "destructive"
241
299
  });
242
300
  if (!confirmed) return;
243
- const res = await withScopedApiRequestHeaders(
244
- buildOptimisticLockHeader(stage.updatedAt),
245
- () => apiCall("/api/customers/pipeline-stages", {
246
- method: "DELETE",
247
- headers: { "content-type": "application/json" },
248
- body: JSON.stringify({ id: stage.id })
249
- })
250
- );
251
- if (!res.ok) {
252
- const body = res.result ?? {};
253
- const msg = typeof body.error === "string" ? body.error : t("customers.pipelines.errors.stageDeleteFailed", "Failed to delete stage");
301
+ try {
302
+ await runMutation({
303
+ operation: async () => {
304
+ const res = await withScopedApiRequestHeaders(
305
+ buildOptimisticLockHeader(stage.updatedAt),
306
+ () => apiCall("/api/customers/pipeline-stages", {
307
+ method: "DELETE",
308
+ headers: { "content-type": "application/json" },
309
+ body: JSON.stringify({ id: stage.id })
310
+ })
311
+ );
312
+ if (!res.ok) {
313
+ const body = res.result ?? {};
314
+ const msg = typeof body.error === "string" ? body.error : t("customers.pipelines.errors.stageDeleteFailed", "Failed to delete stage");
315
+ throw new Error(msg);
316
+ }
317
+ },
318
+ context: stageMutationContext,
319
+ mutationPayload: { action: "delete", id: stage.id }
320
+ });
321
+ } catch (err) {
322
+ const msg = err instanceof Error && err.message ? err.message : t("customers.pipelines.errors.stageDeleteFailed", "Failed to delete stage");
254
323
  flash(msg, "error");
255
324
  return;
256
325
  }
257
326
  flash(t("customers.pipelines.flash.stageDeleted", "Stage deleted"), "success");
258
327
  await loadStages(stage.pipelineId);
259
- }, [confirm, loadStages, t]);
328
+ }, [confirm, loadStages, runMutation, stageMutationContext, t]);
260
329
  const handleMoveStage = React.useCallback(async (stage, direction) => {
261
330
  const pipelineStages = stages[stage.pipelineId] ?? [];
262
331
  const idx = pipelineStages.findIndex((s) => s.id === stage.id);
@@ -268,17 +337,28 @@ function PipelineSettings() {
268
337
  reordered[idx] = reordered[swapIdx];
269
338
  reordered[swapIdx] = temp;
270
339
  const orderedStages = reordered.map((s, i) => ({ id: s.id, order: i }));
271
- const res = await apiCall("/api/customers/pipeline-stages/reorder", {
272
- method: "POST",
273
- headers: { "content-type": "application/json" },
274
- body: JSON.stringify({ stages: orderedStages })
275
- });
276
- if (!res.ok) {
277
- flash(t("customers.pipelines.errors.reorderFailed", "Failed to reorder stages"), "error");
340
+ try {
341
+ await runMutation({
342
+ operation: async () => {
343
+ const res = await apiCall("/api/customers/pipeline-stages/reorder", {
344
+ method: "POST",
345
+ headers: { "content-type": "application/json" },
346
+ body: JSON.stringify({ stages: orderedStages })
347
+ });
348
+ if (!res.ok) {
349
+ throw new Error(t("customers.pipelines.errors.reorderFailed", "Failed to reorder stages"));
350
+ }
351
+ },
352
+ context: stageMutationContext,
353
+ mutationPayload: { action: "reorder", stages: orderedStages }
354
+ });
355
+ } catch (err) {
356
+ const msg = err instanceof Error && err.message ? err.message : t("customers.pipelines.errors.reorderFailed", "Failed to reorder stages");
357
+ flash(msg, "error");
278
358
  return;
279
359
  }
280
360
  await loadStages(stage.pipelineId);
281
- }, [stages, loadStages, t]);
361
+ }, [stages, loadStages, runMutation, stageMutationContext, t]);
282
362
  const handleKeyDown = React.useCallback(
283
363
  (handler) => (e) => {
284
364
  if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/customers/components/PipelineSettings.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { Checkbox } from '@open-mercato/ui/primitives/checkbox'\nimport {\n Dialog,\n DialogContent,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport {\n Accordion,\n AccordionContent,\n AccordionItem,\n AccordionTrigger,\n} from '@open-mercato/ui/primitives/accordion'\nimport {\n AppearanceSelector,\n type AppearanceSelectorLabels,\n} from '@open-mercato/core/modules/dictionaries/components/AppearanceSelector'\nimport {\n renderDictionaryColor,\n renderDictionaryIcon,\n} from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'\n\ntype Pipeline = {\n id: string\n name: string\n isDefault: boolean\n organizationId: string\n tenantId: string\n updatedAt?: string | null\n}\n\ntype PipelineStage = {\n id: string\n pipelineId: string\n label: string\n order: number\n color: string | null\n icon: string | null\n updatedAt?: string | null\n}\n\ntype PipelineDialogState =\n | { mode: 'create' }\n | { mode: 'edit'; entry: Pipeline }\n\ntype StageDialogState =\n | { mode: 'create'; pipelineId: string }\n | { mode: 'edit'; entry: PipelineStage }\n\nfunction normalizePipeline(raw: Record<string, unknown>): Pipeline {\n return {\n id: typeof raw.id === 'string' ? raw.id : '',\n name: typeof raw.name === 'string' ? raw.name : '',\n isDefault: raw.isDefault === true || raw.is_default === true,\n organizationId: typeof raw.organizationId === 'string' ? raw.organizationId : (typeof raw.organization_id === 'string' ? raw.organization_id : ''),\n tenantId: typeof raw.tenantId === 'string' ? raw.tenantId : (typeof raw.tenant_id === 'string' ? raw.tenant_id : ''),\n updatedAt: typeof raw.updatedAt === 'string' ? raw.updatedAt : (typeof raw.updated_at === 'string' ? raw.updated_at : null),\n }\n}\n\nfunction normalizeStage(raw: Record<string, unknown>): PipelineStage {\n return {\n id: typeof raw.id === 'string' ? raw.id : '',\n pipelineId: typeof raw.pipelineId === 'string' ? raw.pipelineId : (typeof raw.pipeline_id === 'string' ? raw.pipeline_id : ''),\n label: typeof raw.label === 'string' ? raw.label : '',\n order: typeof raw.order === 'number' ? raw.order : 0,\n color: typeof raw.color === 'string' && raw.color.trim().length ? raw.color.trim() : null,\n icon: typeof raw.icon === 'string' && raw.icon.trim().length ? raw.icon.trim() : null,\n updatedAt: typeof raw.updatedAt === 'string' ? raw.updatedAt : (typeof raw.updated_at === 'string' ? raw.updated_at : null),\n }\n}\n\nexport default function PipelineSettings(): React.ReactElement {\n const t = useT()\n const scopeVersion = useOrganizationScopeVersion()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n\n const [pipelines, setPipelines] = React.useState<Pipeline[]>([])\n const [loadingPipelines, setLoadingPipelines] = React.useState(false)\n const [pipelineDialog, setPipelineDialog] = React.useState<PipelineDialogState | null>(null)\n const [pipelineForm, setPipelineForm] = React.useState({ name: '', isDefault: false })\n const [submittingPipeline, setSubmittingPipeline] = React.useState(false)\n\n const [expandedPipelineId, setExpandedPipelineId] = React.useState<string | null>(null)\n const [stages, setStages] = React.useState<Record<string, PipelineStage[]>>({})\n const [loadingStages, setLoadingStages] = React.useState<Record<string, boolean>>({})\n const [stageDialog, setStageDialog] = React.useState<StageDialogState | null>(null)\n const [stageForm, setStageForm] = React.useState({ label: '', color: null as string | null, icon: null as string | null })\n const [submittingStage, setSubmittingStage] = React.useState(false)\n\n const loadPipelines = React.useCallback(async () => {\n setLoadingPipelines(true)\n try {\n const data = await readApiResultOrThrow<{ items?: unknown[] }>(\n '/api/customers/pipelines',\n undefined,\n { errorMessage: t('customers.pipelines.errors.loadFailed', 'Failed to load pipelines'), fallback: { items: [] } },\n )\n const items = Array.isArray(data?.items) ? data.items : []\n setPipelines(items.map((item) => normalizePipeline(item as Record<string, unknown>)))\n } finally {\n setLoadingPipelines(false)\n }\n }, [t])\n\n const loadStages = React.useCallback(async (pipelineId: string) => {\n setLoadingStages((prev) => ({ ...prev, [pipelineId]: true }))\n try {\n const data = await readApiResultOrThrow<{ items?: unknown[] }>(\n `/api/customers/pipeline-stages?pipelineId=${encodeURIComponent(pipelineId)}`,\n undefined,\n { errorMessage: t('customers.pipelines.errors.stagesLoadFailed', 'Failed to load stages'), fallback: { items: [] } },\n )\n const items = Array.isArray(data?.items) ? data.items : []\n setStages((prev) => ({\n ...prev,\n [pipelineId]: items.map((item) => normalizeStage(item as Record<string, unknown>)),\n }))\n } finally {\n setLoadingStages((prev) => ({ ...prev, [pipelineId]: false }))\n }\n }, [t])\n\n React.useEffect(() => {\n void loadPipelines()\n }, [loadPipelines, scopeVersion])\n\n React.useEffect(() => {\n if (expandedPipelineId) {\n void loadStages(expandedPipelineId)\n }\n }, [expandedPipelineId, loadStages])\n\n const openCreatePipeline = React.useCallback(() => {\n setPipelineForm({ name: '', isDefault: false })\n setPipelineDialog({ mode: 'create' })\n }, [])\n\n const openEditPipeline = React.useCallback((pipeline: Pipeline) => {\n setPipelineForm({ name: pipeline.name, isDefault: pipeline.isDefault })\n setPipelineDialog({ mode: 'edit', entry: pipeline })\n }, [])\n\n const closePipelineDialog = React.useCallback(() => {\n setPipelineDialog(null)\n }, [])\n\n const handlePipelineSubmit = React.useCallback(async () => {\n if (!pipelineForm.name.trim()) return\n setSubmittingPipeline(true)\n try {\n if (pipelineDialog?.mode === 'create') {\n const res = await apiCall('/api/customers/pipelines', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ name: pipelineForm.name.trim(), isDefault: pipelineForm.isDefault }),\n })\n if (!res.ok) {\n await raiseCrudError(res.response, t('customers.pipelines.errors.createFailed', 'Failed to create pipeline'))\n return\n }\n flash(t('customers.pipelines.flash.created', 'Pipeline created'), 'success')\n } else if (pipelineDialog?.mode === 'edit') {\n const res = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(pipelineDialog.entry.updatedAt),\n () =>\n apiCall('/api/customers/pipelines', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: pipelineDialog.entry.id, name: pipelineForm.name.trim(), isDefault: pipelineForm.isDefault }),\n }),\n )\n if (!res.ok) {\n await raiseCrudError(res.response, t('customers.pipelines.errors.updateFailed', 'Failed to update pipeline'))\n return\n }\n flash(t('customers.pipelines.flash.updated', 'Pipeline updated'), 'success')\n }\n setPipelineDialog(null)\n await loadPipelines()\n } finally {\n setSubmittingPipeline(false)\n }\n }, [pipelineDialog, pipelineForm, loadPipelines, t])\n\n const handleDeletePipeline = React.useCallback(async (pipeline: Pipeline) => {\n const confirmed = await confirm({\n title: t('customers.pipelines.confirm.deleteTitle', 'Delete pipeline'),\n text: t('customers.pipelines.confirm.deleteDesc', 'Are you sure you want to delete this pipeline? This cannot be undone.'),\n confirmText: t('customers.pipelines.confirm.deleteConfirm', 'Delete'),\n variant: 'destructive',\n })\n if (!confirmed) return\n const res = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(pipeline.updatedAt),\n () =>\n apiCall('/api/customers/pipelines', {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: pipeline.id }),\n }),\n )\n if (!res.ok) {\n const body = (res.result ?? {}) as Record<string, unknown>\n const msg = typeof body.error === 'string' ? body.error : t('customers.pipelines.errors.deleteFailed', 'Failed to delete pipeline')\n flash(msg, 'error')\n return\n }\n flash(t('customers.pipelines.flash.deleted', 'Pipeline deleted'), 'success')\n if (expandedPipelineId === pipeline.id) setExpandedPipelineId(null)\n await loadPipelines()\n }, [confirm, expandedPipelineId, loadPipelines, t])\n\n const openCreateStage = React.useCallback((pipelineId: string) => {\n setStageForm({ label: '', color: null, icon: null })\n setStageDialog({ mode: 'create', pipelineId })\n }, [])\n\n const openEditStage = React.useCallback((stage: PipelineStage) => {\n setStageForm({ label: stage.label, color: stage.color, icon: stage.icon })\n setStageDialog({ mode: 'edit', entry: stage })\n }, [])\n\n const closeStageDialog = React.useCallback(() => {\n setStageDialog(null)\n }, [])\n\n const handleStageSubmit = React.useCallback(async () => {\n if (!stageForm.label.trim()) return\n setSubmittingStage(true)\n try {\n const appearance: Record<string, unknown> = {}\n if (stageForm.color) appearance.color = stageForm.color\n if (stageForm.icon) appearance.icon = stageForm.icon\n\n if (stageDialog?.mode === 'create') {\n const res = await apiCall('/api/customers/pipeline-stages', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ pipelineId: stageDialog.pipelineId, label: stageForm.label.trim(), ...appearance }),\n })\n if (!res.ok) {\n await raiseCrudError(res.response, t('customers.pipelines.errors.stageCreateFailed', 'Failed to create stage'))\n return\n }\n flash(t('customers.pipelines.flash.stageCreated', 'Stage created'), 'success')\n await loadStages(stageDialog.pipelineId)\n } else if (stageDialog?.mode === 'edit') {\n const res = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(stageDialog.entry.updatedAt),\n () =>\n apiCall('/api/customers/pipeline-stages', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: stageDialog.entry.id, label: stageForm.label.trim(), ...appearance }),\n }),\n )\n if (!res.ok) {\n await raiseCrudError(res.response, t('customers.pipelines.errors.stageUpdateFailed', 'Failed to update stage'))\n return\n }\n flash(t('customers.pipelines.flash.stageUpdated', 'Stage updated'), 'success')\n await loadStages(stageDialog.entry.pipelineId)\n }\n setStageDialog(null)\n } finally {\n setSubmittingStage(false)\n }\n }, [stageDialog, stageForm, loadStages, t])\n\n const handleDeleteStage = React.useCallback(async (stage: PipelineStage) => {\n const confirmed = await confirm({\n title: t('customers.pipelines.confirm.stageDeleteTitle', 'Delete stage'),\n text: t('customers.pipelines.confirm.stageDeleteDesc', 'Are you sure you want to delete this stage?'),\n confirmText: t('customers.pipelines.confirm.stageDeleteConfirm', 'Delete'),\n variant: 'destructive',\n })\n if (!confirmed) return\n const res = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(stage.updatedAt),\n () =>\n apiCall('/api/customers/pipeline-stages', {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: stage.id }),\n }),\n )\n if (!res.ok) {\n const body = (res.result ?? {}) as Record<string, unknown>\n const msg = typeof body.error === 'string' ? body.error : t('customers.pipelines.errors.stageDeleteFailed', 'Failed to delete stage')\n flash(msg, 'error')\n return\n }\n flash(t('customers.pipelines.flash.stageDeleted', 'Stage deleted'), 'success')\n await loadStages(stage.pipelineId)\n }, [confirm, loadStages, t])\n\n const handleMoveStage = React.useCallback(async (stage: PipelineStage, direction: 'up' | 'down') => {\n const pipelineStages = stages[stage.pipelineId] ?? []\n const idx = pipelineStages.findIndex((s) => s.id === stage.id)\n if (idx < 0) return\n const swapIdx = direction === 'up' ? idx - 1 : idx + 1\n if (swapIdx < 0 || swapIdx >= pipelineStages.length) return\n\n const reordered = [...pipelineStages]\n const temp = reordered[idx]\n reordered[idx] = reordered[swapIdx]\n reordered[swapIdx] = temp\n\n const orderedStages = reordered.map((s, i) => ({ id: s.id, order: i }))\n const res = await apiCall('/api/customers/pipeline-stages/reorder', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ stages: orderedStages }),\n })\n if (!res.ok) {\n flash(t('customers.pipelines.errors.reorderFailed', 'Failed to reorder stages'), 'error')\n return\n }\n await loadStages(stage.pipelineId)\n }, [stages, loadStages, t])\n\n const handleKeyDown = React.useCallback(\n (handler: () => void) => (e: React.KeyboardEvent) => {\n if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {\n e.preventDefault()\n handler()\n }\n },\n [],\n )\n\n const appearanceLabels = React.useMemo<AppearanceSelectorLabels>(() => ({\n colorLabel: t('customers.pipelines.stageForm.color', 'Color'),\n colorClearLabel: t('customers.pipelines.stageForm.colorClear', 'Remove color'),\n iconLabel: t('customers.pipelines.stageForm.icon', 'Icon'),\n iconPlaceholder: t('customers.pipelines.stageForm.iconPlaceholder', 'e.g. lucide:star'),\n iconPickerTriggerLabel: t('customers.pipelines.stageForm.iconPicker', 'Pick icon'),\n iconSearchPlaceholder: t('customers.pipelines.stageForm.iconSearch', 'Search icons\u2026'),\n iconSearchEmptyLabel: t('customers.pipelines.stageForm.iconSearchEmpty', 'No icons found'),\n iconSuggestionsLabel: t('customers.pipelines.stageForm.iconSuggestions', 'Suggestions'),\n iconClearLabel: t('customers.pipelines.stageForm.iconClear', 'Remove icon'),\n previewEmptyLabel: t('customers.pipelines.stageForm.previewEmpty', 'No appearance set'),\n }), [t])\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h3 className=\"text-base font-semibold\">{t('customers.pipelines.title', 'Sales Pipelines')}</h3>\n <p className=\"text-sm text-muted-foreground\">\n {t('customers.pipelines.description', 'Manage sales pipelines and their stages.')}\n </p>\n </div>\n <Button size=\"sm\" onClick={openCreatePipeline}>\n {t('customers.pipelines.actions.create', 'Add pipeline')}\n </Button>\n </div>\n\n {loadingPipelines ? (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner className=\"h-4 w-4\" />\n {t('customers.pipelines.loading', 'Loading pipelines\u2026')}\n </div>\n ) : pipelines.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {t('customers.pipelines.empty', 'No pipelines yet. Create one to get started.')}\n </p>\n ) : (\n <Accordion\n type=\"single\"\n collapsible\n value={expandedPipelineId ?? ''}\n onValueChange={(next) => setExpandedPipelineId(next || null)}\n className=\"space-y-2\"\n >\n {pipelines.map((pipeline) => {\n const pipelineStages = stages[pipeline.id] ?? []\n const isLoadingStages = loadingStages[pipeline.id] ?? false\n\n return (\n <AccordionItem key={pipeline.id} value={pipeline.id}>\n <AccordionTrigger triggerIcon=\"chevron\">\n <span className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium\">{pipeline.name}</span>\n {pipeline.isDefault ? (\n <span className=\"rounded-full bg-primary/10 px-2 py-0.5 text-xs text-primary\">\n {t('customers.pipelines.defaultBadge', 'Default')}\n </span>\n ) : null}\n </span>\n </AccordionTrigger>\n <AccordionContent>\n <div className=\"mb-3 flex items-center justify-between\">\n <span className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n {t('customers.pipelines.stages.title', 'Stages')}\n </span>\n <Button size=\"sm\" variant=\"outline\" onClick={() => openCreateStage(pipeline.id)}>\n {t('customers.pipelines.stages.add', 'Add stage')}\n </Button>\n </div>\n\n {isLoadingStages ? (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner className=\"h-3 w-3\" />\n {t('customers.pipelines.stages.loading', 'Loading\u2026')}\n </div>\n ) : pipelineStages.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {t('customers.pipelines.stages.empty', 'No stages yet.')}\n </p>\n ) : (\n <div className=\"divide-y divide-border rounded-md border bg-background\">\n {pipelineStages.map((stage, idx) => (\n <div key={stage.id} className=\"flex items-center justify-between gap-3 px-3 py-2\">\n <div className=\"flex items-center gap-2\">\n <span className=\"w-5 text-center text-xs text-muted-foreground\">{idx + 1}</span>\n {stage.color ? renderDictionaryColor(stage.color, 'h-3 w-3 rounded-full') : null}\n {stage.icon ? renderDictionaryIcon(stage.icon, 'h-4 w-4') : null}\n <span className=\"text-sm\">{stage.label}</span>\n </div>\n <div className=\"flex items-center gap-1\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-7 w-7\"\n disabled={idx === 0}\n onClick={() => void handleMoveStage(stage, 'up')}\n title={t('customers.pipelines.stages.moveUp', 'Move up')}\n >\n \u2191\n </Button>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-7 w-7\"\n disabled={idx === pipelineStages.length - 1}\n onClick={() => void handleMoveStage(stage, 'down')}\n title={t('customers.pipelines.stages.moveDown', 'Move down')}\n >\n \u2193\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => openEditStage(stage)}\n >\n {t('customers.pipelines.stages.edit', 'Edit')}\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-destructive hover:text-destructive\"\n onClick={() => void handleDeleteStage(stage)}\n >\n {t('customers.pipelines.stages.delete', 'Delete')}\n </Button>\n </div>\n </div>\n ))}\n </div>\n )}\n\n <div className=\"mt-4 flex items-center justify-end gap-2 border-t pt-3\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => openEditPipeline(pipeline)}\n >\n {t('customers.pipelines.actions.edit', 'Edit pipeline')}\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-destructive hover:text-destructive\"\n onClick={() => void handleDeletePipeline(pipeline)}\n >\n {t('customers.pipelines.actions.delete', 'Delete pipeline')}\n </Button>\n </div>\n </AccordionContent>\n </AccordionItem>\n )\n })}\n </Accordion>\n )}\n\n {/* Pipeline Dialog */}\n <Dialog open={pipelineDialog !== null} onOpenChange={(open) => { if (!open) closePipelineDialog() }}>\n <DialogContent onKeyDown={handleKeyDown(handlePipelineSubmit)}>\n <DialogHeader>\n <DialogTitle>\n {pipelineDialog?.mode === 'create'\n ? t('customers.pipelines.dialog.createTitle', 'Create pipeline')\n : t('customers.pipelines.dialog.editTitle', 'Edit pipeline')}\n </DialogTitle>\n </DialogHeader>\n <div className=\"space-y-4 py-2\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"pipeline-name\">{t('customers.pipelines.form.name', 'Name')}</Label>\n <Input\n id=\"pipeline-name\"\n value={pipelineForm.name}\n onChange={(e) => setPipelineForm((prev) => ({ ...prev, name: e.target.value }))}\n placeholder={t('customers.pipelines.form.namePlaceholder', 'e.g. New Business')}\n autoFocus\n />\n </div>\n <div className=\"flex items-center gap-2\">\n <Checkbox\n id=\"pipeline-default\"\n checked={pipelineForm.isDefault}\n onCheckedChange={(checked) => setPipelineForm((prev) => ({ ...prev, isDefault: checked === true }))}\n />\n <Label htmlFor=\"pipeline-default\" className=\"cursor-pointer\">\n {t('customers.pipelines.form.isDefault', 'Set as default pipeline')}\n </Label>\n </div>\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={closePipelineDialog} disabled={submittingPipeline}>\n {t('customers.pipelines.dialog.cancel', 'Cancel')}\n </Button>\n <Button onClick={() => void handlePipelineSubmit()} disabled={submittingPipeline || !pipelineForm.name.trim()}>\n {submittingPipeline ? <Spinner className=\"mr-2 h-4 w-4\" /> : null}\n {t('customers.pipelines.dialog.save', 'Save')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n\n {ConfirmDialogElement}\n\n {/* Stage Dialog */}\n <Dialog open={stageDialog !== null} onOpenChange={(open) => { if (!open) closeStageDialog() }}>\n <DialogContent onKeyDown={handleKeyDown(handleStageSubmit)}>\n <DialogHeader>\n <DialogTitle>\n {stageDialog?.mode === 'create'\n ? t('customers.pipelines.stageDialog.createTitle', 'Add stage')\n : t('customers.pipelines.stageDialog.editTitle', 'Edit stage')}\n </DialogTitle>\n </DialogHeader>\n <div className=\"space-y-4 py-2\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"stage-label\">{t('customers.pipelines.stageForm.label', 'Label')}</Label>\n <Input\n id=\"stage-label\"\n value={stageForm.label}\n onChange={(e) => setStageForm((prev) => ({ ...prev, label: e.target.value }))}\n placeholder={t('customers.pipelines.stageForm.labelPlaceholder', 'e.g. Discovery')}\n autoFocus\n />\n </div>\n <AppearanceSelector\n color={stageForm.color}\n icon={stageForm.icon}\n onColorChange={(next) => setStageForm((prev) => ({ ...prev, color: next }))}\n onIconChange={(next) => setStageForm((prev) => ({ ...prev, icon: next }))}\n labels={appearanceLabels}\n />\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={closeStageDialog} disabled={submittingStage}>\n {t('customers.pipelines.stageDialog.cancel', 'Cancel')}\n </Button>\n <Button onClick={() => void handleStageSubmit()} disabled={submittingStage || !stageForm.label.trim()}>\n {submittingStage ? <Spinner className=\"mr-2 h-4 w-4\" /> : null}\n {t('customers.pipelines.stageDialog.save', 'Save')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </div>\n )\n}\n"],
5
- "mappings": ";AA2WQ,SACE,KADF;AAzWR,YAAY,WAAW;AACvB,SAAS,cAAc;AACvB,SAAS,aAAa;AACtB,SAAS,aAAa;AACtB,SAAS,gBAAgB;AACzB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,aAAa;AACtB,SAAS,SAAS,sBAAsB,mCAAmC;AAC3E,SAAS,iCAAiC;AAC1C,SAAS,sBAAsB;AAC/B,SAAS,mCAAmC;AAC5C,SAAS,YAAY;AACrB,SAAS,wBAAwB;AACjC,SAAS,eAAe;AACxB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,OAEK;AACP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AA6BP,SAAS,kBAAkB,KAAwC;AACjE,SAAO;AAAA,IACL,IAAI,OAAO,IAAI,OAAO,WAAW,IAAI,KAAK;AAAA,IAC1C,MAAM,OAAO,IAAI,SAAS,WAAW,IAAI,OAAO;AAAA,IAChD,WAAW,IAAI,cAAc,QAAQ,IAAI,eAAe;AAAA,IACxD,gBAAgB,OAAO,IAAI,mBAAmB,WAAW,IAAI,iBAAkB,OAAO,IAAI,oBAAoB,WAAW,IAAI,kBAAkB;AAAA,IAC/I,UAAU,OAAO,IAAI,aAAa,WAAW,IAAI,WAAY,OAAO,IAAI,cAAc,WAAW,IAAI,YAAY;AAAA,IACjH,WAAW,OAAO,IAAI,cAAc,WAAW,IAAI,YAAa,OAAO,IAAI,eAAe,WAAW,IAAI,aAAa;AAAA,EACxH;AACF;AAEA,SAAS,eAAe,KAA6C;AACnE,SAAO;AAAA,IACL,IAAI,OAAO,IAAI,OAAO,WAAW,IAAI,KAAK;AAAA,IAC1C,YAAY,OAAO,IAAI,eAAe,WAAW,IAAI,aAAc,OAAO,IAAI,gBAAgB,WAAW,IAAI,cAAc;AAAA,IAC3H,OAAO,OAAO,IAAI,UAAU,WAAW,IAAI,QAAQ;AAAA,IACnD,OAAO,OAAO,IAAI,UAAU,WAAW,IAAI,QAAQ;AAAA,IACnD,OAAO,OAAO,IAAI,UAAU,YAAY,IAAI,MAAM,KAAK,EAAE,SAAS,IAAI,MAAM,KAAK,IAAI;AAAA,IACrF,MAAM,OAAO,IAAI,SAAS,YAAY,IAAI,KAAK,KAAK,EAAE,SAAS,IAAI,KAAK,KAAK,IAAI;AAAA,IACjF,WAAW,OAAO,IAAI,cAAc,WAAW,IAAI,YAAa,OAAO,IAAI,eAAe,WAAW,IAAI,aAAa;AAAA,EACxH;AACF;AAEe,SAAR,mBAAwD;AAC7D,QAAM,IAAI,KAAK;AACf,QAAM,eAAe,4BAA4B;AACjD,QAAM,EAAE,SAAS,qBAAqB,IAAI,iBAAiB;AAE3D,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAqB,CAAC,CAAC;AAC/D,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,MAAM,SAAS,KAAK;AACpE,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,MAAM,SAAqC,IAAI;AAC3F,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAS,EAAE,MAAM,IAAI,WAAW,MAAM,CAAC;AACrF,QAAM,CAAC,oBAAoB,qBAAqB,IAAI,MAAM,SAAS,KAAK;AAExE,QAAM,CAAC,oBAAoB,qBAAqB,IAAI,MAAM,SAAwB,IAAI;AACtF,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAA0C,CAAC,CAAC;AAC9E,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAkC,CAAC,CAAC;AACpF,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAkC,IAAI;AAClF,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAS,EAAE,OAAO,IAAI,OAAO,MAAuB,MAAM,KAAsB,CAAC;AACzH,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,MAAM,SAAS,KAAK;AAElE,QAAM,gBAAgB,MAAM,YAAY,YAAY;AAClD,wBAAoB,IAAI;AACxB,QAAI;AACF,YAAM,OAAO,MAAM;AAAA,QACjB;AAAA,QACA;AAAA,QACA,EAAE,cAAc,EAAE,yCAAyC,0BAA0B,GAAG,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE;AAAA,MAClH;AACA,YAAM,QAAQ,MAAM,QAAQ,MAAM,KAAK,IAAI,KAAK,QAAQ,CAAC;AACzD,mBAAa,MAAM,IAAI,CAAC,SAAS,kBAAkB,IAA+B,CAAC,CAAC;AAAA,IACtF,UAAE;AACA,0BAAoB,KAAK;AAAA,IAC3B;AAAA,EACF,GAAG,CAAC,CAAC,CAAC;AAEN,QAAM,aAAa,MAAM,YAAY,OAAO,eAAuB;AACjE,qBAAiB,CAAC,UAAU,EAAE,GAAG,MAAM,CAAC,UAAU,GAAG,KAAK,EAAE;AAC5D,QAAI;AACF,YAAM,OAAO,MAAM;AAAA,QACjB,6CAA6C,mBAAmB,UAAU,CAAC;AAAA,QAC3E;AAAA,QACA,EAAE,cAAc,EAAE,+CAA+C,uBAAuB,GAAG,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE;AAAA,MACrH;AACA,YAAM,QAAQ,MAAM,QAAQ,MAAM,KAAK,IAAI,KAAK,QAAQ,CAAC;AACzD,gBAAU,CAAC,UAAU;AAAA,QACnB,GAAG;AAAA,QACH,CAAC,UAAU,GAAG,MAAM,IAAI,CAAC,SAAS,eAAe,IAA+B,CAAC;AAAA,MACnF,EAAE;AAAA,IACJ,UAAE;AACA,uBAAiB,CAAC,UAAU,EAAE,GAAG,MAAM,CAAC,UAAU,GAAG,MAAM,EAAE;AAAA,IAC/D;AAAA,EACF,GAAG,CAAC,CAAC,CAAC;AAEN,QAAM,UAAU,MAAM;AACpB,SAAK,cAAc;AAAA,EACrB,GAAG,CAAC,eAAe,YAAY,CAAC;AAEhC,QAAM,UAAU,MAAM;AACpB,QAAI,oBAAoB;AACtB,WAAK,WAAW,kBAAkB;AAAA,IACpC;AAAA,EACF,GAAG,CAAC,oBAAoB,UAAU,CAAC;AAEnC,QAAM,qBAAqB,MAAM,YAAY,MAAM;AACjD,oBAAgB,EAAE,MAAM,IAAI,WAAW,MAAM,CAAC;AAC9C,sBAAkB,EAAE,MAAM,SAAS,CAAC;AAAA,EACtC,GAAG,CAAC,CAAC;AAEL,QAAM,mBAAmB,MAAM,YAAY,CAAC,aAAuB;AACjE,oBAAgB,EAAE,MAAM,SAAS,MAAM,WAAW,SAAS,UAAU,CAAC;AACtE,sBAAkB,EAAE,MAAM,QAAQ,OAAO,SAAS,CAAC;AAAA,EACrD,GAAG,CAAC,CAAC;AAEL,QAAM,sBAAsB,MAAM,YAAY,MAAM;AAClD,sBAAkB,IAAI;AAAA,EACxB,GAAG,CAAC,CAAC;AAEL,QAAM,uBAAuB,MAAM,YAAY,YAAY;AACzD,QAAI,CAAC,aAAa,KAAK,KAAK,EAAG;AAC/B,0BAAsB,IAAI;AAC1B,QAAI;AACF,UAAI,gBAAgB,SAAS,UAAU;AACrC,cAAM,MAAM,MAAM,QAAQ,4BAA4B;AAAA,UACpD,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,KAAK,UAAU,EAAE,MAAM,aAAa,KAAK,KAAK,GAAG,WAAW,aAAa,UAAU,CAAC;AAAA,QAC5F,CAAC;AACD,YAAI,CAAC,IAAI,IAAI;AACX,gBAAM,eAAe,IAAI,UAAU,EAAE,2CAA2C,2BAA2B,CAAC;AAC5G;AAAA,QACF;AACA,cAAM,EAAE,qCAAqC,kBAAkB,GAAG,SAAS;AAAA,MAC7E,WAAW,gBAAgB,SAAS,QAAQ;AAC1C,cAAM,MAAM,MAAM;AAAA,UAChB,0BAA0B,eAAe,MAAM,SAAS;AAAA,UACxD,MACE,QAAQ,4BAA4B;AAAA,YAClC,QAAQ;AAAA,YACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,YAC9C,MAAM,KAAK,UAAU,EAAE,IAAI,eAAe,MAAM,IAAI,MAAM,aAAa,KAAK,KAAK,GAAG,WAAW,aAAa,UAAU,CAAC;AAAA,UACzH,CAAC;AAAA,QACL;AACA,YAAI,CAAC,IAAI,IAAI;AACX,gBAAM,eAAe,IAAI,UAAU,EAAE,2CAA2C,2BAA2B,CAAC;AAC5G;AAAA,QACF;AACA,cAAM,EAAE,qCAAqC,kBAAkB,GAAG,SAAS;AAAA,MAC7E;AACA,wBAAkB,IAAI;AACtB,YAAM,cAAc;AAAA,IACtB,UAAE;AACA,4BAAsB,KAAK;AAAA,IAC7B;AAAA,EACF,GAAG,CAAC,gBAAgB,cAAc,eAAe,CAAC,CAAC;AAEnD,QAAM,uBAAuB,MAAM,YAAY,OAAO,aAAuB;AAC3E,UAAM,YAAY,MAAM,QAAQ;AAAA,MAC9B,OAAO,EAAE,2CAA2C,iBAAiB;AAAA,MACrE,MAAM,EAAE,0CAA0C,uEAAuE;AAAA,MACzH,aAAa,EAAE,6CAA6C,QAAQ;AAAA,MACpE,SAAS;AAAA,IACX,CAAC;AACD,QAAI,CAAC,UAAW;AAChB,UAAM,MAAM,MAAM;AAAA,MAChB,0BAA0B,SAAS,SAAS;AAAA,MAC5C,MACE,QAAQ,4BAA4B;AAAA,QAClC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,IAAI,SAAS,GAAG,CAAC;AAAA,MAC1C,CAAC;AAAA,IACL;AACA,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,OAAQ,IAAI,UAAU,CAAC;AAC7B,YAAM,MAAM,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,EAAE,2CAA2C,2BAA2B;AAClI,YAAM,KAAK,OAAO;AAClB;AAAA,IACF;AACA,UAAM,EAAE,qCAAqC,kBAAkB,GAAG,SAAS;AAC3E,QAAI,uBAAuB,SAAS,GAAI,uBAAsB,IAAI;AAClE,UAAM,cAAc;AAAA,EACtB,GAAG,CAAC,SAAS,oBAAoB,eAAe,CAAC,CAAC;AAElD,QAAM,kBAAkB,MAAM,YAAY,CAAC,eAAuB;AAChE,iBAAa,EAAE,OAAO,IAAI,OAAO,MAAM,MAAM,KAAK,CAAC;AACnD,mBAAe,EAAE,MAAM,UAAU,WAAW,CAAC;AAAA,EAC/C,GAAG,CAAC,CAAC;AAEL,QAAM,gBAAgB,MAAM,YAAY,CAAC,UAAyB;AAChE,iBAAa,EAAE,OAAO,MAAM,OAAO,OAAO,MAAM,OAAO,MAAM,MAAM,KAAK,CAAC;AACzE,mBAAe,EAAE,MAAM,QAAQ,OAAO,MAAM,CAAC;AAAA,EAC/C,GAAG,CAAC,CAAC;AAEL,QAAM,mBAAmB,MAAM,YAAY,MAAM;AAC/C,mBAAe,IAAI;AAAA,EACrB,GAAG,CAAC,CAAC;AAEL,QAAM,oBAAoB,MAAM,YAAY,YAAY;AACtD,QAAI,CAAC,UAAU,MAAM,KAAK,EAAG;AAC7B,uBAAmB,IAAI;AACvB,QAAI;AACF,YAAM,aAAsC,CAAC;AAC7C,UAAI,UAAU,MAAO,YAAW,QAAQ,UAAU;AAClD,UAAI,UAAU,KAAM,YAAW,OAAO,UAAU;AAEhD,UAAI,aAAa,SAAS,UAAU;AAClC,cAAM,MAAM,MAAM,QAAQ,kCAAkC;AAAA,UAC1D,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,KAAK,UAAU,EAAE,YAAY,YAAY,YAAY,OAAO,UAAU,MAAM,KAAK,GAAG,GAAG,WAAW,CAAC;AAAA,QAC3G,CAAC;AACD,YAAI,CAAC,IAAI,IAAI;AACX,gBAAM,eAAe,IAAI,UAAU,EAAE,gDAAgD,wBAAwB,CAAC;AAC9G;AAAA,QACF;AACA,cAAM,EAAE,0CAA0C,eAAe,GAAG,SAAS;AAC7E,cAAM,WAAW,YAAY,UAAU;AAAA,MACzC,WAAW,aAAa,SAAS,QAAQ;AACvC,cAAM,MAAM,MAAM;AAAA,UAChB,0BAA0B,YAAY,MAAM,SAAS;AAAA,UACrD,MACE,QAAQ,kCAAkC;AAAA,YACxC,QAAQ;AAAA,YACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,YAC9C,MAAM,KAAK,UAAU,EAAE,IAAI,YAAY,MAAM,IAAI,OAAO,UAAU,MAAM,KAAK,GAAG,GAAG,WAAW,CAAC;AAAA,UACjG,CAAC;AAAA,QACL;AACA,YAAI,CAAC,IAAI,IAAI;AACX,gBAAM,eAAe,IAAI,UAAU,EAAE,gDAAgD,wBAAwB,CAAC;AAC9G;AAAA,QACF;AACA,cAAM,EAAE,0CAA0C,eAAe,GAAG,SAAS;AAC7E,cAAM,WAAW,YAAY,MAAM,UAAU;AAAA,MAC/C;AACA,qBAAe,IAAI;AAAA,IACrB,UAAE;AACA,yBAAmB,KAAK;AAAA,IAC1B;AAAA,EACF,GAAG,CAAC,aAAa,WAAW,YAAY,CAAC,CAAC;AAE1C,QAAM,oBAAoB,MAAM,YAAY,OAAO,UAAyB;AAC1E,UAAM,YAAY,MAAM,QAAQ;AAAA,MAC9B,OAAO,EAAE,gDAAgD,cAAc;AAAA,MACvE,MAAM,EAAE,+CAA+C,6CAA6C;AAAA,MACpG,aAAa,EAAE,kDAAkD,QAAQ;AAAA,MACzE,SAAS;AAAA,IACX,CAAC;AACD,QAAI,CAAC,UAAW;AAChB,UAAM,MAAM,MAAM;AAAA,MAChB,0BAA0B,MAAM,SAAS;AAAA,MACzC,MACE,QAAQ,kCAAkC;AAAA,QACxC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,IAAI,MAAM,GAAG,CAAC;AAAA,MACvC,CAAC;AAAA,IACL;AACA,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,OAAQ,IAAI,UAAU,CAAC;AAC7B,YAAM,MAAM,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,EAAE,gDAAgD,wBAAwB;AACpI,YAAM,KAAK,OAAO;AAClB;AAAA,IACF;AACA,UAAM,EAAE,0CAA0C,eAAe,GAAG,SAAS;AAC7E,UAAM,WAAW,MAAM,UAAU;AAAA,EACnC,GAAG,CAAC,SAAS,YAAY,CAAC,CAAC;AAE3B,QAAM,kBAAkB,MAAM,YAAY,OAAO,OAAsB,cAA6B;AAClG,UAAM,iBAAiB,OAAO,MAAM,UAAU,KAAK,CAAC;AACpD,UAAM,MAAM,eAAe,UAAU,CAAC,MAAM,EAAE,OAAO,MAAM,EAAE;AAC7D,QAAI,MAAM,EAAG;AACb,UAAM,UAAU,cAAc,OAAO,MAAM,IAAI,MAAM;AACrD,QAAI,UAAU,KAAK,WAAW,eAAe,OAAQ;AAErD,UAAM,YAAY,CAAC,GAAG,cAAc;AACpC,UAAM,OAAO,UAAU,GAAG;AAC1B,cAAU,GAAG,IAAI,UAAU,OAAO;AAClC,cAAU,OAAO,IAAI;AAErB,UAAM,gBAAgB,UAAU,IAAI,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,IAAI,OAAO,EAAE,EAAE;AACtE,UAAM,MAAM,MAAM,QAAQ,0CAA0C;AAAA,MAClE,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,EAAE,QAAQ,cAAc,CAAC;AAAA,IAChD,CAAC;AACD,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,EAAE,4CAA4C,0BAA0B,GAAG,OAAO;AACxF;AAAA,IACF;AACA,UAAM,WAAW,MAAM,UAAU;AAAA,EACnC,GAAG,CAAC,QAAQ,YAAY,CAAC,CAAC;AAE1B,QAAM,gBAAgB,MAAM;AAAA,IAC1B,CAAC,YAAwB,CAAC,MAA2B;AACnD,WAAK,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,SAAS;AACjD,UAAE,eAAe;AACjB,gBAAQ;AAAA,MACV;AAAA,IACF;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,mBAAmB,MAAM,QAAkC,OAAO;AAAA,IACtE,YAAY,EAAE,uCAAuC,OAAO;AAAA,IAC5D,iBAAiB,EAAE,4CAA4C,cAAc;AAAA,IAC7E,WAAW,EAAE,sCAAsC,MAAM;AAAA,IACzD,iBAAiB,EAAE,iDAAiD,kBAAkB;AAAA,IACtF,wBAAwB,EAAE,4CAA4C,WAAW;AAAA,IACjF,uBAAuB,EAAE,4CAA4C,oBAAe;AAAA,IACpF,sBAAsB,EAAE,iDAAiD,gBAAgB;AAAA,IACzF,sBAAsB,EAAE,iDAAiD,aAAa;AAAA,IACtF,gBAAgB,EAAE,2CAA2C,aAAa;AAAA,IAC1E,mBAAmB,EAAE,8CAA8C,mBAAmB;AAAA,EACxF,IAAI,CAAC,CAAC,CAAC;AAEP,SACE,qBAAC,SAAI,WAAU,aACb;AAAA,yBAAC,SAAI,WAAU,qCACb;AAAA,2BAAC,SACC;AAAA,4BAAC,QAAG,WAAU,2BAA2B,YAAE,6BAA6B,iBAAiB,GAAE;AAAA,QAC3F,oBAAC,OAAE,WAAU,iCACV,YAAE,mCAAmC,0CAA0C,GAClF;AAAA,SACF;AAAA,MACA,oBAAC,UAAO,MAAK,MAAK,SAAS,oBACxB,YAAE,sCAAsC,cAAc,GACzD;AAAA,OACF;AAAA,IAEC,mBACC,qBAAC,SAAI,WAAU,yDACb;AAAA,0BAAC,WAAQ,WAAU,WAAU;AAAA,MAC5B,EAAE,+BAA+B,yBAAoB;AAAA,OACxD,IACE,UAAU,WAAW,IACvB,oBAAC,OAAE,WAAU,iCACV,YAAE,6BAA6B,8CAA8C,GAChF,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,aAAW;AAAA,QACX,OAAO,sBAAsB;AAAA,QAC7B,eAAe,CAAC,SAAS,sBAAsB,QAAQ,IAAI;AAAA,QAC3D,WAAU;AAAA,QAET,oBAAU,IAAI,CAAC,aAAa;AAC3B,gBAAM,iBAAiB,OAAO,SAAS,EAAE,KAAK,CAAC;AAC/C,gBAAM,kBAAkB,cAAc,SAAS,EAAE,KAAK;AAEtD,iBACE,qBAAC,iBAAgC,OAAO,SAAS,IAC/C;AAAA,gCAAC,oBAAiB,aAAY,WAC5B,+BAAC,UAAK,WAAU,2BACd;AAAA,kCAAC,UAAK,WAAU,uBAAuB,mBAAS,MAAK;AAAA,cACpD,SAAS,YACR,oBAAC,UAAK,WAAU,+DACb,YAAE,oCAAoC,SAAS,GAClD,IACE;AAAA,eACN,GACF;AAAA,YACA,qBAAC,oBACC;AAAA,mCAAC,SAAI,WAAU,0CACb;AAAA,oCAAC,UAAK,WAAU,qEACb,YAAE,oCAAoC,QAAQ,GACjD;AAAA,gBACA,oBAAC,UAAO,MAAK,MAAK,SAAQ,WAAU,SAAS,MAAM,gBAAgB,SAAS,EAAE,GAC3E,YAAE,kCAAkC,WAAW,GAClD;AAAA,iBACF;AAAA,cAEC,kBACC,qBAAC,SAAI,WAAU,yDACb;AAAA,oCAAC,WAAQ,WAAU,WAAU;AAAA,gBAC5B,EAAE,sCAAsC,eAAU;AAAA,iBACrD,IACE,eAAe,WAAW,IAC5B,oBAAC,OAAE,WAAU,iCACV,YAAE,oCAAoC,gBAAgB,GACzD,IAEA,oBAAC,SAAI,WAAU,0DACZ,yBAAe,IAAI,CAAC,OAAO,QAC1B,qBAAC,SAAmB,WAAU,qDAC5B;AAAA,qCAAC,SAAI,WAAU,2BACb;AAAA,sCAAC,UAAK,WAAU,iDAAiD,gBAAM,GAAE;AAAA,kBACxE,MAAM,QAAQ,sBAAsB,MAAM,OAAO,sBAAsB,IAAI;AAAA,kBAC3E,MAAM,OAAO,qBAAqB,MAAM,MAAM,SAAS,IAAI;AAAA,kBAC5D,oBAAC,UAAK,WAAU,WAAW,gBAAM,OAAM;AAAA,mBACzC;AAAA,gBACA,qBAAC,SAAI,WAAU,2BACb;AAAA;AAAA,oBAAC;AAAA;AAAA,sBACC,SAAQ;AAAA,sBACR,MAAK;AAAA,sBACL,WAAU;AAAA,sBACV,UAAU,QAAQ;AAAA,sBAClB,SAAS,MAAM,KAAK,gBAAgB,OAAO,IAAI;AAAA,sBAC/C,OAAO,EAAE,qCAAqC,SAAS;AAAA,sBACxD;AAAA;AAAA,kBAED;AAAA,kBACA;AAAA,oBAAC;AAAA;AAAA,sBACC,SAAQ;AAAA,sBACR,MAAK;AAAA,sBACL,WAAU;AAAA,sBACV,UAAU,QAAQ,eAAe,SAAS;AAAA,sBAC1C,SAAS,MAAM,KAAK,gBAAgB,OAAO,MAAM;AAAA,sBACjD,OAAO,EAAE,uCAAuC,WAAW;AAAA,sBAC5D;AAAA;AAAA,kBAED;AAAA,kBACA;AAAA,oBAAC;AAAA;AAAA,sBACC,SAAQ;AAAA,sBACR,MAAK;AAAA,sBACL,SAAS,MAAM,cAAc,KAAK;AAAA,sBAEjC,YAAE,mCAAmC,MAAM;AAAA;AAAA,kBAC9C;AAAA,kBACA;AAAA,oBAAC;AAAA;AAAA,sBACC,SAAQ;AAAA,sBACR,MAAK;AAAA,sBACL,WAAU;AAAA,sBACV,SAAS,MAAM,KAAK,kBAAkB,KAAK;AAAA,sBAE1C,YAAE,qCAAqC,QAAQ;AAAA;AAAA,kBAClD;AAAA,mBACF;AAAA,mBA3CQ,MAAM,EA4ChB,CACD,GACH;AAAA,cAGF,qBAAC,SAAI,WAAU,0DACb;AAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,SAAQ;AAAA,oBACR,MAAK;AAAA,oBACL,SAAS,MAAM,iBAAiB,QAAQ;AAAA,oBAEvC,YAAE,oCAAoC,eAAe;AAAA;AAAA,gBACxD;AAAA,gBACA;AAAA,kBAAC;AAAA;AAAA,oBACC,SAAQ;AAAA,oBACR,MAAK;AAAA,oBACL,WAAU;AAAA,oBACV,SAAS,MAAM,KAAK,qBAAqB,QAAQ;AAAA,oBAEhD,YAAE,sCAAsC,iBAAiB;AAAA;AAAA,gBAC5D;AAAA,iBACF;AAAA,eACF;AAAA,eAnGkB,SAAS,EAoG7B;AAAA,QAEJ,CAAC;AAAA;AAAA,IACH;AAAA,IAIF,oBAAC,UAAO,MAAM,mBAAmB,MAAM,cAAc,CAAC,SAAS;AAAE,UAAI,CAAC,KAAM,qBAAoB;AAAA,IAAE,GAChG,+BAAC,iBAAc,WAAW,cAAc,oBAAoB,GAC1D;AAAA,0BAAC,gBACC,8BAAC,eACE,0BAAgB,SAAS,WACtB,EAAE,0CAA0C,iBAAiB,IAC7D,EAAE,wCAAwC,eAAe,GAC/D,GACF;AAAA,MACA,qBAAC,SAAI,WAAU,kBACb;AAAA,6BAAC,SAAI,WAAU,aACb;AAAA,8BAAC,SAAM,SAAQ,iBAAiB,YAAE,iCAAiC,MAAM,GAAE;AAAA,UAC3E;AAAA,YAAC;AAAA;AAAA,cACC,IAAG;AAAA,cACH,OAAO,aAAa;AAAA,cACpB,UAAU,CAAC,MAAM,gBAAgB,CAAC,UAAU,EAAE,GAAG,MAAM,MAAM,EAAE,OAAO,MAAM,EAAE;AAAA,cAC9E,aAAa,EAAE,4CAA4C,mBAAmB;AAAA,cAC9E,WAAS;AAAA;AAAA,UACX;AAAA,WACF;AAAA,QACA,qBAAC,SAAI,WAAU,2BACb;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,IAAG;AAAA,cACH,SAAS,aAAa;AAAA,cACtB,iBAAiB,CAAC,YAAY,gBAAgB,CAAC,UAAU,EAAE,GAAG,MAAM,WAAW,YAAY,KAAK,EAAE;AAAA;AAAA,UACpG;AAAA,UACA,oBAAC,SAAM,SAAQ,oBAAmB,WAAU,kBACzC,YAAE,sCAAsC,yBAAyB,GACpE;AAAA,WACF;AAAA,SACF;AAAA,MACA,qBAAC,gBACC;AAAA,4BAAC,UAAO,SAAQ,WAAU,SAAS,qBAAqB,UAAU,oBAC/D,YAAE,qCAAqC,QAAQ,GAClD;AAAA,QACA,qBAAC,UAAO,SAAS,MAAM,KAAK,qBAAqB,GAAG,UAAU,sBAAsB,CAAC,aAAa,KAAK,KAAK,GACzG;AAAA,+BAAqB,oBAAC,WAAQ,WAAU,gBAAe,IAAK;AAAA,UAC5D,EAAE,mCAAmC,MAAM;AAAA,WAC9C;AAAA,SACF;AAAA,OACF,GACF;AAAA,IAEC;AAAA,IAGD,oBAAC,UAAO,MAAM,gBAAgB,MAAM,cAAc,CAAC,SAAS;AAAE,UAAI,CAAC,KAAM,kBAAiB;AAAA,IAAE,GAC1F,+BAAC,iBAAc,WAAW,cAAc,iBAAiB,GACvD;AAAA,0BAAC,gBACC,8BAAC,eACE,uBAAa,SAAS,WACnB,EAAE,+CAA+C,WAAW,IAC5D,EAAE,6CAA6C,YAAY,GACjE,GACF;AAAA,MACA,qBAAC,SAAI,WAAU,kBACb;AAAA,6BAAC,SAAI,WAAU,aACb;AAAA,8BAAC,SAAM,SAAQ,eAAe,YAAE,uCAAuC,OAAO,GAAE;AAAA,UAChF;AAAA,YAAC;AAAA;AAAA,cACC,IAAG;AAAA,cACH,OAAO,UAAU;AAAA,cACjB,UAAU,CAAC,MAAM,aAAa,CAAC,UAAU,EAAE,GAAG,MAAM,OAAO,EAAE,OAAO,MAAM,EAAE;AAAA,cAC5E,aAAa,EAAE,kDAAkD,gBAAgB;AAAA,cACjF,WAAS;AAAA;AAAA,UACX;AAAA,WACF;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO,UAAU;AAAA,YACjB,MAAM,UAAU;AAAA,YAChB,eAAe,CAAC,SAAS,aAAa,CAAC,UAAU,EAAE,GAAG,MAAM,OAAO,KAAK,EAAE;AAAA,YAC1E,cAAc,CAAC,SAAS,aAAa,CAAC,UAAU,EAAE,GAAG,MAAM,MAAM,KAAK,EAAE;AAAA,YACxE,QAAQ;AAAA;AAAA,QACV;AAAA,SACF;AAAA,MACA,qBAAC,gBACC;AAAA,4BAAC,UAAO,SAAQ,WAAU,SAAS,kBAAkB,UAAU,iBAC5D,YAAE,0CAA0C,QAAQ,GACvD;AAAA,QACA,qBAAC,UAAO,SAAS,MAAM,KAAK,kBAAkB,GAAG,UAAU,mBAAmB,CAAC,UAAU,MAAM,KAAK,GACjG;AAAA,4BAAkB,oBAAC,WAAQ,WAAU,gBAAe,IAAK;AAAA,UACzD,EAAE,wCAAwC,MAAM;AAAA,WACnD;AAAA,SACF;AAAA,OACF,GACF;AAAA,KACF;AAEJ;",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { Checkbox } from '@open-mercato/ui/primitives/checkbox'\nimport {\n Dialog,\n DialogContent,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'\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'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport {\n Accordion,\n AccordionContent,\n AccordionItem,\n AccordionTrigger,\n} from '@open-mercato/ui/primitives/accordion'\nimport {\n AppearanceSelector,\n type AppearanceSelectorLabels,\n} from '@open-mercato/core/modules/dictionaries/components/AppearanceSelector'\nimport {\n renderDictionaryColor,\n renderDictionaryIcon,\n} from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'\n\ntype Pipeline = {\n id: string\n name: string\n isDefault: boolean\n organizationId: string\n tenantId: string\n updatedAt?: string | null\n}\n\ntype PipelineStage = {\n id: string\n pipelineId: string\n label: string\n order: number\n color: string | null\n icon: string | null\n updatedAt?: string | null\n}\n\ntype PipelineDialogState =\n | { mode: 'create' }\n | { mode: 'edit'; entry: Pipeline }\n\ntype StageDialogState =\n | { mode: 'create'; pipelineId: string }\n | { mode: 'edit'; entry: PipelineStage }\n\nfunction normalizePipeline(raw: Record<string, unknown>): Pipeline {\n return {\n id: typeof raw.id === 'string' ? raw.id : '',\n name: typeof raw.name === 'string' ? raw.name : '',\n isDefault: raw.isDefault === true || raw.is_default === true,\n organizationId: typeof raw.organizationId === 'string' ? raw.organizationId : (typeof raw.organization_id === 'string' ? raw.organization_id : ''),\n tenantId: typeof raw.tenantId === 'string' ? raw.tenantId : (typeof raw.tenant_id === 'string' ? raw.tenant_id : ''),\n updatedAt: typeof raw.updatedAt === 'string' ? raw.updatedAt : (typeof raw.updated_at === 'string' ? raw.updated_at : null),\n }\n}\n\nfunction normalizeStage(raw: Record<string, unknown>): PipelineStage {\n return {\n id: typeof raw.id === 'string' ? raw.id : '',\n pipelineId: typeof raw.pipelineId === 'string' ? raw.pipelineId : (typeof raw.pipeline_id === 'string' ? raw.pipeline_id : ''),\n label: typeof raw.label === 'string' ? raw.label : '',\n order: typeof raw.order === 'number' ? raw.order : 0,\n color: typeof raw.color === 'string' && raw.color.trim().length ? raw.color.trim() : null,\n icon: typeof raw.icon === 'string' && raw.icon.trim().length ? raw.icon.trim() : null,\n updatedAt: typeof raw.updatedAt === 'string' ? raw.updatedAt : (typeof raw.updated_at === 'string' ? raw.updated_at : null),\n }\n}\n\nexport default function PipelineSettings(): React.ReactElement {\n const t = useT()\n const scopeVersion = useOrganizationScopeVersion()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n\n const [pipelines, setPipelines] = React.useState<Pipeline[]>([])\n const [loadingPipelines, setLoadingPipelines] = React.useState(false)\n const [pipelineDialog, setPipelineDialog] = React.useState<PipelineDialogState | null>(null)\n const [pipelineForm, setPipelineForm] = React.useState({ name: '', isDefault: false })\n const [submittingPipeline, setSubmittingPipeline] = React.useState(false)\n\n const [expandedPipelineId, setExpandedPipelineId] = React.useState<string | null>(null)\n const [stages, setStages] = React.useState<Record<string, PipelineStage[]>>({})\n const [loadingStages, setLoadingStages] = React.useState<Record<string, boolean>>({})\n const [stageDialog, setStageDialog] = React.useState<StageDialogState | null>(null)\n const [stageForm, setStageForm] = React.useState({ label: '', color: null as string | null, icon: null as string | null })\n const [submittingStage, setSubmittingStage] = React.useState(false)\n\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: 'customers-pipeline-settings',\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n const pipelineMutationContext = React.useMemo(\n () => ({\n formId: 'customers-pipeline-settings',\n resourceKind: 'customers.pipeline',\n retryLastMutation,\n }),\n [retryLastMutation],\n )\n const stageMutationContext = React.useMemo(\n () => ({\n formId: 'customers-pipeline-settings',\n resourceKind: 'customers.pipeline_stage',\n retryLastMutation,\n }),\n [retryLastMutation],\n )\n\n const loadPipelines = React.useCallback(async () => {\n setLoadingPipelines(true)\n try {\n const data = await readApiResultOrThrow<{ items?: unknown[] }>(\n '/api/customers/pipelines',\n undefined,\n { errorMessage: t('customers.pipelines.errors.loadFailed', 'Failed to load pipelines'), fallback: { items: [] } },\n )\n const items = Array.isArray(data?.items) ? data.items : []\n setPipelines(items.map((item) => normalizePipeline(item as Record<string, unknown>)))\n } finally {\n setLoadingPipelines(false)\n }\n }, [t])\n\n const loadStages = React.useCallback(async (pipelineId: string) => {\n setLoadingStages((prev) => ({ ...prev, [pipelineId]: true }))\n try {\n const data = await readApiResultOrThrow<{ items?: unknown[] }>(\n `/api/customers/pipeline-stages?pipelineId=${encodeURIComponent(pipelineId)}`,\n undefined,\n { errorMessage: t('customers.pipelines.errors.stagesLoadFailed', 'Failed to load stages'), fallback: { items: [] } },\n )\n const items = Array.isArray(data?.items) ? data.items : []\n setStages((prev) => ({\n ...prev,\n [pipelineId]: items.map((item) => normalizeStage(item as Record<string, unknown>)),\n }))\n } finally {\n setLoadingStages((prev) => ({ ...prev, [pipelineId]: false }))\n }\n }, [t])\n\n React.useEffect(() => {\n void loadPipelines()\n }, [loadPipelines, scopeVersion])\n\n React.useEffect(() => {\n if (expandedPipelineId) {\n void loadStages(expandedPipelineId)\n }\n }, [expandedPipelineId, loadStages])\n\n const openCreatePipeline = React.useCallback(() => {\n setPipelineForm({ name: '', isDefault: false })\n setPipelineDialog({ mode: 'create' })\n }, [])\n\n const openEditPipeline = React.useCallback((pipeline: Pipeline) => {\n setPipelineForm({ name: pipeline.name, isDefault: pipeline.isDefault })\n setPipelineDialog({ mode: 'edit', entry: pipeline })\n }, [])\n\n const closePipelineDialog = React.useCallback(() => {\n setPipelineDialog(null)\n }, [])\n\n const handlePipelineSubmit = React.useCallback(async () => {\n if (!pipelineForm.name.trim()) return\n setSubmittingPipeline(true)\n try {\n if (pipelineDialog?.mode === 'create') {\n await runMutation({\n operation: async () => {\n const res = await apiCall('/api/customers/pipelines', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ name: pipelineForm.name.trim(), isDefault: pipelineForm.isDefault }),\n })\n if (!res.ok) {\n await raiseCrudError(res.response, t('customers.pipelines.errors.createFailed', 'Failed to create pipeline'))\n }\n },\n context: pipelineMutationContext,\n mutationPayload: { action: 'create', name: pipelineForm.name.trim(), isDefault: pipelineForm.isDefault },\n })\n flash(t('customers.pipelines.flash.created', 'Pipeline created'), 'success')\n } else if (pipelineDialog?.mode === 'edit') {\n await runMutation({\n operation: async () => {\n const res = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(pipelineDialog.entry.updatedAt),\n () =>\n apiCall('/api/customers/pipelines', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: pipelineDialog.entry.id, name: pipelineForm.name.trim(), isDefault: pipelineForm.isDefault }),\n }),\n )\n if (!res.ok) {\n await raiseCrudError(res.response, t('customers.pipelines.errors.updateFailed', 'Failed to update pipeline'))\n }\n },\n context: pipelineMutationContext,\n mutationPayload: { action: 'update', id: pipelineDialog.entry.id, name: pipelineForm.name.trim(), isDefault: pipelineForm.isDefault },\n })\n flash(t('customers.pipelines.flash.updated', 'Pipeline updated'), 'success')\n }\n setPipelineDialog(null)\n await loadPipelines()\n } catch {\n return\n } finally {\n setSubmittingPipeline(false)\n }\n }, [pipelineDialog, pipelineForm, loadPipelines, pipelineMutationContext, runMutation, t])\n\n const handleDeletePipeline = React.useCallback(async (pipeline: Pipeline) => {\n const confirmed = await confirm({\n title: t('customers.pipelines.confirm.deleteTitle', 'Delete pipeline'),\n text: t('customers.pipelines.confirm.deleteDesc', 'Are you sure you want to delete this pipeline? This cannot be undone.'),\n confirmText: t('customers.pipelines.confirm.deleteConfirm', 'Delete'),\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n await runMutation({\n operation: async () => {\n const res = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(pipeline.updatedAt),\n () =>\n apiCall('/api/customers/pipelines', {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: pipeline.id }),\n }),\n )\n if (!res.ok) {\n const body = (res.result ?? {}) as Record<string, unknown>\n const msg = typeof body.error === 'string' ? body.error : t('customers.pipelines.errors.deleteFailed', 'Failed to delete pipeline')\n throw new Error(msg)\n }\n },\n context: pipelineMutationContext,\n mutationPayload: { action: 'delete', id: pipeline.id },\n })\n } catch (err) {\n const msg = err instanceof Error && err.message ? err.message : t('customers.pipelines.errors.deleteFailed', 'Failed to delete pipeline')\n flash(msg, 'error')\n return\n }\n flash(t('customers.pipelines.flash.deleted', 'Pipeline deleted'), 'success')\n if (expandedPipelineId === pipeline.id) setExpandedPipelineId(null)\n await loadPipelines()\n }, [confirm, expandedPipelineId, loadPipelines, pipelineMutationContext, runMutation, t])\n\n const openCreateStage = React.useCallback((pipelineId: string) => {\n setStageForm({ label: '', color: null, icon: null })\n setStageDialog({ mode: 'create', pipelineId })\n }, [])\n\n const openEditStage = React.useCallback((stage: PipelineStage) => {\n setStageForm({ label: stage.label, color: stage.color, icon: stage.icon })\n setStageDialog({ mode: 'edit', entry: stage })\n }, [])\n\n const closeStageDialog = React.useCallback(() => {\n setStageDialog(null)\n }, [])\n\n const handleStageSubmit = React.useCallback(async () => {\n if (!stageForm.label.trim()) return\n setSubmittingStage(true)\n try {\n const appearance: Record<string, unknown> = {}\n if (stageForm.color) appearance.color = stageForm.color\n if (stageForm.icon) appearance.icon = stageForm.icon\n\n if (stageDialog?.mode === 'create') {\n const targetPipelineId = stageDialog.pipelineId\n await runMutation({\n operation: async () => {\n const res = await apiCall('/api/customers/pipeline-stages', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ pipelineId: targetPipelineId, label: stageForm.label.trim(), ...appearance }),\n })\n if (!res.ok) {\n await raiseCrudError(res.response, t('customers.pipelines.errors.stageCreateFailed', 'Failed to create stage'))\n }\n },\n context: stageMutationContext,\n mutationPayload: { action: 'create', pipelineId: targetPipelineId, label: stageForm.label.trim(), ...appearance },\n })\n flash(t('customers.pipelines.flash.stageCreated', 'Stage created'), 'success')\n await loadStages(targetPipelineId)\n } else if (stageDialog?.mode === 'edit') {\n const targetStage = stageDialog.entry\n await runMutation({\n operation: async () => {\n const res = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(targetStage.updatedAt),\n () =>\n apiCall('/api/customers/pipeline-stages', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: targetStage.id, label: stageForm.label.trim(), ...appearance }),\n }),\n )\n if (!res.ok) {\n await raiseCrudError(res.response, t('customers.pipelines.errors.stageUpdateFailed', 'Failed to update stage'))\n }\n },\n context: stageMutationContext,\n mutationPayload: { action: 'update', id: targetStage.id, label: stageForm.label.trim(), ...appearance },\n })\n flash(t('customers.pipelines.flash.stageUpdated', 'Stage updated'), 'success')\n await loadStages(targetStage.pipelineId)\n }\n setStageDialog(null)\n } catch {\n return\n } finally {\n setSubmittingStage(false)\n }\n }, [stageDialog, stageForm, loadStages, runMutation, stageMutationContext, t])\n\n const handleDeleteStage = React.useCallback(async (stage: PipelineStage) => {\n const confirmed = await confirm({\n title: t('customers.pipelines.confirm.stageDeleteTitle', 'Delete stage'),\n text: t('customers.pipelines.confirm.stageDeleteDesc', 'Are you sure you want to delete this stage?'),\n confirmText: t('customers.pipelines.confirm.stageDeleteConfirm', 'Delete'),\n variant: 'destructive',\n })\n if (!confirmed) return\n try {\n await runMutation({\n operation: async () => {\n const res = await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(stage.updatedAt),\n () =>\n apiCall('/api/customers/pipeline-stages', {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: stage.id }),\n }),\n )\n if (!res.ok) {\n const body = (res.result ?? {}) as Record<string, unknown>\n const msg = typeof body.error === 'string' ? body.error : t('customers.pipelines.errors.stageDeleteFailed', 'Failed to delete stage')\n throw new Error(msg)\n }\n },\n context: stageMutationContext,\n mutationPayload: { action: 'delete', id: stage.id },\n })\n } catch (err) {\n const msg = err instanceof Error && err.message ? err.message : t('customers.pipelines.errors.stageDeleteFailed', 'Failed to delete stage')\n flash(msg, 'error')\n return\n }\n flash(t('customers.pipelines.flash.stageDeleted', 'Stage deleted'), 'success')\n await loadStages(stage.pipelineId)\n }, [confirm, loadStages, runMutation, stageMutationContext, t])\n\n const handleMoveStage = React.useCallback(async (stage: PipelineStage, direction: 'up' | 'down') => {\n const pipelineStages = stages[stage.pipelineId] ?? []\n const idx = pipelineStages.findIndex((s) => s.id === stage.id)\n if (idx < 0) return\n const swapIdx = direction === 'up' ? idx - 1 : idx + 1\n if (swapIdx < 0 || swapIdx >= pipelineStages.length) return\n\n const reordered = [...pipelineStages]\n const temp = reordered[idx]\n reordered[idx] = reordered[swapIdx]\n reordered[swapIdx] = temp\n\n const orderedStages = reordered.map((s, i) => ({ id: s.id, order: i }))\n try {\n await runMutation({\n operation: async () => {\n const res = await apiCall('/api/customers/pipeline-stages/reorder', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ stages: orderedStages }),\n })\n if (!res.ok) {\n throw new Error(t('customers.pipelines.errors.reorderFailed', 'Failed to reorder stages'))\n }\n },\n context: stageMutationContext,\n mutationPayload: { action: 'reorder', stages: orderedStages },\n })\n } catch (err) {\n const msg = err instanceof Error && err.message ? err.message : t('customers.pipelines.errors.reorderFailed', 'Failed to reorder stages')\n flash(msg, 'error')\n return\n }\n await loadStages(stage.pipelineId)\n }, [stages, loadStages, runMutation, stageMutationContext, t])\n\n const handleKeyDown = React.useCallback(\n (handler: () => void) => (e: React.KeyboardEvent) => {\n if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {\n e.preventDefault()\n handler()\n }\n },\n [],\n )\n\n const appearanceLabels = React.useMemo<AppearanceSelectorLabels>(() => ({\n colorLabel: t('customers.pipelines.stageForm.color', 'Color'),\n colorClearLabel: t('customers.pipelines.stageForm.colorClear', 'Remove color'),\n iconLabel: t('customers.pipelines.stageForm.icon', 'Icon'),\n iconPlaceholder: t('customers.pipelines.stageForm.iconPlaceholder', 'e.g. lucide:star'),\n iconPickerTriggerLabel: t('customers.pipelines.stageForm.iconPicker', 'Pick icon'),\n iconSearchPlaceholder: t('customers.pipelines.stageForm.iconSearch', 'Search icons\u2026'),\n iconSearchEmptyLabel: t('customers.pipelines.stageForm.iconSearchEmpty', 'No icons found'),\n iconSuggestionsLabel: t('customers.pipelines.stageForm.iconSuggestions', 'Suggestions'),\n iconClearLabel: t('customers.pipelines.stageForm.iconClear', 'Remove icon'),\n previewEmptyLabel: t('customers.pipelines.stageForm.previewEmpty', 'No appearance set'),\n }), [t])\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h3 className=\"text-base font-semibold\">{t('customers.pipelines.title', 'Sales Pipelines')}</h3>\n <p className=\"text-sm text-muted-foreground\">\n {t('customers.pipelines.description', 'Manage sales pipelines and their stages.')}\n </p>\n </div>\n <Button size=\"sm\" onClick={openCreatePipeline}>\n {t('customers.pipelines.actions.create', 'Add pipeline')}\n </Button>\n </div>\n\n {loadingPipelines ? (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner className=\"h-4 w-4\" />\n {t('customers.pipelines.loading', 'Loading pipelines\u2026')}\n </div>\n ) : pipelines.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {t('customers.pipelines.empty', 'No pipelines yet. Create one to get started.')}\n </p>\n ) : (\n <Accordion\n type=\"single\"\n collapsible\n value={expandedPipelineId ?? ''}\n onValueChange={(next) => setExpandedPipelineId(next || null)}\n className=\"space-y-2\"\n >\n {pipelines.map((pipeline) => {\n const pipelineStages = stages[pipeline.id] ?? []\n const isLoadingStages = loadingStages[pipeline.id] ?? false\n\n return (\n <AccordionItem key={pipeline.id} value={pipeline.id}>\n <AccordionTrigger triggerIcon=\"chevron\">\n <span className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium\">{pipeline.name}</span>\n {pipeline.isDefault ? (\n <span className=\"rounded-full bg-primary/10 px-2 py-0.5 text-xs text-primary\">\n {t('customers.pipelines.defaultBadge', 'Default')}\n </span>\n ) : null}\n </span>\n </AccordionTrigger>\n <AccordionContent>\n <div className=\"mb-3 flex items-center justify-between\">\n <span className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n {t('customers.pipelines.stages.title', 'Stages')}\n </span>\n <Button size=\"sm\" variant=\"outline\" onClick={() => openCreateStage(pipeline.id)}>\n {t('customers.pipelines.stages.add', 'Add stage')}\n </Button>\n </div>\n\n {isLoadingStages ? (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner className=\"h-3 w-3\" />\n {t('customers.pipelines.stages.loading', 'Loading\u2026')}\n </div>\n ) : pipelineStages.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {t('customers.pipelines.stages.empty', 'No stages yet.')}\n </p>\n ) : (\n <div className=\"divide-y divide-border rounded-md border bg-background\">\n {pipelineStages.map((stage, idx) => (\n <div key={stage.id} className=\"flex items-center justify-between gap-3 px-3 py-2\">\n <div className=\"flex items-center gap-2\">\n <span className=\"w-5 text-center text-xs text-muted-foreground\">{idx + 1}</span>\n {stage.color ? renderDictionaryColor(stage.color, 'h-3 w-3 rounded-full') : null}\n {stage.icon ? renderDictionaryIcon(stage.icon, 'h-4 w-4') : null}\n <span className=\"text-sm\">{stage.label}</span>\n </div>\n <div className=\"flex items-center gap-1\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-7 w-7\"\n disabled={idx === 0}\n onClick={() => void handleMoveStage(stage, 'up')}\n title={t('customers.pipelines.stages.moveUp', 'Move up')}\n >\n \u2191\n </Button>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-7 w-7\"\n disabled={idx === pipelineStages.length - 1}\n onClick={() => void handleMoveStage(stage, 'down')}\n title={t('customers.pipelines.stages.moveDown', 'Move down')}\n >\n \u2193\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => openEditStage(stage)}\n >\n {t('customers.pipelines.stages.edit', 'Edit')}\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-destructive hover:text-destructive\"\n onClick={() => void handleDeleteStage(stage)}\n >\n {t('customers.pipelines.stages.delete', 'Delete')}\n </Button>\n </div>\n </div>\n ))}\n </div>\n )}\n\n <div className=\"mt-4 flex items-center justify-end gap-2 border-t pt-3\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => openEditPipeline(pipeline)}\n >\n {t('customers.pipelines.actions.edit', 'Edit pipeline')}\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-destructive hover:text-destructive\"\n onClick={() => void handleDeletePipeline(pipeline)}\n >\n {t('customers.pipelines.actions.delete', 'Delete pipeline')}\n </Button>\n </div>\n </AccordionContent>\n </AccordionItem>\n )\n })}\n </Accordion>\n )}\n\n {/* Pipeline Dialog */}\n <Dialog open={pipelineDialog !== null} onOpenChange={(open) => { if (!open) closePipelineDialog() }}>\n <DialogContent onKeyDown={handleKeyDown(handlePipelineSubmit)}>\n <DialogHeader>\n <DialogTitle>\n {pipelineDialog?.mode === 'create'\n ? t('customers.pipelines.dialog.createTitle', 'Create pipeline')\n : t('customers.pipelines.dialog.editTitle', 'Edit pipeline')}\n </DialogTitle>\n </DialogHeader>\n <div className=\"space-y-4 py-2\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"pipeline-name\">{t('customers.pipelines.form.name', 'Name')}</Label>\n <Input\n id=\"pipeline-name\"\n value={pipelineForm.name}\n onChange={(e) => setPipelineForm((prev) => ({ ...prev, name: e.target.value }))}\n placeholder={t('customers.pipelines.form.namePlaceholder', 'e.g. New Business')}\n autoFocus\n />\n </div>\n <div className=\"flex items-center gap-2\">\n <Checkbox\n id=\"pipeline-default\"\n checked={pipelineForm.isDefault}\n onCheckedChange={(checked) => setPipelineForm((prev) => ({ ...prev, isDefault: checked === true }))}\n />\n <Label htmlFor=\"pipeline-default\" className=\"cursor-pointer\">\n {t('customers.pipelines.form.isDefault', 'Set as default pipeline')}\n </Label>\n </div>\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={closePipelineDialog} disabled={submittingPipeline}>\n {t('customers.pipelines.dialog.cancel', 'Cancel')}\n </Button>\n <Button onClick={() => void handlePipelineSubmit()} disabled={submittingPipeline || !pipelineForm.name.trim()}>\n {submittingPipeline ? <Spinner className=\"mr-2 h-4 w-4\" /> : null}\n {t('customers.pipelines.dialog.save', 'Save')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n\n {ConfirmDialogElement}\n\n {/* Stage Dialog */}\n <Dialog open={stageDialog !== null} onOpenChange={(open) => { if (!open) closeStageDialog() }}>\n <DialogContent onKeyDown={handleKeyDown(handleStageSubmit)}>\n <DialogHeader>\n <DialogTitle>\n {stageDialog?.mode === 'create'\n ? t('customers.pipelines.stageDialog.createTitle', 'Add stage')\n : t('customers.pipelines.stageDialog.editTitle', 'Edit stage')}\n </DialogTitle>\n </DialogHeader>\n <div className=\"space-y-4 py-2\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"stage-label\">{t('customers.pipelines.stageForm.label', 'Label')}</Label>\n <Input\n id=\"stage-label\"\n value={stageForm.label}\n onChange={(e) => setStageForm((prev) => ({ ...prev, label: e.target.value }))}\n placeholder={t('customers.pipelines.stageForm.labelPlaceholder', 'e.g. Discovery')}\n autoFocus\n />\n </div>\n <AppearanceSelector\n color={stageForm.color}\n icon={stageForm.icon}\n onColorChange={(next) => setStageForm((prev) => ({ ...prev, color: next }))}\n onIconChange={(next) => setStageForm((prev) => ({ ...prev, icon: next }))}\n labels={appearanceLabels}\n />\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={closeStageDialog} disabled={submittingStage}>\n {t('customers.pipelines.stageDialog.cancel', 'Cancel')}\n </Button>\n <Button onClick={() => void handleStageSubmit()} disabled={submittingStage || !stageForm.label.trim()}>\n {submittingStage ? <Spinner className=\"mr-2 h-4 w-4\" /> : null}\n {t('customers.pipelines.stageDialog.save', 'Save')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </div>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -7,11 +7,12 @@ import { isToday } from "date-fns/isToday";
7
7
  import { isTomorrow } from "date-fns/isTomorrow";
8
8
  import { startOfDay } from "date-fns/startOfDay";
9
9
  import { CalendarClock } from "lucide-react";
10
- import { useT } from "@open-mercato/shared/lib/i18n/context";
10
+ import { useLocale, useT } from "@open-mercato/shared/lib/i18n/context";
11
11
  import { cn } from "@open-mercato/shared/lib/utils";
12
12
  import { Avatar } from "@open-mercato/ui/primitives/avatar";
13
13
  import { Button } from "@open-mercato/ui/primitives/button";
14
14
  import { EmptyState } from "@open-mercato/ui/primitives/empty-state";
15
+ import { eventDisplayTitle, pluralCategory } from "../../lib/calendar/labels.js";
15
16
  const MAX_AVATARS_PER_ROW = 2;
16
17
  const CATEGORY_BADGE_CLASS = {
17
18
  meeting: "bg-status-info-bg text-status-info-text",
@@ -70,13 +71,17 @@ function buildDayGroups(anchor, horizonDays, items) {
70
71
  }
71
72
  function AgendaDayHeader({ date, count }) {
72
73
  const t = useT();
74
+ const locale = useLocale();
73
75
  const todayMarker = isToday(date);
74
76
  const tomorrowMarker = !todayMarker && isTomorrow(date);
77
+ const countKey = `customers.calendar.agenda.eventsCount.${pluralCategory(locale, count)}`;
78
+ const resolvedCount = t(countKey, { count });
79
+ const countLabel = resolvedCount === countKey ? t("customers.calendar.agenda.eventsCount.other", "{count} events", { count }) : resolvedCount;
75
80
  return /* @__PURE__ */ jsxs("div", { className: "flex w-full items-center gap-2 bg-muted/50 px-3 py-2.5 sm:px-5", children: [
76
81
  /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-foreground", children: groupLabelOf(date) }),
77
82
  todayMarker || tomorrowMarker ? /* @__PURE__ */ jsx("span", { className: cn("text-xs font-medium", todayMarker ? "text-foreground" : "text-muted-foreground"), children: `\xB7 ${todayMarker ? t("customers.calendar.agenda.today", "Today") : t("customers.calendar.agenda.tomorrow", "Tomorrow")}` }) : null,
78
83
  /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "w-2.5 shrink-0" }),
79
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: count === 1 ? t("customers.calendar.agenda.eventCount", "1 event") : t("customers.calendar.agenda.eventsCount", "{count} events", { count }) })
84
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: countLabel })
80
85
  ] });
81
86
  }
82
87
  function AgendaRow({
@@ -87,6 +92,7 @@ function AgendaRow({
87
92
  const t = useT();
88
93
  const canceled = item.status === "canceled";
89
94
  const done = item.status === "done";
95
+ const title = eventDisplayTitle(item.title, t("customers.calendar.grid.untitled", "Untitled"));
90
96
  const typeLabel = typeLabels?.[item.interactionType] ?? deriveTypeLabel(item.interactionType);
91
97
  const platformLabels = {
92
98
  zoom: t("customers.calendar.platformFull.zoom", "Zoom"),
@@ -103,7 +109,7 @@ function AgendaRow({
103
109
  {
104
110
  type: "button",
105
111
  variant: "ghost",
106
- "aria-label": `${item.title} \xB7 ${ariaTime}`,
112
+ "aria-label": `${title} \xB7 ${ariaTime}`,
107
113
  onClick: () => onItemClick(item),
108
114
  className: cn(
109
115
  "h-auto w-full justify-start whitespace-normal rounded-none bg-background px-3 py-3 text-left transition-colors hover:bg-muted/30 sm:gap-3.5 sm:px-5",
@@ -131,7 +137,7 @@ function AgendaRow({
131
137
  done || canceled ? "text-muted-foreground" : "text-foreground",
132
138
  canceled && "line-through"
133
139
  ),
134
- children: item.title
140
+ children: title
135
141
  }
136
142
  ),
137
143
  /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-muted-foreground", children: locationLabel ? `${typeLabel} \xB7 ${locationLabel}` : typeLabel })