@aglyn/plugins-outreach 1.0.0-beta.196 → 1.0.0-beta.198
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/compliance-section.d.ts +3 -1
- package/src/lib/components/compliance-section.js +110 -45
- package/src/lib/components/compliance-section.js.map +1 -1
- package/src/lib/components/enroll-dialog.d.ts +7 -1
- package/src/lib/components/enroll-dialog.js +106 -3
- 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/outreach-ui.js +1 -0
- package/src/lib/components/outreach-ui.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 +51 -26
- 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/engine/delivery-status.d.ts +8 -0
- package/src/lib/engine/delivery-status.js +10 -5
- package/src/lib/engine/delivery-status.js.map +1 -1
- package/src/lib/engine/enrollment-state.d.ts +2 -0
- package/src/lib/engine/enrollment-state.js +2 -2
- package/src/lib/engine/enrollment-state.js.map +1 -1
- package/src/lib/engine/gates.d.ts +28 -1
- package/src/lib/engine/gates.js +28 -1
- package/src/lib/engine/gates.js.map +1 -1
- package/src/lib/engine/mail-gateway.d.ts +97 -0
- package/src/lib/engine/mail-gateway.js +226 -0
- package/src/lib/engine/mail-gateway.js.map +1 -0
- package/src/lib/engine/mailbox-health.d.ts +3 -3
- package/src/lib/engine/mailbox-health.js +17 -11
- package/src/lib/engine/mailbox-health.js.map +1 -1
- package/src/lib/engine/thread-classification.d.ts +2 -0
- package/src/lib/engine/thread-classification.js +2 -1
- package/src/lib/engine/thread-classification.js.map +1 -1
- package/src/lib/enrollment/enroll-people.d.ts +6 -1
- package/src/lib/enrollment/enroll-people.js +18 -4
- package/src/lib/enrollment/enroll-people.js.map +1 -1
- package/src/lib/enrollment/gate-lookups.d.ts +7 -0
- package/src/lib/enrollment/gate-lookups.js +11 -2
- package/src/lib/enrollment/gate-lookups.js.map +1 -1
- package/src/lib/model/outreach-api.d.ts +35 -1
- package/src/lib/model/outreach-api.js.map +1 -1
- package/src/lib/model/outreach.types.d.ts +85 -3
- package/src/lib/model/outreach.types.js +21 -2
- 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/enroll-routes.js +23 -4
- package/src/lib/routes/enroll-routes.js.map +1 -1
- package/src/lib/routes/enrollment-routes.js +15 -1
- package/src/lib/routes/enrollment-routes.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 +27 -0
- package/src/lib/routes/register-routes.js.map +1 -1
- package/src/lib/routes/route-deps.d.ts +6 -0
- package/src/lib/routes/route-deps.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
- package/src/lib/runtime/gateway-ledger.d.ts +46 -0
- package/src/lib/runtime/gateway-ledger.js +113 -0
- package/src/lib/runtime/gateway-ledger.js.map +1 -0
- package/src/lib/runtime/platform-runtime-deps.js +3 -0
- package/src/lib/runtime/platform-runtime-deps.js.map +1 -1
- package/src/lib/runtime/runtime-deps.d.ts +6 -0
- package/src/lib/runtime/runtime-deps.js.map +1 -1
- package/src/lib/runtime/send-job.js +48 -0
- package/src/lib/runtime/send-job.js.map +1 -1
- package/src/lib/runtime/sync-job.d.ts +2 -0
- package/src/lib/runtime/sync-job.js +40 -3
- package/src/lib/runtime/sync-job.js.map +1 -1
- package/src/lib/storage/domain-intel-store.d.ts +107 -0
- package/src/lib/storage/domain-intel-store.js +320 -0
- package/src/lib/storage/domain-intel-store.js.map +1 -0
|
@@ -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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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type OutreachSequence } from '../model/outreach.types';
|
|
2
|
+
import type { OutreachApi } from './use-outreach-api';
|
|
3
|
+
export interface OutreachStepTestDialogProps {
|
|
4
|
+
open: boolean;
|
|
5
|
+
onClose(): void;
|
|
6
|
+
orgId: string;
|
|
7
|
+
/** The stored sequence: the test is sent from what is saved. */
|
|
8
|
+
sequence: OutreachSequence;
|
|
9
|
+
stepIndex: number;
|
|
10
|
+
/** The consent group of the sequence's site: whose names a contact search shows. */
|
|
11
|
+
contactGroupId: string;
|
|
12
|
+
/** The member's own address, where the test goes unless they type another. */
|
|
13
|
+
selfEmail: string | null;
|
|
14
|
+
/** True while the editor holds edits the stored sequence does not. */
|
|
15
|
+
unsaved: boolean;
|
|
16
|
+
api: OutreachApi;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Send a test of one step (AGL-3325): the step as the sequence stores it,
|
|
20
|
+
* rendered for a chosen person — the sample person the preview uses, or an
|
|
21
|
+
* open lead or contact of the sequence's site — with the personal line the
|
|
22
|
+
* member types, sent through the sequence's mailbox to the member's own
|
|
23
|
+
* address or one they name. The route prefixes the subject with `[Test]`,
|
|
24
|
+
* counts it on nothing but the mailbox's test tally, and never enrolls the
|
|
25
|
+
* person whose record filled the tokens.
|
|
26
|
+
*/
|
|
27
|
+
export declare function OutreachStepTestDialog(props: OutreachStepTestDialogProps): import("react").JSX.Element;
|
|
28
|
+
export declare namespace OutreachStepTestDialog {
|
|
29
|
+
var displayName: string;
|
|
30
|
+
}
|
|
31
|
+
export default OutreachStepTestDialog;
|