@open-mercato/core 0.6.1-develop.3260.1.dc719cac84 → 0.6.1-develop.3278.1.a4d26475e4

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 (212) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/domain_mapping/index.js +31 -0
  3. package/dist/generated/entities/domain_mapping/index.js.map +7 -0
  4. package/dist/generated/entities.ids.generated.js +2 -1
  5. package/dist/generated/entities.ids.generated.js.map +2 -2
  6. package/dist/generated/entity-fields-registry.js +16 -0
  7. package/dist/generated/entity-fields-registry.js.map +2 -2
  8. package/dist/helpers/integration/salesUi.js +18 -17
  9. package/dist/helpers/integration/salesUi.js.map +2 -2
  10. package/dist/modules/customer_accounts/acl.js +2 -1
  11. package/dist/modules/customer_accounts/acl.js.map +2 -2
  12. package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/health-check.js +110 -0
  13. package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/health-check.js.map +7 -0
  14. package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/verify.js +114 -0
  15. package/dist/modules/customer_accounts/api/admin/domain-mappings/[id]/verify.js.map +7 -0
  16. package/dist/modules/customer_accounts/api/admin/domain-mappings.js +271 -0
  17. package/dist/modules/customer_accounts/api/admin/domain-mappings.js.map +7 -0
  18. package/dist/modules/customer_accounts/api/domain-check.js +70 -0
  19. package/dist/modules/customer_accounts/api/domain-check.js.map +7 -0
  20. package/dist/modules/customer_accounts/api/domain-resolve/all.js +128 -0
  21. package/dist/modules/customer_accounts/api/domain-resolve/all.js.map +7 -0
  22. package/dist/modules/customer_accounts/api/domain-resolve.js +84 -0
  23. package/dist/modules/customer_accounts/api/domain-resolve.js.map +7 -0
  24. package/dist/modules/customer_accounts/api/login.js +14 -3
  25. package/dist/modules/customer_accounts/api/login.js.map +2 -2
  26. package/dist/modules/customer_accounts/api/magic-link/request.js +14 -3
  27. package/dist/modules/customer_accounts/api/magic-link/request.js.map +2 -2
  28. package/dist/modules/customer_accounts/api/password/reset-request.js +14 -3
  29. package/dist/modules/customer_accounts/api/password/reset-request.js.map +2 -2
  30. package/dist/modules/customer_accounts/api/signup.js +32 -5
  31. package/dist/modules/customer_accounts/api/signup.js.map +2 -2
  32. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Actions.js +48 -0
  33. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Actions.js.map +7 -0
  34. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js +43 -0
  35. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js.map +7 -0
  36. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DnsConfig.js +53 -0
  37. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DnsConfig.js.map +7 -0
  38. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DomainPanel.js +69 -0
  39. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DomainPanel.js.map +7 -0
  40. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/OrgSwitcher.js +31 -0
  41. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/OrgSwitcher.js.map +7 -0
  42. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/RegisterDialog.js +147 -0
  43. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/RegisterDialog.js.map +7 -0
  44. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/StatusBadgeSection.js +39 -0
  45. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/StatusBadgeSection.js.map +7 -0
  46. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Stepper.js +60 -0
  47. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Stepper.js.map +7 -0
  48. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/domainStatusMap.js +11 -0
  49. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/domainStatusMap.js.map +7 -0
  50. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/types.js +1 -0
  51. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/types.js.map +7 -0
  52. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.js +361 -0
  53. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.js.map +7 -0
  54. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.meta.js +28 -0
  55. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.meta.js.map +7 -0
  56. package/dist/modules/customer_accounts/data/enrichers.js +78 -12
  57. package/dist/modules/customer_accounts/data/enrichers.js.map +2 -2
  58. package/dist/modules/customer_accounts/data/entities.js +85 -2
  59. package/dist/modules/customer_accounts/data/entities.js.map +2 -2
  60. package/dist/modules/customer_accounts/data/guards.js +130 -0
  61. package/dist/modules/customer_accounts/data/guards.js.map +7 -0
  62. package/dist/modules/customer_accounts/data/validators.js +19 -0
  63. package/dist/modules/customer_accounts/data/validators.js.map +2 -2
  64. package/dist/modules/customer_accounts/di.js +23 -1
  65. package/dist/modules/customer_accounts/di.js.map +2 -2
  66. package/dist/modules/customer_accounts/events.js +9 -2
  67. package/dist/modules/customer_accounts/events.js.map +2 -2
  68. package/dist/modules/customer_accounts/lib/customerAuthServer.js +27 -7
  69. package/dist/modules/customer_accounts/lib/customerAuthServer.js.map +2 -2
  70. package/dist/modules/customer_accounts/lib/customerUrl.js +48 -0
  71. package/dist/modules/customer_accounts/lib/customerUrl.js.map +7 -0
  72. package/dist/modules/customer_accounts/lib/hostname.js +55 -0
  73. package/dist/modules/customer_accounts/lib/hostname.js.map +7 -0
  74. package/dist/modules/customer_accounts/lib/platformDomains.js +7 -0
  75. package/dist/modules/customer_accounts/lib/platformDomains.js.map +7 -0
  76. package/dist/modules/customer_accounts/lib/proxyRanges.js +71 -0
  77. package/dist/modules/customer_accounts/lib/proxyRanges.js.map +7 -0
  78. package/dist/modules/customer_accounts/lib/rateLimiter.js +7 -0
  79. package/dist/modules/customer_accounts/lib/rateLimiter.js.map +2 -2
  80. package/dist/modules/customer_accounts/lib/resolveTenantContext.js +63 -0
  81. package/dist/modules/customer_accounts/lib/resolveTenantContext.js.map +7 -0
  82. package/dist/modules/customer_accounts/lib/secretCompare.js +13 -0
  83. package/dist/modules/customer_accounts/lib/secretCompare.js.map +7 -0
  84. package/dist/modules/customer_accounts/migrations/Migration20260430120000_customer_accounts.js +37 -0
  85. package/dist/modules/customer_accounts/migrations/Migration20260430120000_customer_accounts.js.map +7 -0
  86. package/dist/modules/customer_accounts/notifications.client.js +76 -0
  87. package/dist/modules/customer_accounts/notifications.client.js.map +2 -2
  88. package/dist/modules/customer_accounts/notifications.js +76 -0
  89. package/dist/modules/customer_accounts/notifications.js.map +2 -2
  90. package/dist/modules/customer_accounts/services/domainMappingService.js +497 -0
  91. package/dist/modules/customer_accounts/services/domainMappingService.js.map +7 -0
  92. package/dist/modules/customer_accounts/setup.js +48 -1
  93. package/dist/modules/customer_accounts/setup.js.map +2 -2
  94. package/dist/modules/customer_accounts/subscribers/invalidateDomainCache.js +29 -0
  95. package/dist/modules/customer_accounts/subscribers/invalidateDomainCache.js.map +7 -0
  96. package/dist/modules/customer_accounts/subscribers/notifyDomainActivated.js +29 -0
  97. package/dist/modules/customer_accounts/subscribers/notifyDomainActivated.js.map +7 -0
  98. package/dist/modules/customer_accounts/subscribers/notifyDomainDnsFailed.js +30 -0
  99. package/dist/modules/customer_accounts/subscribers/notifyDomainDnsFailed.js.map +7 -0
  100. package/dist/modules/customer_accounts/subscribers/notifyDomainTlsFailed.js +31 -0
  101. package/dist/modules/customer_accounts/subscribers/notifyDomainTlsFailed.js.map +7 -0
  102. package/dist/modules/customer_accounts/subscribers/notifyDomainVerified.js +29 -0
  103. package/dist/modules/customer_accounts/subscribers/notifyDomainVerified.js.map +7 -0
  104. package/dist/modules/customer_accounts/workers/domainTlsRetryWorker.js +67 -0
  105. package/dist/modules/customer_accounts/workers/domainTlsRetryWorker.js.map +7 -0
  106. package/dist/modules/customer_accounts/workers/domainVerificationWorker.js +34 -0
  107. package/dist/modules/customer_accounts/workers/domainVerificationWorker.js.map +7 -0
  108. package/dist/modules/customers/api/companies/route.js +4 -9
  109. package/dist/modules/customers/api/companies/route.js.map +2 -2
  110. package/dist/modules/customers/api/deals/route.js +4 -9
  111. package/dist/modules/customers/api/deals/route.js.map +2 -2
  112. package/dist/modules/customers/api/people/route.js +4 -9
  113. package/dist/modules/customers/api/people/route.js.map +2 -2
  114. package/dist/modules/customers/api/utils.js +0 -12
  115. package/dist/modules/customers/api/utils.js.map +2 -2
  116. package/dist/modules/customers/backend/customers/companies/page.js +361 -284
  117. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  118. package/dist/modules/customers/backend/customers/deals/page.js +378 -473
  119. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  120. package/dist/modules/customers/backend/customers/people/page.js +368 -286
  121. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  122. package/dist/modules/customers/components/detail/assignableStaff.js +15 -1
  123. package/dist/modules/customers/components/detail/assignableStaff.js.map +2 -2
  124. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.js +188 -36
  125. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.js.map +2 -2
  126. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.js +2 -2
  127. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.js.map +2 -2
  128. package/dist/modules/customers/widgets/injection-table.js +6 -0
  129. package/dist/modules/customers/widgets/injection-table.js.map +2 -2
  130. package/dist/modules/perspectives/services/perspectiveService.js +16 -2
  131. package/dist/modules/perspectives/services/perspectiveService.js.map +2 -2
  132. package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js +1 -0
  133. package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js.map +2 -2
  134. package/dist/modules/query_index/lib/engine.js +31 -8
  135. package/dist/modules/query_index/lib/engine.js.map +2 -2
  136. package/generated/entities/domain_mapping/index.ts +14 -0
  137. package/generated/entities.ids.generated.ts +2 -1
  138. package/generated/entity-fields-registry.ts +16 -0
  139. package/package.json +5 -5
  140. package/src/helpers/integration/salesUi.ts +27 -23
  141. package/src/modules/customer_accounts/acl.ts +1 -0
  142. package/src/modules/customer_accounts/api/admin/domain-mappings/[id]/health-check.ts +119 -0
  143. package/src/modules/customer_accounts/api/admin/domain-mappings/[id]/verify.ts +123 -0
  144. package/src/modules/customer_accounts/api/admin/domain-mappings.ts +303 -0
  145. package/src/modules/customer_accounts/api/domain-check.ts +83 -0
  146. package/src/modules/customer_accounts/api/domain-resolve/all.ts +139 -0
  147. package/src/modules/customer_accounts/api/domain-resolve.ts +91 -0
  148. package/src/modules/customer_accounts/api/login.ts +14 -3
  149. package/src/modules/customer_accounts/api/magic-link/request.ts +14 -3
  150. package/src/modules/customer_accounts/api/password/reset-request.ts +15 -3
  151. package/src/modules/customer_accounts/api/signup.ts +36 -5
  152. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Actions.tsx +70 -0
  153. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.tsx +66 -0
  154. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DnsConfig.tsx +78 -0
  155. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/DomainPanel.tsx +90 -0
  156. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/OrgSwitcher.tsx +43 -0
  157. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/RegisterDialog.tsx +178 -0
  158. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/StatusBadgeSection.tsx +56 -0
  159. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Stepper.tsx +69 -0
  160. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/domainStatusMap.ts +10 -0
  161. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/types.ts +31 -0
  162. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/page.meta.ts +26 -0
  163. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/page.tsx +411 -0
  164. package/src/modules/customer_accounts/data/enrichers.ts +95 -12
  165. package/src/modules/customer_accounts/data/entities.ts +80 -1
  166. package/src/modules/customer_accounts/data/guards.ts +155 -0
  167. package/src/modules/customer_accounts/data/validators.ts +26 -0
  168. package/src/modules/customer_accounts/di.ts +35 -1
  169. package/src/modules/customer_accounts/events.ts +8 -1
  170. package/src/modules/customer_accounts/i18n/de.json +78 -0
  171. package/src/modules/customer_accounts/i18n/en.json +78 -0
  172. package/src/modules/customer_accounts/i18n/es.json +78 -0
  173. package/src/modules/customer_accounts/i18n/pl.json +78 -0
  174. package/src/modules/customer_accounts/lib/customerAuthServer.ts +44 -8
  175. package/src/modules/customer_accounts/lib/customerUrl.ts +72 -0
  176. package/src/modules/customer_accounts/lib/hostname.ts +76 -0
  177. package/src/modules/customer_accounts/lib/platformDomains.ts +14 -0
  178. package/src/modules/customer_accounts/lib/proxyRanges.ts +85 -0
  179. package/src/modules/customer_accounts/lib/rateLimiter.ts +10 -0
  180. package/src/modules/customer_accounts/lib/resolveTenantContext.ts +96 -0
  181. package/src/modules/customer_accounts/lib/secretCompare.ts +20 -0
  182. package/src/modules/customer_accounts/migrations/.snapshot-open-mercato.json +341 -0
  183. package/src/modules/customer_accounts/migrations/Migration20260430120000_customer_accounts.ts +38 -0
  184. package/src/modules/customer_accounts/notifications.client.ts +76 -0
  185. package/src/modules/customer_accounts/notifications.ts +76 -0
  186. package/src/modules/customer_accounts/services/domainMappingService.ts +632 -0
  187. package/src/modules/customer_accounts/setup.ts +81 -1
  188. package/src/modules/customer_accounts/subscribers/invalidateDomainCache.ts +35 -0
  189. package/src/modules/customer_accounts/subscribers/notifyDomainActivated.ts +32 -0
  190. package/src/modules/customer_accounts/subscribers/notifyDomainDnsFailed.ts +33 -0
  191. package/src/modules/customer_accounts/subscribers/notifyDomainTlsFailed.ts +34 -0
  192. package/src/modules/customer_accounts/subscribers/notifyDomainVerified.ts +32 -0
  193. package/src/modules/customer_accounts/workers/domainTlsRetryWorker.ts +84 -0
  194. package/src/modules/customer_accounts/workers/domainVerificationWorker.ts +45 -0
  195. package/src/modules/customers/api/companies/route.ts +4 -9
  196. package/src/modules/customers/api/deals/route.ts +4 -9
  197. package/src/modules/customers/api/people/route.ts +4 -9
  198. package/src/modules/customers/api/utils.ts +0 -14
  199. package/src/modules/customers/backend/customers/companies/page.tsx +321 -237
  200. package/src/modules/customers/backend/customers/deals/page.tsx +321 -455
  201. package/src/modules/customers/backend/customers/people/page.tsx +331 -240
  202. package/src/modules/customers/components/detail/assignableStaff.ts +21 -0
  203. package/src/modules/customers/i18n/de.json +31 -0
  204. package/src/modules/customers/i18n/en.json +31 -0
  205. package/src/modules/customers/i18n/es.json +31 -0
  206. package/src/modules/customers/i18n/pl.json +31 -0
  207. package/src/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.tsx +197 -40
  208. package/src/modules/customers/widgets/injection/ai-assistant-trigger/widget.ts +13 -7
  209. package/src/modules/customers/widgets/injection-table.ts +6 -0
  210. package/src/modules/perspectives/services/perspectiveService.ts +30 -2
  211. package/src/modules/portal/frontend/[orgSlug]/portal/login/page.tsx +1 -0
  212. package/src/modules/query_index/lib/engine.ts +46 -8
@@ -34,8 +34,9 @@ function readNumber(value) {
34
34
  return 0;
35
35
  }
36
36
  function resolveView(tableId) {
37
- if (typeof tableId === "string" && tableId.includes("companies")) {
38
- return "customers.companies.list";
37
+ if (typeof tableId === "string") {
38
+ if (tableId.includes("deals")) return "customers.deals.list";
39
+ if (tableId.includes("companies")) return "customers.companies.list";
39
40
  }
40
41
  return "customers.people.list";
41
42
  }
@@ -58,6 +59,78 @@ function buildPageContext(context) {
58
59
  function useCustomerSuggestions(view, hasSelection, selectedCount) {
59
60
  const t = useT();
60
61
  return React.useMemo(() => {
62
+ if (view === "customers.deals.list") {
63
+ if (hasSelection) {
64
+ return [
65
+ {
66
+ label: t(
67
+ "customers.ai_assistant.suggestions.summarizeSelectedDeals",
68
+ "Summarize selected deals"
69
+ ),
70
+ prompt: `Give me a summary of my ${selectedCount} selected deals \u2014 stage, value, and close date`,
71
+ icon: /* @__PURE__ */ jsx(Handshake, { className: "size-4" })
72
+ },
73
+ {
74
+ label: t(
75
+ "customers.ai_assistant.suggestions.atRiskSelectedDeals",
76
+ "Selected deals at risk"
77
+ ),
78
+ prompt: `Of my ${selectedCount} selected deals, which look at risk of slipping their close date?`,
79
+ icon: /* @__PURE__ */ jsx(Handshake, { className: "size-4" })
80
+ },
81
+ {
82
+ label: t(
83
+ "customers.ai_assistant.suggestions.companiesForSelectedDeals",
84
+ "Companies on selected deals"
85
+ ),
86
+ prompt: `List the companies associated with my ${selectedCount} selected deals`,
87
+ icon: /* @__PURE__ */ jsx(Building2, { className: "size-4" })
88
+ }
89
+ ];
90
+ }
91
+ return [
92
+ {
93
+ label: t(
94
+ "customers.ai_assistant.suggestions.dealsClosingThisMonth",
95
+ "Deals closing this month"
96
+ ),
97
+ prompt: "Which deals are closing this month?",
98
+ icon: /* @__PURE__ */ jsx(Handshake, { className: "size-4" })
99
+ },
100
+ {
101
+ label: t(
102
+ "customers.ai_assistant.suggestions.dealsAtRisk",
103
+ "Deals at risk of slipping"
104
+ ),
105
+ prompt: "Show me deals at risk of slipping their close date",
106
+ icon: /* @__PURE__ */ jsx(Handshake, { className: "size-4" })
107
+ },
108
+ {
109
+ label: t(
110
+ "customers.ai_assistant.suggestions.totalOpenDealValue",
111
+ "Total open deal value"
112
+ ),
113
+ prompt: "What's the total value of open deals across the pipeline?",
114
+ icon: /* @__PURE__ */ jsx(Handshake, { className: "size-4" })
115
+ },
116
+ {
117
+ label: t(
118
+ "customers.ai_assistant.suggestions.topDealsByValue",
119
+ "Top deals by value"
120
+ ),
121
+ prompt: "Show me the top 5 deals by value",
122
+ icon: /* @__PURE__ */ jsx(Handshake, { className: "size-4" })
123
+ },
124
+ {
125
+ label: t(
126
+ "customers.ai_assistant.suggestions.searchDeals",
127
+ "Search for a deal"
128
+ ),
129
+ prompt: "Search for deals by title, company, or stage",
130
+ icon: /* @__PURE__ */ jsx(Search, { className: "size-4" })
131
+ }
132
+ ];
133
+ }
61
134
  if (view === "customers.companies.list") {
62
135
  if (hasSelection) {
63
136
  return [
@@ -170,19 +243,106 @@ function useCustomerContextItems(pageContext) {
170
243
  return React.useMemo(() => {
171
244
  const items = [];
172
245
  const { selectedCount, totalMatching } = pageContext.extra;
173
- const isCompanies = pageContext.view === "customers.companies.list";
246
+ const view = pageContext.view;
247
+ let selectedKey = "customers.ai_assistant.context.selectedPeople";
248
+ let selectedFallback = "{count} contacts selected";
249
+ let matchingKey = "customers.ai_assistant.context.matchingPeople";
250
+ let matchingFallback = "{count} contacts in view";
251
+ if (view === "customers.companies.list") {
252
+ selectedKey = "customers.ai_assistant.context.selectedCompanies";
253
+ selectedFallback = "{count} companies selected";
254
+ matchingKey = "customers.ai_assistant.context.matchingCompanies";
255
+ matchingFallback = "{count} companies in view";
256
+ } else if (view === "customers.deals.list") {
257
+ selectedKey = "customers.ai_assistant.context.selectedDeals";
258
+ selectedFallback = "{count} deals selected";
259
+ matchingKey = "customers.ai_assistant.context.matchingDeals";
260
+ matchingFallback = "{count} deals in view";
261
+ }
174
262
  if (selectedCount > 0) {
175
- const key = isCompanies ? "customers.ai_assistant.context.selectedCompanies" : "customers.ai_assistant.context.selectedPeople";
176
- const fallback = isCompanies ? "{count} companies selected" : "{count} contacts selected";
177
- items.push({ label: t(key, fallback).replace("{count}", String(selectedCount)) });
263
+ items.push({ label: t(selectedKey, selectedFallback).replace("{count}", String(selectedCount)) });
178
264
  } else if (totalMatching > 0) {
179
- const key = isCompanies ? "customers.ai_assistant.context.matchingCompanies" : "customers.ai_assistant.context.matchingPeople";
180
- const fallback = isCompanies ? "{count} companies in view" : "{count} contacts in view";
181
- items.push({ label: t(key, fallback).replace("{count}", String(totalMatching)) });
265
+ items.push({ label: t(matchingKey, matchingFallback).replace("{count}", String(totalMatching)) });
182
266
  }
183
267
  return items;
184
268
  }, [pageContext, t]);
185
269
  }
270
+ function useViewCopy(view, selectedCount) {
271
+ const t = useT();
272
+ return React.useMemo(() => {
273
+ const replaceCount = (s) => s.replace("{count}", String(selectedCount));
274
+ if (view === "customers.deals.list") {
275
+ return {
276
+ welcomeDescriptionAll: t(
277
+ "customers.ai_assistant.sheet.welcomeDescriptionAllDeals",
278
+ "Ask about deals, pipeline, or close dates:"
279
+ ),
280
+ welcomeDescriptionSelection: replaceCount(
281
+ t(
282
+ "customers.ai_assistant.sheet.welcomeDescriptionSelectionDeals",
283
+ "Ready to explore your {count} selected deals:"
284
+ )
285
+ ),
286
+ descriptionWithSelection: replaceCount(
287
+ t(
288
+ "customers.ai_assistant.sheet.descriptionWithSelectionDeals",
289
+ "Working with {count} selected deals. Ask about their stage, value, and close dates."
290
+ )
291
+ ),
292
+ triggerAriaLabel: t(
293
+ "customers.ai_assistant.trigger.ariaLabelDeals",
294
+ "Open AI assistant for deals"
295
+ )
296
+ };
297
+ }
298
+ if (view === "customers.companies.list") {
299
+ return {
300
+ welcomeDescriptionAll: t(
301
+ "customers.ai_assistant.sheet.welcomeDescriptionAllCompanies",
302
+ "Ask me anything about your companies, deals, and activities:"
303
+ ),
304
+ welcomeDescriptionSelection: replaceCount(
305
+ t(
306
+ "customers.ai_assistant.sheet.welcomeDescriptionSelectionCompanies",
307
+ "Ready to explore your {count} selected companies:"
308
+ )
309
+ ),
310
+ descriptionWithSelection: replaceCount(
311
+ t(
312
+ "customers.ai_assistant.sheet.descriptionWithSelectionCompanies",
313
+ "Working with {count} selected companies. Ask about their deals, contacts, and activities."
314
+ )
315
+ ),
316
+ triggerAriaLabel: t(
317
+ "customers.ai_assistant.trigger.ariaLabelCompanies",
318
+ "Open AI assistant for companies"
319
+ )
320
+ };
321
+ }
322
+ return {
323
+ welcomeDescriptionAll: t(
324
+ "customers.ai_assistant.sheet.welcomeDescriptionAll",
325
+ "Ask me anything about your customers, companies, and deals:"
326
+ ),
327
+ welcomeDescriptionSelection: replaceCount(
328
+ t(
329
+ "customers.ai_assistant.sheet.welcomeDescriptionSelection",
330
+ "Ready to explore your {count} selected contacts:"
331
+ )
332
+ ),
333
+ descriptionWithSelection: replaceCount(
334
+ t(
335
+ "customers.ai_assistant.sheet.descriptionWithSelection",
336
+ "Working with {count} selected contacts. Ask about their details, deals, companies, and activities."
337
+ )
338
+ ),
339
+ triggerAriaLabel: t(
340
+ "customers.ai_assistant.trigger.ariaLabel",
341
+ "Open AI assistant for people"
342
+ )
343
+ };
344
+ }, [view, selectedCount, t]);
345
+ }
186
346
  function useCustomerAgents() {
187
347
  const t = useT();
188
348
  return React.useMemo(
@@ -213,6 +373,7 @@ function AiAssistantTriggerWidget({ context }) {
213
373
  const hasSelection = selectedCount > 0;
214
374
  const suggestions = useCustomerSuggestions(pageContext.view, hasSelection, selectedCount);
215
375
  const contextItems = useCustomerContextItems(pageContext);
376
+ const viewCopy = useViewCopy(pageContext.view, selectedCount);
216
377
  const openAgent = React.useCallback((agentId) => {
217
378
  setActiveAgent(agentId);
218
379
  setLastAgent(agentId);
@@ -253,13 +414,7 @@ function AiAssistantTriggerWidget({ context }) {
253
414
  suggestions,
254
415
  contextItems,
255
416
  welcomeTitle: t("customers.ai_assistant.sheet.welcomeTitle", "CRM Assistant"),
256
- welcomeDescription: hasSelection ? t(
257
- "customers.ai_assistant.sheet.welcomeDescriptionSelection",
258
- "Ready to explore your {count} selected contacts:"
259
- ).replace("{count}", String(selectedCount)) : t(
260
- "customers.ai_assistant.sheet.welcomeDescriptionAll",
261
- "Ask me anything about your customers, companies, and deals:"
262
- )
417
+ welcomeDescription: hasSelection ? viewCopy.welcomeDescriptionSelection : viewCopy.welcomeDescriptionAll
263
418
  });
264
419
  setOpen(false);
265
420
  }, [
@@ -269,14 +424,11 @@ function AiAssistantTriggerWidget({ context }) {
269
424
  dock,
270
425
  hasSelection,
271
426
  pageContext,
272
- selectedCount,
273
427
  suggestions,
274
- t
428
+ t,
429
+ viewCopy
275
430
  ]);
276
- const triggerLabel = t(
277
- "customers.ai_assistant.trigger.ariaLabel",
278
- "Open AI assistant for people"
279
- );
431
+ const triggerLabel = viewCopy.triggerAriaLabel;
280
432
  const labelText = t("customers.ai_assistant.trigger.label", "AI");
281
433
  const moreAgentsLabel = t(
282
434
  "customers.ai_assistant.trigger.moreAgentsAriaLabel",
@@ -295,10 +447,17 @@ function AiAssistantTriggerWidget({ context }) {
295
447
  title: triggerLabel,
296
448
  className: cn(
297
449
  "relative",
450
+ "hover:bg-brand-violet/10",
298
451
  agents.length > 1 && "rounded-r-none border-r-0"
299
452
  ),
300
453
  children: [
301
- /* @__PURE__ */ jsx(Sparkles, { className: "size-4", "aria-hidden": true }),
454
+ /* @__PURE__ */ jsx(
455
+ Sparkles,
456
+ {
457
+ className: "size-4 text-brand-violet",
458
+ "aria-hidden": true
459
+ }
460
+ ),
302
461
  /* @__PURE__ */ jsx("span", { children: labelText }),
303
462
  hasSelection ? /* @__PURE__ */ jsx(
304
463
  "span",
@@ -395,10 +554,7 @@ function AiAssistantTriggerWidget({ context }) {
395
554
  }
396
555
  ) : null
397
556
  ] }),
398
- /* @__PURE__ */ jsx(DialogDescription, { children: hasSelection ? t(
399
- "customers.ai_assistant.sheet.descriptionWithSelection",
400
- "Working with {count} selected contacts. Ask about their details, deals, companies, and activities."
401
- ).replace("{count}", String(selectedCount)) : t(
557
+ /* @__PURE__ */ jsx(DialogDescription, { children: hasSelection ? viewCopy.descriptionWithSelection : t(
402
558
  "customers.ai_assistant.sheet.description",
403
559
  "Your CRM assistant. Ask about people, companies, deals, and activities."
404
560
  ) })
@@ -411,7 +567,8 @@ function AiAssistantTriggerWidget({ context }) {
411
567
  suggestions,
412
568
  contextItems,
413
569
  hasSelection,
414
- selectedCount
570
+ selectedCount,
571
+ viewCopy
415
572
  }
416
573
  )
417
574
  ]
@@ -425,7 +582,8 @@ function CustomersChatBody({
425
582
  suggestions,
426
583
  contextItems,
427
584
  hasSelection,
428
- selectedCount
585
+ selectedCount,
586
+ viewCopy
429
587
  }) {
430
588
  const t = useT();
431
589
  const sessions = useAiChatSessions();
@@ -449,13 +607,7 @@ function CustomersChatBody({
449
607
  suggestions,
450
608
  contextItems,
451
609
  welcomeTitle: t("customers.ai_assistant.sheet.welcomeTitle", "CRM Assistant"),
452
- welcomeDescription: hasSelection ? t(
453
- "customers.ai_assistant.sheet.welcomeDescriptionSelection",
454
- "Ready to explore your {count} selected contacts:"
455
- ).replace("{count}", String(selectedCount)) : t(
456
- "customers.ai_assistant.sheet.welcomeDescriptionAll",
457
- "Ask me anything about your customers, companies, and deals:"
458
- )
610
+ welcomeDescription: hasSelection ? viewCopy.welcomeDescriptionSelection : viewCopy.welcomeDescriptionAll
459
611
  },
460
612
  session.id
461
613
  ) : null })
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.tsx"],
4
- "sourcesContent": ["\"use client\"\n\n/**\n * Step 4.10 \u2014 Backend AiChat injection widget (client).\n *\n * Renders a compact, round, icon-only trigger in the DataTable\n * `:search-trailing` injection slot (right next to the list search input).\n * Clicking the trigger opens a popover listing the AI agents this widget\n * exposes \u2014 currently `customers.account_assistant`, but the popover is\n * the agreed extension point for additional customers-domain agents\n * (selection digesters, deal-shapers, etc.). Picking an agent opens a\n * right-side sheet embedding `<AiChat>` for that agent.\n *\n * `pageContext` shape matches spec \u00A710.1 (view / recordType / recordId\n * / extra). The host DataTable provides selection + total information\n * through the `context` prop injected by `<InjectionSpot>`.\n */\n\nimport * as React from 'react'\nimport { Building2, ChevronDown, Handshake, PanelRightOpen, Search, Sparkles, Users } from 'lucide-react'\nimport { AiChat, type AiChatSuggestion, type AiChatContextItem } from '@open-mercato/ui/ai/AiChat'\nimport { useAiDock } from '@open-mercato/ui/ai/AiDock'\nimport { useAiChatSessions } from '@open-mercato/ui/ai/AiChatSessions'\nimport { ChatPaneTabs } from '@open-mercato/ui/ai/ChatPaneTabs'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { Popover, PopoverContent, PopoverTrigger } from '@open-mercato/ui/primitives/popover'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\nexport const CUSTOMERS_AI_INJECT_AGENT_ID = 'customers.account_assistant'\n\nexport type CustomersAiInjectView = 'customers.people.list' | 'customers.companies.list'\n\nexport interface CustomersAiInjectPageContext {\n view: CustomersAiInjectView\n recordType: null\n recordId: string | null\n extra: {\n selectedCount: number\n totalMatching: number\n }\n}\n\nexport function computeCustomersAiInjectPageContext(\n context: HostInjectionContext | undefined,\n): CustomersAiInjectPageContext {\n return buildPageContext(context)\n}\n\ninterface HostInjectionContext {\n tableId?: string | null\n title?: string\n selectedRowIds?: string[]\n selectedCount?: number\n total?: number\n totalMatching?: number\n rowCount?: number\n}\n\ninterface AiAssistantTriggerProps {\n context?: HostInjectionContext\n}\n\nfunction readString(value: unknown): string {\n return typeof value === 'string' ? value : ''\n}\n\nfunction readNumber(value: unknown): number {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string') {\n const parsed = Number.parseInt(value, 10)\n if (Number.isFinite(parsed)) return parsed\n }\n return 0\n}\n\nfunction resolveView(tableId: string | null | undefined): CustomersAiInjectView {\n if (typeof tableId === 'string' && tableId.includes('companies')) {\n return 'customers.companies.list'\n }\n return 'customers.people.list'\n}\n\nfunction buildPageContext(context: HostInjectionContext | undefined): CustomersAiInjectPageContext {\n const selectedIdsRaw = Array.isArray(context?.selectedRowIds) ? context?.selectedRowIds ?? [] : []\n const selectedIds = selectedIdsRaw.map(readString).filter((id) => id.length > 0)\n const selectedCount = selectedIds.length > 0\n ? selectedIds.length\n : readNumber(context?.selectedCount)\n const totalMatching = readNumber(context?.totalMatching ?? context?.total ?? context?.rowCount)\n const recordId = selectedIds.length > 0 ? selectedIds.join(',') : null\n return {\n view: resolveView(context?.tableId),\n recordType: null,\n recordId,\n extra: {\n selectedCount,\n totalMatching,\n },\n }\n}\n\nfunction useCustomerSuggestions(\n view: CustomersAiInjectView,\n hasSelection: boolean,\n selectedCount: number,\n): AiChatSuggestion[] {\n const t = useT()\n return React.useMemo(() => {\n if (view === 'customers.companies.list') {\n if (hasSelection) {\n return [\n {\n label: t(\n 'customers.ai_assistant.suggestions.summarizeSelectedCompanies',\n 'Summarize selected companies',\n ),\n prompt: `Give me a summary of my ${selectedCount} selected companies \u2014 size, industry, and recent activity`,\n icon: <Building2 className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.dealsForSelectedCompanies',\n 'Show deals for selected companies',\n ),\n prompt: `Show me all deals associated with my ${selectedCount} selected companies`,\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.peopleAtSelectedCompanies',\n 'List people at selected companies',\n ),\n prompt: `List the contacts (people) associated with my ${selectedCount} selected companies`,\n icon: <Users className=\"size-4\" />,\n },\n ]\n }\n return [\n {\n label: t(\n 'customers.ai_assistant.suggestions.searchCompanies',\n 'Search for a company',\n ),\n prompt: 'Search for companies by name, industry, or tax ID',\n icon: <Search className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.topCompaniesByDeals',\n 'Top companies by deal value',\n ),\n prompt: 'Show me the companies with the highest open deal value',\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.companiesWithoutContacts',\n 'Companies missing contacts',\n ),\n prompt: 'Find companies that have no associated people yet',\n icon: <Users className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.companiesActivityOverview',\n 'Activity overview',\n ),\n prompt: 'Give me an overview of recent company-level activities and interactions',\n icon: <Building2 className=\"size-4\" />,\n },\n ]\n }\n if (hasSelection) {\n return [\n {\n label: t('customers.ai_assistant.suggestions.summarizeSelected', 'Summarize selected contacts'),\n prompt: `Give me a summary of my ${selectedCount} selected contacts \u2014 key details and recent activity`,\n icon: <Users className=\"size-4\" />,\n },\n {\n label: t('customers.ai_assistant.suggestions.findDeals', 'Show deals for selected people'),\n prompt: `Show me all deals associated with my ${selectedCount} selected contacts`,\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t('customers.ai_assistant.suggestions.findCompanies', 'Find related companies'),\n prompt: `Find companies related to my ${selectedCount} selected contacts`,\n icon: <Building2 className=\"size-4\" />,\n },\n ]\n }\n return [\n {\n label: t('customers.ai_assistant.suggestions.searchPeople', 'Search for a contact'),\n prompt: 'Search for contacts by name, email, or company',\n icon: <Search className=\"size-4\" />,\n },\n {\n label: t('customers.ai_assistant.suggestions.recentDeals', 'Show recent deals'),\n prompt: 'Show me the most recent deals and their current stages',\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t('customers.ai_assistant.suggestions.topCompanies', 'List top companies'),\n prompt: 'List companies with the most associated contacts and deals',\n icon: <Building2 className=\"size-4\" />,\n },\n {\n label: t('customers.ai_assistant.suggestions.activityOverview', 'Activity overview'),\n prompt: 'Give me an overview of recent customer activities and interactions',\n icon: <Users className=\"size-4\" />,\n },\n ]\n }, [view, hasSelection, selectedCount, t])\n}\n\nfunction useCustomerContextItems(pageContext: CustomersAiInjectPageContext): AiChatContextItem[] {\n const t = useT()\n return React.useMemo(() => {\n const items: AiChatContextItem[] = []\n const { selectedCount, totalMatching } = pageContext.extra\n const isCompanies = pageContext.view === 'customers.companies.list'\n if (selectedCount > 0) {\n const key = isCompanies\n ? 'customers.ai_assistant.context.selectedCompanies'\n : 'customers.ai_assistant.context.selectedPeople'\n const fallback = isCompanies ? '{count} companies selected' : '{count} contacts selected'\n items.push({ label: t(key, fallback).replace('{count}', String(selectedCount)) })\n } else if (totalMatching > 0) {\n const key = isCompanies\n ? 'customers.ai_assistant.context.matchingCompanies'\n : 'customers.ai_assistant.context.matchingPeople'\n const fallback = isCompanies ? '{count} companies in view' : '{count} contacts in view'\n items.push({ label: t(key, fallback).replace('{count}', String(totalMatching)) })\n }\n return items\n }, [pageContext, t])\n}\n\ninterface CustomerAgentDescriptor {\n id: string\n label: string\n description: string\n icon: React.ReactNode\n}\n\nfunction useCustomerAgents(): CustomerAgentDescriptor[] {\n const t = useT()\n return React.useMemo(\n () => [\n {\n id: CUSTOMERS_AI_INJECT_AGENT_ID,\n label: t('customers.ai_assistant.agents.account.label', 'CRM Assistant'),\n description: t(\n 'customers.ai_assistant.agents.account.description',\n 'Explore people, companies, deals, and activities.',\n ),\n icon: <Sparkles className=\"size-4\" />,\n },\n ],\n [t],\n )\n}\n\nexport default function AiAssistantTriggerWidget({ context }: AiAssistantTriggerProps) {\n const t = useT()\n const dock = useAiDock()\n const [open, setOpen] = React.useState(false)\n const [popoverOpen, setPopoverOpen] = React.useState(false)\n const [activeAgent, setActiveAgent] = React.useState<string>(CUSTOMERS_AI_INJECT_AGENT_ID)\n const [lastAgent, setLastAgent] = React.useState<string | null>(null)\n const pageContext = React.useMemo(() => buildPageContext(context), [context])\n const agents = useCustomerAgents()\n\n const selectedCount = pageContext.extra.selectedCount\n const hasSelection = selectedCount > 0\n const suggestions = useCustomerSuggestions(pageContext.view, hasSelection, selectedCount)\n const contextItems = useCustomerContextItems(pageContext)\n\n const openAgent = React.useCallback((agentId: string) => {\n setActiveAgent(agentId)\n setLastAgent(agentId)\n setPopoverOpen(false)\n if (dock.state.assistant?.agent === agentId) {\n dock.dock(dock.state.assistant)\n setOpen(false)\n return\n }\n setOpen(true)\n }, [dock])\n\n const handleSelectAgent = React.useCallback((agentId: string) => {\n openAgent(agentId)\n }, [openAgent])\n\n const handleMainTriggerClick = React.useCallback(() => {\n if (agents.length === 1) {\n openAgent(agents[0].id)\n return\n }\n if (lastAgent && agents.some((a) => a.id === lastAgent)) {\n openAgent(lastAgent)\n return\n }\n setPopoverOpen(true)\n }, [agents, lastAgent, openAgent])\n\n const handleDock = React.useCallback(() => {\n const agent = agents.find((a) => a.id === activeAgent) ?? agents[0]\n if (!agent) return\n dock.dock({\n agent: agent.id,\n label: agent.label,\n description: t('customers.ai_assistant.dock.subtitle', 'Customers'),\n pageContext: pageContext as unknown as Record<string, unknown>,\n placeholder: t(\n 'customers.ai_assistant.sheet.composerPlaceholder',\n 'Ask about people, companies, deals...',\n ),\n suggestions,\n contextItems,\n welcomeTitle: t('customers.ai_assistant.sheet.welcomeTitle', 'CRM Assistant'),\n welcomeDescription: hasSelection\n ? t(\n 'customers.ai_assistant.sheet.welcomeDescriptionSelection',\n 'Ready to explore your {count} selected contacts:',\n ).replace('{count}', String(selectedCount))\n : t(\n 'customers.ai_assistant.sheet.welcomeDescriptionAll',\n 'Ask me anything about your customers, companies, and deals:',\n ),\n })\n setOpen(false)\n }, [\n activeAgent,\n agents,\n contextItems,\n dock,\n hasSelection,\n pageContext,\n selectedCount,\n suggestions,\n t,\n ])\n\n const triggerLabel = t(\n 'customers.ai_assistant.trigger.ariaLabel',\n 'Open AI assistant for people',\n )\n\n const labelText = t('customers.ai_assistant.trigger.label', 'AI')\n const moreAgentsLabel = t(\n 'customers.ai_assistant.trigger.moreAgentsAriaLabel',\n 'Choose an AI assistant',\n )\n\n return (\n <>\n <div className=\"inline-flex items-center\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={handleMainTriggerClick}\n data-ai-customers-inject-trigger=\"\"\n aria-label={triggerLabel}\n title={triggerLabel}\n className={cn(\n 'relative',\n agents.length > 1 && 'rounded-r-none border-r-0',\n )}\n >\n <Sparkles className=\"size-4\" aria-hidden />\n <span>{labelText}</span>\n {hasSelection ? (\n <span\n className=\"absolute -top-1 -right-1 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-[10px] font-medium leading-none text-primary-foreground\"\n data-ai-customers-inject-selected-count={selectedCount}\n >\n {selectedCount}\n </span>\n ) : null}\n </Button>\n {agents.length > 1 ? (\n <Popover open={popoverOpen} onOpenChange={setPopoverOpen}>\n <PopoverTrigger asChild>\n <IconButton\n type=\"button\"\n variant=\"outline\"\n size=\"lg\"\n aria-label={moreAgentsLabel}\n title={moreAgentsLabel}\n className=\"rounded-l-none\"\n data-ai-customers-inject-picker=\"\"\n >\n <ChevronDown className=\"size-4\" aria-hidden />\n </IconButton>\n </PopoverTrigger>\n <PopoverContent align=\"end\" className=\"w-72 p-1\">\n <div className=\"px-3 pt-2 pb-1 text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n {t('customers.ai_assistant.popover.heading', 'AI assistants')}\n </div>\n <div className=\"flex flex-col gap-0.5\">\n {agents.map((agent) => (\n <button\n key={agent.id}\n type=\"button\"\n onClick={() => handleSelectAgent(agent.id)}\n data-ai-customers-inject-agent-option={agent.id}\n className=\"flex items-start gap-2 rounded-sm px-2 py-2 text-left text-sm hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground focus-visible:outline-none\"\n >\n <span className=\"mt-0.5 inline-flex size-6 items-center justify-center rounded-full bg-secondary text-secondary-foreground\">\n {agent.icon}\n </span>\n <span className=\"flex-1 min-w-0\">\n <span className=\"block font-medium leading-tight\">{agent.label}</span>\n <span className=\"block text-xs text-muted-foreground leading-snug\">\n {agent.description}\n </span>\n </span>\n </button>\n ))}\n </div>\n </PopoverContent>\n </Popover>\n ) : null}\n </div>\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogContent\n className={cn(\n // Mobile: full-screen sheet (no rounded corners, fills the\n // viewport). Desktop (\u2265sm): right-anchored side sheet.\n // The Dialog primitive ships a centering transform at the sm\n // breakpoint (`sm:top-1/2 sm:left-1/2 sm:-translate-x-1/2\n // sm:-translate-y-1/2 sm:inset-auto`); each must be overridden\n // at the same `sm:` breakpoint or the panel renders half off\n // the viewport.\n 'top-0 left-0 right-0 bottom-0 translate-x-0 translate-y-0 max-w-none w-screen h-svh max-h-svh rounded-none',\n 'sm:top-0 sm:bottom-0 sm:right-0 sm:left-auto sm:translate-x-0 sm:translate-y-0',\n 'sm:max-w-xl sm:w-[36rem] sm:rounded-l-2xl sm:h-screen sm:max-h-screen',\n 'flex flex-col gap-3 p-4 z-[70]',\n )}\n data-ai-customers-inject-sheet=\"\"\n >\n <DialogHeader>\n <div className=\"flex items-center gap-3 pr-8\">\n {/* Dock button lives on the LEFT \u2014 the Dialog primitive\n auto-renders an X close button absolutely positioned in\n the top-right corner, so anything we drop in the header's\n right side visually collides with it. Mobile hides the\n dock entirely (the side panel is desktop-only). */}\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t('customers.ai_assistant.sheet.dock', 'Dock to side')}\n title={t('customers.ai_assistant.sheet.dock', 'Dock to side')}\n onClick={handleDock}\n data-ai-customers-inject-dock=\"\"\n className=\"hidden lg:inline-flex shrink-0\"\n >\n <PanelRightOpen className=\"size-4\" aria-hidden />\n </IconButton>\n <DialogTitle className=\"flex-1 min-w-0 truncate\">\n {t('customers.ai_assistant.sheet.title', 'Customers AI assistant')}\n </DialogTitle>\n {hasSelection ? (\n <span\n className=\"shrink-0 inline-flex items-center rounded-full border border-border bg-secondary px-2 py-0.5 text-xs text-secondary-foreground\"\n data-ai-customers-inject-selection-pill=\"\"\n data-ai-customers-inject-selected-count={selectedCount}\n >\n {t(\n 'customers.ai_assistant.sheet.selectionPill',\n 'Acting on {count} selected',\n ).replace('{count}', String(selectedCount))}\n </span>\n ) : null}\n </div>\n <DialogDescription>\n {hasSelection\n ? t(\n 'customers.ai_assistant.sheet.descriptionWithSelection',\n 'Working with {count} selected contacts. Ask about their details, deals, companies, and activities.',\n ).replace('{count}', String(selectedCount))\n : t(\n 'customers.ai_assistant.sheet.description',\n 'Your CRM assistant. Ask about people, companies, deals, and activities.',\n )}\n </DialogDescription>\n </DialogHeader>\n <CustomersChatBody\n activeAgent={activeAgent}\n pageContext={pageContext}\n suggestions={suggestions}\n contextItems={contextItems}\n hasSelection={hasSelection}\n selectedCount={selectedCount}\n />\n </DialogContent>\n </Dialog>\n </>\n )\n}\n\ninterface CustomersChatBodyProps {\n activeAgent: string\n pageContext: CustomersAiInjectPageContext\n suggestions: AiChatSuggestion[]\n contextItems: AiChatContextItem[]\n hasSelection: boolean\n selectedCount: number\n}\n\nfunction CustomersChatBody({\n activeAgent,\n pageContext,\n suggestions,\n contextItems,\n hasSelection,\n selectedCount,\n}: CustomersChatBodyProps) {\n const t = useT()\n const sessions = useAiChatSessions()\n const session = sessions.getActiveSession(activeAgent)\n\n // Lazily ensure an open session exists. Running `ensureSession` inside an\n // effect (not inline during render) keeps the provider's setState calls\n // outside of the render phase. The first frame may render without a\n // session \u2014 that's fine, we render the tab strip alone until the next\n // tick when the new session is committed and `getActiveSession` returns it.\n React.useEffect(() => {\n if (!session) sessions.ensureSession(activeAgent)\n }, [activeAgent, session, sessions])\n\n return (\n <>\n <ChatPaneTabs agentId={activeAgent} className=\"border-b\" />\n <div className=\"min-h-0 flex-1\" data-ai-customers-inject-chat-container=\"\">\n {session ? (\n <AiChat\n // `key` forces a fresh mount when the active tab changes so the\n // AI SDK's status doesn't leak across sessions.\n key={session.id}\n agent={activeAgent}\n conversationId={session.conversationId}\n pageContext={pageContext as unknown as Record<string, unknown>}\n className=\"h-full\"\n placeholder={t(\n 'customers.ai_assistant.sheet.composerPlaceholder',\n 'Ask about people, companies, deals...',\n )}\n suggestions={suggestions}\n contextItems={contextItems}\n welcomeTitle={t('customers.ai_assistant.sheet.welcomeTitle', 'CRM Assistant')}\n welcomeDescription={\n hasSelection\n ? t(\n 'customers.ai_assistant.sheet.welcomeDescriptionSelection',\n 'Ready to explore your {count} selected contacts:',\n ).replace('{count}', String(selectedCount))\n : t(\n 'customers.ai_assistant.sheet.welcomeDescriptionAll',\n 'Ask me anything about your customers, companies, and deals:',\n )\n }\n />\n ) : null}\n </div>\n </>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\n/**\n * Step 4.10 \u2014 Backend AiChat injection widget (client).\n *\n * Renders a compact, round, icon-only trigger in the DataTable\n * `:search-trailing` injection slot (right next to the list search input).\n * Clicking the trigger opens a popover listing the AI agents this widget\n * exposes \u2014 currently `customers.account_assistant`, but the popover is\n * the agreed extension point for additional customers-domain agents\n * (selection digesters, deal-shapers, etc.). Picking an agent opens a\n * right-side sheet embedding `<AiChat>` for that agent.\n *\n * `pageContext` shape matches spec \u00A710.1 (view / recordType / recordId\n * / extra). The host DataTable provides selection + total information\n * through the `context` prop injected by `<InjectionSpot>`.\n */\n\nimport * as React from 'react'\nimport { Building2, ChevronDown, Handshake, PanelRightOpen, Search, Sparkles, Users } from 'lucide-react'\nimport { AiChat, type AiChatSuggestion, type AiChatContextItem } from '@open-mercato/ui/ai/AiChat'\nimport { useAiDock } from '@open-mercato/ui/ai/AiDock'\nimport { useAiChatSessions } from '@open-mercato/ui/ai/AiChatSessions'\nimport { ChatPaneTabs } from '@open-mercato/ui/ai/ChatPaneTabs'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { Popover, PopoverContent, PopoverTrigger } from '@open-mercato/ui/primitives/popover'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\nexport const CUSTOMERS_AI_INJECT_AGENT_ID = 'customers.account_assistant'\n\nexport type CustomersAiInjectView =\n | 'customers.people.list'\n | 'customers.companies.list'\n | 'customers.deals.list'\n\nexport interface CustomersAiInjectPageContext {\n view: CustomersAiInjectView\n recordType: null\n recordId: string | null\n extra: {\n selectedCount: number\n totalMatching: number\n }\n}\n\nexport function computeCustomersAiInjectPageContext(\n context: HostInjectionContext | undefined,\n): CustomersAiInjectPageContext {\n return buildPageContext(context)\n}\n\ninterface HostInjectionContext {\n tableId?: string | null\n title?: string\n selectedRowIds?: string[]\n selectedCount?: number\n total?: number\n totalMatching?: number\n rowCount?: number\n}\n\ninterface AiAssistantTriggerProps {\n context?: HostInjectionContext\n}\n\nfunction readString(value: unknown): string {\n return typeof value === 'string' ? value : ''\n}\n\nfunction readNumber(value: unknown): number {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string') {\n const parsed = Number.parseInt(value, 10)\n if (Number.isFinite(parsed)) return parsed\n }\n return 0\n}\n\nfunction resolveView(tableId: string | null | undefined): CustomersAiInjectView {\n if (typeof tableId === 'string') {\n if (tableId.includes('deals')) return 'customers.deals.list'\n if (tableId.includes('companies')) return 'customers.companies.list'\n }\n return 'customers.people.list'\n}\n\nfunction buildPageContext(context: HostInjectionContext | undefined): CustomersAiInjectPageContext {\n const selectedIdsRaw = Array.isArray(context?.selectedRowIds) ? context?.selectedRowIds ?? [] : []\n const selectedIds = selectedIdsRaw.map(readString).filter((id) => id.length > 0)\n const selectedCount = selectedIds.length > 0\n ? selectedIds.length\n : readNumber(context?.selectedCount)\n const totalMatching = readNumber(context?.totalMatching ?? context?.total ?? context?.rowCount)\n const recordId = selectedIds.length > 0 ? selectedIds.join(',') : null\n return {\n view: resolveView(context?.tableId),\n recordType: null,\n recordId,\n extra: {\n selectedCount,\n totalMatching,\n },\n }\n}\n\nfunction useCustomerSuggestions(\n view: CustomersAiInjectView,\n hasSelection: boolean,\n selectedCount: number,\n): AiChatSuggestion[] {\n const t = useT()\n return React.useMemo(() => {\n if (view === 'customers.deals.list') {\n if (hasSelection) {\n return [\n {\n label: t(\n 'customers.ai_assistant.suggestions.summarizeSelectedDeals',\n 'Summarize selected deals',\n ),\n prompt: `Give me a summary of my ${selectedCount} selected deals \u2014 stage, value, and close date`,\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.atRiskSelectedDeals',\n 'Selected deals at risk',\n ),\n prompt: `Of my ${selectedCount} selected deals, which look at risk of slipping their close date?`,\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.companiesForSelectedDeals',\n 'Companies on selected deals',\n ),\n prompt: `List the companies associated with my ${selectedCount} selected deals`,\n icon: <Building2 className=\"size-4\" />,\n },\n ]\n }\n return [\n {\n label: t(\n 'customers.ai_assistant.suggestions.dealsClosingThisMonth',\n 'Deals closing this month',\n ),\n prompt: 'Which deals are closing this month?',\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.dealsAtRisk',\n 'Deals at risk of slipping',\n ),\n prompt: 'Show me deals at risk of slipping their close date',\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.totalOpenDealValue',\n 'Total open deal value',\n ),\n prompt: \"What's the total value of open deals across the pipeline?\",\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.topDealsByValue',\n 'Top deals by value',\n ),\n prompt: 'Show me the top 5 deals by value',\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.searchDeals',\n 'Search for a deal',\n ),\n prompt: 'Search for deals by title, company, or stage',\n icon: <Search className=\"size-4\" />,\n },\n ]\n }\n if (view === 'customers.companies.list') {\n if (hasSelection) {\n return [\n {\n label: t(\n 'customers.ai_assistant.suggestions.summarizeSelectedCompanies',\n 'Summarize selected companies',\n ),\n prompt: `Give me a summary of my ${selectedCount} selected companies \u2014 size, industry, and recent activity`,\n icon: <Building2 className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.dealsForSelectedCompanies',\n 'Show deals for selected companies',\n ),\n prompt: `Show me all deals associated with my ${selectedCount} selected companies`,\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.peopleAtSelectedCompanies',\n 'List people at selected companies',\n ),\n prompt: `List the contacts (people) associated with my ${selectedCount} selected companies`,\n icon: <Users className=\"size-4\" />,\n },\n ]\n }\n return [\n {\n label: t(\n 'customers.ai_assistant.suggestions.searchCompanies',\n 'Search for a company',\n ),\n prompt: 'Search for companies by name, industry, or tax ID',\n icon: <Search className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.topCompaniesByDeals',\n 'Top companies by deal value',\n ),\n prompt: 'Show me the companies with the highest open deal value',\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.companiesWithoutContacts',\n 'Companies missing contacts',\n ),\n prompt: 'Find companies that have no associated people yet',\n icon: <Users className=\"size-4\" />,\n },\n {\n label: t(\n 'customers.ai_assistant.suggestions.companiesActivityOverview',\n 'Activity overview',\n ),\n prompt: 'Give me an overview of recent company-level activities and interactions',\n icon: <Building2 className=\"size-4\" />,\n },\n ]\n }\n if (hasSelection) {\n return [\n {\n label: t('customers.ai_assistant.suggestions.summarizeSelected', 'Summarize selected contacts'),\n prompt: `Give me a summary of my ${selectedCount} selected contacts \u2014 key details and recent activity`,\n icon: <Users className=\"size-4\" />,\n },\n {\n label: t('customers.ai_assistant.suggestions.findDeals', 'Show deals for selected people'),\n prompt: `Show me all deals associated with my ${selectedCount} selected contacts`,\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t('customers.ai_assistant.suggestions.findCompanies', 'Find related companies'),\n prompt: `Find companies related to my ${selectedCount} selected contacts`,\n icon: <Building2 className=\"size-4\" />,\n },\n ]\n }\n return [\n {\n label: t('customers.ai_assistant.suggestions.searchPeople', 'Search for a contact'),\n prompt: 'Search for contacts by name, email, or company',\n icon: <Search className=\"size-4\" />,\n },\n {\n label: t('customers.ai_assistant.suggestions.recentDeals', 'Show recent deals'),\n prompt: 'Show me the most recent deals and their current stages',\n icon: <Handshake className=\"size-4\" />,\n },\n {\n label: t('customers.ai_assistant.suggestions.topCompanies', 'List top companies'),\n prompt: 'List companies with the most associated contacts and deals',\n icon: <Building2 className=\"size-4\" />,\n },\n {\n label: t('customers.ai_assistant.suggestions.activityOverview', 'Activity overview'),\n prompt: 'Give me an overview of recent customer activities and interactions',\n icon: <Users className=\"size-4\" />,\n },\n ]\n }, [view, hasSelection, selectedCount, t])\n}\n\nfunction useCustomerContextItems(pageContext: CustomersAiInjectPageContext): AiChatContextItem[] {\n const t = useT()\n return React.useMemo(() => {\n const items: AiChatContextItem[] = []\n const { selectedCount, totalMatching } = pageContext.extra\n const view = pageContext.view\n let selectedKey = 'customers.ai_assistant.context.selectedPeople'\n let selectedFallback = '{count} contacts selected'\n let matchingKey = 'customers.ai_assistant.context.matchingPeople'\n let matchingFallback = '{count} contacts in view'\n if (view === 'customers.companies.list') {\n selectedKey = 'customers.ai_assistant.context.selectedCompanies'\n selectedFallback = '{count} companies selected'\n matchingKey = 'customers.ai_assistant.context.matchingCompanies'\n matchingFallback = '{count} companies in view'\n } else if (view === 'customers.deals.list') {\n selectedKey = 'customers.ai_assistant.context.selectedDeals'\n selectedFallback = '{count} deals selected'\n matchingKey = 'customers.ai_assistant.context.matchingDeals'\n matchingFallback = '{count} deals in view'\n }\n if (selectedCount > 0) {\n items.push({ label: t(selectedKey, selectedFallback).replace('{count}', String(selectedCount)) })\n } else if (totalMatching > 0) {\n items.push({ label: t(matchingKey, matchingFallback).replace('{count}', String(totalMatching)) })\n }\n return items\n }, [pageContext, t])\n}\n\ninterface ViewCopy {\n welcomeDescriptionAll: string\n welcomeDescriptionSelection: string\n descriptionWithSelection: string\n triggerAriaLabel: string\n}\n\nfunction useViewCopy(view: CustomersAiInjectView, selectedCount: number): ViewCopy {\n const t = useT()\n return React.useMemo(() => {\n const replaceCount = (s: string) => s.replace('{count}', String(selectedCount))\n if (view === 'customers.deals.list') {\n return {\n welcomeDescriptionAll: t(\n 'customers.ai_assistant.sheet.welcomeDescriptionAllDeals',\n 'Ask about deals, pipeline, or close dates:',\n ),\n welcomeDescriptionSelection: replaceCount(\n t(\n 'customers.ai_assistant.sheet.welcomeDescriptionSelectionDeals',\n 'Ready to explore your {count} selected deals:',\n ),\n ),\n descriptionWithSelection: replaceCount(\n t(\n 'customers.ai_assistant.sheet.descriptionWithSelectionDeals',\n 'Working with {count} selected deals. Ask about their stage, value, and close dates.',\n ),\n ),\n triggerAriaLabel: t(\n 'customers.ai_assistant.trigger.ariaLabelDeals',\n 'Open AI assistant for deals',\n ),\n }\n }\n if (view === 'customers.companies.list') {\n return {\n welcomeDescriptionAll: t(\n 'customers.ai_assistant.sheet.welcomeDescriptionAllCompanies',\n 'Ask me anything about your companies, deals, and activities:',\n ),\n welcomeDescriptionSelection: replaceCount(\n t(\n 'customers.ai_assistant.sheet.welcomeDescriptionSelectionCompanies',\n 'Ready to explore your {count} selected companies:',\n ),\n ),\n descriptionWithSelection: replaceCount(\n t(\n 'customers.ai_assistant.sheet.descriptionWithSelectionCompanies',\n 'Working with {count} selected companies. Ask about their deals, contacts, and activities.',\n ),\n ),\n triggerAriaLabel: t(\n 'customers.ai_assistant.trigger.ariaLabelCompanies',\n 'Open AI assistant for companies',\n ),\n }\n }\n return {\n welcomeDescriptionAll: t(\n 'customers.ai_assistant.sheet.welcomeDescriptionAll',\n 'Ask me anything about your customers, companies, and deals:',\n ),\n welcomeDescriptionSelection: replaceCount(\n t(\n 'customers.ai_assistant.sheet.welcomeDescriptionSelection',\n 'Ready to explore your {count} selected contacts:',\n ),\n ),\n descriptionWithSelection: replaceCount(\n t(\n 'customers.ai_assistant.sheet.descriptionWithSelection',\n 'Working with {count} selected contacts. Ask about their details, deals, companies, and activities.',\n ),\n ),\n triggerAriaLabel: t(\n 'customers.ai_assistant.trigger.ariaLabel',\n 'Open AI assistant for people',\n ),\n }\n }, [view, selectedCount, t])\n}\n\ninterface CustomerAgentDescriptor {\n id: string\n label: string\n description: string\n icon: React.ReactNode\n}\n\nfunction useCustomerAgents(): CustomerAgentDescriptor[] {\n const t = useT()\n return React.useMemo(\n () => [\n {\n id: CUSTOMERS_AI_INJECT_AGENT_ID,\n label: t('customers.ai_assistant.agents.account.label', 'CRM Assistant'),\n description: t(\n 'customers.ai_assistant.agents.account.description',\n 'Explore people, companies, deals, and activities.',\n ),\n icon: <Sparkles className=\"size-4\" />,\n },\n ],\n [t],\n )\n}\n\nexport default function AiAssistantTriggerWidget({ context }: AiAssistantTriggerProps) {\n const t = useT()\n const dock = useAiDock()\n const [open, setOpen] = React.useState(false)\n const [popoverOpen, setPopoverOpen] = React.useState(false)\n const [activeAgent, setActiveAgent] = React.useState<string>(CUSTOMERS_AI_INJECT_AGENT_ID)\n const [lastAgent, setLastAgent] = React.useState<string | null>(null)\n const pageContext = React.useMemo(() => buildPageContext(context), [context])\n const agents = useCustomerAgents()\n\n const selectedCount = pageContext.extra.selectedCount\n const hasSelection = selectedCount > 0\n const suggestions = useCustomerSuggestions(pageContext.view, hasSelection, selectedCount)\n const contextItems = useCustomerContextItems(pageContext)\n const viewCopy = useViewCopy(pageContext.view, selectedCount)\n\n const openAgent = React.useCallback((agentId: string) => {\n setActiveAgent(agentId)\n setLastAgent(agentId)\n setPopoverOpen(false)\n if (dock.state.assistant?.agent === agentId) {\n dock.dock(dock.state.assistant)\n setOpen(false)\n return\n }\n setOpen(true)\n }, [dock])\n\n const handleSelectAgent = React.useCallback((agentId: string) => {\n openAgent(agentId)\n }, [openAgent])\n\n const handleMainTriggerClick = React.useCallback(() => {\n if (agents.length === 1) {\n openAgent(agents[0].id)\n return\n }\n if (lastAgent && agents.some((a) => a.id === lastAgent)) {\n openAgent(lastAgent)\n return\n }\n setPopoverOpen(true)\n }, [agents, lastAgent, openAgent])\n\n const handleDock = React.useCallback(() => {\n const agent = agents.find((a) => a.id === activeAgent) ?? agents[0]\n if (!agent) return\n dock.dock({\n agent: agent.id,\n label: agent.label,\n description: t('customers.ai_assistant.dock.subtitle', 'Customers'),\n pageContext: pageContext as unknown as Record<string, unknown>,\n placeholder: t(\n 'customers.ai_assistant.sheet.composerPlaceholder',\n 'Ask about people, companies, deals...',\n ),\n suggestions,\n contextItems,\n welcomeTitle: t('customers.ai_assistant.sheet.welcomeTitle', 'CRM Assistant'),\n welcomeDescription: hasSelection\n ? viewCopy.welcomeDescriptionSelection\n : viewCopy.welcomeDescriptionAll,\n })\n setOpen(false)\n }, [\n activeAgent,\n agents,\n contextItems,\n dock,\n hasSelection,\n pageContext,\n suggestions,\n t,\n viewCopy,\n ])\n\n const triggerLabel = viewCopy.triggerAriaLabel\n\n const labelText = t('customers.ai_assistant.trigger.label', 'AI')\n const moreAgentsLabel = t(\n 'customers.ai_assistant.trigger.moreAgentsAriaLabel',\n 'Choose an AI assistant',\n )\n\n return (\n <>\n <div className=\"inline-flex items-center\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={handleMainTriggerClick}\n data-ai-customers-inject-trigger=\"\"\n aria-label={triggerLabel}\n title={triggerLabel}\n className={cn(\n 'relative',\n 'hover:bg-brand-violet/10',\n agents.length > 1 && 'rounded-r-none border-r-0',\n )}\n >\n <Sparkles\n className=\"size-4 text-brand-violet\"\n aria-hidden\n />\n <span>{labelText}</span>\n {hasSelection ? (\n <span\n className=\"absolute -top-1 -right-1 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-[10px] font-medium leading-none text-primary-foreground\"\n data-ai-customers-inject-selected-count={selectedCount}\n >\n {selectedCount}\n </span>\n ) : null}\n </Button>\n {agents.length > 1 ? (\n <Popover open={popoverOpen} onOpenChange={setPopoverOpen}>\n <PopoverTrigger asChild>\n <IconButton\n type=\"button\"\n variant=\"outline\"\n size=\"lg\"\n aria-label={moreAgentsLabel}\n title={moreAgentsLabel}\n className=\"rounded-l-none\"\n data-ai-customers-inject-picker=\"\"\n >\n <ChevronDown className=\"size-4\" aria-hidden />\n </IconButton>\n </PopoverTrigger>\n <PopoverContent align=\"end\" className=\"w-72 p-1\">\n <div className=\"px-3 pt-2 pb-1 text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n {t('customers.ai_assistant.popover.heading', 'AI assistants')}\n </div>\n <div className=\"flex flex-col gap-0.5\">\n {agents.map((agent) => (\n <button\n key={agent.id}\n type=\"button\"\n onClick={() => handleSelectAgent(agent.id)}\n data-ai-customers-inject-agent-option={agent.id}\n className=\"flex items-start gap-2 rounded-sm px-2 py-2 text-left text-sm hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground focus-visible:outline-none\"\n >\n <span className=\"mt-0.5 inline-flex size-6 items-center justify-center rounded-full bg-secondary text-secondary-foreground\">\n {agent.icon}\n </span>\n <span className=\"flex-1 min-w-0\">\n <span className=\"block font-medium leading-tight\">{agent.label}</span>\n <span className=\"block text-xs text-muted-foreground leading-snug\">\n {agent.description}\n </span>\n </span>\n </button>\n ))}\n </div>\n </PopoverContent>\n </Popover>\n ) : null}\n </div>\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogContent\n className={cn(\n // Mobile: full-screen sheet (no rounded corners, fills the\n // viewport). Desktop (\u2265sm): right-anchored side sheet.\n // The Dialog primitive ships a centering transform at the sm\n // breakpoint (`sm:top-1/2 sm:left-1/2 sm:-translate-x-1/2\n // sm:-translate-y-1/2 sm:inset-auto`); each must be overridden\n // at the same `sm:` breakpoint or the panel renders half off\n // the viewport.\n 'top-0 left-0 right-0 bottom-0 translate-x-0 translate-y-0 max-w-none w-screen h-svh max-h-svh rounded-none',\n 'sm:top-0 sm:bottom-0 sm:right-0 sm:left-auto sm:translate-x-0 sm:translate-y-0',\n 'sm:max-w-xl sm:w-[36rem] sm:rounded-l-2xl sm:h-screen sm:max-h-screen',\n 'flex flex-col gap-3 p-4 z-[70]',\n )}\n data-ai-customers-inject-sheet=\"\"\n >\n <DialogHeader>\n <div className=\"flex items-center gap-3 pr-8\">\n {/* Dock button lives on the LEFT \u2014 the Dialog primitive\n auto-renders an X close button absolutely positioned in\n the top-right corner, so anything we drop in the header's\n right side visually collides with it. Mobile hides the\n dock entirely (the side panel is desktop-only). */}\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t('customers.ai_assistant.sheet.dock', 'Dock to side')}\n title={t('customers.ai_assistant.sheet.dock', 'Dock to side')}\n onClick={handleDock}\n data-ai-customers-inject-dock=\"\"\n className=\"hidden lg:inline-flex shrink-0\"\n >\n <PanelRightOpen className=\"size-4\" aria-hidden />\n </IconButton>\n <DialogTitle className=\"flex-1 min-w-0 truncate\">\n {t('customers.ai_assistant.sheet.title', 'Customers AI assistant')}\n </DialogTitle>\n {hasSelection ? (\n <span\n className=\"shrink-0 inline-flex items-center rounded-full border border-border bg-secondary px-2 py-0.5 text-xs text-secondary-foreground\"\n data-ai-customers-inject-selection-pill=\"\"\n data-ai-customers-inject-selected-count={selectedCount}\n >\n {t(\n 'customers.ai_assistant.sheet.selectionPill',\n 'Acting on {count} selected',\n ).replace('{count}', String(selectedCount))}\n </span>\n ) : null}\n </div>\n <DialogDescription>\n {hasSelection\n ? viewCopy.descriptionWithSelection\n : t(\n 'customers.ai_assistant.sheet.description',\n 'Your CRM assistant. Ask about people, companies, deals, and activities.',\n )}\n </DialogDescription>\n </DialogHeader>\n <CustomersChatBody\n activeAgent={activeAgent}\n pageContext={pageContext}\n suggestions={suggestions}\n contextItems={contextItems}\n hasSelection={hasSelection}\n selectedCount={selectedCount}\n viewCopy={viewCopy}\n />\n </DialogContent>\n </Dialog>\n </>\n )\n}\n\ninterface CustomersChatBodyProps {\n activeAgent: string\n pageContext: CustomersAiInjectPageContext\n suggestions: AiChatSuggestion[]\n contextItems: AiChatContextItem[]\n hasSelection: boolean\n selectedCount: number\n viewCopy: ViewCopy\n}\n\nfunction CustomersChatBody({\n activeAgent,\n pageContext,\n suggestions,\n contextItems,\n hasSelection,\n selectedCount,\n viewCopy,\n}: CustomersChatBodyProps) {\n const t = useT()\n const sessions = useAiChatSessions()\n const session = sessions.getActiveSession(activeAgent)\n\n // Lazily ensure an open session exists. Running `ensureSession` inside an\n // effect (not inline during render) keeps the provider's setState calls\n // outside of the render phase. The first frame may render without a\n // session \u2014 that's fine, we render the tab strip alone until the next\n // tick when the new session is committed and `getActiveSession` returns it.\n React.useEffect(() => {\n if (!session) sessions.ensureSession(activeAgent)\n }, [activeAgent, session, sessions])\n\n return (\n <>\n <ChatPaneTabs agentId={activeAgent} className=\"border-b\" />\n <div className=\"min-h-0 flex-1\" data-ai-customers-inject-chat-container=\"\">\n {session ? (\n <AiChat\n // `key` forces a fresh mount when the active tab changes so the\n // AI SDK's status doesn't leak across sessions.\n key={session.id}\n agent={activeAgent}\n conversationId={session.conversationId}\n pageContext={pageContext as unknown as Record<string, unknown>}\n className=\"h-full\"\n placeholder={t(\n 'customers.ai_assistant.sheet.composerPlaceholder',\n 'Ask about people, companies, deals...',\n )}\n suggestions={suggestions}\n contextItems={contextItems}\n welcomeTitle={t('customers.ai_assistant.sheet.welcomeTitle', 'CRM Assistant')}\n welcomeDescription={\n hasSelection\n ? viewCopy.welcomeDescriptionSelection\n : viewCopy.welcomeDescriptionAll\n }\n />\n ) : null}\n </div>\n </>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -3,8 +3,8 @@ const widget = {
3
3
  metadata: {
4
4
  id: "customers.injection.ai-assistant-trigger",
5
5
  title: "Customers AI Assistant Trigger",
6
- description: 'Renders an "Ask AI" button in the people list header that opens a sheet embedding the customers account assistant.',
7
- features: ["customers.people.view", "ai_assistant.view"],
6
+ description: 'Renders an "Ask AI" button in the people / companies / deals list headers that opens a sheet embedding the customers account assistant.',
7
+ features: ["ai_assistant.view"],
8
8
  requiredModules: ["ai_assistant"],
9
9
  priority: 100,
10
10
  enabled: true
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/customers/widgets/injection/ai-assistant-trigger/widget.ts"],
4
- "sourcesContent": ["import type { InjectionWidgetModule } from '@open-mercato/shared/modules/widgets/injection'\nimport AiAssistantTriggerWidget from './widget.client'\n\n/**\n * Step 4.10 \u2014 Backend AiChat injection example.\n *\n * Demonstrates how a third-party module can drop `<AiChat>` onto a page\n * it does NOT own (the customers People list) via the existing widget\n * injection system. Targets spot `data-table:customers.people.list:header`,\n * which is owned by the `DataTable` primitive in `packages/ui`.\n *\n * The trigger button opens a right-side sheet embedding\n * `<AiChat agent=\"customers.account_assistant\" pageContext={...} />`.\n * `pageContext` follows the spec \u00A710.1 shape:\n *\n * { view: 'customers.people.list',\n * recordType: null,\n * recordId: string, // \"\" or comma-separated UUIDs\n * extra: { selectedCount, totalMatching } }\n *\n * Feature-gated behind `customers.people.view` + `ai_assistant.view`.\n */\nconst widget: InjectionWidgetModule<Record<string, unknown>, Record<string, unknown>> = {\n metadata: {\n id: 'customers.injection.ai-assistant-trigger',\n title: 'Customers AI Assistant Trigger',\n description:\n 'Renders an \"Ask AI\" button in the people list header that opens a sheet embedding the customers account assistant.',\n features: ['customers.people.view', 'ai_assistant.view'],\n requiredModules: ['ai_assistant'],\n priority: 100,\n enabled: true,\n },\n Widget: AiAssistantTriggerWidget,\n}\n\nexport default widget\n"],
5
- "mappings": "AACA,OAAO,8BAA8B;AAqBrC,MAAM,SAAkF;AAAA,EACtF,UAAU;AAAA,IACR,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,aACE;AAAA,IACF,UAAU,CAAC,yBAAyB,mBAAmB;AAAA,IACvD,iBAAiB,CAAC,cAAc;AAAA,IAChC,UAAU;AAAA,IACV,SAAS;AAAA,EACX;AAAA,EACA,QAAQ;AACV;AAEA,IAAO,iBAAQ;",
4
+ "sourcesContent": ["import type { InjectionWidgetModule } from '@open-mercato/shared/modules/widgets/injection'\nimport AiAssistantTriggerWidget from './widget.client'\n\n/**\n * Step 4.10 \u2014 Backend AiChat injection example.\n *\n * Demonstrates how a third-party module can drop `<AiChat>` onto a page\n * it does NOT own (the customers People / Companies / Deals lists) via\n * the existing widget injection system. Targets DataTable header /\n * search-trailing spots owned by the `DataTable` primitive in\n * `packages/ui`.\n *\n * The trigger button opens a right-side sheet embedding\n * `<AiChat agent=\"customers.account_assistant\" pageContext={...} />`.\n * `pageContext` follows the spec \u00A710.1 shape:\n *\n * { view: 'customers.people.list' | 'customers.companies.list' | 'customers.deals.list',\n * recordType: null,\n * recordId: string, // \"\" or comma-separated UUIDs\n * extra: { selectedCount, totalMatching } }\n *\n * Feature-gated behind `ai_assistant.view` only \u2014 the host CRM page\n * already enforces its own `customers.people.view`/`companies.view`/\n * `deals.view` guard, so the widget gate just needs to ensure the user\n * has AI assistant access. (Earlier versions also listed\n * `customers.people.view`, which is AND-evaluated by the registry and\n * hid the trigger from companies-only or deals-only viewers.)\n */\nconst widget: InjectionWidgetModule<Record<string, unknown>, Record<string, unknown>> = {\n metadata: {\n id: 'customers.injection.ai-assistant-trigger',\n title: 'Customers AI Assistant Trigger',\n description:\n 'Renders an \"Ask AI\" button in the people / companies / deals list headers that opens a sheet embedding the customers account assistant.',\n features: ['ai_assistant.view'],\n requiredModules: ['ai_assistant'],\n priority: 100,\n enabled: true,\n },\n Widget: AiAssistantTriggerWidget,\n}\n\nexport default widget\n"],
5
+ "mappings": "AACA,OAAO,8BAA8B;AA2BrC,MAAM,SAAkF;AAAA,EACtF,UAAU;AAAA,IACR,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,aACE;AAAA,IACF,UAAU,CAAC,mBAAmB;AAAA,IAC9B,iBAAiB,CAAC,cAAc;AAAA,IAChC,UAAU;AAAA,IACV,SAAS;AAAA,EACX;AAAA,EACA,QAAQ;AACV;AAEA,IAAO,iBAAQ;",
6
6
  "names": []
7
7
  }
@@ -11,6 +11,12 @@ const injectionTable = {
11
11
  priority: 100
12
12
  }
13
13
  ],
14
+ "data-table:customers.deals.list:search-trailing": [
15
+ {
16
+ widgetId: "customers.injection.ai-assistant-trigger",
17
+ priority: 100
18
+ }
19
+ ],
14
20
  "detail:customers.deal:header": [
15
21
  {
16
22
  widgetId: "customers.injection.ai-deal-detail-trigger",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/customers/widgets/injection-table.ts"],
4
- "sourcesContent": ["import type { ModuleInjectionTable } from '@open-mercato/shared/modules/widgets/injection'\n\n/**\n * Step 4.10 / Step 5.15 \u2014 customers module injection table.\n *\n * - Step 4.10 drops the `ai-assistant-trigger` widget on the People-list\n * `DataTable` `:search-trailing` slot, which renders adjacent to the\n * list search input. The previous mount point was the `:header` slot\n * (separate row); the round icon-only trigger now lives next to the\n * search box for a tighter, single-row toolbar.\n * - Step 5.15 (Phase 3 WS-D) adds the `ai-deal-detail-trigger` widget on\n * the Deal detail page header spot (`detail:customers.deal:header`).\n *\n * Both widgets embed `<AiChat agent=\"customers.account_assistant\" \u2026>`\n * with a selection- or record-aware `pageContext`. The page files\n * themselves only register the shared `<InjectionSpot>` mount point \u2014\n * the trigger, sheet, and chat surface live entirely in the injection\n * widgets so third-party modules can copy the pattern unchanged.\n */\nexport const injectionTable: ModuleInjectionTable = {\n 'data-table:customers.people.list:search-trailing': [\n {\n widgetId: 'customers.injection.ai-assistant-trigger',\n priority: 100,\n },\n ],\n 'data-table:customers.companies.list:search-trailing': [\n {\n widgetId: 'customers.injection.ai-assistant-trigger',\n priority: 100,\n },\n ],\n 'detail:customers.deal:header': [\n {\n widgetId: 'customers.injection.ai-deal-detail-trigger',\n priority: 100,\n },\n ],\n}\n\nexport default injectionTable\n"],
5
- "mappings": "AAmBO,MAAM,iBAAuC;AAAA,EAClD,oDAAoD;AAAA,IAClD;AAAA,MACE,UAAU;AAAA,MACV,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA,uDAAuD;AAAA,IACrD;AAAA,MACE,UAAU;AAAA,MACV,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA,gCAAgC;AAAA,IAC9B;AAAA,MACE,UAAU;AAAA,MACV,UAAU;AAAA,IACZ;AAAA,EACF;AACF;AAEA,IAAO,0BAAQ;",
4
+ "sourcesContent": ["import type { ModuleInjectionTable } from '@open-mercato/shared/modules/widgets/injection'\n\n/**\n * Step 4.10 / Step 5.15 \u2014 customers module injection table.\n *\n * - Step 4.10 drops the `ai-assistant-trigger` widget on the People-list\n * `DataTable` `:search-trailing` slot, which renders adjacent to the\n * list search input. The previous mount point was the `:header` slot\n * (separate row); the round icon-only trigger now lives next to the\n * search box for a tighter, single-row toolbar.\n * - Step 5.15 (Phase 3 WS-D) adds the `ai-deal-detail-trigger` widget on\n * the Deal detail page header spot (`detail:customers.deal:header`).\n *\n * Both widgets embed `<AiChat agent=\"customers.account_assistant\" \u2026>`\n * with a selection- or record-aware `pageContext`. The page files\n * themselves only register the shared `<InjectionSpot>` mount point \u2014\n * the trigger, sheet, and chat surface live entirely in the injection\n * widgets so third-party modules can copy the pattern unchanged.\n */\nexport const injectionTable: ModuleInjectionTable = {\n 'data-table:customers.people.list:search-trailing': [\n {\n widgetId: 'customers.injection.ai-assistant-trigger',\n priority: 100,\n },\n ],\n 'data-table:customers.companies.list:search-trailing': [\n {\n widgetId: 'customers.injection.ai-assistant-trigger',\n priority: 100,\n },\n ],\n 'data-table:customers.deals.list:search-trailing': [\n {\n widgetId: 'customers.injection.ai-assistant-trigger',\n priority: 100,\n },\n ],\n 'detail:customers.deal:header': [\n {\n widgetId: 'customers.injection.ai-deal-detail-trigger',\n priority: 100,\n },\n ],\n}\n\nexport default injectionTable\n"],
5
+ "mappings": "AAmBO,MAAM,iBAAuC;AAAA,EAClD,oDAAoD;AAAA,IAClD;AAAA,MACE,UAAU;AAAA,MACV,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA,uDAAuD;AAAA,IACrD;AAAA,MACE,UAAU;AAAA,MACV,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA,mDAAmD;AAAA,IACjD;AAAA,MACE,UAAU;AAAA,MACV,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA,gCAAgC;AAAA,IAC9B;AAAA,MACE,UAAU;AAAA,MACV,UAAU;AAAA,IACZ;AAAA,EACF;AACF;AAEA,IAAO,0BAAQ;",
6
6
  "names": []
7
7
  }
@@ -27,25 +27,38 @@ function isPerspectivesState(value) {
27
27
  if (!Array.isArray(record.rolePerspectives) || record.rolePerspectives.some((item) => !isResolvedRolePerspective(item))) return false;
28
28
  return true;
29
29
  }
30
+ function maybeMigrateLegacyFilterValues(settings) {
31
+ const filters = settings.filters;
32
+ if (!filters || typeof filters !== "object") return settings;
33
+ const record = filters;
34
+ if ("v" in record && record.v === 2) return settings;
35
+ if ("root" in record) return settings;
36
+ if (typeof console !== "undefined") {
37
+ console.warn("[perspectives] Dropping legacy filterValues shape; please re-create the perspective with the new filter UI.");
38
+ }
39
+ return { ...settings, filters: void 0 };
40
+ }
30
41
  function toResolvedPerspective(entity) {
42
+ const settings = maybeMigrateLegacyFilterValues(entity.settingsJson ?? {});
31
43
  return {
32
44
  id: entity.id,
33
45
  name: entity.name,
34
46
  tableId: entity.tableId,
35
47
  isDefault: !!entity.isDefault,
36
- settings: entity.settingsJson ?? {},
48
+ settings,
37
49
  createdAt: entity.createdAt.toISOString(),
38
50
  updatedAt: entity.updatedAt ? entity.updatedAt.toISOString() : null
39
51
  };
40
52
  }
41
53
  function toResolvedRolePerspective(entity) {
54
+ const settings = maybeMigrateLegacyFilterValues(entity.settingsJson ?? {});
42
55
  return {
43
56
  id: entity.id,
44
57
  roleId: entity.roleId,
45
58
  tableId: entity.tableId,
46
59
  name: entity.name,
47
60
  isDefault: !!entity.isDefault,
48
- settings: entity.settingsJson ?? {},
61
+ settings,
49
62
  tenantId: nullish(entity.tenantId),
50
63
  organizationId: nullish(entity.organizationId),
51
64
  createdAt: entity.createdAt.toISOString(),
@@ -277,6 +290,7 @@ export {
277
290
  clearRolePerspectives,
278
291
  deleteUserPerspective,
279
292
  loadPerspectivesState,
293
+ maybeMigrateLegacyFilterValues,
280
294
  saveRolePerspectives,
281
295
  saveUserPerspective
282
296
  };