@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 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/model/deal-board-model.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 * The pipeline's arithmetic and the stage editor's rules (AGL-2598), as pure\n * functions — no Firestore, no React — so the board, the table, the detail\n * page and the server route read one definition of \"what is this pipeline\n * worth\" and \"which stage edits are legal\".\n */\n\n// The module itself and never the `@aglyn/aglyn` barrel: this file is\n// reached from the server entry through the deal-stage route, and the barrel\n// carries client-only context modules the App Router graph refuses on a\n// server path (AGL-405/1349).\nimport {\n type CrmDeal,\n type CrmDealStage,\n type CrmPipeline,\n DEFAULT_DEAL_STAGES,\n dealStageById,\n isPipelineArchived,\n weightedDealAmountCents,\n} from '@aglyn/aglyn/app-utils/crm'\n\n/** A deal as the console reads it: the model plus its document id. */\nexport type DealDoc = CrmDeal & {\n $id: string\n /**\n * The contact's and the company's names, copied onto the deal when the\n * link is made. A board of two hundred cards cannot afford a keyed read per\n * card to caption each one, and the caption is allowed to lag a rename —\n * the link itself is the id, which never changes.\n */\n contactName?: string\n companyName?: string\n}\n\n/** A pipeline as the console reads it. */\nexport type PipelineDoc = CrmPipeline & { $id: string }\n\n/** The document id the seeded default pipeline is written under. */\nexport const DEFAULT_PIPELINE_ID = 'default'\n\n/** Name of the pipeline every org starts with. */\nexport const DEFAULT_PIPELINE_NAME = 'Sales'\n\nexport const PIPELINE_NAME_MAX = 60\n\n/** The pipelines a picker offers and a new deal may land in: the ones not archived. */\nexport function activePipelines<T extends Pick<CrmPipeline, 'archivedAt'>>(\n pipelines: readonly T[],\n): T[] {\n return pipelines.filter((pipeline) => !isPipelineArchived(pipeline))\n}\n\n/**\n * The pipeline a new deal lands in when nobody picks one: the active one\n * marked default, else the first active one, else nothing. An archived\n * pipeline is never the default even if its flag was left on — archiving\n * clears the flag, but a document written by an older console may not\n * have had it cleared, and a default nobody can create a deal in is a\n * board that opens on a pipeline the picker does not list.\n */\nexport function defaultPipelineOf<T extends Pick<CrmPipeline, 'archivedAt' | 'isDefault'>>(\n pipelines: readonly T[],\n): T | null {\n const active = activePipelines(pipelines)\n return active.find((pipeline) => pipeline.isDefault) ?? active[0] ?? null\n}\n\n/**\n * Why a pipeline cannot be created or renamed to `name`, or `null`.\n *\n * Names are unique among the ACTIVE pipelines, compared without case: the\n * picker lists them by name alone, and two rows reading \"Sales\" would be a\n * choice nobody can make. An archived pipeline's name is free to reuse —\n * \"Sales 2025\" retired and \"Sales\" started over is the point of archiving.\n */\nexport function pipelineNameProblem(\n name: string,\n pipelines: ReadonlyArray<Pick<CrmPipeline, 'name' | 'archivedAt'> & { $id: string }>,\n ownId?: string,\n): string | null {\n const trimmed = String(name ?? '').trim()\n if (!trimmed) return 'A pipeline needs a name.'\n const taken = activePipelines(pipelines).find(\n (pipeline) =>\n pipeline.$id !== ownId &&\n String(pipeline.name ?? '').trim().toLowerCase() === trimmed.toLowerCase(),\n )\n if (taken) return `Another pipeline is already called \"${taken.name}\".`\n return null\n}\n\n/**\n * Why a pipeline cannot be archived, or `null` when it can.\n *\n * `openDeals` is the server's count, asked for before this is called, the\n * way `stageRemovalRefusal` asks: an open deal in an archived pipeline is\n * a card on no board, and the count is taken at the click because a deal\n * can be created into the pipeline from another tab meanwhile. The default\n * pipeline cannot go while it is the default — a new deal has to land\n * somewhere — and the last active one cannot go at all, for the same\n * reason from the other side.\n */\nexport function pipelineArchiveRefusal(\n pipeline: Pick<CrmPipeline, 'archivedAt' | 'isDefault'> & { $id: string },\n pipelines: ReadonlyArray<Pick<CrmPipeline, 'archivedAt'> & { $id: string }>,\n openDeals: number,\n): string | null {\n if (isPipelineArchived(pipeline)) return 'This pipeline is already archived.'\n if (activePipelines(pipelines).filter((entry) => entry.$id !== pipeline.$id).length === 0) {\n return 'The last pipeline cannot be archived.'\n }\n if (pipeline.isDefault) {\n return 'This is the default pipeline. Make another one the default first.'\n }\n if (openDeals > 0) {\n return (\n `${openDeals.toLocaleString()} open ${openDeals === 1 ? 'deal is' : 'deals are'} ` +\n 'in this pipeline. Close or move them first, so none is left on a board nobody opens.'\n )\n }\n return null\n}\n\n/** What every pipeline write stamps besides its name. */\nexport interface PipelineWriteContext {\n visibleTo: readonly string[]\n hostId: string\n uid: string\n nowMs: number\n}\n\n/**\n * The document a new pipeline is created as: the name, a COPY of the\n * default stages, not the default, active. A copy for the reason\n * `DEFAULT_DEAL_STAGES` gives — a second pipeline seeded later must start\n * from the original set, whatever the first one's editor did to its own.\n */\nexport function newPipelineDocument(\n name: string,\n context: PipelineWriteContext,\n): Record<string, unknown> {\n return {\n name: String(name ?? '').trim().slice(0, PIPELINE_NAME_MAX),\n stages: DEFAULT_DEAL_STAGES.map((stage) => ({ ...stage })),\n isDefault: false,\n archivedAt: null,\n visibleTo: [...context.visibleTo],\n hostId: context.hostId,\n createdByUid: context.uid,\n createdAt: new Date(context.nowMs),\n updatedAt: new Date(context.nowMs),\n }\n}\n\n/**\n * Currencies the amount picker offers, lowercase ISO 4217 as the model\n * stores them. Short on purpose: a picker of a hundred and eighty currencies\n * is a scroll, and a deal in one outside this list is entered through the\n * API with whatever code it carries — the display below formats any code\n * `Intl` knows.\n */\nexport const DEAL_CURRENCIES = [\n 'usd',\n 'eur',\n 'gbp',\n 'cad',\n 'aud',\n 'nzd',\n 'chf',\n 'jpy',\n 'sek',\n 'nok',\n 'dkk',\n 'mxn',\n 'brl',\n 'inr',\n 'sgd',\n] as const\n\nexport const DEFAULT_DEAL_CURRENCY = 'usd'\n\n/** How a deal's `status` reads on screen. */\nexport const DEAL_STATUS_LABELS: Record<CrmDeal['status'], string> = {\n open: 'Open',\n won: 'Won',\n lost: 'Lost',\n}\n\n/** Cents → the reader's locale, or a plain number when the code is unknown. */\nexport function formatMoney(cents: number, currency: string | undefined): string {\n const code = String(currency || DEFAULT_DEAL_CURRENCY).toUpperCase()\n const amount = (Number(cents) || 0) / 100\n try {\n return new Intl.NumberFormat(undefined, {\n style: 'currency',\n currency: code,\n }).format(amount)\n } catch {\n return `${amount.toFixed(2)} ${code}`\n }\n}\n\n/**\n * A typed amount, as cents, or `null` when it is not a number.\n *\n * Accepts what people paste from an invoice — a thousands separator, a\n * currency symbol, surrounding space — and refuses a negative: a deal is\n * worth zero or more, and a negative amount here is a typo rather than a\n * refund. Rounded rather than truncated so `19.995` does not lose a cent.\n */\nexport function parseAmountInput(input: string): number | null {\n const cleaned = String(input ?? '')\n .replace(/[^0-9.-]/g, '')\n .trim()\n if (!cleaned) return null\n const value = Number(cleaned)\n if (!Number.isFinite(value) || value < 0) return null\n return Math.round(value * 100)\n}\n\n/** Cents → the string the amount field shows, `''` for nothing. */\nexport function amountInputValue(cents: number | undefined | null): string {\n if (typeof cents !== 'number' || !Number.isFinite(cents)) return ''\n return (cents / 100).toFixed(2)\n}\n\n/**\n * Epoch ms from whatever a timestamp field holds after a round trip — a\n * Firestore `Timestamp`, a `Date` a client just wrote, or a number.\n */\nexport function timestampMs(value: unknown): number | null {\n if (!value) return null\n if (typeof value === 'number') return Number.isFinite(value) ? value : null\n if (value instanceof Date) return value.getTime()\n const seconds = (value as { seconds?: unknown }).seconds\n if (typeof seconds === 'number') return seconds * 1000\n const toMillis = (value as { toMillis?: () => number }).toMillis\n if (typeof toMillis === 'function') return toMillis.call(value)\n return null\n}\n\n/** Whole days a deal has sat in its current stage, never negative. */\nexport function daysInStage(\n deal: Pick<CrmDeal, 'stageChangedAtMs' | 'createdAt'>,\n nowMs: number,\n): number {\n const since =\n typeof deal.stageChangedAtMs === 'number'\n ? deal.stageChangedAtMs\n : timestampMs(deal.createdAt)\n if (!since) return 0\n return Math.max(0, Math.floor((nowMs - since) / 86_400_000))\n}\n\n/** A sum per currency, because two currencies never add into one number. */\nexport type AmountByCurrency = Record<string, number>\n\nfunction addAmount(\n totals: AmountByCurrency,\n currency: string | undefined,\n cents: number,\n): void {\n const code = String(currency || DEFAULT_DEAL_CURRENCY).toLowerCase()\n totals[code] = (totals[code] ?? 0) + cents\n}\n\nexport interface BoardSummary {\n /** Deals whose `status` is `open`, whatever stage they sit in. */\n openCount: number\n /** Every open deal's full amount, per currency. */\n valueByCurrency: AmountByCurrency\n /** Every open deal's amount times its stage's probability, per currency. */\n weightedByCurrency: AmountByCurrency\n}\n\n/**\n * The three figures above the board.\n *\n * Closed deals are excluded from both totals rather than counted at full or\n * zero value: the summary describes what is still in play, and a won deal\n * counted in \"pipeline value\" would make the pipeline look larger every time\n * something closed. `weightedDealAmountCents` already answers zero for a\n * stage the pipeline no longer has.\n */\nexport function boardSummary(\n deals: readonly Pick<\n CrmDeal,\n 'status' | 'amountCents' | 'currency' | 'stageId'\n >[],\n pipeline: Pick<CrmPipeline, 'stages'> | null | undefined,\n): BoardSummary {\n const summary: BoardSummary = {\n openCount: 0,\n valueByCurrency: {},\n weightedByCurrency: {},\n }\n for (const deal of deals) {\n if (deal.status !== 'open') continue\n summary.openCount += 1\n const amount = Math.max(0, Math.round(Number(deal.amountCents ?? 0) || 0))\n addAmount(summary.valueByCurrency, deal.currency, amount)\n addAmount(\n summary.weightedByCurrency,\n deal.currency,\n weightedDealAmountCents(deal, dealStageById(pipeline, deal.stageId)),\n )\n }\n return summary\n}\n\n/** `$1,200.00 · €300.00` — one figure per currency, largest first. */\nexport function formatAmountByCurrency(totals: AmountByCurrency): string {\n const entries = Object.entries(totals).sort((a, b) => b[1] - a[1])\n if (!entries.length) return formatMoney(0, DEFAULT_DEAL_CURRENCY)\n return entries.map(([code, cents]) => formatMoney(cents, code)).join(' · ')\n}\n\n/** The pipeline's stages in `order`, ties broken by position. */\nexport function sortedStages(\n pipeline: Pick<CrmPipeline, 'stages'> | null | undefined,\n): CrmDealStage[] {\n return [...(pipeline?.stages ?? [])]\n .map((stage, index) => ({ stage, index }))\n .sort((a, b) => a.stage.order - b.stage.order || a.index - b.index)\n .map(({ stage }) => stage)\n}\n\n/** The stages a deal can be dragged between — everything that is not closed. */\nexport function openStages(\n pipeline: Pick<CrmPipeline, 'stages'> | null | undefined,\n): CrmDealStage[] {\n return sortedStages(pipeline).filter((stage) => stage.kind === 'open')\n}\n\n/** The stage whose `kind` is `won` or `lost`, or null when the pipeline lacks one. */\nexport function closingStage(\n pipeline: Pick<CrmPipeline, 'stages'> | null | undefined,\n kind: 'won' | 'lost',\n): CrmDealStage | null {\n return sortedStages(pipeline).find((stage) => stage.kind === kind) ?? null\n}\n\n/**\n * The id a new stage is stored under: the name as a slug, made unique\n * against the stages already there.\n *\n * A slug rather than a random id because the id appears in every deal that\n * sits in the stage and in every automation filter that names it, so a\n * reader looking at `stageId == \"demo-booked\"` should be able to tell which\n * stage that is without opening the pipeline. Uniqueness is what keeps a\n * second \"Demo\" from silently becoming the first.\n */\nexport function newStageId(\n name: string,\n existing: readonly CrmDealStage[],\n): string {\n const base =\n String(name ?? '')\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 40) || 'stage'\n const taken = new Set(existing.map((stage) => stage.id))\n if (!taken.has(base)) return base\n let suffix = 2\n while (taken.has(`${base}-${suffix}`)) suffix += 1\n return `${base}-${suffix}`\n}\n\n/**\n * Stages re-numbered 0..n in the order they are GIVEN.\n *\n * Positional on purpose: every caller hands this an array it has already\n * arranged — a swap, a splice, a filter over the sorted stages — and the\n * stored `order` on each entry is the arrangement being replaced. Sorting by\n * it here would put a swapped pair straight back where it was.\n */\nexport function renumberStages(\n stages: readonly CrmDealStage[],\n): CrmDealStage[] {\n return stages.map((stage, order) => ({ ...stage, order }))\n}\n\n/** 0–100, whole numbers; anything else becomes the nearest bound. */\nexport function clampProbability(value: unknown): number {\n const number = Math.round(Number(value))\n if (!Number.isFinite(number)) return 0\n return Math.min(100, Math.max(0, number))\n}\n\n/**\n * Add an open stage in front of the closing stages.\n *\n * Won and Lost stay last: a pipeline reads left to right as a progression,\n * and a stage added after \"Won\" would be a step past the end.\n */\nexport function addStage(\n stages: readonly CrmDealStage[],\n name: string,\n probability = 50,\n): CrmDealStage[] {\n const trimmed = String(name ?? '').trim()\n if (!trimmed) return [...stages]\n const sorted = sortedStages({ stages: [...stages] })\n const firstClosed = sorted.findIndex((stage) => stage.kind !== 'open')\n const at = firstClosed === -1 ? sorted.length : firstClosed\n const stage: CrmDealStage = {\n id: newStageId(trimmed, sorted),\n name: trimmed,\n order: at,\n probability: clampProbability(probability),\n kind: 'open',\n }\n sorted.splice(at, 0, stage)\n return renumberStages(sorted)\n}\n\nexport function renameStage(\n stages: readonly CrmDealStage[],\n stageId: string,\n name: string,\n): CrmDealStage[] {\n const trimmed = String(name ?? '').trim()\n return stages.map((stage) =>\n stage.id === stageId && trimmed ? { ...stage, name: trimmed } : stage,\n )\n}\n\n/**\n * Set an OPEN stage's probability. Won is 100 and Lost is 0 by definition —\n * `weightedDealAmountCents` reads the status before the stage, so a Won\n * stage at 60% would change nothing and only mislead the editor.\n */\nexport function setStageProbability(\n stages: readonly CrmDealStage[],\n stageId: string,\n probability: unknown,\n): CrmDealStage[] {\n return stages.map((stage) =>\n stage.id === stageId && stage.kind === 'open'\n ? { ...stage, probability: clampProbability(probability) }\n : stage,\n )\n}\n\n/**\n * Swap an open stage with its open neighbor. The closing stages are not in\n * the reorderable range, so an open stage can never be moved past Won.\n */\nexport function moveStage(\n stages: readonly CrmDealStage[],\n stageId: string,\n direction: 'up' | 'down',\n): CrmDealStage[] {\n const sorted = sortedStages({ stages: [...stages] })\n const index = sorted.findIndex((stage) => stage.id === stageId)\n if (index === -1 || sorted[index].kind !== 'open') return sorted\n const target = direction === 'up' ? index - 1 : index + 1\n if (target < 0 || target >= sorted.length) return sorted\n if (sorted[target].kind !== 'open') return sorted\n const swapped = [...sorted]\n const moving = swapped[index]\n swapped[index] = swapped[target]\n swapped[target] = moving\n return renumberStages(swapped)\n}\n\n/**\n * Why a stage cannot be removed, or `null` when it can.\n *\n * `dealsInStage` is the server's count, asked for before this is called: a\n * stage with deals in it cannot go, because every one of those deals would\n * be left pointing at a stage that no longer exists and worth nothing to the\n * forecast. The closing stages cannot go at all — a deal has to be able to\n * be won and lost.\n */\nexport function stageRemovalRefusal(\n stages: readonly CrmDealStage[],\n stageId: string,\n dealsInStage: number,\n): string | null {\n const stage = stages.find((entry) => entry.id === stageId)\n if (!stage) return 'This stage is no longer in the pipeline.'\n if (stage.kind !== 'open') {\n return `Every pipeline keeps its ${stage.kind === 'won' ? 'Won' : 'Lost'} stage.`\n }\n if (openStages({ stages: [...stages] }).length <= 1) {\n return 'A pipeline needs at least one open stage.'\n }\n if (dealsInStage > 0) {\n return (\n `${dealsInStage.toLocaleString()} ${dealsInStage === 1 ? 'deal is' : 'deals are'} ` +\n 'in this stage. Move them first, so none is left in a stage that no longer exists.'\n )\n }\n return null\n}\n\nexport function removeStage(\n stages: readonly CrmDealStage[],\n stageId: string,\n): CrmDealStage[] {\n return renumberStages(stages.filter((stage) => stage.id !== stageId))\n}\n\n/**\n * What a stored set of stages has to satisfy before it is written back:\n * unique ids, a name on every stage, exactly one Won and one Lost, at least\n * one open stage. The editor refuses a save that fails this rather than\n * writing a pipeline the board cannot draw.\n */\nexport function stagesProblem(stages: readonly CrmDealStage[]): string | null {\n if (!stages.length) return 'A pipeline needs stages.'\n const ids = new Set<string>()\n for (const stage of stages) {\n if (!stage.id) return 'Every stage needs an id.'\n if (ids.has(stage.id)) return `Two stages share the id \"${stage.id}\".`\n ids.add(stage.id)\n if (!String(stage.name ?? '').trim()) return 'Every stage needs a name.'\n }\n const won = stages.filter((stage) => stage.kind === 'won').length\n const lost = stages.filter((stage) => stage.kind === 'lost').length\n if (won !== 1) return 'A pipeline has exactly one Won stage.'\n if (lost !== 1) return 'A pipeline has exactly one Lost stage.'\n if (!stages.some((stage) => stage.kind === 'open')) {\n return 'A pipeline needs at least one open stage.'\n }\n return null\n}\n\n/**\n * The event a stage change emits, and the flat payload a workflow filter\n * reads (AGL-2598). Strings and numbers only — a workflow expression\n * evaluates over scalars, and an absent link is the empty string rather than\n * a missing key so `contactId == \"\"` is a filter somebody can write.\n */\nexport type DealEventName = 'dealStageChanged' | 'dealWon' | 'dealLost'\n\nexport function dealEventName(status: CrmDeal['status']): DealEventName {\n if (status === 'won') return 'dealWon'\n if (status === 'lost') return 'dealLost'\n return 'dealStageChanged'\n}\n\nexport function dealEventPayload(\n dealId: string,\n deal: Pick<\n CrmDeal,\n | 'title'\n | 'amountCents'\n | 'currency'\n | 'stageId'\n | 'ownerUid'\n | 'contactId'\n | 'companyId'\n | 'lostReason'\n >,\n previousStageId: string,\n): Record<string, string | number> {\n return {\n dealId,\n title: String(deal.title ?? ''),\n amountCents: Math.max(0, Math.round(Number(deal.amountCents ?? 0) || 0)),\n currency: String(deal.currency || DEFAULT_DEAL_CURRENCY).toLowerCase(),\n stageId: String(deal.stageId ?? ''),\n previousStageId: String(previousStageId ?? ''),\n ownerUid: String(deal.ownerUid ?? ''),\n contactId: String(deal.contactId ?? ''),\n companyId: String(deal.companyId ?? ''),\n ...(deal.lostReason ? { lostReason: String(deal.lostReason) } : {}),\n }\n}\n\n/** Initials for an avatar: the first letters of the first two words. */\nexport function initialsFor(label: string): string {\n const words = String(label ?? '')\n .trim()\n .split(/[\\s@._-]+/)\n .filter(Boolean)\n const letters = words\n .slice(0, 2)\n .map((word) => word[0])\n .join('')\n return (letters || '?').toUpperCase()\n}\n\n/**\n * A stable hue for one person, so an owner keeps one color across cards and\n * sessions. Hashing the label rather than picking from a palette is what\n * makes the same uid the same color on every machine.\n */\nexport function hueFor(label: string): number {\n let hash = 0\n for (let index = 0; index < label.length; index += 1) {\n hash = (hash * 31 + label.charCodeAt(index)) % 360\n }\n return hash\n}\n\n/** `2026-09-05` for a date input, from epoch ms; `''` for none. */\nexport function dateInputValue(ms: number | null | undefined): string {\n if (typeof ms !== 'number' || !Number.isFinite(ms)) return ''\n const at = new Date(ms)\n const offset = at.getTimezoneOffset() * 60_000\n return new Date(ms - offset).toISOString().slice(0, 10)\n}\n\n/**\n * Epoch ms at local noon of a typed `YYYY-MM-DD`, or `null`.\n *\n * Noon rather than midnight so the day survives a timezone round trip: a\n * close date stored at local midnight and read back one timezone west shows\n * the day before.\n */\nexport function dateInputMs(value: string): number | null {\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(String(value ?? '').trim())\n if (!match) return null\n const at = new Date(\n Number(match[1]),\n Number(match[2]) - 1,\n Number(match[3]),\n 12,\n 0,\n 0,\n 0,\n )\n return Number.isNaN(at.getTime()) ? null : at.getTime()\n}\n"],"names":["DEFAULT_DEAL_STAGES","dealStageById","isPipelineArchived","weightedDealAmountCents","DEFAULT_PIPELINE_ID","DEFAULT_PIPELINE_NAME","PIPELINE_NAME_MAX","activePipelines","pipelines","filter","pipeline","defaultPipelineOf","active","find","isDefault","pipelineNameProblem","name","ownId","trimmed","String","trim","taken","$id","toLowerCase","pipelineArchiveRefusal","openDeals","entry","length","toLocaleString","newPipelineDocument","context","slice","stages","map","stage","archivedAt","visibleTo","hostId","createdByUid","uid","createdAt","Date","nowMs","updatedAt","DEAL_CURRENCIES","DEFAULT_DEAL_CURRENCY","DEAL_STATUS_LABELS","open","won","lost","formatMoney","cents","currency","code","toUpperCase","amount","Number","Intl","NumberFormat","undefined","style","format","toFixed","parseAmountInput","input","cleaned","replace","value","isFinite","Math","round","amountInputValue","timestampMs","getTime","seconds","toMillis","call","daysInStage","deal","since","stageChangedAtMs","max","floor","addAmount","totals","boardSummary","deals","summary","openCount","valueByCurrency","weightedByCurrency","status","amountCents","stageId","formatAmountByCurrency","entries","Object","sort","a","b","join","sortedStages","index","order","openStages","kind","closingStage","newStageId","existing","base","Set","id","has","suffix","renumberStages","clampProbability","number","min","addStage","probability","sorted","firstClosed","findIndex","at","splice","renameStage","setStageProbability","moveStage","direction","target","swapped","moving","stageRemovalRefusal","dealsInStage","removeStage","stagesProblem","ids","add","some","dealEventName","dealEventPayload","dealId","previousStageId","title","ownerUid","contactId","companyId","lostReason","initialsFor","label","words","split","Boolean","letters","word","hueFor","hash","charCodeAt","dateInputValue","ms","offset","getTimezoneOffset","toISOString","dateInputMs","match","exec","isNaN"],"mappings":"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"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import { type DealDoc, type PipelineDoc } from './deal-board-model';
|
|
18
|
+
/** What the drawer edits — strings as the inputs hold them. */
|
|
19
|
+
export interface DealFormValues {
|
|
20
|
+
title: string;
|
|
21
|
+
pipelineId: string;
|
|
22
|
+
stageId: string;
|
|
23
|
+
/** As typed: `1,250.00`; parsed at save. */
|
|
24
|
+
amount: string;
|
|
25
|
+
currency: string;
|
|
26
|
+
/** `YYYY-MM-DD` from a date input, or empty. */
|
|
27
|
+
expectedClose: string;
|
|
28
|
+
ownerUid: string;
|
|
29
|
+
contactId: string;
|
|
30
|
+
contactName: string;
|
|
31
|
+
companyId: string;
|
|
32
|
+
companyName: string;
|
|
33
|
+
notes: string;
|
|
34
|
+
}
|
|
35
|
+
export declare const DEAL_TITLE_MAX = 120;
|
|
36
|
+
export declare const DEAL_NOTES_MAX = 4000;
|
|
37
|
+
/**
|
|
38
|
+
* A blank form aimed at a pipeline's first open stage.
|
|
39
|
+
*
|
|
40
|
+
* `defaults` is how a contact's page or a company's page starts a deal
|
|
41
|
+
* already linked to itself: the caller passes the ids and names it knows and
|
|
42
|
+
* the drawer opens with those fields filled. Anything it does not pass stays
|
|
43
|
+
* blank rather than being guessed.
|
|
44
|
+
*/
|
|
45
|
+
export declare function emptyDealForm(pipeline: Pick<PipelineDoc, '$id' | 'stages'> | null | undefined, defaults?: Partial<DealFormValues>): DealFormValues;
|
|
46
|
+
/** The form an existing deal opens as. */
|
|
47
|
+
export declare function dealFormFromDoc(deal: DealDoc): DealFormValues;
|
|
48
|
+
/**
|
|
49
|
+
* Why the form cannot be saved, or `null` when it can.
|
|
50
|
+
*
|
|
51
|
+
* A title is the one thing a deal needs — a card with no title is a card
|
|
52
|
+
* nobody can find on the board. The amount is optional but, if typed, has to
|
|
53
|
+
* parse: a deal saved with an amount the merchant typed and the model dropped
|
|
54
|
+
* would forecast at zero without saying so. The stage is only checked on a
|
|
55
|
+
* new deal, because an existing deal's stage is moved by the stage route and
|
|
56
|
+
* is not this form's to write.
|
|
57
|
+
*/
|
|
58
|
+
export declare function dealFormProblem(values: DealFormValues, mode: 'create' | 'edit'): string | null;
|
|
59
|
+
/** What every deal write stamps besides the fields the form edits. */
|
|
60
|
+
export interface DealWriteContext {
|
|
61
|
+
visibleTo: string[];
|
|
62
|
+
hostId: string;
|
|
63
|
+
uid: string;
|
|
64
|
+
nowMs: number;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The document a new deal is created as. Open, in the chosen stage, with
|
|
68
|
+
* the stage clock started now so "days in stage" counts from creation.
|
|
69
|
+
*/
|
|
70
|
+
export declare function dealDocumentFromForm(values: DealFormValues, context: DealWriteContext): Record<string, unknown>;
|
|
71
|
+
/**
|
|
72
|
+
* The patch an edit writes. Never the stage, the status or the scope: the
|
|
73
|
+
* stage goes through the server route so the automations hear it, and the
|
|
74
|
+
* scope is changed only by an org-wide member on purpose.
|
|
75
|
+
*
|
|
76
|
+
* Nor the amount or the currency of a deal whose amount is DERIVED
|
|
77
|
+
* (`amountDerived`, AGL-2620): with line items on the deal the amount is
|
|
78
|
+
* their sum and the currency is theirs, both owned by the products card. The
|
|
79
|
+
* form shows them read-only, and a patch that re-wrote the number it was
|
|
80
|
+
* seeded with would overwrite a line added from another tab meanwhile.
|
|
81
|
+
*/
|
|
82
|
+
export declare function dealPatchFromForm(values: DealFormValues, nowMs: number, options?: {
|
|
83
|
+
amountDerived?: boolean;
|
|
84
|
+
}): {
|
|
85
|
+
set: Record<string, unknown>;
|
|
86
|
+
clear: string[];
|
|
87
|
+
};
|
|
88
|
+
/** A contact as the picker offers it. */
|
|
89
|
+
export interface ContactChoice {
|
|
90
|
+
id: string;
|
|
91
|
+
name: string;
|
|
92
|
+
email: string;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The contacts a typed query matches, from a window of recent contact rows,
|
|
96
|
+
* in the list's own grammar.
|
|
97
|
+
*
|
|
98
|
+
* In memory rather than by query because the contacts collection has no
|
|
99
|
+
* composite index over `visibleTo` with `nameLower` or `email`, and a
|
|
100
|
+
* scoped list can carry only one array clause — so the picker reads the
|
|
101
|
+
* newest rows the viewer may see (bounded, ordered) and matches here. The
|
|
102
|
+
* match is what the list's search does: the normalized query as a prefix of
|
|
103
|
+
* the email, or as a prefix of any word of the name (`nameTokens` holds
|
|
104
|
+
* every word prefix a write produced; `nameLower` covers rows written before
|
|
105
|
+
* the tokens existed). The holder's own name for the person wins over the
|
|
106
|
+
* canonical one, as everywhere else.
|
|
107
|
+
*/
|
|
108
|
+
export declare function contactChoicesFor(queryText: string, rows: readonly Record<string, unknown>[],
|
|
109
|
+
/**
|
|
110
|
+
* The holder each row is named through: one group for every row under a
|
|
111
|
+
* site, or a resolver per row at the organization level (AGL-2630), where
|
|
112
|
+
* each contact reads through its own primary holder.
|
|
113
|
+
*/
|
|
114
|
+
groupId: string | ((row: Record<string, unknown>) => string), max?: number): ContactChoice[];
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2026 Aglyn LLC
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/ /**
|
|
18
|
+
* The deal drawer's values and the documents they become (AGL-2598), as pure
|
|
19
|
+
* functions — the drawer holds strings the way inputs do, and this module is
|
|
20
|
+
* the one place those strings turn into the typed fields a `CrmDeal` stores.
|
|
21
|
+
* Kept out of the component so the translation can be tested without a
|
|
22
|
+
* Firestore or a DOM, and so the contact card and the company card, which
|
|
23
|
+
* open the same drawer with a link preselected, build a deal the same way.
|
|
24
|
+
*/ import { contactDisplayName, nameSearchKey, nameSearchToken } from "@aglyn/aglyn";
|
|
25
|
+
import { amountInputValue, dateInputMs, dateInputValue, DEFAULT_DEAL_CURRENCY, openStages, parseAmountInput } from "./deal-board-model.js";
|
|
26
|
+
export const DEAL_TITLE_MAX = 120;
|
|
27
|
+
export const DEAL_NOTES_MAX = 4000;
|
|
28
|
+
/**
|
|
29
|
+
* A blank form aimed at a pipeline's first open stage.
|
|
30
|
+
*
|
|
31
|
+
* `defaults` is how a contact's page or a company's page starts a deal
|
|
32
|
+
* already linked to itself: the caller passes the ids and names it knows and
|
|
33
|
+
* the drawer opens with those fields filled. Anything it does not pass stays
|
|
34
|
+
* blank rather than being guessed.
|
|
35
|
+
*/ export function emptyDealForm(pipeline, defaults = {}) {
|
|
36
|
+
var _ref, _ref1;
|
|
37
|
+
const firstOpen = openStages(pipeline)[0];
|
|
38
|
+
return _extends({
|
|
39
|
+
title: '',
|
|
40
|
+
pipelineId: (_ref = pipeline == null ? void 0 : pipeline.$id) != null ? _ref : '',
|
|
41
|
+
stageId: (_ref1 = firstOpen == null ? void 0 : firstOpen.id) != null ? _ref1 : '',
|
|
42
|
+
amount: '',
|
|
43
|
+
currency: DEFAULT_DEAL_CURRENCY,
|
|
44
|
+
expectedClose: '',
|
|
45
|
+
ownerUid: '',
|
|
46
|
+
contactId: '',
|
|
47
|
+
contactName: '',
|
|
48
|
+
companyId: '',
|
|
49
|
+
companyName: '',
|
|
50
|
+
notes: ''
|
|
51
|
+
}, defaults);
|
|
52
|
+
}
|
|
53
|
+
/** The form an existing deal opens as. */ export function dealFormFromDoc(deal) {
|
|
54
|
+
var _deal_title, _deal_pipelineId, _deal_stageId, _deal_ownerUid, _deal_contactId, _deal_contactName, _deal_companyId, _deal_companyName, _deal_notes;
|
|
55
|
+
return {
|
|
56
|
+
title: String((_deal_title = deal.title) != null ? _deal_title : ''),
|
|
57
|
+
pipelineId: String((_deal_pipelineId = deal.pipelineId) != null ? _deal_pipelineId : ''),
|
|
58
|
+
stageId: String((_deal_stageId = deal.stageId) != null ? _deal_stageId : ''),
|
|
59
|
+
amount: amountInputValue(deal.amountCents),
|
|
60
|
+
currency: String(deal.currency || DEFAULT_DEAL_CURRENCY).toLowerCase(),
|
|
61
|
+
expectedClose: dateInputValue(deal.expectedCloseAtMs),
|
|
62
|
+
ownerUid: String((_deal_ownerUid = deal.ownerUid) != null ? _deal_ownerUid : ''),
|
|
63
|
+
contactId: String((_deal_contactId = deal.contactId) != null ? _deal_contactId : ''),
|
|
64
|
+
contactName: String((_deal_contactName = deal.contactName) != null ? _deal_contactName : ''),
|
|
65
|
+
companyId: String((_deal_companyId = deal.companyId) != null ? _deal_companyId : ''),
|
|
66
|
+
companyName: String((_deal_companyName = deal.companyName) != null ? _deal_companyName : ''),
|
|
67
|
+
notes: String((_deal_notes = deal.notes) != null ? _deal_notes : '')
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Why the form cannot be saved, or `null` when it can.
|
|
72
|
+
*
|
|
73
|
+
* A title is the one thing a deal needs — a card with no title is a card
|
|
74
|
+
* nobody can find on the board. The amount is optional but, if typed, has to
|
|
75
|
+
* parse: a deal saved with an amount the merchant typed and the model dropped
|
|
76
|
+
* would forecast at zero without saying so. The stage is only checked on a
|
|
77
|
+
* new deal, because an existing deal's stage is moved by the stage route and
|
|
78
|
+
* is not this form's to write.
|
|
79
|
+
*/ export function dealFormProblem(values, mode) {
|
|
80
|
+
if (!values.title.trim()) return 'A deal needs a title.';
|
|
81
|
+
if (values.amount.trim() && parseAmountInput(values.amount) === null) {
|
|
82
|
+
return 'The amount has to be a number of zero or more.';
|
|
83
|
+
}
|
|
84
|
+
if (values.expectedClose.trim() && dateInputMs(values.expectedClose) === null) {
|
|
85
|
+
return 'The expected close has to be a date.';
|
|
86
|
+
}
|
|
87
|
+
if (mode === 'create' && (!values.pipelineId || !values.stageId)) {
|
|
88
|
+
return 'Pick the stage the deal starts in.';
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The optional fields, as the values to SET and the keys to CLEAR.
|
|
94
|
+
*
|
|
95
|
+
* Split rather than written as `null` because a deal's optional ids are
|
|
96
|
+
* typed as absent-or-string, and the indexes on `contactId` and `companyId`
|
|
97
|
+
* are what the contact and company pages query — a `null` there would be a
|
|
98
|
+
* row those pages cannot see and a filter on "no contact" could not find
|
|
99
|
+
* either. On a create the cleared keys are simply not written; on an edit
|
|
100
|
+
* the component turns them into field deletes.
|
|
101
|
+
*/ function optionalFields(values) {
|
|
102
|
+
const set = {};
|
|
103
|
+
const clear = [];
|
|
104
|
+
const put = (key, value)=>{
|
|
105
|
+
if (value === null || value === '') clear.push(key);
|
|
106
|
+
else set[key] = value;
|
|
107
|
+
};
|
|
108
|
+
put('amountCents', parseAmountInput(values.amount));
|
|
109
|
+
put('expectedCloseAtMs', dateInputMs(values.expectedClose));
|
|
110
|
+
put('ownerUid', values.ownerUid.trim());
|
|
111
|
+
put('contactId', values.contactId.trim());
|
|
112
|
+
put('contactName', values.contactId.trim() ? values.contactName.trim() : '');
|
|
113
|
+
put('companyId', values.companyId.trim());
|
|
114
|
+
put('companyName', values.companyId.trim() ? values.companyName.trim() : '');
|
|
115
|
+
put('notes', values.notes.trim().slice(0, DEAL_NOTES_MAX));
|
|
116
|
+
return {
|
|
117
|
+
set,
|
|
118
|
+
clear
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* The document a new deal is created as. Open, in the chosen stage, with
|
|
123
|
+
* the stage clock started now so "days in stage" counts from creation.
|
|
124
|
+
*/ export function dealDocumentFromForm(values, context) {
|
|
125
|
+
const title = values.title.trim().slice(0, DEAL_TITLE_MAX);
|
|
126
|
+
const { set } = optionalFields(values);
|
|
127
|
+
return _extends({
|
|
128
|
+
title,
|
|
129
|
+
titleLower: nameSearchKey(title),
|
|
130
|
+
pipelineId: values.pipelineId,
|
|
131
|
+
stageId: values.stageId,
|
|
132
|
+
status: 'open',
|
|
133
|
+
currency: String(values.currency || DEFAULT_DEAL_CURRENCY).toLowerCase(),
|
|
134
|
+
stageChangedAtMs: context.nowMs
|
|
135
|
+
}, set, {
|
|
136
|
+
visibleTo: context.visibleTo,
|
|
137
|
+
hostId: context.hostId,
|
|
138
|
+
createdByUid: context.uid,
|
|
139
|
+
createdAt: new Date(context.nowMs),
|
|
140
|
+
updatedAt: new Date(context.nowMs)
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* The patch an edit writes. Never the stage, the status or the scope: the
|
|
145
|
+
* stage goes through the server route so the automations hear it, and the
|
|
146
|
+
* scope is changed only by an org-wide member on purpose.
|
|
147
|
+
*
|
|
148
|
+
* Nor the amount or the currency of a deal whose amount is DERIVED
|
|
149
|
+
* (`amountDerived`, AGL-2620): with line items on the deal the amount is
|
|
150
|
+
* their sum and the currency is theirs, both owned by the products card. The
|
|
151
|
+
* form shows them read-only, and a patch that re-wrote the number it was
|
|
152
|
+
* seeded with would overwrite a line added from another tab meanwhile.
|
|
153
|
+
*/ export function dealPatchFromForm(values, nowMs, options = {}) {
|
|
154
|
+
const title = values.title.trim().slice(0, DEAL_TITLE_MAX);
|
|
155
|
+
const { set, clear } = optionalFields(values);
|
|
156
|
+
if (options.amountDerived) {
|
|
157
|
+
delete set['amountCents'];
|
|
158
|
+
return {
|
|
159
|
+
set: _extends({
|
|
160
|
+
title,
|
|
161
|
+
titleLower: nameSearchKey(title)
|
|
162
|
+
}, set, {
|
|
163
|
+
updatedAt: new Date(nowMs)
|
|
164
|
+
}),
|
|
165
|
+
clear: clear.filter((key)=>key !== 'amountCents')
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
set: _extends({
|
|
170
|
+
title,
|
|
171
|
+
titleLower: nameSearchKey(title),
|
|
172
|
+
currency: String(values.currency || DEFAULT_DEAL_CURRENCY).toLowerCase()
|
|
173
|
+
}, set, {
|
|
174
|
+
updatedAt: new Date(nowMs)
|
|
175
|
+
}),
|
|
176
|
+
clear
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* The contacts a typed query matches, from a window of recent contact rows,
|
|
181
|
+
* in the list's own grammar.
|
|
182
|
+
*
|
|
183
|
+
* In memory rather than by query because the contacts collection has no
|
|
184
|
+
* composite index over `visibleTo` with `nameLower` or `email`, and a
|
|
185
|
+
* scoped list can carry only one array clause — so the picker reads the
|
|
186
|
+
* newest rows the viewer may see (bounded, ordered) and matches here. The
|
|
187
|
+
* match is what the list's search does: the normalized query as a prefix of
|
|
188
|
+
* the email, or as a prefix of any word of the name (`nameTokens` holds
|
|
189
|
+
* every word prefix a write produced; `nameLower` covers rows written before
|
|
190
|
+
* the tokens existed). The holder's own name for the person wins over the
|
|
191
|
+
* canonical one, as everywhere else.
|
|
192
|
+
*/ export function contactChoicesFor(queryText, rows, /**
|
|
193
|
+
* The holder each row is named through: one group for every row under a
|
|
194
|
+
* site, or a resolver per row at the organization level (AGL-2630), where
|
|
195
|
+
* each contact reads through its own primary holder.
|
|
196
|
+
*/ groupId, max = 8) {
|
|
197
|
+
const key = nameSearchKey(queryText);
|
|
198
|
+
const token = nameSearchToken(queryText);
|
|
199
|
+
const choices = [];
|
|
200
|
+
for (const row of rows){
|
|
201
|
+
var _row_email, _row_$id;
|
|
202
|
+
const email = String((_row_email = row['email']) != null ? _row_email : '').toLowerCase();
|
|
203
|
+
const name = contactDisplayName(row, typeof groupId === 'function' ? groupId(row) : groupId);
|
|
204
|
+
const nameLower = nameSearchKey(name);
|
|
205
|
+
const tokens = Array.isArray(row['nameTokens']) ? row['nameTokens'].map(String) : [];
|
|
206
|
+
const matches = !key || email.startsWith(key) || nameLower.startsWith(key) || nameLower.includes(` ${key}`) || (token ? tokens.includes(token) : false);
|
|
207
|
+
if (!matches) continue;
|
|
208
|
+
choices.push({
|
|
209
|
+
id: String((_row_$id = row['$id']) != null ? _row_$id : ''),
|
|
210
|
+
name,
|
|
211
|
+
email
|
|
212
|
+
});
|
|
213
|
+
if (choices.length >= max) break;
|
|
214
|
+
}
|
|
215
|
+
return choices;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
//# sourceMappingURL=deal-form-model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/model/deal-form-model.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 * The deal drawer's values and the documents they become (AGL-2598), as pure\n * functions — the drawer holds strings the way inputs do, and this module is\n * the one place those strings turn into the typed fields a `CrmDeal` stores.\n * Kept out of the component so the translation can be tested without a\n * Firestore or a DOM, and so the contact card and the company card, which\n * open the same drawer with a link preselected, build a deal the same way.\n */\n\nimport {\n contactDisplayName,\n type CrmDeal,\n nameSearchKey,\n nameSearchToken,\n} from '@aglyn/aglyn'\nimport {\n amountInputValue,\n dateInputMs,\n dateInputValue,\n DEFAULT_DEAL_CURRENCY,\n type DealDoc,\n openStages,\n parseAmountInput,\n type PipelineDoc,\n} from './deal-board-model'\n\n/** What the drawer edits — strings as the inputs hold them. */\nexport interface DealFormValues {\n title: string\n pipelineId: string\n stageId: string\n /** As typed: `1,250.00`; parsed at save. */\n amount: string\n currency: string\n /** `YYYY-MM-DD` from a date input, or empty. */\n expectedClose: string\n ownerUid: string\n contactId: string\n contactName: string\n companyId: string\n companyName: string\n notes: string\n}\n\nexport const DEAL_TITLE_MAX = 120\nexport const DEAL_NOTES_MAX = 4000\n\n/**\n * A blank form aimed at a pipeline's first open stage.\n *\n * `defaults` is how a contact's page or a company's page starts a deal\n * already linked to itself: the caller passes the ids and names it knows and\n * the drawer opens with those fields filled. Anything it does not pass stays\n * blank rather than being guessed.\n */\nexport function emptyDealForm(\n pipeline: Pick<PipelineDoc, '$id' | 'stages'> | null | undefined,\n defaults: Partial<DealFormValues> = {},\n): DealFormValues {\n const firstOpen = openStages(pipeline)[0]\n return {\n title: '',\n pipelineId: pipeline?.$id ?? '',\n stageId: firstOpen?.id ?? '',\n amount: '',\n currency: DEFAULT_DEAL_CURRENCY,\n expectedClose: '',\n ownerUid: '',\n contactId: '',\n contactName: '',\n companyId: '',\n companyName: '',\n notes: '',\n ...defaults,\n }\n}\n\n/** The form an existing deal opens as. */\nexport function dealFormFromDoc(deal: DealDoc): DealFormValues {\n return {\n title: String(deal.title ?? ''),\n pipelineId: String(deal.pipelineId ?? ''),\n stageId: String(deal.stageId ?? ''),\n amount: amountInputValue(deal.amountCents),\n currency: String(deal.currency || DEFAULT_DEAL_CURRENCY).toLowerCase(),\n expectedClose: dateInputValue(deal.expectedCloseAtMs),\n ownerUid: String(deal.ownerUid ?? ''),\n contactId: String(deal.contactId ?? ''),\n contactName: String(deal.contactName ?? ''),\n companyId: String(deal.companyId ?? ''),\n companyName: String(deal.companyName ?? ''),\n notes: String(deal.notes ?? ''),\n }\n}\n\n/**\n * Why the form cannot be saved, or `null` when it can.\n *\n * A title is the one thing a deal needs — a card with no title is a card\n * nobody can find on the board. The amount is optional but, if typed, has to\n * parse: a deal saved with an amount the merchant typed and the model dropped\n * would forecast at zero without saying so. The stage is only checked on a\n * new deal, because an existing deal's stage is moved by the stage route and\n * is not this form's to write.\n */\nexport function dealFormProblem(\n values: DealFormValues,\n mode: 'create' | 'edit',\n): string | null {\n if (!values.title.trim()) return 'A deal needs a title.'\n if (values.amount.trim() && parseAmountInput(values.amount) === null) {\n return 'The amount has to be a number of zero or more.'\n }\n if (values.expectedClose.trim() && dateInputMs(values.expectedClose) === null) {\n return 'The expected close has to be a date.'\n }\n if (mode === 'create' && (!values.pipelineId || !values.stageId)) {\n return 'Pick the stage the deal starts in.'\n }\n return null\n}\n\n/** What every deal write stamps besides the fields the form edits. */\nexport interface DealWriteContext {\n visibleTo: string[]\n hostId: string\n uid: string\n nowMs: number\n}\n\n/**\n * The optional fields, as the values to SET and the keys to CLEAR.\n *\n * Split rather than written as `null` because a deal's optional ids are\n * typed as absent-or-string, and the indexes on `contactId` and `companyId`\n * are what the contact and company pages query — a `null` there would be a\n * row those pages cannot see and a filter on \"no contact\" could not find\n * either. On a create the cleared keys are simply not written; on an edit\n * the component turns them into field deletes.\n */\nfunction optionalFields(values: DealFormValues): {\n set: Record<string, string | number>\n clear: string[]\n} {\n const set: Record<string, string | number> = {}\n const clear: string[] = []\n const put = (key: string, value: string | number | null) => {\n if (value === null || value === '') clear.push(key)\n else set[key] = value\n }\n put('amountCents', parseAmountInput(values.amount))\n put('expectedCloseAtMs', dateInputMs(values.expectedClose))\n put('ownerUid', values.ownerUid.trim())\n put('contactId', values.contactId.trim())\n put('contactName', values.contactId.trim() ? values.contactName.trim() : '')\n put('companyId', values.companyId.trim())\n put('companyName', values.companyId.trim() ? values.companyName.trim() : '')\n put('notes', values.notes.trim().slice(0, DEAL_NOTES_MAX))\n return { set, clear }\n}\n\n/**\n * The document a new deal is created as. Open, in the chosen stage, with\n * the stage clock started now so \"days in stage\" counts from creation.\n */\nexport function dealDocumentFromForm(\n values: DealFormValues,\n context: DealWriteContext,\n): Record<string, unknown> {\n const title = values.title.trim().slice(0, DEAL_TITLE_MAX)\n const { set } = optionalFields(values)\n return {\n title,\n titleLower: nameSearchKey(title),\n pipelineId: values.pipelineId,\n stageId: values.stageId,\n status: 'open' satisfies CrmDeal['status'],\n currency: String(values.currency || DEFAULT_DEAL_CURRENCY).toLowerCase(),\n stageChangedAtMs: context.nowMs,\n ...set,\n visibleTo: context.visibleTo,\n hostId: context.hostId,\n createdByUid: context.uid,\n createdAt: new Date(context.nowMs),\n updatedAt: new Date(context.nowMs),\n }\n}\n\n/**\n * The patch an edit writes. Never the stage, the status or the scope: the\n * stage goes through the server route so the automations hear it, and the\n * scope is changed only by an org-wide member on purpose.\n *\n * Nor the amount or the currency of a deal whose amount is DERIVED\n * (`amountDerived`, AGL-2620): with line items on the deal the amount is\n * their sum and the currency is theirs, both owned by the products card. The\n * form shows them read-only, and a patch that re-wrote the number it was\n * seeded with would overwrite a line added from another tab meanwhile.\n */\nexport function dealPatchFromForm(\n values: DealFormValues,\n nowMs: number,\n options: { amountDerived?: boolean } = {},\n): { set: Record<string, unknown>; clear: string[] } {\n const title = values.title.trim().slice(0, DEAL_TITLE_MAX)\n const { set, clear } = optionalFields(values)\n if (options.amountDerived) {\n delete set['amountCents']\n return {\n set: { title, titleLower: nameSearchKey(title), ...set, updatedAt: new Date(nowMs) },\n clear: clear.filter((key) => key !== 'amountCents'),\n }\n }\n return {\n set: {\n title,\n titleLower: nameSearchKey(title),\n currency: String(values.currency || DEFAULT_DEAL_CURRENCY).toLowerCase(),\n ...set,\n updatedAt: new Date(nowMs),\n },\n clear,\n }\n}\n\n/** A contact as the picker offers it. */\nexport interface ContactChoice {\n id: string\n name: string\n email: string\n}\n\n/**\n * The contacts a typed query matches, from a window of recent contact rows,\n * in the list's own grammar.\n *\n * In memory rather than by query because the contacts collection has no\n * composite index over `visibleTo` with `nameLower` or `email`, and a\n * scoped list can carry only one array clause — so the picker reads the\n * newest rows the viewer may see (bounded, ordered) and matches here. The\n * match is what the list's search does: the normalized query as a prefix of\n * the email, or as a prefix of any word of the name (`nameTokens` holds\n * every word prefix a write produced; `nameLower` covers rows written before\n * the tokens existed). The holder's own name for the person wins over the\n * canonical one, as everywhere else.\n */\nexport function contactChoicesFor(\n queryText: string,\n rows: readonly Record<string, unknown>[],\n /**\n * The holder each row is named through: one group for every row under a\n * site, or a resolver per row at the organization level (AGL-2630), where\n * each contact reads through its own primary holder.\n */\n groupId: string | ((row: Record<string, unknown>) => string),\n max = 8,\n): ContactChoice[] {\n const key = nameSearchKey(queryText)\n const token = nameSearchToken(queryText)\n const choices: ContactChoice[] = []\n for (const row of rows) {\n const email = String(row['email'] ?? '').toLowerCase()\n const name = contactDisplayName(\n row,\n typeof groupId === 'function' ? groupId(row) : groupId,\n )\n const nameLower = nameSearchKey(name)\n const tokens = Array.isArray(row['nameTokens'])\n ? (row['nameTokens'] as unknown[]).map(String)\n : []\n const matches =\n !key ||\n email.startsWith(key) ||\n nameLower.startsWith(key) ||\n nameLower.includes(` ${key}`) ||\n (token ? tokens.includes(token) : false)\n if (!matches) continue\n choices.push({ id: String(row['$id'] ?? ''), name, email })\n if (choices.length >= max) break\n }\n return choices\n}\n"],"names":["contactDisplayName","nameSearchKey","nameSearchToken","amountInputValue","dateInputMs","dateInputValue","DEFAULT_DEAL_CURRENCY","openStages","parseAmountInput","DEAL_TITLE_MAX","DEAL_NOTES_MAX","emptyDealForm","pipeline","defaults","firstOpen","title","pipelineId","$id","stageId","id","amount","currency","expectedClose","ownerUid","contactId","contactName","companyId","companyName","notes","dealFormFromDoc","deal","String","amountCents","toLowerCase","expectedCloseAtMs","dealFormProblem","values","mode","trim","optionalFields","set","clear","put","key","value","push","slice","dealDocumentFromForm","context","titleLower","status","stageChangedAtMs","nowMs","visibleTo","hostId","createdByUid","uid","createdAt","Date","updatedAt","dealPatchFromForm","options","amountDerived","filter","contactChoicesFor","queryText","rows","groupId","max","token","choices","row","email","name","nameLower","tokens","Array","isArray","map","matches","startsWith","includes","length"],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* The deals CSV — one file from the table's Export button and the bulk
|
|
19
|
+
* bar's (AGL-2621).
|
|
20
|
+
*
|
|
21
|
+
* A deal names its stage, its owner, its contact and its company by id, and
|
|
22
|
+
* an id is nothing a spreadsheet can read: the stage and the pipeline are
|
|
23
|
+
* written by NAME, resolved through the caller's pipelines, the owner by
|
|
24
|
+
* address, and the contact and the company by the names the deal already
|
|
25
|
+
* carries as captions. The amount is written in major units (`1250.00`),
|
|
26
|
+
* with the currency beside it, because a column of cents is a column
|
|
27
|
+
* somebody will sum wrongly.
|
|
28
|
+
*
|
|
29
|
+
* There is no deals import, so the header is free to read well; it stays
|
|
30
|
+
* in the table's column order so the file and the screen agree.
|
|
31
|
+
*/
|
|
32
|
+
export { csvAmount, csvInstant, DEAL_CSV_COLUMNS, dealsCsv, type DealCsvOptions, type DealCsvRow, } from '@aglyn/aglyn';
|
|
@@ -0,0 +1,37 @@
|
|
|
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
|
+
* The deals CSV — one file from the table's Export button and the bulk
|
|
18
|
+
* bar's (AGL-2621).
|
|
19
|
+
*
|
|
20
|
+
* A deal names its stage, its owner, its contact and its company by id, and
|
|
21
|
+
* an id is nothing a spreadsheet can read: the stage and the pipeline are
|
|
22
|
+
* written by NAME, resolved through the caller's pipelines, the owner by
|
|
23
|
+
* address, and the contact and the company by the names the deal already
|
|
24
|
+
* carries as captions. The amount is written in major units (`1250.00`),
|
|
25
|
+
* with the currency beside it, because a column of cents is a column
|
|
26
|
+
* somebody will sum wrongly.
|
|
27
|
+
*
|
|
28
|
+
* There is no deals import, so the header is free to read well; it stays
|
|
29
|
+
* in the table's column order so the file and the screen agree.
|
|
30
|
+
*/ /*
|
|
31
|
+
* The columns and the cells moved to `@aglyn/aglyn` under AGL-2662 so the
|
|
32
|
+
* server's whole-collection export can write the same file: the console
|
|
33
|
+
* app may not import a feature plugin, and one feature must not have two
|
|
34
|
+
* file formats. This module keeps its name and its callers.
|
|
35
|
+
*/ export { csvAmount, csvInstant, DEAL_CSV_COLUMNS, dealsCsv } from "@aglyn/aglyn";
|
|
36
|
+
|
|
37
|
+
//# sourceMappingURL=deals-csv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/model/deals-csv.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 * The deals CSV — one file from the table's Export button and the bulk\n * bar's (AGL-2621).\n *\n * A deal names its stage, its owner, its contact and its company by id, and\n * an id is nothing a spreadsheet can read: the stage and the pipeline are\n * written by NAME, resolved through the caller's pipelines, the owner by\n * address, and the contact and the company by the names the deal already\n * carries as captions. The amount is written in major units (`1250.00`),\n * with the currency beside it, because a column of cents is a column\n * somebody will sum wrongly.\n *\n * There is no deals import, so the header is free to read well; it stays\n * in the table's column order so the file and the screen agree.\n */\n\n/*\n * The columns and the cells moved to `@aglyn/aglyn` under AGL-2662 so the\n * server's whole-collection export can write the same file: the console\n * app may not import a feature plugin, and one feature must not have two\n * file formats. This module keeps its name and its callers.\n */\nexport {\n csvAmount,\n csvInstant,\n DEAL_CSV_COLUMNS,\n dealsCsv,\n type DealCsvOptions,\n type DealCsvRow,\n} from '@aglyn/aglyn'\n"],"names":["csvAmount","csvInstant","DEAL_CSV_COLUMNS","dealsCsv"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;CAcC,GAED;;;;;CAKC,GACD,SACEA,SAAS,EACTC,UAAU,EACVC,gBAAgB,EAChBC,QAAQ,QAGH,eAAc"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import type { ConsoleImportColumn, ConsoleImportColumnShape } from '@aglyn/aglyn/plugin-manager/record-zone-props';
|
|
18
|
+
/**
|
|
19
|
+
* WHAT A FILE'S COLUMN HOLDS, WITHOUT SAYING WHAT IS IN IT (AGL-2917).
|
|
20
|
+
*
|
|
21
|
+
* The import drawer's `importMapping` zone tells a widget each column's header
|
|
22
|
+
* and one word for its values — an email address, a phone number, a date —
|
|
23
|
+
* and never a cell. The word is read here, in the browser, from the rows the
|
|
24
|
+
* drawer already parsed, so a widget that asks a model to match the columns
|
|
25
|
+
* has nothing of the file's people to pass on.
|
|
26
|
+
*/
|
|
27
|
+
/** Non-empty cells a column's shape is read from. */
|
|
28
|
+
export declare const IMPORT_SHAPE_SAMPLE = 50;
|
|
29
|
+
/** Rows a column's cells are looked for in; past these a file says nothing new about its shape. */
|
|
30
|
+
export declare const IMPORT_SHAPE_ROWS = 500;
|
|
31
|
+
/** The share of those cells that must agree for a column to take a shape. */
|
|
32
|
+
export declare const IMPORT_SHAPE_AGREEMENT = 0.8;
|
|
33
|
+
/** One column's shape, from its cells: the first shape most of them agree on, else `text`. */
|
|
34
|
+
export declare function importColumnShape(cells: readonly unknown[]): ConsoleImportColumnShape;
|
|
35
|
+
/** Every column of a parsed file: its header, and the shape of what the rows under it hold. */
|
|
36
|
+
export declare function importColumns(headers: readonly string[], rows: ReadonlyArray<readonly unknown[]>): ConsoleImportColumn[];
|