@open-mercato/core 0.6.6 → 0.6.7-develop.6553.1.2ec7246e04

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 (271) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/gateway_session_initialization/index.js +23 -0
  3. package/dist/generated/entities/gateway_session_initialization/index.js.map +7 -0
  4. package/dist/generated/entities.ids.generated.js +1 -0
  5. package/dist/generated/entities.ids.generated.js.map +2 -2
  6. package/dist/generated/entity-fields-registry.js +12 -0
  7. package/dist/generated/entity-fields-registry.js.map +2 -2
  8. package/dist/modules/api_keys/api/keys/route.js +30 -13
  9. package/dist/modules/api_keys/api/keys/route.js.map +2 -2
  10. package/dist/modules/api_keys/cli.js +2 -1
  11. package/dist/modules/api_keys/cli.js.map +2 -2
  12. package/dist/modules/attachments/api/file/[id]/route.js +9 -7
  13. package/dist/modules/attachments/api/file/[id]/route.js.map +2 -2
  14. package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js +9 -7
  15. package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js.map +2 -2
  16. package/dist/modules/attachments/api/route.js +21 -17
  17. package/dist/modules/attachments/api/route.js.map +2 -2
  18. package/dist/modules/attachments/components/AttachmentContentPreview.js +6 -35
  19. package/dist/modules/attachments/components/AttachmentContentPreview.js.map +2 -2
  20. package/dist/modules/attachments/lib/reconcileOrganization.js +110 -0
  21. package/dist/modules/attachments/lib/reconcileOrganization.js.map +7 -0
  22. package/dist/modules/attachments/lib/requestScope.js +10 -0
  23. package/dist/modules/attachments/lib/requestScope.js.map +7 -0
  24. package/dist/modules/audit_logs/backend/audit-logs/page.js +14 -24
  25. package/dist/modules/audit_logs/backend/audit-logs/page.js.map +2 -2
  26. package/dist/modules/auth/api/autologin.js +85 -0
  27. package/dist/modules/auth/api/autologin.js.map +7 -0
  28. package/dist/modules/auth/api/users/route.js +6 -2
  29. package/dist/modules/auth/api/users/route.js.map +2 -2
  30. package/dist/modules/auth/cli.js +3 -2
  31. package/dist/modules/auth/cli.js.map +2 -2
  32. package/dist/modules/auth/lib/autologin.js +19 -0
  33. package/dist/modules/auth/lib/autologin.js.map +7 -0
  34. package/dist/modules/configs/lib/upgrade-actions.js +22 -0
  35. package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
  36. package/dist/modules/customer_accounts/api/admin/roles/[id]/acl.js +1 -0
  37. package/dist/modules/customer_accounts/api/admin/roles/[id]/acl.js.map +2 -2
  38. package/dist/modules/customer_accounts/api/admin/roles/[id].js +17 -3
  39. package/dist/modules/customer_accounts/api/admin/roles/[id].js.map +2 -2
  40. package/dist/modules/customer_accounts/api/portal/users/[id]/roles.js +6 -1
  41. package/dist/modules/customer_accounts/api/portal/users/[id]/roles.js.map +2 -2
  42. package/dist/modules/customer_accounts/api/portal/users-invite.js +6 -1
  43. package/dist/modules/customer_accounts/api/portal/users-invite.js.map +2 -2
  44. package/dist/modules/customer_accounts/services/customerInvitationService.js +1 -0
  45. package/dist/modules/customer_accounts/services/customerInvitationService.js.map +2 -2
  46. package/dist/modules/customer_accounts/services/customerRbacService.js +6 -1
  47. package/dist/modules/customer_accounts/services/customerRbacService.js.map +2 -2
  48. package/dist/modules/customer_accounts/widgets/injection-table.js +0 -2
  49. package/dist/modules/customer_accounts/widgets/injection-table.js.map +2 -2
  50. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +7 -1
  51. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
  52. package/dist/modules/customers/backend/customers/deals/pipeline/components/ViewTabsRow.js +2 -2
  53. package/dist/modules/customers/backend/customers/deals/pipeline/components/ViewTabsRow.js.map +2 -2
  54. package/dist/modules/customers/components/detail/CompanyDetailTabs.js +37 -43
  55. package/dist/modules/customers/components/detail/CompanyDetailTabs.js.map +2 -2
  56. package/dist/modules/customers/components/detail/DealDetailTabs.js +23 -35
  57. package/dist/modules/customers/components/detail/DealDetailTabs.js.map +2 -2
  58. package/dist/modules/customers/components/detail/DetailTabsLayout.js +6 -21
  59. package/dist/modules/customers/components/detail/DetailTabsLayout.js.map +2 -2
  60. package/dist/modules/customers/components/detail/MobilePersonDetail.js +26 -32
  61. package/dist/modules/customers/components/detail/MobilePersonDetail.js.map +2 -2
  62. package/dist/modules/customers/components/detail/PersonDetailTabs.js +29 -39
  63. package/dist/modules/customers/components/detail/PersonDetailTabs.js.map +2 -2
  64. package/dist/modules/customers/components/detail/useDealsAccess.js +16 -0
  65. package/dist/modules/customers/components/detail/useDealsAccess.js.map +7 -0
  66. package/dist/modules/dashboards/api/roles/widgets/route.js +22 -3
  67. package/dist/modules/dashboards/api/roles/widgets/route.js.map +2 -2
  68. package/dist/modules/dashboards/api/users/widgets/route.js +22 -3
  69. package/dist/modules/dashboards/api/users/widgets/route.js.map +2 -2
  70. package/dist/modules/dashboards/cli.js +4 -3
  71. package/dist/modules/dashboards/cli.js.map +2 -2
  72. package/dist/modules/dashboards/lib/widgetAssignmentScope.js +9 -1
  73. package/dist/modules/dashboards/lib/widgetAssignmentScope.js.map +2 -2
  74. package/dist/modules/dictionaries/commands/entries.js +7 -14
  75. package/dist/modules/dictionaries/commands/entries.js.map +2 -2
  76. package/dist/modules/dictionaries/commands/entry-operations.js +7 -16
  77. package/dist/modules/dictionaries/commands/entry-operations.js.map +2 -2
  78. package/dist/modules/dictionaries/commands/factory.js +9 -3
  79. package/dist/modules/dictionaries/commands/factory.js.map +2 -2
  80. package/dist/modules/dictionaries/fields/dictionary.js +56 -41
  81. package/dist/modules/dictionaries/fields/dictionary.js.map +2 -2
  82. package/dist/modules/directory/utils/organizationScope.js +10 -3
  83. package/dist/modules/directory/utils/organizationScope.js.map +2 -2
  84. package/dist/modules/entities/api/records.js +5 -4
  85. package/dist/modules/entities/api/records.js.map +2 -2
  86. package/dist/modules/entities/backend/entities/system/[entityId]/page.meta.js +1 -0
  87. package/dist/modules/entities/backend/entities/system/[entityId]/page.meta.js.map +2 -2
  88. package/dist/modules/entities/backend/entities/user/[entityId]/page.js +49 -49
  89. package/dist/modules/entities/backend/entities/user/[entityId]/page.js.map +2 -2
  90. package/dist/modules/entities/backend/entities/user/[entityId]/page.meta.js +1 -0
  91. package/dist/modules/entities/backend/entities/user/[entityId]/page.meta.js.map +2 -2
  92. package/dist/modules/entities/cli.js +2 -1
  93. package/dist/modules/entities/cli.js.map +2 -2
  94. package/dist/modules/feature_toggles/api/check/boolean/route.js +0 -14
  95. package/dist/modules/feature_toggles/api/check/boolean/route.js.map +2 -2
  96. package/dist/modules/feature_toggles/defaults.json +8 -0
  97. package/dist/modules/integrations/backend/integrations/[id]/page.js +9 -80
  98. package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
  99. package/dist/modules/payment_gateways/data/entities.js +45 -0
  100. package/dist/modules/payment_gateways/data/entities.js.map +2 -2
  101. package/dist/modules/payment_gateways/lib/gateway-service.js +173 -38
  102. package/dist/modules/payment_gateways/lib/gateway-service.js.map +3 -3
  103. package/dist/modules/payment_gateways/lib/session-idempotency.js +104 -0
  104. package/dist/modules/payment_gateways/lib/session-idempotency.js.map +7 -0
  105. package/dist/modules/payment_gateways/migrations/Migration20260709220938_payment_gateways.js +11 -0
  106. package/dist/modules/payment_gateways/migrations/Migration20260709220938_payment_gateways.js.map +7 -0
  107. package/dist/modules/planner/backend/planner/availability-rulesets/[id]/page.js +16 -14
  108. package/dist/modules/planner/backend/planner/availability-rulesets/[id]/page.js.map +2 -2
  109. package/dist/modules/planner/components/AvailabilityRulesEditor.js +18 -30
  110. package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
  111. package/dist/modules/query_index/api/status.js +2 -8
  112. package/dist/modules/query_index/api/status.js.map +2 -2
  113. package/dist/modules/resources/backend/resources/resources/[id]/page.js +24 -24
  114. package/dist/modules/resources/backend/resources/resources/[id]/page.js.map +2 -2
  115. package/dist/modules/sales/backend/sales/channels/create/page.js +6 -0
  116. package/dist/modules/sales/backend/sales/channels/create/page.js.map +2 -2
  117. package/dist/modules/sales/backend/sales/channels/create/page.meta.js +2 -0
  118. package/dist/modules/sales/backend/sales/channels/create/page.meta.js.map +2 -2
  119. package/dist/modules/sales/backend/sales/channels/offers/page.js +6 -0
  120. package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
  121. package/dist/modules/sales/backend/sales/channels/offers/page.meta.js +2 -0
  122. package/dist/modules/sales/backend/sales/channels/offers/page.meta.js.map +2 -2
  123. package/dist/modules/sales/backend/sales/channels/page.js +6 -0
  124. package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
  125. package/dist/modules/sales/backend/sales/channels/page.meta.js +2 -0
  126. package/dist/modules/sales/backend/sales/channels/page.meta.js.map +2 -2
  127. package/dist/modules/sales/backend/sales/documents/[id]/page.js +6 -4
  128. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  129. package/dist/modules/sales/commands/documents.js +44 -0
  130. package/dist/modules/sales/commands/documents.js.map +2 -2
  131. package/dist/modules/sales/commands/returns.js +2 -27
  132. package/dist/modules/sales/commands/returns.js.map +2 -2
  133. package/dist/modules/sales/components/SalesChannelsDisabledNotice.js +15 -0
  134. package/dist/modules/sales/components/SalesChannelsDisabledNotice.js.map +7 -0
  135. package/dist/modules/sales/components/documents/AddressesSection.js +6 -2
  136. package/dist/modules/sales/components/documents/AddressesSection.js.map +2 -2
  137. package/dist/modules/sales/components/documents/ItemsSection.js +1 -0
  138. package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
  139. package/dist/modules/sales/components/documents/LineItemDialog.js +9 -0
  140. package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
  141. package/dist/modules/sales/components/documents/SalesDocumentForm.js +51 -3
  142. package/dist/modules/sales/components/documents/SalesDocumentForm.js.map +2 -2
  143. package/dist/modules/sales/components/documents/SalesDocumentsTable.js +10 -8
  144. package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
  145. package/dist/modules/sales/components/documents/ShipmentDialog.js +59 -38
  146. package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
  147. package/dist/modules/sales/components/useSalesChannelsEnabled.js +44 -0
  148. package/dist/modules/sales/components/useSalesChannelsEnabled.js.map +7 -0
  149. package/dist/modules/sales/lib/calculations.js +6 -0
  150. package/dist/modules/sales/lib/calculations.js.map +2 -2
  151. package/dist/modules/sales/lib/salesChannelsToggle.js +18 -0
  152. package/dist/modules/sales/lib/salesChannelsToggle.js.map +7 -0
  153. package/dist/modules/sales/lib/salesChannelsToggleId.js +5 -0
  154. package/dist/modules/sales/lib/salesChannelsToggleId.js.map +7 -0
  155. package/dist/modules/sales/lib/shipments/snapshots.js +27 -1
  156. package/dist/modules/sales/lib/shipments/snapshots.js.map +2 -2
  157. package/dist/modules/staff/backend/staff/team-members/[id]/page.js +22 -25
  158. package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
  159. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +12 -14
  160. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  161. package/dist/modules/staff/lib/timesheets-projects-ui/SavedViewTabs.js +10 -29
  162. package/dist/modules/staff/lib/timesheets-projects-ui/SavedViewTabs.js.map +2 -2
  163. package/dist/modules/sync_excel/lib/adapters/customers.js +19 -20
  164. package/dist/modules/sync_excel/lib/adapters/customers.js.map +2 -2
  165. package/dist/modules/sync_excel/lib/parser.js +185 -0
  166. package/dist/modules/sync_excel/lib/parser.js.map +2 -2
  167. package/dist/modules/sync_excel/lib/upload-storage.js +20 -1
  168. package/dist/modules/sync_excel/lib/upload-storage.js.map +2 -2
  169. package/generated/entities/gateway_session_initialization/index.ts +10 -0
  170. package/generated/entities.ids.generated.ts +1 -0
  171. package/generated/entity-fields-registry.ts +12 -0
  172. package/package.json +9 -8
  173. package/src/modules/api_keys/api/keys/route.ts +36 -13
  174. package/src/modules/api_keys/cli.ts +2 -3
  175. package/src/modules/attachments/api/file/[id]/route.ts +11 -7
  176. package/src/modules/attachments/api/image/[id]/[[...slug]]/route.ts +11 -7
  177. package/src/modules/attachments/api/route.ts +26 -21
  178. package/src/modules/attachments/components/AttachmentContentPreview.tsx +9 -44
  179. package/src/modules/attachments/lib/reconcileOrganization.ts +188 -0
  180. package/src/modules/attachments/lib/requestScope.ts +27 -0
  181. package/src/modules/audit_logs/backend/audit-logs/page.tsx +12 -22
  182. package/src/modules/auth/api/autologin.ts +100 -0
  183. package/src/modules/auth/api/users/route.ts +11 -2
  184. package/src/modules/auth/cli.ts +3 -2
  185. package/src/modules/auth/lib/autologin.ts +36 -0
  186. package/src/modules/configs/i18n/de.json +5 -1
  187. package/src/modules/configs/i18n/en.json +5 -1
  188. package/src/modules/configs/i18n/es.json +5 -1
  189. package/src/modules/configs/i18n/pl.json +5 -1
  190. package/src/modules/configs/lib/upgrade-actions.ts +24 -0
  191. package/src/modules/customer_accounts/AGENTS.md +8 -6
  192. package/src/modules/customer_accounts/api/admin/roles/[id]/acl.ts +1 -0
  193. package/src/modules/customer_accounts/api/admin/roles/[id].ts +17 -3
  194. package/src/modules/customer_accounts/api/portal/users/[id]/roles.ts +6 -1
  195. package/src/modules/customer_accounts/api/portal/users-invite.ts +6 -1
  196. package/src/modules/customer_accounts/services/customerInvitationService.ts +1 -0
  197. package/src/modules/customer_accounts/services/customerRbacService.ts +9 -2
  198. package/src/modules/customer_accounts/widgets/injection-table.ts +0 -2
  199. package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +11 -1
  200. package/src/modules/customers/backend/customers/deals/pipeline/components/ViewTabsRow.tsx +6 -2
  201. package/src/modules/customers/components/detail/CompanyDetailTabs.tsx +39 -55
  202. package/src/modules/customers/components/detail/DealDetailTabs.tsx +24 -49
  203. package/src/modules/customers/components/detail/DetailTabsLayout.tsx +13 -24
  204. package/src/modules/customers/components/detail/MobilePersonDetail.tsx +25 -31
  205. package/src/modules/customers/components/detail/PersonDetailTabs.tsx +29 -50
  206. package/src/modules/customers/components/detail/useDealsAccess.ts +19 -0
  207. package/src/modules/dashboards/api/roles/widgets/route.ts +22 -3
  208. package/src/modules/dashboards/api/users/widgets/route.ts +22 -3
  209. package/src/modules/dashboards/cli.ts +4 -9
  210. package/src/modules/dashboards/lib/widgetAssignmentScope.ts +18 -0
  211. package/src/modules/dictionaries/commands/entries.ts +7 -16
  212. package/src/modules/dictionaries/commands/entry-operations.ts +8 -18
  213. package/src/modules/dictionaries/commands/factory.ts +8 -2
  214. package/src/modules/dictionaries/fields/dictionary.tsx +55 -32
  215. package/src/modules/dictionaries/i18n/de.json +2 -0
  216. package/src/modules/dictionaries/i18n/en.json +2 -0
  217. package/src/modules/dictionaries/i18n/es.json +2 -0
  218. package/src/modules/dictionaries/i18n/pl.json +2 -0
  219. package/src/modules/directory/utils/organizationScope.ts +36 -2
  220. package/src/modules/entities/api/records.ts +5 -7
  221. package/src/modules/entities/backend/entities/system/[entityId]/page.meta.ts +1 -0
  222. package/src/modules/entities/backend/entities/user/[entityId]/page.meta.ts +1 -0
  223. package/src/modules/entities/backend/entities/user/[entityId]/page.tsx +51 -49
  224. package/src/modules/entities/cli.ts +2 -1
  225. package/src/modules/entities/i18n/de.json +34 -0
  226. package/src/modules/entities/i18n/en.json +34 -0
  227. package/src/modules/entities/i18n/es.json +34 -0
  228. package/src/modules/entities/i18n/pl.json +34 -0
  229. package/src/modules/feature_toggles/api/check/boolean/route.ts +0 -15
  230. package/src/modules/feature_toggles/defaults.json +8 -0
  231. package/src/modules/integrations/backend/integrations/[id]/page.tsx +16 -59
  232. package/src/modules/payment_gateways/data/entities.ts +39 -0
  233. package/src/modules/payment_gateways/lib/gateway-service.ts +211 -40
  234. package/src/modules/payment_gateways/lib/session-idempotency.ts +142 -0
  235. package/src/modules/payment_gateways/migrations/.snapshot-open-mercato.json +236 -0
  236. package/src/modules/payment_gateways/migrations/Migration20260709220938_payment_gateways.ts +10 -0
  237. package/src/modules/planner/backend/planner/availability-rulesets/[id]/page.tsx +14 -20
  238. package/src/modules/planner/components/AvailabilityRulesEditor.tsx +16 -34
  239. package/src/modules/query_index/api/status.ts +2 -8
  240. package/src/modules/resources/backend/resources/resources/[id]/page.tsx +27 -37
  241. package/src/modules/sales/backend/sales/channels/create/page.meta.ts +3 -0
  242. package/src/modules/sales/backend/sales/channels/create/page.tsx +7 -0
  243. package/src/modules/sales/backend/sales/channels/offers/page.meta.ts +3 -0
  244. package/src/modules/sales/backend/sales/channels/offers/page.tsx +7 -0
  245. package/src/modules/sales/backend/sales/channels/page.meta.ts +4 -0
  246. package/src/modules/sales/backend/sales/channels/page.tsx +7 -0
  247. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +7 -5
  248. package/src/modules/sales/commands/documents.ts +65 -0
  249. package/src/modules/sales/commands/returns.ts +2 -32
  250. package/src/modules/sales/components/SalesChannelsDisabledNotice.tsx +21 -0
  251. package/src/modules/sales/components/documents/AddressesSection.tsx +6 -2
  252. package/src/modules/sales/components/documents/ItemsSection.tsx +5 -0
  253. package/src/modules/sales/components/documents/LineItemDialog.tsx +10 -0
  254. package/src/modules/sales/components/documents/SalesDocumentForm.tsx +53 -3
  255. package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +10 -8
  256. package/src/modules/sales/components/documents/ShipmentDialog.tsx +65 -40
  257. package/src/modules/sales/components/useSalesChannelsEnabled.ts +59 -0
  258. package/src/modules/sales/i18n/de.json +5 -0
  259. package/src/modules/sales/i18n/en.json +5 -0
  260. package/src/modules/sales/i18n/es.json +5 -0
  261. package/src/modules/sales/i18n/pl.json +5 -0
  262. package/src/modules/sales/lib/calculations.ts +15 -0
  263. package/src/modules/sales/lib/salesChannelsToggle.ts +26 -0
  264. package/src/modules/sales/lib/salesChannelsToggleId.ts +1 -0
  265. package/src/modules/sales/lib/shipments/snapshots.ts +30 -0
  266. package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +25 -34
  267. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +11 -17
  268. package/src/modules/staff/lib/timesheets-projects-ui/SavedViewTabs.tsx +11 -28
  269. package/src/modules/sync_excel/lib/adapters/customers.ts +20 -22
  270. package/src/modules/sync_excel/lib/parser.ts +245 -0
  271. package/src/modules/sync_excel/lib/upload-storage.ts +23 -1
@@ -296,6 +296,13 @@ export function ShipmentDialog({
296
296
  const [addressOptions, setAddressOptions] = React.useState<ShipmentAddressOption[]>([])
297
297
  const [addressLoading, setAddressLoading] = React.useState(false)
298
298
  const [addressError, setAddressError] = React.useState<string | null>(null)
299
+ const addressLoadPromiseRef = React.useRef<{
300
+ orderId: string
301
+ promise: Promise<ShipmentAddressOption[]>
302
+ } | null>(null)
303
+ const currentAddressOrderIdRef = React.useRef(orderId)
304
+ currentAddressOrderIdRef.current = orderId
305
+ const initializedDialogKeyRef = React.useRef<string | null>(null)
299
306
  const [documentStatuses, setDocumentStatuses] = React.useState<StatusOption[]>([])
300
307
  const [lineStatuses, setLineStatuses] = React.useState<StatusOption[]>([])
301
308
  const [shipmentStatuses, setShipmentStatuses] = React.useState<StatusOption[]>([])
@@ -479,37 +486,52 @@ export function ShipmentDialog({
479
486
  [],
480
487
  )
481
488
 
482
- const loadAddressOptions = React.useCallback(async () => {
489
+ const loadAddressOptions = React.useCallback((): Promise<ShipmentAddressOption[]> => {
490
+ const activeRequest = addressLoadPromiseRef.current
491
+ if (activeRequest?.orderId === orderId) return activeRequest.promise
492
+
483
493
  setAddressLoading(true)
484
- try {
485
- const params = new URLSearchParams({
486
- page: '1',
487
- pageSize: '100',
488
- documentId: orderId,
489
- documentKind: 'order',
490
- })
491
- const response = await apiCall<{ items?: Array<Record<string, unknown>> }>(
492
- `/api/sales/document-addresses?${params.toString()}`,
493
- undefined,
494
- { fallback: { items: [] } },
495
- )
496
- const items = Array.isArray(response.result?.items) ? response.result.items : []
497
- const mapped = items
498
- .map((item) => mapDocumentAddressOption(item))
499
- .filter((entry): entry is ShipmentAddressOption => Boolean(entry))
500
- mergeAddressOptions(mapped)
501
- setAddressError(null)
502
- return mapped
503
- } catch (err) {
504
- logger.error('sales.shipments.addresses.load', { err })
505
- setAddressError(
506
- t('sales.documents.shipments.addressLoadError', 'Failed to load addresses.'),
507
- )
508
- return []
509
- } finally {
510
- setAddressLoading(false)
511
- }
512
- }, [mapDocumentAddressOption, mergeAddressOptions, orderId, t])
494
+ const request = (async () => {
495
+ try {
496
+ const params = new URLSearchParams({
497
+ page: '1',
498
+ pageSize: '100',
499
+ documentId: orderId,
500
+ documentKind: 'order',
501
+ })
502
+ const response = await apiCall<{ items?: Array<Record<string, unknown>> }>(
503
+ `/api/sales/document-addresses?${params.toString()}`,
504
+ undefined,
505
+ { fallback: { items: [] } },
506
+ )
507
+ const items = Array.isArray(response.result?.items) ? response.result.items : []
508
+ const mapped = items
509
+ .map((item) => mapDocumentAddressOption(item))
510
+ .filter((entry): entry is ShipmentAddressOption => Boolean(entry))
511
+ const loadedOptions = dedupeAddressOptions([...baseAddressOptions, ...mapped])
512
+ if (currentAddressOrderIdRef.current !== orderId) return baseAddressOptions
513
+ mergeAddressOptions(loadedOptions)
514
+ setAddressError(null)
515
+ return loadedOptions
516
+ } catch (err) {
517
+ logger.error('sales.shipments.addresses.load', { err })
518
+ if (currentAddressOrderIdRef.current === orderId) {
519
+ setAddressError(
520
+ t('sales.documents.shipments.addressLoadError', 'Failed to load addresses.'),
521
+ )
522
+ }
523
+ return baseAddressOptions
524
+ }
525
+ })()
526
+ addressLoadPromiseRef.current = { orderId, promise: request }
527
+ void request.then(() => {
528
+ if (addressLoadPromiseRef.current?.promise === request) {
529
+ addressLoadPromiseRef.current = null
530
+ setAddressLoading(false)
531
+ }
532
+ })
533
+ return request
534
+ }, [baseAddressOptions, mapDocumentAddressOption, mergeAddressOptions, orderId, t])
513
535
 
514
536
  const buildPriceSubtitle = React.useCallback(
515
537
  (option: ShippingMethodOption): string | undefined => {
@@ -851,16 +873,20 @@ export function ShipmentDialog({
851
873
  )
852
874
 
853
875
  React.useEffect(() => {
854
- if (!open) return
876
+ if (!open) {
877
+ initializedDialogKeyRef.current = null
878
+ return
879
+ }
880
+ const dialogKey = `${orderId}:${mode}:${shipment?.id ?? 'new'}`
881
+ if (initializedDialogKeyRef.current === dialogKey) return
882
+ initializedDialogKeyRef.current = dialogKey
855
883
  setFormResetKey((prev) => prev + 1)
856
884
  if (!shippingMethods.length) {
857
885
  void loadShippingMethods()
858
886
  }
859
887
  setAddressOptions(baseAddressOptions)
860
888
  setAddressError(null)
861
- if (!addressOptions.length) {
862
- void loadAddressOptions()
863
- }
889
+ void loadAddressOptions()
864
890
  if (!shipmentStatuses.length) {
865
891
  void loadShipmentStatuses()
866
892
  }
@@ -871,7 +897,6 @@ export function ShipmentDialog({
871
897
  void loadLineStatuses()
872
898
  }
873
899
  }, [
874
- addressOptions.length,
875
900
  baseAddressOptions,
876
901
  documentStatuses.length,
877
902
  loadAddressOptions,
@@ -881,6 +906,8 @@ export function ShipmentDialog({
881
906
  loadShippingMethods,
882
907
  mode,
883
908
  open,
909
+ orderId,
910
+ shipment?.id,
884
911
  lineStatuses.length,
885
912
  shipmentStatuses.length,
886
913
  shippingMethods.length,
@@ -939,11 +966,9 @@ export function ShipmentDialog({
939
966
 
940
967
  const fetchAddressItems = React.useCallback(
941
968
  async (query?: string): Promise<LookupSelectItem[]> => {
942
- if (!addressOptions.length && !addressLoading) {
943
- await loadAddressOptions()
944
- }
969
+ const options = addressOptions.length ? addressOptions : await loadAddressOptions()
945
970
  const needle = query?.trim().toLowerCase() ?? ''
946
- return addressOptions
971
+ return options
947
972
  .filter((option) => {
948
973
  if (!needle) return true
949
974
  return (
@@ -962,7 +987,7 @@ export function ShipmentDialog({
962
987
  ),
963
988
  }))
964
989
  },
965
- [addressLoading, addressOptions, loadAddressOptions],
990
+ [addressOptions, loadAddressOptions],
966
991
  )
967
992
 
968
993
  const validateItems = React.useCallback(
@@ -0,0 +1,59 @@
1
+ "use client"
2
+
3
+ import * as React from 'react'
4
+ import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
5
+ import { SALES_CHANNELS_TOGGLE_ID } from '../lib/salesChannelsToggleId'
6
+
7
+ export { SALES_CHANNELS_TOGGLE_ID }
8
+
9
+ const CACHE_TTL_MS = 60_000
10
+
11
+ type FeatureToggleCheckResponse = {
12
+ ok?: boolean
13
+ value?: unknown
14
+ }
15
+
16
+ // Fails open: channels stay visible unless the toggle explicitly resolves to
17
+ // false. A missing toggle definition (404) or an absent feature_toggles module
18
+ // must not hide sales channel UI.
19
+ async function fetchSalesChannelsEnabled(): Promise<boolean> {
20
+ try {
21
+ const call = await apiCall<FeatureToggleCheckResponse>(
22
+ `/api/feature_toggles/check/boolean?identifier=${SALES_CHANNELS_TOGGLE_ID}`,
23
+ )
24
+ if (!call.ok) return true
25
+ return !(call.result?.ok === true && call.result.value === false)
26
+ } catch {
27
+ return true
28
+ }
29
+ }
30
+
31
+ let cache: { value: Promise<boolean>; at: number } | null = null
32
+
33
+ function getSalesChannelsEnabled(): Promise<boolean> {
34
+ const now = Date.now()
35
+ if (!cache || now - cache.at > CACHE_TTL_MS) {
36
+ cache = { value: fetchSalesChannelsEnabled(), at: now }
37
+ }
38
+ return cache.value
39
+ }
40
+
41
+ export function useSalesChannelsEnabled(): { enabled: boolean; isLoading: boolean } {
42
+ const [resolved, setResolved] = React.useState<boolean | null>(null)
43
+
44
+ React.useEffect(() => {
45
+ let active = true
46
+ getSalesChannelsEnabled()
47
+ .then((value) => {
48
+ if (active) setResolved(value)
49
+ })
50
+ .catch(() => {
51
+ if (active) setResolved(true)
52
+ })
53
+ return () => {
54
+ active = false
55
+ }
56
+ }, [])
57
+
58
+ return { enabled: resolved !== false, isLoading: resolved === null }
59
+ }
@@ -60,6 +60,7 @@
60
60
  "sales.channels.actions.backToList": "Zurück zu Kanälen",
61
61
  "sales.channels.actions.create": "Kanal hinzufügen",
62
62
  "sales.channels.create": "Vertriebskanal erstellen",
63
+ "sales.channels.disabledNotice": "Vertriebskanäle sind für diesen Mandanten deaktiviert. Aktivieren Sie das Feature-Toggle \"Sales Channels\", um sie zu verwalten.",
63
64
  "sales.channels.form.address1": "Adresszeile 1",
64
65
  "sales.channels.form.address2": "Adresszeile 2",
65
66
  "sales.channels.form.city": "Stadt",
@@ -638,8 +639,10 @@
638
639
  "sales.documents.detail.items.errorDelete": "Position konnte nicht gelöscht werden.",
639
640
  "sales.documents.detail.items.errorDeleteShipped": "Positionen mit versandten Artikeln können nicht gelöscht werden.",
640
641
  "sales.documents.detail.items.errorLoad": "Positionen konnten nicht geladen werden.",
642
+ "sales.documents.detail.items.errorPriceShipped": "Preis oder Einheit einer Position mit versandten Artikeln können nicht geändert werden.",
641
643
  "sales.documents.detail.items.errorProductRequired": "Wähle ein Produkt aus, um fortzufahren.",
642
644
  "sales.documents.detail.items.errorQuantity": "Die Menge muss größer als 0 sein.",
645
+ "sales.documents.detail.items.errorQuantityBelowShipped": "Die Menge kann nicht unter die bereits versandten {{shipped}} gesenkt werden.",
643
646
  "sales.documents.detail.items.errorSave": "Position konnte nicht gespeichert werden.",
644
647
  "sales.documents.detail.items.errorUnitPrice": "Der Stückpreis muss größer als 0 sein.",
645
648
  "sales.documents.detail.items.errorVariantRequired": "Wähle eine Variante aus, um fortzufahren.",
@@ -950,8 +953,10 @@
950
953
  "sales.documents.items.errorDeleteShipped": "Positionen mit versandten Artikeln können nicht gelöscht werden.",
951
954
  "sales.documents.items.errorLoad": "Positionen konnten nicht geladen werden.",
952
955
  "sales.documents.items.errorNameRequired": "Name ist für benutzerdefinierte Positionen erforderlich.",
956
+ "sales.documents.items.errorPriceShipped": "Preis oder Einheit einer Position mit versandten Artikeln können nicht geändert werden.",
953
957
  "sales.documents.items.errorProductRequired": "Wähle ein Produkt aus, um fortzufahren.",
954
958
  "sales.documents.items.errorQuantity": "Die Menge muss größer als 0 sein.",
959
+ "sales.documents.items.errorQuantityBelowShipped": "Die Menge kann nicht unter die bereits versandten {{shipped}} gesenkt werden.",
955
960
  "sales.documents.items.errorSave": "Position konnte nicht gespeichert werden.",
956
961
  "sales.documents.items.errorScope": "Organisation und Mandant sind erforderlich.",
957
962
  "sales.documents.items.errorUnitPrice": "Der Stückpreis muss größer als 0 sein.",
@@ -60,6 +60,7 @@
60
60
  "sales.channels.actions.backToList": "Back to channels",
61
61
  "sales.channels.actions.create": "Add channel",
62
62
  "sales.channels.create": "Create sales channel",
63
+ "sales.channels.disabledNotice": "Sales channels are disabled for this tenant. Enable the \"Sales Channels\" feature toggle to manage them.",
63
64
  "sales.channels.form.address1": "Address line 1",
64
65
  "sales.channels.form.address2": "Address line 2",
65
66
  "sales.channels.form.city": "City",
@@ -638,8 +639,10 @@
638
639
  "sales.documents.detail.items.errorDelete": "Failed to delete line.",
639
640
  "sales.documents.detail.items.errorDeleteShipped": "You cannot delete a line that has shipped items.",
640
641
  "sales.documents.detail.items.errorLoad": "Failed to load items.",
642
+ "sales.documents.detail.items.errorPriceShipped": "You cannot change the price or unit of a line that has shipped items.",
641
643
  "sales.documents.detail.items.errorProductRequired": "Select a product to continue.",
642
644
  "sales.documents.detail.items.errorQuantity": "Quantity must be greater than 0.",
645
+ "sales.documents.detail.items.errorQuantityBelowShipped": "You cannot lower the quantity below the {{shipped}} already shipped.",
643
646
  "sales.documents.detail.items.errorSave": "Failed to save line.",
644
647
  "sales.documents.detail.items.errorUnitPrice": "Unit price must be greater than 0.",
645
648
  "sales.documents.detail.items.errorVariantRequired": "Select a variant to continue.",
@@ -950,8 +953,10 @@
950
953
  "sales.documents.items.errorDeleteShipped": "You cannot delete a line that has shipped items.",
951
954
  "sales.documents.items.errorLoad": "Failed to load items.",
952
955
  "sales.documents.items.errorNameRequired": "Name is required for custom lines.",
956
+ "sales.documents.items.errorPriceShipped": "You cannot change the price or unit of a line that has shipped items.",
953
957
  "sales.documents.items.errorProductRequired": "Select a product to continue.",
954
958
  "sales.documents.items.errorQuantity": "Quantity must be greater than 0.",
959
+ "sales.documents.items.errorQuantityBelowShipped": "You cannot lower the quantity below the {{shipped}} already shipped.",
955
960
  "sales.documents.items.errorSave": "Failed to save line.",
956
961
  "sales.documents.items.errorScope": "Organization and tenant are required.",
957
962
  "sales.documents.items.errorUnitPrice": "Unit price must be greater than 0.",
@@ -60,6 +60,7 @@
60
60
  "sales.channels.actions.backToList": "Volver a canales",
61
61
  "sales.channels.actions.create": "Añadir canal",
62
62
  "sales.channels.create": "Crear canal de ventas",
63
+ "sales.channels.disabledNotice": "Los canales de venta están desactivados para este inquilino. Active el conmutador de función \"Sales Channels\" para gestionarlos.",
63
64
  "sales.channels.form.address1": "Línea de dirección 1",
64
65
  "sales.channels.form.address2": "Línea de dirección 2",
65
66
  "sales.channels.form.city": "Ciudad",
@@ -638,8 +639,10 @@
638
639
  "sales.documents.detail.items.errorDelete": "No se pudo eliminar la línea.",
639
640
  "sales.documents.detail.items.errorDeleteShipped": "No puedes eliminar una línea que ya tiene envíos.",
640
641
  "sales.documents.detail.items.errorLoad": "No se pudieron cargar los artículos.",
642
+ "sales.documents.detail.items.errorPriceShipped": "No puedes cambiar el precio ni la unidad de una línea que ya tiene envíos.",
641
643
  "sales.documents.detail.items.errorProductRequired": "Selecciona un producto para continuar.",
642
644
  "sales.documents.detail.items.errorQuantity": "La cantidad debe ser mayor que 0.",
645
+ "sales.documents.detail.items.errorQuantityBelowShipped": "No puedes reducir la cantidad por debajo de las {{shipped}} ya enviadas.",
643
646
  "sales.documents.detail.items.errorSave": "No se pudo guardar la línea.",
644
647
  "sales.documents.detail.items.errorUnitPrice": "El precio unitario debe ser mayor que 0.",
645
648
  "sales.documents.detail.items.errorVariantRequired": "Selecciona una variante para continuar.",
@@ -950,8 +953,10 @@
950
953
  "sales.documents.items.errorDeleteShipped": "No puedes eliminar una línea que ya tiene envíos.",
951
954
  "sales.documents.items.errorLoad": "No se pudieron cargar los artículos.",
952
955
  "sales.documents.items.errorNameRequired": "El nombre es obligatorio para líneas personalizadas.",
956
+ "sales.documents.items.errorPriceShipped": "No puedes cambiar el precio ni la unidad de una línea que ya tiene envíos.",
953
957
  "sales.documents.items.errorProductRequired": "Selecciona un producto para continuar.",
954
958
  "sales.documents.items.errorQuantity": "La cantidad debe ser mayor que 0.",
959
+ "sales.documents.items.errorQuantityBelowShipped": "No puedes reducir la cantidad por debajo de las {{shipped}} ya enviadas.",
955
960
  "sales.documents.items.errorSave": "No se pudo guardar la línea.",
956
961
  "sales.documents.items.errorScope": "Se requieren organización e inquilino.",
957
962
  "sales.documents.items.errorUnitPrice": "El precio unitario debe ser mayor que 0.",
@@ -60,6 +60,7 @@
60
60
  "sales.channels.actions.backToList": "Powrót do kanałów",
61
61
  "sales.channels.actions.create": "Dodaj kanał",
62
62
  "sales.channels.create": "Utwórz kanał sprzedaży",
63
+ "sales.channels.disabledNotice": "Kanały sprzedaży są wyłączone dla tego najemcy. Włącz przełącznik funkcji \"Sales Channels\", aby nimi zarządzać.",
63
64
  "sales.channels.form.address1": "Adres linia 1",
64
65
  "sales.channels.form.address2": "Adres linia 2",
65
66
  "sales.channels.form.city": "Miasto",
@@ -638,8 +639,10 @@
638
639
  "sales.documents.detail.items.errorDelete": "Nie udało się usunąć pozycji.",
639
640
  "sales.documents.detail.items.errorDeleteShipped": "Nie można usunąć pozycji, która ma wysłane sztuki.",
640
641
  "sales.documents.detail.items.errorLoad": "Nie udało się wczytać pozycji.",
642
+ "sales.documents.detail.items.errorPriceShipped": "Nie można zmienić ceny ani jednostki pozycji, która ma wysłane sztuki.",
641
643
  "sales.documents.detail.items.errorProductRequired": "Wybierz produkt, aby kontynuować.",
642
644
  "sales.documents.detail.items.errorQuantity": "Ilość musi być większa niż 0.",
645
+ "sales.documents.detail.items.errorQuantityBelowShipped": "Nie można obniżyć ilości poniżej {{shipped}} już wysłanych.",
643
646
  "sales.documents.detail.items.errorSave": "Nie udało się zapisać pozycji.",
644
647
  "sales.documents.detail.items.errorUnitPrice": "Cena jednostkowa musi być większa niż 0.",
645
648
  "sales.documents.detail.items.errorVariantRequired": "Wybierz wariant, aby kontynuować.",
@@ -950,8 +953,10 @@
950
953
  "sales.documents.items.errorDeleteShipped": "Nie można usunąć pozycji, która ma wysłane sztuki.",
951
954
  "sales.documents.items.errorLoad": "Nie udało się załadować pozycji.",
952
955
  "sales.documents.items.errorNameRequired": "Nazwa jest wymagana dla niestandardowych pozycji.",
956
+ "sales.documents.items.errorPriceShipped": "Nie można zmienić ceny ani jednostki pozycji, która ma wysłane sztuki.",
953
957
  "sales.documents.items.errorProductRequired": "Wybierz produkt, aby kontynuować.",
954
958
  "sales.documents.items.errorQuantity": "Ilość musi być większa niż 0.",
959
+ "sales.documents.items.errorQuantityBelowShipped": "Nie można obniżyć ilości poniżej {{shipped}} już wysłanych.",
955
960
  "sales.documents.items.errorSave": "Nie udało się zapisać pozycji.",
956
961
  "sales.documents.items.errorScope": "Wymagane są organizacja i najemca.",
957
962
  "sales.documents.items.errorUnitPrice": "Cena jednostkowa musi być większa niż 0.",
@@ -208,6 +208,21 @@ function buildBaseDocumentResult(params: {
208
208
  }
209
209
  break
210
210
  default:
211
+ // `return` (credit) adjustments are handled by the dedicated loop below;
212
+ // skip them here so an order-scoped return is not counted twice.
213
+ if (adj.kind === 'return') break
214
+ // Custom / operator-defined kinds carry an operator-controlled sign
215
+ // (positive adds, negative credits). Fold the raw signed amount into the
216
+ // grand total so a persisted adjustment can never be silently dropped
217
+ // from the headline total while still appearing in the itemized
218
+ // breakdown (#4052). No abs()/clamp here: unlike the sign-constrained
219
+ // kinds above, custom kinds are intentionally unconstrained
220
+ // (see enforceAdjustmentSign).
221
+ subtotalNet += net
222
+ subtotalGross += gross
223
+ if (taxPortion) {
224
+ taxTotal += taxPortion
225
+ }
211
226
  break
212
227
  }
213
228
  }
@@ -0,0 +1,26 @@
1
+ import { SALES_CHANNELS_TOGGLE_ID } from './salesChannelsToggleId'
2
+
3
+ export { SALES_CHANNELS_TOGGLE_ID }
4
+
5
+ type BoolConfigResult = { ok: boolean; value?: unknown }
6
+
7
+ type FeatureTogglesServiceLike = {
8
+ getBoolConfig: (identifier: string, tenantId: string) => Promise<BoolConfigResult>
9
+ }
10
+
11
+ // Server-side counterpart of useSalesChannelsEnabled with the same fail-open
12
+ // contract: channels stay enabled unless the toggle explicitly resolves to
13
+ // false. The DI container and feature_toggles service are optional peers, so
14
+ // they are loaded lazily and any failure keeps channels visible.
15
+ export async function isSalesChannelsEnabledForTenant(tenantId: string | null | undefined): Promise<boolean> {
16
+ if (!tenantId) return true
17
+ try {
18
+ const { createRequestContainer } = await import('@open-mercato/shared/lib/di/container')
19
+ const container = await createRequestContainer()
20
+ const service = container.resolve('featureTogglesService') as FeatureTogglesServiceLike
21
+ const result = await service.getBoolConfig(SALES_CHANNELS_TOGGLE_ID, tenantId)
22
+ return !(result.ok === true && result.value === false)
23
+ } catch {
24
+ return true
25
+ }
26
+ }
@@ -0,0 +1 @@
1
+ export const SALES_CHANNELS_TOGGLE_ID = 'sales_channels_enabled'
@@ -136,4 +136,34 @@ export const refreshShipmentItemsSnapshot = async (
136
136
  return snapshot
137
137
  }
138
138
 
139
+ export const loadShippedQuantityByLine = async (
140
+ em: EntityManager,
141
+ orderId: string,
142
+ scope: { tenantId: string; organizationId: string },
143
+ ): Promise<Map<string, number>> => {
144
+ const shipments = await findWithDecryption(
145
+ em,
146
+ SalesShipment,
147
+ { order: orderId, deletedAt: null },
148
+ {},
149
+ scope,
150
+ )
151
+ const shippedByLine = new Map<string, number>()
152
+ if (!shipments.length) return shippedByLine
153
+ const items = await findWithDecryption(
154
+ em,
155
+ SalesShipmentItem,
156
+ { shipment: { $in: shipments.map((shipment) => shipment.id) } },
157
+ {},
158
+ scope,
159
+ )
160
+ items.forEach((item) => {
161
+ const orderLineId = typeof item.orderLine === 'string' ? item.orderLine : (item.orderLine?.id ?? null)
162
+ if (!orderLineId) return
163
+ const next = (shippedByLine.get(orderLineId) ?? 0) + coerceShipmentQuantity(item.quantity)
164
+ shippedByLine.set(orderLineId, next)
165
+ })
166
+ return shippedByLine
167
+ }
168
+
139
169
  export type { ShipmentItemSnapshot } from './types'
@@ -5,6 +5,7 @@ import Link from 'next/link'
5
5
  import { usePathname, useRouter, useSearchParams } from 'next/navigation'
6
6
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
7
7
  import { Button } from '@open-mercato/ui/primitives/button'
8
+ import { Tabs, TabsList, TabsTrigger } from '@open-mercato/ui/primitives/tabs'
8
9
  import { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
9
10
  import { extractCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields-client'
10
11
  import { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'
@@ -432,29 +433,22 @@ export default function StaffTeamMemberDetailPage({ params }: { params?: { id?:
432
433
  </div>
433
434
  </div>
434
435
 
435
- <div className="border-b">
436
- <nav
437
- className="flex flex-wrap items-center gap-5 text-sm"
436
+ <Tabs
437
+ value={activePanel}
438
+ onValueChange={(value) => setActivePanel(value as 'details' | 'availability' | 'jobHistory')}
439
+ variant="underline"
440
+ >
441
+ <TabsList
442
+ className="w-full flex-wrap"
438
443
  aria-label={t('staff.teamMembers.detail.tabs.label', 'Team member sections')}
439
444
  >
440
445
  {panelTabs.map((tab) => (
441
- <button
442
- key={tab.id}
443
- type="button"
444
- role="tab"
445
- aria-selected={activePanel === tab.id}
446
- onClick={() => setActivePanel(tab.id)}
447
- className={`relative -mb-px border-b-2 px-0 py-2 text-sm font-medium transition-colors ${
448
- activePanel === tab.id
449
- ? 'border-accent-indigo text-foreground'
450
- : 'border-transparent text-muted-foreground hover:text-foreground'
451
- }`}
452
- >
446
+ <TabsTrigger key={tab.id} value={tab.id}>
453
447
  {tab.label}
454
- </button>
448
+ </TabsTrigger>
455
449
  ))}
456
- </nav>
457
- </div>
450
+ </TabsList>
451
+ </Tabs>
458
452
 
459
453
  {activePanel === 'details' ? (
460
454
  <>
@@ -500,22 +494,19 @@ export default function StaffTeamMemberDetailPage({ params }: { params?: { id?:
500
494
 
501
495
  <div className="rounded-lg border bg-card p-4">
502
496
  <div className="flex flex-wrap items-center justify-between gap-3">
503
- <div className="flex gap-2">
504
- {tabs.map((tab) => (
505
- <button
506
- key={tab.id}
507
- type="button"
508
- onClick={() => setActiveTab(tab.id)}
509
- className={`relative -mb-px border-b-2 px-0 py-1 text-sm font-medium transition-colors ${
510
- activeTab === tab.id
511
- ? 'border-accent-indigo text-foreground'
512
- : 'border-transparent text-muted-foreground hover:text-foreground'
513
- }`}
514
- >
515
- {tab.label}
516
- </button>
517
- ))}
518
- </div>
497
+ <Tabs
498
+ value={activeTab}
499
+ onValueChange={(value) => setActiveTab(value as 'notes' | 'activities' | 'addresses')}
500
+ variant="underline"
501
+ >
502
+ <TabsList className="h-auto flex-wrap border-b-0">
503
+ {tabs.map((tab) => (
504
+ <TabsTrigger key={tab.id} value={tab.id}>
505
+ {tab.label}
506
+ </TabsTrigger>
507
+ ))}
508
+ </TabsList>
509
+ </Tabs>
519
510
  {sectionAction ? (
520
511
  <Button
521
512
  type="button"
@@ -12,6 +12,7 @@ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimi
12
12
  import { DataTable, withDataTableNamespaces } from '@open-mercato/ui/backend/DataTable'
13
13
  import { RowActions } from '@open-mercato/ui/backend/RowActions'
14
14
  import { Button } from '@open-mercato/ui/primitives/button'
15
+ import { Tabs, TabsList, TabsTrigger } from '@open-mercato/ui/primitives/tabs'
15
16
  import { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'
16
17
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
17
18
  import { useT } from '@open-mercato/shared/lib/i18n/context'
@@ -362,29 +363,22 @@ export default function StaffTeamEditPage({ params }: { params?: { id?: string }
362
363
  <Page>
363
364
  <PageBody>
364
365
  <div className="space-y-6">
365
- <div className="border-b">
366
- <nav className="flex flex-wrap items-center gap-5 text-sm" aria-label={memberLabels.tabs.label}>
366
+ <Tabs
367
+ value={activeTab}
368
+ onValueChange={(value) => setActiveTab(value as 'details' | 'members')}
369
+ variant="underline"
370
+ >
371
+ <TabsList className="w-full flex-wrap" aria-label={memberLabels.tabs.label}>
367
372
  {[
368
373
  { id: 'details', label: memberLabels.tabs.details },
369
374
  { id: 'members', label: memberLabels.tabs.members },
370
375
  ].map((tab) => (
371
- <button
372
- key={tab.id}
373
- type="button"
374
- role="tab"
375
- aria-selected={activeTab === tab.id}
376
- onClick={() => setActiveTab(tab.id as 'details' | 'members')}
377
- className={`relative -mb-px border-b-2 px-0 py-2 text-sm font-medium transition-colors ${
378
- activeTab === tab.id
379
- ? 'border-accent-indigo text-foreground'
380
- : 'border-transparent text-muted-foreground hover:text-foreground'
381
- }`}
382
- >
376
+ <TabsTrigger key={tab.id} value={tab.id}>
383
377
  {tab.label}
384
- </button>
378
+ </TabsTrigger>
385
379
  ))}
386
- </nav>
387
- </div>
380
+ </TabsList>
381
+ </Tabs>
388
382
 
389
383
  {activeTab === 'details' ? (
390
384
  <TeamForm
@@ -1,7 +1,7 @@
1
1
  "use client"
2
2
 
3
3
  import * as React from 'react'
4
- import { Button } from '@open-mercato/ui/primitives/button'
4
+ import { Tabs, TabsList, TabsTrigger } from '@open-mercato/ui/primitives/tabs'
5
5
 
6
6
  export type SavedViewTab = {
7
7
  id: string
@@ -19,35 +19,18 @@ export type SavedViewTabsProps = {
19
19
 
20
20
  export function SavedViewTabs({ tabs, activeId, onSelect, className, ariaLabel }: SavedViewTabsProps) {
21
21
  return (
22
- <div
23
- role="tablist"
24
- aria-label={ariaLabel ?? 'Saved views'}
25
- className={`flex items-center gap-1 border-b border-border ${className ?? ''}`}
26
- >
27
- {tabs.map((tab) => {
28
- const isActive = tab.id === activeId
29
- return (
30
- <Button
22
+ <Tabs value={activeId} onValueChange={onSelect} variant="underline" className={className}>
23
+ <TabsList className="w-full flex-wrap" aria-label={ariaLabel ?? 'Saved views'}>
24
+ {tabs.map((tab) => (
25
+ <TabsTrigger
31
26
  key={tab.id}
32
- type="button"
33
- variant="ghost"
34
- size="sm"
35
- role="tab"
36
- aria-selected={isActive}
37
- className={`h-auto rounded-none px-3 py-2 text-sm hover:bg-transparent ${
38
- isActive
39
- ? 'border-b-2 border-foreground font-medium text-foreground'
40
- : 'border-b-2 border-transparent text-muted-foreground hover:text-foreground'
41
- }`}
42
- onClick={() => onSelect(tab.id)}
27
+ value={tab.id}
28
+ count={typeof tab.count === 'number' ? tab.count : undefined}
43
29
  >
44
30
  {tab.label}
45
- {typeof tab.count === 'number' ? (
46
- <span className="ml-1.5 text-xs text-muted-foreground/70 tabular-nums">{tab.count}</span>
47
- ) : null}
48
- </Button>
49
- )
50
- })}
51
- </div>
31
+ </TabsTrigger>
32
+ ))}
33
+ </TabsList>
34
+ </Tabs>
52
35
  )
53
36
  }