@aglyn/plugins-crm 1.0.0-beta.143
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.
- package/LICENSE +201 -0
- package/README.md +7 -0
- package/package.json +57 -0
- package/src/index.d.ts +21 -0
- package/src/index.js +29 -0
- package/src/index.js.map +1 -0
- package/src/lib/components/activity-list.d.ts +98 -0
- package/src/lib/components/activity-list.js +321 -0
- package/src/lib/components/activity-list.js.map +1 -0
- package/src/lib/components/activity-queries.d.ts +117 -0
- package/src/lib/components/activity-queries.js +198 -0
- package/src/lib/components/activity-queries.js.map +1 -0
- package/src/lib/components/add-to-list-button.d.ts +25 -0
- package/src/lib/components/add-to-list-button.js +64 -0
- package/src/lib/components/add-to-list-button.js.map +1 -0
- package/src/lib/components/add-to-list-dialog.d.ts +24 -0
- package/src/lib/components/add-to-list-dialog.js +327 -0
- package/src/lib/components/add-to-list-dialog.js.map +1 -0
- package/src/lib/components/assignment-rule-drawer.d.ts +43 -0
- package/src/lib/components/assignment-rule-drawer.js +293 -0
- package/src/lib/components/assignment-rule-drawer.js.map +1 -0
- package/src/lib/components/companies-bulk-bar.d.ts +29 -0
- package/src/lib/components/companies-bulk-bar.js +274 -0
- package/src/lib/components/companies-bulk-bar.js.map +1 -0
- package/src/lib/components/companies-section.d.ts +54 -0
- package/src/lib/components/companies-section.js +522 -0
- package/src/lib/components/companies-section.js.map +1 -0
- package/src/lib/components/company-contacts-card.d.ts +49 -0
- package/src/lib/components/company-contacts-card.js +435 -0
- package/src/lib/components/company-contacts-card.js.map +1 -0
- package/src/lib/components/company-deals-card.d.ts +20 -0
- package/src/lib/components/company-deals-card.js +38 -0
- package/src/lib/components/company-deals-card.js.map +1 -0
- package/src/lib/components/company-detail-page.d.ts +20 -0
- package/src/lib/components/company-detail-page.js +165 -0
- package/src/lib/components/company-detail-page.js.map +1 -0
- package/src/lib/components/company-edit-drawer.d.ts +65 -0
- package/src/lib/components/company-edit-drawer.js +433 -0
- package/src/lib/components/company-edit-drawer.js.map +1 -0
- package/src/lib/components/company-import-drawer.d.ts +42 -0
- package/src/lib/components/company-import-drawer.js +99 -0
- package/src/lib/components/company-import-drawer.js.map +1 -0
- package/src/lib/components/company-picker.d.ts +127 -0
- package/src/lib/components/company-picker.js +323 -0
- package/src/lib/components/company-picker.js.map +1 -0
- package/src/lib/components/company-properties-card.d.ts +47 -0
- package/src/lib/components/company-properties-card.js +289 -0
- package/src/lib/components/company-properties-card.js.map +1 -0
- package/src/lib/components/contact-address-fields.d.ts +29 -0
- package/src/lib/components/contact-address-fields.js +149 -0
- package/src/lib/components/contact-address-fields.js.map +1 -0
- package/src/lib/components/contact-associations-card.d.ts +72 -0
- package/src/lib/components/contact-associations-card.js +296 -0
- package/src/lib/components/contact-associations-card.js.map +1 -0
- package/src/lib/components/contact-custom-columns.d.ts +38 -0
- package/src/lib/components/contact-custom-columns.js +102 -0
- package/src/lib/components/contact-custom-columns.js.map +1 -0
- package/src/lib/components/contact-custom-fields-card.d.ts +39 -0
- package/src/lib/components/contact-custom-fields-card.js +204 -0
- package/src/lib/components/contact-custom-fields-card.js.map +1 -0
- package/src/lib/components/contact-deals-card.d.ts +20 -0
- package/src/lib/components/contact-deals-card.js +38 -0
- package/src/lib/components/contact-deals-card.js.map +1 -0
- package/src/lib/components/contact-detail-page.d.ts +46 -0
- package/src/lib/components/contact-detail-page.js +535 -0
- package/src/lib/components/contact-detail-page.js.map +1 -0
- package/src/lib/components/contact-duplicates-card.d.ts +42 -0
- package/src/lib/components/contact-duplicates-card.js +167 -0
- package/src/lib/components/contact-duplicates-card.js.map +1 -0
- package/src/lib/components/contact-field-drawer.d.ts +64 -0
- package/src/lib/components/contact-field-drawer.js +251 -0
- package/src/lib/components/contact-field-drawer.js.map +1 -0
- package/src/lib/components/contact-import-drawer.d.ts +46 -0
- package/src/lib/components/contact-import-drawer.js +149 -0
- package/src/lib/components/contact-import-drawer.js.map +1 -0
- package/src/lib/components/contact-known-by-card.d.ts +39 -0
- package/src/lib/components/contact-known-by-card.js +142 -0
- package/src/lib/components/contact-known-by-card.js.map +1 -0
- package/src/lib/components/contact-list-columns.d.ts +53 -0
- package/src/lib/components/contact-list-columns.js +288 -0
- package/src/lib/components/contact-list-columns.js.map +1 -0
- package/src/lib/components/contact-merge-dialog.d.ts +79 -0
- package/src/lib/components/contact-merge-dialog.js +405 -0
- package/src/lib/components/contact-merge-dialog.js.map +1 -0
- package/src/lib/components/contact-properties-card.d.ts +89 -0
- package/src/lib/components/contact-properties-card.js +492 -0
- package/src/lib/components/contact-properties-card.js.map +1 -0
- package/src/lib/components/contact-timeline-card.d.ts +68 -0
- package/src/lib/components/contact-timeline-card.js +435 -0
- package/src/lib/components/contact-timeline-card.js.map +1 -0
- package/src/lib/components/contacts-bulk-bar.d.ts +91 -0
- package/src/lib/components/contacts-bulk-bar.js +493 -0
- package/src/lib/components/contacts-bulk-bar.js.map +1 -0
- package/src/lib/components/contacts-section.d.ts +26 -0
- package/src/lib/components/contacts-section.js +1025 -0
- package/src/lib/components/contacts-section.js.map +1 -0
- package/src/lib/components/copy-capture-address-button.d.ts +33 -0
- package/src/lib/components/copy-capture-address-button.js +60 -0
- package/src/lib/components/copy-capture-address-button.js.map +1 -0
- package/src/lib/components/crm-attribution-zone.d.ts +38 -0
- package/src/lib/components/crm-attribution-zone.js +36 -0
- package/src/lib/components/crm-attribution-zone.js.map +1 -0
- package/src/lib/components/crm-booking-zone.d.ts +61 -0
- package/src/lib/components/crm-booking-zone.js +50 -0
- package/src/lib/components/crm-booking-zone.js.map +1 -0
- package/src/lib/components/crm-bulk-bar-frame.d.ts +47 -0
- package/src/lib/components/crm-bulk-bar-frame.js +137 -0
- package/src/lib/components/crm-bulk-bar-frame.js.map +1 -0
- package/src/lib/components/crm-call-actions.d.ts +65 -0
- package/src/lib/components/crm-call-actions.js +110 -0
- package/src/lib/components/crm-call-actions.js.map +1 -0
- package/src/lib/components/crm-column-menu.d.ts +21 -0
- package/src/lib/components/crm-column-menu.js +103 -0
- package/src/lib/components/crm-column-menu.js.map +1 -0
- package/src/lib/components/crm-console-page.d.ts +19 -0
- package/src/lib/components/crm-console-page.js +151 -0
- package/src/lib/components/crm-console-page.js.map +1 -0
- package/src/lib/components/crm-console-sections.d.ts +52 -0
- package/src/lib/components/crm-console-sections.js +83 -0
- package/src/lib/components/crm-console-sections.js.map +1 -0
- package/src/lib/components/crm-custom-field-control.d.ts +26 -0
- package/src/lib/components/crm-custom-field-control.js +131 -0
- package/src/lib/components/crm-custom-field-control.js.map +1 -0
- package/src/lib/components/crm-email-template-tools.d.ts +69 -0
- package/src/lib/components/crm-email-template-tools.js +240 -0
- package/src/lib/components/crm-email-template-tools.js.map +1 -0
- package/src/lib/components/crm-export-all-button.d.ts +39 -0
- package/src/lib/components/crm-export-all-button.js +98 -0
- package/src/lib/components/crm-export-all-button.js.map +1 -0
- package/src/lib/components/crm-filter-bar.d.ts +53 -0
- package/src/lib/components/crm-filter-bar.js +294 -0
- package/src/lib/components/crm-filter-bar.js.map +1 -0
- package/src/lib/components/crm-glance-card.d.ts +54 -0
- package/src/lib/components/crm-glance-card.js +219 -0
- package/src/lib/components/crm-glance-card.js.map +1 -0
- package/src/lib/components/crm-next-activity-column.d.ts +35 -0
- package/src/lib/components/crm-next-activity-column.js +100 -0
- package/src/lib/components/crm-next-activity-column.js.map +1 -0
- package/src/lib/components/crm-record-header.d.ts +97 -0
- package/src/lib/components/crm-record-header.js +160 -0
- package/src/lib/components/crm-record-header.js.map +1 -0
- package/src/lib/components/crm-record-insights-zone.d.ts +50 -0
- package/src/lib/components/crm-record-insights-zone.js +169 -0
- package/src/lib/components/crm-record-insights-zone.js.map +1 -0
- package/src/lib/components/crm-record-picker.d.ts +42 -0
- package/src/lib/components/crm-record-picker.js +104 -0
- package/src/lib/components/crm-record-picker.js.map +1 -0
- package/src/lib/components/crm-send-email-button.d.ts +39 -0
- package/src/lib/components/crm-send-email-button.js +81 -0
- package/src/lib/components/crm-send-email-button.js.map +1 -0
- package/src/lib/components/crm-send-email-dialog.d.ts +61 -0
- package/src/lib/components/crm-send-email-dialog.js +851 -0
- package/src/lib/components/crm-send-email-dialog.js.map +1 -0
- package/src/lib/components/crm-site-picker.d.ts +61 -0
- package/src/lib/components/crm-site-picker.js +99 -0
- package/src/lib/components/crm-site-picker.js.map +1 -0
- package/src/lib/components/crm-suite-lock.d.ts +29 -0
- package/src/lib/components/crm-suite-lock.js +114 -0
- package/src/lib/components/crm-suite-lock.js.map +1 -0
- package/src/lib/components/crm-tasks-due-card.d.ts +42 -0
- package/src/lib/components/crm-tasks-due-card.js +225 -0
- package/src/lib/components/crm-tasks-due-card.js.map +1 -0
- package/src/lib/components/crm-views-control.d.ts +65 -0
- package/src/lib/components/crm-views-control.js +518 -0
- package/src/lib/components/crm-views-control.js.map +1 -0
- package/src/lib/components/csv-import-drawer.d.ts +132 -0
- package/src/lib/components/csv-import-drawer.js +746 -0
- package/src/lib/components/csv-import-drawer.js.map +1 -0
- package/src/lib/components/deal-board.d.ts +70 -0
- package/src/lib/components/deal-board.js +478 -0
- package/src/lib/components/deal-board.js.map +1 -0
- package/src/lib/components/deal-detail-page.d.ts +18 -0
- package/src/lib/components/deal-detail-page.js +323 -0
- package/src/lib/components/deal-detail-page.js.map +1 -0
- package/src/lib/components/deal-edit-drawer.d.ts +59 -0
- package/src/lib/components/deal-edit-drawer.js +640 -0
- package/src/lib/components/deal-edit-drawer.js.map +1 -0
- package/src/lib/components/deal-import-drawer.d.ts +24 -0
- package/src/lib/components/deal-import-drawer.js +83 -0
- package/src/lib/components/deal-import-drawer.js.map +1 -0
- package/src/lib/components/deal-products-card.d.ts +48 -0
- package/src/lib/components/deal-products-card.js +614 -0
- package/src/lib/components/deal-products-card.js.map +1 -0
- package/src/lib/components/deal-properties-card.d.ts +29 -0
- package/src/lib/components/deal-properties-card.js +144 -0
- package/src/lib/components/deal-properties-card.js.map +1 -0
- package/src/lib/components/deal-stage-card.d.ts +24 -0
- package/src/lib/components/deal-stage-card.js +199 -0
- package/src/lib/components/deal-stage-card.js.map +1 -0
- package/src/lib/components/deals-bulk-bar.d.ts +31 -0
- package/src/lib/components/deals-bulk-bar.js +340 -0
- package/src/lib/components/deals-bulk-bar.js.map +1 -0
- package/src/lib/components/deals-section.d.ts +49 -0
- package/src/lib/components/deals-section.js +767 -0
- package/src/lib/components/deals-section.js.map +1 -0
- package/src/lib/components/email-capture-card.d.ts +42 -0
- package/src/lib/components/email-capture-card.js +134 -0
- package/src/lib/components/email-capture-card.js.map +1 -0
- package/src/lib/components/email-template-drawer.d.ts +33 -0
- package/src/lib/components/email-template-drawer.js +209 -0
- package/src/lib/components/email-template-drawer.js.map +1 -0
- package/src/lib/components/email-templates-card.d.ts +33 -0
- package/src/lib/components/email-templates-card.js +370 -0
- package/src/lib/components/email-templates-card.js.map +1 -0
- package/src/lib/components/erase-person-action.d.ts +92 -0
- package/src/lib/components/erase-person-action.js +261 -0
- package/src/lib/components/erase-person-action.js.map +1 -0
- package/src/lib/components/fields-section.d.ts +37 -0
- package/src/lib/components/fields-section.js +641 -0
- package/src/lib/components/fields-section.js.map +1 -0
- package/src/lib/components/form-contact-fields-card.d.ts +46 -0
- package/src/lib/components/form-contact-fields-card.js +253 -0
- package/src/lib/components/form-contact-fields-card.js.map +1 -0
- package/src/lib/components/insert-link-at-caret.d.ts +28 -0
- package/src/lib/components/insert-link-at-caret.js +37 -0
- package/src/lib/components/insert-link-at-caret.js.map +1 -0
- package/src/lib/components/lead-convert-dialog.d.ts +32 -0
- package/src/lib/components/lead-convert-dialog.js +493 -0
- package/src/lib/components/lead-convert-dialog.js.map +1 -0
- package/src/lib/components/lead-detail-page.d.ts +16 -0
- package/src/lib/components/lead-detail-page.js +163 -0
- package/src/lib/components/lead-detail-page.js.map +1 -0
- package/src/lib/components/lead-history-card.d.ts +32 -0
- package/src/lib/components/lead-history-card.js +165 -0
- package/src/lib/components/lead-history-card.js.map +1 -0
- package/src/lib/components/lead-import-drawer.d.ts +24 -0
- package/src/lib/components/lead-import-drawer.js +95 -0
- package/src/lib/components/lead-import-drawer.js.map +1 -0
- package/src/lib/components/lead-owner-select.d.ts +35 -0
- package/src/lib/components/lead-owner-select.js +91 -0
- package/src/lib/components/lead-owner-select.js.map +1 -0
- package/src/lib/components/lead-properties-card.d.ts +61 -0
- package/src/lib/components/lead-properties-card.js +395 -0
- package/src/lib/components/lead-properties-card.js.map +1 -0
- package/src/lib/components/lead-status-chip.d.ts +12 -0
- package/src/lib/components/lead-status-chip.js +43 -0
- package/src/lib/components/lead-status-chip.js.map +1 -0
- package/src/lib/components/lead-surfaces-note.d.ts +126 -0
- package/src/lib/components/lead-surfaces-note.js +262 -0
- package/src/lib/components/lead-surfaces-note.js.map +1 -0
- package/src/lib/components/lead-unqualify-dialog.d.ts +24 -0
- package/src/lib/components/lead-unqualify-dialog.js +125 -0
- package/src/lib/components/lead-unqualify-dialog.js.map +1 -0
- package/src/lib/components/leads-bulk-bar.d.ts +58 -0
- package/src/lib/components/leads-bulk-bar.js +307 -0
- package/src/lib/components/leads-bulk-bar.js.map +1 -0
- package/src/lib/components/leads-section.d.ts +23 -0
- package/src/lib/components/leads-section.js +664 -0
- package/src/lib/components/leads-section.js.map +1 -0
- package/src/lib/components/linked-deals-card.d.ts +32 -0
- package/src/lib/components/linked-deals-card.js +163 -0
- package/src/lib/components/linked-deals-card.js.map +1 -0
- package/src/lib/components/log-activity-dialog.d.ts +60 -0
- package/src/lib/components/log-activity-dialog.js +368 -0
- package/src/lib/components/log-activity-dialog.js.map +1 -0
- package/src/lib/components/lost-reason-dialog.d.ts +26 -0
- package/src/lib/components/lost-reason-dialog.js +95 -0
- package/src/lib/components/lost-reason-dialog.js.map +1 -0
- package/src/lib/components/new-contact-drawer.d.ts +78 -0
- package/src/lib/components/new-contact-drawer.js +343 -0
- package/src/lib/components/new-contact-drawer.js.map +1 -0
- package/src/lib/components/org-lead-surfaces-note.d.ts +57 -0
- package/src/lib/components/org-lead-surfaces-note.js +172 -0
- package/src/lib/components/org-lead-surfaces-note.js.map +1 -0
- package/src/lib/components/owner-avatar.d.ts +18 -0
- package/src/lib/components/owner-avatar.js +61 -0
- package/src/lib/components/owner-avatar.js.map +1 -0
- package/src/lib/components/pipeline-stages-dialog.d.ts +43 -0
- package/src/lib/components/pipeline-stages-dialog.js +339 -0
- package/src/lib/components/pipeline-stages-dialog.js.map +1 -0
- package/src/lib/components/pipelines-dialog.d.ts +55 -0
- package/src/lib/components/pipelines-dialog.js +517 -0
- package/src/lib/components/pipelines-dialog.js.map +1 -0
- package/src/lib/components/recent-activity-feed.d.ts +39 -0
- package/src/lib/components/recent-activity-feed.js +103 -0
- package/src/lib/components/recent-activity-feed.js.map +1 -0
- package/src/lib/components/recipes-card.d.ts +39 -0
- package/src/lib/components/recipes-card.js +485 -0
- package/src/lib/components/recipes-card.js.map +1 -0
- package/src/lib/components/record-activity-card.d.ts +26 -0
- package/src/lib/components/record-activity-card.js +121 -0
- package/src/lib/components/record-activity-card.js.map +1 -0
- package/src/lib/components/record-files-card.d.ts +67 -0
- package/src/lib/components/record-files-card.js +290 -0
- package/src/lib/components/record-files-card.js.map +1 -0
- package/src/lib/components/record-tasks-card.d.ts +32 -0
- package/src/lib/components/record-tasks-card.js +306 -0
- package/src/lib/components/record-tasks-card.js.map +1 -0
- package/src/lib/components/reports/activity-card.d.ts +57 -0
- package/src/lib/components/reports/activity-card.js +281 -0
- package/src/lib/components/reports/activity-card.js.map +1 -0
- package/src/lib/components/reports/closed-deals-card.d.ts +20 -0
- package/src/lib/components/reports/closed-deals-card.js +194 -0
- package/src/lib/components/reports/closed-deals-card.js.map +1 -0
- package/src/lib/components/reports/contacts-mix-card.d.ts +27 -0
- package/src/lib/components/reports/contacts-mix-card.js +161 -0
- package/src/lib/components/reports/contacts-mix-card.js.map +1 -0
- package/src/lib/components/reports/contacts-trend-card.d.ts +31 -0
- package/src/lib/components/reports/contacts-trend-card.js +144 -0
- package/src/lib/components/reports/contacts-trend-card.js.map +1 -0
- package/src/lib/components/reports/forecast-card.d.ts +22 -0
- package/src/lib/components/reports/forecast-card.js +251 -0
- package/src/lib/components/reports/forecast-card.js.map +1 -0
- package/src/lib/components/reports/lead-counts.d.ts +32 -0
- package/src/lib/components/reports/lead-counts.js +44 -0
- package/src/lib/components/reports/lead-counts.js.map +1 -0
- package/src/lib/components/reports/lead-funnel-card.d.ts +38 -0
- package/src/lib/components/reports/lead-funnel-card.js +375 -0
- package/src/lib/components/reports/lead-funnel-card.js.map +1 -0
- package/src/lib/components/reports/pipeline-card.d.ts +19 -0
- package/src/lib/components/reports/pipeline-card.js +338 -0
- package/src/lib/components/reports/pipeline-card.js.map +1 -0
- package/src/lib/components/reports/report-bar-chart.d.ts +39 -0
- package/src/lib/components/reports/report-bar-chart.js +133 -0
- package/src/lib/components/reports/report-bar-chart.js.map +1 -0
- package/src/lib/components/reports/report-breakdown.d.ts +32 -0
- package/src/lib/components/reports/report-breakdown.js +105 -0
- package/src/lib/components/reports/report-breakdown.js.map +1 -0
- package/src/lib/components/reports/report-export.d.ts +37 -0
- package/src/lib/components/reports/report-export.js +66 -0
- package/src/lib/components/reports/report-export.js.map +1 -0
- package/src/lib/components/reports/report-format.d.ts +36 -0
- package/src/lib/components/reports/report-format.js +43 -0
- package/src/lib/components/reports/report-format.js.map +1 -0
- package/src/lib/components/reports/report-scope.d.ts +81 -0
- package/src/lib/components/reports/report-scope.js +58 -0
- package/src/lib/components/reports/report-scope.js.map +1 -0
- package/src/lib/components/reports/report-stat-tile.d.ts +47 -0
- package/src/lib/components/reports/report-stat-tile.js +96 -0
- package/src/lib/components/reports/report-stat-tile.js.map +1 -0
- package/src/lib/components/reports/source-conversion-card.d.ts +28 -0
- package/src/lib/components/reports/source-conversion-card.js +205 -0
- package/src/lib/components/reports/source-conversion-card.js.map +1 -0
- package/src/lib/components/reports/tasks-card.d.ts +26 -0
- package/src/lib/components/reports/tasks-card.js +279 -0
- package/src/lib/components/reports/tasks-card.js.map +1 -0
- package/src/lib/components/reports/use-aggregate-read.d.ts +99 -0
- package/src/lib/components/reports/use-aggregate-read.js +195 -0
- package/src/lib/components/reports/use-aggregate-read.js.map +1 -0
- package/src/lib/components/reports/won-lost-by-owner-card.d.ts +30 -0
- package/src/lib/components/reports/won-lost-by-owner-card.js +168 -0
- package/src/lib/components/reports/won-lost-by-owner-card.js.map +1 -0
- package/src/lib/components/reports-section.d.ts +42 -0
- package/src/lib/components/reports-section.js +308 -0
- package/src/lib/components/reports-section.js.map +1 -0
- package/src/lib/components/sending-addresses-card.d.ts +49 -0
- package/src/lib/components/sending-addresses-card.js +262 -0
- package/src/lib/components/sending-addresses-card.js.map +1 -0
- package/src/lib/components/settings-section.d.ts +151 -0
- package/src/lib/components/settings-section.js +783 -0
- package/src/lib/components/settings-section.js.map +1 -0
- package/src/lib/components/task-cells.d.ts +51 -0
- package/src/lib/components/task-cells.js +139 -0
- package/src/lib/components/task-cells.js.map +1 -0
- package/src/lib/components/task-edit-drawer.d.ts +53 -0
- package/src/lib/components/task-edit-drawer.js +483 -0
- package/src/lib/components/task-edit-drawer.js.map +1 -0
- package/src/lib/components/task-import-drawer.d.ts +24 -0
- package/src/lib/components/task-import-drawer.js +82 -0
- package/src/lib/components/task-import-drawer.js.map +1 -0
- package/src/lib/components/task-snooze-menu.d.ts +65 -0
- package/src/lib/components/task-snooze-menu.js +207 -0
- package/src/lib/components/task-snooze-menu.js.map +1 -0
- package/src/lib/components/tasks-bulk-bar.d.ts +25 -0
- package/src/lib/components/tasks-bulk-bar.js +348 -0
- package/src/lib/components/tasks-bulk-bar.js.map +1 -0
- package/src/lib/components/tasks-calendar.d.ts +38 -0
- package/src/lib/components/tasks-calendar.js +228 -0
- package/src/lib/components/tasks-calendar.js.map +1 -0
- package/src/lib/components/tasks-section.d.ts +27 -0
- package/src/lib/components/tasks-section.js +579 -0
- package/src/lib/components/tasks-section.js.map +1 -0
- package/src/lib/components/use-contact-campaign-emails.d.ts +32 -0
- package/src/lib/components/use-contact-campaign-emails.js +83 -0
- package/src/lib/components/use-contact-campaign-emails.js.map +1 -0
- package/src/lib/components/use-crm-api.d.ts +44 -0
- package/src/lib/components/use-crm-api.js +84 -0
- package/src/lib/components/use-crm-api.js.map +1 -0
- package/src/lib/components/use-crm-inbound-address.d.ts +60 -0
- package/src/lib/components/use-crm-inbound-address.js +124 -0
- package/src/lib/components/use-crm-inbound-address.js.map +1 -0
- package/src/lib/components/use-emails-hub-path.d.ts +24 -0
- package/src/lib/components/use-emails-hub-path.js +59 -0
- package/src/lib/components/use-emails-hub-path.js.map +1 -0
- package/src/lib/components/use-member-email-aliases.d.ts +54 -0
- package/src/lib/components/use-member-email-aliases.js +186 -0
- package/src/lib/components/use-member-email-aliases.js.map +1 -0
- package/src/lib/components/use-org-members.d.ts +40 -0
- package/src/lib/components/use-org-members.js +100 -0
- package/src/lib/components/use-org-members.js.map +1 -0
- package/src/lib/constants/api-routes.d.ts +109 -0
- package/src/lib/constants/api-routes.js +74 -0
- package/src/lib/constants/api-routes.js.map +1 -0
- package/src/lib/constants/bundle-common.d.ts +14 -0
- package/src/lib/constants/bundle-common.js +14 -0
- package/src/lib/constants/bundle-common.js.map +1 -0
- package/src/lib/constants/company-filters.d.ts +18 -0
- package/src/lib/constants/company-filters.js +51 -0
- package/src/lib/constants/company-filters.js.map +1 -0
- package/src/lib/constants/contact-filters.d.ts +38 -0
- package/src/lib/constants/contact-filters.js +294 -0
- package/src/lib/constants/contact-filters.js.map +1 -0
- package/src/lib/hooks/use-contact-field-definitions.d.ts +48 -0
- package/src/lib/hooks/use-contact-field-definitions.js +72 -0
- package/src/lib/hooks/use-contact-field-definitions.js.map +1 -0
- package/src/lib/hooks/use-contact-update.d.ts +25 -0
- package/src/lib/hooks/use-contact-update.js +80 -0
- package/src/lib/hooks/use-contact-update.js.map +1 -0
- package/src/lib/hooks/use-crm-activity-logger.d.ts +73 -0
- package/src/lib/hooks/use-crm-activity-logger.js +101 -0
- package/src/lib/hooks/use-crm-activity-logger.js.map +1 -0
- package/src/lib/hooks/use-crm-bulk-apply.d.ts +52 -0
- package/src/lib/hooks/use-crm-bulk-apply.js +112 -0
- package/src/lib/hooks/use-crm-bulk-apply.js.map +1 -0
- package/src/lib/hooks/use-crm-default-view.d.ts +24 -0
- package/src/lib/hooks/use-crm-default-view.js +64 -0
- package/src/lib/hooks/use-crm-default-view.js.map +1 -0
- package/src/lib/hooks/use-crm-email-templates.d.ts +59 -0
- package/src/lib/hooks/use-crm-email-templates.js +103 -0
- package/src/lib/hooks/use-crm-email-templates.js.map +1 -0
- package/src/lib/hooks/use-crm-hub-path.d.ts +29 -0
- package/src/lib/hooks/use-crm-hub-path.js +41 -0
- package/src/lib/hooks/use-crm-hub-path.js.map +1 -0
- package/src/lib/hooks/use-crm-org-mount.d.ts +97 -0
- package/src/lib/hooks/use-crm-org-mount.js +215 -0
- package/src/lib/hooks/use-crm-org-mount.js.map +1 -0
- package/src/lib/hooks/use-crm-record-names.d.ts +52 -0
- package/src/lib/hooks/use-crm-record-names.js +126 -0
- package/src/lib/hooks/use-crm-record-names.js.map +1 -0
- package/src/lib/hooks/use-crm-records-quota.d.ts +68 -0
- package/src/lib/hooks/use-crm-records-quota.js +118 -0
- package/src/lib/hooks/use-crm-records-quota.js.map +1 -0
- package/src/lib/hooks/use-crm-saved-view.d.ts +96 -0
- package/src/lib/hooks/use-crm-saved-view.js +329 -0
- package/src/lib/hooks/use-crm-saved-view.js.map +1 -0
- package/src/lib/hooks/use-crm-scope.d.ts +117 -0
- package/src/lib/hooks/use-crm-scope.js +147 -0
- package/src/lib/hooks/use-crm-scope.js.map +1 -0
- package/src/lib/hooks/use-crm-tasks.d.ts +85 -0
- package/src/lib/hooks/use-crm-tasks.js +153 -0
- package/src/lib/hooks/use-crm-tasks.js.map +1 -0
- package/src/lib/hooks/use-crm-view-grid.d.ts +55 -0
- package/src/lib/hooks/use-crm-view-grid.js +170 -0
- package/src/lib/hooks/use-crm-view-grid.js.map +1 -0
- package/src/lib/hooks/use-crm-views.d.ts +42 -0
- package/src/lib/hooks/use-crm-views.js +84 -0
- package/src/lib/hooks/use-crm-views.js.map +1 -0
- package/src/lib/hooks/use-deal-stage-api.d.ts +69 -0
- package/src/lib/hooks/use-deal-stage-api.js +78 -0
- package/src/lib/hooks/use-deal-stage-api.js.map +1 -0
- package/src/lib/hooks/use-deals.d.ts +47 -0
- package/src/lib/hooks/use-deals.js +105 -0
- package/src/lib/hooks/use-deals.js.map +1 -0
- package/src/lib/hooks/use-org-leads.d.ts +53 -0
- package/src/lib/hooks/use-org-leads.js +114 -0
- package/src/lib/hooks/use-org-leads.js.map +1 -0
- package/src/lib/hooks/use-org-member-directory.d.ts +36 -0
- package/src/lib/hooks/use-org-member-directory.js +116 -0
- package/src/lib/hooks/use-org-member-directory.js.map +1 -0
- package/src/lib/hooks/use-org-member-options.d.ts +38 -0
- package/src/lib/hooks/use-org-member-options.js +131 -0
- package/src/lib/hooks/use-org-member-options.js.map +1 -0
- package/src/lib/hooks/use-pipeline.d.ts +71 -0
- package/src/lib/hooks/use-pipeline.js +159 -0
- package/src/lib/hooks/use-pipeline.js.map +1 -0
- package/src/lib/model/companies-bulk-writes.d.ts +40 -0
- package/src/lib/model/companies-bulk-writes.js +103 -0
- package/src/lib/model/companies-bulk-writes.js.map +1 -0
- package/src/lib/model/companies-csv.d.ts +36 -0
- package/src/lib/model/companies-csv.js +38 -0
- package/src/lib/model/companies-csv.js.map +1 -0
- package/src/lib/model/companies.d.ts +99 -0
- package/src/lib/model/companies.js +168 -0
- package/src/lib/model/companies.js.map +1 -0
- package/src/lib/model/company-delete-route.d.ts +43 -0
- package/src/lib/model/company-delete-route.js +31 -0
- package/src/lib/model/company-delete-route.js.map +1 -0
- package/src/lib/model/company-delete.d.ts +42 -0
- package/src/lib/model/company-delete.js +52 -0
- package/src/lib/model/company-delete.js.map +1 -0
- package/src/lib/model/contact-duplicates.d.ts +58 -0
- package/src/lib/model/contact-duplicates.js +67 -0
- package/src/lib/model/contact-duplicates.js.map +1 -0
- package/src/lib/model/contact-record.d.ts +113 -0
- package/src/lib/model/contact-record.js +70 -0
- package/src/lib/model/contact-record.js.map +1 -0
- package/src/lib/model/contact-update.d.ts +93 -0
- package/src/lib/model/contact-update.js +50 -0
- package/src/lib/model/contact-update.js.map +1 -0
- package/src/lib/model/contacts-bulk-writes.d.ts +173 -0
- package/src/lib/model/contacts-bulk-writes.js +196 -0
- package/src/lib/model/contacts-bulk-writes.js.map +1 -0
- package/src/lib/model/contacts-csv.d.ts +54 -0
- package/src/lib/model/contacts-csv.js +67 -0
- package/src/lib/model/contacts-csv.js.map +1 -0
- package/src/lib/model/contacts-list-seed.d.ts +51 -0
- package/src/lib/model/contacts-list-seed.js +63 -0
- package/src/lib/model/contacts-list-seed.js.map +1 -0
- package/src/lib/model/crm-activity-report.d.ts +27 -0
- package/src/lib/model/crm-activity-report.js +56 -0
- package/src/lib/model/crm-activity-report.js.map +1 -0
- package/src/lib/model/crm-api.d.ts +49 -0
- package/src/lib/model/crm-api.js +47 -0
- package/src/lib/model/crm-api.js.map +1 -0
- package/src/lib/model/crm-bulk-writes.d.ts +147 -0
- package/src/lib/model/crm-bulk-writes.js +207 -0
- package/src/lib/model/crm-bulk-writes.js.map +1 -0
- package/src/lib/model/crm-company-import.d.ts +154 -0
- package/src/lib/model/crm-company-import.js +331 -0
- package/src/lib/model/crm-company-import.js.map +1 -0
- package/src/lib/model/crm-custom-draft.d.ts +62 -0
- package/src/lib/model/crm-custom-draft.js +67 -0
- package/src/lib/model/crm-custom-draft.js.map +1 -0
- package/src/lib/model/crm-deal-import.d.ts +112 -0
- package/src/lib/model/crm-deal-import.js +263 -0
- package/src/lib/model/crm-deal-import.js.map +1 -0
- package/src/lib/model/crm-import.d.ts +244 -0
- package/src/lib/model/crm-import.js +466 -0
- package/src/lib/model/crm-import.js.map +1 -0
- package/src/lib/model/crm-lead-import.d.ts +105 -0
- package/src/lib/model/crm-lead-import.js +255 -0
- package/src/lib/model/crm-lead-import.js.map +1 -0
- package/src/lib/model/crm-record-routes.d.ts +21 -0
- package/src/lib/model/crm-record-routes.js +70 -0
- package/src/lib/model/crm-record-routes.js.map +1 -0
- package/src/lib/model/crm-routes.d.ts +101 -0
- package/src/lib/model/crm-routes.js +92 -0
- package/src/lib/model/crm-routes.js.map +1 -0
- package/src/lib/model/crm-task-import.d.ts +94 -0
- package/src/lib/model/crm-task-import.js +275 -0
- package/src/lib/model/crm-task-import.js.map +1 -0
- package/src/lib/model/crm-view-param.d.ts +43 -0
- package/src/lib/model/crm-view-param.js +49 -0
- package/src/lib/model/crm-view-param.js.map +1 -0
- package/src/lib/model/deal-board-model.d.ts +241 -0
- package/src/lib/model/deal-board-model.js +439 -0
- package/src/lib/model/deal-board-model.js.map +1 -0
- package/src/lib/model/deal-form-model.d.ts +114 -0
- package/src/lib/model/deal-form-model.js +218 -0
- package/src/lib/model/deal-form-model.js.map +1 -0
- package/src/lib/model/deals-csv.d.ts +32 -0
- package/src/lib/model/deals-csv.js +37 -0
- package/src/lib/model/deals-csv.js.map +1 -0
- package/src/lib/model/import-column-shapes.d.ts +36 -0
- package/src/lib/model/import-column-shapes.js +76 -0
- package/src/lib/model/import-column-shapes.js.map +1 -0
- package/src/lib/model/lead-company-suggestion.d.ts +61 -0
- package/src/lib/model/lead-company-suggestion.js +65 -0
- package/src/lib/model/lead-company-suggestion.js.map +1 -0
- package/src/lib/model/lead-filters.d.ts +29 -0
- package/src/lib/model/lead-filters.js +39 -0
- package/src/lib/model/lead-filters.js.map +1 -0
- package/src/lib/model/lead-surfaces.d.ts +58 -0
- package/src/lib/model/lead-surfaces.js +67 -0
- package/src/lib/model/lead-surfaces.js.map +1 -0
- package/src/lib/model/leads-csv.d.ts +40 -0
- package/src/lib/model/leads-csv.js +40 -0
- package/src/lib/model/leads-csv.js.map +1 -0
- package/src/lib/model/next-activity-api.d.ts +31 -0
- package/src/lib/model/next-activity-api.js +57 -0
- package/src/lib/model/next-activity-api.js.map +1 -0
- package/src/lib/model/next-activity.d.ts +64 -0
- package/src/lib/model/next-activity.js +47 -0
- package/src/lib/model/next-activity.js.map +1 -0
- package/src/lib/model/person-key-web.d.ts +43 -0
- package/src/lib/model/person-key-web.js +52 -0
- package/src/lib/model/person-key-web.js.map +1 -0
- package/src/lib/model/record-facts.d.ts +282 -0
- package/src/lib/model/record-facts.js +434 -0
- package/src/lib/model/record-facts.js.map +1 -0
- package/src/lib/model/task-api.d.ts +28 -0
- package/src/lib/model/task-api.js +57 -0
- package/src/lib/model/task-api.js.map +1 -0
- package/src/lib/model/task-calendar.d.ts +108 -0
- package/src/lib/model/task-calendar.js +139 -0
- package/src/lib/model/task-calendar.js.map +1 -0
- package/src/lib/model/task-routes.d.ts +177 -0
- package/src/lib/model/task-routes.js +197 -0
- package/src/lib/model/task-routes.js.map +1 -0
- package/src/lib/model/task-scope.d.ts +45 -0
- package/src/lib/model/task-scope.js +51 -0
- package/src/lib/model/task-scope.js.map +1 -0
- package/src/lib/model/task-views.d.ts +149 -0
- package/src/lib/model/task-views.js +286 -0
- package/src/lib/model/task-views.js.map +1 -0
- package/src/lib/model/tasks-csv.d.ts +28 -0
- package/src/lib/model/tasks-csv.js +28 -0
- package/src/lib/model/tasks-csv.js.map +1 -0
- package/src/lib/plugin.d.ts +32 -0
- package/src/lib/plugin.js +208 -0
- package/src/lib/plugin.js.map +1 -0
- package/src/lib/server/companies-import.d.ts +57 -0
- package/src/lib/server/companies-import.js +242 -0
- package/src/lib/server/companies-import.js.map +1 -0
- package/src/lib/server/company-delete.d.ts +59 -0
- package/src/lib/server/company-delete.js +146 -0
- package/src/lib/server/company-delete.js.map +1 -0
- package/src/lib/server/contact-email-history.d.ts +82 -0
- package/src/lib/server/contact-email-history.js +264 -0
- package/src/lib/server/contact-email-history.js.map +1 -0
- package/src/lib/server/contact-profile.d.ts +41 -0
- package/src/lib/server/contact-profile.js +45 -0
- package/src/lib/server/contact-profile.js.map +1 -0
- package/src/lib/server/contact-update.d.ts +92 -0
- package/src/lib/server/contact-update.js +512 -0
- package/src/lib/server/contact-update.js.map +1 -0
- package/src/lib/server/contacts-import.d.ts +73 -0
- package/src/lib/server/contacts-import.js +263 -0
- package/src/lib/server/contacts-import.js.map +1 -0
- package/src/lib/server/contacts-merge.d.ts +86 -0
- package/src/lib/server/contacts-merge.js +206 -0
- package/src/lib/server/contacts-merge.js.map +1 -0
- package/src/lib/server/deals-import.d.ts +76 -0
- package/src/lib/server/deals-import.js +246 -0
- package/src/lib/server/deals-import.js.map +1 -0
- package/src/lib/server/email-send.d.ts +119 -0
- package/src/lib/server/email-send.js +667 -0
- package/src/lib/server/email-send.js.map +1 -0
- package/src/lib/server/email-template-duplicate.d.ts +19 -0
- package/src/lib/server/email-template-duplicate.js +206 -0
- package/src/lib/server/email-template-duplicate.js.map +1 -0
- package/src/lib/server/erase-person.d.ts +108 -0
- package/src/lib/server/erase-person.js +309 -0
- package/src/lib/server/erase-person.js.map +1 -0
- package/src/lib/server/import-context.d.ts +85 -0
- package/src/lib/server/import-context.js +181 -0
- package/src/lib/server/import-context.js.map +1 -0
- package/src/lib/server/inbound-address.d.ts +61 -0
- package/src/lib/server/inbound-address.js +188 -0
- package/src/lib/server/inbound-address.js.map +1 -0
- package/src/lib/server/lead-convert.d.ts +78 -0
- package/src/lib/server/lead-convert.js +250 -0
- package/src/lib/server/lead-convert.js.map +1 -0
- package/src/lib/server/leads-import.d.ts +84 -0
- package/src/lib/server/leads-import.js +261 -0
- package/src/lib/server/leads-import.js.map +1 -0
- package/src/lib/server/next-activity-routes.d.ts +33 -0
- package/src/lib/server/next-activity-routes.js +110 -0
- package/src/lib/server/next-activity-routes.js.map +1 -0
- package/src/lib/server/org-activity.d.ts +61 -0
- package/src/lib/server/org-activity.js +120 -0
- package/src/lib/server/org-activity.js.map +1 -0
- package/src/lib/server/org-caller.d.ts +110 -0
- package/src/lib/server/org-caller.js +154 -0
- package/src/lib/server/org-caller.js.map +1 -0
- package/src/lib/server/recipe-routes.d.ts +75 -0
- package/src/lib/server/recipe-routes.js +442 -0
- package/src/lib/server/recipe-routes.js.map +1 -0
- package/src/lib/server/record-facts.d.ts +65 -0
- package/src/lib/server/record-facts.js +337 -0
- package/src/lib/server/record-facts.js.map +1 -0
- package/src/lib/server/record-timeline.d.ts +51 -0
- package/src/lib/server/record-timeline.js +215 -0
- package/src/lib/server/record-timeline.js.map +1 -0
- package/src/lib/server/suite-gate.d.ts +42 -0
- package/src/lib/server/suite-gate.js +87 -0
- package/src/lib/server/suite-gate.js.map +1 -0
- package/src/lib/server/task-routes.d.ts +132 -0
- package/src/lib/server/task-routes.js +699 -0
- package/src/lib/server/task-routes.js.map +1 -0
- package/src/lib/server/tasks-import.d.ts +54 -0
- package/src/lib/server/tasks-import.js +157 -0
- package/src/lib/server/tasks-import.js.map +1 -0
- package/src/lib/server-deal-stage.d.ts +88 -0
- package/src/lib/server-deal-stage.js +316 -0
- package/src/lib/server-deal-stage.js.map +1 -0
- package/src/lib/server.d.ts +149 -0
- package/src/lib/server.js +615 -0
- package/src/lib/server.js.map +1 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { type DocumentData, type Query } from 'firebase/firestore';
|
|
2
|
+
import { type DependencyList } from 'react';
|
|
3
|
+
export type AggregateReadStatus = 'loading' | 'success' | 'error';
|
|
4
|
+
/**
|
|
5
|
+
* A server aggregate as a card renders it: the value once it has arrived,
|
|
6
|
+
* and `null` until then or when the server refused.
|
|
7
|
+
*
|
|
8
|
+
* `null` and not 0 for the same reason `Figure` draws a dash for it — "the
|
|
9
|
+
* count has not arrived" and "the count is zero" are different facts and a
|
|
10
|
+
* tile that showed 0 for the first would be read as the second.
|
|
11
|
+
*/
|
|
12
|
+
export interface AggregateRead<T> {
|
|
13
|
+
value: T | null;
|
|
14
|
+
status: AggregateReadStatus;
|
|
15
|
+
}
|
|
16
|
+
export interface AggregateReadOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Remember the answer under this key for {@link AGGREGATE_READ_TTL_MS},
|
|
19
|
+
* so the same question asked again — the section reopened, a card
|
|
20
|
+
* remounted — is answered from memory rather than from the server. Omit
|
|
21
|
+
* it and every change of `deps` reads.
|
|
22
|
+
*/
|
|
23
|
+
cacheKey?: string;
|
|
24
|
+
ttlMs?: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* How long a report answer is trusted (AGL-2614).
|
|
28
|
+
*
|
|
29
|
+
* A report is a snapshot, not a listener: nothing on the page moves while
|
|
30
|
+
* it is open, and the reader who closes it and opens it again a moment
|
|
31
|
+
* later is asking the same question. Sixty seconds is long enough to cover
|
|
32
|
+
* that round trip and short enough that a deal closed over lunch is on the
|
|
33
|
+
* page after it. The Refresh control is the way to ask sooner.
|
|
34
|
+
*/
|
|
35
|
+
export declare const AGGREGATE_READ_TTL_MS = 60000;
|
|
36
|
+
/**
|
|
37
|
+
* Forgets every answer whose key starts with `prefix` — the section's
|
|
38
|
+
* Refresh, which names its own scope so a refresh of one org's reports
|
|
39
|
+
* does not throw away another's.
|
|
40
|
+
*/
|
|
41
|
+
export declare function invalidateAggregateReads(prefix: string): void;
|
|
42
|
+
/** Test seam: forget everything. */
|
|
43
|
+
export declare function resetAggregateReadCache(): void;
|
|
44
|
+
/**
|
|
45
|
+
* One server-side read, run once per change of `deps` and never per render,
|
|
46
|
+
* and — when keyed — once per {@link AGGREGATE_READ_TTL_MS} rather than once
|
|
47
|
+
* per mount.
|
|
48
|
+
*
|
|
49
|
+
* The CRM reports are built from `getCountFromServer`,
|
|
50
|
+
* `getAggregateFromServer` and bounded `getDocs` windows — a count is a
|
|
51
|
+
* count, not the length of a capped listener — and each of those is a
|
|
52
|
+
* promise rather than a subscription, so it needs the effect-with-an-active-
|
|
53
|
+
* flag the contacts list already writes for its head-count. This is that
|
|
54
|
+
* effect, once, so that five cards do not carry five copies of it and one of
|
|
55
|
+
* them forgets the flag and sets state on an unmounted card.
|
|
56
|
+
*
|
|
57
|
+
* ## Why the answers are remembered
|
|
58
|
+
*
|
|
59
|
+
* The section reads up to 3,500 documents to draw its windows — the newest
|
|
60
|
+
* thousand contacts, a thousand open deals, a thousand open tasks, five
|
|
61
|
+
* hundred closed deals of each outcome — and it read them again every time
|
|
62
|
+
* the reader arrived, including the arrival ten seconds after they left to
|
|
63
|
+
* check a deal the report named. A report is a snapshot; the second arrival
|
|
64
|
+
* is the same question. A keyed read is answered from memory while its
|
|
65
|
+
* answer is fresh, and the key names the scope and the period so a different
|
|
66
|
+
* org, a different site group or a different period is a different question.
|
|
67
|
+
* Refusals are never remembered: a dash the server drew once should not be
|
|
68
|
+
* drawn from memory for a minute after the rules change.
|
|
69
|
+
*
|
|
70
|
+
* `read` returning `null` means "not yet" — the org scope has not resolved,
|
|
71
|
+
* the tokens are not known — and leaves the read pending rather than
|
|
72
|
+
* counting against nothing. The latest `read` is always the one invoked, via
|
|
73
|
+
* a ref, so the caller can close over fresh props without listing every one
|
|
74
|
+
* of them in `deps`; what `deps` names is WHEN to read again.
|
|
75
|
+
*/
|
|
76
|
+
export declare function useAggregateRead<T>(read: () => Promise<T> | null, deps: DependencyList, options?: AggregateReadOptions): AggregateRead<T>;
|
|
77
|
+
/** A bounded window of documents as a card draws it, and whether the bound bit. */
|
|
78
|
+
export interface WindowRead<T> {
|
|
79
|
+
/** At most `ceiling` rows — the probe row is never among them. */
|
|
80
|
+
rows: T[];
|
|
81
|
+
/** The collection holds MORE than the ceiling. A fact from the probe, not a guess. */
|
|
82
|
+
truncated: boolean;
|
|
83
|
+
status: AggregateReadStatus;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* A bounded window, read once and remembered — the report's replacement
|
|
87
|
+
* for a live listener over the same query.
|
|
88
|
+
*
|
|
89
|
+
* The cards used to hold their windows open as `onSnapshot` listeners,
|
|
90
|
+
* which is the right shape for a list somebody edits and the wrong one for
|
|
91
|
+
* a report: the listener re-delivered the whole window on every mount, and
|
|
92
|
+
* a snapshot that updates under the reader while they compare it to the
|
|
93
|
+
* tile above it is a page that contradicts itself. `getDocs` answers once,
|
|
94
|
+
* `ceilingedWindow` drops the probe row the caller over-fetched by, and the
|
|
95
|
+
* answer is remembered under `cacheKey` like every other report read.
|
|
96
|
+
*
|
|
97
|
+
* `build` returning `null` means "not yet", the way `read` does above.
|
|
98
|
+
*/
|
|
99
|
+
export declare function useWindowRead<T extends DocumentData>(build: () => Query | null, ceiling: number, deps: DependencyList, options?: AggregateReadOptions): WindowRead<T>;
|
|
@@ -0,0 +1,195 @@
|
|
|
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 { ceilingedWindow } from "@aglyn/tenant-feature-instance/hooks/host-collection-queries";
|
|
19
|
+
import { getDocs } from "firebase/firestore";
|
|
20
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
21
|
+
/**
|
|
22
|
+
* How long a report answer is trusted (AGL-2614).
|
|
23
|
+
*
|
|
24
|
+
* A report is a snapshot, not a listener: nothing on the page moves while
|
|
25
|
+
* it is open, and the reader who closes it and opens it again a moment
|
|
26
|
+
* later is asking the same question. Sixty seconds is long enough to cover
|
|
27
|
+
* that round trip and short enough that a deal closed over lunch is on the
|
|
28
|
+
* page after it. The Refresh control is the way to ask sooner.
|
|
29
|
+
*/ export const AGGREGATE_READ_TTL_MS = 60000;
|
|
30
|
+
/**
|
|
31
|
+
* The answers, for the life of the page load. Module state rather than
|
|
32
|
+
* React state because the point is to outlive the card that asked: the
|
|
33
|
+
* section unmounts when the reader leaves it, and everything a component
|
|
34
|
+
* held goes with it.
|
|
35
|
+
*/ const cache = new Map();
|
|
36
|
+
/**
|
|
37
|
+
* The reads still in the air, by key (AGL-2620). Two cards that mount
|
|
38
|
+
* together and ask the same question — the pipeline card and the forecast
|
|
39
|
+
* card both want the open-deal window — would each miss the empty cache
|
|
40
|
+
* and each read a thousand documents; the second joins the first's
|
|
41
|
+
* promise instead. An entry lives only until its promise settles, so a
|
|
42
|
+
* refusal is neither remembered nor re-joined.
|
|
43
|
+
*/ const inflight = new Map();
|
|
44
|
+
/** A fresh cached answer under `key`, or undefined. Expired entries are dropped on the way. */ function readCache(key, nowMs) {
|
|
45
|
+
const entry = cache.get(key);
|
|
46
|
+
if (!entry) return undefined;
|
|
47
|
+
if (entry.expiresAt <= nowMs) {
|
|
48
|
+
cache.delete(key);
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
return entry;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Forgets every answer whose key starts with `prefix` — the section's
|
|
55
|
+
* Refresh, which names its own scope so a refresh of one org's reports
|
|
56
|
+
* does not throw away another's.
|
|
57
|
+
*/ export function invalidateAggregateReads(prefix) {
|
|
58
|
+
for (const key of [
|
|
59
|
+
...cache.keys()
|
|
60
|
+
]){
|
|
61
|
+
if (key.startsWith(prefix)) cache.delete(key);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** Test seam: forget everything. */ export function resetAggregateReadCache() {
|
|
65
|
+
cache.clear();
|
|
66
|
+
inflight.clear();
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* One server-side read, run once per change of `deps` and never per render,
|
|
70
|
+
* and — when keyed — once per {@link AGGREGATE_READ_TTL_MS} rather than once
|
|
71
|
+
* per mount.
|
|
72
|
+
*
|
|
73
|
+
* The CRM reports are built from `getCountFromServer`,
|
|
74
|
+
* `getAggregateFromServer` and bounded `getDocs` windows — a count is a
|
|
75
|
+
* count, not the length of a capped listener — and each of those is a
|
|
76
|
+
* promise rather than a subscription, so it needs the effect-with-an-active-
|
|
77
|
+
* flag the contacts list already writes for its head-count. This is that
|
|
78
|
+
* effect, once, so that five cards do not carry five copies of it and one of
|
|
79
|
+
* them forgets the flag and sets state on an unmounted card.
|
|
80
|
+
*
|
|
81
|
+
* ## Why the answers are remembered
|
|
82
|
+
*
|
|
83
|
+
* The section reads up to 3,500 documents to draw its windows — the newest
|
|
84
|
+
* thousand contacts, a thousand open deals, a thousand open tasks, five
|
|
85
|
+
* hundred closed deals of each outcome — and it read them again every time
|
|
86
|
+
* the reader arrived, including the arrival ten seconds after they left to
|
|
87
|
+
* check a deal the report named. A report is a snapshot; the second arrival
|
|
88
|
+
* is the same question. A keyed read is answered from memory while its
|
|
89
|
+
* answer is fresh, and the key names the scope and the period so a different
|
|
90
|
+
* org, a different site group or a different period is a different question.
|
|
91
|
+
* Refusals are never remembered: a dash the server drew once should not be
|
|
92
|
+
* drawn from memory for a minute after the rules change.
|
|
93
|
+
*
|
|
94
|
+
* `read` returning `null` means "not yet" — the org scope has not resolved,
|
|
95
|
+
* the tokens are not known — and leaves the read pending rather than
|
|
96
|
+
* counting against nothing. The latest `read` is always the one invoked, via
|
|
97
|
+
* a ref, so the caller can close over fresh props without listing every one
|
|
98
|
+
* of them in `deps`; what `deps` names is WHEN to read again.
|
|
99
|
+
*/ export function useAggregateRead(read, deps, options = {}) {
|
|
100
|
+
const { cacheKey, ttlMs = AGGREGATE_READ_TTL_MS } = options;
|
|
101
|
+
const [state, setState] = useState({
|
|
102
|
+
value: null,
|
|
103
|
+
status: 'loading'
|
|
104
|
+
});
|
|
105
|
+
const readRef = useRef(read);
|
|
106
|
+
readRef.current = read;
|
|
107
|
+
useEffect(()=>{
|
|
108
|
+
const remembered = cacheKey ? readCache(cacheKey, Date.now()) : undefined;
|
|
109
|
+
if (remembered) {
|
|
110
|
+
setState({
|
|
111
|
+
value: remembered.value,
|
|
112
|
+
status: 'success'
|
|
113
|
+
});
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
const joined = cacheKey ? inflight.get(cacheKey) : undefined;
|
|
117
|
+
const pending = joined != null ? joined : readRef.current();
|
|
118
|
+
setState({
|
|
119
|
+
value: null,
|
|
120
|
+
status: 'loading'
|
|
121
|
+
});
|
|
122
|
+
if (!pending) return undefined;
|
|
123
|
+
if (cacheKey && !joined) {
|
|
124
|
+
inflight.set(cacheKey, pending);
|
|
125
|
+
pending.finally(()=>{
|
|
126
|
+
if (inflight.get(cacheKey) === pending) inflight.delete(cacheKey);
|
|
127
|
+
}).catch(()=>undefined);
|
|
128
|
+
}
|
|
129
|
+
let active = true;
|
|
130
|
+
pending.then((value)=>{
|
|
131
|
+
if (cacheKey) cache.set(cacheKey, {
|
|
132
|
+
value,
|
|
133
|
+
expiresAt: Date.now() + ttlMs
|
|
134
|
+
});
|
|
135
|
+
if (active) setState({
|
|
136
|
+
value,
|
|
137
|
+
status: 'success'
|
|
138
|
+
});
|
|
139
|
+
}).catch(()=>{
|
|
140
|
+
// Denied or failed: the tile draws its dash. Nothing here substitutes
|
|
141
|
+
// a zero, because a zero is a measurement and this is the absence of
|
|
142
|
+
// one.
|
|
143
|
+
if (active) setState({
|
|
144
|
+
value: null,
|
|
145
|
+
status: 'error'
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
return ()=>{
|
|
149
|
+
active = false;
|
|
150
|
+
};
|
|
151
|
+
// `deps` is the caller's dependency list, forwarded the way
|
|
152
|
+
// `useFirestoreCollection` forwards its own; the key changes with them.
|
|
153
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
154
|
+
}, [
|
|
155
|
+
...deps,
|
|
156
|
+
cacheKey
|
|
157
|
+
]);
|
|
158
|
+
return state;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* A bounded window, read once and remembered — the report's replacement
|
|
162
|
+
* for a live listener over the same query.
|
|
163
|
+
*
|
|
164
|
+
* The cards used to hold their windows open as `onSnapshot` listeners,
|
|
165
|
+
* which is the right shape for a list somebody edits and the wrong one for
|
|
166
|
+
* a report: the listener re-delivered the whole window on every mount, and
|
|
167
|
+
* a snapshot that updates under the reader while they compare it to the
|
|
168
|
+
* tile above it is a page that contradicts itself. `getDocs` answers once,
|
|
169
|
+
* `ceilingedWindow` drops the probe row the caller over-fetched by, and the
|
|
170
|
+
* answer is remembered under `cacheKey` like every other report read.
|
|
171
|
+
*
|
|
172
|
+
* `build` returning `null` means "not yet", the way `read` does above.
|
|
173
|
+
*/ export function useWindowRead(build, ceiling, deps, options = {}) {
|
|
174
|
+
const buildRef = useRef(build);
|
|
175
|
+
buildRef.current = build;
|
|
176
|
+
const read = useAggregateRead(()=>{
|
|
177
|
+
const query = buildRef.current();
|
|
178
|
+
return query ? getDocs(query).then((snapshot)=>ceilingedWindow(snapshot.docs.map((document)=>_extends({}, document.data(), {
|
|
179
|
+
$id: document.id
|
|
180
|
+
})), ceiling)) : null;
|
|
181
|
+
}, deps, options);
|
|
182
|
+
return useMemo(()=>{
|
|
183
|
+
var _ref, _ref1;
|
|
184
|
+
var _read_value, _read_value1;
|
|
185
|
+
return {
|
|
186
|
+
rows: (_ref = (_read_value = read.value) == null ? void 0 : _read_value.rows) != null ? _ref : [],
|
|
187
|
+
truncated: (_ref1 = (_read_value1 = read.value) == null ? void 0 : _read_value1.truncated) != null ? _ref1 : false,
|
|
188
|
+
status: read.status
|
|
189
|
+
};
|
|
190
|
+
}, [
|
|
191
|
+
read
|
|
192
|
+
]);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
//# sourceMappingURL=use-aggregate-read.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/plugins/crm/src/lib/components/reports/use-aggregate-read.ts"],"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 { ceilingedWindow } from '@aglyn/tenant-feature-instance/hooks/host-collection-queries'\nimport { type DocumentData, getDocs, type Query } from 'firebase/firestore'\nimport { type DependencyList, useEffect, useMemo, useRef, useState } from 'react'\n\nexport type AggregateReadStatus = 'loading' | 'success' | 'error'\n\n/**\n * A server aggregate as a card renders it: the value once it has arrived,\n * and `null` until then or when the server refused.\n *\n * `null` and not 0 for the same reason `Figure` draws a dash for it — \"the\n * count has not arrived\" and \"the count is zero\" are different facts and a\n * tile that showed 0 for the first would be read as the second.\n */\nexport interface AggregateRead<T> {\n value: T | null\n status: AggregateReadStatus\n}\n\nexport interface AggregateReadOptions {\n /**\n * Remember the answer under this key for {@link AGGREGATE_READ_TTL_MS},\n * so the same question asked again — the section reopened, a card\n * remounted — is answered from memory rather than from the server. Omit\n * it and every change of `deps` reads.\n */\n cacheKey?: string\n ttlMs?: number\n}\n\n/**\n * How long a report answer is trusted (AGL-2614).\n *\n * A report is a snapshot, not a listener: nothing on the page moves while\n * it is open, and the reader who closes it and opens it again a moment\n * later is asking the same question. Sixty seconds is long enough to cover\n * that round trip and short enough that a deal closed over lunch is on the\n * page after it. The Refresh control is the way to ask sooner.\n */\nexport const AGGREGATE_READ_TTL_MS = 60_000\n\ninterface CacheEntry {\n value: unknown\n expiresAt: number\n}\n\n/**\n * The answers, for the life of the page load. Module state rather than\n * React state because the point is to outlive the card that asked: the\n * section unmounts when the reader leaves it, and everything a component\n * held goes with it.\n */\nconst cache = new Map<string, CacheEntry>()\n\n/**\n * The reads still in the air, by key (AGL-2620). Two cards that mount\n * together and ask the same question — the pipeline card and the forecast\n * card both want the open-deal window — would each miss the empty cache\n * and each read a thousand documents; the second joins the first's\n * promise instead. An entry lives only until its promise settles, so a\n * refusal is neither remembered nor re-joined.\n */\nconst inflight = new Map<string, Promise<unknown>>()\n\n/** A fresh cached answer under `key`, or undefined. Expired entries are dropped on the way. */\nfunction readCache(key: string, nowMs: number): CacheEntry | undefined {\n const entry = cache.get(key)\n if (!entry) return undefined\n if (entry.expiresAt <= nowMs) {\n cache.delete(key)\n return undefined\n }\n return entry\n}\n\n/**\n * Forgets every answer whose key starts with `prefix` — the section's\n * Refresh, which names its own scope so a refresh of one org's reports\n * does not throw away another's.\n */\nexport function invalidateAggregateReads(prefix: string): void {\n for (const key of [...cache.keys()]) {\n if (key.startsWith(prefix)) cache.delete(key)\n }\n}\n\n/** Test seam: forget everything. */\nexport function resetAggregateReadCache(): void {\n cache.clear()\n inflight.clear()\n}\n\n/**\n * One server-side read, run once per change of `deps` and never per render,\n * and — when keyed — once per {@link AGGREGATE_READ_TTL_MS} rather than once\n * per mount.\n *\n * The CRM reports are built from `getCountFromServer`,\n * `getAggregateFromServer` and bounded `getDocs` windows — a count is a\n * count, not the length of a capped listener — and each of those is a\n * promise rather than a subscription, so it needs the effect-with-an-active-\n * flag the contacts list already writes for its head-count. This is that\n * effect, once, so that five cards do not carry five copies of it and one of\n * them forgets the flag and sets state on an unmounted card.\n *\n * ## Why the answers are remembered\n *\n * The section reads up to 3,500 documents to draw its windows — the newest\n * thousand contacts, a thousand open deals, a thousand open tasks, five\n * hundred closed deals of each outcome — and it read them again every time\n * the reader arrived, including the arrival ten seconds after they left to\n * check a deal the report named. A report is a snapshot; the second arrival\n * is the same question. A keyed read is answered from memory while its\n * answer is fresh, and the key names the scope and the period so a different\n * org, a different site group or a different period is a different question.\n * Refusals are never remembered: a dash the server drew once should not be\n * drawn from memory for a minute after the rules change.\n *\n * `read` returning `null` means \"not yet\" — the org scope has not resolved,\n * the tokens are not known — and leaves the read pending rather than\n * counting against nothing. The latest `read` is always the one invoked, via\n * a ref, so the caller can close over fresh props without listing every one\n * of them in `deps`; what `deps` names is WHEN to read again.\n */\nexport function useAggregateRead<T>(\n read: () => Promise<T> | null,\n deps: DependencyList,\n options: AggregateReadOptions = {},\n): AggregateRead<T> {\n const { cacheKey, ttlMs = AGGREGATE_READ_TTL_MS } = options\n const [state, setState] = useState<AggregateRead<T>>({\n value: null,\n status: 'loading',\n })\n const readRef = useRef(read)\n readRef.current = read\n\n useEffect(() => {\n const remembered = cacheKey ? readCache(cacheKey, Date.now()) : undefined\n if (remembered) {\n setState({ value: remembered.value as T, status: 'success' })\n return undefined\n }\n const joined = cacheKey ? (inflight.get(cacheKey) as Promise<T> | undefined) : undefined\n const pending = joined ?? readRef.current()\n setState({ value: null, status: 'loading' })\n if (!pending) return undefined\n if (cacheKey && !joined) {\n inflight.set(cacheKey, pending)\n pending.finally(() => {\n if (inflight.get(cacheKey) === pending) inflight.delete(cacheKey)\n }).catch(() => undefined)\n }\n let active = true\n pending\n .then((value) => {\n if (cacheKey) cache.set(cacheKey, { value, expiresAt: Date.now() + ttlMs })\n if (active) setState({ value, status: 'success' })\n })\n .catch(() => {\n // Denied or failed: the tile draws its dash. Nothing here substitutes\n // a zero, because a zero is a measurement and this is the absence of\n // one.\n if (active) setState({ value: null, status: 'error' })\n })\n return () => {\n active = false\n }\n // `deps` is the caller's dependency list, forwarded the way\n // `useFirestoreCollection` forwards its own; the key changes with them.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [...deps, cacheKey])\n\n return state\n}\n\n/** A bounded window of documents as a card draws it, and whether the bound bit. */\nexport interface WindowRead<T> {\n /** At most `ceiling` rows — the probe row is never among them. */\n rows: T[]\n /** The collection holds MORE than the ceiling. A fact from the probe, not a guess. */\n truncated: boolean\n status: AggregateReadStatus\n}\n\n/**\n * A bounded window, read once and remembered — the report's replacement\n * for a live listener over the same query.\n *\n * The cards used to hold their windows open as `onSnapshot` listeners,\n * which is the right shape for a list somebody edits and the wrong one for\n * a report: the listener re-delivered the whole window on every mount, and\n * a snapshot that updates under the reader while they compare it to the\n * tile above it is a page that contradicts itself. `getDocs` answers once,\n * `ceilingedWindow` drops the probe row the caller over-fetched by, and the\n * answer is remembered under `cacheKey` like every other report read.\n *\n * `build` returning `null` means \"not yet\", the way `read` does above.\n */\nexport function useWindowRead<T extends DocumentData>(\n build: () => Query | null,\n ceiling: number,\n deps: DependencyList,\n options: AggregateReadOptions = {},\n): WindowRead<T> {\n const buildRef = useRef(build)\n buildRef.current = build\n const read = useAggregateRead<{ rows: T[]; truncated: boolean }>(\n () => {\n const query = buildRef.current()\n return query\n ? getDocs(query).then((snapshot) =>\n ceilingedWindow(\n snapshot.docs.map(\n (document) => ({ ...document.data(), $id: document.id }) as unknown as T,\n ),\n ceiling,\n ),\n )\n : null\n },\n deps,\n options,\n )\n return useMemo(\n () => ({\n rows: read.value?.rows ?? [],\n truncated: read.value?.truncated ?? false,\n status: read.status,\n }),\n [read],\n )\n}\n"],"names":["ceilingedWindow","getDocs","useEffect","useMemo","useRef","useState","AGGREGATE_READ_TTL_MS","cache","Map","inflight","readCache","key","nowMs","entry","get","undefined","expiresAt","delete","invalidateAggregateReads","prefix","keys","startsWith","resetAggregateReadCache","clear","useAggregateRead","read","deps","options","cacheKey","ttlMs","state","setState","value","status","readRef","current","remembered","Date","now","joined","pending","set","finally","catch","active","then","useWindowRead","build","ceiling","buildRef","query","snapshot","docs","map","document","data","$id","id","rows","truncated"],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type CrmReportScope } from './report-scope';
|
|
2
|
+
export interface WonLostByOwnerCardProps {
|
|
3
|
+
report: CrmReportScope;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Who closed what (AGL-2662): the period's wins and losses, by the person
|
|
7
|
+
* who owned the deal at the close.
|
|
8
|
+
*
|
|
9
|
+
* ## It costs no read
|
|
10
|
+
*
|
|
11
|
+
* The two windows are the SAME queries the Won and lost card builds, under
|
|
12
|
+
* the same cache keys — `closed:won` and `closed:lost` — so
|
|
13
|
+
* `useAggregateRead` either answers this card from the remembered window or
|
|
14
|
+
* joins the other card's in-flight promise. Two cards, one pair of reads.
|
|
15
|
+
* A key of its own here would have doubled the most expensive read on the
|
|
16
|
+
* page to ask a different question of the same rows.
|
|
17
|
+
*
|
|
18
|
+
* ## Why the rows can under-report a very busy period
|
|
19
|
+
*
|
|
20
|
+
* The window is bounded, and the bound is on the deals rather than on the
|
|
21
|
+
* owners — so a period with more than {@link CLOSED_DEAL_CEILING} closes of
|
|
22
|
+
* an outcome is tallied over the most recent of them, and the caption says
|
|
23
|
+
* so. A server-side group-by would need one aggregate per owner, and the
|
|
24
|
+
* owner list is not known before the read.
|
|
25
|
+
*/
|
|
26
|
+
export declare function WonLostByOwnerCard(props: WonLostByOwnerCardProps): import("react").JSX.Element;
|
|
27
|
+
export declare namespace WonLostByOwnerCard {
|
|
28
|
+
var displayName: string;
|
|
29
|
+
}
|
|
30
|
+
export default WonLostByOwnerCard;
|
|
@@ -0,0 +1,168 @@
|
|
|
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 { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
18
|
+
import * as Aglyn from "@aglyn/aglyn";
|
|
19
|
+
import { money } from "@aglyn/shared-ui-email-campaigns/components/report-figures";
|
|
20
|
+
import { CardDisplay } from "@aglyn/shared-ui-jsx";
|
|
21
|
+
import { percent } from "@aglyn/shared-ui-jsx/components/measured-figures.component";
|
|
22
|
+
import { Alert, Stack, Typography } from "@mui/material";
|
|
23
|
+
import { limit, orderBy, query, where } from "firebase/firestore";
|
|
24
|
+
import { useMemo } from "react";
|
|
25
|
+
import { useFirestore } from "@aglyn/tenant-feature-instance";
|
|
26
|
+
import { useOrgMemberOptions } from "../../hooks/use-org-member-options.js";
|
|
27
|
+
import { ReportBreakdown } from "./report-breakdown.js";
|
|
28
|
+
import { reportCacheKey, scopedCollection, visibleToClause } from "./report-scope.js";
|
|
29
|
+
import { useWindowRead } from "./use-aggregate-read.js";
|
|
30
|
+
/**
|
|
31
|
+
* The same ceiling the Won and lost card reads its chart under, spelled
|
|
32
|
+
* from one constant so the two cards share the read rather than each
|
|
33
|
+
* paying for its own.
|
|
34
|
+
*/ const CLOSED_DEAL_CEILING = 500;
|
|
35
|
+
/** How many owners the breakdown draws before folding the rest into one row. */ const OWNER_ROW_CEILING = 8;
|
|
36
|
+
/**
|
|
37
|
+
* Who closed what (AGL-2662): the period's wins and losses, by the person
|
|
38
|
+
* who owned the deal at the close.
|
|
39
|
+
*
|
|
40
|
+
* ## It costs no read
|
|
41
|
+
*
|
|
42
|
+
* The two windows are the SAME queries the Won and lost card builds, under
|
|
43
|
+
* the same cache keys — `closed:won` and `closed:lost` — so
|
|
44
|
+
* `useAggregateRead` either answers this card from the remembered window or
|
|
45
|
+
* joins the other card's in-flight promise. Two cards, one pair of reads.
|
|
46
|
+
* A key of its own here would have doubled the most expensive read on the
|
|
47
|
+
* page to ask a different question of the same rows.
|
|
48
|
+
*
|
|
49
|
+
* ## Why the rows can under-report a very busy period
|
|
50
|
+
*
|
|
51
|
+
* The window is bounded, and the bound is on the deals rather than on the
|
|
52
|
+
* owners — so a period with more than {@link CLOSED_DEAL_CEILING} closes of
|
|
53
|
+
* an outcome is tallied over the most recent of them, and the caption says
|
|
54
|
+
* so. A server-side group-by would need one aggregate per owner, and the
|
|
55
|
+
* owner list is not known before the read.
|
|
56
|
+
*/ export function WonLostByOwnerCard(props) {
|
|
57
|
+
const { report } = props;
|
|
58
|
+
const { scope, tokens, range } = report;
|
|
59
|
+
const firestore = useFirestore();
|
|
60
|
+
// The roster resolves a uid into the name the team reads; a uid it no
|
|
61
|
+
// longer carries — somebody who has left — is named by its own id rather
|
|
62
|
+
// than dropped, because their closed deals still happened.
|
|
63
|
+
const roster = useOrgMemberOptions(scope[1]);
|
|
64
|
+
const closedInPeriod = (status)=>query(scopedCollection(firestore, scope, 'deals'), ...visibleToClause(tokens), where('status', '==', status), where('closedAtMs', '>=', range.from), where('closedAtMs', '<', range.to), orderBy('closedAtMs', 'desc'), limit(CLOSED_DEAL_CEILING + 1));
|
|
65
|
+
const wonWindow = useWindowRead(()=>closedInPeriod('won'), CLOSED_DEAL_CEILING, [
|
|
66
|
+
firestore,
|
|
67
|
+
scope,
|
|
68
|
+
tokens,
|
|
69
|
+
range
|
|
70
|
+
], {
|
|
71
|
+
cacheKey: reportCacheKey(report, 'closed:won')
|
|
72
|
+
});
|
|
73
|
+
const lostWindow = useWindowRead(()=>closedInPeriod('lost'), CLOSED_DEAL_CEILING, [
|
|
74
|
+
firestore,
|
|
75
|
+
scope,
|
|
76
|
+
tokens,
|
|
77
|
+
range
|
|
78
|
+
], {
|
|
79
|
+
cacheKey: reportCacheKey(report, 'closed:lost')
|
|
80
|
+
});
|
|
81
|
+
const owners = useMemo(()=>Aglyn.wonLostByOwner(wonWindow.rows, lostWindow.rows), [
|
|
82
|
+
wonWindow.rows,
|
|
83
|
+
lostWindow.rows
|
|
84
|
+
]);
|
|
85
|
+
const currency = useMemo(()=>Aglyn.currencyOfDeals(wonWindow.rows).currency, [
|
|
86
|
+
wonWindow.rows
|
|
87
|
+
]);
|
|
88
|
+
const read = wonWindow.status === 'success' && lostWindow.status === 'success';
|
|
89
|
+
const failed = wonWindow.status === 'error' || lostWindow.status === 'error';
|
|
90
|
+
const rows = useMemo(()=>{
|
|
91
|
+
const named = (uid)=>{
|
|
92
|
+
var _ref;
|
|
93
|
+
var _Aglyn_findOrgMember;
|
|
94
|
+
return uid ? (_ref = (_Aglyn_findOrgMember = Aglyn.findOrgMember(roster.options, uid)) == null ? void 0 : _Aglyn_findOrgMember.label) != null ? _ref : uid : 'Unassigned';
|
|
95
|
+
};
|
|
96
|
+
const shown = owners.slice(0, OWNER_ROW_CEILING);
|
|
97
|
+
const folded = owners.slice(OWNER_ROW_CEILING);
|
|
98
|
+
const rest = folded.reduce((total, owner)=>({
|
|
99
|
+
won: total.won + owner.won,
|
|
100
|
+
lost: total.lost + owner.lost,
|
|
101
|
+
wonAmountCents: total.wonAmountCents + owner.wonAmountCents
|
|
102
|
+
}), {
|
|
103
|
+
won: 0,
|
|
104
|
+
lost: 0,
|
|
105
|
+
wonAmountCents: 0
|
|
106
|
+
});
|
|
107
|
+
return [
|
|
108
|
+
...shown.map((owner)=>({
|
|
109
|
+
key: owner.uid || 'unassigned',
|
|
110
|
+
label: named(owner.uid),
|
|
111
|
+
value: owner.won,
|
|
112
|
+
display: `${owner.won.toLocaleString()} won`,
|
|
113
|
+
note: `${owner.lost.toLocaleString()} lost · ` + `${owner.winRate === null ? 'no rate' : `${percent(owner.winRate)} win rate`} · ` + `${money(owner.wonAmountCents, currency)}`,
|
|
114
|
+
color: 'success.main'
|
|
115
|
+
})),
|
|
116
|
+
...folded.length ? [
|
|
117
|
+
{
|
|
118
|
+
key: '$rest',
|
|
119
|
+
label: `${folded.length} more`,
|
|
120
|
+
value: rest.won,
|
|
121
|
+
display: `${rest.won.toLocaleString()} won`,
|
|
122
|
+
note: `${rest.lost.toLocaleString()} lost · ${money(rest.wonAmountCents, currency)}`,
|
|
123
|
+
color: 'success.main'
|
|
124
|
+
}
|
|
125
|
+
] : []
|
|
126
|
+
];
|
|
127
|
+
}, [
|
|
128
|
+
owners,
|
|
129
|
+
roster.options,
|
|
130
|
+
currency
|
|
131
|
+
]);
|
|
132
|
+
return /*#__PURE__*/ _jsx(CardDisplay, {
|
|
133
|
+
header: 'Won and lost by owner',
|
|
134
|
+
help: Aglyn.pluginDocsHelp('crmReports', {
|
|
135
|
+
anchor: '#won-and-lost-by-owner',
|
|
136
|
+
excerpt: 'Closed deals grouped by the person who owned them at the close, ' + 'ranked by how much closed rather than by win rate, with the deals ' + 'nobody owned kept as a row of their own.'
|
|
137
|
+
}),
|
|
138
|
+
contentGutterX: true,
|
|
139
|
+
contentGutterY: true,
|
|
140
|
+
children: /*#__PURE__*/ _jsxs(Stack, {
|
|
141
|
+
spacing: 1.5,
|
|
142
|
+
children: [
|
|
143
|
+
failed ? /*#__PURE__*/ _jsx(Alert, {
|
|
144
|
+
severity: "warning",
|
|
145
|
+
children: 'The closed deals could not be read, so this is not an empty period.'
|
|
146
|
+
}) : null,
|
|
147
|
+
/*#__PURE__*/ _jsx(ReportBreakdown, {
|
|
148
|
+
rows: rows,
|
|
149
|
+
emptyText: read ? 'Nothing closed in this period.' : 'Reading…'
|
|
150
|
+
}),
|
|
151
|
+
wonWindow.truncated || lostWindow.truncated ? /*#__PURE__*/ _jsx(Typography, {
|
|
152
|
+
variant: "caption",
|
|
153
|
+
color: "text.secondary",
|
|
154
|
+
children: `Counted over the ${CLOSED_DEAL_CEILING} most recently closed of each outcome; this period held more.`
|
|
155
|
+
}) : null,
|
|
156
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
157
|
+
variant: "caption",
|
|
158
|
+
color: "text.secondary",
|
|
159
|
+
children: 'Credited to whoever owned the deal when it closed, not to whoever created it.'
|
|
160
|
+
})
|
|
161
|
+
]
|
|
162
|
+
})
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
WonLostByOwnerCard.displayName = 'WonLostByOwnerCard';
|
|
166
|
+
export default WonLostByOwnerCard;
|
|
167
|
+
|
|
168
|
+
//# sourceMappingURL=won-lost-by-owner-card.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/plugins/crm/src/lib/components/reports/won-lost-by-owner-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 { money } from '@aglyn/shared-ui-email-campaigns/components/report-figures'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { percent } from '@aglyn/shared-ui-jsx/components/measured-figures.component'\nimport { Alert, Stack, Typography } from '@mui/material'\nimport { limit, orderBy, query, where } from 'firebase/firestore'\nimport { useMemo } from 'react'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport { useOrgMemberOptions } from '../../hooks/use-org-member-options'\nimport { ReportBreakdown } from './report-breakdown'\nimport {\n type CrmReportScope,\n reportCacheKey,\n scopedCollection,\n visibleToClause,\n} from './report-scope'\nimport { useWindowRead } from './use-aggregate-read'\n\n/**\n * The same ceiling the Won and lost card reads its chart under, spelled\n * from one constant so the two cards share the read rather than each\n * paying for its own.\n */\nconst CLOSED_DEAL_CEILING = 500\n\n/** How many owners the breakdown draws before folding the rest into one row. */\nconst OWNER_ROW_CEILING = 8\n\ntype DealRow = Aglyn.CrmDeal & { $id: string }\n\nexport interface WonLostByOwnerCardProps {\n report: CrmReportScope\n}\n\n/**\n * Who closed what (AGL-2662): the period's wins and losses, by the person\n * who owned the deal at the close.\n *\n * ## It costs no read\n *\n * The two windows are the SAME queries the Won and lost card builds, under\n * the same cache keys — `closed:won` and `closed:lost` — so\n * `useAggregateRead` either answers this card from the remembered window or\n * joins the other card's in-flight promise. Two cards, one pair of reads.\n * A key of its own here would have doubled the most expensive read on the\n * page to ask a different question of the same rows.\n *\n * ## Why the rows can under-report a very busy period\n *\n * The window is bounded, and the bound is on the deals rather than on the\n * owners — so a period with more than {@link CLOSED_DEAL_CEILING} closes of\n * an outcome is tallied over the most recent of them, and the caption says\n * so. A server-side group-by would need one aggregate per owner, and the\n * owner list is not known before the read.\n */\nexport function WonLostByOwnerCard(props: WonLostByOwnerCardProps) {\n const { report } = props\n const { scope, tokens, range } = report\n const firestore = useFirestore()\n // The roster resolves a uid into the name the team reads; a uid it no\n // longer carries — somebody who has left — is named by its own id rather\n // than dropped, because their closed deals still happened.\n const roster = useOrgMemberOptions(scope[1])\n\n const closedInPeriod = (status: Aglyn.CrmDealStatus) =>\n query(\n scopedCollection(firestore, scope, 'deals'),\n ...visibleToClause(tokens),\n where('status', '==', status),\n where('closedAtMs', '>=', range.from),\n where('closedAtMs', '<', range.to),\n orderBy('closedAtMs', 'desc'),\n limit(CLOSED_DEAL_CEILING + 1),\n )\n\n const wonWindow = useWindowRead<DealRow>(\n () => closedInPeriod('won'),\n CLOSED_DEAL_CEILING,\n [firestore, scope, tokens, range],\n { cacheKey: reportCacheKey(report, 'closed:won') },\n )\n const lostWindow = useWindowRead<DealRow>(\n () => closedInPeriod('lost'),\n CLOSED_DEAL_CEILING,\n [firestore, scope, tokens, range],\n { cacheKey: reportCacheKey(report, 'closed:lost') },\n )\n\n const owners = useMemo(\n () => Aglyn.wonLostByOwner(wonWindow.rows, lostWindow.rows),\n [wonWindow.rows, lostWindow.rows],\n )\n const currency = useMemo(\n () => Aglyn.currencyOfDeals(wonWindow.rows).currency,\n [wonWindow.rows],\n )\n\n const read = wonWindow.status === 'success' && lostWindow.status === 'success'\n const failed = wonWindow.status === 'error' || lostWindow.status === 'error'\n\n const rows = useMemo(() => {\n const named = (uid: string) =>\n uid\n ? (Aglyn.findOrgMember(roster.options, uid)?.label ?? uid)\n : 'Unassigned'\n const shown = owners.slice(0, OWNER_ROW_CEILING)\n const folded = owners.slice(OWNER_ROW_CEILING)\n const rest = folded.reduce(\n (total, owner) => ({\n won: total.won + owner.won,\n lost: total.lost + owner.lost,\n wonAmountCents: total.wonAmountCents + owner.wonAmountCents,\n }),\n { won: 0, lost: 0, wonAmountCents: 0 },\n )\n return [\n ...shown.map((owner) => ({\n key: owner.uid || 'unassigned',\n label: named(owner.uid),\n value: owner.won,\n display: `${owner.won.toLocaleString()} won`,\n note:\n `${owner.lost.toLocaleString()} lost · ` +\n `${owner.winRate === null ? 'no rate' : `${percent(owner.winRate)} win rate`} · ` +\n `${money(owner.wonAmountCents, currency)}`,\n color: 'success.main',\n })),\n ...(folded.length\n ? [\n {\n key: '$rest',\n label: `${folded.length} more`,\n value: rest.won,\n display: `${rest.won.toLocaleString()} won`,\n note: `${rest.lost.toLocaleString()} lost · ${money(rest.wonAmountCents, currency)}`,\n color: 'success.main',\n },\n ]\n : []),\n ]\n }, [owners, roster.options, currency])\n\n return (\n <CardDisplay\n header={'Won and lost by owner'}\n help={Aglyn.pluginDocsHelp('crmReports', {\n anchor: '#won-and-lost-by-owner',\n excerpt:\n 'Closed deals grouped by the person who owned them at the close, ' +\n 'ranked by how much closed rather than by win rate, with the deals ' +\n 'nobody owned kept as a row of their own.',\n })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1.5}>\n {failed ? (\n <Alert severity=\"warning\">\n {'The closed deals could not be read, so this is not an empty period.'}\n </Alert>\n ) : null}\n <ReportBreakdown\n rows={rows}\n emptyText={read ? 'Nothing closed in this period.' : 'Reading…'}\n />\n {wonWindow.truncated || lostWindow.truncated ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`Counted over the ${CLOSED_DEAL_CEILING} most recently closed of each outcome; this period held more.`}\n </Typography>\n ) : null}\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Credited to whoever owned the deal when it closed, not to whoever created it.'}\n </Typography>\n </Stack>\n </CardDisplay>\n )\n}\nWonLostByOwnerCard.displayName = 'WonLostByOwnerCard'\n\nexport default WonLostByOwnerCard\n"],"names":["Aglyn","money","CardDisplay","percent","Alert","Stack","Typography","limit","orderBy","query","where","useMemo","useFirestore","useOrgMemberOptions","ReportBreakdown","reportCacheKey","scopedCollection","visibleToClause","useWindowRead","CLOSED_DEAL_CEILING","OWNER_ROW_CEILING","WonLostByOwnerCard","props","report","scope","tokens","range","firestore","roster","closedInPeriod","status","from","to","wonWindow","cacheKey","lostWindow","owners","wonLostByOwner","rows","currency","currencyOfDeals","read","failed","named","uid","findOrgMember","options","label","shown","slice","folded","rest","reduce","total","owner","won","lost","wonAmountCents","map","key","value","display","toLocaleString","note","winRate","color","length","header","help","pluginDocsHelp","anchor","excerpt","contentGutterX","contentGutterY","spacing","severity","emptyText","truncated","variant","displayName"],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ConsolePluginPageProps } from '@aglyn/aglyn';
|
|
2
|
+
/**
|
|
3
|
+
* `/crm/reports` — the CRM in aggregate (AGL-2604, AGL-2624).
|
|
4
|
+
*
|
|
5
|
+
* Nine cards, each reading its own collection: contacts over time, contacts
|
|
6
|
+
* by source and stage, which sources convert, this site's lead funnel, the
|
|
7
|
+
* open pipeline, what closed, who logged what, the forecast by close month
|
|
8
|
+
* (AGL-2620, over the pipeline card's own window), and the task load. This
|
|
9
|
+
* section resolves what they share and hands it down — see `CrmReportScope`
|
|
10
|
+
* — so that every card counts the same reader's records over the same
|
|
11
|
+
* clock. The lead funnel is the one card handed the site as well: leads live
|
|
12
|
+
* under the host, not the org.
|
|
13
|
+
*
|
|
14
|
+
* ## The period is a control, and the reads follow it
|
|
15
|
+
*
|
|
16
|
+
* Nothing on this page reads a year on mount. The picker names the period,
|
|
17
|
+
* the period sizes every read (one count per week of it, the deals closed
|
|
18
|
+
* within it), and the default is thirty days. The three cards that are
|
|
19
|
+
* stocks rather than flows — the open pipeline, its forecast, the open
|
|
20
|
+
* tasks — ignore the period because "what is open" has no period.
|
|
21
|
+
*
|
|
22
|
+
* ## Scoped like the list
|
|
23
|
+
*
|
|
24
|
+
* Every query carries `visibleTo array-contains-any` over the group's
|
|
25
|
+
* tokens, the predicate the rules evaluate, so a report can never count a
|
|
26
|
+
* record its reader could not open — and the total here is the reader's
|
|
27
|
+
* total, not the org's billable audience, which is the head-count the
|
|
28
|
+
* contacts list keeps for the quota.
|
|
29
|
+
*
|
|
30
|
+
* ## Read once a minute, not once an arrival
|
|
31
|
+
*
|
|
32
|
+
* Every read on this page is keyed by the scope and the period and kept
|
|
33
|
+
* for `AGGREGATE_READ_TTL_MS` — see `useAggregateRead` — so leaving to open
|
|
34
|
+
* a deal the report named and coming straight back costs nothing. Refresh
|
|
35
|
+
* forgets this reader's answers for this org and anchors the period again,
|
|
36
|
+
* which is what makes every card ask afresh.
|
|
37
|
+
*/
|
|
38
|
+
export declare function ContactsReportsSection(props: ConsolePluginPageProps): import("react").JSX.Element;
|
|
39
|
+
export declare namespace ContactsReportsSection {
|
|
40
|
+
var displayName: string;
|
|
41
|
+
}
|
|
42
|
+
export default ContactsReportsSection;
|