@aglyn/plugins-crm 1.0.0-beta.221 → 1.0.0-beta.223
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-next-activity-column.js +1 -1
- package/src/lib/components/crm-next-activity-column.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/pipeline-card.js +2 -1
- package/src/lib/components/reports/pipeline-card.js.map +1 -1
- 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 +97 -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 +17 -2
- 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-next-activity.d.ts +107 -0
- package/src/lib/model/crm-next-activity.js +140 -0
- package/src/lib/model/crm-next-activity.js.map +1 -0
- package/src/lib/model/crm-recipes.d.ts +40 -0
- package/src/lib/model/crm-recipes.js +259 -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/crm-task-reminders.d.ts +152 -0
- package/src/lib/model/crm-task-reminders.js +161 -0
- package/src/lib/model/crm-task-reminders.js.map +1 -0
- 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/next-activity.d.ts +1 -1
- package/src/lib/model/next-activity.js.map +1 -1
- 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 +78 -34
- 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 +188 -0
- package/src/lib/server/assign-contact-owner.js +446 -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 +140 -0
- package/src/lib/server/automation-steps.js +669 -0
- package/src/lib/server/automation-steps.js.map +1 -0
- package/src/lib/server/capture-contact.d.ts +17 -0
- package/src/lib/server/capture-contact.js +95 -26
- 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-email-activity.d.ts +118 -0
- package/src/lib/server/crm-email-activity.js +148 -0
- package/src/lib/server/crm-email-activity.js.map +1 -0
- package/src/lib/server/crm-inbound-email.js +1 -1
- package/src/lib/server/crm-inbound-email.js.map +1 -1
- package/src/lib/server/crm-next-activity.d.ts +74 -0
- package/src/lib/server/crm-next-activity.js +194 -0
- package/src/lib/server/crm-next-activity.js.map +1 -0
- 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/email-send.js +2 -1
- package/src/lib/server/email-send.js.map +1 -1
- 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 +23 -5
- package/src/lib/server/merge-contacts.js.map +1 -1
- package/src/lib/server/next-activity-routes.js +2 -1
- package/src/lib/server/next-activity-routes.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 +22 -8
- 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 +40 -3
- 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-reminders-route.js +2 -1
- package/src/lib/server/task-reminders-route.js.map +1 -1
- package/src/lib/server/task-routes.js +33 -17
- 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
|
@@ -16,13 +16,15 @@
|
|
|
16
16
|
*/ 'use client';
|
|
17
17
|
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
18
18
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
19
|
+
import { crmLeadStatusLabelFor, crmLeadStatusOptions } from "@aglyn/aglyn";
|
|
19
20
|
import { useSnackbar } from "@aglyn/shared-ui-snackstack";
|
|
20
21
|
import { useFirestore } from "@aglyn/tenant-feature-instance";
|
|
21
|
-
import { Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, TextField } from "@mui/material";
|
|
22
|
+
import { Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, MenuItem, Stack, TextField } from "@mui/material";
|
|
22
23
|
import { doc, serverTimestamp, updateDoc } from "firebase/firestore";
|
|
23
24
|
import { useEffect, useState } from "react";
|
|
24
25
|
import { useCrmScope } from "../hooks/use-crm-scope.js";
|
|
25
26
|
import { useCrmSharingFollowUp } from "../hooks/use-crm-sharing.js";
|
|
27
|
+
import { useLeadStatusPicklist } from "../hooks/use-lead-status-picklist.js";
|
|
26
28
|
/** The most a reason may say — shared with the bulk bar's one-reason-for-all (AGL-2662). */ export const UNQUALIFY_REASON_MAX = 500;
|
|
27
29
|
const REASON_MAX = UNQUALIFY_REASON_MAX;
|
|
28
30
|
/**
|
|
@@ -34,7 +36,7 @@ const REASON_MAX = UNQUALIFY_REASON_MAX;
|
|
|
34
36
|
* not in the catch-all's update exclusions, so a site admin, editor or
|
|
35
37
|
* author may update it, and nothing here needs the server.
|
|
36
38
|
*/ export function LeadUnqualifyDialog(props) {
|
|
37
|
-
const { open, onClose, hostId, leadId, leadLabel } = props;
|
|
39
|
+
const { open, onClose, hostId, leadId, leadLabel, statusLabel } = props;
|
|
38
40
|
// The lead's collection is the org's now (AGL-3275); the site still names
|
|
39
41
|
// the surface, and the scope hook resolves the org from it.
|
|
40
42
|
const { orgId } = useCrmScope({
|
|
@@ -45,10 +47,21 @@ const REASON_MAX = UNQUALIFY_REASON_MAX;
|
|
|
45
47
|
const { enqueueSnackbar } = useSnackbar();
|
|
46
48
|
const [reason, setReason] = useState('');
|
|
47
49
|
const [busy, setBusy] = useState(false);
|
|
50
|
+
// The org's Unqualified values (AGL-3512): a choice only when it keeps several.
|
|
51
|
+
const statuses = useLeadStatusPicklist(orgId);
|
|
52
|
+
const closedAs = crmLeadStatusOptions(statuses.picklist, [
|
|
53
|
+
'unqualified'
|
|
54
|
+
]);
|
|
55
|
+
const [label, setLabel] = useState('');
|
|
48
56
|
useEffect(()=>{
|
|
49
|
-
if (open)
|
|
57
|
+
if (!open) return;
|
|
58
|
+
setReason('');
|
|
59
|
+
setLabel(statusLabel || crmLeadStatusLabelFor(statuses.picklist, 'unqualified'));
|
|
60
|
+
// Seeded as the dialog opens; the list arriving later does not reset a pick.
|
|
61
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
50
62
|
}, [
|
|
51
|
-
open
|
|
63
|
+
open,
|
|
64
|
+
statusLabel
|
|
52
65
|
]);
|
|
53
66
|
const submit = async ()=>{
|
|
54
67
|
const trimmed = reason.trim();
|
|
@@ -64,6 +77,8 @@ const REASON_MAX = UNQUALIFY_REASON_MAX;
|
|
|
64
77
|
try {
|
|
65
78
|
const fields = {
|
|
66
79
|
status: 'unqualified',
|
|
80
|
+
// The meaning and the org's label for it, always together (AGL-3512).
|
|
81
|
+
statusLabel: label || crmLeadStatusLabelFor(statuses.picklist, 'unqualified'),
|
|
67
82
|
unqualifiedReason: trimmed.slice(0, REASON_MAX)
|
|
68
83
|
};
|
|
69
84
|
await updateDoc(doc(firestore, 'orgs', orgId, 'leads', leadId), _extends({}, fields, {
|
|
@@ -73,7 +88,7 @@ const REASON_MAX = UNQUALIFY_REASON_MAX;
|
|
|
73
88
|
followUpSharing('leads', [
|
|
74
89
|
leadId
|
|
75
90
|
]);
|
|
76
|
-
enqueueSnackbar(
|
|
91
|
+
enqueueSnackbar(`Lead marked ${fields.statusLabel}`, {
|
|
77
92
|
variant: 'success',
|
|
78
93
|
persist: false
|
|
79
94
|
});
|
|
@@ -103,19 +118,36 @@ const REASON_MAX = UNQUALIFY_REASON_MAX;
|
|
|
103
118
|
},
|
|
104
119
|
children: 'The lead stays on file and drops out of the open list. Say why, ' + 'so the reason can be counted later.'
|
|
105
120
|
}),
|
|
106
|
-
/*#__PURE__*/
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
121
|
+
/*#__PURE__*/ _jsxs(Stack, {
|
|
122
|
+
spacing: 2,
|
|
123
|
+
children: [
|
|
124
|
+
closedAs.length > 1 ? /*#__PURE__*/ _jsx(TextField, {
|
|
125
|
+
select: true,
|
|
126
|
+
size: "small",
|
|
127
|
+
label: "Status",
|
|
128
|
+
value: closedAs.some((option)=>option.label === label) ? label : '',
|
|
129
|
+
onChange: (event)=>setLabel(String(event.target.value)),
|
|
130
|
+
fullWidth: true,
|
|
131
|
+
children: closedAs.map((option)=>/*#__PURE__*/ _jsx(MenuItem, {
|
|
132
|
+
value: option.label,
|
|
133
|
+
children: option.label
|
|
134
|
+
}, option.label))
|
|
135
|
+
}) : null,
|
|
136
|
+
/*#__PURE__*/ _jsx(TextField, {
|
|
137
|
+
label: "Reason",
|
|
138
|
+
value: reason,
|
|
139
|
+
onChange: (event)=>setReason(event.target.value),
|
|
140
|
+
multiline: true,
|
|
141
|
+
minRows: 2,
|
|
142
|
+
fullWidth: true,
|
|
143
|
+
autoFocus: true,
|
|
144
|
+
slotProps: {
|
|
145
|
+
htmlInput: {
|
|
146
|
+
maxLength: REASON_MAX
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
]
|
|
119
151
|
})
|
|
120
152
|
]
|
|
121
153
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/lead-unqualify-dialog.tsx"],"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'use client'\n\nimport type { CrmLeadFields } from '@aglyn/aglyn'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport {\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogContentText,\n DialogTitle,\n TextField,\n} from '@mui/material'\nimport { doc, serverTimestamp, updateDoc } from 'firebase/firestore'\nimport { useEffect, useState } from 'react'\nimport { useCrmScope } from '../hooks/use-crm-scope'\nimport { useCrmSharingFollowUp } from '../hooks/use-crm-sharing'\n\n/** The most a reason may say — shared with the bulk bar's one-reason-for-all (AGL-2662). */\nexport const UNQUALIFY_REASON_MAX = 500\nconst REASON_MAX = UNQUALIFY_REASON_MAX\n\nexport interface LeadUnqualifyDialogProps {\n open: boolean\n onClose: () => void\n /**\n * The lead's own site, or `null` at the organization level (AGL-3278).\n * The write is an org-scoped one-field update; the site is read for the\n * org root, which the mount answers without one.\n */\n hostId: string | null\n leadId: string\n /** How the lead reads in the title — its name, else its address. */\n leadLabel: string\n}\n\n/**\n * Close a lead without converting it, with the reason why (AGL-2608).\n *\n * The reason is REQUIRED. An unqualified lead with no reason is a row that\n * says only \"somebody gave up\", and the one thing a report on lost leads\n * wants to count is why. A client-direct write: `hosts/{hostId}/leads` is\n * not in the catch-all's update exclusions, so a site admin, editor or\n * author may update it, and nothing here needs the server.\n */\nexport function LeadUnqualifyDialog(props: LeadUnqualifyDialogProps) {\n const { open, onClose, hostId, leadId, leadLabel } = props\n // The lead's collection is the org's now (AGL-3275); the site still names\n // the surface, and the scope hook resolves the org from it.\n const { orgId } = useCrmScope({ hostId })\n const followUpSharing = useCrmSharingFollowUp(hostId, orgId)\n const firestore = useFirestore()\n const { enqueueSnackbar } = useSnackbar()\n const [reason, setReason] = useState('')\n const [busy, setBusy] = useState(false)\n\n useEffect(() => {\n if (open) setReason('')\n }, [open])\n\n const submit = async () => {\n const trimmed = reason.trim()\n if (!trimmed) return\n if (!orgId) {\n enqueueSnackbar('Still loading this workspace — try again in a moment.', {\n variant: 'warning',\n persist: false,\n })\n return\n }\n setBusy(true)\n try {\n const fields: Required<Pick<CrmLeadFields, 'status' | 'unqualifiedReason'>> = {\n status: 'unqualified',\n unqualifiedReason: trimmed.slice(0, REASON_MAX),\n }\n await updateDoc(doc(firestore, 'orgs', orgId, 'leads', leadId), {\n ...fields,\n updatedAt: serverTimestamp(),\n })\n // A rule may share by status (AGL-3336).\n followUpSharing('leads', [leadId])\n enqueueSnackbar('Lead marked unqualified', { variant: 'success', persist: false })\n onClose()\n } catch (error) {\n enqueueSnackbar(\n error instanceof Error ? error.message : 'The lead could not be updated.',\n { variant: 'error' },\n )\n } finally {\n setBusy(false)\n }\n }\n\n return (\n <Dialog open={open} onClose={onClose} maxWidth=\"xs\" fullWidth>\n <DialogTitle>{`Unqualify ${leadLabel}?`}</DialogTitle>\n <DialogContent>\n <DialogContentText sx={{ mb: 2 }}>\n {'The lead stays on file and drops out of the open list. Say why, ' +\n 'so the reason can be counted later.'}\n </DialogContentText>\n <TextField\n label=\"Reason\"\n value={reason}\n onChange={(event) => setReason(event.target.value)}\n multiline\n minRows={2}\n fullWidth\n autoFocus\n slotProps={{ htmlInput: { maxLength: REASON_MAX } }}\n />\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} disabled={busy}>\n {'Cancel'}\n </Button>\n <Button\n variant=\"contained\"\n color=\"warning\"\n onClick={() => void submit()}\n disabled={busy || !reason.trim()}\n >\n {'Unqualify'}\n </Button>\n </DialogActions>\n </Dialog>\n )\n}\nLeadUnqualifyDialog.displayName = 'LeadUnqualifyDialog'\n\nexport default LeadUnqualifyDialog\n"],"names":["useSnackbar","useFirestore","Button","Dialog","DialogActions","DialogContent","DialogContentText","DialogTitle","TextField","doc","serverTimestamp","updateDoc","useEffect","useState","useCrmScope","useCrmSharingFollowUp","UNQUALIFY_REASON_MAX","REASON_MAX","LeadUnqualifyDialog","props","open","onClose","hostId","leadId","leadLabel","orgId","followUpSharing","firestore","enqueueSnackbar","reason","setReason","busy","setBusy","submit","trimmed","trim","variant","persist","fields","status","unqualifiedReason","slice","updatedAt","error","Error","message","maxWidth","fullWidth","sx","mb","label","value","onChange","event","target","multiline","minRows","autoFocus","slotProps","htmlInput","maxLength","onClick","disabled","color","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;;AAGA,SAASA,WAAW,QAAQ,8BAA6B;AACzD,SAASC,YAAY,QAAQ,iCAAgC;AAC7D,SACEC,MAAM,EACNC,MAAM,EACNC,aAAa,EACbC,aAAa,EACbC,iBAAiB,EACjBC,WAAW,EACXC,SAAS,QACJ,gBAAe;AACtB,SAASC,GAAG,EAAEC,eAAe,EAAEC,SAAS,QAAQ,qBAAoB;AACpE,SAASC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAC3C,SAASC,WAAW,QAAQ,4BAAwB;AACpD,SAASC,qBAAqB,QAAQ,8BAA0B;AAEhE,0FAA0F,GAC1F,OAAO,MAAMC,uBAAuB,IAAG;AACvC,MAAMC,aAAaD;AAgBnB;;;;;;;;CAQC,GACD,OAAO,SAASE,oBAAoBC,KAA+B;IACjE,MAAM,EAAEC,IAAI,EAAEC,OAAO,EAAEC,MAAM,EAAEC,MAAM,EAAEC,SAAS,EAAE,GAAGL;IACrD,0EAA0E;IAC1E,4DAA4D;IAC5D,MAAM,EAAEM,KAAK,EAAE,GAAGX,YAAY;QAAEQ;IAAO;IACvC,MAAMI,kBAAkBX,sBAAsBO,QAAQG;IACtD,MAAME,YAAY1B;IAClB,MAAM,EAAE2B,eAAe,EAAE,GAAG5B;IAC5B,MAAM,CAAC6B,QAAQC,UAAU,GAAGjB,SAAS;IACrC,MAAM,CAACkB,MAAMC,QAAQ,GAAGnB,SAAS;IAEjCD,UAAU;QACR,IAAIQ,MAAMU,UAAU;IACtB,GAAG;QAACV;KAAK;IAET,MAAMa,SAAS;QACb,MAAMC,UAAUL,OAAOM,IAAI;QAC3B,IAAI,CAACD,SAAS;QACd,IAAI,CAACT,OAAO;YACVG,gBAAgB,yDAAyD;gBACvEQ,SAAS;gBACTC,SAAS;YACX;YACA;QACF;QACAL,QAAQ;QACR,IAAI;YACF,MAAMM,SAAwE;gBAC5EC,QAAQ;gBACRC,mBAAmBN,QAAQO,KAAK,CAAC,GAAGxB;YACtC;YACA,MAAMN,UAAUF,IAAIkB,WAAW,QAAQF,OAAO,SAASF,SAAS,aAC3De;gBACHI,WAAWhC;;YAEb,yCAAyC;YACzCgB,gBAAgB,SAAS;gBAACH;aAAO;YACjCK,gBAAgB,2BAA2B;gBAAEQ,SAAS;gBAAWC,SAAS;YAAM;YAChFhB;QACF,EAAE,OAAOsB,OAAO;YACdf,gBACEe,iBAAiBC,QAAQD,MAAME,OAAO,GAAG,kCACzC;gBAAET,SAAS;YAAQ;QAEvB,SAAU;YACRJ,QAAQ;QACV;IACF;IAEA,qBACE,MAAC7B;QAAOiB,MAAMA;QAAMC,SAASA;QAASyB,UAAS;QAAKC,SAAS;;0BAC3D,KAACxC;0BAAa,CAAC,UAAU,EAAEiB,UAAU,CAAC,CAAC;;0BACvC,MAACnB;;kCACC,KAACC;wBAAkB0C,IAAI;4BAAEC,IAAI;wBAAE;kCAC5B,qEACC;;kCAEJ,KAACzC;wBACC0C,OAAM;wBACNC,OAAOtB;wBACPuB,UAAU,CAACC,QAAUvB,UAAUuB,MAAMC,MAAM,CAACH,KAAK;wBACjDI,SAAS;wBACTC,SAAS;wBACTT,SAAS;wBACTU,SAAS;wBACTC,WAAW;4BAAEC,WAAW;gCAAEC,WAAW3C;4BAAW;wBAAE;;;;0BAGtD,MAACb;;kCACC,KAACF;wBAAO2D,SAASxC;wBAASyC,UAAU/B;kCACjC;;kCAEH,KAAC7B;wBACCkC,SAAQ;wBACR2B,OAAM;wBACNF,SAAS,IAAM,KAAK5B;wBACpB6B,UAAU/B,QAAQ,CAACF,OAAOM,IAAI;kCAE7B;;;;;;AAKX;AACAjB,oBAAoB8C,WAAW,GAAG;AAElC,eAAe9C,oBAAmB"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/lead-unqualify-dialog.tsx"],"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'use client'\n\nimport { type CrmLeadFields, crmLeadStatusLabelFor, crmLeadStatusOptions } from '@aglyn/aglyn'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport {\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogContentText,\n DialogTitle,\n MenuItem,\n Stack,\n TextField,\n} from '@mui/material'\nimport { doc, serverTimestamp, updateDoc } from 'firebase/firestore'\nimport { useEffect, useState } from 'react'\nimport { useCrmScope } from '../hooks/use-crm-scope'\nimport { useCrmSharingFollowUp } from '../hooks/use-crm-sharing'\nimport { useLeadStatusPicklist } from '../hooks/use-lead-status-picklist'\n\n/** The most a reason may say — shared with the bulk bar's one-reason-for-all (AGL-2662). */\nexport const UNQUALIFY_REASON_MAX = 500\nconst REASON_MAX = UNQUALIFY_REASON_MAX\n\nexport interface LeadUnqualifyDialogProps {\n open: boolean\n onClose: () => void\n /**\n * The lead's own site, or `null` at the organization level (AGL-3278).\n * The write is an org-scoped one-field update; the site is read for the\n * org root, which the mount answers without one.\n */\n hostId: string | null\n leadId: string\n /** How the lead reads in the title — its name, else its address. */\n leadLabel: string\n /**\n * The Unqualified value the lead is closed as, when the reader already\n * picked one (AGL-3512); the meaning's default label otherwise.\n */\n statusLabel?: string | null\n}\n\n/**\n * Close a lead without converting it, with the reason why (AGL-2608).\n *\n * The reason is REQUIRED. An unqualified lead with no reason is a row that\n * says only \"somebody gave up\", and the one thing a report on lost leads\n * wants to count is why. A client-direct write: `hosts/{hostId}/leads` is\n * not in the catch-all's update exclusions, so a site admin, editor or\n * author may update it, and nothing here needs the server.\n */\nexport function LeadUnqualifyDialog(props: LeadUnqualifyDialogProps) {\n const { open, onClose, hostId, leadId, leadLabel, statusLabel } = props\n // The lead's collection is the org's now (AGL-3275); the site still names\n // the surface, and the scope hook resolves the org from it.\n const { orgId } = useCrmScope({ hostId })\n const followUpSharing = useCrmSharingFollowUp(hostId, orgId)\n const firestore = useFirestore()\n const { enqueueSnackbar } = useSnackbar()\n const [reason, setReason] = useState('')\n const [busy, setBusy] = useState(false)\n // The org's Unqualified values (AGL-3512): a choice only when it keeps several.\n const statuses = useLeadStatusPicklist(orgId)\n const closedAs = crmLeadStatusOptions(statuses.picklist, ['unqualified'])\n const [label, setLabel] = useState('')\n\n useEffect(() => {\n if (!open) return\n setReason('')\n setLabel(statusLabel || crmLeadStatusLabelFor(statuses.picklist, 'unqualified'))\n // Seeded as the dialog opens; the list arriving later does not reset a pick.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open, statusLabel])\n\n const submit = async () => {\n const trimmed = reason.trim()\n if (!trimmed) return\n if (!orgId) {\n enqueueSnackbar('Still loading this workspace — try again in a moment.', {\n variant: 'warning',\n persist: false,\n })\n return\n }\n setBusy(true)\n try {\n const fields: Required<Pick<CrmLeadFields, 'status' | 'statusLabel' | 'unqualifiedReason'>> = {\n status: 'unqualified',\n // The meaning and the org's label for it, always together (AGL-3512).\n statusLabel: label || crmLeadStatusLabelFor(statuses.picklist, 'unqualified'),\n unqualifiedReason: trimmed.slice(0, REASON_MAX),\n }\n await updateDoc(doc(firestore, 'orgs', orgId, 'leads', leadId), {\n ...fields,\n updatedAt: serverTimestamp(),\n })\n // A rule may share by status (AGL-3336).\n followUpSharing('leads', [leadId])\n enqueueSnackbar(`Lead marked ${fields.statusLabel}`, { variant: 'success', persist: false })\n onClose()\n } catch (error) {\n enqueueSnackbar(\n error instanceof Error ? error.message : 'The lead could not be updated.',\n { variant: 'error' },\n )\n } finally {\n setBusy(false)\n }\n }\n\n return (\n <Dialog open={open} onClose={onClose} maxWidth=\"xs\" fullWidth>\n <DialogTitle>{`Unqualify ${leadLabel}?`}</DialogTitle>\n <DialogContent>\n <DialogContentText sx={{ mb: 2 }}>\n {'The lead stays on file and drops out of the open list. Say why, ' +\n 'so the reason can be counted later.'}\n </DialogContentText>\n <Stack spacing={2}>\n {closedAs.length > 1 ? (\n <TextField\n select\n size=\"small\"\n label=\"Status\"\n value={closedAs.some((option) => option.label === label) ? label : ''}\n onChange={(event) => setLabel(String(event.target.value))}\n fullWidth\n >\n {closedAs.map((option) => (\n <MenuItem key={option.label} value={option.label}>\n {option.label}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n <TextField\n label=\"Reason\"\n value={reason}\n onChange={(event) => setReason(event.target.value)}\n multiline\n minRows={2}\n fullWidth\n autoFocus\n slotProps={{ htmlInput: { maxLength: REASON_MAX } }}\n />\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} disabled={busy}>\n {'Cancel'}\n </Button>\n <Button\n variant=\"contained\"\n color=\"warning\"\n onClick={() => void submit()}\n disabled={busy || !reason.trim()}\n >\n {'Unqualify'}\n </Button>\n </DialogActions>\n </Dialog>\n )\n}\nLeadUnqualifyDialog.displayName = 'LeadUnqualifyDialog'\n\nexport default LeadUnqualifyDialog\n"],"names":["crmLeadStatusLabelFor","crmLeadStatusOptions","useSnackbar","useFirestore","Button","Dialog","DialogActions","DialogContent","DialogContentText","DialogTitle","MenuItem","Stack","TextField","doc","serverTimestamp","updateDoc","useEffect","useState","useCrmScope","useCrmSharingFollowUp","useLeadStatusPicklist","UNQUALIFY_REASON_MAX","REASON_MAX","LeadUnqualifyDialog","props","open","onClose","hostId","leadId","leadLabel","statusLabel","orgId","followUpSharing","firestore","enqueueSnackbar","reason","setReason","busy","setBusy","statuses","closedAs","picklist","label","setLabel","submit","trimmed","trim","variant","persist","fields","status","unqualifiedReason","slice","updatedAt","error","Error","message","maxWidth","fullWidth","sx","mb","spacing","length","select","size","value","some","option","onChange","event","String","target","map","multiline","minRows","autoFocus","slotProps","htmlInput","maxLength","onClick","disabled","color","displayName"],"mappings":"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"}
|
|
@@ -25,7 +25,6 @@
|
|
|
25
25
|
*/
|
|
26
26
|
import { type CrmLeadFields } from '@aglyn/aglyn';
|
|
27
27
|
import type { OrgMemberOptions } from '../hooks/use-org-member-options';
|
|
28
|
-
import { type LeadCsvOptions } from '../model/leads-csv';
|
|
29
28
|
/**
|
|
30
29
|
* One row of the Leads list as the bar needs it: the document's fields,
|
|
31
30
|
* the grid's key, and which document under which site a write names.
|
|
@@ -40,8 +39,6 @@ export interface LeadsBulkBarProps {
|
|
|
40
39
|
onSelectedChange: (ids: string[]) => void;
|
|
41
40
|
/** The section's roster — already read for the Owner column. */
|
|
42
41
|
roster: OrgMemberOptions;
|
|
43
|
-
/** How the export names the owner and, at the org level, the site — the list's own. */
|
|
44
|
-
csv?: LeadCsvOptions;
|
|
45
42
|
/** The organization these leads belong to; null while it is unresolved. */
|
|
46
43
|
orgId?: string | null;
|
|
47
44
|
/**
|
|
@@ -40,7 +40,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
40
40
|
* menu and the inline select do. A lead already closed is not unqualified
|
|
41
41
|
* twice. A lead already at the asked status is skipped rather than rewritten
|
|
42
42
|
* so "Nothing to change" means what it says.
|
|
43
|
-
*/ import { CRM_LEAD_OPEN_STATUSES,
|
|
43
|
+
*/ import { CRM_LEAD_OPEN_STATUSES, crmLeadStatus, crmLeadStatusLabel, crmLeadStatusLabelFor, crmLeadStatusOptions, isCrmLeadOpen, readContainerIds } from "@aglyn/aglyn";
|
|
44
44
|
import ContainerPicker from "@aglyn/tenant-feature-instance/components/container-picker";
|
|
45
45
|
import { useFirestore } from "@aglyn/tenant-feature-instance";
|
|
46
46
|
import { Button, MenuItem, TextField } from "@mui/material";
|
|
@@ -48,13 +48,13 @@ import { arrayUnion, deleteField, doc, serverTimestamp } from "firebase/firestor
|
|
|
48
48
|
import { useCallback, useMemo, useState } from "react";
|
|
49
49
|
import { useCampaignFilingLog } from "../hooks/use-campaign-filing-log.js";
|
|
50
50
|
import { useCrmBulkApply } from "../hooks/use-crm-bulk-apply.js";
|
|
51
|
+
import { useLeadStatusPicklist } from "../hooks/use-lead-status-picklist.js";
|
|
51
52
|
import { useCrmCampaigns } from "../hooks/use-crm-campaigns.js";
|
|
52
|
-
import { downloadTextFile } from "../model/contacts-csv.js";
|
|
53
53
|
import { crmClientListFields } from "../model/crm-list-query.js";
|
|
54
54
|
import { crmBulkWriters, runCrmBulkWrites } from "../model/crm-bulk-writes.js";
|
|
55
|
-
import { leadsCsv } from "../model/leads-csv.js";
|
|
56
55
|
import { CrmBulkBarFrame, CrmBulkValueDialog, countNoun } from "./crm-bulk-bar-frame.js";
|
|
57
|
-
import
|
|
56
|
+
import { CrmExportButton } from "./crm-transfer-buttons.js";
|
|
57
|
+
import { CRM_LEADS_RESOURCE } from "../transfer/fields.js";
|
|
58
58
|
import { CrmBulkShareButton } from "./record-sharing-card.js";
|
|
59
59
|
import { LeadOwnerSelect } from "./lead-owner-select.js";
|
|
60
60
|
import { UNQUALIFY_REASON_MAX } from "./lead-unqualify-dialog.js";
|
|
@@ -79,10 +79,16 @@ export function LeadsBulkBar(props) {
|
|
|
79
79
|
LeadsBulkBar.displayName = 'LeadsBulkBar';
|
|
80
80
|
function LeadsBulkBarBody(props) {
|
|
81
81
|
var _props_orgId, _props_hostId;
|
|
82
|
-
const { rows, selected, onSelectedChange, roster
|
|
82
|
+
const { rows, selected, onSelectedChange, roster } = props;
|
|
83
83
|
const orgId = (_props_orgId = props.orgId) != null ? _props_orgId : null;
|
|
84
84
|
const hostId = (_props_hostId = props.hostId) != null ? _props_hostId : null;
|
|
85
85
|
const followUpSharing = useCrmSharingFollowUp(hostId, orgId);
|
|
86
|
+
// The org's lead status values (AGL-3512): Set status picks a value by its
|
|
87
|
+
// label, and every write stamps the meaning and the label together.
|
|
88
|
+
const statuses = useLeadStatusPicklist(orgId);
|
|
89
|
+
const statusChoices = useMemo(()=>crmLeadStatusOptions(statuses.picklist, SETTABLE_STATUSES), [
|
|
90
|
+
statuses.picklist
|
|
91
|
+
]);
|
|
86
92
|
const firestore = useFirestore();
|
|
87
93
|
const { busy, report, apply, dismissReport } = useCrmBulkApply({
|
|
88
94
|
recordKind: 'lead'
|
|
@@ -243,7 +249,9 @@ function LeadsBulkBarBody(props) {
|
|
|
243
249
|
return;
|
|
244
250
|
}
|
|
245
251
|
if (action === 'status') {
|
|
246
|
-
const
|
|
252
|
+
const choice = statusChoices.find((entry)=>entry.label === value);
|
|
253
|
+
if (!choice) return;
|
|
254
|
+
const status = choice.status;
|
|
247
255
|
for (const lead of selectedRows){
|
|
248
256
|
const current = crmLeadStatus(lead);
|
|
249
257
|
if (lead.convertedContactId) {
|
|
@@ -251,10 +259,10 @@ function LeadsBulkBarBody(props) {
|
|
|
251
259
|
label: labelOf(lead),
|
|
252
260
|
reason: 'was converted'
|
|
253
261
|
});
|
|
254
|
-
} else if (current === status) {
|
|
262
|
+
} else if (current === status && crmLeadStatusLabel(lead, statuses.picklist) === choice.label) {
|
|
255
263
|
skipped.push({
|
|
256
264
|
label: labelOf(lead),
|
|
257
|
-
reason: `already ${
|
|
265
|
+
reason: `already ${choice.label}`
|
|
258
266
|
});
|
|
259
267
|
} else {
|
|
260
268
|
writes.push({
|
|
@@ -262,7 +270,8 @@ function LeadsBulkBarBody(props) {
|
|
|
262
270
|
label: labelOf(lead),
|
|
263
271
|
kind: 'update',
|
|
264
272
|
data: _extends({
|
|
265
|
-
status
|
|
273
|
+
status,
|
|
274
|
+
statusLabel: choice.label
|
|
266
275
|
}, current === 'unqualified' ? {
|
|
267
276
|
unqualifiedReason: deleteField()
|
|
268
277
|
} : {}, {
|
|
@@ -296,6 +305,7 @@ function LeadsBulkBarBody(props) {
|
|
|
296
305
|
kind: 'update',
|
|
297
306
|
data: {
|
|
298
307
|
status: 'unqualified',
|
|
308
|
+
statusLabel: crmLeadStatusLabelFor(statuses.picklist, 'unqualified'),
|
|
299
309
|
unqualifiedReason: reason,
|
|
300
310
|
updatedAt: serverTimestamp()
|
|
301
311
|
}
|
|
@@ -315,13 +325,9 @@ function LeadsBulkBarBody(props) {
|
|
|
315
325
|
runPlan,
|
|
316
326
|
logFiling,
|
|
317
327
|
hostId,
|
|
318
|
-
props.org
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
downloadTextFile('leads-selected.csv', 'text/csv', leadsCsv(selectedRows, csv));
|
|
322
|
-
}, [
|
|
323
|
-
selectedRows,
|
|
324
|
-
csv
|
|
328
|
+
props.org,
|
|
329
|
+
statusChoices,
|
|
330
|
+
statuses.picklist
|
|
325
331
|
]);
|
|
326
332
|
const canApply = pending === 'owner' || (pending === 'campaign' ? campaignIds.length > 0 : pending === 'status' ? Boolean(value) : Boolean(value.trim()));
|
|
327
333
|
return /*#__PURE__*/ _jsxs(CrmBulkBarFrame, {
|
|
@@ -353,10 +359,10 @@ function LeadsBulkBarBody(props) {
|
|
|
353
359
|
value: value,
|
|
354
360
|
onChange: (event)=>setValue(event.target.value),
|
|
355
361
|
helperText: "Closing a lead goes through Unqualify, which asks why.",
|
|
356
|
-
children:
|
|
357
|
-
value:
|
|
358
|
-
children:
|
|
359
|
-
},
|
|
362
|
+
children: statusChoices.map((choice)=>/*#__PURE__*/ _jsx(MenuItem, {
|
|
363
|
+
value: choice.label,
|
|
364
|
+
children: choice.label
|
|
365
|
+
}, choice.label))
|
|
360
366
|
}) : pending === 'unqualify' ? /*#__PURE__*/ _jsx(TextField, {
|
|
361
367
|
label: "Reason",
|
|
362
368
|
value: value,
|
|
@@ -415,16 +421,10 @@ function LeadsBulkBarBody(props) {
|
|
|
415
421
|
orgId: orgId,
|
|
416
422
|
disabled: busy
|
|
417
423
|
}),
|
|
418
|
-
/*#__PURE__*/ _jsx(
|
|
419
|
-
|
|
420
|
-
disabled: busy,
|
|
421
|
-
onClick: handleExport,
|
|
422
|
-
children: 'Export CSV'
|
|
423
|
-
}),
|
|
424
|
-
/*#__PURE__*/ _jsx(CrmExportAllButton, {
|
|
425
|
-
resource: "leads",
|
|
426
|
-
orgId: orgId,
|
|
424
|
+
/*#__PURE__*/ _jsx(CrmExportButton, {
|
|
425
|
+
resource: CRM_LEADS_RESOURCE,
|
|
427
426
|
hostId: hostId,
|
|
427
|
+
selection: selectedRows.map((row)=>row.$id),
|
|
428
428
|
disabled: busy
|
|
429
429
|
})
|
|
430
430
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/leads-bulk-bar.tsx"],"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'use client'\n\n/**\n * The bar over the Leads table, for whatever rows are ticked (AGL-2662).\n *\n * Hand them to an owner, set their status, unqualify them with one reason,\n * or take them into a spreadsheet. Every act here is a document write and\n * nothing more — a lead's status and owner are the team's own notes on a\n * capture, with no event and no notification behind them, which is why the\n * row's inline select writes them client-direct — so the whole bar goes\n * through the shared batched runner under the same rules.\n *\n * ## A row names its own site\n *\n * A lead lives at `orgs/{orgId}/leads/{leadId}` (AGL-3275), and at the organization\n * level the selection spans sites, so the reference for a write is built\n * from the ROW rather than from a scope the bar was handed: the row knows\n * its site, the bar need not. Under a site every row names the same one.\n *\n * ## What is declined, by name\n *\n * A converted lead's status IS its conversion, stamped by the route, and\n * the bar leaves it alone under Set status and Unqualify the way the row\n * menu and the inline select do. A lead already closed is not unqualified\n * twice. A lead already at the asked status is skipped rather than rewritten\n * so \"Nothing to change\" means what it says.\n */\n\nimport {\n CRM_LEAD_OPEN_STATUSES,\n CRM_LEAD_STATUS_LABELS,\n type CrmLeadFields,\n type CrmLeadStatus,\n crmLeadStatus,\n isCrmLeadOpen,\n readContainerIds,\n} from '@aglyn/aglyn'\nimport ContainerPicker from '@aglyn/tenant-feature-instance/components/container-picker'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport { Button, MenuItem, TextField } from '@mui/material'\nimport { arrayUnion, deleteField, doc, serverTimestamp } from 'firebase/firestore'\nimport { useCallback, useMemo, useState } from 'react'\nimport { useCampaignFilingLog } from '../hooks/use-campaign-filing-log'\nimport { useCrmBulkApply } from '../hooks/use-crm-bulk-apply'\nimport { useCrmCampaigns } from '../hooks/use-crm-campaigns'\nimport type { OrgMemberOptions } from '../hooks/use-org-member-options'\nimport { downloadTextFile } from '../model/contacts-csv'\nimport { crmClientListFields } from '../model/crm-list-query'\nimport {\n type CrmBulkPlan,\n type CrmBulkSkip,\n type CrmBulkWrite,\n crmBulkWriters,\n runCrmBulkWrites,\n} from '../model/crm-bulk-writes'\nimport { type LeadCsvOptions, leadsCsv } from '../model/leads-csv'\nimport {\n type CrmBulkNoun,\n CrmBulkBarFrame,\n CrmBulkValueDialog,\n countNoun,\n} from './crm-bulk-bar-frame'\nimport CrmExportAllButton from './crm-export-all-button'\nimport { CrmBulkShareButton } from './record-sharing-card'\nimport { LeadOwnerSelect } from './lead-owner-select'\nimport { UNQUALIFY_REASON_MAX } from './lead-unqualify-dialog'\nimport { useCrmSharingFollowUp } from '../hooks/use-crm-sharing'\nimport { leadPrimaryGroup } from '../model/contact-holder'\n\n/**\n * One row of the Leads list as the bar needs it: the document's fields,\n * the grid's key, and which document under which site a write names.\n */\nexport type LeadBulkRow = Record<string, unknown> &\n CrmLeadFields & { $id: string; leadId: string }\n\nexport interface LeadsBulkBarProps {\n rows: readonly LeadBulkRow[]\n selected: readonly string[]\n onSelectedChange: (ids: string[]) => void\n /** The section's roster — already read for the Owner column. */\n roster: OrgMemberOptions\n /** How the export names the owner and, at the org level, the site — the list's own. */\n csv?: LeadCsvOptions\n /** The organization these leads belong to; null while it is unresolved. */\n orgId?: string | null\n /**\n * The site the list is read under, or `null` at the organization level\n * where the complete export spans every site (AGL-2662).\n */\n hostId?: string | null\n /**\n * The org the shell passed, for the scope a filing entry carries\n * (AGL-3274): Add to campaign writes one on each lead's Activity, and\n * the entry is visible to whoever sees a record made on this site.\n */\n org?: Record<string, unknown> | null\n}\n\nconst NOUN: CrmBulkNoun = { singular: 'lead', plural: 'leads' }\n\ntype PendingAction = 'owner' | 'status' | 'unqualify' | 'campaign'\n\nconst ACTION_TITLES: Record<PendingAction, string> = {\n owner: 'Set the owner',\n status: 'Set the status',\n unqualify: 'Unqualify',\n campaign: 'Add to campaign',\n}\n\n/** The statuses the bar can set — the open ones; closing goes through Unqualify. */\nconst SETTABLE_STATUSES: readonly CrmLeadStatus[] = CRM_LEAD_OPEN_STATUSES\n\n/** The label a report lists a lead under — its name, else its address. */\nconst labelOf = (lead: LeadBulkRow): string =>\n String(lead['name'] || lead['email'] || lead.leadId)\n\nexport function LeadsBulkBar(props: LeadsBulkBarProps) {\n if (!props.selected.length) return null\n return <LeadsBulkBarBody {...props} />\n}\nLeadsBulkBar.displayName = 'LeadsBulkBar'\n\nfunction LeadsBulkBarBody(props: LeadsBulkBarProps) {\n const { rows, selected, onSelectedChange, roster, csv } = props\n const orgId = props.orgId ?? null\n const hostId = props.hostId ?? null\n const followUpSharing = useCrmSharingFollowUp(hostId, orgId)\n const firestore = useFirestore()\n const { busy, report, apply, dismissReport } = useCrmBulkApply({ recordKind: 'lead' })\n\n const selectedRows = useMemo(() => {\n const chosen = new Set(selected)\n return rows.filter((row) => chosen.has(row.$id))\n }, [rows, selected])\n\n const [pending, setPending] = useState<PendingAction | null>(null)\n const [value, setValue] = useState('')\n /*\n * The campaigns to add the selection to (AGL-3254), and the containers\n * the picker offers — read only while that dialog is open. Under a site,\n * the campaigns placed on it; at the organization level, where a\n * selection spans sites, every campaign in the org.\n */\n const [campaignIds, setCampaignIds] = useState<string[]>([])\n const campaigns = useCrmCampaigns(\n { hostId, orgId },\n { enabled: pending === 'campaign' },\n )\n const logFiling = useCampaignFilingLog({ orgId, hostId, org: props.org })\n\n // The reference a write names is the row's own site and document.\n const writers = useMemo(() => {\n const byId = new Map(rows.map((row) => [row.$id, row]))\n return crmBulkWriters(firestore, (id) => {\n const row = byId.get(id)\n // One org collection (AGL-3275): the row no longer names a site, and\n // the bulk write addresses the same document at either level.\n // `orgs//leads` is a path Firestore accepts the shape of and nothing\n // owns, so an absent org refuses the write rather than composing one.\n if (!orgId) throw new Error('[crm] a lead write needs an organization')\n return doc(firestore, 'orgs', orgId, 'leads', row?.leadId ?? id)\n })\n }, [firestore, rows])\n\n const openAction = (action: PendingAction) => {\n setValue('')\n setCampaignIds([])\n setPending(action)\n }\n\n const runPlan = useCallback(\n (plan: CrmBulkPlan, done: (count: number) => string) =>\n apply({\n attempted: plan.writes.length,\n skipped: plan.skipped,\n job: async () => {\n const outcome = await runCrmBulkWrites(writers, plan.writes, (write) => write.label)\n // A client-direct write owes the sharing rules a re-evaluation (AGL-3336).\n followUpSharing(\n 'leads',\n plan.writes.filter((write) => write.kind === 'update').map((write) => rows.find((row) => row.$id === write.id)?.leadId ?? write.id),\n )\n return outcome\n },\n done,\n }),\n [apply, writers, rows, followUpSharing],\n )\n\n const handleApply = useCallback(async () => {\n if (!pending) return\n const action = pending\n setPending(null)\n const writes: CrmBulkWrite[] = []\n const skipped: CrmBulkSkip[] = []\n if (action === 'owner') {\n for (const lead of selectedRows) {\n writes.push({\n id: lead.$id,\n label: labelOf(lead),\n kind: 'update',\n data: { ownerUid: value ? value : deleteField(), updatedAt: serverTimestamp() },\n })\n }\n await runPlan(\n { writes, skipped },\n (count) => (value ? `Owner set on ${countNoun(count, NOUN)}` : `Owner cleared on ${countNoun(count, NOUN)}`),\n )\n return\n }\n if (action === 'campaign') {\n // Added to what each lead carries — `arrayUnion`, the way a sequence's\n // enroll and the import add one — never in place of it.\n for (const lead of selectedRows) {\n // The union as it will stand, for the list field it moves (AGL-3321).\n const union = [...new Set([...readContainerIds(lead, 'campaign'), ...campaignIds])]\n writes.push({\n id: lead.$id,\n label: labelOf(lead),\n kind: 'update',\n data: {\n campaignIds: arrayUnion(...campaignIds),\n ...crmClientListFields('leads', lead, { campaignIds: union }, ['scopedCampaignIds']),\n updatedAt: serverTimestamp(),\n },\n })\n }\n const outcome = await runPlan(\n { writes, skipped },\n (count) =>\n `Added ${countNoun(count, NOUN)} to ${campaignIds.length === 1 ? 'the campaign' : `${campaignIds.length} campaigns`}`,\n )\n // What landed is written on each lead's Activity (AGL-3274): one\n // \"Filed under\" per campaign the lead was not already in, and none\n // for a lead the store refused — the row's own document is the\n // membership, and the entry only records the act.\n const refused = new Set(outcome.refused.map((entry) => entry.label))\n const named = campaignIds.map((campaignId) => ({\n id: campaignId,\n name: campaigns.options.find((option) => option.value === campaignId)?.label ?? '',\n }))\n for (const lead of selectedRows) {\n if (refused.has(labelOf(lead))) continue\n const already = readContainerIds(lead, 'campaign')\n const filed = named.filter((campaign) => !already.includes(campaign.id))\n // At the organization level each entry carries its own lead's first\n // capturing site, the one the lead's page files as.\n if (filed.length) {\n await logFiling(\n { leadId: lead.leadId },\n { filed },\n hostId ? null : leadPrimaryGroup(lead, props.org ?? null).hostId || null,\n )\n }\n }\n return\n }\n if (action === 'status') {\n const status = value as CrmLeadStatus\n for (const lead of selectedRows) {\n const current = crmLeadStatus(lead)\n if (lead.convertedContactId) {\n skipped.push({ label: labelOf(lead), reason: 'was converted' })\n } else if (current === status) {\n skipped.push({\n label: labelOf(lead),\n reason: `already ${CRM_LEAD_STATUS_LABELS[status]}`,\n })\n } else {\n writes.push({\n id: lead.$id,\n label: labelOf(lead),\n kind: 'update',\n data: {\n status,\n // Reopening an unqualified lead clears its reason, as the row does.\n ...(current === 'unqualified' ? { unqualifiedReason: deleteField() } : {}),\n updatedAt: serverTimestamp(),\n },\n })\n }\n }\n await runPlan(\n { writes, skipped },\n (count) => `Status set on ${countNoun(count, NOUN)}`,\n )\n return\n }\n const reason = value.trim().slice(0, UNQUALIFY_REASON_MAX)\n for (const lead of selectedRows) {\n if (lead.convertedContactId) {\n skipped.push({ label: labelOf(lead), reason: 'was converted' })\n } else if (!isCrmLeadOpen(lead)) {\n skipped.push({ label: labelOf(lead), reason: 'is already closed' })\n } else {\n writes.push({\n id: lead.$id,\n label: labelOf(lead),\n kind: 'update',\n data: {\n status: 'unqualified' satisfies CrmLeadStatus,\n unqualifiedReason: reason,\n updatedAt: serverTimestamp(),\n },\n })\n }\n }\n await runPlan(\n { writes, skipped },\n (count) => `Marked ${countNoun(count, NOUN)} unqualified`,\n )\n }, [pending, value, campaignIds, campaigns.options, selectedRows, runPlan, logFiling, hostId, props.org])\n\n const handleExport = useCallback(() => {\n downloadTextFile('leads-selected.csv', 'text/csv', leadsCsv(selectedRows, csv))\n }, [selectedRows, csv])\n\n const canApply =\n pending === 'owner' ||\n (pending === 'campaign'\n ? campaignIds.length > 0\n : pending === 'status'\n ? Boolean(value)\n : Boolean(value.trim()))\n\n return (\n <CrmBulkBarFrame\n count={selected.length}\n noun={NOUN}\n busy={busy}\n onClear={() => onSelectedChange([])}\n report={report}\n onDismissReport={dismissReport}\n extras={\n <CrmBulkValueDialog\n open={pending !== null}\n title={pending ? ACTION_TITLES[pending] : ''}\n count={selected.length}\n noun={NOUN}\n busy={busy}\n canApply={canApply}\n applyLabel={pending === 'unqualify' ? 'Unqualify' : pending === 'campaign' ? 'Add' : undefined}\n onClose={() => setPending(null)}\n onApply={() => void handleApply()}\n >\n {pending === 'owner' ? (\n <LeadOwnerSelect value={value} onChange={setValue} roster={roster} size=\"medium\" />\n ) : pending === 'status' ? (\n <TextField\n select\n size=\"small\"\n label=\"Status\"\n value={value}\n onChange={(event) => setValue(event.target.value)}\n helperText=\"Closing a lead goes through Unqualify, which asks why.\"\n >\n {SETTABLE_STATUSES.map((status) => (\n <MenuItem key={status} value={status}>\n {CRM_LEAD_STATUS_LABELS[status]}\n </MenuItem>\n ))}\n </TextField>\n ) : pending === 'unqualify' ? (\n <TextField\n label=\"Reason\"\n value={value}\n onChange={(event) => setValue(event.target.value)}\n multiline\n minRows={2}\n autoFocus\n helperText=\"One reason for every selected lead, kept on each so it can be counted later.\"\n slotProps={{ htmlInput: { maxLength: UNQUALIFY_REASON_MAX } }}\n />\n ) : pending === 'campaign' ? (\n <ContainerPicker\n kind=\"campaign\"\n options={campaigns.options}\n value={campaignIds}\n onChange={setCampaignIds}\n helperText=\"Added to the campaigns each selected lead is already in. It does not decide who a campaign mails.\"\n empty={campaigns.ready && !campaigns.options.length}\n emptyText={\n hostId\n ? 'This site has no campaigns yet. Create one from Marketing to file leads under it.'\n : 'There are no campaigns yet. Create one from Marketing to file leads under it.'\n }\n />\n ) : null}\n </CrmBulkValueDialog>\n }\n >\n <Button size=\"small\" disabled={busy} onClick={() => openAction('owner')}>\n {'Set owner'}\n </Button>\n <Button size=\"small\" disabled={busy} onClick={() => openAction('status')}>\n {'Set status'}\n </Button>\n <Button size=\"small\" disabled={busy} onClick={() => openAction('unqualify')}>\n {'Unqualify'}\n </Button>\n <Button size=\"small\" disabled={busy} onClick={() => openAction('campaign')}>\n {'Add to campaign'}\n </Button>\n <CrmBulkShareButton\n object=\"leads\"\n ids={selectedRows.map((row) => row.leadId ?? row.$id)}\n hostId={hostId}\n orgId={orgId}\n disabled={busy}\n />\n <Button size=\"small\" disabled={busy} onClick={handleExport}>\n {'Export CSV'}\n </Button>\n {/*\n The selection's file is the rows on screen; this one is the whole\n collection, streamed by the server (AGL-2662).\n */}\n <CrmExportAllButton\n resource=\"leads\"\n orgId={orgId}\n hostId={hostId}\n disabled={busy}\n />\n </CrmBulkBarFrame>\n )\n}\nLeadsBulkBarBody.displayName = 'LeadsBulkBarBody'\n\nexport default LeadsBulkBar\n"],"names":["CRM_LEAD_OPEN_STATUSES","CRM_LEAD_STATUS_LABELS","crmLeadStatus","isCrmLeadOpen","readContainerIds","ContainerPicker","useFirestore","Button","MenuItem","TextField","arrayUnion","deleteField","doc","serverTimestamp","useCallback","useMemo","useState","useCampaignFilingLog","useCrmBulkApply","useCrmCampaigns","downloadTextFile","crmClientListFields","crmBulkWriters","runCrmBulkWrites","leadsCsv","CrmBulkBarFrame","CrmBulkValueDialog","countNoun","CrmExportAllButton","CrmBulkShareButton","LeadOwnerSelect","UNQUALIFY_REASON_MAX","useCrmSharingFollowUp","leadPrimaryGroup","NOUN","singular","plural","ACTION_TITLES","owner","status","unqualify","campaign","SETTABLE_STATUSES","labelOf","lead","String","leadId","LeadsBulkBar","props","selected","length","LeadsBulkBarBody","displayName","rows","onSelectedChange","roster","csv","orgId","hostId","followUpSharing","firestore","busy","report","apply","dismissReport","recordKind","selectedRows","chosen","Set","filter","row","has","$id","pending","setPending","value","setValue","campaignIds","setCampaignIds","campaigns","enabled","logFiling","org","writers","byId","Map","map","id","get","Error","openAction","action","runPlan","plan","done","attempted","writes","skipped","job","outcome","write","label","kind","find","handleApply","push","data","ownerUid","updatedAt","count","union","refused","entry","named","campaignId","name","options","option","already","filed","includes","current","convertedContactId","reason","unqualifiedReason","trim","slice","handleExport","canApply","Boolean","noun","onClear","onDismissReport","extras","open","title","applyLabel","undefined","onClose","onApply","onChange","size","select","event","target","helperText","multiline","minRows","autoFocus","slotProps","htmlInput","maxLength","empty","ready","emptyText","disabled","onClick","object","ids","resource"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/leads-bulk-bar.tsx"],"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'use client'\n\n/**\n * The bar over the Leads table, for whatever rows are ticked (AGL-2662).\n *\n * Hand them to an owner, set their status, unqualify them with one reason,\n * or take them into a spreadsheet. Every act here is a document write and\n * nothing more — a lead's status and owner are the team's own notes on a\n * capture, with no event and no notification behind them, which is why the\n * row's inline select writes them client-direct — so the whole bar goes\n * through the shared batched runner under the same rules.\n *\n * ## A row names its own site\n *\n * A lead lives at `orgs/{orgId}/leads/{leadId}` (AGL-3275), and at the organization\n * level the selection spans sites, so the reference for a write is built\n * from the ROW rather than from a scope the bar was handed: the row knows\n * its site, the bar need not. Under a site every row names the same one.\n *\n * ## What is declined, by name\n *\n * A converted lead's status IS its conversion, stamped by the route, and\n * the bar leaves it alone under Set status and Unqualify the way the row\n * menu and the inline select do. A lead already closed is not unqualified\n * twice. A lead already at the asked status is skipped rather than rewritten\n * so \"Nothing to change\" means what it says.\n */\n\nimport {\n CRM_LEAD_OPEN_STATUSES,\n type CrmLeadFields,\n type CrmLeadStatus,\n crmLeadStatus,\n crmLeadStatusLabel,\n crmLeadStatusLabelFor,\n crmLeadStatusOptions,\n isCrmLeadOpen,\n readContainerIds,\n} from '@aglyn/aglyn'\nimport ContainerPicker from '@aglyn/tenant-feature-instance/components/container-picker'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport { Button, MenuItem, TextField } from '@mui/material'\nimport { arrayUnion, deleteField, doc, serverTimestamp } from 'firebase/firestore'\nimport { useCallback, useMemo, useState } from 'react'\nimport { useCampaignFilingLog } from '../hooks/use-campaign-filing-log'\nimport { useCrmBulkApply } from '../hooks/use-crm-bulk-apply'\nimport { useLeadStatusPicklist } from '../hooks/use-lead-status-picklist'\nimport { useCrmCampaigns } from '../hooks/use-crm-campaigns'\nimport type { OrgMemberOptions } from '../hooks/use-org-member-options'\nimport { crmClientListFields } from '../model/crm-list-query'\nimport {\n type CrmBulkPlan,\n type CrmBulkSkip,\n type CrmBulkWrite,\n crmBulkWriters,\n runCrmBulkWrites,\n} from '../model/crm-bulk-writes'\nimport {\n type CrmBulkNoun,\n CrmBulkBarFrame,\n CrmBulkValueDialog,\n countNoun,\n} from './crm-bulk-bar-frame'\nimport { CrmExportButton } from './crm-transfer-buttons'\nimport { CRM_LEADS_RESOURCE } from '../transfer/fields'\nimport { CrmBulkShareButton } from './record-sharing-card'\nimport { LeadOwnerSelect } from './lead-owner-select'\nimport { UNQUALIFY_REASON_MAX } from './lead-unqualify-dialog'\nimport { useCrmSharingFollowUp } from '../hooks/use-crm-sharing'\nimport { leadPrimaryGroup } from '../model/contact-holder'\n\n/**\n * One row of the Leads list as the bar needs it: the document's fields,\n * the grid's key, and which document under which site a write names.\n */\nexport type LeadBulkRow = Record<string, unknown> &\n CrmLeadFields & { $id: string; leadId: string }\n\nexport interface LeadsBulkBarProps {\n rows: readonly LeadBulkRow[]\n selected: readonly string[]\n onSelectedChange: (ids: string[]) => void\n /** The section's roster — already read for the Owner column. */\n roster: OrgMemberOptions\n /** The organization these leads belong to; null while it is unresolved. */\n orgId?: string | null\n /**\n * The site the list is read under, or `null` at the organization level\n * where the complete export spans every site (AGL-2662).\n */\n hostId?: string | null\n /**\n * The org the shell passed, for the scope a filing entry carries\n * (AGL-3274): Add to campaign writes one on each lead's Activity, and\n * the entry is visible to whoever sees a record made on this site.\n */\n org?: Record<string, unknown> | null\n}\n\nconst NOUN: CrmBulkNoun = { singular: 'lead', plural: 'leads' }\n\ntype PendingAction = 'owner' | 'status' | 'unqualify' | 'campaign'\n\nconst ACTION_TITLES: Record<PendingAction, string> = {\n owner: 'Set the owner',\n status: 'Set the status',\n unqualify: 'Unqualify',\n campaign: 'Add to campaign',\n}\n\n/** The statuses the bar can set — the open ones; closing goes through Unqualify. */\nconst SETTABLE_STATUSES: readonly CrmLeadStatus[] = CRM_LEAD_OPEN_STATUSES\n\n/** The label a report lists a lead under — its name, else its address. */\nconst labelOf = (lead: LeadBulkRow): string =>\n String(lead['name'] || lead['email'] || lead.leadId)\n\nexport function LeadsBulkBar(props: LeadsBulkBarProps) {\n if (!props.selected.length) return null\n return <LeadsBulkBarBody {...props} />\n}\nLeadsBulkBar.displayName = 'LeadsBulkBar'\n\nfunction LeadsBulkBarBody(props: LeadsBulkBarProps) {\n const { rows, selected, onSelectedChange, roster } = props\n const orgId = props.orgId ?? null\n const hostId = props.hostId ?? null\n const followUpSharing = useCrmSharingFollowUp(hostId, orgId)\n // The org's lead status values (AGL-3512): Set status picks a value by its\n // label, and every write stamps the meaning and the label together.\n const statuses = useLeadStatusPicklist(orgId)\n const statusChoices = useMemo(\n () => crmLeadStatusOptions(statuses.picklist, SETTABLE_STATUSES),\n [statuses.picklist],\n )\n const firestore = useFirestore()\n const { busy, report, apply, dismissReport } = useCrmBulkApply({ recordKind: 'lead' })\n\n const selectedRows = useMemo(() => {\n const chosen = new Set(selected)\n return rows.filter((row) => chosen.has(row.$id))\n }, [rows, selected])\n\n const [pending, setPending] = useState<PendingAction | null>(null)\n const [value, setValue] = useState('')\n /*\n * The campaigns to add the selection to (AGL-3254), and the containers\n * the picker offers — read only while that dialog is open. Under a site,\n * the campaigns placed on it; at the organization level, where a\n * selection spans sites, every campaign in the org.\n */\n const [campaignIds, setCampaignIds] = useState<string[]>([])\n const campaigns = useCrmCampaigns(\n { hostId, orgId },\n { enabled: pending === 'campaign' },\n )\n const logFiling = useCampaignFilingLog({ orgId, hostId, org: props.org })\n\n // The reference a write names is the row's own site and document.\n const writers = useMemo(() => {\n const byId = new Map(rows.map((row) => [row.$id, row]))\n return crmBulkWriters(firestore, (id) => {\n const row = byId.get(id)\n // One org collection (AGL-3275): the row no longer names a site, and\n // the bulk write addresses the same document at either level.\n // `orgs//leads` is a path Firestore accepts the shape of and nothing\n // owns, so an absent org refuses the write rather than composing one.\n if (!orgId) throw new Error('[crm] a lead write needs an organization')\n return doc(firestore, 'orgs', orgId, 'leads', row?.leadId ?? id)\n })\n }, [firestore, rows])\n\n const openAction = (action: PendingAction) => {\n setValue('')\n setCampaignIds([])\n setPending(action)\n }\n\n const runPlan = useCallback(\n (plan: CrmBulkPlan, done: (count: number) => string) =>\n apply({\n attempted: plan.writes.length,\n skipped: plan.skipped,\n job: async () => {\n const outcome = await runCrmBulkWrites(writers, plan.writes, (write) => write.label)\n // A client-direct write owes the sharing rules a re-evaluation (AGL-3336).\n followUpSharing(\n 'leads',\n plan.writes.filter((write) => write.kind === 'update').map((write) => rows.find((row) => row.$id === write.id)?.leadId ?? write.id),\n )\n return outcome\n },\n done,\n }),\n [apply, writers, rows, followUpSharing],\n )\n\n const handleApply = useCallback(async () => {\n if (!pending) return\n const action = pending\n setPending(null)\n const writes: CrmBulkWrite[] = []\n const skipped: CrmBulkSkip[] = []\n if (action === 'owner') {\n for (const lead of selectedRows) {\n writes.push({\n id: lead.$id,\n label: labelOf(lead),\n kind: 'update',\n data: { ownerUid: value ? value : deleteField(), updatedAt: serverTimestamp() },\n })\n }\n await runPlan(\n { writes, skipped },\n (count) => (value ? `Owner set on ${countNoun(count, NOUN)}` : `Owner cleared on ${countNoun(count, NOUN)}`),\n )\n return\n }\n if (action === 'campaign') {\n // Added to what each lead carries — `arrayUnion`, the way a sequence's\n // enroll and the import add one — never in place of it.\n for (const lead of selectedRows) {\n // The union as it will stand, for the list field it moves (AGL-3321).\n const union = [...new Set([...readContainerIds(lead, 'campaign'), ...campaignIds])]\n writes.push({\n id: lead.$id,\n label: labelOf(lead),\n kind: 'update',\n data: {\n campaignIds: arrayUnion(...campaignIds),\n ...crmClientListFields('leads', lead, { campaignIds: union }, ['scopedCampaignIds']),\n updatedAt: serverTimestamp(),\n },\n })\n }\n const outcome = await runPlan(\n { writes, skipped },\n (count) =>\n `Added ${countNoun(count, NOUN)} to ${campaignIds.length === 1 ? 'the campaign' : `${campaignIds.length} campaigns`}`,\n )\n // What landed is written on each lead's Activity (AGL-3274): one\n // \"Filed under\" per campaign the lead was not already in, and none\n // for a lead the store refused — the row's own document is the\n // membership, and the entry only records the act.\n const refused = new Set(outcome.refused.map((entry) => entry.label))\n const named = campaignIds.map((campaignId) => ({\n id: campaignId,\n name: campaigns.options.find((option) => option.value === campaignId)?.label ?? '',\n }))\n for (const lead of selectedRows) {\n if (refused.has(labelOf(lead))) continue\n const already = readContainerIds(lead, 'campaign')\n const filed = named.filter((campaign) => !already.includes(campaign.id))\n // At the organization level each entry carries its own lead's first\n // capturing site, the one the lead's page files as.\n if (filed.length) {\n await logFiling(\n { leadId: lead.leadId },\n { filed },\n hostId ? null : leadPrimaryGroup(lead, props.org ?? null).hostId || null,\n )\n }\n }\n return\n }\n if (action === 'status') {\n const choice = statusChoices.find((entry) => entry.label === value)\n if (!choice) return\n const status = choice.status\n for (const lead of selectedRows) {\n const current = crmLeadStatus(lead)\n if (lead.convertedContactId) {\n skipped.push({ label: labelOf(lead), reason: 'was converted' })\n } else if (current === status && crmLeadStatusLabel(lead, statuses.picklist) === choice.label) {\n skipped.push({\n label: labelOf(lead),\n reason: `already ${choice.label}`,\n })\n } else {\n writes.push({\n id: lead.$id,\n label: labelOf(lead),\n kind: 'update',\n data: {\n status,\n statusLabel: choice.label,\n // Reopening an unqualified lead clears its reason, as the row does.\n ...(current === 'unqualified' ? { unqualifiedReason: deleteField() } : {}),\n updatedAt: serverTimestamp(),\n },\n })\n }\n }\n await runPlan(\n { writes, skipped },\n (count) => `Status set on ${countNoun(count, NOUN)}`,\n )\n return\n }\n const reason = value.trim().slice(0, UNQUALIFY_REASON_MAX)\n for (const lead of selectedRows) {\n if (lead.convertedContactId) {\n skipped.push({ label: labelOf(lead), reason: 'was converted' })\n } else if (!isCrmLeadOpen(lead)) {\n skipped.push({ label: labelOf(lead), reason: 'is already closed' })\n } else {\n writes.push({\n id: lead.$id,\n label: labelOf(lead),\n kind: 'update',\n data: {\n status: 'unqualified' satisfies CrmLeadStatus,\n statusLabel: crmLeadStatusLabelFor(statuses.picklist, 'unqualified'),\n unqualifiedReason: reason,\n updatedAt: serverTimestamp(),\n },\n })\n }\n }\n await runPlan(\n { writes, skipped },\n (count) => `Marked ${countNoun(count, NOUN)} unqualified`,\n )\n }, [pending, value, campaignIds, campaigns.options, selectedRows, runPlan, logFiling, hostId, props.org, statusChoices, statuses.picklist])\n\n const canApply =\n pending === 'owner' ||\n (pending === 'campaign'\n ? campaignIds.length > 0\n : pending === 'status'\n ? Boolean(value)\n : Boolean(value.trim()))\n\n return (\n <CrmBulkBarFrame\n count={selected.length}\n noun={NOUN}\n busy={busy}\n onClear={() => onSelectedChange([])}\n report={report}\n onDismissReport={dismissReport}\n extras={\n <CrmBulkValueDialog\n open={pending !== null}\n title={pending ? ACTION_TITLES[pending] : ''}\n count={selected.length}\n noun={NOUN}\n busy={busy}\n canApply={canApply}\n applyLabel={pending === 'unqualify' ? 'Unqualify' : pending === 'campaign' ? 'Add' : undefined}\n onClose={() => setPending(null)}\n onApply={() => void handleApply()}\n >\n {pending === 'owner' ? (\n <LeadOwnerSelect value={value} onChange={setValue} roster={roster} size=\"medium\" />\n ) : pending === 'status' ? (\n <TextField\n select\n size=\"small\"\n label=\"Status\"\n value={value}\n onChange={(event) => setValue(event.target.value)}\n helperText=\"Closing a lead goes through Unqualify, which asks why.\"\n >\n {statusChoices.map((choice) => (\n <MenuItem key={choice.label} value={choice.label}>\n {choice.label}\n </MenuItem>\n ))}\n </TextField>\n ) : pending === 'unqualify' ? (\n <TextField\n label=\"Reason\"\n value={value}\n onChange={(event) => setValue(event.target.value)}\n multiline\n minRows={2}\n autoFocus\n helperText=\"One reason for every selected lead, kept on each so it can be counted later.\"\n slotProps={{ htmlInput: { maxLength: UNQUALIFY_REASON_MAX } }}\n />\n ) : pending === 'campaign' ? (\n <ContainerPicker\n kind=\"campaign\"\n options={campaigns.options}\n value={campaignIds}\n onChange={setCampaignIds}\n helperText=\"Added to the campaigns each selected lead is already in. It does not decide who a campaign mails.\"\n empty={campaigns.ready && !campaigns.options.length}\n emptyText={\n hostId\n ? 'This site has no campaigns yet. Create one from Marketing to file leads under it.'\n : 'There are no campaigns yet. Create one from Marketing to file leads under it.'\n }\n />\n ) : null}\n </CrmBulkValueDialog>\n }\n >\n <Button size=\"small\" disabled={busy} onClick={() => openAction('owner')}>\n {'Set owner'}\n </Button>\n <Button size=\"small\" disabled={busy} onClick={() => openAction('status')}>\n {'Set status'}\n </Button>\n <Button size=\"small\" disabled={busy} onClick={() => openAction('unqualify')}>\n {'Unqualify'}\n </Button>\n <Button size=\"small\" disabled={busy} onClick={() => openAction('campaign')}>\n {'Add to campaign'}\n </Button>\n <CrmBulkShareButton\n object=\"leads\"\n ids={selectedRows.map((row) => row.leadId ?? row.$id)}\n hostId={hostId}\n orgId={orgId}\n disabled={busy}\n />\n {/* The selection through the export dialog: every field, chosen (AGL-3528). */}\n <CrmExportButton\n resource={CRM_LEADS_RESOURCE}\n hostId={hostId}\n selection={selectedRows.map((row) => row.$id)}\n disabled={busy}\n />\n </CrmBulkBarFrame>\n )\n}\nLeadsBulkBarBody.displayName = 'LeadsBulkBarBody'\n\nexport default LeadsBulkBar\n"],"names":["CRM_LEAD_OPEN_STATUSES","crmLeadStatus","crmLeadStatusLabel","crmLeadStatusLabelFor","crmLeadStatusOptions","isCrmLeadOpen","readContainerIds","ContainerPicker","useFirestore","Button","MenuItem","TextField","arrayUnion","deleteField","doc","serverTimestamp","useCallback","useMemo","useState","useCampaignFilingLog","useCrmBulkApply","useLeadStatusPicklist","useCrmCampaigns","crmClientListFields","crmBulkWriters","runCrmBulkWrites","CrmBulkBarFrame","CrmBulkValueDialog","countNoun","CrmExportButton","CRM_LEADS_RESOURCE","CrmBulkShareButton","LeadOwnerSelect","UNQUALIFY_REASON_MAX","useCrmSharingFollowUp","leadPrimaryGroup","NOUN","singular","plural","ACTION_TITLES","owner","status","unqualify","campaign","SETTABLE_STATUSES","labelOf","lead","String","leadId","LeadsBulkBar","props","selected","length","LeadsBulkBarBody","displayName","rows","onSelectedChange","roster","orgId","hostId","followUpSharing","statuses","statusChoices","picklist","firestore","busy","report","apply","dismissReport","recordKind","selectedRows","chosen","Set","filter","row","has","$id","pending","setPending","value","setValue","campaignIds","setCampaignIds","campaigns","enabled","logFiling","org","writers","byId","Map","map","id","get","Error","openAction","action","runPlan","plan","done","attempted","writes","skipped","job","outcome","write","label","kind","find","handleApply","push","data","ownerUid","updatedAt","count","union","refused","entry","named","campaignId","name","options","option","already","filed","includes","choice","current","convertedContactId","reason","statusLabel","unqualifiedReason","trim","slice","canApply","Boolean","noun","onClear","onDismissReport","extras","open","title","applyLabel","undefined","onClose","onApply","onChange","size","select","event","target","helperText","multiline","minRows","autoFocus","slotProps","htmlInput","maxLength","empty","ready","emptyText","disabled","onClick","object","ids","resource","selection"],"mappings":"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"}
|