@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,36 @@
|
|
|
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
|
+
*/
|
|
17
|
+
import { type CrmMemberOption } from '@aglyn/aglyn';
|
|
18
|
+
/** One person a task can be assigned to. */
|
|
19
|
+
export interface OrgMemberOption extends CrmMemberOption {
|
|
20
|
+
role: string;
|
|
21
|
+
}
|
|
22
|
+
/** Test seam. */
|
|
23
|
+
export declare function resetOrgMemberDirectoryCache(): void;
|
|
24
|
+
export interface OrgMemberDirectory {
|
|
25
|
+
members: OrgMemberOption[];
|
|
26
|
+
/** The read has not settled — the picker disables rather than offers nobody. */
|
|
27
|
+
loading: boolean;
|
|
28
|
+
/** Why the roster could not be read, when it could not. */
|
|
29
|
+
error: string | null;
|
|
30
|
+
/**
|
|
31
|
+
* A stored reference — a uid, or an address the roster has — as a person,
|
|
32
|
+
* or the reference itself for someone no longer on the roster.
|
|
33
|
+
*/
|
|
34
|
+
nameOf: (ref: string | null | undefined) => string;
|
|
35
|
+
}
|
|
36
|
+
export declare function useOrgMemberDirectory(orgId: string | null | undefined): OrgMemberDirectory;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2026 Aglyn LLC
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/ import { crmMemberOption, findOrgMember } from "@aglyn/aglyn";
|
|
18
|
+
import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
|
|
19
|
+
import { useUser } from "@aglyn/tenant-feature-instance";
|
|
20
|
+
import { useCallback, useEffect, useState } from "react";
|
|
21
|
+
/**
|
|
22
|
+
* The org's roster, read once per org per page and shared by every mount.
|
|
23
|
+
*
|
|
24
|
+
* The roster is what the assignee picker offers and what the assignee
|
|
25
|
+
* column prints — a uid is not a name anybody recognizes. It comes from
|
|
26
|
+
* `GET /api/orgs/members`, which re-derives membership with the Admin SDK
|
|
27
|
+
* (the client-side rules cannot evaluate a list query against the member's
|
|
28
|
+
* own-document clause, so a direct read is refused for the very people who
|
|
29
|
+
* may assign tasks). A roster changes on the order of days; the promise is
|
|
30
|
+
* kept so a second card on the same page shares the request rather than
|
|
31
|
+
* repeating it.
|
|
32
|
+
*/ const rosterCache = new Map();
|
|
33
|
+
/** Test seam. */ export function resetOrgMemberDirectoryCache() {
|
|
34
|
+
rosterCache.clear();
|
|
35
|
+
}
|
|
36
|
+
function memberOption(member) {
|
|
37
|
+
var _member_role;
|
|
38
|
+
const option = crmMemberOption(member);
|
|
39
|
+
return option ? _extends({}, option, {
|
|
40
|
+
role: String((_member_role = member['role']) != null ? _member_role : '')
|
|
41
|
+
}) : null;
|
|
42
|
+
}
|
|
43
|
+
export function useOrgMemberDirectory(orgId) {
|
|
44
|
+
const { data: user } = useUser();
|
|
45
|
+
const [state, setState] = useState({
|
|
46
|
+
orgId: null,
|
|
47
|
+
members: [],
|
|
48
|
+
loading: false,
|
|
49
|
+
error: null
|
|
50
|
+
});
|
|
51
|
+
useEffect(()=>{
|
|
52
|
+
if (!orgId || !user) return;
|
|
53
|
+
let active = true;
|
|
54
|
+
let roster = rosterCache.get(orgId);
|
|
55
|
+
if (!roster) {
|
|
56
|
+
roster = authorizedFetch(user, `/api/orgs/members?orgId=${encodeURIComponent(orgId)}`).then(async (response)=>{
|
|
57
|
+
var _payload_members;
|
|
58
|
+
const payload = await response.json().catch(()=>({}));
|
|
59
|
+
if (!response.ok) {
|
|
60
|
+
throw new Error(payload.error || 'The team could not be loaded.');
|
|
61
|
+
}
|
|
62
|
+
return ((_payload_members = payload.members) != null ? _payload_members : []).map(memberOption).filter((member)=>member !== null).sort((a, b)=>a.label.localeCompare(b.label));
|
|
63
|
+
});
|
|
64
|
+
rosterCache.set(orgId, roster);
|
|
65
|
+
// A failed read is not kept: the next mount asks again rather than
|
|
66
|
+
// remembering an outage for the life of the page.
|
|
67
|
+
roster.catch(()=>rosterCache.delete(orgId));
|
|
68
|
+
}
|
|
69
|
+
setState({
|
|
70
|
+
orgId,
|
|
71
|
+
members: [],
|
|
72
|
+
loading: true,
|
|
73
|
+
error: null
|
|
74
|
+
});
|
|
75
|
+
roster.then((members)=>{
|
|
76
|
+
if (active) setState({
|
|
77
|
+
orgId,
|
|
78
|
+
members,
|
|
79
|
+
loading: false,
|
|
80
|
+
error: null
|
|
81
|
+
});
|
|
82
|
+
}, (error)=>{
|
|
83
|
+
if (active) {
|
|
84
|
+
setState({
|
|
85
|
+
orgId,
|
|
86
|
+
members: [],
|
|
87
|
+
loading: false,
|
|
88
|
+
error: error instanceof Error ? error.message : String(error)
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
return ()=>{
|
|
93
|
+
active = false;
|
|
94
|
+
};
|
|
95
|
+
}, [
|
|
96
|
+
orgId,
|
|
97
|
+
user
|
|
98
|
+
]);
|
|
99
|
+
const members = state.orgId === orgId ? state.members : [];
|
|
100
|
+
const nameOf = useCallback((ref)=>{
|
|
101
|
+
var _ref;
|
|
102
|
+
var _findOrgMember;
|
|
103
|
+
if (!ref) return '';
|
|
104
|
+
return (_ref = (_findOrgMember = findOrgMember(members, ref)) == null ? void 0 : _findOrgMember.label) != null ? _ref : ref;
|
|
105
|
+
}, [
|
|
106
|
+
members
|
|
107
|
+
]);
|
|
108
|
+
return {
|
|
109
|
+
members,
|
|
110
|
+
loading: Boolean(orgId) && (state.orgId !== orgId || state.loading),
|
|
111
|
+
error: state.orgId === orgId ? state.error : null,
|
|
112
|
+
nameOf
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
//# sourceMappingURL=use-org-member-directory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/hooks/use-org-member-directory.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\nimport { type CrmMemberOption, crmMemberOption, findOrgMember } from '@aglyn/aglyn'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport { useCallback, useEffect, useState } from 'react'\n\n/** One person a task can be assigned to. */\nexport interface OrgMemberOption extends CrmMemberOption {\n role: string\n}\n\n/**\n * The org's roster, read once per org per page and shared by every mount.\n *\n * The roster is what the assignee picker offers and what the assignee\n * column prints — a uid is not a name anybody recognizes. It comes from\n * `GET /api/orgs/members`, which re-derives membership with the Admin SDK\n * (the client-side rules cannot evaluate a list query against the member's\n * own-document clause, so a direct read is refused for the very people who\n * may assign tasks). A roster changes on the order of days; the promise is\n * kept so a second card on the same page shares the request rather than\n * repeating it.\n */\nconst rosterCache = new Map<string, Promise<OrgMemberOption[]>>()\n\n/** Test seam. */\nexport function resetOrgMemberDirectoryCache(): void {\n rosterCache.clear()\n}\n\nfunction memberOption(member: Record<string, unknown>): OrgMemberOption | null {\n const option = crmMemberOption(member)\n return option ? { ...option, role: String(member['role'] ?? '') } : null\n}\n\nexport interface OrgMemberDirectory {\n members: OrgMemberOption[]\n /** The read has not settled — the picker disables rather than offers nobody. */\n loading: boolean\n /** Why the roster could not be read, when it could not. */\n error: string | null\n /**\n * A stored reference — a uid, or an address the roster has — as a person,\n * or the reference itself for someone no longer on the roster.\n */\n nameOf: (ref: string | null | undefined) => string\n}\n\nexport function useOrgMemberDirectory(\n orgId: string | null | undefined,\n): OrgMemberDirectory {\n const { data: user } = useUser()\n const [state, setState] = useState<{\n orgId: string | null\n members: OrgMemberOption[]\n loading: boolean\n error: string | null\n }>({ orgId: null, members: [], loading: false, error: null })\n\n useEffect(() => {\n if (!orgId || !user) return\n let active = true\n let roster = rosterCache.get(orgId)\n if (!roster) {\n roster = authorizedFetch(\n user,\n `/api/orgs/members?orgId=${encodeURIComponent(orgId)}`,\n ).then(async (response) => {\n const payload = (await response.json().catch(() => ({}))) as {\n members?: Array<Record<string, unknown>>\n error?: string\n }\n if (!response.ok) {\n throw new Error(payload.error || 'The team could not be loaded.')\n }\n return (payload.members ?? [])\n .map(memberOption)\n .filter((member): member is OrgMemberOption => member !== null)\n .sort((a, b) => a.label.localeCompare(b.label))\n })\n rosterCache.set(orgId, roster)\n // A failed read is not kept: the next mount asks again rather than\n // remembering an outage for the life of the page.\n roster.catch(() => rosterCache.delete(orgId))\n }\n setState({ orgId, members: [], loading: true, error: null })\n roster.then(\n (members) => {\n if (active) setState({ orgId, members, loading: false, error: null })\n },\n (error: unknown) => {\n if (active) {\n setState({\n orgId,\n members: [],\n loading: false,\n error: error instanceof Error ? error.message : String(error),\n })\n }\n },\n )\n return () => {\n active = false\n }\n }, [orgId, user])\n\n const members = state.orgId === orgId ? state.members : []\n const nameOf = useCallback(\n (ref: string | null | undefined) => {\n if (!ref) return ''\n return findOrgMember(members, ref)?.label ?? ref\n },\n [members],\n )\n return {\n members,\n loading: Boolean(orgId) && (state.orgId !== orgId || state.loading),\n error: state.orgId === orgId ? state.error : null,\n nameOf,\n }\n}\n"],"names":["crmMemberOption","findOrgMember","authorizedFetch","useUser","useCallback","useEffect","useState","rosterCache","Map","resetOrgMemberDirectoryCache","clear","memberOption","member","option","role","String","useOrgMemberDirectory","orgId","data","user","state","setState","members","loading","error","active","roster","get","encodeURIComponent","then","response","payload","json","catch","ok","Error","map","filter","sort","a","b","label","localeCompare","set","delete","message","nameOf","ref","Boolean"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAA+BA,eAAe,EAAEC,aAAa,QAAQ,eAAc;AACnF,SAASC,eAAe,QAAQ,2CAA0C;AAC1E,SAASC,OAAO,QAAQ,iCAAgC;AACxD,SAASC,WAAW,EAAEC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAOxD;;;;;;;;;;;CAWC,GACD,MAAMC,cAAc,IAAIC;AAExB,eAAe,GACf,OAAO,SAASC;IACdF,YAAYG,KAAK;AACnB;AAEA,SAASC,aAAaC,MAA+B;QAETA;IAD1C,MAAMC,SAASb,gBAAgBY;IAC/B,OAAOC,SAAS,aAAKA;QAAQC,MAAMC,QAAOH,eAAAA,MAAM,CAAC,OAAO,YAAdA,eAAkB;SAAQ;AACtE;AAeA,OAAO,SAASI,sBACdC,KAAgC;IAEhC,MAAM,EAAEC,MAAMC,IAAI,EAAE,GAAGhB;IACvB,MAAM,CAACiB,OAAOC,SAAS,GAAGf,SAKvB;QAAEW,OAAO;QAAMK,SAAS,EAAE;QAAEC,SAAS;QAAOC,OAAO;IAAK;IAE3DnB,UAAU;QACR,IAAI,CAACY,SAAS,CAACE,MAAM;QACrB,IAAIM,SAAS;QACb,IAAIC,SAASnB,YAAYoB,GAAG,CAACV;QAC7B,IAAI,CAACS,QAAQ;YACXA,SAASxB,gBACPiB,MACA,CAAC,wBAAwB,EAAES,mBAAmBX,QAAQ,EACtDY,IAAI,CAAC,OAAOC;oBAQJC;gBAPR,MAAMA,UAAW,MAAMD,SAASE,IAAI,GAAGC,KAAK,CAAC,IAAO,CAAA,CAAC,CAAA;gBAIrD,IAAI,CAACH,SAASI,EAAE,EAAE;oBAChB,MAAM,IAAIC,MAAMJ,QAAQP,KAAK,IAAI;gBACnC;gBACA,OAAO,EAACO,mBAAAA,QAAQT,OAAO,YAAfS,mBAAmB,EAAE,EAC1BK,GAAG,CAACzB,cACJ0B,MAAM,CAAC,CAACzB,SAAsCA,WAAW,MACzD0B,IAAI,CAAC,CAACC,GAAGC,IAAMD,EAAEE,KAAK,CAACC,aAAa,CAACF,EAAEC,KAAK;YACjD;YACAlC,YAAYoC,GAAG,CAAC1B,OAAOS;YACvB,mEAAmE;YACnE,kDAAkD;YAClDA,OAAOO,KAAK,CAAC,IAAM1B,YAAYqC,MAAM,CAAC3B;QACxC;QACAI,SAAS;YAAEJ;YAAOK,SAAS,EAAE;YAAEC,SAAS;YAAMC,OAAO;QAAK;QAC1DE,OAAOG,IAAI,CACT,CAACP;YACC,IAAIG,QAAQJ,SAAS;gBAAEJ;gBAAOK;gBAASC,SAAS;gBAAOC,OAAO;YAAK;QACrE,GACA,CAACA;YACC,IAAIC,QAAQ;gBACVJ,SAAS;oBACPJ;oBACAK,SAAS,EAAE;oBACXC,SAAS;oBACTC,OAAOA,iBAAiBW,QAAQX,MAAMqB,OAAO,GAAG9B,OAAOS;gBACzD;YACF;QACF;QAEF,OAAO;YACLC,SAAS;QACX;IACF,GAAG;QAACR;QAAOE;KAAK;IAEhB,MAAMG,UAAUF,MAAMH,KAAK,KAAKA,QAAQG,MAAME,OAAO,GAAG,EAAE;IAC1D,MAAMwB,SAAS1C,YACb,CAAC2C;;YAEQ9C;QADP,IAAI,CAAC8C,KAAK,OAAO;QACjB,gBAAO9C,iBAAAA,cAAcqB,SAASyB,yBAAvB9C,eAA6BwC,KAAK,mBAAIM;IAC/C,GACA;QAACzB;KAAQ;IAEX,OAAO;QACLA;QACAC,SAASyB,QAAQ/B,UAAWG,CAAAA,MAAMH,KAAK,KAAKA,SAASG,MAAMG,OAAO,AAAD;QACjEC,OAAOJ,MAAMH,KAAK,KAAKA,QAAQG,MAAMI,KAAK,GAAG;QAC7CsB;IACF;AACF"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type CrmMemberOption } from '@aglyn/aglyn';
|
|
2
|
+
/** One teammate, as an owner picker or an owner column needs them. */
|
|
3
|
+
export type OrgMemberOption = CrmMemberOption;
|
|
4
|
+
export interface OrgMemberOptions {
|
|
5
|
+
options: OrgMemberOption[];
|
|
6
|
+
/**
|
|
7
|
+
* Label for a stored reference — a uid, or an address the roster has — or
|
|
8
|
+
* the reference's own tail when the roster does not know it.
|
|
9
|
+
*/
|
|
10
|
+
labelFor: (ref: string | null | undefined) => string;
|
|
11
|
+
/**
|
|
12
|
+
* The member's ADDRESS for a stored reference — what a CSV export writes
|
|
13
|
+
* in an owner column, because an import resolves an owner by email —
|
|
14
|
+
* falling back to the label and then to the reference itself.
|
|
15
|
+
*/
|
|
16
|
+
emailFor: (ref: string | null | undefined) => string;
|
|
17
|
+
/** The roster has answered — with people, or with a refusal. */
|
|
18
|
+
ready: boolean;
|
|
19
|
+
/** An org is named and the roster has not answered for it yet. */
|
|
20
|
+
loading: boolean;
|
|
21
|
+
/** The roster was refused or unreachable; `options` is then empty. */
|
|
22
|
+
error: string | null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The org's team, for choosing and naming a record's OWNER (AGL-2597).
|
|
26
|
+
*
|
|
27
|
+
* Read through `GET /api/orgs/members` rather than by listening to
|
|
28
|
+
* `orgs/{orgId}/members`, because the rules on that collection admit a list
|
|
29
|
+
* only to an org-wide member: a scoped editor — precisely the person an
|
|
30
|
+
* agency has running one client's CRM — is refused, and a picker that is
|
|
31
|
+
* empty for them cannot assign an owner. The route re-derives membership
|
|
32
|
+
* with the Admin SDK and answers any member of the org.
|
|
33
|
+
*
|
|
34
|
+
* Fetched once per org and held; the roster changes on a settings page, not
|
|
35
|
+
* while somebody is filing a company. A signed-out mount fetches nothing.
|
|
36
|
+
*/
|
|
37
|
+
export declare function useOrgMemberOptions(orgId: string | null | undefined): OrgMemberOptions;
|
|
38
|
+
export default useOrgMemberOptions;
|
|
@@ -0,0 +1,131 @@
|
|
|
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 { crmMemberOption, findOrgMember } from "@aglyn/aglyn";
|
|
18
|
+
import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
|
|
19
|
+
import { useUser } from "@aglyn/tenant-feature-instance";
|
|
20
|
+
import { useEffect, useMemo, useState } from "react";
|
|
21
|
+
/**
|
|
22
|
+
* The org's team, for choosing and naming a record's OWNER (AGL-2597).
|
|
23
|
+
*
|
|
24
|
+
* Read through `GET /api/orgs/members` rather than by listening to
|
|
25
|
+
* `orgs/{orgId}/members`, because the rules on that collection admit a list
|
|
26
|
+
* only to an org-wide member: a scoped editor — precisely the person an
|
|
27
|
+
* agency has running one client's CRM — is refused, and a picker that is
|
|
28
|
+
* empty for them cannot assign an owner. The route re-derives membership
|
|
29
|
+
* with the Admin SDK and answers any member of the org.
|
|
30
|
+
*
|
|
31
|
+
* Fetched once per org and held; the roster changes on a settings page, not
|
|
32
|
+
* while somebody is filing a company. A signed-out mount fetches nothing.
|
|
33
|
+
*/ export function useOrgMemberOptions(orgId) {
|
|
34
|
+
var _ref;
|
|
35
|
+
const { data: user } = useUser();
|
|
36
|
+
/*
|
|
37
|
+
* The effect is keyed on WHO is signed in, not on the object that says so.
|
|
38
|
+
* `useUser` hands back one `User` per session, but the contract here is
|
|
39
|
+
* one fetch per org, and that must hold under any provider — a test double
|
|
40
|
+
* that mints a fresh object per render, or a listener that re-emits on a
|
|
41
|
+
* token refresh — or the roster is fetched in a loop. The object itself is
|
|
42
|
+
* only the bearer of the token, read from the closure at fetch time.
|
|
43
|
+
*/ const uid = (_ref = user == null ? void 0 : user.uid) != null ? _ref : null;
|
|
44
|
+
const [state, setState] = useState(null);
|
|
45
|
+
useEffect(()=>{
|
|
46
|
+
if (!orgId || !user) return undefined;
|
|
47
|
+
let active = true;
|
|
48
|
+
void authorizedFetch(user, `/api/orgs/members?orgId=${encodeURIComponent(orgId)}`).then(async (response)=>{
|
|
49
|
+
const payload = await response.json().catch(()=>({}));
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
var _ref;
|
|
52
|
+
throw new Error(String((_ref = payload == null ? void 0 : payload.error) != null ? _ref : 'The team could not be loaded'));
|
|
53
|
+
}
|
|
54
|
+
const members = Array.isArray(payload == null ? void 0 : payload.members) ? payload.members : [];
|
|
55
|
+
const options = members.map((member)=>crmMemberOption(member))// A member with no uid cannot own anything. One with no name and
|
|
56
|
+
// no address is still on the team and is listed by uid — dropping
|
|
57
|
+
// them made them the one member nobody could assign to.
|
|
58
|
+
.filter((option)=>option !== null).sort((left, right)=>left.label.localeCompare(right.label, undefined, {
|
|
59
|
+
sensitivity: 'base'
|
|
60
|
+
}));
|
|
61
|
+
if (active) setState({
|
|
62
|
+
orgId,
|
|
63
|
+
options,
|
|
64
|
+
error: null
|
|
65
|
+
});
|
|
66
|
+
}).catch((error)=>{
|
|
67
|
+
if (active) {
|
|
68
|
+
setState({
|
|
69
|
+
orgId,
|
|
70
|
+
options: [],
|
|
71
|
+
error: error instanceof Error ? error.message : 'The team could not be loaded'
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
return ()=>{
|
|
76
|
+
active = false;
|
|
77
|
+
};
|
|
78
|
+
// `user` is read for its token only; `uid` is the identity that decides
|
|
79
|
+
// the answer — see the note above the state.
|
|
80
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
81
|
+
}, [
|
|
82
|
+
orgId,
|
|
83
|
+
uid
|
|
84
|
+
]);
|
|
85
|
+
// An answer for a different org is a stale one: the section that asked for
|
|
86
|
+
// it has moved on, and naming last org's people as this one's owners is a
|
|
87
|
+
// disclosure as well as a mistake.
|
|
88
|
+
const current = state && state.orgId === orgId ? state : null;
|
|
89
|
+
const options = useMemo(()=>{
|
|
90
|
+
var _ref;
|
|
91
|
+
return (_ref = current == null ? void 0 : current.options) != null ? _ref : [];
|
|
92
|
+
}, [
|
|
93
|
+
current
|
|
94
|
+
]);
|
|
95
|
+
const labelFor = useMemo(()=>(ref)=>{
|
|
96
|
+
var _ref;
|
|
97
|
+
var _findOrgMember;
|
|
98
|
+
return ref ? (_ref = (_findOrgMember = findOrgMember(options, ref)) == null ? void 0 : _findOrgMember.label) != null ? _ref : `Member ${ref.slice(-6)}` : '';
|
|
99
|
+
}, [
|
|
100
|
+
options
|
|
101
|
+
]);
|
|
102
|
+
const emailFor = useMemo(()=>(ref)=>{
|
|
103
|
+
if (!ref) return '';
|
|
104
|
+
const member = findOrgMember(options, ref);
|
|
105
|
+
return (member == null ? void 0 : member.email) || (member == null ? void 0 : member.label) || ref;
|
|
106
|
+
}, [
|
|
107
|
+
options
|
|
108
|
+
]);
|
|
109
|
+
// One object per answer, so a column list or a drawer that lists this in
|
|
110
|
+
// its dependencies recomputes when the roster changes and not per render.
|
|
111
|
+
return useMemo(()=>{
|
|
112
|
+
var _ref;
|
|
113
|
+
return {
|
|
114
|
+
options,
|
|
115
|
+
labelFor,
|
|
116
|
+
emailFor,
|
|
117
|
+
ready: current !== null,
|
|
118
|
+
loading: Boolean(orgId) && current === null,
|
|
119
|
+
error: (_ref = current == null ? void 0 : current.error) != null ? _ref : null
|
|
120
|
+
};
|
|
121
|
+
}, [
|
|
122
|
+
options,
|
|
123
|
+
labelFor,
|
|
124
|
+
emailFor,
|
|
125
|
+
current,
|
|
126
|
+
orgId
|
|
127
|
+
]);
|
|
128
|
+
}
|
|
129
|
+
export default useOrgMemberOptions;
|
|
130
|
+
|
|
131
|
+
//# sourceMappingURL=use-org-member-options.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/hooks/use-org-member-options.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 { type CrmMemberOption, crmMemberOption, findOrgMember } from '@aglyn/aglyn'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport { useEffect, useMemo, useState } from 'react'\n\n/** One teammate, as an owner picker or an owner column needs them. */\nexport type OrgMemberOption = CrmMemberOption\n\nexport interface OrgMemberOptions {\n options: OrgMemberOption[]\n /**\n * Label for a stored reference — a uid, or an address the roster has — or\n * the reference's own tail when the roster does not know it.\n */\n labelFor: (ref: string | null | undefined) => string\n /**\n * The member's ADDRESS for a stored reference — what a CSV export writes\n * in an owner column, because an import resolves an owner by email —\n * falling back to the label and then to the reference itself.\n */\n emailFor: (ref: string | null | undefined) => string\n /** The roster has answered — with people, or with a refusal. */\n ready: boolean\n /** An org is named and the roster has not answered for it yet. */\n loading: boolean\n /** The roster was refused or unreachable; `options` is then empty. */\n error: string | null\n}\n\n/**\n * The org's team, for choosing and naming a record's OWNER (AGL-2597).\n *\n * Read through `GET /api/orgs/members` rather than by listening to\n * `orgs/{orgId}/members`, because the rules on that collection admit a list\n * only to an org-wide member: a scoped editor — precisely the person an\n * agency has running one client's CRM — is refused, and a picker that is\n * empty for them cannot assign an owner. The route re-derives membership\n * with the Admin SDK and answers any member of the org.\n *\n * Fetched once per org and held; the roster changes on a settings page, not\n * while somebody is filing a company. A signed-out mount fetches nothing.\n */\nexport function useOrgMemberOptions(\n orgId: string | null | undefined,\n): OrgMemberOptions {\n const { data: user } = useUser()\n /*\n * The effect is keyed on WHO is signed in, not on the object that says so.\n * `useUser` hands back one `User` per session, but the contract here is\n * one fetch per org, and that must hold under any provider — a test double\n * that mints a fresh object per render, or a listener that re-emits on a\n * token refresh — or the roster is fetched in a loop. The object itself is\n * only the bearer of the token, read from the closure at fetch time.\n */\n const uid = user?.uid ?? null\n const [state, setState] = useState<{\n orgId: string\n options: OrgMemberOption[]\n error: string | null\n } | null>(null)\n\n useEffect(() => {\n if (!orgId || !user) return undefined\n let active = true\n void authorizedFetch(\n user,\n `/api/orgs/members?orgId=${encodeURIComponent(orgId)}`,\n )\n .then(async (response) => {\n const payload = await response.json().catch(() => ({}))\n if (!response.ok) {\n throw new Error(\n String(payload?.error ?? 'The team could not be loaded'),\n )\n }\n const members: Array<Record<string, unknown>> = Array.isArray(\n payload?.members,\n )\n ? payload.members\n : []\n const options = members\n .map((member) => crmMemberOption(member))\n // A member with no uid cannot own anything. One with no name and\n // no address is still on the team and is listed by uid — dropping\n // them made them the one member nobody could assign to.\n .filter((option): option is OrgMemberOption => option !== null)\n .sort((left, right) =>\n left.label.localeCompare(right.label, undefined, {\n sensitivity: 'base',\n }),\n )\n if (active) setState({ orgId, options, error: null })\n })\n .catch((error: unknown) => {\n if (active) {\n setState({\n orgId,\n options: [],\n error:\n error instanceof Error\n ? error.message\n : 'The team could not be loaded',\n })\n }\n })\n return () => {\n active = false\n }\n // `user` is read for its token only; `uid` is the identity that decides\n // the answer — see the note above the state.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [orgId, uid])\n\n // An answer for a different org is a stale one: the section that asked for\n // it has moved on, and naming last org's people as this one's owners is a\n // disclosure as well as a mistake.\n const current = state && state.orgId === orgId ? state : null\n const options = useMemo(() => current?.options ?? [], [current])\n const labelFor = useMemo(\n () => (ref: string | null | undefined) =>\n ref ? (findOrgMember(options, ref)?.label ?? `Member ${ref.slice(-6)}`) : '',\n [options],\n )\n const emailFor = useMemo(\n () => (ref: string | null | undefined) => {\n if (!ref) return ''\n const member = findOrgMember(options, ref)\n return member?.email || member?.label || ref\n },\n [options],\n )\n\n // One object per answer, so a column list or a drawer that lists this in\n // its dependencies recomputes when the roster changes and not per render.\n return useMemo(\n () => ({\n options,\n labelFor,\n emailFor,\n ready: current !== null,\n loading: Boolean(orgId) && current === null,\n error: current?.error ?? null,\n }),\n [options, labelFor, emailFor, current, orgId],\n )\n}\n\nexport default useOrgMemberOptions\n"],"names":["crmMemberOption","findOrgMember","authorizedFetch","useUser","useEffect","useMemo","useState","useOrgMemberOptions","orgId","data","user","uid","state","setState","undefined","active","encodeURIComponent","then","response","payload","json","catch","ok","Error","String","error","members","Array","isArray","options","map","member","filter","option","sort","left","right","label","localeCompare","sensitivity","message","current","labelFor","ref","slice","emailFor","email","ready","loading","Boolean"],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { type PipelineDoc } from '../model/deal-board-model';
|
|
2
|
+
/** The most pipelines one org is read with. */
|
|
3
|
+
export declare const PIPELINE_READ_LIMIT = 20;
|
|
4
|
+
export interface UsePipelineOptions {
|
|
5
|
+
/**
|
|
6
|
+
* The site whose console is reading — provenance on the seeded pipeline —
|
|
7
|
+
* or `null` at the organization level (AGL-2630).
|
|
8
|
+
*/
|
|
9
|
+
hostId: string | null;
|
|
10
|
+
/** The org document the shell passed, for the scope the seed is stamped with. */
|
|
11
|
+
org: Record<string, unknown> | null | undefined;
|
|
12
|
+
}
|
|
13
|
+
export interface UsePipelineResult {
|
|
14
|
+
/** Every pipeline the viewer may read, archived ones included, in document-id order. */
|
|
15
|
+
pipelines: PipelineDoc[];
|
|
16
|
+
/** The pipelines a picker offers — see `activePipelines`. */
|
|
17
|
+
activePipelines: PipelineDoc[];
|
|
18
|
+
/** The default ACTIVE pipeline — `isDefault`, else the first — or null before one exists. */
|
|
19
|
+
pipeline: PipelineDoc | null;
|
|
20
|
+
status: 'loading' | 'success' | 'error';
|
|
21
|
+
fromCache: boolean;
|
|
22
|
+
/** A seed write is in flight because the org had no pipeline. */
|
|
23
|
+
seeding: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* The scope tokens this viewer reads with — what a new deal is filtered
|
|
26
|
+
* by — or `null` at the organization level, where nothing filters.
|
|
27
|
+
*/
|
|
28
|
+
visibleToTokens: readonly string[] | null;
|
|
29
|
+
pipelineById: (id: string | undefined) => PipelineDoc | null;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The org's pipelines, and the default one seeded the first time nobody has
|
|
33
|
+
* made any (AGL-2598).
|
|
34
|
+
*
|
|
35
|
+
* ## Bounded, scoped, ordered
|
|
36
|
+
*
|
|
37
|
+
* `orgs/{orgId}/pipelines` is read with the same `visibleTo` predicate every
|
|
38
|
+
* CRM listener uses, ordered by document id and capped at twenty. A pipeline
|
|
39
|
+
* is a handful of documents per org — the cap is there so the query has one,
|
|
40
|
+
* not because anybody is expected to reach it. Archived pipelines are read
|
|
41
|
+
* too (AGL-2620): a closed deal still names the pipeline it closed in, and
|
|
42
|
+
* `pipelineById` has to resolve its stage on the deal's page and in the
|
|
43
|
+
* reports. Only `activePipelines` and the default exclude them.
|
|
44
|
+
*
|
|
45
|
+
* ## The seed
|
|
46
|
+
*
|
|
47
|
+
* A merchant opening Deals for the first time should see a board, not a
|
|
48
|
+
* setup step. When the SERVER has confirmed the org has no pipeline at all,
|
|
49
|
+
* the hook writes one — `Sales`, with the default stages, `isDefault` — under
|
|
50
|
+
* a fixed document id inside a transaction that first re-reads it. The
|
|
51
|
+
* transaction is what makes two tabs opening the section together produce
|
|
52
|
+
* one pipeline rather than two: whichever commits second sees the document
|
|
53
|
+
* and writes nothing.
|
|
54
|
+
*
|
|
55
|
+
* A cached empty answer does not seed. The listener reports `fromCache`
|
|
56
|
+
* while the local cache is serving it, and an empty cache is what a first
|
|
57
|
+
* load looks like in every org that already has a pipeline — seeding on it
|
|
58
|
+
* would race the real answer and, on an offline tab, write a second default
|
|
59
|
+
* once the tab reconnected.
|
|
60
|
+
*
|
|
61
|
+
* The seed carries what every CRM creator stamps: the scope a contact
|
|
62
|
+
* captured on this site would carry, the site as provenance, and the
|
|
63
|
+
* creating account. At the ORGANIZATION level (AGL-2630) the seed is the
|
|
64
|
+
* org's: stamped `['org']`, so every site's board opens on the same default
|
|
65
|
+
* pipeline rather than each site seeding one of its own the others cannot
|
|
66
|
+
* see — a pipeline's stages describe how the business sells, and at this
|
|
67
|
+
* level the business is the org. Its provenance is the site the reader has
|
|
68
|
+
* picked, when they have; a default made before any pick names none.
|
|
69
|
+
*/
|
|
70
|
+
export declare function usePipeline(orgId: string | null | undefined, options: UsePipelineOptions): UsePipelineResult;
|
|
71
|
+
export default usePipeline;
|
|
@@ -0,0 +1,159 @@
|
|
|
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, DEFAULT_DEAL_STAGES, ORG_SCOPE_TOKEN } from "@aglyn/aglyn";
|
|
18
|
+
import { useFirestore, useFirestoreCollection, useUser } from "@aglyn/tenant-feature-instance";
|
|
19
|
+
import { collection, doc, documentId, limit, orderBy, query, runTransaction } from "firebase/firestore";
|
|
20
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
21
|
+
import { activePipelines, DEFAULT_PIPELINE_ID, DEFAULT_PIPELINE_NAME, defaultPipelineOf } from "../model/deal-board-model.js";
|
|
22
|
+
import { crmScopeListable, crmVisibleToClause, useCrmScope } from "./use-crm-scope.js";
|
|
23
|
+
/** The most pipelines one org is read with. */ export const PIPELINE_READ_LIMIT = 20;
|
|
24
|
+
/**
|
|
25
|
+
* The org's pipelines, and the default one seeded the first time nobody has
|
|
26
|
+
* made any (AGL-2598).
|
|
27
|
+
*
|
|
28
|
+
* ## Bounded, scoped, ordered
|
|
29
|
+
*
|
|
30
|
+
* `orgs/{orgId}/pipelines` is read with the same `visibleTo` predicate every
|
|
31
|
+
* CRM listener uses, ordered by document id and capped at twenty. A pipeline
|
|
32
|
+
* is a handful of documents per org — the cap is there so the query has one,
|
|
33
|
+
* not because anybody is expected to reach it. Archived pipelines are read
|
|
34
|
+
* too (AGL-2620): a closed deal still names the pipeline it closed in, and
|
|
35
|
+
* `pipelineById` has to resolve its stage on the deal's page and in the
|
|
36
|
+
* reports. Only `activePipelines` and the default exclude them.
|
|
37
|
+
*
|
|
38
|
+
* ## The seed
|
|
39
|
+
*
|
|
40
|
+
* A merchant opening Deals for the first time should see a board, not a
|
|
41
|
+
* setup step. When the SERVER has confirmed the org has no pipeline at all,
|
|
42
|
+
* the hook writes one — `Sales`, with the default stages, `isDefault` — under
|
|
43
|
+
* a fixed document id inside a transaction that first re-reads it. The
|
|
44
|
+
* transaction is what makes two tabs opening the section together produce
|
|
45
|
+
* one pipeline rather than two: whichever commits second sees the document
|
|
46
|
+
* and writes nothing.
|
|
47
|
+
*
|
|
48
|
+
* A cached empty answer does not seed. The listener reports `fromCache`
|
|
49
|
+
* while the local cache is serving it, and an empty cache is what a first
|
|
50
|
+
* load looks like in every org that already has a pipeline — seeding on it
|
|
51
|
+
* would race the real answer and, on an offline tab, write a second default
|
|
52
|
+
* once the tab reconnected.
|
|
53
|
+
*
|
|
54
|
+
* The seed carries what every CRM creator stamps: the scope a contact
|
|
55
|
+
* captured on this site would carry, the site as provenance, and the
|
|
56
|
+
* creating account. At the ORGANIZATION level (AGL-2630) the seed is the
|
|
57
|
+
* org's: stamped `['org']`, so every site's board opens on the same default
|
|
58
|
+
* pipeline rather than each site seeding one of its own the others cannot
|
|
59
|
+
* see — a pipeline's stages describe how the business sells, and at this
|
|
60
|
+
* level the business is the org. Its provenance is the site the reader has
|
|
61
|
+
* picked, when they have; a default made before any pick names none.
|
|
62
|
+
*/ export function usePipeline(orgId, options) {
|
|
63
|
+
const { hostId, org } = options;
|
|
64
|
+
const firestore = useFirestore();
|
|
65
|
+
const { data: user } = useUser();
|
|
66
|
+
const uid = user == null ? void 0 : user.uid;
|
|
67
|
+
// The reader's tokens and the seed's stamp, from the one scope hook
|
|
68
|
+
// (AGL-2614): under a site the group's tokens both ways; at the org level
|
|
69
|
+
// no read clause, and a create stamp from the picked site.
|
|
70
|
+
const { level, visibleTo: visibleToTokens, createHostId, createTokens } = useCrmScope({
|
|
71
|
+
hostId,
|
|
72
|
+
org: org
|
|
73
|
+
});
|
|
74
|
+
const { data, status, fromCache } = useFirestoreCollection(()=>orgId && crmScopeListable(visibleToTokens) ? query(collection(firestore, 'orgs', orgId, CRM_COLLECTIONS.pipelines), ...crmVisibleToClause(visibleToTokens), orderBy(documentId()), limit(PIPELINE_READ_LIMIT)) : null, [
|
|
75
|
+
firestore,
|
|
76
|
+
orgId,
|
|
77
|
+
visibleToTokens
|
|
78
|
+
], {
|
|
79
|
+
idField: '$id'
|
|
80
|
+
});
|
|
81
|
+
const [seeding, setSeeding] = useState(false);
|
|
82
|
+
// One seed attempt per org per mount, whatever the listener does after.
|
|
83
|
+
const seededFor = useRef(null);
|
|
84
|
+
useEffect(()=>{
|
|
85
|
+
if (!orgId || !uid) return;
|
|
86
|
+
// Under a site the seed needs the site's own stamp; without one it
|
|
87
|
+
// would be a pipeline nobody may read.
|
|
88
|
+
if (level === 'site' && !createTokens.length) return;
|
|
89
|
+
if (status !== 'success' || fromCache || data.length > 0) return;
|
|
90
|
+
if (seededFor.current === orgId) return;
|
|
91
|
+
seededFor.current = orgId;
|
|
92
|
+
let active = true;
|
|
93
|
+
setSeeding(true);
|
|
94
|
+
const ref = doc(firestore, 'orgs', orgId, CRM_COLLECTIONS.pipelines, DEFAULT_PIPELINE_ID);
|
|
95
|
+
void runTransaction(firestore, async (transaction)=>{
|
|
96
|
+
const existing = await transaction.get(ref);
|
|
97
|
+
if (existing.exists()) return;
|
|
98
|
+
transaction.set(ref, {
|
|
99
|
+
name: DEFAULT_PIPELINE_NAME,
|
|
100
|
+
stages: [
|
|
101
|
+
...DEFAULT_DEAL_STAGES
|
|
102
|
+
],
|
|
103
|
+
isDefault: true,
|
|
104
|
+
archivedAt: null,
|
|
105
|
+
visibleTo: level === 'site' ? [
|
|
106
|
+
...createTokens
|
|
107
|
+
] : [
|
|
108
|
+
ORG_SCOPE_TOKEN
|
|
109
|
+
],
|
|
110
|
+
hostId: createHostId,
|
|
111
|
+
createdByUid: uid,
|
|
112
|
+
createdAt: new Date(),
|
|
113
|
+
updatedAt: new Date()
|
|
114
|
+
});
|
|
115
|
+
}).catch((error)=>{
|
|
116
|
+
console.error(error);
|
|
117
|
+
}).finally(()=>{
|
|
118
|
+
if (active) setSeeding(false);
|
|
119
|
+
});
|
|
120
|
+
return ()=>{
|
|
121
|
+
active = false;
|
|
122
|
+
};
|
|
123
|
+
}, [
|
|
124
|
+
firestore,
|
|
125
|
+
orgId,
|
|
126
|
+
uid,
|
|
127
|
+
level,
|
|
128
|
+
createTokens,
|
|
129
|
+
createHostId,
|
|
130
|
+
status,
|
|
131
|
+
fromCache,
|
|
132
|
+
data
|
|
133
|
+
]);
|
|
134
|
+
return useMemo(()=>{
|
|
135
|
+
const pipelines = data != null ? data : [];
|
|
136
|
+
return {
|
|
137
|
+
pipelines,
|
|
138
|
+
activePipelines: activePipelines(pipelines),
|
|
139
|
+
pipeline: defaultPipelineOf(pipelines),
|
|
140
|
+
status,
|
|
141
|
+
fromCache,
|
|
142
|
+
seeding,
|
|
143
|
+
visibleToTokens,
|
|
144
|
+
pipelineById: (id)=>{
|
|
145
|
+
var _pipelines_find;
|
|
146
|
+
return id ? (_pipelines_find = pipelines.find((entry)=>entry.$id === id)) != null ? _pipelines_find : null : null;
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}, [
|
|
150
|
+
data,
|
|
151
|
+
status,
|
|
152
|
+
fromCache,
|
|
153
|
+
seeding,
|
|
154
|
+
visibleToTokens
|
|
155
|
+
]);
|
|
156
|
+
}
|
|
157
|
+
export default usePipeline;
|
|
158
|
+
|
|
159
|
+
//# sourceMappingURL=use-pipeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/hooks/use-pipeline.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 DEFAULT_DEAL_STAGES,\n ORG_SCOPE_TOKEN,\n} from '@aglyn/aglyn'\nimport {\n useFirestore,\n useFirestoreCollection,\n useUser,\n} from '@aglyn/tenant-feature-instance'\nimport {\n collection,\n doc,\n documentId,\n limit,\n orderBy,\n query,\n runTransaction,\n where,\n} from 'firebase/firestore'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport {\n activePipelines,\n DEFAULT_PIPELINE_ID,\n DEFAULT_PIPELINE_NAME,\n defaultPipelineOf,\n type PipelineDoc,\n} from '../model/deal-board-model'\nimport {\n crmScopeListable,\n crmVisibleToClause,\n useCrmScope,\n} from './use-crm-scope'\n\n/** The most pipelines one org is read with. */\nexport const PIPELINE_READ_LIMIT = 20\n\nexport interface UsePipelineOptions {\n /**\n * The site whose console is reading — provenance on the seeded pipeline —\n * or `null` at the organization level (AGL-2630).\n */\n hostId: string | null\n /** The org document the shell passed, for the scope the seed is stamped with. */\n org: Record<string, unknown> | null | undefined\n}\n\nexport interface UsePipelineResult {\n /** Every pipeline the viewer may read, archived ones included, in document-id order. */\n pipelines: PipelineDoc[]\n /** The pipelines a picker offers — see `activePipelines`. */\n activePipelines: PipelineDoc[]\n /** The default ACTIVE pipeline — `isDefault`, else the first — or null before one exists. */\n pipeline: PipelineDoc | null\n status: 'loading' | 'success' | 'error'\n fromCache: boolean\n /** A seed write is in flight because the org had no pipeline. */\n seeding: boolean\n /**\n * The scope tokens this viewer reads with — what a new deal is filtered\n * by — or `null` at the organization level, where nothing filters.\n */\n visibleToTokens: readonly string[] | null\n pipelineById: (id: string | undefined) => PipelineDoc | null\n}\n\n/**\n * The org's pipelines, and the default one seeded the first time nobody has\n * made any (AGL-2598).\n *\n * ## Bounded, scoped, ordered\n *\n * `orgs/{orgId}/pipelines` is read with the same `visibleTo` predicate every\n * CRM listener uses, ordered by document id and capped at twenty. A pipeline\n * is a handful of documents per org — the cap is there so the query has one,\n * not because anybody is expected to reach it. Archived pipelines are read\n * too (AGL-2620): a closed deal still names the pipeline it closed in, and\n * `pipelineById` has to resolve its stage on the deal's page and in the\n * reports. Only `activePipelines` and the default exclude them.\n *\n * ## The seed\n *\n * A merchant opening Deals for the first time should see a board, not a\n * setup step. When the SERVER has confirmed the org has no pipeline at all,\n * the hook writes one — `Sales`, with the default stages, `isDefault` — under\n * a fixed document id inside a transaction that first re-reads it. The\n * transaction is what makes two tabs opening the section together produce\n * one pipeline rather than two: whichever commits second sees the document\n * and writes nothing.\n *\n * A cached empty answer does not seed. The listener reports `fromCache`\n * while the local cache is serving it, and an empty cache is what a first\n * load looks like in every org that already has a pipeline — seeding on it\n * would race the real answer and, on an offline tab, write a second default\n * once the tab reconnected.\n *\n * The seed carries what every CRM creator stamps: the scope a contact\n * captured on this site would carry, the site as provenance, and the\n * creating account. At the ORGANIZATION level (AGL-2630) the seed is the\n * org's: stamped `['org']`, so every site's board opens on the same default\n * pipeline rather than each site seeding one of its own the others cannot\n * see — a pipeline's stages describe how the business sells, and at this\n * level the business is the org. Its provenance is the site the reader has\n * picked, when they have; a default made before any pick names none.\n */\nexport function usePipeline(\n orgId: string | null | undefined,\n options: UsePipelineOptions,\n): UsePipelineResult {\n const { hostId, org } = options\n const firestore = useFirestore()\n const { data: user } = useUser()\n const uid = user?.uid\n\n // The reader's tokens and the seed's stamp, from the one scope hook\n // (AGL-2614): under a site the group's tokens both ways; at the org level\n // no read clause, and a create stamp from the picked site.\n const {\n level,\n visibleTo: visibleToTokens,\n createHostId,\n createTokens,\n } = useCrmScope({ hostId, org: org as Record<string, unknown> | null })\n\n const { data, status, fromCache } = useFirestoreCollection<PipelineDoc>(\n () =>\n orgId && crmScopeListable(visibleToTokens)\n ? query(\n collection(firestore, 'orgs', orgId, CRM_COLLECTIONS.pipelines),\n ...crmVisibleToClause(visibleToTokens),\n orderBy(documentId()),\n limit(PIPELINE_READ_LIMIT),\n )\n : null,\n [firestore, orgId, visibleToTokens],\n { idField: '$id' },\n )\n\n const [seeding, setSeeding] = useState(false)\n // One seed attempt per org per mount, whatever the listener does after.\n const seededFor = useRef<string | null>(null)\n useEffect(() => {\n if (!orgId || !uid) return\n // Under a site the seed needs the site's own stamp; without one it\n // would be a pipeline nobody may read.\n if (level === 'site' && !createTokens.length) return\n if (status !== 'success' || fromCache || data.length > 0) return\n if (seededFor.current === orgId) return\n seededFor.current = orgId\n let active = true\n setSeeding(true)\n const ref = doc(\n firestore,\n 'orgs',\n orgId,\n CRM_COLLECTIONS.pipelines,\n DEFAULT_PIPELINE_ID,\n )\n void runTransaction(firestore, async (transaction) => {\n const existing = await transaction.get(ref)\n if (existing.exists()) return\n transaction.set(ref, {\n name: DEFAULT_PIPELINE_NAME,\n stages: [...DEFAULT_DEAL_STAGES],\n isDefault: true,\n archivedAt: null,\n visibleTo: level === 'site' ? [...createTokens] : [ORG_SCOPE_TOKEN],\n hostId: createHostId,\n createdByUid: uid,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n })\n .catch((error) => {\n console.error(error)\n })\n .finally(() => {\n if (active) setSeeding(false)\n })\n return () => {\n active = false\n }\n }, [\n firestore,\n orgId,\n uid,\n level,\n createTokens,\n createHostId,\n status,\n fromCache,\n data,\n ])\n\n return useMemo(() => {\n const pipelines = data ?? []\n return {\n pipelines,\n activePipelines: activePipelines(pipelines),\n pipeline: defaultPipelineOf(pipelines),\n status,\n fromCache,\n seeding,\n visibleToTokens,\n pipelineById: (id) =>\n id ? (pipelines.find((entry) => entry.$id === id) ?? null) : null,\n }\n }, [data, status, fromCache, seeding, visibleToTokens])\n}\n\nexport default usePipeline\n"],"names":["CRM_COLLECTIONS","DEFAULT_DEAL_STAGES","ORG_SCOPE_TOKEN","useFirestore","useFirestoreCollection","useUser","collection","doc","documentId","limit","orderBy","query","runTransaction","useEffect","useMemo","useRef","useState","activePipelines","DEFAULT_PIPELINE_ID","DEFAULT_PIPELINE_NAME","defaultPipelineOf","crmScopeListable","crmVisibleToClause","useCrmScope","PIPELINE_READ_LIMIT","usePipeline","orgId","options","hostId","org","firestore","data","user","uid","level","visibleTo","visibleToTokens","createHostId","createTokens","status","fromCache","pipelines","idField","seeding","setSeeding","seededFor","length","current","active","ref","transaction","existing","get","exists","set","name","stages","isDefault","archivedAt","createdByUid","createdAt","Date","updatedAt","catch","error","console","finally","pipeline","pipelineById","id","find","entry","$id"],"mappings":"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"}
|