@aglyn/plugins-crm 1.0.0-beta.221 → 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 +65 -21
- 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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/constants/api-routes.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { CrmActionRecipeId } from '
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/constants/api-routes.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { CrmActionRecipeId } from '../model/crm-recipes'\n\n/**\n * The CRM plugin's console API routes, as the dispatcher keys them.\n *\n * One file both halves import, because the server registers the path and\n * the client fetches it, and a route spelled twice is a route that 404s\n * the day one spelling changes. Client-safe: constants only.\n */\nexport const CRM_API_ROUTES = {\n ping: 'crm/ping',\n /**\n * `POST` — moves one contact to a lifecycle stage, or clears it; see\n * `server.ts`.\n */\n contactStage: 'crm/contact-stage',\n /**\n * `POST` — one holder's profile fields on one or more contacts (AGL-2804):\n * every facet write the console makes; see `server/contact-update.ts`.\n */\n contactUpdate: 'crm/contact-update',\n /**\n * `POST` — lets one or more contacts go from a site, or deletes them once\n * nobody else holds them, keeping every marketing refusal they held\n * (AGL-3338); see `server/contact-remove.ts`.\n */\n contactRemove: 'crm/contact-remove',\n /**\n * `POST` — unlinks the contacts that name a company, then deletes it\n * (AGL-2804); see `server/company-delete.ts`.\n */\n companyDelete: 'crm/company-delete',\n /** `POST` — one email to one person from a record; see `server/email-send.ts`. */\n emailSend: 'crm/email-send',\n /**\n * `POST` — one line in the ORGANIZATION's activity feed for an act the\n * org-level hub performed client-direct (AGL-2634); see\n * `server/org-activity.ts`.\n */\n orgActivity: 'crm/org-activity',\n /**\n * `POST` — installs one recipe's action on one site (AGL-2639): the\n * server builds it, stamps it and writes it into the site's actions;\n * see `server/recipe-routes.ts`.\n */\n recipeInstall: 'crm/recipe-install',\n /**\n * `POST` — which recipes each of the organization's sites carries\n * (AGL-2639), read off the stamps; see `server/recipe-routes.ts`.\n */\n recipeStatus: 'crm/recipe-status',\n /**\n * `POST` — the workspace's email capture address (AGL-2657), minted on\n * the first ask and replaced with `rotate: true`; see\n * `server/inbound-address.ts`.\n */\n inboundAddress: 'crm/inbound-address',\n /**\n * `POST` (a run) or `GET` (the plan) — the daily digest, posted by the\n * scheduler with the cron secret (AGL-2619); a machine's route, see\n * `server/daily-digest-route.ts`.\n */\n dailyDigest: 'crm/daily-digest',\n /**\n * `POST` (a run) or `GET` (the plan) — the hourly task reminders, posted\n * by the scheduler with the cron secret (AGL-2659); a machine's route, see\n * `server/task-reminders-route.ts`.\n */\n taskReminders: 'crm/task-reminders',\n /**\n * `POST` — the mail provider's signed notice of a message received at a\n * capture address (AGL-2657); a machine's route, see\n * `server/inbound-route.ts`.\n */\n inbound: 'crm/inbound',\n} as const\n\n/**\n * One site's answer to `crm/recipe-status` (AGL-2639).\n *\n * `installed` names the recipes a live action on the site is stamped with.\n * `unstamped` counts the live actions that carry NO stamp — every action\n * saved before the stamp existed — and a site with any is one the reader\n * cannot call free of a recipe: the card names it as \"may already have\n * it\" rather than offering the install as if to a blank site.\n */\nexport interface CrmRecipeSiteStatus {\n hostId: string\n installed: CrmActionRecipeId[]\n unstamped: number\n}\n\n/** The body `crm/recipe-install` reads, beside the route scope. */\nexport interface CrmRecipeInstallRequest {\n hostId: string\n orgId?: string\n recipeId: CrmActionRecipeId\n /** The site's form a recipe that picks one (`picks: 'form'`) is keyed on. */\n formId?: string\n}\n\n/** What a successful `crm/recipe-install` answers with. */\nexport interface CrmRecipeInstallResult {\n ok: true\n actionId: string\n /** The installed action's name, as the site's Actions list will show it. */\n name: string\n recipeId: CrmActionRecipeId\n}\n\n/**\n * What an organization-level activity line may point at: the CRM's record\n * kinds, and nothing the org feed holds for its own reasons (a member, a\n * subscription). The route refuses any other type, so a bulk bar cannot\n * write a line the feed's presenter would render as a workspace event.\n */\nexport const CRM_ORG_ACTIVITY_KINDS = [\n 'contact',\n 'company',\n 'deal',\n 'lead',\n 'task',\n] as const\n\nexport type CrmOrgActivityKind = (typeof CRM_ORG_ACTIVITY_KINDS)[number]\n\n/** The most an activity line may say — a sentence, not a report. */\nexport const CRM_ORG_ACTIVITY_ACTION_MAX = 160\n\n/** The browser-side URL for a route key. */\nexport function crmApiUrl(route: keyof typeof CRM_API_ROUTES): string {\n return `/api/${CRM_API_ROUTES[route]}`\n}\n"],"names":["CRM_API_ROUTES","ping","contactStage","contactUpdate","contactRemove","companyDelete","emailSend","orgActivity","recipeInstall","recipeStatus","inboundAddress","dailyDigest","taskReminders","inbound","CRM_ORG_ACTIVITY_KINDS","CRM_ORG_ACTIVITY_ACTION_MAX","crmApiUrl","route"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAID;;;;;;CAMC,GACD,OAAO,MAAMA,iBAAiB;IAC5BC,MAAM;IACN;;;GAGC,GACDC,cAAc;IACd;;;GAGC,GACDC,eAAe;IACf;;;;GAIC,GACDC,eAAe;IACf;;;GAGC,GACDC,eAAe;IACf,gFAAgF,GAChFC,WAAW;IACX;;;;GAIC,GACDC,aAAa;IACb;;;;GAIC,GACDC,eAAe;IACf;;;GAGC,GACDC,cAAc;IACd;;;;GAIC,GACDC,gBAAgB;IAChB;;;;GAIC,GACDC,aAAa;IACb;;;;GAIC,GACDC,eAAe;IACf;;;;GAIC,GACDC,SAAS;AACX,EAAU;AAmCV;;;;;CAKC,GACD,OAAO,MAAMC,yBAAyB;IACpC;IACA;IACA;IACA;IACA;CACD,CAAS;AAIV,kEAAkE,GAClE,OAAO,MAAMC,8BAA8B,IAAG;AAE9C,0CAA0C,GAC1C,OAAO,SAASC,UAAUC,KAAkC;IAC1D,OAAO,CAAC,KAAK,EAAEjB,cAAc,CAACiB,MAAM,EAAE;AACxC"}
|
|
@@ -14,3 +14,9 @@
|
|
|
14
14
|
export declare const BUNDLE_ID = "crm";
|
|
15
15
|
/** The records band's meter in the monthly usage sweep (`server/crm-records-meter.ts`). */
|
|
16
16
|
export declare const CRM_RECORDS_METER_ID = "records";
|
|
17
|
+
/**
|
|
18
|
+
* The automation steps this plugin runs for the workflows engine
|
|
19
|
+
* (`server/automation-steps.ts`), as `plugins.config.json` declares them
|
|
20
|
+
* under `serverSteps`.
|
|
21
|
+
*/
|
|
22
|
+
export declare const CRM_STEP_TYPES: readonly ["setContactStage", "addContactTag", "assignContactOwner", "createCrmTask", "logCrmActivity"];
|
|
@@ -11,5 +11,16 @@
|
|
|
11
11
|
* (AGL-2614), so `contacts` now reads as an unknown id like any other.
|
|
12
12
|
*/ export const BUNDLE_ID = 'crm';
|
|
13
13
|
/** The records band's meter in the monthly usage sweep (`server/crm-records-meter.ts`). */ export const CRM_RECORDS_METER_ID = 'records';
|
|
14
|
+
/**
|
|
15
|
+
* The automation steps this plugin runs for the workflows engine
|
|
16
|
+
* (`server/automation-steps.ts`), as `plugins.config.json` declares them
|
|
17
|
+
* under `serverSteps`.
|
|
18
|
+
*/ export const CRM_STEP_TYPES = [
|
|
19
|
+
'setContactStage',
|
|
20
|
+
'addContactTag',
|
|
21
|
+
'assignContactOwner',
|
|
22
|
+
'createCrmTask',
|
|
23
|
+
'logCrmActivity'
|
|
24
|
+
];
|
|
14
25
|
|
|
15
26
|
//# sourceMappingURL=bundle-common.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/constants/bundle-common.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 */\n\n/**\n * The persisted plugin id (AGL-2595). It was `contacts` while the surface was\n * one list; the stored lists were rewritten and the plugin manager's alias for\n * the old value was retired once that backfill reported nothing left\n * (AGL-2614), so `contacts` now reads as an unknown id like any other.\n */\nexport const BUNDLE_ID = 'crm'\n\n/** The records band's meter in the monthly usage sweep (`server/crm-records-meter.ts`). */\nexport const CRM_RECORDS_METER_ID = 'records'\n"],"names":["BUNDLE_ID","CRM_RECORDS_METER_ID"],"mappings":"AAAA;;;;;;CAMC,GAED;;;;;CAKC,GACD,OAAO,MAAMA,YAAY,MAAK;AAE9B,yFAAyF,GACzF,OAAO,MAAMC,uBAAuB,UAAS"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/constants/bundle-common.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 */\n\n/**\n * The persisted plugin id (AGL-2595). It was `contacts` while the surface was\n * one list; the stored lists were rewritten and the plugin manager's alias for\n * the old value was retired once that backfill reported nothing left\n * (AGL-2614), so `contacts` now reads as an unknown id like any other.\n */\nexport const BUNDLE_ID = 'crm'\n\n/** The records band's meter in the monthly usage sweep (`server/crm-records-meter.ts`). */\nexport const CRM_RECORDS_METER_ID = 'records'\n\n/**\n * The automation steps this plugin runs for the workflows engine\n * (`server/automation-steps.ts`), as `plugins.config.json` declares them\n * under `serverSteps`.\n */\nexport const CRM_STEP_TYPES = [\n 'setContactStage',\n 'addContactTag',\n 'assignContactOwner',\n 'createCrmTask',\n 'logCrmActivity',\n] as const\n"],"names":["BUNDLE_ID","CRM_RECORDS_METER_ID","CRM_STEP_TYPES"],"mappings":"AAAA;;;;;;CAMC,GAED;;;;;CAKC,GACD,OAAO,MAAMA,YAAY,MAAK;AAE9B,yFAAyF,GACzF,OAAO,MAAMC,uBAAuB,UAAS;AAE7C;;;;CAIC,GACD,OAAO,MAAMC,iBAAiB;IAC5B;IACA;IACA;IACA;IACA;CACD,CAAS"}
|
|
@@ -17,6 +17,20 @@
|
|
|
17
17
|
import type { ListFilterField } from '@aglyn/shared-ui-jsx/const/list-filter';
|
|
18
18
|
import type { ListQueryDeclaration, ListQuerySort } from '@aglyn/shared-ui-jsx/const/list-query-plan';
|
|
19
19
|
export declare const COMPANY_LIST_FILTER_FIELDS: readonly ListFilterField[];
|
|
20
|
+
/** The picklist columns the list filters by, each with the list its choices come from. */
|
|
21
|
+
export declare const COMPANY_PICKLIST_FILTERS: readonly [{
|
|
22
|
+
readonly column: "type";
|
|
23
|
+
readonly picklistId: "accountType";
|
|
24
|
+
readonly header: "Type";
|
|
25
|
+
}, {
|
|
26
|
+
readonly column: "industry";
|
|
27
|
+
readonly picklistId: "industry";
|
|
28
|
+
readonly header: "Industry";
|
|
29
|
+
}, {
|
|
30
|
+
readonly column: "rating";
|
|
31
|
+
readonly picklistId: "rating";
|
|
32
|
+
readonly header: "Rating";
|
|
33
|
+
}];
|
|
20
34
|
/**
|
|
21
35
|
* A collaborator's search (see `prefixSearch` in `useCrmListQuery`): the
|
|
22
36
|
* start of the name, beside the scope clause — the prefix range the
|
|
@@ -31,6 +31,12 @@ import { CRM_LIST_SEARCH } from "../model/crm-list-query.js";
|
|
|
31
31
|
* choice from the roster, and "No next activity" asks `nextTaskAtMs ==
|
|
32
32
|
* null`. Each equality rides one `(field, updatedAt DESC)` composite, and
|
|
33
33
|
* one `(field, nameLower ASC)` beside it for the prefix.
|
|
34
|
+
*
|
|
35
|
+
* Type, Industry and Rating (AGL-3514) are choices from the org's lists,
|
|
36
|
+
* asked of the KEY each writer stores beside the label (`typeKey`,
|
|
37
|
+
* `industryKey`, `ratingKey` — see `crmCompanyListFields`): a choice's
|
|
38
|
+
* value is the key, its caption the label, so the query compares what
|
|
39
|
+
* the record keyed rather than how the label happens to be cased.
|
|
34
40
|
*/ export const COMPANY_LIST_FILTER_FIELDS = [
|
|
35
41
|
{
|
|
36
42
|
column: 'name',
|
|
@@ -52,7 +58,51 @@ import { CRM_LIST_SEARCH } from "../model/crm-list-query.js";
|
|
|
52
58
|
'isAnyOf'
|
|
53
59
|
]
|
|
54
60
|
},
|
|
55
|
-
CRM_NEXT_ACTIVITY_FILTER_FIELD
|
|
61
|
+
CRM_NEXT_ACTIVITY_FILTER_FIELD,
|
|
62
|
+
{
|
|
63
|
+
column: 'type',
|
|
64
|
+
kind: 'exact',
|
|
65
|
+
path: 'typeKey',
|
|
66
|
+
operators: [
|
|
67
|
+
'equals',
|
|
68
|
+
'isAnyOf'
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
column: 'industry',
|
|
73
|
+
kind: 'exact',
|
|
74
|
+
path: 'industryKey',
|
|
75
|
+
operators: [
|
|
76
|
+
'equals',
|
|
77
|
+
'isAnyOf'
|
|
78
|
+
]
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
column: 'rating',
|
|
82
|
+
kind: 'exact',
|
|
83
|
+
path: 'ratingKey',
|
|
84
|
+
operators: [
|
|
85
|
+
'equals',
|
|
86
|
+
'isAnyOf'
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
];
|
|
90
|
+
/** The picklist columns the list filters by, each with the list its choices come from. */ export const COMPANY_PICKLIST_FILTERS = [
|
|
91
|
+
{
|
|
92
|
+
column: 'type',
|
|
93
|
+
picklistId: 'accountType',
|
|
94
|
+
header: 'Type'
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
column: 'industry',
|
|
98
|
+
picklistId: 'industry',
|
|
99
|
+
header: 'Industry'
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
column: 'rating',
|
|
103
|
+
picklistId: 'rating',
|
|
104
|
+
header: 'Rating'
|
|
105
|
+
}
|
|
56
106
|
];
|
|
57
107
|
/**
|
|
58
108
|
* A collaborator's search (see `prefixSearch` in `useCrmListQuery`): the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/constants/company-filters.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { ListFilterField } from '@aglyn/shared-ui-jsx/const/list-filter'\n\nimport { CRM_NEXT_ACTIVITY_FILTER_FIELD } from '../components/crm-next-activity-column'\nimport type { ListQueryDeclaration, ListQuerySort } from '@aglyn/shared-ui-jsx/const/list-query-plan'\nimport { CRM_LIST_SEARCH } from '../model/crm-list-query'\n\n/*\n * Companies (`orgs/{orgId}/companies`, read scoped to a host) — every clause\n * and the search word on the list's one query, newest change first\n * (AGL-3321).\n *\n * The search box reads `searchTokens`, the word prefixes of the name and\n * the domain every company writer stamps (`crmCompanyListFields`). Under a\n * site the scope clause (`visibleTo array-contains-any`) is the query's one\n * array clause, and the search folds into it (`scopedSearchTokens`) for a\n * reader who may drop it.\n *\n * The name is also an exact match and a PREFIX range over `nameLower` — the\n * list's one range, which then orders the list by name. The owner is a\n * choice from the roster, and \"No next activity\" asks `nextTaskAtMs ==\n * null`. Each equality rides one `(field, updatedAt DESC)` composite, and\n * one `(field, nameLower ASC)` beside it for the prefix.\n */\nexport const COMPANY_LIST_FILTER_FIELDS: readonly ListFilterField[] = [\n {\n column: 'name',\n kind: 'text',\n path: 'name',\n lowerPath: 'nameLower',\n presence: 'always',\n operators: ['equals', 'startsWith'],\n },\n {\n column: 'ownerUid',\n kind: 'exact',\n path: 'ownerUid',\n operators: ['equals', 'isAnyOf'],\n },\n CRM_NEXT_ACTIVITY_FILTER_FIELD,\n]\n\n/**\n * A collaborator's search (see `prefixSearch` in `useCrmListQuery`): the\n * start of the name, beside the scope clause — the prefix range the\n * `(field, nameLower)` composites already serve.\n */\nexport const COMPANY_PREFIX_SEARCH = {\n field: COMPANY_LIST_FILTER_FIELDS[0],\n notice: 'Search matches the start of a company’s name for access limited to specific sites.',\n}\n\n/** The list's orders: newest change first, and by name under a prefix. */\nexport const COMPANY_LIST_SORTS: readonly ListQuerySort[] = [\n { path: 'updatedAt', direction: 'desc' },\n { path: 'nameLower', direction: 'asc' },\n]\n\nexport const COMPANY_LIST_DECLARATION: ListQueryDeclaration = {\n fields: COMPANY_LIST_FILTER_FIELDS,\n sorts: COMPANY_LIST_SORTS,\n search: CRM_LIST_SEARCH,\n}\n"],"names":["CRM_NEXT_ACTIVITY_FILTER_FIELD","CRM_LIST_SEARCH","COMPANY_LIST_FILTER_FIELDS","column","kind","path","lowerPath","presence","operators","COMPANY_PREFIX_SEARCH","field","notice","COMPANY_LIST_SORTS","direction","COMPANY_LIST_DECLARATION","fields","sorts","search"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAID,SAASA,8BAA8B,QAAQ,4CAAwC;AAEvF,SAASC,eAAe,QAAQ,6BAAyB;AAEzD
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/constants/company-filters.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { ListFilterField } from '@aglyn/shared-ui-jsx/const/list-filter'\n\nimport { CRM_NEXT_ACTIVITY_FILTER_FIELD } from '../components/crm-next-activity-column'\nimport type { ListQueryDeclaration, ListQuerySort } from '@aglyn/shared-ui-jsx/const/list-query-plan'\nimport { CRM_LIST_SEARCH } from '../model/crm-list-query'\n\n/*\n * Companies (`orgs/{orgId}/companies`, read scoped to a host) — every clause\n * and the search word on the list's one query, newest change first\n * (AGL-3321).\n *\n * The search box reads `searchTokens`, the word prefixes of the name and\n * the domain every company writer stamps (`crmCompanyListFields`). Under a\n * site the scope clause (`visibleTo array-contains-any`) is the query's one\n * array clause, and the search folds into it (`scopedSearchTokens`) for a\n * reader who may drop it.\n *\n * The name is also an exact match and a PREFIX range over `nameLower` — the\n * list's one range, which then orders the list by name. The owner is a\n * choice from the roster, and \"No next activity\" asks `nextTaskAtMs ==\n * null`. Each equality rides one `(field, updatedAt DESC)` composite, and\n * one `(field, nameLower ASC)` beside it for the prefix.\n *\n * Type, Industry and Rating (AGL-3514) are choices from the org's lists,\n * asked of the KEY each writer stores beside the label (`typeKey`,\n * `industryKey`, `ratingKey` — see `crmCompanyListFields`): a choice's\n * value is the key, its caption the label, so the query compares what\n * the record keyed rather than how the label happens to be cased.\n */\nexport const COMPANY_LIST_FILTER_FIELDS: readonly ListFilterField[] = [\n {\n column: 'name',\n kind: 'text',\n path: 'name',\n lowerPath: 'nameLower',\n presence: 'always',\n operators: ['equals', 'startsWith'],\n },\n {\n column: 'ownerUid',\n kind: 'exact',\n path: 'ownerUid',\n operators: ['equals', 'isAnyOf'],\n },\n CRM_NEXT_ACTIVITY_FILTER_FIELD,\n { column: 'type', kind: 'exact', path: 'typeKey', operators: ['equals', 'isAnyOf'] },\n { column: 'industry', kind: 'exact', path: 'industryKey', operators: ['equals', 'isAnyOf'] },\n { column: 'rating', kind: 'exact', path: 'ratingKey', operators: ['equals', 'isAnyOf'] },\n]\n\n/** The picklist columns the list filters by, each with the list its choices come from. */\nexport const COMPANY_PICKLIST_FILTERS = [\n { column: 'type', picklistId: 'accountType', header: 'Type' },\n { column: 'industry', picklistId: 'industry', header: 'Industry' },\n { column: 'rating', picklistId: 'rating', header: 'Rating' },\n] as const\n\n/**\n * A collaborator's search (see `prefixSearch` in `useCrmListQuery`): the\n * start of the name, beside the scope clause — the prefix range the\n * `(field, nameLower)` composites already serve.\n */\nexport const COMPANY_PREFIX_SEARCH = {\n field: COMPANY_LIST_FILTER_FIELDS[0],\n notice: 'Search matches the start of a company’s name for access limited to specific sites.',\n}\n\n/** The list's orders: newest change first, and by name under a prefix. */\nexport const COMPANY_LIST_SORTS: readonly ListQuerySort[] = [\n { path: 'updatedAt', direction: 'desc' },\n { path: 'nameLower', direction: 'asc' },\n]\n\nexport const COMPANY_LIST_DECLARATION: ListQueryDeclaration = {\n fields: COMPANY_LIST_FILTER_FIELDS,\n sorts: COMPANY_LIST_SORTS,\n search: CRM_LIST_SEARCH,\n}\n"],"names":["CRM_NEXT_ACTIVITY_FILTER_FIELD","CRM_LIST_SEARCH","COMPANY_LIST_FILTER_FIELDS","column","kind","path","lowerPath","presence","operators","COMPANY_PICKLIST_FILTERS","picklistId","header","COMPANY_PREFIX_SEARCH","field","notice","COMPANY_LIST_SORTS","direction","COMPANY_LIST_DECLARATION","fields","sorts","search"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAID,SAASA,8BAA8B,QAAQ,4CAAwC;AAEvF,SAASC,eAAe,QAAQ,6BAAyB;AAEzD;;;;;;;;;;;;;;;;;;;;;;CAsBC,GACD,OAAO,MAAMC,6BAAyD;IACpE;QACEC,QAAQ;QACRC,MAAM;QACNC,MAAM;QACNC,WAAW;QACXC,UAAU;QACVC,WAAW;YAAC;YAAU;SAAa;IACrC;IACA;QACEL,QAAQ;QACRC,MAAM;QACNC,MAAM;QACNG,WAAW;YAAC;YAAU;SAAU;IAClC;IACAR;IACA;QAAEG,QAAQ;QAAQC,MAAM;QAASC,MAAM;QAAWG,WAAW;YAAC;YAAU;SAAU;IAAC;IACnF;QAAEL,QAAQ;QAAYC,MAAM;QAASC,MAAM;QAAeG,WAAW;YAAC;YAAU;SAAU;IAAC;IAC3F;QAAEL,QAAQ;QAAUC,MAAM;QAASC,MAAM;QAAaG,WAAW;YAAC;YAAU;SAAU;IAAC;CACxF,CAAA;AAED,wFAAwF,GACxF,OAAO,MAAMC,2BAA2B;IACtC;QAAEN,QAAQ;QAAQO,YAAY;QAAeC,QAAQ;IAAO;IAC5D;QAAER,QAAQ;QAAYO,YAAY;QAAYC,QAAQ;IAAW;IACjE;QAAER,QAAQ;QAAUO,YAAY;QAAUC,QAAQ;IAAS;CAC5D,CAAS;AAEV;;;;CAIC,GACD,OAAO,MAAMC,wBAAwB;IACnCC,OAAOX,0BAA0B,CAAC,EAAE;IACpCY,QAAQ;AACV,EAAC;AAED,wEAAwE,GACxE,OAAO,MAAMC,qBAA+C;IAC1D;QAAEV,MAAM;QAAaW,WAAW;IAAO;IACvC;QAAEX,MAAM;QAAaW,WAAW;IAAM;CACvC,CAAA;AAED,OAAO,MAAMC,2BAAiD;IAC5DC,QAAQhB;IACRiB,OAAOJ;IACPK,QAAQnB;AACV,EAAC"}
|
|
@@ -18,6 +18,8 @@ import { type ContactFieldDefinition } from '@aglyn/aglyn';
|
|
|
18
18
|
import type { ListFilterField, ListFilterRequest } from '@aglyn/shared-ui-jsx/const/list-filter';
|
|
19
19
|
import type { ListQueryDeclaration, ListQuerySort } from '@aglyn/shared-ui-jsx/const/list-query-plan';
|
|
20
20
|
import { type CrmClauseAsked } from '../model/crm-list-query';
|
|
21
|
+
/** The column a contact's lead source filters as (AGL-3511). */
|
|
22
|
+
export declare const CONTACT_LEAD_SOURCE_COLUMN = "leadSource";
|
|
21
23
|
/** What the Filters panel offers on a contact, in the grid's grammar. */
|
|
22
24
|
export declare const CONTACT_LIST_FILTER_FIELDS: readonly ListFilterField[];
|
|
23
25
|
/**
|
|
@@ -111,6 +111,7 @@ import { CRM_LIST_SEARCH, crmAnyOf, crmClauseValues } from "../model/crm-list-qu
|
|
|
111
111
|
'contains'
|
|
112
112
|
]
|
|
113
113
|
};
|
|
114
|
+
/** The column a contact's lead source filters as (AGL-3511). */ export const CONTACT_LEAD_SOURCE_COLUMN = 'leadSource';
|
|
114
115
|
/** What the Filters panel offers on a contact, in the grid's grammar. */ export const CONTACT_LIST_FILTER_FIELDS = [
|
|
115
116
|
NAME_FIELD,
|
|
116
117
|
EMAIL_FIELD,
|
|
@@ -224,6 +225,20 @@ import { CRM_LIST_SEARCH, crmAnyOf, crmClauseValues } from "../model/crm-list-qu
|
|
|
224
225
|
]
|
|
225
226
|
},
|
|
226
227
|
/*
|
|
228
|
+
* A holder's lead source (AGL-3511), picked from the org's Lead source
|
|
229
|
+
* values and asked through its `leadSource` facet key — the label as the
|
|
230
|
+
* picklist compares it.
|
|
231
|
+
*/ {
|
|
232
|
+
column: CONTACT_LEAD_SOURCE_COLUMN,
|
|
233
|
+
kind: 'exact',
|
|
234
|
+
path: 'leadSource',
|
|
235
|
+
operators: [
|
|
236
|
+
'equals',
|
|
237
|
+
'isAnyOf',
|
|
238
|
+
'isNotEmpty'
|
|
239
|
+
]
|
|
240
|
+
},
|
|
241
|
+
/*
|
|
227
242
|
* The verdict on the address (AGL-3245), asked through `emailStatus` —
|
|
228
243
|
* `none` for a person nothing has been said about, which is what "is
|
|
229
244
|
* empty" asks.
|
|
@@ -325,6 +340,7 @@ import { CRM_LIST_SEARCH, crmAnyOf, crmClauseValues } from "../model/crm-list-qu
|
|
|
325
340
|
[CRM_CONTACT_VIEW_FIELDS.stage]: 'Stage',
|
|
326
341
|
[CRM_CONTACT_VIEW_FIELDS.source]: 'Source',
|
|
327
342
|
[CRM_CONTACT_VIEW_FIELDS.company]: 'Company',
|
|
343
|
+
[CONTACT_LEAD_SOURCE_COLUMN]: 'Lead source',
|
|
328
344
|
emailState: 'Email'
|
|
329
345
|
};
|
|
330
346
|
/*------------------------------------------
|
|
@@ -428,7 +444,8 @@ export const CONTACT_LIST_DECLARATION = {
|
|
|
428
444
|
[CRM_CONTACT_VIEW_FIELDS.company]: 'company',
|
|
429
445
|
[CRM_CONTACT_VIEW_FIELDS.tags]: 'tag',
|
|
430
446
|
[CRM_CONTACT_VIEW_FIELDS.orders]: 'orders',
|
|
431
|
-
[CRM_CONTACT_VIEW_FIELDS.ltv]: 'ltv'
|
|
447
|
+
[CRM_CONTACT_VIEW_FIELDS.ltv]: 'ltv',
|
|
448
|
+
[CONTACT_LEAD_SOURCE_COLUMN]: 'leadSource'
|
|
432
449
|
};
|
|
433
450
|
/** Why a reader whose access is some sites cannot filter by a holder's field. */ const LIMITED_ACCESS = 'your access is limited to specific sites, which this filter cannot be combined with — an organization administrator can use it';
|
|
434
451
|
/** The facet field a grid column names, or null for one that is not a facet field. */ function facetFieldOf(column) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/constants/contact-filters.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 {\n type ContactFieldDefinition,\n CRM_CONTACT_VIEW_FIELDS,\n CRM_FACET_KEY_ANY_GROUP,\n type CrmFacetKeyField,\n crmContactCustomColumn,\n crmFacetKey,\n crmFacetKeyValue,\n EMAIL_STATE_STATUSES,\n} from '@aglyn/aglyn'\nimport type {\n ListFilterField,\n ListFilterRequest,\n} from '@aglyn/shared-ui-jsx/const/list-filter'\nimport type {\n ListQueryDeclaration,\n ListQuerySort,\n} from '@aglyn/shared-ui-jsx/const/list-query-plan'\nimport {\n CRM_LIST_SEARCH,\n type CrmClauseAsked,\n crmAnyOf,\n crmClauseValues,\n} from '../model/crm-list-query'\n\n/*==========================================\n * CONTACTS (`orgs/{orgId}/contacts`, read scoped to a host) — WHAT THE\n * PANEL OFFERS, AND WHAT THE QUERY ASKS (AGL-3321).\n *\n * Every clause and the search word are on the list's one Firestore query,\n * newest change first, paged by it. A contact is one row shared by every\n * holder, and most of what a holder filters by — the owner, the stage, the\n * sources, the company, the tags, a custom value, whether the person has\n * bought — lives on that holder's FACET, a path per holder no index can\n * reach. So every writer stamps `facetKeys` (`crmContactFacetKeys` in\n * `@aglyn/aglyn`): `{group}:{field}={value}` for each holder's value,\n * `{group}:{field}` for \"has one\", and the same under `*` for any holder.\n * Under a site a facet clause asks the viewing holder's key; at the\n * organization level, any holder's.\n *\n * What one query holds decides the rest:\n *\n * - ONE array clause. The scope clause (`visibleTo array-contains-any`),\n * the search (`searchTokens`), a name word (`nameTokens`), a form\n * (`formIds`) and a facet clause (`facetKeys`) all are one. The search\n * folds into the scope clause for a reader who may drop it; a facet or\n * form clause stands in the scope clause's place for that reader, since\n * only a record the viewing holder keeps carries its key. Two of them\n * at once is refused by name.\n * - ONE range. `updatedAt`, the field the list is ordered by, composes\n * with anything; Created and a name or address prefix order the list by\n * their own field and so stand alone beside the scope clause\n * (`contactSoloClause`), served by the `(visibleTo, field)` composites.\n * - equalities (the address, the site, the verdict, \"No next activity\")\n * compose freely, one `(field, updatedAt DESC)` composite each.\n *\n * The per-holder FIGURES (orders, lifetime value) live on each holder's\n * facet, where no range reaches — a top-level `ordersCount` was never\n * written — so they offer \"is not empty\" alone: \"has bought\".\n *\n * A collaborator's search is the start of the name (`CONTACT_PREFIX_SEARCH`).\n *=========================================*/\n\n/**\n * A word of the canonical name (`nameTokens`), the whole of it, or its start\n * or end — `nameLower`, which every contact carries, and `nameReversed`.\n */\nconst NAME_FIELD: ListFilterField = {\n column: 'name',\n kind: 'text',\n path: 'name',\n lowerPath: 'nameLower',\n tokensPath: 'nameTokens',\n reversedPath: 'nameReversed',\n operators: ['contains', 'equals', 'startsWith', 'endsWith'],\n}\n\n/**\n * The address. `normalizeContactEmail` lower-cases before every write, so the\n * stored value IS its own normalized key.\n */\nconst EMAIL_FIELD: ListFilterField = {\n column: 'email',\n kind: 'text',\n path: 'email',\n lowerPath: 'email',\n presence: 'always',\n operators: ['equals', 'startsWith'],\n}\n\n/** When the person was first captured — a range the list is then ordered by. */\nconst CREATED_FIELD: ListFilterField = {\n column: CRM_CONTACT_VIEW_FIELDS.createdAt,\n kind: 'date',\n path: 'createdAt',\n presence: 'always',\n operators: ['is', 'after', 'onOrAfter', 'before', 'onOrBefore'],\n}\n\n/**\n * The forms a person came in through — the top-level `formIds` mirror of the\n * interactions' `formId` (AGL-2612), matched whole and as typed because a\n * form id is minted with mixed case. The form's own page links here with its\n * id; nobody types one.\n */\nconst FORM_FIELD: ListFilterField = {\n column: 'formIds',\n kind: 'text',\n path: 'formIds',\n tokensPath: 'formIds',\n verbatimTokens: true,\n operators: ['contains'],\n}\n\n/** What the Filters panel offers on a contact, in the grid's grammar. */\nexport const CONTACT_LIST_FILTER_FIELDS: readonly ListFilterField[] = [\n NAME_FIELD,\n EMAIL_FIELD,\n {\n // A holder's tags, matched whole and case-insensitively.\n column: CRM_CONTACT_VIEW_FIELDS.tags,\n kind: 'exact',\n path: 'tags',\n operators: ['contains', 'isAnyOf', 'isNotEmpty'],\n },\n FORM_FIELD,\n { column: 'hostId', kind: 'exact', path: 'hostId', presence: 'always' },\n // Per-holder figures: \"has bought\", never a range across holders.\n {\n column: CRM_CONTACT_VIEW_FIELDS.orders,\n kind: 'number',\n path: 'ordersCount',\n operators: ['isNotEmpty'],\n },\n {\n column: CRM_CONTACT_VIEW_FIELDS.ltv,\n kind: 'number',\n path: 'ltvCents',\n operators: ['isNotEmpty'],\n },\n CREATED_FIELD,\n {\n column: CRM_CONTACT_VIEW_FIELDS.updatedAt,\n kind: 'date',\n path: 'updatedAt',\n presence: 'always',\n operators: ['is', 'after', 'onOrAfter', 'before', 'onOrBefore'],\n },\n /*\n * \"No next activity\" (AGL-2661): `nextTaskAtMs == null`, which every\n * contact carries from its creation until a task is scheduled against it.\n */\n {\n column: 'nextTaskAtMs',\n kind: 'date',\n path: 'nextTaskAtMs',\n presence: 'nullable',\n storedAs: 'millis',\n operators: ['isEmpty'],\n },\n /*\n * THE FACET FIELDS (AGL-2617). The values are picked, not typed — a uid\n * from the roster, a stage from the fixed list, a source from its labels,\n * a company from the picker — so `equals` and `isAnyOf` are what they\n * offer; the section supplies the choices. The column names are the\n * contract `CRM_CONTACT_VIEW_FIELDS` states, because the dynamic-list\n * translator reads a view by them. The paths are the flattened ROW'S.\n */\n {\n column: CRM_CONTACT_VIEW_FIELDS.owner,\n kind: 'exact',\n path: 'ownerUid',\n operators: ['equals', 'isAnyOf', 'isNotEmpty'],\n },\n {\n column: CRM_CONTACT_VIEW_FIELDS.stage,\n kind: 'exact',\n path: 'lifecycleStage',\n operators: ['equals', 'isAnyOf', 'isNotEmpty'],\n },\n {\n // The `sources` presence map, matched on its keys — see `keysOf`.\n column: CRM_CONTACT_VIEW_FIELDS.source,\n kind: 'exact',\n path: 'sources',\n keysOf: true,\n operators: ['equals', 'isAnyOf'],\n },\n {\n column: CRM_CONTACT_VIEW_FIELDS.company,\n kind: 'exact',\n path: 'companyId',\n operators: ['equals', 'isNotEmpty'],\n },\n /*\n * The verdict on the address (AGL-3245), asked through `emailStatus` —\n * `none` for a person nothing has been said about, which is what \"is\n * empty\" asks.\n */\n {\n column: 'emailState',\n kind: 'exact',\n path: 'emailState.status',\n presence: 'nullable',\n operators: ['equals', 'isAnyOf', 'isEmpty', 'isNotEmpty'],\n },\n]\n\n/**\n * One filter field per active custom contact field (AGL-2617). A custom\n * value lives under the holder's `custom` map, so it is asked through the\n * holder's facet key: equality on a picked, typed or ticked value, and\n * \"is not empty\". Its column is the one the table shows it under —\n * `crmContactCustomColumn(key)` — so a clause and a column agree on the name.\n */\nexport function contactCustomFilterFields(\n definitions: readonly Pick<\n ContactFieldDefinition,\n 'key' | 'type' | 'retiredAt'\n >[],\n): ListFilterField[] {\n return definitions\n .filter((definition) => !definition.retiredAt)\n .map((definition): ListFilterField => {\n const column = crmContactCustomColumn(definition.key)\n const path = `custom.${definition.key}`\n switch (definition.type) {\n case 'number':\n return {\n column,\n kind: 'number',\n path,\n operators: ['=', 'isNotEmpty'],\n }\n case 'date':\n return { column, kind: 'date', path, operators: ['isNotEmpty'] }\n case 'checkbox':\n return { column, kind: 'boolean', path }\n case 'select':\n return {\n column,\n kind: 'exact',\n path,\n operators: ['equals', 'isAnyOf', 'isNotEmpty'],\n }\n default:\n return {\n column,\n kind: 'text',\n path,\n lowerPath: path,\n operators: ['equals', 'isNotEmpty'],\n }\n }\n })\n}\n\n/** The custom fields' headers, keyed by the column each filters as. */\nexport function contactCustomFilterHeaders(\n definitions: readonly Pick<ContactFieldDefinition, 'key' | 'label'>[],\n): Record<string, string> {\n return Object.fromEntries(\n definitions.map((definition) => [\n crmContactCustomColumn(definition.key),\n definition.label || definition.key,\n ]),\n )\n}\n\n/**\n * How every filterable contact field reads — on a chip, in the add-filter\n * picker, and as the header of a filter-only hidden column.\n */\nexport const CONTACT_LIST_FILTER_HEADERS: Readonly<Record<string, string>> = {\n name: 'Contact',\n email: 'Email',\n tags: 'Tags',\n formIds: 'Form ID',\n hostId: 'Site ID',\n ordersCount: 'Orders',\n ltvCents: 'Lifetime value (cents)',\n createdAt: 'Created',\n updatedAt: 'Updated',\n nextTaskAtMs: 'Next activity',\n [CRM_CONTACT_VIEW_FIELDS.owner]: 'Owner',\n [CRM_CONTACT_VIEW_FIELDS.stage]: 'Stage',\n [CRM_CONTACT_VIEW_FIELDS.source]: 'Source',\n [CRM_CONTACT_VIEW_FIELDS.company]: 'Company',\n emailState: 'Email',\n}\n\n/*------------------------------------------\n * THE QUERY THE CONTACTS LIST RUNS.\n *-----------------------------------------*/\n\n/** The list's one order: newest change first. */\nexport const CONTACT_LIST_SORTS: readonly ListQuerySort[] = [\n { path: 'updatedAt', direction: 'desc' },\n]\n\n/** The stored array a facet clause is asked of. */\nexport const CONTACT_FACET_KEYS_PATH = 'facetKeys'\n\n/** The fields the Contacts query asks — each a stored field, as named. */\nexport const CONTACT_QUERY_FIELDS: readonly ListFilterField[] = [\n NAME_FIELD,\n EMAIL_FIELD,\n FORM_FIELD,\n CREATED_FIELD,\n { column: 'hostId', kind: 'exact', path: 'hostId', presence: 'always' },\n {\n column: 'updatedAt',\n kind: 'date',\n path: 'updatedAt',\n presence: 'always',\n operators: ['is', 'after', 'onOrAfter', 'before', 'onOrBefore'],\n },\n {\n column: 'nextTaskAtMs',\n kind: 'date',\n path: 'nextTaskAtMs',\n presence: 'nullable',\n storedAs: 'millis',\n operators: ['isEmpty'],\n },\n {\n column: 'emailStatus',\n kind: 'exact',\n path: 'emailStatus',\n operators: ['equals', 'isAnyOf'],\n },\n {\n column: CONTACT_FACET_KEYS_PATH,\n kind: 'exact',\n path: CONTACT_FACET_KEYS_PATH,\n tokensPath: CONTACT_FACET_KEYS_PATH,\n operators: ['contains', 'isAnyOf'],\n },\n]\n\nexport const CONTACT_LIST_DECLARATION: ListQueryDeclaration = {\n fields: CONTACT_QUERY_FIELDS,\n sorts: CONTACT_LIST_SORTS,\n search: CRM_LIST_SEARCH,\n}\n\n/** The grid fields a stored clause passes to the query unchanged. */\nconst AS_STORED = new Set([\n 'name',\n 'email',\n 'formIds',\n 'hostId',\n 'createdAt',\n 'updatedAt',\n 'nextTaskAtMs',\n])\n\n/**\n * The ranges that stand ALONE beside the scope clause: Created and a name\n * or address prefix each order the list by their own field, which the\n * `(visibleTo, field)` composites serve and nothing beside them. Updated is\n * not one: the list is ordered by it already, so it composes freely.\n */\nexport function contactSoloClause(clause: ListFilterRequest): boolean {\n if (clause.field === 'createdAt') return true\n if (clause.field === 'name') return clause.op === 'startsWith' || clause.op === 'endsWith'\n return clause.field === 'email' && clause.op === 'startsWith'\n}\n\n/**\n * A collaborator's search (see `prefixSearch` in `useCrmListQuery`): the\n * start of the contact's name, beside the scope clause.\n */\nexport const CONTACT_PREFIX_SEARCH = {\n field: NAME_FIELD,\n notice: 'Search matches the start of a contact’s name for access limited to specific sites.',\n}\n\n/** The facet field each grid column is keyed under. */\nconst FACET_FIELDS: Readonly<Record<string, CrmFacetKeyField>> = {\n [CRM_CONTACT_VIEW_FIELDS.owner]: 'owner',\n [CRM_CONTACT_VIEW_FIELDS.stage]: 'stage',\n [CRM_CONTACT_VIEW_FIELDS.source]: 'source',\n [CRM_CONTACT_VIEW_FIELDS.company]: 'company',\n [CRM_CONTACT_VIEW_FIELDS.tags]: 'tag',\n [CRM_CONTACT_VIEW_FIELDS.orders]: 'orders',\n [CRM_CONTACT_VIEW_FIELDS.ltv]: 'ltv',\n}\n\n/** Who is asking: the viewing holder, and whether their query may drop the scope clause. */\nexport interface ContactQueryReader {\n /** The viewing holder's group id under a site; `null` at the organization level. */\n groupId: string | null\n /**\n * Whether the reader's query may stand a facet or form clause in the\n * scope clause's place (`useCrmFoldsScope`) — always at the organization\n * level, where there is no scope clause.\n */\n foldsScope: boolean\n}\n\n/** Why a reader whose access is some sites cannot filter by a holder's field. */\nconst LIMITED_ACCESS =\n 'your access is limited to specific sites, which this filter cannot be combined with — an organization administrator can use it'\n\n/** The facet field a grid column names, or null for one that is not a facet field. */\nfunction facetFieldOf(column: string): CrmFacetKeyField | null {\n if (FACET_FIELDS[column]) return FACET_FIELDS[column]\n if (column.startsWith(CRM_CONTACT_VIEW_FIELDS.custom)) {\n return `custom.${column.slice(CRM_CONTACT_VIEW_FIELDS.custom.length)}`\n }\n return null\n}\n\n/**\n * One grid clause as the clause the Contacts query asks — see the block at\n * the top. A clause a query cannot serve for this reader is refused by\n * name, never matched over the rows a page loaded.\n */\nexport function contactQueryClause(\n clause: ListFilterRequest,\n reader: ContactQueryReader,\n): CrmClauseAsked {\n if (AS_STORED.has(clause.field)) {\n if (clause.field === 'formIds' && !reader.foldsScope)\n return { refused: LIMITED_ACCESS }\n return { field: clause.field, op: clause.op, value: clause.value }\n }\n if (clause.field === 'emailState') {\n if (clause.op === 'isEmpty')\n return { field: 'emailStatus', op: 'equals', value: 'none' }\n if (clause.op === 'isNotEmpty')\n return crmAnyOf('emailStatus', EMAIL_STATE_STATUSES)\n const values = crmClauseValues(clause)\n if (\n values.some(\n (value) => !(EMAIL_STATE_STATUSES as readonly string[]).includes(value),\n )\n ) {\n return { refused: 'pick one of the choices' }\n }\n return crmAnyOf('emailStatus', values)\n }\n const facet = facetFieldOf(clause.field)\n if (!facet) return { refused: 'this list does not filter by that' }\n if (!reader.foldsScope) return { refused: LIMITED_ACCESS }\n const group = reader.groupId ?? CRM_FACET_KEY_ANY_GROUP\n if (clause.op === 'isNotEmpty') {\n return {\n field: CONTACT_FACET_KEYS_PATH,\n op: 'contains',\n value: crmFacetKey(group, facet),\n }\n }\n const keys: string[] = []\n for (const value of crmClauseValues(clause)) {\n const key = crmFacetKeyValue(clause.op === '=' ? Number(value) : value)\n if (key === null) return { refused: 'no value yet' }\n keys.push(crmFacetKey(group, facet, key))\n }\n const asked = crmAnyOf(CONTACT_FACET_KEYS_PATH, keys)\n // One key is an `array-contains`; several, an `array-contains-any`.\n return 'refused' in asked || asked.op === 'isAnyOf'\n ? asked\n : { ...asked, op: 'contains' }\n}\n\n/**\n * Whether an asked clause stands in for the scope clause: a facet key of\n * the viewing holder, which only a record that holder keeps carries, or a\n * form, which only its site's captures carry.\n */\nexport function contactClauseImpliesScope(groupId: string | null) {\n return (clause: ListFilterRequest): boolean => {\n if (clause.field === 'formIds') return true\n if (clause.field !== CONTACT_FACET_KEYS_PATH || !groupId) return false\n return crmClauseValues(clause).every((key) => key.startsWith(`${groupId}:`))\n }\n}\n\n/**\n * The query shapes the Contacts list can send, as declarations the index\n * pin enumerates (AGL-3321): the list's own, without its solo ranges, whose\n * every equality rides `(field, updatedAt DESC)`; and each solo range beside\n * the scope clause alone.\n */\nexport function contactIndexShapes(): ListQueryDeclaration[] {\n const soloOps = new Set(['startsWith', 'endsWith'])\n const main: ListQueryDeclaration = {\n ...CONTACT_LIST_DECLARATION,\n fields: CONTACT_QUERY_FIELDS.filter((field) => field !== CREATED_FIELD).map((field) =>\n field.operators\n ? { ...field, operators: field.operators.filter((op) => !soloOps.has(op)) }\n : field,\n ),\n }\n const solo: ListQueryDeclaration = {\n fields: [\n { ...NAME_FIELD, operators: ['startsWith', 'endsWith'] },\n { ...EMAIL_FIELD, operators: ['startsWith'] },\n CREATED_FIELD,\n ],\n sorts: [],\n }\n return [main, solo]\n}\n"],"names":["CRM_CONTACT_VIEW_FIELDS","CRM_FACET_KEY_ANY_GROUP","crmContactCustomColumn","crmFacetKey","crmFacetKeyValue","EMAIL_STATE_STATUSES","CRM_LIST_SEARCH","crmAnyOf","crmClauseValues","NAME_FIELD","column","kind","path","lowerPath","tokensPath","reversedPath","operators","EMAIL_FIELD","presence","CREATED_FIELD","createdAt","FORM_FIELD","verbatimTokens","CONTACT_LIST_FILTER_FIELDS","tags","orders","ltv","updatedAt","storedAs","owner","stage","source","keysOf","company","contactCustomFilterFields","definitions","filter","definition","retiredAt","map","key","type","contactCustomFilterHeaders","Object","fromEntries","label","CONTACT_LIST_FILTER_HEADERS","name","email","formIds","hostId","ordersCount","ltvCents","nextTaskAtMs","emailState","CONTACT_LIST_SORTS","direction","CONTACT_FACET_KEYS_PATH","CONTACT_QUERY_FIELDS","CONTACT_LIST_DECLARATION","fields","sorts","search","AS_STORED","Set","contactSoloClause","clause","field","op","CONTACT_PREFIX_SEARCH","notice","FACET_FIELDS","LIMITED_ACCESS","facetFieldOf","startsWith","custom","slice","length","contactQueryClause","reader","has","foldsScope","refused","value","values","some","includes","facet","group","groupId","keys","Number","push","asked","contactClauseImpliesScope","every","contactIndexShapes","soloOps","main","solo"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAEEA,uBAAuB,EACvBC,uBAAuB,EAEvBC,sBAAsB,EACtBC,WAAW,EACXC,gBAAgB,EAChBC,oBAAoB,QACf,eAAc;AASrB,SACEC,eAAe,EAEfC,QAAQ,EACRC,eAAe,QACV,6BAAyB;AAEhC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2CAoC2C,GAE3C;;;CAGC,GACD,MAAMC,aAA8B;IAClCC,QAAQ;IACRC,MAAM;IACNC,MAAM;IACNC,WAAW;IACXC,YAAY;IACZC,cAAc;IACdC,WAAW;QAAC;QAAY;QAAU;QAAc;KAAW;AAC7D;AAEA;;;CAGC,GACD,MAAMC,cAA+B;IACnCP,QAAQ;IACRC,MAAM;IACNC,MAAM;IACNC,WAAW;IACXK,UAAU;IACVF,WAAW;QAAC;QAAU;KAAa;AACrC;AAEA,8EAA8E,GAC9E,MAAMG,gBAAiC;IACrCT,QAAQV,wBAAwBoB,SAAS;IACzCT,MAAM;IACNC,MAAM;IACNM,UAAU;IACVF,WAAW;QAAC;QAAM;QAAS;QAAa;QAAU;KAAa;AACjE;AAEA;;;;;CAKC,GACD,MAAMK,aAA8B;IAClCX,QAAQ;IACRC,MAAM;IACNC,MAAM;IACNE,YAAY;IACZQ,gBAAgB;IAChBN,WAAW;QAAC;KAAW;AACzB;AAEA,uEAAuE,GACvE,OAAO,MAAMO,6BAAyD;IACpEd;IACAQ;IACA;QACE,yDAAyD;QACzDP,QAAQV,wBAAwBwB,IAAI;QACpCb,MAAM;QACNC,MAAM;QACNI,WAAW;YAAC;YAAY;YAAW;SAAa;IAClD;IACAK;IACA;QAAEX,QAAQ;QAAUC,MAAM;QAASC,MAAM;QAAUM,UAAU;IAAS;IACtE,kEAAkE;IAClE;QACER,QAAQV,wBAAwByB,MAAM;QACtCd,MAAM;QACNC,MAAM;QACNI,WAAW;YAAC;SAAa;IAC3B;IACA;QACEN,QAAQV,wBAAwB0B,GAAG;QACnCf,MAAM;QACNC,MAAM;QACNI,WAAW;YAAC;SAAa;IAC3B;IACAG;IACA;QACET,QAAQV,wBAAwB2B,SAAS;QACzChB,MAAM;QACNC,MAAM;QACNM,UAAU;QACVF,WAAW;YAAC;YAAM;YAAS;YAAa;YAAU;SAAa;IACjE;IACA;;;GAGC,GACD;QACEN,QAAQ;QACRC,MAAM;QACNC,MAAM;QACNM,UAAU;QACVU,UAAU;QACVZ,WAAW;YAAC;SAAU;IACxB;IACA;;;;;;;GAOC,GACD;QACEN,QAAQV,wBAAwB6B,KAAK;QACrClB,MAAM;QACNC,MAAM;QACNI,WAAW;YAAC;YAAU;YAAW;SAAa;IAChD;IACA;QACEN,QAAQV,wBAAwB8B,KAAK;QACrCnB,MAAM;QACNC,MAAM;QACNI,WAAW;YAAC;YAAU;YAAW;SAAa;IAChD;IACA;QACE,kEAAkE;QAClEN,QAAQV,wBAAwB+B,MAAM;QACtCpB,MAAM;QACNC,MAAM;QACNoB,QAAQ;QACRhB,WAAW;YAAC;YAAU;SAAU;IAClC;IACA;QACEN,QAAQV,wBAAwBiC,OAAO;QACvCtB,MAAM;QACNC,MAAM;QACNI,WAAW;YAAC;YAAU;SAAa;IACrC;IACA;;;;GAIC,GACD;QACEN,QAAQ;QACRC,MAAM;QACNC,MAAM;QACNM,UAAU;QACVF,WAAW;YAAC;YAAU;YAAW;YAAW;SAAa;IAC3D;CACD,CAAA;AAED;;;;;;CAMC,GACD,OAAO,SAASkB,0BACdC,WAGG;IAEH,OAAOA,YACJC,MAAM,CAAC,CAACC,aAAe,CAACA,WAAWC,SAAS,EAC5CC,GAAG,CAAC,CAACF;QACJ,MAAM3B,SAASR,uBAAuBmC,WAAWG,GAAG;QACpD,MAAM5B,OAAO,CAAC,OAAO,EAAEyB,WAAWG,GAAG,EAAE;QACvC,OAAQH,WAAWI,IAAI;YACrB,KAAK;gBACH,OAAO;oBACL/B;oBACAC,MAAM;oBACNC;oBACAI,WAAW;wBAAC;wBAAK;qBAAa;gBAChC;YACF,KAAK;gBACH,OAAO;oBAAEN;oBAAQC,MAAM;oBAAQC;oBAAMI,WAAW;wBAAC;qBAAa;gBAAC;YACjE,KAAK;gBACH,OAAO;oBAAEN;oBAAQC,MAAM;oBAAWC;gBAAK;YACzC,KAAK;gBACH,OAAO;oBACLF;oBACAC,MAAM;oBACNC;oBACAI,WAAW;wBAAC;wBAAU;wBAAW;qBAAa;gBAChD;YACF;gBACE,OAAO;oBACLN;oBACAC,MAAM;oBACNC;oBACAC,WAAWD;oBACXI,WAAW;wBAAC;wBAAU;qBAAa;gBACrC;QACJ;IACF;AACJ;AAEA,qEAAqE,GACrE,OAAO,SAAS0B,2BACdP,WAAqE;IAErE,OAAOQ,OAAOC,WAAW,CACvBT,YAAYI,GAAG,CAAC,CAACF,aAAe;YAC9BnC,uBAAuBmC,WAAWG,GAAG;YACrCH,WAAWQ,KAAK,IAAIR,WAAWG,GAAG;SACnC;AAEL;AAEA;;;CAGC,GACD,OAAO,MAAMM,8BAAgE;IAC3EC,MAAM;IACNC,OAAO;IACPxB,MAAM;IACNyB,SAAS;IACTC,QAAQ;IACRC,aAAa;IACbC,UAAU;IACVhC,WAAW;IACXO,WAAW;IACX0B,cAAc;IACd,CAACrD,wBAAwB6B,KAAK,CAAC,EAAE;IACjC,CAAC7B,wBAAwB8B,KAAK,CAAC,EAAE;IACjC,CAAC9B,wBAAwB+B,MAAM,CAAC,EAAE;IAClC,CAAC/B,wBAAwBiC,OAAO,CAAC,EAAE;IACnCqB,YAAY;AACd,EAAC;AAED;;2CAE2C,GAE3C,+CAA+C,GAC/C,OAAO,MAAMC,qBAA+C;IAC1D;QAAE3C,MAAM;QAAa4C,WAAW;IAAO;CACxC,CAAA;AAED,iDAAiD,GACjD,OAAO,MAAMC,0BAA0B,YAAW;AAElD,wEAAwE,GACxE,OAAO,MAAMC,uBAAmD;IAC9DjD;IACAQ;IACAI;IACAF;IACA;QAAET,QAAQ;QAAUC,MAAM;QAASC,MAAM;QAAUM,UAAU;IAAS;IACtE;QACER,QAAQ;QACRC,MAAM;QACNC,MAAM;QACNM,UAAU;QACVF,WAAW;YAAC;YAAM;YAAS;YAAa;YAAU;SAAa;IACjE;IACA;QACEN,QAAQ;QACRC,MAAM;QACNC,MAAM;QACNM,UAAU;QACVU,UAAU;QACVZ,WAAW;YAAC;SAAU;IACxB;IACA;QACEN,QAAQ;QACRC,MAAM;QACNC,MAAM;QACNI,WAAW;YAAC;YAAU;SAAU;IAClC;IACA;QACEN,QAAQ+C;QACR9C,MAAM;QACNC,MAAM6C;QACN3C,YAAY2C;QACZzC,WAAW;YAAC;YAAY;SAAU;IACpC;CACD,CAAA;AAED,OAAO,MAAM2C,2BAAiD;IAC5DC,QAAQF;IACRG,OAAON;IACPO,QAAQxD;AACV,EAAC;AAED,mEAAmE,GACnE,MAAMyD,YAAY,IAAIC,IAAI;IACxB;IACA;IACA;IACA;IACA;IACA;IACA;CACD;AAED;;;;;CAKC,GACD,OAAO,SAASC,kBAAkBC,MAAyB;IACzD,IAAIA,OAAOC,KAAK,KAAK,aAAa,OAAO;IACzC,IAAID,OAAOC,KAAK,KAAK,QAAQ,OAAOD,OAAOE,EAAE,KAAK,gBAAgBF,OAAOE,EAAE,KAAK;IAChF,OAAOF,OAAOC,KAAK,KAAK,WAAWD,OAAOE,EAAE,KAAK;AACnD;AAEA;;;CAGC,GACD,OAAO,MAAMC,wBAAwB;IACnCF,OAAO1D;IACP6D,QAAQ;AACV,EAAC;AAED,qDAAqD,GACrD,MAAMC,eAA2D;IAC/D,CAACvE,wBAAwB6B,KAAK,CAAC,EAAE;IACjC,CAAC7B,wBAAwB8B,KAAK,CAAC,EAAE;IACjC,CAAC9B,wBAAwB+B,MAAM,CAAC,EAAE;IAClC,CAAC/B,wBAAwBiC,OAAO,CAAC,EAAE;IACnC,CAACjC,wBAAwBwB,IAAI,CAAC,EAAE;IAChC,CAACxB,wBAAwByB,MAAM,CAAC,EAAE;IAClC,CAACzB,wBAAwB0B,GAAG,CAAC,EAAE;AACjC;AAcA,+EAA+E,GAC/E,MAAM8C,iBACJ;AAEF,oFAAoF,GACpF,SAASC,aAAa/D,MAAc;IAClC,IAAI6D,YAAY,CAAC7D,OAAO,EAAE,OAAO6D,YAAY,CAAC7D,OAAO;IACrD,IAAIA,OAAOgE,UAAU,CAAC1E,wBAAwB2E,MAAM,GAAG;QACrD,OAAO,CAAC,OAAO,EAAEjE,OAAOkE,KAAK,CAAC5E,wBAAwB2E,MAAM,CAACE,MAAM,GAAG;IACxE;IACA,OAAO;AACT;AAEA;;;;CAIC,GACD,OAAO,SAASC,mBACdZ,MAAyB,EACzBa,MAA0B;QAyBZA;IAvBd,IAAIhB,UAAUiB,GAAG,CAACd,OAAOC,KAAK,GAAG;QAC/B,IAAID,OAAOC,KAAK,KAAK,aAAa,CAACY,OAAOE,UAAU,EAClD,OAAO;YAAEC,SAASV;QAAe;QACnC,OAAO;YAAEL,OAAOD,OAAOC,KAAK;YAAEC,IAAIF,OAAOE,EAAE;YAAEe,OAAOjB,OAAOiB,KAAK;QAAC;IACnE;IACA,IAAIjB,OAAOC,KAAK,KAAK,cAAc;QACjC,IAAID,OAAOE,EAAE,KAAK,WAChB,OAAO;YAAED,OAAO;YAAeC,IAAI;YAAUe,OAAO;QAAO;QAC7D,IAAIjB,OAAOE,EAAE,KAAK,cAChB,OAAO7D,SAAS,eAAeF;QACjC,MAAM+E,SAAS5E,gBAAgB0D;QAC/B,IACEkB,OAAOC,IAAI,CACT,CAACF,QAAU,CAAC,AAAC9E,qBAA2CiF,QAAQ,CAACH,SAEnE;YACA,OAAO;gBAAED,SAAS;YAA0B;QAC9C;QACA,OAAO3E,SAAS,eAAe6E;IACjC;IACA,MAAMG,QAAQd,aAAaP,OAAOC,KAAK;IACvC,IAAI,CAACoB,OAAO,OAAO;QAAEL,SAAS;IAAoC;IAClE,IAAI,CAACH,OAAOE,UAAU,EAAE,OAAO;QAAEC,SAASV;IAAe;IACzD,MAAMgB,SAAQT,kBAAAA,OAAOU,OAAO,YAAdV,kBAAkB9E;IAChC,IAAIiE,OAAOE,EAAE,KAAK,cAAc;QAC9B,OAAO;YACLD,OAAOV;YACPW,IAAI;YACJe,OAAOhF,YAAYqF,OAAOD;QAC5B;IACF;IACA,MAAMG,OAAiB,EAAE;IACzB,KAAK,MAAMP,SAAS3E,gBAAgB0D,QAAS;QAC3C,MAAM1B,MAAMpC,iBAAiB8D,OAAOE,EAAE,KAAK,MAAMuB,OAAOR,SAASA;QACjE,IAAI3C,QAAQ,MAAM,OAAO;YAAE0C,SAAS;QAAe;QACnDQ,KAAKE,IAAI,CAACzF,YAAYqF,OAAOD,OAAO/C;IACtC;IACA,MAAMqD,QAAQtF,SAASkD,yBAAyBiC;IAChD,oEAAoE;IACpE,OAAO,aAAaG,SAASA,MAAMzB,EAAE,KAAK,YACtCyB,QACA,aAAKA;QAAOzB,IAAI;;AACtB;AAEA;;;;CAIC,GACD,OAAO,SAAS0B,0BAA0BL,OAAsB;IAC9D,OAAO,CAACvB;QACN,IAAIA,OAAOC,KAAK,KAAK,WAAW,OAAO;QACvC,IAAID,OAAOC,KAAK,KAAKV,2BAA2B,CAACgC,SAAS,OAAO;QACjE,OAAOjF,gBAAgB0D,QAAQ6B,KAAK,CAAC,CAACvD,MAAQA,IAAIkC,UAAU,CAAC,GAAGe,QAAQ,CAAC,CAAC;IAC5E;AACF;AAEA;;;;;CAKC,GACD,OAAO,SAASO;IACd,MAAMC,UAAU,IAAIjC,IAAI;QAAC;QAAc;KAAW;IAClD,MAAMkC,OAA6B,aAC9BvC;QACHC,QAAQF,qBAAqBtB,MAAM,CAAC,CAAC+B,QAAUA,UAAUhD,eAAeoB,GAAG,CAAC,CAAC4B,QAC3EA,MAAMnD,SAAS,GACX,aAAKmD;gBAAOnD,WAAWmD,MAAMnD,SAAS,CAACoB,MAAM,CAAC,CAACgC,KAAO,CAAC6B,QAAQjB,GAAG,CAACZ;iBACnED;;IAGR,MAAMgC,OAA6B;QACjCvC,QAAQ;YACN,aAAKnD;gBAAYO,WAAW;oBAAC;oBAAc;iBAAW;;YACtD,aAAKC;gBAAaD,WAAW;oBAAC;iBAAa;;YAC3CG;SACD;QACD0C,OAAO,EAAE;IACX;IACA,OAAO;QAACqC;QAAMC;KAAK;AACrB"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/constants/contact-filters.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 {\n type ContactFieldDefinition,\n CRM_CONTACT_VIEW_FIELDS,\n CRM_FACET_KEY_ANY_GROUP,\n type CrmFacetKeyField,\n crmContactCustomColumn,\n crmFacetKey,\n crmFacetKeyValue,\n EMAIL_STATE_STATUSES,\n} from '@aglyn/aglyn'\nimport type {\n ListFilterField,\n ListFilterRequest,\n} from '@aglyn/shared-ui-jsx/const/list-filter'\nimport type {\n ListQueryDeclaration,\n ListQuerySort,\n} from '@aglyn/shared-ui-jsx/const/list-query-plan'\nimport {\n CRM_LIST_SEARCH,\n type CrmClauseAsked,\n crmAnyOf,\n crmClauseValues,\n} from '../model/crm-list-query'\n\n/*==========================================\n * CONTACTS (`orgs/{orgId}/contacts`, read scoped to a host) — WHAT THE\n * PANEL OFFERS, AND WHAT THE QUERY ASKS (AGL-3321).\n *\n * Every clause and the search word are on the list's one Firestore query,\n * newest change first, paged by it. A contact is one row shared by every\n * holder, and most of what a holder filters by — the owner, the stage, the\n * sources, the company, the tags, a custom value, whether the person has\n * bought — lives on that holder's FACET, a path per holder no index can\n * reach. So every writer stamps `facetKeys` (`crmContactFacetKeys` in\n * `@aglyn/aglyn`): `{group}:{field}={value}` for each holder's value,\n * `{group}:{field}` for \"has one\", and the same under `*` for any holder.\n * Under a site a facet clause asks the viewing holder's key; at the\n * organization level, any holder's.\n *\n * What one query holds decides the rest:\n *\n * - ONE array clause. The scope clause (`visibleTo array-contains-any`),\n * the search (`searchTokens`), a name word (`nameTokens`), a form\n * (`formIds`) and a facet clause (`facetKeys`) all are one. The search\n * folds into the scope clause for a reader who may drop it; a facet or\n * form clause stands in the scope clause's place for that reader, since\n * only a record the viewing holder keeps carries its key. Two of them\n * at once is refused by name.\n * - ONE range. `updatedAt`, the field the list is ordered by, composes\n * with anything; Created and a name or address prefix order the list by\n * their own field and so stand alone beside the scope clause\n * (`contactSoloClause`), served by the `(visibleTo, field)` composites.\n * - equalities (the address, the site, the verdict, \"No next activity\")\n * compose freely, one `(field, updatedAt DESC)` composite each.\n *\n * The per-holder FIGURES (orders, lifetime value) live on each holder's\n * facet, where no range reaches — a top-level `ordersCount` was never\n * written — so they offer \"is not empty\" alone: \"has bought\".\n *\n * A collaborator's search is the start of the name (`CONTACT_PREFIX_SEARCH`).\n *=========================================*/\n\n/**\n * A word of the canonical name (`nameTokens`), the whole of it, or its start\n * or end — `nameLower`, which every contact carries, and `nameReversed`.\n */\nconst NAME_FIELD: ListFilterField = {\n column: 'name',\n kind: 'text',\n path: 'name',\n lowerPath: 'nameLower',\n tokensPath: 'nameTokens',\n reversedPath: 'nameReversed',\n operators: ['contains', 'equals', 'startsWith', 'endsWith'],\n}\n\n/**\n * The address. `normalizeContactEmail` lower-cases before every write, so the\n * stored value IS its own normalized key.\n */\nconst EMAIL_FIELD: ListFilterField = {\n column: 'email',\n kind: 'text',\n path: 'email',\n lowerPath: 'email',\n presence: 'always',\n operators: ['equals', 'startsWith'],\n}\n\n/** When the person was first captured — a range the list is then ordered by. */\nconst CREATED_FIELD: ListFilterField = {\n column: CRM_CONTACT_VIEW_FIELDS.createdAt,\n kind: 'date',\n path: 'createdAt',\n presence: 'always',\n operators: ['is', 'after', 'onOrAfter', 'before', 'onOrBefore'],\n}\n\n/**\n * The forms a person came in through — the top-level `formIds` mirror of the\n * interactions' `formId` (AGL-2612), matched whole and as typed because a\n * form id is minted with mixed case. The form's own page links here with its\n * id; nobody types one.\n */\nconst FORM_FIELD: ListFilterField = {\n column: 'formIds',\n kind: 'text',\n path: 'formIds',\n tokensPath: 'formIds',\n verbatimTokens: true,\n operators: ['contains'],\n}\n\n/** The column a contact's lead source filters as (AGL-3511). */\nexport const CONTACT_LEAD_SOURCE_COLUMN = 'leadSource'\n\n/** What the Filters panel offers on a contact, in the grid's grammar. */\nexport const CONTACT_LIST_FILTER_FIELDS: readonly ListFilterField[] = [\n NAME_FIELD,\n EMAIL_FIELD,\n {\n // A holder's tags, matched whole and case-insensitively.\n column: CRM_CONTACT_VIEW_FIELDS.tags,\n kind: 'exact',\n path: 'tags',\n operators: ['contains', 'isAnyOf', 'isNotEmpty'],\n },\n FORM_FIELD,\n { column: 'hostId', kind: 'exact', path: 'hostId', presence: 'always' },\n // Per-holder figures: \"has bought\", never a range across holders.\n {\n column: CRM_CONTACT_VIEW_FIELDS.orders,\n kind: 'number',\n path: 'ordersCount',\n operators: ['isNotEmpty'],\n },\n {\n column: CRM_CONTACT_VIEW_FIELDS.ltv,\n kind: 'number',\n path: 'ltvCents',\n operators: ['isNotEmpty'],\n },\n CREATED_FIELD,\n {\n column: CRM_CONTACT_VIEW_FIELDS.updatedAt,\n kind: 'date',\n path: 'updatedAt',\n presence: 'always',\n operators: ['is', 'after', 'onOrAfter', 'before', 'onOrBefore'],\n },\n /*\n * \"No next activity\" (AGL-2661): `nextTaskAtMs == null`, which every\n * contact carries from its creation until a task is scheduled against it.\n */\n {\n column: 'nextTaskAtMs',\n kind: 'date',\n path: 'nextTaskAtMs',\n presence: 'nullable',\n storedAs: 'millis',\n operators: ['isEmpty'],\n },\n /*\n * THE FACET FIELDS (AGL-2617). The values are picked, not typed — a uid\n * from the roster, a stage from the fixed list, a source from its labels,\n * a company from the picker — so `equals` and `isAnyOf` are what they\n * offer; the section supplies the choices. The column names are the\n * contract `CRM_CONTACT_VIEW_FIELDS` states, because the dynamic-list\n * translator reads a view by them. The paths are the flattened ROW'S.\n */\n {\n column: CRM_CONTACT_VIEW_FIELDS.owner,\n kind: 'exact',\n path: 'ownerUid',\n operators: ['equals', 'isAnyOf', 'isNotEmpty'],\n },\n {\n column: CRM_CONTACT_VIEW_FIELDS.stage,\n kind: 'exact',\n path: 'lifecycleStage',\n operators: ['equals', 'isAnyOf', 'isNotEmpty'],\n },\n {\n // The `sources` presence map, matched on its keys — see `keysOf`.\n column: CRM_CONTACT_VIEW_FIELDS.source,\n kind: 'exact',\n path: 'sources',\n keysOf: true,\n operators: ['equals', 'isAnyOf'],\n },\n {\n column: CRM_CONTACT_VIEW_FIELDS.company,\n kind: 'exact',\n path: 'companyId',\n operators: ['equals', 'isNotEmpty'],\n },\n /*\n * A holder's lead source (AGL-3511), picked from the org's Lead source\n * values and asked through its `leadSource` facet key — the label as the\n * picklist compares it.\n */\n {\n column: CONTACT_LEAD_SOURCE_COLUMN,\n kind: 'exact',\n path: 'leadSource',\n operators: ['equals', 'isAnyOf', 'isNotEmpty'],\n },\n /*\n * The verdict on the address (AGL-3245), asked through `emailStatus` —\n * `none` for a person nothing has been said about, which is what \"is\n * empty\" asks.\n */\n {\n column: 'emailState',\n kind: 'exact',\n path: 'emailState.status',\n presence: 'nullable',\n operators: ['equals', 'isAnyOf', 'isEmpty', 'isNotEmpty'],\n },\n]\n\n/**\n * One filter field per active custom contact field (AGL-2617). A custom\n * value lives under the holder's `custom` map, so it is asked through the\n * holder's facet key: equality on a picked, typed or ticked value, and\n * \"is not empty\". Its column is the one the table shows it under —\n * `crmContactCustomColumn(key)` — so a clause and a column agree on the name.\n */\nexport function contactCustomFilterFields(\n definitions: readonly Pick<\n ContactFieldDefinition,\n 'key' | 'type' | 'retiredAt'\n >[],\n): ListFilterField[] {\n return definitions\n .filter((definition) => !definition.retiredAt)\n .map((definition): ListFilterField => {\n const column = crmContactCustomColumn(definition.key)\n const path = `custom.${definition.key}`\n switch (definition.type) {\n case 'number':\n return {\n column,\n kind: 'number',\n path,\n operators: ['=', 'isNotEmpty'],\n }\n case 'date':\n return { column, kind: 'date', path, operators: ['isNotEmpty'] }\n case 'checkbox':\n return { column, kind: 'boolean', path }\n case 'select':\n return {\n column,\n kind: 'exact',\n path,\n operators: ['equals', 'isAnyOf', 'isNotEmpty'],\n }\n default:\n return {\n column,\n kind: 'text',\n path,\n lowerPath: path,\n operators: ['equals', 'isNotEmpty'],\n }\n }\n })\n}\n\n/** The custom fields' headers, keyed by the column each filters as. */\nexport function contactCustomFilterHeaders(\n definitions: readonly Pick<ContactFieldDefinition, 'key' | 'label'>[],\n): Record<string, string> {\n return Object.fromEntries(\n definitions.map((definition) => [\n crmContactCustomColumn(definition.key),\n definition.label || definition.key,\n ]),\n )\n}\n\n/**\n * How every filterable contact field reads — on a chip, in the add-filter\n * picker, and as the header of a filter-only hidden column.\n */\nexport const CONTACT_LIST_FILTER_HEADERS: Readonly<Record<string, string>> = {\n name: 'Contact',\n email: 'Email',\n tags: 'Tags',\n formIds: 'Form ID',\n hostId: 'Site ID',\n ordersCount: 'Orders',\n ltvCents: 'Lifetime value (cents)',\n createdAt: 'Created',\n updatedAt: 'Updated',\n nextTaskAtMs: 'Next activity',\n [CRM_CONTACT_VIEW_FIELDS.owner]: 'Owner',\n [CRM_CONTACT_VIEW_FIELDS.stage]: 'Stage',\n [CRM_CONTACT_VIEW_FIELDS.source]: 'Source',\n [CRM_CONTACT_VIEW_FIELDS.company]: 'Company',\n [CONTACT_LEAD_SOURCE_COLUMN]: 'Lead source',\n emailState: 'Email',\n}\n\n/*------------------------------------------\n * THE QUERY THE CONTACTS LIST RUNS.\n *-----------------------------------------*/\n\n/** The list's one order: newest change first. */\nexport const CONTACT_LIST_SORTS: readonly ListQuerySort[] = [\n { path: 'updatedAt', direction: 'desc' },\n]\n\n/** The stored array a facet clause is asked of. */\nexport const CONTACT_FACET_KEYS_PATH = 'facetKeys'\n\n/** The fields the Contacts query asks — each a stored field, as named. */\nexport const CONTACT_QUERY_FIELDS: readonly ListFilterField[] = [\n NAME_FIELD,\n EMAIL_FIELD,\n FORM_FIELD,\n CREATED_FIELD,\n { column: 'hostId', kind: 'exact', path: 'hostId', presence: 'always' },\n {\n column: 'updatedAt',\n kind: 'date',\n path: 'updatedAt',\n presence: 'always',\n operators: ['is', 'after', 'onOrAfter', 'before', 'onOrBefore'],\n },\n {\n column: 'nextTaskAtMs',\n kind: 'date',\n path: 'nextTaskAtMs',\n presence: 'nullable',\n storedAs: 'millis',\n operators: ['isEmpty'],\n },\n {\n column: 'emailStatus',\n kind: 'exact',\n path: 'emailStatus',\n operators: ['equals', 'isAnyOf'],\n },\n {\n column: CONTACT_FACET_KEYS_PATH,\n kind: 'exact',\n path: CONTACT_FACET_KEYS_PATH,\n tokensPath: CONTACT_FACET_KEYS_PATH,\n operators: ['contains', 'isAnyOf'],\n },\n]\n\nexport const CONTACT_LIST_DECLARATION: ListQueryDeclaration = {\n fields: CONTACT_QUERY_FIELDS,\n sorts: CONTACT_LIST_SORTS,\n search: CRM_LIST_SEARCH,\n}\n\n/** The grid fields a stored clause passes to the query unchanged. */\nconst AS_STORED = new Set([\n 'name',\n 'email',\n 'formIds',\n 'hostId',\n 'createdAt',\n 'updatedAt',\n 'nextTaskAtMs',\n])\n\n/**\n * The ranges that stand ALONE beside the scope clause: Created and a name\n * or address prefix each order the list by their own field, which the\n * `(visibleTo, field)` composites serve and nothing beside them. Updated is\n * not one: the list is ordered by it already, so it composes freely.\n */\nexport function contactSoloClause(clause: ListFilterRequest): boolean {\n if (clause.field === 'createdAt') return true\n if (clause.field === 'name') return clause.op === 'startsWith' || clause.op === 'endsWith'\n return clause.field === 'email' && clause.op === 'startsWith'\n}\n\n/**\n * A collaborator's search (see `prefixSearch` in `useCrmListQuery`): the\n * start of the contact's name, beside the scope clause.\n */\nexport const CONTACT_PREFIX_SEARCH = {\n field: NAME_FIELD,\n notice: 'Search matches the start of a contact’s name for access limited to specific sites.',\n}\n\n/** The facet field each grid column is keyed under. */\nconst FACET_FIELDS: Readonly<Record<string, CrmFacetKeyField>> = {\n [CRM_CONTACT_VIEW_FIELDS.owner]: 'owner',\n [CRM_CONTACT_VIEW_FIELDS.stage]: 'stage',\n [CRM_CONTACT_VIEW_FIELDS.source]: 'source',\n [CRM_CONTACT_VIEW_FIELDS.company]: 'company',\n [CRM_CONTACT_VIEW_FIELDS.tags]: 'tag',\n [CRM_CONTACT_VIEW_FIELDS.orders]: 'orders',\n [CRM_CONTACT_VIEW_FIELDS.ltv]: 'ltv',\n [CONTACT_LEAD_SOURCE_COLUMN]: 'leadSource',\n}\n\n/** Who is asking: the viewing holder, and whether their query may drop the scope clause. */\nexport interface ContactQueryReader {\n /** The viewing holder's group id under a site; `null` at the organization level. */\n groupId: string | null\n /**\n * Whether the reader's query may stand a facet or form clause in the\n * scope clause's place (`useCrmFoldsScope`) — always at the organization\n * level, where there is no scope clause.\n */\n foldsScope: boolean\n}\n\n/** Why a reader whose access is some sites cannot filter by a holder's field. */\nconst LIMITED_ACCESS =\n 'your access is limited to specific sites, which this filter cannot be combined with — an organization administrator can use it'\n\n/** The facet field a grid column names, or null for one that is not a facet field. */\nfunction facetFieldOf(column: string): CrmFacetKeyField | null {\n if (FACET_FIELDS[column]) return FACET_FIELDS[column]\n if (column.startsWith(CRM_CONTACT_VIEW_FIELDS.custom)) {\n return `custom.${column.slice(CRM_CONTACT_VIEW_FIELDS.custom.length)}`\n }\n return null\n}\n\n/**\n * One grid clause as the clause the Contacts query asks — see the block at\n * the top. A clause a query cannot serve for this reader is refused by\n * name, never matched over the rows a page loaded.\n */\nexport function contactQueryClause(\n clause: ListFilterRequest,\n reader: ContactQueryReader,\n): CrmClauseAsked {\n if (AS_STORED.has(clause.field)) {\n if (clause.field === 'formIds' && !reader.foldsScope)\n return { refused: LIMITED_ACCESS }\n return { field: clause.field, op: clause.op, value: clause.value }\n }\n if (clause.field === 'emailState') {\n if (clause.op === 'isEmpty')\n return { field: 'emailStatus', op: 'equals', value: 'none' }\n if (clause.op === 'isNotEmpty')\n return crmAnyOf('emailStatus', EMAIL_STATE_STATUSES)\n const values = crmClauseValues(clause)\n if (\n values.some(\n (value) => !(EMAIL_STATE_STATUSES as readonly string[]).includes(value),\n )\n ) {\n return { refused: 'pick one of the choices' }\n }\n return crmAnyOf('emailStatus', values)\n }\n const facet = facetFieldOf(clause.field)\n if (!facet) return { refused: 'this list does not filter by that' }\n if (!reader.foldsScope) return { refused: LIMITED_ACCESS }\n const group = reader.groupId ?? CRM_FACET_KEY_ANY_GROUP\n if (clause.op === 'isNotEmpty') {\n return {\n field: CONTACT_FACET_KEYS_PATH,\n op: 'contains',\n value: crmFacetKey(group, facet),\n }\n }\n const keys: string[] = []\n for (const value of crmClauseValues(clause)) {\n const key = crmFacetKeyValue(clause.op === '=' ? Number(value) : value)\n if (key === null) return { refused: 'no value yet' }\n keys.push(crmFacetKey(group, facet, key))\n }\n const asked = crmAnyOf(CONTACT_FACET_KEYS_PATH, keys)\n // One key is an `array-contains`; several, an `array-contains-any`.\n return 'refused' in asked || asked.op === 'isAnyOf'\n ? asked\n : { ...asked, op: 'contains' }\n}\n\n/**\n * Whether an asked clause stands in for the scope clause: a facet key of\n * the viewing holder, which only a record that holder keeps carries, or a\n * form, which only its site's captures carry.\n */\nexport function contactClauseImpliesScope(groupId: string | null) {\n return (clause: ListFilterRequest): boolean => {\n if (clause.field === 'formIds') return true\n if (clause.field !== CONTACT_FACET_KEYS_PATH || !groupId) return false\n return crmClauseValues(clause).every((key) => key.startsWith(`${groupId}:`))\n }\n}\n\n/**\n * The query shapes the Contacts list can send, as declarations the index\n * pin enumerates (AGL-3321): the list's own, without its solo ranges, whose\n * every equality rides `(field, updatedAt DESC)`; and each solo range beside\n * the scope clause alone.\n */\nexport function contactIndexShapes(): ListQueryDeclaration[] {\n const soloOps = new Set(['startsWith', 'endsWith'])\n const main: ListQueryDeclaration = {\n ...CONTACT_LIST_DECLARATION,\n fields: CONTACT_QUERY_FIELDS.filter((field) => field !== CREATED_FIELD).map((field) =>\n field.operators\n ? { ...field, operators: field.operators.filter((op) => !soloOps.has(op)) }\n : field,\n ),\n }\n const solo: ListQueryDeclaration = {\n fields: [\n { ...NAME_FIELD, operators: ['startsWith', 'endsWith'] },\n { ...EMAIL_FIELD, operators: ['startsWith'] },\n CREATED_FIELD,\n ],\n sorts: [],\n }\n return [main, solo]\n}\n"],"names":["CRM_CONTACT_VIEW_FIELDS","CRM_FACET_KEY_ANY_GROUP","crmContactCustomColumn","crmFacetKey","crmFacetKeyValue","EMAIL_STATE_STATUSES","CRM_LIST_SEARCH","crmAnyOf","crmClauseValues","NAME_FIELD","column","kind","path","lowerPath","tokensPath","reversedPath","operators","EMAIL_FIELD","presence","CREATED_FIELD","createdAt","FORM_FIELD","verbatimTokens","CONTACT_LEAD_SOURCE_COLUMN","CONTACT_LIST_FILTER_FIELDS","tags","orders","ltv","updatedAt","storedAs","owner","stage","source","keysOf","company","contactCustomFilterFields","definitions","filter","definition","retiredAt","map","key","type","contactCustomFilterHeaders","Object","fromEntries","label","CONTACT_LIST_FILTER_HEADERS","name","email","formIds","hostId","ordersCount","ltvCents","nextTaskAtMs","emailState","CONTACT_LIST_SORTS","direction","CONTACT_FACET_KEYS_PATH","CONTACT_QUERY_FIELDS","CONTACT_LIST_DECLARATION","fields","sorts","search","AS_STORED","Set","contactSoloClause","clause","field","op","CONTACT_PREFIX_SEARCH","notice","FACET_FIELDS","LIMITED_ACCESS","facetFieldOf","startsWith","custom","slice","length","contactQueryClause","reader","has","foldsScope","refused","value","values","some","includes","facet","group","groupId","keys","Number","push","asked","contactClauseImpliesScope","every","contactIndexShapes","soloOps","main","solo"],"mappings":"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"}
|
|
@@ -14,13 +14,34 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
|
-
import type { ListFilterField } from '@aglyn/shared-ui-jsx/const/list-filter';
|
|
17
|
+
import type { ListFilterField, ListFilterRequest } from '@aglyn/shared-ui-jsx/const/list-filter';
|
|
18
|
+
import type { ListGridFilterCodec } from '@aglyn/shared-ui-jsx/const/list-grid-filter';
|
|
18
19
|
import type { ListQueryDeclaration, ListQueryFilter, ListQuerySort } from '@aglyn/shared-ui-jsx/const/list-query-plan';
|
|
20
|
+
import { type CrmViewFilterClause } from '@aglyn/aglyn/app-utils/crm';
|
|
21
|
+
import { type CrmClauseAsked } from '../model/crm-list-query';
|
|
19
22
|
/** The table's one order: newest change first. */
|
|
20
23
|
export declare const DEAL_LIST_SORTS: readonly ListQuerySort[];
|
|
24
|
+
/**
|
|
25
|
+
* The Type and Lead source controls' "none" choice (AGL-3516). Held in the
|
|
26
|
+
* saved view as an `isEmpty` clause, never as this string.
|
|
27
|
+
*/
|
|
28
|
+
export declare const DEAL_PICKLIST_FILTER_NONE = "\0none";
|
|
29
|
+
/**
|
|
30
|
+
* The Type and Lead source clauses' codec: "none" is an `isEmpty` clause,
|
|
31
|
+
* shown as a choice of the select — the Leads list's own.
|
|
32
|
+
*/
|
|
33
|
+
export declare const DEAL_FILTER_CODECS: Readonly<Record<string, ListGridFilterCodec>>;
|
|
21
34
|
/** What the deals table's Filters panel offers — every one asked of the query. */
|
|
22
35
|
export declare const DEAL_LIST_FILTER_FIELDS: readonly ListFilterField[];
|
|
36
|
+
/** The fields the Deals query asks — each a stored field, as named. */
|
|
37
|
+
export declare const DEAL_QUERY_FIELDS: readonly ListFilterField[];
|
|
23
38
|
export declare const DEAL_LIST_DECLARATION: ListQueryDeclaration;
|
|
39
|
+
/**
|
|
40
|
+
* One grid clause as the clause the Deals query asks (AGL-3516): a Type or
|
|
41
|
+
* a Lead source by its key — "none" as `isEmpty` — and every other clause
|
|
42
|
+
* as it is stored.
|
|
43
|
+
*/
|
|
44
|
+
export declare function dealQueryClause(clause: CrmViewFilterClause | ListFilterRequest): CrmClauseAsked;
|
|
24
45
|
/**
|
|
25
46
|
* A collaborator's search (see `prefixSearch` in `useCrmListQuery`): the
|
|
26
47
|
* start of the title, `titleLower`, beside the scope clause.
|
|
@@ -13,8 +13,10 @@
|
|
|
13
13
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
|
-
*/ import {
|
|
17
|
-
import {
|
|
16
|
+
*/ import { crmPicklistKey } from "@aglyn/aglyn/app-utils/crm";
|
|
17
|
+
import { CRM_NEXT_ACTIVITY_FILTER_FIELD } from "../components/crm-next-activity-column.js";
|
|
18
|
+
import { CRM_LIST_SEARCH, crmAnyOf, crmClauseValues, crmSelectField } from "../model/crm-list-query.js";
|
|
19
|
+
import { LEAD_SOURCE_CODEC, LEAD_SOURCE_FILTER_NONE } from "../model/lead-filters.js";
|
|
18
20
|
/*
|
|
19
21
|
* THE DEALS TABLE'S QUERY (AGL-3321).
|
|
20
22
|
*
|
|
@@ -29,21 +31,82 @@ import { CRM_LIST_SEARCH, crmSelectField } from "../model/crm-list-query.js";
|
|
|
29
31
|
*
|
|
30
32
|
* The board is not this: it draws a pipeline's open deals whole, bounded,
|
|
31
33
|
* and filters nothing.
|
|
34
|
+
*
|
|
35
|
+
* Type and Lead source (AGL-3516) are asked through the keys every deal
|
|
36
|
+
* writer stores beside the labels (`typeKey`, `leadSourceKey` in
|
|
37
|
+
* `crmDealListFields`) — `null` for none, so "No type" is `== null` — the
|
|
38
|
+
* way the Leads list asks its lead source (`dealQueryClause`).
|
|
32
39
|
*/ /** The table's one order: newest change first. */ export const DEAL_LIST_SORTS = [
|
|
33
40
|
{
|
|
34
41
|
path: 'updatedAt',
|
|
35
42
|
direction: 'desc'
|
|
36
43
|
}
|
|
37
44
|
];
|
|
45
|
+
/**
|
|
46
|
+
* The Type and Lead source controls' "none" choice (AGL-3516). Held in the
|
|
47
|
+
* saved view as an `isEmpty` clause, never as this string.
|
|
48
|
+
*/ export const DEAL_PICKLIST_FILTER_NONE = LEAD_SOURCE_FILTER_NONE;
|
|
49
|
+
/**
|
|
50
|
+
* The Type and Lead source clauses' codec: "none" is an `isEmpty` clause,
|
|
51
|
+
* shown as a choice of the select — the Leads list's own.
|
|
52
|
+
*/ export const DEAL_FILTER_CODECS = {
|
|
53
|
+
type: LEAD_SOURCE_CODEC,
|
|
54
|
+
leadSource: LEAD_SOURCE_CODEC
|
|
55
|
+
};
|
|
38
56
|
/** What the deals table's Filters panel offers — every one asked of the query. */ export const DEAL_LIST_FILTER_FIELDS = [
|
|
39
57
|
crmSelectField('status'),
|
|
40
|
-
CRM_NEXT_ACTIVITY_FILTER_FIELD
|
|
58
|
+
CRM_NEXT_ACTIVITY_FILTER_FIELD,
|
|
59
|
+
crmSelectField('type'),
|
|
60
|
+
crmSelectField('leadSource')
|
|
61
|
+
];
|
|
62
|
+
/** A picklist key the query asks: `null` for none, as every deal writer stores it. */ const keyField = (column)=>({
|
|
63
|
+
column,
|
|
64
|
+
kind: 'exact',
|
|
65
|
+
path: column,
|
|
66
|
+
presence: 'nullable',
|
|
67
|
+
operators: [
|
|
68
|
+
'equals',
|
|
69
|
+
'isAnyOf',
|
|
70
|
+
'isEmpty'
|
|
71
|
+
]
|
|
72
|
+
});
|
|
73
|
+
/** The fields the Deals query asks — each a stored field, as named. */ export const DEAL_QUERY_FIELDS = [
|
|
74
|
+
crmSelectField('status'),
|
|
75
|
+
CRM_NEXT_ACTIVITY_FILTER_FIELD,
|
|
76
|
+
keyField('typeKey'),
|
|
77
|
+
keyField('leadSourceKey')
|
|
41
78
|
];
|
|
42
79
|
export const DEAL_LIST_DECLARATION = {
|
|
43
|
-
fields:
|
|
80
|
+
fields: DEAL_QUERY_FIELDS,
|
|
44
81
|
sorts: DEAL_LIST_SORTS,
|
|
45
82
|
search: CRM_LIST_SEARCH
|
|
46
83
|
};
|
|
84
|
+
/** The picklist fields the panel shows by label and the query asks by key. */ const DEAL_KEYED_FIELDS = {
|
|
85
|
+
type: 'typeKey',
|
|
86
|
+
leadSource: 'leadSourceKey'
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* One grid clause as the clause the Deals query asks (AGL-3516): a Type or
|
|
90
|
+
* a Lead source by its key — "none" as `isEmpty` — and every other clause
|
|
91
|
+
* as it is stored.
|
|
92
|
+
*/ export function dealQueryClause(clause) {
|
|
93
|
+
const key = DEAL_KEYED_FIELDS[clause.field];
|
|
94
|
+
if (!key) return clause;
|
|
95
|
+
const values = crmClauseValues(clause);
|
|
96
|
+
if (clause.op === 'isEmpty' || values.length === 1 && values[0] === DEAL_PICKLIST_FILTER_NONE) {
|
|
97
|
+
return {
|
|
98
|
+
field: key,
|
|
99
|
+
op: 'isEmpty',
|
|
100
|
+
value: ''
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
if (values.includes(DEAL_PICKLIST_FILTER_NONE)) {
|
|
104
|
+
return {
|
|
105
|
+
refused: '"None" cannot be picked beside a value'
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return crmAnyOf(key, values.map((value)=>crmPicklistKey(value)).filter((entry)=>Boolean(entry)));
|
|
109
|
+
}
|
|
47
110
|
/**
|
|
48
111
|
* A collaborator's search (see `prefixSearch` in `useCrmListQuery`): the
|
|
49
112
|
* start of the title, `titleLower`, beside the scope clause.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/constants/deal-filters.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { ListFilterField } from '@aglyn/shared-ui-jsx/const/list-filter'\nimport type {\n ListQueryDeclaration,\n ListQueryFilter,\n ListQuerySort,\n} from '@aglyn/shared-ui-jsx/const/list-query-plan'\nimport { CRM_NEXT_ACTIVITY_FILTER_FIELD } from '../components/crm-next-activity-column'\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/constants/deal-filters.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { ListFilterField, ListFilterRequest } from '@aglyn/shared-ui-jsx/const/list-filter'\nimport type { ListGridFilterCodec } from '@aglyn/shared-ui-jsx/const/list-grid-filter'\nimport type {\n ListQueryDeclaration,\n ListQueryFilter,\n ListQuerySort,\n} from '@aglyn/shared-ui-jsx/const/list-query-plan'\nimport { crmPicklistKey, type CrmViewFilterClause } from '@aglyn/aglyn/app-utils/crm'\nimport { CRM_NEXT_ACTIVITY_FILTER_FIELD } from '../components/crm-next-activity-column'\nimport {\n CRM_LIST_SEARCH,\n crmAnyOf,\n type CrmClauseAsked,\n crmClauseValues,\n crmSelectField,\n} from '../model/crm-list-query'\nimport { LEAD_SOURCE_CODEC, LEAD_SOURCE_FILTER_NONE } from '../model/lead-filters'\n\n/*\n * THE DEALS TABLE'S QUERY (AGL-3321).\n *\n * `orgs/{orgId}/deals` in the chosen pipeline — the pipeline is the query's\n * base beside the scope clause — newest change first, paged by that query.\n * The status, \"No next activity\" (`nextTaskAtMs == null`, which every deal\n * is created carrying) and the search word are predicates on the same\n * query. The search reads `searchTokens`, the word prefixes of the title\n * every deal writer stamps (`crmDealListFields`), so \"renewal\" finds \"Acme\n * renewal\" on any page. Under a site the scope clause is the query's one\n * array clause and the search folds into it for a reader who may drop it.\n *\n * The board is not this: it draws a pipeline's open deals whole, bounded,\n * and filters nothing.\n *\n * Type and Lead source (AGL-3516) are asked through the keys every deal\n * writer stores beside the labels (`typeKey`, `leadSourceKey` in\n * `crmDealListFields`) — `null` for none, so \"No type\" is `== null` — the\n * way the Leads list asks its lead source (`dealQueryClause`).\n */\n\n/** The table's one order: newest change first. */\nexport const DEAL_LIST_SORTS: readonly ListQuerySort[] = [\n { path: 'updatedAt', direction: 'desc' },\n]\n\n/**\n * The Type and Lead source controls' \"none\" choice (AGL-3516). Held in the\n * saved view as an `isEmpty` clause, never as this string.\n */\nexport const DEAL_PICKLIST_FILTER_NONE = LEAD_SOURCE_FILTER_NONE\n\n/**\n * The Type and Lead source clauses' codec: \"none\" is an `isEmpty` clause,\n * shown as a choice of the select — the Leads list's own.\n */\nexport const DEAL_FILTER_CODECS: Readonly<Record<string, ListGridFilterCodec>> = {\n type: LEAD_SOURCE_CODEC,\n leadSource: LEAD_SOURCE_CODEC,\n}\n\n/** What the deals table's Filters panel offers — every one asked of the query. */\nexport const DEAL_LIST_FILTER_FIELDS: readonly ListFilterField[] = [\n crmSelectField('status'),\n CRM_NEXT_ACTIVITY_FILTER_FIELD,\n crmSelectField('type'),\n crmSelectField('leadSource'),\n]\n\n/** A picklist key the query asks: `null` for none, as every deal writer stores it. */\nconst keyField = (column: string): ListFilterField => ({\n column,\n kind: 'exact',\n path: column,\n presence: 'nullable',\n operators: ['equals', 'isAnyOf', 'isEmpty'],\n})\n\n/** The fields the Deals query asks — each a stored field, as named. */\nexport const DEAL_QUERY_FIELDS: readonly ListFilterField[] = [\n crmSelectField('status'),\n CRM_NEXT_ACTIVITY_FILTER_FIELD,\n keyField('typeKey'),\n keyField('leadSourceKey'),\n]\n\nexport const DEAL_LIST_DECLARATION: ListQueryDeclaration = {\n fields: DEAL_QUERY_FIELDS,\n sorts: DEAL_LIST_SORTS,\n search: CRM_LIST_SEARCH,\n}\n\n/** The picklist fields the panel shows by label and the query asks by key. */\nconst DEAL_KEYED_FIELDS: Readonly<Record<string, string>> = {\n type: 'typeKey',\n leadSource: 'leadSourceKey',\n}\n\n/**\n * One grid clause as the clause the Deals query asks (AGL-3516): a Type or\n * a Lead source by its key — \"none\" as `isEmpty` — and every other clause\n * as it is stored.\n */\nexport function dealQueryClause(clause: CrmViewFilterClause | ListFilterRequest): CrmClauseAsked {\n const key = DEAL_KEYED_FIELDS[clause.field]\n if (!key) return clause\n const values = crmClauseValues(clause)\n if (clause.op === 'isEmpty' || (values.length === 1 && values[0] === DEAL_PICKLIST_FILTER_NONE)) {\n return { field: key, op: 'isEmpty', value: '' }\n }\n if (values.includes(DEAL_PICKLIST_FILTER_NONE)) {\n return { refused: '\"None\" cannot be picked beside a value' }\n }\n return crmAnyOf(\n key,\n values.map((value) => crmPicklistKey(value)).filter((entry): entry is string => Boolean(entry)),\n )\n}\n\n/**\n * A collaborator's search (see `prefixSearch` in `useCrmListQuery`): the\n * start of the title, `titleLower`, beside the scope clause.\n */\nexport const DEAL_PREFIX_SEARCH = {\n field: {\n column: 'title',\n kind: 'text',\n path: 'title',\n lowerPath: 'titleLower',\n presence: 'always',\n operators: ['startsWith'],\n } satisfies ListFilterField,\n notice: 'Search matches the start of a deal’s title for access limited to specific sites.',\n}\n\n/** The chosen pipeline, as the query's base beside the scope clause. */\nexport function dealPipelineBase(pipelineId: string | null): ListQueryFilter[] {\n return pipelineId ? [{ path: 'pipelineId', op: '==', value: pipelineId }] : []\n}\n\n/** The base's shape for `listQueryIndexes`: the pipeline, an equality. */\nexport const DEAL_LIST_PIPELINE_BASE_INDEX = [{ path: 'pipelineId' }] as const\n"],"names":["crmPicklistKey","CRM_NEXT_ACTIVITY_FILTER_FIELD","CRM_LIST_SEARCH","crmAnyOf","crmClauseValues","crmSelectField","LEAD_SOURCE_CODEC","LEAD_SOURCE_FILTER_NONE","DEAL_LIST_SORTS","path","direction","DEAL_PICKLIST_FILTER_NONE","DEAL_FILTER_CODECS","type","leadSource","DEAL_LIST_FILTER_FIELDS","keyField","column","kind","presence","operators","DEAL_QUERY_FIELDS","DEAL_LIST_DECLARATION","fields","sorts","search","DEAL_KEYED_FIELDS","dealQueryClause","clause","key","field","values","op","length","value","includes","refused","map","filter","entry","Boolean","DEAL_PREFIX_SEARCH","lowerPath","notice","dealPipelineBase","pipelineId","DEAL_LIST_PIPELINE_BASE_INDEX"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GASD,SAASA,cAAc,QAAkC,6BAA4B;AACrF,SAASC,8BAA8B,QAAQ,4CAAwC;AACvF,SACEC,eAAe,EACfC,QAAQ,EAERC,eAAe,EACfC,cAAc,QACT,6BAAyB;AAChC,SAASC,iBAAiB,EAAEC,uBAAuB,QAAQ,2BAAuB;AAElF;;;;;;;;;;;;;;;;;;;CAmBC,GAED,gDAAgD,GAChD,OAAO,MAAMC,kBAA4C;IACvD;QAAEC,MAAM;QAAaC,WAAW;IAAO;CACxC,CAAA;AAED;;;CAGC,GACD,OAAO,MAAMC,4BAA4BJ,wBAAuB;AAEhE;;;CAGC,GACD,OAAO,MAAMK,qBAAoE;IAC/EC,MAAMP;IACNQ,YAAYR;AACd,EAAC;AAED,gFAAgF,GAChF,OAAO,MAAMS,0BAAsD;IACjEV,eAAe;IACfJ;IACAI,eAAe;IACfA,eAAe;CAChB,CAAA;AAED,oFAAoF,GACpF,MAAMW,WAAW,CAACC,SAAqC,CAAA;QACrDA;QACAC,MAAM;QACNT,MAAMQ;QACNE,UAAU;QACVC,WAAW;YAAC;YAAU;YAAW;SAAU;IAC7C,CAAA;AAEA,qEAAqE,GACrE,OAAO,MAAMC,oBAAgD;IAC3DhB,eAAe;IACfJ;IACAe,SAAS;IACTA,SAAS;CACV,CAAA;AAED,OAAO,MAAMM,wBAA8C;IACzDC,QAAQF;IACRG,OAAOhB;IACPiB,QAAQvB;AACV,EAAC;AAED,4EAA4E,GAC5E,MAAMwB,oBAAsD;IAC1Db,MAAM;IACNC,YAAY;AACd;AAEA;;;;CAIC,GACD,OAAO,SAASa,gBAAgBC,MAA+C;IAC7E,MAAMC,MAAMH,iBAAiB,CAACE,OAAOE,KAAK,CAAC;IAC3C,IAAI,CAACD,KAAK,OAAOD;IACjB,MAAMG,SAAS3B,gBAAgBwB;IAC/B,IAAIA,OAAOI,EAAE,KAAK,aAAcD,OAAOE,MAAM,KAAK,KAAKF,MAAM,CAAC,EAAE,KAAKpB,2BAA4B;QAC/F,OAAO;YAAEmB,OAAOD;YAAKG,IAAI;YAAWE,OAAO;QAAG;IAChD;IACA,IAAIH,OAAOI,QAAQ,CAACxB,4BAA4B;QAC9C,OAAO;YAAEyB,SAAS;QAAyC;IAC7D;IACA,OAAOjC,SACL0B,KACAE,OAAOM,GAAG,CAAC,CAACH,QAAUlC,eAAekC,QAAQI,MAAM,CAAC,CAACC,QAA2BC,QAAQD;AAE5F;AAEA;;;CAGC,GACD,OAAO,MAAME,qBAAqB;IAChCX,OAAO;QACLb,QAAQ;QACRC,MAAM;QACNT,MAAM;QACNiC,WAAW;QACXvB,UAAU;QACVC,WAAW;YAAC;SAAa;IAC3B;IACAuB,QAAQ;AACV,EAAC;AAED,sEAAsE,GACtE,OAAO,SAASC,iBAAiBC,UAAyB;IACxD,OAAOA,aAAa;QAAC;YAAEpC,MAAM;YAAcuB,IAAI;YAAME,OAAOW;QAAW;KAAE,GAAG,EAAE;AAChF;AAEA,wEAAwE,GACxE,OAAO,MAAMC,gCAAgC;IAAC;QAAErC,MAAM;IAAa;CAAE,CAAS"}
|
|
@@ -28,6 +28,9 @@
|
|
|
28
28
|
* records band and collection sizes join `GET /v1/usage`. The API is the
|
|
29
29
|
* console's alone, so the tenant runtime does not register them.
|
|
30
30
|
*
|
|
31
|
+
* It registers the server half of the CRM's import and export resources
|
|
32
|
+
* (`transfer/register.ts`).
|
|
33
|
+
*
|
|
31
34
|
* It also clears a removed container off the CRM's records: a campaign is
|
|
32
35
|
* deleted from the console, and its owner asks every plugin's membership
|
|
33
36
|
* detacher first (`server/container-detach.ts`).
|