@aglyn/plugins-email 1.0.0-beta.179 → 1.0.0-beta.181

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 (65) hide show
  1. package/README.md +2 -1
  2. package/package.json +17 -17
  3. package/src/lib/components/email-org-mount.d.ts +116 -0
  4. package/src/lib/components/email-org-mount.js +211 -0
  5. package/src/lib/components/email-org-mount.js.map +1 -0
  6. package/src/lib/components/email-topic-detail.d.ts +2 -1
  7. package/src/lib/components/email-topic-detail.js +7 -1
  8. package/src/lib/components/email-topic-detail.js.map +1 -1
  9. package/src/lib/components/email-topics-card.d.ts +2 -1
  10. package/src/lib/components/email-topics-card.js +7 -1
  11. package/src/lib/components/email-topics-card.js.map +1 -1
  12. package/src/lib/components/email-zones.d.ts +28 -1
  13. package/src/lib/components/email-zones.js.map +1 -1
  14. package/src/lib/components/emails-console-page.d.ts +4 -0
  15. package/src/lib/components/emails-console-page.js +107 -5
  16. package/src/lib/components/emails-console-page.js.map +1 -1
  17. package/src/lib/components/list-detail-card.d.ts +19 -3
  18. package/src/lib/components/list-detail-card.js +52 -13
  19. package/src/lib/components/list-detail-card.js.map +1 -1
  20. package/src/lib/components/list-edit-card.d.ts +2 -1
  21. package/src/lib/components/list-edit-card.js +56 -17
  22. package/src/lib/components/list-edit-card.js.map +1 -1
  23. package/src/lib/components/lists-card.d.ts +2 -1
  24. package/src/lib/components/lists-card.js +5 -5
  25. package/src/lib/components/lists-card.js.map +1 -1
  26. package/src/lib/components/org-email-templates-card.d.ts +34 -0
  27. package/src/lib/components/org-email-templates-card.js +470 -0
  28. package/src/lib/components/org-email-templates-card.js.map +1 -0
  29. package/src/lib/components/org-sending-card.d.ts +30 -0
  30. package/src/lib/components/org-sending-card.js +680 -0
  31. package/src/lib/components/org-sending-card.js.map +1 -0
  32. package/src/lib/components/org-sending-domain-detail.d.ts +28 -0
  33. package/src/lib/components/org-sending-domain-detail.js +497 -0
  34. package/src/lib/components/org-sending-domain-detail.js.map +1 -0
  35. package/src/lib/components/org-site-suppressions.d.ts +20 -0
  36. package/src/lib/components/org-site-suppressions.js +99 -0
  37. package/src/lib/components/org-site-suppressions.js.map +1 -0
  38. package/src/lib/components/org-suppressions-card.d.ts +22 -0
  39. package/src/lib/components/org-suppressions-card.js +196 -0
  40. package/src/lib/components/org-suppressions-card.js.map +1 -0
  41. package/src/lib/components/sending-domain-detail.d.ts +12 -0
  42. package/src/lib/components/sending-domain-detail.js +4 -1
  43. package/src/lib/components/sending-domain-detail.js.map +1 -1
  44. package/src/lib/components/suppression-totals.d.ts +63 -0
  45. package/src/lib/components/suppression-totals.js +105 -0
  46. package/src/lib/components/suppression-totals.js.map +1 -0
  47. package/src/lib/components/suppressions-card.js +7 -80
  48. package/src/lib/components/suppressions-card.js.map +1 -1
  49. package/src/lib/components/use-org-email-topics.d.ts +9 -1
  50. package/src/lib/components/use-org-email-topics.js +4 -3
  51. package/src/lib/components/use-org-email-topics.js.map +1 -1
  52. package/src/lib/components/use-org-sending-views.d.ts +39 -0
  53. package/src/lib/components/use-org-sending-views.js +124 -0
  54. package/src/lib/components/use-org-sending-views.js.map +1 -0
  55. package/src/lib/declarations.console-server.d.ts +31 -0
  56. package/src/lib/declarations.console-server.js +43 -0
  57. package/src/lib/declarations.console-server.js.map +1 -0
  58. package/src/lib/model/sending-domain-status.d.ts +36 -0
  59. package/src/lib/model/sending-domain-status.js +46 -0
  60. package/src/lib/model/sending-domain-status.js.map +1 -1
  61. package/src/lib/plugin.js +29 -1
  62. package/src/lib/plugin.js.map +1 -1
  63. package/src/lib/server.d.ts +27 -0
  64. package/src/lib/server.js +130 -5
  65. package/src/lib/server.js.map +1 -1
@@ -0,0 +1,680 @@
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 { ICON_VARIANT_CLOSE } from "@aglyn/shared-data-enums";
21
+ import { mdiDeleteOutline, mdiDnsOutline, mdiEmailCheckOutline, mdiRefresh } from "@aglyn/shared-data-mdi";
22
+ import { AppLink, CardDisplay, Container, MdiIcon, SrOnly, useConfirmationContext } from "@aglyn/shared-ui-jsx";
23
+ import { ListPagination } from "@aglyn/shared-ui-jsx/components/list-pagination.component";
24
+ import { NavigationDrawerComponent } from "@aglyn/shared-ui-jsx/components/navigation-drawer.component";
25
+ import RowActionsMenu from "@aglyn/shared-ui-jsx/components/row-actions-menu.component";
26
+ import { ScrollTable } from "@aglyn/shared-ui-jsx/components/scroll-table.component";
27
+ import { useSnackbar } from "@aglyn/shared-ui-snackstack";
28
+ import { useSendingApi } from "@aglyn/tenant-feature-instance/hooks/use-sending-identity-api";
29
+ import { Alert, Button, Chip, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, IconButton, Stack, TableBody, TableCell, TableHead, TableRow, TextField, Typography } from "@mui/material";
30
+ import { useRouter } from "next/navigation";
31
+ import { useCallback, useRef, useState } from "react";
32
+ import { describeOrgSendingDomainRemoval, describeSendingDomain, INCONCLUSIVE_CHECK } from "../model/sending-domain-status.js";
33
+ import { OrgSiteSelect, orgSiteEmailsPath, orgSiteName, orgSitePageLabel, useEmailOrgMount, useOrgSitePage } from "./email-org-mount.js";
34
+ import SendingSenderDrawer from "./sending-sender-drawer.js";
35
+ import { useOrgSendingViews } from "./use-org-sending-views.js";
36
+ /** How a site's identity reads in one chip. */ function identityChip(view) {
37
+ if (view.refusal) return {
38
+ label: 'Cannot send',
39
+ color: 'warning'
40
+ };
41
+ if (view.identitySource === 'custom') {
42
+ return {
43
+ label: 'Own domain',
44
+ color: 'primary'
45
+ };
46
+ }
47
+ return {
48
+ label: 'Shared address',
49
+ color: 'default'
50
+ };
51
+ }
52
+ /**
53
+ * WHO EVERY SITE'S MAIL COMES FROM, AND THE DOMAINS THE ORGANIZATION PROVED.
54
+ *
55
+ * The two halves of a site's Sending section, over the organization. A domain
56
+ * is proved once by the org — publishing its records is a statement about a
57
+ * zone — and each site then chooses which proved domain it sends as, so the
58
+ * page is two tables: the sites, each with what it sends as right now and its
59
+ * default sender, and the domains, each with the sites sending as it.
60
+ *
61
+ * Every site row is read through the same route the site's own page reads,
62
+ * so what it says is what a campaign sent as that site would get. The reads
63
+ * are one per site over one page of sites, and the org's domains come back on
64
+ * each of them, so there is no separate read for the domain table. What the
65
+ * domain table says about which sites send as a domain is what the pages read
66
+ * so far say, and the removal confirmation counts the sites not yet read.
67
+ *
68
+ * Managing — adding a domain, checking it, choosing what a site sends as, or
69
+ * editing a sender — needs the organization admin role, exactly as on a
70
+ * site's page, and the page says so to anybody else rather than offering
71
+ * controls that would refuse.
72
+ */ export function OrgSendingCard(props) {
73
+ const mount = useEmailOrgMount();
74
+ return mount ? /*#__PURE__*/ _jsx(OrgSendingTables, {
75
+ mount: mount,
76
+ basePath: props.basePath
77
+ }) : null;
78
+ }
79
+ OrgSendingCard.displayName = 'OrgSendingCard';
80
+ function OrgSendingTables(props) {
81
+ var _ref, _views_senderEditor_hostId, _ref1;
82
+ const { mount, basePath } = props;
83
+ const call = useSendingApi();
84
+ const router = useRouter();
85
+ const { confirm } = useConfirmationContext();
86
+ const { enqueueSnackbar } = useSnackbar();
87
+ // Held in a ref for the reason the site card holds its own: the callbacks
88
+ // below depend on it, and the hook hands back a fresh function per render.
89
+ const notifyRef = useRef(enqueueSnackbar);
90
+ notifyRef.current = enqueueSnackbar;
91
+ const sitePage = useOrgSitePage(mount);
92
+ const { sites } = sitePage;
93
+ const { views, errors, loading, reload, first } = useOrgSendingViews(sites);
94
+ const canManage = (first == null ? void 0 : first.canManage) === true;
95
+ const entitled = (first == null ? void 0 : first.entitled) === true;
96
+ const domains = (_ref = first == null ? void 0 : first.domains) != null ? _ref : [];
97
+ const [senderEditor, setSenderEditor] = useState(null);
98
+ const [domainBusy, setDomainBusy] = useState('');
99
+ /** The domain a site is being chosen for, with the site chosen so far. */ const [assigning, setAssigning] = useState(null);
100
+ const [adding, setAdding] = useState(false);
101
+ const [newDomain, setNewDomain] = useState('');
102
+ const [addError, setAddError] = useState('');
103
+ const [addingBusy, setAddingBusy] = useState(false);
104
+ /** A domain's own page, the org route's twin of a site's. */ const domainPath = useCallback((domain)=>`${basePath}/sending/${encodeURIComponent(domain)}`, [
105
+ basePath
106
+ ]);
107
+ /** The sites read so far whose mail leaves on `domain`. */ const sitesSendingAs = (domain)=>mount.hosts.filter((site)=>{
108
+ var _views_site_id;
109
+ return ((_views_site_id = views[site.id]) == null ? void 0 : _views_site_id.selected) === domain;
110
+ });
111
+ const handleVerifyDomain = useCallback(async (domain)=>{
112
+ if (domainBusy) return;
113
+ setDomainBusy(domain);
114
+ const { response, payload } = await call({
115
+ path: 'sending-domains',
116
+ method: 'POST',
117
+ body: {
118
+ orgId: mount.orgId,
119
+ domain,
120
+ action: 'verify'
121
+ }
122
+ });
123
+ setDomainBusy('');
124
+ // No answer from DNS changes nothing about the record; see the site
125
+ // card, which holds the same third outcome.
126
+ if (response.status === 503) {
127
+ return void notifyRef.current(INCONCLUSIVE_CHECK.text, {
128
+ variant: 'info'
129
+ });
130
+ }
131
+ if (!response.ok) {
132
+ var _ref;
133
+ return void notifyRef.current((_ref = payload == null ? void 0 : payload.error) != null ? _ref : 'The check could not run', {
134
+ variant: 'warning'
135
+ });
136
+ }
137
+ await reload();
138
+ notifyRef.current((payload == null ? void 0 : payload.verified) ? `Verified — ${domain} can send` : `Checked. Some of ${domain}’s records are still missing.`, {
139
+ variant: (payload == null ? void 0 : payload.verified) ? 'success' : 'info'
140
+ });
141
+ }, [
142
+ call,
143
+ domainBusy,
144
+ mount.orgId,
145
+ reload
146
+ ]);
147
+ const handleUseDomain = useCallback(async (hostId, domain)=>{
148
+ if (domainBusy) return;
149
+ setDomainBusy(domain);
150
+ const { response, payload } = await call({
151
+ path: 'sending-identity',
152
+ method: 'POST',
153
+ body: {
154
+ hostId,
155
+ domain
156
+ }
157
+ });
158
+ setDomainBusy('');
159
+ if (!response.ok) {
160
+ var _ref;
161
+ return void notifyRef.current((_ref = payload == null ? void 0 : payload.error) != null ? _ref : 'Could not change the identity', {
162
+ variant: 'warning'
163
+ });
164
+ }
165
+ setAssigning(null);
166
+ await reload(hostId);
167
+ // The address the ROUTE settled on, not the one this click asked for.
168
+ const site = orgSiteName(mount, hostId);
169
+ notifyRef.current((payload == null ? void 0 : payload.from) ? `${site} now sends as ${payload.from}` : `${site}’s sending address was changed`, {
170
+ variant: 'success'
171
+ });
172
+ }, [
173
+ call,
174
+ domainBusy,
175
+ mount,
176
+ reload
177
+ ]);
178
+ const handleReleaseDomain = useCallback(async (domain)=>{
179
+ if (domainBusy) return;
180
+ const ok = await confirm(_extends({}, describeOrgSendingDomainRemoval({
181
+ domain,
182
+ sites: mount.hosts.filter((site)=>{
183
+ var _views_site_id;
184
+ return ((_views_site_id = views[site.id]) == null ? void 0 : _views_site_id.selected) === domain;
185
+ }).map((site)=>{
186
+ var _views_site_id;
187
+ return {
188
+ name: orgSiteName(mount, site.id),
189
+ issued: ((_views_site_id = views[site.id]) == null ? void 0 : _views_site_id.platformDomain) === domain
190
+ };
191
+ }),
192
+ unchecked: mount.hosts.filter((site)=>!views[site.id]).length
193
+ }), {
194
+ confirmationButtonProps: {
195
+ color: 'error'
196
+ }
197
+ }))// `confirm` resolves with no value and REJECTS on cancel.
198
+ .then(()=>true).catch(()=>false);
199
+ if (!ok) return;
200
+ setDomainBusy(domain);
201
+ const { response, payload } = await call({
202
+ path: 'sending-domains',
203
+ method: 'DELETE',
204
+ query: {
205
+ orgId: mount.orgId,
206
+ domain
207
+ }
208
+ });
209
+ setDomainBusy('');
210
+ if (!response.ok) {
211
+ var _ref;
212
+ return void notifyRef.current((_ref = payload == null ? void 0 : payload.error) != null ? _ref : 'Could not remove the domain', {
213
+ variant: 'warning'
214
+ });
215
+ }
216
+ await reload();
217
+ }, [
218
+ call,
219
+ confirm,
220
+ domainBusy,
221
+ mount,
222
+ reload,
223
+ views
224
+ ]);
225
+ const handleAdd = useCallback(async ()=>{
226
+ var _ref;
227
+ const domain = newDomain.trim();
228
+ if (!domain || addingBusy) return;
229
+ setAddError('');
230
+ setAddingBusy(true);
231
+ const { response, payload } = await call({
232
+ path: 'sending-domains',
233
+ method: 'POST',
234
+ body: {
235
+ orgId: mount.orgId,
236
+ domain,
237
+ action: 'request'
238
+ }
239
+ });
240
+ setAddingBusy(false);
241
+ if (!response.ok) {
242
+ var _ref1;
243
+ setAddError((_ref1 = payload == null ? void 0 : payload.error) != null ? _ref1 : 'Could not add that domain');
244
+ return;
245
+ }
246
+ setAdding(false);
247
+ setNewDomain('');
248
+ // Straight to the records, which are what the person came for.
249
+ router.push(domainPath((_ref = payload == null ? void 0 : payload.domain) != null ? _ref : domain));
250
+ }, [
251
+ addingBusy,
252
+ call,
253
+ domainPath,
254
+ mount.orgId,
255
+ newDomain,
256
+ router
257
+ ]);
258
+ /**
259
+ * What may be done to one domain from here, disabled with the reason rather
260
+ * than hidden — the same four entries, gated the same way, as the site
261
+ * card's row menu. The difference is the send-as entry: over the org it
262
+ * asks which site.
263
+ */ const domainActions = (record)=>{
264
+ const working = Boolean(domainBusy);
265
+ const domainRouteReason = !canManage ? 'Managing sending domains needs the organization admin role.' : !entitled ? 'Managing sending domains needs a plan that carries sending as ' + 'your own domain.' : '';
266
+ return [
267
+ {
268
+ key: 'open',
269
+ label: 'Open domain',
270
+ icon: /*#__PURE__*/ _jsx(MdiIcon, {
271
+ path: mdiDnsOutline.path,
272
+ size: 0.8
273
+ }),
274
+ href: domainPath(record.domain)
275
+ },
276
+ {
277
+ key: 'verify',
278
+ label: 'Check DNS',
279
+ icon: /*#__PURE__*/ _jsx(MdiIcon, {
280
+ path: mdiRefresh.path,
281
+ size: 0.8
282
+ }),
283
+ disabled: working || record.status === 'requested' || Boolean(domainRouteReason),
284
+ disabledReason: record.status === 'requested' ? 'No records have been issued for this domain yet, so there is ' + 'nothing to look for. Open it to see what it is waiting on.' : domainRouteReason || undefined,
285
+ onClick: ()=>void handleVerifyDomain(record.domain)
286
+ },
287
+ {
288
+ key: 'use',
289
+ label: 'Send a site’s email as this domain…',
290
+ icon: /*#__PURE__*/ _jsx(MdiIcon, {
291
+ path: mdiEmailCheckOutline.path,
292
+ size: 0.8
293
+ }),
294
+ disabled: working || record.status !== 'verified' || !canManage,
295
+ disabledReason: record.status !== 'verified' ? 'Only a verified domain can be sent as. Publish its records and ' + 'check DNS first.' : !canManage ? 'Choosing what a site sends as needs the organization admin ' + 'role.' : undefined,
296
+ onClick: ()=>{
297
+ var _mount_pickedHostId;
298
+ if (mount.hosts.length === 1) {
299
+ return void handleUseDomain(mount.hosts[0].id, record.domain);
300
+ }
301
+ setAssigning({
302
+ domain: record.domain,
303
+ hostId: (_mount_pickedHostId = mount.pickedHostId) != null ? _mount_pickedHostId : ''
304
+ });
305
+ }
306
+ },
307
+ {
308
+ key: 'release',
309
+ label: 'Remove domain',
310
+ icon: /*#__PURE__*/ _jsx(MdiIcon, {
311
+ path: mdiDeleteOutline.path,
312
+ size: 0.8
313
+ }),
314
+ destructive: true,
315
+ disabled: working || Boolean(domainRouteReason),
316
+ disabledReason: domainRouteReason || undefined,
317
+ onClick: ()=>void handleReleaseDomain(record.domain)
318
+ }
319
+ ];
320
+ };
321
+ const noSites = mount.hostsReady && mount.hosts.length === 0;
322
+ const editorView = senderEditor ? (_views_senderEditor_hostId = views[senderEditor.hostId]) != null ? _views_senderEditor_hostId : null : null;
323
+ return /*#__PURE__*/ _jsxs(CardDisplay, {
324
+ header: "Sending",
325
+ help: pluginDocsHelp('emailCampaigns', {
326
+ anchor: '#sending-domains'
327
+ }),
328
+ contentGutterX: true,
329
+ contentGutterY: true,
330
+ contentBordered: "all",
331
+ HeaderProps: {
332
+ action: canManage ? /*#__PURE__*/ _jsx(Button, {
333
+ size: "small",
334
+ variant: "contained",
335
+ disabled: !entitled,
336
+ onClick: ()=>setAdding(true),
337
+ children: 'Add domain'
338
+ }) : null
339
+ },
340
+ children: [
341
+ /*#__PURE__*/ _jsxs(Stack, {
342
+ spacing: 1.5,
343
+ children: [
344
+ /*#__PURE__*/ _jsx(Typography, {
345
+ variant: "body2",
346
+ color: "text.secondary",
347
+ children: 'Receipts, password resets, booking confirmations and other ' + 'account email send from every site on every plan, including ' + 'Free. A domain is proved once for the whole organization; each ' + 'site then chooses which proved domain its mail leaves on, and ' + 'which senders it may use.'
348
+ }),
349
+ first && !canManage ? /*#__PURE__*/ _jsx(Alert, {
350
+ severity: "info",
351
+ children: 'Adding a domain, choosing what a site sends as and editing its ' + 'senders need the organization admin role. You can see the ' + 'current state here.'
352
+ }) : null,
353
+ /*#__PURE__*/ _jsx(Typography, {
354
+ variant: "body2",
355
+ sx: {
356
+ fontWeight: 'bold'
357
+ },
358
+ children: 'Sites'
359
+ }),
360
+ noSites ? /*#__PURE__*/ _jsx(Typography, {
361
+ variant: "body2",
362
+ color: "text.secondary",
363
+ children: 'This organization has no sites yet. What a site sends as is ' + 'chosen per site, so there is nothing to show until there is one.'
364
+ }) : /*#__PURE__*/ _jsxs(ScrollTable, {
365
+ size: "small",
366
+ children: [
367
+ /*#__PURE__*/ _jsx(TableHead, {
368
+ children: /*#__PURE__*/ _jsxs(TableRow, {
369
+ children: [
370
+ /*#__PURE__*/ _jsx(TableCell, {
371
+ children: 'Site'
372
+ }),
373
+ /*#__PURE__*/ _jsx(TableCell, {
374
+ children: 'Sends as'
375
+ }),
376
+ /*#__PURE__*/ _jsx(TableCell, {
377
+ children: 'Status'
378
+ }),
379
+ /*#__PURE__*/ _jsx(TableCell, {
380
+ children: 'Default sender'
381
+ }),
382
+ /*#__PURE__*/ _jsx(TableCell, {
383
+ align: "right",
384
+ children: /*#__PURE__*/ _jsx(SrOnly, {
385
+ children: 'Actions'
386
+ })
387
+ })
388
+ ]
389
+ })
390
+ }),
391
+ /*#__PURE__*/ _jsx(TableBody, {
392
+ children: sites.map((site)=>{
393
+ var _ref, _sender_from;
394
+ var _view_refusal;
395
+ const view = views[site.id];
396
+ const error = errors[site.id];
397
+ const sendingPage = orgSiteEmailsPath(mount, site.id, 'sending');
398
+ const sender = view == null ? void 0 : view.senders.find((entry)=>entry.isDefault);
399
+ const chip = view ? identityChip(view) : null;
400
+ return /*#__PURE__*/ _jsxs(TableRow, {
401
+ children: [
402
+ /*#__PURE__*/ _jsx(TableCell, {
403
+ children: sendingPage ? /*#__PURE__*/ _jsx(AppLink, {
404
+ href: sendingPage,
405
+ children: orgSiteName(mount, site.id)
406
+ }) : orgSiteName(mount, site.id)
407
+ }),
408
+ /*#__PURE__*/ _jsx(TableCell, {
409
+ sx: {
410
+ fontFamily: view ? 'monospace' : undefined
411
+ },
412
+ children: view ? (_ref = (_view_refusal = view.refusal) == null ? void 0 : _view_refusal.message) != null ? _ref : view.identity : /*#__PURE__*/ _jsx(Typography, {
413
+ variant: "body2",
414
+ color: "text.secondary",
415
+ children: error || 'Loading…'
416
+ })
417
+ }),
418
+ /*#__PURE__*/ _jsx(TableCell, {
419
+ children: chip ? /*#__PURE__*/ _jsx(Chip, {
420
+ size: "small",
421
+ label: chip.label,
422
+ color: chip.color
423
+ }) : null
424
+ }),
425
+ /*#__PURE__*/ _jsx(TableCell, {
426
+ children: sender ? [
427
+ sender.fromName,
428
+ (_sender_from = sender.from) != null ? _sender_from : `${sender.localPart}@`
429
+ ].filter(Boolean).join(' · ') : view ? '—' : null
430
+ }),
431
+ /*#__PURE__*/ _jsx(TableCell, {
432
+ align: "right",
433
+ children: view && canManage ? /*#__PURE__*/ _jsx(Button, {
434
+ size: "small",
435
+ onClick: ()=>{
436
+ var _ref;
437
+ return setSenderEditor({
438
+ hostId: site.id,
439
+ senderId: (_ref = sender == null ? void 0 : sender.id) != null ? _ref : null
440
+ });
441
+ },
442
+ children: 'Edit sender'
443
+ }) : null
444
+ })
445
+ ]
446
+ }, site.id);
447
+ })
448
+ })
449
+ ]
450
+ }),
451
+ noSites ? null : /*#__PURE__*/ _jsx(ListPagination, {
452
+ page: sitePage.page,
453
+ pageSize: sitePage.pageSize,
454
+ rowCount: sites.length,
455
+ count: sitePage.count,
456
+ onPageChange: sitePage.setPage,
457
+ labelDisplayedRows: orgSitePageLabel
458
+ }),
459
+ /*#__PURE__*/ _jsx(Typography, {
460
+ variant: "body2",
461
+ sx: {
462
+ fontWeight: 'bold'
463
+ },
464
+ children: 'Domains'
465
+ }),
466
+ first && !entitled ? /*#__PURE__*/ _jsx(Typography, {
467
+ variant: "body2",
468
+ color: "text.secondary",
469
+ children: (first.customDomainPlan ? `Sending as your own domain starts on the ${first.customDomainPlan} plan. ` : '') + 'Until then each site sends on a shared sending address, whose ' + 'delivery reputation is pooled with the other sites on it.'
470
+ }) : null,
471
+ !first ? /*#__PURE__*/ _jsx(Typography, {
472
+ variant: "body2",
473
+ color: "text.secondary",
474
+ children: noSites ? 'Domains are listed once the organization has a site.' : loading ? 'Loading…' : 'The organization’s domains could not be read, because no ' + 'site’s sending identity could.'
475
+ }) : domains.length ? /*#__PURE__*/ _jsxs(ScrollTable, {
476
+ size: "small",
477
+ children: [
478
+ /*#__PURE__*/ _jsx(TableHead, {
479
+ children: /*#__PURE__*/ _jsxs(TableRow, {
480
+ children: [
481
+ /*#__PURE__*/ _jsx(TableCell, {
482
+ children: 'Domain'
483
+ }),
484
+ /*#__PURE__*/ _jsx(TableCell, {
485
+ children: 'State'
486
+ }),
487
+ /*#__PURE__*/ _jsx(TableCell, {
488
+ children: 'Sites sending as it'
489
+ }),
490
+ /*#__PURE__*/ _jsx(TableCell, {
491
+ align: "right",
492
+ children: /*#__PURE__*/ _jsx(SrOnly, {
493
+ children: 'Actions'
494
+ })
495
+ })
496
+ ]
497
+ })
498
+ }),
499
+ /*#__PURE__*/ _jsx(TableBody, {
500
+ children: domains.map((record)=>{
501
+ var _record_lastIssueError;
502
+ const state = describeSendingDomain({
503
+ status: record.status,
504
+ pendingProvider: record.status === 'requested',
505
+ issueError: (_record_lastIssueError = record.lastIssueError) != null ? _record_lastIssueError : null
506
+ });
507
+ const using = sitesSendingAs(record.domain);
508
+ return /*#__PURE__*/ _jsxs(TableRow, {
509
+ hover: true,
510
+ sx: {
511
+ cursor: 'pointer'
512
+ },
513
+ onClick: ()=>router.push(domainPath(record.domain)),
514
+ children: [
515
+ /*#__PURE__*/ _jsx(TableCell, {
516
+ sx: {
517
+ fontFamily: 'monospace'
518
+ },
519
+ children: record.domain
520
+ }),
521
+ /*#__PURE__*/ _jsx(TableCell, {
522
+ children: /*#__PURE__*/ _jsx(Chip, {
523
+ size: "small",
524
+ label: state.label,
525
+ color: state.color,
526
+ variant: state.sending ? 'filled' : 'outlined'
527
+ })
528
+ }),
529
+ /*#__PURE__*/ _jsx(TableCell, {
530
+ children: using.length ? using.map((site)=>orgSiteName(mount, site.id)).join(', ') : /*#__PURE__*/ _jsx(Typography, {
531
+ variant: "body2",
532
+ color: "text.secondary",
533
+ children: '—'
534
+ })
535
+ }),
536
+ /*#__PURE__*/ _jsx(TableCell, {
537
+ align: "right",
538
+ padding: "none",
539
+ children: /*#__PURE__*/ _jsx(RowActionsMenu, {
540
+ label: record.domain,
541
+ items: domainActions(record)
542
+ })
543
+ })
544
+ ]
545
+ }, record.domain);
546
+ })
547
+ })
548
+ ]
549
+ }) : /*#__PURE__*/ _jsx(Typography, {
550
+ variant: "body2",
551
+ color: "text.secondary",
552
+ children: 'No domain has been added yet.'
553
+ })
554
+ ]
555
+ }),
556
+ senderEditor && editorView ? /*#__PURE__*/ _jsx(SendingSenderDrawer, {
557
+ open: true,
558
+ hostId: senderEditor.hostId,
559
+ view: editorView,
560
+ senderId: senderEditor.senderId,
561
+ onClose: ()=>setSenderEditor(null),
562
+ onSaved: ()=>void reload(senderEditor.hostId)
563
+ }) : null,
564
+ /*#__PURE__*/ _jsxs(Dialog, {
565
+ open: Boolean(assigning),
566
+ onClose: domainBusy ? undefined : ()=>setAssigning(null),
567
+ fullWidth: true,
568
+ maxWidth: "xs",
569
+ children: [
570
+ /*#__PURE__*/ _jsx(DialogTitle, {
571
+ children: `Send as ${(_ref1 = assigning == null ? void 0 : assigning.domain) != null ? _ref1 : ''}`
572
+ }),
573
+ /*#__PURE__*/ _jsxs(DialogContent, {
574
+ children: [
575
+ /*#__PURE__*/ _jsx(DialogContentText, {
576
+ sx: {
577
+ mb: 2
578
+ },
579
+ children: 'The site whose email should leave on this domain. Its receipts, ' + 'account email and campaigns all move to it.'
580
+ }),
581
+ assigning ? /*#__PURE__*/ _jsx(OrgSiteSelect, {
582
+ mount: mount,
583
+ label: "Site",
584
+ value: assigning.hostId,
585
+ onChange: (hostId)=>setAssigning((current)=>current ? _extends({}, current, {
586
+ hostId
587
+ }) : current)
588
+ }) : null
589
+ ]
590
+ }),
591
+ /*#__PURE__*/ _jsxs(DialogActions, {
592
+ children: [
593
+ /*#__PURE__*/ _jsx(Button, {
594
+ onClick: ()=>setAssigning(null),
595
+ disabled: Boolean(domainBusy),
596
+ children: 'Cancel'
597
+ }),
598
+ /*#__PURE__*/ _jsx(Button, {
599
+ variant: "contained",
600
+ disabled: !(assigning == null ? void 0 : assigning.hostId) || Boolean(domainBusy),
601
+ onClick: ()=>assigning ? void handleUseDomain(assigning.hostId, assigning.domain) : undefined,
602
+ children: domainBusy ? 'Changing…' : 'Send as this domain'
603
+ })
604
+ ]
605
+ })
606
+ ]
607
+ }),
608
+ /*#__PURE__*/ _jsx(NavigationDrawerComponent, {
609
+ open: adding,
610
+ anchor: "right",
611
+ variant: "temporary",
612
+ onClose: ()=>setAdding(false),
613
+ AppBarProps: {
614
+ color: 'surface'
615
+ },
616
+ appBarLeft: /*#__PURE__*/ _jsxs(_Fragment, {
617
+ children: [
618
+ /*#__PURE__*/ _jsxs(IconButton, {
619
+ color: "inherit",
620
+ edge: "start",
621
+ onClick: ()=>setAdding(false),
622
+ sx: {
623
+ mr: 2
624
+ },
625
+ children: [
626
+ /*#__PURE__*/ _jsx(MdiIcon, {
627
+ path: ICON_VARIANT_CLOSE.path
628
+ }),
629
+ /*#__PURE__*/ _jsx(SrOnly, {
630
+ children: "close drawer"
631
+ })
632
+ ]
633
+ }),
634
+ /*#__PURE__*/ _jsx(Typography, {
635
+ variant: "h6",
636
+ component: "div",
637
+ children: 'Add a sending domain'
638
+ })
639
+ ]
640
+ }),
641
+ children: /*#__PURE__*/ _jsx(Container, {
642
+ gutterY: true,
643
+ children: /*#__PURE__*/ _jsxs(Stack, {
644
+ spacing: 2,
645
+ children: [
646
+ /*#__PURE__*/ _jsx(Typography, {
647
+ variant: "body2",
648
+ color: "text.secondary",
649
+ children: 'The domain your mail should come from — the part after the @. ' + 'Use a domain you control the DNS for; you will be given ' + 'records to publish on it. Once it verifies, any of this ' + 'organization’s sites can send as it.'
650
+ }),
651
+ /*#__PURE__*/ _jsx(TextField, {
652
+ label: "Domain",
653
+ value: newDomain,
654
+ onChange: (event)=>setNewDomain(event.target.value),
655
+ size: "small",
656
+ autoFocus: true,
657
+ placeholder: "acme.com",
658
+ helperText: "For example acme.com — not an address"
659
+ }),
660
+ addError ? /*#__PURE__*/ _jsx(Alert, {
661
+ severity: "error",
662
+ children: addError
663
+ }) : null,
664
+ /*#__PURE__*/ _jsx(Button, {
665
+ variant: "contained",
666
+ disabled: !newDomain.trim() || addingBusy,
667
+ onClick: ()=>void handleAdd(),
668
+ children: addingBusy ? 'Adding…' : 'Add domain'
669
+ })
670
+ ]
671
+ })
672
+ })
673
+ })
674
+ ]
675
+ });
676
+ }
677
+ OrgSendingTables.displayName = 'OrgSendingTables';
678
+ export default OrgSendingCard;
679
+
680
+ //# sourceMappingURL=org-sending-card.js.map