@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,466 @@
|
|
|
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
|
+
* BRINGING A SPREADSHEET OF PEOPLE INTO THE CRM — the pure half (AGL-2602).
|
|
18
|
+
*
|
|
19
|
+
* A merchant arriving from another CRM has a CSV: a header row naming
|
|
20
|
+
* columns in that product's vocabulary, and one person per line. Nothing
|
|
21
|
+
* about that file is in our shape. This module is every decision about the
|
|
22
|
+
* file that needs no Firestore and no React — which columns mean what, how a
|
|
23
|
+
* cell becomes a stored value, and what to say about a row that cannot be
|
|
24
|
+
* stored — so that the console drawer and the server route read the same
|
|
25
|
+
* rules rather than two copies of them.
|
|
26
|
+
*
|
|
27
|
+
* ## Three stages, and only the last one touches a record
|
|
28
|
+
*
|
|
29
|
+
* 1. {@link guessContactImportMapping} reads the header and proposes a
|
|
30
|
+
* column → field mapping from a table of aliases. It is a PROPOSAL: the
|
|
31
|
+
* drawer shows it and the operator corrects it, because an export's
|
|
32
|
+
* "Company" column may hold a job title and no alias table can know.
|
|
33
|
+
* 2. {@link mapContactImportRow} applies the mapping to one line, producing
|
|
34
|
+
* the raw row the browser posts. Strings only — the browser trims nothing
|
|
35
|
+
* and validates nothing, so the server is the one place the file is
|
|
36
|
+
* judged and a stale drawer cannot judge it differently.
|
|
37
|
+
* 3. {@link normalizeContactImportRow} turns a raw row into the values that
|
|
38
|
+
* will be written, through the SAME normalizers every other door uses:
|
|
39
|
+
* `normalizeContactEmail` for the dedupe key, `normalizePhone` for E.164,
|
|
40
|
+
* `normalizeAddress` for the postal shape, `isContactLifecycleStage` for
|
|
41
|
+
* the stage. An importer with its own idea of a valid phone number is how
|
|
42
|
+
* one collection ends up storing a value three ways.
|
|
43
|
+
*
|
|
44
|
+
* ## A bad cell is dropped and named; a bad row is skipped and named
|
|
45
|
+
*
|
|
46
|
+
* The only thing that makes a row unstorable is an unusable address, because
|
|
47
|
+
* the address is the identity the shared row is keyed by. Everything else is
|
|
48
|
+
* a value on the row: a phone that cannot be read as a number is left off
|
|
49
|
+
* the contact and REPORTED under {@link ContactImportRow.dropped}, so the
|
|
50
|
+
* operator learns that forty rows had a phone in a format we could not read
|
|
51
|
+
* rather than discovering, weeks later, that the phone column silently went
|
|
52
|
+
* nowhere. `list-import.ts` applies the same rule to a line that is not an
|
|
53
|
+
* address: reported, never quietly discarded.
|
|
54
|
+
*
|
|
55
|
+
* ## Ceilings are on the work, never on the audience
|
|
56
|
+
*
|
|
57
|
+
* {@link CONTACT_IMPORT_MAX_ROWS} bounds one FILE, and the drawer says so
|
|
58
|
+
* before anything is sent. {@link CONTACT_IMPORT_CHUNK_SIZE} bounds one
|
|
59
|
+
* REQUEST, so a five-thousand-row file is twenty-five requests with a
|
|
60
|
+
* progress bar between them rather than one that times out halfway with no
|
|
61
|
+
* record of what it did. Neither one ever trims a person to fit: a row past
|
|
62
|
+
* the file ceiling is refused with the file, and a row is never dropped to
|
|
63
|
+
* make a chunk smaller. The audience band is enforced where every capture
|
|
64
|
+
* enforces it — inside `upsertHostContact` — and an import reports that
|
|
65
|
+
* refusal per row rather than pre-empting it with a count of its own.
|
|
66
|
+
*/ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
67
|
+
import { normalizeAddress, normalizePhone } from "@aglyn/aglyn/foundation/definitions/contact.types";
|
|
68
|
+
import { normalizeContactEmail } from "@aglyn/aglyn/app-utils/contacts";
|
|
69
|
+
import { CSV_IMPORT_CHUNK_SIZE, CSV_IMPORT_MAX_BODY_BYTES, CSV_IMPORT_MAX_ROWS, CSV_IMPORT_PREVIEW_ROWS, customImportTarget, emptyImportResult, guessImportMapping, importAliasKeys, importSkippedCsv, importTextValue, mapImportRow, mergeImportResults, parseImportFlag, parseImportTags } from "@aglyn/aglyn/app-utils/csv-import";
|
|
70
|
+
import { CONTACT_LIFECYCLE_STAGE_LABELS, CONTACT_LIFECYCLE_STAGES, isContactLifecycleStage } from "@aglyn/aglyn/app-utils/crm";
|
|
71
|
+
/*
|
|
72
|
+
* The custom-target grammar is the shared module's; it is re-exported here
|
|
73
|
+
* because every reader of a contact mapping has always taken it from this
|
|
74
|
+
* file, and the companies import reads the same two functions from theirs.
|
|
75
|
+
*/ export { customImportTarget, customImportTargetKey } from "@aglyn/aglyn/app-utils/csv-import";
|
|
76
|
+
/**
|
|
77
|
+
* The ceilings are the shared ones (`csv-import.ts`), under the names this
|
|
78
|
+
* file has always exported: one file, one request and one preview are
|
|
79
|
+
* bounded the same way for every collection, because the bounds are about
|
|
80
|
+
* the browser's memory and a request's budget rather than about people.
|
|
81
|
+
*/ export const CONTACT_IMPORT_MAX_ROWS = CSV_IMPORT_MAX_ROWS;
|
|
82
|
+
export const CONTACT_IMPORT_CHUNK_SIZE = CSV_IMPORT_CHUNK_SIZE;
|
|
83
|
+
export const CONTACT_IMPORT_MAX_BODY_BYTES = CSV_IMPORT_MAX_BODY_BYTES;
|
|
84
|
+
export const CONTACT_IMPORT_PREVIEW_ROWS = CSV_IMPORT_PREVIEW_ROWS;
|
|
85
|
+
/**
|
|
86
|
+
* The most tags one row may carry — the profile drawer's own cap, so an
|
|
87
|
+
* imported person cannot hold more tags than a hand-edited one.
|
|
88
|
+
*/ export const CONTACT_IMPORT_TAGS_MAX = 20;
|
|
89
|
+
/** The longest text a name, title, company name or custom text value keeps. */ const NAME_MAX = 120;
|
|
90
|
+
const CUSTOM_TEXT_MAX = 1000;
|
|
91
|
+
/**
|
|
92
|
+
* The standard fields a column may be mapped to, in the order the mapping
|
|
93
|
+
* menu lists them. A custom field is mapped by its key — see
|
|
94
|
+
* {@link ContactImportTargetId}.
|
|
95
|
+
*/ export const CONTACT_IMPORT_FIELDS = [
|
|
96
|
+
'email',
|
|
97
|
+
'name',
|
|
98
|
+
'phone',
|
|
99
|
+
'jobTitle',
|
|
100
|
+
'companyName',
|
|
101
|
+
'addressLine1',
|
|
102
|
+
'addressLine2',
|
|
103
|
+
'addressCity',
|
|
104
|
+
'addressState',
|
|
105
|
+
'addressPostalCode',
|
|
106
|
+
'addressCountry',
|
|
107
|
+
'tags',
|
|
108
|
+
'ownerEmail',
|
|
109
|
+
'lifecycleStage',
|
|
110
|
+
'marketingConsent'
|
|
111
|
+
];
|
|
112
|
+
/** How each field reads in the mapping menu. Typed so a field cannot ship unlabeled. */ export const CONTACT_IMPORT_FIELD_LABELS = {
|
|
113
|
+
email: 'Email (required)',
|
|
114
|
+
name: 'Name',
|
|
115
|
+
phone: 'Phone',
|
|
116
|
+
jobTitle: 'Job title',
|
|
117
|
+
companyName: 'Company name',
|
|
118
|
+
addressLine1: 'Address line 1',
|
|
119
|
+
addressLine2: 'Address line 2',
|
|
120
|
+
addressCity: 'City',
|
|
121
|
+
addressState: 'State or region',
|
|
122
|
+
addressPostalCode: 'Postal code',
|
|
123
|
+
addressCountry: 'Country (two-letter code)',
|
|
124
|
+
tags: 'Tags (comma or | separated)',
|
|
125
|
+
ownerEmail: 'Owner (team member email)',
|
|
126
|
+
lifecycleStage: 'Lifecycle stage',
|
|
127
|
+
marketingConsent: 'Marketing consent (yes/no)'
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Header aliases per field, matched after `importHeaderKey` normalization.
|
|
131
|
+
*
|
|
132
|
+
* The vocabulary of the exports people actually arrive with — HubSpot's
|
|
133
|
+
* "First Name"/"Last Name" pair is deliberately NOT here, because two columns
|
|
134
|
+
* cannot map to one field and a guess that took only the first name would
|
|
135
|
+
* store half a name and look right in the preview. An operator with a split
|
|
136
|
+
* name maps one of the two by hand, and the preview shows what they chose.
|
|
137
|
+
*/ const FIELD_ALIASES = {
|
|
138
|
+
email: [
|
|
139
|
+
'email',
|
|
140
|
+
'email address',
|
|
141
|
+
'e-mail',
|
|
142
|
+
'e-mail address',
|
|
143
|
+
'contact email',
|
|
144
|
+
'primary email',
|
|
145
|
+
'work email'
|
|
146
|
+
],
|
|
147
|
+
name: [
|
|
148
|
+
'name',
|
|
149
|
+
'full name',
|
|
150
|
+
'contact name',
|
|
151
|
+
'display name',
|
|
152
|
+
'person'
|
|
153
|
+
],
|
|
154
|
+
phone: [
|
|
155
|
+
'phone',
|
|
156
|
+
'phone number',
|
|
157
|
+
'telephone',
|
|
158
|
+
'mobile',
|
|
159
|
+
'mobile phone',
|
|
160
|
+
'cell',
|
|
161
|
+
'work phone'
|
|
162
|
+
],
|
|
163
|
+
jobTitle: [
|
|
164
|
+
'job title',
|
|
165
|
+
'title',
|
|
166
|
+
'position',
|
|
167
|
+
'role'
|
|
168
|
+
],
|
|
169
|
+
companyName: [
|
|
170
|
+
'company',
|
|
171
|
+
'company name',
|
|
172
|
+
'organization',
|
|
173
|
+
'organisation',
|
|
174
|
+
'account'
|
|
175
|
+
],
|
|
176
|
+
addressLine1: [
|
|
177
|
+
'address',
|
|
178
|
+
'address line 1',
|
|
179
|
+
'street',
|
|
180
|
+
'street address',
|
|
181
|
+
'address 1'
|
|
182
|
+
],
|
|
183
|
+
addressLine2: [
|
|
184
|
+
'address line 2',
|
|
185
|
+
'address 2',
|
|
186
|
+
'street 2',
|
|
187
|
+
'apartment',
|
|
188
|
+
'suite'
|
|
189
|
+
],
|
|
190
|
+
addressCity: [
|
|
191
|
+
'city',
|
|
192
|
+
'town',
|
|
193
|
+
'locality'
|
|
194
|
+
],
|
|
195
|
+
addressState: [
|
|
196
|
+
'state',
|
|
197
|
+
'region',
|
|
198
|
+
'province',
|
|
199
|
+
'county',
|
|
200
|
+
'state/region'
|
|
201
|
+
],
|
|
202
|
+
addressPostalCode: [
|
|
203
|
+
'postal code',
|
|
204
|
+
'postcode',
|
|
205
|
+
'zip',
|
|
206
|
+
'zip code',
|
|
207
|
+
'post code'
|
|
208
|
+
],
|
|
209
|
+
addressCountry: [
|
|
210
|
+
'country',
|
|
211
|
+
'country code',
|
|
212
|
+
'country/region'
|
|
213
|
+
],
|
|
214
|
+
tags: [
|
|
215
|
+
'tags',
|
|
216
|
+
'tag',
|
|
217
|
+
'labels',
|
|
218
|
+
'groups',
|
|
219
|
+
'segments'
|
|
220
|
+
],
|
|
221
|
+
ownerEmail: [
|
|
222
|
+
'owner',
|
|
223
|
+
'owner email',
|
|
224
|
+
'contact owner',
|
|
225
|
+
'assigned to',
|
|
226
|
+
'account owner'
|
|
227
|
+
],
|
|
228
|
+
lifecycleStage: [
|
|
229
|
+
'lifecycle stage',
|
|
230
|
+
'stage',
|
|
231
|
+
'lifecycle',
|
|
232
|
+
'status',
|
|
233
|
+
'lead status'
|
|
234
|
+
],
|
|
235
|
+
marketingConsent: [
|
|
236
|
+
'marketing consent',
|
|
237
|
+
'consent',
|
|
238
|
+
'opt in',
|
|
239
|
+
'opt-in',
|
|
240
|
+
'opted in',
|
|
241
|
+
'subscribed',
|
|
242
|
+
'marketing opt in',
|
|
243
|
+
'email consent'
|
|
244
|
+
]
|
|
245
|
+
};
|
|
246
|
+
/** {@link FIELD_ALIASES}, normalized once for the matcher. */ const FIELD_ALIAS_KEYS = importAliasKeys(CONTACT_IMPORT_FIELDS, FIELD_ALIASES);
|
|
247
|
+
/**
|
|
248
|
+
* A proposed mapping from a file's header row — the shared matcher over the
|
|
249
|
+
* contact vocabulary. Custom fields win over standard aliases when a header
|
|
250
|
+
* matches a definition's label or key exactly: a merchant who defined a
|
|
251
|
+
* field called "Status" meant THEIR status, not our lifecycle stage.
|
|
252
|
+
*/ export function guessContactImportMapping(columns, fields = []) {
|
|
253
|
+
return guessImportMapping(columns, CONTACT_IMPORT_FIELDS, FIELD_ALIAS_KEYS, fields);
|
|
254
|
+
}
|
|
255
|
+
/** One parsed line under the mapping. Empty cells are left absent. */ export function mapContactImportRow(cells, mapping) {
|
|
256
|
+
return mapImportRow(cells, mapping);
|
|
257
|
+
}
|
|
258
|
+
/** How a skip reason reads on screen and in the downloaded file. */ export const CONTACT_IMPORT_SKIP_LABELS = {
|
|
259
|
+
'invalid-email': 'Not a valid email address',
|
|
260
|
+
duplicate: 'Appears earlier in the file',
|
|
261
|
+
'audience-band': 'Contact limit reached',
|
|
262
|
+
erased: 'Erased from this workspace at their request',
|
|
263
|
+
'write-failed': 'Could not be saved'
|
|
264
|
+
};
|
|
265
|
+
/**
|
|
266
|
+
* A tag cell as the tags it names.
|
|
267
|
+
*
|
|
268
|
+
* `|` or `,` separated — the CSV export writes `|`, most other products
|
|
269
|
+
* write `,` — lowercased and trimmed like the profile drawer's own tag
|
|
270
|
+
* field, so an imported `VIP` and a typed `vip` are one tag and not two.
|
|
271
|
+
*/ export function parseContactImportTags(value) {
|
|
272
|
+
return parseImportTags(value, CONTACT_IMPORT_TAGS_MAX);
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* A yes/no cell as a boolean, or `null` when it is neither.
|
|
276
|
+
*
|
|
277
|
+
* The affirmatives are the ones consent and checkbox columns actually
|
|
278
|
+
* carry; the negatives are listed so that an explicit `no` is a `false`
|
|
279
|
+
* rather than an unreadable value that gets reported as dropped.
|
|
280
|
+
*/ export function parseContactImportFlag(value) {
|
|
281
|
+
return parseImportFlag(value);
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* A stage cell as a stage, accepting the stored id (`sales-qualified`), its
|
|
285
|
+
* label ("Sales qualified") and the spellings between them
|
|
286
|
+
* (`Sales_Qualified`), or `null` when it names none of them.
|
|
287
|
+
*/ export function parseContactImportLifecycleStage(value) {
|
|
288
|
+
const text = String(value != null ? value : '').trim().toLowerCase();
|
|
289
|
+
if (!text) return null;
|
|
290
|
+
const key = text.replace(/[\s_]+/g, '-');
|
|
291
|
+
if (isContactLifecycleStage(key)) return key;
|
|
292
|
+
for (const stage of CONTACT_LIFECYCLE_STAGES){
|
|
293
|
+
if (CONTACT_LIFECYCLE_STAGE_LABELS[stage].toLowerCase() === text) return stage;
|
|
294
|
+
}
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* A cell as the value a custom field of this type stores.
|
|
299
|
+
*
|
|
300
|
+
* `{ value }` when it read, `{ value: undefined }` for a blank cell (nothing
|
|
301
|
+
* to write and nothing to report), and `null` when the cell held something
|
|
302
|
+
* the type cannot hold — a word in a number field, a choice a select does
|
|
303
|
+
* not offer. A date is stored as epoch milliseconds, the shape every other
|
|
304
|
+
* timestamp on a CRM record takes (`dueAtMs`, `expectedCloseAtMs`).
|
|
305
|
+
*/ export function parseContactImportCustomValue(definition, value) {
|
|
306
|
+
const text = String(value != null ? value : '').trim();
|
|
307
|
+
if (!text) return {
|
|
308
|
+
value: undefined
|
|
309
|
+
};
|
|
310
|
+
switch(definition.type){
|
|
311
|
+
case 'text':
|
|
312
|
+
case 'url':
|
|
313
|
+
return {
|
|
314
|
+
value: text.slice(0, CUSTOM_TEXT_MAX)
|
|
315
|
+
};
|
|
316
|
+
case 'number':
|
|
317
|
+
{
|
|
318
|
+
const number = Number(text.replace(/[,\s$€£]/g, ''));
|
|
319
|
+
return Number.isFinite(number) ? {
|
|
320
|
+
value: number
|
|
321
|
+
} : null;
|
|
322
|
+
}
|
|
323
|
+
case 'date':
|
|
324
|
+
{
|
|
325
|
+
const ms = Date.parse(text);
|
|
326
|
+
return Number.isFinite(ms) ? {
|
|
327
|
+
value: ms
|
|
328
|
+
} : null;
|
|
329
|
+
}
|
|
330
|
+
case 'select':
|
|
331
|
+
{
|
|
332
|
+
var _definition_options;
|
|
333
|
+
const wanted = text.toLowerCase();
|
|
334
|
+
const option = ((_definition_options = definition.options) != null ? _definition_options : []).find((candidate)=>String(candidate).trim().toLowerCase() === wanted);
|
|
335
|
+
return option === undefined ? null : {
|
|
336
|
+
value: String(option)
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
case 'checkbox':
|
|
340
|
+
{
|
|
341
|
+
const flag = parseContactImportFlag(text);
|
|
342
|
+
return flag === null ? null : {
|
|
343
|
+
value: flag
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
default:
|
|
347
|
+
return null;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* One raw row as the values that will be written, or the reason it cannot
|
|
352
|
+
* be.
|
|
353
|
+
*
|
|
354
|
+
* `fields` is the holder's live definitions: a custom value under a key
|
|
355
|
+
* with no definition is dropped and reported, because a value nobody
|
|
356
|
+
* defined a field for is a value no form will ever show.
|
|
357
|
+
*/ export function normalizeContactImportRow(raw, fields = []) {
|
|
358
|
+
const email = normalizeContactEmail(raw.email);
|
|
359
|
+
if (!email) {
|
|
360
|
+
var _raw_email;
|
|
361
|
+
return {
|
|
362
|
+
ok: false,
|
|
363
|
+
reason: 'invalid-email',
|
|
364
|
+
input: String((_raw_email = raw.email) != null ? _raw_email : '').trim()
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
const dropped = [];
|
|
368
|
+
const drop = (field, value)=>{
|
|
369
|
+
dropped.push({
|
|
370
|
+
field,
|
|
371
|
+
value: String(value != null ? value : '').trim()
|
|
372
|
+
});
|
|
373
|
+
};
|
|
374
|
+
const row = {
|
|
375
|
+
email,
|
|
376
|
+
tags: parseContactImportTags(raw.tags),
|
|
377
|
+
marketingConsent: false,
|
|
378
|
+
custom: {},
|
|
379
|
+
dropped
|
|
380
|
+
};
|
|
381
|
+
const name = importTextValue(raw.name, NAME_MAX);
|
|
382
|
+
if (name) row.name = name;
|
|
383
|
+
const jobTitle = importTextValue(raw.jobTitle, NAME_MAX);
|
|
384
|
+
if (jobTitle) row.jobTitle = jobTitle;
|
|
385
|
+
const companyName = importTextValue(raw.companyName, NAME_MAX);
|
|
386
|
+
if (companyName) row.companyName = companyName;
|
|
387
|
+
const phoneText = importTextValue(raw.phone, 64);
|
|
388
|
+
if (phoneText) {
|
|
389
|
+
const phone = normalizePhone(phoneText);
|
|
390
|
+
if (phone) row.phone = phone;
|
|
391
|
+
else drop('phone', phoneText);
|
|
392
|
+
}
|
|
393
|
+
const address = normalizeAddress({
|
|
394
|
+
line1: importTextValue(raw.addressLine1, 200),
|
|
395
|
+
line2: importTextValue(raw.addressLine2, 200),
|
|
396
|
+
city: importTextValue(raw.addressCity, 120),
|
|
397
|
+
state: importTextValue(raw.addressState, 120),
|
|
398
|
+
postalCode: importTextValue(raw.addressPostalCode, 32),
|
|
399
|
+
country: importTextValue(raw.addressCountry, 8)
|
|
400
|
+
});
|
|
401
|
+
if (address) row.address = address;
|
|
402
|
+
// The country is the one address part the normalizer drops silently — a
|
|
403
|
+
// typed name is not a code — so it is the one part the report has to name.
|
|
404
|
+
const countryText = importTextValue(raw.addressCountry, 64);
|
|
405
|
+
if (countryText && !(address == null ? void 0 : address.country)) drop('addressCountry', countryText);
|
|
406
|
+
const ownerText = importTextValue(raw.ownerEmail, 320);
|
|
407
|
+
if (ownerText) {
|
|
408
|
+
const owner = normalizeContactEmail(ownerText);
|
|
409
|
+
if (owner) row.ownerEmail = owner;
|
|
410
|
+
else drop('ownerEmail', ownerText);
|
|
411
|
+
}
|
|
412
|
+
const stageText = importTextValue(raw.lifecycleStage, 64);
|
|
413
|
+
if (stageText) {
|
|
414
|
+
const stage = parseContactImportLifecycleStage(stageText);
|
|
415
|
+
if (stage) row.lifecycleStage = stage;
|
|
416
|
+
else drop('lifecycleStage', stageText);
|
|
417
|
+
}
|
|
418
|
+
const consentText = raw.marketingConsent;
|
|
419
|
+
if (consentText !== undefined && consentText !== null && String(consentText).trim()) {
|
|
420
|
+
const flag = parseContactImportFlag(consentText);
|
|
421
|
+
if (flag === null) drop('marketingConsent', consentText);
|
|
422
|
+
else row.marketingConsent = flag;
|
|
423
|
+
}
|
|
424
|
+
const custom = raw.custom && typeof raw.custom === 'object' && !Array.isArray(raw.custom) ? raw.custom : {};
|
|
425
|
+
const definitions = new Map(fields.map((field)=>[
|
|
426
|
+
field.key,
|
|
427
|
+
field
|
|
428
|
+
]));
|
|
429
|
+
for (const [key, value] of Object.entries(custom)){
|
|
430
|
+
const definition = definitions.get(key);
|
|
431
|
+
if (!definition) {
|
|
432
|
+
drop(customImportTarget(key), value);
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
const parsed = parseContactImportCustomValue(definition, value);
|
|
436
|
+
if (!parsed) drop(customImportTarget(key), value);
|
|
437
|
+
else if (parsed.value !== undefined) row.custom[key] = parsed.value;
|
|
438
|
+
}
|
|
439
|
+
return {
|
|
440
|
+
ok: true,
|
|
441
|
+
row
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
/** An empty tally, for the drawer to fold chunk results into. */ export function emptyContactImportResult() {
|
|
445
|
+
return _extends({}, emptyImportResult(), {
|
|
446
|
+
companiesCreated: 0
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Two results as one — the shared fold, with the company tally beside it.
|
|
451
|
+
* `offset` is where the chunk started in the file, so a skipped row's index
|
|
452
|
+
* comes back as its position in the FILE rather than in the request.
|
|
453
|
+
*/ export function mergeContactImportResults(total, chunk, offset = 0) {
|
|
454
|
+
var _chunk_companiesCreated;
|
|
455
|
+
return _extends({}, mergeImportResults(total, chunk, offset), {
|
|
456
|
+
companiesCreated: total.companiesCreated + Number((_chunk_companiesCreated = chunk.companiesCreated) != null ? _chunk_companiesCreated : 0)
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* The skipped rows as a file the operator can fix and re-import: the
|
|
461
|
+
* original columns verbatim plus a trailing `Skipped because` column.
|
|
462
|
+
*/ export function contactImportSkippedCsv(columns, entries) {
|
|
463
|
+
return importSkippedCsv(columns, entries, CONTACT_IMPORT_SKIP_LABELS);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
//# sourceMappingURL=crm-import.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/model/crm-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 * BRINGING A SPREADSHEET OF PEOPLE INTO THE CRM — the pure half (AGL-2602).\n *\n * A merchant arriving from another CRM has a CSV: a header row naming\n * columns in that product's vocabulary, and one person per line. Nothing\n * about that file is in our shape. This module is every decision about the\n * file that needs no Firestore and no React — which columns mean what, how a\n * cell becomes a stored value, and what to say about a row that cannot be\n * stored — so that the console drawer and the server route read the same\n * rules rather than two copies of them.\n *\n * ## Three stages, and only the last one touches a record\n *\n * 1. {@link guessContactImportMapping} reads the header and proposes a\n * column → field mapping from a table of aliases. It is a PROPOSAL: the\n * drawer shows it and the operator corrects it, because an export's\n * \"Company\" column may hold a job title and no alias table can know.\n * 2. {@link mapContactImportRow} applies the mapping to one line, producing\n * the raw row the browser posts. Strings only — the browser trims nothing\n * and validates nothing, so the server is the one place the file is\n * judged and a stale drawer cannot judge it differently.\n * 3. {@link normalizeContactImportRow} turns a raw row into the values that\n * will be written, through the SAME normalizers every other door uses:\n * `normalizeContactEmail` for the dedupe key, `normalizePhone` for E.164,\n * `normalizeAddress` for the postal shape, `isContactLifecycleStage` for\n * the stage. An importer with its own idea of a valid phone number is how\n * one collection ends up storing a value three ways.\n *\n * ## A bad cell is dropped and named; a bad row is skipped and named\n *\n * The only thing that makes a row unstorable is an unusable address, because\n * the address is the identity the shared row is keyed by. Everything else is\n * a value on the row: a phone that cannot be read as a number is left off\n * the contact and REPORTED under {@link ContactImportRow.dropped}, so the\n * operator learns that forty rows had a phone in a format we could not read\n * rather than discovering, weeks later, that the phone column silently went\n * nowhere. `list-import.ts` applies the same rule to a line that is not an\n * address: reported, never quietly discarded.\n *\n * ## Ceilings are on the work, never on the audience\n *\n * {@link CONTACT_IMPORT_MAX_ROWS} bounds one FILE, and the drawer says so\n * before anything is sent. {@link CONTACT_IMPORT_CHUNK_SIZE} bounds one\n * REQUEST, so a five-thousand-row file is twenty-five requests with a\n * progress bar between them rather than one that times out halfway with no\n * record of what it did. Neither one ever trims a person to fit: a row past\n * the file ceiling is refused with the file, and a row is never dropped to\n * make a chunk smaller. The audience band is enforced where every capture\n * enforces it — inside `upsertHostContact` — and an import reports that\n * refusal per row rather than pre-empting it with a count of its own.\n */\n\nimport type { AglynPostalAddress } from '@aglyn/aglyn/foundation/definitions/contact.types'\nimport { normalizeAddress, normalizePhone } from '@aglyn/aglyn/foundation/definitions/contact.types'\nimport { normalizeContactEmail } from '@aglyn/aglyn/app-utils/contacts'\nimport {\n CSV_IMPORT_CHUNK_SIZE,\n CSV_IMPORT_MAX_BODY_BYTES,\n CSV_IMPORT_MAX_ROWS,\n CSV_IMPORT_PREVIEW_ROWS,\n customImportTarget,\n customImportTargetKey,\n emptyImportResult,\n guessImportMapping,\n importAliasKeys,\n type ImportChunkResult,\n type ImportDroppedValue,\n importSkippedCsv,\n importTextValue,\n mapImportRow,\n mergeImportResults,\n parseImportFlag,\n parseImportTags,\n} from '@aglyn/aglyn/app-utils/csv-import'\nimport {\n CONTACT_LIFECYCLE_STAGE_LABELS,\n CONTACT_LIFECYCLE_STAGES,\n type ContactCustomValue,\n type ContactFieldDefinition,\n type ContactLifecycleStage,\n isContactLifecycleStage,\n} from '@aglyn/aglyn/app-utils/crm'\n\n/*\n * The custom-target grammar is the shared module's; it is re-exported here\n * because every reader of a contact mapping has always taken it from this\n * file, and the companies import reads the same two functions from theirs.\n */\nexport { customImportTarget, customImportTargetKey } from '@aglyn/aglyn/app-utils/csv-import'\n\n/**\n * The ceilings are the shared ones (`csv-import.ts`), under the names this\n * file has always exported: one file, one request and one preview are\n * bounded the same way for every collection, because the bounds are about\n * the browser's memory and a request's budget rather than about people.\n */\nexport const CONTACT_IMPORT_MAX_ROWS = CSV_IMPORT_MAX_ROWS\n\nexport const CONTACT_IMPORT_CHUNK_SIZE = CSV_IMPORT_CHUNK_SIZE\n\nexport const CONTACT_IMPORT_MAX_BODY_BYTES = CSV_IMPORT_MAX_BODY_BYTES\n\nexport const CONTACT_IMPORT_PREVIEW_ROWS = CSV_IMPORT_PREVIEW_ROWS\n\n/**\n * The most tags one row may carry — the profile drawer's own cap, so an\n * imported person cannot hold more tags than a hand-edited one.\n */\nexport const CONTACT_IMPORT_TAGS_MAX = 20\n\n/** The longest text a name, title, company name or custom text value keeps. */\nconst NAME_MAX = 120\nconst CUSTOM_TEXT_MAX = 1_000\n\n/**\n * The standard fields a column may be mapped to, in the order the mapping\n * menu lists them. A custom field is mapped by its key — see\n * {@link ContactImportTargetId}.\n */\nexport const CONTACT_IMPORT_FIELDS = [\n 'email',\n 'name',\n 'phone',\n 'jobTitle',\n 'companyName',\n 'addressLine1',\n 'addressLine2',\n 'addressCity',\n 'addressState',\n 'addressPostalCode',\n 'addressCountry',\n 'tags',\n 'ownerEmail',\n 'lifecycleStage',\n 'marketingConsent',\n] as const\n\nexport type ContactImportField = (typeof CONTACT_IMPORT_FIELDS)[number]\n\n/** How each field reads in the mapping menu. Typed so a field cannot ship unlabeled. */\nexport const CONTACT_IMPORT_FIELD_LABELS: Record<ContactImportField, string> = {\n email: 'Email (required)',\n name: 'Name',\n phone: 'Phone',\n jobTitle: 'Job title',\n companyName: 'Company name',\n addressLine1: 'Address line 1',\n addressLine2: 'Address line 2',\n addressCity: 'City',\n addressState: 'State or region',\n addressPostalCode: 'Postal code',\n addressCountry: 'Country (two-letter code)',\n tags: 'Tags (comma or | separated)',\n ownerEmail: 'Owner (team member email)',\n lifecycleStage: 'Lifecycle stage',\n marketingConsent: 'Marketing consent (yes/no)',\n}\n\n/**\n * Header aliases per field, matched after `importHeaderKey` normalization.\n *\n * The vocabulary of the exports people actually arrive with — HubSpot's\n * \"First Name\"/\"Last Name\" pair is deliberately NOT here, because two columns\n * cannot map to one field and a guess that took only the first name would\n * store half a name and look right in the preview. An operator with a split\n * name maps one of the two by hand, and the preview shows what they chose.\n */\nconst FIELD_ALIASES: Record<ContactImportField, readonly string[]> = {\n email: [\n 'email',\n 'email address',\n 'e-mail',\n 'e-mail address',\n 'contact email',\n 'primary email',\n 'work email',\n ],\n name: ['name', 'full name', 'contact name', 'display name', 'person'],\n phone: [\n 'phone',\n 'phone number',\n 'telephone',\n 'mobile',\n 'mobile phone',\n 'cell',\n 'work phone',\n ],\n jobTitle: ['job title', 'title', 'position', 'role'],\n companyName: ['company', 'company name', 'organization', 'organisation', 'account'],\n addressLine1: ['address', 'address line 1', 'street', 'street address', 'address 1'],\n addressLine2: ['address line 2', 'address 2', 'street 2', 'apartment', 'suite'],\n addressCity: ['city', 'town', 'locality'],\n addressState: ['state', 'region', 'province', 'county', 'state/region'],\n addressPostalCode: ['postal code', 'postcode', 'zip', 'zip code', 'post code'],\n addressCountry: ['country', 'country code', 'country/region'],\n tags: ['tags', 'tag', 'labels', 'groups', 'segments'],\n ownerEmail: ['owner', 'owner email', 'contact owner', 'assigned to', 'account owner'],\n lifecycleStage: ['lifecycle stage', 'stage', 'lifecycle', 'status', 'lead status'],\n marketingConsent: [\n 'marketing consent',\n 'consent',\n 'opt in',\n 'opt-in',\n 'opted in',\n 'subscribed',\n 'marketing opt in',\n 'email consent',\n ],\n}\n\n/**\n * What a column is mapped to: a standard field by name, or a custom field\n * as `custom:<key>` — see `customImportTarget`.\n */\nexport type ContactImportTargetId = ContactImportField | `custom:${string}`\n\n/** Column index → target. A column absent from the map is not imported. */\nexport type ContactImportMapping = Record<number, ContactImportTargetId>\n\n/** {@link FIELD_ALIASES}, normalized once for the matcher. */\nconst FIELD_ALIAS_KEYS = importAliasKeys(CONTACT_IMPORT_FIELDS, FIELD_ALIASES)\n\n/**\n * A proposed mapping from a file's header row — the shared matcher over the\n * contact vocabulary. Custom fields win over standard aliases when a header\n * matches a definition's label or key exactly: a merchant who defined a\n * field called \"Status\" meant THEIR status, not our lifecycle stage.\n */\nexport function guessContactImportMapping(\n columns: readonly string[],\n fields: readonly Pick<ContactFieldDefinition, 'key' | 'label'>[] = [],\n): ContactImportMapping {\n return guessImportMapping(columns, CONTACT_IMPORT_FIELDS, FIELD_ALIAS_KEYS, fields)\n}\n\n/**\n * What the browser posts for one line: the cells the mapping selected,\n * under the field they were mapped to, verbatim.\n *\n * Values are `unknown` because the server reads this off an untrusted body;\n * the mapper below only ever writes strings into it.\n */\nexport interface ContactImportRawRow {\n email?: unknown\n name?: unknown\n phone?: unknown\n jobTitle?: unknown\n companyName?: unknown\n addressLine1?: unknown\n addressLine2?: unknown\n addressCity?: unknown\n addressState?: unknown\n addressPostalCode?: unknown\n addressCountry?: unknown\n tags?: unknown\n ownerEmail?: unknown\n lifecycleStage?: unknown\n marketingConsent?: unknown\n /** Custom values keyed by definition key. */\n custom?: unknown\n}\n\n/** One parsed line under the mapping. Empty cells are left absent. */\nexport function mapContactImportRow(\n cells: readonly string[],\n mapping: ContactImportMapping,\n): ContactImportRawRow {\n return mapImportRow(cells, mapping)\n}\n\n/** Why one row was not stored. */\nexport type ContactImportSkipReason =\n | 'invalid-email'\n | 'duplicate'\n | 'audience-band'\n /** The person was erased from this workspace at their request (AGL-2623). */\n | 'erased'\n | 'write-failed'\n\n/** How a skip reason reads on screen and in the downloaded file. */\nexport const CONTACT_IMPORT_SKIP_LABELS: Record<ContactImportSkipReason, string> = {\n 'invalid-email': 'Not a valid email address',\n duplicate: 'Appears earlier in the file',\n 'audience-band': 'Contact limit reached',\n erased: 'Erased from this workspace at their request',\n 'write-failed': 'Could not be saved',\n}\n\n/** A cell the file carried that could not be read as the field it was mapped to. */\nexport type ContactImportDroppedValue = ImportDroppedValue\n\n/** One row, ready to be written. */\nexport interface ContactImportRow {\n email: string\n name?: string\n /** E.164. */\n phone?: string\n jobTitle?: string\n companyName?: string\n address?: AglynPostalAddress\n /** Lowercased, deduplicated, capped at {@link CONTACT_IMPORT_TAGS_MAX}. */\n tags: string[]\n /** Normalized, for the server to resolve against the org's members. */\n ownerEmail?: string\n lifecycleStage?: ContactLifecycleStage\n marketingConsent: boolean\n /** Only the keys whose definition the caller supplied and whose value read. */\n custom: Record<string, ContactCustomValue>\n dropped: ContactImportDroppedValue[]\n}\n\nexport type ContactImportRowVerdict =\n | { ok: true; row: ContactImportRow }\n | { ok: false; reason: 'invalid-email'; input: string }\n\n/**\n * A tag cell as the tags it names.\n *\n * `|` or `,` separated — the CSV export writes `|`, most other products\n * write `,` — lowercased and trimmed like the profile drawer's own tag\n * field, so an imported `VIP` and a typed `vip` are one tag and not two.\n */\nexport function parseContactImportTags(value: unknown): string[] {\n return parseImportTags(value, CONTACT_IMPORT_TAGS_MAX)\n}\n\n/**\n * A yes/no cell as a boolean, or `null` when it is neither.\n *\n * The affirmatives are the ones consent and checkbox columns actually\n * carry; the negatives are listed so that an explicit `no` is a `false`\n * rather than an unreadable value that gets reported as dropped.\n */\nexport function parseContactImportFlag(value: unknown): boolean | null {\n return parseImportFlag(value)\n}\n\n/**\n * A stage cell as a stage, accepting the stored id (`sales-qualified`), its\n * label (\"Sales qualified\") and the spellings between them\n * (`Sales_Qualified`), or `null` when it names none of them.\n */\nexport function parseContactImportLifecycleStage(\n value: unknown,\n): ContactLifecycleStage | null {\n const text = String(value ?? '')\n .trim()\n .toLowerCase()\n if (!text) return null\n const key = text.replace(/[\\s_]+/g, '-')\n if (isContactLifecycleStage(key)) return key\n for (const stage of CONTACT_LIFECYCLE_STAGES) {\n if (CONTACT_LIFECYCLE_STAGE_LABELS[stage].toLowerCase() === text) return stage\n }\n return null\n}\n\n/**\n * A cell as the value a custom field of this type stores.\n *\n * `{ value }` when it read, `{ value: undefined }` for a blank cell (nothing\n * to write and nothing to report), and `null` when the cell held something\n * the type cannot hold — a word in a number field, a choice a select does\n * not offer. A date is stored as epoch milliseconds, the shape every other\n * timestamp on a CRM record takes (`dueAtMs`, `expectedCloseAtMs`).\n */\nexport function parseContactImportCustomValue(\n definition: Pick<ContactFieldDefinition, 'type' | 'options'>,\n value: unknown,\n): { value: ContactCustomValue | undefined } | null {\n const text = String(value ?? '').trim()\n if (!text) return { value: undefined }\n switch (definition.type) {\n case 'text':\n case 'url':\n return { value: text.slice(0, CUSTOM_TEXT_MAX) }\n case 'number': {\n const number = Number(text.replace(/[,\\s$€£]/g, ''))\n return Number.isFinite(number) ? { value: number } : null\n }\n case 'date': {\n const ms = Date.parse(text)\n return Number.isFinite(ms) ? { value: ms } : null\n }\n case 'select': {\n const wanted = text.toLowerCase()\n const option = (definition.options ?? []).find(\n (candidate) => String(candidate).trim().toLowerCase() === wanted,\n )\n return option === undefined ? null : { value: String(option) }\n }\n case 'checkbox': {\n const flag = parseContactImportFlag(text)\n return flag === null ? null : { value: flag }\n }\n default:\n return null\n }\n}\n\n/**\n * One raw row as the values that will be written, or the reason it cannot\n * be.\n *\n * `fields` is the holder's live definitions: a custom value under a key\n * with no definition is dropped and reported, because a value nobody\n * defined a field for is a value no form will ever show.\n */\nexport function normalizeContactImportRow(\n raw: ContactImportRawRow,\n fields: readonly Pick<ContactFieldDefinition, 'key' | 'type' | 'options'>[] = [],\n): ContactImportRowVerdict {\n const email = normalizeContactEmail(raw.email)\n if (!email) {\n return { ok: false, reason: 'invalid-email', input: String(raw.email ?? '').trim() }\n }\n const dropped: ContactImportDroppedValue[] = []\n const drop = (field: string, value: unknown) => {\n dropped.push({ field, value: String(value ?? '').trim() })\n }\n\n const row: ContactImportRow = {\n email,\n tags: parseContactImportTags(raw.tags),\n marketingConsent: false,\n custom: {},\n dropped,\n }\n\n const name = importTextValue(raw.name, NAME_MAX)\n if (name) row.name = name\n const jobTitle = importTextValue(raw.jobTitle, NAME_MAX)\n if (jobTitle) row.jobTitle = jobTitle\n const companyName = importTextValue(raw.companyName, NAME_MAX)\n if (companyName) row.companyName = companyName\n\n const phoneText = importTextValue(raw.phone, 64)\n if (phoneText) {\n const phone = normalizePhone(phoneText)\n if (phone) row.phone = phone\n else drop('phone', phoneText)\n }\n\n const address = normalizeAddress({\n line1: importTextValue(raw.addressLine1, 200),\n line2: importTextValue(raw.addressLine2, 200),\n city: importTextValue(raw.addressCity, 120),\n state: importTextValue(raw.addressState, 120),\n postalCode: importTextValue(raw.addressPostalCode, 32),\n country: importTextValue(raw.addressCountry, 8),\n })\n if (address) row.address = address\n // The country is the one address part the normalizer drops silently — a\n // typed name is not a code — so it is the one part the report has to name.\n const countryText = importTextValue(raw.addressCountry, 64)\n if (countryText && !address?.country) drop('addressCountry', countryText)\n\n const ownerText = importTextValue(raw.ownerEmail, 320)\n if (ownerText) {\n const owner = normalizeContactEmail(ownerText)\n if (owner) row.ownerEmail = owner\n else drop('ownerEmail', ownerText)\n }\n\n const stageText = importTextValue(raw.lifecycleStage, 64)\n if (stageText) {\n const stage = parseContactImportLifecycleStage(stageText)\n if (stage) row.lifecycleStage = stage\n else drop('lifecycleStage', stageText)\n }\n\n const consentText = raw.marketingConsent\n if (consentText !== undefined && consentText !== null && String(consentText).trim()) {\n const flag = parseContactImportFlag(consentText)\n if (flag === null) drop('marketingConsent', consentText)\n else row.marketingConsent = flag\n }\n\n const custom =\n raw.custom && typeof raw.custom === 'object' && !Array.isArray(raw.custom)\n ? (raw.custom as Record<string, unknown>)\n : {}\n const definitions = new Map(fields.map((field) => [field.key, field]))\n for (const [key, value] of Object.entries(custom)) {\n const definition = definitions.get(key)\n if (!definition) {\n drop(customImportTarget(key), value)\n continue\n }\n const parsed = parseContactImportCustomValue(definition, value)\n if (!parsed) drop(customImportTarget(key), value)\n else if (parsed.value !== undefined) row.custom[key] = parsed.value\n }\n\n return { ok: true, row }\n}\n\n/** One row the server did not store, by its index in the request, named by address. */\nexport interface ContactImportSkippedRow {\n index: number\n email: string\n reason: ContactImportSkipReason\n}\n\n/**\n * What one request did — the shared tally plus how many companies the\n * company column brought into being. The drawer sums these across a file.\n */\nexport interface ContactImportChunkResult\n extends ImportChunkResult<ContactImportSkippedRow> {\n companiesCreated: number\n}\n\n/** An empty tally, for the drawer to fold chunk results into. */\nexport function emptyContactImportResult(): ContactImportChunkResult {\n return { ...emptyImportResult<ContactImportSkippedRow>(), companiesCreated: 0 }\n}\n\n/**\n * Two results as one — the shared fold, with the company tally beside it.\n * `offset` is where the chunk started in the file, so a skipped row's index\n * comes back as its position in the FILE rather than in the request.\n */\nexport function mergeContactImportResults(\n total: ContactImportChunkResult,\n chunk: ContactImportChunkResult,\n offset = 0,\n): ContactImportChunkResult {\n return {\n ...mergeImportResults(total, chunk, offset),\n companiesCreated: total.companiesCreated + Number(chunk.companiesCreated ?? 0),\n }\n}\n\n/**\n * The skipped rows as a file the operator can fix and re-import: the\n * original columns verbatim plus a trailing `Skipped because` column.\n */\nexport function contactImportSkippedCsv(\n columns: readonly string[],\n entries: readonly { cells: readonly string[]; reason: ContactImportSkipReason }[],\n): string {\n return importSkippedCsv(columns, entries, CONTACT_IMPORT_SKIP_LABELS)\n}\n"],"names":["normalizeAddress","normalizePhone","normalizeContactEmail","CSV_IMPORT_CHUNK_SIZE","CSV_IMPORT_MAX_BODY_BYTES","CSV_IMPORT_MAX_ROWS","CSV_IMPORT_PREVIEW_ROWS","customImportTarget","emptyImportResult","guessImportMapping","importAliasKeys","importSkippedCsv","importTextValue","mapImportRow","mergeImportResults","parseImportFlag","parseImportTags","CONTACT_LIFECYCLE_STAGE_LABELS","CONTACT_LIFECYCLE_STAGES","isContactLifecycleStage","customImportTargetKey","CONTACT_IMPORT_MAX_ROWS","CONTACT_IMPORT_CHUNK_SIZE","CONTACT_IMPORT_MAX_BODY_BYTES","CONTACT_IMPORT_PREVIEW_ROWS","CONTACT_IMPORT_TAGS_MAX","NAME_MAX","CUSTOM_TEXT_MAX","CONTACT_IMPORT_FIELDS","CONTACT_IMPORT_FIELD_LABELS","email","name","phone","jobTitle","companyName","addressLine1","addressLine2","addressCity","addressState","addressPostalCode","addressCountry","tags","ownerEmail","lifecycleStage","marketingConsent","FIELD_ALIASES","FIELD_ALIAS_KEYS","guessContactImportMapping","columns","fields","mapContactImportRow","cells","mapping","CONTACT_IMPORT_SKIP_LABELS","duplicate","erased","parseContactImportTags","value","parseContactImportFlag","parseContactImportLifecycleStage","text","String","trim","toLowerCase","key","replace","stage","parseContactImportCustomValue","definition","undefined","type","slice","number","Number","isFinite","ms","Date","parse","wanted","option","options","find","candidate","flag","normalizeContactImportRow","raw","ok","reason","input","dropped","drop","field","push","row","custom","phoneText","address","line1","line2","city","state","postalCode","country","countryText","ownerText","owner","stageText","consentText","Array","isArray","definitions","Map","map","Object","entries","get","parsed","emptyContactImportResult","companiesCreated","mergeContactImportResults","total","chunk","offset","contactImportSkippedCsv"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkDC;AAGD,SAASA,gBAAgB,EAAEC,cAAc,QAAQ,oDAAmD;AACpG,SAASC,qBAAqB,QAAQ,kCAAiC;AACvE,SACEC,qBAAqB,EACrBC,yBAAyB,EACzBC,mBAAmB,EACnBC,uBAAuB,EACvBC,kBAAkB,EAElBC,iBAAiB,EACjBC,kBAAkB,EAClBC,eAAe,EAGfC,gBAAgB,EAChBC,eAAe,EACfC,YAAY,EACZC,kBAAkB,EAClBC,eAAe,EACfC,eAAe,QACV,oCAAmC;AAC1C,SACEC,8BAA8B,EAC9BC,wBAAwB,EAIxBC,uBAAuB,QAClB,6BAA4B;AAEnC;;;;CAIC,GACD,SAASZ,kBAAkB,EAAEa,qBAAqB,QAAQ,oCAAmC;AAE7F;;;;;CAKC,GACD,OAAO,MAAMC,0BAA0BhB,oBAAmB;AAE1D,OAAO,MAAMiB,4BAA4BnB,sBAAqB;AAE9D,OAAO,MAAMoB,gCAAgCnB,0BAAyB;AAEtE,OAAO,MAAMoB,8BAA8BlB,wBAAuB;AAElE;;;CAGC,GACD,OAAO,MAAMmB,0BAA0B,GAAE;AAEzC,6EAA6E,GAC7E,MAAMC,WAAW;AACjB,MAAMC,kBAAkB;AAExB;;;;CAIC,GACD,OAAO,MAAMC,wBAAwB;IACnC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACD,CAAS;AAIV,sFAAsF,GACtF,OAAO,MAAMC,8BAAkE;IAC7EC,OAAO;IACPC,MAAM;IACNC,OAAO;IACPC,UAAU;IACVC,aAAa;IACbC,cAAc;IACdC,cAAc;IACdC,aAAa;IACbC,cAAc;IACdC,mBAAmB;IACnBC,gBAAgB;IAChBC,MAAM;IACNC,YAAY;IACZC,gBAAgB;IAChBC,kBAAkB;AACpB,EAAC;AAED;;;;;;;;CAQC,GACD,MAAMC,gBAA+D;IACnEf,OAAO;QACL;QACA;QACA;QACA;QACA;QACA;QACA;KACD;IACDC,MAAM;QAAC;QAAQ;QAAa;QAAgB;QAAgB;KAAS;IACrEC,OAAO;QACL;QACA;QACA;QACA;QACA;QACA;QACA;KACD;IACDC,UAAU;QAAC;QAAa;QAAS;QAAY;KAAO;IACpDC,aAAa;QAAC;QAAW;QAAgB;QAAgB;QAAgB;KAAU;IACnFC,cAAc;QAAC;QAAW;QAAkB;QAAU;QAAkB;KAAY;IACpFC,cAAc;QAAC;QAAkB;QAAa;QAAY;QAAa;KAAQ;IAC/EC,aAAa;QAAC;QAAQ;QAAQ;KAAW;IACzCC,cAAc;QAAC;QAAS;QAAU;QAAY;QAAU;KAAe;IACvEC,mBAAmB;QAAC;QAAe;QAAY;QAAO;QAAY;KAAY;IAC9EC,gBAAgB;QAAC;QAAW;QAAgB;KAAiB;IAC7DC,MAAM;QAAC;QAAQ;QAAO;QAAU;QAAU;KAAW;IACrDC,YAAY;QAAC;QAAS;QAAe;QAAiB;QAAe;KAAgB;IACrFC,gBAAgB;QAAC;QAAmB;QAAS;QAAa;QAAU;KAAc;IAClFC,kBAAkB;QAChB;QACA;QACA;QACA;QACA;QACA;QACA;QACA;KACD;AACH;AAWA,4DAA4D,GAC5D,MAAME,mBAAmBpC,gBAAgBkB,uBAAuBiB;AAEhE;;;;;CAKC,GACD,OAAO,SAASE,0BACdC,OAA0B,EAC1BC,SAAmE,EAAE;IAErE,OAAOxC,mBAAmBuC,SAASpB,uBAAuBkB,kBAAkBG;AAC9E;AA6BA,oEAAoE,GACpE,OAAO,SAASC,oBACdC,KAAwB,EACxBC,OAA6B;IAE7B,OAAOvC,aAAasC,OAAOC;AAC7B;AAWA,kEAAkE,GAClE,OAAO,MAAMC,6BAAsE;IACjF,iBAAiB;IACjBC,WAAW;IACX,iBAAiB;IACjBC,QAAQ;IACR,gBAAgB;AAClB,EAAC;AA6BD;;;;;;CAMC,GACD,OAAO,SAASC,uBAAuBC,KAAc;IACnD,OAAOzC,gBAAgByC,OAAOhC;AAChC;AAEA;;;;;;CAMC,GACD,OAAO,SAASiC,uBAAuBD,KAAc;IACnD,OAAO1C,gBAAgB0C;AACzB;AAEA;;;;CAIC,GACD,OAAO,SAASE,iCACdF,KAAc;IAEd,MAAMG,OAAOC,OAAOJ,gBAAAA,QAAS,IAC1BK,IAAI,GACJC,WAAW;IACd,IAAI,CAACH,MAAM,OAAO;IAClB,MAAMI,MAAMJ,KAAKK,OAAO,CAAC,WAAW;IACpC,IAAI9C,wBAAwB6C,MAAM,OAAOA;IACzC,KAAK,MAAME,SAAShD,yBAA0B;QAC5C,IAAID,8BAA8B,CAACiD,MAAM,CAACH,WAAW,OAAOH,MAAM,OAAOM;IAC3E;IACA,OAAO;AACT;AAEA;;;;;;;;CAQC,GACD,OAAO,SAASC,8BACdC,UAA4D,EAC5DX,KAAc;IAEd,MAAMG,OAAOC,OAAOJ,gBAAAA,QAAS,IAAIK,IAAI;IACrC,IAAI,CAACF,MAAM,OAAO;QAAEH,OAAOY;IAAU;IACrC,OAAQD,WAAWE,IAAI;QACrB,KAAK;QACL,KAAK;YACH,OAAO;gBAAEb,OAAOG,KAAKW,KAAK,CAAC,GAAG5C;YAAiB;QACjD,KAAK;YAAU;gBACb,MAAM6C,SAASC,OAAOb,KAAKK,OAAO,CAAC,aAAa;gBAChD,OAAOQ,OAAOC,QAAQ,CAACF,UAAU;oBAAEf,OAAOe;gBAAO,IAAI;YACvD;QACA,KAAK;YAAQ;gBACX,MAAMG,KAAKC,KAAKC,KAAK,CAACjB;gBACtB,OAAOa,OAAOC,QAAQ,CAACC,MAAM;oBAAElB,OAAOkB;gBAAG,IAAI;YAC/C;QACA,KAAK;YAAU;oBAEGP;gBADhB,MAAMU,SAASlB,KAAKG,WAAW;gBAC/B,MAAMgB,SAAS,EAACX,sBAAAA,WAAWY,OAAO,YAAlBZ,sBAAsB,EAAE,EAAEa,IAAI,CAC5C,CAACC,YAAcrB,OAAOqB,WAAWpB,IAAI,GAAGC,WAAW,OAAOe;gBAE5D,OAAOC,WAAWV,YAAY,OAAO;oBAAEZ,OAAOI,OAAOkB;gBAAQ;YAC/D;QACA,KAAK;YAAY;gBACf,MAAMI,OAAOzB,uBAAuBE;gBACpC,OAAOuB,SAAS,OAAO,OAAO;oBAAE1B,OAAO0B;gBAAK;YAC9C;QACA;YACE,OAAO;IACX;AACF;AAEA;;;;;;;CAOC,GACD,OAAO,SAASC,0BACdC,GAAwB,EACxBpC,SAA8E,EAAE;IAEhF,MAAMnB,QAAQ5B,sBAAsBmF,IAAIvD,KAAK;IAC7C,IAAI,CAACA,OAAO;YACiDuD;QAA3D,OAAO;YAAEC,IAAI;YAAOC,QAAQ;YAAiBC,OAAO3B,QAAOwB,aAAAA,IAAIvD,KAAK,YAATuD,aAAa,IAAIvB,IAAI;QAAG;IACrF;IACA,MAAM2B,UAAuC,EAAE;IAC/C,MAAMC,OAAO,CAACC,OAAelC;QAC3BgC,QAAQG,IAAI,CAAC;YAAED;YAAOlC,OAAOI,OAAOJ,gBAAAA,QAAS,IAAIK,IAAI;QAAG;IAC1D;IAEA,MAAM+B,MAAwB;QAC5B/D;QACAW,MAAMe,uBAAuB6B,IAAI5C,IAAI;QACrCG,kBAAkB;QAClBkD,QAAQ,CAAC;QACTL;IACF;IAEA,MAAM1D,OAAOnB,gBAAgByE,IAAItD,IAAI,EAAEL;IACvC,IAAIK,MAAM8D,IAAI9D,IAAI,GAAGA;IACrB,MAAME,WAAWrB,gBAAgByE,IAAIpD,QAAQ,EAAEP;IAC/C,IAAIO,UAAU4D,IAAI5D,QAAQ,GAAGA;IAC7B,MAAMC,cAActB,gBAAgByE,IAAInD,WAAW,EAAER;IACrD,IAAIQ,aAAa2D,IAAI3D,WAAW,GAAGA;IAEnC,MAAM6D,YAAYnF,gBAAgByE,IAAIrD,KAAK,EAAE;IAC7C,IAAI+D,WAAW;QACb,MAAM/D,QAAQ/B,eAAe8F;QAC7B,IAAI/D,OAAO6D,IAAI7D,KAAK,GAAGA;aAClB0D,KAAK,SAASK;IACrB;IAEA,MAAMC,UAAUhG,iBAAiB;QAC/BiG,OAAOrF,gBAAgByE,IAAIlD,YAAY,EAAE;QACzC+D,OAAOtF,gBAAgByE,IAAIjD,YAAY,EAAE;QACzC+D,MAAMvF,gBAAgByE,IAAIhD,WAAW,EAAE;QACvC+D,OAAOxF,gBAAgByE,IAAI/C,YAAY,EAAE;QACzC+D,YAAYzF,gBAAgByE,IAAI9C,iBAAiB,EAAE;QACnD+D,SAAS1F,gBAAgByE,IAAI7C,cAAc,EAAE;IAC/C;IACA,IAAIwD,SAASH,IAAIG,OAAO,GAAGA;IAC3B,wEAAwE;IACxE,2EAA2E;IAC3E,MAAMO,cAAc3F,gBAAgByE,IAAI7C,cAAc,EAAE;IACxD,IAAI+D,eAAe,EAACP,2BAAAA,QAASM,OAAO,GAAEZ,KAAK,kBAAkBa;IAE7D,MAAMC,YAAY5F,gBAAgByE,IAAI3C,UAAU,EAAE;IAClD,IAAI8D,WAAW;QACb,MAAMC,QAAQvG,sBAAsBsG;QACpC,IAAIC,OAAOZ,IAAInD,UAAU,GAAG+D;aACvBf,KAAK,cAAcc;IAC1B;IAEA,MAAME,YAAY9F,gBAAgByE,IAAI1C,cAAc,EAAE;IACtD,IAAI+D,WAAW;QACb,MAAMxC,QAAQP,iCAAiC+C;QAC/C,IAAIxC,OAAO2B,IAAIlD,cAAc,GAAGuB;aAC3BwB,KAAK,kBAAkBgB;IAC9B;IAEA,MAAMC,cAActB,IAAIzC,gBAAgB;IACxC,IAAI+D,gBAAgBtC,aAAasC,gBAAgB,QAAQ9C,OAAO8C,aAAa7C,IAAI,IAAI;QACnF,MAAMqB,OAAOzB,uBAAuBiD;QACpC,IAAIxB,SAAS,MAAMO,KAAK,oBAAoBiB;aACvCd,IAAIjD,gBAAgB,GAAGuC;IAC9B;IAEA,MAAMW,SACJT,IAAIS,MAAM,IAAI,OAAOT,IAAIS,MAAM,KAAK,YAAY,CAACc,MAAMC,OAAO,CAACxB,IAAIS,MAAM,IACpET,IAAIS,MAAM,GACX,CAAC;IACP,MAAMgB,cAAc,IAAIC,IAAI9D,OAAO+D,GAAG,CAAC,CAACrB,QAAU;YAACA,MAAM3B,GAAG;YAAE2B;SAAM;IACpE,KAAK,MAAM,CAAC3B,KAAKP,MAAM,IAAIwD,OAAOC,OAAO,CAACpB,QAAS;QACjD,MAAM1B,aAAa0C,YAAYK,GAAG,CAACnD;QACnC,IAAI,CAACI,YAAY;YACfsB,KAAKnF,mBAAmByD,MAAMP;YAC9B;QACF;QACA,MAAM2D,SAASjD,8BAA8BC,YAAYX;QACzD,IAAI,CAAC2D,QAAQ1B,KAAKnF,mBAAmByD,MAAMP;aACtC,IAAI2D,OAAO3D,KAAK,KAAKY,WAAWwB,IAAIC,MAAM,CAAC9B,IAAI,GAAGoD,OAAO3D,KAAK;IACrE;IAEA,OAAO;QAAE6B,IAAI;QAAMO;IAAI;AACzB;AAkBA,+DAA+D,GAC/D,OAAO,SAASwB;IACd,OAAO,aAAK7G;QAA8C8G,kBAAkB;;AAC9E;AAEA;;;;CAIC,GACD,OAAO,SAASC,0BACdC,KAA+B,EAC/BC,KAA+B,EAC/BC,SAAS,CAAC;QAI0CD;IAFpD,OAAO,aACF3G,mBAAmB0G,OAAOC,OAAOC;QACpCJ,kBAAkBE,MAAMF,gBAAgB,GAAG7C,QAAOgD,0BAAAA,MAAMH,gBAAgB,YAAtBG,0BAA0B;;AAEhF;AAEA;;;CAGC,GACD,OAAO,SAASE,wBACd3E,OAA0B,EAC1BkE,OAAiF;IAEjF,OAAOvG,iBAAiBqC,SAASkE,SAAS7D;AAC5C"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import { type CrmLeadStatus } from '@aglyn/aglyn/app-utils/crm';
|
|
18
|
+
import { type ImportChunkResult, type ImportDroppedValue } from '@aglyn/aglyn/app-utils/csv-import';
|
|
19
|
+
/** The shared ceilings, under this collection's names. */
|
|
20
|
+
export declare const LEAD_IMPORT_MAX_ROWS = 5000;
|
|
21
|
+
export declare const LEAD_IMPORT_CHUNK_SIZE = 200;
|
|
22
|
+
export declare const LEAD_IMPORT_MAX_BODY_BYTES = 2000000;
|
|
23
|
+
export declare const LEAD_IMPORT_PREVIEW_ROWS = 10;
|
|
24
|
+
/** The same caps the lead's own cards hold a typed value to. */
|
|
25
|
+
export declare const LEAD_IMPORT_NOTES_MAX = 4000;
|
|
26
|
+
export declare const LEAD_IMPORT_REASON_MAX = 500;
|
|
27
|
+
/**
|
|
28
|
+
* The statuses a file may set.
|
|
29
|
+
*
|
|
30
|
+
* The list's own status control offers exactly these three, and for the
|
|
31
|
+
* reason given above: `qualified` is written by the conversion, beside the
|
|
32
|
+
* contact it produced.
|
|
33
|
+
*/
|
|
34
|
+
export declare const LEAD_IMPORT_STATUSES: readonly CrmLeadStatus[];
|
|
35
|
+
/** The fields a column may be mapped to, in the order the mapping menu lists them. */
|
|
36
|
+
export declare const LEAD_IMPORT_FIELDS: readonly ["email", "name", "status", "ownerEmail", "unqualifiedReason", "notes"];
|
|
37
|
+
export type LeadImportField = (typeof LEAD_IMPORT_FIELDS)[number];
|
|
38
|
+
/** How each field reads in the mapping menu. Typed so a field cannot ship unlabeled. */
|
|
39
|
+
export declare const LEAD_IMPORT_FIELD_LABELS: Record<LeadImportField, string>;
|
|
40
|
+
/** Column index → field. A column absent from the map is not imported. */
|
|
41
|
+
export type LeadImportMapping = Record<number, LeadImportField>;
|
|
42
|
+
/** A proposed mapping from a file's header row, each field taken at most once. */
|
|
43
|
+
export declare function guessLeadImportMapping(columns: readonly string[]): LeadImportMapping;
|
|
44
|
+
/**
|
|
45
|
+
* What the browser posts for one line: the cells the mapping selected,
|
|
46
|
+
* under the field they were mapped to, verbatim. `unknown` because the
|
|
47
|
+
* server reads this off an untrusted body.
|
|
48
|
+
*/
|
|
49
|
+
export type LeadImportRawRow = Partial<Record<LeadImportField, unknown>>;
|
|
50
|
+
/** One parsed line under the mapping. Empty cells are left absent; a lead has no custom fields. */
|
|
51
|
+
export declare function mapLeadImportRow(cells: readonly string[], mapping: Record<number, LeadImportField | `custom:${string}`>): LeadImportRawRow;
|
|
52
|
+
export type LeadImportSkipReason = 'invalid-email' | 'duplicate' | 'lead-ceiling' | 'write-failed';
|
|
53
|
+
/** How a skip reason reads on screen and in the downloaded file. */
|
|
54
|
+
export declare const LEAD_IMPORT_SKIP_LABELS: Record<LeadImportSkipReason, string>;
|
|
55
|
+
/** One row, ready for the server to resolve and write. */
|
|
56
|
+
export interface LeadImportRow {
|
|
57
|
+
/** Normalized — the address `personKey` derives the document id from. */
|
|
58
|
+
email: string;
|
|
59
|
+
name?: string;
|
|
60
|
+
/** Absent leaves an existing lead's status alone and a new one reading as `new`. */
|
|
61
|
+
status?: CrmLeadStatus;
|
|
62
|
+
/** Normalized, for the server to resolve against the org's members. */
|
|
63
|
+
ownerEmail?: string;
|
|
64
|
+
/** Only ever set beside `status: 'unqualified'` — the pair is one fact. */
|
|
65
|
+
unqualifiedReason?: string;
|
|
66
|
+
notes?: string;
|
|
67
|
+
dropped: ImportDroppedValue[];
|
|
68
|
+
}
|
|
69
|
+
export type LeadImportRowVerdict = {
|
|
70
|
+
ok: true;
|
|
71
|
+
row: LeadImportRow;
|
|
72
|
+
} | {
|
|
73
|
+
ok: false;
|
|
74
|
+
reason: 'invalid-email';
|
|
75
|
+
input: string;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* A status cell by id or by label — `working`, `Working` and `WORKING` are
|
|
79
|
+
* one status. `null` for a cell naming no status a file may set, which
|
|
80
|
+
* includes the well-spelled `Qualified` the export writes for a converted
|
|
81
|
+
* lead.
|
|
82
|
+
*/
|
|
83
|
+
export declare function parseImportLeadStatus(value: unknown): CrmLeadStatus | null;
|
|
84
|
+
/**
|
|
85
|
+
* One raw row as the values that will be written, or the reason it cannot
|
|
86
|
+
* be. Refused here only for an unusable address, because the address is
|
|
87
|
+
* the document id; the owner is refused by the server, which alone can
|
|
88
|
+
* look them up.
|
|
89
|
+
*/
|
|
90
|
+
export declare function normalizeLeadImportRow(raw: LeadImportRawRow): LeadImportRowVerdict;
|
|
91
|
+
/** One row the server did not store, by its index in the request, named by the address. */
|
|
92
|
+
export interface LeadImportSkippedRow {
|
|
93
|
+
index: number;
|
|
94
|
+
email: string;
|
|
95
|
+
reason: LeadImportSkipReason;
|
|
96
|
+
}
|
|
97
|
+
/** What one request did. The drawer sums these across a file. */
|
|
98
|
+
export type LeadImportChunkResult = ImportChunkResult<LeadImportSkippedRow>;
|
|
99
|
+
export declare function emptyLeadImportResult(): LeadImportChunkResult;
|
|
100
|
+
export declare function mergeLeadImportResults(total: LeadImportChunkResult, chunk: LeadImportChunkResult, offset?: number): LeadImportChunkResult;
|
|
101
|
+
/** The skipped rows as a file the operator can fix and re-import. */
|
|
102
|
+
export declare function leadImportSkippedCsv(columns: readonly string[], entries: readonly {
|
|
103
|
+
cells: readonly string[];
|
|
104
|
+
reason: LeadImportSkipReason;
|
|
105
|
+
}[]): string;
|