@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,84 @@
|
|
|
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 { CRM_COLLECTIONS, crmViewIsListed, normalizeCrmViewState } from "@aglyn/aglyn";
|
|
19
|
+
import { useFirestore, useFirestoreCollection } from "@aglyn/tenant-feature-instance";
|
|
20
|
+
import { collection, limit, orderBy, query, where } from "firebase/firestore";
|
|
21
|
+
import { useMemo } from "react";
|
|
22
|
+
import { crmScopeListable, crmVisibleToClause } from "./use-crm-scope.js";
|
|
23
|
+
/**
|
|
24
|
+
* The menu's window.
|
|
25
|
+
*
|
|
26
|
+
* A LOOKUP, not a list: nothing pages this, a reader opens a menu rather
|
|
27
|
+
* than walking it, and a hundred named views for one section of one org is
|
|
28
|
+
* past what anybody scrolls. Bounded so the listen cannot grow with the
|
|
29
|
+
* org; a view past the window is still reachable by its address, which
|
|
30
|
+
* resolves through the same rows and so reads as "not listed" rather than
|
|
31
|
+
* as missing — see the controller.
|
|
32
|
+
*/ export const CRM_VIEWS_LIMIT = 100;
|
|
33
|
+
/**
|
|
34
|
+
* The saved views of one section, mine and shared (AGL-2617).
|
|
35
|
+
*
|
|
36
|
+
* ONE listener, filtered on the reader's own tokens like every CRM read,
|
|
37
|
+
* narrowed to the section and ordered by name — the `(visibleTo, section,
|
|
38
|
+
* name)` index. What the rules admit is wider than what the menu shows: a
|
|
39
|
+
* colleague's private view is readable under the contacts predicate and
|
|
40
|
+
* hidden here by `crmViewIsListed`, because the privacy of a working
|
|
41
|
+
* arrangement is a courtesy between colleagues rather than a disclosure
|
|
42
|
+
* boundary, and a rule per reader would need a query per reader.
|
|
43
|
+
*
|
|
44
|
+
* `orderBy('name')` also FILTERS: a view written without a name would be
|
|
45
|
+
* missing from its own menu. The controller refuses an empty name, which is
|
|
46
|
+
* the only writer.
|
|
47
|
+
*/ export function useCrmViews(options) {
|
|
48
|
+
const { scope, visibleTo, section, uid } = options;
|
|
49
|
+
const firestore = useFirestore();
|
|
50
|
+
const { data, status } = useFirestoreCollection(()=>scope && crmScopeListable(visibleTo) ? query(collection(firestore, scope[0], scope[1], CRM_COLLECTIONS.views), ...crmVisibleToClause(visibleTo), where('section', '==', section), orderBy('name'), limit(CRM_VIEWS_LIMIT)) : null, [
|
|
51
|
+
firestore,
|
|
52
|
+
scope,
|
|
53
|
+
visibleTo,
|
|
54
|
+
section
|
|
55
|
+
], {
|
|
56
|
+
idField: '$id'
|
|
57
|
+
});
|
|
58
|
+
const views = useMemo(()=>(data != null ? data : []).map((row)=>{
|
|
59
|
+
var _row_name, _row_ownerUid, _row_createdByUid, _row_hostId;
|
|
60
|
+
return _extends({
|
|
61
|
+
$id: String(row['$id']),
|
|
62
|
+
section,
|
|
63
|
+
name: String((_row_name = row['name']) != null ? _row_name : ''),
|
|
64
|
+
ownerUid: String((_row_ownerUid = row['ownerUid']) != null ? _row_ownerUid : ''),
|
|
65
|
+
createdByUid: String((_row_createdByUid = row['createdByUid']) != null ? _row_createdByUid : ''),
|
|
66
|
+
shared: row['shared'] === true,
|
|
67
|
+
hostId: String((_row_hostId = row['hostId']) != null ? _row_hostId : ''),
|
|
68
|
+
visibleTo: Array.isArray(row['visibleTo']) ? row['visibleTo'] : [],
|
|
69
|
+
createdAt: row['createdAt'],
|
|
70
|
+
updatedAt: row['updatedAt']
|
|
71
|
+
}, normalizeCrmViewState(row));
|
|
72
|
+
}).filter((view)=>view.name && crmViewIsListed(view, uid)), [
|
|
73
|
+
data,
|
|
74
|
+
section,
|
|
75
|
+
uid
|
|
76
|
+
]);
|
|
77
|
+
return {
|
|
78
|
+
views,
|
|
79
|
+
ready: Boolean(scope) && status !== 'loading'
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export default useCrmViews;
|
|
83
|
+
|
|
84
|
+
//# sourceMappingURL=use-crm-views.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/hooks/use-crm-views.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 {\n CRM_COLLECTIONS,\n type CrmSavedView,\n type CrmViewSection,\n crmViewIsListed,\n normalizeCrmViewState,\n} from '@aglyn/aglyn'\nimport { useFirestore, useFirestoreCollection } from '@aglyn/tenant-feature-instance'\nimport { collection, limit, orderBy, query, where } from 'firebase/firestore'\nimport { useMemo } from 'react'\nimport { crmScopeListable, crmVisibleToClause } from './use-crm-scope'\n\n/** One saved view as the menu lists it: the document, held to shape, with its id. */\nexport interface CrmSavedViewRow extends CrmSavedView {\n $id: string\n}\n\n/**\n * The menu's window.\n *\n * A LOOKUP, not a list: nothing pages this, a reader opens a menu rather\n * than walking it, and a hundred named views for one section of one org is\n * past what anybody scrolls. Bounded so the listen cannot grow with the\n * org; a view past the window is still reachable by its address, which\n * resolves through the same rows and so reads as \"not listed\" rather than\n * as missing — see the controller.\n */\nexport const CRM_VIEWS_LIMIT = 100\n\n/**\n * The saved views of one section, mine and shared (AGL-2617).\n *\n * ONE listener, filtered on the reader's own tokens like every CRM read,\n * narrowed to the section and ordered by name — the `(visibleTo, section,\n * name)` index. What the rules admit is wider than what the menu shows: a\n * colleague's private view is readable under the contacts predicate and\n * hidden here by `crmViewIsListed`, because the privacy of a working\n * arrangement is a courtesy between colleagues rather than a disclosure\n * boundary, and a rule per reader would need a query per reader.\n *\n * `orderBy('name')` also FILTERS: a view written without a name would be\n * missing from its own menu. The controller refuses an empty name, which is\n * the only writer.\n */\nexport function useCrmViews(options: {\n scope: readonly ['orgs', string] | null\n /** The reader's tokens, or `null` at the organization level — no clause (AGL-2630). */\n visibleTo: readonly string[] | null\n section: CrmViewSection\n uid: string | null | undefined\n}): { views: CrmSavedViewRow[]; ready: boolean } {\n const { scope, visibleTo, section, uid } = options\n const firestore = useFirestore()\n const { data, status } = useFirestoreCollection<Record<string, unknown>>(\n () =>\n scope && crmScopeListable(visibleTo)\n ? query(\n collection(firestore, scope[0], scope[1], CRM_COLLECTIONS.views),\n ...crmVisibleToClause(visibleTo),\n where('section', '==', section),\n orderBy('name'),\n limit(CRM_VIEWS_LIMIT),\n )\n : null,\n [firestore, scope, visibleTo, section],\n { idField: '$id' },\n )\n const views = useMemo<CrmSavedViewRow[]>(\n () =>\n (data ?? [])\n .map((row) => ({\n $id: String(row['$id']),\n section,\n name: String(row['name'] ?? ''),\n ownerUid: String(row['ownerUid'] ?? ''),\n createdByUid: String(row['createdByUid'] ?? ''),\n shared: row['shared'] === true,\n hostId: String(row['hostId'] ?? ''),\n visibleTo: Array.isArray(row['visibleTo'])\n ? (row['visibleTo'] as string[])\n : [],\n createdAt: row['createdAt'],\n updatedAt: row['updatedAt'],\n ...normalizeCrmViewState(row),\n }))\n .filter((view) => view.name && crmViewIsListed(view, uid)),\n [data, section, uid],\n )\n return { views, ready: Boolean(scope) && status !== 'loading' }\n}\n\nexport default useCrmViews\n"],"names":["CRM_COLLECTIONS","crmViewIsListed","normalizeCrmViewState","useFirestore","useFirestoreCollection","collection","limit","orderBy","query","where","useMemo","crmScopeListable","crmVisibleToClause","CRM_VIEWS_LIMIT","useCrmViews","options","scope","visibleTo","section","uid","firestore","data","status","views","idField","map","row","$id","String","name","ownerUid","createdByUid","shared","hostId","Array","isArray","createdAt","updatedAt","filter","view","ready","Boolean"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SACEA,eAAe,EAGfC,eAAe,EACfC,qBAAqB,QAChB,eAAc;AACrB,SAASC,YAAY,EAAEC,sBAAsB,QAAQ,iCAAgC;AACrF,SAASC,UAAU,EAAEC,KAAK,EAAEC,OAAO,EAAEC,KAAK,EAAEC,KAAK,QAAQ,qBAAoB;AAC7E,SAASC,OAAO,QAAQ,QAAO;AAC/B,SAASC,gBAAgB,EAAEC,kBAAkB,QAAQ,qBAAiB;AAOtE;;;;;;;;;CASC,GACD,OAAO,MAAMC,kBAAkB,IAAG;AAElC;;;;;;;;;;;;;;CAcC,GACD,OAAO,SAASC,YAAYC,OAM3B;IACC,MAAM,EAAEC,KAAK,EAAEC,SAAS,EAAEC,OAAO,EAAEC,GAAG,EAAE,GAAGJ;IAC3C,MAAMK,YAAYjB;IAClB,MAAM,EAAEkB,IAAI,EAAEC,MAAM,EAAE,GAAGlB,uBACvB,IACEY,SAASL,iBAAiBM,aACtBT,MACEH,WAAWe,WAAWJ,KAAK,CAAC,EAAE,EAAEA,KAAK,CAAC,EAAE,EAAEhB,gBAAgBuB,KAAK,MAC5DX,mBAAmBK,YACtBR,MAAM,WAAW,MAAMS,UACvBX,QAAQ,SACRD,MAAMO,oBAER,MACN;QAACO;QAAWJ;QAAOC;QAAWC;KAAQ,EACtC;QAAEM,SAAS;IAAM;IAEnB,MAAMD,QAAQb,QACZ,IACE,CAACW,eAAAA,OAAQ,EAAE,EACRI,GAAG,CAAC,CAACC;gBAGSA,WACIA,eACIA,mBAENA;mBAPF;gBACbC,KAAKC,OAAOF,GAAG,CAAC,MAAM;gBACtBR;gBACAW,MAAMD,QAAOF,YAAAA,GAAG,CAAC,OAAO,YAAXA,YAAe;gBAC5BI,UAAUF,QAAOF,gBAAAA,GAAG,CAAC,WAAW,YAAfA,gBAAmB;gBACpCK,cAAcH,QAAOF,oBAAAA,GAAG,CAAC,eAAe,YAAnBA,oBAAuB;gBAC5CM,QAAQN,GAAG,CAAC,SAAS,KAAK;gBAC1BO,QAAQL,QAAOF,cAAAA,GAAG,CAAC,SAAS,YAAbA,cAAiB;gBAChCT,WAAWiB,MAAMC,OAAO,CAACT,GAAG,CAAC,YAAY,IACpCA,GAAG,CAAC,YAAY,GACjB,EAAE;gBACNU,WAAWV,GAAG,CAAC,YAAY;gBAC3BW,WAAWX,GAAG,CAAC,YAAY;eACxBxB,sBAAsBwB;WAE1BY,MAAM,CAAC,CAACC,OAASA,KAAKV,IAAI,IAAI5B,gBAAgBsC,MAAMpB,OACzD;QAACE;QAAMH;QAASC;KAAI;IAEtB,OAAO;QAAEI;QAAOiB,OAAOC,QAAQzB,UAAUM,WAAW;IAAU;AAChE;AAEA,eAAeR,YAAW"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/** What `POST /api/crm/deal-stage` accepts, minus the `hostId` this hook adds. */
|
|
2
|
+
export type DealStageRequest = {
|
|
3
|
+
dealId: string;
|
|
4
|
+
stageId: string;
|
|
5
|
+
} | {
|
|
6
|
+
dealId: string;
|
|
7
|
+
status: 'won';
|
|
8
|
+
} | {
|
|
9
|
+
dealId: string;
|
|
10
|
+
status: 'lost';
|
|
11
|
+
lostReason?: string;
|
|
12
|
+
};
|
|
13
|
+
/** What the route answers on success. */
|
|
14
|
+
export interface DealStageResponse {
|
|
15
|
+
ok: true;
|
|
16
|
+
dealId: string;
|
|
17
|
+
stageId: string;
|
|
18
|
+
status: 'open' | 'won' | 'lost';
|
|
19
|
+
previousStageId: string;
|
|
20
|
+
/** The host event that was emitted, or `null` for a move to the same stage. */
|
|
21
|
+
event: 'dealStageChanged' | 'dealWon' | 'dealLost' | null;
|
|
22
|
+
/**
|
|
23
|
+
* What a win did to the deal's contact (AGL-2641): the lifecycle stage
|
|
24
|
+
* the person now holds and whether this call raised it to `customer`.
|
|
25
|
+
* `null` on anything but a win, and on a deal that names no contact.
|
|
26
|
+
*/
|
|
27
|
+
customer: {
|
|
28
|
+
contactId: string;
|
|
29
|
+
lifecycleStage: string;
|
|
30
|
+
advanced: boolean;
|
|
31
|
+
} | null;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The ONE door a stage change goes through (AGL-2598).
|
|
35
|
+
*
|
|
36
|
+
* A deal's stage, its won and its lost are not written by the browser even
|
|
37
|
+
* though the rules would let them be: the console can create and edit a deal
|
|
38
|
+
* client-direct, but a move is the moment an automation wants to hear about
|
|
39
|
+
* — "notify the owner when a deal is won", "file a task when one goes to
|
|
40
|
+
* negotiation" — and a workflow can only hear an event the server emits.
|
|
41
|
+
* Every surface that moves a deal (the board's drag, a card's menu, the
|
|
42
|
+
* detail page's stepper and its Won and Lost buttons) calls this, so there
|
|
43
|
+
* is no stage change that fires no event.
|
|
44
|
+
*
|
|
45
|
+
* Errors are thrown with the route's own message, which is written for the
|
|
46
|
+
* person reading it, so a caller shows `error.message` and nothing else.
|
|
47
|
+
*/
|
|
48
|
+
/**
|
|
49
|
+
* What a call names: the deal, and — at the organization level (AGL-2630),
|
|
50
|
+
* where the surface has no site of its own — the site the deal was made on,
|
|
51
|
+
* which is the site the route emits the stage event for. Under a site the
|
|
52
|
+
* mounted site wins, as it always has.
|
|
53
|
+
*/
|
|
54
|
+
export interface DealStageRef {
|
|
55
|
+
$id: string;
|
|
56
|
+
hostId?: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Beneath the org hub's mount the body names the ORG (AGL-2634), and the
|
|
60
|
+
* route runs its org variant: authorized by the org rather than by a role
|
|
61
|
+
* on the deal's site, so a deal no site captured moves too — the site
|
|
62
|
+
* beside it, when the deal has one, is where the event goes.
|
|
63
|
+
*/
|
|
64
|
+
export declare function useDealStageApi(hostId: string | null): {
|
|
65
|
+
moveToStage: (deal: DealStageRef, stageId: string) => Promise<DealStageResponse>;
|
|
66
|
+
markWon: (deal: DealStageRef) => Promise<DealStageResponse>;
|
|
67
|
+
markLost: (deal: DealStageRef, lostReason?: string) => Promise<DealStageResponse>;
|
|
68
|
+
};
|
|
69
|
+
export type DealStageApi = ReturnType<typeof useDealStageApi>;
|
|
@@ -0,0 +1,78 @@
|
|
|
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 { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
|
|
19
|
+
import { useUser } from "@aglyn/tenant-feature-instance";
|
|
20
|
+
import { useCallback, useMemo } from "react";
|
|
21
|
+
import { useCrmOrgMount } from "./use-crm-org-mount.js";
|
|
22
|
+
/**
|
|
23
|
+
* Beneath the org hub's mount the body names the ORG (AGL-2634), and the
|
|
24
|
+
* route runs its org variant: authorized by the org rather than by a role
|
|
25
|
+
* on the deal's site, so a deal no site captured moves too — the site
|
|
26
|
+
* beside it, when the deal has one, is where the event goes.
|
|
27
|
+
*/ export function useDealStageApi(hostId) {
|
|
28
|
+
var _ref;
|
|
29
|
+
var _useCrmOrgMount;
|
|
30
|
+
const { data: user } = useUser();
|
|
31
|
+
const orgId = (_ref = (_useCrmOrgMount = useCrmOrgMount()) == null ? void 0 : _useCrmOrgMount.orgId) != null ? _ref : null;
|
|
32
|
+
const call = useCallback(async (deal, body)=>{
|
|
33
|
+
var _ref;
|
|
34
|
+
const site = (_ref = hostId != null ? hostId : deal.hostId) != null ? _ref : null;
|
|
35
|
+
if (!site && !orgId) {
|
|
36
|
+
throw new Error('This deal names no site, so its stage cannot be moved.');
|
|
37
|
+
}
|
|
38
|
+
const response = await authorizedFetch(user, '/api/crm/deal-stage', {
|
|
39
|
+
method: 'POST',
|
|
40
|
+
headers: {
|
|
41
|
+
'Content-Type': 'application/json'
|
|
42
|
+
},
|
|
43
|
+
body: JSON.stringify(_extends({
|
|
44
|
+
hostId: site
|
|
45
|
+
}, orgId ? {
|
|
46
|
+
orgId
|
|
47
|
+
} : {}, body))
|
|
48
|
+
});
|
|
49
|
+
const payload = await response.json().catch(()=>({}));
|
|
50
|
+
if (!response.ok || !('ok' in payload)) {
|
|
51
|
+
throw new Error(payload.error || 'The deal could not be moved. Try again.');
|
|
52
|
+
}
|
|
53
|
+
return payload;
|
|
54
|
+
}, [
|
|
55
|
+
user,
|
|
56
|
+
hostId,
|
|
57
|
+
orgId
|
|
58
|
+
]);
|
|
59
|
+
return useMemo(()=>({
|
|
60
|
+
moveToStage: (deal, stageId)=>call(deal, {
|
|
61
|
+
dealId: deal.$id,
|
|
62
|
+
stageId
|
|
63
|
+
}),
|
|
64
|
+
markWon: (deal)=>call(deal, {
|
|
65
|
+
dealId: deal.$id,
|
|
66
|
+
status: 'won'
|
|
67
|
+
}),
|
|
68
|
+
markLost: (deal, lostReason)=>call(deal, {
|
|
69
|
+
dealId: deal.$id,
|
|
70
|
+
status: 'lost',
|
|
71
|
+
lostReason
|
|
72
|
+
})
|
|
73
|
+
}), [
|
|
74
|
+
call
|
|
75
|
+
]);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
//# sourceMappingURL=use-deal-stage-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/hooks/use-deal-stage-api.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 { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport { useCallback, useMemo } from 'react'\nimport { useCrmOrgMount } from './use-crm-org-mount'\n\n/** What `POST /api/crm/deal-stage` accepts, minus the `hostId` this hook adds. */\nexport type DealStageRequest =\n | { dealId: string; stageId: string }\n | { dealId: string; status: 'won' }\n | { dealId: string; status: 'lost'; lostReason?: string }\n\n/** What the route answers on success. */\nexport interface DealStageResponse {\n ok: true\n dealId: string\n stageId: string\n status: 'open' | 'won' | 'lost'\n previousStageId: string\n /** The host event that was emitted, or `null` for a move to the same stage. */\n event: 'dealStageChanged' | 'dealWon' | 'dealLost' | null\n /**\n * What a win did to the deal's contact (AGL-2641): the lifecycle stage\n * the person now holds and whether this call raised it to `customer`.\n * `null` on anything but a win, and on a deal that names no contact.\n */\n customer: { contactId: string; lifecycleStage: string; advanced: boolean } | null\n}\n\n/**\n * The ONE door a stage change goes through (AGL-2598).\n *\n * A deal's stage, its won and its lost are not written by the browser even\n * though the rules would let them be: the console can create and edit a deal\n * client-direct, but a move is the moment an automation wants to hear about\n * — \"notify the owner when a deal is won\", \"file a task when one goes to\n * negotiation\" — and a workflow can only hear an event the server emits.\n * Every surface that moves a deal (the board's drag, a card's menu, the\n * detail page's stepper and its Won and Lost buttons) calls this, so there\n * is no stage change that fires no event.\n *\n * Errors are thrown with the route's own message, which is written for the\n * person reading it, so a caller shows `error.message` and nothing else.\n */\n/**\n * What a call names: the deal, and — at the organization level (AGL-2630),\n * where the surface has no site of its own — the site the deal was made on,\n * which is the site the route emits the stage event for. Under a site the\n * mounted site wins, as it always has.\n */\nexport interface DealStageRef {\n $id: string\n hostId?: string\n}\n\n/**\n * Beneath the org hub's mount the body names the ORG (AGL-2634), and the\n * route runs its org variant: authorized by the org rather than by a role\n * on the deal's site, so a deal no site captured moves too — the site\n * beside it, when the deal has one, is where the event goes.\n */\nexport function useDealStageApi(hostId: string | null) {\n const { data: user } = useUser()\n const orgId = useCrmOrgMount()?.orgId ?? null\n\n const call = useCallback(\n async (\n deal: DealStageRef,\n body: DealStageRequest,\n ): Promise<DealStageResponse> => {\n const site = hostId ?? deal.hostId ?? null\n if (!site && !orgId) {\n throw new Error('This deal names no site, so its stage cannot be moved.')\n }\n const response = await authorizedFetch(user, '/api/crm/deal-stage', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n hostId: site,\n ...(orgId ? { orgId } : {}),\n ...body,\n }),\n })\n const payload = (await response.json().catch(() => ({}))) as\n | DealStageResponse\n | { error?: string }\n if (!response.ok || !('ok' in payload)) {\n throw new Error(\n (payload as { error?: string }).error ||\n 'The deal could not be moved. Try again.',\n )\n }\n return payload\n },\n [user, hostId, orgId],\n )\n\n return useMemo(\n () => ({\n moveToStage: (deal: DealStageRef, stageId: string) =>\n call(deal, { dealId: deal.$id, stageId }),\n markWon: (deal: DealStageRef) => call(deal, { dealId: deal.$id, status: 'won' }),\n markLost: (deal: DealStageRef, lostReason?: string) =>\n call(deal, { dealId: deal.$id, status: 'lost', lostReason }),\n }),\n [call],\n )\n}\n\nexport type DealStageApi = ReturnType<typeof useDealStageApi>\n"],"names":["authorizedFetch","useUser","useCallback","useMemo","useCrmOrgMount","useDealStageApi","hostId","data","user","orgId","call","deal","body","site","Error","response","method","headers","JSON","stringify","payload","json","catch","ok","error","moveToStage","stageId","dealId","$id","markWon","status","markLost","lostReason"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SAASA,eAAe,QAAQ,2CAA0C;AAC1E,SAASC,OAAO,QAAQ,iCAAgC;AACxD,SAASC,WAAW,EAAEC,OAAO,QAAQ,QAAO;AAC5C,SAASC,cAAc,QAAQ,yBAAqB;AAmDpD;;;;;CAKC,GACD,OAAO,SAASC,gBAAgBC,MAAqB;;QAErCF;IADd,MAAM,EAAEG,MAAMC,IAAI,EAAE,GAAGP;IACvB,MAAMQ,iBAAQL,kBAAAA,qCAAAA,gBAAkBK,KAAK,mBAAI;IAEzC,MAAMC,OAAOR,YACX,OACES,MACAC;YAEaN;QAAb,MAAMO,QAAOP,OAAAA,iBAAAA,SAAUK,KAAKL,MAAM,YAArBA,OAAyB;QACtC,IAAI,CAACO,QAAQ,CAACJ,OAAO;YACnB,MAAM,IAAIK,MAAM;QAClB;QACA,MAAMC,WAAW,MAAMf,gBAAgBQ,MAAM,uBAAuB;YAClEQ,QAAQ;YACRC,SAAS;gBAAE,gBAAgB;YAAmB;YAC9CL,MAAMM,KAAKC,SAAS,CAAC;gBACnBb,QAAQO;eACJJ,QAAQ;gBAAEA;YAAM,IAAI,CAAC,GACtBG;QAEP;QACA,MAAMQ,UAAW,MAAML,SAASM,IAAI,GAAGC,KAAK,CAAC,IAAO,CAAA,CAAC,CAAA;QAGrD,IAAI,CAACP,SAASQ,EAAE,IAAI,CAAE,CAAA,QAAQH,OAAM,GAAI;YACtC,MAAM,IAAIN,MACR,AAACM,QAA+BI,KAAK,IACnC;QAEN;QACA,OAAOJ;IACT,GACA;QAACZ;QAAMF;QAAQG;KAAM;IAGvB,OAAON,QACL,IAAO,CAAA;YACLsB,aAAa,CAACd,MAAoBe,UAChChB,KAAKC,MAAM;oBAAEgB,QAAQhB,KAAKiB,GAAG;oBAAEF;gBAAQ;YACzCG,SAAS,CAAClB,OAAuBD,KAAKC,MAAM;oBAAEgB,QAAQhB,KAAKiB,GAAG;oBAAEE,QAAQ;gBAAM;YAC9EC,UAAU,CAACpB,MAAoBqB,aAC7BtB,KAAKC,MAAM;oBAAEgB,QAAQhB,KAAKiB,GAAG;oBAAEE,QAAQ;oBAAQE;gBAAW;QAC9D,CAAA,GACA;QAACtB;KAAK;AAEV"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type CrmDealStatus } from '@aglyn/aglyn';
|
|
2
|
+
import type { DealDoc } from '../model/deal-board-model';
|
|
3
|
+
/**
|
|
4
|
+
* The most open cards one pipeline draws. A board past this is a board
|
|
5
|
+
* nobody reads column by column; the table is paged and reaches the rest.
|
|
6
|
+
*/
|
|
7
|
+
export declare const BOARD_OPEN_LIMIT = 500;
|
|
8
|
+
/** Closed deals shown per closed column when it is expanded. */
|
|
9
|
+
export declare const BOARD_CLOSED_LIMIT = 50;
|
|
10
|
+
/** Deals listed on a contact's or a company's page. */
|
|
11
|
+
export declare const LINKED_DEALS_LIMIT = 25;
|
|
12
|
+
/**
|
|
13
|
+
* The deals of one status in one pipeline, newest activity first (AGL-2598).
|
|
14
|
+
*
|
|
15
|
+
* Every query here carries the viewer's scope tokens and an `orderBy`, and
|
|
16
|
+
* each shape sits on an index the foundation declared:
|
|
17
|
+
* `(visibleTo, pipelineId, status, updatedAt DESC)`. Passing `null` for the
|
|
18
|
+
* pipeline or an EMPTY token list issues nothing, which is how a column
|
|
19
|
+
* stays quiet until the pipeline has loaded and a collapsed Won column
|
|
20
|
+
* costs no read. A `null` token list is the ORGANIZATION level (AGL-2630):
|
|
21
|
+
* the clause is dropped and the query rides the `(pipelineId, status,
|
|
22
|
+
* updatedAt DESC)` twin of that index.
|
|
23
|
+
*/
|
|
24
|
+
export declare function useDealsByStatus(orgId: string | null, readTokens: readonly string[] | null, pipelineId: string | null, status: CrmDealStatus, max: number): import("@aglyn/tenant-feature-instance").UseFirestoreCollectionResult<DealDoc>;
|
|
25
|
+
/**
|
|
26
|
+
* Every deal the viewer may see, paged, optionally narrowed to one status
|
|
27
|
+
* and to one pipeline — the table's read. Four shapes, four indexes:
|
|
28
|
+
* `(visibleTo, updatedAt DESC)`, `(visibleTo, status, updatedAt DESC)`,
|
|
29
|
+
* `(visibleTo, pipelineId, updatedAt DESC)` and
|
|
30
|
+
* `(visibleTo, pipelineId, status, updatedAt DESC)`. At the organization
|
|
31
|
+
* level (AGL-2630) the scope clause is dropped and the same shapes ride
|
|
32
|
+
* their twins without `visibleTo` — `(status, updatedAt DESC)`,
|
|
33
|
+
* `(pipelineId, updatedAt DESC)`, `(pipelineId, status, updatedAt DESC)`;
|
|
34
|
+
* the first shape orders on one field and needs none.
|
|
35
|
+
*/
|
|
36
|
+
export declare function usePagedDeals(orgId: string | null, readTokens: readonly string[] | null, status: CrmDealStatus | 'all', pipelineId?: string | null): import("@aglyn/tenant-feature-instance").UsePagedCollectionResult<DealDoc>;
|
|
37
|
+
/**
|
|
38
|
+
* The deals that name one contact or one company, for the card on that
|
|
39
|
+
* record's page. `(visibleTo, contactId|companyId, updatedAt DESC)`.
|
|
40
|
+
*/
|
|
41
|
+
export declare function useLinkedDeals(orgId: string | null, readTokens: readonly string[] | null, link: {
|
|
42
|
+
contactId: string;
|
|
43
|
+
} | {
|
|
44
|
+
companyId: string;
|
|
45
|
+
}): import("@aglyn/tenant-feature-instance").UseFirestoreCollectionResult<DealDoc>;
|
|
46
|
+
/** One deal, live. `undefined` while loading, and after a delete. */
|
|
47
|
+
export declare function useDeal(orgId: string | null, dealId: string): import("@aglyn/tenant-feature-instance").UseFirestoreDocResult<DealDoc>;
|
|
@@ -0,0 +1,105 @@
|
|
|
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 { CRM_COLLECTIONS } from "@aglyn/aglyn";
|
|
18
|
+
import { useFirestore, useFirestoreCollection, useFirestoreDoc, usePagedCollection } from "@aglyn/tenant-feature-instance";
|
|
19
|
+
import { collection, doc, limit, orderBy, query, where } from "firebase/firestore";
|
|
20
|
+
import { crmScopeListable, crmVisibleToClause } from "./use-crm-scope.js";
|
|
21
|
+
/**
|
|
22
|
+
* The most open cards one pipeline draws. A board past this is a board
|
|
23
|
+
* nobody reads column by column; the table is paged and reaches the rest.
|
|
24
|
+
*/ export const BOARD_OPEN_LIMIT = 500;
|
|
25
|
+
/** Closed deals shown per closed column when it is expanded. */ export const BOARD_CLOSED_LIMIT = 50;
|
|
26
|
+
/** Deals listed on a contact's or a company's page. */ export const LINKED_DEALS_LIMIT = 25;
|
|
27
|
+
/**
|
|
28
|
+
* The deals of one status in one pipeline, newest activity first (AGL-2598).
|
|
29
|
+
*
|
|
30
|
+
* Every query here carries the viewer's scope tokens and an `orderBy`, and
|
|
31
|
+
* each shape sits on an index the foundation declared:
|
|
32
|
+
* `(visibleTo, pipelineId, status, updatedAt DESC)`. Passing `null` for the
|
|
33
|
+
* pipeline or an EMPTY token list issues nothing, which is how a column
|
|
34
|
+
* stays quiet until the pipeline has loaded and a collapsed Won column
|
|
35
|
+
* costs no read. A `null` token list is the ORGANIZATION level (AGL-2630):
|
|
36
|
+
* the clause is dropped and the query rides the `(pipelineId, status,
|
|
37
|
+
* updatedAt DESC)` twin of that index.
|
|
38
|
+
*/ export function useDealsByStatus(orgId, readTokens, pipelineId, status, max) {
|
|
39
|
+
const firestore = useFirestore();
|
|
40
|
+
return useFirestoreCollection(()=>orgId && pipelineId && crmScopeListable(readTokens) ? query(collection(firestore, 'orgs', orgId, CRM_COLLECTIONS.deals), ...crmVisibleToClause(readTokens), where('pipelineId', '==', pipelineId), where('status', '==', status), orderBy('updatedAt', 'desc'), limit(max)) : null, [
|
|
41
|
+
firestore,
|
|
42
|
+
orgId,
|
|
43
|
+
readTokens,
|
|
44
|
+
pipelineId,
|
|
45
|
+
status,
|
|
46
|
+
max
|
|
47
|
+
], {
|
|
48
|
+
idField: '$id'
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Every deal the viewer may see, paged, optionally narrowed to one status
|
|
53
|
+
* and to one pipeline — the table's read. Four shapes, four indexes:
|
|
54
|
+
* `(visibleTo, updatedAt DESC)`, `(visibleTo, status, updatedAt DESC)`,
|
|
55
|
+
* `(visibleTo, pipelineId, updatedAt DESC)` and
|
|
56
|
+
* `(visibleTo, pipelineId, status, updatedAt DESC)`. At the organization
|
|
57
|
+
* level (AGL-2630) the scope clause is dropped and the same shapes ride
|
|
58
|
+
* their twins without `visibleTo` — `(status, updatedAt DESC)`,
|
|
59
|
+
* `(pipelineId, updatedAt DESC)`, `(pipelineId, status, updatedAt DESC)`;
|
|
60
|
+
* the first shape orders on one field and needs none.
|
|
61
|
+
*/ export function usePagedDeals(orgId, readTokens, status, pipelineId = null) {
|
|
62
|
+
const firestore = useFirestore();
|
|
63
|
+
return usePagedCollection((pageLimit)=>orgId && crmScopeListable(readTokens) ? query(collection(firestore, 'orgs', orgId, CRM_COLLECTIONS.deals), ...crmVisibleToClause(readTokens), ...pipelineId ? [
|
|
64
|
+
where('pipelineId', '==', pipelineId)
|
|
65
|
+
] : [], ...status === 'all' ? [] : [
|
|
66
|
+
where('status', '==', status)
|
|
67
|
+
], orderBy('updatedAt', 'desc'), limit(pageLimit)) : null, [
|
|
68
|
+
firestore,
|
|
69
|
+
orgId,
|
|
70
|
+
readTokens,
|
|
71
|
+
status,
|
|
72
|
+
pipelineId
|
|
73
|
+
], {
|
|
74
|
+
idField: '$id'
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The deals that name one contact or one company, for the card on that
|
|
79
|
+
* record's page. `(visibleTo, contactId|companyId, updatedAt DESC)`.
|
|
80
|
+
*/ export function useLinkedDeals(orgId, readTokens, link) {
|
|
81
|
+
const firestore = useFirestore();
|
|
82
|
+
const field = 'contactId' in link ? 'contactId' : 'companyId';
|
|
83
|
+
const id = 'contactId' in link ? link.contactId : link.companyId;
|
|
84
|
+
return useFirestoreCollection(()=>orgId && id && crmScopeListable(readTokens) ? query(collection(firestore, 'orgs', orgId, CRM_COLLECTIONS.deals), ...crmVisibleToClause(readTokens), where(field, '==', id), orderBy('updatedAt', 'desc'), limit(LINKED_DEALS_LIMIT)) : null, [
|
|
85
|
+
firestore,
|
|
86
|
+
orgId,
|
|
87
|
+
readTokens,
|
|
88
|
+
field,
|
|
89
|
+
id
|
|
90
|
+
], {
|
|
91
|
+
idField: '$id'
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
/** One deal, live. `undefined` while loading, and after a delete. */ export function useDeal(orgId, dealId) {
|
|
95
|
+
const firestore = useFirestore();
|
|
96
|
+
return useFirestoreDoc(()=>orgId && dealId ? doc(firestore, 'orgs', orgId, CRM_COLLECTIONS.deals, dealId) : null, [
|
|
97
|
+
firestore,
|
|
98
|
+
orgId,
|
|
99
|
+
dealId
|
|
100
|
+
], {
|
|
101
|
+
idField: '$id'
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
//# sourceMappingURL=use-deals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/hooks/use-deals.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 { CRM_COLLECTIONS, type CrmDealStatus } from '@aglyn/aglyn'\nimport {\n useFirestore,\n useFirestoreCollection,\n useFirestoreDoc,\n usePagedCollection,\n} from '@aglyn/tenant-feature-instance'\nimport {\n collection,\n doc,\n limit,\n orderBy,\n query,\n where,\n} from 'firebase/firestore'\nimport type { DealDoc } from '../model/deal-board-model'\nimport { crmScopeListable, crmVisibleToClause } from './use-crm-scope'\n\n/**\n * The most open cards one pipeline draws. A board past this is a board\n * nobody reads column by column; the table is paged and reaches the rest.\n */\nexport const BOARD_OPEN_LIMIT = 500\n\n/** Closed deals shown per closed column when it is expanded. */\nexport const BOARD_CLOSED_LIMIT = 50\n\n/** Deals listed on a contact's or a company's page. */\nexport const LINKED_DEALS_LIMIT = 25\n\n/**\n * The deals of one status in one pipeline, newest activity first (AGL-2598).\n *\n * Every query here carries the viewer's scope tokens and an `orderBy`, and\n * each shape sits on an index the foundation declared:\n * `(visibleTo, pipelineId, status, updatedAt DESC)`. Passing `null` for the\n * pipeline or an EMPTY token list issues nothing, which is how a column\n * stays quiet until the pipeline has loaded and a collapsed Won column\n * costs no read. A `null` token list is the ORGANIZATION level (AGL-2630):\n * the clause is dropped and the query rides the `(pipelineId, status,\n * updatedAt DESC)` twin of that index.\n */\nexport function useDealsByStatus(\n orgId: string | null,\n readTokens: readonly string[] | null,\n pipelineId: string | null,\n status: CrmDealStatus,\n max: number,\n) {\n const firestore = useFirestore()\n return useFirestoreCollection<DealDoc>(\n () =>\n orgId && pipelineId && crmScopeListable(readTokens)\n ? query(\n collection(firestore, 'orgs', orgId, CRM_COLLECTIONS.deals),\n ...crmVisibleToClause(readTokens),\n where('pipelineId', '==', pipelineId),\n where('status', '==', status),\n orderBy('updatedAt', 'desc'),\n limit(max),\n )\n : null,\n [firestore, orgId, readTokens, pipelineId, status, max],\n { idField: '$id' },\n )\n}\n\n/**\n * Every deal the viewer may see, paged, optionally narrowed to one status\n * and to one pipeline — the table's read. Four shapes, four indexes:\n * `(visibleTo, updatedAt DESC)`, `(visibleTo, status, updatedAt DESC)`,\n * `(visibleTo, pipelineId, updatedAt DESC)` and\n * `(visibleTo, pipelineId, status, updatedAt DESC)`. At the organization\n * level (AGL-2630) the scope clause is dropped and the same shapes ride\n * their twins without `visibleTo` — `(status, updatedAt DESC)`,\n * `(pipelineId, updatedAt DESC)`, `(pipelineId, status, updatedAt DESC)`;\n * the first shape orders on one field and needs none.\n */\nexport function usePagedDeals(\n orgId: string | null,\n readTokens: readonly string[] | null,\n status: CrmDealStatus | 'all',\n pipelineId: string | null = null,\n) {\n const firestore = useFirestore()\n return usePagedCollection<DealDoc>(\n (pageLimit) =>\n orgId && crmScopeListable(readTokens)\n ? query(\n collection(firestore, 'orgs', orgId, CRM_COLLECTIONS.deals),\n ...crmVisibleToClause(readTokens),\n ...(pipelineId ? [where('pipelineId', '==', pipelineId)] : []),\n ...(status === 'all' ? [] : [where('status', '==', status)]),\n orderBy('updatedAt', 'desc'),\n limit(pageLimit),\n )\n : null,\n [firestore, orgId, readTokens, status, pipelineId],\n { idField: '$id' },\n )\n}\n\n/**\n * The deals that name one contact or one company, for the card on that\n * record's page. `(visibleTo, contactId|companyId, updatedAt DESC)`.\n */\nexport function useLinkedDeals(\n orgId: string | null,\n readTokens: readonly string[] | null,\n link: { contactId: string } | { companyId: string },\n) {\n const firestore = useFirestore()\n const field = 'contactId' in link ? 'contactId' : 'companyId'\n const id = 'contactId' in link ? link.contactId : link.companyId\n return useFirestoreCollection<DealDoc>(\n () =>\n orgId && id && crmScopeListable(readTokens)\n ? query(\n collection(firestore, 'orgs', orgId, CRM_COLLECTIONS.deals),\n ...crmVisibleToClause(readTokens),\n where(field, '==', id),\n orderBy('updatedAt', 'desc'),\n limit(LINKED_DEALS_LIMIT),\n )\n : null,\n [firestore, orgId, readTokens, field, id],\n { idField: '$id' },\n )\n}\n\n/** One deal, live. `undefined` while loading, and after a delete. */\nexport function useDeal(orgId: string | null, dealId: string) {\n const firestore = useFirestore()\n return useFirestoreDoc<DealDoc>(\n () =>\n orgId && dealId\n ? doc(firestore, 'orgs', orgId, CRM_COLLECTIONS.deals, dealId)\n : null,\n [firestore, orgId, dealId],\n { idField: '$id' },\n )\n}\n"],"names":["CRM_COLLECTIONS","useFirestore","useFirestoreCollection","useFirestoreDoc","usePagedCollection","collection","doc","limit","orderBy","query","where","crmScopeListable","crmVisibleToClause","BOARD_OPEN_LIMIT","BOARD_CLOSED_LIMIT","LINKED_DEALS_LIMIT","useDealsByStatus","orgId","readTokens","pipelineId","status","max","firestore","deals","idField","usePagedDeals","pageLimit","useLinkedDeals","link","field","id","contactId","companyId","useDeal","dealId"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;AAEA,SAASA,eAAe,QAA4B,eAAc;AAClE,SACEC,YAAY,EACZC,sBAAsB,EACtBC,eAAe,EACfC,kBAAkB,QACb,iCAAgC;AACvC,SACEC,UAAU,EACVC,GAAG,EACHC,KAAK,EACLC,OAAO,EACPC,KAAK,EACLC,KAAK,QACA,qBAAoB;AAE3B,SAASC,gBAAgB,EAAEC,kBAAkB,QAAQ,qBAAiB;AAEtE;;;CAGC,GACD,OAAO,MAAMC,mBAAmB,IAAG;AAEnC,8DAA8D,GAC9D,OAAO,MAAMC,qBAAqB,GAAE;AAEpC,qDAAqD,GACrD,OAAO,MAAMC,qBAAqB,GAAE;AAEpC;;;;;;;;;;;CAWC,GACD,OAAO,SAASC,iBACdC,KAAoB,EACpBC,UAAoC,EACpCC,UAAyB,EACzBC,MAAqB,EACrBC,GAAW;IAEX,MAAMC,YAAYrB;IAClB,OAAOC,uBACL,IACEe,SAASE,cAAcR,iBAAiBO,cACpCT,MACEJ,WAAWiB,WAAW,QAAQL,OAAOjB,gBAAgBuB,KAAK,MACvDX,mBAAmBM,aACtBR,MAAM,cAAc,MAAMS,aAC1BT,MAAM,UAAU,MAAMU,SACtBZ,QAAQ,aAAa,SACrBD,MAAMc,QAER,MACN;QAACC;QAAWL;QAAOC;QAAYC;QAAYC;QAAQC;KAAI,EACvD;QAAEG,SAAS;IAAM;AAErB;AAEA;;;;;;;;;;CAUC,GACD,OAAO,SAASC,cACdR,KAAoB,EACpBC,UAAoC,EACpCE,MAA6B,EAC7BD,aAA4B,IAAI;IAEhC,MAAMG,YAAYrB;IAClB,OAAOG,mBACL,CAACsB,YACCT,SAASN,iBAAiBO,cACtBT,MACEJ,WAAWiB,WAAW,QAAQL,OAAOjB,gBAAgBuB,KAAK,MACvDX,mBAAmBM,gBAClBC,aAAa;YAACT,MAAM,cAAc,MAAMS;SAAY,GAAG,EAAE,KACzDC,WAAW,QAAQ,EAAE,GAAG;YAACV,MAAM,UAAU,MAAMU;SAAQ,EAC3DZ,QAAQ,aAAa,SACrBD,MAAMmB,cAER,MACN;QAACJ;QAAWL;QAAOC;QAAYE;QAAQD;KAAW,EAClD;QAAEK,SAAS;IAAM;AAErB;AAEA;;;CAGC,GACD,OAAO,SAASG,eACdV,KAAoB,EACpBC,UAAoC,EACpCU,IAAmD;IAEnD,MAAMN,YAAYrB;IAClB,MAAM4B,QAAQ,eAAeD,OAAO,cAAc;IAClD,MAAME,KAAK,eAAeF,OAAOA,KAAKG,SAAS,GAAGH,KAAKI,SAAS;IAChE,OAAO9B,uBACL,IACEe,SAASa,MAAMnB,iBAAiBO,cAC5BT,MACEJ,WAAWiB,WAAW,QAAQL,OAAOjB,gBAAgBuB,KAAK,MACvDX,mBAAmBM,aACtBR,MAAMmB,OAAO,MAAMC,KACnBtB,QAAQ,aAAa,SACrBD,MAAMQ,uBAER,MACN;QAACO;QAAWL;QAAOC;QAAYW;QAAOC;KAAG,EACzC;QAAEN,SAAS;IAAM;AAErB;AAEA,mEAAmE,GACnE,OAAO,SAASS,QAAQhB,KAAoB,EAAEiB,MAAc;IAC1D,MAAMZ,YAAYrB;IAClB,OAAOE,gBACL,IACEc,SAASiB,SACL5B,IAAIgB,WAAW,QAAQL,OAAOjB,gBAAgBuB,KAAK,EAAEW,UACrD,MACN;QAACZ;QAAWL;QAAOiB;KAAO,EAC1B;QAAEV,SAAS;IAAM;AAErB"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type DocumentData } from 'firebase/firestore';
|
|
2
|
+
/** One site's lead, as the organization-level list carries it. */
|
|
3
|
+
export interface OrgLeadRow extends DocumentData {
|
|
4
|
+
/**
|
|
5
|
+
* The grid's row id: `{hostId}/{leadId}`. A lead's own id is a PERSON
|
|
6
|
+
* KEY — the same person on two sites has the same id on both — so the
|
|
7
|
+
* document id alone cannot key a list that spans sites.
|
|
8
|
+
*/
|
|
9
|
+
$id: string;
|
|
10
|
+
/** `hosts/{hostId}/leads/{leadId}` — the document id. */
|
|
11
|
+
leadId: string;
|
|
12
|
+
/** The site the lead lives under; what every write and link names. */
|
|
13
|
+
hostId: string;
|
|
14
|
+
}
|
|
15
|
+
export interface OrgLeadsResult {
|
|
16
|
+
/** Every site's window, merged and ordered newest-seen first, cut to the window. */
|
|
17
|
+
data: OrgLeadRow[];
|
|
18
|
+
/** `loading` until every site has answered once; `error` when any refused. */
|
|
19
|
+
status: 'loading' | 'success' | 'error';
|
|
20
|
+
/** Some site had more than its window — there are leads this list is not showing. */
|
|
21
|
+
truncated: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* THE ORGANIZATION'S LEADS, read a site at a time (AGL-2630).
|
|
25
|
+
*
|
|
26
|
+
* A lead lives under its site — `hosts/{hostId}/leads`, host-scoped by
|
|
27
|
+
* PATH — and there is no org-level collection to listen to, no `orgId` on
|
|
28
|
+
* the document to group by, and no rule admitting a collection-group read.
|
|
29
|
+
* So the org hub opens one listener per site the org has, each the exact
|
|
30
|
+
* query the site's own Leads section runs, and merges the answers. An
|
|
31
|
+
* org-wide member is a member of every site, which is what admits each
|
|
32
|
+
* listener under the rules the site section already relies on; an org has
|
|
33
|
+
* a handful of sites and at most thirty in a consent group, so the fan-out
|
|
34
|
+
* is bounded by the org, not by the data.
|
|
35
|
+
*
|
|
36
|
+
* Each site's window is cut at `windowSize + 1`, the way the site section
|
|
37
|
+
* cuts its own, and the merged list is cut again at `windowSize`: the list
|
|
38
|
+
* is "the most recently seen across the org", and a site with a thousand
|
|
39
|
+
* leads must not crowd out a site with ten. `truncated` says some site had
|
|
40
|
+
* more, so the caption beneath the table can say the window is not the
|
|
41
|
+
* whole collection.
|
|
42
|
+
*
|
|
43
|
+
* The listeners are torn down together when the site list changes — a site
|
|
44
|
+
* added or removed re-opens the set — and nothing opens for an empty list,
|
|
45
|
+
* which is also how the hook stays quiet under a site, where the section
|
|
46
|
+
* hands it no sites at all.
|
|
47
|
+
*/
|
|
48
|
+
export declare function useOrgLeads(options: {
|
|
49
|
+
/** The org's sites, by document id; empty opens nothing. */
|
|
50
|
+
hostIds: readonly string[];
|
|
51
|
+
windowSize: number;
|
|
52
|
+
}): OrgLeadsResult;
|
|
53
|
+
export default useOrgLeads;
|
|
@@ -0,0 +1,114 @@
|
|
|
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 { useFirestore } from "@aglyn/tenant-feature-instance";
|
|
19
|
+
import { collection, limit, onSnapshot, orderBy, query } from "firebase/firestore";
|
|
20
|
+
import { useEffect, useMemo, useState } from "react";
|
|
21
|
+
/**
|
|
22
|
+
* THE ORGANIZATION'S LEADS, read a site at a time (AGL-2630).
|
|
23
|
+
*
|
|
24
|
+
* A lead lives under its site — `hosts/{hostId}/leads`, host-scoped by
|
|
25
|
+
* PATH — and there is no org-level collection to listen to, no `orgId` on
|
|
26
|
+
* the document to group by, and no rule admitting a collection-group read.
|
|
27
|
+
* So the org hub opens one listener per site the org has, each the exact
|
|
28
|
+
* query the site's own Leads section runs, and merges the answers. An
|
|
29
|
+
* org-wide member is a member of every site, which is what admits each
|
|
30
|
+
* listener under the rules the site section already relies on; an org has
|
|
31
|
+
* a handful of sites and at most thirty in a consent group, so the fan-out
|
|
32
|
+
* is bounded by the org, not by the data.
|
|
33
|
+
*
|
|
34
|
+
* Each site's window is cut at `windowSize + 1`, the way the site section
|
|
35
|
+
* cuts its own, and the merged list is cut again at `windowSize`: the list
|
|
36
|
+
* is "the most recently seen across the org", and a site with a thousand
|
|
37
|
+
* leads must not crowd out a site with ten. `truncated` says some site had
|
|
38
|
+
* more, so the caption beneath the table can say the window is not the
|
|
39
|
+
* whole collection.
|
|
40
|
+
*
|
|
41
|
+
* The listeners are torn down together when the site list changes — a site
|
|
42
|
+
* added or removed re-opens the set — and nothing opens for an empty list,
|
|
43
|
+
* which is also how the hook stays quiet under a site, where the section
|
|
44
|
+
* hands it no sites at all.
|
|
45
|
+
*/ export function useOrgLeads(options) {
|
|
46
|
+
const { hostIds, windowSize } = options;
|
|
47
|
+
const firestore = useFirestore();
|
|
48
|
+
const key = hostIds.join('\n');
|
|
49
|
+
const [bySite, setBySite] = useState({});
|
|
50
|
+
useEffect(()=>{
|
|
51
|
+
setBySite({});
|
|
52
|
+
const sites = key ? key.split('\n') : [];
|
|
53
|
+
if (!sites.length) return undefined;
|
|
54
|
+
const stops = sites.map((hostId)=>onSnapshot(query(collection(firestore, 'hosts', hostId, 'leads'), orderBy('lastSeenAtMs', 'desc'), limit(windowSize + 1)), (snapshot)=>{
|
|
55
|
+
const rows = snapshot.docs.map((entry)=>_extends({}, entry.data(), {
|
|
56
|
+
$id: `${hostId}/${entry.id}`,
|
|
57
|
+
leadId: entry.id,
|
|
58
|
+
hostId
|
|
59
|
+
}));
|
|
60
|
+
setBySite((current)=>_extends({}, current, {
|
|
61
|
+
[hostId]: {
|
|
62
|
+
rows: rows.slice(0, windowSize),
|
|
63
|
+
truncated: rows.length > windowSize
|
|
64
|
+
}
|
|
65
|
+
}));
|
|
66
|
+
}, (error)=>{
|
|
67
|
+
console.error(error);
|
|
68
|
+
setBySite((current)=>_extends({}, current, {
|
|
69
|
+
[hostId]: 'error'
|
|
70
|
+
}));
|
|
71
|
+
}));
|
|
72
|
+
return ()=>{
|
|
73
|
+
for (const stop of stops)stop();
|
|
74
|
+
};
|
|
75
|
+
}, [
|
|
76
|
+
firestore,
|
|
77
|
+
key,
|
|
78
|
+
windowSize
|
|
79
|
+
]);
|
|
80
|
+
return useMemo(()=>{
|
|
81
|
+
const sites = key ? key.split('\n') : [];
|
|
82
|
+
const answers = sites.map((hostId)=>bySite[hostId]);
|
|
83
|
+
if (answers.some((answer)=>answer === 'error')) {
|
|
84
|
+
return {
|
|
85
|
+
data: [],
|
|
86
|
+
status: 'error',
|
|
87
|
+
truncated: false
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
if (!sites.length || answers.some((answer)=>!answer)) {
|
|
91
|
+
return {
|
|
92
|
+
data: [],
|
|
93
|
+
status: 'loading',
|
|
94
|
+
truncated: false
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
const merged = answers.flatMap((answer)=>answer === 'error' || !answer ? [] : answer.rows).sort((a, b)=>{
|
|
98
|
+
var _b_lastSeenAtMs, _a_lastSeenAtMs;
|
|
99
|
+
return Number((_b_lastSeenAtMs = b['lastSeenAtMs']) != null ? _b_lastSeenAtMs : 0) - Number((_a_lastSeenAtMs = a['lastSeenAtMs']) != null ? _a_lastSeenAtMs : 0);
|
|
100
|
+
});
|
|
101
|
+
return {
|
|
102
|
+
data: merged.slice(0, windowSize),
|
|
103
|
+
status: 'success',
|
|
104
|
+
truncated: merged.length > windowSize || answers.some((answer)=>answer !== 'error' && (answer == null ? void 0 : answer.truncated))
|
|
105
|
+
};
|
|
106
|
+
}, [
|
|
107
|
+
bySite,
|
|
108
|
+
key,
|
|
109
|
+
windowSize
|
|
110
|
+
]);
|
|
111
|
+
}
|
|
112
|
+
export default useOrgLeads;
|
|
113
|
+
|
|
114
|
+
//# sourceMappingURL=use-org-leads.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/hooks/use-org-leads.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 { useFirestore } from '@aglyn/tenant-feature-instance'\nimport {\n collection,\n limit,\n onSnapshot,\n orderBy,\n query,\n type DocumentData,\n} from 'firebase/firestore'\nimport { useEffect, useMemo, useState } from 'react'\n\n/** One site's lead, as the organization-level list carries it. */\nexport interface OrgLeadRow extends DocumentData {\n /**\n * The grid's row id: `{hostId}/{leadId}`. A lead's own id is a PERSON\n * KEY — the same person on two sites has the same id on both — so the\n * document id alone cannot key a list that spans sites.\n */\n $id: string\n /** `hosts/{hostId}/leads/{leadId}` — the document id. */\n leadId: string\n /** The site the lead lives under; what every write and link names. */\n hostId: string\n}\n\nexport interface OrgLeadsResult {\n /** Every site's window, merged and ordered newest-seen first, cut to the window. */\n data: OrgLeadRow[]\n /** `loading` until every site has answered once; `error` when any refused. */\n status: 'loading' | 'success' | 'error'\n /** Some site had more than its window — there are leads this list is not showing. */\n truncated: boolean\n}\n\n/**\n * THE ORGANIZATION'S LEADS, read a site at a time (AGL-2630).\n *\n * A lead lives under its site — `hosts/{hostId}/leads`, host-scoped by\n * PATH — and there is no org-level collection to listen to, no `orgId` on\n * the document to group by, and no rule admitting a collection-group read.\n * So the org hub opens one listener per site the org has, each the exact\n * query the site's own Leads section runs, and merges the answers. An\n * org-wide member is a member of every site, which is what admits each\n * listener under the rules the site section already relies on; an org has\n * a handful of sites and at most thirty in a consent group, so the fan-out\n * is bounded by the org, not by the data.\n *\n * Each site's window is cut at `windowSize + 1`, the way the site section\n * cuts its own, and the merged list is cut again at `windowSize`: the list\n * is \"the most recently seen across the org\", and a site with a thousand\n * leads must not crowd out a site with ten. `truncated` says some site had\n * more, so the caption beneath the table can say the window is not the\n * whole collection.\n *\n * The listeners are torn down together when the site list changes — a site\n * added or removed re-opens the set — and nothing opens for an empty list,\n * which is also how the hook stays quiet under a site, where the section\n * hands it no sites at all.\n */\nexport function useOrgLeads(options: {\n /** The org's sites, by document id; empty opens nothing. */\n hostIds: readonly string[]\n windowSize: number\n}): OrgLeadsResult {\n const { hostIds, windowSize } = options\n const firestore = useFirestore()\n const key = hostIds.join('\\n')\n const [bySite, setBySite] = useState<\n Record<string, { rows: OrgLeadRow[]; truncated: boolean } | 'error'>\n >({})\n\n useEffect(() => {\n setBySite({})\n const sites = key ? key.split('\\n') : []\n if (!sites.length) return undefined\n const stops = sites.map((hostId) =>\n onSnapshot(\n query(\n collection(firestore, 'hosts', hostId, 'leads'),\n orderBy('lastSeenAtMs', 'desc'),\n limit(windowSize + 1),\n ),\n (snapshot) => {\n const rows = snapshot.docs.map((entry) => ({\n ...(entry.data() as DocumentData),\n $id: `${hostId}/${entry.id}`,\n leadId: entry.id,\n hostId,\n }))\n setBySite((current) => ({\n ...current,\n [hostId]: {\n rows: rows.slice(0, windowSize),\n truncated: rows.length > windowSize,\n },\n }))\n },\n (error) => {\n console.error(error)\n setBySite((current) => ({ ...current, [hostId]: 'error' }))\n },\n ),\n )\n return () => {\n for (const stop of stops) stop()\n }\n }, [firestore, key, windowSize])\n\n return useMemo(() => {\n const sites = key ? key.split('\\n') : []\n const answers = sites.map((hostId) => bySite[hostId])\n if (answers.some((answer) => answer === 'error')) {\n return { data: [], status: 'error' as const, truncated: false }\n }\n if (!sites.length || answers.some((answer) => !answer)) {\n return { data: [], status: 'loading' as const, truncated: false }\n }\n const merged = answers\n .flatMap((answer) => (answer === 'error' || !answer ? [] : answer.rows))\n .sort(\n (a, b) =>\n Number(b['lastSeenAtMs'] ?? 0) - Number(a['lastSeenAtMs'] ?? 0),\n )\n return {\n data: merged.slice(0, windowSize),\n status: 'success' as const,\n truncated:\n merged.length > windowSize ||\n answers.some((answer) => answer !== 'error' && answer?.truncated),\n }\n }, [bySite, key, windowSize])\n}\n\nexport default useOrgLeads\n"],"names":["useFirestore","collection","limit","onSnapshot","orderBy","query","useEffect","useMemo","useState","useOrgLeads","options","hostIds","windowSize","firestore","key","join","bySite","setBySite","sites","split","length","undefined","stops","map","hostId","snapshot","rows","docs","entry","data","$id","id","leadId","current","slice","truncated","error","console","stop","answers","some","answer","status","merged","flatMap","sort","a","b","Number"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SAASA,YAAY,QAAQ,iCAAgC;AAC7D,SACEC,UAAU,EACVC,KAAK,EACLC,UAAU,EACVC,OAAO,EACPC,KAAK,QAEA,qBAAoB;AAC3B,SAASC,SAAS,EAAEC,OAAO,EAAEC,QAAQ,QAAQ,QAAO;AAyBpD;;;;;;;;;;;;;;;;;;;;;;;;CAwBC,GACD,OAAO,SAASC,YAAYC,OAI3B;IACC,MAAM,EAAEC,OAAO,EAAEC,UAAU,EAAE,GAAGF;IAChC,MAAMG,YAAYb;IAClB,MAAMc,MAAMH,QAAQI,IAAI,CAAC;IACzB,MAAM,CAACC,QAAQC,UAAU,GAAGT,SAE1B,CAAC;IAEHF,UAAU;QACRW,UAAU,CAAC;QACX,MAAMC,QAAQJ,MAAMA,IAAIK,KAAK,CAAC,QAAQ,EAAE;QACxC,IAAI,CAACD,MAAME,MAAM,EAAE,OAAOC;QAC1B,MAAMC,QAAQJ,MAAMK,GAAG,CAAC,CAACC,SACvBrB,WACEE,MACEJ,WAAWY,WAAW,SAASW,QAAQ,UACvCpB,QAAQ,gBAAgB,SACxBF,MAAMU,aAAa,KAErB,CAACa;gBACC,MAAMC,OAAOD,SAASE,IAAI,CAACJ,GAAG,CAAC,CAACK,QAAW,aACrCA,MAAMC,IAAI;wBACdC,KAAK,GAAGN,OAAO,CAAC,EAAEI,MAAMG,EAAE,EAAE;wBAC5BC,QAAQJ,MAAMG,EAAE;wBAChBP;;gBAEFP,UAAU,CAACgB,UAAa,aACnBA;wBACH,CAACT,OAAO,EAAE;4BACRE,MAAMA,KAAKQ,KAAK,CAAC,GAAGtB;4BACpBuB,WAAWT,KAAKN,MAAM,GAAGR;wBAC3B;;YAEJ,GACA,CAACwB;gBACCC,QAAQD,KAAK,CAACA;gBACdnB,UAAU,CAACgB,UAAa,aAAKA;wBAAS,CAACT,OAAO,EAAE;;YAClD;QAGJ,OAAO;YACL,KAAK,MAAMc,QAAQhB,MAAOgB;QAC5B;IACF,GAAG;QAACzB;QAAWC;QAAKF;KAAW;IAE/B,OAAOL,QAAQ;QACb,MAAMW,QAAQJ,MAAMA,IAAIK,KAAK,CAAC,QAAQ,EAAE;QACxC,MAAMoB,UAAUrB,MAAMK,GAAG,CAAC,CAACC,SAAWR,MAAM,CAACQ,OAAO;QACpD,IAAIe,QAAQC,IAAI,CAAC,CAACC,SAAWA,WAAW,UAAU;YAChD,OAAO;gBAAEZ,MAAM,EAAE;gBAAEa,QAAQ;gBAAkBP,WAAW;YAAM;QAChE;QACA,IAAI,CAACjB,MAAME,MAAM,IAAImB,QAAQC,IAAI,CAAC,CAACC,SAAW,CAACA,SAAS;YACtD,OAAO;gBAAEZ,MAAM,EAAE;gBAAEa,QAAQ;gBAAoBP,WAAW;YAAM;QAClE;QACA,MAAMQ,SAASJ,QACZK,OAAO,CAAC,CAACH,SAAYA,WAAW,WAAW,CAACA,SAAS,EAAE,GAAGA,OAAOf,IAAI,EACrEmB,IAAI,CACH,CAACC,GAAGC;gBACKA,iBAAiCD;mBAAxCE,QAAOD,kBAAAA,CAAC,CAAC,eAAe,YAAjBA,kBAAqB,KAAKC,QAAOF,kBAAAA,CAAC,CAAC,eAAe,YAAjBA,kBAAqB;;QAEnE,OAAO;YACLjB,MAAMc,OAAOT,KAAK,CAAC,GAAGtB;YACtB8B,QAAQ;YACRP,WACEQ,OAAOvB,MAAM,GAAGR,cAChB2B,QAAQC,IAAI,CAAC,CAACC,SAAWA,WAAW,YAAWA,0BAAAA,OAAQN,SAAS;QACpE;IACF,GAAG;QAACnB;QAAQF;QAAKF;KAAW;AAC9B;AAEA,eAAeH,YAAW"}
|