@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,329 @@
|
|
|
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, CRM_VIEW_NAME_MAX, crmViewStateEquals, EMPTY_CRM_VIEW_STATE, ORG_SCOPE_TOKEN } from "@aglyn/aglyn";
|
|
19
|
+
import { useFirestore, useScopeTokens, useUser } from "@aglyn/tenant-feature-instance";
|
|
20
|
+
import { addDoc, collection, deleteDoc, doc, updateDoc } from "firebase/firestore";
|
|
21
|
+
import { useRouter, useSearchParams } from "next/navigation";
|
|
22
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
23
|
+
import { crmRoutes } from "../model/crm-routes.js";
|
|
24
|
+
import { crmViewHref, crmViewIdFromParams } from "../model/crm-view-param.js";
|
|
25
|
+
import { useCrmScope } from "./use-crm-scope.js";
|
|
26
|
+
import { useCrmDefaultView } from "./use-crm-default-view.js";
|
|
27
|
+
import { useCrmViews } from "./use-crm-views.js";
|
|
28
|
+
/**
|
|
29
|
+
* The address value that means "no view, whatever my default is".
|
|
30
|
+
*
|
|
31
|
+
* Without it, choosing the plain list would clear the key and the default
|
|
32
|
+
* would reapply on the next render — the reader could never leave their
|
|
33
|
+
* own default. Written only when a default exists to be overridden, so an
|
|
34
|
+
* address stays clean for a reader who never set one.
|
|
35
|
+
*/ export const CRM_VIEW_NONE = 'all';
|
|
36
|
+
/**
|
|
37
|
+
* One section's saved views, resolved against its address (AGL-2617).
|
|
38
|
+
*
|
|
39
|
+
* The controller behind the views control on every CRM list. It owns three
|
|
40
|
+
* things the list used to keep in loose state and lose on reload: which
|
|
41
|
+
* view is open, what the reader has changed since, and how that gets back
|
|
42
|
+
* to the document.
|
|
43
|
+
*
|
|
44
|
+
* ## The address is the truth about which view is open
|
|
45
|
+
*
|
|
46
|
+
* `?view=<id>` names the view; choosing one navigates. So a view is a link,
|
|
47
|
+
* Back returns to the one before it, and a reload lands where the reader
|
|
48
|
+
* was. When the address names nothing, the reader's default for this
|
|
49
|
+
* section (their own profile's) opens instead — unless the list was opened
|
|
50
|
+
* FOR something, a form's captures or one address, in which case the plain
|
|
51
|
+
* list shows what it was asked for and nothing narrows it further.
|
|
52
|
+
*
|
|
53
|
+
* ## The seed is kept, first
|
|
54
|
+
*
|
|
55
|
+
* A Contacts address that carries a seed and a view means both: the seed's
|
|
56
|
+
* clause leads the view's, so the query serves the seed (it reaches the
|
|
57
|
+
* whole collection, as it did before views existed) and the view narrows
|
|
58
|
+
* the window. That is why `initial` folds into every baseline rather than
|
|
59
|
+
* being a starting state the view replaces.
|
|
60
|
+
*
|
|
61
|
+
* ## A view's state is applied once per view
|
|
62
|
+
*
|
|
63
|
+
* When the address names a view, its stored filters, columns and sort
|
|
64
|
+
* become the working state the moment the listing carries it — once. A
|
|
65
|
+
* later change to the document does not overwrite what the reader is in
|
|
66
|
+
* the middle of; a change the reader makes is theirs until they save it or
|
|
67
|
+
* reset. The default applies only onto an untouched plain list, so a reader
|
|
68
|
+
* who started narrowing before their profile arrived keeps what they typed.
|
|
69
|
+
*/ export function useCrmSavedView(options) {
|
|
70
|
+
var _ref, _ref1, _ref2, _ref3;
|
|
71
|
+
var _options_initial, _options_initial1, _options_initial2;
|
|
72
|
+
const { section, hostId, org, basePath } = options;
|
|
73
|
+
const initialFilters = (_ref = (_options_initial = options.initial) == null ? void 0 : _options_initial.filters) != null ? _ref : EMPTY_CRM_VIEW_STATE.filters;
|
|
74
|
+
const initialColumns = (_ref1 = (_options_initial1 = options.initial) == null ? void 0 : _options_initial1.columns) != null ? _ref1 : EMPTY_CRM_VIEW_STATE.columns;
|
|
75
|
+
const initialSort = (_ref2 = (_options_initial2 = options.initial) == null ? void 0 : _options_initial2.sort) != null ? _ref2 : EMPTY_CRM_VIEW_STATE.sort;
|
|
76
|
+
const seeded = initialFilters.length > 0;
|
|
77
|
+
const firestore = useFirestore();
|
|
78
|
+
const router = useRouter();
|
|
79
|
+
const searchParams = useSearchParams();
|
|
80
|
+
const { data: user } = useUser();
|
|
81
|
+
const uid = (_ref3 = user == null ? void 0 : user.uid) != null ? _ref3 : null;
|
|
82
|
+
const { scope, orgId, visibleTo, createTokens } = useCrmScope({
|
|
83
|
+
hostId,
|
|
84
|
+
org
|
|
85
|
+
});
|
|
86
|
+
const reach = useScopeTokens(orgId != null ? orgId : undefined);
|
|
87
|
+
const { views, ready } = useCrmViews({
|
|
88
|
+
scope,
|
|
89
|
+
visibleTo,
|
|
90
|
+
section,
|
|
91
|
+
uid
|
|
92
|
+
});
|
|
93
|
+
const { defaultViewId, ready: defaultReady, setDefault: writeDefault } = useCrmDefaultView({
|
|
94
|
+
uid,
|
|
95
|
+
orgId,
|
|
96
|
+
section
|
|
97
|
+
});
|
|
98
|
+
const sectionPath = useMemo(()=>crmRoutes(basePath).section(section), [
|
|
99
|
+
basePath,
|
|
100
|
+
section
|
|
101
|
+
]);
|
|
102
|
+
/*
|
|
103
|
+
* Which view the address asks for. `all` is the reader stepping out of
|
|
104
|
+
* their default on purpose; nothing at all defers to the default, except
|
|
105
|
+
* on a seeded list, which shows what it was opened for.
|
|
106
|
+
*/ const addressed = crmViewIdFromParams(searchParams);
|
|
107
|
+
const currentId = addressed === CRM_VIEW_NONE ? null : addressed != null ? addressed : seeded ? null : defaultViewId;
|
|
108
|
+
const current = useMemo(()=>{
|
|
109
|
+
var _views_find;
|
|
110
|
+
return currentId ? (_views_find = views.find((view)=>view.$id === currentId)) != null ? _views_find : null : null;
|
|
111
|
+
}, [
|
|
112
|
+
views,
|
|
113
|
+
currentId
|
|
114
|
+
]);
|
|
115
|
+
const missing = Boolean(currentId) && ready && defaultReady && !current;
|
|
116
|
+
/** What the list opens on for a view — the seed in front — or for none. */ const baseline = useCallback((view)=>{
|
|
117
|
+
var _view_sort;
|
|
118
|
+
return view ? {
|
|
119
|
+
filters: [
|
|
120
|
+
...initialFilters,
|
|
121
|
+
...view.filters
|
|
122
|
+
],
|
|
123
|
+
columns: view.columns.length ? view.columns : initialColumns,
|
|
124
|
+
sort: (_view_sort = view.sort) != null ? _view_sort : initialSort
|
|
125
|
+
} : {
|
|
126
|
+
filters: initialFilters,
|
|
127
|
+
columns: initialColumns,
|
|
128
|
+
sort: initialSort
|
|
129
|
+
};
|
|
130
|
+
}, [
|
|
131
|
+
initialFilters,
|
|
132
|
+
initialColumns,
|
|
133
|
+
initialSort
|
|
134
|
+
]);
|
|
135
|
+
const [state, setState] = useState(()=>baseline(null));
|
|
136
|
+
/** The view whose state the working state was last set from — `''` for none. */ const applied = useRef('');
|
|
137
|
+
const dirty = !crmViewStateEquals(state, baseline(current));
|
|
138
|
+
useEffect(()=>{
|
|
139
|
+
var _ref;
|
|
140
|
+
const target = (_ref = current == null ? void 0 : current.$id) != null ? _ref : '';
|
|
141
|
+
if (applied.current === target) return;
|
|
142
|
+
// A default arriving under a list the reader already narrowed is not
|
|
143
|
+
// allowed to take that narrowing away; an explicit choice always is.
|
|
144
|
+
if (target && !addressed && dirty) return;
|
|
145
|
+
applied.current = target;
|
|
146
|
+
setState(baseline(current));
|
|
147
|
+
}, [
|
|
148
|
+
current,
|
|
149
|
+
addressed,
|
|
150
|
+
dirty,
|
|
151
|
+
baseline
|
|
152
|
+
]);
|
|
153
|
+
const setFilters = useCallback((filters)=>setState((prev)=>_extends({}, prev, {
|
|
154
|
+
filters
|
|
155
|
+
})), []);
|
|
156
|
+
const setColumns = useCallback((columns)=>setState((prev)=>_extends({}, prev, {
|
|
157
|
+
columns
|
|
158
|
+
})), []);
|
|
159
|
+
const setSort = useCallback((sort)=>setState((prev)=>_extends({}, prev, {
|
|
160
|
+
sort
|
|
161
|
+
})), []);
|
|
162
|
+
const reset = useCallback(()=>setState(baseline(current)), [
|
|
163
|
+
baseline,
|
|
164
|
+
current
|
|
165
|
+
]);
|
|
166
|
+
const select = useCallback((viewId)=>{
|
|
167
|
+
// Leaving a view for the plain list under a default has to SAY so,
|
|
168
|
+
// or the default would reopen on the next render.
|
|
169
|
+
const value = viewId != null ? viewId : defaultViewId ? CRM_VIEW_NONE : null;
|
|
170
|
+
router.push(crmViewHref(sectionPath, searchParams, value));
|
|
171
|
+
}, [
|
|
172
|
+
router,
|
|
173
|
+
sectionPath,
|
|
174
|
+
searchParams,
|
|
175
|
+
defaultViewId
|
|
176
|
+
]);
|
|
177
|
+
const [busy, setBusy] = useState(false);
|
|
178
|
+
const guarded = useCallback(async (work)=>{
|
|
179
|
+
setBusy(true);
|
|
180
|
+
try {
|
|
181
|
+
return await work();
|
|
182
|
+
} finally{
|
|
183
|
+
setBusy(false);
|
|
184
|
+
}
|
|
185
|
+
}, []);
|
|
186
|
+
const viewRef = useCallback((viewId)=>scope ? doc(firestore, scope[0], scope[1], CRM_COLLECTIONS.views, viewId) : null, [
|
|
187
|
+
firestore,
|
|
188
|
+
scope
|
|
189
|
+
]);
|
|
190
|
+
/** The three list-facing fields as the document stores them — without the seed. */ const stored = useCallback(()=>({
|
|
191
|
+
// The seed is the address's, not the view's: a view saved while a
|
|
192
|
+
// form's captures were open must not carry that form forever.
|
|
193
|
+
filters: state.filters.filter((clause)=>!initialFilters.some((seed)=>seed.field === clause.field && seed.op === clause.op && seed.value === clause.value)),
|
|
194
|
+
columns: state.columns,
|
|
195
|
+
sort: state.sort
|
|
196
|
+
}), [
|
|
197
|
+
state,
|
|
198
|
+
initialFilters
|
|
199
|
+
]);
|
|
200
|
+
const save = useCallback(async ()=>{
|
|
201
|
+
const ref = current ? viewRef(current.$id) : null;
|
|
202
|
+
if (!ref) return;
|
|
203
|
+
await guarded(()=>updateDoc(ref, _extends({}, stored(), {
|
|
204
|
+
updatedAt: new Date()
|
|
205
|
+
})));
|
|
206
|
+
}, [
|
|
207
|
+
current,
|
|
208
|
+
viewRef,
|
|
209
|
+
guarded,
|
|
210
|
+
stored
|
|
211
|
+
]);
|
|
212
|
+
const saveAs = useCallback(async (name, shared)=>{
|
|
213
|
+
const trimmed = name.trim().slice(0, CRM_VIEW_NAME_MAX);
|
|
214
|
+
if (!trimmed || !scope || !uid) return null;
|
|
215
|
+
const created = await guarded(()=>addDoc(collection(firestore, scope[0], scope[1], CRM_COLLECTIONS.views), _extends({
|
|
216
|
+
section,
|
|
217
|
+
name: trimmed
|
|
218
|
+
}, stored(), {
|
|
219
|
+
ownerUid: uid,
|
|
220
|
+
createdByUid: uid,
|
|
221
|
+
shared,
|
|
222
|
+
// A view made at the organization level belongs to the org
|
|
223
|
+
// (AGL-2630): it names no site, and it is stamped org-wide so
|
|
224
|
+
// every site's reader lists it — unlike a record, a view is a
|
|
225
|
+
// working arrangement, not a person's data.
|
|
226
|
+
hostId: hostId != null ? hostId : null,
|
|
227
|
+
visibleTo: hostId ? createTokens : [
|
|
228
|
+
ORG_SCOPE_TOKEN
|
|
229
|
+
],
|
|
230
|
+
createdAt: new Date(),
|
|
231
|
+
updatedAt: new Date()
|
|
232
|
+
})));
|
|
233
|
+
// The new view's state IS the working state, so nothing is lost when
|
|
234
|
+
// the address changes and the listing catches up.
|
|
235
|
+
applied.current = created.id;
|
|
236
|
+
select(created.id);
|
|
237
|
+
return created.id;
|
|
238
|
+
}, [
|
|
239
|
+
scope,
|
|
240
|
+
uid,
|
|
241
|
+
guarded,
|
|
242
|
+
firestore,
|
|
243
|
+
section,
|
|
244
|
+
stored,
|
|
245
|
+
hostId,
|
|
246
|
+
createTokens,
|
|
247
|
+
select
|
|
248
|
+
]);
|
|
249
|
+
const rename = useCallback(async (name)=>{
|
|
250
|
+
const trimmed = name.trim().slice(0, CRM_VIEW_NAME_MAX);
|
|
251
|
+
const ref = current ? viewRef(current.$id) : null;
|
|
252
|
+
if (!trimmed || !ref) return;
|
|
253
|
+
await guarded(()=>updateDoc(ref, {
|
|
254
|
+
name: trimmed,
|
|
255
|
+
updatedAt: new Date()
|
|
256
|
+
}));
|
|
257
|
+
}, [
|
|
258
|
+
current,
|
|
259
|
+
viewRef,
|
|
260
|
+
guarded
|
|
261
|
+
]);
|
|
262
|
+
const setShared = useCallback(async (shared)=>{
|
|
263
|
+
const ref = current ? viewRef(current.$id) : null;
|
|
264
|
+
if (!ref) return;
|
|
265
|
+
await guarded(()=>updateDoc(ref, {
|
|
266
|
+
shared,
|
|
267
|
+
updatedAt: new Date()
|
|
268
|
+
}));
|
|
269
|
+
}, [
|
|
270
|
+
current,
|
|
271
|
+
viewRef,
|
|
272
|
+
guarded
|
|
273
|
+
]);
|
|
274
|
+
const isDefault = Boolean(currentId) && defaultViewId === currentId;
|
|
275
|
+
const setDefault = useCallback(async (viewId)=>{
|
|
276
|
+
await guarded(()=>writeDefault(viewId));
|
|
277
|
+
}, [
|
|
278
|
+
guarded,
|
|
279
|
+
writeDefault
|
|
280
|
+
]);
|
|
281
|
+
const remove = useCallback(async ()=>{
|
|
282
|
+
const ref = current ? viewRef(current.$id) : null;
|
|
283
|
+
if (!ref || !current) return;
|
|
284
|
+
await guarded(async ()=>{
|
|
285
|
+
await deleteDoc(ref);
|
|
286
|
+
// A default pointing at nothing would open every visit on "missing".
|
|
287
|
+
if (defaultViewId === current.$id) await writeDefault(null);
|
|
288
|
+
});
|
|
289
|
+
applied.current = '';
|
|
290
|
+
select(null);
|
|
291
|
+
}, [
|
|
292
|
+
current,
|
|
293
|
+
viewRef,
|
|
294
|
+
guarded,
|
|
295
|
+
defaultViewId,
|
|
296
|
+
writeDefault,
|
|
297
|
+
select
|
|
298
|
+
]);
|
|
299
|
+
const canEdit = Boolean(current && uid && (current.createdByUid === uid || reach.loaded && reach.orgWide));
|
|
300
|
+
return {
|
|
301
|
+
section,
|
|
302
|
+
views,
|
|
303
|
+
ready: ready && defaultReady,
|
|
304
|
+
current,
|
|
305
|
+
currentId,
|
|
306
|
+
missing,
|
|
307
|
+
state,
|
|
308
|
+
setFilters,
|
|
309
|
+
setColumns,
|
|
310
|
+
setSort,
|
|
311
|
+
dirty,
|
|
312
|
+
canEdit,
|
|
313
|
+
uid,
|
|
314
|
+
select,
|
|
315
|
+
reset,
|
|
316
|
+
save,
|
|
317
|
+
saveAs,
|
|
318
|
+
rename,
|
|
319
|
+
setShared,
|
|
320
|
+
remove,
|
|
321
|
+
defaultViewId,
|
|
322
|
+
isDefault,
|
|
323
|
+
setDefault,
|
|
324
|
+
busy
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
export default useCrmSavedView;
|
|
328
|
+
|
|
329
|
+
//# sourceMappingURL=use-crm-saved-view.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/hooks/use-crm-saved-view.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 CRM_VIEW_NAME_MAX,\n type CrmViewFilterClause,\n type CrmViewSection,\n type CrmViewSort,\n type CrmViewState,\n crmViewStateEquals,\n EMPTY_CRM_VIEW_STATE,\n ORG_SCOPE_TOKEN,\n} from '@aglyn/aglyn'\nimport {\n useFirestore,\n useScopeTokens,\n useUser,\n} from '@aglyn/tenant-feature-instance'\nimport {\n addDoc,\n collection,\n deleteDoc,\n doc,\n updateDoc,\n} from 'firebase/firestore'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { crmRoutes } from '../model/crm-routes'\nimport { crmViewHref, crmViewIdFromParams } from '../model/crm-view-param'\nimport { type CrmOrgDoc, useCrmScope } from './use-crm-scope'\nimport { useCrmDefaultView } from './use-crm-default-view'\nimport { type CrmSavedViewRow, useCrmViews } from './use-crm-views'\n\n/**\n * The address value that means \"no view, whatever my default is\".\n *\n * Without it, choosing the plain list would clear the key and the default\n * would reapply on the next render — the reader could never leave their\n * own default. Written only when a default exists to be overridden, so an\n * address stays clean for a reader who never set one.\n */\nexport const CRM_VIEW_NONE = 'all'\n\n/** What a section holds of its saved views, and can do with them. */\nexport interface CrmSavedViewController {\n section: CrmViewSection\n /** Mine and shared, by name. */\n views: CrmSavedViewRow[]\n /** Whether the listing has settled — a missing view is not missing until it has. */\n ready: boolean\n /** The view the list is showing, when the address or the default names a listed one. */\n current: CrmSavedViewRow | null\n /** The id the address names, listed or not. */\n currentId: string | null\n /** The address names a view the listing does not carry. */\n missing: boolean\n /** The list's working state: what the reader has narrowed, shown and sorted. */\n state: CrmViewState\n setFilters: (filters: CrmViewFilterClause[]) => void\n setColumns: (columns: string[]) => void\n setSort: (sort: CrmViewSort | null) => void\n /** The working state differs from the view it started from. */\n dirty: boolean\n /** The reader may change or remove the current view — its creator, or an org-wide member. */\n canEdit: boolean\n uid: string | null\n /** Open a view by id, or the plain list with `null`. */\n select: (viewId: string | null) => void\n /** Back to the view's saved state, or the plain list's. */\n reset: () => void\n save: () => Promise<void>\n /** Creates, opens, and answers with the new id. */\n saveAs: (name: string, shared: boolean) => Promise<string | null>\n rename: (name: string) => Promise<void>\n setShared: (shared: boolean) => Promise<void>\n remove: () => Promise<void>\n defaultViewId: string | null\n isDefault: boolean\n setDefault: (viewId: string | null) => Promise<void>\n /** A write is in flight. */\n busy: boolean\n}\n\n/**\n * One section's saved views, resolved against its address (AGL-2617).\n *\n * The controller behind the views control on every CRM list. It owns three\n * things the list used to keep in loose state and lose on reload: which\n * view is open, what the reader has changed since, and how that gets back\n * to the document.\n *\n * ## The address is the truth about which view is open\n *\n * `?view=<id>` names the view; choosing one navigates. So a view is a link,\n * Back returns to the one before it, and a reload lands where the reader\n * was. When the address names nothing, the reader's default for this\n * section (their own profile's) opens instead — unless the list was opened\n * FOR something, a form's captures or one address, in which case the plain\n * list shows what it was asked for and nothing narrows it further.\n *\n * ## The seed is kept, first\n *\n * A Contacts address that carries a seed and a view means both: the seed's\n * clause leads the view's, so the query serves the seed (it reaches the\n * whole collection, as it did before views existed) and the view narrows\n * the window. That is why `initial` folds into every baseline rather than\n * being a starting state the view replaces.\n *\n * ## A view's state is applied once per view\n *\n * When the address names a view, its stored filters, columns and sort\n * become the working state the moment the listing carries it — once. A\n * later change to the document does not overwrite what the reader is in\n * the middle of; a change the reader makes is theirs until they save it or\n * reset. The default applies only onto an untouched plain list, so a reader\n * who started narrowing before their profile arrived keeps what they typed.\n */\nexport function useCrmSavedView(options: {\n section: CrmViewSection\n /** The site the list is read under, or `null` at the organization level. */\n hostId: string | null\n org?: CrmOrgDoc\n /** The CRM hub path the section hangs beneath — what the address is built from. */\n basePath: string\n /** What the list was opened for: clauses every baseline keeps in front. */\n initial?: Partial<CrmViewState>\n}): CrmSavedViewController {\n const { section, hostId, org, basePath } = options\n const initialFilters = options.initial?.filters ?? EMPTY_CRM_VIEW_STATE.filters\n const initialColumns = options.initial?.columns ?? EMPTY_CRM_VIEW_STATE.columns\n const initialSort = options.initial?.sort ?? EMPTY_CRM_VIEW_STATE.sort\n const seeded = initialFilters.length > 0\n\n const firestore = useFirestore()\n const router = useRouter()\n const searchParams = useSearchParams()\n const { data: user } = useUser()\n const uid = (user as { uid?: string } | null | undefined)?.uid ?? null\n const { scope, orgId, visibleTo, createTokens } = useCrmScope({ hostId, org })\n const reach = useScopeTokens(orgId ?? undefined)\n const { views, ready } = useCrmViews({ scope, visibleTo, section, uid })\n const {\n defaultViewId,\n ready: defaultReady,\n setDefault: writeDefault,\n } = useCrmDefaultView({ uid, orgId, section })\n\n const sectionPath = useMemo(\n () => crmRoutes(basePath).section(section),\n [basePath, section],\n )\n\n /*\n * Which view the address asks for. `all` is the reader stepping out of\n * their default on purpose; nothing at all defers to the default, except\n * on a seeded list, which shows what it was opened for.\n */\n const addressed = crmViewIdFromParams(searchParams)\n const currentId =\n addressed === CRM_VIEW_NONE\n ? null\n : addressed ?? (seeded ? null : defaultViewId)\n const current = useMemo(\n () => (currentId ? views.find((view) => view.$id === currentId) ?? null : null),\n [views, currentId],\n )\n const missing = Boolean(currentId) && ready && defaultReady && !current\n\n /** What the list opens on for a view — the seed in front — or for none. */\n const baseline = useCallback(\n (view: CrmSavedViewRow | null): CrmViewState =>\n view\n ? {\n filters: [...initialFilters, ...view.filters],\n columns: view.columns.length ? view.columns : initialColumns,\n sort: view.sort ?? initialSort,\n }\n : { filters: initialFilters, columns: initialColumns, sort: initialSort },\n [initialFilters, initialColumns, initialSort],\n )\n\n const [state, setState] = useState<CrmViewState>(() => baseline(null))\n /** The view whose state the working state was last set from — `''` for none. */\n const applied = useRef<string>('')\n const dirty = !crmViewStateEquals(state, baseline(current))\n\n useEffect(() => {\n const target = current?.$id ?? ''\n if (applied.current === target) return\n // A default arriving under a list the reader already narrowed is not\n // allowed to take that narrowing away; an explicit choice always is.\n if (target && !addressed && dirty) return\n applied.current = target\n setState(baseline(current))\n }, [current, addressed, dirty, baseline])\n\n const setFilters = useCallback(\n (filters: CrmViewFilterClause[]) => setState((prev) => ({ ...prev, filters })),\n [],\n )\n const setColumns = useCallback(\n (columns: string[]) => setState((prev) => ({ ...prev, columns })),\n [],\n )\n const setSort = useCallback(\n (sort: CrmViewSort | null) => setState((prev) => ({ ...prev, sort })),\n [],\n )\n const reset = useCallback(() => setState(baseline(current)), [baseline, current])\n\n const select = useCallback(\n (viewId: string | null) => {\n // Leaving a view for the plain list under a default has to SAY so,\n // or the default would reopen on the next render.\n const value = viewId ?? (defaultViewId ? CRM_VIEW_NONE : null)\n router.push(crmViewHref(sectionPath, searchParams, value))\n },\n [router, sectionPath, searchParams, defaultViewId],\n )\n\n const [busy, setBusy] = useState(false)\n const guarded = useCallback(async <T,>(work: () => Promise<T>): Promise<T> => {\n setBusy(true)\n try {\n return await work()\n } finally {\n setBusy(false)\n }\n }, [])\n\n const viewRef = useCallback(\n (viewId: string) =>\n scope ? doc(firestore, scope[0], scope[1], CRM_COLLECTIONS.views, viewId) : null,\n [firestore, scope],\n )\n\n /** The three list-facing fields as the document stores them — without the seed. */\n const stored = useCallback(\n (): CrmViewState => ({\n // The seed is the address's, not the view's: a view saved while a\n // form's captures were open must not carry that form forever.\n filters: state.filters.filter(\n (clause) =>\n !initialFilters.some(\n (seed) =>\n seed.field === clause.field &&\n seed.op === clause.op &&\n seed.value === clause.value,\n ),\n ),\n columns: state.columns,\n sort: state.sort,\n }),\n [state, initialFilters],\n )\n\n const save = useCallback(async () => {\n const ref = current ? viewRef(current.$id) : null\n if (!ref) return\n await guarded(() => updateDoc(ref, { ...stored(), updatedAt: new Date() }))\n }, [current, viewRef, guarded, stored])\n\n const saveAs = useCallback(\n async (name: string, shared: boolean) => {\n const trimmed = name.trim().slice(0, CRM_VIEW_NAME_MAX)\n if (!trimmed || !scope || !uid) return null\n const created = await guarded(() =>\n addDoc(collection(firestore, scope[0], scope[1], CRM_COLLECTIONS.views), {\n section,\n name: trimmed,\n ...stored(),\n ownerUid: uid,\n createdByUid: uid,\n shared,\n // A view made at the organization level belongs to the org\n // (AGL-2630): it names no site, and it is stamped org-wide so\n // every site's reader lists it — unlike a record, a view is a\n // working arrangement, not a person's data.\n hostId: hostId ?? null,\n visibleTo: hostId ? createTokens : [ORG_SCOPE_TOKEN],\n createdAt: new Date(),\n updatedAt: new Date(),\n }),\n )\n // The new view's state IS the working state, so nothing is lost when\n // the address changes and the listing catches up.\n applied.current = created.id\n select(created.id)\n return created.id\n },\n [scope, uid, guarded, firestore, section, stored, hostId, createTokens, select],\n )\n\n const rename = useCallback(\n async (name: string) => {\n const trimmed = name.trim().slice(0, CRM_VIEW_NAME_MAX)\n const ref = current ? viewRef(current.$id) : null\n if (!trimmed || !ref) return\n await guarded(() => updateDoc(ref, { name: trimmed, updatedAt: new Date() }))\n },\n [current, viewRef, guarded],\n )\n\n const setShared = useCallback(\n async (shared: boolean) => {\n const ref = current ? viewRef(current.$id) : null\n if (!ref) return\n await guarded(() => updateDoc(ref, { shared, updatedAt: new Date() }))\n },\n [current, viewRef, guarded],\n )\n\n const isDefault = Boolean(currentId) && defaultViewId === currentId\n\n const setDefault = useCallback(\n async (viewId: string | null) => {\n await guarded(() => writeDefault(viewId))\n },\n [guarded, writeDefault],\n )\n\n const remove = useCallback(async () => {\n const ref = current ? viewRef(current.$id) : null\n if (!ref || !current) return\n await guarded(async () => {\n await deleteDoc(ref)\n // A default pointing at nothing would open every visit on \"missing\".\n if (defaultViewId === current.$id) await writeDefault(null)\n })\n applied.current = ''\n select(null)\n }, [current, viewRef, guarded, defaultViewId, writeDefault, select])\n\n const canEdit = Boolean(\n current &&\n uid &&\n (current.createdByUid === uid || (reach.loaded && reach.orgWide)),\n )\n\n return {\n section,\n views,\n ready: ready && defaultReady,\n current,\n currentId,\n missing,\n state,\n setFilters,\n setColumns,\n setSort,\n dirty,\n canEdit,\n uid,\n select,\n reset,\n save,\n saveAs,\n rename,\n setShared,\n remove,\n defaultViewId,\n isDefault,\n setDefault,\n busy,\n }\n}\n\nexport default useCrmSavedView\n"],"names":["CRM_COLLECTIONS","CRM_VIEW_NAME_MAX","crmViewStateEquals","EMPTY_CRM_VIEW_STATE","ORG_SCOPE_TOKEN","useFirestore","useScopeTokens","useUser","addDoc","collection","deleteDoc","doc","updateDoc","useRouter","useSearchParams","useCallback","useEffect","useMemo","useRef","useState","crmRoutes","crmViewHref","crmViewIdFromParams","useCrmScope","useCrmDefaultView","useCrmViews","CRM_VIEW_NONE","useCrmSavedView","options","section","hostId","org","basePath","initialFilters","initial","filters","initialColumns","columns","initialSort","sort","seeded","length","firestore","router","searchParams","data","user","uid","scope","orgId","visibleTo","createTokens","reach","undefined","views","ready","defaultViewId","defaultReady","setDefault","writeDefault","sectionPath","addressed","currentId","current","find","view","$id","missing","Boolean","baseline","state","setState","applied","dirty","target","setFilters","prev","setColumns","setSort","reset","select","viewId","value","push","busy","setBusy","guarded","work","viewRef","stored","filter","clause","some","seed","field","op","save","ref","updatedAt","Date","saveAs","name","shared","trimmed","trim","slice","created","ownerUid","createdByUid","createdAt","id","rename","setShared","isDefault","remove","canEdit","loaded","orgWide"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SACEA,eAAe,EACfC,iBAAiB,EAKjBC,kBAAkB,EAClBC,oBAAoB,EACpBC,eAAe,QACV,eAAc;AACrB,SACEC,YAAY,EACZC,cAAc,EACdC,OAAO,QACF,iCAAgC;AACvC,SACEC,MAAM,EACNC,UAAU,EACVC,SAAS,EACTC,GAAG,EACHC,SAAS,QACJ,qBAAoB;AAC3B,SAASC,SAAS,EAAEC,eAAe,QAAQ,kBAAiB;AAC5D,SAASC,WAAW,EAAEC,SAAS,EAAEC,OAAO,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,QAAO;AACzE,SAASC,SAAS,QAAQ,yBAAqB;AAC/C,SAASC,WAAW,EAAEC,mBAAmB,QAAQ,6BAAyB;AAC1E,SAAyBC,WAAW,QAAQ,qBAAiB;AAC7D,SAASC,iBAAiB,QAAQ,4BAAwB;AAC1D,SAA+BC,WAAW,QAAQ,qBAAiB;AAEnE;;;;;;;CAOC,GACD,OAAO,MAAMC,gBAAgB,MAAK;AA0ClC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiCC,GACD,OAAO,SAASC,gBAAgBC,OAS/B;;QAEwBA,kBACAA,mBACHA;IAHpB,MAAM,EAAEC,OAAO,EAAEC,MAAM,EAAEC,GAAG,EAAEC,QAAQ,EAAE,GAAGJ;IAC3C,MAAMK,0BAAiBL,mBAAAA,QAAQM,OAAO,qBAAfN,iBAAiBO,OAAO,mBAAIhC,qBAAqBgC,OAAO;IAC/E,MAAMC,2BAAiBR,oBAAAA,QAAQM,OAAO,qBAAfN,kBAAiBS,OAAO,oBAAIlC,qBAAqBkC,OAAO;IAC/E,MAAMC,wBAAcV,oBAAAA,QAAQM,OAAO,qBAAfN,kBAAiBW,IAAI,oBAAIpC,qBAAqBoC,IAAI;IACtE,MAAMC,SAASP,eAAeQ,MAAM,GAAG;IAEvC,MAAMC,YAAYrC;IAClB,MAAMsC,SAAS9B;IACf,MAAM+B,eAAe9B;IACrB,MAAM,EAAE+B,MAAMC,IAAI,EAAE,GAAGvC;IACvB,MAAMwC,eAAOD,wBAAD,AAACA,KAA8CC,GAAG,oBAAI;IAClE,MAAM,EAAEC,KAAK,EAAEC,KAAK,EAAEC,SAAS,EAAEC,YAAY,EAAE,GAAG5B,YAAY;QAAEO;QAAQC;IAAI;IAC5E,MAAMqB,QAAQ9C,eAAe2C,gBAAAA,QAASI;IACtC,MAAM,EAAEC,KAAK,EAAEC,KAAK,EAAE,GAAG9B,YAAY;QAAEuB;QAAOE;QAAWrB;QAASkB;IAAI;IACtE,MAAM,EACJS,aAAa,EACbD,OAAOE,YAAY,EACnBC,YAAYC,YAAY,EACzB,GAAGnC,kBAAkB;QAAEuB;QAAKE;QAAOpB;IAAQ;IAE5C,MAAM+B,cAAc3C,QAClB,IAAMG,UAAUY,UAAUH,OAAO,CAACA,UAClC;QAACG;QAAUH;KAAQ;IAGrB;;;;GAIC,GACD,MAAMgC,YAAYvC,oBAAoBsB;IACtC,MAAMkB,YACJD,cAAcnC,gBACV,OACAmC,oBAAAA,YAAcrB,SAAS,OAAOgB;IACpC,MAAMO,UAAU9C,QACd;YAAmBqC;eAAZQ,aAAYR,cAAAA,MAAMU,IAAI,CAAC,CAACC,OAASA,KAAKC,GAAG,KAAKJ,sBAAlCR,cAAgD,OAAO;OAC1E;QAACA;QAAOQ;KAAU;IAEpB,MAAMK,UAAUC,QAAQN,cAAcP,SAASE,gBAAgB,CAACM;IAEhE,yEAAyE,GACzE,MAAMM,WAAWtD,YACf,CAACkD;YAKaA;eAJZA,OACI;YACE9B,SAAS;mBAAIF;mBAAmBgC,KAAK9B,OAAO;aAAC;YAC7CE,SAAS4B,KAAK5B,OAAO,CAACI,MAAM,GAAGwB,KAAK5B,OAAO,GAAGD;YAC9CG,IAAI,GAAE0B,aAAAA,KAAK1B,IAAI,YAAT0B,aAAa3B;QACrB,IACA;YAAEH,SAASF;YAAgBI,SAASD;YAAgBG,MAAMD;QAAY;OAC5E;QAACL;QAAgBG;QAAgBE;KAAY;IAG/C,MAAM,CAACgC,OAAOC,SAAS,GAAGpD,SAAuB,IAAMkD,SAAS;IAChE,8EAA8E,GAC9E,MAAMG,UAAUtD,OAAe;IAC/B,MAAMuD,QAAQ,CAACvE,mBAAmBoE,OAAOD,SAASN;IAElD/C,UAAU;;QACR,MAAM0D,iBAASX,2BAAAA,QAASG,GAAG,mBAAI;QAC/B,IAAIM,QAAQT,OAAO,KAAKW,QAAQ;QAChC,qEAAqE;QACrE,qEAAqE;QACrE,IAAIA,UAAU,CAACb,aAAaY,OAAO;QACnCD,QAAQT,OAAO,GAAGW;QAClBH,SAASF,SAASN;IACpB,GAAG;QAACA;QAASF;QAAWY;QAAOJ;KAAS;IAExC,MAAMM,aAAa5D,YACjB,CAACoB,UAAmCoC,SAAS,CAACK,OAAU,aAAKA;gBAAMzC;iBACnE,EAAE;IAEJ,MAAM0C,aAAa9D,YACjB,CAACsB,UAAsBkC,SAAS,CAACK,OAAU,aAAKA;gBAAMvC;iBACtD,EAAE;IAEJ,MAAMyC,UAAU/D,YACd,CAACwB,OAA6BgC,SAAS,CAACK,OAAU,aAAKA;gBAAMrC;iBAC7D,EAAE;IAEJ,MAAMwC,QAAQhE,YAAY,IAAMwD,SAASF,SAASN,WAAW;QAACM;QAAUN;KAAQ;IAEhF,MAAMiB,SAASjE,YACb,CAACkE;QACC,mEAAmE;QACnE,kDAAkD;QAClD,MAAMC,QAAQD,iBAAAA,SAAWzB,gBAAgB9B,gBAAgB;QACzDiB,OAAOwC,IAAI,CAAC9D,YAAYuC,aAAahB,cAAcsC;IACrD,GACA;QAACvC;QAAQiB;QAAahB;QAAcY;KAAc;IAGpD,MAAM,CAAC4B,MAAMC,QAAQ,GAAGlE,SAAS;IACjC,MAAMmE,UAAUvE,YAAY,OAAWwE;QACrCF,QAAQ;QACR,IAAI;YACF,OAAO,MAAME;QACf,SAAU;YACRF,QAAQ;QACV;IACF,GAAG,EAAE;IAEL,MAAMG,UAAUzE,YACd,CAACkE,SACCjC,QAAQrC,IAAI+B,WAAWM,KAAK,CAAC,EAAE,EAAEA,KAAK,CAAC,EAAE,EAAEhD,gBAAgBsD,KAAK,EAAE2B,UAAU,MAC9E;QAACvC;QAAWM;KAAM;IAGpB,iFAAiF,GACjF,MAAMyC,SAAS1E,YACb,IAAqB,CAAA;YACnB,kEAAkE;YAClE,8DAA8D;YAC9DoB,SAASmC,MAAMnC,OAAO,CAACuD,MAAM,CAC3B,CAACC,SACC,CAAC1D,eAAe2D,IAAI,CAClB,CAACC,OACCA,KAAKC,KAAK,KAAKH,OAAOG,KAAK,IAC3BD,KAAKE,EAAE,KAAKJ,OAAOI,EAAE,IACrBF,KAAKX,KAAK,KAAKS,OAAOT,KAAK;YAGnC7C,SAASiC,MAAMjC,OAAO;YACtBE,MAAM+B,MAAM/B,IAAI;QAClB,CAAA,GACA;QAAC+B;QAAOrC;KAAe;IAGzB,MAAM+D,OAAOjF,YAAY;QACvB,MAAMkF,MAAMlC,UAAUyB,QAAQzB,QAAQG,GAAG,IAAI;QAC7C,IAAI,CAAC+B,KAAK;QACV,MAAMX,QAAQ,IAAM1E,UAAUqF,KAAK,aAAKR;gBAAUS,WAAW,IAAIC;;IACnE,GAAG;QAACpC;QAASyB;QAASF;QAASG;KAAO;IAEtC,MAAMW,SAASrF,YACb,OAAOsF,MAAcC;QACnB,MAAMC,UAAUF,KAAKG,IAAI,GAAGC,KAAK,CAAC,GAAGxG;QACrC,IAAI,CAACsG,WAAW,CAACvD,SAAS,CAACD,KAAK,OAAO;QACvC,MAAM2D,UAAU,MAAMpB,QAAQ,IAC5B9E,OAAOC,WAAWiC,WAAWM,KAAK,CAAC,EAAE,EAAEA,KAAK,CAAC,EAAE,EAAEhD,gBAAgBsD,KAAK,GAAG;gBACvEzB;gBACAwE,MAAME;eACHd;gBACHkB,UAAU5D;gBACV6D,cAAc7D;gBACduD;gBACA,2DAA2D;gBAC3D,8DAA8D;gBAC9D,8DAA8D;gBAC9D,4CAA4C;gBAC5CxE,MAAM,EAAEA,iBAAAA,SAAU;gBAClBoB,WAAWpB,SAASqB,eAAe;oBAAC/C;iBAAgB;gBACpDyG,WAAW,IAAIV;gBACfD,WAAW,IAAIC;;QAGnB,qEAAqE;QACrE,kDAAkD;QAClD3B,QAAQT,OAAO,GAAG2C,QAAQI,EAAE;QAC5B9B,OAAO0B,QAAQI,EAAE;QACjB,OAAOJ,QAAQI,EAAE;IACnB,GACA;QAAC9D;QAAOD;QAAKuC;QAAS5C;QAAWb;QAAS4D;QAAQ3D;QAAQqB;QAAc6B;KAAO;IAGjF,MAAM+B,SAAShG,YACb,OAAOsF;QACL,MAAME,UAAUF,KAAKG,IAAI,GAAGC,KAAK,CAAC,GAAGxG;QACrC,MAAMgG,MAAMlC,UAAUyB,QAAQzB,QAAQG,GAAG,IAAI;QAC7C,IAAI,CAACqC,WAAW,CAACN,KAAK;QACtB,MAAMX,QAAQ,IAAM1E,UAAUqF,KAAK;gBAAEI,MAAME;gBAASL,WAAW,IAAIC;YAAO;IAC5E,GACA;QAACpC;QAASyB;QAASF;KAAQ;IAG7B,MAAM0B,YAAYjG,YAChB,OAAOuF;QACL,MAAML,MAAMlC,UAAUyB,QAAQzB,QAAQG,GAAG,IAAI;QAC7C,IAAI,CAAC+B,KAAK;QACV,MAAMX,QAAQ,IAAM1E,UAAUqF,KAAK;gBAAEK;gBAAQJ,WAAW,IAAIC;YAAO;IACrE,GACA;QAACpC;QAASyB;QAASF;KAAQ;IAG7B,MAAM2B,YAAY7C,QAAQN,cAAcN,kBAAkBM;IAE1D,MAAMJ,aAAa3C,YACjB,OAAOkE;QACL,MAAMK,QAAQ,IAAM3B,aAAasB;IACnC,GACA;QAACK;QAAS3B;KAAa;IAGzB,MAAMuD,SAASnG,YAAY;QACzB,MAAMkF,MAAMlC,UAAUyB,QAAQzB,QAAQG,GAAG,IAAI;QAC7C,IAAI,CAAC+B,OAAO,CAAClC,SAAS;QACtB,MAAMuB,QAAQ;YACZ,MAAM5E,UAAUuF;YAChB,qEAAqE;YACrE,IAAIzC,kBAAkBO,QAAQG,GAAG,EAAE,MAAMP,aAAa;QACxD;QACAa,QAAQT,OAAO,GAAG;QAClBiB,OAAO;IACT,GAAG;QAACjB;QAASyB;QAASF;QAAS9B;QAAeG;QAAcqB;KAAO;IAEnE,MAAMmC,UAAU/C,QACdL,WACEhB,OACCgB,CAAAA,QAAQ6C,YAAY,KAAK7D,OAAQK,MAAMgE,MAAM,IAAIhE,MAAMiE,OAAO;IAGnE,OAAO;QACLxF;QACAyB;QACAC,OAAOA,SAASE;QAChBM;QACAD;QACAK;QACAG;QACAK;QACAE;QACAC;QACAL;QACA0C;QACApE;QACAiC;QACAD;QACAiB;QACAI;QACAW;QACAC;QACAE;QACA1D;QACAyD;QACAvD;QACA0B;IACF;AACF;AAEA,eAAezD,gBAAe"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { type ConsentGroup, type ConsolePluginPageProps, type ScopeToken } from '@aglyn/aglyn';
|
|
2
|
+
import { type QueryConstraint } from 'firebase/firestore';
|
|
3
|
+
/**
|
|
4
|
+
* The org document as a CRM surface receives it: the shell's typed billing
|
|
5
|
+
* projection on a page, or the plain record a card was handed. Read as a
|
|
6
|
+
* record at the one seam below, so no component casts it itself.
|
|
7
|
+
*/
|
|
8
|
+
export type CrmOrgDoc = ConsolePluginPageProps['org'] | Record<string, unknown> | null | undefined;
|
|
9
|
+
/** Where a CRM surface is mounted: under one site, or over the whole org. */
|
|
10
|
+
export type CrmScopeLevel = 'site' | 'org';
|
|
11
|
+
export interface CrmScope {
|
|
12
|
+
/** The org data root, or `null` until the org lookup settles or when there is none. */
|
|
13
|
+
scope: readonly ['orgs', string] | null;
|
|
14
|
+
/** The owning org, or null while the lookup runs or when there is none. */
|
|
15
|
+
orgId: string | null;
|
|
16
|
+
/** Whether the org lookup has settled — `orgId` null after this means no org. */
|
|
17
|
+
ready: boolean;
|
|
18
|
+
/** Under a site, or at the organization level (AGL-2630). */
|
|
19
|
+
level: CrmScopeLevel;
|
|
20
|
+
/** The mounted site, or `null` at the organization level. */
|
|
21
|
+
hostId: string | null;
|
|
22
|
+
/**
|
|
23
|
+
* The controller this surface is being viewed as, or `null` at the org
|
|
24
|
+
* level, where there is no one viewing site: a cross-holder reader
|
|
25
|
+
* resolves each contact's own facet instead (`contactPrimaryGroup`).
|
|
26
|
+
*/
|
|
27
|
+
consentGroup: ConsentGroup | null;
|
|
28
|
+
/**
|
|
29
|
+
* What this viewer may LIST — every listener's `array-contains-any`
|
|
30
|
+
* predicate, capped at what the operator accepts — or `null` at the org
|
|
31
|
+
* level, where an org-wide member's listeners carry NO scope clause: the
|
|
32
|
+
* rules' `canReadScoped()` short-circuits on `isOrgWideMember()`, so the
|
|
33
|
+
* clause would only ever narrow what the reader is already allowed. Hand
|
|
34
|
+
* it to {@link crmVisibleToClause}, which spells both cases. Stable across
|
|
35
|
+
* renders for the same org and site, so it can sit in a query's
|
|
36
|
+
* dependency list.
|
|
37
|
+
*/
|
|
38
|
+
visibleTo: readonly ScopeToken[] | null;
|
|
39
|
+
/**
|
|
40
|
+
* The site a record this viewer CREATES is captured by: the mounted site,
|
|
41
|
+
* or at the org level the site the reader picked (`null` until they
|
|
42
|
+
* have). A create must hold until this is known — the tokens below are
|
|
43
|
+
* empty without it, and a record stamped with none is visible to nobody.
|
|
44
|
+
*/
|
|
45
|
+
createHostId: string | null;
|
|
46
|
+
/** The consent group of {@link createHostId} — what a create records consent for. */
|
|
47
|
+
createGroup: ConsentGroup | null;
|
|
48
|
+
/** What a record this viewer CREATES is stamped with; empty until {@link createHostId} is known. */
|
|
49
|
+
createTokens: readonly ScopeToken[];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The scope facts every CRM surface needs, resolved once (AGL-2597, one
|
|
53
|
+
* hook since AGL-2614, two levels since AGL-2630).
|
|
54
|
+
*
|
|
55
|
+
* A CRM surface — a list, a record page, a drawer, a card on somebody
|
|
56
|
+
* else's page — resolves its consent group, builds the token list its
|
|
57
|
+
* listener filters on, builds the tokens a create stamps, and looks up the
|
|
58
|
+
* org root. Every section needs the same answers, and for a while the deals
|
|
59
|
+
* had a hook of their own that answered them under different names
|
|
60
|
+
* (`readTokens` for `visibleTo`), which is two chances to filter by one set
|
|
61
|
+
* of tokens and stamp with another. This is the one place the expression
|
|
62
|
+
* lives: a surface that spelled its own could omit the `'org'` token and
|
|
63
|
+
* stop listing the org-wide records the rules would have let it read.
|
|
64
|
+
*
|
|
65
|
+
* ## Two levels, one call
|
|
66
|
+
*
|
|
67
|
+
* Under a site, `hostId` names it and everything follows from the org
|
|
68
|
+
* document and that id, as it always has. At the ORGANIZATION level the
|
|
69
|
+
* hub hands every surface `hostId: null`, and the org comes from the mount
|
|
70
|
+
* the hub published (`useCrmOrgMount`): the org root is known at once,
|
|
71
|
+
* reads are unscoped, and the site a create stamps from is the one the
|
|
72
|
+
* reader picked. A surface written against `{ scope, visibleTo,
|
|
73
|
+
* createTokens }` serves both levels without knowing which it is on — the
|
|
74
|
+
* two differences it cannot ignore are that `visibleTo` and `consentGroup`
|
|
75
|
+
* are `null` at the org level, and both are typed to say so.
|
|
76
|
+
*
|
|
77
|
+
* `visibleTo` and `createTokens` are different questions with different
|
|
78
|
+
* answers. The first is the union of everything this viewer may see; the
|
|
79
|
+
* second is where a new record lands, which `crmScopeTokens` decides from
|
|
80
|
+
* the org's default and is narrower — a group of one site creates for that
|
|
81
|
+
* site, and reads the org-wide rows beside its own.
|
|
82
|
+
*
|
|
83
|
+
* Pure over the org document the shell already passed, so it costs no read;
|
|
84
|
+
* only the org lookup behind `useOrgDataScope` is asynchronous, and `ready`
|
|
85
|
+
* is how a surface tells "not yet" from "no org".
|
|
86
|
+
*/
|
|
87
|
+
export declare function useCrmScope(props: {
|
|
88
|
+
hostId: string | null | undefined;
|
|
89
|
+
org?: CrmOrgDoc;
|
|
90
|
+
}): CrmScope;
|
|
91
|
+
/**
|
|
92
|
+
* The scope clause a CRM listener carries, spelled once for both levels
|
|
93
|
+
* (AGL-2630).
|
|
94
|
+
*
|
|
95
|
+
* Under a site: `where('visibleTo', 'array-contains-any', tokens)`, the
|
|
96
|
+
* predicate the rules evaluate with `hasAny`, which is what makes a
|
|
97
|
+
* filtered query provable per document. At the org level: NOTHING — the
|
|
98
|
+
* reader is an org-wide member, the rules admit them to every row, and a
|
|
99
|
+
* query that kept the clause would need the org's whole site list on it
|
|
100
|
+
* and still miss a site the list did not carry. Spread into the query, so
|
|
101
|
+
* the two cases are one line at every listener: `...crmVisibleToClause(
|
|
102
|
+
* scope.visibleTo)`.
|
|
103
|
+
*
|
|
104
|
+
* A query that drops an `array-contains-any` may need a composite index
|
|
105
|
+
* the scoped form did not: the equality and range clauses beside it stay,
|
|
106
|
+
* and Firestore composes them on their own. The org-level entries live in
|
|
107
|
+
* `cloud/firebase-firestore.indexes.json` beside their scoped twins.
|
|
108
|
+
*/
|
|
109
|
+
export declare function crmVisibleToClause(visibleTo: readonly ScopeToken[] | readonly string[] | null | undefined): QueryConstraint[];
|
|
110
|
+
/**
|
|
111
|
+
* Whether a listener that filters on `visibleTo` has anything to ask for:
|
|
112
|
+
* `true` at the org level (no clause is a complete question), and under a
|
|
113
|
+
* site only when the token list is non-empty — an `array-contains-any`
|
|
114
|
+
* over nothing is a query Firestore refuses.
|
|
115
|
+
*/
|
|
116
|
+
export declare function crmScopeListable(visibleTo: readonly ScopeToken[] | readonly string[] | null | undefined): boolean;
|
|
117
|
+
export default useCrmScope;
|
|
@@ -0,0 +1,147 @@
|
|
|
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 { consentGroupForHost, crmReadTokens, crmScopeTokens } from "@aglyn/aglyn";
|
|
18
|
+
import { useOrgDataScope } from "@aglyn/tenant-feature-instance";
|
|
19
|
+
import { where } from "firebase/firestore";
|
|
20
|
+
import { useMemo } from "react";
|
|
21
|
+
import { useCrmOrgMount } from "./use-crm-org-mount.js";
|
|
22
|
+
const NO_TOKENS = Object.freeze([]);
|
|
23
|
+
/**
|
|
24
|
+
* The scope facts every CRM surface needs, resolved once (AGL-2597, one
|
|
25
|
+
* hook since AGL-2614, two levels since AGL-2630).
|
|
26
|
+
*
|
|
27
|
+
* A CRM surface — a list, a record page, a drawer, a card on somebody
|
|
28
|
+
* else's page — resolves its consent group, builds the token list its
|
|
29
|
+
* listener filters on, builds the tokens a create stamps, and looks up the
|
|
30
|
+
* org root. Every section needs the same answers, and for a while the deals
|
|
31
|
+
* had a hook of their own that answered them under different names
|
|
32
|
+
* (`readTokens` for `visibleTo`), which is two chances to filter by one set
|
|
33
|
+
* of tokens and stamp with another. This is the one place the expression
|
|
34
|
+
* lives: a surface that spelled its own could omit the `'org'` token and
|
|
35
|
+
* stop listing the org-wide records the rules would have let it read.
|
|
36
|
+
*
|
|
37
|
+
* ## Two levels, one call
|
|
38
|
+
*
|
|
39
|
+
* Under a site, `hostId` names it and everything follows from the org
|
|
40
|
+
* document and that id, as it always has. At the ORGANIZATION level the
|
|
41
|
+
* hub hands every surface `hostId: null`, and the org comes from the mount
|
|
42
|
+
* the hub published (`useCrmOrgMount`): the org root is known at once,
|
|
43
|
+
* reads are unscoped, and the site a create stamps from is the one the
|
|
44
|
+
* reader picked. A surface written against `{ scope, visibleTo,
|
|
45
|
+
* createTokens }` serves both levels without knowing which it is on — the
|
|
46
|
+
* two differences it cannot ignore are that `visibleTo` and `consentGroup`
|
|
47
|
+
* are `null` at the org level, and both are typed to say so.
|
|
48
|
+
*
|
|
49
|
+
* `visibleTo` and `createTokens` are different questions with different
|
|
50
|
+
* answers. The first is the union of everything this viewer may see; the
|
|
51
|
+
* second is where a new record lands, which `crmScopeTokens` decides from
|
|
52
|
+
* the org's default and is narrower — a group of one site creates for that
|
|
53
|
+
* site, and reads the org-wide rows beside its own.
|
|
54
|
+
*
|
|
55
|
+
* Pure over the org document the shell already passed, so it costs no read;
|
|
56
|
+
* only the org lookup behind `useOrgDataScope` is asynchronous, and `ready`
|
|
57
|
+
* is how a surface tells "not yet" from "no org".
|
|
58
|
+
*/ export function useCrmScope(props) {
|
|
59
|
+
var _props_org, _ref;
|
|
60
|
+
const hostId = props.hostId || null;
|
|
61
|
+
const org = (_props_org = props.org) != null ? _props_org : null;
|
|
62
|
+
const mount = useCrmOrgMount();
|
|
63
|
+
// Under a site the org is looked up from the host index; at the org level
|
|
64
|
+
// it is the mount's, known at once. A `null` host with no mount is a
|
|
65
|
+
// surface mounted nowhere, and resolves to no org rather than guessing.
|
|
66
|
+
const { scope, orgId, ready } = useOrgDataScope(hostId ? {
|
|
67
|
+
hostId
|
|
68
|
+
} : {
|
|
69
|
+
orgId: mount == null ? void 0 : mount.orgId
|
|
70
|
+
});
|
|
71
|
+
const level = hostId ? 'site' : 'org';
|
|
72
|
+
const consentGroup = useMemo(()=>hostId ? consentGroupForHost(org, hostId) : null, [
|
|
73
|
+
org,
|
|
74
|
+
hostId
|
|
75
|
+
]);
|
|
76
|
+
const visibleTo = useMemo(()=>consentGroup ? crmReadTokens(consentGroup) : null, [
|
|
77
|
+
consentGroup
|
|
78
|
+
]);
|
|
79
|
+
const createHostId = (_ref = hostId != null ? hostId : mount == null ? void 0 : mount.createHostId) != null ? _ref : null;
|
|
80
|
+
const createGroup = useMemo(()=>createHostId ? consentGroupForHost(org, createHostId) : null, [
|
|
81
|
+
org,
|
|
82
|
+
createHostId
|
|
83
|
+
]);
|
|
84
|
+
const createTokens = useMemo(()=>createGroup ? crmScopeTokens(org, createGroup) : NO_TOKENS, [
|
|
85
|
+
org,
|
|
86
|
+
createGroup
|
|
87
|
+
]);
|
|
88
|
+
return useMemo(()=>({
|
|
89
|
+
scope,
|
|
90
|
+
orgId,
|
|
91
|
+
ready,
|
|
92
|
+
level,
|
|
93
|
+
hostId,
|
|
94
|
+
consentGroup,
|
|
95
|
+
visibleTo,
|
|
96
|
+
createHostId,
|
|
97
|
+
createGroup,
|
|
98
|
+
createTokens
|
|
99
|
+
}), [
|
|
100
|
+
scope,
|
|
101
|
+
orgId,
|
|
102
|
+
ready,
|
|
103
|
+
level,
|
|
104
|
+
hostId,
|
|
105
|
+
consentGroup,
|
|
106
|
+
visibleTo,
|
|
107
|
+
createHostId,
|
|
108
|
+
createGroup,
|
|
109
|
+
createTokens
|
|
110
|
+
]);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* The scope clause a CRM listener carries, spelled once for both levels
|
|
114
|
+
* (AGL-2630).
|
|
115
|
+
*
|
|
116
|
+
* Under a site: `where('visibleTo', 'array-contains-any', tokens)`, the
|
|
117
|
+
* predicate the rules evaluate with `hasAny`, which is what makes a
|
|
118
|
+
* filtered query provable per document. At the org level: NOTHING — the
|
|
119
|
+
* reader is an org-wide member, the rules admit them to every row, and a
|
|
120
|
+
* query that kept the clause would need the org's whole site list on it
|
|
121
|
+
* and still miss a site the list did not carry. Spread into the query, so
|
|
122
|
+
* the two cases are one line at every listener: `...crmVisibleToClause(
|
|
123
|
+
* scope.visibleTo)`.
|
|
124
|
+
*
|
|
125
|
+
* A query that drops an `array-contains-any` may need a composite index
|
|
126
|
+
* the scoped form did not: the equality and range clauses beside it stay,
|
|
127
|
+
* and Firestore composes them on their own. The org-level entries live in
|
|
128
|
+
* `cloud/firebase-firestore.indexes.json` beside their scoped twins.
|
|
129
|
+
*/ export function crmVisibleToClause(visibleTo) {
|
|
130
|
+
if (!visibleTo) return [];
|
|
131
|
+
return [
|
|
132
|
+
where('visibleTo', 'array-contains-any', [
|
|
133
|
+
...visibleTo
|
|
134
|
+
])
|
|
135
|
+
];
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Whether a listener that filters on `visibleTo` has anything to ask for:
|
|
139
|
+
* `true` at the org level (no clause is a complete question), and under a
|
|
140
|
+
* site only when the token list is non-empty — an `array-contains-any`
|
|
141
|
+
* over nothing is a query Firestore refuses.
|
|
142
|
+
*/ export function crmScopeListable(visibleTo) {
|
|
143
|
+
return visibleTo === null || visibleTo === undefined || visibleTo.length > 0;
|
|
144
|
+
}
|
|
145
|
+
export default useCrmScope;
|
|
146
|
+
|
|
147
|
+
//# sourceMappingURL=use-crm-scope.js.map
|