@aglyn/plugins-crm 1.0.0-beta.143
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +7 -0
- package/package.json +57 -0
- package/src/index.d.ts +21 -0
- package/src/index.js +29 -0
- package/src/index.js.map +1 -0
- package/src/lib/components/activity-list.d.ts +98 -0
- package/src/lib/components/activity-list.js +321 -0
- package/src/lib/components/activity-list.js.map +1 -0
- package/src/lib/components/activity-queries.d.ts +117 -0
- package/src/lib/components/activity-queries.js +198 -0
- package/src/lib/components/activity-queries.js.map +1 -0
- package/src/lib/components/add-to-list-button.d.ts +25 -0
- package/src/lib/components/add-to-list-button.js +64 -0
- package/src/lib/components/add-to-list-button.js.map +1 -0
- package/src/lib/components/add-to-list-dialog.d.ts +24 -0
- package/src/lib/components/add-to-list-dialog.js +327 -0
- package/src/lib/components/add-to-list-dialog.js.map +1 -0
- package/src/lib/components/assignment-rule-drawer.d.ts +43 -0
- package/src/lib/components/assignment-rule-drawer.js +293 -0
- package/src/lib/components/assignment-rule-drawer.js.map +1 -0
- package/src/lib/components/companies-bulk-bar.d.ts +29 -0
- package/src/lib/components/companies-bulk-bar.js +274 -0
- package/src/lib/components/companies-bulk-bar.js.map +1 -0
- package/src/lib/components/companies-section.d.ts +54 -0
- package/src/lib/components/companies-section.js +522 -0
- package/src/lib/components/companies-section.js.map +1 -0
- package/src/lib/components/company-contacts-card.d.ts +49 -0
- package/src/lib/components/company-contacts-card.js +435 -0
- package/src/lib/components/company-contacts-card.js.map +1 -0
- package/src/lib/components/company-deals-card.d.ts +20 -0
- package/src/lib/components/company-deals-card.js +38 -0
- package/src/lib/components/company-deals-card.js.map +1 -0
- package/src/lib/components/company-detail-page.d.ts +20 -0
- package/src/lib/components/company-detail-page.js +165 -0
- package/src/lib/components/company-detail-page.js.map +1 -0
- package/src/lib/components/company-edit-drawer.d.ts +65 -0
- package/src/lib/components/company-edit-drawer.js +433 -0
- package/src/lib/components/company-edit-drawer.js.map +1 -0
- package/src/lib/components/company-import-drawer.d.ts +42 -0
- package/src/lib/components/company-import-drawer.js +99 -0
- package/src/lib/components/company-import-drawer.js.map +1 -0
- package/src/lib/components/company-picker.d.ts +127 -0
- package/src/lib/components/company-picker.js +323 -0
- package/src/lib/components/company-picker.js.map +1 -0
- package/src/lib/components/company-properties-card.d.ts +47 -0
- package/src/lib/components/company-properties-card.js +289 -0
- package/src/lib/components/company-properties-card.js.map +1 -0
- package/src/lib/components/contact-address-fields.d.ts +29 -0
- package/src/lib/components/contact-address-fields.js +149 -0
- package/src/lib/components/contact-address-fields.js.map +1 -0
- package/src/lib/components/contact-associations-card.d.ts +72 -0
- package/src/lib/components/contact-associations-card.js +296 -0
- package/src/lib/components/contact-associations-card.js.map +1 -0
- package/src/lib/components/contact-custom-columns.d.ts +38 -0
- package/src/lib/components/contact-custom-columns.js +102 -0
- package/src/lib/components/contact-custom-columns.js.map +1 -0
- package/src/lib/components/contact-custom-fields-card.d.ts +39 -0
- package/src/lib/components/contact-custom-fields-card.js +204 -0
- package/src/lib/components/contact-custom-fields-card.js.map +1 -0
- package/src/lib/components/contact-deals-card.d.ts +20 -0
- package/src/lib/components/contact-deals-card.js +38 -0
- package/src/lib/components/contact-deals-card.js.map +1 -0
- package/src/lib/components/contact-detail-page.d.ts +46 -0
- package/src/lib/components/contact-detail-page.js +535 -0
- package/src/lib/components/contact-detail-page.js.map +1 -0
- package/src/lib/components/contact-duplicates-card.d.ts +42 -0
- package/src/lib/components/contact-duplicates-card.js +167 -0
- package/src/lib/components/contact-duplicates-card.js.map +1 -0
- package/src/lib/components/contact-field-drawer.d.ts +64 -0
- package/src/lib/components/contact-field-drawer.js +251 -0
- package/src/lib/components/contact-field-drawer.js.map +1 -0
- package/src/lib/components/contact-import-drawer.d.ts +46 -0
- package/src/lib/components/contact-import-drawer.js +149 -0
- package/src/lib/components/contact-import-drawer.js.map +1 -0
- package/src/lib/components/contact-known-by-card.d.ts +39 -0
- package/src/lib/components/contact-known-by-card.js +142 -0
- package/src/lib/components/contact-known-by-card.js.map +1 -0
- package/src/lib/components/contact-list-columns.d.ts +53 -0
- package/src/lib/components/contact-list-columns.js +288 -0
- package/src/lib/components/contact-list-columns.js.map +1 -0
- package/src/lib/components/contact-merge-dialog.d.ts +79 -0
- package/src/lib/components/contact-merge-dialog.js +405 -0
- package/src/lib/components/contact-merge-dialog.js.map +1 -0
- package/src/lib/components/contact-properties-card.d.ts +89 -0
- package/src/lib/components/contact-properties-card.js +492 -0
- package/src/lib/components/contact-properties-card.js.map +1 -0
- package/src/lib/components/contact-timeline-card.d.ts +68 -0
- package/src/lib/components/contact-timeline-card.js +435 -0
- package/src/lib/components/contact-timeline-card.js.map +1 -0
- package/src/lib/components/contacts-bulk-bar.d.ts +91 -0
- package/src/lib/components/contacts-bulk-bar.js +493 -0
- package/src/lib/components/contacts-bulk-bar.js.map +1 -0
- package/src/lib/components/contacts-section.d.ts +26 -0
- package/src/lib/components/contacts-section.js +1025 -0
- package/src/lib/components/contacts-section.js.map +1 -0
- package/src/lib/components/copy-capture-address-button.d.ts +33 -0
- package/src/lib/components/copy-capture-address-button.js +60 -0
- package/src/lib/components/copy-capture-address-button.js.map +1 -0
- package/src/lib/components/crm-attribution-zone.d.ts +38 -0
- package/src/lib/components/crm-attribution-zone.js +36 -0
- package/src/lib/components/crm-attribution-zone.js.map +1 -0
- package/src/lib/components/crm-booking-zone.d.ts +61 -0
- package/src/lib/components/crm-booking-zone.js +50 -0
- package/src/lib/components/crm-booking-zone.js.map +1 -0
- package/src/lib/components/crm-bulk-bar-frame.d.ts +47 -0
- package/src/lib/components/crm-bulk-bar-frame.js +137 -0
- package/src/lib/components/crm-bulk-bar-frame.js.map +1 -0
- package/src/lib/components/crm-call-actions.d.ts +65 -0
- package/src/lib/components/crm-call-actions.js +110 -0
- package/src/lib/components/crm-call-actions.js.map +1 -0
- package/src/lib/components/crm-column-menu.d.ts +21 -0
- package/src/lib/components/crm-column-menu.js +103 -0
- package/src/lib/components/crm-column-menu.js.map +1 -0
- package/src/lib/components/crm-console-page.d.ts +19 -0
- package/src/lib/components/crm-console-page.js +151 -0
- package/src/lib/components/crm-console-page.js.map +1 -0
- package/src/lib/components/crm-console-sections.d.ts +52 -0
- package/src/lib/components/crm-console-sections.js +83 -0
- package/src/lib/components/crm-console-sections.js.map +1 -0
- package/src/lib/components/crm-custom-field-control.d.ts +26 -0
- package/src/lib/components/crm-custom-field-control.js +131 -0
- package/src/lib/components/crm-custom-field-control.js.map +1 -0
- package/src/lib/components/crm-email-template-tools.d.ts +69 -0
- package/src/lib/components/crm-email-template-tools.js +240 -0
- package/src/lib/components/crm-email-template-tools.js.map +1 -0
- package/src/lib/components/crm-export-all-button.d.ts +39 -0
- package/src/lib/components/crm-export-all-button.js +98 -0
- package/src/lib/components/crm-export-all-button.js.map +1 -0
- package/src/lib/components/crm-filter-bar.d.ts +53 -0
- package/src/lib/components/crm-filter-bar.js +294 -0
- package/src/lib/components/crm-filter-bar.js.map +1 -0
- package/src/lib/components/crm-glance-card.d.ts +54 -0
- package/src/lib/components/crm-glance-card.js +219 -0
- package/src/lib/components/crm-glance-card.js.map +1 -0
- package/src/lib/components/crm-next-activity-column.d.ts +35 -0
- package/src/lib/components/crm-next-activity-column.js +100 -0
- package/src/lib/components/crm-next-activity-column.js.map +1 -0
- package/src/lib/components/crm-record-header.d.ts +97 -0
- package/src/lib/components/crm-record-header.js +160 -0
- package/src/lib/components/crm-record-header.js.map +1 -0
- package/src/lib/components/crm-record-insights-zone.d.ts +50 -0
- package/src/lib/components/crm-record-insights-zone.js +169 -0
- package/src/lib/components/crm-record-insights-zone.js.map +1 -0
- package/src/lib/components/crm-record-picker.d.ts +42 -0
- package/src/lib/components/crm-record-picker.js +104 -0
- package/src/lib/components/crm-record-picker.js.map +1 -0
- package/src/lib/components/crm-send-email-button.d.ts +39 -0
- package/src/lib/components/crm-send-email-button.js +81 -0
- package/src/lib/components/crm-send-email-button.js.map +1 -0
- package/src/lib/components/crm-send-email-dialog.d.ts +61 -0
- package/src/lib/components/crm-send-email-dialog.js +851 -0
- package/src/lib/components/crm-send-email-dialog.js.map +1 -0
- package/src/lib/components/crm-site-picker.d.ts +61 -0
- package/src/lib/components/crm-site-picker.js +99 -0
- package/src/lib/components/crm-site-picker.js.map +1 -0
- package/src/lib/components/crm-suite-lock.d.ts +29 -0
- package/src/lib/components/crm-suite-lock.js +114 -0
- package/src/lib/components/crm-suite-lock.js.map +1 -0
- package/src/lib/components/crm-tasks-due-card.d.ts +42 -0
- package/src/lib/components/crm-tasks-due-card.js +225 -0
- package/src/lib/components/crm-tasks-due-card.js.map +1 -0
- package/src/lib/components/crm-views-control.d.ts +65 -0
- package/src/lib/components/crm-views-control.js +518 -0
- package/src/lib/components/crm-views-control.js.map +1 -0
- package/src/lib/components/csv-import-drawer.d.ts +132 -0
- package/src/lib/components/csv-import-drawer.js +746 -0
- package/src/lib/components/csv-import-drawer.js.map +1 -0
- package/src/lib/components/deal-board.d.ts +70 -0
- package/src/lib/components/deal-board.js +478 -0
- package/src/lib/components/deal-board.js.map +1 -0
- package/src/lib/components/deal-detail-page.d.ts +18 -0
- package/src/lib/components/deal-detail-page.js +323 -0
- package/src/lib/components/deal-detail-page.js.map +1 -0
- package/src/lib/components/deal-edit-drawer.d.ts +59 -0
- package/src/lib/components/deal-edit-drawer.js +640 -0
- package/src/lib/components/deal-edit-drawer.js.map +1 -0
- package/src/lib/components/deal-import-drawer.d.ts +24 -0
- package/src/lib/components/deal-import-drawer.js +83 -0
- package/src/lib/components/deal-import-drawer.js.map +1 -0
- package/src/lib/components/deal-products-card.d.ts +48 -0
- package/src/lib/components/deal-products-card.js +614 -0
- package/src/lib/components/deal-products-card.js.map +1 -0
- package/src/lib/components/deal-properties-card.d.ts +29 -0
- package/src/lib/components/deal-properties-card.js +144 -0
- package/src/lib/components/deal-properties-card.js.map +1 -0
- package/src/lib/components/deal-stage-card.d.ts +24 -0
- package/src/lib/components/deal-stage-card.js +199 -0
- package/src/lib/components/deal-stage-card.js.map +1 -0
- package/src/lib/components/deals-bulk-bar.d.ts +31 -0
- package/src/lib/components/deals-bulk-bar.js +340 -0
- package/src/lib/components/deals-bulk-bar.js.map +1 -0
- package/src/lib/components/deals-section.d.ts +49 -0
- package/src/lib/components/deals-section.js +767 -0
- package/src/lib/components/deals-section.js.map +1 -0
- package/src/lib/components/email-capture-card.d.ts +42 -0
- package/src/lib/components/email-capture-card.js +134 -0
- package/src/lib/components/email-capture-card.js.map +1 -0
- package/src/lib/components/email-template-drawer.d.ts +33 -0
- package/src/lib/components/email-template-drawer.js +209 -0
- package/src/lib/components/email-template-drawer.js.map +1 -0
- package/src/lib/components/email-templates-card.d.ts +33 -0
- package/src/lib/components/email-templates-card.js +370 -0
- package/src/lib/components/email-templates-card.js.map +1 -0
- package/src/lib/components/erase-person-action.d.ts +92 -0
- package/src/lib/components/erase-person-action.js +261 -0
- package/src/lib/components/erase-person-action.js.map +1 -0
- package/src/lib/components/fields-section.d.ts +37 -0
- package/src/lib/components/fields-section.js +641 -0
- package/src/lib/components/fields-section.js.map +1 -0
- package/src/lib/components/form-contact-fields-card.d.ts +46 -0
- package/src/lib/components/form-contact-fields-card.js +253 -0
- package/src/lib/components/form-contact-fields-card.js.map +1 -0
- package/src/lib/components/insert-link-at-caret.d.ts +28 -0
- package/src/lib/components/insert-link-at-caret.js +37 -0
- package/src/lib/components/insert-link-at-caret.js.map +1 -0
- package/src/lib/components/lead-convert-dialog.d.ts +32 -0
- package/src/lib/components/lead-convert-dialog.js +493 -0
- package/src/lib/components/lead-convert-dialog.js.map +1 -0
- package/src/lib/components/lead-detail-page.d.ts +16 -0
- package/src/lib/components/lead-detail-page.js +163 -0
- package/src/lib/components/lead-detail-page.js.map +1 -0
- package/src/lib/components/lead-history-card.d.ts +32 -0
- package/src/lib/components/lead-history-card.js +165 -0
- package/src/lib/components/lead-history-card.js.map +1 -0
- package/src/lib/components/lead-import-drawer.d.ts +24 -0
- package/src/lib/components/lead-import-drawer.js +95 -0
- package/src/lib/components/lead-import-drawer.js.map +1 -0
- package/src/lib/components/lead-owner-select.d.ts +35 -0
- package/src/lib/components/lead-owner-select.js +91 -0
- package/src/lib/components/lead-owner-select.js.map +1 -0
- package/src/lib/components/lead-properties-card.d.ts +61 -0
- package/src/lib/components/lead-properties-card.js +395 -0
- package/src/lib/components/lead-properties-card.js.map +1 -0
- package/src/lib/components/lead-status-chip.d.ts +12 -0
- package/src/lib/components/lead-status-chip.js +43 -0
- package/src/lib/components/lead-status-chip.js.map +1 -0
- package/src/lib/components/lead-surfaces-note.d.ts +126 -0
- package/src/lib/components/lead-surfaces-note.js +262 -0
- package/src/lib/components/lead-surfaces-note.js.map +1 -0
- package/src/lib/components/lead-unqualify-dialog.d.ts +24 -0
- package/src/lib/components/lead-unqualify-dialog.js +125 -0
- package/src/lib/components/lead-unqualify-dialog.js.map +1 -0
- package/src/lib/components/leads-bulk-bar.d.ts +58 -0
- package/src/lib/components/leads-bulk-bar.js +307 -0
- package/src/lib/components/leads-bulk-bar.js.map +1 -0
- package/src/lib/components/leads-section.d.ts +23 -0
- package/src/lib/components/leads-section.js +664 -0
- package/src/lib/components/leads-section.js.map +1 -0
- package/src/lib/components/linked-deals-card.d.ts +32 -0
- package/src/lib/components/linked-deals-card.js +163 -0
- package/src/lib/components/linked-deals-card.js.map +1 -0
- package/src/lib/components/log-activity-dialog.d.ts +60 -0
- package/src/lib/components/log-activity-dialog.js +368 -0
- package/src/lib/components/log-activity-dialog.js.map +1 -0
- package/src/lib/components/lost-reason-dialog.d.ts +26 -0
- package/src/lib/components/lost-reason-dialog.js +95 -0
- package/src/lib/components/lost-reason-dialog.js.map +1 -0
- package/src/lib/components/new-contact-drawer.d.ts +78 -0
- package/src/lib/components/new-contact-drawer.js +343 -0
- package/src/lib/components/new-contact-drawer.js.map +1 -0
- package/src/lib/components/org-lead-surfaces-note.d.ts +57 -0
- package/src/lib/components/org-lead-surfaces-note.js +172 -0
- package/src/lib/components/org-lead-surfaces-note.js.map +1 -0
- package/src/lib/components/owner-avatar.d.ts +18 -0
- package/src/lib/components/owner-avatar.js +61 -0
- package/src/lib/components/owner-avatar.js.map +1 -0
- package/src/lib/components/pipeline-stages-dialog.d.ts +43 -0
- package/src/lib/components/pipeline-stages-dialog.js +339 -0
- package/src/lib/components/pipeline-stages-dialog.js.map +1 -0
- package/src/lib/components/pipelines-dialog.d.ts +55 -0
- package/src/lib/components/pipelines-dialog.js +517 -0
- package/src/lib/components/pipelines-dialog.js.map +1 -0
- package/src/lib/components/recent-activity-feed.d.ts +39 -0
- package/src/lib/components/recent-activity-feed.js +103 -0
- package/src/lib/components/recent-activity-feed.js.map +1 -0
- package/src/lib/components/recipes-card.d.ts +39 -0
- package/src/lib/components/recipes-card.js +485 -0
- package/src/lib/components/recipes-card.js.map +1 -0
- package/src/lib/components/record-activity-card.d.ts +26 -0
- package/src/lib/components/record-activity-card.js +121 -0
- package/src/lib/components/record-activity-card.js.map +1 -0
- package/src/lib/components/record-files-card.d.ts +67 -0
- package/src/lib/components/record-files-card.js +290 -0
- package/src/lib/components/record-files-card.js.map +1 -0
- package/src/lib/components/record-tasks-card.d.ts +32 -0
- package/src/lib/components/record-tasks-card.js +306 -0
- package/src/lib/components/record-tasks-card.js.map +1 -0
- package/src/lib/components/reports/activity-card.d.ts +57 -0
- package/src/lib/components/reports/activity-card.js +281 -0
- package/src/lib/components/reports/activity-card.js.map +1 -0
- package/src/lib/components/reports/closed-deals-card.d.ts +20 -0
- package/src/lib/components/reports/closed-deals-card.js +194 -0
- package/src/lib/components/reports/closed-deals-card.js.map +1 -0
- package/src/lib/components/reports/contacts-mix-card.d.ts +27 -0
- package/src/lib/components/reports/contacts-mix-card.js +161 -0
- package/src/lib/components/reports/contacts-mix-card.js.map +1 -0
- package/src/lib/components/reports/contacts-trend-card.d.ts +31 -0
- package/src/lib/components/reports/contacts-trend-card.js +144 -0
- package/src/lib/components/reports/contacts-trend-card.js.map +1 -0
- package/src/lib/components/reports/forecast-card.d.ts +22 -0
- package/src/lib/components/reports/forecast-card.js +251 -0
- package/src/lib/components/reports/forecast-card.js.map +1 -0
- package/src/lib/components/reports/lead-counts.d.ts +32 -0
- package/src/lib/components/reports/lead-counts.js +44 -0
- package/src/lib/components/reports/lead-counts.js.map +1 -0
- package/src/lib/components/reports/lead-funnel-card.d.ts +38 -0
- package/src/lib/components/reports/lead-funnel-card.js +375 -0
- package/src/lib/components/reports/lead-funnel-card.js.map +1 -0
- package/src/lib/components/reports/pipeline-card.d.ts +19 -0
- package/src/lib/components/reports/pipeline-card.js +338 -0
- package/src/lib/components/reports/pipeline-card.js.map +1 -0
- package/src/lib/components/reports/report-bar-chart.d.ts +39 -0
- package/src/lib/components/reports/report-bar-chart.js +133 -0
- package/src/lib/components/reports/report-bar-chart.js.map +1 -0
- package/src/lib/components/reports/report-breakdown.d.ts +32 -0
- package/src/lib/components/reports/report-breakdown.js +105 -0
- package/src/lib/components/reports/report-breakdown.js.map +1 -0
- package/src/lib/components/reports/report-export.d.ts +37 -0
- package/src/lib/components/reports/report-export.js +66 -0
- package/src/lib/components/reports/report-export.js.map +1 -0
- package/src/lib/components/reports/report-format.d.ts +36 -0
- package/src/lib/components/reports/report-format.js +43 -0
- package/src/lib/components/reports/report-format.js.map +1 -0
- package/src/lib/components/reports/report-scope.d.ts +81 -0
- package/src/lib/components/reports/report-scope.js +58 -0
- package/src/lib/components/reports/report-scope.js.map +1 -0
- package/src/lib/components/reports/report-stat-tile.d.ts +47 -0
- package/src/lib/components/reports/report-stat-tile.js +96 -0
- package/src/lib/components/reports/report-stat-tile.js.map +1 -0
- package/src/lib/components/reports/source-conversion-card.d.ts +28 -0
- package/src/lib/components/reports/source-conversion-card.js +205 -0
- package/src/lib/components/reports/source-conversion-card.js.map +1 -0
- package/src/lib/components/reports/tasks-card.d.ts +26 -0
- package/src/lib/components/reports/tasks-card.js +279 -0
- package/src/lib/components/reports/tasks-card.js.map +1 -0
- package/src/lib/components/reports/use-aggregate-read.d.ts +99 -0
- package/src/lib/components/reports/use-aggregate-read.js +195 -0
- package/src/lib/components/reports/use-aggregate-read.js.map +1 -0
- package/src/lib/components/reports/won-lost-by-owner-card.d.ts +30 -0
- package/src/lib/components/reports/won-lost-by-owner-card.js +168 -0
- package/src/lib/components/reports/won-lost-by-owner-card.js.map +1 -0
- package/src/lib/components/reports-section.d.ts +42 -0
- package/src/lib/components/reports-section.js +308 -0
- package/src/lib/components/reports-section.js.map +1 -0
- package/src/lib/components/sending-addresses-card.d.ts +49 -0
- package/src/lib/components/sending-addresses-card.js +262 -0
- package/src/lib/components/sending-addresses-card.js.map +1 -0
- package/src/lib/components/settings-section.d.ts +151 -0
- package/src/lib/components/settings-section.js +783 -0
- package/src/lib/components/settings-section.js.map +1 -0
- package/src/lib/components/task-cells.d.ts +51 -0
- package/src/lib/components/task-cells.js +139 -0
- package/src/lib/components/task-cells.js.map +1 -0
- package/src/lib/components/task-edit-drawer.d.ts +53 -0
- package/src/lib/components/task-edit-drawer.js +483 -0
- package/src/lib/components/task-edit-drawer.js.map +1 -0
- package/src/lib/components/task-import-drawer.d.ts +24 -0
- package/src/lib/components/task-import-drawer.js +82 -0
- package/src/lib/components/task-import-drawer.js.map +1 -0
- package/src/lib/components/task-snooze-menu.d.ts +65 -0
- package/src/lib/components/task-snooze-menu.js +207 -0
- package/src/lib/components/task-snooze-menu.js.map +1 -0
- package/src/lib/components/tasks-bulk-bar.d.ts +25 -0
- package/src/lib/components/tasks-bulk-bar.js +348 -0
- package/src/lib/components/tasks-bulk-bar.js.map +1 -0
- package/src/lib/components/tasks-calendar.d.ts +38 -0
- package/src/lib/components/tasks-calendar.js +228 -0
- package/src/lib/components/tasks-calendar.js.map +1 -0
- package/src/lib/components/tasks-section.d.ts +27 -0
- package/src/lib/components/tasks-section.js +579 -0
- package/src/lib/components/tasks-section.js.map +1 -0
- package/src/lib/components/use-contact-campaign-emails.d.ts +32 -0
- package/src/lib/components/use-contact-campaign-emails.js +83 -0
- package/src/lib/components/use-contact-campaign-emails.js.map +1 -0
- package/src/lib/components/use-crm-api.d.ts +44 -0
- package/src/lib/components/use-crm-api.js +84 -0
- package/src/lib/components/use-crm-api.js.map +1 -0
- package/src/lib/components/use-crm-inbound-address.d.ts +60 -0
- package/src/lib/components/use-crm-inbound-address.js +124 -0
- package/src/lib/components/use-crm-inbound-address.js.map +1 -0
- package/src/lib/components/use-emails-hub-path.d.ts +24 -0
- package/src/lib/components/use-emails-hub-path.js +59 -0
- package/src/lib/components/use-emails-hub-path.js.map +1 -0
- package/src/lib/components/use-member-email-aliases.d.ts +54 -0
- package/src/lib/components/use-member-email-aliases.js +186 -0
- package/src/lib/components/use-member-email-aliases.js.map +1 -0
- package/src/lib/components/use-org-members.d.ts +40 -0
- package/src/lib/components/use-org-members.js +100 -0
- package/src/lib/components/use-org-members.js.map +1 -0
- package/src/lib/constants/api-routes.d.ts +109 -0
- package/src/lib/constants/api-routes.js +74 -0
- package/src/lib/constants/api-routes.js.map +1 -0
- package/src/lib/constants/bundle-common.d.ts +14 -0
- package/src/lib/constants/bundle-common.js +14 -0
- package/src/lib/constants/bundle-common.js.map +1 -0
- package/src/lib/constants/company-filters.d.ts +18 -0
- package/src/lib/constants/company-filters.js +51 -0
- package/src/lib/constants/company-filters.js.map +1 -0
- package/src/lib/constants/contact-filters.d.ts +38 -0
- package/src/lib/constants/contact-filters.js +294 -0
- package/src/lib/constants/contact-filters.js.map +1 -0
- package/src/lib/hooks/use-contact-field-definitions.d.ts +48 -0
- package/src/lib/hooks/use-contact-field-definitions.js +72 -0
- package/src/lib/hooks/use-contact-field-definitions.js.map +1 -0
- package/src/lib/hooks/use-contact-update.d.ts +25 -0
- package/src/lib/hooks/use-contact-update.js +80 -0
- package/src/lib/hooks/use-contact-update.js.map +1 -0
- package/src/lib/hooks/use-crm-activity-logger.d.ts +73 -0
- package/src/lib/hooks/use-crm-activity-logger.js +101 -0
- package/src/lib/hooks/use-crm-activity-logger.js.map +1 -0
- package/src/lib/hooks/use-crm-bulk-apply.d.ts +52 -0
- package/src/lib/hooks/use-crm-bulk-apply.js +112 -0
- package/src/lib/hooks/use-crm-bulk-apply.js.map +1 -0
- package/src/lib/hooks/use-crm-default-view.d.ts +24 -0
- package/src/lib/hooks/use-crm-default-view.js +64 -0
- package/src/lib/hooks/use-crm-default-view.js.map +1 -0
- package/src/lib/hooks/use-crm-email-templates.d.ts +59 -0
- package/src/lib/hooks/use-crm-email-templates.js +103 -0
- package/src/lib/hooks/use-crm-email-templates.js.map +1 -0
- package/src/lib/hooks/use-crm-hub-path.d.ts +29 -0
- package/src/lib/hooks/use-crm-hub-path.js +41 -0
- package/src/lib/hooks/use-crm-hub-path.js.map +1 -0
- package/src/lib/hooks/use-crm-org-mount.d.ts +97 -0
- package/src/lib/hooks/use-crm-org-mount.js +215 -0
- package/src/lib/hooks/use-crm-org-mount.js.map +1 -0
- package/src/lib/hooks/use-crm-record-names.d.ts +52 -0
- package/src/lib/hooks/use-crm-record-names.js +126 -0
- package/src/lib/hooks/use-crm-record-names.js.map +1 -0
- package/src/lib/hooks/use-crm-records-quota.d.ts +68 -0
- package/src/lib/hooks/use-crm-records-quota.js +118 -0
- package/src/lib/hooks/use-crm-records-quota.js.map +1 -0
- package/src/lib/hooks/use-crm-saved-view.d.ts +96 -0
- package/src/lib/hooks/use-crm-saved-view.js +329 -0
- package/src/lib/hooks/use-crm-saved-view.js.map +1 -0
- package/src/lib/hooks/use-crm-scope.d.ts +117 -0
- package/src/lib/hooks/use-crm-scope.js +147 -0
- package/src/lib/hooks/use-crm-scope.js.map +1 -0
- package/src/lib/hooks/use-crm-tasks.d.ts +85 -0
- package/src/lib/hooks/use-crm-tasks.js +153 -0
- package/src/lib/hooks/use-crm-tasks.js.map +1 -0
- package/src/lib/hooks/use-crm-view-grid.d.ts +55 -0
- package/src/lib/hooks/use-crm-view-grid.js +170 -0
- package/src/lib/hooks/use-crm-view-grid.js.map +1 -0
- package/src/lib/hooks/use-crm-views.d.ts +42 -0
- package/src/lib/hooks/use-crm-views.js +84 -0
- package/src/lib/hooks/use-crm-views.js.map +1 -0
- package/src/lib/hooks/use-deal-stage-api.d.ts +69 -0
- package/src/lib/hooks/use-deal-stage-api.js +78 -0
- package/src/lib/hooks/use-deal-stage-api.js.map +1 -0
- package/src/lib/hooks/use-deals.d.ts +47 -0
- package/src/lib/hooks/use-deals.js +105 -0
- package/src/lib/hooks/use-deals.js.map +1 -0
- package/src/lib/hooks/use-org-leads.d.ts +53 -0
- package/src/lib/hooks/use-org-leads.js +114 -0
- package/src/lib/hooks/use-org-leads.js.map +1 -0
- package/src/lib/hooks/use-org-member-directory.d.ts +36 -0
- package/src/lib/hooks/use-org-member-directory.js +116 -0
- package/src/lib/hooks/use-org-member-directory.js.map +1 -0
- package/src/lib/hooks/use-org-member-options.d.ts +38 -0
- package/src/lib/hooks/use-org-member-options.js +131 -0
- package/src/lib/hooks/use-org-member-options.js.map +1 -0
- package/src/lib/hooks/use-pipeline.d.ts +71 -0
- package/src/lib/hooks/use-pipeline.js +159 -0
- package/src/lib/hooks/use-pipeline.js.map +1 -0
- package/src/lib/model/companies-bulk-writes.d.ts +40 -0
- package/src/lib/model/companies-bulk-writes.js +103 -0
- package/src/lib/model/companies-bulk-writes.js.map +1 -0
- package/src/lib/model/companies-csv.d.ts +36 -0
- package/src/lib/model/companies-csv.js +38 -0
- package/src/lib/model/companies-csv.js.map +1 -0
- package/src/lib/model/companies.d.ts +99 -0
- package/src/lib/model/companies.js +168 -0
- package/src/lib/model/companies.js.map +1 -0
- package/src/lib/model/company-delete-route.d.ts +43 -0
- package/src/lib/model/company-delete-route.js +31 -0
- package/src/lib/model/company-delete-route.js.map +1 -0
- package/src/lib/model/company-delete.d.ts +42 -0
- package/src/lib/model/company-delete.js +52 -0
- package/src/lib/model/company-delete.js.map +1 -0
- package/src/lib/model/contact-duplicates.d.ts +58 -0
- package/src/lib/model/contact-duplicates.js +67 -0
- package/src/lib/model/contact-duplicates.js.map +1 -0
- package/src/lib/model/contact-record.d.ts +113 -0
- package/src/lib/model/contact-record.js +70 -0
- package/src/lib/model/contact-record.js.map +1 -0
- package/src/lib/model/contact-update.d.ts +93 -0
- package/src/lib/model/contact-update.js +50 -0
- package/src/lib/model/contact-update.js.map +1 -0
- package/src/lib/model/contacts-bulk-writes.d.ts +173 -0
- package/src/lib/model/contacts-bulk-writes.js +196 -0
- package/src/lib/model/contacts-bulk-writes.js.map +1 -0
- package/src/lib/model/contacts-csv.d.ts +54 -0
- package/src/lib/model/contacts-csv.js +67 -0
- package/src/lib/model/contacts-csv.js.map +1 -0
- package/src/lib/model/contacts-list-seed.d.ts +51 -0
- package/src/lib/model/contacts-list-seed.js +63 -0
- package/src/lib/model/contacts-list-seed.js.map +1 -0
- package/src/lib/model/crm-activity-report.d.ts +27 -0
- package/src/lib/model/crm-activity-report.js +56 -0
- package/src/lib/model/crm-activity-report.js.map +1 -0
- package/src/lib/model/crm-api.d.ts +49 -0
- package/src/lib/model/crm-api.js +47 -0
- package/src/lib/model/crm-api.js.map +1 -0
- package/src/lib/model/crm-bulk-writes.d.ts +147 -0
- package/src/lib/model/crm-bulk-writes.js +207 -0
- package/src/lib/model/crm-bulk-writes.js.map +1 -0
- package/src/lib/model/crm-company-import.d.ts +154 -0
- package/src/lib/model/crm-company-import.js +331 -0
- package/src/lib/model/crm-company-import.js.map +1 -0
- package/src/lib/model/crm-custom-draft.d.ts +62 -0
- package/src/lib/model/crm-custom-draft.js +67 -0
- package/src/lib/model/crm-custom-draft.js.map +1 -0
- package/src/lib/model/crm-deal-import.d.ts +112 -0
- package/src/lib/model/crm-deal-import.js +263 -0
- package/src/lib/model/crm-deal-import.js.map +1 -0
- package/src/lib/model/crm-import.d.ts +244 -0
- package/src/lib/model/crm-import.js +466 -0
- package/src/lib/model/crm-import.js.map +1 -0
- package/src/lib/model/crm-lead-import.d.ts +105 -0
- package/src/lib/model/crm-lead-import.js +255 -0
- package/src/lib/model/crm-lead-import.js.map +1 -0
- package/src/lib/model/crm-record-routes.d.ts +21 -0
- package/src/lib/model/crm-record-routes.js +70 -0
- package/src/lib/model/crm-record-routes.js.map +1 -0
- package/src/lib/model/crm-routes.d.ts +101 -0
- package/src/lib/model/crm-routes.js +92 -0
- package/src/lib/model/crm-routes.js.map +1 -0
- package/src/lib/model/crm-task-import.d.ts +94 -0
- package/src/lib/model/crm-task-import.js +275 -0
- package/src/lib/model/crm-task-import.js.map +1 -0
- package/src/lib/model/crm-view-param.d.ts +43 -0
- package/src/lib/model/crm-view-param.js +49 -0
- package/src/lib/model/crm-view-param.js.map +1 -0
- package/src/lib/model/deal-board-model.d.ts +241 -0
- package/src/lib/model/deal-board-model.js +439 -0
- package/src/lib/model/deal-board-model.js.map +1 -0
- package/src/lib/model/deal-form-model.d.ts +114 -0
- package/src/lib/model/deal-form-model.js +218 -0
- package/src/lib/model/deal-form-model.js.map +1 -0
- package/src/lib/model/deals-csv.d.ts +32 -0
- package/src/lib/model/deals-csv.js +37 -0
- package/src/lib/model/deals-csv.js.map +1 -0
- package/src/lib/model/import-column-shapes.d.ts +36 -0
- package/src/lib/model/import-column-shapes.js +76 -0
- package/src/lib/model/import-column-shapes.js.map +1 -0
- package/src/lib/model/lead-company-suggestion.d.ts +61 -0
- package/src/lib/model/lead-company-suggestion.js +65 -0
- package/src/lib/model/lead-company-suggestion.js.map +1 -0
- package/src/lib/model/lead-filters.d.ts +29 -0
- package/src/lib/model/lead-filters.js +39 -0
- package/src/lib/model/lead-filters.js.map +1 -0
- package/src/lib/model/lead-surfaces.d.ts +58 -0
- package/src/lib/model/lead-surfaces.js +67 -0
- package/src/lib/model/lead-surfaces.js.map +1 -0
- package/src/lib/model/leads-csv.d.ts +40 -0
- package/src/lib/model/leads-csv.js +40 -0
- package/src/lib/model/leads-csv.js.map +1 -0
- package/src/lib/model/next-activity-api.d.ts +31 -0
- package/src/lib/model/next-activity-api.js +57 -0
- package/src/lib/model/next-activity-api.js.map +1 -0
- package/src/lib/model/next-activity.d.ts +64 -0
- package/src/lib/model/next-activity.js +47 -0
- package/src/lib/model/next-activity.js.map +1 -0
- package/src/lib/model/person-key-web.d.ts +43 -0
- package/src/lib/model/person-key-web.js +52 -0
- package/src/lib/model/person-key-web.js.map +1 -0
- package/src/lib/model/record-facts.d.ts +282 -0
- package/src/lib/model/record-facts.js +434 -0
- package/src/lib/model/record-facts.js.map +1 -0
- package/src/lib/model/task-api.d.ts +28 -0
- package/src/lib/model/task-api.js +57 -0
- package/src/lib/model/task-api.js.map +1 -0
- package/src/lib/model/task-calendar.d.ts +108 -0
- package/src/lib/model/task-calendar.js +139 -0
- package/src/lib/model/task-calendar.js.map +1 -0
- package/src/lib/model/task-routes.d.ts +177 -0
- package/src/lib/model/task-routes.js +197 -0
- package/src/lib/model/task-routes.js.map +1 -0
- package/src/lib/model/task-scope.d.ts +45 -0
- package/src/lib/model/task-scope.js +51 -0
- package/src/lib/model/task-scope.js.map +1 -0
- package/src/lib/model/task-views.d.ts +149 -0
- package/src/lib/model/task-views.js +286 -0
- package/src/lib/model/task-views.js.map +1 -0
- package/src/lib/model/tasks-csv.d.ts +28 -0
- package/src/lib/model/tasks-csv.js +28 -0
- package/src/lib/model/tasks-csv.js.map +1 -0
- package/src/lib/plugin.d.ts +32 -0
- package/src/lib/plugin.js +208 -0
- package/src/lib/plugin.js.map +1 -0
- package/src/lib/server/companies-import.d.ts +57 -0
- package/src/lib/server/companies-import.js +242 -0
- package/src/lib/server/companies-import.js.map +1 -0
- package/src/lib/server/company-delete.d.ts +59 -0
- package/src/lib/server/company-delete.js +146 -0
- package/src/lib/server/company-delete.js.map +1 -0
- package/src/lib/server/contact-email-history.d.ts +82 -0
- package/src/lib/server/contact-email-history.js +264 -0
- package/src/lib/server/contact-email-history.js.map +1 -0
- package/src/lib/server/contact-profile.d.ts +41 -0
- package/src/lib/server/contact-profile.js +45 -0
- package/src/lib/server/contact-profile.js.map +1 -0
- package/src/lib/server/contact-update.d.ts +92 -0
- package/src/lib/server/contact-update.js +512 -0
- package/src/lib/server/contact-update.js.map +1 -0
- package/src/lib/server/contacts-import.d.ts +73 -0
- package/src/lib/server/contacts-import.js +263 -0
- package/src/lib/server/contacts-import.js.map +1 -0
- package/src/lib/server/contacts-merge.d.ts +86 -0
- package/src/lib/server/contacts-merge.js +206 -0
- package/src/lib/server/contacts-merge.js.map +1 -0
- package/src/lib/server/deals-import.d.ts +76 -0
- package/src/lib/server/deals-import.js +246 -0
- package/src/lib/server/deals-import.js.map +1 -0
- package/src/lib/server/email-send.d.ts +119 -0
- package/src/lib/server/email-send.js +667 -0
- package/src/lib/server/email-send.js.map +1 -0
- package/src/lib/server/email-template-duplicate.d.ts +19 -0
- package/src/lib/server/email-template-duplicate.js +206 -0
- package/src/lib/server/email-template-duplicate.js.map +1 -0
- package/src/lib/server/erase-person.d.ts +108 -0
- package/src/lib/server/erase-person.js +309 -0
- package/src/lib/server/erase-person.js.map +1 -0
- package/src/lib/server/import-context.d.ts +85 -0
- package/src/lib/server/import-context.js +181 -0
- package/src/lib/server/import-context.js.map +1 -0
- package/src/lib/server/inbound-address.d.ts +61 -0
- package/src/lib/server/inbound-address.js +188 -0
- package/src/lib/server/inbound-address.js.map +1 -0
- package/src/lib/server/lead-convert.d.ts +78 -0
- package/src/lib/server/lead-convert.js +250 -0
- package/src/lib/server/lead-convert.js.map +1 -0
- package/src/lib/server/leads-import.d.ts +84 -0
- package/src/lib/server/leads-import.js +261 -0
- package/src/lib/server/leads-import.js.map +1 -0
- package/src/lib/server/next-activity-routes.d.ts +33 -0
- package/src/lib/server/next-activity-routes.js +110 -0
- package/src/lib/server/next-activity-routes.js.map +1 -0
- package/src/lib/server/org-activity.d.ts +61 -0
- package/src/lib/server/org-activity.js +120 -0
- package/src/lib/server/org-activity.js.map +1 -0
- package/src/lib/server/org-caller.d.ts +110 -0
- package/src/lib/server/org-caller.js +154 -0
- package/src/lib/server/org-caller.js.map +1 -0
- package/src/lib/server/recipe-routes.d.ts +75 -0
- package/src/lib/server/recipe-routes.js +442 -0
- package/src/lib/server/recipe-routes.js.map +1 -0
- package/src/lib/server/record-facts.d.ts +65 -0
- package/src/lib/server/record-facts.js +337 -0
- package/src/lib/server/record-facts.js.map +1 -0
- package/src/lib/server/record-timeline.d.ts +51 -0
- package/src/lib/server/record-timeline.js +215 -0
- package/src/lib/server/record-timeline.js.map +1 -0
- package/src/lib/server/suite-gate.d.ts +42 -0
- package/src/lib/server/suite-gate.js +87 -0
- package/src/lib/server/suite-gate.js.map +1 -0
- package/src/lib/server/task-routes.d.ts +132 -0
- package/src/lib/server/task-routes.js +699 -0
- package/src/lib/server/task-routes.js.map +1 -0
- package/src/lib/server/tasks-import.d.ts +54 -0
- package/src/lib/server/tasks-import.js +157 -0
- package/src/lib/server/tasks-import.js.map +1 -0
- package/src/lib/server-deal-stage.d.ts +88 -0
- package/src/lib/server-deal-stage.js +316 -0
- package/src/lib/server-deal-stage.js.map +1 -0
- package/src/lib/server.d.ts +149 -0
- package/src/lib/server.js +615 -0
- package/src/lib/server.js.map +1 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* `POST /api/crm/leads-import` — one chunk of a leads file, written
|
|
19
|
+
* (AGL-2701).
|
|
20
|
+
*
|
|
21
|
+
* The browser has already read the file and applied the operator's column
|
|
22
|
+
* mapping; what arrives here is up to {@link LEAD_IMPORT_CHUNK_SIZE} raw
|
|
23
|
+
* rows in the vocabulary `crm-lead-import.ts` defines. This route judges
|
|
24
|
+
* each one through the same normalizer, resolves the owner against the
|
|
25
|
+
* org's roster, and writes it in two halves.
|
|
26
|
+
*
|
|
27
|
+
* ## The capture half goes through the door every capture goes through
|
|
28
|
+
*
|
|
29
|
+
* `addHostLead` — the ONE writer of `hosts/{hostId}/leads` for the sign-up
|
|
30
|
+
* handler and both booking paths — is the writer here too. That is what
|
|
31
|
+
* makes an imported row key on `personKey` the way a second form
|
|
32
|
+
* submission does, land under the site by path with `capturedByHostIds`
|
|
33
|
+
* naming it, count against `LEADS_MAX_PER_HOST` inside the transaction
|
|
34
|
+
* that writes, and record NO marketing basis. A bulk path with its own
|
|
35
|
+
* `add()` would grow an unkeyed, unbounded copy of the site's leads under
|
|
36
|
+
* a second set of rules.
|
|
37
|
+
*
|
|
38
|
+
* ## A lead is host-scoped by path, so nothing here stamps `visibleTo`
|
|
39
|
+
*
|
|
40
|
+
* The five org collections carry `visibleTo` because they sit under
|
|
41
|
+
* `orgs/{orgId}` and the rules decide per document which sites may read
|
|
42
|
+
* them. A lead does not: `hosts/{hostId}/leads` is private to the site by
|
|
43
|
+
* path, the rules admit the site's own members, and the list therefore
|
|
44
|
+
* reads it with no scope clause. An import that stamped scope tokens onto
|
|
45
|
+
* a lead would be writing a field nothing reads, and would suggest a
|
|
46
|
+
* sharing decision the collection does not have. The site is the whole
|
|
47
|
+
* scope, and the drawer's picker is where it is chosen.
|
|
48
|
+
*
|
|
49
|
+
* ## No file may hand a lead a consent it did not give
|
|
50
|
+
*
|
|
51
|
+
* A capture writes a marketing basis only when the visitor ticked a box in
|
|
52
|
+
* front of them. A CSV row has no such event behind it, so the lead
|
|
53
|
+
* vocabulary has no consent column and this route passes no
|
|
54
|
+
* `marketingConsent`: an imported lead is mailable only if some earlier
|
|
55
|
+
* capture on this site already recorded a basis, which `addHostLead`
|
|
56
|
+
* carries forward untouched. The contacts import has a consent column
|
|
57
|
+
* because its own export writes one and the merchant is asserting a basis
|
|
58
|
+
* they hold per person; the leads export writes none, and a column the
|
|
59
|
+
* file cannot fill is a column that cannot be misread.
|
|
60
|
+
*
|
|
61
|
+
* ## The working half is written the way the list writes it
|
|
62
|
+
*
|
|
63
|
+
* A status, an owner, a reason and notes are the CRM's annotations, not
|
|
64
|
+
* the capture's, and the leads list already writes them client-direct onto
|
|
65
|
+
* the same document. A row that carries any of them gets one merge write
|
|
66
|
+
* after the door's, stamped `updatedAt` exactly as the list stamps it. A
|
|
67
|
+
* row that carries none costs nothing extra.
|
|
68
|
+
*
|
|
69
|
+
* ## Created or merged is read once for the chunk
|
|
70
|
+
*
|
|
71
|
+
* The door reports only whether the lead was STORED, and the result panel
|
|
72
|
+
* has to say how many people were added and how many updated. One
|
|
73
|
+
* `getAll` over the chunk's document ids answers that for every row in a
|
|
74
|
+
* single round trip, before any of them is written — which is also the
|
|
75
|
+
* only moment the answer is still true.
|
|
76
|
+
*/
|
|
77
|
+
import { type PluginApiHandler } from '@aglyn/aglyn/server';
|
|
78
|
+
/**
|
|
79
|
+
* `POST crm/leads-import` — `{ hostId, rows }` → a {@link LeadImportChunkResult}.
|
|
80
|
+
*
|
|
81
|
+
* Rows are written one after another so the ceiling's local count stays
|
|
82
|
+
* honest across the request.
|
|
83
|
+
*/
|
|
84
|
+
export declare const crmLeadsImportHandler: PluginApiHandler;
|
|
@@ -0,0 +1,261 @@
|
|
|
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
|
+
*/ /**
|
|
18
|
+
* `POST /api/crm/leads-import` — one chunk of a leads file, written
|
|
19
|
+
* (AGL-2701).
|
|
20
|
+
*
|
|
21
|
+
* The browser has already read the file and applied the operator's column
|
|
22
|
+
* mapping; what arrives here is up to {@link LEAD_IMPORT_CHUNK_SIZE} raw
|
|
23
|
+
* rows in the vocabulary `crm-lead-import.ts` defines. This route judges
|
|
24
|
+
* each one through the same normalizer, resolves the owner against the
|
|
25
|
+
* org's roster, and writes it in two halves.
|
|
26
|
+
*
|
|
27
|
+
* ## The capture half goes through the door every capture goes through
|
|
28
|
+
*
|
|
29
|
+
* `addHostLead` — the ONE writer of `hosts/{hostId}/leads` for the sign-up
|
|
30
|
+
* handler and both booking paths — is the writer here too. That is what
|
|
31
|
+
* makes an imported row key on `personKey` the way a second form
|
|
32
|
+
* submission does, land under the site by path with `capturedByHostIds`
|
|
33
|
+
* naming it, count against `LEADS_MAX_PER_HOST` inside the transaction
|
|
34
|
+
* that writes, and record NO marketing basis. A bulk path with its own
|
|
35
|
+
* `add()` would grow an unkeyed, unbounded copy of the site's leads under
|
|
36
|
+
* a second set of rules.
|
|
37
|
+
*
|
|
38
|
+
* ## A lead is host-scoped by path, so nothing here stamps `visibleTo`
|
|
39
|
+
*
|
|
40
|
+
* The five org collections carry `visibleTo` because they sit under
|
|
41
|
+
* `orgs/{orgId}` and the rules decide per document which sites may read
|
|
42
|
+
* them. A lead does not: `hosts/{hostId}/leads` is private to the site by
|
|
43
|
+
* path, the rules admit the site's own members, and the list therefore
|
|
44
|
+
* reads it with no scope clause. An import that stamped scope tokens onto
|
|
45
|
+
* a lead would be writing a field nothing reads, and would suggest a
|
|
46
|
+
* sharing decision the collection does not have. The site is the whole
|
|
47
|
+
* scope, and the drawer's picker is where it is chosen.
|
|
48
|
+
*
|
|
49
|
+
* ## No file may hand a lead a consent it did not give
|
|
50
|
+
*
|
|
51
|
+
* A capture writes a marketing basis only when the visitor ticked a box in
|
|
52
|
+
* front of them. A CSV row has no such event behind it, so the lead
|
|
53
|
+
* vocabulary has no consent column and this route passes no
|
|
54
|
+
* `marketingConsent`: an imported lead is mailable only if some earlier
|
|
55
|
+
* capture on this site already recorded a basis, which `addHostLead`
|
|
56
|
+
* carries forward untouched. The contacts import has a consent column
|
|
57
|
+
* because its own export writes one and the merchant is asserting a basis
|
|
58
|
+
* they hold per person; the leads export writes none, and a column the
|
|
59
|
+
* file cannot fill is a column that cannot be misread.
|
|
60
|
+
*
|
|
61
|
+
* ## The working half is written the way the list writes it
|
|
62
|
+
*
|
|
63
|
+
* A status, an owner, a reason and notes are the CRM's annotations, not
|
|
64
|
+
* the capture's, and the leads list already writes them client-direct onto
|
|
65
|
+
* the same document. A row that carries any of them gets one merge write
|
|
66
|
+
* after the door's, stamped `updatedAt` exactly as the list stamps it. A
|
|
67
|
+
* row that carries none costs nothing extra.
|
|
68
|
+
*
|
|
69
|
+
* ## Created or merged is read once for the chunk
|
|
70
|
+
*
|
|
71
|
+
* The door reports only whether the lead was STORED, and the result panel
|
|
72
|
+
* has to say how many people were added and how many updated. One
|
|
73
|
+
* `getAll` over the chunk's document ids answers that for every row in a
|
|
74
|
+
* single round trip, before any of them is written — which is also the
|
|
75
|
+
* only moment the answer is still true.
|
|
76
|
+
*/ import { checkVisitorRecordCeiling, LEADS_MAX_PER_HOST, personKey } from "@aglyn/aglyn/server";
|
|
77
|
+
import { LEAD_IMPORT_CHUNK_SIZE, LEAD_IMPORT_MAX_BODY_BYTES, normalizeLeadImportRow } from "../model/crm-lead-import.js";
|
|
78
|
+
import { addHostLead, firebaseAdmin } from "@aglyn/tenant-data-admin";
|
|
79
|
+
import { FieldValue } from "firebase-admin/firestore";
|
|
80
|
+
import { ownerDirectory, readImportRows, resolveImportContext } from "./import-context.js";
|
|
81
|
+
/**
|
|
82
|
+
* The surface an imported lead names, beside `signup`, `booking` and
|
|
83
|
+
* `form:{formId}`.
|
|
84
|
+
*
|
|
85
|
+
* Typed as the capture-source union rather than a bare string, which is
|
|
86
|
+
* what the contacts import gets for free from the door it calls: a lead's
|
|
87
|
+
* `source` is plain text on the way in, so this is the only place the word
|
|
88
|
+
* can be held to the same vocabulary the source filters and the labels
|
|
89
|
+
* read. One person brought in by one file reads the same on both records.
|
|
90
|
+
*/ const LEAD_IMPORT_SOURCE = 'import';
|
|
91
|
+
/** The team's annotations on one row, or nothing when the file named none. */ function workingState(row, ownerUid) {
|
|
92
|
+
const fields = _extends({}, row.status ? {
|
|
93
|
+
status: row.status
|
|
94
|
+
} : {}, ownerUid ? {
|
|
95
|
+
ownerUid
|
|
96
|
+
} : {}, row.unqualifiedReason ? {
|
|
97
|
+
unqualifiedReason: row.unqualifiedReason
|
|
98
|
+
} : {}, row.notes ? {
|
|
99
|
+
notes: row.notes
|
|
100
|
+
} : {});
|
|
101
|
+
return Object.keys(fields).length ? fields : null;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* `POST crm/leads-import` — `{ hostId, rows }` → a {@link LeadImportChunkResult}.
|
|
105
|
+
*
|
|
106
|
+
* Rows are written one after another so the ceiling's local count stays
|
|
107
|
+
* honest across the request.
|
|
108
|
+
*/ export const crmLeadsImportHandler = async (req, res)=>{
|
|
109
|
+
if (req.method !== 'POST') {
|
|
110
|
+
res.setHeader('Allow', 'POST');
|
|
111
|
+
return res.status(405).json({
|
|
112
|
+
error: 'Method not allowed'
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
const read = readImportRows(req, {
|
|
116
|
+
maxBodyBytes: LEAD_IMPORT_MAX_BODY_BYTES,
|
|
117
|
+
chunkSize: LEAD_IMPORT_CHUNK_SIZE
|
|
118
|
+
});
|
|
119
|
+
if ('error' in read) return res.status(read.status).json({
|
|
120
|
+
error: read.error
|
|
121
|
+
});
|
|
122
|
+
try {
|
|
123
|
+
const context = await resolveImportContext(req);
|
|
124
|
+
if (context.ok === false) return res.status(context.status).json(context.body);
|
|
125
|
+
const skipped = [];
|
|
126
|
+
const dropped = {};
|
|
127
|
+
const normalized = [];
|
|
128
|
+
/*
|
|
129
|
+
* A duplicate WITHIN the request is skipped rather than merged, for
|
|
130
|
+
* the reason the contacts import gives: the second row would merge
|
|
131
|
+
* onto the first's document and the tally would read one created and
|
|
132
|
+
* one merged for one person in one file. Across requests the door's
|
|
133
|
+
* own dedupe answers, and reports a merge.
|
|
134
|
+
*/ const seen = new Set();
|
|
135
|
+
read.rows.forEach((raw, index)=>{
|
|
136
|
+
const verdict = normalizeLeadImportRow(raw);
|
|
137
|
+
if (verdict.ok === false) {
|
|
138
|
+
skipped.push({
|
|
139
|
+
index,
|
|
140
|
+
email: verdict.input,
|
|
141
|
+
reason: verdict.reason
|
|
142
|
+
});
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// Non-null by construction: the normalizer refused every address
|
|
146
|
+
// this derivation could not key.
|
|
147
|
+
const key = personKey(verdict.row.email);
|
|
148
|
+
if (seen.has(key)) {
|
|
149
|
+
skipped.push({
|
|
150
|
+
index,
|
|
151
|
+
email: verdict.row.email,
|
|
152
|
+
reason: 'duplicate'
|
|
153
|
+
});
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
seen.add(key);
|
|
157
|
+
for (const entry of verdict.row.dropped){
|
|
158
|
+
var _dropped_entry_field;
|
|
159
|
+
dropped[entry.field] = ((_dropped_entry_field = dropped[entry.field]) != null ? _dropped_entry_field : 0) + 1;
|
|
160
|
+
}
|
|
161
|
+
normalized.push({
|
|
162
|
+
index,
|
|
163
|
+
row: verdict.row,
|
|
164
|
+
key
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
const firestore = firebaseAdmin.app().firestore();
|
|
168
|
+
const hostRef = firestore.collection('hosts').doc(context.hostId);
|
|
169
|
+
const leadsRef = hostRef.collection('leads');
|
|
170
|
+
const refs = normalized.map((entry)=>leadsRef.doc(entry.key));
|
|
171
|
+
const [owners, before] = await Promise.all([
|
|
172
|
+
ownerDirectory(context.orgId, normalized.map((entry)=>entry.row)),
|
|
173
|
+
refs.length ? firestore.getAll(...refs) : Promise.resolve([])
|
|
174
|
+
]);
|
|
175
|
+
const held = new Set(before.filter((snapshot)=>snapshot.exists).map((snapshot)=>snapshot.id));
|
|
176
|
+
const ownersUnresolved = new Set();
|
|
177
|
+
let counted = null;
|
|
178
|
+
let createdHere = 0;
|
|
179
|
+
let created = 0;
|
|
180
|
+
let merged = 0;
|
|
181
|
+
for (const { index, row, key } of normalized){
|
|
182
|
+
const isNew = !held.has(key);
|
|
183
|
+
/*
|
|
184
|
+
* The platform lead ceiling, judged the way the deals import judges
|
|
185
|
+
* the records band: one count at the first create, re-judged locally
|
|
186
|
+
* as the request creates more. The door re-judges it authoritatively
|
|
187
|
+
* inside its own transaction, so a race is still refused there — this
|
|
188
|
+
* is what lets the operator's skipped file say WHY rather than only
|
|
189
|
+
* that the row could not be saved.
|
|
190
|
+
*/ if (isNew) {
|
|
191
|
+
if (counted === null) {
|
|
192
|
+
counted = (await leadsRef.count().get()).data().count;
|
|
193
|
+
}
|
|
194
|
+
if (checkVisitorRecordCeiling(counted + createdHere, LEADS_MAX_PER_HOST).exceeded) {
|
|
195
|
+
skipped.push({
|
|
196
|
+
index,
|
|
197
|
+
email: row.email,
|
|
198
|
+
reason: 'lead-ceiling'
|
|
199
|
+
});
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
let ownerUid;
|
|
204
|
+
if (row.ownerEmail) {
|
|
205
|
+
ownerUid = owners.get(row.ownerEmail);
|
|
206
|
+
if (!ownerUid) ownersUnresolved.add(row.ownerEmail);
|
|
207
|
+
}
|
|
208
|
+
const stored = await addHostLead({
|
|
209
|
+
hostRef,
|
|
210
|
+
hostId: context.hostId,
|
|
211
|
+
lead: _extends({
|
|
212
|
+
email: row.email
|
|
213
|
+
}, row.name ? {
|
|
214
|
+
name: row.name
|
|
215
|
+
} : {}, {
|
|
216
|
+
source: LEAD_IMPORT_SOURCE
|
|
217
|
+
})
|
|
218
|
+
});
|
|
219
|
+
if (!stored) {
|
|
220
|
+
skipped.push({
|
|
221
|
+
index,
|
|
222
|
+
email: row.email,
|
|
223
|
+
reason: 'write-failed'
|
|
224
|
+
});
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const working = workingState(row, ownerUid);
|
|
228
|
+
if (working) {
|
|
229
|
+
await leadsRef.doc(key).set(_extends({}, working, {
|
|
230
|
+
updatedAt: FieldValue.serverTimestamp()
|
|
231
|
+
}), {
|
|
232
|
+
merge: true
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
if (isNew) {
|
|
236
|
+
created += 1;
|
|
237
|
+
createdHere += 1;
|
|
238
|
+
} else {
|
|
239
|
+
merged += 1;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
const result = {
|
|
243
|
+
received: read.rows.length,
|
|
244
|
+
created,
|
|
245
|
+
merged,
|
|
246
|
+
skipped: skipped.sort((a, b)=>a.index - b.index),
|
|
247
|
+
dropped,
|
|
248
|
+
ownersUnresolved: [
|
|
249
|
+
...ownersUnresolved
|
|
250
|
+
]
|
|
251
|
+
};
|
|
252
|
+
return res.status(200).json(result);
|
|
253
|
+
} catch (error) {
|
|
254
|
+
console.error('crm/leads-import failed', error);
|
|
255
|
+
return res.status(500).json({
|
|
256
|
+
error: 'The import could not continue.'
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
//# sourceMappingURL=leads-import.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/server/leads-import.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\n/**\n * `POST /api/crm/leads-import` — one chunk of a leads file, written\n * (AGL-2701).\n *\n * The browser has already read the file and applied the operator's column\n * mapping; what arrives here is up to {@link LEAD_IMPORT_CHUNK_SIZE} raw\n * rows in the vocabulary `crm-lead-import.ts` defines. This route judges\n * each one through the same normalizer, resolves the owner against the\n * org's roster, and writes it in two halves.\n *\n * ## The capture half goes through the door every capture goes through\n *\n * `addHostLead` — the ONE writer of `hosts/{hostId}/leads` for the sign-up\n * handler and both booking paths — is the writer here too. That is what\n * makes an imported row key on `personKey` the way a second form\n * submission does, land under the site by path with `capturedByHostIds`\n * naming it, count against `LEADS_MAX_PER_HOST` inside the transaction\n * that writes, and record NO marketing basis. A bulk path with its own\n * `add()` would grow an unkeyed, unbounded copy of the site's leads under\n * a second set of rules.\n *\n * ## A lead is host-scoped by path, so nothing here stamps `visibleTo`\n *\n * The five org collections carry `visibleTo` because they sit under\n * `orgs/{orgId}` and the rules decide per document which sites may read\n * them. A lead does not: `hosts/{hostId}/leads` is private to the site by\n * path, the rules admit the site's own members, and the list therefore\n * reads it with no scope clause. An import that stamped scope tokens onto\n * a lead would be writing a field nothing reads, and would suggest a\n * sharing decision the collection does not have. The site is the whole\n * scope, and the drawer's picker is where it is chosen.\n *\n * ## No file may hand a lead a consent it did not give\n *\n * A capture writes a marketing basis only when the visitor ticked a box in\n * front of them. A CSV row has no such event behind it, so the lead\n * vocabulary has no consent column and this route passes no\n * `marketingConsent`: an imported lead is mailable only if some earlier\n * capture on this site already recorded a basis, which `addHostLead`\n * carries forward untouched. The contacts import has a consent column\n * because its own export writes one and the merchant is asserting a basis\n * they hold per person; the leads export writes none, and a column the\n * file cannot fill is a column that cannot be misread.\n *\n * ## The working half is written the way the list writes it\n *\n * A status, an owner, a reason and notes are the CRM's annotations, not\n * the capture's, and the leads list already writes them client-direct onto\n * the same document. A row that carries any of them gets one merge write\n * after the door's, stamped `updatedAt` exactly as the list stamps it. A\n * row that carries none costs nothing extra.\n *\n * ## Created or merged is read once for the chunk\n *\n * The door reports only whether the lead was STORED, and the result panel\n * has to say how many people were added and how many updated. One\n * `getAll` over the chunk's document ids answers that for every row in a\n * single round trip, before any of them is written — which is also the\n * only moment the answer is still true.\n */\n\nimport {\n checkVisitorRecordCeiling,\n type ContactSource,\n LEADS_MAX_PER_HOST,\n personKey,\n type PluginApiHandler,\n} from '@aglyn/aglyn/server'\nimport {\n LEAD_IMPORT_CHUNK_SIZE,\n LEAD_IMPORT_MAX_BODY_BYTES,\n type LeadImportChunkResult,\n type LeadImportRawRow,\n type LeadImportRow,\n type LeadImportSkippedRow,\n normalizeLeadImportRow,\n} from '../model/crm-lead-import'\nimport { addHostLead, firebaseAdmin } from '@aglyn/tenant-data-admin'\nimport { FieldValue } from 'firebase-admin/firestore'\nimport {\n ownerDirectory,\n readImportRows,\n resolveImportContext,\n} from './import-context'\n\n/**\n * The surface an imported lead names, beside `signup`, `booking` and\n * `form:{formId}`.\n *\n * Typed as the capture-source union rather than a bare string, which is\n * what the contacts import gets for free from the door it calls: a lead's\n * `source` is plain text on the way in, so this is the only place the word\n * can be held to the same vocabulary the source filters and the labels\n * read. One person brought in by one file reads the same on both records.\n */\nconst LEAD_IMPORT_SOURCE: ContactSource = 'import'\n\n/** The team's annotations on one row, or nothing when the file named none. */\nfunction workingState(\n row: LeadImportRow,\n ownerUid: string | undefined,\n): Record<string, unknown> | null {\n const fields: Record<string, unknown> = {\n ...(row.status ? { status: row.status } : {}),\n ...(ownerUid ? { ownerUid } : {}),\n ...(row.unqualifiedReason ? { unqualifiedReason: row.unqualifiedReason } : {}),\n ...(row.notes ? { notes: row.notes } : {}),\n }\n return Object.keys(fields).length ? fields : null\n}\n\n/**\n * `POST crm/leads-import` — `{ hostId, rows }` → a {@link LeadImportChunkResult}.\n *\n * Rows are written one after another so the ceiling's local count stays\n * honest across the request.\n */\nexport const crmLeadsImportHandler: PluginApiHandler = async (req, res) => {\n if (req.method !== 'POST') {\n res.setHeader('Allow', 'POST')\n return res.status(405).json({ error: 'Method not allowed' })\n }\n const read = readImportRows<LeadImportRawRow>(req, {\n maxBodyBytes: LEAD_IMPORT_MAX_BODY_BYTES,\n chunkSize: LEAD_IMPORT_CHUNK_SIZE,\n })\n if ('error' in read) return res.status(read.status).json({ error: read.error })\n try {\n const context = await resolveImportContext(req)\n if (context.ok === false) return res.status(context.status).json(context.body)\n\n const skipped: LeadImportSkippedRow[] = []\n const dropped: Record<string, number> = {}\n const normalized: { index: number; row: LeadImportRow; key: string }[] = []\n /*\n * A duplicate WITHIN the request is skipped rather than merged, for\n * the reason the contacts import gives: the second row would merge\n * onto the first's document and the tally would read one created and\n * one merged for one person in one file. Across requests the door's\n * own dedupe answers, and reports a merge.\n */\n const seen = new Set<string>()\n read.rows.forEach((raw, index) => {\n const verdict = normalizeLeadImportRow(raw)\n if (verdict.ok === false) {\n skipped.push({ index, email: verdict.input, reason: verdict.reason })\n return\n }\n // Non-null by construction: the normalizer refused every address\n // this derivation could not key.\n const key = personKey(verdict.row.email) as string\n if (seen.has(key)) {\n skipped.push({ index, email: verdict.row.email, reason: 'duplicate' })\n return\n }\n seen.add(key)\n for (const entry of verdict.row.dropped) {\n dropped[entry.field] = (dropped[entry.field] ?? 0) + 1\n }\n normalized.push({ index, row: verdict.row, key })\n })\n\n const firestore = firebaseAdmin.app().firestore()\n const hostRef = firestore.collection('hosts').doc(context.hostId)\n const leadsRef = hostRef.collection('leads')\n const refs = normalized.map((entry) => leadsRef.doc(entry.key))\n const [owners, before] = await Promise.all([\n ownerDirectory(\n context.orgId,\n normalized.map((entry) => entry.row),\n ),\n refs.length ? firestore.getAll(...refs) : Promise.resolve([]),\n ])\n const held = new Set(\n before.filter((snapshot) => snapshot.exists).map((snapshot) => snapshot.id),\n )\n const ownersUnresolved = new Set<string>()\n let counted: number | null = null\n let createdHere = 0\n let created = 0\n let merged = 0\n\n for (const { index, row, key } of normalized) {\n const isNew = !held.has(key)\n /*\n * The platform lead ceiling, judged the way the deals import judges\n * the records band: one count at the first create, re-judged locally\n * as the request creates more. The door re-judges it authoritatively\n * inside its own transaction, so a race is still refused there — this\n * is what lets the operator's skipped file say WHY rather than only\n * that the row could not be saved.\n */\n if (isNew) {\n if (counted === null) {\n counted = (await leadsRef.count().get()).data().count\n }\n if (\n checkVisitorRecordCeiling(counted + createdHere, LEADS_MAX_PER_HOST)\n .exceeded\n ) {\n skipped.push({ index, email: row.email, reason: 'lead-ceiling' })\n continue\n }\n }\n let ownerUid: string | undefined\n if (row.ownerEmail) {\n ownerUid = owners.get(row.ownerEmail)\n if (!ownerUid) ownersUnresolved.add(row.ownerEmail)\n }\n const stored = await addHostLead({\n hostRef,\n hostId: context.hostId,\n lead: {\n email: row.email,\n ...(row.name ? { name: row.name } : {}),\n source: LEAD_IMPORT_SOURCE,\n },\n })\n if (!stored) {\n skipped.push({ index, email: row.email, reason: 'write-failed' })\n continue\n }\n const working = workingState(row, ownerUid)\n if (working) {\n await leadsRef\n .doc(key)\n .set(\n { ...working, updatedAt: FieldValue.serverTimestamp() },\n { merge: true },\n )\n }\n if (isNew) {\n created += 1\n createdHere += 1\n } else {\n merged += 1\n }\n }\n\n const result: LeadImportChunkResult = {\n received: read.rows.length,\n created,\n merged,\n skipped: skipped.sort((a, b) => a.index - b.index),\n dropped,\n ownersUnresolved: [...ownersUnresolved],\n }\n return res.status(200).json(result)\n } catch (error) {\n console.error('crm/leads-import failed', error)\n return res.status(500).json({ error: 'The import could not continue.' })\n }\n}\n"],"names":["checkVisitorRecordCeiling","LEADS_MAX_PER_HOST","personKey","LEAD_IMPORT_CHUNK_SIZE","LEAD_IMPORT_MAX_BODY_BYTES","normalizeLeadImportRow","addHostLead","firebaseAdmin","FieldValue","ownerDirectory","readImportRows","resolveImportContext","LEAD_IMPORT_SOURCE","workingState","row","ownerUid","fields","status","unqualifiedReason","notes","Object","keys","length","crmLeadsImportHandler","req","res","method","setHeader","json","error","read","maxBodyBytes","chunkSize","context","ok","body","skipped","dropped","normalized","seen","Set","rows","forEach","raw","index","verdict","push","email","input","reason","key","has","add","entry","field","firestore","app","hostRef","collection","doc","hostId","leadsRef","refs","map","owners","before","Promise","all","orgId","getAll","resolve","held","filter","snapshot","exists","id","ownersUnresolved","counted","createdHere","created","merged","isNew","count","get","data","exceeded","ownerEmail","stored","lead","name","source","working","set","updatedAt","serverTimestamp","merge","result","received","sort","a","b","console"],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
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 { PluginApiHandler } from '@aglyn/aglyn/server';
|
|
18
|
+
/**
|
|
19
|
+
* `POST crm/next-activity` — recompute `nextTaskAtMs` (AGL-2661).
|
|
20
|
+
*
|
|
21
|
+
* Body: `{ hostId | orgId, links: [{ contactId?, companyId?, dealId? }] }`
|
|
22
|
+
* after a client-direct task write, or `{ hostId | orgId, all: true }` for
|
|
23
|
+
* the Fields section's whole-organization recompute. Authorized the way the
|
|
24
|
+
* task routes are — a member who may write this CRM — and never more
|
|
25
|
+
* revealing than a task write: the route stores a figure derived from
|
|
26
|
+
* tasks the caller could already read, and answers with counts.
|
|
27
|
+
*
|
|
28
|
+
* The links are not checked against the caller's scope. A scoped member
|
|
29
|
+
* naming a record they cannot see gets that record's figure recomputed
|
|
30
|
+
* from its own tasks, which is the figure it should carry anyway; nothing
|
|
31
|
+
* about the record comes back.
|
|
32
|
+
*/
|
|
33
|
+
export declare const crmNextActivityHandler: PluginApiHandler;
|
|
@@ -0,0 +1,110 @@
|
|
|
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
|
+
*/ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
17
|
+
import { firebaseAdmin, recomputeCrmNextTaskAt, sweepCrmNextTaskAt } from "@aglyn/tenant-data-admin";
|
|
18
|
+
import { CRM_NEXT_ACTIVITY_LINKS_MAX } from "../model/next-activity.js";
|
|
19
|
+
import { readCrmRouteScope } from "./org-caller.js";
|
|
20
|
+
import { authorizeCrmWriter } from "./task-routes.js";
|
|
21
|
+
/**
|
|
22
|
+
* `POST crm/next-activity` — recompute `nextTaskAtMs` (AGL-2661).
|
|
23
|
+
*
|
|
24
|
+
* Body: `{ hostId | orgId, links: [{ contactId?, companyId?, dealId? }] }`
|
|
25
|
+
* after a client-direct task write, or `{ hostId | orgId, all: true }` for
|
|
26
|
+
* the Fields section's whole-organization recompute. Authorized the way the
|
|
27
|
+
* task routes are — a member who may write this CRM — and never more
|
|
28
|
+
* revealing than a task write: the route stores a figure derived from
|
|
29
|
+
* tasks the caller could already read, and answers with counts.
|
|
30
|
+
*
|
|
31
|
+
* The links are not checked against the caller's scope. A scoped member
|
|
32
|
+
* naming a record they cannot see gets that record's figure recomputed
|
|
33
|
+
* from its own tasks, which is the figure it should carry anyway; nothing
|
|
34
|
+
* about the record comes back.
|
|
35
|
+
*/ export const crmNextActivityHandler = async (req, res)=>{
|
|
36
|
+
var _req_body;
|
|
37
|
+
if (req.method !== 'POST') {
|
|
38
|
+
res.status(405).json({
|
|
39
|
+
error: 'Method not allowed'
|
|
40
|
+
});
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const body = (_req_body = req.body) != null ? _req_body : {};
|
|
44
|
+
const scope = readCrmRouteScope(body);
|
|
45
|
+
if (!scope) {
|
|
46
|
+
res.status(400).json({
|
|
47
|
+
error: 'Missing hostId'
|
|
48
|
+
});
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const writer = await authorizeCrmWriter(req, scope);
|
|
53
|
+
if (writer.ok === false) {
|
|
54
|
+
res.status(writer.status).json(writer.body);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const firestore = firebaseAdmin.app().firestore();
|
|
58
|
+
if (body['all'] === true) {
|
|
59
|
+
const sweep = await sweepCrmNextTaskAt(firestore, writer.orgId);
|
|
60
|
+
const answer = {
|
|
61
|
+
ok: true,
|
|
62
|
+
records: sweep.scheduled + sweep.cleared,
|
|
63
|
+
missing: 0,
|
|
64
|
+
tasks: sweep.tasks,
|
|
65
|
+
cleared: sweep.cleared,
|
|
66
|
+
truncated: sweep.truncated
|
|
67
|
+
};
|
|
68
|
+
res.status(200).json(answer);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const links = readLinks(body['links']);
|
|
72
|
+
if (!links) {
|
|
73
|
+
res.status(400).json({
|
|
74
|
+
error: 'Name the records to recompute, or ask for all of them.'
|
|
75
|
+
});
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const result = await recomputeCrmNextTaskAt(firestore, writer.orgId, links);
|
|
79
|
+
const answer = _extends({
|
|
80
|
+
ok: true
|
|
81
|
+
}, result);
|
|
82
|
+
res.status(200).json(answer);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
console.error('[crm] next-activity failed', error);
|
|
85
|
+
res.status(500).json({
|
|
86
|
+
error: 'The next activity could not be recomputed.'
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
/** The links off the body, held to ids: a string each, trimmed, capped. */ function readLinks(raw) {
|
|
91
|
+
if (!Array.isArray(raw)) return null;
|
|
92
|
+
const links = [];
|
|
93
|
+
for (const entry of raw.slice(0, CRM_NEXT_ACTIVITY_LINKS_MAX)){
|
|
94
|
+
const record = entry != null ? entry : {};
|
|
95
|
+
const link = {};
|
|
96
|
+
for (const field of [
|
|
97
|
+
'contactId',
|
|
98
|
+
'companyId',
|
|
99
|
+
'dealId'
|
|
100
|
+
]){
|
|
101
|
+
var _record_field;
|
|
102
|
+
const id = String((_record_field = record[field]) != null ? _record_field : '').trim().slice(0, 128);
|
|
103
|
+
if (id) link[field] = id;
|
|
104
|
+
}
|
|
105
|
+
if (Object.keys(link).length) links.push(link);
|
|
106
|
+
}
|
|
107
|
+
return links;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
//# sourceMappingURL=next-activity-routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/server/next-activity-routes.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 { CrmNextActivityLink, PluginApiHandler } from '@aglyn/aglyn/server'\nimport {\n firebaseAdmin,\n recomputeCrmNextTaskAt,\n sweepCrmNextTaskAt,\n} from '@aglyn/tenant-data-admin'\nimport {\n CRM_NEXT_ACTIVITY_LINKS_MAX,\n type CrmNextActivityResponse,\n} from '../model/next-activity'\nimport { readCrmRouteScope } from './org-caller'\nimport { authorizeCrmWriter } from './task-routes'\n\n/**\n * `POST crm/next-activity` — recompute `nextTaskAtMs` (AGL-2661).\n *\n * Body: `{ hostId | orgId, links: [{ contactId?, companyId?, dealId? }] }`\n * after a client-direct task write, or `{ hostId | orgId, all: true }` for\n * the Fields section's whole-organization recompute. Authorized the way the\n * task routes are — a member who may write this CRM — and never more\n * revealing than a task write: the route stores a figure derived from\n * tasks the caller could already read, and answers with counts.\n *\n * The links are not checked against the caller's scope. A scoped member\n * naming a record they cannot see gets that record's figure recomputed\n * from its own tasks, which is the figure it should carry anyway; nothing\n * about the record comes back.\n */\nexport const crmNextActivityHandler: PluginApiHandler = async (req, res) => {\n if (req.method !== 'POST') {\n res.status(405).json({ error: 'Method not allowed' })\n return\n }\n const body = (req.body ?? {}) as Record<string, unknown>\n const scope = readCrmRouteScope(body)\n if (!scope) {\n res.status(400).json({ error: 'Missing hostId' })\n return\n }\n try {\n const writer = await authorizeCrmWriter(req, scope)\n if (writer.ok === false) {\n res.status(writer.status).json(writer.body)\n return\n }\n const firestore = firebaseAdmin.app().firestore()\n if (body['all'] === true) {\n const sweep = await sweepCrmNextTaskAt(firestore, writer.orgId)\n const answer: CrmNextActivityResponse = {\n ok: true,\n records: sweep.scheduled + sweep.cleared,\n missing: 0,\n tasks: sweep.tasks,\n cleared: sweep.cleared,\n truncated: sweep.truncated,\n }\n res.status(200).json(answer)\n return\n }\n const links = readLinks(body['links'])\n if (!links) {\n res.status(400).json({ error: 'Name the records to recompute, or ask for all of them.' })\n return\n }\n const result = await recomputeCrmNextTaskAt(firestore, writer.orgId, links)\n const answer: CrmNextActivityResponse = { ok: true, ...result }\n res.status(200).json(answer)\n } catch (error) {\n console.error('[crm] next-activity failed', error)\n res.status(500).json({ error: 'The next activity could not be recomputed.' })\n }\n}\n\n/** The links off the body, held to ids: a string each, trimmed, capped. */\nfunction readLinks(raw: unknown): CrmNextActivityLink[] | null {\n if (!Array.isArray(raw)) return null\n const links: CrmNextActivityLink[] = []\n for (const entry of raw.slice(0, CRM_NEXT_ACTIVITY_LINKS_MAX)) {\n const record = (entry ?? {}) as Record<string, unknown>\n const link: CrmNextActivityLink = {}\n for (const field of ['contactId', 'companyId', 'dealId'] as const) {\n const id = String(record[field] ?? '').trim().slice(0, 128)\n if (id) link[field] = id\n }\n if (Object.keys(link).length) links.push(link)\n }\n return links\n}\n"],"names":["firebaseAdmin","recomputeCrmNextTaskAt","sweepCrmNextTaskAt","CRM_NEXT_ACTIVITY_LINKS_MAX","readCrmRouteScope","authorizeCrmWriter","crmNextActivityHandler","req","res","method","status","json","error","body","scope","writer","ok","firestore","app","sweep","orgId","answer","records","scheduled","cleared","missing","tasks","truncated","links","readLinks","result","console","raw","Array","isArray","entry","slice","record","link","field","id","String","trim","Object","keys","length","push"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;AAGD,SACEA,aAAa,EACbC,sBAAsB,EACtBC,kBAAkB,QACb,2BAA0B;AACjC,SACEC,2BAA2B,QAEtB,4BAAwB;AAC/B,SAASC,iBAAiB,QAAQ,kBAAc;AAChD,SAASC,kBAAkB,QAAQ,mBAAe;AAElD;;;;;;;;;;;;;;CAcC,GACD,OAAO,MAAMC,yBAA2C,OAAOC,KAAKC;QAKpDD;IAJd,IAAIA,IAAIE,MAAM,KAAK,QAAQ;QACzBD,IAAIE,MAAM,CAAC,KAAKC,IAAI,CAAC;YAAEC,OAAO;QAAqB;QACnD;IACF;IACA,MAAMC,QAAQN,YAAAA,IAAIM,IAAI,YAARN,YAAY,CAAC;IAC3B,MAAMO,QAAQV,kBAAkBS;IAChC,IAAI,CAACC,OAAO;QACVN,IAAIE,MAAM,CAAC,KAAKC,IAAI,CAAC;YAAEC,OAAO;QAAiB;QAC/C;IACF;IACA,IAAI;QACF,MAAMG,SAAS,MAAMV,mBAAmBE,KAAKO;QAC7C,IAAIC,OAAOC,EAAE,KAAK,OAAO;YACvBR,IAAIE,MAAM,CAACK,OAAOL,MAAM,EAAEC,IAAI,CAACI,OAAOF,IAAI;YAC1C;QACF;QACA,MAAMI,YAAYjB,cAAckB,GAAG,GAAGD,SAAS;QAC/C,IAAIJ,IAAI,CAAC,MAAM,KAAK,MAAM;YACxB,MAAMM,QAAQ,MAAMjB,mBAAmBe,WAAWF,OAAOK,KAAK;YAC9D,MAAMC,SAAkC;gBACtCL,IAAI;gBACJM,SAASH,MAAMI,SAAS,GAAGJ,MAAMK,OAAO;gBACxCC,SAAS;gBACTC,OAAOP,MAAMO,KAAK;gBAClBF,SAASL,MAAMK,OAAO;gBACtBG,WAAWR,MAAMQ,SAAS;YAC5B;YACAnB,IAAIE,MAAM,CAAC,KAAKC,IAAI,CAACU;YACrB;QACF;QACA,MAAMO,QAAQC,UAAUhB,IAAI,CAAC,QAAQ;QACrC,IAAI,CAACe,OAAO;YACVpB,IAAIE,MAAM,CAAC,KAAKC,IAAI,CAAC;gBAAEC,OAAO;YAAyD;YACvF;QACF;QACA,MAAMkB,SAAS,MAAM7B,uBAAuBgB,WAAWF,OAAOK,KAAK,EAAEQ;QACrE,MAAMP,SAAkC;YAAEL,IAAI;WAASc;QACvDtB,IAAIE,MAAM,CAAC,KAAKC,IAAI,CAACU;IACvB,EAAE,OAAOT,OAAO;QACdmB,QAAQnB,KAAK,CAAC,8BAA8BA;QAC5CJ,IAAIE,MAAM,CAAC,KAAKC,IAAI,CAAC;YAAEC,OAAO;QAA6C;IAC7E;AACF,EAAC;AAED,yEAAyE,GACzE,SAASiB,UAAUG,GAAY;IAC7B,IAAI,CAACC,MAAMC,OAAO,CAACF,MAAM,OAAO;IAChC,MAAMJ,QAA+B,EAAE;IACvC,KAAK,MAAMO,SAASH,IAAII,KAAK,CAAC,GAAGjC,6BAA8B;QAC7D,MAAMkC,SAAUF,gBAAAA,QAAS,CAAC;QAC1B,MAAMG,OAA4B,CAAC;QACnC,KAAK,MAAMC,SAAS;YAAC;YAAa;YAAa;SAAS,CAAW;gBAC/CF;YAAlB,MAAMG,KAAKC,QAAOJ,gBAAAA,MAAM,CAACE,MAAM,YAAbF,gBAAiB,IAAIK,IAAI,GAAGN,KAAK,CAAC,GAAG;YACvD,IAAII,IAAIF,IAAI,CAACC,MAAM,GAAGC;QACxB;QACA,IAAIG,OAAOC,IAAI,CAACN,MAAMO,MAAM,EAAEjB,MAAMkB,IAAI,CAACR;IAC3C;IACA,OAAOV;AACT"}
|
|
@@ -0,0 +1,61 @@
|
|
|
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
|
+
/**
|
|
18
|
+
* `POST /api/crm/org-activity` — one line in the organization's activity
|
|
19
|
+
* feed (AGL-2634).
|
|
20
|
+
*
|
|
21
|
+
* Body: `{ orgId, action, target: { type, id?, name? } }`. Answers
|
|
22
|
+
* `{ ok: true }` once the line is appended.
|
|
23
|
+
*
|
|
24
|
+
* ## Why a route, when the site feed takes the browser's word for it
|
|
25
|
+
*
|
|
26
|
+
* A site's feed at `hosts/{hostId}/activity` is written client-direct: the
|
|
27
|
+
* bulk bars log "Deleted deal" per row through `useHostActivityLogger`,
|
|
28
|
+
* under a rule that lets a member holding a content role append. The
|
|
29
|
+
* organization's feed at `orgs/{orgId}/activity` is not — `allow write: if
|
|
30
|
+
* false` for every client, because the feed names who did what across every
|
|
31
|
+
* site and its only reader is `/api/orgs/activity`, gated on `org.auditLog`
|
|
32
|
+
* with the Admin SDK. So an act performed at the ORGANIZATION level with a
|
|
33
|
+
* client-direct write — a bulk bar over the org's deals, which writes the
|
|
34
|
+
* documents from the browser — has no feed it can reach, and the org hub
|
|
35
|
+
* wrote no line at all for its bulk actions. This is the door: the same
|
|
36
|
+
* trust the site feed extends to the browser (a verified member, holding
|
|
37
|
+
* the permission the CRM is gated on), narrowed to the org hub's reach
|
|
38
|
+
* (org-wide, never a site collaborator) and to the CRM's own record kinds.
|
|
39
|
+
*
|
|
40
|
+
* What it does NOT vouch for is that the act happened: like every
|
|
41
|
+
* client-written feed line, the sentence is the caller's. The routes that
|
|
42
|
+
* perform an org-level act themselves — the stage move, the merge, the
|
|
43
|
+
* erasure, the email — write their own line with the Admin SDK and never
|
|
44
|
+
* call here.
|
|
45
|
+
*/
|
|
46
|
+
import type { PluginApiHandler } from '@aglyn/aglyn/server';
|
|
47
|
+
import { type CrmOrgActivityKind } from '../constants/api-routes';
|
|
48
|
+
export declare const CRM_ORG_ACTIVITY_ROUTE: "crm/org-activity";
|
|
49
|
+
/** The body the org-level bulk bars post. */
|
|
50
|
+
export interface OrgActivityRequest {
|
|
51
|
+
orgId: string;
|
|
52
|
+
/** The sentence — "Owner set on 3 deals" — as the bar's snackbar said it. */
|
|
53
|
+
action: string;
|
|
54
|
+
target: {
|
|
55
|
+
type: CrmOrgActivityKind;
|
|
56
|
+
id?: string;
|
|
57
|
+
name?: string;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export declare const crmOrgActivityHandler: PluginApiHandler;
|
|
61
|
+
export default crmOrgActivityHandler;
|