@aglyn/plugins-crm 1.0.0-beta.217 → 1.0.0-beta.219

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 (249) hide show
  1. package/package.json +14 -13
  2. package/src/index.d.ts +1 -0
  3. package/src/index.js.map +1 -1
  4. package/src/lib/components/companies-bulk-bar.js +12 -4
  5. package/src/lib/components/companies-bulk-bar.js.map +1 -1
  6. package/src/lib/components/company-detail-page.js +11 -0
  7. package/src/lib/components/company-detail-page.js.map +1 -1
  8. package/src/lib/components/company-edit-drawer.js +12 -2
  9. package/src/lib/components/company-edit-drawer.js.map +1 -1
  10. package/src/lib/components/company-import-drawer.d.ts +1 -1
  11. package/src/lib/components/company-picker.js +10 -3
  12. package/src/lib/components/company-picker.js.map +1 -1
  13. package/src/lib/components/contact-detail-page.d.ts +4 -4
  14. package/src/lib/components/contact-detail-page.js +50 -53
  15. package/src/lib/components/contact-detail-page.js.map +1 -1
  16. package/src/lib/components/contact-list-columns.d.ts +3 -0
  17. package/src/lib/components/contact-list-columns.js +21 -5
  18. package/src/lib/components/contact-list-columns.js.map +1 -1
  19. package/src/lib/components/contact-merge-dialog.js +2 -1
  20. package/src/lib/components/contact-merge-dialog.js.map +1 -1
  21. package/src/lib/components/contact-timeline-card.js +4 -3
  22. package/src/lib/components/contact-timeline-card.js.map +1 -1
  23. package/src/lib/components/contacts-bulk-bar.d.ts +3 -3
  24. package/src/lib/components/contacts-bulk-bar.js +24 -51
  25. package/src/lib/components/contacts-bulk-bar.js.map +1 -1
  26. package/src/lib/components/contacts-section.js +9 -3
  27. package/src/lib/components/contacts-section.js.map +1 -1
  28. package/src/lib/components/crm-export-all-button.d.ts +1 -1
  29. package/src/lib/components/crm-export-all-button.js.map +1 -1
  30. package/src/lib/components/crm-send-email-dialog.js +9 -6
  31. package/src/lib/components/crm-send-email-dialog.js.map +1 -1
  32. package/src/lib/components/csv-import-drawer.js +2 -2
  33. package/src/lib/components/csv-import-drawer.js.map +1 -1
  34. package/src/lib/components/deal-detail-page.js +18 -2
  35. package/src/lib/components/deal-detail-page.js.map +1 -1
  36. package/src/lib/components/deal-edit-drawer.js +10 -0
  37. package/src/lib/components/deal-edit-drawer.js.map +1 -1
  38. package/src/lib/components/deals-bulk-bar.js +13 -5
  39. package/src/lib/components/deals-bulk-bar.js.map +1 -1
  40. package/src/lib/components/lead-campaigns-card.js +6 -0
  41. package/src/lib/components/lead-campaigns-card.js.map +1 -1
  42. package/src/lib/components/lead-detail-page.js +25 -5
  43. package/src/lib/components/lead-detail-page.js.map +1 -1
  44. package/src/lib/components/lead-properties-card.js +6 -0
  45. package/src/lib/components/lead-properties-card.js.map +1 -1
  46. package/src/lib/components/lead-surfaces-note.js +4 -5
  47. package/src/lib/components/lead-surfaces-note.js.map +1 -1
  48. package/src/lib/components/lead-unqualify-dialog.js +6 -0
  49. package/src/lib/components/lead-unqualify-dialog.js.map +1 -1
  50. package/src/lib/components/leads-bulk-bar.js +28 -3
  51. package/src/lib/components/leads-bulk-bar.js.map +1 -1
  52. package/src/lib/components/leads-section.js +36 -7
  53. package/src/lib/components/leads-section.js.map +1 -1
  54. package/src/lib/components/org-lead-surfaces-note.js +6 -2
  55. package/src/lib/components/org-lead-surfaces-note.js.map +1 -1
  56. package/src/lib/components/recipes-card.js +9 -4
  57. package/src/lib/components/recipes-card.js.map +1 -1
  58. package/src/lib/components/record-sharing-card.d.ts +57 -0
  59. package/src/lib/components/record-sharing-card.js +244 -0
  60. package/src/lib/components/record-sharing-card.js.map +1 -0
  61. package/src/lib/components/settings-section.js +7 -0
  62. package/src/lib/components/settings-section.js.map +1 -1
  63. package/src/lib/components/share-records-dialog.d.ts +28 -0
  64. package/src/lib/components/share-records-dialog.js +184 -0
  65. package/src/lib/components/share-records-dialog.js.map +1 -0
  66. package/src/lib/components/sharing-rule-drawer.d.ts +37 -0
  67. package/src/lib/components/sharing-rule-drawer.js +374 -0
  68. package/src/lib/components/sharing-rule-drawer.js.map +1 -0
  69. package/src/lib/components/sharing-rules-card.d.ts +24 -0
  70. package/src/lib/components/sharing-rules-card.js +287 -0
  71. package/src/lib/components/sharing-rules-card.js.map +1 -0
  72. package/src/lib/components/use-crm-api.d.ts +1 -1
  73. package/src/lib/components/use-crm-api.js.map +1 -1
  74. package/src/lib/components/use-site-route-context.d.ts +25 -0
  75. package/src/lib/components/use-site-route-context.js +58 -0
  76. package/src/lib/components/use-site-route-context.js.map +1 -0
  77. package/src/lib/constants/api-routes.d.ts +31 -0
  78. package/src/lib/constants/api-routes.js +27 -1
  79. package/src/lib/constants/api-routes.js.map +1 -1
  80. package/src/lib/declarations.console-server.d.ts +37 -0
  81. package/src/lib/declarations.console-server.js +103 -0
  82. package/src/lib/declarations.console-server.js.map +1 -0
  83. package/src/lib/declarations.server.d.ts +6 -0
  84. package/src/lib/declarations.server.js +33 -0
  85. package/src/lib/declarations.server.js.map +1 -1
  86. package/src/lib/hooks/use-contact-remove.d.ts +35 -0
  87. package/src/lib/hooks/use-contact-remove.js +77 -0
  88. package/src/lib/hooks/use-contact-remove.js.map +1 -0
  89. package/src/lib/hooks/use-crm-sharing.d.ts +30 -0
  90. package/src/lib/hooks/use-crm-sharing.js +109 -0
  91. package/src/lib/hooks/use-crm-sharing.js.map +1 -0
  92. package/src/lib/model/companies-csv.d.ts +1 -1
  93. package/src/lib/model/companies-csv.js +2 -2
  94. package/src/lib/model/companies-csv.js.map +1 -1
  95. package/src/lib/model/contact-holder.d.ts +68 -0
  96. package/src/lib/model/contact-holder.js +129 -0
  97. package/src/lib/model/contact-holder.js.map +1 -0
  98. package/src/lib/model/contact-interaction-links.d.ts +46 -0
  99. package/src/lib/model/contact-interaction-links.js +65 -0
  100. package/src/lib/model/contact-interaction-links.js.map +1 -0
  101. package/src/lib/model/contact-merge.d.ts +136 -0
  102. package/src/lib/model/contact-merge.js +412 -0
  103. package/src/lib/model/contact-merge.js.map +1 -0
  104. package/src/lib/model/contact-record.d.ts +7 -1
  105. package/src/lib/model/contact-record.js +6 -5
  106. package/src/lib/model/contact-record.js.map +1 -1
  107. package/src/lib/model/contact-remove.d.ts +48 -0
  108. package/src/lib/model/contact-remove.js +22 -0
  109. package/src/lib/model/contact-remove.js.map +1 -0
  110. package/src/lib/model/contacts-bulk-writes.d.ts +2 -71
  111. package/src/lib/model/contacts-bulk-writes.js +2 -82
  112. package/src/lib/model/contacts-bulk-writes.js.map +1 -1
  113. package/src/lib/model/contacts-csv.d.ts +2 -2
  114. package/src/lib/model/contacts-csv.js +2 -2
  115. package/src/lib/model/contacts-csv.js.map +1 -1
  116. package/src/lib/model/crm-csv.d.ts +239 -0
  117. package/src/lib/model/crm-csv.js +467 -0
  118. package/src/lib/model/crm-csv.js.map +1 -0
  119. package/src/lib/model/crm-digest.d.ts +185 -0
  120. package/src/lib/model/crm-digest.js +272 -0
  121. package/src/lib/model/crm-digest.js.map +1 -0
  122. package/src/lib/model/crm-import.d.ts +7 -1
  123. package/src/lib/model/crm-import.js +2 -1
  124. package/src/lib/model/crm-import.js.map +1 -1
  125. package/src/lib/model/crm-record-routes.d.ts +5 -0
  126. package/src/lib/model/crm-record-routes.js +7 -0
  127. package/src/lib/model/crm-record-routes.js.map +1 -1
  128. package/src/lib/model/crm-routes.d.ts +6 -5
  129. package/src/lib/model/crm-routes.js +7 -7
  130. package/src/lib/model/crm-routes.js.map +1 -1
  131. package/src/lib/model/crm-search-sources.d.ts +15 -0
  132. package/src/lib/model/crm-search-sources.js +162 -0
  133. package/src/lib/model/crm-search-sources.js.map +1 -0
  134. package/src/lib/model/crm-sharing-api.d.ts +32 -0
  135. package/src/lib/model/crm-sharing-api.js +59 -0
  136. package/src/lib/model/crm-sharing-api.js.map +1 -0
  137. package/src/lib/model/crm-sharing.d.ts +333 -0
  138. package/src/lib/model/crm-sharing.js +603 -0
  139. package/src/lib/model/crm-sharing.js.map +1 -0
  140. package/src/lib/model/deals-csv.d.ts +1 -1
  141. package/src/lib/model/deals-csv.js +4 -5
  142. package/src/lib/model/deals-csv.js.map +1 -1
  143. package/src/lib/model/lead-filters.d.ts +1 -1
  144. package/src/lib/model/leads-csv.d.ts +1 -1
  145. package/src/lib/model/leads-csv.js +1 -1
  146. package/src/lib/model/leads-csv.js.map +1 -1
  147. package/src/lib/model/tasks-csv.d.ts +1 -1
  148. package/src/lib/model/tasks-csv.js +1 -1
  149. package/src/lib/model/tasks-csv.js.map +1 -1
  150. package/src/lib/plan-entitlements.d.ts +75 -0
  151. package/src/lib/plan-entitlements.js +56 -0
  152. package/src/lib/plan-entitlements.js.map +1 -0
  153. package/src/lib/plugin.js +3 -0
  154. package/src/lib/plugin.js.map +1 -1
  155. package/src/lib/server/api-v1/contacts-merge.d.ts +20 -0
  156. package/src/lib/server/api-v1/contacts-merge.js +120 -0
  157. package/src/lib/server/api-v1/contacts-merge.js.map +1 -0
  158. package/src/lib/server/api-v1/contacts.d.ts +18 -0
  159. package/src/lib/server/api-v1/contacts.js +1076 -0
  160. package/src/lib/server/api-v1/contacts.js.map +1 -0
  161. package/src/lib/server/api-v1/crm-activities.d.ts +18 -0
  162. package/src/lib/server/api-v1/crm-activities.js +269 -0
  163. package/src/lib/server/api-v1/crm-activities.js.map +1 -0
  164. package/src/lib/server/api-v1/crm-companies.d.ts +18 -0
  165. package/src/lib/server/api-v1/crm-companies.js +391 -0
  166. package/src/lib/server/api-v1/crm-companies.js.map +1 -0
  167. package/src/lib/server/api-v1/crm-deals.d.ts +18 -0
  168. package/src/lib/server/api-v1/crm-deals.js +631 -0
  169. package/src/lib/server/api-v1/crm-deals.js.map +1 -0
  170. package/src/lib/server/api-v1/crm-email-templates.d.ts +18 -0
  171. package/src/lib/server/api-v1/crm-email-templates.js +336 -0
  172. package/src/lib/server/api-v1/crm-email-templates.js.map +1 -0
  173. package/src/lib/server/api-v1/crm-leads.d.ts +18 -0
  174. package/src/lib/server/api-v1/crm-leads.js +832 -0
  175. package/src/lib/server/api-v1/crm-leads.js.map +1 -0
  176. package/src/lib/server/api-v1/crm-pipelines.d.ts +76 -0
  177. package/src/lib/server/api-v1/crm-pipelines.js +146 -0
  178. package/src/lib/server/api-v1/crm-pipelines.js.map +1 -0
  179. package/src/lib/server/api-v1/crm-shared.d.ts +271 -0
  180. package/src/lib/server/api-v1/crm-shared.js +436 -0
  181. package/src/lib/server/api-v1/crm-shared.js.map +1 -0
  182. package/src/lib/server/api-v1/crm-tasks.d.ts +18 -0
  183. package/src/lib/server/api-v1/crm-tasks.js +398 -0
  184. package/src/lib/server/api-v1/crm-tasks.js.map +1 -0
  185. package/src/lib/server/api-v1/openapi.d.ts +29 -0
  186. package/src/lib/server/api-v1/openapi.js +985 -0
  187. package/src/lib/server/api-v1/openapi.js.map +1 -0
  188. package/src/lib/server/api-v1/usage.d.ts +26 -0
  189. package/src/lib/server/api-v1/usage.js +73 -0
  190. package/src/lib/server/api-v1/usage.js.map +1 -0
  191. package/src/lib/server/consent-group-participant.js +1 -1
  192. package/src/lib/server/consent-group-participant.js.map +1 -1
  193. package/src/lib/server/contact-lifecycle-floor.d.ts +89 -0
  194. package/src/lib/server/contact-lifecycle-floor.js +91 -0
  195. package/src/lib/server/contact-lifecycle-floor.js.map +1 -0
  196. package/src/lib/server/contact-remove.d.ts +75 -0
  197. package/src/lib/server/contact-remove.js +244 -0
  198. package/src/lib/server/contact-remove.js.map +1 -0
  199. package/src/lib/server/contact-update.js +2 -1
  200. package/src/lib/server/contact-update.js.map +1 -1
  201. package/src/lib/server/contacts-import.d.ts +47 -11
  202. package/src/lib/server/contacts-import.js +171 -71
  203. package/src/lib/server/contacts-import.js.map +1 -1
  204. package/src/lib/server/contacts-merge.d.ts +1 -1
  205. package/src/lib/server/contacts-merge.js +3 -2
  206. package/src/lib/server/contacts-merge.js.map +1 -1
  207. package/src/lib/server/convert-host-lead.d.ts +189 -0
  208. package/src/lib/server/convert-host-lead.js +518 -0
  209. package/src/lib/server/convert-host-lead.js.map +1 -0
  210. package/src/lib/server/crm-sharing.d.ts +108 -0
  211. package/src/lib/server/crm-sharing.js +790 -0
  212. package/src/lib/server/crm-sharing.js.map +1 -0
  213. package/src/lib/server/daily-digest-route.d.ts +107 -0
  214. package/src/lib/server/daily-digest-route.js +588 -0
  215. package/src/lib/server/daily-digest-route.js.map +1 -0
  216. package/src/lib/server/export-route.d.ts +60 -0
  217. package/src/lib/server/export-route.js +447 -0
  218. package/src/lib/server/export-route.js.map +1 -0
  219. package/src/lib/server/inbound-route.d.ts +74 -0
  220. package/src/lib/server/inbound-route.js +257 -0
  221. package/src/lib/server/inbound-route.js.map +1 -0
  222. package/src/lib/server/lead-convert.d.ts +1 -1
  223. package/src/lib/server/lead-convert.js +2 -2
  224. package/src/lib/server/lead-convert.js.map +1 -1
  225. package/src/lib/server/merge-contacts.d.ts +55 -0
  226. package/src/lib/server/merge-contacts.js +274 -0
  227. package/src/lib/server/merge-contacts.js.map +1 -0
  228. package/src/lib/server/pipeline-record-index.d.ts +36 -0
  229. package/src/lib/server/pipeline-record-index.js +87 -0
  230. package/src/lib/server/pipeline-record-index.js.map +1 -0
  231. package/src/lib/server/record-facts.js +2 -1
  232. package/src/lib/server/record-facts.js.map +1 -1
  233. package/src/lib/server/task-reminders-route.d.ts +82 -0
  234. package/src/lib/server/task-reminders-route.js +497 -0
  235. package/src/lib/server/task-reminders-route.js.map +1 -0
  236. package/src/lib/server-deal-stage.js +2 -1
  237. package/src/lib/server-deal-stage.js.map +1 -1
  238. package/src/lib/server.d.ts +5 -2
  239. package/src/lib/server.js +47 -2
  240. package/src/lib/server.js.map +1 -1
  241. package/src/lib/testing/stand-in-site-record-routes.d.ts +26 -0
  242. package/src/lib/testing/stand-in-site-record-routes.js +55 -0
  243. package/src/lib/testing/stand-in-site-record-routes.js.map +1 -0
  244. package/src/lib/usage-axes.d.ts +35 -0
  245. package/src/lib/usage-axes.js +68 -0
  246. package/src/lib/usage-axes.js.map +1 -0
  247. package/src/lib/components/use-emails-hub-path.d.ts +0 -24
  248. package/src/lib/components/use-emails-hub-path.js +0 -59
  249. package/src/lib/components/use-emails-hub-path.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/sharing-rule-drawer.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n CONTACT_LIFECYCLE_STAGE_LABELS,\n CONTACT_LIFECYCLE_STAGES,\n CRM_LEAD_STATUS_LABELS,\n CRM_LEAD_STATUSES,\n crmMemberPickerLabel,\n} from '@aglyn/aglyn'\nimport { hostIdsFromScope, ORG_SCOPE_TOKEN } from '@aglyn/aglyn/app-utils/scope-tokens'\nimport {\n Button,\n Checkbox,\n Drawer,\n FormControl,\n FormControlLabel,\n FormGroup,\n FormHelperText,\n FormLabel,\n MenuItem,\n Radio,\n RadioGroup,\n Stack,\n Switch,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useEffect, useState } from 'react'\nimport type { OrgMemberOption } from '../hooks/use-org-member-directory'\nimport {\n CRM_SHARING_CRITERIA_FOR,\n CRM_SHARING_OBJECT_LABELS,\n CRM_SHARING_OBJECTS,\n type CrmShareAccess,\n type CrmSharingCriteria,\n type CrmSharingObject,\n type CrmSharingRule,\n type CrmSharingSite,\n} from '../model/crm-sharing'\n\n/** What the drawer hands back: the rule as `crm/sharing` `rule-save` reads it. */\nexport interface SharingRuleDraft {\n id?: string\n name: string\n object: CrmSharingObject\n enabled: boolean\n sourceHostIds: string[]\n criteria: CrmSharingCriteria\n targets: 'all' | string[]\n access: CrmShareAccess\n}\n\nexport interface SharingRuleDrawerProps {\n open: boolean\n onClose: () => void\n /** The rule being edited, or `null` for a new one. */\n rule: CrmSharingRule | null\n sites: readonly CrmSharingSite[]\n members: readonly OrgMemberOption[]\n campaigns: readonly { value: string; label: string }[]\n busy: boolean\n onSubmit: (draft: SharingRuleDraft) => void\n}\n\nconst splitList = (text: string) =>\n [...new Set(text.split(',').map((entry) => entry.trim()).filter(Boolean))].slice(0, 20)\n\n/**\n * A sharing rule, three parts (AGL-3336): WHICH RECORDS (an object, the\n * sites that captured them, optional criteria), SHARED WITH (sites, or all\n * sites), and ACCESS (read-only unless edit is chosen).\n */\nexport function SharingRuleDrawer(props: SharingRuleDrawerProps) {\n const { open, onClose, rule, sites, members, campaigns, busy, onSubmit } = props\n const [name, setName] = useState('')\n const [object, setObject] = useState<CrmSharingObject>('leads')\n const [enabled, setEnabled] = useState(true)\n const [sourceHostIds, setSourceHostIds] = useState<string[]>([])\n const [leadSources, setLeadSources] = useState('')\n const [tags, setTags] = useState('')\n const [stages, setStages] = useState<string[]>([])\n const [ownerUids, setOwnerUids] = useState<string[]>([])\n const [campaignIds, setCampaignIds] = useState<string[]>([])\n const [allSites, setAllSites] = useState(true)\n const [targets, setTargets] = useState<string[]>([])\n const [access, setAccess] = useState<CrmShareAccess>('read')\n\n useEffect(() => {\n if (!open) return\n setName(rule?.name ?? '')\n setObject(rule?.object ?? 'leads')\n setEnabled(rule?.enabled ?? true)\n setSourceHostIds(rule?.sourceHostIds ?? [])\n setLeadSources((rule?.criteria.leadSources ?? []).join(', '))\n setTags((rule?.criteria.tags ?? []).join(', '))\n setStages(rule?.criteria.stages ?? [])\n setOwnerUids(rule?.criteria.ownerUids ?? [])\n setCampaignIds(rule?.criteria.campaignIds ?? [])\n setAllSites(rule ? rule.targets.includes(ORG_SCOPE_TOKEN) : true)\n setTargets(rule ? hostIdsFromScope(rule.targets) : [])\n setAccess(rule?.access ?? 'read')\n }, [open, rule])\n\n const offers = CRM_SHARING_CRITERIA_FOR[object]\n const stageOptions: { value: string; label: string }[] =\n object === 'leads'\n ? CRM_LEAD_STATUSES.map((value) => ({ value, label: CRM_LEAD_STATUS_LABELS[value] }))\n : object === 'contacts'\n ? CONTACT_LIFECYCLE_STAGES.map((value) => ({\n value,\n label: CONTACT_LIFECYCLE_STAGE_LABELS[value],\n }))\n : []\n const canSubmit =\n !busy && name.trim().length > 0 && (allSites || targets.length > 0)\n\n const submit = () => {\n if (!canSubmit) return\n const criteria: CrmSharingCriteria = {}\n if (offers.includes('leadSources') && splitList(leadSources).length) {\n criteria.leadSources = splitList(leadSources)\n }\n if (offers.includes('tags') && splitList(tags).length) {\n criteria.tags = splitList(tags).map((tag) => tag.toLowerCase())\n }\n if (stageOptions.length && stages.length) criteria.stages = stages\n if (offers.includes('ownerUids') && ownerUids.length) criteria.ownerUids = ownerUids\n if (offers.includes('campaignIds') && campaignIds.length) criteria.campaignIds = campaignIds\n onSubmit({\n ...(rule ? { id: rule.id } : {}),\n name: name.trim(),\n object,\n enabled,\n sourceHostIds,\n criteria,\n targets: allSites ? 'all' : targets,\n access,\n })\n }\n\n const siteName = (id: string) => sites.find((site) => site.id === id)?.name ?? id\n return (\n <Drawer anchor=\"right\" open={open} onClose={busy ? undefined : onClose}>\n <Stack spacing={2} sx={{ width: 400, maxWidth: '100vw', p: 3 }}>\n <Typography variant=\"h6\">{rule ? 'Edit sharing rule' : 'New sharing rule'}</Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Shares every record the rule matches — the ones you already have, and every one ' +\n 'captured or changed from now on. Sharing lets a site see and work a record; it ' +\n 'never shares the person’s consent.'}\n </Typography>\n <TextField\n size=\"small\"\n label=\"Name\"\n value={name}\n onChange={(event) => setName(event.target.value)}\n slotProps={{ htmlInput: { maxLength: 120 } }}\n required\n fullWidth\n />\n <FormLabel>{'Which records'}</FormLabel>\n <TextField\n select\n size=\"small\"\n label=\"Records\"\n value={object}\n onChange={(event) => {\n setObject(event.target.value as CrmSharingObject)\n setStages([])\n }}\n disabled={Boolean(rule)}\n helperText={rule ? 'A rule keeps the kind of record it was made for.' : undefined}\n fullWidth\n >\n {CRM_SHARING_OBJECTS.map((option) => (\n <MenuItem key={option} value={option}>\n {CRM_SHARING_OBJECT_LABELS[option].many}\n </MenuItem>\n ))}\n </TextField>\n <TextField\n select\n size=\"small\"\n label=\"Captured on\"\n value={sourceHostIds}\n onChange={(event) => {\n const value = event.target.value as unknown\n setSourceHostIds(Array.isArray(value) ? (value as string[]) : String(value).split(','))\n }}\n slotProps={{\n select: {\n multiple: true,\n displayEmpty: true,\n renderValue: (value) =>\n (value as string[]).length ? (value as string[]).map(siteName).join(', ') : 'Any site',\n },\n inputLabel: { shrink: true },\n }}\n helperText=\"The sites the record was captured or created on.\"\n fullWidth\n >\n {sites.map((site) => (\n <MenuItem key={site.id} value={site.id}>\n {site.name}\n </MenuItem>\n ))}\n </TextField>\n {offers.includes('leadSources') ? (\n <TextField\n size=\"small\"\n label=\"Lead source\"\n value={leadSources}\n onChange={(event) => setLeadSources(event.target.value)}\n helperText=\"Any of these, separated by commas. Blank for any source.\"\n fullWidth\n />\n ) : null}\n {offers.includes('tags') ? (\n <TextField\n size=\"small\"\n label=\"Tags\"\n value={tags}\n onChange={(event) => setTags(event.target.value)}\n helperText=\"Any of these, separated by commas. Blank for any tags.\"\n fullWidth\n />\n ) : null}\n {stageOptions.length ? (\n <TextField\n select\n size=\"small\"\n label={object === 'leads' ? 'Status' : 'Lifecycle stage'}\n value={stages}\n onChange={(event) => {\n const value = event.target.value as unknown\n setStages(Array.isArray(value) ? (value as string[]) : String(value).split(','))\n }}\n slotProps={{\n select: {\n multiple: true,\n displayEmpty: true,\n renderValue: (value) =>\n (value as string[]).length\n ? (value as string[])\n .map((id) => stageOptions.find((option) => option.value === id)?.label ?? id)\n .join(', ')\n : 'Any',\n },\n inputLabel: { shrink: true },\n }}\n fullWidth\n >\n {stageOptions.map((option) => (\n <MenuItem key={option.value} value={option.value}>\n {option.label}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n {offers.includes('campaignIds') && campaigns.length ? (\n <TextField\n select\n size=\"small\"\n label=\"Campaign\"\n value={campaignIds}\n onChange={(event) => {\n const value = event.target.value as unknown\n setCampaignIds(Array.isArray(value) ? (value as string[]) : String(value).split(','))\n }}\n slotProps={{\n select: {\n multiple: true,\n displayEmpty: true,\n renderValue: (value) =>\n (value as string[]).length\n ? (value as string[])\n .map((id) => campaigns.find((option) => option.value === id)?.label ?? id)\n .join(', ')\n : 'Any',\n },\n inputLabel: { shrink: true },\n }}\n fullWidth\n >\n {campaigns.map((option) => (\n <MenuItem key={option.value} value={option.value}>\n {option.label}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n <TextField\n select\n size=\"small\"\n label=\"Owner\"\n value={ownerUids}\n onChange={(event) => {\n const value = event.target.value as unknown\n setOwnerUids(Array.isArray(value) ? (value as string[]) : String(value).split(','))\n }}\n slotProps={{\n select: {\n multiple: true,\n displayEmpty: true,\n renderValue: (value) =>\n (value as string[]).length\n ? (value as string[])\n .map((uid) => {\n const member = members.find((entry) => entry.uid === uid)\n return member ? crmMemberPickerLabel(member) : uid\n })\n .join(', ')\n : 'Anyone',\n },\n inputLabel: { shrink: true },\n }}\n fullWidth\n >\n {members.map((member) => (\n <MenuItem key={member.uid} value={member.uid}>\n {crmMemberPickerLabel(member)}\n </MenuItem>\n ))}\n </TextField>\n <FormControl>\n <FormLabel>{'Shared with'}</FormLabel>\n <FormGroup>\n <FormControlLabel\n control={\n <Checkbox checked={allSites} onChange={(event) => setAllSites(event.target.checked)} />\n }\n label=\"All sites, including sites added later\"\n />\n {sites.map((site) => (\n <FormControlLabel\n key={site.id}\n control={\n <Checkbox\n checked={allSites || targets.includes(site.id)}\n disabled={allSites}\n onChange={(event) =>\n setTargets((current) =>\n event.target.checked\n ? [...current, site.id]\n : current.filter((id) => id !== site.id),\n )\n }\n />\n }\n label={site.name}\n />\n ))}\n </FormGroup>\n </FormControl>\n <FormControl>\n <FormLabel>{'Access'}</FormLabel>\n <RadioGroup\n value={access}\n onChange={(event) => setAccess(event.target.value === 'edit' ? 'edit' : 'read')}\n >\n <FormControlLabel value=\"read\" control={<Radio size=\"small\" />} label=\"Read-only\" />\n <FormControlLabel value=\"edit\" control={<Radio size=\"small\" />} label=\"Read and edit\" />\n </RadioGroup>\n </FormControl>\n <FormControlLabel\n control={<Switch checked={enabled} onChange={(event) => setEnabled(event.target.checked)} />}\n label=\"On\"\n />\n {!enabled ? (\n <FormHelperText>{'Off keeps the rule and takes away what it shared.'}</FormHelperText>\n ) : null}\n <Stack direction=\"row\" spacing={1}>\n <Button variant=\"contained\" disabled={!canSubmit} onClick={submit}>\n {rule ? 'Save rule' : 'Add rule'}\n </Button>\n <Button onClick={onClose} disabled={busy}>\n {'Cancel'}\n </Button>\n </Stack>\n </Stack>\n </Drawer>\n )\n}\nSharingRuleDrawer.displayName = 'SharingRuleDrawer'\n\nexport default SharingRuleDrawer\n"],"names":["CONTACT_LIFECYCLE_STAGE_LABELS","CONTACT_LIFECYCLE_STAGES","CRM_LEAD_STATUS_LABELS","CRM_LEAD_STATUSES","crmMemberPickerLabel","hostIdsFromScope","ORG_SCOPE_TOKEN","Button","Checkbox","Drawer","FormControl","FormControlLabel","FormGroup","FormHelperText","FormLabel","MenuItem","Radio","RadioGroup","Stack","Switch","TextField","Typography","useEffect","useState","CRM_SHARING_CRITERIA_FOR","CRM_SHARING_OBJECT_LABELS","CRM_SHARING_OBJECTS","splitList","text","Set","split","map","entry","trim","filter","Boolean","slice","SharingRuleDrawer","props","open","onClose","rule","sites","members","campaigns","busy","onSubmit","name","setName","object","setObject","enabled","setEnabled","sourceHostIds","setSourceHostIds","leadSources","setLeadSources","tags","setTags","stages","setStages","ownerUids","setOwnerUids","campaignIds","setCampaignIds","allSites","setAllSites","targets","setTargets","access","setAccess","criteria","join","includes","offers","stageOptions","value","label","canSubmit","length","submit","tag","toLowerCase","id","siteName","find","site","anchor","undefined","spacing","sx","width","maxWidth","p","variant","color","size","onChange","event","target","slotProps","htmlInput","maxLength","required","fullWidth","select","disabled","helperText","option","many","Array","isArray","String","multiple","displayEmpty","renderValue","inputLabel","shrink","uid","member","control","checked","current","direction","onClick","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;;AAEA,SACEA,8BAA8B,EAC9BC,wBAAwB,EACxBC,sBAAsB,EACtBC,iBAAiB,EACjBC,oBAAoB,QACf,eAAc;AACrB,SAASC,gBAAgB,EAAEC,eAAe,QAAQ,sCAAqC;AACvF,SACEC,MAAM,EACNC,QAAQ,EACRC,MAAM,EACNC,WAAW,EACXC,gBAAgB,EAChBC,SAAS,EACTC,cAAc,EACdC,SAAS,EACTC,QAAQ,EACRC,KAAK,EACLC,UAAU,EACVC,KAAK,EACLC,MAAM,EACNC,SAAS,EACTC,UAAU,QACL,gBAAe;AACtB,SAASC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAE3C,SACEC,wBAAwB,EACxBC,yBAAyB,EACzBC,mBAAmB,QAMd,0BAAsB;AA0B7B,MAAMC,YAAY,CAACC,OACjB;WAAI,IAAIC,IAAID,KAAKE,KAAK,CAAC,KAAKC,GAAG,CAAC,CAACC,QAAUA,MAAMC,IAAI,IAAIC,MAAM,CAACC;KAAU,CAACC,KAAK,CAAC,GAAG;AAEtF;;;;CAIC,GACD,OAAO,SAASC,kBAAkBC,KAA6B;IAC7D,MAAM,EAAEC,IAAI,EAAEC,OAAO,EAAEC,IAAI,EAAEC,KAAK,EAAEC,OAAO,EAAEC,SAAS,EAAEC,IAAI,EAAEC,QAAQ,EAAE,GAAGR;IAC3E,MAAM,CAACS,MAAMC,QAAQ,GAAGzB,SAAS;IACjC,MAAM,CAAC0B,QAAQC,UAAU,GAAG3B,SAA2B;IACvD,MAAM,CAAC4B,SAASC,WAAW,GAAG7B,SAAS;IACvC,MAAM,CAAC8B,eAAeC,iBAAiB,GAAG/B,SAAmB,EAAE;IAC/D,MAAM,CAACgC,aAAaC,eAAe,GAAGjC,SAAS;IAC/C,MAAM,CAACkC,MAAMC,QAAQ,GAAGnC,SAAS;IACjC,MAAM,CAACoC,QAAQC,UAAU,GAAGrC,SAAmB,EAAE;IACjD,MAAM,CAACsC,WAAWC,aAAa,GAAGvC,SAAmB,EAAE;IACvD,MAAM,CAACwC,aAAaC,eAAe,GAAGzC,SAAmB,EAAE;IAC3D,MAAM,CAAC0C,UAAUC,YAAY,GAAG3C,SAAS;IACzC,MAAM,CAAC4C,SAASC,WAAW,GAAG7C,SAAmB,EAAE;IACnD,MAAM,CAAC8C,QAAQC,UAAU,GAAG/C,SAAyB;IAErDD,UAAU;;QACR,IAAI,CAACiB,MAAM;QACXS,gBAAQP,wBAAAA,KAAMM,IAAI,mBAAI;QACtBG,mBAAUT,wBAAAA,KAAMQ,MAAM,oBAAI;QAC1BG,oBAAWX,wBAAAA,KAAMU,OAAO,oBAAI;QAC5BG,0BAAiBb,wBAAAA,KAAMY,aAAa,oBAAI,EAAE;QAC1CG,eAAe,UAACf,wBAAAA,KAAM8B,QAAQ,CAAChB,WAAW,oBAAI,EAAE,EAAEiB,IAAI,CAAC;QACvDd,QAAQ,UAACjB,wBAAAA,KAAM8B,QAAQ,CAACd,IAAI,oBAAI,EAAE,EAAEe,IAAI,CAAC;QACzCZ,mBAAUnB,wBAAAA,KAAM8B,QAAQ,CAACZ,MAAM,oBAAI,EAAE;QACrCG,sBAAarB,wBAAAA,KAAM8B,QAAQ,CAACV,SAAS,oBAAI,EAAE;QAC3CG,wBAAevB,wBAAAA,KAAM8B,QAAQ,CAACR,WAAW,oBAAI,EAAE;QAC/CG,YAAYzB,OAAOA,KAAK0B,OAAO,CAACM,QAAQ,CAACnE,mBAAmB;QAC5D8D,WAAW3B,OAAOpC,iBAAiBoC,KAAK0B,OAAO,IAAI,EAAE;QACrDG,mBAAU7B,wBAAAA,KAAM4B,MAAM,oBAAI;IAC5B,GAAG;QAAC9B;QAAME;KAAK;IAEf,MAAMiC,SAASlD,wBAAwB,CAACyB,OAAO;IAC/C,MAAM0B,eACJ1B,WAAW,UACP9C,kBAAkB4B,GAAG,CAAC,CAAC6C,QAAW,CAAA;YAAEA;YAAOC,OAAO3E,sBAAsB,CAAC0E,MAAM;QAAC,CAAA,KAChF3B,WAAW,aACThD,yBAAyB8B,GAAG,CAAC,CAAC6C,QAAW,CAAA;YACvCA;YACAC,OAAO7E,8BAA8B,CAAC4E,MAAM;QAC9C,CAAA,KACA,EAAE;IACV,MAAME,YACJ,CAACjC,QAAQE,KAAKd,IAAI,GAAG8C,MAAM,GAAG,KAAMd,CAAAA,YAAYE,QAAQY,MAAM,GAAG,CAAA;IAEnE,MAAMC,SAAS;QACb,IAAI,CAACF,WAAW;QAChB,MAAMP,WAA+B,CAAC;QACtC,IAAIG,OAAOD,QAAQ,CAAC,kBAAkB9C,UAAU4B,aAAawB,MAAM,EAAE;YACnER,SAAShB,WAAW,GAAG5B,UAAU4B;QACnC;QACA,IAAImB,OAAOD,QAAQ,CAAC,WAAW9C,UAAU8B,MAAMsB,MAAM,EAAE;YACrDR,SAASd,IAAI,GAAG9B,UAAU8B,MAAM1B,GAAG,CAAC,CAACkD,MAAQA,IAAIC,WAAW;QAC9D;QACA,IAAIP,aAAaI,MAAM,IAAIpB,OAAOoB,MAAM,EAAER,SAASZ,MAAM,GAAGA;QAC5D,IAAIe,OAAOD,QAAQ,CAAC,gBAAgBZ,UAAUkB,MAAM,EAAER,SAASV,SAAS,GAAGA;QAC3E,IAAIa,OAAOD,QAAQ,CAAC,kBAAkBV,YAAYgB,MAAM,EAAER,SAASR,WAAW,GAAGA;QACjFjB,SAAS,aACHL,OAAO;YAAE0C,IAAI1C,KAAK0C,EAAE;QAAC,IAAI,CAAC;YAC9BpC,MAAMA,KAAKd,IAAI;YACfgB;YACAE;YACAE;YACAkB;YACAJ,SAASF,WAAW,QAAQE;YAC5BE;;IAEJ;IAEA,MAAMe,WAAW,CAACD;;YAAezC;wBAAAA,cAAAA,MAAM2C,IAAI,CAAC,CAACC,OAASA,KAAKH,EAAE,KAAKA,wBAAjCzC,YAAsCK,IAAI,mBAAIoC;;IAC/E,qBACE,KAAC1E;QAAO8E,QAAO;QAAQhD,MAAMA;QAAMC,SAASK,OAAO2C,YAAYhD;kBAC7D,cAAA,MAACtB;YAAMuE,SAAS;YAAGC,IAAI;gBAAEC,OAAO;gBAAKC,UAAU;gBAASC,GAAG;YAAE;;8BAC3D,KAACxE;oBAAWyE,SAAQ;8BAAMrD,OAAO,sBAAsB;;8BACvD,KAACpB;oBAAWyE,SAAQ;oBAAQC,OAAM;8BAC/B,qFACC,oFACA;;8BAEJ,KAAC3E;oBACC4E,MAAK;oBACLnB,OAAM;oBACND,OAAO7B;oBACPkD,UAAU,CAACC,QAAUlD,QAAQkD,MAAMC,MAAM,CAACvB,KAAK;oBAC/CwB,WAAW;wBAAEC,WAAW;4BAAEC,WAAW;wBAAI;oBAAE;oBAC3CC,QAAQ;oBACRC,SAAS;;8BAEX,KAAC1F;8BAAW;;8BACZ,KAACM;oBACCqF,MAAM;oBACNT,MAAK;oBACLnB,OAAM;oBACND,OAAO3B;oBACPgD,UAAU,CAACC;wBACThD,UAAUgD,MAAMC,MAAM,CAACvB,KAAK;wBAC5BhB,UAAU,EAAE;oBACd;oBACA8C,UAAUvE,QAAQM;oBAClBkE,YAAYlE,OAAO,qDAAqD+C;oBACxEgB,SAAS;8BAER9E,oBAAoBK,GAAG,CAAC,CAAC6E,uBACxB,KAAC7F;4BAAsB6D,OAAOgC;sCAC3BnF,yBAAyB,CAACmF,OAAO,CAACC,IAAI;2BAD1BD;;8BAKnB,KAACxF;oBACCqF,MAAM;oBACNT,MAAK;oBACLnB,OAAM;oBACND,OAAOvB;oBACP4C,UAAU,CAACC;wBACT,MAAMtB,QAAQsB,MAAMC,MAAM,CAACvB,KAAK;wBAChCtB,iBAAiBwD,MAAMC,OAAO,CAACnC,SAAUA,QAAqBoC,OAAOpC,OAAO9C,KAAK,CAAC;oBACpF;oBACAsE,WAAW;wBACTK,QAAQ;4BACNQ,UAAU;4BACVC,cAAc;4BACdC,aAAa,CAACvC,QACZ,AAACA,MAAmBG,MAAM,GAAG,AAACH,MAAmB7C,GAAG,CAACqD,UAAUZ,IAAI,CAAC,QAAQ;wBAChF;wBACA4C,YAAY;4BAAEC,QAAQ;wBAAK;oBAC7B;oBACAV,YAAW;oBACXH,SAAS;8BAER9D,MAAMX,GAAG,CAAC,CAACuD,qBACV,KAACvE;4BAAuB6D,OAAOU,KAAKH,EAAE;sCACnCG,KAAKvC,IAAI;2BADGuC,KAAKH,EAAE;;gBAKzBT,OAAOD,QAAQ,CAAC,+BACf,KAACrD;oBACC4E,MAAK;oBACLnB,OAAM;oBACND,OAAOrB;oBACP0C,UAAU,CAACC,QAAU1C,eAAe0C,MAAMC,MAAM,CAACvB,KAAK;oBACtD+B,YAAW;oBACXH,SAAS;qBAET;gBACH9B,OAAOD,QAAQ,CAAC,wBACf,KAACrD;oBACC4E,MAAK;oBACLnB,OAAM;oBACND,OAAOnB;oBACPwC,UAAU,CAACC,QAAUxC,QAAQwC,MAAMC,MAAM,CAACvB,KAAK;oBAC/C+B,YAAW;oBACXH,SAAS;qBAET;gBACH7B,aAAaI,MAAM,iBAClB,KAAC3D;oBACCqF,MAAM;oBACNT,MAAK;oBACLnB,OAAO5B,WAAW,UAAU,WAAW;oBACvC2B,OAAOjB;oBACPsC,UAAU,CAACC;wBACT,MAAMtB,QAAQsB,MAAMC,MAAM,CAACvB,KAAK;wBAChChB,UAAUkD,MAAMC,OAAO,CAACnC,SAAUA,QAAqBoC,OAAOpC,OAAO9C,KAAK,CAAC;oBAC7E;oBACAsE,WAAW;wBACTK,QAAQ;4BACNQ,UAAU;4BACVC,cAAc;4BACdC,aAAa,CAACvC,QACZ,AAACA,MAAmBG,MAAM,GACtB,AAACH,MACE7C,GAAG,CAAC,CAACoD;;wCAAOR;oDAAAA,qBAAAA,aAAaU,IAAI,CAAC,CAACuB,SAAWA,OAAOhC,KAAK,KAAKO,wBAA/CR,mBAAoDE,KAAK,mBAAIM;mCACzEX,IAAI,CAAC,QACR;wBACR;wBACA4C,YAAY;4BAAEC,QAAQ;wBAAK;oBAC7B;oBACAb,SAAS;8BAER7B,aAAa5C,GAAG,CAAC,CAAC6E,uBACjB,KAAC7F;4BAA4B6D,OAAOgC,OAAOhC,KAAK;sCAC7CgC,OAAO/B,KAAK;2BADA+B,OAAOhC,KAAK;qBAK7B;gBACHF,OAAOD,QAAQ,CAAC,kBAAkB7B,UAAUmC,MAAM,iBACjD,KAAC3D;oBACCqF,MAAM;oBACNT,MAAK;oBACLnB,OAAM;oBACND,OAAOb;oBACPkC,UAAU,CAACC;wBACT,MAAMtB,QAAQsB,MAAMC,MAAM,CAACvB,KAAK;wBAChCZ,eAAe8C,MAAMC,OAAO,CAACnC,SAAUA,QAAqBoC,OAAOpC,OAAO9C,KAAK,CAAC;oBAClF;oBACAsE,WAAW;wBACTK,QAAQ;4BACNQ,UAAU;4BACVC,cAAc;4BACdC,aAAa,CAACvC,QACZ,AAACA,MAAmBG,MAAM,GACtB,AAACH,MACE7C,GAAG,CAAC,CAACoD;;wCAAOvC;oDAAAA,kBAAAA,UAAUyC,IAAI,CAAC,CAACuB,SAAWA,OAAOhC,KAAK,KAAKO,wBAA5CvC,gBAAiDiC,KAAK,mBAAIM;mCACtEX,IAAI,CAAC,QACR;wBACR;wBACA4C,YAAY;4BAAEC,QAAQ;wBAAK;oBAC7B;oBACAb,SAAS;8BAER5D,UAAUb,GAAG,CAAC,CAAC6E,uBACd,KAAC7F;4BAA4B6D,OAAOgC,OAAOhC,KAAK;sCAC7CgC,OAAO/B,KAAK;2BADA+B,OAAOhC,KAAK;qBAK7B;8BACJ,KAACxD;oBACCqF,MAAM;oBACNT,MAAK;oBACLnB,OAAM;oBACND,OAAOf;oBACPoC,UAAU,CAACC;wBACT,MAAMtB,QAAQsB,MAAMC,MAAM,CAACvB,KAAK;wBAChCd,aAAagD,MAAMC,OAAO,CAACnC,SAAUA,QAAqBoC,OAAOpC,OAAO9C,KAAK,CAAC;oBAChF;oBACAsE,WAAW;wBACTK,QAAQ;4BACNQ,UAAU;4BACVC,cAAc;4BACdC,aAAa,CAACvC,QACZ,AAACA,MAAmBG,MAAM,GACtB,AAACH,MACE7C,GAAG,CAAC,CAACuF;oCACJ,MAAMC,SAAS5E,QAAQ0C,IAAI,CAAC,CAACrD,QAAUA,MAAMsF,GAAG,KAAKA;oCACrD,OAAOC,SAASnH,qBAAqBmH,UAAUD;gCACjD,GACC9C,IAAI,CAAC,QACR;wBACR;wBACA4C,YAAY;4BAAEC,QAAQ;wBAAK;oBAC7B;oBACAb,SAAS;8BAER7D,QAAQZ,GAAG,CAAC,CAACwF,uBACZ,KAACxG;4BAA0B6D,OAAO2C,OAAOD,GAAG;sCACzClH,qBAAqBmH;2BADTA,OAAOD,GAAG;;8BAK7B,MAAC5G;;sCACC,KAACI;sCAAW;;sCACZ,MAACF;;8CACC,KAACD;oCACC6G,uBACE,KAAChH;wCAASiH,SAASxD;wCAAUgC,UAAU,CAACC,QAAUhC,YAAYgC,MAAMC,MAAM,CAACsB,OAAO;;oCAEpF5C,OAAM;;gCAEPnC,MAAMX,GAAG,CAAC,CAACuD,qBACV,KAAC3E;wCAEC6G,uBACE,KAAChH;4CACCiH,SAASxD,YAAYE,QAAQM,QAAQ,CAACa,KAAKH,EAAE;4CAC7CuB,UAAUzC;4CACVgC,UAAU,CAACC,QACT9B,WAAW,CAACsD,UACVxB,MAAMC,MAAM,CAACsB,OAAO,GAChB;2DAAIC;wDAASpC,KAAKH,EAAE;qDAAC,GACrBuC,QAAQxF,MAAM,CAAC,CAACiD,KAAOA,OAAOG,KAAKH,EAAE;;wCAKjDN,OAAOS,KAAKvC,IAAI;uCAdXuC,KAAKH,EAAE;;;;;8BAmBpB,MAACzE;;sCACC,KAACI;sCAAW;;sCACZ,MAACG;4BACC2D,OAAOP;4BACP4B,UAAU,CAACC,QAAU5B,UAAU4B,MAAMC,MAAM,CAACvB,KAAK,KAAK,SAAS,SAAS;;8CAExE,KAACjE;oCAAiBiE,OAAM;oCAAO4C,uBAAS,KAACxG;wCAAMgF,MAAK;;oCAAYnB,OAAM;;8CACtE,KAAClE;oCAAiBiE,OAAM;oCAAO4C,uBAAS,KAACxG;wCAAMgF,MAAK;;oCAAYnB,OAAM;;;;;;8BAG1E,KAAClE;oBACC6G,uBAAS,KAACrG;wBAAOsG,SAAStE;wBAAS8C,UAAU,CAACC,QAAU9C,WAAW8C,MAAMC,MAAM,CAACsB,OAAO;;oBACvF5C,OAAM;;gBAEP,CAAC1B,wBACA,KAACtC;8BAAgB;qBACf;8BACJ,MAACK;oBAAMyG,WAAU;oBAAMlC,SAAS;;sCAC9B,KAAClF;4BAAOuF,SAAQ;4BAAYY,UAAU,CAAC5B;4BAAW8C,SAAS5C;sCACxDvC,OAAO,cAAc;;sCAExB,KAAClC;4BAAOqH,SAASpF;4BAASkE,UAAU7D;sCACjC;;;;;;;AAMb;AACAR,kBAAkBwF,WAAW,GAAG;AAEhC,eAAexF,kBAAiB"}
@@ -0,0 +1,24 @@
1
+ import type { CrmOrgDoc } from '../hooks/use-crm-scope';
2
+ export interface SharingRulesCardProps {
3
+ orgId: string | null;
4
+ org: CrmOrgDoc;
5
+ canManage: boolean;
6
+ ready: boolean;
7
+ }
8
+ /**
9
+ * SHARING RULES — the organization's automated sharing (AGL-3336).
10
+ *
11
+ * A rule shares every record it matches with other sites: the existing ones
12
+ * through a recompute that runs when the rule is saved, with its progress
13
+ * on the row, and every later capture or change through the record-written
14
+ * seam. The recompute runs in passes; the card keeps asking while the page
15
+ * is open, and a rule whose run stopped offers Resume.
16
+ *
17
+ * Mounted at the organization level only: a rule is a fact about the org,
18
+ * and its targets are the org's sites.
19
+ */
20
+ export declare function SharingRulesCard(props: SharingRulesCardProps): import("react").JSX.Element;
21
+ export declare namespace SharingRulesCard {
22
+ var displayName: string;
23
+ }
24
+ export default SharingRulesCard;
@@ -0,0 +1,287 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ 'use client';
17
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
18
+ import { CONTACT_LIFECYCLE_STAGE_LABELS, CRM_LEAD_STATUS_LABELS, pluginDocsHelp } from "@aglyn/aglyn";
19
+ import { mdiDeleteOutline, mdiPencilOutline, mdiRefresh } from "@aglyn/shared-data-mdi";
20
+ import { CardDisplay, MdiIcon } from "@aglyn/shared-ui-jsx";
21
+ import RowActionsMenu from "@aglyn/shared-ui-jsx/components/row-actions-menu.component";
22
+ import { ScrollTable } from "@aglyn/shared-ui-jsx/components/scroll-table.component";
23
+ import { useSnackbar } from "@aglyn/shared-ui-snackstack";
24
+ import { useUser } from "@aglyn/tenant-feature-instance";
25
+ import { Button, LinearProgress, Stack, TableBody, TableCell, TableHead, TableRow, Typography } from "@mui/material";
26
+ import { useCallback, useState } from "react";
27
+ import { useCrmCampaigns } from "../hooks/use-crm-campaigns.js";
28
+ import { useCrmSharingRules, useCrmSharingSites } from "../hooks/use-crm-sharing.js";
29
+ import { useOrgMemberDirectory } from "../hooks/use-org-member-directory.js";
30
+ import { CRM_SHARING_RULES_MAX, describeShareTargets, describeSharingRuleScope } from "../model/crm-sharing.js";
31
+ import { callCrmSharing } from "../model/crm-sharing-api.js";
32
+ import { SharingRuleDrawer } from "./sharing-rule-drawer.js";
33
+ /** How far a rule's recompute has got, as the table shows it. */ function runLabel(rule) {
34
+ var _run_error;
35
+ const run = rule.run;
36
+ if (rule.deleting) return 'Removing what it shared…';
37
+ if (!run) return rule.enabled ? 'On' : 'Off';
38
+ if (run.status === 'running') {
39
+ return `${run.mode === 'remove' ? 'Removing' : 'Applying'} — ${run.processed} checked, ${run.changed} changed`;
40
+ }
41
+ if (run.status === 'failed') return (_run_error = run.error) != null ? _run_error : 'Stopped';
42
+ return `${rule.enabled ? 'On' : 'Off'} — ${run.changed} ${run.changed === 1 ? 'record' : 'records'} changed`;
43
+ }
44
+ /**
45
+ * SHARING RULES — the organization's automated sharing (AGL-3336).
46
+ *
47
+ * A rule shares every record it matches with other sites: the existing ones
48
+ * through a recompute that runs when the rule is saved, with its progress
49
+ * on the row, and every later capture or change through the record-written
50
+ * seam. The recompute runs in passes; the card keeps asking while the page
51
+ * is open, and a rule whose run stopped offers Resume.
52
+ *
53
+ * Mounted at the organization level only: a rule is a fact about the org,
54
+ * and its targets are the org's sites.
55
+ */ export function SharingRulesCard(props) {
56
+ const { orgId, org, canManage, ready } = props;
57
+ const { data: user } = useUser();
58
+ const { enqueueSnackbar } = useSnackbar();
59
+ const rules = useCrmSharingRules(org);
60
+ const sites = useCrmSharingSites({
61
+ hostId: null,
62
+ orgId,
63
+ enabled: canManage
64
+ });
65
+ const roster = useOrgMemberDirectory(orgId);
66
+ const campaigns = useCrmCampaigns({
67
+ hostId: null,
68
+ orgId
69
+ }, {
70
+ enabled: canManage
71
+ });
72
+ const [editing, setEditing] = useState(null);
73
+ const [drawerOpen, setDrawerOpen] = useState(false);
74
+ const [busy, setBusy] = useState(false);
75
+ const scope = orgId ? {
76
+ orgId
77
+ } : null;
78
+ /** One action, then `rule-continue` until the rule's run has nothing left. */ const run = useCallback(async (action, body, said)=>{
79
+ if (!scope) return;
80
+ setBusy(true);
81
+ try {
82
+ var _ref, _body_ruleId;
83
+ var _answer_rule;
84
+ let answer = await callCrmSharing(user, scope, action, body);
85
+ const ruleId = (_ref = (_answer_rule = answer.rule) == null ? void 0 : _answer_rule.id) != null ? _ref : String((_body_ruleId = body['ruleId']) != null ? _body_ruleId : '');
86
+ while(answer.more && ruleId){
87
+ answer = await callCrmSharing(user, scope, 'rule-continue', {
88
+ ruleId
89
+ });
90
+ }
91
+ enqueueSnackbar(said, {
92
+ variant: 'success',
93
+ persist: false
94
+ });
95
+ setDrawerOpen(false);
96
+ } catch (error) {
97
+ enqueueSnackbar(error.message, {
98
+ variant: 'error',
99
+ allowDuplicate: true
100
+ });
101
+ } finally{
102
+ setBusy(false);
103
+ }
104
+ }, // `scope` is rebuilt from `orgId` every render.
105
+ // eslint-disable-next-line react-hooks/exhaustive-deps
106
+ [
107
+ user,
108
+ orgId,
109
+ enqueueSnackbar
110
+ ]);
111
+ const save = (draft)=>void run('rule-save', {
112
+ rule: draft
113
+ }, draft.id ? 'Rule saved' : 'Rule added');
114
+ const canEdit = ready && canManage && !busy;
115
+ const stageLabel = (id)=>{
116
+ var _ref, _CRM_LEAD_STATUS_LABELS_id;
117
+ return (_ref = (_CRM_LEAD_STATUS_LABELS_id = CRM_LEAD_STATUS_LABELS[id]) != null ? _CRM_LEAD_STATUS_LABELS_id : CONTACT_LIFECYCLE_STAGE_LABELS[id]) != null ? _ref : id;
118
+ };
119
+ return /*#__PURE__*/ _jsxs(CardDisplay, {
120
+ header: 'Sharing rules',
121
+ help: pluginDocsHelp('crmSharing', {
122
+ anchor: '#sharing-rules'
123
+ }),
124
+ contentGutterX: true,
125
+ contentGutterY: true,
126
+ contentBordered: "all",
127
+ HeaderProps: {
128
+ action: /*#__PURE__*/ _jsx(Button, {
129
+ variant: "contained",
130
+ disabled: !canEdit || rules.length >= CRM_SHARING_RULES_MAX,
131
+ onClick: ()=>{
132
+ setEditing(null);
133
+ setDrawerOpen(true);
134
+ },
135
+ children: 'Add rule'
136
+ })
137
+ },
138
+ children: [
139
+ /*#__PURE__*/ _jsxs(Stack, {
140
+ spacing: 2,
141
+ children: [
142
+ /*#__PURE__*/ _jsx(Typography, {
143
+ variant: "body2",
144
+ color: "text.secondary",
145
+ children: 'Share leads, contacts, companies or deals captured on one site with other sites, ' + 'automatically. A rule applies to the records you have now and to every one captured ' + 'or changed later. Sharing is visibility only: a site never gains a person’s consent ' + 'through it.'
146
+ }),
147
+ busy ? /*#__PURE__*/ _jsx(LinearProgress, {}) : null,
148
+ rules.length === 0 ? /*#__PURE__*/ _jsx(Typography, {
149
+ variant: "body2",
150
+ color: "text.secondary",
151
+ children: 'No sharing rules. Every record is visible only to the sites that captured it.'
152
+ }) : /*#__PURE__*/ _jsxs(ScrollTable, {
153
+ size: "small",
154
+ children: [
155
+ /*#__PURE__*/ _jsx(TableHead, {
156
+ children: /*#__PURE__*/ _jsxs(TableRow, {
157
+ children: [
158
+ /*#__PURE__*/ _jsx(TableCell, {
159
+ children: 'Rule'
160
+ }),
161
+ /*#__PURE__*/ _jsx(TableCell, {
162
+ children: 'Which records'
163
+ }),
164
+ /*#__PURE__*/ _jsx(TableCell, {
165
+ children: 'Shared with'
166
+ }),
167
+ /*#__PURE__*/ _jsx(TableCell, {
168
+ children: 'Status'
169
+ }),
170
+ /*#__PURE__*/ _jsx(TableCell, {
171
+ align: "right"
172
+ })
173
+ ]
174
+ })
175
+ }),
176
+ /*#__PURE__*/ _jsx(TableBody, {
177
+ children: rules.map((rule)=>/*#__PURE__*/ _jsxs(TableRow, {
178
+ hover: true,
179
+ children: [
180
+ /*#__PURE__*/ _jsx(TableCell, {
181
+ children: /*#__PURE__*/ _jsx(Typography, {
182
+ variant: "body2",
183
+ children: rule.name
184
+ })
185
+ }),
186
+ /*#__PURE__*/ _jsx(TableCell, {
187
+ children: /*#__PURE__*/ _jsx(Typography, {
188
+ variant: "body2",
189
+ children: describeSharingRuleScope(rule, sites.siteName, {
190
+ owner: roster.nameOf,
191
+ stage: stageLabel
192
+ })
193
+ })
194
+ }),
195
+ /*#__PURE__*/ _jsx(TableCell, {
196
+ children: /*#__PURE__*/ _jsx(Typography, {
197
+ variant: "body2",
198
+ children: `${describeShareTargets(rule.targets, sites.siteName)}, ${rule.access === 'edit' ? 'read and edit' : 'read-only'}`
199
+ })
200
+ }),
201
+ /*#__PURE__*/ _jsx(TableCell, {
202
+ children: /*#__PURE__*/ _jsx(Typography, {
203
+ variant: "body2",
204
+ color: "text.secondary",
205
+ children: runLabel(rule)
206
+ })
207
+ }),
208
+ /*#__PURE__*/ _jsx(TableCell, {
209
+ align: "right",
210
+ sx: {
211
+ width: 56
212
+ },
213
+ children: /*#__PURE__*/ _jsx(RowActionsMenu, {
214
+ label: rule.name,
215
+ items: [
216
+ {
217
+ key: 'edit',
218
+ label: 'Edit rule',
219
+ icon: /*#__PURE__*/ _jsx(MdiIcon, {
220
+ path: mdiPencilOutline.path,
221
+ size: 0.8
222
+ }),
223
+ disabled: !canEdit || rule.deleting === true,
224
+ onClick: ()=>{
225
+ setEditing(rule);
226
+ setDrawerOpen(true);
227
+ }
228
+ },
229
+ ...rule.run && rule.run.status !== 'done' ? [
230
+ {
231
+ key: 'resume',
232
+ label: 'Resume',
233
+ icon: /*#__PURE__*/ _jsx(MdiIcon, {
234
+ path: mdiRefresh.path,
235
+ size: 0.8
236
+ }),
237
+ disabled: !canEdit,
238
+ onClick: ()=>void run('rule-continue', {
239
+ ruleId: rule.id
240
+ }, 'Rule applied')
241
+ }
242
+ ] : [],
243
+ {
244
+ key: 'delete',
245
+ label: 'Delete rule',
246
+ icon: /*#__PURE__*/ _jsx(MdiIcon, {
247
+ path: mdiDeleteOutline.path,
248
+ size: 0.8
249
+ }),
250
+ destructive: true,
251
+ disabled: !canEdit,
252
+ onClick: ()=>void run('rule-delete', {
253
+ ruleId: rule.id
254
+ }, 'Rule deleted')
255
+ }
256
+ ]
257
+ })
258
+ })
259
+ ]
260
+ }, rule.id))
261
+ })
262
+ ]
263
+ }),
264
+ ready && !canManage ? /*#__PURE__*/ _jsx(Typography, {
265
+ variant: "caption",
266
+ color: "text.secondary",
267
+ children: 'Only a workspace owner or admin can change sharing rules.'
268
+ }) : null
269
+ ]
270
+ }),
271
+ /*#__PURE__*/ _jsx(SharingRuleDrawer, {
272
+ open: drawerOpen,
273
+ onClose: ()=>setDrawerOpen(false),
274
+ rule: editing,
275
+ sites: sites.sites,
276
+ members: roster.members,
277
+ campaigns: campaigns.options,
278
+ busy: busy,
279
+ onSubmit: save
280
+ })
281
+ ]
282
+ });
283
+ }
284
+ SharingRulesCard.displayName = 'SharingRulesCard';
285
+ export default SharingRulesCard;
286
+
287
+ //# sourceMappingURL=sharing-rules-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/sharing-rules-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 CONTACT_LIFECYCLE_STAGE_LABELS,\n type ContactLifecycleStage,\n CRM_LEAD_STATUS_LABELS,\n type CrmLeadStatus,\n pluginDocsHelp,\n} from '@aglyn/aglyn'\nimport { mdiDeleteOutline, mdiPencilOutline, mdiRefresh } from '@aglyn/shared-data-mdi'\nimport { CardDisplay, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport RowActionsMenu 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 { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Button,\n LinearProgress,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n Typography,\n} from '@mui/material'\nimport { useCallback, useState } from 'react'\nimport { useCrmCampaigns } from '../hooks/use-crm-campaigns'\nimport { useCrmSharingRules, useCrmSharingSites } from '../hooks/use-crm-sharing'\nimport type { CrmOrgDoc } from '../hooks/use-crm-scope'\nimport { useOrgMemberDirectory } from '../hooks/use-org-member-directory'\nimport {\n CRM_SHARING_RULES_MAX,\n type CrmSharingAction,\n type CrmSharingRule,\n describeShareTargets,\n describeSharingRuleScope,\n} from '../model/crm-sharing'\nimport { callCrmSharing } from '../model/crm-sharing-api'\nimport { SharingRuleDrawer, type SharingRuleDraft } from './sharing-rule-drawer'\n\nexport interface SharingRulesCardProps {\n orgId: string | null\n org: CrmOrgDoc\n canManage: boolean\n ready: boolean\n}\n\n/** How far a rule's recompute has got, as the table shows it. */\nfunction runLabel(rule: CrmSharingRule): string {\n const run = rule.run\n if (rule.deleting) return 'Removing what it shared…'\n if (!run) return rule.enabled ? 'On' : 'Off'\n if (run.status === 'running') {\n return `${run.mode === 'remove' ? 'Removing' : 'Applying'} — ${run.processed} checked, ${run.changed} changed`\n }\n if (run.status === 'failed') return run.error ?? 'Stopped'\n return `${rule.enabled ? 'On' : 'Off'} — ${run.changed} ${run.changed === 1 ? 'record' : 'records'} changed`\n}\n\n/**\n * SHARING RULES — the organization's automated sharing (AGL-3336).\n *\n * A rule shares every record it matches with other sites: the existing ones\n * through a recompute that runs when the rule is saved, with its progress\n * on the row, and every later capture or change through the record-written\n * seam. The recompute runs in passes; the card keeps asking while the page\n * is open, and a rule whose run stopped offers Resume.\n *\n * Mounted at the organization level only: a rule is a fact about the org,\n * and its targets are the org's sites.\n */\nexport function SharingRulesCard(props: SharingRulesCardProps) {\n const { orgId, org, canManage, ready } = props\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const rules = useCrmSharingRules(org)\n const sites = useCrmSharingSites({ hostId: null, orgId, enabled: canManage })\n const roster = useOrgMemberDirectory(orgId)\n const campaigns = useCrmCampaigns({ hostId: null, orgId }, { enabled: canManage })\n const [editing, setEditing] = useState<CrmSharingRule | null>(null)\n const [drawerOpen, setDrawerOpen] = useState(false)\n const [busy, setBusy] = useState(false)\n const scope = orgId ? { orgId } : null\n\n /** One action, then `rule-continue` until the rule's run has nothing left. */\n const run = useCallback(\n async (action: CrmSharingAction, body: Record<string, unknown>, said: string) => {\n if (!scope) return\n setBusy(true)\n try {\n let answer = await callCrmSharing(user, scope, action, body)\n const ruleId = answer.rule?.id ?? String(body['ruleId'] ?? '')\n while (answer.more && ruleId) {\n answer = await callCrmSharing(user, scope, 'rule-continue', { ruleId })\n }\n enqueueSnackbar(said, { variant: 'success', persist: false })\n setDrawerOpen(false)\n } catch (error) {\n enqueueSnackbar((error as Error).message, { variant: 'error', allowDuplicate: true })\n } finally {\n setBusy(false)\n }\n },\n // `scope` is rebuilt from `orgId` every render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [user, orgId, enqueueSnackbar],\n )\n\n const save = (draft: SharingRuleDraft) =>\n void run('rule-save', { rule: draft }, draft.id ? 'Rule saved' : 'Rule added')\n\n const canEdit = ready && canManage && !busy\n const stageLabel = (id: string) =>\n CRM_LEAD_STATUS_LABELS[id as CrmLeadStatus] ??\n CONTACT_LIFECYCLE_STAGE_LABELS[id as ContactLifecycleStage] ??\n id\n return (\n <CardDisplay\n header={'Sharing rules'}\n help={pluginDocsHelp('crmSharing', { anchor: '#sharing-rules' })}\n contentGutterX\n contentGutterY\n contentBordered=\"all\"\n HeaderProps={{\n action: (\n <Button\n variant=\"contained\"\n disabled={!canEdit || rules.length >= CRM_SHARING_RULES_MAX}\n onClick={() => {\n setEditing(null)\n setDrawerOpen(true)\n }}\n >\n {'Add rule'}\n </Button>\n ),\n }}\n >\n <Stack spacing={2}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Share leads, contacts, companies or deals captured on one site with other sites, ' +\n 'automatically. A rule applies to the records you have now and to every one captured ' +\n 'or changed later. Sharing is visibility only: a site never gains a person’s consent ' +\n 'through it.'}\n </Typography>\n {busy ? <LinearProgress /> : null}\n {rules.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No sharing rules. Every record is visible only to the sites that captured it.'}\n </Typography>\n ) : (\n <ScrollTable size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{'Rule'}</TableCell>\n <TableCell>{'Which records'}</TableCell>\n <TableCell>{'Shared with'}</TableCell>\n <TableCell>{'Status'}</TableCell>\n <TableCell align=\"right\" />\n </TableRow>\n </TableHead>\n <TableBody>\n {rules.map((rule) => (\n <TableRow key={rule.id} hover>\n <TableCell>\n <Typography variant=\"body2\">{rule.name}</Typography>\n </TableCell>\n <TableCell>\n <Typography variant=\"body2\">\n {describeSharingRuleScope(rule, sites.siteName, {\n owner: roster.nameOf,\n stage: stageLabel,\n })}\n </Typography>\n </TableCell>\n <TableCell>\n <Typography variant=\"body2\">\n {`${describeShareTargets(rule.targets, sites.siteName)}, ${\n rule.access === 'edit' ? 'read and edit' : 'read-only'\n }`}\n </Typography>\n </TableCell>\n <TableCell>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {runLabel(rule)}\n </Typography>\n </TableCell>\n <TableCell align=\"right\" sx={{ width: 56 }}>\n <RowActionsMenu\n label={rule.name}\n items={[\n {\n key: 'edit',\n label: 'Edit rule',\n icon: <MdiIcon path={mdiPencilOutline.path} size={0.8} />,\n disabled: !canEdit || rule.deleting === true,\n onClick: () => {\n setEditing(rule)\n setDrawerOpen(true)\n },\n },\n ...(rule.run && rule.run.status !== 'done'\n ? [\n {\n key: 'resume',\n label: 'Resume',\n icon: <MdiIcon path={mdiRefresh.path} size={0.8} />,\n disabled: !canEdit,\n onClick: () =>\n void run('rule-continue', { ruleId: rule.id }, 'Rule applied'),\n },\n ]\n : []),\n {\n key: 'delete',\n label: 'Delete rule',\n icon: <MdiIcon path={mdiDeleteOutline.path} size={0.8} />,\n destructive: true,\n disabled: !canEdit,\n onClick: () =>\n void run('rule-delete', { ruleId: rule.id }, 'Rule deleted'),\n },\n ]}\n />\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </ScrollTable>\n )}\n {ready && !canManage ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Only a workspace owner or admin can change sharing rules.'}\n </Typography>\n ) : null}\n </Stack>\n <SharingRuleDrawer\n open={drawerOpen}\n onClose={() => setDrawerOpen(false)}\n rule={editing}\n sites={sites.sites}\n members={roster.members}\n campaigns={campaigns.options}\n busy={busy}\n onSubmit={save}\n />\n </CardDisplay>\n )\n}\nSharingRulesCard.displayName = 'SharingRulesCard'\n\nexport default SharingRulesCard\n"],"names":["CONTACT_LIFECYCLE_STAGE_LABELS","CRM_LEAD_STATUS_LABELS","pluginDocsHelp","mdiDeleteOutline","mdiPencilOutline","mdiRefresh","CardDisplay","MdiIcon","RowActionsMenu","ScrollTable","useSnackbar","useUser","Button","LinearProgress","Stack","TableBody","TableCell","TableHead","TableRow","Typography","useCallback","useState","useCrmCampaigns","useCrmSharingRules","useCrmSharingSites","useOrgMemberDirectory","CRM_SHARING_RULES_MAX","describeShareTargets","describeSharingRuleScope","callCrmSharing","SharingRuleDrawer","runLabel","rule","run","deleting","enabled","status","mode","processed","changed","error","SharingRulesCard","props","orgId","org","canManage","ready","data","user","enqueueSnackbar","rules","sites","hostId","roster","campaigns","editing","setEditing","drawerOpen","setDrawerOpen","busy","setBusy","scope","action","body","said","answer","ruleId","id","String","more","variant","persist","message","allowDuplicate","save","draft","canEdit","stageLabel","header","help","anchor","contentGutterX","contentGutterY","contentBordered","HeaderProps","disabled","length","onClick","spacing","color","size","align","map","hover","name","siteName","owner","nameOf","stage","targets","access","sx","width","label","items","key","icon","path","destructive","open","onClose","members","options","onSubmit","displayName"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  /** The routes `registerCrmConsoleApi` registers under `/api/crm/`. */
2
- export type CrmApiRoute = 'contacts-create' | 'leads-create' | 'contact-email-history' | 'contact-update' | 'company-delete' | 'contacts-merge' | 'email-send' | 'email-check' | 'erase-person' | 'org-activity' | 'recipe-install' | 'recipe-status' | 'inbound-address' | 'email-template-duplicate';
2
+ export type CrmApiRoute = 'contacts-create' | 'leads-create' | 'contact-email-history' | 'contact-update' | 'contact-remove' | 'company-delete' | 'contacts-merge' | 'email-send' | 'email-check' | 'erase-person' | 'org-activity' | 'recipe-install' | 'recipe-status' | 'inbound-address' | 'email-template-duplicate';
3
3
  /** What one call to the CRM API answered with. */
4
4
  export interface CrmApiResult {
5
5
  response: Response;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/use-crm-api.ts"],"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 authorizedFetch,\n type TokenSource,\n} from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport { useCallback, useRef } from 'react'\nimport { useCrmOrgMount } from '../hooks/use-crm-org-mount'\n\n/** The routes `registerCrmConsoleApi` registers under `/api/crm/`. */\nexport type CrmApiRoute =\n | 'contacts-create'\n | 'leads-create'\n | 'contact-email-history'\n | 'contact-update'\n | 'company-delete'\n | 'contacts-merge'\n | 'email-send'\n | 'email-check'\n | 'erase-person'\n | 'org-activity'\n | 'recipe-install'\n | 'recipe-status'\n | 'inbound-address'\n | 'email-template-duplicate'\n\n/** What one call to the CRM API answered with. */\nexport interface CrmApiResult {\n response: Response\n payload: Record<string, any>\n}\n\n/**\n * ONE AUTHORIZED POST TO THE CRM's SERVER HALF (AGL-2596).\n *\n * The route is a fixed member of {@link CrmApiRoute} rather than a string\n * the caller composes, so a component cannot post the console's credentials\n * anywhere this plugin does not own. The site id rides in the body because\n * that is where the dispatcher's per-site gate reads it from.\n *\n * The user is read through a ref, for the reason the campaign API hook\n * gives: the user object is a new object on most renders, and a callback\n * that changed identity with it would re-fire every effect that depends on\n * it. The token is fetched at call time either way — `authorizedFetch`\n * resolves it under a deadline and answers a 401 of its own rather than\n * sending the request unauthenticated, so a signed-out caller is told so\n * through the same `response.ok` branch every other refusal takes.\n */\n/**\n * ## The organization level\n *\n * Beneath the org hub's mount (AGL-2630) the body also names the ORG, and\n * that is what makes the call the route's org variant (AGL-2634): the route\n * authorizes the caller by the org rather than by a site's role, and a\n * `hostId` beside it — the record's own capturing site, or `null` for a\n * record no site has captured — is what the act needs a site FOR, never\n * what it is authorized against. Under a site there is no mount and the\n * body is what it always was.\n */\nexport function useCrmApi(\n /**\n * The site the request is made for. At the organization level the\n * record's own site, or `null` for a record no site has captured — the\n * org variant of a route serves both, and a route with no org variant\n * (a create, which is always captured BY a site) refuses the null as it\n * always has.\n */\n hostId: string | null,\n) {\n const { data: user } = useUser()\n const userRef = useRef(user)\n userRef.current = user\n const orgId = useCrmOrgMount()?.orgId ?? null\n return useCallback(\n async (\n route: CrmApiRoute,\n payload: Record<string, unknown>,\n ): Promise<CrmApiResult> => {\n const response = await authorizedFetch(\n userRef.current as TokenSource | null | undefined,\n `/api/crm/${route}`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n hostId,\n ...(orgId ? { orgId } : {}),\n ...payload,\n }),\n },\n )\n const json = await response.json().catch(() => ({}))\n return { response, payload: json as Record<string, any> }\n },\n [hostId, orgId],\n )\n}\n\nexport default useCrmApi\n"],"names":["authorizedFetch","useUser","useCallback","useRef","useCrmOrgMount","useCrmApi","hostId","data","user","userRef","current","orgId","route","payload","response","method","headers","body","JSON","stringify","json","catch"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SACEA,eAAe,QAEV,2CAA0C;AACjD,SAASC,OAAO,QAAQ,iCAAgC;AACxD,SAASC,WAAW,EAAEC,MAAM,QAAQ,QAAO;AAC3C,SAASC,cAAc,QAAQ,gCAA4B;AAyB3D;;;;;;;;;;;;;;;CAeC,GACD;;;;;;;;;;CAUC,GACD,OAAO,SAASC,UACd;;;;;;GAMC,GACDC,MAAqB;;QAKPF;IAHd,MAAM,EAAEG,MAAMC,IAAI,EAAE,GAAGP;IACvB,MAAMQ,UAAUN,OAAOK;IACvBC,QAAQC,OAAO,GAAGF;IAClB,MAAMG,iBAAQP,kBAAAA,qCAAAA,gBAAkBO,KAAK,mBAAI;IACzC,OAAOT,YACL,OACEU,OACAC;QAEA,MAAMC,WAAW,MAAMd,gBACrBS,QAAQC,OAAO,EACf,CAAC,SAAS,EAAEE,OAAO,EACnB;YACEG,QAAQ;YACRC,SAAS;gBAAE,gBAAgB;YAAmB;YAC9CC,MAAMC,KAAKC,SAAS,CAAC;gBACnBb;eACIK,QAAQ;gBAAEA;YAAM,IAAI,CAAC,GACtBE;QAEP;QAEF,MAAMO,OAAO,MAAMN,SAASM,IAAI,GAAGC,KAAK,CAAC,IAAO,CAAA,CAAC,CAAA;QACjD,OAAO;YAAEP;YAAUD,SAASO;QAA4B;IAC1D,GACA;QAACd;QAAQK;KAAM;AAEnB;AAEA,eAAeN,UAAS"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/use-crm-api.ts"],"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 authorizedFetch,\n type TokenSource,\n} from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport { useCallback, useRef } from 'react'\nimport { useCrmOrgMount } from '../hooks/use-crm-org-mount'\n\n/** The routes `registerCrmConsoleApi` registers under `/api/crm/`. */\nexport type CrmApiRoute =\n | 'contacts-create'\n | 'leads-create'\n | 'contact-email-history'\n | 'contact-update'\n | 'contact-remove'\n | 'company-delete'\n | 'contacts-merge'\n | 'email-send'\n | 'email-check'\n | 'erase-person'\n | 'org-activity'\n | 'recipe-install'\n | 'recipe-status'\n | 'inbound-address'\n | 'email-template-duplicate'\n\n/** What one call to the CRM API answered with. */\nexport interface CrmApiResult {\n response: Response\n payload: Record<string, any>\n}\n\n/**\n * ONE AUTHORIZED POST TO THE CRM's SERVER HALF (AGL-2596).\n *\n * The route is a fixed member of {@link CrmApiRoute} rather than a string\n * the caller composes, so a component cannot post the console's credentials\n * anywhere this plugin does not own. The site id rides in the body because\n * that is where the dispatcher's per-site gate reads it from.\n *\n * The user is read through a ref, for the reason the campaign API hook\n * gives: the user object is a new object on most renders, and a callback\n * that changed identity with it would re-fire every effect that depends on\n * it. The token is fetched at call time either way — `authorizedFetch`\n * resolves it under a deadline and answers a 401 of its own rather than\n * sending the request unauthenticated, so a signed-out caller is told so\n * through the same `response.ok` branch every other refusal takes.\n */\n/**\n * ## The organization level\n *\n * Beneath the org hub's mount (AGL-2630) the body also names the ORG, and\n * that is what makes the call the route's org variant (AGL-2634): the route\n * authorizes the caller by the org rather than by a site's role, and a\n * `hostId` beside it — the record's own capturing site, or `null` for a\n * record no site has captured — is what the act needs a site FOR, never\n * what it is authorized against. Under a site there is no mount and the\n * body is what it always was.\n */\nexport function useCrmApi(\n /**\n * The site the request is made for. At the organization level the\n * record's own site, or `null` for a record no site has captured — the\n * org variant of a route serves both, and a route with no org variant\n * (a create, which is always captured BY a site) refuses the null as it\n * always has.\n */\n hostId: string | null,\n) {\n const { data: user } = useUser()\n const userRef = useRef(user)\n userRef.current = user\n const orgId = useCrmOrgMount()?.orgId ?? null\n return useCallback(\n async (\n route: CrmApiRoute,\n payload: Record<string, unknown>,\n ): Promise<CrmApiResult> => {\n const response = await authorizedFetch(\n userRef.current as TokenSource | null | undefined,\n `/api/crm/${route}`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n hostId,\n ...(orgId ? { orgId } : {}),\n ...payload,\n }),\n },\n )\n const json = await response.json().catch(() => ({}))\n return { response, payload: json as Record<string, any> }\n },\n [hostId, orgId],\n )\n}\n\nexport default useCrmApi\n"],"names":["authorizedFetch","useUser","useCallback","useRef","useCrmOrgMount","useCrmApi","hostId","data","user","userRef","current","orgId","route","payload","response","method","headers","body","JSON","stringify","json","catch"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SACEA,eAAe,QAEV,2CAA0C;AACjD,SAASC,OAAO,QAAQ,iCAAgC;AACxD,SAASC,WAAW,EAAEC,MAAM,QAAQ,QAAO;AAC3C,SAASC,cAAc,QAAQ,gCAA4B;AA0B3D;;;;;;;;;;;;;;;CAeC,GACD;;;;;;;;;;CAUC,GACD,OAAO,SAASC,UACd;;;;;;GAMC,GACDC,MAAqB;;QAKPF;IAHd,MAAM,EAAEG,MAAMC,IAAI,EAAE,GAAGP;IACvB,MAAMQ,UAAUN,OAAOK;IACvBC,QAAQC,OAAO,GAAGF;IAClB,MAAMG,iBAAQP,kBAAAA,qCAAAA,gBAAkBO,KAAK,mBAAI;IACzC,OAAOT,YACL,OACEU,OACAC;QAEA,MAAMC,WAAW,MAAMd,gBACrBS,QAAQC,OAAO,EACf,CAAC,SAAS,EAAEE,OAAO,EACnB;YACEG,QAAQ;YACRC,SAAS;gBAAE,gBAAgB;YAAmB;YAC9CC,MAAMC,KAAKC,SAAS,CAAC;gBACnBb;eACIK,QAAQ;gBAAEA;YAAM,IAAI,CAAC,GACtBE;QAEP;QAEF,MAAMO,OAAO,MAAMN,SAASM,IAAI,GAAGC,KAAK,CAAC,IAAO,CAAA,CAAC,CAAA;QACjD,OAAO;YAAEP;YAAUD,SAASO;QAA4B;IAC1D,GACA;QAACd;QAAQK;KAAM;AAEnB;AAEA,eAAeN,UAAS"}
@@ -0,0 +1,25 @@
1
+ import type { PluginRecordRouteContext } from '@aglyn/aglyn/plugin-manager/plugin-record-routes';
2
+ /**
3
+ * The SITE a CRM surface asks the record-route registry about — the
4
+ * organization's slug and the site's subdomain — or `null` when it cannot
5
+ * name one (AGL-3080).
6
+ *
7
+ * What a CRM record page links to on another plugin's page is a site's: the
8
+ * report of an email a site sent, the Sending section that fixes a site's
9
+ * missing identity. The address itself is the owner's to publish
10
+ * (`pluginRecordHref('emailMessage', …)`), and this names the site to ask it
11
+ * for. Read off the URL rather than resolved, because the `[host]` segment is
12
+ * the site's subdomain and nothing a CRM component holds says what that is.
13
+ *
14
+ * ⚠️ Under a site, THIS SITE only. A record page under one site cannot
15
+ * address a sibling site's pages without that site's subdomain, which a
16
+ * contact document does not carry.
17
+ *
18
+ * At the ORGANIZATION level (AGL-2634) the URL names no site, and the mount
19
+ * holds the org's site list instead: handed a `hostId`, this answers that
20
+ * site — or `null` for a site whose subdomain the list could not answer,
21
+ * which is named and not linked. Handed none, `null`: an org-level page has
22
+ * no site of its own to fall back to.
23
+ */
24
+ export declare function useSiteRouteContext(hostId?: string | null): PluginRecordRouteContext | null;
25
+ export default useSiteRouteContext;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ 'use client';
17
+ import { useParams } from "next/navigation";
18
+ import { useCrmOrgMount } from "../hooks/use-crm-org-mount.js";
19
+ /**
20
+ * The SITE a CRM surface asks the record-route registry about — the
21
+ * organization's slug and the site's subdomain — or `null` when it cannot
22
+ * name one (AGL-3080).
23
+ *
24
+ * What a CRM record page links to on another plugin's page is a site's: the
25
+ * report of an email a site sent, the Sending section that fixes a site's
26
+ * missing identity. The address itself is the owner's to publish
27
+ * (`pluginRecordHref('emailMessage', …)`), and this names the site to ask it
28
+ * for. Read off the URL rather than resolved, because the `[host]` segment is
29
+ * the site's subdomain and nothing a CRM component holds says what that is.
30
+ *
31
+ * ⚠️ Under a site, THIS SITE only. A record page under one site cannot
32
+ * address a sibling site's pages without that site's subdomain, which a
33
+ * contact document does not carry.
34
+ *
35
+ * At the ORGANIZATION level (AGL-2634) the URL names no site, and the mount
36
+ * holds the org's site list instead: handed a `hostId`, this answers that
37
+ * site — or `null` for a site whose subdomain the list could not answer,
38
+ * which is named and not linked. Handed none, `null`: an org-level page has
39
+ * no site of its own to fall back to.
40
+ */ export function useSiteRouteContext(hostId) {
41
+ const params = useParams();
42
+ const mount = useCrmOrgMount();
43
+ const orgSlug = params == null ? void 0 : params.orgSlug;
44
+ const host = params == null ? void 0 : params.host;
45
+ if (orgSlug && host) return {
46
+ orgSlug,
47
+ host
48
+ };
49
+ if (!mount || !hostId) return null;
50
+ const subdomain = mount.siteSubdomain(hostId);
51
+ return subdomain ? {
52
+ orgSlug: mount.orgSlug,
53
+ host: subdomain
54
+ } : null;
55
+ }
56
+ export default useSiteRouteContext;
57
+
58
+ //# sourceMappingURL=use-site-route-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/use-site-route-context.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport type { PluginRecordRouteContext } from '@aglyn/aglyn/plugin-manager/plugin-record-routes'\nimport { useParams } from 'next/navigation'\nimport { useCrmOrgMount } from '../hooks/use-crm-org-mount'\n\n/**\n * The SITE a CRM surface asks the record-route registry about — the\n * organization's slug and the site's subdomain — or `null` when it cannot\n * name one (AGL-3080).\n *\n * What a CRM record page links to on another plugin's page is a site's: the\n * report of an email a site sent, the Sending section that fixes a site's\n * missing identity. The address itself is the owner's to publish\n * (`pluginRecordHref('emailMessage', …)`), and this names the site to ask it\n * for. Read off the URL rather than resolved, because the `[host]` segment is\n * the site's subdomain and nothing a CRM component holds says what that is.\n *\n * ⚠️ Under a site, THIS SITE only. A record page under one site cannot\n * address a sibling site's pages without that site's subdomain, which a\n * contact document does not carry.\n *\n * At the ORGANIZATION level (AGL-2634) the URL names no site, and the mount\n * holds the org's site list instead: handed a `hostId`, this answers that\n * site — or `null` for a site whose subdomain the list could not answer,\n * which is named and not linked. Handed none, `null`: an org-level page has\n * no site of its own to fall back to.\n */\nexport function useSiteRouteContext(hostId?: string | null): PluginRecordRouteContext | null {\n const params = useParams<{ orgSlug: string; host: string }>()\n const mount = useCrmOrgMount()\n const orgSlug = params?.orgSlug\n const host = params?.host\n if (orgSlug && host) return { orgSlug, host }\n if (!mount || !hostId) return null\n const subdomain = mount.siteSubdomain(hostId)\n return subdomain ? { orgSlug: mount.orgSlug, host: subdomain } : null\n}\n\nexport default useSiteRouteContext\n"],"names":["useParams","useCrmOrgMount","useSiteRouteContext","hostId","params","mount","orgSlug","host","subdomain","siteSubdomain"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;AAGA,SAASA,SAAS,QAAQ,kBAAiB;AAC3C,SAASC,cAAc,QAAQ,gCAA4B;AAE3D;;;;;;;;;;;;;;;;;;;;;CAqBC,GACD,OAAO,SAASC,oBAAoBC,MAAsB;IACxD,MAAMC,SAASJ;IACf,MAAMK,QAAQJ;IACd,MAAMK,UAAUF,0BAAAA,OAAQE,OAAO;IAC/B,MAAMC,OAAOH,0BAAAA,OAAQG,IAAI;IACzB,IAAID,WAAWC,MAAM,OAAO;QAAED;QAASC;IAAK;IAC5C,IAAI,CAACF,SAAS,CAACF,QAAQ,OAAO;IAC9B,MAAMK,YAAYH,MAAMI,aAAa,CAACN;IACtC,OAAOK,YAAY;QAAEF,SAASD,MAAMC,OAAO;QAAEC,MAAMC;IAAU,IAAI;AACnE;AAEA,eAAeN,oBAAmB"}
@@ -34,6 +34,12 @@ export declare const CRM_API_ROUTES: {
34
34
  * every facet write the console makes; see `server/contact-update.ts`.
35
35
  */
36
36
  readonly contactUpdate: "crm/contact-update";
37
+ /**
38
+ * `POST` — lets one or more contacts go from a site, or deletes them once
39
+ * nobody else holds them, keeping every marketing refusal they held
40
+ * (AGL-3338); see `server/contact-remove.ts`.
41
+ */
42
+ readonly contactRemove: "crm/contact-remove";
37
43
  /**
38
44
  * `POST` — unlinks the contacts that name a company, then deletes it
39
45
  * (AGL-2804); see `server/company-delete.ts`.
@@ -64,6 +70,31 @@ export declare const CRM_API_ROUTES: {
64
70
  * `server/inbound-address.ts`.
65
71
  */
66
72
  readonly inboundAddress: "crm/inbound-address";
73
+ /**
74
+ * `POST` (a run) or `GET` (the plan) — the daily digest, posted by the
75
+ * scheduler with the cron secret (AGL-2619); a machine's route, see
76
+ * `server/daily-digest-route.ts`.
77
+ */
78
+ readonly dailyDigest: "crm/daily-digest";
79
+ /**
80
+ * `POST` (a run) or `GET` (the plan) — the hourly task reminders, posted
81
+ * by the scheduler with the cron secret (AGL-2659); a machine's route, see
82
+ * `server/task-reminders-route.ts`.
83
+ */
84
+ readonly taskReminders: "crm/task-reminders";
85
+ /**
86
+ * `POST` — the mail provider's signed notice of a message received at a
87
+ * capture address (AGL-2657); a machine's route, see
88
+ * `server/inbound-route.ts`.
89
+ */
90
+ readonly inbound: "crm/inbound";
91
+ /**
92
+ * `GET` — one of the organization's CRM collections, or a site's leads,
93
+ * as a CSV streamed over every row (AGL-2662); the contacts and leads
94
+ * files are every workspace's (a portability route), see
95
+ * `server/export-route.ts`.
96
+ */
97
+ readonly export: "crm/export";
67
98
  };
68
99
  /**
69
100
  * One site's answer to `crm/recipe-status` (AGL-2639).