@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
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/ 'use client';
|
|
17
17
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
18
|
-
import { dealStageById, pluginDocsHelp, weightedDealAmountCents } from "@aglyn/aglyn";
|
|
18
|
+
import { CRM_FORECAST_CATEGORY_LABELS, dealForecastCategory, dealProbability, dealStageById, pluginDocsHelp, readDealProbability, weightedDealAmountCents } from "@aglyn/aglyn";
|
|
19
19
|
import { AppLink, CardDisplay } from "@aglyn/shared-ui-jsx";
|
|
20
20
|
import { Link, Stack, Typography } from "@mui/material";
|
|
21
21
|
import { formatContactCustomValue } from "./contact-custom-columns.js";
|
|
@@ -57,11 +57,18 @@ function Row(props) {
|
|
|
57
57
|
* a stored figure would go stale the moment they did. The contact and the
|
|
58
58
|
* company are links into their own pages — the id is the link; the name on
|
|
59
59
|
* the deal is a caption that may lag a rename.
|
|
60
|
+
*
|
|
61
|
+
* Salesforce's Opportunity fields (AGL-3516) follow: the Type, the Lead
|
|
62
|
+
* source, the Next step, the probability — the deal's own override, with
|
|
63
|
+
* its stage's beside it, or the stage's — the forecast category and the
|
|
64
|
+
* campaign. Edit changes them; a stage move re-stamps the last two.
|
|
60
65
|
*/ export function DealPropertiesCard(props) {
|
|
61
66
|
var _ref;
|
|
62
|
-
const { deal, pipeline, ownerLabel, routes, customFields = [] } = props;
|
|
67
|
+
const { deal, pipeline, ownerLabel, routes, customFields = [], campaignName } = props;
|
|
63
68
|
const stage = dealStageById(pipeline, deal.stageId);
|
|
64
69
|
const weighted = weightedDealAmountCents(deal, stage);
|
|
70
|
+
const probability = dealProbability(deal, stage);
|
|
71
|
+
const overridden = typeof readDealProbability(deal.probability) === 'number';
|
|
65
72
|
const createdMs = timestampMs(deal.createdAt);
|
|
66
73
|
return /*#__PURE__*/ _jsx(CardDisplay, {
|
|
67
74
|
header: 'Properties',
|
|
@@ -79,12 +86,32 @@ function Row(props) {
|
|
|
79
86
|
}),
|
|
80
87
|
/*#__PURE__*/ _jsx(Row, {
|
|
81
88
|
label: "Weighted value",
|
|
82
|
-
children: `${formatMoney(weighted, deal.currency)}` + (deal.status === 'open' && stage ? ` at ${
|
|
89
|
+
children: `${formatMoney(weighted, deal.currency)}` + (deal.status === 'open' && stage && probability !== null ? ` at ${probability}%` : '')
|
|
90
|
+
}),
|
|
91
|
+
/*#__PURE__*/ _jsx(Row, {
|
|
92
|
+
label: "Probability",
|
|
93
|
+
children: probability === null ? 'Not set' : overridden && stage ? `${probability}% · from stage: ${stage.probability}%` : `${probability}%`
|
|
94
|
+
}),
|
|
95
|
+
/*#__PURE__*/ _jsx(Row, {
|
|
96
|
+
label: "Forecast category",
|
|
97
|
+
children: CRM_FORECAST_CATEGORY_LABELS[dealForecastCategory(deal, stage)]
|
|
83
98
|
}),
|
|
84
99
|
/*#__PURE__*/ _jsx(Row, {
|
|
85
100
|
label: "Expected close",
|
|
86
101
|
children: typeof deal.expectedCloseAtMs === 'number' ? new Date(deal.expectedCloseAtMs).toLocaleDateString() : 'Not set'
|
|
87
102
|
}),
|
|
103
|
+
/*#__PURE__*/ _jsx(Row, {
|
|
104
|
+
label: "Type",
|
|
105
|
+
children: deal.type || 'Not set'
|
|
106
|
+
}),
|
|
107
|
+
/*#__PURE__*/ _jsx(Row, {
|
|
108
|
+
label: "Lead source",
|
|
109
|
+
children: deal.leadSource || 'Not set'
|
|
110
|
+
}),
|
|
111
|
+
/*#__PURE__*/ _jsx(Row, {
|
|
112
|
+
label: "Next step",
|
|
113
|
+
children: deal.nextStep || 'Not set'
|
|
114
|
+
}),
|
|
88
115
|
/*#__PURE__*/ _jsx(Row, {
|
|
89
116
|
label: "Owner",
|
|
90
117
|
children: ownerLabel || 'Nobody yet'
|
|
@@ -103,6 +130,10 @@ function Row(props) {
|
|
|
103
130
|
children: deal.companyName || 'Open company'
|
|
104
131
|
}) : 'None'
|
|
105
132
|
}),
|
|
133
|
+
/*#__PURE__*/ _jsx(Row, {
|
|
134
|
+
label: "Campaign",
|
|
135
|
+
children: campaignName || 'None'
|
|
136
|
+
}),
|
|
106
137
|
/*#__PURE__*/ _jsx(Row, {
|
|
107
138
|
label: "Pipeline",
|
|
108
139
|
children: (_ref = pipeline == null ? void 0 : pipeline.name) != null ? _ref : deal.pipelineId
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/deal-properties-card.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 {\n type ContactFieldDefinition,\n dealStageById,\n pluginDocsHelp,\n weightedDealAmountCents,\n} from '@aglyn/aglyn'\nimport { AppLink, CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { Link, Stack, Typography } from '@mui/material'\nimport type { ReactNode } from 'react'\nimport { formatContactCustomValue } from './contact-custom-columns'\nimport {\n type DealDoc,\n formatMoney,\n type PipelineDoc,\n timestampMs,\n} from '../model/deal-board-model'\nimport type { CrmRoutes } from '../model/crm-routes'\n\nexport interface DealPropertiesCardProps {\n deal: DealDoc\n pipeline: PipelineDoc | null\n ownerLabel: string\n routes: CrmRoutes\n /**\n * The org's ACTIVE deal field definitions (AGL-2661), one row each under\n * the fixed properties. Handed in by the page, which resolves the org\n * once for every card on it.\n */\n customFields?: readonly ContactFieldDefinition[]\n}\n\nfunction Row(props: { label: string; children: ReactNode }) {\n return (\n <Stack direction=\"row\" spacing={2} sx={{ alignItems: 'baseline' }}>\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ width: 120, flexShrink: 0 }}\n >\n {props.label}\n </Typography>\n <Typography variant=\"body2\" component=\"div\" sx={{ flex: 1, minWidth: 0 }}>\n {props.children}\n </Typography>\n </Stack>\n )\n}\n\n/**\n * What the deal is worth and who it is with (AGL-2598).\n *\n * The weighted value is computed here from the live pipeline rather than\n * stored, because the stage's probability is the merchant's to change and\n * a stored figure would go stale the moment they did. The contact and the\n * company are links into their own pages — the id is the link; the name on\n * the deal is a caption that may lag a rename.\n */\nexport function DealPropertiesCard(props: DealPropertiesCardProps) {\n const { deal, pipeline, ownerLabel, routes, customFields = [] } = props\n const stage = dealStageById(pipeline, deal.stageId)\n const weighted = weightedDealAmountCents(deal, stage)\n const createdMs = timestampMs(deal.createdAt)\n return (\n <CardDisplay\n header={'Properties'}\n help={pluginDocsHelp('deals', { anchor: '#a-deals-page' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1.25}>\n <Row label=\"Amount\">\n {typeof deal.amountCents === 'number'\n ? formatMoney(deal.amountCents, deal.currency)\n : 'Not set'}\n </Row>\n <Row label=\"Weighted value\">\n {`${formatMoney(weighted, deal.currency)}` +\n (deal.status === 'open' && stage ? ` at ${stage.probability}%` : '')}\n </Row>\n <Row label=\"Expected close\">\n {typeof deal.expectedCloseAtMs === 'number'\n ? new Date(deal.expectedCloseAtMs).toLocaleDateString()\n : 'Not set'}\n </Row>\n <Row label=\"Owner\">{ownerLabel || 'Nobody yet'}</Row>\n <Row label=\"Contact\">\n {deal.contactId ? (\n <AppLink href={routes.contact(deal.contactId)}>\n {deal.contactName || 'Open contact'}\n </AppLink>\n ) : (\n 'None'\n )}\n </Row>\n <Row label=\"Company\">\n {deal.companyId ? (\n <AppLink href={routes.company(deal.companyId)}>\n {deal.companyName || 'Open company'}\n </AppLink>\n ) : (\n 'None'\n )}\n </Row>\n <Row label=\"Pipeline\">{pipeline?.name ?? deal.pipelineId}</Row>\n <Row label=\"Created\">\n {createdMs ? new Date(createdMs).toLocaleDateString() : '—'}\n </Row>\n {deal.notes ? (\n <Row label=\"Notes\">\n <Typography variant=\"body2\" sx={{ whiteSpace: 'pre-wrap' }}>\n {deal.notes}\n </Typography>\n </Row>\n ) : null}\n {/*\n The org's own deal fields (AGL-2661), after the built-in ones under\n \"More fields\", as a record's Details keep them; none defined, none\n drawn.\n */}\n {customFields.length ? (\n <Typography variant=\"subtitle2\" sx={{ pt: 1 }}>\n {'More fields'}\n </Typography>\n ) : null}\n {customFields.map((definition) => {\n const text = formatContactCustomValue(definition, deal.custom?.[definition.key])\n return (\n <Row key={definition.key} label={definition.label || definition.key}>\n {text && definition.type === 'url' ? (\n <Link href={text} target=\"_blank\" rel=\"noreferrer noopener\">\n {text}\n </Link>\n ) : (\n text || 'Not set'\n )}\n </Row>\n )\n })}\n </Stack>\n </CardDisplay>\n )\n}\nDealPropertiesCard.displayName = 'DealPropertiesCard'\n\nexport default DealPropertiesCard\n"],"names":["dealStageById","pluginDocsHelp","weightedDealAmountCents","AppLink","CardDisplay","Link","Stack","Typography","formatContactCustomValue","formatMoney","timestampMs","Row","props","direction","spacing","sx","alignItems","variant","color","width","flexShrink","label","component","flex","minWidth","children","DealPropertiesCard","deal","pipeline","ownerLabel","routes","customFields","stage","stageId","weighted","createdMs","createdAt","header","help","anchor","contentGutterX","contentGutterY","amountCents","currency","status","probability","expectedCloseAtMs","Date","toLocaleDateString","contactId","href","contact","contactName","companyId","company","companyName","name","pipelineId","notes","whiteSpace","length","pt","map","definition","text","custom","key","type","target","rel","displayName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/deal-properties-card.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 {\n type ContactFieldDefinition,\n CRM_FORECAST_CATEGORY_LABELS,\n dealForecastCategory,\n dealProbability,\n dealStageById,\n pluginDocsHelp,\n readDealProbability,\n weightedDealAmountCents,\n} from '@aglyn/aglyn'\nimport { AppLink, CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { Link, Stack, Typography } from '@mui/material'\nimport type { ReactNode } from 'react'\nimport { formatContactCustomValue } from './contact-custom-columns'\nimport {\n type DealDoc,\n formatMoney,\n type PipelineDoc,\n timestampMs,\n} from '../model/deal-board-model'\nimport type { CrmRoutes } from '../model/crm-routes'\n\nexport interface DealPropertiesCardProps {\n deal: DealDoc\n pipeline: PipelineDoc | null\n ownerLabel: string\n routes: CrmRoutes\n /**\n * The org's ACTIVE deal field definitions (AGL-2661), one row each under\n * the fixed properties. Handed in by the page, which resolves the org\n * once for every card on it.\n */\n customFields?: readonly ContactFieldDefinition[]\n /** The deal's campaign by name, resolved by the page; absent when it names none. */\n campaignName?: string\n}\n\nfunction Row(props: { label: string; children: ReactNode }) {\n return (\n <Stack direction=\"row\" spacing={2} sx={{ alignItems: 'baseline' }}>\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ width: 120, flexShrink: 0 }}\n >\n {props.label}\n </Typography>\n <Typography variant=\"body2\" component=\"div\" sx={{ flex: 1, minWidth: 0 }}>\n {props.children}\n </Typography>\n </Stack>\n )\n}\n\n/**\n * What the deal is worth and who it is with (AGL-2598).\n *\n * The weighted value is computed here from the live pipeline rather than\n * stored, because the stage's probability is the merchant's to change and\n * a stored figure would go stale the moment they did. The contact and the\n * company are links into their own pages — the id is the link; the name on\n * the deal is a caption that may lag a rename.\n *\n * Salesforce's Opportunity fields (AGL-3516) follow: the Type, the Lead\n * source, the Next step, the probability — the deal's own override, with\n * its stage's beside it, or the stage's — the forecast category and the\n * campaign. Edit changes them; a stage move re-stamps the last two.\n */\nexport function DealPropertiesCard(props: DealPropertiesCardProps) {\n const { deal, pipeline, ownerLabel, routes, customFields = [], campaignName } = props\n const stage = dealStageById(pipeline, deal.stageId)\n const weighted = weightedDealAmountCents(deal, stage)\n const probability = dealProbability(deal, stage)\n const overridden = typeof readDealProbability(deal.probability) === 'number'\n const createdMs = timestampMs(deal.createdAt)\n return (\n <CardDisplay\n header={'Properties'}\n help={pluginDocsHelp('deals', { anchor: '#a-deals-page' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1.25}>\n <Row label=\"Amount\">\n {typeof deal.amountCents === 'number'\n ? formatMoney(deal.amountCents, deal.currency)\n : 'Not set'}\n </Row>\n <Row label=\"Weighted value\">\n {`${formatMoney(weighted, deal.currency)}` +\n (deal.status === 'open' && stage && probability !== null ? ` at ${probability}%` : '')}\n </Row>\n <Row label=\"Probability\">\n {probability === null\n ? 'Not set'\n : overridden && stage\n ? `${probability}% · from stage: ${stage.probability}%`\n : `${probability}%`}\n </Row>\n <Row label=\"Forecast category\">\n {CRM_FORECAST_CATEGORY_LABELS[dealForecastCategory(deal, stage)]}\n </Row>\n <Row label=\"Expected close\">\n {typeof deal.expectedCloseAtMs === 'number'\n ? new Date(deal.expectedCloseAtMs).toLocaleDateString()\n : 'Not set'}\n </Row>\n <Row label=\"Type\">{deal.type || 'Not set'}</Row>\n <Row label=\"Lead source\">{deal.leadSource || 'Not set'}</Row>\n <Row label=\"Next step\">{deal.nextStep || 'Not set'}</Row>\n <Row label=\"Owner\">{ownerLabel || 'Nobody yet'}</Row>\n <Row label=\"Contact\">\n {deal.contactId ? (\n <AppLink href={routes.contact(deal.contactId)}>\n {deal.contactName || 'Open contact'}\n </AppLink>\n ) : (\n 'None'\n )}\n </Row>\n <Row label=\"Company\">\n {deal.companyId ? (\n <AppLink href={routes.company(deal.companyId)}>\n {deal.companyName || 'Open company'}\n </AppLink>\n ) : (\n 'None'\n )}\n </Row>\n <Row label=\"Campaign\">{campaignName || 'None'}</Row>\n <Row label=\"Pipeline\">{pipeline?.name ?? deal.pipelineId}</Row>\n <Row label=\"Created\">\n {createdMs ? new Date(createdMs).toLocaleDateString() : '—'}\n </Row>\n {deal.notes ? (\n <Row label=\"Notes\">\n <Typography variant=\"body2\" sx={{ whiteSpace: 'pre-wrap' }}>\n {deal.notes}\n </Typography>\n </Row>\n ) : null}\n {/*\n The org's own deal fields (AGL-2661), after the built-in ones under\n \"More fields\", as a record's Details keep them; none defined, none\n drawn.\n */}\n {customFields.length ? (\n <Typography variant=\"subtitle2\" sx={{ pt: 1 }}>\n {'More fields'}\n </Typography>\n ) : null}\n {customFields.map((definition) => {\n const text = formatContactCustomValue(definition, deal.custom?.[definition.key])\n return (\n <Row key={definition.key} label={definition.label || definition.key}>\n {text && definition.type === 'url' ? (\n <Link href={text} target=\"_blank\" rel=\"noreferrer noopener\">\n {text}\n </Link>\n ) : (\n text || 'Not set'\n )}\n </Row>\n )\n })}\n </Stack>\n </CardDisplay>\n )\n}\nDealPropertiesCard.displayName = 'DealPropertiesCard'\n\nexport default DealPropertiesCard\n"],"names":["CRM_FORECAST_CATEGORY_LABELS","dealForecastCategory","dealProbability","dealStageById","pluginDocsHelp","readDealProbability","weightedDealAmountCents","AppLink","CardDisplay","Link","Stack","Typography","formatContactCustomValue","formatMoney","timestampMs","Row","props","direction","spacing","sx","alignItems","variant","color","width","flexShrink","label","component","flex","minWidth","children","DealPropertiesCard","deal","pipeline","ownerLabel","routes","customFields","campaignName","stage","stageId","weighted","probability","overridden","createdMs","createdAt","header","help","anchor","contentGutterX","contentGutterY","amountCents","currency","status","expectedCloseAtMs","Date","toLocaleDateString","type","leadSource","nextStep","contactId","href","contact","contactName","companyId","company","companyName","name","pipelineId","notes","whiteSpace","length","pt","map","definition","text","custom","key","target","rel","displayName"],"mappings":"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"}
|
|
@@ -16,6 +16,10 @@ export interface DealStageCardProps {
|
|
|
16
16
|
* verdict — Won or Lost, when, and why — and a way back: reopening is a
|
|
17
17
|
* move into an open stage, which the route emits as `dealStageChanged` like
|
|
18
18
|
* any other move.
|
|
19
|
+
*
|
|
20
|
+
* The line under the stepper reads the deal's own probability and forecast
|
|
21
|
+
* category (AGL-3516) — a move gives it the new stage's, which the route
|
|
22
|
+
* stamps, and the stepper says so on hover.
|
|
19
23
|
*/
|
|
20
24
|
export declare function DealStageCard(props: DealStageCardProps): import("react").JSX.Element;
|
|
21
25
|
export declare namespace DealStageCard {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/ 'use client';
|
|
17
17
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
18
|
-
import { pluginDocsHelp } from "@aglyn/aglyn";
|
|
18
|
+
import { CRM_FORECAST_CATEGORY_LABELS, dealForecastCategory, dealProbability, pluginDocsHelp } from "@aglyn/aglyn";
|
|
19
19
|
import { mdiThumbDownOutline, mdiTrophyOutline } from "@aglyn/shared-data-mdi";
|
|
20
20
|
import { CardDisplay, MdiIcon } from "@aglyn/shared-ui-jsx";
|
|
21
21
|
import { useSnackbar } from "@aglyn/shared-ui-snackstack";
|
|
@@ -33,6 +33,10 @@ import { LostReasonDialog } from "./lost-reason-dialog.js";
|
|
|
33
33
|
* verdict — Won or Lost, when, and why — and a way back: reopening is a
|
|
34
34
|
* move into an open stage, which the route emits as `dealStageChanged` like
|
|
35
35
|
* any other move.
|
|
36
|
+
*
|
|
37
|
+
* The line under the stepper reads the deal's own probability and forecast
|
|
38
|
+
* category (AGL-3516) — a move gives it the new stage's, which the route
|
|
39
|
+
* stamps, and the stepper says so on hover.
|
|
36
40
|
*/ export function DealStageCard(props) {
|
|
37
41
|
const { deal, pipeline, api, nowMs } = props;
|
|
38
42
|
const { enqueueSnackbar } = useSnackbar();
|
|
@@ -63,6 +67,9 @@ import { LostReasonDialog } from "./lost-reason-dialog.js";
|
|
|
63
67
|
]);
|
|
64
68
|
const closed = deal.status !== 'open';
|
|
65
69
|
const days = daysInStage(deal, nowMs);
|
|
70
|
+
const current = activeIndex === -1 ? null : stages[activeIndex];
|
|
71
|
+
const probability = dealProbability(deal, current);
|
|
72
|
+
const forecast = CRM_FORECAST_CATEGORY_LABELS[dealForecastCategory(deal, current)];
|
|
66
73
|
return /*#__PURE__*/ _jsxs(CardDisplay, {
|
|
67
74
|
header: 'Stage',
|
|
68
75
|
help: pluginDocsHelp('deals', {
|
|
@@ -169,6 +176,7 @@ import { LostReasonDialog } from "./lost-reason-dialog.js";
|
|
|
169
176
|
children: stages.map((stage, index)=>/*#__PURE__*/ _jsx(Step, {
|
|
170
177
|
completed: index < activeIndex,
|
|
171
178
|
children: /*#__PURE__*/ _jsx(StepButton, {
|
|
179
|
+
title: `Move here: ${stage.probability}% likely to close`,
|
|
172
180
|
disabled: busy || stage.id === deal.stageId,
|
|
173
181
|
onClick: ()=>void run(()=>api.moveToStage(deal, stage.id), `Moved to ${stage.name}`),
|
|
174
182
|
children: stage.name
|
|
@@ -181,7 +189,7 @@ import { LostReasonDialog } from "./lost-reason-dialog.js";
|
|
|
181
189
|
sx: {
|
|
182
190
|
textAlign: 'center'
|
|
183
191
|
},
|
|
184
|
-
children:
|
|
192
|
+
children: !current ? 'This deal sits in a stage the pipeline no longer has. Pick one above.' : `${days} ${days === 1 ? 'day' : 'days'} in ${current.name} · ` + `${probability != null ? probability : current.probability}% likely to close · ${forecast}`
|
|
185
193
|
})
|
|
186
194
|
]
|
|
187
195
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/deal-stage-card.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 { pluginDocsHelp } from '@aglyn/aglyn'\nimport { mdiThumbDownOutline, mdiTrophyOutline } from '@aglyn/shared-data-mdi'\nimport { CardDisplay, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Button,\n Chip,\n MenuItem,\n Stack,\n Step,\n StepButton,\n Stepper,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useCallback, useState } from 'react'\nimport { type DealStageApi } from '../hooks/use-deal-stage-api'\nimport {\n daysInStage,\n DEAL_STATUS_LABELS,\n type DealDoc,\n openStages,\n type PipelineDoc,\n} from '../model/deal-board-model'\nimport { LostReasonDialog } from './lost-reason-dialog'\n\nexport interface DealStageCardProps {\n deal: DealDoc\n pipeline: PipelineDoc | null\n api: DealStageApi\n nowMs: number\n}\n\n/**\n * Where the deal is, and the controls that move it (AGL-2598).\n *\n * A stepper across the open stages — click one to move there — and the two\n * closing buttons. Every one of them calls the stage route, never Firestore:\n * the move is what an automation listens for, and the route is the only\n * writer that emits it. On a closed deal the stepper gives way to the\n * verdict — Won or Lost, when, and why — and a way back: reopening is a\n * move into an open stage, which the route emits as `dealStageChanged` like\n * any other move.\n */\nexport function DealStageCard(props: DealStageCardProps) {\n const { deal, pipeline, api, nowMs } = props\n const { enqueueSnackbar } = useSnackbar()\n const [busy, setBusy] = useState(false)\n const [losing, setLosing] = useState(false)\n const stages = openStages(pipeline)\n const activeIndex = stages.findIndex((stage) => stage.id === deal.stageId)\n\n const run = useCallback(\n async (request: () => Promise<unknown>, done: string) => {\n setBusy(true)\n try {\n await request()\n enqueueSnackbar(done, { variant: 'success', persist: false })\n return true\n } catch (error) {\n enqueueSnackbar(\n error instanceof Error ? error.message : 'The deal could not be moved.',\n { variant: 'warning', allowDuplicate: true },\n )\n return false\n } finally {\n setBusy(false)\n }\n },\n [enqueueSnackbar],\n )\n\n const closed = deal.status !== 'open'\n const days = daysInStage(deal, nowMs)\n\n return (\n <CardDisplay\n header={'Stage'}\n help={pluginDocsHelp('deals', { anchor: '#moving-winning-and-losing' })}\n HeaderProps={{\n action: !closed ? (\n <Stack direction=\"row\" spacing={1}>\n <Button\n size=\"small\"\n color=\"success\"\n variant=\"outlined\"\n disabled={busy || !pipeline}\n startIcon={<MdiIcon path={mdiTrophyOutline.path} size={0.8} />}\n onClick={() => void run(() => api.markWon(deal), 'Deal won')}\n >\n {'Won'}\n </Button>\n <Button\n size=\"small\"\n color=\"error\"\n variant=\"outlined\"\n disabled={busy || !pipeline}\n startIcon={<MdiIcon path={mdiThumbDownOutline.path} size={0.8} />}\n onClick={() => setLosing(true)}\n >\n {'Lost'}\n </Button>\n </Stack>\n ) : null,\n }}\n contentGutterX\n contentGutterY\n >\n {!pipeline ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {\"This deal's pipeline no longer exists, so it has no stages to move through.\"}\n </Typography>\n ) : closed ? (\n <Stack spacing={1.5}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Chip\n label={DEAL_STATUS_LABELS[deal.status]}\n color={deal.status === 'won' ? 'success' : 'default'}\n />\n <Typography variant=\"body2\" color=\"text.secondary\">\n {typeof deal.closedAtMs === 'number'\n ? `Closed ${new Date(deal.closedAtMs).toLocaleDateString()}`\n : 'Closed'}\n </Typography>\n </Stack>\n {deal.status === 'lost' && deal.lostReason ? (\n <Typography variant=\"body2\">{deal.lostReason}</Typography>\n ) : null}\n <TextField\n select\n size=\"small\"\n label=\"Reopen into\"\n value=\"\"\n disabled={busy}\n onChange={(event) => {\n const stageId = event.target.value\n if (!stageId) return\n void run(() => api.moveToStage(deal, stageId), 'Deal reopened')\n }}\n helperText=\"Reopening puts the deal back in an open stage and tells the automations.\"\n sx={{ maxWidth: 320 }}\n >\n <MenuItem value=\"\">{'Pick a stage'}</MenuItem>\n {stages.map((stage) => (\n <MenuItem key={stage.id} value={stage.id}>\n {stage.name}\n </MenuItem>\n ))}\n </TextField>\n </Stack>\n ) : (\n <Stack spacing={1.5}>\n <Stepper nonLinear activeStep={activeIndex} alternativeLabel>\n {stages.map((stage, index) => (\n <Step key={stage.id} completed={index < activeIndex}>\n <StepButton\n disabled={busy || stage.id === deal.stageId}\n onClick={() =>\n void run(\n () => api.moveToStage(deal, stage.id),\n `Moved to ${stage.name}`,\n )\n }\n >\n {stage.name}\n </StepButton>\n </Step>\n ))}\n </Stepper>\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ textAlign: 'center' }}>\n {activeIndex === -1\n ? 'This deal sits in a stage the pipeline no longer has. Pick one above.'\n : `${days} ${days === 1 ? 'day' : 'days'} in ${stages[activeIndex].name} · ` +\n `${stages[activeIndex].probability}% likely to close`}\n </Typography>\n </Stack>\n )}\n <LostReasonDialog\n open={losing}\n dealTitle={deal.title}\n busy={busy}\n onClose={() => setLosing(false)}\n onConfirm={(reason) =>\n void run(() => api.markLost(deal, reason), 'Deal marked lost').then(\n (ok) => ok && setLosing(false),\n )\n }\n />\n </CardDisplay>\n )\n}\nDealStageCard.displayName = 'DealStageCard'\n\nexport default DealStageCard\n"],"names":["pluginDocsHelp","mdiThumbDownOutline","mdiTrophyOutline","CardDisplay","MdiIcon","useSnackbar","Button","Chip","MenuItem","Stack","Step","StepButton","Stepper","TextField","Typography","useCallback","useState","daysInStage","DEAL_STATUS_LABELS","openStages","LostReasonDialog","DealStageCard","props","deal","pipeline","api","nowMs","enqueueSnackbar","busy","setBusy","losing","setLosing","stages","activeIndex","findIndex","stage","id","stageId","run","request","done","variant","persist","error","Error","message","allowDuplicate","closed","status","days","header","help","anchor","HeaderProps","action","direction","spacing","size","color","disabled","startIcon","path","onClick","markWon","contentGutterX","contentGutterY","sx","alignItems","label","closedAtMs","Date","toLocaleDateString","lostReason","select","value","onChange","event","target","moveToStage","helperText","maxWidth","map","name","nonLinear","activeStep","alternativeLabel","index","completed","textAlign","probability","open","dealTitle","title","onClose","onConfirm","reason","markLost","then","ok","displayName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/deal-stage-card.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 {\n CRM_FORECAST_CATEGORY_LABELS,\n dealForecastCategory,\n dealProbability,\n pluginDocsHelp,\n} from '@aglyn/aglyn'\nimport { mdiThumbDownOutline, mdiTrophyOutline } from '@aglyn/shared-data-mdi'\nimport { CardDisplay, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Button,\n Chip,\n MenuItem,\n Stack,\n Step,\n StepButton,\n Stepper,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useCallback, useState } from 'react'\nimport { type DealStageApi } from '../hooks/use-deal-stage-api'\nimport {\n daysInStage,\n DEAL_STATUS_LABELS,\n type DealDoc,\n openStages,\n type PipelineDoc,\n} from '../model/deal-board-model'\nimport { LostReasonDialog } from './lost-reason-dialog'\n\nexport interface DealStageCardProps {\n deal: DealDoc\n pipeline: PipelineDoc | null\n api: DealStageApi\n nowMs: number\n}\n\n/**\n * Where the deal is, and the controls that move it (AGL-2598).\n *\n * A stepper across the open stages — click one to move there — and the two\n * closing buttons. Every one of them calls the stage route, never Firestore:\n * the move is what an automation listens for, and the route is the only\n * writer that emits it. On a closed deal the stepper gives way to the\n * verdict — Won or Lost, when, and why — and a way back: reopening is a\n * move into an open stage, which the route emits as `dealStageChanged` like\n * any other move.\n *\n * The line under the stepper reads the deal's own probability and forecast\n * category (AGL-3516) — a move gives it the new stage's, which the route\n * stamps, and the stepper says so on hover.\n */\nexport function DealStageCard(props: DealStageCardProps) {\n const { deal, pipeline, api, nowMs } = props\n const { enqueueSnackbar } = useSnackbar()\n const [busy, setBusy] = useState(false)\n const [losing, setLosing] = useState(false)\n const stages = openStages(pipeline)\n const activeIndex = stages.findIndex((stage) => stage.id === deal.stageId)\n\n const run = useCallback(\n async (request: () => Promise<unknown>, done: string) => {\n setBusy(true)\n try {\n await request()\n enqueueSnackbar(done, { variant: 'success', persist: false })\n return true\n } catch (error) {\n enqueueSnackbar(\n error instanceof Error ? error.message : 'The deal could not be moved.',\n { variant: 'warning', allowDuplicate: true },\n )\n return false\n } finally {\n setBusy(false)\n }\n },\n [enqueueSnackbar],\n )\n\n const closed = deal.status !== 'open'\n const days = daysInStage(deal, nowMs)\n const current = activeIndex === -1 ? null : stages[activeIndex]\n const probability = dealProbability(deal, current)\n const forecast = CRM_FORECAST_CATEGORY_LABELS[dealForecastCategory(deal, current)]\n\n return (\n <CardDisplay\n header={'Stage'}\n help={pluginDocsHelp('deals', { anchor: '#moving-winning-and-losing' })}\n HeaderProps={{\n action: !closed ? (\n <Stack direction=\"row\" spacing={1}>\n <Button\n size=\"small\"\n color=\"success\"\n variant=\"outlined\"\n disabled={busy || !pipeline}\n startIcon={<MdiIcon path={mdiTrophyOutline.path} size={0.8} />}\n onClick={() => void run(() => api.markWon(deal), 'Deal won')}\n >\n {'Won'}\n </Button>\n <Button\n size=\"small\"\n color=\"error\"\n variant=\"outlined\"\n disabled={busy || !pipeline}\n startIcon={<MdiIcon path={mdiThumbDownOutline.path} size={0.8} />}\n onClick={() => setLosing(true)}\n >\n {'Lost'}\n </Button>\n </Stack>\n ) : null,\n }}\n contentGutterX\n contentGutterY\n >\n {!pipeline ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {\"This deal's pipeline no longer exists, so it has no stages to move through.\"}\n </Typography>\n ) : closed ? (\n <Stack spacing={1.5}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Chip\n label={DEAL_STATUS_LABELS[deal.status]}\n color={deal.status === 'won' ? 'success' : 'default'}\n />\n <Typography variant=\"body2\" color=\"text.secondary\">\n {typeof deal.closedAtMs === 'number'\n ? `Closed ${new Date(deal.closedAtMs).toLocaleDateString()}`\n : 'Closed'}\n </Typography>\n </Stack>\n {deal.status === 'lost' && deal.lostReason ? (\n <Typography variant=\"body2\">{deal.lostReason}</Typography>\n ) : null}\n <TextField\n select\n size=\"small\"\n label=\"Reopen into\"\n value=\"\"\n disabled={busy}\n onChange={(event) => {\n const stageId = event.target.value\n if (!stageId) return\n void run(() => api.moveToStage(deal, stageId), 'Deal reopened')\n }}\n helperText=\"Reopening puts the deal back in an open stage and tells the automations.\"\n sx={{ maxWidth: 320 }}\n >\n <MenuItem value=\"\">{'Pick a stage'}</MenuItem>\n {stages.map((stage) => (\n <MenuItem key={stage.id} value={stage.id}>\n {stage.name}\n </MenuItem>\n ))}\n </TextField>\n </Stack>\n ) : (\n <Stack spacing={1.5}>\n <Stepper nonLinear activeStep={activeIndex} alternativeLabel>\n {stages.map((stage, index) => (\n <Step key={stage.id} completed={index < activeIndex}>\n <StepButton\n title={`Move here: ${stage.probability}% likely to close`}\n disabled={busy || stage.id === deal.stageId}\n onClick={() =>\n void run(\n () => api.moveToStage(deal, stage.id),\n `Moved to ${stage.name}`,\n )\n }\n >\n {stage.name}\n </StepButton>\n </Step>\n ))}\n </Stepper>\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ textAlign: 'center' }}>\n {!current\n ? 'This deal sits in a stage the pipeline no longer has. Pick one above.'\n : `${days} ${days === 1 ? 'day' : 'days'} in ${current.name} · ` +\n `${probability ?? current.probability}% likely to close · ${forecast}`}\n </Typography>\n </Stack>\n )}\n <LostReasonDialog\n open={losing}\n dealTitle={deal.title}\n busy={busy}\n onClose={() => setLosing(false)}\n onConfirm={(reason) =>\n void run(() => api.markLost(deal, reason), 'Deal marked lost').then(\n (ok) => ok && setLosing(false),\n )\n }\n />\n </CardDisplay>\n )\n}\nDealStageCard.displayName = 'DealStageCard'\n\nexport default DealStageCard\n"],"names":["CRM_FORECAST_CATEGORY_LABELS","dealForecastCategory","dealProbability","pluginDocsHelp","mdiThumbDownOutline","mdiTrophyOutline","CardDisplay","MdiIcon","useSnackbar","Button","Chip","MenuItem","Stack","Step","StepButton","Stepper","TextField","Typography","useCallback","useState","daysInStage","DEAL_STATUS_LABELS","openStages","LostReasonDialog","DealStageCard","props","deal","pipeline","api","nowMs","enqueueSnackbar","busy","setBusy","losing","setLosing","stages","activeIndex","findIndex","stage","id","stageId","run","request","done","variant","persist","error","Error","message","allowDuplicate","closed","status","days","current","probability","forecast","header","help","anchor","HeaderProps","action","direction","spacing","size","color","disabled","startIcon","path","onClick","markWon","contentGutterX","contentGutterY","sx","alignItems","label","closedAtMs","Date","toLocaleDateString","lostReason","select","value","onChange","event","target","moveToStage","helperText","maxWidth","map","name","nonLinear","activeStep","alternativeLabel","index","completed","title","textAlign","open","dealTitle","onClose","onConfirm","reason","markLost","then","ok","displayName"],"mappings":"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"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { DealStageApi } from '../hooks/use-deal-stage-api';
|
|
2
2
|
import type { OrgMemberDirectory } from '../hooks/use-org-member-directory';
|
|
3
3
|
import { type DealDoc, type PipelineDoc } from '../model/deal-board-model';
|
|
4
|
-
import { type DealCsvOptions } from '../model/deals-csv';
|
|
5
4
|
export interface DealsBulkBarProps {
|
|
6
5
|
/**
|
|
7
6
|
* The site whose activity feed the bar's acts are logged in, or `null`
|
|
@@ -21,8 +20,6 @@ export interface DealsBulkBarProps {
|
|
|
21
20
|
roster: OrgMemberDirectory;
|
|
22
21
|
/** The one door a stage change goes through. */
|
|
23
22
|
api: DealStageApi;
|
|
24
|
-
/** How the export names the pipeline, the stage and the owner — the table's own. */
|
|
25
|
-
csv?: DealCsvOptions;
|
|
26
23
|
}
|
|
27
24
|
export declare function DealsBulkBar(props: DealsBulkBarProps): import("react").JSX.Element;
|
|
28
25
|
export declare namespace DealsBulkBar {
|
|
@@ -41,12 +41,11 @@ import { Button, MenuItem, TextField, Typography } from "@mui/material";
|
|
|
41
41
|
import { deleteField, doc } from "firebase/firestore";
|
|
42
42
|
import { useCallback, useMemo, useState } from "react";
|
|
43
43
|
import { useCrmBulkApply } from "../hooks/use-crm-bulk-apply.js";
|
|
44
|
-
import { downloadTextFile } from "../model/contacts-csv.js";
|
|
45
44
|
import { crmBulkWriters, runCrmBulkCalls, runCrmBulkWrites } from "../model/crm-bulk-writes.js";
|
|
46
45
|
import { closingStage, openStages } from "../model/deal-board-model.js";
|
|
47
|
-
import { dealsCsv } from "../model/deals-csv.js";
|
|
48
46
|
import { CrmBulkBarFrame, CrmBulkValueDialog, countNoun } from "./crm-bulk-bar-frame.js";
|
|
49
|
-
import
|
|
47
|
+
import { CrmExportButton } from "./crm-transfer-buttons.js";
|
|
48
|
+
import { CRM_DEALS_RESOURCE } from "../transfer/fields.js";
|
|
50
49
|
import { LostReasonDialog } from "./lost-reason-dialog.js";
|
|
51
50
|
import { useCrmSharingFollowUp } from "../hooks/use-crm-sharing.js";
|
|
52
51
|
const NOUN = {
|
|
@@ -64,9 +63,9 @@ export function DealsBulkBar(props) {
|
|
|
64
63
|
}
|
|
65
64
|
DealsBulkBar.displayName = 'DealsBulkBar';
|
|
66
65
|
function DealsBulkBarBody(props) {
|
|
67
|
-
var _ref, _roster_error, _ref1
|
|
66
|
+
var _ref, _roster_error, _ref1;
|
|
68
67
|
var _selectedRows_;
|
|
69
|
-
const { hostId, scope, rows, selected, onSelectedChange, pipelineById, roster, api
|
|
68
|
+
const { hostId, scope, rows, selected, onSelectedChange, pipelineById, roster, api } = props;
|
|
70
69
|
const firestore = useFirestore();
|
|
71
70
|
const { confirm } = useConfirmationContext();
|
|
72
71
|
const followUpSharing = useCrmSharingFollowUp(hostId != null ? hostId : null, (_ref = scope == null ? void 0 : scope[1]) != null ? _ref : null);
|
|
@@ -189,12 +188,6 @@ function DealsBulkBarBody(props) {
|
|
|
189
188
|
api,
|
|
190
189
|
runCalls
|
|
191
190
|
]);
|
|
192
|
-
const handleExport = useCallback(()=>{
|
|
193
|
-
downloadTextFile('deals-selected.csv', 'text/csv', dealsCsv(selectedRows, csv));
|
|
194
|
-
}, [
|
|
195
|
-
selectedRows,
|
|
196
|
-
csv
|
|
197
|
-
]);
|
|
198
191
|
const handleDelete = useCallback(async ()=>{
|
|
199
192
|
if (!scope || !selectedRows.length) return;
|
|
200
193
|
const count = selectedRows.length;
|
|
@@ -320,16 +313,10 @@ function DealsBulkBarBody(props) {
|
|
|
320
313
|
onClick: ()=>setLosing(true),
|
|
321
314
|
children: 'Mark lost'
|
|
322
315
|
}),
|
|
323
|
-
/*#__PURE__*/ _jsx(
|
|
324
|
-
|
|
325
|
-
disabled: busy,
|
|
326
|
-
onClick: handleExport,
|
|
327
|
-
children: 'Export CSV'
|
|
328
|
-
}),
|
|
329
|
-
/*#__PURE__*/ _jsx(CrmExportAllButton, {
|
|
330
|
-
resource: "deals",
|
|
331
|
-
orgId: (_ref2 = scope == null ? void 0 : scope[1]) != null ? _ref2 : null,
|
|
316
|
+
/*#__PURE__*/ _jsx(CrmExportButton, {
|
|
317
|
+
resource: CRM_DEALS_RESOURCE,
|
|
332
318
|
hostId: hostId,
|
|
319
|
+
selection: selectedRows.map((row)=>row.$id),
|
|
333
320
|
disabled: busy
|
|
334
321
|
}),
|
|
335
322
|
/*#__PURE__*/ _jsx(Button, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/deals-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 deals table, for whatever rows are ticked (AGL-2621).\n *\n * Move them to a stage, mark them lost, hand them to an owner, take them\n * into a spreadsheet, or delete them. The owner and the delete are\n * document writes and go through the shared runner in batches; the stage\n * and the loss go through `crm/deal-stage`, ONE REQUEST PER DEAL, because\n * a stage change is what an automation listens for and only the route\n * emits the event — a batch that wrote `stageId` would move forty deals\n * and tell no workflow. Each refusal carries the route's own sentence,\n * named by the deal.\n *\n * ## Set stage needs one pipeline\n *\n * A stage belongs to a pipeline, and a selection that spans two has no\n * one list of stages to offer. The dialog says so rather than offering the\n * first pipeline's stages to a deal in the second, which the route would\n * refuse one row at a time.\n */\n\nimport { CRM_COLLECTIONS, dealStageById, crmMemberPickerLabel } from '@aglyn/aglyn'\nimport { useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport { useFirestore, useHostActivityLogger } from '@aglyn/tenant-feature-instance'\nimport { Button, MenuItem, TextField, Typography } from '@mui/material'\nimport { deleteField, doc } from 'firebase/firestore'\nimport { useCallback, useMemo, useState } from 'react'\nimport { useCrmBulkApply } from '../hooks/use-crm-bulk-apply'\nimport type { DealStageApi } from '../hooks/use-deal-stage-api'\nimport type { OrgMemberDirectory } from '../hooks/use-org-member-directory'\nimport { downloadTextFile } from '../model/contacts-csv'\nimport {\n type CrmBulkPlan,\n type CrmBulkWrite,\n crmBulkWriters,\n runCrmBulkCalls,\n runCrmBulkWrites,\n} from '../model/crm-bulk-writes'\nimport {\n type DealDoc,\n type PipelineDoc,\n closingStage,\n openStages,\n} from '../model/deal-board-model'\nimport { type DealCsvOptions, dealsCsv } from '../model/deals-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 { LostReasonDialog } from './lost-reason-dialog'\nimport { useCrmSharingFollowUp } from '../hooks/use-crm-sharing'\n\nexport interface DealsBulkBarProps {\n /**\n * The site whose activity feed the bar's acts are logged in, or `null`\n * at the organization level (AGL-2630), where the selection spans sites\n * and the org's feed takes one line for the action instead (AGL-2634);\n * a stage move runs as the org there and is logged per deal by the route.\n */\n hostId: string | null\n /** `['orgs', orgId]`, or `null` while the org is unresolved. */\n scope: readonly ['orgs', string] | null\n rows: readonly DealDoc[]\n selected: readonly string[]\n onSelectedChange: (ids: string[]) => void\n /** The pipeline a deal's `pipelineId` names, for the stage list. */\n pipelineById: (id: string | undefined) => PipelineDoc | null\n /** The section's roster — already read for the Owner column. */\n roster: OrgMemberDirectory\n /** The one door a stage change goes through. */\n api: DealStageApi\n /** How the export names the pipeline, the stage and the owner — the table's own. */\n csv?: DealCsvOptions\n}\n\nconst NOUN: CrmBulkNoun = { singular: 'deal', plural: 'deals' }\n\ntype PendingAction = 'stage' | 'owner'\n\nconst ACTION_TITLES: Record<PendingAction, string> = {\n stage: 'Set the stage',\n owner: 'Set the owner',\n}\n\n/** The label a report lists a deal under. */\nconst labelOf = (deal: DealDoc): string => deal.title || 'Untitled deal'\n\nexport function DealsBulkBar(props: DealsBulkBarProps) {\n if (!props.selected.length) return null\n return <DealsBulkBarBody {...props} />\n}\nDealsBulkBar.displayName = 'DealsBulkBar'\n\nfunction DealsBulkBarBody(props: DealsBulkBarProps) {\n const { hostId, scope, rows, selected, onSelectedChange, pipelineById, roster, api, csv } =\n props\n const firestore = useFirestore()\n const { confirm } = useConfirmationContext()\n const followUpSharing = useCrmSharingFollowUp(hostId ?? null, scope?.[1] ?? null)\n const logActivity = useHostActivityLogger(hostId ?? undefined)\n const { busy, report, apply, dismissReport } = useCrmBulkApply({ recordKind: 'deal' })\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 /*\n * The one pipeline the selection is in, or `null` when it spans more\n * than one — in which case Set stage has no list to offer.\n */\n const pipeline = useMemo(() => {\n const ids = new Set(selectedRows.map((deal) => String(deal.pipelineId ?? '')))\n return ids.size === 1 ? pipelineById([...ids][0]) : null\n }, [selectedRows, pipelineById])\n const stages = useMemo(() => {\n if (!pipeline) return []\n const won = closingStage(pipeline, 'won')\n return [...openStages(pipeline), ...(won ? [won] : [])]\n }, [pipeline])\n\n const [pending, setPending] = useState<PendingAction | null>(null)\n const [value, setValue] = useState('')\n const [losing, setLosing] = useState(false)\n\n const writers = useMemo(\n () =>\n crmBulkWriters(firestore, (id) =>\n doc(firestore, scope?.[0] ?? 'orgs', scope?.[1] ?? '', CRM_COLLECTIONS.deals, id),\n ),\n [firestore, scope],\n )\n\n const openAction = (action: PendingAction) => {\n setValue('')\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 'deals',\n plan.writes.filter((write) => write.kind === 'update').map((write) => write.id),\n )\n return outcome\n },\n done,\n }),\n [apply, writers, followUpSharing],\n )\n\n /** One route request per deal, in order, named by title. */\n const runCalls = useCallback(\n (call: (deal: DealDoc) => Promise<unknown>, done: (count: number) => string) =>\n apply({\n attempted: selectedRows.length,\n skipped: [],\n job: () => runCrmBulkCalls(selectedRows, labelOf, call),\n done,\n // The stage route writes the org's line per deal (AGL-2634).\n loggedByRoute: true,\n }),\n [apply, selectedRows],\n )\n\n const handleApply = useCallback(async () => {\n if (!pending || !scope) return\n const action = pending\n setPending(null)\n if (action === 'stage') {\n const stage = dealStageById(pipeline, value)\n if (!stage) return\n await runCalls(\n (deal) => api.moveToStage(deal, stage.id),\n (count) => `Moved ${countNoun(count, NOUN)} to ${stage.name}`,\n )\n return\n }\n const nowMs = Date.now()\n const writes: CrmBulkWrite[] = selectedRows.map((deal) => ({\n id: deal.$id,\n label: labelOf(deal),\n kind: 'update',\n data: {\n ownerUid: value ? value : deleteField(),\n updatedAt: new Date(nowMs),\n },\n }))\n await runPlan({ writes, skipped: [] }, (count) => `Owner set on ${countNoun(count, NOUN)}`)\n }, [pending, scope, value, pipeline, selectedRows, api, runCalls, runPlan])\n\n const handleLost = useCallback(\n async (reason: string) => {\n setLosing(false)\n await runCalls(\n (deal) => api.markLost(deal, reason),\n (count) => `Marked ${countNoun(count, NOUN)} lost`,\n )\n },\n [api, runCalls],\n )\n\n const handleExport = useCallback(() => {\n downloadTextFile('deals-selected.csv', 'text/csv', dealsCsv(selectedRows, csv))\n }, [selectedRows, csv])\n\n const handleDelete = useCallback(async () => {\n if (!scope || !selectedRows.length) return\n const count = selectedRows.length\n const confirmed = await confirm({\n title: count === 1 ? 'Delete this deal?' : `Delete ${count} deals?`,\n description:\n `${count === 1 ? 'It is' : 'They are'} removed from the pipeline along ` +\n 'with their notes. The contacts and the companies they name are not ' +\n 'touched.',\n confirmationText: count === 1 ? 'Delete deal' : 'Delete deals',\n confirmationButtonProps: { color: 'error' },\n })\n // `confirm` resolves with no value and REJECTS on cancel.\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n const writes: CrmBulkWrite[] = selectedRows.map((deal) => ({\n id: deal.$id,\n label: labelOf(deal),\n kind: 'delete',\n }))\n const outcome = await runPlan(\n { writes, skipped: [] },\n (done) => `Deleted ${countNoun(done, NOUN)}`,\n )\n // One line per deal that went, as its own page would write.\n const refused = new Set(outcome.refused.map((row) => row.label))\n for (const deal of selectedRows) {\n if (!refused.has(labelOf(deal))) {\n logActivity('Deleted deal', { type: 'deal', id: deal.$id, name: deal.title })\n }\n }\n onSelectedChange(\n selectedRows.filter((deal) => refused.has(labelOf(deal))).map((deal) => deal.$id),\n )\n }, [scope, selectedRows, confirm, runPlan, logActivity, onSelectedChange])\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 <>\n <CrmBulkValueDialog\n open={pending !== null}\n title={pending ? ACTION_TITLES[pending] : ''}\n count={selected.length}\n noun={NOUN}\n busy={busy}\n canApply={pending === 'owner' || Boolean(value)}\n onClose={() => setPending(null)}\n onApply={() => void handleApply()}\n >\n {pending === 'stage' ? (\n pipeline ? (\n <TextField\n select\n size=\"small\"\n label=\"Stage\"\n value={value}\n onChange={(event) => setValue(event.target.value)}\n helperText={`${pipeline.name} — each move fires its event, as a drag does`}\n >\n {stages.map((stage) => (\n <MenuItem key={stage.id} value={stage.id}>\n {stage.name}\n </MenuItem>\n ))}\n </TextField>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'The selected deals are in different pipelines, so there is ' +\n 'no one list of stages to move them to. Select deals from ' +\n 'one pipeline.'}\n </Typography>\n )\n ) : pending === 'owner' ? (\n <TextField\n select\n size=\"small\"\n label=\"Owner\"\n value={value}\n onChange={(event) => setValue(event.target.value)}\n disabled={roster.loading}\n error={Boolean(roster.error)}\n helperText={roster.error ?? (roster.loading ? 'Loading the team…' : undefined)}\n >\n <MenuItem value=\"\">{'Nobody — clear the owner'}</MenuItem>\n {roster.members.map((member) => (\n <MenuItem key={member.uid} value={member.uid}>\n {crmMemberPickerLabel(member)}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n </CrmBulkValueDialog>\n <LostReasonDialog\n open={losing}\n dealTitle={selectedRows[0]?.title ?? ''}\n count={selectedRows.length}\n busy={busy}\n onClose={() => setLosing(false)}\n onConfirm={(reason) => void handleLost(reason)}\n />\n </>\n }\n >\n <Button size=\"small\" disabled={busy || !scope} onClick={() => openAction('stage')}>\n {'Set stage'}\n </Button>\n <Button size=\"small\" disabled={busy || !scope} onClick={() => openAction('owner')}>\n {'Set owner'}\n </Button>\n <Button size=\"small\" disabled={busy || !scope} onClick={() => setLosing(true)}>\n {'Mark lost'}\n </Button>\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=\"deals\"\n orgId={scope?.[1] ?? null}\n hostId={hostId}\n disabled={busy}\n />\n <Button\n size=\"small\"\n color=\"error\"\n disabled={busy || !scope}\n onClick={() => void handleDelete()}\n >\n {'Delete'}\n </Button>\n </CrmBulkBarFrame>\n )\n}\nDealsBulkBarBody.displayName = 'DealsBulkBarBody'\n\nexport default DealsBulkBar\n"],"names":["CRM_COLLECTIONS","dealStageById","crmMemberPickerLabel","useConfirmationContext","useFirestore","useHostActivityLogger","Button","MenuItem","TextField","Typography","deleteField","doc","useCallback","useMemo","useState","useCrmBulkApply","downloadTextFile","crmBulkWriters","runCrmBulkCalls","runCrmBulkWrites","closingStage","openStages","dealsCsv","CrmBulkBarFrame","CrmBulkValueDialog","countNoun","CrmExportAllButton","LostReasonDialog","useCrmSharingFollowUp","NOUN","singular","plural","ACTION_TITLES","stage","owner","labelOf","deal","title","DealsBulkBar","props","selected","length","DealsBulkBarBody","displayName","roster","selectedRows","hostId","scope","rows","onSelectedChange","pipelineById","api","csv","firestore","confirm","followUpSharing","logActivity","undefined","busy","report","apply","dismissReport","recordKind","chosen","Set","filter","row","has","$id","pipeline","ids","map","String","pipelineId","size","stages","won","pending","setPending","value","setValue","losing","setLosing","writers","id","deals","openAction","action","runPlan","plan","done","attempted","writes","skipped","job","outcome","write","label","kind","runCalls","call","loggedByRoute","handleApply","moveToStage","count","name","nowMs","Date","now","data","ownerUid","updatedAt","handleLost","reason","markLost","handleExport","handleDelete","confirmed","description","confirmationText","confirmationButtonProps","color","then","catch","refused","type","noun","onClear","onDismissReport","extras","open","canApply","Boolean","onClose","onApply","select","onChange","event","target","helperText","variant","disabled","loading","error","members","member","uid","dealTitle","onConfirm","onClick","resource","orgId"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/deals-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 deals table, for whatever rows are ticked (AGL-2621).\n *\n * Move them to a stage, mark them lost, hand them to an owner, take them\n * into a spreadsheet, or delete them. The owner and the delete are\n * document writes and go through the shared runner in batches; the stage\n * and the loss go through `crm/deal-stage`, ONE REQUEST PER DEAL, because\n * a stage change is what an automation listens for and only the route\n * emits the event — a batch that wrote `stageId` would move forty deals\n * and tell no workflow. Each refusal carries the route's own sentence,\n * named by the deal.\n *\n * ## Set stage needs one pipeline\n *\n * A stage belongs to a pipeline, and a selection that spans two has no\n * one list of stages to offer. The dialog says so rather than offering the\n * first pipeline's stages to a deal in the second, which the route would\n * refuse one row at a time.\n */\n\nimport { CRM_COLLECTIONS, dealStageById, crmMemberPickerLabel } from '@aglyn/aglyn'\nimport { useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport { useFirestore, useHostActivityLogger } from '@aglyn/tenant-feature-instance'\nimport { Button, MenuItem, TextField, Typography } from '@mui/material'\nimport { deleteField, doc } from 'firebase/firestore'\nimport { useCallback, useMemo, useState } from 'react'\nimport { useCrmBulkApply } from '../hooks/use-crm-bulk-apply'\nimport type { DealStageApi } from '../hooks/use-deal-stage-api'\nimport type { OrgMemberDirectory } from '../hooks/use-org-member-directory'\nimport {\n type CrmBulkPlan,\n type CrmBulkWrite,\n crmBulkWriters,\n runCrmBulkCalls,\n runCrmBulkWrites,\n} from '../model/crm-bulk-writes'\nimport {\n type DealDoc,\n type PipelineDoc,\n closingStage,\n openStages,\n} from '../model/deal-board-model'\nimport {\n type CrmBulkNoun,\n CrmBulkBarFrame,\n CrmBulkValueDialog,\n countNoun,\n} from './crm-bulk-bar-frame'\nimport { CrmExportButton } from './crm-transfer-buttons'\nimport { CRM_DEALS_RESOURCE } from '../transfer/fields'\nimport { LostReasonDialog } from './lost-reason-dialog'\nimport { useCrmSharingFollowUp } from '../hooks/use-crm-sharing'\n\nexport interface DealsBulkBarProps {\n /**\n * The site whose activity feed the bar's acts are logged in, or `null`\n * at the organization level (AGL-2630), where the selection spans sites\n * and the org's feed takes one line for the action instead (AGL-2634);\n * a stage move runs as the org there and is logged per deal by the route.\n */\n hostId: string | null\n /** `['orgs', orgId]`, or `null` while the org is unresolved. */\n scope: readonly ['orgs', string] | null\n rows: readonly DealDoc[]\n selected: readonly string[]\n onSelectedChange: (ids: string[]) => void\n /** The pipeline a deal's `pipelineId` names, for the stage list. */\n pipelineById: (id: string | undefined) => PipelineDoc | null\n /** The section's roster — already read for the Owner column. */\n roster: OrgMemberDirectory\n /** The one door a stage change goes through. */\n api: DealStageApi\n}\n\nconst NOUN: CrmBulkNoun = { singular: 'deal', plural: 'deals' }\n\ntype PendingAction = 'stage' | 'owner'\n\nconst ACTION_TITLES: Record<PendingAction, string> = {\n stage: 'Set the stage',\n owner: 'Set the owner',\n}\n\n/** The label a report lists a deal under. */\nconst labelOf = (deal: DealDoc): string => deal.title || 'Untitled deal'\n\nexport function DealsBulkBar(props: DealsBulkBarProps) {\n if (!props.selected.length) return null\n return <DealsBulkBarBody {...props} />\n}\nDealsBulkBar.displayName = 'DealsBulkBar'\n\nfunction DealsBulkBarBody(props: DealsBulkBarProps) {\n const { hostId, scope, rows, selected, onSelectedChange, pipelineById, roster, api } =\n props\n const firestore = useFirestore()\n const { confirm } = useConfirmationContext()\n const followUpSharing = useCrmSharingFollowUp(hostId ?? null, scope?.[1] ?? null)\n const logActivity = useHostActivityLogger(hostId ?? undefined)\n const { busy, report, apply, dismissReport } = useCrmBulkApply({ recordKind: 'deal' })\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 /*\n * The one pipeline the selection is in, or `null` when it spans more\n * than one — in which case Set stage has no list to offer.\n */\n const pipeline = useMemo(() => {\n const ids = new Set(selectedRows.map((deal) => String(deal.pipelineId ?? '')))\n return ids.size === 1 ? pipelineById([...ids][0]) : null\n }, [selectedRows, pipelineById])\n const stages = useMemo(() => {\n if (!pipeline) return []\n const won = closingStage(pipeline, 'won')\n return [...openStages(pipeline), ...(won ? [won] : [])]\n }, [pipeline])\n\n const [pending, setPending] = useState<PendingAction | null>(null)\n const [value, setValue] = useState('')\n const [losing, setLosing] = useState(false)\n\n const writers = useMemo(\n () =>\n crmBulkWriters(firestore, (id) =>\n doc(firestore, scope?.[0] ?? 'orgs', scope?.[1] ?? '', CRM_COLLECTIONS.deals, id),\n ),\n [firestore, scope],\n )\n\n const openAction = (action: PendingAction) => {\n setValue('')\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 'deals',\n plan.writes.filter((write) => write.kind === 'update').map((write) => write.id),\n )\n return outcome\n },\n done,\n }),\n [apply, writers, followUpSharing],\n )\n\n /** One route request per deal, in order, named by title. */\n const runCalls = useCallback(\n (call: (deal: DealDoc) => Promise<unknown>, done: (count: number) => string) =>\n apply({\n attempted: selectedRows.length,\n skipped: [],\n job: () => runCrmBulkCalls(selectedRows, labelOf, call),\n done,\n // The stage route writes the org's line per deal (AGL-2634).\n loggedByRoute: true,\n }),\n [apply, selectedRows],\n )\n\n const handleApply = useCallback(async () => {\n if (!pending || !scope) return\n const action = pending\n setPending(null)\n if (action === 'stage') {\n const stage = dealStageById(pipeline, value)\n if (!stage) return\n await runCalls(\n (deal) => api.moveToStage(deal, stage.id),\n (count) => `Moved ${countNoun(count, NOUN)} to ${stage.name}`,\n )\n return\n }\n const nowMs = Date.now()\n const writes: CrmBulkWrite[] = selectedRows.map((deal) => ({\n id: deal.$id,\n label: labelOf(deal),\n kind: 'update',\n data: {\n ownerUid: value ? value : deleteField(),\n updatedAt: new Date(nowMs),\n },\n }))\n await runPlan({ writes, skipped: [] }, (count) => `Owner set on ${countNoun(count, NOUN)}`)\n }, [pending, scope, value, pipeline, selectedRows, api, runCalls, runPlan])\n\n const handleLost = useCallback(\n async (reason: string) => {\n setLosing(false)\n await runCalls(\n (deal) => api.markLost(deal, reason),\n (count) => `Marked ${countNoun(count, NOUN)} lost`,\n )\n },\n [api, runCalls],\n )\n\n const handleDelete = useCallback(async () => {\n if (!scope || !selectedRows.length) return\n const count = selectedRows.length\n const confirmed = await confirm({\n title: count === 1 ? 'Delete this deal?' : `Delete ${count} deals?`,\n description:\n `${count === 1 ? 'It is' : 'They are'} removed from the pipeline along ` +\n 'with their notes. The contacts and the companies they name are not ' +\n 'touched.',\n confirmationText: count === 1 ? 'Delete deal' : 'Delete deals',\n confirmationButtonProps: { color: 'error' },\n })\n // `confirm` resolves with no value and REJECTS on cancel.\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n const writes: CrmBulkWrite[] = selectedRows.map((deal) => ({\n id: deal.$id,\n label: labelOf(deal),\n kind: 'delete',\n }))\n const outcome = await runPlan(\n { writes, skipped: [] },\n (done) => `Deleted ${countNoun(done, NOUN)}`,\n )\n // One line per deal that went, as its own page would write.\n const refused = new Set(outcome.refused.map((row) => row.label))\n for (const deal of selectedRows) {\n if (!refused.has(labelOf(deal))) {\n logActivity('Deleted deal', { type: 'deal', id: deal.$id, name: deal.title })\n }\n }\n onSelectedChange(\n selectedRows.filter((deal) => refused.has(labelOf(deal))).map((deal) => deal.$id),\n )\n }, [scope, selectedRows, confirm, runPlan, logActivity, onSelectedChange])\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 <>\n <CrmBulkValueDialog\n open={pending !== null}\n title={pending ? ACTION_TITLES[pending] : ''}\n count={selected.length}\n noun={NOUN}\n busy={busy}\n canApply={pending === 'owner' || Boolean(value)}\n onClose={() => setPending(null)}\n onApply={() => void handleApply()}\n >\n {pending === 'stage' ? (\n pipeline ? (\n <TextField\n select\n size=\"small\"\n label=\"Stage\"\n value={value}\n onChange={(event) => setValue(event.target.value)}\n helperText={`${pipeline.name} — each move fires its event, as a drag does`}\n >\n {stages.map((stage) => (\n <MenuItem key={stage.id} value={stage.id}>\n {stage.name}\n </MenuItem>\n ))}\n </TextField>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'The selected deals are in different pipelines, so there is ' +\n 'no one list of stages to move them to. Select deals from ' +\n 'one pipeline.'}\n </Typography>\n )\n ) : pending === 'owner' ? (\n <TextField\n select\n size=\"small\"\n label=\"Owner\"\n value={value}\n onChange={(event) => setValue(event.target.value)}\n disabled={roster.loading}\n error={Boolean(roster.error)}\n helperText={roster.error ?? (roster.loading ? 'Loading the team…' : undefined)}\n >\n <MenuItem value=\"\">{'Nobody — clear the owner'}</MenuItem>\n {roster.members.map((member) => (\n <MenuItem key={member.uid} value={member.uid}>\n {crmMemberPickerLabel(member)}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n </CrmBulkValueDialog>\n <LostReasonDialog\n open={losing}\n dealTitle={selectedRows[0]?.title ?? ''}\n count={selectedRows.length}\n busy={busy}\n onClose={() => setLosing(false)}\n onConfirm={(reason) => void handleLost(reason)}\n />\n </>\n }\n >\n <Button size=\"small\" disabled={busy || !scope} onClick={() => openAction('stage')}>\n {'Set stage'}\n </Button>\n <Button size=\"small\" disabled={busy || !scope} onClick={() => openAction('owner')}>\n {'Set owner'}\n </Button>\n <Button size=\"small\" disabled={busy || !scope} onClick={() => setLosing(true)}>\n {'Mark lost'}\n </Button>\n {/* The selection through the export dialog: every field, chosen (AGL-3528). */}\n <CrmExportButton\n resource={CRM_DEALS_RESOURCE}\n hostId={hostId}\n selection={selectedRows.map((row) => row.$id)}\n disabled={busy}\n />\n <Button\n size=\"small\"\n color=\"error\"\n disabled={busy || !scope}\n onClick={() => void handleDelete()}\n >\n {'Delete'}\n </Button>\n </CrmBulkBarFrame>\n )\n}\nDealsBulkBarBody.displayName = 'DealsBulkBarBody'\n\nexport default DealsBulkBar\n"],"names":["CRM_COLLECTIONS","dealStageById","crmMemberPickerLabel","useConfirmationContext","useFirestore","useHostActivityLogger","Button","MenuItem","TextField","Typography","deleteField","doc","useCallback","useMemo","useState","useCrmBulkApply","crmBulkWriters","runCrmBulkCalls","runCrmBulkWrites","closingStage","openStages","CrmBulkBarFrame","CrmBulkValueDialog","countNoun","CrmExportButton","CRM_DEALS_RESOURCE","LostReasonDialog","useCrmSharingFollowUp","NOUN","singular","plural","ACTION_TITLES","stage","owner","labelOf","deal","title","DealsBulkBar","props","selected","length","DealsBulkBarBody","displayName","roster","selectedRows","hostId","scope","rows","onSelectedChange","pipelineById","api","firestore","confirm","followUpSharing","logActivity","undefined","busy","report","apply","dismissReport","recordKind","chosen","Set","filter","row","has","$id","pipeline","ids","map","String","pipelineId","size","stages","won","pending","setPending","value","setValue","losing","setLosing","writers","id","deals","openAction","action","runPlan","plan","done","attempted","writes","skipped","job","outcome","write","label","kind","runCalls","call","loggedByRoute","handleApply","moveToStage","count","name","nowMs","Date","now","data","ownerUid","updatedAt","handleLost","reason","markLost","handleDelete","confirmed","description","confirmationText","confirmationButtonProps","color","then","catch","refused","type","noun","onClear","onDismissReport","extras","open","canApply","Boolean","onClose","onApply","select","onChange","event","target","helperText","variant","disabled","loading","error","members","member","uid","dealTitle","onConfirm","onClick","resource","selection"],"mappings":"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"}
|
|
@@ -38,9 +38,9 @@ import { type ConsolePluginPageProps } from '@aglyn/aglyn';
|
|
|
38
38
|
*
|
|
39
39
|
* The table's rows are selectable, and a selection raises `DealsBulkBar`
|
|
40
40
|
* over it — whose stage moves go through the same `useDealStageApi` as a
|
|
41
|
-
* drag. Export
|
|
42
|
-
* `
|
|
43
|
-
*
|
|
41
|
+
* drag. Import and Export open the console's import wizard and export
|
|
42
|
+
* dialog on `crm.deals` (AGL-3528): the export over the selection, the
|
|
43
|
+
* list's own query, or every deal.
|
|
44
44
|
*/
|
|
45
45
|
export declare function DealsSection(props: ConsolePluginPageProps): import("react").JSX.Element;
|
|
46
46
|
export declare namespace DealsSection {
|