@aglyn/plugins-crm 1.0.0-beta.296 → 1.1.0

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.
Files changed (161) hide show
  1. package/package.json +14 -14
  2. package/src/lib/components/activity-link-fields.d.ts +34 -0
  3. package/src/lib/components/activity-link-fields.js +141 -0
  4. package/src/lib/components/activity-link-fields.js.map +1 -0
  5. package/src/lib/components/activity-list.d.ts +8 -0
  6. package/src/lib/components/activity-list.js +4 -2
  7. package/src/lib/components/activity-list.js.map +1 -1
  8. package/src/lib/components/activity-queries.d.ts +7 -2
  9. package/src/lib/components/activity-queries.js +7 -3
  10. package/src/lib/components/activity-queries.js.map +1 -1
  11. package/src/lib/components/company-contact-roles-card.d.ts +42 -0
  12. package/src/lib/components/company-contact-roles-card.js +135 -0
  13. package/src/lib/components/company-contact-roles-card.js.map +1 -0
  14. package/src/lib/components/company-detail-page.js +12 -1
  15. package/src/lib/components/company-detail-page.js.map +1 -1
  16. package/src/lib/components/company-edit-drawer.js +9 -6
  17. package/src/lib/components/company-edit-drawer.js.map +1 -1
  18. package/src/lib/components/company-picker.js +4 -2
  19. package/src/lib/components/company-picker.js.map +1 -1
  20. package/src/lib/components/contact-associations-card.js +15 -1
  21. package/src/lib/components/contact-associations-card.js.map +1 -1
  22. package/src/lib/components/contact-detail-page.js +13 -14
  23. package/src/lib/components/contact-detail-page.js.map +1 -1
  24. package/src/lib/components/contact-list-columns.js +5 -4
  25. package/src/lib/components/contact-list-columns.js.map +1 -1
  26. package/src/lib/components/contact-properties-card.js +41 -12
  27. package/src/lib/components/contact-properties-card.js.map +1 -1
  28. package/src/lib/components/contact-roles-editor.d.ts +54 -0
  29. package/src/lib/components/contact-roles-editor.js +270 -0
  30. package/src/lib/components/contact-roles-editor.js.map +1 -0
  31. package/src/lib/components/contacts-section.js +6 -4
  32. package/src/lib/components/contacts-section.js.map +1 -1
  33. package/src/lib/components/crm-console-sections.js +1 -1
  34. package/src/lib/components/crm-console-sections.js.map +1 -1
  35. package/src/lib/components/crm-record-picker.d.ts +19 -0
  36. package/src/lib/components/crm-record-picker.js +40 -11
  37. package/src/lib/components/crm-record-picker.js.map +1 -1
  38. package/src/lib/components/crm-related-to-picker.d.ts +33 -0
  39. package/src/lib/components/crm-related-to-picker.js +135 -0
  40. package/src/lib/components/crm-related-to-picker.js.map +1 -0
  41. package/src/lib/components/crm-site-picker.d.ts +13 -1
  42. package/src/lib/components/crm-site-picker.js +15 -6
  43. package/src/lib/components/crm-site-picker.js.map +1 -1
  44. package/src/lib/components/crm-transfer-buttons.d.ts +5 -0
  45. package/src/lib/components/crm-transfer-buttons.js +9 -6
  46. package/src/lib/components/crm-transfer-buttons.js.map +1 -1
  47. package/src/lib/components/deal-contact-roles-card.js +21 -239
  48. package/src/lib/components/deal-contact-roles-card.js.map +1 -1
  49. package/src/lib/components/deal-detail-page.js +2 -1
  50. package/src/lib/components/deal-detail-page.js.map +1 -1
  51. package/src/lib/components/deal-edit-drawer.js +11 -7
  52. package/src/lib/components/deal-edit-drawer.js.map +1 -1
  53. package/src/lib/components/deals-section.js +5 -4
  54. package/src/lib/components/deals-section.js.map +1 -1
  55. package/src/lib/components/lead-campaigns-card.js +26 -14
  56. package/src/lib/components/lead-campaigns-card.js.map +1 -1
  57. package/src/lib/components/log-activity-dialog.d.ts +27 -9
  58. package/src/lib/components/log-activity-dialog.js +96 -32
  59. package/src/lib/components/log-activity-dialog.js.map +1 -1
  60. package/src/lib/components/new-contact-drawer.d.ts +7 -3
  61. package/src/lib/components/new-contact-drawer.js +27 -15
  62. package/src/lib/components/new-contact-drawer.js.map +1 -1
  63. package/src/lib/components/picklist-values-card.js +7 -4
  64. package/src/lib/components/picklist-values-card.js.map +1 -1
  65. package/src/lib/components/pipeline-stages-dialog.js +5 -1
  66. package/src/lib/components/pipeline-stages-dialog.js.map +1 -1
  67. package/src/lib/components/record-activity-card.d.ts +11 -2
  68. package/src/lib/components/record-activity-card.js +90 -5
  69. package/src/lib/components/record-activity-card.js.map +1 -1
  70. package/src/lib/components/settings-section.d.ts +4 -2
  71. package/src/lib/components/settings-section.js +7 -5
  72. package/src/lib/components/settings-section.js.map +1 -1
  73. package/src/lib/hooks/crm-activity-client-reads.d.ts +8 -0
  74. package/src/lib/hooks/crm-activity-client-reads.js +50 -0
  75. package/src/lib/hooks/crm-activity-client-reads.js.map +1 -0
  76. package/src/lib/hooks/use-campaign-filing-log.d.ts +2 -1
  77. package/src/lib/hooks/use-campaign-filing-log.js +10 -3
  78. package/src/lib/hooks/use-campaign-filing-log.js.map +1 -1
  79. package/src/lib/hooks/use-crm-org-mount.d.ts +8 -6
  80. package/src/lib/hooks/use-crm-org-mount.js +2 -1
  81. package/src/lib/hooks/use-crm-org-mount.js.map +1 -1
  82. package/src/lib/hooks/use-crm-scope.d.ts +17 -5
  83. package/src/lib/hooks/use-crm-scope.js +12 -5
  84. package/src/lib/hooks/use-crm-scope.js.map +1 -1
  85. package/src/lib/model/contact-holder.d.ts +12 -0
  86. package/src/lib/model/contact-holder.js +20 -1
  87. package/src/lib/model/contact-holder.js.map +1 -1
  88. package/src/lib/model/contact-record.d.ts +5 -0
  89. package/src/lib/model/contact-record.js +7 -1
  90. package/src/lib/model/contact-record.js.map +1 -1
  91. package/src/lib/model/contact-update.d.ts +6 -0
  92. package/src/lib/model/contact-update.js +1 -0
  93. package/src/lib/model/contact-update.js.map +1 -1
  94. package/src/lib/model/crm-activity-link.d.ts +117 -0
  95. package/src/lib/model/crm-activity-link.js +113 -0
  96. package/src/lib/model/crm-activity-link.js.map +1 -0
  97. package/src/lib/model/crm-api.d.ts +4 -1
  98. package/src/lib/model/crm-api.js +9 -5
  99. package/src/lib/model/crm-api.js.map +1 -1
  100. package/src/lib/model/deal-board-model.js +5 -3
  101. package/src/lib/model/deal-board-model.js.map +1 -1
  102. package/src/lib/model/deal-contact-roles.d.ts +15 -1
  103. package/src/lib/model/deal-contact-roles.js +17 -3
  104. package/src/lib/model/deal-contact-roles.js.map +1 -1
  105. package/src/lib/model/settle-stage-names.d.ts +25 -0
  106. package/src/lib/model/settle-stage-names.js +29 -0
  107. package/src/lib/model/settle-stage-names.js.map +1 -0
  108. package/src/lib/native-nav.js +1 -1
  109. package/src/lib/native-nav.js.map +1 -1
  110. package/src/lib/server/add-contact-by-hand.d.ts +84 -0
  111. package/src/lib/server/add-contact-by-hand.js +212 -0
  112. package/src/lib/server/add-contact-by-hand.js.map +1 -0
  113. package/src/lib/server/api-v1/contacts.js +61 -23
  114. package/src/lib/server/api-v1/contacts.js.map +1 -1
  115. package/src/lib/server/api-v1/crm-activities.js +13 -3
  116. package/src/lib/server/api-v1/crm-activities.js.map +1 -1
  117. package/src/lib/server/api-v1/crm-pipelines.d.ts +1 -1
  118. package/src/lib/server/api-v1/crm-pipelines.js +8 -2
  119. package/src/lib/server/api-v1/crm-pipelines.js.map +1 -1
  120. package/src/lib/server/api-v1/crm-shared.d.ts +16 -9
  121. package/src/lib/server/api-v1/crm-shared.js +23 -11
  122. package/src/lib/server/api-v1/crm-shared.js.map +1 -1
  123. package/src/lib/server/api-v1/openapi.js +3 -3
  124. package/src/lib/server/api-v1/openapi.js.map +1 -1
  125. package/src/lib/server/automation-steps.js +11 -9
  126. package/src/lib/server/automation-steps.js.map +1 -1
  127. package/src/lib/server/campaign-filing-activity.js +7 -2
  128. package/src/lib/server/campaign-filing-activity.js.map +1 -1
  129. package/src/lib/server/contact-update.d.ts +4 -0
  130. package/src/lib/server/contact-update.js +71 -10
  131. package/src/lib/server/contact-update.js.map +1 -1
  132. package/src/lib/server/convert-host-lead.js +13 -2
  133. package/src/lib/server/convert-host-lead.js.map +1 -1
  134. package/src/lib/server/convert-open-lead.js +8 -1
  135. package/src/lib/server/convert-open-lead.js.map +1 -1
  136. package/src/lib/server/crm-activity-company.d.ts +25 -0
  137. package/src/lib/server/crm-activity-company.js +34 -0
  138. package/src/lib/server/crm-activity-company.js.map +1 -0
  139. package/src/lib/server/crm-inbound-email.js +21 -15
  140. package/src/lib/server/crm-inbound-email.js.map +1 -1
  141. package/src/lib/server/crm-sharing.js +9 -2
  142. package/src/lib/server/crm-sharing.js.map +1 -1
  143. package/src/lib/server/email-send.js +10 -8
  144. package/src/lib/server/email-send.js.map +1 -1
  145. package/src/lib/server/merge-contacts.js +6 -2
  146. package/src/lib/server/merge-contacts.js.map +1 -1
  147. package/src/lib/server/org-caller.d.ts +8 -0
  148. package/src/lib/server/org-caller.js +10 -0
  149. package/src/lib/server/org-caller.js.map +1 -1
  150. package/src/lib/server/record-timeline.d.ts +3 -1
  151. package/src/lib/server/record-timeline.js +11 -3
  152. package/src/lib/server/record-timeline.js.map +1 -1
  153. package/src/lib/server/task-reminders-route.d.ts +7 -5
  154. package/src/lib/server/task-reminders-route.js +82 -15
  155. package/src/lib/server/task-reminders-route.js.map +1 -1
  156. package/src/lib/server.js +152 -43
  157. package/src/lib/server.js.map +1 -1
  158. package/src/lib/transfer/contacts.js +83 -48
  159. package/src/lib/transfer/contacts.js.map +1 -1
  160. package/src/lib/transfer/fields.js +1 -2
  161. package/src/lib/transfer/fields.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/new-contact-drawer.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n composeContactName,\n CONTACT_LIFECYCLE_STAGE_LABELS,\n CONTACT_NAME_PART_MAX,\n CRM_SALUTATION_PICKLIST,\n CONTACT_LIFECYCLE_STAGES,\n consentGroupDisclosure,\n consentGroupDisclosureKey,\n type AglynOrgBilling,\n type AglynPostalAddress,\n type ConsentGroup,\n type ContactLifecycleStage,\n normalizeAddress,\n normalizeContactEmail,\n normalizePhone,\n crmMemberPickerLabel,\n} from '@aglyn/aglyn'\nimport { ICON_VARIANT_CLOSE } from '@aglyn/shared-data-enums'\nimport { Container, MdiIcon, SrOnly } from '@aglyn/shared-ui-jsx'\nimport { NavigationDrawerComponent } from '@aglyn/shared-ui-jsx/components/navigation-drawer.component'\nimport {\n Alert,\n Button,\n Checkbox,\n FormControlLabel,\n FormHelperText,\n IconButton,\n MenuItem,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useEffect, useState } from 'react'\nimport { useCrmPicklist } from '../hooks/use-crm-picklist'\nimport { useCrmScope } from '../hooks/use-crm-scope'\nimport { parseContactTags } from '../model/contact-record'\nimport {\n CompanyPicker,\n type CompanyOption,\n useCompanyOptions,\n useCreateCompany,\n} from './company-picker'\nimport {\n ContactAddressFields,\n EMPTY_ADDRESS,\n type AddressDraft,\n} from './contact-address-fields'\nimport { CrmSitePicker } from './crm-site-picker'\nimport { CrmPicklistSelect } from './picklist-select'\nimport type { OrgMemberOption } from './use-org-members'\n\n/** What the drawer hands back — already normalized where the route would. */\nexport interface NewContactValues {\n email: string\n /** The name as typed, or the first and last names' composition when either was given. */\n name: string\n /** Salesforce's standard contact fields (AGL-3515), `''` (or `false`) for none. */\n salutation: string\n firstName: string\n lastName: string\n mobilePhone: string\n department: string\n doNotCall: boolean\n phone: string\n jobTitle: string\n /** The picked company's name — the label the list column and the search read. */\n companyName: string\n /** The picked company, or `null` for none (AGL-2613). */\n companyId: string | null\n ownerUid: string\n lifecycleStage: ContactLifecycleStage | ''\n tags: string[]\n address: AglynPostalAddress | null\n marketingConsent: boolean\n /**\n * The key of the consent-group sentence shown beside the box, when one was\n * (AGL-3320) — what lets the route record the opt-in for the whole group\n * the sentence named. `null` when the site sends on its own.\n */\n disclosedConsentGroup: string | null\n}\n\nexport interface NewContactDrawerProps {\n open: boolean\n onClose: () => void\n /**\n * The site the contact is being added from — what scopes the company list\n * and the controller the consent checkbox records a basis for. `null` at\n * the organization level (AGL-2630), where the drawer asks which site\n * with a picker and holds its submit until one is named.\n */\n hostId: string | null\n /** The org document the shell passed, for the company picker's scope. */\n org?: Partial<AglynOrgBilling> | null\n /** The request is in flight — the form holds still and the button says so. */\n busy?: boolean\n /** What the route answered when it refused, shown above the form. */\n error?: string | null\n /** The team, for the owner picker. */\n owners: OrgMemberOption[]\n /** The roster has answered — an empty list is then \"nobody to pick\". */\n ownersReady: boolean\n onSubmit: (values: NewContactValues) => void\n}\n\n/**\n * ADDING ONE PERSON BY HAND, IN A DRAWER (AGL-2596).\n *\n * A drawer and not a form above the list: a create form sitting on top of a\n * table is the shape every list in this console was built to stop. The\n * button in the list's header opens this, and the list is exactly as it was\n * behind it.\n *\n * ## What is checked here and what is not\n *\n * The two fields a person can mistype in a way the record cannot hold — the\n * email and the phone number — are checked before the request leaves, with\n * the same normalizers the route runs, so the refusal lands under the field\n * rather than as a sentence from the server. Everything else is the route's\n * to decide: whether the address already belongs to somebody (it merges),\n * whether the plan's band has room (it refuses, and its sentence is shown\n * above the form unchanged).\n *\n * ## The consent box is a claim, and says so\n *\n * The forms plugin's rule for the opt-in field is that submitting a form is\n * never itself consent. Typing somebody into the CRM is not either — so the\n * checkbox is worded as the person's act, not the operator's, and the helper\n * carries the group disclosure the form would have shown them.\n *\n * ## The company is a record, picked or created here\n *\n * The Company field is the picker (AGL-2613): a choice from the companies\n * this site may see, with a \"Create …\" row for a name none of them carry, so\n * the person filing a contact never leaves the drawer to make the account.\n * What leaves the drawer is the company's id AND its name — the id is the\n * link the route writes into the facet, the name is the label the list\n * column and the global search keep reading. The company listen opens with\n * the drawer and closes with it, because the drawer is mounted only while\n * it is open.\n */\nexport function NewContactDrawer(props: NewContactDrawerProps) {\n const {\n open,\n onClose,\n hostId,\n org,\n busy,\n error,\n owners,\n ownersReady,\n onSubmit,\n } = props\n /*\n * The controller the consent checkbox records a basis FOR: the mounted\n * site's group, or at the organization level the picked site's — the one\n * the route will capture the contact under. `null` until a site is\n * picked, and the submit waits on it.\n */\n const { createHostId, createGroup, orgId } = useCrmScope({ hostId, org })\n const consentGroup: ConsentGroup | null = createGroup\n const salutations = useCrmPicklist(CRM_SALUTATION_PICKLIST, open ? orgId : null)\n const companies = useCompanyOptions({ hostId, org, enabled: open })\n const createCompany = useCreateCompany({ hostId, org })\n\n const [email, setEmail] = useState('')\n const [name, setName] = useState('')\n const [salutation, setSalutation] = useState('')\n const [firstName, setFirstName] = useState('')\n const [lastName, setLastName] = useState('')\n const [mobilePhone, setMobilePhone] = useState('')\n const [department, setDepartment] = useState('')\n const [doNotCall, setDoNotCall] = useState(false)\n const [mobileError, setMobileError] = useState('')\n const [phone, setPhone] = useState('')\n const [jobTitle, setJobTitle] = useState('')\n const [company, setCompany] = useState<CompanyOption | null>(null)\n const [ownerUid, setOwnerUid] = useState('')\n const [lifecycleStage, setLifecycleStage] = useState<\n ContactLifecycleStage | ''\n >('')\n const [tags, setTags] = useState('')\n const [address, setAddress] = useState<AddressDraft>(EMPTY_ADDRESS)\n const [marketingConsent, setMarketingConsent] = useState(false)\n const [emailError, setEmailError] = useState('')\n const [phoneError, setPhoneError] = useState('')\n\n /*\n * A fresh form on every opening. The drawer stays mounted behind the list\n * between uses, and a person typed and then abandoned must not reappear\n * half-filled the next time somebody reaches for \"New contact\".\n */\n useEffect(() => {\n if (!open) return\n setEmail('')\n setName('')\n setSalutation('')\n setFirstName('')\n setLastName('')\n setMobilePhone('')\n setDepartment('')\n setDoNotCall(false)\n setMobileError('')\n setPhone('')\n setJobTitle('')\n setCompany(null)\n setOwnerUid('')\n setLifecycleStage('')\n setTags('')\n setAddress(EMPTY_ADDRESS)\n setMarketingConsent(false)\n setEmailError('')\n setPhoneError('')\n }, [open])\n\n const handleSubmit = () => {\n const normalizedEmail = normalizeContactEmail(email)\n const trimmedPhone = phone.trim()\n const normalizedPhone = trimmedPhone ? normalizePhone(trimmedPhone) : ''\n setEmailError(normalizedEmail ? '' : 'Enter a valid email address.')\n setPhoneError(\n trimmedPhone && !normalizedPhone\n ? 'Enter it with its country code, like +1 512 555 0107.'\n : '',\n )\n const trimmedMobile = mobilePhone.trim()\n const normalizedMobile = trimmedMobile ? normalizePhone(trimmedMobile) : ''\n setMobileError(\n trimmedMobile && !normalizedMobile\n ? 'Enter it with its country code, like +1 512 555 0107.'\n : '',\n )\n if (\n !normalizedEmail ||\n (trimmedPhone && !normalizedPhone) ||\n (trimmedMobile && !normalizedMobile)\n ) {\n return\n }\n onSubmit({\n email: normalizedEmail,\n name: composedName || name.trim().slice(0, 120),\n salutation,\n firstName: firstName.trim(),\n lastName: lastName.trim(),\n mobilePhone: normalizedMobile ?? '',\n department: department.trim().slice(0, 120),\n doNotCall,\n phone: normalizedPhone ?? '',\n jobTitle: jobTitle.trim().slice(0, 120),\n companyName: company ? company.name.trim().slice(0, 120) : '',\n companyId: company?.id ?? null,\n ownerUid,\n lifecycleStage,\n tags: parseContactTags(tags),\n address: normalizeAddress(address),\n marketingConsent,\n disclosedConsentGroup: consentGroup\n ? consentGroupDisclosureKey(consentGroup)\n : null,\n })\n }\n\n const disclosure = consentGroup ? consentGroupDisclosure(consentGroup) : null\n // While a first or last name is typed, the name is theirs (AGL-3515).\n const composedName = composeContactName(firstName, lastName)\n\n return (\n <NavigationDrawerComponent\n open={open}\n anchor=\"right\"\n variant=\"temporary\"\n onClose={onClose}\n AppBarProps={{ color: 'surface' }}\n sx={{ '& .MuiDrawer-paper': { width: { xs: '100%', sm: 480 } } }}\n appBarLeft={\n <>\n <IconButton\n color=\"inherit\"\n edge=\"start\"\n onClick={onClose}\n sx={{ mr: 2 }}\n >\n <MdiIcon path={ICON_VARIANT_CLOSE.path} />\n <SrOnly>close drawer</SrOnly>\n </IconButton>\n <Typography variant=\"h6\" component=\"div\">\n {'New contact'}\n </Typography>\n </>\n }\n appBarRight={\n <Button variant=\"outlined\" color=\"inherit\" onClick={onClose}>\n {'Cancel'}\n </Button>\n }\n >\n <Container gutterY>\n <Stack spacing={2}>\n {error ? <Alert severity=\"warning\">{error}</Alert> : null}\n {/*\n First, because everything below it is filed under the answer: the\n site decides which of the org's sites may see the person and whose\n consent the checkbox at the foot records. Renders nothing under a\n site (AGL-2630).\n */}\n <CrmSitePicker\n hostId={hostId}\n disabled={Boolean(busy)}\n helperText=\"The site that captures this person — it decides which sites may see them and whose marketing consent the checkbox below records.\"\n />\n <TextField\n size=\"small\"\n label=\"Email\"\n type=\"email\"\n value={email}\n onChange={(event) => setEmail(event.target.value)}\n error={Boolean(emailError)}\n helperText={\n emailError ||\n 'The one field that is required — it is what makes two captures one person.'\n }\n required\n fullWidth\n autoFocus\n />\n <CrmPicklistSelect\n picklistId={CRM_SALUTATION_PICKLIST}\n picklist={salutations.picklist}\n value={salutation}\n onChange={setSalutation}\n disabled={Boolean(busy)}\n />\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={2}>\n <TextField\n size=\"small\"\n label=\"First name\"\n value={firstName}\n onChange={(event) => setFirstName(event.target.value)}\n slotProps={{ htmlInput: { maxLength: CONTACT_NAME_PART_MAX } }}\n fullWidth\n />\n <TextField\n size=\"small\"\n label=\"Last name\"\n value={lastName}\n onChange={(event) => setLastName(event.target.value)}\n slotProps={{ htmlInput: { maxLength: CONTACT_NAME_PART_MAX } }}\n fullWidth\n />\n </Stack>\n <TextField\n size=\"small\"\n label=\"Name\"\n value={composedName || name}\n onChange={(event) => setName(event.target.value)}\n slotProps={{\n htmlInput: { maxLength: 120 },\n input: { readOnly: Boolean(composedName) },\n }}\n helperText={\n composedName\n ? 'Made of the first and last name.'\n : 'Or the whole name in one field.'\n }\n fullWidth\n />\n <TextField\n size=\"small\"\n label=\"Phone\"\n value={phone}\n onChange={(event) => setPhone(event.target.value)}\n error={Boolean(phoneError)}\n helperText={phoneError || 'With the country code, like +1 512 555 0107'}\n fullWidth\n />\n <TextField\n size=\"small\"\n label=\"Mobile phone\"\n value={mobilePhone}\n onChange={(event) => setMobilePhone(event.target.value)}\n error={Boolean(mobileError)}\n helperText={mobileError || undefined}\n fullWidth\n />\n <FormControlLabel\n control={\n <Checkbox\n checked={doNotCall}\n onChange={(event) => setDoNotCall(event.target.checked)}\n />\n }\n label=\"Do not call\"\n />\n <TextField\n size=\"small\"\n label=\"Job title\"\n value={jobTitle}\n onChange={(event) => setJobTitle(event.target.value)}\n slotProps={{ htmlInput: { maxLength: 120 } }}\n fullWidth\n />\n <TextField\n size=\"small\"\n label=\"Department\"\n value={department}\n onChange={(event) => setDepartment(event.target.value)}\n slotProps={{ htmlInput: { maxLength: 120 } }}\n fullWidth\n />\n <CompanyPicker\n options={companies.options}\n ready={companies.ready}\n truncated={companies.truncated}\n value={company?.id ?? null}\n onChange={(_id, picked) => setCompany(picked)}\n onCreate={createCompany}\n email={email}\n disabled={Boolean(busy)}\n helperText=\"The account this person works for. Type a name nobody has filed yet to create it.\"\n />\n <TextField\n select\n size=\"small\"\n label=\"Lifecycle stage\"\n value={lifecycleStage}\n onChange={(event) =>\n setLifecycleStage(event.target.value as ContactLifecycleStage | '')\n }\n fullWidth\n >\n <MenuItem value=\"\">{'Not placed yet'}</MenuItem>\n {CONTACT_LIFECYCLE_STAGES.map((stage) => (\n <MenuItem key={stage} value={stage}>\n {CONTACT_LIFECYCLE_STAGE_LABELS[stage]}\n </MenuItem>\n ))}\n </TextField>\n <TextField\n select\n size=\"small\"\n label=\"Owner\"\n value={ownerUid}\n onChange={(event) => setOwnerUid(event.target.value)}\n helperText={\n ownersReady && !owners.length\n ? 'The team roster could not be read, so nobody can be picked yet.'\n : 'The team member responsible for this relationship'\n }\n fullWidth\n >\n <MenuItem value=\"\">{'Unassigned'}</MenuItem>\n {owners.map((owner) => (\n <MenuItem key={owner.uid} value={owner.uid}>\n {crmMemberPickerLabel(owner)}\n </MenuItem>\n ))}\n </TextField>\n <TextField\n size=\"small\"\n label=\"Tags\"\n placeholder=\"vip, beta\"\n helperText=\"Comma-separated\"\n value={tags}\n onChange={(event) => setTags(event.target.value)}\n fullWidth\n />\n <Typography variant=\"subtitle2\">{'Mailing address'}</Typography>\n <ContactAddressFields value={address} onChange={setAddress} />\n <Stack>\n <FormControlLabel\n control={\n <Checkbox\n checked={marketingConsent}\n onChange={(event) => setMarketingConsent(event.target.checked)}\n />\n }\n label=\"This person opted in to marketing email\"\n />\n <FormHelperText>\n {(disclosure ? `${disclosure} ` : '') +\n 'Tick it only if they agreed. Adding a contact is never itself consent.'}\n </FormHelperText>\n </Stack>\n <Button\n variant=\"contained\"\n color=\"primary\"\n // Held until the capturing site is known: the route resolves the\n // org from it and stamps the record with it.\n disabled={Boolean(busy) || !createHostId}\n onClick={handleSubmit}\n >\n {busy ? 'Adding…' : 'Add contact'}\n </Button>\n </Stack>\n </Container>\n </NavigationDrawerComponent>\n )\n}\n\nexport default NewContactDrawer\n"],"names":["composeContactName","CONTACT_LIFECYCLE_STAGE_LABELS","CONTACT_NAME_PART_MAX","CRM_SALUTATION_PICKLIST","CONTACT_LIFECYCLE_STAGES","consentGroupDisclosure","consentGroupDisclosureKey","normalizeAddress","normalizeContactEmail","normalizePhone","crmMemberPickerLabel","ICON_VARIANT_CLOSE","Container","MdiIcon","SrOnly","NavigationDrawerComponent","Alert","Button","Checkbox","FormControlLabel","FormHelperText","IconButton","MenuItem","Stack","TextField","Typography","useEffect","useState","useCrmPicklist","useCrmScope","parseContactTags","CompanyPicker","useCompanyOptions","useCreateCompany","ContactAddressFields","EMPTY_ADDRESS","CrmSitePicker","CrmPicklistSelect","NewContactDrawer","props","open","onClose","hostId","org","busy","error","owners","ownersReady","onSubmit","createHostId","createGroup","orgId","consentGroup","salutations","companies","enabled","createCompany","email","setEmail","name","setName","salutation","setSalutation","firstName","setFirstName","lastName","setLastName","mobilePhone","setMobilePhone","department","setDepartment","doNotCall","setDoNotCall","mobileError","setMobileError","phone","setPhone","jobTitle","setJobTitle","company","setCompany","ownerUid","setOwnerUid","lifecycleStage","setLifecycleStage","tags","setTags","address","setAddress","marketingConsent","setMarketingConsent","emailError","setEmailError","phoneError","setPhoneError","handleSubmit","normalizedEmail","trimmedPhone","trim","normalizedPhone","trimmedMobile","normalizedMobile","composedName","slice","companyName","companyId","id","disclosedConsentGroup","disclosure","anchor","variant","AppBarProps","color","sx","width","xs","sm","appBarLeft","edge","onClick","mr","path","component","appBarRight","gutterY","spacing","severity","disabled","Boolean","helperText","size","label","type","value","onChange","event","target","required","fullWidth","autoFocus","picklistId","picklist","direction","slotProps","htmlInput","maxLength","input","readOnly","undefined","control","checked","options","ready","truncated","_id","picked","onCreate","select","map","stage","length","owner","uid","placeholder"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/new-contact-drawer.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n composeContactName,\n CONTACT_LIFECYCLE_STAGE_LABELS,\n CONTACT_NAME_PART_MAX,\n CRM_SALUTATION_PICKLIST,\n CONTACT_LIFECYCLE_STAGES,\n consentGroupDisclosure,\n consentGroupDisclosureKey,\n type AglynOrgBilling,\n type AglynPostalAddress,\n type ConsentGroup,\n type ContactLifecycleStage,\n normalizeAddress,\n normalizeContactEmail,\n normalizePhone,\n crmMemberPickerLabel,\n} from '@aglyn/aglyn'\nimport { ICON_VARIANT_CLOSE } from '@aglyn/shared-data-enums'\nimport { Container, MdiIcon, SrOnly } from '@aglyn/shared-ui-jsx'\nimport { NavigationDrawerComponent } from '@aglyn/shared-ui-jsx/components/navigation-drawer.component'\nimport {\n Alert,\n Button,\n Checkbox,\n FormControlLabel,\n FormHelperText,\n IconButton,\n MenuItem,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useEffect, useState } from 'react'\nimport { useCrmPicklist } from '../hooks/use-crm-picklist'\nimport { useCrmScope } from '../hooks/use-crm-scope'\nimport { parseContactTags } from '../model/contact-record'\nimport {\n CompanyPicker,\n type CompanyOption,\n useCompanyOptions,\n useCreateCompany,\n} from './company-picker'\nimport {\n ContactAddressFields,\n EMPTY_ADDRESS,\n type AddressDraft,\n} from './contact-address-fields'\nimport { CrmSitePicker } from './crm-site-picker'\nimport { CrmPicklistSelect } from './picklist-select'\nimport type { OrgMemberOption } from './use-org-members'\n\n/** What the drawer hands back — already normalized where the route would. */\nexport interface NewContactValues {\n /** Normalized, or `''` for a person with no address yet (AGL-4122). */\n email: string\n /** The name as typed, or the first and last names' composition when either was given. */\n name: string\n /** Salesforce's standard contact fields (AGL-3515), `''` (or `false`) for none. */\n salutation: string\n firstName: string\n lastName: string\n mobilePhone: string\n department: string\n doNotCall: boolean\n phone: string\n jobTitle: string\n /** The picked company's name — the label the list column and the search read. */\n companyName: string\n /** The picked company, or `null` for none (AGL-2613). */\n companyId: string | null\n ownerUid: string\n lifecycleStage: ContactLifecycleStage | ''\n tags: string[]\n address: AglynPostalAddress | null\n marketingConsent: boolean\n /**\n * The key of the consent-group sentence shown beside the box, when one was\n * (AGL-3320) — what lets the route record the opt-in for the whole group\n * the sentence named. `null` when the site sends on its own.\n */\n disclosedConsentGroup: string | null\n}\n\nexport interface NewContactDrawerProps {\n open: boolean\n onClose: () => void\n /**\n * The site the contact is being added from — what scopes the company list\n * and the controller the consent checkbox records a basis for. `null` at\n * the organization level (AGL-2630), where the drawer asks which site with\n * a picker. An org whose records belong to every site may leave it empty,\n * and one that keeps them apart holds the submit until one is named.\n */\n hostId: string | null\n /** The org document the shell passed, for the company picker's scope. */\n org?: Partial<AglynOrgBilling> | null\n /** The request is in flight — the form holds still and the button says so. */\n busy?: boolean\n /** What the route answered when it refused, shown above the form. */\n error?: string | null\n /** The team, for the owner picker. */\n owners: OrgMemberOption[]\n /** The roster has answered — an empty list is then \"nobody to pick\". */\n ownersReady: boolean\n onSubmit: (values: NewContactValues) => void\n}\n\n/**\n * ADDING ONE PERSON BY HAND, IN A DRAWER (AGL-2596).\n *\n * A drawer and not a form above the list: a create form sitting on top of a\n * table is the shape every list in this console was built to stop. The\n * button in the list's header opens this, and the list is exactly as it was\n * behind it.\n *\n * ## What is checked here and what is not\n *\n * The two fields a person can mistype in a way the record cannot hold — the\n * email and the phone number — are checked before the request leaves, with\n * the same normalizers the route runs, so the refusal lands under the field\n * rather than as a sentence from the server. Neither is required: a contact\n * needs a name, an email or a number, because the team works a deal with the\n * person in front of them and the address can come later (AGL-4122). Everything else is the route's\n * to decide: whether the address already belongs to somebody (it merges),\n * whether the plan's band has room (it refuses, and its sentence is shown\n * above the form unchanged).\n *\n * ## The consent box is a claim, and says so\n *\n * The forms plugin's rule for the opt-in field is that submitting a form is\n * never itself consent. Typing somebody into the CRM is not either — so the\n * checkbox is worded as the person's act, not the operator's, and the helper\n * carries the group disclosure the form would have shown them.\n *\n * ## The company is a record, picked or created here\n *\n * The Company field is the picker (AGL-2613): a choice from the companies\n * this site may see, with a \"Create …\" row for a name none of them carry, so\n * the person filing a contact never leaves the drawer to make the account.\n * What leaves the drawer is the company's id AND its name — the id is the\n * link the route writes into the facet, the name is the label the list\n * column and the global search keep reading. The company listen opens with\n * the drawer and closes with it, because the drawer is mounted only while\n * it is open.\n */\nexport function NewContactDrawer(props: NewContactDrawerProps) {\n const {\n open,\n onClose,\n hostId,\n org,\n busy,\n error,\n owners,\n ownersReady,\n onSubmit,\n } = props\n /*\n * The controller the consent checkbox records a basis FOR: the mounted\n * site's group, or at the organization level the picked site's — the one\n * the route will capture the contact under. `null` until a site is\n * picked, and the submit waits on it.\n */\n const { createHostId, createGroup, createNeedsSite, orgId } = useCrmScope({ hostId, org })\n const consentGroup: ConsentGroup | null = createGroup\n const salutations = useCrmPicklist(CRM_SALUTATION_PICKLIST, open ? orgId : null)\n const companies = useCompanyOptions({ hostId, org, enabled: open })\n const createCompany = useCreateCompany({ hostId, org })\n\n const [email, setEmail] = useState('')\n const [name, setName] = useState('')\n const [salutation, setSalutation] = useState('')\n const [firstName, setFirstName] = useState('')\n const [lastName, setLastName] = useState('')\n const [mobilePhone, setMobilePhone] = useState('')\n const [department, setDepartment] = useState('')\n const [doNotCall, setDoNotCall] = useState(false)\n const [mobileError, setMobileError] = useState('')\n const [phone, setPhone] = useState('')\n const [jobTitle, setJobTitle] = useState('')\n const [company, setCompany] = useState<CompanyOption | null>(null)\n const [ownerUid, setOwnerUid] = useState('')\n const [lifecycleStage, setLifecycleStage] = useState<\n ContactLifecycleStage | ''\n >('')\n const [tags, setTags] = useState('')\n const [address, setAddress] = useState<AddressDraft>(EMPTY_ADDRESS)\n const [marketingConsent, setMarketingConsent] = useState(false)\n const [emailError, setEmailError] = useState('')\n const [phoneError, setPhoneError] = useState('')\n const [identityError, setIdentityError] = useState('')\n\n /*\n * A fresh form on every opening. The drawer stays mounted behind the list\n * between uses, and a person typed and then abandoned must not reappear\n * half-filled the next time somebody reaches for \"New contact\".\n */\n useEffect(() => {\n if (!open) return\n setEmail('')\n setName('')\n setSalutation('')\n setFirstName('')\n setLastName('')\n setMobilePhone('')\n setDepartment('')\n setDoNotCall(false)\n setMobileError('')\n setPhone('')\n setJobTitle('')\n setCompany(null)\n setOwnerUid('')\n setLifecycleStage('')\n setTags('')\n setAddress(EMPTY_ADDRESS)\n setMarketingConsent(false)\n setEmailError('')\n setPhoneError('')\n setIdentityError('')\n }, [open])\n\n const handleSubmit = () => {\n const trimmedEmail = email.trim()\n const normalizedEmail = trimmedEmail ? normalizeContactEmail(trimmedEmail) : ''\n const trimmedPhone = phone.trim()\n const normalizedPhone = trimmedPhone ? normalizePhone(trimmedPhone) : ''\n setEmailError(trimmedEmail && !normalizedEmail ? 'Enter a valid email address.' : '')\n setPhoneError(\n trimmedPhone && !normalizedPhone\n ? 'Enter it with its country code, like +1 512 555 0107.'\n : '',\n )\n const trimmedMobile = mobilePhone.trim()\n const normalizedMobile = trimmedMobile ? normalizePhone(trimmedMobile) : ''\n setMobileError(\n trimmedMobile && !normalizedMobile\n ? 'Enter it with its country code, like +1 512 555 0107.'\n : '',\n )\n const identified = Boolean(\n trimmedEmail || trimmedPhone || trimmedMobile || composedName || name.trim(),\n )\n setIdentityError(identified ? '' : 'Enter a name, an email or a phone number.')\n if (\n !identified ||\n (trimmedEmail && !normalizedEmail) ||\n (trimmedPhone && !normalizedPhone) ||\n (trimmedMobile && !normalizedMobile)\n ) {\n return\n }\n onSubmit({\n email: normalizedEmail ?? '',\n name: composedName || name.trim().slice(0, 120),\n salutation,\n firstName: firstName.trim(),\n lastName: lastName.trim(),\n mobilePhone: normalizedMobile ?? '',\n department: department.trim().slice(0, 120),\n doNotCall,\n phone: normalizedPhone ?? '',\n jobTitle: jobTitle.trim().slice(0, 120),\n companyName: company ? company.name.trim().slice(0, 120) : '',\n companyId: company?.id ?? null,\n ownerUid,\n lifecycleStage,\n tags: parseContactTags(tags),\n address: normalizeAddress(address),\n marketingConsent: marketingConsent && Boolean(normalizedEmail),\n disclosedConsentGroup: consentGroup\n ? consentGroupDisclosureKey(consentGroup)\n : null,\n })\n }\n\n const disclosure = consentGroup ? consentGroupDisclosure(consentGroup) : null\n const hasEmail = Boolean(email.trim())\n // While a first or last name is typed, the name is theirs (AGL-3515).\n const composedName = composeContactName(firstName, lastName)\n\n return (\n <NavigationDrawerComponent\n open={open}\n anchor=\"right\"\n variant=\"temporary\"\n onClose={onClose}\n AppBarProps={{ color: 'surface' }}\n sx={{ '& .MuiDrawer-paper': { width: { xs: '100%', sm: 480 } } }}\n appBarLeft={\n <>\n <IconButton\n color=\"inherit\"\n edge=\"start\"\n onClick={onClose}\n sx={{ mr: 2 }}\n >\n <MdiIcon path={ICON_VARIANT_CLOSE.path} />\n <SrOnly>close drawer</SrOnly>\n </IconButton>\n <Typography variant=\"h6\" component=\"div\">\n {'New contact'}\n </Typography>\n </>\n }\n appBarRight={\n <Button variant=\"outlined\" color=\"inherit\" onClick={onClose}>\n {'Cancel'}\n </Button>\n }\n >\n <Container gutterY>\n <Stack spacing={2}>\n {error ? <Alert severity=\"warning\">{error}</Alert> : null}\n {identityError ? <Alert severity=\"warning\">{identityError}</Alert> : null}\n {/*\n First, because the consent at the foot is recorded against the\n answer. Renders nothing under a site (AGL-2630).\n */}\n <CrmSitePicker\n hostId={hostId}\n optional={!createNeedsSite}\n disabled={Boolean(busy)}\n helperText={\n createNeedsSite\n ? 'The site that captures this person — it decides which sites may see them and whose marketing consent the checkbox below records.'\n : 'Where they came from, if anywhere. Every site in the organization can see them either way.'\n }\n />\n <TextField\n size=\"small\"\n label=\"Email\"\n type=\"email\"\n value={email}\n onChange={(event) => setEmail(event.target.value)}\n error={Boolean(emailError)}\n helperText={\n emailError ||\n 'Optional. You can add it later, and an address already on a contact merges into them.'\n }\n fullWidth\n autoFocus\n />\n <CrmPicklistSelect\n picklistId={CRM_SALUTATION_PICKLIST}\n picklist={salutations.picklist}\n value={salutation}\n onChange={setSalutation}\n disabled={Boolean(busy)}\n />\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={2}>\n <TextField\n size=\"small\"\n label=\"First name\"\n value={firstName}\n onChange={(event) => setFirstName(event.target.value)}\n slotProps={{ htmlInput: { maxLength: CONTACT_NAME_PART_MAX } }}\n fullWidth\n />\n <TextField\n size=\"small\"\n label=\"Last name\"\n value={lastName}\n onChange={(event) => setLastName(event.target.value)}\n slotProps={{ htmlInput: { maxLength: CONTACT_NAME_PART_MAX } }}\n fullWidth\n />\n </Stack>\n <TextField\n size=\"small\"\n label=\"Name\"\n value={composedName || name}\n onChange={(event) => setName(event.target.value)}\n slotProps={{\n htmlInput: { maxLength: 120 },\n input: { readOnly: Boolean(composedName) },\n }}\n helperText={\n composedName\n ? 'Made of the first and last name.'\n : 'Or the whole name in one field.'\n }\n fullWidth\n />\n <TextField\n size=\"small\"\n label=\"Phone\"\n value={phone}\n onChange={(event) => setPhone(event.target.value)}\n error={Boolean(phoneError)}\n helperText={phoneError || 'With the country code, like +1 512 555 0107'}\n fullWidth\n />\n <TextField\n size=\"small\"\n label=\"Mobile phone\"\n value={mobilePhone}\n onChange={(event) => setMobilePhone(event.target.value)}\n error={Boolean(mobileError)}\n helperText={mobileError || undefined}\n fullWidth\n />\n <FormControlLabel\n control={\n <Checkbox\n checked={doNotCall}\n onChange={(event) => setDoNotCall(event.target.checked)}\n />\n }\n label=\"Do not call\"\n />\n <TextField\n size=\"small\"\n label=\"Job title\"\n value={jobTitle}\n onChange={(event) => setJobTitle(event.target.value)}\n slotProps={{ htmlInput: { maxLength: 120 } }}\n fullWidth\n />\n <TextField\n size=\"small\"\n label=\"Department\"\n value={department}\n onChange={(event) => setDepartment(event.target.value)}\n slotProps={{ htmlInput: { maxLength: 120 } }}\n fullWidth\n />\n <CompanyPicker\n options={companies.options}\n ready={companies.ready}\n truncated={companies.truncated}\n value={company?.id ?? null}\n onChange={(_id, picked) => setCompany(picked)}\n onCreate={createCompany}\n email={email}\n disabled={Boolean(busy)}\n helperText=\"The account this person works for. Type a name nobody has filed yet to create it.\"\n />\n <TextField\n select\n size=\"small\"\n label=\"Lifecycle stage\"\n value={lifecycleStage}\n onChange={(event) =>\n setLifecycleStage(event.target.value as ContactLifecycleStage | '')\n }\n fullWidth\n >\n <MenuItem value=\"\">{'Not placed yet'}</MenuItem>\n {CONTACT_LIFECYCLE_STAGES.map((stage) => (\n <MenuItem key={stage} value={stage}>\n {CONTACT_LIFECYCLE_STAGE_LABELS[stage]}\n </MenuItem>\n ))}\n </TextField>\n <TextField\n select\n size=\"small\"\n label=\"Owner\"\n value={ownerUid}\n onChange={(event) => setOwnerUid(event.target.value)}\n helperText={\n ownersReady && !owners.length\n ? 'The team roster could not be read, so nobody can be picked yet.'\n : 'The team member responsible for this relationship'\n }\n fullWidth\n >\n <MenuItem value=\"\">{'Unassigned'}</MenuItem>\n {owners.map((owner) => (\n <MenuItem key={owner.uid} value={owner.uid}>\n {crmMemberPickerLabel(owner)}\n </MenuItem>\n ))}\n </TextField>\n <TextField\n size=\"small\"\n label=\"Tags\"\n placeholder=\"vip, beta\"\n helperText=\"Comma-separated\"\n value={tags}\n onChange={(event) => setTags(event.target.value)}\n fullWidth\n />\n <Typography variant=\"subtitle2\">{'Mailing address'}</Typography>\n <ContactAddressFields value={address} onChange={setAddress} />\n <Stack>\n <FormControlLabel\n control={\n <Checkbox\n checked={marketingConsent && hasEmail}\n disabled={!hasEmail}\n onChange={(event) => setMarketingConsent(event.target.checked)}\n />\n }\n label=\"This person opted in to marketing email\"\n />\n <FormHelperText>\n {!hasEmail\n ? 'Add an email first: there is nowhere to send marketing without one.'\n : (disclosure ? `${disclosure} ` : '') +\n (createHostId ? '' : 'With no source site, this records it on every site. ') +\n 'Tick it only if they agreed. Adding a contact is never itself consent.'}\n </FormHelperText>\n </Stack>\n <Button\n variant=\"contained\"\n color=\"primary\"\n // Held only where the site decides who may see the record.\n disabled={Boolean(busy) || (createNeedsSite && !createHostId)}\n onClick={handleSubmit}\n >\n {busy ? 'Adding…' : 'Add contact'}\n </Button>\n </Stack>\n </Container>\n </NavigationDrawerComponent>\n )\n}\n\nexport default NewContactDrawer\n"],"names":["composeContactName","CONTACT_LIFECYCLE_STAGE_LABELS","CONTACT_NAME_PART_MAX","CRM_SALUTATION_PICKLIST","CONTACT_LIFECYCLE_STAGES","consentGroupDisclosure","consentGroupDisclosureKey","normalizeAddress","normalizeContactEmail","normalizePhone","crmMemberPickerLabel","ICON_VARIANT_CLOSE","Container","MdiIcon","SrOnly","NavigationDrawerComponent","Alert","Button","Checkbox","FormControlLabel","FormHelperText","IconButton","MenuItem","Stack","TextField","Typography","useEffect","useState","useCrmPicklist","useCrmScope","parseContactTags","CompanyPicker","useCompanyOptions","useCreateCompany","ContactAddressFields","EMPTY_ADDRESS","CrmSitePicker","CrmPicklistSelect","NewContactDrawer","props","open","onClose","hostId","org","busy","error","owners","ownersReady","onSubmit","createHostId","createGroup","createNeedsSite","orgId","consentGroup","salutations","companies","enabled","createCompany","email","setEmail","name","setName","salutation","setSalutation","firstName","setFirstName","lastName","setLastName","mobilePhone","setMobilePhone","department","setDepartment","doNotCall","setDoNotCall","mobileError","setMobileError","phone","setPhone","jobTitle","setJobTitle","company","setCompany","ownerUid","setOwnerUid","lifecycleStage","setLifecycleStage","tags","setTags","address","setAddress","marketingConsent","setMarketingConsent","emailError","setEmailError","phoneError","setPhoneError","identityError","setIdentityError","handleSubmit","trimmedEmail","trim","normalizedEmail","trimmedPhone","normalizedPhone","trimmedMobile","normalizedMobile","identified","Boolean","composedName","slice","companyName","companyId","id","disclosedConsentGroup","disclosure","hasEmail","anchor","variant","AppBarProps","color","sx","width","xs","sm","appBarLeft","edge","onClick","mr","path","component","appBarRight","gutterY","spacing","severity","optional","disabled","helperText","size","label","type","value","onChange","event","target","fullWidth","autoFocus","picklistId","picklist","direction","slotProps","htmlInput","maxLength","input","readOnly","undefined","control","checked","options","ready","truncated","_id","picked","onCreate","select","map","stage","length","owner","uid","placeholder"],"mappings":"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"}
@@ -109,7 +109,7 @@ import { crmTaskCallScope } from "../model/task-routes.js";
109
109
  const failed = `The ${definition.plural} could not be saved.`;
110
110
  const isStandard = (value)=>isStandardPicklistValueId(definition, value.id);
111
111
  /** Write the whole list — the list-only moves above. */ const save = useCallback(async (next, done)=>{
112
- if (!orgId || busy) return;
112
+ if (!orgId || busy) return false;
113
113
  setBusy(true);
114
114
  try {
115
115
  const verdict = await writeGuardedBySeed({
@@ -132,18 +132,20 @@ import { crmTaskCallScope } from "../model/task-routes.js";
132
132
  variant: 'warning',
133
133
  persist: false
134
134
  });
135
- return;
135
+ return false;
136
136
  }
137
137
  enqueueSnackbar(done, {
138
138
  variant: 'success',
139
139
  persist: false
140
140
  });
141
+ return true;
141
142
  } catch (error) {
142
143
  console.error(error);
143
144
  enqueueSnackbar(failed, {
144
145
  variant: 'error',
145
146
  allowDuplicate: true
146
147
  });
148
+ return false;
147
149
  } finally{
148
150
  setBusy(false);
149
151
  }
@@ -255,8 +257,9 @@ import { crmTaskCallScope } from "../model/task-routes.js";
255
257
  });
256
258
  return;
257
259
  }
258
- setNameDialog(null);
259
- await save(move.picklist, `“${draftName.trim()}” added`);
260
+ // The dialog holds what was typed until the list has really saved
261
+ // (AGL-4120): closing first made a refused add read as done.
262
+ if (await save(move.picklist, `“${draftName.trim()}” added`)) setNameDialog(null);
260
263
  return;
261
264
  }
262
265
  const result = await post({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/picklist-values-card.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n CRM_COLLECTIONS,\n CRM_LEAD_TEXT_MAX,\n type CrmPicklist,\n type CrmPicklistDefinition,\n type CrmPicklistId,\n type CrmPicklistObject,\n type CrmPicklistValue,\n isStandardPicklistValueId,\n newResourceScopeFields,\n ORG_SCOPE_TOKEN,\n} from '@aglyn/aglyn'\nimport {\n mdiArrowDown,\n mdiArrowUp,\n mdiDeleteOutline,\n mdiDragVertical,\n mdiEyeOffOutline,\n mdiEyeOutline,\n mdiPencilOutline,\n mdiStarOutline,\n} from '@aglyn/shared-data-mdi'\nimport { MdiIcon, SrOnly } from '@aglyn/shared-ui-jsx'\nimport RowActionsMenu, {\n type RowActionsMenuItem,\n} from '@aglyn/shared-ui-jsx/components/row-actions-menu.component'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport {\n useFirestore,\n useUser,\n writeGuardedBySeed,\n} from '@aglyn/tenant-feature-instance'\nimport {\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogContentText,\n DialogTitle,\n IconButton,\n MenuItem,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TextField,\n Typography,\n} from '@mui/material'\nimport { doc, serverTimestamp, setDoc } from 'firebase/firestore'\nimport { useCallback, useState } from 'react'\nimport { useCrmPicklist } from '../hooks/use-crm-picklist'\nimport {\n addPicklistValue,\n crmPicklistValuesRouteUrl,\n movePicklistValue,\n picklistAddableMeanings,\n picklistKeepsGroups,\n picklistMeaningLabel,\n picklistReplacements,\n type PicklistValuesAction,\n type PicklistValuesRequest,\n type PicklistValuesResponse,\n setPicklistDefault,\n setPicklistValueActive,\n setPicklistValueGroup,\n sortPicklistValues,\n} from '../model/picklist-values'\nimport { crmTaskCallScope } from '../model/task-routes'\n\nexport interface PicklistValuesCardProps {\n /** Which of the CRM's standard picklists this card manages. */\n picklistId: CrmPicklistId\n /** The mounted site, or `null` at the organization level. */\n hostId: string | null\n /** The org whose list this is; `null` while it settles. */\n orgId: string | null\n /** The site a newly written list records as its definer — the Fields page's own. */\n createHostId: string | null\n}\n\n/** Each object's noun, singular and plural, for the card's sentences. */\nconst OBJECT_NOUNS: Record<CrmPicklistObject, [string, string]> = {\n contact: ['contact', 'contacts'],\n company: ['company', 'companies'],\n deal: ['deal', 'deals'],\n lead: ['lead', 'leads'],\n task: ['task', 'tasks'],\n}\n\n/** \"a\", \"a and b\", \"a, b and c\". */\nfunction sentenceList(words: readonly string[]): string {\n return words.length < 2\n ? (words[0] ?? '')\n : `${words.slice(0, -1).join(', ')} and ${words[words.length - 1]}`\n}\n\n/** The objects whose records hold the field's label, in the definition's order. */\nfunction targetObjects(definition: CrmPicklistDefinition): CrmPicklistObject[] {\n return [...new Set(definition.targets.map((target) => target.object))]\n}\n\n/** The add and rename dialog's state: which value, if any, is being renamed. */\ntype NameDialog = { mode: 'add' } | { mode: 'rename'; value: CrmPicklistValue } | null\n\n/**\n * ONE STANDARD PICKLIST'S VALUES (AGL-3298, AGL-3510) — Salesforce's\n * picklist value set for a standard field, managed on the Fields tab of the\n * object the field belongs to.\n *\n * Standard values are marked, and cannot be deleted — every org has them —\n * but rename, reorder, regroup, deactivate and default are theirs as much\n * as an added value's. Every move that touches only the LIST — add,\n * reorder by drag or arrows, sort A–Z, activate, deactivate, group,\n * default — is one client write of the whole document, guarded against a\n * cached read like every seeded save on the Fields page. The ones that\n * change RECORDS — rename, and delete with a replacement, and on a list\n * whose records keep the value's group (Lead source's direction, AGL-3577)\n * group and add — go to `crm/picklist-values`, which rewrites the list and\n * every record the definition's targets name together; see that route.\n *\n * An org that has never edited the list reads the standard values, and the\n * first move here writes them down as the org's own.\n */\nexport function PicklistValuesCard(props: PicklistValuesCardProps) {\n const { picklistId, hostId, orgId, createHostId } = props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const { definition, picklist, stored, ready, fromCache } = useCrmPicklist(picklistId, orgId)\n const [busy, setBusy] = useState(false)\n const [nameDialog, setNameDialog] = useState<NameDialog>(null)\n const [draftName, setDraftName] = useState('')\n const [draftGroup, setDraftGroup] = useState('')\n const [draftMeaning, setDraftMeaning] = useState('')\n const [nameError, setNameError] = useState('')\n const [deleting, setDeleting] = useState<CrmPicklistValue | null>(null)\n const [replaceWith, setReplaceWith] = useState('')\n const [dragFrom, setDragFrom] = useState<number | null>(null)\n\n const groups = definition.groups ?? []\n // Whether records keep a value's group, so a regroup and an add change them too.\n const keepsGroups = picklistKeepsGroups(definition)\n const meanings = definition.meanings ?? []\n // An added value may not take a meaning only the platform sets (AGL-3512).\n const addableMeanings = picklistAddableMeanings(definition)\n const [noun, nouns] = OBJECT_NOUNS[definition.object]\n const holders = targetObjects(definition).map((object) => OBJECT_NOUNS[object])\n // A list of suggestions only — a task's subjects (AGL-3517) — that no record holds.\n const suggestions = holders.length === 0\n const field = definition.label.toLowerCase()\n const failed = `The ${definition.plural} could not be saved.`\n const isStandard = (value: CrmPicklistValue) => isStandardPicklistValueId(definition, value.id)\n\n /** Write the whole list — the list-only moves above. */\n const save = useCallback(\n async (next: CrmPicklist, done: string) => {\n if (!orgId || busy) return\n setBusy(true)\n try {\n const verdict = await writeGuardedBySeed({ subject: `${field} list`, fromCache }, () =>\n setDoc(\n doc(firestore, 'orgs', orgId, CRM_COLLECTIONS.picklists, picklistId),\n {\n values: next.values,\n defaultValueId: next.defaultValueId,\n updatedAt: serverTimestamp(),\n // The org-wide stamp the rules require of a new CRM document,\n // the same one a field definition carries.\n ...(stored\n ? {}\n : {\n hostId: createHostId,\n createdAt: serverTimestamp(),\n ...newResourceScopeFields([ORG_SCOPE_TOKEN]),\n }),\n },\n { merge: true },\n ),\n )\n if (!verdict.ok) {\n enqueueSnackbar(verdict.message, { variant: 'warning', persist: false })\n return\n }\n enqueueSnackbar(done, { variant: 'success', persist: false })\n } catch (error) {\n console.error(error)\n enqueueSnackbar(failed, { variant: 'error', allowDuplicate: true })\n } finally {\n setBusy(false)\n }\n },\n [orgId, busy, field, fromCache, firestore, picklistId, stored, createHostId, enqueueSnackbar, failed],\n )\n\n /** A rename or a delete — the moves that change records too. */\n const post = useCallback(\n async (request: PicklistValuesAction): Promise<PicklistValuesResponse | null> => {\n const scope = crmTaskCallScope(hostId, orgId)\n if (!scope || busy) return null\n setBusy(true)\n try {\n const response = await authorizedFetch(user, crmPicklistValuesRouteUrl(), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ ...request, picklistId, ...scope } satisfies PicklistValuesRequest),\n })\n const payload = (await response.json().catch(() => ({}))) as { error?: string }\n if (!response.ok) {\n enqueueSnackbar(payload.error || failed, { variant: 'warning', persist: false })\n return null\n }\n return payload as PicklistValuesResponse\n } catch (error) {\n console.error(error)\n enqueueSnackbar(failed, { variant: 'error', allowDuplicate: true })\n return null\n } finally {\n setBusy(false)\n }\n },\n [hostId, orgId, busy, user, picklistId, enqueueSnackbar, failed],\n )\n\n /** \" — 2 leads and 1 contact updated\", or nothing when no record held the value. */\n const moved = (result: PicklistValuesResponse) => {\n const counts = targetObjects(definition).map((object) => {\n const count = result.updated?.[object] ?? 0\n const [one, many] = OBJECT_NOUNS[object]\n return { count, text: `${count.toLocaleString()} ${count === 1 ? one : many}` }\n })\n return counts.some((entry) => entry.count)\n ? ` — ${sentenceList(counts.map((entry) => entry.text))} updated`\n : ''\n }\n\n const openAdd = () => {\n setDraftName('')\n setDraftGroup('')\n setDraftMeaning('')\n setNameError('')\n setNameDialog({ mode: 'add' })\n }\n const openRename = (value: CrmPicklistValue) => {\n setDraftName(value.label)\n setNameError('')\n setNameDialog({ mode: 'rename', value })\n }\n\n const submitName = async () => {\n if (!nameDialog) return\n if (nameDialog.mode === 'add') {\n const move = addPicklistValue(definition, picklist, draftName, {\n group: draftGroup || null,\n meaning: draftMeaning || null,\n })\n if (move.ok === false) return void setNameError(move.error)\n if (keepsGroups) {\n // Records may already hold the label, and keep its group (AGL-3577).\n const result = await post({\n action: 'add',\n label: draftName,\n group: draftGroup || null,\n meaning: draftMeaning || null,\n })\n if (!result) return\n setNameDialog(null)\n enqueueSnackbar(`“${draftName.trim()}” added${moved(result)}`, {\n variant: 'success',\n persist: false,\n })\n return\n }\n setNameDialog(null)\n await save(move.picklist, `“${draftName.trim()}” added`)\n return\n }\n const result = await post({ action: 'rename', valueId: nameDialog.value.id, label: draftName })\n if (!result) return\n setNameDialog(null)\n enqueueSnackbar(`Renamed${moved(result)}`, { variant: 'success', persist: false })\n }\n\n const openDelete = (value: CrmPicklistValue) => {\n setReplaceWith('')\n setDeleting(value)\n }\n const submitDelete = async () => {\n if (!deleting) return\n const result = await post({\n action: 'delete',\n valueId: deleting.id,\n replaceWith: replaceWith || null,\n })\n if (!result) return\n setDeleting(null)\n enqueueSnackbar(`“${deleting.label}” deleted${moved(result)}`, {\n variant: 'success',\n persist: false,\n })\n }\n\n const move = (from: number, to: number) =>\n void save(movePicklistValue(picklist, from, to), 'Order saved')\n\n const rowActions = (value: CrmPicklistValue): RowActionsMenuItem[] => {\n const isDefault = picklist.defaultValueId === value.id\n const items: RowActionsMenuItem[] = [\n {\n key: 'rename',\n label: 'Rename',\n icon: <MdiIcon path={mdiPencilOutline.path} size={0.8} />,\n disabled: busy,\n onClick: () => openRename(value),\n },\n {\n key: 'default',\n label: isDefault ? 'Clear default' : 'Make default',\n icon: <MdiIcon path={mdiStarOutline.path} size={0.8} />,\n disabled: busy || (!isDefault && !value.active),\n disabledReason: !value.active ? 'An inactive value cannot be the default' : undefined,\n onClick: () =>\n void save(\n setPicklistDefault(picklist, isDefault ? null : value.id),\n isDefault ? 'Default cleared' : `New ${nouns} start as “${value.label}”`,\n ),\n },\n {\n key: 'active',\n label: value.active ? 'Deactivate' : 'Activate',\n icon: (\n <MdiIcon path={value.active ? mdiEyeOffOutline.path : mdiEyeOutline.path} size={0.8} />\n ),\n disabled: busy,\n onClick: () =>\n void save(\n setPicklistValueActive(picklist, value.id, !value.active),\n value.active ? `“${value.label}” deactivated` : `“${value.label}” activated`,\n ),\n },\n ]\n // A standard value is every organization's: it is deactivated, never deleted.\n if (!isStandard(value)) {\n items.push({\n key: 'delete',\n label: 'Delete…',\n icon: <MdiIcon path={mdiDeleteOutline.path} size={0.8} />,\n destructive: true,\n disabled: busy,\n onClick: () => openDelete(value),\n })\n }\n return items\n }\n\n const replacements = deleting ? picklistReplacements(definition, picklist, deleting.id) : []\n const columns = 4 + (groups.length ? 1 : 0) + (meanings.length ? 1 : 0)\n const heldBy = sentenceList(holders.map(([, many]) => many)).replace(/^./, (letter) =>\n letter.toUpperCase(),\n )\n\n return (\n <Stack spacing={1.5}>\n <Stack\n direction={{ xs: 'column', sm: 'row' }}\n spacing={1}\n sx={{ justifyContent: 'space-between', alignItems: { sm: 'center' } }}\n >\n <Stack spacing={0.25}>\n <Typography variant=\"subtitle1\">{`${definition.label} values`}</Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {(suggestions\n ? `The suggestions every ${noun}’s ${definition.label} offers, in this order. ` +\n 'Any text may still be typed, and renaming or deleting a suggestion ' +\n `changes no ${noun}. `\n : `The choices in every ${noun}’s ${definition.label} select, in this order. ` +\n (definition.restricted\n ? 'An import or an API write naming anything else is refused. '\n : '') +\n 'A deactivated value stays on the records that hold it. ') +\n 'Standard values come with every organization and cannot be deleted; ' +\n 'add your own beside them.'}\n </Typography>\n </Stack>\n {orgId ? (\n <Stack direction=\"row\" spacing={1} sx={{ flexShrink: 0 }}>\n <Button\n size=\"small\"\n disabled={busy || picklist.values.length < 2}\n onClick={() => void save(sortPicklistValues(picklist), 'Sorted A–Z')}\n >\n {'Sort A–Z'}\n </Button>\n <Button size=\"small\" variant=\"outlined\" disabled={busy} onClick={openAdd}>\n {'Add value'}\n </Button>\n </Stack>\n ) : null}\n </Stack>\n {!ready ? null : (\n <ScrollTable size=\"small\" aria-label={`${definition.label} values`}>\n <TableHead>\n <TableRow>\n <TableCell sx={{ width: 120 }}>{'Order'}</TableCell>\n <TableCell>{'Value'}</TableCell>\n {groups.length ? <TableCell>{'Group'}</TableCell> : null}\n {meanings.length ? <TableCell>{'Means'}</TableCell> : null}\n <TableCell>{'Status'}</TableCell>\n <TableCell align=\"right\" />\n </TableRow>\n </TableHead>\n <TableBody>\n {picklist.values.length === 0 ? (\n <TableRow>\n <TableCell colSpan={columns}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`No values yet. Add one, and every ${noun} can be given it.`}\n </Typography>\n </TableCell>\n </TableRow>\n ) : null}\n {picklist.values.map((value, index) => (\n <TableRow\n key={value.id}\n hover\n // Native drag and drop for a pointer; the arrows beside the\n // handle are the same move for a keyboard or a touch screen.\n draggable={!busy}\n onDragStart={(event) => {\n setDragFrom(index)\n event.dataTransfer.effectAllowed = 'move'\n }}\n onDragOver={(event) => {\n if (dragFrom === null) return\n event.preventDefault()\n event.dataTransfer.dropEffect = 'move'\n }}\n onDrop={(event) => {\n event.preventDefault()\n if (dragFrom !== null && dragFrom !== index) move(dragFrom, index)\n setDragFrom(null)\n }}\n onDragEnd={() => setDragFrom(null)}\n sx={{\n ...(value.active ? {} : { opacity: 0.6 }),\n ...(dragFrom === index ? { outline: 1, outlineColor: 'primary.main' } : {}),\n }}\n >\n <TableCell>\n <Stack direction=\"row\" spacing={0} sx={{ alignItems: 'center' }}>\n <MdiIcon\n path={mdiDragVertical.path}\n size={0.8}\n style={{ cursor: busy ? 'default' : 'grab' }}\n aria-hidden\n />\n <IconButton\n size=\"small\"\n disabled={index === 0 || busy}\n onClick={() => move(index, index - 1)}\n >\n <MdiIcon path={mdiArrowUp.path} size={0.7} />\n <SrOnly>{`Move ${value.label} up`}</SrOnly>\n </IconButton>\n <IconButton\n size=\"small\"\n disabled={index === picklist.values.length - 1 || busy}\n onClick={() => move(index, index + 1)}\n >\n <MdiIcon path={mdiArrowDown.path} size={0.7} />\n <SrOnly>{`Move ${value.label} down`}</SrOnly>\n </IconButton>\n </Stack>\n </TableCell>\n <TableCell>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"body2\">{value.label}</Typography>\n {isStandard(value) ? (\n <Chip size=\"small\" variant=\"outlined\" label=\"Standard\" />\n ) : null}\n </Stack>\n </TableCell>\n {groups.length ? (\n <TableCell sx={{ minWidth: 150 }}>\n <TextField\n select\n size=\"small\"\n variant=\"standard\"\n value={value.group ?? ''}\n disabled={busy || !orgId}\n onChange={(event) => {\n const group = String(event.target.value) || null\n const name = groups.find((entry) => entry.id === group)?.label\n const done = name ? `“${value.label}” is ${name}` : `“${value.label}” has no group`\n if (!keepsGroups) {\n void save(setPicklistValueGroup(definition, picklist, value.id, group), done)\n return\n }\n // Its records keep the group too (AGL-3577): the route moves both.\n void post({ action: 'group', valueId: value.id, group }).then((result) => {\n if (result) {\n enqueueSnackbar(`${done}${moved(result)}`, { variant: 'success', persist: false })\n }\n })\n }}\n fullWidth\n slotProps={{\n select: { displayEmpty: true, 'aria-label': `Group of ${value.label}` },\n }}\n >\n <MenuItem value=\"\">{'None'}</MenuItem>\n {groups.map((group) => (\n <MenuItem key={group.id} value={group.id}>\n {group.label}\n </MenuItem>\n ))}\n </TextField>\n </TableCell>\n ) : null}\n {meanings.length ? (\n // What the value means to the CRM: fixed for a standard value, and\n // chosen when an added one is made.\n <TableCell>\n <Typography variant=\"body2\" color={value.meaning ? 'text.primary' : 'text.secondary'}>\n {value.meaning ? picklistMeaningLabel(definition, value.meaning) : '—'}\n </Typography>\n </TableCell>\n ) : null}\n <TableCell>\n <Stack useFlexGap direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', alignItems: 'center' }}>\n {value.active ? null : (\n <Chip size=\"small\" variant=\"outlined\" color=\"warning\" label=\"Inactive\" />\n )}\n {picklist.defaultValueId === value.id ? (\n <Chip size=\"small\" variant=\"outlined\" color=\"primary\" label=\"Default\" />\n ) : null}\n {value.active && picklist.defaultValueId !== value.id ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Active'}\n </Typography>\n ) : null}\n </Stack>\n </TableCell>\n <TableCell align=\"right\" sx={{ width: 56 }}>\n <RowActionsMenu label={value.label} items={rowActions(value)} />\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </ScrollTable>\n )}\n {ready && !stored ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'These are the standard values. Your first change here makes the list your ' +\n 'organization’s own.'}\n </Typography>\n ) : null}\n\n <Dialog open={nameDialog !== null} onClose={() => setNameDialog(null)} fullWidth maxWidth=\"xs\">\n <DialogTitle>\n {nameDialog?.mode === 'rename' ? `Rename “${nameDialog.value.label}”` : `Add a ${field}`}\n </DialogTitle>\n <DialogContent>\n <Stack spacing={1.5} sx={{ pt: 1 }}>\n {nameDialog?.mode === 'rename' && !suggestions ? (\n <DialogContentText variant=\"body2\">\n {`Every ${sentenceList(holders.map(([one]) => one))} holding this value ` +\n 'is updated to the new name.'}\n </DialogContentText>\n ) : null}\n <TextField\n size=\"small\"\n label=\"Value\"\n value={draftName}\n onChange={(event) => {\n setDraftName(event.target.value)\n setNameError('')\n }}\n onKeyDown={(event) => {\n if (event.key === 'Enter') void submitName()\n }}\n error={Boolean(nameError)}\n helperText={nameError || ' '}\n slotProps={{ htmlInput: { maxLength: CRM_LEAD_TEXT_MAX } }}\n autoFocus\n fullWidth\n />\n {nameDialog?.mode === 'add' && groups.length ? (\n <TextField\n select\n size=\"small\"\n label=\"Group\"\n value={draftGroup}\n onChange={(event) => setDraftGroup(String(event.target.value))}\n fullWidth\n slotProps={{ select: { displayEmpty: true }, inputLabel: { shrink: true } }}\n >\n <MenuItem value=\"\">{'None'}</MenuItem>\n {groups.map((group) => (\n <MenuItem key={group.id} value={group.id}>\n {group.label}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n {nameDialog?.mode === 'add' && meanings.length ? (\n <TextField\n select\n size=\"small\"\n label=\"Means\"\n value={draftMeaning}\n onChange={(event) => {\n setDraftMeaning(String(event.target.value))\n setNameError('')\n }}\n fullWidth\n >\n {addableMeanings.map((meaning) => (\n <MenuItem key={meaning} value={meaning}>\n {picklistMeaningLabel(definition, meaning)}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setNameDialog(null)}>{'Cancel'}</Button>\n <Button\n variant=\"contained\"\n disabled={busy || !draftName.trim()}\n onClick={() => void submitName()}\n >\n {nameDialog?.mode === 'rename' ? 'Rename' : 'Add'}\n </Button>\n </DialogActions>\n </Dialog>\n\n <Dialog open={deleting !== null} onClose={() => setDeleting(null)} fullWidth maxWidth=\"xs\">\n <DialogTitle>{`Delete “${deleting?.label ?? ''}”?`}</DialogTitle>\n <DialogContent>\n <Stack spacing={1.5} sx={{ pt: 1 }}>\n <DialogContentText variant=\"body2\">\n {suggestions\n ? `The suggestion leaves the list for good. No ${noun} changes.`\n : `The value leaves the list for good. ${heldBy} that hold it ` +\n (meanings.length\n ? 'are moved to the value you pick here, which means the same. '\n : 'are moved to the value you pick here, or cleared. ') +\n 'To keep it on those records instead, deactivate it.'}\n </DialogContentText>\n {suggestions ? null : (\n <TextField\n select\n size=\"small\"\n label=\"Replace it on existing records with\"\n value={replaceWith}\n onChange={(event) => setReplaceWith(String(event.target.value))}\n fullWidth\n slotProps={{ select: { displayEmpty: true }, inputLabel: { shrink: true } }}\n >\n {/* A value with a meaning moves its records to another of that meaning, never off. */}\n {meanings.length ? null : <MenuItem value=\"\">{'None — clear it'}</MenuItem>}\n {replacements.map((value) => (\n <MenuItem key={value.id} value={value.label}>\n {value.label}\n </MenuItem>\n ))}\n </TextField>\n )}\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setDeleting(null)}>{'Cancel'}</Button>\n <Button\n variant=\"contained\"\n color=\"error\"\n disabled={busy || (meanings.length > 0 && !replaceWith)}\n onClick={() => void submitDelete()}\n >\n {'Delete value'}\n </Button>\n </DialogActions>\n </Dialog>\n </Stack>\n )\n}\nPicklistValuesCard.displayName = 'PicklistValuesCard'\n\nexport default PicklistValuesCard\n"],"names":["CRM_COLLECTIONS","CRM_LEAD_TEXT_MAX","isStandardPicklistValueId","newResourceScopeFields","ORG_SCOPE_TOKEN","mdiArrowDown","mdiArrowUp","mdiDeleteOutline","mdiDragVertical","mdiEyeOffOutline","mdiEyeOutline","mdiPencilOutline","mdiStarOutline","MdiIcon","SrOnly","RowActionsMenu","ScrollTable","useSnackbar","authorizedFetch","useFirestore","useUser","writeGuardedBySeed","Button","Chip","Dialog","DialogActions","DialogContent","DialogContentText","DialogTitle","IconButton","MenuItem","Stack","TableBody","TableCell","TableHead","TableRow","TextField","Typography","doc","serverTimestamp","setDoc","useCallback","useState","useCrmPicklist","addPicklistValue","crmPicklistValuesRouteUrl","movePicklistValue","picklistAddableMeanings","picklistKeepsGroups","picklistMeaningLabel","picklistReplacements","setPicklistDefault","setPicklistValueActive","setPicklistValueGroup","sortPicklistValues","crmTaskCallScope","OBJECT_NOUNS","contact","company","deal","lead","task","sentenceList","words","length","slice","join","targetObjects","definition","Set","targets","map","target","object","PicklistValuesCard","props","picklistId","hostId","orgId","createHostId","firestore","data","user","enqueueSnackbar","picklist","stored","ready","fromCache","busy","setBusy","nameDialog","setNameDialog","draftName","setDraftName","draftGroup","setDraftGroup","draftMeaning","setDraftMeaning","nameError","setNameError","deleting","setDeleting","replaceWith","setReplaceWith","dragFrom","setDragFrom","groups","keepsGroups","meanings","addableMeanings","noun","nouns","holders","suggestions","field","label","toLowerCase","failed","plural","isStandard","value","id","save","next","done","verdict","subject","picklists","values","defaultValueId","updatedAt","createdAt","merge","ok","message","variant","persist","error","console","allowDuplicate","post","request","scope","response","method","headers","body","JSON","stringify","payload","json","catch","moved","result","counts","count","updated","one","many","text","toLocaleString","some","entry","openAdd","mode","openRename","submitName","move","group","meaning","action","trim","valueId","openDelete","submitDelete","from","to","rowActions","isDefault","items","key","icon","path","size","disabled","onClick","active","disabledReason","undefined","push","destructive","replacements","columns","heldBy","replace","letter","toUpperCase","spacing","direction","xs","sm","sx","justifyContent","alignItems","color","restricted","flexShrink","aria-label","width","align","colSpan","index","hover","draggable","onDragStart","event","dataTransfer","effectAllowed","onDragOver","preventDefault","dropEffect","onDrop","onDragEnd","opacity","outline","outlineColor","style","cursor","aria-hidden","minWidth","select","onChange","String","name","find","then","fullWidth","slotProps","displayEmpty","useFlexGap","flexWrap","open","onClose","maxWidth","pt","onKeyDown","Boolean","helperText","htmlInput","maxLength","autoFocus","inputLabel","shrink","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/picklist-values-card.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n CRM_COLLECTIONS,\n CRM_LEAD_TEXT_MAX,\n type CrmPicklist,\n type CrmPicklistDefinition,\n type CrmPicklistId,\n type CrmPicklistObject,\n type CrmPicklistValue,\n isStandardPicklistValueId,\n newResourceScopeFields,\n ORG_SCOPE_TOKEN,\n} from '@aglyn/aglyn'\nimport {\n mdiArrowDown,\n mdiArrowUp,\n mdiDeleteOutline,\n mdiDragVertical,\n mdiEyeOffOutline,\n mdiEyeOutline,\n mdiPencilOutline,\n mdiStarOutline,\n} from '@aglyn/shared-data-mdi'\nimport { MdiIcon, SrOnly } from '@aglyn/shared-ui-jsx'\nimport RowActionsMenu, {\n type RowActionsMenuItem,\n} from '@aglyn/shared-ui-jsx/components/row-actions-menu.component'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport {\n useFirestore,\n useUser,\n writeGuardedBySeed,\n} from '@aglyn/tenant-feature-instance'\nimport {\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogContentText,\n DialogTitle,\n IconButton,\n MenuItem,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TextField,\n Typography,\n} from '@mui/material'\nimport { doc, serverTimestamp, setDoc } from 'firebase/firestore'\nimport { useCallback, useState } from 'react'\nimport { useCrmPicklist } from '../hooks/use-crm-picklist'\nimport {\n addPicklistValue,\n crmPicklistValuesRouteUrl,\n movePicklistValue,\n picklistAddableMeanings,\n picklistKeepsGroups,\n picklistMeaningLabel,\n picklistReplacements,\n type PicklistValuesAction,\n type PicklistValuesRequest,\n type PicklistValuesResponse,\n setPicklistDefault,\n setPicklistValueActive,\n setPicklistValueGroup,\n sortPicklistValues,\n} from '../model/picklist-values'\nimport { crmTaskCallScope } from '../model/task-routes'\n\nexport interface PicklistValuesCardProps {\n /** Which of the CRM's standard picklists this card manages. */\n picklistId: CrmPicklistId\n /** The mounted site, or `null` at the organization level. */\n hostId: string | null\n /** The org whose list this is; `null` while it settles. */\n orgId: string | null\n /** The site a newly written list records as its definer — the Fields page's own. */\n createHostId: string | null\n}\n\n/** Each object's noun, singular and plural, for the card's sentences. */\nconst OBJECT_NOUNS: Record<CrmPicklistObject, [string, string]> = {\n contact: ['contact', 'contacts'],\n company: ['company', 'companies'],\n deal: ['deal', 'deals'],\n lead: ['lead', 'leads'],\n task: ['task', 'tasks'],\n}\n\n/** \"a\", \"a and b\", \"a, b and c\". */\nfunction sentenceList(words: readonly string[]): string {\n return words.length < 2\n ? (words[0] ?? '')\n : `${words.slice(0, -1).join(', ')} and ${words[words.length - 1]}`\n}\n\n/** The objects whose records hold the field's label, in the definition's order. */\nfunction targetObjects(definition: CrmPicklistDefinition): CrmPicklistObject[] {\n return [...new Set(definition.targets.map((target) => target.object))]\n}\n\n/** The add and rename dialog's state: which value, if any, is being renamed. */\ntype NameDialog = { mode: 'add' } | { mode: 'rename'; value: CrmPicklistValue } | null\n\n/**\n * ONE STANDARD PICKLIST'S VALUES (AGL-3298, AGL-3510) — Salesforce's\n * picklist value set for a standard field, managed on the Fields tab of the\n * object the field belongs to.\n *\n * Standard values are marked, and cannot be deleted — every org has them —\n * but rename, reorder, regroup, deactivate and default are theirs as much\n * as an added value's. Every move that touches only the LIST — add,\n * reorder by drag or arrows, sort A–Z, activate, deactivate, group,\n * default — is one client write of the whole document, guarded against a\n * cached read like every seeded save on the Fields page. The ones that\n * change RECORDS — rename, and delete with a replacement, and on a list\n * whose records keep the value's group (Lead source's direction, AGL-3577)\n * group and add — go to `crm/picklist-values`, which rewrites the list and\n * every record the definition's targets name together; see that route.\n *\n * An org that has never edited the list reads the standard values, and the\n * first move here writes them down as the org's own.\n */\nexport function PicklistValuesCard(props: PicklistValuesCardProps) {\n const { picklistId, hostId, orgId, createHostId } = props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const { definition, picklist, stored, ready, fromCache } = useCrmPicklist(picklistId, orgId)\n const [busy, setBusy] = useState(false)\n const [nameDialog, setNameDialog] = useState<NameDialog>(null)\n const [draftName, setDraftName] = useState('')\n const [draftGroup, setDraftGroup] = useState('')\n const [draftMeaning, setDraftMeaning] = useState('')\n const [nameError, setNameError] = useState('')\n const [deleting, setDeleting] = useState<CrmPicklistValue | null>(null)\n const [replaceWith, setReplaceWith] = useState('')\n const [dragFrom, setDragFrom] = useState<number | null>(null)\n\n const groups = definition.groups ?? []\n // Whether records keep a value's group, so a regroup and an add change them too.\n const keepsGroups = picklistKeepsGroups(definition)\n const meanings = definition.meanings ?? []\n // An added value may not take a meaning only the platform sets (AGL-3512).\n const addableMeanings = picklistAddableMeanings(definition)\n const [noun, nouns] = OBJECT_NOUNS[definition.object]\n const holders = targetObjects(definition).map((object) => OBJECT_NOUNS[object])\n // A list of suggestions only — a task's subjects (AGL-3517) — that no record holds.\n const suggestions = holders.length === 0\n const field = definition.label.toLowerCase()\n const failed = `The ${definition.plural} could not be saved.`\n const isStandard = (value: CrmPicklistValue) => isStandardPicklistValueId(definition, value.id)\n\n /** Write the whole list — the list-only moves above. */\n const save = useCallback(\n async (next: CrmPicklist, done: string): Promise<boolean> => {\n if (!orgId || busy) return false\n setBusy(true)\n try {\n const verdict = await writeGuardedBySeed({ subject: `${field} list`, fromCache }, () =>\n setDoc(\n doc(firestore, 'orgs', orgId, CRM_COLLECTIONS.picklists, picklistId),\n {\n values: next.values,\n defaultValueId: next.defaultValueId,\n updatedAt: serverTimestamp(),\n // The org-wide stamp the rules require of a new CRM document,\n // the same one a field definition carries.\n ...(stored\n ? {}\n : {\n hostId: createHostId,\n createdAt: serverTimestamp(),\n ...newResourceScopeFields([ORG_SCOPE_TOKEN]),\n }),\n },\n { merge: true },\n ),\n )\n if (!verdict.ok) {\n enqueueSnackbar(verdict.message, { variant: 'warning', persist: false })\n return false\n }\n enqueueSnackbar(done, { variant: 'success', persist: false })\n return true\n } catch (error) {\n console.error(error)\n enqueueSnackbar(failed, { variant: 'error', allowDuplicate: true })\n return false\n } finally {\n setBusy(false)\n }\n },\n [orgId, busy, field, fromCache, firestore, picklistId, stored, createHostId, enqueueSnackbar, failed],\n )\n\n /** A rename or a delete — the moves that change records too. */\n const post = useCallback(\n async (request: PicklistValuesAction): Promise<PicklistValuesResponse | null> => {\n const scope = crmTaskCallScope(hostId, orgId)\n if (!scope || busy) return null\n setBusy(true)\n try {\n const response = await authorizedFetch(user, crmPicklistValuesRouteUrl(), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ ...request, picklistId, ...scope } satisfies PicklistValuesRequest),\n })\n const payload = (await response.json().catch(() => ({}))) as { error?: string }\n if (!response.ok) {\n enqueueSnackbar(payload.error || failed, { variant: 'warning', persist: false })\n return null\n }\n return payload as PicklistValuesResponse\n } catch (error) {\n console.error(error)\n enqueueSnackbar(failed, { variant: 'error', allowDuplicate: true })\n return null\n } finally {\n setBusy(false)\n }\n },\n [hostId, orgId, busy, user, picklistId, enqueueSnackbar, failed],\n )\n\n /** \" — 2 leads and 1 contact updated\", or nothing when no record held the value. */\n const moved = (result: PicklistValuesResponse) => {\n const counts = targetObjects(definition).map((object) => {\n const count = result.updated?.[object] ?? 0\n const [one, many] = OBJECT_NOUNS[object]\n return { count, text: `${count.toLocaleString()} ${count === 1 ? one : many}` }\n })\n return counts.some((entry) => entry.count)\n ? ` — ${sentenceList(counts.map((entry) => entry.text))} updated`\n : ''\n }\n\n const openAdd = () => {\n setDraftName('')\n setDraftGroup('')\n setDraftMeaning('')\n setNameError('')\n setNameDialog({ mode: 'add' })\n }\n const openRename = (value: CrmPicklistValue) => {\n setDraftName(value.label)\n setNameError('')\n setNameDialog({ mode: 'rename', value })\n }\n\n const submitName = async () => {\n if (!nameDialog) return\n if (nameDialog.mode === 'add') {\n const move = addPicklistValue(definition, picklist, draftName, {\n group: draftGroup || null,\n meaning: draftMeaning || null,\n })\n if (move.ok === false) return void setNameError(move.error)\n if (keepsGroups) {\n // Records may already hold the label, and keep its group (AGL-3577).\n const result = await post({\n action: 'add',\n label: draftName,\n group: draftGroup || null,\n meaning: draftMeaning || null,\n })\n if (!result) return\n setNameDialog(null)\n enqueueSnackbar(`“${draftName.trim()}” added${moved(result)}`, {\n variant: 'success',\n persist: false,\n })\n return\n }\n // The dialog holds what was typed until the list has really saved\n // (AGL-4120): closing first made a refused add read as done.\n if (await save(move.picklist, `“${draftName.trim()}” added`)) setNameDialog(null)\n return\n }\n const result = await post({ action: 'rename', valueId: nameDialog.value.id, label: draftName })\n if (!result) return\n setNameDialog(null)\n enqueueSnackbar(`Renamed${moved(result)}`, { variant: 'success', persist: false })\n }\n\n const openDelete = (value: CrmPicklistValue) => {\n setReplaceWith('')\n setDeleting(value)\n }\n const submitDelete = async () => {\n if (!deleting) return\n const result = await post({\n action: 'delete',\n valueId: deleting.id,\n replaceWith: replaceWith || null,\n })\n if (!result) return\n setDeleting(null)\n enqueueSnackbar(`“${deleting.label}” deleted${moved(result)}`, {\n variant: 'success',\n persist: false,\n })\n }\n\n const move = (from: number, to: number) =>\n void save(movePicklistValue(picklist, from, to), 'Order saved')\n\n const rowActions = (value: CrmPicklistValue): RowActionsMenuItem[] => {\n const isDefault = picklist.defaultValueId === value.id\n const items: RowActionsMenuItem[] = [\n {\n key: 'rename',\n label: 'Rename',\n icon: <MdiIcon path={mdiPencilOutline.path} size={0.8} />,\n disabled: busy,\n onClick: () => openRename(value),\n },\n {\n key: 'default',\n label: isDefault ? 'Clear default' : 'Make default',\n icon: <MdiIcon path={mdiStarOutline.path} size={0.8} />,\n disabled: busy || (!isDefault && !value.active),\n disabledReason: !value.active ? 'An inactive value cannot be the default' : undefined,\n onClick: () =>\n void save(\n setPicklistDefault(picklist, isDefault ? null : value.id),\n isDefault ? 'Default cleared' : `New ${nouns} start as “${value.label}”`,\n ),\n },\n {\n key: 'active',\n label: value.active ? 'Deactivate' : 'Activate',\n icon: (\n <MdiIcon path={value.active ? mdiEyeOffOutline.path : mdiEyeOutline.path} size={0.8} />\n ),\n disabled: busy,\n onClick: () =>\n void save(\n setPicklistValueActive(picklist, value.id, !value.active),\n value.active ? `“${value.label}” deactivated` : `“${value.label}” activated`,\n ),\n },\n ]\n // A standard value is every organization's: it is deactivated, never deleted.\n if (!isStandard(value)) {\n items.push({\n key: 'delete',\n label: 'Delete…',\n icon: <MdiIcon path={mdiDeleteOutline.path} size={0.8} />,\n destructive: true,\n disabled: busy,\n onClick: () => openDelete(value),\n })\n }\n return items\n }\n\n const replacements = deleting ? picklistReplacements(definition, picklist, deleting.id) : []\n const columns = 4 + (groups.length ? 1 : 0) + (meanings.length ? 1 : 0)\n const heldBy = sentenceList(holders.map(([, many]) => many)).replace(/^./, (letter) =>\n letter.toUpperCase(),\n )\n\n return (\n <Stack spacing={1.5}>\n <Stack\n direction={{ xs: 'column', sm: 'row' }}\n spacing={1}\n sx={{ justifyContent: 'space-between', alignItems: { sm: 'center' } }}\n >\n <Stack spacing={0.25}>\n <Typography variant=\"subtitle1\">{`${definition.label} values`}</Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {(suggestions\n ? `The suggestions every ${noun}’s ${definition.label} offers, in this order. ` +\n 'Any text may still be typed, and renaming or deleting a suggestion ' +\n `changes no ${noun}. `\n : `The choices in every ${noun}’s ${definition.label} select, in this order. ` +\n (definition.restricted\n ? 'An import or an API write naming anything else is refused. '\n : '') +\n 'A deactivated value stays on the records that hold it. ') +\n 'Standard values come with every organization and cannot be deleted; ' +\n 'add your own beside them.'}\n </Typography>\n </Stack>\n {orgId ? (\n <Stack direction=\"row\" spacing={1} sx={{ flexShrink: 0 }}>\n <Button\n size=\"small\"\n disabled={busy || picklist.values.length < 2}\n onClick={() => void save(sortPicklistValues(picklist), 'Sorted A–Z')}\n >\n {'Sort A–Z'}\n </Button>\n <Button size=\"small\" variant=\"outlined\" disabled={busy} onClick={openAdd}>\n {'Add value'}\n </Button>\n </Stack>\n ) : null}\n </Stack>\n {!ready ? null : (\n <ScrollTable size=\"small\" aria-label={`${definition.label} values`}>\n <TableHead>\n <TableRow>\n <TableCell sx={{ width: 120 }}>{'Order'}</TableCell>\n <TableCell>{'Value'}</TableCell>\n {groups.length ? <TableCell>{'Group'}</TableCell> : null}\n {meanings.length ? <TableCell>{'Means'}</TableCell> : null}\n <TableCell>{'Status'}</TableCell>\n <TableCell align=\"right\" />\n </TableRow>\n </TableHead>\n <TableBody>\n {picklist.values.length === 0 ? (\n <TableRow>\n <TableCell colSpan={columns}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`No values yet. Add one, and every ${noun} can be given it.`}\n </Typography>\n </TableCell>\n </TableRow>\n ) : null}\n {picklist.values.map((value, index) => (\n <TableRow\n key={value.id}\n hover\n // Native drag and drop for a pointer; the arrows beside the\n // handle are the same move for a keyboard or a touch screen.\n draggable={!busy}\n onDragStart={(event) => {\n setDragFrom(index)\n event.dataTransfer.effectAllowed = 'move'\n }}\n onDragOver={(event) => {\n if (dragFrom === null) return\n event.preventDefault()\n event.dataTransfer.dropEffect = 'move'\n }}\n onDrop={(event) => {\n event.preventDefault()\n if (dragFrom !== null && dragFrom !== index) move(dragFrom, index)\n setDragFrom(null)\n }}\n onDragEnd={() => setDragFrom(null)}\n sx={{\n ...(value.active ? {} : { opacity: 0.6 }),\n ...(dragFrom === index ? { outline: 1, outlineColor: 'primary.main' } : {}),\n }}\n >\n <TableCell>\n <Stack direction=\"row\" spacing={0} sx={{ alignItems: 'center' }}>\n <MdiIcon\n path={mdiDragVertical.path}\n size={0.8}\n style={{ cursor: busy ? 'default' : 'grab' }}\n aria-hidden\n />\n <IconButton\n size=\"small\"\n disabled={index === 0 || busy}\n onClick={() => move(index, index - 1)}\n >\n <MdiIcon path={mdiArrowUp.path} size={0.7} />\n <SrOnly>{`Move ${value.label} up`}</SrOnly>\n </IconButton>\n <IconButton\n size=\"small\"\n disabled={index === picklist.values.length - 1 || busy}\n onClick={() => move(index, index + 1)}\n >\n <MdiIcon path={mdiArrowDown.path} size={0.7} />\n <SrOnly>{`Move ${value.label} down`}</SrOnly>\n </IconButton>\n </Stack>\n </TableCell>\n <TableCell>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"body2\">{value.label}</Typography>\n {isStandard(value) ? (\n <Chip size=\"small\" variant=\"outlined\" label=\"Standard\" />\n ) : null}\n </Stack>\n </TableCell>\n {groups.length ? (\n <TableCell sx={{ minWidth: 150 }}>\n <TextField\n select\n size=\"small\"\n variant=\"standard\"\n value={value.group ?? ''}\n disabled={busy || !orgId}\n onChange={(event) => {\n const group = String(event.target.value) || null\n const name = groups.find((entry) => entry.id === group)?.label\n const done = name ? `“${value.label}” is ${name}` : `“${value.label}” has no group`\n if (!keepsGroups) {\n void save(setPicklistValueGroup(definition, picklist, value.id, group), done)\n return\n }\n // Its records keep the group too (AGL-3577): the route moves both.\n void post({ action: 'group', valueId: value.id, group }).then((result) => {\n if (result) {\n enqueueSnackbar(`${done}${moved(result)}`, { variant: 'success', persist: false })\n }\n })\n }}\n fullWidth\n slotProps={{\n select: { displayEmpty: true, 'aria-label': `Group of ${value.label}` },\n }}\n >\n <MenuItem value=\"\">{'None'}</MenuItem>\n {groups.map((group) => (\n <MenuItem key={group.id} value={group.id}>\n {group.label}\n </MenuItem>\n ))}\n </TextField>\n </TableCell>\n ) : null}\n {meanings.length ? (\n // What the value means to the CRM: fixed for a standard value, and\n // chosen when an added one is made.\n <TableCell>\n <Typography variant=\"body2\" color={value.meaning ? 'text.primary' : 'text.secondary'}>\n {value.meaning ? picklistMeaningLabel(definition, value.meaning) : '—'}\n </Typography>\n </TableCell>\n ) : null}\n <TableCell>\n <Stack useFlexGap direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', alignItems: 'center' }}>\n {value.active ? null : (\n <Chip size=\"small\" variant=\"outlined\" color=\"warning\" label=\"Inactive\" />\n )}\n {picklist.defaultValueId === value.id ? (\n <Chip size=\"small\" variant=\"outlined\" color=\"primary\" label=\"Default\" />\n ) : null}\n {value.active && picklist.defaultValueId !== value.id ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Active'}\n </Typography>\n ) : null}\n </Stack>\n </TableCell>\n <TableCell align=\"right\" sx={{ width: 56 }}>\n <RowActionsMenu label={value.label} items={rowActions(value)} />\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </ScrollTable>\n )}\n {ready && !stored ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'These are the standard values. Your first change here makes the list your ' +\n 'organization’s own.'}\n </Typography>\n ) : null}\n\n <Dialog open={nameDialog !== null} onClose={() => setNameDialog(null)} fullWidth maxWidth=\"xs\">\n <DialogTitle>\n {nameDialog?.mode === 'rename' ? `Rename “${nameDialog.value.label}”` : `Add a ${field}`}\n </DialogTitle>\n <DialogContent>\n <Stack spacing={1.5} sx={{ pt: 1 }}>\n {nameDialog?.mode === 'rename' && !suggestions ? (\n <DialogContentText variant=\"body2\">\n {`Every ${sentenceList(holders.map(([one]) => one))} holding this value ` +\n 'is updated to the new name.'}\n </DialogContentText>\n ) : null}\n <TextField\n size=\"small\"\n label=\"Value\"\n value={draftName}\n onChange={(event) => {\n setDraftName(event.target.value)\n setNameError('')\n }}\n onKeyDown={(event) => {\n if (event.key === 'Enter') void submitName()\n }}\n error={Boolean(nameError)}\n helperText={nameError || ' '}\n slotProps={{ htmlInput: { maxLength: CRM_LEAD_TEXT_MAX } }}\n autoFocus\n fullWidth\n />\n {nameDialog?.mode === 'add' && groups.length ? (\n <TextField\n select\n size=\"small\"\n label=\"Group\"\n value={draftGroup}\n onChange={(event) => setDraftGroup(String(event.target.value))}\n fullWidth\n slotProps={{ select: { displayEmpty: true }, inputLabel: { shrink: true } }}\n >\n <MenuItem value=\"\">{'None'}</MenuItem>\n {groups.map((group) => (\n <MenuItem key={group.id} value={group.id}>\n {group.label}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n {nameDialog?.mode === 'add' && meanings.length ? (\n <TextField\n select\n size=\"small\"\n label=\"Means\"\n value={draftMeaning}\n onChange={(event) => {\n setDraftMeaning(String(event.target.value))\n setNameError('')\n }}\n fullWidth\n >\n {addableMeanings.map((meaning) => (\n <MenuItem key={meaning} value={meaning}>\n {picklistMeaningLabel(definition, meaning)}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setNameDialog(null)}>{'Cancel'}</Button>\n <Button\n variant=\"contained\"\n disabled={busy || !draftName.trim()}\n onClick={() => void submitName()}\n >\n {nameDialog?.mode === 'rename' ? 'Rename' : 'Add'}\n </Button>\n </DialogActions>\n </Dialog>\n\n <Dialog open={deleting !== null} onClose={() => setDeleting(null)} fullWidth maxWidth=\"xs\">\n <DialogTitle>{`Delete “${deleting?.label ?? ''}”?`}</DialogTitle>\n <DialogContent>\n <Stack spacing={1.5} sx={{ pt: 1 }}>\n <DialogContentText variant=\"body2\">\n {suggestions\n ? `The suggestion leaves the list for good. No ${noun} changes.`\n : `The value leaves the list for good. ${heldBy} that hold it ` +\n (meanings.length\n ? 'are moved to the value you pick here, which means the same. '\n : 'are moved to the value you pick here, or cleared. ') +\n 'To keep it on those records instead, deactivate it.'}\n </DialogContentText>\n {suggestions ? null : (\n <TextField\n select\n size=\"small\"\n label=\"Replace it on existing records with\"\n value={replaceWith}\n onChange={(event) => setReplaceWith(String(event.target.value))}\n fullWidth\n slotProps={{ select: { displayEmpty: true }, inputLabel: { shrink: true } }}\n >\n {/* A value with a meaning moves its records to another of that meaning, never off. */}\n {meanings.length ? null : <MenuItem value=\"\">{'None — clear it'}</MenuItem>}\n {replacements.map((value) => (\n <MenuItem key={value.id} value={value.label}>\n {value.label}\n </MenuItem>\n ))}\n </TextField>\n )}\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setDeleting(null)}>{'Cancel'}</Button>\n <Button\n variant=\"contained\"\n color=\"error\"\n disabled={busy || (meanings.length > 0 && !replaceWith)}\n onClick={() => void submitDelete()}\n >\n {'Delete value'}\n </Button>\n </DialogActions>\n </Dialog>\n </Stack>\n )\n}\nPicklistValuesCard.displayName = 'PicklistValuesCard'\n\nexport default PicklistValuesCard\n"],"names":["CRM_COLLECTIONS","CRM_LEAD_TEXT_MAX","isStandardPicklistValueId","newResourceScopeFields","ORG_SCOPE_TOKEN","mdiArrowDown","mdiArrowUp","mdiDeleteOutline","mdiDragVertical","mdiEyeOffOutline","mdiEyeOutline","mdiPencilOutline","mdiStarOutline","MdiIcon","SrOnly","RowActionsMenu","ScrollTable","useSnackbar","authorizedFetch","useFirestore","useUser","writeGuardedBySeed","Button","Chip","Dialog","DialogActions","DialogContent","DialogContentText","DialogTitle","IconButton","MenuItem","Stack","TableBody","TableCell","TableHead","TableRow","TextField","Typography","doc","serverTimestamp","setDoc","useCallback","useState","useCrmPicklist","addPicklistValue","crmPicklistValuesRouteUrl","movePicklistValue","picklistAddableMeanings","picklistKeepsGroups","picklistMeaningLabel","picklistReplacements","setPicklistDefault","setPicklistValueActive","setPicklistValueGroup","sortPicklistValues","crmTaskCallScope","OBJECT_NOUNS","contact","company","deal","lead","task","sentenceList","words","length","slice","join","targetObjects","definition","Set","targets","map","target","object","PicklistValuesCard","props","picklistId","hostId","orgId","createHostId","firestore","data","user","enqueueSnackbar","picklist","stored","ready","fromCache","busy","setBusy","nameDialog","setNameDialog","draftName","setDraftName","draftGroup","setDraftGroup","draftMeaning","setDraftMeaning","nameError","setNameError","deleting","setDeleting","replaceWith","setReplaceWith","dragFrom","setDragFrom","groups","keepsGroups","meanings","addableMeanings","noun","nouns","holders","suggestions","field","label","toLowerCase","failed","plural","isStandard","value","id","save","next","done","verdict","subject","picklists","values","defaultValueId","updatedAt","createdAt","merge","ok","message","variant","persist","error","console","allowDuplicate","post","request","scope","response","method","headers","body","JSON","stringify","payload","json","catch","moved","result","counts","count","updated","one","many","text","toLocaleString","some","entry","openAdd","mode","openRename","submitName","move","group","meaning","action","trim","valueId","openDelete","submitDelete","from","to","rowActions","isDefault","items","key","icon","path","size","disabled","onClick","active","disabledReason","undefined","push","destructive","replacements","columns","heldBy","replace","letter","toUpperCase","spacing","direction","xs","sm","sx","justifyContent","alignItems","color","restricted","flexShrink","aria-label","width","align","colSpan","index","hover","draggable","onDragStart","event","dataTransfer","effectAllowed","onDragOver","preventDefault","dropEffect","onDrop","onDragEnd","opacity","outline","outlineColor","style","cursor","aria-hidden","minWidth","select","onChange","String","name","find","then","fullWidth","slotProps","displayEmpty","useFlexGap","flexWrap","open","onClose","maxWidth","pt","onKeyDown","Boolean","helperText","htmlInput","maxLength","autoFocus","inputLabel","shrink","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;;AAEA,SACEA,eAAe,EACfC,iBAAiB,EAMjBC,yBAAyB,EACzBC,sBAAsB,EACtBC,eAAe,QACV,eAAc;AACrB,SACEC,YAAY,EACZC,UAAU,EACVC,gBAAgB,EAChBC,eAAe,EACfC,gBAAgB,EAChBC,aAAa,EACbC,gBAAgB,EAChBC,cAAc,QACT,yBAAwB;AAC/B,SAASC,OAAO,EAAEC,MAAM,QAAQ,uBAAsB;AACtD,OAAOC,oBAEA,6DAA4D;AACnE,SAASC,WAAW,QAAQ,yDAAwD;AACpF,SAASC,WAAW,QAAQ,8BAA6B;AACzD,SAASC,eAAe,QAAQ,2CAA0C;AAC1E,SACEC,YAAY,EACZC,OAAO,EACPC,kBAAkB,QACb,iCAAgC;AACvC,SACEC,MAAM,EACNC,IAAI,EACJC,MAAM,EACNC,aAAa,EACbC,aAAa,EACbC,iBAAiB,EACjBC,WAAW,EACXC,UAAU,EACVC,QAAQ,EACRC,KAAK,EACLC,SAAS,EACTC,SAAS,EACTC,SAAS,EACTC,QAAQ,EACRC,SAAS,EACTC,UAAU,QACL,gBAAe;AACtB,SAASC,GAAG,EAAEC,eAAe,EAAEC,MAAM,QAAQ,qBAAoB;AACjE,SAASC,WAAW,EAAEC,QAAQ,QAAQ,QAAO;AAC7C,SAASC,cAAc,QAAQ,+BAA2B;AAC1D,SACEC,gBAAgB,EAChBC,yBAAyB,EACzBC,iBAAiB,EACjBC,uBAAuB,EACvBC,mBAAmB,EACnBC,oBAAoB,EACpBC,oBAAoB,EAIpBC,kBAAkB,EAClBC,sBAAsB,EACtBC,qBAAqB,EACrBC,kBAAkB,QACb,8BAA0B;AACjC,SAASC,gBAAgB,QAAQ,0BAAsB;AAavD,uEAAuE,GACvE,MAAMC,eAA4D;IAChEC,SAAS;QAAC;QAAW;KAAW;IAChCC,SAAS;QAAC;QAAW;KAAY;IACjCC,MAAM;QAAC;QAAQ;KAAQ;IACvBC,MAAM;QAAC;QAAQ;KAAQ;IACvBC,MAAM;QAAC;QAAQ;KAAQ;AACzB;AAEA,kCAAkC,GAClC,SAASC,aAAaC,KAAwB;QAEvCA;IADL,OAAOA,MAAMC,MAAM,GAAG,KACjBD,UAAAA,KAAK,CAAC,EAAE,YAARA,UAAY,KACb,GAAGA,MAAME,KAAK,CAAC,GAAG,CAAC,GAAGC,IAAI,CAAC,MAAM,KAAK,EAAEH,KAAK,CAACA,MAAMC,MAAM,GAAG,EAAE,EAAE;AACvE;AAEA,iFAAiF,GACjF,SAASG,cAAcC,UAAiC;IACtD,OAAO;WAAI,IAAIC,IAAID,WAAWE,OAAO,CAACC,GAAG,CAAC,CAACC,SAAWA,OAAOC,MAAM;KAAG;AACxE;AAKA;;;;;;;;;;;;;;;;;;CAkBC,GACD,OAAO,SAASC,mBAAmBC,KAA8B;QAgBhDP,oBAGEA;IAlBjB,MAAM,EAAEQ,UAAU,EAAEC,MAAM,EAAEC,KAAK,EAAEC,YAAY,EAAE,GAAGJ;IACpD,MAAMK,YAAY7D;IAClB,MAAM,EAAE8D,MAAMC,IAAI,EAAE,GAAG9D;IACvB,MAAM,EAAE+D,eAAe,EAAE,GAAGlE;IAC5B,MAAM,EAAEmD,UAAU,EAAEgB,QAAQ,EAAEC,MAAM,EAAEC,KAAK,EAAEC,SAAS,EAAE,GAAG5C,eAAeiC,YAAYE;IACtF,MAAM,CAACU,MAAMC,QAAQ,GAAG/C,SAAS;IACjC,MAAM,CAACgD,YAAYC,cAAc,GAAGjD,SAAqB;IACzD,MAAM,CAACkD,WAAWC,aAAa,GAAGnD,SAAS;IAC3C,MAAM,CAACoD,YAAYC,cAAc,GAAGrD,SAAS;IAC7C,MAAM,CAACsD,cAAcC,gBAAgB,GAAGvD,SAAS;IACjD,MAAM,CAACwD,WAAWC,aAAa,GAAGzD,SAAS;IAC3C,MAAM,CAAC0D,UAAUC,YAAY,GAAG3D,SAAkC;IAClE,MAAM,CAAC4D,aAAaC,eAAe,GAAG7D,SAAS;IAC/C,MAAM,CAAC8D,UAAUC,YAAY,GAAG/D,SAAwB;IAExD,MAAMgE,UAAStC,qBAAAA,WAAWsC,MAAM,YAAjBtC,qBAAqB,EAAE;IACtC,iFAAiF;IACjF,MAAMuC,cAAc3D,oBAAoBoB;IACxC,MAAMwC,YAAWxC,uBAAAA,WAAWwC,QAAQ,YAAnBxC,uBAAuB,EAAE;IAC1C,2EAA2E;IAC3E,MAAMyC,kBAAkB9D,wBAAwBqB;IAChD,MAAM,CAAC0C,MAAMC,MAAM,GAAGvD,YAAY,CAACY,WAAWK,MAAM,CAAC;IACrD,MAAMuC,UAAU7C,cAAcC,YAAYG,GAAG,CAAC,CAACE,SAAWjB,YAAY,CAACiB,OAAO;IAC9E,oFAAoF;IACpF,MAAMwC,cAAcD,QAAQhD,MAAM,KAAK;IACvC,MAAMkD,QAAQ9C,WAAW+C,KAAK,CAACC,WAAW;IAC1C,MAAMC,SAAS,CAAC,IAAI,EAAEjD,WAAWkD,MAAM,CAAC,oBAAoB,CAAC;IAC7D,MAAMC,aAAa,CAACC,QAA4BtH,0BAA0BkE,YAAYoD,MAAMC,EAAE;IAE9F,sDAAsD,GACtD,MAAMC,OAAOjF,YACX,OAAOkF,MAAmBC;QACxB,IAAI,CAAC9C,SAASU,MAAM,OAAO;QAC3BC,QAAQ;QACR,IAAI;YACF,MAAMoC,UAAU,MAAMxG,mBAAmB;gBAAEyG,SAAS,GAAGZ,MAAM,KAAK,CAAC;gBAAE3B;YAAU,GAAG,IAChF/C,OACEF,IAAI0C,WAAW,QAAQF,OAAO9E,gBAAgB+H,SAAS,EAAEnD,aACzD;oBACEoD,QAAQL,KAAKK,MAAM;oBACnBC,gBAAgBN,KAAKM,cAAc;oBACnCC,WAAW3F;mBAGP8C,SACA,CAAC,IACD;oBACER,QAAQE;oBACRoD,WAAW5F;mBACRpC,uBAAuB;oBAACC;iBAAgB,KAGnD;oBAAEgI,OAAO;gBAAK;YAGlB,IAAI,CAACP,QAAQQ,EAAE,EAAE;gBACflD,gBAAgB0C,QAAQS,OAAO,EAAE;oBAAEC,SAAS;oBAAWC,SAAS;gBAAM;gBACtE,OAAO;YACT;YACArD,gBAAgByC,MAAM;gBAAEW,SAAS;gBAAWC,SAAS;YAAM;YAC3D,OAAO;QACT,EAAE,OAAOC,OAAO;YACdC,QAAQD,KAAK,CAACA;YACdtD,gBAAgBkC,QAAQ;gBAAEkB,SAAS;gBAASI,gBAAgB;YAAK;YACjE,OAAO;QACT,SAAU;YACRlD,QAAQ;QACV;IACF,GACA;QAACX;QAAOU;QAAM0B;QAAO3B;QAAWP;QAAWJ;QAAYS;QAAQN;QAAcI;QAAiBkC;KAAO;IAGvG,8DAA8D,GAC9D,MAAMuB,OAAOnG,YACX,OAAOoG;QACL,MAAMC,QAAQvF,iBAAiBsB,QAAQC;QACvC,IAAI,CAACgE,SAAStD,MAAM,OAAO;QAC3BC,QAAQ;QACR,IAAI;YACF,MAAMsD,WAAW,MAAM7H,gBAAgBgE,MAAMrC,6BAA6B;gBACxEmG,QAAQ;gBACRC,SAAS;oBAAE,gBAAgB;gBAAmB;gBAC9CC,MAAMC,KAAKC,SAAS,CAAC,aAAKP;oBAASjE;mBAAekE;YACpD;YACA,MAAMO,UAAW,MAAMN,SAASO,IAAI,GAAGC,KAAK,CAAC,IAAO,CAAA,CAAC,CAAA;YACrD,IAAI,CAACR,SAASV,EAAE,EAAE;gBAChBlD,gBAAgBkE,QAAQZ,KAAK,IAAIpB,QAAQ;oBAAEkB,SAAS;oBAAWC,SAAS;gBAAM;gBAC9E,OAAO;YACT;YACA,OAAOa;QACT,EAAE,OAAOZ,OAAO;YACdC,QAAQD,KAAK,CAACA;YACdtD,gBAAgBkC,QAAQ;gBAAEkB,SAAS;gBAASI,gBAAgB;YAAK;YACjE,OAAO;QACT,SAAU;YACRlD,QAAQ;QACV;IACF,GACA;QAACZ;QAAQC;QAAOU;QAAMN;QAAMN;QAAYO;QAAiBkC;KAAO;IAGlE,kFAAkF,GAClF,MAAMmC,QAAQ,CAACC;QACb,MAAMC,SAASvF,cAAcC,YAAYG,GAAG,CAAC,CAACE;;gBAC9BgF;YAAd,MAAME,iBAAQF,kBAAAA,OAAOG,OAAO,qBAAdH,eAAgB,CAAChF,OAAO,mBAAI;YAC1C,MAAM,CAACoF,KAAKC,KAAK,GAAGtG,YAAY,CAACiB,OAAO;YACxC,OAAO;gBAAEkF;gBAAOI,MAAM,GAAGJ,MAAMK,cAAc,GAAG,CAAC,EAAEL,UAAU,IAAIE,MAAMC,MAAM;YAAC;QAChF;QACA,OAAOJ,OAAOO,IAAI,CAAC,CAACC,QAAUA,MAAMP,KAAK,IACrC,CAAC,GAAG,EAAE7F,aAAa4F,OAAOnF,GAAG,CAAC,CAAC2F,QAAUA,MAAMH,IAAI,GAAG,QAAQ,CAAC,GAC/D;IACN;IAEA,MAAMI,UAAU;QACdtE,aAAa;QACbE,cAAc;QACdE,gBAAgB;QAChBE,aAAa;QACbR,cAAc;YAAEyE,MAAM;QAAM;IAC9B;IACA,MAAMC,aAAa,CAAC7C;QAClB3B,aAAa2B,MAAML,KAAK;QACxBhB,aAAa;QACbR,cAAc;YAAEyE,MAAM;YAAU5C;QAAM;IACxC;IAEA,MAAM8C,aAAa;QACjB,IAAI,CAAC5E,YAAY;QACjB,IAAIA,WAAW0E,IAAI,KAAK,OAAO;YAC7B,MAAMG,OAAO3H,iBAAiBwB,YAAYgB,UAAUQ,WAAW;gBAC7D4E,OAAO1E,cAAc;gBACrB2E,SAASzE,gBAAgB;YAC3B;YACA,IAAIuE,KAAKlC,EAAE,KAAK,OAAO,OAAO,KAAKlC,aAAaoE,KAAK9B,KAAK;YAC1D,IAAI9B,aAAa;gBACf,qEAAqE;gBACrE,MAAM8C,SAAS,MAAMb,KAAK;oBACxB8B,QAAQ;oBACRvD,OAAOvB;oBACP4E,OAAO1E,cAAc;oBACrB2E,SAASzE,gBAAgB;gBAC3B;gBACA,IAAI,CAACyD,QAAQ;gBACb9D,cAAc;gBACdR,gBAAgB,CAAC,CAAC,EAAES,UAAU+E,IAAI,GAAG,OAAO,EAAEnB,MAAMC,SAAS,EAAE;oBAC7DlB,SAAS;oBACTC,SAAS;gBACX;gBACA;YACF;YACA,kEAAkE;YAClE,6DAA6D;YAC7D,IAAI,MAAMd,KAAK6C,KAAKnF,QAAQ,EAAE,CAAC,CAAC,EAAEQ,UAAU+E,IAAI,GAAG,OAAO,CAAC,GAAGhF,cAAc;YAC5E;QACF;QACA,MAAM8D,SAAS,MAAMb,KAAK;YAAE8B,QAAQ;YAAUE,SAASlF,WAAW8B,KAAK,CAACC,EAAE;YAAEN,OAAOvB;QAAU;QAC7F,IAAI,CAAC6D,QAAQ;QACb9D,cAAc;QACdR,gBAAgB,CAAC,OAAO,EAAEqE,MAAMC,SAAS,EAAE;YAAElB,SAAS;YAAWC,SAAS;QAAM;IAClF;IAEA,MAAMqC,aAAa,CAACrD;QAClBjB,eAAe;QACfF,YAAYmB;IACd;IACA,MAAMsD,eAAe;QACnB,IAAI,CAAC1E,UAAU;QACf,MAAMqD,SAAS,MAAMb,KAAK;YACxB8B,QAAQ;YACRE,SAASxE,SAASqB,EAAE;YACpBnB,aAAaA,eAAe;QAC9B;QACA,IAAI,CAACmD,QAAQ;QACbpD,YAAY;QACZlB,gBAAgB,CAAC,CAAC,EAAEiB,SAASe,KAAK,CAAC,SAAS,EAAEqC,MAAMC,SAAS,EAAE;YAC7DlB,SAAS;YACTC,SAAS;QACX;IACF;IAEA,MAAM+B,OAAO,CAACQ,MAAcC,KAC1B,KAAKtD,KAAK5E,kBAAkBsC,UAAU2F,MAAMC,KAAK;IAEnD,MAAMC,aAAa,CAACzD;QAClB,MAAM0D,YAAY9F,SAAS6C,cAAc,KAAKT,MAAMC,EAAE;QACtD,MAAM0D,QAA8B;YAClC;gBACEC,KAAK;gBACLjE,OAAO;gBACPkE,oBAAM,KAACxK;oBAAQyK,MAAM3K,iBAAiB2K,IAAI;oBAAEC,MAAM;;gBAClDC,UAAUhG;gBACViG,SAAS,IAAMpB,WAAW7C;YAC5B;YACA;gBACE4D,KAAK;gBACLjE,OAAO+D,YAAY,kBAAkB;gBACrCG,oBAAM,KAACxK;oBAAQyK,MAAM1K,eAAe0K,IAAI;oBAAEC,MAAM;;gBAChDC,UAAUhG,QAAS,CAAC0F,aAAa,CAAC1D,MAAMkE,MAAM;gBAC9CC,gBAAgB,CAACnE,MAAMkE,MAAM,GAAG,4CAA4CE;gBAC5EH,SAAS,IACP,KAAK/D,KACHvE,mBAAmBiC,UAAU8F,YAAY,OAAO1D,MAAMC,EAAE,GACxDyD,YAAY,oBAAoB,CAAC,IAAI,EAAEnE,MAAM,WAAW,EAAES,MAAML,KAAK,CAAC,CAAC,CAAC;YAE9E;YACA;gBACEiE,KAAK;gBACLjE,OAAOK,MAAMkE,MAAM,GAAG,eAAe;gBACrCL,oBACE,KAACxK;oBAAQyK,MAAM9D,MAAMkE,MAAM,GAAGjL,iBAAiB6K,IAAI,GAAG5K,cAAc4K,IAAI;oBAAEC,MAAM;;gBAElFC,UAAUhG;gBACViG,SAAS,IACP,KAAK/D,KACHtE,uBAAuBgC,UAAUoC,MAAMC,EAAE,EAAE,CAACD,MAAMkE,MAAM,GACxDlE,MAAMkE,MAAM,GAAG,CAAC,CAAC,EAAElE,MAAML,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,EAAEK,MAAML,KAAK,CAAC,WAAW,CAAC;YAElF;SACD;QACD,8EAA8E;QAC9E,IAAI,CAACI,WAAWC,QAAQ;YACtB2D,MAAMU,IAAI,CAAC;gBACTT,KAAK;gBACLjE,OAAO;gBACPkE,oBAAM,KAACxK;oBAAQyK,MAAM/K,iBAAiB+K,IAAI;oBAAEC,MAAM;;gBAClDO,aAAa;gBACbN,UAAUhG;gBACViG,SAAS,IAAMZ,WAAWrD;YAC5B;QACF;QACA,OAAO2D;IACT;IAEA,MAAMY,eAAe3F,WAAWlD,qBAAqBkB,YAAYgB,UAAUgB,SAASqB,EAAE,IAAI,EAAE;IAC5F,MAAMuE,UAAU,IAAKtF,CAAAA,OAAO1C,MAAM,GAAG,IAAI,CAAA,IAAM4C,CAAAA,SAAS5C,MAAM,GAAG,IAAI,CAAA;IACrE,MAAMiI,SAASnI,aAAakD,QAAQzC,GAAG,CAAC,CAAC,GAAGuF,KAAK,GAAKA,OAAOoC,OAAO,CAAC,MAAM,CAACC,SAC1EA,OAAOC,WAAW;IAGpB,qBACE,MAACrK;QAAMsK,SAAS;;0BACd,MAACtK;gBACCuK,WAAW;oBAAEC,IAAI;oBAAUC,IAAI;gBAAM;gBACrCH,SAAS;gBACTI,IAAI;oBAAEC,gBAAgB;oBAAiBC,YAAY;wBAAEH,IAAI;oBAAS;gBAAE;;kCAEpE,MAACzK;wBAAMsK,SAAS;;0CACd,KAAChK;gCAAWkG,SAAQ;0CAAa,GAAGnE,WAAW+C,KAAK,CAAC,OAAO,CAAC;;0CAC7D,KAAC9E;gCAAWkG,SAAQ;gCAAQqE,OAAM;0CAC/B,AAAC3F,CAAAA,cACE,CAAC,sBAAsB,EAAEH,KAAK,GAAG,EAAE1C,WAAW+C,KAAK,CAAC,wBAAwB,CAAC,GAC7E,wEACA,CAAC,WAAW,EAAEL,KAAK,EAAE,CAAC,GACtB,CAAC,qBAAqB,EAAEA,KAAK,GAAG,EAAE1C,WAAW+C,KAAK,CAAC,wBAAwB,CAAC,GAC3E/C,CAAAA,WAAWyI,UAAU,GAClB,gEACA,EAAC,IACL,yDAAwD,IAC1D,yEACA;;;;oBAGL/H,sBACC,MAAC/C;wBAAMuK,WAAU;wBAAMD,SAAS;wBAAGI,IAAI;4BAAEK,YAAY;wBAAE;;0CACrD,KAACxL;gCACCiK,MAAK;gCACLC,UAAUhG,QAAQJ,SAAS4C,MAAM,CAAChE,MAAM,GAAG;gCAC3CyH,SAAS,IAAM,KAAK/D,KAAKpE,mBAAmB8B,WAAW;0CAEtD;;0CAEH,KAAC9D;gCAAOiK,MAAK;gCAAQhD,SAAQ;gCAAWiD,UAAUhG;gCAAMiG,SAAStB;0CAC9D;;;yBAGH;;;YAEL,CAAC7E,QAAQ,qBACR,MAACtE;gBAAYuK,MAAK;gBAAQwB,cAAY,GAAG3I,WAAW+C,KAAK,CAAC,OAAO,CAAC;;kCAChE,KAACjF;kCACC,cAAA,MAACC;;8CACC,KAACF;oCAAUwK,IAAI;wCAAEO,OAAO;oCAAI;8CAAI;;8CAChC,KAAC/K;8CAAW;;gCACXyE,OAAO1C,MAAM,iBAAG,KAAC/B;8CAAW;qCAAuB;gCACnD2E,SAAS5C,MAAM,iBAAG,KAAC/B;8CAAW;qCAAuB;8CACtD,KAACA;8CAAW;;8CACZ,KAACA;oCAAUgL,OAAM;;;;;kCAGrB,MAACjL;;4BACEoD,SAAS4C,MAAM,CAAChE,MAAM,KAAK,kBAC1B,KAAC7B;0CACC,cAAA,KAACF;oCAAUiL,SAASlB;8CAClB,cAAA,KAAC3J;wCAAWkG,SAAQ;wCAAQqE,OAAM;kDAC/B,CAAC,kCAAkC,EAAE9F,KAAK,iBAAiB,CAAC;;;iCAIjE;4BACH1B,SAAS4C,MAAM,CAACzD,GAAG,CAAC,CAACiD,OAAO2F;oCAmEZ3F;qDAlEf,MAACrF;oCAECiL,KAAK;oCACL,4DAA4D;oCAC5D,6DAA6D;oCAC7DC,WAAW,CAAC7H;oCACZ8H,aAAa,CAACC;wCACZ9G,YAAY0G;wCACZI,MAAMC,YAAY,CAACC,aAAa,GAAG;oCACrC;oCACAC,YAAY,CAACH;wCACX,IAAI/G,aAAa,MAAM;wCACvB+G,MAAMI,cAAc;wCACpBJ,MAAMC,YAAY,CAACI,UAAU,GAAG;oCAClC;oCACAC,QAAQ,CAACN;wCACPA,MAAMI,cAAc;wCACpB,IAAInH,aAAa,QAAQA,aAAa2G,OAAO5C,KAAK/D,UAAU2G;wCAC5D1G,YAAY;oCACd;oCACAqH,WAAW,IAAMrH,YAAY;oCAC7BgG,IAAI,aACEjF,MAAMkE,MAAM,GAAG,CAAC,IAAI;wCAAEqC,SAAS;oCAAI,GACnCvH,aAAa2G,QAAQ;wCAAEa,SAAS;wCAAGC,cAAc;oCAAe,IAAI,CAAC;;sDAG3E,KAAChM;sDACC,cAAA,MAACF;gDAAMuK,WAAU;gDAAMD,SAAS;gDAAGI,IAAI;oDAAEE,YAAY;gDAAS;;kEAC5D,KAAC9L;wDACCyK,MAAM9K,gBAAgB8K,IAAI;wDAC1BC,MAAM;wDACN2C,OAAO;4DAAEC,QAAQ3I,OAAO,YAAY;wDAAO;wDAC3C4I,aAAW;;kEAEb,MAACvM;wDACC0J,MAAK;wDACLC,UAAU2B,UAAU,KAAK3H;wDACzBiG,SAAS,IAAMlB,KAAK4C,OAAOA,QAAQ;;0EAEnC,KAACtM;gEAAQyK,MAAMhL,WAAWgL,IAAI;gEAAEC,MAAM;;0EACtC,KAACzK;0EAAQ,CAAC,KAAK,EAAE0G,MAAML,KAAK,CAAC,GAAG,CAAC;;;;kEAEnC,MAACtF;wDACC0J,MAAK;wDACLC,UAAU2B,UAAU/H,SAAS4C,MAAM,CAAChE,MAAM,GAAG,KAAKwB;wDAClDiG,SAAS,IAAMlB,KAAK4C,OAAOA,QAAQ;;0EAEnC,KAACtM;gEAAQyK,MAAMjL,aAAaiL,IAAI;gEAAEC,MAAM;;0EACxC,KAACzK;0EAAQ,CAAC,KAAK,EAAE0G,MAAML,KAAK,CAAC,KAAK,CAAC;;;;;;;sDAIzC,KAAClF;sDACC,cAAA,MAACF;gDAAMuK,WAAU;gDAAMD,SAAS;gDAAGI,IAAI;oDAAEE,YAAY;gDAAS;;kEAC5D,KAACtK;wDAAWkG,SAAQ;kEAASf,MAAML,KAAK;;oDACvCI,WAAWC,uBACV,KAACjG;wDAAKgK,MAAK;wDAAQhD,SAAQ;wDAAWpB,OAAM;yDAC1C;;;;wCAGPT,OAAO1C,MAAM,iBACZ,KAAC/B;4CAAUwK,IAAI;gDAAE4B,UAAU;4CAAI;sDAC7B,cAAA,MAACjM;gDACCkM,MAAM;gDACN/C,MAAK;gDACLhD,SAAQ;gDACRf,KAAK,GAAEA,eAAAA,MAAMgD,KAAK,YAAXhD,eAAe;gDACtBgE,UAAUhG,QAAQ,CAACV;gDACnByJ,UAAU,CAAChB;wDAEI7G;oDADb,MAAM8D,QAAQgE,OAAOjB,MAAM/I,MAAM,CAACgD,KAAK,KAAK;oDAC5C,MAAMiH,QAAO/H,eAAAA,OAAOgI,IAAI,CAAC,CAACxE,QAAUA,MAAMzC,EAAE,KAAK+C,2BAApC9D,aAA4CS,KAAK;oDAC9D,MAAMS,OAAO6G,OAAO,CAAC,CAAC,EAAEjH,MAAML,KAAK,CAAC,KAAK,EAAEsH,MAAM,GAAG,CAAC,CAAC,EAAEjH,MAAML,KAAK,CAAC,cAAc,CAAC;oDACnF,IAAI,CAACR,aAAa;wDAChB,KAAKe,KAAKrE,sBAAsBe,YAAYgB,UAAUoC,MAAMC,EAAE,EAAE+C,QAAQ5C;wDACxE;oDACF;oDACA,mEAAmE;oDACnE,KAAKgB,KAAK;wDAAE8B,QAAQ;wDAASE,SAASpD,MAAMC,EAAE;wDAAE+C;oDAAM,GAAGmE,IAAI,CAAC,CAAClF;wDAC7D,IAAIA,QAAQ;4DACVtE,gBAAgB,GAAGyC,OAAO4B,MAAMC,SAAS,EAAE;gEAAElB,SAAS;gEAAWC,SAAS;4DAAM;wDAClF;oDACF;gDACF;gDACAoG,SAAS;gDACTC,WAAW;oDACTP,QAAQ;wDAAEQ,cAAc;wDAAM,cAAc,CAAC,SAAS,EAAEtH,MAAML,KAAK,EAAE;oDAAC;gDACxE;;kEAEA,KAACrF;wDAAS0F,OAAM;kEAAI;;oDACnBd,OAAOnC,GAAG,CAAC,CAACiG,sBACX,KAAC1I;4DAAwB0F,OAAOgD,MAAM/C,EAAE;sEACrC+C,MAAMrD,KAAK;2DADCqD,MAAM/C,EAAE;;;6CAM3B;wCACHb,SAAS5C,MAAM,GACd,mEAAmE;wCACnE,oCAAoC;sDACpC,KAAC/B;sDACC,cAAA,KAACI;gDAAWkG,SAAQ;gDAAQqE,OAAOpF,MAAMiD,OAAO,GAAG,iBAAiB;0DACjEjD,MAAMiD,OAAO,GAAGxH,qBAAqBmB,YAAYoD,MAAMiD,OAAO,IAAI;;6CAGrE;sDACJ,KAACxI;sDACC,cAAA,MAACF;gDAAMgN,UAAU;gDAACzC,WAAU;gDAAMD,SAAS;gDAAGI,IAAI;oDAAEuC,UAAU;oDAAQrC,YAAY;gDAAS;;oDACxFnF,MAAMkE,MAAM,GAAG,qBACd,KAACnK;wDAAKgK,MAAK;wDAAQhD,SAAQ;wDAAWqE,OAAM;wDAAUzF,OAAM;;oDAE7D/B,SAAS6C,cAAc,KAAKT,MAAMC,EAAE,iBACnC,KAAClG;wDAAKgK,MAAK;wDAAQhD,SAAQ;wDAAWqE,OAAM;wDAAUzF,OAAM;yDAC1D;oDACHK,MAAMkE,MAAM,IAAItG,SAAS6C,cAAc,KAAKT,MAAMC,EAAE,iBACnD,KAACpF;wDAAWkG,SAAQ;wDAAQqE,OAAM;kEAC/B;yDAED;;;;sDAGR,KAAC3K;4CAAUgL,OAAM;4CAAQR,IAAI;gDAAEO,OAAO;4CAAG;sDACvC,cAAA,KAACjM;gDAAeoG,OAAOK,MAAML,KAAK;gDAAEgE,OAAOF,WAAWzD;;;;mCAzHnDA,MAAMC,EAAE;;;;;;YAgItBnC,SAAS,CAACD,uBACT,KAAChD;gBAAWkG,SAAQ;gBAAUqE,OAAM;0BACjC,+EACC;iBAEF;0BAEJ,MAACpL;gBAAOyN,MAAMvJ,eAAe;gBAAMwJ,SAAS,IAAMvJ,cAAc;gBAAOiJ,SAAS;gBAACO,UAAS;;kCACxF,KAACvN;kCACE8D,CAAAA,8BAAAA,WAAY0E,IAAI,MAAK,WAAW,CAAC,QAAQ,EAAE1E,WAAW8B,KAAK,CAACL,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,EAAED,OAAO;;kCAE1F,KAACxF;kCACC,cAAA,MAACK;4BAAMsK,SAAS;4BAAKI,IAAI;gCAAE2C,IAAI;4BAAE;;gCAC9B1J,CAAAA,8BAAAA,WAAY0E,IAAI,MAAK,YAAY,CAACnD,4BACjC,KAACtF;oCAAkB4G,SAAQ;8CACxB,CAAC,MAAM,EAAEzE,aAAakD,QAAQzC,GAAG,CAAC,CAAC,CAACsF,IAAI,GAAKA,MAAM,oBAAoB,CAAC,GACvE;qCAEF;8CACJ,KAACzH;oCACCmJ,MAAK;oCACLpE,OAAM;oCACNK,OAAO5B;oCACP2I,UAAU,CAAChB;wCACT1H,aAAa0H,MAAM/I,MAAM,CAACgD,KAAK;wCAC/BrB,aAAa;oCACf;oCACAkJ,WAAW,CAAC9B;wCACV,IAAIA,MAAMnC,GAAG,KAAK,SAAS,KAAKd;oCAClC;oCACA7B,OAAO6G,QAAQpJ;oCACfqJ,YAAYrJ,aAAa;oCACzB2I,WAAW;wCAAEW,WAAW;4CAAEC,WAAWxP;wCAAkB;oCAAE;oCACzDyP,SAAS;oCACTd,SAAS;;gCAEVlJ,CAAAA,8BAAAA,WAAY0E,IAAI,MAAK,SAAS1D,OAAO1C,MAAM,iBAC1C,MAAC5B;oCACCkM,MAAM;oCACN/C,MAAK;oCACLpE,OAAM;oCACNK,OAAO1B;oCACPyI,UAAU,CAAChB,QAAUxH,cAAcyI,OAAOjB,MAAM/I,MAAM,CAACgD,KAAK;oCAC5DoH,SAAS;oCACTC,WAAW;wCAAEP,QAAQ;4CAAEQ,cAAc;wCAAK;wCAAGa,YAAY;4CAAEC,QAAQ;wCAAK;oCAAE;;sDAE1E,KAAC9N;4CAAS0F,OAAM;sDAAI;;wCACnBd,OAAOnC,GAAG,CAAC,CAACiG,sBACX,KAAC1I;gDAAwB0F,OAAOgD,MAAM/C,EAAE;0DACrC+C,MAAMrD,KAAK;+CADCqD,MAAM/C,EAAE;;qCAKzB;gCACH/B,CAAAA,8BAAAA,WAAY0E,IAAI,MAAK,SAASxD,SAAS5C,MAAM,iBAC5C,KAAC5B;oCACCkM,MAAM;oCACN/C,MAAK;oCACLpE,OAAM;oCACNK,OAAOxB;oCACPuI,UAAU,CAAChB;wCACTtH,gBAAgBuI,OAAOjB,MAAM/I,MAAM,CAACgD,KAAK;wCACzCrB,aAAa;oCACf;oCACAyI,SAAS;8CAER/H,gBAAgBtC,GAAG,CAAC,CAACkG,wBACpB,KAAC3I;4CAAuB0F,OAAOiD;sDAC5BxH,qBAAqBmB,YAAYqG;2CADrBA;qCAKjB;;;;kCAGR,MAAChJ;;0CACC,KAACH;gCAAOmK,SAAS,IAAM9F,cAAc;0CAAQ;;0CAC7C,KAACrE;gCACCiH,SAAQ;gCACRiD,UAAUhG,QAAQ,CAACI,UAAU+E,IAAI;gCACjCc,SAAS,IAAM,KAAKnB;0CAEnB5E,CAAAA,8BAAAA,WAAY0E,IAAI,MAAK,WAAW,WAAW;;;;;;0BAKlD,MAAC5I;gBAAOyN,MAAM7I,aAAa;gBAAM8I,SAAS,IAAM7I,YAAY;gBAAOuI,SAAS;gBAACO,UAAS;;kCACpF,KAACvN;kCAAa,CAAC,QAAQ,UAAEwE,4BAAAA,SAAUe,KAAK,mBAAI,GAAG,EAAE,CAAC;;kCAClD,KAACzF;kCACC,cAAA,MAACK;4BAAMsK,SAAS;4BAAKI,IAAI;gCAAE2C,IAAI;4BAAE;;8CAC/B,KAACzN;oCAAkB4G,SAAQ;8CACxBtB,cACG,CAAC,4CAA4C,EAAEH,KAAK,SAAS,CAAC,GAC9D,CAAC,oCAAoC,EAAEmF,OAAO,cAAc,CAAC,GAC5DrF,CAAAA,SAAS5C,MAAM,GACZ,iEACA,oDAAmD,IACvD;;gCAELiD,cAAc,qBACb,MAAC7E;oCACCkM,MAAM;oCACN/C,MAAK;oCACLpE,OAAM;oCACNK,OAAOlB;oCACPiI,UAAU,CAAChB,QAAUhH,eAAeiI,OAAOjB,MAAM/I,MAAM,CAACgD,KAAK;oCAC7DoH,SAAS;oCACTC,WAAW;wCAAEP,QAAQ;4CAAEQ,cAAc;wCAAK;wCAAGa,YAAY;4CAAEC,QAAQ;wCAAK;oCAAE;;wCAGzEhJ,SAAS5C,MAAM,GAAG,qBAAO,KAAClC;4CAAS0F,OAAM;sDAAI;;wCAC7CuE,aAAaxH,GAAG,CAAC,CAACiD,sBACjB,KAAC1F;gDAAwB0F,OAAOA,MAAML,KAAK;0DACxCK,MAAML,KAAK;+CADCK,MAAMC,EAAE;;;;;;kCAQjC,MAAChG;;0CACC,KAACH;gCAAOmK,SAAS,IAAMpF,YAAY;0CAAQ;;0CAC3C,KAAC/E;gCACCiH,SAAQ;gCACRqE,OAAM;gCACNpB,UAAUhG,QAASoB,SAAS5C,MAAM,GAAG,KAAK,CAACsC;gCAC3CmF,SAAS,IAAM,KAAKX;0CAEnB;;;;;;;;AAMb;AACApG,mBAAmBmL,WAAW,GAAG;AAEjC,eAAenL,mBAAkB"}
@@ -25,6 +25,7 @@ import { collection, doc, getCountFromServer, query, updateDoc, where } from "fi
25
25
  import { useCallback, useEffect, useState } from "react";
26
26
  import { crmVisibleToClause } from "../hooks/use-crm-scope.js";
27
27
  import { addStage, moveStage, removeStage, renameStage, setStageForecastCategory, setStageProbability, sortedStages, stageRemovalRefusal, stagesProblem } from "../model/deal-board-model.js";
28
+ import { settleStageNames } from "../model/settle-stage-names.js";
28
29
  /**
29
30
  * The pipeline's stages, edited as one draft and saved as one write.
30
31
  *
@@ -116,7 +117,7 @@ import { addStage, moveStage, removeStage, renameStage, setStageForecastCategory
116
117
  fromCache
117
118
  }, async ()=>{
118
119
  await updateDoc(doc(firestore, 'orgs', orgId, CRM_COLLECTIONS.pipelines, pipeline.$id), {
119
- stages,
120
+ stages: settleStageNames(stages),
120
121
  updatedAt: new Date()
121
122
  });
122
123
  });
@@ -242,6 +243,7 @@ import { addStage, moveStage, removeStage, renameStage, setStageForecastCategory
242
243
  children: /*#__PURE__*/ _jsx("span", {
243
244
  children: /*#__PURE__*/ _jsx(IconButton, {
244
245
  size: "small",
246
+ "aria-label": `Move ${stage.name} up`,
245
247
  disabled: first,
246
248
  onClick: ()=>setStages((current)=>moveStage(current, stage.id, 'up')),
247
249
  children: /*#__PURE__*/ _jsx(MdiIcon, {
@@ -256,6 +258,7 @@ import { addStage, moveStage, removeStage, renameStage, setStageForecastCategory
256
258
  children: /*#__PURE__*/ _jsx("span", {
257
259
  children: /*#__PURE__*/ _jsx(IconButton, {
258
260
  size: "small",
261
+ "aria-label": `Move ${stage.name} down`,
259
262
  disabled: lastOpen,
260
263
  onClick: ()=>setStages((current)=>moveStage(current, stage.id, 'down')),
261
264
  children: /*#__PURE__*/ _jsx(MdiIcon, {
@@ -271,6 +274,7 @@ import { addStage, moveStage, removeStage, renameStage, setStageForecastCategory
271
274
  children: /*#__PURE__*/ _jsx(IconButton, {
272
275
  size: "small",
273
276
  color: "error",
277
+ "aria-label": `Remove ${stage.name}`,
274
278
  disabled: counting === stage.id || busy,
275
279
  onClick: ()=>void handleRemove(stage.id),
276
280
  children: /*#__PURE__*/ _jsx(MdiIcon, {