@aglyn/plugins-crm 1.0.0-beta.220 → 1.0.0-beta.222
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/package.json +13 -13
- package/src/lib/components/activity-list.js +12 -5
- package/src/lib/components/activity-list.js.map +1 -1
- package/src/lib/components/companies-bulk-bar.d.ts +1 -4
- package/src/lib/components/companies-bulk-bar.js +7 -20
- package/src/lib/components/companies-bulk-bar.js.map +1 -1
- package/src/lib/components/companies-section.d.ts +3 -3
- package/src/lib/components/companies-section.js +149 -46
- package/src/lib/components/companies-section.js.map +1 -1
- package/src/lib/components/company-edit-drawer.js +281 -20
- package/src/lib/components/company-edit-drawer.js.map +1 -1
- package/src/lib/components/company-properties-card.js +99 -8
- package/src/lib/components/company-properties-card.js.map +1 -1
- package/src/lib/components/contact-detail-page.js +3 -1
- package/src/lib/components/contact-detail-page.js.map +1 -1
- package/src/lib/components/contact-merge-dialog.js +33 -10
- package/src/lib/components/contact-merge-dialog.js.map +1 -1
- package/src/lib/components/contact-properties-card.d.ts +0 -69
- package/src/lib/components/contact-properties-card.js +350 -60
- package/src/lib/components/contact-properties-card.js.map +1 -1
- package/src/lib/components/contact-reports-to-field.d.ts +46 -0
- package/src/lib/components/contact-reports-to-field.js +67 -0
- package/src/lib/components/contact-reports-to-field.js.map +1 -0
- package/src/lib/components/contacts-bulk-bar.d.ts +1 -8
- package/src/lib/components/contacts-bulk-bar.js +7 -19
- package/src/lib/components/contacts-bulk-bar.js.map +1 -1
- package/src/lib/components/contacts-section.js +46 -33
- package/src/lib/components/contacts-section.js.map +1 -1
- package/src/lib/components/crm-call-actions.d.ts +10 -0
- package/src/lib/components/crm-call-actions.js +26 -9
- package/src/lib/components/crm-call-actions.js.map +1 -1
- package/src/lib/components/crm-record-picker.d.ts +15 -0
- package/src/lib/components/crm-record-picker.js +16 -10
- package/src/lib/components/crm-record-picker.js.map +1 -1
- package/src/lib/components/crm-transfer-buttons.d.ts +54 -0
- package/src/lib/components/crm-transfer-buttons.js +167 -0
- package/src/lib/components/crm-transfer-buttons.js.map +1 -0
- package/src/lib/components/deal-contact-roles-card.d.ts +46 -0
- package/src/lib/components/deal-contact-roles-card.js +368 -0
- package/src/lib/components/deal-contact-roles-card.js.map +1 -0
- package/src/lib/components/deal-detail-page.d.ts +3 -2
- package/src/lib/components/deal-detail-page.js +25 -6
- package/src/lib/components/deal-detail-page.js.map +1 -1
- package/src/lib/components/deal-edit-drawer.d.ts +10 -0
- package/src/lib/components/deal-edit-drawer.js +219 -10
- package/src/lib/components/deal-edit-drawer.js.map +1 -1
- package/src/lib/components/deal-properties-card.d.ts +7 -0
- package/src/lib/components/deal-properties-card.js +34 -3
- package/src/lib/components/deal-properties-card.js.map +1 -1
- package/src/lib/components/deal-stage-card.d.ts +4 -0
- package/src/lib/components/deal-stage-card.js +10 -2
- package/src/lib/components/deal-stage-card.js.map +1 -1
- package/src/lib/components/deals-bulk-bar.d.ts +0 -3
- package/src/lib/components/deals-bulk-bar.js +7 -20
- package/src/lib/components/deals-bulk-bar.js.map +1 -1
- package/src/lib/components/deals-section.d.ts +3 -3
- package/src/lib/components/deals-section.js +123 -63
- package/src/lib/components/deals-section.js.map +1 -1
- package/src/lib/components/fields-section.js +31 -17
- package/src/lib/components/fields-section.js.map +1 -1
- package/src/lib/components/lead-convert-dialog.d.ts +4 -0
- package/src/lib/components/lead-convert-dialog.js +23 -1
- package/src/lib/components/lead-convert-dialog.js.map +1 -1
- package/src/lib/components/lead-detail-page.js +8 -2
- package/src/lib/components/lead-detail-page.js.map +1 -1
- package/src/lib/components/lead-profile-fields.d.ts +71 -0
- package/src/lib/components/lead-profile-fields.js +386 -0
- package/src/lib/components/lead-profile-fields.js.map +1 -0
- package/src/lib/components/lead-properties-card.d.ts +2 -1
- package/src/lib/components/lead-properties-card.js +85 -176
- package/src/lib/components/lead-properties-card.js.map +1 -1
- package/src/lib/components/lead-source-select.d.ts +4 -23
- package/src/lib/components/lead-source-select.js +9 -73
- package/src/lib/components/lead-source-select.js.map +1 -1
- package/src/lib/components/lead-status-chip.d.ts +9 -3
- package/src/lib/components/lead-status-chip.js +8 -4
- package/src/lib/components/lead-status-chip.js.map +1 -1
- package/src/lib/components/lead-status-options.d.ts +20 -0
- package/src/lib/components/lead-status-options.js +51 -0
- package/src/lib/components/lead-status-options.js.map +1 -0
- package/src/lib/components/lead-unqualify-dialog.d.ts +5 -0
- package/src/lib/components/lead-unqualify-dialog.js +50 -18
- package/src/lib/components/lead-unqualify-dialog.js.map +1 -1
- package/src/lib/components/leads-bulk-bar.d.ts +0 -3
- package/src/lib/components/leads-bulk-bar.js +29 -29
- package/src/lib/components/leads-bulk-bar.js.map +1 -1
- package/src/lib/components/leads-section.js +120 -68
- package/src/lib/components/leads-section.js.map +1 -1
- package/src/lib/components/linked-deals-card.d.ts +4 -0
- package/src/lib/components/linked-deals-card.js +47 -3
- package/src/lib/components/linked-deals-card.js.map +1 -1
- package/src/lib/components/log-activity-dialog.js +43 -3
- package/src/lib/components/log-activity-dialog.js.map +1 -1
- package/src/lib/components/new-contact-drawer.d.ts +8 -0
- package/src/lib/components/new-contact-drawer.js +108 -6
- package/src/lib/components/new-contact-drawer.js.map +1 -1
- package/src/lib/components/new-lead-drawer.d.ts +9 -2
- package/src/lib/components/new-lead-drawer.js +88 -121
- package/src/lib/components/new-lead-drawer.js.map +1 -1
- package/src/lib/components/picklist-select.d.ts +36 -0
- package/src/lib/components/picklist-select.js +114 -0
- package/src/lib/components/picklist-select.js.map +1 -0
- package/src/lib/components/picklist-values-card.d.ts +34 -0
- package/src/lib/components/{lead-source-values-card.js → picklist-values-card.js} +241 -58
- package/src/lib/components/picklist-values-card.js.map +1 -0
- package/src/lib/components/pipeline-stages-dialog.d.ts +7 -0
- package/src/lib/components/pipeline-stages-dialog.js +26 -4
- package/src/lib/components/pipeline-stages-dialog.js.map +1 -1
- package/src/lib/components/pipelines-dialog.js +15 -6
- package/src/lib/components/pipelines-dialog.js.map +1 -1
- package/src/lib/components/recent-activity-feed.js +20 -5
- package/src/lib/components/recent-activity-feed.js.map +1 -1
- package/src/lib/components/recipes-card.js +4 -2
- package/src/lib/components/recipes-card.js.map +1 -1
- package/src/lib/components/record-tasks-card.js +15 -8
- package/src/lib/components/record-tasks-card.js.map +1 -1
- package/src/lib/components/reports/forecast-card.d.ts +6 -0
- package/src/lib/components/reports/forecast-card.js +101 -3
- package/src/lib/components/reports/forecast-card.js.map +1 -1
- package/src/lib/components/reports/lead-funnel-card.d.ts +12 -0
- package/src/lib/components/reports/lead-funnel-card.js +9 -5
- package/src/lib/components/reports/lead-funnel-card.js.map +1 -1
- package/src/lib/components/reports/lead-source-card.d.ts +23 -0
- package/src/lib/components/reports/lead-source-card.js +318 -0
- package/src/lib/components/reports/lead-source-card.js.map +1 -0
- package/src/lib/components/reports/report-export.js +1 -1
- package/src/lib/components/reports/report-export.js.map +1 -1
- package/src/lib/components/reports-section.js +8 -0
- package/src/lib/components/reports-section.js.map +1 -1
- package/src/lib/components/sharing-rule-drawer.d.ts +5 -0
- package/src/lib/components/sharing-rule-drawer.js +108 -19
- package/src/lib/components/sharing-rule-drawer.js.map +1 -1
- package/src/lib/components/sharing-rules-card.js +11 -3
- package/src/lib/components/sharing-rules-card.js.map +1 -1
- package/src/lib/components/task-cells.d.ts +9 -1
- package/src/lib/components/task-cells.js +9 -4
- package/src/lib/components/task-cells.js.map +1 -1
- package/src/lib/components/task-edit-drawer.js +111 -43
- package/src/lib/components/task-edit-drawer.js.map +1 -1
- package/src/lib/components/tasks-bulk-bar.d.ts +0 -3
- package/src/lib/components/tasks-bulk-bar.js +7 -20
- package/src/lib/components/tasks-bulk-bar.js.map +1 -1
- package/src/lib/components/tasks-calendar.d.ts +3 -0
- package/src/lib/components/tasks-calendar.js +8 -1
- package/src/lib/components/tasks-calendar.js.map +1 -1
- package/src/lib/components/tasks-section.d.ts +3 -3
- package/src/lib/components/tasks-section.js +58 -43
- package/src/lib/components/tasks-section.js.map +1 -1
- package/src/lib/constants/api-routes.d.ts +2 -9
- package/src/lib/constants/api-routes.js +1 -7
- package/src/lib/constants/api-routes.js.map +1 -1
- package/src/lib/constants/bundle-common.d.ts +6 -0
- package/src/lib/constants/bundle-common.js +11 -0
- package/src/lib/constants/bundle-common.js.map +1 -1
- package/src/lib/constants/company-filters.d.ts +14 -0
- package/src/lib/constants/company-filters.js +51 -1
- package/src/lib/constants/company-filters.js.map +1 -1
- package/src/lib/constants/contact-filters.d.ts +2 -0
- package/src/lib/constants/contact-filters.js +18 -1
- package/src/lib/constants/contact-filters.js.map +1 -1
- package/src/lib/constants/deal-filters.d.ts +22 -1
- package/src/lib/constants/deal-filters.js +67 -4
- package/src/lib/constants/deal-filters.js.map +1 -1
- package/src/lib/declarations.console-server.d.ts +3 -0
- package/src/lib/declarations.console-server.js +35 -0
- package/src/lib/declarations.console-server.js.map +1 -1
- package/src/lib/declarations.d.ts +26 -0
- package/src/lib/declarations.js +35 -0
- package/src/lib/declarations.js.map +1 -0
- package/src/lib/declarations.server.d.ts +11 -0
- package/src/lib/declarations.server.js +92 -1
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/hooks/use-company-picklists.d.ts +15 -0
- package/src/lib/hooks/use-company-picklists.js +55 -0
- package/src/lib/hooks/use-company-picklists.js.map +1 -0
- package/src/lib/hooks/use-crm-picklist.d.ts +25 -0
- package/src/lib/hooks/use-crm-picklist.js +53 -0
- package/src/lib/hooks/use-crm-picklist.js.map +1 -0
- package/src/lib/hooks/use-crm-record-names.d.ts +16 -0
- package/src/lib/hooks/use-crm-record-names.js +45 -19
- package/src/lib/hooks/use-crm-record-names.js.map +1 -1
- package/src/lib/hooks/use-crm-task-picklists.d.ts +11 -0
- package/src/lib/hooks/use-crm-task-picklists.js +45 -0
- package/src/lib/hooks/use-crm-task-picklists.js.map +1 -0
- package/src/lib/hooks/use-deals.d.ts +14 -0
- package/src/lib/hooks/use-deals.js +29 -0
- package/src/lib/hooks/use-deals.js.map +1 -1
- package/src/lib/hooks/use-lead-picklists.d.ts +14 -0
- package/src/lib/hooks/use-lead-picklists.js +46 -0
- package/src/lib/hooks/use-lead-picklists.js.map +1 -0
- package/src/lib/hooks/use-lead-source-picklist.d.ts +5 -19
- package/src/lib/hooks/use-lead-source-picklist.js +12 -22
- package/src/lib/hooks/use-lead-source-picklist.js.map +1 -1
- package/src/lib/hooks/use-lead-status-picklist.d.ts +9 -0
- package/src/lib/hooks/use-lead-status-picklist.js +40 -0
- package/src/lib/hooks/use-lead-status-picklist.js.map +1 -0
- package/src/lib/model/companies.d.ts +32 -3
- package/src/lib/model/companies.js +133 -16
- package/src/lib/model/companies.js.map +1 -1
- package/src/lib/model/contact-merge.d.ts +27 -3
- package/src/lib/model/contact-merge.js +81 -7
- package/src/lib/model/contact-merge.js.map +1 -1
- package/src/lib/model/contact-record.d.ts +17 -0
- package/src/lib/model/contact-record.js +15 -1
- package/src/lib/model/contact-record.js.map +1 -1
- package/src/lib/model/contact-update.d.ts +31 -2
- package/src/lib/model/contact-update.js +14 -0
- package/src/lib/model/contact-update.js.map +1 -1
- package/src/lib/model/crm-list-query.d.ts +5 -0
- package/src/lib/model/crm-list-query.js +8 -0
- package/src/lib/model/crm-list-query.js.map +1 -1
- package/src/lib/model/crm-recipes.d.ts +40 -0
- package/src/lib/model/crm-recipes.js +244 -0
- package/src/lib/model/crm-recipes.js.map +1 -0
- package/src/lib/model/crm-record-counts.d.ts +36 -0
- package/src/lib/model/crm-record-counts.js +54 -0
- package/src/lib/model/crm-record-counts.js.map +1 -0
- package/src/lib/model/crm-record-lists.d.ts +23 -0
- package/src/lib/model/crm-record-lists.js +161 -0
- package/src/lib/model/crm-record-lists.js.map +1 -0
- package/src/lib/model/crm-reports.d.ts +73 -3
- package/src/lib/model/crm-reports.js +120 -1
- package/src/lib/model/crm-reports.js.map +1 -1
- package/src/lib/model/crm-sharing.d.ts +21 -3
- package/src/lib/model/crm-sharing.js +27 -6
- package/src/lib/model/crm-sharing.js.map +1 -1
- package/src/lib/model/deal-board-model.d.ts +8 -2
- package/src/lib/model/deal-board-model.js +15 -2
- package/src/lib/model/deal-board-model.js.map +1 -1
- package/src/lib/model/deal-contact-roles.d.ts +45 -0
- package/src/lib/model/deal-contact-roles.js +73 -0
- package/src/lib/model/deal-contact-roles.js.map +1 -0
- package/src/lib/model/deal-form-model.d.ts +56 -0
- package/src/lib/model/deal-form-model.js +79 -7
- package/src/lib/model/deal-form-model.js.map +1 -1
- package/src/lib/model/download-text-file.d.ts +23 -0
- package/src/lib/model/download-text-file.js +34 -0
- package/src/lib/model/download-text-file.js.map +1 -0
- package/src/lib/model/lead-filters.d.ts +36 -2
- package/src/lib/model/lead-filters.js +123 -2
- package/src/lib/model/lead-filters.js.map +1 -1
- package/src/lib/model/lead-source-direction.d.ts +38 -0
- package/src/lib/model/lead-source-direction.js +52 -0
- package/src/lib/model/lead-source-direction.js.map +1 -0
- package/src/lib/model/picklist-values.d.ts +119 -0
- package/src/lib/model/picklist-values.js +256 -0
- package/src/lib/model/picklist-values.js.map +1 -0
- package/src/lib/model/record-facts-disclosed.d.ts +81 -0
- package/src/lib/model/record-facts-disclosed.js +281 -0
- package/src/lib/model/record-facts-disclosed.js.map +1 -0
- package/src/lib/model/record-facts.d.ts +215 -34
- package/src/lib/model/record-facts.js +307 -128
- package/src/lib/model/record-facts.js.map +1 -1
- package/src/lib/model/task-picklists.d.ts +52 -0
- package/src/lib/model/task-picklists.js +83 -0
- package/src/lib/model/task-picklists.js.map +1 -0
- package/src/lib/model/task-routes.d.ts +13 -0
- package/src/lib/model/task-routes.js +23 -4
- package/src/lib/model/task-routes.js.map +1 -1
- package/src/lib/plan-entitlements.js +6 -4
- package/src/lib/plan-entitlements.js.map +1 -1
- package/src/lib/plugin.js +17 -0
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/server/api-v1/contacts.js +220 -14
- package/src/lib/server/api-v1/contacts.js.map +1 -1
- package/src/lib/server/api-v1/crm-activities.js +14 -1
- package/src/lib/server/api-v1/crm-activities.js.map +1 -1
- package/src/lib/server/api-v1/crm-companies.js +123 -14
- package/src/lib/server/api-v1/crm-companies.js.map +1 -1
- package/src/lib/server/api-v1/crm-deals.js +291 -26
- package/src/lib/server/api-v1/crm-deals.js.map +1 -1
- package/src/lib/server/api-v1/crm-leads.js +144 -38
- package/src/lib/server/api-v1/crm-leads.js.map +1 -1
- package/src/lib/server/api-v1/crm-pipelines.d.ts +6 -0
- package/src/lib/server/api-v1/crm-pipelines.js +8 -2
- package/src/lib/server/api-v1/crm-pipelines.js.map +1 -1
- package/src/lib/server/api-v1/crm-tasks.js +75 -33
- package/src/lib/server/api-v1/crm-tasks.js.map +1 -1
- package/src/lib/server/api-v1/openapi.d.ts +0 -11
- package/src/lib/server/api-v1/openapi.js +171 -16
- package/src/lib/server/api-v1/openapi.js.map +1 -1
- package/src/lib/server/assign-contact-owner.d.ts +162 -0
- package/src/lib/server/assign-contact-owner.js +340 -0
- package/src/lib/server/assign-contact-owner.js.map +1 -0
- package/src/lib/server/associate-company-by-domain.d.ts +70 -0
- package/src/lib/server/associate-company-by-domain.js +153 -0
- package/src/lib/server/associate-company-by-domain.js.map +1 -0
- package/src/lib/server/automation-steps.d.ts +139 -0
- package/src/lib/server/automation-steps.js +472 -0
- package/src/lib/server/automation-steps.js.map +1 -0
- package/src/lib/server/capture-contact.js +69 -22
- package/src/lib/server/capture-contact.js.map +1 -1
- package/src/lib/server/capture-host-contact.d.ts +104 -0
- package/src/lib/server/capture-host-contact.js +151 -0
- package/src/lib/server/capture-host-contact.js.map +1 -0
- package/src/lib/server/company-children.d.ts +25 -0
- package/src/lib/server/company-children.js +42 -0
- package/src/lib/server/company-children.js.map +1 -0
- package/src/lib/server/company-delete.d.ts +18 -0
- package/src/lib/server/company-delete.js +44 -21
- package/src/lib/server/company-delete.js.map +1 -1
- package/src/lib/server/contact-email-history.js +21 -19
- package/src/lib/server/contact-email-history.js.map +1 -1
- package/src/lib/server/contact-remove.js +27 -2
- package/src/lib/server/contact-remove.js.map +1 -1
- package/src/lib/server/contact-reports-to.d.ts +61 -0
- package/src/lib/server/contact-reports-to.js +114 -0
- package/src/lib/server/contact-reports-to.js.map +1 -0
- package/src/lib/server/contact-update.d.ts +42 -1
- package/src/lib/server/contact-update.js +204 -23
- package/src/lib/server/contact-update.js.map +1 -1
- package/src/lib/server/convert-host-lead.d.ts +46 -1
- package/src/lib/server/convert-host-lead.js +173 -15
- package/src/lib/server/convert-host-lead.js.map +1 -1
- package/src/lib/server/convert-open-lead.js +12 -5
- package/src/lib/server/convert-open-lead.js.map +1 -1
- package/src/lib/server/crm-record-indexes.d.ts +45 -0
- package/src/lib/server/crm-record-indexes.js +78 -0
- package/src/lib/server/crm-record-indexes.js.map +1 -0
- package/src/lib/server/crm-sharing.d.ts +9 -1
- package/src/lib/server/crm-sharing.js +23 -3
- package/src/lib/server/crm-sharing.js.map +1 -1
- package/src/lib/server/deal-contact-roles.d.ts +27 -0
- package/src/lib/server/deal-contact-roles.js +89 -0
- package/src/lib/server/deal-contact-roles.js.map +1 -0
- package/src/lib/server/lead-convert.d.ts +5 -0
- package/src/lib/server/lead-convert.js +19 -3
- package/src/lib/server/lead-convert.js.map +1 -1
- package/src/lib/server/lead-create.d.ts +17 -2
- package/src/lib/server/lead-create.js +46 -14
- package/src/lib/server/lead-create.js.map +1 -1
- package/src/lib/server/lead-nurturing.d.ts +6 -0
- package/src/lib/server/lead-nurturing.js +57 -20
- package/src/lib/server/lead-nurturing.js.map +1 -1
- package/src/lib/server/lead-picklists.d.ts +35 -0
- package/src/lib/server/lead-picklists.js +52 -0
- package/src/lib/server/lead-picklists.js.map +1 -0
- package/src/lib/server/lead-source-picklist.d.ts +1 -13
- package/src/lib/server/lead-source-picklist.js +6 -33
- package/src/lib/server/lead-source-picklist.js.map +1 -1
- package/src/lib/server/merge-contacts.js +22 -4
- package/src/lib/server/merge-contacts.js.map +1 -1
- package/src/lib/server/person-eraser.d.ts +53 -0
- package/src/lib/server/person-eraser.js +149 -0
- package/src/lib/server/person-eraser.js.map +1 -0
- package/src/lib/server/person-records.d.ts +69 -0
- package/src/lib/server/person-records.js +317 -0
- package/src/lib/server/person-records.js.map +1 -0
- package/src/lib/server/person-refund.d.ts +97 -0
- package/src/lib/server/person-refund.js +221 -0
- package/src/lib/server/person-refund.js.map +1 -0
- package/src/lib/server/picklist-values.d.ts +21 -0
- package/src/lib/server/picklist-values.js +308 -0
- package/src/lib/server/picklist-values.js.map +1 -0
- package/src/lib/server/read-picklist.d.ts +44 -0
- package/src/lib/server/read-picklist.js +65 -0
- package/src/lib/server/read-picklist.js.map +1 -0
- package/src/lib/server/recipe-routes.js +15 -7
- package/src/lib/server/recipe-routes.js.map +1 -1
- package/src/lib/server/record-email-state.js +7 -1
- package/src/lib/server/record-email-state.js.map +1 -1
- package/src/lib/server/record-facts.d.ts +3 -0
- package/src/lib/server/record-facts.js +233 -31
- package/src/lib/server/record-facts.js.map +1 -1
- package/src/lib/server/record-origin.d.ts +27 -0
- package/src/lib/server/record-origin.js +104 -0
- package/src/lib/server/record-origin.js.map +1 -0
- package/src/lib/server/record-timeline.js +15 -2
- package/src/lib/server/record-timeline.js.map +1 -1
- package/src/lib/server/suite-gate.js +4 -3
- package/src/lib/server/suite-gate.js.map +1 -1
- package/src/lib/server/task-routes.js +30 -16
- package/src/lib/server/task-routes.js.map +1 -1
- package/src/lib/server-deal-stage.d.ts +5 -1
- package/src/lib/server-deal-stage.js +10 -5
- package/src/lib/server-deal-stage.js.map +1 -1
- package/src/lib/server.d.ts +8 -3
- package/src/lib/server.js +47 -43
- package/src/lib/server.js.map +1 -1
- package/src/lib/testing/memory-firestore.d.ts +55 -0
- package/src/lib/testing/memory-firestore.js +335 -0
- package/src/lib/testing/memory-firestore.js.map +1 -0
- package/src/lib/testing/stand-in-email-send-index.d.ts +25 -0
- package/src/lib/testing/stand-in-email-send-index.js +49 -0
- package/src/lib/testing/stand-in-email-send-index.js.map +1 -0
- package/src/lib/transfer/activities.d.ts +30 -0
- package/src/lib/transfer/activities.js +132 -0
- package/src/lib/transfer/activities.js.map +1 -0
- package/src/lib/transfer/aliases.d.ts +22 -0
- package/src/lib/transfer/aliases.js +1342 -0
- package/src/lib/transfer/aliases.js.map +1 -0
- package/src/lib/transfer/common.d.ts +222 -0
- package/src/lib/transfer/common.js +832 -0
- package/src/lib/transfer/common.js.map +1 -0
- package/src/lib/transfer/companies.d.ts +43 -0
- package/src/lib/transfer/companies.js +549 -0
- package/src/lib/transfer/companies.js.map +1 -0
- package/src/lib/transfer/contacts.d.ts +31 -0
- package/src/lib/transfer/contacts.js +727 -0
- package/src/lib/transfer/contacts.js.map +1 -0
- package/src/lib/transfer/deals.d.ts +66 -0
- package/src/lib/transfer/deals.js +698 -0
- package/src/lib/transfer/deals.js.map +1 -0
- package/src/lib/transfer/email-templates-package.d.ts +40 -0
- package/src/lib/transfer/email-templates-package.js +76 -0
- package/src/lib/transfer/email-templates-package.js.map +1 -0
- package/src/lib/transfer/email-templates-package.server.d.ts +32 -0
- package/src/lib/transfer/email-templates-package.server.js +176 -0
- package/src/lib/transfer/email-templates-package.server.js.map +1 -0
- package/src/lib/transfer/fields.d.ts +102 -0
- package/src/lib/transfer/fields.js +1755 -0
- package/src/lib/transfer/fields.js.map +1 -0
- package/src/lib/transfer/leads.d.ts +30 -0
- package/src/lib/transfer/leads.js +582 -0
- package/src/lib/transfer/leads.js.map +1 -0
- package/src/lib/transfer/pipelines.d.ts +21 -0
- package/src/lib/transfer/pipelines.js +172 -0
- package/src/lib/transfer/pipelines.js.map +1 -0
- package/src/lib/transfer/register.d.ts +47 -0
- package/src/lib/transfer/register.js +277 -0
- package/src/lib/transfer/register.js.map +1 -0
- package/src/lib/transfer/tasks.d.ts +30 -0
- package/src/lib/transfer/tasks.js +418 -0
- package/src/lib/transfer/tasks.js.map +1 -0
- package/src/lib/transfer/ui.d.ts +20 -0
- package/src/lib/transfer/ui.js +42 -0
- package/src/lib/transfer/ui.js.map +1 -0
- package/src/lib/components/company-import-drawer.d.ts +0 -42
- package/src/lib/components/company-import-drawer.js +0 -99
- package/src/lib/components/company-import-drawer.js.map +0 -1
- package/src/lib/components/contact-import-drawer.d.ts +0 -46
- package/src/lib/components/contact-import-drawer.js +0 -149
- package/src/lib/components/contact-import-drawer.js.map +0 -1
- package/src/lib/components/crm-export-all-button.d.ts +0 -39
- package/src/lib/components/crm-export-all-button.js +0 -98
- package/src/lib/components/crm-export-all-button.js.map +0 -1
- package/src/lib/components/csv-import-drawer.d.ts +0 -139
- package/src/lib/components/csv-import-drawer.js +0 -757
- package/src/lib/components/csv-import-drawer.js.map +0 -1
- package/src/lib/components/deal-import-drawer.d.ts +0 -24
- package/src/lib/components/deal-import-drawer.js +0 -83
- package/src/lib/components/deal-import-drawer.js.map +0 -1
- package/src/lib/components/lead-import-drawer.d.ts +0 -27
- package/src/lib/components/lead-import-drawer.js +0 -118
- package/src/lib/components/lead-import-drawer.js.map +0 -1
- package/src/lib/components/lead-source-values-card.d.ts +0 -27
- package/src/lib/components/lead-source-values-card.js.map +0 -1
- package/src/lib/components/task-import-drawer.d.ts +0 -24
- package/src/lib/components/task-import-drawer.js +0 -82
- package/src/lib/components/task-import-drawer.js.map +0 -1
- package/src/lib/model/companies-csv.d.ts +0 -36
- package/src/lib/model/companies-csv.js +0 -38
- package/src/lib/model/companies-csv.js.map +0 -1
- package/src/lib/model/contacts-csv.d.ts +0 -54
- package/src/lib/model/contacts-csv.js +0 -67
- package/src/lib/model/contacts-csv.js.map +0 -1
- package/src/lib/model/crm-company-import.d.ts +0 -154
- package/src/lib/model/crm-company-import.js +0 -331
- package/src/lib/model/crm-company-import.js.map +0 -1
- package/src/lib/model/crm-csv.d.ts +0 -239
- package/src/lib/model/crm-csv.js +0 -467
- package/src/lib/model/crm-csv.js.map +0 -1
- package/src/lib/model/crm-deal-import.d.ts +0 -112
- package/src/lib/model/crm-deal-import.js +0 -263
- package/src/lib/model/crm-deal-import.js.map +0 -1
- package/src/lib/model/crm-import.d.ts +0 -250
- package/src/lib/model/crm-import.js +0 -467
- package/src/lib/model/crm-import.js.map +0 -1
- package/src/lib/model/crm-lead-import.d.ts +0 -138
- package/src/lib/model/crm-lead-import.js +0 -446
- package/src/lib/model/crm-lead-import.js.map +0 -1
- package/src/lib/model/crm-task-import.d.ts +0 -94
- package/src/lib/model/crm-task-import.js +0 -275
- package/src/lib/model/crm-task-import.js.map +0 -1
- package/src/lib/model/deals-csv.d.ts +0 -32
- package/src/lib/model/deals-csv.js +0 -36
- package/src/lib/model/deals-csv.js.map +0 -1
- package/src/lib/model/import-column-shapes.d.ts +0 -36
- package/src/lib/model/import-column-shapes.js +0 -76
- package/src/lib/model/import-column-shapes.js.map +0 -1
- package/src/lib/model/lead-source-values.d.ts +0 -82
- package/src/lib/model/lead-source-values.js +0 -149
- package/src/lib/model/lead-source-values.js.map +0 -1
- package/src/lib/model/leads-csv.d.ts +0 -40
- package/src/lib/model/leads-csv.js +0 -40
- package/src/lib/model/leads-csv.js.map +0 -1
- package/src/lib/model/tasks-csv.d.ts +0 -28
- package/src/lib/model/tasks-csv.js +0 -28
- package/src/lib/model/tasks-csv.js.map +0 -1
- package/src/lib/server/companies-import.d.ts +0 -57
- package/src/lib/server/companies-import.js +0 -242
- package/src/lib/server/companies-import.js.map +0 -1
- package/src/lib/server/contacts-import.d.ts +0 -109
- package/src/lib/server/contacts-import.js +0 -393
- package/src/lib/server/contacts-import.js.map +0 -1
- package/src/lib/server/deals-import.d.ts +0 -76
- package/src/lib/server/deals-import.js +0 -248
- package/src/lib/server/deals-import.js.map +0 -1
- package/src/lib/server/export-route.d.ts +0 -60
- package/src/lib/server/export-route.js +0 -447
- package/src/lib/server/export-route.js.map +0 -1
- package/src/lib/server/import-context.d.ts +0 -87
- package/src/lib/server/import-context.js +0 -182
- package/src/lib/server/import-context.js.map +0 -1
- package/src/lib/server/lead-source-values.d.ts +0 -50
- package/src/lib/server/lead-source-values.js +0 -191
- package/src/lib/server/lead-source-values.js.map +0 -1
- package/src/lib/server/leads-import.d.ts +0 -84
- package/src/lib/server/leads-import.js +0 -367
- package/src/lib/server/leads-import.js.map +0 -1
- package/src/lib/server/tasks-import.d.ts +0 -54
- package/src/lib/server/tasks-import.js +0 -160
- package/src/lib/server/tasks-import.js.map +0 -1
|
@@ -16,18 +16,20 @@
|
|
|
16
16
|
*/ 'use client';
|
|
17
17
|
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
18
18
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
19
|
-
import { CRM_COLLECTIONS, findOrgMember, crmMemberPickerLabel } from "@aglyn/aglyn";
|
|
19
|
+
import { CRM_COLLECTIONS, crmPicklistActiveValues, crmPicklistDefaultLabel, crmPicklistOptions, crmPicklistShownLabel, crmPicklistValueByLabel, findOrgMember, crmMemberPickerLabel } from "@aglyn/aglyn";
|
|
20
20
|
import { useConfirmationContext } from "@aglyn/shared-ui-jsx";
|
|
21
21
|
import { useSnackbar } from "@aglyn/shared-ui-snackstack";
|
|
22
22
|
import { useFirestore, useUser, writeGuardedBySeed } from "@aglyn/tenant-feature-instance";
|
|
23
|
-
import { Button, Drawer, MenuItem, Stack, TextField, Typography } from "@mui/material";
|
|
23
|
+
import { Autocomplete, Button, Drawer, MenuItem, Stack, TextField, Typography } from "@mui/material";
|
|
24
24
|
import { deleteDoc, doc } from "firebase/firestore";
|
|
25
25
|
import { useState } from "react";
|
|
26
26
|
import { useOrgMemberDirectory } from "../hooks/use-org-member-directory.js";
|
|
27
27
|
import { refreshCrmNextActivity } from "../model/next-activity-api.js";
|
|
28
28
|
import { saveCrmTask } from "../model/task-api.js";
|
|
29
29
|
import { CRM_TASK_NOTES_MAX, CRM_TASK_TITLE_MAX, crmTaskFieldsOf } from "../model/task-routes.js";
|
|
30
|
-
import {
|
|
30
|
+
import { dueAtToLocalInput, localInputToDueAt } from "../model/task-views.js";
|
|
31
|
+
import { useCrmPicklist } from "../hooks/use-crm-picklist.js";
|
|
32
|
+
import { useCrmTaskPicklists } from "../hooks/use-crm-task-picklists.js";
|
|
31
33
|
import { useCrmOrgMount } from "../hooks/use-crm-org-mount.js";
|
|
32
34
|
import { useCrmScope } from "../hooks/use-crm-scope.js";
|
|
33
35
|
import { isOrgTask } from "../model/task-scope.js";
|
|
@@ -56,8 +58,43 @@ import TaskSnoozeMenu from "./task-snooze-menu.js";
|
|
|
56
58
|
});
|
|
57
59
|
}
|
|
58
60
|
TaskEditDrawer.displayName = 'TaskEditDrawer';
|
|
61
|
+
/**
|
|
62
|
+
* One of a task's semantic picklists as a select (AGL-3517): the org's
|
|
63
|
+
* active values in its order — narrowed by `include` when only some may be
|
|
64
|
+
* picked — and the task's own label when the list would not offer it, so
|
|
65
|
+
* the field shows what is stored. No "None": a task always has a status, a
|
|
66
|
+
* priority and a type.
|
|
67
|
+
*/ function TaskPicklistField(props) {
|
|
68
|
+
const { label, picklist, value, stored, include, onChange, disabled, helperText } = props;
|
|
69
|
+
const options = crmPicklistOptions(picklist, stored || value).filter((option)=>{
|
|
70
|
+
var _crmPicklistValueByLabel;
|
|
71
|
+
return option.label === value || option.label === stored || !include || include((_crmPicklistValueByLabel = crmPicklistValueByLabel(picklist, option.label)) == null ? void 0 : _crmPicklistValueByLabel.meaning);
|
|
72
|
+
});
|
|
73
|
+
return /*#__PURE__*/ _jsx(TextField, {
|
|
74
|
+
select: true,
|
|
75
|
+
label: label,
|
|
76
|
+
value: options.some((option)=>option.label === value) ? value : '',
|
|
77
|
+
onChange: (event)=>{
|
|
78
|
+
var _ref;
|
|
79
|
+
var _crmPicklistValueByLabel;
|
|
80
|
+
const next = String(event.target.value);
|
|
81
|
+
onChange(next, (_ref = (_crmPicklistValueByLabel = crmPicklistValueByLabel(picklist, next)) == null ? void 0 : _crmPicklistValueByLabel.meaning) != null ? _ref : null);
|
|
82
|
+
},
|
|
83
|
+
size: "small",
|
|
84
|
+
disabled: disabled,
|
|
85
|
+
helperText: helperText,
|
|
86
|
+
sx: {
|
|
87
|
+
flex: 1
|
|
88
|
+
},
|
|
89
|
+
children: options.map((option)=>/*#__PURE__*/ _jsx(MenuItem, {
|
|
90
|
+
value: option.label,
|
|
91
|
+
disabled: (option.inactive || option.unlisted) && option.label !== (stored || value),
|
|
92
|
+
children: option.inactive ? `${option.label} (inactive)` : option.unlisted ? `${option.label} (not in the list)` : option.label
|
|
93
|
+
}, option.label))
|
|
94
|
+
});
|
|
95
|
+
}
|
|
59
96
|
function TaskForm(props) {
|
|
60
|
-
var _ref, _task_hostId, _ref1, _ref2, _directory_error;
|
|
97
|
+
var _ref, _task_hostId, _ref1, _ref2, _ref3, _ref4, _ref5, _directory_error;
|
|
61
98
|
const { onClose, hostId, org, orgId, scope, readTokens, task, prefill, seed, onSaved } = props;
|
|
62
99
|
const firestore = useFirestore();
|
|
63
100
|
const { data: user } = useUser();
|
|
@@ -110,6 +147,22 @@ function TaskForm(props) {
|
|
|
110
147
|
});
|
|
111
148
|
const assignee = findOrgMember(directory.members, fields.assigneeUid);
|
|
112
149
|
const [busy, setBusy] = useState(false);
|
|
150
|
+
/*
|
|
151
|
+
* TYPE, PRIORITY AND STATUS (AGL-3517) are the org's picklists, each value
|
|
152
|
+
* meaning one of the task's `kind`, `priority` or `status`. What a field
|
|
153
|
+
* shows is the task's own label, else — on a new task whose button named
|
|
154
|
+
* no meaning — the list's default, else the first active value of the
|
|
155
|
+
* meaning it holds. The save sends the label shown, and the route stores
|
|
156
|
+
* the meaning its value carries beside it.
|
|
157
|
+
*/ const picklists = useCrmTaskPicklists(orgId);
|
|
158
|
+
const subjects = useCrmPicklist('taskSubject', orgId);
|
|
159
|
+
const isNew = !task;
|
|
160
|
+
const shown = (picklist, label, meaning, prefilled)=>label || (isNew && !prefilled ? crmPicklistDefaultLabel(picklist) : null) || crmPicklistShownLabel(picklist, meaning, null);
|
|
161
|
+
const typeLabel = shown(picklists.type, fields.typeLabel, fields.kind, Boolean(prefill == null ? void 0 : prefill.kind));
|
|
162
|
+
const priorityLabel = shown(picklists.priority, fields.priorityLabel, fields.priority, Boolean(prefill == null ? void 0 : prefill.priority));
|
|
163
|
+
const status = (task == null ? void 0 : task.status) === 'done' ? 'done' : 'open';
|
|
164
|
+
const statusLabel = shown(picklists.status, fields.statusLabel, status, false);
|
|
165
|
+
const subjectOptions = crmPicklistActiveValues(subjects.picklist).map((value)=>value.label);
|
|
113
166
|
const [error, setError] = useState(null);
|
|
114
167
|
const set = (key, value)=>setFields((prev)=>_extends({}, prev, {
|
|
115
168
|
[key]: value
|
|
@@ -147,7 +200,10 @@ function TaskForm(props) {
|
|
|
147
200
|
taskId: task.$id
|
|
148
201
|
} : {}, {
|
|
149
202
|
task: _extends({}, fields, {
|
|
150
|
-
title: fields.title.trim()
|
|
203
|
+
title: fields.title.trim(),
|
|
204
|
+
typeLabel,
|
|
205
|
+
priorityLabel,
|
|
206
|
+
statusLabel
|
|
151
207
|
})
|
|
152
208
|
}));
|
|
153
209
|
enqueueSnackbar(task ? 'Task saved' : result.notified ? 'Task created and the assignee notified' : 'Task created', {
|
|
@@ -190,7 +246,8 @@ function TaskForm(props) {
|
|
|
190
246
|
confirmationButtonProps: {
|
|
191
247
|
color: 'error'
|
|
192
248
|
}
|
|
193
|
-
})
|
|
249
|
+
})// `confirm` resolves with no value and REJECTS on cancel (AGL-3509).
|
|
250
|
+
.then(()=>true).catch(()=>false);
|
|
194
251
|
if (!confirmed) return;
|
|
195
252
|
setBusy(true);
|
|
196
253
|
try {
|
|
@@ -243,53 +300,64 @@ function TaskForm(props) {
|
|
|
243
300
|
color: "text.secondary",
|
|
244
301
|
children: isOrgTask(task) ? 'Filed with the organization — no site.' : `Filed from ${mount.siteName(task.hostId)}.`
|
|
245
302
|
}) : null,
|
|
246
|
-
/*#__PURE__*/ _jsx(
|
|
247
|
-
|
|
248
|
-
value: fields.title,
|
|
249
|
-
onChange: (event)=>set('title', event.target.value),
|
|
303
|
+
/*#__PURE__*/ _jsx(Autocomplete, {
|
|
304
|
+
freeSolo: true,
|
|
250
305
|
size: "small",
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
306
|
+
options: subjectOptions,
|
|
307
|
+
inputValue: fields.title,
|
|
308
|
+
onInputChange: (_event, value)=>set('title', value),
|
|
309
|
+
renderInput: (params)=>/*#__PURE__*/ _jsx(TextField, _extends({}, params, {
|
|
310
|
+
label: "Subject",
|
|
311
|
+
size: "small",
|
|
312
|
+
autoFocus: true,
|
|
313
|
+
required: true,
|
|
314
|
+
slotProps: _extends({}, params.slotProps, {
|
|
315
|
+
htmlInput: _extends({}, params.slotProps.htmlInput, {
|
|
316
|
+
maxLength: CRM_TASK_TITLE_MAX
|
|
317
|
+
})
|
|
318
|
+
})
|
|
319
|
+
}))
|
|
258
320
|
}),
|
|
259
321
|
/*#__PURE__*/ _jsxs(Stack, {
|
|
260
322
|
direction: "row",
|
|
261
323
|
spacing: 1,
|
|
262
324
|
children: [
|
|
263
|
-
/*#__PURE__*/ _jsx(
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
value:
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
children: CRM_TASK_KIND_LABELS[kind]
|
|
275
|
-
}, kind))
|
|
325
|
+
/*#__PURE__*/ _jsx(TaskPicklistField, {
|
|
326
|
+
label: "Type",
|
|
327
|
+
picklist: picklists.type,
|
|
328
|
+
value: typeLabel,
|
|
329
|
+
stored: (_ref1 = task == null ? void 0 : task.typeLabel) != null ? _ref1 : undefined,
|
|
330
|
+
onChange: (label, meaning)=>setFields((prev)=>_extends({}, prev, {
|
|
331
|
+
typeLabel: label
|
|
332
|
+
}, meaning ? {
|
|
333
|
+
kind: meaning
|
|
334
|
+
} : {})),
|
|
335
|
+
disabled: busy
|
|
276
336
|
}),
|
|
277
|
-
/*#__PURE__*/ _jsx(
|
|
278
|
-
select: true,
|
|
337
|
+
/*#__PURE__*/ _jsx(TaskPicklistField, {
|
|
279
338
|
label: "Priority",
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
}, priority))
|
|
339
|
+
picklist: picklists.priority,
|
|
340
|
+
value: priorityLabel,
|
|
341
|
+
stored: (_ref2 = task == null ? void 0 : task.priorityLabel) != null ? _ref2 : undefined,
|
|
342
|
+
onChange: (label, meaning)=>setFields((prev)=>_extends({}, prev, {
|
|
343
|
+
priorityLabel: label
|
|
344
|
+
}, meaning ? {
|
|
345
|
+
priority: meaning
|
|
346
|
+
} : {})),
|
|
347
|
+
disabled: busy
|
|
290
348
|
})
|
|
291
349
|
]
|
|
292
350
|
}),
|
|
351
|
+
/*#__PURE__*/ _jsx(TaskPicklistField, {
|
|
352
|
+
label: "Status",
|
|
353
|
+
picklist: picklists.status,
|
|
354
|
+
value: statusLabel,
|
|
355
|
+
stored: (_ref3 = task == null ? void 0 : task.statusLabel) != null ? _ref3 : undefined,
|
|
356
|
+
include: (meaning)=>meaning === status,
|
|
357
|
+
onChange: (label)=>set('statusLabel', label),
|
|
358
|
+
disabled: busy,
|
|
359
|
+
helperText: status === 'done' ? 'Untick the task in the list to reopen it.' : 'Tick the task in the list to complete it.'
|
|
360
|
+
}),
|
|
293
361
|
/*#__PURE__*/ _jsxs(Stack, {
|
|
294
362
|
direction: "row",
|
|
295
363
|
spacing: 1,
|
|
@@ -369,7 +437,7 @@ function TaskForm(props) {
|
|
|
369
437
|
// a save writes their uid. One the roster does not hold is kept as
|
|
370
438
|
// its own option: a controlled select whose value is absent from
|
|
371
439
|
// its options renders empty, which reads as unassigned.
|
|
372
|
-
value: (
|
|
440
|
+
value: (_ref4 = (_ref5 = assignee == null ? void 0 : assignee.uid) != null ? _ref5 : fields.assigneeUid) != null ? _ref4 : '',
|
|
373
441
|
onChange: (event)=>set('assigneeUid', event.target.value || null),
|
|
374
442
|
size: "small",
|
|
375
443
|
disabled: directory.loading,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/task-edit-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 { CRM_COLLECTIONS, findOrgMember, crmMemberPickerLabel } from '@aglyn/aglyn'\nimport { useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n useFirestore,\n useUser,\n writeGuardedBySeed,\n} from '@aglyn/tenant-feature-instance'\nimport {\n Button,\n Drawer,\n MenuItem,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { deleteDoc, doc } from 'firebase/firestore'\nimport { useMemo, useState } from 'react'\nimport type { CrmTaskRow } from '../hooks/use-crm-tasks'\nimport { useOrgMemberDirectory } from '../hooks/use-org-member-directory'\nimport { refreshCrmNextActivity } from '../model/next-activity-api'\nimport { saveCrmTask } from '../model/task-api'\nimport {\n CRM_TASK_NOTES_MAX,\n CRM_TASK_TITLE_MAX,\n type CrmTaskFields,\n crmTaskFieldsOf,\n} from '../model/task-routes'\nimport {\n CRM_TASK_KIND_LABELS,\n CRM_TASK_KINDS,\n CRM_TASK_PRIORITIES,\n CRM_TASK_PRIORITY_LABELS,\n dueAtToLocalInput,\n localInputToDueAt,\n} from '../model/task-views'\nimport { useCrmOrgMount } from '../hooks/use-crm-org-mount'\nimport { useCrmScope } from '../hooks/use-crm-scope'\nimport { isOrgTask } from '../model/task-scope'\nimport CrmRecordPicker from './crm-record-picker'\nimport { CrmSitePicker } from './crm-site-picker'\nimport TaskSnoozeMenu from './task-snooze-menu'\n\nexport interface TaskEditDrawerProps {\n open: boolean\n onClose: () => void\n /**\n * The site the drawer is opened under, or `null` at the organization\n * level (AGL-2630), where a NEW task asks which site it is filed from —\n * or whether it is the organization's own, with no site (AGL-2637) —\n * and an edit keeps the task's own.\n */\n hostId: string | null\n org?: Record<string, unknown> | null\n orgId: string | null | undefined\n scope: readonly [string, string] | null\n /** The reader's tokens, or `null` at the organization level — no clause. */\n readTokens: readonly string[] | null\n /** The task being edited; absent while creating. */\n task?: CrmTaskRow | null\n /**\n * What a \"New task\" button pressed on a record fills in before the person\n * types: the record's own id, so the link is made for them.\n */\n prefill?: Partial<CrmTaskFields>\n /**\n * The verdict of the listener the edited row came from. An edit is refused\n * when the seed is unconfirmed, for the reason the contacts drawer refuses:\n * every field on this form is written back on save, and a form seeded from\n * a stale cache writes the stale values over newer ones.\n */\n seed?: { fromCache: boolean; unreadable: boolean }\n onSaved?: (taskId: string) => void\n}\n\n/**\n * Create or edit one task (AGL-2599).\n *\n * A drawer on the list, the console's standing shape for creating — the\n * list stays where it was and the form slides over it. The same drawer edits,\n * because a task is small enough that \"the record's own page\" would be this\n * form with more air around it.\n *\n * The form is mounted only while the drawer is open and keyed by the task,\n * so opening a different task or \"New task\" after an edit starts from that\n * task's values and not from whatever was last typed.\n */\nexport function TaskEditDrawer(props: TaskEditDrawerProps) {\n const { open, onClose, task } = props\n return (\n <Drawer anchor=\"right\" open={open} onClose={onClose}>\n {open ? <TaskForm key={task?.$id ?? 'new'} {...props} /> : null}\n </Drawer>\n )\n}\nTaskEditDrawer.displayName = 'TaskEditDrawer'\n\nfunction TaskForm(props: TaskEditDrawerProps) {\n const { onClose, hostId, org, orgId, scope, readTokens, task, prefill, seed, onSaved } =\n props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const directory = useOrgMemberDirectory(orgId)\n const mount = useCrmOrgMount()\n // The viewing group under a site, for a linked contact's facet name; null\n // at the organization level, where the picker names each contact through\n // its own holder. The site the route files a NEW task from is the mounted\n // one or the picked one — or none, when the reader files the task with\n // the organization itself; an edit goes back to the task's own.\n const { consentGroup, createHostId } = useCrmScope({ hostId, org })\n const groupId = consentGroup?.groupId ?? null\n const [noSite, setNoSite] = useState(false)\n const filedFrom = task ? (task.hostId ?? null) : noSite ? null : createHostId\n /*\n * Whether the form knows where the task goes: an edit always does (the\n * document does), and a create does once a site is picked or the\n * organization is. `filedFrom` alone cannot say — it is null for both\n * \"not yet picked\" and \"the organization's own\".\n */\n const filed = Boolean(task) || noSite || Boolean(createHostId)\n /*\n * Which level the route runs at: the site the drawer is mounted under, or\n * the organization, with the site a NEW task is filed from named beside\n * it. An edit names no site — the route reads the task's own.\n */\n const routeScope = hostId\n ? { hostId }\n : orgId\n ? { orgId, ...(task || !filedFrom ? {} : { hostId: filedFrom }) }\n : null\n\n const [fields, setFields] = useState<CrmTaskFields>(() => {\n const base = crmTaskFieldsOf(task ?? {})\n if (task) return base\n // A new task is the creator's own unless a button said otherwise: it\n // lands in \"My tasks\", and assigning it elsewhere is a choice made in\n // the picker rather than a default that notifies a teammate by accident.\n // Its reminder is left UNSAID (AGL-2659) — the field shows the due time\n // and the route decides — until the person touches it.\n return { ...base, remindAtMs: undefined, assigneeUid: user?.uid ?? null, ...prefill }\n })\n const assignee = findOrgMember(directory.members, fields.assigneeUid)\n const [busy, setBusy] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const set = <K extends keyof CrmTaskFields>(key: K, value: CrmTaskFields[K]) =>\n setFields((prev) => ({ ...prev, [key]: value }))\n /*\n * The due date, and the reminder with it (AGL-2659): a reminder left\n * unsaid shows the due time and needs no help, and one a person set to\n * the due time follows it here as the route would follow it — so what\n * the field shows after the change is what the save will store.\n */\n const setDue = (dueAtMs: number | null) =>\n setFields((prev) => ({\n ...prev,\n dueAtMs,\n ...(typeof prev.remindAtMs === 'number' && prev.remindAtMs === prev.dueAtMs\n ? { remindAtMs: dueAtMs }\n : {}),\n }))\n /** What the reminder field shows: the due time while nothing is said. */\n const remindAtMs = fields.remindAtMs === undefined ? fields.dueAtMs : fields.remindAtMs\n const reminderHelp =\n remindAtMs === null\n ? 'No reminder.'\n : remindAtMs === fields.dueAtMs\n ? 'At the due time — the assignee is reminded in the console and by email.'\n : 'The assignee is reminded in the console and by email.'\n\n const save = async () => {\n if (!fields.title.trim()) {\n setError('A task needs a title.')\n return\n }\n if (!filed) {\n setError('Pick the site this task is filed from, or file it with the organization.')\n return\n }\n if (!routeScope) {\n setError('The organization is still loading. Try again in a moment.')\n return\n }\n setBusy(true)\n setError(null)\n try {\n const write = async () => {\n const result = await saveCrmTask(user, {\n ...routeScope,\n ...(task ? { taskId: task.$id } : {}),\n task: { ...fields, title: fields.title.trim() },\n })\n enqueueSnackbar(\n task\n ? 'Task saved'\n : result.notified\n ? 'Task created and the assignee notified'\n : 'Task created',\n { variant: 'success' },\n )\n onSaved?.(result.taskId)\n onClose()\n }\n if (task) {\n const verdict = await writeGuardedBySeed(\n {\n subject: 'task',\n fromCache: seed?.fromCache ?? false,\n unreadable: seed?.unreadable ?? false,\n },\n write,\n )\n if (!verdict.ok) {\n enqueueSnackbar(verdict.message ?? 'The task could not be saved.', {\n variant: 'warning',\n })\n }\n } else {\n await write()\n }\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : 'The task could not be saved.')\n } finally {\n setBusy(false)\n }\n }\n\n /*\n * Deleting is client-direct: it has no side effect outside the document,\n * and the rules gate it on the same predicate as any other CRM write.\n */\n const remove = async () => {\n if (!task || !scope) return\n const confirmed = await confirm({\n title: 'Delete this task?',\n description: `\"${task.title}\" is removed for everyone who can see it. Completed tasks can be kept instead — tick it done.`,\n confirmationText: 'Delete',\n confirmationButtonProps: { color: 'error' },\n })\n if (!confirmed) return\n setBusy(true)\n try {\n await deleteDoc(\n doc(firestore, scope[0], scope[1], CRM_COLLECTIONS.tasks, task.$id),\n )\n enqueueSnackbar('Task deleted', { variant: 'success' })\n onClose()\n // A client-direct write: the records it named are told (AGL-2661).\n await refreshCrmNextActivity(user, routeScope, [task])\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : 'The task could not be deleted.')\n } finally {\n setBusy(false)\n }\n }\n\n return (\n <Stack\n component=\"form\"\n spacing={2}\n sx={{ width: { xs: '100vw', sm: 420 }, p: 3 }}\n onSubmit={(event: { preventDefault: () => void }) => {\n event.preventDefault()\n void save()\n }}\n >\n <Typography variant=\"h6\">{task ? 'Edit task' : 'New task'}</Typography>\n {/* Only a new task at the organization level asks — see `filedFrom`. */}\n {task ? null : (\n <CrmSitePicker\n hostId={hostId}\n disabled={busy}\n noSite={{\n label: 'This organization (no site)',\n helperText:\n 'A task of the organization itself, listed from the ' +\n 'organization hub and from every site. Completing it runs no ' +\n \"site's automations.\",\n picked: noSite,\n onPick: setNoSite,\n }}\n />\n )}\n {/* An edit at the organization level says where the task is filed. */}\n {task && mount ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {isOrgTask(task)\n ? 'Filed with the organization — no site.'\n : `Filed from ${mount.siteName(task.hostId as string)}.`}\n </Typography>\n ) : null}\n <TextField\n label=\"Title\"\n value={fields.title}\n onChange={(event) => set('title', event.target.value)}\n size=\"small\"\n autoFocus\n required\n slotProps={{ htmlInput: { maxLength: CRM_TASK_TITLE_MAX } }}\n />\n <Stack direction=\"row\" spacing={1}>\n <TextField\n select\n label=\"Kind\"\n value={fields.kind}\n onChange={(event) => set('kind', event.target.value as CrmTaskFields['kind'])}\n size=\"small\"\n sx={{ flex: 1 }}\n >\n {CRM_TASK_KINDS.map((kind) => (\n <MenuItem key={kind} value={kind}>\n {CRM_TASK_KIND_LABELS[kind]}\n </MenuItem>\n ))}\n </TextField>\n <TextField\n select\n label=\"Priority\"\n value={fields.priority}\n onChange={(event) =>\n set('priority', event.target.value as CrmTaskFields['priority'])\n }\n size=\"small\"\n sx={{ flex: 1 }}\n >\n {CRM_TASK_PRIORITIES.map((priority) => (\n <MenuItem key={priority} value={priority}>\n {CRM_TASK_PRIORITY_LABELS[priority]}\n </MenuItem>\n ))}\n </TextField>\n </Stack>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'flex-start' }}>\n <TextField\n label=\"Due\"\n type=\"datetime-local\"\n value={dueAtToLocalInput(fields.dueAtMs)}\n onChange={(event) => setDue(localInputToDueAt(event.target.value))}\n size=\"small\"\n slotProps={{ inputLabel: { shrink: true } }}\n helperText=\"Leave empty for a task with no due date.\"\n sx={{ flex: 1 }}\n />\n {/*\n A stored task is snoozed in place — the one write lands and the\n field follows it, so a later Save carries the same date. A task\n that does not exist yet has nothing to write, so the same menu\n fills the field and the save is the write.\n */}\n <TaskSnoozeMenu\n variant=\"button\"\n dueAtMs={fields.dueAtMs}\n remindAtMs={remindAtMs}\n disabled={busy}\n target={\n task && scope\n ? { write: { scope, taskId: task.$id } }\n : { pick: setDue }\n }\n onSnoozed={setDue}\n />\n </Stack>\n {/*\n The reminder (AGL-2659), beside the due date it follows by default.\n Its own field rather than a switch, because \"remind me an hour\n before\" is the second thing a person wants after \"remind me\".\n */}\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'flex-start' }}>\n <TextField\n label=\"Remind me\"\n type=\"datetime-local\"\n value={dueAtToLocalInput(remindAtMs)}\n onChange={(event) => set('remindAtMs', localInputToDueAt(event.target.value))}\n size=\"small\"\n slotProps={{ inputLabel: { shrink: true } }}\n helperText={reminderHelp}\n sx={{ flex: 1 }}\n />\n <Button\n size=\"small\"\n onClick={() => set('remindAtMs', null)}\n disabled={busy || remindAtMs === null}\n >\n {'No reminder'}\n </Button>\n </Stack>\n <TextField\n select\n label=\"Assignee\"\n // The stored assignee resolved to a member — by uid, or by an\n // address the roster has — so the picker highlights the person and\n // a save writes their uid. One the roster does not hold is kept as\n // its own option: a controlled select whose value is absent from\n // its options renders empty, which reads as unassigned.\n value={assignee?.uid ?? fields.assigneeUid ?? ''}\n onChange={(event) => set('assigneeUid', event.target.value || null)}\n size=\"small\"\n disabled={directory.loading}\n helperText={\n directory.error ??\n (fields.assigneeUid && fields.assigneeUid !== user?.uid\n ? 'They will be notified when you save.'\n : undefined)\n }\n >\n <MenuItem value=\"\">{'Unassigned'}</MenuItem>\n {fields.assigneeUid && !assignee ? (\n <MenuItem value={fields.assigneeUid}>{directory.nameOf(fields.assigneeUid)}</MenuItem>\n ) : null}\n {directory.members.map((member) => (\n <MenuItem key={member.uid} value={member.uid}>\n {crmMemberPickerLabel(member)}\n {member.uid === user?.uid ? ' (you)' : ''}\n </MenuItem>\n ))}\n </TextField>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Linked to'}\n </Typography>\n <CrmRecordPicker\n kind=\"contact\"\n scope={scope}\n readTokens={readTokens}\n groupId={groupId}\n org={org}\n value={fields.contactId}\n onChange={(id) => set('contactId', id)}\n disabled={busy}\n />\n <CrmRecordPicker\n kind=\"company\"\n scope={scope}\n readTokens={readTokens}\n groupId={groupId}\n org={org}\n value={fields.companyId}\n onChange={(id) => set('companyId', id)}\n disabled={busy}\n />\n <CrmRecordPicker\n kind=\"deal\"\n scope={scope}\n readTokens={readTokens}\n groupId={groupId}\n org={org}\n value={fields.dealId}\n onChange={(id) => set('dealId', id)}\n disabled={busy}\n />\n <TextField\n label=\"Notes\"\n value={fields.notes}\n onChange={(event) => set('notes', event.target.value)}\n size=\"small\"\n multiline\n minRows={3}\n slotProps={{ htmlInput: { maxLength: CRM_TASK_NOTES_MAX } }}\n />\n {error ? (\n <Typography variant=\"body2\" color=\"error\">\n {error}\n </Typography>\n ) : null}\n <Stack direction=\"row\" spacing={1} sx={{ justifyContent: 'flex-end' }}>\n {task ? (\n <Button\n color=\"error\"\n onClick={() => void remove()}\n disabled={busy}\n sx={{ mr: 'auto' }}\n >\n {'Delete'}\n </Button>\n ) : null}\n <Button onClick={onClose} disabled={busy}>\n {'Cancel'}\n </Button>\n <Button type=\"submit\" variant=\"contained\" disabled={busy || !filed}>\n {task ? 'Save' : 'Create task'}\n </Button>\n </Stack>\n </Stack>\n )\n}\nTaskForm.displayName = 'TaskForm'\n\nexport default TaskEditDrawer\n"],"names":["CRM_COLLECTIONS","findOrgMember","crmMemberPickerLabel","useConfirmationContext","useSnackbar","useFirestore","useUser","writeGuardedBySeed","Button","Drawer","MenuItem","Stack","TextField","Typography","deleteDoc","doc","useState","useOrgMemberDirectory","refreshCrmNextActivity","saveCrmTask","CRM_TASK_NOTES_MAX","CRM_TASK_TITLE_MAX","crmTaskFieldsOf","CRM_TASK_KIND_LABELS","CRM_TASK_KINDS","CRM_TASK_PRIORITIES","CRM_TASK_PRIORITY_LABELS","dueAtToLocalInput","localInputToDueAt","useCrmOrgMount","useCrmScope","isOrgTask","CrmRecordPicker","CrmSitePicker","TaskSnoozeMenu","TaskEditDrawer","props","open","onClose","task","anchor","TaskForm","$id","displayName","assignee","directory","hostId","org","orgId","scope","readTokens","prefill","seed","onSaved","firestore","data","user","enqueueSnackbar","confirm","mount","consentGroup","createHostId","groupId","noSite","setNoSite","filedFrom","filed","Boolean","routeScope","fields","setFields","base","remindAtMs","undefined","assigneeUid","uid","members","busy","setBusy","error","setError","set","key","value","prev","setDue","dueAtMs","reminderHelp","save","title","trim","write","result","taskId","notified","variant","verdict","subject","fromCache","unreadable","ok","message","cause","Error","remove","confirmed","description","confirmationText","confirmationButtonProps","color","tasks","component","spacing","sx","width","xs","sm","p","onSubmit","event","preventDefault","disabled","label","helperText","picked","onPick","siteName","onChange","target","size","autoFocus","required","slotProps","htmlInput","maxLength","direction","select","kind","flex","map","priority","alignItems","type","inputLabel","shrink","pick","onSnoozed","onClick","loading","nameOf","member","contactId","id","companyId","dealId","notes","multiline","minRows","justifyContent","mr"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/task-edit-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 CRM_COLLECTIONS,\n type CrmPicklist,\n crmPicklistActiveValues,\n crmPicklistDefaultLabel,\n crmPicklistOptions,\n crmPicklistShownLabel,\n crmPicklistValueByLabel,\n findOrgMember,\n crmMemberPickerLabel,\n} from '@aglyn/aglyn'\nimport { useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n useFirestore,\n useUser,\n writeGuardedBySeed,\n} from '@aglyn/tenant-feature-instance'\nimport {\n Autocomplete,\n Button,\n Drawer,\n MenuItem,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { deleteDoc, doc } from 'firebase/firestore'\nimport { useMemo, useState } from 'react'\nimport type { CrmTaskRow } from '../hooks/use-crm-tasks'\nimport { useOrgMemberDirectory } from '../hooks/use-org-member-directory'\nimport { refreshCrmNextActivity } from '../model/next-activity-api'\nimport { saveCrmTask } from '../model/task-api'\nimport {\n CRM_TASK_NOTES_MAX,\n CRM_TASK_TITLE_MAX,\n type CrmTaskFields,\n crmTaskFieldsOf,\n} from '../model/task-routes'\nimport { dueAtToLocalInput, localInputToDueAt } from '../model/task-views'\nimport { useCrmPicklist } from '../hooks/use-crm-picklist'\nimport { useCrmTaskPicklists } from '../hooks/use-crm-task-picklists'\nimport { useCrmOrgMount } from '../hooks/use-crm-org-mount'\nimport { useCrmScope } from '../hooks/use-crm-scope'\nimport { isOrgTask } from '../model/task-scope'\nimport CrmRecordPicker from './crm-record-picker'\nimport { CrmSitePicker } from './crm-site-picker'\nimport TaskSnoozeMenu from './task-snooze-menu'\n\nexport interface TaskEditDrawerProps {\n open: boolean\n onClose: () => void\n /**\n * The site the drawer is opened under, or `null` at the organization\n * level (AGL-2630), where a NEW task asks which site it is filed from —\n * or whether it is the organization's own, with no site (AGL-2637) —\n * and an edit keeps the task's own.\n */\n hostId: string | null\n org?: Record<string, unknown> | null\n orgId: string | null | undefined\n scope: readonly [string, string] | null\n /** The reader's tokens, or `null` at the organization level — no clause. */\n readTokens: readonly string[] | null\n /** The task being edited; absent while creating. */\n task?: CrmTaskRow | null\n /**\n * What a \"New task\" button pressed on a record fills in before the person\n * types: the record's own id, so the link is made for them.\n */\n prefill?: Partial<CrmTaskFields>\n /**\n * The verdict of the listener the edited row came from. An edit is refused\n * when the seed is unconfirmed, for the reason the contacts drawer refuses:\n * every field on this form is written back on save, and a form seeded from\n * a stale cache writes the stale values over newer ones.\n */\n seed?: { fromCache: boolean; unreadable: boolean }\n onSaved?: (taskId: string) => void\n}\n\n/**\n * Create or edit one task (AGL-2599).\n *\n * A drawer on the list, the console's standing shape for creating — the\n * list stays where it was and the form slides over it. The same drawer edits,\n * because a task is small enough that \"the record's own page\" would be this\n * form with more air around it.\n *\n * The form is mounted only while the drawer is open and keyed by the task,\n * so opening a different task or \"New task\" after an edit starts from that\n * task's values and not from whatever was last typed.\n */\nexport function TaskEditDrawer(props: TaskEditDrawerProps) {\n const { open, onClose, task } = props\n return (\n <Drawer anchor=\"right\" open={open} onClose={onClose}>\n {open ? <TaskForm key={task?.$id ?? 'new'} {...props} /> : null}\n </Drawer>\n )\n}\nTaskEditDrawer.displayName = 'TaskEditDrawer'\n\n/**\n * One of a task's semantic picklists as a select (AGL-3517): the org's\n * active values in its order — narrowed by `include` when only some may be\n * picked — and the task's own label when the list would not offer it, so\n * the field shows what is stored. No \"None\": a task always has a status, a\n * priority and a type.\n */\nfunction TaskPicklistField(props: {\n label: string\n picklist: CrmPicklist\n value: string\n stored?: string\n include?: (meaning: string | null | undefined) => boolean\n onChange: (label: string, meaning: string | null) => void\n disabled?: boolean\n helperText?: string\n}) {\n const { label, picklist, value, stored, include, onChange, disabled, helperText } = props\n const options = crmPicklistOptions(picklist, stored || value).filter(\n (option) =>\n option.label === value ||\n option.label === stored ||\n !include ||\n include(crmPicklistValueByLabel(picklist, option.label)?.meaning),\n )\n return (\n <TextField\n select\n label={label}\n value={options.some((option) => option.label === value) ? value : ''}\n onChange={(event) => {\n const next = String(event.target.value)\n onChange(next, crmPicklistValueByLabel(picklist, next)?.meaning ?? null)\n }}\n size=\"small\"\n disabled={disabled}\n helperText={helperText}\n sx={{ flex: 1 }}\n >\n {options.map((option) => (\n <MenuItem\n key={option.label}\n value={option.label}\n disabled={(option.inactive || option.unlisted) && option.label !== (stored || value)}\n >\n {option.inactive\n ? `${option.label} (inactive)`\n : option.unlisted\n ? `${option.label} (not in the list)`\n : option.label}\n </MenuItem>\n ))}\n </TextField>\n )\n}\n\nfunction TaskForm(props: TaskEditDrawerProps) {\n const { onClose, hostId, org, orgId, scope, readTokens, task, prefill, seed, onSaved } =\n props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const directory = useOrgMemberDirectory(orgId)\n const mount = useCrmOrgMount()\n // The viewing group under a site, for a linked contact's facet name; null\n // at the organization level, where the picker names each contact through\n // its own holder. The site the route files a NEW task from is the mounted\n // one or the picked one — or none, when the reader files the task with\n // the organization itself; an edit goes back to the task's own.\n const { consentGroup, createHostId } = useCrmScope({ hostId, org })\n const groupId = consentGroup?.groupId ?? null\n const [noSite, setNoSite] = useState(false)\n const filedFrom = task ? (task.hostId ?? null) : noSite ? null : createHostId\n /*\n * Whether the form knows where the task goes: an edit always does (the\n * document does), and a create does once a site is picked or the\n * organization is. `filedFrom` alone cannot say — it is null for both\n * \"not yet picked\" and \"the organization's own\".\n */\n const filed = Boolean(task) || noSite || Boolean(createHostId)\n /*\n * Which level the route runs at: the site the drawer is mounted under, or\n * the organization, with the site a NEW task is filed from named beside\n * it. An edit names no site — the route reads the task's own.\n */\n const routeScope = hostId\n ? { hostId }\n : orgId\n ? { orgId, ...(task || !filedFrom ? {} : { hostId: filedFrom }) }\n : null\n\n const [fields, setFields] = useState<CrmTaskFields>(() => {\n const base = crmTaskFieldsOf(task ?? {})\n if (task) return base\n // A new task is the creator's own unless a button said otherwise: it\n // lands in \"My tasks\", and assigning it elsewhere is a choice made in\n // the picker rather than a default that notifies a teammate by accident.\n // Its reminder is left UNSAID (AGL-2659) — the field shows the due time\n // and the route decides — until the person touches it.\n return { ...base, remindAtMs: undefined, assigneeUid: user?.uid ?? null, ...prefill }\n })\n const assignee = findOrgMember(directory.members, fields.assigneeUid)\n const [busy, setBusy] = useState(false)\n /*\n * TYPE, PRIORITY AND STATUS (AGL-3517) are the org's picklists, each value\n * meaning one of the task's `kind`, `priority` or `status`. What a field\n * shows is the task's own label, else — on a new task whose button named\n * no meaning — the list's default, else the first active value of the\n * meaning it holds. The save sends the label shown, and the route stores\n * the meaning its value carries beside it.\n */\n const picklists = useCrmTaskPicklists(orgId)\n const subjects = useCrmPicklist('taskSubject', orgId)\n const isNew = !task\n const shown = (\n picklist: CrmPicklist,\n label: string | undefined,\n meaning: string,\n prefilled: boolean,\n ) =>\n label ||\n (isNew && !prefilled ? crmPicklistDefaultLabel(picklist) : null) ||\n crmPicklistShownLabel(picklist, meaning, null)\n const typeLabel = shown(picklists.type, fields.typeLabel, fields.kind, Boolean(prefill?.kind))\n const priorityLabel = shown(\n picklists.priority,\n fields.priorityLabel,\n fields.priority,\n Boolean(prefill?.priority),\n )\n const status = task?.status === 'done' ? 'done' : 'open'\n const statusLabel = shown(picklists.status, fields.statusLabel, status, false)\n const subjectOptions = crmPicklistActiveValues(subjects.picklist).map((value) => value.label)\n const [error, setError] = useState<string | null>(null)\n\n const set = <K extends keyof CrmTaskFields>(key: K, value: CrmTaskFields[K]) =>\n setFields((prev) => ({ ...prev, [key]: value }))\n /*\n * The due date, and the reminder with it (AGL-2659): a reminder left\n * unsaid shows the due time and needs no help, and one a person set to\n * the due time follows it here as the route would follow it — so what\n * the field shows after the change is what the save will store.\n */\n const setDue = (dueAtMs: number | null) =>\n setFields((prev) => ({\n ...prev,\n dueAtMs,\n ...(typeof prev.remindAtMs === 'number' && prev.remindAtMs === prev.dueAtMs\n ? { remindAtMs: dueAtMs }\n : {}),\n }))\n /** What the reminder field shows: the due time while nothing is said. */\n const remindAtMs = fields.remindAtMs === undefined ? fields.dueAtMs : fields.remindAtMs\n const reminderHelp =\n remindAtMs === null\n ? 'No reminder.'\n : remindAtMs === fields.dueAtMs\n ? 'At the due time — the assignee is reminded in the console and by email.'\n : 'The assignee is reminded in the console and by email.'\n\n const save = async () => {\n if (!fields.title.trim()) {\n setError('A task needs a title.')\n return\n }\n if (!filed) {\n setError('Pick the site this task is filed from, or file it with the organization.')\n return\n }\n if (!routeScope) {\n setError('The organization is still loading. Try again in a moment.')\n return\n }\n setBusy(true)\n setError(null)\n try {\n const write = async () => {\n const result = await saveCrmTask(user, {\n ...routeScope,\n ...(task ? { taskId: task.$id } : {}),\n task: {\n ...fields,\n title: fields.title.trim(),\n typeLabel,\n priorityLabel,\n statusLabel,\n },\n })\n enqueueSnackbar(\n task\n ? 'Task saved'\n : result.notified\n ? 'Task created and the assignee notified'\n : 'Task created',\n { variant: 'success' },\n )\n onSaved?.(result.taskId)\n onClose()\n }\n if (task) {\n const verdict = await writeGuardedBySeed(\n {\n subject: 'task',\n fromCache: seed?.fromCache ?? false,\n unreadable: seed?.unreadable ?? false,\n },\n write,\n )\n if (!verdict.ok) {\n enqueueSnackbar(verdict.message ?? 'The task could not be saved.', {\n variant: 'warning',\n })\n }\n } else {\n await write()\n }\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : 'The task could not be saved.')\n } finally {\n setBusy(false)\n }\n }\n\n /*\n * Deleting is client-direct: it has no side effect outside the document,\n * and the rules gate it on the same predicate as any other CRM write.\n */\n const remove = async () => {\n if (!task || !scope) return\n const confirmed = await confirm({\n title: 'Delete this task?',\n description: `\"${task.title}\" is removed for everyone who can see it. Completed tasks can be kept instead — tick it done.`,\n confirmationText: 'Delete',\n confirmationButtonProps: { color: 'error' },\n })\n // `confirm` resolves with no value and REJECTS on cancel (AGL-3509).\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n setBusy(true)\n try {\n await deleteDoc(\n doc(firestore, scope[0], scope[1], CRM_COLLECTIONS.tasks, task.$id),\n )\n enqueueSnackbar('Task deleted', { variant: 'success' })\n onClose()\n // A client-direct write: the records it named are told (AGL-2661).\n await refreshCrmNextActivity(user, routeScope, [task])\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : 'The task could not be deleted.')\n } finally {\n setBusy(false)\n }\n }\n\n return (\n <Stack\n component=\"form\"\n spacing={2}\n sx={{ width: { xs: '100vw', sm: 420 }, p: 3 }}\n onSubmit={(event: { preventDefault: () => void }) => {\n event.preventDefault()\n void save()\n }}\n >\n <Typography variant=\"h6\">{task ? 'Edit task' : 'New task'}</Typography>\n {/* Only a new task at the organization level asks — see `filedFrom`. */}\n {task ? null : (\n <CrmSitePicker\n hostId={hostId}\n disabled={busy}\n noSite={{\n label: 'This organization (no site)',\n helperText:\n 'A task of the organization itself, listed from the ' +\n 'organization hub and from every site. Completing it runs no ' +\n \"site's automations.\",\n picked: noSite,\n onPick: setNoSite,\n }}\n />\n )}\n {/* An edit at the organization level says where the task is filed. */}\n {task && mount ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {isOrgTask(task)\n ? 'Filed with the organization — no site.'\n : `Filed from ${mount.siteName(task.hostId as string)}.`}\n </Typography>\n ) : null}\n {/*\n The subject (AGL-3517): Salesforce's Subject combobox — the org's\n suggestions under a field that takes any text, so the title stays\n the person's own words.\n */}\n <Autocomplete\n freeSolo\n size=\"small\"\n options={subjectOptions}\n inputValue={fields.title}\n onInputChange={(_event, value) => set('title', value)}\n renderInput={(params) => (\n <TextField\n {...params}\n label=\"Subject\"\n size=\"small\"\n autoFocus\n required\n slotProps={{\n ...params.slotProps,\n htmlInput: { ...params.slotProps.htmlInput, maxLength: CRM_TASK_TITLE_MAX },\n }}\n />\n )}\n />\n <Stack direction=\"row\" spacing={1}>\n <TaskPicklistField\n label=\"Type\"\n picklist={picklists.type}\n value={typeLabel}\n stored={task?.typeLabel ?? undefined}\n onChange={(label, meaning) =>\n setFields((prev) => ({\n ...prev,\n typeLabel: label,\n ...(meaning ? { kind: meaning as CrmTaskFields['kind'] } : {}),\n }))\n }\n disabled={busy}\n />\n <TaskPicklistField\n label=\"Priority\"\n picklist={picklists.priority}\n value={priorityLabel}\n stored={task?.priorityLabel ?? undefined}\n onChange={(label, meaning) =>\n setFields((prev) => ({\n ...prev,\n priorityLabel: label,\n ...(meaning ? { priority: meaning as CrmTaskFields['priority'] } : {}),\n }))\n }\n disabled={busy}\n />\n </Stack>\n {/*\n Only the values of the task's own status: ticking it done in the\n list is what completes a task, because that is what runs the\n site's automations.\n */}\n <TaskPicklistField\n label=\"Status\"\n picklist={picklists.status}\n value={statusLabel}\n stored={task?.statusLabel ?? undefined}\n include={(meaning) => meaning === status}\n onChange={(label) => set('statusLabel', label)}\n disabled={busy}\n helperText={\n status === 'done'\n ? 'Untick the task in the list to reopen it.'\n : 'Tick the task in the list to complete it.'\n }\n />\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'flex-start' }}>\n <TextField\n label=\"Due\"\n type=\"datetime-local\"\n value={dueAtToLocalInput(fields.dueAtMs)}\n onChange={(event) => setDue(localInputToDueAt(event.target.value))}\n size=\"small\"\n slotProps={{ inputLabel: { shrink: true } }}\n helperText=\"Leave empty for a task with no due date.\"\n sx={{ flex: 1 }}\n />\n {/*\n A stored task is snoozed in place — the one write lands and the\n field follows it, so a later Save carries the same date. A task\n that does not exist yet has nothing to write, so the same menu\n fills the field and the save is the write.\n */}\n <TaskSnoozeMenu\n variant=\"button\"\n dueAtMs={fields.dueAtMs}\n remindAtMs={remindAtMs}\n disabled={busy}\n target={\n task && scope\n ? { write: { scope, taskId: task.$id } }\n : { pick: setDue }\n }\n onSnoozed={setDue}\n />\n </Stack>\n {/*\n The reminder (AGL-2659), beside the due date it follows by default.\n Its own field rather than a switch, because \"remind me an hour\n before\" is the second thing a person wants after \"remind me\".\n */}\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'flex-start' }}>\n <TextField\n label=\"Remind me\"\n type=\"datetime-local\"\n value={dueAtToLocalInput(remindAtMs)}\n onChange={(event) => set('remindAtMs', localInputToDueAt(event.target.value))}\n size=\"small\"\n slotProps={{ inputLabel: { shrink: true } }}\n helperText={reminderHelp}\n sx={{ flex: 1 }}\n />\n <Button\n size=\"small\"\n onClick={() => set('remindAtMs', null)}\n disabled={busy || remindAtMs === null}\n >\n {'No reminder'}\n </Button>\n </Stack>\n <TextField\n select\n label=\"Assignee\"\n // The stored assignee resolved to a member — by uid, or by an\n // address the roster has — so the picker highlights the person and\n // a save writes their uid. One the roster does not hold is kept as\n // its own option: a controlled select whose value is absent from\n // its options renders empty, which reads as unassigned.\n value={assignee?.uid ?? fields.assigneeUid ?? ''}\n onChange={(event) => set('assigneeUid', event.target.value || null)}\n size=\"small\"\n disabled={directory.loading}\n helperText={\n directory.error ??\n (fields.assigneeUid && fields.assigneeUid !== user?.uid\n ? 'They will be notified when you save.'\n : undefined)\n }\n >\n <MenuItem value=\"\">{'Unassigned'}</MenuItem>\n {fields.assigneeUid && !assignee ? (\n <MenuItem value={fields.assigneeUid}>{directory.nameOf(fields.assigneeUid)}</MenuItem>\n ) : null}\n {directory.members.map((member) => (\n <MenuItem key={member.uid} value={member.uid}>\n {crmMemberPickerLabel(member)}\n {member.uid === user?.uid ? ' (you)' : ''}\n </MenuItem>\n ))}\n </TextField>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Linked to'}\n </Typography>\n <CrmRecordPicker\n kind=\"contact\"\n scope={scope}\n readTokens={readTokens}\n groupId={groupId}\n org={org}\n value={fields.contactId}\n onChange={(id) => set('contactId', id)}\n disabled={busy}\n />\n <CrmRecordPicker\n kind=\"company\"\n scope={scope}\n readTokens={readTokens}\n groupId={groupId}\n org={org}\n value={fields.companyId}\n onChange={(id) => set('companyId', id)}\n disabled={busy}\n />\n <CrmRecordPicker\n kind=\"deal\"\n scope={scope}\n readTokens={readTokens}\n groupId={groupId}\n org={org}\n value={fields.dealId}\n onChange={(id) => set('dealId', id)}\n disabled={busy}\n />\n <TextField\n label=\"Notes\"\n value={fields.notes}\n onChange={(event) => set('notes', event.target.value)}\n size=\"small\"\n multiline\n minRows={3}\n slotProps={{ htmlInput: { maxLength: CRM_TASK_NOTES_MAX } }}\n />\n {error ? (\n <Typography variant=\"body2\" color=\"error\">\n {error}\n </Typography>\n ) : null}\n <Stack direction=\"row\" spacing={1} sx={{ justifyContent: 'flex-end' }}>\n {task ? (\n <Button\n color=\"error\"\n onClick={() => void remove()}\n disabled={busy}\n sx={{ mr: 'auto' }}\n >\n {'Delete'}\n </Button>\n ) : null}\n <Button onClick={onClose} disabled={busy}>\n {'Cancel'}\n </Button>\n <Button type=\"submit\" variant=\"contained\" disabled={busy || !filed}>\n {task ? 'Save' : 'Create task'}\n </Button>\n </Stack>\n </Stack>\n )\n}\nTaskForm.displayName = 'TaskForm'\n\nexport default TaskEditDrawer\n"],"names":["CRM_COLLECTIONS","crmPicklistActiveValues","crmPicklistDefaultLabel","crmPicklistOptions","crmPicklistShownLabel","crmPicklistValueByLabel","findOrgMember","crmMemberPickerLabel","useConfirmationContext","useSnackbar","useFirestore","useUser","writeGuardedBySeed","Autocomplete","Button","Drawer","MenuItem","Stack","TextField","Typography","deleteDoc","doc","useState","useOrgMemberDirectory","refreshCrmNextActivity","saveCrmTask","CRM_TASK_NOTES_MAX","CRM_TASK_TITLE_MAX","crmTaskFieldsOf","dueAtToLocalInput","localInputToDueAt","useCrmPicklist","useCrmTaskPicklists","useCrmOrgMount","useCrmScope","isOrgTask","CrmRecordPicker","CrmSitePicker","TaskSnoozeMenu","TaskEditDrawer","props","open","onClose","task","anchor","TaskForm","$id","displayName","TaskPicklistField","label","picklist","value","stored","include","onChange","disabled","helperText","options","filter","option","meaning","select","some","event","next","String","target","size","sx","flex","map","inactive","unlisted","assignee","directory","hostId","org","orgId","scope","readTokens","prefill","seed","onSaved","firestore","data","user","enqueueSnackbar","confirm","mount","consentGroup","createHostId","groupId","noSite","setNoSite","filedFrom","filed","Boolean","routeScope","fields","setFields","base","remindAtMs","undefined","assigneeUid","uid","members","busy","setBusy","picklists","subjects","isNew","shown","prefilled","typeLabel","type","kind","priorityLabel","priority","status","statusLabel","subjectOptions","error","setError","set","key","prev","setDue","dueAtMs","reminderHelp","save","title","trim","write","result","taskId","notified","variant","verdict","subject","fromCache","unreadable","ok","message","cause","Error","remove","confirmed","description","confirmationText","confirmationButtonProps","color","then","catch","tasks","component","spacing","width","xs","sm","p","onSubmit","preventDefault","picked","onPick","siteName","freeSolo","inputValue","onInputChange","_event","renderInput","params","autoFocus","required","slotProps","htmlInput","maxLength","direction","alignItems","inputLabel","shrink","pick","onSnoozed","onClick","loading","nameOf","member","contactId","id","companyId","dealId","notes","multiline","minRows","justifyContent","mr"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;;AAEA,SACEA,eAAe,EAEfC,uBAAuB,EACvBC,uBAAuB,EACvBC,kBAAkB,EAClBC,qBAAqB,EACrBC,uBAAuB,EACvBC,aAAa,EACbC,oBAAoB,QACf,eAAc;AACrB,SAASC,sBAAsB,QAAQ,uBAAsB;AAC7D,SAASC,WAAW,QAAQ,8BAA6B;AACzD,SACEC,YAAY,EACZC,OAAO,EACPC,kBAAkB,QACb,iCAAgC;AACvC,SACEC,YAAY,EACZC,MAAM,EACNC,MAAM,EACNC,QAAQ,EACRC,KAAK,EACLC,SAAS,EACTC,UAAU,QACL,gBAAe;AACtB,SAASC,SAAS,EAAEC,GAAG,QAAQ,qBAAoB;AACnD,SAAkBC,QAAQ,QAAQ,QAAO;AAEzC,SAASC,qBAAqB,QAAQ,uCAAmC;AACzE,SAASC,sBAAsB,QAAQ,gCAA4B;AACnE,SAASC,WAAW,QAAQ,uBAAmB;AAC/C,SACEC,kBAAkB,EAClBC,kBAAkB,EAElBC,eAAe,QACV,0BAAsB;AAC7B,SAASC,iBAAiB,EAAEC,iBAAiB,QAAQ,yBAAqB;AAC1E,SAASC,cAAc,QAAQ,+BAA2B;AAC1D,SAASC,mBAAmB,QAAQ,qCAAiC;AACrE,SAASC,cAAc,QAAQ,gCAA4B;AAC3D,SAASC,WAAW,QAAQ,4BAAwB;AACpD,SAASC,SAAS,QAAQ,yBAAqB;AAC/C,OAAOC,qBAAqB,yBAAqB;AACjD,SAASC,aAAa,QAAQ,uBAAmB;AACjD,OAAOC,oBAAoB,wBAAoB;AAkC/C;;;;;;;;;;;CAWC,GACD,OAAO,SAASC,eAAeC,KAA0B;;IACvD,MAAM,EAAEC,IAAI,EAAEC,OAAO,EAAEC,IAAI,EAAE,GAAGH;IAChC,qBACE,KAACzB;QAAO6B,QAAO;QAAQH,MAAMA;QAAMC,SAASA;kBACzCD,qBAAO,KAACI,uBAAsCL,gBAAxBG,wBAAAA,KAAMG,GAAG,mBAAI,SAAuB;;AAGjE;AACAP,eAAeQ,WAAW,GAAG;AAE7B;;;;;;CAMC,GACD,SAASC,kBAAkBR,KAS1B;IACC,MAAM,EAAES,KAAK,EAAEC,QAAQ,EAAEC,KAAK,EAAEC,MAAM,EAAEC,OAAO,EAAEC,QAAQ,EAAEC,QAAQ,EAAEC,UAAU,EAAE,GAAGhB;IACpF,MAAMiB,UAAUtD,mBAAmB+C,UAAUE,UAAUD,OAAOO,MAAM,CAClE,CAACC;YAIStD;eAHRsD,OAAOV,KAAK,KAAKE,SACjBQ,OAAOV,KAAK,KAAKG,UACjB,CAACC,WACDA,SAAQhD,2BAAAA,wBAAwB6C,UAAUS,OAAOV,KAAK,sBAA9C5C,yBAAiDuD,OAAO;;IAEpE,qBACE,KAAC1C;QACC2C,MAAM;QACNZ,OAAOA;QACPE,OAAOM,QAAQK,IAAI,CAAC,CAACH,SAAWA,OAAOV,KAAK,KAAKE,SAASA,QAAQ;QAClEG,UAAU,CAACS;;gBAEM1D;YADf,MAAM2D,OAAOC,OAAOF,MAAMG,MAAM,CAACf,KAAK;YACtCG,SAASU,eAAM3D,2BAAAA,wBAAwB6C,UAAUc,0BAAlC3D,yBAAyCuD,OAAO,mBAAI;QACrE;QACAO,MAAK;QACLZ,UAAUA;QACVC,YAAYA;QACZY,IAAI;YAAEC,MAAM;QAAE;kBAEbZ,QAAQa,GAAG,CAAC,CAACX,uBACZ,KAAC3C;gBAECmC,OAAOQ,OAAOV,KAAK;gBACnBM,UAAU,AAACI,CAAAA,OAAOY,QAAQ,IAAIZ,OAAOa,QAAQ,AAAD,KAAMb,OAAOV,KAAK,KAAMG,CAAAA,UAAUD,KAAI;0BAEjFQ,OAAOY,QAAQ,GACZ,GAAGZ,OAAOV,KAAK,CAAC,WAAW,CAAC,GAC5BU,OAAOa,QAAQ,GACb,GAAGb,OAAOV,KAAK,CAAC,kBAAkB,CAAC,GACnCU,OAAOV,KAAK;eARbU,OAAOV,KAAK;;AAa3B;AAEA,SAASJ,SAASL,KAA0B;cAiBhBG,mCAkWb8B,cAKLC;IAvXR,MAAM,EAAEhC,OAAO,EAAEiC,MAAM,EAAEC,GAAG,EAAEC,KAAK,EAAEC,KAAK,EAAEC,UAAU,EAAEpC,IAAI,EAAEqC,OAAO,EAAEC,IAAI,EAAEC,OAAO,EAAE,GACpF1C;IACF,MAAM2C,YAAYzE;IAClB,MAAM,EAAE0E,MAAMC,IAAI,EAAE,GAAG1E;IACvB,MAAM,EAAE2E,eAAe,EAAE,GAAG7E;IAC5B,MAAM,EAAE8E,OAAO,EAAE,GAAG/E;IACpB,MAAMkE,YAAYnD,sBAAsBsD;IACxC,MAAMW,QAAQvD;IACd,0EAA0E;IAC1E,yEAAyE;IACzE,0EAA0E;IAC1E,uEAAuE;IACvE,gEAAgE;IAChE,MAAM,EAAEwD,YAAY,EAAEC,YAAY,EAAE,GAAGxD,YAAY;QAAEyC;QAAQC;IAAI;IACjE,MAAMe,kBAAUF,gCAAAA,aAAcE,OAAO,mBAAI;IACzC,MAAM,CAACC,QAAQC,UAAU,GAAGvE,SAAS;IACrC,MAAMwE,YAAYnD,QAAQA,eAAAA,KAAKgC,MAAM,YAAXhC,eAAe,OAAQiD,SAAS,OAAOF;IACjE;;;;;GAKC,GACD,MAAMK,QAAQC,QAAQrD,SAASiD,UAAUI,QAAQN;IACjD;;;;GAIC,GACD,MAAMO,aAAatB,SACf;QAAEA;IAAO,IACTE,QACE;QAAEA;OAAWlC,QAAQ,CAACmD,YAAY,CAAC,IAAI;QAAEnB,QAAQmB;IAAU,KAC3D;IAEN,MAAM,CAACI,QAAQC,UAAU,GAAG7E,SAAwB;;QAClD,MAAM8E,OAAOxE,gBAAgBe,eAAAA,OAAQ,CAAC;QACtC,IAAIA,MAAM,OAAOyD;QACjB,qEAAqE;QACrE,sEAAsE;QACtE,yEAAyE;QACzE,wEAAwE;QACxE,uDAAuD;QACvD,OAAO,aAAKA;YAAMC,YAAYC;YAAWC,WAAW,UAAElB,wBAAAA,KAAMmB,GAAG,mBAAI;WAASxB;IAC9E;IACA,MAAMP,WAAWnE,cAAcoE,UAAU+B,OAAO,EAAEP,OAAOK,WAAW;IACpE,MAAM,CAACG,MAAMC,QAAQ,GAAGrF,SAAS;IACjC;;;;;;;GAOC,GACD,MAAMsF,YAAY5E,oBAAoB6C;IACtC,MAAMgC,WAAW9E,eAAe,eAAe8C;IAC/C,MAAMiC,QAAQ,CAACnE;IACf,MAAMoE,QAAQ,CACZ7D,UACAD,OACAW,SACAoD,YAEA/D,SACC6D,CAAAA,SAAS,CAACE,YAAY9G,wBAAwBgD,YAAY,IAAG,KAC9D9C,sBAAsB8C,UAAUU,SAAS;IAC3C,MAAMqD,YAAYF,MAAMH,UAAUM,IAAI,EAAEhB,OAAOe,SAAS,EAAEf,OAAOiB,IAAI,EAAEnB,QAAQhB,2BAAAA,QAASmC,IAAI;IAC5F,MAAMC,gBAAgBL,MACpBH,UAAUS,QAAQ,EAClBnB,OAAOkB,aAAa,EACpBlB,OAAOmB,QAAQ,EACfrB,QAAQhB,2BAAAA,QAASqC,QAAQ;IAE3B,MAAMC,SAAS3E,CAAAA,wBAAAA,KAAM2E,MAAM,MAAK,SAAS,SAAS;IAClD,MAAMC,cAAcR,MAAMH,UAAUU,MAAM,EAAEpB,OAAOqB,WAAW,EAAED,QAAQ;IACxE,MAAME,iBAAiBvH,wBAAwB4G,SAAS3D,QAAQ,EAAEoB,GAAG,CAAC,CAACnB,QAAUA,MAAMF,KAAK;IAC5F,MAAM,CAACwE,OAAOC,SAAS,GAAGpG,SAAwB;IAElD,MAAMqG,MAAM,CAAgCC,KAAQzE,QAClDgD,UAAU,CAAC0B,OAAU,aAAKA;gBAAM,CAACD,IAAI,EAAEzE;;IACzC;;;;;GAKC,GACD,MAAM2E,SAAS,CAACC,UACd5B,UAAU,CAAC0B,OAAU,aAChBA;gBACHE;eACI,OAAOF,KAAKxB,UAAU,KAAK,YAAYwB,KAAKxB,UAAU,KAAKwB,KAAKE,OAAO,GACvE;gBAAE1B,YAAY0B;YAAQ,IACtB,CAAC;IAET,uEAAuE,GACvE,MAAM1B,aAAaH,OAAOG,UAAU,KAAKC,YAAYJ,OAAO6B,OAAO,GAAG7B,OAAOG,UAAU;IACvF,MAAM2B,eACJ3B,eAAe,OACX,iBACAA,eAAeH,OAAO6B,OAAO,GAC3B,4EACA;IAER,MAAME,OAAO;QACX,IAAI,CAAC/B,OAAOgC,KAAK,CAACC,IAAI,IAAI;YACxBT,SAAS;YACT;QACF;QACA,IAAI,CAAC3B,OAAO;YACV2B,SAAS;YACT;QACF;QACA,IAAI,CAACzB,YAAY;YACfyB,SAAS;YACT;QACF;QACAf,QAAQ;QACRe,SAAS;QACT,IAAI;YACF,MAAMU,QAAQ;gBACZ,MAAMC,SAAS,MAAM5G,YAAY4D,MAAM,aAClCY,YACCtD,OAAO;oBAAE2F,QAAQ3F,KAAKG,GAAG;gBAAC,IAAI,CAAC;oBACnCH,MAAM,aACDuD;wBACHgC,OAAOhC,OAAOgC,KAAK,CAACC,IAAI;wBACxBlB;wBACAG;wBACAG;;;gBAGJjC,gBACE3C,OACI,eACA0F,OAAOE,QAAQ,GACb,2CACA,gBACN;oBAAEC,SAAS;gBAAU;gBAEvBtD,2BAAAA,QAAUmD,OAAOC,MAAM;gBACvB5F;YACF;YACA,IAAIC,MAAM;;gBACR,MAAM8F,UAAU,MAAM7H,mBACpB;oBACE8H,SAAS;oBACTC,SAAS,UAAE1D,wBAAAA,KAAM0D,SAAS,mBAAI;oBAC9BC,UAAU,WAAE3D,wBAAAA,KAAM2D,UAAU,oBAAI;gBAClC,GACAR;gBAEF,IAAI,CAACK,QAAQI,EAAE,EAAE;wBACCJ;oBAAhBnD,iBAAgBmD,mBAAAA,QAAQK,OAAO,YAAfL,mBAAmB,gCAAgC;wBACjED,SAAS;oBACX;gBACF;YACF,OAAO;gBACL,MAAMJ;YACR;QACF,EAAE,OAAOW,OAAO;YACdrB,SAASqB,iBAAiBC,QAAQD,MAAMD,OAAO,GAAG;QACpD,SAAU;YACRnC,QAAQ;QACV;IACF;IAEA;;;GAGC,GACD,MAAMsC,SAAS;QACb,IAAI,CAACtG,QAAQ,CAACmC,OAAO;QACrB,MAAMoE,YAAY,MAAM3D,QAAQ;YAC9B2C,OAAO;YACPiB,aAAa,CAAC,CAAC,EAAExG,KAAKuF,KAAK,CAAC,6FAA6F,CAAC;YAC1HkB,kBAAkB;YAClBC,yBAAyB;gBAAEC,OAAO;YAAQ;QAC5C,EACE,qEAAqE;SACpEC,IAAI,CAAC,IAAM,MACXC,KAAK,CAAC,IAAM;QACf,IAAI,CAACN,WAAW;QAChBvC,QAAQ;QACR,IAAI;YACF,MAAMvF,UACJC,IAAI8D,WAAWL,KAAK,CAAC,EAAE,EAAEA,KAAK,CAAC,EAAE,EAAE9E,gBAAgByJ,KAAK,EAAE9G,KAAKG,GAAG;YAEpEwC,gBAAgB,gBAAgB;gBAAEkD,SAAS;YAAU;YACrD9F;YACA,mEAAmE;YACnE,MAAMlB,uBAAuB6D,MAAMY,YAAY;gBAACtD;aAAK;QACvD,EAAE,OAAOoG,OAAO;YACdrB,SAASqB,iBAAiBC,QAAQD,MAAMD,OAAO,GAAG;QACpD,SAAU;YACRnC,QAAQ;QACV;IACF;IAEA,qBACE,MAAC1F;QACCyI,WAAU;QACVC,SAAS;QACTvF,IAAI;YAAEwF,OAAO;gBAAEC,IAAI;gBAASC,IAAI;YAAI;YAAGC,GAAG;QAAE;QAC5CC,UAAU,CAACjG;YACTA,MAAMkG,cAAc;YACpB,KAAKhC;QACP;;0BAEA,KAAC9G;gBAAWqH,SAAQ;0BAAM7F,OAAO,cAAc;;YAE9CA,OAAO,qBACN,KAACN;gBACCsC,QAAQA;gBACRpB,UAAUmD;gBACVd,QAAQ;oBACN3C,OAAO;oBACPO,YACE,wDACA,iEACA;oBACF0G,QAAQtE;oBACRuE,QAAQtE;gBACV;;YAIHlD,QAAQ6C,sBACP,KAACrE;gBAAWqH,SAAQ;gBAAUc,OAAM;0BACjCnH,UAAUQ,QACP,2CACA,CAAC,WAAW,EAAE6C,MAAM4E,QAAQ,CAACzH,KAAKgC,MAAM,EAAY,CAAC,CAAC;iBAE1D;0BAMJ,KAAC9D;gBACCwJ,QAAQ;gBACRlG,MAAK;gBACLV,SAAS+D;gBACT8C,YAAYpE,OAAOgC,KAAK;gBACxBqC,eAAe,CAACC,QAAQrH,QAAUwE,IAAI,SAASxE;gBAC/CsH,aAAa,CAACC,uBACZ,KAACxJ,wBACKwJ;wBACJzH,OAAM;wBACNkB,MAAK;wBACLwG,SAAS;wBACTC,QAAQ;wBACRC,WAAW,aACNH,OAAOG,SAAS;4BACnBC,WAAW,aAAKJ,OAAOG,SAAS,CAACC,SAAS;gCAAEC,WAAWpJ;;;;;0BAK/D,MAACV;gBAAM+J,WAAU;gBAAMrB,SAAS;;kCAC9B,KAAC3G;wBACCC,OAAM;wBACNC,UAAU0D,UAAUM,IAAI;wBACxB/D,OAAO8D;wBACP7D,MAAM,WAAET,wBAAAA,KAAMsE,SAAS,oBAAIX;wBAC3BhD,UAAU,CAACL,OAAOW,UAChBuC,UAAU,CAAC0B,OAAU,aAChBA;oCACHZ,WAAWhE;mCACPW,UAAU;oCAAEuD,MAAMvD;gCAAiC,IAAI,CAAC;wBAGhEL,UAAUmD;;kCAEZ,KAAC1D;wBACCC,OAAM;wBACNC,UAAU0D,UAAUS,QAAQ;wBAC5BlE,OAAOiE;wBACPhE,MAAM,WAAET,wBAAAA,KAAMyE,aAAa,oBAAId;wBAC/BhD,UAAU,CAACL,OAAOW,UAChBuC,UAAU,CAAC0B,OAAU,aAChBA;oCACHT,eAAenE;mCACXW,UAAU;oCAAEyD,UAAUzD;gCAAqC,IAAI,CAAC;wBAGxEL,UAAUmD;;;;0BAQd,KAAC1D;gBACCC,OAAM;gBACNC,UAAU0D,UAAUU,MAAM;gBAC1BnE,OAAOoE;gBACPnE,MAAM,WAAET,wBAAAA,KAAM4E,WAAW,oBAAIjB;gBAC7BjD,SAAS,CAACO,UAAYA,YAAY0D;gBAClChE,UAAU,CAACL,QAAU0E,IAAI,eAAe1E;gBACxCM,UAAUmD;gBACVlD,YACE8D,WAAW,SACP,8CACA;;0BAGR,MAACrG;gBAAM+J,WAAU;gBAAMrB,SAAS;gBAAGvF,IAAI;oBAAE6G,YAAY;gBAAa;;kCAChE,KAAC/J;wBACC+B,OAAM;wBACNiE,MAAK;wBACL/D,OAAOtB,kBAAkBqE,OAAO6B,OAAO;wBACvCzE,UAAU,CAACS,QAAU+D,OAAOhG,kBAAkBiC,MAAMG,MAAM,CAACf,KAAK;wBAChEgB,MAAK;wBACL0G,WAAW;4BAAEK,YAAY;gCAAEC,QAAQ;4BAAK;wBAAE;wBAC1C3H,YAAW;wBACXY,IAAI;4BAAEC,MAAM;wBAAE;;kCAQhB,KAAC/B;wBACCkG,SAAQ;wBACRT,SAAS7B,OAAO6B,OAAO;wBACvB1B,YAAYA;wBACZ9C,UAAUmD;wBACVxC,QACEvB,QAAQmC,QACJ;4BAAEsD,OAAO;gCAAEtD;gCAAOwD,QAAQ3F,KAAKG,GAAG;4BAAC;wBAAE,IACrC;4BAAEsI,MAAMtD;wBAAO;wBAErBuD,WAAWvD;;;;0BAQf,MAAC7G;gBAAM+J,WAAU;gBAAMrB,SAAS;gBAAGvF,IAAI;oBAAE6G,YAAY;gBAAa;;kCAChE,KAAC/J;wBACC+B,OAAM;wBACNiE,MAAK;wBACL/D,OAAOtB,kBAAkBwE;wBACzB/C,UAAU,CAACS,QAAU4D,IAAI,cAAc7F,kBAAkBiC,MAAMG,MAAM,CAACf,KAAK;wBAC3EgB,MAAK;wBACL0G,WAAW;4BAAEK,YAAY;gCAAEC,QAAQ;4BAAK;wBAAE;wBAC1C3H,YAAYwE;wBACZ5D,IAAI;4BAAEC,MAAM;wBAAE;;kCAEhB,KAACvD;wBACCqD,MAAK;wBACLmH,SAAS,IAAM3D,IAAI,cAAc;wBACjCpE,UAAUmD,QAAQL,eAAe;kCAEhC;;;;0BAGL,MAACnF;gBACC2C,MAAM;gBACNZ,OAAM;gBACN,8DAA8D;gBAC9D,mEAAmE;gBACnE,mEAAmE;gBACnE,iEAAiE;gBACjE,wDAAwD;gBACxDE,KAAK,GAAEsB,iBAAAA,4BAAAA,SAAU+B,GAAG,oBAAIN,OAAOK,WAAW,YAAnC9B,QAAuC;gBAC9CnB,UAAU,CAACS,QAAU4D,IAAI,eAAe5D,MAAMG,MAAM,CAACf,KAAK,IAAI;gBAC9DgB,MAAK;gBACLZ,UAAUmB,UAAU6G,OAAO;gBAC3B/H,UAAU,GACRkB,mBAAAA,UAAU+C,KAAK,YAAf/C,mBACCwB,OAAOK,WAAW,IAAIL,OAAOK,WAAW,MAAKlB,wBAAAA,KAAMmB,GAAG,IACnD,yCACAF;;kCAGN,KAACtF;wBAASmC,OAAM;kCAAI;;oBACnB+C,OAAOK,WAAW,IAAI,CAAC9B,yBACtB,KAACzD;wBAASmC,OAAO+C,OAAOK,WAAW;kCAAG7B,UAAU8G,MAAM,CAACtF,OAAOK,WAAW;yBACvE;oBACH7B,UAAU+B,OAAO,CAACnC,GAAG,CAAC,CAACmH,uBACtB,MAACzK;4BAA0BmC,OAAOsI,OAAOjF,GAAG;;gCACzCjG,qBAAqBkL;gCACrBA,OAAOjF,GAAG,MAAKnB,wBAAAA,KAAMmB,GAAG,IAAG,WAAW;;2BAF1BiF,OAAOjF,GAAG;;;0BAM7B,KAACrF;gBAAWqH,SAAQ;gBAAWc,OAAM;0BAClC;;0BAEH,KAAClH;gBACC+E,MAAK;gBACLrC,OAAOA;gBACPC,YAAYA;gBACZY,SAASA;gBACTf,KAAKA;gBACLzB,OAAO+C,OAAOwF,SAAS;gBACvBpI,UAAU,CAACqI,KAAOhE,IAAI,aAAagE;gBACnCpI,UAAUmD;;0BAEZ,KAACtE;gBACC+E,MAAK;gBACLrC,OAAOA;gBACPC,YAAYA;gBACZY,SAASA;gBACTf,KAAKA;gBACLzB,OAAO+C,OAAO0F,SAAS;gBACvBtI,UAAU,CAACqI,KAAOhE,IAAI,aAAagE;gBACnCpI,UAAUmD;;0BAEZ,KAACtE;gBACC+E,MAAK;gBACLrC,OAAOA;gBACPC,YAAYA;gBACZY,SAASA;gBACTf,KAAKA;gBACLzB,OAAO+C,OAAO2F,MAAM;gBACpBvI,UAAU,CAACqI,KAAOhE,IAAI,UAAUgE;gBAChCpI,UAAUmD;;0BAEZ,KAACxF;gBACC+B,OAAM;gBACNE,OAAO+C,OAAO4F,KAAK;gBACnBxI,UAAU,CAACS,QAAU4D,IAAI,SAAS5D,MAAMG,MAAM,CAACf,KAAK;gBACpDgB,MAAK;gBACL4H,SAAS;gBACTC,SAAS;gBACTnB,WAAW;oBAAEC,WAAW;wBAAEC,WAAWrJ;oBAAmB;gBAAE;;YAE3D+F,sBACC,KAACtG;gBAAWqH,SAAQ;gBAAQc,OAAM;0BAC/B7B;iBAED;0BACJ,MAACxG;gBAAM+J,WAAU;gBAAMrB,SAAS;gBAAGvF,IAAI;oBAAE6H,gBAAgB;gBAAW;;oBACjEtJ,qBACC,KAAC7B;wBACCwI,OAAM;wBACNgC,SAAS,IAAM,KAAKrC;wBACpB1F,UAAUmD;wBACVtC,IAAI;4BAAE8H,IAAI;wBAAO;kCAEhB;yBAED;kCACJ,KAACpL;wBAAOwK,SAAS5I;wBAASa,UAAUmD;kCACjC;;kCAEH,KAAC5F;wBAAOoG,MAAK;wBAASsB,SAAQ;wBAAYjF,UAAUmD,QAAQ,CAACX;kCAC1DpD,OAAO,SAAS;;;;;;AAK3B;AACAE,SAASE,WAAW,GAAG;AAEvB,eAAeR,eAAc"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { CrmTaskRow } from '../hooks/use-crm-tasks';
|
|
2
2
|
import type { OrgMemberDirectory } from '../hooks/use-org-member-directory';
|
|
3
|
-
import { type TaskCsvOptions } from '../model/tasks-csv';
|
|
4
3
|
export interface TasksBulkBarProps {
|
|
5
4
|
/**
|
|
6
5
|
* The site the task routes run as, or `null` at the organization level
|
|
@@ -15,8 +14,6 @@ export interface TasksBulkBarProps {
|
|
|
15
14
|
onSelectedChange: (ids: string[]) => void;
|
|
16
15
|
/** The section's roster — already read for the Assignee column. */
|
|
17
16
|
directory: OrgMemberDirectory;
|
|
18
|
-
/** How the export names the assignee and the linked records — the list's own. */
|
|
19
|
-
csv?: TaskCsvOptions;
|
|
20
17
|
}
|
|
21
18
|
export declare function TasksBulkBar(props: TasksBulkBarProps): import("react").JSX.Element;
|
|
22
19
|
export declare namespace TasksBulkBar {
|
|
@@ -40,15 +40,14 @@ import { Button, MenuItem, TextField } from "@mui/material";
|
|
|
40
40
|
import { doc, serverTimestamp } from "firebase/firestore";
|
|
41
41
|
import { useCallback, useMemo, useState } from "react";
|
|
42
42
|
import { useCrmBulkApply } from "../hooks/use-crm-bulk-apply.js";
|
|
43
|
-
import { downloadTextFile } from "../model/contacts-csv.js";
|
|
44
43
|
import { refreshCrmNextActivity } from "../model/next-activity-api.js";
|
|
45
44
|
import { crmBulkWriters, runCrmBulkBatch, runCrmBulkCalls, runCrmBulkWrites } from "../model/crm-bulk-writes.js";
|
|
46
45
|
import { completeCrmTask, completeCrmTasks, saveCrmTask, saveCrmTasks } from "../model/task-api.js";
|
|
47
46
|
import { crmTaskCallScope, crmTaskFieldsOf } from "../model/task-routes.js";
|
|
48
47
|
import { dueAtToLocalInput, localInputToDueAt } from "../model/task-views.js";
|
|
49
|
-
import { tasksCsv } from "../model/tasks-csv.js";
|
|
50
48
|
import { CrmBulkBarFrame, CrmBulkValueDialog, countNoun } from "./crm-bulk-bar-frame.js";
|
|
51
|
-
import
|
|
49
|
+
import { CrmExportButton } from "./crm-transfer-buttons.js";
|
|
50
|
+
import { CRM_TASKS_RESOURCE } from "../transfer/fields.js";
|
|
52
51
|
const NOUN = {
|
|
53
52
|
singular: 'task',
|
|
54
53
|
plural: 'tasks'
|
|
@@ -64,8 +63,8 @@ export function TasksBulkBar(props) {
|
|
|
64
63
|
}
|
|
65
64
|
TasksBulkBar.displayName = 'TasksBulkBar';
|
|
66
65
|
function TasksBulkBarBody(props) {
|
|
67
|
-
var _ref, _directory_error
|
|
68
|
-
const { hostId, scope, rows, selected, onSelectedChange, directory
|
|
66
|
+
var _ref, _directory_error;
|
|
67
|
+
const { hostId, scope, rows, selected, onSelectedChange, directory } = props;
|
|
69
68
|
const firestore = useFirestore();
|
|
70
69
|
const { data: user } = useUser();
|
|
71
70
|
const { confirm } = useConfirmationContext();
|
|
@@ -209,12 +208,6 @@ function TasksBulkBarBody(props) {
|
|
|
209
208
|
hostId,
|
|
210
209
|
runPlan
|
|
211
210
|
]);
|
|
212
|
-
const handleExport = useCallback(()=>{
|
|
213
|
-
downloadTextFile('tasks-selected.csv', 'text/csv', tasksCsv(selectedRows, csv));
|
|
214
|
-
}, [
|
|
215
|
-
selectedRows,
|
|
216
|
-
csv
|
|
217
|
-
]);
|
|
218
211
|
const handleDelete = useCallback(async ()=>{
|
|
219
212
|
if (!scope || !selectedRows.length) return;
|
|
220
213
|
const count = selectedRows.length;
|
|
@@ -320,16 +313,10 @@ function TasksBulkBarBody(props) {
|
|
|
320
313
|
},
|
|
321
314
|
children: 'Set due'
|
|
322
315
|
}),
|
|
323
|
-
/*#__PURE__*/ _jsx(
|
|
324
|
-
|
|
325
|
-
disabled: busy,
|
|
326
|
-
onClick: handleExport,
|
|
327
|
-
children: 'Export CSV'
|
|
328
|
-
}),
|
|
329
|
-
/*#__PURE__*/ _jsx(CrmExportAllButton, {
|
|
330
|
-
resource: "tasks",
|
|
331
|
-
orgId: (_ref1 = scope == null ? void 0 : scope[1]) != null ? _ref1 : null,
|
|
316
|
+
/*#__PURE__*/ _jsx(CrmExportButton, {
|
|
317
|
+
resource: CRM_TASKS_RESOURCE,
|
|
332
318
|
hostId: hostId,
|
|
319
|
+
selection: selectedRows.map((row)=>row.$id),
|
|
333
320
|
disabled: busy
|
|
334
321
|
}),
|
|
335
322
|
/*#__PURE__*/ _jsx(Button, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/tasks-bulk-bar.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\n/**\n * The bar over the tasks list, for whatever rows are ticked (AGL-2621).\n *\n * Complete them, hand them to somebody, move their due date, take them\n * into a spreadsheet, or delete them. Two of these have a side effect\n * outside the document and go through their ROUTE: completing fires\n * `taskCompleted`, which only the server emits, and assigning tells the\n * assignee, which only the server may write into somebody's inbox — so\n * Complete goes through `crm/task-complete` and Assign through\n * `crm/task-save`, exactly as the checkbox and the drawer do for one task.\n * Under a site that is one request per task, each authorized against the\n * site; beneath the organization hub it is ONE request per action, the\n * routes' org-level batch form (AGL-2637), authorized once by the org and\n * answered per task. The due date and the delete have no effect beyond\n * the document, so they are batched client-direct writes under the rules,\n * as reopening is.\n */\n\nimport { CRM_COLLECTIONS, crmMemberPickerLabel } from '@aglyn/aglyn'\nimport { useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport { useFirestore, useUser } from '@aglyn/tenant-feature-instance'\nimport { Button, MenuItem, TextField } from '@mui/material'\nimport { doc, serverTimestamp } from 'firebase/firestore'\nimport { useCallback, useMemo, useState } from 'react'\nimport { useCrmBulkApply } from '../hooks/use-crm-bulk-apply'\nimport type { CrmTaskRow } from '../hooks/use-crm-tasks'\nimport type { OrgMemberDirectory } from '../hooks/use-org-member-directory'\nimport { downloadTextFile } from '../model/contacts-csv'\nimport { refreshCrmNextActivity } from '../model/next-activity-api'\nimport {\n type CrmBulkPlan,\n type CrmBulkWrite,\n crmBulkWriters,\n runCrmBulkBatch,\n runCrmBulkCalls,\n runCrmBulkWrites,\n} from '../model/crm-bulk-writes'\nimport {\n completeCrmTask,\n completeCrmTasks,\n saveCrmTask,\n saveCrmTasks,\n} from '../model/task-api'\nimport { crmTaskCallScope, crmTaskFieldsOf } from '../model/task-routes'\nimport { dueAtToLocalInput, localInputToDueAt } from '../model/task-views'\nimport { type TaskCsvOptions, tasksCsv } from '../model/tasks-csv'\nimport {\n type CrmBulkNoun,\n CrmBulkBarFrame,\n CrmBulkValueDialog,\n countNoun,\n} from './crm-bulk-bar-frame'\nimport CrmExportAllButton from './crm-export-all-button'\n\nexport interface TasksBulkBarProps {\n /**\n * The site the task routes run as, or `null` at the organization level\n * (AGL-2630), where the routes run as the org — their batch form, one\n * request per action (AGL-2637).\n */\n hostId: string | null\n /** `['orgs', orgId]`, or `null` while the org is unresolved. */\n scope: readonly [string, string] | null\n rows: readonly CrmTaskRow[]\n selected: readonly string[]\n onSelectedChange: (ids: string[]) => void\n /** The section's roster — already read for the Assignee column. */\n directory: OrgMemberDirectory\n /** How the export names the assignee and the linked records — the list's own. */\n csv?: TaskCsvOptions\n}\n\nconst NOUN: CrmBulkNoun = { singular: 'task', plural: 'tasks' }\n\ntype PendingAction = 'assign' | 'due'\n\nconst ACTION_TITLES: Record<PendingAction, string> = {\n assign: 'Assign to',\n due: 'Set the due date',\n}\n\nconst labelOf = (task: CrmTaskRow): string => task.title || task.$id\n\nexport function TasksBulkBar(props: TasksBulkBarProps) {\n if (!props.selected.length) return null\n return <TasksBulkBarBody {...props} />\n}\nTasksBulkBar.displayName = 'TasksBulkBar'\n\nfunction TasksBulkBarBody(props: TasksBulkBarProps) {\n const { hostId, scope, rows, selected, onSelectedChange, directory, csv } = props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { confirm } = useConfirmationContext()\n const { busy, report, apply, dismissReport } = useCrmBulkApply({ recordKind: 'task' })\n\n const selectedRows = useMemo(() => {\n const chosen = new Set(selected)\n return rows.filter((row) => chosen.has(row.$id))\n }, [rows, selected])\n\n const [pending, setPending] = useState<PendingAction | null>(null)\n const [value, setValue] = useState('')\n\n const writers = useMemo(\n () =>\n crmBulkWriters(firestore, (id) =>\n doc(firestore, scope?.[0] ?? 'orgs', scope?.[1] ?? '', CRM_COLLECTIONS.tasks, id),\n ),\n [firestore, scope],\n )\n\n const openAction = (action: PendingAction) => {\n setValue('')\n setPending(action)\n }\n\n // The org the batch form names — the scope's, which is the org's root.\n const orgId = scope?.[1] ?? null\n\n const runPlan = useCallback(\n async (plan: CrmBulkPlan, done: (count: number) => string) => {\n const outcome = await apply({\n attempted: plan.writes.length,\n skipped: plan.skipped,\n job: () => runCrmBulkWrites(writers, plan.writes, (write) => write.label),\n done,\n })\n // Client-direct writes: the records the selection names are told\n // (AGL-2661), once each however many tasks named them.\n const written = new Set(plan.writes.map((write) => write.id))\n await refreshCrmNextActivity(\n user,\n crmTaskCallScope(hostId, orgId),\n selectedRows.filter((task) => written.has(task.$id)),\n )\n return outcome\n },\n [apply, writers, user, hostId, orgId, selectedRows],\n )\n\n /**\n * The routes over the rows: under a site one request per task, in\n * order, named by title; beneath the org hub one request for them all,\n * tallied off its per-task answers.\n */\n const runCalls = useCallback(\n (\n tasks: readonly CrmTaskRow[],\n call: {\n each: (task: CrmTaskRow) => Promise<unknown>\n batch: (tasks: readonly CrmTaskRow[]) => Promise<ReadonlyArray<{ taskId: string; ok: boolean; error?: string }>>\n },\n done: (count: number) => string,\n ) =>\n apply({\n attempted: tasks.length,\n skipped: [],\n job: () =>\n hostId\n ? runCrmBulkCalls(tasks, labelOf, call.each)\n : runCrmBulkBatch(\n tasks,\n (task) => task.$id,\n labelOf,\n async (rows) =>\n (await call.batch(rows)).map(({ taskId, ...rest }) => ({ id: taskId, ...rest })),\n ),\n done,\n }),\n [apply, hostId],\n )\n\n /*\n * A task already done is left alone rather than sent: the route would\n * answer `alreadyDone` and write nothing, and a request that can only\n * answer nothing is not worth its round trip.\n */\n const handleComplete = useCallback(async () => {\n if (!orgId) return\n const open = selectedRows.filter((task) => task.status !== 'done')\n await runCalls(\n open,\n {\n each: (task) => completeCrmTask(user, { hostId: hostId as string, taskId: task.$id }),\n batch: async (tasks) =>\n (await completeCrmTasks(user, { orgId, taskIds: tasks.map((task) => task.$id) }))\n .results,\n },\n (count) => `Completed ${countNoun(count, NOUN)}`,\n )\n }, [selectedRows, runCalls, user, hostId, orgId])\n\n const handleApply = useCallback(async () => {\n if (!pending || !scope) return\n const action = pending\n setPending(null)\n if (action === 'assign') {\n const assigneeUid = value || null\n const who = assigneeUid ? directory.nameOf(assigneeUid) : 'nobody'\n const reassigned = (task: CrmTaskRow) => ({\n taskId: task.$id,\n task: { ...crmTaskFieldsOf(task), assigneeUid },\n })\n await runCalls(\n selectedRows,\n {\n each: (task) => saveCrmTask(user, { hostId: hostId as string, ...reassigned(task) }),\n batch: async (tasks) =>\n (await saveCrmTasks(user, { orgId: scope[1], tasks: tasks.map(reassigned) })).results,\n },\n (count) => `Assigned ${countNoun(count, NOUN)} to ${who}`,\n )\n return\n }\n const dueAtMs = localInputToDueAt(value)\n const writes: CrmBulkWrite[] = selectedRows.map((task) => ({\n id: task.$id,\n label: labelOf(task),\n kind: 'update',\n // `null` rather than a deleted field: every view orders by `dueAtMs`,\n // and a document missing it drops out of the `orderBy` entirely.\n data: { dueAtMs, updatedAt: serverTimestamp() },\n }))\n await runPlan(\n { writes, skipped: [] },\n (count) =>\n dueAtMs === null\n ? `Due date cleared on ${countNoun(count, NOUN)}`\n : `Due date set on ${countNoun(count, NOUN)}`,\n )\n }, [pending, scope, value, directory, selectedRows, runCalls, user, hostId, runPlan])\n\n const handleExport = useCallback(() => {\n downloadTextFile('tasks-selected.csv', 'text/csv', tasksCsv(selectedRows, csv))\n }, [selectedRows, csv])\n\n const handleDelete = useCallback(async () => {\n if (!scope || !selectedRows.length) return\n const count = selectedRows.length\n const confirmed = await confirm({\n title: count === 1 ? 'Delete this task?' : `Delete ${count} tasks?`,\n description:\n `${count === 1 ? 'It is' : 'They are'} removed for everyone who can ` +\n 'see them. A finished task is better ticked done, which keeps it in ' +\n 'the Done view.',\n confirmationText: count === 1 ? 'Delete task' : 'Delete tasks',\n confirmationButtonProps: { color: 'error' },\n })\n // `confirm` resolves with no value and REJECTS on cancel.\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n const writes: CrmBulkWrite[] = selectedRows.map((task) => ({\n id: task.$id,\n label: labelOf(task),\n kind: 'delete',\n }))\n const outcome = await runPlan(\n { writes, skipped: [] },\n (done) => `Deleted ${countNoun(done, NOUN)}`,\n )\n const refused = new Set(outcome.refused.map((row) => row.label))\n onSelectedChange(\n selectedRows.filter((task) => refused.has(labelOf(task))).map((task) => task.$id),\n )\n }, [scope, selectedRows, confirm, runPlan, onSelectedChange])\n\n return (\n <CrmBulkBarFrame\n count={selected.length}\n noun={NOUN}\n busy={busy}\n onClear={() => onSelectedChange([])}\n report={report}\n onDismissReport={dismissReport}\n extras={\n <CrmBulkValueDialog\n open={pending !== null}\n title={pending ? ACTION_TITLES[pending] : ''}\n count={selected.length}\n noun={NOUN}\n busy={busy}\n canApply\n onClose={() => setPending(null)}\n onApply={() => void handleApply()}\n >\n {pending === 'assign' ? (\n <TextField\n select\n size=\"small\"\n label=\"Assignee\"\n value={value}\n onChange={(event) => setValue(event.target.value)}\n disabled={directory.loading}\n error={Boolean(directory.error)}\n helperText={\n directory.error ??\n (directory.loading\n ? 'Loading the team…'\n : 'Somebody other than you is told, as the drawer tells them')\n }\n >\n <MenuItem value=\"\">{'Nobody — clear the assignee'}</MenuItem>\n {directory.members.map((member) => (\n <MenuItem key={member.uid} value={member.uid}>\n {crmMemberPickerLabel(member)}\n </MenuItem>\n ))}\n </TextField>\n ) : pending === 'due' ? (\n <TextField\n size=\"small\"\n type=\"datetime-local\"\n label=\"Due\"\n value={value}\n onChange={(event) => setValue(event.target.value)}\n helperText=\"Leave it empty to clear the due date\"\n slotProps={{ inputLabel: { shrink: true } }}\n />\n ) : null}\n </CrmBulkValueDialog>\n }\n >\n <Button size=\"small\" disabled={busy || !scope} onClick={() => void handleComplete()}>\n {'Complete'}\n </Button>\n <Button size=\"small\" disabled={busy || !scope} onClick={() => openAction('assign')}>\n {'Assign'}\n </Button>\n <Button\n size=\"small\"\n disabled={busy || !scope}\n onClick={() => {\n openAction('due')\n // Start from the first task's own date, so a nudge is an edit\n // rather than a retype.\n setValue(dueAtToLocalInput(selectedRows[0]?.dueAtMs))\n }}\n >\n {'Set due'}\n </Button>\n <Button size=\"small\" disabled={busy} onClick={handleExport}>\n {'Export CSV'}\n </Button>\n {/*\n The selection's file is the rows on screen; this one is the whole\n collection, streamed by the server (AGL-2662).\n */}\n <CrmExportAllButton\n resource=\"tasks\"\n orgId={scope?.[1] ?? null}\n hostId={hostId}\n disabled={busy}\n />\n <Button\n size=\"small\"\n color=\"error\"\n disabled={busy || !scope}\n onClick={() => void handleDelete()}\n >\n {'Delete'}\n </Button>\n </CrmBulkBarFrame>\n )\n}\nTasksBulkBarBody.displayName = 'TasksBulkBarBody'\n\nexport default TasksBulkBar\n"],"names":["CRM_COLLECTIONS","crmMemberPickerLabel","useConfirmationContext","useFirestore","useUser","Button","MenuItem","TextField","doc","serverTimestamp","useCallback","useMemo","useState","useCrmBulkApply","downloadTextFile","refreshCrmNextActivity","crmBulkWriters","runCrmBulkBatch","runCrmBulkCalls","runCrmBulkWrites","completeCrmTask","completeCrmTasks","saveCrmTask","saveCrmTasks","crmTaskCallScope","crmTaskFieldsOf","dueAtToLocalInput","localInputToDueAt","tasksCsv","CrmBulkBarFrame","CrmBulkValueDialog","countNoun","CrmExportAllButton","NOUN","singular","plural","ACTION_TITLES","assign","due","labelOf","task","title","$id","TasksBulkBar","props","selected","length","TasksBulkBarBody","displayName","directory","hostId","scope","rows","onSelectedChange","csv","firestore","data","user","confirm","busy","report","apply","dismissReport","recordKind","selectedRows","chosen","Set","filter","row","has","pending","setPending","value","setValue","writers","id","tasks","openAction","action","orgId","runPlan","plan","done","outcome","attempted","writes","skipped","job","write","label","written","map","runCalls","call","each","batch","taskId","rest","handleComplete","open","status","taskIds","results","count","handleApply","assigneeUid","who","nameOf","reassigned","dueAtMs","kind","updatedAt","handleExport","handleDelete","confirmed","description","confirmationText","confirmationButtonProps","color","then","catch","refused","noun","onClear","onDismissReport","extras","canApply","onClose","onApply","select","size","onChange","event","target","disabled","loading","error","Boolean","helperText","members","member","uid","type","slotProps","inputLabel","shrink","onClick","resource"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;;;AAEA;;;;;;;;;;;;;;;;CAgBC,GAED,SAASA,eAAe,EAAEC,oBAAoB,QAAQ,eAAc;AACpE,SAASC,sBAAsB,QAAQ,uBAAsB;AAC7D,SAASC,YAAY,EAAEC,OAAO,QAAQ,iCAAgC;AACtE,SAASC,MAAM,EAAEC,QAAQ,EAAEC,SAAS,QAAQ,gBAAe;AAC3D,SAASC,GAAG,EAAEC,eAAe,QAAQ,qBAAoB;AACzD,SAASC,WAAW,EAAEC,OAAO,EAAEC,QAAQ,QAAQ,QAAO;AACtD,SAASC,eAAe,QAAQ,iCAA6B;AAG7D,SAASC,gBAAgB,QAAQ,2BAAuB;AACxD,SAASC,sBAAsB,QAAQ,gCAA4B;AACnE,SAGEC,cAAc,EACdC,eAAe,EACfC,eAAe,EACfC,gBAAgB,QACX,8BAA0B;AACjC,SACEC,eAAe,EACfC,gBAAgB,EAChBC,WAAW,EACXC,YAAY,QACP,uBAAmB;AAC1B,SAASC,gBAAgB,EAAEC,eAAe,QAAQ,0BAAsB;AACxE,SAASC,iBAAiB,EAAEC,iBAAiB,QAAQ,yBAAqB;AAC1E,SAA8BC,QAAQ,QAAQ,wBAAoB;AAClE,SAEEC,eAAe,EACfC,kBAAkB,EAClBC,SAAS,QACJ,0BAAsB;AAC7B,OAAOC,wBAAwB,6BAAyB;AAoBxD,MAAMC,OAAoB;IAAEC,UAAU;IAAQC,QAAQ;AAAQ;AAI9D,MAAMC,gBAA+C;IACnDC,QAAQ;IACRC,KAAK;AACP;AAEA,MAAMC,UAAU,CAACC,OAA6BA,KAAKC,KAAK,IAAID,KAAKE,GAAG;AAEpE,OAAO,SAASC,aAAaC,KAAwB;IACnD,IAAI,CAACA,MAAMC,QAAQ,CAACC,MAAM,EAAE,OAAO;IACnC,qBAAO,KAACC,+BAAqBH;AAC/B;AACAD,aAAaK,WAAW,GAAG;AAE3B,SAASD,iBAAiBH,KAAwB;cAgNlCK;IA/Md,MAAM,EAAEC,MAAM,EAAEC,KAAK,EAAEC,IAAI,EAAEP,QAAQ,EAAEQ,gBAAgB,EAAEJ,SAAS,EAAEK,GAAG,EAAE,GAAGV;IAC5E,MAAMW,YAAYpD;IAClB,MAAM,EAAEqD,MAAMC,IAAI,EAAE,GAAGrD;IACvB,MAAM,EAAEsD,OAAO,EAAE,GAAGxD;IACpB,MAAM,EAAEyD,IAAI,EAAEC,MAAM,EAAEC,KAAK,EAAEC,aAAa,EAAE,GAAGjD,gBAAgB;QAAEkD,YAAY;IAAO;IAEpF,MAAMC,eAAerD,QAAQ;QAC3B,MAAMsD,SAAS,IAAIC,IAAIrB;QACvB,OAAOO,KAAKe,MAAM,CAAC,CAACC,MAAQH,OAAOI,GAAG,CAACD,IAAI1B,GAAG;IAChD,GAAG;QAACU;QAAMP;KAAS;IAEnB,MAAM,CAACyB,SAASC,WAAW,GAAG3D,SAA+B;IAC7D,MAAM,CAAC4D,OAAOC,SAAS,GAAG7D,SAAS;IAEnC,MAAM8D,UAAU/D,QACd,IACEK,eAAeuC,WAAW,CAACoB;;mBACzBnE,IAAI+C,mBAAWJ,yBAAAA,KAAO,CAAC,EAAE,mBAAI,iBAAQA,yBAAAA,KAAO,CAAC,EAAE,oBAAI,IAAInD,gBAAgB4E,KAAK,EAAED;YAElF;QAACpB;QAAWJ;KAAM;IAGpB,MAAM0B,aAAa,CAACC;QAClBL,SAAS;QACTF,WAAWO;IACb;IAEA,uEAAuE;IACvE,MAAMC,gBAAQ5B,yBAAAA,KAAO,CAAC,EAAE,mBAAI;IAE5B,MAAM6B,UAAUtE,YACd,OAAOuE,MAAmBC;QACxB,MAAMC,UAAU,MAAMtB,MAAM;YAC1BuB,WAAWH,KAAKI,MAAM,CAACvC,MAAM;YAC7BwC,SAASL,KAAKK,OAAO;YACrBC,KAAK,IAAMpE,iBAAiBuD,SAASO,KAAKI,MAAM,EAAE,CAACG,QAAUA,MAAMC,KAAK;YACxEP;QACF;QACA,iEAAiE;QACjE,uDAAuD;QACvD,MAAMQ,UAAU,IAAIxB,IAAIe,KAAKI,MAAM,CAACM,GAAG,CAAC,CAACH,QAAUA,MAAMb,EAAE;QAC3D,MAAM5D,uBACJ0C,MACAjC,iBAAiB0B,QAAQ6B,QACzBf,aAAaG,MAAM,CAAC,CAAC3B,OAASkD,QAAQrB,GAAG,CAAC7B,KAAKE,GAAG;QAEpD,OAAOyC;IACT,GACA;QAACtB;QAAOa;QAASjB;QAAMP;QAAQ6B;QAAOf;KAAa;IAGrD;;;;GAIC,GACD,MAAM4B,WAAWlF,YACf,CACEkE,OACAiB,MAIAX,OAEArB,MAAM;YACJuB,WAAWR,MAAM9B,MAAM;YACvBwC,SAAS,EAAE;YACXC,KAAK,IACHrC,SACIhC,gBAAgB0D,OAAOrC,SAASsD,KAAKC,IAAI,IACzC7E,gBACE2D,OACA,CAACpC,OAASA,KAAKE,GAAG,EAClBH,SACA,OAAOa,OACL,AAAC,CAAA,MAAMyC,KAAKE,KAAK,CAAC3C,KAAI,EAAGuC,GAAG,CAAC;4BAAC,EAAEK,MAAM,EAAW,OAANC;;;+BAAY;4BAAEtB,IAAIqB;2BAAWC;;YAElFf;QACF,IACF;QAACrB;QAAOX;KAAO;IAGjB;;;;GAIC,GACD,MAAMgD,iBAAiBxF,YAAY;QACjC,IAAI,CAACqE,OAAO;QACZ,MAAMoB,OAAOnC,aAAaG,MAAM,CAAC,CAAC3B,OAASA,KAAK4D,MAAM,KAAK;QAC3D,MAAMR,SACJO,MACA;YACEL,MAAM,CAACtD,OAASpB,gBAAgBqC,MAAM;oBAAEP,QAAQA;oBAAkB8C,QAAQxD,KAAKE,GAAG;gBAAC;YACnFqD,OAAO,OAAOnB,QACZ,AAAC,CAAA,MAAMvD,iBAAiBoC,MAAM;oBAAEsB;oBAAOsB,SAASzB,MAAMe,GAAG,CAAC,CAACnD,OAASA,KAAKE,GAAG;gBAAE,EAAC,EAC5E4D,OAAO;QACd,GACA,CAACC,QAAU,CAAC,UAAU,EAAExE,UAAUwE,OAAOtE,OAAO;IAEpD,GAAG;QAAC+B;QAAc4B;QAAUnC;QAAMP;QAAQ6B;KAAM;IAEhD,MAAMyB,cAAc9F,YAAY;QAC9B,IAAI,CAAC4D,WAAW,CAACnB,OAAO;QACxB,MAAM2B,SAASR;QACfC,WAAW;QACX,IAAIO,WAAW,UAAU;YACvB,MAAM2B,cAAcjC,SAAS;YAC7B,MAAMkC,MAAMD,cAAcxD,UAAU0D,MAAM,CAACF,eAAe;YAC1D,MAAMG,aAAa,CAACpE,OAAsB,CAAA;oBACxCwD,QAAQxD,KAAKE,GAAG;oBAChBF,MAAM,aAAKf,gBAAgBe;wBAAOiE;;gBACpC,CAAA;YACA,MAAMb,SACJ5B,cACA;gBACE8B,MAAM,CAACtD,OAASlB,YAAYmC,MAAM;wBAAEP,QAAQA;uBAAqB0D,WAAWpE;gBAC5EuD,OAAO,OAAOnB,QACZ,AAAC,CAAA,MAAMrD,aAAakC,MAAM;wBAAEsB,OAAO5B,KAAK,CAAC,EAAE;wBAAEyB,OAAOA,MAAMe,GAAG,CAACiB;oBAAY,EAAC,EAAGN,OAAO;YACzF,GACA,CAACC,QAAU,CAAC,SAAS,EAAExE,UAAUwE,OAAOtE,MAAM,IAAI,EAAEyE,KAAK;YAE3D;QACF;QACA,MAAMG,UAAUlF,kBAAkB6C;QAClC,MAAMa,SAAyBrB,aAAa2B,GAAG,CAAC,CAACnD,OAAU,CAAA;gBACzDmC,IAAInC,KAAKE,GAAG;gBACZ+C,OAAOlD,QAAQC;gBACfsE,MAAM;gBACN,sEAAsE;gBACtE,iEAAiE;gBACjEtD,MAAM;oBAAEqD;oBAASE,WAAWtG;gBAAkB;YAChD,CAAA;QACA,MAAMuE,QACJ;YAAEK;YAAQC,SAAS,EAAE;QAAC,GACtB,CAACiB,QACCM,YAAY,OACR,CAAC,oBAAoB,EAAE9E,UAAUwE,OAAOtE,OAAO,GAC/C,CAAC,gBAAgB,EAAEF,UAAUwE,OAAOtE,OAAO;IAErD,GAAG;QAACqC;QAASnB;QAAOqB;QAAOvB;QAAWe;QAAc4B;QAAUnC;QAAMP;QAAQ8B;KAAQ;IAEpF,MAAMgC,eAAetG,YAAY;QAC/BI,iBAAiB,sBAAsB,YAAYc,SAASoC,cAAcV;IAC5E,GAAG;QAACU;QAAcV;KAAI;IAEtB,MAAM2D,eAAevG,YAAY;QAC/B,IAAI,CAACyC,SAAS,CAACa,aAAalB,MAAM,EAAE;QACpC,MAAMyD,QAAQvC,aAAalB,MAAM;QACjC,MAAMoE,YAAY,MAAMxD,QAAQ;YAC9BjB,OAAO8D,UAAU,IAAI,sBAAsB,CAAC,OAAO,EAAEA,MAAM,OAAO,CAAC;YACnEY,aACE,GAAGZ,UAAU,IAAI,UAAU,WAAW,8BAA8B,CAAC,GACrE,wEACA;YACFa,kBAAkBb,UAAU,IAAI,gBAAgB;YAChDc,yBAAyB;gBAAEC,OAAO;YAAQ;QAC5C,EACE,0DAA0D;SACzDC,IAAI,CAAC,IAAM,MACXC,KAAK,CAAC,IAAM;QACf,IAAI,CAACN,WAAW;QAChB,MAAM7B,SAAyBrB,aAAa2B,GAAG,CAAC,CAACnD,OAAU,CAAA;gBACzDmC,IAAInC,KAAKE,GAAG;gBACZ+C,OAAOlD,QAAQC;gBACfsE,MAAM;YACR,CAAA;QACA,MAAM3B,UAAU,MAAMH,QACpB;YAAEK;YAAQC,SAAS,EAAE;QAAC,GACtB,CAACJ,OAAS,CAAC,QAAQ,EAAEnD,UAAUmD,MAAMjD,OAAO;QAE9C,MAAMwF,UAAU,IAAIvD,IAAIiB,QAAQsC,OAAO,CAAC9B,GAAG,CAAC,CAACvB,MAAQA,IAAIqB,KAAK;QAC9DpC,iBACEW,aAAaG,MAAM,CAAC,CAAC3B,OAASiF,QAAQpD,GAAG,CAAC9B,QAAQC,QAAQmD,GAAG,CAAC,CAACnD,OAASA,KAAKE,GAAG;IAEpF,GAAG;QAACS;QAAOa;QAAcN;QAASsB;QAAS3B;KAAiB;IAE5D,qBACE,MAACxB;QACC0E,OAAO1D,SAASC,MAAM;QACtB4E,MAAMzF;QACN0B,MAAMA;QACNgE,SAAS,IAAMtE,iBAAiB,EAAE;QAClCO,QAAQA;QACRgE,iBAAiB9D;QACjB+D,sBACE,KAAC/F;YACCqE,MAAM7B,YAAY;YAClB7B,OAAO6B,UAAUlC,aAAa,CAACkC,QAAQ,GAAG;YAC1CiC,OAAO1D,SAASC,MAAM;YACtB4E,MAAMzF;YACN0B,MAAMA;YACNmE,QAAQ;YACRC,SAAS,IAAMxD,WAAW;YAC1ByD,SAAS,IAAM,KAAKxB;sBAEnBlC,YAAY,yBACX,MAAC/D;gBACC0H,MAAM;gBACNC,MAAK;gBACLzC,OAAM;gBACNjB,OAAOA;gBACP2D,UAAU,CAACC,QAAU3D,SAAS2D,MAAMC,MAAM,CAAC7D,KAAK;gBAChD8D,UAAUrF,UAAUsF,OAAO;gBAC3BC,OAAOC,QAAQxF,UAAUuF,KAAK;gBAC9BE,UAAU,GACRzF,mBAAAA,UAAUuF,KAAK,YAAfvF,mBACCA,UAAUsF,OAAO,GACd,sBACA;;kCAGN,KAACjI;wBAASkE,OAAM;kCAAI;;oBACnBvB,UAAU0F,OAAO,CAAChD,GAAG,CAAC,CAACiD,uBACtB,KAACtI;4BAA0BkE,OAAOoE,OAAOC,GAAG;sCACzC5I,qBAAqB2I;2BADTA,OAAOC,GAAG;;iBAK3BvE,YAAY,sBACd,KAAC/D;gBACC2H,MAAK;gBACLY,MAAK;gBACLrD,OAAM;gBACNjB,OAAOA;gBACP2D,UAAU,CAACC,QAAU3D,SAAS2D,MAAMC,MAAM,CAAC7D,KAAK;gBAChDkE,YAAW;gBACXK,WAAW;oBAAEC,YAAY;wBAAEC,QAAQ;oBAAK;gBAAE;iBAE1C;;;0BAIR,KAAC5I;gBAAO6H,MAAK;gBAAQI,UAAU3E,QAAQ,CAACR;gBAAO+F,SAAS,IAAM,KAAKhD;0BAChE;;0BAEH,KAAC7F;gBAAO6H,MAAK;gBAAQI,UAAU3E,QAAQ,CAACR;gBAAO+F,SAAS,IAAMrE,WAAW;0BACtE;;0BAEH,KAACxE;gBACC6H,MAAK;gBACLI,UAAU3E,QAAQ,CAACR;gBACnB+F,SAAS;wBAIoBlF;oBAH3Ba,WAAW;oBACX,8DAA8D;oBAC9D,wBAAwB;oBACxBJ,SAAS/C,mBAAkBsC,iBAAAA,YAAY,CAAC,EAAE,qBAAfA,eAAiB6C,OAAO;gBACrD;0BAEC;;0BAEH,KAACxG;gBAAO6H,MAAK;gBAAQI,UAAU3E;gBAAMuF,SAASlC;0BAC3C;;0BAMH,KAAChF;gBACCmH,UAAS;gBACTpE,KAAK,WAAE5B,yBAAAA,KAAO,CAAC,EAAE,oBAAI;gBACrBD,QAAQA;gBACRoF,UAAU3E;;0BAEZ,KAACtD;gBACC6H,MAAK;gBACLZ,OAAM;gBACNgB,UAAU3E,QAAQ,CAACR;gBACnB+F,SAAS,IAAM,KAAKjC;0BAEnB;;;;AAIT;AACAlE,iBAAiBC,WAAW,GAAG;AAE/B,eAAeL,aAAY"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/tasks-bulk-bar.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\n/**\n * The bar over the tasks list, for whatever rows are ticked (AGL-2621).\n *\n * Complete them, hand them to somebody, move their due date, take them\n * into a spreadsheet, or delete them. Two of these have a side effect\n * outside the document and go through their ROUTE: completing fires\n * `taskCompleted`, which only the server emits, and assigning tells the\n * assignee, which only the server may write into somebody's inbox — so\n * Complete goes through `crm/task-complete` and Assign through\n * `crm/task-save`, exactly as the checkbox and the drawer do for one task.\n * Under a site that is one request per task, each authorized against the\n * site; beneath the organization hub it is ONE request per action, the\n * routes' org-level batch form (AGL-2637), authorized once by the org and\n * answered per task. The due date and the delete have no effect beyond\n * the document, so they are batched client-direct writes under the rules,\n * as reopening is.\n */\n\nimport { CRM_COLLECTIONS, crmMemberPickerLabel } from '@aglyn/aglyn'\nimport { useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport { useFirestore, useUser } from '@aglyn/tenant-feature-instance'\nimport { Button, MenuItem, TextField } from '@mui/material'\nimport { doc, serverTimestamp } from 'firebase/firestore'\nimport { useCallback, useMemo, useState } from 'react'\nimport { useCrmBulkApply } from '../hooks/use-crm-bulk-apply'\nimport type { CrmTaskRow } from '../hooks/use-crm-tasks'\nimport type { OrgMemberDirectory } from '../hooks/use-org-member-directory'\nimport { refreshCrmNextActivity } from '../model/next-activity-api'\nimport {\n type CrmBulkPlan,\n type CrmBulkWrite,\n crmBulkWriters,\n runCrmBulkBatch,\n runCrmBulkCalls,\n runCrmBulkWrites,\n} from '../model/crm-bulk-writes'\nimport {\n completeCrmTask,\n completeCrmTasks,\n saveCrmTask,\n saveCrmTasks,\n} from '../model/task-api'\nimport { crmTaskCallScope, crmTaskFieldsOf } from '../model/task-routes'\nimport { dueAtToLocalInput, localInputToDueAt } from '../model/task-views'\nimport {\n type CrmBulkNoun,\n CrmBulkBarFrame,\n CrmBulkValueDialog,\n countNoun,\n} from './crm-bulk-bar-frame'\nimport { CrmExportButton } from './crm-transfer-buttons'\nimport { CRM_TASKS_RESOURCE } from '../transfer/fields'\n\nexport interface TasksBulkBarProps {\n /**\n * The site the task routes run as, or `null` at the organization level\n * (AGL-2630), where the routes run as the org — their batch form, one\n * request per action (AGL-2637).\n */\n hostId: string | null\n /** `['orgs', orgId]`, or `null` while the org is unresolved. */\n scope: readonly [string, string] | null\n rows: readonly CrmTaskRow[]\n selected: readonly string[]\n onSelectedChange: (ids: string[]) => void\n /** The section's roster — already read for the Assignee column. */\n directory: OrgMemberDirectory\n}\n\nconst NOUN: CrmBulkNoun = { singular: 'task', plural: 'tasks' }\n\ntype PendingAction = 'assign' | 'due'\n\nconst ACTION_TITLES: Record<PendingAction, string> = {\n assign: 'Assign to',\n due: 'Set the due date',\n}\n\nconst labelOf = (task: CrmTaskRow): string => task.title || task.$id\n\nexport function TasksBulkBar(props: TasksBulkBarProps) {\n if (!props.selected.length) return null\n return <TasksBulkBarBody {...props} />\n}\nTasksBulkBar.displayName = 'TasksBulkBar'\n\nfunction TasksBulkBarBody(props: TasksBulkBarProps) {\n const { hostId, scope, rows, selected, onSelectedChange, directory } = props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { confirm } = useConfirmationContext()\n const { busy, report, apply, dismissReport } = useCrmBulkApply({ recordKind: 'task' })\n\n const selectedRows = useMemo(() => {\n const chosen = new Set(selected)\n return rows.filter((row) => chosen.has(row.$id))\n }, [rows, selected])\n\n const [pending, setPending] = useState<PendingAction | null>(null)\n const [value, setValue] = useState('')\n\n const writers = useMemo(\n () =>\n crmBulkWriters(firestore, (id) =>\n doc(firestore, scope?.[0] ?? 'orgs', scope?.[1] ?? '', CRM_COLLECTIONS.tasks, id),\n ),\n [firestore, scope],\n )\n\n const openAction = (action: PendingAction) => {\n setValue('')\n setPending(action)\n }\n\n // The org the batch form names — the scope's, which is the org's root.\n const orgId = scope?.[1] ?? null\n\n const runPlan = useCallback(\n async (plan: CrmBulkPlan, done: (count: number) => string) => {\n const outcome = await apply({\n attempted: plan.writes.length,\n skipped: plan.skipped,\n job: () => runCrmBulkWrites(writers, plan.writes, (write) => write.label),\n done,\n })\n // Client-direct writes: the records the selection names are told\n // (AGL-2661), once each however many tasks named them.\n const written = new Set(plan.writes.map((write) => write.id))\n await refreshCrmNextActivity(\n user,\n crmTaskCallScope(hostId, orgId),\n selectedRows.filter((task) => written.has(task.$id)),\n )\n return outcome\n },\n [apply, writers, user, hostId, orgId, selectedRows],\n )\n\n /**\n * The routes over the rows: under a site one request per task, in\n * order, named by title; beneath the org hub one request for them all,\n * tallied off its per-task answers.\n */\n const runCalls = useCallback(\n (\n tasks: readonly CrmTaskRow[],\n call: {\n each: (task: CrmTaskRow) => Promise<unknown>\n batch: (tasks: readonly CrmTaskRow[]) => Promise<ReadonlyArray<{ taskId: string; ok: boolean; error?: string }>>\n },\n done: (count: number) => string,\n ) =>\n apply({\n attempted: tasks.length,\n skipped: [],\n job: () =>\n hostId\n ? runCrmBulkCalls(tasks, labelOf, call.each)\n : runCrmBulkBatch(\n tasks,\n (task) => task.$id,\n labelOf,\n async (rows) =>\n (await call.batch(rows)).map(({ taskId, ...rest }) => ({ id: taskId, ...rest })),\n ),\n done,\n }),\n [apply, hostId],\n )\n\n /*\n * A task already done is left alone rather than sent: the route would\n * answer `alreadyDone` and write nothing, and a request that can only\n * answer nothing is not worth its round trip.\n */\n const handleComplete = useCallback(async () => {\n if (!orgId) return\n const open = selectedRows.filter((task) => task.status !== 'done')\n await runCalls(\n open,\n {\n each: (task) => completeCrmTask(user, { hostId: hostId as string, taskId: task.$id }),\n batch: async (tasks) =>\n (await completeCrmTasks(user, { orgId, taskIds: tasks.map((task) => task.$id) }))\n .results,\n },\n (count) => `Completed ${countNoun(count, NOUN)}`,\n )\n }, [selectedRows, runCalls, user, hostId, orgId])\n\n const handleApply = useCallback(async () => {\n if (!pending || !scope) return\n const action = pending\n setPending(null)\n if (action === 'assign') {\n const assigneeUid = value || null\n const who = assigneeUid ? directory.nameOf(assigneeUid) : 'nobody'\n const reassigned = (task: CrmTaskRow) => ({\n taskId: task.$id,\n task: { ...crmTaskFieldsOf(task), assigneeUid },\n })\n await runCalls(\n selectedRows,\n {\n each: (task) => saveCrmTask(user, { hostId: hostId as string, ...reassigned(task) }),\n batch: async (tasks) =>\n (await saveCrmTasks(user, { orgId: scope[1], tasks: tasks.map(reassigned) })).results,\n },\n (count) => `Assigned ${countNoun(count, NOUN)} to ${who}`,\n )\n return\n }\n const dueAtMs = localInputToDueAt(value)\n const writes: CrmBulkWrite[] = selectedRows.map((task) => ({\n id: task.$id,\n label: labelOf(task),\n kind: 'update',\n // `null` rather than a deleted field: every view orders by `dueAtMs`,\n // and a document missing it drops out of the `orderBy` entirely.\n data: { dueAtMs, updatedAt: serverTimestamp() },\n }))\n await runPlan(\n { writes, skipped: [] },\n (count) =>\n dueAtMs === null\n ? `Due date cleared on ${countNoun(count, NOUN)}`\n : `Due date set on ${countNoun(count, NOUN)}`,\n )\n }, [pending, scope, value, directory, selectedRows, runCalls, user, hostId, runPlan])\n\n const handleDelete = useCallback(async () => {\n if (!scope || !selectedRows.length) return\n const count = selectedRows.length\n const confirmed = await confirm({\n title: count === 1 ? 'Delete this task?' : `Delete ${count} tasks?`,\n description:\n `${count === 1 ? 'It is' : 'They are'} removed for everyone who can ` +\n 'see them. A finished task is better ticked done, which keeps it in ' +\n 'the Done view.',\n confirmationText: count === 1 ? 'Delete task' : 'Delete tasks',\n confirmationButtonProps: { color: 'error' },\n })\n // `confirm` resolves with no value and REJECTS on cancel.\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n const writes: CrmBulkWrite[] = selectedRows.map((task) => ({\n id: task.$id,\n label: labelOf(task),\n kind: 'delete',\n }))\n const outcome = await runPlan(\n { writes, skipped: [] },\n (done) => `Deleted ${countNoun(done, NOUN)}`,\n )\n const refused = new Set(outcome.refused.map((row) => row.label))\n onSelectedChange(\n selectedRows.filter((task) => refused.has(labelOf(task))).map((task) => task.$id),\n )\n }, [scope, selectedRows, confirm, runPlan, onSelectedChange])\n\n return (\n <CrmBulkBarFrame\n count={selected.length}\n noun={NOUN}\n busy={busy}\n onClear={() => onSelectedChange([])}\n report={report}\n onDismissReport={dismissReport}\n extras={\n <CrmBulkValueDialog\n open={pending !== null}\n title={pending ? ACTION_TITLES[pending] : ''}\n count={selected.length}\n noun={NOUN}\n busy={busy}\n canApply\n onClose={() => setPending(null)}\n onApply={() => void handleApply()}\n >\n {pending === 'assign' ? (\n <TextField\n select\n size=\"small\"\n label=\"Assignee\"\n value={value}\n onChange={(event) => setValue(event.target.value)}\n disabled={directory.loading}\n error={Boolean(directory.error)}\n helperText={\n directory.error ??\n (directory.loading\n ? 'Loading the team…'\n : 'Somebody other than you is told, as the drawer tells them')\n }\n >\n <MenuItem value=\"\">{'Nobody — clear the assignee'}</MenuItem>\n {directory.members.map((member) => (\n <MenuItem key={member.uid} value={member.uid}>\n {crmMemberPickerLabel(member)}\n </MenuItem>\n ))}\n </TextField>\n ) : pending === 'due' ? (\n <TextField\n size=\"small\"\n type=\"datetime-local\"\n label=\"Due\"\n value={value}\n onChange={(event) => setValue(event.target.value)}\n helperText=\"Leave it empty to clear the due date\"\n slotProps={{ inputLabel: { shrink: true } }}\n />\n ) : null}\n </CrmBulkValueDialog>\n }\n >\n <Button size=\"small\" disabled={busy || !scope} onClick={() => void handleComplete()}>\n {'Complete'}\n </Button>\n <Button size=\"small\" disabled={busy || !scope} onClick={() => openAction('assign')}>\n {'Assign'}\n </Button>\n <Button\n size=\"small\"\n disabled={busy || !scope}\n onClick={() => {\n openAction('due')\n // Start from the first task's own date, so a nudge is an edit\n // rather than a retype.\n setValue(dueAtToLocalInput(selectedRows[0]?.dueAtMs))\n }}\n >\n {'Set due'}\n </Button>\n {/* The selection through the export dialog: every field, chosen (AGL-3528). */}\n <CrmExportButton\n resource={CRM_TASKS_RESOURCE}\n hostId={hostId}\n selection={selectedRows.map((row) => row.$id)}\n disabled={busy}\n />\n <Button\n size=\"small\"\n color=\"error\"\n disabled={busy || !scope}\n onClick={() => void handleDelete()}\n >\n {'Delete'}\n </Button>\n </CrmBulkBarFrame>\n )\n}\nTasksBulkBarBody.displayName = 'TasksBulkBarBody'\n\nexport default TasksBulkBar\n"],"names":["CRM_COLLECTIONS","crmMemberPickerLabel","useConfirmationContext","useFirestore","useUser","Button","MenuItem","TextField","doc","serverTimestamp","useCallback","useMemo","useState","useCrmBulkApply","refreshCrmNextActivity","crmBulkWriters","runCrmBulkBatch","runCrmBulkCalls","runCrmBulkWrites","completeCrmTask","completeCrmTasks","saveCrmTask","saveCrmTasks","crmTaskCallScope","crmTaskFieldsOf","dueAtToLocalInput","localInputToDueAt","CrmBulkBarFrame","CrmBulkValueDialog","countNoun","CrmExportButton","CRM_TASKS_RESOURCE","NOUN","singular","plural","ACTION_TITLES","assign","due","labelOf","task","title","$id","TasksBulkBar","props","selected","length","TasksBulkBarBody","displayName","directory","hostId","scope","rows","onSelectedChange","firestore","data","user","confirm","busy","report","apply","dismissReport","recordKind","selectedRows","chosen","Set","filter","row","has","pending","setPending","value","setValue","writers","id","tasks","openAction","action","orgId","runPlan","plan","done","outcome","attempted","writes","skipped","job","write","label","written","map","runCalls","call","each","batch","taskId","rest","handleComplete","open","status","taskIds","results","count","handleApply","assigneeUid","who","nameOf","reassigned","dueAtMs","kind","updatedAt","handleDelete","confirmed","description","confirmationText","confirmationButtonProps","color","then","catch","refused","noun","onClear","onDismissReport","extras","canApply","onClose","onApply","select","size","onChange","event","target","disabled","loading","error","Boolean","helperText","members","member","uid","type","slotProps","inputLabel","shrink","onClick","resource","selection"],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type CrmTaskPicklists } from '@aglyn/aglyn';
|
|
1
2
|
import type { CrmTaskRow } from '../hooks/use-crm-tasks';
|
|
2
3
|
export interface TasksCalendarProps {
|
|
3
4
|
/** The window the list is holding — the calendar reads no more than it. */
|
|
@@ -6,6 +7,8 @@ export interface TasksCalendarProps {
|
|
|
6
7
|
onOpen: (task: CrmTaskRow) => void;
|
|
7
8
|
/** The list's window filled, so neither surface is showing everything. */
|
|
8
9
|
truncated?: boolean;
|
|
10
|
+
/** The org's task lists, which name each entry's type and status on hover (AGL-3517). */
|
|
11
|
+
picklists?: CrmTaskPicklists;
|
|
9
12
|
}
|
|
10
13
|
/**
|
|
11
14
|
* A MONTH OF TASKS, BY DUE DATE (AGL-2662).
|