@open-mercato/core 0.6.7-develop.6758.1.697eade236 → 0.6.7-develop.6766.1.40c9011995

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 (263) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/api_keys/backend/api-keys/page.js +2 -1
  3. package/dist/modules/api_keys/backend/api-keys/page.js.map +2 -2
  4. package/dist/modules/api_keys/extension-points.js +16 -0
  5. package/dist/modules/api_keys/extension-points.js.map +7 -0
  6. package/dist/modules/audit_logs/components/AccessLogsTable.js +2 -1
  7. package/dist/modules/audit_logs/components/AccessLogsTable.js.map +2 -2
  8. package/dist/modules/audit_logs/components/AuditLogsActions.js +2 -1
  9. package/dist/modules/audit_logs/components/AuditLogsActions.js.map +2 -2
  10. package/dist/modules/audit_logs/extension-points.js +14 -0
  11. package/dist/modules/audit_logs/extension-points.js.map +7 -0
  12. package/dist/modules/auth/backend/roles/page.js +2 -1
  13. package/dist/modules/auth/backend/roles/page.js.map +2 -2
  14. package/dist/modules/auth/backend/users/page.js +2 -1
  15. package/dist/modules/auth/backend/users/page.js.map +2 -2
  16. package/dist/modules/auth/extension-points.js +21 -0
  17. package/dist/modules/auth/extension-points.js.map +7 -0
  18. package/dist/modules/auth/frontend/login.js +2 -1
  19. package/dist/modules/auth/frontend/login.js.map +2 -2
  20. package/dist/modules/business_rules/backend/rules/page.js +2 -1
  21. package/dist/modules/business_rules/backend/rules/page.js.map +2 -2
  22. package/dist/modules/business_rules/events.js +13 -0
  23. package/dist/modules/business_rules/events.js.map +7 -0
  24. package/dist/modules/business_rules/extension-points.js +13 -0
  25. package/dist/modules/business_rules/extension-points.js.map +7 -0
  26. package/dist/modules/catalog/backend/catalog/products/[id]/page.js +2 -1
  27. package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
  28. package/dist/modules/catalog/backend/catalog/products/create/page.js +2 -1
  29. package/dist/modules/catalog/backend/catalog/products/create/page.js.map +2 -2
  30. package/dist/modules/catalog/components/categories/CategoriesDataTable.js +2 -1
  31. package/dist/modules/catalog/components/categories/CategoriesDataTable.js.map +2 -2
  32. package/dist/modules/catalog/components/products/ProductsDataTable.js +3 -2
  33. package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
  34. package/dist/modules/catalog/events.js +1 -0
  35. package/dist/modules/catalog/events.js.map +2 -2
  36. package/dist/modules/catalog/extension-points.js +27 -0
  37. package/dist/modules/catalog/extension-points.js.map +7 -0
  38. package/dist/modules/communication_channels/backend/communication_channels/channels/page.js +2 -1
  39. package/dist/modules/communication_channels/backend/communication_channels/channels/page.js.map +2 -2
  40. package/dist/modules/communication_channels/backend/profile/communication-channels/page.js +3 -2
  41. package/dist/modules/communication_channels/backend/profile/communication-channels/page.js.map +2 -2
  42. package/dist/modules/communication_channels/extension-points.js +20 -0
  43. package/dist/modules/communication_channels/extension-points.js.map +7 -0
  44. package/dist/modules/configs/backend/config/system-status/page.js +2 -1
  45. package/dist/modules/configs/backend/config/system-status/page.js.map +2 -2
  46. package/dist/modules/configs/extension-points.js +18 -0
  47. package/dist/modules/configs/extension-points.js.map +7 -0
  48. package/dist/modules/currencies/backend/currencies/page.js +2 -1
  49. package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
  50. package/dist/modules/currencies/backend/exchange-rates/page.js +2 -1
  51. package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
  52. package/dist/modules/currencies/extension-points.js +14 -0
  53. package/dist/modules/currencies/extension-points.js.map +7 -0
  54. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js +2 -1
  55. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js.map +2 -2
  56. package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js +2 -1
  57. package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js.map +2 -2
  58. package/dist/modules/customer_accounts/extension-points.js +14 -0
  59. package/dist/modules/customer_accounts/extension-points.js.map +7 -0
  60. package/dist/modules/customers/backend/customers/companies/[id]/page.js +2 -1
  61. package/dist/modules/customers/backend/customers/companies/[id]/page.js.map +2 -2
  62. package/dist/modules/customers/backend/customers/companies/page.js +2 -1
  63. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  64. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +5 -4
  65. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
  66. package/dist/modules/customers/backend/customers/deals/[id]/page.js +5 -4
  67. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  68. package/dist/modules/customers/backend/customers/deals/page.js +2 -1
  69. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  70. package/dist/modules/customers/backend/customers/people/[id]/page.js +2 -1
  71. package/dist/modules/customers/backend/customers/people/[id]/page.js.map +2 -2
  72. package/dist/modules/customers/backend/customers/people/page.js +2 -1
  73. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  74. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +5 -4
  75. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
  76. package/dist/modules/customers/components/CustomerTodosTable.js +2 -1
  77. package/dist/modules/customers/components/CustomerTodosTable.js.map +2 -2
  78. package/dist/modules/customers/extension-points.js +42 -0
  79. package/dist/modules/customers/extension-points.js.map +7 -0
  80. package/dist/modules/customers/lib/calendar/format.js +8 -4
  81. package/dist/modules/customers/lib/calendar/format.js.map +2 -2
  82. package/dist/modules/data_sync/backend/data-sync/page.js +2 -1
  83. package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
  84. package/dist/modules/data_sync/extension-points.js +13 -0
  85. package/dist/modules/data_sync/extension-points.js.map +7 -0
  86. package/dist/modules/directory/backend/directory/organizations/page.js +2 -1
  87. package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
  88. package/dist/modules/directory/backend/directory/tenants/page.js +2 -1
  89. package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
  90. package/dist/modules/directory/extension-points.js +14 -0
  91. package/dist/modules/directory/extension-points.js.map +7 -0
  92. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +5 -1
  93. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
  94. package/dist/modules/entities/components/SystemEntitiesTable.js +2 -1
  95. package/dist/modules/entities/components/SystemEntitiesTable.js.map +2 -2
  96. package/dist/modules/entities/components/UserEntitiesTable.js +2 -1
  97. package/dist/modules/entities/components/UserEntitiesTable.js.map +2 -2
  98. package/dist/modules/entities/extension-points.js +21 -0
  99. package/dist/modules/entities/extension-points.js.map +7 -0
  100. package/dist/modules/integrations/backend/integrations/[id]/page.js +2 -4
  101. package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
  102. package/dist/modules/integrations/extension-points.js +30 -0
  103. package/dist/modules/integrations/extension-points.js.map +7 -0
  104. package/dist/modules/messages/components/ComposeMessagePageClient.js +2 -1
  105. package/dist/modules/messages/components/ComposeMessagePageClient.js.map +2 -2
  106. package/dist/modules/messages/components/MessageDetailPageClient.js +3 -2
  107. package/dist/modules/messages/components/MessageDetailPageClient.js.map +2 -2
  108. package/dist/modules/messages/components/MessagesInboxPageClient.js +2 -1
  109. package/dist/modules/messages/components/MessagesInboxPageClient.js.map +2 -2
  110. package/dist/modules/messages/extension-points.js +16 -0
  111. package/dist/modules/messages/extension-points.js.map +7 -0
  112. package/dist/modules/payment_gateways/backend/payment-gateways/page.js +3 -2
  113. package/dist/modules/payment_gateways/backend/payment-gateways/page.js.map +2 -2
  114. package/dist/modules/payment_gateways/extension-points.js +19 -0
  115. package/dist/modules/payment_gateways/extension-points.js.map +7 -0
  116. package/dist/modules/portal/extension-points.js +32 -0
  117. package/dist/modules/portal/extension-points.js.map +7 -0
  118. package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js +3 -3
  119. package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js.map +2 -2
  120. package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js +3 -3
  121. package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js.map +2 -2
  122. package/dist/modules/portal/frontend/[orgSlug]/portal/page.js +3 -3
  123. package/dist/modules/portal/frontend/[orgSlug]/portal/page.js.map +2 -2
  124. package/dist/modules/portal/frontend/[orgSlug]/portal/profile/page.js +3 -3
  125. package/dist/modules/portal/frontend/[orgSlug]/portal/profile/page.js.map +2 -2
  126. package/dist/modules/portal/frontend/[orgSlug]/portal/reset-password/page.js +3 -3
  127. package/dist/modules/portal/frontend/[orgSlug]/portal/reset-password/page.js.map +2 -2
  128. package/dist/modules/portal/frontend/[orgSlug]/portal/signup/page.js +3 -3
  129. package/dist/modules/portal/frontend/[orgSlug]/portal/signup/page.js.map +2 -2
  130. package/dist/modules/query_index/components/QueryIndexesTable.js +2 -1
  131. package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
  132. package/dist/modules/query_index/events.js +20 -0
  133. package/dist/modules/query_index/events.js.map +7 -0
  134. package/dist/modules/query_index/extension-points.js +13 -0
  135. package/dist/modules/query_index/extension-points.js.map +7 -0
  136. package/dist/modules/resources/backend/resources/resource-types/page.js +2 -1
  137. package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
  138. package/dist/modules/resources/backend/resources/resources/page.js +2 -1
  139. package/dist/modules/resources/backend/resources/resources/page.js.map +2 -2
  140. package/dist/modules/resources/extension-points.js +14 -0
  141. package/dist/modules/resources/extension-points.js.map +7 -0
  142. package/dist/modules/sales/backend/sales/documents/[id]/page.js +11 -3
  143. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  144. package/dist/modules/sales/components/documents/ItemsSection.js +2 -1
  145. package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
  146. package/dist/modules/sales/components/documents/SalesDocumentsTable.js +4 -1
  147. package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
  148. package/dist/modules/sales/events.js +1 -0
  149. package/dist/modules/sales/events.js.map +2 -2
  150. package/dist/modules/sales/extension-points.js +36 -0
  151. package/dist/modules/sales/extension-points.js.map +7 -0
  152. package/dist/modules/wms/components/backend/WmsConfigurationPage.js +5 -4
  153. package/dist/modules/wms/components/backend/WmsConfigurationPage.js.map +2 -2
  154. package/dist/modules/wms/components/backend/WmsInventoryConsolePage.js +4 -3
  155. package/dist/modules/wms/components/backend/WmsInventoryConsolePage.js.map +2 -2
  156. package/dist/modules/wms/components/backend/WmsLocationDetailPage.js +3 -2
  157. package/dist/modules/wms/components/backend/WmsLocationDetailPage.js.map +2 -2
  158. package/dist/modules/wms/components/backend/WmsLotDetailPage.js +3 -2
  159. package/dist/modules/wms/components/backend/WmsLotDetailPage.js.map +2 -2
  160. package/dist/modules/wms/components/backend/WmsLotsListPage.js +2 -1
  161. package/dist/modules/wms/components/backend/WmsLotsListPage.js.map +2 -2
  162. package/dist/modules/wms/components/backend/WmsSkuDetailPage.js +3 -2
  163. package/dist/modules/wms/components/backend/WmsSkuDetailPage.js.map +2 -2
  164. package/dist/modules/wms/extension-points.js +26 -0
  165. package/dist/modules/wms/extension-points.js.map +7 -0
  166. package/dist/modules/workflows/backend/definitions/page.js +2 -1
  167. package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
  168. package/dist/modules/workflows/backend/instances/page.js +2 -1
  169. package/dist/modules/workflows/backend/instances/page.js.map +2 -2
  170. package/dist/modules/workflows/backend/tasks/page.js +2 -1
  171. package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
  172. package/dist/modules/workflows/events.js +1 -0
  173. package/dist/modules/workflows/events.js.map +2 -2
  174. package/dist/modules/workflows/extension-points.js +15 -0
  175. package/dist/modules/workflows/extension-points.js.map +7 -0
  176. package/package.json +7 -7
  177. package/src/modules/api_keys/backend/api-keys/page.tsx +2 -1
  178. package/src/modules/api_keys/extension-points.ts +13 -0
  179. package/src/modules/audit_logs/components/AccessLogsTable.tsx +2 -1
  180. package/src/modules/audit_logs/components/AuditLogsActions.tsx +2 -1
  181. package/src/modules/audit_logs/extension-points.ts +11 -0
  182. package/src/modules/auth/backend/roles/page.tsx +2 -1
  183. package/src/modules/auth/backend/users/page.tsx +2 -1
  184. package/src/modules/auth/extension-points.ts +18 -0
  185. package/src/modules/auth/frontend/login.tsx +2 -1
  186. package/src/modules/business_rules/backend/rules/page.tsx +2 -1
  187. package/src/modules/business_rules/events.ts +10 -0
  188. package/src/modules/business_rules/extension-points.ts +10 -0
  189. package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +2 -1
  190. package/src/modules/catalog/backend/catalog/products/create/page.tsx +2 -1
  191. package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +2 -1
  192. package/src/modules/catalog/components/products/ProductsDataTable.tsx +3 -2
  193. package/src/modules/catalog/events.ts +1 -0
  194. package/src/modules/catalog/extension-points.ts +24 -0
  195. package/src/modules/communication_channels/backend/communication_channels/channels/page.tsx +2 -1
  196. package/src/modules/communication_channels/backend/profile/communication-channels/page.tsx +3 -2
  197. package/src/modules/communication_channels/extension-points.ts +17 -0
  198. package/src/modules/configs/backend/config/system-status/page.tsx +2 -1
  199. package/src/modules/configs/extension-points.ts +15 -0
  200. package/src/modules/currencies/backend/currencies/page.tsx +2 -1
  201. package/src/modules/currencies/backend/exchange-rates/page.tsx +2 -1
  202. package/src/modules/currencies/extension-points.ts +11 -0
  203. package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +2 -1
  204. package/src/modules/customer_accounts/backend/customer_accounts/users/page.tsx +2 -1
  205. package/src/modules/customer_accounts/extension-points.ts +11 -0
  206. package/src/modules/customers/backend/customers/companies/[id]/page.tsx +2 -1
  207. package/src/modules/customers/backend/customers/companies/page.tsx +2 -1
  208. package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +5 -4
  209. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +5 -4
  210. package/src/modules/customers/backend/customers/deals/page.tsx +2 -1
  211. package/src/modules/customers/backend/customers/people/[id]/page.tsx +2 -1
  212. package/src/modules/customers/backend/customers/people/page.tsx +2 -1
  213. package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +5 -4
  214. package/src/modules/customers/components/CustomerTodosTable.tsx +2 -1
  215. package/src/modules/customers/extension-points.ts +40 -0
  216. package/src/modules/customers/lib/calendar/format.ts +10 -4
  217. package/src/modules/data_sync/backend/data-sync/page.tsx +2 -1
  218. package/src/modules/data_sync/extension-points.ts +10 -0
  219. package/src/modules/directory/backend/directory/organizations/page.tsx +2 -1
  220. package/src/modules/directory/backend/directory/tenants/page.tsx +2 -1
  221. package/src/modules/directory/extension-points.ts +11 -0
  222. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +5 -1
  223. package/src/modules/entities/components/SystemEntitiesTable.tsx +2 -1
  224. package/src/modules/entities/components/UserEntitiesTable.tsx +2 -1
  225. package/src/modules/entities/extension-points.ts +18 -0
  226. package/src/modules/integrations/backend/integrations/[id]/page.tsx +2 -2
  227. package/src/modules/integrations/extension-points.ts +27 -0
  228. package/src/modules/messages/components/ComposeMessagePageClient.tsx +2 -1
  229. package/src/modules/messages/components/MessageDetailPageClient.tsx +3 -2
  230. package/src/modules/messages/components/MessagesInboxPageClient.tsx +2 -1
  231. package/src/modules/messages/extension-points.ts +13 -0
  232. package/src/modules/payment_gateways/backend/payment-gateways/page.tsx +3 -2
  233. package/src/modules/payment_gateways/extension-points.ts +16 -0
  234. package/src/modules/portal/extension-points.ts +30 -0
  235. package/src/modules/portal/frontend/[orgSlug]/portal/dashboard/page.tsx +3 -3
  236. package/src/modules/portal/frontend/[orgSlug]/portal/login/page.tsx +3 -3
  237. package/src/modules/portal/frontend/[orgSlug]/portal/page.tsx +3 -3
  238. package/src/modules/portal/frontend/[orgSlug]/portal/profile/page.tsx +3 -3
  239. package/src/modules/portal/frontend/[orgSlug]/portal/reset-password/page.tsx +4 -4
  240. package/src/modules/portal/frontend/[orgSlug]/portal/signup/page.tsx +3 -3
  241. package/src/modules/query_index/components/QueryIndexesTable.tsx +2 -1
  242. package/src/modules/query_index/events.ts +17 -0
  243. package/src/modules/query_index/extension-points.ts +10 -0
  244. package/src/modules/resources/backend/resources/resource-types/page.tsx +2 -1
  245. package/src/modules/resources/backend/resources/resources/page.tsx +2 -1
  246. package/src/modules/resources/extension-points.ts +11 -0
  247. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +11 -3
  248. package/src/modules/sales/components/documents/ItemsSection.tsx +2 -1
  249. package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +6 -1
  250. package/src/modules/sales/events.ts +1 -0
  251. package/src/modules/sales/extension-points.ts +33 -0
  252. package/src/modules/wms/components/backend/WmsConfigurationPage.tsx +5 -4
  253. package/src/modules/wms/components/backend/WmsInventoryConsolePage.tsx +4 -3
  254. package/src/modules/wms/components/backend/WmsLocationDetailPage.tsx +3 -2
  255. package/src/modules/wms/components/backend/WmsLotDetailPage.tsx +3 -2
  256. package/src/modules/wms/components/backend/WmsLotsListPage.tsx +2 -1
  257. package/src/modules/wms/components/backend/WmsSkuDetailPage.tsx +3 -2
  258. package/src/modules/wms/extension-points.ts +23 -0
  259. package/src/modules/workflows/backend/definitions/page.tsx +2 -1
  260. package/src/modules/workflows/backend/instances/page.tsx +2 -1
  261. package/src/modules/workflows/backend/tasks/page.tsx +2 -1
  262. package/src/modules/workflows/events.ts +1 -0
  263. package/src/modules/workflows/extension-points.ts +12 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/data_sync/backend/data-sync/page.tsx"],
4
- "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { StatusBadge } from '@open-mercato/ui/primitives/status-badge'\nimport { Card, CardContent, CardHeader, CardTitle } from '@open-mercato/ui/primitives/card'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { Alert, AlertDescription } from '@open-mercato/ui/primitives/alert'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { Separator } from '@open-mercato/ui/primitives/separator'\nimport { Switch } from '@open-mercato/ui/primitives/switch'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n ArrowRightLeft,\n Boxes,\n CalendarClock,\n Clock3,\n Gauge,\n Play,\n PlugZap,\n Repeat,\n Settings2,\n ShieldCheck,\n} from 'lucide-react'\nimport { getSyncRunStatusVariant, getSyncSummaryVariant } from '../../lib/syncRunStatus'\n\ntype SyncRunRow = {\n id: string\n integrationId: string\n entityType: string\n direction: 'import' | 'export'\n status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled' | 'paused'\n createdCount: number\n updatedCount: number\n failedCount: number\n createdAt: string\n}\n\ntype ResponsePayload = {\n items: SyncRunRow[]\n total: number\n page: number\n totalPages: number\n}\n\ntype SyncOption = {\n integrationId: string\n title: string\n description?: string | null\n providerKey?: string | null\n direction: 'import' | 'export' | 'bidirectional'\n runMode?: 'generic' | 'provider'\n canStartRun?: boolean\n supportedEntities: string[]\n hasCredentials: boolean\n isEnabled: boolean\n settingsPath: string\n}\n\ntype SyncOptionsResponse = {\n items: SyncOption[]\n}\n\ntype SyncScheduleRecord = {\n id: string\n integrationId: string\n entityType: string\n direction: 'import' | 'export'\n scheduleType: 'cron' | 'interval'\n scheduleValue: string\n timezone: string\n fullSync: boolean\n isEnabled: boolean\n lastRunAt: string | null\n updatedAt?: string | null\n}\n\ntype SyncSchedulesResponse = {\n items?: SyncScheduleRecord[]\n}\n\ntype SyncScheduleEditorState = {\n id?: string\n scheduleType: 'cron' | 'interval'\n scheduleValue: string\n timezone: string\n fullSync: boolean\n isEnabled: boolean\n lastRunAt: string | null\n updatedAt?: string | null\n}\n\nconst DEFAULT_TIMEZONE = Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'\n\nfunction formatEntityTypeLabel(entityType: string): string {\n return entityType\n .replace(/[_-]+/g, ' ')\n .replace(/\\b\\w/g, (letter) => letter.toUpperCase())\n}\n\nfunction buildDefaultScheduleState(entityType: string): SyncScheduleEditorState {\n const normalized = entityType.trim().toLowerCase()\n const longerInterval = normalized === 'categories' || normalized === 'attributes'\n return {\n scheduleType: 'interval',\n scheduleValue: longerInterval ? '6h' : '1h',\n timezone: DEFAULT_TIMEZONE,\n fullSync: normalized !== 'products',\n isEnabled: true,\n lastRunAt: null,\n updatedAt: null,\n }\n}\n\nexport default function SyncRunsDashboardPage() {\n const router = useRouter()\n const [rows, setRows] = React.useState<SyncRunRow[]>([])\n const [options, setOptions] = React.useState<SyncOption[]>([])\n const [page, setPage] = React.useState(1)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n const [isLoading, setIsLoading] = React.useState(true)\n const [isLoadingOptions, setIsLoadingOptions] = React.useState(true)\n const [selectedIntegrationId, setSelectedIntegrationId] = React.useState('')\n const [selectedEntityType, setSelectedEntityType] = React.useState('')\n const [selectedDirection, setSelectedDirection] = React.useState<'import' | 'export'>('import')\n const [batchSize, setBatchSize] = React.useState('100')\n const [fullSync, setFullSync] = React.useState(false)\n const [scheduleEditor, setScheduleEditor] = React.useState<SyncScheduleEditorState>(() => buildDefaultScheduleState(''))\n const [isLoadingSchedule, setIsLoadingSchedule] = React.useState(false)\n const [isSavingSchedule, setIsSavingSchedule] = React.useState(false)\n const [isDeletingSchedule, setIsDeletingSchedule] = React.useState(false)\n const [reloadToken, setReloadToken] = React.useState(0)\n const scopeVersion = useOrganizationScopeVersion()\n const t = useT()\n const { runMutation } = useGuardedMutation<Record<string, unknown>>({\n contextId: 'data_sync.dashboard',\n })\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', '20')\n if (filterValues.status) params.set('status', filterValues.status as string)\n if (filterValues.direction) params.set('direction', filterValues.direction as string)\n if (search.trim()) params.set('search', search.trim())\n const fallback: ResponsePayload = { items: [], total: 0, page, totalPages: 1 }\n const call = await apiCall<ResponsePayload>(\n `/api/data_sync/runs?${params.toString()}`,\n undefined,\n { fallback },\n )\n if (!call.ok) {\n flash(t('data_sync.dashboard.loadError'), 'error')\n if (!cancelled) setIsLoading(false)\n return\n }\n const payload = call.result ?? fallback\n if (!cancelled) {\n setRows(Array.isArray(payload.items) ? payload.items : [])\n setTotal(payload.total || 0)\n setTotalPages(payload.totalPages || 1)\n setIsLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [page, filterValues, search, reloadToken, scopeVersion, t])\n\n React.useEffect(() => {\n let cancelled = false\n async function loadOptions() {\n setIsLoadingOptions(true)\n const fallback: SyncOptionsResponse = { items: [] }\n const call = await apiCall<SyncOptionsResponse>('/api/data_sync/options', undefined, { fallback })\n if (!cancelled) {\n if (!call.ok) {\n flash(t('data_sync.dashboard.loadError'), 'error')\n setOptions([])\n setIsLoadingOptions(false)\n return\n }\n\n const nextItems = Array.isArray(call.result?.items) ? call.result.items : []\n setOptions(nextItems)\n setSelectedIntegrationId((current) => {\n if (current && nextItems.some((item) => item.integrationId === current)) return current\n return nextItems[0]?.integrationId ?? ''\n })\n setIsLoadingOptions(false)\n }\n }\n\n void loadOptions()\n return () => { cancelled = true }\n }, [scopeVersion, t])\n\n const selectedIntegration = React.useMemo(\n () => options.find((item) => item.integrationId === selectedIntegrationId) ?? null,\n [options, selectedIntegrationId],\n )\n\n const entityOptions = React.useMemo(\n () => selectedIntegration?.supportedEntities ?? [],\n [selectedIntegration],\n )\n\n React.useEffect(() => {\n if (!selectedIntegration) {\n setSelectedEntityType('')\n return\n }\n setSelectedEntityType((current) => (\n current && selectedIntegration.supportedEntities.includes(current)\n ? current\n : (selectedIntegration.supportedEntities[0] ?? '')\n ))\n setSelectedDirection(selectedIntegration.direction === 'export' ? 'export' : 'import')\n }, [selectedIntegration])\n\n React.useEffect(() => {\n if (!selectedIntegration || !selectedEntityType) {\n setScheduleEditor(buildDefaultScheduleState(selectedEntityType))\n return\n }\n\n const currentIntegration = selectedIntegration\n let cancelled = false\n async function loadSchedule() {\n setIsLoadingSchedule(true)\n const integrationId = currentIntegration.integrationId\n const params = new URLSearchParams({\n integrationId,\n entityType: selectedEntityType,\n direction: selectedDirection,\n page: '1',\n pageSize: '1',\n })\n const fallback: SyncSchedulesResponse = { items: [] }\n const call = await apiCall<SyncSchedulesResponse>(`/api/data_sync/schedules?${params.toString()}`, undefined, { fallback })\n\n if (cancelled) return\n\n if (!call.ok) {\n setScheduleEditor(buildDefaultScheduleState(selectedEntityType))\n setIsLoadingSchedule(false)\n return\n }\n\n const record = Array.isArray(call.result?.items) ? call.result?.items[0] : undefined\n if (!record) {\n setScheduleEditor(buildDefaultScheduleState(selectedEntityType))\n setIsLoadingSchedule(false)\n return\n }\n\n setScheduleEditor({\n id: record.id,\n scheduleType: record.scheduleType,\n scheduleValue: record.scheduleValue,\n timezone: record.timezone,\n fullSync: record.fullSync,\n isEnabled: record.isEnabled,\n lastRunAt: record.lastRunAt,\n updatedAt: record.updatedAt ?? null,\n })\n setIsLoadingSchedule(false)\n }\n\n void loadSchedule()\n return () => { cancelled = true }\n }, [selectedDirection, selectedEntityType, selectedIntegration, scopeVersion])\n\n const updateScheduleEditor = React.useCallback((changes: Partial<SyncScheduleEditorState>) => {\n setScheduleEditor((current) => ({ ...current, ...changes }))\n }, [])\n\n const handleCancel = React.useCallback(async (row: SyncRunRow) => {\n // optimistic-lock-exempt: run lifecycle action endpoint (cancel), not a concurrent record edit\n const call = await apiCall(`/api/data_sync/runs/${encodeURIComponent(row.id)}/cancel`, {\n method: 'POST',\n }, { fallback: null })\n if (call.ok) {\n flash(t('data_sync.runs.detail.cancelSuccess'), 'success')\n setReloadToken((token) => token + 1)\n } else {\n flash(t('data_sync.runs.detail.cancelError'), 'error')\n }\n }, [t])\n\n const handleRetry = React.useCallback(async (row: SyncRunRow) => {\n // optimistic-lock-exempt: run lifecycle action endpoint (retry), not a concurrent record edit\n const call = await apiCall(`/api/data_sync/runs/${encodeURIComponent(row.id)}/retry`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ fromBeginning: false }),\n }, { fallback: null })\n if (call.ok) {\n flash(t('data_sync.runs.detail.retrySuccess'), 'success')\n setReloadToken((token) => token + 1)\n } else {\n flash(t('data_sync.runs.detail.retryError'), 'error')\n }\n }, [t])\n\n const handleFiltersApply = React.useCallback((values: FilterValues) => {\n const next: FilterValues = {}\n Object.entries(values).forEach(([key, value]) => {\n if (value !== undefined && value !== '') next[key] = value\n })\n setFilterValues(next)\n setPage(1)\n }, [])\n\n const handleFiltersClear = React.useCallback(() => {\n setFilterValues({})\n setPage(1)\n }, [])\n\n const handleStartSync = React.useCallback(async () => {\n if (!selectedIntegration || !selectedEntityType) return\n\n const parsedBatchSize = Number.parseInt(batchSize, 10)\n if (!Number.isFinite(parsedBatchSize) || parsedBatchSize < 1 || parsedBatchSize > 1000) {\n flash(t('data_sync.dashboard.start.invalidBatchSize', 'Batch size must be between 1 and 1000.'), 'error')\n return\n }\n\n try {\n const call = await runMutation({\n // optimistic-lock-exempt: starts a new sync run (create), not a concurrent record edit\n operation: () => apiCall<{ id: string }>('/api/data_sync/run', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n integrationId: selectedIntegration.integrationId,\n entityType: selectedEntityType,\n direction: selectedDirection,\n batchSize: parsedBatchSize,\n fullSync,\n }),\n }, { fallback: null }),\n mutationPayload: {\n integrationId: selectedIntegration.integrationId,\n entityType: selectedEntityType,\n direction: selectedDirection,\n batchSize: parsedBatchSize,\n fullSync,\n },\n context: {\n operation: 'create',\n actionId: 'start-sync-run',\n integrationId: selectedIntegration.integrationId,\n },\n })\n\n if (!call.ok || !call.result?.id) {\n flash((call.result as { error?: string } | null)?.error ?? t('data_sync.dashboard.start.error', 'Failed to start sync run'), 'error')\n return\n }\n\n flash(t('data_sync.dashboard.start.success', 'Sync run started'), 'success')\n setReloadToken((token) => token + 1)\n router.push(`/backend/data-sync/runs/${encodeURIComponent(call.result.id)}`)\n } catch (error) {\n const message = error instanceof Error ? error.message : t('data_sync.dashboard.start.error', 'Failed to start sync run')\n flash(message, 'error')\n }\n }, [batchSize, fullSync, router, runMutation, selectedDirection, selectedEntityType, selectedIntegration, t])\n\n const handleSaveSchedule = React.useCallback(async () => {\n if (!selectedIntegration || !selectedEntityType) return\n if (scheduleEditor.scheduleValue.trim().length === 0) {\n flash(t('data_sync.dashboard.schedule.invalidValue', 'Provide a schedule value before saving.'), 'error')\n return\n }\n\n setIsSavingSchedule(true)\n try {\n const call = await runMutation({\n // Keyed upsert (POST). When the editor holds an existing schedule's\n // `updatedAt`, the lock header version-checks the resolved row on the\n // server; a brand-new schedule has a null `updatedAt`, so the header is\n // empty and the create path stays unaffected.\n operation: () => withScopedApiRequestHeaders(\n buildOptimisticLockHeader(scheduleEditor.updatedAt),\n () => apiCall<SyncScheduleRecord>('/api/data_sync/schedules', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n integrationId: selectedIntegration.integrationId,\n entityType: selectedEntityType,\n direction: selectedDirection,\n scheduleType: scheduleEditor.scheduleType,\n scheduleValue: scheduleEditor.scheduleValue.trim(),\n timezone: scheduleEditor.timezone.trim() || DEFAULT_TIMEZONE,\n fullSync: scheduleEditor.fullSync,\n isEnabled: scheduleEditor.isEnabled,\n }),\n }, { fallback: null }),\n ),\n mutationPayload: {\n integrationId: selectedIntegration.integrationId,\n entityType: selectedEntityType,\n direction: selectedDirection,\n scheduleType: scheduleEditor.scheduleType,\n scheduleValue: scheduleEditor.scheduleValue.trim(),\n timezone: scheduleEditor.timezone.trim() || DEFAULT_TIMEZONE,\n fullSync: scheduleEditor.fullSync,\n isEnabled: scheduleEditor.isEnabled,\n },\n context: {\n operation: 'update',\n actionId: 'save-sync-schedule',\n integrationId: selectedIntegration.integrationId,\n },\n })\n\n if (!call.ok || !call.result) {\n const conflictError = Object.assign(\n new Error((call.result as { error?: string } | null)?.error ?? t('data_sync.dashboard.schedule.error', 'Failed to save recurring schedule')),\n {\n status: call.status,\n ...(call.result && typeof call.result === 'object' ? call.result : {}),\n },\n )\n if (surfaceRecordConflict(conflictError, t)) {\n return\n }\n flash((call.result as { error?: string } | null)?.error ?? t('data_sync.dashboard.schedule.error', 'Failed to save recurring schedule'), 'error')\n return\n }\n\n setScheduleEditor({\n id: call.result.id,\n scheduleType: call.result.scheduleType,\n scheduleValue: call.result.scheduleValue,\n timezone: call.result.timezone,\n fullSync: call.result.fullSync,\n isEnabled: call.result.isEnabled,\n lastRunAt: call.result.lastRunAt,\n updatedAt: call.result.updatedAt ?? null,\n })\n flash(t('data_sync.dashboard.schedule.success', 'Recurring schedule saved'), 'success')\n } catch (error) {\n const message = error instanceof Error ? error.message : t('data_sync.dashboard.schedule.error', 'Failed to save recurring schedule')\n flash(message, 'error')\n } finally {\n setIsSavingSchedule(false)\n }\n }, [runMutation, scheduleEditor, selectedDirection, selectedEntityType, selectedIntegration, t])\n\n const handleDeleteSchedule = React.useCallback(async () => {\n if (!scheduleEditor.id) return\n\n setIsDeletingSchedule(true)\n try {\n const call = await runMutation({\n operation: () => withScopedApiRequestHeaders(\n buildOptimisticLockHeader(scheduleEditor.updatedAt),\n () => apiCall(`/api/data_sync/schedules/${encodeURIComponent(scheduleEditor.id as string)}`, {\n method: 'DELETE',\n }, { fallback: null }),\n ),\n mutationPayload: {\n scheduleId: scheduleEditor.id,\n },\n context: {\n operation: 'delete',\n actionId: 'delete-sync-schedule',\n },\n })\n\n if (!call.ok) {\n flash((call.result as { error?: string } | null)?.error ?? t('data_sync.dashboard.schedule.deleteError', 'Failed to remove recurring schedule'), 'error')\n return\n }\n\n setScheduleEditor(buildDefaultScheduleState(selectedEntityType))\n flash(t('data_sync.dashboard.schedule.deleteSuccess', 'Recurring schedule removed'), 'success')\n } catch (error) {\n const message = error instanceof Error ? error.message : t('data_sync.dashboard.schedule.deleteError', 'Failed to remove recurring schedule')\n flash(message, 'error')\n } finally {\n setIsDeletingSchedule(false)\n }\n }, [runMutation, scheduleEditor.id, scheduleEditor.updatedAt, selectedEntityType, t])\n\n const filters: FilterDef[] = [\n {\n id: 'status',\n type: 'select',\n label: t('data_sync.dashboard.filters.status'),\n options: [\n { label: t('data_sync.dashboard.filters.allStatuses'), value: '' },\n { label: t('data_sync.dashboard.status.pending'), value: 'pending' },\n { label: t('data_sync.dashboard.status.running'), value: 'running' },\n { label: t('data_sync.dashboard.status.completed'), value: 'completed' },\n { label: t('data_sync.dashboard.status.failed'), value: 'failed' },\n { label: t('data_sync.dashboard.status.cancelled'), value: 'cancelled' },\n ],\n },\n {\n id: 'direction',\n type: 'select',\n label: t('data_sync.dashboard.columns.direction'),\n options: [\n { label: t('data_sync.dashboard.filters.allDirections'), value: '' },\n { label: t('data_sync.dashboard.direction.import'), value: 'import' },\n { label: t('data_sync.dashboard.direction.export'), value: 'export' },\n ],\n },\n ]\n\n const columns = React.useMemo<ColumnDef<SyncRunRow>[]>(() => [\n {\n accessorKey: 'integrationId',\n header: t('data_sync.dashboard.columns.integration'),\n cell: ({ row }) => <span className=\"font-medium text-sm\">{row.original.integrationId}</span>,\n },\n {\n accessorKey: 'entityType',\n header: t('data_sync.dashboard.columns.entityType'),\n },\n {\n accessorKey: 'direction',\n header: t('data_sync.dashboard.columns.direction'),\n cell: ({ row }) => (\n <Badge variant=\"outline\">\n {t(`data_sync.dashboard.direction.${row.original.direction}`)}\n </Badge>\n ),\n },\n {\n accessorKey: 'status',\n header: t('data_sync.dashboard.columns.status'),\n cell: ({ row }) => (\n <StatusBadge variant={getSyncRunStatusVariant(row.original.status)}>\n {t(`data_sync.dashboard.status.${row.original.status}`)}\n </StatusBadge>\n ),\n },\n {\n accessorKey: 'createdCount',\n header: t('data_sync.dashboard.columns.created'),\n },\n {\n accessorKey: 'updatedCount',\n header: t('data_sync.dashboard.columns.updated'),\n },\n {\n accessorKey: 'failedCount',\n header: t('data_sync.dashboard.columns.failed'),\n },\n {\n accessorKey: 'createdAt',\n header: t('data_sync.dashboard.columns.createdAt'),\n cell: ({ row }) => new Date(row.original.createdAt).toLocaleString(),\n },\n ], [t])\n\n const canStartSelectedIntegration = Boolean(\n selectedIntegration\n && selectedEntityType\n && selectedIntegration.isEnabled\n && selectedIntegration.canStartRun !== false\n && selectedIntegration.hasCredentials,\n )\n const hasSavedSchedule = Boolean(scheduleEditor.id)\n const selectedEntityLabel = selectedEntityType ? formatEntityTypeLabel(selectedEntityType) : t('data_sync.dashboard.columns.entityType')\n const integrationStateVariant = getSyncSummaryVariant(selectedIntegration?.isEnabled ? 'enabled' : 'disabled')\n const credentialsVariant = getSyncSummaryVariant(selectedIntegration?.hasCredentials ? 'ready' : 'missing')\n const scheduleVariant = getSyncSummaryVariant(\n hasSavedSchedule\n ? (scheduleEditor.isEnabled ? 'scheduled' : 'paused')\n : 'none',\n )\n\n return (\n <Page>\n <PageBody className=\"space-y-6\">\n <Card>\n <CardHeader className=\"space-y-4\">\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-2 text-xs font-medium uppercase tracking-widest text-muted-foreground\">\n <Repeat className=\"size-4\" />\n <span>{t('data_sync.dashboard.start.eyebrow', 'Run once or keep it recurring')}</span>\n </div>\n <div className=\"space-y-1\">\n <CardTitle>{t('data_sync.dashboard.start.title', 'Start or schedule a sync')}</CardTitle>\n <p className=\"max-w-3xl text-sm text-muted-foreground\">\n {t('data_sync.dashboard.start.description', 'Pick a sync target, launch an ad-hoc run, or save a recurring schedule for the same entity and direction from this page.')}\n </p>\n </div>\n </div>\n {selectedIntegration ? (\n <Button asChild variant=\"outline\">\n <Link href={selectedIntegration.settingsPath}>\n <Settings2 className=\"mr-2 size-4\" />\n {t('integrations.marketplace.configure')}\n </Link>\n </Button>\n ) : null}\n </div>\n\n {selectedIntegration ? (\n <div className=\"flex flex-wrap gap-2\">\n <Badge variant=\"outline\" className=\"gap-1.5\">\n <PlugZap className=\"size-3.5\" />\n {selectedIntegration.title}\n </Badge>\n <Badge variant=\"outline\" className=\"gap-1.5\">\n <ArrowRightLeft className=\"size-3.5\" />\n {t(`data_sync.dashboard.direction.${selectedDirection}`)}\n </Badge>\n <Badge variant={integrationStateVariant} className=\"gap-1.5\">\n <ShieldCheck className=\"size-3.5\" />\n {selectedIntegration.isEnabled\n ? t('data_sync.dashboard.start.status.enabled', 'Integration enabled')\n : t('data_sync.dashboard.start.status.disabled', 'Integration disabled')}\n </Badge>\n <Badge variant={credentialsVariant} className=\"gap-1.5\">\n <PlugZap className=\"size-3.5\" />\n {selectedIntegration.hasCredentials\n ? t('data_sync.dashboard.start.status.credentialsReady', 'Credentials ready')\n : t('data_sync.dashboard.start.status.credentialsMissing', 'Credentials missing')}\n </Badge>\n <Badge variant={scheduleVariant} className=\"gap-1.5\">\n <CalendarClock className=\"size-3.5\" />\n {hasSavedSchedule\n ? (scheduleEditor.isEnabled\n ? t('data_sync.dashboard.schedule.status.enabled', 'Recurring schedule active')\n : t('data_sync.dashboard.schedule.status.disabled', 'Recurring schedule paused'))\n : t('data_sync.dashboard.schedule.status.none', 'No recurring schedule')}\n </Badge>\n </div>\n ) : null}\n </CardHeader>\n <CardContent className=\"space-y-6\">\n <div className=\"grid gap-4 xl:grid-cols-3\">\n <div className=\"space-y-2 xl:col-span-1\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <PlugZap className=\"size-4 text-muted-foreground\" />\n <span>{t('data_sync.dashboard.columns.integration')}</span>\n </Label>\n <Select\n value={selectedIntegrationId || undefined}\n onValueChange={(value) => setSelectedIntegrationId(value ?? '')}\n disabled={isLoadingOptions || options.length === 0}\n >\n <SelectTrigger size=\"lg\">\n <SelectValue\n placeholder={\n options.length === 0\n ? t('integrations.marketplace.noResults', 'No integrations found')\n : undefined\n }\n />\n </SelectTrigger>\n <SelectContent>\n {options.map((item) => (\n <SelectItem key={item.integrationId} value={item.integrationId}>\n {item.title}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <Boxes className=\"size-4 text-muted-foreground\" />\n <span>{t('data_sync.dashboard.columns.entityType')}</span>\n </Label>\n <Select\n value={selectedEntityType || undefined}\n onValueChange={(value) => setSelectedEntityType(value ?? '')}\n disabled={entityOptions.length === 0}\n >\n <SelectTrigger size=\"lg\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {entityOptions.map((entityType) => (\n <SelectItem key={entityType} value={entityType}>\n {formatEntityTypeLabel(entityType)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <ArrowRightLeft className=\"size-4 text-muted-foreground\" />\n <span>{t('data_sync.dashboard.columns.direction')}</span>\n </Label>\n <Select\n value={selectedDirection}\n onValueChange={(value) => setSelectedDirection(value === 'export' ? 'export' : 'import')}\n disabled={selectedIntegration?.direction !== 'bidirectional'}\n >\n <SelectTrigger size=\"lg\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"import\">{t('data_sync.dashboard.direction.import')}</SelectItem>\n {(selectedIntegration?.direction === 'bidirectional' || selectedIntegration?.direction === 'export') ? (\n <SelectItem value=\"export\">{t('data_sync.dashboard.direction.export')}</SelectItem>\n ) : null}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n {selectedIntegration?.description ? (\n <p className=\"text-sm text-muted-foreground\">{selectedIntegration.description}</p>\n ) : null}\n\n <div className=\"grid gap-4 xl:grid-cols-2\">\n <div className=\"rounded-xl border bg-muted/30 p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <Play className=\"size-4 text-primary\" />\n <h3 className=\"text-sm font-semibold\">{t('data_sync.dashboard.start.runNowTitle', 'Run once now')}</h3>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {t('data_sync.dashboard.start.runNowDescription', 'Use this for the next immediate sync. Batch size and full-sync mode apply only to this manual run.')}\n </p>\n </div>\n <Badge variant=\"outline\">{selectedEntityLabel}</Badge>\n </div>\n\n <Separator className=\"my-4\" />\n\n <div className=\"grid gap-4 sm:grid-cols-[minmax(0,180px)_1fr]\">\n <div className=\"space-y-2\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <Gauge className=\"size-4 text-muted-foreground\" />\n <span>{t('data_sync.dashboard.start.batchSize', 'Batch size')}</span>\n </Label>\n <Input\n value={batchSize}\n onChange={(event) => setBatchSize(event.target.value)}\n inputMode=\"numeric\"\n />\n </div>\n <div className=\"rounded-lg border bg-background p-3\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"space-y-1\">\n <Label className=\"text-sm font-medium\">{t('data_sync.dashboard.start.fullSync', 'Run as full sync')}</Label>\n <p className=\"text-xs text-muted-foreground\">\n {t('data_sync.dashboard.start.fullSyncHelp', 'Ignore the saved cursor and process the entire source again for this run.')}\n </p>\n </div>\n <Switch checked={fullSync} onCheckedChange={setFullSync} />\n </div>\n </div>\n </div>\n\n <div className=\"mt-4 flex flex-wrap items-center justify-between gap-3\">\n <p className=\"text-xs text-muted-foreground\">\n {t('data_sync.dashboard.start.runNowFootnote', 'Manual runs show progress immediately and land on the run detail page after launch.')}\n </p>\n <Button\n type=\"button\"\n onClick={() => void handleStartSync()}\n disabled={!canStartSelectedIntegration}\n >\n <Play className=\"mr-2 size-4\" />\n {t('data_sync.dashboard.start.submit', 'Start sync')}\n </Button>\n </div>\n </div>\n\n <div className=\"rounded-xl border bg-muted/30 p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <CalendarClock className=\"size-4 text-primary\" />\n <h3 className=\"text-sm font-semibold\">{t('data_sync.dashboard.schedule.title', 'Recurring schedule')}</h3>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {t('data_sync.dashboard.schedule.description', 'Save a repeating schedule for the selected integration, entity, and direction without leaving this dashboard.')}\n </p>\n </div>\n <Badge variant=\"outline\">\n {hasSavedSchedule\n ? (scheduleEditor.isEnabled\n ? t('data_sync.dashboard.schedule.status.shortEnabled', 'Scheduled')\n : t('data_sync.dashboard.schedule.status.shortDisabled', 'Paused'))\n : t('data_sync.dashboard.schedule.status.shortNone', 'One-time only')}\n </Badge>\n </div>\n\n <Separator className=\"my-4\" />\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <Clock3 className=\"size-4 text-muted-foreground\" />\n <span>{t('data_sync.dashboard.schedule.type', 'Schedule type')}</span>\n </Label>\n <Select\n value={scheduleEditor.scheduleType}\n onValueChange={(value) => updateScheduleEditor({\n scheduleType: value === 'cron' ? 'cron' : 'interval',\n })}\n disabled={isLoadingSchedule || isSavingSchedule || isDeletingSchedule || !selectedIntegration || !selectedEntityType}\n >\n <SelectTrigger size=\"lg\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"interval\">{t('data_sync.dashboard.schedule.interval', 'Interval')}</SelectItem>\n <SelectItem value=\"cron\">{t('data_sync.dashboard.schedule.cron', 'Cron')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <CalendarClock className=\"size-4 text-muted-foreground\" />\n <span>\n {scheduleEditor.scheduleType === 'cron'\n ? t('data_sync.dashboard.schedule.cronValue', 'Cron expression')\n : t('data_sync.dashboard.schedule.intervalValue', 'Interval')}\n </span>\n </Label>\n <Input\n value={scheduleEditor.scheduleValue}\n onChange={(event) => updateScheduleEditor({ scheduleValue: event.target.value })}\n disabled={isLoadingSchedule || isSavingSchedule || isDeletingSchedule || !selectedIntegration || !selectedEntityType}\n placeholder={scheduleEditor.scheduleType === 'cron' ? '0 * * * *' : '1h'}\n />\n <p className=\"text-xs text-muted-foreground\">\n {scheduleEditor.scheduleType === 'cron'\n ? t('data_sync.dashboard.schedule.cronHelp', 'Example: `0 * * * *` runs at the start of every hour.')\n : t('data_sync.dashboard.schedule.intervalHelp', 'Example: `1h`, `6h`, or `24h` for repeating intervals.')}\n </p>\n </div>\n <div className=\"space-y-2 sm:col-span-2\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <Clock3 className=\"size-4 text-muted-foreground\" />\n <span>{t('data_sync.dashboard.schedule.timezone', 'Timezone')}</span>\n </Label>\n <Input\n value={scheduleEditor.timezone}\n onChange={(event) => updateScheduleEditor({ timezone: event.target.value })}\n disabled={isLoadingSchedule || isSavingSchedule || isDeletingSchedule || !selectedIntegration || !selectedEntityType}\n />\n </div>\n </div>\n\n <div className=\"mt-4 grid gap-3\">\n <div className=\"rounded-lg border bg-background p-3\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"space-y-1\">\n <Label className=\"text-sm font-medium\">{t('data_sync.dashboard.schedule.fullSync', 'Run scheduled jobs as full sync')}</Label>\n <p className=\"text-xs text-muted-foreground\">\n {t('data_sync.dashboard.schedule.fullSyncHelp', 'When enabled, every recurring run starts from the beginning instead of the saved cursor.')}\n </p>\n </div>\n <Switch\n checked={scheduleEditor.fullSync}\n onCheckedChange={(checked) => updateScheduleEditor({ fullSync: checked })}\n disabled={isLoadingSchedule || isSavingSchedule || isDeletingSchedule || !selectedIntegration || !selectedEntityType}\n />\n </div>\n </div>\n <div className=\"rounded-lg border bg-background p-3\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"space-y-1\">\n <Label className=\"text-sm font-medium\">{t('data_sync.dashboard.schedule.enabled', 'Schedule enabled')}</Label>\n <p className=\"text-xs text-muted-foreground\">\n {t('data_sync.dashboard.schedule.enabledHelp', 'Pause the recurring job without deleting the schedule definition.')}\n </p>\n </div>\n <Switch\n checked={scheduleEditor.isEnabled}\n onCheckedChange={(checked) => updateScheduleEditor({ isEnabled: checked })}\n disabled={isLoadingSchedule || isSavingSchedule || isDeletingSchedule || !selectedIntegration || !selectedEntityType}\n />\n </div>\n </div>\n </div>\n\n <div className=\"mt-4 flex flex-wrap items-center justify-between gap-3\">\n <div className=\"space-y-1 text-xs text-muted-foreground\">\n <div>\n {hasSavedSchedule\n ? (scheduleEditor.lastRunAt\n ? t('data_sync.dashboard.schedule.lastRun', 'Last scheduled run: {value}', {\n value: new Date(scheduleEditor.lastRunAt).toLocaleString(),\n })\n : t('data_sync.dashboard.schedule.neverRun', 'Saved, but no scheduled execution has completed yet.'))\n : t('data_sync.dashboard.schedule.none', 'No recurring schedule saved for this target yet.')}\n </div>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => void handleDeleteSchedule()}\n disabled={!hasSavedSchedule || isDeletingSchedule}\n >\n {isDeletingSchedule\n ? t('data_sync.dashboard.schedule.deleting', 'Removing...')\n : t('data_sync.dashboard.schedule.delete', 'Remove schedule')}\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => void handleSaveSchedule()}\n disabled={isSavingSchedule || !selectedIntegration || !selectedEntityType}\n >\n <CalendarClock className=\"mr-2 size-4\" />\n {isSavingSchedule\n ? t('data_sync.dashboard.schedule.saving', 'Saving...')\n : t('data_sync.dashboard.schedule.save', 'Save recurring schedule')}\n </Button>\n </div>\n </div>\n </div>\n </div>\n\n {selectedIntegration && !selectedIntegration.isEnabled ? (\n <Alert variant=\"warning\">\n <AlertDescription>\n {t('integrations.detail.state.disabled', 'This integration is disabled. Enable it on the integration settings page before starting a sync.')}\n </AlertDescription>\n </Alert>\n ) : null}\n {selectedIntegration && !selectedIntegration.hasCredentials ? (\n <Alert variant=\"warning\">\n <AlertDescription>\n {t('integrations.detail.credentials.notConfigured', 'Credentials are not configured yet. Save the integration credentials before starting a sync.')}\n </AlertDescription>\n </Alert>\n ) : null}\n {selectedIntegration && selectedIntegration.canStartRun === false ? (\n <Alert variant=\"info\">\n <AlertDescription>\n {t('data_sync.dashboard.start.providerManaged', 'This integration starts sync runs from its own setup flow. Open the integration settings page to continue.')}\n </AlertDescription>\n </Alert>\n ) : null}\n </CardContent>\n </Card>\n\n <DataTable\n stickyActionsColumn\n title={t('data_sync.dashboard.title')}\n columns={columns}\n data={rows}\n filters={filters}\n filterValues={filterValues}\n onFiltersApply={handleFiltersApply}\n onFiltersClear={handleFiltersClear}\n searchValue={search}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n searchPlaceholder={t('data_sync.dashboard.searchPlaceholder')}\n perspective={{ tableId: 'data_sync.runs' }}\n onRowClick={(row) => {\n router.push(`/backend/data-sync/runs/${encodeURIComponent(row.id)}`)\n }}\n rowActions={(row) => (\n <RowActions items={[\n {\n id: 'view',\n label: t('data_sync.dashboard.actions.view'),\n onSelect: () => { router.push(`/backend/data-sync/runs/${encodeURIComponent(row.id)}`) },\n },\n ...(row.status === 'running' ? [{\n id: 'cancel',\n label: t('data_sync.runs.detail.cancel'),\n destructive: true,\n onSelect: () => { void handleCancel(row) },\n }] : []),\n ...(row.status === 'failed' ? [{\n id: 'retry',\n label: t('data_sync.runs.detail.retry'),\n onSelect: () => { void handleRetry(row) },\n }] : []),\n ]} />\n )}\n pagination={{ page, pageSize: 20, total, totalPages, onPageChange: setPage }}\n isLoading={isLoading}\n />\n </PageBody>\n </Page>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { extensionPoints } from '@open-mercato/core/modules/data_sync/extension-points'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { StatusBadge } from '@open-mercato/ui/primitives/status-badge'\nimport { Card, CardContent, CardHeader, CardTitle } from '@open-mercato/ui/primitives/card'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { Alert, AlertDescription } from '@open-mercato/ui/primitives/alert'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { Separator } from '@open-mercato/ui/primitives/separator'\nimport { Switch } from '@open-mercato/ui/primitives/switch'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n ArrowRightLeft,\n Boxes,\n CalendarClock,\n Clock3,\n Gauge,\n Play,\n PlugZap,\n Repeat,\n Settings2,\n ShieldCheck,\n} from 'lucide-react'\nimport { getSyncRunStatusVariant, getSyncSummaryVariant } from '../../lib/syncRunStatus'\n\ntype SyncRunRow = {\n id: string\n integrationId: string\n entityType: string\n direction: 'import' | 'export'\n status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled' | 'paused'\n createdCount: number\n updatedCount: number\n failedCount: number\n createdAt: string\n}\n\ntype ResponsePayload = {\n items: SyncRunRow[]\n total: number\n page: number\n totalPages: number\n}\n\ntype SyncOption = {\n integrationId: string\n title: string\n description?: string | null\n providerKey?: string | null\n direction: 'import' | 'export' | 'bidirectional'\n runMode?: 'generic' | 'provider'\n canStartRun?: boolean\n supportedEntities: string[]\n hasCredentials: boolean\n isEnabled: boolean\n settingsPath: string\n}\n\ntype SyncOptionsResponse = {\n items: SyncOption[]\n}\n\ntype SyncScheduleRecord = {\n id: string\n integrationId: string\n entityType: string\n direction: 'import' | 'export'\n scheduleType: 'cron' | 'interval'\n scheduleValue: string\n timezone: string\n fullSync: boolean\n isEnabled: boolean\n lastRunAt: string | null\n updatedAt?: string | null\n}\n\ntype SyncSchedulesResponse = {\n items?: SyncScheduleRecord[]\n}\n\ntype SyncScheduleEditorState = {\n id?: string\n scheduleType: 'cron' | 'interval'\n scheduleValue: string\n timezone: string\n fullSync: boolean\n isEnabled: boolean\n lastRunAt: string | null\n updatedAt?: string | null\n}\n\nconst DEFAULT_TIMEZONE = Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'\n\nfunction formatEntityTypeLabel(entityType: string): string {\n return entityType\n .replace(/[_-]+/g, ' ')\n .replace(/\\b\\w/g, (letter) => letter.toUpperCase())\n}\n\nfunction buildDefaultScheduleState(entityType: string): SyncScheduleEditorState {\n const normalized = entityType.trim().toLowerCase()\n const longerInterval = normalized === 'categories' || normalized === 'attributes'\n return {\n scheduleType: 'interval',\n scheduleValue: longerInterval ? '6h' : '1h',\n timezone: DEFAULT_TIMEZONE,\n fullSync: normalized !== 'products',\n isEnabled: true,\n lastRunAt: null,\n updatedAt: null,\n }\n}\n\nexport default function SyncRunsDashboardPage() {\n const router = useRouter()\n const [rows, setRows] = React.useState<SyncRunRow[]>([])\n const [options, setOptions] = React.useState<SyncOption[]>([])\n const [page, setPage] = React.useState(1)\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n const [isLoading, setIsLoading] = React.useState(true)\n const [isLoadingOptions, setIsLoadingOptions] = React.useState(true)\n const [selectedIntegrationId, setSelectedIntegrationId] = React.useState('')\n const [selectedEntityType, setSelectedEntityType] = React.useState('')\n const [selectedDirection, setSelectedDirection] = React.useState<'import' | 'export'>('import')\n const [batchSize, setBatchSize] = React.useState('100')\n const [fullSync, setFullSync] = React.useState(false)\n const [scheduleEditor, setScheduleEditor] = React.useState<SyncScheduleEditorState>(() => buildDefaultScheduleState(''))\n const [isLoadingSchedule, setIsLoadingSchedule] = React.useState(false)\n const [isSavingSchedule, setIsSavingSchedule] = React.useState(false)\n const [isDeletingSchedule, setIsDeletingSchedule] = React.useState(false)\n const [reloadToken, setReloadToken] = React.useState(0)\n const scopeVersion = useOrganizationScopeVersion()\n const t = useT()\n const { runMutation } = useGuardedMutation<Record<string, unknown>>({\n contextId: 'data_sync.dashboard',\n })\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', '20')\n if (filterValues.status) params.set('status', filterValues.status as string)\n if (filterValues.direction) params.set('direction', filterValues.direction as string)\n if (search.trim()) params.set('search', search.trim())\n const fallback: ResponsePayload = { items: [], total: 0, page, totalPages: 1 }\n const call = await apiCall<ResponsePayload>(\n `/api/data_sync/runs?${params.toString()}`,\n undefined,\n { fallback },\n )\n if (!call.ok) {\n flash(t('data_sync.dashboard.loadError'), 'error')\n if (!cancelled) setIsLoading(false)\n return\n }\n const payload = call.result ?? fallback\n if (!cancelled) {\n setRows(Array.isArray(payload.items) ? payload.items : [])\n setTotal(payload.total || 0)\n setTotalPages(payload.totalPages || 1)\n setIsLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [page, filterValues, search, reloadToken, scopeVersion, t])\n\n React.useEffect(() => {\n let cancelled = false\n async function loadOptions() {\n setIsLoadingOptions(true)\n const fallback: SyncOptionsResponse = { items: [] }\n const call = await apiCall<SyncOptionsResponse>('/api/data_sync/options', undefined, { fallback })\n if (!cancelled) {\n if (!call.ok) {\n flash(t('data_sync.dashboard.loadError'), 'error')\n setOptions([])\n setIsLoadingOptions(false)\n return\n }\n\n const nextItems = Array.isArray(call.result?.items) ? call.result.items : []\n setOptions(nextItems)\n setSelectedIntegrationId((current) => {\n if (current && nextItems.some((item) => item.integrationId === current)) return current\n return nextItems[0]?.integrationId ?? ''\n })\n setIsLoadingOptions(false)\n }\n }\n\n void loadOptions()\n return () => { cancelled = true }\n }, [scopeVersion, t])\n\n const selectedIntegration = React.useMemo(\n () => options.find((item) => item.integrationId === selectedIntegrationId) ?? null,\n [options, selectedIntegrationId],\n )\n\n const entityOptions = React.useMemo(\n () => selectedIntegration?.supportedEntities ?? [],\n [selectedIntegration],\n )\n\n React.useEffect(() => {\n if (!selectedIntegration) {\n setSelectedEntityType('')\n return\n }\n setSelectedEntityType((current) => (\n current && selectedIntegration.supportedEntities.includes(current)\n ? current\n : (selectedIntegration.supportedEntities[0] ?? '')\n ))\n setSelectedDirection(selectedIntegration.direction === 'export' ? 'export' : 'import')\n }, [selectedIntegration])\n\n React.useEffect(() => {\n if (!selectedIntegration || !selectedEntityType) {\n setScheduleEditor(buildDefaultScheduleState(selectedEntityType))\n return\n }\n\n const currentIntegration = selectedIntegration\n let cancelled = false\n async function loadSchedule() {\n setIsLoadingSchedule(true)\n const integrationId = currentIntegration.integrationId\n const params = new URLSearchParams({\n integrationId,\n entityType: selectedEntityType,\n direction: selectedDirection,\n page: '1',\n pageSize: '1',\n })\n const fallback: SyncSchedulesResponse = { items: [] }\n const call = await apiCall<SyncSchedulesResponse>(`/api/data_sync/schedules?${params.toString()}`, undefined, { fallback })\n\n if (cancelled) return\n\n if (!call.ok) {\n setScheduleEditor(buildDefaultScheduleState(selectedEntityType))\n setIsLoadingSchedule(false)\n return\n }\n\n const record = Array.isArray(call.result?.items) ? call.result?.items[0] : undefined\n if (!record) {\n setScheduleEditor(buildDefaultScheduleState(selectedEntityType))\n setIsLoadingSchedule(false)\n return\n }\n\n setScheduleEditor({\n id: record.id,\n scheduleType: record.scheduleType,\n scheduleValue: record.scheduleValue,\n timezone: record.timezone,\n fullSync: record.fullSync,\n isEnabled: record.isEnabled,\n lastRunAt: record.lastRunAt,\n updatedAt: record.updatedAt ?? null,\n })\n setIsLoadingSchedule(false)\n }\n\n void loadSchedule()\n return () => { cancelled = true }\n }, [selectedDirection, selectedEntityType, selectedIntegration, scopeVersion])\n\n const updateScheduleEditor = React.useCallback((changes: Partial<SyncScheduleEditorState>) => {\n setScheduleEditor((current) => ({ ...current, ...changes }))\n }, [])\n\n const handleCancel = React.useCallback(async (row: SyncRunRow) => {\n // optimistic-lock-exempt: run lifecycle action endpoint (cancel), not a concurrent record edit\n const call = await apiCall(`/api/data_sync/runs/${encodeURIComponent(row.id)}/cancel`, {\n method: 'POST',\n }, { fallback: null })\n if (call.ok) {\n flash(t('data_sync.runs.detail.cancelSuccess'), 'success')\n setReloadToken((token) => token + 1)\n } else {\n flash(t('data_sync.runs.detail.cancelError'), 'error')\n }\n }, [t])\n\n const handleRetry = React.useCallback(async (row: SyncRunRow) => {\n // optimistic-lock-exempt: run lifecycle action endpoint (retry), not a concurrent record edit\n const call = await apiCall(`/api/data_sync/runs/${encodeURIComponent(row.id)}/retry`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ fromBeginning: false }),\n }, { fallback: null })\n if (call.ok) {\n flash(t('data_sync.runs.detail.retrySuccess'), 'success')\n setReloadToken((token) => token + 1)\n } else {\n flash(t('data_sync.runs.detail.retryError'), 'error')\n }\n }, [t])\n\n const handleFiltersApply = React.useCallback((values: FilterValues) => {\n const next: FilterValues = {}\n Object.entries(values).forEach(([key, value]) => {\n if (value !== undefined && value !== '') next[key] = value\n })\n setFilterValues(next)\n setPage(1)\n }, [])\n\n const handleFiltersClear = React.useCallback(() => {\n setFilterValues({})\n setPage(1)\n }, [])\n\n const handleStartSync = React.useCallback(async () => {\n if (!selectedIntegration || !selectedEntityType) return\n\n const parsedBatchSize = Number.parseInt(batchSize, 10)\n if (!Number.isFinite(parsedBatchSize) || parsedBatchSize < 1 || parsedBatchSize > 1000) {\n flash(t('data_sync.dashboard.start.invalidBatchSize', 'Batch size must be between 1 and 1000.'), 'error')\n return\n }\n\n try {\n const call = await runMutation({\n // optimistic-lock-exempt: starts a new sync run (create), not a concurrent record edit\n operation: () => apiCall<{ id: string }>('/api/data_sync/run', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n integrationId: selectedIntegration.integrationId,\n entityType: selectedEntityType,\n direction: selectedDirection,\n batchSize: parsedBatchSize,\n fullSync,\n }),\n }, { fallback: null }),\n mutationPayload: {\n integrationId: selectedIntegration.integrationId,\n entityType: selectedEntityType,\n direction: selectedDirection,\n batchSize: parsedBatchSize,\n fullSync,\n },\n context: {\n operation: 'create',\n actionId: 'start-sync-run',\n integrationId: selectedIntegration.integrationId,\n },\n })\n\n if (!call.ok || !call.result?.id) {\n flash((call.result as { error?: string } | null)?.error ?? t('data_sync.dashboard.start.error', 'Failed to start sync run'), 'error')\n return\n }\n\n flash(t('data_sync.dashboard.start.success', 'Sync run started'), 'success')\n setReloadToken((token) => token + 1)\n router.push(`/backend/data-sync/runs/${encodeURIComponent(call.result.id)}`)\n } catch (error) {\n const message = error instanceof Error ? error.message : t('data_sync.dashboard.start.error', 'Failed to start sync run')\n flash(message, 'error')\n }\n }, [batchSize, fullSync, router, runMutation, selectedDirection, selectedEntityType, selectedIntegration, t])\n\n const handleSaveSchedule = React.useCallback(async () => {\n if (!selectedIntegration || !selectedEntityType) return\n if (scheduleEditor.scheduleValue.trim().length === 0) {\n flash(t('data_sync.dashboard.schedule.invalidValue', 'Provide a schedule value before saving.'), 'error')\n return\n }\n\n setIsSavingSchedule(true)\n try {\n const call = await runMutation({\n // Keyed upsert (POST). When the editor holds an existing schedule's\n // `updatedAt`, the lock header version-checks the resolved row on the\n // server; a brand-new schedule has a null `updatedAt`, so the header is\n // empty and the create path stays unaffected.\n operation: () => withScopedApiRequestHeaders(\n buildOptimisticLockHeader(scheduleEditor.updatedAt),\n () => apiCall<SyncScheduleRecord>('/api/data_sync/schedules', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n integrationId: selectedIntegration.integrationId,\n entityType: selectedEntityType,\n direction: selectedDirection,\n scheduleType: scheduleEditor.scheduleType,\n scheduleValue: scheduleEditor.scheduleValue.trim(),\n timezone: scheduleEditor.timezone.trim() || DEFAULT_TIMEZONE,\n fullSync: scheduleEditor.fullSync,\n isEnabled: scheduleEditor.isEnabled,\n }),\n }, { fallback: null }),\n ),\n mutationPayload: {\n integrationId: selectedIntegration.integrationId,\n entityType: selectedEntityType,\n direction: selectedDirection,\n scheduleType: scheduleEditor.scheduleType,\n scheduleValue: scheduleEditor.scheduleValue.trim(),\n timezone: scheduleEditor.timezone.trim() || DEFAULT_TIMEZONE,\n fullSync: scheduleEditor.fullSync,\n isEnabled: scheduleEditor.isEnabled,\n },\n context: {\n operation: 'update',\n actionId: 'save-sync-schedule',\n integrationId: selectedIntegration.integrationId,\n },\n })\n\n if (!call.ok || !call.result) {\n const conflictError = Object.assign(\n new Error((call.result as { error?: string } | null)?.error ?? t('data_sync.dashboard.schedule.error', 'Failed to save recurring schedule')),\n {\n status: call.status,\n ...(call.result && typeof call.result === 'object' ? call.result : {}),\n },\n )\n if (surfaceRecordConflict(conflictError, t)) {\n return\n }\n flash((call.result as { error?: string } | null)?.error ?? t('data_sync.dashboard.schedule.error', 'Failed to save recurring schedule'), 'error')\n return\n }\n\n setScheduleEditor({\n id: call.result.id,\n scheduleType: call.result.scheduleType,\n scheduleValue: call.result.scheduleValue,\n timezone: call.result.timezone,\n fullSync: call.result.fullSync,\n isEnabled: call.result.isEnabled,\n lastRunAt: call.result.lastRunAt,\n updatedAt: call.result.updatedAt ?? null,\n })\n flash(t('data_sync.dashboard.schedule.success', 'Recurring schedule saved'), 'success')\n } catch (error) {\n const message = error instanceof Error ? error.message : t('data_sync.dashboard.schedule.error', 'Failed to save recurring schedule')\n flash(message, 'error')\n } finally {\n setIsSavingSchedule(false)\n }\n }, [runMutation, scheduleEditor, selectedDirection, selectedEntityType, selectedIntegration, t])\n\n const handleDeleteSchedule = React.useCallback(async () => {\n if (!scheduleEditor.id) return\n\n setIsDeletingSchedule(true)\n try {\n const call = await runMutation({\n operation: () => withScopedApiRequestHeaders(\n buildOptimisticLockHeader(scheduleEditor.updatedAt),\n () => apiCall(`/api/data_sync/schedules/${encodeURIComponent(scheduleEditor.id as string)}`, {\n method: 'DELETE',\n }, { fallback: null }),\n ),\n mutationPayload: {\n scheduleId: scheduleEditor.id,\n },\n context: {\n operation: 'delete',\n actionId: 'delete-sync-schedule',\n },\n })\n\n if (!call.ok) {\n flash((call.result as { error?: string } | null)?.error ?? t('data_sync.dashboard.schedule.deleteError', 'Failed to remove recurring schedule'), 'error')\n return\n }\n\n setScheduleEditor(buildDefaultScheduleState(selectedEntityType))\n flash(t('data_sync.dashboard.schedule.deleteSuccess', 'Recurring schedule removed'), 'success')\n } catch (error) {\n const message = error instanceof Error ? error.message : t('data_sync.dashboard.schedule.deleteError', 'Failed to remove recurring schedule')\n flash(message, 'error')\n } finally {\n setIsDeletingSchedule(false)\n }\n }, [runMutation, scheduleEditor.id, scheduleEditor.updatedAt, selectedEntityType, t])\n\n const filters: FilterDef[] = [\n {\n id: 'status',\n type: 'select',\n label: t('data_sync.dashboard.filters.status'),\n options: [\n { label: t('data_sync.dashboard.filters.allStatuses'), value: '' },\n { label: t('data_sync.dashboard.status.pending'), value: 'pending' },\n { label: t('data_sync.dashboard.status.running'), value: 'running' },\n { label: t('data_sync.dashboard.status.completed'), value: 'completed' },\n { label: t('data_sync.dashboard.status.failed'), value: 'failed' },\n { label: t('data_sync.dashboard.status.cancelled'), value: 'cancelled' },\n ],\n },\n {\n id: 'direction',\n type: 'select',\n label: t('data_sync.dashboard.columns.direction'),\n options: [\n { label: t('data_sync.dashboard.filters.allDirections'), value: '' },\n { label: t('data_sync.dashboard.direction.import'), value: 'import' },\n { label: t('data_sync.dashboard.direction.export'), value: 'export' },\n ],\n },\n ]\n\n const columns = React.useMemo<ColumnDef<SyncRunRow>[]>(() => [\n {\n accessorKey: 'integrationId',\n header: t('data_sync.dashboard.columns.integration'),\n cell: ({ row }) => <span className=\"font-medium text-sm\">{row.original.integrationId}</span>,\n },\n {\n accessorKey: 'entityType',\n header: t('data_sync.dashboard.columns.entityType'),\n },\n {\n accessorKey: 'direction',\n header: t('data_sync.dashboard.columns.direction'),\n cell: ({ row }) => (\n <Badge variant=\"outline\">\n {t(`data_sync.dashboard.direction.${row.original.direction}`)}\n </Badge>\n ),\n },\n {\n accessorKey: 'status',\n header: t('data_sync.dashboard.columns.status'),\n cell: ({ row }) => (\n <StatusBadge variant={getSyncRunStatusVariant(row.original.status)}>\n {t(`data_sync.dashboard.status.${row.original.status}`)}\n </StatusBadge>\n ),\n },\n {\n accessorKey: 'createdCount',\n header: t('data_sync.dashboard.columns.created'),\n },\n {\n accessorKey: 'updatedCount',\n header: t('data_sync.dashboard.columns.updated'),\n },\n {\n accessorKey: 'failedCount',\n header: t('data_sync.dashboard.columns.failed'),\n },\n {\n accessorKey: 'createdAt',\n header: t('data_sync.dashboard.columns.createdAt'),\n cell: ({ row }) => new Date(row.original.createdAt).toLocaleString(),\n },\n ], [t])\n\n const canStartSelectedIntegration = Boolean(\n selectedIntegration\n && selectedEntityType\n && selectedIntegration.isEnabled\n && selectedIntegration.canStartRun !== false\n && selectedIntegration.hasCredentials,\n )\n const hasSavedSchedule = Boolean(scheduleEditor.id)\n const selectedEntityLabel = selectedEntityType ? formatEntityTypeLabel(selectedEntityType) : t('data_sync.dashboard.columns.entityType')\n const integrationStateVariant = getSyncSummaryVariant(selectedIntegration?.isEnabled ? 'enabled' : 'disabled')\n const credentialsVariant = getSyncSummaryVariant(selectedIntegration?.hasCredentials ? 'ready' : 'missing')\n const scheduleVariant = getSyncSummaryVariant(\n hasSavedSchedule\n ? (scheduleEditor.isEnabled ? 'scheduled' : 'paused')\n : 'none',\n )\n\n return (\n <Page>\n <PageBody className=\"space-y-6\">\n <Card>\n <CardHeader className=\"space-y-4\">\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-2 text-xs font-medium uppercase tracking-widest text-muted-foreground\">\n <Repeat className=\"size-4\" />\n <span>{t('data_sync.dashboard.start.eyebrow', 'Run once or keep it recurring')}</span>\n </div>\n <div className=\"space-y-1\">\n <CardTitle>{t('data_sync.dashboard.start.title', 'Start or schedule a sync')}</CardTitle>\n <p className=\"max-w-3xl text-sm text-muted-foreground\">\n {t('data_sync.dashboard.start.description', 'Pick a sync target, launch an ad-hoc run, or save a recurring schedule for the same entity and direction from this page.')}\n </p>\n </div>\n </div>\n {selectedIntegration ? (\n <Button asChild variant=\"outline\">\n <Link href={selectedIntegration.settingsPath}>\n <Settings2 className=\"mr-2 size-4\" />\n {t('integrations.marketplace.configure')}\n </Link>\n </Button>\n ) : null}\n </div>\n\n {selectedIntegration ? (\n <div className=\"flex flex-wrap gap-2\">\n <Badge variant=\"outline\" className=\"gap-1.5\">\n <PlugZap className=\"size-3.5\" />\n {selectedIntegration.title}\n </Badge>\n <Badge variant=\"outline\" className=\"gap-1.5\">\n <ArrowRightLeft className=\"size-3.5\" />\n {t(`data_sync.dashboard.direction.${selectedDirection}`)}\n </Badge>\n <Badge variant={integrationStateVariant} className=\"gap-1.5\">\n <ShieldCheck className=\"size-3.5\" />\n {selectedIntegration.isEnabled\n ? t('data_sync.dashboard.start.status.enabled', 'Integration enabled')\n : t('data_sync.dashboard.start.status.disabled', 'Integration disabled')}\n </Badge>\n <Badge variant={credentialsVariant} className=\"gap-1.5\">\n <PlugZap className=\"size-3.5\" />\n {selectedIntegration.hasCredentials\n ? t('data_sync.dashboard.start.status.credentialsReady', 'Credentials ready')\n : t('data_sync.dashboard.start.status.credentialsMissing', 'Credentials missing')}\n </Badge>\n <Badge variant={scheduleVariant} className=\"gap-1.5\">\n <CalendarClock className=\"size-3.5\" />\n {hasSavedSchedule\n ? (scheduleEditor.isEnabled\n ? t('data_sync.dashboard.schedule.status.enabled', 'Recurring schedule active')\n : t('data_sync.dashboard.schedule.status.disabled', 'Recurring schedule paused'))\n : t('data_sync.dashboard.schedule.status.none', 'No recurring schedule')}\n </Badge>\n </div>\n ) : null}\n </CardHeader>\n <CardContent className=\"space-y-6\">\n <div className=\"grid gap-4 xl:grid-cols-3\">\n <div className=\"space-y-2 xl:col-span-1\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <PlugZap className=\"size-4 text-muted-foreground\" />\n <span>{t('data_sync.dashboard.columns.integration')}</span>\n </Label>\n <Select\n value={selectedIntegrationId || undefined}\n onValueChange={(value) => setSelectedIntegrationId(value ?? '')}\n disabled={isLoadingOptions || options.length === 0}\n >\n <SelectTrigger size=\"lg\">\n <SelectValue\n placeholder={\n options.length === 0\n ? t('integrations.marketplace.noResults', 'No integrations found')\n : undefined\n }\n />\n </SelectTrigger>\n <SelectContent>\n {options.map((item) => (\n <SelectItem key={item.integrationId} value={item.integrationId}>\n {item.title}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <Boxes className=\"size-4 text-muted-foreground\" />\n <span>{t('data_sync.dashboard.columns.entityType')}</span>\n </Label>\n <Select\n value={selectedEntityType || undefined}\n onValueChange={(value) => setSelectedEntityType(value ?? '')}\n disabled={entityOptions.length === 0}\n >\n <SelectTrigger size=\"lg\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {entityOptions.map((entityType) => (\n <SelectItem key={entityType} value={entityType}>\n {formatEntityTypeLabel(entityType)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <ArrowRightLeft className=\"size-4 text-muted-foreground\" />\n <span>{t('data_sync.dashboard.columns.direction')}</span>\n </Label>\n <Select\n value={selectedDirection}\n onValueChange={(value) => setSelectedDirection(value === 'export' ? 'export' : 'import')}\n disabled={selectedIntegration?.direction !== 'bidirectional'}\n >\n <SelectTrigger size=\"lg\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"import\">{t('data_sync.dashboard.direction.import')}</SelectItem>\n {(selectedIntegration?.direction === 'bidirectional' || selectedIntegration?.direction === 'export') ? (\n <SelectItem value=\"export\">{t('data_sync.dashboard.direction.export')}</SelectItem>\n ) : null}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n {selectedIntegration?.description ? (\n <p className=\"text-sm text-muted-foreground\">{selectedIntegration.description}</p>\n ) : null}\n\n <div className=\"grid gap-4 xl:grid-cols-2\">\n <div className=\"rounded-xl border bg-muted/30 p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <Play className=\"size-4 text-primary\" />\n <h3 className=\"text-sm font-semibold\">{t('data_sync.dashboard.start.runNowTitle', 'Run once now')}</h3>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {t('data_sync.dashboard.start.runNowDescription', 'Use this for the next immediate sync. Batch size and full-sync mode apply only to this manual run.')}\n </p>\n </div>\n <Badge variant=\"outline\">{selectedEntityLabel}</Badge>\n </div>\n\n <Separator className=\"my-4\" />\n\n <div className=\"grid gap-4 sm:grid-cols-[minmax(0,180px)_1fr]\">\n <div className=\"space-y-2\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <Gauge className=\"size-4 text-muted-foreground\" />\n <span>{t('data_sync.dashboard.start.batchSize', 'Batch size')}</span>\n </Label>\n <Input\n value={batchSize}\n onChange={(event) => setBatchSize(event.target.value)}\n inputMode=\"numeric\"\n />\n </div>\n <div className=\"rounded-lg border bg-background p-3\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"space-y-1\">\n <Label className=\"text-sm font-medium\">{t('data_sync.dashboard.start.fullSync', 'Run as full sync')}</Label>\n <p className=\"text-xs text-muted-foreground\">\n {t('data_sync.dashboard.start.fullSyncHelp', 'Ignore the saved cursor and process the entire source again for this run.')}\n </p>\n </div>\n <Switch checked={fullSync} onCheckedChange={setFullSync} />\n </div>\n </div>\n </div>\n\n <div className=\"mt-4 flex flex-wrap items-center justify-between gap-3\">\n <p className=\"text-xs text-muted-foreground\">\n {t('data_sync.dashboard.start.runNowFootnote', 'Manual runs show progress immediately and land on the run detail page after launch.')}\n </p>\n <Button\n type=\"button\"\n onClick={() => void handleStartSync()}\n disabled={!canStartSelectedIntegration}\n >\n <Play className=\"mr-2 size-4\" />\n {t('data_sync.dashboard.start.submit', 'Start sync')}\n </Button>\n </div>\n </div>\n\n <div className=\"rounded-xl border bg-muted/30 p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <CalendarClock className=\"size-4 text-primary\" />\n <h3 className=\"text-sm font-semibold\">{t('data_sync.dashboard.schedule.title', 'Recurring schedule')}</h3>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {t('data_sync.dashboard.schedule.description', 'Save a repeating schedule for the selected integration, entity, and direction without leaving this dashboard.')}\n </p>\n </div>\n <Badge variant=\"outline\">\n {hasSavedSchedule\n ? (scheduleEditor.isEnabled\n ? t('data_sync.dashboard.schedule.status.shortEnabled', 'Scheduled')\n : t('data_sync.dashboard.schedule.status.shortDisabled', 'Paused'))\n : t('data_sync.dashboard.schedule.status.shortNone', 'One-time only')}\n </Badge>\n </div>\n\n <Separator className=\"my-4\" />\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <Clock3 className=\"size-4 text-muted-foreground\" />\n <span>{t('data_sync.dashboard.schedule.type', 'Schedule type')}</span>\n </Label>\n <Select\n value={scheduleEditor.scheduleType}\n onValueChange={(value) => updateScheduleEditor({\n scheduleType: value === 'cron' ? 'cron' : 'interval',\n })}\n disabled={isLoadingSchedule || isSavingSchedule || isDeletingSchedule || !selectedIntegration || !selectedEntityType}\n >\n <SelectTrigger size=\"lg\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"interval\">{t('data_sync.dashboard.schedule.interval', 'Interval')}</SelectItem>\n <SelectItem value=\"cron\">{t('data_sync.dashboard.schedule.cron', 'Cron')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <CalendarClock className=\"size-4 text-muted-foreground\" />\n <span>\n {scheduleEditor.scheduleType === 'cron'\n ? t('data_sync.dashboard.schedule.cronValue', 'Cron expression')\n : t('data_sync.dashboard.schedule.intervalValue', 'Interval')}\n </span>\n </Label>\n <Input\n value={scheduleEditor.scheduleValue}\n onChange={(event) => updateScheduleEditor({ scheduleValue: event.target.value })}\n disabled={isLoadingSchedule || isSavingSchedule || isDeletingSchedule || !selectedIntegration || !selectedEntityType}\n placeholder={scheduleEditor.scheduleType === 'cron' ? '0 * * * *' : '1h'}\n />\n <p className=\"text-xs text-muted-foreground\">\n {scheduleEditor.scheduleType === 'cron'\n ? t('data_sync.dashboard.schedule.cronHelp', 'Example: `0 * * * *` runs at the start of every hour.')\n : t('data_sync.dashboard.schedule.intervalHelp', 'Example: `1h`, `6h`, or `24h` for repeating intervals.')}\n </p>\n </div>\n <div className=\"space-y-2 sm:col-span-2\">\n <Label className=\"flex items-center gap-2 text-sm font-medium\">\n <Clock3 className=\"size-4 text-muted-foreground\" />\n <span>{t('data_sync.dashboard.schedule.timezone', 'Timezone')}</span>\n </Label>\n <Input\n value={scheduleEditor.timezone}\n onChange={(event) => updateScheduleEditor({ timezone: event.target.value })}\n disabled={isLoadingSchedule || isSavingSchedule || isDeletingSchedule || !selectedIntegration || !selectedEntityType}\n />\n </div>\n </div>\n\n <div className=\"mt-4 grid gap-3\">\n <div className=\"rounded-lg border bg-background p-3\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"space-y-1\">\n <Label className=\"text-sm font-medium\">{t('data_sync.dashboard.schedule.fullSync', 'Run scheduled jobs as full sync')}</Label>\n <p className=\"text-xs text-muted-foreground\">\n {t('data_sync.dashboard.schedule.fullSyncHelp', 'When enabled, every recurring run starts from the beginning instead of the saved cursor.')}\n </p>\n </div>\n <Switch\n checked={scheduleEditor.fullSync}\n onCheckedChange={(checked) => updateScheduleEditor({ fullSync: checked })}\n disabled={isLoadingSchedule || isSavingSchedule || isDeletingSchedule || !selectedIntegration || !selectedEntityType}\n />\n </div>\n </div>\n <div className=\"rounded-lg border bg-background p-3\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"space-y-1\">\n <Label className=\"text-sm font-medium\">{t('data_sync.dashboard.schedule.enabled', 'Schedule enabled')}</Label>\n <p className=\"text-xs text-muted-foreground\">\n {t('data_sync.dashboard.schedule.enabledHelp', 'Pause the recurring job without deleting the schedule definition.')}\n </p>\n </div>\n <Switch\n checked={scheduleEditor.isEnabled}\n onCheckedChange={(checked) => updateScheduleEditor({ isEnabled: checked })}\n disabled={isLoadingSchedule || isSavingSchedule || isDeletingSchedule || !selectedIntegration || !selectedEntityType}\n />\n </div>\n </div>\n </div>\n\n <div className=\"mt-4 flex flex-wrap items-center justify-between gap-3\">\n <div className=\"space-y-1 text-xs text-muted-foreground\">\n <div>\n {hasSavedSchedule\n ? (scheduleEditor.lastRunAt\n ? t('data_sync.dashboard.schedule.lastRun', 'Last scheduled run: {value}', {\n value: new Date(scheduleEditor.lastRunAt).toLocaleString(),\n })\n : t('data_sync.dashboard.schedule.neverRun', 'Saved, but no scheduled execution has completed yet.'))\n : t('data_sync.dashboard.schedule.none', 'No recurring schedule saved for this target yet.')}\n </div>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => void handleDeleteSchedule()}\n disabled={!hasSavedSchedule || isDeletingSchedule}\n >\n {isDeletingSchedule\n ? t('data_sync.dashboard.schedule.deleting', 'Removing...')\n : t('data_sync.dashboard.schedule.delete', 'Remove schedule')}\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => void handleSaveSchedule()}\n disabled={isSavingSchedule || !selectedIntegration || !selectedEntityType}\n >\n <CalendarClock className=\"mr-2 size-4\" />\n {isSavingSchedule\n ? t('data_sync.dashboard.schedule.saving', 'Saving...')\n : t('data_sync.dashboard.schedule.save', 'Save recurring schedule')}\n </Button>\n </div>\n </div>\n </div>\n </div>\n\n {selectedIntegration && !selectedIntegration.isEnabled ? (\n <Alert variant=\"warning\">\n <AlertDescription>\n {t('integrations.detail.state.disabled', 'This integration is disabled. Enable it on the integration settings page before starting a sync.')}\n </AlertDescription>\n </Alert>\n ) : null}\n {selectedIntegration && !selectedIntegration.hasCredentials ? (\n <Alert variant=\"warning\">\n <AlertDescription>\n {t('integrations.detail.credentials.notConfigured', 'Credentials are not configured yet. Save the integration credentials before starting a sync.')}\n </AlertDescription>\n </Alert>\n ) : null}\n {selectedIntegration && selectedIntegration.canStartRun === false ? (\n <Alert variant=\"info\">\n <AlertDescription>\n {t('data_sync.dashboard.start.providerManaged', 'This integration starts sync runs from its own setup flow. Open the integration settings page to continue.')}\n </AlertDescription>\n </Alert>\n ) : null}\n </CardContent>\n </Card>\n\n <DataTable\n stickyActionsColumn\n title={t('data_sync.dashboard.title')}\n columns={columns}\n data={rows}\n filters={filters}\n filterValues={filterValues}\n onFiltersApply={handleFiltersApply}\n onFiltersClear={handleFiltersClear}\n searchValue={search}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n searchPlaceholder={t('data_sync.dashboard.searchPlaceholder')}\n perspective={{ tableId: extensionPoints.hosts.runsTable.tableId }}\n onRowClick={(row) => {\n router.push(`/backend/data-sync/runs/${encodeURIComponent(row.id)}`)\n }}\n rowActions={(row) => (\n <RowActions items={[\n {\n id: 'view',\n label: t('data_sync.dashboard.actions.view'),\n onSelect: () => { router.push(`/backend/data-sync/runs/${encodeURIComponent(row.id)}`) },\n },\n ...(row.status === 'running' ? [{\n id: 'cancel',\n label: t('data_sync.runs.detail.cancel'),\n destructive: true,\n onSelect: () => { void handleCancel(row) },\n }] : []),\n ...(row.status === 'failed' ? [{\n id: 'retry',\n label: t('data_sync.runs.detail.retry'),\n onSelect: () => { void handleRetry(row) },\n }] : []),\n ]} />\n )}\n pagination={{ page, pageSize: 20, total, totalPages, onPageChange: setPage }}\n isLoading={isLoading}\n />\n </PageBody>\n </Page>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,13 @@
1
+ import { dataTableExtensionHost, defineModuleExtensionPoints } from "@open-mercato/shared/modules/widgets/extension-points";
2
+ const extensionPoints = defineModuleExtensionPoints({
3
+ moduleId: "data_sync",
4
+ hosts: {
5
+ runsTable: dataTableExtensionHost({ tableId: "data_sync.runs", source: "backend/data-sync/page.tsx" })
6
+ }
7
+ });
8
+ var extension_points_default = extensionPoints;
9
+ export {
10
+ extension_points_default as default,
11
+ extensionPoints
12
+ };
13
+ //# sourceMappingURL=extension-points.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/data_sync/extension-points.ts"],
4
+ "sourcesContent": ["import { dataTableExtensionHost, defineModuleExtensionPoints } from '@open-mercato/shared/modules/widgets/extension-points'\n\nexport const extensionPoints = defineModuleExtensionPoints({\n moduleId: 'data_sync',\n hosts: {\n runsTable: dataTableExtensionHost({ tableId: 'data_sync.runs', source: 'backend/data-sync/page.tsx' }),\n },\n})\n\nexport default extensionPoints\n"],
5
+ "mappings": "AAAA,SAAS,wBAAwB,mCAAmC;AAE7D,MAAM,kBAAkB,4BAA4B;AAAA,EACzD,UAAU;AAAA,EACV,OAAO;AAAA,IACL,WAAW,uBAAuB,EAAE,SAAS,kBAAkB,QAAQ,6BAA6B,CAAC;AAAA,EACvG;AACF,CAAC;AAED,IAAO,2BAAQ;",
6
+ "names": []
7
+ }
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
+ import { extensionPoints } from "@open-mercato/core/modules/directory/extension-points";
4
5
  import Link from "next/link";
5
6
  import { useQuery, useQueryClient } from "@tanstack/react-query";
6
7
  import { Page, PageBody } from "@open-mercato/ui/backend/Page";
@@ -219,7 +220,7 @@ function DirectoryOrganizationsPage() {
219
220
  setPage(1);
220
221
  },
221
222
  sortable: false,
222
- perspective: { tableId: "directory.organizations.list" },
223
+ perspective: { tableId: extensionPoints.hosts.organizationsTable.tableId },
223
224
  rowActions: (row) => canManage ? /* @__PURE__ */ jsx(
224
225
  RowActions,
225
226
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/directory/backend/directory/organizations/page.tsx"],
4
- "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { ListEmptyState } from '@open-mercato/ui/backend/filters/ListEmptyState'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport type { FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCall, apiCallOrThrow, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype OrganizationRow = {\n id: string\n name: string\n tenantId: string\n tenantName?: string | null\n parentId: string | null\n parentName: string | null\n depth: number\n rootId: string\n treePath: string\n pathLabel: string\n ancestorIds: string[]\n childIds: string[]\n descendantIds: string[]\n childrenCount: number\n descendantsCount: number\n isActive: boolean\n updatedAt?: string | null\n}\n\ntype OrganizationsResponse = {\n items: OrganizationRow[]\n total: number\n page: number\n pageSize: number\n totalPages: number\n isSuperAdmin?: boolean\n}\n\nconst TREE_BASE_INDENT = 18\nconst TREE_STEP_INDENT = 14\n\nfunction formatTreeLabel(name: string, depth: number): string {\n if (depth <= 0) return name\n return `${'\\u00A0'.repeat(Math.max(0, (depth - 1) * 2))}\u21B3 ${name}`\n}\n\nfunction computeIndent(depth: number): number {\n if (depth <= 0) return 0\n return TREE_BASE_INDENT + (depth - 1) * TREE_STEP_INDENT\n}\n\nexport default function DirectoryOrganizationsPage() {\n const queryClient = useQueryClient()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const [page, setPage] = React.useState(1)\n const [status, setStatus] = React.useState<string>('all')\n const [search, setSearch] = React.useState('')\n const [canManage, setCanManage] = React.useState(false)\n const scopeVersion = useOrganizationScopeVersion()\n const t = useT()\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n try {\n const call = await apiCall<{ granted?: string[]; ok?: boolean }>('/api/auth/feature-check', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ features: ['directory.organizations.manage'] }),\n })\n if (!cancelled) {\n const granted = Array.isArray(call.result?.granted) ? call.result?.granted : []\n setCanManage(call.result?.ok === true || granted.includes('directory.organizations.manage'))\n }\n } catch {\n if (!cancelled) setCanManage(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [])\n\n const queryParams = React.useMemo(() => {\n const params = new URLSearchParams()\n params.set('view', 'manage')\n params.set('page', String(page))\n params.set('pageSize', '50')\n params.set('status', status)\n if (status !== 'active') params.set('includeInactive', 'true')\n if (search) params.set('search', search)\n return params.toString()\n }, [page, status, search])\n\n const { data, isLoading } = useQuery<OrganizationsResponse>({\n queryKey: ['directory-organizations', queryParams, scopeVersion],\n queryFn: async () => {\n return readApiResultOrThrow<OrganizationsResponse>(\n `/api/directory/organizations?${queryParams}`,\n undefined,\n { errorMessage: t('directory.organizations.list.error.load', 'Failed to load organizations') },\n )\n },\n })\n\n const rows = data?.items ?? []\n const isSuperAdmin = data?.isSuperAdmin ?? false\n const columns = React.useMemo<ColumnDef<OrganizationRow>[]>(() => {\n const base: ColumnDef<OrganizationRow>[] = [\n {\n accessorKey: 'name',\n header: t('directory.organizations.list.columns.organization', 'Organization'),\n cell: ({ row }) => {\n const depth = row.original.depth ?? 0\n return (\n <div className=\"flex items-center text-sm font-medium leading-none text-foreground\">\n <span\n style={{ marginLeft: computeIndent(depth), whiteSpace: 'pre' }}\n >\n {formatTreeLabel(row.original.name, depth)}\n </span>\n </div>\n )\n },\n meta: { priority: 1 },\n },\n {\n accessorKey: 'pathLabel',\n header: t('directory.organizations.list.columns.path', 'Path'),\n meta: { priority: 3 },\n cell: ({ getValue }) => {\n const value = getValue<string>()\n return <span className=\"text-xs text-muted-foreground\">{value}</span>\n },\n },\n {\n accessorKey: 'parentName',\n header: t('directory.organizations.list.columns.parent', 'Parent'),\n meta: { priority: 4 },\n cell: ({ getValue }) => getValue<string>() || t('directory.organizations.common.none', '\u2014'),\n },\n {\n accessorKey: 'childrenCount',\n header: t('directory.organizations.list.columns.children', 'Children'),\n meta: { priority: 5 },\n },\n {\n accessorKey: 'isActive',\n header: t('directory.organizations.list.columns.active', 'Active'),\n enableSorting: false,\n meta: { priority: 2 },\n cell: ({ getValue }) => <BooleanIcon value={Boolean(getValue())} />, \n },\n ]\n if (isSuperAdmin) {\n base.splice(1, 0, {\n accessorKey: 'tenantName',\n header: t('directory.organizations.list.columns.tenant', 'Tenant'),\n meta: { priority: 2 },\n cell: ({ row }) => {\n const value = row.original.tenantName ?? row.original.tenantId\n return <span className=\"text-xs text-muted-foreground\">{value}</span>\n },\n })\n }\n return base\n }, [isSuperAdmin, t])\n const total = data?.total ?? 0\n const totalPages = data?.totalPages ?? 0\n\n const deleteMutationContextId = 'directory-organizations-list:single-delete'\n const { runMutation: runDeleteMutation, retryLastMutation: retryDeleteMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n resourceId: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: deleteMutationContextId,\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n\n const handleDelete = React.useCallback(async (org: OrganizationRow) => {\n const confirmLabel = t('directory.organizations.list.confirmDelete', 'Archive organization \"{{name}}\"?', { name: org.name })\n const confirmed = await confirm({\n title: t('directory.organizations.list.actions.delete', 'Delete'),\n text: confirmLabel,\n variant: 'destructive',\n })\n if (!confirmed) return\n\n try {\n await runDeleteMutation({\n operation: () => withScopedApiRequestHeaders(\n buildOptimisticLockHeader(org.updatedAt),\n () => apiCallOrThrow(\n `/api/directory/organizations?id=${encodeURIComponent(org.id)}`,\n { method: 'DELETE' },\n { errorMessage: t('directory.organizations.list.error.delete', 'Failed to delete organization') },\n ),\n ),\n context: {\n formId: deleteMutationContextId,\n resourceKind: 'directory.organization',\n resourceId: org.id,\n retryLastMutation: retryDeleteMutation,\n },\n })\n await queryClient.invalidateQueries({ queryKey: ['directory-organizations'] })\n flash(t('directory.organizations.flash.deleted', 'Organization deleted'), 'success')\n } catch (err: unknown) {\n // A stale delete surfaces the unified conflict bar (via the guarded\n // mutation) \u2014 skip the generic error flash to avoid a double message.\n if (surfaceRecordConflict(err, t)) {\n await queryClient.invalidateQueries({ queryKey: ['directory-organizations'] })\n return\n }\n const fallback = t('directory.organizations.list.error.delete', 'Failed to delete organization')\n const message = err instanceof Error ? err.message : fallback\n flash(message, 'error')\n }\n }, [confirm, deleteMutationContextId, queryClient, retryDeleteMutation, runDeleteMutation, t])\n\n return (\n <Page>\n <PageBody>\n <DataTable\n title={t('directory.organizations.list.title', 'Organizations')}\n actions={canManage ? (\n <Button asChild>\n <Link href=\"/backend/directory/organizations/create\">\n {t('directory.organizations.list.actions.create', 'Create')}\n </Link>\n </Button>\n ) : undefined}\n columns={columns}\n data={rows}\n searchValue={search}\n searchPlaceholder={t('directory.organizations.list.searchPlaceholder', 'Search organizations')}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n filters={[\n {\n id: 'status',\n label: t('directory.organizations.list.filters.status', 'Status'),\n type: 'select',\n options: [\n { value: 'all', label: t('directory.organizations.list.filters.all', 'All') },\n { value: 'active', label: t('directory.organizations.list.filters.active', 'Active') },\n { value: 'inactive', label: t('directory.organizations.list.filters.inactive', 'Inactive') },\n ],\n },\n ]}\n filterValues={status === 'all' ? {} : { status }}\n onFiltersApply={(vals: FilterValues) => {\n const nextStatus = (vals.status as string) || 'all'\n setStatus(nextStatus)\n setPage(1)\n }}\n onFiltersClear={() => {\n setStatus('all')\n setPage(1)\n }}\n sortable={false}\n perspective={{ tableId: 'directory.organizations.list' }}\n rowActions={(row) => (\n canManage ? (\n <RowActions\n items={[\n { id: 'edit', label: t('directory.organizations.list.actions.edit', 'Edit'), href: `/backend/directory/organizations/${row.id}/edit` },\n { id: 'delete', label: t('directory.organizations.list.actions.delete', 'Delete'), destructive: true, onSelect: () => handleDelete(row) },\n ]}\n />\n ) : null\n )}\n emptyState={(\n <ListEmptyState\n entityName={t('directory.organizations.list.title', 'Organizations')}\n createHref=\"/backend/directory/organizations/create\"\n createLabel={t('directory.organizations.list.actions.create', 'Create')}\n />\n )}\n pagination={{ page, pageSize: 50, total, totalPages, onPageChange: setPage }}\n isLoading={isLoading}\n />\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { extensionPoints } from '@open-mercato/core/modules/directory/extension-points'\nimport Link from 'next/link'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { ListEmptyState } from '@open-mercato/ui/backend/filters/ListEmptyState'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport type { FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCall, apiCallOrThrow, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype OrganizationRow = {\n id: string\n name: string\n tenantId: string\n tenantName?: string | null\n parentId: string | null\n parentName: string | null\n depth: number\n rootId: string\n treePath: string\n pathLabel: string\n ancestorIds: string[]\n childIds: string[]\n descendantIds: string[]\n childrenCount: number\n descendantsCount: number\n isActive: boolean\n updatedAt?: string | null\n}\n\ntype OrganizationsResponse = {\n items: OrganizationRow[]\n total: number\n page: number\n pageSize: number\n totalPages: number\n isSuperAdmin?: boolean\n}\n\nconst TREE_BASE_INDENT = 18\nconst TREE_STEP_INDENT = 14\n\nfunction formatTreeLabel(name: string, depth: number): string {\n if (depth <= 0) return name\n return `${'\\u00A0'.repeat(Math.max(0, (depth - 1) * 2))}\u21B3 ${name}`\n}\n\nfunction computeIndent(depth: number): number {\n if (depth <= 0) return 0\n return TREE_BASE_INDENT + (depth - 1) * TREE_STEP_INDENT\n}\n\nexport default function DirectoryOrganizationsPage() {\n const queryClient = useQueryClient()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const [page, setPage] = React.useState(1)\n const [status, setStatus] = React.useState<string>('all')\n const [search, setSearch] = React.useState('')\n const [canManage, setCanManage] = React.useState(false)\n const scopeVersion = useOrganizationScopeVersion()\n const t = useT()\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n try {\n const call = await apiCall<{ granted?: string[]; ok?: boolean }>('/api/auth/feature-check', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ features: ['directory.organizations.manage'] }),\n })\n if (!cancelled) {\n const granted = Array.isArray(call.result?.granted) ? call.result?.granted : []\n setCanManage(call.result?.ok === true || granted.includes('directory.organizations.manage'))\n }\n } catch {\n if (!cancelled) setCanManage(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [])\n\n const queryParams = React.useMemo(() => {\n const params = new URLSearchParams()\n params.set('view', 'manage')\n params.set('page', String(page))\n params.set('pageSize', '50')\n params.set('status', status)\n if (status !== 'active') params.set('includeInactive', 'true')\n if (search) params.set('search', search)\n return params.toString()\n }, [page, status, search])\n\n const { data, isLoading } = useQuery<OrganizationsResponse>({\n queryKey: ['directory-organizations', queryParams, scopeVersion],\n queryFn: async () => {\n return readApiResultOrThrow<OrganizationsResponse>(\n `/api/directory/organizations?${queryParams}`,\n undefined,\n { errorMessage: t('directory.organizations.list.error.load', 'Failed to load organizations') },\n )\n },\n })\n\n const rows = data?.items ?? []\n const isSuperAdmin = data?.isSuperAdmin ?? false\n const columns = React.useMemo<ColumnDef<OrganizationRow>[]>(() => {\n const base: ColumnDef<OrganizationRow>[] = [\n {\n accessorKey: 'name',\n header: t('directory.organizations.list.columns.organization', 'Organization'),\n cell: ({ row }) => {\n const depth = row.original.depth ?? 0\n return (\n <div className=\"flex items-center text-sm font-medium leading-none text-foreground\">\n <span\n style={{ marginLeft: computeIndent(depth), whiteSpace: 'pre' }}\n >\n {formatTreeLabel(row.original.name, depth)}\n </span>\n </div>\n )\n },\n meta: { priority: 1 },\n },\n {\n accessorKey: 'pathLabel',\n header: t('directory.organizations.list.columns.path', 'Path'),\n meta: { priority: 3 },\n cell: ({ getValue }) => {\n const value = getValue<string>()\n return <span className=\"text-xs text-muted-foreground\">{value}</span>\n },\n },\n {\n accessorKey: 'parentName',\n header: t('directory.organizations.list.columns.parent', 'Parent'),\n meta: { priority: 4 },\n cell: ({ getValue }) => getValue<string>() || t('directory.organizations.common.none', '\u2014'),\n },\n {\n accessorKey: 'childrenCount',\n header: t('directory.organizations.list.columns.children', 'Children'),\n meta: { priority: 5 },\n },\n {\n accessorKey: 'isActive',\n header: t('directory.organizations.list.columns.active', 'Active'),\n enableSorting: false,\n meta: { priority: 2 },\n cell: ({ getValue }) => <BooleanIcon value={Boolean(getValue())} />, \n },\n ]\n if (isSuperAdmin) {\n base.splice(1, 0, {\n accessorKey: 'tenantName',\n header: t('directory.organizations.list.columns.tenant', 'Tenant'),\n meta: { priority: 2 },\n cell: ({ row }) => {\n const value = row.original.tenantName ?? row.original.tenantId\n return <span className=\"text-xs text-muted-foreground\">{value}</span>\n },\n })\n }\n return base\n }, [isSuperAdmin, t])\n const total = data?.total ?? 0\n const totalPages = data?.totalPages ?? 0\n\n const deleteMutationContextId = 'directory-organizations-list:single-delete'\n const { runMutation: runDeleteMutation, retryLastMutation: retryDeleteMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n resourceId: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: deleteMutationContextId,\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n\n const handleDelete = React.useCallback(async (org: OrganizationRow) => {\n const confirmLabel = t('directory.organizations.list.confirmDelete', 'Archive organization \"{{name}}\"?', { name: org.name })\n const confirmed = await confirm({\n title: t('directory.organizations.list.actions.delete', 'Delete'),\n text: confirmLabel,\n variant: 'destructive',\n })\n if (!confirmed) return\n\n try {\n await runDeleteMutation({\n operation: () => withScopedApiRequestHeaders(\n buildOptimisticLockHeader(org.updatedAt),\n () => apiCallOrThrow(\n `/api/directory/organizations?id=${encodeURIComponent(org.id)}`,\n { method: 'DELETE' },\n { errorMessage: t('directory.organizations.list.error.delete', 'Failed to delete organization') },\n ),\n ),\n context: {\n formId: deleteMutationContextId,\n resourceKind: 'directory.organization',\n resourceId: org.id,\n retryLastMutation: retryDeleteMutation,\n },\n })\n await queryClient.invalidateQueries({ queryKey: ['directory-organizations'] })\n flash(t('directory.organizations.flash.deleted', 'Organization deleted'), 'success')\n } catch (err: unknown) {\n // A stale delete surfaces the unified conflict bar (via the guarded\n // mutation) \u2014 skip the generic error flash to avoid a double message.\n if (surfaceRecordConflict(err, t)) {\n await queryClient.invalidateQueries({ queryKey: ['directory-organizations'] })\n return\n }\n const fallback = t('directory.organizations.list.error.delete', 'Failed to delete organization')\n const message = err instanceof Error ? err.message : fallback\n flash(message, 'error')\n }\n }, [confirm, deleteMutationContextId, queryClient, retryDeleteMutation, runDeleteMutation, t])\n\n return (\n <Page>\n <PageBody>\n <DataTable\n title={t('directory.organizations.list.title', 'Organizations')}\n actions={canManage ? (\n <Button asChild>\n <Link href=\"/backend/directory/organizations/create\">\n {t('directory.organizations.list.actions.create', 'Create')}\n </Link>\n </Button>\n ) : undefined}\n columns={columns}\n data={rows}\n searchValue={search}\n searchPlaceholder={t('directory.organizations.list.searchPlaceholder', 'Search organizations')}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n filters={[\n {\n id: 'status',\n label: t('directory.organizations.list.filters.status', 'Status'),\n type: 'select',\n options: [\n { value: 'all', label: t('directory.organizations.list.filters.all', 'All') },\n { value: 'active', label: t('directory.organizations.list.filters.active', 'Active') },\n { value: 'inactive', label: t('directory.organizations.list.filters.inactive', 'Inactive') },\n ],\n },\n ]}\n filterValues={status === 'all' ? {} : { status }}\n onFiltersApply={(vals: FilterValues) => {\n const nextStatus = (vals.status as string) || 'all'\n setStatus(nextStatus)\n setPage(1)\n }}\n onFiltersClear={() => {\n setStatus('all')\n setPage(1)\n }}\n sortable={false}\n perspective={{ tableId: extensionPoints.hosts.organizationsTable.tableId }}\n rowActions={(row) => (\n canManage ? (\n <RowActions\n items={[\n { id: 'edit', label: t('directory.organizations.list.actions.edit', 'Edit'), href: `/backend/directory/organizations/${row.id}/edit` },\n { id: 'delete', label: t('directory.organizations.list.actions.delete', 'Delete'), destructive: true, onSelect: () => handleDelete(row) },\n ]}\n />\n ) : null\n )}\n emptyState={(\n <ListEmptyState\n entityName={t('directory.organizations.list.title', 'Organizations')}\n createHref=\"/backend/directory/organizations/create\"\n createLabel={t('directory.organizations.list.actions.create', 'Create')}\n />\n )}\n pagination={{ page, pageSize: 50, total, totalPages, onPageChange: setPage }}\n isLoading={isLoading}\n />\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
+ import { extensionPoints } from "@open-mercato/core/modules/directory/extension-points";
4
5
  import Link from "next/link";
5
6
  import { useQuery, useQueryClient } from "@tanstack/react-query";
6
7
  import { Page, PageBody } from "@open-mercato/ui/backend/Page";
@@ -169,7 +170,7 @@ function DirectoryTenantsPage() {
169
170
  setSorting(state);
170
171
  setPage(1);
171
172
  },
172
- perspective: { tableId: "directory.tenants.list" },
173
+ perspective: { tableId: extensionPoints.hosts.tenantsTable.tableId },
173
174
  rowActions: (row) => canManage ? /* @__PURE__ */ jsx(
174
175
  RowActions,
175
176
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/directory/backend/directory/tenants/page.tsx"],
4
- "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport type { ColumnDef, SortingState } from '@tanstack/react-table'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { ListEmptyState } from '@open-mercato/ui/backend/filters/ListEmptyState'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'\nimport type { FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCall, apiCallOrThrow, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype TenantRow = {\n id: string\n name: string\n isActive: boolean\n createdAt: string | null\n updatedAt: string | null\n}\n\ntype TenantsResponse = {\n items: TenantRow[]\n total: number\n page: number\n pageSize: number\n totalPages: number\n}\n\n\nexport default function DirectoryTenantsPage() {\n const queryClient = useQueryClient()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const [sorting, setSorting] = React.useState<SortingState>([{ id: 'name', desc: false }])\n const [page, setPage] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const [filters, setFilters] = React.useState<FilterValues>({})\n const [canManage, setCanManage] = React.useState(false)\n const scopeVersion = useOrganizationScopeVersion()\n const t = useT()\n const columns = React.useMemo<ColumnDef<TenantRow>[]>(() => [\n { accessorKey: 'name', header: t('directory.tenants.list.columns.tenant', 'Tenant'), meta: { priority: 1 } },\n {\n accessorKey: 'isActive',\n header: t('directory.tenants.list.columns.active', 'Active'),\n enableSorting: false,\n meta: { priority: 2 },\n cell: ({ getValue }) => <BooleanIcon value={Boolean(getValue())} />,\n },\n {\n accessorKey: 'createdAt',\n header: t('directory.tenants.list.columns.created', 'Created'),\n meta: { priority: 3 },\n cell: ({ getValue }) => {\n const timestamp = getValue() as string | null\n if (!timestamp) return <span className=\"text-xs text-muted-foreground\">\u2014</span>\n const date = new Date(timestamp)\n if (Number.isNaN(date.getTime())) return <span className=\"text-xs text-muted-foreground\">\u2014</span>\n return <span>{date.toLocaleString()}</span>\n },\n },\n ], [t])\n\n React.useEffect(() => {\n let cancelled = false\n async function loadFeature() {\n try {\n const call = await apiCall<{ ok?: boolean; granted?: string[] }>('/api/auth/feature-check', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ features: ['directory.tenants.manage'] }),\n })\n if (!cancelled) {\n const granted = Array.isArray(call.result?.granted) ? call.result!.granted! : []\n setCanManage(call.result?.ok === true || granted.includes('directory.tenants.manage'))\n }\n } catch {\n if (!cancelled) setCanManage(false)\n }\n }\n loadFeature()\n return () => { cancelled = true }\n }, [])\n\n const queryParams = React.useMemo(() => {\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', '20')\n if (sorting.length > 0) {\n params.set('sortField', sorting[0]?.id || 'name')\n params.set('sortDir', sorting[0]?.desc ? 'desc' : 'asc')\n }\n if (search) params.set('search', search)\n if (filters.active !== undefined && filters.active !== '') params.set('isActive', String(filters.active))\n return params.toString()\n }, [page, sorting, search, filters])\n\n const { data, isLoading } = useQuery({\n queryKey: ['directory-tenants', queryParams, scopeVersion],\n queryFn: async (): Promise<TenantsResponse> => {\n return readApiResultOrThrow<TenantsResponse>(\n `/api/directory/tenants?${queryParams}`,\n undefined,\n { errorMessage: t('directory.tenants.list.error.load', 'Failed to load tenants') },\n )\n },\n })\n\n const rows = data?.items ?? []\n const total = data?.total ?? 0\n const totalPages = data?.totalPages ?? 0\n\n const deleteMutationContextId = 'directory-tenants-list:single-delete'\n const { runMutation: runDeleteMutation, retryLastMutation: retryDeleteMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n resourceId: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: deleteMutationContextId,\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n\n const handleDelete = React.useCallback(async (tenant: TenantRow) => {\n const confirmMessage = t('directory.tenants.list.confirmDelete', 'Delete tenant \"{{name}}\"? This will archive it.').replace('{{name}}', tenant.name)\n const confirmed = await confirm({\n title: t('common.delete', 'Delete'),\n text: confirmMessage,\n variant: 'destructive',\n })\n if (!confirmed) return\n\n try {\n await runDeleteMutation({\n operation: () => withScopedApiRequestHeaders(\n buildOptimisticLockHeader(tenant.updatedAt),\n () => apiCallOrThrow(\n `/api/directory/tenants?id=${encodeURIComponent(tenant.id)}`,\n { method: 'DELETE' },\n { errorMessage: t('directory.tenants.list.error.delete', 'Failed to delete tenant') },\n ),\n ),\n context: {\n formId: deleteMutationContextId,\n resourceKind: 'directory.tenant',\n resourceId: tenant.id,\n retryLastMutation: retryDeleteMutation,\n },\n })\n await queryClient.invalidateQueries({ queryKey: ['directory-tenants'] })\n flash(t('directory.tenants.list.success.delete', 'Tenant deleted'), 'success')\n } catch (err: unknown) {\n // A stale delete surfaces the unified conflict bar (via the guarded\n // mutation) \u2014 skip the generic error flash to avoid a double message.\n if (surfaceRecordConflict(err, t)) {\n await queryClient.invalidateQueries({ queryKey: ['directory-tenants'] })\n return\n }\n const message = err instanceof Error ? err.message : t('directory.tenants.list.error.delete', 'Failed to delete tenant')\n flash(message, 'error')\n }\n }, [confirm, deleteMutationContextId, queryClient, retryDeleteMutation, runDeleteMutation, t])\n\n return (\n <Page>\n <PageBody>\n <DataTable\n title={t('directory.tenants.list.title', 'Tenants')}\n actions={canManage ? (\n <Button asChild>\n <Link href=\"/backend/directory/tenants/create\">{t('directory.tenants.list.actions.create', 'Create')}</Link>\n </Button>\n ) : undefined}\n columns={columns}\n data={rows}\n searchValue={search}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n filters={[{ id: 'active', label: t('directory.tenants.list.filters.status', 'Status'), type: 'select', options: [\n { value: 'true', label: t('directory.tenants.list.filters.active', 'Active') },\n { value: 'false', label: t('directory.tenants.list.filters.inactive', 'Inactive') },\n ] }]}\n filterValues={filters}\n onFiltersApply={(vals) => { setFilters(vals); setPage(1) }}\n onFiltersClear={() => { setFilters({}); setPage(1) }}\n sortable\n sorting={sorting}\n onSortingChange={(state) => { setSorting(state); setPage(1) }}\n perspective={{ tableId: 'directory.tenants.list' }}\n rowActions={(row) => (\n canManage ? (\n <RowActions\n items={[\n { id: 'edit', label: t('common.edit', 'Edit'), href: `/backend/directory/tenants/${row.id}/edit` },\n { id: 'delete', label: t('common.delete', 'Delete'), destructive: true, onSelect: () => handleDelete(row) },\n ]}\n />\n ) : null\n )}\n emptyState={(\n <ListEmptyState\n entityName={t('directory.tenants.list.title', 'Tenants')}\n createHref=\"/backend/directory/tenants/create\"\n createLabel={t('directory.tenants.list.actions.create', 'Create')}\n />\n )}\n pagination={{ page, pageSize: 20, total, totalPages, onPageChange: setPage }}\n isLoading={isLoading}\n />\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { extensionPoints } from '@open-mercato/core/modules/directory/extension-points'\nimport Link from 'next/link'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport type { ColumnDef, SortingState } from '@tanstack/react-table'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { ListEmptyState } from '@open-mercato/ui/backend/filters/ListEmptyState'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'\nimport type { FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCall, apiCallOrThrow, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype TenantRow = {\n id: string\n name: string\n isActive: boolean\n createdAt: string | null\n updatedAt: string | null\n}\n\ntype TenantsResponse = {\n items: TenantRow[]\n total: number\n page: number\n pageSize: number\n totalPages: number\n}\n\n\nexport default function DirectoryTenantsPage() {\n const queryClient = useQueryClient()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const [sorting, setSorting] = React.useState<SortingState>([{ id: 'name', desc: false }])\n const [page, setPage] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const [filters, setFilters] = React.useState<FilterValues>({})\n const [canManage, setCanManage] = React.useState(false)\n const scopeVersion = useOrganizationScopeVersion()\n const t = useT()\n const columns = React.useMemo<ColumnDef<TenantRow>[]>(() => [\n { accessorKey: 'name', header: t('directory.tenants.list.columns.tenant', 'Tenant'), meta: { priority: 1 } },\n {\n accessorKey: 'isActive',\n header: t('directory.tenants.list.columns.active', 'Active'),\n enableSorting: false,\n meta: { priority: 2 },\n cell: ({ getValue }) => <BooleanIcon value={Boolean(getValue())} />,\n },\n {\n accessorKey: 'createdAt',\n header: t('directory.tenants.list.columns.created', 'Created'),\n meta: { priority: 3 },\n cell: ({ getValue }) => {\n const timestamp = getValue() as string | null\n if (!timestamp) return <span className=\"text-xs text-muted-foreground\">\u2014</span>\n const date = new Date(timestamp)\n if (Number.isNaN(date.getTime())) return <span className=\"text-xs text-muted-foreground\">\u2014</span>\n return <span>{date.toLocaleString()}</span>\n },\n },\n ], [t])\n\n React.useEffect(() => {\n let cancelled = false\n async function loadFeature() {\n try {\n const call = await apiCall<{ ok?: boolean; granted?: string[] }>('/api/auth/feature-check', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ features: ['directory.tenants.manage'] }),\n })\n if (!cancelled) {\n const granted = Array.isArray(call.result?.granted) ? call.result!.granted! : []\n setCanManage(call.result?.ok === true || granted.includes('directory.tenants.manage'))\n }\n } catch {\n if (!cancelled) setCanManage(false)\n }\n }\n loadFeature()\n return () => { cancelled = true }\n }, [])\n\n const queryParams = React.useMemo(() => {\n const params = new URLSearchParams()\n params.set('page', String(page))\n params.set('pageSize', '20')\n if (sorting.length > 0) {\n params.set('sortField', sorting[0]?.id || 'name')\n params.set('sortDir', sorting[0]?.desc ? 'desc' : 'asc')\n }\n if (search) params.set('search', search)\n if (filters.active !== undefined && filters.active !== '') params.set('isActive', String(filters.active))\n return params.toString()\n }, [page, sorting, search, filters])\n\n const { data, isLoading } = useQuery({\n queryKey: ['directory-tenants', queryParams, scopeVersion],\n queryFn: async (): Promise<TenantsResponse> => {\n return readApiResultOrThrow<TenantsResponse>(\n `/api/directory/tenants?${queryParams}`,\n undefined,\n { errorMessage: t('directory.tenants.list.error.load', 'Failed to load tenants') },\n )\n },\n })\n\n const rows = data?.items ?? []\n const total = data?.total ?? 0\n const totalPages = data?.totalPages ?? 0\n\n const deleteMutationContextId = 'directory-tenants-list:single-delete'\n const { runMutation: runDeleteMutation, retryLastMutation: retryDeleteMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n resourceId: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: deleteMutationContextId,\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n\n const handleDelete = React.useCallback(async (tenant: TenantRow) => {\n const confirmMessage = t('directory.tenants.list.confirmDelete', 'Delete tenant \"{{name}}\"? This will archive it.').replace('{{name}}', tenant.name)\n const confirmed = await confirm({\n title: t('common.delete', 'Delete'),\n text: confirmMessage,\n variant: 'destructive',\n })\n if (!confirmed) return\n\n try {\n await runDeleteMutation({\n operation: () => withScopedApiRequestHeaders(\n buildOptimisticLockHeader(tenant.updatedAt),\n () => apiCallOrThrow(\n `/api/directory/tenants?id=${encodeURIComponent(tenant.id)}`,\n { method: 'DELETE' },\n { errorMessage: t('directory.tenants.list.error.delete', 'Failed to delete tenant') },\n ),\n ),\n context: {\n formId: deleteMutationContextId,\n resourceKind: 'directory.tenant',\n resourceId: tenant.id,\n retryLastMutation: retryDeleteMutation,\n },\n })\n await queryClient.invalidateQueries({ queryKey: ['directory-tenants'] })\n flash(t('directory.tenants.list.success.delete', 'Tenant deleted'), 'success')\n } catch (err: unknown) {\n // A stale delete surfaces the unified conflict bar (via the guarded\n // mutation) \u2014 skip the generic error flash to avoid a double message.\n if (surfaceRecordConflict(err, t)) {\n await queryClient.invalidateQueries({ queryKey: ['directory-tenants'] })\n return\n }\n const message = err instanceof Error ? err.message : t('directory.tenants.list.error.delete', 'Failed to delete tenant')\n flash(message, 'error')\n }\n }, [confirm, deleteMutationContextId, queryClient, retryDeleteMutation, runDeleteMutation, t])\n\n return (\n <Page>\n <PageBody>\n <DataTable\n title={t('directory.tenants.list.title', 'Tenants')}\n actions={canManage ? (\n <Button asChild>\n <Link href=\"/backend/directory/tenants/create\">{t('directory.tenants.list.actions.create', 'Create')}</Link>\n </Button>\n ) : undefined}\n columns={columns}\n data={rows}\n searchValue={search}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n filters={[{ id: 'active', label: t('directory.tenants.list.filters.status', 'Status'), type: 'select', options: [\n { value: 'true', label: t('directory.tenants.list.filters.active', 'Active') },\n { value: 'false', label: t('directory.tenants.list.filters.inactive', 'Inactive') },\n ] }]}\n filterValues={filters}\n onFiltersApply={(vals) => { setFilters(vals); setPage(1) }}\n onFiltersClear={() => { setFilters({}); setPage(1) }}\n sortable\n sorting={sorting}\n onSortingChange={(state) => { setSorting(state); setPage(1) }}\n perspective={{ tableId: extensionPoints.hosts.tenantsTable.tableId }}\n rowActions={(row) => (\n canManage ? (\n <RowActions\n items={[\n { id: 'edit', label: t('common.edit', 'Edit'), href: `/backend/directory/tenants/${row.id}/edit` },\n { id: 'delete', label: t('common.delete', 'Delete'), destructive: true, onSelect: () => handleDelete(row) },\n ]}\n />\n ) : null\n )}\n emptyState={(\n <ListEmptyState\n entityName={t('directory.tenants.list.title', 'Tenants')}\n createHref=\"/backend/directory/tenants/create\"\n createLabel={t('directory.tenants.list.actions.create', 'Create')}\n />\n )}\n pagination={{ page, pageSize: 20, total, totalPages, onPageChange: setPage }}\n isLoading={isLoading}\n />\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }