@aglyn/plugins-email 1.0.0-beta.143
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +7 -0
- package/package.json +58 -0
- package/src/index.d.ts +35 -0
- package/src/index.js +35 -0
- package/src/index.js.map +1 -0
- package/src/lib/components/campaign-design-create-widget.d.ts +21 -0
- package/src/lib/components/campaign-design-create-widget.js +61 -0
- package/src/lib/components/campaign-design-create-widget.js.map +1 -0
- package/src/lib/components/campaign-sender-editor-widget.d.ts +18 -0
- package/src/lib/components/campaign-sender-editor-widget.js +39 -0
- package/src/lib/components/campaign-sender-editor-widget.js.map +1 -0
- package/src/lib/components/campaign-topic-options-widget.d.ts +19 -0
- package/src/lib/components/campaign-topic-options-widget.js +48 -0
- package/src/lib/components/campaign-topic-options-widget.js.map +1 -0
- package/src/lib/components/campaign-topic-select.d.ts +32 -0
- package/src/lib/components/campaign-topic-select.js +87 -0
- package/src/lib/components/campaign-topic-select.js.map +1 -0
- package/src/lib/components/dynamic-list-rule-fields.d.ts +173 -0
- package/src/lib/components/dynamic-list-rule-fields.js +1473 -0
- package/src/lib/components/dynamic-list-rule-fields.js.map +1 -0
- package/src/lib/components/email-blocks.d.ts +111 -0
- package/src/lib/components/email-blocks.js +875 -0
- package/src/lib/components/email-blocks.js.map +1 -0
- package/src/lib/components/email-design-preview.d.ts +62 -0
- package/src/lib/components/email-design-preview.js +174 -0
- package/src/lib/components/email-design-preview.js.map +1 -0
- package/src/lib/components/email-screens-card.d.ts +42 -0
- package/src/lib/components/email-screens-card.js +277 -0
- package/src/lib/components/email-screens-card.js.map +1 -0
- package/src/lib/components/email-template-detail.d.ts +48 -0
- package/src/lib/components/email-template-detail.js +681 -0
- package/src/lib/components/email-template-detail.js.map +1 -0
- package/src/lib/components/email-topic-detail.d.ts +32 -0
- package/src/lib/components/email-topic-detail.js +293 -0
- package/src/lib/components/email-topic-detail.js.map +1 -0
- package/src/lib/components/email-topics-card.d.ts +46 -0
- package/src/lib/components/email-topics-card.js +327 -0
- package/src/lib/components/email-topics-card.js.map +1 -0
- package/src/lib/components/email-zones.d.ts +28 -0
- package/src/lib/components/email-zones.js +20 -0
- package/src/lib/components/email-zones.js.map +1 -0
- package/src/lib/components/emails-console-page.d.ts +32 -0
- package/src/lib/components/emails-console-page.js +229 -0
- package/src/lib/components/emails-console-page.js.map +1 -0
- package/src/lib/components/emails-console-sections.d.ts +36 -0
- package/src/lib/components/emails-console-sections.js +108 -0
- package/src/lib/components/emails-console-sections.js.map +1 -0
- package/src/lib/components/list-detail-card.d.ts +47 -0
- package/src/lib/components/list-detail-card.js +273 -0
- package/src/lib/components/list-detail-card.js.map +1 -0
- package/src/lib/components/list-edit-card.d.ts +11 -0
- package/src/lib/components/list-edit-card.js +287 -0
- package/src/lib/components/list-edit-card.js.map +1 -0
- package/src/lib/components/list-import-drawer.d.ts +22 -0
- package/src/lib/components/list-import-drawer.js +662 -0
- package/src/lib/components/list-import-drawer.js.map +1 -0
- package/src/lib/components/list-members-panel.d.ts +94 -0
- package/src/lib/components/list-members-panel.js +686 -0
- package/src/lib/components/list-members-panel.js.map +1 -0
- package/src/lib/components/lists-card.d.ts +28 -0
- package/src/lib/components/lists-card.js +377 -0
- package/src/lib/components/lists-card.js.map +1 -0
- package/src/lib/components/sending-domain-detail.d.ts +26 -0
- package/src/lib/components/sending-domain-detail.js +496 -0
- package/src/lib/components/sending-domain-detail.js.map +1 -0
- package/src/lib/components/sending-domains-card.d.ts +33 -0
- package/src/lib/components/sending-domains-card.js +962 -0
- package/src/lib/components/sending-domains-card.js.map +1 -0
- package/src/lib/components/sending-sender-drawer.d.ts +94 -0
- package/src/lib/components/sending-sender-drawer.js +543 -0
- package/src/lib/components/sending-sender-drawer.js.map +1 -0
- package/src/lib/components/suppressions-card.d.ts +49 -0
- package/src/lib/components/suppressions-card.js +639 -0
- package/src/lib/components/suppressions-card.js.map +1 -0
- package/src/lib/components/use-org-email-topics.d.ts +79 -0
- package/src/lib/components/use-org-email-topics.js +111 -0
- package/src/lib/components/use-org-email-topics.js.map +1 -0
- package/src/lib/constants/bundle-common.d.ts +18 -0
- package/src/lib/constants/bundle-common.js +18 -0
- package/src/lib/constants/bundle-common.js.map +1 -0
- package/src/lib/hooks/use-org-company-options.d.ts +20 -0
- package/src/lib/hooks/use-org-company-options.js +138 -0
- package/src/lib/hooks/use-org-company-options.js.map +1 -0
- package/src/lib/hooks/use-org-contact-fields.d.ts +40 -0
- package/src/lib/hooks/use-org-contact-fields.js +91 -0
- package/src/lib/hooks/use-org-contact-fields.js.map +1 -0
- package/src/lib/hooks/use-org-contact-segments.d.ts +16 -0
- package/src/lib/hooks/use-org-contact-segments.js +55 -0
- package/src/lib/hooks/use-org-contact-segments.js.map +1 -0
- package/src/lib/hooks/use-org-crm-views.d.ts +8 -0
- package/src/lib/hooks/use-org-crm-views.js +74 -0
- package/src/lib/hooks/use-org-crm-views.js.map +1 -0
- package/src/lib/hooks/use-org-lists.d.ts +8 -0
- package/src/lib/hooks/use-org-lists.js +47 -0
- package/src/lib/hooks/use-org-lists.js.map +1 -0
- package/src/lib/model/email-design-document.d.ts +52 -0
- package/src/lib/model/email-design-document.js +62 -0
- package/src/lib/model/email-design-document.js.map +1 -0
- package/src/lib/model/index.d.ts +64 -0
- package/src/lib/model/index.js +71 -0
- package/src/lib/model/index.js.map +1 -0
- package/src/lib/model/sending-domain-status.d.ts +99 -0
- package/src/lib/model/sending-domain-status.js +196 -0
- package/src/lib/model/sending-domain-status.js.map +1 -0
- package/src/lib/model/template-provenance.d.ts +113 -0
- package/src/lib/model/template-provenance.js +107 -0
- package/src/lib/model/template-provenance.js.map +1 -0
- package/src/lib/model/template-report.d.ts +158 -0
- package/src/lib/model/template-report.js +249 -0
- package/src/lib/model/template-report.js.map +1 -0
- package/src/lib/plugin.d.ts +27 -0
- package/src/lib/plugin.js +163 -0
- package/src/lib/plugin.js.map +1 -0
- package/src/lib/server-console.d.ts +116 -0
- package/src/lib/server-console.js +422 -0
- package/src/lib/server-console.js.map +1 -0
- package/src/lib/server-email-drafts.d.ts +104 -0
- package/src/lib/server-email-drafts.js +381 -0
- package/src/lib/server-email-drafts.js.map +1 -0
- package/src/lib/server-list-gate.d.ts +183 -0
- package/src/lib/server-list-gate.js +365 -0
- package/src/lib/server-list-gate.js.map +1 -0
- package/src/lib/server-list-import.d.ts +199 -0
- package/src/lib/server-list-import.js +632 -0
- package/src/lib/server-list-import.js.map +1 -0
- package/src/lib/server-suppressions.d.ts +135 -0
- package/src/lib/server-suppressions.js +295 -0
- package/src/lib/server-suppressions.js.map +1 -0
- package/src/lib/server.d.ts +19 -0
- package/src/lib/server.js +834 -0
- package/src/lib/server.js.map +1 -0
- package/src/lib/site.d.ts +26 -0
- package/src/lib/site.js +81 -0
- package/src/lib/site.js.map +1 -0
- package/src/lib/unsubscribe-link.d.ts +311 -0
- package/src/lib/unsubscribe-link.js +398 -0
- package/src/lib/unsubscribe-link.js.map +1 -0
- package/src/lib/utils/create-email-screen.d.ts +59 -0
- package/src/lib/utils/create-email-screen.js +59 -0
- package/src/lib/utils/create-email-screen.js.map +1 -0
- package/src/lib/utils/generate-preset-id.d.ts +19 -0
- package/src/lib/utils/generate-preset-id.js +25 -0
- package/src/lib/utils/generate-preset-id.js.map +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/components/sending-domains-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 CardDisplay,\n Container,\n HelpTip,\n MdiIcon,\n SrOnly,\n useConfirmationContext,\n} from '@aglyn/shared-ui-jsx'\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 Alert,\n Button,\n Chip,\n Divider,\n IconButton,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport { useRouter } from 'next/navigation'\nimport {\n describeSendingDomain,\n describeSendingDomainRemoval,\n INCONCLUSIVE_CHECK,\n} from '../model/sending-domain-status'\nimport SendingSenderDrawer from './sending-sender-drawer'\nimport {\n useSendingApi,\n type SendingIdentityView,\n} from '@aglyn/tenant-feature-instance/hooks/use-sending-identity-api'\n\nexport interface SendingDomainsCardProps {\n hostId: string\n /** `/…/emails`, which every card in this section is handed. */\n basePath: string\n}\n\n/**\n * WHERE A WORKSPACE PROVES IT OWNS THE DOMAIN ITS MAIL LEAVES ON.\n *\n * ## The two halves on one screen, and why they are not one control\n *\n * A domain is proved by the ORG — publishing a DKIM record is a statement\n * about a zone, and an agency running four sites on `client.com` should make\n * it once. Which of the proved domains THIS SITE sends as is a separate,\n * per-site choice, because the `From:` a recipient sees belongs to the site.\n *\n * So the top of this card says what this site sends as right now, and the\n * table below is the org's set of domains. Collapsing them would make one of\n * the two questions unanswerable: with one control per site, four sites would\n * publish the same record four times; with one control per org, an agency\n * could not give two clients two different `From:` lines.\n *\n * ## Every state gets its own sentence\n *\n * Five situations, and `describeSendingDomain` holds four of them — the fifth,\n * a check nobody answered, is deliberately not a state at all. It rides the\n * surface as a transient notice beside whatever the record still says, which\n * is the whole reason the model file exists.\n */\nexport function SendingDomainsCard(props: SendingDomainsCardProps) {\n const { hostId, basePath } = props\n const call = useSendingApi()\n const router = useRouter()\n const { confirm } = useConfirmationContext()\n const { enqueueSnackbar } = useSnackbar()\n /*\n * The snackbar held in a REF, for the reason `use-campaign-send-api` holds\n * the user in one: `load` is a dependency of the effect that runs it, so\n * anything whose identity changes per render re-runs the fetch — and a\n * hook returning a fresh object each render turns that into a loop that\n * never settles. Reading it through a ref makes the effect depend on the\n * things that actually identify the request.\n */\n const notifyRef = useRef(enqueueSnackbar)\n notifyRef.current = enqueueSnackbar\n\n const [view, setView] = useState<SendingIdentityView | null>(null)\n const [loading, setLoading] = useState(true)\n const [adding, setAdding] = useState(false)\n /**\n * The open sender editor, and which row it is on.\n *\n * One state rather than an `open` flag beside an id, so the drawer cannot be\n * open with no answer to \"which sender\" — the id and the openness are the\n * same fact. `senderId: null` is the add case.\n */\n const [senderEditor, setSenderEditor] = useState<{\n senderId: string | null\n } | null>(null)\n /** The row a list action is in flight for, so only its buttons go quiet. */\n const [senderBusy, setSenderBusy] = useState('')\n /** The same, for the domains table below it. */\n const [domainBusy, setDomainBusy] = useState('')\n const [addingBusy, setAddingBusy] = useState(false)\n const [newDomain, setNewDomain] = useState('')\n const [addError, setAddError] = useState('')\n const [claimingDedicated, setClaimingDedicated] = useState(false)\n\n const load = useCallback(async () => {\n setLoading(true)\n const { response, payload } = await call({\n path: 'sending-identity',\n method: 'GET',\n query: { hostId },\n })\n setLoading(false)\n if (!response.ok) {\n /*\n * The refusal itself, not a generic failure. A reader without the org\n * admin role, or on a plan that does not carry white-label, needs to\n * know which of those it is — \"something went wrong\" would send them\n * to support for an answer the response already gave.\n */\n setView(null)\n return void notifyRef.current(payload?.error ?? 'Could not read the sending identity', {\n variant: 'warning',\n })\n }\n setView(payload as SendingIdentityView)\n }, [call, hostId])\n\n useEffect(() => {\n void load()\n }, [load])\n\n /**\n * A DOMAIN'S OWN PAGE, BUILT IN ONE PLACE.\n *\n * Three things navigate here — the row, the row's menu, and the redirect\n * after adding a domain — and the `sending/` segment is the whole address:\n * `${basePath}/{domain}` resolves to no section and renders an empty page,\n * which is the worst possible landing for all three, because every one of\n * them happens at the moment somebody needs the DNS records. Derived once\n * so a second copy cannot be written without it.\n */\n const domainPath = useCallback(\n (domain: string) => `${basePath}/sending/${encodeURIComponent(domain)}`,\n [basePath],\n )\n\n /**\n * The list's own two actions — move the default, and remove a sender.\n *\n * Both are a POST and a re-read rather than a local edit of `view`. The\n * server decides which row is the default and what the projection onto the\n * host document becomes, so re-reading is what keeps this list and the\n * address a send would actually resolve to one answer.\n */\n const handleSenderAction = useCallback(\n async (action: 'makeDefaultSender' | 'deleteSender', senderId: string) => {\n setSenderBusy(senderId)\n const { response, payload } = await call({\n path: 'sending-identity',\n method: 'POST',\n body: { hostId, action, senderId },\n })\n setSenderBusy('')\n if (!response.ok) {\n return void notifyRef.current(\n payload?.error ?? 'Could not update that sender',\n { variant: 'warning' },\n )\n }\n void load()\n },\n [call, hostId, load],\n )\n\n /*\n * THE ROW'S OWN ACTIONS.\n *\n * Each one posts to the route that owns the decision rather than to a route\n * that happens to be nearby: checking DNS and releasing a claim are facts\n * about the ORG's domain, and which of them this SITE sends as is a per-site\n * selection. That split is the reason there are two routes, and a menu that\n * collapsed it would be the surface deciding an agency's client sends as\n * another client's name.\n *\n * All three re-read afterwards instead of patching `view`. The server\n * decides what the identity resolves to, and a list that edited itself from\n * a response body would be the second opinion this card exists not to be.\n */\n const handleVerifyDomain = useCallback(\n async (domain: string) => {\n if (!view?.orgId || domainBusy) return\n setDomainBusy(domain)\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'POST',\n body: { orgId: view.orgId, domain, action: 'verify' },\n })\n setDomainBusy('')\n /*\n * THE THIRD OUTCOME, held here exactly as the domain's own page holds\n * it. A `503` means the lookup got no answer, so nothing about the\n * record changed; reporting it as a failed check is how a customer\n * whose DNS is perfect ends up editing a zone that has nothing wrong\n * with it.\n */\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 load()\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, view?.orgId, domainBusy, load],\n )\n\n const handleUseDomain = useCallback(\n async (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 await load()\n // The address the ROUTE settled on, not the one this click asked for —\n // only the response knows what the `From:` line now says.\n notifyRef.current(\n payload?.from\n ? `This site now sends as ${payload.from}`\n : 'This site’s sending address was changed',\n { variant: 'success' },\n )\n },\n [call, hostId, domainBusy, load],\n )\n\n const handleReleaseDomain = useCallback(\n async (domain: string) => {\n if (!view?.orgId || domainBusy) return\n const ok = await confirm({\n // The same three sentences the domain's own page asks with. Two\n // confirmations describing one action differently is how a merchant\n // learns to dismiss the harsher of them.\n ...describeSendingDomainRemoval({\n domain,\n selected: view.selected,\n platformDomain: view.platformDomain,\n }),\n confirmationButtonProps: { color: 'error' },\n })\n // `confirm` resolves with no value and REJECTS on cancel, so the\n // resolved value alone can never gate this.\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: view.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 load()\n },\n [\n call,\n confirm,\n domainBusy,\n view?.orgId,\n view?.selected,\n view?.platformDomain,\n load,\n ],\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: view?.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 /*\n * Straight to the domain's own page, because the thing the person came\n * for is now there: the records to publish. A list row saying\n * \"Publish the records\" with the records one click further away would\n * make the next step something they have to go looking for.\n */\n router.push(domainPath(payload?.domain ?? domain))\n }, [call, view?.orgId, domainPath, router, newDomain, addingBusy])\n\n /**\n * ASK FOR THE PLATFORM SUBDOMAIN.\n *\n * The second of the two ways out of the marketing refusal, and the one for a\n * merchant who cannot publish DNS — an agency's client on a domain somebody\n * else administers, or a site whose registrar access is a support ticket\n * away. It is offered rather than issued because it spends a provider domain\n * slot and three records in Aglyn's own zone, where a domain the merchant\n * owns spends neither.\n *\n * Reloads rather than patching the view from the response. The claim moves\n * three things at once — the site's selection, the org's domain list and the\n * identity the send path resolves — and a surface that updated one of them\n * from a response body would be the second opinion this card exists not to\n * be.\n */\n const handleClaimDedicated = useCallback(async () => {\n if (claimingDedicated) return\n setClaimingDedicated(true)\n const { response, payload } = await call({\n path: 'sending-identity',\n method: 'POST',\n body: { hostId, action: 'request-dedicated' },\n })\n setClaimingDedicated(false)\n if (!response.ok) {\n return void notifyRef.current(\n payload?.error ?? 'Could not ask for a sending domain for this site',\n { variant: 'warning' },\n )\n }\n /*\n * \"Asked for\", not \"is being set up\". The claim is the only thing this\n * press completed; creating the domain happens on our side afterwards and\n * can be held at the provider's domain allowance, so a message promising\n * setup would be promising the half that is not ours to promise. The\n * domain's row is where the answer arrives, which is why this points at\n * it rather than restating it.\n */\n notifyRef.current(\n `${payload?.selected} has been asked for. Its row below says where the ` +\n 'setup has got to. Account email keeps sending meanwhile, and ' +\n 'campaigns can go out once the domain verifies.',\n { variant: 'success' },\n )\n await load()\n }, [call, hostId, claimingDedicated, load])\n\n /**\n * WHAT MAY BE DONE TO ONE DOMAIN, AND WHY NOT WHEN NOT.\n *\n * Every item here is an operation the routes behind this card can actually\n * perform, and one whose gate this card can see. Nothing is offered on a\n * guess: a menu entry that 403s teaches a reader the console does not know\n * its own rules, and it costs them a round trip to find out.\n *\n * DISABLED WITH A REASON rather than hidden, for the reason\n * `RowActionsMenuItem.disabledReason` exists: an absent control and an\n * inapplicable one look identical, so a viewer who cannot remove a domain\n * would otherwise be left wondering whether the console can remove domains\n * at all.\n *\n * The gates are not one gate, because the routes are not one route:\n *\n * - Open is navigation and needs nothing.\n * - Check DNS and Remove go to the DOMAINS route, which is owner/admin on\n * the org AND carries the own-domain entitlement across the whole\n * handler — so both conditions are read here.\n * - Sending as a domain goes to the IDENTITY route, whose write gate is\n * `org.settings` alone. It also refuses an unverified domain, which is\n * a state this list can already see, so it is named rather than\n * discovered.\n */\n const domainActions = useCallback(\n (record: SendingIdentityView['domains'][number]): RowActionsMenuItem[] => {\n const working = Boolean(domainBusy)\n const manage = view?.canManage === true\n const entitled = view?.entitled === true\n const selected = view?.selected === record.domain\n const orgAdminReason =\n 'Managing sending domains needs the organization admin role.'\n const domainRouteReason = !manage\n ? orgAdminReason\n : !entitled\n ? 'Managing sending domains needs a plan that carries sending as ' +\n 'your own domain.'\n : ''\n\n return [\n {\n key: 'open',\n label: 'Open domain',\n icon: <MdiIcon path={mdiDnsOutline.path} size={0.8} />,\n // An `href`, so the domain's page is middle-clickable and copyable\n // like any other link rather than only reachable by this handler.\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 || record.status === 'requested' || Boolean(domainRouteReason),\n disabledReason:\n record.status === 'requested'\n ? 'No records have been issued for this domain yet, so there ' +\n 'is 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 this site’s email as this domain',\n icon: <MdiIcon path={mdiEmailCheckOutline.path} size={0.8} />,\n disabled:\n working || selected || record.status !== 'verified' || !manage,\n disabledReason: selected\n ? 'This site already sends as this domain.'\n : record.status !== 'verified'\n ? 'Only a verified domain can be sent as. Publish its records ' +\n 'and check DNS first.'\n : !manage\n ? 'Choosing what this site sends as needs the organization ' +\n 'admin role.'\n : undefined,\n onClick: () => void handleUseDomain(record.domain),\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 domainBusy,\n domainPath,\n view?.canManage,\n view?.entitled,\n view?.selected,\n handleVerifyDomain,\n handleUseDomain,\n handleReleaseDomain,\n ],\n )\n\n const domains = view?.domains ?? []\n const senders = view?.senders ?? []\n /*\n * Whether this site has a domain of its own to name a mailbox on — the same\n * question the route asks before it accepts one. A site on the shared pool\n * has exactly one sender and its mailbox is fixed, so offering to add a\n * second would offer a choice the write refuses.\n */\n const canAddSender = Boolean(view?.selected)\n\n return (\n <CardDisplay\n header=\"Sending domains\"\n help={pluginDocsHelp('emailCampaigns', {\n anchor: '#sending-domains',\n })}\n contentGutterX\n contentGutterY\n contentBordered=\"all\"\n HeaderProps={{\n action: view?.canManage ? (\n <Stack direction=\"row\" spacing={1}>\n {/*\n THE ACTION SITS IN THE HEADER, not above the list it adds to.\n\n This page is built of vertical-tab sections, so a create control\n stacked over the table would put an editor between a reader and\n the rows they came to read — the same reason every create in the\n console opens a drawer.\n\n Senders are offered on every plan, unlike the domain beside\n them: the name in front of the address and where replies land\n are honored on the pooled Aglyn address exactly as they are on a\n site's own domain, and gating them behind the paid tier would\n withhold the free half of a capability over the paid half. A\n SECOND sender is a different matter and is disabled on the pool,\n because the mailbox that would distinguish it is fixed there.\n */}\n <Button\n size=\"small\"\n variant=\"outlined\"\n disabled={!view || !canAddSender}\n onClick={() => setSenderEditor({ senderId: null })}\n >\n {'Add sender'}\n </Button>\n <Button\n size=\"small\"\n variant=\"contained\"\n disabled={!view?.entitled || !view?.orgId}\n onClick={() => setAdding(true)}\n >\n {'Add domain'}\n </Button>\n </Stack>\n ) : null,\n }}\n >\n <Stack spacing={1.5}>\n {/*\n THE LADDER, IN THE ORDER A READER NEEDS IT.\n\n What already works, then what does not, then the two ways out. One\n claim per block: the same material as one paragraph is the same\n material nobody finishes, and the sentence a merchant came for —\n \"do my receipts send?\" — was the one buried in the middle of it.\n */}\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Receipts, password resets, booking confirmations and other ' +\n 'account email send on every plan, including Free. Nothing here ' +\n 'has to be finished first.'}\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Campaigns send on the shared address too. Its delivery ' +\n 'reputation is pooled with the other sites on it, so campaigns ' +\n 'there are held to tighter complaint and bounce limits — a ' +\n 'domain of this site’s own lifts that, and is the name your ' +\n 'recipients will recognize.'}\n </Typography>\n\n {/*\n WHAT THIS SITE SENDS AS, RIGHT NOW.\n\n Read from the same resolver the send path calls, so this is the\n answer a campaign would get and not a second opinion assembled from\n the table below. A surface that re-derived it would eventually show\n a healthy address for a send that refuses.\n */}\n {view ? (\n <Alert severity={view.refusal ? 'warning' : 'info'}>\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {view.refusal ? 'This site cannot send' : 'This site sends as'}\n </Typography>\n <Typography variant=\"body2\">\n {view.refusal?.message ?? view.identity}\n </Typography>\n {view.identitySource === 'shared' && !view.refusal ? (\n <Typography variant=\"body2\" sx={{ mt: 0.5 }}>\n {'Recipients see an Aglyn address, and replies still reach ' +\n 'you. The sending reputation on it is shared with the other ' +\n 'sites using it rather than being yours.'}\n </Typography>\n ) : null}\n {/*\n THE SENDER, beside the address rather than only inside the\n drawer that sets it.\n\n A person reading this card is asking what their recipients see,\n and the address alone does not answer it: an inbox shows the\n name first. Reply-to is named only when one is set, because a\n message without one takes replies at the sending address and\n saying \"none\" would read as replies going nowhere.\n */}\n {view.fromName || view.replyTo ? (\n <Typography variant=\"body2\" sx={{ mt: 0.5 }}>\n {view.fromName\n ? `Recipients see it from ${view.fromName}.`\n : 'Recipients see the address with no name in front of it.'}\n {view.replyTo ? ` Replies go to ${view.replyTo}.` : ''}\n </Typography>\n ) : null}\n {/*\n A stored mailbox that is not the one in use, said out loud. The\n alternative is a settings drawer showing `sales` beside mail\n that is leaving as `notifications@`.\n */}\n {view.localPart && !view.localPartInUse && !view.refusal ? (\n <Typography variant=\"body2\" sx={{ mt: 0.5 }}>\n {`This site is set to send as ${view.localPart}@, which ` +\n 'takes effect once it has a domain of its own. The shared ' +\n 'address has one fixed mailbox for every site on it.'}\n </Typography>\n ) : null}\n </Alert>\n ) : null}\n\n {/*\n THE SENDERS THIS SITE HOLDS.\n\n A list rather than one editable address, because a site legitimately\n sends as several people — and because a mailbox has to be one\n somebody serves. A composer that could type an address per campaign\n would mint mailboxes that exist in one message's headers and nowhere\n else; a composer that picks from this list can only reach an address\n that was configured here and validated once.\n\n The DEFAULT is what an email that names no sender goes out as, and\n it is also the row projected onto the host document — which is why\n nothing needed backfilling: a site that never opens this list has\n exactly one sender, the one it has always had.\n */}\n {view ? (\n <>\n <Divider />\n {/*\n The card's own help affordance points at the domain half, which\n is what the header is about. A site's senders are a different\n question with a section of their own, so the block that asks it\n carries its own tip rather than sending a reader to the top of a\n page about domains.\n */}\n <Stack\n direction=\"row\"\n spacing={0.5}\n sx={{ alignItems: 'center' }}\n >\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {'Senders'}\n </Typography>\n <HelpTip\n {...pluginDocsHelp('emailCampaigns', {\n anchor: '#senders',\n title: 'Senders',\n excerpt:\n 'The addresses this site may send as — a mailbox, a name ' +\n 'and a reply address each. A campaign picks one; the ' +\n 'default is what an email that names no other goes out as.',\n })}\n />\n </Stack>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'The addresses this site may send as. Every email you compose ' +\n 'picks one of them; the default is what an email that names ' +\n 'no other goes out as.'}\n </Typography>\n {!canAddSender ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'This site sends on a shared Aglyn address, whose mailbox is ' +\n 'fixed and shared with the other sites on it, so it has one ' +\n 'sender. A domain of this site’s own is what makes a second ' +\n 'address possible.'}\n </Typography>\n ) : null}\n <ScrollTable size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{'Sends as'}</TableCell>\n <TableCell>{'Name'}</TableCell>\n <TableCell>{'Replies to'}</TableCell>\n <TableCell align=\"right\">{'Default'}</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {senders.map((sender) => (\n <TableRow\n key={sender.id}\n hover={view.canManage}\n sx={{ cursor: view.canManage ? 'pointer' : 'default' }}\n onClick={\n view.canManage\n ? () => setSenderEditor({ senderId: sender.id })\n : undefined\n }\n >\n <TableCell sx={{ fontFamily: 'monospace' }}>\n {/*\n The whole address as the SERVER assembled it, and the\n mailbox alone when there is none to assemble it onto.\n A site whose domain is not settled has a real stored\n mailbox that is not yet in effect, and the alert above\n says so — printing a domain here that no send would\n use would be this surface inventing the answer.\n */}\n {sender.from ?? `${sender.localPart}@`}\n </TableCell>\n <TableCell>{sender.fromName ?? '—'}</TableCell>\n <TableCell>{sender.replyTo ?? '—'}</TableCell>\n <TableCell align=\"right\">\n {sender.isDefault ? (\n <Chip size=\"small\" label=\"Default\" color=\"primary\" />\n ) : view.canManage ? (\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ justifyContent: 'flex-end' }}\n >\n {/*\n `stopPropagation` because the row itself opens the\n editor. Without it, making a sender the default\n would also open a drawer over the list that just\n changed underneath it.\n */}\n <Button\n size=\"small\"\n disabled={senderBusy === sender.id}\n onClick={(event) => {\n event.stopPropagation()\n void handleSenderAction(\n 'makeDefaultSender',\n sender.id,\n )\n }}\n >\n {'Make default'}\n </Button>\n <Button\n size=\"small\"\n color=\"error\"\n disabled={senderBusy === sender.id}\n onClick={(event) => {\n event.stopPropagation()\n void handleSenderAction(\n 'deleteSender',\n sender.id,\n )\n }}\n >\n {'Remove'}\n </Button>\n </Stack>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'—'}\n </Typography>\n )}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </ScrollTable>\n </>\n ) : null}\n\n {/*\n THE THREE PLACES A SITE'S MAIL CAN LEAVE FROM, AND WHICH OF THEM IS\n A PROMISE.\n\n It read as \"two ways to get one\", which was two errors in four words.\n The shared address is not a way to get a domain and is not an absence\n of one either — it is where every site starts, it needs nothing, and\n it is the only one of the three that is always available. Leaving it\n out of the list made the two that are conditional read as the whole\n of the model, so a site that had neither looked broken.\n\n The order is what is guaranteed, then what a merchant can take, then\n what they can ask for. That ranking is the honest one and it is not\n the order of preference somebody would guess: the domain a CUSTOMER\n owns is the better `From:` line AND the one nothing on our side can\n hold up, because it costs our zone nothing.\n\n A domain we set up is last because it is the one this card must not\n oversell. The plan admits the REQUEST; the provider's account-wide\n domain allowance decides whether it is filled, and a claim made with\n no room waits. It used to arrive by itself — at site creation, on the\n upgrade webhook, from a sweep — so a merchant who upgraded may be\n looking for something that is now a button, which is the other half\n of what these sentences have to say.\n\n Plan names come from the server, which derives them from the\n entitlement tables. A tier written in here is pricing copy that keeps\n rendering after the gate beneath it moves.\n */}\n {view ? (\n <>\n <Divider />\n {/*\n Pointed at the section that says a domain we set up is asked for\n rather than included, which is the thing a merchant most needs\n before they choose between the two conditional options and the\n thing that reads worst when it is discovered afterwards.\n */}\n <Stack\n direction=\"row\"\n spacing={0.5}\n sx={{ alignItems: 'center' }}\n >\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {'Where this site’s mail can leave from'}\n </Typography>\n <HelpTip\n {...pluginDocsHelp('emailCampaigns', {\n anchor: '#a-domain-we-set-up-is-a-request',\n title: 'A domain we set up is asked for',\n excerpt:\n 'The shared address is always there. A domain you own you ' +\n 'add yourself. A domain we set up is a request against a ' +\n 'limited allowance, so it can wait — receipts and account ' +\n 'email keep sending whichever you are on.',\n })}\n />\n </Stack>\n <Typography variant=\"body2\" color=\"text.secondary\">\n <Typography\n component=\"span\"\n variant=\"body2\"\n sx={{ fontWeight: 'bold' }}\n >\n {'The shared Aglyn address. '}\n </Typography>\n {'Every plan, nothing to set up, and the one thing here you are ' +\n 'never waiting for. It carries receipts and account email ' +\n 'only, and its sending reputation is shared with the other ' +\n 'sites on it rather than being this site’s.'}\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n <Typography\n component=\"span\"\n variant=\"body2\"\n sx={{ fontWeight: 'bold' }}\n >\n {'A domain you already own. '}\n </Typography>\n {(view.customDomainPlan\n ? `Sending as your own domain starts on the ${view.customDomainPlan} plan. `\n : '') +\n 'Your own name in the From: line and a sending reputation ' +\n 'that is entirely yours. The trade is the DNS work: you ' +\n 'publish three records in your zone, and we never write to ' +\n 'it. Nothing on our side can hold this one up.'}\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n <Typography\n component=\"span\"\n variant=\"body2\"\n sx={{ fontWeight: 'bold' }}\n >\n {'A domain we set up. '}\n </Typography>\n {view.platformDomain\n ? `This site has one — ${view.platformDomain} — with no ` +\n 'records for you to publish, and the sending reputation on ' +\n 'it is this site’s alone. Recipients see an address on our ' +\n 'domain rather than on yours.'\n : 'Nothing for you to publish, and a sending reputation this ' +\n 'site holds alone; recipients see an address on our domain ' +\n 'rather than on yours. ' +\n /*\n * THE PART A MERCHANT WOULD OTHERWISE FEEL CHEATED BY, said\n * before they press rather than in the refusal afterwards.\n *\n * Each of these domains is a slot in the mail provider's\n * account-wide allowance plus three records in our own zone.\n * A card that named a tier and stopped would be promising an\n * inclusion the platform does not sell — and the sentence\n * that has to travel with the correction is the one about\n * what does not stop meanwhile, because \"we might not have\n * room\" reads as an outage without it.\n */\n (view.dedicatedDomainPlan\n ? `It needs ${view.dedicatedDomainPlan}, and the plan ` +\n 'admits the request rather than handing you the domain: '\n : 'It is a request rather than something a plan hands ' +\n 'you: ') +\n 'we hold a limited number of sending domains with our mail ' +\n 'provider, so one asked for when there is no room waits ' +\n 'until there is. Nothing stops while it waits — this site ' +\n 'keeps sending receipts and account email on the address ' +\n 'above, and only campaigns wait with the domain.'}\n </Typography>\n {/*\n The action, beside the sentence that describes it rather than in\n an alert of its own — a second box repeating the same trade would\n be the surface arguing with itself about which option it prefers.\n\n Only for a reader who can act on it, and only when the plan\n carries one and this site has none. Somebody without\n `org.settings` gets the role notice further down instead.\n */}\n {view.dedicated?.available && view.canManage ? (\n <Button\n size=\"small\"\n variant=\"outlined\"\n sx={{ alignSelf: 'flex-start' }}\n disabled={claimingDedicated}\n onClick={() => void handleClaimDedicated()}\n >\n {/*\n THE LABEL NAMES WHAT THE PRESS DOES, which is ask.\n\n \"Set up\" promises the outcome, and the outcome is not this\n button's to promise: creating the domain happens afterwards\n and can be held at the provider's domain allowance. A label\n that overstated it would make the waiting state read as a\n failure of something that had already happened.\n */}\n {claimingDedicated\n ? 'Asking…'\n : view.dedicated.proposed\n ? `Ask for ${view.dedicated.proposed}`\n : 'Ask for a domain for this site'}\n </Button>\n ) : null}\n {/*\n A PLAN GATE ON THE DOMAIN IS NOT A GATE ON THE MAIL.\n\n Said only to the reader it applies to, and said here rather than\n as an alert: the address this site sends account email from is\n already named above, and repeating \"you cannot\" in a colored box\n would read as an outage on a site that is sending perfectly well.\n */}\n {!view.entitled ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Account email from this site sends without either of those ' +\n '— the address is shown above.'}\n </Typography>\n ) : null}\n <Divider />\n </>\n ) : null}\n\n {/*\n The role gate holds whatever the plan says, so it is stated whatever\n the plan says. Nesting it under `entitled` left the reader who is\n BOTH un-entitled and unable to manage with no explanation of either.\n */}\n {view && !view.canManage ? (\n <Alert severity=\"info\">\n {'Adding a domain and choosing what this site sends as needs the ' +\n 'organization admin role. You can see the current state here.'}\n </Alert>\n ) : null}\n\n {loading ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Loading…'}\n </Typography>\n ) : domains.length ? (\n <ScrollTable size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{'Domain'}</TableCell>\n <TableCell>{'State'}</TableCell>\n <TableCell>{'Used by this site'}</TableCell>\n {/*\n Named for a screen reader and blank on screen. A visible\n header over an overflow column labels the menus rather than\n the data, and every other actions column in this console is\n headed the same way.\n */}\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 // The list read does not carry the provider detail — the\n // domain's own page does — so `requested` reads here as the\n // waiting state it usually is, and the page is where the\n // difference between \"no credential\" and \"the provider\n // refused\" is spelled out.\n pendingProvider: record.status === 'requested',\n // The REASON is the exception to that, because one of its\n // values is not a fault. A claim held at the provider's\n // domain allowance would otherwise read here as \"waiting on\n // a signing key\", whose whole instruction is to press a\n // button that cannot move it.\n issueError: record.lastIssueError ?? null,\n })\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 {view?.selected === record.domain ? (\n <Chip size=\"small\" label=\"Sending as this\" color=\"primary\" />\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'—'}\n </Typography>\n )}\n </TableCell>\n {/*\n The overflow, which the menu itself keeps from taking\n the row's own navigation with it: `RowActionsMenu` stops\n propagation on open, so pressing the button does not\n also push the domain's page out from under the menu it\n just opened.\n */}\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 {/*\n Editing the sender is a drawer too, for the standing reason a create\n is: the alternative is a form stacked above the table of domains,\n which puts an open editor between a reader and the thing they came to\n read.\n */}\n <SendingSenderDrawer\n open={Boolean(senderEditor)}\n hostId={hostId}\n view={view}\n senderId={senderEditor?.senderId ?? null}\n onClose={() => setSenderEditor(null)}\n onSaved={() => void load()}\n />\n\n {/*\n Creating is a drawer, the same as every other create in the console.\n\n Composed from `NavigationDrawerComponent` — the primitive\n `CreateArtifactDrawer` is itself built on — rather than from that\n wrapper, for the reason `suppressions-card.tsx` states beside the same\n choice: the shared schema opens with a required `displayName` labeled\n \"Friendly name for internal reference\", and a sending domain has no\n such thing. Its identity IS the domain, which is also its document id.\n Offering a name box that the writer ignores would teach the person it\n was used.\n */}\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. Mailbox providers do not delegate ' +\n 'sending for their own names, so a Gmail or Outlook address ' +\n 'cannot be verified here.'}\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}\nSendingDomainsCard.displayName = 'SendingDomainsCard'\n\nexport default SendingDomainsCard\n"],"names":["pluginDocsHelp","ICON_VARIANT_CLOSE","mdiDeleteOutline","mdiDnsOutline","mdiEmailCheckOutline","mdiRefresh","CardDisplay","Container","HelpTip","MdiIcon","SrOnly","useConfirmationContext","NavigationDrawerComponent","RowActionsMenu","ScrollTable","useSnackbar","Alert","Button","Chip","Divider","IconButton","Stack","TableBody","TableCell","TableHead","TableRow","TextField","Typography","useCallback","useEffect","useRef","useState","useRouter","describeSendingDomain","describeSendingDomainRemoval","INCONCLUSIVE_CHECK","SendingSenderDrawer","useSendingApi","SendingDomainsCard","props","view","hostId","basePath","call","router","confirm","enqueueSnackbar","notifyRef","current","setView","loading","setLoading","adding","setAdding","senderEditor","setSenderEditor","senderBusy","setSenderBusy","domainBusy","setDomainBusy","addingBusy","setAddingBusy","newDomain","setNewDomain","addError","setAddError","claimingDedicated","setClaimingDedicated","load","response","payload","path","method","query","ok","error","variant","domainPath","domain","encodeURIComponent","handleSenderAction","action","senderId","body","handleVerifyDomain","orgId","status","text","verified","handleUseDomain","from","handleReleaseDomain","selected","platformDomain","confirmationButtonProps","color","then","catch","handleAdd","trim","push","handleClaimDedicated","domainActions","record","working","Boolean","manage","canManage","entitled","orgAdminReason","domainRouteReason","key","label","icon","size","href","disabled","disabledReason","undefined","onClick","destructive","domains","senders","canAddSender","header","help","anchor","contentGutterX","contentGutterY","contentBordered","HeaderProps","direction","spacing","severity","refusal","sx","fontWeight","message","identity","identitySource","mt","fromName","replyTo","localPart","localPartInUse","alignItems","title","excerpt","align","map","sender","hover","cursor","id","fontFamily","isDefault","justifyContent","event","stopPropagation","component","customDomainPlan","dedicatedDomainPlan","dedicated","available","alignSelf","proposed","length","state","pendingProvider","issueError","lastIssueError","sending","padding","items","open","onClose","onSaved","AppBarProps","appBarLeft","edge","mr","gutterY","value","onChange","target","autoFocus","placeholder","helperText","displayName"],"mappings":"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"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { type SendingIdentityView } from '@aglyn/tenant-feature-instance/hooks/use-sending-identity-api';
|
|
2
|
+
export interface SendingSenderDrawerProps {
|
|
3
|
+
open: boolean;
|
|
4
|
+
hostId: string;
|
|
5
|
+
/** The identity as the route reports it. Null while the card is loading. */
|
|
6
|
+
view: SendingIdentityView | null;
|
|
7
|
+
/**
|
|
8
|
+
* WHICH SENDER THIS DRAWER IS EDITING, or `null` to add one.
|
|
9
|
+
*
|
|
10
|
+
* The two modes differ in what the fields start as and in nothing else: an
|
|
11
|
+
* edit opens on the row's stored values, and an add opens empty so the
|
|
12
|
+
* picker below fills a NEW sender rather than overwriting the one this site
|
|
13
|
+
* already has.
|
|
14
|
+
*/
|
|
15
|
+
senderId?: string | null;
|
|
16
|
+
onClose: () => void;
|
|
17
|
+
/**
|
|
18
|
+
* Called after a successful save, so the caller can re-read the identity.
|
|
19
|
+
*
|
|
20
|
+
* Carries the id the route answered with, which is what a caller that has to
|
|
21
|
+
* SELECT the new sender needs — the composer opens this drawer from its From
|
|
22
|
+
* picker, and the row it must then choose does not exist in any list it
|
|
23
|
+
* holds until it re-reads. A caller that only refreshes ignores it.
|
|
24
|
+
*/
|
|
25
|
+
onSaved: (senderId?: string) => void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* ONE SENDER THIS SITE MAY SEND AS.
|
|
29
|
+
*
|
|
30
|
+
* Three fields, and they are not three of a kind. The MAILBOX is the part of
|
|
31
|
+
* the address in front of the `@`; the domain behind it is whatever this
|
|
32
|
+
* site's verified identity resolves to and is not editable here, because
|
|
33
|
+
* DMARC on the sending apex is published `adkim=s` and the `From:` domain has
|
|
34
|
+
* to be exactly the domain whose DKIM key signed the message. The NAME is
|
|
35
|
+
* what a recipient reads in their inbox list. The REPLY ADDRESS is the only
|
|
36
|
+
* one of the three that may name a mailbox on a domain nobody here has
|
|
37
|
+
* verified — which is what makes "replies reach me personally" possible
|
|
38
|
+
* without pretending the mail came from a personal account.
|
|
39
|
+
*
|
|
40
|
+
* ## Why a site holds a LIST, and why the composer picks from it
|
|
41
|
+
*
|
|
42
|
+
* The composer already carries a from name and a reply-to per campaign, and
|
|
43
|
+
* keeps them: those are per-message facts. The mailbox is not, because it is
|
|
44
|
+
* an address that has to work — a bounce returns to it, and a mail client
|
|
45
|
+
* that ignores `Reply-To:` answers to it. A mailbox that exists in one
|
|
46
|
+
* campaign's headers and nowhere else is an address nobody serves.
|
|
47
|
+
*
|
|
48
|
+
* So a mailbox is configured HERE, once, and a campaign chooses among the
|
|
49
|
+
* senders that were configured. A per-send address field would be the thing
|
|
50
|
+
* that closure removed; a per-send choice between approved senders is not.
|
|
51
|
+
*
|
|
52
|
+
* The gates differ for the same reason. Deciding which addresses this site's
|
|
53
|
+
* mail may leave on is an organization-admin decision and lives here beside
|
|
54
|
+
* the domain it depends on; the composer is admin-or-editor and may pick
|
|
55
|
+
* among them and name the person in front of the address.
|
|
56
|
+
*
|
|
57
|
+
* ## The picker
|
|
58
|
+
*
|
|
59
|
+
* "Send as a person" is mostly one action — take a teammate's name and their
|
|
60
|
+
* mailbox and make the mail read as theirs — so the picker does all three
|
|
61
|
+
* fields at once and then lets any of them be edited. The address it proposes
|
|
62
|
+
* is derived from the part of their email in front of the `@`, which is the
|
|
63
|
+
* name they already answer to, and their real address becomes the reply
|
|
64
|
+
* target rather than the sender: a `From:` on their own mail provider's
|
|
65
|
+
* domain could never align, and would be rejected rather than delivered.
|
|
66
|
+
*
|
|
67
|
+
* Opened to ADD, it fills a new sender and leaves the ones this site already
|
|
68
|
+
* has alone — which is what makes "send as Jamie" an address beside the
|
|
69
|
+
* others rather than a rename of the only one.
|
|
70
|
+
*
|
|
71
|
+
* ## Who is on the list
|
|
72
|
+
*
|
|
73
|
+
* Everybody who can reach this site, from wherever their access comes from:
|
|
74
|
+
* the organization's team, whose members reach a site through an org role or
|
|
75
|
+
* an `allHosts` flag or a per-site grant, and the site's own collaborators,
|
|
76
|
+
* who are on it and nowhere else. Both, because either alone is a picker that
|
|
77
|
+
* is missing people — and a workspace that manages its team entirely at the
|
|
78
|
+
* org level has an empty site roster, so reading only that one offered no
|
|
79
|
+
* teammates at all.
|
|
80
|
+
*
|
|
81
|
+
* Nobody else. An org member scoped to other sites is not a candidate sender
|
|
82
|
+
* for this one: the address would work, and the person would be signing mail
|
|
83
|
+
* for a site they cannot open. `hostRoleFor` draws that line, and it is the
|
|
84
|
+
* same one the rules and every server gate draw.
|
|
85
|
+
*
|
|
86
|
+
* The roster is read only while this drawer is open. It is four collection
|
|
87
|
+
* reads on a settings surface, and ones that happen because somebody asked for
|
|
88
|
+
* them are a different thing from ones that happen because a card mounted.
|
|
89
|
+
*/
|
|
90
|
+
export declare function SendingSenderDrawer(props: SendingSenderDrawerProps): import("react").JSX.Element;
|
|
91
|
+
export declare namespace SendingSenderDrawer {
|
|
92
|
+
var displayName: string;
|
|
93
|
+
}
|
|
94
|
+
export default SendingSenderDrawer;
|