@aglyn/plugins-marketing 1.0.0-beta.143
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +7 -0
- package/package.json +56 -0
- package/src/index.d.ts +19 -0
- package/src/index.js +20 -0
- package/src/index.js.map +1 -0
- package/src/lib/components/announcement-bar-card.component.d.ts +20 -0
- package/src/lib/components/announcement-bar-card.component.js +292 -0
- package/src/lib/components/announcement-bar-card.component.js.map +1 -0
- package/src/lib/components/campaign-composer.d.ts +84 -0
- package/src/lib/components/campaign-composer.js +1643 -0
- package/src/lib/components/campaign-composer.js.map +1 -0
- package/src/lib/components/campaign-conversions-card.d.ts +22 -0
- package/src/lib/components/campaign-conversions-card.js +642 -0
- package/src/lib/components/campaign-conversions-card.js.map +1 -0
- package/src/lib/components/campaign-detail-card.d.ts +55 -0
- package/src/lib/components/campaign-detail-card.js +771 -0
- package/src/lib/components/campaign-detail-card.js.map +1 -0
- package/src/lib/components/campaign-edit-drawer.d.ts +60 -0
- package/src/lib/components/campaign-edit-drawer.js +252 -0
- package/src/lib/components/campaign-edit-drawer.js.map +1 -0
- package/src/lib/components/campaign-email-zones.d.ts +111 -0
- package/src/lib/components/campaign-email-zones.js +54 -0
- package/src/lib/components/campaign-email-zones.js.map +1 -0
- package/src/lib/components/campaign-glance-card.component.d.ts +18 -0
- package/src/lib/components/campaign-glance-card.component.js +151 -0
- package/src/lib/components/campaign-glance-card.component.js.map +1 -0
- package/src/lib/components/campaign-members-section.d.ts +87 -0
- package/src/lib/components/campaign-members-section.js +499 -0
- package/src/lib/components/campaign-members-section.js.map +1 -0
- package/src/lib/components/campaign-reach-sections.d.ts +137 -0
- package/src/lib/components/campaign-reach-sections.js +694 -0
- package/src/lib/components/campaign-reach-sections.js.map +1 -0
- package/src/lib/components/campaign-report-card.d.ts +91 -0
- package/src/lib/components/campaign-report-card.js +667 -0
- package/src/lib/components/campaign-report-card.js.map +1 -0
- package/src/lib/components/campaign-test-send-drawer.d.ts +45 -0
- package/src/lib/components/campaign-test-send-drawer.js +238 -0
- package/src/lib/components/campaign-test-send-drawer.js.map +1 -0
- package/src/lib/components/campaigns-card.d.ts +31 -0
- package/src/lib/components/campaigns-card.js +667 -0
- package/src/lib/components/campaigns-card.js.map +1 -0
- package/src/lib/components/conversion-attribution.component.d.ts +62 -0
- package/src/lib/components/conversion-attribution.component.js +127 -0
- package/src/lib/components/conversion-attribution.component.js.map +1 -0
- package/src/lib/components/email-compose-card.d.ts +12 -0
- package/src/lib/components/email-compose-card.js +157 -0
- package/src/lib/components/email-compose-card.js.map +1 -0
- package/src/lib/components/email-detail.d.ts +40 -0
- package/src/lib/components/email-detail.js +1180 -0
- package/src/lib/components/email-detail.js.map +1 -0
- package/src/lib/components/email-edit-drawer.d.ts +54 -0
- package/src/lib/components/email-edit-drawer.js +172 -0
- package/src/lib/components/email-edit-drawer.js.map +1 -0
- package/src/lib/components/email-messages-widget.d.ts +22 -0
- package/src/lib/components/email-messages-widget.js +49 -0
- package/src/lib/components/email-messages-widget.js.map +1 -0
- package/src/lib/components/email-recipients-card.d.ts +50 -0
- package/src/lib/components/email-recipients-card.js +338 -0
- package/src/lib/components/email-recipients-card.js.map +1 -0
- package/src/lib/components/emails-list-card.d.ts +36 -0
- package/src/lib/components/emails-list-card.js +544 -0
- package/src/lib/components/emails-list-card.js.map +1 -0
- package/src/lib/components/experiment-zones.d.ts +78 -0
- package/src/lib/components/experiment-zones.js +65 -0
- package/src/lib/components/experiment-zones.js.map +1 -0
- package/src/lib/components/host-experiments-card.component.d.ts +17 -0
- package/src/lib/components/host-experiments-card.component.js +887 -0
- package/src/lib/components/host-experiments-card.component.js.map +1 -0
- package/src/lib/components/host-marketing-summary-card.component.d.ts +15 -0
- package/src/lib/components/host-marketing-summary-card.component.js +161 -0
- package/src/lib/components/host-marketing-summary-card.component.js.map +1 -0
- package/src/lib/components/host-overlays-card.component.d.ts +19 -0
- package/src/lib/components/host-overlays-card.component.js +783 -0
- package/src/lib/components/host-overlays-card.component.js.map +1 -0
- package/src/lib/components/marketing-console-page.d.ts +28 -0
- package/src/lib/components/marketing-console-page.js +166 -0
- package/src/lib/components/marketing-console-page.js.map +1 -0
- package/src/lib/components/marketing-console-sections.d.ts +34 -0
- package/src/lib/components/marketing-console-sections.js +77 -0
- package/src/lib/components/marketing-console-sections.js.map +1 -0
- package/src/lib/components/overlay-stats-row.component.d.ts +15 -0
- package/src/lib/components/overlay-stats-row.component.js +78 -0
- package/src/lib/components/overlay-stats-row.component.js.map +1 -0
- package/src/lib/components/popup-card.component.d.ts +18 -0
- package/src/lib/components/popup-card.component.js +462 -0
- package/src/lib/components/popup-card.component.js.map +1 -0
- package/src/lib/components/record-attribution-widget.d.ts +35 -0
- package/src/lib/components/record-attribution-widget.js +33 -0
- package/src/lib/components/record-attribution-widget.js.map +1 -0
- package/src/lib/components/site-runtime.d.ts +9 -0
- package/src/lib/components/site-runtime.js +1081 -0
- package/src/lib/components/site-runtime.js.map +1 -0
- package/src/lib/components/use-campaign-send-api.d.ts +35 -0
- package/src/lib/components/use-campaign-send-api.js +99 -0
- package/src/lib/components/use-campaign-send-api.js.map +1 -0
- package/src/lib/components/use-campaign-topic-options.d.ts +20 -0
- package/src/lib/components/use-campaign-topic-options.js +44 -0
- package/src/lib/components/use-campaign-topic-options.js.map +1 -0
- package/src/lib/components/use-emails-hub-path.d.ts +39 -0
- package/src/lib/components/use-emails-hub-path.js +70 -0
- package/src/lib/components/use-emails-hub-path.js.map +1 -0
- package/src/lib/components/use-marketing-hub-path.d.ts +21 -0
- package/src/lib/components/use-marketing-hub-path.js +50 -0
- package/src/lib/components/use-marketing-hub-path.js.map +1 -0
- package/src/lib/components/use-overlay-copy-editor.d.ts +79 -0
- package/src/lib/components/use-overlay-copy-editor.js +110 -0
- package/src/lib/components/use-overlay-copy-editor.js.map +1 -0
- package/src/lib/constants/bundle-common.d.ts +8 -0
- package/src/lib/constants/bundle-common.js +9 -0
- package/src/lib/constants/bundle-common.js.map +1 -0
- package/src/lib/model/campaign-membership-figures.d.ts +109 -0
- package/src/lib/model/campaign-membership-figures.js +113 -0
- package/src/lib/model/campaign-membership-figures.js.map +1 -0
- package/src/lib/model/compile-client-automations.d.ts +65 -0
- package/src/lib/model/compile-client-automations.js +95 -0
- package/src/lib/model/compile-client-automations.js.map +1 -0
- package/src/lib/model/experiments.d.ts +130 -0
- package/src/lib/model/experiments.js +186 -0
- package/src/lib/model/experiments.js.map +1 -0
- package/src/lib/model/index.d.ts +26 -0
- package/src/lib/model/index.js +26 -0
- package/src/lib/model/index.js.map +1 -0
- package/src/lib/model/marketing-record-routes.d.ts +6 -0
- package/src/lib/model/marketing-record-routes.js +46 -0
- package/src/lib/model/marketing-record-routes.js.map +1 -0
- package/src/lib/model/overlay-copy.d.ts +62 -0
- package/src/lib/model/overlay-copy.js +78 -0
- package/src/lib/model/overlay-copy.js.map +1 -0
- package/src/lib/model/overlays.d.ts +113 -0
- package/src/lib/model/overlays.js +93 -0
- package/src/lib/model/overlays.js.map +1 -0
- package/src/lib/model/site-contract.d.ts +100 -0
- package/src/lib/model/site-contract.js +23 -0
- package/src/lib/model/site-contract.js.map +1 -0
- package/src/lib/plugin.d.ts +27 -0
- package/src/lib/plugin.js +168 -0
- package/src/lib/plugin.js.map +1 -0
- package/src/lib/preview-props.d.ts +31 -0
- package/src/lib/preview-props.js +54 -0
- package/src/lib/preview-props.js.map +1 -0
- package/src/lib/server/campaign-manage.d.ts +62 -0
- package/src/lib/server/campaign-manage.js +829 -0
- package/src/lib/server/campaign-manage.js.map +1 -0
- package/src/lib/server/campaign-process-scheduled.d.ts +26 -0
- package/src/lib/server/campaign-process-scheduled.js +198 -0
- package/src/lib/server/campaign-process-scheduled.js.map +1 -0
- package/src/lib/server/campaign-recipients.d.ts +55 -0
- package/src/lib/server/campaign-recipients.js +219 -0
- package/src/lib/server/campaign-recipients.js.map +1 -0
- package/src/lib/server/campaign-send.d.ts +487 -0
- package/src/lib/server/campaign-send.js +3157 -0
- package/src/lib/server/campaign-send.js.map +1 -0
- package/src/lib/server/email-campaign-reach.d.ts +174 -0
- package/src/lib/server/email-campaign-reach.js +254 -0
- package/src/lib/server/email-campaign-reach.js.map +1 -0
- package/src/lib/server/email-events.d.ts +27 -0
- package/src/lib/server/email-events.js +714 -0
- package/src/lib/server/email-events.js.map +1 -0
- package/src/lib/server/get-client-automations.d.ts +60 -0
- package/src/lib/server/get-client-automations.js +87 -0
- package/src/lib/server/get-client-automations.js.map +1 -0
- package/src/lib/server/get-overlays.d.ts +28 -0
- package/src/lib/server/get-overlays.js +65 -0
- package/src/lib/server/get-overlays.js.map +1 -0
- package/src/lib/server/get-screen-experiments.d.ts +42 -0
- package/src/lib/server/get-screen-experiments.js +82 -0
- package/src/lib/server/get-screen-experiments.js.map +1 -0
- package/src/lib/server/lists-materialize.d.ts +18 -0
- package/src/lib/server/lists-materialize.js +130 -0
- package/src/lib/server/lists-materialize.js.map +1 -0
- package/src/lib/server/marketing-figures.d.ts +37 -0
- package/src/lib/server/marketing-figures.js +248 -0
- package/src/lib/server/marketing-figures.js.map +1 -0
- package/src/lib/server/site-page-enricher.d.ts +26 -0
- package/src/lib/server/site-page-enricher.js +210 -0
- package/src/lib/server/site-page-enricher.js.map +1 -0
- package/src/lib/server.d.ts +20 -0
- package/src/lib/server.js +152 -0
- package/src/lib/server.js.map +1 -0
- package/src/lib/site.d.ts +22 -0
- package/src/lib/site.js +48 -0
- package/src/lib/site.js.map +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/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":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ConsolePluginPageProps } from '@aglyn/aglyn';
|
|
2
|
+
/**
|
|
3
|
+
* Marketing page (AGL-251 → AGL-395): the at-a-glance rollup, the email
|
|
4
|
+
* campaigns, the overlay managers (multi-overlay + announcement bar + popup),
|
|
5
|
+
* and A/B testing — owned by the marketing plugin and rendered by the shell's
|
|
6
|
+
* generic plugin route. Each gated card runs its own entitlement check
|
|
7
|
+
* (overlays vs A/B are distinct plan flags) off the shell's resolved `org`;
|
|
8
|
+
* the popup image picker uses the shell's media browser via `useMediaPicker`.
|
|
9
|
+
*
|
|
10
|
+
* A CAMPAIGN belongs here and not on the Emails console because it is a
|
|
11
|
+
* container with a window of dates, a set of lists, a topic and revenue
|
|
12
|
+
* attribution — a marketing object that reaches people by email. Its server
|
|
13
|
+
* half has always lived in this plugin. The individual MESSAGES, the
|
|
14
|
+
* templates they are built from and the identities they leave on stay on
|
|
15
|
+
* Emails, and each email's page links here for its campaign.
|
|
16
|
+
*
|
|
17
|
+
* Sections are ROUTES (AGL-2501). `HubTabs lazy` already mounted one panel, so
|
|
18
|
+
* this is not a read saving — `marketing-console-read-cost.spec.tsx` was
|
|
19
|
+
* written BEFORE the conversion precisely to hold that line, and reports the
|
|
20
|
+
* same counts after. What routing adds is that the URL names the section: it is
|
|
21
|
+
* linkable, the back button walks sections, the breadcrumb says where you are,
|
|
22
|
+
* and "mount only what is open" is structural rather than a `lazy` flag.
|
|
23
|
+
*/
|
|
24
|
+
export declare function MarketingConsolePage(props: ConsolePluginPageProps): import("react").JSX.Element;
|
|
25
|
+
export declare namespace MarketingConsolePage {
|
|
26
|
+
var displayName: string;
|
|
27
|
+
}
|
|
28
|
+
export default MarketingConsolePage;
|
|
@@ -0,0 +1,166 @@
|
|
|
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 { GridItems } from "@aglyn/shared-ui-jsx";
|
|
19
|
+
import { HubSections } from "@aglyn/shared-ui-next";
|
|
20
|
+
import AnnouncementBarCard from "./announcement-bar-card.component.js";
|
|
21
|
+
import CampaignDetailCard from "./campaign-detail-card.js";
|
|
22
|
+
import CampaignsCard from "./campaigns-card.js";
|
|
23
|
+
import CampaignConversionsCard from "./campaign-conversions-card.js";
|
|
24
|
+
import HostExperimentsCard from "./host-experiments-card.component.js";
|
|
25
|
+
import HostMarketingSummaryCard from "./host-marketing-summary-card.component.js";
|
|
26
|
+
import HostOverlaysCard from "./host-overlays-card.component.js";
|
|
27
|
+
import PopupCard from "./popup-card.component.js";
|
|
28
|
+
/**
|
|
29
|
+
* The body of one marketing section, built only when that section is the one
|
|
30
|
+
* being read (AGL-2501).
|
|
31
|
+
*
|
|
32
|
+
* A function rather than a map of nodes on purpose: a `Record<id, ReactNode>`
|
|
33
|
+
* would CONSTRUCT every section every render, and each card opens its
|
|
34
|
+
* Firestore listens on mount — which is the entire cost this page exists to
|
|
35
|
+
* stop paying. Only the returned branch is ever built.
|
|
36
|
+
*/ function sectionBody(section, hostId, org, /**
|
|
37
|
+
* The section's OWN segments — `segments[1]` onward, already sliced by the
|
|
38
|
+
* caller. A section that owns deeper routes reads them here; one that does
|
|
39
|
+
* not simply ignores them.
|
|
40
|
+
*/ detail, basePath) {
|
|
41
|
+
switch(section){
|
|
42
|
+
case 'overview':
|
|
43
|
+
return /*#__PURE__*/ _jsx(HostMarketingSummaryCard, {
|
|
44
|
+
hostId: hostId
|
|
45
|
+
});
|
|
46
|
+
case 'campaigns':
|
|
47
|
+
/*
|
|
48
|
+
* `/marketing/campaigns/{id}` is one campaign, and it is a ROUTE rather
|
|
49
|
+
* than an expanded row for two reasons: it is linkable, which is what a
|
|
50
|
+
* merchant wants to paste into a message about last week's send; and
|
|
51
|
+
* the composer plus the campaign list are the surface's expensive
|
|
52
|
+
* listens, so a reader who came for one campaign's numbers must not pay
|
|
53
|
+
* for them.
|
|
54
|
+
*
|
|
55
|
+
* The id may name a CAMPAIGN or, for every URL minted before campaigns
|
|
56
|
+
* grouped their emails, a single SEND. `CampaignDetailCard` answers that
|
|
57
|
+
* by reading, and falls through to the send's own report — so no pasted
|
|
58
|
+
* link stops resolving.
|
|
59
|
+
*
|
|
60
|
+
* No registry entry is needed — the shell hands a section every segment
|
|
61
|
+
* beneath it, so a section owns its own subtree. The gate is the
|
|
62
|
+
* section's, which is the same gate the composer is behind.
|
|
63
|
+
*/ return detail[0] ? /*#__PURE__*/ _jsx(CampaignDetailCard, {
|
|
64
|
+
hostId: hostId,
|
|
65
|
+
campaignId: detail[0],
|
|
66
|
+
basePath: basePath
|
|
67
|
+
}) : /*#__PURE__*/ _jsx(CampaignsCard, {
|
|
68
|
+
hostId: hostId,
|
|
69
|
+
basePath: basePath
|
|
70
|
+
});
|
|
71
|
+
case 'conversions':
|
|
72
|
+
/*
|
|
73
|
+
* `/marketing/conversions/{campaignId}` narrows the list to one
|
|
74
|
+
* campaign's credited conversions — the drill-down from that campaign's
|
|
75
|
+
* report. A segment rather than a query string, so the section owns its
|
|
76
|
+
* own subtree exactly as `campaigns` does and the URL is linkable.
|
|
77
|
+
*/ return /*#__PURE__*/ _jsx(CampaignConversionsCard, {
|
|
78
|
+
hostId: hostId,
|
|
79
|
+
basePath: basePath,
|
|
80
|
+
campaignId: detail[0]
|
|
81
|
+
});
|
|
82
|
+
case 'overlays':
|
|
83
|
+
return /*#__PURE__*/ _jsx(GridItems, {
|
|
84
|
+
masonry: true,
|
|
85
|
+
spacing: 3,
|
|
86
|
+
items: [
|
|
87
|
+
{
|
|
88
|
+
size: {
|
|
89
|
+
xs: 12
|
|
90
|
+
},
|
|
91
|
+
children: /*#__PURE__*/ _jsx(HostOverlaysCard, {
|
|
92
|
+
hostId: hostId,
|
|
93
|
+
org: org
|
|
94
|
+
})
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
size: {
|
|
98
|
+
xs: 12,
|
|
99
|
+
md: 6
|
|
100
|
+
},
|
|
101
|
+
children: /*#__PURE__*/ _jsx(AnnouncementBarCard, {
|
|
102
|
+
hostId: hostId,
|
|
103
|
+
org: org
|
|
104
|
+
})
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
size: {
|
|
108
|
+
xs: 12,
|
|
109
|
+
md: 6
|
|
110
|
+
},
|
|
111
|
+
children: /*#__PURE__*/ _jsx(PopupCard, {
|
|
112
|
+
hostId: hostId,
|
|
113
|
+
org: org
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
]
|
|
117
|
+
});
|
|
118
|
+
case 'experiments':
|
|
119
|
+
return /*#__PURE__*/ _jsx(HostExperimentsCard, {
|
|
120
|
+
hostId: hostId,
|
|
121
|
+
org: org
|
|
122
|
+
});
|
|
123
|
+
default:
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Marketing page (AGL-251 → AGL-395): the at-a-glance rollup, the email
|
|
129
|
+
* campaigns, the overlay managers (multi-overlay + announcement bar + popup),
|
|
130
|
+
* and A/B testing — owned by the marketing plugin and rendered by the shell's
|
|
131
|
+
* generic plugin route. Each gated card runs its own entitlement check
|
|
132
|
+
* (overlays vs A/B are distinct plan flags) off the shell's resolved `org`;
|
|
133
|
+
* the popup image picker uses the shell's media browser via `useMediaPicker`.
|
|
134
|
+
*
|
|
135
|
+
* A CAMPAIGN belongs here and not on the Emails console because it is a
|
|
136
|
+
* container with a window of dates, a set of lists, a topic and revenue
|
|
137
|
+
* attribution — a marketing object that reaches people by email. Its server
|
|
138
|
+
* half has always lived in this plugin. The individual MESSAGES, the
|
|
139
|
+
* templates they are built from and the identities they leave on stay on
|
|
140
|
+
* Emails, and each email's page links here for its campaign.
|
|
141
|
+
*
|
|
142
|
+
* Sections are ROUTES (AGL-2501). `HubTabs lazy` already mounted one panel, so
|
|
143
|
+
* this is not a read saving — `marketing-console-read-cost.spec.tsx` was
|
|
144
|
+
* written BEFORE the conversion precisely to hold that line, and reports the
|
|
145
|
+
* same counts after. What routing adds is that the URL names the section: it is
|
|
146
|
+
* linkable, the back button walks sections, the breadcrumb says where you are,
|
|
147
|
+
* and "mount only what is open" is structural rather than a `lazy` flag.
|
|
148
|
+
*/ export function MarketingConsolePage(props) {
|
|
149
|
+
const { hostId, org, section, sections, basePath, segments } = props;
|
|
150
|
+
/*
|
|
151
|
+
* Nothing until the URL names a section. The shell redirects a bare hub URL
|
|
152
|
+
* to the landing section and holds a spinner while it does, so this state is
|
|
153
|
+
* transient — and rendering a default section here instead would pay for its
|
|
154
|
+
* listens on a URL that is already being replaced.
|
|
155
|
+
*/ if (!section || !(sections == null ? void 0 : sections.length) || !basePath) return null;
|
|
156
|
+
return /*#__PURE__*/ _jsx(HubSections, {
|
|
157
|
+
sections: sections,
|
|
158
|
+
children: sectionBody(section, hostId, org, (// `segments[0]` IS the section — the shell resolved it into `section`
|
|
159
|
+
// already — so what a section owns is everything after it.
|
|
160
|
+
segments != null ? segments : []).slice(1), basePath)
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
MarketingConsolePage.displayName = 'MarketingConsolePage';
|
|
164
|
+
export default MarketingConsolePage;
|
|
165
|
+
|
|
166
|
+
//# sourceMappingURL=marketing-console-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/marketing-console-page.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 type { AglynOrgBilling, ConsolePluginPageProps } from '@aglyn/aglyn'\nimport { GridItems } from '@aglyn/shared-ui-jsx'\nimport { HubSections } from '@aglyn/shared-ui-next'\nimport type { ReactNode } from 'react'\nimport AnnouncementBarCard from './announcement-bar-card.component'\nimport CampaignDetailCard from './campaign-detail-card'\nimport CampaignsCard from './campaigns-card'\nimport CampaignConversionsCard from './campaign-conversions-card'\nimport HostExperimentsCard from './host-experiments-card.component'\nimport HostMarketingSummaryCard from './host-marketing-summary-card.component'\nimport HostOverlaysCard from './host-overlays-card.component'\nimport PopupCard from './popup-card.component'\nimport type { MarketingConsoleSectionId } from './marketing-console-sections'\n\n/**\n * The body of one marketing section, built only when that section is the one\n * being read (AGL-2501).\n *\n * A function rather than a map of nodes on purpose: a `Record<id, ReactNode>`\n * would CONSTRUCT every section every render, and each card opens its\n * Firestore listens on mount — which is the entire cost this page exists to\n * stop paying. Only the returned branch is ever built.\n */\nfunction sectionBody(\n section: MarketingConsoleSectionId,\n hostId: string,\n org: Partial<AglynOrgBilling> | undefined,\n /**\n * The section's OWN segments — `segments[1]` onward, already sliced by the\n * caller. A section that owns deeper routes reads them here; one that does\n * not simply ignores them.\n */\n detail: readonly string[],\n basePath: string,\n): ReactNode {\n switch (section) {\n case 'overview':\n return <HostMarketingSummaryCard hostId={hostId} />\n case 'campaigns':\n /*\n * `/marketing/campaigns/{id}` is one campaign, and it is a ROUTE rather\n * than an expanded row for two reasons: it is linkable, which is what a\n * merchant wants to paste into a message about last week's send; and\n * the composer plus the campaign list are the surface's expensive\n * listens, so a reader who came for one campaign's numbers must not pay\n * for them.\n *\n * The id may name a CAMPAIGN or, for every URL minted before campaigns\n * grouped their emails, a single SEND. `CampaignDetailCard` answers that\n * by reading, and falls through to the send's own report — so no pasted\n * link stops resolving.\n *\n * No registry entry is needed — the shell hands a section every segment\n * beneath it, so a section owns its own subtree. The gate is the\n * section's, which is the same gate the composer is behind.\n */\n return detail[0] ? (\n <CampaignDetailCard\n hostId={hostId}\n campaignId={detail[0]}\n basePath={basePath}\n />\n ) : (\n <CampaignsCard hostId={hostId} basePath={basePath} />\n )\n case 'conversions':\n /*\n * `/marketing/conversions/{campaignId}` narrows the list to one\n * campaign's credited conversions — the drill-down from that campaign's\n * report. A segment rather than a query string, so the section owns its\n * own subtree exactly as `campaigns` does and the URL is linkable.\n */\n return (\n <CampaignConversionsCard\n hostId={hostId}\n basePath={basePath}\n campaignId={detail[0]}\n />\n )\n case 'overlays':\n return (\n <GridItems\n masonry\n spacing={3}\n items={[\n {\n size: { xs: 12 },\n children: <HostOverlaysCard hostId={hostId} org={org} />,\n },\n {\n size: { xs: 12, md: 6 },\n children: <AnnouncementBarCard hostId={hostId} org={org} />,\n },\n {\n size: { xs: 12, md: 6 },\n children: <PopupCard hostId={hostId} org={org} />,\n },\n ]}\n />\n )\n case 'experiments':\n return <HostExperimentsCard hostId={hostId} org={org} />\n default:\n return null\n }\n}\n\n/**\n * Marketing page (AGL-251 → AGL-395): the at-a-glance rollup, the email\n * campaigns, the overlay managers (multi-overlay + announcement bar + popup),\n * and A/B testing — owned by the marketing plugin and rendered by the shell's\n * generic plugin route. Each gated card runs its own entitlement check\n * (overlays vs A/B are distinct plan flags) off the shell's resolved `org`;\n * the popup image picker uses the shell's media browser via `useMediaPicker`.\n *\n * A CAMPAIGN belongs here and not on the Emails console because it is a\n * container with a window of dates, a set of lists, a topic and revenue\n * attribution — a marketing object that reaches people by email. Its server\n * half has always lived in this plugin. The individual MESSAGES, the\n * templates they are built from and the identities they leave on stay on\n * Emails, and each email's page links here for its campaign.\n *\n * Sections are ROUTES (AGL-2501). `HubTabs lazy` already mounted one panel, so\n * this is not a read saving — `marketing-console-read-cost.spec.tsx` was\n * written BEFORE the conversion precisely to hold that line, and reports the\n * same counts after. What routing adds is that the URL names the section: it is\n * linkable, the back button walks sections, the breadcrumb says where you are,\n * and \"mount only what is open\" is structural rather than a `lazy` flag.\n */\nexport function MarketingConsolePage(props: ConsolePluginPageProps) {\n const { hostId, org, section, sections, basePath, segments } = props\n\n /*\n * Nothing until the URL names a section. The shell redirects a bare hub URL\n * to the landing section and holds a spinner while it does, so this state is\n * transient — and rendering a default section here instead would pay for its\n * listens on a URL that is already being replaced.\n */\n if (!section || !sections?.length || !basePath) return null\n\n return (\n <HubSections sections={sections}>\n {sectionBody(\n section as MarketingConsoleSectionId,\n hostId,\n org,\n // `segments[0]` IS the section — the shell resolved it into `section`\n // already — so what a section owns is everything after it.\n (segments ?? []).slice(1),\n basePath,\n )}\n </HubSections>\n )\n}\nMarketingConsolePage.displayName = 'MarketingConsolePage'\n\nexport default MarketingConsolePage\n"],"names":["GridItems","HubSections","AnnouncementBarCard","CampaignDetailCard","CampaignsCard","CampaignConversionsCard","HostExperimentsCard","HostMarketingSummaryCard","HostOverlaysCard","PopupCard","sectionBody","section","hostId","org","detail","basePath","campaignId","masonry","spacing","items","size","xs","children","md","MarketingConsolePage","props","sections","segments","length","slice","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAGA,SAASA,SAAS,QAAQ,uBAAsB;AAChD,SAASC,WAAW,QAAQ,wBAAuB;AAEnD,OAAOC,yBAAyB,uCAAmC;AACnE,OAAOC,wBAAwB,4BAAwB;AACvD,OAAOC,mBAAmB,sBAAkB;AAC5C,OAAOC,6BAA6B,iCAA6B;AACjE,OAAOC,yBAAyB,uCAAmC;AACnE,OAAOC,8BAA8B,6CAAyC;AAC9E,OAAOC,sBAAsB,oCAAgC;AAC7D,OAAOC,eAAe,4BAAwB;AAG9C;;;;;;;;CAQC,GACD,SAASC,YACPC,OAAkC,EAClCC,MAAc,EACdC,GAAyC,EACzC;;;;GAIC,GACDC,MAAyB,EACzBC,QAAgB;IAEhB,OAAQJ;QACN,KAAK;YACH,qBAAO,KAACJ;gBAAyBK,QAAQA;;QAC3C,KAAK;YACH;;;;;;;;;;;;;;;;OAgBC,GACD,OAAOE,MAAM,CAAC,EAAE,iBACd,KAACX;gBACCS,QAAQA;gBACRI,YAAYF,MAAM,CAAC,EAAE;gBACrBC,UAAUA;+BAGZ,KAACX;gBAAcQ,QAAQA;gBAAQG,UAAUA;;QAE7C,KAAK;YACH;;;;;OAKC,GACD,qBACE,KAACV;gBACCO,QAAQA;gBACRG,UAAUA;gBACVC,YAAYF,MAAM,CAAC,EAAE;;QAG3B,KAAK;YACH,qBACE,KAACd;gBACCiB,OAAO;gBACPC,SAAS;gBACTC,OAAO;oBACL;wBACEC,MAAM;4BAAEC,IAAI;wBAAG;wBACfC,wBAAU,KAACd;4BAAiBI,QAAQA;4BAAQC,KAAKA;;oBACnD;oBACA;wBACEO,MAAM;4BAAEC,IAAI;4BAAIE,IAAI;wBAAE;wBACtBD,wBAAU,KAACpB;4BAAoBU,QAAQA;4BAAQC,KAAKA;;oBACtD;oBACA;wBACEO,MAAM;4BAAEC,IAAI;4BAAIE,IAAI;wBAAE;wBACtBD,wBAAU,KAACb;4BAAUG,QAAQA;4BAAQC,KAAKA;;oBAC5C;iBACD;;QAGP,KAAK;YACH,qBAAO,KAACP;gBAAoBM,QAAQA;gBAAQC,KAAKA;;QACnD;YACE,OAAO;IACX;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;CAqBC,GACD,OAAO,SAASW,qBAAqBC,KAA6B;IAChE,MAAM,EAAEb,MAAM,EAAEC,GAAG,EAAEF,OAAO,EAAEe,QAAQ,EAAEX,QAAQ,EAAEY,QAAQ,EAAE,GAAGF;IAE/D;;;;;GAKC,GACD,IAAI,CAACd,WAAW,EAACe,4BAAAA,SAAUE,MAAM,KAAI,CAACb,UAAU,OAAO;IAEvD,qBACE,KAACd;QAAYyB,UAAUA;kBACpBhB,YACCC,SACAC,QACAC,KAGA,CAFA,sEAAsE;QACtE,2DAA2D;QAC1Dc,mBAAAA,WAAY,EAAE,EAAEE,KAAK,CAAC,IACvBd;;AAIR;AACAS,qBAAqBM,WAAW,GAAG;AAEnC,eAAeN,qBAAoB"}
|
|
@@ -0,0 +1,34 @@
|
|
|
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 { ConsoleNavSection } from '@aglyn/aglyn';
|
|
18
|
+
export type MarketingConsoleSectionId = 'overview' | 'campaigns' | 'conversions' | 'overlays' | 'experiments';
|
|
19
|
+
/**
|
|
20
|
+
* The marketing console's sections, in rail order (AGL-2501).
|
|
21
|
+
*
|
|
22
|
+
* One list, read twice and never copied: `plugin.ts` registers it on the nav
|
|
23
|
+
* item so the shell can route and gate each section, and the page switches its
|
|
24
|
+
* body on the id the shell resolves back.
|
|
25
|
+
*
|
|
26
|
+
* Ids are the `?tab=` ids this page already deep-linked by, kept deliberately
|
|
27
|
+
* so a bookmark that named a tab names the same section as a route. `experiments`
|
|
28
|
+
* therefore keeps its id while its LABEL reads "A/B testing".
|
|
29
|
+
*
|
|
30
|
+
* No `navTabId` on any of them: they inherit the Marketing nav item's gate.
|
|
31
|
+
* The per-card ENTITLEMENT checks are unchanged and still live in the cards —
|
|
32
|
+
* overlays and A/B are distinct plan flags, and a plan is not a release flag.
|
|
33
|
+
*/
|
|
34
|
+
export declare const MARKETING_CONSOLE_SECTIONS: readonly ConsoleNavSection[];
|
|
@@ -0,0 +1,77 @@
|
|
|
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
|
+
* The marketing console's sections, in rail order (AGL-2501).
|
|
18
|
+
*
|
|
19
|
+
* One list, read twice and never copied: `plugin.ts` registers it on the nav
|
|
20
|
+
* item so the shell can route and gate each section, and the page switches its
|
|
21
|
+
* body on the id the shell resolves back.
|
|
22
|
+
*
|
|
23
|
+
* Ids are the `?tab=` ids this page already deep-linked by, kept deliberately
|
|
24
|
+
* so a bookmark that named a tab names the same section as a route. `experiments`
|
|
25
|
+
* therefore keeps its id while its LABEL reads "A/B testing".
|
|
26
|
+
*
|
|
27
|
+
* No `navTabId` on any of them: they inherit the Marketing nav item's gate.
|
|
28
|
+
* The per-card ENTITLEMENT checks are unchanged and still live in the cards —
|
|
29
|
+
* overlays and A/B are distinct plan flags, and a plan is not a release flag.
|
|
30
|
+
*/ export const MARKETING_CONSOLE_SECTIONS = [
|
|
31
|
+
{
|
|
32
|
+
id: 'overview',
|
|
33
|
+
label: 'Overview'
|
|
34
|
+
},
|
|
35
|
+
/*
|
|
36
|
+
* A campaign is a CONTAINER with a window of dates, a set of lists, a topic
|
|
37
|
+
* and revenue attribution — a marketing object that reaches people by
|
|
38
|
+
* email, rather than an email object. It sits beside A/B testing because
|
|
39
|
+
* that is what campaign results are attributed against, and beside the
|
|
40
|
+
* overlays because both are ways of putting the same offer in front of the
|
|
41
|
+
* same audience.
|
|
42
|
+
*
|
|
43
|
+
* The individual MESSAGES stay on the Emails console, along with the
|
|
44
|
+
* templates they are built from and the sending identities they leave on.
|
|
45
|
+
* Each email's page links here for the campaign it belongs to.
|
|
46
|
+
*/ {
|
|
47
|
+
id: 'campaigns',
|
|
48
|
+
label: 'Campaigns'
|
|
49
|
+
},
|
|
50
|
+
/*
|
|
51
|
+
* What the campaigns CAUSED, from the conversions' end. A campaign's own
|
|
52
|
+
* report answers this for that campaign; a section is what answers it for
|
|
53
|
+
* the site, and it is the only place two things can be said that a campaign
|
|
54
|
+
* report structurally cannot: the conversions credited to a tagged web link
|
|
55
|
+
* rather than to a campaign document, and the ones credited to nothing at
|
|
56
|
+
* all. A surface listing only the credited ones renders "we credited nine
|
|
57
|
+
* of these" as "nine of these happened".
|
|
58
|
+
*/ {
|
|
59
|
+
id: 'conversions',
|
|
60
|
+
label: 'Conversions'
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: 'overlays',
|
|
64
|
+
label: 'Overlays'
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: 'experiments',
|
|
68
|
+
label: 'A/B testing'
|
|
69
|
+
}
|
|
70
|
+
] /*
|
|
71
|
+
* Rail ORDER decides where `/marketing` lands: the shell redirects a bare hub
|
|
72
|
+
* URL to the first section in this list the reader may open (AGL-2501). There
|
|
73
|
+
* is deliberately no separate default constant — a second place to say which
|
|
74
|
+
* section is first is a second place for it to disagree with the rail.
|
|
75
|
+
*/ ;
|
|
76
|
+
|
|
77
|
+
//# sourceMappingURL=marketing-console-sections.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/marketing-console-sections.ts"],"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\nimport type { ConsoleNavSection } from '@aglyn/aglyn'\n\nexport type MarketingConsoleSectionId =\n | 'overview'\n | 'campaigns'\n | 'conversions'\n | 'overlays'\n | 'experiments'\n\n/**\n * The marketing console's sections, in rail order (AGL-2501).\n *\n * One list, read twice and never copied: `plugin.ts` registers it on the nav\n * item so the shell can route and gate each section, and the page switches its\n * body on the id the shell resolves back.\n *\n * Ids are the `?tab=` ids this page already deep-linked by, kept deliberately\n * so a bookmark that named a tab names the same section as a route. `experiments`\n * therefore keeps its id while its LABEL reads \"A/B testing\".\n *\n * No `navTabId` on any of them: they inherit the Marketing nav item's gate.\n * The per-card ENTITLEMENT checks are unchanged and still live in the cards —\n * overlays and A/B are distinct plan flags, and a plan is not a release flag.\n */\nexport const MARKETING_CONSOLE_SECTIONS: readonly ConsoleNavSection[] = [\n { id: 'overview', label: 'Overview' },\n /*\n * A campaign is a CONTAINER with a window of dates, a set of lists, a topic\n * and revenue attribution — a marketing object that reaches people by\n * email, rather than an email object. It sits beside A/B testing because\n * that is what campaign results are attributed against, and beside the\n * overlays because both are ways of putting the same offer in front of the\n * same audience.\n *\n * The individual MESSAGES stay on the Emails console, along with the\n * templates they are built from and the sending identities they leave on.\n * Each email's page links here for the campaign it belongs to.\n */\n { id: 'campaigns', label: 'Campaigns' },\n /*\n * What the campaigns CAUSED, from the conversions' end. A campaign's own\n * report answers this for that campaign; a section is what answers it for\n * the site, and it is the only place two things can be said that a campaign\n * report structurally cannot: the conversions credited to a tagged web link\n * rather than to a campaign document, and the ones credited to nothing at\n * all. A surface listing only the credited ones renders \"we credited nine\n * of these\" as \"nine of these happened\".\n */\n { id: 'conversions', label: 'Conversions' },\n { id: 'overlays', label: 'Overlays' },\n { id: 'experiments', label: 'A/B testing' },\n]\n\n/*\n * Rail ORDER decides where `/marketing` lands: the shell redirects a bare hub\n * URL to the first section in this list the reader may open (AGL-2501). There\n * is deliberately no separate default constant — a second place to say which\n * section is first is a second place for it to disagree with the rail.\n */\n"],"names":["MARKETING_CONSOLE_SECTIONS","id","label"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAWD;;;;;;;;;;;;;;CAcC,GACD,OAAO,MAAMA,6BAA2D;IACtE;QAAEC,IAAI;QAAYC,OAAO;IAAW;IACpC;;;;;;;;;;;GAWC,GACD;QAAED,IAAI;QAAaC,OAAO;IAAY;IACtC;;;;;;;;GAQC,GACD;QAAED,IAAI;QAAeC,OAAO;IAAc;IAC1C;QAAED,IAAI;QAAYC,OAAO;IAAW;IACpC;QAAED,IAAI;QAAeC,OAAO;IAAc;CAC3C,CAED;;;;;CAKC,IAPA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 14-day overlay metrics line (AGL-200): sums the `overlays` map from the
|
|
3
|
+
* AGL-82 analytics day docs. `impressionKey`/`actionKey` pick which
|
|
4
|
+
* counters a given card cares about (popup vs announcement bar).
|
|
5
|
+
*/
|
|
6
|
+
export declare function OverlayStatsRow(props: {
|
|
7
|
+
hostId: string;
|
|
8
|
+
impressionKey?: string;
|
|
9
|
+
actionKey: string;
|
|
10
|
+
actionLabel: string;
|
|
11
|
+
}): import("react").JSX.Element;
|
|
12
|
+
export declare namespace OverlayStatsRow {
|
|
13
|
+
var displayName: string;
|
|
14
|
+
}
|
|
15
|
+
export default OverlayStatsRow;
|
|
@@ -0,0 +1,78 @@
|
|
|
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 { Typography } from "@mui/material";
|
|
19
|
+
import { doc, getDoc } from "firebase/firestore";
|
|
20
|
+
import { useEffect, useState } from "react";
|
|
21
|
+
import { useFirestore } from "@aglyn/tenant-feature-instance";
|
|
22
|
+
const DAYS = 14;
|
|
23
|
+
/**
|
|
24
|
+
* 14-day overlay metrics line (AGL-200): sums the `overlays` map from the
|
|
25
|
+
* AGL-82 analytics day docs. `impressionKey`/`actionKey` pick which
|
|
26
|
+
* counters a given card cares about (popup vs announcement bar).
|
|
27
|
+
*/ export function OverlayStatsRow(props) {
|
|
28
|
+
var _stats_impressionKey, _stats_actionKey;
|
|
29
|
+
const { hostId, impressionKey, actionKey, actionLabel } = props;
|
|
30
|
+
const firestore = useFirestore();
|
|
31
|
+
const [stats, setStats] = useState(null);
|
|
32
|
+
useEffect(()=>{
|
|
33
|
+
let active = true;
|
|
34
|
+
const ids = Array.from({
|
|
35
|
+
length: DAYS
|
|
36
|
+
}, (_, index)=>{
|
|
37
|
+
const date = new Date();
|
|
38
|
+
date.setDate(date.getDate() - (DAYS - 1 - index));
|
|
39
|
+
return date.toISOString().slice(0, 10);
|
|
40
|
+
});
|
|
41
|
+
void Promise.all(ids.map((id)=>getDoc(doc(firestore, 'hosts', hostId, 'analytics', id)).then((snapshot)=>{
|
|
42
|
+
var _snapshot_get;
|
|
43
|
+
return (_snapshot_get = snapshot.get('overlays')) != null ? _snapshot_get : {};
|
|
44
|
+
}).catch(()=>({})))).then((days)=>{
|
|
45
|
+
if (!active) return;
|
|
46
|
+
const totals = {};
|
|
47
|
+
for (const day of days){
|
|
48
|
+
for (const [key, value] of Object.entries(day)){
|
|
49
|
+
var _totals_key;
|
|
50
|
+
totals[key] = ((_totals_key = totals[key]) != null ? _totals_key : 0) + Number(value != null ? value : 0);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
setStats(totals);
|
|
54
|
+
});
|
|
55
|
+
return ()=>{
|
|
56
|
+
active = false;
|
|
57
|
+
};
|
|
58
|
+
}, [
|
|
59
|
+
firestore,
|
|
60
|
+
hostId
|
|
61
|
+
]);
|
|
62
|
+
if (!stats) return null;
|
|
63
|
+
const impressions = impressionKey ? (_stats_impressionKey = stats[impressionKey]) != null ? _stats_impressionKey : 0 : null;
|
|
64
|
+
const actions = (_stats_actionKey = stats[actionKey]) != null ? _stats_actionKey : 0;
|
|
65
|
+
if (!impressions && !actions) return null;
|
|
66
|
+
return /*#__PURE__*/ _jsx(Typography, {
|
|
67
|
+
variant: "caption",
|
|
68
|
+
color: "text.secondary",
|
|
69
|
+
children: [
|
|
70
|
+
impressions !== null ? `${impressions.toLocaleString()} impressions` : null,
|
|
71
|
+
`${actions.toLocaleString()} ${actionLabel}`
|
|
72
|
+
].filter(Boolean).join(' · ') + ' (14 days)'
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
OverlayStatsRow.displayName = 'OverlayStatsRow';
|
|
76
|
+
export default OverlayStatsRow;
|
|
77
|
+
|
|
78
|
+
//# sourceMappingURL=overlay-stats-row.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/overlay-stats-row.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 { Typography } from '@mui/material'\nimport { doc, getDoc } from 'firebase/firestore'\nimport { useEffect, useState } from 'react'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\n\nconst DAYS = 14\n\n/**\n * 14-day overlay metrics line (AGL-200): sums the `overlays` map from the\n * AGL-82 analytics day docs. `impressionKey`/`actionKey` pick which\n * counters a given card cares about (popup vs announcement bar).\n */\nexport function OverlayStatsRow(props: {\n hostId: string\n impressionKey?: string\n actionKey: string\n actionLabel: string\n}) {\n const { hostId, impressionKey, actionKey, actionLabel } = props\n const firestore = useFirestore()\n const [stats, setStats] = useState<Record<string, number> | null>(null)\n\n useEffect(() => {\n let active = true\n const ids = Array.from({ length: DAYS }, (_, index) => {\n const date = new Date()\n date.setDate(date.getDate() - (DAYS - 1 - index))\n return date.toISOString().slice(0, 10)\n })\n void Promise.all(\n ids.map((id) =>\n getDoc(doc(firestore, 'hosts', hostId, 'analytics', id))\n .then(\n (snapshot) =>\n (snapshot.get('overlays') ?? {}) as Record<string, number>,\n )\n .catch(() => ({}) as Record<string, number>),\n ),\n ).then((days) => {\n if (!active) return\n const totals: Record<string, number> = {}\n for (const day of days) {\n for (const [key, value] of Object.entries(day)) {\n totals[key] = (totals[key] ?? 0) + Number(value ?? 0)\n }\n }\n setStats(totals)\n })\n return () => {\n active = false\n }\n }, [firestore, hostId])\n\n if (!stats) return null\n const impressions = impressionKey ? (stats[impressionKey] ?? 0) : null\n const actions = stats[actionKey] ?? 0\n if (!impressions && !actions) return null\n return (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {[\n impressions !== null\n ? `${impressions.toLocaleString()} impressions`\n : null,\n `${actions.toLocaleString()} ${actionLabel}`,\n ]\n .filter(Boolean)\n .join(' · ') + ' (14 days)'}\n </Typography>\n )\n}\nOverlayStatsRow.displayName = 'OverlayStatsRow'\n\nexport default OverlayStatsRow\n"],"names":["Typography","doc","getDoc","useEffect","useState","useFirestore","DAYS","OverlayStatsRow","props","stats","hostId","impressionKey","actionKey","actionLabel","firestore","setStats","active","ids","Array","from","length","_","index","date","Date","setDate","getDate","toISOString","slice","Promise","all","map","id","then","snapshot","get","catch","days","totals","day","key","value","Object","entries","Number","impressions","actions","variant","color","toLocaleString","filter","Boolean","join","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SAASA,UAAU,QAAQ,gBAAe;AAC1C,SAASC,GAAG,EAAEC,MAAM,QAAQ,qBAAoB;AAChD,SAASC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAC3C,SAASC,YAAY,QAAQ,iCAAgC;AAE7D,MAAMC,OAAO;AAEb;;;;CAIC,GACD,OAAO,SAASC,gBAAgBC,KAK/B;QAqCsCC,sBACrBA;IArChB,MAAM,EAAEC,MAAM,EAAEC,aAAa,EAAEC,SAAS,EAAEC,WAAW,EAAE,GAAGL;IAC1D,MAAMM,YAAYT;IAClB,MAAM,CAACI,OAAOM,SAAS,GAAGX,SAAwC;IAElED,UAAU;QACR,IAAIa,SAAS;QACb,MAAMC,MAAMC,MAAMC,IAAI,CAAC;YAAEC,QAAQd;QAAK,GAAG,CAACe,GAAGC;YAC3C,MAAMC,OAAO,IAAIC;YACjBD,KAAKE,OAAO,CAACF,KAAKG,OAAO,KAAMpB,CAAAA,OAAO,IAAIgB,KAAI;YAC9C,OAAOC,KAAKI,WAAW,GAAGC,KAAK,CAAC,GAAG;QACrC;QACA,KAAKC,QAAQC,GAAG,CACdb,IAAIc,GAAG,CAAC,CAACC,KACP9B,OAAOD,IAAIa,WAAW,SAASJ,QAAQ,aAAasB,KACjDC,IAAI,CACH,CAACC;oBACEA;wBAAAA,gBAAAA,SAASC,GAAG,CAAC,uBAAbD,gBAA4B,CAAC;eAEjCE,KAAK,CAAC,IAAO,CAAA,CAAC,CAAA,KAEnBH,IAAI,CAAC,CAACI;YACN,IAAI,CAACrB,QAAQ;YACb,MAAMsB,SAAiC,CAAC;YACxC,KAAK,MAAMC,OAAOF,KAAM;gBACtB,KAAK,MAAM,CAACG,KAAKC,MAAM,IAAIC,OAAOC,OAAO,CAACJ,KAAM;wBAC/BD;oBAAfA,MAAM,CAACE,IAAI,GAAG,EAACF,cAAAA,MAAM,CAACE,IAAI,YAAXF,cAAe,KAAKM,OAAOH,gBAAAA,QAAS;gBACrD;YACF;YACA1B,SAASuB;QACX;QACA,OAAO;YACLtB,SAAS;QACX;IACF,GAAG;QAACF;QAAWJ;KAAO;IAEtB,IAAI,CAACD,OAAO,OAAO;IACnB,MAAMoC,cAAclC,iBAAiBF,uBAAAA,KAAK,CAACE,cAAc,YAApBF,uBAAwB,IAAK;IAClE,MAAMqC,WAAUrC,mBAAAA,KAAK,CAACG,UAAU,YAAhBH,mBAAoB;IACpC,IAAI,CAACoC,eAAe,CAACC,SAAS,OAAO;IACrC,qBACE,KAAC9C;QAAW+C,SAAQ;QAAUC,OAAM;kBACjC;YACCH,gBAAgB,OACZ,GAAGA,YAAYI,cAAc,GAAG,YAAY,CAAC,GAC7C;YACJ,GAAGH,QAAQG,cAAc,GAAG,CAAC,EAAEpC,aAAa;SAC7C,CACEqC,MAAM,CAACC,SACPC,IAAI,CAAC,SAAS;;AAGvB;AACA7C,gBAAgB8C,WAAW,GAAG;AAE9B,eAAe9C,gBAAe"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type AglynOrgBilling } from '@aglyn/aglyn';
|
|
2
|
+
export interface PopupCardProps {
|
|
3
|
+
hostId: string;
|
|
4
|
+
/** Resolved entitlement source (AGL-395). */
|
|
5
|
+
org?: Partial<AglynOrgBilling>;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Promotional popup editor (AGL-196): one popup per host, persisted on the
|
|
9
|
+
* host doc; the tenant render handles triggers, scheduling, and
|
|
10
|
+
* frequency capping. marketingOverlays-gated (Starter+). The headline and
|
|
11
|
+
* body are edited through `useOverlayCopyEditor`, like the bar's text
|
|
12
|
+
* (AGL-2885).
|
|
13
|
+
*/
|
|
14
|
+
export declare function PopupCard(props: PopupCardProps): import("react").JSX.Element;
|
|
15
|
+
export declare namespace PopupCard {
|
|
16
|
+
var displayName: string;
|
|
17
|
+
}
|
|
18
|
+
export default PopupCard;
|