@aglyn/plugins-crm 1.0.0-beta.218 → 1.0.0-beta.220

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 (346) hide show
  1. package/README.md +2 -0
  2. package/package.json +14 -14
  3. package/src/index.d.ts +1 -0
  4. package/src/index.js.map +1 -1
  5. package/src/lib/components/companies-bulk-bar.js +12 -4
  6. package/src/lib/components/companies-bulk-bar.js.map +1 -1
  7. package/src/lib/components/company-detail-page.js +11 -0
  8. package/src/lib/components/company-detail-page.js.map +1 -1
  9. package/src/lib/components/company-edit-drawer.js +12 -2
  10. package/src/lib/components/company-edit-drawer.js.map +1 -1
  11. package/src/lib/components/company-import-drawer.d.ts +1 -1
  12. package/src/lib/components/company-picker.js +10 -3
  13. package/src/lib/components/company-picker.js.map +1 -1
  14. package/src/lib/components/contact-associations-card.js +4 -3
  15. package/src/lib/components/contact-associations-card.js.map +1 -1
  16. package/src/lib/components/contact-detail-page.d.ts +4 -4
  17. package/src/lib/components/contact-detail-page.js +50 -53
  18. package/src/lib/components/contact-detail-page.js.map +1 -1
  19. package/src/lib/components/contact-list-columns.d.ts +3 -0
  20. package/src/lib/components/contact-list-columns.js +21 -5
  21. package/src/lib/components/contact-list-columns.js.map +1 -1
  22. package/src/lib/components/contact-merge-dialog.js +2 -1
  23. package/src/lib/components/contact-merge-dialog.js.map +1 -1
  24. package/src/lib/components/contact-timeline-card.js +4 -3
  25. package/src/lib/components/contact-timeline-card.js.map +1 -1
  26. package/src/lib/components/contacts-bulk-bar.d.ts +3 -3
  27. package/src/lib/components/contacts-bulk-bar.js +24 -51
  28. package/src/lib/components/contacts-bulk-bar.js.map +1 -1
  29. package/src/lib/components/contacts-section.js +9 -3
  30. package/src/lib/components/contacts-section.js.map +1 -1
  31. package/src/lib/components/crm-booking-zone.d.ts +8 -2
  32. package/src/lib/components/crm-booking-zone.js.map +1 -1
  33. package/src/lib/components/crm-export-all-button.d.ts +1 -1
  34. package/src/lib/components/crm-export-all-button.js.map +1 -1
  35. package/src/lib/components/crm-glance-card.js +2 -1
  36. package/src/lib/components/crm-glance-card.js.map +1 -1
  37. package/src/lib/components/crm-send-email-dialog.js +9 -6
  38. package/src/lib/components/crm-send-email-dialog.js.map +1 -1
  39. package/src/lib/components/csv-import-drawer.js +2 -2
  40. package/src/lib/components/csv-import-drawer.js.map +1 -1
  41. package/src/lib/components/deal-detail-page.js +18 -2
  42. package/src/lib/components/deal-detail-page.js.map +1 -1
  43. package/src/lib/components/deal-edit-drawer.js +10 -0
  44. package/src/lib/components/deal-edit-drawer.js.map +1 -1
  45. package/src/lib/components/deal-products-card.js +27 -16
  46. package/src/lib/components/deal-products-card.js.map +1 -1
  47. package/src/lib/components/deals-bulk-bar.js +13 -5
  48. package/src/lib/components/deals-bulk-bar.js.map +1 -1
  49. package/src/lib/components/lead-campaigns-card.d.ts +4 -4
  50. package/src/lib/components/lead-campaigns-card.js +15 -8
  51. package/src/lib/components/lead-campaigns-card.js.map +1 -1
  52. package/src/lib/components/lead-detail-page.js +25 -5
  53. package/src/lib/components/lead-detail-page.js.map +1 -1
  54. package/src/lib/components/lead-properties-card.js +10 -0
  55. package/src/lib/components/lead-properties-card.js.map +1 -1
  56. package/src/lib/components/lead-status-chip.js +3 -1
  57. package/src/lib/components/lead-status-chip.js.map +1 -1
  58. package/src/lib/components/lead-surfaces-note.js +4 -5
  59. package/src/lib/components/lead-surfaces-note.js.map +1 -1
  60. package/src/lib/components/lead-unqualify-dialog.js +6 -0
  61. package/src/lib/components/lead-unqualify-dialog.js.map +1 -1
  62. package/src/lib/components/leads-bulk-bar.js +34 -11
  63. package/src/lib/components/leads-bulk-bar.js.map +1 -1
  64. package/src/lib/components/leads-section.js +41 -8
  65. package/src/lib/components/leads-section.js.map +1 -1
  66. package/src/lib/components/new-lead-drawer.js +5 -4
  67. package/src/lib/components/new-lead-drawer.js.map +1 -1
  68. package/src/lib/components/org-lead-surfaces-note.js +6 -2
  69. package/src/lib/components/org-lead-surfaces-note.js.map +1 -1
  70. package/src/lib/components/recipes-card.js +9 -4
  71. package/src/lib/components/recipes-card.js.map +1 -1
  72. package/src/lib/components/record-sharing-card.d.ts +57 -0
  73. package/src/lib/components/record-sharing-card.js +244 -0
  74. package/src/lib/components/record-sharing-card.js.map +1 -0
  75. package/src/lib/components/reports/activity-card.js +4 -3
  76. package/src/lib/components/reports/activity-card.js.map +1 -1
  77. package/src/lib/components/reports/closed-deals-card.js +4 -3
  78. package/src/lib/components/reports/closed-deals-card.js.map +1 -1
  79. package/src/lib/components/reports/contacts-mix-card.js +3 -2
  80. package/src/lib/components/reports/contacts-mix-card.js.map +1 -1
  81. package/src/lib/components/reports/contacts-trend-card.js +2 -1
  82. package/src/lib/components/reports/contacts-trend-card.js.map +1 -1
  83. package/src/lib/components/reports/forecast-card.js +3 -2
  84. package/src/lib/components/reports/forecast-card.js.map +1 -1
  85. package/src/lib/components/reports/lead-funnel-card.js +8 -6
  86. package/src/lib/components/reports/lead-funnel-card.js.map +1 -1
  87. package/src/lib/components/reports/pipeline-card.js +4 -3
  88. package/src/lib/components/reports/pipeline-card.js.map +1 -1
  89. package/src/lib/components/reports/report-format.d.ts +1 -1
  90. package/src/lib/components/reports/report-format.js.map +1 -1
  91. package/src/lib/components/reports/report-scope.d.ts +1 -1
  92. package/src/lib/components/reports/report-scope.js.map +1 -1
  93. package/src/lib/components/reports/source-conversion-card.js +2 -1
  94. package/src/lib/components/reports/source-conversion-card.js.map +1 -1
  95. package/src/lib/components/reports/tasks-card.js +2 -1
  96. package/src/lib/components/reports/tasks-card.js.map +1 -1
  97. package/src/lib/components/reports/won-lost-by-owner-card.js +3 -2
  98. package/src/lib/components/reports/won-lost-by-owner-card.js.map +1 -1
  99. package/src/lib/components/reports-section.js +4 -4
  100. package/src/lib/components/reports-section.js.map +1 -1
  101. package/src/lib/components/settings-section.js +7 -0
  102. package/src/lib/components/settings-section.js.map +1 -1
  103. package/src/lib/components/share-records-dialog.d.ts +28 -0
  104. package/src/lib/components/share-records-dialog.js +184 -0
  105. package/src/lib/components/share-records-dialog.js.map +1 -0
  106. package/src/lib/components/sharing-rule-drawer.d.ts +37 -0
  107. package/src/lib/components/sharing-rule-drawer.js +374 -0
  108. package/src/lib/components/sharing-rule-drawer.js.map +1 -0
  109. package/src/lib/components/sharing-rules-card.d.ts +24 -0
  110. package/src/lib/components/sharing-rules-card.js +287 -0
  111. package/src/lib/components/sharing-rules-card.js.map +1 -0
  112. package/src/lib/components/use-crm-api.d.ts +1 -1
  113. package/src/lib/components/use-crm-api.js.map +1 -1
  114. package/src/lib/components/use-site-route-context.d.ts +25 -0
  115. package/src/lib/components/use-site-route-context.js +58 -0
  116. package/src/lib/components/use-site-route-context.js.map +1 -0
  117. package/src/lib/constants/api-routes.d.ts +31 -0
  118. package/src/lib/constants/api-routes.js +27 -1
  119. package/src/lib/constants/api-routes.js.map +1 -1
  120. package/src/lib/constants/bundle-common.d.ts +2 -0
  121. package/src/lib/constants/bundle-common.js +1 -0
  122. package/src/lib/constants/bundle-common.js.map +1 -1
  123. package/src/lib/declarations.console-server.d.ts +41 -0
  124. package/src/lib/declarations.console-server.js +115 -0
  125. package/src/lib/declarations.console-server.js.map +1 -0
  126. package/src/lib/declarations.server.d.ts +12 -0
  127. package/src/lib/declarations.server.js +84 -1
  128. package/src/lib/declarations.server.js.map +1 -1
  129. package/src/lib/hooks/use-contact-remove.d.ts +35 -0
  130. package/src/lib/hooks/use-contact-remove.js +77 -0
  131. package/src/lib/hooks/use-contact-remove.js.map +1 -0
  132. package/src/lib/hooks/use-crm-campaigns.d.ts +2 -2
  133. package/src/lib/hooks/use-crm-campaigns.js +3 -3
  134. package/src/lib/hooks/use-crm-campaigns.js.map +1 -1
  135. package/src/lib/hooks/use-crm-sharing.d.ts +30 -0
  136. package/src/lib/hooks/use-crm-sharing.js +109 -0
  137. package/src/lib/hooks/use-crm-sharing.js.map +1 -0
  138. package/src/lib/model/campaign-filing-activity.d.ts +1 -1
  139. package/src/lib/model/campaign-filing-activity.js +1 -1
  140. package/src/lib/model/campaign-filing-activity.js.map +1 -1
  141. package/src/lib/model/companies-csv.d.ts +1 -1
  142. package/src/lib/model/companies-csv.js +2 -2
  143. package/src/lib/model/companies-csv.js.map +1 -1
  144. package/src/lib/model/contact-holder.d.ts +68 -0
  145. package/src/lib/model/contact-holder.js +129 -0
  146. package/src/lib/model/contact-holder.js.map +1 -0
  147. package/src/lib/model/contact-interaction-links.d.ts +46 -0
  148. package/src/lib/model/contact-interaction-links.js +65 -0
  149. package/src/lib/model/contact-interaction-links.js.map +1 -0
  150. package/src/lib/model/contact-merge.d.ts +136 -0
  151. package/src/lib/model/contact-merge.js +412 -0
  152. package/src/lib/model/contact-merge.js.map +1 -0
  153. package/src/lib/model/contact-record.d.ts +7 -1
  154. package/src/lib/model/contact-record.js +7 -6
  155. package/src/lib/model/contact-record.js.map +1 -1
  156. package/src/lib/model/contact-remove.d.ts +48 -0
  157. package/src/lib/model/contact-remove.js +22 -0
  158. package/src/lib/model/contact-remove.js.map +1 -0
  159. package/src/lib/model/contacts-bulk-writes.d.ts +2 -71
  160. package/src/lib/model/contacts-bulk-writes.js +2 -82
  161. package/src/lib/model/contacts-bulk-writes.js.map +1 -1
  162. package/src/lib/model/contacts-csv.d.ts +2 -2
  163. package/src/lib/model/contacts-csv.js +2 -2
  164. package/src/lib/model/contacts-csv.js.map +1 -1
  165. package/src/lib/model/crm-csv.d.ts +239 -0
  166. package/src/lib/model/crm-csv.js +467 -0
  167. package/src/lib/model/crm-csv.js.map +1 -0
  168. package/src/lib/model/crm-digest.d.ts +227 -0
  169. package/src/lib/model/crm-digest.js +336 -0
  170. package/src/lib/model/crm-digest.js.map +1 -0
  171. package/src/lib/model/crm-import.d.ts +7 -1
  172. package/src/lib/model/crm-import.js +2 -1
  173. package/src/lib/model/crm-import.js.map +1 -1
  174. package/src/lib/model/crm-inbound.d.ts +162 -0
  175. package/src/lib/model/crm-inbound.js +208 -0
  176. package/src/lib/model/crm-inbound.js.map +1 -0
  177. package/src/lib/model/crm-lead-import.d.ts +1 -1
  178. package/src/lib/model/crm-lead-import.js +4 -4
  179. package/src/lib/model/crm-lead-import.js.map +1 -1
  180. package/src/lib/model/crm-record-routes.d.ts +5 -0
  181. package/src/lib/model/crm-record-routes.js +7 -0
  182. package/src/lib/model/crm-record-routes.js.map +1 -1
  183. package/src/lib/model/crm-reports.d.ts +467 -0
  184. package/src/lib/model/crm-reports.js +667 -0
  185. package/src/lib/model/crm-reports.js.map +1 -0
  186. package/src/lib/model/crm-routes.d.ts +6 -5
  187. package/src/lib/model/crm-routes.js +7 -7
  188. package/src/lib/model/crm-routes.js.map +1 -1
  189. package/src/lib/model/crm-search-sources.d.ts +15 -0
  190. package/src/lib/model/crm-search-sources.js +162 -0
  191. package/src/lib/model/crm-search-sources.js.map +1 -0
  192. package/src/lib/model/crm-sharing-api.d.ts +32 -0
  193. package/src/lib/model/crm-sharing-api.js +59 -0
  194. package/src/lib/model/crm-sharing-api.js.map +1 -0
  195. package/src/lib/model/crm-sharing.d.ts +333 -0
  196. package/src/lib/model/crm-sharing.js +603 -0
  197. package/src/lib/model/crm-sharing.js.map +1 -0
  198. package/src/lib/model/deals-csv.d.ts +1 -1
  199. package/src/lib/model/deals-csv.js +4 -5
  200. package/src/lib/model/deals-csv.js.map +1 -1
  201. package/src/lib/model/lead-filters.d.ts +5 -4
  202. package/src/lib/model/lead-filters.js +7 -5
  203. package/src/lib/model/lead-filters.js.map +1 -1
  204. package/src/lib/model/leads-csv.d.ts +1 -1
  205. package/src/lib/model/leads-csv.js +1 -1
  206. package/src/lib/model/leads-csv.js.map +1 -1
  207. package/src/lib/model/tasks-csv.d.ts +1 -1
  208. package/src/lib/model/tasks-csv.js +1 -1
  209. package/src/lib/model/tasks-csv.js.map +1 -1
  210. package/src/lib/plan-entitlements.d.ts +75 -0
  211. package/src/lib/plan-entitlements.js +56 -0
  212. package/src/lib/plan-entitlements.js.map +1 -0
  213. package/src/lib/plugin.js +39 -0
  214. package/src/lib/plugin.js.map +1 -1
  215. package/src/lib/server/api-v1/contacts-merge.d.ts +20 -0
  216. package/src/lib/server/api-v1/contacts-merge.js +120 -0
  217. package/src/lib/server/api-v1/contacts-merge.js.map +1 -0
  218. package/src/lib/server/api-v1/contacts.d.ts +18 -0
  219. package/src/lib/server/api-v1/contacts.js +1076 -0
  220. package/src/lib/server/api-v1/contacts.js.map +1 -0
  221. package/src/lib/server/api-v1/crm-activities.d.ts +18 -0
  222. package/src/lib/server/api-v1/crm-activities.js +269 -0
  223. package/src/lib/server/api-v1/crm-activities.js.map +1 -0
  224. package/src/lib/server/api-v1/crm-companies.d.ts +18 -0
  225. package/src/lib/server/api-v1/crm-companies.js +391 -0
  226. package/src/lib/server/api-v1/crm-companies.js.map +1 -0
  227. package/src/lib/server/api-v1/crm-deals.d.ts +18 -0
  228. package/src/lib/server/api-v1/crm-deals.js +631 -0
  229. package/src/lib/server/api-v1/crm-deals.js.map +1 -0
  230. package/src/lib/server/api-v1/crm-email-templates.d.ts +18 -0
  231. package/src/lib/server/api-v1/crm-email-templates.js +336 -0
  232. package/src/lib/server/api-v1/crm-email-templates.js.map +1 -0
  233. package/src/lib/server/api-v1/crm-leads.d.ts +18 -0
  234. package/src/lib/server/api-v1/crm-leads.js +833 -0
  235. package/src/lib/server/api-v1/crm-leads.js.map +1 -0
  236. package/src/lib/server/api-v1/crm-pipelines.d.ts +76 -0
  237. package/src/lib/server/api-v1/crm-pipelines.js +146 -0
  238. package/src/lib/server/api-v1/crm-pipelines.js.map +1 -0
  239. package/src/lib/server/api-v1/crm-shared.d.ts +271 -0
  240. package/src/lib/server/api-v1/crm-shared.js +436 -0
  241. package/src/lib/server/api-v1/crm-shared.js.map +1 -0
  242. package/src/lib/server/api-v1/crm-tasks.d.ts +18 -0
  243. package/src/lib/server/api-v1/crm-tasks.js +398 -0
  244. package/src/lib/server/api-v1/crm-tasks.js.map +1 -0
  245. package/src/lib/server/api-v1/openapi.d.ts +29 -0
  246. package/src/lib/server/api-v1/openapi.js +985 -0
  247. package/src/lib/server/api-v1/openapi.js.map +1 -0
  248. package/src/lib/server/api-v1/usage.d.ts +26 -0
  249. package/src/lib/server/api-v1/usage.js +73 -0
  250. package/src/lib/server/api-v1/usage.js.map +1 -0
  251. package/src/lib/server/campaign-filing-activity.d.ts +1 -1
  252. package/src/lib/server/campaign-filing-activity.js +7 -11
  253. package/src/lib/server/campaign-filing-activity.js.map +1 -1
  254. package/src/lib/server/capture-contact.js +15 -4
  255. package/src/lib/server/capture-contact.js.map +1 -1
  256. package/src/lib/server/consent-group-participant.js +1 -1
  257. package/src/lib/server/consent-group-participant.js.map +1 -1
  258. package/src/lib/server/contact-email-engagement.d.ts +33 -0
  259. package/src/lib/server/contact-email-engagement.js +146 -0
  260. package/src/lib/server/contact-email-engagement.js.map +1 -0
  261. package/src/lib/server/contact-lifecycle-floor.d.ts +89 -0
  262. package/src/lib/server/contact-lifecycle-floor.js +91 -0
  263. package/src/lib/server/contact-lifecycle-floor.js.map +1 -0
  264. package/src/lib/server/contact-remove.d.ts +75 -0
  265. package/src/lib/server/contact-remove.js +244 -0
  266. package/src/lib/server/contact-remove.js.map +1 -0
  267. package/src/lib/server/contact-update.js +4 -3
  268. package/src/lib/server/contact-update.js.map +1 -1
  269. package/src/lib/server/contacts-import.d.ts +47 -11
  270. package/src/lib/server/contacts-import.js +171 -71
  271. package/src/lib/server/contacts-import.js.map +1 -1
  272. package/src/lib/server/contacts-merge.d.ts +1 -1
  273. package/src/lib/server/contacts-merge.js +3 -2
  274. package/src/lib/server/contacts-merge.js.map +1 -1
  275. package/src/lib/server/container-detach.d.ts +31 -0
  276. package/src/lib/server/container-detach.js +115 -0
  277. package/src/lib/server/container-detach.js.map +1 -0
  278. package/src/lib/server/convert-host-lead.d.ts +189 -0
  279. package/src/lib/server/convert-host-lead.js +518 -0
  280. package/src/lib/server/convert-host-lead.js.map +1 -0
  281. package/src/lib/server/crm-inbound-email.d.ts +162 -0
  282. package/src/lib/server/crm-inbound-email.js +259 -0
  283. package/src/lib/server/crm-inbound-email.js.map +1 -0
  284. package/src/lib/server/crm-records-meter.d.ts +50 -0
  285. package/src/lib/server/crm-records-meter.js +126 -0
  286. package/src/lib/server/crm-records-meter.js.map +1 -0
  287. package/src/lib/server/crm-sharing.d.ts +108 -0
  288. package/src/lib/server/crm-sharing.js +790 -0
  289. package/src/lib/server/crm-sharing.js.map +1 -0
  290. package/src/lib/server/daily-digest-route.d.ts +101 -0
  291. package/src/lib/server/daily-digest-route.js +578 -0
  292. package/src/lib/server/daily-digest-route.js.map +1 -0
  293. package/src/lib/server/export-route.d.ts +60 -0
  294. package/src/lib/server/export-route.js +447 -0
  295. package/src/lib/server/export-route.js.map +1 -0
  296. package/src/lib/server/inbound-address.js +5 -3
  297. package/src/lib/server/inbound-address.js.map +1 -1
  298. package/src/lib/server/inbound-route.d.ts +74 -0
  299. package/src/lib/server/inbound-route.js +260 -0
  300. package/src/lib/server/inbound-route.js.map +1 -0
  301. package/src/lib/server/lead-campaign-carry.js +3 -3
  302. package/src/lib/server/lead-campaign-carry.js.map +1 -1
  303. package/src/lib/server/lead-convert.d.ts +1 -1
  304. package/src/lib/server/lead-convert.js +2 -2
  305. package/src/lib/server/lead-convert.js.map +1 -1
  306. package/src/lib/server/lead-create.d.ts +1 -1
  307. package/src/lib/server/lead-create.js +10 -15
  308. package/src/lib/server/lead-create.js.map +1 -1
  309. package/src/lib/server/lead-nurturing.d.ts +23 -0
  310. package/src/lib/server/lead-nurturing.js +82 -0
  311. package/src/lib/server/lead-nurturing.js.map +1 -0
  312. package/src/lib/server/leads-import.js +7 -7
  313. package/src/lib/server/leads-import.js.map +1 -1
  314. package/src/lib/server/merge-contacts.d.ts +55 -0
  315. package/src/lib/server/merge-contacts.js +274 -0
  316. package/src/lib/server/merge-contacts.js.map +1 -0
  317. package/src/lib/server/pipeline-record-index.d.ts +36 -0
  318. package/src/lib/server/pipeline-record-index.js +87 -0
  319. package/src/lib/server/pipeline-record-index.js.map +1 -0
  320. package/src/lib/server/record-email-state.js +23 -0
  321. package/src/lib/server/record-email-state.js.map +1 -1
  322. package/src/lib/server/record-facts.js +2 -1
  323. package/src/lib/server/record-facts.js.map +1 -1
  324. package/src/lib/server/record-timeline.d.ts +12 -0
  325. package/src/lib/server/record-timeline.js +127 -15
  326. package/src/lib/server/record-timeline.js.map +1 -1
  327. package/src/lib/server/task-reminders-route.d.ts +82 -0
  328. package/src/lib/server/task-reminders-route.js +499 -0
  329. package/src/lib/server/task-reminders-route.js.map +1 -0
  330. package/src/lib/server/task-routes.d.ts +2 -0
  331. package/src/lib/server/task-routes.js +22 -3
  332. package/src/lib/server/task-routes.js.map +1 -1
  333. package/src/lib/server-deal-stage.js +2 -1
  334. package/src/lib/server-deal-stage.js.map +1 -1
  335. package/src/lib/server.d.ts +5 -2
  336. package/src/lib/server.js +47 -2
  337. package/src/lib/server.js.map +1 -1
  338. package/src/lib/testing/stand-in-site-record-routes.d.ts +26 -0
  339. package/src/lib/testing/stand-in-site-record-routes.js +55 -0
  340. package/src/lib/testing/stand-in-site-record-routes.js.map +1 -0
  341. package/src/lib/usage-axes.d.ts +35 -0
  342. package/src/lib/usage-axes.js +75 -0
  343. package/src/lib/usage-axes.js.map +1 -0
  344. package/src/lib/components/use-emails-hub-path.d.ts +0 -24
  345. package/src/lib/components/use-emails-hub-path.js +0 -59
  346. package/src/lib/components/use-emails-hub-path.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/leads-bulk-bar.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\n/**\n * The bar over the Leads table, for whatever rows are ticked (AGL-2662).\n *\n * Hand them to an owner, set their status, unqualify them with one reason,\n * or take them into a spreadsheet. Every act here is a document write and\n * nothing more — a lead's status and owner are the team's own notes on a\n * capture, with no event and no notification behind them, which is why the\n * row's inline select writes them client-direct — so the whole bar goes\n * through the shared batched runner under the same rules.\n *\n * ## A row names its own site\n *\n * A lead lives at `orgs/{orgId}/leads/{leadId}` (AGL-3275), and at the organization\n * level the selection spans sites, so the reference for a write is built\n * from the ROW rather than from a scope the bar was handed: the row knows\n * its site, the bar need not. Under a site every row names the same one.\n *\n * ## What is declined, by name\n *\n * A converted lead's status IS its conversion, stamped by the route, and\n * the bar leaves it alone under Set status and Unqualify the way the row\n * menu and the inline select do. A lead already closed is not unqualified\n * twice. A lead already at the asked status is skipped rather than rewritten\n * so \"Nothing to change\" means what it says.\n */\n\nimport {\n CRM_LEAD_STATUS_LABELS,\n type CrmLeadFields,\n type CrmLeadStatus,\n crmLeadStatus,\n isCrmLeadOpen,\n leadPrimaryGroup,\n readCampaignIds,\n} from '@aglyn/aglyn'\nimport CampaignPicker from '@aglyn/shared-ui-email-campaigns/components/campaign-picker.component'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport { Button, MenuItem, TextField } from '@mui/material'\nimport { arrayUnion, deleteField, doc, serverTimestamp } from 'firebase/firestore'\nimport { useCallback, useMemo, useState } from 'react'\nimport { useCampaignFilingLog } from '../hooks/use-campaign-filing-log'\nimport { useCrmBulkApply } from '../hooks/use-crm-bulk-apply'\nimport { useCrmCampaigns } from '../hooks/use-crm-campaigns'\nimport type { OrgMemberOptions } from '../hooks/use-org-member-options'\nimport { downloadTextFile } from '../model/contacts-csv'\nimport { crmClientListFields } from '../model/crm-list-query'\nimport {\n type CrmBulkPlan,\n type CrmBulkSkip,\n type CrmBulkWrite,\n crmBulkWriters,\n runCrmBulkWrites,\n} from '../model/crm-bulk-writes'\nimport { type LeadCsvOptions, leadsCsv } from '../model/leads-csv'\nimport {\n type CrmBulkNoun,\n CrmBulkBarFrame,\n CrmBulkValueDialog,\n countNoun,\n} from './crm-bulk-bar-frame'\nimport CrmExportAllButton from './crm-export-all-button'\nimport { LeadOwnerSelect } from './lead-owner-select'\nimport { UNQUALIFY_REASON_MAX } from './lead-unqualify-dialog'\n\n/**\n * One row of the Leads list as the bar needs it: the document's fields,\n * the grid's key, and which document under which site a write names.\n */\nexport type LeadBulkRow = Record<string, unknown> &\n CrmLeadFields & { $id: string; leadId: string }\n\nexport interface LeadsBulkBarProps {\n rows: readonly LeadBulkRow[]\n selected: readonly string[]\n onSelectedChange: (ids: string[]) => void\n /** The section's roster — already read for the Owner column. */\n roster: OrgMemberOptions\n /** How the export names the owner and, at the org level, the site — the list's own. */\n csv?: LeadCsvOptions\n /** The organization these leads belong to; null while it is unresolved. */\n orgId?: string | null\n /**\n * The site the list is read under, or `null` at the organization level\n * where the complete export spans every site (AGL-2662).\n */\n hostId?: string | null\n /**\n * The org the shell passed, for the scope a filing entry carries\n * (AGL-3274): Add to campaign writes one on each lead's Activity, and\n * the entry is visible to whoever sees a record made on this site.\n */\n org?: Record<string, unknown> | null\n}\n\nconst NOUN: CrmBulkNoun = { singular: 'lead', plural: 'leads' }\n\ntype PendingAction = 'owner' | 'status' | 'unqualify' | 'campaign'\n\nconst ACTION_TITLES: Record<PendingAction, string> = {\n owner: 'Set the owner',\n status: 'Set the status',\n unqualify: 'Unqualify',\n campaign: 'Add to campaign',\n}\n\n/** The statuses the bar can set — the open ones; closing goes through Unqualify. */\nconst SETTABLE_STATUSES: readonly CrmLeadStatus[] = ['new', 'working']\n\n/** The label a report lists a lead under — its name, else its address. */\nconst labelOf = (lead: LeadBulkRow): string =>\n String(lead['name'] || lead['email'] || lead.leadId)\n\nexport function LeadsBulkBar(props: LeadsBulkBarProps) {\n if (!props.selected.length) return null\n return <LeadsBulkBarBody {...props} />\n}\nLeadsBulkBar.displayName = 'LeadsBulkBar'\n\nfunction LeadsBulkBarBody(props: LeadsBulkBarProps) {\n const { rows, selected, onSelectedChange, roster, csv } = props\n const orgId = props.orgId ?? null\n const hostId = props.hostId ?? null\n const firestore = useFirestore()\n const { busy, report, apply, dismissReport } = useCrmBulkApply({ recordKind: 'lead' })\n\n const selectedRows = useMemo(() => {\n const chosen = new Set(selected)\n return rows.filter((row) => chosen.has(row.$id))\n }, [rows, selected])\n\n const [pending, setPending] = useState<PendingAction | null>(null)\n const [value, setValue] = useState('')\n /*\n * The campaigns to add the selection to (AGL-3254), and the containers\n * the picker offers — read only while that dialog is open. Under a site,\n * the campaigns placed on it; at the organization level, where a\n * selection spans sites, every campaign in the org.\n */\n const [campaignIds, setCampaignIds] = useState<string[]>([])\n const campaigns = useCrmCampaigns(\n { hostId, orgId },\n { enabled: pending === 'campaign' },\n )\n const logFiling = useCampaignFilingLog({ orgId, hostId, org: props.org })\n\n // The reference a write names is the row's own site and document.\n const writers = useMemo(() => {\n const byId = new Map(rows.map((row) => [row.$id, row]))\n return crmBulkWriters(firestore, (id) => {\n const row = byId.get(id)\n // One org collection (AGL-3275): the row no longer names a site, and\n // the bulk write addresses the same document at either level.\n // `orgs//leads` is a path Firestore accepts the shape of and nothing\n // owns, so an absent org refuses the write rather than composing one.\n if (!orgId) throw new Error('[crm] a lead write needs an organization')\n return doc(firestore, 'orgs', orgId, 'leads', row?.leadId ?? id)\n })\n }, [firestore, rows])\n\n const openAction = (action: PendingAction) => {\n setValue('')\n setCampaignIds([])\n setPending(action)\n }\n\n const runPlan = useCallback(\n (plan: CrmBulkPlan, done: (count: number) => string) =>\n apply({\n attempted: plan.writes.length,\n skipped: plan.skipped,\n job: () => runCrmBulkWrites(writers, plan.writes, (write) => write.label),\n done,\n }),\n [apply, writers],\n )\n\n const handleApply = useCallback(async () => {\n if (!pending) return\n const action = pending\n setPending(null)\n const writes: CrmBulkWrite[] = []\n const skipped: CrmBulkSkip[] = []\n if (action === 'owner') {\n for (const lead of selectedRows) {\n writes.push({\n id: lead.$id,\n label: labelOf(lead),\n kind: 'update',\n data: { ownerUid: value ? value : deleteField(), updatedAt: serverTimestamp() },\n })\n }\n await runPlan(\n { writes, skipped },\n (count) => (value ? `Owner set on ${countNoun(count, NOUN)}` : `Owner cleared on ${countNoun(count, NOUN)}`),\n )\n return\n }\n if (action === 'campaign') {\n // Added to what each lead carries — `arrayUnion`, the way a sequence's\n // enroll and the import add one — never in place of it.\n for (const lead of selectedRows) {\n // The union as it will stand, for the list field it moves (AGL-3321).\n const union = [...new Set([...readCampaignIds(lead), ...campaignIds])]\n writes.push({\n id: lead.$id,\n label: labelOf(lead),\n kind: 'update',\n data: {\n campaignIds: arrayUnion(...campaignIds),\n ...crmClientListFields('leads', lead, { campaignIds: union }, ['scopedCampaignIds']),\n updatedAt: serverTimestamp(),\n },\n })\n }\n const outcome = await runPlan(\n { writes, skipped },\n (count) =>\n `Added ${countNoun(count, NOUN)} to ${campaignIds.length === 1 ? 'the campaign' : `${campaignIds.length} campaigns`}`,\n )\n // What landed is written on each lead's Activity (AGL-3274): one\n // \"Filed under\" per campaign the lead was not already in, and none\n // for a lead the store refused — the row's own document is the\n // membership, and the entry only records the act.\n const refused = new Set(outcome.refused.map((entry) => entry.label))\n const named = campaignIds.map((campaignId) => ({\n id: campaignId,\n name: campaigns.options.find((option) => option.value === campaignId)?.label ?? '',\n }))\n for (const lead of selectedRows) {\n if (refused.has(labelOf(lead))) continue\n const already = readCampaignIds(lead)\n const filed = named.filter((campaign) => !already.includes(campaign.id))\n // At the organization level each entry carries its own lead's first\n // capturing site, the one the lead's page files as.\n if (filed.length) {\n await logFiling(\n { leadId: lead.leadId },\n { filed },\n hostId ? null : leadPrimaryGroup(lead, props.org ?? null).hostId || null,\n )\n }\n }\n return\n }\n if (action === 'status') {\n const status = value as CrmLeadStatus\n for (const lead of selectedRows) {\n const current = crmLeadStatus(lead)\n if (lead.convertedContactId) {\n skipped.push({ label: labelOf(lead), reason: 'was converted' })\n } else if (current === status) {\n skipped.push({\n label: labelOf(lead),\n reason: `already ${CRM_LEAD_STATUS_LABELS[status]}`,\n })\n } else {\n writes.push({\n id: lead.$id,\n label: labelOf(lead),\n kind: 'update',\n data: {\n status,\n // Reopening an unqualified lead clears its reason, as the row does.\n ...(current === 'unqualified' ? { unqualifiedReason: deleteField() } : {}),\n updatedAt: serverTimestamp(),\n },\n })\n }\n }\n await runPlan(\n { writes, skipped },\n (count) => `Status set on ${countNoun(count, NOUN)}`,\n )\n return\n }\n const reason = value.trim().slice(0, UNQUALIFY_REASON_MAX)\n for (const lead of selectedRows) {\n if (lead.convertedContactId) {\n skipped.push({ label: labelOf(lead), reason: 'was converted' })\n } else if (!isCrmLeadOpen(lead)) {\n skipped.push({ label: labelOf(lead), reason: 'is already closed' })\n } else {\n writes.push({\n id: lead.$id,\n label: labelOf(lead),\n kind: 'update',\n data: {\n status: 'unqualified' satisfies CrmLeadStatus,\n unqualifiedReason: reason,\n updatedAt: serverTimestamp(),\n },\n })\n }\n }\n await runPlan(\n { writes, skipped },\n (count) => `Marked ${countNoun(count, NOUN)} unqualified`,\n )\n }, [pending, value, campaignIds, campaigns.options, selectedRows, runPlan, logFiling, hostId, props.org])\n\n const handleExport = useCallback(() => {\n downloadTextFile('leads-selected.csv', 'text/csv', leadsCsv(selectedRows, csv))\n }, [selectedRows, csv])\n\n const canApply =\n pending === 'owner' ||\n (pending === 'campaign'\n ? campaignIds.length > 0\n : pending === 'status'\n ? Boolean(value)\n : Boolean(value.trim()))\n\n return (\n <CrmBulkBarFrame\n count={selected.length}\n noun={NOUN}\n busy={busy}\n onClear={() => onSelectedChange([])}\n report={report}\n onDismissReport={dismissReport}\n extras={\n <CrmBulkValueDialog\n open={pending !== null}\n title={pending ? ACTION_TITLES[pending] : ''}\n count={selected.length}\n noun={NOUN}\n busy={busy}\n canApply={canApply}\n applyLabel={pending === 'unqualify' ? 'Unqualify' : pending === 'campaign' ? 'Add' : undefined}\n onClose={() => setPending(null)}\n onApply={() => void handleApply()}\n >\n {pending === 'owner' ? (\n <LeadOwnerSelect value={value} onChange={setValue} roster={roster} size=\"medium\" />\n ) : pending === 'status' ? (\n <TextField\n select\n size=\"small\"\n label=\"Status\"\n value={value}\n onChange={(event) => setValue(event.target.value)}\n helperText=\"Closing a lead goes through Unqualify, which asks why.\"\n >\n {SETTABLE_STATUSES.map((status) => (\n <MenuItem key={status} value={status}>\n {CRM_LEAD_STATUS_LABELS[status]}\n </MenuItem>\n ))}\n </TextField>\n ) : pending === 'unqualify' ? (\n <TextField\n label=\"Reason\"\n value={value}\n onChange={(event) => setValue(event.target.value)}\n multiline\n minRows={2}\n autoFocus\n helperText=\"One reason for every selected lead, kept on each so it can be counted later.\"\n slotProps={{ htmlInput: { maxLength: UNQUALIFY_REASON_MAX } }}\n />\n ) : pending === 'campaign' ? (\n <CampaignPicker\n options={campaigns.options}\n value={campaignIds}\n onChange={setCampaignIds}\n helperText=\"Added to the campaigns each selected lead is already in. It does not decide who a campaign mails.\"\n empty={campaigns.ready && !campaigns.options.length}\n emptyText={\n hostId\n ? 'This site has no campaigns yet. Create one from Marketing to file leads under it.'\n : 'There are no campaigns yet. Create one from Marketing to file leads under it.'\n }\n />\n ) : null}\n </CrmBulkValueDialog>\n }\n >\n <Button size=\"small\" disabled={busy} onClick={() => openAction('owner')}>\n {'Set owner'}\n </Button>\n <Button size=\"small\" disabled={busy} onClick={() => openAction('status')}>\n {'Set status'}\n </Button>\n <Button size=\"small\" disabled={busy} onClick={() => openAction('unqualify')}>\n {'Unqualify'}\n </Button>\n <Button size=\"small\" disabled={busy} onClick={() => openAction('campaign')}>\n {'Add to campaign'}\n </Button>\n <Button size=\"small\" disabled={busy} onClick={handleExport}>\n {'Export CSV'}\n </Button>\n {/*\n The selection's file is the rows on screen; this one is the whole\n collection, streamed by the server (AGL-2662).\n */}\n <CrmExportAllButton\n resource=\"leads\"\n orgId={orgId}\n hostId={hostId}\n disabled={busy}\n />\n </CrmBulkBarFrame>\n )\n}\nLeadsBulkBarBody.displayName = 'LeadsBulkBarBody'\n\nexport default LeadsBulkBar\n"],"names":["CRM_LEAD_STATUS_LABELS","crmLeadStatus","isCrmLeadOpen","leadPrimaryGroup","readCampaignIds","CampaignPicker","useFirestore","Button","MenuItem","TextField","arrayUnion","deleteField","doc","serverTimestamp","useCallback","useMemo","useState","useCampaignFilingLog","useCrmBulkApply","useCrmCampaigns","downloadTextFile","crmClientListFields","crmBulkWriters","runCrmBulkWrites","leadsCsv","CrmBulkBarFrame","CrmBulkValueDialog","countNoun","CrmExportAllButton","LeadOwnerSelect","UNQUALIFY_REASON_MAX","NOUN","singular","plural","ACTION_TITLES","owner","status","unqualify","campaign","SETTABLE_STATUSES","labelOf","lead","String","leadId","LeadsBulkBar","props","selected","length","LeadsBulkBarBody","displayName","rows","onSelectedChange","roster","csv","orgId","hostId","firestore","busy","report","apply","dismissReport","recordKind","selectedRows","chosen","Set","filter","row","has","$id","pending","setPending","value","setValue","campaignIds","setCampaignIds","campaigns","enabled","logFiling","org","writers","byId","Map","map","id","get","Error","openAction","action","runPlan","plan","done","attempted","writes","skipped","job","write","label","handleApply","push","kind","data","ownerUid","updatedAt","count","union","outcome","refused","entry","named","campaignId","name","options","find","option","already","filed","includes","current","convertedContactId","reason","unqualifiedReason","trim","slice","handleExport","canApply","Boolean","noun","onClear","onDismissReport","extras","open","title","applyLabel","undefined","onClose","onApply","onChange","size","select","event","target","helperText","multiline","minRows","autoFocus","slotProps","htmlInput","maxLength","empty","ready","emptyText","disabled","onClick","resource"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;;AAEA;;;;;;;;;;;;;;;;;;;;;;;;CAwBC,GAED,SACEA,sBAAsB,EAGtBC,aAAa,EACbC,aAAa,EACbC,gBAAgB,EAChBC,eAAe,QACV,eAAc;AACrB,OAAOC,oBAAoB,wEAAuE;AAClG,SAASC,YAAY,QAAQ,iCAAgC;AAC7D,SAASC,MAAM,EAAEC,QAAQ,EAAEC,SAAS,QAAQ,gBAAe;AAC3D,SAASC,UAAU,EAAEC,WAAW,EAAEC,GAAG,EAAEC,eAAe,QAAQ,qBAAoB;AAClF,SAASC,WAAW,EAAEC,OAAO,EAAEC,QAAQ,QAAQ,QAAO;AACtD,SAASC,oBAAoB,QAAQ,sCAAkC;AACvE,SAASC,eAAe,QAAQ,iCAA6B;AAC7D,SAASC,eAAe,QAAQ,gCAA4B;AAE5D,SAASC,gBAAgB,QAAQ,2BAAuB;AACxD,SAASC,mBAAmB,QAAQ,6BAAyB;AAC7D,SAIEC,cAAc,EACdC,gBAAgB,QACX,8BAA0B;AACjC,SAA8BC,QAAQ,QAAQ,wBAAoB;AAClE,SAEEC,eAAe,EACfC,kBAAkB,EAClBC,SAAS,QACJ,0BAAsB;AAC7B,OAAOC,wBAAwB,6BAAyB;AACxD,SAASC,eAAe,QAAQ,yBAAqB;AACrD,SAASC,oBAAoB,QAAQ,6BAAyB;AAgC9D,MAAMC,OAAoB;IAAEC,UAAU;IAAQC,QAAQ;AAAQ;AAI9D,MAAMC,gBAA+C;IACnDC,OAAO;IACPC,QAAQ;IACRC,WAAW;IACXC,UAAU;AACZ;AAEA,kFAAkF,GAClF,MAAMC,oBAA8C;IAAC;IAAO;CAAU;AAEtE,wEAAwE,GACxE,MAAMC,UAAU,CAACC,OACfC,OAAOD,IAAI,CAAC,OAAO,IAAIA,IAAI,CAAC,QAAQ,IAAIA,KAAKE,MAAM;AAErD,OAAO,SAASC,aAAaC,KAAwB;IACnD,IAAI,CAACA,MAAMC,QAAQ,CAACC,MAAM,EAAE,OAAO;IACnC,qBAAO,KAACC,+BAAqBH;AAC/B;AACAD,aAAaK,WAAW,GAAG;AAE3B,SAASD,iBAAiBH,KAAwB;QAElCA,cACCA;IAFf,MAAM,EAAEK,IAAI,EAAEJ,QAAQ,EAAEK,gBAAgB,EAAEC,MAAM,EAAEC,GAAG,EAAE,GAAGR;IAC1D,MAAMS,SAAQT,eAAAA,MAAMS,KAAK,YAAXT,eAAe;IAC7B,MAAMU,UAASV,gBAAAA,MAAMU,MAAM,YAAZV,gBAAgB;IAC/B,MAAMW,YAAYlD;IAClB,MAAM,EAAEmD,IAAI,EAAEC,MAAM,EAAEC,KAAK,EAAEC,aAAa,EAAE,GAAG1C,gBAAgB;QAAE2C,YAAY;IAAO;IAEpF,MAAMC,eAAe/C,QAAQ;QAC3B,MAAMgD,SAAS,IAAIC,IAAIlB;QACvB,OAAOI,KAAKe,MAAM,CAAC,CAACC,MAAQH,OAAOI,GAAG,CAACD,IAAIE,GAAG;IAChD,GAAG;QAAClB;QAAMJ;KAAS;IAEnB,MAAM,CAACuB,SAASC,WAAW,GAAGtD,SAA+B;IAC7D,MAAM,CAACuD,OAAOC,SAAS,GAAGxD,SAAS;IACnC;;;;;GAKC,GACD,MAAM,CAACyD,aAAaC,eAAe,GAAG1D,SAAmB,EAAE;IAC3D,MAAM2D,YAAYxD,gBAChB;QAAEoC;QAAQD;IAAM,GAChB;QAAEsB,SAASP,YAAY;IAAW;IAEpC,MAAMQ,YAAY5D,qBAAqB;QAAEqC;QAAOC;QAAQuB,KAAKjC,MAAMiC,GAAG;IAAC;IAEvE,kEAAkE;IAClE,MAAMC,UAAUhE,QAAQ;QACtB,MAAMiE,OAAO,IAAIC,IAAI/B,KAAKgC,GAAG,CAAC,CAAChB,MAAQ;gBAACA,IAAIE,GAAG;gBAAEF;aAAI;QACrD,OAAO5C,eAAekC,WAAW,CAAC2B;;YAChC,MAAMjB,MAAMc,KAAKI,GAAG,CAACD;YACrB,qEAAqE;YACrE,8DAA8D;YAC9D,qEAAqE;YACrE,sEAAsE;YACtE,IAAI,CAAC7B,OAAO,MAAM,IAAI+B,MAAM;YAC5B,OAAOzE,IAAI4C,WAAW,QAAQF,OAAO,iBAASY,uBAAAA,IAAKvB,MAAM,mBAAIwC;QAC/D;IACF,GAAG;QAAC3B;QAAWN;KAAK;IAEpB,MAAMoC,aAAa,CAACC;QAClBf,SAAS;QACTE,eAAe,EAAE;QACjBJ,WAAWiB;IACb;IAEA,MAAMC,UAAU1E,YACd,CAAC2E,MAAmBC,OAClB/B,MAAM;YACJgC,WAAWF,KAAKG,MAAM,CAAC7C,MAAM;YAC7B8C,SAASJ,KAAKI,OAAO;YACrBC,KAAK,IAAMvE,iBAAiBwD,SAASU,KAAKG,MAAM,EAAE,CAACG,QAAUA,MAAMC,KAAK;YACxEN;QACF,IACF;QAAC/B;QAAOoB;KAAQ;IAGlB,MAAMkB,cAAcnF,YAAY;QAC9B,IAAI,CAACuD,SAAS;QACd,MAAMkB,SAASlB;QACfC,WAAW;QACX,MAAMsB,SAAyB,EAAE;QACjC,MAAMC,UAAyB,EAAE;QACjC,IAAIN,WAAW,SAAS;YACtB,KAAK,MAAM9C,QAAQqB,aAAc;gBAC/B8B,OAAOM,IAAI,CAAC;oBACVf,IAAI1C,KAAK2B,GAAG;oBACZ4B,OAAOxD,QAAQC;oBACf0D,MAAM;oBACNC,MAAM;wBAAEC,UAAU9B,QAAQA,QAAQ5D;wBAAe2F,WAAWzF;oBAAkB;gBAChF;YACF;YACA,MAAM2E,QACJ;gBAAEI;gBAAQC;YAAQ,GAClB,CAACU,QAAWhC,QAAQ,CAAC,aAAa,EAAE5C,UAAU4E,OAAOxE,OAAO,GAAG,CAAC,iBAAiB,EAAEJ,UAAU4E,OAAOxE,OAAO;YAE7G;QACF;QACA,IAAIwD,WAAW,YAAY;YACzB,uEAAuE;YACvE,wDAAwD;YACxD,KAAK,MAAM9C,QAAQqB,aAAc;gBAC/B,sEAAsE;gBACtE,MAAM0C,QAAQ;uBAAI,IAAIxC,IAAI;2BAAI5D,gBAAgBqC;2BAAUgC;qBAAY;iBAAE;gBACtEmB,OAAOM,IAAI,CAAC;oBACVf,IAAI1C,KAAK2B,GAAG;oBACZ4B,OAAOxD,QAAQC;oBACf0D,MAAM;oBACNC,MAAM;wBACJ3B,aAAa/D,cAAc+D;uBACxBpD,oBAAoB,SAASoB,MAAM;wBAAEgC,aAAa+B;oBAAM,GAAG;wBAAC;qBAAoB;wBACnFF,WAAWzF;;gBAEf;YACF;YACA,MAAM4F,UAAU,MAAMjB,QACpB;gBAAEI;gBAAQC;YAAQ,GAClB,CAACU,QACC,CAAC,MAAM,EAAE5E,UAAU4E,OAAOxE,MAAM,IAAI,EAAE0C,YAAY1B,MAAM,KAAK,IAAI,iBAAiB,GAAG0B,YAAY1B,MAAM,CAAC,UAAU,CAAC,EAAE;YAEzH,iEAAiE;YACjE,mEAAmE;YACnE,+DAA+D;YAC/D,kDAAkD;YAClD,MAAM2D,UAAU,IAAI1C,IAAIyC,QAAQC,OAAO,CAACxB,GAAG,CAAC,CAACyB,QAAUA,MAAMX,KAAK;YAClE,MAAMY,QAAQnC,YAAYS,GAAG,CAAC,CAAC2B;;oBAEvBlC;uBAFuC;oBAC7CQ,IAAI0B;oBACJC,IAAI,WAAEnC,0BAAAA,UAAUoC,OAAO,CAACC,IAAI,CAAC,CAACC,SAAWA,OAAO1C,KAAK,KAAKsC,gCAApDlC,wBAAiEqB,KAAK,mBAAI;gBAClF;;YACA,KAAK,MAAMvD,QAAQqB,aAAc;gBAC/B,IAAI4C,QAAQvC,GAAG,CAAC3B,QAAQC,QAAQ;gBAChC,MAAMyE,UAAU9G,gBAAgBqC;gBAChC,MAAM0E,QAAQP,MAAM3C,MAAM,CAAC,CAAC3B,WAAa,CAAC4E,QAAQE,QAAQ,CAAC9E,SAAS6C,EAAE;gBACtE,oEAAoE;gBACpE,oDAAoD;gBACpD,IAAIgC,MAAMpE,MAAM,EAAE;wBAIyBF;oBAHzC,MAAMgC,UACJ;wBAAElC,QAAQF,KAAKE,MAAM;oBAAC,GACtB;wBAAEwE;oBAAM,GACR5D,SAAS,OAAOpD,iBAAiBsC,OAAMI,aAAAA,MAAMiC,GAAG,YAATjC,aAAa,MAAMU,MAAM,IAAI;gBAExE;YACF;YACA;QACF;QACA,IAAIgC,WAAW,UAAU;YACvB,MAAMnD,SAASmC;YACf,KAAK,MAAM9B,QAAQqB,aAAc;gBAC/B,MAAMuD,UAAUpH,cAAcwC;gBAC9B,IAAIA,KAAK6E,kBAAkB,EAAE;oBAC3BzB,QAAQK,IAAI,CAAC;wBAAEF,OAAOxD,QAAQC;wBAAO8E,QAAQ;oBAAgB;gBAC/D,OAAO,IAAIF,YAAYjF,QAAQ;oBAC7ByD,QAAQK,IAAI,CAAC;wBACXF,OAAOxD,QAAQC;wBACf8E,QAAQ,CAAC,QAAQ,EAAEvH,sBAAsB,CAACoC,OAAO,EAAE;oBACrD;gBACF,OAAO;oBACLwD,OAAOM,IAAI,CAAC;wBACVf,IAAI1C,KAAK2B,GAAG;wBACZ4B,OAAOxD,QAAQC;wBACf0D,MAAM;wBACNC,MAAM;4BACJhE;2BAEIiF,YAAY,gBAAgB;4BAAEG,mBAAmB7G;wBAAc,IAAI,CAAC;4BACxE2F,WAAWzF;;oBAEf;gBACF;YACF;YACA,MAAM2E,QACJ;gBAAEI;gBAAQC;YAAQ,GAClB,CAACU,QAAU,CAAC,cAAc,EAAE5E,UAAU4E,OAAOxE,OAAO;YAEtD;QACF;QACA,MAAMwF,SAAShD,MAAMkD,IAAI,GAAGC,KAAK,CAAC,GAAG5F;QACrC,KAAK,MAAMW,QAAQqB,aAAc;YAC/B,IAAIrB,KAAK6E,kBAAkB,EAAE;gBAC3BzB,QAAQK,IAAI,CAAC;oBAAEF,OAAOxD,QAAQC;oBAAO8E,QAAQ;gBAAgB;YAC/D,OAAO,IAAI,CAACrH,cAAcuC,OAAO;gBAC/BoD,QAAQK,IAAI,CAAC;oBAAEF,OAAOxD,QAAQC;oBAAO8E,QAAQ;gBAAoB;YACnE,OAAO;gBACL3B,OAAOM,IAAI,CAAC;oBACVf,IAAI1C,KAAK2B,GAAG;oBACZ4B,OAAOxD,QAAQC;oBACf0D,MAAM;oBACNC,MAAM;wBACJhE,QAAQ;wBACRoF,mBAAmBD;wBACnBjB,WAAWzF;oBACb;gBACF;YACF;QACF;QACA,MAAM2E,QACJ;YAAEI;YAAQC;QAAQ,GAClB,CAACU,QAAU,CAAC,OAAO,EAAE5E,UAAU4E,OAAOxE,MAAM,YAAY,CAAC;IAE7D,GAAG;QAACsC;QAASE;QAAOE;QAAaE,UAAUoC,OAAO;QAAEjD;QAAc0B;QAASX;QAAWtB;QAAQV,MAAMiC,GAAG;KAAC;IAExG,MAAM6C,eAAe7G,YAAY;QAC/BM,iBAAiB,sBAAsB,YAAYI,SAASsC,cAAcT;IAC5E,GAAG;QAACS;QAAcT;KAAI;IAEtB,MAAMuE,WACJvD,YAAY,WACXA,CAAAA,YAAY,aACTI,YAAY1B,MAAM,GAAG,IACrBsB,YAAY,WACVwD,QAAQtD,SACRsD,QAAQtD,MAAMkD,IAAI,GAAE;IAE5B,qBACE,MAAChG;QACC8E,OAAOzD,SAASC,MAAM;QACtB+E,MAAM/F;QACN0B,MAAMA;QACNsE,SAAS,IAAM5E,iBAAiB,EAAE;QAClCO,QAAQA;QACRsE,iBAAiBpE;QACjBqE,sBACE,KAACvG;YACCwG,MAAM7D,YAAY;YAClB8D,OAAO9D,UAAUnC,aAAa,CAACmC,QAAQ,GAAG;YAC1CkC,OAAOzD,SAASC,MAAM;YACtB+E,MAAM/F;YACN0B,MAAMA;YACNmE,UAAUA;YACVQ,YAAY/D,YAAY,cAAc,cAAcA,YAAY,aAAa,QAAQgE;YACrFC,SAAS,IAAMhE,WAAW;YAC1BiE,SAAS,IAAM,KAAKtC;sBAEnB5B,YAAY,wBACX,KAACxC;gBAAgB0C,OAAOA;gBAAOiE,UAAUhE;gBAAUpB,QAAQA;gBAAQqF,MAAK;iBACtEpE,YAAY,yBACd,KAAC5D;gBACCiI,MAAM;gBACND,MAAK;gBACLzC,OAAM;gBACNzB,OAAOA;gBACPiE,UAAU,CAACG,QAAUnE,SAASmE,MAAMC,MAAM,CAACrE,KAAK;gBAChDsE,YAAW;0BAEVtG,kBAAkB2C,GAAG,CAAC,CAAC9C,uBACtB,KAAC5B;wBAAsB+D,OAAOnC;kCAC3BpC,sBAAsB,CAACoC,OAAO;uBADlBA;iBAKjBiC,YAAY,4BACd,KAAC5D;gBACCuF,OAAM;gBACNzB,OAAOA;gBACPiE,UAAU,CAACG,QAAUnE,SAASmE,MAAMC,MAAM,CAACrE,KAAK;gBAChDuE,SAAS;gBACTC,SAAS;gBACTC,SAAS;gBACTH,YAAW;gBACXI,WAAW;oBAAEC,WAAW;wBAAEC,WAAWrH;oBAAqB;gBAAE;iBAE5DuC,YAAY,2BACd,KAAChE;gBACC0G,SAASpC,UAAUoC,OAAO;gBAC1BxC,OAAOE;gBACP+D,UAAU9D;gBACVmE,YAAW;gBACXO,OAAOzE,UAAU0E,KAAK,IAAI,CAAC1E,UAAUoC,OAAO,CAAChE,MAAM;gBACnDuG,WACE/F,SACI,sFACA;iBAGN;;;0BAIR,KAAChD;gBAAOkI,MAAK;gBAAQc,UAAU9F;gBAAM+F,SAAS,IAAMlE,WAAW;0BAC5D;;0BAEH,KAAC/E;gBAAOkI,MAAK;gBAAQc,UAAU9F;gBAAM+F,SAAS,IAAMlE,WAAW;0BAC5D;;0BAEH,KAAC/E;gBAAOkI,MAAK;gBAAQc,UAAU9F;gBAAM+F,SAAS,IAAMlE,WAAW;0BAC5D;;0BAEH,KAAC/E;gBAAOkI,MAAK;gBAAQc,UAAU9F;gBAAM+F,SAAS,IAAMlE,WAAW;0BAC5D;;0BAEH,KAAC/E;gBAAOkI,MAAK;gBAAQc,UAAU9F;gBAAM+F,SAAS7B;0BAC3C;;0BAMH,KAAC/F;gBACC6H,UAAS;gBACTnG,OAAOA;gBACPC,QAAQA;gBACRgG,UAAU9F;;;;AAIlB;AACAT,iBAAiBC,WAAW,GAAG;AAE/B,eAAeL,aAAY"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/leads-bulk-bar.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\n/**\n * The bar over the Leads table, for whatever rows are ticked (AGL-2662).\n *\n * Hand them to an owner, set their status, unqualify them with one reason,\n * or take them into a spreadsheet. Every act here is a document write and\n * nothing more — a lead's status and owner are the team's own notes on a\n * capture, with no event and no notification behind them, which is why the\n * row's inline select writes them client-direct — so the whole bar goes\n * through the shared batched runner under the same rules.\n *\n * ## A row names its own site\n *\n * A lead lives at `orgs/{orgId}/leads/{leadId}` (AGL-3275), and at the organization\n * level the selection spans sites, so the reference for a write is built\n * from the ROW rather than from a scope the bar was handed: the row knows\n * its site, the bar need not. Under a site every row names the same one.\n *\n * ## What is declined, by name\n *\n * A converted lead's status IS its conversion, stamped by the route, and\n * the bar leaves it alone under Set status and Unqualify the way the row\n * menu and the inline select do. A lead already closed is not unqualified\n * twice. A lead already at the asked status is skipped rather than rewritten\n * so \"Nothing to change\" means what it says.\n */\n\nimport {\n CRM_LEAD_OPEN_STATUSES,\n CRM_LEAD_STATUS_LABELS,\n type CrmLeadFields,\n type CrmLeadStatus,\n crmLeadStatus,\n isCrmLeadOpen,\n readContainerIds,\n} from '@aglyn/aglyn'\nimport ContainerPicker from '@aglyn/tenant-feature-instance/components/container-picker'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport { Button, MenuItem, TextField } from '@mui/material'\nimport { arrayUnion, deleteField, doc, serverTimestamp } from 'firebase/firestore'\nimport { useCallback, useMemo, useState } from 'react'\nimport { useCampaignFilingLog } from '../hooks/use-campaign-filing-log'\nimport { useCrmBulkApply } from '../hooks/use-crm-bulk-apply'\nimport { useCrmCampaigns } from '../hooks/use-crm-campaigns'\nimport type { OrgMemberOptions } from '../hooks/use-org-member-options'\nimport { downloadTextFile } from '../model/contacts-csv'\nimport { crmClientListFields } from '../model/crm-list-query'\nimport {\n type CrmBulkPlan,\n type CrmBulkSkip,\n type CrmBulkWrite,\n crmBulkWriters,\n runCrmBulkWrites,\n} from '../model/crm-bulk-writes'\nimport { type LeadCsvOptions, leadsCsv } from '../model/leads-csv'\nimport {\n type CrmBulkNoun,\n CrmBulkBarFrame,\n CrmBulkValueDialog,\n countNoun,\n} from './crm-bulk-bar-frame'\nimport CrmExportAllButton from './crm-export-all-button'\nimport { CrmBulkShareButton } from './record-sharing-card'\nimport { LeadOwnerSelect } from './lead-owner-select'\nimport { UNQUALIFY_REASON_MAX } from './lead-unqualify-dialog'\nimport { useCrmSharingFollowUp } from '../hooks/use-crm-sharing'\nimport { leadPrimaryGroup } from '../model/contact-holder'\n\n/**\n * One row of the Leads list as the bar needs it: the document's fields,\n * the grid's key, and which document under which site a write names.\n */\nexport type LeadBulkRow = Record<string, unknown> &\n CrmLeadFields & { $id: string; leadId: string }\n\nexport interface LeadsBulkBarProps {\n rows: readonly LeadBulkRow[]\n selected: readonly string[]\n onSelectedChange: (ids: string[]) => void\n /** The section's roster — already read for the Owner column. */\n roster: OrgMemberOptions\n /** How the export names the owner and, at the org level, the site — the list's own. */\n csv?: LeadCsvOptions\n /** The organization these leads belong to; null while it is unresolved. */\n orgId?: string | null\n /**\n * The site the list is read under, or `null` at the organization level\n * where the complete export spans every site (AGL-2662).\n */\n hostId?: string | null\n /**\n * The org the shell passed, for the scope a filing entry carries\n * (AGL-3274): Add to campaign writes one on each lead's Activity, and\n * the entry is visible to whoever sees a record made on this site.\n */\n org?: Record<string, unknown> | null\n}\n\nconst NOUN: CrmBulkNoun = { singular: 'lead', plural: 'leads' }\n\ntype PendingAction = 'owner' | 'status' | 'unqualify' | 'campaign'\n\nconst ACTION_TITLES: Record<PendingAction, string> = {\n owner: 'Set the owner',\n status: 'Set the status',\n unqualify: 'Unqualify',\n campaign: 'Add to campaign',\n}\n\n/** The statuses the bar can set — the open ones; closing goes through Unqualify. */\nconst SETTABLE_STATUSES: readonly CrmLeadStatus[] = CRM_LEAD_OPEN_STATUSES\n\n/** The label a report lists a lead under — its name, else its address. */\nconst labelOf = (lead: LeadBulkRow): string =>\n String(lead['name'] || lead['email'] || lead.leadId)\n\nexport function LeadsBulkBar(props: LeadsBulkBarProps) {\n if (!props.selected.length) return null\n return <LeadsBulkBarBody {...props} />\n}\nLeadsBulkBar.displayName = 'LeadsBulkBar'\n\nfunction LeadsBulkBarBody(props: LeadsBulkBarProps) {\n const { rows, selected, onSelectedChange, roster, csv } = props\n const orgId = props.orgId ?? null\n const hostId = props.hostId ?? null\n const followUpSharing = useCrmSharingFollowUp(hostId, orgId)\n const firestore = useFirestore()\n const { busy, report, apply, dismissReport } = useCrmBulkApply({ recordKind: 'lead' })\n\n const selectedRows = useMemo(() => {\n const chosen = new Set(selected)\n return rows.filter((row) => chosen.has(row.$id))\n }, [rows, selected])\n\n const [pending, setPending] = useState<PendingAction | null>(null)\n const [value, setValue] = useState('')\n /*\n * The campaigns to add the selection to (AGL-3254), and the containers\n * the picker offers — read only while that dialog is open. Under a site,\n * the campaigns placed on it; at the organization level, where a\n * selection spans sites, every campaign in the org.\n */\n const [campaignIds, setCampaignIds] = useState<string[]>([])\n const campaigns = useCrmCampaigns(\n { hostId, orgId },\n { enabled: pending === 'campaign' },\n )\n const logFiling = useCampaignFilingLog({ orgId, hostId, org: props.org })\n\n // The reference a write names is the row's own site and document.\n const writers = useMemo(() => {\n const byId = new Map(rows.map((row) => [row.$id, row]))\n return crmBulkWriters(firestore, (id) => {\n const row = byId.get(id)\n // One org collection (AGL-3275): the row no longer names a site, and\n // the bulk write addresses the same document at either level.\n // `orgs//leads` is a path Firestore accepts the shape of and nothing\n // owns, so an absent org refuses the write rather than composing one.\n if (!orgId) throw new Error('[crm] a lead write needs an organization')\n return doc(firestore, 'orgs', orgId, 'leads', row?.leadId ?? id)\n })\n }, [firestore, rows])\n\n const openAction = (action: PendingAction) => {\n setValue('')\n setCampaignIds([])\n setPending(action)\n }\n\n const runPlan = useCallback(\n (plan: CrmBulkPlan, done: (count: number) => string) =>\n apply({\n attempted: plan.writes.length,\n skipped: plan.skipped,\n job: async () => {\n const outcome = await runCrmBulkWrites(writers, plan.writes, (write) => write.label)\n // A client-direct write owes the sharing rules a re-evaluation (AGL-3336).\n followUpSharing(\n 'leads',\n plan.writes.filter((write) => write.kind === 'update').map((write) => rows.find((row) => row.$id === write.id)?.leadId ?? write.id),\n )\n return outcome\n },\n done,\n }),\n [apply, writers, rows, followUpSharing],\n )\n\n const handleApply = useCallback(async () => {\n if (!pending) return\n const action = pending\n setPending(null)\n const writes: CrmBulkWrite[] = []\n const skipped: CrmBulkSkip[] = []\n if (action === 'owner') {\n for (const lead of selectedRows) {\n writes.push({\n id: lead.$id,\n label: labelOf(lead),\n kind: 'update',\n data: { ownerUid: value ? value : deleteField(), updatedAt: serverTimestamp() },\n })\n }\n await runPlan(\n { writes, skipped },\n (count) => (value ? `Owner set on ${countNoun(count, NOUN)}` : `Owner cleared on ${countNoun(count, NOUN)}`),\n )\n return\n }\n if (action === 'campaign') {\n // Added to what each lead carries — `arrayUnion`, the way a sequence's\n // enroll and the import add one — never in place of it.\n for (const lead of selectedRows) {\n // The union as it will stand, for the list field it moves (AGL-3321).\n const union = [...new Set([...readContainerIds(lead, 'campaign'), ...campaignIds])]\n writes.push({\n id: lead.$id,\n label: labelOf(lead),\n kind: 'update',\n data: {\n campaignIds: arrayUnion(...campaignIds),\n ...crmClientListFields('leads', lead, { campaignIds: union }, ['scopedCampaignIds']),\n updatedAt: serverTimestamp(),\n },\n })\n }\n const outcome = await runPlan(\n { writes, skipped },\n (count) =>\n `Added ${countNoun(count, NOUN)} to ${campaignIds.length === 1 ? 'the campaign' : `${campaignIds.length} campaigns`}`,\n )\n // What landed is written on each lead's Activity (AGL-3274): one\n // \"Filed under\" per campaign the lead was not already in, and none\n // for a lead the store refused — the row's own document is the\n // membership, and the entry only records the act.\n const refused = new Set(outcome.refused.map((entry) => entry.label))\n const named = campaignIds.map((campaignId) => ({\n id: campaignId,\n name: campaigns.options.find((option) => option.value === campaignId)?.label ?? '',\n }))\n for (const lead of selectedRows) {\n if (refused.has(labelOf(lead))) continue\n const already = readContainerIds(lead, 'campaign')\n const filed = named.filter((campaign) => !already.includes(campaign.id))\n // At the organization level each entry carries its own lead's first\n // capturing site, the one the lead's page files as.\n if (filed.length) {\n await logFiling(\n { leadId: lead.leadId },\n { filed },\n hostId ? null : leadPrimaryGroup(lead, props.org ?? null).hostId || null,\n )\n }\n }\n return\n }\n if (action === 'status') {\n const status = value as CrmLeadStatus\n for (const lead of selectedRows) {\n const current = crmLeadStatus(lead)\n if (lead.convertedContactId) {\n skipped.push({ label: labelOf(lead), reason: 'was converted' })\n } else if (current === status) {\n skipped.push({\n label: labelOf(lead),\n reason: `already ${CRM_LEAD_STATUS_LABELS[status]}`,\n })\n } else {\n writes.push({\n id: lead.$id,\n label: labelOf(lead),\n kind: 'update',\n data: {\n status,\n // Reopening an unqualified lead clears its reason, as the row does.\n ...(current === 'unqualified' ? { unqualifiedReason: deleteField() } : {}),\n updatedAt: serverTimestamp(),\n },\n })\n }\n }\n await runPlan(\n { writes, skipped },\n (count) => `Status set on ${countNoun(count, NOUN)}`,\n )\n return\n }\n const reason = value.trim().slice(0, UNQUALIFY_REASON_MAX)\n for (const lead of selectedRows) {\n if (lead.convertedContactId) {\n skipped.push({ label: labelOf(lead), reason: 'was converted' })\n } else if (!isCrmLeadOpen(lead)) {\n skipped.push({ label: labelOf(lead), reason: 'is already closed' })\n } else {\n writes.push({\n id: lead.$id,\n label: labelOf(lead),\n kind: 'update',\n data: {\n status: 'unqualified' satisfies CrmLeadStatus,\n unqualifiedReason: reason,\n updatedAt: serverTimestamp(),\n },\n })\n }\n }\n await runPlan(\n { writes, skipped },\n (count) => `Marked ${countNoun(count, NOUN)} unqualified`,\n )\n }, [pending, value, campaignIds, campaigns.options, selectedRows, runPlan, logFiling, hostId, props.org])\n\n const handleExport = useCallback(() => {\n downloadTextFile('leads-selected.csv', 'text/csv', leadsCsv(selectedRows, csv))\n }, [selectedRows, csv])\n\n const canApply =\n pending === 'owner' ||\n (pending === 'campaign'\n ? campaignIds.length > 0\n : pending === 'status'\n ? Boolean(value)\n : Boolean(value.trim()))\n\n return (\n <CrmBulkBarFrame\n count={selected.length}\n noun={NOUN}\n busy={busy}\n onClear={() => onSelectedChange([])}\n report={report}\n onDismissReport={dismissReport}\n extras={\n <CrmBulkValueDialog\n open={pending !== null}\n title={pending ? ACTION_TITLES[pending] : ''}\n count={selected.length}\n noun={NOUN}\n busy={busy}\n canApply={canApply}\n applyLabel={pending === 'unqualify' ? 'Unqualify' : pending === 'campaign' ? 'Add' : undefined}\n onClose={() => setPending(null)}\n onApply={() => void handleApply()}\n >\n {pending === 'owner' ? (\n <LeadOwnerSelect value={value} onChange={setValue} roster={roster} size=\"medium\" />\n ) : pending === 'status' ? (\n <TextField\n select\n size=\"small\"\n label=\"Status\"\n value={value}\n onChange={(event) => setValue(event.target.value)}\n helperText=\"Closing a lead goes through Unqualify, which asks why.\"\n >\n {SETTABLE_STATUSES.map((status) => (\n <MenuItem key={status} value={status}>\n {CRM_LEAD_STATUS_LABELS[status]}\n </MenuItem>\n ))}\n </TextField>\n ) : pending === 'unqualify' ? (\n <TextField\n label=\"Reason\"\n value={value}\n onChange={(event) => setValue(event.target.value)}\n multiline\n minRows={2}\n autoFocus\n helperText=\"One reason for every selected lead, kept on each so it can be counted later.\"\n slotProps={{ htmlInput: { maxLength: UNQUALIFY_REASON_MAX } }}\n />\n ) : pending === 'campaign' ? (\n <ContainerPicker\n kind=\"campaign\"\n options={campaigns.options}\n value={campaignIds}\n onChange={setCampaignIds}\n helperText=\"Added to the campaigns each selected lead is already in. It does not decide who a campaign mails.\"\n empty={campaigns.ready && !campaigns.options.length}\n emptyText={\n hostId\n ? 'This site has no campaigns yet. Create one from Marketing to file leads under it.'\n : 'There are no campaigns yet. Create one from Marketing to file leads under it.'\n }\n />\n ) : null}\n </CrmBulkValueDialog>\n }\n >\n <Button size=\"small\" disabled={busy} onClick={() => openAction('owner')}>\n {'Set owner'}\n </Button>\n <Button size=\"small\" disabled={busy} onClick={() => openAction('status')}>\n {'Set status'}\n </Button>\n <Button size=\"small\" disabled={busy} onClick={() => openAction('unqualify')}>\n {'Unqualify'}\n </Button>\n <Button size=\"small\" disabled={busy} onClick={() => openAction('campaign')}>\n {'Add to campaign'}\n </Button>\n <CrmBulkShareButton\n object=\"leads\"\n ids={selectedRows.map((row) => row.leadId ?? row.$id)}\n hostId={hostId}\n orgId={orgId}\n disabled={busy}\n />\n <Button size=\"small\" disabled={busy} onClick={handleExport}>\n {'Export CSV'}\n </Button>\n {/*\n The selection's file is the rows on screen; this one is the whole\n collection, streamed by the server (AGL-2662).\n */}\n <CrmExportAllButton\n resource=\"leads\"\n orgId={orgId}\n hostId={hostId}\n disabled={busy}\n />\n </CrmBulkBarFrame>\n )\n}\nLeadsBulkBarBody.displayName = 'LeadsBulkBarBody'\n\nexport default LeadsBulkBar\n"],"names":["CRM_LEAD_OPEN_STATUSES","CRM_LEAD_STATUS_LABELS","crmLeadStatus","isCrmLeadOpen","readContainerIds","ContainerPicker","useFirestore","Button","MenuItem","TextField","arrayUnion","deleteField","doc","serverTimestamp","useCallback","useMemo","useState","useCampaignFilingLog","useCrmBulkApply","useCrmCampaigns","downloadTextFile","crmClientListFields","crmBulkWriters","runCrmBulkWrites","leadsCsv","CrmBulkBarFrame","CrmBulkValueDialog","countNoun","CrmExportAllButton","CrmBulkShareButton","LeadOwnerSelect","UNQUALIFY_REASON_MAX","useCrmSharingFollowUp","leadPrimaryGroup","NOUN","singular","plural","ACTION_TITLES","owner","status","unqualify","campaign","SETTABLE_STATUSES","labelOf","lead","String","leadId","LeadsBulkBar","props","selected","length","LeadsBulkBarBody","displayName","rows","onSelectedChange","roster","csv","orgId","hostId","followUpSharing","firestore","busy","report","apply","dismissReport","recordKind","selectedRows","chosen","Set","filter","row","has","$id","pending","setPending","value","setValue","campaignIds","setCampaignIds","campaigns","enabled","logFiling","org","writers","byId","Map","map","id","get","Error","openAction","action","runPlan","plan","done","attempted","writes","skipped","job","outcome","write","label","kind","find","handleApply","push","data","ownerUid","updatedAt","count","union","refused","entry","named","campaignId","name","options","option","already","filed","includes","current","convertedContactId","reason","unqualifiedReason","trim","slice","handleExport","canApply","Boolean","noun","onClear","onDismissReport","extras","open","title","applyLabel","undefined","onClose","onApply","onChange","size","select","event","target","helperText","multiline","minRows","autoFocus","slotProps","htmlInput","maxLength","empty","ready","emptyText","disabled","onClick","object","ids","resource"],"mappings":"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"}
@@ -42,6 +42,7 @@ import { Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, MenuIte
42
42
  import { deleteField, doc, serverTimestamp, updateDoc } from "firebase/firestore";
43
43
  import { useRouter } from "next/navigation";
44
44
  import { useCallback, useEffect, useMemo, useState } from "react";
45
+ import { leadPrimaryGroup } from "../model/contact-holder.js";
45
46
  import { downloadTextFile } from "../model/contacts-csv.js";
46
47
  import { crmRoutes } from "../model/crm-routes.js";
47
48
  import { LEAD_EMAIL_FILTER_OPTIONS, LEAD_FILTER_CODECS, LEAD_LIST_DECLARATION, LEAD_LIST_FILTER_FIELDS, LEAD_LIST_FILTER_HEADERS, LEAD_SOURCE_FILTER_NONE, LEAD_STATUS_FILTER_OPTIONS, leadClausesForGrid, LEAD_PREFIX_SEARCH, leadClauseImpliesScope, leadClausesToStore, leadQueryClause } from "../model/lead-filters.js";
@@ -60,6 +61,9 @@ import { LeadOwnerSelect } from "./lead-owner-select.js";
60
61
  import { CONVERT_PENDING_ERASURE_REASON } from "./lead-properties-card.js";
61
62
  import { CrmEmailStateChip } from "./crm-email-state-chip.js";
62
63
  import { LeadStatusChip } from "./lead-status-chip.js";
64
+ import { CrmShareChipView } from "./record-sharing-card.js";
65
+ import { crmShareChipFor } from "../model/crm-sharing.js";
66
+ import { useCrmSharingFollowUp } from "../hooks/use-crm-sharing.js";
63
67
  import LeadSurfacesNote from "./lead-surfaces-note.js";
64
68
  import { LeadUnqualifyDialog } from "./lead-unqualify-dialog.js";
65
69
  import LeadsBulkBar from "./leads-bulk-bar.js";
@@ -87,10 +91,13 @@ import OrgLeadSurfacesNote from "./org-lead-surfaces-note.js";
87
91
  const firestore = useFirestore();
88
92
  const router = useRouter();
89
93
  const { enqueueSnackbar } = useSnackbar();
90
- const { orgId, createHostId } = useCrmScope({
94
+ const { orgId, createHostId, consentGroup } = useCrmScope({
91
95
  hostId,
92
96
  org
93
97
  });
98
+ // The viewing site's group: a row it sees only through a share is chipped
99
+ // with who shared it (AGL-3336). None at the organization level.
100
+ const viewingHostIds = consentGroup == null ? void 0 : consentGroup.hostIds;
94
101
  const mount = useCrmOrgMount();
95
102
  const roster = useOrgMemberOptions(orgId);
96
103
  // The org's lead fields, for the optional columns below (AGL-3272).
@@ -341,6 +348,8 @@ import OrgLeadSurfacesNote from "./org-lead-surfaces-note.js";
341
348
  crmApi,
342
349
  enqueueSnackbar
343
350
  ]);
351
+ // A client-direct write owes the org's sharing rules a re-evaluation (AGL-3336).
352
+ const followUpSharing = useCrmSharingFollowUp(hostId, orgId);
344
353
  const writeLead = useCallback(async (lead, fields, done)=>{
345
354
  if (!orgId) {
346
355
  enqueueSnackbar('Still loading this workspace — try again in a moment.', {
@@ -353,6 +362,9 @@ import OrgLeadSurfacesNote from "./org-lead-surfaces-note.js";
353
362
  await updateDoc(doc(firestore, 'orgs', orgId, 'leads', lead.leadId), _extends({}, fields, {
354
363
  updatedAt: serverTimestamp()
355
364
  }));
365
+ followUpSharing('leads', [
366
+ lead.leadId
367
+ ]);
356
368
  enqueueSnackbar(done, {
357
369
  variant: 'success',
358
370
  persist: false
@@ -364,7 +376,8 @@ import OrgLeadSurfacesNote from "./org-lead-surfaces-note.js";
364
376
  }
365
377
  }, [
366
378
  firestore,
367
- enqueueSnackbar
379
+ enqueueSnackbar,
380
+ followUpSharing
368
381
  ]);
369
382
  const columns = useMemo(()=>[
370
383
  {
@@ -383,10 +396,24 @@ import OrgLeadSurfacesNote from "./org-lead-surfaces-note.js";
383
396
  height: '100%'
384
397
  },
385
398
  children: [
386
- /*#__PURE__*/ _jsx(Typography, {
387
- variant: "body2",
388
- noWrap: true,
389
- children: String(row['name'] || row['email'] || row.$id)
399
+ /*#__PURE__*/ _jsxs(Stack, {
400
+ direction: "row",
401
+ spacing: 1,
402
+ sx: {
403
+ alignItems: 'center',
404
+ minWidth: 0
405
+ },
406
+ children: [
407
+ /*#__PURE__*/ _jsx(Typography, {
408
+ variant: "body2",
409
+ noWrap: true,
410
+ children: String(row['name'] || row['email'] || row.$id)
411
+ }),
412
+ /*#__PURE__*/ _jsx(CrmShareChipView, {
413
+ chip: crmShareChipFor(row, viewingHostIds != null ? viewingHostIds : []),
414
+ org: org
415
+ })
416
+ ]
390
417
  }),
391
418
  row['name'] ? /*#__PURE__*/ _jsx(Typography, {
392
419
  variant: "caption",
@@ -533,7 +560,7 @@ import OrgLeadSurfacesNote from "./org-lead-surfaces-note.js";
533
560
  headerName: 'Campaign',
534
561
  flex: 1,
535
562
  minWidth: 150,
536
- valueGetter: (_value, row)=>Aglyn.readCampaignIds(row).map(campaignName).join(', ')
563
+ valueGetter: (_value, row)=>Aglyn.readContainerIds(row, 'campaign').map(campaignName).join(', ')
537
564
  },
538
565
  {
539
566
  field: 'lastSeenAtMs',
@@ -627,6 +654,8 @@ import OrgLeadSurfacesNote from "./org-lead-surfaces-note.js";
627
654
  routes,
628
655
  writeLead,
629
656
  hostId,
657
+ viewingHostIds,
658
+ org,
630
659
  mount,
631
660
  campaignName,
632
661
  leadFields.active,
@@ -784,7 +813,7 @@ import OrgLeadSurfacesNote from "./org-lead-surfaces-note.js";
784
813
  /*#__PURE__*/ _jsx(LeadConvertDialog, {
785
814
  open: Boolean(converting),
786
815
  onClose: ()=>setConverting(null),
787
- hostId: hostId != null ? hostId : Aglyn.leadPrimaryGroup(converting, org).hostId || null,
816
+ hostId: hostId != null ? hostId : leadPrimaryGroup(converting, org).hostId || null,
788
817
  orgId: orgId,
789
818
  org: org,
790
819
  leadId: (_ref1 = converting == null ? void 0 : converting.leadId) != null ? _ref1 : '',
@@ -842,6 +871,10 @@ CrmLeadsSection.displayName = 'CrmLeadsSection';
842
871
  value: "new",
843
872
  children: Aglyn.CRM_LEAD_STATUS_LABELS.new
844
873
  }),
874
+ /*#__PURE__*/ _jsx(MenuItem, {
875
+ value: "nurturing",
876
+ children: Aglyn.CRM_LEAD_STATUS_LABELS.nurturing
877
+ }),
845
878
  /*#__PURE__*/ _jsx(MenuItem, {
846
879
  value: "working",
847
880
  children: Aglyn.CRM_LEAD_STATUS_LABELS.working
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/leads-section.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport type {\n ConsolePluginPageProps,\n CrmLeadFields,\n CrmViewFilterClause,\n CrmLeadStatus,\n} from '@aglyn/aglyn'\nimport {\n mdiAccountArrowRight,\n mdiAccountCancelOutline,\n mdiAccountConvertOutline,\n mdiAccountTieOutline,\n} from '@aglyn/shared-data-mdi'\nimport { CardDisplay, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { ListTable } from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport {\n type OrgMemberOptions,\n useOrgMemberOptions,\n} from '../hooks/use-org-member-options'\nimport { useCrmOrgMount } from '../hooks/use-crm-org-mount'\nimport { useCrmSavedView } from '../hooks/use-crm-saved-view'\nimport { useCrmScope } from '../hooks/use-crm-scope'\nimport { useCrmCampaigns } from '../hooks/use-crm-campaigns'\nimport { scopeTokensForHost } from '@aglyn/aglyn/app-utils/scope-tokens'\nimport { useContactFieldDefinitions } from '../hooks/use-contact-field-definitions'\nimport { customFieldColumns } from './contact-custom-columns'\nimport { useCrmViewGrid } from '../hooks/use-crm-view-grid'\nimport { CRM_LIST_SLOTS, CrmColumnOrderProvider } from './crm-column-menu'\nimport { useCrmFoldsScope, useCrmListQuery } from '../hooks/use-crm-list-query'\nimport CrmViewsControl from './crm-views-control'\nimport { CrmListActions, CrmListToolbar } from './crm-list-toolbar'\nimport RowActionsMenu from '@aglyn/shared-ui-jsx/components/row-actions-menu.component'\nimport ListQueryNotices from '@aglyn/shared-ui-jsx/components/list-query-notices.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport {\n Box,\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n MenuItem,\n Select,\n Stack,\n Typography,\n} from '@mui/material'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport {\n deleteField,\n doc,\n serverTimestamp,\n updateDoc,\n} from 'firebase/firestore'\nimport { useRouter } from 'next/navigation'\nimport { useCallback, useEffect, useMemo, useState } from 'react'\nimport { downloadTextFile } from '../model/contacts-csv'\nimport { crmRoutes } from '../model/crm-routes'\nimport {\n LEAD_EMAIL_FILTER_OPTIONS,\n LEAD_FILTER_CODECS,\n LEAD_LIST_DECLARATION,\n LEAD_LIST_FILTER_FIELDS,\n LEAD_LIST_FILTER_HEADERS,\n LEAD_SOURCE_FILTER_NONE,\n LEAD_STATUS_FILTER_OPTIONS,\n leadClausesForGrid,\n LEAD_PREFIX_SEARCH,\n leadClauseImpliesScope,\n leadClausesToStore,\n leadQueryClause,\n} from '../model/lead-filters'\nimport { crmAskClauses, crmQueryRefusals } from '../model/crm-list-query'\nimport {\n type ListFilterOption,\n listFilterGridColumns,\n} from '@aglyn/shared-ui-jsx/const/list-grid-filter'\nimport { useListGridFilter } from '@aglyn/shared-ui-jsx/hooks/use-list-grid-filter'\nimport ListFilterChips from '@aglyn/shared-ui-jsx/components/list-filter-chips.component'\nimport { useLeadSourcePicklist } from '../hooks/use-lead-source-picklist'\nimport { type LeadCsvOptions, leadsCsv } from '../model/leads-csv'\nimport { LeadConvertDialog } from './lead-convert-dialog'\nimport {\n leadSourceLabel,\n leadSources,\n leadTimeLabel,\n} from './lead-history-card'\nimport { LeadImportButton } from './lead-import-drawer'\nimport NewLeadDrawer, { type NewLeadValues } from './new-lead-drawer'\nimport { useCrmApi } from './use-crm-api'\nimport { LeadOwnerSelect } from './lead-owner-select'\nimport { CONVERT_PENDING_ERASURE_REASON } from './lead-properties-card'\nimport { CrmEmailStateChip } from './crm-email-state-chip'\nimport { LeadStatusChip } from './lead-status-chip'\nimport LeadSurfacesNote from './lead-surfaces-note'\nimport { LeadUnqualifyDialog } from './lead-unqualify-dialog'\nimport LeadsBulkBar from './leads-bulk-bar'\nimport OrgLeadSurfacesNote from './org-lead-surfaces-note'\n\n/**\n * One row of the list. `$id` keys the grid and `leadId` names the document,\n * and they are the same value at both levels (AGL-3275).\n *\n * `$id` used to be `{hostId}/{leadId}` at the organization level, because a\n * lead's id is a person key and the same person met by two sites was two\n * documents carrying it — so the id alone could not key a list that spanned\n * sites. One org collection ends that, and the row no longer carries a site\n * at all: which sites hold this person is `capturedByHostIds`, a fact about\n * the person rather than part of their address.\n */\ntype LeadRow = Record<string, unknown> &\n CrmLeadFields & { $id: string; leadId: string }\n\n/**\n * `/crm/leads` — the people a site has met but not yet qualified (AGL-2608).\n *\n * A section of its own, the way Salesforce keeps Leads apart from Contacts:\n * a lead is a capture — a form, a booking, a sign-up — that somebody has\n * still to work, and it converts into a contact, a company and a deal when\n * it is real. Reads `orgs/{orgId}/leads` narrowed by `visibleTo` to the sites\n * this viewer may see (AGL-3275) — the same collection and the same clause at\n * both levels, which is what lets ONE listener serve a section that used to\n * open one per site — and pages it by a query that carries every filter and\n * the search word (AGL-3321; see `leadQueryClause`).\n *\n * Under a site the clause names that site; at the ORGANIZATION level an\n * org-wide member reads without one, since the rules short-circuit on\n * `isOrgWideMember()` and a clause would only narrow what they may already\n * read. The per-site notes — which of a site's forms file a lead — belong to\n * a site's own hub and are not drawn here.\n */\nexport function CrmLeadsSection(props: ConsolePluginPageProps) {\n const { hostId, org, basePath } = props\n const firestore = useFirestore()\n const router = useRouter()\n const { enqueueSnackbar } = useSnackbar()\n const { orgId, createHostId } = useCrmScope({ hostId, org })\n const mount = useCrmOrgMount()\n const roster = useOrgMemberOptions(orgId)\n // The org's lead fields, for the optional columns below (AGL-3272).\n const leadFields = useContactFieldDefinitions(orgId, 'lead')\n // The org's lead source values (AGL-3298): the filter's menu and the\n // column's sort order.\n const leadSourceList = useLeadSourcePicklist(orgId)\n const routes = crmRoutes(basePath ?? '')\n\n /*\n * Under a site: the ORG collection, narrowed to what this site may see\n * (AGL-3275) by `visibleTo array-contains-any` over the site's own tokens.\n * At the ORGANIZATION level an org-wide member reads with no scope clause,\n * which is what `null` asks for.\n */\n const scopeTokens = useMemo(\n () => (hostId ? scopeTokensForHost(hostId) : null),\n [hostId],\n )\n const dataRoot = useMemo(\n () => (orgId ? (['orgs', orgId] as const) : null),\n [orgId],\n )\n const foldsScope = useCrmFoldsScope(orgId, scopeTokens)\n\n /*\n * What the list is narrowed by is the saved VIEW'S (AGL-2617), and the\n * grid's own Filters panel and quick search edit it (AGL-3313): Status,\n * Email, Owner, Lead source and Campaign are select columns of the grid.\n * The clauses keep the shape the old dropdowns stored — see\n * `leadClausesForGrid` — so no saved view of leads loses its filter.\n */\n const views = useCrmSavedView({\n section: 'leads',\n hostId,\n org: props.org,\n basePath: basePath ?? '',\n })\n const clauses = useMemo(\n () => leadClausesForGrid(views.state.filters),\n [views.state.filters],\n )\n const setClauses = useCallback(\n (next: CrmViewFilterClause[]) => views.setFilters(leadClausesToStore(next)),\n [views.setFilters],\n )\n const gridFilter = useListGridFilter({\n clauses,\n onChange: setClauses,\n selectFields: ['status', 'emailState', 'ownerUid'],\n codecs: LEAD_FILTER_CODECS,\n })\n const campaigns = useCrmCampaigns({ hostId, orgId }, { enabled: true })\n const campaignName = useCallback(\n (id: string) =>\n campaigns.options.find((option) => option.value === id)?.label ?? id,\n [campaigns.options],\n )\n /*\n * The choices each select column offers. A stored clause naming a value\n * no longer listed — a retired campaign, a lead source since removed —\n * stays a choice, so the panel can show it and clear it.\n */\n const filterOptions = useMemo<Record<string, ListFilterOption[]>>(() => {\n const stale = (field: string, known: readonly { value: string }[]) =>\n clauses\n .filter((clause) => clause.field === field && clause.op !== 'isEmpty')\n .flatMap((clause) => clause.value.split(','))\n .map((value) => value.trim())\n .filter((value) => value && !known.some((option) => option.value === value))\n .map((value) => ({ value, label: value }))\n const campaignOptions = campaigns.options.map((option) => ({\n value: option.value,\n label: option.label,\n }))\n // Salesforce's Lead Source (AGL-3298): every value the org keeps, inactive ones marked.\n const sourceOptions = [\n ...leadSourceList.picklist.values.map((value) => ({\n value: value.label,\n label: value.active ? value.label : `${value.label} (inactive)`,\n })),\n { value: LEAD_SOURCE_FILTER_NONE, label: 'No lead source' },\n ]\n return {\n status: LEAD_STATUS_FILTER_OPTIONS,\n emailState: LEAD_EMAIL_FILTER_OPTIONS,\n ownerUid: roster.options.map((option) => ({\n value: option.uid,\n label: Aglyn.crmMemberPickerLabel(option),\n })),\n campaignIds: [...campaignOptions, ...stale('campaignIds', campaignOptions)],\n leadSource: [...sourceOptions, ...stale('leadSource', sourceOptions)],\n }\n }, [clauses, campaigns.options, leadSourceList.picklist, roster.options])\n /*\n * Every clause and the search word on ONE query (AGL-3321): each stored\n * clause asked through the field its writer keeps (`leadQueryClause`),\n * paged by that query, and what one query cannot hold refused by name\n * above the list — never matched over the rows a page happened to load.\n */\n const asked = useMemo(\n () =>\n crmAskClauses(clauses, (clause) =>\n leadQueryClause(clause, { scopeTokens, foldsScope }),\n ),\n [clauses, scopeTokens, foldsScope],\n )\n const searchKey = gridFilter.searchWords.join(' ')\n const paged = useCrmListQuery<Record<string, unknown> & CrmLeadFields & { $id: string }>({\n scope: dataRoot,\n collection: 'leads',\n visibleTo: scopeTokens,\n foldsScope,\n declaration: LEAD_LIST_DECLARATION,\n clauses: asked.clauses,\n search: gridFilter.searchWords,\n impliesScope: leadClauseImpliesScope,\n prefixSearch: LEAD_PREFIX_SEARCH,\n })\n const status = paged.status\n const rows = useMemo<LeadRow[]>(\n () => paged.rows.map((row) => ({ ...row, leadId: row.$id })),\n [paged.rows],\n )\n const refused = useMemo(\n () =>\n crmQueryRefusals(paged.plan, asked, {\n fields: LEAD_LIST_FILTER_FIELDS,\n headers: LEAD_LIST_FILTER_HEADERS,\n options: filterOptions,\n }),\n [paged.plan, asked, filterOptions],\n )\n /** The list as it opens — Open leads, nothing typed — whose emptiness is news. */\n const opening =\n !searchKey.trim() &&\n clauses.length === 1 &&\n clauses[0].field === 'status' &&\n clauses[0].value === 'open'\n\n /*\n * The ticked rows, for the bulk bar (AGL-2662). Cleared when the filter or\n * the search term changes: a selection made on the open leads is not a\n * selection of the unqualified ones, and the bar's count would be over\n * rows no longer listed.\n */\n const [selectedIds, setSelectedIds] = useState<string[]>([])\n useEffect(\n () => setSelectedIds([]),\n [views.state.filters, searchKey, paged.page],\n )\n // How the file names the owner and, at the org level, the site.\n const csvOptions: LeadCsvOptions = useMemo(\n () => ({\n ownerEmail: roster.emailFor,\n ...(hostId ? {} : { siteName: (id: string) => mount?.siteName(id) }),\n }),\n [roster.emailFor, hostId, mount],\n )\n // The page on screen; Export all on the bulk bar takes the whole list.\n const handleExport = useCallback(() => {\n downloadTextFile('leads.csv', 'text/csv', leadsCsv(rows, csvOptions))\n }, [rows, csvOptions])\n\n const [assigning, setAssigning] = useState<LeadRow | null>(null)\n const [unqualifying, setUnqualifying] = useState<LeadRow | null>(null)\n // The row whose conversion dialog is open (AGL-2641) — the same dialog\n // the lead's page opens, fed the row so the list is one click shorter.\n const [converting, setConverting] = useState<LeadRow | null>(null)\n\n /*==========================================\n * NEW LEAD (AGL-3231) — Salesforce's New Lead, in a drawer over the\n * list. The route files the lead through the one lead door under the\n * mounted site, or at the organization level under the site the drawer's\n * picker named; the drawer holds its submit until one is known. It makes\n * a lead and nothing else — the conversion is what makes the contact.\n *=========================================*/\n const crmApi = useCrmApi(createHostId)\n const [createOpen, setCreateOpen] = useState(false)\n const [createBusy, setCreateBusy] = useState(false)\n const [createError, setCreateError] = useState<string | null>(null)\n const handleCreate = useCallback(\n async (values: NewLeadValues) => {\n setCreateBusy(true)\n setCreateError(null)\n try {\n const { response, payload } = await crmApi('leads-create', {\n email: values.email,\n ...(values.name ? { name: values.name } : {}),\n ...(values.company ? { company: values.company } : {}),\n ...(values.jobTitle ? { jobTitle: values.jobTitle } : {}),\n ...(values.phone ? { phone: values.phone } : {}),\n ...(values.website ? { website: values.website } : {}),\n ...(values.leadSource ? { leadSource: values.leadSource } : {}),\n ...(values.address ? { address: values.address } : {}),\n ...(values.tags.length ? { tags: values.tags } : {}),\n ...(values.campaignIds.length\n ? { campaignIds: values.campaignIds }\n : {}),\n ...(values.ownerUid ? { ownerUid: values.ownerUid } : {}),\n ...(values.notes ? { notes: values.notes } : {}),\n // The org's own lead fields (AGL-3272), sent only when one was\n // filled — the route reads the definitions to judge the map, and\n // a body without it pays for no read.\n ...(values.custom ? { custom: values.custom } : {}),\n status: values.status,\n })\n if (!response.ok) {\n // The route's own sentence, shown above the form unchanged.\n setCreateError(\n String(payload['error'] ?? 'The lead could not be added.'),\n )\n return\n }\n // The activity entry is the route's: it verified the caller and\n // performed the write.\n enqueueSnackbar(\n payload['created']\n ? 'Lead added'\n : 'This site already held a lead for that address — it was updated',\n { variant: 'success', persist: false },\n )\n setCreateOpen(false)\n } catch (error) {\n console.error(error)\n setCreateError('The lead could not be added.')\n } finally {\n setCreateBusy(false)\n }\n },\n [crmApi, enqueueSnackbar],\n )\n\n const writeLead = useCallback(\n async (lead: LeadRow, fields: Record<string, unknown>, done: string) => {\n if (!orgId) {\n enqueueSnackbar('Still loading this workspace — try again in a moment.', {\n variant: 'warning',\n persist: false,\n })\n return\n }\n try {\n await updateDoc(\n doc(firestore, 'orgs', orgId, 'leads', lead.leadId),\n {\n ...fields,\n updatedAt: serverTimestamp(),\n },\n )\n enqueueSnackbar(done, { variant: 'success', persist: false })\n } catch (error) {\n enqueueSnackbar(\n error instanceof Error\n ? error.message\n : 'The lead could not be updated.',\n { variant: 'error' },\n )\n }\n },\n [firestore, enqueueSnackbar],\n )\n\n const columns = useMemo<GridColDef[]>(\n () => [\n {\n field: 'name',\n headerName: 'Lead',\n flex: 1.4,\n minWidth: 160,\n valueGetter: (_value, row: LeadRow) =>\n String(row['name'] || row['email'] || ''),\n renderCell: ({ row }: { row: LeadRow }) => (\n <Stack\n spacing={0}\n sx={{ minWidth: 0, justifyContent: 'center', height: '100%' }}\n >\n <Typography variant=\"body2\" noWrap>\n {String(row['name'] || row['email'] || row.$id)}\n </Typography>\n {row['name'] ? (\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {String(row['email'] ?? '')}\n </Typography>\n ) : null}\n </Stack>\n ),\n },\n // The lead's own profile (AGL-3231): the two facts a work queue is\n // scanned by, beside the person.\n {\n field: 'company',\n headerName: 'Company',\n flex: 1,\n minWidth: 140,\n valueGetter: (_value, row: LeadRow) => String(row.company ?? ''),\n },\n {\n field: 'jobTitle',\n headerName: 'Title',\n flex: 0.9,\n minWidth: 130,\n valueGetter: (_value, row: LeadRow) => String(row.jobTitle ?? ''),\n },\n {\n field: 'status',\n headerName: 'Status',\n flex: 0.9,\n minWidth: 150,\n valueGetter: (_value, row: LeadRow) => Aglyn.crmLeadStatus(row),\n renderCell: ({ row }: { row: LeadRow }) => (\n <InlineStatus\n lead={row}\n onChange={(next) => {\n if (next === 'unqualified') {\n setUnqualifying(row)\n return\n }\n void writeLead(\n row,\n {\n status: next,\n ...(Aglyn.crmLeadStatus(row) === 'unqualified'\n ? { unqualifiedReason: deleteField() }\n : {}),\n },\n 'Status updated',\n )\n }}\n />\n ),\n },\n {\n /*\n * The verdict on the address (AGL-3245): the chip the lead's page\n * carries, so a bounced lead is told apart in the queue it is worked\n * from; its label for the sort and the export.\n */\n field: 'emailState',\n headerName: 'Email',\n flex: 0.9,\n minWidth: 150,\n valueGetter: (_value, row: LeadRow) => {\n const state = Aglyn.readEmailState(row)\n return state ? Aglyn.EMAIL_STATE_LABELS[state.status] : ''\n },\n renderCell: ({ row }: { row: LeadRow }) => {\n const state = Aglyn.readEmailState(row)\n return state ? (\n <CrmEmailStateChip state={state} />\n ) : (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'—'}\n </Typography>\n )\n },\n },\n {\n field: 'ownerUid',\n headerName: 'Owner',\n flex: 1,\n minWidth: 140,\n valueGetter: (_value, row: LeadRow) =>\n row.ownerUid ? roster.labelFor(row.ownerUid) : 'Unassigned',\n },\n // Only at the organization level, where a row can be any site's.\n ...(hostId\n ? []\n : [\n {\n field: 'hostId',\n headerName: 'Site',\n flex: 0.9,\n minWidth: 140,\n /*\n * KNOWN BY, not \"the site\" (AGL-3275). A lead is one org row\n * and several sites in a consent group can hold the same\n * person, so this names every site that captured them — the\n * answer the Contacts list has always given in its own column.\n */\n valueGetter: (_value: unknown, row: LeadRow) => {\n const held = Array.isArray(row['capturedByHostIds'])\n ? (row['capturedByHostIds'] as string[])\n : []\n return held.map((id) => mount?.siteName(id) ?? id).join(', ')\n },\n } satisfies GridColDef,\n ]),\n {\n field: 'sources',\n headerName: 'Source',\n flex: 1,\n minWidth: 140,\n valueGetter: (_value, row: LeadRow) =>\n leadSources(row).map(leadSourceLabel).join(', '),\n },\n /*\n * Salesforce's Lead Source (AGL-3298), beside the surfaces that\n * captured the person. Sorted in the order the org keeps its values,\n * as a picklist sorts, with a value the list does not hold after\n * every listed one and a lead with none last.\n */\n {\n field: 'leadSource',\n headerName: 'Lead source',\n flex: 1,\n minWidth: 150,\n valueGetter: (_value, row: LeadRow) => String(row.leadSource ?? ''),\n sortComparator: (a: unknown, b: unknown) =>\n Aglyn.crmPicklistRank(leadSourceList.picklist, a) -\n Aglyn.crmPicklistRank(leadSourceList.picklist, b) ||\n String(a ?? '').localeCompare(String(b ?? '')),\n } satisfies GridColDef,\n {\n field: 'tags',\n headerName: 'Tags',\n flex: 0.9,\n minWidth: 140,\n valueGetter: (_value, row: LeadRow) => (row.tags ?? []).join(', '),\n },\n // The campaigns the lead is filed under (AGL-3254), by name — the\n // ids are the storage.\n {\n field: 'campaignIds',\n headerName: 'Campaign',\n flex: 1,\n minWidth: 150,\n valueGetter: (_value: unknown, row: LeadRow) =>\n Aglyn.readCampaignIds(row).map(campaignName).join(', '),\n } satisfies GridColDef,\n {\n field: 'lastSeenAtMs',\n headerName: 'Last seen',\n flex: 0.9,\n minWidth: 160,\n valueGetter: (_value, row: LeadRow) =>\n leadTimeLabel(row['lastSeenAtMs'] ?? row['createdAt']),\n },\n // The org's lead fields as optional columns (AGL-3272), read off the\n // row's own `custom` map the way the contacts list reads its own.\n // Ahead of the row menu, so the overflow stays at the right edge\n // however many fields the org has defined.\n ...customFieldColumns(leadFields.active),\n {\n field: 'actions',\n headerName: '',\n width: 56,\n sortable: false,\n filterable: false,\n disableColumnMenu: true,\n renderCell: ({ row }: { row: LeadRow }) => {\n /*\n * Why Convert is refused, in the order the lead's page refuses it:\n * a converted lead has its contact already, a closed one was\n * judged not real, and a person with an erasure pending must not\n * be captured again — a conversion is a capture (AGL-2623).\n */\n const converted = Boolean(row.convertedContactId)\n const erasurePending = Aglyn.readErasureRequestedAtMs(row) !== null\n const convertRefusal = converted\n ? 'This lead was converted'\n : !Aglyn.isCrmLeadOpen(row)\n ? 'This lead was unqualified'\n : erasurePending\n ? CONVERT_PENDING_ERASURE_REASON\n : null\n return (\n <Box\n onClick={(event) => event.stopPropagation()}\n sx={{ display: 'flex', alignItems: 'center', height: '100%' }}\n >\n <RowActionsMenu\n label={String(row['email'] ?? row.$id)}\n items={[\n {\n key: 'open',\n label: 'Open lead',\n icon: (\n <MdiIcon path={mdiAccountArrowRight.path} size={0.8} />\n ),\n href: routes.lead(row.leadId),\n },\n {\n key: 'convert',\n label: 'Convert…',\n icon: (\n <MdiIcon\n path={mdiAccountConvertOutline.path}\n size={0.8}\n />\n ),\n onClick: () => setConverting(row),\n disabled: convertRefusal !== null,\n disabledReason: convertRefusal ?? undefined,\n },\n {\n key: 'assign',\n label: 'Assign owner',\n icon: (\n <MdiIcon path={mdiAccountTieOutline.path} size={0.8} />\n ),\n onClick: () => setAssigning(row),\n },\n {\n key: 'unqualify',\n label: 'Unqualify',\n icon: (\n <MdiIcon path={mdiAccountCancelOutline.path} size={0.8} />\n ),\n onClick: () => setUnqualifying(row),\n disabled:\n !Aglyn.isCrmLeadOpen(row) ||\n Boolean(row.convertedContactId),\n disabledReason: row.convertedContactId\n ? 'This lead was converted'\n : 'This lead is already closed',\n },\n ]}\n />\n </Box>\n )\n },\n },\n ],\n [\n roster,\n routes,\n writeLead,\n hostId,\n mount,\n campaignName,\n leadFields.active,\n leadSourceList.picklist,\n ],\n )\n /*\n * The column and sort models are the view's (AGL-2617); the filterable\n * columns are the declared fields, as selects over their choices (AGL-3313).\n */\n const filterColumns = useMemo(\n () =>\n listFilterGridColumns(columns, LEAD_LIST_FILTER_FIELDS, filterOptions, LEAD_LIST_FILTER_HEADERS),\n [columns, filterOptions],\n )\n const grid = useCrmViewGrid(views, filterColumns)\n\n return (\n <>\n <CardDisplay\n header={'Leads'}\n help={Aglyn.pluginDocsHelp('crmLeads', {\n anchor: '#filter-the-leads',\n })}\n contentGutterX\n contentGutterY\n HeaderProps={{\n // The record actions, top right and never clipped (AGL-3311).\n action: (\n <CrmListActions>\n <LeadImportButton hostId={hostId} orgId={orgId} />\n <Button size=\"small\" onClick={handleExport} disabled={!rows.length}>\n {'Export CSV'}\n </Button>\n <Button\n size=\"small\"\n variant=\"contained\"\n onClick={() => setCreateOpen(true)}\n >\n {'New lead'}\n </Button>\n </CrmListActions>\n ),\n }}\n >\n <Stack spacing={2}>\n {/* Which surfaces file a lead, by name (AGL-2612) — under a site its own, at the org level every site's (AGL-2638). */}\n {hostId ? (\n <LeadSurfacesNote hostId={hostId} />\n ) : (\n <OrgLeadSurfacesNote />\n )}\n {/*\n The saved view, and every clause it is narrowed by as a chip\n (AGL-3313). The clauses are set in the grid's own Filters panel;\n the chips show the whole set, which that panel shows one of.\n */}\n <CrmListToolbar label=\"Lead filters\">\n <CrmViewsControl controller={views} allLabel=\"All leads\" />\n <ListFilterChips\n fields={LEAD_LIST_FILTER_FIELDS}\n headers={LEAD_LIST_FILTER_HEADERS}\n clauses={clauses}\n onChange={setClauses}\n options={filterOptions}\n marksServed={false}\n />\n </CrmListToolbar>\n <ListQueryNotices refused={refused} notices={paged.plan.notices} />\n <LeadsBulkBar\n rows={rows}\n selected={selectedIds}\n onSelectedChange={setSelectedIds}\n roster={roster}\n csv={csvOptions}\n orgId={orgId}\n hostId={hostId}\n org={org as Record<string, unknown> | undefined}\n />\n <CrmColumnOrderProvider value={grid.columnOrder}>\n <ListTable\n rows={rows}\n columns={grid.columns}\n slots={CRM_LIST_SLOTS}\n selectable={{\n selected: selectedIds,\n onChange: setSelectedIds,\n }}\n loading={status === 'loading'}\n onOpen={(_id, row: LeadRow) => router.push(routes.lead(row.leadId))}\n // Columns are the view's, controlled (AGL-2617). The query\n // orders the list — newest seen first — so the grid sorts\n // nothing itself: a sort over one page would reorder that page.\n columnVisibilityModel={grid.columnVisibilityModel}\n onColumnVisibilityModelChange={grid.onColumnVisibilityModelChange}\n sortingMode=\"server\"\n disableColumnSorting\n // The panel and the search are the grid's; the query answers\n // both (AGL-3321).\n filterMode=\"server\"\n filterModel={gridFilter.filterModel}\n onFilterModelChange={gridFilter.onFilterModelChange}\n quickFilter\n noRowsLabel={opening ? 'No open leads' : 'No leads match these filters'}\n noRowsDescription={\n opening\n ? 'Bookings and lead-routed forms on your site become leads on their own — or add one with New lead, or bring a list in with Import CSV.'\n : undefined\n }\n // Paged by the footer below, so the grid must not also slice.\n hideFooter\n />\n </CrmColumnOrderProvider>\n <ListPagination\n page={paged.page}\n pageSize={paged.pageSize}\n rowCount={rows.length}\n hasMore={paged.hasMore}\n onPageChange={paged.setPage}\n onPageSizeChange={paged.setPageSize}\n />\n </Stack>\n </CardDisplay>\n <NewLeadDrawer\n open={createOpen}\n onClose={() => setCreateOpen(false)}\n hostId={hostId ?? null}\n org={org as Record<string, unknown> | undefined}\n busy={createBusy}\n error={createError}\n roster={roster}\n orgId={orgId}\n onSubmit={(values) => void handleCreate(values)}\n />\n <AssignOwnerDialog\n lead={assigning}\n roster={roster}\n onClose={() => setAssigning(null)}\n onAssign={(uid) => {\n if (!assigning) return\n void writeLead(\n assigning,\n { ownerUid: uid || deleteField() },\n uid ? 'Owner assigned' : 'Owner cleared',\n )\n setAssigning(null)\n }}\n />\n <LeadUnqualifyDialog\n open={Boolean(unqualifying)}\n onClose={() => setUnqualifying(null)}\n hostId={hostId ?? null}\n leadId={unqualifying?.leadId ?? ''}\n leadLabel={String(\n unqualifying?.['name'] || unqualifying?.['email'] || '',\n )}\n />\n {/* The site the conversion is filed as (AGL-2641), which since\n AGL-3275 is the first site that captured this person rather than\n \"the site the row lives under\" — a lead shared by a consent group\n lives under none of them in particular. Under a site the mounted\n one is used, and the two agree for every single-brand org. */}\n <LeadConvertDialog\n open={Boolean(converting)}\n onClose={() => setConverting(null)}\n hostId={\n hostId ??\n (Aglyn.leadPrimaryGroup(converting, org as Record<string, unknown>).hostId || null)\n }\n orgId={orgId}\n org={org as Record<string, unknown> | undefined}\n leadId={converting?.leadId ?? ''}\n lead={converting ?? {}}\n basePath={basePath ?? ''}\n roster={roster}\n />\n </>\n )\n}\nCrmLeadsSection.displayName = 'CrmLeadsSection'\n\n/**\n * The status, editable in place for a lead that is still open.\n *\n * A converted lead shows the chip alone: its status IS the conversion, and\n * the route stamped it. The select stops its click from reaching the row, or\n * every status change would also open the record.\n */\nfunction InlineStatus(props: {\n lead: LeadRow\n onChange: (next: CrmLeadStatus) => void\n}) {\n const { lead, onChange } = props\n if (lead.convertedContactId) {\n return (\n <Box sx={{ display: 'flex', alignItems: 'center', height: '100%' }}>\n <LeadStatusChip lead={lead} />\n </Box>\n )\n }\n const status = Aglyn.crmLeadStatus(lead)\n return (\n <Box\n onClick={(event) => event.stopPropagation()}\n sx={{\n display: 'flex',\n alignItems: 'center',\n height: '100%',\n width: '100%',\n }}\n >\n <Select\n size=\"small\"\n variant=\"standard\"\n disableUnderline\n value={status}\n onChange={(event) => onChange(event.target.value as CrmLeadStatus)}\n renderValue={() => <LeadStatusChip lead={lead} />}\n sx={{ width: '100%' }}\n >\n <MenuItem value=\"new\">{Aglyn.CRM_LEAD_STATUS_LABELS.new}</MenuItem>\n <MenuItem value=\"working\">\n {Aglyn.CRM_LEAD_STATUS_LABELS.working}\n </MenuItem>\n <MenuItem value=\"unqualified\">{`${Aglyn.CRM_LEAD_STATUS_LABELS.unqualified}…`}</MenuItem>\n </Select>\n </Box>\n )\n}\nInlineStatus.displayName = 'InlineStatus'\n\n/** Hand a lead to a team member. */\nfunction AssignOwnerDialog(props: {\n lead: LeadRow | null\n roster: OrgMemberOptions\n onClose: () => void\n onAssign: (uid: string) => void\n}) {\n const { lead, roster, onClose, onAssign } = props\n const [uid, setUid] = useState<string | null>(null)\n const current = uid ?? String(lead?.ownerUid ?? '')\n return (\n <Dialog\n open={Boolean(lead)}\n onClose={onClose}\n maxWidth=\"xs\"\n fullWidth\n slotProps={{ transition: { onExited: () => setUid(null) } }}\n >\n <DialogTitle>{`Assign ${String(lead?.['name'] || lead?.['email'] || 'lead')}`}</DialogTitle>\n <DialogContent>\n <Box sx={{ pt: 1 }}>\n <LeadOwnerSelect\n value={current}\n onChange={setUid}\n roster={roster}\n size=\"medium\"\n />\n </Box>\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose}>{'Cancel'}</Button>\n <Button variant=\"contained\" onClick={() => onAssign(current)}>\n {'Assign'}\n </Button>\n </DialogActions>\n </Dialog>\n )\n}\nAssignOwnerDialog.displayName = 'AssignOwnerDialog'\n\nexport default CrmLeadsSection\n"],"names":["Aglyn","mdiAccountArrowRight","mdiAccountCancelOutline","mdiAccountConvertOutline","mdiAccountTieOutline","CardDisplay","MdiIcon","ListPagination","ListTable","useOrgMemberOptions","useCrmOrgMount","useCrmSavedView","useCrmScope","useCrmCampaigns","scopeTokensForHost","useContactFieldDefinitions","customFieldColumns","useCrmViewGrid","CRM_LIST_SLOTS","CrmColumnOrderProvider","useCrmFoldsScope","useCrmListQuery","CrmViewsControl","CrmListActions","CrmListToolbar","RowActionsMenu","ListQueryNotices","useSnackbar","useFirestore","Box","Button","Dialog","DialogActions","DialogContent","DialogTitle","MenuItem","Select","Stack","Typography","deleteField","doc","serverTimestamp","updateDoc","useRouter","useCallback","useEffect","useMemo","useState","downloadTextFile","crmRoutes","LEAD_EMAIL_FILTER_OPTIONS","LEAD_FILTER_CODECS","LEAD_LIST_DECLARATION","LEAD_LIST_FILTER_FIELDS","LEAD_LIST_FILTER_HEADERS","LEAD_SOURCE_FILTER_NONE","LEAD_STATUS_FILTER_OPTIONS","leadClausesForGrid","LEAD_PREFIX_SEARCH","leadClauseImpliesScope","leadClausesToStore","leadQueryClause","crmAskClauses","crmQueryRefusals","listFilterGridColumns","useListGridFilter","ListFilterChips","useLeadSourcePicklist","leadsCsv","LeadConvertDialog","leadSourceLabel","leadSources","leadTimeLabel","LeadImportButton","NewLeadDrawer","useCrmApi","LeadOwnerSelect","CONVERT_PENDING_ERASURE_REASON","CrmEmailStateChip","LeadStatusChip","LeadSurfacesNote","LeadUnqualifyDialog","LeadsBulkBar","OrgLeadSurfacesNote","CrmLeadsSection","props","hostId","org","basePath","firestore","router","enqueueSnackbar","orgId","createHostId","mount","roster","leadFields","leadSourceList","routes","scopeTokens","dataRoot","foldsScope","views","section","clauses","state","filters","setClauses","next","setFilters","gridFilter","onChange","selectFields","codecs","campaigns","enabled","campaignName","id","options","find","option","value","label","filterOptions","stale","field","known","filter","clause","op","flatMap","split","map","trim","some","campaignOptions","sourceOptions","picklist","values","active","status","emailState","ownerUid","uid","crmMemberPickerLabel","campaignIds","leadSource","asked","searchKey","searchWords","join","paged","scope","collection","visibleTo","declaration","search","impliesScope","prefixSearch","rows","row","leadId","$id","refused","plan","fields","headers","opening","length","selectedIds","setSelectedIds","page","csvOptions","ownerEmail","emailFor","siteName","handleExport","assigning","setAssigning","unqualifying","setUnqualifying","converting","setConverting","crmApi","createOpen","setCreateOpen","createBusy","setCreateBusy","createError","setCreateError","handleCreate","response","payload","email","name","company","jobTitle","phone","website","address","tags","notes","custom","ok","String","variant","persist","error","console","writeLead","lead","done","updatedAt","Error","message","columns","headerName","flex","minWidth","valueGetter","_value","renderCell","spacing","sx","justifyContent","height","noWrap","color","crmLeadStatus","InlineStatus","unqualifiedReason","readEmailState","EMAIL_STATE_LABELS","labelFor","held","Array","isArray","sortComparator","a","b","crmPicklistRank","localeCompare","readCampaignIds","width","sortable","filterable","disableColumnMenu","converted","Boolean","convertedContactId","erasurePending","readErasureRequestedAtMs","convertRefusal","isCrmLeadOpen","onClick","event","stopPropagation","display","alignItems","items","key","icon","path","size","href","disabled","disabledReason","undefined","filterColumns","grid","header","help","pluginDocsHelp","anchor","contentGutterX","contentGutterY","HeaderProps","action","controller","allLabel","marksServed","notices","selected","onSelectedChange","csv","columnOrder","slots","selectable","loading","onOpen","_id","push","columnVisibilityModel","onColumnVisibilityModelChange","sortingMode","disableColumnSorting","filterMode","filterModel","onFilterModelChange","quickFilter","noRowsLabel","noRowsDescription","hideFooter","pageSize","rowCount","hasMore","onPageChange","setPage","onPageSizeChange","setPageSize","open","onClose","busy","onSubmit","AssignOwnerDialog","onAssign","leadLabel","leadPrimaryGroup","displayName","disableUnderline","target","renderValue","CRM_LEAD_STATUS_LABELS","new","working","unqualified","setUid","current","maxWidth","fullWidth","slotProps","transition","onExited","pt"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/leads-section.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport type {\n ConsolePluginPageProps,\n CrmLeadFields,\n CrmViewFilterClause,\n CrmLeadStatus,\n} from '@aglyn/aglyn'\nimport {\n mdiAccountArrowRight,\n mdiAccountCancelOutline,\n mdiAccountConvertOutline,\n mdiAccountTieOutline,\n} from '@aglyn/shared-data-mdi'\nimport { CardDisplay, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { ListTable } from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport {\n type OrgMemberOptions,\n useOrgMemberOptions,\n} from '../hooks/use-org-member-options'\nimport { useCrmOrgMount } from '../hooks/use-crm-org-mount'\nimport { useCrmSavedView } from '../hooks/use-crm-saved-view'\nimport { useCrmScope } from '../hooks/use-crm-scope'\nimport { useCrmCampaigns } from '../hooks/use-crm-campaigns'\nimport { scopeTokensForHost } from '@aglyn/aglyn/app-utils/scope-tokens'\nimport { useContactFieldDefinitions } from '../hooks/use-contact-field-definitions'\nimport { customFieldColumns } from './contact-custom-columns'\nimport { useCrmViewGrid } from '../hooks/use-crm-view-grid'\nimport { CRM_LIST_SLOTS, CrmColumnOrderProvider } from './crm-column-menu'\nimport { useCrmFoldsScope, useCrmListQuery } from '../hooks/use-crm-list-query'\nimport CrmViewsControl from './crm-views-control'\nimport { CrmListActions, CrmListToolbar } from './crm-list-toolbar'\nimport RowActionsMenu from '@aglyn/shared-ui-jsx/components/row-actions-menu.component'\nimport ListQueryNotices from '@aglyn/shared-ui-jsx/components/list-query-notices.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport {\n Box,\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n MenuItem,\n Select,\n Stack,\n Typography,\n} from '@mui/material'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport {\n deleteField,\n doc,\n serverTimestamp,\n updateDoc,\n} from 'firebase/firestore'\nimport { useRouter } from 'next/navigation'\nimport { useCallback, useEffect, useMemo, useState } from 'react'\nimport { leadPrimaryGroup } from '../model/contact-holder'\nimport { downloadTextFile } from '../model/contacts-csv'\nimport { crmRoutes } from '../model/crm-routes'\nimport {\n LEAD_EMAIL_FILTER_OPTIONS,\n LEAD_FILTER_CODECS,\n LEAD_LIST_DECLARATION,\n LEAD_LIST_FILTER_FIELDS,\n LEAD_LIST_FILTER_HEADERS,\n LEAD_SOURCE_FILTER_NONE,\n LEAD_STATUS_FILTER_OPTIONS,\n leadClausesForGrid,\n LEAD_PREFIX_SEARCH,\n leadClauseImpliesScope,\n leadClausesToStore,\n leadQueryClause,\n} from '../model/lead-filters'\nimport { crmAskClauses, crmQueryRefusals } from '../model/crm-list-query'\nimport {\n type ListFilterOption,\n listFilterGridColumns,\n} from '@aglyn/shared-ui-jsx/const/list-grid-filter'\nimport { useListGridFilter } from '@aglyn/shared-ui-jsx/hooks/use-list-grid-filter'\nimport ListFilterChips from '@aglyn/shared-ui-jsx/components/list-filter-chips.component'\nimport { useLeadSourcePicklist } from '../hooks/use-lead-source-picklist'\nimport { type LeadCsvOptions, leadsCsv } from '../model/leads-csv'\nimport { LeadConvertDialog } from './lead-convert-dialog'\nimport {\n leadSourceLabel,\n leadSources,\n leadTimeLabel,\n} from './lead-history-card'\nimport { LeadImportButton } from './lead-import-drawer'\nimport NewLeadDrawer, { type NewLeadValues } from './new-lead-drawer'\nimport { useCrmApi } from './use-crm-api'\nimport { LeadOwnerSelect } from './lead-owner-select'\nimport { CONVERT_PENDING_ERASURE_REASON } from './lead-properties-card'\nimport { CrmEmailStateChip } from './crm-email-state-chip'\nimport { LeadStatusChip } from './lead-status-chip'\nimport { CrmShareChipView } from './record-sharing-card'\nimport { crmShareChipFor } from '../model/crm-sharing'\nimport { useCrmSharingFollowUp } from '../hooks/use-crm-sharing'\nimport LeadSurfacesNote from './lead-surfaces-note'\nimport { LeadUnqualifyDialog } from './lead-unqualify-dialog'\nimport LeadsBulkBar from './leads-bulk-bar'\nimport OrgLeadSurfacesNote from './org-lead-surfaces-note'\n\n/**\n * One row of the list. `$id` keys the grid and `leadId` names the document,\n * and they are the same value at both levels (AGL-3275).\n *\n * `$id` used to be `{hostId}/{leadId}` at the organization level, because a\n * lead's id is a person key and the same person met by two sites was two\n * documents carrying it — so the id alone could not key a list that spanned\n * sites. One org collection ends that, and the row no longer carries a site\n * at all: which sites hold this person is `capturedByHostIds`, a fact about\n * the person rather than part of their address.\n */\ntype LeadRow = Record<string, unknown> &\n CrmLeadFields & { $id: string; leadId: string }\n\n/**\n * `/crm/leads` — the people a site has met but not yet qualified (AGL-2608).\n *\n * A section of its own, the way Salesforce keeps Leads apart from Contacts:\n * a lead is a capture — a form, a booking, a sign-up — that somebody has\n * still to work, and it converts into a contact, a company and a deal when\n * it is real. Reads `orgs/{orgId}/leads` narrowed by `visibleTo` to the sites\n * this viewer may see (AGL-3275) — the same collection and the same clause at\n * both levels, which is what lets ONE listener serve a section that used to\n * open one per site — and pages it by a query that carries every filter and\n * the search word (AGL-3321; see `leadQueryClause`).\n *\n * Under a site the clause names that site; at the ORGANIZATION level an\n * org-wide member reads without one, since the rules short-circuit on\n * `isOrgWideMember()` and a clause would only narrow what they may already\n * read. The per-site notes — which of a site's forms file a lead — belong to\n * a site's own hub and are not drawn here.\n */\nexport function CrmLeadsSection(props: ConsolePluginPageProps) {\n const { hostId, org, basePath } = props\n const firestore = useFirestore()\n const router = useRouter()\n const { enqueueSnackbar } = useSnackbar()\n const { orgId, createHostId, consentGroup } = useCrmScope({ hostId, org })\n // The viewing site's group: a row it sees only through a share is chipped\n // with who shared it (AGL-3336). None at the organization level.\n const viewingHostIds = consentGroup?.hostIds\n const mount = useCrmOrgMount()\n const roster = useOrgMemberOptions(orgId)\n // The org's lead fields, for the optional columns below (AGL-3272).\n const leadFields = useContactFieldDefinitions(orgId, 'lead')\n // The org's lead source values (AGL-3298): the filter's menu and the\n // column's sort order.\n const leadSourceList = useLeadSourcePicklist(orgId)\n const routes = crmRoutes(basePath ?? '')\n\n /*\n * Under a site: the ORG collection, narrowed to what this site may see\n * (AGL-3275) by `visibleTo array-contains-any` over the site's own tokens.\n * At the ORGANIZATION level an org-wide member reads with no scope clause,\n * which is what `null` asks for.\n */\n const scopeTokens = useMemo(\n () => (hostId ? scopeTokensForHost(hostId) : null),\n [hostId],\n )\n const dataRoot = useMemo(\n () => (orgId ? (['orgs', orgId] as const) : null),\n [orgId],\n )\n const foldsScope = useCrmFoldsScope(orgId, scopeTokens)\n\n /*\n * What the list is narrowed by is the saved VIEW'S (AGL-2617), and the\n * grid's own Filters panel and quick search edit it (AGL-3313): Status,\n * Email, Owner, Lead source and Campaign are select columns of the grid.\n * The clauses keep the shape the old dropdowns stored — see\n * `leadClausesForGrid` — so no saved view of leads loses its filter.\n */\n const views = useCrmSavedView({\n section: 'leads',\n hostId,\n org: props.org,\n basePath: basePath ?? '',\n })\n const clauses = useMemo(\n () => leadClausesForGrid(views.state.filters),\n [views.state.filters],\n )\n const setClauses = useCallback(\n (next: CrmViewFilterClause[]) => views.setFilters(leadClausesToStore(next)),\n [views.setFilters],\n )\n const gridFilter = useListGridFilter({\n clauses,\n onChange: setClauses,\n selectFields: ['status', 'emailState', 'ownerUid'],\n codecs: LEAD_FILTER_CODECS,\n })\n const campaigns = useCrmCampaigns({ hostId, orgId }, { enabled: true })\n const campaignName = useCallback(\n (id: string) =>\n campaigns.options.find((option) => option.value === id)?.label ?? id,\n [campaigns.options],\n )\n /*\n * The choices each select column offers. A stored clause naming a value\n * no longer listed — a retired campaign, a lead source since removed —\n * stays a choice, so the panel can show it and clear it.\n */\n const filterOptions = useMemo<Record<string, ListFilterOption[]>>(() => {\n const stale = (field: string, known: readonly { value: string }[]) =>\n clauses\n .filter((clause) => clause.field === field && clause.op !== 'isEmpty')\n .flatMap((clause) => clause.value.split(','))\n .map((value) => value.trim())\n .filter((value) => value && !known.some((option) => option.value === value))\n .map((value) => ({ value, label: value }))\n const campaignOptions = campaigns.options.map((option) => ({\n value: option.value,\n label: option.label,\n }))\n // Salesforce's Lead Source (AGL-3298): every value the org keeps, inactive ones marked.\n const sourceOptions = [\n ...leadSourceList.picklist.values.map((value) => ({\n value: value.label,\n label: value.active ? value.label : `${value.label} (inactive)`,\n })),\n { value: LEAD_SOURCE_FILTER_NONE, label: 'No lead source' },\n ]\n return {\n status: LEAD_STATUS_FILTER_OPTIONS,\n emailState: LEAD_EMAIL_FILTER_OPTIONS,\n ownerUid: roster.options.map((option) => ({\n value: option.uid,\n label: Aglyn.crmMemberPickerLabel(option),\n })),\n campaignIds: [...campaignOptions, ...stale('campaignIds', campaignOptions)],\n leadSource: [...sourceOptions, ...stale('leadSource', sourceOptions)],\n }\n }, [clauses, campaigns.options, leadSourceList.picklist, roster.options])\n /*\n * Every clause and the search word on ONE query (AGL-3321): each stored\n * clause asked through the field its writer keeps (`leadQueryClause`),\n * paged by that query, and what one query cannot hold refused by name\n * above the list — never matched over the rows a page happened to load.\n */\n const asked = useMemo(\n () =>\n crmAskClauses(clauses, (clause) =>\n leadQueryClause(clause, { scopeTokens, foldsScope }),\n ),\n [clauses, scopeTokens, foldsScope],\n )\n const searchKey = gridFilter.searchWords.join(' ')\n const paged = useCrmListQuery<Record<string, unknown> & CrmLeadFields & { $id: string }>({\n scope: dataRoot,\n collection: 'leads',\n visibleTo: scopeTokens,\n foldsScope,\n declaration: LEAD_LIST_DECLARATION,\n clauses: asked.clauses,\n search: gridFilter.searchWords,\n impliesScope: leadClauseImpliesScope,\n prefixSearch: LEAD_PREFIX_SEARCH,\n })\n const status = paged.status\n const rows = useMemo<LeadRow[]>(\n () => paged.rows.map((row) => ({ ...row, leadId: row.$id })),\n [paged.rows],\n )\n const refused = useMemo(\n () =>\n crmQueryRefusals(paged.plan, asked, {\n fields: LEAD_LIST_FILTER_FIELDS,\n headers: LEAD_LIST_FILTER_HEADERS,\n options: filterOptions,\n }),\n [paged.plan, asked, filterOptions],\n )\n /** The list as it opens — Open leads, nothing typed — whose emptiness is news. */\n const opening =\n !searchKey.trim() &&\n clauses.length === 1 &&\n clauses[0].field === 'status' &&\n clauses[0].value === 'open'\n\n /*\n * The ticked rows, for the bulk bar (AGL-2662). Cleared when the filter or\n * the search term changes: a selection made on the open leads is not a\n * selection of the unqualified ones, and the bar's count would be over\n * rows no longer listed.\n */\n const [selectedIds, setSelectedIds] = useState<string[]>([])\n useEffect(\n () => setSelectedIds([]),\n [views.state.filters, searchKey, paged.page],\n )\n // How the file names the owner and, at the org level, the site.\n const csvOptions: LeadCsvOptions = useMemo(\n () => ({\n ownerEmail: roster.emailFor,\n ...(hostId ? {} : { siteName: (id: string) => mount?.siteName(id) }),\n }),\n [roster.emailFor, hostId, mount],\n )\n // The page on screen; Export all on the bulk bar takes the whole list.\n const handleExport = useCallback(() => {\n downloadTextFile('leads.csv', 'text/csv', leadsCsv(rows, csvOptions))\n }, [rows, csvOptions])\n\n const [assigning, setAssigning] = useState<LeadRow | null>(null)\n const [unqualifying, setUnqualifying] = useState<LeadRow | null>(null)\n // The row whose conversion dialog is open (AGL-2641) — the same dialog\n // the lead's page opens, fed the row so the list is one click shorter.\n const [converting, setConverting] = useState<LeadRow | null>(null)\n\n /*==========================================\n * NEW LEAD (AGL-3231) — Salesforce's New Lead, in a drawer over the\n * list. The route files the lead through the one lead door under the\n * mounted site, or at the organization level under the site the drawer's\n * picker named; the drawer holds its submit until one is known. It makes\n * a lead and nothing else — the conversion is what makes the contact.\n *=========================================*/\n const crmApi = useCrmApi(createHostId)\n const [createOpen, setCreateOpen] = useState(false)\n const [createBusy, setCreateBusy] = useState(false)\n const [createError, setCreateError] = useState<string | null>(null)\n const handleCreate = useCallback(\n async (values: NewLeadValues) => {\n setCreateBusy(true)\n setCreateError(null)\n try {\n const { response, payload } = await crmApi('leads-create', {\n email: values.email,\n ...(values.name ? { name: values.name } : {}),\n ...(values.company ? { company: values.company } : {}),\n ...(values.jobTitle ? { jobTitle: values.jobTitle } : {}),\n ...(values.phone ? { phone: values.phone } : {}),\n ...(values.website ? { website: values.website } : {}),\n ...(values.leadSource ? { leadSource: values.leadSource } : {}),\n ...(values.address ? { address: values.address } : {}),\n ...(values.tags.length ? { tags: values.tags } : {}),\n ...(values.campaignIds.length\n ? { campaignIds: values.campaignIds }\n : {}),\n ...(values.ownerUid ? { ownerUid: values.ownerUid } : {}),\n ...(values.notes ? { notes: values.notes } : {}),\n // The org's own lead fields (AGL-3272), sent only when one was\n // filled — the route reads the definitions to judge the map, and\n // a body without it pays for no read.\n ...(values.custom ? { custom: values.custom } : {}),\n status: values.status,\n })\n if (!response.ok) {\n // The route's own sentence, shown above the form unchanged.\n setCreateError(\n String(payload['error'] ?? 'The lead could not be added.'),\n )\n return\n }\n // The activity entry is the route's: it verified the caller and\n // performed the write.\n enqueueSnackbar(\n payload['created']\n ? 'Lead added'\n : 'This site already held a lead for that address — it was updated',\n { variant: 'success', persist: false },\n )\n setCreateOpen(false)\n } catch (error) {\n console.error(error)\n setCreateError('The lead could not be added.')\n } finally {\n setCreateBusy(false)\n }\n },\n [crmApi, enqueueSnackbar],\n )\n\n // A client-direct write owes the org's sharing rules a re-evaluation (AGL-3336).\n const followUpSharing = useCrmSharingFollowUp(hostId, orgId)\n const writeLead = useCallback(\n async (lead: LeadRow, fields: Record<string, unknown>, done: string) => {\n if (!orgId) {\n enqueueSnackbar('Still loading this workspace — try again in a moment.', {\n variant: 'warning',\n persist: false,\n })\n return\n }\n try {\n await updateDoc(\n doc(firestore, 'orgs', orgId, 'leads', lead.leadId),\n {\n ...fields,\n updatedAt: serverTimestamp(),\n },\n )\n followUpSharing('leads', [lead.leadId])\n enqueueSnackbar(done, { variant: 'success', persist: false })\n } catch (error) {\n enqueueSnackbar(\n error instanceof Error\n ? error.message\n : 'The lead could not be updated.',\n { variant: 'error' },\n )\n }\n },\n [firestore, enqueueSnackbar, followUpSharing],\n )\n\n const columns = useMemo<GridColDef[]>(\n () => [\n {\n field: 'name',\n headerName: 'Lead',\n flex: 1.4,\n minWidth: 160,\n valueGetter: (_value, row: LeadRow) =>\n String(row['name'] || row['email'] || ''),\n renderCell: ({ row }: { row: LeadRow }) => (\n <Stack\n spacing={0}\n sx={{ minWidth: 0, justifyContent: 'center', height: '100%' }}\n >\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center', minWidth: 0 }}>\n <Typography variant=\"body2\" noWrap>\n {String(row['name'] || row['email'] || row.$id)}\n </Typography>\n <CrmShareChipView chip={crmShareChipFor(row, viewingHostIds ?? [])} org={org} />\n </Stack>\n {row['name'] ? (\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {String(row['email'] ?? '')}\n </Typography>\n ) : null}\n </Stack>\n ),\n },\n // The lead's own profile (AGL-3231): the two facts a work queue is\n // scanned by, beside the person.\n {\n field: 'company',\n headerName: 'Company',\n flex: 1,\n minWidth: 140,\n valueGetter: (_value, row: LeadRow) => String(row.company ?? ''),\n },\n {\n field: 'jobTitle',\n headerName: 'Title',\n flex: 0.9,\n minWidth: 130,\n valueGetter: (_value, row: LeadRow) => String(row.jobTitle ?? ''),\n },\n {\n field: 'status',\n headerName: 'Status',\n flex: 0.9,\n minWidth: 150,\n valueGetter: (_value, row: LeadRow) => Aglyn.crmLeadStatus(row),\n renderCell: ({ row }: { row: LeadRow }) => (\n <InlineStatus\n lead={row}\n onChange={(next) => {\n if (next === 'unqualified') {\n setUnqualifying(row)\n return\n }\n void writeLead(\n row,\n {\n status: next,\n ...(Aglyn.crmLeadStatus(row) === 'unqualified'\n ? { unqualifiedReason: deleteField() }\n : {}),\n },\n 'Status updated',\n )\n }}\n />\n ),\n },\n {\n /*\n * The verdict on the address (AGL-3245): the chip the lead's page\n * carries, so a bounced lead is told apart in the queue it is worked\n * from; its label for the sort and the export.\n */\n field: 'emailState',\n headerName: 'Email',\n flex: 0.9,\n minWidth: 150,\n valueGetter: (_value, row: LeadRow) => {\n const state = Aglyn.readEmailState(row)\n return state ? Aglyn.EMAIL_STATE_LABELS[state.status] : ''\n },\n renderCell: ({ row }: { row: LeadRow }) => {\n const state = Aglyn.readEmailState(row)\n return state ? (\n <CrmEmailStateChip state={state} />\n ) : (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'—'}\n </Typography>\n )\n },\n },\n {\n field: 'ownerUid',\n headerName: 'Owner',\n flex: 1,\n minWidth: 140,\n valueGetter: (_value, row: LeadRow) =>\n row.ownerUid ? roster.labelFor(row.ownerUid) : 'Unassigned',\n },\n // Only at the organization level, where a row can be any site's.\n ...(hostId\n ? []\n : [\n {\n field: 'hostId',\n headerName: 'Site',\n flex: 0.9,\n minWidth: 140,\n /*\n * KNOWN BY, not \"the site\" (AGL-3275). A lead is one org row\n * and several sites in a consent group can hold the same\n * person, so this names every site that captured them — the\n * answer the Contacts list has always given in its own column.\n */\n valueGetter: (_value: unknown, row: LeadRow) => {\n const held = Array.isArray(row['capturedByHostIds'])\n ? (row['capturedByHostIds'] as string[])\n : []\n return held.map((id) => mount?.siteName(id) ?? id).join(', ')\n },\n } satisfies GridColDef,\n ]),\n {\n field: 'sources',\n headerName: 'Source',\n flex: 1,\n minWidth: 140,\n valueGetter: (_value, row: LeadRow) =>\n leadSources(row).map(leadSourceLabel).join(', '),\n },\n /*\n * Salesforce's Lead Source (AGL-3298), beside the surfaces that\n * captured the person. Sorted in the order the org keeps its values,\n * as a picklist sorts, with a value the list does not hold after\n * every listed one and a lead with none last.\n */\n {\n field: 'leadSource',\n headerName: 'Lead source',\n flex: 1,\n minWidth: 150,\n valueGetter: (_value, row: LeadRow) => String(row.leadSource ?? ''),\n sortComparator: (a: unknown, b: unknown) =>\n Aglyn.crmPicklistRank(leadSourceList.picklist, a) -\n Aglyn.crmPicklistRank(leadSourceList.picklist, b) ||\n String(a ?? '').localeCompare(String(b ?? '')),\n } satisfies GridColDef,\n {\n field: 'tags',\n headerName: 'Tags',\n flex: 0.9,\n minWidth: 140,\n valueGetter: (_value, row: LeadRow) => (row.tags ?? []).join(', '),\n },\n // The campaigns the lead is filed under (AGL-3254), by name — the\n // ids are the storage.\n {\n field: 'campaignIds',\n headerName: 'Campaign',\n flex: 1,\n minWidth: 150,\n valueGetter: (_value: unknown, row: LeadRow) =>\n Aglyn.readContainerIds(row, 'campaign').map(campaignName).join(', '),\n } satisfies GridColDef,\n {\n field: 'lastSeenAtMs',\n headerName: 'Last seen',\n flex: 0.9,\n minWidth: 160,\n valueGetter: (_value, row: LeadRow) =>\n leadTimeLabel(row['lastSeenAtMs'] ?? row['createdAt']),\n },\n // The org's lead fields as optional columns (AGL-3272), read off the\n // row's own `custom` map the way the contacts list reads its own.\n // Ahead of the row menu, so the overflow stays at the right edge\n // however many fields the org has defined.\n ...customFieldColumns(leadFields.active),\n {\n field: 'actions',\n headerName: '',\n width: 56,\n sortable: false,\n filterable: false,\n disableColumnMenu: true,\n renderCell: ({ row }: { row: LeadRow }) => {\n /*\n * Why Convert is refused, in the order the lead's page refuses it:\n * a converted lead has its contact already, a closed one was\n * judged not real, and a person with an erasure pending must not\n * be captured again — a conversion is a capture (AGL-2623).\n */\n const converted = Boolean(row.convertedContactId)\n const erasurePending = Aglyn.readErasureRequestedAtMs(row) !== null\n const convertRefusal = converted\n ? 'This lead was converted'\n : !Aglyn.isCrmLeadOpen(row)\n ? 'This lead was unqualified'\n : erasurePending\n ? CONVERT_PENDING_ERASURE_REASON\n : null\n return (\n <Box\n onClick={(event) => event.stopPropagation()}\n sx={{ display: 'flex', alignItems: 'center', height: '100%' }}\n >\n <RowActionsMenu\n label={String(row['email'] ?? row.$id)}\n items={[\n {\n key: 'open',\n label: 'Open lead',\n icon: (\n <MdiIcon path={mdiAccountArrowRight.path} size={0.8} />\n ),\n href: routes.lead(row.leadId),\n },\n {\n key: 'convert',\n label: 'Convert…',\n icon: (\n <MdiIcon\n path={mdiAccountConvertOutline.path}\n size={0.8}\n />\n ),\n onClick: () => setConverting(row),\n disabled: convertRefusal !== null,\n disabledReason: convertRefusal ?? undefined,\n },\n {\n key: 'assign',\n label: 'Assign owner',\n icon: (\n <MdiIcon path={mdiAccountTieOutline.path} size={0.8} />\n ),\n onClick: () => setAssigning(row),\n },\n {\n key: 'unqualify',\n label: 'Unqualify',\n icon: (\n <MdiIcon path={mdiAccountCancelOutline.path} size={0.8} />\n ),\n onClick: () => setUnqualifying(row),\n disabled:\n !Aglyn.isCrmLeadOpen(row) ||\n Boolean(row.convertedContactId),\n disabledReason: row.convertedContactId\n ? 'This lead was converted'\n : 'This lead is already closed',\n },\n ]}\n />\n </Box>\n )\n },\n },\n ],\n [\n roster,\n routes,\n writeLead,\n hostId,\n viewingHostIds,\n org,\n mount,\n campaignName,\n leadFields.active,\n leadSourceList.picklist,\n ],\n )\n /*\n * The column and sort models are the view's (AGL-2617); the filterable\n * columns are the declared fields, as selects over their choices (AGL-3313).\n */\n const filterColumns = useMemo(\n () =>\n listFilterGridColumns(columns, LEAD_LIST_FILTER_FIELDS, filterOptions, LEAD_LIST_FILTER_HEADERS),\n [columns, filterOptions],\n )\n const grid = useCrmViewGrid(views, filterColumns)\n\n return (\n <>\n <CardDisplay\n header={'Leads'}\n help={Aglyn.pluginDocsHelp('crmLeads', {\n anchor: '#filter-the-leads',\n })}\n contentGutterX\n contentGutterY\n HeaderProps={{\n // The record actions, top right and never clipped (AGL-3311).\n action: (\n <CrmListActions>\n <LeadImportButton hostId={hostId} orgId={orgId} />\n <Button size=\"small\" onClick={handleExport} disabled={!rows.length}>\n {'Export CSV'}\n </Button>\n <Button\n size=\"small\"\n variant=\"contained\"\n onClick={() => setCreateOpen(true)}\n >\n {'New lead'}\n </Button>\n </CrmListActions>\n ),\n }}\n >\n <Stack spacing={2}>\n {/* Which surfaces file a lead, by name (AGL-2612) — under a site its own, at the org level every site's (AGL-2638). */}\n {hostId ? (\n <LeadSurfacesNote hostId={hostId} />\n ) : (\n <OrgLeadSurfacesNote />\n )}\n {/*\n The saved view, and every clause it is narrowed by as a chip\n (AGL-3313). The clauses are set in the grid's own Filters panel;\n the chips show the whole set, which that panel shows one of.\n */}\n <CrmListToolbar label=\"Lead filters\">\n <CrmViewsControl controller={views} allLabel=\"All leads\" />\n <ListFilterChips\n fields={LEAD_LIST_FILTER_FIELDS}\n headers={LEAD_LIST_FILTER_HEADERS}\n clauses={clauses}\n onChange={setClauses}\n options={filterOptions}\n marksServed={false}\n />\n </CrmListToolbar>\n <ListQueryNotices refused={refused} notices={paged.plan.notices} />\n <LeadsBulkBar\n rows={rows}\n selected={selectedIds}\n onSelectedChange={setSelectedIds}\n roster={roster}\n csv={csvOptions}\n orgId={orgId}\n hostId={hostId}\n org={org as Record<string, unknown> | undefined}\n />\n <CrmColumnOrderProvider value={grid.columnOrder}>\n <ListTable\n rows={rows}\n columns={grid.columns}\n slots={CRM_LIST_SLOTS}\n selectable={{\n selected: selectedIds,\n onChange: setSelectedIds,\n }}\n loading={status === 'loading'}\n onOpen={(_id, row: LeadRow) => router.push(routes.lead(row.leadId))}\n // Columns are the view's, controlled (AGL-2617). The query\n // orders the list — newest seen first — so the grid sorts\n // nothing itself: a sort over one page would reorder that page.\n columnVisibilityModel={grid.columnVisibilityModel}\n onColumnVisibilityModelChange={grid.onColumnVisibilityModelChange}\n sortingMode=\"server\"\n disableColumnSorting\n // The panel and the search are the grid's; the query answers\n // both (AGL-3321).\n filterMode=\"server\"\n filterModel={gridFilter.filterModel}\n onFilterModelChange={gridFilter.onFilterModelChange}\n quickFilter\n noRowsLabel={opening ? 'No open leads' : 'No leads match these filters'}\n noRowsDescription={\n opening\n ? 'Bookings and lead-routed forms on your site become leads on their own — or add one with New lead, or bring a list in with Import CSV.'\n : undefined\n }\n // Paged by the footer below, so the grid must not also slice.\n hideFooter\n />\n </CrmColumnOrderProvider>\n <ListPagination\n page={paged.page}\n pageSize={paged.pageSize}\n rowCount={rows.length}\n hasMore={paged.hasMore}\n onPageChange={paged.setPage}\n onPageSizeChange={paged.setPageSize}\n />\n </Stack>\n </CardDisplay>\n <NewLeadDrawer\n open={createOpen}\n onClose={() => setCreateOpen(false)}\n hostId={hostId ?? null}\n org={org as Record<string, unknown> | undefined}\n busy={createBusy}\n error={createError}\n roster={roster}\n orgId={orgId}\n onSubmit={(values) => void handleCreate(values)}\n />\n <AssignOwnerDialog\n lead={assigning}\n roster={roster}\n onClose={() => setAssigning(null)}\n onAssign={(uid) => {\n if (!assigning) return\n void writeLead(\n assigning,\n { ownerUid: uid || deleteField() },\n uid ? 'Owner assigned' : 'Owner cleared',\n )\n setAssigning(null)\n }}\n />\n <LeadUnqualifyDialog\n open={Boolean(unqualifying)}\n onClose={() => setUnqualifying(null)}\n hostId={hostId ?? null}\n leadId={unqualifying?.leadId ?? ''}\n leadLabel={String(\n unqualifying?.['name'] || unqualifying?.['email'] || '',\n )}\n />\n {/* The site the conversion is filed as (AGL-2641), which since\n AGL-3275 is the first site that captured this person rather than\n \"the site the row lives under\" — a lead shared by a consent group\n lives under none of them in particular. Under a site the mounted\n one is used, and the two agree for every single-brand org. */}\n <LeadConvertDialog\n open={Boolean(converting)}\n onClose={() => setConverting(null)}\n hostId={\n hostId ??\n (leadPrimaryGroup(converting, org as Record<string, unknown>).hostId || null)\n }\n orgId={orgId}\n org={org as Record<string, unknown> | undefined}\n leadId={converting?.leadId ?? ''}\n lead={converting ?? {}}\n basePath={basePath ?? ''}\n roster={roster}\n />\n </>\n )\n}\nCrmLeadsSection.displayName = 'CrmLeadsSection'\n\n/**\n * The status, editable in place for a lead that is still open.\n *\n * A converted lead shows the chip alone: its status IS the conversion, and\n * the route stamped it. The select stops its click from reaching the row, or\n * every status change would also open the record.\n */\nfunction InlineStatus(props: {\n lead: LeadRow\n onChange: (next: CrmLeadStatus) => void\n}) {\n const { lead, onChange } = props\n if (lead.convertedContactId) {\n return (\n <Box sx={{ display: 'flex', alignItems: 'center', height: '100%' }}>\n <LeadStatusChip lead={lead} />\n </Box>\n )\n }\n const status = Aglyn.crmLeadStatus(lead)\n return (\n <Box\n onClick={(event) => event.stopPropagation()}\n sx={{\n display: 'flex',\n alignItems: 'center',\n height: '100%',\n width: '100%',\n }}\n >\n <Select\n size=\"small\"\n variant=\"standard\"\n disableUnderline\n value={status}\n onChange={(event) => onChange(event.target.value as CrmLeadStatus)}\n renderValue={() => <LeadStatusChip lead={lead} />}\n sx={{ width: '100%' }}\n >\n <MenuItem value=\"new\">{Aglyn.CRM_LEAD_STATUS_LABELS.new}</MenuItem>\n <MenuItem value=\"nurturing\">\n {Aglyn.CRM_LEAD_STATUS_LABELS.nurturing}\n </MenuItem>\n <MenuItem value=\"working\">\n {Aglyn.CRM_LEAD_STATUS_LABELS.working}\n </MenuItem>\n <MenuItem value=\"unqualified\">{`${Aglyn.CRM_LEAD_STATUS_LABELS.unqualified}…`}</MenuItem>\n </Select>\n </Box>\n )\n}\nInlineStatus.displayName = 'InlineStatus'\n\n/** Hand a lead to a team member. */\nfunction AssignOwnerDialog(props: {\n lead: LeadRow | null\n roster: OrgMemberOptions\n onClose: () => void\n onAssign: (uid: string) => void\n}) {\n const { lead, roster, onClose, onAssign } = props\n const [uid, setUid] = useState<string | null>(null)\n const current = uid ?? String(lead?.ownerUid ?? '')\n return (\n <Dialog\n open={Boolean(lead)}\n onClose={onClose}\n maxWidth=\"xs\"\n fullWidth\n slotProps={{ transition: { onExited: () => setUid(null) } }}\n >\n <DialogTitle>{`Assign ${String(lead?.['name'] || lead?.['email'] || 'lead')}`}</DialogTitle>\n <DialogContent>\n <Box sx={{ pt: 1 }}>\n <LeadOwnerSelect\n value={current}\n onChange={setUid}\n roster={roster}\n size=\"medium\"\n />\n </Box>\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose}>{'Cancel'}</Button>\n <Button variant=\"contained\" onClick={() => onAssign(current)}>\n {'Assign'}\n </Button>\n </DialogActions>\n </Dialog>\n )\n}\nAssignOwnerDialog.displayName = 'AssignOwnerDialog'\n\nexport default CrmLeadsSection\n"],"names":["Aglyn","mdiAccountArrowRight","mdiAccountCancelOutline","mdiAccountConvertOutline","mdiAccountTieOutline","CardDisplay","MdiIcon","ListPagination","ListTable","useOrgMemberOptions","useCrmOrgMount","useCrmSavedView","useCrmScope","useCrmCampaigns","scopeTokensForHost","useContactFieldDefinitions","customFieldColumns","useCrmViewGrid","CRM_LIST_SLOTS","CrmColumnOrderProvider","useCrmFoldsScope","useCrmListQuery","CrmViewsControl","CrmListActions","CrmListToolbar","RowActionsMenu","ListQueryNotices","useSnackbar","useFirestore","Box","Button","Dialog","DialogActions","DialogContent","DialogTitle","MenuItem","Select","Stack","Typography","deleteField","doc","serverTimestamp","updateDoc","useRouter","useCallback","useEffect","useMemo","useState","leadPrimaryGroup","downloadTextFile","crmRoutes","LEAD_EMAIL_FILTER_OPTIONS","LEAD_FILTER_CODECS","LEAD_LIST_DECLARATION","LEAD_LIST_FILTER_FIELDS","LEAD_LIST_FILTER_HEADERS","LEAD_SOURCE_FILTER_NONE","LEAD_STATUS_FILTER_OPTIONS","leadClausesForGrid","LEAD_PREFIX_SEARCH","leadClauseImpliesScope","leadClausesToStore","leadQueryClause","crmAskClauses","crmQueryRefusals","listFilterGridColumns","useListGridFilter","ListFilterChips","useLeadSourcePicklist","leadsCsv","LeadConvertDialog","leadSourceLabel","leadSources","leadTimeLabel","LeadImportButton","NewLeadDrawer","useCrmApi","LeadOwnerSelect","CONVERT_PENDING_ERASURE_REASON","CrmEmailStateChip","LeadStatusChip","CrmShareChipView","crmShareChipFor","useCrmSharingFollowUp","LeadSurfacesNote","LeadUnqualifyDialog","LeadsBulkBar","OrgLeadSurfacesNote","CrmLeadsSection","props","hostId","org","basePath","firestore","router","enqueueSnackbar","orgId","createHostId","consentGroup","viewingHostIds","hostIds","mount","roster","leadFields","leadSourceList","routes","scopeTokens","dataRoot","foldsScope","views","section","clauses","state","filters","setClauses","next","setFilters","gridFilter","onChange","selectFields","codecs","campaigns","enabled","campaignName","id","options","find","option","value","label","filterOptions","stale","field","known","filter","clause","op","flatMap","split","map","trim","some","campaignOptions","sourceOptions","picklist","values","active","status","emailState","ownerUid","uid","crmMemberPickerLabel","campaignIds","leadSource","asked","searchKey","searchWords","join","paged","scope","collection","visibleTo","declaration","search","impliesScope","prefixSearch","rows","row","leadId","$id","refused","plan","fields","headers","opening","length","selectedIds","setSelectedIds","page","csvOptions","ownerEmail","emailFor","siteName","handleExport","assigning","setAssigning","unqualifying","setUnqualifying","converting","setConverting","crmApi","createOpen","setCreateOpen","createBusy","setCreateBusy","createError","setCreateError","handleCreate","response","payload","email","name","company","jobTitle","phone","website","address","tags","notes","custom","ok","String","variant","persist","error","console","followUpSharing","writeLead","lead","done","updatedAt","Error","message","columns","headerName","flex","minWidth","valueGetter","_value","renderCell","spacing","sx","justifyContent","height","direction","alignItems","noWrap","chip","color","crmLeadStatus","InlineStatus","unqualifiedReason","readEmailState","EMAIL_STATE_LABELS","labelFor","held","Array","isArray","sortComparator","a","b","crmPicklistRank","localeCompare","readContainerIds","width","sortable","filterable","disableColumnMenu","converted","Boolean","convertedContactId","erasurePending","readErasureRequestedAtMs","convertRefusal","isCrmLeadOpen","onClick","event","stopPropagation","display","items","key","icon","path","size","href","disabled","disabledReason","undefined","filterColumns","grid","header","help","pluginDocsHelp","anchor","contentGutterX","contentGutterY","HeaderProps","action","controller","allLabel","marksServed","notices","selected","onSelectedChange","csv","columnOrder","slots","selectable","loading","onOpen","_id","push","columnVisibilityModel","onColumnVisibilityModelChange","sortingMode","disableColumnSorting","filterMode","filterModel","onFilterModelChange","quickFilter","noRowsLabel","noRowsDescription","hideFooter","pageSize","rowCount","hasMore","onPageChange","setPage","onPageSizeChange","setPageSize","open","onClose","busy","onSubmit","AssignOwnerDialog","onAssign","leadLabel","displayName","disableUnderline","target","renderValue","CRM_LEAD_STATUS_LABELS","new","nurturing","working","unqualified","setUid","current","maxWidth","fullWidth","slotProps","transition","onExited","pt"],"mappings":"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"}
@@ -1,12 +1,12 @@
1
1
  'use client';
2
2
  import { _ as _extends } from "@swc/helpers/_/_extends";
3
3
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
4
- import { CRM_LEAD_STATUS_LABELS, CRM_LEAD_TEXT_MAX, campaignMembershipValue, crmPicklistDefaultLabel, normalizeCrmLeadTags, normalizeContactEmail, normalizeCrmLeadProfile } from "@aglyn/aglyn";
4
+ import { CRM_LEAD_STATUS_LABELS, CRM_LEAD_TEXT_MAX, containerMembershipValue, crmPicklistDefaultLabel, normalizeCrmLeadTags, normalizeContactEmail, normalizeCrmLeadProfile } from "@aglyn/aglyn";
5
5
  import { ICON_VARIANT_CLOSE } from "@aglyn/shared-data-enums";
6
6
  import { Container, MdiIcon, SrOnly } from "@aglyn/shared-ui-jsx";
7
7
  import { NavigationDrawerComponent } from "@aglyn/shared-ui-jsx/components/navigation-drawer.component";
8
8
  import { Alert, Button, IconButton, MenuItem, Stack, TextField, Typography } from "@mui/material";
9
- import CampaignPicker from "@aglyn/shared-ui-email-campaigns/components/campaign-picker.component";
9
+ import ContainerPicker from "@aglyn/tenant-feature-instance/components/container-picker";
10
10
  import { useEffect, useState } from "react";
11
11
  import { useContactFieldDefinitions } from "../hooks/use-contact-field-definitions.js";
12
12
  import { useCrmCampaigns } from "../hooks/use-crm-campaigns.js";
@@ -152,7 +152,7 @@ const NOTES_MAX = 4000;
152
152
  status,
153
153
  ownerUid,
154
154
  tags: (_patch_tags = patch.tags) != null ? _patch_tags : [],
155
- campaignIds: campaignMembershipValue(campaignIds),
155
+ campaignIds: containerMembershipValue(campaignIds),
156
156
  address: (_patch_address = patch.address) != null ? _patch_address : null,
157
157
  notes: notes.trim().slice(0, NOTES_MAX),
158
158
  custom: crmCustomDraftDocument(custom)
@@ -337,7 +337,8 @@ const NOTES_MAX = 4000;
337
337
  onChange: (event)=>setTags(event.target.value),
338
338
  fullWidth: true
339
339
  }),
340
- /*#__PURE__*/ _jsx(CampaignPicker, {
340
+ /*#__PURE__*/ _jsx(ContainerPicker, {
341
+ kind: "campaign",
341
342
  options: campaigns.options,
342
343
  value: campaignIds,
343
344
  onChange: setCampaignIds,