@aglyn/plugins-email 1.0.0-beta.234 → 1.0.0-beta.236

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 (31) hide show
  1. package/package.json +16 -16
  2. package/src/lib/components/email-screens-card.js +2 -2
  3. package/src/lib/components/email-screens-card.js.map +1 -1
  4. package/src/lib/components/email-template-detail.js +2 -4
  5. package/src/lib/components/email-template-detail.js.map +1 -1
  6. package/src/lib/components/email-topic-detail.js +1 -1
  7. package/src/lib/components/email-topic-detail.js.map +1 -1
  8. package/src/lib/components/list-detail-card.js +5 -6
  9. package/src/lib/components/list-detail-card.js.map +1 -1
  10. package/src/lib/components/list-edit-card.js +1 -1
  11. package/src/lib/components/list-edit-card.js.map +1 -1
  12. package/src/lib/components/lists-card.js +2 -2
  13. package/src/lib/components/lists-card.js.map +1 -1
  14. package/src/lib/components/org-email-templates-card.js +4 -4
  15. package/src/lib/components/org-email-templates-card.js.map +1 -1
  16. package/src/lib/components/org-sending-card.js +1 -1
  17. package/src/lib/components/org-sending-card.js.map +1 -1
  18. package/src/lib/components/org-sending-domain-detail.js +1 -1
  19. package/src/lib/components/org-sending-domain-detail.js.map +1 -1
  20. package/src/lib/components/org-suppressions-card.js +1 -1
  21. package/src/lib/components/org-suppressions-card.js.map +1 -1
  22. package/src/lib/components/sending-domain-detail.js +1 -1
  23. package/src/lib/components/sending-domain-detail.js.map +1 -1
  24. package/src/lib/components/sending-domains-card.js +1 -3
  25. package/src/lib/components/sending-domains-card.js.map +1 -1
  26. package/src/lib/components/sending-sender-drawer.js +1 -3
  27. package/src/lib/components/sending-sender-drawer.js.map +1 -1
  28. package/src/lib/components/site-consent-group-card.js +1 -1
  29. package/src/lib/components/site-consent-group-card.js.map +1 -1
  30. package/src/lib/components/suppressions-card.js +1 -1
  31. package/src/lib/components/suppressions-card.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/components/org-sending-card.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 { ICON_VARIANT_CLOSE } from '@aglyn/shared-data-enums'\nimport {\n mdiDeleteOutline,\n mdiDnsOutline,\n mdiEmailCheckOutline,\n mdiRefresh,\n} from '@aglyn/shared-data-mdi'\nimport {\n AppLink,\n CardDisplay,\n Container,\n MdiIcon,\n SrOnly,\n useConfirmationContext,\n} from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { NavigationDrawerComponent } from '@aglyn/shared-ui-jsx/components/navigation-drawer.component'\nimport RowActionsMenu, {\n type RowActionsMenuItem,\n} from '@aglyn/shared-ui-jsx/components/row-actions-menu.component'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n useSendingApi,\n type SendingIdentityView,\n} from '@aglyn/tenant-feature-instance/hooks/use-sending-identity-api'\nimport {\n Alert,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogContentText,\n DialogTitle,\n IconButton,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useRouter } from 'next/navigation'\nimport { useCallback, useRef, useState } from 'react'\nimport {\n describeOrgSendingDomainRemoval,\n describeSendingDomain,\n INCONCLUSIVE_CHECK,\n} from '../model/sending-domain-status'\nimport {\n OrgSiteSelect,\n orgSiteEmailsPath,\n orgSiteName,\n orgSitePageLabel,\n useEmailOrgMount,\n useOrgSitePage,\n type EmailOrgMount,\n} from './email-org-mount'\nimport SendingSenderDrawer from './sending-sender-drawer'\nimport { useOrgSendingViews } from './use-org-sending-views'\n\nexport interface OrgSendingCardProps {\n /** The organization's Emails page, which the domain pages hang beneath. */\n basePath: string\n}\n\n/** How a site's identity reads in one chip. */\nfunction identityChip(view: SendingIdentityView): {\n label: string\n color: 'default' | 'primary' | 'warning'\n} {\n if (view.refusal) return { label: 'Cannot send', color: 'warning' }\n if (view.identitySource === 'custom') {\n return { label: 'Own domain', color: 'primary' }\n }\n return { label: 'Shared address', color: 'default' }\n}\n\n/**\n * WHO EVERY SITE'S MAIL COMES FROM, AND THE DOMAINS THE ORGANIZATION PROVED.\n *\n * The two halves of a site's Sending section, over the organization. A domain\n * is proved once by the org — publishing its records is a statement about a\n * zone — and each site then chooses which proved domain it sends as, so the\n * page is two tables: the sites, each with what it sends as right now and its\n * default sender, and the domains, each with the sites sending as it.\n *\n * Every site row is read through the same route the site's own page reads,\n * so what it says is what a campaign sent as that site would get. The reads\n * are one per site over one page of sites, and the org's domains come back on\n * each of them, so there is no separate read for the domain table. What the\n * domain table says about which sites send as a domain is what the pages read\n * so far say, and the removal confirmation counts the sites not yet read.\n *\n * Managing — adding a domain, checking it, choosing what a site sends as, or\n * editing a sender — needs the organization admin role, exactly as on a\n * site's page, and the page says so to anybody else rather than offering\n * controls that would refuse.\n */\nexport function OrgSendingCard(props: OrgSendingCardProps) {\n const mount = useEmailOrgMount()\n return mount ? (\n <OrgSendingTables mount={mount} basePath={props.basePath} />\n ) : null\n}\nOrgSendingCard.displayName = 'OrgSendingCard'\n\nfunction OrgSendingTables(props: { mount: EmailOrgMount; basePath: string }) {\n const { mount, basePath } = props\n const call = useSendingApi()\n const router = useRouter()\n const { confirm } = useConfirmationContext()\n const { enqueueSnackbar } = useSnackbar()\n // Held in a ref for the reason the site card holds its own: the callbacks\n // below depend on it, and the hook hands back a fresh function per render.\n const notifyRef = useRef(enqueueSnackbar)\n notifyRef.current = enqueueSnackbar\n\n const sitePage = useOrgSitePage(mount)\n const { sites } = sitePage\n const { views, errors, loading, reload, first } = useOrgSendingViews(sites)\n const canManage = first?.canManage === true\n const entitled = first?.entitled === true\n const domains = first?.domains ?? []\n\n const [senderEditor, setSenderEditor] = useState<{\n hostId: string\n senderId: string | null\n } | null>(null)\n const [domainBusy, setDomainBusy] = useState('')\n /** The domain a site is being chosen for, with the site chosen so far. */\n const [assigning, setAssigning] = useState<{\n domain: string\n hostId: string\n } | null>(null)\n const [adding, setAdding] = useState(false)\n const [newDomain, setNewDomain] = useState('')\n const [addError, setAddError] = useState('')\n const [addingBusy, setAddingBusy] = useState(false)\n\n /** A domain's own page, the org route's twin of a site's. */\n const domainPath = useCallback(\n (domain: string) => `${basePath}/sending/${encodeURIComponent(domain)}`,\n [basePath],\n )\n /** The sites read so far whose mail leaves on `domain`. */\n const sitesSendingAs = (domain: string) =>\n mount.hosts.filter((site) => views[site.id]?.selected === domain)\n\n const handleVerifyDomain = useCallback(\n async (domain: string) => {\n if (domainBusy) return\n setDomainBusy(domain)\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'POST',\n body: { orgId: mount.orgId, domain, action: 'verify' },\n })\n setDomainBusy('')\n // No answer from DNS changes nothing about the record; see the site\n // card, which holds the same third outcome.\n if (response.status === 503) {\n return void notifyRef.current(INCONCLUSIVE_CHECK.text, {\n variant: 'info',\n })\n }\n if (!response.ok) {\n return void notifyRef.current(\n payload?.error ?? 'The check could not run',\n { variant: 'warning' },\n )\n }\n await reload()\n notifyRef.current(\n payload?.verified\n ? `Verified — ${domain} can send`\n : `Checked. Some of ${domain}’s records are still missing.`,\n { variant: payload?.verified ? 'success' : 'info' },\n )\n },\n [call, domainBusy, mount.orgId, reload],\n )\n\n const handleUseDomain = useCallback(\n async (hostId: string, domain: string) => {\n if (domainBusy) return\n setDomainBusy(domain)\n const { response, payload } = await call({\n path: 'sending-identity',\n method: 'POST',\n body: { hostId, domain },\n })\n setDomainBusy('')\n if (!response.ok) {\n return void notifyRef.current(\n payload?.error ?? 'Could not change the identity',\n { variant: 'warning' },\n )\n }\n setAssigning(null)\n await reload(hostId)\n // The address the ROUTE settled on, not the one this click asked for.\n const site = orgSiteName(mount, hostId)\n notifyRef.current(\n payload?.from\n ? `${site} now sends as ${payload.from}`\n : `${site}’s sending address was changed`,\n { variant: 'success' },\n )\n },\n [call, domainBusy, mount, reload],\n )\n\n const handleReleaseDomain = useCallback(\n async (domain: string) => {\n if (domainBusy) return\n const ok = await confirm({\n ...describeOrgSendingDomainRemoval({\n domain,\n sites: mount.hosts\n .filter((site) => views[site.id]?.selected === domain)\n .map((site) => ({\n name: orgSiteName(mount, site.id),\n issued: views[site.id]?.platformDomain === domain,\n })),\n unchecked: mount.hosts.filter((site) => !views[site.id]).length,\n }),\n confirmationButtonProps: { color: 'error' },\n })\n // `confirm` resolves with no value and REJECTS on cancel.\n .then(() => true)\n .catch(() => false)\n if (!ok) return\n setDomainBusy(domain)\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'DELETE',\n query: { orgId: mount.orgId, domain },\n })\n setDomainBusy('')\n if (!response.ok) {\n return void notifyRef.current(\n payload?.error ?? 'Could not remove the domain',\n { variant: 'warning' },\n )\n }\n await reload()\n },\n [call, confirm, domainBusy, mount, reload, views],\n )\n\n const handleAdd = useCallback(async () => {\n const domain = newDomain.trim()\n if (!domain || addingBusy) return\n setAddError('')\n setAddingBusy(true)\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'POST',\n body: { orgId: mount.orgId, domain, action: 'request' },\n })\n setAddingBusy(false)\n if (!response.ok) {\n setAddError(payload?.error ?? 'Could not add that domain')\n return\n }\n setAdding(false)\n setNewDomain('')\n // Straight to the records, which are what the person came for.\n router.push(domainPath(payload?.domain ?? domain))\n }, [addingBusy, call, domainPath, mount.orgId, newDomain, router])\n\n /**\n * What may be done to one domain from here, disabled with the reason rather\n * than hidden — the same four entries, gated the same way, as the site\n * card's row menu. The difference is the send-as entry: over the org it\n * asks which site.\n */\n const domainActions = (\n record: SendingIdentityView['domains'][number],\n ): RowActionsMenuItem[] => {\n const working = Boolean(domainBusy)\n const domainRouteReason = !canManage\n ? 'Managing sending domains needs the organization admin role.'\n : !entitled\n ? 'Managing sending domains needs a plan that carries sending as ' +\n 'your own domain.'\n : ''\n return [\n {\n key: 'open',\n label: 'Open domain',\n icon: <MdiIcon path={mdiDnsOutline.path} size={0.8} />,\n href: domainPath(record.domain),\n },\n {\n key: 'verify',\n label: 'Check DNS',\n icon: <MdiIcon path={mdiRefresh.path} size={0.8} />,\n disabled:\n working ||\n record.status === 'requested' ||\n Boolean(domainRouteReason),\n disabledReason:\n record.status === 'requested'\n ? 'No records have been issued for this domain yet, so there is ' +\n 'nothing to look for. Open it to see what it is waiting on.'\n : domainRouteReason || undefined,\n onClick: () => void handleVerifyDomain(record.domain),\n },\n {\n key: 'use',\n label: 'Send a site’s email as this domain…',\n icon: <MdiIcon path={mdiEmailCheckOutline.path} size={0.8} />,\n disabled: working || record.status !== 'verified' || !canManage,\n disabledReason:\n record.status !== 'verified'\n ? 'Only a verified domain can be sent as. Publish its records and ' +\n 'check DNS first.'\n : !canManage\n ? 'Choosing what a site sends as needs the organization admin ' +\n 'role.'\n : undefined,\n onClick: () => {\n if (mount.hosts.length === 1) {\n return void handleUseDomain(mount.hosts[0].id, record.domain)\n }\n setAssigning({\n domain: record.domain,\n hostId: mount.pickedHostId ?? '',\n })\n },\n },\n {\n key: 'release',\n label: 'Remove domain',\n icon: <MdiIcon path={mdiDeleteOutline.path} size={0.8} />,\n destructive: true,\n disabled: working || Boolean(domainRouteReason),\n disabledReason: domainRouteReason || undefined,\n onClick: () => void handleReleaseDomain(record.domain),\n },\n ]\n }\n\n const noSites = mount.hostsReady && mount.hosts.length === 0\n const editorView = senderEditor ? (views[senderEditor.hostId] ?? null) : null\n\n return (\n <CardDisplay\n header=\"Sending\"\n help={pluginDocsHelp('emailCampaigns', { anchor: '#sending-domains' })}\n contentGutterX\n contentGutterY\n contentBordered=\"all\"\n HeaderProps={{\n action: canManage ? (\n <Button\n size=\"small\"\n variant=\"contained\"\n disabled={!entitled}\n onClick={() => setAdding(true)}\n >\n {'Add domain'}\n </Button>\n ) : null,\n }}\n >\n <Stack spacing={1.5}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Receipts, password resets, booking confirmations and other ' +\n 'account email send from every site on every plan, including ' +\n 'Free. A domain is proved once for the whole organization; each ' +\n 'site then chooses which proved domain its mail leaves on, and ' +\n 'which senders it may use.'}\n </Typography>\n\n {first && !canManage ? (\n <Alert severity=\"info\">\n {'Adding a domain, choosing what a site sends as and editing its ' +\n 'senders need the organization admin role. You can see the ' +\n 'current state here.'}\n </Alert>\n ) : null}\n\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {'Sites'}\n </Typography>\n {noSites ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'This organization has no sites yet. What a site sends as is ' +\n 'chosen per site, so there is nothing to show until there is one.'}\n </Typography>\n ) : (\n <ScrollTable size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{'Site'}</TableCell>\n <TableCell>{'Sends as'}</TableCell>\n <TableCell>{'Status'}</TableCell>\n <TableCell>{'Default sender'}</TableCell>\n <TableCell align=\"right\">\n <SrOnly>{'Actions'}</SrOnly>\n </TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {sites.map((site) => {\n const view = views[site.id]\n const error = errors[site.id]\n const sendingPage = orgSiteEmailsPath(mount, site.id, 'sending')\n const sender = view?.senders.find((entry) => entry.isDefault)\n const chip = view ? identityChip(view) : null\n return (\n <TableRow key={site.id}>\n <TableCell>\n {sendingPage ? (\n <AppLink href={sendingPage}>\n {orgSiteName(mount, site.id)}\n </AppLink>\n ) : (\n orgSiteName(mount, site.id)\n )}\n </TableCell>\n <TableCell\n sx={{ fontFamily: view ? 'monospace' : undefined }}\n >\n {view ? (\n (view.refusal?.message ?? view.identity)\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {error || 'Loading…'}\n </Typography>\n )}\n </TableCell>\n <TableCell>\n {chip ? (\n <Chip\n size=\"small\"\n label={chip.label}\n color={chip.color}\n />\n ) : null}\n </TableCell>\n <TableCell>\n {sender\n ? [\n sender.fromName,\n sender.from ?? `${sender.localPart}@`,\n ]\n .filter(Boolean)\n .join(' · ')\n : view\n ? '—'\n : null}\n </TableCell>\n <TableCell align=\"right\">\n {view && canManage ? (\n <Button\n size=\"small\"\n onClick={() =>\n setSenderEditor({\n hostId: site.id,\n senderId: sender?.id ?? null,\n })\n }\n >\n {'Edit sender'}\n </Button>\n ) : null}\n </TableCell>\n </TableRow>\n )\n })}\n </TableBody>\n </ScrollTable>\n )}\n {noSites ? null : (\n <ListPagination\n page={sitePage.page}\n pageSize={sitePage.pageSize}\n rowCount={sites.length}\n count={sitePage.count}\n onPageChange={sitePage.setPage}\n labelDisplayedRows={orgSitePageLabel}\n />\n )}\n\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {'Domains'}\n </Typography>\n {first && !entitled ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {(first.customDomainPlan\n ? `Sending as your own domain starts on the ${first.customDomainPlan} plan. `\n : '') +\n 'Until then each site sends on a shared sending address, whose ' +\n 'delivery reputation is pooled with the other sites on it.'}\n </Typography>\n ) : null}\n {!first ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {noSites\n ? 'Domains are listed once the organization has a site.'\n : loading\n ? 'Loading…'\n : 'The organization’s domains could not be read, because no ' +\n 'site’s sending identity could.'}\n </Typography>\n ) : domains.length ? (\n <ScrollTable size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{'Domain'}</TableCell>\n <TableCell>{'State'}</TableCell>\n <TableCell>{'Sites sending as it'}</TableCell>\n <TableCell align=\"right\">\n <SrOnly>{'Actions'}</SrOnly>\n </TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {domains.map((record) => {\n const state = describeSendingDomain({\n status: record.status,\n pendingProvider: record.status === 'requested',\n issueError: record.lastIssueError ?? null,\n })\n const using = sitesSendingAs(record.domain)\n return (\n <TableRow\n key={record.domain}\n hover\n sx={{ cursor: 'pointer' }}\n onClick={() => router.push(domainPath(record.domain))}\n >\n <TableCell sx={{ fontFamily: 'monospace' }}>\n {record.domain}\n </TableCell>\n <TableCell>\n <Chip\n size=\"small\"\n label={state.label}\n color={state.color}\n variant={state.sending ? 'filled' : 'outlined'}\n />\n </TableCell>\n <TableCell>\n {using.length ? (\n using\n .map((site) => orgSiteName(mount, site.id))\n .join(', ')\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'—'}\n </Typography>\n )}\n </TableCell>\n <TableCell align=\"right\" padding=\"none\">\n <RowActionsMenu\n label={record.domain}\n items={domainActions(record)}\n />\n </TableCell>\n </TableRow>\n )\n })}\n </TableBody>\n </ScrollTable>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No domain has been added yet.'}\n </Typography>\n )}\n </Stack>\n\n {/* One site's sender, in the same drawer the site's own page opens. */}\n {senderEditor && editorView ? (\n <SendingSenderDrawer\n open\n hostId={senderEditor.hostId}\n view={editorView}\n senderId={senderEditor.senderId}\n onClose={() => setSenderEditor(null)}\n onSaved={() => void reload(senderEditor.hostId)}\n />\n ) : null}\n\n <Dialog\n open={Boolean(assigning)}\n onClose={domainBusy ? undefined : () => setAssigning(null)}\n fullWidth\n maxWidth=\"xs\"\n >\n <DialogTitle>{`Send as ${assigning?.domain ?? ''}`}</DialogTitle>\n <DialogContent>\n <DialogContentText sx={{ mb: 2 }}>\n {'The site whose email should leave on this domain. Its receipts, ' +\n 'account email and campaigns all move to it.'}\n </DialogContentText>\n {assigning ? (\n <OrgSiteSelect\n mount={mount}\n label=\"Site\"\n value={assigning.hostId}\n onChange={(hostId) =>\n setAssigning((current) =>\n current ? { ...current, hostId } : current,\n )\n }\n />\n ) : null}\n </DialogContent>\n <DialogActions>\n <Button\n onClick={() => setAssigning(null)}\n disabled={Boolean(domainBusy)}\n >\n {'Cancel'}\n </Button>\n <Button\n variant=\"contained\"\n disabled={!assigning?.hostId || Boolean(domainBusy)}\n onClick={() =>\n assigning\n ? void handleUseDomain(assigning.hostId, assigning.domain)\n : undefined\n }\n >\n {domainBusy ? 'Changing…' : 'Send as this domain'}\n </Button>\n </DialogActions>\n </Dialog>\n\n {/* Adding is a drawer, as on a site's page; see the site card. */}\n <NavigationDrawerComponent\n open={adding}\n anchor=\"right\"\n variant=\"temporary\"\n onClose={() => setAdding(false)}\n AppBarProps={{ color: 'surface' }}\n appBarLeft={\n <>\n <IconButton\n color=\"inherit\"\n edge=\"start\"\n onClick={() => setAdding(false)}\n sx={{ mr: 2 }}\n >\n <MdiIcon path={ICON_VARIANT_CLOSE.path} />\n <SrOnly>close drawer</SrOnly>\n </IconButton>\n <Typography variant=\"h6\" component=\"div\">\n {'Add a sending domain'}\n </Typography>\n </>\n }\n >\n <Container gutterY>\n <Stack spacing={2}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'The domain your mail should come from — the part after the @. ' +\n 'Use a domain you control the DNS for; you will be given ' +\n 'records to publish on it. Once it verifies, any of this ' +\n 'organization’s sites can send as it.'}\n </Typography>\n <TextField\n label=\"Domain\"\n value={newDomain}\n onChange={(event) => setNewDomain(event.target.value)}\n size=\"small\"\n autoFocus\n placeholder=\"acme.com\"\n helperText=\"For example acme.com — not an address\"\n />\n {addError ? <Alert severity=\"error\">{addError}</Alert> : null}\n <Button\n variant=\"contained\"\n disabled={!newDomain.trim() || addingBusy}\n onClick={() => void handleAdd()}\n >\n {addingBusy ? 'Adding…' : 'Add domain'}\n </Button>\n </Stack>\n </Container>\n </NavigationDrawerComponent>\n </CardDisplay>\n )\n}\nOrgSendingTables.displayName = 'OrgSendingTables'\n\nexport default OrgSendingCard\n"],"names":["pluginDocsHelp","ICON_VARIANT_CLOSE","mdiDeleteOutline","mdiDnsOutline","mdiEmailCheckOutline","mdiRefresh","AppLink","CardDisplay","Container","MdiIcon","SrOnly","useConfirmationContext","ListPagination","NavigationDrawerComponent","RowActionsMenu","ScrollTable","useSnackbar","useSendingApi","Alert","Button","Chip","Dialog","DialogActions","DialogContent","DialogContentText","DialogTitle","IconButton","Stack","TableBody","TableCell","TableHead","TableRow","TextField","Typography","useRouter","useCallback","useRef","useState","describeOrgSendingDomainRemoval","describeSendingDomain","INCONCLUSIVE_CHECK","OrgSiteSelect","orgSiteEmailsPath","orgSiteName","orgSitePageLabel","useEmailOrgMount","useOrgSitePage","SendingSenderDrawer","useOrgSendingViews","identityChip","view","refusal","label","color","identitySource","OrgSendingCard","props","mount","OrgSendingTables","basePath","displayName","views","call","router","confirm","enqueueSnackbar","notifyRef","current","sitePage","sites","errors","loading","reload","first","canManage","entitled","domains","senderEditor","setSenderEditor","domainBusy","setDomainBusy","assigning","setAssigning","adding","setAdding","newDomain","setNewDomain","addError","setAddError","addingBusy","setAddingBusy","domainPath","domain","encodeURIComponent","sitesSendingAs","hosts","filter","site","id","selected","handleVerifyDomain","response","payload","path","method","body","orgId","action","status","text","variant","ok","error","verified","handleUseDomain","hostId","from","handleReleaseDomain","map","name","issued","platformDomain","unchecked","length","confirmationButtonProps","then","catch","query","handleAdd","trim","push","domainActions","record","working","Boolean","domainRouteReason","key","icon","size","href","disabled","disabledReason","undefined","onClick","pickedHostId","destructive","noSites","hostsReady","editorView","header","help","anchor","contentGutterX","contentGutterY","contentBordered","HeaderProps","spacing","severity","sx","fontWeight","align","sender","sendingPage","senders","find","entry","isDefault","chip","fontFamily","message","identity","fromName","localPart","join","senderId","page","pageSize","rowCount","count","onPageChange","setPage","labelDisplayedRows","customDomainPlan","state","pendingProvider","issueError","lastIssueError","using","hover","cursor","sending","padding","items","open","onClose","onSaved","fullWidth","maxWidth","mb","value","onChange","AppBarProps","appBarLeft","edge","mr","component","gutterY","event","target","autoFocus","placeholder","helperText"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;;AAEA,SAASA,cAAc,QAAQ,eAAc;AAC7C,SAASC,kBAAkB,QAAQ,2BAA0B;AAC7D,SACEC,gBAAgB,EAChBC,aAAa,EACbC,oBAAoB,EACpBC,UAAU,QACL,yBAAwB;AAC/B,SACEC,OAAO,EACPC,WAAW,EACXC,SAAS,EACTC,OAAO,EACPC,MAAM,EACNC,sBAAsB,QACjB,uBAAsB;AAC7B,SAASC,cAAc,QAAQ,4DAA2D;AAC1F,SAASC,yBAAyB,QAAQ,8DAA6D;AACvG,OAAOC,oBAEA,6DAA4D;AACnE,SAASC,WAAW,QAAQ,yDAAwD;AACpF,SAASC,WAAW,QAAQ,8BAA6B;AACzD,SACEC,aAAa,QAER,gEAA+D;AACtE,SACEC,KAAK,EACLC,MAAM,EACNC,IAAI,EACJC,MAAM,EACNC,aAAa,EACbC,aAAa,EACbC,iBAAiB,EACjBC,WAAW,EACXC,UAAU,EACVC,KAAK,EACLC,SAAS,EACTC,SAAS,EACTC,SAAS,EACTC,QAAQ,EACRC,SAAS,EACTC,UAAU,QACL,gBAAe;AACtB,SAASC,SAAS,QAAQ,kBAAiB;AAC3C,SAASC,WAAW,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,QAAO;AACrD,SACEC,+BAA+B,EAC/BC,qBAAqB,EACrBC,kBAAkB,QACb,oCAAgC;AACvC,SACEC,aAAa,EACbC,iBAAiB,EACjBC,WAAW,EACXC,gBAAgB,EAChBC,gBAAgB,EAChBC,cAAc,QAET,uBAAmB;AAC1B,OAAOC,yBAAyB,6BAAyB;AACzD,SAASC,kBAAkB,QAAQ,6BAAyB;AAO5D,6CAA6C,GAC7C,SAASC,aAAaC,IAAyB;IAI7C,IAAIA,KAAKC,OAAO,EAAE,OAAO;QAAEC,OAAO;QAAeC,OAAO;IAAU;IAClE,IAAIH,KAAKI,cAAc,KAAK,UAAU;QACpC,OAAO;YAAEF,OAAO;YAAcC,OAAO;QAAU;IACjD;IACA,OAAO;QAAED,OAAO;QAAkBC,OAAO;IAAU;AACrD;AAEA;;;;;;;;;;;;;;;;;;;;CAoBC,GACD,OAAO,SAASE,eAAeC,KAA0B;IACvD,MAAMC,QAAQZ;IACd,OAAOY,sBACL,KAACC;QAAiBD,OAAOA;QAAOE,UAAUH,MAAMG,QAAQ;SACtD;AACN;AACAJ,eAAeK,WAAW,GAAG;AAE7B,SAASF,iBAAiBF,KAAiD;cA+OtCK;IA9OnC,MAAM,EAAEJ,KAAK,EAAEE,QAAQ,EAAE,GAAGH;IAC5B,MAAMM,OAAO7C;IACb,MAAM8C,SAAS7B;IACf,MAAM,EAAE8B,OAAO,EAAE,GAAGrD;IACpB,MAAM,EAAEsD,eAAe,EAAE,GAAGjD;IAC5B,0EAA0E;IAC1E,2EAA2E;IAC3E,MAAMkD,YAAY9B,OAAO6B;IACzBC,UAAUC,OAAO,GAAGF;IAEpB,MAAMG,WAAWtB,eAAeW;IAChC,MAAM,EAAEY,KAAK,EAAE,GAAGD;IAClB,MAAM,EAAEP,KAAK,EAAES,MAAM,EAAEC,OAAO,EAAEC,MAAM,EAAEC,KAAK,EAAE,GAAGzB,mBAAmBqB;IACrE,MAAMK,YAAYD,CAAAA,yBAAAA,MAAOC,SAAS,MAAK;IACvC,MAAMC,WAAWF,CAAAA,yBAAAA,MAAOE,QAAQ,MAAK;IACrC,MAAMC,kBAAUH,yBAAAA,MAAOG,OAAO,mBAAI,EAAE;IAEpC,MAAM,CAACC,cAAcC,gBAAgB,GAAGzC,SAG9B;IACV,MAAM,CAAC0C,YAAYC,cAAc,GAAG3C,SAAS;IAC7C,wEAAwE,GACxE,MAAM,CAAC4C,WAAWC,aAAa,GAAG7C,SAGxB;IACV,MAAM,CAAC8C,QAAQC,UAAU,GAAG/C,SAAS;IACrC,MAAM,CAACgD,WAAWC,aAAa,GAAGjD,SAAS;IAC3C,MAAM,CAACkD,UAAUC,YAAY,GAAGnD,SAAS;IACzC,MAAM,CAACoD,YAAYC,cAAc,GAAGrD,SAAS;IAE7C,2DAA2D,GAC3D,MAAMsD,aAAaxD,YACjB,CAACyD,SAAmB,GAAGjC,SAAS,SAAS,EAAEkC,mBAAmBD,SAAS,EACvE;QAACjC;KAAS;IAEZ,yDAAyD,GACzD,MAAMmC,iBAAiB,CAACF,SACtBnC,MAAMsC,KAAK,CAACC,MAAM,CAAC,CAACC;gBAASpC;mBAAAA,EAAAA,iBAAAA,KAAK,CAACoC,KAAKC,EAAE,CAAC,qBAAdrC,eAAgBsC,QAAQ,MAAKP;;IAE5D,MAAMQ,qBAAqBjE,YACzB,OAAOyD;QACL,IAAIb,YAAY;QAChBC,cAAcY;QACd,MAAM,EAAES,QAAQ,EAAEC,OAAO,EAAE,GAAG,MAAMxC,KAAK;YACvCyC,MAAM;YACNC,QAAQ;YACRC,MAAM;gBAAEC,OAAOjD,MAAMiD,KAAK;gBAAEd;gBAAQe,QAAQ;YAAS;QACvD;QACA3B,cAAc;QACd,oEAAoE;QACpE,4CAA4C;QAC5C,IAAIqB,SAASO,MAAM,KAAK,KAAK;YAC3B,OAAO,KAAK1C,UAAUC,OAAO,CAAC3B,mBAAmBqE,IAAI,EAAE;gBACrDC,SAAS;YACX;QACF;QACA,IAAI,CAACT,SAASU,EAAE,EAAE;;YAChB,OAAO,KAAK7C,UAAUC,OAAO,SAC3BmC,2BAAAA,QAASU,KAAK,mBAAI,2BAClB;gBAAEF,SAAS;YAAU;QAEzB;QACA,MAAMtC;QACNN,UAAUC,OAAO,CACfmC,CAAAA,2BAAAA,QAASW,QAAQ,IACb,CAAC,WAAW,EAAErB,OAAO,SAAS,CAAC,GAC/B,CAAC,iBAAiB,EAAEA,OAAO,6BAA6B,CAAC,EAC7D;YAAEkB,SAASR,CAAAA,2BAAAA,QAASW,QAAQ,IAAG,YAAY;QAAO;IAEtD,GACA;QAACnD;QAAMiB;QAAYtB,MAAMiD,KAAK;QAAElC;KAAO;IAGzC,MAAM0C,kBAAkB/E,YACtB,OAAOgF,QAAgBvB;QACrB,IAAIb,YAAY;QAChBC,cAAcY;QACd,MAAM,EAAES,QAAQ,EAAEC,OAAO,EAAE,GAAG,MAAMxC,KAAK;YACvCyC,MAAM;YACNC,QAAQ;YACRC,MAAM;gBAAEU;gBAAQvB;YAAO;QACzB;QACAZ,cAAc;QACd,IAAI,CAACqB,SAASU,EAAE,EAAE;;YAChB,OAAO,KAAK7C,UAAUC,OAAO,SAC3BmC,2BAAAA,QAASU,KAAK,mBAAI,iCAClB;gBAAEF,SAAS;YAAU;QAEzB;QACA5B,aAAa;QACb,MAAMV,OAAO2C;QACb,sEAAsE;QACtE,MAAMlB,OAAOtD,YAAYc,OAAO0D;QAChCjD,UAAUC,OAAO,CACfmC,CAAAA,2BAAAA,QAASc,IAAI,IACT,GAAGnB,KAAK,cAAc,EAAEK,QAAQc,IAAI,EAAE,GACtC,GAAGnB,KAAK,8BAA8B,CAAC,EAC3C;YAAEa,SAAS;QAAU;IAEzB,GACA;QAAChD;QAAMiB;QAAYtB;QAAOe;KAAO;IAGnC,MAAM6C,sBAAsBlF,YAC1B,OAAOyD;QACL,IAAIb,YAAY;QAChB,MAAMgC,KAAK,MAAM/C,QAAQ,aACpB1B,gCAAgC;YACjCsD;YACAvB,OAAOZ,MAAMsC,KAAK,CACfC,MAAM,CAAC,CAACC;oBAASpC;uBAAAA,EAAAA,iBAAAA,KAAK,CAACoC,KAAKC,EAAE,CAAC,qBAAdrC,eAAgBsC,QAAQ,MAAKP;eAC9C0B,GAAG,CAAC,CAACrB;oBAEIpC;uBAFM;oBACd0D,MAAM5E,YAAYc,OAAOwC,KAAKC,EAAE;oBAChCsB,QAAQ3D,EAAAA,iBAAAA,KAAK,CAACoC,KAAKC,EAAE,CAAC,qBAAdrC,eAAgB4D,cAAc,MAAK7B;gBAC7C;;YACF8B,WAAWjE,MAAMsC,KAAK,CAACC,MAAM,CAAC,CAACC,OAAS,CAACpC,KAAK,CAACoC,KAAKC,EAAE,CAAC,EAAEyB,MAAM;QACjE;YACAC,yBAAyB;gBAAEvE,OAAO;YAAQ;WAE1C,0DAA0D;SACzDwE,IAAI,CAAC,IAAM,MACXC,KAAK,CAAC,IAAM;QACf,IAAI,CAACf,IAAI;QACT/B,cAAcY;QACd,MAAM,EAAES,QAAQ,EAAEC,OAAO,EAAE,GAAG,MAAMxC,KAAK;YACvCyC,MAAM;YACNC,QAAQ;YACRuB,OAAO;gBAAErB,OAAOjD,MAAMiD,KAAK;gBAAEd;YAAO;QACtC;QACAZ,cAAc;QACd,IAAI,CAACqB,SAASU,EAAE,EAAE;;YAChB,OAAO,KAAK7C,UAAUC,OAAO,SAC3BmC,2BAAAA,QAASU,KAAK,mBAAI,+BAClB;gBAAEF,SAAS;YAAU;QAEzB;QACA,MAAMtC;IACR,GACA;QAACV;QAAME;QAASe;QAAYtB;QAAOe;QAAQX;KAAM;IAGnD,MAAMmE,YAAY7F,YAAY;;QAC5B,MAAMyD,SAASP,UAAU4C,IAAI;QAC7B,IAAI,CAACrC,UAAUH,YAAY;QAC3BD,YAAY;QACZE,cAAc;QACd,MAAM,EAAEW,QAAQ,EAAEC,OAAO,EAAE,GAAG,MAAMxC,KAAK;YACvCyC,MAAM;YACNC,QAAQ;YACRC,MAAM;gBAAEC,OAAOjD,MAAMiD,KAAK;gBAAEd;gBAAQe,QAAQ;YAAU;QACxD;QACAjB,cAAc;QACd,IAAI,CAACW,SAASU,EAAE,EAAE;;YAChBvB,qBAAYc,2BAAAA,QAASU,KAAK,oBAAI;YAC9B;QACF;QACA5B,UAAU;QACVE,aAAa;QACb,+DAA+D;QAC/DvB,OAAOmE,IAAI,CAACvC,mBAAWW,2BAAAA,QAASV,MAAM,mBAAIA;IAC5C,GAAG;QAACH;QAAY3B;QAAM6B;QAAYlC,MAAMiD,KAAK;QAAErB;QAAWtB;KAAO;IAEjE;;;;;GAKC,GACD,MAAMoE,gBAAgB,CACpBC;QAEA,MAAMC,UAAUC,QAAQvD;QACxB,MAAMwD,oBAAoB,CAAC7D,YACvB,gEACA,CAACC,WACC,mEACA,qBACA;QACN,OAAO;YACL;gBACE6D,KAAK;gBACLpF,OAAO;gBACPqF,oBAAM,KAAChI;oBAAQ8F,MAAMpG,cAAcoG,IAAI;oBAAEmC,MAAM;;gBAC/CC,MAAMhD,WAAWyC,OAAOxC,MAAM;YAChC;YACA;gBACE4C,KAAK;gBACLpF,OAAO;gBACPqF,oBAAM,KAAChI;oBAAQ8F,MAAMlG,WAAWkG,IAAI;oBAAEmC,MAAM;;gBAC5CE,UACEP,WACAD,OAAOxB,MAAM,KAAK,eAClB0B,QAAQC;gBACVM,gBACET,OAAOxB,MAAM,KAAK,cACd,kEACA,+DACA2B,qBAAqBO;gBAC3BC,SAAS,IAAM,KAAK3C,mBAAmBgC,OAAOxC,MAAM;YACtD;YACA;gBACE4C,KAAK;gBACLpF,OAAO;gBACPqF,oBAAM,KAAChI;oBAAQ8F,MAAMnG,qBAAqBmG,IAAI;oBAAEmC,MAAM;;gBACtDE,UAAUP,WAAWD,OAAOxB,MAAM,KAAK,cAAc,CAAClC;gBACtDmE,gBACET,OAAOxB,MAAM,KAAK,aACd,oEACA,qBACA,CAAClC,YACC,gEACA,UACAoE;gBACRC,SAAS;wBAMGtF;oBALV,IAAIA,MAAMsC,KAAK,CAAC4B,MAAM,KAAK,GAAG;wBAC5B,OAAO,KAAKT,gBAAgBzD,MAAMsC,KAAK,CAAC,EAAE,CAACG,EAAE,EAAEkC,OAAOxC,MAAM;oBAC9D;oBACAV,aAAa;wBACXU,QAAQwC,OAAOxC,MAAM;wBACrBuB,MAAM,GAAE1D,sBAAAA,MAAMuF,YAAY,YAAlBvF,sBAAsB;oBAChC;gBACF;YACF;YACA;gBACE+E,KAAK;gBACLpF,OAAO;gBACPqF,oBAAM,KAAChI;oBAAQ8F,MAAMrG,iBAAiBqG,IAAI;oBAAEmC,MAAM;;gBAClDO,aAAa;gBACbL,UAAUP,WAAWC,QAAQC;gBAC7BM,gBAAgBN,qBAAqBO;gBACrCC,SAAS,IAAM,KAAK1B,oBAAoBe,OAAOxC,MAAM;YACvD;SACD;IACH;IAEA,MAAMsD,UAAUzF,MAAM0F,UAAU,IAAI1F,MAAMsC,KAAK,CAAC4B,MAAM,KAAK;IAC3D,MAAMyB,aAAavE,gBAAgBhB,6BAAAA,KAAK,CAACgB,aAAasC,MAAM,CAAC,YAA1BtD,6BAA8B,OAAQ;IAEzE,qBACE,MAACtD;QACC8I,QAAO;QACPC,MAAMtJ,eAAe,kBAAkB;YAAEuJ,QAAQ;QAAmB;QACpEC,cAAc;QACdC,cAAc;QACdC,iBAAgB;QAChBC,aAAa;YACXhD,QAAQjC,0BACN,KAACvD;gBACCuH,MAAK;gBACL5B,SAAQ;gBACR8B,UAAU,CAACjE;gBACXoE,SAAS,IAAM3D,UAAU;0BAExB;iBAED;QACN;;0BAEA,MAACzD;gBAAMiI,SAAS;;kCACd,KAAC3H;wBAAW6E,SAAQ;wBAAQzD,OAAM;kCAC/B,gEACC,iEACA,oEACA,mEACA;;oBAGHoB,SAAS,CAACC,0BACT,KAACxD;wBAAM2I,UAAS;kCACb,oEACC,+DACA;yBAEF;kCAEJ,KAAC5H;wBAAW6E,SAAQ;wBAAQgD,IAAI;4BAAEC,YAAY;wBAAO;kCAClD;;oBAEFb,wBACC,KAACjH;wBAAW6E,SAAQ;wBAAQzD,OAAM;kCAC/B,iEACC;uCAGJ,MAACtC;wBAAY2H,MAAK;;0CAChB,KAAC5G;0CACC,cAAA,MAACC;;sDACC,KAACF;sDAAW;;sDACZ,KAACA;sDAAW;;sDACZ,KAACA;sDAAW;;sDACZ,KAACA;sDAAW;;sDACZ,KAACA;4CAAUmI,OAAM;sDACf,cAAA,KAACtJ;0DAAQ;;;;;;0CAIf,KAACkB;0CACEyC,MAAMiD,GAAG,CAAC,CAACrB;8CAyCEgE;wCApBH/G;oCApBT,MAAMA,OAAOW,KAAK,CAACoC,KAAKC,EAAE,CAAC;oCAC3B,MAAMc,QAAQ1C,MAAM,CAAC2B,KAAKC,EAAE,CAAC;oCAC7B,MAAMgE,cAAcxH,kBAAkBe,OAAOwC,KAAKC,EAAE,EAAE;oCACtD,MAAM+D,SAAS/G,wBAAAA,KAAMiH,OAAO,CAACC,IAAI,CAAC,CAACC,QAAUA,MAAMC,SAAS;oCAC5D,MAAMC,OAAOrH,OAAOD,aAAaC,QAAQ;oCACzC,qBACE,MAACnB;;0DACC,KAACF;0DACEqI,4BACC,KAAC5J;oDAAQqI,MAAMuB;8DACZvH,YAAYc,OAAOwC,KAAKC,EAAE;qDAG7BvD,YAAYc,OAAOwC,KAAKC,EAAE;;0DAG9B,KAACrE;gDACCiI,IAAI;oDAAEU,YAAYtH,OAAO,cAAc4F;gDAAU;0DAEhD5F,gBACEA,gBAAAA,KAAKC,OAAO,qBAAZD,cAAcuH,OAAO,mBAAIvH,KAAKwH,QAAQ,iBAEvC,KAACzI;oDAAW6E,SAAQ;oDAAQzD,OAAM;8DAC/B2D,SAAS;;;0DAIhB,KAACnF;0DACE0I,qBACC,KAACnJ;oDACCsH,MAAK;oDACLtF,OAAOmH,KAAKnH,KAAK;oDACjBC,OAAOkH,KAAKlH,KAAK;qDAEjB;;0DAEN,KAACxB;0DACEoI,SACG;oDACEA,OAAOU,QAAQ;qDACfV,eAAAA,OAAO7C,IAAI,YAAX6C,eAAe,GAAGA,OAAOW,SAAS,CAAC,CAAC,CAAC;iDACtC,CACE5E,MAAM,CAACsC,SACPuC,IAAI,CAAC,SACR3H,OACE,MACA;;0DAER,KAACrB;gDAAUmI,OAAM;0DACd9G,QAAQwB,0BACP,KAACvD;oDACCuH,MAAK;oDACLK,SAAS;;+DACPjE,gBAAgB;4DACdqC,QAAQlB,KAAKC,EAAE;4DACf4E,QAAQ,UAAEb,0BAAAA,OAAQ/D,EAAE,mBAAI;wDAC1B;;8DAGD;qDAED;;;uCAvDOD,KAAKC,EAAE;gCA2D1B;;;;oBAILgD,UAAU,qBACT,KAACtI;wBACCmK,MAAM3G,SAAS2G,IAAI;wBACnBC,UAAU5G,SAAS4G,QAAQ;wBAC3BC,UAAU5G,MAAMsD,MAAM;wBACtBuD,OAAO9G,SAAS8G,KAAK;wBACrBC,cAAc/G,SAASgH,OAAO;wBAC9BC,oBAAoBzI;;kCAIxB,KAACX;wBAAW6E,SAAQ;wBAAQgD,IAAI;4BAAEC,YAAY;wBAAO;kCAClD;;oBAEFtF,SAAS,CAACE,yBACT,KAAC1C;wBAAW6E,SAAQ;wBAAQzD,OAAM;kCAC/B,AAACoB,CAAAA,MAAM6G,gBAAgB,GACpB,CAAC,yCAAyC,EAAE7G,MAAM6G,gBAAgB,CAAC,OAAO,CAAC,GAC3E,EAAC,IACH,mEACA;yBAEF;oBACH,CAAC7G,sBACA,KAACxC;wBAAW6E,SAAQ;wBAAQzD,OAAM;kCAC/B6F,UACG,yDACA3E,UACE,aACA,8DACA;yBAENK,QAAQ+C,MAAM,iBAChB,MAAC5G;wBAAY2H,MAAK;;0CAChB,KAAC5G;0CACC,cAAA,MAACC;;sDACC,KAACF;sDAAW;;sDACZ,KAACA;sDAAW;;sDACZ,KAACA;sDAAW;;sDACZ,KAACA;4CAAUmI,OAAM;sDACf,cAAA,KAACtJ;0DAAQ;;;;;;0CAIf,KAACkB;0CACEgD,QAAQ0C,GAAG,CAAC,CAACc;wCAIEA;oCAHd,MAAMmD,QAAQhJ,sBAAsB;wCAClCqE,QAAQwB,OAAOxB,MAAM;wCACrB4E,iBAAiBpD,OAAOxB,MAAM,KAAK;wCACnC6E,UAAU,GAAErD,yBAAAA,OAAOsD,cAAc,YAArBtD,yBAAyB;oCACvC;oCACA,MAAMuD,QAAQ7F,eAAesC,OAAOxC,MAAM;oCAC1C,qBACE,MAAC7D;wCAEC6J,KAAK;wCACL9B,IAAI;4CAAE+B,QAAQ;wCAAU;wCACxB9C,SAAS,IAAMhF,OAAOmE,IAAI,CAACvC,WAAWyC,OAAOxC,MAAM;;0DAEnD,KAAC/D;gDAAUiI,IAAI;oDAAEU,YAAY;gDAAY;0DACtCpC,OAAOxC,MAAM;;0DAEhB,KAAC/D;0DACC,cAAA,KAACT;oDACCsH,MAAK;oDACLtF,OAAOmI,MAAMnI,KAAK;oDAClBC,OAAOkI,MAAMlI,KAAK;oDAClByD,SAASyE,MAAMO,OAAO,GAAG,WAAW;;;0DAGxC,KAACjK;0DACE8J,MAAMhE,MAAM,GACXgE,MACGrE,GAAG,CAAC,CAACrB,OAAStD,YAAYc,OAAOwC,KAAKC,EAAE,GACxC2E,IAAI,CAAC,sBAER,KAAC5I;oDAAW6E,SAAQ;oDAAQzD,OAAM;8DAC/B;;;0DAIP,KAACxB;gDAAUmI,OAAM;gDAAQ+B,SAAQ;0DAC/B,cAAA,KAACjL;oDACCsC,OAAOgF,OAAOxC,MAAM;oDACpBoG,OAAO7D,cAAcC;;;;uCA9BpBA,OAAOxC,MAAM;gCAmCxB;;;uCAIJ,KAAC3D;wBAAW6E,SAAQ;wBAAQzD,OAAM;kCAC/B;;;;YAMNwB,gBAAgBuE,2BACf,KAACrG;gBACCkJ,IAAI;gBACJ9E,QAAQtC,aAAasC,MAAM;gBAC3BjE,MAAMkG;gBACN0B,UAAUjG,aAAaiG,QAAQ;gBAC/BoB,SAAS,IAAMpH,gBAAgB;gBAC/BqH,SAAS,IAAM,KAAK3H,OAAOK,aAAasC,MAAM;iBAE9C;0BAEJ,MAAC9F;gBACC4K,MAAM3D,QAAQrD;gBACdiH,SAASnH,aAAa+D,YAAY,IAAM5D,aAAa;gBACrDkH,SAAS;gBACTC,UAAS;;kCAET,KAAC5K;kCAAa,CAAC,QAAQ,WAAEwD,6BAAAA,UAAWW,MAAM,oBAAI,IAAI;;kCAClD,MAACrE;;0CACC,KAACC;gCAAkBsI,IAAI;oCAAEwC,IAAI;gCAAE;0CAC5B,qEACC;;4BAEHrH,0BACC,KAACxC;gCACCgB,OAAOA;gCACPL,OAAM;gCACNmJ,OAAOtH,UAAUkC,MAAM;gCACvBqF,UAAU,CAACrF,SACTjC,aAAa,CAACf,UACZA,UAAU,aAAKA;4CAASgD;6CAAWhD;iCAIvC;;;kCAEN,MAAC7C;;0CACC,KAACH;gCACC4H,SAAS,IAAM7D,aAAa;gCAC5B0D,UAAUN,QAAQvD;0CAEjB;;0CAEH,KAAC5D;gCACC2F,SAAQ;gCACR8B,UAAU,EAAC3D,6BAAAA,UAAWkC,MAAM,KAAImB,QAAQvD;gCACxCgE,SAAS,IACP9D,YACI,KAAKiC,gBAAgBjC,UAAUkC,MAAM,EAAElC,UAAUW,MAAM,IACvDkD;0CAGL/D,aAAa,cAAc;;;;;;0BAMlC,KAAClE;gBACCoL,MAAM9G;gBACNoE,QAAO;gBACPzC,SAAQ;gBACRoF,SAAS,IAAM9G,UAAU;gBACzBqH,aAAa;oBAAEpJ,OAAO;gBAAU;gBAChCqJ,0BACE;;sCACE,MAAChL;4BACC2B,OAAM;4BACNsJ,MAAK;4BACL5D,SAAS,IAAM3D,UAAU;4BACzB0E,IAAI;gCAAE8C,IAAI;4BAAE;;8CAEZ,KAACnM;oCAAQ8F,MAAMtG,mBAAmBsG,IAAI;;8CACtC,KAAC7F;8CAAO;;;;sCAEV,KAACuB;4BAAW6E,SAAQ;4BAAK+F,WAAU;sCAChC;;;;0BAKP,cAAA,KAACrM;oBAAUsM,OAAO;8BAChB,cAAA,MAACnL;wBAAMiI,SAAS;;0CACd,KAAC3H;gCAAW6E,SAAQ;gCAAQzD,OAAM;0CAC/B,mEACC,6DACA,6DACA;;0CAEJ,KAACrB;gCACCoB,OAAM;gCACNmJ,OAAOlH;gCACPmH,UAAU,CAACO,QAAUzH,aAAayH,MAAMC,MAAM,CAACT,KAAK;gCACpD7D,MAAK;gCACLuE,SAAS;gCACTC,aAAY;gCACZC,YAAW;;4BAEZ5H,yBAAW,KAACrE;gCAAM2I,UAAS;0CAAStE;iCAAoB;0CACzD,KAACpE;gCACC2F,SAAQ;gCACR8B,UAAU,CAACvD,UAAU4C,IAAI,MAAMxC;gCAC/BsD,SAAS,IAAM,KAAKf;0CAEnBvC,aAAa,YAAY;;;;;;;;AAOxC;AACA/B,iBAAiBE,WAAW,GAAG;AAE/B,eAAeL,eAAc"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/components/org-sending-card.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 { ICON_VARIANT_CLOSE } from '@aglyn/shared-data-enums'\nimport {\n mdiDeleteOutline,\n mdiDnsOutline,\n mdiEmailCheckOutline,\n mdiRefresh,\n} from '@aglyn/shared-data-mdi'\nimport {\n AppLink,\n CardDisplay,\n Container,\n MdiIcon,\n SrOnly,\n useConfirmationContext,\n} from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { NavigationDrawerComponent } from '@aglyn/shared-ui-jsx/components/navigation-drawer.component'\nimport RowActionsMenu, {\n type RowActionsMenuItem,\n} from '@aglyn/shared-ui-jsx/components/row-actions-menu.component'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n useSendingApi,\n type SendingIdentityView,\n} from '@aglyn/tenant-feature-instance/hooks/use-sending-identity-api'\nimport {\n Alert,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogContentText,\n DialogTitle,\n IconButton,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useRouter } from 'next/navigation'\nimport { useCallback, useRef, useState } from 'react'\nimport {\n describeOrgSendingDomainRemoval,\n describeSendingDomain,\n INCONCLUSIVE_CHECK,\n} from '../model/sending-domain-status'\nimport {\n OrgSiteSelect,\n orgSiteEmailsPath,\n orgSiteName,\n orgSitePageLabel,\n useEmailOrgMount,\n useOrgSitePage,\n type EmailOrgMount,\n} from './email-org-mount'\nimport SendingSenderDrawer from './sending-sender-drawer'\nimport { useOrgSendingViews } from './use-org-sending-views'\n\nexport interface OrgSendingCardProps {\n /** The organization's Emails page, which the domain pages hang beneath. */\n basePath: string\n}\n\n/** How a site's identity reads in one chip. */\nfunction identityChip(view: SendingIdentityView): {\n label: string\n color: 'default' | 'primary' | 'warning'\n} {\n if (view.refusal) return { label: 'Cannot send', color: 'warning' }\n if (view.identitySource === 'custom') {\n return { label: 'Own domain', color: 'primary' }\n }\n return { label: 'Shared address', color: 'default' }\n}\n\n/**\n * WHO EVERY SITE'S MAIL COMES FROM, AND THE DOMAINS THE ORGANIZATION PROVED.\n *\n * The two halves of a site's Sending section, over the organization. A domain\n * is proved once by the org — publishing its records is a statement about a\n * zone — and each site then chooses which proved domain it sends as, so the\n * page is two tables: the sites, each with what it sends as right now and its\n * default sender, and the domains, each with the sites sending as it.\n *\n * Every site row is read through the same route the site's own page reads,\n * so what it says is what a campaign sent as that site would get. The reads\n * are one per site over one page of sites, and the org's domains come back on\n * each of them, so there is no separate read for the domain table. What the\n * domain table says about which sites send as a domain is what the pages read\n * so far say, and the removal confirmation counts the sites not yet read.\n *\n * Managing — adding a domain, checking it, choosing what a site sends as, or\n * editing a sender — needs the organization admin role, exactly as on a\n * site's page, and the page says so to anybody else rather than offering\n * controls that would refuse.\n */\nexport function OrgSendingCard(props: OrgSendingCardProps) {\n const mount = useEmailOrgMount()\n return mount ? (\n <OrgSendingTables mount={mount} basePath={props.basePath} />\n ) : null\n}\nOrgSendingCard.displayName = 'OrgSendingCard'\n\nfunction OrgSendingTables(props: { mount: EmailOrgMount; basePath: string }) {\n const { mount, basePath } = props\n const call = useSendingApi()\n const router = useRouter()\n const { confirm } = useConfirmationContext()\n const { enqueueSnackbar } = useSnackbar()\n // Held in a ref for the reason the site card holds its own: the callbacks\n // below depend on it, and the hook hands back a fresh function per render.\n const notifyRef = useRef(enqueueSnackbar)\n notifyRef.current = enqueueSnackbar\n\n const sitePage = useOrgSitePage(mount)\n const { sites } = sitePage\n const { views, errors, loading, reload, first } = useOrgSendingViews(sites)\n const canManage = first?.canManage === true\n const entitled = first?.entitled === true\n const domains = first?.domains ?? []\n\n const [senderEditor, setSenderEditor] = useState<{\n hostId: string\n senderId: string | null\n } | null>(null)\n const [domainBusy, setDomainBusy] = useState('')\n /** The domain a site is being chosen for, with the site chosen so far. */\n const [assigning, setAssigning] = useState<{\n domain: string\n hostId: string\n } | null>(null)\n const [adding, setAdding] = useState(false)\n const [newDomain, setNewDomain] = useState('')\n const [addError, setAddError] = useState('')\n const [addingBusy, setAddingBusy] = useState(false)\n\n /** A domain's own page, the org route's twin of a site's. */\n const domainPath = useCallback(\n (domain: string) => `${basePath}/sending/${encodeURIComponent(domain)}`,\n [basePath],\n )\n /** The sites read so far whose mail leaves on `domain`. */\n const sitesSendingAs = (domain: string) =>\n mount.hosts.filter((site) => views[site.id]?.selected === domain)\n\n const handleVerifyDomain = useCallback(\n async (domain: string) => {\n if (domainBusy) return\n setDomainBusy(domain)\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'POST',\n body: { orgId: mount.orgId, domain, action: 'verify' },\n })\n setDomainBusy('')\n // No answer from DNS changes nothing about the record; see the site\n // card, which holds the same third outcome.\n if (response.status === 503) {\n return void notifyRef.current(INCONCLUSIVE_CHECK.text, {\n variant: 'info',\n })\n }\n if (!response.ok) {\n return void notifyRef.current(\n payload?.error ?? 'The check could not run',\n { variant: 'warning' },\n )\n }\n await reload()\n notifyRef.current(\n payload?.verified\n ? `Verified — ${domain} can send`\n : `Checked. Some of ${domain}’s records are still missing.`,\n { variant: payload?.verified ? 'success' : 'info' },\n )\n },\n [call, domainBusy, mount.orgId, reload],\n )\n\n const handleUseDomain = useCallback(\n async (hostId: string, domain: string) => {\n if (domainBusy) return\n setDomainBusy(domain)\n const { response, payload } = await call({\n path: 'sending-identity',\n method: 'POST',\n body: { hostId, domain },\n })\n setDomainBusy('')\n if (!response.ok) {\n return void notifyRef.current(\n payload?.error ?? 'Could not change the identity',\n { variant: 'warning' },\n )\n }\n setAssigning(null)\n await reload(hostId)\n // The address the ROUTE settled on, not the one this click asked for.\n const site = orgSiteName(mount, hostId)\n notifyRef.current(\n payload?.from\n ? `${site} now sends as ${payload.from}`\n : `${site}’s sending address was changed`,\n { variant: 'success' },\n )\n },\n [call, domainBusy, mount, reload],\n )\n\n const handleReleaseDomain = useCallback(\n async (domain: string) => {\n if (domainBusy) return\n const ok = await confirm({\n ...describeOrgSendingDomainRemoval({\n domain,\n sites: mount.hosts\n .filter((site) => views[site.id]?.selected === domain)\n .map((site) => ({\n name: orgSiteName(mount, site.id),\n issued: views[site.id]?.platformDomain === domain,\n })),\n unchecked: mount.hosts.filter((site) => !views[site.id]).length,\n }),\n confirmationButtonProps: { color: 'error' },\n })\n // `confirm` resolves with no value and REJECTS on cancel.\n .then(() => true)\n .catch(() => false)\n if (!ok) return\n setDomainBusy(domain)\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'DELETE',\n query: { orgId: mount.orgId, domain },\n })\n setDomainBusy('')\n if (!response.ok) {\n return void notifyRef.current(\n payload?.error ?? 'Could not remove the domain',\n { variant: 'warning' },\n )\n }\n await reload()\n },\n [call, confirm, domainBusy, mount, reload, views],\n )\n\n const handleAdd = useCallback(async () => {\n const domain = newDomain.trim()\n if (!domain || addingBusy) return\n setAddError('')\n setAddingBusy(true)\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'POST',\n body: { orgId: mount.orgId, domain, action: 'request' },\n })\n setAddingBusy(false)\n if (!response.ok) {\n setAddError(payload?.error ?? 'Could not add that domain')\n return\n }\n setAdding(false)\n setNewDomain('')\n // Straight to the records, which are what the person came for.\n router.push(domainPath(payload?.domain ?? domain))\n }, [addingBusy, call, domainPath, mount.orgId, newDomain, router])\n\n /**\n * What may be done to one domain from here, disabled with the reason rather\n * than hidden — the same four entries, gated the same way, as the site\n * card's row menu. The difference is the send-as entry: over the org it\n * asks which site.\n */\n const domainActions = (\n record: SendingIdentityView['domains'][number],\n ): RowActionsMenuItem[] => {\n const working = Boolean(domainBusy)\n const domainRouteReason = !canManage\n ? 'Managing sending domains needs the organization admin role.'\n : !entitled\n ? 'Managing sending domains needs a plan that carries sending as ' +\n 'your own domain.'\n : ''\n return [\n {\n key: 'open',\n label: 'Open domain',\n icon: <MdiIcon path={mdiDnsOutline.path} size={0.8} />,\n href: domainPath(record.domain),\n },\n {\n key: 'verify',\n label: 'Check DNS',\n icon: <MdiIcon path={mdiRefresh.path} size={0.8} />,\n disabled:\n working ||\n record.status === 'requested' ||\n Boolean(domainRouteReason),\n disabledReason:\n record.status === 'requested'\n ? 'No records have been issued for this domain yet, so there is ' +\n 'nothing to look for. Open it to see what it is waiting on.'\n : domainRouteReason || undefined,\n onClick: () => void handleVerifyDomain(record.domain),\n },\n {\n key: 'use',\n label: 'Send a site’s email as this domain…',\n icon: <MdiIcon path={mdiEmailCheckOutline.path} size={0.8} />,\n disabled: working || record.status !== 'verified' || !canManage,\n disabledReason:\n record.status !== 'verified'\n ? 'Only a verified domain can be sent as. Publish its records and ' +\n 'check DNS first.'\n : !canManage\n ? 'Choosing what a site sends as needs the organization admin ' +\n 'role.'\n : undefined,\n onClick: () => {\n if (mount.hosts.length === 1) {\n return void handleUseDomain(mount.hosts[0].id, record.domain)\n }\n setAssigning({\n domain: record.domain,\n hostId: mount.pickedHostId ?? '',\n })\n },\n },\n {\n key: 'release',\n label: 'Remove domain',\n icon: <MdiIcon path={mdiDeleteOutline.path} size={0.8} />,\n destructive: true,\n disabled: working || Boolean(domainRouteReason),\n disabledReason: domainRouteReason || undefined,\n onClick: () => void handleReleaseDomain(record.domain),\n },\n ]\n }\n\n const noSites = mount.hostsReady && mount.hosts.length === 0\n const editorView = senderEditor ? (views[senderEditor.hostId] ?? null) : null\n\n return (\n <CardDisplay\n header=\"Sending\"\n help={pluginDocsHelp('emailCampaigns', { anchor: '#two-ways-to-get-a-domain' })}\n contentGutterX\n contentGutterY\n contentBordered=\"all\"\n HeaderProps={{\n action: canManage ? (\n <Button\n size=\"small\"\n variant=\"contained\"\n disabled={!entitled}\n onClick={() => setAdding(true)}\n >\n {'Add domain'}\n </Button>\n ) : null,\n }}\n >\n <Stack spacing={1.5}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Receipts, password resets, booking confirmations and other ' +\n 'account email send from every site on every plan, including ' +\n 'Free. A domain is proved once for the whole organization; each ' +\n 'site then chooses which proved domain its mail leaves on, and ' +\n 'which senders it may use.'}\n </Typography>\n\n {first && !canManage ? (\n <Alert severity=\"info\">\n {'Adding a domain, choosing what a site sends as and editing its ' +\n 'senders need the organization admin role. You can see the ' +\n 'current state here.'}\n </Alert>\n ) : null}\n\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {'Sites'}\n </Typography>\n {noSites ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'This organization has no sites yet. What a site sends as is ' +\n 'chosen per site, so there is nothing to show until there is one.'}\n </Typography>\n ) : (\n <ScrollTable size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{'Site'}</TableCell>\n <TableCell>{'Sends as'}</TableCell>\n <TableCell>{'Status'}</TableCell>\n <TableCell>{'Default sender'}</TableCell>\n <TableCell align=\"right\">\n <SrOnly>{'Actions'}</SrOnly>\n </TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {sites.map((site) => {\n const view = views[site.id]\n const error = errors[site.id]\n const sendingPage = orgSiteEmailsPath(mount, site.id, 'sending')\n const sender = view?.senders.find((entry) => entry.isDefault)\n const chip = view ? identityChip(view) : null\n return (\n <TableRow key={site.id}>\n <TableCell>\n {sendingPage ? (\n <AppLink href={sendingPage}>\n {orgSiteName(mount, site.id)}\n </AppLink>\n ) : (\n orgSiteName(mount, site.id)\n )}\n </TableCell>\n <TableCell\n sx={{ fontFamily: view ? 'monospace' : undefined }}\n >\n {view ? (\n (view.refusal?.message ?? view.identity)\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {error || 'Loading…'}\n </Typography>\n )}\n </TableCell>\n <TableCell>\n {chip ? (\n <Chip\n size=\"small\"\n label={chip.label}\n color={chip.color}\n />\n ) : null}\n </TableCell>\n <TableCell>\n {sender\n ? [\n sender.fromName,\n sender.from ?? `${sender.localPart}@`,\n ]\n .filter(Boolean)\n .join(' · ')\n : view\n ? '—'\n : null}\n </TableCell>\n <TableCell align=\"right\">\n {view && canManage ? (\n <Button\n size=\"small\"\n onClick={() =>\n setSenderEditor({\n hostId: site.id,\n senderId: sender?.id ?? null,\n })\n }\n >\n {'Edit sender'}\n </Button>\n ) : null}\n </TableCell>\n </TableRow>\n )\n })}\n </TableBody>\n </ScrollTable>\n )}\n {noSites ? null : (\n <ListPagination\n page={sitePage.page}\n pageSize={sitePage.pageSize}\n rowCount={sites.length}\n count={sitePage.count}\n onPageChange={sitePage.setPage}\n labelDisplayedRows={orgSitePageLabel}\n />\n )}\n\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {'Domains'}\n </Typography>\n {first && !entitled ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {(first.customDomainPlan\n ? `Sending as your own domain starts on the ${first.customDomainPlan} plan. `\n : '') +\n 'Until then each site sends on a shared sending address, whose ' +\n 'delivery reputation is pooled with the other sites on it.'}\n </Typography>\n ) : null}\n {!first ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {noSites\n ? 'Domains are listed once the organization has a site.'\n : loading\n ? 'Loading…'\n : 'The organization’s domains could not be read, because no ' +\n 'site’s sending identity could.'}\n </Typography>\n ) : domains.length ? (\n <ScrollTable size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{'Domain'}</TableCell>\n <TableCell>{'State'}</TableCell>\n <TableCell>{'Sites sending as it'}</TableCell>\n <TableCell align=\"right\">\n <SrOnly>{'Actions'}</SrOnly>\n </TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {domains.map((record) => {\n const state = describeSendingDomain({\n status: record.status,\n pendingProvider: record.status === 'requested',\n issueError: record.lastIssueError ?? null,\n })\n const using = sitesSendingAs(record.domain)\n return (\n <TableRow\n key={record.domain}\n hover\n sx={{ cursor: 'pointer' }}\n onClick={() => router.push(domainPath(record.domain))}\n >\n <TableCell sx={{ fontFamily: 'monospace' }}>\n {record.domain}\n </TableCell>\n <TableCell>\n <Chip\n size=\"small\"\n label={state.label}\n color={state.color}\n variant={state.sending ? 'filled' : 'outlined'}\n />\n </TableCell>\n <TableCell>\n {using.length ? (\n using\n .map((site) => orgSiteName(mount, site.id))\n .join(', ')\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'—'}\n </Typography>\n )}\n </TableCell>\n <TableCell align=\"right\" padding=\"none\">\n <RowActionsMenu\n label={record.domain}\n items={domainActions(record)}\n />\n </TableCell>\n </TableRow>\n )\n })}\n </TableBody>\n </ScrollTable>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No domain has been added yet.'}\n </Typography>\n )}\n </Stack>\n\n {/* One site's sender, in the same drawer the site's own page opens. */}\n {senderEditor && editorView ? (\n <SendingSenderDrawer\n open\n hostId={senderEditor.hostId}\n view={editorView}\n senderId={senderEditor.senderId}\n onClose={() => setSenderEditor(null)}\n onSaved={() => void reload(senderEditor.hostId)}\n />\n ) : null}\n\n <Dialog\n open={Boolean(assigning)}\n onClose={domainBusy ? undefined : () => setAssigning(null)}\n fullWidth\n maxWidth=\"xs\"\n >\n <DialogTitle>{`Send as ${assigning?.domain ?? ''}`}</DialogTitle>\n <DialogContent>\n <DialogContentText sx={{ mb: 2 }}>\n {'The site whose email should leave on this domain. Its receipts, ' +\n 'account email and campaigns all move to it.'}\n </DialogContentText>\n {assigning ? (\n <OrgSiteSelect\n mount={mount}\n label=\"Site\"\n value={assigning.hostId}\n onChange={(hostId) =>\n setAssigning((current) =>\n current ? { ...current, hostId } : current,\n )\n }\n />\n ) : null}\n </DialogContent>\n <DialogActions>\n <Button\n onClick={() => setAssigning(null)}\n disabled={Boolean(domainBusy)}\n >\n {'Cancel'}\n </Button>\n <Button\n variant=\"contained\"\n disabled={!assigning?.hostId || Boolean(domainBusy)}\n onClick={() =>\n assigning\n ? void handleUseDomain(assigning.hostId, assigning.domain)\n : undefined\n }\n >\n {domainBusy ? 'Changing…' : 'Send as this domain'}\n </Button>\n </DialogActions>\n </Dialog>\n\n {/* Adding is a drawer, as on a site's page; see the site card. */}\n <NavigationDrawerComponent\n open={adding}\n anchor=\"right\"\n variant=\"temporary\"\n onClose={() => setAdding(false)}\n AppBarProps={{ color: 'surface' }}\n appBarLeft={\n <>\n <IconButton\n color=\"inherit\"\n edge=\"start\"\n onClick={() => setAdding(false)}\n sx={{ mr: 2 }}\n >\n <MdiIcon path={ICON_VARIANT_CLOSE.path} />\n <SrOnly>close drawer</SrOnly>\n </IconButton>\n <Typography variant=\"h6\" component=\"div\">\n {'Add a sending domain'}\n </Typography>\n </>\n }\n >\n <Container gutterY>\n <Stack spacing={2}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'The domain your mail should come from — the part after the @. ' +\n 'Use a domain you control the DNS for; you will be given ' +\n 'records to publish on it. Once it verifies, any of this ' +\n 'organization’s sites can send as it.'}\n </Typography>\n <TextField\n label=\"Domain\"\n value={newDomain}\n onChange={(event) => setNewDomain(event.target.value)}\n size=\"small\"\n autoFocus\n placeholder=\"acme.com\"\n helperText=\"For example acme.com — not an address\"\n />\n {addError ? <Alert severity=\"error\">{addError}</Alert> : null}\n <Button\n variant=\"contained\"\n disabled={!newDomain.trim() || addingBusy}\n onClick={() => void handleAdd()}\n >\n {addingBusy ? 'Adding…' : 'Add domain'}\n </Button>\n </Stack>\n </Container>\n </NavigationDrawerComponent>\n </CardDisplay>\n )\n}\nOrgSendingTables.displayName = 'OrgSendingTables'\n\nexport default OrgSendingCard\n"],"names":["pluginDocsHelp","ICON_VARIANT_CLOSE","mdiDeleteOutline","mdiDnsOutline","mdiEmailCheckOutline","mdiRefresh","AppLink","CardDisplay","Container","MdiIcon","SrOnly","useConfirmationContext","ListPagination","NavigationDrawerComponent","RowActionsMenu","ScrollTable","useSnackbar","useSendingApi","Alert","Button","Chip","Dialog","DialogActions","DialogContent","DialogContentText","DialogTitle","IconButton","Stack","TableBody","TableCell","TableHead","TableRow","TextField","Typography","useRouter","useCallback","useRef","useState","describeOrgSendingDomainRemoval","describeSendingDomain","INCONCLUSIVE_CHECK","OrgSiteSelect","orgSiteEmailsPath","orgSiteName","orgSitePageLabel","useEmailOrgMount","useOrgSitePage","SendingSenderDrawer","useOrgSendingViews","identityChip","view","refusal","label","color","identitySource","OrgSendingCard","props","mount","OrgSendingTables","basePath","displayName","views","call","router","confirm","enqueueSnackbar","notifyRef","current","sitePage","sites","errors","loading","reload","first","canManage","entitled","domains","senderEditor","setSenderEditor","domainBusy","setDomainBusy","assigning","setAssigning","adding","setAdding","newDomain","setNewDomain","addError","setAddError","addingBusy","setAddingBusy","domainPath","domain","encodeURIComponent","sitesSendingAs","hosts","filter","site","id","selected","handleVerifyDomain","response","payload","path","method","body","orgId","action","status","text","variant","ok","error","verified","handleUseDomain","hostId","from","handleReleaseDomain","map","name","issued","platformDomain","unchecked","length","confirmationButtonProps","then","catch","query","handleAdd","trim","push","domainActions","record","working","Boolean","domainRouteReason","key","icon","size","href","disabled","disabledReason","undefined","onClick","pickedHostId","destructive","noSites","hostsReady","editorView","header","help","anchor","contentGutterX","contentGutterY","contentBordered","HeaderProps","spacing","severity","sx","fontWeight","align","sender","sendingPage","senders","find","entry","isDefault","chip","fontFamily","message","identity","fromName","localPart","join","senderId","page","pageSize","rowCount","count","onPageChange","setPage","labelDisplayedRows","customDomainPlan","state","pendingProvider","issueError","lastIssueError","using","hover","cursor","sending","padding","items","open","onClose","onSaved","fullWidth","maxWidth","mb","value","onChange","AppBarProps","appBarLeft","edge","mr","component","gutterY","event","target","autoFocus","placeholder","helperText"],"mappings":"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"}
@@ -280,7 +280,7 @@ function OrgSendingDomainPage(props) {
280
280
  const card = /*#__PURE__*/ _jsx(CardDisplay, {
281
281
  header: 'Sending domain',
282
282
  help: pluginDocsHelp('emailCampaigns', {
283
- anchor: '#sending-domains'
283
+ anchor: '#org-sending-domain'
284
284
  }),
285
285
  contentGutterX: true,
286
286
  contentGutterY: true,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/components/org-sending-domain-detail.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 { PageHeaderRecord, pluginDocsHelp } from '@aglyn/aglyn'\nimport { mdiDeleteOutline, mdiEmailCheckOutline } from '@aglyn/shared-data-mdi'\nimport {\n AppLink,\n CardDisplay,\n MdiIcon,\n useConfirmationContext,\n} from '@aglyn/shared-ui-jsx'\nimport RowActionsMenu, {\n type RowActionsMenuItem,\n} from '@aglyn/shared-ui-jsx/components/row-actions-menu.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport SenderReadinessPanel from '@aglyn/tenant-feature-instance/components/sender-readiness-panel'\nimport {\n useSendingApi,\n type SendingDomainView,\n} from '@aglyn/tenant-feature-instance/hooks/use-sending-identity-api'\nimport { Alert, Button, Chip, Divider, Stack, Typography } from '@mui/material'\nimport { useRouter } from 'next/navigation'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport {\n describeOrgSendingDomainRemoval,\n describeSendingDomain,\n INCONCLUSIVE_CHECK,\n} from '../model/sending-domain-status'\nimport {\n OrgSiteSelect,\n orgSiteName,\n useEmailOrgMount,\n useOrgSitePage,\n type EmailOrgMount,\n} from './email-org-mount'\nimport { RecordRow } from './sending-domain-detail'\nimport { useOrgSendingViews } from './use-org-sending-views'\n\nexport interface OrgSendingDomainDetailProps {\n domain: string\n /** The organization's Emails page, for the way back to Sending. */\n basePath: string\n}\n\n/**\n * ONE SENDING DOMAIN, from the organization's page —\n * `/[orgSlug]/emails/sending/{domain}`.\n *\n * The domain is the organization's, so its page reads the same as it does\n * from a site's: what to publish, whether a lookup can see it, and the checks\n * that move it on. What changes is the half that is a site's choice. Under a\n * site this page asks whether THIS site sends as the domain; over the org it\n * lists the sites that do, and offers to move another site onto it.\n *\n * The records come from the domains route, which is owner-or-admin. An editor\n * still sees the domain's state and the sites sending as it — both come back\n * on each site's own identity read, which an editor may make — and is told\n * why the records are not shown rather than handed an error.\n *\n * `inconclusive` is held apart from the record exactly as on the site's page:\n * a lookup nobody answered changed nothing.\n */\nexport function OrgSendingDomainDetail(props: OrgSendingDomainDetailProps) {\n const mount = useEmailOrgMount()\n return mount ? <OrgSendingDomainPage mount={mount} {...props} /> : null\n}\nOrgSendingDomainDetail.displayName = 'OrgSendingDomainDetail'\n\nfunction OrgSendingDomainPage(\n props: OrgSendingDomainDetailProps & { mount: EmailOrgMount },\n) {\n const { mount, domain, basePath } = props\n const call = useSendingApi()\n const router = useRouter()\n const { confirm } = useConfirmationContext()\n const { enqueueSnackbar } = useSnackbar()\n // Held in a ref: `load` depends on it and the hook hands a fresh function\n // back per render, which would re-run the fetch on every render.\n const notifyRef = useRef(enqueueSnackbar)\n notifyRef.current = enqueueSnackbar\n\n // The first page of sites by name: enough to say which of them send as\n // this domain, and bounded however many sites the organization runs.\n const { sites } = useOrgSitePage(mount)\n const {\n views,\n reload,\n first,\n loading: viewsLoading,\n } = useOrgSendingViews(sites)\n const canManage = first?.canManage === true\n\n const [record, setRecord] = useState<SendingDomainView | null>(null)\n const [refusal, setRefusal] = useState('')\n const [loading, setLoading] = useState(true)\n const [busy, setBusy] = useState(false)\n const [unreachable, setUnreachable] = useState(false)\n const [assignHostId, setAssignHostId] = useState('')\n\n const load = useCallback(async () => {\n setLoading(true)\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'GET',\n query: { orgId: mount.orgId },\n })\n setLoading(false)\n if (!response.ok) {\n setRecord(null)\n setRefusal(payload?.error ?? 'This domain’s records could not be read.')\n return\n }\n setRefusal('')\n setRecord(\n (payload?.domains ?? []).find(\n (one: SendingDomainView) => one.domain === domain,\n ) ?? null,\n )\n }, [call, mount.orgId, domain])\n\n useEffect(() => {\n void load()\n }, [load])\n\n /** The domain as the list knows it, for a reader the records route refused. */\n const listed = first?.domains.find((one) => one.domain === domain) ?? null\n const status = record?.status ?? listed?.status ?? null\n const using = mount.hosts.filter(\n (site) => views[site.id]?.selected === domain,\n )\n /** Sites whose identity this page has not read, so cannot speak for. */\n const unchecked = mount.hosts.filter((site) => !views[site.id]).length\n const offered = mount.hosts.filter(\n (site) => views[site.id]?.selected !== domain,\n )\n\n const handleVerify = useCallback(async () => {\n if (busy) return\n setBusy(true)\n setUnreachable(false)\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'POST',\n body: { orgId: mount.orgId, domain, action: 'verify' },\n })\n setBusy(false)\n if (response.status === 503) {\n setUnreachable(true)\n return\n }\n if (!response.ok) {\n return void notifyRef.current(\n payload?.error ?? 'The check could not run',\n {\n variant: 'warning',\n },\n )\n }\n await Promise.all([load(), reload()])\n notifyRef.current(\n payload?.verified\n ? 'Verified — this domain can send'\n : 'Checked. Some records are still missing.',\n { variant: payload?.verified ? 'success' : 'info' },\n )\n }, [busy, call, domain, load, mount.orgId, reload])\n\n const handleRequestRecords = useCallback(async () => {\n if (busy) return\n setBusy(true)\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'POST',\n body: { orgId: mount.orgId, domain, action: 'request' },\n })\n setBusy(false)\n if (!response.ok) {\n return void notifyRef.current(\n payload?.error ?? 'Could not request records',\n { variant: 'warning' },\n )\n }\n await load()\n }, [busy, call, domain, load, mount.orgId])\n\n /** Moves one site onto this domain, or off it with `'platform'`. */\n const handleUse = useCallback(\n async (hostId: string, next: string) => {\n if (busy || !hostId) return\n setBusy(true)\n const { response, payload } = await call({\n path: 'sending-identity',\n method: 'POST',\n body: { hostId, domain: next },\n })\n setBusy(false)\n if (!response.ok) {\n return void notifyRef.current(\n payload?.error ?? 'Could not change the identity',\n { variant: 'warning' },\n )\n }\n setAssignHostId('')\n await reload(hostId)\n // The address the ROUTE settled on — see the site's page.\n const site = orgSiteName(mount, hostId)\n notifyRef.current(\n payload?.from\n ? `${site} now sends as ${payload.from}`\n : `${site}’s sending address was changed`,\n { variant: 'success' },\n )\n },\n [busy, call, mount, reload],\n )\n\n const handleRelease = useCallback(async () => {\n const ok = await confirm({\n ...describeOrgSendingDomainRemoval({\n domain,\n sites: using.map((site) => ({\n name: orgSiteName(mount, site.id),\n issued: views[site.id]?.platformDomain === domain,\n })),\n unchecked,\n }),\n confirmationButtonProps: { color: 'error' },\n })\n // `confirm` resolves with no value and REJECTS on cancel.\n .then(() => true)\n .catch(() => false)\n if (!ok) return\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'DELETE',\n query: { orgId: mount.orgId, domain },\n })\n if (!response.ok) {\n return void notifyRef.current(\n payload?.error ?? 'Could not remove the domain',\n { variant: 'warning' },\n )\n }\n router.push(`${basePath}/sending`)\n }, [basePath, call, confirm, domain, mount, router, unchecked, using, views])\n\n const state = status\n ? describeSendingDomain({\n status,\n pendingProvider: record?.pendingProvider ?? status === 'requested',\n issueError: record?.lastIssueError ?? listed?.lastIssueError ?? null,\n missing: record?.lastMissing ?? null,\n })\n : null\n const missing = new Set(record?.lastMissing ?? [])\n const actions: RowActionsMenuItem[] =\n canManage && record\n ? [\n {\n key: 'release',\n label: 'Remove domain',\n icon: <MdiIcon path={mdiDeleteOutline.path} size={0.8} />,\n destructive: true,\n onClick: () => void handleRelease(),\n },\n ]\n : []\n\n const card = (\n <CardDisplay\n header={'Sending domain'}\n help={pluginDocsHelp('emailCampaigns', { anchor: '#sending-domains' })}\n contentGutterX\n contentGutterY\n contentBordered=\"all\"\n HeaderProps={{\n action: (\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Button\n component={AppLink as any}\n {...({ componentVariant: 'naked', nativeButton: false } as any)}\n href={`${basePath}/sending`}\n size=\"small\"\n color=\"primary\"\n >\n {'All of Sending'}\n </Button>\n {actions.length ? <RowActionsMenu items={actions} /> : null}\n </Stack>\n ),\n }}\n >\n {(loading || viewsLoading) && !state ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Loading…'}\n </Typography>\n ) : !state ? (\n <Alert severity=\"warning\">\n {refusal ||\n `${domain} is not claimed by this organization. It may have been ` +\n 'removed — go back and add it again if you still want it.'}\n </Alert>\n ) : (\n <Stack spacing={2}>\n <Stack\n direction=\"row\"\n spacing={1}\n useFlexGap\n sx={{ alignItems: 'center', flexWrap: 'wrap' }}\n >\n <Chip\n label={state.label}\n color={state.color}\n variant={state.sending ? 'filled' : 'outlined'}\n />\n </Stack>\n\n <Alert severity={state.severity}>{state.text}</Alert>\n\n {unreachable ? (\n <Alert severity={INCONCLUSIVE_CHECK.severity} icon={false}>\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {INCONCLUSIVE_CHECK.label}\n </Typography>\n <Typography variant=\"body2\">{INCONCLUSIVE_CHECK.text}</Typography>\n </Alert>\n ) : null}\n\n {/*\n THE SITES SENDING AS IT, of those this page read. Each is the\n site's own choice, and moving one off it is the org twin of the\n \"stop sending\" control on a site's page.\n */}\n <Divider />\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {'Sites sending as this domain'}\n </Typography>\n {using.length ? (\n <Stack spacing={1}>\n {using.map((site) => (\n <Stack\n key={site.id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n <Typography variant=\"body2\">\n {orgSiteName(mount, site.id)}\n </Typography>\n {canManage ? (\n <Button\n size=\"small\"\n disabled={busy}\n onClick={() => void handleUse(site.id, 'platform')}\n >\n {'Stop sending as this domain'}\n </Button>\n ) : null}\n </Stack>\n ))}\n </Stack>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {unchecked\n ? 'None of the sites checked sends as it.'\n : 'No site sends as it.'}\n </Typography>\n )}\n {unchecked && !viewsLoading ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`${unchecked} more ${unchecked === 1 ? 'site was' : 'sites were'} ` +\n 'not checked; the Sending page reads them a page at a time.'}\n </Typography>\n ) : null}\n {canManage && status === 'verified' && offered.length ? (\n <Stack\n direction=\"row\"\n spacing={1}\n useFlexGap\n sx={{ alignItems: 'center', flexWrap: 'wrap' }}\n >\n <OrgSiteSelect\n mount={mount}\n label=\"Send a site’s email as this domain\"\n value={assignHostId}\n onChange={setAssignHostId}\n sites={offered}\n />\n <Button\n variant=\"outlined\"\n startIcon={\n <MdiIcon path={mdiEmailCheckOutline.path} size={0.8} />\n }\n disabled={busy || !assignHostId}\n onClick={() => void handleUse(assignHostId, domain)}\n >\n {'Send as this domain'}\n </Button>\n </Stack>\n ) : null}\n\n {/*\n THE RECORDS, which only the domains route returns. A reader it\n refused is told why, in place of the records, rather than shown an\n empty section that reads as \"nothing to publish\".\n */}\n <Divider />\n {refusal ? (\n <Alert severity=\"info\">\n {canManage\n ? refusal\n : 'The records to publish, and the checks that verify them, ' +\n 'are shown to the organization’s owners and admins.'}\n </Alert>\n ) : record ? (\n <>\n {record.dmarc ? (\n <Alert\n severity={\n record.dmarc.policy === 'reject' &&\n record.status !== 'verified'\n ? 'error'\n : 'info'\n }\n >\n {record.dmarc.consequence}\n </Alert>\n ) : null}\n {/*\n SPF, DKIM and DMARC as a receiver reads them, and whether they\n align with the From domain (AGL-3328). The records table below is\n what to publish; this is what the published zone adds up to.\n */}\n {record.readiness ? (\n <SenderReadinessPanel readiness={record.readiness} />\n ) : null}\n {record.records?.some((entry) => entry.value) ? (\n <>\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {'Publish these records'}\n </Typography>\n <Stack spacing={2}>\n {record.records\n .filter((entry) => entry.value)\n .map((entry) => (\n <RecordRow\n key={`${entry.type}:${entry.name}`}\n record={entry}\n missing={missing.has(`${entry.type}:${entry.name}`)}\n />\n ))}\n {record.dmarcSuggestion &&\n record.dmarc?.policy === 'absent' ? (\n <RecordRow\n record={record.dmarcSuggestion}\n missing={false}\n />\n ) : null}\n </Stack>\n </>\n ) : null}\n <Stack\n useFlexGap\n direction=\"row\"\n spacing={1}\n sx={{ flexWrap: 'wrap', gap: 1 }}\n >\n {record.status === 'requested' && canManage ? (\n <Button\n variant=\"contained\"\n disabled={busy}\n onClick={() => void handleRequestRecords()}\n >\n {busy ? 'Working…' : 'Request records'}\n </Button>\n ) : null}\n {record.status !== 'requested' ? (\n <Button\n variant=\"contained\"\n disabled={busy}\n onClick={() => void handleVerify()}\n >\n {busy ? 'Checking…' : 'Check DNS'}\n </Button>\n ) : null}\n </Stack>\n </>\n ) : null}\n </Stack>\n )}\n </CardDisplay>\n )\n\n return (\n <>\n <PageHeaderRecord title={domain} />\n {card}\n </>\n )\n}\nOrgSendingDomainPage.displayName = 'OrgSendingDomainPage'\n\nexport default OrgSendingDomainDetail\n"],"names":["PageHeaderRecord","pluginDocsHelp","mdiDeleteOutline","mdiEmailCheckOutline","AppLink","CardDisplay","MdiIcon","useConfirmationContext","RowActionsMenu","useSnackbar","SenderReadinessPanel","useSendingApi","Alert","Button","Chip","Divider","Stack","Typography","useRouter","useCallback","useEffect","useRef","useState","describeOrgSendingDomainRemoval","describeSendingDomain","INCONCLUSIVE_CHECK","OrgSiteSelect","orgSiteName","useEmailOrgMount","useOrgSitePage","RecordRow","useOrgSendingViews","OrgSendingDomainDetail","props","mount","OrgSendingDomainPage","displayName","record","domain","basePath","call","router","confirm","enqueueSnackbar","notifyRef","current","sites","views","reload","first","loading","viewsLoading","canManage","setRecord","refusal","setRefusal","setLoading","busy","setBusy","unreachable","setUnreachable","assignHostId","setAssignHostId","load","response","payload","path","method","query","orgId","ok","error","domains","find","one","listed","status","using","hosts","filter","site","id","selected","unchecked","length","offered","handleVerify","body","action","variant","Promise","all","verified","handleRequestRecords","handleUse","hostId","next","from","handleRelease","map","name","issued","platformDomain","confirmationButtonProps","color","then","catch","push","state","pendingProvider","issueError","lastIssueError","missing","lastMissing","Set","actions","key","label","icon","size","destructive","onClick","card","header","help","anchor","contentGutterX","contentGutterY","contentBordered","HeaderProps","direction","spacing","sx","alignItems","component","componentVariant","nativeButton","href","items","severity","useFlexGap","flexWrap","sending","text","fontWeight","disabled","value","onChange","startIcon","dmarc","policy","consequence","readiness","records","some","entry","has","type","dmarcSuggestion","gap","title"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/components/org-sending-domain-detail.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 { PageHeaderRecord, pluginDocsHelp } from '@aglyn/aglyn'\nimport { mdiDeleteOutline, mdiEmailCheckOutline } from '@aglyn/shared-data-mdi'\nimport {\n AppLink,\n CardDisplay,\n MdiIcon,\n useConfirmationContext,\n} from '@aglyn/shared-ui-jsx'\nimport RowActionsMenu, {\n type RowActionsMenuItem,\n} from '@aglyn/shared-ui-jsx/components/row-actions-menu.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport SenderReadinessPanel from '@aglyn/tenant-feature-instance/components/sender-readiness-panel'\nimport {\n useSendingApi,\n type SendingDomainView,\n} from '@aglyn/tenant-feature-instance/hooks/use-sending-identity-api'\nimport { Alert, Button, Chip, Divider, Stack, Typography } from '@mui/material'\nimport { useRouter } from 'next/navigation'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport {\n describeOrgSendingDomainRemoval,\n describeSendingDomain,\n INCONCLUSIVE_CHECK,\n} from '../model/sending-domain-status'\nimport {\n OrgSiteSelect,\n orgSiteName,\n useEmailOrgMount,\n useOrgSitePage,\n type EmailOrgMount,\n} from './email-org-mount'\nimport { RecordRow } from './sending-domain-detail'\nimport { useOrgSendingViews } from './use-org-sending-views'\n\nexport interface OrgSendingDomainDetailProps {\n domain: string\n /** The organization's Emails page, for the way back to Sending. */\n basePath: string\n}\n\n/**\n * ONE SENDING DOMAIN, from the organization's page —\n * `/[orgSlug]/emails/sending/{domain}`.\n *\n * The domain is the organization's, so its page reads the same as it does\n * from a site's: what to publish, whether a lookup can see it, and the checks\n * that move it on. What changes is the half that is a site's choice. Under a\n * site this page asks whether THIS site sends as the domain; over the org it\n * lists the sites that do, and offers to move another site onto it.\n *\n * The records come from the domains route, which is owner-or-admin. An editor\n * still sees the domain's state and the sites sending as it — both come back\n * on each site's own identity read, which an editor may make — and is told\n * why the records are not shown rather than handed an error.\n *\n * `inconclusive` is held apart from the record exactly as on the site's page:\n * a lookup nobody answered changed nothing.\n */\nexport function OrgSendingDomainDetail(props: OrgSendingDomainDetailProps) {\n const mount = useEmailOrgMount()\n return mount ? <OrgSendingDomainPage mount={mount} {...props} /> : null\n}\nOrgSendingDomainDetail.displayName = 'OrgSendingDomainDetail'\n\nfunction OrgSendingDomainPage(\n props: OrgSendingDomainDetailProps & { mount: EmailOrgMount },\n) {\n const { mount, domain, basePath } = props\n const call = useSendingApi()\n const router = useRouter()\n const { confirm } = useConfirmationContext()\n const { enqueueSnackbar } = useSnackbar()\n // Held in a ref: `load` depends on it and the hook hands a fresh function\n // back per render, which would re-run the fetch on every render.\n const notifyRef = useRef(enqueueSnackbar)\n notifyRef.current = enqueueSnackbar\n\n // The first page of sites by name: enough to say which of them send as\n // this domain, and bounded however many sites the organization runs.\n const { sites } = useOrgSitePage(mount)\n const {\n views,\n reload,\n first,\n loading: viewsLoading,\n } = useOrgSendingViews(sites)\n const canManage = first?.canManage === true\n\n const [record, setRecord] = useState<SendingDomainView | null>(null)\n const [refusal, setRefusal] = useState('')\n const [loading, setLoading] = useState(true)\n const [busy, setBusy] = useState(false)\n const [unreachable, setUnreachable] = useState(false)\n const [assignHostId, setAssignHostId] = useState('')\n\n const load = useCallback(async () => {\n setLoading(true)\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'GET',\n query: { orgId: mount.orgId },\n })\n setLoading(false)\n if (!response.ok) {\n setRecord(null)\n setRefusal(payload?.error ?? 'This domain’s records could not be read.')\n return\n }\n setRefusal('')\n setRecord(\n (payload?.domains ?? []).find(\n (one: SendingDomainView) => one.domain === domain,\n ) ?? null,\n )\n }, [call, mount.orgId, domain])\n\n useEffect(() => {\n void load()\n }, [load])\n\n /** The domain as the list knows it, for a reader the records route refused. */\n const listed = first?.domains.find((one) => one.domain === domain) ?? null\n const status = record?.status ?? listed?.status ?? null\n const using = mount.hosts.filter(\n (site) => views[site.id]?.selected === domain,\n )\n /** Sites whose identity this page has not read, so cannot speak for. */\n const unchecked = mount.hosts.filter((site) => !views[site.id]).length\n const offered = mount.hosts.filter(\n (site) => views[site.id]?.selected !== domain,\n )\n\n const handleVerify = useCallback(async () => {\n if (busy) return\n setBusy(true)\n setUnreachable(false)\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'POST',\n body: { orgId: mount.orgId, domain, action: 'verify' },\n })\n setBusy(false)\n if (response.status === 503) {\n setUnreachable(true)\n return\n }\n if (!response.ok) {\n return void notifyRef.current(\n payload?.error ?? 'The check could not run',\n {\n variant: 'warning',\n },\n )\n }\n await Promise.all([load(), reload()])\n notifyRef.current(\n payload?.verified\n ? 'Verified — this domain can send'\n : 'Checked. Some records are still missing.',\n { variant: payload?.verified ? 'success' : 'info' },\n )\n }, [busy, call, domain, load, mount.orgId, reload])\n\n const handleRequestRecords = useCallback(async () => {\n if (busy) return\n setBusy(true)\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'POST',\n body: { orgId: mount.orgId, domain, action: 'request' },\n })\n setBusy(false)\n if (!response.ok) {\n return void notifyRef.current(\n payload?.error ?? 'Could not request records',\n { variant: 'warning' },\n )\n }\n await load()\n }, [busy, call, domain, load, mount.orgId])\n\n /** Moves one site onto this domain, or off it with `'platform'`. */\n const handleUse = useCallback(\n async (hostId: string, next: string) => {\n if (busy || !hostId) return\n setBusy(true)\n const { response, payload } = await call({\n path: 'sending-identity',\n method: 'POST',\n body: { hostId, domain: next },\n })\n setBusy(false)\n if (!response.ok) {\n return void notifyRef.current(\n payload?.error ?? 'Could not change the identity',\n { variant: 'warning' },\n )\n }\n setAssignHostId('')\n await reload(hostId)\n // The address the ROUTE settled on — see the site's page.\n const site = orgSiteName(mount, hostId)\n notifyRef.current(\n payload?.from\n ? `${site} now sends as ${payload.from}`\n : `${site}’s sending address was changed`,\n { variant: 'success' },\n )\n },\n [busy, call, mount, reload],\n )\n\n const handleRelease = useCallback(async () => {\n const ok = await confirm({\n ...describeOrgSendingDomainRemoval({\n domain,\n sites: using.map((site) => ({\n name: orgSiteName(mount, site.id),\n issued: views[site.id]?.platformDomain === domain,\n })),\n unchecked,\n }),\n confirmationButtonProps: { color: 'error' },\n })\n // `confirm` resolves with no value and REJECTS on cancel.\n .then(() => true)\n .catch(() => false)\n if (!ok) return\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'DELETE',\n query: { orgId: mount.orgId, domain },\n })\n if (!response.ok) {\n return void notifyRef.current(\n payload?.error ?? 'Could not remove the domain',\n { variant: 'warning' },\n )\n }\n router.push(`${basePath}/sending`)\n }, [basePath, call, confirm, domain, mount, router, unchecked, using, views])\n\n const state = status\n ? describeSendingDomain({\n status,\n pendingProvider: record?.pendingProvider ?? status === 'requested',\n issueError: record?.lastIssueError ?? listed?.lastIssueError ?? null,\n missing: record?.lastMissing ?? null,\n })\n : null\n const missing = new Set(record?.lastMissing ?? [])\n const actions: RowActionsMenuItem[] =\n canManage && record\n ? [\n {\n key: 'release',\n label: 'Remove domain',\n icon: <MdiIcon path={mdiDeleteOutline.path} size={0.8} />,\n destructive: true,\n onClick: () => void handleRelease(),\n },\n ]\n : []\n\n const card = (\n <CardDisplay\n header={'Sending domain'}\n help={pluginDocsHelp('emailCampaigns', { anchor: '#org-sending-domain' })}\n contentGutterX\n contentGutterY\n contentBordered=\"all\"\n HeaderProps={{\n action: (\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Button\n component={AppLink as any}\n {...({ componentVariant: 'naked', nativeButton: false } as any)}\n href={`${basePath}/sending`}\n size=\"small\"\n color=\"primary\"\n >\n {'All of Sending'}\n </Button>\n {actions.length ? <RowActionsMenu items={actions} /> : null}\n </Stack>\n ),\n }}\n >\n {(loading || viewsLoading) && !state ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Loading…'}\n </Typography>\n ) : !state ? (\n <Alert severity=\"warning\">\n {refusal ||\n `${domain} is not claimed by this organization. It may have been ` +\n 'removed — go back and add it again if you still want it.'}\n </Alert>\n ) : (\n <Stack spacing={2}>\n <Stack\n direction=\"row\"\n spacing={1}\n useFlexGap\n sx={{ alignItems: 'center', flexWrap: 'wrap' }}\n >\n <Chip\n label={state.label}\n color={state.color}\n variant={state.sending ? 'filled' : 'outlined'}\n />\n </Stack>\n\n <Alert severity={state.severity}>{state.text}</Alert>\n\n {unreachable ? (\n <Alert severity={INCONCLUSIVE_CHECK.severity} icon={false}>\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {INCONCLUSIVE_CHECK.label}\n </Typography>\n <Typography variant=\"body2\">{INCONCLUSIVE_CHECK.text}</Typography>\n </Alert>\n ) : null}\n\n {/*\n THE SITES SENDING AS IT, of those this page read. Each is the\n site's own choice, and moving one off it is the org twin of the\n \"stop sending\" control on a site's page.\n */}\n <Divider />\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {'Sites sending as this domain'}\n </Typography>\n {using.length ? (\n <Stack spacing={1}>\n {using.map((site) => (\n <Stack\n key={site.id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n <Typography variant=\"body2\">\n {orgSiteName(mount, site.id)}\n </Typography>\n {canManage ? (\n <Button\n size=\"small\"\n disabled={busy}\n onClick={() => void handleUse(site.id, 'platform')}\n >\n {'Stop sending as this domain'}\n </Button>\n ) : null}\n </Stack>\n ))}\n </Stack>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {unchecked\n ? 'None of the sites checked sends as it.'\n : 'No site sends as it.'}\n </Typography>\n )}\n {unchecked && !viewsLoading ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`${unchecked} more ${unchecked === 1 ? 'site was' : 'sites were'} ` +\n 'not checked; the Sending page reads them a page at a time.'}\n </Typography>\n ) : null}\n {canManage && status === 'verified' && offered.length ? (\n <Stack\n direction=\"row\"\n spacing={1}\n useFlexGap\n sx={{ alignItems: 'center', flexWrap: 'wrap' }}\n >\n <OrgSiteSelect\n mount={mount}\n label=\"Send a site’s email as this domain\"\n value={assignHostId}\n onChange={setAssignHostId}\n sites={offered}\n />\n <Button\n variant=\"outlined\"\n startIcon={\n <MdiIcon path={mdiEmailCheckOutline.path} size={0.8} />\n }\n disabled={busy || !assignHostId}\n onClick={() => void handleUse(assignHostId, domain)}\n >\n {'Send as this domain'}\n </Button>\n </Stack>\n ) : null}\n\n {/*\n THE RECORDS, which only the domains route returns. A reader it\n refused is told why, in place of the records, rather than shown an\n empty section that reads as \"nothing to publish\".\n */}\n <Divider />\n {refusal ? (\n <Alert severity=\"info\">\n {canManage\n ? refusal\n : 'The records to publish, and the checks that verify them, ' +\n 'are shown to the organization’s owners and admins.'}\n </Alert>\n ) : record ? (\n <>\n {record.dmarc ? (\n <Alert\n severity={\n record.dmarc.policy === 'reject' &&\n record.status !== 'verified'\n ? 'error'\n : 'info'\n }\n >\n {record.dmarc.consequence}\n </Alert>\n ) : null}\n {/*\n SPF, DKIM and DMARC as a receiver reads them, and whether they\n align with the From domain (AGL-3328). The records table below is\n what to publish; this is what the published zone adds up to.\n */}\n {record.readiness ? (\n <SenderReadinessPanel readiness={record.readiness} />\n ) : null}\n {record.records?.some((entry) => entry.value) ? (\n <>\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {'Publish these records'}\n </Typography>\n <Stack spacing={2}>\n {record.records\n .filter((entry) => entry.value)\n .map((entry) => (\n <RecordRow\n key={`${entry.type}:${entry.name}`}\n record={entry}\n missing={missing.has(`${entry.type}:${entry.name}`)}\n />\n ))}\n {record.dmarcSuggestion &&\n record.dmarc?.policy === 'absent' ? (\n <RecordRow\n record={record.dmarcSuggestion}\n missing={false}\n />\n ) : null}\n </Stack>\n </>\n ) : null}\n <Stack\n useFlexGap\n direction=\"row\"\n spacing={1}\n sx={{ flexWrap: 'wrap', gap: 1 }}\n >\n {record.status === 'requested' && canManage ? (\n <Button\n variant=\"contained\"\n disabled={busy}\n onClick={() => void handleRequestRecords()}\n >\n {busy ? 'Working…' : 'Request records'}\n </Button>\n ) : null}\n {record.status !== 'requested' ? (\n <Button\n variant=\"contained\"\n disabled={busy}\n onClick={() => void handleVerify()}\n >\n {busy ? 'Checking…' : 'Check DNS'}\n </Button>\n ) : null}\n </Stack>\n </>\n ) : null}\n </Stack>\n )}\n </CardDisplay>\n )\n\n return (\n <>\n <PageHeaderRecord title={domain} />\n {card}\n </>\n )\n}\nOrgSendingDomainPage.displayName = 'OrgSendingDomainPage'\n\nexport default OrgSendingDomainDetail\n"],"names":["PageHeaderRecord","pluginDocsHelp","mdiDeleteOutline","mdiEmailCheckOutline","AppLink","CardDisplay","MdiIcon","useConfirmationContext","RowActionsMenu","useSnackbar","SenderReadinessPanel","useSendingApi","Alert","Button","Chip","Divider","Stack","Typography","useRouter","useCallback","useEffect","useRef","useState","describeOrgSendingDomainRemoval","describeSendingDomain","INCONCLUSIVE_CHECK","OrgSiteSelect","orgSiteName","useEmailOrgMount","useOrgSitePage","RecordRow","useOrgSendingViews","OrgSendingDomainDetail","props","mount","OrgSendingDomainPage","displayName","record","domain","basePath","call","router","confirm","enqueueSnackbar","notifyRef","current","sites","views","reload","first","loading","viewsLoading","canManage","setRecord","refusal","setRefusal","setLoading","busy","setBusy","unreachable","setUnreachable","assignHostId","setAssignHostId","load","response","payload","path","method","query","orgId","ok","error","domains","find","one","listed","status","using","hosts","filter","site","id","selected","unchecked","length","offered","handleVerify","body","action","variant","Promise","all","verified","handleRequestRecords","handleUse","hostId","next","from","handleRelease","map","name","issued","platformDomain","confirmationButtonProps","color","then","catch","push","state","pendingProvider","issueError","lastIssueError","missing","lastMissing","Set","actions","key","label","icon","size","destructive","onClick","card","header","help","anchor","contentGutterX","contentGutterY","contentBordered","HeaderProps","direction","spacing","sx","alignItems","component","componentVariant","nativeButton","href","items","severity","useFlexGap","flexWrap","sending","text","fontWeight","disabled","value","onChange","startIcon","dmarc","policy","consequence","readiness","records","some","entry","has","type","dmarcSuggestion","gap","title"],"mappings":"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"}
@@ -98,7 +98,7 @@ function OrgSuppressionsTable(props) {
98
98
  return /*#__PURE__*/ _jsx(CardDisplay, {
99
99
  header: "Suppressions",
100
100
  help: pluginDocsHelp('emailCampaigns', {
101
- anchor: '#suppressions'
101
+ anchor: '#org-suppressions'
102
102
  }),
103
103
  contentGutterX: true,
104
104
  contentGutterY: true,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/components/org-suppressions-card.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 { AppLink, CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport {\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n Typography,\n} from '@mui/material'\nimport { useRouter } from 'next/navigation'\nimport { useEffect, useRef, useState } from 'react'\nimport {\n orgSiteName,\n orgSitePageLabel,\n useEmailOrgMount,\n useOrgSitePage,\n type EmailOrgMount,\n} from './email-org-mount'\nimport {\n readSuppressionTotals,\n SUPPRESSION_REASONS,\n type SuppressionTotals,\n} from './suppression-totals'\n\n/** The breakdown's columns, in the order a site's own card lists its chips. */\nconst REASON_COLUMNS: ReadonlyArray<keyof SuppressionTotals> = [\n 'unsubscribe',\n 'bounce',\n 'complaint',\n 'manual',\n]\n\n/** A site's figures, or why there are none. */\ntype SiteTotals = SuppressionTotals | 'failed'\n\n/**\n * EVERY SITE'S SUPPRESSION LIST, SUMMED BY REASON.\n *\n * A suppression is one site's: the unsubscribe was from that site's mail, the\n * bounce was an address that site sent to, and the send path filters each\n * site's audience by its own list. So the organization's page does not merge\n * the lists into one — an address suppressed on one site is still mailable by\n * another — it answers the question a person managing several sites actually\n * has, \"is any site's list going stale?\", with one row per site, and opens a\n * site's own list from its row.\n *\n * The figures are the same four server aggregates a site's own card reads,\n * through the same function, so a row here and that site's chips agree. They\n * are read for one page of sites at a time and never as documents: a\n * suppression list can hold tens of thousands of addresses, and an aggregate\n * costs the same whatever it counts.\n */\nexport function OrgSuppressionsCard() {\n const mount = useEmailOrgMount()\n return mount ? <OrgSuppressionsTable mount={mount} /> : null\n}\nOrgSuppressionsCard.displayName = 'OrgSuppressionsCard'\n\nfunction OrgSuppressionsTable(props: { mount: EmailOrgMount }) {\n const { mount } = props\n const firestore = useFirestore()\n const router = useRouter()\n const sitePage = useOrgSitePage(mount)\n const { sites } = sitePage\n const [totals, setTotals] = useState<Record<string, SiteTotals>>({})\n\n /*\n * Each site is counted ONCE per visit. Paging asks only the sites not yet\n * counted, so paging back re-reads nothing. An answer is kept whenever it\n * lands, so moving on mid-flight does not strand a site's count — only\n * leaving the section discards one.\n */\n const requested = useRef(new Set<string>())\n const mounted = useRef(true)\n useEffect(() => {\n mounted.current = true\n return () => {\n mounted.current = false\n }\n }, [])\n useEffect(() => {\n for (const site of sites) {\n if (requested.current.has(site.id)) continue\n requested.current.add(site.id)\n const settle = (read: SiteTotals) => {\n if (mounted.current) {\n setTotals((current) => ({ ...current, [site.id]: read }))\n }\n }\n void readSuppressionTotals(firestore, site.id)\n .then(settle)\n .catch(() => settle('failed'))\n }\n }, [firestore, sites])\n\n const siteHref = (hostId: string) =>\n `${mount.basePath}/suppressions/${encodeURIComponent(hostId)}`\n const noSites = mount.hostsReady && mount.hosts.length === 0\n const anyFailed = sites.some((site) => totals[site.id] === 'failed')\n\n return (\n <CardDisplay\n header=\"Suppressions\"\n help={pluginDocsHelp('emailCampaigns', { anchor: '#suppressions' })}\n contentGutterX\n contentGutterY\n contentBordered=\"all\"\n >\n <Stack spacing={1.5}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Addresses each site’s marketing email skips, and why. Every site ' +\n 'keeps its own list — someone who unsubscribed from one site can ' +\n 'still be emailed by another — so open a site to see who is on ' +\n 'its list, or to add or remove someone.'}\n </Typography>\n {noSites ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'This organization has no sites yet.'}\n </Typography>\n ) : (\n <ScrollTable size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{'Site'}</TableCell>\n {REASON_COLUMNS.map((reason) => (\n <TableCell key={reason} align=\"right\">\n {SUPPRESSION_REASONS[reason].label}\n </TableCell>\n ))}\n <TableCell align=\"right\">{'Total'}</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {sites.map((site) => {\n const read = totals[site.id]\n const figures = read && read !== 'failed' ? read : null\n /*\n * Loading and failed are different answers, and neither is a\n * zero. A dash says the count could not be read; the ellipsis\n * says it has not arrived yet.\n */\n const cell = (value: number | undefined) =>\n figures\n ? Number(value ?? 0).toLocaleString()\n : read === 'failed'\n ? '—'\n : '…'\n const total = figures\n ? REASON_COLUMNS.reduce(\n (sum, reason) => sum + figures[reason],\n 0,\n )\n : undefined\n return (\n <TableRow\n key={site.id}\n hover\n sx={{ cursor: 'pointer' }}\n onClick={() => router.push(siteHref(site.id))}\n >\n <TableCell>\n {/*\n The row's own handler would fire too and push the same\n route twice — one history entry per back press.\n */}\n <AppLink\n href={siteHref(site.id)}\n onClick={(event: { stopPropagation: () => void }) =>\n event.stopPropagation()\n }\n >\n {orgSiteName(mount, site.id)}\n </AppLink>\n </TableCell>\n {REASON_COLUMNS.map((reason) => (\n <TableCell key={reason} align=\"right\">\n {cell(figures?.[reason])}\n </TableCell>\n ))}\n <TableCell align=\"right\">{cell(total)}</TableCell>\n </TableRow>\n )\n })}\n </TableBody>\n </ScrollTable>\n )}\n {anyFailed ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'A dash is a count that could not be read. It is not the same ' +\n 'as nobody being suppressed.'}\n </Typography>\n ) : null}\n {noSites ? null : (\n <ListPagination\n page={sitePage.page}\n pageSize={sitePage.pageSize}\n rowCount={sites.length}\n count={sitePage.count}\n onPageChange={sitePage.setPage}\n labelDisplayedRows={orgSitePageLabel}\n />\n )}\n </Stack>\n </CardDisplay>\n )\n}\nOrgSuppressionsTable.displayName = 'OrgSuppressionsTable'\n\nexport default OrgSuppressionsCard\n"],"names":["pluginDocsHelp","AppLink","CardDisplay","ListPagination","ScrollTable","useFirestore","Stack","TableBody","TableCell","TableHead","TableRow","Typography","useRouter","useEffect","useRef","useState","orgSiteName","orgSitePageLabel","useEmailOrgMount","useOrgSitePage","readSuppressionTotals","SUPPRESSION_REASONS","REASON_COLUMNS","OrgSuppressionsCard","mount","OrgSuppressionsTable","displayName","props","firestore","router","sitePage","sites","totals","setTotals","requested","Set","mounted","current","site","has","id","add","settle","read","then","catch","siteHref","hostId","basePath","encodeURIComponent","noSites","hostsReady","hosts","length","anyFailed","some","header","help","anchor","contentGutterX","contentGutterY","contentBordered","spacing","variant","color","size","map","reason","align","label","figures","cell","value","Number","toLocaleString","total","reduce","sum","undefined","hover","sx","cursor","onClick","push","href","event","stopPropagation","page","pageSize","rowCount","count","onPageChange","setPage","labelDisplayedRows"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/components/org-suppressions-card.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 { AppLink, CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport {\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n Typography,\n} from '@mui/material'\nimport { useRouter } from 'next/navigation'\nimport { useEffect, useRef, useState } from 'react'\nimport {\n orgSiteName,\n orgSitePageLabel,\n useEmailOrgMount,\n useOrgSitePage,\n type EmailOrgMount,\n} from './email-org-mount'\nimport {\n readSuppressionTotals,\n SUPPRESSION_REASONS,\n type SuppressionTotals,\n} from './suppression-totals'\n\n/** The breakdown's columns, in the order a site's own card lists its chips. */\nconst REASON_COLUMNS: ReadonlyArray<keyof SuppressionTotals> = [\n 'unsubscribe',\n 'bounce',\n 'complaint',\n 'manual',\n]\n\n/** A site's figures, or why there are none. */\ntype SiteTotals = SuppressionTotals | 'failed'\n\n/**\n * EVERY SITE'S SUPPRESSION LIST, SUMMED BY REASON.\n *\n * A suppression is one site's: the unsubscribe was from that site's mail, the\n * bounce was an address that site sent to, and the send path filters each\n * site's audience by its own list. So the organization's page does not merge\n * the lists into one — an address suppressed on one site is still mailable by\n * another — it answers the question a person managing several sites actually\n * has, \"is any site's list going stale?\", with one row per site, and opens a\n * site's own list from its row.\n *\n * The figures are the same four server aggregates a site's own card reads,\n * through the same function, so a row here and that site's chips agree. They\n * are read for one page of sites at a time and never as documents: a\n * suppression list can hold tens of thousands of addresses, and an aggregate\n * costs the same whatever it counts.\n */\nexport function OrgSuppressionsCard() {\n const mount = useEmailOrgMount()\n return mount ? <OrgSuppressionsTable mount={mount} /> : null\n}\nOrgSuppressionsCard.displayName = 'OrgSuppressionsCard'\n\nfunction OrgSuppressionsTable(props: { mount: EmailOrgMount }) {\n const { mount } = props\n const firestore = useFirestore()\n const router = useRouter()\n const sitePage = useOrgSitePage(mount)\n const { sites } = sitePage\n const [totals, setTotals] = useState<Record<string, SiteTotals>>({})\n\n /*\n * Each site is counted ONCE per visit. Paging asks only the sites not yet\n * counted, so paging back re-reads nothing. An answer is kept whenever it\n * lands, so moving on mid-flight does not strand a site's count — only\n * leaving the section discards one.\n */\n const requested = useRef(new Set<string>())\n const mounted = useRef(true)\n useEffect(() => {\n mounted.current = true\n return () => {\n mounted.current = false\n }\n }, [])\n useEffect(() => {\n for (const site of sites) {\n if (requested.current.has(site.id)) continue\n requested.current.add(site.id)\n const settle = (read: SiteTotals) => {\n if (mounted.current) {\n setTotals((current) => ({ ...current, [site.id]: read }))\n }\n }\n void readSuppressionTotals(firestore, site.id)\n .then(settle)\n .catch(() => settle('failed'))\n }\n }, [firestore, sites])\n\n const siteHref = (hostId: string) =>\n `${mount.basePath}/suppressions/${encodeURIComponent(hostId)}`\n const noSites = mount.hostsReady && mount.hosts.length === 0\n const anyFailed = sites.some((site) => totals[site.id] === 'failed')\n\n return (\n <CardDisplay\n header=\"Suppressions\"\n help={pluginDocsHelp('emailCampaigns', { anchor: '#org-suppressions' })}\n contentGutterX\n contentGutterY\n contentBordered=\"all\"\n >\n <Stack spacing={1.5}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Addresses each site’s marketing email skips, and why. Every site ' +\n 'keeps its own list — someone who unsubscribed from one site can ' +\n 'still be emailed by another — so open a site to see who is on ' +\n 'its list, or to add or remove someone.'}\n </Typography>\n {noSites ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'This organization has no sites yet.'}\n </Typography>\n ) : (\n <ScrollTable size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{'Site'}</TableCell>\n {REASON_COLUMNS.map((reason) => (\n <TableCell key={reason} align=\"right\">\n {SUPPRESSION_REASONS[reason].label}\n </TableCell>\n ))}\n <TableCell align=\"right\">{'Total'}</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {sites.map((site) => {\n const read = totals[site.id]\n const figures = read && read !== 'failed' ? read : null\n /*\n * Loading and failed are different answers, and neither is a\n * zero. A dash says the count could not be read; the ellipsis\n * says it has not arrived yet.\n */\n const cell = (value: number | undefined) =>\n figures\n ? Number(value ?? 0).toLocaleString()\n : read === 'failed'\n ? '—'\n : '…'\n const total = figures\n ? REASON_COLUMNS.reduce(\n (sum, reason) => sum + figures[reason],\n 0,\n )\n : undefined\n return (\n <TableRow\n key={site.id}\n hover\n sx={{ cursor: 'pointer' }}\n onClick={() => router.push(siteHref(site.id))}\n >\n <TableCell>\n {/*\n The row's own handler would fire too and push the same\n route twice — one history entry per back press.\n */}\n <AppLink\n href={siteHref(site.id)}\n onClick={(event: { stopPropagation: () => void }) =>\n event.stopPropagation()\n }\n >\n {orgSiteName(mount, site.id)}\n </AppLink>\n </TableCell>\n {REASON_COLUMNS.map((reason) => (\n <TableCell key={reason} align=\"right\">\n {cell(figures?.[reason])}\n </TableCell>\n ))}\n <TableCell align=\"right\">{cell(total)}</TableCell>\n </TableRow>\n )\n })}\n </TableBody>\n </ScrollTable>\n )}\n {anyFailed ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'A dash is a count that could not be read. It is not the same ' +\n 'as nobody being suppressed.'}\n </Typography>\n ) : null}\n {noSites ? null : (\n <ListPagination\n page={sitePage.page}\n pageSize={sitePage.pageSize}\n rowCount={sites.length}\n count={sitePage.count}\n onPageChange={sitePage.setPage}\n labelDisplayedRows={orgSitePageLabel}\n />\n )}\n </Stack>\n </CardDisplay>\n )\n}\nOrgSuppressionsTable.displayName = 'OrgSuppressionsTable'\n\nexport default OrgSuppressionsCard\n"],"names":["pluginDocsHelp","AppLink","CardDisplay","ListPagination","ScrollTable","useFirestore","Stack","TableBody","TableCell","TableHead","TableRow","Typography","useRouter","useEffect","useRef","useState","orgSiteName","orgSitePageLabel","useEmailOrgMount","useOrgSitePage","readSuppressionTotals","SUPPRESSION_REASONS","REASON_COLUMNS","OrgSuppressionsCard","mount","OrgSuppressionsTable","displayName","props","firestore","router","sitePage","sites","totals","setTotals","requested","Set","mounted","current","site","has","id","add","settle","read","then","catch","siteHref","hostId","basePath","encodeURIComponent","noSites","hostsReady","hosts","length","anyFailed","some","header","help","anchor","contentGutterX","contentGutterY","contentBordered","spacing","variant","color","size","map","reason","align","label","figures","cell","value","Number","toLocaleString","total","reduce","sum","undefined","hover","sx","cursor","onClick","push","href","event","stopPropagation","page","pageSize","rowCount","count","onPageChange","setPage","labelDisplayedRows"],"mappings":"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"}
@@ -347,7 +347,7 @@ RecordRow.displayName = 'RecordRow';
347
347
  what this surface publishes upward. */ const card = /*#__PURE__*/ _jsx(CardDisplay, {
348
348
  header: 'Sending domain',
349
349
  help: pluginDocsHelp('emailCampaigns', {
350
- anchor: '#sending-domains'
350
+ anchor: '#domain-states'
351
351
  }),
352
352
  contentGutterX: true,
353
353
  contentGutterY: true,