@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,258 @@
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 } from "react/jsx-runtime";
19
+ import { pluginDocsHelp } from "@aglyn/aglyn";
20
+ import EmptyStateComponent from "@aglyn/shared-ui-jsx/components/empty-state.component";
21
+ import { CardDisplay } from "@aglyn/shared-ui-jsx";
22
+ import { useSnackbar } from "@aglyn/shared-ui-snackstack";
23
+ import { useUser } from "@aglyn/tenant-feature-instance";
24
+ import { Alert, Button, Card, CircularProgress, Stack, Typography } from "@mui/material";
25
+ import { useCallback, useEffect, useRef, useState } from "react";
26
+ import { parseConnectReturnFragment } from "../mailboxes/mailbox-api.js";
27
+ import { MailboxCard } from "./mailbox-card.js";
28
+ import { useOutreachMailboxApi } from "./use-outreach-mailbox-api.js";
29
+ import { useOutreachMailboxes } from "./use-outreach-mailboxes.js";
30
+ const leavePage = (url)=>window.location.assign(url);
31
+ /** The connect button's accessible name, spelled once for the specs. */ export const CONNECT_WITH_GOOGLE_LABEL = 'Connect with Google';
32
+ /** What Google's redirect back said, in the words the panel shows. */ const RETURN_ERROR_MESSAGES = {
33
+ access_denied: 'Google access was not granted, so no mailbox was connected.',
34
+ expired: 'The connection took too long. Connect the mailbox again.',
35
+ google_error: 'Google could not complete the connection. Connect the mailbox again.'
36
+ };
37
+ /** Takes a connect's return fragment out of the address bar without a history entry. */ function stripFragment() {
38
+ try {
39
+ window.history.replaceState(null, '', window.location.pathname + window.location.search);
40
+ } catch (unused) {
41
+ // A restricted document: the connect still finishes.
42
+ }
43
+ }
44
+ /** The browser's IANA timezone, for a new mailbox's sending window. */ function browserTimezone() {
45
+ try {
46
+ return Intl.DateTimeFormat().resolvedOptions().timeZone || undefined;
47
+ } catch (unused) {
48
+ return undefined;
49
+ }
50
+ }
51
+ /**
52
+ * The Mailboxes section (AGL-2978): the organization's connected Google
53
+ * mailboxes, and the connect that adds one.
54
+ *
55
+ * A connect leaves the console for Google's consent screen and comes back
56
+ * here through the callback route, which puts the authorization code in this
57
+ * page's URL fragment. The section reads the fragment once, takes it out of
58
+ * the address bar before anything else, and finishes the connect with the
59
+ * member's own session — the step that refuses a connect started by anyone
60
+ * else.
61
+ */ export function OutreachMailboxesSection(props) {
62
+ var _ref;
63
+ const { orgId, navigate = leavePage } = props;
64
+ const { data: user } = useUser();
65
+ // Effects key on the uid, never on the account object: a fresh object for
66
+ // the same person must not re-run a connect's completion or a fetch.
67
+ const uid = (_ref = user == null ? void 0 : user.uid) != null ? _ref : null;
68
+ const api = useOutreachMailboxApi(orgId);
69
+ const listed = useOutreachMailboxes(orgId);
70
+ const { enqueueSnackbar } = useSnackbar();
71
+ const [availability, setAvailability] = useState({
72
+ status: 'loading'
73
+ });
74
+ const [returned, setReturned] = useState(null);
75
+ const [connecting, setConnecting] = useState(false);
76
+ const returnHandled = useRef(false);
77
+ // The connect's return: read once per mount, before availability, so the
78
+ // code leaves the address bar as early as the page can take it.
79
+ useEffect(()=>{
80
+ if (returnHandled.current || !orgId || !uid) return;
81
+ const fragment = parseConnectReturnFragment(window.location.hash);
82
+ returnHandled.current = true;
83
+ if (!fragment) return;
84
+ stripFragment();
85
+ if (fragment.kind === 'error') {
86
+ setReturned({
87
+ status: 'failed',
88
+ message: RETURN_ERROR_MESSAGES[fragment.reason]
89
+ });
90
+ return;
91
+ }
92
+ setReturned({
93
+ status: 'completing'
94
+ });
95
+ api.complete({
96
+ code: fragment.code,
97
+ state: fragment.state,
98
+ timezone: browserTimezone()
99
+ }).then((done)=>setReturned({
100
+ status: 'connected',
101
+ email: done.mailbox.email,
102
+ created: done.created,
103
+ confirmedAliases: done.confirmedAliases
104
+ })).catch((error)=>setReturned({
105
+ status: 'failed',
106
+ message: error.message
107
+ }));
108
+ }, [
109
+ api,
110
+ orgId,
111
+ uid
112
+ ]);
113
+ useEffect(()=>{
114
+ if (!orgId || !uid) return undefined;
115
+ let current = true;
116
+ setAvailability({
117
+ status: 'loading'
118
+ });
119
+ api.availability().then((answer)=>current && setAvailability(_extends({
120
+ status: 'ready'
121
+ }, answer))).catch((error)=>current && setAvailability({
122
+ status: 'error',
123
+ message: error.message
124
+ }));
125
+ return ()=>{
126
+ current = false;
127
+ };
128
+ }, [
129
+ api,
130
+ orgId,
131
+ uid
132
+ ]);
133
+ const connect = useCallback(async ()=>{
134
+ setConnecting(true);
135
+ try {
136
+ navigate(await api.connect());
137
+ } catch (error) {
138
+ setConnecting(false);
139
+ enqueueSnackbar(error.message, {
140
+ variant: 'error',
141
+ allowDuplicate: true
142
+ });
143
+ }
144
+ }, [
145
+ api,
146
+ enqueueSnackbar,
147
+ navigate
148
+ ]);
149
+ const configured = availability.status === 'ready' && availability.configured;
150
+ const canManageAll = availability.status === 'ready' && availability.canManageAll;
151
+ const connectButton = /*#__PURE__*/ _jsx(Button, {
152
+ variant: "contained",
153
+ disabled: !configured || connecting,
154
+ onClick: ()=>void connect(),
155
+ children: CONNECT_WITH_GOOGLE_LABEL
156
+ });
157
+ const mine = listed.mailboxes.filter((mailbox)=>mailbox.connectedByUid === uid);
158
+ const others = listed.mailboxes.filter((mailbox)=>mailbox.connectedByUid !== uid);
159
+ return /*#__PURE__*/ _jsxs(Stack, {
160
+ spacing: 2,
161
+ children: [
162
+ /*#__PURE__*/ _jsx(CardDisplay, {
163
+ header: "Mailboxes",
164
+ help: pluginDocsHelp('sequences', {
165
+ anchor: '#connect-a-mailbox'
166
+ }),
167
+ contentGutterX: true,
168
+ contentGutterY: true,
169
+ HeaderProps: {
170
+ action: listed.mailboxes.length ? connectButton : undefined
171
+ },
172
+ children: /*#__PURE__*/ _jsxs(Stack, {
173
+ spacing: 1.5,
174
+ children: [
175
+ /*#__PURE__*/ _jsx(Typography, {
176
+ variant: "body2",
177
+ color: "text.secondary",
178
+ children: 'A sequence sends from your own Google mailbox, so a message sits in your Sent mail ' + 'and a reply comes back to your inbox. Each member connects their own.'
179
+ }),
180
+ availability.status === 'ready' && !availability.configured ? /*#__PURE__*/ _jsx(Alert, {
181
+ severity: "info",
182
+ children: "Connecting a Google mailbox is not configured on this deployment."
183
+ }) : null,
184
+ availability.status === 'error' ? /*#__PURE__*/ _jsx(Alert, {
185
+ severity: "error",
186
+ children: availability.message
187
+ }) : null,
188
+ (returned == null ? void 0 : returned.status) === 'completing' ? /*#__PURE__*/ _jsx(Alert, {
189
+ severity: "info",
190
+ icon: /*#__PURE__*/ _jsx(CircularProgress, {
191
+ size: 18
192
+ }),
193
+ children: "Finishing the connection…"
194
+ }) : null,
195
+ (returned == null ? void 0 : returned.status) === 'connected' ? /*#__PURE__*/ _jsx(Alert, {
196
+ severity: "success",
197
+ onClose: ()=>setReturned(null),
198
+ children: `${returned.created ? 'Connected' : 'Reconnected'} ${returned.email}.` + (returned.confirmedAliases.length ? ` Verified ${returned.confirmedAliases.join(', ')} as your own.` : '')
199
+ }) : null,
200
+ (returned == null ? void 0 : returned.status) === 'failed' ? /*#__PURE__*/ _jsx(Alert, {
201
+ severity: "error",
202
+ onClose: ()=>setReturned(null),
203
+ children: returned.message
204
+ }) : null
205
+ ]
206
+ })
207
+ }),
208
+ listed.status === 'loading' ? /*#__PURE__*/ _jsxs(Stack, {
209
+ direction: "row",
210
+ spacing: 1,
211
+ sx: {
212
+ alignItems: 'center'
213
+ },
214
+ role: "status",
215
+ children: [
216
+ /*#__PURE__*/ _jsx(CircularProgress, {
217
+ size: 18
218
+ }),
219
+ /*#__PURE__*/ _jsx(Typography, {
220
+ variant: "body2",
221
+ color: "text.secondary",
222
+ children: "Loading mailboxes…"
223
+ })
224
+ ]
225
+ }) : null,
226
+ listed.status === 'error' ? /*#__PURE__*/ _jsx(Alert, {
227
+ severity: "error",
228
+ children: "The mailboxes could not be loaded. Reload the page to try again."
229
+ }) : null,
230
+ listed.status === 'ready' && !listed.mailboxes.length ? /*#__PURE__*/ _jsx(Card, {
231
+ variant: "outlined",
232
+ children: /*#__PURE__*/ _jsx(EmptyStateComponent, {
233
+ label: "No mailboxes connected",
234
+ description: configured ? 'Connect your Google mailbox to send sequences from your own address.' : undefined,
235
+ action: configured ? connectButton : undefined
236
+ })
237
+ }) : null,
238
+ [
239
+ ...mine,
240
+ ...others
241
+ ].map((mailbox)=>{
242
+ const isMine = mailbox.connectedByUid === uid;
243
+ return /*#__PURE__*/ _jsx(MailboxCard, {
244
+ mailbox: mailbox,
245
+ isMine: isMine,
246
+ canManage: isMine || canManageAll,
247
+ api: api,
248
+ onReconnect: ()=>void connect(),
249
+ reconnecting: connecting || !configured
250
+ }, mailbox.id);
251
+ })
252
+ ]
253
+ });
254
+ }
255
+ OutreachMailboxesSection.displayName = 'OutreachMailboxesSection';
256
+ export default OutreachMailboxesSection;
257
+
258
+ //# sourceMappingURL=mailboxes-section.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/components/mailboxes-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 EmptyStateComponent from '@aglyn/shared-ui-jsx/components/empty-state.component'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport { Alert, Button, Card, CircularProgress, Stack, Typography } from '@mui/material'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport {\n parseConnectReturnFragment,\n type OutreachConnectReturnError,\n type OutreachMailboxAvailability,\n} from '../mailboxes/mailbox-api'\nimport { MailboxCard } from './mailbox-card'\nimport { useOutreachMailboxApi } from './use-outreach-mailbox-api'\nimport { useOutreachMailboxes } from './use-outreach-mailboxes'\n\nexport interface OutreachMailboxesSectionProps {\n /** The organization the hub is mounted under; `null` until the shell knows it. */\n orgId: string | null\n /** Sends the browser to Google's consent screen. A test seam; the page's own location by default. */\n navigate?: (url: string) => void\n}\n\nconst leavePage = (url: string) => window.location.assign(url)\n\n/** The connect button's accessible name, spelled once for the specs. */\nexport const CONNECT_WITH_GOOGLE_LABEL = 'Connect with Google'\n\n/** What Google's redirect back said, in the words the panel shows. */\nconst RETURN_ERROR_MESSAGES: Record<OutreachConnectReturnError, string> = {\n access_denied: 'Google access was not granted, so no mailbox was connected.',\n expired: 'The connection took too long. Connect the mailbox again.',\n google_error: 'Google could not complete the connection. Connect the mailbox again.',\n}\n\ntype Availability =\n | { status: 'loading' }\n | ({ status: 'ready' } & OutreachMailboxAvailability)\n | { status: 'error'; message: string }\n\ntype Returned =\n | { status: 'completing' }\n | { status: 'connected'; email: string; created: boolean; confirmedAliases: string[] }\n | { status: 'failed'; message: string }\n\n/** Takes a connect's return fragment out of the address bar without a history entry. */\nfunction stripFragment(): void {\n try {\n window.history.replaceState(null, '', window.location.pathname + window.location.search)\n } catch {\n // A restricted document: the connect still finishes.\n }\n}\n\n/** The browser's IANA timezone, for a new mailbox's sending window. */\nfunction browserTimezone(): string | undefined {\n try {\n return Intl.DateTimeFormat().resolvedOptions().timeZone || undefined\n } catch {\n return undefined\n }\n}\n\n/**\n * The Mailboxes section (AGL-2978): the organization's connected Google\n * mailboxes, and the connect that adds one.\n *\n * A connect leaves the console for Google's consent screen and comes back\n * here through the callback route, which puts the authorization code in this\n * page's URL fragment. The section reads the fragment once, takes it out of\n * the address bar before anything else, and finishes the connect with the\n * member's own session — the step that refuses a connect started by anyone\n * else.\n */\nexport function OutreachMailboxesSection(props: OutreachMailboxesSectionProps) {\n const { orgId, navigate = leavePage } = props\n const { data: user } = useUser()\n // Effects key on the uid, never on the account object: a fresh object for\n // the same person must not re-run a connect's completion or a fetch.\n const uid = user?.uid ?? null\n const api = useOutreachMailboxApi(orgId)\n const listed = useOutreachMailboxes(orgId)\n const { enqueueSnackbar } = useSnackbar()\n const [availability, setAvailability] = useState<Availability>({ status: 'loading' })\n const [returned, setReturned] = useState<Returned | null>(null)\n const [connecting, setConnecting] = useState(false)\n const returnHandled = useRef(false)\n\n // The connect's return: read once per mount, before availability, so the\n // code leaves the address bar as early as the page can take it.\n useEffect(() => {\n if (returnHandled.current || !orgId || !uid) return\n const fragment = parseConnectReturnFragment(window.location.hash)\n returnHandled.current = true\n if (!fragment) return\n stripFragment()\n if (fragment.kind === 'error') {\n setReturned({ status: 'failed', message: RETURN_ERROR_MESSAGES[fragment.reason] })\n return\n }\n setReturned({ status: 'completing' })\n api\n .complete({ code: fragment.code, state: fragment.state, timezone: browserTimezone() })\n .then((done) =>\n setReturned({\n status: 'connected',\n email: done.mailbox.email,\n created: done.created,\n confirmedAliases: done.confirmedAliases,\n }),\n )\n .catch((error: Error) => setReturned({ status: 'failed', message: error.message }))\n }, [api, orgId, uid])\n\n useEffect(() => {\n if (!orgId || !uid) return undefined\n let current = true\n setAvailability({ status: 'loading' })\n api\n .availability()\n .then((answer) => current && setAvailability({ status: 'ready', ...answer }))\n .catch((error: Error) => current && setAvailability({ status: 'error', message: error.message }))\n return () => {\n current = false\n }\n }, [api, orgId, uid])\n\n const connect = useCallback(async () => {\n setConnecting(true)\n try {\n navigate(await api.connect())\n } catch (error) {\n setConnecting(false)\n enqueueSnackbar((error as Error).message, { variant: 'error', allowDuplicate: true })\n }\n }, [api, enqueueSnackbar, navigate])\n\n const configured = availability.status === 'ready' && availability.configured\n const canManageAll = availability.status === 'ready' && availability.canManageAll\n const connectButton = (\n <Button variant=\"contained\" disabled={!configured || connecting} onClick={() => void connect()}>\n {CONNECT_WITH_GOOGLE_LABEL}\n </Button>\n )\n const mine = listed.mailboxes.filter((mailbox) => mailbox.connectedByUid === uid)\n const others = listed.mailboxes.filter((mailbox) => mailbox.connectedByUid !== uid)\n\n return (\n <Stack spacing={2}>\n <CardDisplay\n header=\"Mailboxes\"\n help={pluginDocsHelp('sequences', { anchor: '#connect-a-mailbox' })}\n contentGutterX\n contentGutterY\n HeaderProps={{ action: listed.mailboxes.length ? connectButton : undefined }}\n >\n <Stack spacing={1.5}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'A sequence sends from your own Google mailbox, so a message sits in your Sent mail ' +\n 'and a reply comes back to your inbox. Each member connects their own.'}\n </Typography>\n {availability.status === 'ready' && !availability.configured ? (\n <Alert severity=\"info\">Connecting a Google mailbox is not configured on this deployment.</Alert>\n ) : null}\n {availability.status === 'error' ? <Alert severity=\"error\">{availability.message}</Alert> : null}\n {returned?.status === 'completing' ? (\n <Alert severity=\"info\" icon={<CircularProgress size={18} />}>\n Finishing the connection…\n </Alert>\n ) : null}\n {returned?.status === 'connected' ? (\n <Alert severity=\"success\" onClose={() => setReturned(null)}>\n {`${returned.created ? 'Connected' : 'Reconnected'} ${returned.email}.` +\n (returned.confirmedAliases.length\n ? ` Verified ${returned.confirmedAliases.join(', ')} as your own.`\n : '')}\n </Alert>\n ) : null}\n {returned?.status === 'failed' ? (\n <Alert severity=\"error\" onClose={() => setReturned(null)}>\n {returned.message}\n </Alert>\n ) : null}\n </Stack>\n </CardDisplay>\n\n {listed.status === 'loading' ? (\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }} role=\"status\">\n <CircularProgress size={18} />\n <Typography variant=\"body2\" color=\"text.secondary\">\n Loading mailboxes…\n </Typography>\n </Stack>\n ) : null}\n {listed.status === 'error' ? (\n <Alert severity=\"error\">The mailboxes could not be loaded. Reload the page to try again.</Alert>\n ) : null}\n {listed.status === 'ready' && !listed.mailboxes.length ? (\n <Card variant=\"outlined\">\n <EmptyStateComponent\n label=\"No mailboxes connected\"\n description={\n configured\n ? 'Connect your Google mailbox to send sequences from your own address.'\n : undefined\n }\n action={configured ? connectButton : undefined}\n />\n </Card>\n ) : null}\n {[...mine, ...others].map((mailbox) => {\n const isMine = mailbox.connectedByUid === uid\n return (\n <MailboxCard\n key={mailbox.id}\n mailbox={mailbox}\n isMine={isMine}\n canManage={isMine || canManageAll}\n api={api}\n onReconnect={() => void connect()}\n reconnecting={connecting || !configured}\n />\n )\n })}\n </Stack>\n )\n}\nOutreachMailboxesSection.displayName = 'OutreachMailboxesSection'\n\nexport default OutreachMailboxesSection\n"],"names":["pluginDocsHelp","EmptyStateComponent","CardDisplay","useSnackbar","useUser","Alert","Button","Card","CircularProgress","Stack","Typography","useCallback","useEffect","useRef","useState","parseConnectReturnFragment","MailboxCard","useOutreachMailboxApi","useOutreachMailboxes","leavePage","url","window","location","assign","CONNECT_WITH_GOOGLE_LABEL","RETURN_ERROR_MESSAGES","access_denied","expired","google_error","stripFragment","history","replaceState","pathname","search","browserTimezone","Intl","DateTimeFormat","resolvedOptions","timeZone","undefined","OutreachMailboxesSection","props","orgId","navigate","data","user","uid","api","listed","enqueueSnackbar","availability","setAvailability","status","returned","setReturned","connecting","setConnecting","returnHandled","current","fragment","hash","kind","message","reason","complete","code","state","timezone","then","done","email","mailbox","created","confirmedAliases","catch","error","answer","connect","variant","allowDuplicate","configured","canManageAll","connectButton","disabled","onClick","mine","mailboxes","filter","connectedByUid","others","spacing","header","help","anchor","contentGutterX","contentGutterY","HeaderProps","action","length","color","severity","icon","size","onClose","join","direction","sx","alignItems","role","label","description","map","isMine","canManage","onReconnect","reconnecting","id","displayName"],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { ConsolePluginPageProps } from '@aglyn/aglyn';
2
+ /**
3
+ * The Sequences hub (AGL-2974), mounted by the console's generic
4
+ * ORGANIZATION-level route at `/[orgSlug]/outreach/<section>`.
5
+ *
6
+ * The shell has already decided everything about access before this renders:
7
+ * the `release_outreach` gate through the nav item's tab id, the
8
+ * `features.outreach` entitlement and the `outreach.use` permission declared
9
+ * on the extension, and org-wide reach. It hands over `hostId: null` and an
10
+ * `orgMount`; each section takes the organization from the mount, and the
11
+ * Sequences section its own pages from the path below it.
12
+ */
13
+ export declare function OutreachConsolePage(props: ConsolePluginPageProps): import("react").JSX.Element;
14
+ export declare namespace OutreachConsolePage {
15
+ var displayName: string;
16
+ }
17
+ export default OutreachConsolePage;
@@ -0,0 +1,81 @@
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 { jsx as _jsx } from "react/jsx-runtime";
18
+ import { HubSections } from "@aglyn/shared-ui-next";
19
+ import ComplianceSection from "./compliance-section.js";
20
+ import MailboxesSection from "./mailboxes-section.js";
21
+ import SequencesSection from "./sequences-section.js";
22
+ /**
23
+ * The body of the section being read, and only that one — a function rather
24
+ * than a map of nodes so an unopened section is never constructed, which is
25
+ * what keeps its listens closed once sections read data.
26
+ */ function sectionBody(section, props) {
27
+ switch(section){
28
+ case 'sequences':
29
+ var _ref, _props_org, _props_segments;
30
+ var _props_orgMount;
31
+ return /*#__PURE__*/ _jsx(SequencesSection, {
32
+ orgId: (_ref = (_props_orgMount = props.orgMount) == null ? void 0 : _props_orgMount.orgId) != null ? _ref : null,
33
+ orgMount: props.orgMount,
34
+ org: (_props_org = props.org) != null ? _props_org : null,
35
+ sectionPath: `${props.basePath}/sequences`,
36
+ subpath: ((_props_segments = props.segments) != null ? _props_segments : []).slice(1),
37
+ mailboxesPath: `${props.basePath}/mailboxes`,
38
+ compliancePath: `${props.basePath}/compliance`
39
+ });
40
+ case 'mailboxes':
41
+ var _ref1;
42
+ var _props_orgMount1;
43
+ return /*#__PURE__*/ _jsx(MailboxesSection, {
44
+ orgId: (_ref1 = (_props_orgMount1 = props.orgMount) == null ? void 0 : _props_orgMount1.orgId) != null ? _ref1 : null
45
+ });
46
+ case 'compliance':
47
+ var _ref2;
48
+ var _props_orgMount2;
49
+ return /*#__PURE__*/ _jsx(ComplianceSection, {
50
+ orgId: (_ref2 = (_props_orgMount2 = props.orgMount) == null ? void 0 : _props_orgMount2.orgId) != null ? _ref2 : null
51
+ });
52
+ default:
53
+ return null;
54
+ }
55
+ }
56
+ /**
57
+ * The Sequences hub (AGL-2974), mounted by the console's generic
58
+ * ORGANIZATION-level route at `/[orgSlug]/outreach/<section>`.
59
+ *
60
+ * The shell has already decided everything about access before this renders:
61
+ * the `release_outreach` gate through the nav item's tab id, the
62
+ * `features.outreach` entitlement and the `outreach.use` permission declared
63
+ * on the extension, and org-wide reach. It hands over `hostId: null` and an
64
+ * `orgMount`; each section takes the organization from the mount, and the
65
+ * Sequences section its own pages from the path below it.
66
+ */ export function OutreachConsolePage(props) {
67
+ const { section, sections, basePath } = props;
68
+ /*
69
+ * Nothing until the URL names a section. The shell redirects a bare
70
+ * `/outreach` to the landing section and holds a spinner while it does, so
71
+ * this state is transient.
72
+ */ if (!section || !(sections == null ? void 0 : sections.length) || !basePath) return null;
73
+ return /*#__PURE__*/ _jsx(HubSections, {
74
+ sections: sections,
75
+ children: sectionBody(section, props)
76
+ });
77
+ }
78
+ OutreachConsolePage.displayName = 'OutreachConsolePage';
79
+ export default OutreachConsolePage;
80
+
81
+ //# sourceMappingURL=outreach-console-page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/components/outreach-console-page.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 type { ConsolePluginPageProps } from '@aglyn/aglyn'\nimport { HubSections } from '@aglyn/shared-ui-next'\nimport type { ReactNode } from 'react'\nimport ComplianceSection from './compliance-section'\nimport MailboxesSection from './mailboxes-section'\nimport type { OutreachConsoleSectionId } from './outreach-console-sections'\nimport SequencesSection from './sequences-section'\n\n/**\n * The body of the section being read, and only that one — a function rather\n * than a map of nodes so an unopened section is never constructed, which is\n * what keeps its listens closed once sections read data.\n */\nfunction sectionBody(section: OutreachConsoleSectionId, props: ConsolePluginPageProps): ReactNode {\n switch (section) {\n case 'sequences':\n return (\n <SequencesSection\n orgId={props.orgMount?.orgId ?? null}\n orgMount={props.orgMount}\n org={(props.org as Record<string, unknown> | undefined) ?? null}\n sectionPath={`${props.basePath}/sequences`}\n subpath={(props.segments ?? []).slice(1)}\n mailboxesPath={`${props.basePath}/mailboxes`}\n compliancePath={`${props.basePath}/compliance`}\n />\n )\n case 'mailboxes':\n return <MailboxesSection orgId={props.orgMount?.orgId ?? null} />\n case 'compliance':\n return <ComplianceSection orgId={props.orgMount?.orgId ?? null} />\n default:\n return null\n }\n}\n\n/**\n * The Sequences hub (AGL-2974), mounted by the console's generic\n * ORGANIZATION-level route at `/[orgSlug]/outreach/<section>`.\n *\n * The shell has already decided everything about access before this renders:\n * the `release_outreach` gate through the nav item's tab id, the\n * `features.outreach` entitlement and the `outreach.use` permission declared\n * on the extension, and org-wide reach. It hands over `hostId: null` and an\n * `orgMount`; each section takes the organization from the mount, and the\n * Sequences section its own pages from the path below it.\n */\nexport function OutreachConsolePage(props: ConsolePluginPageProps) {\n const { section, sections, basePath } = props\n\n /*\n * Nothing until the URL names a section. The shell redirects a bare\n * `/outreach` to the landing section and holds a spinner while it does, so\n * this state is transient.\n */\n if (!section || !sections?.length || !basePath) return null\n\n return (\n <HubSections sections={sections}>\n {sectionBody(section as OutreachConsoleSectionId, props)}\n </HubSections>\n )\n}\nOutreachConsolePage.displayName = 'OutreachConsolePage'\n\nexport default OutreachConsolePage\n"],"names":["HubSections","ComplianceSection","MailboxesSection","SequencesSection","sectionBody","section","props","orgId","orgMount","org","sectionPath","basePath","subpath","segments","slice","mailboxesPath","compliancePath","OutreachConsolePage","sections","length","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAGA,SAASA,WAAW,QAAQ,wBAAuB;AAEnD,OAAOC,uBAAuB,0BAAsB;AACpD,OAAOC,sBAAsB,yBAAqB;AAElD,OAAOC,sBAAsB,yBAAqB;AAElD;;;;CAIC,GACD,SAASC,YAAYC,OAAiC,EAAEC,KAA6B;IACnF,OAAQD;QACN,KAAK;sBAKOC,YAEIA;gBAJHA;YAFX,qBACE,KAACH;gBACCI,KAAK,WAAED,kBAAAA,MAAME,QAAQ,qBAAdF,gBAAgBC,KAAK,mBAAI;gBAChCC,UAAUF,MAAME,QAAQ;gBACxBC,GAAG,GAAGH,aAAAA,MAAMG,GAAG,YAATH,aAAqD;gBAC3DI,aAAa,GAAGJ,MAAMK,QAAQ,CAAC,UAAU,CAAC;gBAC1CC,SAAS,EAACN,kBAAAA,MAAMO,QAAQ,YAAdP,kBAAkB,EAAE,EAAEQ,KAAK,CAAC;gBACtCC,eAAe,GAAGT,MAAMK,QAAQ,CAAC,UAAU,CAAC;gBAC5CK,gBAAgB,GAAGV,MAAMK,QAAQ,CAAC,WAAW,CAAC;;QAGpD,KAAK;;gBAC6BL;YAAhC,qBAAO,KAACJ;gBAAiBK,KAAK,YAAED,mBAAAA,MAAME,QAAQ,qBAAdF,iBAAgBC,KAAK,oBAAI;;QAC3D,KAAK;;gBAC8BD;YAAjC,qBAAO,KAACL;gBAAkBM,KAAK,YAAED,mBAAAA,MAAME,QAAQ,qBAAdF,iBAAgBC,KAAK,oBAAI;;QAC5D;YACE,OAAO;IACX;AACF;AAEA;;;;;;;;;;CAUC,GACD,OAAO,SAASU,oBAAoBX,KAA6B;IAC/D,MAAM,EAAED,OAAO,EAAEa,QAAQ,EAAEP,QAAQ,EAAE,GAAGL;IAExC;;;;GAIC,GACD,IAAI,CAACD,WAAW,EAACa,4BAAAA,SAAUC,MAAM,KAAI,CAACR,UAAU,OAAO;IAEvD,qBACE,KAACX;QAAYkB,UAAUA;kBACpBd,YAAYC,SAAqCC;;AAGxD;AACAW,oBAAoBG,WAAW,GAAG;AAElC,eAAeH,oBAAmB"}
@@ -0,0 +1,43 @@
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
+ */
17
+ import type { ConsoleNavSection } from '@aglyn/aglyn';
18
+ export type OutreachConsoleSectionId = 'sequences' | 'mailboxes' | 'compliance';
19
+ /**
20
+ * The Sequences hub's sections, in rail order (AGL-2974).
21
+ *
22
+ * Read twice and never copied, the CRM's rule: `plugin.ts` registers the list
23
+ * on the org nav item so the shell routes and gates each section, and the hub
24
+ * page switches its body on the id the shell resolves back. The console's
25
+ * `PLUGIN_SECTIONS` title table keeps a checked copy for its server layout.
26
+ *
27
+ * Ids are persisted vocabulary — `/[orgSlug]/outreach/sequences` is a link
28
+ * people keep, and the surface slug stays `outreach` for the same reason the
29
+ * plugin id does (AGL-3199). The sequence list is first because it is where
30
+ * the work is, and so it is where a bare `/outreach` lands.
31
+ *
32
+ * Its LABEL is "All sequences" rather than "Sequences", because the hub is
33
+ * called Sequences and the shell prints the two together — in the tab title,
34
+ * the page header and the breadcrumb. A section named after its surface says
35
+ * one word twice and tells the reader nothing;
36
+ * `plugin-section-tab-titles.spec.ts` refuses it.
37
+ *
38
+ * Every section inherits the nav item's `release_outreach` gate and the
39
+ * extension's `features.outreach` entitlement and `outreach.use` permission;
40
+ * a section that later needs a narrower one declares it, which can only
41
+ * narrow.
42
+ */
43
+ export declare const OUTREACH_CONSOLE_SECTIONS: readonly ConsoleNavSection[];
@@ -0,0 +1,55 @@
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
+ */ /**
17
+ * The Sequences hub's sections, in rail order (AGL-2974).
18
+ *
19
+ * Read twice and never copied, the CRM's rule: `plugin.ts` registers the list
20
+ * on the org nav item so the shell routes and gates each section, and the hub
21
+ * page switches its body on the id the shell resolves back. The console's
22
+ * `PLUGIN_SECTIONS` title table keeps a checked copy for its server layout.
23
+ *
24
+ * Ids are persisted vocabulary — `/[orgSlug]/outreach/sequences` is a link
25
+ * people keep, and the surface slug stays `outreach` for the same reason the
26
+ * plugin id does (AGL-3199). The sequence list is first because it is where
27
+ * the work is, and so it is where a bare `/outreach` lands.
28
+ *
29
+ * Its LABEL is "All sequences" rather than "Sequences", because the hub is
30
+ * called Sequences and the shell prints the two together — in the tab title,
31
+ * the page header and the breadcrumb. A section named after its surface says
32
+ * one word twice and tells the reader nothing;
33
+ * `plugin-section-tab-titles.spec.ts` refuses it.
34
+ *
35
+ * Every section inherits the nav item's `release_outreach` gate and the
36
+ * extension's `features.outreach` entitlement and `outreach.use` permission;
37
+ * a section that later needs a narrower one declares it, which can only
38
+ * narrow.
39
+ */ export const OUTREACH_CONSOLE_SECTIONS = [
40
+ {
41
+ id: 'sequences',
42
+ label: 'All sequences'
43
+ },
44
+ {
45
+ id: 'mailboxes',
46
+ label: 'Mailboxes'
47
+ },
48
+ // The footer's sender identity and the allowed countries (AGL-2980).
49
+ {
50
+ id: 'compliance',
51
+ label: 'Compliance'
52
+ }
53
+ ];
54
+
55
+ //# sourceMappingURL=outreach-console-sections.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/components/outreach-console-sections.ts"],"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\nimport type { ConsoleNavSection } from '@aglyn/aglyn'\n\nexport type OutreachConsoleSectionId = 'sequences' | 'mailboxes' | 'compliance'\n\n/**\n * The Sequences hub's sections, in rail order (AGL-2974).\n *\n * Read twice and never copied, the CRM's rule: `plugin.ts` registers the list\n * on the org nav item so the shell routes and gates each section, and the hub\n * page switches its body on the id the shell resolves back. The console's\n * `PLUGIN_SECTIONS` title table keeps a checked copy for its server layout.\n *\n * Ids are persisted vocabulary — `/[orgSlug]/outreach/sequences` is a link\n * people keep, and the surface slug stays `outreach` for the same reason the\n * plugin id does (AGL-3199). The sequence list is first because it is where\n * the work is, and so it is where a bare `/outreach` lands.\n *\n * Its LABEL is \"All sequences\" rather than \"Sequences\", because the hub is\n * called Sequences and the shell prints the two together — in the tab title,\n * the page header and the breadcrumb. A section named after its surface says\n * one word twice and tells the reader nothing;\n * `plugin-section-tab-titles.spec.ts` refuses it.\n *\n * Every section inherits the nav item's `release_outreach` gate and the\n * extension's `features.outreach` entitlement and `outreach.use` permission;\n * a section that later needs a narrower one declares it, which can only\n * narrow.\n */\nexport const OUTREACH_CONSOLE_SECTIONS: readonly ConsoleNavSection[] = [\n { id: 'sequences', label: 'All sequences' },\n { id: 'mailboxes', label: 'Mailboxes' },\n // The footer's sender identity and the allowed countries (AGL-2980).\n { id: 'compliance', label: 'Compliance' },\n]\n"],"names":["OUTREACH_CONSOLE_SECTIONS","id","label"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAMD;;;;;;;;;;;;;;;;;;;;;;;CAuBC,GACD,OAAO,MAAMA,4BAA0D;IACrE;QAAEC,IAAI;QAAaC,OAAO;IAAgB;IAC1C;QAAED,IAAI;QAAaC,OAAO;IAAY;IACtC,qEAAqE;IACrE;QAAED,IAAI;QAAcC,OAAO;IAAa;CACzC,CAAA"}
@@ -0,0 +1,51 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { OutreachEnrollmentStatus, OutreachSequenceStatus, OutreachStopReason } from '../model/outreach.types';
3
+ import type { OutreachLoadStatus } from './use-outreach-data';
4
+ /**
5
+ * The pieces every Outreach page shares (AGL-2980): the words for each
6
+ * status, the states a read can be in, and a link that navigates inside the
7
+ * console without reloading it.
8
+ */
9
+ export declare const OUTREACH_SEQUENCE_STATUS_LABELS: Record<OutreachSequenceStatus, string>;
10
+ export declare const OUTREACH_ENROLLMENT_STATUS_LABELS: Record<OutreachEnrollmentStatus, string>;
11
+ /** Why an enrollment is not active, as the table says it. */
12
+ export declare const OUTREACH_STOP_REASON_LABELS: Record<OutreachStopReason, string>;
13
+ export declare function OutreachSequenceStatusChip(props: {
14
+ status: OutreachSequenceStatus;
15
+ }): import("react").JSX.Element;
16
+ export declare function OutreachEnrollmentStatusChip(props: {
17
+ status: OutreachEnrollmentStatus;
18
+ }): import("react").JSX.Element;
19
+ /** Progress for a read still on its way. */
20
+ export declare function OutreachLoading(props: {
21
+ label: string;
22
+ }): import("react").JSX.Element;
23
+ /** The words a refused read shows when the route gave none of its own. */
24
+ export declare const OUTREACH_REFUSED_MESSAGE = "You don't have access to this in Sequences. Ask an organization owner or admin for the Use Sequences permission.";
25
+ /**
26
+ * What a failed or refused read shows. A refusal is a statement about the
27
+ * reader, not a fault, so it reads as information and offers no retry.
28
+ */
29
+ export declare function OutreachLoadProblem(props: {
30
+ status: Extract<OutreachLoadStatus, 'error' | 'refused'>;
31
+ /** What could not be read, for the error sentence. */
32
+ what: string;
33
+ message?: string | null;
34
+ onRetry?: () => void;
35
+ }): import("react").JSX.Element;
36
+ /** Navigation inside the console, without reloading it. */
37
+ export declare function useOutreachNavigate(): (href: string) => void;
38
+ /**
39
+ * A link inside the console: a real anchor, so it opens in a new tab when
40
+ * asked to, and a client navigation otherwise.
41
+ */
42
+ export declare function OutreachLink(props: {
43
+ href: string;
44
+ children: ReactNode;
45
+ underline?: 'hover' | 'always' | 'none';
46
+ }): import("react").JSX.Element;
47
+ /**
48
+ * A moment as a reader in `timeZone` reads it — "Sep 22, 9:14 AM CDT" — or
49
+ * in the reader's own zone when none is known.
50
+ */
51
+ export declare function formatOutreachTime(ms: number | null | undefined, timeZone?: string | null): string;