@aglyn/plugins-marketing 1.0.0-beta.209 → 1.0.0-beta.211

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.
@@ -25,9 +25,10 @@ import { ScrollTable } from "@aglyn/shared-ui-jsx/components/scroll-table.compon
25
25
  import { useSnackbar } from "@aglyn/shared-ui-snackstack";
26
26
  import { Timestamp } from "@aglyn/shared-util-timestamp";
27
27
  import { Alert, Button, Chip, IconButton, Dialog, DialogActions, DialogContent, DialogTitle, MenuItem, Stack, Switch, TableBody, TableCell, TableHead, TableRow, TextField, Typography } from "@mui/material";
28
- import { collection, deleteDoc, doc, limit, orderBy, query, setDoc } from "firebase/firestore";
28
+ import { collection, doc, limit, orderBy, query } from "firebase/firestore";
29
29
  import { useState } from "react";
30
- import { useFirestore, useFirestoreCollection, useHostActivityLogger, writeGuardedBySeed } from "@aglyn/tenant-feature-instance";
30
+ import { useFirestore, useFirestoreCollection, useHostActivityLogger, writeGuardedBySeed, useUser } from "@aglyn/tenant-feature-instance";
31
+ import { writeSiteWideChange } from "@aglyn/tenant-feature-instance/hooks/helpers/site-wide-change";
31
32
  import { overlayDisplayName, overlayEngagementLabel, overlayPagesLabel, overlayWindowLabel } from "./overlay-labels.js";
32
33
  import { OVERLAY_COPY_HELPER_TEXT, shownAsTypedHelperText, useOverlayCopyEditor } from "./use-overlay-copy-editor.js";
33
34
  /**
@@ -80,6 +81,7 @@ function parsePatterns(value) {
80
81
  var _editor_bar, _editor_popup, _editor_popup1, _editor_bar1, _editor_popup2, _editor_popup3, _editor_bar2, _editor_bar3, _editor_bar4, _editor_bar5, _editor_popup4, _editor_popup5, _editor_popup6, _editor_popup7, _editor_popup8, _editor_popup9, _editor_popup10, _editor_popup11, _editor_popup12, _editor_popup13, _editor_bar6, _editor_popup14, _editor_popup15;
81
82
  const { hostId } = props;
82
83
  const firestore = useFirestore();
84
+ const { data: user } = useUser();
83
85
  const { org } = props;
84
86
  const { enqueueSnackbar } = useSnackbar();
85
87
  const { confirm } = useConfirmationContext();
@@ -221,11 +223,16 @@ function parsePatterns(value) {
221
223
  unreadable: Boolean(editor.$id) && overlaysStatus === 'error',
222
224
  fromCache: Boolean(editor.$id) && overlaysFromCache
223
225
  }, async ()=>{
224
- await setDoc(doc(firestore, 'hosts', hostId, 'overlays', id), _extends({}, cleaned, {
225
- updatedAt: Timestamp.now()
226
- }, editor.$id ? {} : {
227
- createdAt: Timestamp.now()
228
- }));
226
+ await writeSiteWideChange({
227
+ firestore,
228
+ user,
229
+ hostId,
230
+ write: (batch)=>batch.set(doc(firestore, 'hosts', hostId, 'overlays', id), _extends({}, cleaned, {
231
+ updatedAt: Timestamp.now()
232
+ }, editor.$id ? {} : {
233
+ createdAt: Timestamp.now()
234
+ }))
235
+ });
229
236
  logActivity(editor.$id ? 'Updated overlay' : 'Created overlay', _extends({
230
237
  type: 'content',
231
238
  id
@@ -266,7 +273,12 @@ function parsePatterns(value) {
266
273
  // resolved value alone made this always return (AGL-950).
267
274
  .then(()=>true).catch(()=>false);
268
275
  if (!confirmed) return;
269
- await deleteDoc(doc(firestore, 'hosts', hostId, 'overlays', overlay.$id));
276
+ await writeSiteWideChange({
277
+ firestore,
278
+ user,
279
+ hostId,
280
+ write: (batch)=>batch.delete(doc(firestore, 'hosts', hostId, 'overlays', overlay.$id))
281
+ });
270
282
  enqueueSnackbar('Overlay deleted', {
271
283
  variant: 'success',
272
284
  persist: false
@@ -287,25 +299,37 @@ function parsePatterns(value) {
287
299
  if (!(neighbor == null ? void 0 : neighbor.$id) || !(current == null ? void 0 : current.$id)) return;
288
300
  const currentOrder = (_current_order = current.order) != null ? _current_order : index;
289
301
  const neighborOrder = (_neighbor_order = neighbor.order) != null ? _neighbor_order : index + direction;
290
- await Promise.all([
291
- setDoc(doc(firestore, 'hosts', hostId, 'overlays', current.$id), {
292
- order: neighborOrder === currentOrder ? neighborOrder + direction : neighborOrder
293
- }, {
294
- merge: true
295
- }),
296
- setDoc(doc(firestore, 'hosts', hostId, 'overlays', neighbor.$id), {
297
- order: currentOrder
298
- }, {
299
- merge: true
300
- })
301
- ]).catch(console.error);
302
+ // One batch, so the swap lands whole — and the order decides which
303
+ // overlay a visitor sees, so it carries the site's cache drop.
304
+ await writeSiteWideChange({
305
+ firestore,
306
+ user,
307
+ hostId,
308
+ write: (batch)=>{
309
+ batch.set(doc(firestore, 'hosts', hostId, 'overlays', current.$id), {
310
+ order: neighborOrder === currentOrder ? neighborOrder + direction : neighborOrder
311
+ }, {
312
+ merge: true
313
+ });
314
+ batch.set(doc(firestore, 'hosts', hostId, 'overlays', neighbor.$id), {
315
+ order: currentOrder
316
+ }, {
317
+ merge: true
318
+ });
319
+ }
320
+ }).catch(console.error);
302
321
  };
303
322
  const handleToggle = async (overlay)=>{
304
323
  if (!overlay.$id) return;
305
- await setDoc(doc(firestore, 'hosts', hostId, 'overlays', overlay.$id), {
306
- enabled: overlay.enabled === false
307
- }, {
308
- merge: true
324
+ await writeSiteWideChange({
325
+ firestore,
326
+ user,
327
+ hostId,
328
+ write: (batch)=>batch.set(doc(firestore, 'hosts', hostId, 'overlays', overlay.$id), {
329
+ enabled: overlay.enabled === false
330
+ }, {
331
+ merge: true
332
+ })
309
333
  });
310
334
  };
311
335
  const statusChip = (overlay)=>{
@@ -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 overlayStatus,\n type HostOverlay,\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 {\n collection,\n deleteDoc,\n doc,\n limit,\n orderBy,\n query,\n setDoc,\n} from 'firebase/firestore'\nimport { useState } from 'react'\nimport {\n useFirestore,\n useFirestoreCollection,\n useHostActivityLogger,\n writeGuardedBySeed,\n} from '@aglyn/tenant-feature-instance'\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'\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 = 50\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 { 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 setDoc(doc(firestore, 'hosts', hostId, 'overlays', id), {\n ...cleaned,\n updatedAt: Timestamp.now(),\n ...(editor.$id ? {} : { createdAt: Timestamp.now() }),\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 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 deleteDoc(doc(firestore, 'hosts', hostId, 'overlays', overlay.$id))\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 await Promise.all([\n setDoc(\n doc(firestore, 'hosts', hostId, 'overlays', current.$id),\n { order: neighborOrder === currentOrder ? neighborOrder + direction : neighborOrder },\n { merge: true },\n ),\n setDoc(\n doc(firestore, 'hosts', hostId, 'overlays', neighbor.$id),\n { order: currentOrder },\n { merge: true },\n ),\n ]).catch(console.error)\n }\n\n const handleToggle = async (overlay: OverlayDraft) => {\n if (!overlay.$id) return\n await setDoc(\n doc(firestore, 'hosts', hostId, 'overlays', overlay.$id),\n { enabled: overlay.enabled === false },\n { merge: true },\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 </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 <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 ) : (\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 {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","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","deleteDoc","doc","limit","orderBy","query","setDoc","useState","useFirestore","useFirestoreCollection","useHostActivityLogger","writeGuardedBySeed","overlayDisplayName","overlayEngagementLabel","overlayPagesLabel","overlayWindowLabel","OVERLAY_COPY_HELPER_TEXT","shownAsTypedHelperText","useOverlayCopyEditor","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","org","enqueueSnackbar","confirm","logActivity","entitled","data","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","now","type","ok","message","error","console","handleDelete","overlay","confirmed","title","description","confirmationText","confirmationButtonProps","color","then","catch","handleMove","index","direction","current","neighbor","currentOrder","order","neighborOrder","Promise","all","merge","handleToggle","statusChip","size","label","header","help","anchor","excerpt","contentGutterX","contentGutterY","contentBordered","severity","spacing","onClick","align","stats","aria-label","disabled","indexOf","path","sx","fontSize","checked","onChange","slotProps","input","open","onClose","maxWidth","fullWidth","mt","event","target","helperText","required","editable","href","backgroundColor","textColor","multiline","minRows","ctaLabel","ctaHref","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 overlayStatus,\n type HostOverlay,\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'\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 = 50\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 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 </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 <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 ) : (\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 {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","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","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","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","align","stats","aria-label","disabled","indexOf","path","sx","fontSize","checked","onChange","slotProps","input","open","onClose","maxWidth","fullWidth","mt","event","target","helperText","required","editable","href","backgroundColor","textColor","multiline","minRows","ctaLabel","ctaHref","select","minWidth","oncePerSession","inputLabel","shrink","startAtMs","endAtMs","pathPatterns","join","excludePathPatterns","pending","displayName"],"mappings":"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"}
@@ -23,9 +23,10 @@ import { ListPagination } from "@aglyn/shared-ui-jsx/components/list-pagination.
23
23
  import { ScrollTable } from "@aglyn/shared-ui-jsx/components/scroll-table.component";
24
24
  import { useSnackbar } from "@aglyn/shared-ui-snackstack";
25
25
  import { Alert, Chip, Stack, Switch, TableBody, TableCell, TableHead, TableRow, Typography } from "@mui/material";
26
- import { collection, doc, getDoc, getDocs, limit, orderBy, query, setDoc } from "firebase/firestore";
26
+ import { collection, doc, getDoc, getDocs, limit, orderBy, query } from "firebase/firestore";
27
27
  import { Fragment, useState } from "react";
28
- import { useFirestore } from "@aglyn/tenant-feature-instance";
28
+ import { useFirestore, useUser } from "@aglyn/tenant-feature-instance";
29
+ import { writeSiteWideChange } from "@aglyn/tenant-feature-instance/hooks/helpers/site-wide-change";
29
30
  import { ceilingedWindow } from "@aglyn/tenant-feature-instance/hooks/host-collection-queries";
30
31
  import { useMarketingOrgMount } from "./marketing-org-mount.js";
31
32
  import { OrgSitePickerButton, orgSiteSectionHref } from "./org-site-picker-button.js";
@@ -103,6 +104,7 @@ const STATUS_CHIP = {
103
104
  const { org } = props;
104
105
  const mount = useMarketingOrgMount();
105
106
  const firestore = useFirestore();
107
+ const { data: user } = useUser();
106
108
  const { enqueueSnackbar } = useSnackbar();
107
109
  const entitled = checkEntitlement(org, 'marketingOverlays');
108
110
  const sites = (_ref = mount == null ? void 0 : mount.hosts) != null ? _ref : [];
@@ -118,10 +120,15 @@ const STATUS_CHIP = {
118
120
  */ const handleToggle = async (hostId, overlay)=>{
119
121
  const enabled = overlay.enabled === false;
120
122
  try {
121
- await setDoc(doc(firestore, 'hosts', hostId, 'overlays', overlay.$id), {
122
- enabled
123
- }, {
124
- merge: true
123
+ await writeSiteWideChange({
124
+ firestore,
125
+ user,
126
+ hostId,
127
+ write: (batch)=>batch.set(doc(firestore, 'hosts', hostId, 'overlays', overlay.$id), {
128
+ enabled
129
+ }, {
130
+ merge: true
131
+ })
125
132
  });
126
133
  patch(hostId, (site)=>_extends({}, site, {
127
134
  rows: site.rows.map((row)=>row.$id === overlay.$id ? _extends({}, row, {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/org-overlays-card.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n type AglynOrgBilling,\n checkEntitlement,\n pluginDocsHelp,\n} from '@aglyn/aglyn'\nimport { compareOverlayPrecedence, overlayStatus, type HostOverlay } from '../model'\nimport { AppLink, CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n Chip,\n Stack,\n Switch,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n Typography,\n} from '@mui/material'\nimport {\n collection,\n doc,\n getDoc,\n getDocs,\n limit,\n orderBy,\n query,\n setDoc,\n type Firestore,\n} from 'firebase/firestore'\nimport { Fragment, useState } from 'react'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport { ceilingedWindow } from '@aglyn/tenant-feature-instance/hooks/host-collection-queries'\nimport { useMarketingOrgMount } from './marketing-org-mount'\nimport {\n OrgSitePickerButton,\n orgSiteSectionHref,\n} from './org-site-picker-button'\nimport {\n overlayDisplayName,\n overlayEngagementLabel,\n overlayPagesLabel,\n overlayWindowLabel,\n} from './overlay-labels'\nimport { ORG_SITES_PER_PAGE, useOrgSiteReads } from './use-one-shot-reads'\n\n/**\n * How many overlays are read for each site.\n *\n * Smaller than the site's own ceiling on purpose: a site runs a handful of\n * bars and popups, a page here holds a page of sites' worth of them, and a\n * site with more says so on its row and links to its own list, where every\n * one of them is — and where the order they take can be changed.\n */\nexport const ORG_OVERLAYS_PER_SITE = 10\n\ntype OverlayRow = HostOverlay & { $id: string }\n\n/** What one site's group of rows is drawn from. */\ninterface SiteOverlays {\n /** In the order the site shows them: see `compareOverlayPrecedence`. */\n rows: OverlayRow[]\n /** The site has more overlays than were read. */\n truncated: boolean\n /**\n * Whether the site's always-on default bar and popup would show — the ones\n * a matching overlay takes priority over. `null` when the site document\n * could not be read, which is drawn as nothing rather than as \"off\".\n */\n defaults: { bar: boolean; popup: boolean } | null\n}\n\n/**\n * One site's overlays and its defaults: the overlays ordered and ceilinged\n * the way the site's own list reads them — `name` is on every overlay its\n * only creator writes — and the host document for the defaults, which the\n * console shell already listens to for its site list.\n */\nasync function readSiteOverlays(\n firestore: Firestore,\n hostId: string,\n): Promise<SiteOverlays> {\n const [overlays, host] = await Promise.all([\n getDocs(\n query(\n collection(firestore, 'hosts', hostId, 'overlays'),\n orderBy('name'),\n limit(ORG_OVERLAYS_PER_SITE + 1),\n ),\n ),\n getDoc(doc(firestore, 'hosts', hostId)).catch(() => null),\n ])\n const { rows, truncated } = ceilingedWindow(\n overlays.docs.map(\n (entry) => ({ ...(entry.data() as HostOverlay), $id: entry.id }) as OverlayRow,\n ),\n ORG_OVERLAYS_PER_SITE,\n )\n const site = host?.exists() ? (host.data() as Record<string, any>) : null\n return {\n // In the order the site shows them. A window the ceiling cut short is\n // the first overlays by name, and the site's line says so.\n rows: [...rows].sort(compareOverlayPrecedence),\n truncated,\n // The same test the page enricher applies before it draws either one.\n defaults: site\n ? {\n bar: Boolean(site.announcementBar?.enabled && site.announcementBar?.text),\n popup: Boolean(site.popup?.enabled && site.popup?.body),\n }\n : null,\n }\n}\n\nconst STATUS_CHIP: Record<\n ReturnType<typeof overlayStatus>,\n { label: string; color: 'default' | 'info' | 'success' }\n> = {\n off: { label: 'Off', color: 'default' },\n scheduled: { label: 'Scheduled', color: 'info' },\n live: { label: 'Live', color: 'success' },\n}\n\n/** Columns in a row, so a site's own line can span all of them. */\nconst COLUMNS = 7\n\nexport interface OrgOverlaysCardProps {\n /** The org's plan, for the `marketingOverlays` entitlement. */\n org?: Partial<AglynOrgBilling>\n}\n\n/**\n * Every site's announcement bars and popups, on the organization's\n * Marketing hub.\n *\n * READ-ONLY but for the switch. An overlay is drawn on one site's pages and\n * written against that site's variables, screens and order, so its editor\n * stays on the site: each row's Edit and the New button open the site's own\n * Overlays section. What the org hub adds is the view across sites — which\n * of them are showing what, and their lifetime engagement — and the one\n * change that needs no editor, turning an overlay on or off.\n *\n * Grouped by site, each group in the order the site shows its overlays,\n * under a line naming the site and whether its default bar and popup are\n * on. No 14-day analytics row: that is a document read per day per site,\n * and it lives on the site.\n */\nexport function OrgOverlaysCard(props: OrgOverlaysCardProps) {\n const { org } = props\n const mount = useMarketingOrgMount()\n const firestore = useFirestore()\n const { enqueueSnackbar } = useSnackbar()\n const entitled = checkEntitlement(org, 'marketingOverlays')\n\n const sites = mount?.hosts ?? []\n const [page, setPage] = useState(0)\n const pageSites = sites.slice(\n page * ORG_SITES_PER_PAGE,\n (page + 1) * ORG_SITES_PER_PAGE,\n )\n const { reads, patch } = useOrgSiteReads<SiteOverlays>(\n entitled ? pageSites.map((site) => site.id) : [],\n (hostId) => readSiteOverlays(firestore, hostId),\n [firestore, entitled],\n )\n\n /**\n * One field, merged, on the row's own site — the write the site's own\n * switch makes, and one the rules allow every editor of that site.\n */\n const handleToggle = async (hostId: string, overlay: OverlayRow) => {\n const enabled = overlay.enabled === false\n try {\n await setDoc(\n doc(firestore, 'hosts', hostId, 'overlays', overlay.$id),\n { enabled },\n { merge: true },\n )\n patch(hostId, (site) => ({\n ...site,\n rows: site.rows.map((row) =>\n row.$id === overlay.$id ? { ...row, enabled } : row,\n ),\n }))\n } catch (error) {\n console.error(error)\n enqueueSnackbar('The overlay could not be switched', {\n variant: 'error',\n })\n }\n }\n\n return (\n <CardDisplay\n header=\"Announcement bars & popups\"\n subheader=\"Every site in this organization\"\n help={pluginDocsHelp('marketingOverlays', {\n anchor: '#across-your-sites',\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 ) : !mount || !mount.hostsReady ? null : !sites.length ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'This organization has no sites yet. Overlays are drawn on a ' +\n 'site’s pages, so they are created on a site.'}\n </Typography>\n ) : (\n <Stack spacing={2}>\n <Stack\n direction=\"row\"\n spacing={2}\n sx={{ alignItems: 'center', justifyContent: 'space-between' }}\n >\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Each site shows the first matching bar and popup in its own ' +\n 'order. Edit and create them on the site; switch them on or ' +\n 'off here.'}\n </Typography>\n <OrgSitePickerButton\n mount={mount}\n section=\"overlays\"\n label=\"New overlay\"\n />\n </Stack>\n <ScrollTable size=\"small\" aria-label=\"Overlays by site\">\n <TableHead>\n <TableRow>\n <TableCell>{'Overlay'}</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 {pageSites.map((site) => {\n const read = reads.get(site.id)\n const answer = read?.value ?? null\n const href = orgSiteSectionHref(mount, site.id, 'overlays')\n const name = site.name || site.subdomain || site.id\n return (\n <Fragment key={site.id}>\n {/* The site's own line: its name, its defaults, and what was not read. */}\n <TableRow>\n <TableCell colSpan={COLUMNS}>\n <Stack\n direction=\"row\"\n spacing={2}\n sx={{ alignItems: 'baseline', flexWrap: 'wrap' }}\n >\n <Typography variant=\"subtitle2\">\n {href ? <AppLink href={href}>{name}</AppLink> : name}\n </Typography>\n {answer?.defaults ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`Default bar ${answer.defaults.bar ? 'on' : 'off'} · ` +\n `default popup ${answer.defaults.popup ? 'on' : 'off'}`}\n </Typography>\n ) : null}\n {answer?.truncated ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`More than ${ORG_OVERLAYS_PER_SITE} on this ` +\n 'site — these are the first by name; the ' +\n 'site’s own list has every one.'}\n </Typography>\n ) : null}\n </Stack>\n </TableCell>\n </TableRow>\n {read?.status === 'error' ? (\n <TableRow>\n <TableCell colSpan={COLUMNS}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'This site’s overlays could not be read.'}\n </Typography>\n </TableCell>\n </TableRow>\n ) : answer && !answer.rows.length ? (\n <TableRow>\n <TableCell colSpan={COLUMNS}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No overlays on this site.'}\n </Typography>\n </TableCell>\n </TableRow>\n ) : (\n (answer?.rows ?? []).map((overlay) => {\n const status = STATUS_CHIP[overlayStatus(overlay, Date.now())]\n return (\n <TableRow key={overlay.$id}>\n <TableCell>{overlayDisplayName(overlay)}</TableCell>\n <TableCell>\n {overlay.kind === 'bar' ? 'Bar' : 'Popup'}\n </TableCell>\n <TableCell>\n <Chip\n size=\"small\"\n color={status.color}\n label={status.label}\n />\n </TableCell>\n <TableCell>{overlayWindowLabel(overlay)}</TableCell>\n <TableCell>{overlayPagesLabel(overlay)}</TableCell>\n <TableCell>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {overlayEngagementLabel(overlay.stats)}\n </Typography>\n </TableCell>\n <TableCell align=\"right\">\n <Switch\n size=\"small\"\n checked={overlay.enabled !== false}\n onChange={() => void handleToggle(site.id, overlay)}\n slotProps={{\n input: {\n 'aria-label': `Show ${overlayDisplayName(overlay)} on ${name}`,\n },\n }}\n />\n {/* A site the shell could not address has no Overlays section to open. */}\n {href ? (\n <AppLink\n componentVariant=\"button\"\n href={href}\n size=\"small\"\n color=\"primary\"\n aria-label={`Edit ${overlayDisplayName(overlay)} on ${name}`}\n >\n {'Edit'}\n </AppLink>\n ) : null}\n </TableCell>\n </TableRow>\n )\n })\n )}\n </Fragment>\n )\n })}\n </TableBody>\n </ScrollTable>\n <ListPagination\n page={page}\n pageSize={ORG_SITES_PER_PAGE}\n rowCount={pageSites.length}\n count={sites.length}\n onPageChange={setPage}\n labelDisplayedRows={({ from, to, count }) =>\n `Sites ${from}–${to} of ${count}`\n }\n />\n </Stack>\n )}\n </CardDisplay>\n )\n}\nOrgOverlaysCard.displayName = 'OrgOverlaysCard'\n\nexport default OrgOverlaysCard\n"],"names":["checkEntitlement","pluginDocsHelp","compareOverlayPrecedence","overlayStatus","AppLink","CardDisplay","ListPagination","ScrollTable","useSnackbar","Alert","Chip","Stack","Switch","TableBody","TableCell","TableHead","TableRow","Typography","collection","doc","getDoc","getDocs","limit","orderBy","query","setDoc","Fragment","useState","useFirestore","ceilingedWindow","useMarketingOrgMount","OrgSitePickerButton","orgSiteSectionHref","overlayDisplayName","overlayEngagementLabel","overlayPagesLabel","overlayWindowLabel","ORG_SITES_PER_PAGE","useOrgSiteReads","ORG_OVERLAYS_PER_SITE","readSiteOverlays","firestore","hostId","site","overlays","host","Promise","all","catch","rows","truncated","docs","map","entry","data","$id","id","exists","sort","defaults","bar","Boolean","announcementBar","enabled","text","popup","body","STATUS_CHIP","off","label","color","scheduled","live","COLUMNS","OrgOverlaysCard","props","org","mount","enqueueSnackbar","entitled","sites","hosts","page","setPage","pageSites","slice","reads","patch","handleToggle","overlay","merge","row","error","console","variant","header","subheader","help","anchor","contentGutterX","contentGutterY","contentBordered","severity","hostsReady","length","spacing","direction","sx","alignItems","justifyContent","section","size","aria-label","align","read","get","answer","value","href","name","subdomain","colSpan","flexWrap","status","Date","now","kind","stats","checked","onChange","slotProps","input","componentVariant","pageSize","rowCount","count","onPageChange","labelDisplayedRows","from","to","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/org-overlays-card.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n type AglynOrgBilling,\n checkEntitlement,\n pluginDocsHelp,\n} from '@aglyn/aglyn'\nimport { compareOverlayPrecedence, overlayStatus, type HostOverlay } from '../model'\nimport { AppLink, CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n Chip,\n Stack,\n Switch,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n Typography,\n} from '@mui/material'\nimport {\n collection,\n doc,\n getDoc,\n getDocs,\n limit,\n orderBy,\n query,\n type Firestore,\n} from 'firebase/firestore'\nimport { Fragment, useState } from 'react'\nimport { useFirestore, useUser } from '@aglyn/tenant-feature-instance'\nimport { writeSiteWideChange } from '@aglyn/tenant-feature-instance/hooks/helpers/site-wide-change'\nimport { ceilingedWindow } from '@aglyn/tenant-feature-instance/hooks/host-collection-queries'\nimport { useMarketingOrgMount } from './marketing-org-mount'\nimport {\n OrgSitePickerButton,\n orgSiteSectionHref,\n} from './org-site-picker-button'\nimport {\n overlayDisplayName,\n overlayEngagementLabel,\n overlayPagesLabel,\n overlayWindowLabel,\n} from './overlay-labels'\nimport { ORG_SITES_PER_PAGE, useOrgSiteReads } from './use-one-shot-reads'\n\n/**\n * How many overlays are read for each site.\n *\n * Smaller than the site's own ceiling on purpose: a site runs a handful of\n * bars and popups, a page here holds a page of sites' worth of them, and a\n * site with more says so on its row and links to its own list, where every\n * one of them is — and where the order they take can be changed.\n */\nexport const ORG_OVERLAYS_PER_SITE = 10\n\ntype OverlayRow = HostOverlay & { $id: string }\n\n/** What one site's group of rows is drawn from. */\ninterface SiteOverlays {\n /** In the order the site shows them: see `compareOverlayPrecedence`. */\n rows: OverlayRow[]\n /** The site has more overlays than were read. */\n truncated: boolean\n /**\n * Whether the site's always-on default bar and popup would show — the ones\n * a matching overlay takes priority over. `null` when the site document\n * could not be read, which is drawn as nothing rather than as \"off\".\n */\n defaults: { bar: boolean; popup: boolean } | null\n}\n\n/**\n * One site's overlays and its defaults: the overlays ordered and ceilinged\n * the way the site's own list reads them — `name` is on every overlay its\n * only creator writes — and the host document for the defaults, which the\n * console shell already listens to for its site list.\n */\nasync function readSiteOverlays(\n firestore: Firestore,\n hostId: string,\n): Promise<SiteOverlays> {\n const [overlays, host] = await Promise.all([\n getDocs(\n query(\n collection(firestore, 'hosts', hostId, 'overlays'),\n orderBy('name'),\n limit(ORG_OVERLAYS_PER_SITE + 1),\n ),\n ),\n getDoc(doc(firestore, 'hosts', hostId)).catch(() => null),\n ])\n const { rows, truncated } = ceilingedWindow(\n overlays.docs.map(\n (entry) => ({ ...(entry.data() as HostOverlay), $id: entry.id }) as OverlayRow,\n ),\n ORG_OVERLAYS_PER_SITE,\n )\n const site = host?.exists() ? (host.data() as Record<string, any>) : null\n return {\n // In the order the site shows them. A window the ceiling cut short is\n // the first overlays by name, and the site's line says so.\n rows: [...rows].sort(compareOverlayPrecedence),\n truncated,\n // The same test the page enricher applies before it draws either one.\n defaults: site\n ? {\n bar: Boolean(site.announcementBar?.enabled && site.announcementBar?.text),\n popup: Boolean(site.popup?.enabled && site.popup?.body),\n }\n : null,\n }\n}\n\nconst STATUS_CHIP: Record<\n ReturnType<typeof overlayStatus>,\n { label: string; color: 'default' | 'info' | 'success' }\n> = {\n off: { label: 'Off', color: 'default' },\n scheduled: { label: 'Scheduled', color: 'info' },\n live: { label: 'Live', color: 'success' },\n}\n\n/** Columns in a row, so a site's own line can span all of them. */\nconst COLUMNS = 7\n\nexport interface OrgOverlaysCardProps {\n /** The org's plan, for the `marketingOverlays` entitlement. */\n org?: Partial<AglynOrgBilling>\n}\n\n/**\n * Every site's announcement bars and popups, on the organization's\n * Marketing hub.\n *\n * READ-ONLY but for the switch. An overlay is drawn on one site's pages and\n * written against that site's variables, screens and order, so its editor\n * stays on the site: each row's Edit and the New button open the site's own\n * Overlays section. What the org hub adds is the view across sites — which\n * of them are showing what, and their lifetime engagement — and the one\n * change that needs no editor, turning an overlay on or off.\n *\n * Grouped by site, each group in the order the site shows its overlays,\n * under a line naming the site and whether its default bar and popup are\n * on. No 14-day analytics row: that is a document read per day per site,\n * and it lives on the site.\n */\nexport function OrgOverlaysCard(props: OrgOverlaysCardProps) {\n const { org } = props\n const mount = useMarketingOrgMount()\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const entitled = checkEntitlement(org, 'marketingOverlays')\n\n const sites = mount?.hosts ?? []\n const [page, setPage] = useState(0)\n const pageSites = sites.slice(\n page * ORG_SITES_PER_PAGE,\n (page + 1) * ORG_SITES_PER_PAGE,\n )\n const { reads, patch } = useOrgSiteReads<SiteOverlays>(\n entitled ? pageSites.map((site) => site.id) : [],\n (hostId) => readSiteOverlays(firestore, hostId),\n [firestore, entitled],\n )\n\n /**\n * One field, merged, on the row's own site — the write the site's own\n * switch makes, and one the rules allow every editor of that site.\n */\n const handleToggle = async (hostId: string, overlay: OverlayRow) => {\n const enabled = overlay.enabled === false\n try {\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 },\n { merge: true },\n ),\n })\n patch(hostId, (site) => ({\n ...site,\n rows: site.rows.map((row) =>\n row.$id === overlay.$id ? { ...row, enabled } : row,\n ),\n }))\n } catch (error) {\n console.error(error)\n enqueueSnackbar('The overlay could not be switched', {\n variant: 'error',\n })\n }\n }\n\n return (\n <CardDisplay\n header=\"Announcement bars & popups\"\n subheader=\"Every site in this organization\"\n help={pluginDocsHelp('marketingOverlays', {\n anchor: '#across-your-sites',\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 ) : !mount || !mount.hostsReady ? null : !sites.length ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'This organization has no sites yet. Overlays are drawn on a ' +\n 'site’s pages, so they are created on a site.'}\n </Typography>\n ) : (\n <Stack spacing={2}>\n <Stack\n direction=\"row\"\n spacing={2}\n sx={{ alignItems: 'center', justifyContent: 'space-between' }}\n >\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Each site shows the first matching bar and popup in its own ' +\n 'order. Edit and create them on the site; switch them on or ' +\n 'off here.'}\n </Typography>\n <OrgSitePickerButton\n mount={mount}\n section=\"overlays\"\n label=\"New overlay\"\n />\n </Stack>\n <ScrollTable size=\"small\" aria-label=\"Overlays by site\">\n <TableHead>\n <TableRow>\n <TableCell>{'Overlay'}</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 {pageSites.map((site) => {\n const read = reads.get(site.id)\n const answer = read?.value ?? null\n const href = orgSiteSectionHref(mount, site.id, 'overlays')\n const name = site.name || site.subdomain || site.id\n return (\n <Fragment key={site.id}>\n {/* The site's own line: its name, its defaults, and what was not read. */}\n <TableRow>\n <TableCell colSpan={COLUMNS}>\n <Stack\n direction=\"row\"\n spacing={2}\n sx={{ alignItems: 'baseline', flexWrap: 'wrap' }}\n >\n <Typography variant=\"subtitle2\">\n {href ? <AppLink href={href}>{name}</AppLink> : name}\n </Typography>\n {answer?.defaults ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`Default bar ${answer.defaults.bar ? 'on' : 'off'} · ` +\n `default popup ${answer.defaults.popup ? 'on' : 'off'}`}\n </Typography>\n ) : null}\n {answer?.truncated ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`More than ${ORG_OVERLAYS_PER_SITE} on this ` +\n 'site — these are the first by name; the ' +\n 'site’s own list has every one.'}\n </Typography>\n ) : null}\n </Stack>\n </TableCell>\n </TableRow>\n {read?.status === 'error' ? (\n <TableRow>\n <TableCell colSpan={COLUMNS}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'This site’s overlays could not be read.'}\n </Typography>\n </TableCell>\n </TableRow>\n ) : answer && !answer.rows.length ? (\n <TableRow>\n <TableCell colSpan={COLUMNS}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No overlays on this site.'}\n </Typography>\n </TableCell>\n </TableRow>\n ) : (\n (answer?.rows ?? []).map((overlay) => {\n const status = STATUS_CHIP[overlayStatus(overlay, Date.now())]\n return (\n <TableRow key={overlay.$id}>\n <TableCell>{overlayDisplayName(overlay)}</TableCell>\n <TableCell>\n {overlay.kind === 'bar' ? 'Bar' : 'Popup'}\n </TableCell>\n <TableCell>\n <Chip\n size=\"small\"\n color={status.color}\n label={status.label}\n />\n </TableCell>\n <TableCell>{overlayWindowLabel(overlay)}</TableCell>\n <TableCell>{overlayPagesLabel(overlay)}</TableCell>\n <TableCell>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {overlayEngagementLabel(overlay.stats)}\n </Typography>\n </TableCell>\n <TableCell align=\"right\">\n <Switch\n size=\"small\"\n checked={overlay.enabled !== false}\n onChange={() => void handleToggle(site.id, overlay)}\n slotProps={{\n input: {\n 'aria-label': `Show ${overlayDisplayName(overlay)} on ${name}`,\n },\n }}\n />\n {/* A site the shell could not address has no Overlays section to open. */}\n {href ? (\n <AppLink\n componentVariant=\"button\"\n href={href}\n size=\"small\"\n color=\"primary\"\n aria-label={`Edit ${overlayDisplayName(overlay)} on ${name}`}\n >\n {'Edit'}\n </AppLink>\n ) : null}\n </TableCell>\n </TableRow>\n )\n })\n )}\n </Fragment>\n )\n })}\n </TableBody>\n </ScrollTable>\n <ListPagination\n page={page}\n pageSize={ORG_SITES_PER_PAGE}\n rowCount={pageSites.length}\n count={sites.length}\n onPageChange={setPage}\n labelDisplayedRows={({ from, to, count }) =>\n `Sites ${from}–${to} of ${count}`\n }\n />\n </Stack>\n )}\n </CardDisplay>\n )\n}\nOrgOverlaysCard.displayName = 'OrgOverlaysCard'\n\nexport default OrgOverlaysCard\n"],"names":["checkEntitlement","pluginDocsHelp","compareOverlayPrecedence","overlayStatus","AppLink","CardDisplay","ListPagination","ScrollTable","useSnackbar","Alert","Chip","Stack","Switch","TableBody","TableCell","TableHead","TableRow","Typography","collection","doc","getDoc","getDocs","limit","orderBy","query","Fragment","useState","useFirestore","useUser","writeSiteWideChange","ceilingedWindow","useMarketingOrgMount","OrgSitePickerButton","orgSiteSectionHref","overlayDisplayName","overlayEngagementLabel","overlayPagesLabel","overlayWindowLabel","ORG_SITES_PER_PAGE","useOrgSiteReads","ORG_OVERLAYS_PER_SITE","readSiteOverlays","firestore","hostId","site","overlays","host","Promise","all","catch","rows","truncated","docs","map","entry","data","$id","id","exists","sort","defaults","bar","Boolean","announcementBar","enabled","text","popup","body","STATUS_CHIP","off","label","color","scheduled","live","COLUMNS","OrgOverlaysCard","props","org","mount","user","enqueueSnackbar","entitled","sites","hosts","page","setPage","pageSites","slice","reads","patch","handleToggle","overlay","write","batch","set","merge","row","error","console","variant","header","subheader","help","anchor","contentGutterX","contentGutterY","contentBordered","severity","hostsReady","length","spacing","direction","sx","alignItems","justifyContent","section","size","aria-label","align","read","get","answer","value","href","name","subdomain","colSpan","flexWrap","status","Date","now","kind","stats","checked","onChange","slotProps","input","componentVariant","pageSize","rowCount","count","onPageChange","labelDisplayedRows","from","to","displayName"],"mappings":"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"}