@open-mercato/core 0.6.7-develop.6758.1.697eade236 → 0.6.7-develop.6768.1.9d2c4efc43

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 (269) 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 +5 -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/api/deals/summary/route.js +9 -115
  61. package/dist/modules/customers/api/deals/summary/route.js.map +2 -2
  62. package/dist/modules/customers/backend/customers/companies/[id]/page.js +2 -1
  63. package/dist/modules/customers/backend/customers/companies/[id]/page.js.map +2 -2
  64. package/dist/modules/customers/backend/customers/companies/page.js +2 -1
  65. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  66. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +5 -4
  67. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
  68. package/dist/modules/customers/backend/customers/deals/[id]/page.js +5 -4
  69. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  70. package/dist/modules/customers/backend/customers/deals/page.js +2 -1
  71. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  72. package/dist/modules/customers/backend/customers/people/[id]/page.js +2 -1
  73. package/dist/modules/customers/backend/customers/people/[id]/page.js.map +2 -2
  74. package/dist/modules/customers/backend/customers/people/page.js +2 -1
  75. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  76. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +5 -4
  77. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
  78. package/dist/modules/customers/components/CustomerTodosTable.js +2 -1
  79. package/dist/modules/customers/components/CustomerTodosTable.js.map +2 -2
  80. package/dist/modules/customers/extension-points.js +42 -0
  81. package/dist/modules/customers/extension-points.js.map +7 -0
  82. package/dist/modules/customers/lib/calendar/format.js +8 -4
  83. package/dist/modules/customers/lib/calendar/format.js.map +2 -2
  84. package/dist/modules/customers/lib/dealsSummaryQueries.js +144 -0
  85. package/dist/modules/customers/lib/dealsSummaryQueries.js.map +7 -0
  86. package/dist/modules/data_sync/backend/data-sync/page.js +2 -1
  87. package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
  88. package/dist/modules/data_sync/extension-points.js +13 -0
  89. package/dist/modules/data_sync/extension-points.js.map +7 -0
  90. package/dist/modules/directory/backend/directory/organizations/page.js +2 -1
  91. package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
  92. package/dist/modules/directory/backend/directory/tenants/page.js +2 -1
  93. package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
  94. package/dist/modules/directory/extension-points.js +14 -0
  95. package/dist/modules/directory/extension-points.js.map +7 -0
  96. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +5 -1
  97. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
  98. package/dist/modules/entities/components/SystemEntitiesTable.js +2 -1
  99. package/dist/modules/entities/components/SystemEntitiesTable.js.map +2 -2
  100. package/dist/modules/entities/components/UserEntitiesTable.js +2 -1
  101. package/dist/modules/entities/components/UserEntitiesTable.js.map +2 -2
  102. package/dist/modules/entities/extension-points.js +21 -0
  103. package/dist/modules/entities/extension-points.js.map +7 -0
  104. package/dist/modules/integrations/backend/integrations/[id]/page.js +2 -4
  105. package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
  106. package/dist/modules/integrations/extension-points.js +30 -0
  107. package/dist/modules/integrations/extension-points.js.map +7 -0
  108. package/dist/modules/messages/components/ComposeMessagePageClient.js +2 -1
  109. package/dist/modules/messages/components/ComposeMessagePageClient.js.map +2 -2
  110. package/dist/modules/messages/components/MessageDetailPageClient.js +3 -2
  111. package/dist/modules/messages/components/MessageDetailPageClient.js.map +2 -2
  112. package/dist/modules/messages/components/MessagesInboxPageClient.js +2 -1
  113. package/dist/modules/messages/components/MessagesInboxPageClient.js.map +2 -2
  114. package/dist/modules/messages/extension-points.js +16 -0
  115. package/dist/modules/messages/extension-points.js.map +7 -0
  116. package/dist/modules/payment_gateways/backend/payment-gateways/page.js +3 -2
  117. package/dist/modules/payment_gateways/backend/payment-gateways/page.js.map +2 -2
  118. package/dist/modules/payment_gateways/extension-points.js +19 -0
  119. package/dist/modules/payment_gateways/extension-points.js.map +7 -0
  120. package/dist/modules/portal/extension-points.js +32 -0
  121. package/dist/modules/portal/extension-points.js.map +7 -0
  122. package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js +3 -3
  123. package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js.map +2 -2
  124. package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js +3 -3
  125. package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js.map +2 -2
  126. package/dist/modules/portal/frontend/[orgSlug]/portal/page.js +3 -3
  127. package/dist/modules/portal/frontend/[orgSlug]/portal/page.js.map +2 -2
  128. package/dist/modules/portal/frontend/[orgSlug]/portal/profile/page.js +3 -3
  129. package/dist/modules/portal/frontend/[orgSlug]/portal/profile/page.js.map +2 -2
  130. package/dist/modules/portal/frontend/[orgSlug]/portal/reset-password/page.js +3 -3
  131. package/dist/modules/portal/frontend/[orgSlug]/portal/reset-password/page.js.map +2 -2
  132. package/dist/modules/portal/frontend/[orgSlug]/portal/signup/page.js +3 -3
  133. package/dist/modules/portal/frontend/[orgSlug]/portal/signup/page.js.map +2 -2
  134. package/dist/modules/query_index/components/QueryIndexesTable.js +2 -1
  135. package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
  136. package/dist/modules/query_index/events.js +20 -0
  137. package/dist/modules/query_index/events.js.map +7 -0
  138. package/dist/modules/query_index/extension-points.js +13 -0
  139. package/dist/modules/query_index/extension-points.js.map +7 -0
  140. package/dist/modules/resources/backend/resources/resource-types/page.js +2 -1
  141. package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
  142. package/dist/modules/resources/backend/resources/resources/page.js +2 -1
  143. package/dist/modules/resources/backend/resources/resources/page.js.map +2 -2
  144. package/dist/modules/resources/extension-points.js +14 -0
  145. package/dist/modules/resources/extension-points.js.map +7 -0
  146. package/dist/modules/sales/backend/sales/documents/[id]/page.js +11 -3
  147. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  148. package/dist/modules/sales/components/documents/ItemsSection.js +2 -1
  149. package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
  150. package/dist/modules/sales/components/documents/SalesDocumentsTable.js +4 -1
  151. package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
  152. package/dist/modules/sales/events.js +1 -0
  153. package/dist/modules/sales/events.js.map +2 -2
  154. package/dist/modules/sales/extension-points.js +36 -0
  155. package/dist/modules/sales/extension-points.js.map +7 -0
  156. package/dist/modules/wms/components/backend/WmsConfigurationPage.js +5 -4
  157. package/dist/modules/wms/components/backend/WmsConfigurationPage.js.map +2 -2
  158. package/dist/modules/wms/components/backend/WmsInventoryConsolePage.js +4 -3
  159. package/dist/modules/wms/components/backend/WmsInventoryConsolePage.js.map +2 -2
  160. package/dist/modules/wms/components/backend/WmsLocationDetailPage.js +3 -2
  161. package/dist/modules/wms/components/backend/WmsLocationDetailPage.js.map +2 -2
  162. package/dist/modules/wms/components/backend/WmsLotDetailPage.js +3 -2
  163. package/dist/modules/wms/components/backend/WmsLotDetailPage.js.map +2 -2
  164. package/dist/modules/wms/components/backend/WmsLotsListPage.js +2 -1
  165. package/dist/modules/wms/components/backend/WmsLotsListPage.js.map +2 -2
  166. package/dist/modules/wms/components/backend/WmsSkuDetailPage.js +3 -2
  167. package/dist/modules/wms/components/backend/WmsSkuDetailPage.js.map +2 -2
  168. package/dist/modules/wms/extension-points.js +26 -0
  169. package/dist/modules/wms/extension-points.js.map +7 -0
  170. package/dist/modules/workflows/backend/definitions/page.js +2 -1
  171. package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
  172. package/dist/modules/workflows/backend/instances/page.js +2 -1
  173. package/dist/modules/workflows/backend/instances/page.js.map +2 -2
  174. package/dist/modules/workflows/backend/tasks/page.js +2 -1
  175. package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
  176. package/dist/modules/workflows/events.js +1 -0
  177. package/dist/modules/workflows/events.js.map +2 -2
  178. package/dist/modules/workflows/extension-points.js +15 -0
  179. package/dist/modules/workflows/extension-points.js.map +7 -0
  180. package/package.json +7 -7
  181. package/src/modules/api_keys/backend/api-keys/page.tsx +2 -1
  182. package/src/modules/api_keys/extension-points.ts +13 -0
  183. package/src/modules/audit_logs/components/AccessLogsTable.tsx +2 -1
  184. package/src/modules/audit_logs/components/AuditLogsActions.tsx +2 -1
  185. package/src/modules/audit_logs/extension-points.ts +11 -0
  186. package/src/modules/auth/backend/roles/page.tsx +2 -1
  187. package/src/modules/auth/backend/users/page.tsx +2 -1
  188. package/src/modules/auth/extension-points.ts +18 -0
  189. package/src/modules/auth/frontend/login.tsx +5 -1
  190. package/src/modules/business_rules/backend/rules/page.tsx +2 -1
  191. package/src/modules/business_rules/events.ts +10 -0
  192. package/src/modules/business_rules/extension-points.ts +10 -0
  193. package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +2 -1
  194. package/src/modules/catalog/backend/catalog/products/create/page.tsx +2 -1
  195. package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +2 -1
  196. package/src/modules/catalog/components/products/ProductsDataTable.tsx +3 -2
  197. package/src/modules/catalog/events.ts +1 -0
  198. package/src/modules/catalog/extension-points.ts +24 -0
  199. package/src/modules/communication_channels/backend/communication_channels/channels/page.tsx +2 -1
  200. package/src/modules/communication_channels/backend/profile/communication-channels/page.tsx +3 -2
  201. package/src/modules/communication_channels/extension-points.ts +17 -0
  202. package/src/modules/configs/backend/config/system-status/page.tsx +2 -1
  203. package/src/modules/configs/extension-points.ts +15 -0
  204. package/src/modules/currencies/backend/currencies/page.tsx +2 -1
  205. package/src/modules/currencies/backend/exchange-rates/page.tsx +2 -1
  206. package/src/modules/currencies/extension-points.ts +11 -0
  207. package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +2 -1
  208. package/src/modules/customer_accounts/backend/customer_accounts/users/page.tsx +2 -1
  209. package/src/modules/customer_accounts/extension-points.ts +11 -0
  210. package/src/modules/customers/api/deals/summary/route.ts +10 -158
  211. package/src/modules/customers/backend/customers/companies/[id]/page.tsx +2 -1
  212. package/src/modules/customers/backend/customers/companies/page.tsx +2 -1
  213. package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +5 -4
  214. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +5 -4
  215. package/src/modules/customers/backend/customers/deals/page.tsx +2 -1
  216. package/src/modules/customers/backend/customers/people/[id]/page.tsx +2 -1
  217. package/src/modules/customers/backend/customers/people/page.tsx +2 -1
  218. package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +5 -4
  219. package/src/modules/customers/components/CustomerTodosTable.tsx +2 -1
  220. package/src/modules/customers/extension-points.ts +40 -0
  221. package/src/modules/customers/lib/calendar/format.ts +10 -4
  222. package/src/modules/customers/lib/dealsSummaryQueries.ts +211 -0
  223. package/src/modules/data_sync/backend/data-sync/page.tsx +2 -1
  224. package/src/modules/data_sync/extension-points.ts +10 -0
  225. package/src/modules/directory/backend/directory/organizations/page.tsx +2 -1
  226. package/src/modules/directory/backend/directory/tenants/page.tsx +2 -1
  227. package/src/modules/directory/extension-points.ts +11 -0
  228. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +5 -1
  229. package/src/modules/entities/components/SystemEntitiesTable.tsx +2 -1
  230. package/src/modules/entities/components/UserEntitiesTable.tsx +2 -1
  231. package/src/modules/entities/extension-points.ts +18 -0
  232. package/src/modules/integrations/backend/integrations/[id]/page.tsx +2 -2
  233. package/src/modules/integrations/extension-points.ts +27 -0
  234. package/src/modules/messages/components/ComposeMessagePageClient.tsx +2 -1
  235. package/src/modules/messages/components/MessageDetailPageClient.tsx +3 -2
  236. package/src/modules/messages/components/MessagesInboxPageClient.tsx +2 -1
  237. package/src/modules/messages/extension-points.ts +13 -0
  238. package/src/modules/payment_gateways/backend/payment-gateways/page.tsx +3 -2
  239. package/src/modules/payment_gateways/extension-points.ts +16 -0
  240. package/src/modules/portal/extension-points.ts +30 -0
  241. package/src/modules/portal/frontend/[orgSlug]/portal/dashboard/page.tsx +3 -3
  242. package/src/modules/portal/frontend/[orgSlug]/portal/login/page.tsx +3 -3
  243. package/src/modules/portal/frontend/[orgSlug]/portal/page.tsx +3 -3
  244. package/src/modules/portal/frontend/[orgSlug]/portal/profile/page.tsx +3 -3
  245. package/src/modules/portal/frontend/[orgSlug]/portal/reset-password/page.tsx +4 -4
  246. package/src/modules/portal/frontend/[orgSlug]/portal/signup/page.tsx +3 -3
  247. package/src/modules/query_index/components/QueryIndexesTable.tsx +2 -1
  248. package/src/modules/query_index/events.ts +17 -0
  249. package/src/modules/query_index/extension-points.ts +10 -0
  250. package/src/modules/resources/backend/resources/resource-types/page.tsx +2 -1
  251. package/src/modules/resources/backend/resources/resources/page.tsx +2 -1
  252. package/src/modules/resources/extension-points.ts +11 -0
  253. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +11 -3
  254. package/src/modules/sales/components/documents/ItemsSection.tsx +2 -1
  255. package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +6 -1
  256. package/src/modules/sales/events.ts +1 -0
  257. package/src/modules/sales/extension-points.ts +33 -0
  258. package/src/modules/wms/components/backend/WmsConfigurationPage.tsx +5 -4
  259. package/src/modules/wms/components/backend/WmsInventoryConsolePage.tsx +4 -3
  260. package/src/modules/wms/components/backend/WmsLocationDetailPage.tsx +3 -2
  261. package/src/modules/wms/components/backend/WmsLotDetailPage.tsx +3 -2
  262. package/src/modules/wms/components/backend/WmsLotsListPage.tsx +2 -1
  263. package/src/modules/wms/components/backend/WmsSkuDetailPage.tsx +3 -2
  264. package/src/modules/wms/extension-points.ts +23 -0
  265. package/src/modules/workflows/backend/definitions/page.tsx +2 -1
  266. package/src/modules/workflows/backend/instances/page.tsx +2 -1
  267. package/src/modules/workflows/backend/tasks/page.tsx +2 -1
  268. package/src/modules/workflows/events.ts +1 -0
  269. package/src/modules/workflows/extension-points.ts +12 -0
@@ -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
  }
@@ -0,0 +1,211 @@
1
+ import type { EntityManager as CoreEntityManager } from '@mikro-orm/core'
2
+ import type { EntityManager as PgEntityManager } from '@mikro-orm/postgresql'
3
+ import { fetchStuckDealIds } from './stuckDeals'
4
+
5
+ type DateWindow = {
6
+ start: Date
7
+ end: Date
8
+ }
9
+
10
+ export type OpenPipelineRow = {
11
+ stage: string | null
12
+ currency: string | null
13
+ total: string | number | null
14
+ count: string | number
15
+ owner_user_id: string | null
16
+ }
17
+
18
+ export type WindowSumRow = {
19
+ currency: string | null
20
+ current_total: string | number | null
21
+ current_count: string | number
22
+ previous_total: string | number | null
23
+ previous_count: string | number
24
+ }
25
+
26
+ export type WinLossRow = {
27
+ current_won: string | number
28
+ current_lost: string | number
29
+ previous_won: string | number
30
+ previous_lost: string | number
31
+ }
32
+
33
+ export type WinRateMonthRow = {
34
+ period: string
35
+ won: string | number
36
+ lost: string | number
37
+ }
38
+
39
+ export type DealsSummaryQueryRows = {
40
+ openRows: OpenPipelineRow[]
41
+ inflowRows: WindowSumRow[]
42
+ wonRows: WindowSumRow[]
43
+ winLossRows: WinLossRow[]
44
+ seriesRows: WinRateMonthRow[]
45
+ overdueRows: Array<{ id: string }>
46
+ openStuckIds: string[]
47
+ }
48
+
49
+ export type DealsSummaryQueryInput = {
50
+ em: CoreEntityManager
51
+ tenantId: string
52
+ organizationIds: string[]
53
+ currentQuarter: DateWindow
54
+ previousQuarter: DateWindow
55
+ seriesStart: Date
56
+ }
57
+
58
+ type SqlPredicate = {
59
+ clause: string
60
+ values: string[]
61
+ }
62
+
63
+ export function buildOpenDealPredicate(statuses: readonly string[]): SqlPredicate {
64
+ if (statuses.length === 0) {
65
+ throw new Error('[internal] Open-deal predicate requires at least one status')
66
+ }
67
+
68
+ const placeholders = statuses.map(() => '?').join(',')
69
+ return {
70
+ clause: `status IN (${placeholders}) AND closure_outcome IS NULL`,
71
+ values: [...statuses],
72
+ }
73
+ }
74
+
75
+ export async function loadDealsSummaryQueryRows(input: DealsSummaryQueryInput): Promise<DealsSummaryQueryRows> {
76
+ if (input.organizationIds.length === 0) {
77
+ throw new Error('[internal] Deals summary query requires an organization scope')
78
+ }
79
+
80
+ const connection = input.em.getConnection()
81
+ const organizationPlaceholders = input.organizationIds.map(() => '?').join(',')
82
+ const scopeWhere = `tenant_id = ? AND organization_id IN (${organizationPlaceholders}) AND deleted_at IS NULL`
83
+ const scopeValues: Array<string | number | null> = [input.tenantId, ...input.organizationIds]
84
+ const openDealsPredicate = buildOpenDealPredicate(['open', 'in_progress'])
85
+
86
+ const openRows = await connection.execute<OpenPipelineRow[]>(
87
+ `SELECT
88
+ pipeline_stage AS stage,
89
+ UPPER(COALESCE(value_currency, '')) AS currency,
90
+ COALESCE(SUM(value_amount), 0) AS total,
91
+ COUNT(*) AS count,
92
+ owner_user_id
93
+ FROM customer_deals
94
+ WHERE ${scopeWhere} AND ${openDealsPredicate.clause}
95
+ GROUP BY pipeline_stage, UPPER(COALESCE(value_currency, '')), owner_user_id`,
96
+ [...scopeValues, ...openDealsPredicate.values],
97
+ )
98
+
99
+ const inflowRows = await connection.execute<WindowSumRow[]>(
100
+ `SELECT
101
+ UPPER(COALESCE(value_currency, '')) AS currency,
102
+ COALESCE(SUM(value_amount) FILTER (WHERE created_at >= ? AND created_at < ?), 0) AS current_total,
103
+ COUNT(*) FILTER (WHERE created_at >= ? AND created_at < ?) AS current_count,
104
+ COALESCE(SUM(value_amount) FILTER (WHERE created_at >= ? AND created_at < ?), 0) AS previous_total,
105
+ COUNT(*) FILTER (WHERE created_at >= ? AND created_at < ?) AS previous_count
106
+ FROM customer_deals
107
+ WHERE ${scopeWhere} AND ${openDealsPredicate.clause}
108
+ GROUP BY UPPER(COALESCE(value_currency, ''))`,
109
+ [
110
+ input.currentQuarter.start.toISOString(),
111
+ input.currentQuarter.end.toISOString(),
112
+ input.currentQuarter.start.toISOString(),
113
+ input.currentQuarter.end.toISOString(),
114
+ input.previousQuarter.start.toISOString(),
115
+ input.previousQuarter.end.toISOString(),
116
+ input.previousQuarter.start.toISOString(),
117
+ input.previousQuarter.end.toISOString(),
118
+ ...scopeValues,
119
+ ...openDealsPredicate.values,
120
+ ],
121
+ )
122
+
123
+ const wonRows = await connection.execute<WindowSumRow[]>(
124
+ `SELECT
125
+ UPPER(COALESCE(value_currency, '')) AS currency,
126
+ COALESCE(SUM(value_amount) FILTER (WHERE updated_at >= ? AND updated_at < ?), 0) AS current_total,
127
+ COUNT(*) FILTER (WHERE updated_at >= ? AND updated_at < ?) AS current_count,
128
+ COALESCE(SUM(value_amount) FILTER (WHERE updated_at >= ? AND updated_at < ?), 0) AS previous_total,
129
+ COUNT(*) FILTER (WHERE updated_at >= ? AND updated_at < ?) AS previous_count
130
+ FROM customer_deals
131
+ WHERE ${scopeWhere} AND (status = 'win' OR closure_outcome = 'won')
132
+ GROUP BY UPPER(COALESCE(value_currency, ''))`,
133
+ [
134
+ input.currentQuarter.start.toISOString(),
135
+ input.currentQuarter.end.toISOString(),
136
+ input.currentQuarter.start.toISOString(),
137
+ input.currentQuarter.end.toISOString(),
138
+ input.previousQuarter.start.toISOString(),
139
+ input.previousQuarter.end.toISOString(),
140
+ input.previousQuarter.start.toISOString(),
141
+ input.previousQuarter.end.toISOString(),
142
+ ...scopeValues,
143
+ ],
144
+ )
145
+
146
+ const winLossRows = await connection.execute<WinLossRow[]>(
147
+ `SELECT
148
+ COUNT(*) FILTER (WHERE (status = 'win' OR closure_outcome = 'won') AND updated_at >= ? AND updated_at < ?) AS current_won,
149
+ COUNT(*) FILTER (WHERE (status = 'loose' OR closure_outcome = 'lost') AND updated_at >= ? AND updated_at < ?) AS current_lost,
150
+ COUNT(*) FILTER (WHERE (status = 'win' OR closure_outcome = 'won') AND updated_at >= ? AND updated_at < ?) AS previous_won,
151
+ COUNT(*) FILTER (WHERE (status = 'loose' OR closure_outcome = 'lost') AND updated_at >= ? AND updated_at < ?) AS previous_lost
152
+ FROM customer_deals
153
+ WHERE ${scopeWhere}`,
154
+ [
155
+ input.currentQuarter.start.toISOString(),
156
+ input.currentQuarter.end.toISOString(),
157
+ input.currentQuarter.start.toISOString(),
158
+ input.currentQuarter.end.toISOString(),
159
+ input.previousQuarter.start.toISOString(),
160
+ input.previousQuarter.end.toISOString(),
161
+ input.previousQuarter.start.toISOString(),
162
+ input.previousQuarter.end.toISOString(),
163
+ ...scopeValues,
164
+ ],
165
+ )
166
+
167
+ const seriesRows = await connection.execute<WinRateMonthRow[]>(
168
+ `SELECT
169
+ to_char(date_trunc('month', updated_at AT TIME ZONE 'UTC'), 'YYYY-MM') AS period,
170
+ COUNT(*) FILTER (WHERE status = 'win' OR closure_outcome = 'won') AS won,
171
+ COUNT(*) FILTER (WHERE status = 'loose' OR closure_outcome = 'lost') AS lost
172
+ FROM customer_deals
173
+ WHERE ${scopeWhere} AND updated_at >= ?
174
+ GROUP BY 1`,
175
+ [...scopeValues, input.seriesStart.toISOString()],
176
+ )
177
+
178
+ const overdueDealsPredicate = buildOpenDealPredicate(['open'])
179
+ const overdueRows = await connection.execute<Array<{ id: string }>>(
180
+ `SELECT id FROM customer_deals
181
+ WHERE ${scopeWhere} AND ${overdueDealsPredicate.clause} AND expected_close_at IS NOT NULL AND expected_close_at < CURRENT_DATE`,
182
+ [...scopeValues, ...overdueDealsPredicate.values],
183
+ )
184
+
185
+ const stuckIdLists = await Promise.all(
186
+ input.organizationIds.map((organizationId) =>
187
+ fetchStuckDealIds(input.em as unknown as PgEntityManager, organizationId, input.tenantId),
188
+ ),
189
+ )
190
+ const stuckIdValues = Array.from(new Set(stuckIdLists.flat()))
191
+ let openStuckIds: string[] = []
192
+ if (stuckIdValues.length > 0) {
193
+ const stuckPlaceholders = stuckIdValues.map(() => '?').join(',')
194
+ const openStuckRows: Array<{ id: string }> = await connection.execute<Array<{ id: string }>>(
195
+ `SELECT id FROM customer_deals
196
+ WHERE ${scopeWhere} AND ${openDealsPredicate.clause} AND id IN (${stuckPlaceholders})`,
197
+ [...scopeValues, ...openDealsPredicate.values, ...stuckIdValues],
198
+ )
199
+ openStuckIds = openStuckRows.map((row) => row.id)
200
+ }
201
+
202
+ return {
203
+ openRows,
204
+ inflowRows,
205
+ wonRows,
206
+ winLossRows,
207
+ seriesRows,
208
+ overdueRows,
209
+ openStuckIds,
210
+ }
211
+ }
@@ -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={[
@@ -1,6 +1,7 @@
1
1
 
2
2
  "use client"
3
3
  import * as React from 'react'
4
+ import { extensionPoints } from '@open-mercato/core/modules/entities/extension-points'
4
5
  import Link from 'next/link'
5
6
  import { useQuery } from '@tanstack/react-query'
6
7
  import type { ColumnDef, SortingState } from '@tanstack/react-table'
@@ -96,7 +97,7 @@ export default function UserEntitiesTable() {
96
97
  sortable
97
98
  sorting={sorting}
98
99
  onSortingChange={setSorting}
99
- perspective={{ tableId: 'entities.user.list' }}
100
+ perspective={{ tableId: extensionPoints.hosts.userEntitiesTable.tableId }}
100
101
  emptyState={(
101
102
  <ListEmptyState
102
103
  entityName={t('entities.user.table.title', 'User Entities')}
@@ -0,0 +1,18 @@
1
+ import { dataTableExtensionHost, defineModuleExtensionPoints, injectionExtensionHost } from '@open-mercato/shared/modules/widgets/extension-points'
2
+
3
+ export const extensionPoints = defineModuleExtensionPoints({
4
+ moduleId: 'entities',
5
+ hosts: {
6
+ systemEntitiesTable: dataTableExtensionHost({ tableId: 'entities.system.list', source: 'components/SystemEntitiesTable.tsx' }),
7
+ userEntitiesTable: dataTableExtensionHost({ tableId: 'entities.user.list', source: 'components/UserEntitiesTable.tsx' }),
8
+ userRecordsTable: injectionExtensionHost({
9
+ family: 'entity',
10
+ pattern: 'data-table:entities.user.records.{entityId}',
11
+ parameters: { entityId: { source: 'route.params.entityId' } },
12
+ supported: ['render-widget', 'headless-widget', 'column-widget', 'row-action', 'bulk-action', 'filter-widget'],
13
+ source: 'backend/entities/user/[entityId]/records/page.tsx',
14
+ }),
15
+ },
16
+ })
17
+
18
+ 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/integrations/extension-points'
3
4
  import { usePathname, useRouter, useSearchParams } from 'next/navigation'
4
5
  import { z } from 'zod'
5
6
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
@@ -31,7 +32,6 @@ import { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'
31
32
  import { useT } from '@open-mercato/shared/lib/i18n/context'
32
33
  import { cn } from '@open-mercato/shared/lib/utils'
33
34
  import {
34
- LEGACY_INTEGRATION_DETAIL_TABS_SPOT_ID,
35
35
  type CredentialFieldType,
36
36
  type IntegrationCredentialField,
37
37
  type IntegrationDetailBuiltInTab,
@@ -530,7 +530,7 @@ export default function IntegrationDetailPage({ params }: IntegrationDetailPageP
530
530
  }, [logLevel, resolveCurrentIntegrationId])
531
531
 
532
532
  const detailWidgetSpotId = React.useMemo(
533
- () => resolveIntegrationDetailWidgetSpotId(detail?.integration ?? null, LEGACY_INTEGRATION_DETAIL_TABS_SPOT_ID),
533
+ () => resolveIntegrationDetailWidgetSpotId(detail?.integration ?? null, extensionPoints.hosts.legacyDetailTabs.spotId),
534
534
  [detail?.integration],
535
535
  )
536
536
  const mutationContextId = React.useMemo(
@@ -0,0 +1,27 @@
1
+ import { defineModuleExtensionPoints, injectionExtensionHost } from '@open-mercato/shared/modules/widgets/extension-points'
2
+
3
+ export const extensionPoints = defineModuleExtensionPoints({
4
+ moduleId: 'integrations',
5
+ hosts: {
6
+ detail: injectionExtensionHost({
7
+ family: 'integration',
8
+ pattern: 'integrations.detail:{integrationId}',
9
+ parameters: {
10
+ integrationId: { source: 'IntegrationDefinition.id', pattern: '^[a-z0-9_]+$' },
11
+ },
12
+ fallbacks: ['integrations.detail:tabs'],
13
+ supported: ['render-widget'],
14
+ contextContract: 'integrations.detail.v1',
15
+ source: 'backend/integrations/[id]/page.tsx',
16
+ }),
17
+ legacyDetailTabs: injectionExtensionHost({
18
+ family: 'integration',
19
+ spotId: 'integrations.detail:tabs',
20
+ supported: ['render-widget'],
21
+ contextContract: 'integrations.detail.v1',
22
+ source: 'backend/integrations/[id]/page.tsx',
23
+ }),
24
+ },
25
+ })
26
+
27
+ export default extensionPoints
@@ -1,6 +1,7 @@
1
1
  "use client"
2
2
 
3
3
  import { useRouter } from 'next/navigation'
4
+ import { extensionPoints } from '@open-mercato/core/modules/messages/extension-points'
4
5
  import { MessageComposer } from '@open-mercato/ui/backend/messages'
5
6
  // UMES extension surface — compose page injection spot (SPEC-045d §9.3a).
6
7
  // Channel provider packages inject "composer capabilities" widgets here
@@ -21,7 +22,7 @@ export function ComposeMessagePageClient() {
21
22
  scoping) into this spot purely as additional UI siblings.
22
23
  */}
23
24
  <InjectionSpot
24
- spotId="crud-form:messages:message:fields"
25
+ spotId={extensionPoints.hosts.composeFields.spotId}
25
26
  context={{ form: 'compose' }}
26
27
  data={{}}
27
28
  />
@@ -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/messages/extension-points'
4
5
  import { MessageComposer } from '@open-mercato/ui/backend/messages'
5
6
  import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
6
7
  import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
@@ -94,7 +95,7 @@ function MessageConversationDetailItem({
94
95
 
95
96
  {/* UMES — channel payload renderer mounts here (channel-linked emails, Slack blocks, etc.). */}
96
97
  <InjectionSpot
97
- spotId="detail:messages:message:body:after"
98
+ spotId={extensionPoints.hosts.detailBodyAfter.spotId}
98
99
  context={{ messageId }}
99
100
  data={state.detail}
100
101
  />
@@ -105,7 +106,7 @@ function MessageConversationDetailItem({
105
106
  `userFeatures` lets feature-gated widgets (e.g. the channel reassignment editor,
106
107
  gated on `communication_channels.assign`) render their controls for permitted users. */}
107
108
  <InjectionSpot
108
- spotId="detail:messages:message:sidebar"
109
+ spotId={extensionPoints.hosts.detailSidebar.spotId}
109
110
  context={{ messageId, userFeatures }}
110
111
  data={state.detail}
111
112
  />
@@ -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/messages/extension-points'
4
5
  import Link from 'next/link'
5
6
  import { useRouter } from 'next/navigation'
6
7
  import { useQuery, useQueryClient } from '@tanstack/react-query'
@@ -369,7 +370,7 @@ export function MessagesInboxPageClient() {
369
370
  // UMES extension surface — opt into widget injection at:
370
371
  // data-table:messages:columns / :row-actions / :bulk-actions / :filters / :toolbar / :search-trailing
371
372
  // (SPEC-045d §9.3a — communication_channels hub renders channel badge + delivery status here)
372
- extensionTableId="messages"
373
+ extensionTableId={extensionPoints.hosts.inboxTable.tableId}
373
374
  columns={columns}
374
375
  data={rows}
375
376
  bulkActions={bulkActions}
@@ -0,0 +1,13 @@
1
+ import { dataTableExtensionHost, defineModuleExtensionPoints, injectionExtensionHost } from '@open-mercato/shared/modules/widgets/extension-points'
2
+
3
+ export const extensionPoints = defineModuleExtensionPoints({
4
+ moduleId: 'messages',
5
+ hosts: {
6
+ inboxTable: dataTableExtensionHost({ tableId: 'messages', source: 'components/MessagesInboxPageClient.tsx' }),
7
+ composeFields: injectionExtensionHost({ family: 'generic', spotId: 'crud-form:messages:message:fields', supported: ['render-widget'], source: 'components/ComposeMessagePageClient.tsx' }),
8
+ detailBodyAfter: injectionExtensionHost({ family: 'detail', spotId: 'detail:messages:message:body:after', supported: ['render-widget'], source: 'components/MessageDetailPageClient.tsx' }),
9
+ detailSidebar: injectionExtensionHost({ family: 'detail', spotId: 'detail:messages:message:sidebar', supported: ['render-widget'], source: 'components/MessageDetailPageClient.tsx' }),
10
+ },
11
+ })
12
+
13
+ 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/payment_gateways/extension-points'
3
4
  import { useSearchParams } from 'next/navigation'
4
5
  import type { ColumnDef } from '@tanstack/react-table'
5
6
  import type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'
@@ -472,7 +473,7 @@ export default function PaymentTransactionsPage() {
472
473
  searchValue={search}
473
474
  onSearchChange={(value) => { setSearch(value); setPage(1) }}
474
475
  searchPlaceholder={t('payment_gateways.transactions.searchPlaceholder', 'Search by payment, transaction, session, or gateway id')}
475
- perspective={{ tableId: 'payment_gateways.transactions.list' }}
476
+ perspective={{ tableId: extensionPoints.hosts.transactionsTable.tableId }}
476
477
  pagination={{ page, pageSize: 20, total, totalPages, onPageChange: setPage }}
477
478
  isLoading={isLoading}
478
479
  onRowClick={(row) => setSelectedId((current) => current === row.id ? null : row.id)}
@@ -691,7 +692,7 @@ export default function PaymentTransactionsPage() {
691
692
  className="p-4"
692
693
  />
693
694
  <InjectionSpot
694
- spotId="admin.page:payment-gateways/transactions:after"
695
+ spotId={extensionPoints.hosts.transactionsAfter.spotId}
695
696
  context={{ selectedPaymentId: detail.transaction.paymentId }}
696
697
  />
697
698
  </div>