@open-mercato/core 0.6.6-develop.6204.1.30b1f58642 → 0.6.6-develop.6227.1.2695efff30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) 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/components/DictionariesManager.js +7 -4
  57. package/dist/modules/dictionaries/components/DictionariesManager.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/components/DictionariesManager.tsx +7 -4
  165. package/src/modules/directory/api/organization-branding/route.ts +61 -0
  166. package/src/modules/directory/backend/directory/branding/page.tsx +16 -10
  167. package/src/modules/directory/backend/directory/organizations/page.tsx +35 -8
  168. package/src/modules/directory/backend/directory/tenants/page.tsx +37 -13
  169. package/src/modules/entities/api/definitions.batch.ts +17 -0
  170. package/src/modules/entities/api/definitions.mutation-guard.ts +80 -0
  171. package/src/modules/entities/api/definitions.restore.ts +17 -0
  172. package/src/modules/entities/api/definitions.ts +30 -0
  173. package/src/modules/entities/api/entities.ts +35 -3
  174. package/src/modules/entities/api/records.ts +20 -0
  175. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +33 -10
  176. package/src/modules/feature_toggles/api/overrides/route.ts +44 -1
  177. package/src/modules/inbox_ops/api/emails/[id]/route.ts +32 -0
  178. package/src/modules/inbox_ops/api/proposals/[id]/accept-all/route.ts +32 -0
  179. package/src/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.ts +33 -0
  180. package/src/modules/inbox_ops/api/proposals/[id]/categorize/route.ts +33 -0
  181. package/src/modules/messages/components/message-detail/hooks/useMessageDetailsActions.ts +80 -42
  182. package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +21 -4
  183. package/src/modules/payment_gateways/api/cancel/route.ts +39 -0
  184. package/src/modules/payment_gateways/api/capture/route.ts +39 -0
  185. package/src/modules/payment_gateways/api/guards.ts +59 -0
  186. package/src/modules/payment_gateways/api/refund/route.ts +39 -0
  187. package/src/modules/payment_gateways/api/sessions/route.ts +40 -0
  188. package/src/modules/planner/api/availability-date-specific.ts +10 -0
  189. package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +26 -5
  190. package/src/modules/planner/commands/availability-date-specific.ts +24 -0
  191. package/src/modules/planner/components/AvailabilityRulesEditor.tsx +122 -41
  192. package/src/modules/query_index/api/purge.ts +37 -3
  193. package/src/modules/query_index/api/reindex.ts +43 -3
  194. package/src/modules/query_index/components/QueryIndexesTable.tsx +66 -24
  195. package/src/modules/shipping_carriers/api/shipments/route.ts +31 -0
  196. package/src/modules/shipping_carriers/api/tracking/refresh/route.ts +53 -0
  197. package/src/modules/shipping_carriers/data/validators.ts +5 -0
  198. package/src/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.ts +29 -8
  199. package/src/modules/shipping_carriers/lib/shipping-service.ts +43 -7
  200. package/src/modules/shipping_carriers/workers/status-poller.ts +1 -1
  201. package/src/modules/translations/components/TranslationManager.tsx +65 -21
@@ -0,0 +1,55 @@
1
+ import { NextResponse } from "next/server";
2
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
3
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
+ import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
5
+ import { trackingRefreshSchema } from "../../../data/validators.js";
6
+ import { shippingCarriersTag } from "../../openapi.js";
7
+ const metadata = {
8
+ path: "/shipping-carriers/tracking/refresh",
9
+ POST: { requireAuth: true, requireFeatures: ["shipping_carriers.manage"] }
10
+ };
11
+ async function POST(req) {
12
+ const auth = await getAuthFromRequest(req);
13
+ if (!auth?.tenantId || !auth.orgId) {
14
+ return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
15
+ }
16
+ const payload = await readJsonSafe(req);
17
+ const parsed = trackingRefreshSchema.safeParse(payload);
18
+ if (!parsed.success) {
19
+ return NextResponse.json({ error: "Invalid payload", details: parsed.error.flatten() }, { status: 422 });
20
+ }
21
+ const container = await createRequestContainer();
22
+ const service = container.resolve("shippingCarrierService");
23
+ try {
24
+ const tracking = await service.refreshTracking({
25
+ ...parsed.data,
26
+ organizationId: auth.orgId,
27
+ tenantId: auth.tenantId
28
+ });
29
+ return NextResponse.json(tracking);
30
+ } catch (error) {
31
+ const message = error instanceof Error ? error.message : "Failed to refresh tracking";
32
+ return NextResponse.json({ error: message }, { status: 502 });
33
+ }
34
+ }
35
+ const openApi = {
36
+ tags: [shippingCarriersTag],
37
+ summary: "Refresh tracking",
38
+ methods: {
39
+ POST: {
40
+ summary: "Refresh tracking and persist the latest shipment status",
41
+ tags: [shippingCarriersTag],
42
+ responses: [
43
+ { status: 200, description: "Tracking refreshed and shipment status persisted" },
44
+ { status: 422, description: "Validation failed" },
45
+ { status: 502, description: "Provider upstream error" }
46
+ ]
47
+ }
48
+ }
49
+ };
50
+ export {
51
+ POST,
52
+ metadata,
53
+ openApi
54
+ };
55
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/shipping_carriers/api/tracking/refresh/route.ts"],
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport type { ShippingCarrierService } from '../../../lib/shipping-service'\nimport { trackingRefreshSchema } from '../../../data/validators'\nimport { shippingCarriersTag } from '../../openapi'\n\nexport const metadata = {\n path: '/shipping-carriers/tracking/refresh',\n POST: { requireAuth: true, requireFeatures: ['shipping_carriers.manage'] },\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n const payload = await readJsonSafe<unknown>(req)\n const parsed = trackingRefreshSchema.safeParse(payload)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid payload', details: parsed.error.flatten() }, { status: 422 })\n }\n const container = await createRequestContainer()\n const service = container.resolve('shippingCarrierService') as ShippingCarrierService\n try {\n const tracking = await service.refreshTracking({\n ...parsed.data,\n organizationId: auth.orgId as string,\n tenantId: auth.tenantId,\n })\n return NextResponse.json(tracking)\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : 'Failed to refresh tracking'\n return NextResponse.json({ error: message }, { status: 502 })\n }\n}\n\nexport const openApi = {\n tags: [shippingCarriersTag],\n summary: 'Refresh tracking',\n methods: {\n POST: {\n summary: 'Refresh tracking and persist the latest shipment status',\n tags: [shippingCarriersTag],\n responses: [\n { status: 200, description: 'Tracking refreshed and shipment status persisted' },\n { status: 422, description: 'Validation failed' },\n { status: 502, description: 'Provider upstream error' },\n ],\n },\n },\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AACvC,SAAS,oBAAoB;AAE7B,SAAS,6BAA6B;AACtC,SAAS,2BAA2B;AAE7B,MAAM,WAAW;AAAA,EACtB,MAAM;AAAA,EACN,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,0BAA0B,EAAE;AAC3E;AAEA,eAAsB,KAAK,KAAc;AACvC,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,YAAY,CAAC,KAAK,OAAO;AAClC,WAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrE;AACA,QAAM,UAAU,MAAM,aAAsB,GAAG;AAC/C,QAAM,SAAS,sBAAsB,UAAU,OAAO;AACtD,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,aAAa,KAAK,EAAE,OAAO,mBAAmB,SAAS,OAAO,MAAM,QAAQ,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACzG;AACA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,UAAU,UAAU,QAAQ,wBAAwB;AAC1D,MAAI;AACF,UAAM,WAAW,MAAM,QAAQ,gBAAgB;AAAA,MAC7C,GAAG,OAAO;AAAA,MACV,gBAAgB,KAAK;AAAA,MACrB,UAAU,KAAK;AAAA,IACjB,CAAC;AACD,WAAO,aAAa,KAAK,QAAQ;AAAA,EACnC,SAAS,OAAgB;AACvB,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,WAAO,aAAa,KAAK,EAAE,OAAO,QAAQ,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC9D;AACF;AAEO,MAAM,UAAU;AAAA,EACrB,MAAM,CAAC,mBAAmB;AAAA,EAC1B,SAAS;AAAA,EACT,SAAS;AAAA,IACP,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,MAAM,CAAC,mBAAmB;AAAA,MAC1B,WAAW;AAAA,QACT,EAAE,QAAQ,KAAK,aAAa,mDAAmD;AAAA,QAC/E,EAAE,QAAQ,KAAK,aAAa,oBAAoB;AAAA,QAChD,EAAE,QAAQ,KAAK,aAAa,0BAA0B;AAAA,MACxD;AAAA,IACF;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -46,6 +46,10 @@ const trackingQuerySchema = z.object({
46
46
  message: "shipmentId or trackingNumber is required",
47
47
  path: ["shipmentId"]
48
48
  });
49
+ const trackingRefreshSchema = z.object({
50
+ providerKey: z.string().min(1),
51
+ shipmentId: z.string().uuid()
52
+ });
49
53
  const cancelShipmentSchema = z.object({
50
54
  providerKey: z.string().min(1),
51
55
  shipmentId: z.string().uuid(),
@@ -63,6 +67,7 @@ export {
63
67
  cancelShipmentSchema,
64
68
  createShipmentSchema,
65
69
  searchDropOffPointsQuerySchema,
66
- trackingQuerySchema
70
+ trackingQuerySchema,
71
+ trackingRefreshSchema
67
72
  };
68
73
  //# sourceMappingURL=validators.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/shipping_carriers/data/validators.ts"],
4
- "sourcesContent": ["import { z } from 'zod'\n\nconst addressSchema = z.object({\n countryCode: z.string().min(2).max(3),\n postalCode: z.string().min(1),\n city: z.string().min(1),\n line1: z.string().min(1),\n line2: z.string().optional(),\n})\n\nconst packageSchema = z.object({\n weightKg: z.number().positive(),\n lengthCm: z.number().positive(),\n widthCm: z.number().positive(),\n heightCm: z.number().positive(),\n})\n\n/** Permissive phone regex: allows +, digits, spaces, dashes, dots, and parentheses. Min 7 chars. */\nexport const PHONE_REGEX = /^[+\\d][\\d\\s\\-().]{6,}$/\n\nconst emailField = z.string().trim().email('Invalid email address.').max(320).optional()\nconst phoneField = z.string().trim().regex(PHONE_REGEX, 'Invalid phone number.').max(50).optional()\n\nexport const calculateRatesSchema = z.object({\n providerKey: z.string().min(1),\n origin: addressSchema,\n destination: addressSchema,\n packages: z.array(packageSchema).min(1),\n receiverPhone: phoneField,\n receiverEmail: emailField,\n})\n\nexport const createShipmentSchema = z.object({\n providerKey: z.string().min(1),\n orderId: z.string().uuid(),\n origin: addressSchema,\n destination: addressSchema,\n packages: z.array(packageSchema).min(1),\n serviceCode: z.string().min(1),\n labelFormat: z.enum(['pdf', 'zpl', 'png']).optional(),\n senderPhone: phoneField,\n senderEmail: emailField,\n receiverPhone: phoneField,\n receiverEmail: emailField,\n targetPoint: z.string().optional(),\n c2cSendingMethod: z.enum(['parcel_locker', 'dispatch_order', 'pop', 'any_point']).optional(),\n})\n\nexport const trackingQuerySchema = z.object({\n providerKey: z.string().min(1),\n shipmentId: z.string().uuid().optional(),\n trackingNumber: z.string().min(1).optional(),\n}).refine((value) => Boolean(value.shipmentId || value.trackingNumber), {\n message: 'shipmentId or trackingNumber is required',\n path: ['shipmentId'],\n})\n\nexport const cancelShipmentSchema = z.object({\n providerKey: z.string().min(1),\n shipmentId: z.string().uuid(),\n reason: z.string().max(200).optional(),\n})\n\nexport const searchDropOffPointsQuerySchema = z.object({\n providerKey: z.string().min(1),\n query: z.string().optional(),\n type: z.string().optional(),\n postCode: z.string().optional(),\n})\n"],
5
- "mappings": "AAAA,SAAS,SAAS;AAElB,MAAM,gBAAgB,EAAE,OAAO;AAAA,EAC7B,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC;AAAA,EACpC,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC5B,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACtB,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACvB,OAAO,EAAE,OAAO,EAAE,SAAS;AAC7B,CAAC;AAED,MAAM,gBAAgB,EAAE,OAAO;AAAA,EAC7B,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,EAC7B,UAAU,EAAE,OAAO,EAAE,SAAS;AAChC,CAAC;AAGM,MAAM,cAAc;AAE3B,MAAM,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,wBAAwB,EAAE,IAAI,GAAG,EAAE,SAAS;AACvF,MAAM,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,uBAAuB,EAAE,IAAI,EAAE,EAAE,SAAS;AAE3F,MAAM,uBAAuB,EAAE,OAAO;AAAA,EAC3C,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC7B,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,UAAU,EAAE,MAAM,aAAa,EAAE,IAAI,CAAC;AAAA,EACtC,eAAe;AAAA,EACf,eAAe;AACjB,CAAC;AAEM,MAAM,uBAAuB,EAAE,OAAO;AAAA,EAC3C,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC7B,SAAS,EAAE,OAAO,EAAE,KAAK;AAAA,EACzB,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,UAAU,EAAE,MAAM,aAAa,EAAE,IAAI,CAAC;AAAA,EACtC,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC7B,aAAa,EAAE,KAAK,CAAC,OAAO,OAAO,KAAK,CAAC,EAAE,SAAS;AAAA,EACpD,aAAa;AAAA,EACb,aAAa;AAAA,EACb,eAAe;AAAA,EACf,eAAe;AAAA,EACf,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,kBAAkB,EAAE,KAAK,CAAC,iBAAiB,kBAAkB,OAAO,WAAW,CAAC,EAAE,SAAS;AAC7F,CAAC;AAEM,MAAM,sBAAsB,EAAE,OAAO;AAAA,EAC1C,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC7B,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACvC,gBAAgB,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAC7C,CAAC,EAAE,OAAO,CAAC,UAAU,QAAQ,MAAM,cAAc,MAAM,cAAc,GAAG;AAAA,EACtE,SAAS;AAAA,EACT,MAAM,CAAC,YAAY;AACrB,CAAC;AAEM,MAAM,uBAAuB,EAAE,OAAO;AAAA,EAC3C,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC7B,YAAY,EAAE,OAAO,EAAE,KAAK;AAAA,EAC5B,QAAQ,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,SAAS;AACvC,CAAC;AAEM,MAAM,iCAAiC,EAAE,OAAO;AAAA,EACrD,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC7B,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,EAC1B,UAAU,EAAE,OAAO,EAAE,SAAS;AAChC,CAAC;",
4
+ "sourcesContent": ["import { z } from 'zod'\n\nconst addressSchema = z.object({\n countryCode: z.string().min(2).max(3),\n postalCode: z.string().min(1),\n city: z.string().min(1),\n line1: z.string().min(1),\n line2: z.string().optional(),\n})\n\nconst packageSchema = z.object({\n weightKg: z.number().positive(),\n lengthCm: z.number().positive(),\n widthCm: z.number().positive(),\n heightCm: z.number().positive(),\n})\n\n/** Permissive phone regex: allows +, digits, spaces, dashes, dots, and parentheses. Min 7 chars. */\nexport const PHONE_REGEX = /^[+\\d][\\d\\s\\-().]{6,}$/\n\nconst emailField = z.string().trim().email('Invalid email address.').max(320).optional()\nconst phoneField = z.string().trim().regex(PHONE_REGEX, 'Invalid phone number.').max(50).optional()\n\nexport const calculateRatesSchema = z.object({\n providerKey: z.string().min(1),\n origin: addressSchema,\n destination: addressSchema,\n packages: z.array(packageSchema).min(1),\n receiverPhone: phoneField,\n receiverEmail: emailField,\n})\n\nexport const createShipmentSchema = z.object({\n providerKey: z.string().min(1),\n orderId: z.string().uuid(),\n origin: addressSchema,\n destination: addressSchema,\n packages: z.array(packageSchema).min(1),\n serviceCode: z.string().min(1),\n labelFormat: z.enum(['pdf', 'zpl', 'png']).optional(),\n senderPhone: phoneField,\n senderEmail: emailField,\n receiverPhone: phoneField,\n receiverEmail: emailField,\n targetPoint: z.string().optional(),\n c2cSendingMethod: z.enum(['parcel_locker', 'dispatch_order', 'pop', 'any_point']).optional(),\n})\n\nexport const trackingQuerySchema = z.object({\n providerKey: z.string().min(1),\n shipmentId: z.string().uuid().optional(),\n trackingNumber: z.string().min(1).optional(),\n}).refine((value) => Boolean(value.shipmentId || value.trackingNumber), {\n message: 'shipmentId or trackingNumber is required',\n path: ['shipmentId'],\n})\n\nexport const trackingRefreshSchema = z.object({\n providerKey: z.string().min(1),\n shipmentId: z.string().uuid(),\n})\n\nexport const cancelShipmentSchema = z.object({\n providerKey: z.string().min(1),\n shipmentId: z.string().uuid(),\n reason: z.string().max(200).optional(),\n})\n\nexport const searchDropOffPointsQuerySchema = z.object({\n providerKey: z.string().min(1),\n query: z.string().optional(),\n type: z.string().optional(),\n postCode: z.string().optional(),\n})\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,7 @@
1
1
  import * as React from "react";
2
2
  import { useRouter, useSearchParams } from "next/navigation";
3
3
  import { flash } from "@open-mercato/ui/backend/FlashMessages";
4
+ import { useGuardedMutation } from "@open-mercato/ui/backend/injection/useGuardedMutation";
4
5
  import { useT } from "@open-mercato/shared/lib/i18n/context";
5
6
  import {
6
7
  fetchProviders,
@@ -29,6 +30,9 @@ const useShipmentWizard = () => {
29
30
  const t = useT();
30
31
  const router = useRouter();
31
32
  const searchParams = useSearchParams();
33
+ const { runMutation, retryLastMutation } = useGuardedMutation({
34
+ contextId: "shipping_carriers.shipment.create"
35
+ });
32
36
  const orderId = searchParams?.get("orderId") ?? null;
33
37
  const [step, setStep] = React.useState("provider");
34
38
  const [providers, setProviders] = React.useState([]);
@@ -103,7 +107,7 @@ const useShipmentWizard = () => {
103
107
  const handleSubmit = async () => {
104
108
  if (!selectedProvider || !selectedRate || !orderId) return;
105
109
  setIsSubmitting(true);
106
- const result = await createShipment({
110
+ const payload = {
107
111
  providerKey: selectedProvider,
108
112
  orderId,
109
113
  origin,
@@ -117,13 +121,29 @@ const useShipmentWizard = () => {
117
121
  ...receiverContact.email ? { receiverEmail: receiverContact.email } : {},
118
122
  ...targetPoint ? { targetPoint } : {},
119
123
  ...c2cSendingMethod ? { c2cSendingMethod } : {}
120
- });
121
- setIsSubmitting(false);
122
- if (result.ok) {
123
- flash(t("shipping_carriers.create.success", "Shipment created successfully."), "success");
124
- router.push(backHref);
125
- } else {
126
- flash(t("shipping_carriers.create.error.create", result.error), "error");
124
+ };
125
+ try {
126
+ const result = await runMutation({
127
+ operation: () => createShipment(payload),
128
+ context: {
129
+ operation: "create",
130
+ resourceKind: "shipping_carriers.shipment",
131
+ resourceId: orderId,
132
+ retryLastMutation
133
+ },
134
+ mutationPayload: payload
135
+ });
136
+ if (result.ok) {
137
+ flash(t("shipping_carriers.create.success", "Shipment created successfully."), "success");
138
+ router.push(backHref);
139
+ } else {
140
+ flash(t("shipping_carriers.create.error.create", result.error), "error");
141
+ }
142
+ } catch (error) {
143
+ const message = error instanceof Error ? error.message : t("shipping_carriers.create.error.create", "Failed to create shipment.");
144
+ flash(t("shipping_carriers.create.error.create", message), "error");
145
+ } finally {
146
+ setIsSubmitting(false);
127
147
  }
128
148
  };
129
149
  const goToStep = (next) => setStep(next);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.ts"],
4
- "sourcesContent": ["import * as React from 'react'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n fetchProviders,\n fetchOrderAddresses,\n fetchRates,\n createShipment,\n fetchDropOffPoints,\n} from './shipmentApi'\nimport type {\n WizardStep,\n Provider,\n Address,\n PackageDimension,\n ShippingRate,\n LabelFormat,\n DocumentAddress,\n ContactInfo,\n DropOffPoint,\n} from '../types'\n\nconst EMPTY_ADDRESS: Address = { countryCode: '', postalCode: '', city: '', line1: '' }\nconst EMPTY_CONTACT: ContactInfo = { phone: '', email: '' }\nconst DEFAULT_PACKAGE: PackageDimension = { weightKg: 1, lengthCm: 20, widthCm: 15, heightCm: 10 }\n\nconst buildAddressFromDocument = (doc: DocumentAddress): Address => ({\n countryCode: doc.country ?? '',\n postalCode: doc.postal_code ?? '',\n city: doc.city ?? '',\n line1: doc.address_line1,\n line2: doc.address_line2 ?? undefined,\n})\n\nconst isAddressValid = (addr: Address) =>\n addr.countryCode.length >= 2 &&\n addr.postalCode.length > 0 &&\n addr.city.length > 0 &&\n addr.line1.length > 0\n\nconst isPackageValid = (pkg: PackageDimension) =>\n pkg.weightKg > 0 && pkg.lengthCm > 0 && pkg.widthCm > 0 && pkg.heightCm > 0\n\nconst EMAIL_REGEX = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\nconst PHONE_REGEX = /^[+\\d][\\d\\s\\-().]{6,}$/\n\nconst isEmailValid = (email: string) => email === '' || EMAIL_REGEX.test(email)\nconst isPhoneValid = (phone: string) => phone === '' || PHONE_REGEX.test(phone)\n\nexport type ShipmentWizard = {\n step: WizardStep\n backHref: string\n\n // Provider step\n providers: Provider[]\n isLoadingProviders: boolean\n providerError: string | null\n selectedProvider: string | null\n\n // Configure step\n origin: Address\n destination: Address\n packages: PackageDimension[]\n labelFormat: LabelFormat\n senderContact: ContactInfo\n receiverContact: ContactInfo\n targetPoint: string\n c2cSendingMethod: string\n isFetchingRates: boolean\n canProceedFromConfigure: boolean\n senderContactErrors: { email: string | null; phone: string | null }\n receiverContactErrors: { email: string | null; phone: string | null }\n\n // Drop-off point search\n dropOffPointQuery: string\n dropOffPoints: DropOffPoint[]\n isFetchingDropOffPoints: boolean\n dropOffPointsError: string | null\n\n // Confirm step\n rates: ShippingRate[]\n ratesError: string | null\n selectedRate: ShippingRate | null\n isSubmitting: boolean\n\n // Actions\n goToStep: (step: WizardStep) => void\n handleProviderSelect: (providerKey: string) => void\n handleConfigureNext: () => void\n handleSubmit: () => void\n setOrigin: (address: Address) => void\n setDestination: (address: Address) => void\n setPackages: (packages: PackageDimension[]) => void\n setLabelFormat: (format: LabelFormat) => void\n setSenderContact: (contact: ContactInfo) => void\n setReceiverContact: (contact: ContactInfo) => void\n setTargetPoint: (point: string) => void\n setC2cSendingMethod: (method: string) => void\n setSelectedRate: (rate: ShippingRate) => void\n searchDropOffPoints: (query: string) => void\n}\n\nexport const useShipmentWizard = (): ShipmentWizard => {\n const t = useT()\n const router = useRouter()\n const searchParams = useSearchParams()\n const orderId = searchParams?.get('orderId') ?? null\n\n const [step, setStep] = React.useState<WizardStep>('provider')\n const [providers, setProviders] = React.useState<Provider[]>([])\n const [isLoadingProviders, setIsLoadingProviders] = React.useState(true)\n const [providerError, setProviderError] = React.useState<string | null>(null)\n const [selectedProvider, setSelectedProvider] = React.useState<string | null>(null)\n\n const [origin, setOrigin] = React.useState<Address>(EMPTY_ADDRESS)\n const [destination, setDestination] = React.useState<Address>(EMPTY_ADDRESS)\n const [packages, setPackages] = React.useState<PackageDimension[]>([DEFAULT_PACKAGE])\n const [labelFormat, setLabelFormat] = React.useState<LabelFormat>('pdf')\n const [senderContact, setSenderContact] = React.useState<ContactInfo>(EMPTY_CONTACT)\n const [receiverContact, setReceiverContact] = React.useState<ContactInfo>(EMPTY_CONTACT)\n const [targetPoint, setTargetPoint] = React.useState<string>('')\n const [c2cSendingMethod, setC2cSendingMethod] = React.useState<string>('')\n\n const [dropOffPointQuery, setDropOffPointQuery] = React.useState<string>('')\n const [dropOffPoints, setDropOffPoints] = React.useState<DropOffPoint[]>([])\n const [isFetchingDropOffPoints, setIsFetchingDropOffPoints] = React.useState(false)\n const [dropOffPointsError, setDropOffPointsError] = React.useState<string | null>(null)\n\n const [rates, setRates] = React.useState<ShippingRate[]>([])\n const [isFetchingRates, setIsFetchingRates] = React.useState(false)\n const [ratesError, setRatesError] = React.useState<string | null>(null)\n const [selectedRate, setSelectedRate] = React.useState<ShippingRate | null>(null)\n\n const [isSubmitting, setIsSubmitting] = React.useState(false)\n\n const backHref = orderId ? `/backend/sales/orders/${orderId}` : '/backend/sales/orders'\n\n React.useEffect(() => {\n const loadProviders = async () => {\n setIsLoadingProviders(true)\n setProviderError(null)\n const result = await fetchProviders()\n if (result.ok) {\n setProviders(result.providers)\n } else {\n setProviderError(t('shipping_carriers.create.error.loadProviders', result.error))\n }\n setIsLoadingProviders(false)\n }\n void loadProviders()\n }, [t])\n\n React.useEffect(() => {\n if (!orderId) return\n const prefillAddresses = async () => {\n const result = await fetchOrderAddresses(orderId)\n if (!result.ok) return\n const items = result.items\n const shippingAddr =\n items.find((item) => item.purpose === 'shipping') ??\n items.find((item) => item.purpose === 'delivery') ??\n items[0]\n if (shippingAddr) setDestination(buildAddressFromDocument(shippingAddr))\n }\n void prefillAddresses()\n }, [orderId])\n\n const loadRates = async () => {\n if (!selectedProvider) return\n setIsFetchingRates(true)\n setRatesError(null)\n setRates([])\n setSelectedRate(null)\n const result = await fetchRates({\n providerKey: selectedProvider,\n origin,\n destination,\n packages,\n ...(receiverContact.phone ? { receiverPhone: receiverContact.phone } : {}),\n ...(receiverContact.email ? { receiverEmail: receiverContact.email } : {}),\n })\n if (result.ok) {\n setRates(result.rates)\n if (result.rates.length > 0) setSelectedRate(result.rates[0])\n } else {\n setRatesError(t('shipping_carriers.create.error.fetchRates', result.error))\n }\n setIsFetchingRates(false)\n }\n\n const handleSubmit = async () => {\n if (!selectedProvider || !selectedRate || !orderId) return\n setIsSubmitting(true)\n const result = await createShipment({\n providerKey: selectedProvider,\n orderId,\n origin,\n destination,\n packages,\n serviceCode: selectedRate.serviceCode,\n labelFormat,\n ...(senderContact.phone ? { senderPhone: senderContact.phone } : {}),\n ...(senderContact.email ? { senderEmail: senderContact.email } : {}),\n ...(receiverContact.phone ? { receiverPhone: receiverContact.phone } : {}),\n ...(receiverContact.email ? { receiverEmail: receiverContact.email } : {}),\n ...(targetPoint ? { targetPoint } : {}),\n ...(c2cSendingMethod ? { c2cSendingMethod } : {}),\n })\n setIsSubmitting(false)\n if (result.ok) {\n flash(t('shipping_carriers.create.success', 'Shipment created successfully.'), 'success')\n router.push(backHref)\n } else {\n flash(t('shipping_carriers.create.error.create', result.error), 'error')\n }\n }\n\n const goToStep = (next: WizardStep) => setStep(next)\n\n const handleProviderSelect = (providerKey: string) => {\n setSelectedProvider(providerKey)\n goToStep('configure')\n }\n\n const handleConfigureNext = () => {\n void loadRates().then(() => goToStep('confirm'))\n }\n\n const searchDropOffPoints = async (query: string) => {\n if (!selectedProvider) return\n setDropOffPointQuery(query)\n setIsFetchingDropOffPoints(true)\n setDropOffPointsError(null)\n const isPostCode = /^\\d{2}-?\\d{3}$/.test(query.trim())\n const resolvedType = c2cSendingMethod === 'pop' ? 'pop' : 'parcel_locker'\n const result = await fetchDropOffPoints({\n providerKey: selectedProvider,\n ...(isPostCode ? { postCode: query.trim() } : { query: query.trim() }),\n type: resolvedType,\n })\n if (result.ok) {\n setDropOffPoints(result.points)\n } else {\n setDropOffPointsError(t('shipping_carriers.create.error.fetchDropOffPoints', result.error))\n setDropOffPoints([])\n }\n setIsFetchingDropOffPoints(false)\n }\n\n const canProceedFromConfigure =\n isAddressValid(origin) &&\n isAddressValid(destination) &&\n packages.length > 0 &&\n packages.every(isPackageValid) &&\n isEmailValid(senderContact.email) &&\n isPhoneValid(senderContact.phone) &&\n isEmailValid(receiverContact.email) &&\n isPhoneValid(receiverContact.phone)\n\n const senderContactErrors = {\n email: !isEmailValid(senderContact.email) ? t('shipping_carriers.create.error.invalidEmail', 'Invalid email address.') : null,\n phone: !isPhoneValid(senderContact.phone) ? t('shipping_carriers.create.error.invalidPhone', 'Invalid phone number.') : null,\n }\n const receiverContactErrors = {\n email: !isEmailValid(receiverContact.email) ? t('shipping_carriers.create.error.invalidEmail', 'Invalid email address.') : null,\n phone: !isPhoneValid(receiverContact.phone) ? t('shipping_carriers.create.error.invalidPhone', 'Invalid phone number.') : null,\n }\n\n return {\n step,\n backHref,\n providers,\n isLoadingProviders,\n providerError,\n selectedProvider,\n origin,\n destination,\n packages,\n labelFormat,\n senderContact,\n receiverContact,\n targetPoint,\n c2cSendingMethod,\n isFetchingRates,\n canProceedFromConfigure,\n senderContactErrors,\n receiverContactErrors,\n dropOffPointQuery,\n dropOffPoints,\n isFetchingDropOffPoints,\n dropOffPointsError,\n rates,\n ratesError,\n selectedRate,\n isSubmitting,\n goToStep,\n handleProviderSelect,\n handleConfigureNext,\n handleSubmit: () => void handleSubmit(),\n setOrigin,\n setDestination,\n setPackages,\n setLabelFormat,\n setSenderContact,\n setReceiverContact,\n setTargetPoint,\n setC2cSendingMethod,\n setSelectedRate,\n searchDropOffPoints: (query: string) => void searchDropOffPoints(query),\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import * as React from 'react'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n fetchProviders,\n fetchOrderAddresses,\n fetchRates,\n createShipment,\n fetchDropOffPoints,\n} from './shipmentApi'\nimport type { CreateShipmentParams } from './shipmentApi'\nimport type {\n WizardStep,\n Provider,\n Address,\n PackageDimension,\n ShippingRate,\n LabelFormat,\n DocumentAddress,\n ContactInfo,\n DropOffPoint,\n} from '../types'\n\nconst EMPTY_ADDRESS: Address = { countryCode: '', postalCode: '', city: '', line1: '' }\nconst EMPTY_CONTACT: ContactInfo = { phone: '', email: '' }\nconst DEFAULT_PACKAGE: PackageDimension = { weightKg: 1, lengthCm: 20, widthCm: 15, heightCm: 10 }\n\nconst buildAddressFromDocument = (doc: DocumentAddress): Address => ({\n countryCode: doc.country ?? '',\n postalCode: doc.postal_code ?? '',\n city: doc.city ?? '',\n line1: doc.address_line1,\n line2: doc.address_line2 ?? undefined,\n})\n\nconst isAddressValid = (addr: Address) =>\n addr.countryCode.length >= 2 &&\n addr.postalCode.length > 0 &&\n addr.city.length > 0 &&\n addr.line1.length > 0\n\nconst isPackageValid = (pkg: PackageDimension) =>\n pkg.weightKg > 0 && pkg.lengthCm > 0 && pkg.widthCm > 0 && pkg.heightCm > 0\n\nconst EMAIL_REGEX = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\nconst PHONE_REGEX = /^[+\\d][\\d\\s\\-().]{6,}$/\n\nconst isEmailValid = (email: string) => email === '' || EMAIL_REGEX.test(email)\nconst isPhoneValid = (phone: string) => phone === '' || PHONE_REGEX.test(phone)\n\nexport type ShipmentWizard = {\n step: WizardStep\n backHref: string\n\n // Provider step\n providers: Provider[]\n isLoadingProviders: boolean\n providerError: string | null\n selectedProvider: string | null\n\n // Configure step\n origin: Address\n destination: Address\n packages: PackageDimension[]\n labelFormat: LabelFormat\n senderContact: ContactInfo\n receiverContact: ContactInfo\n targetPoint: string\n c2cSendingMethod: string\n isFetchingRates: boolean\n canProceedFromConfigure: boolean\n senderContactErrors: { email: string | null; phone: string | null }\n receiverContactErrors: { email: string | null; phone: string | null }\n\n // Drop-off point search\n dropOffPointQuery: string\n dropOffPoints: DropOffPoint[]\n isFetchingDropOffPoints: boolean\n dropOffPointsError: string | null\n\n // Confirm step\n rates: ShippingRate[]\n ratesError: string | null\n selectedRate: ShippingRate | null\n isSubmitting: boolean\n\n // Actions\n goToStep: (step: WizardStep) => void\n handleProviderSelect: (providerKey: string) => void\n handleConfigureNext: () => void\n handleSubmit: () => void\n setOrigin: (address: Address) => void\n setDestination: (address: Address) => void\n setPackages: (packages: PackageDimension[]) => void\n setLabelFormat: (format: LabelFormat) => void\n setSenderContact: (contact: ContactInfo) => void\n setReceiverContact: (contact: ContactInfo) => void\n setTargetPoint: (point: string) => void\n setC2cSendingMethod: (method: string) => void\n setSelectedRate: (rate: ShippingRate) => void\n searchDropOffPoints: (query: string) => void\n}\n\nexport const useShipmentWizard = (): ShipmentWizard => {\n const t = useT()\n const router = useRouter()\n const searchParams = useSearchParams()\n const { runMutation, retryLastMutation } = useGuardedMutation({\n contextId: 'shipping_carriers.shipment.create',\n })\n const orderId = searchParams?.get('orderId') ?? null\n\n const [step, setStep] = React.useState<WizardStep>('provider')\n const [providers, setProviders] = React.useState<Provider[]>([])\n const [isLoadingProviders, setIsLoadingProviders] = React.useState(true)\n const [providerError, setProviderError] = React.useState<string | null>(null)\n const [selectedProvider, setSelectedProvider] = React.useState<string | null>(null)\n\n const [origin, setOrigin] = React.useState<Address>(EMPTY_ADDRESS)\n const [destination, setDestination] = React.useState<Address>(EMPTY_ADDRESS)\n const [packages, setPackages] = React.useState<PackageDimension[]>([DEFAULT_PACKAGE])\n const [labelFormat, setLabelFormat] = React.useState<LabelFormat>('pdf')\n const [senderContact, setSenderContact] = React.useState<ContactInfo>(EMPTY_CONTACT)\n const [receiverContact, setReceiverContact] = React.useState<ContactInfo>(EMPTY_CONTACT)\n const [targetPoint, setTargetPoint] = React.useState<string>('')\n const [c2cSendingMethod, setC2cSendingMethod] = React.useState<string>('')\n\n const [dropOffPointQuery, setDropOffPointQuery] = React.useState<string>('')\n const [dropOffPoints, setDropOffPoints] = React.useState<DropOffPoint[]>([])\n const [isFetchingDropOffPoints, setIsFetchingDropOffPoints] = React.useState(false)\n const [dropOffPointsError, setDropOffPointsError] = React.useState<string | null>(null)\n\n const [rates, setRates] = React.useState<ShippingRate[]>([])\n const [isFetchingRates, setIsFetchingRates] = React.useState(false)\n const [ratesError, setRatesError] = React.useState<string | null>(null)\n const [selectedRate, setSelectedRate] = React.useState<ShippingRate | null>(null)\n\n const [isSubmitting, setIsSubmitting] = React.useState(false)\n\n const backHref = orderId ? `/backend/sales/orders/${orderId}` : '/backend/sales/orders'\n\n React.useEffect(() => {\n const loadProviders = async () => {\n setIsLoadingProviders(true)\n setProviderError(null)\n const result = await fetchProviders()\n if (result.ok) {\n setProviders(result.providers)\n } else {\n setProviderError(t('shipping_carriers.create.error.loadProviders', result.error))\n }\n setIsLoadingProviders(false)\n }\n void loadProviders()\n }, [t])\n\n React.useEffect(() => {\n if (!orderId) return\n const prefillAddresses = async () => {\n const result = await fetchOrderAddresses(orderId)\n if (!result.ok) return\n const items = result.items\n const shippingAddr =\n items.find((item) => item.purpose === 'shipping') ??\n items.find((item) => item.purpose === 'delivery') ??\n items[0]\n if (shippingAddr) setDestination(buildAddressFromDocument(shippingAddr))\n }\n void prefillAddresses()\n }, [orderId])\n\n const loadRates = async () => {\n if (!selectedProvider) return\n setIsFetchingRates(true)\n setRatesError(null)\n setRates([])\n setSelectedRate(null)\n const result = await fetchRates({\n providerKey: selectedProvider,\n origin,\n destination,\n packages,\n ...(receiverContact.phone ? { receiverPhone: receiverContact.phone } : {}),\n ...(receiverContact.email ? { receiverEmail: receiverContact.email } : {}),\n })\n if (result.ok) {\n setRates(result.rates)\n if (result.rates.length > 0) setSelectedRate(result.rates[0])\n } else {\n setRatesError(t('shipping_carriers.create.error.fetchRates', result.error))\n }\n setIsFetchingRates(false)\n }\n\n const handleSubmit = async () => {\n if (!selectedProvider || !selectedRate || !orderId) return\n setIsSubmitting(true)\n const payload: CreateShipmentParams = {\n providerKey: selectedProvider,\n orderId,\n origin,\n destination,\n packages,\n serviceCode: selectedRate.serviceCode,\n labelFormat,\n ...(senderContact.phone ? { senderPhone: senderContact.phone } : {}),\n ...(senderContact.email ? { senderEmail: senderContact.email } : {}),\n ...(receiverContact.phone ? { receiverPhone: receiverContact.phone } : {}),\n ...(receiverContact.email ? { receiverEmail: receiverContact.email } : {}),\n ...(targetPoint ? { targetPoint } : {}),\n ...(c2cSendingMethod ? { c2cSendingMethod } : {}),\n }\n try {\n const result = await runMutation({\n operation: () => createShipment(payload),\n context: {\n operation: 'create',\n resourceKind: 'shipping_carriers.shipment',\n resourceId: orderId,\n retryLastMutation,\n },\n mutationPayload: payload as unknown as Record<string, unknown>,\n })\n if (result.ok) {\n flash(t('shipping_carriers.create.success', 'Shipment created successfully.'), 'success')\n router.push(backHref)\n } else {\n flash(t('shipping_carriers.create.error.create', result.error), 'error')\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : t('shipping_carriers.create.error.create', 'Failed to create shipment.')\n flash(t('shipping_carriers.create.error.create', message), 'error')\n } finally {\n setIsSubmitting(false)\n }\n }\n\n const goToStep = (next: WizardStep) => setStep(next)\n\n const handleProviderSelect = (providerKey: string) => {\n setSelectedProvider(providerKey)\n goToStep('configure')\n }\n\n const handleConfigureNext = () => {\n void loadRates().then(() => goToStep('confirm'))\n }\n\n const searchDropOffPoints = async (query: string) => {\n if (!selectedProvider) return\n setDropOffPointQuery(query)\n setIsFetchingDropOffPoints(true)\n setDropOffPointsError(null)\n const isPostCode = /^\\d{2}-?\\d{3}$/.test(query.trim())\n const resolvedType = c2cSendingMethod === 'pop' ? 'pop' : 'parcel_locker'\n const result = await fetchDropOffPoints({\n providerKey: selectedProvider,\n ...(isPostCode ? { postCode: query.trim() } : { query: query.trim() }),\n type: resolvedType,\n })\n if (result.ok) {\n setDropOffPoints(result.points)\n } else {\n setDropOffPointsError(t('shipping_carriers.create.error.fetchDropOffPoints', result.error))\n setDropOffPoints([])\n }\n setIsFetchingDropOffPoints(false)\n }\n\n const canProceedFromConfigure =\n isAddressValid(origin) &&\n isAddressValid(destination) &&\n packages.length > 0 &&\n packages.every(isPackageValid) &&\n isEmailValid(senderContact.email) &&\n isPhoneValid(senderContact.phone) &&\n isEmailValid(receiverContact.email) &&\n isPhoneValid(receiverContact.phone)\n\n const senderContactErrors = {\n email: !isEmailValid(senderContact.email) ? t('shipping_carriers.create.error.invalidEmail', 'Invalid email address.') : null,\n phone: !isPhoneValid(senderContact.phone) ? t('shipping_carriers.create.error.invalidPhone', 'Invalid phone number.') : null,\n }\n const receiverContactErrors = {\n email: !isEmailValid(receiverContact.email) ? t('shipping_carriers.create.error.invalidEmail', 'Invalid email address.') : null,\n phone: !isPhoneValid(receiverContact.phone) ? t('shipping_carriers.create.error.invalidPhone', 'Invalid phone number.') : null,\n }\n\n return {\n step,\n backHref,\n providers,\n isLoadingProviders,\n providerError,\n selectedProvider,\n origin,\n destination,\n packages,\n labelFormat,\n senderContact,\n receiverContact,\n targetPoint,\n c2cSendingMethod,\n isFetchingRates,\n canProceedFromConfigure,\n senderContactErrors,\n receiverContactErrors,\n dropOffPointQuery,\n dropOffPoints,\n isFetchingDropOffPoints,\n dropOffPointsError,\n rates,\n ratesError,\n selectedRate,\n isSubmitting,\n goToStep,\n handleProviderSelect,\n handleConfigureNext,\n handleSubmit: () => void handleSubmit(),\n setOrigin,\n setDestination,\n setPackages,\n setLabelFormat,\n setSenderContact,\n setReceiverContact,\n setTargetPoint,\n setC2cSendingMethod,\n setSelectedRate,\n searchDropOffPoints: (query: string) => void searchDropOffPoints(query),\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -2,7 +2,13 @@ import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find"
2
2
  import { CarrierShipment } from "../data/entities.js";
3
3
  import { emitShippingEvent } from "../events.js";
4
4
  import { getShippingAdapter } from "./adapter-registry.js";
5
- import { isValidShippingTransition, ShipmentCancelNotAllowedError } from "./status-sync.js";
5
+ import {
6
+ getTerminalShippingEvent,
7
+ isValidShippingTransition,
8
+ ShipmentCancelNotAllowedError,
9
+ syncShipmentStatus,
10
+ TERMINAL_SHIPPING_STATUSES
11
+ } from "./status-sync.js";
6
12
  function createShippingCarrierService(deps) {
7
13
  const { em, integrationCredentialsService } = deps;
8
14
  async function findShipmentOrThrow(shipmentId, scope) {
@@ -112,16 +118,40 @@ function createShippingCarrierService(deps) {
112
118
  tenantId: input.tenantId
113
119
  }
114
120
  ) : null;
115
- const tracking = await adapter.getTracking({
121
+ return adapter.getTracking({
116
122
  shipmentId: shipment?.carrierShipmentId ?? input.shipmentId,
117
123
  trackingNumber: input.trackingNumber ?? shipment?.trackingNumber,
118
124
  credentials
119
125
  });
120
- if (shipment) {
121
- shipment.unifiedStatus = tracking.status;
122
- shipment.trackingEvents = tracking.events;
123
- shipment.lastPolledAt = /* @__PURE__ */ new Date();
124
- await em.flush();
126
+ },
127
+ async refreshTracking(input) {
128
+ const scope = { organizationId: input.organizationId, tenantId: input.tenantId };
129
+ const shipment = await findShipmentOrThrow(input.shipmentId, scope);
130
+ const { adapter, credentials } = await resolveAdapter(input.providerKey, scope);
131
+ const tracking = await adapter.getTracking({
132
+ shipmentId: shipment.carrierShipmentId,
133
+ trackingNumber: shipment.trackingNumber,
134
+ credentials
135
+ });
136
+ const previousStatus = shipment.unifiedStatus;
137
+ shipment.trackingEvents = tracking.events;
138
+ shipment.lastPolledAt = /* @__PURE__ */ new Date();
139
+ const transitionApplied = syncShipmentStatus(shipment, tracking.status);
140
+ await em.flush();
141
+ if (transitionApplied) {
142
+ const eventPayload = {
143
+ shipmentId: shipment.id,
144
+ providerKey: input.providerKey,
145
+ previousStatus,
146
+ newStatus: tracking.status,
147
+ organizationId: input.organizationId,
148
+ tenantId: input.tenantId
149
+ };
150
+ await emitShippingEvent("shipping_carriers.shipment.status_changed", eventPayload);
151
+ if (TERMINAL_SHIPPING_STATUSES.has(tracking.status)) {
152
+ const terminalEvent = getTerminalShippingEvent(tracking.status);
153
+ if (terminalEvent) await emitShippingEvent(terminalEvent, eventPayload);
154
+ }
125
155
  }
126
156
  return tracking;
127
157
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/shipping_carriers/lib/shipping-service.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { CredentialsService } from '../../integrations/lib/credentials-service'\nimport { CarrierShipment } from '../data/entities'\nimport { emitShippingEvent } from '../events'\nimport { getShippingAdapter } from './adapter-registry'\nimport type { UnifiedShipmentStatus } from './adapter'\nimport { isValidShippingTransition, ShipmentCancelNotAllowedError } from './status-sync'\n\nexport function createShippingCarrierService(deps: {\n em: EntityManager\n integrationCredentialsService: CredentialsService\n}) {\n const { em, integrationCredentialsService } = deps\n\n async function findShipmentOrThrow(\n shipmentId: string,\n scope: { organizationId: string; tenantId: string },\n ): Promise<CarrierShipment> {\n const shipment = await findOneWithDecryption(\n em,\n CarrierShipment,\n {\n id: shipmentId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n if (!shipment) {\n throw new Error('Shipment not found')\n }\n return shipment\n }\n\n async function resolveAdapter(providerKey: string, scope: { organizationId: string; tenantId: string }) {\n const adapter = getShippingAdapter(providerKey)\n if (!adapter) throw new Error(`No shipping adapter registered for provider: ${providerKey}`)\n const credentials = await integrationCredentialsService.resolve(`carrier_${providerKey}`, scope) ?? {}\n return { adapter, credentials }\n }\n\n return {\n async calculateRates(input: {\n providerKey: string\n origin: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n destination: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n packages: Array<{ weightKg: number; lengthCm: number; widthCm: number; heightCm: number }>\n receiverPhone?: string\n receiverEmail?: string\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const mergedCredentials = {\n ...credentials,\n ...(input.receiverPhone !== undefined ? { receiverPhone: input.receiverPhone } : {}),\n ...(input.receiverEmail !== undefined ? { receiverEmail: input.receiverEmail } : {}),\n }\n return adapter.calculateRates({\n origin: input.origin,\n destination: input.destination,\n packages: input.packages,\n credentials: mergedCredentials,\n })\n },\n\n async createShipment(input: {\n providerKey: string\n orderId: string\n origin: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n destination: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n packages: Array<{ weightKg: number; lengthCm: number; widthCm: number; heightCm: number }>\n serviceCode: string\n labelFormat?: 'pdf' | 'zpl' | 'png'\n senderPhone?: string\n senderEmail?: string\n receiverPhone?: string\n receiverEmail?: string\n targetPoint?: string\n c2cSendingMethod?: string\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const mergedCredentials = {\n ...credentials,\n ...(input.senderPhone !== undefined ? { senderPhone: input.senderPhone } : {}),\n ...(input.senderEmail !== undefined ? { senderEmail: input.senderEmail } : {}),\n ...(input.receiverPhone !== undefined ? { receiverPhone: input.receiverPhone } : {}),\n ...(input.receiverEmail !== undefined ? { receiverEmail: input.receiverEmail } : {}),\n ...(input.targetPoint !== undefined ? { targetPoint: input.targetPoint } : {}),\n ...(input.c2cSendingMethod !== undefined ? { c2cSendingMethod: input.c2cSendingMethod } : {}),\n }\n const created = await adapter.createShipment({\n orderId: input.orderId,\n origin: input.origin,\n destination: input.destination,\n packages: input.packages,\n serviceCode: input.serviceCode,\n credentials: mergedCredentials,\n labelFormat: input.labelFormat,\n })\n const shipment = em.create(CarrierShipment, {\n orderId: input.orderId,\n providerKey: input.providerKey,\n carrierShipmentId: created.shipmentId,\n trackingNumber: created.trackingNumber,\n unifiedStatus: 'label_created',\n labelUrl: created.labelUrl ?? null,\n labelData: created.labelData ?? null,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n await em.persist(shipment).flush()\n await emitShippingEvent('shipping_carriers.shipment.created', {\n shipmentId: shipment.id,\n orderId: input.orderId,\n providerKey: input.providerKey,\n trackingNumber: created.trackingNumber,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n return shipment\n },\n\n async getTracking(input: {\n providerKey: string\n shipmentId?: string\n trackingNumber?: string\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const shipment = input.shipmentId\n ? await findOneWithDecryption(\n em,\n CarrierShipment,\n {\n id: input.shipmentId,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n deletedAt: null,\n },\n undefined,\n {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n },\n )\n : null\n const tracking = await adapter.getTracking({\n shipmentId: shipment?.carrierShipmentId ?? input.shipmentId,\n trackingNumber: input.trackingNumber ?? shipment?.trackingNumber,\n credentials,\n })\n if (shipment) {\n shipment.unifiedStatus = tracking.status\n shipment.trackingEvents = tracking.events\n shipment.lastPolledAt = new Date()\n await em.flush()\n }\n return tracking\n },\n\n async cancelShipment(input: {\n providerKey: string\n shipmentId: string\n reason?: string\n organizationId: string\n tenantId: string\n }) {\n const scope = { organizationId: input.organizationId, tenantId: input.tenantId }\n const shipment = await findShipmentOrThrow(input.shipmentId, scope)\n if (!isValidShippingTransition(shipment.unifiedStatus as UnifiedShipmentStatus, 'cancelled')) {\n throw new ShipmentCancelNotAllowedError(shipment.unifiedStatus)\n }\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const result = await adapter.cancelShipment({\n shipmentId: shipment.carrierShipmentId,\n reason: input.reason,\n credentials,\n })\n shipment.unifiedStatus = result.status\n await em.flush()\n await emitShippingEvent('shipping_carriers.shipment.cancelled', {\n shipmentId: shipment.id,\n providerKey: input.providerKey,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n return result\n },\n\n async findShipmentByCarrierId(\n providerKey: string,\n carrierShipmentId: string,\n scope: { organizationId: string; tenantId: string },\n ) {\n return findOneWithDecryption(\n em,\n CarrierShipment,\n {\n providerKey,\n carrierShipmentId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n },\n\n async searchDropOffPoints(input: {\n providerKey: string\n query?: string\n type?: string\n postCode?: string\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n if (!adapter.searchDropOffPoints) {\n throw new Error(`Provider ${input.providerKey} does not support drop-off point search`)\n }\n return adapter.searchDropOffPoints({\n query: input.query,\n type: input.type,\n postCode: input.postCode,\n credentials,\n })\n },\n }\n}\n\nexport type ShippingCarrierService = ReturnType<typeof createShippingCarrierService>\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { CredentialsService } from '../../integrations/lib/credentials-service'\nimport { CarrierShipment } from '../data/entities'\nimport { emitShippingEvent } from '../events'\nimport { getShippingAdapter } from './adapter-registry'\nimport type { UnifiedShipmentStatus } from './adapter'\nimport {\n getTerminalShippingEvent,\n isValidShippingTransition,\n ShipmentCancelNotAllowedError,\n syncShipmentStatus,\n TERMINAL_SHIPPING_STATUSES,\n} from './status-sync'\n\nexport function createShippingCarrierService(deps: {\n em: EntityManager\n integrationCredentialsService: CredentialsService\n}) {\n const { em, integrationCredentialsService } = deps\n\n async function findShipmentOrThrow(\n shipmentId: string,\n scope: { organizationId: string; tenantId: string },\n ): Promise<CarrierShipment> {\n const shipment = await findOneWithDecryption(\n em,\n CarrierShipment,\n {\n id: shipmentId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n if (!shipment) {\n throw new Error('Shipment not found')\n }\n return shipment\n }\n\n async function resolveAdapter(providerKey: string, scope: { organizationId: string; tenantId: string }) {\n const adapter = getShippingAdapter(providerKey)\n if (!adapter) throw new Error(`No shipping adapter registered for provider: ${providerKey}`)\n const credentials = await integrationCredentialsService.resolve(`carrier_${providerKey}`, scope) ?? {}\n return { adapter, credentials }\n }\n\n return {\n async calculateRates(input: {\n providerKey: string\n origin: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n destination: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n packages: Array<{ weightKg: number; lengthCm: number; widthCm: number; heightCm: number }>\n receiverPhone?: string\n receiverEmail?: string\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const mergedCredentials = {\n ...credentials,\n ...(input.receiverPhone !== undefined ? { receiverPhone: input.receiverPhone } : {}),\n ...(input.receiverEmail !== undefined ? { receiverEmail: input.receiverEmail } : {}),\n }\n return adapter.calculateRates({\n origin: input.origin,\n destination: input.destination,\n packages: input.packages,\n credentials: mergedCredentials,\n })\n },\n\n async createShipment(input: {\n providerKey: string\n orderId: string\n origin: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n destination: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n packages: Array<{ weightKg: number; lengthCm: number; widthCm: number; heightCm: number }>\n serviceCode: string\n labelFormat?: 'pdf' | 'zpl' | 'png'\n senderPhone?: string\n senderEmail?: string\n receiverPhone?: string\n receiverEmail?: string\n targetPoint?: string\n c2cSendingMethod?: string\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const mergedCredentials = {\n ...credentials,\n ...(input.senderPhone !== undefined ? { senderPhone: input.senderPhone } : {}),\n ...(input.senderEmail !== undefined ? { senderEmail: input.senderEmail } : {}),\n ...(input.receiverPhone !== undefined ? { receiverPhone: input.receiverPhone } : {}),\n ...(input.receiverEmail !== undefined ? { receiverEmail: input.receiverEmail } : {}),\n ...(input.targetPoint !== undefined ? { targetPoint: input.targetPoint } : {}),\n ...(input.c2cSendingMethod !== undefined ? { c2cSendingMethod: input.c2cSendingMethod } : {}),\n }\n const created = await adapter.createShipment({\n orderId: input.orderId,\n origin: input.origin,\n destination: input.destination,\n packages: input.packages,\n serviceCode: input.serviceCode,\n credentials: mergedCredentials,\n labelFormat: input.labelFormat,\n })\n const shipment = em.create(CarrierShipment, {\n orderId: input.orderId,\n providerKey: input.providerKey,\n carrierShipmentId: created.shipmentId,\n trackingNumber: created.trackingNumber,\n unifiedStatus: 'label_created',\n labelUrl: created.labelUrl ?? null,\n labelData: created.labelData ?? null,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n await em.persist(shipment).flush()\n await emitShippingEvent('shipping_carriers.shipment.created', {\n shipmentId: shipment.id,\n orderId: input.orderId,\n providerKey: input.providerKey,\n trackingNumber: created.trackingNumber,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n return shipment\n },\n\n async getTracking(input: {\n providerKey: string\n shipmentId?: string\n trackingNumber?: string\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const shipment = input.shipmentId\n ? await findOneWithDecryption(\n em,\n CarrierShipment,\n {\n id: input.shipmentId,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n deletedAt: null,\n },\n undefined,\n {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n },\n )\n : null\n return adapter.getTracking({\n shipmentId: shipment?.carrierShipmentId ?? input.shipmentId,\n trackingNumber: input.trackingNumber ?? shipment?.trackingNumber,\n credentials,\n })\n },\n\n async refreshTracking(input: {\n providerKey: string\n shipmentId: string\n organizationId: string\n tenantId: string\n }) {\n const scope = { organizationId: input.organizationId, tenantId: input.tenantId }\n const shipment = await findShipmentOrThrow(input.shipmentId, scope)\n const { adapter, credentials } = await resolveAdapter(input.providerKey, scope)\n const tracking = await adapter.getTracking({\n shipmentId: shipment.carrierShipmentId,\n trackingNumber: shipment.trackingNumber,\n credentials,\n })\n const previousStatus = shipment.unifiedStatus\n shipment.trackingEvents = tracking.events\n shipment.lastPolledAt = new Date()\n const transitionApplied = syncShipmentStatus(shipment, tracking.status)\n await em.flush()\n if (transitionApplied) {\n const eventPayload = {\n shipmentId: shipment.id,\n providerKey: input.providerKey,\n previousStatus,\n newStatus: tracking.status,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n }\n await emitShippingEvent('shipping_carriers.shipment.status_changed', eventPayload)\n if (TERMINAL_SHIPPING_STATUSES.has(tracking.status)) {\n const terminalEvent = getTerminalShippingEvent(tracking.status)\n if (terminalEvent) await emitShippingEvent(terminalEvent, eventPayload)\n }\n }\n return tracking\n },\n\n async cancelShipment(input: {\n providerKey: string\n shipmentId: string\n reason?: string\n organizationId: string\n tenantId: string\n }) {\n const scope = { organizationId: input.organizationId, tenantId: input.tenantId }\n const shipment = await findShipmentOrThrow(input.shipmentId, scope)\n if (!isValidShippingTransition(shipment.unifiedStatus as UnifiedShipmentStatus, 'cancelled')) {\n throw new ShipmentCancelNotAllowedError(shipment.unifiedStatus)\n }\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const result = await adapter.cancelShipment({\n shipmentId: shipment.carrierShipmentId,\n reason: input.reason,\n credentials,\n })\n shipment.unifiedStatus = result.status\n await em.flush()\n await emitShippingEvent('shipping_carriers.shipment.cancelled', {\n shipmentId: shipment.id,\n providerKey: input.providerKey,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n return result\n },\n\n async findShipmentByCarrierId(\n providerKey: string,\n carrierShipmentId: string,\n scope: { organizationId: string; tenantId: string },\n ) {\n return findOneWithDecryption(\n em,\n CarrierShipment,\n {\n providerKey,\n carrierShipmentId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n },\n\n async searchDropOffPoints(input: {\n providerKey: string\n query?: string\n type?: string\n postCode?: string\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n if (!adapter.searchDropOffPoints) {\n throw new Error(`Provider ${input.providerKey} does not support drop-off point search`)\n }\n return adapter.searchDropOffPoints({\n query: input.query,\n type: input.type,\n postCode: input.postCode,\n credentials,\n })\n },\n }\n}\n\nexport type ShippingCarrierService = ReturnType<typeof createShippingCarrierService>\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -6,7 +6,7 @@ const metadata = {
6
6
  async function handle(job, ctx) {
7
7
  const service = ctx.resolve("shippingCarrierService");
8
8
  for (const shipmentId of job.payload.shipmentIds) {
9
- await service.getTracking({
9
+ await service.refreshTracking({
10
10
  providerKey: job.payload.providerKey,
11
11
  shipmentId,
12
12
  organizationId: job.payload.scope.organizationId,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/shipping_carriers/workers/status-poller.ts"],
4
- "sourcesContent": ["import type { JobContext, QueuedJob, WorkerMeta } from '@open-mercato/queue'\nimport type { ShippingCarrierService } from '../lib/shipping-service'\n\ntype PollerJobPayload = {\n providerKey: string\n shipmentIds: string[]\n scope: {\n organizationId: string\n tenantId: string\n }\n}\n\ntype HandlerContext = JobContext & {\n resolve: <T = unknown>(name: string) => T\n}\n\nexport const metadata: WorkerMeta = {\n queue: 'shipping-carriers-status-poller',\n id: 'shipping-carriers:status-poller',\n concurrency: 2,\n}\n\nexport default async function handle(job: QueuedJob<PollerJobPayload>, ctx: HandlerContext): Promise<void> {\n const service = ctx.resolve<ShippingCarrierService>('shippingCarrierService')\n for (const shipmentId of job.payload.shipmentIds) {\n await service.getTracking({\n providerKey: job.payload.providerKey,\n shipmentId,\n organizationId: job.payload.scope.organizationId,\n tenantId: job.payload.scope.tenantId,\n })\n }\n}\n"],
5
- "mappings": "AAgBO,MAAM,WAAuB;AAAA,EAClC,OAAO;AAAA,EACP,IAAI;AAAA,EACJ,aAAa;AACf;AAEA,eAAO,OAA8B,KAAkC,KAAoC;AACzG,QAAM,UAAU,IAAI,QAAgC,wBAAwB;AAC5E,aAAW,cAAc,IAAI,QAAQ,aAAa;AAChD,UAAM,QAAQ,YAAY;AAAA,MACxB,aAAa,IAAI,QAAQ;AAAA,MACzB;AAAA,MACA,gBAAgB,IAAI,QAAQ,MAAM;AAAA,MAClC,UAAU,IAAI,QAAQ,MAAM;AAAA,IAC9B,CAAC;AAAA,EACH;AACF;",
4
+ "sourcesContent": ["import type { JobContext, QueuedJob, WorkerMeta } from '@open-mercato/queue'\nimport type { ShippingCarrierService } from '../lib/shipping-service'\n\ntype PollerJobPayload = {\n providerKey: string\n shipmentIds: string[]\n scope: {\n organizationId: string\n tenantId: string\n }\n}\n\ntype HandlerContext = JobContext & {\n resolve: <T = unknown>(name: string) => T\n}\n\nexport const metadata: WorkerMeta = {\n queue: 'shipping-carriers-status-poller',\n id: 'shipping-carriers:status-poller',\n concurrency: 2,\n}\n\nexport default async function handle(job: QueuedJob<PollerJobPayload>, ctx: HandlerContext): Promise<void> {\n const service = ctx.resolve<ShippingCarrierService>('shippingCarrierService')\n for (const shipmentId of job.payload.shipmentIds) {\n await service.refreshTracking({\n providerKey: job.payload.providerKey,\n shipmentId,\n organizationId: job.payload.scope.organizationId,\n tenantId: job.payload.scope.tenantId,\n })\n }\n}\n"],
5
+ "mappings": "AAgBO,MAAM,WAAuB;AAAA,EAClC,OAAO;AAAA,EACP,IAAI;AAAA,EACJ,aAAa;AACf;AAEA,eAAO,OAA8B,KAAkC,KAAoC;AACzG,QAAM,UAAU,IAAI,QAAgC,wBAAwB;AAC5E,aAAW,cAAc,IAAI,QAAQ,aAAa;AAChD,UAAM,QAAQ,gBAAgB;AAAA,MAC5B,aAAa,IAAI,QAAQ;AAAA,MACzB;AAAA,MACA,gBAAgB,IAAI,QAAQ,MAAM;AAAA,MAClC,UAAU,IAAI,QAAQ,MAAM;AAAA,IAC9B,CAAC;AAAA,EACH;AACF;",
6
6
  "names": []
7
7
  }
@@ -9,6 +9,7 @@ import { LoadingMessage, ErrorMessage } from "@open-mercato/ui/backend/detail";
9
9
  import { flash } from "@open-mercato/ui/backend/FlashMessages";
10
10
  import { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from "@open-mercato/ui/backend/utils/apiCall";
11
11
  import { buildOptimisticLockHeader } from "@open-mercato/ui/backend/utils/optimisticLock";
12
+ import { useGuardedMutation } from "@open-mercato/ui/backend/injection/useGuardedMutation";
12
13
  import { useCustomFieldDefs } from "@open-mercato/ui/backend/utils/customFieldDefs";
13
14
  import { Save, Plus, X } from "lucide-react";
14
15
  import { useOrganizationScopeVersion } from "@open-mercato/shared/lib/frontend/useOrganizationScope";
@@ -17,6 +18,8 @@ import { locales as defaultLocales } from "@open-mercato/shared/lib/i18n/config"
17
18
  import { ISO_639_1, isValidIso639, getIso639Label } from "@open-mercato/shared/lib/i18n/iso639";
18
19
  import { formatEntityLabel, buildEntityListUrl, getRecordLabel, resolveBaseValue } from "../lib/helpers.js";
19
20
  import { resolveFieldList } from "../lib/resolve-field-list.js";
21
+ const TRANSLATION_MUTATION_CONTEXT_ID = "translations.entity-translations";
22
+ const SUPPORTED_LOCALES_MUTATION_CONTEXT_ID = "translations.supported-locales";
20
23
  function useTranslationLocales() {
21
24
  return useQuery({
22
25
  queryKey: ["translation-locales"],
@@ -170,6 +173,7 @@ function TranslationManager({
170
173
  setEditedTranslations(parsed);
171
174
  }
172
175
  }, [translationSignature, translationData]);
176
+ const { runMutation, retryLastMutation } = useGuardedMutation({ contextId: TRANSLATION_MUTATION_CONTEXT_ID });
173
177
  const mutation = useMutation({
174
178
  mutationFn: async () => {
175
179
  if (!entityType || !recordId) {
@@ -191,21 +195,35 @@ function TranslationManager({
191
195
  console.warn("[translations] Save skipped: payload is empty \u2014 no locale contains any non-empty field");
192
196
  throw new Error(t("translations.manager.errors.nothingToSave", "Nothing to save \u2014 enter a translation first"));
193
197
  }
194
- const res = await withScopedApiRequestHeaders(
195
- buildOptimisticLockHeader(translationData?.updatedAt),
196
- () => apiCall(
197
- `/api/translations/${encodeURIComponent(entityType)}/${encodeURIComponent(recordId)}`,
198
- {
199
- method: "PUT",
200
- headers: { "content-type": "application/json" },
201
- body: JSON.stringify(body)
198
+ return runMutation({
199
+ operation: async () => {
200
+ const res = await withScopedApiRequestHeaders(
201
+ buildOptimisticLockHeader(translationData?.updatedAt),
202
+ () => apiCall(
203
+ `/api/translations/${encodeURIComponent(entityType)}/${encodeURIComponent(recordId)}`,
204
+ {
205
+ method: "PUT",
206
+ headers: { "content-type": "application/json" },
207
+ body: JSON.stringify(body)
208
+ }
209
+ )
210
+ );
211
+ if (!res.ok) {
212
+ throw new Error(t("translations.manager.errors.save", "Failed to save translations"));
202
213
  }
203
- )
204
- );
205
- if (!res.ok) {
206
- throw new Error(t("translations.manager.errors.save", "Failed to save translations"));
207
- }
208
- return true;
214
+ return true;
215
+ },
216
+ context: {
217
+ formId: TRANSLATION_MUTATION_CONTEXT_ID,
218
+ entityType,
219
+ recordId,
220
+ resourceKind: "translation",
221
+ resourceId: recordId,
222
+ data: translationData ?? null,
223
+ retryLastMutation
224
+ },
225
+ mutationPayload: body
226
+ });
209
227
  },
210
228
  onSuccess: () => {
211
229
  flash(t("translations.manager.flash.saved", "Translations saved"), "success");
@@ -415,15 +433,26 @@ function LocaleManager() {
415
433
  const queryClient = useQueryClient();
416
434
  const { data: locales = [], isLoading } = useTranslationLocales();
417
435
  const [newLocale, setNewLocale] = React.useState("");
436
+ const { runMutation, retryLastMutation } = useGuardedMutation({ contextId: SUPPORTED_LOCALES_MUTATION_CONTEXT_ID });
418
437
  const mutation = useMutation({
419
438
  mutationFn: async (updatedLocales) => {
420
- const res = await apiCall("/api/translations/locales", {
421
- method: "PUT",
422
- headers: { "content-type": "application/json" },
423
- body: JSON.stringify({ locales: updatedLocales })
439
+ return runMutation({
440
+ operation: async () => {
441
+ const res = await apiCall("/api/translations/locales", {
442
+ method: "PUT",
443
+ headers: { "content-type": "application/json" },
444
+ body: JSON.stringify({ locales: updatedLocales })
445
+ });
446
+ if (!res.ok) throw new Error("Failed to save locales");
447
+ return res.result?.locales ?? updatedLocales;
448
+ },
449
+ context: {
450
+ formId: SUPPORTED_LOCALES_MUTATION_CONTEXT_ID,
451
+ resourceKind: "translation-locales",
452
+ retryLastMutation
453
+ },
454
+ mutationPayload: { locales: updatedLocales }
424
455
  });
425
- if (!res.ok) throw new Error("Failed to save locales");
426
- return res.result?.locales ?? updatedLocales;
427
456
  },
428
457
  onSuccess: (result) => {
429
458
  queryClient.setQueryData(["translation-locales"], result);