@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/mailbox-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 { pluginDocsHelp } from '@aglyn/aglyn'\nimport { CardDisplay, useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n Autocomplete,\n Box,\n Button,\n Chip,\n FormControlLabel,\n FormHelperText,\n MenuItem,\n Stack,\n Switch,\n TextField,\n ToggleButton,\n ToggleButtonGroup,\n Typography,\n} from '@mui/material'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport {\n formatMinuteOfDay,\n isValidTimezone,\n OUTREACH_DEFAULT_WINDOW,\n OUTREACH_MAILBOX_STATUS_LABELS,\n OUTREACH_MAX_DAILY_CAP,\n OUTREACH_RAMP_STEPS,\n OUTREACH_WEEKDAY_LABELS,\n outreachEffectiveDailyCap,\n summarizeMailboxHealth,\n validateDailyCap,\n validateDisplayName,\n validateSendWindow,\n} from '../mailboxes/mailbox-settings'\nimport type {\n OutreachMailbox,\n OutreachMailboxStatus,\n OutreachSendWindow,\n} from '../model/outreach.types'\nimport type { OutreachMailboxApi } from './use-outreach-mailbox-api'\n\nexport interface MailboxCardProps {\n mailbox: OutreachMailbox\n /** The viewer is the member who connected it. */\n isMine: boolean\n /** The viewer may change, pause and disconnect it: its member, or an org owner or admin. */\n canManage: boolean\n api: OutreachMailboxApi\n /** Starts a new Google connect, for a mailbox that needs reconnecting. */\n onReconnect(): void\n /** True while that connect is on its way to Google. */\n reconnecting?: boolean\n /** The clock the health window and today's limit are read on. */\n nowMs?: number\n}\n\n/**\n * What the card says about a mailbox that paused ITSELF (AGL-2981): the\n * engine's own sentence, when it paused, and — after a complaint — the day\n * resuming stops being premature. Nothing sends from it until a member\n * resumes it.\n */\nexport function autoPauseSentence(mailbox: Pick<OutreachMailbox, 'autoPause' | 'timezone'>): string {\n const pause = mailbox.autoPause\n if (!pause) return ''\n const day = (atMs: number) => {\n try {\n return new Intl.DateTimeFormat('en-US', { dateStyle: 'medium', timeZone: mailbox.timezone }).format(atMs)\n } catch {\n return new Intl.DateTimeFormat('en-US', { dateStyle: 'medium', timeZone: 'UTC' }).format(atMs)\n }\n }\n const until = typeof pause.untilMs === 'number' ? ` Wait until ${day(pause.untilMs)} before resuming it.` : ''\n return `Sequences paused this mailbox on ${day(pause.atMs)}. ${pause.message}${until} Nothing sends from it until it is resumed.`\n}\n\n/** Accessible names of the card's actions, spelled once for the specs. */\nexport const MAILBOX_ACTION_LABELS = {\n save: 'Save settings',\n pause: 'Pause',\n resume: 'Resume',\n test: 'Send a test to myself',\n testElsewhere: 'Send a test',\n testAddress: 'Test address',\n warmUp: 'Warm up gradually',\n disconnect: 'Disconnect',\n reconnect: 'Reconnect',\n} as const\n\nconst WEEKDAY_NAMES = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']\n\nconst STATUS_COLOR: Record<OutreachMailboxStatus, 'success' | 'default' | 'warning' | 'error'> = {\n connected: 'success',\n paused: 'default',\n reconnect_required: 'warning',\n disconnected: 'error',\n}\n\n/** Every half hour of the day, and midnight at its end. */\nconst MINUTE_OPTIONS = Array.from({ length: 49 }, (_, index) => index * 30)\n\n/** The zones this runtime knows, the mailbox's own included whatever it is. */\nfunction timezoneOptions(current: string): string[] {\n const supported = (Intl as { supportedValuesOf?: (key: string) => string[] }).supportedValuesOf?.('timeZone') ?? []\n const zones = supported.length ? supported : ['UTC']\n return zones.includes(current) ? zones : [current, ...zones]\n}\n\n/** The ramp, as the cap's helper text reads it. */\nconst RAMP_SENTENCE = `Warm-up: ${OUTREACH_RAMP_STEPS.map((step, index) =>\n `week ${step.week}${index === OUTREACH_RAMP_STEPS.length - 1 ? '+' : ''}: ${step.limit}`,\n).join(' · ')} a day, never above the cap.`\n\ninterface Draft {\n sendAs: string\n displayName: string\n dailyCap: string\n window: OutreachSendWindow\n timezone: string\n /** Whether the warm-up ramp applies — `rampStartedAtMs` as a switch. */\n warmUp: boolean\n}\n\nconst draftOf = (mailbox: OutreachMailbox): Draft => ({\n sendAs: mailbox.sendAs ?? mailbox.email,\n displayName: mailbox.displayName ?? '',\n dailyCap: String(mailbox.dailyCap ?? ''),\n window: mailbox.window ?? OUTREACH_DEFAULT_WINDOW,\n timezone: mailbox.timezone ?? 'UTC',\n warmUp: typeof mailbox.rampStartedAtMs === 'number',\n})\n\n/**\n * One connected mailbox (AGL-2978): its account and status, its last seven\n * days, its settings, and pause, test and disconnect.\n *\n * Every control is offered only where the route will allow it: the settings\n * form and pause and disconnect to the member who connected the mailbox or an\n * org owner or admin, and the test and the reconnect to its member alone,\n * because both act in that member's own Google account.\n */\nexport function MailboxCard(props: MailboxCardProps) {\n const { mailbox, isMine, canManage, api, onReconnect, reconnecting = false, nowMs = Date.now() } = props\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const [draft, setDraft] = useState<Draft>(() => draftOf(mailbox))\n const [saving, setSaving] = useState(false)\n const [saveError, setSaveError] = useState<string | null>(null)\n const [busy, setBusy] = useState<'status' | 'test' | 'disconnect' | null>(null)\n // Where a test goes when not to the member themself (AGL-3228): a test to\n // one's own address never leaves Google and shows no authentication result.\n const [testAddress, setTestAddress] = useState('')\n\n // A save here, a change made from another tab, or a reconnect rewrites the\n // stored settings; the form follows them, unless the member has unsaved\n // edits of their own, which are never thrown away.\n const stored = useMemo(() => draftOf(mailbox), [mailbox])\n const storedKey = JSON.stringify(stored)\n const seededFrom = useRef(storedKey)\n const dirty = JSON.stringify(draft) !== storedKey\n useEffect(() => {\n if (seededFrom.current === storedKey) return\n const previous = seededFrom.current\n seededFrom.current = storedKey\n setDraft((current) => (JSON.stringify(current) === previous ? stored : current))\n }, [storedKey, stored])\n\n const health = summarizeMailboxHealth(mailbox.health, mailbox.timezone, nowMs)\n const needsReconnect = mailbox.status === 'reconnect_required' || mailbox.status === 'disconnected'\n const capCheck = validateDailyCap(draft.dailyCap === '' ? Number.NaN : Number(draft.dailyCap))\n const windowCheck = validateSendWindow(draft.window)\n const nameCheck = validateDisplayName(draft.displayName)\n const timezoneValid = isValidTimezone(draft.timezone)\n const invalid =\n typeof capCheck !== 'number' || !('days' in windowCheck) || typeof nameCheck !== 'string' || !timezoneValid\n const todayLimit = outreachEffectiveDailyCap(\n {\n dailyCap: typeof capCheck === 'number' ? capCheck : mailbox.dailyCap,\n // A ramp switched on in the draft reads as starting today.\n rampStartedAtMs: draft.warmUp ? (mailbox.rampStartedAtMs ?? nowMs) : null,\n timezone: timezoneValid ? draft.timezone : mailbox.timezone,\n },\n nowMs,\n )\n\n const save = async () => {\n setSaving(true)\n setSaveError(null)\n try {\n await api.saveSettings({\n mailboxId: mailbox.id,\n ...(draft.sendAs !== stored.sendAs ? { sendAs: draft.sendAs } : {}),\n ...(draft.displayName !== stored.displayName ? { displayName: draft.displayName } : {}),\n ...(draft.dailyCap !== stored.dailyCap ? { dailyCap: Number(draft.dailyCap) } : {}),\n ...(JSON.stringify(draft.window) !== JSON.stringify(stored.window) ? { window: draft.window } : {}),\n ...(draft.timezone !== stored.timezone ? { timezone: draft.timezone } : {}),\n ...(draft.warmUp !== stored.warmUp ? { warmUp: draft.warmUp } : {}),\n })\n enqueueSnackbar('Mailbox settings saved', { variant: 'success' })\n } catch (error) {\n setSaveError((error as Error).message)\n } finally {\n setSaving(false)\n }\n }\n\n const togglePaused = async () => {\n const paused = mailbox.status !== 'paused'\n setBusy('status')\n try {\n await api.setPaused(mailbox.id, paused)\n enqueueSnackbar(paused ? 'Mailbox paused' : 'Mailbox resumed', { variant: 'success' })\n } catch (error) {\n enqueueSnackbar((error as Error).message, { variant: 'error', allowDuplicate: true })\n } finally {\n setBusy(null)\n }\n }\n\n const sendTest = async () => {\n setBusy('test')\n try {\n const to = testAddress.trim()\n const sent = await api.sendTest(mailbox.id, to || undefined)\n enqueueSnackbar(\n to\n ? `Test sent to ${sent.sentTo}. Its original source shows whether the sender authenticated.`\n : `Test sent to ${sent.sentTo}. Check your inbox.`,\n { variant: 'success' },\n )\n } catch (error) {\n enqueueSnackbar((error as Error).message, { variant: 'error', allowDuplicate: true })\n } finally {\n setBusy(null)\n }\n }\n\n const disconnect = async () => {\n const confirmed = await confirm({\n title: 'Disconnect this mailbox?',\n description:\n 'Disconnecting deletes the access stored for this mailbox and asks Google to revoke it. ' +\n 'Nothing is sent from it again unless it is connected again.',\n confirmationText: MAILBOX_ACTION_LABELS.disconnect,\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n setBusy('disconnect')\n try {\n const { revocation } = await api.disconnect(mailbox.id)\n if (revocation === 'failed') {\n enqueueSnackbar(\n 'Mailbox disconnected, but Google could not confirm the access was revoked. ' +\n 'Remove it from the Google Account’s third-party connections to be sure.',\n { variant: 'warning', persist: true },\n )\n } else if (revocation === 'kept-for-other-mailbox') {\n enqueueSnackbar(\n 'Mailbox disconnected. Google access stays in place for another connected mailbox on the same account.',\n { variant: 'success' },\n )\n } else {\n enqueueSnackbar('Mailbox disconnected', { variant: 'success' })\n }\n } catch (error) {\n enqueueSnackbar((error as Error).message, { variant: 'error', allowDuplicate: true })\n setBusy(null)\n }\n }\n\n const sendAsLabel = (email: string) => {\n const option = mailbox.sendAsOptions?.find((entry) => entry.email === email)\n return option?.displayName ? `${option.displayName} <${option.email}>` : email\n }\n\n return (\n <CardDisplay\n header={mailbox.email}\n subheader={isMine ? 'Your mailbox' : 'A teammate’s mailbox'}\n help={pluginDocsHelp('sequences', { anchor: '#connect-a-mailbox' })}\n contentGutterX\n contentGutterY\n HeaderProps={{\n action: (\n <Chip\n size=\"small\"\n label={OUTREACH_MAILBOX_STATUS_LABELS[mailbox.status] ?? mailbox.status}\n color={STATUS_COLOR[mailbox.status] ?? 'default'}\n />\n ),\n }}\n >\n <Stack spacing={2}>\n {needsReconnect ? (\n <Alert\n severity=\"warning\"\n action={\n isMine ? (\n <Button color=\"inherit\" size=\"small\" disabled={reconnecting} onClick={onReconnect}>\n {MAILBOX_ACTION_LABELS.reconnect}\n </Button>\n ) : undefined\n }\n >\n {`${\n mailbox.status === 'disconnected'\n ? 'This mailbox was disconnected.'\n : 'Google stopped accepting this mailbox’s connection.'\n } ${\n isMine\n ? 'Nothing sends from it until you connect it again.'\n : 'Only the member who connected it can reconnect it.'\n }`}\n </Alert>\n ) : null}\n {mailbox.status === 'paused' && mailbox.autoPause ? (\n <Alert severity=\"error\" role=\"alert\">\n {autoPauseSentence(mailbox)}\n </Alert>\n ) : null}\n\n <Box>\n <Typography variant=\"subtitle2\">Last 7 days</Typography>\n {health.hasSent ? null : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n No sends yet\n </Typography>\n )}\n <Stack direction=\"row\" spacing={3} sx={{ mt: 0.5 }}>\n {[\n ['Sent', health.sent],\n ['Bounces', health.bounces],\n ['Replies', health.replies],\n ].map(([label, value]) => (\n <Box key={label as string} aria-label={`${label} in the last 7 days`}>\n <Typography variant=\"h6\" component=\"p\">\n {value}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {label}\n </Typography>\n </Box>\n ))}\n </Stack>\n </Box>\n\n {canManage ? (\n <Stack spacing={2} component=\"form\" onSubmit={(event) => event.preventDefault()}>\n <TextField\n select\n size=\"small\"\n label=\"Send as\"\n value={draft.sendAs}\n onChange={(event) => setDraft({ ...draft, sendAs: event.target.value })}\n helperText=\"Addresses Gmail has verified for this account.\"\n >\n {(mailbox.sendAsOptions?.length ? mailbox.sendAsOptions : [{ email: mailbox.sendAs }]).map((option) => (\n <MenuItem key={option.email} value={option.email}>\n {sendAsLabel(option.email)}\n </MenuItem>\n ))}\n </TextField>\n <TextField\n size=\"small\"\n label=\"Display name\"\n value={draft.displayName}\n onChange={(event) => setDraft({ ...draft, displayName: event.target.value })}\n error={typeof nameCheck !== 'string'}\n helperText={typeof nameCheck !== 'string' ? nameCheck.message : 'The name recipients see beside the address.'}\n />\n <TextField\n size=\"small\"\n type=\"number\"\n label=\"Daily cap\"\n value={draft.dailyCap}\n onChange={(event) => setDraft({ ...draft, dailyCap: event.target.value })}\n error={typeof capCheck !== 'number'}\n slotProps={{ htmlInput: { min: 1, max: OUTREACH_MAX_DAILY_CAP, step: 1 } }}\n helperText={\n typeof capCheck !== 'number'\n ? capCheck.message\n : draft.warmUp\n ? `${RAMP_SENTENCE} Today’s limit: ${todayLimit}.`\n : `No warm-up: today’s limit is the cap, ${todayLimit}.`\n }\n />\n <FormControlLabel\n control={\n <Switch\n checked={draft.warmUp}\n onChange={(event) => setDraft({ ...draft, warmUp: event.target.checked })}\n />\n }\n label={MAILBOX_ACTION_LABELS.warmUp}\n />\n <FormHelperText sx={{ mt: -1 }}>\n {'For a mailbox that is new or has not sent much. One that has sent mail for years already has ' +\n 'its reputation and can start at its cap.'}\n </FormHelperText>\n <Box>\n <Typography variant=\"subtitle2\" gutterBottom>\n Sending window\n </Typography>\n <ToggleButtonGroup\n size=\"small\"\n aria-label=\"Sending days\"\n value={draft.window.days}\n onChange={(_event, days: number[]) =>\n setDraft({ ...draft, window: { ...draft.window, days: [...days].sort((a, b) => a - b) } })\n }\n >\n {OUTREACH_WEEKDAY_LABELS.map((label, day) => (\n <ToggleButton key={label} value={day} aria-label={WEEKDAY_NAMES[day]}>\n {label}\n </ToggleButton>\n ))}\n </ToggleButtonGroup>\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={2} sx={{ mt: 2 }}>\n <TextField\n select\n size=\"small\"\n label=\"From\"\n value={draft.window.startMinute}\n onChange={(event) =>\n setDraft({ ...draft, window: { ...draft.window, startMinute: Number(event.target.value) } })\n }\n >\n {MINUTE_OPTIONS.slice(0, -1).map((minute) => (\n <MenuItem key={minute} value={minute}>\n {formatMinuteOfDay(minute)}\n </MenuItem>\n ))}\n </TextField>\n <TextField\n select\n size=\"small\"\n label=\"Until\"\n value={draft.window.endMinute}\n onChange={(event) =>\n setDraft({ ...draft, window: { ...draft.window, endMinute: Number(event.target.value) } })\n }\n >\n {MINUTE_OPTIONS.slice(1).map((minute) => (\n <MenuItem key={minute} value={minute}>\n {formatMinuteOfDay(minute)}\n </MenuItem>\n ))}\n </TextField>\n <Autocomplete\n size=\"small\"\n disableClearable\n options={timezoneOptions(mailbox.timezone)}\n value={draft.timezone}\n onChange={(_event, timezone) => setDraft({ ...draft, timezone: timezone ?? draft.timezone })}\n sx={{ minWidth: 240 }}\n renderInput={(params) => <TextField {...params} label=\"Timezone\" />}\n />\n </Stack>\n {'days' in windowCheck ? null : <FormHelperText error>{windowCheck.message}</FormHelperText>}\n </Box>\n {saveError ? <FormHelperText error>{saveError}</FormHelperText> : null}\n <Box>\n <Button variant=\"contained\" disabled={!dirty || invalid || saving} onClick={() => void save()}>\n {MAILBOX_ACTION_LABELS.save}\n </Button>\n </Box>\n </Stack>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`Sends as ${sendAsLabel(stored.sendAs)}, at most ${stored.dailyCap} a day, ` +\n `${stored.window.days.map((day) => OUTREACH_WEEKDAY_LABELS[day]).join(', ')} ` +\n `${formatMinuteOfDay(stored.window.startMinute)}–${formatMinuteOfDay(stored.window.endMinute)} ` +\n `(${stored.timezone}).`}\n </Typography>\n )}\n\n {isMine && !needsReconnect ? (\n <TextField\n label={MAILBOX_ACTION_LABELS.testAddress}\n value={testAddress}\n onChange={(event) => setTestAddress(event.target.value)}\n disabled={busy !== null}\n size=\"small\"\n type=\"email\"\n helperText={\n 'Leave empty to send the test to yourself. A test to an outside mailbox you can ' +\n 'read is the only one whose original source shows the sender authentication results.'\n }\n fullWidth\n />\n ) : null}\n\n {canManage || isMine ? (\n <Stack direction=\"row\" spacing={1} useFlexGap sx={{ flexWrap: 'wrap' }}>\n {canManage && !needsReconnect ? (\n <Button variant=\"outlined\" disabled={busy !== null} onClick={() => void togglePaused()}>\n {mailbox.status === 'paused' ? MAILBOX_ACTION_LABELS.resume : MAILBOX_ACTION_LABELS.pause}\n </Button>\n ) : null}\n {isMine && !needsReconnect ? (\n <Button variant=\"outlined\" disabled={busy !== null} onClick={() => void sendTest()}>\n {testAddress.trim() ? MAILBOX_ACTION_LABELS.testElsewhere : MAILBOX_ACTION_LABELS.test}\n </Button>\n ) : null}\n {canManage ? (\n <Button color=\"error\" disabled={busy !== null} onClick={() => void disconnect()}>\n {MAILBOX_ACTION_LABELS.disconnect}\n </Button>\n ) : null}\n </Stack>\n ) : null}\n </Stack>\n </CardDisplay>\n )\n}\nMailboxCard.displayName = 'MailboxCard'\n\nexport default MailboxCard\n"],"names":["pluginDocsHelp","CardDisplay","useConfirmationContext","useSnackbar","Alert","Autocomplete","Box","Button","Chip","FormControlLabel","FormHelperText","MenuItem","Stack","Switch","TextField","ToggleButton","ToggleButtonGroup","Typography","useEffect","useMemo","useRef","useState","formatMinuteOfDay","isValidTimezone","OUTREACH_DEFAULT_WINDOW","OUTREACH_MAILBOX_STATUS_LABELS","OUTREACH_MAX_DAILY_CAP","OUTREACH_RAMP_STEPS","OUTREACH_WEEKDAY_LABELS","outreachEffectiveDailyCap","summarizeMailboxHealth","validateDailyCap","validateDisplayName","validateSendWindow","autoPauseSentence","mailbox","pause","autoPause","day","atMs","Intl","DateTimeFormat","dateStyle","timeZone","timezone","format","until","untilMs","message","MAILBOX_ACTION_LABELS","save","resume","test","testElsewhere","testAddress","warmUp","disconnect","reconnect","WEEKDAY_NAMES","STATUS_COLOR","connected","paused","reconnect_required","disconnected","MINUTE_OPTIONS","Array","from","length","_","index","timezoneOptions","current","supported","supportedValuesOf","zones","includes","RAMP_SENTENCE","map","step","week","limit","join","draftOf","sendAs","email","displayName","dailyCap","String","window","rampStartedAtMs","MailboxCard","props","isMine","canManage","api","onReconnect","reconnecting","nowMs","Date","now","enqueueSnackbar","confirm","draft","setDraft","saving","setSaving","saveError","setSaveError","busy","setBusy","setTestAddress","stored","storedKey","JSON","stringify","seededFrom","dirty","previous","health","needsReconnect","status","capCheck","Number","NaN","windowCheck","nameCheck","timezoneValid","invalid","todayLimit","saveSettings","mailboxId","id","variant","error","togglePaused","setPaused","allowDuplicate","sendTest","to","trim","sent","undefined","sentTo","confirmed","title","description","confirmationText","confirmationButtonProps","color","then","catch","revocation","persist","sendAsLabel","option","sendAsOptions","find","entry","header","subheader","help","anchor","contentGutterX","contentGutterY","HeaderProps","action","size","label","spacing","severity","disabled","onClick","role","hasSent","direction","sx","mt","bounces","replies","value","aria-label","component","onSubmit","event","preventDefault","select","onChange","target","helperText","type","slotProps","htmlInput","min","max","control","checked","gutterBottom","days","_event","sort","a","b","xs","sm","startMinute","slice","minute","endMinute","disableClearable","options","minWidth","renderInput","params","fullWidth","useFlexGap","flexWrap"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;;AAEA,SAASA,cAAc,QAAQ,eAAc;AAC7C,SAASC,WAAW,EAAEC,sBAAsB,QAAQ,uBAAsB;AAC1E,SAASC,WAAW,QAAQ,8BAA6B;AACzD,SACEC,KAAK,EACLC,YAAY,EACZC,GAAG,EACHC,MAAM,EACNC,IAAI,EACJC,gBAAgB,EAChBC,cAAc,EACdC,QAAQ,EACRC,KAAK,EACLC,MAAM,EACNC,SAAS,EACTC,YAAY,EACZC,iBAAiB,EACjBC,UAAU,QACL,gBAAe;AACtB,SAASC,SAAS,EAAEC,OAAO,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,QAAO;AAC5D,SACEC,iBAAiB,EACjBC,eAAe,EACfC,uBAAuB,EACvBC,8BAA8B,EAC9BC,sBAAsB,EACtBC,mBAAmB,EACnBC,uBAAuB,EACvBC,yBAAyB,EACzBC,sBAAsB,EACtBC,gBAAgB,EAChBC,mBAAmB,EACnBC,kBAAkB,QACb,mCAA+B;AAuBtC;;;;;CAKC,GACD,OAAO,SAASC,kBAAkBC,OAAwD;IACxF,MAAMC,QAAQD,QAAQE,SAAS;IAC/B,IAAI,CAACD,OAAO,OAAO;IACnB,MAAME,MAAM,CAACC;QACX,IAAI;YACF,OAAO,IAAIC,KAAKC,cAAc,CAAC,SAAS;gBAAEC,WAAW;gBAAUC,UAAUR,QAAQS,QAAQ;YAAC,GAAGC,MAAM,CAACN;QACtG,EAAE,eAAM;YACN,OAAO,IAAIC,KAAKC,cAAc,CAAC,SAAS;gBAAEC,WAAW;gBAAUC,UAAU;YAAM,GAAGE,MAAM,CAACN;QAC3F;IACF;IACA,MAAMO,QAAQ,OAAOV,MAAMW,OAAO,KAAK,WAAW,CAAC,YAAY,EAAET,IAAIF,MAAMW,OAAO,EAAE,oBAAoB,CAAC,GAAG;IAC5G,OAAO,CAAC,iCAAiC,EAAET,IAAIF,MAAMG,IAAI,EAAE,EAAE,EAAEH,MAAMY,OAAO,GAAGF,MAAM,2CAA2C,CAAC;AACnI;AAEA,wEAAwE,GACxE,OAAO,MAAMG,wBAAwB;IACnCC,MAAM;IACNd,OAAO;IACPe,QAAQ;IACRC,MAAM;IACNC,eAAe;IACfC,aAAa;IACbC,QAAQ;IACRC,YAAY;IACZC,WAAW;AACb,EAAU;AAEV,MAAMC,gBAAgB;IAAC;IAAU;IAAU;IAAW;IAAa;IAAY;IAAU;CAAW;AAEpG,MAAMC,eAA2F;IAC/FC,WAAW;IACXC,QAAQ;IACRC,oBAAoB;IACpBC,cAAc;AAChB;AAEA,yDAAyD,GACzD,MAAMC,iBAAiBC,MAAMC,IAAI,CAAC;IAAEC,QAAQ;AAAG,GAAG,CAACC,GAAGC,QAAUA,QAAQ;AAExE,6EAA6E,GAC7E,SAASC,gBAAgBC,OAAe;;IACtC,MAAMC,oBAAY,AAAChC,KAA2DiC,iBAAiB,oBAA7E,AAACjC,KAA2DiC,iBAAiB,MAA5EjC,MAA+E,8BAAe,EAAE;IACnH,MAAMkC,QAAQF,UAAUL,MAAM,GAAGK,YAAY;QAAC;KAAM;IACpD,OAAOE,MAAMC,QAAQ,CAACJ,WAAWG,QAAQ;QAACH;WAAYG;KAAM;AAC9D;AAEA,iDAAiD,GACjD,MAAME,gBAAgB,CAAC,SAAS,EAAEjD,oBAAoBkD,GAAG,CAAC,CAACC,MAAMT,QAC/D,CAAC,KAAK,EAAES,KAAKC,IAAI,GAAGV,UAAU1C,oBAAoBwC,MAAM,GAAG,IAAI,MAAM,GAAG,EAAE,EAAEW,KAAKE,KAAK,EAAE,EACxFC,IAAI,CAAC,OAAO,4BAA4B,CAAC;AAY3C,MAAMC,UAAU,CAAC/C;QACPA,iBACKA,sBACIA,mBACTA,iBACEA;WAL0C;QACpDgD,MAAM,GAAEhD,kBAAAA,QAAQgD,MAAM,YAAdhD,kBAAkBA,QAAQiD,KAAK;QACvCC,WAAW,GAAElD,uBAAAA,QAAQkD,WAAW,YAAnBlD,uBAAuB;QACpCmD,UAAUC,QAAOpD,oBAAAA,QAAQmD,QAAQ,YAAhBnD,oBAAoB;QACrCqD,MAAM,GAAErD,kBAAAA,QAAQqD,MAAM,YAAdrD,kBAAkBX;QAC1BoB,QAAQ,GAAET,oBAAAA,QAAQS,QAAQ,YAAhBT,oBAAoB;QAC9BoB,QAAQ,OAAOpB,QAAQsD,eAAe,KAAK;IAC7C;;AAEA;;;;;;;;CAQC,GACD,OAAO,SAASC,YAAYC,KAAuB;QAsCZxD,0BA6GpBV,gDACAkC;QAqEHxB;IAxNd,MAAM,EAAEA,OAAO,EAAEyD,MAAM,EAAEC,SAAS,EAAEC,GAAG,EAAEC,WAAW,EAAEC,eAAe,KAAK,EAAEC,QAAQC,KAAKC,GAAG,EAAE,EAAE,GAAGR;IACnG,MAAM,EAAES,eAAe,EAAE,GAAGjG;IAC5B,MAAM,EAAEkG,OAAO,EAAE,GAAGnG;IACpB,MAAM,CAACoG,OAAOC,SAAS,GAAGlF,SAAgB,IAAM6D,QAAQ/C;IACxD,MAAM,CAACqE,QAAQC,UAAU,GAAGpF,SAAS;IACrC,MAAM,CAACqF,WAAWC,aAAa,GAAGtF,SAAwB;IAC1D,MAAM,CAACuF,MAAMC,QAAQ,GAAGxF,SAAkD;IAC1E,0EAA0E;IAC1E,4EAA4E;IAC5E,MAAM,CAACiC,aAAawD,eAAe,GAAGzF,SAAS;IAE/C,2EAA2E;IAC3E,wEAAwE;IACxE,mDAAmD;IACnD,MAAM0F,SAAS5F,QAAQ,IAAM+D,QAAQ/C,UAAU;QAACA;KAAQ;IACxD,MAAM6E,YAAYC,KAAKC,SAAS,CAACH;IACjC,MAAMI,aAAa/F,OAAO4F;IAC1B,MAAMI,QAAQH,KAAKC,SAAS,CAACZ,WAAWU;IACxC9F,UAAU;QACR,IAAIiG,WAAW5C,OAAO,KAAKyC,WAAW;QACtC,MAAMK,WAAWF,WAAW5C,OAAO;QACnC4C,WAAW5C,OAAO,GAAGyC;QACrBT,SAAS,CAAChC,UAAa0C,KAAKC,SAAS,CAAC3C,aAAa8C,WAAWN,SAASxC;IACzE,GAAG;QAACyC;QAAWD;KAAO;IAEtB,MAAMO,SAASxF,uBAAuBK,QAAQmF,MAAM,EAAEnF,QAAQS,QAAQ,EAAEqD;IACxE,MAAMsB,iBAAiBpF,QAAQqF,MAAM,KAAK,wBAAwBrF,QAAQqF,MAAM,KAAK;IACrF,MAAMC,WAAW1F,iBAAiBuE,MAAMhB,QAAQ,KAAK,KAAKoC,OAAOC,GAAG,GAAGD,OAAOpB,MAAMhB,QAAQ;IAC5F,MAAMsC,cAAc3F,mBAAmBqE,MAAMd,MAAM;IACnD,MAAMqC,YAAY7F,oBAAoBsE,MAAMjB,WAAW;IACvD,MAAMyC,gBAAgBvG,gBAAgB+E,MAAM1D,QAAQ;IACpD,MAAMmF,UACJ,OAAON,aAAa,YAAY,CAAE,CAAA,UAAUG,WAAU,KAAM,OAAOC,cAAc,YAAY,CAACC;IAChG,MAAME,aAAanG,0BACjB;QACEyD,UAAU,OAAOmC,aAAa,WAAWA,WAAWtF,QAAQmD,QAAQ;QACpE,2DAA2D;QAC3DG,iBAAiBa,MAAM/C,MAAM,IAAIpB,2BAAAA,QAAQsD,eAAe,YAAvBtD,2BAA2B8D,QAAS;QACrErD,UAAUkF,gBAAgBxB,MAAM1D,QAAQ,GAAGT,QAAQS,QAAQ;IAC7D,GACAqD;IAGF,MAAM/C,OAAO;QACXuD,UAAU;QACVE,aAAa;QACb,IAAI;YACF,MAAMb,IAAImC,YAAY,CAAC;gBACrBC,WAAW/F,QAAQgG,EAAE;eACjB7B,MAAMnB,MAAM,KAAK4B,OAAO5B,MAAM,GAAG;gBAAEA,QAAQmB,MAAMnB,MAAM;YAAC,IAAI,CAAC,GAC7DmB,MAAMjB,WAAW,KAAK0B,OAAO1B,WAAW,GAAG;gBAAEA,aAAaiB,MAAMjB,WAAW;YAAC,IAAI,CAAC,GACjFiB,MAAMhB,QAAQ,KAAKyB,OAAOzB,QAAQ,GAAG;gBAAEA,UAAUoC,OAAOpB,MAAMhB,QAAQ;YAAE,IAAI,CAAC,GAC7E2B,KAAKC,SAAS,CAACZ,MAAMd,MAAM,MAAMyB,KAAKC,SAAS,CAACH,OAAOvB,MAAM,IAAI;gBAAEA,QAAQc,MAAMd,MAAM;YAAC,IAAI,CAAC,GAC7Fc,MAAM1D,QAAQ,KAAKmE,OAAOnE,QAAQ,GAAG;gBAAEA,UAAU0D,MAAM1D,QAAQ;YAAC,IAAI,CAAC,GACrE0D,MAAM/C,MAAM,KAAKwD,OAAOxD,MAAM,GAAG;gBAAEA,QAAQ+C,MAAM/C,MAAM;YAAC,IAAI,CAAC;YAEnE6C,gBAAgB,0BAA0B;gBAAEgC,SAAS;YAAU;QACjE,EAAE,OAAOC,OAAO;YACd1B,aAAa,AAAC0B,MAAgBrF,OAAO;QACvC,SAAU;YACRyD,UAAU;QACZ;IACF;IAEA,MAAM6B,eAAe;QACnB,MAAMzE,SAAS1B,QAAQqF,MAAM,KAAK;QAClCX,QAAQ;QACR,IAAI;YACF,MAAMf,IAAIyC,SAAS,CAACpG,QAAQgG,EAAE,EAAEtE;YAChCuC,gBAAgBvC,SAAS,mBAAmB,mBAAmB;gBAAEuE,SAAS;YAAU;QACtF,EAAE,OAAOC,OAAO;YACdjC,gBAAgB,AAACiC,MAAgBrF,OAAO,EAAE;gBAAEoF,SAAS;gBAASI,gBAAgB;YAAK;QACrF,SAAU;YACR3B,QAAQ;QACV;IACF;IAEA,MAAM4B,WAAW;QACf5B,QAAQ;QACR,IAAI;YACF,MAAM6B,KAAKpF,YAAYqF,IAAI;YAC3B,MAAMC,OAAO,MAAM9C,IAAI2C,QAAQ,CAACtG,QAAQgG,EAAE,EAAEO,MAAMG;YAClDzC,gBACEsC,KACI,CAAC,aAAa,EAAEE,KAAKE,MAAM,CAAC,6DAA6D,CAAC,GAC1F,CAAC,aAAa,EAAEF,KAAKE,MAAM,CAAC,mBAAmB,CAAC,EACpD;gBAAEV,SAAS;YAAU;QAEzB,EAAE,OAAOC,OAAO;YACdjC,gBAAgB,AAACiC,MAAgBrF,OAAO,EAAE;gBAAEoF,SAAS;gBAASI,gBAAgB;YAAK;QACrF,SAAU;YACR3B,QAAQ;QACV;IACF;IAEA,MAAMrD,aAAa;QACjB,MAAMuF,YAAY,MAAM1C,QAAQ;YAC9B2C,OAAO;YACPC,aACE,4FACA;YACFC,kBAAkBjG,sBAAsBO,UAAU;YAClD2F,yBAAyB;gBAAEC,OAAO;YAAQ;QAC5C,GACGC,IAAI,CAAC,IAAM,MACXC,KAAK,CAAC,IAAM;QACf,IAAI,CAACP,WAAW;QAChBlC,QAAQ;QACR,IAAI;YACF,MAAM,EAAE0C,UAAU,EAAE,GAAG,MAAMzD,IAAItC,UAAU,CAACrB,QAAQgG,EAAE;YACtD,IAAIoB,eAAe,UAAU;gBAC3BnD,gBACE,gFACE,2EACF;oBAAEgC,SAAS;oBAAWoB,SAAS;gBAAK;YAExC,OAAO,IAAID,eAAe,0BAA0B;gBAClDnD,gBACE,yGACA;oBAAEgC,SAAS;gBAAU;YAEzB,OAAO;gBACLhC,gBAAgB,wBAAwB;oBAAEgC,SAAS;gBAAU;YAC/D;QACF,EAAE,OAAOC,OAAO;YACdjC,gBAAgB,AAACiC,MAAgBrF,OAAO,EAAE;gBAAEoF,SAAS;gBAASI,gBAAgB;YAAK;YACnF3B,QAAQ;QACV;IACF;IAEA,MAAM4C,cAAc,CAACrE;YACJjD;QAAf,MAAMuH,UAASvH,yBAAAA,QAAQwH,aAAa,qBAArBxH,uBAAuByH,IAAI,CAAC,CAACC,QAAUA,MAAMzE,KAAK,KAAKA;QACtE,OAAOsE,CAAAA,0BAAAA,OAAQrE,WAAW,IAAG,GAAGqE,OAAOrE,WAAW,CAAC,EAAE,EAAEqE,OAAOtE,KAAK,CAAC,CAAC,CAAC,GAAGA;IAC3E;IAEA,qBACE,KAACnF;QACC6J,QAAQ3H,QAAQiD,KAAK;QACrB2E,WAAWnE,SAAS,iBAAiB;QACrCoE,MAAMhK,eAAe,aAAa;YAAEiK,QAAQ;QAAqB;QACjEC,cAAc;QACdC,cAAc;QACdC,aAAa;YACXC,sBACE,KAAC7J;gBACC8J,MAAK;gBACLC,KAAK,GAAE9I,iDAAAA,8BAA8B,CAACU,QAAQqF,MAAM,CAAC,YAA9C/F,iDAAkDU,QAAQqF,MAAM;gBACvE4B,KAAK,GAAEzF,+BAAAA,YAAY,CAACxB,QAAQqF,MAAM,CAAC,YAA5B7D,+BAAgC;;QAG7C;kBAEA,cAAA,MAAC/C;YAAM4J,SAAS;;gBACbjD,+BACC,KAACnH;oBACCqK,UAAS;oBACTJ,QACEzE,uBACE,KAACrF;wBAAO6I,OAAM;wBAAUkB,MAAK;wBAAQI,UAAU1E;wBAAc2E,SAAS5E;kCACnE9C,sBAAsBQ,SAAS;yBAEhCoF;8BAGL,GACC1G,QAAQqF,MAAM,KAAK,iBACf,mCACA,sDACL,CAAC,EACA5B,SACI,sDACA,sDACJ;qBAEF;gBACHzD,QAAQqF,MAAM,KAAK,YAAYrF,QAAQE,SAAS,iBAC/C,KAACjC;oBAAMqK,UAAS;oBAAQG,MAAK;8BAC1B1I,kBAAkBC;qBAEnB;8BAEJ,MAAC7B;;sCACC,KAACW;4BAAWmH,SAAQ;sCAAY;;wBAC/Bd,OAAOuD,OAAO,GAAG,qBAChB,KAAC5J;4BAAWmH,SAAQ;4BAAQgB,OAAM;sCAAiB;;sCAIrD,KAACxI;4BAAMkK,WAAU;4BAAMN,SAAS;4BAAGO,IAAI;gCAAEC,IAAI;4BAAI;sCAC9C;gCACC;oCAAC;oCAAQ1D,OAAOsB,IAAI;iCAAC;gCACrB;oCAAC;oCAAWtB,OAAO2D,OAAO;iCAAC;gCAC3B;oCAAC;oCAAW3D,OAAO4D,OAAO;iCAAC;6BAC5B,CAACrG,GAAG,CAAC,CAAC,CAAC0F,OAAOY,MAAM,iBACnB,MAAC7K;oCAA0B8K,cAAY,GAAGb,MAAM,mBAAmB,CAAC;;sDAClE,KAACtJ;4CAAWmH,SAAQ;4CAAKiD,WAAU;sDAChCF;;sDAEH,KAAClK;4CAAWmH,SAAQ;4CAAUgB,OAAM;sDACjCmB;;;mCALKA;;;;gBAYf1E,0BACC,MAACjF;oBAAM4J,SAAS;oBAAGa,WAAU;oBAAOC,UAAU,CAACC,QAAUA,MAAMC,cAAc;;sCAC3E,KAAC1K;4BACC2K,MAAM;4BACNnB,MAAK;4BACLC,OAAM;4BACNY,OAAO7E,MAAMnB,MAAM;4BACnBuG,UAAU,CAACH,QAAUhF,SAAS,aAAKD;oCAAOnB,QAAQoG,MAAMI,MAAM,CAACR,KAAK;;4BACpES,YAAW;sCAEV,AAACzJ,CAAAA,EAAAA,yBAAAA,QAAQwH,aAAa,qBAArBxH,uBAAuBgC,MAAM,IAAGhC,QAAQwH,aAAa,GAAG;gCAAC;oCAAEvE,OAAOjD,QAAQgD,MAAM;gCAAC;6BAAE,AAAD,EAAGN,GAAG,CAAC,CAAC6E,uBAC1F,KAAC/I;oCAA4BwK,OAAOzB,OAAOtE,KAAK;8CAC7CqE,YAAYC,OAAOtE,KAAK;mCADZsE,OAAOtE,KAAK;;sCAK/B,KAACtE;4BACCwJ,MAAK;4BACLC,OAAM;4BACNY,OAAO7E,MAAMjB,WAAW;4BACxBqG,UAAU,CAACH,QAAUhF,SAAS,aAAKD;oCAAOjB,aAAakG,MAAMI,MAAM,CAACR,KAAK;;4BACzE9C,OAAO,OAAOR,cAAc;4BAC5B+D,YAAY,OAAO/D,cAAc,WAAWA,UAAU7E,OAAO,GAAG;;sCAElE,KAAClC;4BACCwJ,MAAK;4BACLuB,MAAK;4BACLtB,OAAM;4BACNY,OAAO7E,MAAMhB,QAAQ;4BACrBoG,UAAU,CAACH,QAAUhF,SAAS,aAAKD;oCAAOhB,UAAUiG,MAAMI,MAAM,CAACR,KAAK;;4BACtE9C,OAAO,OAAOZ,aAAa;4BAC3BqE,WAAW;gCAAEC,WAAW;oCAAEC,KAAK;oCAAGC,KAAKvK;oCAAwBoD,MAAM;gCAAE;4BAAE;4BACzE8G,YACE,OAAOnE,aAAa,WAChBA,SAASzE,OAAO,GAChBsD,MAAM/C,MAAM,GACV,GAAGqB,cAAc,gBAAgB,EAAEoD,WAAW,CAAC,CAAC,GAChD,CAAC,sCAAsC,EAAEA,WAAW,CAAC,CAAC;;sCAGhE,KAACvH;4BACCyL,uBACE,KAACrL;gCACCsL,SAAS7F,MAAM/C,MAAM;gCACrBmI,UAAU,CAACH,QAAUhF,SAAS,aAAKD;wCAAO/C,QAAQgI,MAAMI,MAAM,CAACQ,OAAO;;;4BAG1E5B,OAAOtH,sBAAsBM,MAAM;;sCAErC,KAAC7C;4BAAeqK,IAAI;gCAAEC,IAAI,CAAC;4BAAE;sCAC1B,kGACC;;sCAEJ,MAAC1K;;8CACC,KAACW;oCAAWmH,SAAQ;oCAAYgE,YAAY;8CAAC;;8CAG7C,KAACpL;oCACCsJ,MAAK;oCACLc,cAAW;oCACXD,OAAO7E,MAAMd,MAAM,CAAC6G,IAAI;oCACxBX,UAAU,CAACY,QAAQD,OACjB9F,SAAS,aAAKD;4CAAOd,QAAQ,aAAKc,MAAMd,MAAM;gDAAE6G,MAAM;uDAAIA;iDAAK,CAACE,IAAI,CAAC,CAACC,GAAGC,IAAMD,IAAIC;;;8CAGpF7K,wBAAwBiD,GAAG,CAAC,CAAC0F,OAAOjI,oBACnC,KAACvB;4CAAyBoK,OAAO7I;4CAAK8I,cAAY1H,aAAa,CAACpB,IAAI;sDACjEiI;2CADgBA;;8CAKvB,MAAC3J;oCAAMkK,WAAW;wCAAE4B,IAAI;wCAAUC,IAAI;oCAAM;oCAAGnC,SAAS;oCAAGO,IAAI;wCAAEC,IAAI;oCAAE;;sDACrE,KAAClK;4CACC2K,MAAM;4CACNnB,MAAK;4CACLC,OAAM;4CACNY,OAAO7E,MAAMd,MAAM,CAACoH,WAAW;4CAC/BlB,UAAU,CAACH,QACThF,SAAS,aAAKD;oDAAOd,QAAQ,aAAKc,MAAMd,MAAM;wDAAEoH,aAAalF,OAAO6D,MAAMI,MAAM,CAACR,KAAK;;;sDAGvFnH,eAAe6I,KAAK,CAAC,GAAG,CAAC,GAAGhI,GAAG,CAAC,CAACiI,uBAChC,KAACnM;oDAAsBwK,OAAO2B;8DAC3BxL,kBAAkBwL;mDADNA;;sDAKnB,KAAChM;4CACC2K,MAAM;4CACNnB,MAAK;4CACLC,OAAM;4CACNY,OAAO7E,MAAMd,MAAM,CAACuH,SAAS;4CAC7BrB,UAAU,CAACH,QACThF,SAAS,aAAKD;oDAAOd,QAAQ,aAAKc,MAAMd,MAAM;wDAAEuH,WAAWrF,OAAO6D,MAAMI,MAAM,CAACR,KAAK;;;sDAGrFnH,eAAe6I,KAAK,CAAC,GAAGhI,GAAG,CAAC,CAACiI,uBAC5B,KAACnM;oDAAsBwK,OAAO2B;8DAC3BxL,kBAAkBwL;mDADNA;;sDAKnB,KAACzM;4CACCiK,MAAK;4CACL0C,gBAAgB;4CAChBC,SAAS3I,gBAAgBnC,QAAQS,QAAQ;4CACzCuI,OAAO7E,MAAM1D,QAAQ;4CACrB8I,UAAU,CAACY,QAAQ1J,WAAa2D,SAAS,aAAKD;oDAAO1D,QAAQ,EAAEA,mBAAAA,WAAY0D,MAAM1D,QAAQ;;4CACzFmI,IAAI;gDAAEmC,UAAU;4CAAI;4CACpBC,aAAa,CAACC,uBAAW,KAACtM,wBAAcsM;oDAAQ7C,OAAM;;;;;gCAGzD,UAAU3C,cAAc,qBAAO,KAAClH;oCAAe2H,KAAK;8CAAET,YAAY5E,OAAO;;;;wBAE3E0D,0BAAY,KAAChG;4BAAe2H,KAAK;sCAAE3B;6BAA8B;sCAClE,KAACpG;sCACC,cAAA,KAACC;gCAAO6H,SAAQ;gCAAYsC,UAAU,CAACtD,SAASW,WAAWvB;gCAAQmE,SAAS,IAAM,KAAKzH;0CACpFD,sBAAsBC,IAAI;;;;mCAKjC,KAACjC;oBAAWmH,SAAQ;oBAAQgB,OAAM;8BAC/B,CAAC,SAAS,EAAEK,YAAY1C,OAAO5B,MAAM,EAAE,UAAU,EAAE4B,OAAOzB,QAAQ,CAAC,QAAQ,CAAC,GAC3E,GAAGyB,OAAOvB,MAAM,CAAC6G,IAAI,CAACxH,GAAG,CAAC,CAACvC,MAAQV,uBAAuB,CAACU,IAAI,EAAE2C,IAAI,CAAC,MAAM,CAAC,CAAC,GAC9E,GAAG3D,kBAAkByF,OAAOvB,MAAM,CAACoH,WAAW,EAAE,CAAC,EAAEtL,kBAAkByF,OAAOvB,MAAM,CAACuH,SAAS,EAAE,CAAC,CAAC,GAChG,CAAC,CAAC,EAAEhG,OAAOnE,QAAQ,CAAC,EAAE,CAAC;;gBAI5BgD,UAAU,CAAC2B,+BACV,KAACzG;oBACCyJ,OAAOtH,sBAAsBK,WAAW;oBACxC6H,OAAO7H;oBACPoI,UAAU,CAACH,QAAUzE,eAAeyE,MAAMI,MAAM,CAACR,KAAK;oBACtDT,UAAU9D,SAAS;oBACnB0D,MAAK;oBACLuB,MAAK;oBACLD,YACE,oFACA;oBAEFyB,SAAS;qBAET;gBAEHxH,aAAaD,uBACZ,MAAChF;oBAAMkK,WAAU;oBAAMN,SAAS;oBAAG8C,UAAU;oBAACvC,IAAI;wBAAEwC,UAAU;oBAAO;;wBAClE1H,aAAa,CAAC0B,+BACb,KAAChH;4BAAO6H,SAAQ;4BAAWsC,UAAU9D,SAAS;4BAAM+D,SAAS,IAAM,KAAKrC;sCACrEnG,QAAQqF,MAAM,KAAK,WAAWvE,sBAAsBE,MAAM,GAAGF,sBAAsBb,KAAK;6BAEzF;wBACHwD,UAAU,CAAC2B,+BACV,KAAChH;4BAAO6H,SAAQ;4BAAWsC,UAAU9D,SAAS;4BAAM+D,SAAS,IAAM,KAAKlC;sCACrEnF,YAAYqF,IAAI,KAAK1F,sBAAsBI,aAAa,GAAGJ,sBAAsBG,IAAI;6BAEtF;wBACHyC,0BACC,KAACtF;4BAAO6I,OAAM;4BAAQsB,UAAU9D,SAAS;4BAAM+D,SAAS,IAAM,KAAKnH;sCAChEP,sBAAsBO,UAAU;6BAEjC;;qBAEJ;;;;AAIZ;AACAkC,YAAYL,WAAW,GAAG;AAE1B,eAAeK,YAAW"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/components/mailbox-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 { pluginDocsHelp } from '@aglyn/aglyn'\nimport { CardDisplay, useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n Autocomplete,\n Box,\n Button,\n Chip,\n FormControlLabel,\n FormHelperText,\n MenuItem,\n Stack,\n Switch,\n TextField,\n ToggleButton,\n ToggleButtonGroup,\n Typography,\n} from '@mui/material'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport {\n formatMinuteOfDay,\n isValidTimezone,\n OUTREACH_DEFAULT_WINDOW,\n OUTREACH_MAILBOX_STATUS_LABELS,\n OUTREACH_MAX_DAILY_CAP,\n OUTREACH_RAMP_STEPS,\n OUTREACH_WEEKDAY_LABELS,\n outreachEffectiveDailyCap,\n outreachLocalDay,\n summarizeMailboxHealth,\n validateDailyCap,\n validateDisplayName,\n validateSendWindow,\n} from '../mailboxes/mailbox-settings'\nimport type {\n OutreachMailbox,\n OutreachMailboxStatus,\n OutreachSendWindow,\n} from '../model/outreach.types'\nimport type { OutreachMailboxApi } from './use-outreach-mailbox-api'\n\nexport interface MailboxCardProps {\n mailbox: OutreachMailbox\n /** The viewer is the member who connected it. */\n isMine: boolean\n /** The viewer may change, pause and disconnect it: its member, or an org owner or admin. */\n canManage: boolean\n api: OutreachMailboxApi\n /** Starts a new Google connect, for a mailbox that needs reconnecting. */\n onReconnect(): void\n /** True while that connect is on its way to Google. */\n reconnecting?: boolean\n /** The clock the health window and today's limit are read on. */\n nowMs?: number\n}\n\n/**\n * What the card says about a mailbox that paused ITSELF (AGL-2981): the\n * engine's own sentence, when it paused, and — after a complaint — the day\n * resuming stops being premature. Nothing sends from it until a member\n * resumes it.\n */\nexport function autoPauseSentence(mailbox: Pick<OutreachMailbox, 'autoPause' | 'timezone'>): string {\n const pause = mailbox.autoPause\n if (!pause) return ''\n const day = (atMs: number) => {\n try {\n return new Intl.DateTimeFormat('en-US', { dateStyle: 'medium', timeZone: mailbox.timezone }).format(atMs)\n } catch {\n return new Intl.DateTimeFormat('en-US', { dateStyle: 'medium', timeZone: 'UTC' }).format(atMs)\n }\n }\n const until = typeof pause.untilMs === 'number' ? ` Wait until ${day(pause.untilMs)} before resuming it.` : ''\n return `Sequences paused this mailbox on ${day(pause.atMs)}. ${pause.message}${until} Nothing sends from it until it is resumed.`\n}\n\n/** Accessible names of the card's actions, spelled once for the specs. */\nexport const MAILBOX_ACTION_LABELS = {\n save: 'Save settings',\n pause: 'Pause',\n resume: 'Resume',\n test: 'Send a test to myself',\n testElsewhere: 'Send a test',\n testAddress: 'Test address',\n warmUp: 'Warm up gradually',\n disconnect: 'Disconnect',\n reconnect: 'Reconnect',\n} as const\n\nconst WEEKDAY_NAMES = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']\n\nconst STATUS_COLOR: Record<OutreachMailboxStatus, 'success' | 'default' | 'warning' | 'error'> = {\n connected: 'success',\n paused: 'default',\n reconnect_required: 'warning',\n disconnected: 'error',\n}\n\n/** Every half hour of the day, and midnight at its end. */\nconst MINUTE_OPTIONS = Array.from({ length: 49 }, (_, index) => index * 30)\n\n/** The zones this runtime knows, the mailbox's own included whatever it is. */\nfunction timezoneOptions(current: string): string[] {\n const supported = (Intl as { supportedValuesOf?: (key: string) => string[] }).supportedValuesOf?.('timeZone') ?? []\n const zones = supported.length ? supported : ['UTC']\n return zones.includes(current) ? zones : [current, ...zones]\n}\n\n/** The ramp, as the cap's helper text reads it. */\nconst RAMP_SENTENCE = `Warm-up: ${OUTREACH_RAMP_STEPS.map((step, index) =>\n `week ${step.week}${index === OUTREACH_RAMP_STEPS.length - 1 ? '+' : ''}: ${step.limit}`,\n).join(' · ')} a day, never above the cap.`\n\ninterface Draft {\n sendAs: string\n displayName: string\n dailyCap: string\n window: OutreachSendWindow\n timezone: string\n /** Whether the warm-up ramp applies — `rampStartedAtMs` as a switch. */\n warmUp: boolean\n}\n\nconst draftOf = (mailbox: OutreachMailbox): Draft => ({\n sendAs: mailbox.sendAs ?? mailbox.email,\n displayName: mailbox.displayName ?? '',\n dailyCap: String(mailbox.dailyCap ?? ''),\n window: mailbox.window ?? OUTREACH_DEFAULT_WINDOW,\n timezone: mailbox.timezone ?? 'UTC',\n warmUp: typeof mailbox.rampStartedAtMs === 'number',\n})\n\n/**\n * One connected mailbox (AGL-2978): its account and status, its last seven\n * days, its settings, and pause, test and disconnect.\n *\n * Every control is offered only where the route will allow it: the settings\n * form and pause and disconnect to the member who connected the mailbox or an\n * org owner or admin, and the test and the reconnect to its member alone,\n * because both act in that member's own Google account.\n */\nexport function MailboxCard(props: MailboxCardProps) {\n const { mailbox, isMine, canManage, api, onReconnect, reconnecting = false, nowMs = Date.now() } = props\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const [draft, setDraft] = useState<Draft>(() => draftOf(mailbox))\n const [saving, setSaving] = useState(false)\n const [saveError, setSaveError] = useState<string | null>(null)\n const [busy, setBusy] = useState<'status' | 'test' | 'disconnect' | null>(null)\n // Where a test goes when not to the member themself (AGL-3228): a test to\n // one's own address never leaves Google and shows no authentication result.\n const [testAddress, setTestAddress] = useState('')\n\n // A save here, a change made from another tab, or a reconnect rewrites the\n // stored settings; the form follows them, unless the member has unsaved\n // edits of their own, which are never thrown away.\n const stored = useMemo(() => draftOf(mailbox), [mailbox])\n const storedKey = JSON.stringify(stored)\n const seededFrom = useRef(storedKey)\n const dirty = JSON.stringify(draft) !== storedKey\n useEffect(() => {\n if (seededFrom.current === storedKey) return\n const previous = seededFrom.current\n seededFrom.current = storedKey\n setDraft((current) => (JSON.stringify(current) === previous ? stored : current))\n }, [storedKey, stored])\n\n const health = summarizeMailboxHealth(mailbox.health, mailbox.timezone, nowMs)\n // Tests of a sequence step sent today (AGL-3325): counted beside the\n // sends, never among them, so the cap and the seven-day sum stay honest.\n const testsToday = Math.max(\n 0,\n Number(mailbox.health?.daily?.[outreachLocalDay(nowMs, mailbox.timezone)]?.tests) || 0,\n )\n const needsReconnect = mailbox.status === 'reconnect_required' || mailbox.status === 'disconnected'\n const capCheck = validateDailyCap(draft.dailyCap === '' ? Number.NaN : Number(draft.dailyCap))\n const windowCheck = validateSendWindow(draft.window)\n const nameCheck = validateDisplayName(draft.displayName)\n const timezoneValid = isValidTimezone(draft.timezone)\n const invalid =\n typeof capCheck !== 'number' || !('days' in windowCheck) || typeof nameCheck !== 'string' || !timezoneValid\n const todayLimit = outreachEffectiveDailyCap(\n {\n dailyCap: typeof capCheck === 'number' ? capCheck : mailbox.dailyCap,\n // A ramp switched on in the draft reads as starting today.\n rampStartedAtMs: draft.warmUp ? (mailbox.rampStartedAtMs ?? nowMs) : null,\n timezone: timezoneValid ? draft.timezone : mailbox.timezone,\n },\n nowMs,\n )\n\n const save = async () => {\n setSaving(true)\n setSaveError(null)\n try {\n await api.saveSettings({\n mailboxId: mailbox.id,\n ...(draft.sendAs !== stored.sendAs ? { sendAs: draft.sendAs } : {}),\n ...(draft.displayName !== stored.displayName ? { displayName: draft.displayName } : {}),\n ...(draft.dailyCap !== stored.dailyCap ? { dailyCap: Number(draft.dailyCap) } : {}),\n ...(JSON.stringify(draft.window) !== JSON.stringify(stored.window) ? { window: draft.window } : {}),\n ...(draft.timezone !== stored.timezone ? { timezone: draft.timezone } : {}),\n ...(draft.warmUp !== stored.warmUp ? { warmUp: draft.warmUp } : {}),\n })\n enqueueSnackbar('Mailbox settings saved', { variant: 'success' })\n } catch (error) {\n setSaveError((error as Error).message)\n } finally {\n setSaving(false)\n }\n }\n\n const togglePaused = async () => {\n const paused = mailbox.status !== 'paused'\n setBusy('status')\n try {\n await api.setPaused(mailbox.id, paused)\n enqueueSnackbar(paused ? 'Mailbox paused' : 'Mailbox resumed', { variant: 'success' })\n } catch (error) {\n enqueueSnackbar((error as Error).message, { variant: 'error', allowDuplicate: true })\n } finally {\n setBusy(null)\n }\n }\n\n const sendTest = async () => {\n setBusy('test')\n try {\n const to = testAddress.trim()\n const sent = await api.sendTest(mailbox.id, to || undefined)\n enqueueSnackbar(\n to\n ? `Test sent to ${sent.sentTo}. Its original source shows whether the sender authenticated.`\n : `Test sent to ${sent.sentTo}. Check your inbox.`,\n { variant: 'success' },\n )\n } catch (error) {\n enqueueSnackbar((error as Error).message, { variant: 'error', allowDuplicate: true })\n } finally {\n setBusy(null)\n }\n }\n\n const disconnect = async () => {\n const confirmed = await confirm({\n title: 'Disconnect this mailbox?',\n description:\n 'Disconnecting deletes the access stored for this mailbox and asks Google to revoke it. ' +\n 'Nothing is sent from it again unless it is connected again.',\n confirmationText: MAILBOX_ACTION_LABELS.disconnect,\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n setBusy('disconnect')\n try {\n const { revocation } = await api.disconnect(mailbox.id)\n if (revocation === 'failed') {\n enqueueSnackbar(\n 'Mailbox disconnected, but Google could not confirm the access was revoked. ' +\n 'Remove it from the Google Account’s third-party connections to be sure.',\n { variant: 'warning', persist: true },\n )\n } else if (revocation === 'kept-for-other-mailbox') {\n enqueueSnackbar(\n 'Mailbox disconnected. Google access stays in place for another connected mailbox on the same account.',\n { variant: 'success' },\n )\n } else {\n enqueueSnackbar('Mailbox disconnected', { variant: 'success' })\n }\n } catch (error) {\n enqueueSnackbar((error as Error).message, { variant: 'error', allowDuplicate: true })\n setBusy(null)\n }\n }\n\n const sendAsLabel = (email: string) => {\n const option = mailbox.sendAsOptions?.find((entry) => entry.email === email)\n return option?.displayName ? `${option.displayName} <${option.email}>` : email\n }\n\n return (\n <CardDisplay\n header={mailbox.email}\n subheader={isMine ? 'Your mailbox' : 'A teammate’s mailbox'}\n help={pluginDocsHelp('sequences', { anchor: '#connect-a-mailbox' })}\n contentGutterX\n contentGutterY\n HeaderProps={{\n action: (\n <Chip\n size=\"small\"\n label={OUTREACH_MAILBOX_STATUS_LABELS[mailbox.status] ?? mailbox.status}\n color={STATUS_COLOR[mailbox.status] ?? 'default'}\n />\n ),\n }}\n >\n <Stack spacing={2}>\n {needsReconnect ? (\n <Alert\n severity=\"warning\"\n action={\n isMine ? (\n <Button color=\"inherit\" size=\"small\" disabled={reconnecting} onClick={onReconnect}>\n {MAILBOX_ACTION_LABELS.reconnect}\n </Button>\n ) : undefined\n }\n >\n {`${\n mailbox.status === 'disconnected'\n ? 'This mailbox was disconnected.'\n : 'Google stopped accepting this mailbox’s connection.'\n } ${\n isMine\n ? 'Nothing sends from it until you connect it again.'\n : 'Only the member who connected it can reconnect it.'\n }`}\n </Alert>\n ) : null}\n {mailbox.status === 'paused' && mailbox.autoPause ? (\n <Alert severity=\"error\" role=\"alert\">\n {autoPauseSentence(mailbox)}\n </Alert>\n ) : null}\n\n <Box>\n <Typography variant=\"subtitle2\">Last 7 days</Typography>\n {health.hasSent ? null : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n No sends yet\n </Typography>\n )}\n <Stack direction=\"row\" spacing={3} sx={{ mt: 0.5 }}>\n {[\n ['Sent', health.sent],\n ['Bounces', health.bounces],\n ['Replies', health.replies],\n ].map(([label, value]) => (\n <Box key={label as string} aria-label={`${label} in the last 7 days`}>\n <Typography variant=\"h6\" component=\"p\">\n {value}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {label}\n </Typography>\n </Box>\n ))}\n </Stack>\n <Typography variant=\"caption\" color=\"text.secondary\" aria-label=\"Tests today\">\n {`Tests today: ${testsToday}`}\n </Typography>\n </Box>\n\n {canManage ? (\n <Stack spacing={2} component=\"form\" onSubmit={(event) => event.preventDefault()}>\n <TextField\n select\n size=\"small\"\n label=\"Send as\"\n value={draft.sendAs}\n onChange={(event) => setDraft({ ...draft, sendAs: event.target.value })}\n helperText=\"Addresses Gmail has verified for this account.\"\n >\n {(mailbox.sendAsOptions?.length ? mailbox.sendAsOptions : [{ email: mailbox.sendAs }]).map((option) => (\n <MenuItem key={option.email} value={option.email}>\n {sendAsLabel(option.email)}\n </MenuItem>\n ))}\n </TextField>\n <TextField\n size=\"small\"\n label=\"Display name\"\n value={draft.displayName}\n onChange={(event) => setDraft({ ...draft, displayName: event.target.value })}\n error={typeof nameCheck !== 'string'}\n helperText={typeof nameCheck !== 'string' ? nameCheck.message : 'The name recipients see beside the address.'}\n />\n <TextField\n size=\"small\"\n type=\"number\"\n label=\"Daily cap\"\n value={draft.dailyCap}\n onChange={(event) => setDraft({ ...draft, dailyCap: event.target.value })}\n error={typeof capCheck !== 'number'}\n slotProps={{ htmlInput: { min: 1, max: OUTREACH_MAX_DAILY_CAP, step: 1 } }}\n helperText={\n typeof capCheck !== 'number'\n ? capCheck.message\n : draft.warmUp\n ? `${RAMP_SENTENCE} Today’s limit: ${todayLimit}.`\n : `No warm-up: today’s limit is the cap, ${todayLimit}.`\n }\n />\n <FormControlLabel\n control={\n <Switch\n checked={draft.warmUp}\n onChange={(event) => setDraft({ ...draft, warmUp: event.target.checked })}\n />\n }\n label={MAILBOX_ACTION_LABELS.warmUp}\n />\n <FormHelperText sx={{ mt: -1 }}>\n {'For a mailbox that is new or has not sent much. One that has sent mail for years already has ' +\n 'its reputation and can start at its cap.'}\n </FormHelperText>\n <Box>\n <Typography variant=\"subtitle2\" gutterBottom>\n Sending window\n </Typography>\n <ToggleButtonGroup\n size=\"small\"\n aria-label=\"Sending days\"\n value={draft.window.days}\n onChange={(_event, days: number[]) =>\n setDraft({ ...draft, window: { ...draft.window, days: [...days].sort((a, b) => a - b) } })\n }\n >\n {OUTREACH_WEEKDAY_LABELS.map((label, day) => (\n <ToggleButton key={label} value={day} aria-label={WEEKDAY_NAMES[day]}>\n {label}\n </ToggleButton>\n ))}\n </ToggleButtonGroup>\n <Stack direction={{ xs: 'column', sm: 'row' }} spacing={2} sx={{ mt: 2 }}>\n <TextField\n select\n size=\"small\"\n label=\"From\"\n value={draft.window.startMinute}\n onChange={(event) =>\n setDraft({ ...draft, window: { ...draft.window, startMinute: Number(event.target.value) } })\n }\n >\n {MINUTE_OPTIONS.slice(0, -1).map((minute) => (\n <MenuItem key={minute} value={minute}>\n {formatMinuteOfDay(minute)}\n </MenuItem>\n ))}\n </TextField>\n <TextField\n select\n size=\"small\"\n label=\"Until\"\n value={draft.window.endMinute}\n onChange={(event) =>\n setDraft({ ...draft, window: { ...draft.window, endMinute: Number(event.target.value) } })\n }\n >\n {MINUTE_OPTIONS.slice(1).map((minute) => (\n <MenuItem key={minute} value={minute}>\n {formatMinuteOfDay(minute)}\n </MenuItem>\n ))}\n </TextField>\n <Autocomplete\n size=\"small\"\n disableClearable\n options={timezoneOptions(mailbox.timezone)}\n value={draft.timezone}\n onChange={(_event, timezone) => setDraft({ ...draft, timezone: timezone ?? draft.timezone })}\n sx={{ minWidth: 240 }}\n renderInput={(params) => <TextField {...params} label=\"Timezone\" />}\n />\n </Stack>\n {'days' in windowCheck ? null : <FormHelperText error>{windowCheck.message}</FormHelperText>}\n </Box>\n {saveError ? <FormHelperText error>{saveError}</FormHelperText> : null}\n <Box>\n <Button variant=\"contained\" disabled={!dirty || invalid || saving} onClick={() => void save()}>\n {MAILBOX_ACTION_LABELS.save}\n </Button>\n </Box>\n </Stack>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`Sends as ${sendAsLabel(stored.sendAs)}, at most ${stored.dailyCap} a day, ` +\n `${stored.window.days.map((day) => OUTREACH_WEEKDAY_LABELS[day]).join(', ')} ` +\n `${formatMinuteOfDay(stored.window.startMinute)}–${formatMinuteOfDay(stored.window.endMinute)} ` +\n `(${stored.timezone}).`}\n </Typography>\n )}\n\n {isMine && !needsReconnect ? (\n <TextField\n label={MAILBOX_ACTION_LABELS.testAddress}\n value={testAddress}\n onChange={(event) => setTestAddress(event.target.value)}\n disabled={busy !== null}\n size=\"small\"\n type=\"email\"\n helperText={\n 'Leave empty to send the test to yourself. A test to an outside mailbox you can ' +\n 'read is the only one whose original source shows the sender authentication results.'\n }\n fullWidth\n />\n ) : null}\n\n {canManage || isMine ? (\n <Stack direction=\"row\" spacing={1} useFlexGap sx={{ flexWrap: 'wrap' }}>\n {canManage && !needsReconnect ? (\n <Button variant=\"outlined\" disabled={busy !== null} onClick={() => void togglePaused()}>\n {mailbox.status === 'paused' ? MAILBOX_ACTION_LABELS.resume : MAILBOX_ACTION_LABELS.pause}\n </Button>\n ) : null}\n {isMine && !needsReconnect ? (\n <Button variant=\"outlined\" disabled={busy !== null} onClick={() => void sendTest()}>\n {testAddress.trim() ? MAILBOX_ACTION_LABELS.testElsewhere : MAILBOX_ACTION_LABELS.test}\n </Button>\n ) : null}\n {canManage ? (\n <Button color=\"error\" disabled={busy !== null} onClick={() => void disconnect()}>\n {MAILBOX_ACTION_LABELS.disconnect}\n </Button>\n ) : null}\n </Stack>\n ) : null}\n </Stack>\n </CardDisplay>\n )\n}\nMailboxCard.displayName = 'MailboxCard'\n\nexport default MailboxCard\n"],"names":["pluginDocsHelp","CardDisplay","useConfirmationContext","useSnackbar","Alert","Autocomplete","Box","Button","Chip","FormControlLabel","FormHelperText","MenuItem","Stack","Switch","TextField","ToggleButton","ToggleButtonGroup","Typography","useEffect","useMemo","useRef","useState","formatMinuteOfDay","isValidTimezone","OUTREACH_DEFAULT_WINDOW","OUTREACH_MAILBOX_STATUS_LABELS","OUTREACH_MAX_DAILY_CAP","OUTREACH_RAMP_STEPS","OUTREACH_WEEKDAY_LABELS","outreachEffectiveDailyCap","outreachLocalDay","summarizeMailboxHealth","validateDailyCap","validateDisplayName","validateSendWindow","autoPauseSentence","mailbox","pause","autoPause","day","atMs","Intl","DateTimeFormat","dateStyle","timeZone","timezone","format","until","untilMs","message","MAILBOX_ACTION_LABELS","save","resume","test","testElsewhere","testAddress","warmUp","disconnect","reconnect","WEEKDAY_NAMES","STATUS_COLOR","connected","paused","reconnect_required","disconnected","MINUTE_OPTIONS","Array","from","length","_","index","timezoneOptions","current","supported","supportedValuesOf","zones","includes","RAMP_SENTENCE","map","step","week","limit","join","draftOf","sendAs","email","displayName","dailyCap","String","window","rampStartedAtMs","MailboxCard","props","isMine","canManage","api","onReconnect","reconnecting","nowMs","Date","now","enqueueSnackbar","confirm","draft","setDraft","saving","setSaving","saveError","setSaveError","busy","setBusy","setTestAddress","stored","storedKey","JSON","stringify","seededFrom","dirty","previous","health","testsToday","Math","max","Number","daily","tests","needsReconnect","status","capCheck","NaN","windowCheck","nameCheck","timezoneValid","invalid","todayLimit","saveSettings","mailboxId","id","variant","error","togglePaused","setPaused","allowDuplicate","sendTest","to","trim","sent","undefined","sentTo","confirmed","title","description","confirmationText","confirmationButtonProps","color","then","catch","revocation","persist","sendAsLabel","option","sendAsOptions","find","entry","header","subheader","help","anchor","contentGutterX","contentGutterY","HeaderProps","action","size","label","spacing","severity","disabled","onClick","role","hasSent","direction","sx","mt","bounces","replies","value","aria-label","component","onSubmit","event","preventDefault","select","onChange","target","helperText","type","slotProps","htmlInput","min","control","checked","gutterBottom","days","_event","sort","a","b","xs","sm","startMinute","slice","minute","endMinute","disableClearable","options","minWidth","renderInput","params","fullWidth","useFlexGap","flexWrap"],"mappings":"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"}
|
|
@@ -379,6 +379,7 @@ import { useOutreachEnrollments, useOutreachSequence, useOutreachSequenceLinks }
|
|
|
379
379
|
props.tab === 'steps' ? /*#__PURE__*/ _jsx(OutreachSequenceEditor, {
|
|
380
380
|
orgId: orgId,
|
|
381
381
|
orgMount: props.orgMount,
|
|
382
|
+
org: props.org,
|
|
382
383
|
sequence: sequence,
|
|
383
384
|
settings: props.settings,
|
|
384
385
|
mailboxes: props.mailboxes,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/components/sequence-detail.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 { consentGroupForHost } from '@aglyn/aglyn/app-utils/consent-groups'\nimport type { ConsolePluginOrgMount } from '@aglyn/aglyn'\nimport {\n mdiAccountMultiplePlusOutline,\n mdiChevronLeft,\n} from '@aglyn/shared-data-mdi'\nimport { MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Button,\n Card,\n Dialog,\n DialogActions,\n DialogContent,\n DialogContentText,\n DialogTitle,\n Stack,\n Tab,\n Tabs,\n Typography,\n} from '@mui/material'\nimport EmptyStateComponent from '@aglyn/shared-ui-jsx/components/empty-state.component'\nimport { useState } from 'react'\nimport { validateOutreachOrgSettings } from '../engine/sequence-validation'\nimport type { OutreachSequenceAction } from '../model/outreach-api'\nimport {\n outreachMailboxActivationIssue,\n outreachSequenceMailboxState,\n type OutreachSequenceIssue,\n type OutreachSequenceMailboxState,\n} from '../model/sequence-draft'\nimport { OutreachEnrollDialog } from './enroll-dialog'\nimport { OutreachEnrollmentsTable } from './enrollments-table'\nimport {\n OutreachLink,\n OutreachLoading,\n OutreachLoadProblem,\n OutreachSequenceStatusChip,\n useOutreachNavigate,\n} from './outreach-ui'\nimport { OutreachSequenceEditor } from './sequence-editor'\nimport { OutreachSequenceReportCard } from './sequence-report-card'\nimport { OutreachRouteError, useOutreachApi } from './use-outreach-api'\nimport {\n useOutreachEnrollments,\n useOutreachSequence,\n useOutreachSequenceLinks,\n} from './use-outreach-data'\nimport type { OutreachMailboxesResult } from './use-outreach-mailboxes'\nimport type { OutreachSettingsLoad } from './use-outreach-settings'\n\nexport type OutreachSequenceTab = 'steps' | 'enrollments'\n\nexport interface OutreachSequenceDetailProps {\n orgId: string\n orgMount?: ConsolePluginOrgMount\n /** The organization document the shell loaded: whose consent groups name a site's contacts. */\n org?: Record<string, unknown> | null\n /** Where the Sequences section lives: `/[orgSlug]/outreach/sequences`. */\n sectionPath: string\n sequenceId: string\n tab: OutreachSequenceTab\n settings: OutreachSettingsLoad\n mailboxes: OutreachMailboxesResult\n /** The Mailboxes section: where a mailbox is connected, resumed or reconnected. */\n mailboxesPath: string\n /** The Compliance section: where the footer's legal name and address are set. */\n compliancePath: string\n}\n\n/** Something activation would refuse, and where it is put right. */\nexport interface OutreachActivationBlocker {\n message: string\n href: string\n linkLabel: string\n}\n\n/**\n * What stops a sequence being activated, read before anyone clicks: its\n * mailbox not sending, and a footer the organization cannot write yet —\n * each in the words the status route refuses with. Only a finished read\n * says something is missing, so nothing is claimed while one is loading.\n */\nexport function outreachActivationBlockers(input: {\n sequence: { mailboxId: string }\n mailboxes: OutreachMailboxesResult\n settings: OutreachSettingsLoad\n paths: { steps: string; mailboxes: string; compliance: string }\n}): OutreachActivationBlocker[] {\n const blockers: OutreachActivationBlocker[] = []\n if (input.mailboxes.status === 'ready') {\n const mailbox =\n input.mailboxes.mailboxes.find((entry) => entry.id === input.sequence.mailboxId) ?? null\n const issue = outreachMailboxActivationIssue(input.sequence.mailboxId, mailbox)\n if (issue) {\n blockers.push(\n issue.code === 'mailbox_not_sending'\n ? { message: issue.message, href: input.paths.mailboxes, linkLabel: 'Open Mailboxes' }\n : { message: issue.message, href: input.paths.steps, linkLabel: 'Choose a mailbox' },\n )\n }\n }\n if (input.settings.status === 'ready') {\n for (const issue of validateOutreachOrgSettings(input.settings.settings)) {\n blockers.push({ message: issue.message, href: input.paths.compliance, linkLabel: 'Open Compliance' })\n }\n }\n return blockers\n}\n\n/** What an active sequence's page says while its mailbox is not sending. */\nconst NOT_SENDING: Record<Exclude<OutreachSequenceMailboxState, 'sending'>, string> = {\n none: 'This sequence has no mailbox, so nothing is sent from it.',\n gone: 'This sequence’s mailbox is no longer connected, so nothing is sent from it.',\n paused: 'This sequence’s mailbox is paused, so nothing is sent until it is resumed.',\n reconnect_required:\n 'Google stopped accepting this sequence’s mailbox, so nothing is sent until it is reconnected.',\n}\n\n/** The words an action's button and its snackbar use. */\nconst ACTIONS: Record<OutreachSequenceAction, { label: string; done: string }> =\n {\n activate: { label: 'Activate', done: 'Sequence activated.' },\n pause: { label: 'Pause', done: 'Sequence paused.' },\n archive: { label: 'Archive', done: 'Sequence archived.' },\n }\n\n/**\n * One sequence (AGL-2980): its steps in the editor, the people enrolled in\n * it, and what can be done with it — activate, pause, archive, delete a\n * draft, and enroll people in an active one.\n */\nexport function OutreachSequenceDetail(props: OutreachSequenceDetailProps) {\n const { orgId, sequenceId, sectionPath } = props\n const api = useOutreachApi(orgId)\n const navigate = useOutreachNavigate()\n const { enqueueSnackbar } = useSnackbar()\n const { data: user } = useUser()\n const loaded = useOutreachSequence(orgId, sequenceId)\n const enrollments = useOutreachEnrollments(\n props.tab === 'enrollments' ? orgId : null,\n sequenceId,\n )\n // One document however large the sequence, and read on both tabs, because\n // the report card sits above them (AGL-3239).\n const links = useOutreachSequenceLinks(orgId, sequenceId)\n const [busy, setBusy] = useState(false)\n const [refusal, setRefusal] = useState<{\n message: string\n issues: OutreachSequenceIssue[]\n } | null>(null)\n const [confirming, setConfirming] = useState<'archive' | 'delete' | null>(\n null,\n )\n const [enrolling, setEnrolling] = useState(false)\n\n const sequence = loaded.data\n const detailPath = `${sectionPath}/${sequenceId}`\n const back = (\n <OutreachLink href={sectionPath}>\n <Stack\n direction=\"row\"\n spacing={0.5}\n component=\"span\"\n sx={{ alignItems: 'center', display: 'inline-flex' }}\n >\n <MdiIcon path={mdiChevronLeft.path} fontSize=\"small\" />\n <span>Sequences</span>\n </Stack>\n </OutreachLink>\n )\n\n if (loaded.status === 'loading')\n return <OutreachLoading label=\"Loading the sequence…\" />\n if (loaded.status === 'error' || loaded.status === 'refused') {\n return <OutreachLoadProblem status={loaded.status} what=\"sequence\" />\n }\n if (!sequence) {\n return (\n <Stack spacing={2}>\n {back}\n <Card variant=\"outlined\">\n <EmptyStateComponent label=\"This sequence no longer exists\" />\n </Card>\n </Stack>\n )\n }\n\n const mailbox =\n props.mailboxes.mailboxes.find((entry) => entry.id === sequence.mailboxId) ??\n null\n const mailboxState =\n props.mailboxes.status === 'ready'\n ? outreachSequenceMailboxState(sequence.mailboxId, mailbox)\n : 'sending'\n const activatable = sequence.status === 'draft' || sequence.status === 'paused'\n const blockers = activatable\n ? outreachActivationBlockers({\n sequence,\n mailboxes: props.mailboxes,\n settings: props.settings,\n paths: {\n steps: detailPath,\n mailboxes: props.mailboxesPath,\n compliance: props.compliancePath,\n },\n })\n : []\n // Enrolling onto a mailbox that is gone is refused by the route; a paused\n // one only makes the new people wait with everyone else.\n const canEnroll =\n sequence.status === 'active' &&\n mailboxState !== 'gone' &&\n mailboxState !== 'none'\n const act = async (action: OutreachSequenceAction) => {\n setBusy(true)\n setRefusal(null)\n try {\n const answer = await api.setSequenceStatus(sequence.id, action)\n enqueueSnackbar(\n action === 'archive' && answer.stoppedEnrollments\n ? `${ACTIONS.archive.done} ${answer.stoppedEnrollments} ${answer.stoppedEnrollments === 1 ? 'person was' : 'people were'} stopped.`\n : ACTIONS[action].done,\n { variant: 'success' },\n )\n } catch (error) {\n setRefusal({\n message: (error as Error).message,\n issues:\n error instanceof OutreachRouteError\n ? error.issues.filter(\n (issue): issue is OutreachSequenceIssue => 'path' in issue,\n )\n : [],\n })\n } finally {\n setBusy(false)\n }\n }\n const remove = async () => {\n setBusy(true)\n setRefusal(null)\n try {\n await api.deleteSequence(sequence.id)\n enqueueSnackbar('Sequence deleted.', { variant: 'success' })\n navigate(sectionPath)\n } catch (error) {\n setRefusal({ message: (error as Error).message, issues: [] })\n setBusy(false)\n }\n }\n\n const enrollButton = (\n <Button\n variant=\"contained\"\n startIcon={\n <MdiIcon path={mdiAccountMultiplePlusOutline.path} fontSize=\"small\" />\n }\n disabled={!canEnroll}\n onClick={() => setEnrolling(true)}\n >\n Enroll people\n </Button>\n )\n\n return (\n <Stack spacing={2}>\n {back}\n <Stack\n direction={{ xs: 'column', md: 'row' }}\n spacing={1.5}\n sx={{ alignItems: { md: 'center' }, justifyContent: 'space-between' }}\n >\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center', minWidth: 0 }}\n >\n <Typography\n variant=\"h5\"\n component=\"h2\"\n sx={{ wordBreak: 'break-word' }}\n >\n {sequence.name}\n </Typography>\n <OutreachSequenceStatusChip status={sequence.status} />\n </Stack>\n <Stack direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', rowGap: 1 }}>\n {activatable ? (\n <Button\n variant=\"outlined\"\n disabled={busy || blockers.length > 0}\n onClick={() => void act('activate')}\n >\n {ACTIONS.activate.label}\n </Button>\n ) : null}\n {sequence.status === 'active' ? (\n <Button\n variant=\"outlined\"\n disabled={busy}\n onClick={() => void act('pause')}\n >\n {ACTIONS.pause.label}\n </Button>\n ) : null}\n {sequence.status !== 'archived' ? (\n <Button\n variant=\"outlined\"\n color=\"error\"\n disabled={busy}\n onClick={() => setConfirming('archive')}\n >\n {ACTIONS.archive.label}\n </Button>\n ) : null}\n {sequence.status === 'draft' ? (\n <Button\n variant=\"text\"\n color=\"error\"\n disabled={busy}\n onClick={() => setConfirming('delete')}\n >\n Delete\n </Button>\n ) : null}\n {sequence.status === 'active' ? enrollButton : null}\n </Stack>\n </Stack>\n\n {refusal ? (\n <Alert severity=\"error\" onClose={() => setRefusal(null)}>\n <Stack spacing={0.5}>\n <span>{refusal.message}</span>\n {refusal.issues.length > 1 ? (\n <Stack component=\"ul\" spacing={0.25} sx={{ m: 0, pl: 2.5 }}>\n {refusal.issues.slice(1).map((issue) => (\n <li key={`${issue.path}:${issue.code}`}>{issue.message}</li>\n ))}\n </Stack>\n ) : null}\n </Stack>\n </Alert>\n ) : null}\n {blockers.length ? (\n <Alert severity=\"warning\" data-testid=\"outreach-activation-blockers\">\n <Stack spacing={0.5}>\n <span>\n {sequence.status === 'draft'\n ? 'A draft sends nothing, and this one can’t be activated yet:'\n : 'This sequence can’t be activated again yet:'}\n </span>\n <Stack component=\"ul\" spacing={0.25} sx={{ m: 0, pl: 2.5 }}>\n {blockers.map((blocker) => (\n <li key={blocker.message}>\n {blocker.message}{' '}\n <OutreachLink href={blocker.href}>{blocker.linkLabel}</OutreachLink>\n </li>\n ))}\n </Stack>\n </Stack>\n </Alert>\n ) : sequence.status === 'draft' ? (\n <Alert severity=\"info\">\n A draft sends nothing. Activate it to enroll people.\n </Alert>\n ) : null}\n {sequence.status === 'active' && mailboxState !== 'sending' ? (\n <Alert severity=\"warning\">\n {NOT_SENDING[mailboxState]}{' '}\n <OutreachLink href={props.mailboxesPath}>Open Mailboxes</OutreachLink>\n </Alert>\n ) : null}\n\n {/*\n * Above the tabs, not inside one (AGL-3239): what a sequence is doing\n * is the question a rep opens it with, and putting it behind a tab\n * makes the steps the first answer to \"how is this going\".\n *\n * A draft has sent nothing and measured nothing, so it gets no card\n * rather than a row of zeroes.\n */}\n {sequence.status === 'draft' ? null : (\n <OutreachSequenceReportCard\n sequence={sequence}\n links={links}\n timeZone={mailbox?.timezone ?? null}\n />\n )}\n\n <Tabs\n value={props.tab}\n onChange={(_event, next: OutreachSequenceTab) =>\n navigate(next === 'steps' ? detailPath : `${detailPath}/enrollments`)\n }\n aria-label=\"Sequence\"\n >\n <Tab value=\"steps\" label=\"Steps\" />\n <Tab value=\"enrollments\" label=\"Enrollments\" />\n </Tabs>\n\n {props.tab === 'steps' ? (\n <OutreachSequenceEditor\n orgId={orgId}\n orgMount={props.orgMount}\n sequence={sequence}\n settings={props.settings}\n mailboxes={props.mailboxes}\n mailboxesPath={props.mailboxesPath}\n onSaved={() => undefined}\n />\n ) : (\n <OutreachEnrollmentsTable\n enrollments={enrollments}\n steps={sequence.steps}\n trackClicks={sequence.settings.trackClicks}\n timeZone={mailbox?.timezone ?? null}\n api={api}\n enrollAction={sequence.status === 'active' ? enrollButton : undefined}\n />\n )}\n\n <OutreachEnrollDialog\n open={enrolling}\n onClose={() => setEnrolling(false)}\n orgId={orgId}\n sequence={sequence}\n contactGroupId={\n sequence.hostId\n ? consentGroupForHost(props.org ?? null, sequence.hostId).groupId\n : ''\n }\n uid={user?.uid ?? null}\n api={api}\n />\n\n <Dialog\n open={Boolean(confirming)}\n onClose={() => setConfirming(null)}\n fullWidth\n maxWidth=\"xs\"\n >\n <DialogTitle>\n {confirming === 'delete'\n ? 'Delete this draft?'\n : 'Archive this sequence?'}\n </DialogTitle>\n <DialogContent>\n <DialogContentText>\n {confirming === 'delete'\n ? 'It is removed for good. Nobody was ever enrolled in it.'\n : 'Archiving is final. Everyone still in it is stopped, and it takes no one new.'}\n </DialogContentText>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setConfirming(null)}>Cancel</Button>\n <Button\n color=\"error\"\n variant=\"contained\"\n onClick={() => {\n const chosen = confirming\n setConfirming(null)\n if (chosen === 'delete') void remove()\n else void act('archive')\n }}\n >\n {confirming === 'delete' ? 'Delete' : 'Archive'}\n </Button>\n </DialogActions>\n </Dialog>\n </Stack>\n )\n}\nOutreachSequenceDetail.displayName = 'OutreachSequenceDetail'\n\nexport default OutreachSequenceDetail\n"],"names":["consentGroupForHost","mdiAccountMultiplePlusOutline","mdiChevronLeft","MdiIcon","useSnackbar","useUser","Alert","Button","Card","Dialog","DialogActions","DialogContent","DialogContentText","DialogTitle","Stack","Tab","Tabs","Typography","EmptyStateComponent","useState","validateOutreachOrgSettings","outreachMailboxActivationIssue","outreachSequenceMailboxState","OutreachEnrollDialog","OutreachEnrollmentsTable","OutreachLink","OutreachLoading","OutreachLoadProblem","OutreachSequenceStatusChip","useOutreachNavigate","OutreachSequenceEditor","OutreachSequenceReportCard","OutreachRouteError","useOutreachApi","useOutreachEnrollments","useOutreachSequence","useOutreachSequenceLinks","outreachActivationBlockers","input","blockers","mailboxes","status","mailbox","find","entry","id","sequence","mailboxId","issue","push","code","message","href","paths","linkLabel","steps","settings","compliance","NOT_SENDING","none","gone","paused","reconnect_required","ACTIONS","activate","label","done","pause","archive","OutreachSequenceDetail","props","orgId","sequenceId","sectionPath","api","navigate","enqueueSnackbar","data","user","loaded","enrollments","tab","links","busy","setBusy","refusal","setRefusal","confirming","setConfirming","enrolling","setEnrolling","detailPath","back","direction","spacing","component","sx","alignItems","display","path","fontSize","span","what","variant","mailboxState","activatable","mailboxesPath","compliancePath","canEnroll","act","action","answer","setSequenceStatus","stoppedEnrollments","error","issues","filter","remove","deleteSequence","enrollButton","startIcon","disabled","onClick","xs","md","justifyContent","minWidth","wordBreak","name","flexWrap","rowGap","length","color","severity","onClose","m","pl","slice","map","li","data-testid","blocker","timeZone","timezone","value","onChange","_event","next","aria-label","orgMount","onSaved","undefined","trackClicks","enrollAction","open","contactGroupId","hostId","org","groupId","uid","Boolean","fullWidth","maxWidth","chosen","displayName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/components/sequence-detail.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 { consentGroupForHost } from '@aglyn/aglyn/app-utils/consent-groups'\nimport type { ConsolePluginOrgMount } from '@aglyn/aglyn'\nimport {\n mdiAccountMultiplePlusOutline,\n mdiChevronLeft,\n} from '@aglyn/shared-data-mdi'\nimport { MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Button,\n Card,\n Dialog,\n DialogActions,\n DialogContent,\n DialogContentText,\n DialogTitle,\n Stack,\n Tab,\n Tabs,\n Typography,\n} from '@mui/material'\nimport EmptyStateComponent from '@aglyn/shared-ui-jsx/components/empty-state.component'\nimport { useState } from 'react'\nimport { validateOutreachOrgSettings } from '../engine/sequence-validation'\nimport type { OutreachSequenceAction } from '../model/outreach-api'\nimport {\n outreachMailboxActivationIssue,\n outreachSequenceMailboxState,\n type OutreachSequenceIssue,\n type OutreachSequenceMailboxState,\n} from '../model/sequence-draft'\nimport { OutreachEnrollDialog } from './enroll-dialog'\nimport { OutreachEnrollmentsTable } from './enrollments-table'\nimport {\n OutreachLink,\n OutreachLoading,\n OutreachLoadProblem,\n OutreachSequenceStatusChip,\n useOutreachNavigate,\n} from './outreach-ui'\nimport { OutreachSequenceEditor } from './sequence-editor'\nimport { OutreachSequenceReportCard } from './sequence-report-card'\nimport { OutreachRouteError, useOutreachApi } from './use-outreach-api'\nimport {\n useOutreachEnrollments,\n useOutreachSequence,\n useOutreachSequenceLinks,\n} from './use-outreach-data'\nimport type { OutreachMailboxesResult } from './use-outreach-mailboxes'\nimport type { OutreachSettingsLoad } from './use-outreach-settings'\n\nexport type OutreachSequenceTab = 'steps' | 'enrollments'\n\nexport interface OutreachSequenceDetailProps {\n orgId: string\n orgMount?: ConsolePluginOrgMount\n /** The organization document the shell loaded: whose consent groups name a site's contacts. */\n org?: Record<string, unknown> | null\n /** Where the Sequences section lives: `/[orgSlug]/outreach/sequences`. */\n sectionPath: string\n sequenceId: string\n tab: OutreachSequenceTab\n settings: OutreachSettingsLoad\n mailboxes: OutreachMailboxesResult\n /** The Mailboxes section: where a mailbox is connected, resumed or reconnected. */\n mailboxesPath: string\n /** The Compliance section: where the footer's legal name and address are set. */\n compliancePath: string\n}\n\n/** Something activation would refuse, and where it is put right. */\nexport interface OutreachActivationBlocker {\n message: string\n href: string\n linkLabel: string\n}\n\n/**\n * What stops a sequence being activated, read before anyone clicks: its\n * mailbox not sending, and a footer the organization cannot write yet —\n * each in the words the status route refuses with. Only a finished read\n * says something is missing, so nothing is claimed while one is loading.\n */\nexport function outreachActivationBlockers(input: {\n sequence: { mailboxId: string }\n mailboxes: OutreachMailboxesResult\n settings: OutreachSettingsLoad\n paths: { steps: string; mailboxes: string; compliance: string }\n}): OutreachActivationBlocker[] {\n const blockers: OutreachActivationBlocker[] = []\n if (input.mailboxes.status === 'ready') {\n const mailbox =\n input.mailboxes.mailboxes.find((entry) => entry.id === input.sequence.mailboxId) ?? null\n const issue = outreachMailboxActivationIssue(input.sequence.mailboxId, mailbox)\n if (issue) {\n blockers.push(\n issue.code === 'mailbox_not_sending'\n ? { message: issue.message, href: input.paths.mailboxes, linkLabel: 'Open Mailboxes' }\n : { message: issue.message, href: input.paths.steps, linkLabel: 'Choose a mailbox' },\n )\n }\n }\n if (input.settings.status === 'ready') {\n for (const issue of validateOutreachOrgSettings(input.settings.settings)) {\n blockers.push({ message: issue.message, href: input.paths.compliance, linkLabel: 'Open Compliance' })\n }\n }\n return blockers\n}\n\n/** What an active sequence's page says while its mailbox is not sending. */\nconst NOT_SENDING: Record<Exclude<OutreachSequenceMailboxState, 'sending'>, string> = {\n none: 'This sequence has no mailbox, so nothing is sent from it.',\n gone: 'This sequence’s mailbox is no longer connected, so nothing is sent from it.',\n paused: 'This sequence’s mailbox is paused, so nothing is sent until it is resumed.',\n reconnect_required:\n 'Google stopped accepting this sequence’s mailbox, so nothing is sent until it is reconnected.',\n}\n\n/** The words an action's button and its snackbar use. */\nconst ACTIONS: Record<OutreachSequenceAction, { label: string; done: string }> =\n {\n activate: { label: 'Activate', done: 'Sequence activated.' },\n pause: { label: 'Pause', done: 'Sequence paused.' },\n archive: { label: 'Archive', done: 'Sequence archived.' },\n }\n\n/**\n * One sequence (AGL-2980): its steps in the editor, the people enrolled in\n * it, and what can be done with it — activate, pause, archive, delete a\n * draft, and enroll people in an active one.\n */\nexport function OutreachSequenceDetail(props: OutreachSequenceDetailProps) {\n const { orgId, sequenceId, sectionPath } = props\n const api = useOutreachApi(orgId)\n const navigate = useOutreachNavigate()\n const { enqueueSnackbar } = useSnackbar()\n const { data: user } = useUser()\n const loaded = useOutreachSequence(orgId, sequenceId)\n const enrollments = useOutreachEnrollments(\n props.tab === 'enrollments' ? orgId : null,\n sequenceId,\n )\n // One document however large the sequence, and read on both tabs, because\n // the report card sits above them (AGL-3239).\n const links = useOutreachSequenceLinks(orgId, sequenceId)\n const [busy, setBusy] = useState(false)\n const [refusal, setRefusal] = useState<{\n message: string\n issues: OutreachSequenceIssue[]\n } | null>(null)\n const [confirming, setConfirming] = useState<'archive' | 'delete' | null>(\n null,\n )\n const [enrolling, setEnrolling] = useState(false)\n\n const sequence = loaded.data\n const detailPath = `${sectionPath}/${sequenceId}`\n const back = (\n <OutreachLink href={sectionPath}>\n <Stack\n direction=\"row\"\n spacing={0.5}\n component=\"span\"\n sx={{ alignItems: 'center', display: 'inline-flex' }}\n >\n <MdiIcon path={mdiChevronLeft.path} fontSize=\"small\" />\n <span>Sequences</span>\n </Stack>\n </OutreachLink>\n )\n\n if (loaded.status === 'loading')\n return <OutreachLoading label=\"Loading the sequence…\" />\n if (loaded.status === 'error' || loaded.status === 'refused') {\n return <OutreachLoadProblem status={loaded.status} what=\"sequence\" />\n }\n if (!sequence) {\n return (\n <Stack spacing={2}>\n {back}\n <Card variant=\"outlined\">\n <EmptyStateComponent label=\"This sequence no longer exists\" />\n </Card>\n </Stack>\n )\n }\n\n const mailbox =\n props.mailboxes.mailboxes.find((entry) => entry.id === sequence.mailboxId) ??\n null\n const mailboxState =\n props.mailboxes.status === 'ready'\n ? outreachSequenceMailboxState(sequence.mailboxId, mailbox)\n : 'sending'\n const activatable = sequence.status === 'draft' || sequence.status === 'paused'\n const blockers = activatable\n ? outreachActivationBlockers({\n sequence,\n mailboxes: props.mailboxes,\n settings: props.settings,\n paths: {\n steps: detailPath,\n mailboxes: props.mailboxesPath,\n compliance: props.compliancePath,\n },\n })\n : []\n // Enrolling onto a mailbox that is gone is refused by the route; a paused\n // one only makes the new people wait with everyone else.\n const canEnroll =\n sequence.status === 'active' &&\n mailboxState !== 'gone' &&\n mailboxState !== 'none'\n const act = async (action: OutreachSequenceAction) => {\n setBusy(true)\n setRefusal(null)\n try {\n const answer = await api.setSequenceStatus(sequence.id, action)\n enqueueSnackbar(\n action === 'archive' && answer.stoppedEnrollments\n ? `${ACTIONS.archive.done} ${answer.stoppedEnrollments} ${answer.stoppedEnrollments === 1 ? 'person was' : 'people were'} stopped.`\n : ACTIONS[action].done,\n { variant: 'success' },\n )\n } catch (error) {\n setRefusal({\n message: (error as Error).message,\n issues:\n error instanceof OutreachRouteError\n ? error.issues.filter(\n (issue): issue is OutreachSequenceIssue => 'path' in issue,\n )\n : [],\n })\n } finally {\n setBusy(false)\n }\n }\n const remove = async () => {\n setBusy(true)\n setRefusal(null)\n try {\n await api.deleteSequence(sequence.id)\n enqueueSnackbar('Sequence deleted.', { variant: 'success' })\n navigate(sectionPath)\n } catch (error) {\n setRefusal({ message: (error as Error).message, issues: [] })\n setBusy(false)\n }\n }\n\n const enrollButton = (\n <Button\n variant=\"contained\"\n startIcon={\n <MdiIcon path={mdiAccountMultiplePlusOutline.path} fontSize=\"small\" />\n }\n disabled={!canEnroll}\n onClick={() => setEnrolling(true)}\n >\n Enroll people\n </Button>\n )\n\n return (\n <Stack spacing={2}>\n {back}\n <Stack\n direction={{ xs: 'column', md: 'row' }}\n spacing={1.5}\n sx={{ alignItems: { md: 'center' }, justifyContent: 'space-between' }}\n >\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center', minWidth: 0 }}\n >\n <Typography\n variant=\"h5\"\n component=\"h2\"\n sx={{ wordBreak: 'break-word' }}\n >\n {sequence.name}\n </Typography>\n <OutreachSequenceStatusChip status={sequence.status} />\n </Stack>\n <Stack direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', rowGap: 1 }}>\n {activatable ? (\n <Button\n variant=\"outlined\"\n disabled={busy || blockers.length > 0}\n onClick={() => void act('activate')}\n >\n {ACTIONS.activate.label}\n </Button>\n ) : null}\n {sequence.status === 'active' ? (\n <Button\n variant=\"outlined\"\n disabled={busy}\n onClick={() => void act('pause')}\n >\n {ACTIONS.pause.label}\n </Button>\n ) : null}\n {sequence.status !== 'archived' ? (\n <Button\n variant=\"outlined\"\n color=\"error\"\n disabled={busy}\n onClick={() => setConfirming('archive')}\n >\n {ACTIONS.archive.label}\n </Button>\n ) : null}\n {sequence.status === 'draft' ? (\n <Button\n variant=\"text\"\n color=\"error\"\n disabled={busy}\n onClick={() => setConfirming('delete')}\n >\n Delete\n </Button>\n ) : null}\n {sequence.status === 'active' ? enrollButton : null}\n </Stack>\n </Stack>\n\n {refusal ? (\n <Alert severity=\"error\" onClose={() => setRefusal(null)}>\n <Stack spacing={0.5}>\n <span>{refusal.message}</span>\n {refusal.issues.length > 1 ? (\n <Stack component=\"ul\" spacing={0.25} sx={{ m: 0, pl: 2.5 }}>\n {refusal.issues.slice(1).map((issue) => (\n <li key={`${issue.path}:${issue.code}`}>{issue.message}</li>\n ))}\n </Stack>\n ) : null}\n </Stack>\n </Alert>\n ) : null}\n {blockers.length ? (\n <Alert severity=\"warning\" data-testid=\"outreach-activation-blockers\">\n <Stack spacing={0.5}>\n <span>\n {sequence.status === 'draft'\n ? 'A draft sends nothing, and this one can’t be activated yet:'\n : 'This sequence can’t be activated again yet:'}\n </span>\n <Stack component=\"ul\" spacing={0.25} sx={{ m: 0, pl: 2.5 }}>\n {blockers.map((blocker) => (\n <li key={blocker.message}>\n {blocker.message}{' '}\n <OutreachLink href={blocker.href}>{blocker.linkLabel}</OutreachLink>\n </li>\n ))}\n </Stack>\n </Stack>\n </Alert>\n ) : sequence.status === 'draft' ? (\n <Alert severity=\"info\">\n A draft sends nothing. Activate it to enroll people.\n </Alert>\n ) : null}\n {sequence.status === 'active' && mailboxState !== 'sending' ? (\n <Alert severity=\"warning\">\n {NOT_SENDING[mailboxState]}{' '}\n <OutreachLink href={props.mailboxesPath}>Open Mailboxes</OutreachLink>\n </Alert>\n ) : null}\n\n {/*\n * Above the tabs, not inside one (AGL-3239): what a sequence is doing\n * is the question a rep opens it with, and putting it behind a tab\n * makes the steps the first answer to \"how is this going\".\n *\n * A draft has sent nothing and measured nothing, so it gets no card\n * rather than a row of zeroes.\n */}\n {sequence.status === 'draft' ? null : (\n <OutreachSequenceReportCard\n sequence={sequence}\n links={links}\n timeZone={mailbox?.timezone ?? null}\n />\n )}\n\n <Tabs\n value={props.tab}\n onChange={(_event, next: OutreachSequenceTab) =>\n navigate(next === 'steps' ? detailPath : `${detailPath}/enrollments`)\n }\n aria-label=\"Sequence\"\n >\n <Tab value=\"steps\" label=\"Steps\" />\n <Tab value=\"enrollments\" label=\"Enrollments\" />\n </Tabs>\n\n {props.tab === 'steps' ? (\n <OutreachSequenceEditor\n orgId={orgId}\n orgMount={props.orgMount}\n org={props.org}\n sequence={sequence}\n settings={props.settings}\n mailboxes={props.mailboxes}\n mailboxesPath={props.mailboxesPath}\n onSaved={() => undefined}\n />\n ) : (\n <OutreachEnrollmentsTable\n enrollments={enrollments}\n steps={sequence.steps}\n trackClicks={sequence.settings.trackClicks}\n timeZone={mailbox?.timezone ?? null}\n api={api}\n enrollAction={sequence.status === 'active' ? enrollButton : undefined}\n />\n )}\n\n <OutreachEnrollDialog\n open={enrolling}\n onClose={() => setEnrolling(false)}\n orgId={orgId}\n sequence={sequence}\n contactGroupId={\n sequence.hostId\n ? consentGroupForHost(props.org ?? null, sequence.hostId).groupId\n : ''\n }\n uid={user?.uid ?? null}\n api={api}\n />\n\n <Dialog\n open={Boolean(confirming)}\n onClose={() => setConfirming(null)}\n fullWidth\n maxWidth=\"xs\"\n >\n <DialogTitle>\n {confirming === 'delete'\n ? 'Delete this draft?'\n : 'Archive this sequence?'}\n </DialogTitle>\n <DialogContent>\n <DialogContentText>\n {confirming === 'delete'\n ? 'It is removed for good. Nobody was ever enrolled in it.'\n : 'Archiving is final. Everyone still in it is stopped, and it takes no one new.'}\n </DialogContentText>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setConfirming(null)}>Cancel</Button>\n <Button\n color=\"error\"\n variant=\"contained\"\n onClick={() => {\n const chosen = confirming\n setConfirming(null)\n if (chosen === 'delete') void remove()\n else void act('archive')\n }}\n >\n {confirming === 'delete' ? 'Delete' : 'Archive'}\n </Button>\n </DialogActions>\n </Dialog>\n </Stack>\n )\n}\nOutreachSequenceDetail.displayName = 'OutreachSequenceDetail'\n\nexport default OutreachSequenceDetail\n"],"names":["consentGroupForHost","mdiAccountMultiplePlusOutline","mdiChevronLeft","MdiIcon","useSnackbar","useUser","Alert","Button","Card","Dialog","DialogActions","DialogContent","DialogContentText","DialogTitle","Stack","Tab","Tabs","Typography","EmptyStateComponent","useState","validateOutreachOrgSettings","outreachMailboxActivationIssue","outreachSequenceMailboxState","OutreachEnrollDialog","OutreachEnrollmentsTable","OutreachLink","OutreachLoading","OutreachLoadProblem","OutreachSequenceStatusChip","useOutreachNavigate","OutreachSequenceEditor","OutreachSequenceReportCard","OutreachRouteError","useOutreachApi","useOutreachEnrollments","useOutreachSequence","useOutreachSequenceLinks","outreachActivationBlockers","input","blockers","mailboxes","status","mailbox","find","entry","id","sequence","mailboxId","issue","push","code","message","href","paths","linkLabel","steps","settings","compliance","NOT_SENDING","none","gone","paused","reconnect_required","ACTIONS","activate","label","done","pause","archive","OutreachSequenceDetail","props","orgId","sequenceId","sectionPath","api","navigate","enqueueSnackbar","data","user","loaded","enrollments","tab","links","busy","setBusy","refusal","setRefusal","confirming","setConfirming","enrolling","setEnrolling","detailPath","back","direction","spacing","component","sx","alignItems","display","path","fontSize","span","what","variant","mailboxState","activatable","mailboxesPath","compliancePath","canEnroll","act","action","answer","setSequenceStatus","stoppedEnrollments","error","issues","filter","remove","deleteSequence","enrollButton","startIcon","disabled","onClick","xs","md","justifyContent","minWidth","wordBreak","name","flexWrap","rowGap","length","color","severity","onClose","m","pl","slice","map","li","data-testid","blocker","timeZone","timezone","value","onChange","_event","next","aria-label","orgMount","org","onSaved","undefined","trackClicks","enrollAction","open","contactGroupId","hostId","groupId","uid","Boolean","fullWidth","maxWidth","chosen","displayName"],"mappings":"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"}
|
|
@@ -5,6 +5,8 @@ import type { OutreachSettingsLoad } from './use-outreach-settings';
|
|
|
5
5
|
export interface OutreachSequenceEditorProps {
|
|
6
6
|
orgId: string;
|
|
7
7
|
orgMount?: ConsolePluginOrgMount;
|
|
8
|
+
/** The organization document, for the consent group a test send's person picker searches (AGL-3325). */
|
|
9
|
+
org?: Record<string, unknown> | null;
|
|
8
10
|
/** The stored sequence, or `null` for a new one. */
|
|
9
11
|
sequence: OutreachSequence | null;
|
|
10
12
|
/** The organization's compliance settings: its countries, and the preview's footer. */
|
|
@@ -18,6 +18,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
18
18
|
import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
|
|
19
19
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
20
20
|
import { pluginDocsHelp } from "@aglyn/aglyn";
|
|
21
|
+
import { consentGroupForHost } from "@aglyn/aglyn/app-utils/consent-groups";
|
|
21
22
|
import { mdiPlus } from "@aglyn/shared-data-mdi";
|
|
22
23
|
import { CardDisplay, MdiIcon } from "@aglyn/shared-ui-jsx";
|
|
23
24
|
import CampaignPicker from "@aglyn/shared-ui-email-campaigns/components/campaign-picker.component";
|
|
@@ -33,6 +34,7 @@ import { OutreachSendWindowFields } from "./send-window-fields.js";
|
|
|
33
34
|
import { OutreachSequenceMailboxPicker } from "./sequence-mailbox-picker.js";
|
|
34
35
|
import { OutreachSequencePreview } from "./sequence-preview.js";
|
|
35
36
|
import { OutreachStepCard } from "./sequence-step-card.js";
|
|
37
|
+
import { OutreachStepTestDialog } from "./step-test-dialog.js";
|
|
36
38
|
import { OutreachRouteError, useOutreachApi } from "./use-outreach-api.js";
|
|
37
39
|
import { useOutreachEmailTemplates } from "./use-outreach-crm.js";
|
|
38
40
|
/** The step a dotted issue path is about, and the field within it. */ function stepIssueKey(path) {
|
|
@@ -56,7 +58,7 @@ import { useOutreachEmailTemplates } from "./use-outreach-crm.js";
|
|
|
56
58
|
* no longer change once people are enrolled) comes back from the save and
|
|
57
59
|
* is shown in the same places.
|
|
58
60
|
*/ export function OutreachSequenceEditor(props) {
|
|
59
|
-
var _ref, _ref1, _props_mailboxes_mailboxes_find, _ref2, _issueAt, _issueAt1;
|
|
61
|
+
var _ref, _ref1, _props_mailboxes_mailboxes_find, _ref2, _issueAt, _issueAt1, _props_org, _ref3;
|
|
60
62
|
var _settings_settings, _settings_settings1, _issues_find, _hosts_find;
|
|
61
63
|
const { orgId, orgMount, sequence, settings } = props;
|
|
62
64
|
const api = useOutreachApi(orgId);
|
|
@@ -78,6 +80,8 @@ import { useOutreachEmailTemplates } from "./use-outreach-crm.js";
|
|
|
78
80
|
const [serverIssues, setServerIssues] = useState([]);
|
|
79
81
|
const [saving, setSaving] = useState(false);
|
|
80
82
|
const [taskAnchor, setTaskAnchor] = useState(null);
|
|
83
|
+
// The step whose test-send dialog is open (AGL-3325), by index.
|
|
84
|
+
const [testing, setTesting] = useState(null);
|
|
81
85
|
// A stored sequence the listener updated (a save, a status change) is the
|
|
82
86
|
// new starting point.
|
|
83
87
|
useEffect(()=>{
|
|
@@ -215,6 +219,13 @@ import { useOutreachEmailTemplates } from "./use-outreach-crm.js";
|
|
|
215
219
|
...draft.settings.allowedCountries
|
|
216
220
|
])
|
|
217
221
|
];
|
|
222
|
+
/*
|
|
223
|
+
* A test sends the step as STORED (AGL-3325): the route reads the saved
|
|
224
|
+
* sequence, so a new one has nothing to send yet and an edited one sends
|
|
225
|
+
* its last save — said on the button and in the dialog rather than found
|
|
226
|
+
* out in the inbox.
|
|
227
|
+
*/ const unsaved = sequence !== null && JSON.stringify(draft) !== JSON.stringify(outreachSequenceDraftOf(sequence));
|
|
228
|
+
const sendTestDisabledReason = !sequence ? 'Create the sequence to send a test of it.' : !draft.mailboxId ? 'Choose a mailbox to send the test from.' : null;
|
|
218
229
|
return /*#__PURE__*/ _jsxs(Grid, {
|
|
219
230
|
container: true,
|
|
220
231
|
spacing: 2,
|
|
@@ -318,7 +329,9 @@ import { useOutreachEmailTemplates } from "./use-outreach-crm.js";
|
|
|
318
329
|
onMove: (direction)=>moveStep(index, direction),
|
|
319
330
|
onRemove: ()=>update({
|
|
320
331
|
steps: draft.steps.filter((_entry, position)=>position !== index)
|
|
321
|
-
})
|
|
332
|
+
}),
|
|
333
|
+
onSendTest: step.kind === 'email' && !archived ? ()=>setTesting(index) : undefined,
|
|
334
|
+
sendTestDisabledReason: sendTestDisabledReason
|
|
322
335
|
}, step.id || `step-${index}`)),
|
|
323
336
|
/*#__PURE__*/ _jsxs(Stack, {
|
|
324
337
|
direction: "row",
|
|
@@ -514,6 +527,17 @@ import { useOutreachEmailTemplates } from "./use-outreach-crm.js";
|
|
|
514
527
|
]
|
|
515
528
|
})
|
|
516
529
|
}),
|
|
530
|
+
sequence && testing !== null ? /*#__PURE__*/ _jsx(OutreachStepTestDialog, {
|
|
531
|
+
open: true,
|
|
532
|
+
onClose: ()=>setTesting(null),
|
|
533
|
+
orgId: orgId,
|
|
534
|
+
sequence: sequence,
|
|
535
|
+
stepIndex: testing,
|
|
536
|
+
contactGroupId: sequence.hostId ? consentGroupForHost((_props_org = props.org) != null ? _props_org : null, sequence.hostId).groupId : '',
|
|
537
|
+
selfEmail: (_ref3 = user == null ? void 0 : user.email) != null ? _ref3 : null,
|
|
538
|
+
unsaved: unsaved,
|
|
539
|
+
api: api
|
|
540
|
+
}) : null,
|
|
517
541
|
/*#__PURE__*/ _jsx(Grid, {
|
|
518
542
|
size: {
|
|
519
543
|
xs: 12,
|