@aglyn/plugins-crm 1.0.0-beta.221 → 1.0.0-beta.222

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 (514) hide show
  1. package/README.md +2 -1
  2. package/package.json +13 -13
  3. package/src/lib/components/activity-list.js +12 -5
  4. package/src/lib/components/activity-list.js.map +1 -1
  5. package/src/lib/components/companies-bulk-bar.d.ts +1 -4
  6. package/src/lib/components/companies-bulk-bar.js +7 -20
  7. package/src/lib/components/companies-bulk-bar.js.map +1 -1
  8. package/src/lib/components/companies-section.d.ts +3 -3
  9. package/src/lib/components/companies-section.js +149 -46
  10. package/src/lib/components/companies-section.js.map +1 -1
  11. package/src/lib/components/company-edit-drawer.js +281 -20
  12. package/src/lib/components/company-edit-drawer.js.map +1 -1
  13. package/src/lib/components/company-properties-card.js +99 -8
  14. package/src/lib/components/company-properties-card.js.map +1 -1
  15. package/src/lib/components/contact-detail-page.js +3 -1
  16. package/src/lib/components/contact-detail-page.js.map +1 -1
  17. package/src/lib/components/contact-merge-dialog.js +33 -10
  18. package/src/lib/components/contact-merge-dialog.js.map +1 -1
  19. package/src/lib/components/contact-properties-card.d.ts +0 -69
  20. package/src/lib/components/contact-properties-card.js +350 -60
  21. package/src/lib/components/contact-properties-card.js.map +1 -1
  22. package/src/lib/components/contact-reports-to-field.d.ts +46 -0
  23. package/src/lib/components/contact-reports-to-field.js +67 -0
  24. package/src/lib/components/contact-reports-to-field.js.map +1 -0
  25. package/src/lib/components/contacts-bulk-bar.d.ts +1 -8
  26. package/src/lib/components/contacts-bulk-bar.js +7 -19
  27. package/src/lib/components/contacts-bulk-bar.js.map +1 -1
  28. package/src/lib/components/contacts-section.js +46 -33
  29. package/src/lib/components/contacts-section.js.map +1 -1
  30. package/src/lib/components/crm-call-actions.d.ts +10 -0
  31. package/src/lib/components/crm-call-actions.js +26 -9
  32. package/src/lib/components/crm-call-actions.js.map +1 -1
  33. package/src/lib/components/crm-record-picker.d.ts +15 -0
  34. package/src/lib/components/crm-record-picker.js +16 -10
  35. package/src/lib/components/crm-record-picker.js.map +1 -1
  36. package/src/lib/components/crm-transfer-buttons.d.ts +54 -0
  37. package/src/lib/components/crm-transfer-buttons.js +167 -0
  38. package/src/lib/components/crm-transfer-buttons.js.map +1 -0
  39. package/src/lib/components/deal-contact-roles-card.d.ts +46 -0
  40. package/src/lib/components/deal-contact-roles-card.js +368 -0
  41. package/src/lib/components/deal-contact-roles-card.js.map +1 -0
  42. package/src/lib/components/deal-detail-page.d.ts +3 -2
  43. package/src/lib/components/deal-detail-page.js +25 -6
  44. package/src/lib/components/deal-detail-page.js.map +1 -1
  45. package/src/lib/components/deal-edit-drawer.d.ts +10 -0
  46. package/src/lib/components/deal-edit-drawer.js +219 -10
  47. package/src/lib/components/deal-edit-drawer.js.map +1 -1
  48. package/src/lib/components/deal-properties-card.d.ts +7 -0
  49. package/src/lib/components/deal-properties-card.js +34 -3
  50. package/src/lib/components/deal-properties-card.js.map +1 -1
  51. package/src/lib/components/deal-stage-card.d.ts +4 -0
  52. package/src/lib/components/deal-stage-card.js +10 -2
  53. package/src/lib/components/deal-stage-card.js.map +1 -1
  54. package/src/lib/components/deals-bulk-bar.d.ts +0 -3
  55. package/src/lib/components/deals-bulk-bar.js +7 -20
  56. package/src/lib/components/deals-bulk-bar.js.map +1 -1
  57. package/src/lib/components/deals-section.d.ts +3 -3
  58. package/src/lib/components/deals-section.js +123 -63
  59. package/src/lib/components/deals-section.js.map +1 -1
  60. package/src/lib/components/fields-section.js +31 -17
  61. package/src/lib/components/fields-section.js.map +1 -1
  62. package/src/lib/components/lead-convert-dialog.d.ts +4 -0
  63. package/src/lib/components/lead-convert-dialog.js +23 -1
  64. package/src/lib/components/lead-convert-dialog.js.map +1 -1
  65. package/src/lib/components/lead-detail-page.js +8 -2
  66. package/src/lib/components/lead-detail-page.js.map +1 -1
  67. package/src/lib/components/lead-profile-fields.d.ts +71 -0
  68. package/src/lib/components/lead-profile-fields.js +386 -0
  69. package/src/lib/components/lead-profile-fields.js.map +1 -0
  70. package/src/lib/components/lead-properties-card.d.ts +2 -1
  71. package/src/lib/components/lead-properties-card.js +85 -176
  72. package/src/lib/components/lead-properties-card.js.map +1 -1
  73. package/src/lib/components/lead-source-select.d.ts +4 -23
  74. package/src/lib/components/lead-source-select.js +9 -73
  75. package/src/lib/components/lead-source-select.js.map +1 -1
  76. package/src/lib/components/lead-status-chip.d.ts +9 -3
  77. package/src/lib/components/lead-status-chip.js +8 -4
  78. package/src/lib/components/lead-status-chip.js.map +1 -1
  79. package/src/lib/components/lead-status-options.d.ts +20 -0
  80. package/src/lib/components/lead-status-options.js +51 -0
  81. package/src/lib/components/lead-status-options.js.map +1 -0
  82. package/src/lib/components/lead-unqualify-dialog.d.ts +5 -0
  83. package/src/lib/components/lead-unqualify-dialog.js +50 -18
  84. package/src/lib/components/lead-unqualify-dialog.js.map +1 -1
  85. package/src/lib/components/leads-bulk-bar.d.ts +0 -3
  86. package/src/lib/components/leads-bulk-bar.js +29 -29
  87. package/src/lib/components/leads-bulk-bar.js.map +1 -1
  88. package/src/lib/components/leads-section.js +120 -68
  89. package/src/lib/components/leads-section.js.map +1 -1
  90. package/src/lib/components/linked-deals-card.d.ts +4 -0
  91. package/src/lib/components/linked-deals-card.js +47 -3
  92. package/src/lib/components/linked-deals-card.js.map +1 -1
  93. package/src/lib/components/log-activity-dialog.js +43 -3
  94. package/src/lib/components/log-activity-dialog.js.map +1 -1
  95. package/src/lib/components/new-contact-drawer.d.ts +8 -0
  96. package/src/lib/components/new-contact-drawer.js +108 -6
  97. package/src/lib/components/new-contact-drawer.js.map +1 -1
  98. package/src/lib/components/new-lead-drawer.d.ts +9 -2
  99. package/src/lib/components/new-lead-drawer.js +88 -121
  100. package/src/lib/components/new-lead-drawer.js.map +1 -1
  101. package/src/lib/components/picklist-select.d.ts +36 -0
  102. package/src/lib/components/picklist-select.js +114 -0
  103. package/src/lib/components/picklist-select.js.map +1 -0
  104. package/src/lib/components/picklist-values-card.d.ts +34 -0
  105. package/src/lib/components/{lead-source-values-card.js → picklist-values-card.js} +241 -58
  106. package/src/lib/components/picklist-values-card.js.map +1 -0
  107. package/src/lib/components/pipeline-stages-dialog.d.ts +7 -0
  108. package/src/lib/components/pipeline-stages-dialog.js +26 -4
  109. package/src/lib/components/pipeline-stages-dialog.js.map +1 -1
  110. package/src/lib/components/pipelines-dialog.js +15 -6
  111. package/src/lib/components/pipelines-dialog.js.map +1 -1
  112. package/src/lib/components/recent-activity-feed.js +20 -5
  113. package/src/lib/components/recent-activity-feed.js.map +1 -1
  114. package/src/lib/components/recipes-card.js +4 -2
  115. package/src/lib/components/recipes-card.js.map +1 -1
  116. package/src/lib/components/record-tasks-card.js +15 -8
  117. package/src/lib/components/record-tasks-card.js.map +1 -1
  118. package/src/lib/components/reports/forecast-card.d.ts +6 -0
  119. package/src/lib/components/reports/forecast-card.js +101 -3
  120. package/src/lib/components/reports/forecast-card.js.map +1 -1
  121. package/src/lib/components/reports/lead-funnel-card.d.ts +12 -0
  122. package/src/lib/components/reports/lead-funnel-card.js +9 -5
  123. package/src/lib/components/reports/lead-funnel-card.js.map +1 -1
  124. package/src/lib/components/reports/lead-source-card.d.ts +23 -0
  125. package/src/lib/components/reports/lead-source-card.js +318 -0
  126. package/src/lib/components/reports/lead-source-card.js.map +1 -0
  127. package/src/lib/components/reports/report-export.js +1 -1
  128. package/src/lib/components/reports/report-export.js.map +1 -1
  129. package/src/lib/components/reports-section.js +8 -0
  130. package/src/lib/components/reports-section.js.map +1 -1
  131. package/src/lib/components/sharing-rule-drawer.d.ts +5 -0
  132. package/src/lib/components/sharing-rule-drawer.js +108 -19
  133. package/src/lib/components/sharing-rule-drawer.js.map +1 -1
  134. package/src/lib/components/sharing-rules-card.js +11 -3
  135. package/src/lib/components/sharing-rules-card.js.map +1 -1
  136. package/src/lib/components/task-cells.d.ts +9 -1
  137. package/src/lib/components/task-cells.js +9 -4
  138. package/src/lib/components/task-cells.js.map +1 -1
  139. package/src/lib/components/task-edit-drawer.js +111 -43
  140. package/src/lib/components/task-edit-drawer.js.map +1 -1
  141. package/src/lib/components/tasks-bulk-bar.d.ts +0 -3
  142. package/src/lib/components/tasks-bulk-bar.js +7 -20
  143. package/src/lib/components/tasks-bulk-bar.js.map +1 -1
  144. package/src/lib/components/tasks-calendar.d.ts +3 -0
  145. package/src/lib/components/tasks-calendar.js +8 -1
  146. package/src/lib/components/tasks-calendar.js.map +1 -1
  147. package/src/lib/components/tasks-section.d.ts +3 -3
  148. package/src/lib/components/tasks-section.js +58 -43
  149. package/src/lib/components/tasks-section.js.map +1 -1
  150. package/src/lib/constants/api-routes.d.ts +2 -9
  151. package/src/lib/constants/api-routes.js +1 -7
  152. package/src/lib/constants/api-routes.js.map +1 -1
  153. package/src/lib/constants/bundle-common.d.ts +6 -0
  154. package/src/lib/constants/bundle-common.js +11 -0
  155. package/src/lib/constants/bundle-common.js.map +1 -1
  156. package/src/lib/constants/company-filters.d.ts +14 -0
  157. package/src/lib/constants/company-filters.js +51 -1
  158. package/src/lib/constants/company-filters.js.map +1 -1
  159. package/src/lib/constants/contact-filters.d.ts +2 -0
  160. package/src/lib/constants/contact-filters.js +18 -1
  161. package/src/lib/constants/contact-filters.js.map +1 -1
  162. package/src/lib/constants/deal-filters.d.ts +22 -1
  163. package/src/lib/constants/deal-filters.js +67 -4
  164. package/src/lib/constants/deal-filters.js.map +1 -1
  165. package/src/lib/declarations.console-server.d.ts +3 -0
  166. package/src/lib/declarations.console-server.js +35 -0
  167. package/src/lib/declarations.console-server.js.map +1 -1
  168. package/src/lib/declarations.d.ts +26 -0
  169. package/src/lib/declarations.js +35 -0
  170. package/src/lib/declarations.js.map +1 -0
  171. package/src/lib/declarations.server.d.ts +11 -0
  172. package/src/lib/declarations.server.js +92 -1
  173. package/src/lib/declarations.server.js.map +1 -1
  174. package/src/lib/hooks/use-company-picklists.d.ts +15 -0
  175. package/src/lib/hooks/use-company-picklists.js +55 -0
  176. package/src/lib/hooks/use-company-picklists.js.map +1 -0
  177. package/src/lib/hooks/use-crm-picklist.d.ts +25 -0
  178. package/src/lib/hooks/use-crm-picklist.js +53 -0
  179. package/src/lib/hooks/use-crm-picklist.js.map +1 -0
  180. package/src/lib/hooks/use-crm-record-names.d.ts +16 -0
  181. package/src/lib/hooks/use-crm-record-names.js +45 -19
  182. package/src/lib/hooks/use-crm-record-names.js.map +1 -1
  183. package/src/lib/hooks/use-crm-task-picklists.d.ts +11 -0
  184. package/src/lib/hooks/use-crm-task-picklists.js +45 -0
  185. package/src/lib/hooks/use-crm-task-picklists.js.map +1 -0
  186. package/src/lib/hooks/use-deals.d.ts +14 -0
  187. package/src/lib/hooks/use-deals.js +29 -0
  188. package/src/lib/hooks/use-deals.js.map +1 -1
  189. package/src/lib/hooks/use-lead-picklists.d.ts +14 -0
  190. package/src/lib/hooks/use-lead-picklists.js +46 -0
  191. package/src/lib/hooks/use-lead-picklists.js.map +1 -0
  192. package/src/lib/hooks/use-lead-source-picklist.d.ts +5 -19
  193. package/src/lib/hooks/use-lead-source-picklist.js +12 -22
  194. package/src/lib/hooks/use-lead-source-picklist.js.map +1 -1
  195. package/src/lib/hooks/use-lead-status-picklist.d.ts +9 -0
  196. package/src/lib/hooks/use-lead-status-picklist.js +40 -0
  197. package/src/lib/hooks/use-lead-status-picklist.js.map +1 -0
  198. package/src/lib/model/companies.d.ts +32 -3
  199. package/src/lib/model/companies.js +133 -16
  200. package/src/lib/model/companies.js.map +1 -1
  201. package/src/lib/model/contact-merge.d.ts +27 -3
  202. package/src/lib/model/contact-merge.js +81 -7
  203. package/src/lib/model/contact-merge.js.map +1 -1
  204. package/src/lib/model/contact-record.d.ts +17 -0
  205. package/src/lib/model/contact-record.js +15 -1
  206. package/src/lib/model/contact-record.js.map +1 -1
  207. package/src/lib/model/contact-update.d.ts +31 -2
  208. package/src/lib/model/contact-update.js +14 -0
  209. package/src/lib/model/contact-update.js.map +1 -1
  210. package/src/lib/model/crm-list-query.d.ts +5 -0
  211. package/src/lib/model/crm-list-query.js +8 -0
  212. package/src/lib/model/crm-list-query.js.map +1 -1
  213. package/src/lib/model/crm-recipes.d.ts +40 -0
  214. package/src/lib/model/crm-recipes.js +244 -0
  215. package/src/lib/model/crm-recipes.js.map +1 -0
  216. package/src/lib/model/crm-record-counts.d.ts +36 -0
  217. package/src/lib/model/crm-record-counts.js +54 -0
  218. package/src/lib/model/crm-record-counts.js.map +1 -0
  219. package/src/lib/model/crm-record-lists.d.ts +23 -0
  220. package/src/lib/model/crm-record-lists.js +161 -0
  221. package/src/lib/model/crm-record-lists.js.map +1 -0
  222. package/src/lib/model/crm-reports.d.ts +73 -3
  223. package/src/lib/model/crm-reports.js +120 -1
  224. package/src/lib/model/crm-reports.js.map +1 -1
  225. package/src/lib/model/crm-sharing.d.ts +21 -3
  226. package/src/lib/model/crm-sharing.js +27 -6
  227. package/src/lib/model/crm-sharing.js.map +1 -1
  228. package/src/lib/model/deal-board-model.d.ts +8 -2
  229. package/src/lib/model/deal-board-model.js +15 -2
  230. package/src/lib/model/deal-board-model.js.map +1 -1
  231. package/src/lib/model/deal-contact-roles.d.ts +45 -0
  232. package/src/lib/model/deal-contact-roles.js +73 -0
  233. package/src/lib/model/deal-contact-roles.js.map +1 -0
  234. package/src/lib/model/deal-form-model.d.ts +56 -0
  235. package/src/lib/model/deal-form-model.js +79 -7
  236. package/src/lib/model/deal-form-model.js.map +1 -1
  237. package/src/lib/model/download-text-file.d.ts +23 -0
  238. package/src/lib/model/download-text-file.js +34 -0
  239. package/src/lib/model/download-text-file.js.map +1 -0
  240. package/src/lib/model/lead-filters.d.ts +36 -2
  241. package/src/lib/model/lead-filters.js +123 -2
  242. package/src/lib/model/lead-filters.js.map +1 -1
  243. package/src/lib/model/lead-source-direction.d.ts +38 -0
  244. package/src/lib/model/lead-source-direction.js +52 -0
  245. package/src/lib/model/lead-source-direction.js.map +1 -0
  246. package/src/lib/model/picklist-values.d.ts +119 -0
  247. package/src/lib/model/picklist-values.js +256 -0
  248. package/src/lib/model/picklist-values.js.map +1 -0
  249. package/src/lib/model/record-facts-disclosed.d.ts +81 -0
  250. package/src/lib/model/record-facts-disclosed.js +281 -0
  251. package/src/lib/model/record-facts-disclosed.js.map +1 -0
  252. package/src/lib/model/record-facts.d.ts +215 -34
  253. package/src/lib/model/record-facts.js +307 -128
  254. package/src/lib/model/record-facts.js.map +1 -1
  255. package/src/lib/model/task-picklists.d.ts +52 -0
  256. package/src/lib/model/task-picklists.js +83 -0
  257. package/src/lib/model/task-picklists.js.map +1 -0
  258. package/src/lib/model/task-routes.d.ts +13 -0
  259. package/src/lib/model/task-routes.js +23 -4
  260. package/src/lib/model/task-routes.js.map +1 -1
  261. package/src/lib/plan-entitlements.js +6 -4
  262. package/src/lib/plan-entitlements.js.map +1 -1
  263. package/src/lib/plugin.js +17 -0
  264. package/src/lib/plugin.js.map +1 -1
  265. package/src/lib/server/api-v1/contacts.js +220 -14
  266. package/src/lib/server/api-v1/contacts.js.map +1 -1
  267. package/src/lib/server/api-v1/crm-activities.js +14 -1
  268. package/src/lib/server/api-v1/crm-activities.js.map +1 -1
  269. package/src/lib/server/api-v1/crm-companies.js +123 -14
  270. package/src/lib/server/api-v1/crm-companies.js.map +1 -1
  271. package/src/lib/server/api-v1/crm-deals.js +291 -26
  272. package/src/lib/server/api-v1/crm-deals.js.map +1 -1
  273. package/src/lib/server/api-v1/crm-leads.js +144 -38
  274. package/src/lib/server/api-v1/crm-leads.js.map +1 -1
  275. package/src/lib/server/api-v1/crm-pipelines.d.ts +6 -0
  276. package/src/lib/server/api-v1/crm-pipelines.js +8 -2
  277. package/src/lib/server/api-v1/crm-pipelines.js.map +1 -1
  278. package/src/lib/server/api-v1/crm-tasks.js +75 -33
  279. package/src/lib/server/api-v1/crm-tasks.js.map +1 -1
  280. package/src/lib/server/api-v1/openapi.d.ts +0 -11
  281. package/src/lib/server/api-v1/openapi.js +171 -16
  282. package/src/lib/server/api-v1/openapi.js.map +1 -1
  283. package/src/lib/server/assign-contact-owner.d.ts +162 -0
  284. package/src/lib/server/assign-contact-owner.js +340 -0
  285. package/src/lib/server/assign-contact-owner.js.map +1 -0
  286. package/src/lib/server/associate-company-by-domain.d.ts +70 -0
  287. package/src/lib/server/associate-company-by-domain.js +153 -0
  288. package/src/lib/server/associate-company-by-domain.js.map +1 -0
  289. package/src/lib/server/automation-steps.d.ts +139 -0
  290. package/src/lib/server/automation-steps.js +472 -0
  291. package/src/lib/server/automation-steps.js.map +1 -0
  292. package/src/lib/server/capture-contact.js +65 -21
  293. package/src/lib/server/capture-contact.js.map +1 -1
  294. package/src/lib/server/capture-host-contact.d.ts +104 -0
  295. package/src/lib/server/capture-host-contact.js +151 -0
  296. package/src/lib/server/capture-host-contact.js.map +1 -0
  297. package/src/lib/server/company-children.d.ts +25 -0
  298. package/src/lib/server/company-children.js +42 -0
  299. package/src/lib/server/company-children.js.map +1 -0
  300. package/src/lib/server/company-delete.d.ts +18 -0
  301. package/src/lib/server/company-delete.js +44 -21
  302. package/src/lib/server/company-delete.js.map +1 -1
  303. package/src/lib/server/contact-email-history.js +21 -19
  304. package/src/lib/server/contact-email-history.js.map +1 -1
  305. package/src/lib/server/contact-remove.js +27 -2
  306. package/src/lib/server/contact-remove.js.map +1 -1
  307. package/src/lib/server/contact-reports-to.d.ts +61 -0
  308. package/src/lib/server/contact-reports-to.js +114 -0
  309. package/src/lib/server/contact-reports-to.js.map +1 -0
  310. package/src/lib/server/contact-update.d.ts +42 -1
  311. package/src/lib/server/contact-update.js +204 -23
  312. package/src/lib/server/contact-update.js.map +1 -1
  313. package/src/lib/server/convert-host-lead.d.ts +46 -1
  314. package/src/lib/server/convert-host-lead.js +173 -15
  315. package/src/lib/server/convert-host-lead.js.map +1 -1
  316. package/src/lib/server/convert-open-lead.js +12 -5
  317. package/src/lib/server/convert-open-lead.js.map +1 -1
  318. package/src/lib/server/crm-record-indexes.d.ts +45 -0
  319. package/src/lib/server/crm-record-indexes.js +78 -0
  320. package/src/lib/server/crm-record-indexes.js.map +1 -0
  321. package/src/lib/server/crm-sharing.d.ts +9 -1
  322. package/src/lib/server/crm-sharing.js +23 -3
  323. package/src/lib/server/crm-sharing.js.map +1 -1
  324. package/src/lib/server/deal-contact-roles.d.ts +27 -0
  325. package/src/lib/server/deal-contact-roles.js +89 -0
  326. package/src/lib/server/deal-contact-roles.js.map +1 -0
  327. package/src/lib/server/lead-convert.d.ts +5 -0
  328. package/src/lib/server/lead-convert.js +19 -3
  329. package/src/lib/server/lead-convert.js.map +1 -1
  330. package/src/lib/server/lead-create.d.ts +17 -2
  331. package/src/lib/server/lead-create.js +46 -14
  332. package/src/lib/server/lead-create.js.map +1 -1
  333. package/src/lib/server/lead-nurturing.d.ts +6 -0
  334. package/src/lib/server/lead-nurturing.js +57 -20
  335. package/src/lib/server/lead-nurturing.js.map +1 -1
  336. package/src/lib/server/lead-picklists.d.ts +35 -0
  337. package/src/lib/server/lead-picklists.js +52 -0
  338. package/src/lib/server/lead-picklists.js.map +1 -0
  339. package/src/lib/server/lead-source-picklist.d.ts +1 -13
  340. package/src/lib/server/lead-source-picklist.js +6 -33
  341. package/src/lib/server/lead-source-picklist.js.map +1 -1
  342. package/src/lib/server/merge-contacts.js +22 -4
  343. package/src/lib/server/merge-contacts.js.map +1 -1
  344. package/src/lib/server/person-eraser.d.ts +53 -0
  345. package/src/lib/server/person-eraser.js +149 -0
  346. package/src/lib/server/person-eraser.js.map +1 -0
  347. package/src/lib/server/person-records.d.ts +69 -0
  348. package/src/lib/server/person-records.js +317 -0
  349. package/src/lib/server/person-records.js.map +1 -0
  350. package/src/lib/server/person-refund.d.ts +97 -0
  351. package/src/lib/server/person-refund.js +221 -0
  352. package/src/lib/server/person-refund.js.map +1 -0
  353. package/src/lib/server/picklist-values.d.ts +21 -0
  354. package/src/lib/server/picklist-values.js +308 -0
  355. package/src/lib/server/picklist-values.js.map +1 -0
  356. package/src/lib/server/read-picklist.d.ts +44 -0
  357. package/src/lib/server/read-picklist.js +65 -0
  358. package/src/lib/server/read-picklist.js.map +1 -0
  359. package/src/lib/server/recipe-routes.js +15 -7
  360. package/src/lib/server/recipe-routes.js.map +1 -1
  361. package/src/lib/server/record-email-state.js +7 -1
  362. package/src/lib/server/record-email-state.js.map +1 -1
  363. package/src/lib/server/record-facts.d.ts +3 -0
  364. package/src/lib/server/record-facts.js +233 -31
  365. package/src/lib/server/record-facts.js.map +1 -1
  366. package/src/lib/server/record-origin.d.ts +27 -0
  367. package/src/lib/server/record-origin.js +104 -0
  368. package/src/lib/server/record-origin.js.map +1 -0
  369. package/src/lib/server/record-timeline.js +15 -2
  370. package/src/lib/server/record-timeline.js.map +1 -1
  371. package/src/lib/server/suite-gate.js +4 -3
  372. package/src/lib/server/suite-gate.js.map +1 -1
  373. package/src/lib/server/task-routes.js +30 -16
  374. package/src/lib/server/task-routes.js.map +1 -1
  375. package/src/lib/server-deal-stage.d.ts +5 -1
  376. package/src/lib/server-deal-stage.js +10 -5
  377. package/src/lib/server-deal-stage.js.map +1 -1
  378. package/src/lib/server.d.ts +8 -3
  379. package/src/lib/server.js +47 -43
  380. package/src/lib/server.js.map +1 -1
  381. package/src/lib/testing/memory-firestore.d.ts +55 -0
  382. package/src/lib/testing/memory-firestore.js +335 -0
  383. package/src/lib/testing/memory-firestore.js.map +1 -0
  384. package/src/lib/testing/stand-in-email-send-index.d.ts +25 -0
  385. package/src/lib/testing/stand-in-email-send-index.js +49 -0
  386. package/src/lib/testing/stand-in-email-send-index.js.map +1 -0
  387. package/src/lib/transfer/activities.d.ts +30 -0
  388. package/src/lib/transfer/activities.js +132 -0
  389. package/src/lib/transfer/activities.js.map +1 -0
  390. package/src/lib/transfer/aliases.d.ts +22 -0
  391. package/src/lib/transfer/aliases.js +1342 -0
  392. package/src/lib/transfer/aliases.js.map +1 -0
  393. package/src/lib/transfer/common.d.ts +222 -0
  394. package/src/lib/transfer/common.js +832 -0
  395. package/src/lib/transfer/common.js.map +1 -0
  396. package/src/lib/transfer/companies.d.ts +43 -0
  397. package/src/lib/transfer/companies.js +549 -0
  398. package/src/lib/transfer/companies.js.map +1 -0
  399. package/src/lib/transfer/contacts.d.ts +31 -0
  400. package/src/lib/transfer/contacts.js +727 -0
  401. package/src/lib/transfer/contacts.js.map +1 -0
  402. package/src/lib/transfer/deals.d.ts +66 -0
  403. package/src/lib/transfer/deals.js +698 -0
  404. package/src/lib/transfer/deals.js.map +1 -0
  405. package/src/lib/transfer/email-templates-package.d.ts +40 -0
  406. package/src/lib/transfer/email-templates-package.js +76 -0
  407. package/src/lib/transfer/email-templates-package.js.map +1 -0
  408. package/src/lib/transfer/email-templates-package.server.d.ts +32 -0
  409. package/src/lib/transfer/email-templates-package.server.js +176 -0
  410. package/src/lib/transfer/email-templates-package.server.js.map +1 -0
  411. package/src/lib/transfer/fields.d.ts +102 -0
  412. package/src/lib/transfer/fields.js +1755 -0
  413. package/src/lib/transfer/fields.js.map +1 -0
  414. package/src/lib/transfer/leads.d.ts +30 -0
  415. package/src/lib/transfer/leads.js +582 -0
  416. package/src/lib/transfer/leads.js.map +1 -0
  417. package/src/lib/transfer/pipelines.d.ts +21 -0
  418. package/src/lib/transfer/pipelines.js +172 -0
  419. package/src/lib/transfer/pipelines.js.map +1 -0
  420. package/src/lib/transfer/register.d.ts +47 -0
  421. package/src/lib/transfer/register.js +277 -0
  422. package/src/lib/transfer/register.js.map +1 -0
  423. package/src/lib/transfer/tasks.d.ts +30 -0
  424. package/src/lib/transfer/tasks.js +418 -0
  425. package/src/lib/transfer/tasks.js.map +1 -0
  426. package/src/lib/transfer/ui.d.ts +20 -0
  427. package/src/lib/transfer/ui.js +42 -0
  428. package/src/lib/transfer/ui.js.map +1 -0
  429. package/src/lib/components/company-import-drawer.d.ts +0 -42
  430. package/src/lib/components/company-import-drawer.js +0 -99
  431. package/src/lib/components/company-import-drawer.js.map +0 -1
  432. package/src/lib/components/contact-import-drawer.d.ts +0 -46
  433. package/src/lib/components/contact-import-drawer.js +0 -149
  434. package/src/lib/components/contact-import-drawer.js.map +0 -1
  435. package/src/lib/components/crm-export-all-button.d.ts +0 -39
  436. package/src/lib/components/crm-export-all-button.js +0 -98
  437. package/src/lib/components/crm-export-all-button.js.map +0 -1
  438. package/src/lib/components/csv-import-drawer.d.ts +0 -139
  439. package/src/lib/components/csv-import-drawer.js +0 -757
  440. package/src/lib/components/csv-import-drawer.js.map +0 -1
  441. package/src/lib/components/deal-import-drawer.d.ts +0 -24
  442. package/src/lib/components/deal-import-drawer.js +0 -83
  443. package/src/lib/components/deal-import-drawer.js.map +0 -1
  444. package/src/lib/components/lead-import-drawer.d.ts +0 -27
  445. package/src/lib/components/lead-import-drawer.js +0 -118
  446. package/src/lib/components/lead-import-drawer.js.map +0 -1
  447. package/src/lib/components/lead-source-values-card.d.ts +0 -27
  448. package/src/lib/components/lead-source-values-card.js.map +0 -1
  449. package/src/lib/components/task-import-drawer.d.ts +0 -24
  450. package/src/lib/components/task-import-drawer.js +0 -82
  451. package/src/lib/components/task-import-drawer.js.map +0 -1
  452. package/src/lib/model/companies-csv.d.ts +0 -36
  453. package/src/lib/model/companies-csv.js +0 -38
  454. package/src/lib/model/companies-csv.js.map +0 -1
  455. package/src/lib/model/contacts-csv.d.ts +0 -54
  456. package/src/lib/model/contacts-csv.js +0 -67
  457. package/src/lib/model/contacts-csv.js.map +0 -1
  458. package/src/lib/model/crm-company-import.d.ts +0 -154
  459. package/src/lib/model/crm-company-import.js +0 -331
  460. package/src/lib/model/crm-company-import.js.map +0 -1
  461. package/src/lib/model/crm-csv.d.ts +0 -239
  462. package/src/lib/model/crm-csv.js +0 -467
  463. package/src/lib/model/crm-csv.js.map +0 -1
  464. package/src/lib/model/crm-deal-import.d.ts +0 -112
  465. package/src/lib/model/crm-deal-import.js +0 -263
  466. package/src/lib/model/crm-deal-import.js.map +0 -1
  467. package/src/lib/model/crm-import.d.ts +0 -250
  468. package/src/lib/model/crm-import.js +0 -467
  469. package/src/lib/model/crm-import.js.map +0 -1
  470. package/src/lib/model/crm-lead-import.d.ts +0 -138
  471. package/src/lib/model/crm-lead-import.js +0 -446
  472. package/src/lib/model/crm-lead-import.js.map +0 -1
  473. package/src/lib/model/crm-task-import.d.ts +0 -94
  474. package/src/lib/model/crm-task-import.js +0 -275
  475. package/src/lib/model/crm-task-import.js.map +0 -1
  476. package/src/lib/model/deals-csv.d.ts +0 -32
  477. package/src/lib/model/deals-csv.js +0 -36
  478. package/src/lib/model/deals-csv.js.map +0 -1
  479. package/src/lib/model/import-column-shapes.d.ts +0 -36
  480. package/src/lib/model/import-column-shapes.js +0 -76
  481. package/src/lib/model/import-column-shapes.js.map +0 -1
  482. package/src/lib/model/lead-source-values.d.ts +0 -82
  483. package/src/lib/model/lead-source-values.js +0 -149
  484. package/src/lib/model/lead-source-values.js.map +0 -1
  485. package/src/lib/model/leads-csv.d.ts +0 -40
  486. package/src/lib/model/leads-csv.js +0 -40
  487. package/src/lib/model/leads-csv.js.map +0 -1
  488. package/src/lib/model/tasks-csv.d.ts +0 -28
  489. package/src/lib/model/tasks-csv.js +0 -28
  490. package/src/lib/model/tasks-csv.js.map +0 -1
  491. package/src/lib/server/companies-import.d.ts +0 -57
  492. package/src/lib/server/companies-import.js +0 -242
  493. package/src/lib/server/companies-import.js.map +0 -1
  494. package/src/lib/server/contacts-import.d.ts +0 -109
  495. package/src/lib/server/contacts-import.js +0 -393
  496. package/src/lib/server/contacts-import.js.map +0 -1
  497. package/src/lib/server/deals-import.d.ts +0 -76
  498. package/src/lib/server/deals-import.js +0 -248
  499. package/src/lib/server/deals-import.js.map +0 -1
  500. package/src/lib/server/export-route.d.ts +0 -60
  501. package/src/lib/server/export-route.js +0 -447
  502. package/src/lib/server/export-route.js.map +0 -1
  503. package/src/lib/server/import-context.d.ts +0 -87
  504. package/src/lib/server/import-context.js +0 -182
  505. package/src/lib/server/import-context.js.map +0 -1
  506. package/src/lib/server/lead-source-values.d.ts +0 -50
  507. package/src/lib/server/lead-source-values.js +0 -191
  508. package/src/lib/server/lead-source-values.js.map +0 -1
  509. package/src/lib/server/leads-import.d.ts +0 -84
  510. package/src/lib/server/leads-import.js +0 -367
  511. package/src/lib/server/leads-import.js.map +0 -1
  512. package/src/lib/server/tasks-import.d.ts +0 -54
  513. package/src/lib/server/tasks-import.js +0 -160
  514. package/src/lib/server/tasks-import.js.map +0 -1
@@ -0,0 +1,318 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ 'use client';
17
+ import { _ as _extends } from "@swc/helpers/_/_extends";
18
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
19
+ import * as Aglyn from "@aglyn/aglyn";
20
+ import { CardDisplay } from "@aglyn/shared-ui-jsx";
21
+ import { percent, Section } from "@aglyn/shared-ui-jsx/components/measured-figures.component";
22
+ import { ScrollTable } from "@aglyn/shared-ui-jsx/components/scroll-table.component";
23
+ import { ceilingedWindow } from "@aglyn/tenant-feature-instance/hooks/host-collection-queries";
24
+ import { Alert, Stack, TableBody, TableCell, TableHead, TableRow, Typography } from "@mui/material";
25
+ import { getDocs, limit, orderBy, query } from "firebase/firestore";
26
+ import { useMemo } from "react";
27
+ import { useFirestore } from "@aglyn/tenant-feature-instance";
28
+ import { useCrmOrgMount } from "../../hooks/use-crm-org-mount.js";
29
+ import { useLeadSourcePicklist } from "../../hooks/use-lead-source-picklist.js";
30
+ import { LEAD_SOURCE_DIRECTION_LABELS, LEAD_SOURCE_UNSPECIFIED_LABEL } from "../../model/lead-source-direction.js";
31
+ import { leadsByLeadSource } from "../../model/crm-reports.js";
32
+ import { LEAD_CEILING, leadsBetween } from "./lead-funnel-card.js";
33
+ import { ReportExport } from "./report-export.js";
34
+ import { reportFilename } from "./report-format.js";
35
+ import { reportCacheKey } from "./report-scope.js";
36
+ import { ReportStatTile } from "./report-stat-tile.js";
37
+ import { useAggregateRead, useWindowRead } from "./use-aggregate-read.js";
38
+ /** The CSV's columns: one file for both tables. */ const COLUMNS = [
39
+ 'Kind',
40
+ 'Label',
41
+ 'Direction',
42
+ 'Leads',
43
+ 'Qualified',
44
+ 'Conversion'
45
+ ];
46
+ const LEAD_SOURCE_HELP = Aglyn.pluginDocsHelp('crmReports', {
47
+ anchor: '#lead-sources',
48
+ excerpt: 'The leads first seen in the period by their lead source, and by the ' + 'direction each source is grouped under on the Fields page — Inbound, ' + 'Outbound or Unspecified — with how many of each were qualified.'
49
+ });
50
+ const directionLabel = (direction)=>direction ? LEAD_SOURCE_DIRECTION_LABELS[direction] : LEAD_SOURCE_UNSPECIFIED_LABEL;
51
+ /**
52
+ * Lead sources (AGL-3511): of the leads first seen in the period, how many
53
+ * came from each lead source and in each direction, and how many of each
54
+ * were qualified.
55
+ *
56
+ * The SAME window the Lead funnel places — the period's newest leads by
57
+ * `firstSeenAtMs`, under the same remembered key — so the two cards read
58
+ * the period once between them and can never disagree about which leads
59
+ * it holds. The lead source is grouped through the org's own list as it
60
+ * stands now: a value moved to Outbound on the Fields page reports as
61
+ * Outbound from that moment, for every lead that holds it.
62
+ */ export function LeadSourceCard(props) {
63
+ const { report, hostId } = props;
64
+ const mount = useCrmOrgMount();
65
+ if (hostId) return /*#__PURE__*/ _jsx(SiteLeadSourceCard, {
66
+ report: report,
67
+ hostId: hostId
68
+ });
69
+ if (mount && (!mount.hostsReady || mount.hosts.length > 0)) {
70
+ return /*#__PURE__*/ _jsx(OrgLeadSourceCard, {
71
+ report: report
72
+ });
73
+ }
74
+ return /*#__PURE__*/ _jsx(CardDisplay, {
75
+ header: 'Lead sources',
76
+ help: LEAD_SOURCE_HELP,
77
+ contentGutterX: true,
78
+ contentGutterY: true,
79
+ children: /*#__PURE__*/ _jsx(Typography, {
80
+ variant: "body2",
81
+ color: "text.secondary",
82
+ children: mount ? 'Leads live under a site, and this organization has no sites yet.' : 'Leads live under a site, and this report is mounted under none.'
83
+ })
84
+ });
85
+ }
86
+ LeadSourceCard.displayName = 'LeadSourceCard';
87
+ /** One site's window, under the Lead funnel's own key. */ function SiteLeadSourceCard(props) {
88
+ const { report, hostId } = props;
89
+ const { range } = report;
90
+ const firestore = useFirestore();
91
+ const window = useWindowRead(()=>query(leadsBetween(firestore, report.scope[1], report.tokens, range.from, range.to), orderBy('firstSeenAtMs', 'desc'), limit(LEAD_CEILING + 1)), LEAD_CEILING, [
92
+ firestore,
93
+ hostId,
94
+ range
95
+ ], {
96
+ cacheKey: reportCacheKey(report, `leads:window:${hostId}`)
97
+ });
98
+ return /*#__PURE__*/ _jsx(LeadSourceBody, {
99
+ report: report,
100
+ window: window
101
+ });
102
+ }
103
+ SiteLeadSourceCard.displayName = 'SiteLeadSourceCard';
104
+ /** The organization's one unscoped window, under the Lead funnel's own key. */ function OrgLeadSourceCard(props) {
105
+ const { report } = props;
106
+ const { range } = report;
107
+ const firestore = useFirestore();
108
+ const merged = useAggregateRead(()=>getDocs(query(leadsBetween(firestore, report.scope[1], null, range.from, range.to), orderBy('firstSeenAtMs', 'desc'), limit(LEAD_CEILING + 1))).then((snapshot)=>ceilingedWindow(snapshot.docs.map((document)=>_extends({}, document.data(), {
109
+ $id: document.id
110
+ })), LEAD_CEILING)), [
111
+ firestore,
112
+ report.scope,
113
+ range
114
+ ], {
115
+ cacheKey: reportCacheKey(report, 'leads:window:org')
116
+ });
117
+ const window = useMemo(()=>{
118
+ var _ref, _ref1;
119
+ var _merged_value, _merged_value1;
120
+ return {
121
+ rows: (_ref = (_merged_value = merged.value) == null ? void 0 : _merged_value.rows) != null ? _ref : [],
122
+ truncated: (_ref1 = (_merged_value1 = merged.value) == null ? void 0 : _merged_value1.truncated) != null ? _ref1 : false,
123
+ status: merged.status
124
+ };
125
+ }, [
126
+ merged
127
+ ]);
128
+ return /*#__PURE__*/ _jsx(LeadSourceBody, {
129
+ report: report,
130
+ window: window
131
+ });
132
+ }
133
+ OrgLeadSourceCard.displayName = 'OrgLeadSourceCard';
134
+ /** The card as drawn, whichever level fed it. Pure over the window and the org's list. */ function LeadSourceBody(props) {
135
+ var _ref, _ref1, _ref2;
136
+ var _direction, _direction1, _direction2, _direction3, _direction4;
137
+ const { report, window } = props;
138
+ const { period } = report;
139
+ const { picklist } = useLeadSourcePicklist(report.scope[1]);
140
+ const breakdown = useMemo(()=>leadsByLeadSource(window.rows, picklist), [
141
+ window,
142
+ picklist
143
+ ]);
144
+ const read = window.status === 'success';
145
+ const direction = (id)=>breakdown.directions.find((row)=>row.direction === id);
146
+ const shareOf = (count)=>read && breakdown.total > 0 && count !== undefined ? `${percent(count / breakdown.total)} of the period's leads` : undefined;
147
+ const sourceLabel = (label, listed)=>label ? listed ? label : `${label} (not in the list)` : 'No lead source';
148
+ return /*#__PURE__*/ _jsx(CardDisplay, {
149
+ header: 'Lead sources',
150
+ help: LEAD_SOURCE_HELP,
151
+ contentGutterX: true,
152
+ contentGutterY: true,
153
+ HeaderProps: {
154
+ action: /*#__PURE__*/ _jsx(ReportExport, {
155
+ filename: reportFilename('lead-sources', period),
156
+ columns: COLUMNS,
157
+ rows: ()=>[
158
+ ...breakdown.directions.map((row)=>[
159
+ 'Direction',
160
+ directionLabel(row.direction),
161
+ '',
162
+ row.leads,
163
+ row.qualified,
164
+ row.leads ? percent(row.rate) : ''
165
+ ]),
166
+ ...breakdown.rows.map((row)=>[
167
+ 'Lead source',
168
+ sourceLabel(row.label, row.listed),
169
+ directionLabel(row.direction),
170
+ row.leads,
171
+ row.qualified,
172
+ percent(row.rate)
173
+ ])
174
+ ],
175
+ disabled: !read || !breakdown.total
176
+ })
177
+ },
178
+ children: /*#__PURE__*/ _jsxs(Stack, {
179
+ spacing: 2,
180
+ children: [
181
+ /*#__PURE__*/ _jsxs(Stack, {
182
+ direction: "row",
183
+ spacing: 3,
184
+ sx: {
185
+ flexWrap: 'wrap'
186
+ },
187
+ children: [
188
+ /*#__PURE__*/ _jsx(ReportStatTile, {
189
+ label: LEAD_SOURCE_DIRECTION_LABELS.inbound,
190
+ value: read ? ((_ref = (_direction = direction('inbound')) == null ? void 0 : _direction.leads) != null ? _ref : 0).toLocaleString() : null,
191
+ note: shareOf((_direction1 = direction('inbound')) == null ? void 0 : _direction1.leads)
192
+ }),
193
+ /*#__PURE__*/ _jsx(ReportStatTile, {
194
+ label: LEAD_SOURCE_DIRECTION_LABELS.outbound,
195
+ value: read ? ((_ref1 = (_direction2 = direction('outbound')) == null ? void 0 : _direction2.leads) != null ? _ref1 : 0).toLocaleString() : null,
196
+ note: shareOf((_direction3 = direction('outbound')) == null ? void 0 : _direction3.leads)
197
+ }),
198
+ /*#__PURE__*/ _jsx(ReportStatTile, {
199
+ label: LEAD_SOURCE_UNSPECIFIED_LABEL,
200
+ value: read ? ((_ref2 = (_direction4 = direction(null)) == null ? void 0 : _direction4.leads) != null ? _ref2 : 0).toLocaleString() : null,
201
+ note: 'no lead source, or one in no group'
202
+ })
203
+ ]
204
+ }),
205
+ window.status === 'error' ? /*#__PURE__*/ _jsx(Alert, {
206
+ severity: "warning",
207
+ children: 'The period’s leads could not be read.'
208
+ }) : null,
209
+ /*#__PURE__*/ _jsx(Section, {
210
+ title: 'By direction',
211
+ children: /*#__PURE__*/ _jsxs(ScrollTable, {
212
+ size: "small",
213
+ children: [
214
+ /*#__PURE__*/ _jsx(TableHead, {
215
+ children: /*#__PURE__*/ _jsx(TableRow, {
216
+ children: [
217
+ 'Direction',
218
+ 'Leads',
219
+ 'Qualified',
220
+ 'Conversion'
221
+ ].map((column, index)=>/*#__PURE__*/ _jsx(TableCell, {
222
+ align: index ? 'right' : 'left',
223
+ children: column
224
+ }, column))
225
+ })
226
+ }),
227
+ /*#__PURE__*/ _jsx(TableBody, {
228
+ children: breakdown.directions.map((row)=>{
229
+ var _row_direction;
230
+ return /*#__PURE__*/ _jsxs(TableRow, {
231
+ children: [
232
+ /*#__PURE__*/ _jsx(TableCell, {
233
+ children: directionLabel(row.direction)
234
+ }),
235
+ /*#__PURE__*/ _jsx(TableCell, {
236
+ align: "right",
237
+ children: read ? row.leads.toLocaleString() : '—'
238
+ }),
239
+ /*#__PURE__*/ _jsx(TableCell, {
240
+ align: "right",
241
+ children: read ? row.qualified.toLocaleString() : '—'
242
+ }),
243
+ /*#__PURE__*/ _jsx(TableCell, {
244
+ align: "right",
245
+ children: read && row.leads ? percent(row.rate) : '—'
246
+ })
247
+ ]
248
+ }, (_row_direction = row.direction) != null ? _row_direction : 'none');
249
+ })
250
+ })
251
+ ]
252
+ })
253
+ }),
254
+ /*#__PURE__*/ _jsxs(Section, {
255
+ title: 'By lead source',
256
+ children: [
257
+ breakdown.rows.length ? /*#__PURE__*/ _jsxs(ScrollTable, {
258
+ size: "small",
259
+ children: [
260
+ /*#__PURE__*/ _jsx(TableHead, {
261
+ children: /*#__PURE__*/ _jsx(TableRow, {
262
+ children: [
263
+ 'Lead source',
264
+ 'Direction',
265
+ 'Leads',
266
+ 'Qualified',
267
+ 'Conversion'
268
+ ].map((column, index)=>/*#__PURE__*/ _jsx(TableCell, {
269
+ align: index > 1 ? 'right' : 'left',
270
+ children: column
271
+ }, column))
272
+ })
273
+ }),
274
+ /*#__PURE__*/ _jsx(TableBody, {
275
+ children: breakdown.rows.map((row)=>/*#__PURE__*/ _jsxs(TableRow, {
276
+ children: [
277
+ /*#__PURE__*/ _jsx(TableCell, {
278
+ children: sourceLabel(row.label, row.listed)
279
+ }),
280
+ /*#__PURE__*/ _jsx(TableCell, {
281
+ children: directionLabel(row.direction)
282
+ }),
283
+ /*#__PURE__*/ _jsx(TableCell, {
284
+ align: "right",
285
+ children: row.leads.toLocaleString()
286
+ }),
287
+ /*#__PURE__*/ _jsx(TableCell, {
288
+ align: "right",
289
+ children: row.qualified.toLocaleString()
290
+ }),
291
+ /*#__PURE__*/ _jsx(TableCell, {
292
+ align: "right",
293
+ children: percent(row.rate)
294
+ })
295
+ ]
296
+ }, row.label || 'none'))
297
+ })
298
+ ]
299
+ }) : /*#__PURE__*/ _jsx(Typography, {
300
+ variant: "body2",
301
+ color: "text.secondary",
302
+ children: window.status === 'loading' ? 'Reading…' : 'No leads first seen in this period.'
303
+ }),
304
+ read && window.truncated ? /*#__PURE__*/ _jsx(Typography, {
305
+ variant: "caption",
306
+ color: "text.secondary",
307
+ children: `Read from the ${window.rows.length.toLocaleString()} most recently captured leads in the period.`
308
+ }) : null
309
+ ]
310
+ })
311
+ ]
312
+ })
313
+ });
314
+ }
315
+ LeadSourceBody.displayName = 'LeadSourceBody';
316
+ export default LeadSourceCard;
317
+
318
+ //# sourceMappingURL=lead-source-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../../libs/plugins/crm/src/lib/components/reports/lead-source-card.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 { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport {\n percent,\n Section,\n} from '@aglyn/shared-ui-jsx/components/measured-figures.component'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { ceilingedWindow } from '@aglyn/tenant-feature-instance/hooks/host-collection-queries'\nimport {\n Alert,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n Typography,\n} from '@mui/material'\nimport { getDocs, limit, orderBy, query } from 'firebase/firestore'\nimport { useMemo } from 'react'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport { useCrmOrgMount } from '../../hooks/use-crm-org-mount'\nimport { useLeadSourcePicklist } from '../../hooks/use-lead-source-picklist'\nimport {\n LEAD_SOURCE_DIRECTION_LABELS,\n LEAD_SOURCE_UNSPECIFIED_LABEL,\n type LeadSourceDirection,\n} from '../../model/lead-source-direction'\nimport { leadsByLeadSource } from '../../model/crm-reports'\nimport { LEAD_CEILING, leadsBetween } from './lead-funnel-card'\nimport { ReportExport } from './report-export'\nimport { reportFilename } from './report-format'\nimport { type CrmReportScope, reportCacheKey } from './report-scope'\nimport { ReportStatTile } from './report-stat-tile'\nimport { useAggregateRead, useWindowRead, type WindowRead } from './use-aggregate-read'\n\ntype LeadRow = Record<string, unknown> & Aglyn.CrmLeadFields & { $id: string }\n\n/** The CSV's columns: one file for both tables. */\nconst COLUMNS = ['Kind', 'Label', 'Direction', 'Leads', 'Qualified', 'Conversion'] as const\n\nconst LEAD_SOURCE_HELP = Aglyn.pluginDocsHelp('crmReports', {\n anchor: '#lead-sources',\n excerpt:\n 'The leads first seen in the period by their lead source, and by the ' +\n 'direction each source is grouped under on the Fields page — Inbound, ' +\n 'Outbound or Unspecified — with how many of each were qualified.',\n})\n\nconst directionLabel = (direction: LeadSourceDirection | null): string =>\n direction ? LEAD_SOURCE_DIRECTION_LABELS[direction] : LEAD_SOURCE_UNSPECIFIED_LABEL\n\nexport interface LeadSourceCardProps {\n report: CrmReportScope\n /** The site whose leads are read, or `null` at the organization level — as the Lead funnel takes it. */\n hostId: string | null\n}\n\n/**\n * Lead sources (AGL-3511): of the leads first seen in the period, how many\n * came from each lead source and in each direction, and how many of each\n * were qualified.\n *\n * The SAME window the Lead funnel places — the period's newest leads by\n * `firstSeenAtMs`, under the same remembered key — so the two cards read\n * the period once between them and can never disagree about which leads\n * it holds. The lead source is grouped through the org's own list as it\n * stands now: a value moved to Outbound on the Fields page reports as\n * Outbound from that moment, for every lead that holds it.\n */\nexport function LeadSourceCard(props: LeadSourceCardProps) {\n const { report, hostId } = props\n const mount = useCrmOrgMount()\n if (hostId) return <SiteLeadSourceCard report={report} hostId={hostId} />\n if (mount && (!mount.hostsReady || mount.hosts.length > 0)) {\n return <OrgLeadSourceCard report={report} />\n }\n return (\n <CardDisplay header={'Lead sources'} help={LEAD_SOURCE_HELP} contentGutterX contentGutterY>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {mount\n ? 'Leads live under a site, and this organization has no sites yet.'\n : 'Leads live under a site, and this report is mounted under none.'}\n </Typography>\n </CardDisplay>\n )\n}\nLeadSourceCard.displayName = 'LeadSourceCard'\n\n/** One site's window, under the Lead funnel's own key. */\nfunction SiteLeadSourceCard(props: { report: CrmReportScope; hostId: string }) {\n const { report, hostId } = props\n const { range } = report\n const firestore = useFirestore()\n const window = useWindowRead<LeadRow>(\n () =>\n query(\n leadsBetween(firestore, report.scope[1], report.tokens, range.from, range.to),\n orderBy('firstSeenAtMs', 'desc'),\n limit(LEAD_CEILING + 1),\n ),\n LEAD_CEILING,\n [firestore, hostId, range],\n { cacheKey: reportCacheKey(report, `leads:window:${hostId}`) },\n )\n return <LeadSourceBody report={report} window={window} />\n}\nSiteLeadSourceCard.displayName = 'SiteLeadSourceCard'\n\n/** The organization's one unscoped window, under the Lead funnel's own key. */\nfunction OrgLeadSourceCard(props: { report: CrmReportScope }) {\n const { report } = props\n const { range } = report\n const firestore = useFirestore()\n const merged = useAggregateRead<{ rows: LeadRow[]; truncated: boolean }>(\n () =>\n getDocs(\n query(\n leadsBetween(firestore, report.scope[1], null, range.from, range.to),\n orderBy('firstSeenAtMs', 'desc'),\n limit(LEAD_CEILING + 1),\n ),\n ).then((snapshot) =>\n ceilingedWindow(\n snapshot.docs.map(\n (document) => ({ ...document.data(), $id: document.id }) as unknown as LeadRow,\n ),\n LEAD_CEILING,\n ),\n ),\n [firestore, report.scope, range],\n { cacheKey: reportCacheKey(report, 'leads:window:org') },\n )\n const window = useMemo<WindowRead<LeadRow>>(\n () => ({\n rows: merged.value?.rows ?? [],\n truncated: merged.value?.truncated ?? false,\n status: merged.status,\n }),\n [merged],\n )\n return <LeadSourceBody report={report} window={window} />\n}\nOrgLeadSourceCard.displayName = 'OrgLeadSourceCard'\n\n/** The card as drawn, whichever level fed it. Pure over the window and the org's list. */\nfunction LeadSourceBody(props: { report: CrmReportScope; window: WindowRead<LeadRow> }) {\n const { report, window } = props\n const { period } = report\n const { picklist } = useLeadSourcePicklist(report.scope[1])\n const breakdown = useMemo(() => leadsByLeadSource(window.rows, picklist), [window, picklist])\n const read = window.status === 'success'\n const direction = (id: LeadSourceDirection | null) =>\n breakdown.directions.find((row) => row.direction === id)\n const shareOf = (count: number | undefined): string | undefined =>\n read && breakdown.total > 0 && count !== undefined\n ? `${percent(count / breakdown.total)} of the period's leads`\n : undefined\n const sourceLabel = (label: string, listed: boolean) =>\n label ? (listed ? label : `${label} (not in the list)`) : 'No lead source'\n\n return (\n <CardDisplay\n header={'Lead sources'}\n help={LEAD_SOURCE_HELP}\n contentGutterX\n contentGutterY\n HeaderProps={{\n action: (\n <ReportExport\n filename={reportFilename('lead-sources', period)}\n columns={COLUMNS}\n rows={() => [\n ...breakdown.directions.map((row) => [\n 'Direction',\n directionLabel(row.direction),\n '',\n row.leads,\n row.qualified,\n row.leads ? percent(row.rate) : '',\n ]),\n ...breakdown.rows.map((row) => [\n 'Lead source',\n sourceLabel(row.label, row.listed),\n directionLabel(row.direction),\n row.leads,\n row.qualified,\n percent(row.rate),\n ]),\n ]}\n disabled={!read || !breakdown.total}\n />\n ),\n }}\n >\n <Stack spacing={2}>\n <Stack direction=\"row\" spacing={3} sx={{ flexWrap: 'wrap' }}>\n <ReportStatTile\n label={LEAD_SOURCE_DIRECTION_LABELS.inbound}\n value={read ? (direction('inbound')?.leads ?? 0).toLocaleString() : null}\n note={shareOf(direction('inbound')?.leads)}\n />\n <ReportStatTile\n label={LEAD_SOURCE_DIRECTION_LABELS.outbound}\n value={read ? (direction('outbound')?.leads ?? 0).toLocaleString() : null}\n note={shareOf(direction('outbound')?.leads)}\n />\n <ReportStatTile\n label={LEAD_SOURCE_UNSPECIFIED_LABEL}\n value={read ? (direction(null)?.leads ?? 0).toLocaleString() : null}\n note={'no lead source, or one in no group'}\n />\n </Stack>\n {window.status === 'error' ? (\n <Alert severity=\"warning\">{'The period’s leads could not be read.'}</Alert>\n ) : null}\n <Section title={'By direction'}>\n <ScrollTable size=\"small\">\n <TableHead>\n <TableRow>\n {['Direction', 'Leads', 'Qualified', 'Conversion'].map((column, index) => (\n <TableCell key={column} align={index ? 'right' : 'left'}>\n {column}\n </TableCell>\n ))}\n </TableRow>\n </TableHead>\n <TableBody>\n {breakdown.directions.map((row) => (\n <TableRow key={row.direction ?? 'none'}>\n <TableCell>{directionLabel(row.direction)}</TableCell>\n <TableCell align=\"right\">{read ? row.leads.toLocaleString() : '—'}</TableCell>\n <TableCell align=\"right\">{read ? row.qualified.toLocaleString() : '—'}</TableCell>\n <TableCell align=\"right\">{read && row.leads ? percent(row.rate) : '—'}</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </ScrollTable>\n </Section>\n <Section title={'By lead source'}>\n {breakdown.rows.length ? (\n <ScrollTable size=\"small\">\n <TableHead>\n <TableRow>\n {['Lead source', 'Direction', 'Leads', 'Qualified', 'Conversion'].map(\n (column, index) => (\n <TableCell key={column} align={index > 1 ? 'right' : 'left'}>\n {column}\n </TableCell>\n ),\n )}\n </TableRow>\n </TableHead>\n <TableBody>\n {breakdown.rows.map((row) => (\n <TableRow key={row.label || 'none'}>\n <TableCell>{sourceLabel(row.label, row.listed)}</TableCell>\n <TableCell>{directionLabel(row.direction)}</TableCell>\n <TableCell align=\"right\">{row.leads.toLocaleString()}</TableCell>\n <TableCell align=\"right\">{row.qualified.toLocaleString()}</TableCell>\n <TableCell align=\"right\">{percent(row.rate)}</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </ScrollTable>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {window.status === 'loading' ? 'Reading…' : 'No leads first seen in this period.'}\n </Typography>\n )}\n {read && window.truncated ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`Read from the ${window.rows.length.toLocaleString()} most recently captured leads in the period.`}\n </Typography>\n ) : null}\n </Section>\n </Stack>\n </CardDisplay>\n )\n}\nLeadSourceBody.displayName = 'LeadSourceBody'\n\nexport default LeadSourceCard\n"],"names":["Aglyn","CardDisplay","percent","Section","ScrollTable","ceilingedWindow","Alert","Stack","TableBody","TableCell","TableHead","TableRow","Typography","getDocs","limit","orderBy","query","useMemo","useFirestore","useCrmOrgMount","useLeadSourcePicklist","LEAD_SOURCE_DIRECTION_LABELS","LEAD_SOURCE_UNSPECIFIED_LABEL","leadsByLeadSource","LEAD_CEILING","leadsBetween","ReportExport","reportFilename","reportCacheKey","ReportStatTile","useAggregateRead","useWindowRead","COLUMNS","LEAD_SOURCE_HELP","pluginDocsHelp","anchor","excerpt","directionLabel","direction","LeadSourceCard","props","report","hostId","mount","SiteLeadSourceCard","hostsReady","hosts","length","OrgLeadSourceCard","header","help","contentGutterX","contentGutterY","variant","color","displayName","range","firestore","window","scope","tokens","from","to","cacheKey","LeadSourceBody","merged","then","snapshot","docs","map","document","data","$id","id","rows","value","truncated","status","period","picklist","breakdown","read","directions","find","row","shareOf","count","total","undefined","sourceLabel","label","listed","HeaderProps","action","filename","columns","leads","qualified","rate","disabled","spacing","sx","flexWrap","inbound","toLocaleString","note","outbound","severity","title","size","column","index","align"],"mappings":"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"}
@@ -19,7 +19,7 @@ import { csvDocument } from "@aglyn/aglyn";
19
19
  import { mdiDownloadOutline } from "@aglyn/shared-data-mdi";
20
20
  import { MdiIcon } from "@aglyn/shared-ui-jsx";
21
21
  import { Button } from "@mui/material";
22
- import { downloadTextFile } from "../../model/contacts-csv.js";
22
+ import { downloadTextFile } from "../../model/download-text-file.js";
23
23
  /**
24
24
  * Export CSV for one report table (AGL-2624), in its card's header.
25
25
  *
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../libs/plugins/crm/src/lib/components/reports/report-export.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 { csvDocument } from '@aglyn/aglyn'\nimport { mdiDownloadOutline } from '@aglyn/shared-data-mdi'\nimport { MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { Button } from '@mui/material'\nimport { downloadTextFile } from '../../model/contacts-csv'\n\nexport interface ReportExportProps {\n /** The file's name — see `reportFilename`. */\n filename: string\n /** The header row, in the table's column order. */\n columns: readonly string[]\n /**\n * The rows, one array per line in the header's order, built when the\n * button is pressed rather than on every render: a table of a thousand\n * rows is serialized once, on demand, and never while nobody asked.\n */\n rows: () => ReadonlyArray<ReadonlyArray<unknown>>\n /** Nothing to write yet — the window is still reading, or came back empty. */\n disabled?: boolean\n}\n\n/**\n * Export CSV for one report table (AGL-2624), in its card's header.\n *\n * Client-side, from the window the card has already loaded: a report table\n * is at most a thousand grouped rows, and the file is those rows exactly as\n * drawn — the same names, the same counts, the same order — so a reader can\n * check the export against the screen. It is NOT a fresh read, so when the\n * card's window was full the card says so in a caption beneath the table;\n * an export that quietly wrote a sample as if it were the whole would be\n * the one thing that caption exists to prevent.\n */\nexport function ReportExport(props: ReportExportProps) {\n const { filename, columns, rows, disabled = false } = props\n return (\n <Button\n size=\"small\"\n startIcon={<MdiIcon path={mdiDownloadOutline.path} size={0.8} />}\n disabled={disabled}\n onClick={() =>\n downloadTextFile(filename, 'text/csv', csvDocument(columns, rows()))\n }\n >\n {'Export CSV'}\n </Button>\n )\n}\nReportExport.displayName = 'ReportExport'\n\nexport default ReportExport\n"],"names":["csvDocument","mdiDownloadOutline","MdiIcon","Button","downloadTextFile","ReportExport","props","filename","columns","rows","disabled","size","startIcon","path","onClick","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SAASA,WAAW,QAAQ,eAAc;AAC1C,SAASC,kBAAkB,QAAQ,yBAAwB;AAC3D,SAASC,OAAO,QAAQ,uBAAsB;AAC9C,SAASC,MAAM,QAAQ,gBAAe;AACtC,SAASC,gBAAgB,QAAQ,8BAA0B;AAiB3D;;;;;;;;;;CAUC,GACD,OAAO,SAASC,aAAaC,KAAwB;IACnD,MAAM,EAAEC,QAAQ,EAAEC,OAAO,EAAEC,IAAI,EAAEC,WAAW,KAAK,EAAE,GAAGJ;IACtD,qBACE,KAACH;QACCQ,MAAK;QACLC,yBAAW,KAACV;YAAQW,MAAMZ,mBAAmBY,IAAI;YAAEF,MAAM;;QACzDD,UAAUA;QACVI,SAAS,IACPV,iBAAiBG,UAAU,YAAYP,YAAYQ,SAASC;kBAG7D;;AAGP;AACAJ,aAAaU,WAAW,GAAG;AAE3B,eAAeV,aAAY"}
1
+ {"version":3,"sources":["../../../../../../../../libs/plugins/crm/src/lib/components/reports/report-export.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 { csvDocument } from '@aglyn/aglyn'\nimport { mdiDownloadOutline } from '@aglyn/shared-data-mdi'\nimport { MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { Button } from '@mui/material'\nimport { downloadTextFile } from '../../model/download-text-file'\n\nexport interface ReportExportProps {\n /** The file's name — see `reportFilename`. */\n filename: string\n /** The header row, in the table's column order. */\n columns: readonly string[]\n /**\n * The rows, one array per line in the header's order, built when the\n * button is pressed rather than on every render: a table of a thousand\n * rows is serialized once, on demand, and never while nobody asked.\n */\n rows: () => ReadonlyArray<ReadonlyArray<unknown>>\n /** Nothing to write yet — the window is still reading, or came back empty. */\n disabled?: boolean\n}\n\n/**\n * Export CSV for one report table (AGL-2624), in its card's header.\n *\n * Client-side, from the window the card has already loaded: a report table\n * is at most a thousand grouped rows, and the file is those rows exactly as\n * drawn — the same names, the same counts, the same order — so a reader can\n * check the export against the screen. It is NOT a fresh read, so when the\n * card's window was full the card says so in a caption beneath the table;\n * an export that quietly wrote a sample as if it were the whole would be\n * the one thing that caption exists to prevent.\n */\nexport function ReportExport(props: ReportExportProps) {\n const { filename, columns, rows, disabled = false } = props\n return (\n <Button\n size=\"small\"\n startIcon={<MdiIcon path={mdiDownloadOutline.path} size={0.8} />}\n disabled={disabled}\n onClick={() =>\n downloadTextFile(filename, 'text/csv', csvDocument(columns, rows()))\n }\n >\n {'Export CSV'}\n </Button>\n )\n}\nReportExport.displayName = 'ReportExport'\n\nexport default ReportExport\n"],"names":["csvDocument","mdiDownloadOutline","MdiIcon","Button","downloadTextFile","ReportExport","props","filename","columns","rows","disabled","size","startIcon","path","onClick","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SAASA,WAAW,QAAQ,eAAc;AAC1C,SAASC,kBAAkB,QAAQ,yBAAwB;AAC3D,SAASC,OAAO,QAAQ,uBAAsB;AAC9C,SAASC,MAAM,QAAQ,gBAAe;AACtC,SAASC,gBAAgB,QAAQ,oCAAgC;AAiBjE;;;;;;;;;;CAUC,GACD,OAAO,SAASC,aAAaC,KAAwB;IACnD,MAAM,EAAEC,QAAQ,EAAEC,OAAO,EAAEC,IAAI,EAAEC,WAAW,KAAK,EAAE,GAAGJ;IACtD,qBACE,KAACH;QACCQ,MAAK;QACLC,yBAAW,KAACV;YAAQW,MAAMZ,mBAAmBY,IAAI;YAAEF,MAAM;;QACzDD,UAAUA;QACVI,SAAS,IACPV,iBAAiBG,UAAU,YAAYP,YAAYQ,SAASC;kBAG7D;;AAGP;AACAJ,aAAaU,WAAW,GAAG;AAE3B,eAAeV,aAAY"}
@@ -30,6 +30,7 @@ import { ContactsMixCard } from "./reports/contacts-mix-card.js";
30
30
  import { ContactsTrendCard } from "./reports/contacts-trend-card.js";
31
31
  import { ForecastCard } from "./reports/forecast-card.js";
32
32
  import { LeadFunnelCard } from "./reports/lead-funnel-card.js";
33
+ import { LeadSourceCard } from "./reports/lead-source-card.js";
33
34
  import { PipelineCard } from "./reports/pipeline-card.js";
34
35
  import { SourceConversionCard } from "./reports/source-conversion-card.js";
35
36
  import { WonLostByOwnerCard } from "./reports/won-lost-by-owner-card.js";
@@ -248,6 +249,13 @@ import { CRM_REPORT_PERIOD_LABELS, CRM_REPORT_PERIODS, crmReportRange } from "..
248
249
  hostId: hostId
249
250
  })
250
251
  },
252
+ {
253
+ key: 'lead-sources',
254
+ children: /*#__PURE__*/ _jsx(LeadSourceCard, {
255
+ report: report,
256
+ hostId: hostId
257
+ })
258
+ },
251
259
  {
252
260
  key: 'pipeline',
253
261
  children: /*#__PURE__*/ _jsx(PipelineCard, {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/reports-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 type { ConsolePluginPageProps } from '@aglyn/aglyn'\nimport { mdiRefresh } from '@aglyn/shared-data-mdi'\nimport {\n MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { CardColumns } from '@aglyn/shared-ui-jsx/components/card-columns'\nimport {\n Button,\n Stack,\n ToggleButton,\n ToggleButtonGroup,\n Typography,\n} from '@mui/material'\nimport { getCountFromServer, query } from 'firebase/firestore'\nimport { useCallback, useMemo, useState } from 'react'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport { useCrmScope } from '../hooks/use-crm-scope'\nimport { crmRoutes } from '../model/crm-routes'\nimport { ActivityCard } from './reports/activity-card'\nimport { ClosedDealsCard } from './reports/closed-deals-card'\nimport { ContactsMixCard } from './reports/contacts-mix-card'\nimport { ContactsTrendCard } from './reports/contacts-trend-card'\nimport { ForecastCard } from './reports/forecast-card'\nimport { LeadFunnelCard } from './reports/lead-funnel-card'\nimport { PipelineCard } from './reports/pipeline-card'\nimport { SourceConversionCard } from './reports/source-conversion-card'\nimport { WonLostByOwnerCard } from './reports/won-lost-by-owner-card'\nimport {\n type CrmReportScope,\n reportCacheKey,\n reportCachePrefix,\n scopedCollection,\n visibleToClause,\n} from './reports/report-scope'\nimport { TasksCard } from './reports/tasks-card'\nimport { invalidateAggregateReads, useAggregateRead } from './reports/use-aggregate-read'\nimport {\n CRM_REPORT_PERIOD_LABELS,\n CRM_REPORT_PERIODS,\n type CrmReportPeriod,\n crmReportRange,\n} from '../model/crm-reports'\n\n/**\n * `/crm/reports` — the CRM in aggregate (AGL-2604, AGL-2624).\n *\n * Nine cards, each reading its own collection: contacts over time, contacts\n * by source and stage, which sources convert, this site's lead funnel, the\n * open pipeline, what closed, who logged what, the forecast by close month\n * (AGL-2620, over the pipeline card's own window), and the task load. This\n * section resolves what they share and hands it down — see `CrmReportScope`\n * — so that every card counts the same reader's records over the same\n * clock. The lead funnel is the one card handed the site as well: leads live\n * under the host, not the org.\n *\n * ## The period is a control, and the reads follow it\n *\n * Nothing on this page reads a year on mount. The picker names the period,\n * the period sizes every read (one count per week of it, the deals closed\n * within it), and the default is thirty days. The three cards that are\n * stocks rather than flows — the open pipeline, its forecast, the open\n * tasks — ignore the period because \"what is open\" has no period.\n *\n * ## Scoped like the list\n *\n * Every query carries `visibleTo array-contains-any` over the group's\n * tokens, the predicate the rules evaluate, so a report can never count a\n * record its reader could not open — and the total here is the reader's\n * total, not the org's billable audience, which is the head-count the\n * contacts list keeps for the quota.\n *\n * ## Read once a minute, not once an arrival\n *\n * Every read on this page is keyed by the scope and the period and kept\n * for `AGGREGATE_READ_TTL_MS` — see `useAggregateRead` — so leaving to open\n * a deal the report named and coming straight back costs nothing. Refresh\n * forgets this reader's answers for this org and anchors the period again,\n * which is what makes every card ask afresh.\n */\nexport function ContactsReportsSection(props: ConsolePluginPageProps) {\n const { hostId, org, basePath } = props\n const firestore = useFirestore()\n // The org data root — null until the org lookup settles, and the page\n // reads nothing until it does — and the controller this page is viewed\n // as, resolved the way every CRM surface resolves them so the facet a\n // stage is read from here is the facet the contacts list edits it in.\n const { scope: dataScope, consentGroup, visibleTo } = useCrmScope({ hostId, org })\n // `null` at the organization level (AGL-2630): no clause, and every card\n // totals every site; the holder is then each contact's own.\n const tokens = useMemo(() => (visibleTo ? [...visibleTo] : null), [visibleTo])\n const groupId = consentGroup?.groupId ?? null\n const orgRecord = (org ?? null) as Record<string, unknown> | null\n /*\n * The period AND the moment it was chosen, as one value: the range is\n * anchored when the reader picks, not re-derived from a moving `Date.now()`\n * per render, which would rebuild every query on every pass.\n */\n const [view, setView] = useState<{\n period: CrmReportPeriod\n nowMs: number\n }>(() => ({ period: '30d', nowMs: Date.now() }))\n const range = useMemo(\n () => crmReportRange(view.period, view.nowMs),\n [view],\n )\n const routes = useMemo(() => crmRoutes(basePath ?? ''), [basePath])\n const refresh = useCallback(() => {\n if (dataScope) {\n invalidateAggregateReads(reportCachePrefix({ scope: dataScope, tokens, groupId }))\n }\n // A new anchor is what changes every card's dependencies, so each read\n // runs again and — its key forgotten — reaches the server.\n setView((current) => ({ period: current.period, nowMs: Date.now() }))\n }, [dataScope, tokens, groupId])\n\n const report = useMemo<CrmReportScope | null>(\n () =>\n dataScope\n ? {\n scope: dataScope,\n tokens,\n groupId,\n org: orgRecord,\n period: view.period,\n range,\n nowMs: view.nowMs,\n routes,\n }\n : null,\n [dataScope, tokens, groupId, orgRecord, view, range, routes],\n )\n\n /**\n * Every contact this reader may see, counted once for the two cards that\n * quote it — the tile, and the sample notice that compares the read to it.\n */\n const totalContacts = useAggregateRead<number>(\n () =>\n dataScope\n ? getCountFromServer(\n query(\n scopedCollection(firestore, dataScope, 'contacts'),\n ...visibleToClause(tokens),\n ),\n ).then((snapshot) => snapshot.data().count)\n : null,\n // The anchor is a dependency so Refresh re-asks; the key, which omits\n // it, is what answers a reopened section from memory.\n [firestore, dataScope, tokens, view.nowMs],\n report ? { cacheKey: reportCacheKey(report, 'contacts:total') } : {},\n )\n\n return (\n <Stack spacing={2}>\n <Stack\n direction=\"row\"\n spacing={2}\n sx={{\n alignItems: 'center',\n justifyContent: 'space-between',\n flexWrap: 'wrap',\n }}\n >\n <Typography variant=\"h6\" component=\"h2\">\n {'Reports'}\n </Typography>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center', flexWrap: 'wrap', rowGap: 1 }}>\n <ToggleButtonGroup\n exclusive\n size=\"small\"\n color=\"primary\"\n value={view.period}\n onChange={(_event, next) => {\n if (next) {\n setView({ period: next as CrmReportPeriod, nowMs: Date.now() })\n }\n }}\n aria-label=\"Report period\"\n >\n {CRM_REPORT_PERIODS.map((period) => (\n <ToggleButton key={period} value={period}>\n {CRM_REPORT_PERIOD_LABELS[period]}\n </ToggleButton>\n ))}\n </ToggleButtonGroup>\n <Button\n size=\"small\"\n startIcon={<MdiIcon path={mdiRefresh.path} size={0.8} />}\n disabled={!report}\n onClick={refresh}\n >\n {'Refresh'}\n </Button>\n </Stack>\n </Stack>\n {report ? (\n <>\n {/*\n The same masonry the billing and staff pages use: columns as tall\n as their own contents, so a short card beside a tall one leaves no\n hole. Measured live before this changed — 271, 272 and 111 pixels\n of dead space down one report, and an odd card stranded with an\n empty column beside it.\n */}\n <CardColumns\n spacing={2}\n items={[\n {\n key: 'trend',\n children: (\n <ContactsTrendCard report={report} totalContacts={totalContacts} />\n ),\n },\n {\n key: 'mix',\n children: (\n <ContactsMixCard report={report} totalContacts={totalContacts} />\n ),\n },\n { key: 'source-conversion', children: <SourceConversionCard report={report} /> },\n { key: 'lead-funnel', children: <LeadFunnelCard report={report} hostId={hostId} /> },\n { key: 'pipeline', children: <PipelineCard report={report} /> },\n {\n key: 'closed',\n /*\n One column item carrying two cards, because multicol picks\n its own break: two consecutive items are only USUALLY\n adjacent, and a card that is a footnote to the one above it\n cannot be left to that. Won and lost by owner asks a second\n question of the very rows Won and lost already read — the\n same window under the same `closed:won` and `closed:lost`\n keys — so it is read as that card's breakdown, and\n `break-inside: avoid` keeps the pair whole in one column.\n */\n children: (\n <Stack spacing={2}>\n <ClosedDealsCard report={report} />\n <WonLostByOwnerCard report={report} />\n </Stack>\n ),\n },\n ]}\n />\n {/*\n Outside the columns: multicol cannot span, and these three read\n across the full width.\n */}\n <Stack spacing={2}>\n <ActivityCard report={report} />\n <ForecastCard report={report} />\n <TasksCard report={report} />\n </Stack>\n </>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Loading…'}\n </Typography>\n )}\n </Stack>\n )\n}\nContactsReportsSection.displayName = 'ContactsReportsSection'\n\nexport default ContactsReportsSection\n"],"names":["mdiRefresh","MdiIcon","CardColumns","Button","Stack","ToggleButton","ToggleButtonGroup","Typography","getCountFromServer","query","useCallback","useMemo","useState","useFirestore","useCrmScope","crmRoutes","ActivityCard","ClosedDealsCard","ContactsMixCard","ContactsTrendCard","ForecastCard","LeadFunnelCard","PipelineCard","SourceConversionCard","WonLostByOwnerCard","reportCacheKey","reportCachePrefix","scopedCollection","visibleToClause","TasksCard","invalidateAggregateReads","useAggregateRead","CRM_REPORT_PERIOD_LABELS","CRM_REPORT_PERIODS","crmReportRange","ContactsReportsSection","props","hostId","org","basePath","firestore","scope","dataScope","consentGroup","visibleTo","tokens","groupId","orgRecord","view","setView","period","nowMs","Date","now","range","routes","refresh","current","report","totalContacts","then","snapshot","data","count","cacheKey","spacing","direction","sx","alignItems","justifyContent","flexWrap","variant","component","rowGap","exclusive","size","color","value","onChange","_event","next","aria-label","map","startIcon","path","disabled","onClick","items","key","children","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/reports-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 type { ConsolePluginPageProps } from '@aglyn/aglyn'\nimport { mdiRefresh } from '@aglyn/shared-data-mdi'\nimport {\n MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { CardColumns } from '@aglyn/shared-ui-jsx/components/card-columns'\nimport {\n Button,\n Stack,\n ToggleButton,\n ToggleButtonGroup,\n Typography,\n} from '@mui/material'\nimport { getCountFromServer, query } from 'firebase/firestore'\nimport { useCallback, useMemo, useState } from 'react'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport { useCrmScope } from '../hooks/use-crm-scope'\nimport { crmRoutes } from '../model/crm-routes'\nimport { ActivityCard } from './reports/activity-card'\nimport { ClosedDealsCard } from './reports/closed-deals-card'\nimport { ContactsMixCard } from './reports/contacts-mix-card'\nimport { ContactsTrendCard } from './reports/contacts-trend-card'\nimport { ForecastCard } from './reports/forecast-card'\nimport { LeadFunnelCard } from './reports/lead-funnel-card'\nimport { LeadSourceCard } from './reports/lead-source-card'\nimport { PipelineCard } from './reports/pipeline-card'\nimport { SourceConversionCard } from './reports/source-conversion-card'\nimport { WonLostByOwnerCard } from './reports/won-lost-by-owner-card'\nimport {\n type CrmReportScope,\n reportCacheKey,\n reportCachePrefix,\n scopedCollection,\n visibleToClause,\n} from './reports/report-scope'\nimport { TasksCard } from './reports/tasks-card'\nimport { invalidateAggregateReads, useAggregateRead } from './reports/use-aggregate-read'\nimport {\n CRM_REPORT_PERIOD_LABELS,\n CRM_REPORT_PERIODS,\n type CrmReportPeriod,\n crmReportRange,\n} from '../model/crm-reports'\n\n/**\n * `/crm/reports` — the CRM in aggregate (AGL-2604, AGL-2624).\n *\n * Nine cards, each reading its own collection: contacts over time, contacts\n * by source and stage, which sources convert, this site's lead funnel, the\n * open pipeline, what closed, who logged what, the forecast by close month\n * (AGL-2620, over the pipeline card's own window), and the task load. This\n * section resolves what they share and hands it down — see `CrmReportScope`\n * — so that every card counts the same reader's records over the same\n * clock. The lead funnel is the one card handed the site as well: leads live\n * under the host, not the org.\n *\n * ## The period is a control, and the reads follow it\n *\n * Nothing on this page reads a year on mount. The picker names the period,\n * the period sizes every read (one count per week of it, the deals closed\n * within it), and the default is thirty days. The three cards that are\n * stocks rather than flows — the open pipeline, its forecast, the open\n * tasks — ignore the period because \"what is open\" has no period.\n *\n * ## Scoped like the list\n *\n * Every query carries `visibleTo array-contains-any` over the group's\n * tokens, the predicate the rules evaluate, so a report can never count a\n * record its reader could not open — and the total here is the reader's\n * total, not the org's billable audience, which is the head-count the\n * contacts list keeps for the quota.\n *\n * ## Read once a minute, not once an arrival\n *\n * Every read on this page is keyed by the scope and the period and kept\n * for `AGGREGATE_READ_TTL_MS` — see `useAggregateRead` — so leaving to open\n * a deal the report named and coming straight back costs nothing. Refresh\n * forgets this reader's answers for this org and anchors the period again,\n * which is what makes every card ask afresh.\n */\nexport function ContactsReportsSection(props: ConsolePluginPageProps) {\n const { hostId, org, basePath } = props\n const firestore = useFirestore()\n // The org data root — null until the org lookup settles, and the page\n // reads nothing until it does — and the controller this page is viewed\n // as, resolved the way every CRM surface resolves them so the facet a\n // stage is read from here is the facet the contacts list edits it in.\n const { scope: dataScope, consentGroup, visibleTo } = useCrmScope({ hostId, org })\n // `null` at the organization level (AGL-2630): no clause, and every card\n // totals every site; the holder is then each contact's own.\n const tokens = useMemo(() => (visibleTo ? [...visibleTo] : null), [visibleTo])\n const groupId = consentGroup?.groupId ?? null\n const orgRecord = (org ?? null) as Record<string, unknown> | null\n /*\n * The period AND the moment it was chosen, as one value: the range is\n * anchored when the reader picks, not re-derived from a moving `Date.now()`\n * per render, which would rebuild every query on every pass.\n */\n const [view, setView] = useState<{\n period: CrmReportPeriod\n nowMs: number\n }>(() => ({ period: '30d', nowMs: Date.now() }))\n const range = useMemo(\n () => crmReportRange(view.period, view.nowMs),\n [view],\n )\n const routes = useMemo(() => crmRoutes(basePath ?? ''), [basePath])\n const refresh = useCallback(() => {\n if (dataScope) {\n invalidateAggregateReads(reportCachePrefix({ scope: dataScope, tokens, groupId }))\n }\n // A new anchor is what changes every card's dependencies, so each read\n // runs again and — its key forgotten — reaches the server.\n setView((current) => ({ period: current.period, nowMs: Date.now() }))\n }, [dataScope, tokens, groupId])\n\n const report = useMemo<CrmReportScope | null>(\n () =>\n dataScope\n ? {\n scope: dataScope,\n tokens,\n groupId,\n org: orgRecord,\n period: view.period,\n range,\n nowMs: view.nowMs,\n routes,\n }\n : null,\n [dataScope, tokens, groupId, orgRecord, view, range, routes],\n )\n\n /**\n * Every contact this reader may see, counted once for the two cards that\n * quote it — the tile, and the sample notice that compares the read to it.\n */\n const totalContacts = useAggregateRead<number>(\n () =>\n dataScope\n ? getCountFromServer(\n query(\n scopedCollection(firestore, dataScope, 'contacts'),\n ...visibleToClause(tokens),\n ),\n ).then((snapshot) => snapshot.data().count)\n : null,\n // The anchor is a dependency so Refresh re-asks; the key, which omits\n // it, is what answers a reopened section from memory.\n [firestore, dataScope, tokens, view.nowMs],\n report ? { cacheKey: reportCacheKey(report, 'contacts:total') } : {},\n )\n\n return (\n <Stack spacing={2}>\n <Stack\n direction=\"row\"\n spacing={2}\n sx={{\n alignItems: 'center',\n justifyContent: 'space-between',\n flexWrap: 'wrap',\n }}\n >\n <Typography variant=\"h6\" component=\"h2\">\n {'Reports'}\n </Typography>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center', flexWrap: 'wrap', rowGap: 1 }}>\n <ToggleButtonGroup\n exclusive\n size=\"small\"\n color=\"primary\"\n value={view.period}\n onChange={(_event, next) => {\n if (next) {\n setView({ period: next as CrmReportPeriod, nowMs: Date.now() })\n }\n }}\n aria-label=\"Report period\"\n >\n {CRM_REPORT_PERIODS.map((period) => (\n <ToggleButton key={period} value={period}>\n {CRM_REPORT_PERIOD_LABELS[period]}\n </ToggleButton>\n ))}\n </ToggleButtonGroup>\n <Button\n size=\"small\"\n startIcon={<MdiIcon path={mdiRefresh.path} size={0.8} />}\n disabled={!report}\n onClick={refresh}\n >\n {'Refresh'}\n </Button>\n </Stack>\n </Stack>\n {report ? (\n <>\n {/*\n The same masonry the billing and staff pages use: columns as tall\n as their own contents, so a short card beside a tall one leaves no\n hole. Measured live before this changed — 271, 272 and 111 pixels\n of dead space down one report, and an odd card stranded with an\n empty column beside it.\n */}\n <CardColumns\n spacing={2}\n items={[\n {\n key: 'trend',\n children: (\n <ContactsTrendCard report={report} totalContacts={totalContacts} />\n ),\n },\n {\n key: 'mix',\n children: (\n <ContactsMixCard report={report} totalContacts={totalContacts} />\n ),\n },\n { key: 'source-conversion', children: <SourceConversionCard report={report} /> },\n { key: 'lead-funnel', children: <LeadFunnelCard report={report} hostId={hostId} /> },\n { key: 'lead-sources', children: <LeadSourceCard report={report} hostId={hostId} /> },\n { key: 'pipeline', children: <PipelineCard report={report} /> },\n {\n key: 'closed',\n /*\n One column item carrying two cards, because multicol picks\n its own break: two consecutive items are only USUALLY\n adjacent, and a card that is a footnote to the one above it\n cannot be left to that. Won and lost by owner asks a second\n question of the very rows Won and lost already read — the\n same window under the same `closed:won` and `closed:lost`\n keys — so it is read as that card's breakdown, and\n `break-inside: avoid` keeps the pair whole in one column.\n */\n children: (\n <Stack spacing={2}>\n <ClosedDealsCard report={report} />\n <WonLostByOwnerCard report={report} />\n </Stack>\n ),\n },\n ]}\n />\n {/*\n Outside the columns: multicol cannot span, and these three read\n across the full width.\n */}\n <Stack spacing={2}>\n <ActivityCard report={report} />\n <ForecastCard report={report} />\n <TasksCard report={report} />\n </Stack>\n </>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Loading…'}\n </Typography>\n )}\n </Stack>\n )\n}\nContactsReportsSection.displayName = 'ContactsReportsSection'\n\nexport default ContactsReportsSection\n"],"names":["mdiRefresh","MdiIcon","CardColumns","Button","Stack","ToggleButton","ToggleButtonGroup","Typography","getCountFromServer","query","useCallback","useMemo","useState","useFirestore","useCrmScope","crmRoutes","ActivityCard","ClosedDealsCard","ContactsMixCard","ContactsTrendCard","ForecastCard","LeadFunnelCard","LeadSourceCard","PipelineCard","SourceConversionCard","WonLostByOwnerCard","reportCacheKey","reportCachePrefix","scopedCollection","visibleToClause","TasksCard","invalidateAggregateReads","useAggregateRead","CRM_REPORT_PERIOD_LABELS","CRM_REPORT_PERIODS","crmReportRange","ContactsReportsSection","props","hostId","org","basePath","firestore","scope","dataScope","consentGroup","visibleTo","tokens","groupId","orgRecord","view","setView","period","nowMs","Date","now","range","routes","refresh","current","report","totalContacts","then","snapshot","data","count","cacheKey","spacing","direction","sx","alignItems","justifyContent","flexWrap","variant","component","rowGap","exclusive","size","color","value","onChange","_event","next","aria-label","map","startIcon","path","disabled","onClick","items","key","children","displayName"],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { type CrmPicklist } from '@aglyn/aglyn';
1
2
  import type { OrgMemberOption } from '../hooks/use-org-member-directory';
2
3
  import { type CrmShareAccess, type CrmSharingCriteria, type CrmSharingObject, type CrmSharingRule, type CrmSharingSite } from '../model/crm-sharing';
3
4
  /** What the drawer hands back: the rule as `crm/sharing` `rule-save` reads it. */
@@ -22,6 +23,10 @@ export interface SharingRuleDrawerProps {
22
23
  value: string;
23
24
  label: string;
24
25
  }[];
26
+ /** The org's Lead source list, which the Lead source criterion picks from. */
27
+ leadSourceList?: CrmPicklist;
28
+ /** The org's Lead status list, whose labels name the statuses a rule matches (AGL-3512). */
29
+ leadStatusList?: CrmPicklist;
25
30
  busy: boolean;
26
31
  onSubmit: (draft: SharingRuleDraft) => void;
27
32
  }