@aglyn/plugins-marketing 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 +56 -0
- package/src/index.d.ts +19 -0
- package/src/index.js +20 -0
- package/src/index.js.map +1 -0
- package/src/lib/components/announcement-bar-card.component.d.ts +20 -0
- package/src/lib/components/announcement-bar-card.component.js +292 -0
- package/src/lib/components/announcement-bar-card.component.js.map +1 -0
- package/src/lib/components/campaign-composer.d.ts +84 -0
- package/src/lib/components/campaign-composer.js +1643 -0
- package/src/lib/components/campaign-composer.js.map +1 -0
- package/src/lib/components/campaign-conversions-card.d.ts +22 -0
- package/src/lib/components/campaign-conversions-card.js +642 -0
- package/src/lib/components/campaign-conversions-card.js.map +1 -0
- package/src/lib/components/campaign-detail-card.d.ts +55 -0
- package/src/lib/components/campaign-detail-card.js +771 -0
- package/src/lib/components/campaign-detail-card.js.map +1 -0
- package/src/lib/components/campaign-edit-drawer.d.ts +60 -0
- package/src/lib/components/campaign-edit-drawer.js +252 -0
- package/src/lib/components/campaign-edit-drawer.js.map +1 -0
- package/src/lib/components/campaign-email-zones.d.ts +111 -0
- package/src/lib/components/campaign-email-zones.js +54 -0
- package/src/lib/components/campaign-email-zones.js.map +1 -0
- package/src/lib/components/campaign-glance-card.component.d.ts +18 -0
- package/src/lib/components/campaign-glance-card.component.js +151 -0
- package/src/lib/components/campaign-glance-card.component.js.map +1 -0
- package/src/lib/components/campaign-members-section.d.ts +87 -0
- package/src/lib/components/campaign-members-section.js +499 -0
- package/src/lib/components/campaign-members-section.js.map +1 -0
- package/src/lib/components/campaign-reach-sections.d.ts +137 -0
- package/src/lib/components/campaign-reach-sections.js +694 -0
- package/src/lib/components/campaign-reach-sections.js.map +1 -0
- package/src/lib/components/campaign-report-card.d.ts +91 -0
- package/src/lib/components/campaign-report-card.js +667 -0
- package/src/lib/components/campaign-report-card.js.map +1 -0
- package/src/lib/components/campaign-test-send-drawer.d.ts +45 -0
- package/src/lib/components/campaign-test-send-drawer.js +238 -0
- package/src/lib/components/campaign-test-send-drawer.js.map +1 -0
- package/src/lib/components/campaigns-card.d.ts +31 -0
- package/src/lib/components/campaigns-card.js +667 -0
- package/src/lib/components/campaigns-card.js.map +1 -0
- package/src/lib/components/conversion-attribution.component.d.ts +62 -0
- package/src/lib/components/conversion-attribution.component.js +127 -0
- package/src/lib/components/conversion-attribution.component.js.map +1 -0
- package/src/lib/components/email-compose-card.d.ts +12 -0
- package/src/lib/components/email-compose-card.js +157 -0
- package/src/lib/components/email-compose-card.js.map +1 -0
- package/src/lib/components/email-detail.d.ts +40 -0
- package/src/lib/components/email-detail.js +1180 -0
- package/src/lib/components/email-detail.js.map +1 -0
- package/src/lib/components/email-edit-drawer.d.ts +54 -0
- package/src/lib/components/email-edit-drawer.js +172 -0
- package/src/lib/components/email-edit-drawer.js.map +1 -0
- package/src/lib/components/email-messages-widget.d.ts +22 -0
- package/src/lib/components/email-messages-widget.js +49 -0
- package/src/lib/components/email-messages-widget.js.map +1 -0
- package/src/lib/components/email-recipients-card.d.ts +50 -0
- package/src/lib/components/email-recipients-card.js +338 -0
- package/src/lib/components/email-recipients-card.js.map +1 -0
- package/src/lib/components/emails-list-card.d.ts +36 -0
- package/src/lib/components/emails-list-card.js +544 -0
- package/src/lib/components/emails-list-card.js.map +1 -0
- package/src/lib/components/experiment-zones.d.ts +78 -0
- package/src/lib/components/experiment-zones.js +65 -0
- package/src/lib/components/experiment-zones.js.map +1 -0
- package/src/lib/components/host-experiments-card.component.d.ts +17 -0
- package/src/lib/components/host-experiments-card.component.js +887 -0
- package/src/lib/components/host-experiments-card.component.js.map +1 -0
- package/src/lib/components/host-marketing-summary-card.component.d.ts +15 -0
- package/src/lib/components/host-marketing-summary-card.component.js +161 -0
- package/src/lib/components/host-marketing-summary-card.component.js.map +1 -0
- package/src/lib/components/host-overlays-card.component.d.ts +19 -0
- package/src/lib/components/host-overlays-card.component.js +783 -0
- package/src/lib/components/host-overlays-card.component.js.map +1 -0
- package/src/lib/components/marketing-console-page.d.ts +28 -0
- package/src/lib/components/marketing-console-page.js +166 -0
- package/src/lib/components/marketing-console-page.js.map +1 -0
- package/src/lib/components/marketing-console-sections.d.ts +34 -0
- package/src/lib/components/marketing-console-sections.js +77 -0
- package/src/lib/components/marketing-console-sections.js.map +1 -0
- package/src/lib/components/overlay-stats-row.component.d.ts +15 -0
- package/src/lib/components/overlay-stats-row.component.js +78 -0
- package/src/lib/components/overlay-stats-row.component.js.map +1 -0
- package/src/lib/components/popup-card.component.d.ts +18 -0
- package/src/lib/components/popup-card.component.js +462 -0
- package/src/lib/components/popup-card.component.js.map +1 -0
- package/src/lib/components/record-attribution-widget.d.ts +35 -0
- package/src/lib/components/record-attribution-widget.js +33 -0
- package/src/lib/components/record-attribution-widget.js.map +1 -0
- package/src/lib/components/site-runtime.d.ts +9 -0
- package/src/lib/components/site-runtime.js +1081 -0
- package/src/lib/components/site-runtime.js.map +1 -0
- package/src/lib/components/use-campaign-send-api.d.ts +35 -0
- package/src/lib/components/use-campaign-send-api.js +99 -0
- package/src/lib/components/use-campaign-send-api.js.map +1 -0
- package/src/lib/components/use-campaign-topic-options.d.ts +20 -0
- package/src/lib/components/use-campaign-topic-options.js +44 -0
- package/src/lib/components/use-campaign-topic-options.js.map +1 -0
- package/src/lib/components/use-emails-hub-path.d.ts +39 -0
- package/src/lib/components/use-emails-hub-path.js +70 -0
- package/src/lib/components/use-emails-hub-path.js.map +1 -0
- package/src/lib/components/use-marketing-hub-path.d.ts +21 -0
- package/src/lib/components/use-marketing-hub-path.js +50 -0
- package/src/lib/components/use-marketing-hub-path.js.map +1 -0
- package/src/lib/components/use-overlay-copy-editor.d.ts +79 -0
- package/src/lib/components/use-overlay-copy-editor.js +110 -0
- package/src/lib/components/use-overlay-copy-editor.js.map +1 -0
- package/src/lib/constants/bundle-common.d.ts +8 -0
- package/src/lib/constants/bundle-common.js +9 -0
- package/src/lib/constants/bundle-common.js.map +1 -0
- package/src/lib/model/campaign-membership-figures.d.ts +109 -0
- package/src/lib/model/campaign-membership-figures.js +113 -0
- package/src/lib/model/campaign-membership-figures.js.map +1 -0
- package/src/lib/model/compile-client-automations.d.ts +65 -0
- package/src/lib/model/compile-client-automations.js +95 -0
- package/src/lib/model/compile-client-automations.js.map +1 -0
- package/src/lib/model/experiments.d.ts +130 -0
- package/src/lib/model/experiments.js +186 -0
- package/src/lib/model/experiments.js.map +1 -0
- package/src/lib/model/index.d.ts +26 -0
- package/src/lib/model/index.js +26 -0
- package/src/lib/model/index.js.map +1 -0
- package/src/lib/model/marketing-record-routes.d.ts +6 -0
- package/src/lib/model/marketing-record-routes.js +46 -0
- package/src/lib/model/marketing-record-routes.js.map +1 -0
- package/src/lib/model/overlay-copy.d.ts +62 -0
- package/src/lib/model/overlay-copy.js +78 -0
- package/src/lib/model/overlay-copy.js.map +1 -0
- package/src/lib/model/overlays.d.ts +113 -0
- package/src/lib/model/overlays.js +93 -0
- package/src/lib/model/overlays.js.map +1 -0
- package/src/lib/model/site-contract.d.ts +100 -0
- package/src/lib/model/site-contract.js +23 -0
- package/src/lib/model/site-contract.js.map +1 -0
- package/src/lib/plugin.d.ts +27 -0
- package/src/lib/plugin.js +168 -0
- package/src/lib/plugin.js.map +1 -0
- package/src/lib/preview-props.d.ts +31 -0
- package/src/lib/preview-props.js +54 -0
- package/src/lib/preview-props.js.map +1 -0
- package/src/lib/server/campaign-manage.d.ts +62 -0
- package/src/lib/server/campaign-manage.js +829 -0
- package/src/lib/server/campaign-manage.js.map +1 -0
- package/src/lib/server/campaign-process-scheduled.d.ts +26 -0
- package/src/lib/server/campaign-process-scheduled.js +198 -0
- package/src/lib/server/campaign-process-scheduled.js.map +1 -0
- package/src/lib/server/campaign-recipients.d.ts +55 -0
- package/src/lib/server/campaign-recipients.js +219 -0
- package/src/lib/server/campaign-recipients.js.map +1 -0
- package/src/lib/server/campaign-send.d.ts +487 -0
- package/src/lib/server/campaign-send.js +3157 -0
- package/src/lib/server/campaign-send.js.map +1 -0
- package/src/lib/server/email-campaign-reach.d.ts +174 -0
- package/src/lib/server/email-campaign-reach.js +254 -0
- package/src/lib/server/email-campaign-reach.js.map +1 -0
- package/src/lib/server/email-events.d.ts +27 -0
- package/src/lib/server/email-events.js +714 -0
- package/src/lib/server/email-events.js.map +1 -0
- package/src/lib/server/get-client-automations.d.ts +60 -0
- package/src/lib/server/get-client-automations.js +87 -0
- package/src/lib/server/get-client-automations.js.map +1 -0
- package/src/lib/server/get-overlays.d.ts +28 -0
- package/src/lib/server/get-overlays.js +65 -0
- package/src/lib/server/get-overlays.js.map +1 -0
- package/src/lib/server/get-screen-experiments.d.ts +42 -0
- package/src/lib/server/get-screen-experiments.js +82 -0
- package/src/lib/server/get-screen-experiments.js.map +1 -0
- package/src/lib/server/lists-materialize.d.ts +18 -0
- package/src/lib/server/lists-materialize.js +130 -0
- package/src/lib/server/lists-materialize.js.map +1 -0
- package/src/lib/server/marketing-figures.d.ts +37 -0
- package/src/lib/server/marketing-figures.js +248 -0
- package/src/lib/server/marketing-figures.js.map +1 -0
- package/src/lib/server/site-page-enricher.d.ts +26 -0
- package/src/lib/server/site-page-enricher.js +210 -0
- package/src/lib/server/site-page-enricher.js.map +1 -0
- package/src/lib/server.d.ts +20 -0
- package/src/lib/server.js +152 -0
- package/src/lib/server.js.map +1 -0
- package/src/lib/site.d.ts +22 -0
- package/src/lib/site.js +48 -0
- package/src/lib/site.js.map +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/campaign-composer.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 { buildRoute, checkQuota, Route } from '@aglyn/aglyn'\nimport {\n EMAIL_MERGE_TAGS,\n emailMessageMode,\n emailPlainTextState,\n type EmailMessageMode,\n} from '@aglyn/aglyn/app-utils/recipient-email-render'\nimport { useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport QuotaReadoutComponent from '@aglyn/shared-ui-jsx/components/quota-readout.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n Box,\n Button,\n Chip,\n Divider,\n MenuItem,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { collection, doc, limit, query } from 'firebase/firestore'\nimport {\n CampaignDesignCreate,\n CampaignSenderEditor,\n CampaignTopicSelect,\n} from './campaign-email-zones'\nimport { useCampaignSendApi } from './use-campaign-send-api'\nimport {\n useSendingApi,\n type HostSenderView,\n type SendingIdentityView,\n} from '@aglyn/tenant-feature-instance/hooks/use-sending-identity-api'\nimport { describeCallFailure } from '@aglyn/shared-util-http/authorized-token'\nimport CampaignTestSendDrawer from './campaign-test-send-drawer'\nimport { useRouter } from 'next/navigation'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport {\n useConsoleHostRoute,\n useFirestore,\n useFirestoreCollection,\n useFirestoreDoc,\n useOrgDataScope,\n useHostOrgId,\n useOrgPlan,\n} from '@aglyn/tenant-feature-instance'\n\n// The besigner route is `/[orgSlug]/hosts/[host]/screens/[screenId]/\n// versions/[versionId]/besigner`. Takes the resolved org slug + subdomain,\n// not a host doc id.\nconst besignerHref = (\n orgSlug: string,\n host: string,\n screenId: string,\n versionId: string,\n) => buildRoute(Route.SCREEN_BESIGNER, { orgSlug, host, screenId, versionId })\n\n/**\n * How long the composer waits after a keystroke before asking the server to\n * re-render the message.\n *\n * Longer than the audience debounce below because it fires on typing rather\n * than on a picker: a merchant writing a paragraph would otherwise issue a\n * render for every pause in it.\n */\nconst RENDER_DEBOUNCE_MS = 600\n\n/** How long the audience picker settles before the count is re-resolved. */\nconst COUNT_DEBOUNCE_MS = 400\n\n/** The counts a dry run of the send path reports. */\ninterface AudiencePreview {\n sendable: number\n suppressed: number\n /** The whole audience, before the per-send cap. */\n audienceSize: number\n /** `audienceSize` is a floor — the resolution hit its read ceiling. */\n audienceTruncated: boolean\n /** Of `audienceSize`, how many carry a recorded consent basis. */\n consented: number\n /** Of `audienceSize`, how many are reachable only by grandfathering. */\n grandfathered: number\n /** Of `audienceSize`, how many the consent rule refuses. */\n consentWithheld: number\n /**\n * Of `recipients`, how many asked this site for mail less often than this\n * send would arrive. They are still subscribed and the next campaign\n * outside their interval reaches them.\n */\n cadenceHeld: number\n /** Which address this send would leave on, in the server's own words. */\n identity: string\n identitySource: 'custom' | 'shared' | 'platform' | null\n}\n\n/** The message itself, rendered by the send path's own renderer. */\ninterface RenderedPreview {\n subject: string\n html: string\n text: string\n}\n\nconst plural = (count: number, noun: string): string =>\n `${count.toLocaleString()} ${noun}${count === 1 ? '' : 's'}`\n\nexport interface CampaignComposerProps {\n hostId: string\n /**\n * The campaign this send joins. Every send composed here carries it, so the\n * campaign's rollup covers the send without anything re-scanning.\n */\n emailCampaignId?: string\n /** The campaign's lists — the audience picker opens on the first of them. */\n campaignListIds?: string[]\n /**\n * The campaign's topic, which its emails open on.\n *\n * A default and not a constraint: the topic is a property of the MESSAGE —\n * it decides who this send skips, what the preference page highlights, and\n * which stream a resulting opt-out is recorded against — and one campaign\n * may legitimately carry a newsletter and a promotion.\n */\n campaignTopicId?: string\n /**\n * What the merchant called this email when they created it, for the record\n * rather than for the recipient.\n *\n * Collected by the create drawer on the Emails list, which asks for a name\n * the way every other create in the console does. It is NOT the subject: an\n * email's subject is written for the person opening it and gets edited\n * until the moment it sends, and a merchant looking for \"the one with the\n * discount code\" months later has nothing else to look for.\n */\n displayName?: string\n /**\n * The email record this composer is writing, when it is editing one.\n *\n * Present on an email's own page and absent nowhere else that matters: it\n * is what makes Save, Schedule and Send land on the record the reader\n * already has open rather than minting a second one. The id is the email's\n * identity for its whole life — `performCampaignSend` adopts it, so the URL\n * a merchant has open is the URL the sent email keeps, and the `cid=`\n * inside every unsubscribe link it later mints names this same document.\n */\n campaignId?: string\n /**\n * What the record already holds, for a composer opened on an existing\n * email.\n *\n * Passed down rather than read here: the email's page has the document\n * open already, and a second listen on it would be a second read of the\n * same thing on every mount of this component.\n */\n initial?: {\n subject?: string\n body?: string\n fromName?: string\n replyTo?: string\n preheader?: string\n audience?: string\n listId?: string\n segmentId?: string\n topicId?: string\n templateScreenId?: string\n /** The author's own plain-text part; absent means generated. */\n plainText?: string\n /** The design version that part was written against. */\n plainTextVersionId?: string\n sendAtMs?: number\n /** `platform`, or absent for the site's standing selection. */\n sendingIdentity?: string\n /**\n * Which of the site's senders this email is set to go out as, or absent\n * for the site's default.\n *\n * Read back so a draft reopens on the sender it was saved with, and so a\n * scheduled email that is edited does not silently move onto whichever\n * sender has become the default since.\n */\n senderId?: string\n }\n /** Called once a send or a schedule lands. */\n onSent?: () => void\n}\n\n/**\n * COMPOSE AND SEND ONE EMAIL.\n *\n * Everything a merchant decides about a single message: who it goes to, what\n * it says, who it appears to come from, what it will look like, and — before\n * the button that cannot be taken back — how many people it reaches and how\n * many it does not.\n */\n/**\n * The picker entry that OPENS THE SENDER EDITOR rather than selecting a row.\n *\n * A one-off address is deliberately not offered, and this is what replaces it.\n * An address typed into one campaign and stored nowhere is a mailbox nobody\n * serves — its bounces come back to nothing and a client ignoring `Reply-To:`\n * answers into a void — which does not stop being true because the merchant\n * meant it. What they actually want is a sender they had not set up yet, and\n * from here that costs one drawer: validated once, served, and reusable.\n *\n * A one-off NAME and a one-off reply address are a different matter and have\n * always worked, as the two per-send fields below.\n */\nconst ADD_SENDER_OPTION = 'add-a-sender'\n\n/**\n * What one of the two per-send sender fields is DOING, in a sentence.\n *\n * Three states, and the middle one is why this exists. The composer seeds both\n * fields from the chosen sender, so a merchant opening a new email finds them\n * already filled with values that are defined on another screen — and a field\n * that silently matches a setting elsewhere reads as a second definition of\n * it, which is exactly the \"two pages define overlapping things\" confusion the\n * From line above was missing.\n *\n * Naming the inheritance costs a sentence and settles it: the value came from\n * the sender, editing it is per-email, and the sender is unchanged either way.\n * The send agrees — `campaign-send.ts` resolves `options.fromName ||\n * branding.fromName`, so what is submitted here is what goes out and what is\n * recorded on the report.\n */\nfunction senderFieldHelp(\n field: 'name' | 'reply address',\n saved: string,\n current: string,\n): string {\n const base =\n field === 'name'\n ? 'Shown in front of the address.'\n : 'Where replies go — any mailbox, including a personal one.'\n if (!saved) {\n return field === 'name'\n ? `${base} Your brand name is used when this is empty.`\n : base\n }\n return current.trim() === saved\n ? `${base} This is the sender’s saved ${field}; editing it changes this email only.`\n : `${base} Overrides the sender’s saved ${field}, ${saved}, for this email only.`\n}\n\nexport function CampaignComposer(props: CampaignComposerProps) {\n const {\n hostId,\n emailCampaignId,\n campaignListIds,\n campaignTopicId,\n displayName,\n campaignId,\n initial,\n onSent,\n } = props\n const { orgSlug, subdomain } = useConsoleHostRoute(hostId)\n // Org-shared data root. Null until the org lookup settles, and for a host\n // with no owning org — so the audience picker offers the built-ins alone\n // rather than segments and lists from a dead path.\n const { scope: dataScope } = useOrgDataScope({ hostId })\n const firestore = useFirestore()\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const router = useRouter()\n\n // Contact segments join the built-in audiences.\n const { data: segmentDocs } = useFirestoreCollection<any>(\n () =>\n dataScope\n ? query(\n collection(\n firestore,\n dataScope[0],\n dataScope[1],\n 'contactSegments',\n ),\n limit(50),\n )\n : null,\n [firestore, dataScope],\n { idField: '$id' },\n )\n const segments = [...(segmentDocs ?? [])].sort((a, b) =>\n String(a.name ?? '').localeCompare(String(b.name ?? '')),\n )\n // Org email lists join the audiences.\n const { data: listDocs } = useFirestoreCollection<any>(\n () =>\n dataScope\n ? query(\n collection(firestore, dataScope[0], dataScope[1], 'lists'),\n limit(50),\n )\n : null,\n [firestore, dataScope],\n { idField: '$id' },\n )\n const lists = [...(listDocs ?? [])].sort((a, b) =>\n String(a.name ?? '').localeCompare(String(b.name ?? '')),\n )\n\n /**\n * The monthly campaign allowance, standing rather than only on refusal.\n *\n * READ THE ENFORCEABLE METER, NOT THE COST ONE. `emailSends` beside it\n * counts every receipt, booking reminder and password reset the site sent;\n * showing that total against this cap would tell a merchant they had spent\n * their campaign allowance on order confirmations.\n *\n * PER ORG, because that is where the cap is enforced.\n * `emailSendsPerMonth` is an ORG entitlement and `reserveCampaignEmailSends`\n * claims against `orgs/{orgId}/counters/campaignEmailSends[YYYY-MM]`. A\n * readout and its gate must read the same counter — the number a merchant\n * checks before pressing Send is the worst possible place for a second\n * opinion.\n */\n const { org, ready: orgReady } = useOrgPlan(hostId)\n const campaignOrgId = useHostOrgId(hostId)\n const campaignMonthKey = new Date().toISOString().slice(0, 7)\n const { data: campaignSendCounter } = useFirestoreDoc<\n Record<string, unknown>\n >(\n () =>\n campaignOrgId\n ? doc(firestore, 'orgs', campaignOrgId, 'counters', 'campaignEmailSends')\n : null,\n [firestore, campaignOrgId],\n )\n // An org that has never sent a campaign has no counter document at all;\n // that is a settled zero, and the same zero `orgCampaignEmailSendsForMonth`\n // resolves it to on the server.\n const campaignSendsUsed = Number(\n campaignSendCounter?.[campaignMonthKey] ?? 0,\n )\n\n // Email A/B experiments: running (or winner-decided) email experiments the\n // composer can attach.\n const { data: experimentDocs } = useFirestoreCollection<any>(\n () =>\n query(collection(firestore, 'hosts', hostId, 'experiments'), limit(50)),\n [firestore, hostId],\n { idField: '$id' },\n )\n const emailExperiments = [...(experimentDocs ?? [])]\n .filter(\n (experiment: any) =>\n !experiment.deletedAt &&\n experiment.target === 'email' &&\n (experiment.status === 'running' || experiment.winnerVariantId),\n )\n .sort((a: any, b: any) =>\n String(a.name ?? '').localeCompare(String(b.name ?? '')),\n )\n\n // Designed emails: besigner email documents are screens with kind 'email';\n // campaigns reference them by screen id.\n const { data: screenDocs } = useFirestoreCollection<any>(\n () => query(collection(firestore, 'hosts', hostId, 'screens'), limit(200)),\n [firestore, hostId],\n { idField: '$id' },\n )\n const emailScreens = [...(screenDocs ?? [])]\n .filter((screen: any) => !screen.deletedAt && screen.kind === 'email')\n .sort((a: any, b: any) =>\n String(a.displayName ?? '').localeCompare(String(b.displayName ?? '')),\n )\n\n /*\n * The stored audience, back in the picker's own `kind:id` form.\n *\n * The record keeps the kind and the id in two fields because that is what\n * the send path reads; the picker is one control, so the two are rejoined\n * here. A composer opened on an existing email that defaulted to Leads\n * would be one save away from silently re-aiming it.\n */\n const storedAudience = initial?.audience\n ? initial.audience === 'list' && initial.listId\n ? `list:${initial.listId}`\n : initial.audience === 'segment' && initial.segmentId\n ? `segment:${initial.segmentId}`\n : initial.audience\n : ''\n\n const [subject, setSubject] = useState(initial?.subject ?? '')\n const [body, setBody] = useState(initial?.body ?? '')\n const [fromName, setFromName] = useState(initial?.fromName ?? '')\n const [replyTo, setReplyTo] = useState(initial?.replyTo ?? '')\n const [preheader, setPreheader] = useState(initial?.preheader ?? '')\n /**\n * WHICH OF THIS SITE'S IDENTITIES THIS EMAIL LEAVES ON.\n *\n * Two values and no more — empty for the site's standing selection, and\n * `platform` for the domain the platform issued the site, which is what\n * clearing the selection resolves to. It is deliberately not a domain name:\n * which custom domain a site may use is an org-admin decision stored on the\n * host, and a composer that could name one would be a way for a site editor\n * to send as a domain their site was never given.\n */\n const [sendingIdentity, setSendingIdentity] = useState(\n initial?.sendingIdentity ?? '',\n )\n /**\n * WHICH OF THE SITE'S SENDERS THIS EMAIL GOES OUT AS.\n *\n * An id into `hosts/{hostId}/senders`, empty for the site's default. It is\n * an id and not an address, which is the difference between this and the\n * field above it: the mailbox behind it was configured once under the\n * organization-admin gate, so a composer choosing here can only reach an\n * address the site was already set up to send as.\n */\n const [senderId, setSenderId] = useState(initial?.senderId ?? '')\n /*\n * The sender this composer OPENED on, which is a stable prop rather than the\n * state above. The identity read below seeds two fields from it, and\n * depending on the state would re-issue that request every time somebody\n * changed the picker.\n */\n const openedAsSenderId = initial?.senderId ?? ''\n /*\n * The campaign's own list is where the composer opens, because a send\n * composed inside a campaign aimed at a list is overwhelmingly a send to\n * that list — and an audience picker that defaults to Leads inside such a\n * campaign is one wrong keystroke away from mailing the wrong people.\n */\n const [audience, setAudience] = useState<string>(\n storedAudience || (campaignListIds?.[0] ? `list:${campaignListIds[0]}` : 'leads'),\n )\n const [experimentId, setExperimentId] = useState('')\n /*\n * WHICH STREAM this email belongs to.\n *\n * Empty until the picker settles it against the org's catalog, and sent on\n * every request that resolves an audience — the count as much as the send.\n * `filterTopicSendable` removes the people who have left this stream, so a\n * preview taken without the topic reports a reach the send will not deliver,\n * and a send taken without it records `marketing` for a sales campaign,\n * which is the opt-out a recipient already exercised being ignored.\n */\n const [topicId, setTopicId] = useState(\n initial?.topicId ?? campaignTopicId ?? '',\n )\n const [templateScreenId, setTemplateScreenId] = useState(\n initial?.templateScreenId ?? '',\n )\n /**\n * HOW THIS EMAIL IS WRITTEN — a choice, not an inference.\n *\n * The form used to show a design picker and a message box at once and say\n * nothing about which one the send would use. It used the design and dropped\n * the message, silently, so a merchant who did both mailed something they\n * had not written.\n *\n * TWO modes, because the data model has two things and not three: a saved\n * TEMPLATE is a besigner email screen — the same `kind: 'email'` document\n * the picker below lists and \"Design this email\" creates — so \"designed\"\n * and \"from a template\" are one mode under two names. There is no third\n * thing to offer.\n *\n * Seeded from the record through the same resolver the send path reads, so\n * a draft reopens in the mode it was saved in rather than in whichever the\n * form happens to default to.\n */\n const [mode, setMode] = useState<EmailMessageMode>(() =>\n emailMessageMode({ templateScreenId: initial?.templateScreenId }),\n )\n /**\n * WHERE A DESIGNED EMAIL'S PLAIN-TEXT HALF COMES FROM.\n *\n * Generated by default — the design already produces one, and it is what\n * every designed campaign has always mailed. `authored` is the deliberate\n * override, held as its own state rather than inferred from the text being\n * non-empty: choosing to write one and then clearing the box would\n * otherwise snap the choice back under the author's hands.\n *\n * The RECORD still says it with presence alone — a stored `plainText` is an\n * override and an absent one is generated — so there is no flag on the\n * document to fall out of step with the value beside it.\n */\n const [plainTextSource, setPlainTextSource] = useState<\n 'generated' | 'authored'\n >(() => emailPlainTextState({ plainText: initial?.plainText }).source)\n const [plainText, setPlainText] = useState(initial?.plainText ?? '')\n /**\n * Which design version the text part above was written against.\n *\n * Stamped on every edit, so the record carries the answer to \"does this\n * still describe the design\" rather than leaving a reader to guess. The\n * composer compares it with the design's current version and says when the\n * two have parted; nothing overwrites the author's words on their behalf.\n */\n const [plainTextVersionId, setPlainTextVersionId] = useState(\n initial?.plainTextVersionId ?? '',\n )\n // Scheduling: a future timestamp turns Send into Schedule.\n const [sendAt, setSendAt] = useState('')\n /** Whether the sender editor is open, opened from the From picker. */\n const [addingSender, setAddingSender] = useState(false)\n const [busy, setBusy] = useState(false)\n const [showPreview, setShowPreview] = useState(false)\n\n const designed = mode === 'design'\n /*==========================================\n * WHAT THIS COMPOSER SUBMITS: the field its mode owns, and nothing else.\n *\n * Both halves are kept in component state while the form is open, so\n * switching modes to look at the other one and switching back loses no\n * typing. Only one of them is ever SENT — which is what makes the discard\n * impossible from here rather than merely warned about, and what keeps the\n * stored record in one mode instead of carrying two sources for the route\n * to choose between later.\n *=========================================*/\n const sentTemplateScreenId = designed ? templateScreenId : ''\n const sentBody = designed ? '' : body.trim()\n /*\n * The text half rides only where it has a job. On a plain-text email the\n * body IS the text part, so an override there would be a second source for\n * the same half — which the route refuses, and which this never submits.\n */\n const sentPlainText =\n designed && plainTextSource === 'authored' ? plainText.trim() : ''\n /** Whether the mode has what it needs — a design, or something written. */\n const messageReady = designed\n ? Boolean(templateScreenId)\n : Boolean(body.trim())\n\n const selectedTemplate = emailScreens.find(\n (screen: any) => screen.$id === templateScreenId,\n )\n\n /*\n * The draft save, reachable from above its declaration. `handleDesignCreated`\n * persists the created design onto the record through the same save the\n * Save draft button uses, and that callback closes over most of the form —\n * a ref keeps this one from having to depend on all of it.\n */\n const handleSaveDraftRef = useRef<\n ((overrides?: { templateScreenId?: string }) => Promise<void>) | null\n >(null)\n\n /**\n * A DESIGN FOR THIS EMAIL, without making the author think in templates.\n *\n * A one-off campaign does not want a reusable artifact — it wants its own\n * design document. The data model has only one shape for that (a `kind:\n * 'email'` screen, the same document the picker lists), so this mints one,\n * but named after the email it belongs to rather than \"Untitled email\":\n * the name is the only thing that tells one design from another in the\n * picker, and a list of identical \"Untitled email\" rows tells nobody\n * anything. Saved templates stay what they are — picking one from the\n * select above is the reuse path.\n *\n * The selection is written onto the record before the editor navigation\n * unmounts this form; without that, the design would exist while the\n * campaign still pointed at nothing, and the author would have to come\n * back and find their own screen in the picker.\n */\n const handleDesignFailed = useCallback(\n (error: unknown) => {\n console.error(error)\n enqueueSnackbar(\n (error as { message?: string } | null)?.message ??\n 'Creating the email design failed',\n { variant: 'error' },\n )\n },\n [enqueueSnackbar],\n )\n /*\n * The design itself is minted by the widget in the create zone — a design\n * document is the email plugin's — and named here. What is this composer's\n * is the campaign: recording the choice on it, and opening the editor.\n */\n const handleDesignCreated = useCallback(\n async (design: { screenId: string; versionId: string }) => {\n const { screenId, versionId } = design\n try {\n setTemplateScreenId(screenId)\n if (campaignId) {\n await handleSaveDraftRef.current?.({ templateScreenId: screenId })\n }\n if (orgSlug && subdomain) {\n void router.push(\n besignerHref(orgSlug, subdomain, screenId, versionId),\n )\n }\n } catch (error) {\n handleDesignFailed(error)\n }\n },\n [campaignId, orgSlug, subdomain, router, handleDesignFailed],\n )\n\n /*\n * The one authorized caller of the campaign API, shared with the email\n * detail page's \"send to more people\". It holds the user in a ref for a\n * reason the preview effects below depend on — see the hook.\n */\n const authorizedPost = useCampaignSendApi(hostId)\n\n /*\n * Proofing is a drawer now, not a button that mails you.\n *\n * \"Send test to me\" could only ever answer one of the three questions a\n * proof is asked: what does this look like, to somebody with real data, at\n * an address I can actually open. It mailed the caller, rendered against\n * the caller, from whatever identity the site happened to hold — so a\n * merchant checking their merge tags saw the fallbacks every time.\n */\n const [testOpen, setTestOpen] = useState(false)\n\n /*\n * The audience select's value packs the kind and the id into one string\n * (`list:abc`). Decomposed ONCE here so the preview and the send cannot\n * disagree about what they are asking for — they had two copies of this\n * split, which is how a preview counts a segment while the send resolves\n * a list.\n */\n const audienceKind = audience.startsWith('segment:')\n ? 'segment'\n : audience.startsWith('list:')\n ? 'list'\n : audience\n const segmentId = audience.startsWith('segment:')\n ? audience.slice('segment:'.length)\n : ''\n const listId = audience.startsWith('list:')\n ? audience.slice('list:'.length)\n : ''\n\n /**\n * HOW MANY PEOPLE, AND ON WHAT BASIS — a dry run of the real send path.\n *\n * It comes from the send path rather than a count of its own, so it has\n * already been through audience resolution, de-duplication, the consent\n * rule, the per-send cap, the suppression list and the monthly quota.\n * Counting the audience here instead would be a second set of rules to\n * drift from the one that decides what actually goes out — on the one\n * number a merchant checks before pressing Send.\n */\n const [preview, setPreview] = useState<\n AudiencePreview | { error: string; blocking?: boolean } | null\n >(null)\n useEffect(() => {\n let active = true\n setPreview(null)\n // Debounced: switching audience with the keyboard walks the whole\n // list, and each stop would otherwise be a full audience resolution.\n const timer = setTimeout(async () => {\n try {\n /*\n * NO SUBJECT AND NO BODY, and that is the point of this request.\n *\n * The count is a fact about the AUDIENCE. Nothing in resolving it\n * reads the copy, so nothing about the copy may gate it — a merchant\n * asks \"how many people is this\" before writing the email, which is\n * when the answer is worth having. Sending the draft along would also\n * put the composer's whole audience resolution — up to five thousand\n * documents — behind every keystroke, for a number that had not moved.\n * The message itself is previewed by `rendered` below, which resolves\n * nobody.\n */\n const { response, payload } = await authorizedPost({\n action: 'preview',\n audience: audienceKind,\n ...(segmentId ? { segmentId } : {}),\n ...(listId ? { listId } : {}),\n ...(topicId ? { topicId } : {}),\n /*\n * The identity rides the count, which is what puts the refusal in\n * front of the Send button instead of behind it. The dry run\n * resolves the sending identity on exactly the terms a real send\n * does and answers 409 the same way, so an unverified domain is\n * refused here — before any copy is written — rather than after\n * somebody presses Send.\n */\n ...(sendingIdentity ? { sendingIdentity } : {}),\n /*\n * The chosen SENDER rides the count for the same reason. A sender\n * this site no longer holds is refused by the dry run on exactly\n * the terms a send refuses it, so a draft pointing at a removed one\n * says so at the picker rather than at Send.\n */\n ...(senderId ? { senderId } : {}),\n })\n if (!active) return\n if (!response.ok) {\n // The refusals are useful, not noise: \"The audience is empty\"\n // and the monthly-cap message are exactly what a merchant needs\n // BEFORE writing the email rather than after.\n //\n // A 409 is the one that must also STOP the send. Every other\n // refusal here is about the audience and can be true while the\n // email is still worth composing; this one says the message has\n // nowhere to leave from, and letting Send stay live would trade a\n // sentence the merchant can act on for the same 409 arriving after\n // the click.\n return setPreview({\n error: String(payload?.error ?? ''),\n blocking: response.status === 409,\n })\n }\n setPreview({\n sendable: Number(payload?.sendable ?? 0),\n suppressed: Number(payload?.suppressed ?? 0),\n audienceSize: Number(payload?.audienceSize ?? 0),\n audienceTruncated: Boolean(payload?.audienceTruncated),\n consented: Number(payload?.consented ?? 0),\n grandfathered: Number(payload?.grandfathered ?? 0),\n consentWithheld: Number(payload?.consentWithheld ?? 0),\n cadenceHeld: Number(payload?.cadenceHeld ?? 0),\n identity: String(payload?.identity ?? ''),\n identitySource: (payload?.identitySource ?? null) as\n | 'custom'\n | 'shared'\n | 'platform'\n | null,\n })\n } catch (error) {\n /*\n * A REFUSAL IS NOT A LOADING STATE.\n *\n * `null` is the value this holds before the first answer, and the\n * readout renders it as \"Counting recipients…\". A call that failed\n * before it ever reached the route — authorization that could not be\n * obtained — has to read as the refusal it is, or the composer sits\n * on a sentence about work it has stopped doing and the confirm\n * dialog reports an unreadable count without saying why.\n */\n if (active)\n setPreview({\n error: describeCallFailure(error, 'Could not count this audience'),\n })\n }\n }, COUNT_DEBOUNCE_MS)\n return () => {\n active = false\n clearTimeout(timer)\n }\n }, [\n authorizedPost,\n audienceKind,\n segmentId,\n listId,\n topicId,\n sendingIdentity,\n senderId,\n ])\n\n /**\n * THE IDENTITIES THIS SITE MAY SEND AS, for the picker.\n *\n * One cheap read — the host document and at most one org subcollection\n * document — issued once, and NOT part of the debounced count above: it\n * answers a question about the site rather than about the message, so it\n * cannot move while somebody types.\n *\n * The picker is only offered when there is a choice to make. A site with no\n * verified domain has exactly one identity, and a select with one option is\n * a control that reads as a decision the person failed to take.\n */\n const [identityOptions, setIdentityOptions] = useState<\n { value: string; from: string | null; selectable: boolean }[] | null\n >(null)\n /**\n * The value that means \"the site's standing selection\" in the picker.\n *\n * The picker's options are keyed by domain (and `platform`), while the SEND\n * carries the two-valued thing — empty or `platform`. This is the join\n * between them, and it is derived from the options rather than stored: the\n * site's own domain is whichever offered option is not the platform one.\n */\n const siteDefaultIdentity =\n identityOptions?.find((one) => one.value !== 'platform')?.value ?? 'platform'\n /**\n * The identity as the route reports it, whole.\n *\n * Kept rather than picked apart into the two fields this file reads,\n * because the sender drawer opens from here and takes the same view the\n * Sending card hands it — so the editor and the picker agree about what a\n * sender currently is instead of holding two answers.\n *\n * Null until the read lands, so the controls below render nothing rather\n * than an empty picker — the same reason `identityOptions` starts null.\n */\n const [identityView, setIdentityView] = useState<SendingIdentityView | null>(\n null,\n )\n const senders = identityView?.senders ?? null\n /**\n * Bumped to re-read the identity, and the pending row to select when it\n * lands.\n *\n * A sender added from the composer exists only after the route answers, so\n * the selection cannot be made at the moment somebody presses Save — the\n * list it would select from has not been read yet. The id is parked here and\n * applied by the read, which is also what puts the new sender's name and\n * reply address into the two fields below.\n *\n * A ref rather than state for the pending id: it is read INSIDE the effect,\n * and as state it would be a dependency, so every selection would re-issue\n * the request that had just answered.\n */\n const [identityReads, setIdentityReads] = useState(0)\n const pendingSenderRef = useRef('')\n const sendingApi = useSendingApi()\n useEffect(() => {\n let active = true\n void (async () => {\n const { response, payload } = await sendingApi({\n path: 'sending-identity',\n method: 'GET',\n query: { hostId },\n })\n if (!active || !response.ok) return\n setIdentityOptions(payload?.options ?? [])\n const rows = (payload?.senders ?? []) as HostSenderView[]\n setIdentityView(payload as SendingIdentityView)\n /*\n * A sender just added from this composer is selected here, where the\n * row it names finally exists, and brings its two fields with it —\n * which is the same thing choosing an existing sender does.\n */\n const pending = pendingSenderRef.current\n pendingSenderRef.current = ''\n const added = pending ? rows.find((one) => one.id === pending) : null\n if (added) {\n setSenderId(added.id)\n setFromName(added.fromName ?? '')\n setReplyTo(added.replyTo ?? '')\n return\n }\n /*\n * THE SENDER'S NAME AND REPLY ADDRESS, AS A STARTING POINT AND NOT AS\n * AN OVERRIDE.\n *\n * A sender carries who its mail comes from — a person's name and where\n * their replies land — and this is where that reaches a campaign. It is\n * applied only to a field that is still empty, so it seeds a new\n * composer and never rewrites a value somebody typed, and never one a\n * saved email already carries.\n *\n * Seeding rather than falling back at the send is the difference\n * between a merchant SEEING who this will come from and finding out\n * afterwards. It also keeps one answer on the record: the send stores\n * the fields the composer submitted, so what the report says went out\n * is what was on screen when somebody pressed the button.\n *\n * From the sender this email ALREADY names where it names one, and from\n * the site's fields otherwise. An email set to go out as Jamie seeded\n * from the site's default would open with the wrong person's name in\n * front of Jamie's address.\n */\n const opened = openedAsSenderId\n ? rows.find((one) => one.id === openedAsSenderId)\n : null\n const siteFromName = String(\n (opened ? opened.fromName : payload?.fromName) ?? '',\n )\n const siteReplyTo = String(\n (opened ? opened.replyTo : payload?.replyTo) ?? '',\n )\n if (siteFromName) setFromName((current) => current || siteFromName)\n if (siteReplyTo) setReplyTo((current) => current || siteReplyTo)\n })().catch(() => undefined)\n return () => {\n active = false\n }\n }, [sendingApi, hostId, openedAsSenderId, identityReads])\n\n /**\n * CHOOSING A SENDER BRINGS THE THREE FIELDS IT CARRIES.\n *\n * Picking one is \"send this as Jamie\", so the display name and reply address\n * configured beside the mailbox come with it — including when the sender has\n * none, because leaving the previous person's name in front of somebody\n * else's address is the one outcome nobody asked for. Both fields stay\n * editable afterwards: they are per-message facts, and the send records what\n * was submitted rather than re-deriving them.\n */\n const applySender = useCallback(\n (next: string) => {\n /*\n * A REAL ROW ALWAYS WINS, and the sentinel is only reached when none\n * matched. `ADD_SENDER_OPTION` is a value this product never mints as a\n * document id, and checking the list first means that even if one ever\n * arrived it would be selected as the sender it is rather than opening\n * a drawer — a picker cannot be made to send as the wrong address by a\n * value that happens to collide with a menu entry.\n */\n const chosen = (senders ?? []).find((one) => one.id === next)\n if (!chosen) {\n if (next === ADD_SENDER_OPTION) setAddingSender(true)\n return\n }\n setSenderId(chosen.id)\n setFromName(chosen.fromName ?? '')\n setReplyTo(chosen.replyTo ?? '')\n },\n [senders],\n )\n\n /**\n * The sender this email will actually leave as, and the address with it.\n *\n * Resolved from the list rather than from `senderId` alone, because an email\n * that names no sender leaves on the site's DEFAULT — which is a real row\n * with a real address, and the one the merchant needs to see when they have\n * chosen nothing.\n */\n const chosenSender =\n (senders ?? []).find((one) => one.id === senderId) ??\n (senders ?? []).find((one) => one.isDefault) ??\n (senders ?? [])[0] ??\n null\n\n /**\n * WHAT THE EMAIL LOOKS LIKE, rendered by the code that will mail it.\n *\n * Re-rendered as the copy changes and resolving no audience at all, which\n * is why it is a second request rather than a field on the count above.\n * Only while the pane is open: it reads the designed template and its\n * products, and a merchant who never opens the preview should not pay for\n * one on every campaign.\n */\n const [rendered, setRendered] = useState<\n RenderedPreview | { error: string } | null\n >(null)\n useEffect(() => {\n if (!showPreview) return setRendered(null)\n let active = true\n setRendered(null)\n const timer = setTimeout(async () => {\n try {\n const { response, payload } = await authorizedPost({\n action: 'renderPreview',\n subject: subject.trim(),\n body: sentBody,\n preheader: preheader.trim(),\n ...(sentTemplateScreenId\n ? { templateScreenId: sentTemplateScreenId }\n : {}),\n ...(sentPlainText ? { plainText: sentPlainText } : {}),\n })\n if (!active) return\n if (!response.ok) {\n return setRendered({\n error: String(payload?.error ?? 'Could not render this email'),\n })\n }\n setRendered({\n subject: String(payload?.subject ?? ''),\n html: String(payload?.html ?? ''),\n text: String(payload?.text ?? ''),\n })\n } catch (error) {\n if (active)\n setRendered({\n error: describeCallFailure(error, 'Could not render this email'),\n })\n }\n }, RENDER_DEBOUNCE_MS)\n return () => {\n active = false\n clearTimeout(timer)\n }\n }, [\n authorizedPost,\n showPreview,\n subject,\n sentBody,\n preheader,\n sentTemplateScreenId,\n sentPlainText,\n ])\n\n /**\n * The design's OWN plain text, fetched once so an override starts from what\n * it is replacing rather than from an empty box.\n *\n * `messageText` and not `text`: the rendered `text` ends in this preview's\n * opt-out line, and folding that into a stored override would put a second\n * footer in every message the send appends its own to.\n *\n * One request, on an explicit act. The preview effect above is deliberately\n * behind a button because it re-renders as the copy changes; this fires when\n * somebody chooses to write the text part, which happens once.\n */\n const [adoptingText, setAdoptingText] = useState(false)\n const adoptGeneratedText = useCallback(async () => {\n if (!templateScreenId || adoptingText) return\n setAdoptingText(true)\n try {\n const { response, payload } = await authorizedPost({\n action: 'renderPreview',\n subject: subject.trim(),\n body: '',\n preheader: preheader.trim(),\n templateScreenId,\n })\n if (!response.ok) {\n return void enqueueSnackbar(\n payload?.error ?? 'The design’s text could not be read',\n { variant: 'warning', allowDuplicate: true },\n )\n }\n setPlainText(String(payload?.messageText ?? ''))\n setPlainTextVersionId(selectedTemplate?.versionId ?? '')\n } catch (error) {\n enqueueSnackbar(\n describeCallFailure(error, 'The design’s text could not be read'),\n { variant: 'error', allowDuplicate: true },\n )\n } finally {\n setAdoptingText(false)\n }\n }, [\n templateScreenId,\n adoptingText,\n authorizedPost,\n subject,\n preheader,\n selectedTemplate,\n enqueueSnackbar,\n ])\n\n /**\n * Whether the text part still describes the design, in the composer's own\n * words.\n *\n * The same pure function the record's shape is defined by, so the notice on\n * screen and the state stored cannot disagree about what is authored and\n * what has gone stale.\n */\n const plainTextState = emailPlainTextState(\n {\n plainText: plainTextSource === 'authored' ? plainText : '',\n plainTextVersionId,\n },\n selectedTemplate?.versionId,\n )\n\n const audienceLabel =\n audience === 'leads'\n ? 'lead'\n : audience === 'members'\n ? 'site member'\n : audience.startsWith('list:')\n ? 'list subscriber'\n : 'contact in the segment'\n\n /**\n * WHAT THE CONFIRM DIALOG SAYS, and why it says a number.\n *\n * A dialog for an irreversible bulk action has one job: state the size of\n * what is about to happen. \"goes to every list subscriber who hasn't\n * unsubscribed\" states a RULE, and a merchant cannot tell from a rule\n * whether they are about to mail forty people or four thousand.\n *\n * Three cases, and the third is the one worth being careful about:\n *\n * - the audience fits in one send — say how many;\n * - it does not — say how many of how many, because \"every subscriber\" is\n * then simply false and the remainder is never mailed by this send;\n * - the count could not be read — say THAT, and do not fall back to a\n * sentence that implies completeness. A confirm dialog that asserts a\n * reach it does not know is worse than one that admits it.\n */\n const counts = preview && !('error' in preview) ? preview : null\n const confirmDescription = useCallback(\n (scheduling: boolean, sendAtMs: number): string => {\n const quoted = `\"${subject.trim()}\"`\n const when = scheduling\n ? ` on ${new Date(sendAtMs).toLocaleString()}`\n : ''\n if (!counts) {\n const reason =\n preview && 'error' in preview && preview.error\n ? ` (${preview.error})`\n : ''\n return (\n `${quoted} goes to every ${audienceLabel} who hasn't ` +\n `unsubscribed${when}. The recipient count could not be read${reason}, ` +\n `so this send's size is unknown.`\n )\n }\n const short = counts.audienceSize > counts.sendable\n const reach = short\n ? `${plural(counts.sendable, `${audienceLabel}`)} of ` +\n `${counts.audienceSize.toLocaleString()}${\n counts.audienceTruncated ? ' or more' : ''\n } in this audience`\n : plural(counts.sendable, audienceLabel)\n const withheld: string[] = []\n if (counts.consentWithheld) {\n withheld.push(\n `${counts.consentWithheld.toLocaleString()} withheld — no marketing ` +\n `consent on record`,\n )\n }\n if (counts.suppressed) {\n withheld.push(\n `${counts.suppressed.toLocaleString()} unsubscribed or suppressed`,\n )\n }\n /*\n * Named separately from `suppressed`, because the two ask different\n * things of a merchant. An address that unsubscribed is gone; one\n * holding for its own cadence arrives at the next campaign outside its\n * interval, and reading that as an unsubscribe would send somebody\n * looking for a broken audience that is not broken.\n */\n if (counts.cadenceHeld) {\n withheld.push(\n `${counts.cadenceHeld.toLocaleString()} asked for mail less often ` +\n `than this`,\n )\n }\n if (counts.grandfathered) {\n withheld.push(\n `${counts.grandfathered.toLocaleString()} reachable only because ` +\n `consent is not enforced retroactively`,\n )\n }\n return (\n `${quoted} goes to ${reach}${when}.` +\n (withheld.length ? ` Not counted: ${withheld.join('; ')}.` : '')\n )\n },\n [counts, preview, subject, audienceLabel],\n )\n\n const handleSend = useCallback(async () => {\n if (!subject.trim() || !messageReady || busy) return\n const sendAtMs = sendAt ? new Date(sendAt).getTime() : 0\n const scheduling = Boolean(sendAtMs)\n if (scheduling && sendAtMs <= Date.now()) {\n return void enqueueSnackbar('Pick a future send time', {\n variant: 'warning',\n persist: false,\n })\n }\n const confirmed = await confirm({\n title: scheduling ? 'Schedule this campaign?' : 'Send this campaign?',\n description: confirmDescription(scheduling, sendAtMs),\n confirmationText: scheduling ? 'Schedule' : 'Send',\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n setBusy(true)\n try {\n const { response, payload } = await authorizedPost({\n ...(scheduling ? { action: 'schedule', sendAtMs } : {}),\n /*\n * The record this composer is editing, when it is editing one. The\n * send path adopts the id rather than minting a new one, so sending a\n * draft turns THAT document into the sent email — the page the\n * merchant is looking at stays the page the email lives at, and the\n * `cid=` in its unsubscribe links names it.\n */\n ...(campaignId ? { campaignId } : {}),\n subject: subject.trim(),\n body: sentBody,\n audience: audienceKind,\n ...(segmentId ? { segmentId } : {}),\n ...(listId ? { listId } : {}),\n ...(experimentId ? { experimentId } : {}),\n ...(sentTemplateScreenId\n ? { templateScreenId: sentTemplateScreenId }\n : {}),\n ...(sentPlainText ? { plainText: sentPlainText } : {}),\n ...(emailCampaignId ? { emailCampaignId } : {}),\n ...(displayName ? { displayName } : {}),\n ...(topicId ? { topicId } : {}),\n ...(senderId ? { senderId } : {}),\n fromName: fromName.trim(),\n replyTo: replyTo.trim(),\n preheader: preheader.trim(),\n })\n if (response.status === 501) {\n return void enqueueSnackbar(\n 'Campaigns are not configured on this deployment',\n { variant: 'info', persist: false },\n )\n }\n if (!response.ok) {\n return void enqueueSnackbar(payload?.error ?? 'Send failed', {\n variant: 'warning',\n allowDuplicate: true,\n })\n }\n enqueueSnackbar(\n scheduling\n ? `Scheduled for ${new Date(sendAtMs).toLocaleString()}`\n : `Sent to ${payload.sent} of ${payload.recipients} recipients`,\n { variant: 'success', persist: false },\n )\n /*\n * Cleared only when this composer mints its own sends.\n *\n * On an email's own page the fields ARE that email, and the record now\n * holds exactly what was just sent — blanking them would show the\n * merchant an empty composer for a message that went out a second ago.\n */\n if (!campaignId) {\n setSubject('')\n setBody('')\n setSendAt('')\n setPreheader('')\n }\n onSent?.()\n } catch (error) {\n /*\n * A CLICK THAT DID NOTHING HAS TO SAY SO.\n *\n * This covers the failures that never reach the route at all — chiefly\n * an ID token that cannot be obtained, which is awaited in front of the\n * request. `describeCallFailure` lets that one name itself, because\n * \"nothing was sent, you are signed out\" and \"the send failed\" call for\n * different things from the person reading it.\n */\n console.error(error)\n enqueueSnackbar(describeCallFailure(error, 'An error has occurred'), {\n variant: 'error',\n allowDuplicate: true,\n })\n } finally {\n setBusy(false)\n }\n }, [\n subject,\n sentBody,\n sentPlainText,\n messageReady,\n audienceKind,\n segmentId,\n listId,\n experimentId,\n sentTemplateScreenId,\n emailCampaignId,\n topicId,\n senderId,\n fromName,\n replyTo,\n preheader,\n sendAt,\n busy,\n campaignId,\n displayName,\n authorizedPost,\n confirm,\n confirmDescription,\n enqueueSnackbar,\n onSent,\n ])\n\n /*==========================================\n * SAVING WITHOUT SENDING.\n *\n * The draft is the working document, and it has to be savable on its own —\n * a composer whose only way out is Send is a composer that makes a merchant\n * choose between mailing something half-written and losing it.\n *\n * Only offered when this composer is editing a record. Without a\n * `campaignId` there is nothing to save INTO, and minting a draft from a\n * surface that did not ask for one would leave unsent records behind every\n * time somebody opened a composer and changed their mind.\n *=========================================*/\n const [saving, setSaving] = useState(false)\n const handleSaveDraft = useCallback(\n /*\n * `overrides.templateScreenId` exists for `handleDesignCreated`: it has\n * just minted a design and set it into state, but state set this tick is\n * not yet in `sentTemplateScreenId`, so the save it fires immediately\n * after would record the record's previous design — or none.\n */\n async (overrides?: { templateScreenId?: string }) => {\n if (!campaignId || saving) return\n const savedTemplateScreenId =\n overrides?.templateScreenId ?? sentTemplateScreenId\n setSaving(true)\n try {\n const { response, payload } = await authorizedPost({\n action: 'draft',\n campaignId,\n subject: subject.trim(),\n body: sentBody,\n audience: audienceKind,\n ...(segmentId ? { segmentId } : {}),\n ...(listId ? { listId } : {}),\n ...(experimentId ? { experimentId } : {}),\n ...(savedTemplateScreenId\n ? { templateScreenId: savedTemplateScreenId }\n : {}),\n ...(sentPlainText\n ? {\n plainText: sentPlainText,\n // The design version it was written against, so reopening this\n // draft can say whether the two have parted since.\n ...(plainTextVersionId ? { plainTextVersionId } : {}),\n }\n : {}),\n ...(emailCampaignId ? { emailCampaignId } : {}),\n ...(displayName ? { displayName } : {}),\n ...(topicId ? { topicId } : {}),\n ...(senderId ? { senderId } : {}),\n fromName: fromName.trim(),\n replyTo: replyTo.trim(),\n preheader: preheader.trim(),\n })\n if (!response.ok) {\n return void enqueueSnackbar(payload?.error ?? 'Draft not saved', {\n variant: 'warning',\n allowDuplicate: true,\n })\n }\n enqueueSnackbar('Draft saved', { variant: 'success', persist: false })\n } catch (error) {\n console.error(error)\n enqueueSnackbar(describeCallFailure(error, 'Draft not saved'), {\n variant: 'error',\n allowDuplicate: true,\n })\n } finally {\n setSaving(false)\n }\n }, [\n campaignId,\n saving,\n authorizedPost,\n subject,\n sentBody,\n sentPlainText,\n plainTextVersionId,\n audienceKind,\n segmentId,\n listId,\n experimentId,\n sentTemplateScreenId,\n emailCampaignId,\n displayName,\n topicId,\n senderId,\n fromName,\n replyTo,\n preheader,\n enqueueSnackbar,\n ])\n // Kept current every render; see the ref's declaration above.\n handleSaveDraftRef.current = handleSaveDraft\n\n const quotaLimit = useMemo(\n () =>\n checkQuota(org as never, 'emailSendsPerMonth', campaignSendsUsed).limit,\n [org, campaignSendsUsed],\n )\n\n return (\n <Stack spacing={1.5}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Send an update to your leads or site members. Every email ' +\n 'carries an unsubscribe link; monthly sends are capped by ' +\n 'your plan.'}\n </Typography>\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"Subject\"\n value={subject}\n onChange={(event) => setSubject(event.target.value)}\n size=\"small\"\n sx={{ flex: 1 }}\n />\n <TextField\n select\n label=\"Audience\"\n value={audience}\n onChange={(event) => setAudience(event.target.value as any)}\n size=\"small\"\n sx={{ minWidth: 150 }}\n >\n <MenuItem value=\"leads\">{'Leads'}</MenuItem>\n <MenuItem value=\"members\">{'Site members'}</MenuItem>\n {segments.map((segment: any) => (\n <MenuItem key={segment.$id} value={`segment:${segment.$id}`}>\n {`Segment: ${segment.name}`}\n </MenuItem>\n ))}\n {lists.map((list: any) => (\n <MenuItem key={list.$id} value={`list:${list.$id}`}>\n {`List: ${list.name}`}\n </MenuItem>\n ))}\n </TextField>\n </Stack>\n {/*\n The readout beside the audience picker. It reports what the SEND\n resolved, so an empty audience or a monthly cap is said here — before\n the email is written — instead of after the Send button.\n\n It reports the AUDIENCE beside the send size whenever the two differ.\n One send carries a bounded number of recipients, and a readout showing\n only that number tells a merchant with a 3,000 person list that their\n audience is 500 — the other 2,500 are never mailed and nothing\n anywhere says so.\n */}\n <Typography variant=\"caption\" color=\"text.secondary\">\n {preview === null\n ? 'Counting recipients…'\n : 'error' in preview\n ? preview.error || 'Could not count this audience'\n : `Recipients ${preview.sendable.toLocaleString()}` +\n (preview.audienceSize > preview.sendable\n ? ` of ${preview.audienceSize.toLocaleString()}${\n preview.audienceTruncated ? '+' : ''\n } in this audience`\n : '') +\n (preview.suppressed\n ? ` · ${preview.suppressed.toLocaleString()} unsubscribed or suppressed`\n : '') +\n (preview.cadenceHeld\n ? ` · ${preview.cadenceHeld.toLocaleString()} asked for mail less often than this`\n : '')}\n </Typography>\n {/*\n THE SAME READOUT, BROKEN DOWN BY BASIS.\n\n The line above says how many of this audience one send reaches. This\n one says what that audience is MADE OF, and the numbers are over the\n same `audienceSize` so the two read as one statement rather than two\n competing counts.\n\n The breakdown matters because \"in this audience\" covers two\n populations that are not the same thing: people who ticked a box, and\n people reachable only because enforcement is not retroactive. The\n second group is exactly who disappears if this org ever turns the\n retroactive mode on, so a merchant is owed it BEFORE they write the\n email rather than after an audience collapses.\n */}\n {counts && (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`In this audience: ${counts.consented.toLocaleString()} with a recorded consent basis`}\n {counts.grandfathered\n ? ` · ${counts.grandfathered.toLocaleString()} grandfathered (captured before consent was required)`\n : ''}\n {counts.consentWithheld\n ? ` · ${counts.consentWithheld.toLocaleString()} withheld — no consent on record, never mailed`\n : ''}\n </Typography>\n )}\n {/*\n The monthly campaign cap, standing rather than only on refusal.\n `campaignSendsUsed` is the same counter+month `campaign-send.ts`\n reads, and the limit comes from the same `checkQuota` call, so the\n readout and the gate cannot disagree. `period` says \"this month\"\n because this allowance resets; without it the shared readout says \"on\n your plan\", which for a monthly quota reads as a lifetime allowance.\n */}\n <QuotaReadoutComponent\n ready={orgReady}\n used={campaignSendsUsed}\n limit={quotaLimit}\n noun=\"campaign email\"\n nounPlural=\"campaign emails\"\n period=\"this month\"\n />\n <Divider />\n {/*\n The stream this email belongs to. Its own component and its own read of\n the org's catalog, so the picker a merchant chooses from and the list\n the preference page renders cannot drift apart.\n */}\n <CampaignTopicSelect\n hostId={hostId}\n value={topicId}\n onChange={setTopicId}\n disabled={busy}\n />\n {/*\n WHO THE EMAIL COMES FROM.\n\n The address itself is not here and cannot be: it is the site's\n verified sending identity, resolved on the server from the org\n document. These three are what a merchant is allowed to choose — the\n display name in front of that address, where a reply lands, and the\n line an inbox shows after the subject.\n */}\n {/*\n THE ADDRESS, when this site has more than one it may use.\n\n Offered only when there is a choice: a site with no verified domain\n has exactly one identity, and a select with a single option reads as a\n decision somebody forgot to take. The options come from the server and\n carry the whole address — a control that assembled `${localPart}@${domain}`\n itself would be a second place the address is derived from, and the\n two would disagree the first time either moved.\n */}\n {(identityOptions?.filter((one) => one.selectable).length ?? 0) > 1 ? (\n <TextField\n select\n label=\"From address\"\n value={sendingIdentity || siteDefaultIdentity}\n onChange={(event) =>\n setSendingIdentity(\n event.target.value === siteDefaultIdentity\n ? ''\n : event.target.value,\n )\n }\n size=\"small\"\n helperText=\"Which verified address this email leaves on\"\n >\n {(identityOptions ?? [])\n .filter((one) => one.selectable)\n .map((one) => (\n <MenuItem key={one.value} value={one.value}>\n {one.from ?? one.value}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n {/*\n WHICH SENDER THIS EMAIL GOES OUT AS.\n\n Offered only when there is a choice to make: a site with one sender\n has nothing to pick, and a control with one option reads as a decision\n somebody failed to take.\n\n By ID rather than by address. The mailbox behind each row was\n configured on the site under the organization-admin gate, so this\n control can only reach an address the site was already set up to send\n as — which is what keeps a per-send CHOICE from being the per-send\n ADDRESS FIELD that the sending rules close off. The server refuses an\n id this site does not hold rather than falling back to the default.\n */}\n {/*\n THE PICKER IS OFFERED ONLY WHEN THERE IS A CHOICE, and the ADDRESS is\n offered either way.\n\n A site with one sender has nothing to pick, and a control with one\n option reads as a decision somebody failed to take. What that site\n still needs is the answer to \"what will my recipients see in the From\n line\", which this page did not carry at all — so the sender was defined\n on one screen and the email written on another that never named it,\n and the two read as rivals defining overlapping things.\n\n By ID rather than by address. The mailbox behind each row was\n configured on the site under the organization-admin gate, so this\n control can only reach an address this site was already set up to send\n as — which is what keeps a per-send CHOICE from being the per-send\n ADDRESS FIELD that the sending rules close off. The server refuses an\n id this site does not hold rather than falling back to the default.\n */}\n {(senders?.length ?? 0) > 1 ? (\n <TextField\n select\n label=\"From\"\n value={chosenSender?.id ?? ''}\n onChange={(event) => applySender(event.target.value)}\n size=\"small\"\n helperText=\"Which of this site’s senders this email goes out as\"\n >\n {(senders ?? []).map((sender) => (\n <MenuItem key={sender.id} value={sender.id}>\n {/*\n THE ADDRESS LEADS, and the name sits under it.\n\n The inverse of the teammate picker in the sender drawer, and\n deliberately: that one answers \"which of us is this from\",\n which is a question about a person, while this one answers\n \"what address does this leave on\" — and a row that led with a\n display name would make the reader open the menu to find the\n one fact they came for.\n */}\n <Stack spacing={0}>\n <Typography variant=\"body2\">\n {sender.from ?? `${sender.localPart}@`}\n </Typography>\n {sender.fromName || sender.isDefault ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {[\n sender.fromName,\n sender.isDefault ? 'this site’s default' : '',\n ]\n .filter(Boolean)\n .join(' · ')}\n </Typography>\n ) : null}\n </Stack>\n </MenuItem>\n ))}\n {/*\n Adding one from here rather than sending the merchant to another\n screen and back. It is what a \"one-off sender\" is met with: the\n address is validated, served and reusable, which a per-campaign\n address would be none of.\n\n Offered only to somebody who may write it, and only where a\n second sender is a thing this site can have. Naming the addresses a\n site's mail leaves on is an organization-admin decision, and a site\n on the shared pooled address has one fixed mailbox it does not own\n — so in either case the entry would open a drawer whose Save is\n refused, which is worse than not offering it.\n */}\n {identityView?.canManage && identityView?.selected ? (\n <MenuItem value={ADD_SENDER_OPTION}>\n <Typography variant=\"body2\" color=\"primary\">\n {'Add a sender…'}\n </Typography>\n </MenuItem>\n ) : null}\n </TextField>\n ) : null}\n {/*\n WHAT A RECIPIENT WILL SEE, in one line, from the three fields that\n decide it.\n\n Live rather than stored: the name is whatever is in the field below\n this second, so editing it shows immediately in the From line it will\n produce. Rendered only when there is an address to name — a site whose\n mailbox is not in effect has no address to promise, and the identity\n summary further down is what says so.\n */}\n {chosenSender?.from ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'This email goes out as '}\n <Typography component=\"span\" variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {fromName.trim()\n ? `${fromName.trim()} <${chosenSender.from}>`\n : chosenSender.from}\n </Typography>\n {replyTo.trim() ? `, with replies to ${replyTo.trim()}` : ''}\n {'.'}\n </Typography>\n ) : null}\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"From name\"\n value={fromName}\n onChange={(event) => setFromName(event.target.value)}\n size=\"small\"\n sx={{ flex: 1 }}\n /*\n * THE HELPER SAYS WHAT THIS FIELD IS OVERRIDING.\n *\n * The composer seeds both fields from the chosen sender, so the\n * value sitting here on open is the SENDER'S — and a field that\n * silently matches a setting somewhere else reads as a second\n * definition of it. Saying which state it is in is what makes\n * editing here read as \"for this email\" rather than as changing the\n * site, which is the confusion two screens carrying the same two\n * fields produce on their own.\n */\n helperText={senderFieldHelp(\n 'name',\n chosenSender?.fromName ?? '',\n fromName,\n )}\n />\n <TextField\n label=\"Reply-to\"\n value={replyTo}\n onChange={(event) => setReplyTo(event.target.value)}\n size=\"small\"\n type=\"email\"\n sx={{ flex: 1 }}\n /*\n * The one field here that may name a mailbox on a domain nobody has\n * verified, and the helper says so — because the alternative is a\n * merchant reaching for the From fields to be reachable at their\n * own address. A `From:` on somebody else's mail provider cannot\n * align under the strict DMARC policy the sending apex publishes,\n * so it would be refused by the receiving side rather than\n * delivered; a reply address carries no such requirement.\n */\n helperText={senderFieldHelp(\n 'reply address',\n chosenSender?.replyTo ?? '',\n replyTo,\n )}\n />\n </Stack>\n <TextField\n label=\"Preheader\"\n value={preheader}\n onChange={(event) => setPreheader(event.target.value)}\n size=\"small\"\n helperText=\"The preview line inboxes show after the subject\"\n />\n {emailExperiments.length ? (\n // Email A/B: variant subject/body overrides apply per recipient; a\n // decided experiment sends the winner copy.\n <TextField\n select\n label=\"A/B test\"\n value={experimentId}\n onChange={(event) => setExperimentId(event.target.value)}\n size=\"small\"\n sx={{ minWidth: 150 }}\n >\n <MenuItem value=\"\">{'None'}</MenuItem>\n {emailExperiments.map((experiment: any) => (\n <MenuItem key={experiment.$id} value={experiment.$id}>\n {experiment.name ?? experiment.$id}\n {experiment.winnerVariantId ? ' (winner decided)' : ''}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n <Divider />\n {/*\n HOW THIS EMAIL IS WRITTEN — said out loud, before either half is shown.\n\n The form used to render the design picker and the message box side by\n side with nothing naming which one the send would use. It used the\n design and dropped the message without a word, so a merchant who filled\n in both mailed something they had not written. The mode is what makes\n that unsayable: only the chosen half is on screen, only its field is\n submitted, and the record stores one source rather than two.\n\n Two options and not three. A saved TEMPLATE is a besigner email screen\n — the same document this picker lists and \"Design this email\" creates\n — so \"designed\" and \"from a saved template\" are one mode under two\n names, and offering both would be a distinction the data model does not\n have.\n */}\n <TextField\n select\n label=\"How this email is written\"\n value={mode}\n onChange={(event) => setMode(event.target.value as EmailMessageMode)}\n size=\"small\"\n sx={{ maxWidth: 360 }}\n helperText={\n designed\n ? 'The design is the message. Its plain-text half is generated ' +\n 'from it below, and can be written by hand instead.'\n : 'What you type is the message. Links in it are made clickable ' +\n 'for readers whose mail shows styling.'\n }\n >\n <MenuItem value=\"design\">{'Designed in the besigner'}</MenuItem>\n <MenuItem value=\"text\">{'Written here as plain text'}</MenuItem>\n </TextField>\n {designed ? (\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <TextField\n select\n label=\"Email design\"\n value={templateScreenId}\n onChange={(event) => setTemplateScreenId(event.target.value)}\n size=\"small\"\n sx={{ minWidth: 220 }}\n helperText=\"This email's own design, or a saved template to reuse\"\n >\n <MenuItem value=\"\">{'Choose a design…'}</MenuItem>\n {emailScreens.map((screen: any) => (\n <MenuItem key={screen.$id} value={screen.$id}>\n {screen.displayName ?? screen.$id}\n </MenuItem>\n ))}\n </TextField>\n {selectedTemplate ? (\n <Button\n size=\"small\"\n disabled={!orgSlug || !subdomain}\n onClick={() =>\n void router.push(\n besignerHref(\n orgSlug,\n subdomain,\n selectedTemplate.$id,\n selectedTemplate.versionId,\n ),\n )\n }\n >\n {'Edit design'}\n </Button>\n ) : null}\n {/*\n The one-off path. A design minted here belongs to THIS email —\n named after it, selected on it — and never asks the author to\n think in templates. Reuse is the picker above: a saved template\n is chosen, not created, from a campaign.\n */}\n <CampaignDesignCreate\n hostId={hostId}\n name={displayName?.trim() || subject.trim() || undefined}\n onCreated={(design) => void handleDesignCreated(design)}\n onError={handleDesignFailed}\n />\n </Stack>\n ) : null}\n {/*==========================================\n * THE PLAIN-TEXT HALF OF A DESIGNED EMAIL.\n *\n * Every message goes out as two alternatives, and this is the one a\n * text-only reader actually gets. The design generates it — buttons and\n * product blocks keep their URLs in it, so it is a real message rather\n * than a stub — which is why generated is the default and not a\n * placeholder.\n *\n * Writing one is offered because the generation has a known edge: an\n * inline link inside a rich-text block loses its href, because the text\n * synthesis strips tags. An author who cares what a text-only reader can\n * follow needs a way to say it.\n *\n * Editing the design NEVER rewrites an authored part — losing somebody's\n * writing because they touched the canvas afterwards is not a trade\n * worth making. What that leaves is the other half of the problem, and\n * it is the same shape as the defect this whole surface is being fixed\n * for: a value nobody can see scores the same as one that is not there.\n * So the design version the part was written against is recorded with\n * it, and a part that has fallen behind SAYS SO, here, with the one act\n * that resolves it.\n *\n * Only once a design has been chosen. \"Generated from the design\" is not\n * a thing to offer about a design nobody has picked, and the box it would\n * fill has nothing to fill from.\n *=========================================*/}\n {designed && templateScreenId ? (\n <Stack spacing={1}>\n <TextField\n select\n label=\"Plain-text version\"\n value={plainTextSource}\n onChange={(event) => {\n const next = event.target.value as 'generated' | 'authored'\n setPlainTextSource(next)\n // Start from what it replaces. An empty box would make an\n // override mean \"write this email a second time\".\n if (next === 'authored' && !plainText.trim()) {\n void adoptGeneratedText()\n }\n }}\n size=\"small\"\n sx={{ maxWidth: 360 }}\n helperText={\n plainTextSource === 'authored'\n ? 'What readers whose mail shows no styling receive.'\n : 'Taken from the design, and kept in step with it.'\n }\n >\n <MenuItem value=\"generated\">{'Generated from the design'}</MenuItem>\n <MenuItem value=\"authored\">{'Written here'}</MenuItem>\n </TextField>\n {plainTextState.stale ? (\n <Alert\n severity=\"warning\"\n action={\n <Button\n size=\"small\"\n color=\"inherit\"\n disabled={adoptingText}\n onClick={() => void adoptGeneratedText()}\n >\n {adoptingText ? 'Reading…' : 'Take the design’s text'}\n </Button>\n }\n >\n {'The design has been edited since this plain-text version was ' +\n 'written, so the two may no longer say the same thing. It is ' +\n 'left exactly as you wrote it — nothing has overwritten it — ' +\n 'and this is here because a text half that has fallen behind ' +\n 'is otherwise impossible to see.'}\n </Alert>\n ) : null}\n {plainTextSource === 'authored' ? (\n <TextField\n label=\"The plain-text message\"\n value={plainText}\n onChange={(event) => {\n setPlainText(event.target.value)\n /*\n * Stamped on every edit: this text now describes the design as\n * it stands, which is what makes the notice above go away for\n * the right reason rather than because somebody dismissed it.\n */\n setPlainTextVersionId(selectedTemplate?.versionId ?? '')\n }}\n size=\"small\"\n multiline\n minRows={4}\n helperText={\n adoptingText\n ? 'Reading the design’s own text…'\n : 'Merge tags resolve here too, and the unsubscribe link is ' +\n 'added as a plain address at the end.'\n }\n />\n ) : null}\n {/*\n THE TYPED MESSAGE A DESIGNED EMAIL WOULD OTHERWISE STRAND.\n\n This is the defect made visible. A message typed here and then\n designed used to be submitted, computed and dropped in silence.\n It is no longer submitted at all — so it cannot be dropped by\n anything downstream — and rather than vanishing from the screen as\n well, it is offered the one job it could honestly do.\n */}\n {body.trim() && plainTextSource === 'generated' ? (\n <Alert\n severity=\"info\"\n action={\n <Button\n size=\"small\"\n color=\"inherit\"\n onClick={() => {\n setPlainTextSource('authored')\n setPlainText(body)\n setPlainTextVersionId(selectedTemplate?.versionId ?? '')\n }}\n >\n {'Use it as the plain-text version'}\n </Button>\n }\n >\n {'This email also carries a typed message. A designed email is ' +\n 'not sent from one, so it reaches nobody as it stands, and ' +\n 'saving or sending this email lets it go.'}\n </Alert>\n ) : null}\n </Stack>\n ) : null}\n {designed ? null : (\n <TextField\n label=\"Message\"\n value={body}\n onChange={(event) => setBody(event.target.value)}\n size=\"small\"\n multiline\n minRows={4}\n />\n )}\n {/*\n THE MERGE TAGS, LISTED RATHER THAN REMEMBERED.\n\n `resolveMergeTags` recognizes exactly these three and substitutes the\n fallback for everything else — so a tag typed from memory that is not\n one of them does not fail, it renders as nothing in mail that has\n already gone out. Clicking appends the tag rather than explaining it,\n because the syntax that matters is the pipe: a tag with no fallback\n renders empty for every recipient whose name was never captured.\n */}\n {designed ? null : (\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center', flexWrap: 'wrap' }}\n >\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Personalize:'}\n </Typography>\n {EMAIL_MERGE_TAGS.map((tag) => (\n <Chip\n key={tag.token}\n size=\"small\"\n variant=\"outlined\"\n label={tag.token}\n title={tag.description}\n onClick={() =>\n setBody((current) =>\n current ? `${current}${tag.token}` : tag.token,\n )\n }\n />\n ))}\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'— resolved per recipient at send time'}\n </Typography>\n </Stack>\n )}\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Button size=\"small\" onClick={() => setShowPreview((open) => !open)}>\n {showPreview ? 'Hide preview' : 'Preview email'}\n </Button>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Rendered by the send path, personalized for your own account'}\n </Typography>\n </Stack>\n {showPreview ? (\n rendered === null ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Rendering…'}\n </Typography>\n ) : 'error' in rendered ? (\n <Alert severity=\"warning\">{rendered.error}</Alert>\n ) : (\n <Stack spacing={0.5}>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`Subject: ${rendered.subject || '(none)'}`}\n </Typography>\n {/*\n SANDBOXED, and with an empty attribute rather than a permissive\n one. The document inside is tenant-authored — besigner nodes and\n typed copy — and it is being rendered inside the console origin,\n where a script would run against the operator's own session.\n `sandbox=\"\"` withholds every capability there is to withhold:\n no scripts, no forms, no top-level navigation, and a unique\n opaque origin, which is also what stops a preview reading the\n console's storage.\n */}\n <Box\n component=\"iframe\"\n title=\"Email preview\"\n sandbox=\"\"\n srcDoc={rendered.html}\n sx={{\n width: '100%',\n height: 420,\n border: 1,\n borderColor: 'divider',\n borderRadius: 1,\n backgroundColor: 'background.paper',\n }}\n />\n {/*\n THE OTHER HALF OF THE MESSAGE, which a preview of the HTML alone\n never showed.\n\n Every campaign goes out as two alternatives and this is the one a\n text-only reader receives — footer included, opt-out URL bare\n because markup is invisible here. An author cannot be asked to\n choose between a generated text part and one of their own while\n being shown neither.\n\n Monospace and pre-wrapped: line breaks ARE the formatting of a\n text part, and rendering them as flowed prose would show\n something the recipient does not get.\n */}\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Plain-text version, as it will send'}\n </Typography>\n <Box\n sx={{\n border: 1,\n borderColor: 'divider',\n borderRadius: 1,\n p: 1.5,\n maxHeight: 240,\n overflow: 'auto',\n backgroundColor: 'background.paper',\n fontFamily: 'monospace',\n fontSize: '0.8rem',\n whiteSpace: 'pre-wrap',\n wordBreak: 'break-word',\n }}\n >\n {rendered.text || '(empty)'}\n </Box>\n </Stack>\n )\n ) : null}\n {/*\n WHICH ADDRESS THIS LEAVES ON, or why it cannot leave at all.\n\n Both come from the dry run, which resolves the identity through the\n same function the send does — so this is the answer a campaign would\n actually get and not a second opinion. The refusing case is an Alert\n rather than a caption because it is the one that stops the send, and\n it names the domain and the records still to publish.\n */}\n {preview && 'error' in preview && preview.blocking ? (\n <Alert severity=\"warning\">\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {'This email cannot be sent yet'}\n </Typography>\n <Typography variant=\"body2\">{preview.error}</Typography>\n <Typography variant=\"body2\" sx={{ mt: 0.5 }}>\n {'Nothing has been sent, and nothing about this draft is lost. ' +\n 'Emails → Sending is where this is settled — it says what this ' +\n 'site sends as and what it would take to change it.'}\n </Typography>\n </Alert>\n ) : preview && !('error' in preview) && preview.identity ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {preview.identity}\n </Typography>\n ) : null}\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={\n busy ||\n !subject.trim() ||\n !messageReady ||\n /*\n * REFUSED BEFORE THE CLICK, not after it.\n *\n * The send would answer 409 anyway — that boundary holds whatever\n * this component does. What a disabled button buys is that the\n * merchant reads the reason instead of pressing Send and being\n * told; the two are the same information, and only one of them\n * arrives while there is still something to do about it.\n */\n Boolean(preview && 'error' in preview && preview.blocking)\n }\n onClick={handleSend}\n >\n {busy ? 'Working…' : sendAt ? 'Schedule campaign' : 'Send campaign'}\n </Button>\n {/*\n Never hidden while there is a record to save into. Leaving without\n saving is the ordinary way to use a composer, and a surface that\n only offers Send makes a half-written email something you either\n mail or lose.\n */}\n {campaignId ? (\n <Button\n size=\"small\"\n variant=\"outlined\"\n disabled={saving || busy}\n onClick={() => void handleSaveDraft()}\n >\n {saving ? 'Saving…' : 'Save draft'}\n </Button>\n ) : null}\n <Button\n size=\"small\"\n disabled={busy || !messageReady}\n onClick={() => setTestOpen(true)}\n >\n {'Send test'}\n </Button>\n <TextField\n size=\"small\"\n type=\"datetime-local\"\n label=\"Send at (optional)\"\n slotProps={{ inputLabel: { shrink: true } }}\n value={sendAt}\n onChange={(event) => setSendAt(event.target.value)}\n />\n </Stack>\n {/*\n The proof drawer takes the message as it stands, so a test mails what\n is composed rather than a reconstruction of it. It shares the one\n authorized POST with every other action on this surface.\n */}\n {/*\n THE SENDER EDITOR, mounted only once somebody asks for it.\n\n Not rendered closed. It reads the site's collaborator roster and three\n shapes of the org's team when it opens, and the module it is built from\n is a drawer nobody on this page has asked to see — so it costs nothing\n until the picker's last entry is chosen.\n\n `senderId={null}` is the ADD mode: the fields open empty, so the sender\n this site already has is left exactly where it is rather than renamed\n by somebody composing an email.\n */}\n {addingSender ? (\n <CampaignSenderEditor\n hostId={hostId}\n view={identityView}\n onClose={() => setAddingSender(false)}\n onSaved={(created) => {\n /*\n * The new row is selected by the read, not here: it does not exist\n * in `senders` yet, because the list this picker draws from was\n * fetched before the drawer wrote anything.\n */\n if (created) pendingSenderRef.current = created\n setIdentityReads((count) => count + 1)\n }}\n />\n ) : null}\n <CampaignTestSendDrawer\n open={testOpen}\n onClose={() => setTestOpen(false)}\n post={authorizedPost}\n identity={\n preview && !('error' in preview) ? preview.identity : ''\n }\n message={{\n subject: subject.trim() || 'Test send',\n /*\n * The mode's own field, exactly as the send carries it. A proof\n * assembled from both halves would mail something the campaign\n * cannot produce, which is the one thing a proof must never do.\n */\n body: sentBody,\n fromName: fromName.trim(),\n replyTo: replyTo.trim(),\n ...(senderId ? { senderId } : {}),\n preheader: preheader.trim(),\n ...(sentTemplateScreenId\n ? { templateScreenId: sentTemplateScreenId }\n : {}),\n ...(sentPlainText ? { plainText: sentPlainText } : {}),\n ...(sendingIdentity ? { sendingIdentity } : {}),\n }}\n />\n </Stack>\n )\n}\nCampaignComposer.displayName = 'CampaignComposer'\n\nexport default CampaignComposer\n"],"names":["buildRoute","checkQuota","Route","EMAIL_MERGE_TAGS","emailMessageMode","emailPlainTextState","useConfirmationContext","QuotaReadoutComponent","useSnackbar","Alert","Box","Button","Chip","Divider","MenuItem","Stack","TextField","Typography","collection","doc","limit","query","CampaignDesignCreate","CampaignSenderEditor","CampaignTopicSelect","useCampaignSendApi","useSendingApi","describeCallFailure","CampaignTestSendDrawer","useRouter","useCallback","useEffect","useMemo","useRef","useState","useConsoleHostRoute","useFirestore","useFirestoreCollection","useFirestoreDoc","useOrgDataScope","useHostOrgId","useOrgPlan","besignerHref","orgSlug","host","screenId","versionId","SCREEN_BESIGNER","RENDER_DEBOUNCE_MS","COUNT_DEBOUNCE_MS","plural","count","noun","toLocaleString","ADD_SENDER_OPTION","senderFieldHelp","field","saved","current","base","trim","CampaignComposer","props","initial","identityOptions","hostId","emailCampaignId","campaignListIds","campaignTopicId","displayName","campaignId","onSent","subdomain","scope","dataScope","firestore","enqueueSnackbar","confirm","router","data","segmentDocs","idField","segments","sort","a","b","String","name","localeCompare","listDocs","lists","org","ready","orgReady","campaignOrgId","campaignMonthKey","Date","toISOString","slice","campaignSendCounter","campaignSendsUsed","Number","experimentDocs","emailExperiments","filter","experiment","deletedAt","target","status","winnerVariantId","screenDocs","emailScreens","screen","kind","storedAudience","audience","listId","segmentId","subject","setSubject","body","setBody","fromName","setFromName","replyTo","setReplyTo","preheader","setPreheader","sendingIdentity","setSendingIdentity","senderId","setSenderId","openedAsSenderId","setAudience","experimentId","setExperimentId","topicId","setTopicId","templateScreenId","setTemplateScreenId","mode","setMode","plainTextSource","setPlainTextSource","plainText","source","setPlainText","plainTextVersionId","setPlainTextVersionId","sendAt","setSendAt","addingSender","setAddingSender","busy","setBusy","showPreview","setShowPreview","designed","sentTemplateScreenId","sentBody","sentPlainText","messageReady","Boolean","selectedTemplate","find","$id","handleSaveDraftRef","handleDesignFailed","error","console","message","variant","handleDesignCreated","design","push","authorizedPost","testOpen","setTestOpen","audienceKind","startsWith","length","preview","setPreview","active","timer","setTimeout","response","payload","action","ok","blocking","sendable","suppressed","audienceSize","audienceTruncated","consented","grandfathered","consentWithheld","cadenceHeld","identity","identitySource","clearTimeout","setIdentityOptions","siteDefaultIdentity","one","value","identityView","setIdentityView","senders","identityReads","setIdentityReads","pendingSenderRef","sendingApi","opened","path","method","options","rows","pending","added","id","siteFromName","siteReplyTo","catch","undefined","applySender","next","chosen","chosenSender","isDefault","rendered","setRendered","html","text","adoptingText","setAdoptingText","adoptGeneratedText","allowDuplicate","messageText","plainTextState","audienceLabel","counts","confirmDescription","scheduling","sendAtMs","quoted","when","reason","short","reach","withheld","join","handleSend","getTime","now","persist","confirmed","title","description","confirmationText","then","sent","recipients","saving","setSaving","handleSaveDraft","overrides","savedTemplateScreenId","quotaLimit","spacing","color","direction","label","onChange","event","size","sx","flex","select","minWidth","map","segment","list","used","nounPlural","period","disabled","selectable","helperText","from","sender","localPart","canManage","selected","component","fontWeight","type","maxWidth","alignItems","onClick","onCreated","onError","stale","severity","multiline","minRows","flexWrap","tag","token","open","sandbox","srcDoc","width","height","border","borderColor","borderRadius","backgroundColor","p","maxHeight","overflow","fontFamily","fontSize","whiteSpace","wordBreak","mt","slotProps","inputLabel","shrink","view","onClose","onSaved","created","post"],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface CampaignConversionsCardProps {
|
|
2
|
+
hostId: string;
|
|
3
|
+
/** The marketing hub URL, for the link to each credited campaign. */
|
|
4
|
+
basePath: string;
|
|
5
|
+
/**
|
|
6
|
+
* One campaign's conversions rather than the whole site's.
|
|
7
|
+
*
|
|
8
|
+
* Set from `/marketing/conversions/{campaignId}`. It narrows the EMAIL list
|
|
9
|
+
* only: a web-channel record carries no campaign id, so there is nothing
|
|
10
|
+
* for it to be narrowed by — and the uncredited figure is withheld
|
|
11
|
+
* entirely, because a conversion credited to nobody belongs to no campaign
|
|
12
|
+
* and attributing the site's whole direct traffic to whichever campaign the
|
|
13
|
+
* reader happens to be looking at is the exact inference this join refuses
|
|
14
|
+
* to make.
|
|
15
|
+
*/
|
|
16
|
+
campaignId?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function CampaignConversionsCard(props: CampaignConversionsCardProps): import("react").JSX.Element;
|
|
19
|
+
export declare namespace CampaignConversionsCard {
|
|
20
|
+
var displayName: string;
|
|
21
|
+
}
|
|
22
|
+
export default CampaignConversionsCard;
|