@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/pipeline-stages-dialog.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n CRM_COLLECTIONS,\n CRM_FORECAST_CATEGORIES,\n CRM_FORECAST_CATEGORY_LABELS,\n type CrmDealStage,\n dealStageForecastCategory,\n} from '@aglyn/aglyn'\nimport {\n mdiArrowDown,\n mdiArrowUp,\n mdiDeleteOutline,\n} from '@aglyn/shared-data-mdi'\nimport { MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { useFirestore, writeGuardedBySeed } from '@aglyn/tenant-feature-instance'\nimport {\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n IconButton,\n MenuItem,\n Stack,\n TextField,\n Tooltip,\n Typography,\n} from '@mui/material'\nimport {\n collection,\n doc,\n getCountFromServer,\n query,\n updateDoc,\n where,\n} from 'firebase/firestore'\nimport { useCallback, useEffect, useState } from 'react'\nimport { crmVisibleToClause } from '../hooks/use-crm-scope'\nimport {\n addStage,\n moveStage,\n type PipelineDoc,\n removeStage,\n renameStage,\n setStageForecastCategory,\n setStageProbability,\n sortedStages,\n stageRemovalRefusal,\n stagesProblem,\n} from '../model/deal-board-model'\n\nexport interface PipelineStagesDialogProps {\n open: boolean\n onClose: () => void\n orgId: string\n pipeline: PipelineDoc | null\n /** The listener's verdict on the pipeline row the draft is seeded from. */\n fromCache: boolean\n unreadable: boolean\n /** The scope tokens the deal count is filtered by — the viewer's own. */\n /** The reader's tokens, or `null` at the organization level — no clause (AGL-2630). */\n visibleToTokens: readonly string[] | null\n}\n\n/**\n * The pipeline's stages, edited as one draft and saved as one write.\n *\n * ## One document, one save\n *\n * The stages live as an array on the pipeline document, so every edit here\n * is a local change to a copy and the Save button writes the whole array\n * back. A per-row write would be a pipeline that is half renamed when the\n * network drops, and — because the board draws columns from this array —\n * a board that redraws between every keystroke for everybody looking at it.\n *\n * ## A stage with deals in it stays\n *\n * Removing a stage is the one edit that can strand data: every deal in it\n * would point at a stage id the pipeline no longer has, sit in no column,\n * and be worth nothing to the forecast. So the remove button first asks the\n * server how many deals are in the stage — an aggregate count over the same\n * scoped query the board reads — and refuses with that number when it is\n * not zero. The count is taken at the moment of the click rather than held\n * from when the dialog opened, because a deal can be dragged in from the\n * board in another tab meanwhile.\n *\n * Won and Lost cannot be removed or reordered; their probabilities are\n * fixed by what they mean.\n *\n * ## The forecast category (AGL-3516)\n *\n * Each open stage names the forecast category a deal takes on landing in\n * it — Salesforce's fixed set, Pipeline unless chosen. Won is Closed and\n * Lost is Omitted by definition. A change here applies to the deals that\n * move into the stage from now on; a deal already in it keeps its own.\n */\nexport function PipelineStagesDialog(props: PipelineStagesDialogProps) {\n const { open, onClose, orgId, pipeline, fromCache, unreadable, visibleToTokens } =\n props\n const firestore = useFirestore()\n const { enqueueSnackbar } = useSnackbar()\n const [stages, setStages] = useState<CrmDealStage[]>([])\n const [newName, setNewName] = useState('')\n const [busy, setBusy] = useState(false)\n const [counting, setCounting] = useState<string | null>(null)\n\n // Re-seeded on every open, so the draft starts from the stored stages\n // rather than from whatever the last session left unsaved.\n useEffect(() => {\n if (!open) return\n setStages(sortedStages(pipeline))\n setNewName('')\n }, [open, pipeline])\n\n const handleRemove = useCallback(\n async (stageId: string) => {\n if (!pipeline) return\n setCounting(stageId)\n try {\n const snapshot = await getCountFromServer(\n query(\n collection(firestore, 'orgs', orgId, CRM_COLLECTIONS.deals),\n ...crmVisibleToClause(visibleToTokens),\n where('pipelineId', '==', pipeline.$id),\n where('stageId', '==', stageId),\n ),\n )\n const refusal = stageRemovalRefusal(stages, stageId, snapshot.data().count)\n if (refusal) {\n enqueueSnackbar(refusal, { variant: 'warning', persist: false })\n return\n }\n setStages((current) => removeStage(current, stageId))\n } catch (error) {\n console.error(error)\n enqueueSnackbar('The deals in this stage could not be counted, so it stays.', {\n variant: 'error',\n allowDuplicate: true,\n })\n } finally {\n setCounting(null)\n }\n },\n [firestore, orgId, pipeline, visibleToTokens, stages, enqueueSnackbar],\n )\n\n const problem = stagesProblem(stages)\n\n const handleSave = useCallback(async () => {\n if (!pipeline || problem) return\n setBusy(true)\n try {\n const verdict = await writeGuardedBySeed(\n { subject: 'pipeline', unreadable, fromCache },\n async () => {\n await updateDoc(\n doc(firestore, 'orgs', orgId, CRM_COLLECTIONS.pipelines, pipeline.$id),\n { stages, updatedAt: new Date() },\n )\n },\n )\n if (!verdict.ok) {\n enqueueSnackbar(verdict.message, { variant: 'warning', persist: false })\n return\n }\n enqueueSnackbar('Stages saved', { variant: 'success', persist: false })\n onClose()\n } catch (error) {\n console.error(error)\n enqueueSnackbar('An error has occurred', {\n variant: 'error',\n allowDuplicate: true,\n })\n } finally {\n setBusy(false)\n }\n }, [firestore, orgId, pipeline, problem, stages, unreadable, fromCache, enqueueSnackbar, onClose])\n\n const openCount = stages.filter((stage) => stage.kind === 'open').length\n\n return (\n <Dialog open={open} onClose={busy ? undefined : onClose} fullWidth maxWidth=\"sm\">\n <DialogTitle>{pipeline ? `${pipeline.name} stages` : 'Stages'}</DialogTitle>\n <DialogContent>\n <Stack spacing={2} sx={{ pt: 1 }}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'The probability is the chance a deal in that stage closes, which ' +\n 'is what the weighted value multiplies by. Won is always 100% and ' +\n 'Lost always 0%. The forecast category is where a deal moved into ' +\n 'the stage is forecast; Won is always Closed and Lost always Omitted.'}\n </Typography>\n {stages.map((stage, index) => {\n const isOpen = stage.kind === 'open'\n const first = index === 0\n const lastOpen = index === openCount - 1\n return (\n <Stack\n key={stage.id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n <TextField\n size=\"small\"\n label=\"Stage\"\n value={stage.name}\n onChange={(event) =>\n setStages((current) =>\n renameStage(current, stage.id, event.target.value),\n )\n }\n sx={{ flex: 1 }}\n slotProps={{ htmlInput: { maxLength: 60 } }}\n />\n <TextField\n size=\"small\"\n label=\"Probability\"\n type=\"number\"\n value={stage.probability}\n disabled={!isOpen}\n onChange={(event) =>\n setStages((current) =>\n setStageProbability(current, stage.id, event.target.value),\n )\n }\n sx={{ width: 120 }}\n slotProps={{\n htmlInput: { min: 0, max: 100, step: 5 },\n input: { endAdornment: '%' },\n }}\n />\n <TextField\n select\n size=\"small\"\n label=\"Forecast\"\n value={dealStageForecastCategory(stage)}\n disabled={!isOpen}\n onChange={(event) =>\n setStages((current) =>\n setStageForecastCategory(current, stage.id, event.target.value),\n )\n }\n sx={{ width: 140 }}\n >\n {CRM_FORECAST_CATEGORIES.map((category) => (\n <MenuItem key={category} value={category}>\n {CRM_FORECAST_CATEGORY_LABELS[category]}\n </MenuItem>\n ))}\n </TextField>\n {isOpen ? (\n <>\n <Tooltip title=\"Move up\">\n <span>\n <IconButton\n size=\"small\"\n disabled={first}\n onClick={() =>\n setStages((current) => moveStage(current, stage.id, 'up'))\n }\n >\n <MdiIcon path={mdiArrowUp.path} size={0.8} />\n </IconButton>\n </span>\n </Tooltip>\n <Tooltip title=\"Move down\">\n <span>\n <IconButton\n size=\"small\"\n disabled={lastOpen}\n onClick={() =>\n setStages((current) => moveStage(current, stage.id, 'down'))\n }\n >\n <MdiIcon path={mdiArrowDown.path} size={0.8} />\n </IconButton>\n </span>\n </Tooltip>\n <Tooltip title=\"Remove stage\">\n <span>\n <IconButton\n size=\"small\"\n color=\"error\"\n disabled={counting === stage.id || busy}\n onClick={() => void handleRemove(stage.id)}\n >\n <MdiIcon path={mdiDeleteOutline.path} size={0.8} />\n </IconButton>\n </span>\n </Tooltip>\n </>\n ) : (\n <Chip\n size=\"small\"\n label={stage.kind === 'won' ? 'Won' : 'Lost'}\n color={stage.kind === 'won' ? 'success' : 'default'}\n sx={{ minWidth: 120 }}\n />\n )}\n </Stack>\n )\n })}\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <TextField\n size=\"small\"\n label=\"New stage\"\n value={newName}\n onChange={(event) => setNewName(event.target.value)}\n onKeyDown={(event) => {\n if (event.key !== 'Enter' || !newName.trim()) return\n event.preventDefault()\n setStages((current) => addStage(current, newName))\n setNewName('')\n }}\n sx={{ flex: 1 }}\n slotProps={{ htmlInput: { maxLength: 60 } }}\n />\n <Button\n disabled={!newName.trim()}\n onClick={() => {\n setStages((current) => addStage(current, newName))\n setNewName('')\n }}\n >\n {'Add stage'}\n </Button>\n </Stack>\n {problem ? (\n <Typography variant=\"caption\" color=\"error\">\n {problem}\n </Typography>\n ) : null}\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} disabled={busy}>\n {'Cancel'}\n </Button>\n <Button\n variant=\"contained\"\n disabled={busy || !pipeline || Boolean(problem)}\n onClick={() => void handleSave()}\n >\n {'Save stages'}\n </Button>\n </DialogActions>\n </Dialog>\n )\n}\nPipelineStagesDialog.displayName = 'PipelineStagesDialog'\n\nexport default PipelineStagesDialog\n"],"names":["CRM_COLLECTIONS","CRM_FORECAST_CATEGORIES","CRM_FORECAST_CATEGORY_LABELS","dealStageForecastCategory","mdiArrowDown","mdiArrowUp","mdiDeleteOutline","MdiIcon","useSnackbar","useFirestore","writeGuardedBySeed","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","IconButton","MenuItem","Stack","TextField","Tooltip","Typography","collection","doc","getCountFromServer","query","updateDoc","where","useCallback","useEffect","useState","crmVisibleToClause","addStage","moveStage","removeStage","renameStage","setStageForecastCategory","setStageProbability","sortedStages","stageRemovalRefusal","stagesProblem","PipelineStagesDialog","props","open","onClose","orgId","pipeline","fromCache","unreadable","visibleToTokens","firestore","enqueueSnackbar","stages","setStages","newName","setNewName","busy","setBusy","counting","setCounting","handleRemove","stageId","snapshot","deals","$id","refusal","data","count","variant","persist","current","error","console","allowDuplicate","problem","handleSave","verdict","subject","pipelines","updatedAt","Date","ok","message","openCount","filter","stage","kind","length","undefined","fullWidth","maxWidth","name","spacing","sx","pt","color","map","index","isOpen","first","lastOpen","direction","alignItems","size","label","value","onChange","event","id","target","flex","slotProps","htmlInput","maxLength","type","probability","disabled","width","min","max","step","input","endAdornment","select","category","title","span","onClick","path","minWidth","onKeyDown","key","trim","preventDefault","Boolean","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/pipeline-stages-dialog.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n CRM_COLLECTIONS,\n CRM_FORECAST_CATEGORIES,\n CRM_FORECAST_CATEGORY_LABELS,\n type CrmDealStage,\n dealStageForecastCategory,\n} from '@aglyn/aglyn'\nimport {\n mdiArrowDown,\n mdiArrowUp,\n mdiDeleteOutline,\n} from '@aglyn/shared-data-mdi'\nimport { MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { useFirestore, writeGuardedBySeed } from '@aglyn/tenant-feature-instance'\nimport {\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n IconButton,\n MenuItem,\n Stack,\n TextField,\n Tooltip,\n Typography,\n} from '@mui/material'\nimport {\n collection,\n doc,\n getCountFromServer,\n query,\n updateDoc,\n where,\n} from 'firebase/firestore'\nimport { useCallback, useEffect, useState } from 'react'\nimport { crmVisibleToClause } from '../hooks/use-crm-scope'\nimport {\n addStage,\n moveStage,\n type PipelineDoc,\n removeStage,\n renameStage,\n setStageForecastCategory,\n setStageProbability,\n sortedStages,\n stageRemovalRefusal,\n stagesProblem,\n} from '../model/deal-board-model'\nimport { settleStageNames } from '../model/settle-stage-names'\n\nexport interface PipelineStagesDialogProps {\n open: boolean\n onClose: () => void\n orgId: string\n pipeline: PipelineDoc | null\n /** The listener's verdict on the pipeline row the draft is seeded from. */\n fromCache: boolean\n unreadable: boolean\n /** The scope tokens the deal count is filtered by — the viewer's own. */\n /** The reader's tokens, or `null` at the organization level — no clause (AGL-2630). */\n visibleToTokens: readonly string[] | null\n}\n\n/**\n * The pipeline's stages, edited as one draft and saved as one write.\n *\n * ## One document, one save\n *\n * The stages live as an array on the pipeline document, so every edit here\n * is a local change to a copy and the Save button writes the whole array\n * back. A per-row write would be a pipeline that is half renamed when the\n * network drops, and — because the board draws columns from this array —\n * a board that redraws between every keystroke for everybody looking at it.\n *\n * ## A stage with deals in it stays\n *\n * Removing a stage is the one edit that can strand data: every deal in it\n * would point at a stage id the pipeline no longer has, sit in no column,\n * and be worth nothing to the forecast. So the remove button first asks the\n * server how many deals are in the stage — an aggregate count over the same\n * scoped query the board reads — and refuses with that number when it is\n * not zero. The count is taken at the moment of the click rather than held\n * from when the dialog opened, because a deal can be dragged in from the\n * board in another tab meanwhile.\n *\n * Won and Lost cannot be removed or reordered; their probabilities are\n * fixed by what they mean.\n *\n * ## The forecast category (AGL-3516)\n *\n * Each open stage names the forecast category a deal takes on landing in\n * it — Salesforce's fixed set, Pipeline unless chosen. Won is Closed and\n * Lost is Omitted by definition. A change here applies to the deals that\n * move into the stage from now on; a deal already in it keeps its own.\n */\nexport function PipelineStagesDialog(props: PipelineStagesDialogProps) {\n const { open, onClose, orgId, pipeline, fromCache, unreadable, visibleToTokens } =\n props\n const firestore = useFirestore()\n const { enqueueSnackbar } = useSnackbar()\n const [stages, setStages] = useState<CrmDealStage[]>([])\n const [newName, setNewName] = useState('')\n const [busy, setBusy] = useState(false)\n const [counting, setCounting] = useState<string | null>(null)\n\n // Re-seeded on every open, so the draft starts from the stored stages\n // rather than from whatever the last session left unsaved.\n useEffect(() => {\n if (!open) return\n setStages(sortedStages(pipeline))\n setNewName('')\n }, [open, pipeline])\n\n const handleRemove = useCallback(\n async (stageId: string) => {\n if (!pipeline) return\n setCounting(stageId)\n try {\n const snapshot = await getCountFromServer(\n query(\n collection(firestore, 'orgs', orgId, CRM_COLLECTIONS.deals),\n ...crmVisibleToClause(visibleToTokens),\n where('pipelineId', '==', pipeline.$id),\n where('stageId', '==', stageId),\n ),\n )\n const refusal = stageRemovalRefusal(stages, stageId, snapshot.data().count)\n if (refusal) {\n enqueueSnackbar(refusal, { variant: 'warning', persist: false })\n return\n }\n setStages((current) => removeStage(current, stageId))\n } catch (error) {\n console.error(error)\n enqueueSnackbar('The deals in this stage could not be counted, so it stays.', {\n variant: 'error',\n allowDuplicate: true,\n })\n } finally {\n setCounting(null)\n }\n },\n [firestore, orgId, pipeline, visibleToTokens, stages, enqueueSnackbar],\n )\n\n const problem = stagesProblem(stages)\n\n const handleSave = useCallback(async () => {\n if (!pipeline || problem) return\n setBusy(true)\n try {\n const verdict = await writeGuardedBySeed(\n { subject: 'pipeline', unreadable, fromCache },\n async () => {\n await updateDoc(\n doc(firestore, 'orgs', orgId, CRM_COLLECTIONS.pipelines, pipeline.$id),\n { stages: settleStageNames(stages), updatedAt: new Date() },\n )\n },\n )\n if (!verdict.ok) {\n enqueueSnackbar(verdict.message, { variant: 'warning', persist: false })\n return\n }\n enqueueSnackbar('Stages saved', { variant: 'success', persist: false })\n onClose()\n } catch (error) {\n console.error(error)\n enqueueSnackbar('An error has occurred', {\n variant: 'error',\n allowDuplicate: true,\n })\n } finally {\n setBusy(false)\n }\n }, [firestore, orgId, pipeline, problem, stages, unreadable, fromCache, enqueueSnackbar, onClose])\n\n const openCount = stages.filter((stage) => stage.kind === 'open').length\n\n return (\n <Dialog open={open} onClose={busy ? undefined : onClose} fullWidth maxWidth=\"sm\">\n <DialogTitle>{pipeline ? `${pipeline.name} stages` : 'Stages'}</DialogTitle>\n <DialogContent>\n <Stack spacing={2} sx={{ pt: 1 }}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'The probability is the chance a deal in that stage closes, which ' +\n 'is what the weighted value multiplies by. Won is always 100% and ' +\n 'Lost always 0%. The forecast category is where a deal moved into ' +\n 'the stage is forecast; Won is always Closed and Lost always Omitted.'}\n </Typography>\n {stages.map((stage, index) => {\n const isOpen = stage.kind === 'open'\n const first = index === 0\n const lastOpen = index === openCount - 1\n return (\n <Stack\n key={stage.id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n <TextField\n size=\"small\"\n label=\"Stage\"\n value={stage.name}\n onChange={(event) =>\n setStages((current) =>\n renameStage(current, stage.id, event.target.value),\n )\n }\n sx={{ flex: 1 }}\n slotProps={{ htmlInput: { maxLength: 60 } }}\n />\n <TextField\n size=\"small\"\n label=\"Probability\"\n type=\"number\"\n value={stage.probability}\n disabled={!isOpen}\n onChange={(event) =>\n setStages((current) =>\n setStageProbability(current, stage.id, event.target.value),\n )\n }\n sx={{ width: 120 }}\n slotProps={{\n htmlInput: { min: 0, max: 100, step: 5 },\n input: { endAdornment: '%' },\n }}\n />\n <TextField\n select\n size=\"small\"\n label=\"Forecast\"\n value={dealStageForecastCategory(stage)}\n disabled={!isOpen}\n onChange={(event) =>\n setStages((current) =>\n setStageForecastCategory(current, stage.id, event.target.value),\n )\n }\n sx={{ width: 140 }}\n >\n {CRM_FORECAST_CATEGORIES.map((category) => (\n <MenuItem key={category} value={category}>\n {CRM_FORECAST_CATEGORY_LABELS[category]}\n </MenuItem>\n ))}\n </TextField>\n {isOpen ? (\n <>\n <Tooltip title=\"Move up\">\n <span>\n <IconButton\n size=\"small\"\n aria-label={`Move ${stage.name} up`}\n disabled={first}\n onClick={() =>\n setStages((current) => moveStage(current, stage.id, 'up'))\n }\n >\n <MdiIcon path={mdiArrowUp.path} size={0.8} />\n </IconButton>\n </span>\n </Tooltip>\n <Tooltip title=\"Move down\">\n <span>\n <IconButton\n size=\"small\"\n aria-label={`Move ${stage.name} down`}\n disabled={lastOpen}\n onClick={() =>\n setStages((current) => moveStage(current, stage.id, 'down'))\n }\n >\n <MdiIcon path={mdiArrowDown.path} size={0.8} />\n </IconButton>\n </span>\n </Tooltip>\n <Tooltip title=\"Remove stage\">\n <span>\n <IconButton\n size=\"small\"\n color=\"error\"\n aria-label={`Remove ${stage.name}`}\n disabled={counting === stage.id || busy}\n onClick={() => void handleRemove(stage.id)}\n >\n <MdiIcon path={mdiDeleteOutline.path} size={0.8} />\n </IconButton>\n </span>\n </Tooltip>\n </>\n ) : (\n <Chip\n size=\"small\"\n label={stage.kind === 'won' ? 'Won' : 'Lost'}\n color={stage.kind === 'won' ? 'success' : 'default'}\n sx={{ minWidth: 120 }}\n />\n )}\n </Stack>\n )\n })}\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <TextField\n size=\"small\"\n label=\"New stage\"\n value={newName}\n onChange={(event) => setNewName(event.target.value)}\n onKeyDown={(event) => {\n if (event.key !== 'Enter' || !newName.trim()) return\n event.preventDefault()\n setStages((current) => addStage(current, newName))\n setNewName('')\n }}\n sx={{ flex: 1 }}\n slotProps={{ htmlInput: { maxLength: 60 } }}\n />\n <Button\n disabled={!newName.trim()}\n onClick={() => {\n setStages((current) => addStage(current, newName))\n setNewName('')\n }}\n >\n {'Add stage'}\n </Button>\n </Stack>\n {problem ? (\n <Typography variant=\"caption\" color=\"error\">\n {problem}\n </Typography>\n ) : null}\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} disabled={busy}>\n {'Cancel'}\n </Button>\n <Button\n variant=\"contained\"\n disabled={busy || !pipeline || Boolean(problem)}\n onClick={() => void handleSave()}\n >\n {'Save stages'}\n </Button>\n </DialogActions>\n </Dialog>\n )\n}\nPipelineStagesDialog.displayName = 'PipelineStagesDialog'\n\nexport default PipelineStagesDialog\n"],"names":["CRM_COLLECTIONS","CRM_FORECAST_CATEGORIES","CRM_FORECAST_CATEGORY_LABELS","dealStageForecastCategory","mdiArrowDown","mdiArrowUp","mdiDeleteOutline","MdiIcon","useSnackbar","useFirestore","writeGuardedBySeed","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","IconButton","MenuItem","Stack","TextField","Tooltip","Typography","collection","doc","getCountFromServer","query","updateDoc","where","useCallback","useEffect","useState","crmVisibleToClause","addStage","moveStage","removeStage","renameStage","setStageForecastCategory","setStageProbability","sortedStages","stageRemovalRefusal","stagesProblem","settleStageNames","PipelineStagesDialog","props","open","onClose","orgId","pipeline","fromCache","unreadable","visibleToTokens","firestore","enqueueSnackbar","stages","setStages","newName","setNewName","busy","setBusy","counting","setCounting","handleRemove","stageId","snapshot","deals","$id","refusal","data","count","variant","persist","current","error","console","allowDuplicate","problem","handleSave","verdict","subject","pipelines","updatedAt","Date","ok","message","openCount","filter","stage","kind","length","undefined","fullWidth","maxWidth","name","spacing","sx","pt","color","map","index","isOpen","first","lastOpen","direction","alignItems","size","label","value","onChange","event","id","target","flex","slotProps","htmlInput","maxLength","type","probability","disabled","width","min","max","step","input","endAdornment","select","category","title","span","aria-label","onClick","path","minWidth","onKeyDown","key","trim","preventDefault","Boolean","displayName"],"mappings":"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"}
@@ -3,6 +3,13 @@ export type RecordActivityCardProps = ActivityRecordLink & {
3
3
  /** The site the record is read under, or `null` at the organization level. */
4
4
  hostId: string | null;
5
5
  org: CrmOrg;
6
+ /** The deal's page's own document, so **Log activity** opens on its Primary. */
7
+ deal?: {
8
+ contactId?: unknown;
9
+ contactRoles?: unknown;
10
+ } | null;
11
+ /** The hub's mount path, for the links a company's entries carry. */
12
+ basePath?: string;
6
13
  };
7
14
  /**
8
15
  * The activity log of one record — a contact, a company, a deal or a lead
@@ -14,8 +21,10 @@ export type RecordActivityCardProps = ActivityRecordLink & {
14
21
  * bounded to a hundred, which a page turned past it widens by the next
15
22
  * hundred. **Log activity** and **Expand all** sit in the card header. Which
16
23
  * record is decided by whichever of the four ids the page passes, and that
17
- * id is what the dialog files a new activity against — the page fixes it,
18
- * the reader does not pick it.
24
+ * id is what the dialog's **Name** and **Related To** open on.
25
+ *
26
+ * A company's log is every entry that rolled up to it (AGL-4123), its people's
27
+ * and its deals' as well as its own, so each row says who or what it is about.
19
28
  *
20
29
  * The contact's page does NOT use this card. A person has a second history
21
30
  * the platform captured on the row itself, and the two belong in one
@@ -16,13 +16,59 @@
16
16
  */ 'use client';
17
17
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
18
18
  import { pluginDocsHelp } from "@aglyn/aglyn";
19
- import { CardDisplay } from "@aglyn/shared-ui-jsx";
19
+ import { AppLink, CardDisplay } from "@aglyn/shared-ui-jsx";
20
20
  import { Button, Stack, Typography } from "@mui/material";
21
- import { useCallback, useMemo, useState } from "react";
21
+ import { Fragment, useCallback, useMemo, useState } from "react";
22
+ import { useCrmRecordNames } from "../hooks/use-crm-record-names.js";
23
+ import { crmRoutes } from "../model/crm-routes.js";
22
24
  import { ActivityList } from "./activity-list.js";
23
25
  import { useActivityScope, useActivityWindow } from "./activity-queries.js";
24
26
  import { TimelineExpandAllButton, useTimelineExpansion } from "./activity-timeline.js";
25
27
  import { LogActivityDialog } from "./log-activity-dialog.js";
28
+ /** A rolled-up entry's person, then its deal, and the word for one nobody can name. */ const ABOUT_KINDS = [
29
+ {
30
+ kind: 'contact',
31
+ field: 'contactId',
32
+ label: 'Contact'
33
+ },
34
+ {
35
+ kind: 'deal',
36
+ field: 'dealId',
37
+ label: 'Deal'
38
+ }
39
+ ];
40
+ /**
41
+ * The contact and the deal a company's entry is about (AGL-4123), each a link
42
+ * to its record. A record this reader cannot name, or one since deleted, reads
43
+ * as its kind: the id means nothing to anybody.
44
+ */ function ActivityAbout(props) {
45
+ const { activity, nameOf, hrefOf } = props;
46
+ const about = ABOUT_KINDS.flatMap(({ kind, field, label })=>{
47
+ const id = activity[field];
48
+ return id ? [
49
+ {
50
+ kind,
51
+ id,
52
+ label: nameOf(kind, id) || label
53
+ }
54
+ ] : [];
55
+ });
56
+ if (!about.length) return null;
57
+ return /*#__PURE__*/ _jsx(Typography, {
58
+ variant: "caption",
59
+ color: "text.secondary",
60
+ "data-testid": "activity-about",
61
+ children: about.map(({ kind, id, label }, index)=>/*#__PURE__*/ _jsxs(Fragment, {
62
+ children: [
63
+ index ? ' · ' : null,
64
+ hrefOf ? /*#__PURE__*/ _jsx(AppLink, {
65
+ href: hrefOf(kind, id),
66
+ children: label
67
+ }) : label
68
+ ]
69
+ }, kind))
70
+ });
71
+ }
26
72
  /**
27
73
  * The activity log of one record — a contact, a company, a deal or a lead
28
74
  * (AGL-2615) — with the button that adds to it (AGL-2600).
@@ -33,14 +79,18 @@ import { LogActivityDialog } from "./log-activity-dialog.js";
33
79
  * bounded to a hundred, which a page turned past it widens by the next
34
80
  * hundred. **Log activity** and **Expand all** sit in the card header. Which
35
81
  * record is decided by whichever of the four ids the page passes, and that
36
- * id is what the dialog files a new activity against — the page fixes it,
37
- * the reader does not pick it.
82
+ * id is what the dialog's **Name** and **Related To** open on.
83
+ *
84
+ * A company's log is every entry that rolled up to it (AGL-4123), its people's
85
+ * and its deals' as well as its own, so each row says who or what it is about.
38
86
  *
39
87
  * The contact's page does NOT use this card. A person has a second history
40
88
  * the platform captured on the row itself, and the two belong in one
41
89
  * stream; see `contact-timeline-card.tsx`, which is this card's superset.
42
90
  */ export function RecordActivityCard(props) {
43
- const { hostId, org, contactId, companyId, dealId, leadId } = props;
91
+ var _ref;
92
+ var _scope_consentGroup;
93
+ const { hostId, org, contactId, companyId, dealId, leadId, deal, basePath } = props;
44
94
  const scope = useActivityScope(hostId, org);
45
95
  /*
46
96
  * Memoized because the link is a listener dependency by value inside
@@ -59,6 +109,39 @@ import { LogActivityDialog } from "./log-activity-dialog.js";
59
109
  leadId
60
110
  ]);
61
111
  const activities = useActivityWindow(scope, link);
112
+ /*
113
+ * One read per person and deal the shown rows name, cached for the page and
114
+ * asked only on a company's log: the bounded audience of one account, which
115
+ * the org-wide feed is not, and the reason it names no records.
116
+ */ const aboutRecords = useMemo(()=>companyId ? activities.rows.flatMap((row)=>ABOUT_KINDS.flatMap(({ kind, field })=>{
117
+ const id = row[field];
118
+ return id ? [
119
+ {
120
+ kind,
121
+ id
122
+ }
123
+ ] : [];
124
+ })) : [], [
125
+ companyId,
126
+ activities.rows
127
+ ]);
128
+ const nameOf = useCrmRecordNames({
129
+ orgId: scope.orgId,
130
+ groupId: (_ref = (_scope_consentGroup = scope.consentGroup) == null ? void 0 : _scope_consentGroup.groupId) != null ? _ref : null,
131
+ org: scope.org,
132
+ records: aboutRecords
133
+ });
134
+ const routes = useMemo(()=>basePath ? crmRoutes(basePath) : null, [
135
+ basePath
136
+ ]);
137
+ const aboutFor = useCallback((activity)=>/*#__PURE__*/ _jsx(ActivityAbout, {
138
+ activity: activity,
139
+ nameOf: nameOf,
140
+ hrefOf: routes ? (kind, id)=>routes[kind](id) : null
141
+ }), [
142
+ nameOf,
143
+ routes
144
+ ]);
62
145
  const expansion = useTimelineExpansion();
63
146
  const [dialogOpen, setDialogOpen] = useState(false);
64
147
  const [editing, setEditing] = useState(null);
@@ -108,6 +191,7 @@ import { LogActivityDialog } from "./log-activity-dialog.js";
108
191
  rows: activities.rows,
109
192
  scope: scope,
110
193
  onEdit: openEdit,
194
+ aboutFor: companyId ? aboutFor : undefined,
111
195
  hasMore: activities.hasMore,
112
196
  onShowMore: activities.showMore,
113
197
  loading: activities.status === 'loading',
@@ -128,6 +212,7 @@ import { LogActivityDialog } from "./log-activity-dialog.js";
128
212
  onClose: close,
129
213
  scope: scope,
130
214
  link: link,
215
+ deal: deal,
131
216
  activity: editing
132
217
  })
133
218
  ]
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/record-activity-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 type { CrmActivityRow } from '@aglyn/aglyn'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { Button, Stack, Typography } from '@mui/material'\nimport { useCallback, useMemo, useState } from 'react'\nimport { ActivityList } from './activity-list'\nimport {\n type ActivityRecordLink,\n type CrmOrg,\n useActivityScope,\n useActivityWindow,\n} from './activity-queries'\nimport { TimelineExpandAllButton, useTimelineExpansion } from './activity-timeline'\nimport { LogActivityDialog } from './log-activity-dialog'\n\nexport type RecordActivityCardProps = ActivityRecordLink & {\n /** The site the record is read under, or `null` at the organization level. */\n hostId: string | null\n org: CrmOrg\n}\n\n/**\n * The activity log of one record — a contact, a company, a deal or a lead\n * (AGL-2615) — with the button that adds to it (AGL-2600).\n *\n * One card for all four record pages, because the log is the same thing on\n * each of them: what people did about this record, newest first, one\n * collapsed entry each and paged by the console's footer over a listener\n * bounded to a hundred, which a page turned past it widens by the next\n * hundred. **Log activity** and **Expand all** sit in the card header. Which\n * record is decided by whichever of the four ids the page passes, and that\n * id is what the dialog files a new activity against — the page fixes it,\n * the reader does not pick it.\n *\n * The contact's page does NOT use this card. A person has a second history\n * the platform captured on the row itself, and the two belong in one\n * stream; see `contact-timeline-card.tsx`, which is this card's superset.\n */\nexport function RecordActivityCard(props: RecordActivityCardProps) {\n const { hostId, org, contactId, companyId, dealId, leadId } = props\n const scope = useActivityScope(hostId, org)\n /*\n * Memoized because the link is a listener dependency by value inside\n * `useActivityWindow` and the dialog's prop by reference: a fresh object\n * each render would be harmless to the query, which reads the ids, and\n * noisy for anything that compares the object.\n */\n const link = useMemo<ActivityRecordLink>(\n () => ({ contactId, companyId, dealId, leadId }),\n [contactId, companyId, dealId, leadId],\n )\n const activities = useActivityWindow(scope, link)\n const expansion = useTimelineExpansion()\n const [dialogOpen, setDialogOpen] = useState(false)\n const [editing, setEditing] = useState<CrmActivityRow | null>(null)\n const openNew = useCallback(() => {\n setEditing(null)\n setDialogOpen(true)\n }, [])\n const openEdit = useCallback((activity: CrmActivityRow) => {\n setEditing(activity)\n setDialogOpen(true)\n }, [])\n const close = useCallback(() => setDialogOpen(false), [])\n\n return (\n <>\n <CardDisplay\n header={'Activity'}\n help={pluginDocsHelp('contactActivities', { anchor: '#logging-an-activity' })}\n HeaderProps={{\n action: (\n <Stack direction=\"row\" spacing={1}>\n <TimelineExpandAllButton\n expansion={expansion}\n disabled={!activities.rows.length}\n />\n <Button\n size=\"small\"\n variant=\"contained\"\n color=\"primary\"\n onClick={openNew}\n disabled={!activities.ready}\n >\n {'Log activity'}\n </Button>\n </Stack>\n ),\n }}\n contentGutterX\n contentGutterY\n >\n {activities.status === 'error' ? (\n <Typography variant=\"body2\" color=\"error\">\n {'The activity log could not be loaded.'}\n </Typography>\n ) : (\n <ActivityList\n rows={activities.rows}\n scope={scope}\n onEdit={openEdit}\n hasMore={activities.hasMore}\n onShowMore={activities.showMore}\n loading={activities.status === 'loading'}\n expansion={expansion}\n emptyText=\"A call, an email, a meeting or a note about this record goes here.\"\n emptyAction={\n <Button\n size=\"small\"\n variant=\"contained\"\n color=\"primary\"\n onClick={openNew}\n disabled={!activities.ready}\n >\n {'Log activity'}\n </Button>\n }\n />\n )}\n </CardDisplay>\n <LogActivityDialog\n open={dialogOpen}\n onClose={close}\n scope={scope}\n link={link}\n activity={editing}\n />\n </>\n )\n}\nRecordActivityCard.displayName = 'RecordActivityCard'\n\nexport default RecordActivityCard\n"],"names":["pluginDocsHelp","CardDisplay","Button","Stack","Typography","useCallback","useMemo","useState","ActivityList","useActivityScope","useActivityWindow","TimelineExpandAllButton","useTimelineExpansion","LogActivityDialog","RecordActivityCard","props","hostId","org","contactId","companyId","dealId","leadId","scope","link","activities","expansion","dialogOpen","setDialogOpen","editing","setEditing","openNew","openEdit","activity","close","header","help","anchor","HeaderProps","action","direction","spacing","disabled","rows","length","size","variant","color","onClick","ready","contentGutterX","contentGutterY","status","onEdit","hasMore","onShowMore","showMore","loading","emptyText","emptyAction","open","onClose","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAGA,SAASA,cAAc,QAAQ,eAAc;AAC7C,SAASC,WAAW,QAAQ,uBAAsB;AAClD,SAASC,MAAM,EAAEC,KAAK,EAAEC,UAAU,QAAQ,gBAAe;AACzD,SAASC,WAAW,EAAEC,OAAO,EAAEC,QAAQ,QAAQ,QAAO;AACtD,SAASC,YAAY,QAAQ,qBAAiB;AAC9C,SAGEC,gBAAgB,EAChBC,iBAAiB,QACZ,wBAAoB;AAC3B,SAASC,uBAAuB,EAAEC,oBAAoB,QAAQ,yBAAqB;AACnF,SAASC,iBAAiB,QAAQ,2BAAuB;AAQzD;;;;;;;;;;;;;;;;CAgBC,GACD,OAAO,SAASC,mBAAmBC,KAA8B;IAC/D,MAAM,EAAEC,MAAM,EAAEC,GAAG,EAAEC,SAAS,EAAEC,SAAS,EAAEC,MAAM,EAAEC,MAAM,EAAE,GAAGN;IAC9D,MAAMO,QAAQb,iBAAiBO,QAAQC;IACvC;;;;;GAKC,GACD,MAAMM,OAAOjB,QACX,IAAO,CAAA;YAAEY;YAAWC;YAAWC;YAAQC;QAAO,CAAA,GAC9C;QAACH;QAAWC;QAAWC;QAAQC;KAAO;IAExC,MAAMG,aAAad,kBAAkBY,OAAOC;IAC5C,MAAME,YAAYb;IAClB,MAAM,CAACc,YAAYC,cAAc,GAAGpB,SAAS;IAC7C,MAAM,CAACqB,SAASC,WAAW,GAAGtB,SAAgC;IAC9D,MAAMuB,UAAUzB,YAAY;QAC1BwB,WAAW;QACXF,cAAc;IAChB,GAAG,EAAE;IACL,MAAMI,WAAW1B,YAAY,CAAC2B;QAC5BH,WAAWG;QACXL,cAAc;IAChB,GAAG,EAAE;IACL,MAAMM,QAAQ5B,YAAY,IAAMsB,cAAc,QAAQ,EAAE;IAExD,qBACE;;0BACE,KAAC1B;gBACCiC,QAAQ;gBACRC,MAAMnC,eAAe,qBAAqB;oBAAEoC,QAAQ;gBAAuB;gBAC3EC,aAAa;oBACXC,sBACE,MAACnC;wBAAMoC,WAAU;wBAAMC,SAAS;;0CAC9B,KAAC7B;gCACCc,WAAWA;gCACXgB,UAAU,CAACjB,WAAWkB,IAAI,CAACC,MAAM;;0CAEnC,KAACzC;gCACC0C,MAAK;gCACLC,SAAQ;gCACRC,OAAM;gCACNC,SAASjB;gCACTW,UAAU,CAACjB,WAAWwB,KAAK;0CAE1B;;;;gBAIT;gBACAC,cAAc;gBACdC,cAAc;0BAEb1B,WAAW2B,MAAM,KAAK,wBACrB,KAAC/C;oBAAWyC,SAAQ;oBAAQC,OAAM;8BAC/B;mCAGH,KAACtC;oBACCkC,MAAMlB,WAAWkB,IAAI;oBACrBpB,OAAOA;oBACP8B,QAAQrB;oBACRsB,SAAS7B,WAAW6B,OAAO;oBAC3BC,YAAY9B,WAAW+B,QAAQ;oBAC/BC,SAAShC,WAAW2B,MAAM,KAAK;oBAC/B1B,WAAWA;oBACXgC,WAAU;oBACVC,2BACE,KAACxD;wBACC0C,MAAK;wBACLC,SAAQ;wBACRC,OAAM;wBACNC,SAASjB;wBACTW,UAAU,CAACjB,WAAWwB,KAAK;kCAE1B;;;;0BAMX,KAACnC;gBACC8C,MAAMjC;gBACNkC,SAAS3B;gBACTX,OAAOA;gBACPC,MAAMA;gBACNS,UAAUJ;;;;AAIlB;AACAd,mBAAmB+C,WAAW,GAAG;AAEjC,eAAe/C,mBAAkB"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/components/record-activity-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 type { CrmActivityRow } from '@aglyn/aglyn'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport { AppLink, CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { Button, Stack, Typography } from '@mui/material'\nimport { Fragment, useCallback, useMemo, useState } from 'react'\nimport { type CrmRecordKind, useCrmRecordNames } from '../hooks/use-crm-record-names'\nimport { crmRoutes } from '../model/crm-routes'\nimport { ActivityList } from './activity-list'\nimport {\n type ActivityRecordLink,\n type CrmOrg,\n useActivityScope,\n useActivityWindow,\n} from './activity-queries'\nimport { TimelineExpandAllButton, useTimelineExpansion } from './activity-timeline'\nimport { LogActivityDialog } from './log-activity-dialog'\n\nexport type RecordActivityCardProps = ActivityRecordLink & {\n /** The site the record is read under, or `null` at the organization level. */\n hostId: string | null\n org: CrmOrg\n /** The deal's page's own document, so **Log activity** opens on its Primary. */\n deal?: { contactId?: unknown; contactRoles?: unknown } | null\n /** The hub's mount path, for the links a company's entries carry. */\n basePath?: string\n}\n\n/** A rolled-up entry's person, then its deal, and the word for one nobody can name. */\nconst ABOUT_KINDS = [\n { kind: 'contact', field: 'contactId', label: 'Contact' },\n { kind: 'deal', field: 'dealId', label: 'Deal' },\n] as const satisfies ReadonlyArray<{\n kind: CrmRecordKind\n field: 'contactId' | 'dealId'\n label: string\n}>\n\n/**\n * The contact and the deal a company's entry is about (AGL-4123), each a link\n * to its record. A record this reader cannot name, or one since deleted, reads\n * as its kind: the id means nothing to anybody.\n */\nfunction ActivityAbout(props: {\n activity: CrmActivityRow\n nameOf: (kind: CrmRecordKind, id: string) => string | undefined\n hrefOf: ((kind: CrmRecordKind, id: string) => string) | null\n}) {\n const { activity, nameOf, hrefOf } = props\n const about = ABOUT_KINDS.flatMap(({ kind, field, label }) => {\n const id = activity[field]\n return id ? [{ kind, id, label: nameOf(kind, id) || label }] : []\n })\n if (!about.length) return null\n return (\n <Typography variant=\"caption\" color=\"text.secondary\" data-testid=\"activity-about\">\n {about.map(({ kind, id, label }, index) => (\n <Fragment key={kind}>\n {index ? ' · ' : null}\n {hrefOf ? <AppLink href={hrefOf(kind, id)}>{label}</AppLink> : label}\n </Fragment>\n ))}\n </Typography>\n )\n}\n\n/**\n * The activity log of one record — a contact, a company, a deal or a lead\n * (AGL-2615) — with the button that adds to it (AGL-2600).\n *\n * One card for all four record pages, because the log is the same thing on\n * each of them: what people did about this record, newest first, one\n * collapsed entry each and paged by the console's footer over a listener\n * bounded to a hundred, which a page turned past it widens by the next\n * hundred. **Log activity** and **Expand all** sit in the card header. Which\n * record is decided by whichever of the four ids the page passes, and that\n * id is what the dialog's **Name** and **Related To** open on.\n *\n * A company's log is every entry that rolled up to it (AGL-4123), its people's\n * and its deals' as well as its own, so each row says who or what it is about.\n *\n * The contact's page does NOT use this card. A person has a second history\n * the platform captured on the row itself, and the two belong in one\n * stream; see `contact-timeline-card.tsx`, which is this card's superset.\n */\nexport function RecordActivityCard(props: RecordActivityCardProps) {\n const { hostId, org, contactId, companyId, dealId, leadId, deal, basePath } = props\n const scope = useActivityScope(hostId, org)\n /*\n * Memoized because the link is a listener dependency by value inside\n * `useActivityWindow` and the dialog's prop by reference: a fresh object\n * each render would be harmless to the query, which reads the ids, and\n * noisy for anything that compares the object.\n */\n const link = useMemo<ActivityRecordLink>(\n () => ({ contactId, companyId, dealId, leadId }),\n [contactId, companyId, dealId, leadId],\n )\n const activities = useActivityWindow(scope, link)\n /*\n * One read per person and deal the shown rows name, cached for the page and\n * asked only on a company's log: the bounded audience of one account, which\n * the org-wide feed is not, and the reason it names no records.\n */\n const aboutRecords = useMemo(\n () =>\n companyId\n ? activities.rows.flatMap((row) =>\n ABOUT_KINDS.flatMap(({ kind, field }) => {\n const id = row[field]\n return id ? [{ kind, id }] : []\n }),\n )\n : [],\n [companyId, activities.rows],\n )\n const nameOf = useCrmRecordNames({\n orgId: scope.orgId,\n groupId: scope.consentGroup?.groupId ?? null,\n org: scope.org,\n records: aboutRecords,\n })\n const routes = useMemo(() => (basePath ? crmRoutes(basePath) : null), [basePath])\n const aboutFor = useCallback(\n (activity: CrmActivityRow) => (\n <ActivityAbout\n activity={activity}\n nameOf={nameOf}\n hrefOf={routes ? (kind, id) => routes[kind](id) : null}\n />\n ),\n [nameOf, routes],\n )\n const expansion = useTimelineExpansion()\n const [dialogOpen, setDialogOpen] = useState(false)\n const [editing, setEditing] = useState<CrmActivityRow | null>(null)\n const openNew = useCallback(() => {\n setEditing(null)\n setDialogOpen(true)\n }, [])\n const openEdit = useCallback((activity: CrmActivityRow) => {\n setEditing(activity)\n setDialogOpen(true)\n }, [])\n const close = useCallback(() => setDialogOpen(false), [])\n\n return (\n <>\n <CardDisplay\n header={'Activity'}\n help={pluginDocsHelp('contactActivities', { anchor: '#logging-an-activity' })}\n HeaderProps={{\n action: (\n <Stack direction=\"row\" spacing={1}>\n <TimelineExpandAllButton\n expansion={expansion}\n disabled={!activities.rows.length}\n />\n <Button\n size=\"small\"\n variant=\"contained\"\n color=\"primary\"\n onClick={openNew}\n disabled={!activities.ready}\n >\n {'Log activity'}\n </Button>\n </Stack>\n ),\n }}\n contentGutterX\n contentGutterY\n >\n {activities.status === 'error' ? (\n <Typography variant=\"body2\" color=\"error\">\n {'The activity log could not be loaded.'}\n </Typography>\n ) : (\n <ActivityList\n rows={activities.rows}\n scope={scope}\n onEdit={openEdit}\n aboutFor={companyId ? aboutFor : undefined}\n hasMore={activities.hasMore}\n onShowMore={activities.showMore}\n loading={activities.status === 'loading'}\n expansion={expansion}\n emptyText=\"A call, an email, a meeting or a note about this record goes here.\"\n emptyAction={\n <Button\n size=\"small\"\n variant=\"contained\"\n color=\"primary\"\n onClick={openNew}\n disabled={!activities.ready}\n >\n {'Log activity'}\n </Button>\n }\n />\n )}\n </CardDisplay>\n <LogActivityDialog\n open={dialogOpen}\n onClose={close}\n scope={scope}\n link={link}\n deal={deal}\n activity={editing}\n />\n </>\n )\n}\nRecordActivityCard.displayName = 'RecordActivityCard'\n\nexport default RecordActivityCard\n"],"names":["pluginDocsHelp","AppLink","CardDisplay","Button","Stack","Typography","Fragment","useCallback","useMemo","useState","useCrmRecordNames","crmRoutes","ActivityList","useActivityScope","useActivityWindow","TimelineExpandAllButton","useTimelineExpansion","LogActivityDialog","ABOUT_KINDS","kind","field","label","ActivityAbout","props","activity","nameOf","hrefOf","about","flatMap","id","length","variant","color","data-testid","map","index","href","RecordActivityCard","scope","hostId","org","contactId","companyId","dealId","leadId","deal","basePath","link","activities","aboutRecords","rows","row","orgId","groupId","consentGroup","records","routes","aboutFor","expansion","dialogOpen","setDialogOpen","editing","setEditing","openNew","openEdit","close","header","help","anchor","HeaderProps","action","direction","spacing","disabled","size","onClick","ready","contentGutterX","contentGutterY","status","onEdit","undefined","hasMore","onShowMore","showMore","loading","emptyText","emptyAction","open","onClose","displayName"],"mappings":"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"}
@@ -59,8 +59,10 @@ export declare namespace AutoCreateCompaniesCard {
59
59
  *
60
60
  * Reads `crmDefaultScopeOf`, the same reader the capture door and every CRM
61
61
  * creator stamp from, so the control cannot show one thing while records
62
- * get another — including for an org that has never set it, which inherits
63
- * the dataset default it shared before the split.
62
+ * get another — including for an org that has never set it, which shares
63
+ * with every site unless it set the older dataset default to keep them
64
+ * apart. Keeping each site's records apart is the agency's choice: one
65
+ * client's collaborator then sees only that client's people.
64
66
  */
65
67
  export declare function DefaultRecordSharingCard(props: AutoCreateCompaniesCardProps): import("react").JSX.Element;
66
68
  export declare namespace DefaultRecordSharingCard {
@@ -169,8 +169,10 @@ AutoCreateCompaniesCard.displayName = 'AutoCreateCompaniesCard';
169
169
  *
170
170
  * Reads `crmDefaultScopeOf`, the same reader the capture door and every CRM
171
171
  * creator stamp from, so the control cannot show one thing while records
172
- * get another — including for an org that has never set it, which inherits
173
- * the dataset default it shared before the split.
172
+ * get another — including for an org that has never set it, which shares
173
+ * with every site unless it set the older dataset default to keep them
174
+ * apart. Keeping each site's records apart is the agency's choice: one
175
+ * client's collaborator then sees only that client's people.
174
176
  */ export function DefaultRecordSharingCard(props) {
175
177
  var _crmDefaultScopeOf;
176
178
  const { hostId, org } = props;
@@ -181,7 +183,7 @@ AutoCreateCompaniesCard.displayName = 'AutoCreateCompaniesCard';
181
183
  org
182
184
  });
183
185
  const { canManage, ready: roleReady } = useCanManageCrmSettings(orgId);
184
- const stored = (_crmDefaultScopeOf = crmDefaultScopeOf(org)) != null ? _crmDefaultScopeOf : 'host';
186
+ const stored = (_crmDefaultScopeOf = crmDefaultScopeOf(org)) != null ? _crmDefaultScopeOf : 'org';
185
187
  const [value, setValue] = useState(stored);
186
188
  const [busy, setBusy] = useState(false);
187
189
  useEffect(()=>setValue(stored), [
@@ -235,11 +237,11 @@ AutoCreateCompaniesCard.displayName = 'AutoCreateCompaniesCard';
235
237
  children: [
236
238
  /*#__PURE__*/ _jsx(MenuItem, {
237
239
  value: "org",
238
- children: 'All sites'
240
+ children: 'All sites in the organization'
239
241
  }),
240
242
  /*#__PURE__*/ _jsx(MenuItem, {
241
243
  value: "host",
242
- children: 'The site they came in on, and its consent group'
244
+ children: 'Only the site they came in on (keeps clients apart, for agencies)'
243
245
  })
244
246
  ]
245
247
  }),