@aglyn/plugins-outreach 1.0.0-beta.166 → 1.0.0-beta.168

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aglyn/plugins-outreach",
3
- "version": "1.0.0-beta.166",
3
+ "version": "1.0.0-beta.168",
4
4
  "license": "Apache-2.0",
5
5
  "homepage": "https://aglyn.com",
6
6
  "repository": {
@@ -25,18 +25,18 @@
25
25
  "./package.json": "./package.json"
26
26
  },
27
27
  "dependencies": {
28
- "@aglyn/aglyn": "1.0.0-beta.166",
29
- "@aglyn/shared-data-mdi": "1.0.0-beta.166",
30
- "@aglyn/shared-ui-email-campaigns": "1.0.0-beta.166",
31
- "@aglyn/shared-ui-jsx": "1.0.0-beta.166",
32
- "@aglyn/shared-ui-next": "1.0.0-beta.166",
33
- "@aglyn/shared-ui-snackstack": "1.0.0-beta.166",
34
- "@aglyn/shared-util-http": "1.0.0-beta.166",
35
- "@aglyn/shared-util-timestamp": "1.0.0-beta.166",
36
- "@aglyn/shared-util-tools": "1.0.0-beta.166",
37
- "@aglyn/tenant-data-admin": "1.0.0-beta.166",
38
- "@aglyn/tenant-feature-instance": "1.0.0-beta.166",
39
- "@aglyn/tenant-runtime": "1.0.0-beta.166",
28
+ "@aglyn/aglyn": "1.0.0-beta.168",
29
+ "@aglyn/shared-data-mdi": "1.0.0-beta.168",
30
+ "@aglyn/shared-ui-email-campaigns": "1.0.0-beta.168",
31
+ "@aglyn/shared-ui-jsx": "1.0.0-beta.168",
32
+ "@aglyn/shared-ui-next": "1.0.0-beta.168",
33
+ "@aglyn/shared-ui-snackstack": "1.0.0-beta.168",
34
+ "@aglyn/shared-util-http": "1.0.0-beta.168",
35
+ "@aglyn/shared-util-timestamp": "1.0.0-beta.168",
36
+ "@aglyn/shared-util-tools": "1.0.0-beta.168",
37
+ "@aglyn/tenant-data-admin": "1.0.0-beta.168",
38
+ "@aglyn/tenant-feature-instance": "1.0.0-beta.168",
39
+ "@aglyn/tenant-runtime": "1.0.0-beta.168",
40
40
  "@swc/helpers": "0.5.23"
41
41
  },
42
42
  "peerDependencies": {
@@ -77,6 +77,8 @@ const STATUS_COLORS = {
77
77
  text
78
78
  });
79
79
  const leadSearch = useOutreachLeadSearch({
80
+ // The lead silo is the org's (AGL-3275); the site still narrows it.
81
+ orgId,
80
82
  hostId: sequence.hostId,
81
83
  text: leadText,
82
84
  enabled: props.open && tab === 'leads'
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/components/enroll-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 { pluginDocsHelp } from '@aglyn/aglyn'\nimport { HelpTip } from '@aglyn/shared-ui-jsx'\nimport {\n Alert,\n Button,\n Checkbox,\n Chip,\n type ChipProps,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n FormControlLabel,\n FormGroup,\n List,\n ListItem,\n ListItemButton,\n ListItemIcon,\n ListItemText,\n MenuItem,\n Paper,\n Stack,\n Tab,\n Tabs,\n TextField,\n Typography,\n useMediaQuery,\n useTheme,\n} from '@mui/material'\nimport { useMemo, useState } from 'react'\nimport {\n OUTREACH_ENROLL_BATCH_MAX,\n type OutreachEnrollOutcome,\n type OutreachEnrollPreviewPerson,\n type OutreachEnrollPreviewResponse,\n type OutreachEnrollPreviewStatus,\n type OutreachEnrollSource,\n type OutreachPreviewResponse,\n} from '../model/outreach-api'\nimport {\n OUTREACH_ATTESTATION_LABELS,\n OUTREACH_PERSONAL_LINE_MAX,\n type OutreachAttestationKind,\n type OutreachSequence,\n} from '../model/outreach.types'\nimport { OutreachLoading, OutreachLoadProblem } from './outreach-ui'\nimport type { OutreachApi } from './use-outreach-api'\nimport {\n type OutreachContactOption,\n useOutreachContactSearch,\n useOutreachLeadSearch,\n useOutreachSavedViews,\n} from './use-outreach-crm'\n\nexport interface OutreachEnrollDialogProps {\n open: boolean\n onClose(): void\n orgId: string\n sequence: OutreachSequence\n /** The consent group of the sequence's site: whose names a search shows. */\n contactGroupId: string\n uid: string | null\n api: OutreachApi\n}\n\nconst STATUS_LABELS: Record<OutreachEnrollPreviewStatus, string> = {\n eligible: 'Eligible',\n needs_confirmation: 'Needs you',\n blocked: 'Blocked',\n}\n\nconst STATUS_COLORS: Record<OutreachEnrollPreviewStatus, ChipProps['color']> = {\n eligible: 'success',\n needs_confirmation: 'warning',\n blocked: 'default',\n}\n\n/** What the rep has supplied for one person. */\ninterface Supplied {\n include: boolean\n personalLine: string\n attestations: OutreachAttestationKind[]\n}\n\ntype Stage =\n | { kind: 'source' }\n | { kind: 'previewing' }\n | { kind: 'preview'; answer: OutreachEnrollPreviewResponse }\n | { kind: 'enrolling'; answer: OutreachEnrollPreviewResponse }\n | { kind: 'done'; results: OutreachEnrollOutcome[]; enrolled: number }\n\n/** Whether a person may be sent to Confirm as the rep has left them. */\nexport function outreachPersonReady(\n person: OutreachEnrollPreviewPerson,\n supplied: Supplied | undefined,\n): boolean {\n if (person.status === 'blocked' || !supplied?.include) return false\n const line = supplied.personalLine.trim()\n if (line.length > OUTREACH_PERSONAL_LINE_MAX) return false\n if (person.status === 'eligible') return true\n if (person.requires.personalLine && !line) return false\n return person.requires.attestations.every((kind) =>\n supplied.attestations.includes(kind),\n )\n}\n\n/**\n * Enroll people in a sequence (AGL-2980).\n *\n * The rep picks a saved Contacts view or searches for people, and sees each\n * marked as the gates see them: eligible, blocked with the reason, or\n * waiting on them — for a cold contact, a personal line and the three\n * attestations only they can make, stored on the enrollment with who ticked\n * them and when. Enroll sends only the people who are ready, and the server\n * checks every gate again as it enrolls them.\n */\nexport function OutreachEnrollDialog(props: OutreachEnrollDialogProps) {\n const { orgId, sequence, api } = props\n const theme = useTheme()\n const narrow = useMediaQuery(theme.breakpoints.down('sm'))\n const [tab, setTab] = useState<'view' | 'search' | 'leads'>('view')\n const [viewId, setViewId] = useState('')\n const [text, setText] = useState('')\n const [picked, setPicked] = useState<Map<string, OutreachContactOption>>(\n new Map(),\n )\n // The leads picked from the sequence's site (AGL-3234), kept apart from\n // the contacts: the two are different records and the source names which.\n const [leadText, setLeadText] = useState('')\n const [pickedLeads, setPickedLeads] = useState<Map<string, OutreachContactOption>>(\n new Map(),\n )\n const [stage, setStage] = useState<Stage>({ kind: 'source' })\n const [problem, setProblem] = useState<string | null>(null)\n const [supplied, setSupplied] = useState<Record<string, Supplied>>({})\n const [emailPreview, setEmailPreview] = useState<\n Record<\n string,\n | { status: 'loading' }\n | { status: 'ready'; answer: OutreachPreviewResponse }\n | { status: 'failed'; message: string }\n >\n >({})\n\n const views = useOutreachSavedViews(props.open ? orgId : null, props.uid)\n const search = useOutreachContactSearch({\n orgId: props.open && tab === 'search' ? orgId : null,\n hostId: sequence.hostId,\n contactGroupId: props.contactGroupId,\n text,\n })\n const leadSearch = useOutreachLeadSearch({\n hostId: sequence.hostId,\n text: leadText,\n enabled: props.open && tab === 'leads',\n })\n\n const reset = () => {\n setStage({ kind: 'source' })\n setProblem(null)\n setSupplied({})\n setEmailPreview({})\n }\n const close = () => {\n reset()\n setPicked(new Map())\n setPickedLeads(new Map())\n setText('')\n setLeadText('')\n setViewId('')\n props.onClose()\n }\n\n const preview = async (source: OutreachEnrollSource) => {\n setProblem(null)\n setStage({ kind: 'previewing' })\n try {\n const answer = await api.previewEnrollment(sequence.id, source)\n setSupplied(\n Object.fromEntries(\n answer.people.map((person) => [\n person.personId,\n {\n include: person.status !== 'blocked',\n personalLine: '',\n attestations: [],\n },\n ]),\n ),\n )\n setStage({ kind: 'preview', answer })\n } catch (error) {\n setProblem((error as Error).message)\n setStage({ kind: 'source' })\n }\n }\n\n const answer =\n stage.kind === 'preview' || stage.kind === 'enrolling' ? stage.answer : null\n const ready = useMemo(\n () =>\n answer\n ? answer.people.filter((person) =>\n outreachPersonReady(person, supplied[person.personId]),\n )\n : [],\n [answer, supplied],\n )\n const waiting = answer\n ? answer.people.filter(\n (person) =>\n person.status === 'needs_confirmation' &&\n supplied[person.personId]?.include &&\n !outreachPersonReady(person, supplied[person.personId]),\n ).length\n : 0\n\n const enroll = async () => {\n if (!answer || !ready.length) return\n setProblem(null)\n setStage({ kind: 'enrolling', answer })\n try {\n const result = await api.enroll(\n sequence.id,\n ready.map((person) => ({\n // A contact by id, or a lead by its key (AGL-3234).\n ...(person.target === 'lead' && person.leadId\n ? { leadId: person.leadId }\n : { contactId: person.contactId }),\n personalLine: supplied[person.personId]?.personalLine.trim() ?? '',\n attestations: supplied[person.personId]?.attestations ?? [],\n })),\n )\n setStage({\n kind: 'done',\n results: result.results,\n enrolled: result.enrolled,\n })\n } catch (error) {\n setProblem((error as Error).message)\n setStage({ kind: 'preview', answer })\n }\n }\n\n const supply = (personId: string, next: Partial<Supplied>) =>\n setSupplied((previous) => ({\n ...previous,\n [personId]: { ...previous[personId], ...next } as Supplied,\n }))\n\n const previewEmail = async (person: OutreachEnrollPreviewPerson) => {\n setEmailPreview((previous) => ({\n ...previous,\n [person.personId]: { status: 'loading' },\n }))\n try {\n const result = await api.previewEmail({\n sequenceId: sequence.id,\n ...(person.target === 'lead' && person.leadId\n ? { leadId: person.leadId }\n : { contactId: person.contactId }),\n personalLine: supplied[person.personId]?.personalLine.trim() ?? '',\n })\n setEmailPreview((previous) => ({\n ...previous,\n [person.personId]: { status: 'ready', answer: result },\n }))\n } catch (error) {\n setEmailPreview((previous) => ({\n ...previous,\n [person.personId]: {\n status: 'failed',\n message: (error as Error).message,\n },\n }))\n }\n }\n\n const title = `Enroll people in ${sequence.name}`\n\n return (\n <Dialog\n open={props.open}\n onClose={close}\n fullWidth\n maxWidth=\"md\"\n fullScreen={narrow}\n aria-label={title}\n >\n <DialogTitle>\n <Stack direction=\"row\" spacing={0.5} sx={{ alignItems: 'center' }}>\n <span>{title}</span>\n <HelpTip {...pluginDocsHelp('sequences', { anchor: '#enroll' })} />\n </Stack>\n </DialogTitle>\n <DialogContent dividers>\n <Stack spacing={2}>\n {problem ? <Alert severity=\"error\">{problem}</Alert> : null}\n\n {stage.kind === 'source' ? (\n <>\n <Tabs\n value={tab}\n onChange={(_event, next) => setTab(next)}\n aria-label=\"Where the people come from\"\n >\n <Tab value=\"view\" label=\"Saved view\" />\n <Tab value=\"search\" label=\"Search\" />\n <Tab value=\"leads\" label=\"Leads\" />\n </Tabs>\n {tab === 'view' ? (\n views.status === 'loading' ? (\n <OutreachLoading label=\"Loading saved views…\" />\n ) : views.status === 'error' || views.status === 'refused' ? (\n <OutreachLoadProblem\n status={views.status}\n what=\"saved views\"\n message={\n views.status === 'refused'\n ? 'Enrolling reads your CRM’s contacts, and your role does not include Manage data.'\n : null\n }\n />\n ) : views.data.length ? (\n <TextField\n select\n label=\"Saved Contacts view\"\n value={viewId}\n onChange={(event) => setViewId(event.target.value)}\n helperText={`Enrolls up to ${OUTREACH_ENROLL_BATCH_MAX} of the people the view shows at this sequence’s site — a Contacts view, or a Leads view of the site’s leads.`}\n fullWidth\n >\n {views.data.map((view) => (\n <MenuItem key={view.id} value={view.id}>\n {view.section === 'leads' ? `${view.name} (Leads)` : view.name}\n </MenuItem>\n ))}\n </TextField>\n ) : (\n <Alert severity=\"info\">\n You have no saved Contacts views to enroll from. Save one in\n the CRM, or search instead.\n </Alert>\n )\n ) : tab === 'leads' ? (\n <Stack spacing={1.5}>\n <TextField\n label=\"Search leads\"\n placeholder=\"A name, or an email address\"\n value={leadText}\n onChange={(event) => setLeadText(event.target.value)}\n helperText=\"The open leads at this sequence’s site. A lead is enrolled as it is, and follows itself to the contact it becomes.\"\n fullWidth\n autoFocus\n />\n {leadSearch.status === 'loading' ? (\n <OutreachLoading label=\"Reading leads…\" />\n ) : null}\n {leadSearch.status === 'error' || leadSearch.status === 'refused' ? (\n <OutreachLoadProblem\n status={leadSearch.status}\n what=\"leads\"\n message={\n leadSearch.status === 'refused'\n ? 'Enrolling reads your CRM’s leads, and your role does not include Manage data.'\n : 'The leads could not be read. Try again.'\n }\n />\n ) : null}\n {leadSearch.status === 'ready' && !leadSearch.idle && !leadSearch.data.length ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n No open leads at this sequence’s site match.\n </Typography>\n ) : null}\n {leadSearch.data.length ? (\n <List dense aria-label=\"Lead results\">\n {leadSearch.data.map((lead) => {\n const checked = pickedLeads.has(lead.id)\n return (\n <ListItem key={lead.id} disablePadding>\n <ListItemButton\n onClick={() =>\n setPickedLeads((previous) => {\n const next = new Map(previous)\n if (checked) next.delete(lead.id)\n else if (next.size < OUTREACH_ENROLL_BATCH_MAX) next.set(lead.id, lead)\n return next\n })\n }\n >\n <ListItemIcon>\n <Checkbox edge=\"start\" checked={checked} tabIndex={-1} disableRipple />\n </ListItemIcon>\n <ListItemText\n primary={lead.name || lead.email || lead.id}\n secondary={lead.email}\n />\n </ListItemButton>\n </ListItem>\n )\n })}\n </List>\n ) : null}\n {pickedLeads.size ? (\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ flexWrap: 'wrap', rowGap: 1 }}\n aria-label=\"Picked leads\"\n >\n {[...pickedLeads.values()].map((lead) => (\n <Chip\n key={lead.id}\n size=\"small\"\n label={lead.name || lead.email || lead.id}\n onDelete={() =>\n setPickedLeads((previous) => {\n const next = new Map(previous)\n next.delete(lead.id)\n return next\n })\n }\n />\n ))}\n </Stack>\n ) : null}\n </Stack>\n ) : (\n <Stack spacing={1.5}>\n <TextField\n label=\"Search contacts\"\n placeholder=\"A name, or an email address\"\n value={text}\n onChange={(event) => setText(event.target.value)}\n fullWidth\n autoFocus\n />\n {search.status === 'loading' ? (\n <OutreachLoading label=\"Searching…\" />\n ) : null}\n {search.status === 'error' || search.status === 'refused' ? (\n <OutreachLoadProblem\n status={search.status}\n what=\"contacts\"\n message={\n search.status === 'refused'\n ? 'Enrolling reads your CRM’s contacts, and your role does not include Manage data.'\n : 'The search failed. Try again.'\n }\n />\n ) : null}\n {search.status === 'ready' &&\n !search.idle &&\n !search.data.length ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n No contacts at this sequence’s site match.\n </Typography>\n ) : null}\n {search.data.length ? (\n <List dense aria-label=\"Search results\">\n {search.data.map((contact) => {\n const checked = picked.has(contact.id)\n return (\n <ListItem key={contact.id} disablePadding>\n <ListItemButton\n onClick={() =>\n setPicked((previous) => {\n const next = new Map(previous)\n if (checked) next.delete(contact.id)\n else if (\n next.size < OUTREACH_ENROLL_BATCH_MAX\n )\n next.set(contact.id, contact)\n return next\n })\n }\n >\n <ListItemIcon>\n <Checkbox\n edge=\"start\"\n checked={checked}\n tabIndex={-1}\n disableRipple\n />\n </ListItemIcon>\n <ListItemText\n primary={\n contact.name || contact.email || contact.id\n }\n secondary={contact.email}\n />\n </ListItemButton>\n </ListItem>\n )\n })}\n </List>\n ) : null}\n {picked.size ? (\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ flexWrap: 'wrap', rowGap: 1 }}\n aria-label=\"Picked\"\n >\n {[...picked.values()].map((contact) => (\n <Chip\n key={contact.id}\n size=\"small\"\n label={contact.name || contact.email || contact.id}\n onDelete={() =>\n setPicked((previous) => {\n const next = new Map(previous)\n next.delete(contact.id)\n return next\n })\n }\n />\n ))}\n </Stack>\n ) : null}\n </Stack>\n )}\n </>\n ) : null}\n\n {stage.kind === 'previewing' ? (\n <OutreachLoading label=\"Checking each person…\" />\n ) : null}\n\n {answer ? (\n <Stack spacing={1.5}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {summary(answer)}\n </Typography>\n {answer.truncated ? (\n <Alert severity=\"info\">\n {`This shows the first ${answer.people.length} of ${answer.total}. Enroll these, then preview again for the rest.`}\n </Alert>\n ) : null}\n <Stack\n spacing={1}\n component=\"ul\"\n sx={{ listStyle: 'none', p: 0, m: 0 }}\n aria-label=\"People\"\n >\n {answer.people.map((person) => (\n <PersonRow\n key={person.personId}\n person={person}\n supplied={supplied[person.personId]}\n onSupply={(next) => supply(person.personId, next)}\n emailPreview={emailPreview[person.personId]}\n onPreviewEmail={() => void previewEmail(person)}\n disabled={stage.kind === 'enrolling'}\n />\n ))}\n </Stack>\n {waiting ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`${waiting} ${waiting === 1 ? 'person still needs' : 'people still need'} a personal line or a confirmation, and won't be enrolled until they have one.`}\n </Typography>\n ) : null}\n </Stack>\n ) : null}\n\n {stage.kind === 'done' ? (\n <DoneSummary results={stage.results} enrolled={stage.enrolled} />\n ) : null}\n </Stack>\n </DialogContent>\n <DialogActions>\n {stage.kind === 'done' ? (\n <Button variant=\"contained\" onClick={close}>\n Done\n </Button>\n ) : (\n <>\n <Button onClick={close}>Cancel</Button>\n {stage.kind === 'preview' || stage.kind === 'enrolling' ? (\n <>\n <Button onClick={reset} disabled={stage.kind === 'enrolling'}>\n Back\n </Button>\n <Button\n variant=\"contained\"\n disabled={!ready.length || stage.kind === 'enrolling'}\n onClick={() => void enroll()}\n >\n {stage.kind === 'enrolling'\n ? 'Enrolling…'\n : `Enroll ${ready.length} ${ready.length === 1 ? 'person' : 'people'}`}\n </Button>\n </>\n ) : (\n <Button\n variant=\"contained\"\n disabled={\n stage.kind === 'previewing' ||\n (tab === 'view' ? !viewId : tab === 'leads' ? !pickedLeads.size : !picked.size)\n }\n onClick={() =>\n void preview(\n tab === 'view'\n ? { kind: 'view', viewId }\n : tab === 'leads'\n ? { kind: 'leads', leadIds: [...pickedLeads.keys()] }\n : { kind: 'contacts', contactIds: [...picked.keys()] },\n )\n }\n >\n Check people\n </Button>\n )}\n </>\n )}\n </DialogActions>\n </Dialog>\n )\n}\nOutreachEnrollDialog.displayName = 'OutreachEnrollDialog'\n\nfunction summary(answer: OutreachEnrollPreviewResponse): string {\n const count = (status: OutreachEnrollPreviewStatus) =>\n answer.people.filter((person) => person.status === status).length\n return `${count('eligible')} eligible · ${count('needs_confirmation')} need you · ${count('blocked')} blocked`\n}\n\nfunction PersonRow(props: {\n person: OutreachEnrollPreviewPerson\n supplied: Supplied | undefined\n onSupply(next: Partial<Supplied>): void\n emailPreview:\n | { status: 'loading' }\n | { status: 'ready'; answer: OutreachPreviewResponse }\n | { status: 'failed'; message: string }\n | undefined\n onPreviewEmail(): void\n disabled: boolean\n}) {\n const { person, supplied, disabled } = props\n const name = person.name || person.email || person.personId\n const line = supplied?.personalLine ?? ''\n const tooLong = line.trim().length > OUTREACH_PERSONAL_LINE_MAX\n return (\n <Paper component=\"li\" variant=\"outlined\" sx={{ p: 1.5 }} aria-label={name}>\n <Stack spacing={1}>\n <Stack\n direction={{ xs: 'column', sm: 'row' }}\n spacing={1}\n sx={{ alignItems: { sm: 'center' } }}\n >\n <Stack sx={{ flexGrow: 1, minWidth: 0 }}>\n <Typography variant=\"body2\">{name}</Typography>\n {person.email && person.email !== name ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {person.email}\n </Typography>\n ) : null}\n </Stack>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n {person.target === 'lead' ? (\n <Chip size=\"small\" variant=\"outlined\" label=\"Lead\" />\n ) : null}\n {person.cold ? (\n <Chip size=\"small\" variant=\"outlined\" label=\"Cold\" />\n ) : null}\n <Chip\n size=\"small\"\n label={STATUS_LABELS[person.status]}\n color={STATUS_COLORS[person.status]}\n />\n </Stack>\n </Stack>\n {person.status === 'blocked' ? (\n <Stack component=\"ul\" spacing={0.5} sx={{ m: 0, pl: 2.5 }}>\n {person.blocks.map((block) => (\n <Typography\n key={block.code}\n component=\"li\"\n variant=\"body2\"\n color=\"text.secondary\"\n >\n {block.reason}\n </Typography>\n ))}\n </Stack>\n ) : (\n <Stack spacing={1}>\n <FormControlLabel\n control={\n <Checkbox\n checked={supplied?.include ?? true}\n onChange={(event) =>\n props.onSupply({ include: event.target.checked })\n }\n disabled={disabled}\n />\n }\n label=\"Enroll this person\"\n />\n {supplied?.include === false ? null : (\n <>\n <TextField\n label={\n person.requires.personalLine\n ? 'Personal line'\n : 'Personal line (optional)'\n }\n placeholder=\"One sentence on why you're writing to this person now.\"\n value={line}\n onChange={(event) =>\n props.onSupply({ personalLine: event.target.value })\n }\n disabled={disabled}\n required={person.requires.personalLine}\n error={tooLong}\n helperText={\n tooLong\n ? `Keep it to one sentence, under ${OUTREACH_PERSONAL_LINE_MAX} characters.`\n : 'Goes where the emails say {{enrollment.personalLine}}.'\n }\n fullWidth\n size=\"small\"\n />\n {person.requires.attestations.length ? (\n <FormGroup aria-label={`What you confirm about ${name}`}>\n {person.requires.attestations.map((kind) => (\n <FormControlLabel\n key={kind}\n control={\n <Checkbox\n checked={\n supplied?.attestations.includes(kind) ?? false\n }\n onChange={(event) =>\n props.onSupply({\n attestations: event.target.checked\n ? [...(supplied?.attestations ?? []), kind]\n : (supplied?.attestations ?? []).filter(\n (entry) => entry !== kind,\n ),\n })\n }\n disabled={disabled}\n />\n }\n label={OUTREACH_ATTESTATION_LABELS[kind]}\n />\n ))}\n </FormGroup>\n ) : null}\n <Stack direction=\"row\">\n <Button\n size=\"small\"\n onClick={props.onPreviewEmail}\n disabled={disabled}\n >\n Preview the first email\n </Button>\n </Stack>\n {props.emailPreview?.status === 'loading' ? (\n <OutreachLoading label=\"Writing the email…\" />\n ) : null}\n {props.emailPreview?.status === 'failed' ? (\n <Alert severity=\"error\">{props.emailPreview.message}</Alert>\n ) : null}\n {props.emailPreview?.status === 'ready' ? (\n props.emailPreview.answer.error ? (\n <Alert severity=\"info\">\n {props.emailPreview.answer.error.message}\n </Alert>\n ) : (\n <Paper\n variant=\"outlined\"\n sx={{ p: 1.5 }}\n aria-label={`First email to ${name}`}\n >\n <Typography variant=\"body2\">{`Subject: ${props.emailPreview.answer.subject}`}</Typography>\n <Typography\n variant=\"body2\"\n sx={{\n whiteSpace: 'pre-wrap',\n wordBreak: 'break-word',\n mt: 1,\n }}\n >\n {props.emailPreview.answer.text}\n </Typography>\n </Paper>\n )\n ) : null}\n </>\n )}\n </Stack>\n )}\n </Stack>\n </Paper>\n )\n}\n\nfunction DoneSummary(props: {\n results: OutreachEnrollOutcome[]\n enrolled: number\n}) {\n const refused = props.results.filter(\n (\n result,\n ): result is Extract<OutreachEnrollOutcome, { outcome: 'blocked' }> =>\n result.outcome === 'blocked',\n )\n return (\n <Stack spacing={1.5}>\n <Alert severity={props.enrolled ? 'success' : 'info'}>\n {props.enrolled\n ? `Enrolled ${props.enrolled} ${props.enrolled === 1 ? 'person' : 'people'}. Their first step goes out in the mailbox's sending hours.`\n : 'Nobody was enrolled.'}\n </Alert>\n {refused.length ? (\n <Stack spacing={1}>\n <Typography variant=\"subtitle2\">\n Not enrolled, checked again just now:\n </Typography>\n {refused.map((result) => (\n <Stack key={result.personId} spacing={0.25}>\n <Typography variant=\"body2\">\n {result.email ?? result.personId}\n </Typography>\n {result.blocks.map((block) => (\n <Typography\n key={block.code}\n variant=\"body2\"\n color=\"text.secondary\"\n >\n {block.reason}\n </Typography>\n ))}\n </Stack>\n ))}\n </Stack>\n ) : null}\n </Stack>\n )\n}\n\nexport default OutreachEnrollDialog\n"],"names":["pluginDocsHelp","HelpTip","Alert","Button","Checkbox","Chip","Dialog","DialogActions","DialogContent","DialogTitle","FormControlLabel","FormGroup","List","ListItem","ListItemButton","ListItemIcon","ListItemText","MenuItem","Paper","Stack","Tab","Tabs","TextField","Typography","useMediaQuery","useTheme","useMemo","useState","OUTREACH_ENROLL_BATCH_MAX","OUTREACH_ATTESTATION_LABELS","OUTREACH_PERSONAL_LINE_MAX","OutreachLoading","OutreachLoadProblem","useOutreachContactSearch","useOutreachLeadSearch","useOutreachSavedViews","STATUS_LABELS","eligible","needs_confirmation","blocked","STATUS_COLORS","outreachPersonReady","person","supplied","status","include","line","personalLine","trim","length","requires","attestations","every","kind","includes","OutreachEnrollDialog","props","orgId","sequence","api","theme","narrow","breakpoints","down","tab","setTab","viewId","setViewId","text","setText","picked","setPicked","Map","leadText","setLeadText","pickedLeads","setPickedLeads","stage","setStage","problem","setProblem","setSupplied","emailPreview","setEmailPreview","views","open","uid","search","hostId","contactGroupId","leadSearch","enabled","reset","close","onClose","preview","source","answer","previewEnrollment","id","Object","fromEntries","people","map","personId","error","message","ready","filter","waiting","enroll","result","target","leadId","contactId","results","enrolled","supply","next","previous","previewEmail","sequenceId","title","name","fullWidth","maxWidth","fullScreen","aria-label","direction","spacing","sx","alignItems","span","anchor","dividers","severity","value","onChange","_event","label","what","data","select","event","helperText","view","section","placeholder","autoFocus","idle","variant","color","dense","lead","checked","has","disablePadding","onClick","delete","size","set","edge","tabIndex","disableRipple","primary","email","secondary","flexWrap","rowGap","values","onDelete","contact","summary","truncated","total","component","listStyle","p","m","PersonRow","onSupply","onPreviewEmail","disabled","DoneSummary","leadIds","keys","contactIds","displayName","count","tooLong","xs","sm","flexGrow","minWidth","cold","pl","blocks","block","reason","code","control","required","entry","subject","whiteSpace","wordBreak","mt","refused","outcome"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/components/enroll-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 { pluginDocsHelp } from '@aglyn/aglyn'\nimport { HelpTip } from '@aglyn/shared-ui-jsx'\nimport {\n Alert,\n Button,\n Checkbox,\n Chip,\n type ChipProps,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n FormControlLabel,\n FormGroup,\n List,\n ListItem,\n ListItemButton,\n ListItemIcon,\n ListItemText,\n MenuItem,\n Paper,\n Stack,\n Tab,\n Tabs,\n TextField,\n Typography,\n useMediaQuery,\n useTheme,\n} from '@mui/material'\nimport { useMemo, useState } from 'react'\nimport {\n OUTREACH_ENROLL_BATCH_MAX,\n type OutreachEnrollOutcome,\n type OutreachEnrollPreviewPerson,\n type OutreachEnrollPreviewResponse,\n type OutreachEnrollPreviewStatus,\n type OutreachEnrollSource,\n type OutreachPreviewResponse,\n} from '../model/outreach-api'\nimport {\n OUTREACH_ATTESTATION_LABELS,\n OUTREACH_PERSONAL_LINE_MAX,\n type OutreachAttestationKind,\n type OutreachSequence,\n} from '../model/outreach.types'\nimport { OutreachLoading, OutreachLoadProblem } from './outreach-ui'\nimport type { OutreachApi } from './use-outreach-api'\nimport {\n type OutreachContactOption,\n useOutreachContactSearch,\n useOutreachLeadSearch,\n useOutreachSavedViews,\n} from './use-outreach-crm'\n\nexport interface OutreachEnrollDialogProps {\n open: boolean\n onClose(): void\n orgId: string\n sequence: OutreachSequence\n /** The consent group of the sequence's site: whose names a search shows. */\n contactGroupId: string\n uid: string | null\n api: OutreachApi\n}\n\nconst STATUS_LABELS: Record<OutreachEnrollPreviewStatus, string> = {\n eligible: 'Eligible',\n needs_confirmation: 'Needs you',\n blocked: 'Blocked',\n}\n\nconst STATUS_COLORS: Record<OutreachEnrollPreviewStatus, ChipProps['color']> = {\n eligible: 'success',\n needs_confirmation: 'warning',\n blocked: 'default',\n}\n\n/** What the rep has supplied for one person. */\ninterface Supplied {\n include: boolean\n personalLine: string\n attestations: OutreachAttestationKind[]\n}\n\ntype Stage =\n | { kind: 'source' }\n | { kind: 'previewing' }\n | { kind: 'preview'; answer: OutreachEnrollPreviewResponse }\n | { kind: 'enrolling'; answer: OutreachEnrollPreviewResponse }\n | { kind: 'done'; results: OutreachEnrollOutcome[]; enrolled: number }\n\n/** Whether a person may be sent to Confirm as the rep has left them. */\nexport function outreachPersonReady(\n person: OutreachEnrollPreviewPerson,\n supplied: Supplied | undefined,\n): boolean {\n if (person.status === 'blocked' || !supplied?.include) return false\n const line = supplied.personalLine.trim()\n if (line.length > OUTREACH_PERSONAL_LINE_MAX) return false\n if (person.status === 'eligible') return true\n if (person.requires.personalLine && !line) return false\n return person.requires.attestations.every((kind) =>\n supplied.attestations.includes(kind),\n )\n}\n\n/**\n * Enroll people in a sequence (AGL-2980).\n *\n * The rep picks a saved Contacts view or searches for people, and sees each\n * marked as the gates see them: eligible, blocked with the reason, or\n * waiting on them — for a cold contact, a personal line and the three\n * attestations only they can make, stored on the enrollment with who ticked\n * them and when. Enroll sends only the people who are ready, and the server\n * checks every gate again as it enrolls them.\n */\nexport function OutreachEnrollDialog(props: OutreachEnrollDialogProps) {\n const { orgId, sequence, api } = props\n const theme = useTheme()\n const narrow = useMediaQuery(theme.breakpoints.down('sm'))\n const [tab, setTab] = useState<'view' | 'search' | 'leads'>('view')\n const [viewId, setViewId] = useState('')\n const [text, setText] = useState('')\n const [picked, setPicked] = useState<Map<string, OutreachContactOption>>(\n new Map(),\n )\n // The leads picked from the sequence's site (AGL-3234), kept apart from\n // the contacts: the two are different records and the source names which.\n const [leadText, setLeadText] = useState('')\n const [pickedLeads, setPickedLeads] = useState<Map<string, OutreachContactOption>>(\n new Map(),\n )\n const [stage, setStage] = useState<Stage>({ kind: 'source' })\n const [problem, setProblem] = useState<string | null>(null)\n const [supplied, setSupplied] = useState<Record<string, Supplied>>({})\n const [emailPreview, setEmailPreview] = useState<\n Record<\n string,\n | { status: 'loading' }\n | { status: 'ready'; answer: OutreachPreviewResponse }\n | { status: 'failed'; message: string }\n >\n >({})\n\n const views = useOutreachSavedViews(props.open ? orgId : null, props.uid)\n const search = useOutreachContactSearch({\n orgId: props.open && tab === 'search' ? orgId : null,\n hostId: sequence.hostId,\n contactGroupId: props.contactGroupId,\n text,\n })\n const leadSearch = useOutreachLeadSearch({\n // The lead silo is the org's (AGL-3275); the site still narrows it.\n orgId,\n hostId: sequence.hostId,\n text: leadText,\n enabled: props.open && tab === 'leads',\n })\n\n const reset = () => {\n setStage({ kind: 'source' })\n setProblem(null)\n setSupplied({})\n setEmailPreview({})\n }\n const close = () => {\n reset()\n setPicked(new Map())\n setPickedLeads(new Map())\n setText('')\n setLeadText('')\n setViewId('')\n props.onClose()\n }\n\n const preview = async (source: OutreachEnrollSource) => {\n setProblem(null)\n setStage({ kind: 'previewing' })\n try {\n const answer = await api.previewEnrollment(sequence.id, source)\n setSupplied(\n Object.fromEntries(\n answer.people.map((person) => [\n person.personId,\n {\n include: person.status !== 'blocked',\n personalLine: '',\n attestations: [],\n },\n ]),\n ),\n )\n setStage({ kind: 'preview', answer })\n } catch (error) {\n setProblem((error as Error).message)\n setStage({ kind: 'source' })\n }\n }\n\n const answer =\n stage.kind === 'preview' || stage.kind === 'enrolling' ? stage.answer : null\n const ready = useMemo(\n () =>\n answer\n ? answer.people.filter((person) =>\n outreachPersonReady(person, supplied[person.personId]),\n )\n : [],\n [answer, supplied],\n )\n const waiting = answer\n ? answer.people.filter(\n (person) =>\n person.status === 'needs_confirmation' &&\n supplied[person.personId]?.include &&\n !outreachPersonReady(person, supplied[person.personId]),\n ).length\n : 0\n\n const enroll = async () => {\n if (!answer || !ready.length) return\n setProblem(null)\n setStage({ kind: 'enrolling', answer })\n try {\n const result = await api.enroll(\n sequence.id,\n ready.map((person) => ({\n // A contact by id, or a lead by its key (AGL-3234).\n ...(person.target === 'lead' && person.leadId\n ? { leadId: person.leadId }\n : { contactId: person.contactId }),\n personalLine: supplied[person.personId]?.personalLine.trim() ?? '',\n attestations: supplied[person.personId]?.attestations ?? [],\n })),\n )\n setStage({\n kind: 'done',\n results: result.results,\n enrolled: result.enrolled,\n })\n } catch (error) {\n setProblem((error as Error).message)\n setStage({ kind: 'preview', answer })\n }\n }\n\n const supply = (personId: string, next: Partial<Supplied>) =>\n setSupplied((previous) => ({\n ...previous,\n [personId]: { ...previous[personId], ...next } as Supplied,\n }))\n\n const previewEmail = async (person: OutreachEnrollPreviewPerson) => {\n setEmailPreview((previous) => ({\n ...previous,\n [person.personId]: { status: 'loading' },\n }))\n try {\n const result = await api.previewEmail({\n sequenceId: sequence.id,\n ...(person.target === 'lead' && person.leadId\n ? { leadId: person.leadId }\n : { contactId: person.contactId }),\n personalLine: supplied[person.personId]?.personalLine.trim() ?? '',\n })\n setEmailPreview((previous) => ({\n ...previous,\n [person.personId]: { status: 'ready', answer: result },\n }))\n } catch (error) {\n setEmailPreview((previous) => ({\n ...previous,\n [person.personId]: {\n status: 'failed',\n message: (error as Error).message,\n },\n }))\n }\n }\n\n const title = `Enroll people in ${sequence.name}`\n\n return (\n <Dialog\n open={props.open}\n onClose={close}\n fullWidth\n maxWidth=\"md\"\n fullScreen={narrow}\n aria-label={title}\n >\n <DialogTitle>\n <Stack direction=\"row\" spacing={0.5} sx={{ alignItems: 'center' }}>\n <span>{title}</span>\n <HelpTip {...pluginDocsHelp('sequences', { anchor: '#enroll' })} />\n </Stack>\n </DialogTitle>\n <DialogContent dividers>\n <Stack spacing={2}>\n {problem ? <Alert severity=\"error\">{problem}</Alert> : null}\n\n {stage.kind === 'source' ? (\n <>\n <Tabs\n value={tab}\n onChange={(_event, next) => setTab(next)}\n aria-label=\"Where the people come from\"\n >\n <Tab value=\"view\" label=\"Saved view\" />\n <Tab value=\"search\" label=\"Search\" />\n <Tab value=\"leads\" label=\"Leads\" />\n </Tabs>\n {tab === 'view' ? (\n views.status === 'loading' ? (\n <OutreachLoading label=\"Loading saved views…\" />\n ) : views.status === 'error' || views.status === 'refused' ? (\n <OutreachLoadProblem\n status={views.status}\n what=\"saved views\"\n message={\n views.status === 'refused'\n ? 'Enrolling reads your CRM’s contacts, and your role does not include Manage data.'\n : null\n }\n />\n ) : views.data.length ? (\n <TextField\n select\n label=\"Saved Contacts view\"\n value={viewId}\n onChange={(event) => setViewId(event.target.value)}\n helperText={`Enrolls up to ${OUTREACH_ENROLL_BATCH_MAX} of the people the view shows at this sequence’s site — a Contacts view, or a Leads view of the site’s leads.`}\n fullWidth\n >\n {views.data.map((view) => (\n <MenuItem key={view.id} value={view.id}>\n {view.section === 'leads' ? `${view.name} (Leads)` : view.name}\n </MenuItem>\n ))}\n </TextField>\n ) : (\n <Alert severity=\"info\">\n You have no saved Contacts views to enroll from. Save one in\n the CRM, or search instead.\n </Alert>\n )\n ) : tab === 'leads' ? (\n <Stack spacing={1.5}>\n <TextField\n label=\"Search leads\"\n placeholder=\"A name, or an email address\"\n value={leadText}\n onChange={(event) => setLeadText(event.target.value)}\n helperText=\"The open leads at this sequence’s site. A lead is enrolled as it is, and follows itself to the contact it becomes.\"\n fullWidth\n autoFocus\n />\n {leadSearch.status === 'loading' ? (\n <OutreachLoading label=\"Reading leads…\" />\n ) : null}\n {leadSearch.status === 'error' || leadSearch.status === 'refused' ? (\n <OutreachLoadProblem\n status={leadSearch.status}\n what=\"leads\"\n message={\n leadSearch.status === 'refused'\n ? 'Enrolling reads your CRM’s leads, and your role does not include Manage data.'\n : 'The leads could not be read. Try again.'\n }\n />\n ) : null}\n {leadSearch.status === 'ready' && !leadSearch.idle && !leadSearch.data.length ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n No open leads at this sequence’s site match.\n </Typography>\n ) : null}\n {leadSearch.data.length ? (\n <List dense aria-label=\"Lead results\">\n {leadSearch.data.map((lead) => {\n const checked = pickedLeads.has(lead.id)\n return (\n <ListItem key={lead.id} disablePadding>\n <ListItemButton\n onClick={() =>\n setPickedLeads((previous) => {\n const next = new Map(previous)\n if (checked) next.delete(lead.id)\n else if (next.size < OUTREACH_ENROLL_BATCH_MAX) next.set(lead.id, lead)\n return next\n })\n }\n >\n <ListItemIcon>\n <Checkbox edge=\"start\" checked={checked} tabIndex={-1} disableRipple />\n </ListItemIcon>\n <ListItemText\n primary={lead.name || lead.email || lead.id}\n secondary={lead.email}\n />\n </ListItemButton>\n </ListItem>\n )\n })}\n </List>\n ) : null}\n {pickedLeads.size ? (\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ flexWrap: 'wrap', rowGap: 1 }}\n aria-label=\"Picked leads\"\n >\n {[...pickedLeads.values()].map((lead) => (\n <Chip\n key={lead.id}\n size=\"small\"\n label={lead.name || lead.email || lead.id}\n onDelete={() =>\n setPickedLeads((previous) => {\n const next = new Map(previous)\n next.delete(lead.id)\n return next\n })\n }\n />\n ))}\n </Stack>\n ) : null}\n </Stack>\n ) : (\n <Stack spacing={1.5}>\n <TextField\n label=\"Search contacts\"\n placeholder=\"A name, or an email address\"\n value={text}\n onChange={(event) => setText(event.target.value)}\n fullWidth\n autoFocus\n />\n {search.status === 'loading' ? (\n <OutreachLoading label=\"Searching…\" />\n ) : null}\n {search.status === 'error' || search.status === 'refused' ? (\n <OutreachLoadProblem\n status={search.status}\n what=\"contacts\"\n message={\n search.status === 'refused'\n ? 'Enrolling reads your CRM’s contacts, and your role does not include Manage data.'\n : 'The search failed. Try again.'\n }\n />\n ) : null}\n {search.status === 'ready' &&\n !search.idle &&\n !search.data.length ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n No contacts at this sequence’s site match.\n </Typography>\n ) : null}\n {search.data.length ? (\n <List dense aria-label=\"Search results\">\n {search.data.map((contact) => {\n const checked = picked.has(contact.id)\n return (\n <ListItem key={contact.id} disablePadding>\n <ListItemButton\n onClick={() =>\n setPicked((previous) => {\n const next = new Map(previous)\n if (checked) next.delete(contact.id)\n else if (\n next.size < OUTREACH_ENROLL_BATCH_MAX\n )\n next.set(contact.id, contact)\n return next\n })\n }\n >\n <ListItemIcon>\n <Checkbox\n edge=\"start\"\n checked={checked}\n tabIndex={-1}\n disableRipple\n />\n </ListItemIcon>\n <ListItemText\n primary={\n contact.name || contact.email || contact.id\n }\n secondary={contact.email}\n />\n </ListItemButton>\n </ListItem>\n )\n })}\n </List>\n ) : null}\n {picked.size ? (\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ flexWrap: 'wrap', rowGap: 1 }}\n aria-label=\"Picked\"\n >\n {[...picked.values()].map((contact) => (\n <Chip\n key={contact.id}\n size=\"small\"\n label={contact.name || contact.email || contact.id}\n onDelete={() =>\n setPicked((previous) => {\n const next = new Map(previous)\n next.delete(contact.id)\n return next\n })\n }\n />\n ))}\n </Stack>\n ) : null}\n </Stack>\n )}\n </>\n ) : null}\n\n {stage.kind === 'previewing' ? (\n <OutreachLoading label=\"Checking each person…\" />\n ) : null}\n\n {answer ? (\n <Stack spacing={1.5}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {summary(answer)}\n </Typography>\n {answer.truncated ? (\n <Alert severity=\"info\">\n {`This shows the first ${answer.people.length} of ${answer.total}. Enroll these, then preview again for the rest.`}\n </Alert>\n ) : null}\n <Stack\n spacing={1}\n component=\"ul\"\n sx={{ listStyle: 'none', p: 0, m: 0 }}\n aria-label=\"People\"\n >\n {answer.people.map((person) => (\n <PersonRow\n key={person.personId}\n person={person}\n supplied={supplied[person.personId]}\n onSupply={(next) => supply(person.personId, next)}\n emailPreview={emailPreview[person.personId]}\n onPreviewEmail={() => void previewEmail(person)}\n disabled={stage.kind === 'enrolling'}\n />\n ))}\n </Stack>\n {waiting ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`${waiting} ${waiting === 1 ? 'person still needs' : 'people still need'} a personal line or a confirmation, and won't be enrolled until they have one.`}\n </Typography>\n ) : null}\n </Stack>\n ) : null}\n\n {stage.kind === 'done' ? (\n <DoneSummary results={stage.results} enrolled={stage.enrolled} />\n ) : null}\n </Stack>\n </DialogContent>\n <DialogActions>\n {stage.kind === 'done' ? (\n <Button variant=\"contained\" onClick={close}>\n Done\n </Button>\n ) : (\n <>\n <Button onClick={close}>Cancel</Button>\n {stage.kind === 'preview' || stage.kind === 'enrolling' ? (\n <>\n <Button onClick={reset} disabled={stage.kind === 'enrolling'}>\n Back\n </Button>\n <Button\n variant=\"contained\"\n disabled={!ready.length || stage.kind === 'enrolling'}\n onClick={() => void enroll()}\n >\n {stage.kind === 'enrolling'\n ? 'Enrolling…'\n : `Enroll ${ready.length} ${ready.length === 1 ? 'person' : 'people'}`}\n </Button>\n </>\n ) : (\n <Button\n variant=\"contained\"\n disabled={\n stage.kind === 'previewing' ||\n (tab === 'view' ? !viewId : tab === 'leads' ? !pickedLeads.size : !picked.size)\n }\n onClick={() =>\n void preview(\n tab === 'view'\n ? { kind: 'view', viewId }\n : tab === 'leads'\n ? { kind: 'leads', leadIds: [...pickedLeads.keys()] }\n : { kind: 'contacts', contactIds: [...picked.keys()] },\n )\n }\n >\n Check people\n </Button>\n )}\n </>\n )}\n </DialogActions>\n </Dialog>\n )\n}\nOutreachEnrollDialog.displayName = 'OutreachEnrollDialog'\n\nfunction summary(answer: OutreachEnrollPreviewResponse): string {\n const count = (status: OutreachEnrollPreviewStatus) =>\n answer.people.filter((person) => person.status === status).length\n return `${count('eligible')} eligible · ${count('needs_confirmation')} need you · ${count('blocked')} blocked`\n}\n\nfunction PersonRow(props: {\n person: OutreachEnrollPreviewPerson\n supplied: Supplied | undefined\n onSupply(next: Partial<Supplied>): void\n emailPreview:\n | { status: 'loading' }\n | { status: 'ready'; answer: OutreachPreviewResponse }\n | { status: 'failed'; message: string }\n | undefined\n onPreviewEmail(): void\n disabled: boolean\n}) {\n const { person, supplied, disabled } = props\n const name = person.name || person.email || person.personId\n const line = supplied?.personalLine ?? ''\n const tooLong = line.trim().length > OUTREACH_PERSONAL_LINE_MAX\n return (\n <Paper component=\"li\" variant=\"outlined\" sx={{ p: 1.5 }} aria-label={name}>\n <Stack spacing={1}>\n <Stack\n direction={{ xs: 'column', sm: 'row' }}\n spacing={1}\n sx={{ alignItems: { sm: 'center' } }}\n >\n <Stack sx={{ flexGrow: 1, minWidth: 0 }}>\n <Typography variant=\"body2\">{name}</Typography>\n {person.email && person.email !== name ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {person.email}\n </Typography>\n ) : null}\n </Stack>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n {person.target === 'lead' ? (\n <Chip size=\"small\" variant=\"outlined\" label=\"Lead\" />\n ) : null}\n {person.cold ? (\n <Chip size=\"small\" variant=\"outlined\" label=\"Cold\" />\n ) : null}\n <Chip\n size=\"small\"\n label={STATUS_LABELS[person.status]}\n color={STATUS_COLORS[person.status]}\n />\n </Stack>\n </Stack>\n {person.status === 'blocked' ? (\n <Stack component=\"ul\" spacing={0.5} sx={{ m: 0, pl: 2.5 }}>\n {person.blocks.map((block) => (\n <Typography\n key={block.code}\n component=\"li\"\n variant=\"body2\"\n color=\"text.secondary\"\n >\n {block.reason}\n </Typography>\n ))}\n </Stack>\n ) : (\n <Stack spacing={1}>\n <FormControlLabel\n control={\n <Checkbox\n checked={supplied?.include ?? true}\n onChange={(event) =>\n props.onSupply({ include: event.target.checked })\n }\n disabled={disabled}\n />\n }\n label=\"Enroll this person\"\n />\n {supplied?.include === false ? null : (\n <>\n <TextField\n label={\n person.requires.personalLine\n ? 'Personal line'\n : 'Personal line (optional)'\n }\n placeholder=\"One sentence on why you're writing to this person now.\"\n value={line}\n onChange={(event) =>\n props.onSupply({ personalLine: event.target.value })\n }\n disabled={disabled}\n required={person.requires.personalLine}\n error={tooLong}\n helperText={\n tooLong\n ? `Keep it to one sentence, under ${OUTREACH_PERSONAL_LINE_MAX} characters.`\n : 'Goes where the emails say {{enrollment.personalLine}}.'\n }\n fullWidth\n size=\"small\"\n />\n {person.requires.attestations.length ? (\n <FormGroup aria-label={`What you confirm about ${name}`}>\n {person.requires.attestations.map((kind) => (\n <FormControlLabel\n key={kind}\n control={\n <Checkbox\n checked={\n supplied?.attestations.includes(kind) ?? false\n }\n onChange={(event) =>\n props.onSupply({\n attestations: event.target.checked\n ? [...(supplied?.attestations ?? []), kind]\n : (supplied?.attestations ?? []).filter(\n (entry) => entry !== kind,\n ),\n })\n }\n disabled={disabled}\n />\n }\n label={OUTREACH_ATTESTATION_LABELS[kind]}\n />\n ))}\n </FormGroup>\n ) : null}\n <Stack direction=\"row\">\n <Button\n size=\"small\"\n onClick={props.onPreviewEmail}\n disabled={disabled}\n >\n Preview the first email\n </Button>\n </Stack>\n {props.emailPreview?.status === 'loading' ? (\n <OutreachLoading label=\"Writing the email…\" />\n ) : null}\n {props.emailPreview?.status === 'failed' ? (\n <Alert severity=\"error\">{props.emailPreview.message}</Alert>\n ) : null}\n {props.emailPreview?.status === 'ready' ? (\n props.emailPreview.answer.error ? (\n <Alert severity=\"info\">\n {props.emailPreview.answer.error.message}\n </Alert>\n ) : (\n <Paper\n variant=\"outlined\"\n sx={{ p: 1.5 }}\n aria-label={`First email to ${name}`}\n >\n <Typography variant=\"body2\">{`Subject: ${props.emailPreview.answer.subject}`}</Typography>\n <Typography\n variant=\"body2\"\n sx={{\n whiteSpace: 'pre-wrap',\n wordBreak: 'break-word',\n mt: 1,\n }}\n >\n {props.emailPreview.answer.text}\n </Typography>\n </Paper>\n )\n ) : null}\n </>\n )}\n </Stack>\n )}\n </Stack>\n </Paper>\n )\n}\n\nfunction DoneSummary(props: {\n results: OutreachEnrollOutcome[]\n enrolled: number\n}) {\n const refused = props.results.filter(\n (\n result,\n ): result is Extract<OutreachEnrollOutcome, { outcome: 'blocked' }> =>\n result.outcome === 'blocked',\n )\n return (\n <Stack spacing={1.5}>\n <Alert severity={props.enrolled ? 'success' : 'info'}>\n {props.enrolled\n ? `Enrolled ${props.enrolled} ${props.enrolled === 1 ? 'person' : 'people'}. Their first step goes out in the mailbox's sending hours.`\n : 'Nobody was enrolled.'}\n </Alert>\n {refused.length ? (\n <Stack spacing={1}>\n <Typography variant=\"subtitle2\">\n Not enrolled, checked again just now:\n </Typography>\n {refused.map((result) => (\n <Stack key={result.personId} spacing={0.25}>\n <Typography variant=\"body2\">\n {result.email ?? result.personId}\n </Typography>\n {result.blocks.map((block) => (\n <Typography\n key={block.code}\n variant=\"body2\"\n color=\"text.secondary\"\n >\n {block.reason}\n </Typography>\n ))}\n </Stack>\n ))}\n </Stack>\n ) : null}\n </Stack>\n )\n}\n\nexport default OutreachEnrollDialog\n"],"names":["pluginDocsHelp","HelpTip","Alert","Button","Checkbox","Chip","Dialog","DialogActions","DialogContent","DialogTitle","FormControlLabel","FormGroup","List","ListItem","ListItemButton","ListItemIcon","ListItemText","MenuItem","Paper","Stack","Tab","Tabs","TextField","Typography","useMediaQuery","useTheme","useMemo","useState","OUTREACH_ENROLL_BATCH_MAX","OUTREACH_ATTESTATION_LABELS","OUTREACH_PERSONAL_LINE_MAX","OutreachLoading","OutreachLoadProblem","useOutreachContactSearch","useOutreachLeadSearch","useOutreachSavedViews","STATUS_LABELS","eligible","needs_confirmation","blocked","STATUS_COLORS","outreachPersonReady","person","supplied","status","include","line","personalLine","trim","length","requires","attestations","every","kind","includes","OutreachEnrollDialog","props","orgId","sequence","api","theme","narrow","breakpoints","down","tab","setTab","viewId","setViewId","text","setText","picked","setPicked","Map","leadText","setLeadText","pickedLeads","setPickedLeads","stage","setStage","problem","setProblem","setSupplied","emailPreview","setEmailPreview","views","open","uid","search","hostId","contactGroupId","leadSearch","enabled","reset","close","onClose","preview","source","answer","previewEnrollment","id","Object","fromEntries","people","map","personId","error","message","ready","filter","waiting","enroll","result","target","leadId","contactId","results","enrolled","supply","next","previous","previewEmail","sequenceId","title","name","fullWidth","maxWidth","fullScreen","aria-label","direction","spacing","sx","alignItems","span","anchor","dividers","severity","value","onChange","_event","label","what","data","select","event","helperText","view","section","placeholder","autoFocus","idle","variant","color","dense","lead","checked","has","disablePadding","onClick","delete","size","set","edge","tabIndex","disableRipple","primary","email","secondary","flexWrap","rowGap","values","onDelete","contact","summary","truncated","total","component","listStyle","p","m","PersonRow","onSupply","onPreviewEmail","disabled","DoneSummary","leadIds","keys","contactIds","displayName","count","tooLong","xs","sm","flexGrow","minWidth","cold","pl","blocks","block","reason","code","control","required","entry","subject","whiteSpace","wordBreak","mt","refused","outcome"],"mappings":"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"}
@@ -61,6 +61,7 @@ export declare const OUTREACH_LEADS_WINDOW = 200;
61
61
  * enroll. `idle` until something is typed.
62
62
  */
63
63
  export declare function useOutreachLeadSearch(input: {
64
+ orgId: string | null;
64
65
  hostId: string | null;
65
66
  text: string;
66
67
  enabled: boolean;
@@ -20,6 +20,7 @@ import { CRM_COLLECTIONS, crmLeadDisplayName, crmViewIsListed, isCrmLeadOpen, no
20
20
  import { CRM_EMAIL_TEMPLATES_LIMIT, crmEmailTemplateIsListed, normalizeCrmEmailTemplate } from "@aglyn/aglyn/app-utils/crm-email-templates";
21
21
  import { dynamicListDimensionsForCrmView } from "@aglyn/aglyn/app-utils/dynamic-list-rule";
22
22
  import { nameSearchToken } from "@aglyn/aglyn/app-utils/name-search";
23
+ import { scopeTokensForHost } from "@aglyn/aglyn/app-utils/scope-tokens";
23
24
  import { visibleToHost } from "@aglyn/aglyn/app-utils/scope-tokens";
24
25
  import { useFirestore } from "@aglyn/tenant-feature-instance";
25
26
  import { collection, getDocs, limit, onSnapshot, orderBy, query, where } from "firebase/firestore";
@@ -211,14 +212,14 @@ import { useEffect, useState } from "react";
211
212
  * closed leads are left out: they are a contact's, or a verdict's, to
212
213
  * enroll. `idle` until something is typed.
213
214
  */ export function useOutreachLeadSearch(input) {
214
- const { hostId, text, enabled } = input;
215
+ const { orgId, hostId, text, enabled } = input;
215
216
  const firestore = useFirestore();
216
217
  const [window, setWindow] = useState({
217
218
  status: 'ready',
218
219
  data: []
219
220
  });
220
221
  useEffect(()=>{
221
- if (!enabled || !hostId) {
222
+ if (!enabled || !hostId || !orgId) {
222
223
  setWindow({
223
224
  status: 'ready',
224
225
  data: []
@@ -230,7 +231,10 @@ import { useEffect, useState } from "react";
230
231
  status: 'loading',
231
232
  data: []
232
233
  });
233
- getDocs(query(collection(firestore, 'hosts', hostId, 'leads'), orderBy('lastSeenAtMs', 'desc'), limit(OUTREACH_LEADS_WINDOW))).then((snapshot)=>{
234
+ getDocs(query(// The org collection, narrowed to the site this view belongs to
235
+ // (AGL-3275) — unscoped it would offer one agency client another
236
+ // client's people to enroll.
237
+ collection(firestore, 'orgs', orgId, 'leads'), where('visibleTo', 'array-contains-any', scopeTokensForHost(hostId)), orderBy('lastSeenAtMs', 'desc'), limit(OUTREACH_LEADS_WINDOW))).then((snapshot)=>{
234
238
  if (!current) return;
235
239
  setWindow({
236
240
  status: 'ready',
@@ -256,6 +260,7 @@ import { useEffect, useState } from "react";
256
260
  };
257
261
  }, [
258
262
  firestore,
263
+ orgId,
259
264
  hostId,
260
265
  enabled
261
266
  ]);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/components/use-outreach-crm.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 contactDisplayName,\n normalizeContactEmail,\n} from '@aglyn/aglyn/app-utils/contacts'\nimport {\n CRM_COLLECTIONS,\n crmLeadDisplayName,\n crmViewIsListed,\n isCrmLeadOpen,\n normalizeCrmViewFilters,\n} from '@aglyn/aglyn/app-utils/crm'\nimport {\n CRM_EMAIL_TEMPLATES_LIMIT,\n crmEmailTemplateIsListed,\n normalizeCrmEmailTemplate,\n} from '@aglyn/aglyn/app-utils/crm-email-templates'\nimport { dynamicListDimensionsForCrmView } from '@aglyn/aglyn/app-utils/dynamic-list-rule'\nimport { nameSearchToken } from '@aglyn/aglyn/app-utils/name-search'\nimport { visibleToHost } from '@aglyn/aglyn/app-utils/scope-tokens'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport {\n collection,\n getDocs,\n limit,\n onSnapshot,\n orderBy,\n query,\n where,\n} from 'firebase/firestore'\nimport { useEffect, useState } from 'react'\nimport type { OutreachLoad } from './use-outreach-data'\n\n/**\n * What Outreach reads from the organization's CRM (AGL-2980): the saved\n * Contacts views people can be enrolled from, the email templates a step\n * can send, and the contacts a search finds.\n *\n * Through the core's own CRM model — the collection names, the listing\n * rules, the view translation the dynamic-list sweep uses — and never\n * through the CRM plugin, which a plugin may not import. The Firestore\n * rules gate every one of these reads on `data.manage`, the same permission\n * the enroll routes ask for, so a member who cannot read the people cannot\n * enroll them either.\n */\n\nconst denied = (error: unknown) =>\n (error as { code?: unknown } | null)?.code === 'permission-denied'\n\n/** A saved Contacts or Leads view people can be enrolled from (AGL-3234). */\nexport interface OutreachViewOption {\n id: string\n name: string\n /** Which list the view is of; a Leads view selects the sequence's site's leads. */\n section: 'contacts' | 'leads'\n}\n\n/** The most saved views the picker lists. */\nexport const OUTREACH_VIEWS_LIMIT = 50\n\n/**\n * The saved Contacts views this reader may enroll from: their own and the\n * shared ones — a colleague's private view is theirs — that translate whole\n * into an audience. A view filtering on something enrolling cannot apply is\n * not offered, because dropping that filter would enroll more people than\n * the view shows.\n */\nexport function useOutreachSavedViews(\n orgId: string | null,\n uid: string | null,\n): OutreachLoad<OutreachViewOption[]> {\n const firestore = useFirestore()\n const [result, setResult] = useState<OutreachLoad<OutreachViewOption[]>>({\n status: 'loading',\n data: [],\n })\n useEffect(() => {\n setResult({ status: 'loading', data: [] })\n if (!orgId) return undefined\n return onSnapshot(\n query(\n collection(firestore, 'orgs', orgId, CRM_COLLECTIONS.views),\n where('section', 'in', ['contacts', 'leads']),\n orderBy('name'),\n limit(OUTREACH_VIEWS_LIMIT),\n ),\n (snapshot) =>\n setResult({\n status: 'ready',\n data: snapshot.docs\n .map((entry) => ({\n id: entry.id,\n data: entry.data() as Record<string, unknown>,\n }))\n .filter(\n ({ data }) =>\n typeof data['name'] === 'string' &&\n data['name'] !== '' &&\n crmViewIsListed(\n {\n shared: data['shared'] === true,\n ownerUid: String(data['ownerUid'] ?? ''),\n },\n uid,\n ) &&\n // A Leads view narrows by status alone, which enrolling can\n // always apply; a Contacts view must be readable as a list.\n (data['section'] === 'leads' ||\n dynamicListDimensionsForCrmView(\n normalizeCrmViewFilters(data['filters']),\n ).unsupported.length === 0),\n )\n .map(({ id, data }) => ({\n id,\n name: String(data['name']),\n section: data['section'] === 'leads' ? ('leads' as const) : ('contacts' as const),\n })),\n }),\n (error) => {\n if (!denied(error))\n console.error('[outreach] saved views could not be read', error)\n setResult({ status: denied(error) ? 'refused' : 'error', data: [] })\n },\n )\n }, [firestore, orgId, uid])\n return result\n}\n\n/** A CRM email template a step can send the body of. */\nexport interface OutreachTemplateOption {\n id: string\n name: string\n subject: string\n body: string\n}\n\n/**\n * The CRM email templates a step at this site can use: whole letters, not\n * snippets; the team's and this reader's own; written for the whole\n * organization or for this site.\n */\nexport function useOutreachEmailTemplates(\n orgId: string | null,\n hostId: string | null,\n uid: string | null,\n): OutreachLoad<OutreachTemplateOption[]> {\n const firestore = useFirestore()\n const [result, setResult] = useState<OutreachLoad<OutreachTemplateOption[]>>({\n status: 'loading',\n data: [],\n })\n useEffect(() => {\n setResult({ status: 'loading', data: [] })\n if (!orgId) return undefined\n return onSnapshot(\n query(\n collection(firestore, 'orgs', orgId, CRM_COLLECTIONS.emailTemplates),\n orderBy('name'),\n limit(CRM_EMAIL_TEMPLATES_LIMIT),\n ),\n (snapshot) =>\n setResult({\n status: 'ready',\n data: snapshot.docs\n .map((entry) => ({\n id: entry.id,\n template: normalizeCrmEmailTemplate(entry.data()),\n }))\n .filter(\n ({ template }) =>\n template.kind === 'template' &&\n crmEmailTemplateIsListed(template, uid) &&\n (!hostId || visibleToHost(template.visibleTo, hostId)),\n )\n .map(({ id, template }) => ({\n id,\n name: template.name,\n subject: template.subject,\n body: template.body,\n })),\n }),\n (error) => {\n if (!denied(error))\n console.error('[outreach] email templates could not be read', error)\n setResult({ status: denied(error) ? 'refused' : 'error', data: [] })\n },\n )\n }, [firestore, orgId, hostId, uid])\n return result\n}\n\n/** A contact a search found. */\nexport interface OutreachContactOption {\n id: string\n name: string\n email: string | null\n}\n\n/** The most contacts one search answers with. */\nexport const OUTREACH_SEARCH_LIMIT = 25\n\n/**\n * The contacts at `hostId` a search finds: by address when the text is one,\n * otherwise by a word of the name — the `nameTokens` the contacts carry for\n * the CRM's own search. `idle` until something is typed.\n */\nexport function useOutreachContactSearch(input: {\n orgId: string | null\n hostId: string | null\n contactGroupId: string | null\n text: string\n}): OutreachLoad<OutreachContactOption[]> & { idle: boolean } {\n const { orgId, hostId, contactGroupId, text } = input\n const firestore = useFirestore()\n const [result, setResult] = useState<OutreachLoad<OutreachContactOption[]>>({\n status: 'ready',\n data: [],\n })\n const email = normalizeContactEmail(text)\n const token = email ? '' : nameSearchToken(text)\n const idle = !orgId || !hostId || (!email && !token)\n useEffect(() => {\n if (idle || !orgId || !hostId) {\n setResult({ status: 'ready', data: [] })\n return undefined\n }\n let current = true\n setResult((previous) => ({ status: 'loading', data: previous.data }))\n const contacts = collection(firestore, 'orgs', orgId, 'contacts')\n // Settles the typing before it reads: one query per pause, not per key.\n const timer = setTimeout(() => {\n getDocs(\n email\n ? query(\n contacts,\n where('email', '==', email),\n limit(OUTREACH_SEARCH_LIMIT),\n )\n : query(\n contacts,\n where('nameTokens', 'array-contains', token),\n orderBy('nameLower'),\n limit(OUTREACH_SEARCH_LIMIT),\n ),\n )\n .then((snapshot) => {\n if (!current) return\n setResult({\n status: 'ready',\n data: snapshot.docs\n .map((entry) => ({\n id: entry.id,\n data: entry.data() as Record<string, unknown>,\n }))\n .filter(({ data }) =>\n visibleToHost(\n data['visibleTo'] as string[] | undefined,\n hostId,\n ),\n )\n .map(({ id, data }) => ({\n id,\n name: contactDisplayName(data, contactGroupId ?? hostId).trim(),\n email: normalizeContactEmail(data['email']),\n })),\n })\n })\n .catch((error: unknown) => {\n if (!current) return\n if (!denied(error))\n console.error('[outreach] the contact search failed', error)\n setResult({ status: denied(error) ? 'refused' : 'error', data: [] })\n })\n }, 300)\n return () => {\n current = false\n clearTimeout(timer)\n }\n }, [firestore, orgId, hostId, contactGroupId, email, token, idle])\n return { ...result, idle }\n}\n\n/** The most leads the picker reads — the Leads list's own window. */\nexport const OUTREACH_LEADS_WINDOW = 200\n\n/**\n * The open leads at `hostId` a search finds (AGL-3234): the site's most\n * recently seen leads, read once while the tab is open and narrowed in\n * the browser by a word of the name or the address — a lead carries no\n * search tokens, and the window is the Leads list's own. Converted and\n * closed leads are left out: they are a contact's, or a verdict's, to\n * enroll. `idle` until something is typed.\n */\nexport function useOutreachLeadSearch(input: {\n hostId: string | null\n text: string\n enabled: boolean\n}): OutreachLoad<OutreachContactOption[]> & { idle: boolean } {\n const { hostId, text, enabled } = input\n const firestore = useFirestore()\n const [window, setWindow] = useState<OutreachLoad<OutreachContactOption[]>>({\n status: 'ready',\n data: [],\n })\n useEffect(() => {\n if (!enabled || !hostId) {\n setWindow({ status: 'ready', data: [] })\n return undefined\n }\n let current = true\n setWindow({ status: 'loading', data: [] })\n getDocs(\n query(\n collection(firestore, 'hosts', hostId, 'leads'),\n orderBy('lastSeenAtMs', 'desc'),\n limit(OUTREACH_LEADS_WINDOW),\n ),\n )\n .then((snapshot) => {\n if (!current) return\n setWindow({\n status: 'ready',\n data: snapshot.docs\n .map((entry) => ({ id: entry.id, data: entry.data() as Record<string, unknown> }))\n .filter(({ data }) => isCrmLeadOpen(data as never) && !data['convertedContactId'])\n .map(({ id, data }) => ({\n id,\n name: crmLeadDisplayName(data),\n email: normalizeContactEmail(data['email']),\n })),\n })\n })\n .catch((error: unknown) => {\n if (!current) return\n if (!denied(error)) console.error('[outreach] the leads could not be read', error)\n setWindow({ status: denied(error) ? 'refused' : 'error', data: [] })\n })\n return () => {\n current = false\n }\n }, [firestore, hostId, enabled])\n const needle = text.trim().toLowerCase()\n const idle = !hostId || !needle\n const data = idle\n ? []\n : window.data\n .filter(\n (lead) =>\n lead.name.toLowerCase().includes(needle) ||\n (lead.email ?? '').includes(needle),\n )\n .slice(0, OUTREACH_SEARCH_LIMIT)\n return { ...window, data, idle }\n}\n"],"names":["contactDisplayName","normalizeContactEmail","CRM_COLLECTIONS","crmLeadDisplayName","crmViewIsListed","isCrmLeadOpen","normalizeCrmViewFilters","CRM_EMAIL_TEMPLATES_LIMIT","crmEmailTemplateIsListed","normalizeCrmEmailTemplate","dynamicListDimensionsForCrmView","nameSearchToken","visibleToHost","useFirestore","collection","getDocs","limit","onSnapshot","orderBy","query","where","useEffect","useState","denied","error","code","OUTREACH_VIEWS_LIMIT","useOutreachSavedViews","orgId","uid","firestore","result","setResult","status","data","undefined","views","snapshot","docs","map","entry","id","filter","shared","ownerUid","String","unsupported","length","name","section","console","useOutreachEmailTemplates","hostId","emailTemplates","template","kind","visibleTo","subject","body","OUTREACH_SEARCH_LIMIT","useOutreachContactSearch","input","contactGroupId","text","email","token","idle","current","previous","contacts","timer","setTimeout","then","trim","catch","clearTimeout","OUTREACH_LEADS_WINDOW","useOutreachLeadSearch","enabled","window","setWindow","needle","toLowerCase","lead","includes","slice"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/components/use-outreach-crm.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 contactDisplayName,\n normalizeContactEmail,\n} from '@aglyn/aglyn/app-utils/contacts'\nimport {\n CRM_COLLECTIONS,\n crmLeadDisplayName,\n crmViewIsListed,\n isCrmLeadOpen,\n normalizeCrmViewFilters,\n} from '@aglyn/aglyn/app-utils/crm'\nimport {\n CRM_EMAIL_TEMPLATES_LIMIT,\n crmEmailTemplateIsListed,\n normalizeCrmEmailTemplate,\n} from '@aglyn/aglyn/app-utils/crm-email-templates'\nimport { dynamicListDimensionsForCrmView } from '@aglyn/aglyn/app-utils/dynamic-list-rule'\nimport { nameSearchToken } from '@aglyn/aglyn/app-utils/name-search'\nimport { scopeTokensForHost } from '@aglyn/aglyn/app-utils/scope-tokens'\nimport { visibleToHost } from '@aglyn/aglyn/app-utils/scope-tokens'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport {\n collection,\n getDocs,\n limit,\n onSnapshot,\n orderBy,\n query,\n where,\n} from 'firebase/firestore'\nimport { useEffect, useState } from 'react'\nimport type { OutreachLoad } from './use-outreach-data'\n\n/**\n * What Outreach reads from the organization's CRM (AGL-2980): the saved\n * Contacts views people can be enrolled from, the email templates a step\n * can send, and the contacts a search finds.\n *\n * Through the core's own CRM model — the collection names, the listing\n * rules, the view translation the dynamic-list sweep uses — and never\n * through the CRM plugin, which a plugin may not import. The Firestore\n * rules gate every one of these reads on `data.manage`, the same permission\n * the enroll routes ask for, so a member who cannot read the people cannot\n * enroll them either.\n */\n\nconst denied = (error: unknown) =>\n (error as { code?: unknown } | null)?.code === 'permission-denied'\n\n/** A saved Contacts or Leads view people can be enrolled from (AGL-3234). */\nexport interface OutreachViewOption {\n id: string\n name: string\n /** Which list the view is of; a Leads view selects the sequence's site's leads. */\n section: 'contacts' | 'leads'\n}\n\n/** The most saved views the picker lists. */\nexport const OUTREACH_VIEWS_LIMIT = 50\n\n/**\n * The saved Contacts views this reader may enroll from: their own and the\n * shared ones — a colleague's private view is theirs — that translate whole\n * into an audience. A view filtering on something enrolling cannot apply is\n * not offered, because dropping that filter would enroll more people than\n * the view shows.\n */\nexport function useOutreachSavedViews(\n orgId: string | null,\n uid: string | null,\n): OutreachLoad<OutreachViewOption[]> {\n const firestore = useFirestore()\n const [result, setResult] = useState<OutreachLoad<OutreachViewOption[]>>({\n status: 'loading',\n data: [],\n })\n useEffect(() => {\n setResult({ status: 'loading', data: [] })\n if (!orgId) return undefined\n return onSnapshot(\n query(\n collection(firestore, 'orgs', orgId, CRM_COLLECTIONS.views),\n where('section', 'in', ['contacts', 'leads']),\n orderBy('name'),\n limit(OUTREACH_VIEWS_LIMIT),\n ),\n (snapshot) =>\n setResult({\n status: 'ready',\n data: snapshot.docs\n .map((entry) => ({\n id: entry.id,\n data: entry.data() as Record<string, unknown>,\n }))\n .filter(\n ({ data }) =>\n typeof data['name'] === 'string' &&\n data['name'] !== '' &&\n crmViewIsListed(\n {\n shared: data['shared'] === true,\n ownerUid: String(data['ownerUid'] ?? ''),\n },\n uid,\n ) &&\n // A Leads view narrows by status alone, which enrolling can\n // always apply; a Contacts view must be readable as a list.\n (data['section'] === 'leads' ||\n dynamicListDimensionsForCrmView(\n normalizeCrmViewFilters(data['filters']),\n ).unsupported.length === 0),\n )\n .map(({ id, data }) => ({\n id,\n name: String(data['name']),\n section: data['section'] === 'leads' ? ('leads' as const) : ('contacts' as const),\n })),\n }),\n (error) => {\n if (!denied(error))\n console.error('[outreach] saved views could not be read', error)\n setResult({ status: denied(error) ? 'refused' : 'error', data: [] })\n },\n )\n }, [firestore, orgId, uid])\n return result\n}\n\n/** A CRM email template a step can send the body of. */\nexport interface OutreachTemplateOption {\n id: string\n name: string\n subject: string\n body: string\n}\n\n/**\n * The CRM email templates a step at this site can use: whole letters, not\n * snippets; the team's and this reader's own; written for the whole\n * organization or for this site.\n */\nexport function useOutreachEmailTemplates(\n orgId: string | null,\n hostId: string | null,\n uid: string | null,\n): OutreachLoad<OutreachTemplateOption[]> {\n const firestore = useFirestore()\n const [result, setResult] = useState<OutreachLoad<OutreachTemplateOption[]>>({\n status: 'loading',\n data: [],\n })\n useEffect(() => {\n setResult({ status: 'loading', data: [] })\n if (!orgId) return undefined\n return onSnapshot(\n query(\n collection(firestore, 'orgs', orgId, CRM_COLLECTIONS.emailTemplates),\n orderBy('name'),\n limit(CRM_EMAIL_TEMPLATES_LIMIT),\n ),\n (snapshot) =>\n setResult({\n status: 'ready',\n data: snapshot.docs\n .map((entry) => ({\n id: entry.id,\n template: normalizeCrmEmailTemplate(entry.data()),\n }))\n .filter(\n ({ template }) =>\n template.kind === 'template' &&\n crmEmailTemplateIsListed(template, uid) &&\n (!hostId || visibleToHost(template.visibleTo, hostId)),\n )\n .map(({ id, template }) => ({\n id,\n name: template.name,\n subject: template.subject,\n body: template.body,\n })),\n }),\n (error) => {\n if (!denied(error))\n console.error('[outreach] email templates could not be read', error)\n setResult({ status: denied(error) ? 'refused' : 'error', data: [] })\n },\n )\n }, [firestore, orgId, hostId, uid])\n return result\n}\n\n/** A contact a search found. */\nexport interface OutreachContactOption {\n id: string\n name: string\n email: string | null\n}\n\n/** The most contacts one search answers with. */\nexport const OUTREACH_SEARCH_LIMIT = 25\n\n/**\n * The contacts at `hostId` a search finds: by address when the text is one,\n * otherwise by a word of the name — the `nameTokens` the contacts carry for\n * the CRM's own search. `idle` until something is typed.\n */\nexport function useOutreachContactSearch(input: {\n orgId: string | null\n hostId: string | null\n contactGroupId: string | null\n text: string\n}): OutreachLoad<OutreachContactOption[]> & { idle: boolean } {\n const { orgId, hostId, contactGroupId, text } = input\n const firestore = useFirestore()\n const [result, setResult] = useState<OutreachLoad<OutreachContactOption[]>>({\n status: 'ready',\n data: [],\n })\n const email = normalizeContactEmail(text)\n const token = email ? '' : nameSearchToken(text)\n const idle = !orgId || !hostId || (!email && !token)\n useEffect(() => {\n if (idle || !orgId || !hostId) {\n setResult({ status: 'ready', data: [] })\n return undefined\n }\n let current = true\n setResult((previous) => ({ status: 'loading', data: previous.data }))\n const contacts = collection(firestore, 'orgs', orgId, 'contacts')\n // Settles the typing before it reads: one query per pause, not per key.\n const timer = setTimeout(() => {\n getDocs(\n email\n ? query(\n contacts,\n where('email', '==', email),\n limit(OUTREACH_SEARCH_LIMIT),\n )\n : query(\n contacts,\n where('nameTokens', 'array-contains', token),\n orderBy('nameLower'),\n limit(OUTREACH_SEARCH_LIMIT),\n ),\n )\n .then((snapshot) => {\n if (!current) return\n setResult({\n status: 'ready',\n data: snapshot.docs\n .map((entry) => ({\n id: entry.id,\n data: entry.data() as Record<string, unknown>,\n }))\n .filter(({ data }) =>\n visibleToHost(\n data['visibleTo'] as string[] | undefined,\n hostId,\n ),\n )\n .map(({ id, data }) => ({\n id,\n name: contactDisplayName(data, contactGroupId ?? hostId).trim(),\n email: normalizeContactEmail(data['email']),\n })),\n })\n })\n .catch((error: unknown) => {\n if (!current) return\n if (!denied(error))\n console.error('[outreach] the contact search failed', error)\n setResult({ status: denied(error) ? 'refused' : 'error', data: [] })\n })\n }, 300)\n return () => {\n current = false\n clearTimeout(timer)\n }\n }, [firestore, orgId, hostId, contactGroupId, email, token, idle])\n return { ...result, idle }\n}\n\n/** The most leads the picker reads — the Leads list's own window. */\nexport const OUTREACH_LEADS_WINDOW = 200\n\n/**\n * The open leads at `hostId` a search finds (AGL-3234): the site's most\n * recently seen leads, read once while the tab is open and narrowed in\n * the browser by a word of the name or the address — a lead carries no\n * search tokens, and the window is the Leads list's own. Converted and\n * closed leads are left out: they are a contact's, or a verdict's, to\n * enroll. `idle` until something is typed.\n */\nexport function useOutreachLeadSearch(input: {\n orgId: string | null\n hostId: string | null\n text: string\n enabled: boolean\n}): OutreachLoad<OutreachContactOption[]> & { idle: boolean } {\n const { orgId, hostId, text, enabled } = input\n const firestore = useFirestore()\n const [window, setWindow] = useState<OutreachLoad<OutreachContactOption[]>>({\n status: 'ready',\n data: [],\n })\n useEffect(() => {\n if (!enabled || !hostId || !orgId) {\n setWindow({ status: 'ready', data: [] })\n return undefined\n }\n let current = true\n setWindow({ status: 'loading', data: [] })\n getDocs(\n query(\n // The org collection, narrowed to the site this view belongs to\n // (AGL-3275) — unscoped it would offer one agency client another\n // client's people to enroll.\n collection(firestore, 'orgs', orgId, 'leads'),\n where('visibleTo', 'array-contains-any', scopeTokensForHost(hostId)),\n orderBy('lastSeenAtMs', 'desc'),\n limit(OUTREACH_LEADS_WINDOW),\n ),\n )\n .then((snapshot) => {\n if (!current) return\n setWindow({\n status: 'ready',\n data: snapshot.docs\n .map((entry) => ({ id: entry.id, data: entry.data() as Record<string, unknown> }))\n .filter(({ data }) => isCrmLeadOpen(data as never) && !data['convertedContactId'])\n .map(({ id, data }) => ({\n id,\n name: crmLeadDisplayName(data),\n email: normalizeContactEmail(data['email']),\n })),\n })\n })\n .catch((error: unknown) => {\n if (!current) return\n if (!denied(error)) console.error('[outreach] the leads could not be read', error)\n setWindow({ status: denied(error) ? 'refused' : 'error', data: [] })\n })\n return () => {\n current = false\n }\n }, [firestore, orgId, hostId, enabled])\n const needle = text.trim().toLowerCase()\n const idle = !hostId || !needle\n const data = idle\n ? []\n : window.data\n .filter(\n (lead) =>\n lead.name.toLowerCase().includes(needle) ||\n (lead.email ?? '').includes(needle),\n )\n .slice(0, OUTREACH_SEARCH_LIMIT)\n return { ...window, data, idle }\n}\n"],"names":["contactDisplayName","normalizeContactEmail","CRM_COLLECTIONS","crmLeadDisplayName","crmViewIsListed","isCrmLeadOpen","normalizeCrmViewFilters","CRM_EMAIL_TEMPLATES_LIMIT","crmEmailTemplateIsListed","normalizeCrmEmailTemplate","dynamicListDimensionsForCrmView","nameSearchToken","scopeTokensForHost","visibleToHost","useFirestore","collection","getDocs","limit","onSnapshot","orderBy","query","where","useEffect","useState","denied","error","code","OUTREACH_VIEWS_LIMIT","useOutreachSavedViews","orgId","uid","firestore","result","setResult","status","data","undefined","views","snapshot","docs","map","entry","id","filter","shared","ownerUid","String","unsupported","length","name","section","console","useOutreachEmailTemplates","hostId","emailTemplates","template","kind","visibleTo","subject","body","OUTREACH_SEARCH_LIMIT","useOutreachContactSearch","input","contactGroupId","text","email","token","idle","current","previous","contacts","timer","setTimeout","then","trim","catch","clearTimeout","OUTREACH_LEADS_WINDOW","useOutreachLeadSearch","enabled","window","setWindow","needle","toLowerCase","lead","includes","slice"],"mappings":"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"}