@aglyn/plugins-outreach 1.0.0-beta.196 → 1.0.0-beta.197
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 +14 -14
- package/src/lib/components/enroll-dialog.js +60 -1
- package/src/lib/components/enroll-dialog.js.map +1 -1
- package/src/lib/components/mailbox-card.js +11 -2
- package/src/lib/components/mailbox-card.js.map +1 -1
- package/src/lib/components/sequence-detail.js +1 -0
- package/src/lib/components/sequence-detail.js.map +1 -1
- package/src/lib/components/sequence-editor.d.ts +2 -0
- package/src/lib/components/sequence-editor.js +26 -2
- package/src/lib/components/sequence-editor.js.map +1 -1
- package/src/lib/components/sequence-step-card.d.ts +7 -0
- package/src/lib/components/sequence-step-card.js +37 -14
- package/src/lib/components/sequence-step-card.js.map +1 -1
- package/src/lib/components/sequences-section.js +12 -3
- package/src/lib/components/sequences-section.js.map +1 -1
- package/src/lib/components/step-test-dialog.d.ts +31 -0
- package/src/lib/components/step-test-dialog.js +247 -0
- package/src/lib/components/step-test-dialog.js.map +1 -0
- package/src/lib/components/use-outreach-api.d.ts +8 -1
- package/src/lib/components/use-outreach-api.js +4 -0
- package/src/lib/components/use-outreach-api.js.map +1 -1
- package/src/lib/components/use-outreach-data.d.ts +6 -1
- package/src/lib/components/use-outreach-data.js +6 -3
- package/src/lib/components/use-outreach-data.js.map +1 -1
- package/src/lib/constants/api-routes.d.ts +7 -0
- package/src/lib/constants/api-routes.js +6 -0
- package/src/lib/constants/api-routes.js.map +1 -1
- package/src/lib/model/outreach-api.d.ts +21 -1
- package/src/lib/model/outreach-api.js.map +1 -1
- package/src/lib/model/outreach.types.d.ts +6 -0
- package/src/lib/model/outreach.types.js.map +1 -1
- package/src/lib/model/step-preview.d.ts +7 -1
- package/src/lib/model/step-preview.js +3 -2
- package/src/lib/model/step-preview.js.map +1 -1
- package/src/lib/routes/platform-deps.d.ts +2 -2
- package/src/lib/routes/platform-deps.js +2 -2
- package/src/lib/routes/platform-deps.js.map +1 -1
- package/src/lib/routes/preview-routes.d.ts +24 -0
- package/src/lib/routes/preview-routes.js +99 -95
- package/src/lib/routes/preview-routes.js.map +1 -1
- package/src/lib/routes/register-routes.d.ts +7 -0
- package/src/lib/routes/register-routes.js +24 -0
- package/src/lib/routes/register-routes.js.map +1 -1
- package/src/lib/routes/step-test-routes.d.ts +88 -0
- package/src/lib/routes/step-test-routes.js +275 -0
- package/src/lib/routes/step-test-routes.js.map +1 -0
- package/src/lib/runtime/click-events.js +10 -0
- package/src/lib/runtime/click-events.js.map +1 -1
- package/src/lib/runtime/click-link.d.ts +11 -0
- package/src/lib/runtime/click-link.js +9 -4
- package/src/lib/runtime/click-link.js.map +1 -1
- package/src/lib/runtime/click-route.js +3 -0
- package/src/lib/runtime/click-route.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/components/sequence-editor.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, type ConsolePluginOrgMount } from '@aglyn/aglyn'\nimport { mdiPlus } from '@aglyn/shared-data-mdi'\nimport { CardDisplay, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport CampaignPicker from '@aglyn/shared-ui-email-campaigns/components/campaign-picker.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { useOrgCampaigns, useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Autocomplete,\n Box,\n Button,\n Chip,\n FormControlLabel,\n Grid,\n Menu,\n MenuItem,\n Stack,\n Switch,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useEffect, useMemo, useState } from 'react'\nimport {\n isInThreadEmailStep,\n OUTREACH_SEQUENCE_NAME_MAX,\n validateOutreachSequence,\n} from '../engine/sequence-validation'\nimport { outreachCountryLabel } from '../model/compliance-settings'\nimport {\n OUTREACH_MAX_EMAIL_STEPS,\n OUTREACH_MAX_STEPS,\n OUTREACH_TASK_KIND_LABELS,\n OUTREACH_TASK_KINDS,\n type OutreachSequence,\n type OutreachSequenceStep,\n type OutreachTaskKind,\n} from '../model/outreach.types'\nimport {\n emptyOutreachSequenceDraft,\n newOutreachEmailStep,\n newOutreachTaskStep,\n outreachSequenceDraftOf,\n type OutreachSequenceDraft,\n type OutreachSequenceIssue,\n} from '../model/sequence-draft'\nimport { OutreachSendWindowFields } from './send-window-fields'\nimport { OutreachSequenceMailboxPicker } from './sequence-mailbox-picker'\nimport { OutreachSequencePreview } from './sequence-preview'\nimport { OutreachStepCard } from './sequence-step-card'\nimport { OutreachRouteError, useOutreachApi } from './use-outreach-api'\nimport { useOutreachEmailTemplates } from './use-outreach-crm'\nimport type { OutreachMailboxesResult } from './use-outreach-mailboxes'\nimport type { OutreachSettingsLoad } from './use-outreach-settings'\n\nexport interface OutreachSequenceEditorProps {\n orgId: string\n orgMount?: ConsolePluginOrgMount\n /** The stored sequence, or `null` for a new one. */\n sequence: OutreachSequence | null\n /** The organization's compliance settings: its countries, and the preview's footer. */\n settings: OutreachSettingsLoad\n /**\n * The organization's mailboxes, from the Mailboxes section's listener:\n * what the picker offers, and who the preview's emails are from.\n */\n mailboxes: OutreachMailboxesResult\n /** The Mailboxes section, where the picker sends a member to connect one. */\n mailboxesPath: string\n onSaved(sequence: OutreachSequence): void\n onCancel?(): void\n}\n\n/** The step a dotted issue path is about, and the field within it. */\nfunction stepIssueKey(path: string): { index: number; field: string } | null {\n const match = /^steps\\.(\\d+)(?:\\.(.+))?$/.exec(path)\n if (!match) return null\n const field = match[2] ?? 'step'\n return { index: Number(match[1]), field: field === 'id' ? 'step' : field }\n}\n\n/**\n * The sequence editor (AGL-2980): the name, the site and mailbox, the steps,\n * who it sends to and when, beside a live preview of each email.\n *\n * Every issue the engine's validator finds is shown beside its field — the\n * same validator the save route refuses with — errors once a save was\n * attempted, warnings as soon as they appear. What only the server can\n * judge (whose mailbox, which countries the organization allows, what may\n * no longer change once people are enrolled) comes back from the save and\n * is shown in the same places.\n */\nexport function OutreachSequenceEditor(props: OutreachSequenceEditorProps) {\n const { orgId, orgMount, sequence, settings } = props\n const api = useOutreachApi(orgId)\n const { enqueueSnackbar } = useSnackbar()\n const { data: user } = useUser()\n const uid = user?.uid ?? null\n const mountedHosts = orgMount?.hosts\n const hosts = useMemo(() => mountedHosts ?? [], [mountedHosts])\n const orgCountries = settings.settings?.allowedCountries ?? ['US']\n\n const [draft, setDraft] = useState<OutreachSequenceDraft>(() =>\n sequence\n ? outreachSequenceDraftOf(sequence)\n : emptyOutreachSequenceDraft({\n hostId: hosts.length === 1 ? hosts[0].id : '',\n allowedCountries: orgCountries,\n }),\n )\n const [attempted, setAttempted] = useState(false)\n const [serverIssues, setServerIssues] = useState<OutreachSequenceIssue[]>([])\n const [saving, setSaving] = useState(false)\n const [taskAnchor, setTaskAnchor] = useState<HTMLElement | null>(null)\n\n // A stored sequence the listener updated (a save, a status change) is the\n // new starting point.\n useEffect(() => {\n if (sequence) setDraft(outreachSequenceDraftOf(sequence))\n }, [sequence])\n // A new sequence starts on the organization's countries once they are read.\n const settingsCountries = settings.settings?.allowedCountries.join(',')\n useEffect(() => {\n if (!sequence && settingsCountries) {\n setDraft((previous) => ({\n ...previous,\n settings: {\n ...previous.settings,\n allowedCountries: settingsCountries.split(','),\n },\n }))\n }\n }, [sequence, settingsCountries])\n useEffect(() => {\n if (!sequence && !draft.hostId && hosts.length === 1)\n setDraft((previous) => ({ ...previous, hostId: hosts[0].id }))\n }, [sequence, draft.hostId, hosts])\n\n // A new sequence starts on the member's own mailbox when they have exactly\n // one that is sending.\n const ownSending = props.mailboxes.mailboxes.filter(\n (entry) => entry.connectedByUid === uid && entry.status === 'connected',\n )\n const onlyOwnMailbox = ownSending.length === 1 ? ownSending[0].id : ''\n useEffect(() => {\n if (!sequence && !draft.mailboxId && onlyOwnMailbox)\n setDraft((previous) => ({ ...previous, mailboxId: onlyOwnMailbox }))\n }, [sequence, draft.mailboxId, onlyOwnMailbox])\n\n const templates = useOutreachEmailTemplates(orgId, draft.hostId || null, uid)\n const mailbox =\n props.mailboxes.mailboxes.find((entry) => entry.id === draft.mailboxId) ??\n null\n const archived = sequence?.status === 'archived'\n /*\n * The org's campaigns, for the picker (AGL-3254): read while the editor\n * is open. A sequence is the organization's record and so is a campaign,\n * so the choice is every campaign in the org, whichever site the\n * sequence sends as.\n */\n const campaigns = useOrgCampaigns(orgId, { enabled: true })\n\n const issues = useMemo(() => {\n const judged = [\n ...validateOutreachSequence(draft),\n ...serverIssues,\n ] as OutreachSequenceIssue[]\n return judged.filter(\n (issue) =>\n issue.severity === 'warning' ||\n attempted ||\n serverIssues.includes(issue),\n )\n }, [draft, serverIssues, attempted])\n const issueAt = (path: string) =>\n issues.find((issue) => issue.path === path)?.message\n const stepIssues = (index: number) => {\n const found: Record<string, string> = {}\n for (const issue of issues) {\n const key = stepIssueKey(issue.path)\n if (key?.index === index && !found[key.field])\n found[key.field] = issue.message\n }\n return found\n }\n const listIssue = issueAt('steps')\n const countriesIssue = issues.find((issue) =>\n issue.path.startsWith('settings.allowedCountries'),\n )?.message\n\n const update = (next: Partial<OutreachSequenceDraft>) => {\n setServerIssues([])\n setDraft((previous) => ({ ...previous, ...next }))\n }\n const setStep = (index: number, step: OutreachSequenceStep) =>\n update({\n steps: draft.steps.map((entry, position) =>\n position === index ? step : entry,\n ),\n })\n const moveStep = (index: number, direction: -1 | 1) => {\n const steps = [...draft.steps]\n const [moved] = steps.splice(index, 1)\n steps.splice(index + direction, 0, moved)\n update({ steps })\n }\n const emailCount = draft.steps.filter((step) => step.kind === 'email').length\n const full = draft.steps.length >= OUTREACH_MAX_STEPS\n const threadSubject = (index: number) => {\n for (let position = index - 1; position >= 0; position -= 1) {\n const step = draft.steps[position]\n if (step.kind === 'email' && !isInThreadEmailStep(draft.steps, position))\n return step.subject\n }\n return ''\n }\n\n const save = async () => {\n setAttempted(true)\n if (\n validateOutreachSequence(draft).some(\n (issue) => issue.severity === 'error',\n )\n )\n return\n setSaving(true)\n try {\n const answer = await api.saveSequence(sequence?.id ?? null, draft)\n setServerIssues([])\n setAttempted(false)\n enqueueSnackbar(\n answer.created ? 'Sequence created.' : 'Sequence saved.',\n { variant: 'success' },\n )\n props.onSaved(answer.sequence)\n } catch (error) {\n if (error instanceof OutreachRouteError && error.issues.length) {\n setServerIssues(\n error.issues.filter(\n (issue): issue is OutreachSequenceIssue => 'path' in issue,\n ),\n )\n }\n enqueueSnackbar((error as Error).message, {\n variant: 'error',\n allowDuplicate: true,\n })\n } finally {\n setSaving(false)\n }\n }\n\n const siteName = hosts.find((host) => host.id === draft.hostId)?.name ?? ''\n const countryOptions = [\n ...new Set([...orgCountries, ...draft.settings.allowedCountries]),\n ]\n\n return (\n <Grid container spacing={2}>\n <Grid size={{ xs: 12, md: 7 }}>\n <Stack spacing={2}>\n {archived ? (\n <Alert severity=\"info\">\n This sequence is archived, so it can’t be edited.\n </Alert>\n ) : null}\n <CardDisplay\n header=\"Sequence\"\n help={pluginDocsHelp('sequences', { anchor: '#build-a-sequence' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n <TextField\n label=\"Name\"\n value={draft.name}\n onChange={(event) => update({ name: event.target.value })}\n disabled={archived}\n error={Boolean(issueAt('name'))}\n helperText={issueAt('name')}\n slotProps={{\n htmlInput: { maxLength: OUTREACH_SEQUENCE_NAME_MAX + 20 },\n }}\n fullWidth\n />\n <TextField\n select\n label=\"Site\"\n value={\n hosts.some((host) => host.id === draft.hostId)\n ? draft.hostId\n : ''\n }\n onChange={(event) => update({ hostId: event.target.value })}\n disabled={archived || !hosts.length}\n error={Boolean(issueAt('hostId'))}\n helperText={\n issueAt('hostId') ??\n 'The site whose contacts this sequence emails.'\n }\n fullWidth\n >\n {hosts.map((host) => (\n <MenuItem key={host.id} value={host.id}>\n {host.name}\n </MenuItem>\n ))}\n </TextField>\n <OutreachSequenceMailboxPicker\n orgId={orgId}\n uid={uid}\n mailboxes={props.mailboxes}\n mailboxesPath={props.mailboxesPath}\n value={draft.mailboxId}\n onChange={(mailboxId) => update({ mailboxId })}\n error={issueAt('mailboxId')}\n disabled={archived}\n />\n {/*\n The campaigns this sequence is part of (AGL-3254), picked the\n way a form's page picks them. Everyone enrolled joins them,\n and what the sequence produces is reported under them; it\n does not change who is enrolled or what is sent.\n */}\n <CampaignPicker\n options={campaigns.options}\n value={draft.campaignIds}\n onChange={(campaignIds) => update({ campaignIds })}\n label=\"Campaigns\"\n helperText={\n issueAt('campaignIds') ??\n 'The campaigns this sequence is part of. Everyone enrolled joins them, and its sends, replies, meetings and conversions count on their pages.'\n }\n disabled={archived}\n empty={campaigns.ready && !campaigns.options.length}\n emptyText=\"There are no campaigns yet. Create one from Marketing to file this sequence under it.\"\n />\n </Stack>\n </CardDisplay>\n\n {listIssue ? <Alert severity=\"error\">{listIssue}</Alert> : null}\n {draft.steps.map((step, index) => (\n <OutreachStepCard\n key={step.id || `step-${index}`}\n step={step}\n index={index}\n count={draft.steps.length}\n firstEmail={\n draft.steps.findIndex((entry) => entry.kind === 'email') ===\n index\n }\n inThread={isInThreadEmailStep(draft.steps, index)}\n threadSubject={threadSubject(index)}\n templates={templates.data}\n issues={stepIssues(index)}\n disabled={archived}\n onChange={(next) => setStep(index, next)}\n onMove={(direction) => moveStep(index, direction)}\n onRemove={() =>\n update({\n steps: draft.steps.filter(\n (_entry, position) => position !== index,\n ),\n })\n }\n />\n ))}\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ flexWrap: 'wrap', rowGap: 1 }}\n >\n <Button\n variant=\"outlined\"\n startIcon={<MdiIcon path={mdiPlus.path} fontSize=\"small\" />}\n disabled={\n archived || full || emailCount >= OUTREACH_MAX_EMAIL_STEPS\n }\n onClick={() =>\n update({\n steps: [...draft.steps, newOutreachEmailStep(draft.steps)],\n })\n }\n >\n Add email\n </Button>\n <Button\n variant=\"outlined\"\n startIcon={<MdiIcon path={mdiPlus.path} fontSize=\"small\" />}\n disabled={archived || full}\n onClick={(event) => setTaskAnchor(event.currentTarget)}\n aria-haspopup=\"menu\"\n >\n Add task\n </Button>\n <Menu\n anchorEl={taskAnchor}\n open={Boolean(taskAnchor)}\n onClose={() => setTaskAnchor(null)}\n >\n {OUTREACH_TASK_KINDS.map((kind: OutreachTaskKind) => (\n <MenuItem\n key={kind}\n onClick={() => {\n setTaskAnchor(null)\n update({\n steps: [\n ...draft.steps,\n newOutreachTaskStep(draft.steps, kind),\n ],\n })\n }}\n >\n {OUTREACH_TASK_KIND_LABELS[kind]}\n </MenuItem>\n ))}\n </Menu>\n </Stack>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`Up to ${OUTREACH_MAX_STEPS} steps, ${OUTREACH_MAX_EMAIL_STEPS} of them emails.`}\n </Typography>\n\n <CardDisplay\n header=\"Who it sends to, and when\"\n help={pluginDocsHelp('sequences', {\n anchor: '#build-a-sequence',\n excerpt:\n 'A sequence sends only to the countries both it and your organization allow, and in its mailbox’s sending hours unless it has its own.',\n })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n <Autocomplete<string, true>\n multiple\n options={countryOptions}\n value={draft.settings.allowedCountries}\n getOptionLabel={outreachCountryLabel}\n onChange={(_event, next) =>\n update({\n settings: { ...draft.settings, allowedCountries: next },\n })\n }\n disabled={archived}\n renderValue={(value, getItemProps) =>\n value.map((code, index) => {\n const { key, ...item } = getItemProps({ index })\n return (\n <Chip\n key={key}\n size=\"small\"\n label={outreachCountryLabel(code)}\n {...item}\n />\n )\n })\n }\n renderInput={(params) => (\n <TextField\n {...params}\n label=\"Countries\"\n error={Boolean(countriesIssue)}\n helperText={\n countriesIssue ??\n 'Among the countries your organization allows. Cold contacts are emailed only in the United States.'\n }\n />\n )}\n />\n <FormControlLabel\n control={\n <Switch\n checked={draft.settings.allowCustomers}\n onChange={(event) =>\n update({\n settings: {\n ...draft.settings,\n allowCustomers: event.target.checked,\n },\n })\n }\n disabled={archived}\n />\n }\n label=\"Include customers\"\n />\n {/*\n * Click tracking (AGL-3239), with its cost stated where it is\n * chosen rather than in a document nobody opens. Off by\n * default: it changes the link a cold recipient sees, and the\n * emails stay plain text either way — there is no open\n * tracking to turn on, because a tracking image needs an HTML\n * email and these are not.\n */}\n <Stack spacing={0}>\n <FormControlLabel\n control={\n <Switch\n checked={draft.settings.trackClicks}\n onChange={(event) =>\n update({\n settings: {\n ...draft.settings,\n trackClicks: event.target.checked,\n },\n })\n }\n disabled={archived}\n />\n }\n label=\"Count link clicks\"\n />\n <Typography variant=\"caption\" color=\"text.secondary\">\n Links in these emails are replaced with links of ours that\n forward to the same page, so clicks can be counted. The\n recipient sees the replacement, not your address. Opens\n aren’t counted either way: these emails are plain text, and\n counting an open needs a tracking image.\n </Typography>\n </Stack>\n {/*\n * The mail-client unsubscribe button (AGL-3296), off by\n * default: the header is what makes Apple Mail, Gmail and\n * Outlook present a one-to-one email as a mailing list. The\n * footer's \"reply 'no'\" line is the way out either way.\n */}\n <Stack spacing={0}>\n <FormControlLabel\n control={\n <Switch\n checked={draft.settings.listUnsubscribe}\n onChange={(event) =>\n update({\n settings: {\n ...draft.settings,\n listUnsubscribe: event.target.checked,\n },\n })\n }\n disabled={archived}\n />\n }\n label=\"Add a mail-client unsubscribe button\"\n />\n <Typography variant=\"caption\" color=\"text.secondary\">\n Adds the List-Unsubscribe header, so the recipient’s mail\n app shows its own Unsubscribe button. It’s the easiest way\n out for them, and fewer people mark you as spam when they\n have it, but mail apps then label the email as coming from\n a mailing list. Off, the footer’s “reply ‘no’” line is the\n way out, and it’s always there.\n </Typography>\n </Stack>\n <OutreachSendWindowFields\n window={draft.settings.window}\n onChange={(window) =>\n update({ settings: { ...draft.settings, window } })\n }\n daysIssue={issueAt('settings.window.days')}\n hoursIssue={issueAt('settings.window')}\n disabled={archived}\n />\n </Stack>\n </CardDisplay>\n\n {archived ? null : (\n <Stack\n direction={{ xs: 'column', sm: 'row' }}\n spacing={1}\n sx={{ justifyContent: 'flex-end' }}\n >\n {props.onCancel ? (\n <Button\n variant=\"text\"\n onClick={props.onCancel}\n disabled={saving}\n >\n Cancel\n </Button>\n ) : null}\n <Button\n variant=\"contained\"\n onClick={() => void save()}\n disabled={saving}\n >\n {saving ? 'Saving…' : sequence ? 'Save' : 'Create sequence'}\n </Button>\n </Stack>\n )}\n </Stack>\n </Grid>\n <Grid size={{ xs: 12, md: 5 }}>\n <Box sx={{ position: { md: 'sticky' }, top: { md: 16 } }}>\n <OutreachSequencePreview\n steps={draft.steps}\n orgSettings={settings.settings}\n orgSettingsStatus={settings.status}\n templates={templates.data}\n sender={\n mailbox\n ? {\n name: mailbox.displayName,\n email: mailbox.sendAs || mailbox.email,\n }\n : null\n }\n siteName={siteName}\n />\n </Box>\n </Grid>\n </Grid>\n )\n}\nOutreachSequenceEditor.displayName = 'OutreachSequenceEditor'\n\nexport default OutreachSequenceEditor\n"],"names":["pluginDocsHelp","mdiPlus","CardDisplay","MdiIcon","CampaignPicker","useSnackbar","useOrgCampaigns","useUser","Alert","Autocomplete","Box","Button","Chip","FormControlLabel","Grid","Menu","MenuItem","Stack","Switch","TextField","Typography","useEffect","useMemo","useState","isInThreadEmailStep","OUTREACH_SEQUENCE_NAME_MAX","validateOutreachSequence","outreachCountryLabel","OUTREACH_MAX_EMAIL_STEPS","OUTREACH_MAX_STEPS","OUTREACH_TASK_KIND_LABELS","OUTREACH_TASK_KINDS","emptyOutreachSequenceDraft","newOutreachEmailStep","newOutreachTaskStep","outreachSequenceDraftOf","OutreachSendWindowFields","OutreachSequenceMailboxPicker","OutreachSequencePreview","OutreachStepCard","OutreachRouteError","useOutreachApi","useOutreachEmailTemplates","stepIssueKey","path","match","exec","field","index","Number","OutreachSequenceEditor","props","issueAt","settings","issues","hosts","orgId","orgMount","sequence","api","enqueueSnackbar","data","user","uid","mountedHosts","orgCountries","allowedCountries","draft","setDraft","hostId","length","id","attempted","setAttempted","serverIssues","setServerIssues","saving","setSaving","taskAnchor","setTaskAnchor","settingsCountries","join","previous","split","ownSending","mailboxes","filter","entry","connectedByUid","status","onlyOwnMailbox","mailboxId","templates","mailbox","find","archived","campaigns","enabled","judged","issue","severity","includes","message","stepIssues","found","key","listIssue","countriesIssue","startsWith","update","next","setStep","step","steps","map","position","moveStep","direction","moved","splice","emailCount","kind","full","threadSubject","subject","save","some","answer","saveSequence","created","variant","onSaved","error","allowDuplicate","siteName","host","name","countryOptions","Set","container","spacing","size","xs","md","header","help","anchor","contentGutterX","contentGutterY","label","value","onChange","event","target","disabled","Boolean","helperText","slotProps","htmlInput","maxLength","fullWidth","select","mailboxesPath","options","campaignIds","empty","ready","emptyText","count","firstEmail","findIndex","inThread","onMove","onRemove","_entry","sx","flexWrap","rowGap","startIcon","fontSize","onClick","currentTarget","aria-haspopup","anchorEl","open","onClose","color","excerpt","multiple","getOptionLabel","_event","renderValue","getItemProps","code","item","renderInput","params","control","checked","allowCustomers","trackClicks","listUnsubscribe","window","daysIssue","hoursIssue","sm","justifyContent","onCancel","top","orgSettings","orgSettingsStatus","sender","displayName","email","sendAs"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/components/sequence-editor.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, type ConsolePluginOrgMount } from '@aglyn/aglyn'\nimport { consentGroupForHost } from '@aglyn/aglyn/app-utils/consent-groups'\nimport { mdiPlus } from '@aglyn/shared-data-mdi'\nimport { CardDisplay, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport CampaignPicker from '@aglyn/shared-ui-email-campaigns/components/campaign-picker.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { useOrgCampaigns, useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Autocomplete,\n Box,\n Button,\n Chip,\n FormControlLabel,\n Grid,\n Menu,\n MenuItem,\n Stack,\n Switch,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useEffect, useMemo, useState } from 'react'\nimport {\n isInThreadEmailStep,\n OUTREACH_SEQUENCE_NAME_MAX,\n validateOutreachSequence,\n} from '../engine/sequence-validation'\nimport { outreachCountryLabel } from '../model/compliance-settings'\nimport {\n OUTREACH_MAX_EMAIL_STEPS,\n OUTREACH_MAX_STEPS,\n OUTREACH_TASK_KIND_LABELS,\n OUTREACH_TASK_KINDS,\n type OutreachSequence,\n type OutreachSequenceStep,\n type OutreachTaskKind,\n} from '../model/outreach.types'\nimport {\n emptyOutreachSequenceDraft,\n newOutreachEmailStep,\n newOutreachTaskStep,\n outreachSequenceDraftOf,\n type OutreachSequenceDraft,\n type OutreachSequenceIssue,\n} from '../model/sequence-draft'\nimport { OutreachSendWindowFields } from './send-window-fields'\nimport { OutreachSequenceMailboxPicker } from './sequence-mailbox-picker'\nimport { OutreachSequencePreview } from './sequence-preview'\nimport { OutreachStepCard } from './sequence-step-card'\nimport { OutreachStepTestDialog } from './step-test-dialog'\nimport { OutreachRouteError, useOutreachApi } from './use-outreach-api'\nimport { useOutreachEmailTemplates } from './use-outreach-crm'\nimport type { OutreachMailboxesResult } from './use-outreach-mailboxes'\nimport type { OutreachSettingsLoad } from './use-outreach-settings'\n\nexport interface OutreachSequenceEditorProps {\n orgId: string\n orgMount?: ConsolePluginOrgMount\n /** The organization document, for the consent group a test send's person picker searches (AGL-3325). */\n org?: Record<string, unknown> | null\n /** The stored sequence, or `null` for a new one. */\n sequence: OutreachSequence | null\n /** The organization's compliance settings: its countries, and the preview's footer. */\n settings: OutreachSettingsLoad\n /**\n * The organization's mailboxes, from the Mailboxes section's listener:\n * what the picker offers, and who the preview's emails are from.\n */\n mailboxes: OutreachMailboxesResult\n /** The Mailboxes section, where the picker sends a member to connect one. */\n mailboxesPath: string\n onSaved(sequence: OutreachSequence): void\n onCancel?(): void\n}\n\n/** The step a dotted issue path is about, and the field within it. */\nfunction stepIssueKey(path: string): { index: number; field: string } | null {\n const match = /^steps\\.(\\d+)(?:\\.(.+))?$/.exec(path)\n if (!match) return null\n const field = match[2] ?? 'step'\n return { index: Number(match[1]), field: field === 'id' ? 'step' : field }\n}\n\n/**\n * The sequence editor (AGL-2980): the name, the site and mailbox, the steps,\n * who it sends to and when, beside a live preview of each email.\n *\n * Every issue the engine's validator finds is shown beside its field — the\n * same validator the save route refuses with — errors once a save was\n * attempted, warnings as soon as they appear. What only the server can\n * judge (whose mailbox, which countries the organization allows, what may\n * no longer change once people are enrolled) comes back from the save and\n * is shown in the same places.\n */\nexport function OutreachSequenceEditor(props: OutreachSequenceEditorProps) {\n const { orgId, orgMount, sequence, settings } = props\n const api = useOutreachApi(orgId)\n const { enqueueSnackbar } = useSnackbar()\n const { data: user } = useUser()\n const uid = user?.uid ?? null\n const mountedHosts = orgMount?.hosts\n const hosts = useMemo(() => mountedHosts ?? [], [mountedHosts])\n const orgCountries = settings.settings?.allowedCountries ?? ['US']\n\n const [draft, setDraft] = useState<OutreachSequenceDraft>(() =>\n sequence\n ? outreachSequenceDraftOf(sequence)\n : emptyOutreachSequenceDraft({\n hostId: hosts.length === 1 ? hosts[0].id : '',\n allowedCountries: orgCountries,\n }),\n )\n const [attempted, setAttempted] = useState(false)\n const [serverIssues, setServerIssues] = useState<OutreachSequenceIssue[]>([])\n const [saving, setSaving] = useState(false)\n const [taskAnchor, setTaskAnchor] = useState<HTMLElement | null>(null)\n // The step whose test-send dialog is open (AGL-3325), by index.\n const [testing, setTesting] = useState<number | null>(null)\n\n // A stored sequence the listener updated (a save, a status change) is the\n // new starting point.\n useEffect(() => {\n if (sequence) setDraft(outreachSequenceDraftOf(sequence))\n }, [sequence])\n // A new sequence starts on the organization's countries once they are read.\n const settingsCountries = settings.settings?.allowedCountries.join(',')\n useEffect(() => {\n if (!sequence && settingsCountries) {\n setDraft((previous) => ({\n ...previous,\n settings: {\n ...previous.settings,\n allowedCountries: settingsCountries.split(','),\n },\n }))\n }\n }, [sequence, settingsCountries])\n useEffect(() => {\n if (!sequence && !draft.hostId && hosts.length === 1)\n setDraft((previous) => ({ ...previous, hostId: hosts[0].id }))\n }, [sequence, draft.hostId, hosts])\n\n // A new sequence starts on the member's own mailbox when they have exactly\n // one that is sending.\n const ownSending = props.mailboxes.mailboxes.filter(\n (entry) => entry.connectedByUid === uid && entry.status === 'connected',\n )\n const onlyOwnMailbox = ownSending.length === 1 ? ownSending[0].id : ''\n useEffect(() => {\n if (!sequence && !draft.mailboxId && onlyOwnMailbox)\n setDraft((previous) => ({ ...previous, mailboxId: onlyOwnMailbox }))\n }, [sequence, draft.mailboxId, onlyOwnMailbox])\n\n const templates = useOutreachEmailTemplates(orgId, draft.hostId || null, uid)\n const mailbox =\n props.mailboxes.mailboxes.find((entry) => entry.id === draft.mailboxId) ??\n null\n const archived = sequence?.status === 'archived'\n /*\n * The org's campaigns, for the picker (AGL-3254): read while the editor\n * is open. A sequence is the organization's record and so is a campaign,\n * so the choice is every campaign in the org, whichever site the\n * sequence sends as.\n */\n const campaigns = useOrgCampaigns(orgId, { enabled: true })\n\n const issues = useMemo(() => {\n const judged = [\n ...validateOutreachSequence(draft),\n ...serverIssues,\n ] as OutreachSequenceIssue[]\n return judged.filter(\n (issue) =>\n issue.severity === 'warning' ||\n attempted ||\n serverIssues.includes(issue),\n )\n }, [draft, serverIssues, attempted])\n const issueAt = (path: string) =>\n issues.find((issue) => issue.path === path)?.message\n const stepIssues = (index: number) => {\n const found: Record<string, string> = {}\n for (const issue of issues) {\n const key = stepIssueKey(issue.path)\n if (key?.index === index && !found[key.field])\n found[key.field] = issue.message\n }\n return found\n }\n const listIssue = issueAt('steps')\n const countriesIssue = issues.find((issue) =>\n issue.path.startsWith('settings.allowedCountries'),\n )?.message\n\n const update = (next: Partial<OutreachSequenceDraft>) => {\n setServerIssues([])\n setDraft((previous) => ({ ...previous, ...next }))\n }\n const setStep = (index: number, step: OutreachSequenceStep) =>\n update({\n steps: draft.steps.map((entry, position) =>\n position === index ? step : entry,\n ),\n })\n const moveStep = (index: number, direction: -1 | 1) => {\n const steps = [...draft.steps]\n const [moved] = steps.splice(index, 1)\n steps.splice(index + direction, 0, moved)\n update({ steps })\n }\n const emailCount = draft.steps.filter((step) => step.kind === 'email').length\n const full = draft.steps.length >= OUTREACH_MAX_STEPS\n const threadSubject = (index: number) => {\n for (let position = index - 1; position >= 0; position -= 1) {\n const step = draft.steps[position]\n if (step.kind === 'email' && !isInThreadEmailStep(draft.steps, position))\n return step.subject\n }\n return ''\n }\n\n const save = async () => {\n setAttempted(true)\n if (\n validateOutreachSequence(draft).some(\n (issue) => issue.severity === 'error',\n )\n )\n return\n setSaving(true)\n try {\n const answer = await api.saveSequence(sequence?.id ?? null, draft)\n setServerIssues([])\n setAttempted(false)\n enqueueSnackbar(\n answer.created ? 'Sequence created.' : 'Sequence saved.',\n { variant: 'success' },\n )\n props.onSaved(answer.sequence)\n } catch (error) {\n if (error instanceof OutreachRouteError && error.issues.length) {\n setServerIssues(\n error.issues.filter(\n (issue): issue is OutreachSequenceIssue => 'path' in issue,\n ),\n )\n }\n enqueueSnackbar((error as Error).message, {\n variant: 'error',\n allowDuplicate: true,\n })\n } finally {\n setSaving(false)\n }\n }\n\n const siteName = hosts.find((host) => host.id === draft.hostId)?.name ?? ''\n const countryOptions = [\n ...new Set([...orgCountries, ...draft.settings.allowedCountries]),\n ]\n /*\n * A test sends the step as STORED (AGL-3325): the route reads the saved\n * sequence, so a new one has nothing to send yet and an edited one sends\n * its last save — said on the button and in the dialog rather than found\n * out in the inbox.\n */\n const unsaved =\n sequence !== null &&\n JSON.stringify(draft) !== JSON.stringify(outreachSequenceDraftOf(sequence))\n const sendTestDisabledReason = !sequence\n ? 'Create the sequence to send a test of it.'\n : !draft.mailboxId\n ? 'Choose a mailbox to send the test from.'\n : null\n\n return (\n <Grid container spacing={2}>\n <Grid size={{ xs: 12, md: 7 }}>\n <Stack spacing={2}>\n {archived ? (\n <Alert severity=\"info\">\n This sequence is archived, so it can’t be edited.\n </Alert>\n ) : null}\n <CardDisplay\n header=\"Sequence\"\n help={pluginDocsHelp('sequences', { anchor: '#build-a-sequence' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n <TextField\n label=\"Name\"\n value={draft.name}\n onChange={(event) => update({ name: event.target.value })}\n disabled={archived}\n error={Boolean(issueAt('name'))}\n helperText={issueAt('name')}\n slotProps={{\n htmlInput: { maxLength: OUTREACH_SEQUENCE_NAME_MAX + 20 },\n }}\n fullWidth\n />\n <TextField\n select\n label=\"Site\"\n value={\n hosts.some((host) => host.id === draft.hostId)\n ? draft.hostId\n : ''\n }\n onChange={(event) => update({ hostId: event.target.value })}\n disabled={archived || !hosts.length}\n error={Boolean(issueAt('hostId'))}\n helperText={\n issueAt('hostId') ??\n 'The site whose contacts this sequence emails.'\n }\n fullWidth\n >\n {hosts.map((host) => (\n <MenuItem key={host.id} value={host.id}>\n {host.name}\n </MenuItem>\n ))}\n </TextField>\n <OutreachSequenceMailboxPicker\n orgId={orgId}\n uid={uid}\n mailboxes={props.mailboxes}\n mailboxesPath={props.mailboxesPath}\n value={draft.mailboxId}\n onChange={(mailboxId) => update({ mailboxId })}\n error={issueAt('mailboxId')}\n disabled={archived}\n />\n {/*\n The campaigns this sequence is part of (AGL-3254), picked the\n way a form's page picks them. Everyone enrolled joins them,\n and what the sequence produces is reported under them; it\n does not change who is enrolled or what is sent.\n */}\n <CampaignPicker\n options={campaigns.options}\n value={draft.campaignIds}\n onChange={(campaignIds) => update({ campaignIds })}\n label=\"Campaigns\"\n helperText={\n issueAt('campaignIds') ??\n 'The campaigns this sequence is part of. Everyone enrolled joins them, and its sends, replies, meetings and conversions count on their pages.'\n }\n disabled={archived}\n empty={campaigns.ready && !campaigns.options.length}\n emptyText=\"There are no campaigns yet. Create one from Marketing to file this sequence under it.\"\n />\n </Stack>\n </CardDisplay>\n\n {listIssue ? <Alert severity=\"error\">{listIssue}</Alert> : null}\n {draft.steps.map((step, index) => (\n <OutreachStepCard\n key={step.id || `step-${index}`}\n step={step}\n index={index}\n count={draft.steps.length}\n firstEmail={\n draft.steps.findIndex((entry) => entry.kind === 'email') ===\n index\n }\n inThread={isInThreadEmailStep(draft.steps, index)}\n threadSubject={threadSubject(index)}\n templates={templates.data}\n issues={stepIssues(index)}\n disabled={archived}\n onChange={(next) => setStep(index, next)}\n onMove={(direction) => moveStep(index, direction)}\n onRemove={() =>\n update({\n steps: draft.steps.filter(\n (_entry, position) => position !== index,\n ),\n })\n }\n onSendTest={\n step.kind === 'email' && !archived\n ? () => setTesting(index)\n : undefined\n }\n sendTestDisabledReason={sendTestDisabledReason}\n />\n ))}\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ flexWrap: 'wrap', rowGap: 1 }}\n >\n <Button\n variant=\"outlined\"\n startIcon={<MdiIcon path={mdiPlus.path} fontSize=\"small\" />}\n disabled={\n archived || full || emailCount >= OUTREACH_MAX_EMAIL_STEPS\n }\n onClick={() =>\n update({\n steps: [...draft.steps, newOutreachEmailStep(draft.steps)],\n })\n }\n >\n Add email\n </Button>\n <Button\n variant=\"outlined\"\n startIcon={<MdiIcon path={mdiPlus.path} fontSize=\"small\" />}\n disabled={archived || full}\n onClick={(event) => setTaskAnchor(event.currentTarget)}\n aria-haspopup=\"menu\"\n >\n Add task\n </Button>\n <Menu\n anchorEl={taskAnchor}\n open={Boolean(taskAnchor)}\n onClose={() => setTaskAnchor(null)}\n >\n {OUTREACH_TASK_KINDS.map((kind: OutreachTaskKind) => (\n <MenuItem\n key={kind}\n onClick={() => {\n setTaskAnchor(null)\n update({\n steps: [\n ...draft.steps,\n newOutreachTaskStep(draft.steps, kind),\n ],\n })\n }}\n >\n {OUTREACH_TASK_KIND_LABELS[kind]}\n </MenuItem>\n ))}\n </Menu>\n </Stack>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`Up to ${OUTREACH_MAX_STEPS} steps, ${OUTREACH_MAX_EMAIL_STEPS} of them emails.`}\n </Typography>\n\n <CardDisplay\n header=\"Who it sends to, and when\"\n help={pluginDocsHelp('sequences', {\n anchor: '#build-a-sequence',\n excerpt:\n 'A sequence sends only to the countries both it and your organization allow, and in its mailbox’s sending hours unless it has its own.',\n })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n <Autocomplete<string, true>\n multiple\n options={countryOptions}\n value={draft.settings.allowedCountries}\n getOptionLabel={outreachCountryLabel}\n onChange={(_event, next) =>\n update({\n settings: { ...draft.settings, allowedCountries: next },\n })\n }\n disabled={archived}\n renderValue={(value, getItemProps) =>\n value.map((code, index) => {\n const { key, ...item } = getItemProps({ index })\n return (\n <Chip\n key={key}\n size=\"small\"\n label={outreachCountryLabel(code)}\n {...item}\n />\n )\n })\n }\n renderInput={(params) => (\n <TextField\n {...params}\n label=\"Countries\"\n error={Boolean(countriesIssue)}\n helperText={\n countriesIssue ??\n 'Among the countries your organization allows. Cold contacts are emailed only in the United States.'\n }\n />\n )}\n />\n <FormControlLabel\n control={\n <Switch\n checked={draft.settings.allowCustomers}\n onChange={(event) =>\n update({\n settings: {\n ...draft.settings,\n allowCustomers: event.target.checked,\n },\n })\n }\n disabled={archived}\n />\n }\n label=\"Include customers\"\n />\n {/*\n * Click tracking (AGL-3239), with its cost stated where it is\n * chosen rather than in a document nobody opens. Off by\n * default: it changes the link a cold recipient sees, and the\n * emails stay plain text either way — there is no open\n * tracking to turn on, because a tracking image needs an HTML\n * email and these are not.\n */}\n <Stack spacing={0}>\n <FormControlLabel\n control={\n <Switch\n checked={draft.settings.trackClicks}\n onChange={(event) =>\n update({\n settings: {\n ...draft.settings,\n trackClicks: event.target.checked,\n },\n })\n }\n disabled={archived}\n />\n }\n label=\"Count link clicks\"\n />\n <Typography variant=\"caption\" color=\"text.secondary\">\n Links in these emails are replaced with links of ours that\n forward to the same page, so clicks can be counted. The\n recipient sees the replacement, not your address. Opens\n aren’t counted either way: these emails are plain text, and\n counting an open needs a tracking image.\n </Typography>\n </Stack>\n {/*\n * The mail-client unsubscribe button (AGL-3296), off by\n * default: the header is what makes Apple Mail, Gmail and\n * Outlook present a one-to-one email as a mailing list. The\n * footer's \"reply 'no'\" line is the way out either way.\n */}\n <Stack spacing={0}>\n <FormControlLabel\n control={\n <Switch\n checked={draft.settings.listUnsubscribe}\n onChange={(event) =>\n update({\n settings: {\n ...draft.settings,\n listUnsubscribe: event.target.checked,\n },\n })\n }\n disabled={archived}\n />\n }\n label=\"Add a mail-client unsubscribe button\"\n />\n <Typography variant=\"caption\" color=\"text.secondary\">\n Adds the List-Unsubscribe header, so the recipient’s mail\n app shows its own Unsubscribe button. It’s the easiest way\n out for them, and fewer people mark you as spam when they\n have it, but mail apps then label the email as coming from\n a mailing list. Off, the footer’s “reply ‘no’” line is the\n way out, and it’s always there.\n </Typography>\n </Stack>\n <OutreachSendWindowFields\n window={draft.settings.window}\n onChange={(window) =>\n update({ settings: { ...draft.settings, window } })\n }\n daysIssue={issueAt('settings.window.days')}\n hoursIssue={issueAt('settings.window')}\n disabled={archived}\n />\n </Stack>\n </CardDisplay>\n\n {archived ? null : (\n <Stack\n direction={{ xs: 'column', sm: 'row' }}\n spacing={1}\n sx={{ justifyContent: 'flex-end' }}\n >\n {props.onCancel ? (\n <Button\n variant=\"text\"\n onClick={props.onCancel}\n disabled={saving}\n >\n Cancel\n </Button>\n ) : null}\n <Button\n variant=\"contained\"\n onClick={() => void save()}\n disabled={saving}\n >\n {saving ? 'Saving…' : sequence ? 'Save' : 'Create sequence'}\n </Button>\n </Stack>\n )}\n </Stack>\n </Grid>\n {sequence && testing !== null ? (\n <OutreachStepTestDialog\n open\n onClose={() => setTesting(null)}\n orgId={orgId}\n sequence={sequence}\n stepIndex={testing}\n contactGroupId={\n sequence.hostId\n ? consentGroupForHost(props.org ?? null, sequence.hostId).groupId\n : ''\n }\n selfEmail={user?.email ?? null}\n unsaved={unsaved}\n api={api}\n />\n ) : null}\n <Grid size={{ xs: 12, md: 5 }}>\n <Box sx={{ position: { md: 'sticky' }, top: { md: 16 } }}>\n <OutreachSequencePreview\n steps={draft.steps}\n orgSettings={settings.settings}\n orgSettingsStatus={settings.status}\n templates={templates.data}\n sender={\n mailbox\n ? {\n name: mailbox.displayName,\n email: mailbox.sendAs || mailbox.email,\n }\n : null\n }\n siteName={siteName}\n />\n </Box>\n </Grid>\n </Grid>\n )\n}\nOutreachSequenceEditor.displayName = 'OutreachSequenceEditor'\n\nexport default OutreachSequenceEditor\n"],"names":["pluginDocsHelp","consentGroupForHost","mdiPlus","CardDisplay","MdiIcon","CampaignPicker","useSnackbar","useOrgCampaigns","useUser","Alert","Autocomplete","Box","Button","Chip","FormControlLabel","Grid","Menu","MenuItem","Stack","Switch","TextField","Typography","useEffect","useMemo","useState","isInThreadEmailStep","OUTREACH_SEQUENCE_NAME_MAX","validateOutreachSequence","outreachCountryLabel","OUTREACH_MAX_EMAIL_STEPS","OUTREACH_MAX_STEPS","OUTREACH_TASK_KIND_LABELS","OUTREACH_TASK_KINDS","emptyOutreachSequenceDraft","newOutreachEmailStep","newOutreachTaskStep","outreachSequenceDraftOf","OutreachSendWindowFields","OutreachSequenceMailboxPicker","OutreachSequencePreview","OutreachStepCard","OutreachStepTestDialog","OutreachRouteError","useOutreachApi","useOutreachEmailTemplates","stepIssueKey","path","match","exec","field","index","Number","OutreachSequenceEditor","props","issueAt","settings","issues","hosts","orgId","orgMount","sequence","api","enqueueSnackbar","data","user","uid","mountedHosts","orgCountries","allowedCountries","draft","setDraft","hostId","length","id","attempted","setAttempted","serverIssues","setServerIssues","saving","setSaving","taskAnchor","setTaskAnchor","testing","setTesting","settingsCountries","join","previous","split","ownSending","mailboxes","filter","entry","connectedByUid","status","onlyOwnMailbox","mailboxId","templates","mailbox","find","archived","campaigns","enabled","judged","issue","severity","includes","message","stepIssues","found","key","listIssue","countriesIssue","startsWith","update","next","setStep","step","steps","map","position","moveStep","direction","moved","splice","emailCount","kind","full","threadSubject","subject","save","some","answer","saveSequence","created","variant","onSaved","error","allowDuplicate","siteName","host","name","countryOptions","Set","unsaved","JSON","stringify","sendTestDisabledReason","container","spacing","size","xs","md","header","help","anchor","contentGutterX","contentGutterY","label","value","onChange","event","target","disabled","Boolean","helperText","slotProps","htmlInput","maxLength","fullWidth","select","mailboxesPath","options","campaignIds","empty","ready","emptyText","count","firstEmail","findIndex","inThread","onMove","onRemove","_entry","onSendTest","undefined","sx","flexWrap","rowGap","startIcon","fontSize","onClick","currentTarget","aria-haspopup","anchorEl","open","onClose","color","excerpt","multiple","getOptionLabel","_event","renderValue","getItemProps","code","item","renderInput","params","control","checked","allowCustomers","trackClicks","listUnsubscribe","window","daysIssue","hoursIssue","sm","justifyContent","onCancel","stepIndex","contactGroupId","org","groupId","selfEmail","email","top","orgSettings","orgSettingsStatus","sender","displayName","sendAs"],"mappings":"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"}
|
|
@@ -19,6 +19,13 @@ export interface OutreachStepCardProps {
|
|
|
19
19
|
onChange(step: OutreachSequenceStep): void;
|
|
20
20
|
onMove(direction: -1 | 1): void;
|
|
21
21
|
onRemove(): void;
|
|
22
|
+
/**
|
|
23
|
+
* Opens the test-send dialog for this email step (AGL-3325). Absent for a
|
|
24
|
+
* step that cannot be tested — a task, or an archived sequence.
|
|
25
|
+
*/
|
|
26
|
+
onSendTest?(): void;
|
|
27
|
+
/** Why the test cannot be sent yet, shown on the disabled button; `null` when it can. */
|
|
28
|
+
sendTestDisabledReason?: string | null;
|
|
22
29
|
}
|
|
23
30
|
/**
|
|
24
31
|
* One step of the sequence editor (AGL-2980): an email — subject, body or a
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/ 'use client';
|
|
17
17
|
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
18
18
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
19
|
-
import { mdiArrowDown, mdiArrowUp, mdiCodeBraces, mdiTrashCanOutline } from "@aglyn/shared-data-mdi";
|
|
19
|
+
import { mdiArrowDown, mdiArrowUp, mdiCodeBraces, mdiEmailFastOutline, mdiTrashCanOutline } from "@aglyn/shared-data-mdi";
|
|
20
20
|
import { MdiIcon } from "@aglyn/shared-ui-jsx";
|
|
21
21
|
import { Autocomplete, Button, Card, CardContent, FormControlLabel, IconButton, ListSubheader, Menu, MenuItem, Stack, Switch, TextField, Tooltip, Typography } from "@mui/material";
|
|
22
22
|
import { useRef, useState } from "react";
|
|
@@ -146,7 +146,7 @@ function DelayField(props) {
|
|
|
146
146
|
});
|
|
147
147
|
}
|
|
148
148
|
function EmailStepFields(props) {
|
|
149
|
-
var _props_templates_find;
|
|
149
|
+
var _props_templates_find, _props_sendTestDisabledReason;
|
|
150
150
|
const { step, disabled, issues } = props;
|
|
151
151
|
const subjectRef = useRef(null);
|
|
152
152
|
const bodyRef = useRef(null);
|
|
@@ -244,21 +244,44 @@ function EmailStepFields(props) {
|
|
|
244
244
|
minRows: 6,
|
|
245
245
|
fullWidth: true
|
|
246
246
|
}),
|
|
247
|
-
/*#__PURE__*/
|
|
247
|
+
/*#__PURE__*/ _jsxs(Stack, {
|
|
248
248
|
direction: "row",
|
|
249
249
|
spacing: 1,
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
250
|
+
sx: {
|
|
251
|
+
flexWrap: 'wrap',
|
|
252
|
+
rowGap: 1
|
|
253
|
+
},
|
|
254
|
+
children: [
|
|
255
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
256
|
+
size: "small",
|
|
257
|
+
variant: "outlined",
|
|
258
|
+
startIcon: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
259
|
+
path: mdiCodeBraces.path,
|
|
260
|
+
fontSize: "small"
|
|
261
|
+
}),
|
|
262
|
+
onClick: (event)=>setFieldsAnchor(event.currentTarget),
|
|
263
|
+
disabled: disabled || Boolean(template) && !showSubject,
|
|
264
|
+
"aria-haspopup": "menu",
|
|
265
|
+
children: "Insert field"
|
|
256
266
|
}),
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
267
|
+
props.onSendTest ? /*#__PURE__*/ _jsx(Tooltip, {
|
|
268
|
+
title: (_props_sendTestDisabledReason = props.sendTestDisabledReason) != null ? _props_sendTestDisabledReason : '',
|
|
269
|
+
children: /*#__PURE__*/ _jsx("span", {
|
|
270
|
+
children: /*#__PURE__*/ _jsx(Button, {
|
|
271
|
+
size: "small",
|
|
272
|
+
variant: "outlined",
|
|
273
|
+
startIcon: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
274
|
+
path: mdiEmailFastOutline.path,
|
|
275
|
+
fontSize: "small"
|
|
276
|
+
}),
|
|
277
|
+
onClick: props.onSendTest,
|
|
278
|
+
disabled: Boolean(props.sendTestDisabledReason),
|
|
279
|
+
"aria-label": `Send a test of step ${props.index + 1}`,
|
|
280
|
+
children: "Send a test"
|
|
281
|
+
})
|
|
282
|
+
})
|
|
283
|
+
}) : null
|
|
284
|
+
]
|
|
262
285
|
}),
|
|
263
286
|
/*#__PURE__*/ _jsx(Menu, {
|
|
264
287
|
anchorEl: fieldsAnchor,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/components/sequence-step-card.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n mdiArrowDown,\n mdiArrowUp,\n mdiCodeBraces,\n mdiTrashCanOutline,\n} from '@aglyn/shared-data-mdi'\nimport { MdiIcon } from '@aglyn/shared-ui-jsx'\nimport {\n Autocomplete,\n Button,\n Card,\n CardContent,\n FormControlLabel,\n IconButton,\n ListSubheader,\n Menu,\n MenuItem,\n Stack,\n Switch,\n TextField,\n Tooltip,\n Typography,\n} from '@mui/material'\nimport { useRef, useState } from 'react'\nimport { OUTREACH_MERGE_FIELDS } from '../engine/sequence-validation'\nimport {\n OUTREACH_MAX_STEP_DELAY_BUSINESS_DAYS,\n OUTREACH_TASK_KIND_LABELS,\n OUTREACH_TASK_KINDS,\n type OutreachEmailStep,\n type OutreachSequenceStep,\n type OutreachTaskKind,\n type OutreachTaskStep,\n} from '../model/outreach.types'\nimport type { OutreachTemplateOption } from './use-outreach-crm'\n\nexport interface OutreachStepCardProps {\n step: OutreachSequenceStep\n /** Its position, from 0. */\n index: number\n /** How many steps the sequence has, for the move controls. */\n count: number\n /** Whether it is the sequence's first email, which always starts the thread. */\n firstEmail: boolean\n /** Whether it is sent as a reply in the thread an earlier email started. */\n inThread: boolean\n /** The subject the thread was started with, for an in-thread email's note. */\n threadSubject: string\n templates: readonly OutreachTemplateOption[]\n /** The issue to show beside each field, by the field's own name. */\n issues: Partial<Record<string, string>>\n disabled?: boolean\n onChange(step: OutreachSequenceStep): void\n onMove(direction: -1 | 1): void\n onRemove(): void\n}\n\n/** The merge fields, grouped the way the picker heads them. */\nconst FIELD_GROUPS = [\n ...new Set(OUTREACH_MERGE_FIELDS.map((field) => field.group)),\n]\n\n/**\n * One step of the sequence editor (AGL-2980): an email — subject, body or a\n * CRM template, merge fields, the wait before it — or a task for the rep.\n */\nexport function OutreachStepCard(props: OutreachStepCardProps) {\n const { step, index, count, disabled } = props\n const title =\n step.kind === 'email'\n ? `Step ${index + 1} · Email`\n : `Step ${index + 1} · ${OUTREACH_TASK_KIND_LABELS[(step as OutreachTaskStep).taskKind] ?? 'Task'}`\n return (\n <Card variant=\"outlined\" component=\"section\" aria-label={title}>\n <CardContent>\n <Stack spacing={2}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"subtitle1\" sx={{ flexGrow: 1 }}>\n {title}\n </Typography>\n <Tooltip title=\"Move up\">\n <span>\n <IconButton\n size=\"small\"\n aria-label={`Move step ${index + 1} up`}\n disabled={disabled || index === 0}\n onClick={() => props.onMove(-1)}\n >\n <MdiIcon path={mdiArrowUp.path} fontSize=\"small\" />\n </IconButton>\n </span>\n </Tooltip>\n <Tooltip title=\"Move down\">\n <span>\n <IconButton\n size=\"small\"\n aria-label={`Move step ${index + 1} down`}\n disabled={disabled || index === count - 1}\n onClick={() => props.onMove(1)}\n >\n <MdiIcon path={mdiArrowDown.path} fontSize=\"small\" />\n </IconButton>\n </span>\n </Tooltip>\n <Tooltip title=\"Remove\">\n <span>\n <IconButton\n size=\"small\"\n aria-label={`Remove step ${index + 1}`}\n disabled={disabled || count === 1}\n onClick={props.onRemove}\n >\n <MdiIcon path={mdiTrashCanOutline.path} fontSize=\"small\" />\n </IconButton>\n </span>\n </Tooltip>\n </Stack>\n <DelayField {...props} />\n {step.kind === 'email' ? (\n <EmailStepFields {...props} step={step} />\n ) : step.kind === 'task' ? (\n <TaskStepFields {...props} step={step} />\n ) : (\n <Typography variant=\"body2\" color=\"error\">\n {props.issues['step'] ?? 'A step is an email or a task.'}\n </Typography>\n )}\n </Stack>\n </CardContent>\n </Card>\n )\n}\nOutreachStepCard.displayName = 'OutreachStepCard'\n\nfunction DelayField(props: OutreachStepCardProps) {\n const { step, index, disabled } = props\n const first = index === 0\n const issue = props.issues['delayBusinessDays']\n return (\n <TextField\n type=\"number\"\n label={\n first\n ? 'Wait after enrolling (business days)'\n : 'Wait after the step before (business days)'\n }\n value={\n Number.isFinite(step.delayBusinessDays) ? step.delayBusinessDays : ''\n }\n onChange={(event) =>\n props.onChange({\n ...step,\n delayBusinessDays:\n event.target.value === '' ? Number.NaN : Number(event.target.value),\n })\n }\n disabled={disabled}\n error={Boolean(issue)}\n helperText={\n issue ??\n (first\n ? '0 sends at the next opening of the sending hours.'\n : 'Counted in the mailbox’s timezone, skipping weekends.')\n }\n slotProps={{\n htmlInput: {\n min: 0,\n max: OUTREACH_MAX_STEP_DELAY_BUSINESS_DAYS,\n step: 1,\n },\n }}\n sx={{ maxWidth: { sm: 360 } }}\n />\n )\n}\n\nfunction EmailStepFields(\n props: OutreachStepCardProps & { step: OutreachEmailStep },\n) {\n const { step, disabled, issues } = props\n const subjectRef = useRef<HTMLInputElement | null>(null)\n const bodyRef = useRef<HTMLTextAreaElement | null>(null)\n const lastFocused = useRef<'subject' | 'body'>('body')\n const [fieldsAnchor, setFieldsAnchor] = useState<HTMLElement | null>(null)\n const template =\n props.templates.find((option) => option.id === step.templateId) ?? null\n const showSubject = !props.inThread\n\n /** Puts `{{key}}` where the caret was in the field last focused. */\n const insertField = (key: string) => {\n setFieldsAnchor(null)\n const token = `{{${key}}}`\n const target =\n lastFocused.current === 'subject' && showSubject\n ? 'subject'\n : template\n ? null\n : 'body'\n if (!target) return\n const element = target === 'subject' ? subjectRef.current : bodyRef.current\n const value = step[target]\n const start = element?.selectionStart ?? value.length\n const end = element?.selectionEnd ?? value.length\n props.onChange({\n ...step,\n [target]: `${value.slice(0, start)}${token}${value.slice(end)}`,\n })\n requestAnimationFrame(() => {\n element?.focus()\n element?.setSelectionRange(start + token.length, start + token.length)\n })\n }\n\n return (\n <Stack spacing={2}>\n {props.firstEmail ? null : (\n <FormControlLabel\n control={\n <Switch\n checked={step.replyInThread !== false}\n onChange={(event) =>\n props.onChange({ ...step, replyInThread: event.target.checked })\n }\n disabled={disabled}\n />\n }\n label=\"Reply in the same thread\"\n />\n )}\n {showSubject ? (\n <TextField\n label=\"Subject\"\n value={step.subject}\n inputRef={subjectRef}\n onFocus={() => (lastFocused.current = 'subject')}\n onChange={(event) =>\n props.onChange({ ...step, subject: event.target.value })\n }\n disabled={disabled}\n error={Boolean(issues['subject'])}\n helperText={issues['subject']}\n fullWidth\n />\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {props.threadSubject\n ? `Sent as a reply: “Re: ${props.threadSubject}”.`\n : 'Sent as a reply in the thread the earlier email started.'}\n </Typography>\n )}\n <Autocomplete<OutreachTemplateOption>\n options={[...props.templates]}\n value={template}\n getOptionLabel={(option) => option.name}\n isOptionEqualToValue={(option, value) => option.id === value.id}\n onChange={(_event, next) =>\n props.onChange({\n ...step,\n templateId: next?.id ?? null,\n body: next ? '' : step.body,\n })\n }\n disabled={disabled}\n renderInput={(params) => (\n <TextField\n {...params}\n label=\"CRM email template\"\n helperText={\n template\n ? 'The template’s body is sent; this step’s own subject is the one used.'\n : 'Optional. Pick one to send its body instead of writing it here.'\n }\n />\n )}\n />\n {template ? (\n <TextField\n label=\"Body (from the template)\"\n value={template.body}\n multiline\n minRows={4}\n fullWidth\n disabled\n />\n ) : (\n <TextField\n label=\"Body\"\n value={step.body}\n inputRef={bodyRef}\n onFocus={() => (lastFocused.current = 'body')}\n onChange={(event) =>\n props.onChange({ ...step, body: event.target.value })\n }\n disabled={disabled}\n error={Boolean(issues['body'])}\n helperText={issues['body']}\n multiline\n minRows={6}\n fullWidth\n />\n )}\n <Stack direction=\"row\" spacing={1}>\n <Button\n size=\"small\"\n variant=\"outlined\"\n startIcon={<MdiIcon path={mdiCodeBraces.path} fontSize=\"small\" />}\n onClick={(event) => setFieldsAnchor(event.currentTarget)}\n disabled={disabled || (Boolean(template) && !showSubject)}\n aria-haspopup=\"menu\"\n >\n Insert field\n </Button>\n </Stack>\n <Menu\n anchorEl={fieldsAnchor}\n open={Boolean(fieldsAnchor)}\n onClose={() => setFieldsAnchor(null)}\n >\n {FIELD_GROUPS.flatMap((group) => [\n <ListSubheader key={`group-${group}`}>{group}</ListSubheader>,\n ...OUTREACH_MERGE_FIELDS.filter((field) => field.group === group).map(\n (field) => (\n <MenuItem key={field.key} onClick={() => insertField(field.key)}>\n {field.label}\n </MenuItem>\n ),\n ),\n ])}\n </Menu>\n </Stack>\n )\n}\n\nfunction TaskStepFields(\n props: OutreachStepCardProps & { step: OutreachTaskStep },\n) {\n const { step, disabled, issues } = props\n return (\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={2}>\n <TextField\n select\n label=\"Task\"\n value={OUTREACH_TASK_KINDS.includes(step.taskKind) ? step.taskKind : ''}\n onChange={(event) =>\n props.onChange({\n ...step,\n taskKind: event.target.value as OutreachTaskKind,\n })\n }\n disabled={disabled}\n error={Boolean(issues['taskKind'])}\n helperText={issues['taskKind']}\n sx={{ minWidth: { sm: 180 } }}\n >\n {OUTREACH_TASK_KINDS.map((kind) => (\n <MenuItem key={kind} value={kind}>\n {OUTREACH_TASK_KIND_LABELS[kind]}\n </MenuItem>\n ))}\n </TextField>\n <TextField\n label=\"Title\"\n value={step.title}\n onChange={(event) =>\n props.onChange({ ...step, title: event.target.value })\n }\n disabled={disabled}\n error={Boolean(issues['title'])}\n helperText={\n issues['title'] ??\n 'What the task says, for example “Connect on LinkedIn”.'\n }\n fullWidth\n />\n </Stack>\n )\n}\n\nexport default OutreachStepCard\n"],"names":["mdiArrowDown","mdiArrowUp","mdiCodeBraces","mdiTrashCanOutline","MdiIcon","Autocomplete","Button","Card","CardContent","FormControlLabel","IconButton","ListSubheader","Menu","MenuItem","Stack","Switch","TextField","Tooltip","Typography","useRef","useState","OUTREACH_MERGE_FIELDS","OUTREACH_MAX_STEP_DELAY_BUSINESS_DAYS","OUTREACH_TASK_KIND_LABELS","OUTREACH_TASK_KINDS","FIELD_GROUPS","Set","map","field","group","OutreachStepCard","props","step","index","count","disabled","title","kind","taskKind","variant","component","aria-label","spacing","direction","sx","alignItems","flexGrow","span","size","onClick","onMove","path","fontSize","onRemove","DelayField","EmailStepFields","TaskStepFields","color","issues","displayName","first","issue","type","label","value","Number","isFinite","delayBusinessDays","onChange","event","target","NaN","error","Boolean","helperText","slotProps","htmlInput","min","max","maxWidth","sm","subjectRef","bodyRef","lastFocused","fieldsAnchor","setFieldsAnchor","template","templates","find","option","id","templateId","showSubject","inThread","insertField","key","token","current","element","start","selectionStart","length","end","selectionEnd","slice","requestAnimationFrame","focus","setSelectionRange","firstEmail","control","checked","replyInThread","subject","inputRef","onFocus","fullWidth","threadSubject","options","getOptionLabel","name","isOptionEqualToValue","_event","next","body","renderInput","params","multiline","minRows","startIcon","currentTarget","aria-haspopup","anchorEl","open","onClose","flatMap","filter","xs","select","includes","minWidth"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/components/sequence-step-card.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n mdiArrowDown,\n mdiArrowUp,\n mdiCodeBraces,\n mdiEmailFastOutline,\n mdiTrashCanOutline,\n} from '@aglyn/shared-data-mdi'\nimport { MdiIcon } from '@aglyn/shared-ui-jsx'\nimport {\n Autocomplete,\n Button,\n Card,\n CardContent,\n FormControlLabel,\n IconButton,\n ListSubheader,\n Menu,\n MenuItem,\n Stack,\n Switch,\n TextField,\n Tooltip,\n Typography,\n} from '@mui/material'\nimport { useRef, useState } from 'react'\nimport { OUTREACH_MERGE_FIELDS } from '../engine/sequence-validation'\nimport {\n OUTREACH_MAX_STEP_DELAY_BUSINESS_DAYS,\n OUTREACH_TASK_KIND_LABELS,\n OUTREACH_TASK_KINDS,\n type OutreachEmailStep,\n type OutreachSequenceStep,\n type OutreachTaskKind,\n type OutreachTaskStep,\n} from '../model/outreach.types'\nimport type { OutreachTemplateOption } from './use-outreach-crm'\n\nexport interface OutreachStepCardProps {\n step: OutreachSequenceStep\n /** Its position, from 0. */\n index: number\n /** How many steps the sequence has, for the move controls. */\n count: number\n /** Whether it is the sequence's first email, which always starts the thread. */\n firstEmail: boolean\n /** Whether it is sent as a reply in the thread an earlier email started. */\n inThread: boolean\n /** The subject the thread was started with, for an in-thread email's note. */\n threadSubject: string\n templates: readonly OutreachTemplateOption[]\n /** The issue to show beside each field, by the field's own name. */\n issues: Partial<Record<string, string>>\n disabled?: boolean\n onChange(step: OutreachSequenceStep): void\n onMove(direction: -1 | 1): void\n onRemove(): void\n /**\n * Opens the test-send dialog for this email step (AGL-3325). Absent for a\n * step that cannot be tested — a task, or an archived sequence.\n */\n onSendTest?(): void\n /** Why the test cannot be sent yet, shown on the disabled button; `null` when it can. */\n sendTestDisabledReason?: string | null\n}\n\n/** The merge fields, grouped the way the picker heads them. */\nconst FIELD_GROUPS = [\n ...new Set(OUTREACH_MERGE_FIELDS.map((field) => field.group)),\n]\n\n/**\n * One step of the sequence editor (AGL-2980): an email — subject, body or a\n * CRM template, merge fields, the wait before it — or a task for the rep.\n */\nexport function OutreachStepCard(props: OutreachStepCardProps) {\n const { step, index, count, disabled } = props\n const title =\n step.kind === 'email'\n ? `Step ${index + 1} · Email`\n : `Step ${index + 1} · ${OUTREACH_TASK_KIND_LABELS[(step as OutreachTaskStep).taskKind] ?? 'Task'}`\n return (\n <Card variant=\"outlined\" component=\"section\" aria-label={title}>\n <CardContent>\n <Stack spacing={2}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"subtitle1\" sx={{ flexGrow: 1 }}>\n {title}\n </Typography>\n <Tooltip title=\"Move up\">\n <span>\n <IconButton\n size=\"small\"\n aria-label={`Move step ${index + 1} up`}\n disabled={disabled || index === 0}\n onClick={() => props.onMove(-1)}\n >\n <MdiIcon path={mdiArrowUp.path} fontSize=\"small\" />\n </IconButton>\n </span>\n </Tooltip>\n <Tooltip title=\"Move down\">\n <span>\n <IconButton\n size=\"small\"\n aria-label={`Move step ${index + 1} down`}\n disabled={disabled || index === count - 1}\n onClick={() => props.onMove(1)}\n >\n <MdiIcon path={mdiArrowDown.path} fontSize=\"small\" />\n </IconButton>\n </span>\n </Tooltip>\n <Tooltip title=\"Remove\">\n <span>\n <IconButton\n size=\"small\"\n aria-label={`Remove step ${index + 1}`}\n disabled={disabled || count === 1}\n onClick={props.onRemove}\n >\n <MdiIcon path={mdiTrashCanOutline.path} fontSize=\"small\" />\n </IconButton>\n </span>\n </Tooltip>\n </Stack>\n <DelayField {...props} />\n {step.kind === 'email' ? (\n <EmailStepFields {...props} step={step} />\n ) : step.kind === 'task' ? (\n <TaskStepFields {...props} step={step} />\n ) : (\n <Typography variant=\"body2\" color=\"error\">\n {props.issues['step'] ?? 'A step is an email or a task.'}\n </Typography>\n )}\n </Stack>\n </CardContent>\n </Card>\n )\n}\nOutreachStepCard.displayName = 'OutreachStepCard'\n\nfunction DelayField(props: OutreachStepCardProps) {\n const { step, index, disabled } = props\n const first = index === 0\n const issue = props.issues['delayBusinessDays']\n return (\n <TextField\n type=\"number\"\n label={\n first\n ? 'Wait after enrolling (business days)'\n : 'Wait after the step before (business days)'\n }\n value={\n Number.isFinite(step.delayBusinessDays) ? step.delayBusinessDays : ''\n }\n onChange={(event) =>\n props.onChange({\n ...step,\n delayBusinessDays:\n event.target.value === '' ? Number.NaN : Number(event.target.value),\n })\n }\n disabled={disabled}\n error={Boolean(issue)}\n helperText={\n issue ??\n (first\n ? '0 sends at the next opening of the sending hours.'\n : 'Counted in the mailbox’s timezone, skipping weekends.')\n }\n slotProps={{\n htmlInput: {\n min: 0,\n max: OUTREACH_MAX_STEP_DELAY_BUSINESS_DAYS,\n step: 1,\n },\n }}\n sx={{ maxWidth: { sm: 360 } }}\n />\n )\n}\n\nfunction EmailStepFields(\n props: OutreachStepCardProps & { step: OutreachEmailStep },\n) {\n const { step, disabled, issues } = props\n const subjectRef = useRef<HTMLInputElement | null>(null)\n const bodyRef = useRef<HTMLTextAreaElement | null>(null)\n const lastFocused = useRef<'subject' | 'body'>('body')\n const [fieldsAnchor, setFieldsAnchor] = useState<HTMLElement | null>(null)\n const template =\n props.templates.find((option) => option.id === step.templateId) ?? null\n const showSubject = !props.inThread\n\n /** Puts `{{key}}` where the caret was in the field last focused. */\n const insertField = (key: string) => {\n setFieldsAnchor(null)\n const token = `{{${key}}}`\n const target =\n lastFocused.current === 'subject' && showSubject\n ? 'subject'\n : template\n ? null\n : 'body'\n if (!target) return\n const element = target === 'subject' ? subjectRef.current : bodyRef.current\n const value = step[target]\n const start = element?.selectionStart ?? value.length\n const end = element?.selectionEnd ?? value.length\n props.onChange({\n ...step,\n [target]: `${value.slice(0, start)}${token}${value.slice(end)}`,\n })\n requestAnimationFrame(() => {\n element?.focus()\n element?.setSelectionRange(start + token.length, start + token.length)\n })\n }\n\n return (\n <Stack spacing={2}>\n {props.firstEmail ? null : (\n <FormControlLabel\n control={\n <Switch\n checked={step.replyInThread !== false}\n onChange={(event) =>\n props.onChange({ ...step, replyInThread: event.target.checked })\n }\n disabled={disabled}\n />\n }\n label=\"Reply in the same thread\"\n />\n )}\n {showSubject ? (\n <TextField\n label=\"Subject\"\n value={step.subject}\n inputRef={subjectRef}\n onFocus={() => (lastFocused.current = 'subject')}\n onChange={(event) =>\n props.onChange({ ...step, subject: event.target.value })\n }\n disabled={disabled}\n error={Boolean(issues['subject'])}\n helperText={issues['subject']}\n fullWidth\n />\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {props.threadSubject\n ? `Sent as a reply: “Re: ${props.threadSubject}”.`\n : 'Sent as a reply in the thread the earlier email started.'}\n </Typography>\n )}\n <Autocomplete<OutreachTemplateOption>\n options={[...props.templates]}\n value={template}\n getOptionLabel={(option) => option.name}\n isOptionEqualToValue={(option, value) => option.id === value.id}\n onChange={(_event, next) =>\n props.onChange({\n ...step,\n templateId: next?.id ?? null,\n body: next ? '' : step.body,\n })\n }\n disabled={disabled}\n renderInput={(params) => (\n <TextField\n {...params}\n label=\"CRM email template\"\n helperText={\n template\n ? 'The template’s body is sent; this step’s own subject is the one used.'\n : 'Optional. Pick one to send its body instead of writing it here.'\n }\n />\n )}\n />\n {template ? (\n <TextField\n label=\"Body (from the template)\"\n value={template.body}\n multiline\n minRows={4}\n fullWidth\n disabled\n />\n ) : (\n <TextField\n label=\"Body\"\n value={step.body}\n inputRef={bodyRef}\n onFocus={() => (lastFocused.current = 'body')}\n onChange={(event) =>\n props.onChange({ ...step, body: event.target.value })\n }\n disabled={disabled}\n error={Boolean(issues['body'])}\n helperText={issues['body']}\n multiline\n minRows={6}\n fullWidth\n />\n )}\n <Stack direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', rowGap: 1 }}>\n <Button\n size=\"small\"\n variant=\"outlined\"\n startIcon={<MdiIcon path={mdiCodeBraces.path} fontSize=\"small\" />}\n onClick={(event) => setFieldsAnchor(event.currentTarget)}\n disabled={disabled || (Boolean(template) && !showSubject)}\n aria-haspopup=\"menu\"\n >\n Insert field\n </Button>\n {props.onSendTest ? (\n <Tooltip title={props.sendTestDisabledReason ?? ''}>\n <span>\n <Button\n size=\"small\"\n variant=\"outlined\"\n startIcon={<MdiIcon path={mdiEmailFastOutline.path} fontSize=\"small\" />}\n onClick={props.onSendTest}\n disabled={Boolean(props.sendTestDisabledReason)}\n aria-label={`Send a test of step ${props.index + 1}`}\n >\n Send a test\n </Button>\n </span>\n </Tooltip>\n ) : null}\n </Stack>\n <Menu\n anchorEl={fieldsAnchor}\n open={Boolean(fieldsAnchor)}\n onClose={() => setFieldsAnchor(null)}\n >\n {FIELD_GROUPS.flatMap((group) => [\n <ListSubheader key={`group-${group}`}>{group}</ListSubheader>,\n ...OUTREACH_MERGE_FIELDS.filter((field) => field.group === group).map(\n (field) => (\n <MenuItem key={field.key} onClick={() => insertField(field.key)}>\n {field.label}\n </MenuItem>\n ),\n ),\n ])}\n </Menu>\n </Stack>\n )\n}\n\nfunction TaskStepFields(\n props: OutreachStepCardProps & { step: OutreachTaskStep },\n) {\n const { step, disabled, issues } = props\n return (\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={2}>\n <TextField\n select\n label=\"Task\"\n value={OUTREACH_TASK_KINDS.includes(step.taskKind) ? step.taskKind : ''}\n onChange={(event) =>\n props.onChange({\n ...step,\n taskKind: event.target.value as OutreachTaskKind,\n })\n }\n disabled={disabled}\n error={Boolean(issues['taskKind'])}\n helperText={issues['taskKind']}\n sx={{ minWidth: { sm: 180 } }}\n >\n {OUTREACH_TASK_KINDS.map((kind) => (\n <MenuItem key={kind} value={kind}>\n {OUTREACH_TASK_KIND_LABELS[kind]}\n </MenuItem>\n ))}\n </TextField>\n <TextField\n label=\"Title\"\n value={step.title}\n onChange={(event) =>\n props.onChange({ ...step, title: event.target.value })\n }\n disabled={disabled}\n error={Boolean(issues['title'])}\n helperText={\n issues['title'] ??\n 'What the task says, for example “Connect on LinkedIn”.'\n }\n fullWidth\n />\n </Stack>\n )\n}\n\nexport default OutreachStepCard\n"],"names":["mdiArrowDown","mdiArrowUp","mdiCodeBraces","mdiEmailFastOutline","mdiTrashCanOutline","MdiIcon","Autocomplete","Button","Card","CardContent","FormControlLabel","IconButton","ListSubheader","Menu","MenuItem","Stack","Switch","TextField","Tooltip","Typography","useRef","useState","OUTREACH_MERGE_FIELDS","OUTREACH_MAX_STEP_DELAY_BUSINESS_DAYS","OUTREACH_TASK_KIND_LABELS","OUTREACH_TASK_KINDS","FIELD_GROUPS","Set","map","field","group","OutreachStepCard","props","step","index","count","disabled","title","kind","taskKind","variant","component","aria-label","spacing","direction","sx","alignItems","flexGrow","span","size","onClick","onMove","path","fontSize","onRemove","DelayField","EmailStepFields","TaskStepFields","color","issues","displayName","first","issue","type","label","value","Number","isFinite","delayBusinessDays","onChange","event","target","NaN","error","Boolean","helperText","slotProps","htmlInput","min","max","maxWidth","sm","subjectRef","bodyRef","lastFocused","fieldsAnchor","setFieldsAnchor","template","templates","find","option","id","templateId","showSubject","inThread","insertField","key","token","current","element","start","selectionStart","length","end","selectionEnd","slice","requestAnimationFrame","focus","setSelectionRange","firstEmail","control","checked","replyInThread","subject","inputRef","onFocus","fullWidth","threadSubject","options","getOptionLabel","name","isOptionEqualToValue","_event","next","body","renderInput","params","multiline","minRows","flexWrap","rowGap","startIcon","currentTarget","aria-haspopup","onSendTest","sendTestDisabledReason","anchorEl","open","onClose","flatMap","filter","xs","select","includes","minWidth"],"mappings":"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"}
|
|
@@ -26,7 +26,7 @@ import { ListTable } from "@aglyn/shared-ui-jsx/components/list-table.component"
|
|
|
26
26
|
import { filterListRows, inMemoryListField, listFilterGridColumns } from "@aglyn/shared-ui-jsx/const/list-grid-filter";
|
|
27
27
|
import { TABLE_PAGE_SIZE_DEFAULT } from "@aglyn/shared-ui-jsx/const/table-pagination";
|
|
28
28
|
import { useListGridFilter } from "@aglyn/shared-ui-jsx/hooks/use-list-grid-filter";
|
|
29
|
-
import { Button, Card, CardContent, Chip, Stack, Typography, useMediaQuery, useTheme } from "@mui/material";
|
|
29
|
+
import { Alert, Button, Card, CardContent, Chip, Stack, Typography, useMediaQuery, useTheme } from "@mui/material";
|
|
30
30
|
import { useEffect, useMemo, useState } from "react";
|
|
31
31
|
import { OUTREACH_SEQUENCE_STATUSES } from "../model/outreach.types.js";
|
|
32
32
|
import { OUTREACH_SEQUENCE_STATUS_LABELS, OutreachLink, OutreachLoading, OutreachLoadProblem, OutreachSequenceStatusChip, useOutreachNavigate } from "./outreach-ui.js";
|
|
@@ -216,6 +216,13 @@ const SEQUENCE_FILTER_OPTIONS = {
|
|
|
216
216
|
children: "New sequence"
|
|
217
217
|
});
|
|
218
218
|
const count = (sequence, key)=>counts[sequence.id] ? String(counts[sequence.id][key]) : '—';
|
|
219
|
+
/*
|
|
220
|
+
* The read stops at `OUTREACH_SEQUENCES_LIMIT`, newest first, and probes one
|
|
221
|
+
* past it, so this shows only when older sequences really exist.
|
|
222
|
+
*/ const truncatedNotice = sequences.truncated ? /*#__PURE__*/ _jsx(Alert, {
|
|
223
|
+
severity: "info",
|
|
224
|
+
children: `Showing the ${sequences.data.length.toLocaleString()} newest sequences. ` + 'The filters and the search narrow these only; older sequences are ' + 'not listed here.'
|
|
225
|
+
}) : null;
|
|
219
226
|
const chips = /*#__PURE__*/ _jsx(ListFilterChips, {
|
|
220
227
|
fields: SEQUENCE_FILTER_FIELDS,
|
|
221
228
|
headers: SEQUENCE_FILTER_HEADERS,
|
|
@@ -302,7 +309,8 @@ const SEQUENCE_FILTER_OPTIONS = {
|
|
|
302
309
|
})
|
|
303
310
|
}, sequence.id))
|
|
304
311
|
}),
|
|
305
|
-
footer
|
|
312
|
+
footer,
|
|
313
|
+
truncatedNotice
|
|
306
314
|
]
|
|
307
315
|
});
|
|
308
316
|
} else {
|
|
@@ -362,7 +370,8 @@ const SEQUENCE_FILTER_OPTIONS = {
|
|
|
362
370
|
noRowsLabel: "No sequences match these filters",
|
|
363
371
|
hideFooter: true
|
|
364
372
|
}),
|
|
365
|
-
footer
|
|
373
|
+
footer,
|
|
374
|
+
truncatedNotice
|
|
366
375
|
]
|
|
367
376
|
});
|
|
368
377
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/components/sequences-section.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, type ConsolePluginOrgMount } from '@aglyn/aglyn'\nimport { mdiPlus } from '@aglyn/shared-data-mdi'\nimport { CardDisplay, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport EmptyStateComponent from '@aglyn/shared-ui-jsx/components/empty-state.component'\nimport ListFilterChips from '@aglyn/shared-ui-jsx/components/list-filter-chips.component'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport {\n ListTable,\n type ListTableProps,\n} from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport {\n filterListRows,\n inMemoryListField,\n listFilterGridColumns,\n} from '@aglyn/shared-ui-jsx/const/list-grid-filter'\nimport { TABLE_PAGE_SIZE_DEFAULT } from '@aglyn/shared-ui-jsx/const/table-pagination'\nimport { useListGridFilter } from '@aglyn/shared-ui-jsx/hooks/use-list-grid-filter'\nimport {\n Button,\n Card,\n CardContent,\n Chip,\n Stack,\n Typography,\n useMediaQuery,\n useTheme,\n} from '@mui/material'\nimport { useEffect, useMemo, useState } from 'react'\nimport {\n OUTREACH_SEQUENCE_STATUSES,\n type OutreachMailbox,\n type OutreachSequence,\n} from '../model/outreach.types'\nimport {\n OUTREACH_SEQUENCE_STATUS_LABELS,\n OutreachLink,\n OutreachLoading,\n OutreachLoadProblem,\n OutreachSequenceStatusChip,\n useOutreachNavigate,\n} from './outreach-ui'\nimport {\n OutreachSequenceDetail,\n type OutreachSequenceTab,\n} from './sequence-detail'\nimport { OutreachSequenceEditor } from './sequence-editor'\nimport { useOutreachApi } from './use-outreach-api'\nimport {\n type OutreachSequenceCounts,\n useOutreachSequenceCounts,\n useOutreachSequences,\n} from './use-outreach-data'\nimport { useOutreachMailboxes } from './use-outreach-mailboxes'\nimport { useOutreachComplianceSettings } from './use-outreach-settings'\n\nexport interface OutreachSequencesSectionProps {\n /** The organization the hub is mounted under; `null` until the shell knows it. */\n orgId: string | null\n orgMount?: ConsolePluginOrgMount\n org?: Record<string, unknown> | null\n /** The section's own path: `/[orgSlug]/outreach/sequences`. */\n sectionPath: string\n /** The path below the section: `[]`, `['new']`, `[id]` or `[id, 'enrollments']`. */\n subpath: readonly string[]\n /** The Mailboxes section: `/[orgSlug]/outreach/mailboxes`. */\n mailboxesPath: string\n /** The Compliance section: `/[orgSlug]/outreach/compliance`. */\n compliancePath: string\n}\n\n/** The new-sequence page's segment below the section. */\nexport const OUTREACH_NEW_SEQUENCE_SEGMENT = 'new'\n\n/**\n * The Sequences section (AGL-2980), and the pages below it: the list, a new\n * sequence, and one sequence's steps and enrollments. Each is a real URL, so\n * a sequence is a link a rep can keep and the back button walks them.\n */\nexport function OutreachSequencesSection(props: OutreachSequencesSectionProps) {\n const { orgId, sectionPath, subpath } = props\n const api = useOutreachApi(orgId)\n const settings = useOutreachComplianceSettings(api, orgId)\n const mailboxes = useOutreachMailboxes(orgId)\n const navigate = useOutreachNavigate()\n\n if (!orgId) return <OutreachLoading label=\"Loading sequences…\" />\n const [first, second] = subpath\n if (first === OUTREACH_NEW_SEQUENCE_SEGMENT) {\n return (\n <Stack spacing={2}>\n <OutreachLink href={sectionPath}>Sequences</OutreachLink>\n <Typography variant=\"h5\" component=\"h2\">\n New sequence\n </Typography>\n <OutreachSequenceEditor\n orgId={orgId}\n orgMount={props.orgMount}\n sequence={null}\n settings={settings}\n mailboxes={mailboxes}\n mailboxesPath={props.mailboxesPath}\n onSaved={(sequence) => navigate(`${sectionPath}/${sequence.id}`)}\n onCancel={() => navigate(sectionPath)}\n />\n </Stack>\n )\n }\n if (first) {\n return (\n <OutreachSequenceDetail\n orgId={orgId}\n orgMount={props.orgMount}\n org={props.org}\n sectionPath={sectionPath}\n sequenceId={first}\n tab={\n (second === 'enrollments'\n ? 'enrollments'\n : 'steps') as OutreachSequenceTab\n }\n settings={settings}\n mailboxes={mailboxes}\n mailboxesPath={props.mailboxesPath}\n compliancePath={props.compliancePath}\n />\n )\n }\n return (\n <OutreachSequenceList\n orgId={orgId}\n sectionPath={sectionPath}\n mailboxes={mailboxes.mailboxes}\n />\n )\n}\nOutreachSequencesSection.displayName = 'OutreachSequencesSection'\n\nconst COUNT_COLUMNS: ReadonlyArray<[keyof OutreachSequenceCounts, string]> = [\n ['enrolled', 'Enrolled'],\n ['active', 'Active'],\n ['replied', 'Replied'],\n ['bounced', 'Bounced'],\n ['optedOut', 'Opted out'],\n]\n\n/*\n * What the sequences grid's Filters panel offers (AGL-3317). The list holds\n * every sequence it read, so each clause and the quick search are answered\n * over that whole set before it is paged, never over the page on screen.\n */\nconst SEQUENCE_FILTER_FIELDS = [\n inMemoryListField('name', 'text'),\n inMemoryListField('mailbox', 'text'),\n inMemoryListField('status', 'select'),\n]\nconst SEQUENCE_FILTER_HEADERS: Readonly<Record<string, string>> = {\n name: 'Name',\n mailbox: 'Mailbox',\n status: 'Status',\n}\nconst SEQUENCE_FILTER_OPTIONS = {\n status: OUTREACH_SEQUENCE_STATUSES.map((status) => ({\n value: status,\n label: OUTREACH_SEQUENCE_STATUS_LABELS[status],\n })),\n}\n/** What the quick search reads on a sequence row. */\nconst SEQUENCE_SEARCH_FIELDS = ['name', 'mailbox'] as const\n\n/**\n * Every sequence: its mailbox, its status, and how many people it enrolled\n * and where they stand — a table on wider screens, cards on a phone.\n */\nexport function OutreachSequenceList(props: {\n orgId: string\n sectionPath: string\n mailboxes: readonly OutreachMailbox[]\n}) {\n const { orgId, sectionPath } = props\n const theme = useTheme()\n const narrow = useMediaQuery(theme.breakpoints.down('md'))\n const navigate = useOutreachNavigate()\n const sequences = useOutreachSequences(orgId)\n const [page, setPage] = useState(0)\n const [pageSize, setPageSize] = useState(TABLE_PAGE_SIZE_DEFAULT)\n const gridFilter = useListGridFilter({ selectFields: ['status'] })\n const mailboxLabel = (sequence: OutreachSequence) => {\n const mailbox = props.mailboxes.find(\n (entry) => entry.id === sequence.mailboxId,\n )\n return mailbox\n ? mailbox.sendAs || mailbox.email\n : sequence.mailboxId\n ? 'Mailbox removed'\n : 'No mailbox yet'\n }\n /*\n * Filtered BEFORE the page is cut: the list read every sequence (under\n * `OUTREACH_SEQUENCES_LIMIT`), so a clause or a search word answers over\n * all of them, and the footer counts the matches.\n */\n const searchKey = gridFilter.searchWords.join(' ')\n const matching = useMemo(\n () =>\n filterListRows(\n sequences.data.map((sequence) => ({\n sequence,\n name: sequence.name || 'Untitled',\n mailbox: mailboxLabel(sequence),\n status: sequence.status,\n })),\n SEQUENCE_FILTER_FIELDS,\n gridFilter.clauses,\n { paths: SEQUENCE_SEARCH_FIELDS, words: gridFilter.searchWords },\n ).map((row) => row.sequence),\n // `searchKey` stands for the words, which are a new array each render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [sequences.data, props.mailboxes, gridFilter.clauses, searchKey],\n )\n // A narrowed list starts again at its first page.\n useEffect(() => setPage(0), [gridFilter.clauses, searchKey])\n const pageRows = matching.slice(page * pageSize, (page + 1) * pageSize)\n // Counted for the page on screen only: five aggregations a sequence.\n const counts = useOutreachSequenceCounts(\n orgId,\n pageRows.map((sequence) => sequence.id),\n )\n const footer = (\n <ListPagination\n page={page}\n pageSize={pageSize}\n rowCount={pageRows.length}\n count={matching.length}\n onPageChange={setPage}\n onPageSizeChange={setPageSize}\n />\n )\n const newHref = `${sectionPath}/${OUTREACH_NEW_SEQUENCE_SEGMENT}`\n const newButton = (\n <Button\n variant=\"contained\"\n startIcon={<MdiIcon path={mdiPlus.path} fontSize=\"small\" />}\n href={newHref}\n onClick={(event) => {\n event.preventDefault()\n navigate(newHref)\n }}\n >\n New sequence\n </Button>\n )\n const count = (\n sequence: OutreachSequence,\n key: keyof OutreachSequenceCounts,\n ) => (counts[sequence.id] ? String(counts[sequence.id][key]) : '—')\n\n const chips = (\n <ListFilterChips\n fields={SEQUENCE_FILTER_FIELDS}\n headers={SEQUENCE_FILTER_HEADERS}\n clauses={gridFilter.clauses}\n onChange={gridFilter.setClauses}\n options={SEQUENCE_FILTER_OPTIONS}\n />\n )\n let body\n if (sequences.status === 'loading')\n body = <OutreachLoading label=\"Loading sequences…\" />\n else if (sequences.status === 'error' || sequences.status === 'refused') {\n body = <OutreachLoadProblem status={sequences.status} what=\"sequences\" />\n } else if (!sequences.data.length) {\n body = (\n <Card variant=\"outlined\">\n <EmptyStateComponent\n label=\"No sequences yet\"\n description=\"A sequence is the emails, and the tasks between them, one person gets from one rep.\"\n action={newButton}\n />\n </Card>\n )\n } else if (narrow) {\n body = (\n <Stack spacing={1}>\n {chips}\n <Stack\n spacing={1}\n component=\"ul\"\n sx={{ listStyle: 'none', p: 0, m: 0 }}\n aria-label=\"Sequences\"\n >\n {pageRows.map((sequence) => (\n <Card key={sequence.id} variant=\"outlined\" component=\"li\">\n <CardContent>\n <Stack spacing={1}>\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{\n alignItems: 'center',\n justifyContent: 'space-between',\n }}\n >\n <OutreachLink href={`${sectionPath}/${sequence.id}`}>\n {sequence.name || 'Untitled'}\n </OutreachLink>\n <OutreachSequenceStatusChip status={sequence.status} />\n </Stack>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {mailboxLabel(sequence)}\n </Typography>\n <Stack\n direction=\"row\"\n spacing={0.5}\n sx={{ flexWrap: 'wrap', rowGap: 0.5 }}\n >\n {COUNT_COLUMNS.map(([key, label]) => (\n <Chip\n key={key}\n size=\"small\"\n variant=\"outlined\"\n label={`${label} ${count(sequence, key)}`}\n />\n ))}\n </Stack>\n </Stack>\n </CardContent>\n </Card>\n ))}\n </Stack>\n {footer}\n </Stack>\n )\n } else {\n const columns: NonNullable<ListTableProps['columns']> = listFilterGridColumns([\n { field: 'name', headerName: 'Name', flex: 1, minWidth: 180 },\n { field: 'mailbox', headerName: 'Mailbox', flex: 1, minWidth: 180 },\n {\n field: 'status',\n headerName: 'Status',\n width: 110,\n renderCell: ({ row }) => (\n <OutreachSequenceStatusChip status={row.status} />\n ),\n },\n ...COUNT_COLUMNS.map(([key, label]) => ({\n field: key,\n headerName: label,\n width: 96,\n align: 'right' as const,\n headerAlign: 'right' as const,\n })),\n ], SEQUENCE_FILTER_FIELDS, SEQUENCE_FILTER_OPTIONS, SEQUENCE_FILTER_HEADERS)\n body = (\n <Stack spacing={1}>\n {chips}\n <ListTable\n aria-label=\"Sequences\"\n columns={columns}\n rows={pageRows.map((sequence) => ({\n $id: sequence.id,\n name: sequence.name || 'Untitled',\n mailbox: mailboxLabel(sequence),\n status: sequence.status,\n ...Object.fromEntries(\n COUNT_COLUMNS.map(([key]) => [key, count(sequence, key)]),\n ),\n }))}\n onOpen={(id) => navigate(`${sectionPath}/${id}`)}\n /*\n * The panel and the search are the grid's; the list answers them\n * over every sequence it read (AGL-3317).\n */\n filterMode=\"server\"\n filterModel={gridFilter.filterModel}\n onFilterModelChange={gridFilter.onFilterModelChange}\n quickFilter\n noRowsLabel=\"No sequences match these filters\"\n hideFooter\n />\n {footer}\n </Stack>\n )\n }\n\n return (\n <CardDisplay\n header=\"Sequences\"\n help={pluginDocsHelp('sequences', { anchor: '#sequences' })}\n HeaderProps={{\n action:\n sequences.status === 'ready' && sequences.data.length\n ? newButton\n : undefined,\n }}\n contentGutterX\n contentGutterY\n >\n {body}\n </CardDisplay>\n )\n}\nOutreachSequenceList.displayName = 'OutreachSequenceList'\n\nexport default OutreachSequencesSection\n"],"names":["pluginDocsHelp","mdiPlus","CardDisplay","MdiIcon","EmptyStateComponent","ListFilterChips","ListPagination","ListTable","filterListRows","inMemoryListField","listFilterGridColumns","TABLE_PAGE_SIZE_DEFAULT","useListGridFilter","Button","Card","CardContent","Chip","Stack","Typography","useMediaQuery","useTheme","useEffect","useMemo","useState","OUTREACH_SEQUENCE_STATUSES","OUTREACH_SEQUENCE_STATUS_LABELS","OutreachLink","OutreachLoading","OutreachLoadProblem","OutreachSequenceStatusChip","useOutreachNavigate","OutreachSequenceDetail","OutreachSequenceEditor","useOutreachApi","useOutreachSequenceCounts","useOutreachSequences","useOutreachMailboxes","useOutreachComplianceSettings","OUTREACH_NEW_SEQUENCE_SEGMENT","OutreachSequencesSection","props","orgId","sectionPath","subpath","api","settings","mailboxes","navigate","label","first","second","spacing","href","variant","component","orgMount","sequence","mailboxesPath","onSaved","id","onCancel","org","sequenceId","tab","compliancePath","OutreachSequenceList","displayName","COUNT_COLUMNS","SEQUENCE_FILTER_FIELDS","SEQUENCE_FILTER_HEADERS","name","mailbox","status","SEQUENCE_FILTER_OPTIONS","map","value","SEQUENCE_SEARCH_FIELDS","theme","narrow","breakpoints","down","sequences","page","setPage","pageSize","setPageSize","gridFilter","selectFields","mailboxLabel","find","entry","mailboxId","sendAs","email","searchKey","searchWords","join","matching","data","clauses","paths","words","row","pageRows","slice","counts","footer","rowCount","length","count","onPageChange","onPageSizeChange","newHref","newButton","startIcon","path","fontSize","onClick","event","preventDefault","key","String","chips","fields","headers","onChange","setClauses","options","body","what","description","action","sx","listStyle","p","m","aria-label","direction","alignItems","justifyContent","color","flexWrap","rowGap","size","columns","field","headerName","flex","minWidth","width","renderCell","align","headerAlign","rows","$id","Object","fromEntries","onOpen","filterMode","filterModel","onFilterModelChange","quickFilter","noRowsLabel","hideFooter","header","help","anchor","HeaderProps","undefined","contentGutterX","contentGutterY"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/components/sequences-section.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, type ConsolePluginOrgMount } from '@aglyn/aglyn'\nimport { mdiPlus } from '@aglyn/shared-data-mdi'\nimport { CardDisplay, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport EmptyStateComponent from '@aglyn/shared-ui-jsx/components/empty-state.component'\nimport ListFilterChips from '@aglyn/shared-ui-jsx/components/list-filter-chips.component'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport {\n ListTable,\n type ListTableProps,\n} from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport {\n filterListRows,\n inMemoryListField,\n listFilterGridColumns,\n} from '@aglyn/shared-ui-jsx/const/list-grid-filter'\nimport { TABLE_PAGE_SIZE_DEFAULT } from '@aglyn/shared-ui-jsx/const/table-pagination'\nimport { useListGridFilter } from '@aglyn/shared-ui-jsx/hooks/use-list-grid-filter'\nimport {\n Alert,\n Button,\n Card,\n CardContent,\n Chip,\n Stack,\n Typography,\n useMediaQuery,\n useTheme,\n} from '@mui/material'\nimport { useEffect, useMemo, useState } from 'react'\nimport {\n OUTREACH_SEQUENCE_STATUSES,\n type OutreachMailbox,\n type OutreachSequence,\n} from '../model/outreach.types'\nimport {\n OUTREACH_SEQUENCE_STATUS_LABELS,\n OutreachLink,\n OutreachLoading,\n OutreachLoadProblem,\n OutreachSequenceStatusChip,\n useOutreachNavigate,\n} from './outreach-ui'\nimport {\n OutreachSequenceDetail,\n type OutreachSequenceTab,\n} from './sequence-detail'\nimport { OutreachSequenceEditor } from './sequence-editor'\nimport { useOutreachApi } from './use-outreach-api'\nimport {\n type OutreachSequenceCounts,\n useOutreachSequenceCounts,\n useOutreachSequences,\n} from './use-outreach-data'\nimport { useOutreachMailboxes } from './use-outreach-mailboxes'\nimport { useOutreachComplianceSettings } from './use-outreach-settings'\n\nexport interface OutreachSequencesSectionProps {\n /** The organization the hub is mounted under; `null` until the shell knows it. */\n orgId: string | null\n orgMount?: ConsolePluginOrgMount\n org?: Record<string, unknown> | null\n /** The section's own path: `/[orgSlug]/outreach/sequences`. */\n sectionPath: string\n /** The path below the section: `[]`, `['new']`, `[id]` or `[id, 'enrollments']`. */\n subpath: readonly string[]\n /** The Mailboxes section: `/[orgSlug]/outreach/mailboxes`. */\n mailboxesPath: string\n /** The Compliance section: `/[orgSlug]/outreach/compliance`. */\n compliancePath: string\n}\n\n/** The new-sequence page's segment below the section. */\nexport const OUTREACH_NEW_SEQUENCE_SEGMENT = 'new'\n\n/**\n * The Sequences section (AGL-2980), and the pages below it: the list, a new\n * sequence, and one sequence's steps and enrollments. Each is a real URL, so\n * a sequence is a link a rep can keep and the back button walks them.\n */\nexport function OutreachSequencesSection(props: OutreachSequencesSectionProps) {\n const { orgId, sectionPath, subpath } = props\n const api = useOutreachApi(orgId)\n const settings = useOutreachComplianceSettings(api, orgId)\n const mailboxes = useOutreachMailboxes(orgId)\n const navigate = useOutreachNavigate()\n\n if (!orgId) return <OutreachLoading label=\"Loading sequences…\" />\n const [first, second] = subpath\n if (first === OUTREACH_NEW_SEQUENCE_SEGMENT) {\n return (\n <Stack spacing={2}>\n <OutreachLink href={sectionPath}>Sequences</OutreachLink>\n <Typography variant=\"h5\" component=\"h2\">\n New sequence\n </Typography>\n <OutreachSequenceEditor\n orgId={orgId}\n orgMount={props.orgMount}\n sequence={null}\n settings={settings}\n mailboxes={mailboxes}\n mailboxesPath={props.mailboxesPath}\n onSaved={(sequence) => navigate(`${sectionPath}/${sequence.id}`)}\n onCancel={() => navigate(sectionPath)}\n />\n </Stack>\n )\n }\n if (first) {\n return (\n <OutreachSequenceDetail\n orgId={orgId}\n orgMount={props.orgMount}\n org={props.org}\n sectionPath={sectionPath}\n sequenceId={first}\n tab={\n (second === 'enrollments'\n ? 'enrollments'\n : 'steps') as OutreachSequenceTab\n }\n settings={settings}\n mailboxes={mailboxes}\n mailboxesPath={props.mailboxesPath}\n compliancePath={props.compliancePath}\n />\n )\n }\n return (\n <OutreachSequenceList\n orgId={orgId}\n sectionPath={sectionPath}\n mailboxes={mailboxes.mailboxes}\n />\n )\n}\nOutreachSequencesSection.displayName = 'OutreachSequencesSection'\n\nconst COUNT_COLUMNS: ReadonlyArray<[keyof OutreachSequenceCounts, string]> = [\n ['enrolled', 'Enrolled'],\n ['active', 'Active'],\n ['replied', 'Replied'],\n ['bounced', 'Bounced'],\n ['optedOut', 'Opted out'],\n]\n\n/*\n * What the sequences grid's Filters panel offers (AGL-3317). The list holds\n * every sequence it read, so each clause and the quick search are answered\n * over that whole set before it is paged, never over the page on screen.\n */\nconst SEQUENCE_FILTER_FIELDS = [\n inMemoryListField('name', 'text'),\n inMemoryListField('mailbox', 'text'),\n inMemoryListField('status', 'select'),\n]\nconst SEQUENCE_FILTER_HEADERS: Readonly<Record<string, string>> = {\n name: 'Name',\n mailbox: 'Mailbox',\n status: 'Status',\n}\nconst SEQUENCE_FILTER_OPTIONS = {\n status: OUTREACH_SEQUENCE_STATUSES.map((status) => ({\n value: status,\n label: OUTREACH_SEQUENCE_STATUS_LABELS[status],\n })),\n}\n/** What the quick search reads on a sequence row. */\nconst SEQUENCE_SEARCH_FIELDS = ['name', 'mailbox'] as const\n\n/**\n * Every sequence: its mailbox, its status, and how many people it enrolled\n * and where they stand — a table on wider screens, cards on a phone.\n */\nexport function OutreachSequenceList(props: {\n orgId: string\n sectionPath: string\n mailboxes: readonly OutreachMailbox[]\n}) {\n const { orgId, sectionPath } = props\n const theme = useTheme()\n const narrow = useMediaQuery(theme.breakpoints.down('md'))\n const navigate = useOutreachNavigate()\n const sequences = useOutreachSequences(orgId)\n const [page, setPage] = useState(0)\n const [pageSize, setPageSize] = useState(TABLE_PAGE_SIZE_DEFAULT)\n const gridFilter = useListGridFilter({ selectFields: ['status'] })\n const mailboxLabel = (sequence: OutreachSequence) => {\n const mailbox = props.mailboxes.find(\n (entry) => entry.id === sequence.mailboxId,\n )\n return mailbox\n ? mailbox.sendAs || mailbox.email\n : sequence.mailboxId\n ? 'Mailbox removed'\n : 'No mailbox yet'\n }\n /*\n * Filtered BEFORE the page is cut: the list read every sequence (under\n * `OUTREACH_SEQUENCES_LIMIT`), so a clause or a search word answers over\n * all of them, and the footer counts the matches.\n */\n const searchKey = gridFilter.searchWords.join(' ')\n const matching = useMemo(\n () =>\n filterListRows(\n sequences.data.map((sequence) => ({\n sequence,\n name: sequence.name || 'Untitled',\n mailbox: mailboxLabel(sequence),\n status: sequence.status,\n })),\n SEQUENCE_FILTER_FIELDS,\n gridFilter.clauses,\n { paths: SEQUENCE_SEARCH_FIELDS, words: gridFilter.searchWords },\n ).map((row) => row.sequence),\n // `searchKey` stands for the words, which are a new array each render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [sequences.data, props.mailboxes, gridFilter.clauses, searchKey],\n )\n // A narrowed list starts again at its first page.\n useEffect(() => setPage(0), [gridFilter.clauses, searchKey])\n const pageRows = matching.slice(page * pageSize, (page + 1) * pageSize)\n // Counted for the page on screen only: five aggregations a sequence.\n const counts = useOutreachSequenceCounts(\n orgId,\n pageRows.map((sequence) => sequence.id),\n )\n const footer = (\n <ListPagination\n page={page}\n pageSize={pageSize}\n rowCount={pageRows.length}\n count={matching.length}\n onPageChange={setPage}\n onPageSizeChange={setPageSize}\n />\n )\n const newHref = `${sectionPath}/${OUTREACH_NEW_SEQUENCE_SEGMENT}`\n const newButton = (\n <Button\n variant=\"contained\"\n startIcon={<MdiIcon path={mdiPlus.path} fontSize=\"small\" />}\n href={newHref}\n onClick={(event) => {\n event.preventDefault()\n navigate(newHref)\n }}\n >\n New sequence\n </Button>\n )\n const count = (\n sequence: OutreachSequence,\n key: keyof OutreachSequenceCounts,\n ) => (counts[sequence.id] ? String(counts[sequence.id][key]) : '—')\n\n /*\n * The read stops at `OUTREACH_SEQUENCES_LIMIT`, newest first, and probes one\n * past it, so this shows only when older sequences really exist.\n */\n const truncatedNotice = sequences.truncated ? (\n <Alert severity=\"info\">\n {`Showing the ${sequences.data.length.toLocaleString()} newest sequences. ` +\n 'The filters and the search narrow these only; older sequences are ' +\n 'not listed here.'}\n </Alert>\n ) : null\n\n const chips = (\n <ListFilterChips\n fields={SEQUENCE_FILTER_FIELDS}\n headers={SEQUENCE_FILTER_HEADERS}\n clauses={gridFilter.clauses}\n onChange={gridFilter.setClauses}\n options={SEQUENCE_FILTER_OPTIONS}\n />\n )\n let body\n if (sequences.status === 'loading')\n body = <OutreachLoading label=\"Loading sequences…\" />\n else if (sequences.status === 'error' || sequences.status === 'refused') {\n body = <OutreachLoadProblem status={sequences.status} what=\"sequences\" />\n } else if (!sequences.data.length) {\n body = (\n <Card variant=\"outlined\">\n <EmptyStateComponent\n label=\"No sequences yet\"\n description=\"A sequence is the emails, and the tasks between them, one person gets from one rep.\"\n action={newButton}\n />\n </Card>\n )\n } else if (narrow) {\n body = (\n <Stack spacing={1}>\n {chips}\n <Stack\n spacing={1}\n component=\"ul\"\n sx={{ listStyle: 'none', p: 0, m: 0 }}\n aria-label=\"Sequences\"\n >\n {pageRows.map((sequence) => (\n <Card key={sequence.id} variant=\"outlined\" component=\"li\">\n <CardContent>\n <Stack spacing={1}>\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{\n alignItems: 'center',\n justifyContent: 'space-between',\n }}\n >\n <OutreachLink href={`${sectionPath}/${sequence.id}`}>\n {sequence.name || 'Untitled'}\n </OutreachLink>\n <OutreachSequenceStatusChip status={sequence.status} />\n </Stack>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {mailboxLabel(sequence)}\n </Typography>\n <Stack\n direction=\"row\"\n spacing={0.5}\n sx={{ flexWrap: 'wrap', rowGap: 0.5 }}\n >\n {COUNT_COLUMNS.map(([key, label]) => (\n <Chip\n key={key}\n size=\"small\"\n variant=\"outlined\"\n label={`${label} ${count(sequence, key)}`}\n />\n ))}\n </Stack>\n </Stack>\n </CardContent>\n </Card>\n ))}\n </Stack>\n {footer}\n {truncatedNotice}\n </Stack>\n )\n } else {\n const columns: NonNullable<ListTableProps['columns']> = listFilterGridColumns([\n { field: 'name', headerName: 'Name', flex: 1, minWidth: 180 },\n { field: 'mailbox', headerName: 'Mailbox', flex: 1, minWidth: 180 },\n {\n field: 'status',\n headerName: 'Status',\n width: 110,\n renderCell: ({ row }) => (\n <OutreachSequenceStatusChip status={row.status} />\n ),\n },\n ...COUNT_COLUMNS.map(([key, label]) => ({\n field: key,\n headerName: label,\n width: 96,\n align: 'right' as const,\n headerAlign: 'right' as const,\n })),\n ], SEQUENCE_FILTER_FIELDS, SEQUENCE_FILTER_OPTIONS, SEQUENCE_FILTER_HEADERS)\n body = (\n <Stack spacing={1}>\n {chips}\n <ListTable\n aria-label=\"Sequences\"\n columns={columns}\n rows={pageRows.map((sequence) => ({\n $id: sequence.id,\n name: sequence.name || 'Untitled',\n mailbox: mailboxLabel(sequence),\n status: sequence.status,\n ...Object.fromEntries(\n COUNT_COLUMNS.map(([key]) => [key, count(sequence, key)]),\n ),\n }))}\n onOpen={(id) => navigate(`${sectionPath}/${id}`)}\n /*\n * The panel and the search are the grid's; the list answers them\n * over every sequence it read (AGL-3317).\n */\n filterMode=\"server\"\n filterModel={gridFilter.filterModel}\n onFilterModelChange={gridFilter.onFilterModelChange}\n quickFilter\n noRowsLabel=\"No sequences match these filters\"\n hideFooter\n />\n {footer}\n {truncatedNotice}\n </Stack>\n )\n }\n\n return (\n <CardDisplay\n header=\"Sequences\"\n help={pluginDocsHelp('sequences', { anchor: '#sequences' })}\n HeaderProps={{\n action:\n sequences.status === 'ready' && sequences.data.length\n ? newButton\n : undefined,\n }}\n contentGutterX\n contentGutterY\n >\n {body}\n </CardDisplay>\n )\n}\nOutreachSequenceList.displayName = 'OutreachSequenceList'\n\nexport default OutreachSequencesSection\n"],"names":["pluginDocsHelp","mdiPlus","CardDisplay","MdiIcon","EmptyStateComponent","ListFilterChips","ListPagination","ListTable","filterListRows","inMemoryListField","listFilterGridColumns","TABLE_PAGE_SIZE_DEFAULT","useListGridFilter","Alert","Button","Card","CardContent","Chip","Stack","Typography","useMediaQuery","useTheme","useEffect","useMemo","useState","OUTREACH_SEQUENCE_STATUSES","OUTREACH_SEQUENCE_STATUS_LABELS","OutreachLink","OutreachLoading","OutreachLoadProblem","OutreachSequenceStatusChip","useOutreachNavigate","OutreachSequenceDetail","OutreachSequenceEditor","useOutreachApi","useOutreachSequenceCounts","useOutreachSequences","useOutreachMailboxes","useOutreachComplianceSettings","OUTREACH_NEW_SEQUENCE_SEGMENT","OutreachSequencesSection","props","orgId","sectionPath","subpath","api","settings","mailboxes","navigate","label","first","second","spacing","href","variant","component","orgMount","sequence","mailboxesPath","onSaved","id","onCancel","org","sequenceId","tab","compliancePath","OutreachSequenceList","displayName","COUNT_COLUMNS","SEQUENCE_FILTER_FIELDS","SEQUENCE_FILTER_HEADERS","name","mailbox","status","SEQUENCE_FILTER_OPTIONS","map","value","SEQUENCE_SEARCH_FIELDS","theme","narrow","breakpoints","down","sequences","page","setPage","pageSize","setPageSize","gridFilter","selectFields","mailboxLabel","find","entry","mailboxId","sendAs","email","searchKey","searchWords","join","matching","data","clauses","paths","words","row","pageRows","slice","counts","footer","rowCount","length","count","onPageChange","onPageSizeChange","newHref","newButton","startIcon","path","fontSize","onClick","event","preventDefault","key","String","truncatedNotice","truncated","severity","toLocaleString","chips","fields","headers","onChange","setClauses","options","body","what","description","action","sx","listStyle","p","m","aria-label","direction","alignItems","justifyContent","color","flexWrap","rowGap","size","columns","field","headerName","flex","minWidth","width","renderCell","align","headerAlign","rows","$id","Object","fromEntries","onOpen","filterMode","filterModel","onFilterModelChange","quickFilter","noRowsLabel","hideFooter","header","help","anchor","HeaderProps","undefined","contentGutterX","contentGutterY"],"mappings":"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"}
|