@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/log-activity-dialog.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport type { CrmActivityKind, CrmActivityRow } from '@aglyn/aglyn'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { useUser, useUserName } from '@aglyn/tenant-feature-instance'\nimport {\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n MenuItem,\n Stack,\n TextField,\n} from '@mui/material'\nimport {\n addDoc,\n collection,\n deleteField,\n doc,\n getCountFromServer,\n query,\n updateDoc,\n where,\n} from 'firebase/firestore'\nimport { useCallback, useEffect, useState } from 'react'\nimport { crmVisibleToClause } from '../hooks/use-crm-scope'\nimport type { ActivityRecordLink, ActivityScope } from './activity-queries'\nimport { CrmSitePicker } from './crm-site-picker'\n\n/** The longest body a single activity stores — a call summary, not a document. */\nconst BODY_MAX = 4000\nconst OUTCOME_MAX = 200\nconst DURATION_MAX_MINUTES = 24 * 60\n\n/**\n * Epoch millis as the value a `datetime-local` input holds, in the LOCAL\n * zone — the input has no zone of its own, so an ISO string with a `Z`\n * would land the call an offset away from when it happened.\n */\nfunction toLocalInput(ms: number): string {\n if (!Number.isFinite(ms)) return ''\n const date = new Date(ms)\n const pad = (value: number) => String(value).padStart(2, '0')\n return (\n `${date.getFullYear()}-${pad(date.getMonth() + 1)}-` +\n `${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`\n )\n}\n\n/** The inverse: a `datetime-local` value parsed as local time, or `null`. */\nfunction fromLocalInput(value: string): number | null {\n if (!value) return null\n const ms = new Date(value).getTime()\n return Number.isFinite(ms) ? ms : null\n}\n\ninterface Draft {\n kind: CrmActivityKind\n body: string\n /** The `datetime-local` string, not millis — what the input holds. */\n at: string\n outcome: string\n /** Free text until save, so a half-typed number does not snap to zero. */\n durationMinutes: string\n}\n\n/** The kind a new activity opens on when the caller names none. */\nconst DEFAULT_KIND: CrmActivityKind = 'call'\n\nconst freshDraft = (kind: CrmActivityKind = DEFAULT_KIND): Draft => ({\n kind,\n body: '',\n at: toLocalInput(Date.now()),\n outcome: '',\n durationMinutes: '',\n})\n\nconst draftFrom = (activity: CrmActivityRow): Draft => ({\n kind: Aglyn.isCrmActivityKind(activity.kind) ? activity.kind : 'other',\n body: activity.body ?? '',\n at: toLocalInput(activity.atMs),\n outcome: activity.outcome ?? '',\n durationMinutes:\n typeof activity.durationMinutes === 'number'\n ? String(activity.durationMinutes)\n : '',\n})\n\nexport interface LogActivityDialogProps {\n open: boolean\n onClose: () => void\n /** Where the activity is written, from `useActivityScope`. */\n scope: ActivityScope\n /**\n * The record the activity is filed against, FIXED by the caller. A\n * contact's page logs against that contact; the dialog offers no picker,\n * because an activity logged from a record page is about that record and\n * a picker would be a way to file it somewhere the reader is not looking.\n */\n link: ActivityRecordLink\n /**\n * When set, the dialog EDITS this activity rather than logging a new one.\n * The link, the author and the scope stay what they were; only what was\n * said, when, and how it went can change.\n */\n activity?: CrmActivityRow | null\n /**\n * What a NEW activity opens as (AGL-2661) — the click-to-call button\n * opens the dialog already a call, so logging one is the button and a\n * sentence rather than the button, the kind and a sentence. Ignored for\n * an edit, whose kind is the activity's own.\n */\n kind?: CrmActivityKind\n}\n\n/**\n * Log a call, an email, a meeting, a note or something else against a\n * record (AGL-2600) — or correct one already logged.\n *\n * ## What is written\n *\n * A new activity carries the whole `CrmScoped` stamp: `visibleTo` from\n * `crmScopeTokens`, so it lands in exactly the scope a contact captured on\n * this site would; `hostId` as provenance; `byUid` and `byName` for the\n * author, the name denormalized because a colleague reading the log cannot\n * resolve a uid (see `CrmActivity.byName`); `atMs` for when it HAPPENED,\n * which the reader picks and which defaults to now; and the timestamps.\n *\n * An edit rewrites only the fields the dialog shows. `visibleTo` is never\n * touched — the rules refuse a scoped member changing it, and widening a\n * record is an act for an org-wide member on the record itself — and neither\n * is the author or the link. A kind that stops taking an outcome takes the\n * outcome and the duration off the document rather than leaving a stale\n * \"left a voicemail\" on what is now a note.\n *\n * ## Who may write\n *\n * The rules admit any member who may write scoped org data, on create and\n * on update alike; they do not distinguish the author. The card that opens\n * this dialog for an edit checks the author itself (`useCanEditActivity`),\n * so what reaches here has already passed the console's verdict.\n */\nexport function LogActivityDialog(props: LogActivityDialogProps) {\n const { open, onClose, scope, link, activity, kind: presetKind } = props\n const { firestore, dataScope, hostId, mountedHostId, readTokens, writeTokens } =\n scope\n const { data: user } = useUser()\n const authorName = useUserName()\n const { enqueueSnackbar } = useSnackbar()\n const [draft, setDraft] = useState<Draft>(freshDraft)\n const [saving, setSaving] = useState(false)\n\n /*\n * Seeded on OPEN, not on mount: the dialog stays mounted between uses so\n * its state survives a close, and \"log another call\" must start from now\n * rather than from the last call's timestamp.\n */\n useEffect(() => {\n if (open) setDraft(activity ? draftFrom(activity) : freshDraft(presetKind))\n }, [open, activity, presetKind])\n\n const hasOutcome = Aglyn.activityKindHasOutcome(draft.kind)\n const atMs = fromLocalInput(draft.at)\n const body = draft.body.trim()\n const duration = draft.durationMinutes.trim()\n ? Number(draft.durationMinutes)\n : null\n const durationValid =\n duration === null ||\n (Number.isInteger(duration) &&\n duration >= 0 &&\n duration <= DURATION_MAX_MINUTES)\n const canSave =\n Boolean(dataScope) &&\n Boolean(user?.uid) &&\n // A NEW activity is stamped with a site's scope and provenance; at the\n // organization level that is the picked site, and there is nothing to\n // write until there is one (AGL-2630). An edit never touches either.\n (Boolean(activity) || Boolean(hostId)) &&\n body.length > 0 &&\n atMs !== null &&\n durationValid &&\n !saving\n\n const handleSave = useCallback(async () => {\n // Every guard the button reads, again — a callback can outlive the\n // render that disabled it.\n if (!dataScope || !user?.uid || !body || atMs === null || !durationValid) {\n return\n }\n if (!activity && !hostId) return\n setSaving(true)\n try {\n const said = {\n kind: draft.kind,\n body: body.slice(0, BODY_MAX),\n atMs,\n }\n const outcome = hasOutcome ? draft.outcome.trim().slice(0, OUTCOME_MAX) : ''\n const durationMinutes = hasOutcome && duration !== null ? duration : null\n if (activity) {\n await updateDoc(\n doc(\n firestore,\n dataScope[0],\n dataScope[1],\n Aglyn.CRM_COLLECTIONS.activities,\n activity.$id,\n ),\n {\n ...said,\n // Cleared rather than left: a call turned into a note must not\n // keep the call's outcome, and Firestore refuses `undefined`.\n outcome: outcome ? outcome : deleteField(),\n durationMinutes:\n durationMinutes !== null ? durationMinutes : deleteField(),\n updatedAt: new Date(),\n },\n )\n enqueueSnackbar('Activity updated', { variant: 'success', persist: false })\n } else {\n /*\n * THE PER-RECORD CEILING (AGL-2611): one aggregate on the record\n * this dialog is filing under, before the write — the same count\n * the automation step and the REST create take, on the same link.\n * Scoped to the tokens this viewer may list, as every activity\n * listener is, so the rules admit the aggregate for a scoped\n * member exactly as they admit the timeline.\n */\n const lead = Aglyn.crmActivityCeilingLink(link)\n if (lead) {\n const logged = (\n await getCountFromServer(\n query(\n collection(\n firestore,\n dataScope[0],\n dataScope[1],\n Aglyn.CRM_COLLECTIONS.activities,\n ),\n ...crmVisibleToClause(readTokens),\n where(lead.field, '==', lead.id),\n ),\n )\n ).data().count\n if (!Aglyn.crmActivityLogHasRoom(logged)) {\n enqueueSnackbar(Aglyn.CRM_ACTIVITY_LOG_FULL_MESSAGE, {\n variant: 'warning',\n persist: false,\n })\n return\n }\n }\n await addDoc(\n collection(\n firestore,\n dataScope[0],\n dataScope[1],\n Aglyn.CRM_COLLECTIONS.activities,\n ),\n {\n ...said,\n ...(outcome ? { outcome } : {}),\n ...(durationMinutes !== null ? { durationMinutes } : {}),\n // Only the link the caller fixed. A key with no value is\n // `undefined`, which Firestore refuses, so each is spread in\n // rather than written blank.\n ...(link.contactId ? { contactId: link.contactId } : {}),\n ...(link.companyId ? { companyId: link.companyId } : {}),\n ...(link.dealId ? { dealId: link.dealId } : {}),\n ...(link.leadId ? { leadId: link.leadId } : {}),\n visibleTo: writeTokens,\n hostId,\n byUid: user.uid,\n // Absent rather than blank while the profile is still resolving:\n // the row reads a stored name over anything else, and an empty\n // string would be a name.\n ...(authorName ? { byName: authorName } : {}),\n createdAt: new Date(),\n updatedAt: new Date(),\n },\n )\n enqueueSnackbar('Activity logged', { variant: 'success', persist: false })\n }\n onClose()\n } catch (error) {\n console.error(error)\n enqueueSnackbar('An error has occurred', {\n variant: 'error',\n allowDuplicate: true,\n })\n } finally {\n setSaving(false)\n }\n }, [\n dataScope,\n user,\n body,\n atMs,\n durationValid,\n draft.kind,\n draft.outcome,\n hasOutcome,\n duration,\n activity,\n firestore,\n enqueueSnackbar,\n link,\n readTokens,\n writeTokens,\n hostId,\n authorName,\n onClose,\n ])\n\n return (\n <Dialog open={open} onClose={onClose} maxWidth=\"sm\" fullWidth>\n <DialogTitle>{activity ? 'Edit activity' : 'Log activity'}</DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n {/* Only for a new activity at the organization level — see `canSave`. */}\n {activity ? null : (\n <CrmSitePicker\n hostId={mountedHostId}\n disabled={saving}\n helperText=\"The site this activity is logged from — it decides which of your sites may see it.\"\n />\n )}\n <Stack direction=\"row\" spacing={1} sx={{ mt: 1 }}>\n <TextField\n select\n size=\"small\"\n label=\"Kind\"\n value={draft.kind}\n onChange={(event) =>\n setDraft((prev) => ({\n ...prev,\n kind: Aglyn.isCrmActivityKind(event.target.value)\n ? event.target.value\n : prev.kind,\n }))\n }\n sx={{ minWidth: 140 }}\n >\n {Aglyn.CRM_ACTIVITY_KINDS.map((kind) => (\n <MenuItem key={kind} value={kind}>\n {Aglyn.CRM_ACTIVITY_KIND_LABELS[kind]}\n </MenuItem>\n ))}\n </TextField>\n <TextField\n size=\"small\"\n label=\"When\"\n type=\"datetime-local\"\n value={draft.at}\n onChange={(event) =>\n setDraft((prev) => ({ ...prev, at: event.target.value }))\n }\n error={draft.at.length > 0 && atMs === null}\n slotProps={{ inputLabel: { shrink: true } }}\n sx={{ flex: 1 }}\n />\n </Stack>\n <TextField\n size=\"small\"\n label=\"What happened\"\n value={draft.body}\n onChange={(event) =>\n setDraft((prev) => ({ ...prev, body: event.target.value }))\n }\n multiline\n minRows={3}\n autoFocus\n slotProps={{ htmlInput: { maxLength: BODY_MAX } }}\n />\n {hasOutcome ? (\n <Stack direction=\"row\" spacing={1}>\n <TextField\n size=\"small\"\n label=\"Outcome\"\n placeholder=\"Left a voicemail\"\n value={draft.outcome}\n onChange={(event) =>\n setDraft((prev) => ({ ...prev, outcome: event.target.value }))\n }\n slotProps={{ htmlInput: { maxLength: OUTCOME_MAX } }}\n sx={{ flex: 1 }}\n />\n <TextField\n size=\"small\"\n label=\"Minutes\"\n type=\"number\"\n value={draft.durationMinutes}\n onChange={(event) =>\n setDraft((prev) => ({\n ...prev,\n durationMinutes: event.target.value,\n }))\n }\n error={!durationValid}\n slotProps={{\n htmlInput: { min: 0, max: DURATION_MAX_MINUTES, step: 1 },\n }}\n sx={{ width: 120 }}\n />\n </Stack>\n ) : null}\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} disabled={saving}>\n {'Cancel'}\n </Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={!canSave}\n onClick={handleSave}\n >\n {activity ? 'Save' : 'Log'}\n </Button>\n </DialogActions>\n </Dialog>\n )\n}\nLogActivityDialog.displayName = 'LogActivityDialog'\n\nexport default LogActivityDialog\n"],"names":["Aglyn","useSnackbar","useUser","useUserName","Button","Dialog","DialogActions","DialogContent","DialogTitle","MenuItem","Stack","TextField","addDoc","collection","deleteField","doc","getCountFromServer","query","updateDoc","where","useCallback","useEffect","useState","crmVisibleToClause","CrmSitePicker","BODY_MAX","OUTCOME_MAX","DURATION_MAX_MINUTES","toLocalInput","ms","Number","isFinite","date","Date","pad","value","String","padStart","getFullYear","getMonth","getDate","getHours","getMinutes","fromLocalInput","getTime","DEFAULT_KIND","freshDraft","kind","body","at","now","outcome","durationMinutes","draftFrom","activity","isCrmActivityKind","atMs","LogActivityDialog","props","open","onClose","scope","link","presetKind","firestore","dataScope","hostId","mountedHostId","readTokens","writeTokens","data","user","authorName","enqueueSnackbar","draft","setDraft","saving","setSaving","hasOutcome","activityKindHasOutcome","trim","duration","durationValid","isInteger","canSave","Boolean","uid","length","handleSave","said","slice","CRM_COLLECTIONS","activities","$id","updatedAt","variant","persist","lead","crmActivityCeilingLink","logged","field","id","count","crmActivityLogHasRoom","CRM_ACTIVITY_LOG_FULL_MESSAGE","contactId","companyId","dealId","leadId","visibleTo","byUid","byName","createdAt","error","console","allowDuplicate","maxWidth","fullWidth","sx","display","flexDirection","gap","disabled","helperText","direction","spacing","mt","select","size","label","onChange","event","prev","target","minWidth","CRM_ACTIVITY_KINDS","map","CRM_ACTIVITY_KIND_LABELS","type","slotProps","inputLabel","shrink","flex","multiline","minRows","autoFocus","htmlInput","maxLength","placeholder","min","max","step","width","onClick","color","displayName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/log-activity-dialog.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport type { CrmActivityKind, CrmActivityRow } from '@aglyn/aglyn'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { useUser, useUserName } from '@aglyn/tenant-feature-instance'\nimport {\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n MenuItem,\n Stack,\n TextField,\n} from '@mui/material'\nimport {\n addDoc,\n collection,\n deleteField,\n doc,\n getCountFromServer,\n query,\n updateDoc,\n where,\n} from 'firebase/firestore'\nimport { useCallback, useEffect, useState } from 'react'\nimport { crmVisibleToClause } from '../hooks/use-crm-scope'\nimport type { ActivityRecordLink, ActivityScope } from './activity-queries'\nimport { CrmSitePicker } from './crm-site-picker'\n\n/** The longest body a single activity stores — a call summary, not a document. */\nconst BODY_MAX = 4000\nconst OUTCOME_MAX = 200\nconst DURATION_MAX_MINUTES = 24 * 60\n\n/**\n * Epoch millis as the value a `datetime-local` input holds, in the LOCAL\n * zone — the input has no zone of its own, so an ISO string with a `Z`\n * would land the call an offset away from when it happened.\n */\nfunction toLocalInput(ms: number): string {\n if (!Number.isFinite(ms)) return ''\n const date = new Date(ms)\n const pad = (value: number) => String(value).padStart(2, '0')\n return (\n `${date.getFullYear()}-${pad(date.getMonth() + 1)}-` +\n `${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`\n )\n}\n\n/** The inverse: a `datetime-local` value parsed as local time, or `null`. */\nfunction fromLocalInput(value: string): number | null {\n if (!value) return null\n const ms = new Date(value).getTime()\n return Number.isFinite(ms) ? ms : null\n}\n\ninterface Draft {\n kind: CrmActivityKind\n body: string\n /** The `datetime-local` string, not millis — what the input holds. */\n at: string\n outcome: string\n /** Free text until save, so a half-typed number does not snap to zero. */\n durationMinutes: string\n /**\n * Which way a call or an email went (AGL-3517) — Salesforce's Call Type\n * for a call — or `''` for not said. Kept across a kind change and\n * written only when the kind takes it.\n */\n direction: string\n}\n\n/** The kind a new activity opens on when the caller names none. */\nconst DEFAULT_KIND: CrmActivityKind = 'call'\n\nconst freshDraft = (kind: CrmActivityKind = DEFAULT_KIND): Draft => ({\n kind,\n body: '',\n at: toLocalInput(Date.now()),\n outcome: '',\n durationMinutes: '',\n // Most logged calls and emails are the team's own.\n direction: 'outbound',\n})\n\nconst draftFrom = (activity: CrmActivityRow): Draft => ({\n kind: Aglyn.isCrmActivityKind(activity.kind) ? activity.kind : 'other',\n body: activity.body ?? '',\n at: toLocalInput(activity.atMs),\n outcome: activity.outcome ?? '',\n durationMinutes:\n typeof activity.durationMinutes === 'number'\n ? String(activity.durationMinutes)\n : '',\n direction: Aglyn.crmActivityDirection(activity.kind, activity.direction) ?? '',\n})\n\nexport interface LogActivityDialogProps {\n open: boolean\n onClose: () => void\n /** Where the activity is written, from `useActivityScope`. */\n scope: ActivityScope\n /**\n * The record the activity is filed against, FIXED by the caller. A\n * contact's page logs against that contact; the dialog offers no picker,\n * because an activity logged from a record page is about that record and\n * a picker would be a way to file it somewhere the reader is not looking.\n */\n link: ActivityRecordLink\n /**\n * When set, the dialog EDITS this activity rather than logging a new one.\n * The link, the author and the scope stay what they were; only what was\n * said, when, and how it went can change.\n */\n activity?: CrmActivityRow | null\n /**\n * What a NEW activity opens as (AGL-2661) — the click-to-call button\n * opens the dialog already a call, so logging one is the button and a\n * sentence rather than the button, the kind and a sentence. Ignored for\n * an edit, whose kind is the activity's own.\n */\n kind?: CrmActivityKind\n}\n\n/**\n * Log a call, an email, a meeting, a note or something else against a\n * record (AGL-2600) — or correct one already logged.\n *\n * ## What is written\n *\n * A new activity carries the whole `CrmScoped` stamp: `visibleTo` from\n * `crmScopeTokens`, so it lands in exactly the scope a contact captured on\n * this site would; `hostId` as provenance; `byUid` and `byName` for the\n * author, the name denormalized because a colleague reading the log cannot\n * resolve a uid (see `CrmActivity.byName`); `atMs` for when it HAPPENED,\n * which the reader picks and which defaults to now; and the timestamps.\n *\n * An edit rewrites only the fields the dialog shows. `visibleTo` is never\n * touched — the rules refuse a scoped member changing it, and widening a\n * record is an act for an org-wide member on the record itself — and neither\n * is the author or the link. A kind that stops taking an outcome takes the\n * outcome and the duration off the document rather than leaving a stale\n * \"left a voicemail\" on what is now a note.\n *\n * ## Who may write\n *\n * The rules admit any member who may write scoped org data, on create and\n * on update alike; they do not distinguish the author. The card that opens\n * this dialog for an edit checks the author itself (`useCanEditActivity`),\n * so what reaches here has already passed the console's verdict.\n */\nexport function LogActivityDialog(props: LogActivityDialogProps) {\n const { open, onClose, scope, link, activity, kind: presetKind } = props\n const { firestore, dataScope, hostId, mountedHostId, readTokens, writeTokens } =\n scope\n const { data: user } = useUser()\n const authorName = useUserName()\n const { enqueueSnackbar } = useSnackbar()\n const [draft, setDraft] = useState<Draft>(freshDraft)\n const [saving, setSaving] = useState(false)\n\n /*\n * Seeded on OPEN, not on mount: the dialog stays mounted between uses so\n * its state survives a close, and \"log another call\" must start from now\n * rather than from the last call's timestamp.\n */\n useEffect(() => {\n if (open) setDraft(activity ? draftFrom(activity) : freshDraft(presetKind))\n }, [open, activity, presetKind])\n\n const hasOutcome = Aglyn.activityKindHasOutcome(draft.kind)\n const directions = Aglyn.CRM_ACTIVITY_DIRECTIONS[draft.kind] ?? []\n const direction = Aglyn.crmActivityDirection(draft.kind, draft.direction)\n const atMs = fromLocalInput(draft.at)\n const body = draft.body.trim()\n const duration = draft.durationMinutes.trim()\n ? Number(draft.durationMinutes)\n : null\n const durationValid =\n duration === null ||\n (Number.isInteger(duration) &&\n duration >= 0 &&\n duration <= DURATION_MAX_MINUTES)\n const canSave =\n Boolean(dataScope) &&\n Boolean(user?.uid) &&\n // A NEW activity is stamped with a site's scope and provenance; at the\n // organization level that is the picked site, and there is nothing to\n // write until there is one (AGL-2630). An edit never touches either.\n (Boolean(activity) || Boolean(hostId)) &&\n body.length > 0 &&\n atMs !== null &&\n durationValid &&\n !saving\n\n const handleSave = useCallback(async () => {\n // Every guard the button reads, again — a callback can outlive the\n // render that disabled it.\n if (!dataScope || !user?.uid || !body || atMs === null || !durationValid) {\n return\n }\n if (!activity && !hostId) return\n setSaving(true)\n try {\n const said = {\n kind: draft.kind,\n body: body.slice(0, BODY_MAX),\n atMs,\n }\n const outcome = hasOutcome ? draft.outcome.trim().slice(0, OUTCOME_MAX) : ''\n const durationMinutes = hasOutcome && duration !== null ? duration : null\n if (activity) {\n await updateDoc(\n doc(\n firestore,\n dataScope[0],\n dataScope[1],\n Aglyn.CRM_COLLECTIONS.activities,\n activity.$id,\n ),\n {\n ...said,\n // Cleared rather than left: a call turned into a note must not\n // keep the call's outcome, and Firestore refuses `undefined`.\n outcome: outcome ? outcome : deleteField(),\n direction: direction ?? deleteField(),\n durationMinutes:\n durationMinutes !== null ? durationMinutes : deleteField(),\n updatedAt: new Date(),\n },\n )\n enqueueSnackbar('Activity updated', { variant: 'success', persist: false })\n } else {\n /*\n * THE PER-RECORD CEILING (AGL-2611): one aggregate on the record\n * this dialog is filing under, before the write — the same count\n * the automation step and the REST create take, on the same link.\n * Scoped to the tokens this viewer may list, as every activity\n * listener is, so the rules admit the aggregate for a scoped\n * member exactly as they admit the timeline.\n */\n const lead = Aglyn.crmActivityCeilingLink(link)\n if (lead) {\n const logged = (\n await getCountFromServer(\n query(\n collection(\n firestore,\n dataScope[0],\n dataScope[1],\n Aglyn.CRM_COLLECTIONS.activities,\n ),\n ...crmVisibleToClause(readTokens),\n where(lead.field, '==', lead.id),\n ),\n )\n ).data().count\n if (!Aglyn.crmActivityLogHasRoom(logged)) {\n enqueueSnackbar(Aglyn.CRM_ACTIVITY_LOG_FULL_MESSAGE, {\n variant: 'warning',\n persist: false,\n })\n return\n }\n }\n await addDoc(\n collection(\n firestore,\n dataScope[0],\n dataScope[1],\n Aglyn.CRM_COLLECTIONS.activities,\n ),\n {\n ...said,\n ...(outcome ? { outcome } : {}),\n ...(durationMinutes !== null ? { durationMinutes } : {}),\n ...(direction ? { direction } : {}),\n // Only the link the caller fixed. A key with no value is\n // `undefined`, which Firestore refuses, so each is spread in\n // rather than written blank.\n ...(link.contactId ? { contactId: link.contactId } : {}),\n ...(link.companyId ? { companyId: link.companyId } : {}),\n ...(link.dealId ? { dealId: link.dealId } : {}),\n ...(link.leadId ? { leadId: link.leadId } : {}),\n visibleTo: writeTokens,\n hostId,\n byUid: user.uid,\n // Absent rather than blank while the profile is still resolving:\n // the row reads a stored name over anything else, and an empty\n // string would be a name.\n ...(authorName ? { byName: authorName } : {}),\n createdAt: new Date(),\n updatedAt: new Date(),\n },\n )\n enqueueSnackbar('Activity logged', { variant: 'success', persist: false })\n }\n onClose()\n } catch (error) {\n console.error(error)\n enqueueSnackbar('An error has occurred', {\n variant: 'error',\n allowDuplicate: true,\n })\n } finally {\n setSaving(false)\n }\n }, [\n dataScope,\n user,\n body,\n atMs,\n durationValid,\n draft.kind,\n draft.outcome,\n direction,\n hasOutcome,\n duration,\n activity,\n firestore,\n enqueueSnackbar,\n link,\n readTokens,\n writeTokens,\n hostId,\n authorName,\n onClose,\n ])\n\n return (\n <Dialog open={open} onClose={onClose} maxWidth=\"sm\" fullWidth>\n <DialogTitle>{activity ? 'Edit activity' : 'Log activity'}</DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n {/* Only for a new activity at the organization level — see `canSave`. */}\n {activity ? null : (\n <CrmSitePicker\n hostId={mountedHostId}\n disabled={saving}\n helperText=\"The site this activity is logged from — it decides which of your sites may see it.\"\n />\n )}\n <Stack direction=\"row\" spacing={1} sx={{ mt: 1 }}>\n <TextField\n select\n size=\"small\"\n label=\"Kind\"\n value={draft.kind}\n onChange={(event) =>\n setDraft((prev) => ({\n ...prev,\n kind: Aglyn.isCrmActivityKind(event.target.value)\n ? event.target.value\n : prev.kind,\n }))\n }\n sx={{ minWidth: 140 }}\n >\n {Aglyn.CRM_ACTIVITY_KINDS.map((kind) => (\n <MenuItem key={kind} value={kind}>\n {Aglyn.CRM_ACTIVITY_KIND_LABELS[kind]}\n </MenuItem>\n ))}\n </TextField>\n {directions.length ? (\n <TextField\n select\n size=\"small\"\n label=\"Direction\"\n value={direction ?? ''}\n onChange={(event) =>\n setDraft((prev) => ({ ...prev, direction: String(event.target.value) }))\n }\n slotProps={{ select: { displayEmpty: true }, inputLabel: { shrink: true } }}\n sx={{ minWidth: 130 }}\n >\n <MenuItem value=\"\">{'Not said'}</MenuItem>\n {directions.map((entry) => (\n <MenuItem key={entry} value={entry}>\n {Aglyn.CRM_ACTIVITY_DIRECTION_LABELS[entry]}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n <TextField\n size=\"small\"\n label=\"When\"\n type=\"datetime-local\"\n value={draft.at}\n onChange={(event) =>\n setDraft((prev) => ({ ...prev, at: event.target.value }))\n }\n error={draft.at.length > 0 && atMs === null}\n slotProps={{ inputLabel: { shrink: true } }}\n sx={{ flex: 1 }}\n />\n </Stack>\n <TextField\n size=\"small\"\n label=\"What happened\"\n value={draft.body}\n onChange={(event) =>\n setDraft((prev) => ({ ...prev, body: event.target.value }))\n }\n multiline\n minRows={3}\n autoFocus\n slotProps={{ htmlInput: { maxLength: BODY_MAX } }}\n />\n {hasOutcome ? (\n <Stack direction=\"row\" spacing={1}>\n <TextField\n size=\"small\"\n label=\"Outcome\"\n placeholder=\"Left a voicemail\"\n value={draft.outcome}\n onChange={(event) =>\n setDraft((prev) => ({ ...prev, outcome: event.target.value }))\n }\n slotProps={{ htmlInput: { maxLength: OUTCOME_MAX } }}\n sx={{ flex: 1 }}\n />\n <TextField\n size=\"small\"\n label=\"Minutes\"\n type=\"number\"\n value={draft.durationMinutes}\n onChange={(event) =>\n setDraft((prev) => ({\n ...prev,\n durationMinutes: event.target.value,\n }))\n }\n error={!durationValid}\n slotProps={{\n htmlInput: { min: 0, max: DURATION_MAX_MINUTES, step: 1 },\n }}\n sx={{ width: 120 }}\n />\n </Stack>\n ) : null}\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} disabled={saving}>\n {'Cancel'}\n </Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={!canSave}\n onClick={handleSave}\n >\n {activity ? 'Save' : 'Log'}\n </Button>\n </DialogActions>\n </Dialog>\n )\n}\nLogActivityDialog.displayName = 'LogActivityDialog'\n\nexport default LogActivityDialog\n"],"names":["Aglyn","useSnackbar","useUser","useUserName","Button","Dialog","DialogActions","DialogContent","DialogTitle","MenuItem","Stack","TextField","addDoc","collection","deleteField","doc","getCountFromServer","query","updateDoc","where","useCallback","useEffect","useState","crmVisibleToClause","CrmSitePicker","BODY_MAX","OUTCOME_MAX","DURATION_MAX_MINUTES","toLocalInput","ms","Number","isFinite","date","Date","pad","value","String","padStart","getFullYear","getMonth","getDate","getHours","getMinutes","fromLocalInput","getTime","DEFAULT_KIND","freshDraft","kind","body","at","now","outcome","durationMinutes","direction","draftFrom","activity","isCrmActivityKind","atMs","crmActivityDirection","LogActivityDialog","props","open","onClose","scope","link","presetKind","firestore","dataScope","hostId","mountedHostId","readTokens","writeTokens","data","user","authorName","enqueueSnackbar","draft","setDraft","saving","setSaving","hasOutcome","activityKindHasOutcome","directions","CRM_ACTIVITY_DIRECTIONS","trim","duration","durationValid","isInteger","canSave","Boolean","uid","length","handleSave","said","slice","CRM_COLLECTIONS","activities","$id","updatedAt","variant","persist","lead","crmActivityCeilingLink","logged","field","id","count","crmActivityLogHasRoom","CRM_ACTIVITY_LOG_FULL_MESSAGE","contactId","companyId","dealId","leadId","visibleTo","byUid","byName","createdAt","error","console","allowDuplicate","maxWidth","fullWidth","sx","display","flexDirection","gap","disabled","helperText","spacing","mt","select","size","label","onChange","event","prev","target","minWidth","CRM_ACTIVITY_KINDS","map","CRM_ACTIVITY_KIND_LABELS","slotProps","displayEmpty","inputLabel","shrink","entry","CRM_ACTIVITY_DIRECTION_LABELS","type","flex","multiline","minRows","autoFocus","htmlInput","maxLength","placeholder","min","max","step","width","onClick","color","displayName"],"mappings":"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"}
|
|
@@ -3,7 +3,15 @@ import type { OrgMemberOption } from './use-org-members';
|
|
|
3
3
|
/** What the drawer hands back — already normalized where the route would. */
|
|
4
4
|
export interface NewContactValues {
|
|
5
5
|
email: string;
|
|
6
|
+
/** The name as typed, or the first and last names' composition when either was given. */
|
|
6
7
|
name: string;
|
|
8
|
+
/** Salesforce's standard contact fields (AGL-3515), `''` (or `false`) for none. */
|
|
9
|
+
salutation: string;
|
|
10
|
+
firstName: string;
|
|
11
|
+
lastName: string;
|
|
12
|
+
mobilePhone: string;
|
|
13
|
+
department: string;
|
|
14
|
+
doNotCall: boolean;
|
|
7
15
|
phone: string;
|
|
8
16
|
jobTitle: string;
|
|
9
17
|
/** The picked company's name — the label the list column and the search read. */
|
|
@@ -15,17 +15,19 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/ 'use client';
|
|
17
17
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
18
|
-
import { CONTACT_LIFECYCLE_STAGE_LABELS, CONTACT_LIFECYCLE_STAGES, consentGroupDisclosure, consentGroupDisclosureKey, normalizeAddress, normalizeContactEmail, normalizePhone, crmMemberPickerLabel } from "@aglyn/aglyn";
|
|
18
|
+
import { composeContactName, CONTACT_LIFECYCLE_STAGE_LABELS, CONTACT_NAME_PART_MAX, CRM_SALUTATION_PICKLIST, CONTACT_LIFECYCLE_STAGES, consentGroupDisclosure, consentGroupDisclosureKey, normalizeAddress, normalizeContactEmail, normalizePhone, crmMemberPickerLabel } from "@aglyn/aglyn";
|
|
19
19
|
import { ICON_VARIANT_CLOSE } from "@aglyn/shared-data-enums";
|
|
20
20
|
import { Container, MdiIcon, SrOnly } from "@aglyn/shared-ui-jsx";
|
|
21
21
|
import { NavigationDrawerComponent } from "@aglyn/shared-ui-jsx/components/navigation-drawer.component";
|
|
22
22
|
import { Alert, Button, Checkbox, FormControlLabel, FormHelperText, IconButton, MenuItem, Stack, TextField, Typography } from "@mui/material";
|
|
23
23
|
import { useEffect, useState } from "react";
|
|
24
|
+
import { useCrmPicklist } from "../hooks/use-crm-picklist.js";
|
|
24
25
|
import { useCrmScope } from "../hooks/use-crm-scope.js";
|
|
25
26
|
import { parseContactTags } from "../model/contact-record.js";
|
|
26
27
|
import { CompanyPicker, useCompanyOptions, useCreateCompany } from "./company-picker.js";
|
|
27
28
|
import { ContactAddressFields, EMPTY_ADDRESS } from "./contact-address-fields.js";
|
|
28
29
|
import { CrmSitePicker } from "./crm-site-picker.js";
|
|
30
|
+
import { CrmPicklistSelect } from "./picklist-select.js";
|
|
29
31
|
/**
|
|
30
32
|
* ADDING ONE PERSON BY HAND, IN A DRAWER (AGL-2596).
|
|
31
33
|
*
|
|
@@ -69,11 +71,12 @@ import { CrmSitePicker } from "./crm-site-picker.js";
|
|
|
69
71
|
* site's group, or at the organization level the picked site's — the one
|
|
70
72
|
* the route will capture the contact under. `null` until a site is
|
|
71
73
|
* picked, and the submit waits on it.
|
|
72
|
-
*/ const { createHostId, createGroup } = useCrmScope({
|
|
74
|
+
*/ const { createHostId, createGroup, orgId } = useCrmScope({
|
|
73
75
|
hostId,
|
|
74
76
|
org
|
|
75
77
|
});
|
|
76
78
|
const consentGroup = createGroup;
|
|
79
|
+
const salutations = useCrmPicklist(CRM_SALUTATION_PICKLIST, open ? orgId : null);
|
|
77
80
|
const companies = useCompanyOptions({
|
|
78
81
|
hostId,
|
|
79
82
|
org,
|
|
@@ -85,6 +88,13 @@ import { CrmSitePicker } from "./crm-site-picker.js";
|
|
|
85
88
|
});
|
|
86
89
|
const [email, setEmail] = useState('');
|
|
87
90
|
const [name, setName] = useState('');
|
|
91
|
+
const [salutation, setSalutation] = useState('');
|
|
92
|
+
const [firstName, setFirstName] = useState('');
|
|
93
|
+
const [lastName, setLastName] = useState('');
|
|
94
|
+
const [mobilePhone, setMobilePhone] = useState('');
|
|
95
|
+
const [department, setDepartment] = useState('');
|
|
96
|
+
const [doNotCall, setDoNotCall] = useState(false);
|
|
97
|
+
const [mobileError, setMobileError] = useState('');
|
|
88
98
|
const [phone, setPhone] = useState('');
|
|
89
99
|
const [jobTitle, setJobTitle] = useState('');
|
|
90
100
|
const [company, setCompany] = useState(null);
|
|
@@ -103,6 +113,13 @@ import { CrmSitePicker } from "./crm-site-picker.js";
|
|
|
103
113
|
if (!open) return;
|
|
104
114
|
setEmail('');
|
|
105
115
|
setName('');
|
|
116
|
+
setSalutation('');
|
|
117
|
+
setFirstName('');
|
|
118
|
+
setLastName('');
|
|
119
|
+
setMobilePhone('');
|
|
120
|
+
setDepartment('');
|
|
121
|
+
setDoNotCall(false);
|
|
122
|
+
setMobileError('');
|
|
106
123
|
setPhone('');
|
|
107
124
|
setJobTitle('');
|
|
108
125
|
setCompany(null);
|
|
@@ -123,10 +140,21 @@ import { CrmSitePicker } from "./crm-site-picker.js";
|
|
|
123
140
|
const normalizedPhone = trimmedPhone ? normalizePhone(trimmedPhone) : '';
|
|
124
141
|
setEmailError(normalizedEmail ? '' : 'Enter a valid email address.');
|
|
125
142
|
setPhoneError(trimmedPhone && !normalizedPhone ? 'Enter it with its country code, like +1 512 555 0107.' : '');
|
|
126
|
-
|
|
143
|
+
const trimmedMobile = mobilePhone.trim();
|
|
144
|
+
const normalizedMobile = trimmedMobile ? normalizePhone(trimmedMobile) : '';
|
|
145
|
+
setMobileError(trimmedMobile && !normalizedMobile ? 'Enter it with its country code, like +1 512 555 0107.' : '');
|
|
146
|
+
if (!normalizedEmail || trimmedPhone && !normalizedPhone || trimmedMobile && !normalizedMobile) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
127
149
|
onSubmit({
|
|
128
150
|
email: normalizedEmail,
|
|
129
|
-
name: name.trim().slice(0, 120),
|
|
151
|
+
name: composedName || name.trim().slice(0, 120),
|
|
152
|
+
salutation,
|
|
153
|
+
firstName: firstName.trim(),
|
|
154
|
+
lastName: lastName.trim(),
|
|
155
|
+
mobilePhone: normalizedMobile != null ? normalizedMobile : '',
|
|
156
|
+
department: department.trim().slice(0, 120),
|
|
157
|
+
doNotCall,
|
|
130
158
|
phone: normalizedPhone != null ? normalizedPhone : '',
|
|
131
159
|
jobTitle: jobTitle.trim().slice(0, 120),
|
|
132
160
|
companyName: company ? company.name.trim().slice(0, 120) : '',
|
|
@@ -140,6 +168,8 @@ import { CrmSitePicker } from "./crm-site-picker.js";
|
|
|
140
168
|
});
|
|
141
169
|
};
|
|
142
170
|
const disclosure = consentGroup ? consentGroupDisclosure(consentGroup) : null;
|
|
171
|
+
// While a first or last name is typed, the name is theirs (AGL-3515).
|
|
172
|
+
const composedName = composeContactName(firstName, lastName);
|
|
143
173
|
return /*#__PURE__*/ _jsx(NavigationDrawerComponent, {
|
|
144
174
|
open: open,
|
|
145
175
|
anchor: "right",
|
|
@@ -213,16 +243,60 @@ import { CrmSitePicker } from "./crm-site-picker.js";
|
|
|
213
243
|
fullWidth: true,
|
|
214
244
|
autoFocus: true
|
|
215
245
|
}),
|
|
246
|
+
/*#__PURE__*/ _jsx(CrmPicklistSelect, {
|
|
247
|
+
picklistId: CRM_SALUTATION_PICKLIST,
|
|
248
|
+
picklist: salutations.picklist,
|
|
249
|
+
value: salutation,
|
|
250
|
+
onChange: setSalutation,
|
|
251
|
+
disabled: Boolean(busy)
|
|
252
|
+
}),
|
|
253
|
+
/*#__PURE__*/ _jsxs(Stack, {
|
|
254
|
+
direction: {
|
|
255
|
+
xs: 'column',
|
|
256
|
+
sm: 'row'
|
|
257
|
+
},
|
|
258
|
+
spacing: 2,
|
|
259
|
+
children: [
|
|
260
|
+
/*#__PURE__*/ _jsx(TextField, {
|
|
261
|
+
size: "small",
|
|
262
|
+
label: "First name",
|
|
263
|
+
value: firstName,
|
|
264
|
+
onChange: (event)=>setFirstName(event.target.value),
|
|
265
|
+
slotProps: {
|
|
266
|
+
htmlInput: {
|
|
267
|
+
maxLength: CONTACT_NAME_PART_MAX
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
fullWidth: true
|
|
271
|
+
}),
|
|
272
|
+
/*#__PURE__*/ _jsx(TextField, {
|
|
273
|
+
size: "small",
|
|
274
|
+
label: "Last name",
|
|
275
|
+
value: lastName,
|
|
276
|
+
onChange: (event)=>setLastName(event.target.value),
|
|
277
|
+
slotProps: {
|
|
278
|
+
htmlInput: {
|
|
279
|
+
maxLength: CONTACT_NAME_PART_MAX
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
fullWidth: true
|
|
283
|
+
})
|
|
284
|
+
]
|
|
285
|
+
}),
|
|
216
286
|
/*#__PURE__*/ _jsx(TextField, {
|
|
217
287
|
size: "small",
|
|
218
288
|
label: "Name",
|
|
219
|
-
value: name,
|
|
289
|
+
value: composedName || name,
|
|
220
290
|
onChange: (event)=>setName(event.target.value),
|
|
221
291
|
slotProps: {
|
|
222
292
|
htmlInput: {
|
|
223
293
|
maxLength: 120
|
|
294
|
+
},
|
|
295
|
+
input: {
|
|
296
|
+
readOnly: Boolean(composedName)
|
|
224
297
|
}
|
|
225
298
|
},
|
|
299
|
+
helperText: composedName ? 'Made of the first and last name.' : 'Or the whole name in one field.',
|
|
226
300
|
fullWidth: true
|
|
227
301
|
}),
|
|
228
302
|
/*#__PURE__*/ _jsx(TextField, {
|
|
@@ -234,6 +308,22 @@ import { CrmSitePicker } from "./crm-site-picker.js";
|
|
|
234
308
|
helperText: phoneError || 'With the country code, like +1 512 555 0107',
|
|
235
309
|
fullWidth: true
|
|
236
310
|
}),
|
|
311
|
+
/*#__PURE__*/ _jsx(TextField, {
|
|
312
|
+
size: "small",
|
|
313
|
+
label: "Mobile phone",
|
|
314
|
+
value: mobilePhone,
|
|
315
|
+
onChange: (event)=>setMobilePhone(event.target.value),
|
|
316
|
+
error: Boolean(mobileError),
|
|
317
|
+
helperText: mobileError || undefined,
|
|
318
|
+
fullWidth: true
|
|
319
|
+
}),
|
|
320
|
+
/*#__PURE__*/ _jsx(FormControlLabel, {
|
|
321
|
+
control: /*#__PURE__*/ _jsx(Checkbox, {
|
|
322
|
+
checked: doNotCall,
|
|
323
|
+
onChange: (event)=>setDoNotCall(event.target.checked)
|
|
324
|
+
}),
|
|
325
|
+
label: "Do not call"
|
|
326
|
+
}),
|
|
237
327
|
/*#__PURE__*/ _jsx(TextField, {
|
|
238
328
|
size: "small",
|
|
239
329
|
label: "Job title",
|
|
@@ -246,6 +336,18 @@ import { CrmSitePicker } from "./crm-site-picker.js";
|
|
|
246
336
|
},
|
|
247
337
|
fullWidth: true
|
|
248
338
|
}),
|
|
339
|
+
/*#__PURE__*/ _jsx(TextField, {
|
|
340
|
+
size: "small",
|
|
341
|
+
label: "Department",
|
|
342
|
+
value: department,
|
|
343
|
+
onChange: (event)=>setDepartment(event.target.value),
|
|
344
|
+
slotProps: {
|
|
345
|
+
htmlInput: {
|
|
346
|
+
maxLength: 120
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
fullWidth: true
|
|
350
|
+
}),
|
|
249
351
|
/*#__PURE__*/ _jsx(CompanyPicker, {
|
|
250
352
|
options: companies.options,
|
|
251
353
|
ready: companies.ready,
|
|
@@ -305,7 +407,7 @@ import { CrmSitePicker } from "./crm-site-picker.js";
|
|
|
305
407
|
}),
|
|
306
408
|
/*#__PURE__*/ _jsx(Typography, {
|
|
307
409
|
variant: "subtitle2",
|
|
308
|
-
children: '
|
|
410
|
+
children: 'Mailing address'
|
|
309
411
|
}),
|
|
310
412
|
/*#__PURE__*/ _jsx(ContactAddressFields, {
|
|
311
413
|
value: address,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/new-contact-drawer.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 CONTACT_LIFECYCLE_STAGE_LABELS,\n CONTACT_LIFECYCLE_STAGES,\n consentGroupDisclosure,\n consentGroupDisclosureKey,\n type AglynOrgBilling,\n type AglynPostalAddress,\n type ConsentGroup,\n type ContactLifecycleStage,\n normalizeAddress,\n normalizeContactEmail,\n normalizePhone,\n crmMemberPickerLabel,\n} from '@aglyn/aglyn'\nimport { ICON_VARIANT_CLOSE } from '@aglyn/shared-data-enums'\nimport { Container, MdiIcon, SrOnly } from '@aglyn/shared-ui-jsx'\nimport { NavigationDrawerComponent } from '@aglyn/shared-ui-jsx/components/navigation-drawer.component'\nimport {\n Alert,\n Button,\n Checkbox,\n FormControlLabel,\n FormHelperText,\n IconButton,\n MenuItem,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useEffect, useState } from 'react'\nimport { useCrmScope } from '../hooks/use-crm-scope'\nimport { parseContactTags } from '../model/contact-record'\nimport {\n CompanyPicker,\n type CompanyOption,\n useCompanyOptions,\n useCreateCompany,\n} from './company-picker'\nimport {\n ContactAddressFields,\n EMPTY_ADDRESS,\n type AddressDraft,\n} from './contact-address-fields'\nimport { CrmSitePicker } from './crm-site-picker'\nimport type { OrgMemberOption } from './use-org-members'\n\n/** What the drawer hands back — already normalized where the route would. */\nexport interface NewContactValues {\n email: string\n name: string\n phone: string\n jobTitle: string\n /** The picked company's name — the label the list column and the search read. */\n companyName: string\n /** The picked company, or `null` for none (AGL-2613). */\n companyId: string | null\n ownerUid: string\n lifecycleStage: ContactLifecycleStage | ''\n tags: string[]\n address: AglynPostalAddress | null\n marketingConsent: boolean\n /**\n * The key of the consent-group sentence shown beside the box, when one was\n * (AGL-3320) — what lets the route record the opt-in for the whole group\n * the sentence named. `null` when the site sends on its own.\n */\n disclosedConsentGroup: string | null\n}\n\nexport interface NewContactDrawerProps {\n open: boolean\n onClose: () => void\n /**\n * The site the contact is being added from — what scopes the company list\n * and the controller the consent checkbox records a basis for. `null` at\n * the organization level (AGL-2630), where the drawer asks which site\n * with a picker and holds its submit until one is named.\n */\n hostId: string | null\n /** The org document the shell passed, for the company picker's scope. */\n org?: Partial<AglynOrgBilling> | null\n /** The request is in flight — the form holds still and the button says so. */\n busy?: boolean\n /** What the route answered when it refused, shown above the form. */\n error?: string | null\n /** The team, for the owner picker. */\n owners: OrgMemberOption[]\n /** The roster has answered — an empty list is then \"nobody to pick\". */\n ownersReady: boolean\n onSubmit: (values: NewContactValues) => void\n}\n\n/**\n * ADDING ONE PERSON BY HAND, IN A DRAWER (AGL-2596).\n *\n * A drawer and not a form above the list: a create form sitting on top of a\n * table is the shape every list in this console was built to stop. The\n * button in the list's header opens this, and the list is exactly as it was\n * behind it.\n *\n * ## What is checked here and what is not\n *\n * The two fields a person can mistype in a way the record cannot hold — the\n * email and the phone number — are checked before the request leaves, with\n * the same normalizers the route runs, so the refusal lands under the field\n * rather than as a sentence from the server. Everything else is the route's\n * to decide: whether the address already belongs to somebody (it merges),\n * whether the plan's band has room (it refuses, and its sentence is shown\n * above the form unchanged).\n *\n * ## The consent box is a claim, and says so\n *\n * The forms plugin's rule for the opt-in field is that submitting a form is\n * never itself consent. Typing somebody into the CRM is not either — so the\n * checkbox is worded as the person's act, not the operator's, and the helper\n * carries the group disclosure the form would have shown them.\n *\n * ## The company is a record, picked or created here\n *\n * The Company field is the picker (AGL-2613): a choice from the companies\n * this site may see, with a \"Create …\" row for a name none of them carry, so\n * the person filing a contact never leaves the drawer to make the account.\n * What leaves the drawer is the company's id AND its name — the id is the\n * link the route writes into the facet, the name is the label the list\n * column and the global search keep reading. The company listen opens with\n * the drawer and closes with it, because the drawer is mounted only while\n * it is open.\n */\nexport function NewContactDrawer(props: NewContactDrawerProps) {\n const {\n open,\n onClose,\n hostId,\n org,\n busy,\n error,\n owners,\n ownersReady,\n onSubmit,\n } = props\n /*\n * The controller the consent checkbox records a basis FOR: the mounted\n * site's group, or at the organization level the picked site's — the one\n * the route will capture the contact under. `null` until a site is\n * picked, and the submit waits on it.\n */\n const { createHostId, createGroup } = useCrmScope({ hostId, org })\n const consentGroup: ConsentGroup | null = createGroup\n const companies = useCompanyOptions({ hostId, org, enabled: open })\n const createCompany = useCreateCompany({ hostId, org })\n\n const [email, setEmail] = useState('')\n const [name, setName] = useState('')\n const [phone, setPhone] = useState('')\n const [jobTitle, setJobTitle] = useState('')\n const [company, setCompany] = useState<CompanyOption | null>(null)\n const [ownerUid, setOwnerUid] = useState('')\n const [lifecycleStage, setLifecycleStage] = useState<\n ContactLifecycleStage | ''\n >('')\n const [tags, setTags] = useState('')\n const [address, setAddress] = useState<AddressDraft>(EMPTY_ADDRESS)\n const [marketingConsent, setMarketingConsent] = useState(false)\n const [emailError, setEmailError] = useState('')\n const [phoneError, setPhoneError] = useState('')\n\n /*\n * A fresh form on every opening. The drawer stays mounted behind the list\n * between uses, and a person typed and then abandoned must not reappear\n * half-filled the next time somebody reaches for \"New contact\".\n */\n useEffect(() => {\n if (!open) return\n setEmail('')\n setName('')\n setPhone('')\n setJobTitle('')\n setCompany(null)\n setOwnerUid('')\n setLifecycleStage('')\n setTags('')\n setAddress(EMPTY_ADDRESS)\n setMarketingConsent(false)\n setEmailError('')\n setPhoneError('')\n }, [open])\n\n const handleSubmit = () => {\n const normalizedEmail = normalizeContactEmail(email)\n const trimmedPhone = phone.trim()\n const normalizedPhone = trimmedPhone ? normalizePhone(trimmedPhone) : ''\n setEmailError(normalizedEmail ? '' : 'Enter a valid email address.')\n setPhoneError(\n trimmedPhone && !normalizedPhone\n ? 'Enter it with its country code, like +1 512 555 0107.'\n : '',\n )\n if (!normalizedEmail || (trimmedPhone && !normalizedPhone)) return\n onSubmit({\n email: normalizedEmail,\n name: name.trim().slice(0, 120),\n phone: normalizedPhone ?? '',\n jobTitle: jobTitle.trim().slice(0, 120),\n companyName: company ? company.name.trim().slice(0, 120) : '',\n companyId: company?.id ?? null,\n ownerUid,\n lifecycleStage,\n tags: parseContactTags(tags),\n address: normalizeAddress(address),\n marketingConsent,\n disclosedConsentGroup: consentGroup\n ? consentGroupDisclosureKey(consentGroup)\n : null,\n })\n }\n\n const disclosure = consentGroup ? consentGroupDisclosure(consentGroup) : null\n\n return (\n <NavigationDrawerComponent\n open={open}\n anchor=\"right\"\n variant=\"temporary\"\n onClose={onClose}\n AppBarProps={{ color: 'surface' }}\n sx={{ '& .MuiDrawer-paper': { width: { xs: '100%', sm: 480 } } }}\n appBarLeft={\n <>\n <IconButton\n color=\"inherit\"\n edge=\"start\"\n onClick={onClose}\n sx={{ mr: 2 }}\n >\n <MdiIcon path={ICON_VARIANT_CLOSE.path} />\n <SrOnly>close drawer</SrOnly>\n </IconButton>\n <Typography variant=\"h6\" component=\"div\">\n {'New contact'}\n </Typography>\n </>\n }\n appBarRight={\n <Button variant=\"outlined\" color=\"inherit\" onClick={onClose}>\n {'Cancel'}\n </Button>\n }\n >\n <Container gutterY>\n <Stack spacing={2}>\n {error ? <Alert severity=\"warning\">{error}</Alert> : null}\n {/*\n First, because everything below it is filed under the answer: the\n site decides which of the org's sites may see the person and whose\n consent the checkbox at the foot records. Renders nothing under a\n site (AGL-2630).\n */}\n <CrmSitePicker\n hostId={hostId}\n disabled={Boolean(busy)}\n helperText=\"The site that captures this person — it decides which sites may see them and whose marketing consent the checkbox below records.\"\n />\n <TextField\n size=\"small\"\n label=\"Email\"\n type=\"email\"\n value={email}\n onChange={(event) => setEmail(event.target.value)}\n error={Boolean(emailError)}\n helperText={\n emailError ||\n 'The one field that is required — it is what makes two captures one person.'\n }\n required\n fullWidth\n autoFocus\n />\n <TextField\n size=\"small\"\n label=\"Name\"\n value={name}\n onChange={(event) => setName(event.target.value)}\n slotProps={{ htmlInput: { maxLength: 120 } }}\n fullWidth\n />\n <TextField\n size=\"small\"\n label=\"Phone\"\n value={phone}\n onChange={(event) => setPhone(event.target.value)}\n error={Boolean(phoneError)}\n helperText={phoneError || 'With the country code, like +1 512 555 0107'}\n fullWidth\n />\n <TextField\n size=\"small\"\n label=\"Job title\"\n value={jobTitle}\n onChange={(event) => setJobTitle(event.target.value)}\n slotProps={{ htmlInput: { maxLength: 120 } }}\n fullWidth\n />\n <CompanyPicker\n options={companies.options}\n ready={companies.ready}\n truncated={companies.truncated}\n value={company?.id ?? null}\n onChange={(_id, picked) => setCompany(picked)}\n onCreate={createCompany}\n email={email}\n disabled={Boolean(busy)}\n helperText=\"The account this person works for. Type a name nobody has filed yet to create it.\"\n />\n <TextField\n select\n size=\"small\"\n label=\"Lifecycle stage\"\n value={lifecycleStage}\n onChange={(event) =>\n setLifecycleStage(event.target.value as ContactLifecycleStage | '')\n }\n fullWidth\n >\n <MenuItem value=\"\">{'Not placed yet'}</MenuItem>\n {CONTACT_LIFECYCLE_STAGES.map((stage) => (\n <MenuItem key={stage} value={stage}>\n {CONTACT_LIFECYCLE_STAGE_LABELS[stage]}\n </MenuItem>\n ))}\n </TextField>\n <TextField\n select\n size=\"small\"\n label=\"Owner\"\n value={ownerUid}\n onChange={(event) => setOwnerUid(event.target.value)}\n helperText={\n ownersReady && !owners.length\n ? 'The team roster could not be read, so nobody can be picked yet.'\n : 'The team member responsible for this relationship'\n }\n fullWidth\n >\n <MenuItem value=\"\">{'Unassigned'}</MenuItem>\n {owners.map((owner) => (\n <MenuItem key={owner.uid} value={owner.uid}>\n {crmMemberPickerLabel(owner)}\n </MenuItem>\n ))}\n </TextField>\n <TextField\n size=\"small\"\n label=\"Tags\"\n placeholder=\"vip, beta\"\n helperText=\"Comma-separated\"\n value={tags}\n onChange={(event) => setTags(event.target.value)}\n fullWidth\n />\n <Typography variant=\"subtitle2\">{'Address'}</Typography>\n <ContactAddressFields value={address} onChange={setAddress} />\n <Stack>\n <FormControlLabel\n control={\n <Checkbox\n checked={marketingConsent}\n onChange={(event) => setMarketingConsent(event.target.checked)}\n />\n }\n label=\"This person opted in to marketing email\"\n />\n <FormHelperText>\n {(disclosure ? `${disclosure} ` : '') +\n 'Tick it only if they agreed. Adding a contact is never itself consent.'}\n </FormHelperText>\n </Stack>\n <Button\n variant=\"contained\"\n color=\"primary\"\n // Held until the capturing site is known: the route resolves the\n // org from it and stamps the record with it.\n disabled={Boolean(busy) || !createHostId}\n onClick={handleSubmit}\n >\n {busy ? 'Adding…' : 'Add contact'}\n </Button>\n </Stack>\n </Container>\n </NavigationDrawerComponent>\n )\n}\n\nexport default NewContactDrawer\n"],"names":["CONTACT_LIFECYCLE_STAGE_LABELS","CONTACT_LIFECYCLE_STAGES","consentGroupDisclosure","consentGroupDisclosureKey","normalizeAddress","normalizeContactEmail","normalizePhone","crmMemberPickerLabel","ICON_VARIANT_CLOSE","Container","MdiIcon","SrOnly","NavigationDrawerComponent","Alert","Button","Checkbox","FormControlLabel","FormHelperText","IconButton","MenuItem","Stack","TextField","Typography","useEffect","useState","useCrmScope","parseContactTags","CompanyPicker","useCompanyOptions","useCreateCompany","ContactAddressFields","EMPTY_ADDRESS","CrmSitePicker","NewContactDrawer","props","open","onClose","hostId","org","busy","error","owners","ownersReady","onSubmit","createHostId","createGroup","consentGroup","companies","enabled","createCompany","email","setEmail","name","setName","phone","setPhone","jobTitle","setJobTitle","company","setCompany","ownerUid","setOwnerUid","lifecycleStage","setLifecycleStage","tags","setTags","address","setAddress","marketingConsent","setMarketingConsent","emailError","setEmailError","phoneError","setPhoneError","handleSubmit","normalizedEmail","trimmedPhone","trim","normalizedPhone","slice","companyName","companyId","id","disclosedConsentGroup","disclosure","anchor","variant","AppBarProps","color","sx","width","xs","sm","appBarLeft","edge","onClick","mr","path","component","appBarRight","gutterY","spacing","severity","disabled","Boolean","helperText","size","label","type","value","onChange","event","target","required","fullWidth","autoFocus","slotProps","htmlInput","maxLength","options","ready","truncated","_id","picked","onCreate","select","map","stage","length","owner","uid","placeholder","control","checked"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/new-contact-drawer.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 composeContactName,\n CONTACT_LIFECYCLE_STAGE_LABELS,\n CONTACT_NAME_PART_MAX,\n CRM_SALUTATION_PICKLIST,\n CONTACT_LIFECYCLE_STAGES,\n consentGroupDisclosure,\n consentGroupDisclosureKey,\n type AglynOrgBilling,\n type AglynPostalAddress,\n type ConsentGroup,\n type ContactLifecycleStage,\n normalizeAddress,\n normalizeContactEmail,\n normalizePhone,\n crmMemberPickerLabel,\n} from '@aglyn/aglyn'\nimport { ICON_VARIANT_CLOSE } from '@aglyn/shared-data-enums'\nimport { Container, MdiIcon, SrOnly } from '@aglyn/shared-ui-jsx'\nimport { NavigationDrawerComponent } from '@aglyn/shared-ui-jsx/components/navigation-drawer.component'\nimport {\n Alert,\n Button,\n Checkbox,\n FormControlLabel,\n FormHelperText,\n IconButton,\n MenuItem,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useEffect, useState } from 'react'\nimport { useCrmPicklist } from '../hooks/use-crm-picklist'\nimport { useCrmScope } from '../hooks/use-crm-scope'\nimport { parseContactTags } from '../model/contact-record'\nimport {\n CompanyPicker,\n type CompanyOption,\n useCompanyOptions,\n useCreateCompany,\n} from './company-picker'\nimport {\n ContactAddressFields,\n EMPTY_ADDRESS,\n type AddressDraft,\n} from './contact-address-fields'\nimport { CrmSitePicker } from './crm-site-picker'\nimport { CrmPicklistSelect } from './picklist-select'\nimport type { OrgMemberOption } from './use-org-members'\n\n/** What the drawer hands back — already normalized where the route would. */\nexport interface NewContactValues {\n email: string\n /** The name as typed, or the first and last names' composition when either was given. */\n name: string\n /** Salesforce's standard contact fields (AGL-3515), `''` (or `false`) for none. */\n salutation: string\n firstName: string\n lastName: string\n mobilePhone: string\n department: string\n doNotCall: boolean\n phone: string\n jobTitle: string\n /** The picked company's name — the label the list column and the search read. */\n companyName: string\n /** The picked company, or `null` for none (AGL-2613). */\n companyId: string | null\n ownerUid: string\n lifecycleStage: ContactLifecycleStage | ''\n tags: string[]\n address: AglynPostalAddress | null\n marketingConsent: boolean\n /**\n * The key of the consent-group sentence shown beside the box, when one was\n * (AGL-3320) — what lets the route record the opt-in for the whole group\n * the sentence named. `null` when the site sends on its own.\n */\n disclosedConsentGroup: string | null\n}\n\nexport interface NewContactDrawerProps {\n open: boolean\n onClose: () => void\n /**\n * The site the contact is being added from — what scopes the company list\n * and the controller the consent checkbox records a basis for. `null` at\n * the organization level (AGL-2630), where the drawer asks which site\n * with a picker and holds its submit until one is named.\n */\n hostId: string | null\n /** The org document the shell passed, for the company picker's scope. */\n org?: Partial<AglynOrgBilling> | null\n /** The request is in flight — the form holds still and the button says so. */\n busy?: boolean\n /** What the route answered when it refused, shown above the form. */\n error?: string | null\n /** The team, for the owner picker. */\n owners: OrgMemberOption[]\n /** The roster has answered — an empty list is then \"nobody to pick\". */\n ownersReady: boolean\n onSubmit: (values: NewContactValues) => void\n}\n\n/**\n * ADDING ONE PERSON BY HAND, IN A DRAWER (AGL-2596).\n *\n * A drawer and not a form above the list: a create form sitting on top of a\n * table is the shape every list in this console was built to stop. The\n * button in the list's header opens this, and the list is exactly as it was\n * behind it.\n *\n * ## What is checked here and what is not\n *\n * The two fields a person can mistype in a way the record cannot hold — the\n * email and the phone number — are checked before the request leaves, with\n * the same normalizers the route runs, so the refusal lands under the field\n * rather than as a sentence from the server. Everything else is the route's\n * to decide: whether the address already belongs to somebody (it merges),\n * whether the plan's band has room (it refuses, and its sentence is shown\n * above the form unchanged).\n *\n * ## The consent box is a claim, and says so\n *\n * The forms plugin's rule for the opt-in field is that submitting a form is\n * never itself consent. Typing somebody into the CRM is not either — so the\n * checkbox is worded as the person's act, not the operator's, and the helper\n * carries the group disclosure the form would have shown them.\n *\n * ## The company is a record, picked or created here\n *\n * The Company field is the picker (AGL-2613): a choice from the companies\n * this site may see, with a \"Create …\" row for a name none of them carry, so\n * the person filing a contact never leaves the drawer to make the account.\n * What leaves the drawer is the company's id AND its name — the id is the\n * link the route writes into the facet, the name is the label the list\n * column and the global search keep reading. The company listen opens with\n * the drawer and closes with it, because the drawer is mounted only while\n * it is open.\n */\nexport function NewContactDrawer(props: NewContactDrawerProps) {\n const {\n open,\n onClose,\n hostId,\n org,\n busy,\n error,\n owners,\n ownersReady,\n onSubmit,\n } = props\n /*\n * The controller the consent checkbox records a basis FOR: the mounted\n * site's group, or at the organization level the picked site's — the one\n * the route will capture the contact under. `null` until a site is\n * picked, and the submit waits on it.\n */\n const { createHostId, createGroup, orgId } = useCrmScope({ hostId, org })\n const consentGroup: ConsentGroup | null = createGroup\n const salutations = useCrmPicklist(CRM_SALUTATION_PICKLIST, open ? orgId : null)\n const companies = useCompanyOptions({ hostId, org, enabled: open })\n const createCompany = useCreateCompany({ hostId, org })\n\n const [email, setEmail] = useState('')\n const [name, setName] = useState('')\n const [salutation, setSalutation] = useState('')\n const [firstName, setFirstName] = useState('')\n const [lastName, setLastName] = useState('')\n const [mobilePhone, setMobilePhone] = useState('')\n const [department, setDepartment] = useState('')\n const [doNotCall, setDoNotCall] = useState(false)\n const [mobileError, setMobileError] = useState('')\n const [phone, setPhone] = useState('')\n const [jobTitle, setJobTitle] = useState('')\n const [company, setCompany] = useState<CompanyOption | null>(null)\n const [ownerUid, setOwnerUid] = useState('')\n const [lifecycleStage, setLifecycleStage] = useState<\n ContactLifecycleStage | ''\n >('')\n const [tags, setTags] = useState('')\n const [address, setAddress] = useState<AddressDraft>(EMPTY_ADDRESS)\n const [marketingConsent, setMarketingConsent] = useState(false)\n const [emailError, setEmailError] = useState('')\n const [phoneError, setPhoneError] = useState('')\n\n /*\n * A fresh form on every opening. The drawer stays mounted behind the list\n * between uses, and a person typed and then abandoned must not reappear\n * half-filled the next time somebody reaches for \"New contact\".\n */\n useEffect(() => {\n if (!open) return\n setEmail('')\n setName('')\n setSalutation('')\n setFirstName('')\n setLastName('')\n setMobilePhone('')\n setDepartment('')\n setDoNotCall(false)\n setMobileError('')\n setPhone('')\n setJobTitle('')\n setCompany(null)\n setOwnerUid('')\n setLifecycleStage('')\n setTags('')\n setAddress(EMPTY_ADDRESS)\n setMarketingConsent(false)\n setEmailError('')\n setPhoneError('')\n }, [open])\n\n const handleSubmit = () => {\n const normalizedEmail = normalizeContactEmail(email)\n const trimmedPhone = phone.trim()\n const normalizedPhone = trimmedPhone ? normalizePhone(trimmedPhone) : ''\n setEmailError(normalizedEmail ? '' : 'Enter a valid email address.')\n setPhoneError(\n trimmedPhone && !normalizedPhone\n ? 'Enter it with its country code, like +1 512 555 0107.'\n : '',\n )\n const trimmedMobile = mobilePhone.trim()\n const normalizedMobile = trimmedMobile ? normalizePhone(trimmedMobile) : ''\n setMobileError(\n trimmedMobile && !normalizedMobile\n ? 'Enter it with its country code, like +1 512 555 0107.'\n : '',\n )\n if (\n !normalizedEmail ||\n (trimmedPhone && !normalizedPhone) ||\n (trimmedMobile && !normalizedMobile)\n ) {\n return\n }\n onSubmit({\n email: normalizedEmail,\n name: composedName || name.trim().slice(0, 120),\n salutation,\n firstName: firstName.trim(),\n lastName: lastName.trim(),\n mobilePhone: normalizedMobile ?? '',\n department: department.trim().slice(0, 120),\n doNotCall,\n phone: normalizedPhone ?? '',\n jobTitle: jobTitle.trim().slice(0, 120),\n companyName: company ? company.name.trim().slice(0, 120) : '',\n companyId: company?.id ?? null,\n ownerUid,\n lifecycleStage,\n tags: parseContactTags(tags),\n address: normalizeAddress(address),\n marketingConsent,\n disclosedConsentGroup: consentGroup\n ? consentGroupDisclosureKey(consentGroup)\n : null,\n })\n }\n\n const disclosure = consentGroup ? consentGroupDisclosure(consentGroup) : null\n // While a first or last name is typed, the name is theirs (AGL-3515).\n const composedName = composeContactName(firstName, lastName)\n\n return (\n <NavigationDrawerComponent\n open={open}\n anchor=\"right\"\n variant=\"temporary\"\n onClose={onClose}\n AppBarProps={{ color: 'surface' }}\n sx={{ '& .MuiDrawer-paper': { width: { xs: '100%', sm: 480 } } }}\n appBarLeft={\n <>\n <IconButton\n color=\"inherit\"\n edge=\"start\"\n onClick={onClose}\n sx={{ mr: 2 }}\n >\n <MdiIcon path={ICON_VARIANT_CLOSE.path} />\n <SrOnly>close drawer</SrOnly>\n </IconButton>\n <Typography variant=\"h6\" component=\"div\">\n {'New contact'}\n </Typography>\n </>\n }\n appBarRight={\n <Button variant=\"outlined\" color=\"inherit\" onClick={onClose}>\n {'Cancel'}\n </Button>\n }\n >\n <Container gutterY>\n <Stack spacing={2}>\n {error ? <Alert severity=\"warning\">{error}</Alert> : null}\n {/*\n First, because everything below it is filed under the answer: the\n site decides which of the org's sites may see the person and whose\n consent the checkbox at the foot records. Renders nothing under a\n site (AGL-2630).\n */}\n <CrmSitePicker\n hostId={hostId}\n disabled={Boolean(busy)}\n helperText=\"The site that captures this person — it decides which sites may see them and whose marketing consent the checkbox below records.\"\n />\n <TextField\n size=\"small\"\n label=\"Email\"\n type=\"email\"\n value={email}\n onChange={(event) => setEmail(event.target.value)}\n error={Boolean(emailError)}\n helperText={\n emailError ||\n 'The one field that is required — it is what makes two captures one person.'\n }\n required\n fullWidth\n autoFocus\n />\n <CrmPicklistSelect\n picklistId={CRM_SALUTATION_PICKLIST}\n picklist={salutations.picklist}\n value={salutation}\n onChange={setSalutation}\n disabled={Boolean(busy)}\n />\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={2}>\n <TextField\n size=\"small\"\n label=\"First name\"\n value={firstName}\n onChange={(event) => setFirstName(event.target.value)}\n slotProps={{ htmlInput: { maxLength: CONTACT_NAME_PART_MAX } }}\n fullWidth\n />\n <TextField\n size=\"small\"\n label=\"Last name\"\n value={lastName}\n onChange={(event) => setLastName(event.target.value)}\n slotProps={{ htmlInput: { maxLength: CONTACT_NAME_PART_MAX } }}\n fullWidth\n />\n </Stack>\n <TextField\n size=\"small\"\n label=\"Name\"\n value={composedName || name}\n onChange={(event) => setName(event.target.value)}\n slotProps={{\n htmlInput: { maxLength: 120 },\n input: { readOnly: Boolean(composedName) },\n }}\n helperText={\n composedName\n ? 'Made of the first and last name.'\n : 'Or the whole name in one field.'\n }\n fullWidth\n />\n <TextField\n size=\"small\"\n label=\"Phone\"\n value={phone}\n onChange={(event) => setPhone(event.target.value)}\n error={Boolean(phoneError)}\n helperText={phoneError || 'With the country code, like +1 512 555 0107'}\n fullWidth\n />\n <TextField\n size=\"small\"\n label=\"Mobile phone\"\n value={mobilePhone}\n onChange={(event) => setMobilePhone(event.target.value)}\n error={Boolean(mobileError)}\n helperText={mobileError || undefined}\n fullWidth\n />\n <FormControlLabel\n control={\n <Checkbox\n checked={doNotCall}\n onChange={(event) => setDoNotCall(event.target.checked)}\n />\n }\n label=\"Do not call\"\n />\n <TextField\n size=\"small\"\n label=\"Job title\"\n value={jobTitle}\n onChange={(event) => setJobTitle(event.target.value)}\n slotProps={{ htmlInput: { maxLength: 120 } }}\n fullWidth\n />\n <TextField\n size=\"small\"\n label=\"Department\"\n value={department}\n onChange={(event) => setDepartment(event.target.value)}\n slotProps={{ htmlInput: { maxLength: 120 } }}\n fullWidth\n />\n <CompanyPicker\n options={companies.options}\n ready={companies.ready}\n truncated={companies.truncated}\n value={company?.id ?? null}\n onChange={(_id, picked) => setCompany(picked)}\n onCreate={createCompany}\n email={email}\n disabled={Boolean(busy)}\n helperText=\"The account this person works for. Type a name nobody has filed yet to create it.\"\n />\n <TextField\n select\n size=\"small\"\n label=\"Lifecycle stage\"\n value={lifecycleStage}\n onChange={(event) =>\n setLifecycleStage(event.target.value as ContactLifecycleStage | '')\n }\n fullWidth\n >\n <MenuItem value=\"\">{'Not placed yet'}</MenuItem>\n {CONTACT_LIFECYCLE_STAGES.map((stage) => (\n <MenuItem key={stage} value={stage}>\n {CONTACT_LIFECYCLE_STAGE_LABELS[stage]}\n </MenuItem>\n ))}\n </TextField>\n <TextField\n select\n size=\"small\"\n label=\"Owner\"\n value={ownerUid}\n onChange={(event) => setOwnerUid(event.target.value)}\n helperText={\n ownersReady && !owners.length\n ? 'The team roster could not be read, so nobody can be picked yet.'\n : 'The team member responsible for this relationship'\n }\n fullWidth\n >\n <MenuItem value=\"\">{'Unassigned'}</MenuItem>\n {owners.map((owner) => (\n <MenuItem key={owner.uid} value={owner.uid}>\n {crmMemberPickerLabel(owner)}\n </MenuItem>\n ))}\n </TextField>\n <TextField\n size=\"small\"\n label=\"Tags\"\n placeholder=\"vip, beta\"\n helperText=\"Comma-separated\"\n value={tags}\n onChange={(event) => setTags(event.target.value)}\n fullWidth\n />\n <Typography variant=\"subtitle2\">{'Mailing address'}</Typography>\n <ContactAddressFields value={address} onChange={setAddress} />\n <Stack>\n <FormControlLabel\n control={\n <Checkbox\n checked={marketingConsent}\n onChange={(event) => setMarketingConsent(event.target.checked)}\n />\n }\n label=\"This person opted in to marketing email\"\n />\n <FormHelperText>\n {(disclosure ? `${disclosure} ` : '') +\n 'Tick it only if they agreed. Adding a contact is never itself consent.'}\n </FormHelperText>\n </Stack>\n <Button\n variant=\"contained\"\n color=\"primary\"\n // Held until the capturing site is known: the route resolves the\n // org from it and stamps the record with it.\n disabled={Boolean(busy) || !createHostId}\n onClick={handleSubmit}\n >\n {busy ? 'Adding…' : 'Add contact'}\n </Button>\n </Stack>\n </Container>\n </NavigationDrawerComponent>\n )\n}\n\nexport default NewContactDrawer\n"],"names":["composeContactName","CONTACT_LIFECYCLE_STAGE_LABELS","CONTACT_NAME_PART_MAX","CRM_SALUTATION_PICKLIST","CONTACT_LIFECYCLE_STAGES","consentGroupDisclosure","consentGroupDisclosureKey","normalizeAddress","normalizeContactEmail","normalizePhone","crmMemberPickerLabel","ICON_VARIANT_CLOSE","Container","MdiIcon","SrOnly","NavigationDrawerComponent","Alert","Button","Checkbox","FormControlLabel","FormHelperText","IconButton","MenuItem","Stack","TextField","Typography","useEffect","useState","useCrmPicklist","useCrmScope","parseContactTags","CompanyPicker","useCompanyOptions","useCreateCompany","ContactAddressFields","EMPTY_ADDRESS","CrmSitePicker","CrmPicklistSelect","NewContactDrawer","props","open","onClose","hostId","org","busy","error","owners","ownersReady","onSubmit","createHostId","createGroup","orgId","consentGroup","salutations","companies","enabled","createCompany","email","setEmail","name","setName","salutation","setSalutation","firstName","setFirstName","lastName","setLastName","mobilePhone","setMobilePhone","department","setDepartment","doNotCall","setDoNotCall","mobileError","setMobileError","phone","setPhone","jobTitle","setJobTitle","company","setCompany","ownerUid","setOwnerUid","lifecycleStage","setLifecycleStage","tags","setTags","address","setAddress","marketingConsent","setMarketingConsent","emailError","setEmailError","phoneError","setPhoneError","handleSubmit","normalizedEmail","trimmedPhone","trim","normalizedPhone","trimmedMobile","normalizedMobile","composedName","slice","companyName","companyId","id","disclosedConsentGroup","disclosure","anchor","variant","AppBarProps","color","sx","width","xs","sm","appBarLeft","edge","onClick","mr","path","component","appBarRight","gutterY","spacing","severity","disabled","Boolean","helperText","size","label","type","value","onChange","event","target","required","fullWidth","autoFocus","picklistId","picklist","direction","slotProps","htmlInput","maxLength","input","readOnly","undefined","control","checked","options","ready","truncated","_id","picked","onCreate","select","map","stage","length","owner","uid","placeholder"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type CrmCustomValue, type
|
|
1
|
+
import { type CrmCustomValue, type CrmLeadProfilePatch, type AglynPostalAddress } from '@aglyn/aglyn';
|
|
2
2
|
import type { OrgMemberOptions } from '../hooks/use-org-member-options';
|
|
3
3
|
/** What the drawer hands back — already normalized where the route would. */
|
|
4
4
|
export interface NewLeadValues {
|
|
@@ -9,7 +9,8 @@ export interface NewLeadValues {
|
|
|
9
9
|
phone: string;
|
|
10
10
|
website: string;
|
|
11
11
|
leadSource: string;
|
|
12
|
-
|
|
12
|
+
/** One of the org's New or Working values, by its label (AGL-3512). */
|
|
13
|
+
status: string;
|
|
13
14
|
ownerUid: string;
|
|
14
15
|
tags: string[];
|
|
15
16
|
/** The site's campaigns to file the lead under (AGL-3254), by id. */
|
|
@@ -22,6 +23,12 @@ export interface NewLeadValues {
|
|
|
22
23
|
* route stores a `custom` map only when there is one.
|
|
23
24
|
*/
|
|
24
25
|
custom?: Record<string, CrmCustomValue>;
|
|
26
|
+
/**
|
|
27
|
+
* Salesforce's standard lead fields (AGL-3513) — salutation, the name's
|
|
28
|
+
* parts, mobile, fax, do not call, industry, rating, size and revenue —
|
|
29
|
+
* normalized as the route stores them, only the ones that were filled.
|
|
30
|
+
*/
|
|
31
|
+
standard: Pick<CrmLeadProfilePatch, 'salutation' | 'firstName' | 'lastName' | 'mobilePhone' | 'fax' | 'doNotCall' | 'industry' | 'rating' | 'annualRevenueCents' | 'currency' | 'numberOfEmployees'>;
|
|
25
32
|
}
|
|
26
33
|
export interface NewLeadDrawerProps {
|
|
27
34
|
open: boolean;
|