@aglyn/plugins-outreach 1.0.0-beta.143

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.
Files changed (282) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +93 -0
  3. package/package.json +52 -0
  4. package/src/index.d.ts +22 -0
  5. package/src/index.js +29 -0
  6. package/src/index.js.map +1 -0
  7. package/src/lib/components/compliance-section.d.ts +20 -0
  8. package/src/lib/components/compliance-section.js +291 -0
  9. package/src/lib/components/compliance-section.js.map +1 -0
  10. package/src/lib/components/enroll-dialog.d.ts +36 -0
  11. package/src/lib/components/enroll-dialog.js +656 -0
  12. package/src/lib/components/enroll-dialog.js.map +1 -0
  13. package/src/lib/components/enrollments-table.d.ts +28 -0
  14. package/src/lib/components/enrollments-table.js +375 -0
  15. package/src/lib/components/enrollments-table.js.map +1 -0
  16. package/src/lib/components/mailbox-card.d.ts +46 -0
  17. package/src/lib/components/mailbox-card.js +517 -0
  18. package/src/lib/components/mailbox-card.js.map +1 -0
  19. package/src/lib/components/mailboxes-section.d.ts +24 -0
  20. package/src/lib/components/mailboxes-section.js +258 -0
  21. package/src/lib/components/mailboxes-section.js.map +1 -0
  22. package/src/lib/components/outreach-console-page.d.ts +17 -0
  23. package/src/lib/components/outreach-console-page.js +81 -0
  24. package/src/lib/components/outreach-console-page.js.map +1 -0
  25. package/src/lib/components/outreach-console-sections.d.ts +43 -0
  26. package/src/lib/components/outreach-console-sections.js +55 -0
  27. package/src/lib/components/outreach-console-sections.js.map +1 -0
  28. package/src/lib/components/outreach-ui.d.ts +51 -0
  29. package/src/lib/components/outreach-ui.js +171 -0
  30. package/src/lib/components/outreach-ui.js.map +1 -0
  31. package/src/lib/components/send-window-fields.d.ts +25 -0
  32. package/src/lib/components/send-window-fields.js +178 -0
  33. package/src/lib/components/send-window-fields.js.map +1 -0
  34. package/src/lib/components/sequence-detail.d.ts +54 -0
  35. package/src/lib/components/sequence-detail.js +435 -0
  36. package/src/lib/components/sequence-detail.js.map +1 -0
  37. package/src/lib/components/sequence-editor.d.ts +37 -0
  38. package/src/lib/components/sequence-editor.js +485 -0
  39. package/src/lib/components/sequence-editor.js.map +1 -0
  40. package/src/lib/components/sequence-mailbox-picker.d.ts +44 -0
  41. package/src/lib/components/sequence-mailbox-picker.js +137 -0
  42. package/src/lib/components/sequence-mailbox-picker.js.map +1 -0
  43. package/src/lib/components/sequence-preview.d.ts +27 -0
  44. package/src/lib/components/sequence-preview.js +146 -0
  45. package/src/lib/components/sequence-preview.js.map +1 -0
  46. package/src/lib/components/sequence-step-card.d.ts +31 -0
  47. package/src/lib/components/sequence-step-card.js +326 -0
  48. package/src/lib/components/sequence-step-card.js.map +1 -0
  49. package/src/lib/components/sequences-section.d.ts +40 -0
  50. package/src/lib/components/sequences-section.js +310 -0
  51. package/src/lib/components/sequences-section.js.map +1 -0
  52. package/src/lib/components/use-outreach-api.d.ts +38 -0
  53. package/src/lib/components/use-outreach-api.js +139 -0
  54. package/src/lib/components/use-outreach-api.js.map +1 -0
  55. package/src/lib/components/use-outreach-crm.d.ts +50 -0
  56. package/src/lib/components/use-outreach-crm.js +200 -0
  57. package/src/lib/components/use-outreach-crm.js.map +1 -0
  58. package/src/lib/components/use-outreach-data.d.ts +55 -0
  59. package/src/lib/components/use-outreach-data.js +199 -0
  60. package/src/lib/components/use-outreach-data.js.map +1 -0
  61. package/src/lib/components/use-outreach-mailbox-api.d.ts +30 -0
  62. package/src/lib/components/use-outreach-mailbox-api.js +101 -0
  63. package/src/lib/components/use-outreach-mailbox-api.js.map +1 -0
  64. package/src/lib/components/use-outreach-mailboxes.d.ts +22 -0
  65. package/src/lib/components/use-outreach-mailboxes.js +68 -0
  66. package/src/lib/components/use-outreach-mailboxes.js.map +1 -0
  67. package/src/lib/components/use-outreach-settings.d.ts +17 -0
  68. package/src/lib/components/use-outreach-settings.js +64 -0
  69. package/src/lib/components/use-outreach-settings.js.map +1 -0
  70. package/src/lib/constants/api-routes.d.ts +68 -0
  71. package/src/lib/constants/api-routes.js +57 -0
  72. package/src/lib/constants/api-routes.js.map +1 -0
  73. package/src/lib/constants/bundle-common.d.ts +46 -0
  74. package/src/lib/constants/bundle-common.js +55 -0
  75. package/src/lib/constants/bundle-common.js.map +1 -0
  76. package/src/lib/constants/runtime-jobs.d.ts +24 -0
  77. package/src/lib/constants/runtime-jobs.js +24 -0
  78. package/src/lib/constants/runtime-jobs.js.map +1 -0
  79. package/src/lib/declarations.console-server.d.ts +38 -0
  80. package/src/lib/declarations.console-server.js +99 -0
  81. package/src/lib/declarations.console-server.js.map +1 -0
  82. package/src/lib/engine/compose.d.ts +86 -0
  83. package/src/lib/engine/compose.js +212 -0
  84. package/src/lib/engine/compose.js.map +1 -0
  85. package/src/lib/engine/delivery-status.d.ts +53 -0
  86. package/src/lib/engine/delivery-status.js +184 -0
  87. package/src/lib/engine/delivery-status.js.map +1 -0
  88. package/src/lib/engine/do-not-contact.d.ts +18 -0
  89. package/src/lib/engine/do-not-contact.js +34 -0
  90. package/src/lib/engine/do-not-contact.js.map +1 -0
  91. package/src/lib/engine/enrollment-state.d.ts +156 -0
  92. package/src/lib/engine/enrollment-state.js +303 -0
  93. package/src/lib/engine/enrollment-state.js.map +1 -0
  94. package/src/lib/engine/gates.d.ts +125 -0
  95. package/src/lib/engine/gates.js +232 -0
  96. package/src/lib/engine/gates.js.map +1 -0
  97. package/src/lib/engine/index.d.ts +28 -0
  98. package/src/lib/engine/index.js +33 -0
  99. package/src/lib/engine/index.js.map +1 -0
  100. package/src/lib/engine/mailbox-health.d.ts +49 -0
  101. package/src/lib/engine/mailbox-health.js +83 -0
  102. package/src/lib/engine/mailbox-health.js.map +1 -0
  103. package/src/lib/engine/opt-out-intent.d.ts +41 -0
  104. package/src/lib/engine/opt-out-intent.js +220 -0
  105. package/src/lib/engine/opt-out-intent.js.map +1 -0
  106. package/src/lib/engine/recipient-country.d.ts +41 -0
  107. package/src/lib/engine/recipient-country.js +171 -0
  108. package/src/lib/engine/recipient-country.js.map +1 -0
  109. package/src/lib/engine/schedule.d.ts +70 -0
  110. package/src/lib/engine/schedule.js +115 -0
  111. package/src/lib/engine/schedule.js.map +1 -0
  112. package/src/lib/engine/sending-capacity.d.ts +96 -0
  113. package/src/lib/engine/sending-capacity.js +172 -0
  114. package/src/lib/engine/sending-capacity.js.map +1 -0
  115. package/src/lib/engine/sequence-validation.d.ts +90 -0
  116. package/src/lib/engine/sequence-validation.js +291 -0
  117. package/src/lib/engine/sequence-validation.js.map +1 -0
  118. package/src/lib/engine/thread-classification.d.ts +78 -0
  119. package/src/lib/engine/thread-classification.js +224 -0
  120. package/src/lib/engine/thread-classification.js.map +1 -0
  121. package/src/lib/engine/thread-message.d.ts +80 -0
  122. package/src/lib/engine/thread-message.js +143 -0
  123. package/src/lib/engine/thread-message.js.map +1 -0
  124. package/src/lib/enrollment/enroll-people.d.ts +81 -0
  125. package/src/lib/enrollment/enroll-people.js +250 -0
  126. package/src/lib/enrollment/enroll-people.js.map +1 -0
  127. package/src/lib/enrollment/gate-lookups.d.ts +32 -0
  128. package/src/lib/enrollment/gate-lookups.js +154 -0
  129. package/src/lib/enrollment/gate-lookups.js.map +1 -0
  130. package/src/lib/mailboxes/mailbox-api.d.ts +126 -0
  131. package/src/lib/mailboxes/mailbox-api.js +66 -0
  132. package/src/lib/mailboxes/mailbox-api.js.map +1 -0
  133. package/src/lib/mailboxes/mailbox-credentials.d.ts +95 -0
  134. package/src/lib/mailboxes/mailbox-credentials.js +102 -0
  135. package/src/lib/mailboxes/mailbox-credentials.js.map +1 -0
  136. package/src/lib/mailboxes/mailbox-erasure.d.ts +69 -0
  137. package/src/lib/mailboxes/mailbox-erasure.js +148 -0
  138. package/src/lib/mailboxes/mailbox-erasure.js.map +1 -0
  139. package/src/lib/mailboxes/mailbox-gate.d.ts +75 -0
  140. package/src/lib/mailboxes/mailbox-gate.js +98 -0
  141. package/src/lib/mailboxes/mailbox-gate.js.map +1 -0
  142. package/src/lib/mailboxes/mailbox-revoke.d.ts +59 -0
  143. package/src/lib/mailboxes/mailbox-revoke.js +41 -0
  144. package/src/lib/mailboxes/mailbox-revoke.js.map +1 -0
  145. package/src/lib/mailboxes/mailbox-routes.d.ts +119 -0
  146. package/src/lib/mailboxes/mailbox-routes.js +605 -0
  147. package/src/lib/mailboxes/mailbox-routes.js.map +1 -0
  148. package/src/lib/mailboxes/mailbox-settings.d.ts +110 -0
  149. package/src/lib/mailboxes/mailbox-settings.js +212 -0
  150. package/src/lib/mailboxes/mailbox-settings.js.map +1 -0
  151. package/src/lib/mailboxes/mailbox-transport.d.ts +64 -0
  152. package/src/lib/mailboxes/mailbox-transport.js +94 -0
  153. package/src/lib/mailboxes/mailbox-transport.js.map +1 -0
  154. package/src/lib/mailboxes/oauth-redirect.d.ts +41 -0
  155. package/src/lib/mailboxes/oauth-redirect.js +63 -0
  156. package/src/lib/mailboxes/oauth-redirect.js.map +1 -0
  157. package/src/lib/mailboxes/oauth-state.d.ts +152 -0
  158. package/src/lib/mailboxes/oauth-state.js +212 -0
  159. package/src/lib/mailboxes/oauth-state.js.map +1 -0
  160. package/src/lib/mailboxes/outreach-config.d.ts +64 -0
  161. package/src/lib/mailboxes/outreach-config.js +77 -0
  162. package/src/lib/mailboxes/outreach-config.js.map +1 -0
  163. package/src/lib/mailboxes/register-mailbox-routes.d.ts +47 -0
  164. package/src/lib/mailboxes/register-mailbox-routes.js +135 -0
  165. package/src/lib/mailboxes/register-mailbox-routes.js.map +1 -0
  166. package/src/lib/model/compliance-settings.d.ts +78 -0
  167. package/src/lib/model/compliance-settings.js +155 -0
  168. package/src/lib/model/compliance-settings.js.map +1 -0
  169. package/src/lib/model/outreach-api.d.ts +210 -0
  170. package/src/lib/model/outreach-api.js +28 -0
  171. package/src/lib/model/outreach-api.js.map +1 -0
  172. package/src/lib/model/outreach.types.d.ts +563 -0
  173. package/src/lib/model/outreach.types.js +225 -0
  174. package/src/lib/model/outreach.types.js.map +1 -0
  175. package/src/lib/model/sequence-draft.d.ts +84 -0
  176. package/src/lib/model/sequence-draft.js +239 -0
  177. package/src/lib/model/sequence-draft.js.map +1 -0
  178. package/src/lib/model/step-preview.d.ts +45 -0
  179. package/src/lib/model/step-preview.js +98 -0
  180. package/src/lib/model/step-preview.js.map +1 -0
  181. package/src/lib/model/stored-records.d.ts +23 -0
  182. package/src/lib/model/stored-records.js +84 -0
  183. package/src/lib/model/stored-records.js.map +1 -0
  184. package/src/lib/plugin.d.ts +46 -0
  185. package/src/lib/plugin.js +86 -0
  186. package/src/lib/plugin.js.map +1 -0
  187. package/src/lib/routes/enroll-routes.d.ts +64 -0
  188. package/src/lib/routes/enroll-routes.js +350 -0
  189. package/src/lib/routes/enroll-routes.js.map +1 -0
  190. package/src/lib/routes/enrollment-routes.d.ts +19 -0
  191. package/src/lib/routes/enrollment-routes.js +171 -0
  192. package/src/lib/routes/enrollment-routes.js.map +1 -0
  193. package/src/lib/routes/platform-deps.d.ts +51 -0
  194. package/src/lib/routes/platform-deps.js +66 -0
  195. package/src/lib/routes/platform-deps.js.map +1 -0
  196. package/src/lib/routes/preview-routes.d.ts +33 -0
  197. package/src/lib/routes/preview-routes.js +117 -0
  198. package/src/lib/routes/preview-routes.js.map +1 -0
  199. package/src/lib/routes/register-routes.d.ts +22 -0
  200. package/src/lib/routes/register-routes.js +92 -0
  201. package/src/lib/routes/register-routes.js.map +1 -0
  202. package/src/lib/routes/route-deps.d.ts +49 -0
  203. package/src/lib/routes/route-deps.js +27 -0
  204. package/src/lib/routes/route-deps.js.map +1 -0
  205. package/src/lib/routes/route-gate.d.ts +94 -0
  206. package/src/lib/routes/route-gate.js +87 -0
  207. package/src/lib/routes/route-gate.js.map +1 -0
  208. package/src/lib/routes/route-http.d.ts +32 -0
  209. package/src/lib/routes/route-http.js +62 -0
  210. package/src/lib/routes/route-http.js.map +1 -0
  211. package/src/lib/routes/sequence-routes.d.ts +32 -0
  212. package/src/lib/routes/sequence-routes.js +359 -0
  213. package/src/lib/routes/sequence-routes.js.map +1 -0
  214. package/src/lib/routes/settings-routes.d.ts +30 -0
  215. package/src/lib/routes/settings-routes.js +73 -0
  216. package/src/lib/routes/settings-routes.js.map +1 -0
  217. package/src/lib/runtime/enrollment-events.d.ts +86 -0
  218. package/src/lib/runtime/enrollment-events.js +141 -0
  219. package/src/lib/runtime/enrollment-events.js.map +1 -0
  220. package/src/lib/runtime/fixtures/fake-gmail.d.ts +121 -0
  221. package/src/lib/runtime/fixtures/fake-gmail.js +312 -0
  222. package/src/lib/runtime/fixtures/fake-gmail.js.map +1 -0
  223. package/src/lib/runtime/mailbox-health-store.d.ts +47 -0
  224. package/src/lib/runtime/mailbox-health-store.js +168 -0
  225. package/src/lib/runtime/mailbox-health-store.js.map +1 -0
  226. package/src/lib/runtime/person-erasure.d.ts +39 -0
  227. package/src/lib/runtime/person-erasure.js +57 -0
  228. package/src/lib/runtime/person-erasure.js.map +1 -0
  229. package/src/lib/runtime/platform-runtime-deps.d.ts +29 -0
  230. package/src/lib/runtime/platform-runtime-deps.js +98 -0
  231. package/src/lib/runtime/platform-runtime-deps.js.map +1 -0
  232. package/src/lib/runtime/runtime-deps.d.ts +77 -0
  233. package/src/lib/runtime/runtime-deps.js +18 -0
  234. package/src/lib/runtime/runtime-deps.js.map +1 -0
  235. package/src/lib/runtime/send-job.d.ts +42 -0
  236. package/src/lib/runtime/send-job.js +780 -0
  237. package/src/lib/runtime/send-job.js.map +1 -0
  238. package/src/lib/runtime/sync-job.d.ts +41 -0
  239. package/src/lib/runtime/sync-job.js +480 -0
  240. package/src/lib/runtime/sync-job.js.map +1 -0
  241. package/src/lib/runtime/timeline.d.ts +67 -0
  242. package/src/lib/runtime/timeline.js +93 -0
  243. package/src/lib/runtime/timeline.js.map +1 -0
  244. package/src/lib/runtime/unsubscribe-link.d.ts +75 -0
  245. package/src/lib/runtime/unsubscribe-link.js +121 -0
  246. package/src/lib/runtime/unsubscribe-link.js.map +1 -0
  247. package/src/lib/runtime/unsubscribe-route.d.ts +24 -0
  248. package/src/lib/runtime/unsubscribe-route.js +103 -0
  249. package/src/lib/runtime/unsubscribe-route.js.map +1 -0
  250. package/src/lib/server.d.ts +53 -0
  251. package/src/lib/server.js +94 -0
  252. package/src/lib/server.js.map +1 -0
  253. package/src/lib/storage/compliance-settings-store.d.ts +44 -0
  254. package/src/lib/storage/compliance-settings-store.js +78 -0
  255. package/src/lib/storage/compliance-settings-store.js.map +1 -0
  256. package/src/lib/storage/do-not-contact-store.d.ts +60 -0
  257. package/src/lib/storage/do-not-contact-store.js +168 -0
  258. package/src/lib/storage/do-not-contact-store.js.map +1 -0
  259. package/src/lib/storage/outreach-records.d.ts +33 -0
  260. package/src/lib/storage/outreach-records.js +35 -0
  261. package/src/lib/storage/outreach-records.js.map +1 -0
  262. package/src/lib/subprocessors.d.ts +48 -0
  263. package/src/lib/subprocessors.js +70 -0
  264. package/src/lib/subprocessors.js.map +1 -0
  265. package/src/lib/transport/gmail-client.d.ts +145 -0
  266. package/src/lib/transport/gmail-client.js +229 -0
  267. package/src/lib/transport/gmail-client.js.map +1 -0
  268. package/src/lib/transport/gmail-errors.d.ts +84 -0
  269. package/src/lib/transport/gmail-errors.js +138 -0
  270. package/src/lib/transport/gmail-errors.js.map +1 -0
  271. package/src/lib/transport/google-oauth.d.ts +138 -0
  272. package/src/lib/transport/google-oauth.js +247 -0
  273. package/src/lib/transport/google-oauth.js.map +1 -0
  274. package/src/lib/transport/http.d.ts +53 -0
  275. package/src/lib/transport/http.js +79 -0
  276. package/src/lib/transport/http.js.map +1 -0
  277. package/src/lib/transport/rfc5322.d.ts +116 -0
  278. package/src/lib/transport/rfc5322.js +330 -0
  279. package/src/lib/transport/rfc5322.js.map +1 -0
  280. package/src/lib/transport/send-message.d.ts +69 -0
  281. package/src/lib/transport/send-message.js +77 -0
  282. package/src/lib/transport/send-message.js.map +1 -0
@@ -0,0 +1,656 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ 'use client';
17
+ import { _ as _extends } from "@swc/helpers/_/_extends";
18
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
19
+ import { pluginDocsHelp } from "@aglyn/aglyn";
20
+ import { HelpTip } from "@aglyn/shared-ui-jsx";
21
+ import { Alert, Button, Checkbox, Chip, Dialog, DialogActions, DialogContent, DialogTitle, FormControlLabel, FormGroup, List, ListItem, ListItemButton, ListItemIcon, ListItemText, MenuItem, Paper, Stack, Tab, Tabs, TextField, Typography, useMediaQuery, useTheme } from "@mui/material";
22
+ import { useMemo, useState } from "react";
23
+ import { OUTREACH_ENROLL_BATCH_MAX } from "../model/outreach-api.js";
24
+ import { OUTREACH_ATTESTATION_LABELS, OUTREACH_PERSONAL_LINE_MAX } from "../model/outreach.types.js";
25
+ import { OutreachLoading, OutreachLoadProblem } from "./outreach-ui.js";
26
+ import { useOutreachContactSearch, useOutreachSavedViews } from "./use-outreach-crm.js";
27
+ const STATUS_LABELS = {
28
+ eligible: 'Eligible',
29
+ needs_confirmation: 'Needs you',
30
+ blocked: 'Blocked'
31
+ };
32
+ const STATUS_COLORS = {
33
+ eligible: 'success',
34
+ needs_confirmation: 'warning',
35
+ blocked: 'default'
36
+ };
37
+ /** Whether a person may be sent to Confirm as the rep has left them. */ export function outreachPersonReady(person, supplied) {
38
+ if (person.status === 'blocked' || !(supplied == null ? void 0 : supplied.include)) return false;
39
+ const line = supplied.personalLine.trim();
40
+ if (line.length > OUTREACH_PERSONAL_LINE_MAX) return false;
41
+ if (person.status === 'eligible') return true;
42
+ if (person.requires.personalLine && !line) return false;
43
+ return person.requires.attestations.every((kind)=>supplied.attestations.includes(kind));
44
+ }
45
+ /**
46
+ * Enroll people in a sequence (AGL-2980).
47
+ *
48
+ * The rep picks a saved Contacts view or searches for people, and sees each
49
+ * marked as the gates see them: eligible, blocked with the reason, or
50
+ * waiting on them — for a cold contact, a personal line and the three
51
+ * attestations only they can make, stored on the enrollment with who ticked
52
+ * them and when. Enroll sends only the people who are ready, and the server
53
+ * checks every gate again as it enrolls them.
54
+ */ export function OutreachEnrollDialog(props) {
55
+ const { orgId, sequence, api } = props;
56
+ const theme = useTheme();
57
+ const narrow = useMediaQuery(theme.breakpoints.down('sm'));
58
+ const [tab, setTab] = useState('view');
59
+ const [viewId, setViewId] = useState('');
60
+ const [text, setText] = useState('');
61
+ const [picked, setPicked] = useState(new Map());
62
+ const [stage, setStage] = useState({
63
+ kind: 'source'
64
+ });
65
+ const [problem, setProblem] = useState(null);
66
+ const [supplied, setSupplied] = useState({});
67
+ const [emailPreview, setEmailPreview] = useState({});
68
+ const views = useOutreachSavedViews(props.open ? orgId : null, props.uid);
69
+ const search = useOutreachContactSearch({
70
+ orgId: props.open && tab === 'search' ? orgId : null,
71
+ hostId: sequence.hostId,
72
+ contactGroupId: props.contactGroupId,
73
+ text
74
+ });
75
+ const reset = ()=>{
76
+ setStage({
77
+ kind: 'source'
78
+ });
79
+ setProblem(null);
80
+ setSupplied({});
81
+ setEmailPreview({});
82
+ };
83
+ const close = ()=>{
84
+ reset();
85
+ setPicked(new Map());
86
+ setText('');
87
+ setViewId('');
88
+ props.onClose();
89
+ };
90
+ const preview = async (source)=>{
91
+ setProblem(null);
92
+ setStage({
93
+ kind: 'previewing'
94
+ });
95
+ try {
96
+ const answer = await api.previewEnrollment(sequence.id, source);
97
+ setSupplied(Object.fromEntries(answer.people.map((person)=>[
98
+ person.contactId,
99
+ {
100
+ include: person.status !== 'blocked',
101
+ personalLine: '',
102
+ attestations: []
103
+ }
104
+ ])));
105
+ setStage({
106
+ kind: 'preview',
107
+ answer
108
+ });
109
+ } catch (error) {
110
+ setProblem(error.message);
111
+ setStage({
112
+ kind: 'source'
113
+ });
114
+ }
115
+ };
116
+ const answer = stage.kind === 'preview' || stage.kind === 'enrolling' ? stage.answer : null;
117
+ const ready = useMemo(()=>answer ? answer.people.filter((person)=>outreachPersonReady(person, supplied[person.contactId])) : [], [
118
+ answer,
119
+ supplied
120
+ ]);
121
+ const waiting = answer ? answer.people.filter((person)=>{
122
+ var _supplied_person_contactId;
123
+ return person.status === 'needs_confirmation' && ((_supplied_person_contactId = supplied[person.contactId]) == null ? void 0 : _supplied_person_contactId.include) && !outreachPersonReady(person, supplied[person.contactId]);
124
+ }).length : 0;
125
+ const enroll = async ()=>{
126
+ if (!answer || !ready.length) return;
127
+ setProblem(null);
128
+ setStage({
129
+ kind: 'enrolling',
130
+ answer
131
+ });
132
+ try {
133
+ const result = await api.enroll(sequence.id, ready.map((person)=>{
134
+ var _ref, _ref1;
135
+ var _supplied_person_contactId, _supplied_person_contactId1;
136
+ return {
137
+ contactId: person.contactId,
138
+ personalLine: (_ref = (_supplied_person_contactId = supplied[person.contactId]) == null ? void 0 : _supplied_person_contactId.personalLine.trim()) != null ? _ref : '',
139
+ attestations: (_ref1 = (_supplied_person_contactId1 = supplied[person.contactId]) == null ? void 0 : _supplied_person_contactId1.attestations) != null ? _ref1 : []
140
+ };
141
+ }));
142
+ setStage({
143
+ kind: 'done',
144
+ results: result.results,
145
+ enrolled: result.enrolled
146
+ });
147
+ } catch (error) {
148
+ setProblem(error.message);
149
+ setStage({
150
+ kind: 'preview',
151
+ answer
152
+ });
153
+ }
154
+ };
155
+ const supply = (contactId, next)=>setSupplied((previous)=>_extends({}, previous, {
156
+ [contactId]: _extends({}, previous[contactId], next)
157
+ }));
158
+ const previewEmail = async (person)=>{
159
+ setEmailPreview((previous)=>_extends({}, previous, {
160
+ [person.contactId]: {
161
+ status: 'loading'
162
+ }
163
+ }));
164
+ try {
165
+ var _ref;
166
+ var _supplied_person_contactId;
167
+ const result = await api.previewEmail({
168
+ sequenceId: sequence.id,
169
+ contactId: person.contactId,
170
+ personalLine: (_ref = (_supplied_person_contactId = supplied[person.contactId]) == null ? void 0 : _supplied_person_contactId.personalLine.trim()) != null ? _ref : ''
171
+ });
172
+ setEmailPreview((previous)=>_extends({}, previous, {
173
+ [person.contactId]: {
174
+ status: 'ready',
175
+ answer: result
176
+ }
177
+ }));
178
+ } catch (error) {
179
+ setEmailPreview((previous)=>_extends({}, previous, {
180
+ [person.contactId]: {
181
+ status: 'failed',
182
+ message: error.message
183
+ }
184
+ }));
185
+ }
186
+ };
187
+ const title = `Enroll people in ${sequence.name}`;
188
+ return /*#__PURE__*/ _jsxs(Dialog, {
189
+ open: props.open,
190
+ onClose: close,
191
+ fullWidth: true,
192
+ maxWidth: "md",
193
+ fullScreen: narrow,
194
+ "aria-label": title,
195
+ children: [
196
+ /*#__PURE__*/ _jsx(DialogTitle, {
197
+ children: /*#__PURE__*/ _jsxs(Stack, {
198
+ direction: "row",
199
+ spacing: 0.5,
200
+ sx: {
201
+ alignItems: 'center'
202
+ },
203
+ children: [
204
+ /*#__PURE__*/ _jsx("span", {
205
+ children: title
206
+ }),
207
+ /*#__PURE__*/ _jsx(HelpTip, _extends({}, pluginDocsHelp('sequences', {
208
+ anchor: '#enroll'
209
+ })))
210
+ ]
211
+ })
212
+ }),
213
+ /*#__PURE__*/ _jsx(DialogContent, {
214
+ dividers: true,
215
+ children: /*#__PURE__*/ _jsxs(Stack, {
216
+ spacing: 2,
217
+ children: [
218
+ problem ? /*#__PURE__*/ _jsx(Alert, {
219
+ severity: "error",
220
+ children: problem
221
+ }) : null,
222
+ stage.kind === 'source' ? /*#__PURE__*/ _jsxs(_Fragment, {
223
+ children: [
224
+ /*#__PURE__*/ _jsxs(Tabs, {
225
+ value: tab,
226
+ onChange: (_event, next)=>setTab(next),
227
+ "aria-label": "Where the people come from",
228
+ children: [
229
+ /*#__PURE__*/ _jsx(Tab, {
230
+ value: "view",
231
+ label: "Saved view"
232
+ }),
233
+ /*#__PURE__*/ _jsx(Tab, {
234
+ value: "search",
235
+ label: "Search"
236
+ })
237
+ ]
238
+ }),
239
+ tab === 'view' ? views.status === 'loading' ? /*#__PURE__*/ _jsx(OutreachLoading, {
240
+ label: "Loading saved views…"
241
+ }) : views.status === 'error' || views.status === 'refused' ? /*#__PURE__*/ _jsx(OutreachLoadProblem, {
242
+ status: views.status,
243
+ what: "saved views",
244
+ message: views.status === 'refused' ? 'Enrolling reads your CRM’s contacts, and your role does not include Manage data.' : null
245
+ }) : views.data.length ? /*#__PURE__*/ _jsx(TextField, {
246
+ select: true,
247
+ label: "Saved Contacts view",
248
+ value: viewId,
249
+ onChange: (event)=>setViewId(event.target.value),
250
+ helperText: `Enrolls up to ${OUTREACH_ENROLL_BATCH_MAX} of the people the view shows at this sequence’s site.`,
251
+ fullWidth: true,
252
+ children: views.data.map((view)=>/*#__PURE__*/ _jsx(MenuItem, {
253
+ value: view.id,
254
+ children: view.name
255
+ }, view.id))
256
+ }) : /*#__PURE__*/ _jsx(Alert, {
257
+ severity: "info",
258
+ children: "You have no saved Contacts views to enroll from. Save one in the CRM, or search instead."
259
+ }) : /*#__PURE__*/ _jsxs(Stack, {
260
+ spacing: 1.5,
261
+ children: [
262
+ /*#__PURE__*/ _jsx(TextField, {
263
+ label: "Search contacts",
264
+ placeholder: "A name, or an email address",
265
+ value: text,
266
+ onChange: (event)=>setText(event.target.value),
267
+ fullWidth: true,
268
+ autoFocus: true
269
+ }),
270
+ search.status === 'loading' ? /*#__PURE__*/ _jsx(OutreachLoading, {
271
+ label: "Searching…"
272
+ }) : null,
273
+ search.status === 'error' || search.status === 'refused' ? /*#__PURE__*/ _jsx(OutreachLoadProblem, {
274
+ status: search.status,
275
+ what: "contacts",
276
+ message: search.status === 'refused' ? 'Enrolling reads your CRM’s contacts, and your role does not include Manage data.' : 'The search failed. Try again.'
277
+ }) : null,
278
+ search.status === 'ready' && !search.idle && !search.data.length ? /*#__PURE__*/ _jsx(Typography, {
279
+ variant: "body2",
280
+ color: "text.secondary",
281
+ children: "No contacts at this sequence’s site match."
282
+ }) : null,
283
+ search.data.length ? /*#__PURE__*/ _jsx(List, {
284
+ dense: true,
285
+ "aria-label": "Search results",
286
+ children: search.data.map((contact)=>{
287
+ const checked = picked.has(contact.id);
288
+ return /*#__PURE__*/ _jsx(ListItem, {
289
+ disablePadding: true,
290
+ children: /*#__PURE__*/ _jsxs(ListItemButton, {
291
+ onClick: ()=>setPicked((previous)=>{
292
+ const next = new Map(previous);
293
+ if (checked) next.delete(contact.id);
294
+ else if (next.size < OUTREACH_ENROLL_BATCH_MAX) next.set(contact.id, contact);
295
+ return next;
296
+ }),
297
+ children: [
298
+ /*#__PURE__*/ _jsx(ListItemIcon, {
299
+ children: /*#__PURE__*/ _jsx(Checkbox, {
300
+ edge: "start",
301
+ checked: checked,
302
+ tabIndex: -1,
303
+ disableRipple: true
304
+ })
305
+ }),
306
+ /*#__PURE__*/ _jsx(ListItemText, {
307
+ primary: contact.name || contact.email || contact.id,
308
+ secondary: contact.email
309
+ })
310
+ ]
311
+ })
312
+ }, contact.id);
313
+ })
314
+ }) : null,
315
+ picked.size ? /*#__PURE__*/ _jsx(Stack, {
316
+ direction: "row",
317
+ spacing: 1,
318
+ sx: {
319
+ flexWrap: 'wrap',
320
+ rowGap: 1
321
+ },
322
+ "aria-label": "Picked",
323
+ children: [
324
+ ...picked.values()
325
+ ].map((contact)=>/*#__PURE__*/ _jsx(Chip, {
326
+ size: "small",
327
+ label: contact.name || contact.email || contact.id,
328
+ onDelete: ()=>setPicked((previous)=>{
329
+ const next = new Map(previous);
330
+ next.delete(contact.id);
331
+ return next;
332
+ })
333
+ }, contact.id))
334
+ }) : null
335
+ ]
336
+ })
337
+ ]
338
+ }) : null,
339
+ stage.kind === 'previewing' ? /*#__PURE__*/ _jsx(OutreachLoading, {
340
+ label: "Checking each person…"
341
+ }) : null,
342
+ answer ? /*#__PURE__*/ _jsxs(Stack, {
343
+ spacing: 1.5,
344
+ children: [
345
+ /*#__PURE__*/ _jsx(Typography, {
346
+ variant: "body2",
347
+ color: "text.secondary",
348
+ children: summary(answer)
349
+ }),
350
+ answer.truncated ? /*#__PURE__*/ _jsx(Alert, {
351
+ severity: "info",
352
+ children: `This shows the first ${answer.people.length} of ${answer.total}. Enroll these, then preview again for the rest.`
353
+ }) : null,
354
+ /*#__PURE__*/ _jsx(Stack, {
355
+ spacing: 1,
356
+ component: "ul",
357
+ sx: {
358
+ listStyle: 'none',
359
+ p: 0,
360
+ m: 0
361
+ },
362
+ "aria-label": "People",
363
+ children: answer.people.map((person)=>/*#__PURE__*/ _jsx(PersonRow, {
364
+ person: person,
365
+ supplied: supplied[person.contactId],
366
+ onSupply: (next)=>supply(person.contactId, next),
367
+ emailPreview: emailPreview[person.contactId],
368
+ onPreviewEmail: ()=>void previewEmail(person),
369
+ disabled: stage.kind === 'enrolling'
370
+ }, person.contactId))
371
+ }),
372
+ waiting ? /*#__PURE__*/ _jsx(Typography, {
373
+ variant: "body2",
374
+ color: "text.secondary",
375
+ children: `${waiting} ${waiting === 1 ? 'person still needs' : 'people still need'} a personal line or a confirmation, and won't be enrolled until they have one.`
376
+ }) : null
377
+ ]
378
+ }) : null,
379
+ stage.kind === 'done' ? /*#__PURE__*/ _jsx(DoneSummary, {
380
+ results: stage.results,
381
+ enrolled: stage.enrolled
382
+ }) : null
383
+ ]
384
+ })
385
+ }),
386
+ /*#__PURE__*/ _jsx(DialogActions, {
387
+ children: stage.kind === 'done' ? /*#__PURE__*/ _jsx(Button, {
388
+ variant: "contained",
389
+ onClick: close,
390
+ children: "Done"
391
+ }) : /*#__PURE__*/ _jsxs(_Fragment, {
392
+ children: [
393
+ /*#__PURE__*/ _jsx(Button, {
394
+ onClick: close,
395
+ children: "Cancel"
396
+ }),
397
+ stage.kind === 'preview' || stage.kind === 'enrolling' ? /*#__PURE__*/ _jsxs(_Fragment, {
398
+ children: [
399
+ /*#__PURE__*/ _jsx(Button, {
400
+ onClick: reset,
401
+ disabled: stage.kind === 'enrolling',
402
+ children: "Back"
403
+ }),
404
+ /*#__PURE__*/ _jsx(Button, {
405
+ variant: "contained",
406
+ disabled: !ready.length || stage.kind === 'enrolling',
407
+ onClick: ()=>void enroll(),
408
+ children: stage.kind === 'enrolling' ? 'Enrolling…' : `Enroll ${ready.length} ${ready.length === 1 ? 'person' : 'people'}`
409
+ })
410
+ ]
411
+ }) : /*#__PURE__*/ _jsx(Button, {
412
+ variant: "contained",
413
+ disabled: stage.kind === 'previewing' || (tab === 'view' ? !viewId : !picked.size),
414
+ onClick: ()=>void preview(tab === 'view' ? {
415
+ kind: 'view',
416
+ viewId
417
+ } : {
418
+ kind: 'contacts',
419
+ contactIds: [
420
+ ...picked.keys()
421
+ ]
422
+ }),
423
+ children: "Check people"
424
+ })
425
+ ]
426
+ })
427
+ })
428
+ ]
429
+ });
430
+ }
431
+ OutreachEnrollDialog.displayName = 'OutreachEnrollDialog';
432
+ function summary(answer) {
433
+ const count = (status)=>answer.people.filter((person)=>person.status === status).length;
434
+ return `${count('eligible')} eligible · ${count('needs_confirmation')} need you · ${count('blocked')} blocked`;
435
+ }
436
+ function PersonRow(props) {
437
+ var _ref, _ref1;
438
+ var _props_emailPreview, _props_emailPreview1, _props_emailPreview2;
439
+ const { person, supplied, disabled } = props;
440
+ const name = person.name || person.email || person.contactId;
441
+ const line = (_ref = supplied == null ? void 0 : supplied.personalLine) != null ? _ref : '';
442
+ const tooLong = line.trim().length > OUTREACH_PERSONAL_LINE_MAX;
443
+ return /*#__PURE__*/ _jsx(Paper, {
444
+ component: "li",
445
+ variant: "outlined",
446
+ sx: {
447
+ p: 1.5
448
+ },
449
+ "aria-label": name,
450
+ children: /*#__PURE__*/ _jsxs(Stack, {
451
+ spacing: 1,
452
+ children: [
453
+ /*#__PURE__*/ _jsxs(Stack, {
454
+ direction: {
455
+ xs: 'column',
456
+ sm: 'row'
457
+ },
458
+ spacing: 1,
459
+ sx: {
460
+ alignItems: {
461
+ sm: 'center'
462
+ }
463
+ },
464
+ children: [
465
+ /*#__PURE__*/ _jsxs(Stack, {
466
+ sx: {
467
+ flexGrow: 1,
468
+ minWidth: 0
469
+ },
470
+ children: [
471
+ /*#__PURE__*/ _jsx(Typography, {
472
+ variant: "body2",
473
+ children: name
474
+ }),
475
+ person.email && person.email !== name ? /*#__PURE__*/ _jsx(Typography, {
476
+ variant: "caption",
477
+ color: "text.secondary",
478
+ children: person.email
479
+ }) : null
480
+ ]
481
+ }),
482
+ /*#__PURE__*/ _jsxs(Stack, {
483
+ direction: "row",
484
+ spacing: 1,
485
+ sx: {
486
+ alignItems: 'center'
487
+ },
488
+ children: [
489
+ person.cold ? /*#__PURE__*/ _jsx(Chip, {
490
+ size: "small",
491
+ variant: "outlined",
492
+ label: "Cold"
493
+ }) : null,
494
+ /*#__PURE__*/ _jsx(Chip, {
495
+ size: "small",
496
+ label: STATUS_LABELS[person.status],
497
+ color: STATUS_COLORS[person.status]
498
+ })
499
+ ]
500
+ })
501
+ ]
502
+ }),
503
+ person.status === 'blocked' ? /*#__PURE__*/ _jsx(Stack, {
504
+ component: "ul",
505
+ spacing: 0.5,
506
+ sx: {
507
+ m: 0,
508
+ pl: 2.5
509
+ },
510
+ children: person.blocks.map((block)=>/*#__PURE__*/ _jsx(Typography, {
511
+ component: "li",
512
+ variant: "body2",
513
+ color: "text.secondary",
514
+ children: block.reason
515
+ }, block.code))
516
+ }) : /*#__PURE__*/ _jsxs(Stack, {
517
+ spacing: 1,
518
+ children: [
519
+ /*#__PURE__*/ _jsx(FormControlLabel, {
520
+ control: /*#__PURE__*/ _jsx(Checkbox, {
521
+ checked: (_ref1 = supplied == null ? void 0 : supplied.include) != null ? _ref1 : true,
522
+ onChange: (event)=>props.onSupply({
523
+ include: event.target.checked
524
+ }),
525
+ disabled: disabled
526
+ }),
527
+ label: "Enroll this person"
528
+ }),
529
+ (supplied == null ? void 0 : supplied.include) === false ? null : /*#__PURE__*/ _jsxs(_Fragment, {
530
+ children: [
531
+ /*#__PURE__*/ _jsx(TextField, {
532
+ label: person.requires.personalLine ? 'Personal line' : 'Personal line (optional)',
533
+ placeholder: "One sentence on why you're writing to this person now.",
534
+ value: line,
535
+ onChange: (event)=>props.onSupply({
536
+ personalLine: event.target.value
537
+ }),
538
+ disabled: disabled,
539
+ required: person.requires.personalLine,
540
+ error: tooLong,
541
+ helperText: tooLong ? `Keep it to one sentence, under ${OUTREACH_PERSONAL_LINE_MAX} characters.` : 'Goes where the emails say {{enrollment.personalLine}}.',
542
+ fullWidth: true,
543
+ size: "small"
544
+ }),
545
+ person.requires.attestations.length ? /*#__PURE__*/ _jsx(FormGroup, {
546
+ "aria-label": `What you confirm about ${name}`,
547
+ children: person.requires.attestations.map((kind)=>{
548
+ var _ref;
549
+ return /*#__PURE__*/ _jsx(FormControlLabel, {
550
+ control: /*#__PURE__*/ _jsx(Checkbox, {
551
+ checked: (_ref = supplied == null ? void 0 : supplied.attestations.includes(kind)) != null ? _ref : false,
552
+ onChange: (event)=>{
553
+ var _ref, _ref1;
554
+ return props.onSupply({
555
+ attestations: event.target.checked ? [
556
+ ...(_ref = supplied == null ? void 0 : supplied.attestations) != null ? _ref : [],
557
+ kind
558
+ ] : ((_ref1 = supplied == null ? void 0 : supplied.attestations) != null ? _ref1 : []).filter((entry)=>entry !== kind)
559
+ });
560
+ },
561
+ disabled: disabled
562
+ }),
563
+ label: OUTREACH_ATTESTATION_LABELS[kind]
564
+ }, kind);
565
+ })
566
+ }) : null,
567
+ /*#__PURE__*/ _jsx(Stack, {
568
+ direction: "row",
569
+ children: /*#__PURE__*/ _jsx(Button, {
570
+ size: "small",
571
+ onClick: props.onPreviewEmail,
572
+ disabled: disabled,
573
+ children: "Preview the first email"
574
+ })
575
+ }),
576
+ ((_props_emailPreview = props.emailPreview) == null ? void 0 : _props_emailPreview.status) === 'loading' ? /*#__PURE__*/ _jsx(OutreachLoading, {
577
+ label: "Writing the email…"
578
+ }) : null,
579
+ ((_props_emailPreview1 = props.emailPreview) == null ? void 0 : _props_emailPreview1.status) === 'failed' ? /*#__PURE__*/ _jsx(Alert, {
580
+ severity: "error",
581
+ children: props.emailPreview.message
582
+ }) : null,
583
+ ((_props_emailPreview2 = props.emailPreview) == null ? void 0 : _props_emailPreview2.status) === 'ready' ? props.emailPreview.answer.error ? /*#__PURE__*/ _jsx(Alert, {
584
+ severity: "info",
585
+ children: props.emailPreview.answer.error.message
586
+ }) : /*#__PURE__*/ _jsxs(Paper, {
587
+ variant: "outlined",
588
+ sx: {
589
+ p: 1.5
590
+ },
591
+ "aria-label": `First email to ${name}`,
592
+ children: [
593
+ /*#__PURE__*/ _jsx(Typography, {
594
+ variant: "body2",
595
+ children: `Subject: ${props.emailPreview.answer.subject}`
596
+ }),
597
+ /*#__PURE__*/ _jsx(Typography, {
598
+ variant: "body2",
599
+ sx: {
600
+ whiteSpace: 'pre-wrap',
601
+ wordBreak: 'break-word',
602
+ mt: 1
603
+ },
604
+ children: props.emailPreview.answer.text
605
+ })
606
+ ]
607
+ }) : null
608
+ ]
609
+ })
610
+ ]
611
+ })
612
+ ]
613
+ })
614
+ });
615
+ }
616
+ function DoneSummary(props) {
617
+ const refused = props.results.filter((result)=>result.outcome === 'blocked');
618
+ return /*#__PURE__*/ _jsxs(Stack, {
619
+ spacing: 1.5,
620
+ children: [
621
+ /*#__PURE__*/ _jsx(Alert, {
622
+ severity: props.enrolled ? 'success' : 'info',
623
+ children: props.enrolled ? `Enrolled ${props.enrolled} ${props.enrolled === 1 ? 'person' : 'people'}. Their first step goes out in the mailbox's sending hours.` : 'Nobody was enrolled.'
624
+ }),
625
+ refused.length ? /*#__PURE__*/ _jsxs(Stack, {
626
+ spacing: 1,
627
+ children: [
628
+ /*#__PURE__*/ _jsx(Typography, {
629
+ variant: "subtitle2",
630
+ children: "Not enrolled, checked again just now:"
631
+ }),
632
+ refused.map((result)=>{
633
+ var _result_email;
634
+ return /*#__PURE__*/ _jsxs(Stack, {
635
+ spacing: 0.25,
636
+ children: [
637
+ /*#__PURE__*/ _jsx(Typography, {
638
+ variant: "body2",
639
+ children: (_result_email = result.email) != null ? _result_email : result.contactId
640
+ }),
641
+ result.blocks.map((block)=>/*#__PURE__*/ _jsx(Typography, {
642
+ variant: "body2",
643
+ color: "text.secondary",
644
+ children: block.reason
645
+ }, block.code))
646
+ ]
647
+ }, result.contactId);
648
+ })
649
+ ]
650
+ }) : null
651
+ ]
652
+ });
653
+ }
654
+ export default OutreachEnrollDialog;
655
+
656
+ //# sourceMappingURL=enroll-dialog.js.map