@aglyn/plugins-marketing 1.4.0 → 1.6.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aglyn/plugins-marketing",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "license": "Apache-2.0",
5
5
  "homepage": "https://aglyn.com",
6
6
  "repository": {
@@ -25,21 +25,21 @@
25
25
  "./package.json": "./package.json"
26
26
  },
27
27
  "dependencies": {
28
- "@aglyn/aglyn": "1.4.0",
29
- "@aglyn/shared-data-enums": "1.4.0",
30
- "@aglyn/shared-data-mdi": "1.4.0",
31
- "@aglyn/shared-ui-email-campaigns": "1.4.0",
32
- "@aglyn/shared-ui-jsx": "1.4.0",
33
- "@aglyn/shared-ui-jsx-forms": "1.4.0",
34
- "@aglyn/shared-ui-next": "1.4.0",
35
- "@aglyn/shared-ui-snackstack": "1.4.0",
36
- "@aglyn/shared-util-email": "1.4.0",
37
- "@aglyn/shared-util-http": "1.4.0",
38
- "@aglyn/shared-util-timestamp": "1.4.0",
39
- "@aglyn/shared-util-tools": "1.4.0",
40
- "@aglyn/tenant-data-admin": "1.4.0",
41
- "@aglyn/tenant-feature-instance": "1.4.0",
42
- "@aglyn/tenant-runtime": "1.4.0",
28
+ "@aglyn/aglyn": "1.6.0",
29
+ "@aglyn/shared-data-enums": "1.6.0",
30
+ "@aglyn/shared-data-mdi": "1.6.0",
31
+ "@aglyn/shared-ui-email-campaigns": "1.6.0",
32
+ "@aglyn/shared-ui-jsx": "1.6.0",
33
+ "@aglyn/shared-ui-jsx-forms": "1.6.0",
34
+ "@aglyn/shared-ui-next": "1.6.0",
35
+ "@aglyn/shared-ui-snackstack": "1.6.0",
36
+ "@aglyn/shared-util-email": "1.6.0",
37
+ "@aglyn/shared-util-http": "1.6.0",
38
+ "@aglyn/shared-util-timestamp": "1.6.0",
39
+ "@aglyn/shared-util-tools": "1.6.0",
40
+ "@aglyn/tenant-data-admin": "1.6.0",
41
+ "@aglyn/tenant-feature-instance": "1.6.0",
42
+ "@aglyn/tenant-runtime": "1.6.0",
43
43
  "@swc/helpers": "0.5.23"
44
44
  },
45
45
  "peerDependencies": {
@@ -52,7 +52,7 @@ import { OVERLAY_COPY_HELPER_TEXT, shownAsTypedHelperText, useOverlayCopyEditor
52
52
  * off this seed. Against a cached read, editing the text reinstates the
53
53
  * colours, link and `enabled` flag from that snapshot on a banner shown
54
54
  * on every page of a published site.
55
- */ fromCache: hostFromCache } = useFirestoreDoc(()=>doc(firestore, 'hosts', hostId), [
55
+ */ fromCache: hostFromCache, listen: hostListen } = useFirestoreDoc(()=>doc(firestore, 'hosts', hostId), [
56
56
  firestore,
57
57
  hostId
58
58
  ], {
@@ -106,7 +106,8 @@ import { OVERLAY_COPY_HELPER_TEXT, shownAsTypedHelperText, useOverlayCopyEditor
106
106
  */ const verdict = await writeGuardedBySeed({
107
107
  subject: 'announcement bar',
108
108
  unreadable: hostStatus === 'error',
109
- fromCache: hostFromCache
109
+ fromCache: hostFromCache,
110
+ listen: hostListen
110
111
  }, async ()=>{
111
112
  await writeSiteWideChange({
112
113
  firestore,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/announcement-bar-card.component.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 {\n type AglynOrgBilling,\n checkEntitlement,\n type HostAnnouncementBar,\n pluginDocsHelp,\n} from '@aglyn/aglyn'\nimport { CardDisplay, useLoading } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n Box,\n Button,\n Stack,\n Switch,\n TextField,\n Typography,\n} from '@mui/material'\nimport { doc } from 'firebase/firestore'\nimport { useEffect, useState } from 'react'\nimport {\n useFirestore,\n useFirestoreDoc,\n useHostActivityLogger,\n useUser,\n writeGuardedBySeed,\n} from '@aglyn/tenant-feature-instance'\nimport { writeSiteWideChange } from '@aglyn/tenant-feature-instance/hooks/helpers/site-wide-change'\nimport OverlayStatsRow from './overlay-stats-row.component'\nimport {\n OVERLAY_COPY_HELPER_TEXT,\n shownAsTypedHelperText,\n useOverlayCopyEditor,\n} from './use-overlay-copy-editor'\n\nexport interface AnnouncementBarCardProps {\n hostId: string\n /** Resolved entitlement source (AGL-395). */\n org?: Partial<AglynOrgBilling>\n}\n\n/**\n * Announcement bar editor (AGL-195): site-wide banner config persisted on\n * the host doc; the tenant render gates on the marketingOverlays\n * entitlement and resolves the text through `resolveOverlayCopy`. A typed\n * `{{name}}` is stored as its variable's id token, the only variable form\n * that resolves, and a token that would still show as typed is flagged\n * under the field (AGL-2885). Starter+ (locked-state upsell below),\n * dark-launched for plan-less workspaces like the other AGL-99 gates.\n */\nexport function AnnouncementBarCard(props: AnnouncementBarCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { org } = props\n const { enqueueSnackbar } = useSnackbar()\n const { queueLoading } = useLoading()\n const logActivity = useHostActivityLogger(hostId)\n const {\n data: host,\n status: hostStatus,\n /**\n * The host doc this form is seeded from is unconfirmed by the server\n * (AGL-1358). The save writes the whole `announcementBar` map — a nested\n * map value REPLACES atomically, so `updateDoc` protects only the host\n * doc's other fields, never the bar's own — and every field of it comes\n * off this seed. Against a cached read, editing the text reinstates the\n * colours, link and `enabled` flag from that snapshot on a banner shown\n * on every page of a published site.\n */\n fromCache: hostFromCache,\n } = useFirestoreDoc<any>(\n () => doc(firestore, 'hosts', hostId),\n [firestore, hostId],\n { idField: '$id' },\n )\n const entitled = checkEntitlement(org, 'marketingOverlays')\n\n const saved = (host?.announcementBar ?? {}) as HostAnnouncementBar\n const [draft, setDraft] = useState<HostAnnouncementBar>(saved)\n // Re-seed the form when another session edits the host doc.\n const savedKey = JSON.stringify(saved)\n useEffect(() => {\n setDraft(JSON.parse(savedKey))\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [savedKey])\n\n // `draft.text` is held in its stored form; the field shows it as typed.\n const copy = useOverlayCopyEditor({\n hostId,\n host,\n usesTokens:\n entitled &&\n [saved.text, draft.text].some((text) => text?.includes('{{')),\n })\n // Compared as it would be stored, so a name typed before the variables\n // arrived still counts as the change it becomes on save.\n const dirty =\n JSON.stringify({ ...draft, text: copy.stored(draft.text) }) !== savedKey\n const patch = (partial: Partial<HostAnnouncementBar>) =>\n setDraft((previous) => ({ ...previous, ...partial }))\n const textShownAsTyped = copy.shownAsTyped(draft.text)\n\n const handleSave = async () => {\n const dequeue = queueLoading()\n try {\n /**\n * Refuse a save whose seed the server never confirmed (AGL-1358).\n *\n * There is no create path to exempt, and that is the point: unlike a\n * new row at a fresh uid, this writes a FIXED document path, so the\n * blank `draft` that `host?.announcementBar ?? {}` yields when the\n * cache has never seen a bar is not harmless — it replaces whatever\n * bar the server really holds with an empty, disabled one.\n *\n * The activity log rides inside the guard: a logged \"Updated\n * announcement bar\" whose write never happened leaves the history\n * disagreeing with the bar it claims to explain.\n *\n * The guard WRAPS the write — an early return is a shape you can keep\n * while losing the protection.\n */\n const verdict = await writeGuardedBySeed(\n {\n subject: 'announcement bar',\n unreadable: hostStatus === 'error',\n fromCache: hostFromCache,\n },\n async () => {\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.update(doc(firestore, 'hosts', hostId), {\n announcementBar: {\n enabled: Boolean(draft.enabled),\n text: copy.storedWithin(draft.text, 300),\n href: (draft.href ?? '').trim(),\n backgroundColor: (draft.backgroundColor ?? '').trim(),\n textColor: (draft.textColor ?? '').trim(),\n dismissible: draft.dismissible !== false,\n },\n }),\n })\n logActivity('Updated announcement bar', { type: 'host', id: hostId })\n },\n )\n // A refusal leaves every typed value on screen. A save that silently\n // does nothing sends the user back to retype a form that will be\n // refused again just as quietly.\n if (!verdict.ok) {\n return void enqueueSnackbar(verdict.message, {\n variant: 'warning',\n persist: false,\n })\n }\n enqueueSnackbar('Announcement bar saved', {\n variant: 'success',\n persist: false,\n })\n } catch (error) {\n console.error(error)\n enqueueSnackbar('An error has occurred', {\n variant: 'error',\n allowDuplicate: true,\n })\n } finally {\n dequeue()\n }\n }\n\n return (\n <CardDisplay\n header=\"Announcement bar\"\n help={pluginDocsHelp('marketingOverlays', {\n anchor: '#announcement-bar',\n })}\n contentGutterX\n contentGutterY\n >\n {!entitled ? (\n <Alert severity=\"info\">\n {'Announcement bars and popups are included from the Starter ' +\n 'plan — see Billing to upgrade.'}\n </Alert>\n ) : (\n <Stack spacing={2}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Switch\n checked={Boolean(draft.enabled)}\n onChange={(event) => patch({ enabled: event.target.checked })}\n />\n <Typography variant=\"body2\">\n {draft.enabled\n ? 'Shown on every page of your published site'\n : 'Hidden'}\n </Typography>\n </Stack>\n <TextField\n label=\"Text\"\n size=\"small\"\n value={copy.editable(draft.text)}\n onChange={(event) =>\n patch({ text: copy.stored(event.target.value) })\n }\n error={textShownAsTyped.length > 0}\n helperText={\n shownAsTypedHelperText(textShownAsTyped) ?? OVERLAY_COPY_HELPER_TEXT\n }\n multiline\n fullWidth\n />\n <Stack useFlexGap direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', rowGap: 1 }}>\n <TextField\n label=\"Link (optional)\"\n size=\"small\"\n placeholder=\"/sale or https://…\"\n value={draft.href ?? ''}\n onChange={(event) => patch({ href: event.target.value })}\n sx={{ flex: 1, minWidth: 200 }}\n />\n <TextField\n label=\"Background\"\n size=\"small\"\n placeholder=\"#111827\"\n value={draft.backgroundColor ?? ''}\n onChange={(event) =>\n patch({ backgroundColor: event.target.value })\n }\n sx={{ width: 120 }}\n />\n <TextField\n label=\"Text color\"\n size=\"small\"\n placeholder=\"#ffffff\"\n value={draft.textColor ?? ''}\n onChange={(event) => patch({ textColor: event.target.value })}\n sx={{ width: 120 }}\n />\n </Stack>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Switch\n checked={draft.dismissible !== false}\n onChange={(event) =>\n patch({ dismissible: event.target.checked })\n }\n />\n <Typography variant=\"body2\">\n {'Visitors can dismiss it (re-shown when the text changes)'}\n </Typography>\n </Stack>\n {draft.text ? (\n <Box\n sx={{\n px: 2,\n py: 1,\n borderRadius: 1,\n textAlign: 'center',\n fontSize: 14,\n color: draft.textColor || '#fff',\n backgroundColor: draft.backgroundColor || '#111827',\n }}\n >\n {copy.preview(draft.text)}\n </Box>\n ) : null}\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={\n !dirty ||\n (Boolean(draft.enabled) && !draft.text?.trim()) ||\n copy.pending(draft.text)\n }\n onClick={handleSave}\n sx={{ alignSelf: 'flex-start' }}\n >\n {'Save'}\n </Button>\n <OverlayStatsRow\n hostId={hostId}\n actionKey=\"barClick\"\n actionLabel=\"clicks\"\n />\n </Stack>\n )}\n </CardDisplay>\n )\n}\nAnnouncementBarCard.displayName = 'AnnouncementBarCard'\n\nexport default AnnouncementBarCard\n"],"names":["checkEntitlement","pluginDocsHelp","CardDisplay","useLoading","useSnackbar","Alert","Box","Button","Stack","Switch","TextField","Typography","doc","useEffect","useState","useFirestore","useFirestoreDoc","useHostActivityLogger","useUser","writeGuardedBySeed","writeSiteWideChange","OverlayStatsRow","OVERLAY_COPY_HELPER_TEXT","shownAsTypedHelperText","useOverlayCopyEditor","AnnouncementBarCard","props","draft","hostId","firestore","data","user","org","enqueueSnackbar","queueLoading","logActivity","host","status","hostStatus","fromCache","hostFromCache","idField","entitled","saved","announcementBar","setDraft","savedKey","JSON","stringify","parse","copy","usesTokens","text","some","includes","dirty","stored","patch","partial","previous","textShownAsTyped","shownAsTyped","handleSave","dequeue","verdict","subject","unreadable","write","batch","update","enabled","Boolean","storedWithin","href","trim","backgroundColor","textColor","dismissible","type","id","ok","message","variant","persist","error","console","allowDuplicate","header","help","anchor","contentGutterX","contentGutterY","severity","spacing","direction","sx","alignItems","checked","onChange","event","target","label","size","value","editable","length","helperText","multiline","fullWidth","useFlexGap","flexWrap","rowGap","placeholder","flex","minWidth","width","px","py","borderRadius","textAlign","fontSize","color","preview","disabled","pending","onClick","alignSelf","actionKey","actionLabel","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/announcement-bar-card.component.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 {\n type AglynOrgBilling,\n checkEntitlement,\n type HostAnnouncementBar,\n pluginDocsHelp,\n} from '@aglyn/aglyn'\nimport { CardDisplay, useLoading } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n Box,\n Button,\n Stack,\n Switch,\n TextField,\n Typography,\n} from '@mui/material'\nimport { doc } from 'firebase/firestore'\nimport { useEffect, useState } from 'react'\nimport {\n useFirestore,\n useFirestoreDoc,\n useHostActivityLogger,\n useUser,\n writeGuardedBySeed,\n} from '@aglyn/tenant-feature-instance'\nimport { writeSiteWideChange } from '@aglyn/tenant-feature-instance/hooks/helpers/site-wide-change'\nimport OverlayStatsRow from './overlay-stats-row.component'\nimport {\n OVERLAY_COPY_HELPER_TEXT,\n shownAsTypedHelperText,\n useOverlayCopyEditor,\n} from './use-overlay-copy-editor'\n\nexport interface AnnouncementBarCardProps {\n hostId: string\n /** Resolved entitlement source (AGL-395). */\n org?: Partial<AglynOrgBilling>\n}\n\n/**\n * Announcement bar editor (AGL-195): site-wide banner config persisted on\n * the host doc; the tenant render gates on the marketingOverlays\n * entitlement and resolves the text through `resolveOverlayCopy`. A typed\n * `{{name}}` is stored as its variable's id token, the only variable form\n * that resolves, and a token that would still show as typed is flagged\n * under the field (AGL-2885). Starter+ (locked-state upsell below),\n * dark-launched for plan-less workspaces like the other AGL-99 gates.\n */\nexport function AnnouncementBarCard(props: AnnouncementBarCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { org } = props\n const { enqueueSnackbar } = useSnackbar()\n const { queueLoading } = useLoading()\n const logActivity = useHostActivityLogger(hostId)\n const {\n data: host,\n status: hostStatus,\n /**\n * The host doc this form is seeded from is unconfirmed by the server\n * (AGL-1358). The save writes the whole `announcementBar` map — a nested\n * map value REPLACES atomically, so `updateDoc` protects only the host\n * doc's other fields, never the bar's own — and every field of it comes\n * off this seed. Against a cached read, editing the text reinstates the\n * colours, link and `enabled` flag from that snapshot on a banner shown\n * on every page of a published site.\n */\n fromCache: hostFromCache,\n listen: hostListen,\n } = useFirestoreDoc<any>(\n () => doc(firestore, 'hosts', hostId),\n [firestore, hostId],\n { idField: '$id' },\n )\n const entitled = checkEntitlement(org, 'marketingOverlays')\n\n const saved = (host?.announcementBar ?? {}) as HostAnnouncementBar\n const [draft, setDraft] = useState<HostAnnouncementBar>(saved)\n // Re-seed the form when another session edits the host doc.\n const savedKey = JSON.stringify(saved)\n useEffect(() => {\n setDraft(JSON.parse(savedKey))\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [savedKey])\n\n // `draft.text` is held in its stored form; the field shows it as typed.\n const copy = useOverlayCopyEditor({\n hostId,\n host,\n usesTokens:\n entitled &&\n [saved.text, draft.text].some((text) => text?.includes('{{')),\n })\n // Compared as it would be stored, so a name typed before the variables\n // arrived still counts as the change it becomes on save.\n const dirty =\n JSON.stringify({ ...draft, text: copy.stored(draft.text) }) !== savedKey\n const patch = (partial: Partial<HostAnnouncementBar>) =>\n setDraft((previous) => ({ ...previous, ...partial }))\n const textShownAsTyped = copy.shownAsTyped(draft.text)\n\n const handleSave = async () => {\n const dequeue = queueLoading()\n try {\n /**\n * Refuse a save whose seed the server never confirmed (AGL-1358).\n *\n * There is no create path to exempt, and that is the point: unlike a\n * new row at a fresh uid, this writes a FIXED document path, so the\n * blank `draft` that `host?.announcementBar ?? {}` yields when the\n * cache has never seen a bar is not harmless — it replaces whatever\n * bar the server really holds with an empty, disabled one.\n *\n * The activity log rides inside the guard: a logged \"Updated\n * announcement bar\" whose write never happened leaves the history\n * disagreeing with the bar it claims to explain.\n *\n * The guard WRAPS the write — an early return is a shape you can keep\n * while losing the protection.\n */\n const verdict = await writeGuardedBySeed(\n {\n subject: 'announcement bar',\n unreadable: hostStatus === 'error',\n fromCache: hostFromCache,\n listen: hostListen,\n },\n async () => {\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.update(doc(firestore, 'hosts', hostId), {\n announcementBar: {\n enabled: Boolean(draft.enabled),\n text: copy.storedWithin(draft.text, 300),\n href: (draft.href ?? '').trim(),\n backgroundColor: (draft.backgroundColor ?? '').trim(),\n textColor: (draft.textColor ?? '').trim(),\n dismissible: draft.dismissible !== false,\n },\n }),\n })\n logActivity('Updated announcement bar', { type: 'host', id: hostId })\n },\n )\n // A refusal leaves every typed value on screen. A save that silently\n // does nothing sends the user back to retype a form that will be\n // refused again just as quietly.\n if (!verdict.ok) {\n return void enqueueSnackbar(verdict.message, {\n variant: 'warning',\n persist: false,\n })\n }\n enqueueSnackbar('Announcement bar saved', {\n variant: 'success',\n persist: false,\n })\n } catch (error) {\n console.error(error)\n enqueueSnackbar('An error has occurred', {\n variant: 'error',\n allowDuplicate: true,\n })\n } finally {\n dequeue()\n }\n }\n\n return (\n <CardDisplay\n header=\"Announcement bar\"\n help={pluginDocsHelp('marketingOverlays', {\n anchor: '#announcement-bar',\n })}\n contentGutterX\n contentGutterY\n >\n {!entitled ? (\n <Alert severity=\"info\">\n {'Announcement bars and popups are included from the Starter ' +\n 'plan — see Billing to upgrade.'}\n </Alert>\n ) : (\n <Stack spacing={2}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Switch\n checked={Boolean(draft.enabled)}\n onChange={(event) => patch({ enabled: event.target.checked })}\n />\n <Typography variant=\"body2\">\n {draft.enabled\n ? 'Shown on every page of your published site'\n : 'Hidden'}\n </Typography>\n </Stack>\n <TextField\n label=\"Text\"\n size=\"small\"\n value={copy.editable(draft.text)}\n onChange={(event) =>\n patch({ text: copy.stored(event.target.value) })\n }\n error={textShownAsTyped.length > 0}\n helperText={\n shownAsTypedHelperText(textShownAsTyped) ?? OVERLAY_COPY_HELPER_TEXT\n }\n multiline\n fullWidth\n />\n <Stack useFlexGap direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', rowGap: 1 }}>\n <TextField\n label=\"Link (optional)\"\n size=\"small\"\n placeholder=\"/sale or https://…\"\n value={draft.href ?? ''}\n onChange={(event) => patch({ href: event.target.value })}\n sx={{ flex: 1, minWidth: 200 }}\n />\n <TextField\n label=\"Background\"\n size=\"small\"\n placeholder=\"#111827\"\n value={draft.backgroundColor ?? ''}\n onChange={(event) =>\n patch({ backgroundColor: event.target.value })\n }\n sx={{ width: 120 }}\n />\n <TextField\n label=\"Text color\"\n size=\"small\"\n placeholder=\"#ffffff\"\n value={draft.textColor ?? ''}\n onChange={(event) => patch({ textColor: event.target.value })}\n sx={{ width: 120 }}\n />\n </Stack>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Switch\n checked={draft.dismissible !== false}\n onChange={(event) =>\n patch({ dismissible: event.target.checked })\n }\n />\n <Typography variant=\"body2\">\n {'Visitors can dismiss it (re-shown when the text changes)'}\n </Typography>\n </Stack>\n {draft.text ? (\n <Box\n sx={{\n px: 2,\n py: 1,\n borderRadius: 1,\n textAlign: 'center',\n fontSize: 14,\n color: draft.textColor || '#fff',\n backgroundColor: draft.backgroundColor || '#111827',\n }}\n >\n {copy.preview(draft.text)}\n </Box>\n ) : null}\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={\n !dirty ||\n (Boolean(draft.enabled) && !draft.text?.trim()) ||\n copy.pending(draft.text)\n }\n onClick={handleSave}\n sx={{ alignSelf: 'flex-start' }}\n >\n {'Save'}\n </Button>\n <OverlayStatsRow\n hostId={hostId}\n actionKey=\"barClick\"\n actionLabel=\"clicks\"\n />\n </Stack>\n )}\n </CardDisplay>\n )\n}\nAnnouncementBarCard.displayName = 'AnnouncementBarCard'\n\nexport default AnnouncementBarCard\n"],"names":["checkEntitlement","pluginDocsHelp","CardDisplay","useLoading","useSnackbar","Alert","Box","Button","Stack","Switch","TextField","Typography","doc","useEffect","useState","useFirestore","useFirestoreDoc","useHostActivityLogger","useUser","writeGuardedBySeed","writeSiteWideChange","OverlayStatsRow","OVERLAY_COPY_HELPER_TEXT","shownAsTypedHelperText","useOverlayCopyEditor","AnnouncementBarCard","props","draft","hostId","firestore","data","user","org","enqueueSnackbar","queueLoading","logActivity","host","status","hostStatus","fromCache","hostFromCache","listen","hostListen","idField","entitled","saved","announcementBar","setDraft","savedKey","JSON","stringify","parse","copy","usesTokens","text","some","includes","dirty","stored","patch","partial","previous","textShownAsTyped","shownAsTyped","handleSave","dequeue","verdict","subject","unreadable","write","batch","update","enabled","Boolean","storedWithin","href","trim","backgroundColor","textColor","dismissible","type","id","ok","message","variant","persist","error","console","allowDuplicate","header","help","anchor","contentGutterX","contentGutterY","severity","spacing","direction","sx","alignItems","checked","onChange","event","target","label","size","value","editable","length","helperText","multiline","fullWidth","useFlexGap","flexWrap","rowGap","placeholder","flex","minWidth","width","px","py","borderRadius","textAlign","fontSize","color","preview","disabled","pending","onClick","alignSelf","actionKey","actionLabel","displayName"],"mappings":"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"}
@@ -131,7 +131,7 @@ const EXPERIMENT_PAGE_SORT_HEADERS = {
131
131
  * otherwise only ever moved by the start/pause/finish controls below, so
132
132
  * a cached seed can quietly restart a finished test, or stop a running
133
133
  * one, along with reverting its variant weights and goal.
134
- */ fromCache: experimentsFromCache, plan: experimentPlan } = useListQuery({
134
+ */ fromCache: experimentsFromCache, listen: experimentsListen, plan: experimentPlan } = useListQuery({
135
135
  collection: collection(firestore, 'hosts', hostId, 'experiments'),
136
136
  /*
137
137
  * Ordered by the server and paged (AGL-2501, AGL-2292): the declaration's
@@ -301,7 +301,8 @@ const EXPERIMENT_PAGE_SORT_HEADERS = {
301
301
  */ const verdict = await writeGuardedBySeed({
302
302
  subject: 'experiment',
303
303
  unreadable: Boolean(editor.$id) && experimentsStatus === 'error',
304
- fromCache: Boolean(editor.$id) && experimentsFromCache
304
+ fromCache: Boolean(editor.$id) && experimentsFromCache,
305
+ listen: experimentsListen
305
306
  }, async ()=>{
306
307
  await writeSiteWideChange({
307
308
  firestore,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/host-experiments-card.component.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 {\n type AglynOrgBilling,\n checkEntitlement,\n createResourceUid,\n HOST_EVENT_TYPES,\n isScreenGroup,\n pluginDocsHelp,\n SITE_EVENT_TYPES,\n} from '@aglyn/aglyn'\nimport { nameSearchFields } from '@aglyn/aglyn/app-utils/name-search'\nimport { describeVariantComparison, experimentResultRows, validateExperiment, type ExperimentTarget, type ExperimentVariant, type HostExperiment } from '../model'\nimport { EXPERIMENT_LIST_QUERY } from '../model/experiment-list-query'\nimport {\n mdiChartBar,\n mdiDeleteOutline,\n mdiPause,\n mdiPencilOutline,\n mdiPlay,\n} from '@aglyn/shared-data-mdi'\nimport { CardDisplay, MdiIcon, useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport ListFilterChips from '@aglyn/shared-ui-jsx/components/list-filter-chips.component'\nimport ListQueryNotices, {\n listQueryRefusals,\n} from '@aglyn/shared-ui-jsx/components/list-query-notices.component'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport {\n ListRowActions,\n ListTable,\n listActionsColumn,\n} from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport { listFilterGridColumns } from '@aglyn/shared-ui-jsx/const/list-grid-filter'\nimport { useListGridFilter } from '@aglyn/shared-ui-jsx/hooks/use-list-grid-filter'\nimport { useListColumnSort } from '@aglyn/shared-ui-jsx/hooks/use-list-column-sort'\nimport type { ListQuerySort } from '@aglyn/shared-ui-jsx/const/list-query-plan'\nimport type { RowActionsMenuItem } from '@aglyn/shared-ui-jsx/components/row-actions-menu.component'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { TABLE_ROW_HEIGHT } from '@aglyn/shared-ui-jsx/const/table-pagination'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { Timestamp } from '@aglyn/shared-util-timestamp'\nimport {\n Alert,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n FormControlLabel,\n MenuItem,\n Stack,\n Switch,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TextField,\n Typography,\n} from '@mui/material'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport {\n collection,\n doc,\n getDocs,\n limit,\n query,\n where,\n} from 'firebase/firestore'\nimport { useMemo, useState } from 'react'\nimport {\n useFirestore,\n useFirestoreCollection,\n useHostActivityLogger,\n writeGuardedBySeed,\n useUser,\n} from '@aglyn/tenant-feature-instance'\nimport { writeSiteWideChange } from '@aglyn/tenant-feature-instance/hooks/helpers/site-wide-change'\nimport { useListQuery } from '@aglyn/tenant-feature-instance/hooks/use-list-query'\nimport {\n ExperimentResultZone,\n ExperimentVariantsZone,\n marketingExperimentVariantDrafts,\n type MarketingExperimentVariantDraft,\n} from './experiment-zones'\n\nexport interface HostExperimentsCardProps {\n hostId: string\n /** Resolved entitlement source (AGL-395). */\n org?: Partial<AglynOrgBilling>\n}\n\ntype ExperimentDraft = HostExperiment & { $id?: string }\n\n/** An experiment's status as a chip color, in every list of experiments. */\nexport const EXPERIMENT_STATUS_COLORS: Record<\n string,\n 'default' | 'success' | 'info' | 'warning'\n> = {\n draft: 'default',\n running: 'success',\n paused: 'warning',\n done: 'info',\n}\n\n/*\n * What the experiments grid's Filters panel offers: the fields of the list's\n * query declaration (`EXPERIMENT_LIST_QUERY`), every one served by Firestore\n * (AGL-3321).\n */\nconst EXPERIMENT_FILTER_FIELDS = EXPERIMENT_LIST_QUERY.fields\nconst EXPERIMENT_FILTER_HEADERS: Readonly<Record<string, string>> = {\n name: 'Experiment',\n target: 'Tests',\n status: 'Status',\n}\n/** What each stored `target` reads as; the stored value stays `screen`. */\nconst EXPERIMENT_TARGET_NOUNS: Readonly<Record<string, string>> = {\n screen: 'page',\n section: 'section',\n email: 'email',\n}\nconst EXPERIMENT_FILTER_OPTIONS = {\n target: [\n { value: 'screen', label: 'Page' },\n { value: 'section', label: 'Section' },\n { value: 'email', label: 'Email' },\n ],\n status: Object.keys(EXPERIMENT_STATUS_COLORS).map((status) => ({\n value: status,\n label: status.charAt(0).toUpperCase() + status.slice(1),\n })),\n}\n\n/** Tests is drawn from the target and the variant count: it sorts the page (AGL-3680). */\nconst EXPERIMENT_PAGE_SORTS = {\n target: (experiment: ExperimentDraft) =>\n `${EXPERIMENT_TARGET_NOUNS[experiment.target] ?? experiment.target} ${(experiment.variants ?? []).length}`,\n}\nconst EXPERIMENT_PAGE_SORT_HEADERS = { target: 'Tests' }\n\n/**\n * Experiments manager (AGL-252): create screen/section/email A/B tests\n * with weighted variants and a conversion goal; start/pause/finish them\n * and read exposures/conversions per variant. Business tier\n * (`ab-testing`).\n */\nexport function HostExperimentsCard(props: HostExperimentsCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { org } = props\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const logActivity = useHostActivityLogger(hostId)\n const entitled = checkEntitlement(org, 'abTesting')\n\n /*\n * The Filters panel and the search box are the grid's; the clauses and the\n * words go onto ONE query over the site's experiments, ordered by name, and\n * each page is a page of that query's matches (AGL-3321). A clause the\n * query cannot hold beside the others is not applied, and the notice above\n * the list says which and why — never answered over the experiments that\n * happen to be loaded.\n */\n const gridFilter = useListGridFilter({ selectFields: ['target', 'status'] })\n // Experiment and Status order the query, Tests the page (AGL-3680).\n const [askedSort, setAskedSort] = useState<ListQuerySort | null>(null)\n const filtering =\n gridFilter.clauses.length > 0 || gridFilter.searchWords.some((word) => word.trim())\n const {\n rows: experimentRows,\n hasMore: hasMoreExperiments,\n page: experimentPage,\n setPage: setExperimentPage,\n pageSize: experimentPageSize,\n setPageSize: setExperimentPageSize,\n status: experimentsStatus,\n /**\n * The rows this editor is seeded from are unconfirmed by the server\n * (AGL-1358). Editing spreads a whole stored experiment into `editor`\n * and writes all of it back, so `merge: true` protects nothing — every\n * field is in the payload. `status` is the one that bites: it is\n * otherwise only ever moved by the start/pause/finish controls below, so\n * a cached seed can quietly restart a finished test, or stop a running\n * one, along with reverting its variant weights and goal.\n */\n fromCache: experimentsFromCache,\n plan: experimentPlan,\n } = useListQuery<ExperimentDraft>({\n collection: collection(firestore, 'hosts', hostId, 'experiments'),\n /*\n * Ordered by the server and paged (AGL-2501, AGL-2292): the declaration's\n * one order is `name`, which every writer sets — the editor refuses a save\n * without one (`validateExperiment`), the besigner's section shortcut\n * names its draft, the two send-path readers only ever write the stats\n * beneath an experiment, and `experiments` is not in `IMPORTABLE_FIELDS`.\n * There is no writer that can produce a nameless experiment for `orderBy`\n * to drop.\n *\n * Nothing is dropped after the query either. An experiment is never\n * soft-deleted — Delete below is a `deleteDoc` — so every row the query\n * returns is live, and a page holds as many rows as its size.\n */\n declaration: EXPERIMENT_LIST_QUERY,\n request: { clauses: gridFilter.clauses, search: gridFilter.searchWords, sort: askedSort },\n deps: [firestore, hostId],\n idField: '$id',\n })\n const experimentSort = useListColumnSort<ExperimentDraft>({\n sorts: EXPERIMENT_LIST_QUERY.sorts,\n defaultSort: EXPERIMENT_LIST_QUERY.sorts[0],\n sort: askedSort,\n onSortChange: setAskedSort,\n orderBy: experimentPlan.orderBy,\n rows: experimentRows,\n pageSorts: EXPERIMENT_PAGE_SORTS,\n headers: EXPERIMENT_PAGE_SORT_HEADERS,\n })\n const experiments: ExperimentDraft[] = experimentSort.rows as ExperimentDraft[]\n const experimentColumnsFor = (columns: GridColDef[]) =>\n listFilterGridColumns(\n columns,\n EXPERIMENT_FILTER_FIELDS,\n EXPERIMENT_FILTER_OPTIONS,\n EXPERIMENT_FILTER_HEADERS,\n )\n const experimentRefusals = useMemo(\n () =>\n listQueryRefusals(experimentPlan.refused, {\n fields: EXPERIMENT_FILTER_FIELDS,\n headers: EXPERIMENT_FILTER_HEADERS,\n options: EXPERIMENT_FILTER_OPTIONS,\n }),\n [experimentPlan.refused],\n )\n const [editor, setEditor] = useState<ExperimentDraft | null>(null)\n /*\n * The screens and their versions fill the EDITOR's pickers and nothing\n * else on the card, so they are read while the editor is open and not\n * before: a reader who came to see which tests are running pays for the\n * experiments list alone, not for a hundred screens they never pick from.\n */\n const editorOpen = editor !== null\n const { data: screenDocs } = useFirestoreCollection<any>(\n () =>\n editorOpen\n ? query(collection(firestore, 'hosts', hostId, 'screens'), limit(100))\n : null,\n [firestore, hostId, editorOpen],\n { idField: '$id' },\n )\n // A page group (AGL-3463) has no versions to test against each other.\n const screenOptions = [...(screenDocs ?? [])]\n .filter((screen: any) => !screen.deletedAt && !isScreenGroup(screen))\n .map((screen: any) => ({\n id: screen.$id as string,\n name: (screen.displayName ?? screen.name ?? screen.$id) as string,\n }))\n .sort((a, b) => a.name.localeCompare(b.name))\n\n // Versions of the screen under test (AGL-253): variants pin one each.\n const { data: versionDocs } = useFirestoreCollection<any>(\n () =>\n editor?.screenId\n ? query(\n collection(\n firestore,\n 'hosts',\n hostId,\n 'screens',\n editor.screenId,\n 'versions',\n ),\n limit(50),\n )\n : null,\n [firestore, hostId, editor?.screenId],\n { idField: '$id' },\n )\n const versionOptions = [...(versionDocs ?? [])]\n .filter((version: any) => !version.deletedAt)\n .map((version: any) => ({\n id: version.$id as string,\n name: (version.name ?? version.displayName ?? version.$id) as string,\n }))\n .sort((a, b) => a.name.localeCompare(b.name))\n const [results, setResults] = useState<{\n experiment: ExperimentDraft\n stats: Record<string, { exposures?: number; conversions?: number }>\n } | null>(null)\n\n const patch = (partial: Partial<ExperimentDraft>) =>\n setEditor((previous) =>\n previous ? { ...previous, ...partial } : previous,\n )\n /**\n * What a widget in the variants zone proposes, put into the editor's own\n * fields as unsaved changes. Only the variants the editor already holds are\n * filled, in order: the test's shape — how many arms, their ids, their\n * weights, the version each screen variant pins — is the person's, and a\n * proposal that could add or drop an arm would be changing the test rather\n * than writing copy for it. A field a widget left empty is left alone, so\n * a proposal for an email's subject does not blank a body someone typed.\n */\n const proposeVariants = (\n proposed: readonly MarketingExperimentVariantDraft[],\n ) =>\n setEditor((previous) =>\n previous\n ? {\n ...previous,\n variants: previous.variants.map((variant, index) => {\n const draft = proposed[index]\n if (!draft) return variant\n return {\n ...variant,\n ...(draft.name ? { name: draft.name } : {}),\n ...(draft.subject ? { subject: draft.subject } : {}),\n ...(draft.body ? { body: draft.body } : {}),\n // A draft version a widget made for this variant (AGL-3603),\n // pinned unsaved like a version picked from the list.\n ...(draft.versionId && previous.target !== 'email' ? { versionId: draft.versionId } : {}),\n }\n }),\n }\n : previous,\n )\n const patchVariant = (index: number, partial: Partial<ExperimentVariant>) =>\n setEditor((previous) =>\n previous\n ? {\n ...previous,\n variants: previous.variants.map((variant, index2) =>\n index2 === index ? { ...variant, ...partial } : variant,\n ),\n }\n : previous,\n )\n\n const newExperiment = (): ExperimentDraft => ({\n name: '',\n status: 'draft',\n target: 'screen' as ExperimentTarget,\n variants: [\n { id: 'a', name: 'A (control)', weight: 1 },\n { id: 'b', name: 'B', weight: 1 },\n ],\n goal: { event: 'formSubmission' },\n })\n\n const handleSave = async () => {\n if (!editor) return\n const problem = validateExperiment(editor)\n if (problem) {\n return void enqueueSnackbar(problem, {\n variant: 'warning',\n persist: false,\n })\n }\n const id = editor.$id ?? createResourceUid()\n const { $id: _ignored, ...payload } = editor\n try {\n /**\n * Refuse an EDIT whose seed the server never confirmed (AGL-1358).\n *\n * `merge: true` is here so a cleared end date or auto-winner overwrites\n * (AGL-273), not to protect untouched fields — there are none, because\n * the payload is the whole stored row spread into `editor`. `status`\n * and `winnerVariantId` ride along with it, and those are otherwise\n * only moved by `setStatus`, so a cached seed can restart a test the\n * owner finished or halt one that is splitting live traffic.\n *\n * Only the edit path. A NEW experiment comes from `newExperiment()` at\n * a fresh uid and can overwrite nothing, and the first snapshot of any\n * listener is `fromCache: true`, so guarding a create would refuse a\n * save that was never unsafe.\n *\n * The guard WRAPS the write, and the activity log with it: a logged\n * \"Updated experiment\" whose write never happened leaves the history\n * disagreeing with the experiment it claims to explain.\n */\n const verdict = await writeGuardedBySeed(\n {\n subject: 'experiment',\n unreadable: Boolean(editor.$id) && experimentsStatus === 'error',\n fromCache: Boolean(editor.$id) && experimentsFromCache,\n },\n async () => {\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.set(\n doc(firestore, 'hosts', hostId, 'experiments', id),\n {\n ...JSON.parse(JSON.stringify(payload)),\n /*\n * The name's search keys, from the name being written — the\n * list's Experiment filter and its search box read them\n * (AGL-3321). After the payload, so keys copied from the stored\n * row cannot outlive a rename.\n */\n ...nameSearchFields(editor.name ?? ''),\n // Clearing the end date / auto-winner must overwrite — a\n // merge-set keeps absent keys otherwise (AGL-273).\n endAtMs: editor.endAtMs ?? null,\n autoWinner: editor.autoWinner ?? null,\n updatedAt: Timestamp.now(),\n ...(editor.$id ? {} : { createdAt: Timestamp.now() }),\n },\n { merge: true },\n ),\n })\n logActivity(\n editor.$id ? 'Updated experiment' : 'Created experiment',\n { type: 'content', id, name: editor.name },\n )\n },\n )\n // A refusal keeps the editor open with everything that was typed.\n if (!verdict.ok) {\n return void enqueueSnackbar(verdict.message, {\n variant: 'warning',\n persist: false,\n })\n }\n enqueueSnackbar('Experiment saved', {\n variant: 'success',\n persist: false,\n })\n setEditor(null)\n } catch (error) {\n console.error(error)\n enqueueSnackbar('An error has occurred', { variant: 'error' })\n }\n }\n\n const setStatus = async (\n experiment: ExperimentDraft,\n status: HostExperiment['status'],\n winnerVariantId?: string,\n ) => {\n if (!experiment.$id) return\n /*\n * One running experiment per screen (AGL-265): the page runner only\n * serves the first, so a second would silently never split traffic.\n *\n * Asked of Firestore, not of the rows on screen: the list is one page of\n * a query the reader may have narrowed, and the running test on the same\n * screen is as likely to be on another page, or filtered out, as on this\n * one. Two equalities, served by merging their single-field indexes.\n */\n if (status === 'running' && experiment.screenId) {\n const running = await getDocs(\n query(\n collection(firestore, 'hosts', hostId, 'experiments'),\n where('status', '==', 'running'),\n where('screenId', '==', experiment.screenId),\n limit(2),\n ),\n )\n const clash = running.docs\n .map((entry) => ({ ...(entry.data() as HostExperiment), $id: entry.id }))\n .find((candidate) => candidate.$id !== experiment.$id)\n if (clash) {\n return void enqueueSnackbar(\n `\"${clash.name}\" is already running on that page — pause or ` +\n 'finish it first',\n { variant: 'warning', persist: false },\n )\n }\n }\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.set(\n doc(firestore, 'hosts', hostId, 'experiments', experiment.$id),\n {\n status,\n ...(winnerVariantId ? { winnerVariantId } : {}),\n updatedAt: Timestamp.now(),\n },\n { merge: true },\n ),\n })\n logActivity(`Experiment ${status}`, {\n type: 'content',\n id: experiment.$id,\n name: experiment.name,\n })\n }\n\n const handleDelete = async (experiment: ExperimentDraft) => {\n if (!experiment.$id) return\n const accepted = await confirm({\n title: 'Delete experiment?',\n description: `\"${experiment.name}\" and its results are removed.`,\n confirmationText: 'Delete',\n confirmationButtonProps: { color: 'error' },\n })\n // Resolves with no value and rejects on cancel — gating on the\n // resolved value alone made this always return (AGL-950).\n .then(() => true)\n .catch(() => false)\n if (!accepted) return\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.delete(\n doc(firestore, 'hosts', hostId, 'experiments', experiment.$id),\n ),\n })\n }\n\n const openResults = async (experiment: ExperimentDraft) => {\n if (!experiment.$id) return\n const snapshot = await getDocs(\n collection(\n firestore,\n 'hosts',\n hostId,\n 'experiments',\n experiment.$id,\n 'stats',\n ),\n )\n const stats: Record<string, any> = {}\n snapshot.forEach((entry) => {\n stats[entry.id] = entry.data()\n })\n setResults({ experiment, stats })\n }\n\n /*\n * One row per experiment. The row opens its results, the one thing every\n * experiment has to show; starting, pausing, editing and deleting it are in\n * the row's menu rather than a row of buttons one mis-click from each other.\n */\n const experimentActions = (experiment: ExperimentDraft): RowActionsMenuItem[] => [\n ...(experiment.status === 'running'\n ? [\n {\n key: 'pause',\n label: 'Pause',\n icon: <MdiIcon path={mdiPause.path} size={0.8} />,\n onClick: () => void setStatus(experiment, 'paused'),\n },\n ]\n : experiment.status !== 'done'\n ? [\n {\n key: 'start',\n label: 'Start',\n icon: <MdiIcon path={mdiPlay.path} size={0.8} />,\n onClick: () => void setStatus(experiment, 'running'),\n },\n ]\n : []),\n {\n key: 'edit',\n label: 'Edit',\n icon: <MdiIcon path={mdiPencilOutline.path} size={0.8} />,\n onClick: () => setEditor({ ...experiment }),\n },\n {\n key: 'delete',\n label: 'Delete',\n icon: <MdiIcon path={mdiDeleteOutline.path} size={0.8} />,\n destructive: true,\n onClick: () => void handleDelete(experiment),\n },\n ]\n const experimentColumns: GridColDef<ExperimentDraft>[] = [\n { field: 'name', headerName: 'Experiment', flex: 1, minWidth: 200 },\n {\n field: 'target',\n headerName: 'Tests',\n flex: 1,\n minWidth: 200,\n valueGetter: (_value, experiment) =>\n `${EXPERIMENT_TARGET_NOUNS[experiment.target] ?? experiment.target} · ${(experiment.variants ?? []).length} variants`,\n },\n {\n field: 'status',\n headerName: 'Status',\n width: 130,\n renderCell: ({ row: experiment }) => (\n <Chip\n size=\"small\"\n color={EXPERIMENT_STATUS_COLORS[experiment.status] ?? 'default'}\n label={experiment.status}\n />\n ),\n },\n listActionsColumn(\n (experiment: ExperimentDraft) => (\n <ListRowActions\n label={experiment.name || 'this experiment'}\n quick={{\n icon: mdiChartBar.path,\n label: 'Results',\n onClick: () => void openResults(experiment),\n }}\n items={experimentActions(experiment)}\n />\n ),\n ),\n ]\n\n return (\n <CardDisplay\n header=\"Experiments\"\n help={pluginDocsHelp('emailCampaigns', { anchor: '#experiments' })}\n contentGutterX\n contentGutterY\n contentBordered=\"all\"\n >\n {!entitled ? (\n <Alert severity=\"info\">\n {'A/B experiments are included in the Business plan — see ' +\n 'Billing to upgrade.'}\n </Alert>\n ) : (\n <Stack spacing={1.5}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Split traffic between variants of a page, a section, or a ' +\n 'campaign email, and measure which one converts. Visitors ' +\n 'are assigned deterministically, so everyone keeps seeing ' +\n 'the same variant.'}\n </Typography>\n <Button\n size=\"small\"\n variant=\"contained\"\n color=\"primary\"\n sx={{ alignSelf: 'flex-start' }}\n onClick={() => setEditor(newExperiment())}\n >\n {'New experiment'}\n </Button>\n {experiments.length === 0 && !hasMoreExperiments && experimentPage === 0 && !filtering ? null : (\n <>\n <ListFilterChips\n fields={EXPERIMENT_FILTER_FIELDS}\n headers={EXPERIMENT_FILTER_HEADERS}\n clauses={gridFilter.clauses}\n onChange={gridFilter.setClauses}\n options={EXPERIMENT_FILTER_OPTIONS}\n />\n <ListQueryNotices\n refused={experimentRefusals}\n notices={[...experimentPlan.notices, ...experimentSort.notices]}\n />\n <ListTable\n aria-label=\"Experiments\"\n rows={experiments}\n columns={experimentColumnsFor(experimentColumns as GridColDef[])}\n rowHeight={TABLE_ROW_HEIGHT}\n onOpen={(_id, experiment) => void openResults(experiment)}\n // Paged by the footer below, so the grid must not also slice.\n hideFooter\n // Experiment and Status order the query; Tests sorts the page\n // and says so (AGL-3680).\n columnSort={experimentSort}\n /*\n * The panel and the search are the grid's; every clause and the\n * search word are on the list's query (AGL-3321), so the grid\n * never narrows the page it is handed.\n */\n filterMode=\"server\"\n filterModel={gridFilter.filterModel}\n onFilterModelChange={gridFilter.onFilterModelChange}\n quickFilter\n noRowsLabel=\"No experiments match these filters\"\n />\n <ListPagination\n page={experimentPage}\n pageSize={experimentPageSize}\n rowCount={experiments.length}\n hasMore={hasMoreExperiments}\n onPageChange={setExperimentPage}\n onPageSizeChange={setExperimentPageSize}\n />\n </>\n )}\n </Stack>\n )}\n\n <Dialog\n open={Boolean(editor)}\n onClose={() => setEditor(null)}\n maxWidth=\"sm\"\n fullWidth\n >\n <DialogTitle>\n {editor?.$id ? 'Edit experiment' : 'New experiment'}\n </DialogTitle>\n <DialogContent\n sx={{ display: 'flex', flexDirection: 'column', gap: 1.5, pt: 1 }}\n >\n <TextField\n size=\"small\"\n label=\"Name\"\n required\n value={editor?.name ?? ''}\n onChange={(event) => patch({ name: event.target.value })}\n sx={{ mt: 1 }}\n />\n <Stack direction=\"row\" spacing={1}>\n <TextField\n select\n size=\"small\"\n label=\"Tests\"\n value={editor?.target ?? 'screen'}\n onChange={(event) =>\n patch({ target: event.target.value as ExperimentTarget })\n }\n sx={{ minWidth: 140 }}\n >\n <MenuItem value=\"screen\">{'A page'}</MenuItem>\n <MenuItem value=\"section\">{'A section'}</MenuItem>\n <MenuItem value=\"email\">{'An email'}</MenuItem>\n </TextField>\n {editor?.target !== 'email' ? (\n <TextField\n select\n size=\"small\"\n label=\"Page\"\n value={editor?.screenId ?? ''}\n onChange={(event) => patch({ screenId: event.target.value })}\n sx={{ flex: 1 }}\n >\n {screenOptions.map((option) => (\n <MenuItem key={option.id} value={option.id}>\n {option.name}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n </Stack>\n {editor?.target === 'section' ? (\n <TextField\n size=\"small\"\n label=\"Element id\"\n helperText=\"The canvas element the variants swap (from the besigner)\"\n value={editor?.nodeId ?? ''}\n onChange={(event) => patch({ nodeId: event.target.value })}\n />\n ) : null}\n <TextField\n select\n size=\"small\"\n label=\"Conversion goal\"\n value={editor?.goal?.event ?? 'formSubmission'}\n onChange={(event) =>\n patch({ goal: { ...editor?.goal, event: event.target.value } })\n }\n sx={{ maxWidth: 260 }}\n >\n {[...HOST_EVENT_TYPES, ...SITE_EVENT_TYPES].map((eventType) => (\n <MenuItem key={eventType} value={eventType}>\n {eventType}\n </MenuItem>\n ))}\n </TextField>\n {/* Schedule + auto-winner (AGL-273). */}\n <Stack\n useFlexGap\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center', flexWrap: 'wrap' }}\n >\n <TextField\n size=\"small\"\n type=\"datetime-local\"\n label=\"Ends at (optional)\"\n slotProps={{ inputLabel: { shrink: true } }}\n value={\n editor?.endAtMs\n ? new Date(\n editor.endAtMs -\n new Date().getTimezoneOffset() * 60000,\n )\n .toISOString()\n .slice(0, 16)\n : ''\n }\n onChange={(event) => {\n const ms = event.target.value\n ? new Date(event.target.value).getTime()\n : undefined\n patch({\n endAtMs: Number.isFinite(ms as number) ? ms : undefined,\n })\n }}\n helperText=\"Past this, visitors get the default\"\n />\n <FormControlLabel\n control={\n <Switch\n size=\"small\"\n checked={Boolean(editor?.autoWinner)}\n onChange={(event) =>\n patch({\n autoWinner: event.target.checked\n ? { minExposures: 200, confidence: 0.95 }\n : undefined,\n })\n }\n />\n }\n label=\"Auto-declare winner\"\n />\n {editor?.autoWinner ? (\n <>\n <TextField\n size=\"small\"\n type=\"number\"\n label=\"Min exposures / variant\"\n value={editor.autoWinner.minExposures}\n onChange={(event) =>\n patch({\n autoWinner: {\n ...editor.autoWinner!,\n minExposures: Number(event.target.value),\n },\n })\n }\n sx={{ width: 170 }}\n />\n <TextField\n select\n size=\"small\"\n label=\"Confidence\"\n value={String(editor.autoWinner.confidence)}\n onChange={(event) =>\n patch({\n autoWinner: {\n ...editor.autoWinner!,\n confidence: Number(event.target.value),\n },\n })\n }\n sx={{ width: 120 }}\n >\n <MenuItem value=\"0.9\">{'90%'}</MenuItem>\n <MenuItem value=\"0.95\">{'95%'}</MenuItem>\n <MenuItem value=\"0.99\">{'99%'}</MenuItem>\n </TextField>\n </>\n ) : null}\n </Stack>\n <Typography variant=\"subtitle2\">{'Variants'}</Typography>\n {(editor?.variants ?? []).map((variant, index) => (\n <Stack key={variant.id} spacing={1}>\n <Stack direction=\"row\" spacing={1}>\n <TextField\n size=\"small\"\n label={`Variant ${variant.id.toUpperCase()}`}\n value={variant.name ?? ''}\n onChange={(event) =>\n patchVariant(index, { name: event.target.value })\n }\n sx={{ flex: 1 }}\n />\n <TextField\n size=\"small\"\n type=\"number\"\n label=\"Weight\"\n value={variant.weight ?? 1}\n onChange={(event) =>\n patchVariant(index, { weight: Number(event.target.value) })\n }\n sx={{ width: 100 }}\n />\n {editor?.target !== 'email' ? (\n // Screen/section variants pin a screen version\n // (AGL-253); empty = the published version.\n <TextField\n select\n size=\"small\"\n label=\"Version\"\n value={variant.versionId ?? ''}\n onChange={(event) =>\n patchVariant(index, { versionId: event.target.value })\n }\n sx={{ minWidth: 160 }}\n >\n <MenuItem value=\"\">{'Published (control)'}</MenuItem>\n {versionOptions.map((option) => (\n <MenuItem key={option.id} value={option.id}>\n {option.name}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n </Stack>\n {editor?.target === 'email' ? (\n // Email variants override the campaign copy (AGL-255).\n <Stack direction=\"row\" spacing={1}>\n <TextField\n size=\"small\"\n label=\"Subject override\"\n value={variant.subject ?? ''}\n onChange={(event) =>\n patchVariant(index, { subject: event.target.value })\n }\n sx={{ width: 220 }}\n />\n <TextField\n size=\"small\"\n label=\"Body override\"\n value={variant.body ?? ''}\n onChange={(event) =>\n patchVariant(index, { body: event.target.value })\n }\n multiline\n maxRows={3}\n sx={{ flex: 1 }}\n />\n </Stack>\n ) : null}\n </Stack>\n ))}\n {/*\n Beneath the variants, never above them: a widget here writes copy\n FOR the list that precedes it, and the list is what the person\n came to the dialog for.\n */}\n {editor ? (\n <ExperimentVariantsZone\n hostId={hostId}\n experimentId={editor.$id ?? ''}\n name={editor.name ?? ''}\n target={editor.target ?? 'screen'}\n goal={editor.goal?.event ?? ''}\n screenId={editor.target === 'email' ? '' : (editor.screenId ?? '')}\n nodeId={editor.target === 'section' ? (editor.nodeId ?? '') : ''}\n variants={marketingExperimentVariantDrafts(editor.variants)}\n proposeVariants={proposeVariants}\n />\n ) : null}\n </DialogContent>\n <DialogActions>\n <Button color=\"inherit\" onClick={() => setEditor(null)}>\n {'Cancel'}\n </Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={() => void handleSave()}\n >\n {'Save'}\n </Button>\n </DialogActions>\n </Dialog>\n\n <Dialog\n open={Boolean(results)}\n onClose={() => setResults(null)}\n maxWidth=\"xs\"\n fullWidth\n >\n <DialogTitle>\n {`Results — ${results?.experiment.name ?? ''}`}\n {results?.experiment.autoCompleted ? (\n // The track pipeline decided this one (AGL-273).\n <Chip\n size=\"small\"\n color=\"success\"\n label=\"Auto-completed\"\n sx={{ ml: 1 }}\n />\n ) : null}\n </DialogTitle>\n <DialogContent>\n <ScrollTable size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{'Variant'}</TableCell>\n <TableCell>{'Exposures'}</TableCell>\n <TableCell>{'Conversions'}</TableCell>\n <TableCell>{'Rate'}</TableCell>\n <TableCell>{'vs control'}</TableCell>\n <TableCell align=\"right\" />\n </TableRow>\n </TableHead>\n <TableBody>\n {/* Lift + z-test confidence vs the first variant (AGL-265). */}\n {(results\n ? experimentResultRows(results.experiment, results.stats)\n : []\n ).map(({ variant, summary, comparison, leader, winner }) => (\n <TableRow key={variant.id} selected={leader}>\n <TableCell>\n {variant.name ?? variant.id.toUpperCase()}\n {winner ? ' 🏆' : leader ? ' ▲' : ''}\n </TableCell>\n <TableCell>{summary.exposures}</TableCell>\n <TableCell>{summary.conversions}</TableCell>\n <TableCell>{`${(summary.rate * 100).toFixed(1)}%`}</TableCell>\n <TableCell>{describeVariantComparison(comparison)}</TableCell>\n <TableCell align=\"right\">\n {results?.experiment.status !== 'done' ? (\n <Button\n size=\"small\"\n onClick={() => {\n if (results) {\n void setStatus(results.experiment, 'done', variant.id)\n setResults(null)\n }\n }}\n >\n {'Pick winner'}\n </Button>\n ) : null}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </ScrollTable>\n {/*\n Below the figures, so anything a widget says about this test is\n read after the counts it is talking about, and the test is named\n as the results reader labels it — its name, or its id where it has\n none, which is the same fallback that reader takes.\n */}\n {results ? (\n <ExperimentResultZone\n hostId={hostId}\n experimentId={results.experiment.$id ?? ''}\n test={results.experiment.name?.trim() || (results.experiment.$id ?? '')}\n />\n ) : null}\n </DialogContent>\n <DialogActions>\n <Button color=\"inherit\" onClick={() => setResults(null)}>\n {'Close'}\n </Button>\n </DialogActions>\n </Dialog>\n </CardDisplay>\n )\n}\nHostExperimentsCard.displayName = 'HostExperimentsCard'\n\nexport default HostExperimentsCard\n"],"names":["checkEntitlement","createResourceUid","HOST_EVENT_TYPES","isScreenGroup","pluginDocsHelp","SITE_EVENT_TYPES","nameSearchFields","describeVariantComparison","experimentResultRows","validateExperiment","EXPERIMENT_LIST_QUERY","mdiChartBar","mdiDeleteOutline","mdiPause","mdiPencilOutline","mdiPlay","CardDisplay","MdiIcon","useConfirmationContext","ListFilterChips","ListQueryNotices","listQueryRefusals","ListPagination","ListRowActions","ListTable","listActionsColumn","listFilterGridColumns","useListGridFilter","useListColumnSort","ScrollTable","TABLE_ROW_HEIGHT","useSnackbar","Timestamp","Alert","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","FormControlLabel","MenuItem","Stack","Switch","TableBody","TableCell","TableHead","TableRow","TextField","Typography","collection","doc","getDocs","limit","query","where","useMemo","useState","useFirestore","useFirestoreCollection","useHostActivityLogger","writeGuardedBySeed","useUser","writeSiteWideChange","useListQuery","ExperimentResultZone","ExperimentVariantsZone","marketingExperimentVariantDrafts","EXPERIMENT_STATUS_COLORS","draft","running","paused","done","EXPERIMENT_FILTER_FIELDS","fields","EXPERIMENT_FILTER_HEADERS","name","target","status","EXPERIMENT_TARGET_NOUNS","screen","section","email","EXPERIMENT_FILTER_OPTIONS","value","label","Object","keys","map","charAt","toUpperCase","slice","EXPERIMENT_PAGE_SORTS","experiment","variants","length","EXPERIMENT_PAGE_SORT_HEADERS","HostExperimentsCard","props","editor","results","hostId","firestore","data","user","org","enqueueSnackbar","confirm","logActivity","entitled","gridFilter","selectFields","askedSort","setAskedSort","filtering","clauses","searchWords","some","word","trim","rows","experimentRows","hasMore","hasMoreExperiments","page","experimentPage","setPage","setExperimentPage","pageSize","experimentPageSize","setPageSize","setExperimentPageSize","experimentsStatus","fromCache","experimentsFromCache","plan","experimentPlan","declaration","request","search","sort","deps","idField","experimentSort","sorts","defaultSort","onSortChange","orderBy","pageSorts","headers","experiments","experimentColumnsFor","columns","experimentRefusals","refused","options","setEditor","editorOpen","screenDocs","screenOptions","filter","deletedAt","id","$id","displayName","a","b","localeCompare","versionDocs","screenId","versionOptions","version","setResults","patch","partial","previous","proposeVariants","proposed","variant","index","subject","body","versionId","patchVariant","index2","newExperiment","weight","goal","event","handleSave","problem","persist","_ignored","payload","verdict","unreadable","Boolean","write","batch","set","JSON","parse","stringify","endAtMs","autoWinner","updatedAt","now","createdAt","merge","type","ok","message","error","console","setStatus","winnerVariantId","clash","docs","entry","find","candidate","handleDelete","accepted","title","description","confirmationText","confirmationButtonProps","color","then","catch","delete","openResults","snapshot","stats","forEach","experimentActions","key","icon","path","size","onClick","destructive","experimentColumns","field","headerName","flex","minWidth","valueGetter","_value","width","renderCell","row","quick","items","header","help","anchor","contentGutterX","contentGutterY","contentBordered","severity","spacing","sx","alignSelf","onChange","setClauses","notices","aria-label","rowHeight","onOpen","_id","hideFooter","columnSort","filterMode","filterModel","onFilterModelChange","quickFilter","noRowsLabel","rowCount","onPageChange","onPageSizeChange","open","onClose","maxWidth","fullWidth","display","flexDirection","gap","pt","required","mt","direction","select","option","helperText","nodeId","eventType","useFlexGap","alignItems","flexWrap","slotProps","inputLabel","shrink","Date","getTimezoneOffset","toISOString","ms","getTime","undefined","Number","isFinite","control","checked","minExposures","confidence","String","multiline","maxRows","experimentId","autoCompleted","ml","align","summary","comparison","leader","winner","selected","exposures","conversions","rate","toFixed","test"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/host-experiments-card.component.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 {\n type AglynOrgBilling,\n checkEntitlement,\n createResourceUid,\n HOST_EVENT_TYPES,\n isScreenGroup,\n pluginDocsHelp,\n SITE_EVENT_TYPES,\n} from '@aglyn/aglyn'\nimport { nameSearchFields } from '@aglyn/aglyn/app-utils/name-search'\nimport { describeVariantComparison, experimentResultRows, validateExperiment, type ExperimentTarget, type ExperimentVariant, type HostExperiment } from '../model'\nimport { EXPERIMENT_LIST_QUERY } from '../model/experiment-list-query'\nimport {\n mdiChartBar,\n mdiDeleteOutline,\n mdiPause,\n mdiPencilOutline,\n mdiPlay,\n} from '@aglyn/shared-data-mdi'\nimport { CardDisplay, MdiIcon, useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport ListFilterChips from '@aglyn/shared-ui-jsx/components/list-filter-chips.component'\nimport ListQueryNotices, {\n listQueryRefusals,\n} from '@aglyn/shared-ui-jsx/components/list-query-notices.component'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport {\n ListRowActions,\n ListTable,\n listActionsColumn,\n} from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport { listFilterGridColumns } from '@aglyn/shared-ui-jsx/const/list-grid-filter'\nimport { useListGridFilter } from '@aglyn/shared-ui-jsx/hooks/use-list-grid-filter'\nimport { useListColumnSort } from '@aglyn/shared-ui-jsx/hooks/use-list-column-sort'\nimport type { ListQuerySort } from '@aglyn/shared-ui-jsx/const/list-query-plan'\nimport type { RowActionsMenuItem } from '@aglyn/shared-ui-jsx/components/row-actions-menu.component'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { TABLE_ROW_HEIGHT } from '@aglyn/shared-ui-jsx/const/table-pagination'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { Timestamp } from '@aglyn/shared-util-timestamp'\nimport {\n Alert,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n FormControlLabel,\n MenuItem,\n Stack,\n Switch,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TextField,\n Typography,\n} from '@mui/material'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport {\n collection,\n doc,\n getDocs,\n limit,\n query,\n where,\n} from 'firebase/firestore'\nimport { useMemo, useState } from 'react'\nimport {\n useFirestore,\n useFirestoreCollection,\n useHostActivityLogger,\n writeGuardedBySeed,\n useUser,\n} from '@aglyn/tenant-feature-instance'\nimport { writeSiteWideChange } from '@aglyn/tenant-feature-instance/hooks/helpers/site-wide-change'\nimport { useListQuery } from '@aglyn/tenant-feature-instance/hooks/use-list-query'\nimport {\n ExperimentResultZone,\n ExperimentVariantsZone,\n marketingExperimentVariantDrafts,\n type MarketingExperimentVariantDraft,\n} from './experiment-zones'\n\nexport interface HostExperimentsCardProps {\n hostId: string\n /** Resolved entitlement source (AGL-395). */\n org?: Partial<AglynOrgBilling>\n}\n\ntype ExperimentDraft = HostExperiment & { $id?: string }\n\n/** An experiment's status as a chip color, in every list of experiments. */\nexport const EXPERIMENT_STATUS_COLORS: Record<\n string,\n 'default' | 'success' | 'info' | 'warning'\n> = {\n draft: 'default',\n running: 'success',\n paused: 'warning',\n done: 'info',\n}\n\n/*\n * What the experiments grid's Filters panel offers: the fields of the list's\n * query declaration (`EXPERIMENT_LIST_QUERY`), every one served by Firestore\n * (AGL-3321).\n */\nconst EXPERIMENT_FILTER_FIELDS = EXPERIMENT_LIST_QUERY.fields\nconst EXPERIMENT_FILTER_HEADERS: Readonly<Record<string, string>> = {\n name: 'Experiment',\n target: 'Tests',\n status: 'Status',\n}\n/** What each stored `target` reads as; the stored value stays `screen`. */\nconst EXPERIMENT_TARGET_NOUNS: Readonly<Record<string, string>> = {\n screen: 'page',\n section: 'section',\n email: 'email',\n}\nconst EXPERIMENT_FILTER_OPTIONS = {\n target: [\n { value: 'screen', label: 'Page' },\n { value: 'section', label: 'Section' },\n { value: 'email', label: 'Email' },\n ],\n status: Object.keys(EXPERIMENT_STATUS_COLORS).map((status) => ({\n value: status,\n label: status.charAt(0).toUpperCase() + status.slice(1),\n })),\n}\n\n/** Tests is drawn from the target and the variant count: it sorts the page (AGL-3680). */\nconst EXPERIMENT_PAGE_SORTS = {\n target: (experiment: ExperimentDraft) =>\n `${EXPERIMENT_TARGET_NOUNS[experiment.target] ?? experiment.target} ${(experiment.variants ?? []).length}`,\n}\nconst EXPERIMENT_PAGE_SORT_HEADERS = { target: 'Tests' }\n\n/**\n * Experiments manager (AGL-252): create screen/section/email A/B tests\n * with weighted variants and a conversion goal; start/pause/finish them\n * and read exposures/conversions per variant. Business tier\n * (`ab-testing`).\n */\nexport function HostExperimentsCard(props: HostExperimentsCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { org } = props\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const logActivity = useHostActivityLogger(hostId)\n const entitled = checkEntitlement(org, 'abTesting')\n\n /*\n * The Filters panel and the search box are the grid's; the clauses and the\n * words go onto ONE query over the site's experiments, ordered by name, and\n * each page is a page of that query's matches (AGL-3321). A clause the\n * query cannot hold beside the others is not applied, and the notice above\n * the list says which and why — never answered over the experiments that\n * happen to be loaded.\n */\n const gridFilter = useListGridFilter({ selectFields: ['target', 'status'] })\n // Experiment and Status order the query, Tests the page (AGL-3680).\n const [askedSort, setAskedSort] = useState<ListQuerySort | null>(null)\n const filtering =\n gridFilter.clauses.length > 0 || gridFilter.searchWords.some((word) => word.trim())\n const {\n rows: experimentRows,\n hasMore: hasMoreExperiments,\n page: experimentPage,\n setPage: setExperimentPage,\n pageSize: experimentPageSize,\n setPageSize: setExperimentPageSize,\n status: experimentsStatus,\n /**\n * The rows this editor is seeded from are unconfirmed by the server\n * (AGL-1358). Editing spreads a whole stored experiment into `editor`\n * and writes all of it back, so `merge: true` protects nothing — every\n * field is in the payload. `status` is the one that bites: it is\n * otherwise only ever moved by the start/pause/finish controls below, so\n * a cached seed can quietly restart a finished test, or stop a running\n * one, along with reverting its variant weights and goal.\n */\n fromCache: experimentsFromCache,\n listen: experimentsListen,\n plan: experimentPlan,\n } = useListQuery<ExperimentDraft>({\n collection: collection(firestore, 'hosts', hostId, 'experiments'),\n /*\n * Ordered by the server and paged (AGL-2501, AGL-2292): the declaration's\n * one order is `name`, which every writer sets — the editor refuses a save\n * without one (`validateExperiment`), the besigner's section shortcut\n * names its draft, the two send-path readers only ever write the stats\n * beneath an experiment, and `experiments` is not in `IMPORTABLE_FIELDS`.\n * There is no writer that can produce a nameless experiment for `orderBy`\n * to drop.\n *\n * Nothing is dropped after the query either. An experiment is never\n * soft-deleted — Delete below is a `deleteDoc` — so every row the query\n * returns is live, and a page holds as many rows as its size.\n */\n declaration: EXPERIMENT_LIST_QUERY,\n request: { clauses: gridFilter.clauses, search: gridFilter.searchWords, sort: askedSort },\n deps: [firestore, hostId],\n idField: '$id',\n })\n const experimentSort = useListColumnSort<ExperimentDraft>({\n sorts: EXPERIMENT_LIST_QUERY.sorts,\n defaultSort: EXPERIMENT_LIST_QUERY.sorts[0],\n sort: askedSort,\n onSortChange: setAskedSort,\n orderBy: experimentPlan.orderBy,\n rows: experimentRows,\n pageSorts: EXPERIMENT_PAGE_SORTS,\n headers: EXPERIMENT_PAGE_SORT_HEADERS,\n })\n const experiments: ExperimentDraft[] = experimentSort.rows as ExperimentDraft[]\n const experimentColumnsFor = (columns: GridColDef[]) =>\n listFilterGridColumns(\n columns,\n EXPERIMENT_FILTER_FIELDS,\n EXPERIMENT_FILTER_OPTIONS,\n EXPERIMENT_FILTER_HEADERS,\n )\n const experimentRefusals = useMemo(\n () =>\n listQueryRefusals(experimentPlan.refused, {\n fields: EXPERIMENT_FILTER_FIELDS,\n headers: EXPERIMENT_FILTER_HEADERS,\n options: EXPERIMENT_FILTER_OPTIONS,\n }),\n [experimentPlan.refused],\n )\n const [editor, setEditor] = useState<ExperimentDraft | null>(null)\n /*\n * The screens and their versions fill the EDITOR's pickers and nothing\n * else on the card, so they are read while the editor is open and not\n * before: a reader who came to see which tests are running pays for the\n * experiments list alone, not for a hundred screens they never pick from.\n */\n const editorOpen = editor !== null\n const { data: screenDocs } = useFirestoreCollection<any>(\n () =>\n editorOpen\n ? query(collection(firestore, 'hosts', hostId, 'screens'), limit(100))\n : null,\n [firestore, hostId, editorOpen],\n { idField: '$id' },\n )\n // A page group (AGL-3463) has no versions to test against each other.\n const screenOptions = [...(screenDocs ?? [])]\n .filter((screen: any) => !screen.deletedAt && !isScreenGroup(screen))\n .map((screen: any) => ({\n id: screen.$id as string,\n name: (screen.displayName ?? screen.name ?? screen.$id) as string,\n }))\n .sort((a, b) => a.name.localeCompare(b.name))\n\n // Versions of the screen under test (AGL-253): variants pin one each.\n const { data: versionDocs } = useFirestoreCollection<any>(\n () =>\n editor?.screenId\n ? query(\n collection(\n firestore,\n 'hosts',\n hostId,\n 'screens',\n editor.screenId,\n 'versions',\n ),\n limit(50),\n )\n : null,\n [firestore, hostId, editor?.screenId],\n { idField: '$id' },\n )\n const versionOptions = [...(versionDocs ?? [])]\n .filter((version: any) => !version.deletedAt)\n .map((version: any) => ({\n id: version.$id as string,\n name: (version.name ?? version.displayName ?? version.$id) as string,\n }))\n .sort((a, b) => a.name.localeCompare(b.name))\n const [results, setResults] = useState<{\n experiment: ExperimentDraft\n stats: Record<string, { exposures?: number; conversions?: number }>\n } | null>(null)\n\n const patch = (partial: Partial<ExperimentDraft>) =>\n setEditor((previous) =>\n previous ? { ...previous, ...partial } : previous,\n )\n /**\n * What a widget in the variants zone proposes, put into the editor's own\n * fields as unsaved changes. Only the variants the editor already holds are\n * filled, in order: the test's shape — how many arms, their ids, their\n * weights, the version each screen variant pins — is the person's, and a\n * proposal that could add or drop an arm would be changing the test rather\n * than writing copy for it. A field a widget left empty is left alone, so\n * a proposal for an email's subject does not blank a body someone typed.\n */\n const proposeVariants = (\n proposed: readonly MarketingExperimentVariantDraft[],\n ) =>\n setEditor((previous) =>\n previous\n ? {\n ...previous,\n variants: previous.variants.map((variant, index) => {\n const draft = proposed[index]\n if (!draft) return variant\n return {\n ...variant,\n ...(draft.name ? { name: draft.name } : {}),\n ...(draft.subject ? { subject: draft.subject } : {}),\n ...(draft.body ? { body: draft.body } : {}),\n // A draft version a widget made for this variant (AGL-3603),\n // pinned unsaved like a version picked from the list.\n ...(draft.versionId && previous.target !== 'email' ? { versionId: draft.versionId } : {}),\n }\n }),\n }\n : previous,\n )\n const patchVariant = (index: number, partial: Partial<ExperimentVariant>) =>\n setEditor((previous) =>\n previous\n ? {\n ...previous,\n variants: previous.variants.map((variant, index2) =>\n index2 === index ? { ...variant, ...partial } : variant,\n ),\n }\n : previous,\n )\n\n const newExperiment = (): ExperimentDraft => ({\n name: '',\n status: 'draft',\n target: 'screen' as ExperimentTarget,\n variants: [\n { id: 'a', name: 'A (control)', weight: 1 },\n { id: 'b', name: 'B', weight: 1 },\n ],\n goal: { event: 'formSubmission' },\n })\n\n const handleSave = async () => {\n if (!editor) return\n const problem = validateExperiment(editor)\n if (problem) {\n return void enqueueSnackbar(problem, {\n variant: 'warning',\n persist: false,\n })\n }\n const id = editor.$id ?? createResourceUid()\n const { $id: _ignored, ...payload } = editor\n try {\n /**\n * Refuse an EDIT whose seed the server never confirmed (AGL-1358).\n *\n * `merge: true` is here so a cleared end date or auto-winner overwrites\n * (AGL-273), not to protect untouched fields — there are none, because\n * the payload is the whole stored row spread into `editor`. `status`\n * and `winnerVariantId` ride along with it, and those are otherwise\n * only moved by `setStatus`, so a cached seed can restart a test the\n * owner finished or halt one that is splitting live traffic.\n *\n * Only the edit path. A NEW experiment comes from `newExperiment()` at\n * a fresh uid and can overwrite nothing, and the first snapshot of any\n * listener is `fromCache: true`, so guarding a create would refuse a\n * save that was never unsafe.\n *\n * The guard WRAPS the write, and the activity log with it: a logged\n * \"Updated experiment\" whose write never happened leaves the history\n * disagreeing with the experiment it claims to explain.\n */\n const verdict = await writeGuardedBySeed(\n {\n subject: 'experiment',\n unreadable: Boolean(editor.$id) && experimentsStatus === 'error',\n fromCache: Boolean(editor.$id) && experimentsFromCache,\n listen: experimentsListen,\n },\n async () => {\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.set(\n doc(firestore, 'hosts', hostId, 'experiments', id),\n {\n ...JSON.parse(JSON.stringify(payload)),\n /*\n * The name's search keys, from the name being written — the\n * list's Experiment filter and its search box read them\n * (AGL-3321). After the payload, so keys copied from the stored\n * row cannot outlive a rename.\n */\n ...nameSearchFields(editor.name ?? ''),\n // Clearing the end date / auto-winner must overwrite — a\n // merge-set keeps absent keys otherwise (AGL-273).\n endAtMs: editor.endAtMs ?? null,\n autoWinner: editor.autoWinner ?? null,\n updatedAt: Timestamp.now(),\n ...(editor.$id ? {} : { createdAt: Timestamp.now() }),\n },\n { merge: true },\n ),\n })\n logActivity(\n editor.$id ? 'Updated experiment' : 'Created experiment',\n { type: 'content', id, name: editor.name },\n )\n },\n )\n // A refusal keeps the editor open with everything that was typed.\n if (!verdict.ok) {\n return void enqueueSnackbar(verdict.message, {\n variant: 'warning',\n persist: false,\n })\n }\n enqueueSnackbar('Experiment saved', {\n variant: 'success',\n persist: false,\n })\n setEditor(null)\n } catch (error) {\n console.error(error)\n enqueueSnackbar('An error has occurred', { variant: 'error' })\n }\n }\n\n const setStatus = async (\n experiment: ExperimentDraft,\n status: HostExperiment['status'],\n winnerVariantId?: string,\n ) => {\n if (!experiment.$id) return\n /*\n * One running experiment per screen (AGL-265): the page runner only\n * serves the first, so a second would silently never split traffic.\n *\n * Asked of Firestore, not of the rows on screen: the list is one page of\n * a query the reader may have narrowed, and the running test on the same\n * screen is as likely to be on another page, or filtered out, as on this\n * one. Two equalities, served by merging their single-field indexes.\n */\n if (status === 'running' && experiment.screenId) {\n const running = await getDocs(\n query(\n collection(firestore, 'hosts', hostId, 'experiments'),\n where('status', '==', 'running'),\n where('screenId', '==', experiment.screenId),\n limit(2),\n ),\n )\n const clash = running.docs\n .map((entry) => ({ ...(entry.data() as HostExperiment), $id: entry.id }))\n .find((candidate) => candidate.$id !== experiment.$id)\n if (clash) {\n return void enqueueSnackbar(\n `\"${clash.name}\" is already running on that page — pause or ` +\n 'finish it first',\n { variant: 'warning', persist: false },\n )\n }\n }\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.set(\n doc(firestore, 'hosts', hostId, 'experiments', experiment.$id),\n {\n status,\n ...(winnerVariantId ? { winnerVariantId } : {}),\n updatedAt: Timestamp.now(),\n },\n { merge: true },\n ),\n })\n logActivity(`Experiment ${status}`, {\n type: 'content',\n id: experiment.$id,\n name: experiment.name,\n })\n }\n\n const handleDelete = async (experiment: ExperimentDraft) => {\n if (!experiment.$id) return\n const accepted = await confirm({\n title: 'Delete experiment?',\n description: `\"${experiment.name}\" and its results are removed.`,\n confirmationText: 'Delete',\n confirmationButtonProps: { color: 'error' },\n })\n // Resolves with no value and rejects on cancel — gating on the\n // resolved value alone made this always return (AGL-950).\n .then(() => true)\n .catch(() => false)\n if (!accepted) return\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.delete(\n doc(firestore, 'hosts', hostId, 'experiments', experiment.$id),\n ),\n })\n }\n\n const openResults = async (experiment: ExperimentDraft) => {\n if (!experiment.$id) return\n const snapshot = await getDocs(\n collection(\n firestore,\n 'hosts',\n hostId,\n 'experiments',\n experiment.$id,\n 'stats',\n ),\n )\n const stats: Record<string, any> = {}\n snapshot.forEach((entry) => {\n stats[entry.id] = entry.data()\n })\n setResults({ experiment, stats })\n }\n\n /*\n * One row per experiment. The row opens its results, the one thing every\n * experiment has to show; starting, pausing, editing and deleting it are in\n * the row's menu rather than a row of buttons one mis-click from each other.\n */\n const experimentActions = (experiment: ExperimentDraft): RowActionsMenuItem[] => [\n ...(experiment.status === 'running'\n ? [\n {\n key: 'pause',\n label: 'Pause',\n icon: <MdiIcon path={mdiPause.path} size={0.8} />,\n onClick: () => void setStatus(experiment, 'paused'),\n },\n ]\n : experiment.status !== 'done'\n ? [\n {\n key: 'start',\n label: 'Start',\n icon: <MdiIcon path={mdiPlay.path} size={0.8} />,\n onClick: () => void setStatus(experiment, 'running'),\n },\n ]\n : []),\n {\n key: 'edit',\n label: 'Edit',\n icon: <MdiIcon path={mdiPencilOutline.path} size={0.8} />,\n onClick: () => setEditor({ ...experiment }),\n },\n {\n key: 'delete',\n label: 'Delete',\n icon: <MdiIcon path={mdiDeleteOutline.path} size={0.8} />,\n destructive: true,\n onClick: () => void handleDelete(experiment),\n },\n ]\n const experimentColumns: GridColDef<ExperimentDraft>[] = [\n { field: 'name', headerName: 'Experiment', flex: 1, minWidth: 200 },\n {\n field: 'target',\n headerName: 'Tests',\n flex: 1,\n minWidth: 200,\n valueGetter: (_value, experiment) =>\n `${EXPERIMENT_TARGET_NOUNS[experiment.target] ?? experiment.target} · ${(experiment.variants ?? []).length} variants`,\n },\n {\n field: 'status',\n headerName: 'Status',\n width: 130,\n renderCell: ({ row: experiment }) => (\n <Chip\n size=\"small\"\n color={EXPERIMENT_STATUS_COLORS[experiment.status] ?? 'default'}\n label={experiment.status}\n />\n ),\n },\n listActionsColumn(\n (experiment: ExperimentDraft) => (\n <ListRowActions\n label={experiment.name || 'this experiment'}\n quick={{\n icon: mdiChartBar.path,\n label: 'Results',\n onClick: () => void openResults(experiment),\n }}\n items={experimentActions(experiment)}\n />\n ),\n ),\n ]\n\n return (\n <CardDisplay\n header=\"Experiments\"\n help={pluginDocsHelp('emailCampaigns', { anchor: '#experiments' })}\n contentGutterX\n contentGutterY\n contentBordered=\"all\"\n >\n {!entitled ? (\n <Alert severity=\"info\">\n {'A/B experiments are included in the Business plan — see ' +\n 'Billing to upgrade.'}\n </Alert>\n ) : (\n <Stack spacing={1.5}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Split traffic between variants of a page, a section, or a ' +\n 'campaign email, and measure which one converts. Visitors ' +\n 'are assigned deterministically, so everyone keeps seeing ' +\n 'the same variant.'}\n </Typography>\n <Button\n size=\"small\"\n variant=\"contained\"\n color=\"primary\"\n sx={{ alignSelf: 'flex-start' }}\n onClick={() => setEditor(newExperiment())}\n >\n {'New experiment'}\n </Button>\n {experiments.length === 0 && !hasMoreExperiments && experimentPage === 0 && !filtering ? null : (\n <>\n <ListFilterChips\n fields={EXPERIMENT_FILTER_FIELDS}\n headers={EXPERIMENT_FILTER_HEADERS}\n clauses={gridFilter.clauses}\n onChange={gridFilter.setClauses}\n options={EXPERIMENT_FILTER_OPTIONS}\n />\n <ListQueryNotices\n refused={experimentRefusals}\n notices={[...experimentPlan.notices, ...experimentSort.notices]}\n />\n <ListTable\n aria-label=\"Experiments\"\n rows={experiments}\n columns={experimentColumnsFor(experimentColumns as GridColDef[])}\n rowHeight={TABLE_ROW_HEIGHT}\n onOpen={(_id, experiment) => void openResults(experiment)}\n // Paged by the footer below, so the grid must not also slice.\n hideFooter\n // Experiment and Status order the query; Tests sorts the page\n // and says so (AGL-3680).\n columnSort={experimentSort}\n /*\n * The panel and the search are the grid's; every clause and the\n * search word are on the list's query (AGL-3321), so the grid\n * never narrows the page it is handed.\n */\n filterMode=\"server\"\n filterModel={gridFilter.filterModel}\n onFilterModelChange={gridFilter.onFilterModelChange}\n quickFilter\n noRowsLabel=\"No experiments match these filters\"\n />\n <ListPagination\n page={experimentPage}\n pageSize={experimentPageSize}\n rowCount={experiments.length}\n hasMore={hasMoreExperiments}\n onPageChange={setExperimentPage}\n onPageSizeChange={setExperimentPageSize}\n />\n </>\n )}\n </Stack>\n )}\n\n <Dialog\n open={Boolean(editor)}\n onClose={() => setEditor(null)}\n maxWidth=\"sm\"\n fullWidth\n >\n <DialogTitle>\n {editor?.$id ? 'Edit experiment' : 'New experiment'}\n </DialogTitle>\n <DialogContent\n sx={{ display: 'flex', flexDirection: 'column', gap: 1.5, pt: 1 }}\n >\n <TextField\n size=\"small\"\n label=\"Name\"\n required\n value={editor?.name ?? ''}\n onChange={(event) => patch({ name: event.target.value })}\n sx={{ mt: 1 }}\n />\n <Stack direction=\"row\" spacing={1}>\n <TextField\n select\n size=\"small\"\n label=\"Tests\"\n value={editor?.target ?? 'screen'}\n onChange={(event) =>\n patch({ target: event.target.value as ExperimentTarget })\n }\n sx={{ minWidth: 140 }}\n >\n <MenuItem value=\"screen\">{'A page'}</MenuItem>\n <MenuItem value=\"section\">{'A section'}</MenuItem>\n <MenuItem value=\"email\">{'An email'}</MenuItem>\n </TextField>\n {editor?.target !== 'email' ? (\n <TextField\n select\n size=\"small\"\n label=\"Page\"\n value={editor?.screenId ?? ''}\n onChange={(event) => patch({ screenId: event.target.value })}\n sx={{ flex: 1 }}\n >\n {screenOptions.map((option) => (\n <MenuItem key={option.id} value={option.id}>\n {option.name}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n </Stack>\n {editor?.target === 'section' ? (\n <TextField\n size=\"small\"\n label=\"Element id\"\n helperText=\"The canvas element the variants swap (from the besigner)\"\n value={editor?.nodeId ?? ''}\n onChange={(event) => patch({ nodeId: event.target.value })}\n />\n ) : null}\n <TextField\n select\n size=\"small\"\n label=\"Conversion goal\"\n value={editor?.goal?.event ?? 'formSubmission'}\n onChange={(event) =>\n patch({ goal: { ...editor?.goal, event: event.target.value } })\n }\n sx={{ maxWidth: 260 }}\n >\n {[...HOST_EVENT_TYPES, ...SITE_EVENT_TYPES].map((eventType) => (\n <MenuItem key={eventType} value={eventType}>\n {eventType}\n </MenuItem>\n ))}\n </TextField>\n {/* Schedule + auto-winner (AGL-273). */}\n <Stack\n useFlexGap\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center', flexWrap: 'wrap' }}\n >\n <TextField\n size=\"small\"\n type=\"datetime-local\"\n label=\"Ends at (optional)\"\n slotProps={{ inputLabel: { shrink: true } }}\n value={\n editor?.endAtMs\n ? new Date(\n editor.endAtMs -\n new Date().getTimezoneOffset() * 60000,\n )\n .toISOString()\n .slice(0, 16)\n : ''\n }\n onChange={(event) => {\n const ms = event.target.value\n ? new Date(event.target.value).getTime()\n : undefined\n patch({\n endAtMs: Number.isFinite(ms as number) ? ms : undefined,\n })\n }}\n helperText=\"Past this, visitors get the default\"\n />\n <FormControlLabel\n control={\n <Switch\n size=\"small\"\n checked={Boolean(editor?.autoWinner)}\n onChange={(event) =>\n patch({\n autoWinner: event.target.checked\n ? { minExposures: 200, confidence: 0.95 }\n : undefined,\n })\n }\n />\n }\n label=\"Auto-declare winner\"\n />\n {editor?.autoWinner ? (\n <>\n <TextField\n size=\"small\"\n type=\"number\"\n label=\"Min exposures / variant\"\n value={editor.autoWinner.minExposures}\n onChange={(event) =>\n patch({\n autoWinner: {\n ...editor.autoWinner!,\n minExposures: Number(event.target.value),\n },\n })\n }\n sx={{ width: 170 }}\n />\n <TextField\n select\n size=\"small\"\n label=\"Confidence\"\n value={String(editor.autoWinner.confidence)}\n onChange={(event) =>\n patch({\n autoWinner: {\n ...editor.autoWinner!,\n confidence: Number(event.target.value),\n },\n })\n }\n sx={{ width: 120 }}\n >\n <MenuItem value=\"0.9\">{'90%'}</MenuItem>\n <MenuItem value=\"0.95\">{'95%'}</MenuItem>\n <MenuItem value=\"0.99\">{'99%'}</MenuItem>\n </TextField>\n </>\n ) : null}\n </Stack>\n <Typography variant=\"subtitle2\">{'Variants'}</Typography>\n {(editor?.variants ?? []).map((variant, index) => (\n <Stack key={variant.id} spacing={1}>\n <Stack direction=\"row\" spacing={1}>\n <TextField\n size=\"small\"\n label={`Variant ${variant.id.toUpperCase()}`}\n value={variant.name ?? ''}\n onChange={(event) =>\n patchVariant(index, { name: event.target.value })\n }\n sx={{ flex: 1 }}\n />\n <TextField\n size=\"small\"\n type=\"number\"\n label=\"Weight\"\n value={variant.weight ?? 1}\n onChange={(event) =>\n patchVariant(index, { weight: Number(event.target.value) })\n }\n sx={{ width: 100 }}\n />\n {editor?.target !== 'email' ? (\n // Screen/section variants pin a screen version\n // (AGL-253); empty = the published version.\n <TextField\n select\n size=\"small\"\n label=\"Version\"\n value={variant.versionId ?? ''}\n onChange={(event) =>\n patchVariant(index, { versionId: event.target.value })\n }\n sx={{ minWidth: 160 }}\n >\n <MenuItem value=\"\">{'Published (control)'}</MenuItem>\n {versionOptions.map((option) => (\n <MenuItem key={option.id} value={option.id}>\n {option.name}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n </Stack>\n {editor?.target === 'email' ? (\n // Email variants override the campaign copy (AGL-255).\n <Stack direction=\"row\" spacing={1}>\n <TextField\n size=\"small\"\n label=\"Subject override\"\n value={variant.subject ?? ''}\n onChange={(event) =>\n patchVariant(index, { subject: event.target.value })\n }\n sx={{ width: 220 }}\n />\n <TextField\n size=\"small\"\n label=\"Body override\"\n value={variant.body ?? ''}\n onChange={(event) =>\n patchVariant(index, { body: event.target.value })\n }\n multiline\n maxRows={3}\n sx={{ flex: 1 }}\n />\n </Stack>\n ) : null}\n </Stack>\n ))}\n {/*\n Beneath the variants, never above them: a widget here writes copy\n FOR the list that precedes it, and the list is what the person\n came to the dialog for.\n */}\n {editor ? (\n <ExperimentVariantsZone\n hostId={hostId}\n experimentId={editor.$id ?? ''}\n name={editor.name ?? ''}\n target={editor.target ?? 'screen'}\n goal={editor.goal?.event ?? ''}\n screenId={editor.target === 'email' ? '' : (editor.screenId ?? '')}\n nodeId={editor.target === 'section' ? (editor.nodeId ?? '') : ''}\n variants={marketingExperimentVariantDrafts(editor.variants)}\n proposeVariants={proposeVariants}\n />\n ) : null}\n </DialogContent>\n <DialogActions>\n <Button color=\"inherit\" onClick={() => setEditor(null)}>\n {'Cancel'}\n </Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={() => void handleSave()}\n >\n {'Save'}\n </Button>\n </DialogActions>\n </Dialog>\n\n <Dialog\n open={Boolean(results)}\n onClose={() => setResults(null)}\n maxWidth=\"xs\"\n fullWidth\n >\n <DialogTitle>\n {`Results — ${results?.experiment.name ?? ''}`}\n {results?.experiment.autoCompleted ? (\n // The track pipeline decided this one (AGL-273).\n <Chip\n size=\"small\"\n color=\"success\"\n label=\"Auto-completed\"\n sx={{ ml: 1 }}\n />\n ) : null}\n </DialogTitle>\n <DialogContent>\n <ScrollTable size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{'Variant'}</TableCell>\n <TableCell>{'Exposures'}</TableCell>\n <TableCell>{'Conversions'}</TableCell>\n <TableCell>{'Rate'}</TableCell>\n <TableCell>{'vs control'}</TableCell>\n <TableCell align=\"right\" />\n </TableRow>\n </TableHead>\n <TableBody>\n {/* Lift + z-test confidence vs the first variant (AGL-265). */}\n {(results\n ? experimentResultRows(results.experiment, results.stats)\n : []\n ).map(({ variant, summary, comparison, leader, winner }) => (\n <TableRow key={variant.id} selected={leader}>\n <TableCell>\n {variant.name ?? variant.id.toUpperCase()}\n {winner ? ' 🏆' : leader ? ' ▲' : ''}\n </TableCell>\n <TableCell>{summary.exposures}</TableCell>\n <TableCell>{summary.conversions}</TableCell>\n <TableCell>{`${(summary.rate * 100).toFixed(1)}%`}</TableCell>\n <TableCell>{describeVariantComparison(comparison)}</TableCell>\n <TableCell align=\"right\">\n {results?.experiment.status !== 'done' ? (\n <Button\n size=\"small\"\n onClick={() => {\n if (results) {\n void setStatus(results.experiment, 'done', variant.id)\n setResults(null)\n }\n }}\n >\n {'Pick winner'}\n </Button>\n ) : null}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </ScrollTable>\n {/*\n Below the figures, so anything a widget says about this test is\n read after the counts it is talking about, and the test is named\n as the results reader labels it — its name, or its id where it has\n none, which is the same fallback that reader takes.\n */}\n {results ? (\n <ExperimentResultZone\n hostId={hostId}\n experimentId={results.experiment.$id ?? ''}\n test={results.experiment.name?.trim() || (results.experiment.$id ?? '')}\n />\n ) : null}\n </DialogContent>\n <DialogActions>\n <Button color=\"inherit\" onClick={() => setResults(null)}>\n {'Close'}\n </Button>\n </DialogActions>\n </Dialog>\n </CardDisplay>\n )\n}\nHostExperimentsCard.displayName = 'HostExperimentsCard'\n\nexport default HostExperimentsCard\n"],"names":["checkEntitlement","createResourceUid","HOST_EVENT_TYPES","isScreenGroup","pluginDocsHelp","SITE_EVENT_TYPES","nameSearchFields","describeVariantComparison","experimentResultRows","validateExperiment","EXPERIMENT_LIST_QUERY","mdiChartBar","mdiDeleteOutline","mdiPause","mdiPencilOutline","mdiPlay","CardDisplay","MdiIcon","useConfirmationContext","ListFilterChips","ListQueryNotices","listQueryRefusals","ListPagination","ListRowActions","ListTable","listActionsColumn","listFilterGridColumns","useListGridFilter","useListColumnSort","ScrollTable","TABLE_ROW_HEIGHT","useSnackbar","Timestamp","Alert","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","FormControlLabel","MenuItem","Stack","Switch","TableBody","TableCell","TableHead","TableRow","TextField","Typography","collection","doc","getDocs","limit","query","where","useMemo","useState","useFirestore","useFirestoreCollection","useHostActivityLogger","writeGuardedBySeed","useUser","writeSiteWideChange","useListQuery","ExperimentResultZone","ExperimentVariantsZone","marketingExperimentVariantDrafts","EXPERIMENT_STATUS_COLORS","draft","running","paused","done","EXPERIMENT_FILTER_FIELDS","fields","EXPERIMENT_FILTER_HEADERS","name","target","status","EXPERIMENT_TARGET_NOUNS","screen","section","email","EXPERIMENT_FILTER_OPTIONS","value","label","Object","keys","map","charAt","toUpperCase","slice","EXPERIMENT_PAGE_SORTS","experiment","variants","length","EXPERIMENT_PAGE_SORT_HEADERS","HostExperimentsCard","props","editor","results","hostId","firestore","data","user","org","enqueueSnackbar","confirm","logActivity","entitled","gridFilter","selectFields","askedSort","setAskedSort","filtering","clauses","searchWords","some","word","trim","rows","experimentRows","hasMore","hasMoreExperiments","page","experimentPage","setPage","setExperimentPage","pageSize","experimentPageSize","setPageSize","setExperimentPageSize","experimentsStatus","fromCache","experimentsFromCache","listen","experimentsListen","plan","experimentPlan","declaration","request","search","sort","deps","idField","experimentSort","sorts","defaultSort","onSortChange","orderBy","pageSorts","headers","experiments","experimentColumnsFor","columns","experimentRefusals","refused","options","setEditor","editorOpen","screenDocs","screenOptions","filter","deletedAt","id","$id","displayName","a","b","localeCompare","versionDocs","screenId","versionOptions","version","setResults","patch","partial","previous","proposeVariants","proposed","variant","index","subject","body","versionId","patchVariant","index2","newExperiment","weight","goal","event","handleSave","problem","persist","_ignored","payload","verdict","unreadable","Boolean","write","batch","set","JSON","parse","stringify","endAtMs","autoWinner","updatedAt","now","createdAt","merge","type","ok","message","error","console","setStatus","winnerVariantId","clash","docs","entry","find","candidate","handleDelete","accepted","title","description","confirmationText","confirmationButtonProps","color","then","catch","delete","openResults","snapshot","stats","forEach","experimentActions","key","icon","path","size","onClick","destructive","experimentColumns","field","headerName","flex","minWidth","valueGetter","_value","width","renderCell","row","quick","items","header","help","anchor","contentGutterX","contentGutterY","contentBordered","severity","spacing","sx","alignSelf","onChange","setClauses","notices","aria-label","rowHeight","onOpen","_id","hideFooter","columnSort","filterMode","filterModel","onFilterModelChange","quickFilter","noRowsLabel","rowCount","onPageChange","onPageSizeChange","open","onClose","maxWidth","fullWidth","display","flexDirection","gap","pt","required","mt","direction","select","option","helperText","nodeId","eventType","useFlexGap","alignItems","flexWrap","slotProps","inputLabel","shrink","Date","getTimezoneOffset","toISOString","ms","getTime","undefined","Number","isFinite","control","checked","minExposures","confidence","String","multiline","maxRows","experimentId","autoCompleted","ml","align","summary","comparison","leader","winner","selected","exposures","conversions","rate","toFixed","test"],"mappings":"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"}
@@ -96,7 +96,7 @@ function parsePatterns(value) {
96
96
  * touched. Including `stats`, the lifetime impression/click/dismissal
97
97
  * counters the org beacon increments: a save here would roll live
98
98
  * engagement numbers back to whenever the snapshot was taken.
99
- */ fromCache: overlaysFromCache } = useFirestoreCollection(()=>query(collection(firestore, 'hosts', hostId, 'overlays'), /*
99
+ */ fromCache: overlaysFromCache, listen: overlaysListen } = useFirestoreCollection(()=>query(collection(firestore, 'hosts', hostId, 'overlays'), /*
100
100
  * ORDERED AND CEILINGED, deliberately not paged (AGL-2501).
101
101
  *
102
102
  * ## Why the read is ordered
@@ -222,7 +222,8 @@ function parsePatterns(value) {
222
222
  */ const verdict = await writeGuardedBySeed({
223
223
  subject: 'overlay',
224
224
  unreadable: Boolean(editor.$id) && overlaysStatus === 'error',
225
- fromCache: Boolean(editor.$id) && overlaysFromCache
225
+ fromCache: Boolean(editor.$id) && overlaysFromCache,
226
+ listen: overlaysListen
226
227
  }, async ()=>{
227
228
  await writeSiteWideChange({
228
229
  firestore,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/host-overlays-card.component.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 {\n type AglynOrgBilling,\n checkEntitlement,\n createResourceUid,\n pluginDocsHelp,\n} from '@aglyn/aglyn'\nimport {\n compareOverlayPrecedence,\n OVERLAY_COPY_LIMITS,\n OVERLAY_LIST_CEILING,\n OVERLAY_POPUP_TRIGGERS,\n overlayCopyFromProposal,\n overlayDraftFromProposal,\n overlayStatus,\n type HostOverlay,\n type OverlayCopyProposal,\n} from '../model'\nimport { mdiChevronDown, mdiChevronUp } from '@aglyn/shared-data-mdi'\nimport {\n CardDisplay,\n MdiIcon,\n useConfirmationContext,\n} from '@aglyn/shared-ui-jsx'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { Timestamp } from '@aglyn/shared-util-timestamp'\nimport {\n Alert,\n Button,\n Chip,\n IconButton,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n MenuItem,\n Stack,\n Switch,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TextField,\n Typography,\n} from '@mui/material'\nimport { collection, doc, limit, orderBy, query } from 'firebase/firestore'\nimport { useState } from 'react'\nimport {\n useFirestore,\n useFirestoreCollection,\n useHostActivityLogger,\n writeGuardedBySeed,\n useUser,\n} from '@aglyn/tenant-feature-instance'\nimport { writeSiteWideChange } from '@aglyn/tenant-feature-instance/hooks/helpers/site-wide-change'\nimport {\n overlayDisplayName,\n overlayEngagementLabel,\n overlayPagesLabel,\n overlayWindowLabel,\n} from './overlay-labels'\nimport {\n OVERLAY_COPY_HELPER_TEXT,\n shownAsTypedHelperText,\n useOverlayCopyEditor,\n} from './use-overlay-copy-editor'\nimport {\n HostOverlaysZone,\n OverlayEditorZone,\n type MarketingOverlayDraftResult,\n} from './overlay-zones'\n\nexport interface HostOverlaysCardProps {\n hostId: string\n /** Resolved entitlement source (AGL-395). */\n org?: Partial<AglynOrgBilling>\n}\n\ntype OverlayDraft = HostOverlay & { $id?: string }\n\n/**\n * How many overlays the card reads.\n *\n * A ceiling rather than a page size — see the query, which explains why this\n * one list is deliberately not paginated. Generous on purpose: a site runs a\n * handful of banners and popups, and the number exists to bound a pathological\n * collection rather than to bound a normal one.\n */\nconst CEILING = OVERLAY_LIST_CEILING\n\nconst EMPTY_BAR: OverlayDraft = {\n kind: 'bar',\n enabled: true,\n bar: { dismissible: true },\n}\nconst EMPTY_POPUP: OverlayDraft = {\n kind: 'popup',\n enabled: true,\n popup: { trigger: 'delay', triggerValue: 3, frequencyDays: 7 },\n}\n\nfunction toDatetimeLocal(ms?: number): string {\n if (!ms) return ''\n const date = new Date(ms)\n const pad = (value: number) => String(value).padStart(2, '0')\n return (\n `${date.getFullYear()}-${pad(date.getMonth() + 1)}-` +\n `${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`\n )\n}\n\nfunction fromDatetimeLocal(value: string): number | undefined {\n if (!value) return undefined\n const ms = new Date(value).getTime()\n return Number.isFinite(ms) ? ms : undefined\n}\n\nfunction parsePatterns(value: string): string[] {\n return value\n .split(',')\n .map((pattern) => pattern.trim())\n .filter(Boolean)\n}\n\n/**\n * Marketing hub overlays manager (AGL-251): multiple announcement bars\n * and popups at `hosts/{hostId}/overlays`, each with a schedule window\n * and page targeting; the tenant render shows the first active match per\n * kind (overlay docs win over the legacy single bar/popup fields). A bar's\n * text and a popup's headline and body are edited through\n * `useOverlayCopyEditor`, like the single bar and popup cards (AGL-2885).\n */\nexport function HostOverlaysCard(props: HostOverlaysCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { org } = props\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const logActivity = useHostActivityLogger(hostId)\n const entitled = checkEntitlement(org, 'marketingOverlays')\n\n const {\n data: overlayDocs,\n status: overlaysStatus,\n /**\n * The overlay rows the editor is seeded from are unconfirmed by the\n * server (AGL-1358). Editing one spreads the whole stored row into\n * `editor` and writes it back with NO options argument at all — a full\n * document replace — so a cached seed reverts every field nobody\n * touched. Including `stats`, the lifetime impression/click/dismissal\n * counters the org beacon increments: a save here would roll live\n * engagement numbers back to whenever the snapshot was taken.\n */\n fromCache: overlaysFromCache,\n } = useFirestoreCollection<any>(\n () =>\n query(\n collection(firestore, 'hosts', hostId, 'overlays'),\n /*\n * ORDERED AND CEILINGED, deliberately not paged (AGL-2501).\n *\n * ## Why the read is ordered\n *\n * `limit(50)` alone is answered in DOCUMENT-ID order over ids from\n * `createResourceUid()`, so the window was a pseudo-random fifty that\n * the client sort below arranged into a believable precedence list.\n * `name` is the one field every overlay carries: `handleSave` writes\n * `name: (…).trim() || null` on every save, and it is the only writer\n * that CREATES one — the analytics beacon uses `update()` precisely so\n * a stale page cannot mint a stats-only stray, and `overlays` is not\n * in `IMPORTABLE_FIELDS`, so no restore path makes one either. A null\n * name still matches an `orderBy`; an ABSENT field would not.\n *\n * ## Why this list is not paged\n *\n * The row order IS the feature: the first enabled overlay of each kind\n * is the one a visitor sees (AGL-270), and the arrows reorder by\n * swapping `order` with the ADJACENT row. A page boundary separates a\n * row from the neighbor it would swap with, so the eleventh overlay\n * could never be moved into tenth place — the same reason the console's\n * screen tree and starter bundles are ceilinged rather than paged.\n *\n * `orderBy('order')` is not an option either, and that is what makes\n * the precedence sort a client-side one: `EMPTY_BAR` and `EMPTY_POPUP`\n * carry no `order`, and only `handleMove` ever writes one, so ordering\n * the query on it would hide every overlay nobody has reordered rather\n * than mis-sorting it. The client sort holds the WHOLE window, which is\n * what makes sorting it here honest.\n */\n orderBy('name'),\n // One past the ceiling, so \"there are more overlays than these\" is a\n // fact rather than a guess from `length === 50`.\n limit(CEILING + 1),\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const truncated = (overlayDocs?.length ?? 0) > CEILING\n const overlays: OverlayDraft[] = [...(overlayDocs ?? [])]\n .slice(0, CEILING)\n .sort(compareOverlayPrecedence)\n\n const [editor, setEditor] = useState<OverlayDraft | null>(null)\n // The editor's copy is held in its stored form; the fields show it as typed.\n const copy = useOverlayCopyEditor({\n hostId,\n usesTokens:\n entitled &&\n [editor?.bar?.text, editor?.popup?.headline, editor?.popup?.body].some(\n (text) => text?.includes('{{'),\n ),\n })\n const barTextShownAsTyped = copy.shownAsTyped(editor?.bar?.text)\n const headlineShownAsTyped = copy.shownAsTyped(editor?.popup?.headline)\n const bodyShownAsTyped = copy.shownAsTyped(editor?.popup?.body)\n const patch = (partial: Partial<OverlayDraft>) =>\n setEditor((previous) => (previous ? { ...previous, ...partial } : previous))\n const patchBar = (partial: Partial<NonNullable<OverlayDraft['bar']>>) =>\n setEditor((previous) =>\n previous\n ? { ...previous, bar: { ...(previous.bar ?? {}), ...partial } }\n : previous,\n )\n const patchPopup = (partial: Partial<NonNullable<OverlayDraft['popup']>>) =>\n setEditor((previous) =>\n previous\n ? { ...previous, popup: { ...(previous.popup ?? {}), ...partial } }\n : previous,\n )\n\n const handleSave = async () => {\n if (!editor) return\n if (editor.kind === 'bar' && !editor.bar?.text?.trim()) {\n return void enqueueSnackbar('Enter the bar text', {\n variant: 'warning',\n persist: false,\n })\n }\n if (editor.kind === 'popup' && !editor.popup?.body?.trim()) {\n return void enqueueSnackbar('Enter the popup body', {\n variant: 'warning',\n persist: false,\n })\n }\n const id = editor.$id ?? createResourceUid()\n const {\n $id: _ignored,\n createdAt: _createdAt,\n updatedAt: _updatedAt,\n ...payload\n } = editor as OverlayDraft & { createdAt?: unknown; updatedAt?: unknown }\n try {\n // JSON round-trip strips undefined values Firestore rejects. The copy\n // is stored again here, for a name typed before the variables arrived.\n const cleaned = JSON.parse(\n JSON.stringify({\n ...payload,\n ...(payload.bar\n ? { bar: { ...payload.bar, text: copy.stored(payload.bar.text) } }\n : {}),\n ...(payload.popup\n ? {\n popup: {\n ...payload.popup,\n headline: copy.stored(payload.popup.headline),\n body: copy.stored(payload.popup.body),\n },\n }\n : {}),\n name: (editor.name ?? '').trim() || null,\n }),\n )\n /**\n * Refuse an EDIT whose seed the server never confirmed (AGL-1358).\n *\n * This write has no options argument, so it is a full document replace\n * of a payload copied wholesale off the listener — `merge` would not\n * have helped anyway, since every field is present. The activity log\n * is inside the guard too: a logged \"Updated overlay\" whose write never\n * happened is worse than neither, because the history then disagrees\n * with the overlay it claims to explain.\n *\n * Only the edit path. A NEW overlay starts from `EMPTY_BAR`/\n * `EMPTY_POPUP` at a fresh uid and can overwrite nothing, and the first\n * snapshot of any listener is `fromCache: true`, so guarding a create\n * would refuse a save that was never unsafe.\n *\n * The guard WRAPS the write: an early return is a shape you can keep\n * while losing the protection.\n */\n const verdict = await writeGuardedBySeed(\n {\n subject: 'overlay',\n unreadable: Boolean(editor.$id) && overlaysStatus === 'error',\n fromCache: Boolean(editor.$id) && overlaysFromCache,\n },\n async () => {\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.set(doc(firestore, 'hosts', hostId, 'overlays', id), {\n ...cleaned,\n updatedAt: Timestamp.now(),\n ...(editor.$id ? {} : { createdAt: Timestamp.now() }),\n }),\n })\n logActivity(editor.$id ? 'Updated overlay' : 'Created overlay', {\n type: 'content',\n id,\n ...(editor.name ? { name: editor.name } : {}),\n })\n },\n )\n // A refusal keeps the editor open with everything that was typed.\n if (!verdict.ok) {\n return void enqueueSnackbar(verdict.message, {\n variant: 'warning',\n persist: false,\n })\n }\n enqueueSnackbar('Overlay saved', { variant: 'success', persist: false })\n setEditor(null)\n } catch (error) {\n console.error(error)\n enqueueSnackbar('An error has occurred', { variant: 'error' })\n }\n }\n\n /*\n * What a widget in the list's zone asks for (AGL-3603): a new overlay from\n * proposed copy, cut to the overlay's limits and written SWITCHED OFF —\n * through the same site-wide write a typed overlay takes — then opened in\n * the editor, where a person reads it, changes it and turns it on.\n */\n const createOverlayDraft = async (\n kind: HostOverlay['kind'],\n proposal: OverlayCopyProposal,\n ): Promise<MarketingOverlayDraftResult> => {\n const draft = overlayDraftFromProposal(kind, proposal)\n if (!draft) {\n return {\n ok: false,\n error:\n kind === 'bar'\n ? 'There was no bar text to save.'\n : 'There was no popup body to save.',\n }\n }\n const id = createResourceUid()\n // The list orders on `name`, which every overlay carries, null or not.\n const stored = { ...draft, name: draft.name ?? null }\n try {\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.set(doc(firestore, 'hosts', hostId, 'overlays', id), {\n ...stored,\n createdAt: Timestamp.now(),\n updatedAt: Timestamp.now(),\n }),\n })\n } catch (error) {\n console.error(error)\n return { ok: false, error: 'The overlay could not be saved. Try again.' }\n }\n logActivity('Created overlay', {\n type: 'content',\n id,\n ...(draft.name ? { name: draft.name } : {}),\n })\n setEditor({ ...draft, $id: id })\n return { ok: true, id }\n }\n\n /*\n * What a widget in the editor's zone proposes (AGL-3603), put into the\n * editor's own fields unsaved. Copy is stored as the editor stores typed\n * copy, so a proposed variable name is kept the way a typed one is.\n */\n const proposeOverlayCopy = (proposal: OverlayCopyProposal) => {\n if (!editor) return\n const values = overlayCopyFromProposal(editor.kind, proposal)\n if (values.name) patch({ name: values.name })\n if (editor.kind === 'bar') {\n if (values.text) patchBar({ text: copy.stored(values.text) })\n return\n }\n patchPopup({\n ...(values.headline ? { headline: copy.stored(values.headline) } : {}),\n ...(values.body ? { body: copy.stored(values.body) } : {}),\n ...(values.ctaLabel ? { ctaLabel: values.ctaLabel } : {}),\n ...(values.trigger ? { trigger: values.trigger } : {}),\n ...(values.triggerValue !== undefined ? { triggerValue: values.triggerValue } : {}),\n })\n }\n\n const overlayRules = { limits: OVERLAY_COPY_LIMITS, triggers: OVERLAY_POPUP_TRIGGERS }\n\n const handleDelete = async (overlay: OverlayDraft) => {\n if (!overlay.$id) return\n const confirmed = await confirm({\n title: 'Delete overlay?',\n description: `\"${overlay.name ?? overlay.$id}\" stops showing immediately.`,\n confirmationText: 'Delete',\n confirmationButtonProps: { color: 'error' },\n })\n // Resolves with no value and rejects on cancel — gating on the\n // resolved value alone made this always return (AGL-950).\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.delete(doc(firestore, 'hosts', hostId, 'overlays', overlay.$id)),\n })\n enqueueSnackbar('Overlay deleted', { variant: 'success', persist: false })\n logActivity('Deleted overlay', {\n type: 'content',\n id: overlay.$id,\n ...(overlay.name ? { name: overlay.name } : {}),\n })\n }\n\n // Ordering (AGL-270): the first active overlay per kind wins, so the\n // list order is meaningful — swap `order` with the neighbor.\n const handleMove = async (index: number, direction: -1 | 1) => {\n const neighbor = overlays[index + direction]\n const current = overlays[index]\n if (!neighbor?.$id || !current?.$id) return\n const currentOrder = current.order ?? index\n const neighborOrder = neighbor.order ?? index + direction\n // One batch, so the swap lands whole — and the order decides which\n // overlay a visitor sees, so it carries the site's cache drop.\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) => {\n batch.set(\n doc(firestore, 'hosts', hostId, 'overlays', current.$id as string),\n { order: neighborOrder === currentOrder ? neighborOrder + direction : neighborOrder },\n { merge: true },\n )\n batch.set(\n doc(firestore, 'hosts', hostId, 'overlays', neighbor.$id as string),\n { order: currentOrder },\n { merge: true },\n )\n },\n }).catch(console.error)\n }\n\n const handleToggle = async (overlay: OverlayDraft) => {\n if (!overlay.$id) return\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.set(\n doc(firestore, 'hosts', hostId, 'overlays', overlay.$id),\n { enabled: overlay.enabled === false },\n { merge: true },\n ),\n })\n }\n\n const statusChip = (overlay: OverlayDraft) => {\n const status = overlayStatus(overlay, Date.now())\n if (status === 'off') return <Chip size=\"small\" label=\"Off\" />\n if (status === 'scheduled') {\n return <Chip size=\"small\" color=\"info\" label=\"Scheduled\" />\n }\n return <Chip size=\"small\" color=\"success\" label=\"Live\" />\n }\n\n return (\n <CardDisplay\n header=\"Announcement bars & popups\"\n help={pluginDocsHelp('marketingOverlays', {\n anchor: '#frequency',\n excerpt:\n 'Every bar and popup on this site, with its schedule, page ' +\n 'targeting, and how often a visitor sees it again.',\n })}\n contentGutterX\n contentGutterY\n contentBordered=\"all\"\n >\n {!entitled ? (\n <Alert severity=\"info\">\n {'Announcement bars and popups are included from the Starter ' +\n 'plan — see Billing to upgrade.'}\n </Alert>\n ) : (\n <Stack spacing={2}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Configure as many bars and popups as you need, each with its ' +\n 'own schedule and page targeting. When several match a page, ' +\n 'the first per kind shows.'}\n </Typography>\n <Stack direction=\"row\" spacing={1}>\n <Button\n size=\"small\"\n color=\"primary\"\n variant=\"contained\"\n onClick={() => setEditor({ ...EMPTY_BAR })}\n >\n {'New bar'}\n </Button>\n <Button\n size=\"small\"\n color=\"primary\"\n variant=\"contained\"\n onClick={() => setEditor({ ...EMPTY_POPUP })}\n >\n {'New popup'}\n </Button>\n {/*\n Other ways to start an overlay, from plugins (AGL-3603): the\n `hostOverlays` zone this plugin declares, drawn through the\n shell's own gated slot.\n */}\n <HostOverlaysZone\n hostId={hostId}\n createOverlayDraft={createOverlayDraft}\n {...overlayRules}\n />\n </Stack>\n {truncated ? (\n <Alert severity=\"info\">\n {`Showing ${CEILING} overlays, ordered by name. This site has ` +\n 'more than that — delete the ones you have finished with, or ' +\n 'the ones below the ceiling cannot be reordered against them.'}\n </Alert>\n ) : null}\n {overlays.length === 0 ? (\n <Stack spacing={1} sx={{ alignItems: 'flex-start' }}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No overlays yet — the single announcement bar and popup ' +\n 'below keep working as your default surfaces.'}\n </Typography>\n {/* Other ways to start an overlay, the row's zone again. */}\n <HostOverlaysZone\n hostId={hostId}\n createOverlayDraft={createOverlayDraft}\n {...overlayRules}\n />\n </Stack>\n ) : (\n <ScrollTable size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{'Name'}</TableCell>\n <TableCell>{'Kind'}</TableCell>\n <TableCell>{'Status'}</TableCell>\n <TableCell>{'Window'}</TableCell>\n <TableCell>{'Pages'}</TableCell>\n <TableCell>{'Engagement'}</TableCell>\n <TableCell align=\"right\">{'Actions'}</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {overlays.map((overlay) => (\n <TableRow key={overlay.$id}>\n <TableCell>{overlayDisplayName(overlay)}</TableCell>\n <TableCell>\n {overlay.kind === 'bar' ? 'Bar' : 'Popup'}\n </TableCell>\n <TableCell>{statusChip(overlay)}</TableCell>\n <TableCell>{overlayWindowLabel(overlay)}</TableCell>\n <TableCell>{overlayPagesLabel(overlay)}</TableCell>\n <TableCell>\n {/* Engagement (AGL-271): lifetime counters the org beacon increments. */}\n <Typography variant=\"caption\" color=\"text.secondary\">\n {overlayEngagementLabel(overlay.stats)}\n </Typography>\n </TableCell>\n <TableCell align=\"right\">\n <IconButton\n size=\"small\"\n aria-label=\"move up\"\n disabled={overlays.indexOf(overlay) === 0}\n onClick={() =>\n void handleMove(overlays.indexOf(overlay), -1)\n }\n >\n <MdiIcon path={mdiChevronUp.path} sx={{ fontSize: '1rem' }} />\n </IconButton>\n <IconButton\n size=\"small\"\n aria-label=\"move down\"\n disabled={\n overlays.indexOf(overlay) === overlays.length - 1\n }\n onClick={() =>\n void handleMove(overlays.indexOf(overlay), 1)\n }\n >\n <MdiIcon\n path={mdiChevronDown.path}\n sx={{ fontSize: '1rem' }}\n />\n </IconButton>\n <Switch\n size=\"small\"\n checked={overlay.enabled !== false}\n onChange={() => handleToggle(overlay)}\n slotProps={{ input: { 'aria-label': 'Enabled' } }}\n />\n <Button\n size=\"small\"\n color=\"primary\"\n onClick={() => setEditor({ ...overlay })}\n >\n {'Edit'}\n </Button>\n <Button\n size=\"small\"\n color=\"error\"\n onClick={() => handleDelete(overlay)}\n >\n {'Delete'}\n </Button>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </ScrollTable>\n )}\n </Stack>\n )}\n\n <Dialog\n open={Boolean(editor)}\n onClose={() => setEditor(null)}\n maxWidth=\"sm\"\n fullWidth\n >\n <DialogTitle>\n {editor?.$id\n ? 'Edit overlay'\n : editor?.kind === 'bar'\n ? 'New announcement bar'\n : 'New popup'}\n </DialogTitle>\n <DialogContent>\n {editor ? (\n <Stack spacing={2} sx={{ mt: 1 }}>\n <TextField\n size=\"small\"\n label=\"Name\"\n value={editor.name ?? ''}\n onChange={(event) => patch({ name: event.target.value })}\n helperText=\"Internal label shown in this list\"\n />\n {/*\n Copy proposed by a plugin (AGL-3603): the `overlayEditor`\n zone this plugin declares. A widget fills these fields\n unsaved; Save below is the only write.\n */}\n <OverlayEditorZone\n hostId={hostId}\n overlayId={editor.$id ?? ''}\n kind={editor.kind}\n copy={{\n name: editor.name ?? '',\n text: copy.editable(editor.bar?.text),\n headline: copy.editable(editor.popup?.headline),\n body: copy.editable(editor.popup?.body),\n ctaLabel: editor.popup?.ctaLabel ?? '',\n ctaHref: editor.popup?.ctaHref ?? '',\n trigger: editor.popup?.trigger ?? 'delay',\n triggerValue: editor.popup?.triggerValue ?? null,\n }}\n proposeValues={proposeOverlayCopy}\n {...overlayRules}\n />\n {editor.kind === 'bar' ? (\n <>\n <TextField\n size=\"small\"\n label=\"Text\"\n required\n value={copy.editable(editor.bar?.text)}\n onChange={(event) =>\n patchBar({ text: copy.stored(event.target.value) })\n }\n error={barTextShownAsTyped.length > 0}\n helperText={\n shownAsTypedHelperText(barTextShownAsTyped) ??\n OVERLAY_COPY_HELPER_TEXT\n }\n />\n <TextField\n size=\"small\"\n label=\"Link (optional)\"\n value={editor.bar?.href ?? ''}\n onChange={(event) => patchBar({ href: event.target.value })}\n />\n <Stack direction=\"row\" spacing={1}>\n <TextField\n size=\"small\"\n label=\"Background color\"\n value={editor.bar?.backgroundColor ?? ''}\n onChange={(event) =>\n patchBar({ backgroundColor: event.target.value })\n }\n />\n <TextField\n size=\"small\"\n label=\"Text color\"\n value={editor.bar?.textColor ?? ''}\n onChange={(event) =>\n patchBar({ textColor: event.target.value })\n }\n />\n </Stack>\n </>\n ) : (\n <>\n <TextField\n size=\"small\"\n label=\"Headline\"\n value={copy.editable(editor.popup?.headline)}\n onChange={(event) =>\n patchPopup({ headline: copy.stored(event.target.value) })\n }\n error={headlineShownAsTyped.length > 0}\n helperText={shownAsTypedHelperText(headlineShownAsTyped)}\n />\n <TextField\n size=\"small\"\n label=\"Body\"\n required\n multiline\n minRows={2}\n value={copy.editable(editor.popup?.body)}\n onChange={(event) =>\n patchPopup({ body: copy.stored(event.target.value) })\n }\n error={bodyShownAsTyped.length > 0}\n helperText={\n shownAsTypedHelperText(bodyShownAsTyped) ??\n OVERLAY_COPY_HELPER_TEXT\n }\n />\n <Stack direction=\"row\" spacing={1}>\n <TextField\n size=\"small\"\n label=\"CTA label\"\n value={editor.popup?.ctaLabel ?? ''}\n onChange={(event) =>\n patchPopup({ ctaLabel: event.target.value })\n }\n />\n <TextField\n size=\"small\"\n label=\"CTA link\"\n value={editor.popup?.ctaHref ?? ''}\n onChange={(event) =>\n patchPopup({ ctaHref: event.target.value })\n }\n />\n </Stack>\n <Stack direction=\"row\" spacing={1}>\n <TextField\n select\n size=\"small\"\n label=\"Trigger\"\n sx={{ minWidth: 140 }}\n value={editor.popup?.trigger ?? 'delay'}\n onChange={(event) =>\n patchPopup({\n trigger: event.target\n .value as NonNullable<\n OverlayDraft['popup']\n >['trigger'],\n })\n }\n >\n <MenuItem value=\"delay\">{'After a delay'}</MenuItem>\n <MenuItem value=\"scroll\">{'On scroll'}</MenuItem>\n <MenuItem value=\"exit\">{'On exit intent'}</MenuItem>\n </TextField>\n <TextField\n size=\"small\"\n type=\"number\"\n label={\n (editor.popup?.trigger ?? 'delay') === 'scroll'\n ? 'Scroll %'\n : 'Seconds'\n }\n value={editor.popup?.triggerValue ?? 3}\n onChange={(event) =>\n patchPopup({\n triggerValue: Number(event.target.value),\n })\n }\n />\n {/*\n `Once per session` (AGL-2174). `/product/marketing`\n sells \"per-session frequency caps\" in as many words\n and the only cap that existed was day-based — a\n visitor who dismissed a popup did not see it again\n for a week, which is a different promise. The two are\n alternatives, so the day field goes away when the\n session cap is on rather than sitting there ignored.\n */}\n <TextField\n select\n size=\"small\"\n label=\"Frequency\"\n value={editor.popup?.oncePerSession ? 'session' : 'days'}\n onChange={(event) =>\n patchPopup({\n oncePerSession: event.target.value === 'session',\n })\n }\n sx={{ minWidth: 170 }}\n >\n <MenuItem value=\"session\">\n {'Once per session'}\n </MenuItem>\n <MenuItem value=\"days\">{'Re-show after a while'}</MenuItem>\n </TextField>\n {editor.popup?.oncePerSession ? null : (\n <TextField\n size=\"small\"\n type=\"number\"\n label=\"Re-show after (days)\"\n value={editor.popup?.frequencyDays ?? 7}\n onChange={(event) =>\n patchPopup({\n frequencyDays: Number(event.target.value),\n })\n }\n />\n )}\n </Stack>\n </>\n )}\n <Stack direction=\"row\" spacing={1}>\n <TextField\n size=\"small\"\n type=\"datetime-local\"\n label=\"Show from\"\n slotProps={{ inputLabel: { shrink: true } }}\n value={toDatetimeLocal(editor.startAtMs)}\n onChange={(event) =>\n patch({ startAtMs: fromDatetimeLocal(event.target.value) })\n }\n />\n <TextField\n size=\"small\"\n type=\"datetime-local\"\n label=\"Show until\"\n slotProps={{ inputLabel: { shrink: true } }}\n value={toDatetimeLocal(editor.endAtMs)}\n onChange={(event) =>\n patch({ endAtMs: fromDatetimeLocal(event.target.value) })\n }\n />\n </Stack>\n <TextField\n size=\"small\"\n label=\"Show on pages\"\n value={(editor.pathPatterns ?? []).join(', ')}\n onChange={(event) =>\n patch({ pathPatterns: parsePatterns(event.target.value) })\n }\n helperText={\n 'Comma-separated paths; /blog/* matches a section. ' +\n 'Empty = every page.'\n }\n />\n <TextField\n size=\"small\"\n label=\"Never show on\"\n value={(editor.excludePathPatterns ?? []).join(', ')}\n onChange={(event) =>\n patch({\n excludePathPatterns: parsePatterns(event.target.value),\n })\n }\n />\n </Stack>\n ) : null}\n </DialogContent>\n <DialogActions>\n <Button color=\"inherit\" onClick={() => setEditor(null)}>\n {'Cancel'}\n </Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={handleSave}\n disabled={copy.pending(\n editor?.bar?.text,\n editor?.popup?.headline,\n editor?.popup?.body,\n )}\n >\n {'Save'}\n </Button>\n </DialogActions>\n </Dialog>\n </CardDisplay>\n )\n}\nHostOverlaysCard.displayName = 'HostOverlaysCard'\n\nexport default HostOverlaysCard\n"],"names":["checkEntitlement","createResourceUid","pluginDocsHelp","compareOverlayPrecedence","OVERLAY_COPY_LIMITS","OVERLAY_LIST_CEILING","OVERLAY_POPUP_TRIGGERS","overlayCopyFromProposal","overlayDraftFromProposal","overlayStatus","mdiChevronDown","mdiChevronUp","CardDisplay","MdiIcon","useConfirmationContext","ScrollTable","useSnackbar","Timestamp","Alert","Button","Chip","IconButton","Dialog","DialogActions","DialogContent","DialogTitle","MenuItem","Stack","Switch","TableBody","TableCell","TableHead","TableRow","TextField","Typography","collection","doc","limit","orderBy","query","useState","useFirestore","useFirestoreCollection","useHostActivityLogger","writeGuardedBySeed","useUser","writeSiteWideChange","overlayDisplayName","overlayEngagementLabel","overlayPagesLabel","overlayWindowLabel","OVERLAY_COPY_HELPER_TEXT","shownAsTypedHelperText","useOverlayCopyEditor","HostOverlaysZone","OverlayEditorZone","CEILING","EMPTY_BAR","kind","enabled","bar","dismissible","EMPTY_POPUP","popup","trigger","triggerValue","frequencyDays","toDatetimeLocal","ms","date","Date","pad","value","String","padStart","getFullYear","getMonth","getDate","getHours","getMinutes","fromDatetimeLocal","undefined","getTime","Number","isFinite","parsePatterns","split","map","pattern","trim","filter","Boolean","HostOverlaysCard","props","editor","hostId","firestore","data","user","org","enqueueSnackbar","confirm","logActivity","entitled","overlayDocs","status","overlaysStatus","fromCache","overlaysFromCache","idField","truncated","length","overlays","slice","sort","setEditor","copy","usesTokens","text","headline","body","some","includes","barTextShownAsTyped","shownAsTyped","headlineShownAsTyped","bodyShownAsTyped","patch","partial","previous","patchBar","patchPopup","handleSave","variant","persist","id","$id","_ignored","createdAt","_createdAt","updatedAt","_updatedAt","payload","cleaned","JSON","parse","stringify","stored","name","verdict","subject","unreadable","write","batch","set","now","type","ok","message","error","console","createOverlayDraft","proposal","draft","proposeOverlayCopy","values","ctaLabel","overlayRules","limits","triggers","handleDelete","overlay","confirmed","title","description","confirmationText","confirmationButtonProps","color","then","catch","delete","handleMove","index","direction","current","neighbor","currentOrder","order","neighborOrder","merge","handleToggle","statusChip","size","label","header","help","anchor","excerpt","contentGutterX","contentGutterY","contentBordered","severity","spacing","onClick","sx","alignItems","align","stats","aria-label","disabled","indexOf","path","fontSize","checked","onChange","slotProps","input","open","onClose","maxWidth","fullWidth","mt","event","target","helperText","overlayId","editable","ctaHref","proposeValues","required","href","backgroundColor","textColor","multiline","minRows","select","minWidth","oncePerSession","inputLabel","shrink","startAtMs","endAtMs","pathPatterns","join","excludePathPatterns","pending","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/host-overlays-card.component.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 {\n type AglynOrgBilling,\n checkEntitlement,\n createResourceUid,\n pluginDocsHelp,\n} from '@aglyn/aglyn'\nimport {\n compareOverlayPrecedence,\n OVERLAY_COPY_LIMITS,\n OVERLAY_LIST_CEILING,\n OVERLAY_POPUP_TRIGGERS,\n overlayCopyFromProposal,\n overlayDraftFromProposal,\n overlayStatus,\n type HostOverlay,\n type OverlayCopyProposal,\n} from '../model'\nimport { mdiChevronDown, mdiChevronUp } from '@aglyn/shared-data-mdi'\nimport {\n CardDisplay,\n MdiIcon,\n useConfirmationContext,\n} from '@aglyn/shared-ui-jsx'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { Timestamp } from '@aglyn/shared-util-timestamp'\nimport {\n Alert,\n Button,\n Chip,\n IconButton,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n MenuItem,\n Stack,\n Switch,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TextField,\n Typography,\n} from '@mui/material'\nimport { collection, doc, limit, orderBy, query } from 'firebase/firestore'\nimport { useState } from 'react'\nimport {\n useFirestore,\n useFirestoreCollection,\n useHostActivityLogger,\n writeGuardedBySeed,\n useUser,\n} from '@aglyn/tenant-feature-instance'\nimport { writeSiteWideChange } from '@aglyn/tenant-feature-instance/hooks/helpers/site-wide-change'\nimport {\n overlayDisplayName,\n overlayEngagementLabel,\n overlayPagesLabel,\n overlayWindowLabel,\n} from './overlay-labels'\nimport {\n OVERLAY_COPY_HELPER_TEXT,\n shownAsTypedHelperText,\n useOverlayCopyEditor,\n} from './use-overlay-copy-editor'\nimport {\n HostOverlaysZone,\n OverlayEditorZone,\n type MarketingOverlayDraftResult,\n} from './overlay-zones'\n\nexport interface HostOverlaysCardProps {\n hostId: string\n /** Resolved entitlement source (AGL-395). */\n org?: Partial<AglynOrgBilling>\n}\n\ntype OverlayDraft = HostOverlay & { $id?: string }\n\n/**\n * How many overlays the card reads.\n *\n * A ceiling rather than a page size — see the query, which explains why this\n * one list is deliberately not paginated. Generous on purpose: a site runs a\n * handful of banners and popups, and the number exists to bound a pathological\n * collection rather than to bound a normal one.\n */\nconst CEILING = OVERLAY_LIST_CEILING\n\nconst EMPTY_BAR: OverlayDraft = {\n kind: 'bar',\n enabled: true,\n bar: { dismissible: true },\n}\nconst EMPTY_POPUP: OverlayDraft = {\n kind: 'popup',\n enabled: true,\n popup: { trigger: 'delay', triggerValue: 3, frequencyDays: 7 },\n}\n\nfunction toDatetimeLocal(ms?: number): string {\n if (!ms) return ''\n const date = new Date(ms)\n const pad = (value: number) => String(value).padStart(2, '0')\n return (\n `${date.getFullYear()}-${pad(date.getMonth() + 1)}-` +\n `${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`\n )\n}\n\nfunction fromDatetimeLocal(value: string): number | undefined {\n if (!value) return undefined\n const ms = new Date(value).getTime()\n return Number.isFinite(ms) ? ms : undefined\n}\n\nfunction parsePatterns(value: string): string[] {\n return value\n .split(',')\n .map((pattern) => pattern.trim())\n .filter(Boolean)\n}\n\n/**\n * Marketing hub overlays manager (AGL-251): multiple announcement bars\n * and popups at `hosts/{hostId}/overlays`, each with a schedule window\n * and page targeting; the tenant render shows the first active match per\n * kind (overlay docs win over the legacy single bar/popup fields). A bar's\n * text and a popup's headline and body are edited through\n * `useOverlayCopyEditor`, like the single bar and popup cards (AGL-2885).\n */\nexport function HostOverlaysCard(props: HostOverlaysCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { org } = props\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const logActivity = useHostActivityLogger(hostId)\n const entitled = checkEntitlement(org, 'marketingOverlays')\n\n const {\n data: overlayDocs,\n status: overlaysStatus,\n /**\n * The overlay rows the editor is seeded from are unconfirmed by the\n * server (AGL-1358). Editing one spreads the whole stored row into\n * `editor` and writes it back with NO options argument at all — a full\n * document replace — so a cached seed reverts every field nobody\n * touched. Including `stats`, the lifetime impression/click/dismissal\n * counters the org beacon increments: a save here would roll live\n * engagement numbers back to whenever the snapshot was taken.\n */\n fromCache: overlaysFromCache,\n listen: overlaysListen,\n } = useFirestoreCollection<any>(\n () =>\n query(\n collection(firestore, 'hosts', hostId, 'overlays'),\n /*\n * ORDERED AND CEILINGED, deliberately not paged (AGL-2501).\n *\n * ## Why the read is ordered\n *\n * `limit(50)` alone is answered in DOCUMENT-ID order over ids from\n * `createResourceUid()`, so the window was a pseudo-random fifty that\n * the client sort below arranged into a believable precedence list.\n * `name` is the one field every overlay carries: `handleSave` writes\n * `name: (…).trim() || null` on every save, and it is the only writer\n * that CREATES one — the analytics beacon uses `update()` precisely so\n * a stale page cannot mint a stats-only stray, and `overlays` is not\n * in `IMPORTABLE_FIELDS`, so no restore path makes one either. A null\n * name still matches an `orderBy`; an ABSENT field would not.\n *\n * ## Why this list is not paged\n *\n * The row order IS the feature: the first enabled overlay of each kind\n * is the one a visitor sees (AGL-270), and the arrows reorder by\n * swapping `order` with the ADJACENT row. A page boundary separates a\n * row from the neighbor it would swap with, so the eleventh overlay\n * could never be moved into tenth place — the same reason the console's\n * screen tree and starter bundles are ceilinged rather than paged.\n *\n * `orderBy('order')` is not an option either, and that is what makes\n * the precedence sort a client-side one: `EMPTY_BAR` and `EMPTY_POPUP`\n * carry no `order`, and only `handleMove` ever writes one, so ordering\n * the query on it would hide every overlay nobody has reordered rather\n * than mis-sorting it. The client sort holds the WHOLE window, which is\n * what makes sorting it here honest.\n */\n orderBy('name'),\n // One past the ceiling, so \"there are more overlays than these\" is a\n // fact rather than a guess from `length === 50`.\n limit(CEILING + 1),\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const truncated = (overlayDocs?.length ?? 0) > CEILING\n const overlays: OverlayDraft[] = [...(overlayDocs ?? [])]\n .slice(0, CEILING)\n .sort(compareOverlayPrecedence)\n\n const [editor, setEditor] = useState<OverlayDraft | null>(null)\n // The editor's copy is held in its stored form; the fields show it as typed.\n const copy = useOverlayCopyEditor({\n hostId,\n usesTokens:\n entitled &&\n [editor?.bar?.text, editor?.popup?.headline, editor?.popup?.body].some(\n (text) => text?.includes('{{'),\n ),\n })\n const barTextShownAsTyped = copy.shownAsTyped(editor?.bar?.text)\n const headlineShownAsTyped = copy.shownAsTyped(editor?.popup?.headline)\n const bodyShownAsTyped = copy.shownAsTyped(editor?.popup?.body)\n const patch = (partial: Partial<OverlayDraft>) =>\n setEditor((previous) => (previous ? { ...previous, ...partial } : previous))\n const patchBar = (partial: Partial<NonNullable<OverlayDraft['bar']>>) =>\n setEditor((previous) =>\n previous\n ? { ...previous, bar: { ...(previous.bar ?? {}), ...partial } }\n : previous,\n )\n const patchPopup = (partial: Partial<NonNullable<OverlayDraft['popup']>>) =>\n setEditor((previous) =>\n previous\n ? { ...previous, popup: { ...(previous.popup ?? {}), ...partial } }\n : previous,\n )\n\n const handleSave = async () => {\n if (!editor) return\n if (editor.kind === 'bar' && !editor.bar?.text?.trim()) {\n return void enqueueSnackbar('Enter the bar text', {\n variant: 'warning',\n persist: false,\n })\n }\n if (editor.kind === 'popup' && !editor.popup?.body?.trim()) {\n return void enqueueSnackbar('Enter the popup body', {\n variant: 'warning',\n persist: false,\n })\n }\n const id = editor.$id ?? createResourceUid()\n const {\n $id: _ignored,\n createdAt: _createdAt,\n updatedAt: _updatedAt,\n ...payload\n } = editor as OverlayDraft & { createdAt?: unknown; updatedAt?: unknown }\n try {\n // JSON round-trip strips undefined values Firestore rejects. The copy\n // is stored again here, for a name typed before the variables arrived.\n const cleaned = JSON.parse(\n JSON.stringify({\n ...payload,\n ...(payload.bar\n ? { bar: { ...payload.bar, text: copy.stored(payload.bar.text) } }\n : {}),\n ...(payload.popup\n ? {\n popup: {\n ...payload.popup,\n headline: copy.stored(payload.popup.headline),\n body: copy.stored(payload.popup.body),\n },\n }\n : {}),\n name: (editor.name ?? '').trim() || null,\n }),\n )\n /**\n * Refuse an EDIT whose seed the server never confirmed (AGL-1358).\n *\n * This write has no options argument, so it is a full document replace\n * of a payload copied wholesale off the listener — `merge` would not\n * have helped anyway, since every field is present. The activity log\n * is inside the guard too: a logged \"Updated overlay\" whose write never\n * happened is worse than neither, because the history then disagrees\n * with the overlay it claims to explain.\n *\n * Only the edit path. A NEW overlay starts from `EMPTY_BAR`/\n * `EMPTY_POPUP` at a fresh uid and can overwrite nothing, and the first\n * snapshot of any listener is `fromCache: true`, so guarding a create\n * would refuse a save that was never unsafe.\n *\n * The guard WRAPS the write: an early return is a shape you can keep\n * while losing the protection.\n */\n const verdict = await writeGuardedBySeed(\n {\n subject: 'overlay',\n unreadable: Boolean(editor.$id) && overlaysStatus === 'error',\n fromCache: Boolean(editor.$id) && overlaysFromCache,\n listen: overlaysListen,\n },\n async () => {\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.set(doc(firestore, 'hosts', hostId, 'overlays', id), {\n ...cleaned,\n updatedAt: Timestamp.now(),\n ...(editor.$id ? {} : { createdAt: Timestamp.now() }),\n }),\n })\n logActivity(editor.$id ? 'Updated overlay' : 'Created overlay', {\n type: 'content',\n id,\n ...(editor.name ? { name: editor.name } : {}),\n })\n },\n )\n // A refusal keeps the editor open with everything that was typed.\n if (!verdict.ok) {\n return void enqueueSnackbar(verdict.message, {\n variant: 'warning',\n persist: false,\n })\n }\n enqueueSnackbar('Overlay saved', { variant: 'success', persist: false })\n setEditor(null)\n } catch (error) {\n console.error(error)\n enqueueSnackbar('An error has occurred', { variant: 'error' })\n }\n }\n\n /*\n * What a widget in the list's zone asks for (AGL-3603): a new overlay from\n * proposed copy, cut to the overlay's limits and written SWITCHED OFF —\n * through the same site-wide write a typed overlay takes — then opened in\n * the editor, where a person reads it, changes it and turns it on.\n */\n const createOverlayDraft = async (\n kind: HostOverlay['kind'],\n proposal: OverlayCopyProposal,\n ): Promise<MarketingOverlayDraftResult> => {\n const draft = overlayDraftFromProposal(kind, proposal)\n if (!draft) {\n return {\n ok: false,\n error:\n kind === 'bar'\n ? 'There was no bar text to save.'\n : 'There was no popup body to save.',\n }\n }\n const id = createResourceUid()\n // The list orders on `name`, which every overlay carries, null or not.\n const stored = { ...draft, name: draft.name ?? null }\n try {\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.set(doc(firestore, 'hosts', hostId, 'overlays', id), {\n ...stored,\n createdAt: Timestamp.now(),\n updatedAt: Timestamp.now(),\n }),\n })\n } catch (error) {\n console.error(error)\n return { ok: false, error: 'The overlay could not be saved. Try again.' }\n }\n logActivity('Created overlay', {\n type: 'content',\n id,\n ...(draft.name ? { name: draft.name } : {}),\n })\n setEditor({ ...draft, $id: id })\n return { ok: true, id }\n }\n\n /*\n * What a widget in the editor's zone proposes (AGL-3603), put into the\n * editor's own fields unsaved. Copy is stored as the editor stores typed\n * copy, so a proposed variable name is kept the way a typed one is.\n */\n const proposeOverlayCopy = (proposal: OverlayCopyProposal) => {\n if (!editor) return\n const values = overlayCopyFromProposal(editor.kind, proposal)\n if (values.name) patch({ name: values.name })\n if (editor.kind === 'bar') {\n if (values.text) patchBar({ text: copy.stored(values.text) })\n return\n }\n patchPopup({\n ...(values.headline ? { headline: copy.stored(values.headline) } : {}),\n ...(values.body ? { body: copy.stored(values.body) } : {}),\n ...(values.ctaLabel ? { ctaLabel: values.ctaLabel } : {}),\n ...(values.trigger ? { trigger: values.trigger } : {}),\n ...(values.triggerValue !== undefined ? { triggerValue: values.triggerValue } : {}),\n })\n }\n\n const overlayRules = { limits: OVERLAY_COPY_LIMITS, triggers: OVERLAY_POPUP_TRIGGERS }\n\n const handleDelete = async (overlay: OverlayDraft) => {\n if (!overlay.$id) return\n const confirmed = await confirm({\n title: 'Delete overlay?',\n description: `\"${overlay.name ?? overlay.$id}\" stops showing immediately.`,\n confirmationText: 'Delete',\n confirmationButtonProps: { color: 'error' },\n })\n // Resolves with no value and rejects on cancel — gating on the\n // resolved value alone made this always return (AGL-950).\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.delete(doc(firestore, 'hosts', hostId, 'overlays', overlay.$id)),\n })\n enqueueSnackbar('Overlay deleted', { variant: 'success', persist: false })\n logActivity('Deleted overlay', {\n type: 'content',\n id: overlay.$id,\n ...(overlay.name ? { name: overlay.name } : {}),\n })\n }\n\n // Ordering (AGL-270): the first active overlay per kind wins, so the\n // list order is meaningful — swap `order` with the neighbor.\n const handleMove = async (index: number, direction: -1 | 1) => {\n const neighbor = overlays[index + direction]\n const current = overlays[index]\n if (!neighbor?.$id || !current?.$id) return\n const currentOrder = current.order ?? index\n const neighborOrder = neighbor.order ?? index + direction\n // One batch, so the swap lands whole — and the order decides which\n // overlay a visitor sees, so it carries the site's cache drop.\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) => {\n batch.set(\n doc(firestore, 'hosts', hostId, 'overlays', current.$id as string),\n { order: neighborOrder === currentOrder ? neighborOrder + direction : neighborOrder },\n { merge: true },\n )\n batch.set(\n doc(firestore, 'hosts', hostId, 'overlays', neighbor.$id as string),\n { order: currentOrder },\n { merge: true },\n )\n },\n }).catch(console.error)\n }\n\n const handleToggle = async (overlay: OverlayDraft) => {\n if (!overlay.$id) return\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.set(\n doc(firestore, 'hosts', hostId, 'overlays', overlay.$id),\n { enabled: overlay.enabled === false },\n { merge: true },\n ),\n })\n }\n\n const statusChip = (overlay: OverlayDraft) => {\n const status = overlayStatus(overlay, Date.now())\n if (status === 'off') return <Chip size=\"small\" label=\"Off\" />\n if (status === 'scheduled') {\n return <Chip size=\"small\" color=\"info\" label=\"Scheduled\" />\n }\n return <Chip size=\"small\" color=\"success\" label=\"Live\" />\n }\n\n return (\n <CardDisplay\n header=\"Announcement bars & popups\"\n help={pluginDocsHelp('marketingOverlays', {\n anchor: '#frequency',\n excerpt:\n 'Every bar and popup on this site, with its schedule, page ' +\n 'targeting, and how often a visitor sees it again.',\n })}\n contentGutterX\n contentGutterY\n contentBordered=\"all\"\n >\n {!entitled ? (\n <Alert severity=\"info\">\n {'Announcement bars and popups are included from the Starter ' +\n 'plan — see Billing to upgrade.'}\n </Alert>\n ) : (\n <Stack spacing={2}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Configure as many bars and popups as you need, each with its ' +\n 'own schedule and page targeting. When several match a page, ' +\n 'the first per kind shows.'}\n </Typography>\n <Stack direction=\"row\" spacing={1}>\n <Button\n size=\"small\"\n color=\"primary\"\n variant=\"contained\"\n onClick={() => setEditor({ ...EMPTY_BAR })}\n >\n {'New bar'}\n </Button>\n <Button\n size=\"small\"\n color=\"primary\"\n variant=\"contained\"\n onClick={() => setEditor({ ...EMPTY_POPUP })}\n >\n {'New popup'}\n </Button>\n {/*\n Other ways to start an overlay, from plugins (AGL-3603): the\n `hostOverlays` zone this plugin declares, drawn through the\n shell's own gated slot.\n */}\n <HostOverlaysZone\n hostId={hostId}\n createOverlayDraft={createOverlayDraft}\n {...overlayRules}\n />\n </Stack>\n {truncated ? (\n <Alert severity=\"info\">\n {`Showing ${CEILING} overlays, ordered by name. This site has ` +\n 'more than that — delete the ones you have finished with, or ' +\n 'the ones below the ceiling cannot be reordered against them.'}\n </Alert>\n ) : null}\n {overlays.length === 0 ? (\n <Stack spacing={1} sx={{ alignItems: 'flex-start' }}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No overlays yet — the single announcement bar and popup ' +\n 'below keep working as your default surfaces.'}\n </Typography>\n {/* Other ways to start an overlay, the row's zone again. */}\n <HostOverlaysZone\n hostId={hostId}\n createOverlayDraft={createOverlayDraft}\n {...overlayRules}\n />\n </Stack>\n ) : (\n <ScrollTable size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{'Name'}</TableCell>\n <TableCell>{'Kind'}</TableCell>\n <TableCell>{'Status'}</TableCell>\n <TableCell>{'Window'}</TableCell>\n <TableCell>{'Pages'}</TableCell>\n <TableCell>{'Engagement'}</TableCell>\n <TableCell align=\"right\">{'Actions'}</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {overlays.map((overlay) => (\n <TableRow key={overlay.$id}>\n <TableCell>{overlayDisplayName(overlay)}</TableCell>\n <TableCell>\n {overlay.kind === 'bar' ? 'Bar' : 'Popup'}\n </TableCell>\n <TableCell>{statusChip(overlay)}</TableCell>\n <TableCell>{overlayWindowLabel(overlay)}</TableCell>\n <TableCell>{overlayPagesLabel(overlay)}</TableCell>\n <TableCell>\n {/* Engagement (AGL-271): lifetime counters the org beacon increments. */}\n <Typography variant=\"caption\" color=\"text.secondary\">\n {overlayEngagementLabel(overlay.stats)}\n </Typography>\n </TableCell>\n <TableCell align=\"right\">\n <IconButton\n size=\"small\"\n aria-label=\"move up\"\n disabled={overlays.indexOf(overlay) === 0}\n onClick={() =>\n void handleMove(overlays.indexOf(overlay), -1)\n }\n >\n <MdiIcon path={mdiChevronUp.path} sx={{ fontSize: '1rem' }} />\n </IconButton>\n <IconButton\n size=\"small\"\n aria-label=\"move down\"\n disabled={\n overlays.indexOf(overlay) === overlays.length - 1\n }\n onClick={() =>\n void handleMove(overlays.indexOf(overlay), 1)\n }\n >\n <MdiIcon\n path={mdiChevronDown.path}\n sx={{ fontSize: '1rem' }}\n />\n </IconButton>\n <Switch\n size=\"small\"\n checked={overlay.enabled !== false}\n onChange={() => handleToggle(overlay)}\n slotProps={{ input: { 'aria-label': 'Enabled' } }}\n />\n <Button\n size=\"small\"\n color=\"primary\"\n onClick={() => setEditor({ ...overlay })}\n >\n {'Edit'}\n </Button>\n <Button\n size=\"small\"\n color=\"error\"\n onClick={() => handleDelete(overlay)}\n >\n {'Delete'}\n </Button>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </ScrollTable>\n )}\n </Stack>\n )}\n\n <Dialog\n open={Boolean(editor)}\n onClose={() => setEditor(null)}\n maxWidth=\"sm\"\n fullWidth\n >\n <DialogTitle>\n {editor?.$id\n ? 'Edit overlay'\n : editor?.kind === 'bar'\n ? 'New announcement bar'\n : 'New popup'}\n </DialogTitle>\n <DialogContent>\n {editor ? (\n <Stack spacing={2} sx={{ mt: 1 }}>\n <TextField\n size=\"small\"\n label=\"Name\"\n value={editor.name ?? ''}\n onChange={(event) => patch({ name: event.target.value })}\n helperText=\"Internal label shown in this list\"\n />\n {/*\n Copy proposed by a plugin (AGL-3603): the `overlayEditor`\n zone this plugin declares. A widget fills these fields\n unsaved; Save below is the only write.\n */}\n <OverlayEditorZone\n hostId={hostId}\n overlayId={editor.$id ?? ''}\n kind={editor.kind}\n copy={{\n name: editor.name ?? '',\n text: copy.editable(editor.bar?.text),\n headline: copy.editable(editor.popup?.headline),\n body: copy.editable(editor.popup?.body),\n ctaLabel: editor.popup?.ctaLabel ?? '',\n ctaHref: editor.popup?.ctaHref ?? '',\n trigger: editor.popup?.trigger ?? 'delay',\n triggerValue: editor.popup?.triggerValue ?? null,\n }}\n proposeValues={proposeOverlayCopy}\n {...overlayRules}\n />\n {editor.kind === 'bar' ? (\n <>\n <TextField\n size=\"small\"\n label=\"Text\"\n required\n value={copy.editable(editor.bar?.text)}\n onChange={(event) =>\n patchBar({ text: copy.stored(event.target.value) })\n }\n error={barTextShownAsTyped.length > 0}\n helperText={\n shownAsTypedHelperText(barTextShownAsTyped) ??\n OVERLAY_COPY_HELPER_TEXT\n }\n />\n <TextField\n size=\"small\"\n label=\"Link (optional)\"\n value={editor.bar?.href ?? ''}\n onChange={(event) => patchBar({ href: event.target.value })}\n />\n <Stack direction=\"row\" spacing={1}>\n <TextField\n size=\"small\"\n label=\"Background color\"\n value={editor.bar?.backgroundColor ?? ''}\n onChange={(event) =>\n patchBar({ backgroundColor: event.target.value })\n }\n />\n <TextField\n size=\"small\"\n label=\"Text color\"\n value={editor.bar?.textColor ?? ''}\n onChange={(event) =>\n patchBar({ textColor: event.target.value })\n }\n />\n </Stack>\n </>\n ) : (\n <>\n <TextField\n size=\"small\"\n label=\"Headline\"\n value={copy.editable(editor.popup?.headline)}\n onChange={(event) =>\n patchPopup({ headline: copy.stored(event.target.value) })\n }\n error={headlineShownAsTyped.length > 0}\n helperText={shownAsTypedHelperText(headlineShownAsTyped)}\n />\n <TextField\n size=\"small\"\n label=\"Body\"\n required\n multiline\n minRows={2}\n value={copy.editable(editor.popup?.body)}\n onChange={(event) =>\n patchPopup({ body: copy.stored(event.target.value) })\n }\n error={bodyShownAsTyped.length > 0}\n helperText={\n shownAsTypedHelperText(bodyShownAsTyped) ??\n OVERLAY_COPY_HELPER_TEXT\n }\n />\n <Stack direction=\"row\" spacing={1}>\n <TextField\n size=\"small\"\n label=\"CTA label\"\n value={editor.popup?.ctaLabel ?? ''}\n onChange={(event) =>\n patchPopup({ ctaLabel: event.target.value })\n }\n />\n <TextField\n size=\"small\"\n label=\"CTA link\"\n value={editor.popup?.ctaHref ?? ''}\n onChange={(event) =>\n patchPopup({ ctaHref: event.target.value })\n }\n />\n </Stack>\n <Stack direction=\"row\" spacing={1}>\n <TextField\n select\n size=\"small\"\n label=\"Trigger\"\n sx={{ minWidth: 140 }}\n value={editor.popup?.trigger ?? 'delay'}\n onChange={(event) =>\n patchPopup({\n trigger: event.target\n .value as NonNullable<\n OverlayDraft['popup']\n >['trigger'],\n })\n }\n >\n <MenuItem value=\"delay\">{'After a delay'}</MenuItem>\n <MenuItem value=\"scroll\">{'On scroll'}</MenuItem>\n <MenuItem value=\"exit\">{'On exit intent'}</MenuItem>\n </TextField>\n <TextField\n size=\"small\"\n type=\"number\"\n label={\n (editor.popup?.trigger ?? 'delay') === 'scroll'\n ? 'Scroll %'\n : 'Seconds'\n }\n value={editor.popup?.triggerValue ?? 3}\n onChange={(event) =>\n patchPopup({\n triggerValue: Number(event.target.value),\n })\n }\n />\n {/*\n `Once per session` (AGL-2174). `/product/marketing`\n sells \"per-session frequency caps\" in as many words\n and the only cap that existed was day-based — a\n visitor who dismissed a popup did not see it again\n for a week, which is a different promise. The two are\n alternatives, so the day field goes away when the\n session cap is on rather than sitting there ignored.\n */}\n <TextField\n select\n size=\"small\"\n label=\"Frequency\"\n value={editor.popup?.oncePerSession ? 'session' : 'days'}\n onChange={(event) =>\n patchPopup({\n oncePerSession: event.target.value === 'session',\n })\n }\n sx={{ minWidth: 170 }}\n >\n <MenuItem value=\"session\">\n {'Once per session'}\n </MenuItem>\n <MenuItem value=\"days\">{'Re-show after a while'}</MenuItem>\n </TextField>\n {editor.popup?.oncePerSession ? null : (\n <TextField\n size=\"small\"\n type=\"number\"\n label=\"Re-show after (days)\"\n value={editor.popup?.frequencyDays ?? 7}\n onChange={(event) =>\n patchPopup({\n frequencyDays: Number(event.target.value),\n })\n }\n />\n )}\n </Stack>\n </>\n )}\n <Stack direction=\"row\" spacing={1}>\n <TextField\n size=\"small\"\n type=\"datetime-local\"\n label=\"Show from\"\n slotProps={{ inputLabel: { shrink: true } }}\n value={toDatetimeLocal(editor.startAtMs)}\n onChange={(event) =>\n patch({ startAtMs: fromDatetimeLocal(event.target.value) })\n }\n />\n <TextField\n size=\"small\"\n type=\"datetime-local\"\n label=\"Show until\"\n slotProps={{ inputLabel: { shrink: true } }}\n value={toDatetimeLocal(editor.endAtMs)}\n onChange={(event) =>\n patch({ endAtMs: fromDatetimeLocal(event.target.value) })\n }\n />\n </Stack>\n <TextField\n size=\"small\"\n label=\"Show on pages\"\n value={(editor.pathPatterns ?? []).join(', ')}\n onChange={(event) =>\n patch({ pathPatterns: parsePatterns(event.target.value) })\n }\n helperText={\n 'Comma-separated paths; /blog/* matches a section. ' +\n 'Empty = every page.'\n }\n />\n <TextField\n size=\"small\"\n label=\"Never show on\"\n value={(editor.excludePathPatterns ?? []).join(', ')}\n onChange={(event) =>\n patch({\n excludePathPatterns: parsePatterns(event.target.value),\n })\n }\n />\n </Stack>\n ) : null}\n </DialogContent>\n <DialogActions>\n <Button color=\"inherit\" onClick={() => setEditor(null)}>\n {'Cancel'}\n </Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={handleSave}\n disabled={copy.pending(\n editor?.bar?.text,\n editor?.popup?.headline,\n editor?.popup?.body,\n )}\n >\n {'Save'}\n </Button>\n </DialogActions>\n </Dialog>\n </CardDisplay>\n )\n}\nHostOverlaysCard.displayName = 'HostOverlaysCard'\n\nexport default HostOverlaysCard\n"],"names":["checkEntitlement","createResourceUid","pluginDocsHelp","compareOverlayPrecedence","OVERLAY_COPY_LIMITS","OVERLAY_LIST_CEILING","OVERLAY_POPUP_TRIGGERS","overlayCopyFromProposal","overlayDraftFromProposal","overlayStatus","mdiChevronDown","mdiChevronUp","CardDisplay","MdiIcon","useConfirmationContext","ScrollTable","useSnackbar","Timestamp","Alert","Button","Chip","IconButton","Dialog","DialogActions","DialogContent","DialogTitle","MenuItem","Stack","Switch","TableBody","TableCell","TableHead","TableRow","TextField","Typography","collection","doc","limit","orderBy","query","useState","useFirestore","useFirestoreCollection","useHostActivityLogger","writeGuardedBySeed","useUser","writeSiteWideChange","overlayDisplayName","overlayEngagementLabel","overlayPagesLabel","overlayWindowLabel","OVERLAY_COPY_HELPER_TEXT","shownAsTypedHelperText","useOverlayCopyEditor","HostOverlaysZone","OverlayEditorZone","CEILING","EMPTY_BAR","kind","enabled","bar","dismissible","EMPTY_POPUP","popup","trigger","triggerValue","frequencyDays","toDatetimeLocal","ms","date","Date","pad","value","String","padStart","getFullYear","getMonth","getDate","getHours","getMinutes","fromDatetimeLocal","undefined","getTime","Number","isFinite","parsePatterns","split","map","pattern","trim","filter","Boolean","HostOverlaysCard","props","editor","hostId","firestore","data","user","org","enqueueSnackbar","confirm","logActivity","entitled","overlayDocs","status","overlaysStatus","fromCache","overlaysFromCache","listen","overlaysListen","idField","truncated","length","overlays","slice","sort","setEditor","copy","usesTokens","text","headline","body","some","includes","barTextShownAsTyped","shownAsTyped","headlineShownAsTyped","bodyShownAsTyped","patch","partial","previous","patchBar","patchPopup","handleSave","variant","persist","id","$id","_ignored","createdAt","_createdAt","updatedAt","_updatedAt","payload","cleaned","JSON","parse","stringify","stored","name","verdict","subject","unreadable","write","batch","set","now","type","ok","message","error","console","createOverlayDraft","proposal","draft","proposeOverlayCopy","values","ctaLabel","overlayRules","limits","triggers","handleDelete","overlay","confirmed","title","description","confirmationText","confirmationButtonProps","color","then","catch","delete","handleMove","index","direction","current","neighbor","currentOrder","order","neighborOrder","merge","handleToggle","statusChip","size","label","header","help","anchor","excerpt","contentGutterX","contentGutterY","contentBordered","severity","spacing","onClick","sx","alignItems","align","stats","aria-label","disabled","indexOf","path","fontSize","checked","onChange","slotProps","input","open","onClose","maxWidth","fullWidth","mt","event","target","helperText","overlayId","editable","ctaHref","proposeValues","required","href","backgroundColor","textColor","multiline","minRows","select","minWidth","oncePerSession","inputLabel","shrink","startAtMs","endAtMs","pathPatterns","join","excludePathPatterns","pending","displayName"],"mappings":"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"}
@@ -69,7 +69,7 @@ const fromLocalInput = (value)=>value ? new Date(value).getTime() : undefined;
69
69
  * fields, never the popup's own — and every field of it comes off this
70
70
  * seed. Against a cached read, retyping the headline restores yesterday's
71
71
  * body, link, schedule and enabled flag on a live site.
72
- */ fromCache: hostFromCache } = useFirestoreDoc(()=>doc(firestore, 'hosts', hostId), [
72
+ */ fromCache: hostFromCache, listen: hostListen } = useFirestoreDoc(()=>doc(firestore, 'hosts', hostId), [
73
73
  firestore,
74
74
  hostId
75
75
  ], {
@@ -130,7 +130,8 @@ const fromLocalInput = (value)=>value ? new Date(value).getTime() : undefined;
130
130
  */ const verdict = await writeGuardedBySeed({
131
131
  subject: 'popup',
132
132
  unreadable: hostStatus === 'error',
133
- fromCache: hostFromCache
133
+ fromCache: hostFromCache,
134
+ listen: hostListen
134
135
  }, async ()=>{
135
136
  await writeSiteWideChange({
136
137
  firestore,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/popup-card.component.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 {\n type AglynOrgBilling,\n checkEntitlement,\n type HostPopup,\n inheritedMediaAlt,\n MEDIA_ALT_MAX_LENGTH,\n pluginDocsHelp,\n useMediaPicker,\n} from '@aglyn/aglyn'\nimport { CardDisplay, useLoading } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n Button,\n MenuItem,\n Stack,\n Switch,\n TextField,\n Typography,\n} from '@mui/material'\nimport { doc } from 'firebase/firestore'\nimport { useEffect, useState } from 'react'\nimport {\n useFirestore,\n useFirestoreDoc,\n useHostActivityLogger,\n useUser,\n writeGuardedBySeed,\n} from '@aglyn/tenant-feature-instance'\nimport { writeSiteWideChange } from '@aglyn/tenant-feature-instance/hooks/helpers/site-wide-change'\nimport OverlayStatsRow from './overlay-stats-row.component'\nimport {\n OVERLAY_COPY_HELPER_TEXT,\n shownAsTypedHelperText,\n useOverlayCopyEditor,\n} from './use-overlay-copy-editor'\n\nexport interface PopupCardProps {\n hostId: string\n /** Resolved entitlement source (AGL-395). */\n org?: Partial<AglynOrgBilling>\n}\n\nconst TRIGGERS: Array<{ value: NonNullable<HostPopup['trigger']>; label: string }> = [\n { value: 'delay', label: 'After a delay (seconds)' },\n { value: 'scroll', label: 'At scroll depth (%)' },\n { value: 'exit', label: 'On exit intent (desktop)' },\n]\n\nconst toLocalInput = (ms?: number) => {\n if (!ms) return ''\n const date = new Date(ms - new Date().getTimezoneOffset() * 60000)\n return date.toISOString().slice(0, 16)\n}\nconst fromLocalInput = (value: string) =>\n value ? new Date(value).getTime() : undefined\n\n/**\n * Promotional popup editor (AGL-196): one popup per host, persisted on the\n * host doc; the tenant render handles triggers, scheduling, and\n * frequency capping. marketingOverlays-gated (Starter+). The headline and\n * body are edited through `useOverlayCopyEditor`, like the bar's text\n * (AGL-2885).\n */\nexport function PopupCard(props: PopupCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { org } = props\n const { enqueueSnackbar } = useSnackbar()\n const { queueLoading } = useLoading()\n const logActivity = useHostActivityLogger(hostId)\n const {\n data: host,\n status: hostStatus,\n /**\n * The host doc this form is seeded from is unconfirmed by the server\n * (AGL-1358). The save writes the whole `popup` map — a nested map value\n * REPLACES atomically, so `updateDoc` protects only the host doc's other\n * fields, never the popup's own — and every field of it comes off this\n * seed. Against a cached read, retyping the headline restores yesterday's\n * body, link, schedule and enabled flag on a live site.\n */\n fromCache: hostFromCache,\n } = useFirestoreDoc<any>(\n () => doc(firestore, 'hosts', hostId),\n [firestore, hostId],\n { idField: '$id' },\n )\n const entitled = checkEntitlement(org, 'marketingOverlays')\n\n const saved = (host?.popup ?? {}) as HostPopup\n const [draft, setDraft] = useState<HostPopup>(saved)\n const savedKey = JSON.stringify(saved)\n useEffect(() => {\n setDraft(JSON.parse(savedKey))\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [savedKey])\n\n // The console media browser is provided by the shell (AGL-395).\n const { pickMedia } = useMediaPicker()\n // The headline and body are held in their stored form; the fields show them\n // as typed.\n const copy = useOverlayCopyEditor({\n hostId,\n host,\n usesTokens:\n entitled &&\n [saved.headline, saved.body, draft.headline, draft.body].some((text) =>\n text?.includes('{{'),\n ),\n })\n // Compared as it would be stored, so a name typed before the variables\n // arrived still counts as the change it becomes on save.\n const dirty =\n JSON.stringify({\n ...draft,\n headline: copy.stored(draft.headline),\n body: copy.stored(draft.body),\n }) !== savedKey\n const patch = (partial: Partial<HostPopup>) =>\n setDraft((previous) => ({ ...previous, ...partial }))\n const headlineShownAsTyped = copy.shownAsTyped(draft.headline)\n const bodyShownAsTyped = copy.shownAsTyped(draft.body)\n\n const handleSave = async () => {\n const dequeue = queueLoading()\n try {\n /**\n * Refuse a save whose seed the server never confirmed (AGL-1358).\n *\n * There is no create path to exempt here, and that is the point: unlike\n * a new row at a fresh uid, this writes a FIXED document path, so a\n * blank `draft` — what `host?.popup ?? {}` yields when the cache has\n * never seen a popup — is not harmless. It replaces whatever popup the\n * server really holds with an empty one. Both the populated and the\n * empty seed are guarded for the same reason.\n *\n * The activity log rides inside the guard: a logged \"Updated popup\"\n * whose write never happened leaves the history disagreeing with the\n * popup it claims to explain.\n *\n * The guard WRAPS the write — an early return is a shape you can keep\n * while losing the protection.\n */\n const verdict = await writeGuardedBySeed(\n {\n subject: 'popup',\n unreadable: hostStatus === 'error',\n fromCache: hostFromCache,\n },\n async () => {\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.update(doc(firestore, 'hosts', hostId), {\n popup: {\n enabled: Boolean(draft.enabled),\n headline: copy.storedWithin(draft.headline, 120),\n body: copy.storedWithin(draft.body, 1000),\n imageUrl: (draft.imageUrl ?? '').trim(),\n // AGL-1896. Capped at the same length the media library saves\n // alt through, so a value defaulted from an asset and a value\n // typed here cannot diverge.\n imageAlt: (draft.imageAlt ?? '')\n .trim()\n .slice(0, MEDIA_ALT_MAX_LENGTH),\n ctaLabel: (draft.ctaLabel ?? '').slice(0, 60),\n ctaHref: (draft.ctaHref ?? '').trim(),\n collectEmail: Boolean(draft.collectEmail),\n trigger: draft.trigger ?? 'delay',\n triggerValue: Math.max(0, Number(draft.triggerValue ?? 3)),\n frequencyDays: Math.max(1, Number(draft.frequencyDays ?? 7)),\n startAtMs: draft.startAtMs ?? null,\n endAtMs: draft.endAtMs ?? null,\n },\n }),\n })\n logActivity('Updated popup', { type: 'host', id: hostId })\n },\n )\n // A refusal leaves every typed value on screen. A save that silently\n // does nothing sends the user back to retype a form that will be\n // refused again just as quietly.\n if (!verdict.ok) {\n return void enqueueSnackbar(verdict.message, {\n variant: 'warning',\n persist: false,\n })\n }\n enqueueSnackbar('Popup saved', { variant: 'success', persist: false })\n } catch (error) {\n console.error(error)\n enqueueSnackbar('An error has occurred', {\n variant: 'error',\n allowDuplicate: true,\n })\n } finally {\n dequeue()\n }\n }\n\n return (\n <CardDisplay\n header=\"Promotional popup\"\n help={pluginDocsHelp('marketingOverlays', {\n anchor: '#promotional-popups',\n excerpt:\n 'The site-wide default popup. Its only frequency cap is a number ' +\n 'of days — the once-per-session choice is on the overlays card.',\n })}\n contentGutterX\n contentGutterY\n >\n {!entitled ? (\n <Alert severity=\"info\">\n {'Popups are included from the Starter plan — see Billing to ' +\n 'upgrade.'}\n </Alert>\n ) : (\n <Stack spacing={2}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Switch\n checked={Boolean(draft.enabled)}\n onChange={(event) => patch({ enabled: event.target.checked })}\n />\n <Typography variant=\"body2\">\n {draft.enabled ? 'Active on your published site' : 'Off'}\n </Typography>\n </Stack>\n <TextField\n label=\"Headline\"\n size=\"small\"\n value={copy.editable(draft.headline)}\n onChange={(event) =>\n patch({ headline: copy.stored(event.target.value) })\n }\n error={headlineShownAsTyped.length > 0}\n helperText={shownAsTypedHelperText(headlineShownAsTyped)}\n fullWidth\n />\n <TextField\n label=\"Body\"\n size=\"small\"\n value={copy.editable(draft.body)}\n onChange={(event) =>\n patch({ body: copy.stored(event.target.value) })\n }\n error={bodyShownAsTyped.length > 0}\n helperText={\n shownAsTypedHelperText(bodyShownAsTyped) ?? OVERLAY_COPY_HELPER_TEXT\n }\n multiline\n minRows={2}\n fullWidth\n />\n <Stack useFlexGap direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', rowGap: 1 }}>\n <TextField\n label=\"Image URL (optional)\"\n size=\"small\"\n placeholder=\"Pick from the media library\"\n value={draft.imageUrl ?? ''}\n onChange={(event) => patch({ imageUrl: event.target.value })}\n sx={{ flex: 1, minWidth: 220 }}\n />\n <Button\n size=\"small\"\n onClick={() =>\n void (async () => {\n const media = await pickMedia?.()\n if (!media) return\n // Alt defaults from the asset (AGL-1896), never clobbering\n // one the author already typed for THIS popup. Undefined\n // when the asset has no alt or the field is already\n // filled, and the spread then leaves `imageAlt` alone.\n const inheritedAlt = inheritedMediaAlt({\n placementAlt: draft.imageAlt,\n assetAlt: media.alt,\n })\n patch({\n imageUrl: media.url,\n ...(inheritedAlt ? { imageAlt: inheritedAlt } : {}),\n })\n })()\n }\n >\n {'Browse media'}\n </Button>\n </Stack>\n {/* AGL-1896: the popup image rendered `alt=\"\"` with no field\n behind it. Offered whenever there is an image to describe. */}\n {(draft.imageUrl ?? '').trim() ? (\n <TextField\n label=\"Image alt text\"\n size=\"small\"\n value={draft.imageAlt ?? ''}\n onChange={(event) => patch({ imageAlt: event.target.value })}\n helperText={\n 'Describes the image for screen readers. Filled in from the ' +\n 'media library when you pick an image that has alt text. ' +\n 'Leave empty only if the image is purely decorative.'\n }\n />\n ) : null}\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Switch\n checked={Boolean(draft.collectEmail)}\n onChange={(event) =>\n patch({ collectEmail: event.target.checked })\n }\n />\n <Typography variant=\"body2\">\n {'Collect emails (submissions land in your Inbox and Contacts)'}\n </Typography>\n </Stack>\n <Stack useFlexGap direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', rowGap: 1 }}>\n <TextField\n label=\"Button label\"\n size=\"small\"\n value={draft.ctaLabel ?? ''}\n onChange={(event) => patch({ ctaLabel: event.target.value })}\n sx={{ width: 160 }}\n />\n <TextField\n label=\"Button link\"\n size=\"small\"\n placeholder=\"/sale or https://…\"\n value={draft.ctaHref ?? ''}\n onChange={(event) => patch({ ctaHref: event.target.value })}\n sx={{ flex: 1, minWidth: 180 }}\n />\n </Stack>\n <Stack useFlexGap direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', rowGap: 1 }}>\n <TextField\n select\n label=\"Show\"\n size=\"small\"\n value={draft.trigger ?? 'delay'}\n onChange={(event) =>\n patch({ trigger: event.target.value as HostPopup['trigger'] })\n }\n sx={{ minWidth: 200 }}\n >\n {TRIGGERS.map((option) => (\n <MenuItem key={option.value} value={option.value}>\n {option.label}\n </MenuItem>\n ))}\n </TextField>\n {draft.trigger !== 'exit' ? (\n <TextField\n label={draft.trigger === 'scroll' ? 'Depth %' : 'Seconds'}\n size=\"small\"\n type=\"number\"\n value={draft.triggerValue ?? 3}\n onChange={(event) =>\n patch({ triggerValue: Number(event.target.value) })\n }\n sx={{ width: 110 }}\n />\n ) : null}\n <TextField\n label=\"Re-show after (days)\"\n size=\"small\"\n type=\"number\"\n value={draft.frequencyDays ?? 7}\n onChange={(event) =>\n patch({ frequencyDays: Number(event.target.value) })\n }\n sx={{ width: 160 }}\n />\n </Stack>\n <Stack useFlexGap direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', rowGap: 1 }}>\n <TextField\n label=\"Start (optional)\"\n size=\"small\"\n type=\"datetime-local\"\n value={toLocalInput(draft.startAtMs)}\n onChange={(event) =>\n patch({ startAtMs: fromLocalInput(event.target.value) })\n }\n slotProps={{ inputLabel: { shrink: true } }}\n sx={{ minWidth: 200 }}\n />\n <TextField\n label=\"End (optional)\"\n size=\"small\"\n type=\"datetime-local\"\n value={toLocalInput(draft.endAtMs)}\n onChange={(event) =>\n patch({ endAtMs: fromLocalInput(event.target.value) })\n }\n slotProps={{ inputLabel: { shrink: true } }}\n sx={{ minWidth: 200 }}\n />\n </Stack>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={\n !dirty ||\n (Boolean(draft.enabled) && !draft.body?.trim()) ||\n copy.pending(draft.headline, draft.body)\n }\n onClick={handleSave}\n sx={{ alignSelf: 'flex-start' }}\n >\n {'Save'}\n </Button>\n <OverlayStatsRow\n hostId={hostId}\n impressionKey=\"popupImpression\"\n actionKey=\"popupClick\"\n actionLabel=\"clicks\"\n />\n </Stack>\n )}\n </CardDisplay>\n )\n}\nPopupCard.displayName = 'PopupCard'\n\nexport default PopupCard\n"],"names":["checkEntitlement","inheritedMediaAlt","MEDIA_ALT_MAX_LENGTH","pluginDocsHelp","useMediaPicker","CardDisplay","useLoading","useSnackbar","Alert","Button","MenuItem","Stack","Switch","TextField","Typography","doc","useEffect","useState","useFirestore","useFirestoreDoc","useHostActivityLogger","useUser","writeGuardedBySeed","writeSiteWideChange","OverlayStatsRow","OVERLAY_COPY_HELPER_TEXT","shownAsTypedHelperText","useOverlayCopyEditor","TRIGGERS","value","label","toLocalInput","ms","date","Date","getTimezoneOffset","toISOString","slice","fromLocalInput","getTime","undefined","PopupCard","props","draft","hostId","firestore","data","user","org","enqueueSnackbar","queueLoading","logActivity","host","status","hostStatus","fromCache","hostFromCache","idField","entitled","saved","popup","setDraft","savedKey","JSON","stringify","parse","pickMedia","copy","usesTokens","headline","body","some","text","includes","dirty","stored","patch","partial","previous","headlineShownAsTyped","shownAsTyped","bodyShownAsTyped","handleSave","dequeue","verdict","subject","unreadable","write","batch","update","enabled","Boolean","storedWithin","imageUrl","trim","imageAlt","ctaLabel","ctaHref","collectEmail","trigger","triggerValue","Math","max","Number","frequencyDays","startAtMs","endAtMs","type","id","ok","message","variant","persist","error","console","allowDuplicate","header","help","anchor","excerpt","contentGutterX","contentGutterY","severity","spacing","direction","sx","alignItems","checked","onChange","event","target","size","editable","length","helperText","fullWidth","multiline","minRows","useFlexGap","flexWrap","rowGap","placeholder","flex","minWidth","onClick","media","inheritedAlt","placementAlt","assetAlt","alt","url","width","select","map","option","slotProps","inputLabel","shrink","color","disabled","pending","alignSelf","impressionKey","actionKey","actionLabel","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/popup-card.component.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 {\n type AglynOrgBilling,\n checkEntitlement,\n type HostPopup,\n inheritedMediaAlt,\n MEDIA_ALT_MAX_LENGTH,\n pluginDocsHelp,\n useMediaPicker,\n} from '@aglyn/aglyn'\nimport { CardDisplay, useLoading } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n Button,\n MenuItem,\n Stack,\n Switch,\n TextField,\n Typography,\n} from '@mui/material'\nimport { doc } from 'firebase/firestore'\nimport { useEffect, useState } from 'react'\nimport {\n useFirestore,\n useFirestoreDoc,\n useHostActivityLogger,\n useUser,\n writeGuardedBySeed,\n} from '@aglyn/tenant-feature-instance'\nimport { writeSiteWideChange } from '@aglyn/tenant-feature-instance/hooks/helpers/site-wide-change'\nimport OverlayStatsRow from './overlay-stats-row.component'\nimport {\n OVERLAY_COPY_HELPER_TEXT,\n shownAsTypedHelperText,\n useOverlayCopyEditor,\n} from './use-overlay-copy-editor'\n\nexport interface PopupCardProps {\n hostId: string\n /** Resolved entitlement source (AGL-395). */\n org?: Partial<AglynOrgBilling>\n}\n\nconst TRIGGERS: Array<{ value: NonNullable<HostPopup['trigger']>; label: string }> = [\n { value: 'delay', label: 'After a delay (seconds)' },\n { value: 'scroll', label: 'At scroll depth (%)' },\n { value: 'exit', label: 'On exit intent (desktop)' },\n]\n\nconst toLocalInput = (ms?: number) => {\n if (!ms) return ''\n const date = new Date(ms - new Date().getTimezoneOffset() * 60000)\n return date.toISOString().slice(0, 16)\n}\nconst fromLocalInput = (value: string) =>\n value ? new Date(value).getTime() : undefined\n\n/**\n * Promotional popup editor (AGL-196): one popup per host, persisted on the\n * host doc; the tenant render handles triggers, scheduling, and\n * frequency capping. marketingOverlays-gated (Starter+). The headline and\n * body are edited through `useOverlayCopyEditor`, like the bar's text\n * (AGL-2885).\n */\nexport function PopupCard(props: PopupCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { org } = props\n const { enqueueSnackbar } = useSnackbar()\n const { queueLoading } = useLoading()\n const logActivity = useHostActivityLogger(hostId)\n const {\n data: host,\n status: hostStatus,\n /**\n * The host doc this form is seeded from is unconfirmed by the server\n * (AGL-1358). The save writes the whole `popup` map — a nested map value\n * REPLACES atomically, so `updateDoc` protects only the host doc's other\n * fields, never the popup's own — and every field of it comes off this\n * seed. Against a cached read, retyping the headline restores yesterday's\n * body, link, schedule and enabled flag on a live site.\n */\n fromCache: hostFromCache,\n listen: hostListen,\n } = useFirestoreDoc<any>(\n () => doc(firestore, 'hosts', hostId),\n [firestore, hostId],\n { idField: '$id' },\n )\n const entitled = checkEntitlement(org, 'marketingOverlays')\n\n const saved = (host?.popup ?? {}) as HostPopup\n const [draft, setDraft] = useState<HostPopup>(saved)\n const savedKey = JSON.stringify(saved)\n useEffect(() => {\n setDraft(JSON.parse(savedKey))\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [savedKey])\n\n // The console media browser is provided by the shell (AGL-395).\n const { pickMedia } = useMediaPicker()\n // The headline and body are held in their stored form; the fields show them\n // as typed.\n const copy = useOverlayCopyEditor({\n hostId,\n host,\n usesTokens:\n entitled &&\n [saved.headline, saved.body, draft.headline, draft.body].some((text) =>\n text?.includes('{{'),\n ),\n })\n // Compared as it would be stored, so a name typed before the variables\n // arrived still counts as the change it becomes on save.\n const dirty =\n JSON.stringify({\n ...draft,\n headline: copy.stored(draft.headline),\n body: copy.stored(draft.body),\n }) !== savedKey\n const patch = (partial: Partial<HostPopup>) =>\n setDraft((previous) => ({ ...previous, ...partial }))\n const headlineShownAsTyped = copy.shownAsTyped(draft.headline)\n const bodyShownAsTyped = copy.shownAsTyped(draft.body)\n\n const handleSave = async () => {\n const dequeue = queueLoading()\n try {\n /**\n * Refuse a save whose seed the server never confirmed (AGL-1358).\n *\n * There is no create path to exempt here, and that is the point: unlike\n * a new row at a fresh uid, this writes a FIXED document path, so a\n * blank `draft` — what `host?.popup ?? {}` yields when the cache has\n * never seen a popup — is not harmless. It replaces whatever popup the\n * server really holds with an empty one. Both the populated and the\n * empty seed are guarded for the same reason.\n *\n * The activity log rides inside the guard: a logged \"Updated popup\"\n * whose write never happened leaves the history disagreeing with the\n * popup it claims to explain.\n *\n * The guard WRAPS the write — an early return is a shape you can keep\n * while losing the protection.\n */\n const verdict = await writeGuardedBySeed(\n {\n subject: 'popup',\n unreadable: hostStatus === 'error',\n fromCache: hostFromCache,\n listen: hostListen,\n },\n async () => {\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.update(doc(firestore, 'hosts', hostId), {\n popup: {\n enabled: Boolean(draft.enabled),\n headline: copy.storedWithin(draft.headline, 120),\n body: copy.storedWithin(draft.body, 1000),\n imageUrl: (draft.imageUrl ?? '').trim(),\n // AGL-1896. Capped at the same length the media library saves\n // alt through, so a value defaulted from an asset and a value\n // typed here cannot diverge.\n imageAlt: (draft.imageAlt ?? '')\n .trim()\n .slice(0, MEDIA_ALT_MAX_LENGTH),\n ctaLabel: (draft.ctaLabel ?? '').slice(0, 60),\n ctaHref: (draft.ctaHref ?? '').trim(),\n collectEmail: Boolean(draft.collectEmail),\n trigger: draft.trigger ?? 'delay',\n triggerValue: Math.max(0, Number(draft.triggerValue ?? 3)),\n frequencyDays: Math.max(1, Number(draft.frequencyDays ?? 7)),\n startAtMs: draft.startAtMs ?? null,\n endAtMs: draft.endAtMs ?? null,\n },\n }),\n })\n logActivity('Updated popup', { type: 'host', id: hostId })\n },\n )\n // A refusal leaves every typed value on screen. A save that silently\n // does nothing sends the user back to retype a form that will be\n // refused again just as quietly.\n if (!verdict.ok) {\n return void enqueueSnackbar(verdict.message, {\n variant: 'warning',\n persist: false,\n })\n }\n enqueueSnackbar('Popup saved', { variant: 'success', persist: false })\n } catch (error) {\n console.error(error)\n enqueueSnackbar('An error has occurred', {\n variant: 'error',\n allowDuplicate: true,\n })\n } finally {\n dequeue()\n }\n }\n\n return (\n <CardDisplay\n header=\"Promotional popup\"\n help={pluginDocsHelp('marketingOverlays', {\n anchor: '#promotional-popups',\n excerpt:\n 'The site-wide default popup. Its only frequency cap is a number ' +\n 'of days — the once-per-session choice is on the overlays card.',\n })}\n contentGutterX\n contentGutterY\n >\n {!entitled ? (\n <Alert severity=\"info\">\n {'Popups are included from the Starter plan — see Billing to ' +\n 'upgrade.'}\n </Alert>\n ) : (\n <Stack spacing={2}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Switch\n checked={Boolean(draft.enabled)}\n onChange={(event) => patch({ enabled: event.target.checked })}\n />\n <Typography variant=\"body2\">\n {draft.enabled ? 'Active on your published site' : 'Off'}\n </Typography>\n </Stack>\n <TextField\n label=\"Headline\"\n size=\"small\"\n value={copy.editable(draft.headline)}\n onChange={(event) =>\n patch({ headline: copy.stored(event.target.value) })\n }\n error={headlineShownAsTyped.length > 0}\n helperText={shownAsTypedHelperText(headlineShownAsTyped)}\n fullWidth\n />\n <TextField\n label=\"Body\"\n size=\"small\"\n value={copy.editable(draft.body)}\n onChange={(event) =>\n patch({ body: copy.stored(event.target.value) })\n }\n error={bodyShownAsTyped.length > 0}\n helperText={\n shownAsTypedHelperText(bodyShownAsTyped) ?? OVERLAY_COPY_HELPER_TEXT\n }\n multiline\n minRows={2}\n fullWidth\n />\n <Stack useFlexGap direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', rowGap: 1 }}>\n <TextField\n label=\"Image URL (optional)\"\n size=\"small\"\n placeholder=\"Pick from the media library\"\n value={draft.imageUrl ?? ''}\n onChange={(event) => patch({ imageUrl: event.target.value })}\n sx={{ flex: 1, minWidth: 220 }}\n />\n <Button\n size=\"small\"\n onClick={() =>\n void (async () => {\n const media = await pickMedia?.()\n if (!media) return\n // Alt defaults from the asset (AGL-1896), never clobbering\n // one the author already typed for THIS popup. Undefined\n // when the asset has no alt or the field is already\n // filled, and the spread then leaves `imageAlt` alone.\n const inheritedAlt = inheritedMediaAlt({\n placementAlt: draft.imageAlt,\n assetAlt: media.alt,\n })\n patch({\n imageUrl: media.url,\n ...(inheritedAlt ? { imageAlt: inheritedAlt } : {}),\n })\n })()\n }\n >\n {'Browse media'}\n </Button>\n </Stack>\n {/* AGL-1896: the popup image rendered `alt=\"\"` with no field\n behind it. Offered whenever there is an image to describe. */}\n {(draft.imageUrl ?? '').trim() ? (\n <TextField\n label=\"Image alt text\"\n size=\"small\"\n value={draft.imageAlt ?? ''}\n onChange={(event) => patch({ imageAlt: event.target.value })}\n helperText={\n 'Describes the image for screen readers. Filled in from the ' +\n 'media library when you pick an image that has alt text. ' +\n 'Leave empty only if the image is purely decorative.'\n }\n />\n ) : null}\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Switch\n checked={Boolean(draft.collectEmail)}\n onChange={(event) =>\n patch({ collectEmail: event.target.checked })\n }\n />\n <Typography variant=\"body2\">\n {'Collect emails (submissions land in your Inbox and Contacts)'}\n </Typography>\n </Stack>\n <Stack useFlexGap direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', rowGap: 1 }}>\n <TextField\n label=\"Button label\"\n size=\"small\"\n value={draft.ctaLabel ?? ''}\n onChange={(event) => patch({ ctaLabel: event.target.value })}\n sx={{ width: 160 }}\n />\n <TextField\n label=\"Button link\"\n size=\"small\"\n placeholder=\"/sale or https://…\"\n value={draft.ctaHref ?? ''}\n onChange={(event) => patch({ ctaHref: event.target.value })}\n sx={{ flex: 1, minWidth: 180 }}\n />\n </Stack>\n <Stack useFlexGap direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', rowGap: 1 }}>\n <TextField\n select\n label=\"Show\"\n size=\"small\"\n value={draft.trigger ?? 'delay'}\n onChange={(event) =>\n patch({ trigger: event.target.value as HostPopup['trigger'] })\n }\n sx={{ minWidth: 200 }}\n >\n {TRIGGERS.map((option) => (\n <MenuItem key={option.value} value={option.value}>\n {option.label}\n </MenuItem>\n ))}\n </TextField>\n {draft.trigger !== 'exit' ? (\n <TextField\n label={draft.trigger === 'scroll' ? 'Depth %' : 'Seconds'}\n size=\"small\"\n type=\"number\"\n value={draft.triggerValue ?? 3}\n onChange={(event) =>\n patch({ triggerValue: Number(event.target.value) })\n }\n sx={{ width: 110 }}\n />\n ) : null}\n <TextField\n label=\"Re-show after (days)\"\n size=\"small\"\n type=\"number\"\n value={draft.frequencyDays ?? 7}\n onChange={(event) =>\n patch({ frequencyDays: Number(event.target.value) })\n }\n sx={{ width: 160 }}\n />\n </Stack>\n <Stack useFlexGap direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', rowGap: 1 }}>\n <TextField\n label=\"Start (optional)\"\n size=\"small\"\n type=\"datetime-local\"\n value={toLocalInput(draft.startAtMs)}\n onChange={(event) =>\n patch({ startAtMs: fromLocalInput(event.target.value) })\n }\n slotProps={{ inputLabel: { shrink: true } }}\n sx={{ minWidth: 200 }}\n />\n <TextField\n label=\"End (optional)\"\n size=\"small\"\n type=\"datetime-local\"\n value={toLocalInput(draft.endAtMs)}\n onChange={(event) =>\n patch({ endAtMs: fromLocalInput(event.target.value) })\n }\n slotProps={{ inputLabel: { shrink: true } }}\n sx={{ minWidth: 200 }}\n />\n </Stack>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={\n !dirty ||\n (Boolean(draft.enabled) && !draft.body?.trim()) ||\n copy.pending(draft.headline, draft.body)\n }\n onClick={handleSave}\n sx={{ alignSelf: 'flex-start' }}\n >\n {'Save'}\n </Button>\n <OverlayStatsRow\n hostId={hostId}\n impressionKey=\"popupImpression\"\n actionKey=\"popupClick\"\n actionLabel=\"clicks\"\n />\n </Stack>\n )}\n </CardDisplay>\n )\n}\nPopupCard.displayName = 'PopupCard'\n\nexport default PopupCard\n"],"names":["checkEntitlement","inheritedMediaAlt","MEDIA_ALT_MAX_LENGTH","pluginDocsHelp","useMediaPicker","CardDisplay","useLoading","useSnackbar","Alert","Button","MenuItem","Stack","Switch","TextField","Typography","doc","useEffect","useState","useFirestore","useFirestoreDoc","useHostActivityLogger","useUser","writeGuardedBySeed","writeSiteWideChange","OverlayStatsRow","OVERLAY_COPY_HELPER_TEXT","shownAsTypedHelperText","useOverlayCopyEditor","TRIGGERS","value","label","toLocalInput","ms","date","Date","getTimezoneOffset","toISOString","slice","fromLocalInput","getTime","undefined","PopupCard","props","draft","hostId","firestore","data","user","org","enqueueSnackbar","queueLoading","logActivity","host","status","hostStatus","fromCache","hostFromCache","listen","hostListen","idField","entitled","saved","popup","setDraft","savedKey","JSON","stringify","parse","pickMedia","copy","usesTokens","headline","body","some","text","includes","dirty","stored","patch","partial","previous","headlineShownAsTyped","shownAsTyped","bodyShownAsTyped","handleSave","dequeue","verdict","subject","unreadable","write","batch","update","enabled","Boolean","storedWithin","imageUrl","trim","imageAlt","ctaLabel","ctaHref","collectEmail","trigger","triggerValue","Math","max","Number","frequencyDays","startAtMs","endAtMs","type","id","ok","message","variant","persist","error","console","allowDuplicate","header","help","anchor","excerpt","contentGutterX","contentGutterY","severity","spacing","direction","sx","alignItems","checked","onChange","event","target","size","editable","length","helperText","fullWidth","multiline","minRows","useFlexGap","flexWrap","rowGap","placeholder","flex","minWidth","onClick","media","inheritedAlt","placementAlt","assetAlt","alt","url","width","select","map","option","slotProps","inputLabel","shrink","color","disabled","pending","alignSelf","impressionKey","actionKey","actionLabel","displayName"],"mappings":"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"}