@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.
- package/README.md +2 -1
- package/package.json +17 -17
- package/src/lib/components/email-org-mount.d.ts +116 -0
- package/src/lib/components/email-org-mount.js +211 -0
- package/src/lib/components/email-org-mount.js.map +1 -0
- package/src/lib/components/email-topic-detail.d.ts +2 -1
- package/src/lib/components/email-topic-detail.js +7 -1
- package/src/lib/components/email-topic-detail.js.map +1 -1
- package/src/lib/components/email-topics-card.d.ts +2 -1
- package/src/lib/components/email-topics-card.js +7 -1
- package/src/lib/components/email-topics-card.js.map +1 -1
- package/src/lib/components/email-zones.d.ts +28 -1
- package/src/lib/components/email-zones.js.map +1 -1
- package/src/lib/components/emails-console-page.d.ts +4 -0
- package/src/lib/components/emails-console-page.js +107 -5
- package/src/lib/components/emails-console-page.js.map +1 -1
- package/src/lib/components/list-detail-card.d.ts +19 -3
- package/src/lib/components/list-detail-card.js +52 -13
- package/src/lib/components/list-detail-card.js.map +1 -1
- package/src/lib/components/list-edit-card.d.ts +2 -1
- package/src/lib/components/list-edit-card.js +56 -17
- package/src/lib/components/list-edit-card.js.map +1 -1
- package/src/lib/components/lists-card.d.ts +2 -1
- package/src/lib/components/lists-card.js +5 -5
- package/src/lib/components/lists-card.js.map +1 -1
- package/src/lib/components/org-email-templates-card.d.ts +34 -0
- package/src/lib/components/org-email-templates-card.js +470 -0
- package/src/lib/components/org-email-templates-card.js.map +1 -0
- package/src/lib/components/org-sending-card.d.ts +30 -0
- package/src/lib/components/org-sending-card.js +680 -0
- package/src/lib/components/org-sending-card.js.map +1 -0
- package/src/lib/components/org-sending-domain-detail.d.ts +28 -0
- package/src/lib/components/org-sending-domain-detail.js +497 -0
- package/src/lib/components/org-sending-domain-detail.js.map +1 -0
- package/src/lib/components/org-site-suppressions.d.ts +20 -0
- package/src/lib/components/org-site-suppressions.js +99 -0
- package/src/lib/components/org-site-suppressions.js.map +1 -0
- package/src/lib/components/org-suppressions-card.d.ts +22 -0
- package/src/lib/components/org-suppressions-card.js +196 -0
- package/src/lib/components/org-suppressions-card.js.map +1 -0
- package/src/lib/components/sending-domain-detail.d.ts +12 -0
- package/src/lib/components/sending-domain-detail.js +4 -1
- package/src/lib/components/sending-domain-detail.js.map +1 -1
- package/src/lib/components/suppression-totals.d.ts +63 -0
- package/src/lib/components/suppression-totals.js +105 -0
- package/src/lib/components/suppression-totals.js.map +1 -0
- package/src/lib/components/suppressions-card.js +7 -80
- package/src/lib/components/suppressions-card.js.map +1 -1
- package/src/lib/components/use-org-email-topics.d.ts +9 -1
- package/src/lib/components/use-org-email-topics.js +4 -3
- package/src/lib/components/use-org-email-topics.js.map +1 -1
- package/src/lib/components/use-org-sending-views.d.ts +39 -0
- package/src/lib/components/use-org-sending-views.js +124 -0
- package/src/lib/components/use-org-sending-views.js.map +1 -0
- package/src/lib/declarations.console-server.d.ts +31 -0
- package/src/lib/declarations.console-server.js +43 -0
- package/src/lib/declarations.console-server.js.map +1 -0
- package/src/lib/model/sending-domain-status.d.ts +36 -0
- package/src/lib/model/sending-domain-status.js +46 -0
- package/src/lib/model/sending-domain-status.js.map +1 -1
- package/src/lib/plugin.js +29 -1
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/server.d.ts +27 -0
- package/src/lib/server.js +130 -5
- 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
|