@aglyn/plugins-crm 1.0.0-beta.220 → 1.0.0-beta.222
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/README.md +2 -1
- package/package.json +13 -13
- package/src/lib/components/activity-list.js +12 -5
- package/src/lib/components/activity-list.js.map +1 -1
- package/src/lib/components/companies-bulk-bar.d.ts +1 -4
- package/src/lib/components/companies-bulk-bar.js +7 -20
- package/src/lib/components/companies-bulk-bar.js.map +1 -1
- package/src/lib/components/companies-section.d.ts +3 -3
- package/src/lib/components/companies-section.js +149 -46
- package/src/lib/components/companies-section.js.map +1 -1
- package/src/lib/components/company-edit-drawer.js +281 -20
- package/src/lib/components/company-edit-drawer.js.map +1 -1
- package/src/lib/components/company-properties-card.js +99 -8
- package/src/lib/components/company-properties-card.js.map +1 -1
- package/src/lib/components/contact-detail-page.js +3 -1
- package/src/lib/components/contact-detail-page.js.map +1 -1
- package/src/lib/components/contact-merge-dialog.js +33 -10
- package/src/lib/components/contact-merge-dialog.js.map +1 -1
- package/src/lib/components/contact-properties-card.d.ts +0 -69
- package/src/lib/components/contact-properties-card.js +350 -60
- package/src/lib/components/contact-properties-card.js.map +1 -1
- package/src/lib/components/contact-reports-to-field.d.ts +46 -0
- package/src/lib/components/contact-reports-to-field.js +67 -0
- package/src/lib/components/contact-reports-to-field.js.map +1 -0
- package/src/lib/components/contacts-bulk-bar.d.ts +1 -8
- package/src/lib/components/contacts-bulk-bar.js +7 -19
- package/src/lib/components/contacts-bulk-bar.js.map +1 -1
- package/src/lib/components/contacts-section.js +46 -33
- package/src/lib/components/contacts-section.js.map +1 -1
- package/src/lib/components/crm-call-actions.d.ts +10 -0
- package/src/lib/components/crm-call-actions.js +26 -9
- package/src/lib/components/crm-call-actions.js.map +1 -1
- package/src/lib/components/crm-record-picker.d.ts +15 -0
- package/src/lib/components/crm-record-picker.js +16 -10
- package/src/lib/components/crm-record-picker.js.map +1 -1
- package/src/lib/components/crm-transfer-buttons.d.ts +54 -0
- package/src/lib/components/crm-transfer-buttons.js +167 -0
- package/src/lib/components/crm-transfer-buttons.js.map +1 -0
- package/src/lib/components/deal-contact-roles-card.d.ts +46 -0
- package/src/lib/components/deal-contact-roles-card.js +368 -0
- package/src/lib/components/deal-contact-roles-card.js.map +1 -0
- package/src/lib/components/deal-detail-page.d.ts +3 -2
- package/src/lib/components/deal-detail-page.js +25 -6
- package/src/lib/components/deal-detail-page.js.map +1 -1
- package/src/lib/components/deal-edit-drawer.d.ts +10 -0
- package/src/lib/components/deal-edit-drawer.js +219 -10
- package/src/lib/components/deal-edit-drawer.js.map +1 -1
- package/src/lib/components/deal-properties-card.d.ts +7 -0
- package/src/lib/components/deal-properties-card.js +34 -3
- package/src/lib/components/deal-properties-card.js.map +1 -1
- package/src/lib/components/deal-stage-card.d.ts +4 -0
- package/src/lib/components/deal-stage-card.js +10 -2
- package/src/lib/components/deal-stage-card.js.map +1 -1
- package/src/lib/components/deals-bulk-bar.d.ts +0 -3
- package/src/lib/components/deals-bulk-bar.js +7 -20
- package/src/lib/components/deals-bulk-bar.js.map +1 -1
- package/src/lib/components/deals-section.d.ts +3 -3
- package/src/lib/components/deals-section.js +123 -63
- package/src/lib/components/deals-section.js.map +1 -1
- package/src/lib/components/fields-section.js +31 -17
- package/src/lib/components/fields-section.js.map +1 -1
- package/src/lib/components/lead-convert-dialog.d.ts +4 -0
- package/src/lib/components/lead-convert-dialog.js +23 -1
- package/src/lib/components/lead-convert-dialog.js.map +1 -1
- package/src/lib/components/lead-detail-page.js +8 -2
- package/src/lib/components/lead-detail-page.js.map +1 -1
- package/src/lib/components/lead-profile-fields.d.ts +71 -0
- package/src/lib/components/lead-profile-fields.js +386 -0
- package/src/lib/components/lead-profile-fields.js.map +1 -0
- package/src/lib/components/lead-properties-card.d.ts +2 -1
- package/src/lib/components/lead-properties-card.js +85 -176
- package/src/lib/components/lead-properties-card.js.map +1 -1
- package/src/lib/components/lead-source-select.d.ts +4 -23
- package/src/lib/components/lead-source-select.js +9 -73
- package/src/lib/components/lead-source-select.js.map +1 -1
- package/src/lib/components/lead-status-chip.d.ts +9 -3
- package/src/lib/components/lead-status-chip.js +8 -4
- package/src/lib/components/lead-status-chip.js.map +1 -1
- package/src/lib/components/lead-status-options.d.ts +20 -0
- package/src/lib/components/lead-status-options.js +51 -0
- package/src/lib/components/lead-status-options.js.map +1 -0
- package/src/lib/components/lead-unqualify-dialog.d.ts +5 -0
- package/src/lib/components/lead-unqualify-dialog.js +50 -18
- package/src/lib/components/lead-unqualify-dialog.js.map +1 -1
- package/src/lib/components/leads-bulk-bar.d.ts +0 -3
- package/src/lib/components/leads-bulk-bar.js +29 -29
- package/src/lib/components/leads-bulk-bar.js.map +1 -1
- package/src/lib/components/leads-section.js +120 -68
- package/src/lib/components/leads-section.js.map +1 -1
- package/src/lib/components/linked-deals-card.d.ts +4 -0
- package/src/lib/components/linked-deals-card.js +47 -3
- package/src/lib/components/linked-deals-card.js.map +1 -1
- package/src/lib/components/log-activity-dialog.js +43 -3
- package/src/lib/components/log-activity-dialog.js.map +1 -1
- package/src/lib/components/new-contact-drawer.d.ts +8 -0
- package/src/lib/components/new-contact-drawer.js +108 -6
- package/src/lib/components/new-contact-drawer.js.map +1 -1
- package/src/lib/components/new-lead-drawer.d.ts +9 -2
- package/src/lib/components/new-lead-drawer.js +88 -121
- package/src/lib/components/new-lead-drawer.js.map +1 -1
- package/src/lib/components/picklist-select.d.ts +36 -0
- package/src/lib/components/picklist-select.js +114 -0
- package/src/lib/components/picklist-select.js.map +1 -0
- package/src/lib/components/picklist-values-card.d.ts +34 -0
- package/src/lib/components/{lead-source-values-card.js → picklist-values-card.js} +241 -58
- package/src/lib/components/picklist-values-card.js.map +1 -0
- package/src/lib/components/pipeline-stages-dialog.d.ts +7 -0
- package/src/lib/components/pipeline-stages-dialog.js +26 -4
- package/src/lib/components/pipeline-stages-dialog.js.map +1 -1
- package/src/lib/components/pipelines-dialog.js +15 -6
- package/src/lib/components/pipelines-dialog.js.map +1 -1
- package/src/lib/components/recent-activity-feed.js +20 -5
- package/src/lib/components/recent-activity-feed.js.map +1 -1
- package/src/lib/components/recipes-card.js +4 -2
- package/src/lib/components/recipes-card.js.map +1 -1
- package/src/lib/components/record-tasks-card.js +15 -8
- package/src/lib/components/record-tasks-card.js.map +1 -1
- package/src/lib/components/reports/forecast-card.d.ts +6 -0
- package/src/lib/components/reports/forecast-card.js +101 -3
- package/src/lib/components/reports/forecast-card.js.map +1 -1
- package/src/lib/components/reports/lead-funnel-card.d.ts +12 -0
- package/src/lib/components/reports/lead-funnel-card.js +9 -5
- package/src/lib/components/reports/lead-funnel-card.js.map +1 -1
- package/src/lib/components/reports/lead-source-card.d.ts +23 -0
- package/src/lib/components/reports/lead-source-card.js +318 -0
- package/src/lib/components/reports/lead-source-card.js.map +1 -0
- package/src/lib/components/reports/report-export.js +1 -1
- package/src/lib/components/reports/report-export.js.map +1 -1
- package/src/lib/components/reports-section.js +8 -0
- package/src/lib/components/reports-section.js.map +1 -1
- package/src/lib/components/sharing-rule-drawer.d.ts +5 -0
- package/src/lib/components/sharing-rule-drawer.js +108 -19
- package/src/lib/components/sharing-rule-drawer.js.map +1 -1
- package/src/lib/components/sharing-rules-card.js +11 -3
- package/src/lib/components/sharing-rules-card.js.map +1 -1
- package/src/lib/components/task-cells.d.ts +9 -1
- package/src/lib/components/task-cells.js +9 -4
- package/src/lib/components/task-cells.js.map +1 -1
- package/src/lib/components/task-edit-drawer.js +111 -43
- package/src/lib/components/task-edit-drawer.js.map +1 -1
- package/src/lib/components/tasks-bulk-bar.d.ts +0 -3
- package/src/lib/components/tasks-bulk-bar.js +7 -20
- package/src/lib/components/tasks-bulk-bar.js.map +1 -1
- package/src/lib/components/tasks-calendar.d.ts +3 -0
- package/src/lib/components/tasks-calendar.js +8 -1
- package/src/lib/components/tasks-calendar.js.map +1 -1
- package/src/lib/components/tasks-section.d.ts +3 -3
- package/src/lib/components/tasks-section.js +58 -43
- package/src/lib/components/tasks-section.js.map +1 -1
- package/src/lib/constants/api-routes.d.ts +2 -9
- package/src/lib/constants/api-routes.js +1 -7
- package/src/lib/constants/api-routes.js.map +1 -1
- package/src/lib/constants/bundle-common.d.ts +6 -0
- package/src/lib/constants/bundle-common.js +11 -0
- package/src/lib/constants/bundle-common.js.map +1 -1
- package/src/lib/constants/company-filters.d.ts +14 -0
- package/src/lib/constants/company-filters.js +51 -1
- package/src/lib/constants/company-filters.js.map +1 -1
- package/src/lib/constants/contact-filters.d.ts +2 -0
- package/src/lib/constants/contact-filters.js +18 -1
- package/src/lib/constants/contact-filters.js.map +1 -1
- package/src/lib/constants/deal-filters.d.ts +22 -1
- package/src/lib/constants/deal-filters.js +67 -4
- package/src/lib/constants/deal-filters.js.map +1 -1
- package/src/lib/declarations.console-server.d.ts +3 -0
- package/src/lib/declarations.console-server.js +35 -0
- package/src/lib/declarations.console-server.js.map +1 -1
- package/src/lib/declarations.d.ts +26 -0
- package/src/lib/declarations.js +35 -0
- package/src/lib/declarations.js.map +1 -0
- package/src/lib/declarations.server.d.ts +11 -0
- package/src/lib/declarations.server.js +92 -1
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/hooks/use-company-picklists.d.ts +15 -0
- package/src/lib/hooks/use-company-picklists.js +55 -0
- package/src/lib/hooks/use-company-picklists.js.map +1 -0
- package/src/lib/hooks/use-crm-picklist.d.ts +25 -0
- package/src/lib/hooks/use-crm-picklist.js +53 -0
- package/src/lib/hooks/use-crm-picklist.js.map +1 -0
- package/src/lib/hooks/use-crm-record-names.d.ts +16 -0
- package/src/lib/hooks/use-crm-record-names.js +45 -19
- package/src/lib/hooks/use-crm-record-names.js.map +1 -1
- package/src/lib/hooks/use-crm-task-picklists.d.ts +11 -0
- package/src/lib/hooks/use-crm-task-picklists.js +45 -0
- package/src/lib/hooks/use-crm-task-picklists.js.map +1 -0
- package/src/lib/hooks/use-deals.d.ts +14 -0
- package/src/lib/hooks/use-deals.js +29 -0
- package/src/lib/hooks/use-deals.js.map +1 -1
- package/src/lib/hooks/use-lead-picklists.d.ts +14 -0
- package/src/lib/hooks/use-lead-picklists.js +46 -0
- package/src/lib/hooks/use-lead-picklists.js.map +1 -0
- package/src/lib/hooks/use-lead-source-picklist.d.ts +5 -19
- package/src/lib/hooks/use-lead-source-picklist.js +12 -22
- package/src/lib/hooks/use-lead-source-picklist.js.map +1 -1
- package/src/lib/hooks/use-lead-status-picklist.d.ts +9 -0
- package/src/lib/hooks/use-lead-status-picklist.js +40 -0
- package/src/lib/hooks/use-lead-status-picklist.js.map +1 -0
- package/src/lib/model/companies.d.ts +32 -3
- package/src/lib/model/companies.js +133 -16
- package/src/lib/model/companies.js.map +1 -1
- package/src/lib/model/contact-merge.d.ts +27 -3
- package/src/lib/model/contact-merge.js +81 -7
- package/src/lib/model/contact-merge.js.map +1 -1
- package/src/lib/model/contact-record.d.ts +17 -0
- package/src/lib/model/contact-record.js +15 -1
- package/src/lib/model/contact-record.js.map +1 -1
- package/src/lib/model/contact-update.d.ts +31 -2
- package/src/lib/model/contact-update.js +14 -0
- package/src/lib/model/contact-update.js.map +1 -1
- package/src/lib/model/crm-list-query.d.ts +5 -0
- package/src/lib/model/crm-list-query.js +8 -0
- package/src/lib/model/crm-list-query.js.map +1 -1
- package/src/lib/model/crm-recipes.d.ts +40 -0
- package/src/lib/model/crm-recipes.js +244 -0
- package/src/lib/model/crm-recipes.js.map +1 -0
- package/src/lib/model/crm-record-counts.d.ts +36 -0
- package/src/lib/model/crm-record-counts.js +54 -0
- package/src/lib/model/crm-record-counts.js.map +1 -0
- package/src/lib/model/crm-record-lists.d.ts +23 -0
- package/src/lib/model/crm-record-lists.js +161 -0
- package/src/lib/model/crm-record-lists.js.map +1 -0
- package/src/lib/model/crm-reports.d.ts +73 -3
- package/src/lib/model/crm-reports.js +120 -1
- package/src/lib/model/crm-reports.js.map +1 -1
- package/src/lib/model/crm-sharing.d.ts +21 -3
- package/src/lib/model/crm-sharing.js +27 -6
- package/src/lib/model/crm-sharing.js.map +1 -1
- package/src/lib/model/deal-board-model.d.ts +8 -2
- package/src/lib/model/deal-board-model.js +15 -2
- package/src/lib/model/deal-board-model.js.map +1 -1
- package/src/lib/model/deal-contact-roles.d.ts +45 -0
- package/src/lib/model/deal-contact-roles.js +73 -0
- package/src/lib/model/deal-contact-roles.js.map +1 -0
- package/src/lib/model/deal-form-model.d.ts +56 -0
- package/src/lib/model/deal-form-model.js +79 -7
- package/src/lib/model/deal-form-model.js.map +1 -1
- package/src/lib/model/download-text-file.d.ts +23 -0
- package/src/lib/model/download-text-file.js +34 -0
- package/src/lib/model/download-text-file.js.map +1 -0
- package/src/lib/model/lead-filters.d.ts +36 -2
- package/src/lib/model/lead-filters.js +123 -2
- package/src/lib/model/lead-filters.js.map +1 -1
- package/src/lib/model/lead-source-direction.d.ts +38 -0
- package/src/lib/model/lead-source-direction.js +52 -0
- package/src/lib/model/lead-source-direction.js.map +1 -0
- package/src/lib/model/picklist-values.d.ts +119 -0
- package/src/lib/model/picklist-values.js +256 -0
- package/src/lib/model/picklist-values.js.map +1 -0
- package/src/lib/model/record-facts-disclosed.d.ts +81 -0
- package/src/lib/model/record-facts-disclosed.js +281 -0
- package/src/lib/model/record-facts-disclosed.js.map +1 -0
- package/src/lib/model/record-facts.d.ts +215 -34
- package/src/lib/model/record-facts.js +307 -128
- package/src/lib/model/record-facts.js.map +1 -1
- package/src/lib/model/task-picklists.d.ts +52 -0
- package/src/lib/model/task-picklists.js +83 -0
- package/src/lib/model/task-picklists.js.map +1 -0
- package/src/lib/model/task-routes.d.ts +13 -0
- package/src/lib/model/task-routes.js +23 -4
- package/src/lib/model/task-routes.js.map +1 -1
- package/src/lib/plan-entitlements.js +6 -4
- package/src/lib/plan-entitlements.js.map +1 -1
- package/src/lib/plugin.js +17 -0
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/server/api-v1/contacts.js +220 -14
- package/src/lib/server/api-v1/contacts.js.map +1 -1
- package/src/lib/server/api-v1/crm-activities.js +14 -1
- package/src/lib/server/api-v1/crm-activities.js.map +1 -1
- package/src/lib/server/api-v1/crm-companies.js +123 -14
- package/src/lib/server/api-v1/crm-companies.js.map +1 -1
- package/src/lib/server/api-v1/crm-deals.js +291 -26
- package/src/lib/server/api-v1/crm-deals.js.map +1 -1
- package/src/lib/server/api-v1/crm-leads.js +144 -38
- package/src/lib/server/api-v1/crm-leads.js.map +1 -1
- package/src/lib/server/api-v1/crm-pipelines.d.ts +6 -0
- package/src/lib/server/api-v1/crm-pipelines.js +8 -2
- package/src/lib/server/api-v1/crm-pipelines.js.map +1 -1
- package/src/lib/server/api-v1/crm-tasks.js +75 -33
- package/src/lib/server/api-v1/crm-tasks.js.map +1 -1
- package/src/lib/server/api-v1/openapi.d.ts +0 -11
- package/src/lib/server/api-v1/openapi.js +171 -16
- package/src/lib/server/api-v1/openapi.js.map +1 -1
- package/src/lib/server/assign-contact-owner.d.ts +162 -0
- package/src/lib/server/assign-contact-owner.js +340 -0
- package/src/lib/server/assign-contact-owner.js.map +1 -0
- package/src/lib/server/associate-company-by-domain.d.ts +70 -0
- package/src/lib/server/associate-company-by-domain.js +153 -0
- package/src/lib/server/associate-company-by-domain.js.map +1 -0
- package/src/lib/server/automation-steps.d.ts +139 -0
- package/src/lib/server/automation-steps.js +472 -0
- package/src/lib/server/automation-steps.js.map +1 -0
- package/src/lib/server/capture-contact.js +69 -22
- package/src/lib/server/capture-contact.js.map +1 -1
- package/src/lib/server/capture-host-contact.d.ts +104 -0
- package/src/lib/server/capture-host-contact.js +151 -0
- package/src/lib/server/capture-host-contact.js.map +1 -0
- package/src/lib/server/company-children.d.ts +25 -0
- package/src/lib/server/company-children.js +42 -0
- package/src/lib/server/company-children.js.map +1 -0
- package/src/lib/server/company-delete.d.ts +18 -0
- package/src/lib/server/company-delete.js +44 -21
- package/src/lib/server/company-delete.js.map +1 -1
- package/src/lib/server/contact-email-history.js +21 -19
- package/src/lib/server/contact-email-history.js.map +1 -1
- package/src/lib/server/contact-remove.js +27 -2
- package/src/lib/server/contact-remove.js.map +1 -1
- package/src/lib/server/contact-reports-to.d.ts +61 -0
- package/src/lib/server/contact-reports-to.js +114 -0
- package/src/lib/server/contact-reports-to.js.map +1 -0
- package/src/lib/server/contact-update.d.ts +42 -1
- package/src/lib/server/contact-update.js +204 -23
- package/src/lib/server/contact-update.js.map +1 -1
- package/src/lib/server/convert-host-lead.d.ts +46 -1
- package/src/lib/server/convert-host-lead.js +173 -15
- package/src/lib/server/convert-host-lead.js.map +1 -1
- package/src/lib/server/convert-open-lead.js +12 -5
- package/src/lib/server/convert-open-lead.js.map +1 -1
- package/src/lib/server/crm-record-indexes.d.ts +45 -0
- package/src/lib/server/crm-record-indexes.js +78 -0
- package/src/lib/server/crm-record-indexes.js.map +1 -0
- package/src/lib/server/crm-sharing.d.ts +9 -1
- package/src/lib/server/crm-sharing.js +23 -3
- package/src/lib/server/crm-sharing.js.map +1 -1
- package/src/lib/server/deal-contact-roles.d.ts +27 -0
- package/src/lib/server/deal-contact-roles.js +89 -0
- package/src/lib/server/deal-contact-roles.js.map +1 -0
- package/src/lib/server/lead-convert.d.ts +5 -0
- package/src/lib/server/lead-convert.js +19 -3
- package/src/lib/server/lead-convert.js.map +1 -1
- package/src/lib/server/lead-create.d.ts +17 -2
- package/src/lib/server/lead-create.js +46 -14
- package/src/lib/server/lead-create.js.map +1 -1
- package/src/lib/server/lead-nurturing.d.ts +6 -0
- package/src/lib/server/lead-nurturing.js +57 -20
- package/src/lib/server/lead-nurturing.js.map +1 -1
- package/src/lib/server/lead-picklists.d.ts +35 -0
- package/src/lib/server/lead-picklists.js +52 -0
- package/src/lib/server/lead-picklists.js.map +1 -0
- package/src/lib/server/lead-source-picklist.d.ts +1 -13
- package/src/lib/server/lead-source-picklist.js +6 -33
- package/src/lib/server/lead-source-picklist.js.map +1 -1
- package/src/lib/server/merge-contacts.js +22 -4
- package/src/lib/server/merge-contacts.js.map +1 -1
- package/src/lib/server/person-eraser.d.ts +53 -0
- package/src/lib/server/person-eraser.js +149 -0
- package/src/lib/server/person-eraser.js.map +1 -0
- package/src/lib/server/person-records.d.ts +69 -0
- package/src/lib/server/person-records.js +317 -0
- package/src/lib/server/person-records.js.map +1 -0
- package/src/lib/server/person-refund.d.ts +97 -0
- package/src/lib/server/person-refund.js +221 -0
- package/src/lib/server/person-refund.js.map +1 -0
- package/src/lib/server/picklist-values.d.ts +21 -0
- package/src/lib/server/picklist-values.js +308 -0
- package/src/lib/server/picklist-values.js.map +1 -0
- package/src/lib/server/read-picklist.d.ts +44 -0
- package/src/lib/server/read-picklist.js +65 -0
- package/src/lib/server/read-picklist.js.map +1 -0
- package/src/lib/server/recipe-routes.js +15 -7
- package/src/lib/server/recipe-routes.js.map +1 -1
- package/src/lib/server/record-email-state.js +7 -1
- package/src/lib/server/record-email-state.js.map +1 -1
- package/src/lib/server/record-facts.d.ts +3 -0
- package/src/lib/server/record-facts.js +233 -31
- package/src/lib/server/record-facts.js.map +1 -1
- package/src/lib/server/record-origin.d.ts +27 -0
- package/src/lib/server/record-origin.js +104 -0
- package/src/lib/server/record-origin.js.map +1 -0
- package/src/lib/server/record-timeline.js +15 -2
- package/src/lib/server/record-timeline.js.map +1 -1
- package/src/lib/server/suite-gate.js +4 -3
- package/src/lib/server/suite-gate.js.map +1 -1
- package/src/lib/server/task-routes.js +30 -16
- package/src/lib/server/task-routes.js.map +1 -1
- package/src/lib/server-deal-stage.d.ts +5 -1
- package/src/lib/server-deal-stage.js +10 -5
- package/src/lib/server-deal-stage.js.map +1 -1
- package/src/lib/server.d.ts +8 -3
- package/src/lib/server.js +47 -43
- package/src/lib/server.js.map +1 -1
- package/src/lib/testing/memory-firestore.d.ts +55 -0
- package/src/lib/testing/memory-firestore.js +335 -0
- package/src/lib/testing/memory-firestore.js.map +1 -0
- package/src/lib/testing/stand-in-email-send-index.d.ts +25 -0
- package/src/lib/testing/stand-in-email-send-index.js +49 -0
- package/src/lib/testing/stand-in-email-send-index.js.map +1 -0
- package/src/lib/transfer/activities.d.ts +30 -0
- package/src/lib/transfer/activities.js +132 -0
- package/src/lib/transfer/activities.js.map +1 -0
- package/src/lib/transfer/aliases.d.ts +22 -0
- package/src/lib/transfer/aliases.js +1342 -0
- package/src/lib/transfer/aliases.js.map +1 -0
- package/src/lib/transfer/common.d.ts +222 -0
- package/src/lib/transfer/common.js +832 -0
- package/src/lib/transfer/common.js.map +1 -0
- package/src/lib/transfer/companies.d.ts +43 -0
- package/src/lib/transfer/companies.js +549 -0
- package/src/lib/transfer/companies.js.map +1 -0
- package/src/lib/transfer/contacts.d.ts +31 -0
- package/src/lib/transfer/contacts.js +727 -0
- package/src/lib/transfer/contacts.js.map +1 -0
- package/src/lib/transfer/deals.d.ts +66 -0
- package/src/lib/transfer/deals.js +698 -0
- package/src/lib/transfer/deals.js.map +1 -0
- package/src/lib/transfer/email-templates-package.d.ts +40 -0
- package/src/lib/transfer/email-templates-package.js +76 -0
- package/src/lib/transfer/email-templates-package.js.map +1 -0
- package/src/lib/transfer/email-templates-package.server.d.ts +32 -0
- package/src/lib/transfer/email-templates-package.server.js +176 -0
- package/src/lib/transfer/email-templates-package.server.js.map +1 -0
- package/src/lib/transfer/fields.d.ts +102 -0
- package/src/lib/transfer/fields.js +1755 -0
- package/src/lib/transfer/fields.js.map +1 -0
- package/src/lib/transfer/leads.d.ts +30 -0
- package/src/lib/transfer/leads.js +582 -0
- package/src/lib/transfer/leads.js.map +1 -0
- package/src/lib/transfer/pipelines.d.ts +21 -0
- package/src/lib/transfer/pipelines.js +172 -0
- package/src/lib/transfer/pipelines.js.map +1 -0
- package/src/lib/transfer/register.d.ts +47 -0
- package/src/lib/transfer/register.js +277 -0
- package/src/lib/transfer/register.js.map +1 -0
- package/src/lib/transfer/tasks.d.ts +30 -0
- package/src/lib/transfer/tasks.js +418 -0
- package/src/lib/transfer/tasks.js.map +1 -0
- package/src/lib/transfer/ui.d.ts +20 -0
- package/src/lib/transfer/ui.js +42 -0
- package/src/lib/transfer/ui.js.map +1 -0
- package/src/lib/components/company-import-drawer.d.ts +0 -42
- package/src/lib/components/company-import-drawer.js +0 -99
- package/src/lib/components/company-import-drawer.js.map +0 -1
- package/src/lib/components/contact-import-drawer.d.ts +0 -46
- package/src/lib/components/contact-import-drawer.js +0 -149
- package/src/lib/components/contact-import-drawer.js.map +0 -1
- package/src/lib/components/crm-export-all-button.d.ts +0 -39
- package/src/lib/components/crm-export-all-button.js +0 -98
- package/src/lib/components/crm-export-all-button.js.map +0 -1
- package/src/lib/components/csv-import-drawer.d.ts +0 -139
- package/src/lib/components/csv-import-drawer.js +0 -757
- package/src/lib/components/csv-import-drawer.js.map +0 -1
- package/src/lib/components/deal-import-drawer.d.ts +0 -24
- package/src/lib/components/deal-import-drawer.js +0 -83
- package/src/lib/components/deal-import-drawer.js.map +0 -1
- package/src/lib/components/lead-import-drawer.d.ts +0 -27
- package/src/lib/components/lead-import-drawer.js +0 -118
- package/src/lib/components/lead-import-drawer.js.map +0 -1
- package/src/lib/components/lead-source-values-card.d.ts +0 -27
- package/src/lib/components/lead-source-values-card.js.map +0 -1
- package/src/lib/components/task-import-drawer.d.ts +0 -24
- package/src/lib/components/task-import-drawer.js +0 -82
- package/src/lib/components/task-import-drawer.js.map +0 -1
- package/src/lib/model/companies-csv.d.ts +0 -36
- package/src/lib/model/companies-csv.js +0 -38
- package/src/lib/model/companies-csv.js.map +0 -1
- package/src/lib/model/contacts-csv.d.ts +0 -54
- package/src/lib/model/contacts-csv.js +0 -67
- package/src/lib/model/contacts-csv.js.map +0 -1
- package/src/lib/model/crm-company-import.d.ts +0 -154
- package/src/lib/model/crm-company-import.js +0 -331
- package/src/lib/model/crm-company-import.js.map +0 -1
- package/src/lib/model/crm-csv.d.ts +0 -239
- package/src/lib/model/crm-csv.js +0 -467
- package/src/lib/model/crm-csv.js.map +0 -1
- package/src/lib/model/crm-deal-import.d.ts +0 -112
- package/src/lib/model/crm-deal-import.js +0 -263
- package/src/lib/model/crm-deal-import.js.map +0 -1
- package/src/lib/model/crm-import.d.ts +0 -250
- package/src/lib/model/crm-import.js +0 -467
- package/src/lib/model/crm-import.js.map +0 -1
- package/src/lib/model/crm-lead-import.d.ts +0 -138
- package/src/lib/model/crm-lead-import.js +0 -446
- package/src/lib/model/crm-lead-import.js.map +0 -1
- package/src/lib/model/crm-task-import.d.ts +0 -94
- package/src/lib/model/crm-task-import.js +0 -275
- package/src/lib/model/crm-task-import.js.map +0 -1
- package/src/lib/model/deals-csv.d.ts +0 -32
- package/src/lib/model/deals-csv.js +0 -36
- package/src/lib/model/deals-csv.js.map +0 -1
- package/src/lib/model/import-column-shapes.d.ts +0 -36
- package/src/lib/model/import-column-shapes.js +0 -76
- package/src/lib/model/import-column-shapes.js.map +0 -1
- package/src/lib/model/lead-source-values.d.ts +0 -82
- package/src/lib/model/lead-source-values.js +0 -149
- package/src/lib/model/lead-source-values.js.map +0 -1
- package/src/lib/model/leads-csv.d.ts +0 -40
- package/src/lib/model/leads-csv.js +0 -40
- package/src/lib/model/leads-csv.js.map +0 -1
- package/src/lib/model/tasks-csv.d.ts +0 -28
- package/src/lib/model/tasks-csv.js +0 -28
- package/src/lib/model/tasks-csv.js.map +0 -1
- package/src/lib/server/companies-import.d.ts +0 -57
- package/src/lib/server/companies-import.js +0 -242
- package/src/lib/server/companies-import.js.map +0 -1
- package/src/lib/server/contacts-import.d.ts +0 -109
- package/src/lib/server/contacts-import.js +0 -393
- package/src/lib/server/contacts-import.js.map +0 -1
- package/src/lib/server/deals-import.d.ts +0 -76
- package/src/lib/server/deals-import.js +0 -248
- package/src/lib/server/deals-import.js.map +0 -1
- package/src/lib/server/export-route.d.ts +0 -60
- package/src/lib/server/export-route.js +0 -447
- package/src/lib/server/export-route.js.map +0 -1
- package/src/lib/server/import-context.d.ts +0 -87
- package/src/lib/server/import-context.js +0 -182
- package/src/lib/server/import-context.js.map +0 -1
- package/src/lib/server/lead-source-values.d.ts +0 -50
- package/src/lib/server/lead-source-values.js +0 -191
- package/src/lib/server/lead-source-values.js.map +0 -1
- package/src/lib/server/leads-import.d.ts +0 -84
- package/src/lib/server/leads-import.js +0 -367
- package/src/lib/server/leads-import.js.map +0 -1
- package/src/lib/server/tasks-import.d.ts +0 -54
- package/src/lib/server/tasks-import.js +0 -160
- package/src/lib/server/tasks-import.js.map +0 -1
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { CONTACT_TAG_MAX_LENGTH } from "@aglyn/aglyn/app-utils/crm-kinds";
|
|
17
|
+
/*
|
|
18
|
+
* THE CRM'S RECIPES (AGL-2626).
|
|
19
|
+
*
|
|
20
|
+
* A recipe is a ready-to-edit automation — a trigger, its conditions and an
|
|
21
|
+
* ordered step list — built from the vocabulary the automation editor already
|
|
22
|
+
* offers and handed to that editor as a draft. Nothing is written until the
|
|
23
|
+
* person saves, and every field is theirs to change first: the recipe decides
|
|
24
|
+
* where the editor starts, not what the site runs.
|
|
25
|
+
*
|
|
26
|
+
* The CRM's own, because what each one is FOR is the CRM's: who a new lead is,
|
|
27
|
+
* when a deal is won, when a lead has gone quiet. They reach the automation
|
|
28
|
+
* editor through the platform's recipe seam
|
|
29
|
+
* (`plugin-manager/interaction-recipes`, AGL-3080), registered from this
|
|
30
|
+
* plugin's declarations; the org hub's Recipes card and its install routes
|
|
31
|
+
* read them here. Their ids are declared in `plugins.config.json`
|
|
32
|
+
* (`interactionRecipes`) because a stored automation names one.
|
|
33
|
+
*
|
|
34
|
+
* Definitions only. Three of the four are complete as written. `tagByForm`
|
|
35
|
+
* needs a form, and a form belongs to a site, so the picker that supplies it
|
|
36
|
+
* is the editor's business and host-scoped by nature. That is the seam an
|
|
37
|
+
* org-level mount keeps: the recipes are the same at either level, and only
|
|
38
|
+
* the form picker knows where it is.
|
|
39
|
+
*/ export const CRM_ACTION_RECIPE_IDS = [
|
|
40
|
+
'welcomeNewLead',
|
|
41
|
+
'followUpWonDeal',
|
|
42
|
+
'reengageStaleLead',
|
|
43
|
+
'tagByForm'
|
|
44
|
+
];
|
|
45
|
+
/**
|
|
46
|
+
* The field a resumed wait carries in its scope when it ended on the CLOCK
|
|
47
|
+
* rather than on the event it watched for — the automation engine's word for
|
|
48
|
+
* it, written into the step guard the stale-lead recipe stores.
|
|
49
|
+
*/ const WAIT_TIMED_OUT_FIELD = '_waitTimedOut';
|
|
50
|
+
/**
|
|
51
|
+
* How long the stale-lead recipe holds before it decides a lead has gone
|
|
52
|
+
* quiet. A week: long enough that a rep who is working the lead has had a
|
|
53
|
+
* chance to move its stage, short enough that a lead nobody touched is
|
|
54
|
+
* still warm when the reminder lands.
|
|
55
|
+
*/ export const STALE_LEAD_WAIT_MINUTES = 7 * 24 * 60;
|
|
56
|
+
/**
|
|
57
|
+
* The tag a form's captures get: the form's own name, cut to the tag cap.
|
|
58
|
+
* The name is what the person calls the form, so it is the tag they would
|
|
59
|
+
* have typed; the editor is open to change it before anything is saved.
|
|
60
|
+
*/ export function crmRecipeTagForForm(formName) {
|
|
61
|
+
return formName.trim().slice(0, CONTACT_TAG_MAX_LENGTH);
|
|
62
|
+
}
|
|
63
|
+
export const CRM_ACTION_RECIPES = [
|
|
64
|
+
{
|
|
65
|
+
id: 'welcomeNewLead',
|
|
66
|
+
title: 'Welcome a new lead',
|
|
67
|
+
description: 'When a form makes a new contact: rotate in an owner, book a call for ' + 'tomorrow, send a thank-you, and tag them website.',
|
|
68
|
+
/*
|
|
69
|
+
* The owner first, because the task that follows names no assignee and
|
|
70
|
+
* so goes to whoever owns the contact when it is created — the member
|
|
71
|
+
* the rotation just chose. Round robin rather than a named member: a
|
|
72
|
+
* recipe cannot know who is on the team, and the pool under CRM →
|
|
73
|
+
* Settings is the one place that does. On a workspace with no pool the
|
|
74
|
+
* step fails and the run continues, so the call, the email and the tag
|
|
75
|
+
* still land and the run history says who was not assigned.
|
|
76
|
+
*
|
|
77
|
+
* The email comes before any wait, which makes it an immediate reply to
|
|
78
|
+
* what the visitor just did — transactional, sent from the org's own
|
|
79
|
+
* identity, to the address the event carries.
|
|
80
|
+
*
|
|
81
|
+
* Its words promise no response time. The org hub installs this recipe
|
|
82
|
+
* into a site without its editor opening, so the business never reads
|
|
83
|
+
* the sentence it is sending, and a "within a day" written here would be
|
|
84
|
+
* a commitment made on its behalf that nothing in the run keeps.
|
|
85
|
+
*/ build: ()=>({
|
|
86
|
+
recipe: 'welcomeNewLead',
|
|
87
|
+
name: 'Welcome a new lead',
|
|
88
|
+
trigger: {
|
|
89
|
+
event: 'contactCreated',
|
|
90
|
+
conditions: [
|
|
91
|
+
{
|
|
92
|
+
field: 'source',
|
|
93
|
+
op: 'equals',
|
|
94
|
+
value: 'form'
|
|
95
|
+
}
|
|
96
|
+
],
|
|
97
|
+
combinator: 'and'
|
|
98
|
+
},
|
|
99
|
+
steps: [
|
|
100
|
+
{
|
|
101
|
+
type: 'assignContactOwner',
|
|
102
|
+
roundRobin: true
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
type: 'createCrmTask',
|
|
106
|
+
title: 'Call the new lead',
|
|
107
|
+
kind: 'call',
|
|
108
|
+
dueInDays: 1
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
type: 'sendEmail',
|
|
112
|
+
subject: 'Thanks for getting in touch',
|
|
113
|
+
body: 'Thanks for reaching out. We have your message and will reply ' + 'to this email address.'
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
type: 'addContactTag',
|
|
117
|
+
tag: 'website'
|
|
118
|
+
}
|
|
119
|
+
],
|
|
120
|
+
enabled: true
|
|
121
|
+
})
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
id: 'followUpWonDeal',
|
|
125
|
+
title: 'Follow up a won deal',
|
|
126
|
+
description: 'When a deal is won — which makes the contact a Customer on its own — ' + 'book a check-in call a week out.',
|
|
127
|
+
/*
|
|
128
|
+
* No stage step (AGL-2641): the win itself floors the contact at
|
|
129
|
+
* `customer` before `dealWon` is announced, so a step setting the stage
|
|
130
|
+
* here would at best repeat the write and at worst move an evangelist
|
|
131
|
+
* back to customer — a SET, which is what the step is, and not the
|
|
132
|
+
* floor the win applies. The recipe books the follow-up and nothing
|
|
133
|
+
* else.
|
|
134
|
+
*/ build: ()=>({
|
|
135
|
+
recipe: 'followUpWonDeal',
|
|
136
|
+
name: 'Follow up a won deal',
|
|
137
|
+
trigger: {
|
|
138
|
+
event: 'dealWon'
|
|
139
|
+
},
|
|
140
|
+
steps: [
|
|
141
|
+
{
|
|
142
|
+
type: 'createCrmTask',
|
|
143
|
+
title: 'Check in with the new customer',
|
|
144
|
+
kind: 'call',
|
|
145
|
+
dueInDays: 7
|
|
146
|
+
}
|
|
147
|
+
],
|
|
148
|
+
enabled: true
|
|
149
|
+
})
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
id: 'reengageStaleLead',
|
|
153
|
+
title: 'Re-engage a stale lead',
|
|
154
|
+
description: 'When a contact becomes a lead: wait a week, and if their stage has ' + 'not moved, book a call to bring them back.',
|
|
155
|
+
/*
|
|
156
|
+
* "Unless the stage moved on" is the wait's own event: the flow watches
|
|
157
|
+
* for the next stage change on this person and gives up after a week.
|
|
158
|
+
* Resumed by the clock, the run carries `_waitTimedOut`, and the task
|
|
159
|
+
* step's guard reads it; resumed by the event, the field is absent and
|
|
160
|
+
* the task is skipped — the lead was worked, and nobody is told to
|
|
161
|
+
* re-engage somebody who just moved to Sales qualified.
|
|
162
|
+
*/ build: ()=>({
|
|
163
|
+
recipe: 'reengageStaleLead',
|
|
164
|
+
name: 'Re-engage a stale lead',
|
|
165
|
+
trigger: {
|
|
166
|
+
event: 'contactStageChanged',
|
|
167
|
+
conditions: [
|
|
168
|
+
{
|
|
169
|
+
field: 'lifecycleStage',
|
|
170
|
+
op: 'equals',
|
|
171
|
+
value: 'lead'
|
|
172
|
+
}
|
|
173
|
+
],
|
|
174
|
+
combinator: 'and'
|
|
175
|
+
},
|
|
176
|
+
steps: [
|
|
177
|
+
{
|
|
178
|
+
type: 'waitForEvent',
|
|
179
|
+
eventName: 'contactStageChanged',
|
|
180
|
+
timeoutMinutes: STALE_LEAD_WAIT_MINUTES
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
type: 'createCrmTask',
|
|
184
|
+
title: 'Re-engage a lead that has gone quiet',
|
|
185
|
+
kind: 'call',
|
|
186
|
+
dueInDays: 1,
|
|
187
|
+
when: {
|
|
188
|
+
conditions: [
|
|
189
|
+
{
|
|
190
|
+
field: WAIT_TIMED_OUT_FIELD,
|
|
191
|
+
op: 'notEmpty'
|
|
192
|
+
}
|
|
193
|
+
]
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
],
|
|
197
|
+
enabled: true
|
|
198
|
+
})
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
id: 'tagByForm',
|
|
202
|
+
title: 'Tag by form',
|
|
203
|
+
description: 'When a form you pick makes a new contact: tag them with the form’s ' + 'name.',
|
|
204
|
+
picks: {
|
|
205
|
+
kind: 'form',
|
|
206
|
+
label: 'Form',
|
|
207
|
+
plural: 'forms',
|
|
208
|
+
prompt: 'Pick the form whose new contacts get the tag.',
|
|
209
|
+
none: 'This site has no forms yet. Add one in the besigner, then come back.'
|
|
210
|
+
},
|
|
211
|
+
build: (input)=>{
|
|
212
|
+
var _ref;
|
|
213
|
+
const form = input == null ? void 0 : input.picked;
|
|
214
|
+
return {
|
|
215
|
+
recipe: 'tagByForm',
|
|
216
|
+
name: form ? `Tag ${form.name.trim()} submissions` : 'Tag by form',
|
|
217
|
+
trigger: {
|
|
218
|
+
event: 'contactCreated',
|
|
219
|
+
conditions: [
|
|
220
|
+
{
|
|
221
|
+
field: 'formId',
|
|
222
|
+
op: 'equals',
|
|
223
|
+
value: (_ref = form == null ? void 0 : form.id) != null ? _ref : ''
|
|
224
|
+
}
|
|
225
|
+
],
|
|
226
|
+
combinator: 'and'
|
|
227
|
+
},
|
|
228
|
+
steps: [
|
|
229
|
+
{
|
|
230
|
+
type: 'addContactTag',
|
|
231
|
+
tag: form ? crmRecipeTagForForm(form.name) : ''
|
|
232
|
+
}
|
|
233
|
+
],
|
|
234
|
+
enabled: true
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
];
|
|
239
|
+
/** The recipe with this id, or null for a string that names none. */ export function crmActionRecipe(id) {
|
|
240
|
+
var _CRM_ACTION_RECIPES_find;
|
|
241
|
+
return (_CRM_ACTION_RECIPES_find = CRM_ACTION_RECIPES.find((recipe)=>recipe.id === id)) != null ? _CRM_ACTION_RECIPES_find : null;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
//# sourceMappingURL=crm-recipes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/model/crm-recipes.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CONTACT_TAG_MAX_LENGTH } from '@aglyn/aglyn/app-utils/crm-kinds'\nimport type {\n InteractionRecipe,\n InteractionRecipeInput,\n} from '@aglyn/aglyn/plugin-manager/interaction-recipes'\n\n/*\n * THE CRM'S RECIPES (AGL-2626).\n *\n * A recipe is a ready-to-edit automation — a trigger, its conditions and an\n * ordered step list — built from the vocabulary the automation editor already\n * offers and handed to that editor as a draft. Nothing is written until the\n * person saves, and every field is theirs to change first: the recipe decides\n * where the editor starts, not what the site runs.\n *\n * The CRM's own, because what each one is FOR is the CRM's: who a new lead is,\n * when a deal is won, when a lead has gone quiet. They reach the automation\n * editor through the platform's recipe seam\n * (`plugin-manager/interaction-recipes`, AGL-3080), registered from this\n * plugin's declarations; the org hub's Recipes card and its install routes\n * read them here. Their ids are declared in `plugins.config.json`\n * (`interactionRecipes`) because a stored automation names one.\n *\n * Definitions only. Three of the four are complete as written. `tagByForm`\n * needs a form, and a form belongs to a site, so the picker that supplies it\n * is the editor's business and host-scoped by nature. That is the seam an\n * org-level mount keeps: the recipes are the same at either level, and only\n * the form picker knows where it is.\n */\nexport const CRM_ACTION_RECIPE_IDS = [\n 'welcomeNewLead',\n 'followUpWonDeal',\n 'reengageStaleLead',\n 'tagByForm',\n] as const\n\nexport type CrmActionRecipeId = (typeof CRM_ACTION_RECIPE_IDS)[number]\n\n/** What a recipe is handed before it builds — today only the form `tagByForm` reads. */\nexport type CrmActionRecipeInput = InteractionRecipeInput\n\nexport interface CrmActionRecipe extends InteractionRecipe {\n id: CrmActionRecipeId\n}\n\n/**\n * The field a resumed wait carries in its scope when it ended on the CLOCK\n * rather than on the event it watched for — the automation engine's word for\n * it, written into the step guard the stale-lead recipe stores.\n */\nconst WAIT_TIMED_OUT_FIELD = '_waitTimedOut'\n\n/**\n * How long the stale-lead recipe holds before it decides a lead has gone\n * quiet. A week: long enough that a rep who is working the lead has had a\n * chance to move its stage, short enough that a lead nobody touched is\n * still warm when the reminder lands.\n */\nexport const STALE_LEAD_WAIT_MINUTES = 7 * 24 * 60\n\n/**\n * The tag a form's captures get: the form's own name, cut to the tag cap.\n * The name is what the person calls the form, so it is the tag they would\n * have typed; the editor is open to change it before anything is saved.\n */\nexport function crmRecipeTagForForm(formName: string): string {\n return formName.trim().slice(0, CONTACT_TAG_MAX_LENGTH)\n}\n\nexport const CRM_ACTION_RECIPES: readonly CrmActionRecipe[] = [\n {\n id: 'welcomeNewLead',\n title: 'Welcome a new lead',\n description:\n 'When a form makes a new contact: rotate in an owner, book a call for ' +\n 'tomorrow, send a thank-you, and tag them website.',\n /*\n * The owner first, because the task that follows names no assignee and\n * so goes to whoever owns the contact when it is created — the member\n * the rotation just chose. Round robin rather than a named member: a\n * recipe cannot know who is on the team, and the pool under CRM →\n * Settings is the one place that does. On a workspace with no pool the\n * step fails and the run continues, so the call, the email and the tag\n * still land and the run history says who was not assigned.\n *\n * The email comes before any wait, which makes it an immediate reply to\n * what the visitor just did — transactional, sent from the org's own\n * identity, to the address the event carries.\n *\n * Its words promise no response time. The org hub installs this recipe\n * into a site without its editor opening, so the business never reads\n * the sentence it is sending, and a \"within a day\" written here would be\n * a commitment made on its behalf that nothing in the run keeps.\n */\n build: () => ({\n recipe: 'welcomeNewLead',\n name: 'Welcome a new lead',\n trigger: {\n event: 'contactCreated',\n conditions: [{ field: 'source', op: 'equals', value: 'form' }],\n combinator: 'and',\n },\n steps: [\n { type: 'assignContactOwner', roundRobin: true },\n {\n type: 'createCrmTask',\n title: 'Call the new lead',\n kind: 'call',\n dueInDays: 1,\n },\n {\n type: 'sendEmail',\n subject: 'Thanks for getting in touch',\n body:\n 'Thanks for reaching out. We have your message and will reply ' +\n 'to this email address.',\n },\n { type: 'addContactTag', tag: 'website' },\n ],\n enabled: true,\n }),\n },\n {\n id: 'followUpWonDeal',\n title: 'Follow up a won deal',\n description:\n 'When a deal is won — which makes the contact a Customer on its own — ' +\n 'book a check-in call a week out.',\n /*\n * No stage step (AGL-2641): the win itself floors the contact at\n * `customer` before `dealWon` is announced, so a step setting the stage\n * here would at best repeat the write and at worst move an evangelist\n * back to customer — a SET, which is what the step is, and not the\n * floor the win applies. The recipe books the follow-up and nothing\n * else.\n */\n build: () => ({\n recipe: 'followUpWonDeal',\n name: 'Follow up a won deal',\n trigger: { event: 'dealWon' },\n steps: [\n {\n type: 'createCrmTask',\n title: 'Check in with the new customer',\n kind: 'call',\n dueInDays: 7,\n },\n ],\n enabled: true,\n }),\n },\n {\n id: 'reengageStaleLead',\n title: 'Re-engage a stale lead',\n description:\n 'When a contact becomes a lead: wait a week, and if their stage has ' +\n 'not moved, book a call to bring them back.',\n /*\n * \"Unless the stage moved on\" is the wait's own event: the flow watches\n * for the next stage change on this person and gives up after a week.\n * Resumed by the clock, the run carries `_waitTimedOut`, and the task\n * step's guard reads it; resumed by the event, the field is absent and\n * the task is skipped — the lead was worked, and nobody is told to\n * re-engage somebody who just moved to Sales qualified.\n */\n build: () => ({\n recipe: 'reengageStaleLead',\n name: 'Re-engage a stale lead',\n trigger: {\n event: 'contactStageChanged',\n conditions: [{ field: 'lifecycleStage', op: 'equals', value: 'lead' }],\n combinator: 'and',\n },\n steps: [\n {\n type: 'waitForEvent',\n eventName: 'contactStageChanged',\n timeoutMinutes: STALE_LEAD_WAIT_MINUTES,\n },\n {\n type: 'createCrmTask',\n title: 'Re-engage a lead that has gone quiet',\n kind: 'call',\n dueInDays: 1,\n when: {\n conditions: [{ field: WAIT_TIMED_OUT_FIELD, op: 'notEmpty' }],\n },\n },\n ],\n enabled: true,\n }),\n },\n {\n id: 'tagByForm',\n title: 'Tag by form',\n description:\n 'When a form you pick makes a new contact: tag them with the form’s ' +\n 'name.',\n picks: {\n kind: 'form',\n label: 'Form',\n plural: 'forms',\n prompt: 'Pick the form whose new contacts get the tag.',\n none: 'This site has no forms yet. Add one in the besigner, then come back.',\n },\n build: (input) => {\n const form = input?.picked\n return {\n recipe: 'tagByForm',\n name: form ? `Tag ${form.name.trim()} submissions` : 'Tag by form',\n trigger: {\n event: 'contactCreated',\n conditions: [{ field: 'formId', op: 'equals', value: form?.id ?? '' }],\n combinator: 'and',\n },\n steps: [{ type: 'addContactTag', tag: form ? crmRecipeTagForForm(form.name) : '' }],\n enabled: true,\n }\n },\n },\n]\n\n/** The recipe with this id, or null for a string that names none. */\nexport function crmActionRecipe(id: unknown): CrmActionRecipe | null {\n return CRM_ACTION_RECIPES.find((recipe) => recipe.id === id) ?? null\n}\n"],"names":["CONTACT_TAG_MAX_LENGTH","CRM_ACTION_RECIPE_IDS","WAIT_TIMED_OUT_FIELD","STALE_LEAD_WAIT_MINUTES","crmRecipeTagForForm","formName","trim","slice","CRM_ACTION_RECIPES","id","title","description","build","recipe","name","trigger","event","conditions","field","op","value","combinator","steps","type","roundRobin","kind","dueInDays","subject","body","tag","enabled","eventName","timeoutMinutes","when","picks","label","plural","prompt","none","input","form","picked","crmActionRecipe","find"],"mappings":"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"}
|
|
@@ -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 PluginRecordCountSource } from '@aglyn/aglyn/plugin-manager/plugin-record-counts';
|
|
18
|
+
/**
|
|
19
|
+
* The leads a site CAPTURED (AGL-3275), counted for another plugin's figure
|
|
20
|
+
* (AGL-3080). A lead is one document per person under the organization,
|
|
21
|
+
* stamped in `capturedByHostIds` with every site that met them — so this is
|
|
22
|
+
* exactly the population a site's own lead outcomes are drawn from.
|
|
23
|
+
*
|
|
24
|
+
* Not `visibleTo`: that also names the sites a consent group shares a person
|
|
25
|
+
* with. The price is that a collaborator scoped to one site cannot run the
|
|
26
|
+
* count — the rules prove a lead read by `visibleTo` — and the reader
|
|
27
|
+
* withholds the figure for them.
|
|
28
|
+
*/
|
|
29
|
+
export declare const leadRecordCountSource: PluginRecordCountSource;
|
|
30
|
+
/**
|
|
31
|
+
* The organization's contacts — ONE row per person for every site in it, so
|
|
32
|
+
* the count crosses sites, which the reader is told.
|
|
33
|
+
*/
|
|
34
|
+
export declare const contactRecordCountSource: PluginRecordCountSource;
|
|
35
|
+
/** Called from the console registrar, owner named for a spec that calls it directly. */
|
|
36
|
+
export declare function registerCrmRecordCounts(): void;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { CAPTURED_BY_HOST_FIELD } from "@aglyn/aglyn/app-utils/marketing-consent";
|
|
17
|
+
import { registerPluginRecordCountSource } from "@aglyn/aglyn/plugin-manager/plugin-record-counts";
|
|
18
|
+
import { collection, query, where } from "firebase/firestore";
|
|
19
|
+
import { BUNDLE_ID } from "../constants/bundle-common.js";
|
|
20
|
+
/**
|
|
21
|
+
* The leads a site CAPTURED (AGL-3275), counted for another plugin's figure
|
|
22
|
+
* (AGL-3080). A lead is one document per person under the organization,
|
|
23
|
+
* stamped in `capturedByHostIds` with every site that met them — so this is
|
|
24
|
+
* exactly the population a site's own lead outcomes are drawn from.
|
|
25
|
+
*
|
|
26
|
+
* Not `visibleTo`: that also names the sites a consent group shares a person
|
|
27
|
+
* with. The price is that a collaborator scoped to one site cannot run the
|
|
28
|
+
* count — the rules prove a lead read by `visibleTo` — and the reader
|
|
29
|
+
* withholds the figure for them.
|
|
30
|
+
*/ export const leadRecordCountSource = {
|
|
31
|
+
query (firestore, request) {
|
|
32
|
+
if (!request.orgId || !request.hostId) return null;
|
|
33
|
+
return query(collection(firestore, 'orgs', request.orgId, 'leads'), where(CAPTURED_BY_HOST_FIELD, 'array-contains', request.hostId));
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* The organization's contacts — ONE row per person for every site in it, so
|
|
38
|
+
* the count crosses sites, which the reader is told.
|
|
39
|
+
*/ export const contactRecordCountSource = {
|
|
40
|
+
query (firestore, request) {
|
|
41
|
+
return request.orgId ? collection(firestore, 'orgs', request.orgId, 'contacts') : null;
|
|
42
|
+
},
|
|
43
|
+
crossesSites: true
|
|
44
|
+
};
|
|
45
|
+
/** Called from the console registrar, owner named for a spec that calls it directly. */ export function registerCrmRecordCounts() {
|
|
46
|
+
registerPluginRecordCountSource('lead', leadRecordCountSource, {
|
|
47
|
+
pluginId: BUNDLE_ID
|
|
48
|
+
});
|
|
49
|
+
registerPluginRecordCountSource('contact', contactRecordCountSource, {
|
|
50
|
+
pluginId: BUNDLE_ID
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
//# sourceMappingURL=crm-record-counts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/model/crm-record-counts.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CAPTURED_BY_HOST_FIELD } from '@aglyn/aglyn/app-utils/marketing-consent'\nimport {\n registerPluginRecordCountSource,\n type PluginRecordCountSource,\n} from '@aglyn/aglyn/plugin-manager/plugin-record-counts'\nimport { collection, query, where } from 'firebase/firestore'\nimport { BUNDLE_ID } from '../constants/bundle-common'\n\n/**\n * The leads a site CAPTURED (AGL-3275), counted for another plugin's figure\n * (AGL-3080). A lead is one document per person under the organization,\n * stamped in `capturedByHostIds` with every site that met them — so this is\n * exactly the population a site's own lead outcomes are drawn from.\n *\n * Not `visibleTo`: that also names the sites a consent group shares a person\n * with. The price is that a collaborator scoped to one site cannot run the\n * count — the rules prove a lead read by `visibleTo` — and the reader\n * withholds the figure for them.\n */\nexport const leadRecordCountSource: PluginRecordCountSource = {\n query(firestore, request) {\n if (!request.orgId || !request.hostId) return null\n return query(\n collection(firestore, 'orgs', request.orgId, 'leads'),\n where(CAPTURED_BY_HOST_FIELD, 'array-contains', request.hostId),\n )\n },\n}\n\n/**\n * The organization's contacts — ONE row per person for every site in it, so\n * the count crosses sites, which the reader is told.\n */\nexport const contactRecordCountSource: PluginRecordCountSource = {\n query(firestore, request) {\n return request.orgId ? collection(firestore, 'orgs', request.orgId, 'contacts') : null\n },\n crossesSites: true,\n}\n\n/** Called from the console registrar, owner named for a spec that calls it directly. */\nexport function registerCrmRecordCounts(): void {\n registerPluginRecordCountSource('lead', leadRecordCountSource, { pluginId: BUNDLE_ID })\n registerPluginRecordCountSource('contact', contactRecordCountSource, { pluginId: BUNDLE_ID })\n}\n"],"names":["CAPTURED_BY_HOST_FIELD","registerPluginRecordCountSource","collection","query","where","BUNDLE_ID","leadRecordCountSource","firestore","request","orgId","hostId","contactRecordCountSource","crossesSites","registerCrmRecordCounts","pluginId"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,sBAAsB,QAAQ,2CAA0C;AACjF,SACEC,+BAA+B,QAE1B,mDAAkD;AACzD,SAASC,UAAU,EAAEC,KAAK,EAAEC,KAAK,QAAQ,qBAAoB;AAC7D,SAASC,SAAS,QAAQ,gCAA4B;AAEtD;;;;;;;;;;CAUC,GACD,OAAO,MAAMC,wBAAiD;IAC5DH,OAAMI,SAAS,EAAEC,OAAO;QACtB,IAAI,CAACA,QAAQC,KAAK,IAAI,CAACD,QAAQE,MAAM,EAAE,OAAO;QAC9C,OAAOP,MACLD,WAAWK,WAAW,QAAQC,QAAQC,KAAK,EAAE,UAC7CL,MAAMJ,wBAAwB,kBAAkBQ,QAAQE,MAAM;IAElE;AACF,EAAC;AAED;;;CAGC,GACD,OAAO,MAAMC,2BAAoD;IAC/DR,OAAMI,SAAS,EAAEC,OAAO;QACtB,OAAOA,QAAQC,KAAK,GAAGP,WAAWK,WAAW,QAAQC,QAAQC,KAAK,EAAE,cAAc;IACpF;IACAG,cAAc;AAChB,EAAC;AAED,sFAAsF,GACtF,OAAO,SAASC;IACdZ,gCAAgC,QAAQK,uBAAuB;QAAEQ,UAAUT;IAAU;IACrFJ,gCAAgC,WAAWU,0BAA0B;QAAEG,UAAUT;IAAU;AAC7F"}
|
|
@@ -0,0 +1,23 @@
|
|
|
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 PluginRecordListSource } from '@aglyn/aglyn/plugin-manager/plugin-record-lists';
|
|
18
|
+
export declare const savedViewRecordListSource: PluginRecordListSource;
|
|
19
|
+
export declare const messageTemplateRecordListSource: PluginRecordListSource;
|
|
20
|
+
export declare const contactRecordListSource: PluginRecordListSource;
|
|
21
|
+
export declare const leadRecordListSource: PluginRecordListSource;
|
|
22
|
+
/** Called from the console registrar, owner named for a spec that calls it directly. */
|
|
23
|
+
export declare function registerCrmRecordLists(): void;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { contactDisplayName, normalizeContactEmail } from "@aglyn/aglyn/app-utils/contacts";
|
|
17
|
+
import { CRM_COLLECTIONS, crmLeadDisplayName, crmViewIsListed, isCrmLeadOpen, normalizeCrmViewFilters } from "@aglyn/aglyn/app-utils/crm";
|
|
18
|
+
import { CRM_EMAIL_TEMPLATES_LIMIT, crmEmailTemplateIsListed, normalizeCrmEmailTemplate } from "@aglyn/aglyn/app-utils/crm-email-templates";
|
|
19
|
+
import { dynamicListDimensionsForCrmView } from "@aglyn/aglyn/app-utils/dynamic-list-rule";
|
|
20
|
+
import { nameSearchToken } from "@aglyn/aglyn/app-utils/name-search";
|
|
21
|
+
import { scopeTokensForHost, visibleToHost } from "@aglyn/aglyn/app-utils/scope-tokens";
|
|
22
|
+
import { registerPluginRecordListSource } from "@aglyn/aglyn/plugin-manager/plugin-record-lists";
|
|
23
|
+
import { collection, limit, orderBy, query, where } from "firebase/firestore";
|
|
24
|
+
import { BUNDLE_ID } from "../constants/bundle-common.js";
|
|
25
|
+
/**
|
|
26
|
+
* The CRM's records, FOUND in the console for another plugin's picker
|
|
27
|
+
* (AGL-3080) — a sales sequence choosing whom to enroll and what to send —
|
|
28
|
+
* through `plugin-record-lists`, so the reader never learns where the CRM
|
|
29
|
+
* keeps any of it or which rule its security rules hold a query to.
|
|
30
|
+
*
|
|
31
|
+
* Four kinds, each org-scoped (`orgId` required) and each the query the
|
|
32
|
+
* CRM's own console reads the same records by:
|
|
33
|
+
*
|
|
34
|
+
* `savedView` — the saved views of the Contacts and Leads lists this member
|
|
35
|
+
* may list: their own and the shared ones. Facts:
|
|
36
|
+
* `{ recordKind: 'contact' | 'lead', takeable: boolean }`. `takeable` is
|
|
37
|
+
* whether the view can be taken whole as an audience: a Leads view narrows by
|
|
38
|
+
* status alone, which is always applied; a Contacts view filtering on
|
|
39
|
+
* something only the list itself can apply is not, because dropping that
|
|
40
|
+
* filter would take more people than the view shows.
|
|
41
|
+
*
|
|
42
|
+
* `messageTemplate` — the email templates and snippets this member may list,
|
|
43
|
+
* written for the whole organization or for the named site. Facts:
|
|
44
|
+
* `{ kind: 'template' | 'snippet', subject, body }`.
|
|
45
|
+
*
|
|
46
|
+
* `contact` — a SEARCH of the site's contacts: by address when the text is
|
|
47
|
+
* one, otherwise by a word of the name. None with nothing typed. Named as the
|
|
48
|
+
* site's consent group knows them. Facts: `{ email }`.
|
|
49
|
+
*
|
|
50
|
+
* `lead` — the site's most recently seen leads, newest first. Facts:
|
|
51
|
+
* `{ email, open }`, `open` being a lead nobody has closed or converted.
|
|
52
|
+
*/ const named = (value)=>typeof value === 'string' ? value.trim() : '';
|
|
53
|
+
export const savedViewRecordListSource = {
|
|
54
|
+
query (firestore, request) {
|
|
55
|
+
if (!request.orgId) return null;
|
|
56
|
+
return query(collection(firestore, 'orgs', request.orgId, CRM_COLLECTIONS.views), where('section', 'in', [
|
|
57
|
+
'contacts',
|
|
58
|
+
'leads'
|
|
59
|
+
]), orderBy('name'), limit(request.limit));
|
|
60
|
+
},
|
|
61
|
+
record (id, data, request) {
|
|
62
|
+
var _data_ownerUid, _ref;
|
|
63
|
+
const name = named(data['name']);
|
|
64
|
+
if (!name) return null;
|
|
65
|
+
// A colleague's private view is theirs.
|
|
66
|
+
if (!crmViewIsListed({
|
|
67
|
+
shared: data['shared'] === true,
|
|
68
|
+
ownerUid: String((_data_ownerUid = data['ownerUid']) != null ? _data_ownerUid : '')
|
|
69
|
+
}, (_ref = request == null ? void 0 : request.viewerUid) != null ? _ref : null)) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
const leads = data['section'] === 'leads';
|
|
73
|
+
const takeable = leads || dynamicListDimensionsForCrmView(normalizeCrmViewFilters(data['filters'])).unsupported.length === 0;
|
|
74
|
+
return {
|
|
75
|
+
id,
|
|
76
|
+
name,
|
|
77
|
+
facts: {
|
|
78
|
+
recordKind: leads ? 'lead' : 'contact',
|
|
79
|
+
takeable
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
export const messageTemplateRecordListSource = {
|
|
85
|
+
query (firestore, request) {
|
|
86
|
+
if (!request.orgId) return null;
|
|
87
|
+
return query(collection(firestore, 'orgs', request.orgId, CRM_COLLECTIONS.emailTemplates), orderBy('name'), limit(Math.min(request.limit, CRM_EMAIL_TEMPLATES_LIMIT)));
|
|
88
|
+
},
|
|
89
|
+
record (id, data, request) {
|
|
90
|
+
var _ref;
|
|
91
|
+
const template = normalizeCrmEmailTemplate(data);
|
|
92
|
+
if (!template.name) return null;
|
|
93
|
+
// The team's and this member's own, written for the organization or the site.
|
|
94
|
+
if (!crmEmailTemplateIsListed(template, (_ref = request == null ? void 0 : request.viewerUid) != null ? _ref : null)) return null;
|
|
95
|
+
if ((request == null ? void 0 : request.hostId) && !visibleToHost(template.visibleTo, request.hostId)) return null;
|
|
96
|
+
return {
|
|
97
|
+
id,
|
|
98
|
+
name: template.name,
|
|
99
|
+
facts: {
|
|
100
|
+
kind: template.kind,
|
|
101
|
+
subject: template.subject,
|
|
102
|
+
body: template.body
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
export const contactRecordListSource = {
|
|
108
|
+
query (firestore, request) {
|
|
109
|
+
if (!request.orgId || !request.hostId) return null;
|
|
110
|
+
const email = normalizeContactEmail(request.search);
|
|
111
|
+
const token = email ? '' : nameSearchToken(request.search);
|
|
112
|
+
if (!email && !token) return null;
|
|
113
|
+
const contacts = collection(firestore, 'orgs', request.orgId, 'contacts');
|
|
114
|
+
return email ? query(contacts, where('email', '==', email), limit(request.limit)) : query(contacts, where('nameTokens', 'array-contains', token), orderBy('nameLower'), limit(request.limit));
|
|
115
|
+
},
|
|
116
|
+
record (id, data, request) {
|
|
117
|
+
const hostId = request == null ? void 0 : request.hostId;
|
|
118
|
+
if (hostId && !visibleToHost(data['visibleTo'], hostId)) return null;
|
|
119
|
+
const email = normalizeContactEmail(data['email']);
|
|
120
|
+
const name = contactDisplayName(data, (request == null ? void 0 : request.consentGroupId) || hostId || '').trim() || email || '';
|
|
121
|
+
if (!name) return null;
|
|
122
|
+
return {
|
|
123
|
+
id,
|
|
124
|
+
name,
|
|
125
|
+
facts: {
|
|
126
|
+
email
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
export const leadRecordListSource = {
|
|
132
|
+
query (firestore, request) {
|
|
133
|
+
if (!request.orgId || !request.hostId) return null;
|
|
134
|
+
// The org collection, narrowed to the site (AGL-3275) — unscoped it would
|
|
135
|
+
// offer one agency client another client's people.
|
|
136
|
+
return query(collection(firestore, 'orgs', request.orgId, 'leads'), where('visibleTo', 'array-contains-any', scopeTokensForHost(request.hostId)), orderBy('lastSeenAtMs', 'desc'), limit(request.limit));
|
|
137
|
+
},
|
|
138
|
+
record (id, data) {
|
|
139
|
+
const name = crmLeadDisplayName(data);
|
|
140
|
+
if (!name) return null;
|
|
141
|
+
return {
|
|
142
|
+
id,
|
|
143
|
+
name,
|
|
144
|
+
facts: {
|
|
145
|
+
email: normalizeContactEmail(data['email']),
|
|
146
|
+
open: isCrmLeadOpen(data) && !data['convertedContactId']
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
/** Called from the console registrar, owner named for a spec that calls it directly. */ export function registerCrmRecordLists() {
|
|
152
|
+
const owner = {
|
|
153
|
+
pluginId: BUNDLE_ID
|
|
154
|
+
};
|
|
155
|
+
registerPluginRecordListSource('savedView', savedViewRecordListSource, owner);
|
|
156
|
+
registerPluginRecordListSource('messageTemplate', messageTemplateRecordListSource, owner);
|
|
157
|
+
registerPluginRecordListSource('contact', contactRecordListSource, owner);
|
|
158
|
+
registerPluginRecordListSource('lead', leadRecordListSource, owner);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
//# sourceMappingURL=crm-record-lists.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/model/crm-record-lists.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { contactDisplayName, normalizeContactEmail } from '@aglyn/aglyn/app-utils/contacts'\nimport {\n CRM_COLLECTIONS,\n crmLeadDisplayName,\n crmViewIsListed,\n isCrmLeadOpen,\n normalizeCrmViewFilters,\n} from '@aglyn/aglyn/app-utils/crm'\nimport {\n CRM_EMAIL_TEMPLATES_LIMIT,\n crmEmailTemplateIsListed,\n normalizeCrmEmailTemplate,\n} from '@aglyn/aglyn/app-utils/crm-email-templates'\nimport { dynamicListDimensionsForCrmView } from '@aglyn/aglyn/app-utils/dynamic-list-rule'\nimport { nameSearchToken } from '@aglyn/aglyn/app-utils/name-search'\nimport { scopeTokensForHost, visibleToHost } from '@aglyn/aglyn/app-utils/scope-tokens'\nimport {\n registerPluginRecordListSource,\n type PluginRecordListSource,\n} from '@aglyn/aglyn/plugin-manager/plugin-record-lists'\nimport { collection, limit, orderBy, query, where } from 'firebase/firestore'\nimport { BUNDLE_ID } from '../constants/bundle-common'\n\n/**\n * The CRM's records, FOUND in the console for another plugin's picker\n * (AGL-3080) — a sales sequence choosing whom to enroll and what to send —\n * through `plugin-record-lists`, so the reader never learns where the CRM\n * keeps any of it or which rule its security rules hold a query to.\n *\n * Four kinds, each org-scoped (`orgId` required) and each the query the\n * CRM's own console reads the same records by:\n *\n * `savedView` — the saved views of the Contacts and Leads lists this member\n * may list: their own and the shared ones. Facts:\n * `{ recordKind: 'contact' | 'lead', takeable: boolean }`. `takeable` is\n * whether the view can be taken whole as an audience: a Leads view narrows by\n * status alone, which is always applied; a Contacts view filtering on\n * something only the list itself can apply is not, because dropping that\n * filter would take more people than the view shows.\n *\n * `messageTemplate` — the email templates and snippets this member may list,\n * written for the whole organization or for the named site. Facts:\n * `{ kind: 'template' | 'snippet', subject, body }`.\n *\n * `contact` — a SEARCH of the site's contacts: by address when the text is\n * one, otherwise by a word of the name. None with nothing typed. Named as the\n * site's consent group knows them. Facts: `{ email }`.\n *\n * `lead` — the site's most recently seen leads, newest first. Facts:\n * `{ email, open }`, `open` being a lead nobody has closed or converted.\n */\n\nconst named = (value: unknown): string => (typeof value === 'string' ? value.trim() : '')\n\nexport const savedViewRecordListSource: PluginRecordListSource = {\n query(firestore, request) {\n if (!request.orgId) return null\n return query(\n collection(firestore, 'orgs', request.orgId, CRM_COLLECTIONS.views),\n where('section', 'in', ['contacts', 'leads']),\n orderBy('name'),\n limit(request.limit),\n )\n },\n record(id, data, request) {\n const name = named(data['name'])\n if (!name) return null\n // A colleague's private view is theirs.\n if (\n !crmViewIsListed(\n { shared: data['shared'] === true, ownerUid: String(data['ownerUid'] ?? '') },\n request?.viewerUid ?? null,\n )\n ) {\n return null\n }\n const leads = data['section'] === 'leads'\n const takeable =\n leads || dynamicListDimensionsForCrmView(normalizeCrmViewFilters(data['filters'])).unsupported.length === 0\n return { id, name, facts: { recordKind: leads ? 'lead' : 'contact', takeable } }\n },\n}\n\nexport const messageTemplateRecordListSource: PluginRecordListSource = {\n query(firestore, request) {\n if (!request.orgId) return null\n return query(\n collection(firestore, 'orgs', request.orgId, CRM_COLLECTIONS.emailTemplates),\n orderBy('name'),\n limit(Math.min(request.limit, CRM_EMAIL_TEMPLATES_LIMIT)),\n )\n },\n record(id, data, request) {\n const template = normalizeCrmEmailTemplate(data)\n if (!template.name) return null\n // The team's and this member's own, written for the organization or the site.\n if (!crmEmailTemplateIsListed(template, request?.viewerUid ?? null)) return null\n if (request?.hostId && !visibleToHost(template.visibleTo, request.hostId)) return null\n return {\n id,\n name: template.name,\n facts: { kind: template.kind, subject: template.subject, body: template.body },\n }\n },\n}\n\nexport const contactRecordListSource: PluginRecordListSource = {\n query(firestore, request) {\n if (!request.orgId || !request.hostId) return null\n const email = normalizeContactEmail(request.search)\n const token = email ? '' : nameSearchToken(request.search)\n if (!email && !token) return null\n const contacts = collection(firestore, 'orgs', request.orgId, 'contacts')\n return email\n ? query(contacts, where('email', '==', email), limit(request.limit))\n : query(contacts, where('nameTokens', 'array-contains', token), orderBy('nameLower'), limit(request.limit))\n },\n record(id, data, request) {\n const hostId = request?.hostId\n if (hostId && !visibleToHost(data['visibleTo'] as string[] | undefined, hostId)) return null\n const email = normalizeContactEmail(data['email'])\n const name = contactDisplayName(data, request?.consentGroupId || hostId || '').trim() || email || ''\n if (!name) return null\n return { id, name, facts: { email } }\n },\n}\n\nexport const leadRecordListSource: PluginRecordListSource = {\n query(firestore, request) {\n if (!request.orgId || !request.hostId) return null\n // The org collection, narrowed to the site (AGL-3275) — unscoped it would\n // offer one agency client another client's people.\n return query(\n collection(firestore, 'orgs', request.orgId, 'leads'),\n where('visibleTo', 'array-contains-any', scopeTokensForHost(request.hostId)),\n orderBy('lastSeenAtMs', 'desc'),\n limit(request.limit),\n )\n },\n record(id, data) {\n const name = crmLeadDisplayName(data)\n if (!name) return null\n return {\n id,\n name,\n facts: {\n email: normalizeContactEmail(data['email']),\n open: isCrmLeadOpen(data as never) && !data['convertedContactId'],\n },\n }\n },\n}\n\n/** Called from the console registrar, owner named for a spec that calls it directly. */\nexport function registerCrmRecordLists(): void {\n const owner = { pluginId: BUNDLE_ID }\n registerPluginRecordListSource('savedView', savedViewRecordListSource, owner)\n registerPluginRecordListSource('messageTemplate', messageTemplateRecordListSource, owner)\n registerPluginRecordListSource('contact', contactRecordListSource, owner)\n registerPluginRecordListSource('lead', leadRecordListSource, owner)\n}\n"],"names":["contactDisplayName","normalizeContactEmail","CRM_COLLECTIONS","crmLeadDisplayName","crmViewIsListed","isCrmLeadOpen","normalizeCrmViewFilters","CRM_EMAIL_TEMPLATES_LIMIT","crmEmailTemplateIsListed","normalizeCrmEmailTemplate","dynamicListDimensionsForCrmView","nameSearchToken","scopeTokensForHost","visibleToHost","registerPluginRecordListSource","collection","limit","orderBy","query","where","BUNDLE_ID","named","value","trim","savedViewRecordListSource","firestore","request","orgId","views","record","id","data","name","shared","ownerUid","String","viewerUid","leads","takeable","unsupported","length","facts","recordKind","messageTemplateRecordListSource","emailTemplates","Math","min","template","hostId","visibleTo","kind","subject","body","contactRecordListSource","email","search","token","contacts","consentGroupId","leadRecordListSource","open","registerCrmRecordLists","owner","pluginId"],"mappings":"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"}
|