@aglyn/plugins-marketing 1.0.0-beta.179 → 1.0.0-beta.180
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/README.md +3 -3
- package/package.json +15 -15
- package/src/lib/components/campaign-conversions-card.js +22 -4
- package/src/lib/components/campaign-conversions-card.js.map +1 -1
- package/src/lib/components/campaign-detail-card.js +3 -4
- package/src/lib/components/campaign-detail-card.js.map +1 -1
- package/src/lib/components/campaign-report-card.js +3 -3
- package/src/lib/components/campaign-report-card.js.map +1 -1
- package/src/lib/components/email-compose-card.d.ts +5 -2
- package/src/lib/components/email-compose-card.js +2 -3
- package/src/lib/components/email-compose-card.js.map +1 -1
- package/src/lib/components/email-detail.d.ts +5 -2
- package/src/lib/components/email-detail.js +4 -4
- package/src/lib/components/email-detail.js.map +1 -1
- package/src/lib/components/email-messages-widget.d.ts +17 -2
- package/src/lib/components/email-messages-widget.js +33 -2
- package/src/lib/components/email-messages-widget.js.map +1 -1
- package/src/lib/components/emails-list-card.d.ts +9 -6
- package/src/lib/components/emails-list-card.js +9 -9
- package/src/lib/components/emails-list-card.js.map +1 -1
- package/src/lib/components/host-experiments-card.component.d.ts +2 -0
- package/src/lib/components/host-experiments-card.component.js +20 -25
- package/src/lib/components/host-experiments-card.component.js.map +1 -1
- package/src/lib/components/host-marketing-summary-card.component.d.ts +15 -0
- package/src/lib/components/host-marketing-summary-card.component.js +59 -68
- package/src/lib/components/host-marketing-summary-card.component.js.map +1 -1
- package/src/lib/components/host-overlays-card.component.js +15 -37
- package/src/lib/components/host-overlays-card.component.js.map +1 -1
- package/src/lib/components/inbox-campaigns-widget.d.ts +23 -0
- package/src/lib/components/inbox-campaigns-widget.js +63 -0
- package/src/lib/components/inbox-campaigns-widget.js.map +1 -0
- package/src/lib/components/marketing-aggregates.d.ts +95 -0
- package/src/lib/components/marketing-aggregates.js +114 -0
- package/src/lib/components/marketing-aggregates.js.map +1 -0
- package/src/lib/components/marketing-console-page.js +32 -27
- package/src/lib/components/marketing-console-page.js.map +1 -1
- package/src/lib/components/marketing-console-sections.d.ts +24 -13
- package/src/lib/components/marketing-console-sections.js +37 -12
- package/src/lib/components/marketing-console-sections.js.map +1 -1
- package/src/lib/components/org-conversions-section.d.ts +32 -0
- package/src/lib/components/org-conversions-section.js +130 -0
- package/src/lib/components/org-conversions-section.js.map +1 -0
- package/src/lib/components/org-experiments-card.d.ts +32 -0
- package/src/lib/components/org-experiments-card.js +407 -0
- package/src/lib/components/org-experiments-card.js.map +1 -0
- package/src/lib/components/org-marketing-overview-card.d.ts +34 -0
- package/src/lib/components/org-marketing-overview-card.js +342 -0
- package/src/lib/components/org-marketing-overview-card.js.map +1 -0
- package/src/lib/components/org-overlays-card.d.ts +35 -0
- package/src/lib/components/org-overlays-card.js +345 -0
- package/src/lib/components/org-overlays-card.js.map +1 -0
- package/src/lib/components/org-site-picker-button.d.ts +26 -0
- package/src/lib/components/org-site-picker-button.js +104 -0
- package/src/lib/components/org-site-picker-button.js.map +1 -0
- package/src/lib/components/overlay-labels.d.ts +38 -0
- package/src/lib/components/overlay-labels.js +55 -0
- package/src/lib/components/overlay-labels.js.map +1 -0
- package/src/lib/components/use-emails-hub-path.d.ts +17 -8
- package/src/lib/components/use-emails-hub-path.js +29 -11
- package/src/lib/components/use-emails-hub-path.js.map +1 -1
- package/src/lib/components/use-one-shot-reads.d.ts +55 -0
- package/src/lib/components/use-one-shot-reads.js +168 -0
- package/src/lib/components/use-one-shot-reads.js.map +1 -0
- package/src/lib/model/experiments.d.ts +35 -0
- package/src/lib/model/experiments.js +34 -0
- package/src/lib/model/experiments.js.map +1 -1
- package/src/lib/model/overlays.d.ts +13 -2
- package/src/lib/model/overlays.js +18 -6
- package/src/lib/model/overlays.js.map +1 -1
- package/src/lib/plugin.js +13 -8
- package/src/lib/plugin.js.map +1 -1
|
@@ -18,7 +18,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
18
18
|
import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
|
|
19
19
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
20
20
|
import { checkEntitlement, createResourceUid, pluginDocsHelp } from "@aglyn/aglyn";
|
|
21
|
-
import {
|
|
21
|
+
import { compareOverlayPrecedence, overlayStatus } from "../model/index.js";
|
|
22
22
|
import { mdiChevronDown, mdiChevronUp } from "@aglyn/shared-data-mdi";
|
|
23
23
|
import { CardDisplay, MdiIcon, useConfirmationContext } from "@aglyn/shared-ui-jsx";
|
|
24
24
|
import { ScrollTable } from "@aglyn/shared-ui-jsx/components/scroll-table.component";
|
|
@@ -28,6 +28,7 @@ import { Alert, Button, Chip, IconButton, Dialog, DialogActions, DialogContent,
|
|
|
28
28
|
import { collection, deleteDoc, doc, limit, orderBy, query, setDoc } from "firebase/firestore";
|
|
29
29
|
import { useState } from "react";
|
|
30
30
|
import { useFirestore, useFirestoreCollection, useHostActivityLogger, writeGuardedBySeed } from "@aglyn/tenant-feature-instance";
|
|
31
|
+
import { overlayDisplayName, overlayEngagementLabel, overlayPagesLabel, overlayWindowLabel } from "./overlay-labels.js";
|
|
31
32
|
import { OVERLAY_COPY_HELPER_TEXT, shownAsTypedHelperText, useOverlayCopyEditor } from "./use-overlay-copy-editor.js";
|
|
32
33
|
/**
|
|
33
34
|
* How many overlays the card reads.
|
|
@@ -133,10 +134,7 @@ function parsePatterns(value) {
|
|
|
133
134
|
const truncated = ((_ref = overlayDocs == null ? void 0 : overlayDocs.length) != null ? _ref : 0) > CEILING;
|
|
134
135
|
const overlays = [
|
|
135
136
|
...overlayDocs != null ? overlayDocs : []
|
|
136
|
-
].slice(0, CEILING).sort(
|
|
137
|
-
var _a_order, _b_order, _a_name, _b_name;
|
|
138
|
-
return ((_a_order = a.order) != null ? _a_order : 0) - ((_b_order = b.order) != null ? _b_order : 0) || String((_a_name = a.name) != null ? _a_name : '').localeCompare(String((_b_name = b.name) != null ? _b_name : ''));
|
|
139
|
-
});
|
|
137
|
+
].slice(0, CEILING).sort(compareOverlayPrecedence);
|
|
140
138
|
const [editor, setEditor] = useState(null);
|
|
141
139
|
// The editor's copy is held in its stored form; the fields show it as typed.
|
|
142
140
|
const copy = useOverlayCopyEditor({
|
|
@@ -310,29 +308,13 @@ function parsePatterns(value) {
|
|
|
310
308
|
merge: true
|
|
311
309
|
});
|
|
312
310
|
};
|
|
313
|
-
// Engagement (AGL-271): lifetime counters the org beacon increments.
|
|
314
|
-
const statsLabel = (overlay)=>{
|
|
315
|
-
var _overlay_stats, _stats_impressions, _stats_clicks, _stats_dismissals;
|
|
316
|
-
const stats = (_overlay_stats = overlay.stats) != null ? _overlay_stats : {};
|
|
317
|
-
const impressions = (_stats_impressions = stats.impressions) != null ? _stats_impressions : 0;
|
|
318
|
-
const clicks = (_stats_clicks = stats.clicks) != null ? _stats_clicks : 0;
|
|
319
|
-
const dismissals = (_stats_dismissals = stats.dismissals) != null ? _stats_dismissals : 0;
|
|
320
|
-
if (!impressions && !clicks && !dismissals) return '—';
|
|
321
|
-
const parts = [
|
|
322
|
-
`${impressions.toLocaleString()} views`
|
|
323
|
-
];
|
|
324
|
-
if (clicks) parts.push(`${clicks.toLocaleString()} clicks`);
|
|
325
|
-
if (dismissals) parts.push(`${dismissals.toLocaleString()} dismissed`);
|
|
326
|
-
return parts.join(' · ');
|
|
327
|
-
};
|
|
328
311
|
const statusChip = (overlay)=>{
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
if (!overlayActiveAt(overlay, Date.now())) {
|
|
312
|
+
const status = overlayStatus(overlay, Date.now());
|
|
313
|
+
if (status === 'off') return /*#__PURE__*/ _jsx(Chip, {
|
|
314
|
+
size: "small",
|
|
315
|
+
label: "Off"
|
|
316
|
+
});
|
|
317
|
+
if (status === 'scheduled') {
|
|
336
318
|
return /*#__PURE__*/ _jsx(Chip, {
|
|
337
319
|
size: "small",
|
|
338
320
|
color: "info",
|
|
@@ -426,13 +408,10 @@ function parsePatterns(value) {
|
|
|
426
408
|
})
|
|
427
409
|
}),
|
|
428
410
|
/*#__PURE__*/ _jsx(TableBody, {
|
|
429
|
-
children: overlays.map((overlay)
|
|
430
|
-
var _ref, _overlay_name, _ref1, _ref2;
|
|
431
|
-
var _overlay_bar, _overlay_popup, _overlay_pathPatterns;
|
|
432
|
-
return /*#__PURE__*/ _jsxs(TableRow, {
|
|
411
|
+
children: overlays.map((overlay)=>/*#__PURE__*/ _jsxs(TableRow, {
|
|
433
412
|
children: [
|
|
434
413
|
/*#__PURE__*/ _jsx(TableCell, {
|
|
435
|
-
children: (
|
|
414
|
+
children: overlayDisplayName(overlay)
|
|
436
415
|
}),
|
|
437
416
|
/*#__PURE__*/ _jsx(TableCell, {
|
|
438
417
|
children: overlay.kind === 'bar' ? 'Bar' : 'Popup'
|
|
@@ -441,16 +420,16 @@ function parsePatterns(value) {
|
|
|
441
420
|
children: statusChip(overlay)
|
|
442
421
|
}),
|
|
443
422
|
/*#__PURE__*/ _jsx(TableCell, {
|
|
444
|
-
children:
|
|
423
|
+
children: overlayWindowLabel(overlay)
|
|
445
424
|
}),
|
|
446
425
|
/*#__PURE__*/ _jsx(TableCell, {
|
|
447
|
-
children: (
|
|
426
|
+
children: overlayPagesLabel(overlay)
|
|
448
427
|
}),
|
|
449
428
|
/*#__PURE__*/ _jsx(TableCell, {
|
|
450
429
|
children: /*#__PURE__*/ _jsx(Typography, {
|
|
451
430
|
variant: "caption",
|
|
452
431
|
color: "text.secondary",
|
|
453
|
-
children:
|
|
432
|
+
children: overlayEngagementLabel(overlay.stats)
|
|
454
433
|
})
|
|
455
434
|
}),
|
|
456
435
|
/*#__PURE__*/ _jsxs(TableCell, {
|
|
@@ -505,8 +484,7 @@ function parsePatterns(value) {
|
|
|
505
484
|
]
|
|
506
485
|
})
|
|
507
486
|
]
|
|
508
|
-
}, overlay.$id)
|
|
509
|
-
})
|
|
487
|
+
}, overlay.$id))
|
|
510
488
|
})
|
|
511
489
|
]
|
|
512
490
|
})
|
|
@@ -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 { overlayActiveAt, type HostOverlay } 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 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(\n (a, b) =>\n (a.order ?? 0) - (b.order ?? 0) ||\n String(a.name ?? '').localeCompare(String(b.name ?? '')),\n )\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 // Engagement (AGL-271): lifetime counters the org beacon increments.\n const statsLabel = (overlay: OverlayDraft) => {\n const stats = overlay.stats ?? {}\n const impressions = stats.impressions ?? 0\n const clicks = stats.clicks ?? 0\n const dismissals = stats.dismissals ?? 0\n if (!impressions && !clicks && !dismissals) return '—'\n const parts = [`${impressions.toLocaleString()} views`]\n if (clicks) parts.push(`${clicks.toLocaleString()} clicks`)\n if (dismissals) parts.push(`${dismissals.toLocaleString()} dismissed`)\n return parts.join(' · ')\n }\n\n const statusChip = (overlay: OverlayDraft) => {\n if (overlay.enabled === false) {\n return <Chip size=\"small\" label=\"Off\" />\n }\n if (!overlayActiveAt(overlay, Date.now())) {\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>\n {overlay.name ??\n (overlay.kind === 'bar'\n ? (overlay.bar?.text ?? '').slice(0, 32)\n : (overlay.popup?.headline ?? '').slice(0, 32)) ??\n overlay.$id}\n </TableCell>\n <TableCell>\n {overlay.kind === 'bar' ? 'Bar' : 'Popup'}\n </TableCell>\n <TableCell>{statusChip(overlay)}</TableCell>\n <TableCell>\n {overlay.startAtMs || overlay.endAtMs\n ? `${\n overlay.startAtMs\n ? new Date(overlay.startAtMs).toLocaleDateString()\n : '…'\n } → ${\n overlay.endAtMs\n ? new Date(overlay.endAtMs).toLocaleDateString()\n : '…'\n }`\n : 'Always'}\n </TableCell>\n <TableCell>\n {overlay.pathPatterns?.length\n ? overlay.pathPatterns.join(', ')\n : 'All pages'}\n </TableCell>\n <TableCell>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {statsLabel(overlay)}\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","overlayActiveAt","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","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","a","b","order","name","localeCompare","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","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","neighborOrder","Promise","all","merge","handleToggle","statsLabel","stats","impressions","clicks","dismissals","parts","toLocaleString","push","join","statusChip","size","label","header","help","anchor","excerpt","contentGutterX","contentGutterY","contentBordered","severity","spacing","onClick","align","startAtMs","endAtMs","toLocaleDateString","pathPatterns","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","excludePathPatterns","pending","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;;;AAEA,SAEEA,gBAAgB,EAChBC,iBAAiB,EACjBC,cAAc,QACT,eAAc;AACrB,SAASC,eAAe,QAA0B,oBAAU;AAC5D,SAASC,cAAc,EAAEC,YAAY,QAAQ,yBAAwB;AACrE,SACEC,WAAW,EACXC,OAAO,EACPC,sBAAsB,QACjB,uBAAsB;AAC7B,SAASC,WAAW,QAAQ,yDAAwD;AACpF,SAASC,WAAW,QAAQ,8BAA6B;AACzD,SAASC,SAAS,QAAQ,+BAA8B;AACxD,SACEC,KAAK,EACLC,MAAM,EACNC,IAAI,EACJC,UAAU,EACVC,MAAM,EACNC,aAAa,EACbC,aAAa,EACbC,WAAW,EACXC,QAAQ,EACRC,KAAK,EACLC,MAAM,EACNC,SAAS,EACTC,SAAS,EACTC,SAAS,EACTC,QAAQ,EACRC,SAAS,EACTC,UAAU,QACL,gBAAe;AACtB,SACEC,UAAU,EACVC,SAAS,EACTC,GAAG,EACHC,KAAK,EACLC,OAAO,EACPC,KAAK,EACLC,MAAM,QACD,qBAAoB;AAC3B,SAASC,QAAQ,QAAQ,QAAO;AAChC,SACEC,YAAY,EACZC,sBAAsB,EACtBC,qBAAqB,EACrBC,kBAAkB,QACb,iCAAgC;AACvC,SACEC,wBAAwB,EACxBC,sBAAsB,EACtBC,oBAAoB,QACf,+BAA2B;AAUlC;;;;;;;CAOC,GACD,MAAMC,UAAU;AAEhB,MAAMC,YAA0B;IAC9BC,MAAM;IACNC,SAAS;IACTC,KAAK;QAAEC,aAAa;IAAK;AAC3B;AACA,MAAMC,cAA4B;IAChCJ,MAAM;IACNC,SAAS;IACTI,OAAO;QAAEC,SAAS;QAASC,cAAc;QAAGC,eAAe;IAAE;AAC/D;AAEA,SAASC,gBAAgBC,EAAW;IAClC,IAAI,CAACA,IAAI,OAAO;IAChB,MAAMC,OAAO,IAAIC,KAAKF;IACtB,MAAMG,MAAM,CAACC,QAAkBC,OAAOD,OAAOE,QAAQ,CAAC,GAAG;IACzD,OACE,GAAGL,KAAKM,WAAW,GAAG,CAAC,EAAEJ,IAAIF,KAAKO,QAAQ,KAAK,GAAG,CAAC,CAAC,GACpD,GAAGL,IAAIF,KAAKQ,OAAO,IAAI,CAAC,EAAEN,IAAIF,KAAKS,QAAQ,IAAI,CAAC,EAAEP,IAAIF,KAAKU,UAAU,KAAK;AAE9E;AAEA,SAASC,kBAAkBR,KAAa;IACtC,IAAI,CAACA,OAAO,OAAOS;IACnB,MAAMb,KAAK,IAAIE,KAAKE,OAAOU,OAAO;IAClC,OAAOC,OAAOC,QAAQ,CAAChB,MAAMA,KAAKa;AACpC;AAEA,SAASI,cAAcb,KAAa;IAClC,OAAOA,MACJc,KAAK,CAAC,KACNC,GAAG,CAAC,CAACC,UAAYA,QAAQC,IAAI,IAC7BC,MAAM,CAACC;AACZ;AAEA;;;;;;;CAOC,GACD,OAAO,SAASC,iBAAiBC,KAA4B;cAmctCC,cAgBDxC,8CAqDAA,oEA4HEwC,sBAYAA;QAhkBjBA,aAAmBA,eAAyBA,gBAIHA,cACCA,gBACJA,gBAuXJA,cAadA,cAOEA,cAQAA,cAYYA,gBAaAA,gBAcZA,gBAQAA,gBAYAA,gBAkBJA,gBAIIA,iBAoBAA,iBAaRA,iBAKUA,iBAmEjBA,cACAA,iBACAA;IApqBZ,MAAM,EAAEC,MAAM,EAAE,GAAGF;IACnB,MAAMG,YAAY/C;IAClB,MAAM,EAAEgD,GAAG,EAAE,GAAGJ;IAChB,MAAM,EAAEK,eAAe,EAAE,GAAG5E;IAC5B,MAAM,EAAE6E,OAAO,EAAE,GAAG/E;IACpB,MAAMgF,cAAcjD,sBAAsB4C;IAC1C,MAAMM,WAAWzF,iBAAiBqF,KAAK;IAEvC,MAAM,EACJK,MAAMC,WAAW,EACjBC,QAAQC,cAAc,EACtB;;;;;;;;KAQC,GACDC,WAAWC,iBAAiB,EAC7B,GAAGzD,uBACF,IACEJ,MACEL,WAAWuD,WAAW,SAASD,QAAQ,aACvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;SA8BC,GACDlD,QAAQ,SACR,qEAAqE;QACrE,iDAAiD;QACjDD,MAAMY,UAAU,KAEpB;QAACwC;QAAWD;KAAO,EACnB;QAAEa,SAAS;IAAM;IAEnB,MAAMC,YAAY,SAACN,+BAAAA,YAAaO,MAAM,mBAAI,KAAKtD;IAC/C,MAAMuD,WAA2B;WAAKR,sBAAAA,cAAe,EAAE;KAAE,CACtDS,KAAK,CAAC,GAAGxD,SACTyD,IAAI,CACH,CAACC,GAAGC;YACDD,UAAiBC,UACXD,SAAmCC;eAD1C,EAACD,WAAAA,EAAEE,KAAK,YAAPF,WAAW,OAAMC,WAAAA,EAAEC,KAAK,YAAPD,WAAW,MAC7B1C,QAAOyC,UAAAA,EAAEG,IAAI,YAANH,UAAU,IAAII,aAAa,CAAC7C,QAAO0C,UAAAA,EAAEE,IAAI,YAANF,UAAU;;IAG1D,MAAM,CAACrB,QAAQyB,UAAU,GAAGvE,SAA8B;IAC1D,6EAA6E;IAC7E,MAAMwE,OAAOjE,qBAAqB;QAChCwC;QACA0B,YACEpB,YACA;YAACP,2BAAAA,cAAAA,OAAQlC,GAAG,qBAAXkC,YAAa4B,IAAI;YAAE5B,2BAAAA,gBAAAA,OAAQ/B,KAAK,qBAAb+B,cAAe6B,QAAQ;YAAE7B,2BAAAA,iBAAAA,OAAQ/B,KAAK,qBAAb+B,eAAe8B,IAAI;SAAC,CAACC,IAAI,CACpE,CAACH,OAASA,wBAAAA,KAAMI,QAAQ,CAAC;IAE/B;IACA,MAAMC,sBAAsBP,KAAKQ,YAAY,CAAClC,2BAAAA,eAAAA,OAAQlC,GAAG,qBAAXkC,aAAa4B,IAAI;IAC/D,MAAMO,uBAAuBT,KAAKQ,YAAY,CAAClC,2BAAAA,iBAAAA,OAAQ/B,KAAK,qBAAb+B,eAAe6B,QAAQ;IACtE,MAAMO,mBAAmBV,KAAKQ,YAAY,CAAClC,2BAAAA,iBAAAA,OAAQ/B,KAAK,qBAAb+B,eAAe8B,IAAI;IAC9D,MAAMO,QAAQ,CAACC,UACbb,UAAU,CAACc,WAAcA,WAAW,aAAKA,UAAaD,WAAYC;IACpE,MAAMC,WAAW,CAACF,UAChBb,UAAU,CAACc;gBAEqBA;mBAD9BA,WACI,aAAKA;gBAAUzE,KAAK,cAAMyE,gBAAAA,SAASzE,GAAG,YAAZyE,gBAAgB,CAAC,GAAOD;iBAClDC;;IAER,MAAME,aAAa,CAACH,UAClBb,UAAU,CAACc;gBAEuBA;mBADhCA,WACI,aAAKA;gBAAUtE,OAAO,cAAMsE,kBAAAA,SAAStE,KAAK,YAAdsE,kBAAkB,CAAC,GAAOD;iBACtDC;;IAGR,MAAMG,aAAa;YAcN1C;YAZmBA,kBAAAA,aAMEA,oBAAAA;QAPhC,IAAI,CAACA,QAAQ;QACb,IAAIA,OAAOpC,IAAI,KAAK,SAAS,GAACoC,cAAAA,OAAOlC,GAAG,sBAAVkC,mBAAAA,YAAY4B,IAAI,qBAAhB5B,iBAAkBL,IAAI,KAAI;YACtD,OAAO,KAAKS,gBAAgB,sBAAsB;gBAChDuC,SAAS;gBACTC,SAAS;YACX;QACF;QACA,IAAI5C,OAAOpC,IAAI,KAAK,WAAW,GAACoC,gBAAAA,OAAO/B,KAAK,sBAAZ+B,qBAAAA,cAAc8B,IAAI,qBAAlB9B,mBAAoBL,IAAI,KAAI;YAC1D,OAAO,KAAKS,gBAAgB,wBAAwB;gBAClDuC,SAAS;gBACTC,SAAS;YACX;QACF;QACA,MAAMC,MAAK7C,cAAAA,OAAO8C,GAAG,YAAV9C,cAAcjF;QACzB,MAAM,EACJ+H,KAAKC,QAAQ,EACbC,WAAWC,UAAU,EACrBC,WAAWC,UAAU,EAEtB,GAAGnD,QADCoD,2CACDpD;;;;;QACJ,IAAI;gBAkBSA;YAjBX,sEAAsE;YACtE,uEAAuE;YACvE,MAAMqD,UAAUC,KAAKC,KAAK,CACxBD,KAAKE,SAAS,CAAC,aACVJ,SACCA,QAAQtF,GAAG,GACX;gBAAEA,KAAK,aAAKsF,QAAQtF,GAAG;oBAAE8D,MAAMF,KAAK+B,MAAM,CAACL,QAAQtF,GAAG,CAAC8D,IAAI;;YAAI,IAC/D,CAAC,GACDwB,QAAQnF,KAAK,GACb;gBACEA,OAAO,aACFmF,QAAQnF,KAAK;oBAChB4D,UAAUH,KAAK+B,MAAM,CAACL,QAAQnF,KAAK,CAAC4D,QAAQ;oBAC5CC,MAAMJ,KAAK+B,MAAM,CAACL,QAAQnF,KAAK,CAAC6D,IAAI;;YAExC,IACA,CAAC;gBACLP,MAAM,EAACvB,eAAAA,OAAOuB,IAAI,YAAXvB,eAAe,IAAIL,IAAI,MAAM;;YAGxC;;;;;;;;;;;;;;;;;OAiBC,GACD,MAAM+D,UAAU,MAAMpG,mBACpB;gBACEqG,SAAS;gBACTC,YAAY/D,QAAQG,OAAO8C,GAAG,KAAKnC,mBAAmB;gBACtDC,WAAWf,QAAQG,OAAO8C,GAAG,KAAKjC;YACpC,GACA;gBACE,MAAM5D,OAAOJ,IAAIqD,WAAW,SAASD,QAAQ,YAAY4C,KAAK,aACzDQ;oBACHH,WAAWzH,UAAUoI,GAAG;mBACpB7D,OAAO8C,GAAG,GAAG,CAAC,IAAI;oBAAEE,WAAWvH,UAAUoI,GAAG;gBAAG;gBAErDvD,YAAYN,OAAO8C,GAAG,GAAG,oBAAoB,mBAAmB;oBAC9DgB,MAAM;oBACNjB;mBACI7C,OAAOuB,IAAI,GAAG;oBAAEA,MAAMvB,OAAOuB,IAAI;gBAAC,IAAI,CAAC;YAE/C;YAEF,kEAAkE;YAClE,IAAI,CAACmC,QAAQK,EAAE,EAAE;gBACf,OAAO,KAAK3D,gBAAgBsD,QAAQM,OAAO,EAAE;oBAC3CrB,SAAS;oBACTC,SAAS;gBACX;YACF;YACAxC,gBAAgB,iBAAiB;gBAAEuC,SAAS;gBAAWC,SAAS;YAAM;YACtEnB,UAAU;QACZ,EAAE,OAAOwC,OAAO;YACdC,QAAQD,KAAK,CAACA;YACd7D,gBAAgB,yBAAyB;gBAAEuC,SAAS;YAAQ;QAC9D;IACF;IAEA,MAAMwB,eAAe,OAAOC;YAIPA;QAHnB,IAAI,CAACA,QAAQtB,GAAG,EAAE;QAClB,MAAMuB,YAAY,MAAMhE,QAAQ;YAC9BiE,OAAO;YACPC,aAAa,CAAC,CAAC,GAAEH,gBAAAA,QAAQ7C,IAAI,YAAZ6C,gBAAgBA,QAAQtB,GAAG,CAAC,4BAA4B,CAAC;YAC1E0B,kBAAkB;YAClBC,yBAAyB;gBAAEC,OAAO;YAAQ;QAC5C,EACE,+DAA+D;QAC/D,0DAA0D;SACzDC,IAAI,CAAC,IAAM,MACXC,KAAK,CAAC,IAAM;QACf,IAAI,CAACP,WAAW;QAChB,MAAMzH,UAAUC,IAAIqD,WAAW,SAASD,QAAQ,YAAYmE,QAAQtB,GAAG;QACvE1C,gBAAgB,mBAAmB;YAAEuC,SAAS;YAAWC,SAAS;QAAM;QACxEtC,YAAY,mBAAmB;YAC7BwD,MAAM;YACNjB,IAAIuB,QAAQtB,GAAG;WACXsB,QAAQ7C,IAAI,GAAG;YAAEA,MAAM6C,QAAQ7C,IAAI;QAAC,IAAI,CAAC;IAEjD;IAEA,qEAAqE;IACrE,6DAA6D;IAC7D,MAAMsD,aAAa,OAAOC,OAAeC;YAIlBC,gBACCC;QAJtB,MAAMA,WAAWhE,QAAQ,CAAC6D,QAAQC,UAAU;QAC5C,MAAMC,UAAU/D,QAAQ,CAAC6D,MAAM;QAC/B,IAAI,EAACG,4BAAAA,SAAUnC,GAAG,KAAI,EAACkC,2BAAAA,QAASlC,GAAG,GAAE;QACrC,MAAMoC,gBAAeF,iBAAAA,QAAQ1D,KAAK,YAAb0D,iBAAiBF;QACtC,MAAMK,iBAAgBF,kBAAAA,SAAS3D,KAAK,YAAd2D,kBAAkBH,QAAQC;QAChD,MAAMK,QAAQC,GAAG,CAAC;YAChBpI,OACEJ,IAAIqD,WAAW,SAASD,QAAQ,YAAY+E,QAAQlC,GAAG,GACvD;gBAAExB,OAAO6D,kBAAkBD,eAAeC,gBAAgBJ,YAAYI;YAAc,GACpF;gBAAEG,OAAO;YAAK;YAEhBrI,OACEJ,IAAIqD,WAAW,SAASD,QAAQ,YAAYgF,SAASnC,GAAG,GACxD;gBAAExB,OAAO4D;YAAa,GACtB;gBAAEI,OAAO;YAAK;SAEjB,EAAEV,KAAK,CAACV,QAAQD,KAAK;IACxB;IAEA,MAAMsB,eAAe,OAAOnB;QAC1B,IAAI,CAACA,QAAQtB,GAAG,EAAE;QAClB,MAAM7F,OACJJ,IAAIqD,WAAW,SAASD,QAAQ,YAAYmE,QAAQtB,GAAG,GACvD;YAAEjF,SAASuG,QAAQvG,OAAO,KAAK;QAAM,GACrC;YAAEyH,OAAO;QAAK;IAElB;IAEA,qEAAqE;IACrE,MAAME,aAAa,CAACpB;YACJA,gBACMqB,oBACLA,eACIA;QAHnB,MAAMA,SAAQrB,iBAAAA,QAAQqB,KAAK,YAAbrB,iBAAiB,CAAC;QAChC,MAAMsB,eAAcD,qBAAAA,MAAMC,WAAW,YAAjBD,qBAAqB;QACzC,MAAME,UAASF,gBAAAA,MAAME,MAAM,YAAZF,gBAAgB;QAC/B,MAAMG,cAAaH,oBAAAA,MAAMG,UAAU,YAAhBH,oBAAoB;QACvC,IAAI,CAACC,eAAe,CAACC,UAAU,CAACC,YAAY,OAAO;QACnD,MAAMC,QAAQ;YAAC,GAAGH,YAAYI,cAAc,GAAG,MAAM,CAAC;SAAC;QACvD,IAAIH,QAAQE,MAAME,IAAI,CAAC,GAAGJ,OAAOG,cAAc,GAAG,OAAO,CAAC;QAC1D,IAAIF,YAAYC,MAAME,IAAI,CAAC,GAAGH,WAAWE,cAAc,GAAG,UAAU,CAAC;QACrE,OAAOD,MAAMG,IAAI,CAAC;IACpB;IAEA,MAAMC,aAAa,CAAC7B;QAClB,IAAIA,QAAQvG,OAAO,KAAK,OAAO;YAC7B,qBAAO,KAACjC;gBAAKsK,MAAK;gBAAQC,OAAM;;QAClC;QACA,IAAI,CAAClL,gBAAgBmJ,SAAS5F,KAAKqF,GAAG,KAAK;YACzC,qBAAO,KAACjI;gBAAKsK,MAAK;gBAAQxB,OAAM;gBAAOyB,OAAM;;QAC/C;QACA,qBAAO,KAACvK;YAAKsK,MAAK;YAAQxB,OAAM;YAAUyB,OAAM;;IAClD;IAEA,qBACE,MAAC/K;QACCgL,QAAO;QACPC,MAAMrL,eAAe,qBAAqB;YACxCsL,QAAQ;YACRC,SACE,+DACA;QACJ;QACAC,cAAc;QACdC,cAAc;QACdC,iBAAgB;;YAEf,CAACnG,yBACA,KAAC7E;gBAAMiL,UAAS;0BACb,gEACC;+BAGJ,MAACxK;gBAAMyK,SAAS;;kCACd,KAAClK;wBAAWiG,SAAQ;wBAAQ+B,OAAM;kCAC/B,kEACC,iEACA;;kCAEJ,MAACvI;wBAAM4I,WAAU;wBAAM6B,SAAS;;0CAC9B,KAACjL;gCACCuK,MAAK;gCACLxB,OAAM;gCACN/B,SAAQ;gCACRkE,SAAS,IAAMpF,UAAU,aAAK9D;0CAE7B;;0CAEH,KAAChC;gCACCuK,MAAK;gCACLxB,OAAM;gCACN/B,SAAQ;gCACRkE,SAAS,IAAMpF,UAAU,aAAKzD;0CAE7B;;;;oBAGJ+C,0BACC,KAACrF;wBAAMiL,UAAS;kCACb,CAAC,QAAQ,EAAEjJ,QAAQ,0CAA0C,CAAC,GAC7D,iEACA;yBAEF;oBACHuD,SAASD,MAAM,KAAK,kBACnB,KAACtE;wBAAWiG,SAAQ;wBAAQ+B,OAAM;kCAC/B,6DACC;uCAGJ,MAACnJ;wBAAY2K,MAAK;;0CAChB,KAAC3J;0CACC,cAAA,MAACC;;sDACC,KAACF;sDAAW;;sDACZ,KAACA;sDAAW;;sDACZ,KAACA;sDAAW;;sDACZ,KAACA;sDAAW;;sDACZ,KAACA;sDAAW;;sDACZ,KAACA;sDAAW;;sDACZ,KAACA;4CAAUwK,OAAM;sDAAS;;;;;0CAG9B,KAACzK;0CACE4E,SAASxB,GAAG,CAAC,CAAC2E;wCAGRA,MAAAA;wCAEMA,cACAA,gBAqBNA;yDA1BL,MAAC5H;;0DACC,KAACF;2DACE8H,QAAAA,gBAAAA,QAAQ7C,IAAI,YAAZ6C,gBACEA,QAAQxG,IAAI,KAAK,QACd,WAACwG,eAAAA,QAAQtG,GAAG,qBAAXsG,aAAaxC,IAAI,oBAAI,IAAIV,KAAK,CAAC,GAAG,MACnC,WAACkD,iBAAAA,QAAQnG,KAAK,qBAAbmG,eAAevC,QAAQ,oBAAI,IAAIX,KAAK,CAAC,GAAG,eAH9CkD,OAICA,QAAQtB,GAAG;;0DAEf,KAACxG;0DACE8H,QAAQxG,IAAI,KAAK,QAAQ,QAAQ;;0DAEpC,KAACtB;0DAAW2J,WAAW7B;;0DACvB,KAAC9H;0DACE8H,QAAQ2C,SAAS,IAAI3C,QAAQ4C,OAAO,GACjC,GACE5C,QAAQ2C,SAAS,GACb,IAAIvI,KAAK4F,QAAQ2C,SAAS,EAAEE,kBAAkB,KAC9C,IACL,GAAG,EACF7C,QAAQ4C,OAAO,GACX,IAAIxI,KAAK4F,QAAQ4C,OAAO,EAAEC,kBAAkB,KAC5C,KACJ,GACF;;0DAEN,KAAC3K;0DACE8H,EAAAA,wBAAAA,QAAQ8C,YAAY,qBAApB9C,sBAAsBpD,MAAM,IACzBoD,QAAQ8C,YAAY,CAAClB,IAAI,CAAC,QAC1B;;0DAEN,KAAC1J;0DACC,cAAA,KAACI;oDAAWiG,SAAQ;oDAAU+B,OAAM;8DACjCc,WAAWpB;;;0DAGhB,MAAC9H;gDAAUwK,OAAM;;kEACf,KAACjL;wDACCqK,MAAK;wDACLiB,cAAW;wDACXC,UAAUnG,SAASoG,OAAO,CAACjD,aAAa;wDACxCyC,SAAS,IACP,KAAKhC,WAAW5D,SAASoG,OAAO,CAACjD,UAAU,CAAC;kEAG9C,cAAA,KAAC/I;4DAAQiM,MAAMnM,aAAamM,IAAI;4DAAEC,IAAI;gEAAEC,UAAU;4DAAO;;;kEAE3D,KAAC3L;wDACCqK,MAAK;wDACLiB,cAAW;wDACXC,UACEnG,SAASoG,OAAO,CAACjD,aAAanD,SAASD,MAAM,GAAG;wDAElD6F,SAAS,IACP,KAAKhC,WAAW5D,SAASoG,OAAO,CAACjD,UAAU;kEAG7C,cAAA,KAAC/I;4DACCiM,MAAMpM,eAAeoM,IAAI;4DACzBC,IAAI;gEAAEC,UAAU;4DAAO;;;kEAG3B,KAACpL;wDACC8J,MAAK;wDACLuB,SAASrD,QAAQvG,OAAO,KAAK;wDAC7B6J,UAAU,IAAMnC,aAAanB;wDAC7BuD,WAAW;4DAAEC,OAAO;gEAAE,cAAc;4DAAU;wDAAE;;kEAElD,KAACjM;wDACCuK,MAAK;wDACLxB,OAAM;wDACNmC,SAAS,IAAMpF,UAAU,aAAK2C;kEAE7B;;kEAEH,KAACzI;wDACCuK,MAAK;wDACLxB,OAAM;wDACNmC,SAAS,IAAM1C,aAAaC;kEAE3B;;;;;uCA/EQA,QAAQtB,GAAG;;;;;;;0BA0FtC,MAAChH;gBACC+L,MAAMhI,QAAQG;gBACd8H,SAAS,IAAMrG,UAAU;gBACzBsG,UAAS;gBACTC,SAAS;;kCAET,KAAC/L;kCACE+D,CAAAA,0BAAAA,OAAQ8C,GAAG,IACR,iBACA9C,CAAAA,0BAAAA,OAAQpC,IAAI,MAAK,QACf,yBACA;;kCAER,KAAC5B;kCACEgE,uBACC,MAAC7D;4BAAMyK,SAAS;4BAAGW,IAAI;gCAAEU,IAAI;4BAAE;;8CAC7B,KAACxL;oCACCyJ,MAAK;oCACLC,OAAM;oCACNzH,KAAK,GAAEsB,eAAAA,OAAOuB,IAAI,YAAXvB,eAAe;oCACtB0H,UAAU,CAACQ,QAAU7F,MAAM;4CAAEd,MAAM2G,MAAMC,MAAM,CAACzJ,KAAK;wCAAC;oCACtD0J,YAAW;;gCAEZpI,OAAOpC,IAAI,KAAK,sBACf;;sDACE,KAACnB;4CACCyJ,MAAK;4CACLC,OAAM;4CACNkC,QAAQ;4CACR3J,OAAOgD,KAAK4G,QAAQ,EAACtI,eAAAA,OAAOlC,GAAG,qBAAVkC,aAAY4B,IAAI;4CACrC8F,UAAU,CAACQ,QACT1F,SAAS;oDAAEZ,MAAMF,KAAK+B,MAAM,CAACyE,MAAMC,MAAM,CAACzJ,KAAK;gDAAE;4CAEnDuF,OAAOhC,oBAAoBjB,MAAM,GAAG;4CACpCoH,UAAU,GACR5K,0BAAAA,uBAAuByE,gCAAvBzE,0BACAD;;sDAGJ,KAACd;4CACCyJ,MAAK;4CACLC,OAAM;4CACNzH,KAAK,YAAEsB,eAAAA,OAAOlC,GAAG,qBAAVkC,aAAYuI,IAAI,oBAAI;4CAC3Bb,UAAU,CAACQ,QAAU1F,SAAS;oDAAE+F,MAAML,MAAMC,MAAM,CAACzJ,KAAK;gDAAC;;sDAE3D,MAACvC;4CAAM4I,WAAU;4CAAM6B,SAAS;;8DAC9B,KAACnK;oDACCyJ,MAAK;oDACLC,OAAM;oDACNzH,KAAK,YAAEsB,eAAAA,OAAOlC,GAAG,qBAAVkC,aAAYwI,eAAe,oBAAI;oDACtCd,UAAU,CAACQ,QACT1F,SAAS;4DAAEgG,iBAAiBN,MAAMC,MAAM,CAACzJ,KAAK;wDAAC;;8DAGnD,KAACjC;oDACCyJ,MAAK;oDACLC,OAAM;oDACNzH,KAAK,YAAEsB,eAAAA,OAAOlC,GAAG,qBAAVkC,aAAYyI,SAAS,oBAAI;oDAChCf,UAAU,CAACQ,QACT1F,SAAS;4DAAEiG,WAAWP,MAAMC,MAAM,CAACzJ,KAAK;wDAAC;;;;;mDAMjD;;sDACE,KAACjC;4CACCyJ,MAAK;4CACLC,OAAM;4CACNzH,OAAOgD,KAAK4G,QAAQ,EAACtI,iBAAAA,OAAO/B,KAAK,qBAAZ+B,eAAc6B,QAAQ;4CAC3C6F,UAAU,CAACQ,QACTzF,WAAW;oDAAEZ,UAAUH,KAAK+B,MAAM,CAACyE,MAAMC,MAAM,CAACzJ,KAAK;gDAAE;4CAEzDuF,OAAO9B,qBAAqBnB,MAAM,GAAG;4CACrCoH,YAAY5K,uBAAuB2E;;sDAErC,KAAC1F;4CACCyJ,MAAK;4CACLC,OAAM;4CACNkC,QAAQ;4CACRK,SAAS;4CACTC,SAAS;4CACTjK,OAAOgD,KAAK4G,QAAQ,EAACtI,iBAAAA,OAAO/B,KAAK,qBAAZ+B,eAAc8B,IAAI;4CACvC4F,UAAU,CAACQ,QACTzF,WAAW;oDAAEX,MAAMJ,KAAK+B,MAAM,CAACyE,MAAMC,MAAM,CAACzJ,KAAK;gDAAE;4CAErDuF,OAAO7B,iBAAiBpB,MAAM,GAAG;4CACjCoH,UAAU,GACR5K,2BAAAA,uBAAuB4E,6BAAvB5E,2BACAD;;sDAGJ,MAACpB;4CAAM4I,WAAU;4CAAM6B,SAAS;;8DAC9B,KAACnK;oDACCyJ,MAAK;oDACLC,OAAM;oDACNzH,KAAK,YAAEsB,iBAAAA,OAAO/B,KAAK,qBAAZ+B,eAAc4I,QAAQ,oBAAI;oDACjClB,UAAU,CAACQ,QACTzF,WAAW;4DAAEmG,UAAUV,MAAMC,MAAM,CAACzJ,KAAK;wDAAC;;8DAG9C,KAACjC;oDACCyJ,MAAK;oDACLC,OAAM;oDACNzH,KAAK,YAAEsB,iBAAAA,OAAO/B,KAAK,qBAAZ+B,eAAc6I,OAAO,oBAAI;oDAChCnB,UAAU,CAACQ,QACTzF,WAAW;4DAAEoG,SAASX,MAAMC,MAAM,CAACzJ,KAAK;wDAAC;;;;sDAI/C,MAACvC;4CAAM4I,WAAU;4CAAM6B,SAAS;;8DAC9B,MAACnK;oDACCqM,MAAM;oDACN5C,MAAK;oDACLC,OAAM;oDACNoB,IAAI;wDAAEwB,UAAU;oDAAI;oDACpBrK,KAAK,YAAEsB,iBAAAA,OAAO/B,KAAK,qBAAZ+B,eAAc9B,OAAO,oBAAI;oDAChCwJ,UAAU,CAACQ,QACTzF,WAAW;4DACTvE,SAASgK,MAAMC,MAAM,CAClBzJ,KAAK;wDAGV;;sEAGF,KAACxC;4DAASwC,OAAM;sEAAS;;sEACzB,KAACxC;4DAASwC,OAAM;sEAAU;;sEAC1B,KAACxC;4DAASwC,OAAM;sEAAQ;;;;8DAE1B,KAACjC;oDACCyJ,MAAK;oDACLpC,MAAK;oDACLqC,OACE,WAACnG,iBAAAA,OAAO/B,KAAK,qBAAZ+B,eAAc9B,OAAO,oBAAI,aAAa,WACnC,aACA;oDAENQ,KAAK,YAAEsB,kBAAAA,OAAO/B,KAAK,qBAAZ+B,gBAAc7B,YAAY,oBAAI;oDACrCuJ,UAAU,CAACQ,QACTzF,WAAW;4DACTtE,cAAckB,OAAO6I,MAAMC,MAAM,CAACzJ,KAAK;wDACzC;;8DAYJ,MAACjC;oDACCqM,MAAM;oDACN5C,MAAK;oDACLC,OAAM;oDACNzH,OAAOsB,EAAAA,kBAAAA,OAAO/B,KAAK,qBAAZ+B,gBAAcgJ,cAAc,IAAG,YAAY;oDAClDtB,UAAU,CAACQ,QACTzF,WAAW;4DACTuG,gBAAgBd,MAAMC,MAAM,CAACzJ,KAAK,KAAK;wDACzC;oDAEF6I,IAAI;wDAAEwB,UAAU;oDAAI;;sEAEpB,KAAC7M;4DAASwC,OAAM;sEACb;;sEAEH,KAACxC;4DAASwC,OAAM;sEAAQ;;;;gDAEzBsB,EAAAA,kBAAAA,OAAO/B,KAAK,qBAAZ+B,gBAAcgJ,cAAc,IAAG,qBAC9B,KAACvM;oDACCyJ,MAAK;oDACLpC,MAAK;oDACLqC,OAAM;oDACNzH,KAAK,YAAEsB,kBAAAA,OAAO/B,KAAK,qBAAZ+B,gBAAc5B,aAAa,oBAAI;oDACtCsJ,UAAU,CAACQ,QACTzF,WAAW;4DACTrE,eAAeiB,OAAO6I,MAAMC,MAAM,CAACzJ,KAAK;wDAC1C;;;;;;8CAOZ,MAACvC;oCAAM4I,WAAU;oCAAM6B,SAAS;;sDAC9B,KAACnK;4CACCyJ,MAAK;4CACLpC,MAAK;4CACLqC,OAAM;4CACNwB,WAAW;gDAAEsB,YAAY;oDAAEC,QAAQ;gDAAK;4CAAE;4CAC1CxK,OAAOL,gBAAgB2B,OAAO+G,SAAS;4CACvCW,UAAU,CAACQ,QACT7F,MAAM;oDAAE0E,WAAW7H,kBAAkBgJ,MAAMC,MAAM,CAACzJ,KAAK;gDAAE;;sDAG7D,KAACjC;4CACCyJ,MAAK;4CACLpC,MAAK;4CACLqC,OAAM;4CACNwB,WAAW;gDAAEsB,YAAY;oDAAEC,QAAQ;gDAAK;4CAAE;4CAC1CxK,OAAOL,gBAAgB2B,OAAOgH,OAAO;4CACrCU,UAAU,CAACQ,QACT7F,MAAM;oDAAE2E,SAAS9H,kBAAkBgJ,MAAMC,MAAM,CAACzJ,KAAK;gDAAE;;;;8CAI7D,KAACjC;oCACCyJ,MAAK;oCACLC,OAAM;oCACNzH,OAAO,EAACsB,uBAAAA,OAAOkH,YAAY,YAAnBlH,uBAAuB,EAAE,EAAEgG,IAAI,CAAC;oCACxC0B,UAAU,CAACQ,QACT7F,MAAM;4CAAE6E,cAAc3H,cAAc2I,MAAMC,MAAM,CAACzJ,KAAK;wCAAE;oCAE1D0J,YACE,uDACA;;8CAGJ,KAAC3L;oCACCyJ,MAAK;oCACLC,OAAM;oCACNzH,OAAO,EAACsB,8BAAAA,OAAOmJ,mBAAmB,YAA1BnJ,8BAA8B,EAAE,EAAEgG,IAAI,CAAC;oCAC/C0B,UAAU,CAACQ,QACT7F,MAAM;4CACJ8G,qBAAqB5J,cAAc2I,MAAMC,MAAM,CAACzJ,KAAK;wCACvD;;;6BAIJ;;kCAEN,MAAC3C;;0CACC,KAACJ;gCAAO+I,OAAM;gCAAUmC,SAAS,IAAMpF,UAAU;0CAC9C;;0CAEH,KAAC9F;gCACCgH,SAAQ;gCACR+B,OAAM;gCACNmC,SAASnE;gCACT0E,UAAU1F,KAAK0H,OAAO,CACpBpJ,2BAAAA,eAAAA,OAAQlC,GAAG,qBAAXkC,aAAa4B,IAAI,EACjB5B,2BAAAA,kBAAAA,OAAQ/B,KAAK,qBAAb+B,gBAAe6B,QAAQ,EACvB7B,2BAAAA,kBAAAA,OAAQ/B,KAAK,qBAAb+B,gBAAe8B,IAAI;0CAGpB;;;;;;;;AAMb;AACAhC,iBAAiBuJ,WAAW,GAAG;AAE/B,eAAevJ,iBAAgB"}
|
|
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":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;;;AAEA,SAEEA,gBAAgB,EAChBC,iBAAiB,EACjBC,cAAc,QACT,eAAc;AACrB,SACEC,wBAAwB,EACxBC,aAAa,QAER,oBAAU;AACjB,SAASC,cAAc,EAAEC,YAAY,QAAQ,yBAAwB;AACrE,SACEC,WAAW,EACXC,OAAO,EACPC,sBAAsB,QACjB,uBAAsB;AAC7B,SAASC,WAAW,QAAQ,yDAAwD;AACpF,SAASC,WAAW,QAAQ,8BAA6B;AACzD,SAASC,SAAS,QAAQ,+BAA8B;AACxD,SACEC,KAAK,EACLC,MAAM,EACNC,IAAI,EACJC,UAAU,EACVC,MAAM,EACNC,aAAa,EACbC,aAAa,EACbC,WAAW,EACXC,QAAQ,EACRC,KAAK,EACLC,MAAM,EACNC,SAAS,EACTC,SAAS,EACTC,SAAS,EACTC,QAAQ,EACRC,SAAS,EACTC,UAAU,QACL,gBAAe;AACtB,SACEC,UAAU,EACVC,SAAS,EACTC,GAAG,EACHC,KAAK,EACLC,OAAO,EACPC,KAAK,EACLC,MAAM,QACD,qBAAoB;AAC3B,SAASC,QAAQ,QAAQ,QAAO;AAChC,SACEC,YAAY,EACZC,sBAAsB,EACtBC,qBAAqB,EACrBC,kBAAkB,QACb,iCAAgC;AACvC,SACEC,kBAAkB,EAClBC,sBAAsB,EACtBC,iBAAiB,EACjBC,kBAAkB,QACb,sBAAkB;AACzB,SACEC,wBAAwB,EACxBC,sBAAsB,EACtBC,oBAAoB,QACf,+BAA2B;AAUlC;;;;;;;CAOC,GACD,MAAMC,UAAU;AAEhB,MAAMC,YAA0B;IAC9BC,MAAM;IACNC,SAAS;IACTC,KAAK;QAAEC,aAAa;IAAK;AAC3B;AACA,MAAMC,cAA4B;IAChCJ,MAAM;IACNC,SAAS;IACTI,OAAO;QAAEC,SAAS;QAASC,cAAc;QAAGC,eAAe;IAAE;AAC/D;AAEA,SAASC,gBAAgBC,EAAW;IAClC,IAAI,CAACA,IAAI,OAAO;IAChB,MAAMC,OAAO,IAAIC,KAAKF;IACtB,MAAMG,MAAM,CAACC,QAAkBC,OAAOD,OAAOE,QAAQ,CAAC,GAAG;IACzD,OACE,GAAGL,KAAKM,WAAW,GAAG,CAAC,EAAEJ,IAAIF,KAAKO,QAAQ,KAAK,GAAG,CAAC,CAAC,GACpD,GAAGL,IAAIF,KAAKQ,OAAO,IAAI,CAAC,EAAEN,IAAIF,KAAKS,QAAQ,IAAI,CAAC,EAAEP,IAAIF,KAAKU,UAAU,KAAK;AAE9E;AAEA,SAASC,kBAAkBR,KAAa;IACtC,IAAI,CAACA,OAAO,OAAOS;IACnB,MAAMb,KAAK,IAAIE,KAAKE,OAAOU,OAAO;IAClC,OAAOC,OAAOC,QAAQ,CAAChB,MAAMA,KAAKa;AACpC;AAEA,SAASI,cAAcb,KAAa;IAClC,OAAOA,MACJc,KAAK,CAAC,KACNC,GAAG,CAAC,CAACC,UAAYA,QAAQC,IAAI,IAC7BC,MAAM,CAACC;AACZ;AAEA;;;;;;;CAOC,GACD,OAAO,SAASC,iBAAiBC,KAA4B;cA4ZtCC,cAgBDxC,8CAqDAA,oEA4HEwC,sBAYAA;QA7hBjBA,aAAmBA,eAAyBA,gBAIHA,cACCA,gBACJA,gBAoVJA,cAadA,cAOEA,cAQAA,cAYYA,gBAaAA,gBAcZA,gBAQAA,gBAYAA,gBAkBJA,gBAIIA,iBAoBAA,iBAaRA,iBAKUA,iBAmEjBA,cACAA,iBACAA;IA7nBZ,MAAM,EAAEC,MAAM,EAAE,GAAGF;IACnB,MAAMG,YAAYnD;IAClB,MAAM,EAAEoD,GAAG,EAAE,GAAGJ;IAChB,MAAM,EAAEK,eAAe,EAAE,GAAGhF;IAC5B,MAAM,EAAEiF,OAAO,EAAE,GAAGnF;IACpB,MAAMoF,cAAcrD,sBAAsBgD;IAC1C,MAAMM,WAAW9F,iBAAiB0F,KAAK;IAEvC,MAAM,EACJK,MAAMC,WAAW,EACjBC,QAAQC,cAAc,EACtB;;;;;;;;KAQC,GACDC,WAAWC,iBAAiB,EAC7B,GAAG7D,uBACF,IACEJ,MACEL,WAAW2D,WAAW,SAASD,QAAQ,aACvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;SA8BC,GACDtD,QAAQ,SACR,qEAAqE;QACrE,iDAAiD;QACjDD,MAAMgB,UAAU,KAEpB;QAACwC;QAAWD;KAAO,EACnB;QAAEa,SAAS;IAAM;IAEnB,MAAMC,YAAY,SAACN,+BAAAA,YAAaO,MAAM,mBAAI,KAAKtD;IAC/C,MAAMuD,WAA2B;WAAKR,sBAAAA,cAAe,EAAE;KAAE,CACtDS,KAAK,CAAC,GAAGxD,SACTyD,IAAI,CAACvG;IAER,MAAM,CAACoF,QAAQoB,UAAU,GAAGtE,SAA8B;IAC1D,6EAA6E;IAC7E,MAAMuE,OAAO5D,qBAAqB;QAChCwC;QACAqB,YACEf,YACA;YAACP,2BAAAA,cAAAA,OAAQlC,GAAG,qBAAXkC,YAAauB,IAAI;YAAEvB,2BAAAA,gBAAAA,OAAQ/B,KAAK,qBAAb+B,cAAewB,QAAQ;YAAExB,2BAAAA,iBAAAA,OAAQ/B,KAAK,qBAAb+B,eAAeyB,IAAI;SAAC,CAACC,IAAI,CACpE,CAACH,OAASA,wBAAAA,KAAMI,QAAQ,CAAC;IAE/B;IACA,MAAMC,sBAAsBP,KAAKQ,YAAY,CAAC7B,2BAAAA,eAAAA,OAAQlC,GAAG,qBAAXkC,aAAauB,IAAI;IAC/D,MAAMO,uBAAuBT,KAAKQ,YAAY,CAAC7B,2BAAAA,iBAAAA,OAAQ/B,KAAK,qBAAb+B,eAAewB,QAAQ;IACtE,MAAMO,mBAAmBV,KAAKQ,YAAY,CAAC7B,2BAAAA,iBAAAA,OAAQ/B,KAAK,qBAAb+B,eAAeyB,IAAI;IAC9D,MAAMO,QAAQ,CAACC,UACbb,UAAU,CAACc,WAAcA,WAAW,aAAKA,UAAaD,WAAYC;IACpE,MAAMC,WAAW,CAACF,UAChBb,UAAU,CAACc;gBAEqBA;mBAD9BA,WACI,aAAKA;gBAAUpE,KAAK,cAAMoE,gBAAAA,SAASpE,GAAG,YAAZoE,gBAAgB,CAAC,GAAOD;iBAClDC;;IAER,MAAME,aAAa,CAACH,UAClBb,UAAU,CAACc;gBAEuBA;mBADhCA,WACI,aAAKA;gBAAUjE,OAAO,cAAMiE,kBAAAA,SAASjE,KAAK,YAAdiE,kBAAkB,CAAC,GAAOD;iBACtDC;;IAGR,MAAMG,aAAa;YAcNrC;YAZmBA,kBAAAA,aAMEA,oBAAAA;QAPhC,IAAI,CAACA,QAAQ;QACb,IAAIA,OAAOpC,IAAI,KAAK,SAAS,GAACoC,cAAAA,OAAOlC,GAAG,sBAAVkC,mBAAAA,YAAYuB,IAAI,qBAAhBvB,iBAAkBL,IAAI,KAAI;YACtD,OAAO,KAAKS,gBAAgB,sBAAsB;gBAChDkC,SAAS;gBACTC,SAAS;YACX;QACF;QACA,IAAIvC,OAAOpC,IAAI,KAAK,WAAW,GAACoC,gBAAAA,OAAO/B,KAAK,sBAAZ+B,qBAAAA,cAAcyB,IAAI,qBAAlBzB,mBAAoBL,IAAI,KAAI;YAC1D,OAAO,KAAKS,gBAAgB,wBAAwB;gBAClDkC,SAAS;gBACTC,SAAS;YACX;QACF;QACA,MAAMC,MAAKxC,cAAAA,OAAOyC,GAAG,YAAVzC,cAActF;QACzB,MAAM,EACJ+H,KAAKC,QAAQ,EACbC,WAAWC,UAAU,EACrBC,WAAWC,UAAU,EAEtB,GAAG9C,QADC+C,2CACD/C;;;;;QACJ,IAAI;gBAkBSA;YAjBX,sEAAsE;YACtE,uEAAuE;YACvE,MAAMgD,UAAUC,KAAKC,KAAK,CACxBD,KAAKE,SAAS,CAAC,aACVJ,SACCA,QAAQjF,GAAG,GACX;gBAAEA,KAAK,aAAKiF,QAAQjF,GAAG;oBAAEyD,MAAMF,KAAK+B,MAAM,CAACL,QAAQjF,GAAG,CAACyD,IAAI;;YAAI,IAC/D,CAAC,GACDwB,QAAQ9E,KAAK,GACb;gBACEA,OAAO,aACF8E,QAAQ9E,KAAK;oBAChBuD,UAAUH,KAAK+B,MAAM,CAACL,QAAQ9E,KAAK,CAACuD,QAAQ;oBAC5CC,MAAMJ,KAAK+B,MAAM,CAACL,QAAQ9E,KAAK,CAACwD,IAAI;;YAExC,IACA,CAAC;gBACL4B,MAAM,EAACrD,eAAAA,OAAOqD,IAAI,YAAXrD,eAAe,IAAIL,IAAI,MAAM;;YAGxC;;;;;;;;;;;;;;;;;OAiBC,GACD,MAAM2D,UAAU,MAAMpG,mBACpB;gBACEqG,SAAS;gBACTC,YAAY3D,QAAQG,OAAOyC,GAAG,KAAK9B,mBAAmB;gBACtDC,WAAWf,QAAQG,OAAOyC,GAAG,KAAK5B;YACpC,GACA;gBACE,MAAMhE,OAAOJ,IAAIyD,WAAW,SAASD,QAAQ,YAAYuC,KAAK,aACzDQ;oBACHH,WAAWxH,UAAUoI,GAAG;mBACpBzD,OAAOyC,GAAG,GAAG,CAAC,IAAI;oBAAEE,WAAWtH,UAAUoI,GAAG;gBAAG;gBAErDnD,YAAYN,OAAOyC,GAAG,GAAG,oBAAoB,mBAAmB;oBAC9DiB,MAAM;oBACNlB;mBACIxC,OAAOqD,IAAI,GAAG;oBAAEA,MAAMrD,OAAOqD,IAAI;gBAAC,IAAI,CAAC;YAE/C;YAEF,kEAAkE;YAClE,IAAI,CAACC,QAAQK,EAAE,EAAE;gBACf,OAAO,KAAKvD,gBAAgBkD,QAAQM,OAAO,EAAE;oBAC3CtB,SAAS;oBACTC,SAAS;gBACX;YACF;YACAnC,gBAAgB,iBAAiB;gBAAEkC,SAAS;gBAAWC,SAAS;YAAM;YACtEnB,UAAU;QACZ,EAAE,OAAOyC,OAAO;YACdC,QAAQD,KAAK,CAACA;YACdzD,gBAAgB,yBAAyB;gBAAEkC,SAAS;YAAQ;QAC9D;IACF;IAEA,MAAMyB,eAAe,OAAOC;YAIPA;QAHnB,IAAI,CAACA,QAAQvB,GAAG,EAAE;QAClB,MAAMwB,YAAY,MAAM5D,QAAQ;YAC9B6D,OAAO;YACPC,aAAa,CAAC,CAAC,GAAEH,gBAAAA,QAAQX,IAAI,YAAZW,gBAAgBA,QAAQvB,GAAG,CAAC,4BAA4B,CAAC;YAC1E2B,kBAAkB;YAClBC,yBAAyB;gBAAEC,OAAO;YAAQ;QAC5C,EACE,+DAA+D;QAC/D,0DAA0D;SACzDC,IAAI,CAAC,IAAM,MACXC,KAAK,CAAC,IAAM;QACf,IAAI,CAACP,WAAW;QAChB,MAAMzH,UAAUC,IAAIyD,WAAW,SAASD,QAAQ,YAAY+D,QAAQvB,GAAG;QACvErC,gBAAgB,mBAAmB;YAAEkC,SAAS;YAAWC,SAAS;QAAM;QACxEjC,YAAY,mBAAmB;YAC7BoD,MAAM;YACNlB,IAAIwB,QAAQvB,GAAG;WACXuB,QAAQX,IAAI,GAAG;YAAEA,MAAMW,QAAQX,IAAI;QAAC,IAAI,CAAC;IAEjD;IAEA,qEAAqE;IACrE,6DAA6D;IAC7D,MAAMoB,aAAa,OAAOC,OAAeC;YAIlBC,gBACCC;QAJtB,MAAMA,WAAW5D,QAAQ,CAACyD,QAAQC,UAAU;QAC5C,MAAMC,UAAU3D,QAAQ,CAACyD,MAAM;QAC/B,IAAI,EAACG,4BAAAA,SAAUpC,GAAG,KAAI,EAACmC,2BAAAA,QAASnC,GAAG,GAAE;QACrC,MAAMqC,gBAAeF,iBAAAA,QAAQG,KAAK,YAAbH,iBAAiBF;QACtC,MAAMM,iBAAgBH,kBAAAA,SAASE,KAAK,YAAdF,kBAAkBH,QAAQC;QAChD,MAAMM,QAAQC,GAAG,CAAC;YAChBrI,OACEJ,IAAIyD,WAAW,SAASD,QAAQ,YAAY2E,QAAQnC,GAAG,GACvD;gBAAEsC,OAAOC,kBAAkBF,eAAeE,gBAAgBL,YAAYK;YAAc,GACpF;gBAAEG,OAAO;YAAK;YAEhBtI,OACEJ,IAAIyD,WAAW,SAASD,QAAQ,YAAY4E,SAASpC,GAAG,GACxD;gBAAEsC,OAAOD;YAAa,GACtB;gBAAEK,OAAO;YAAK;SAEjB,EAAEX,KAAK,CAACV,QAAQD,KAAK;IACxB;IAEA,MAAMuB,eAAe,OAAOpB;QAC1B,IAAI,CAACA,QAAQvB,GAAG,EAAE;QAClB,MAAM5F,OACJJ,IAAIyD,WAAW,SAASD,QAAQ,YAAY+D,QAAQvB,GAAG,GACvD;YAAE5E,SAASmG,QAAQnG,OAAO,KAAK;QAAM,GACrC;YAAEsH,OAAO;QAAK;IAElB;IAEA,MAAME,aAAa,CAACrB;QAClB,MAAMtD,SAAS7F,cAAcmJ,SAASxF,KAAKiF,GAAG;QAC9C,IAAI/C,WAAW,OAAO,qBAAO,KAAClF;YAAK8J,MAAK;YAAQC,OAAM;;QACtD,IAAI7E,WAAW,aAAa;YAC1B,qBAAO,KAAClF;gBAAK8J,MAAK;gBAAQhB,OAAM;gBAAOiB,OAAM;;QAC/C;QACA,qBAAO,KAAC/J;YAAK8J,MAAK;YAAQhB,OAAM;YAAUiB,OAAM;;IAClD;IAEA,qBACE,MAACvK;QACCwK,QAAO;QACPC,MAAM9K,eAAe,qBAAqB;YACxC+K,QAAQ;YACRC,SACE,+DACA;QACJ;QACAC,cAAc;QACdC,cAAc;QACdC,iBAAgB;;YAEf,CAACvF,yBACA,KAACjF;gBAAMyK,UAAS;0BACb,gEACC;+BAGJ,MAAChK;gBAAMiK,SAAS;;kCACd,KAAC1J;wBAAWgG,SAAQ;wBAAQgC,OAAM;kCAC/B,kEACC,iEACA;;kCAEJ,MAACvI;wBAAM4I,WAAU;wBAAMqB,SAAS;;0CAC9B,KAACzK;gCACC+J,MAAK;gCACLhB,OAAM;gCACNhC,SAAQ;gCACR2D,SAAS,IAAM7E,UAAU,aAAKzD;0CAE7B;;0CAEH,KAACpC;gCACC+J,MAAK;gCACLhB,OAAM;gCACNhC,SAAQ;gCACR2D,SAAS,IAAM7E,UAAU,aAAKpD;0CAE7B;;;;oBAGJ+C,0BACC,KAACzF;wBAAMyK,UAAS;kCACb,CAAC,QAAQ,EAAErI,QAAQ,0CAA0C,CAAC,GAC7D,iEACA;yBAEF;oBACHuD,SAASD,MAAM,KAAK,kBACnB,KAAC1E;wBAAWgG,SAAQ;wBAAQgC,OAAM;kCAC/B,6DACC;uCAGJ,MAACnJ;wBAAYmK,MAAK;;0CAChB,KAACnJ;0CACC,cAAA,MAACC;;sDACC,KAACF;sDAAW;;sDACZ,KAACA;sDAAW;;sDACZ,KAACA;sDAAW;;sDACZ,KAACA;sDAAW;;sDACZ,KAACA;sDAAW;;sDACZ,KAACA;sDAAW;;sDACZ,KAACA;4CAAUgK,OAAM;sDAAS;;;;;0CAG9B,KAACjK;0CACEgF,SAASxB,GAAG,CAAC,CAACuE,wBACb,MAAC5H;;0DACC,KAACF;0DAAWiB,mBAAmB6G;;0DAC/B,KAAC9H;0DACE8H,QAAQpG,IAAI,KAAK,QAAQ,QAAQ;;0DAEpC,KAAC1B;0DAAWmJ,WAAWrB;;0DACvB,KAAC9H;0DAAWoB,mBAAmB0G;;0DAC/B,KAAC9H;0DAAWmB,kBAAkB2G;;0DAC9B,KAAC9H;0DAEC,cAAA,KAACI;oDAAWgG,SAAQ;oDAAUgC,OAAM;8DACjClH,uBAAuB4G,QAAQmC,KAAK;;;0DAGzC,MAACjK;gDAAUgK,OAAM;;kEACf,KAACzK;wDACC6J,MAAK;wDACLc,cAAW;wDACXC,UAAUpF,SAASqF,OAAO,CAACtC,aAAa;wDACxCiC,SAAS,IACP,KAAKxB,WAAWxD,SAASqF,OAAO,CAACtC,UAAU,CAAC;kEAG9C,cAAA,KAAC/I;4DAAQsL,MAAMxL,aAAawL,IAAI;4DAAEC,IAAI;gEAAEC,UAAU;4DAAO;;;kEAE3D,KAAChL;wDACC6J,MAAK;wDACLc,cAAW;wDACXC,UACEpF,SAASqF,OAAO,CAACtC,aAAa/C,SAASD,MAAM,GAAG;wDAElDiF,SAAS,IACP,KAAKxB,WAAWxD,SAASqF,OAAO,CAACtC,UAAU;kEAG7C,cAAA,KAAC/I;4DACCsL,MAAMzL,eAAeyL,IAAI;4DACzBC,IAAI;gEAAEC,UAAU;4DAAO;;;kEAG3B,KAACzK;wDACCsJ,MAAK;wDACLoB,SAAS1C,QAAQnG,OAAO,KAAK;wDAC7B8I,UAAU,IAAMvB,aAAapB;wDAC7B4C,WAAW;4DAAEC,OAAO;gEAAE,cAAc;4DAAU;wDAAE;;kEAElD,KAACtL;wDACC+J,MAAK;wDACLhB,OAAM;wDACN2B,SAAS,IAAM7E,UAAU,aAAK4C;kEAE7B;;kEAEH,KAACzI;wDACC+J,MAAK;wDACLhB,OAAM;wDACN2B,SAAS,IAAMlC,aAAaC;kEAE3B;;;;;uCA1DQA,QAAQvB,GAAG;;;;;;0BAqEtC,MAAC/G;gBACCoL,MAAMjH,QAAQG;gBACd+G,SAAS,IAAM3F,UAAU;gBACzB4F,UAAS;gBACTC,SAAS;;kCAET,KAACpL;kCACEmE,CAAAA,0BAAAA,OAAQyC,GAAG,IACR,iBACAzC,CAAAA,0BAAAA,OAAQpC,IAAI,MAAK,QACf,yBACA;;kCAER,KAAChC;kCACEoE,uBACC,MAACjE;4BAAMiK,SAAS;4BAAGQ,IAAI;gCAAEU,IAAI;4BAAE;;8CAC7B,KAAC7K;oCACCiJ,MAAK;oCACLC,OAAM;oCACN7G,KAAK,GAAEsB,eAAAA,OAAOqD,IAAI,YAAXrD,eAAe;oCACtB2G,UAAU,CAACQ,QAAUnF,MAAM;4CAAEqB,MAAM8D,MAAMC,MAAM,CAAC1I,KAAK;wCAAC;oCACtD2I,YAAW;;gCAEZrH,OAAOpC,IAAI,KAAK,sBACf;;sDACE,KAACvB;4CACCiJ,MAAK;4CACLC,OAAM;4CACN+B,QAAQ;4CACR5I,OAAO2C,KAAKkG,QAAQ,EAACvH,eAAAA,OAAOlC,GAAG,qBAAVkC,aAAYuB,IAAI;4CACrCoF,UAAU,CAACQ,QACThF,SAAS;oDAAEZ,MAAMF,KAAK+B,MAAM,CAAC+D,MAAMC,MAAM,CAAC1I,KAAK;gDAAE;4CAEnDmF,OAAOjC,oBAAoBZ,MAAM,GAAG;4CACpCqG,UAAU,GACR7J,0BAAAA,uBAAuBoE,gCAAvBpE,0BACAD;;sDAGJ,KAAClB;4CACCiJ,MAAK;4CACLC,OAAM;4CACN7G,KAAK,YAAEsB,eAAAA,OAAOlC,GAAG,qBAAVkC,aAAYwH,IAAI,oBAAI;4CAC3Bb,UAAU,CAACQ,QAAUhF,SAAS;oDAAEqF,MAAML,MAAMC,MAAM,CAAC1I,KAAK;gDAAC;;sDAE3D,MAAC3C;4CAAM4I,WAAU;4CAAMqB,SAAS;;8DAC9B,KAAC3J;oDACCiJ,MAAK;oDACLC,OAAM;oDACN7G,KAAK,YAAEsB,eAAAA,OAAOlC,GAAG,qBAAVkC,aAAYyH,eAAe,oBAAI;oDACtCd,UAAU,CAACQ,QACThF,SAAS;4DAAEsF,iBAAiBN,MAAMC,MAAM,CAAC1I,KAAK;wDAAC;;8DAGnD,KAACrC;oDACCiJ,MAAK;oDACLC,OAAM;oDACN7G,KAAK,YAAEsB,eAAAA,OAAOlC,GAAG,qBAAVkC,aAAY0H,SAAS,oBAAI;oDAChCf,UAAU,CAACQ,QACThF,SAAS;4DAAEuF,WAAWP,MAAMC,MAAM,CAAC1I,KAAK;wDAAC;;;;;mDAMjD;;sDACE,KAACrC;4CACCiJ,MAAK;4CACLC,OAAM;4CACN7G,OAAO2C,KAAKkG,QAAQ,EAACvH,iBAAAA,OAAO/B,KAAK,qBAAZ+B,eAAcwB,QAAQ;4CAC3CmF,UAAU,CAACQ,QACT/E,WAAW;oDAAEZ,UAAUH,KAAK+B,MAAM,CAAC+D,MAAMC,MAAM,CAAC1I,KAAK;gDAAE;4CAEzDmF,OAAO/B,qBAAqBd,MAAM,GAAG;4CACrCqG,YAAY7J,uBAAuBsE;;sDAErC,KAACzF;4CACCiJ,MAAK;4CACLC,OAAM;4CACN+B,QAAQ;4CACRK,SAAS;4CACTC,SAAS;4CACTlJ,OAAO2C,KAAKkG,QAAQ,EAACvH,iBAAAA,OAAO/B,KAAK,qBAAZ+B,eAAcyB,IAAI;4CACvCkF,UAAU,CAACQ,QACT/E,WAAW;oDAAEX,MAAMJ,KAAK+B,MAAM,CAAC+D,MAAMC,MAAM,CAAC1I,KAAK;gDAAE;4CAErDmF,OAAO9B,iBAAiBf,MAAM,GAAG;4CACjCqG,UAAU,GACR7J,2BAAAA,uBAAuBuE,6BAAvBvE,2BACAD;;sDAGJ,MAACxB;4CAAM4I,WAAU;4CAAMqB,SAAS;;8DAC9B,KAAC3J;oDACCiJ,MAAK;oDACLC,OAAM;oDACN7G,KAAK,YAAEsB,iBAAAA,OAAO/B,KAAK,qBAAZ+B,eAAc6H,QAAQ,oBAAI;oDACjClB,UAAU,CAACQ,QACT/E,WAAW;4DAAEyF,UAAUV,MAAMC,MAAM,CAAC1I,KAAK;wDAAC;;8DAG9C,KAACrC;oDACCiJ,MAAK;oDACLC,OAAM;oDACN7G,KAAK,YAAEsB,iBAAAA,OAAO/B,KAAK,qBAAZ+B,eAAc8H,OAAO,oBAAI;oDAChCnB,UAAU,CAACQ,QACT/E,WAAW;4DAAE0F,SAASX,MAAMC,MAAM,CAAC1I,KAAK;wDAAC;;;;sDAI/C,MAAC3C;4CAAM4I,WAAU;4CAAMqB,SAAS;;8DAC9B,MAAC3J;oDACC0L,MAAM;oDACNzC,MAAK;oDACLC,OAAM;oDACNiB,IAAI;wDAAEwB,UAAU;oDAAI;oDACpBtJ,KAAK,YAAEsB,iBAAAA,OAAO/B,KAAK,qBAAZ+B,eAAc9B,OAAO,oBAAI;oDAChCyI,UAAU,CAACQ,QACT/E,WAAW;4DACTlE,SAASiJ,MAAMC,MAAM,CAClB1I,KAAK;wDAGV;;sEAGF,KAAC5C;4DAAS4C,OAAM;sEAAS;;sEACzB,KAAC5C;4DAAS4C,OAAM;sEAAU;;sEAC1B,KAAC5C;4DAAS4C,OAAM;sEAAQ;;;;8DAE1B,KAACrC;oDACCiJ,MAAK;oDACL5B,MAAK;oDACL6B,OACE,WAACvF,iBAAAA,OAAO/B,KAAK,qBAAZ+B,eAAc9B,OAAO,oBAAI,aAAa,WACnC,aACA;oDAENQ,KAAK,YAAEsB,kBAAAA,OAAO/B,KAAK,qBAAZ+B,gBAAc7B,YAAY,oBAAI;oDACrCwI,UAAU,CAACQ,QACT/E,WAAW;4DACTjE,cAAckB,OAAO8H,MAAMC,MAAM,CAAC1I,KAAK;wDACzC;;8DAYJ,MAACrC;oDACC0L,MAAM;oDACNzC,MAAK;oDACLC,OAAM;oDACN7G,OAAOsB,EAAAA,kBAAAA,OAAO/B,KAAK,qBAAZ+B,gBAAciI,cAAc,IAAG,YAAY;oDAClDtB,UAAU,CAACQ,QACT/E,WAAW;4DACT6F,gBAAgBd,MAAMC,MAAM,CAAC1I,KAAK,KAAK;wDACzC;oDAEF8H,IAAI;wDAAEwB,UAAU;oDAAI;;sEAEpB,KAAClM;4DAAS4C,OAAM;sEACb;;sEAEH,KAAC5C;4DAAS4C,OAAM;sEAAQ;;;;gDAEzBsB,EAAAA,kBAAAA,OAAO/B,KAAK,qBAAZ+B,gBAAciI,cAAc,IAAG,qBAC9B,KAAC5L;oDACCiJ,MAAK;oDACL5B,MAAK;oDACL6B,OAAM;oDACN7G,KAAK,YAAEsB,kBAAAA,OAAO/B,KAAK,qBAAZ+B,gBAAc5B,aAAa,oBAAI;oDACtCuI,UAAU,CAACQ,QACT/E,WAAW;4DACThE,eAAeiB,OAAO8H,MAAMC,MAAM,CAAC1I,KAAK;wDAC1C;;;;;;8CAOZ,MAAC3C;oCAAM4I,WAAU;oCAAMqB,SAAS;;sDAC9B,KAAC3J;4CACCiJ,MAAK;4CACL5B,MAAK;4CACL6B,OAAM;4CACNqB,WAAW;gDAAEsB,YAAY;oDAAEC,QAAQ;gDAAK;4CAAE;4CAC1CzJ,OAAOL,gBAAgB2B,OAAOoI,SAAS;4CACvCzB,UAAU,CAACQ,QACTnF,MAAM;oDAAEoG,WAAWlJ,kBAAkBiI,MAAMC,MAAM,CAAC1I,KAAK;gDAAE;;sDAG7D,KAACrC;4CACCiJ,MAAK;4CACL5B,MAAK;4CACL6B,OAAM;4CACNqB,WAAW;gDAAEsB,YAAY;oDAAEC,QAAQ;gDAAK;4CAAE;4CAC1CzJ,OAAOL,gBAAgB2B,OAAOqI,OAAO;4CACrC1B,UAAU,CAACQ,QACTnF,MAAM;oDAAEqG,SAASnJ,kBAAkBiI,MAAMC,MAAM,CAAC1I,KAAK;gDAAE;;;;8CAI7D,KAACrC;oCACCiJ,MAAK;oCACLC,OAAM;oCACN7G,OAAO,EAACsB,uBAAAA,OAAOsI,YAAY,YAAnBtI,uBAAuB,EAAE,EAAEuI,IAAI,CAAC;oCACxC5B,UAAU,CAACQ,QACTnF,MAAM;4CAAEsG,cAAc/I,cAAc4H,MAAMC,MAAM,CAAC1I,KAAK;wCAAE;oCAE1D2I,YACE,uDACA;;8CAGJ,KAAChL;oCACCiJ,MAAK;oCACLC,OAAM;oCACN7G,OAAO,EAACsB,8BAAAA,OAAOwI,mBAAmB,YAA1BxI,8BAA8B,EAAE,EAAEuI,IAAI,CAAC;oCAC/C5B,UAAU,CAACQ,QACTnF,MAAM;4CACJwG,qBAAqBjJ,cAAc4H,MAAMC,MAAM,CAAC1I,KAAK;wCACvD;;;6BAIJ;;kCAEN,MAAC/C;;0CACC,KAACJ;gCAAO+I,OAAM;gCAAU2B,SAAS,IAAM7E,UAAU;0CAC9C;;0CAEH,KAAC7F;gCACC+G,SAAQ;gCACRgC,OAAM;gCACN2B,SAAS5D;gCACTgE,UAAUhF,KAAKoH,OAAO,CACpBzI,2BAAAA,eAAAA,OAAQlC,GAAG,qBAAXkC,aAAauB,IAAI,EACjBvB,2BAAAA,kBAAAA,OAAQ/B,KAAK,qBAAb+B,gBAAewB,QAAQ,EACvBxB,2BAAAA,kBAAAA,OAAQ/B,KAAK,qBAAb+B,gBAAeyB,IAAI;0CAGpB;;;;;;;;AAMb;AACA3B,iBAAiB4I,WAAW,GAAG;AAE/B,eAAe5I,iBAAgB"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ConsolePluginOrgMount } from '@aglyn/aglyn';
|
|
2
|
+
/** What the Inbox's Campaigns zone hands this widget. */
|
|
3
|
+
export interface InboxCampaignsWidgetProps {
|
|
4
|
+
/** The site, or `null` on the organization's Inbox. */
|
|
5
|
+
hostId: string | null;
|
|
6
|
+
/** The organization and its sites — present exactly when `hostId` is `null`. */
|
|
7
|
+
orgMount?: ConsolePluginOrgMount;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The campaigns list, as the Inbox's Campaigns section draws it.
|
|
11
|
+
*
|
|
12
|
+
* Under a site it is the site's card, unchanged. On the organization's Inbox
|
|
13
|
+
* (AGL-3303) the zone hands no site and the shell's org mount instead, and
|
|
14
|
+
* the card reads its org — which campaigns, which sites each is placed on —
|
|
15
|
+
* through the Marketing org mount, so this puts the provider where the org
|
|
16
|
+
* Marketing hub puts it: around the card. A row then opens on the org
|
|
17
|
+
* Marketing hub, where a campaign placed on several sites is edited once.
|
|
18
|
+
*/
|
|
19
|
+
export declare function InboxCampaignsWidget(props: InboxCampaignsWidgetProps): import("react").JSX.Element;
|
|
20
|
+
export declare namespace InboxCampaignsWidget {
|
|
21
|
+
var displayName: string;
|
|
22
|
+
}
|
|
23
|
+
export default InboxCampaignsWidget;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ 'use client';
|
|
17
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
18
|
+
import { buildRoute, Route } from "@aglyn/aglyn";
|
|
19
|
+
import { useMemo } from "react";
|
|
20
|
+
import HostCampaignsCard from "./campaigns-card.js";
|
|
21
|
+
import { MarketingOrgMountProvider } from "./marketing-org-mount.js";
|
|
22
|
+
/**
|
|
23
|
+
* The campaigns list, as the Inbox's Campaigns section draws it.
|
|
24
|
+
*
|
|
25
|
+
* Under a site it is the site's card, unchanged. On the organization's Inbox
|
|
26
|
+
* (AGL-3303) the zone hands no site and the shell's org mount instead, and
|
|
27
|
+
* the card reads its org — which campaigns, which sites each is placed on —
|
|
28
|
+
* through the Marketing org mount, so this puts the provider where the org
|
|
29
|
+
* Marketing hub puts it: around the card. A row then opens on the org
|
|
30
|
+
* Marketing hub, where a campaign placed on several sites is edited once.
|
|
31
|
+
*/ export function InboxCampaignsWidget(props) {
|
|
32
|
+
const { hostId, orgMount } = props;
|
|
33
|
+
const mount = useMemo(()=>hostId == null && orgMount ? {
|
|
34
|
+
orgId: orgMount.orgId,
|
|
35
|
+
orgSlug: orgMount.orgSlug,
|
|
36
|
+
hosts: orgMount.hosts,
|
|
37
|
+
hostsReady: orgMount.hostsReady,
|
|
38
|
+
hostsPath: orgMount.hostsPath,
|
|
39
|
+
basePath: buildRoute(Route.ORG_PLUGIN, {
|
|
40
|
+
orgSlug: orgMount.orgSlug,
|
|
41
|
+
pluginSlug: 'marketing'
|
|
42
|
+
})
|
|
43
|
+
} : null, [
|
|
44
|
+
hostId,
|
|
45
|
+
orgMount
|
|
46
|
+
]);
|
|
47
|
+
if (hostId != null) return /*#__PURE__*/ _jsx(HostCampaignsCard, {
|
|
48
|
+
hostId: hostId
|
|
49
|
+
});
|
|
50
|
+
// No site and no org to stand in for it: nothing to list.
|
|
51
|
+
if (!mount) return null;
|
|
52
|
+
return /*#__PURE__*/ _jsx(MarketingOrgMountProvider, {
|
|
53
|
+
value: mount,
|
|
54
|
+
children: /*#__PURE__*/ _jsx(HostCampaignsCard, {
|
|
55
|
+
hostId: null,
|
|
56
|
+
basePath: mount.basePath
|
|
57
|
+
})
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
InboxCampaignsWidget.displayName = 'InboxCampaignsWidget';
|
|
61
|
+
export default InboxCampaignsWidget;
|
|
62
|
+
|
|
63
|
+
//# sourceMappingURL=inbox-campaigns-widget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/inbox-campaigns-widget.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { buildRoute, Route, type ConsolePluginOrgMount } from '@aglyn/aglyn'\nimport { useMemo } from 'react'\nimport HostCampaignsCard from './campaigns-card'\nimport {\n MarketingOrgMountProvider,\n type MarketingOrgMount,\n} from './marketing-org-mount'\n\n/** What the Inbox's Campaigns zone hands this widget. */\nexport interface InboxCampaignsWidgetProps {\n /** The site, or `null` on the organization's Inbox. */\n hostId: string | null\n /** The organization and its sites — present exactly when `hostId` is `null`. */\n orgMount?: ConsolePluginOrgMount\n}\n\n/**\n * The campaigns list, as the Inbox's Campaigns section draws it.\n *\n * Under a site it is the site's card, unchanged. On the organization's Inbox\n * (AGL-3303) the zone hands no site and the shell's org mount instead, and\n * the card reads its org — which campaigns, which sites each is placed on —\n * through the Marketing org mount, so this puts the provider where the org\n * Marketing hub puts it: around the card. A row then opens on the org\n * Marketing hub, where a campaign placed on several sites is edited once.\n */\nexport function InboxCampaignsWidget(props: InboxCampaignsWidgetProps) {\n const { hostId, orgMount } = props\n const mount = useMemo<MarketingOrgMount | null>(\n () =>\n hostId == null && orgMount\n ? {\n orgId: orgMount.orgId,\n orgSlug: orgMount.orgSlug,\n hosts: orgMount.hosts,\n hostsReady: orgMount.hostsReady,\n hostsPath: orgMount.hostsPath,\n basePath: buildRoute(Route.ORG_PLUGIN, {\n orgSlug: orgMount.orgSlug,\n pluginSlug: 'marketing',\n }),\n }\n : null,\n [hostId, orgMount],\n )\n if (hostId != null) return <HostCampaignsCard hostId={hostId} />\n // No site and no org to stand in for it: nothing to list.\n if (!mount) return null\n return (\n <MarketingOrgMountProvider value={mount}>\n <HostCampaignsCard hostId={null} basePath={mount.basePath} />\n </MarketingOrgMountProvider>\n )\n}\nInboxCampaignsWidget.displayName = 'InboxCampaignsWidget'\n\nexport default InboxCampaignsWidget\n"],"names":["buildRoute","Route","useMemo","HostCampaignsCard","MarketingOrgMountProvider","InboxCampaignsWidget","props","hostId","orgMount","mount","orgId","orgSlug","hosts","hostsReady","hostsPath","basePath","ORG_PLUGIN","pluginSlug","value","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SAASA,UAAU,EAAEC,KAAK,QAAoC,eAAc;AAC5E,SAASC,OAAO,QAAQ,QAAO;AAC/B,OAAOC,uBAAuB,sBAAkB;AAChD,SACEC,yBAAyB,QAEpB,2BAAuB;AAU9B;;;;;;;;;CASC,GACD,OAAO,SAASC,qBAAqBC,KAAgC;IACnE,MAAM,EAAEC,MAAM,EAAEC,QAAQ,EAAE,GAAGF;IAC7B,MAAMG,QAAQP,QACZ,IACEK,UAAU,QAAQC,WACd;YACEE,OAAOF,SAASE,KAAK;YACrBC,SAASH,SAASG,OAAO;YACzBC,OAAOJ,SAASI,KAAK;YACrBC,YAAYL,SAASK,UAAU;YAC/BC,WAAWN,SAASM,SAAS;YAC7BC,UAAUf,WAAWC,MAAMe,UAAU,EAAE;gBACrCL,SAASH,SAASG,OAAO;gBACzBM,YAAY;YACd;QACF,IACA,MACN;QAACV;QAAQC;KAAS;IAEpB,IAAID,UAAU,MAAM,qBAAO,KAACJ;QAAkBI,QAAQA;;IACtD,0DAA0D;IAC1D,IAAI,CAACE,OAAO,OAAO;IACnB,qBACE,KAACL;QAA0Bc,OAAOT;kBAChC,cAAA,KAACN;YAAkBI,QAAQ;YAAMQ,UAAUN,MAAMM,QAAQ;;;AAG/D;AACAV,qBAAqBc,WAAW,GAAG;AAEnC,eAAed,qBAAoB"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import { type Firestore, type Query } from 'firebase/firestore';
|
|
18
|
+
/**
|
|
19
|
+
* THE MARKETING FIGURES, as server aggregates.
|
|
20
|
+
*
|
|
21
|
+
* Every figure the Overview sections print is a count or a sum, and a count
|
|
22
|
+
* is not the length of a capped read: three listeners at `limit(50)` summed
|
|
23
|
+
* whatever fifty documents the server happened to return first, so a site
|
|
24
|
+
* past fifty sends reported the totals of an arbitrary fifty and nothing on
|
|
25
|
+
* screen said so. An aggregation is billed per thousand index entries rather
|
|
26
|
+
* than per document, so the exact figure costs about what one row did.
|
|
27
|
+
*
|
|
28
|
+
* ## One aggregation per field
|
|
29
|
+
*
|
|
30
|
+
* Firestore can compute several aggregations in one query, and it then
|
|
31
|
+
* counts only the documents that hold EVERY field the query aggregates. A
|
|
32
|
+
* send the webhook has not yet stamped with `stats.clicks` would drop out of
|
|
33
|
+
* the `stats.sent` total beside it, and a count asked alongside a sum would
|
|
34
|
+
* count only the documents carrying the summed field. So each figure is its
|
|
35
|
+
* own query, and a query of one field is served by that field's automatic
|
|
36
|
+
* index wherever the query carries no filter.
|
|
37
|
+
*
|
|
38
|
+
* ## `null` is not zero
|
|
39
|
+
*
|
|
40
|
+
* A figure the server refused or failed to answer is `null`, and the card
|
|
41
|
+
* draws a dash for it. Zero is a measurement; a dash is the absence of one,
|
|
42
|
+
* and a tile that printed 0 for a denied read would be read as "nothing
|
|
43
|
+
* happened".
|
|
44
|
+
*/
|
|
45
|
+
/** A server count, or `null` when the server refused or failed it. */
|
|
46
|
+
export declare function countOf(target: Query): Promise<number | null>;
|
|
47
|
+
/**
|
|
48
|
+
* A server sum of one numeric field, or `null` when the server refused or
|
|
49
|
+
* failed it. Documents without the field, or with a value that is not a
|
|
50
|
+
* number, add nothing.
|
|
51
|
+
*/
|
|
52
|
+
export declare function sumOf(target: Query, field: string): Promise<number | null>;
|
|
53
|
+
/** What a set of campaign sends did, summed. */
|
|
54
|
+
export interface SendFigures {
|
|
55
|
+
sent: number | null;
|
|
56
|
+
opens: number | null;
|
|
57
|
+
clicks: number | null;
|
|
58
|
+
/** Sends still waiting on their scheduled time. */
|
|
59
|
+
scheduled: number | null;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The email figures over `sends` — every send of the org, or the ones sent
|
|
63
|
+
* as one site (`campaignSendsQuery` builds both).
|
|
64
|
+
*
|
|
65
|
+
* The site form filters on `visibleTo`, so each of its four figures is
|
|
66
|
+
* served by a composite index that pairs that clause with the summed or
|
|
67
|
+
* compared field; they are declared in `cloud/firebase-firestore.indexes.json`
|
|
68
|
+
* and pinned by `marketing-summary-indexes.spec.ts`. The org form carries no
|
|
69
|
+
* filter and needs nothing but the automatic ones.
|
|
70
|
+
*/
|
|
71
|
+
export declare function readSendFigures(sends: Query): Promise<SendFigures>;
|
|
72
|
+
/** One site's overlays' lifetime engagement, summed. */
|
|
73
|
+
export interface SiteOverlayFigures {
|
|
74
|
+
views: number | null;
|
|
75
|
+
clicks: number | null;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The lifetime counters the beacon increments on every overlay of one site
|
|
79
|
+
* (AGL-271), summed across all of them — not across a window of them.
|
|
80
|
+
*/
|
|
81
|
+
export declare function readSiteOverlayFigures(firestore: Firestore, hostId: string): Promise<SiteOverlayFigures>;
|
|
82
|
+
/** How many of one site's A/B tests are running, and how many are decided. */
|
|
83
|
+
export interface SiteExperimentFigures {
|
|
84
|
+
running: number | null;
|
|
85
|
+
decided: number | null;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* One site's A/B tests, counted by state.
|
|
89
|
+
*
|
|
90
|
+
* "Decided" is a test with a winner, which is what `winnerVariantId` records
|
|
91
|
+
* whether a person picked it or the auto-winner did. `!= null` is the query
|
|
92
|
+
* for a field that is PRESENT: Firestore leaves out documents that lack the
|
|
93
|
+
* field, and no writer stores the id as an explicit null.
|
|
94
|
+
*/
|
|
95
|
+
export declare function readSiteExperimentFigures(firestore: Firestore, hostId: string): Promise<SiteExperimentFigures>;
|