@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
@@ -0,0 +1,24 @@
1
+ import {
2
+ crudFormExtensionHost,
3
+ dataTableExtensionHost,
4
+ defineModuleExtensionPoints,
5
+ } from '@open-mercato/shared/modules/widgets/extension-points'
6
+
7
+ export const extensionPoints = defineModuleExtensionPoints({
8
+ moduleId: 'catalog',
9
+ hosts: {
10
+ productForm: crudFormExtensionHost({
11
+ entityId: 'catalog.product',
12
+ spotId: 'crud-form:catalog.product',
13
+ source: 'backend/catalog/products/[id]/page.tsx',
14
+ }),
15
+ productsTable: dataTableExtensionHost({
16
+ baseSpotId: 'data-table:catalog.products',
17
+ tableId: 'catalog.products.list',
18
+ source: 'components/products/ProductsDataTable.tsx',
19
+ }),
20
+ categoriesTable: dataTableExtensionHost({ tableId: 'catalog.categories.list', source: 'components/categories/CategoriesDataTable.tsx' }),
21
+ },
22
+ })
23
+
24
+ export default extensionPoints
@@ -1,6 +1,7 @@
1
1
  'use client'
2
2
 
3
3
  import * as React from 'react'
4
+ import { extensionPoints } from '@open-mercato/core/modules/communication_channels/extension-points'
4
5
  import type { ColumnDef } from '@tanstack/react-table'
5
6
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
6
7
  import { DataTable } from '@open-mercato/ui/backend/DataTable'
@@ -114,7 +115,7 @@ export default function ChannelsListPage() {
114
115
  <PageBody>
115
116
  <DataTable<ChannelRow>
116
117
  title={t('communication_channels.nav.title', 'Communication Channels')}
117
- extensionTableId="communication_channels.channels"
118
+ extensionTableId={extensionPoints.hosts.channelsTable.tableId}
118
119
  columns={columns}
119
120
  data={rows}
120
121
  isLoading={isLoading}
@@ -1,6 +1,7 @@
1
1
  'use client'
2
2
 
3
3
  import * as React from 'react'
4
+ import { extensionPoints } from '@open-mercato/core/modules/communication_channels/extension-points'
4
5
  import type { ColumnDef } from '@tanstack/react-table'
5
6
  import { useRouter, useSearchParams } from 'next/navigation'
6
7
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
@@ -469,7 +470,7 @@ export default function ProfileCommunicationChannelsPage() {
469
470
  {/* Provider connect entry points injected by each channel-* package
470
471
  (channel-gmail, channel-imap) via UMES. */}
471
472
  <InjectionSpot
472
- spotId="profile:communication-channels:connect"
473
+ spotId={extensionPoints.hosts.profileConnect.spotId}
473
474
  context={{ reload: () => setReloadKey((k) => k + 1) }}
474
475
  data={{}}
475
476
  />
@@ -489,7 +490,7 @@ export default function ProfileCommunicationChannelsPage() {
489
490
 
490
491
  <DataTable<ChannelRow>
491
492
  title={t('communication_channels.profile.tableTitle', 'Your channels')}
492
- extensionTableId="communication_channels.profile.channels"
493
+ extensionTableId={extensionPoints.hosts.profileChannelsTable.tableId}
493
494
  columns={columns}
494
495
  data={rows}
495
496
  isLoading={isLoading}
@@ -0,0 +1,17 @@
1
+ import { dataTableExtensionHost, defineModuleExtensionPoints, injectionExtensionHost } from '@open-mercato/shared/modules/widgets/extension-points'
2
+
3
+ export const extensionPoints = defineModuleExtensionPoints({
4
+ moduleId: 'communication_channels',
5
+ hosts: {
6
+ channelsTable: dataTableExtensionHost({ tableId: 'communication_channels.channels', source: 'backend/communication_channels/channels/page.tsx' }),
7
+ profileConnect: injectionExtensionHost({
8
+ family: 'generic',
9
+ spotId: 'profile:communication-channels:connect',
10
+ supported: ['render-widget'],
11
+ source: 'backend/profile/communication-channels/page.tsx',
12
+ }),
13
+ profileChannelsTable: dataTableExtensionHost({ tableId: 'communication_channels.profile.channels', source: 'backend/profile/communication-channels/page.tsx' }),
14
+ },
15
+ })
16
+
17
+ export default extensionPoints
@@ -1,5 +1,6 @@
1
1
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
2
2
  import { InjectionSpot } from '@open-mercato/ui/backend/injection/InjectionSpot'
3
+ import { extensionPoints } from '@open-mercato/core/modules/configs/extension-points'
3
4
  import SystemStatusPanel from '../../../components/SystemStatusPanel'
4
5
 
5
6
  export default function SystemStatusPage() {
@@ -8,7 +9,7 @@ export default function SystemStatusPage() {
8
9
  <PageBody>
9
10
  <SystemStatusPanel />
10
11
  <InjectionSpot
11
- spotId="configs.system_status:details"
12
+ spotId={extensionPoints.hosts.systemStatusDetails.spotId}
12
13
  context={{ path: '/backend/config/system-status' }}
13
14
  />
14
15
  </PageBody>
@@ -0,0 +1,15 @@
1
+ import { defineModuleExtensionPoints, injectionExtensionHost } from '@open-mercato/shared/modules/widgets/extension-points'
2
+
3
+ export const extensionPoints = defineModuleExtensionPoints({
4
+ moduleId: 'configs',
5
+ hosts: {
6
+ systemStatusDetails: injectionExtensionHost({
7
+ family: 'detail',
8
+ spotId: 'configs.system_status:details',
9
+ supported: ['render-widget'],
10
+ source: 'backend/config/system-status/page.tsx',
11
+ }),
12
+ },
13
+ })
14
+
15
+ export default extensionPoints
@@ -1,6 +1,7 @@
1
1
  'use client'
2
2
 
3
3
  import * as React from 'react'
4
+ import { extensionPoints } from '@open-mercato/core/modules/currencies/extension-points'
4
5
  import Link from 'next/link'
5
6
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
6
7
  import { DataTable } from '@open-mercato/ui/backend/DataTable'
@@ -343,7 +344,7 @@ export default function CurrenciesPage() {
343
344
  )}
344
345
  pagination={{ page, pageSize: 50, total, totalPages, onPageChange: setPage }}
345
346
  isLoading={isLoading}
346
- perspective={{ tableId: 'currencies.list' }}
347
+ perspective={{ tableId: extensionPoints.hosts.currenciesTable.tableId }}
347
348
  />
348
349
  </PageBody>
349
350
  {ConfirmDialogElement}
@@ -1,6 +1,7 @@
1
1
  'use client'
2
2
 
3
3
  import * as React from 'react'
4
+ import { extensionPoints } from '@open-mercato/core/modules/currencies/extension-points'
4
5
  import Link from 'next/link'
5
6
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
6
7
  import { DataTable } from '@open-mercato/ui/backend/DataTable'
@@ -333,7 +334,7 @@ export default function ExchangeRatesPage() {
333
334
  )}
334
335
  pagination={{ page, pageSize: 50, total, totalPages, onPageChange: setPage }}
335
336
  isLoading={isLoading}
336
- perspective={{ tableId: 'exchange-rates.list' }}
337
+ perspective={{ tableId: extensionPoints.hosts.exchangeRatesTable.tableId }}
337
338
  />
338
339
  </PageBody>
339
340
  {ConfirmDialogElement}
@@ -0,0 +1,11 @@
1
+ import { dataTableExtensionHost, defineModuleExtensionPoints } from '@open-mercato/shared/modules/widgets/extension-points'
2
+
3
+ export const extensionPoints = defineModuleExtensionPoints({
4
+ moduleId: 'currencies',
5
+ hosts: {
6
+ currenciesTable: dataTableExtensionHost({ tableId: 'currencies.list', source: 'backend/currencies/page.tsx' }),
7
+ exchangeRatesTable: dataTableExtensionHost({ tableId: 'exchange-rates.list', source: 'backend/exchange-rates/page.tsx' }),
8
+ },
9
+ })
10
+
11
+ export default extensionPoints
@@ -1,6 +1,7 @@
1
1
  "use client"
2
2
 
3
3
  import * as React from 'react'
4
+ import { extensionPoints } from '@open-mercato/core/modules/customer_accounts/extension-points'
4
5
  import Link from 'next/link'
5
6
  import { useRouter } from 'next/navigation'
6
7
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
@@ -213,7 +214,7 @@ export default function CustomerRolesPage() {
213
214
  searchValue={search}
214
215
  onSearchChange={(value) => { setSearch(value); setPage(1) }}
215
216
  searchPlaceholder={t('customer_accounts.admin.roles.searchPlaceholder', 'Search roles...')}
216
- perspective={{ tableId: 'customer_accounts.admin.roles' }}
217
+ perspective={{ tableId: extensionPoints.hosts.rolesTable.tableId }}
217
218
  emptyState={(
218
219
  <ListEmptyState
219
220
  entityName={t('customer_accounts.admin.roles.title', 'Customer Roles')}
@@ -1,6 +1,7 @@
1
1
  "use client"
2
2
 
3
3
  import * as React from 'react'
4
+ import { extensionPoints } from '@open-mercato/core/modules/customer_accounts/extension-points'
4
5
  import Link from 'next/link'
5
6
  import { useRouter } from 'next/navigation'
6
7
  import { Globe, Settings } from 'lucide-react'
@@ -545,7 +546,7 @@ export default function CustomerAccountsPage() {
545
546
  filterValues={filterValues}
546
547
  onFiltersApply={handleFiltersApply}
547
548
  onFiltersClear={handleFiltersClear}
548
- perspective={{ tableId: 'customer_accounts.admin.users' }}
549
+ perspective={{ tableId: extensionPoints.hosts.usersTable.tableId }}
549
550
  emptyState={(
550
551
  <ListEmptyState
551
552
  entityName={t('customer_accounts.admin.title', 'Users')}
@@ -0,0 +1,11 @@
1
+ import { dataTableExtensionHost, defineModuleExtensionPoints } from '@open-mercato/shared/modules/widgets/extension-points'
2
+
3
+ export const extensionPoints = defineModuleExtensionPoints({
4
+ moduleId: 'customer_accounts',
5
+ hosts: {
6
+ usersTable: dataTableExtensionHost({ tableId: 'customer_accounts.admin.users', source: 'backend/customer_accounts/users/page.tsx' }),
7
+ rolesTable: dataTableExtensionHost({ tableId: 'customer_accounts.admin.roles', source: 'backend/customer_accounts/roles/page.tsx' }),
8
+ },
9
+ })
10
+
11
+ export default extensionPoints
@@ -1,6 +1,7 @@
1
1
  "use client"
2
2
 
3
3
  import * as React from 'react'
4
+ import { extensionPoints } from '@open-mercato/core/modules/customers/extension-points'
4
5
  import Link from 'next/link'
5
6
  import { useRouter, useSearchParams } from 'next/navigation'
6
7
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
@@ -967,7 +968,7 @@ export default function CustomerCompanyDetailPage({ params }: { params?: { id?:
967
968
  <h2 className="text-sm font-semibold">{t('customers.companies.detail.sections.details', 'Company details')}</h2>
968
969
  <DetailFieldsSection fields={detailFields} />
969
970
  <InjectionSpot
970
- spotId="customers.company.detail:details"
971
+ spotId={extensionPoints.hosts.companyLegacyDetails.spotId}
971
972
  context={injectionContext}
972
973
  data={data}
973
974
  onDataChange={(next) => setData(next as CompanyOverview)}
@@ -1,6 +1,7 @@
1
1
  "use client"
2
2
 
3
3
  import * as React from 'react'
4
+ import { extensionPoints } from '@open-mercato/core/modules/customers/extension-points'
4
5
  import Link from 'next/link'
5
6
  import { usePathname, useRouter, useSearchParams } from 'next/navigation'
6
7
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
@@ -888,7 +889,7 @@ export default function CustomersCompaniesPage() {
888
889
  searchPlaceholder={t('customers.companies.list.searchPlaceholder')}
889
890
  entityIds={[E.customers.customer_entity, E.customers.customer_company_profile]}
890
891
  onRowClick={(row) => router.push(`/backend/customers/companies-v2/${row.id}`)}
891
- perspective={{ tableId: 'customers.companies.list' }}
892
+ perspective={{ tableId: extensionPoints.hosts.companiesTable.tableId }}
892
893
  sortable
893
894
  manualSorting
894
895
  sorting={sorting}
@@ -1,6 +1,7 @@
1
1
  "use client"
2
2
 
3
3
  import * as React from 'react'
4
+ import { extensionPoints } from '@open-mercato/core/modules/customers/extension-points'
4
5
  import Link from 'next/link'
5
6
  import { useRouter, useSearchParams, usePathname } from 'next/navigation'
6
7
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
@@ -471,8 +472,8 @@ export default function CompanyDetailV2Page({ params }: { params?: { id?: string
471
472
  <PageBody>
472
473
  <div className="space-y-4">
473
474
  {/* UMES header injection */}
474
- <InjectionSpot spotId="detail:customers.company:header" context={injectionContext} data={data} />
475
- <InjectionSpot spotId="detail:customers.company:status-badges" context={injectionContext} data={data} />
475
+ <InjectionSpot spotId={extensionPoints.hosts.companyHeader.spotId} context={injectionContext} data={data} />
476
+ <InjectionSpot spotId={extensionPoints.hosts.companyStatusBadges.spotId} context={injectionContext} data={data} />
476
477
 
477
478
  {/* Persistent company header */}
478
479
  <CompanyDetailHeader
@@ -500,7 +501,7 @@ export default function CompanyDetailV2Page({ params }: { params?: { id?: string
500
501
  <CrudForm<CompanyEditFormValues>
501
502
  embedded
502
503
  trackDirtyWhenEmbedded
503
- injectionSpotId="crud-form:customers.company"
504
+ injectionSpotId={extensionPoints.hosts.companyForm.spotId}
504
505
  entityIds={[E.customers.customer_entity, E.customers.customer_company_profile]}
505
506
  schema={formSchema}
506
507
  fields={formFields}
@@ -606,7 +607,7 @@ export default function CompanyDetailV2Page({ params }: { params?: { id?: string
606
607
  />
607
608
 
608
609
  {/* UMES footer injection */}
609
- <InjectionSpot spotId="detail:customers.company:footer" context={injectionContext} data={data} />
610
+ <InjectionSpot spotId={extensionPoints.hosts.companyFooter.spotId} context={injectionContext} data={data} />
610
611
 
611
612
  {/* Schedule Activity Dialog */}
612
613
  <ScheduleActivityDialog
@@ -1,6 +1,7 @@
1
1
  "use client"
2
2
 
3
3
  import * as React from 'react'
4
+ import { extensionPoints } from '@open-mercato/core/modules/customers/extension-points'
4
5
  import Link from 'next/link'
5
6
  import { Building2, UserSearch, Users } from 'lucide-react'
6
7
  import { EmptyState } from '@open-mercato/ui/primitives/empty-state'
@@ -383,7 +384,7 @@ export default function DealDetailPage({ params }: { params?: { id?: string } })
383
384
  showAssociationsGroup={false}
384
385
  showVersionHistory={false}
385
386
  showCancelAction={false}
386
- injectionSpotId="crud-form:customers.deal"
387
+ injectionSpotId={extensionPoints.hosts.dealForm.spotId}
387
388
  optimisticLockUpdatedAt={data.deal.updatedAt}
388
389
  onDirtyChange={setIsDirty}
389
390
  initialPipelineOptions={formPipelineOptions}
@@ -610,7 +611,7 @@ export default function DealDetailPage({ params }: { params?: { id?: string } })
610
611
  <Page>
611
612
  <PageBody>
612
613
  <div className="space-y-6">
613
- <InjectionSpot spotId="detail:customers.deal:header" context={injectionContext} data={data} />
614
+ <InjectionSpot spotId={extensionPoints.hosts.dealHeader.spotId} context={injectionContext} data={data} />
614
615
 
615
616
  <DealDetailHeader
616
617
  deal={data.deal}
@@ -628,7 +629,7 @@ export default function DealDetailPage({ params }: { params?: { id?: string } })
628
629
  isSaving={isSaving}
629
630
  />
630
631
 
631
- <InjectionSpot spotId="detail:customers.deal:status-badges" context={injectionContext} data={data} />
632
+ <InjectionSpot spotId={extensionPoints.hosts.dealStatusBadges.spotId} context={injectionContext} data={data} />
632
633
 
633
634
  <PipelineStepper
634
635
  stages={data.pipelineStages}
@@ -663,7 +664,7 @@ export default function DealDetailPage({ params }: { params?: { id?: string } })
663
664
  />
664
665
  </DealDetailTabs>
665
666
 
666
- <InjectionSpot spotId="detail:customers.deal:footer" context={injectionContext} data={data} />
667
+ <InjectionSpot spotId={extensionPoints.hosts.dealFooter.spotId} context={injectionContext} data={data} />
667
668
  </div>
668
669
 
669
670
  {ConfirmDialogElement}
@@ -1,6 +1,7 @@
1
1
  "use client"
2
2
 
3
3
  import * as React from 'react'
4
+ import { extensionPoints } from '@open-mercato/core/modules/customers/extension-points'
4
5
  import Link from 'next/link'
5
6
  import { usePathname, useRouter, useSearchParams } from 'next/navigation'
6
7
  import { useQueryClient } from '@tanstack/react-query'
@@ -1146,7 +1147,7 @@ export default function CustomersDealsPage() {
1146
1147
  }}
1147
1148
  exporter={exportConfig}
1148
1149
  entityId={E.customers.customer_deal}
1149
- perspective={{ tableId: 'customers.deals.list' }}
1150
+ perspective={{ tableId: extensionPoints.hosts.dealsTable.tableId }}
1150
1151
  advancedFilter={{
1151
1152
  auto: true,
1152
1153
  value: filterPanel.tree,
@@ -1,6 +1,7 @@
1
1
  "use client"
2
2
 
3
3
  import * as React from 'react'
4
+ import { extensionPoints } from '@open-mercato/core/modules/customers/extension-points'
4
5
  import Link from 'next/link'
5
6
  import { useRouter, useSearchParams } from 'next/navigation'
6
7
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
@@ -922,7 +923,7 @@ export default function CustomerPersonDetailPage({ params }: { params?: { id?: s
922
923
  <h2 className="text-sm font-semibold">{t('customers.people.detail.sections.details')}</h2>
923
924
  <DetailFieldsSection fields={detailFields} />
924
925
  <InjectionSpot
925
- spotId="customers.person.detail:details"
926
+ spotId={extensionPoints.hosts.personLegacyDetails.spotId}
926
927
  context={injectionContext}
927
928
  data={data}
928
929
  onDataChange={(next) => setData(next as PersonOverview)}
@@ -1,6 +1,7 @@
1
1
  "use client"
2
2
 
3
3
  import * as React from 'react'
4
+ import { extensionPoints } from '@open-mercato/core/modules/customers/extension-points'
4
5
  import Link from 'next/link'
5
6
  import { usePathname, useRouter, useSearchParams } from 'next/navigation'
6
7
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
@@ -914,7 +915,7 @@ export default function CustomersPeoplePage() {
914
915
  onSearchChange={(value) => { setSearch(value); setPage(1) }}
915
916
  searchPlaceholder={t('customers.people.list.searchPlaceholder')}
916
917
  entityIds={[E.customers.customer_entity, E.customers.customer_person_profile]}
917
- perspective={{ tableId: 'customers.people.list' }}
918
+ perspective={{ tableId: extensionPoints.hosts.peopleTable.tableId }}
918
919
  onRowClick={(row) => router.push(`/backend/customers/people-v2/${row.id}`)}
919
920
  sortable
920
921
  manualSorting
@@ -1,6 +1,7 @@
1
1
  "use client"
2
2
 
3
3
  import * as React from 'react'
4
+ import { extensionPoints } from '@open-mercato/core/modules/customers/extension-points'
4
5
  import Link from 'next/link'
5
6
  import { useRouter, useSearchParams, usePathname } from 'next/navigation'
6
7
  import { User, Hash, Users, Building2 } from 'lucide-react'
@@ -502,8 +503,8 @@ export default function PersonDetailV2Page({ params }: { params?: { id?: string
502
503
  <PageBody>
503
504
  <div className="space-y-4">
504
505
  {/* UMES header injection (third-party extensions) */}
505
- <InjectionSpot spotId="detail:customers.person:header" context={injectionContext} data={data} />
506
- <InjectionSpot spotId="detail:customers.person:status-badges" context={injectionContext} data={data} />
506
+ <InjectionSpot spotId={extensionPoints.hosts.personHeader.spotId} context={injectionContext} data={data} />
507
+ <InjectionSpot spotId={extensionPoints.hosts.personStatusBadges.spotId} context={injectionContext} data={data} />
507
508
 
508
509
  {/* Persistent person header */}
509
510
  <PersonDetailHeader
@@ -537,7 +538,7 @@ export default function PersonDetailV2Page({ params }: { params?: { id?: string
537
538
  <CrudForm<PersonEditFormValues>
538
539
  embedded
539
540
  trackDirtyWhenEmbedded
540
- injectionSpotId="crud-form:customers.person"
541
+ injectionSpotId={extensionPoints.hosts.personForm.spotId}
541
542
  entityIds={[E.customers.customer_entity, E.customers.customer_person_profile]}
542
543
  schema={formSchema}
543
544
  fields={fields}
@@ -719,7 +720,7 @@ export default function PersonDetailV2Page({ params }: { params?: { id?: string
719
720
  })()}
720
721
 
721
722
  {/* UMES footer injection */}
722
- <InjectionSpot spotId="detail:customers.person:footer" context={injectionContext} data={data} />
723
+ <InjectionSpot spotId={extensionPoints.hosts.personFooter.spotId} context={injectionContext} data={data} />
723
724
 
724
725
  {/* Schedule Activity Dialog — opened from PlannedActivities "+ Schedule" or other triggers */}
725
726
  <ScheduleActivityDialog
@@ -1,6 +1,7 @@
1
1
  "use client"
2
2
 
3
3
  import * as React from 'react'
4
+ import { extensionPoints } from '@open-mercato/core/modules/customers/extension-points'
4
5
  import Link from 'next/link'
5
6
  import { useRouter } from 'next/navigation'
6
7
  import type { ColumnDef } from '@tanstack/react-table'
@@ -253,7 +254,7 @@ export function CustomerTodosTable(): React.JSX.Element {
253
254
  setSearch(value)
254
255
  setPage(1)
255
256
  }}
256
- perspective={{ tableId: 'customers.todos.list' }}
257
+ perspective={{ tableId: extensionPoints.hosts.todosTable.tableId }}
257
258
  rowActions={(row) => {
258
259
  const customerLink = buildCustomerHref(row)
259
260
  const todoHref = row.externalHref ?? resolveTodoHref(row.todoSource, row.todoId)
@@ -0,0 +1,40 @@
1
+ import {
2
+ crudFormExtensionHost,
3
+ dataTableExtensionHost,
4
+ defineModuleExtensionPoints,
5
+ injectionExtensionHost,
6
+ } from '@open-mercato/shared/modules/widgets/extension-points'
7
+
8
+ const detailHost = (spotId: string, source: string) => injectionExtensionHost({
9
+ family: 'detail',
10
+ spotId,
11
+ supported: ['render-widget'],
12
+ contextContract: 'customers.detail.v1',
13
+ source,
14
+ })
15
+
16
+ export const extensionPoints = defineModuleExtensionPoints({
17
+ moduleId: 'customers',
18
+ hosts: {
19
+ companiesTable: dataTableExtensionHost({ tableId: 'customers.companies.list', source: 'backend/customers/companies/page.tsx' }),
20
+ dealsTable: dataTableExtensionHost({ tableId: 'customers.deals.list', source: 'backend/customers/deals/page.tsx' }),
21
+ peopleTable: dataTableExtensionHost({ tableId: 'customers.people.list', source: 'backend/customers/people/page.tsx' }),
22
+ todosTable: dataTableExtensionHost({ tableId: 'customers.todos.list', source: 'components/CustomerTodosTable.tsx' }),
23
+ companyForm: crudFormExtensionHost({ entityId: 'customers.company', spotId: 'crud-form:customers.company', source: 'backend/customers/companies-v2/[id]/page.tsx' }),
24
+ dealForm: crudFormExtensionHost({ entityId: 'customers.deal', spotId: 'crud-form:customers.deal', source: 'backend/customers/deals/[id]/page.tsx' }),
25
+ personForm: crudFormExtensionHost({ entityId: 'customers.person', spotId: 'crud-form:customers.person', source: 'backend/customers/people-v2/[id]/page.tsx' }),
26
+ companyHeader: detailHost('detail:customers.company:header', 'backend/customers/companies-v2/[id]/page.tsx'),
27
+ companyStatusBadges: detailHost('detail:customers.company:status-badges', 'backend/customers/companies-v2/[id]/page.tsx'),
28
+ companyFooter: detailHost('detail:customers.company:footer', 'backend/customers/companies-v2/[id]/page.tsx'),
29
+ companyLegacyDetails: detailHost('customers.company.detail:details', 'backend/customers/companies/[id]/page.tsx'),
30
+ dealHeader: detailHost('detail:customers.deal:header', 'backend/customers/deals/[id]/page.tsx'),
31
+ dealStatusBadges: detailHost('detail:customers.deal:status-badges', 'backend/customers/deals/[id]/page.tsx'),
32
+ dealFooter: detailHost('detail:customers.deal:footer', 'backend/customers/deals/[id]/page.tsx'),
33
+ personHeader: detailHost('detail:customers.person:header', 'backend/customers/people-v2/[id]/page.tsx'),
34
+ personStatusBadges: detailHost('detail:customers.person:status-badges', 'backend/customers/people-v2/[id]/page.tsx'),
35
+ personFooter: detailHost('detail:customers.person:footer', 'backend/customers/people-v2/[id]/page.tsx'),
36
+ personLegacyDetails: detailHost('customers.person.detail:details', 'backend/customers/people/[id]/page.tsx'),
37
+ },
38
+ })
39
+
40
+ export default extensionPoints
@@ -2,6 +2,12 @@ const DATE_OPTIONS: Intl.DateTimeFormatOptions = { day: 'numeric', month: 'short
2
2
 
3
3
  const TIME_OPTIONS: Intl.DateTimeFormatOptions = { hour: 'numeric', minute: '2-digit' }
4
4
 
5
+ const NON_CANONICAL_INTL_SPACING = /[\u00a0\u2007\u2009\u202f]/g
6
+
7
+ function normalizeIntlSpacing(value: string): string {
8
+ return value.replace(NON_CANONICAL_INTL_SPACING, ' ')
9
+ }
10
+
5
11
  export function formatDateLabel(locale: string, date: Date): string {
6
12
  return new Intl.DateTimeFormat(locale, DATE_OPTIONS).format(date)
7
13
  }
@@ -9,17 +15,17 @@ export function formatDateLabel(locale: string, date: Date): string {
9
15
  export function formatDateRangeLabel(locale: string, from: Date, to: Date): string {
10
16
  const formatter = new Intl.DateTimeFormat(locale, DATE_OPTIONS)
11
17
  try {
12
- return formatter.formatRange(from, to)
18
+ return normalizeIntlSpacing(formatter.formatRange(from, to))
13
19
  } catch {
14
- return `${formatter.format(from)} – ${formatter.format(to)}`
20
+ return normalizeIntlSpacing(`${formatter.format(from)} – ${formatter.format(to)}`)
15
21
  }
16
22
  }
17
23
 
18
24
  export function formatTimeRangeLabel(locale: string, start: Date, end: Date): string {
19
25
  const formatter = new Intl.DateTimeFormat(locale, TIME_OPTIONS)
20
26
  try {
21
- return formatter.formatRange(start, end)
27
+ return normalizeIntlSpacing(formatter.formatRange(start, end))
22
28
  } catch {
23
- return `${formatter.format(start)} – ${formatter.format(end)}`
29
+ return normalizeIntlSpacing(`${formatter.format(start)} – ${formatter.format(end)}`)
24
30
  }
25
31
  }
@@ -1,5 +1,6 @@
1
1
  "use client"
2
2
  import * as React from 'react'
3
+ import { extensionPoints } from '@open-mercato/core/modules/data_sync/extension-points'
3
4
  import Link from 'next/link'
4
5
  import { useRouter } from 'next/navigation'
5
6
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
@@ -984,7 +985,7 @@ export default function SyncRunsDashboardPage() {
984
985
  searchValue={search}
985
986
  onSearchChange={(value) => { setSearch(value); setPage(1) }}
986
987
  searchPlaceholder={t('data_sync.dashboard.searchPlaceholder')}
987
- perspective={{ tableId: 'data_sync.runs' }}
988
+ perspective={{ tableId: extensionPoints.hosts.runsTable.tableId }}
988
989
  onRowClick={(row) => {
989
990
  router.push(`/backend/data-sync/runs/${encodeURIComponent(row.id)}`)
990
991
  }}
@@ -0,0 +1,10 @@
1
+ import { dataTableExtensionHost, defineModuleExtensionPoints } from '@open-mercato/shared/modules/widgets/extension-points'
2
+
3
+ export const extensionPoints = defineModuleExtensionPoints({
4
+ moduleId: 'data_sync',
5
+ hosts: {
6
+ runsTable: dataTableExtensionHost({ tableId: 'data_sync.runs', source: 'backend/data-sync/page.tsx' }),
7
+ },
8
+ })
9
+
10
+ export default extensionPoints
@@ -1,5 +1,6 @@
1
1
  "use client"
2
2
  import * as React from 'react'
3
+ import { extensionPoints } from '@open-mercato/core/modules/directory/extension-points'
3
4
  import Link from 'next/link'
4
5
  import { useQuery, useQueryClient } from '@tanstack/react-query'
5
6
  import type { ColumnDef } from '@tanstack/react-table'
@@ -271,7 +272,7 @@ export default function DirectoryOrganizationsPage() {
271
272
  setPage(1)
272
273
  }}
273
274
  sortable={false}
274
- perspective={{ tableId: 'directory.organizations.list' }}
275
+ perspective={{ tableId: extensionPoints.hosts.organizationsTable.tableId }}
275
276
  rowActions={(row) => (
276
277
  canManage ? (
277
278
  <RowActions
@@ -1,5 +1,6 @@
1
1
  "use client"
2
2
  import * as React from 'react'
3
+ import { extensionPoints } from '@open-mercato/core/modules/directory/extension-points'
3
4
  import Link from 'next/link'
4
5
  import { useQuery, useQueryClient } from '@tanstack/react-query'
5
6
  import type { ColumnDef, SortingState } from '@tanstack/react-table'
@@ -193,7 +194,7 @@ export default function DirectoryTenantsPage() {
193
194
  sortable
194
195
  sorting={sorting}
195
196
  onSortingChange={(state) => { setSorting(state); setPage(1) }}
196
- perspective={{ tableId: 'directory.tenants.list' }}
197
+ perspective={{ tableId: extensionPoints.hosts.tenantsTable.tableId }}
197
198
  rowActions={(row) => (
198
199
  canManage ? (
199
200
  <RowActions
@@ -0,0 +1,11 @@
1
+ import { dataTableExtensionHost, defineModuleExtensionPoints } from '@open-mercato/shared/modules/widgets/extension-points'
2
+
3
+ export const extensionPoints = defineModuleExtensionPoints({
4
+ moduleId: 'directory',
5
+ hosts: {
6
+ organizationsTable: dataTableExtensionHost({ tableId: 'directory.organizations.list', source: 'backend/directory/organizations/page.tsx' }),
7
+ tenantsTable: dataTableExtensionHost({ tableId: 'directory.tenants.list', source: 'backend/directory/tenants/page.tsx' }),
8
+ },
9
+ })
10
+
11
+ export default extensionPoints
@@ -1,5 +1,7 @@
1
1
  "use client"
2
2
  import * as React from 'react'
3
+ import { resolveExtensionPointPattern } from '@open-mercato/shared/modules/widgets/extension-points'
4
+ import { extensionPoints } from '@open-mercato/core/modules/entities/extension-points'
3
5
  import { useSearchParams } from 'next/navigation'
4
6
  import type { ColumnDef, SortingState } from '@tanstack/react-table'
5
7
  import { filterCustomFieldDefs, useCustomFieldDefs } from '@open-mercato/ui/backend/utils/customFieldDefs'
@@ -383,7 +385,9 @@ export RECORD_ID="<record uuid>"`}</code></pre>
383
385
  actions={actions}
384
386
  columns={columns}
385
387
  data={data}
386
- perspective={{ tableId: `entities.user.records.${entityId}` }}
388
+ perspective={{
389
+ tableId: resolveExtensionPointPattern(extensionPoints.hosts.userRecordsTable.pattern, { entityId }),
390
+ }}
387
391
  exporter={exportConfig}
388
392
  filters={baseFilters}
389
393
  filterValues={filterValues}
@@ -1,5 +1,6 @@
1
1
  "use client"
2
2
  import * as React from 'react'
3
+ import { extensionPoints } from '@open-mercato/core/modules/entities/extension-points'
3
4
  import { useQuery } from '@tanstack/react-query'
4
5
  import type { ColumnDef, SortingState } from '@tanstack/react-table'
5
6
  import { DataTable } from '@open-mercato/ui/backend/DataTable'
@@ -73,7 +74,7 @@ export default function SystemEntitiesTable() {
73
74
  sortable
74
75
  sorting={sorting}
75
76
  onSortingChange={setSorting}
76
- perspective={{ tableId: 'entities.system.list' }}
77
+ perspective={{ tableId: extensionPoints.hosts.systemEntitiesTable.tableId }}
77
78
  rowActions={(row) => (
78
79
  <RowActions
79
80
  items={[