@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,442 @@
|
|
|
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 RECIPES, INSTALLED FROM THE ORGANIZATION (AGL-2639).
|
|
19
|
+
*
|
|
20
|
+
* On a site, a recipe opens the Actions editor prefilled and writes nothing
|
|
21
|
+
* until the person saves — the editor is the writer, and the site's own
|
|
22
|
+
* role is the authority. The organization-level CRM hub has no editor: it
|
|
23
|
+
* mounts over every site with no site of its own, and an org running five
|
|
24
|
+
* sites installed "Welcome a new lead" five times by hand. These two routes
|
|
25
|
+
* are the org hub's door to a site's automations.
|
|
26
|
+
*
|
|
27
|
+
* `crm/recipe-install` writes the action a recipe builds into ONE site's
|
|
28
|
+
* `hosts/{hostId}/actions`, as the site editor would have saved it — the
|
|
29
|
+
* same builder, the same validator, the same stored shape — and refuses a
|
|
30
|
+
* second install of the same recipe on the same site by the stamp the
|
|
31
|
+
* builder puts on it. A server route rather than a client write for three
|
|
32
|
+
* reasons the browser cannot supply: the document is created by a server
|
|
33
|
+
* on every plan (the rules close `actions` to client creates, and the
|
|
34
|
+
* per-site cap is counted here); the stamp is the server's word, not the
|
|
35
|
+
* caller's; and the site is checked to be one the caller may manage —
|
|
36
|
+
* at the org level, a site OF THE ORG the caller holds org-wide reach in,
|
|
37
|
+
* so a host id typed into the body cannot reach a site under another
|
|
38
|
+
* workspace.
|
|
39
|
+
*
|
|
40
|
+
* `crm/recipe-status` reads the stamps back for every site of the org, so
|
|
41
|
+
* the card can say which sites carry which recipe. An action with no stamp
|
|
42
|
+
* at all — saved before the stamp existed — is counted, not ignored: it
|
|
43
|
+
* may well be a recipe, and the reader is told the site "may already have
|
|
44
|
+
* it" rather than shown a clean slate.
|
|
45
|
+
*
|
|
46
|
+
* Both routes serve the site variant too (`hostId` alone, authorized by the
|
|
47
|
+
* host role that creates actions today), so a site-level caller gets the
|
|
48
|
+
* same answers; the org variant (`orgId` in the body) is the one the org
|
|
49
|
+
* hub uses.
|
|
50
|
+
*/ import { ACTIONS_MAX_PER_HOST, checkEntitlement, createResourceUid, crmActionRecipe, hostActionDocument, hostActionRecipeId, hostRoleCanWrite, planLabelGrantingFeature, validateHostAction } from "@aglyn/aglyn/server";
|
|
51
|
+
import { firebaseAdmin, getOrgForHost, logHostActivity, logOrgActivity } from "@aglyn/tenant-data-admin";
|
|
52
|
+
import { isRefusedIdToken } from "@aglyn/tenant-data-admin/server/id-token-refusal";
|
|
53
|
+
import { FieldValue } from "firebase-admin/firestore";
|
|
54
|
+
import { CRM_API_ROUTES } from "../constants/api-routes.js";
|
|
55
|
+
import { authorizeOrgCaller, orgHostIds, readCrmRouteScope } from "./org-caller.js";
|
|
56
|
+
import { crmSuiteRefusal } from "./suite-gate.js";
|
|
57
|
+
export const CRM_RECIPE_INSTALL_ROUTE = CRM_API_ROUTES.recipeInstall;
|
|
58
|
+
export const CRM_RECIPE_STATUS_ROUTE = CRM_API_ROUTES.recipeStatus;
|
|
59
|
+
const INSTALL_REFUSAL = 'Installing a recipe requires the data permission across the whole workspace';
|
|
60
|
+
const STATUS_REFUSAL = 'Reading which sites carry a recipe requires the data permission across the whole workspace';
|
|
61
|
+
function typed(value, max) {
|
|
62
|
+
return String(value != null ? value : '').trim().slice(0, max);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The site variant's authority: the role on the host document that creates
|
|
66
|
+
* an action through `/api/hosts/resources` today. Staff pass, as they pass
|
|
67
|
+
* that route. Answers the host snapshot it read, so the caller reads it
|
|
68
|
+
* once.
|
|
69
|
+
*/ async function authorizeSiteWriter(req, hostId) {
|
|
70
|
+
var _req_headers_authorization, _host_get, _decoded_email;
|
|
71
|
+
const authorization = String((_req_headers_authorization = req.headers.authorization) != null ? _req_headers_authorization : '');
|
|
72
|
+
const idToken = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : undefined;
|
|
73
|
+
if (!idToken) return {
|
|
74
|
+
ok: false,
|
|
75
|
+
status: 401,
|
|
76
|
+
error: 'Unauthenticated'
|
|
77
|
+
};
|
|
78
|
+
let decoded;
|
|
79
|
+
try {
|
|
80
|
+
decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
// A refused credential is the caller's 401; a failure to check one is
|
|
83
|
+
// ours and keeps a 5xx (AGL-2852).
|
|
84
|
+
if (isRefusedIdToken(error)) return {
|
|
85
|
+
ok: false,
|
|
86
|
+
status: 401,
|
|
87
|
+
error: 'Unauthenticated'
|
|
88
|
+
};
|
|
89
|
+
console.error('[crm] the recipe site writer could not be verified', error);
|
|
90
|
+
return {
|
|
91
|
+
ok: false,
|
|
92
|
+
status: 500,
|
|
93
|
+
error: 'The sign-in could not be checked. Try again.'
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const staff = decoded.staff === true;
|
|
97
|
+
const host = await firebaseAdmin.app().firestore().collection('hosts').doc(hostId).get();
|
|
98
|
+
if (!host.exists) return {
|
|
99
|
+
ok: false,
|
|
100
|
+
status: 404,
|
|
101
|
+
error: 'Unknown site'
|
|
102
|
+
};
|
|
103
|
+
const role = ((_host_get = host.get('memberRoles')) != null ? _host_get : {})[decoded.uid];
|
|
104
|
+
if (!staff && !hostRoleCanWrite(role)) {
|
|
105
|
+
return {
|
|
106
|
+
ok: false,
|
|
107
|
+
status: 403,
|
|
108
|
+
error: 'Editing requires the editor role'
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const resolved = await getOrgForHost(hostId);
|
|
112
|
+
if (!resolved) return {
|
|
113
|
+
ok: false,
|
|
114
|
+
status: 404,
|
|
115
|
+
error: 'Unknown site'
|
|
116
|
+
};
|
|
117
|
+
return {
|
|
118
|
+
ok: true,
|
|
119
|
+
uid: decoded.uid,
|
|
120
|
+
email: (_decoded_email = decoded.email) != null ? _decoded_email : null,
|
|
121
|
+
staff,
|
|
122
|
+
orgId: resolved.orgId,
|
|
123
|
+
org: resolved.org,
|
|
124
|
+
host
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* The org variant's site check: the host document, when it belongs to the
|
|
129
|
+
* organization the caller was admitted to. A site under another workspace
|
|
130
|
+
* — or none — reads as unknown, so a probe learns nothing from the answer.
|
|
131
|
+
*/ async function siteOfOrg(firestore, hostId, orgId) {
|
|
132
|
+
const host = await firestore.collection('hosts').doc(hostId).get();
|
|
133
|
+
return host.exists && host.get('orgId') === orgId ? host : null;
|
|
134
|
+
}
|
|
135
|
+
/** How a site reads in a feed line: its display name, its subdomain, its id. */ function siteLabel(host) {
|
|
136
|
+
var _host_get, _host_get1;
|
|
137
|
+
return String((_host_get = host.get('displayName')) != null ? _host_get : '').trim() || String((_host_get1 = host.get('subdomain')) != null ? _host_get1 : '').trim() || host.id;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* The stamps a site's LIVE actions carry, read with a field mask: the
|
|
141
|
+
* install's duplicate check and the status sweep ask the same question,
|
|
142
|
+
* and neither needs a trigger or a step list to answer it. A soft-deleted
|
|
143
|
+
* action (`deletedAt`) is a freed slot and counts for nothing.
|
|
144
|
+
*/ function readSiteStamps(rows) {
|
|
145
|
+
const installed = new Map();
|
|
146
|
+
let unstamped = 0;
|
|
147
|
+
let live = 0;
|
|
148
|
+
for (const row of rows.docs){
|
|
149
|
+
if (row.get('deletedAt') != null) continue;
|
|
150
|
+
live += 1;
|
|
151
|
+
const stamp = hostActionRecipeId(row.data());
|
|
152
|
+
if (stamp === undefined) unstamped += 1;
|
|
153
|
+
else if (stamp && !installed.has(stamp)) installed.set(stamp, row.id);
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
installed,
|
|
157
|
+
unstamped,
|
|
158
|
+
live
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
const stampsQuery = (host)=>host.collection('actions').select('recipe', 'deletedAt');
|
|
162
|
+
/**
|
|
163
|
+
* `POST /api/crm/recipe-install`
|
|
164
|
+
*
|
|
165
|
+
* The body is a CrmRecipeInstallRequest: the site, the recipe, the form a
|
|
166
|
+
* recipe that needs one is keyed on, and the org at the organization level.
|
|
167
|
+
* Answers 201 with a CrmRecipeInstallResult once the action is written, and
|
|
168
|
+
* 409 when a live action on the site already carries the stamp — naming it,
|
|
169
|
+
* so the caller can say "already installed" rather than install it twice.
|
|
170
|
+
*
|
|
171
|
+
* The write is one transaction: the stamps are read, the duplicate and the
|
|
172
|
+
* per-site cap are judged, and the document is created — all inside it, so
|
|
173
|
+
* two installs racing for the same site cannot both land.
|
|
174
|
+
*/ export const crmRecipeInstallHandler = async (req, res)=>{
|
|
175
|
+
var _req_body, _ref;
|
|
176
|
+
if (req.method !== 'POST') {
|
|
177
|
+
res.setHeader('Allow', 'POST');
|
|
178
|
+
res.status(405).json({
|
|
179
|
+
error: 'Method not allowed'
|
|
180
|
+
});
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const body = typeof req.body === 'string' ? JSON.parse(req.body) : (_req_body = req.body) != null ? _req_body : {};
|
|
184
|
+
const scope = readCrmRouteScope(body);
|
|
185
|
+
const hostId = (_ref = scope == null ? void 0 : scope.hostId) != null ? _ref : '';
|
|
186
|
+
if (!scope || !hostId) {
|
|
187
|
+
res.status(400).json({
|
|
188
|
+
error: 'Missing hostId'
|
|
189
|
+
});
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const recipe = crmActionRecipe(typed(body.recipeId, 64));
|
|
193
|
+
if (!recipe) {
|
|
194
|
+
res.status(400).json({
|
|
195
|
+
error: 'Pick a recipe'
|
|
196
|
+
});
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const formId = typed(body.formId, 200);
|
|
200
|
+
try {
|
|
201
|
+
const firestore = firebaseAdmin.app().firestore();
|
|
202
|
+
let writer;
|
|
203
|
+
if (scope.level === 'org') {
|
|
204
|
+
const caller = await authorizeOrgCaller(req, scope.orgId, {
|
|
205
|
+
needs: 'data.manage',
|
|
206
|
+
refusal: INSTALL_REFUSAL
|
|
207
|
+
});
|
|
208
|
+
if (caller.ok === false) {
|
|
209
|
+
res.status(caller.status).json({
|
|
210
|
+
error: caller.error
|
|
211
|
+
});
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
const host = await siteOfOrg(firestore, hostId, caller.orgId);
|
|
215
|
+
if (!host) {
|
|
216
|
+
res.status(404).json({
|
|
217
|
+
error: 'Unknown site'
|
|
218
|
+
});
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
writer = {
|
|
222
|
+
ok: true,
|
|
223
|
+
uid: caller.uid,
|
|
224
|
+
email: caller.email,
|
|
225
|
+
staff: caller.staff,
|
|
226
|
+
orgId: caller.orgId,
|
|
227
|
+
org: caller.org,
|
|
228
|
+
host
|
|
229
|
+
};
|
|
230
|
+
} else {
|
|
231
|
+
const caller = await authorizeSiteWriter(req, hostId);
|
|
232
|
+
if (caller.ok === false) {
|
|
233
|
+
res.status(caller.status).json({
|
|
234
|
+
error: caller.error
|
|
235
|
+
});
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
writer = caller;
|
|
239
|
+
}
|
|
240
|
+
// A recipe is a CRM automation first (AGL-2787): the suite is asked
|
|
241
|
+
// before the actions builder, so a plan without either is told about
|
|
242
|
+
// the one the recipe belongs to.
|
|
243
|
+
const suite = crmSuiteRefusal(writer.org, 'A CRM recipe');
|
|
244
|
+
if (suite) {
|
|
245
|
+
res.status(suite.status).json(suite.body);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
// The gate the site's own Recipes menu applies, judged here from the
|
|
249
|
+
// org document rather than trusted from the console: a recipe is an
|
|
250
|
+
// action, and the plan that carries one carries the other.
|
|
251
|
+
if (!checkEntitlement(writer.org, 'actions')) {
|
|
252
|
+
var _planLabelGrantingFeature;
|
|
253
|
+
res.status(403).json({
|
|
254
|
+
error: `The actions builder requires the ${(_planLabelGrantingFeature = planLabelGrantingFeature('actions')) != null ? _planLabelGrantingFeature : 'Pro'} ` + 'plan — see Billing to upgrade'
|
|
255
|
+
});
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
const hostRef = writer.host.ref;
|
|
259
|
+
let form;
|
|
260
|
+
if (recipe.needs === 'form') {
|
|
261
|
+
var _snapshot_get;
|
|
262
|
+
if (!formId) {
|
|
263
|
+
res.status(400).json({
|
|
264
|
+
error: 'Pick one of the site’s forms'
|
|
265
|
+
});
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const snapshot = await hostRef.collection('forms').doc(formId).get();
|
|
269
|
+
if (!snapshot.exists) {
|
|
270
|
+
res.status(404).json({
|
|
271
|
+
error: 'Unknown form'
|
|
272
|
+
});
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
// An archived form collects nothing, so a recipe keyed on it would
|
|
276
|
+
// never fire; the name falls back to the id the way every form list's does.
|
|
277
|
+
if (snapshot.get('archivedAt') != null) {
|
|
278
|
+
res.status(400).json({
|
|
279
|
+
error: 'That form is archived and collects nothing'
|
|
280
|
+
});
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
form = {
|
|
284
|
+
id: snapshot.id,
|
|
285
|
+
name: String((_snapshot_get = snapshot.get('displayName')) != null ? _snapshot_get : '').trim() || snapshot.id
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
const action = recipe.build(form ? {
|
|
289
|
+
form
|
|
290
|
+
} : undefined);
|
|
291
|
+
const problem = validateHostAction(action);
|
|
292
|
+
if (problem) {
|
|
293
|
+
res.status(400).json({
|
|
294
|
+
error: problem
|
|
295
|
+
});
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
const actionId = createResourceUid();
|
|
299
|
+
const outcome = await firestore.runTransaction(async (tx)=>{
|
|
300
|
+
const stamps = readSiteStamps(await tx.get(stampsQuery(hostRef)));
|
|
301
|
+
const existing = stamps.installed.get(recipe.id);
|
|
302
|
+
if (existing) {
|
|
303
|
+
return {
|
|
304
|
+
status: 409,
|
|
305
|
+
error: 'Already installed on this site',
|
|
306
|
+
actionId: existing
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
if (stamps.live >= ACTIONS_MAX_PER_HOST) {
|
|
310
|
+
return {
|
|
311
|
+
status: 403,
|
|
312
|
+
error: `interactions and actions are capped at ${ACTIONS_MAX_PER_HOST} per site`
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
tx.create(hostRef.collection('actions').doc(actionId), _extends({}, hostActionDocument(action), {
|
|
316
|
+
createdAt: FieldValue.serverTimestamp(),
|
|
317
|
+
updatedAt: FieldValue.serverTimestamp(),
|
|
318
|
+
createdBy: writer.uid
|
|
319
|
+
}));
|
|
320
|
+
return null;
|
|
321
|
+
});
|
|
322
|
+
if (outcome) {
|
|
323
|
+
res.status(outcome.status).json(_extends({
|
|
324
|
+
error: outcome.error
|
|
325
|
+
}, outcome.actionId ? {
|
|
326
|
+
actionId: outcome.actionId
|
|
327
|
+
} : {}));
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
const actor = {
|
|
331
|
+
uid: writer.uid,
|
|
332
|
+
email: writer.email
|
|
333
|
+
};
|
|
334
|
+
await logHostActivity(hostId, actor, 'Installed recipe', {
|
|
335
|
+
type: 'content',
|
|
336
|
+
id: actionId,
|
|
337
|
+
name: action.name
|
|
338
|
+
});
|
|
339
|
+
// The org hub's own feed line, for the act it performed (AGL-2634).
|
|
340
|
+
if (scope.level === 'org') {
|
|
341
|
+
await logOrgActivity(writer.orgId, actor, `Installed the “${recipe.title}” recipe on ${siteLabel(writer.host)}`, {
|
|
342
|
+
type: 'host',
|
|
343
|
+
id: hostId,
|
|
344
|
+
name: siteLabel(writer.host)
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
const answer = {
|
|
348
|
+
ok: true,
|
|
349
|
+
actionId,
|
|
350
|
+
name: action.name,
|
|
351
|
+
recipeId: recipe.id
|
|
352
|
+
};
|
|
353
|
+
res.status(201).json(answer);
|
|
354
|
+
} catch (error) {
|
|
355
|
+
console.error('[crm] recipe install failed', hostId, error);
|
|
356
|
+
res.status(500).json({
|
|
357
|
+
error: 'The recipe could not be installed'
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
/**
|
|
362
|
+
* `POST /api/crm/recipe-status`
|
|
363
|
+
*
|
|
364
|
+
* The body names the organization (`orgId`) for every one of its sites, or
|
|
365
|
+
* one site (`hostId`) for that site alone. Answers `ok: true` and `sites`,
|
|
366
|
+
* one CrmRecipeSiteStatus per site swept.
|
|
367
|
+
*/ export const crmRecipeStatusHandler = async (req, res)=>{
|
|
368
|
+
var _req_body;
|
|
369
|
+
if (req.method !== 'POST') {
|
|
370
|
+
res.setHeader('Allow', 'POST');
|
|
371
|
+
res.status(405).json({
|
|
372
|
+
error: 'Method not allowed'
|
|
373
|
+
});
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
const body = typeof req.body === 'string' ? JSON.parse(req.body) : (_req_body = req.body) != null ? _req_body : {};
|
|
377
|
+
const scope = readCrmRouteScope(body);
|
|
378
|
+
if (!scope) {
|
|
379
|
+
res.status(400).json({
|
|
380
|
+
error: 'Missing orgId or hostId'
|
|
381
|
+
});
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
try {
|
|
385
|
+
const firestore = firebaseAdmin.app().firestore();
|
|
386
|
+
let orgId = null;
|
|
387
|
+
let org;
|
|
388
|
+
if (scope.level === 'org') {
|
|
389
|
+
const caller = await authorizeOrgCaller(req, scope.orgId, {
|
|
390
|
+
needs: 'data.manage',
|
|
391
|
+
refusal: STATUS_REFUSAL
|
|
392
|
+
});
|
|
393
|
+
if (caller.ok === false) {
|
|
394
|
+
res.status(caller.status).json({
|
|
395
|
+
error: caller.error
|
|
396
|
+
});
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
orgId = caller.orgId;
|
|
400
|
+
org = caller.org;
|
|
401
|
+
} else {
|
|
402
|
+
const caller = await authorizeSiteWriter(req, scope.hostId);
|
|
403
|
+
if (caller.ok === false) {
|
|
404
|
+
res.status(caller.status).json({
|
|
405
|
+
error: caller.error
|
|
406
|
+
});
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
org = caller.org;
|
|
410
|
+
}
|
|
411
|
+
// The plan after the caller, before any site is read (AGL-2851).
|
|
412
|
+
const suite = crmSuiteRefusal(org, 'Reading which sites carry a CRM recipe');
|
|
413
|
+
if (suite) {
|
|
414
|
+
res.status(suite.status).json(suite.body);
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
const hostIds = orgId ? await orgHostIds(firestore, orgId) : [
|
|
418
|
+
scope.hostId
|
|
419
|
+
];
|
|
420
|
+
const sites = await Promise.all(hostIds.map(async (hostId)=>{
|
|
421
|
+
const stamps = readSiteStamps(await stampsQuery(firestore.collection('hosts').doc(hostId)).get());
|
|
422
|
+
return {
|
|
423
|
+
hostId,
|
|
424
|
+
installed: [
|
|
425
|
+
...stamps.installed.keys()
|
|
426
|
+
],
|
|
427
|
+
unstamped: stamps.unstamped
|
|
428
|
+
};
|
|
429
|
+
}));
|
|
430
|
+
res.status(200).json({
|
|
431
|
+
ok: true,
|
|
432
|
+
sites
|
|
433
|
+
});
|
|
434
|
+
} catch (error) {
|
|
435
|
+
console.error('[crm] recipe status failed', error);
|
|
436
|
+
res.status(500).json({
|
|
437
|
+
error: 'The recipe status could not be read'
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
//# sourceMappingURL=recipe-routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/server/recipe-routes.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * THE RECIPES, INSTALLED FROM THE ORGANIZATION (AGL-2639).\n *\n * On a site, a recipe opens the Actions editor prefilled and writes nothing\n * until the person saves — the editor is the writer, and the site's own\n * role is the authority. The organization-level CRM hub has no editor: it\n * mounts over every site with no site of its own, and an org running five\n * sites installed \"Welcome a new lead\" five times by hand. These two routes\n * are the org hub's door to a site's automations.\n *\n * `crm/recipe-install` writes the action a recipe builds into ONE site's\n * `hosts/{hostId}/actions`, as the site editor would have saved it — the\n * same builder, the same validator, the same stored shape — and refuses a\n * second install of the same recipe on the same site by the stamp the\n * builder puts on it. A server route rather than a client write for three\n * reasons the browser cannot supply: the document is created by a server\n * on every plan (the rules close `actions` to client creates, and the\n * per-site cap is counted here); the stamp is the server's word, not the\n * caller's; and the site is checked to be one the caller may manage —\n * at the org level, a site OF THE ORG the caller holds org-wide reach in,\n * so a host id typed into the body cannot reach a site under another\n * workspace.\n *\n * `crm/recipe-status` reads the stamps back for every site of the org, so\n * the card can say which sites carry which recipe. An action with no stamp\n * at all — saved before the stamp existed — is counted, not ignored: it\n * may well be a recipe, and the reader is told the site \"may already have\n * it\" rather than shown a clean slate.\n *\n * Both routes serve the site variant too (`hostId` alone, authorized by the\n * host role that creates actions today), so a site-level caller gets the\n * same answers; the org variant (`orgId` in the body) is the one the org\n * hub uses.\n */\n\nimport {\n ACTIONS_MAX_PER_HOST,\n type AglynOrganization,\n checkEntitlement,\n createResourceUid,\n crmActionRecipe,\n type CrmActionRecipeId,\n hostActionDocument,\n hostActionRecipeId,\n hostRoleCanWrite,\n planLabelGrantingFeature,\n type PluginApiHandler,\n type PluginApiRequest,\n validateHostAction,\n} from '@aglyn/aglyn/server'\nimport {\n firebaseAdmin,\n getOrgForHost,\n logHostActivity,\n logOrgActivity,\n} from '@aglyn/tenant-data-admin'\nimport { isRefusedIdToken } from '@aglyn/tenant-data-admin/server/id-token-refusal'\nimport { FieldValue } from 'firebase-admin/firestore'\nimport {\n CRM_API_ROUTES,\n type CrmRecipeInstallRequest,\n type CrmRecipeInstallResult,\n type CrmRecipeSiteStatus,\n} from '../constants/api-routes'\nimport { authorizeOrgCaller, orgHostIds, readCrmRouteScope } from './org-caller'\nimport { crmSuiteRefusal } from './suite-gate'\n\nexport const CRM_RECIPE_INSTALL_ROUTE = CRM_API_ROUTES.recipeInstall\nexport const CRM_RECIPE_STATUS_ROUTE = CRM_API_ROUTES.recipeStatus\n\ntype Refusal = { ok: false; status: number; error: string }\n\n/** A verified caller who may write a site's actions, and the site's org. */\ninterface SiteWriter {\n ok: true\n uid: string\n email: string | null\n staff: boolean\n orgId: string\n org: Partial<AglynOrganization>\n host: FirebaseFirestore.DocumentSnapshot\n}\n\nconst INSTALL_REFUSAL =\n 'Installing a recipe requires the data permission across the whole workspace'\nconst STATUS_REFUSAL =\n 'Reading which sites carry a recipe requires the data permission across the whole workspace'\n\nfunction typed(value: unknown, max: number): string {\n return String(value ?? '')\n .trim()\n .slice(0, max)\n}\n\n/**\n * The site variant's authority: the role on the host document that creates\n * an action through `/api/hosts/resources` today. Staff pass, as they pass\n * that route. Answers the host snapshot it read, so the caller reads it\n * once.\n */\nasync function authorizeSiteWriter(\n req: PluginApiRequest,\n hostId: string,\n): Promise<SiteWriter | Refusal> {\n const authorization = String(req.headers.authorization ?? '')\n const idToken = authorization.startsWith('Bearer ')\n ? authorization.slice('Bearer '.length)\n : undefined\n if (!idToken) return { ok: false, status: 401, error: 'Unauthenticated' }\n let decoded: { uid: string; email?: string; staff?: unknown }\n try {\n decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken)\n } catch (error) {\n // A refused credential is the caller's 401; a failure to check one is\n // ours and keeps a 5xx (AGL-2852).\n if (isRefusedIdToken(error)) return { ok: false, status: 401, error: 'Unauthenticated' }\n console.error('[crm] the recipe site writer could not be verified', error)\n return { ok: false, status: 500, error: 'The sign-in could not be checked. Try again.' }\n }\n const staff = decoded.staff === true\n const host = await firebaseAdmin\n .app()\n .firestore()\n .collection('hosts')\n .doc(hostId)\n .get()\n if (!host.exists) return { ok: false, status: 404, error: 'Unknown site' }\n const role = (host.get('memberRoles') ?? {})[decoded.uid]\n if (!staff && !hostRoleCanWrite(role)) {\n return { ok: false, status: 403, error: 'Editing requires the editor role' }\n }\n const resolved = await getOrgForHost(hostId)\n if (!resolved) return { ok: false, status: 404, error: 'Unknown site' }\n return {\n ok: true,\n uid: decoded.uid,\n email: decoded.email ?? null,\n staff,\n orgId: resolved.orgId,\n org: resolved.org,\n host,\n }\n}\n\n/**\n * The org variant's site check: the host document, when it belongs to the\n * organization the caller was admitted to. A site under another workspace\n * — or none — reads as unknown, so a probe learns nothing from the answer.\n */\nasync function siteOfOrg(\n firestore: FirebaseFirestore.Firestore,\n hostId: string,\n orgId: string,\n): Promise<FirebaseFirestore.DocumentSnapshot | null> {\n const host = await firestore.collection('hosts').doc(hostId).get()\n return host.exists && host.get('orgId') === orgId ? host : null\n}\n\n/** How a site reads in a feed line: its display name, its subdomain, its id. */\nfunction siteLabel(host: FirebaseFirestore.DocumentSnapshot): string {\n return (\n String(host.get('displayName') ?? '').trim() ||\n String(host.get('subdomain') ?? '').trim() ||\n host.id\n )\n}\n\n/**\n * The stamps a site's LIVE actions carry, read with a field mask: the\n * install's duplicate check and the status sweep ask the same question,\n * and neither needs a trigger or a step list to answer it. A soft-deleted\n * action (`deletedAt`) is a freed slot and counts for nothing.\n */\nfunction readSiteStamps(rows: FirebaseFirestore.QuerySnapshot): {\n installed: Map<CrmActionRecipeId, string>\n unstamped: number\n live: number\n} {\n const installed = new Map<CrmActionRecipeId, string>()\n let unstamped = 0\n let live = 0\n for (const row of rows.docs) {\n if (row.get('deletedAt') != null) continue\n live += 1\n const stamp = hostActionRecipeId(row.data())\n if (stamp === undefined) unstamped += 1\n else if (stamp && !installed.has(stamp)) installed.set(stamp, row.id)\n }\n return { installed, unstamped, live }\n}\n\nconst stampsQuery = (host: FirebaseFirestore.DocumentReference) =>\n host.collection('actions').select('recipe', 'deletedAt')\n\n/**\n * `POST /api/crm/recipe-install`\n *\n * The body is a CrmRecipeInstallRequest: the site, the recipe, the form a\n * recipe that needs one is keyed on, and the org at the organization level.\n * Answers 201 with a CrmRecipeInstallResult once the action is written, and\n * 409 when a live action on the site already carries the stamp — naming it,\n * so the caller can say \"already installed\" rather than install it twice.\n *\n * The write is one transaction: the stamps are read, the duplicate and the\n * per-site cap are judged, and the document is created — all inside it, so\n * two installs racing for the same site cannot both land.\n */\nexport const crmRecipeInstallHandler: PluginApiHandler = async (req, res) => {\n if (req.method !== 'POST') {\n res.setHeader('Allow', 'POST')\n res.status(405).json({ error: 'Method not allowed' })\n return\n }\n const body: Partial<CrmRecipeInstallRequest> =\n typeof req.body === 'string' ? JSON.parse(req.body) : (req.body ?? {})\n const scope = readCrmRouteScope(body as Record<string, unknown>)\n const hostId = scope?.hostId ?? ''\n if (!scope || !hostId) {\n res.status(400).json({ error: 'Missing hostId' })\n return\n }\n const recipe = crmActionRecipe(typed(body.recipeId, 64))\n if (!recipe) {\n res.status(400).json({ error: 'Pick a recipe' })\n return\n }\n const formId = typed(body.formId, 200)\n\n try {\n const firestore = firebaseAdmin.app().firestore()\n let writer: SiteWriter\n if (scope.level === 'org') {\n const caller = await authorizeOrgCaller(req, scope.orgId, {\n needs: 'data.manage',\n refusal: INSTALL_REFUSAL,\n })\n if (caller.ok === false) {\n res.status(caller.status).json({ error: caller.error })\n return\n }\n const host = await siteOfOrg(firestore, hostId, caller.orgId)\n if (!host) {\n res.status(404).json({ error: 'Unknown site' })\n return\n }\n writer = {\n ok: true,\n uid: caller.uid,\n email: caller.email,\n staff: caller.staff,\n orgId: caller.orgId,\n org: caller.org,\n host,\n }\n } else {\n const caller = await authorizeSiteWriter(req, hostId)\n if (caller.ok === false) {\n res.status(caller.status).json({ error: caller.error })\n return\n }\n writer = caller\n }\n\n // A recipe is a CRM automation first (AGL-2787): the suite is asked\n // before the actions builder, so a plan without either is told about\n // the one the recipe belongs to.\n const suite = crmSuiteRefusal(writer.org, 'A CRM recipe')\n if (suite) {\n res.status(suite.status).json(suite.body)\n return\n }\n\n // The gate the site's own Recipes menu applies, judged here from the\n // org document rather than trusted from the console: a recipe is an\n // action, and the plan that carries one carries the other.\n if (!checkEntitlement(writer.org, 'actions')) {\n res.status(403).json({\n error:\n `The actions builder requires the ${planLabelGrantingFeature('actions') ?? 'Pro'} ` +\n 'plan — see Billing to upgrade',\n })\n return\n }\n\n const hostRef = writer.host.ref\n let form: { id: string; name: string } | undefined\n if (recipe.needs === 'form') {\n if (!formId) {\n res.status(400).json({ error: 'Pick one of the site’s forms' })\n return\n }\n const snapshot = await hostRef.collection('forms').doc(formId).get()\n if (!snapshot.exists) {\n res.status(404).json({ error: 'Unknown form' })\n return\n }\n // An archived form collects nothing, so a recipe keyed on it would\n // never fire; the name falls back to the id the way every form list's does.\n if (snapshot.get('archivedAt') != null) {\n res.status(400).json({ error: 'That form is archived and collects nothing' })\n return\n }\n form = {\n id: snapshot.id,\n name: String(snapshot.get('displayName') ?? '').trim() || snapshot.id,\n }\n }\n\n const action = recipe.build(form ? { form } : undefined)\n const problem = validateHostAction(action)\n if (problem) {\n res.status(400).json({ error: problem })\n return\n }\n\n const actionId = createResourceUid()\n const outcome = await firestore.runTransaction(async (tx) => {\n const stamps = readSiteStamps(await tx.get(stampsQuery(hostRef)))\n const existing = stamps.installed.get(recipe.id)\n if (existing) {\n return {\n status: 409,\n error: 'Already installed on this site',\n actionId: existing,\n }\n }\n if (stamps.live >= ACTIONS_MAX_PER_HOST) {\n return {\n status: 403,\n error: `interactions and actions are capped at ${ACTIONS_MAX_PER_HOST} per site`,\n }\n }\n tx.create(hostRef.collection('actions').doc(actionId), {\n ...hostActionDocument(action),\n createdAt: FieldValue.serverTimestamp(),\n updatedAt: FieldValue.serverTimestamp(),\n createdBy: writer.uid,\n })\n return null\n })\n if (outcome) {\n res.status(outcome.status).json({\n error: outcome.error,\n ...(outcome.actionId ? { actionId: outcome.actionId } : {}),\n })\n return\n }\n\n const actor = { uid: writer.uid, email: writer.email }\n await logHostActivity(hostId, actor, 'Installed recipe', {\n type: 'content',\n id: actionId,\n name: action.name,\n })\n // The org hub's own feed line, for the act it performed (AGL-2634).\n if (scope.level === 'org') {\n await logOrgActivity(\n writer.orgId,\n actor,\n `Installed the “${recipe.title}” recipe on ${siteLabel(writer.host)}`,\n { type: 'host', id: hostId, name: siteLabel(writer.host) },\n )\n }\n const answer: CrmRecipeInstallResult = {\n ok: true,\n actionId,\n name: action.name,\n recipeId: recipe.id,\n }\n res.status(201).json(answer)\n } catch (error) {\n console.error('[crm] recipe install failed', hostId, error)\n res.status(500).json({ error: 'The recipe could not be installed' })\n }\n}\n\n/**\n * `POST /api/crm/recipe-status`\n *\n * The body names the organization (`orgId`) for every one of its sites, or\n * one site (`hostId`) for that site alone. Answers `ok: true` and `sites`,\n * one CrmRecipeSiteStatus per site swept.\n */\nexport const crmRecipeStatusHandler: PluginApiHandler = async (req, res) => {\n if (req.method !== 'POST') {\n res.setHeader('Allow', 'POST')\n res.status(405).json({ error: 'Method not allowed' })\n return\n }\n const body: Record<string, unknown> =\n typeof req.body === 'string' ? JSON.parse(req.body) : (req.body ?? {})\n const scope = readCrmRouteScope(body)\n if (!scope) {\n res.status(400).json({ error: 'Missing orgId or hostId' })\n return\n }\n try {\n const firestore = firebaseAdmin.app().firestore()\n let orgId: string | null = null\n let org: unknown\n if (scope.level === 'org') {\n const caller = await authorizeOrgCaller(req, scope.orgId, {\n needs: 'data.manage',\n refusal: STATUS_REFUSAL,\n })\n if (caller.ok === false) {\n res.status(caller.status).json({ error: caller.error })\n return\n }\n orgId = caller.orgId\n org = caller.org\n } else {\n const caller = await authorizeSiteWriter(req, scope.hostId)\n if (caller.ok === false) {\n res.status(caller.status).json({ error: caller.error })\n return\n }\n org = caller.org\n }\n // The plan after the caller, before any site is read (AGL-2851).\n const suite = crmSuiteRefusal(org, 'Reading which sites carry a CRM recipe')\n if (suite) {\n res.status(suite.status).json(suite.body)\n return\n }\n const hostIds = orgId ? await orgHostIds(firestore, orgId) : [scope.hostId]\n const sites = await Promise.all(\n hostIds.map(async (hostId): Promise<CrmRecipeSiteStatus> => {\n const stamps = readSiteStamps(\n await stampsQuery(firestore.collection('hosts').doc(hostId)).get(),\n )\n return {\n hostId,\n installed: [...stamps.installed.keys()],\n unstamped: stamps.unstamped,\n }\n }),\n )\n res.status(200).json({ ok: true, sites })\n } catch (error) {\n console.error('[crm] recipe status failed', error)\n res.status(500).json({ error: 'The recipe status could not be read' })\n }\n}\n"],"names":["ACTIONS_MAX_PER_HOST","checkEntitlement","createResourceUid","crmActionRecipe","hostActionDocument","hostActionRecipeId","hostRoleCanWrite","planLabelGrantingFeature","validateHostAction","firebaseAdmin","getOrgForHost","logHostActivity","logOrgActivity","isRefusedIdToken","FieldValue","CRM_API_ROUTES","authorizeOrgCaller","orgHostIds","readCrmRouteScope","crmSuiteRefusal","CRM_RECIPE_INSTALL_ROUTE","recipeInstall","CRM_RECIPE_STATUS_ROUTE","recipeStatus","INSTALL_REFUSAL","STATUS_REFUSAL","typed","value","max","String","trim","slice","authorizeSiteWriter","req","hostId","host","decoded","authorization","headers","idToken","startsWith","length","undefined","ok","status","error","app","auth","verifyIdToken","console","staff","firestore","collection","doc","get","exists","role","uid","resolved","email","orgId","org","siteOfOrg","siteLabel","id","readSiteStamps","rows","installed","Map","unstamped","live","row","docs","stamp","data","has","set","stampsQuery","select","crmRecipeInstallHandler","res","method","setHeader","json","body","JSON","parse","scope","recipe","recipeId","formId","writer","level","caller","needs","refusal","suite","hostRef","ref","form","snapshot","name","action","build","problem","actionId","outcome","runTransaction","tx","stamps","existing","create","createdAt","serverTimestamp","updatedAt","createdBy","actor","type","title","answer","crmRecipeStatusHandler","hostIds","sites","Promise","all","map","keys"],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
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 PluginRecordFactsReader, type PluginRecordFactsRequest } from '@aglyn/aglyn/plugin-manager/plugin-record-facts';
|
|
18
|
+
import { type ConsentGroup } from '@aglyn/aglyn/server';
|
|
19
|
+
type Data = Record<string, unknown>;
|
|
20
|
+
type Refusal = {
|
|
21
|
+
ok: false;
|
|
22
|
+
status: 400 | 403 | 404;
|
|
23
|
+
error: string;
|
|
24
|
+
};
|
|
25
|
+
/** Logged activities read per record before visibility and the timeline cut. */
|
|
26
|
+
export declare const CRM_FACTS_ACTIVITIES_READ = 40;
|
|
27
|
+
/** Tasks read per record, the record page's own window. */
|
|
28
|
+
export declare const CRM_FACTS_TASKS_READ = 50;
|
|
29
|
+
/** Deals read per contact or company. */
|
|
30
|
+
export declare const CRM_FACTS_DEALS_READ = 20;
|
|
31
|
+
/** Custom field definitions an import catalog reads. */
|
|
32
|
+
export declare const CRM_FACTS_FIELD_DEFINITIONS_READ = 100;
|
|
33
|
+
export declare const CRM_FACTS_UNKNOWN_SITE = "Unknown site";
|
|
34
|
+
export declare const CRM_FACTS_NOT_A_MEMBER = "You are not a member of that organization";
|
|
35
|
+
export declare const CRM_FACTS_SITE_REFUSAL = "Reading CRM records requires the data.manage permission on this site";
|
|
36
|
+
export declare const CRM_FACTS_ORG_REFUSAL = "Reading CRM records across the organization requires the data.manage permission for the whole organization";
|
|
37
|
+
export declare const CRM_FACTS_LEAD_NEEDS_SITE = "A lead is read on the site that captured it";
|
|
38
|
+
/** Where a read runs, once the caller has been admitted. */
|
|
39
|
+
interface CrmFactsScope {
|
|
40
|
+
orgId: string;
|
|
41
|
+
hostId: string | null;
|
|
42
|
+
org: Data;
|
|
43
|
+
/** The site's consent group under a site; `null` at the organization level. */
|
|
44
|
+
group: ConsentGroup | null;
|
|
45
|
+
/** Whether a row's `visibleTo` is readable here. */
|
|
46
|
+
visible: (visibleTo: unknown) => boolean;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Rules 1 to 3: the site, the member and the plan. A refusal names what the
|
|
50
|
+
* member lacks in the words the console's own CRM doors use.
|
|
51
|
+
*/
|
|
52
|
+
export declare function crmFactsScope(request: PluginRecordFactsRequest): Promise<CrmFactsScope | Refusal>;
|
|
53
|
+
export declare const crmContactFactsReader: PluginRecordFactsReader;
|
|
54
|
+
export declare const crmCompanyFactsReader: PluginRecordFactsReader;
|
|
55
|
+
export declare const crmDealFactsReader: PluginRecordFactsReader;
|
|
56
|
+
export declare const crmLeadFactsReader: PluginRecordFactsReader;
|
|
57
|
+
export declare const crmImportFactsReader: PluginRecordFactsReader;
|
|
58
|
+
/** Every reader this plugin registers, by resource name. */
|
|
59
|
+
export declare const CRM_FACTS_READERS: Readonly<Record<string, PluginRecordFactsReader>>;
|
|
60
|
+
/**
|
|
61
|
+
* Registers the readers; the console surface calls it, the one process that
|
|
62
|
+
* runs AI jobs (AGL-3026). Idempotent: a second call replaces the first.
|
|
63
|
+
*/
|
|
64
|
+
export declare function registerCrmRecordFactsReaders(): void;
|
|
65
|
+
export {};
|