@aglyn/plugins-outreach 1.0.0-beta.218 → 1.0.0-beta.219
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +14 -14
- package/src/lib/components/compliance-section.js +72 -34
- package/src/lib/components/compliance-section.js.map +1 -1
- package/src/lib/components/enroll-dialog.d.ts +12 -2
- package/src/lib/components/enroll-dialog.js +72 -18
- package/src/lib/components/enroll-dialog.js.map +1 -1
- package/src/lib/components/enrollment-detail.d.ts +5 -1
- package/src/lib/components/enrollment-detail.js +12 -6
- package/src/lib/components/enrollment-detail.js.map +1 -1
- package/src/lib/components/outreach-console-sections.d.ts +1 -1
- package/src/lib/components/outreach-console-sections.js +1 -1
- package/src/lib/components/outreach-console-sections.js.map +1 -1
- package/src/lib/components/sequence-editor.js +85 -40
- package/src/lib/components/sequence-editor.js.map +1 -1
- package/src/lib/components/use-outreach-api.d.ts +2 -1
- package/src/lib/components/use-outreach-api.js +5 -3
- package/src/lib/components/use-outreach-api.js.map +1 -1
- package/src/lib/engine/compose.d.ts +1 -1
- package/src/lib/engine/compose.js +8 -4
- package/src/lib/engine/compose.js.map +1 -1
- package/src/lib/engine/enrollment-activity.d.ts +9 -1
- package/src/lib/engine/enrollment-activity.js +15 -2
- package/src/lib/engine/enrollment-activity.js.map +1 -1
- package/src/lib/engine/enrollment-state.d.ts +33 -2
- package/src/lib/engine/enrollment-state.js +66 -7
- package/src/lib/engine/enrollment-state.js.map +1 -1
- package/src/lib/engine/sending-capacity.d.ts +1 -1
- package/src/lib/engine/sending-capacity.js +1 -1
- package/src/lib/engine/sending-capacity.js.map +1 -1
- package/src/lib/engine/sequence-validation.d.ts +20 -3
- package/src/lib/engine/sequence-validation.js +30 -4
- package/src/lib/engine/sequence-validation.js.map +1 -1
- package/src/lib/enrollment/enroll-people.js +6 -4
- package/src/lib/enrollment/enroll-people.js.map +1 -1
- package/src/lib/enrollment/step-overrides.d.ts +4 -2
- package/src/lib/enrollment/step-overrides.js +11 -4
- package/src/lib/enrollment/step-overrides.js.map +1 -1
- package/src/lib/mailboxes/mailbox-api.d.ts +5 -1
- package/src/lib/mailboxes/mailbox-api.js.map +1 -1
- package/src/lib/mailboxes/mailbox-routes.d.ts +5 -2
- package/src/lib/mailboxes/mailbox-routes.js +10 -7
- package/src/lib/mailboxes/mailbox-routes.js.map +1 -1
- package/src/lib/mailboxes/mailbox-settings.d.ts +2 -2
- package/src/lib/mailboxes/mailbox-settings.js +1 -1
- package/src/lib/mailboxes/mailbox-settings.js.map +1 -1
- package/src/lib/model/enrollment-timeline.d.ts +1 -1
- package/src/lib/model/enrollment-timeline.js +22 -3
- package/src/lib/model/enrollment-timeline.js.map +1 -1
- package/src/lib/model/outreach-api.d.ts +19 -1
- package/src/lib/model/outreach-api.js.map +1 -1
- package/src/lib/model/outreach.types.d.ts +25 -0
- package/src/lib/model/outreach.types.js.map +1 -1
- package/src/lib/model/step-preview.d.ts +2 -0
- package/src/lib/model/step-preview.js +11 -7
- package/src/lib/model/step-preview.js.map +1 -1
- package/src/lib/model/stored-records.js +26 -0
- package/src/lib/model/stored-records.js.map +1 -1
- package/src/lib/plugin.js +1 -1
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/routes/enroll-routes.d.ts +6 -0
- package/src/lib/routes/enroll-routes.js +21 -6
- package/src/lib/routes/enroll-routes.js.map +1 -1
- package/src/lib/routes/preview-routes.js +14 -5
- package/src/lib/routes/preview-routes.js.map +1 -1
- package/src/lib/routes/step-test-routes.js +3 -3
- package/src/lib/routes/step-test-routes.js.map +1 -1
- package/src/lib/testing/stand-in-crm-record-routes.d.ts +25 -0
- package/src/lib/testing/stand-in-crm-record-routes.js +46 -0
- package/src/lib/testing/stand-in-crm-record-routes.js.map +1 -0
- package/src/lib/transport/gmail-errors.d.ts +27 -0
- package/src/lib/transport/gmail-errors.js +56 -0
- package/src/lib/transport/gmail-errors.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/plugins-outreach",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.219",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -25,19 +25,19 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aglyn/aglyn": "1.0.0-beta.
|
|
29
|
-
"@aglyn/shared-data-mdi": "1.0.0-beta.
|
|
30
|
-
"@aglyn/shared-ui-email-campaigns": "1.0.0-beta.
|
|
31
|
-
"@aglyn/shared-ui-jsx": "1.0.0-beta.
|
|
32
|
-
"@aglyn/shared-ui-next": "1.0.0-beta.
|
|
33
|
-
"@aglyn/shared-ui-snackstack": "1.0.0-beta.
|
|
34
|
-
"@aglyn/shared-util-email": "1.0.0-beta.
|
|
35
|
-
"@aglyn/shared-util-http": "1.0.0-beta.
|
|
36
|
-
"@aglyn/shared-util-timestamp": "1.0.0-beta.
|
|
37
|
-
"@aglyn/shared-util-tools": "1.0.0-beta.
|
|
38
|
-
"@aglyn/tenant-data-admin": "1.0.0-beta.
|
|
39
|
-
"@aglyn/tenant-feature-instance": "1.0.0-beta.
|
|
40
|
-
"@aglyn/tenant-runtime": "1.0.0-beta.
|
|
28
|
+
"@aglyn/aglyn": "1.0.0-beta.219",
|
|
29
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.219",
|
|
30
|
+
"@aglyn/shared-ui-email-campaigns": "1.0.0-beta.219",
|
|
31
|
+
"@aglyn/shared-ui-jsx": "1.0.0-beta.219",
|
|
32
|
+
"@aglyn/shared-ui-next": "1.0.0-beta.219",
|
|
33
|
+
"@aglyn/shared-ui-snackstack": "1.0.0-beta.219",
|
|
34
|
+
"@aglyn/shared-util-email": "1.0.0-beta.219",
|
|
35
|
+
"@aglyn/shared-util-http": "1.0.0-beta.219",
|
|
36
|
+
"@aglyn/shared-util-timestamp": "1.0.0-beta.219",
|
|
37
|
+
"@aglyn/shared-util-tools": "1.0.0-beta.219",
|
|
38
|
+
"@aglyn/tenant-data-admin": "1.0.0-beta.219",
|
|
39
|
+
"@aglyn/tenant-feature-instance": "1.0.0-beta.219",
|
|
40
|
+
"@aglyn/tenant-runtime": "1.0.0-beta.219",
|
|
41
41
|
"@swc/helpers": "0.5.23"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
@@ -21,7 +21,7 @@ import { pluginDocsHelp } from "@aglyn/aglyn";
|
|
|
21
21
|
import { CardDisplay } from "@aglyn/shared-ui-jsx";
|
|
22
22
|
import { useSnackbar } from "@aglyn/shared-ui-snackstack";
|
|
23
23
|
import { Alert, Autocomplete, Button, Chip, Paper, Stack, TextField, Typography } from "@mui/material";
|
|
24
|
-
import { useEffect, useMemo, useRef, useState } from "react";
|
|
24
|
+
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
25
25
|
import { composeOutreachFooter } from "../engine/compose.js";
|
|
26
26
|
import { outreachComplianceSettingsEqual, outreachCountryLabel, outreachCountryOptions, OUTREACH_BRAND_NAME_MAX, OUTREACH_LEGAL_NAME_MAX, OUTREACH_POSTAL_ADDRESS_MAX, validateOutreachComplianceSettings } from "../model/compliance-settings.js";
|
|
27
27
|
import { OutreachDoNotContactDomainsCard } from "./do-not-contact-domains.js";
|
|
@@ -66,6 +66,49 @@ function pickCard(settings, card) {
|
|
|
66
66
|
function cardEqual(card, a, b) {
|
|
67
67
|
return outreachComplianceSettingsEqual(_extends({}, b, pickCard(a, card)), b);
|
|
68
68
|
}
|
|
69
|
+
const countryName = (option)=>option.name;
|
|
70
|
+
const sameCountry = (option, value)=>option.code === value.code;
|
|
71
|
+
/**
|
|
72
|
+
* The allowed countries, as chips — MEMOIZED, and every prop it takes is
|
|
73
|
+
* stable while the countries are (AGL-3423).
|
|
74
|
+
*
|
|
75
|
+
* A multiple Autocomplete hands its input a new chip array as
|
|
76
|
+
* `startAdornment` on every render, and MUI's `InputBase` copies that into
|
|
77
|
+
* its `FormControl` from a passive effect: a state update left pending after
|
|
78
|
+
* every commit the field takes part in. React 19 counts each such commit as a
|
|
79
|
+
* nested update, and keystrokes delivered back to back commit one after
|
|
80
|
+
* another with nothing to clear the count, so the fifty-first throws
|
|
81
|
+
* "Maximum update depth exceeded" (#185) — the sequence editor's countries
|
|
82
|
+
* field did exactly that. The legal name and address share one form with
|
|
83
|
+
* this list, so each keystroke in them re-rendered it; kept out of those
|
|
84
|
+
* renders, it commits only when it changes.
|
|
85
|
+
*/ const ComplianceCountriesField = /*#__PURE__*/ memo(function ComplianceCountriesField(props) {
|
|
86
|
+
const { onChange } = props;
|
|
87
|
+
return /*#__PURE__*/ _jsx(Autocomplete, {
|
|
88
|
+
multiple: true,
|
|
89
|
+
options: props.options,
|
|
90
|
+
value: props.value,
|
|
91
|
+
getOptionLabel: countryName,
|
|
92
|
+
isOptionEqualToValue: sameCountry,
|
|
93
|
+
onChange: (_event, next)=>onChange(next),
|
|
94
|
+
renderValue: (value, getItemProps)=>value.map((option, index)=>{
|
|
95
|
+
const _getItemProps = getItemProps({
|
|
96
|
+
index
|
|
97
|
+
}), { key } = _getItemProps, item = _object_without_properties_loose(_getItemProps, [
|
|
98
|
+
"key"
|
|
99
|
+
]);
|
|
100
|
+
return /*#__PURE__*/ _jsx(Chip, _extends({
|
|
101
|
+
size: "small",
|
|
102
|
+
label: option.name
|
|
103
|
+
}, item), key);
|
|
104
|
+
}),
|
|
105
|
+
renderInput: (params)=>/*#__PURE__*/ _jsx(TextField, _extends({}, params, {
|
|
106
|
+
label: "Countries",
|
|
107
|
+
error: Boolean(props.issue),
|
|
108
|
+
helperText: props.issue
|
|
109
|
+
}))
|
|
110
|
+
});
|
|
111
|
+
});
|
|
69
112
|
/**
|
|
70
113
|
* Sequences → Compliance (AGL-2980): who every email says sent it, the
|
|
71
114
|
* countries a sequence may send to at all, and — below the settings, with
|
|
@@ -135,6 +178,31 @@ function cardEqual(card, a, b) {
|
|
|
135
178
|
}
|
|
136
179
|
return next;
|
|
137
180
|
});
|
|
181
|
+
// Stable, for the memoized countries field: an edit clears the card's
|
|
182
|
+
// issues and replaces the list in whatever form it lands on.
|
|
183
|
+
const setCountries = useCallback((next)=>{
|
|
184
|
+
setServerIssues((previous)=>{
|
|
185
|
+
const cleared = _extends({}, previous);
|
|
186
|
+
for (const field of CARDS.countries.fields){
|
|
187
|
+
delete cleared[field];
|
|
188
|
+
}
|
|
189
|
+
return cleared;
|
|
190
|
+
});
|
|
191
|
+
setForm((previous)=>_extends({}, previous, {
|
|
192
|
+
allowedCountries: next.map((option)=>option.code)
|
|
193
|
+
}));
|
|
194
|
+
}, []);
|
|
195
|
+
const allowedCountries = form.allowedCountries;
|
|
196
|
+
const selectedCountries = useMemo(()=>allowedCountries.map((code)=>{
|
|
197
|
+
var _countries_find;
|
|
198
|
+
return (_countries_find = countries.find((option)=>option.code === code)) != null ? _countries_find : {
|
|
199
|
+
code,
|
|
200
|
+
name: outreachCountryLabel(code)
|
|
201
|
+
};
|
|
202
|
+
}), [
|
|
203
|
+
allowedCountries,
|
|
204
|
+
countries
|
|
205
|
+
]);
|
|
138
206
|
const discard = (card)=>{
|
|
139
207
|
clearIssues(card);
|
|
140
208
|
if (stored) setForm((previous)=>_extends({}, previous, pickCard(stored, card)));
|
|
@@ -177,13 +245,6 @@ function cardEqual(card, a, b) {
|
|
|
177
245
|
onRetry: loaded.reload
|
|
178
246
|
});
|
|
179
247
|
}
|
|
180
|
-
const selectedCountries = form.allowedCountries.map((code)=>{
|
|
181
|
-
var _countries_find;
|
|
182
|
-
return (_countries_find = countries.find((option)=>option.code === code)) != null ? _countries_find : {
|
|
183
|
-
code,
|
|
184
|
-
name: outreachCountryLabel(code)
|
|
185
|
-
};
|
|
186
|
-
});
|
|
187
248
|
const cardActions = (card)=>{
|
|
188
249
|
const dirty = cardDirty(card);
|
|
189
250
|
return /*#__PURE__*/ _jsxs(Stack, {
|
|
@@ -317,34 +378,11 @@ function cardEqual(card, a, b) {
|
|
|
317
378
|
color: "text.secondary",
|
|
318
379
|
children: 'Sequences may send only to people in these countries, and every sequence only to the ones ' + 'it and this list both allow. The United States is the default: Canada, the United Kingdom and most ' + 'of the European Union require a consent basis that an email to someone who never contacted ' + 'you does not have, so a cold email never goes outside the United States whatever this list ' + 'says. Add another country only for people who came to you first.'
|
|
319
380
|
}),
|
|
320
|
-
/*#__PURE__*/ _jsx(
|
|
321
|
-
multiple: true,
|
|
381
|
+
/*#__PURE__*/ _jsx(ComplianceCountriesField, {
|
|
322
382
|
options: countries,
|
|
323
383
|
value: selectedCountries,
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
onChange: (_event, next)=>{
|
|
327
|
-
clearIssues('countries');
|
|
328
|
-
setForm((previous)=>_extends({}, previous, {
|
|
329
|
-
allowedCountries: next.map((option)=>option.code)
|
|
330
|
-
}));
|
|
331
|
-
},
|
|
332
|
-
renderValue: (value, getItemProps)=>value.map((option, index)=>{
|
|
333
|
-
const _getItemProps = getItemProps({
|
|
334
|
-
index
|
|
335
|
-
}), { key } = _getItemProps, item = _object_without_properties_loose(_getItemProps, [
|
|
336
|
-
"key"
|
|
337
|
-
]);
|
|
338
|
-
return /*#__PURE__*/ _jsx(Chip, _extends({
|
|
339
|
-
size: "small",
|
|
340
|
-
label: option.name
|
|
341
|
-
}, item), key);
|
|
342
|
-
}),
|
|
343
|
-
renderInput: (params)=>/*#__PURE__*/ _jsx(TextField, _extends({}, params, {
|
|
344
|
-
label: "Countries",
|
|
345
|
-
error: Boolean(fieldIssue('allowedCountries')),
|
|
346
|
-
helperText: fieldIssue('allowedCountries')
|
|
347
|
-
}))
|
|
384
|
+
issue: fieldIssue('allowedCountries'),
|
|
385
|
+
onChange: setCountries
|
|
348
386
|
})
|
|
349
387
|
]
|
|
350
388
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/components/compliance-section.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n Autocomplete,\n Button,\n Chip,\n Paper,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport { composeOutreachFooter } from '../engine/compose'\nimport {\n outreachComplianceSettingsEqual,\n outreachCountryLabel,\n outreachCountryOptions,\n OUTREACH_BRAND_NAME_MAX,\n OUTREACH_LEGAL_NAME_MAX,\n OUTREACH_POSTAL_ADDRESS_MAX,\n validateOutreachComplianceSettings,\n type OutreachComplianceField,\n type OutreachCountryOption,\n} from '../model/compliance-settings'\nimport type { OutreachComplianceSettings } from '../model/outreach.types'\nimport { OutreachDoNotContactDomainsCard } from './do-not-contact-domains'\nimport { OutreachLoading, OutreachLoadProblem } from './outreach-ui'\nimport { OutreachRouteError, useOutreachApi } from './use-outreach-api'\nimport { useOutreachComplianceSettings } from './use-outreach-settings'\n\nexport interface OutreachComplianceSectionProps {\n /** The organization the hub is mounted under; `null` until the shell knows it. */\n orgId: string | null\n}\n\n/** What CAN-SPAM accepts as the postal address, as the field says it. */\nexport const OUTREACH_POSTAL_ADDRESS_HELP =\n 'A street address, a post office box registered with the US Postal Service, or a private mailbox ' +\n 'registered with the US Postal Service. Every email prints it in its footer.'\n\nconst EMPTY: OutreachComplianceSettings = {\n legalName: '',\n brandName: '',\n postalAddress: '',\n allowedCountries: ['US'],\n}\n\ntype ComplianceCardKey = 'identity' | 'countries'\n\n/**\n * The page's cards and the settings each one owns. Every card saves only its\n * own fields onto what is stored, so saving the countries never commits a\n * half-edited sender identity, and the other way round.\n */\nconst CARDS: Record<\n ComplianceCardKey,\n { fields: readonly (keyof OutreachComplianceSettings)[]; saved: string }\n> = {\n identity: {\n fields: ['legalName', 'brandName', 'postalAddress'],\n saved: 'Sender identity saved.',\n },\n countries: {\n fields: ['allowedCountries'],\n saved: 'Allowed countries saved.',\n },\n}\n\nfunction pickCard(\n settings: OutreachComplianceSettings,\n card: ComplianceCardKey,\n): Partial<OutreachComplianceSettings> {\n return Object.fromEntries(\n CARDS[card].fields.map((field) => [field, settings[field]]),\n )\n}\n\nfunction cardEqual(\n card: ComplianceCardKey,\n a: OutreachComplianceSettings,\n b: OutreachComplianceSettings,\n): boolean {\n return outreachComplianceSettingsEqual({ ...b, ...pickCard(a, card) }, b)\n}\n\n/**\n * Sequences → Compliance (AGL-2980): who every email says sent it, the\n * countries a sequence may send to at all, and — below the settings, with\n * no Save of its own — the domains no sequence emails (AGL-3244). Each\n * settings card saves its own fields from its header, the console's place\n * for a card's actions (AGL-3333).\n *\n * The footer is previewed from what is typed, by the engine's own\n * `composeOutreachFooter`, so the page shows the lines every email will end\n * with — or, while the legal name or the address is empty, the sentence\n * that explains why nothing can be sent and no sequence activated.\n */\nexport function OutreachComplianceSection(\n props: OutreachComplianceSectionProps,\n) {\n const { orgId } = props\n const api = useOutreachApi(orgId)\n const loaded = useOutreachComplianceSettings(api, orgId)\n const { enqueueSnackbar } = useSnackbar()\n const [form, setForm] = useState<OutreachComplianceSettings>(EMPTY)\n const [saving, setSaving] = useState<ComplianceCardKey | null>(null)\n const [serverIssues, setServerIssues] = useState<\n Partial<Record<OutreachComplianceField, string>>\n >({})\n const countries = useMemo(() => outreachCountryOptions(), [])\n\n const stored = loaded.settings\n const previousStored = useRef<OutreachComplianceSettings | null>(null)\n useEffect(() => {\n if (!stored) return\n const before = previousStored.current\n previousStored.current = stored\n // A fresh read replaces every card the member has not touched; a card\n // with unsaved edits keeps them, so saving one card never wipes another.\n setForm((current) => {\n if (!before) return { ...stored }\n let next: OutreachComplianceSettings = { ...stored }\n for (const card of Object.keys(CARDS) as ComplianceCardKey[]) {\n if (!cardEqual(card, current, before)) {\n next = { ...next, ...pickCard(current, card) }\n }\n }\n return next\n })\n }, [stored])\n\n const { settings: normalized, issues } =\n validateOutreachComplianceSettings(form)\n const fieldIssue = (field: OutreachComplianceField) =>\n serverIssues[field] ??\n issues.find((issue) => issue.field === field)?.message\n /** What saving one card would store: its own fields over the stored rest. */\n const candidate = (card: ComplianceCardKey) =>\n validateOutreachComplianceSettings(\n stored ? { ...stored, ...pickCard(form, card) } : form,\n )\n const cardDirty = (card: ComplianceCardKey) =>\n stored\n ? !outreachComplianceSettingsEqual(stored, candidate(card).settings)\n : false\n const cardBlocked = (card: ComplianceCardKey) =>\n candidate(card).issues.some((issue) =>\n (CARDS[card].fields as readonly string[]).includes(issue.field),\n )\n const footer = composeOutreachFooter(normalized)\n const set = (field: keyof OutreachComplianceSettings) => (value: string) => {\n setServerIssues((previous) => {\n const next = { ...previous }\n delete next[field as OutreachComplianceField]\n return next\n })\n setForm((previous) => ({ ...previous, [field]: value }))\n }\n\n const clearIssues = (card: ComplianceCardKey) =>\n setServerIssues((previous) => {\n const next = { ...previous }\n for (const field of CARDS[card].fields) {\n delete next[field as OutreachComplianceField]\n }\n return next\n })\n\n const discard = (card: ComplianceCardKey) => {\n clearIssues(card)\n if (stored)\n setForm((previous) => ({ ...previous, ...pickCard(stored, card) }))\n }\n\n const save = async (card: ComplianceCardKey) => {\n setSaving(card)\n try {\n const answer = await api.saveSettings(candidate(card).settings)\n clearIssues(card)\n setForm((previous) => ({\n ...previous,\n ...pickCard(answer.settings, card),\n }))\n loaded.reload()\n enqueueSnackbar(answer.changed ? CARDS[card].saved : 'Nothing changed.', {\n variant: answer.changed ? 'success' : 'info',\n })\n } catch (error) {\n if (error instanceof OutreachRouteError && error.issues.length) {\n setServerIssues((previous) => ({\n ...previous,\n ...Object.fromEntries(\n error.issues\n .filter(\n (\n issue,\n ): issue is {\n field: OutreachComplianceField\n message: string\n } => 'field' in issue,\n )\n .map((issue) => [issue.field, issue.message]),\n ),\n }))\n }\n enqueueSnackbar((error as Error).message, {\n variant: 'error',\n allowDuplicate: true,\n })\n } finally {\n setSaving(null)\n }\n }\n\n if (loaded.status === 'loading' || (!orgId && !stored)) {\n return <OutreachLoading label=\"Loading compliance settings…\" />\n }\n if (loaded.status === 'error' || loaded.status === 'refused') {\n return (\n <OutreachLoadProblem\n status={loaded.status}\n what=\"compliance settings\"\n message={loaded.message}\n onRetry={loaded.reload}\n />\n )\n }\n\n const selectedCountries = form.allowedCountries.map(\n (code) =>\n countries.find((option) => option.code === code) ?? {\n code,\n name: outreachCountryLabel(code),\n },\n )\n\n const cardActions = (card: ComplianceCardKey) => {\n const dirty = cardDirty(card)\n return (\n <Stack direction=\"row\" spacing={1}>\n <Button\n size=\"small\"\n variant=\"text\"\n disabled={!dirty || saving !== null}\n onClick={() => discard(card)}\n >\n Discard changes\n </Button>\n <Button\n size=\"small\"\n variant=\"contained\"\n disabled={!dirty || saving !== null || cardBlocked(card)}\n onClick={() => void save(card)}\n >\n {saving === card ? 'Saving…' : 'Save'}\n </Button>\n </Stack>\n )\n }\n\n return (\n <Stack spacing={2}>\n <CardDisplay\n header=\"Sender identity\"\n help={pluginDocsHelp('sequences', { anchor: '#compliance-settings' })}\n HeaderProps={{ action: cardActions('identity') }}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Every email a sequence sends ends with a footer naming your organization, its postal ' +\n 'address, that the email is a sales email, and how to stop more of them. The law ' +\n 'requires these on commercial email, so every email gets the footer automatically.'}\n </Typography>\n <TextField\n label=\"Legal name\"\n value={form.legalName}\n onChange={(event) => set('legalName')(event.target.value)}\n error={Boolean(fieldIssue('legalName'))}\n helperText={\n fieldIssue('legalName') ??\n 'Your organization’s legal name, as the footer prints it.'\n }\n slotProps={{\n htmlInput: { maxLength: OUTREACH_LEGAL_NAME_MAX + 20 },\n }}\n fullWidth\n />\n <TextField\n label=\"Brand name\"\n value={form.brandName}\n onChange={(event) => set('brandName')(event.target.value)}\n error={Boolean(fieldIssue('brandName'))}\n helperText={\n fieldIssue('brandName') ??\n 'The name the solicitation sentence uses. Leave empty to use the legal name.'\n }\n slotProps={{\n htmlInput: { maxLength: OUTREACH_BRAND_NAME_MAX + 20 },\n }}\n fullWidth\n />\n <TextField\n label=\"Postal address\"\n value={form.postalAddress}\n onChange={(event) => set('postalAddress')(event.target.value)}\n error={Boolean(fieldIssue('postalAddress'))}\n helperText={\n fieldIssue('postalAddress') ?? OUTREACH_POSTAL_ADDRESS_HELP\n }\n slotProps={{\n htmlInput: { maxLength: OUTREACH_POSTAL_ADDRESS_MAX + 40 },\n }}\n multiline\n minRows={3}\n fullWidth\n />\n {footer.footer ? (\n <Stack spacing={0.5}>\n <Typography variant=\"subtitle2\">Every email ends with</Typography>\n <Paper variant=\"outlined\" sx={{ p: 1.5 }}>\n <Typography\n variant=\"body2\"\n sx={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}\n data-testid=\"outreach-footer-preview\"\n >\n {footer.footer}\n </Typography>\n </Paper>\n </Stack>\n ) : (\n <Alert severity=\"warning\">\n {'No sequence can be activated, and no email sent, until you add your organization’s legal ' +\n 'name and postal address.'}\n </Alert>\n )}\n </Stack>\n </CardDisplay>\n\n <CardDisplay\n header=\"Allowed countries\"\n help={pluginDocsHelp('sequences', { anchor: '#allowed-countries' })}\n HeaderProps={{ action: cardActions('countries') }}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Sequences may send only to people in these countries, and every sequence only to the ones ' +\n 'it and this list both allow. The United States is the default: Canada, the United Kingdom and most ' +\n 'of the European Union require a consent basis that an email to someone who never contacted ' +\n 'you does not have, so a cold email never goes outside the United States whatever this list ' +\n 'says. Add another country only for people who came to you first.'}\n </Typography>\n <Autocomplete<OutreachCountryOption, true>\n multiple\n options={countries}\n value={selectedCountries}\n getOptionLabel={(option) => option.name}\n isOptionEqualToValue={(option, value) => option.code === value.code}\n onChange={(_event, next) => {\n clearIssues('countries')\n setForm((previous) => ({\n ...previous,\n allowedCountries: next.map((option) => option.code),\n }))\n }}\n renderValue={(value, getItemProps) =>\n value.map((option, index) => {\n const { key, ...item } = getItemProps({ index })\n return (\n <Chip key={key} size=\"small\" label={option.name} {...item} />\n )\n })\n }\n renderInput={(params) => (\n <TextField\n {...params}\n label=\"Countries\"\n error={Boolean(fieldIssue('allowedCountries'))}\n helperText={fieldIssue('allowedCountries')}\n />\n )}\n />\n </Stack>\n </CardDisplay>\n\n {/* Its own list, saved as it is edited (AGL-3244): it has no Save. */}\n <OutreachDoNotContactDomainsCard orgId={orgId} />\n </Stack>\n )\n}\nOutreachComplianceSection.displayName = 'OutreachComplianceSection'\n\nexport default OutreachComplianceSection\n"],"names":["pluginDocsHelp","CardDisplay","useSnackbar","Alert","Autocomplete","Button","Chip","Paper","Stack","TextField","Typography","useEffect","useMemo","useRef","useState","composeOutreachFooter","outreachComplianceSettingsEqual","outreachCountryLabel","outreachCountryOptions","OUTREACH_BRAND_NAME_MAX","OUTREACH_LEGAL_NAME_MAX","OUTREACH_POSTAL_ADDRESS_MAX","validateOutreachComplianceSettings","OutreachDoNotContactDomainsCard","OutreachLoading","OutreachLoadProblem","OutreachRouteError","useOutreachApi","useOutreachComplianceSettings","OUTREACH_POSTAL_ADDRESS_HELP","EMPTY","legalName","brandName","postalAddress","allowedCountries","CARDS","identity","fields","saved","countries","pickCard","settings","card","Object","fromEntries","map","field","cardEqual","a","b","OutreachComplianceSection","props","fieldIssue","orgId","api","loaded","enqueueSnackbar","form","setForm","saving","setSaving","serverIssues","setServerIssues","stored","previousStored","before","current","next","keys","normalized","issues","find","issue","message","candidate","cardDirty","cardBlocked","some","includes","footer","set","value","previous","clearIssues","discard","save","answer","saveSettings","reload","changed","variant","error","length","filter","allowDuplicate","status","label","what","onRetry","selectedCountries","code","option","name","cardActions","dirty","direction","spacing","size","disabled","onClick","header","help","anchor","HeaderProps","action","contentGutterX","contentGutterY","color","onChange","event","target","Boolean","helperText","slotProps","htmlInput","maxLength","fullWidth","multiline","minRows","sx","p","whiteSpace","wordBreak","data-testid","severity","multiple","options","getOptionLabel","isOptionEqualToValue","_event","renderValue","getItemProps","index","key","item","renderInput","params","displayName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/components/compliance-section.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n Autocomplete,\n Button,\n Chip,\n Paper,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { composeOutreachFooter } from '../engine/compose'\nimport {\n outreachComplianceSettingsEqual,\n outreachCountryLabel,\n outreachCountryOptions,\n OUTREACH_BRAND_NAME_MAX,\n OUTREACH_LEGAL_NAME_MAX,\n OUTREACH_POSTAL_ADDRESS_MAX,\n validateOutreachComplianceSettings,\n type OutreachComplianceField,\n type OutreachCountryOption,\n} from '../model/compliance-settings'\nimport type { OutreachComplianceSettings } from '../model/outreach.types'\nimport { OutreachDoNotContactDomainsCard } from './do-not-contact-domains'\nimport { OutreachLoading, OutreachLoadProblem } from './outreach-ui'\nimport { OutreachRouteError, useOutreachApi } from './use-outreach-api'\nimport { useOutreachComplianceSettings } from './use-outreach-settings'\n\nexport interface OutreachComplianceSectionProps {\n /** The organization the hub is mounted under; `null` until the shell knows it. */\n orgId: string | null\n}\n\n/** What CAN-SPAM accepts as the postal address, as the field says it. */\nexport const OUTREACH_POSTAL_ADDRESS_HELP =\n 'A street address, a post office box registered with the US Postal Service, or a private mailbox ' +\n 'registered with the US Postal Service. Every email prints it in its footer.'\n\nconst EMPTY: OutreachComplianceSettings = {\n legalName: '',\n brandName: '',\n postalAddress: '',\n allowedCountries: ['US'],\n}\n\ntype ComplianceCardKey = 'identity' | 'countries'\n\n/**\n * The page's cards and the settings each one owns. Every card saves only its\n * own fields onto what is stored, so saving the countries never commits a\n * half-edited sender identity, and the other way round.\n */\nconst CARDS: Record<\n ComplianceCardKey,\n { fields: readonly (keyof OutreachComplianceSettings)[]; saved: string }\n> = {\n identity: {\n fields: ['legalName', 'brandName', 'postalAddress'],\n saved: 'Sender identity saved.',\n },\n countries: {\n fields: ['allowedCountries'],\n saved: 'Allowed countries saved.',\n },\n}\n\nfunction pickCard(\n settings: OutreachComplianceSettings,\n card: ComplianceCardKey,\n): Partial<OutreachComplianceSettings> {\n return Object.fromEntries(\n CARDS[card].fields.map((field) => [field, settings[field]]),\n )\n}\n\nfunction cardEqual(\n card: ComplianceCardKey,\n a: OutreachComplianceSettings,\n b: OutreachComplianceSettings,\n): boolean {\n return outreachComplianceSettingsEqual({ ...b, ...pickCard(a, card) }, b)\n}\n\nconst countryName = (option: OutreachCountryOption): string => option.name\nconst sameCountry = (\n option: OutreachCountryOption,\n value: OutreachCountryOption,\n): boolean => option.code === value.code\n\n/**\n * The allowed countries, as chips — MEMOIZED, and every prop it takes is\n * stable while the countries are (AGL-3423).\n *\n * A multiple Autocomplete hands its input a new chip array as\n * `startAdornment` on every render, and MUI's `InputBase` copies that into\n * its `FormControl` from a passive effect: a state update left pending after\n * every commit the field takes part in. React 19 counts each such commit as a\n * nested update, and keystrokes delivered back to back commit one after\n * another with nothing to clear the count, so the fifty-first throws\n * \"Maximum update depth exceeded\" (#185) — the sequence editor's countries\n * field did exactly that. The legal name and address share one form with\n * this list, so each keystroke in them re-rendered it; kept out of those\n * renders, it commits only when it changes.\n */\nconst ComplianceCountriesField = memo(function ComplianceCountriesField(props: {\n options: readonly OutreachCountryOption[]\n value: readonly OutreachCountryOption[]\n issue?: string\n onChange(next: OutreachCountryOption[]): void\n}) {\n const { onChange } = props\n return (\n <Autocomplete<OutreachCountryOption, true>\n multiple\n options={props.options}\n value={props.value as OutreachCountryOption[]}\n getOptionLabel={countryName}\n isOptionEqualToValue={sameCountry}\n onChange={(_event, next) => onChange(next)}\n renderValue={(value, getItemProps) =>\n value.map((option, index) => {\n const { key, ...item } = getItemProps({ index })\n return <Chip key={key} size=\"small\" label={option.name} {...item} />\n })\n }\n renderInput={(params) => (\n <TextField\n {...params}\n label=\"Countries\"\n error={Boolean(props.issue)}\n helperText={props.issue}\n />\n )}\n />\n )\n})\n\n/**\n * Sequences → Compliance (AGL-2980): who every email says sent it, the\n * countries a sequence may send to at all, and — below the settings, with\n * no Save of its own — the domains no sequence emails (AGL-3244). Each\n * settings card saves its own fields from its header, the console's place\n * for a card's actions (AGL-3333).\n *\n * The footer is previewed from what is typed, by the engine's own\n * `composeOutreachFooter`, so the page shows the lines every email will end\n * with — or, while the legal name or the address is empty, the sentence\n * that explains why nothing can be sent and no sequence activated.\n */\nexport function OutreachComplianceSection(\n props: OutreachComplianceSectionProps,\n) {\n const { orgId } = props\n const api = useOutreachApi(orgId)\n const loaded = useOutreachComplianceSettings(api, orgId)\n const { enqueueSnackbar } = useSnackbar()\n const [form, setForm] = useState<OutreachComplianceSettings>(EMPTY)\n const [saving, setSaving] = useState<ComplianceCardKey | null>(null)\n const [serverIssues, setServerIssues] = useState<\n Partial<Record<OutreachComplianceField, string>>\n >({})\n const countries = useMemo(() => outreachCountryOptions(), [])\n\n const stored = loaded.settings\n const previousStored = useRef<OutreachComplianceSettings | null>(null)\n useEffect(() => {\n if (!stored) return\n const before = previousStored.current\n previousStored.current = stored\n // A fresh read replaces every card the member has not touched; a card\n // with unsaved edits keeps them, so saving one card never wipes another.\n setForm((current) => {\n if (!before) return { ...stored }\n let next: OutreachComplianceSettings = { ...stored }\n for (const card of Object.keys(CARDS) as ComplianceCardKey[]) {\n if (!cardEqual(card, current, before)) {\n next = { ...next, ...pickCard(current, card) }\n }\n }\n return next\n })\n }, [stored])\n\n const { settings: normalized, issues } =\n validateOutreachComplianceSettings(form)\n const fieldIssue = (field: OutreachComplianceField) =>\n serverIssues[field] ??\n issues.find((issue) => issue.field === field)?.message\n /** What saving one card would store: its own fields over the stored rest. */\n const candidate = (card: ComplianceCardKey) =>\n validateOutreachComplianceSettings(\n stored ? { ...stored, ...pickCard(form, card) } : form,\n )\n const cardDirty = (card: ComplianceCardKey) =>\n stored\n ? !outreachComplianceSettingsEqual(stored, candidate(card).settings)\n : false\n const cardBlocked = (card: ComplianceCardKey) =>\n candidate(card).issues.some((issue) =>\n (CARDS[card].fields as readonly string[]).includes(issue.field),\n )\n const footer = composeOutreachFooter(normalized)\n const set = (field: keyof OutreachComplianceSettings) => (value: string) => {\n setServerIssues((previous) => {\n const next = { ...previous }\n delete next[field as OutreachComplianceField]\n return next\n })\n setForm((previous) => ({ ...previous, [field]: value }))\n }\n\n const clearIssues = (card: ComplianceCardKey) =>\n setServerIssues((previous) => {\n const next = { ...previous }\n for (const field of CARDS[card].fields) {\n delete next[field as OutreachComplianceField]\n }\n return next\n })\n\n // Stable, for the memoized countries field: an edit clears the card's\n // issues and replaces the list in whatever form it lands on.\n const setCountries = useCallback((next: OutreachCountryOption[]) => {\n setServerIssues((previous) => {\n const cleared = { ...previous }\n for (const field of CARDS.countries.fields) {\n delete cleared[field as OutreachComplianceField]\n }\n return cleared\n })\n setForm((previous) => ({\n ...previous,\n allowedCountries: next.map((option) => option.code),\n }))\n }, [])\n const allowedCountries = form.allowedCountries\n const selectedCountries = useMemo(\n () =>\n allowedCountries.map(\n (code) =>\n countries.find((option) => option.code === code) ?? {\n code,\n name: outreachCountryLabel(code),\n },\n ),\n [allowedCountries, countries],\n )\n\n const discard = (card: ComplianceCardKey) => {\n clearIssues(card)\n if (stored)\n setForm((previous) => ({ ...previous, ...pickCard(stored, card) }))\n }\n\n const save = async (card: ComplianceCardKey) => {\n setSaving(card)\n try {\n const answer = await api.saveSettings(candidate(card).settings)\n clearIssues(card)\n setForm((previous) => ({\n ...previous,\n ...pickCard(answer.settings, card),\n }))\n loaded.reload()\n enqueueSnackbar(answer.changed ? CARDS[card].saved : 'Nothing changed.', {\n variant: answer.changed ? 'success' : 'info',\n })\n } catch (error) {\n if (error instanceof OutreachRouteError && error.issues.length) {\n setServerIssues((previous) => ({\n ...previous,\n ...Object.fromEntries(\n error.issues\n .filter(\n (\n issue,\n ): issue is {\n field: OutreachComplianceField\n message: string\n } => 'field' in issue,\n )\n .map((issue) => [issue.field, issue.message]),\n ),\n }))\n }\n enqueueSnackbar((error as Error).message, {\n variant: 'error',\n allowDuplicate: true,\n })\n } finally {\n setSaving(null)\n }\n }\n\n if (loaded.status === 'loading' || (!orgId && !stored)) {\n return <OutreachLoading label=\"Loading compliance settings…\" />\n }\n if (loaded.status === 'error' || loaded.status === 'refused') {\n return (\n <OutreachLoadProblem\n status={loaded.status}\n what=\"compliance settings\"\n message={loaded.message}\n onRetry={loaded.reload}\n />\n )\n }\n\n const cardActions = (card: ComplianceCardKey) => {\n const dirty = cardDirty(card)\n return (\n <Stack direction=\"row\" spacing={1}>\n <Button\n size=\"small\"\n variant=\"text\"\n disabled={!dirty || saving !== null}\n onClick={() => discard(card)}\n >\n Discard changes\n </Button>\n <Button\n size=\"small\"\n variant=\"contained\"\n disabled={!dirty || saving !== null || cardBlocked(card)}\n onClick={() => void save(card)}\n >\n {saving === card ? 'Saving…' : 'Save'}\n </Button>\n </Stack>\n )\n }\n\n return (\n <Stack spacing={2}>\n <CardDisplay\n header=\"Sender identity\"\n help={pluginDocsHelp('sequences', { anchor: '#compliance-settings' })}\n HeaderProps={{ action: cardActions('identity') }}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Every email a sequence sends ends with a footer naming your organization, its postal ' +\n 'address, that the email is a sales email, and how to stop more of them. The law ' +\n 'requires these on commercial email, so every email gets the footer automatically.'}\n </Typography>\n <TextField\n label=\"Legal name\"\n value={form.legalName}\n onChange={(event) => set('legalName')(event.target.value)}\n error={Boolean(fieldIssue('legalName'))}\n helperText={\n fieldIssue('legalName') ??\n 'Your organization’s legal name, as the footer prints it.'\n }\n slotProps={{\n htmlInput: { maxLength: OUTREACH_LEGAL_NAME_MAX + 20 },\n }}\n fullWidth\n />\n <TextField\n label=\"Brand name\"\n value={form.brandName}\n onChange={(event) => set('brandName')(event.target.value)}\n error={Boolean(fieldIssue('brandName'))}\n helperText={\n fieldIssue('brandName') ??\n 'The name the solicitation sentence uses. Leave empty to use the legal name.'\n }\n slotProps={{\n htmlInput: { maxLength: OUTREACH_BRAND_NAME_MAX + 20 },\n }}\n fullWidth\n />\n <TextField\n label=\"Postal address\"\n value={form.postalAddress}\n onChange={(event) => set('postalAddress')(event.target.value)}\n error={Boolean(fieldIssue('postalAddress'))}\n helperText={\n fieldIssue('postalAddress') ?? OUTREACH_POSTAL_ADDRESS_HELP\n }\n slotProps={{\n htmlInput: { maxLength: OUTREACH_POSTAL_ADDRESS_MAX + 40 },\n }}\n multiline\n minRows={3}\n fullWidth\n />\n {footer.footer ? (\n <Stack spacing={0.5}>\n <Typography variant=\"subtitle2\">Every email ends with</Typography>\n <Paper variant=\"outlined\" sx={{ p: 1.5 }}>\n <Typography\n variant=\"body2\"\n sx={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}\n data-testid=\"outreach-footer-preview\"\n >\n {footer.footer}\n </Typography>\n </Paper>\n </Stack>\n ) : (\n <Alert severity=\"warning\">\n {'No sequence can be activated, and no email sent, until you add your organization’s legal ' +\n 'name and postal address.'}\n </Alert>\n )}\n </Stack>\n </CardDisplay>\n\n <CardDisplay\n header=\"Allowed countries\"\n help={pluginDocsHelp('sequences', { anchor: '#allowed-countries' })}\n HeaderProps={{ action: cardActions('countries') }}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Sequences may send only to people in these countries, and every sequence only to the ones ' +\n 'it and this list both allow. The United States is the default: Canada, the United Kingdom and most ' +\n 'of the European Union require a consent basis that an email to someone who never contacted ' +\n 'you does not have, so a cold email never goes outside the United States whatever this list ' +\n 'says. Add another country only for people who came to you first.'}\n </Typography>\n <ComplianceCountriesField\n options={countries}\n value={selectedCountries}\n issue={fieldIssue('allowedCountries')}\n onChange={setCountries}\n />\n </Stack>\n </CardDisplay>\n\n {/* Its own list, saved as it is edited (AGL-3244): it has no Save. */}\n <OutreachDoNotContactDomainsCard orgId={orgId} />\n </Stack>\n )\n}\nOutreachComplianceSection.displayName = 'OutreachComplianceSection'\n\nexport default OutreachComplianceSection\n"],"names":["pluginDocsHelp","CardDisplay","useSnackbar","Alert","Autocomplete","Button","Chip","Paper","Stack","TextField","Typography","memo","useCallback","useEffect","useMemo","useRef","useState","composeOutreachFooter","outreachComplianceSettingsEqual","outreachCountryLabel","outreachCountryOptions","OUTREACH_BRAND_NAME_MAX","OUTREACH_LEGAL_NAME_MAX","OUTREACH_POSTAL_ADDRESS_MAX","validateOutreachComplianceSettings","OutreachDoNotContactDomainsCard","OutreachLoading","OutreachLoadProblem","OutreachRouteError","useOutreachApi","useOutreachComplianceSettings","OUTREACH_POSTAL_ADDRESS_HELP","EMPTY","legalName","brandName","postalAddress","allowedCountries","CARDS","identity","fields","saved","countries","pickCard","settings","card","Object","fromEntries","map","field","cardEqual","a","b","countryName","option","name","sameCountry","value","code","ComplianceCountriesField","props","onChange","multiple","options","getOptionLabel","isOptionEqualToValue","_event","next","renderValue","getItemProps","index","key","item","size","label","renderInput","params","error","Boolean","issue","helperText","OutreachComplianceSection","fieldIssue","orgId","api","loaded","enqueueSnackbar","form","setForm","saving","setSaving","serverIssues","setServerIssues","stored","previousStored","before","current","keys","normalized","issues","find","message","candidate","cardDirty","cardBlocked","some","includes","footer","set","previous","clearIssues","setCountries","cleared","selectedCountries","discard","save","answer","saveSettings","reload","changed","variant","length","filter","allowDuplicate","status","what","onRetry","cardActions","dirty","direction","spacing","disabled","onClick","header","help","anchor","HeaderProps","action","contentGutterX","contentGutterY","color","event","target","slotProps","htmlInput","maxLength","fullWidth","multiline","minRows","sx","p","whiteSpace","wordBreak","data-testid","severity","displayName"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type OutreachEnrollGateway, type OutreachEnrollPreviewPerson } from '../model/outreach-api';
|
|
2
|
-
import { type OutreachAttestationKind, type OutreachSequence } from '../model/outreach.types';
|
|
2
|
+
import { type OutreachAttestationKind, type OutreachSequence, type OutreachSequenceStep } from '../model/outreach.types';
|
|
3
3
|
import { type OutreachCuratedDraftState } from './curated-step-editor';
|
|
4
4
|
import type { OutreachApi } from './use-outreach-api';
|
|
5
5
|
export interface OutreachEnrollDialogProps {
|
|
@@ -38,8 +38,18 @@ interface Supplied {
|
|
|
38
38
|
drafts?: Record<number, OutreachCuratedDraftState>;
|
|
39
39
|
curation?: Curation;
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* The steps the dialog offers to begin at (AGL-3228), each by its place in
|
|
43
|
+
* the sequence: "Email 2 · step 3", "Step 2 · LinkedIn".
|
|
44
|
+
*/
|
|
45
|
+
export declare function outreachStartStepOptions(steps: readonly OutreachSequenceStep[]): Array<{
|
|
46
|
+
value: number;
|
|
47
|
+
label: string;
|
|
48
|
+
}>;
|
|
49
|
+
/** What beginning at a later step does, as the dialog says it under the picker. */
|
|
50
|
+
export declare function outreachStartStepHelp(startStepIndex: number): string;
|
|
41
51
|
/** Whether a person may be sent to Confirm as the rep has left them. */
|
|
42
|
-
export declare function outreachPersonReady(person: OutreachEnrollPreviewPerson, supplied: Supplied | undefined): boolean;
|
|
52
|
+
export declare function outreachPersonReady(person: OutreachEnrollPreviewPerson, supplied: Supplied | undefined, startStepIndex?: number): boolean;
|
|
43
53
|
/**
|
|
44
54
|
* Enroll people in a sequence (AGL-2980).
|
|
45
55
|
*
|
|
@@ -23,6 +23,7 @@ import { useMemo, useState } from "react";
|
|
|
23
23
|
import { isOutreachGatewayFronted, outreachGatewayChip } from "../engine/mail-gateway.js";
|
|
24
24
|
import { OUTREACH_ENROLL_BATCH_MAX } from "../model/outreach-api.js";
|
|
25
25
|
import { OUTREACH_ATTESTATION_LABELS, OUTREACH_PERSONAL_LINE_MAX } from "../model/outreach.types.js";
|
|
26
|
+
import { outreachStepName } from "../model/enrollment-timeline.js";
|
|
26
27
|
import { outreachEmailStepLabel } from "./curate-step-dialog.js";
|
|
27
28
|
import { OutreachCuratedStepEditor, outreachDraftState, outreachDraftToOverride, outreachMemberDraftStates } from "./curated-step-editor.js";
|
|
28
29
|
import { OutreachLoading, OutreachLoadProblem } from "./outreach-ui.js";
|
|
@@ -51,21 +52,40 @@ const STATUS_COLORS = {
|
|
|
51
52
|
var _ref;
|
|
52
53
|
return ((_ref = gateway == null ? void 0 : gateway.blocked7) != null ? _ref : 0) > 0;
|
|
53
54
|
}
|
|
54
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* The drafts for the steps that will run: none before the step the
|
|
57
|
+
* enrollment begins at (AGL-3228), which is skipped and never sent.
|
|
58
|
+
*/ function liveDrafts(supplied, startStepIndex) {
|
|
55
59
|
var _ref;
|
|
56
|
-
return Object.values((_ref = supplied == null ? void 0 : supplied.drafts) != null ? _ref : {}).
|
|
60
|
+
return Object.values((_ref = supplied == null ? void 0 : supplied.drafts) != null ? _ref : {}).filter((draft)=>draft.stepIndex >= startStepIndex);
|
|
57
61
|
}
|
|
58
|
-
/**
|
|
59
|
-
|
|
60
|
-
return Object.values((_ref = supplied == null ? void 0 : supplied.drafts) != null ? _ref : {}).some((draft)=>draft.decision === 'pending');
|
|
62
|
+
/** The drafts the rep used, as the enroll and the preview carry them. */ function usedOverrides(supplied, startStepIndex) {
|
|
63
|
+
return liveDrafts(supplied, startStepIndex).map(outreachDraftToOverride).filter((override)=>override !== null);
|
|
61
64
|
}
|
|
62
|
-
/** Whether a
|
|
65
|
+
/** Whether a drafted step is still waiting on the rep to use it or keep the template. */ function hasOpenDrafts(supplied, startStepIndex) {
|
|
66
|
+
return liveDrafts(supplied, startStepIndex).some((draft)=>draft.decision === 'pending');
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The steps the dialog offers to begin at (AGL-3228), each by its place in
|
|
70
|
+
* the sequence: "Email 2 · step 3", "Step 2 · LinkedIn".
|
|
71
|
+
*/ export function outreachStartStepOptions(steps) {
|
|
72
|
+
return steps.map((_step, index)=>({
|
|
73
|
+
value: index,
|
|
74
|
+
label: index === 0 ? `${outreachStepName(steps, 0)} — from the start` : outreachStepName(steps, index)
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
/** What beginning at a later step does, as the dialog says it under the picker. */ export function outreachStartStepHelp(startStepIndex) {
|
|
78
|
+
if (startStepIndex < 1) return 'Everyone starts at step 1, after its delay.';
|
|
79
|
+
const skipped = startStepIndex === 1 ? 'Step 1 is' : `Steps 1–${startStepIndex} are`;
|
|
80
|
+
return `For people who already had the earlier steps by hand. ${skipped} marked skipped and never sent; step ${startStepIndex + 1} goes out after its own delay, counted from now, and starts the email thread.`;
|
|
81
|
+
}
|
|
82
|
+
/** Whether a person may be sent to Confirm as the rep has left them. */ export function outreachPersonReady(person, supplied, startStepIndex = 0) {
|
|
63
83
|
if (person.status === 'blocked' || !(supplied == null ? void 0 : supplied.include)) return false;
|
|
64
84
|
const line = supplied.personalLine.trim();
|
|
65
85
|
if (line.length > OUTREACH_PERSONAL_LINE_MAX) return false;
|
|
66
86
|
// A draft the rep has read but neither used nor kept holds them back
|
|
67
87
|
// (AGL-3324): nothing is enrolled with a decision still open.
|
|
68
|
-
if (hasOpenDrafts(supplied)) return false;
|
|
88
|
+
if (hasOpenDrafts(supplied, startStepIndex)) return false;
|
|
69
89
|
if (person.status === 'eligible') return true;
|
|
70
90
|
if (person.requires.personalLine && !line) return false;
|
|
71
91
|
return person.requires.attestations.every((kind)=>supplied.attestations.includes(kind));
|
|
@@ -98,6 +118,11 @@ const STATUS_COLORS = {
|
|
|
98
118
|
const [supplied, setSupplied] = useState({});
|
|
99
119
|
const [emailPreview, setEmailPreview] = useState({});
|
|
100
120
|
const [stepTests, setStepTests] = useState({});
|
|
121
|
+
// The step everyone in this enrollment begins at (AGL-3228), zero-based.
|
|
122
|
+
const [startStepIndex, setStartStepIndex] = useState(0);
|
|
123
|
+
const startOptions = useMemo(()=>outreachStartStepOptions(sequence.steps), [
|
|
124
|
+
sequence.steps
|
|
125
|
+
]);
|
|
101
126
|
const views = useOutreachSavedViews(props.open ? orgId : null, props.uid);
|
|
102
127
|
const search = useOutreachContactSearch({
|
|
103
128
|
orgId: props.open && tab === 'search' ? orgId : null,
|
|
@@ -120,6 +145,7 @@ const STATUS_COLORS = {
|
|
|
120
145
|
setSupplied({});
|
|
121
146
|
setEmailPreview({});
|
|
122
147
|
setStepTests({});
|
|
148
|
+
setStartStepIndex(0);
|
|
123
149
|
};
|
|
124
150
|
const close = ()=>{
|
|
125
151
|
reset();
|
|
@@ -159,13 +185,14 @@ const STATUS_COLORS = {
|
|
|
159
185
|
}
|
|
160
186
|
};
|
|
161
187
|
const answer = stage.kind === 'preview' || stage.kind === 'enrolling' ? stage.answer : null;
|
|
162
|
-
const ready = useMemo(()=>answer ? answer.people.filter((person)=>outreachPersonReady(person, supplied[person.personId])) : [], [
|
|
188
|
+
const ready = useMemo(()=>answer ? answer.people.filter((person)=>outreachPersonReady(person, supplied[person.personId], startStepIndex)) : [], [
|
|
163
189
|
answer,
|
|
164
|
-
supplied
|
|
190
|
+
supplied,
|
|
191
|
+
startStepIndex
|
|
165
192
|
]);
|
|
166
193
|
const waiting = answer ? answer.people.filter((person)=>{
|
|
167
194
|
var _supplied_person_personId;
|
|
168
|
-
return person.status !== 'blocked' && ((_supplied_person_personId = supplied[person.personId]) == null ? void 0 : _supplied_person_personId.include) && !outreachPersonReady(person, supplied[person.personId]);
|
|
195
|
+
return person.status !== 'blocked' && ((_supplied_person_personId = supplied[person.personId]) == null ? void 0 : _supplied_person_personId.include) && !outreachPersonReady(person, supplied[person.personId], startStepIndex);
|
|
169
196
|
}).length : 0;
|
|
170
197
|
const enroll = async ()=>{
|
|
171
198
|
if (!answer || !ready.length) return;
|
|
@@ -185,10 +212,13 @@ const STATUS_COLORS = {
|
|
|
185
212
|
}, {
|
|
186
213
|
personalLine: (_ref = (_supplied_person_personId = supplied[person.personId]) == null ? void 0 : _supplied_person_personId.personalLine.trim()) != null ? _ref : '',
|
|
187
214
|
attestations: (_ref1 = (_supplied_person_personId1 = supplied[person.personId]) == null ? void 0 : _supplied_person_personId1.attestations) != null ? _ref1 : []
|
|
188
|
-
}, usedOverrides(supplied[person.personId]).length ? {
|
|
189
|
-
stepOverrides: usedOverrides(supplied[person.personId])
|
|
215
|
+
}, usedOverrides(supplied[person.personId], startStepIndex).length ? {
|
|
216
|
+
stepOverrides: usedOverrides(supplied[person.personId], startStepIndex)
|
|
190
217
|
} : {});
|
|
191
|
-
}))
|
|
218
|
+
}), // Named only when it is not the first step (AGL-3228).
|
|
219
|
+
...startStepIndex > 0 ? [
|
|
220
|
+
startStepIndex
|
|
221
|
+
] : []);
|
|
192
222
|
setStage({
|
|
193
223
|
kind: 'done',
|
|
194
224
|
results: result.results,
|
|
@@ -214,7 +244,7 @@ const STATUS_COLORS = {
|
|
|
214
244
|
try {
|
|
215
245
|
var _ref;
|
|
216
246
|
var _supplied_person_personId;
|
|
217
|
-
const overrides = usedOverrides(supplied[person.personId]);
|
|
247
|
+
const overrides = usedOverrides(supplied[person.personId], startStepIndex);
|
|
218
248
|
const result = await api.previewEmail(_extends({
|
|
219
249
|
sequenceId: sequence.id
|
|
220
250
|
}, person.target === 'lead' && person.leadId ? {
|
|
@@ -225,6 +255,8 @@ const STATUS_COLORS = {
|
|
|
225
255
|
personalLine: (_ref = (_supplied_person_personId = supplied[person.personId]) == null ? void 0 : _supplied_person_personId.personalLine.trim()) != null ? _ref : ''
|
|
226
256
|
}, overrides.length ? {
|
|
227
257
|
stepOverrides: overrides
|
|
258
|
+
} : {}, startStepIndex > 0 ? {
|
|
259
|
+
startStepIndex
|
|
228
260
|
} : {}));
|
|
229
261
|
setEmailPreview((previous)=>_extends({}, previous, {
|
|
230
262
|
[person.personId]: {
|
|
@@ -312,7 +344,7 @@ const STATUS_COLORS = {
|
|
|
312
344
|
try {
|
|
313
345
|
var _ref;
|
|
314
346
|
var _supplied_person_personId;
|
|
315
|
-
const overrides = usedOverrides(supplied[person.personId]);
|
|
347
|
+
const overrides = usedOverrides(supplied[person.personId], startStepIndex);
|
|
316
348
|
const answer = await api.sendStepTest(_extends({
|
|
317
349
|
sequenceId: sequence.id
|
|
318
350
|
}, person.target === 'lead' && person.leadId ? {
|
|
@@ -323,6 +355,8 @@ const STATUS_COLORS = {
|
|
|
323
355
|
personalLine: (_ref = (_supplied_person_personId = supplied[person.personId]) == null ? void 0 : _supplied_person_personId.personalLine.trim()) != null ? _ref : ''
|
|
324
356
|
}, overrides.length ? {
|
|
325
357
|
stepOverrides: overrides
|
|
358
|
+
} : {}, startStepIndex > 0 ? {
|
|
359
|
+
startStepIndex
|
|
326
360
|
} : {}));
|
|
327
361
|
setStepTests((previous)=>_extends({}, previous, {
|
|
328
362
|
[person.personId]: {
|
|
@@ -588,6 +622,25 @@ const STATUS_COLORS = {
|
|
|
588
622
|
severity: "info",
|
|
589
623
|
children: `This shows the first ${answer.people.length} of ${answer.total}. Enroll these, then preview again for the rest.`
|
|
590
624
|
}) : null,
|
|
625
|
+
startOptions.length > 1 ? /*#__PURE__*/ _jsx(TextField, {
|
|
626
|
+
select: true,
|
|
627
|
+
size: "small",
|
|
628
|
+
label: "Start at",
|
|
629
|
+
value: startStepIndex,
|
|
630
|
+
onChange: (event)=>{
|
|
631
|
+
setStartStepIndex(Number(event.target.value));
|
|
632
|
+
// A preview or a test was written for the old first email.
|
|
633
|
+
setEmailPreview({});
|
|
634
|
+
setStepTests({});
|
|
635
|
+
},
|
|
636
|
+
helperText: outreachStartStepHelp(startStepIndex),
|
|
637
|
+
disabled: stage.kind === 'enrolling',
|
|
638
|
+
fullWidth: true,
|
|
639
|
+
children: startOptions.map((option)=>/*#__PURE__*/ _jsx(MenuItem, {
|
|
640
|
+
value: option.value,
|
|
641
|
+
children: option.label
|
|
642
|
+
}, option.value))
|
|
643
|
+
}) : null,
|
|
591
644
|
/*#__PURE__*/ _jsx(Stack, {
|
|
592
645
|
spacing: 1,
|
|
593
646
|
component: "ul",
|
|
@@ -600,6 +653,7 @@ const STATUS_COLORS = {
|
|
|
600
653
|
children: answer.people.map((person)=>/*#__PURE__*/ _jsx(PersonRow, {
|
|
601
654
|
person: person,
|
|
602
655
|
steps: sequence.steps,
|
|
656
|
+
startStepIndex: startStepIndex,
|
|
603
657
|
supplied: supplied[person.personId],
|
|
604
658
|
onSupply: (next)=>supply(person.personId, next),
|
|
605
659
|
emailPreview: emailPreview[person.personId],
|
|
@@ -699,7 +753,7 @@ function summary(answer) {
|
|
|
699
753
|
});
|
|
700
754
|
}
|
|
701
755
|
function PersonRow(props) {
|
|
702
|
-
var _ref, _ref1, _ref2
|
|
756
|
+
var _ref, _ref1, _ref2;
|
|
703
757
|
var _person_gateway, _person_gateway1, _person_gateway2, _props_stepTest, _props_stepTest1, _props_stepTest2, _props_stepTest3, _props_emailPreview, _props_emailPreview1, _props_emailPreview2;
|
|
704
758
|
const { person, supplied, disabled } = props;
|
|
705
759
|
const name = person.name || person.email || person.personId;
|
|
@@ -708,7 +762,7 @@ function PersonRow(props) {
|
|
|
708
762
|
const curation = (_ref1 = supplied == null ? void 0 : supplied.curation) != null ? _ref1 : {
|
|
709
763
|
status: 'idle'
|
|
710
764
|
};
|
|
711
|
-
const drafts =
|
|
765
|
+
const drafts = liveDrafts(supplied, props.startStepIndex).sort((a, b)=>a.stepIndex - b.stepIndex);
|
|
712
766
|
const usedCount = drafts.filter((draft)=>draft.decision === 'use').length;
|
|
713
767
|
return /*#__PURE__*/ _jsx(Paper, {
|
|
714
768
|
component: "li",
|
|
@@ -797,7 +851,7 @@ function PersonRow(props) {
|
|
|
797
851
|
children: [
|
|
798
852
|
/*#__PURE__*/ _jsx(FormControlLabel, {
|
|
799
853
|
control: /*#__PURE__*/ _jsx(Checkbox, {
|
|
800
|
-
checked: (
|
|
854
|
+
checked: (_ref2 = supplied == null ? void 0 : supplied.include) != null ? _ref2 : true,
|
|
801
855
|
onChange: (event)=>props.onSupply({
|
|
802
856
|
include: event.target.checked
|
|
803
857
|
}),
|