@aglyn/plugins-marketing 1.0.0-beta.231 → 1.0.0-beta.232

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.
Files changed (29) hide show
  1. package/package.json +16 -16
  2. package/src/lib/components/announcement-bar-card.component.js +1 -0
  3. package/src/lib/components/announcement-bar-card.component.js.map +1 -1
  4. package/src/lib/components/campaign-composer.js +1 -0
  5. package/src/lib/components/campaign-composer.js.map +1 -1
  6. package/src/lib/components/campaign-detail-card.js +1 -0
  7. package/src/lib/components/campaign-detail-card.js.map +1 -1
  8. package/src/lib/components/campaign-edit-drawer.js +2 -0
  9. package/src/lib/components/campaign-edit-drawer.js.map +1 -1
  10. package/src/lib/components/campaign-members-section.js +1 -0
  11. package/src/lib/components/campaign-members-section.js.map +1 -1
  12. package/src/lib/components/campaign-reach-sections.js +3 -0
  13. package/src/lib/components/campaign-reach-sections.js.map +1 -1
  14. package/src/lib/components/email-detail.js +3 -1
  15. package/src/lib/components/email-detail.js.map +1 -1
  16. package/src/lib/components/host-experiments-card.component.js +1 -0
  17. package/src/lib/components/host-experiments-card.component.js.map +1 -1
  18. package/src/lib/components/host-marketing-summary-card.component.js +1 -0
  19. package/src/lib/components/host-marketing-summary-card.component.js.map +1 -1
  20. package/src/lib/components/org-experiments-card.js +1 -0
  21. package/src/lib/components/org-experiments-card.js.map +1 -1
  22. package/src/lib/components/org-marketing-overview-card.js +11 -0
  23. package/src/lib/components/org-marketing-overview-card.js.map +1 -1
  24. package/src/lib/components/org-overlays-card.js +1 -0
  25. package/src/lib/components/org-overlays-card.js.map +1 -1
  26. package/src/lib/components/popup-card.component.js +4 -0
  27. package/src/lib/components/popup-card.component.js.map +1 -1
  28. package/src/lib/components/staff-org-email-card.component.js +3 -0
  29. package/src/lib/components/staff-org-email-card.component.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/org-marketing-overview-card.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n type AglynOrgBilling,\n checkEntitlement,\n pluginDocsHelp,\n} from '@aglyn/aglyn'\nimport { AppLink, CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { TABLE_PAGE_SIZE_DEFAULT } from '@aglyn/shared-ui-jsx/const/table-pagination'\nimport {\n Alert,\n Divider,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n Typography,\n} from '@mui/material'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport { useMemo, useState } from 'react'\nimport { campaignSendsQuery } from './campaign-queries'\nimport {\n readSendFigures,\n readSiteExperimentFigures,\n readSiteOverlayFigures,\n type SiteExperimentFigures,\n type SiteOverlayFigures,\n} from './marketing-aggregates'\nimport { useMarketingOrgMount } from './marketing-org-mount'\nimport { orgSiteSectionHref } from './org-site-picker-button'\nimport { useOneShotRead, useOrgSiteReads } from './use-one-shot-reads'\n\n/**\n * How many sites the Overview reads figures for.\n *\n * The one read on this card that grows with the organization: four\n * aggregations a site. Capped, the landing section of the org hub costs four\n * org-wide aggregations and four for each site up to this number, however\n * many sites there are; the sites past it are counted in a note rather than\n * read.\n */\nexport const ORG_OVERVIEW_SITE_CAP = 25\n\n/** One site's figures: `null` for a channel the plan does not carry. */\ninterface SiteFigures {\n overlays: SiteOverlayFigures | null\n tests: SiteExperimentFigures | null\n}\n\n/** A figure as a cell prints it: a dash for one nobody has measured. */\nconst figure = (value: number | null | undefined): string =>\n value == null ? '—' : value.toLocaleString()\n\n/**\n * A column's total, or `null` unless EVERY site in it answered. A total over\n * the sites that happened to answer would print as the organization's figure\n * while leaving some of it out.\n */\nfunction totalOf(values: ReadonlyArray<number | null | undefined>): number | null {\n let total = 0\n for (const value of values) {\n if (value == null) return null\n total += value\n }\n return total\n}\n\nexport interface OrgMarketingOverviewCardProps {\n /** The org's plan, for which channels it carries. */\n org?: Partial<AglynOrgBilling>\n}\n\n/**\n * Marketing at a glance, over every site — the landing section of the\n * organization's Marketing hub.\n *\n * The email figures come from ONE collection: campaign sends belong to the\n * organization (`orgs/{orgId}/campaigns`), so four aggregations over it\n * answer for every site at once. Overlays and A/B tests are a SITE's —\n * drawn on its pages, splitting its traffic — so their figures are read per\n * site, each site one row, and each row opens that site's own Overview.\n *\n * A channel the plan does not carry is left out rather than read: an\n * organization without overlays or A/B testing pays for no per-site reads\n * at all, and its landing is the email figures alone.\n */\nexport function OrgMarketingOverviewCard(props: OrgMarketingOverviewCardProps) {\n const { org } = props\n const mount = useMarketingOrgMount()\n const firestore = useFirestore()\n const orgId = mount?.orgId ?? null\n const overlaysOn = checkEntitlement(org, 'marketingOverlays')\n const testsOn = checkEntitlement(org, 'abTesting')\n\n const email = useOneShotRead(() => {\n const sends = campaignSendsQuery(firestore, orgId, null)\n return sends ? readSendFigures(sends) : null\n }, [firestore, orgId])\n\n const allSites = useMemo(() => mount?.hosts ?? [], [mount])\n const sites = useMemo(\n () => allSites.slice(0, ORG_OVERVIEW_SITE_CAP),\n [allSites],\n )\n const perSite = useOrgSiteReads<SiteFigures>(\n overlaysOn || testsOn ? sites.map((site) => site.id) : [],\n (hostId) =>\n Promise.all([\n overlaysOn ? readSiteOverlayFigures(firestore, hostId) : null,\n testsOn ? readSiteExperimentFigures(firestore, hostId) : null,\n ]).then(([overlays, tests]) => ({ overlays, tests })),\n [firestore, overlaysOn, testsOn],\n )\n\n const rows = useMemo(\n () =>\n sites.map((site) => {\n const answer = perSite.reads.get(site.id)?.value ?? null\n return {\n id: site.id,\n name: site.name || site.subdomain || site.id,\n href: mount ? orgSiteSectionHref(mount, site.id, 'overview') : null,\n views: answer?.overlays?.views ?? null,\n clicks: answer?.overlays?.clicks ?? null,\n running: answer?.tests?.running ?? null,\n decided: answer?.tests?.decided ?? null,\n }\n }),\n [sites, perSite.reads, mount],\n )\n const totals = useMemo(\n () => ({\n views: totalOf(rows.map((row) => row.views)),\n clicks: totalOf(rows.map((row) => row.clicks)),\n running: totalOf(rows.map((row) => row.running)),\n decided: totalOf(rows.map((row) => row.decided)),\n }),\n [rows],\n )\n\n const [page, setPage] = useState(0)\n const [pageSize, setPageSize] = useState(TABLE_PAGE_SIZE_DEFAULT)\n const pageRows = rows.slice(page * pageSize, (page + 1) * pageSize)\n const truncated = allSites.length > sites.length\n\n const sends = email.value\n const emailMetrics: Array<{ label: string; value: string }> = [\n { label: 'Emails sent', value: figure(sends?.sent) },\n {\n label: 'Opens / clicks',\n value: `${figure(sends?.opens)} / ${figure(sends?.clicks)}`,\n },\n ...(sends?.scheduled\n ? [{ label: 'Scheduled sends', value: figure(sends.scheduled) }]\n : []),\n ]\n\n return (\n <CardDisplay\n header={'At a glance'}\n subheader={'Every site in this organization'}\n help={pluginDocsHelp('marketingOverlays', {\n anchor: '#across-your-sites',\n })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n <Stack\n direction=\"row\"\n spacing={3}\n sx={{ flexWrap: 'wrap', rowGap: 1.5 }}\n >\n {emailMetrics.map((metric) => (\n <Stack key={metric.label} spacing={0}>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {metric.label}\n </Typography>\n <Typography variant=\"h6\">{metric.value}</Typography>\n </Stack>\n ))}\n </Stack>\n\n {overlaysOn || testsOn ? (\n <>\n <Divider />\n {mount && !mount.hostsReady ? null : !sites.length ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'This organization has no sites yet, so there are no ' +\n 'overlays or A/B tests to count.'}\n </Typography>\n ) : (\n <Stack spacing={1}>\n {truncated ? (\n <Alert severity=\"info\">\n {`This organization has ${allSites.length.toLocaleString()} ` +\n `sites. The table and its totals cover the first ` +\n `${ORG_OVERVIEW_SITE_CAP.toLocaleString()}; each other ` +\n \"site's own Marketing page has its figures.\"}\n </Alert>\n ) : null}\n <ScrollTable size=\"small\" aria-label=\"Figures by site\">\n <TableHead>\n <TableRow>\n <TableCell>{'Site'}</TableCell>\n {overlaysOn ? (\n <>\n <TableCell align=\"right\">{'Overlay views'}</TableCell>\n <TableCell align=\"right\">{'Overlay clicks'}</TableCell>\n </>\n ) : null}\n {testsOn ? (\n <>\n <TableCell align=\"right\">{'Tests running'}</TableCell>\n <TableCell align=\"right\">{'Tests decided'}</TableCell>\n </>\n ) : null}\n </TableRow>\n </TableHead>\n <TableBody>\n {pageRows.map((row) => (\n <TableRow key={row.id}>\n <TableCell>\n {row.href ? (\n <AppLink href={row.href}>{row.name}</AppLink>\n ) : (\n row.name\n )}\n </TableCell>\n {overlaysOn ? (\n <>\n <TableCell align=\"right\">{figure(row.views)}</TableCell>\n <TableCell align=\"right\">{figure(row.clicks)}</TableCell>\n </>\n ) : null}\n {testsOn ? (\n <>\n <TableCell align=\"right\">{figure(row.running)}</TableCell>\n <TableCell align=\"right\">{figure(row.decided)}</TableCell>\n </>\n ) : null}\n </TableRow>\n ))}\n {/*\n THE TOTALS, under every page: they are the sum of every\n site the card read, not of the page on screen, and say\n which when not every site was read.\n */}\n <TableRow>\n <TableCell sx={{ fontWeight: 600 }}>\n {truncated\n ? `These ${sites.length.toLocaleString()} sites`\n : 'All sites'}\n </TableCell>\n {overlaysOn ? (\n <>\n <TableCell align=\"right\" sx={{ fontWeight: 600 }}>\n {figure(totals.views)}\n </TableCell>\n <TableCell align=\"right\" sx={{ fontWeight: 600 }}>\n {figure(totals.clicks)}\n </TableCell>\n </>\n ) : null}\n {testsOn ? (\n <>\n <TableCell align=\"right\" sx={{ fontWeight: 600 }}>\n {figure(totals.running)}\n </TableCell>\n <TableCell align=\"right\" sx={{ fontWeight: 600 }}>\n {figure(totals.decided)}\n </TableCell>\n </>\n ) : null}\n </TableRow>\n </TableBody>\n </ScrollTable>\n <ListPagination\n page={page}\n pageSize={pageSize}\n rowCount={pageRows.length}\n count={rows.length}\n onPageChange={setPage}\n onPageSizeChange={setPageSize}\n />\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Overlay views and clicks are lifetime counts. A dash is ' +\n 'a figure that has not arrived or could not be read — ' +\n 'never a zero.'}\n </Typography>\n </Stack>\n )}\n </>\n ) : null}\n </Stack>\n </CardDisplay>\n )\n}\nOrgMarketingOverviewCard.displayName = 'OrgMarketingOverviewCard'\n\nexport default OrgMarketingOverviewCard\n"],"names":["checkEntitlement","pluginDocsHelp","AppLink","CardDisplay","ListPagination","ScrollTable","TABLE_PAGE_SIZE_DEFAULT","Alert","Divider","Stack","TableBody","TableCell","TableHead","TableRow","Typography","useFirestore","useMemo","useState","campaignSendsQuery","readSendFigures","readSiteExperimentFigures","readSiteOverlayFigures","useMarketingOrgMount","orgSiteSectionHref","useOneShotRead","useOrgSiteReads","ORG_OVERVIEW_SITE_CAP","figure","value","toLocaleString","totalOf","values","total","OrgMarketingOverviewCard","props","org","mount","firestore","orgId","overlaysOn","testsOn","email","sends","allSites","hosts","sites","slice","perSite","map","site","id","hostId","Promise","all","then","overlays","tests","rows","answer","reads","get","name","subdomain","href","views","clicks","running","decided","totals","row","page","setPage","pageSize","setPageSize","pageRows","truncated","length","emailMetrics","label","sent","opens","scheduled","header","subheader","help","anchor","contentGutterX","contentGutterY","spacing","direction","sx","flexWrap","rowGap","metric","variant","color","hostsReady","severity","size","aria-label","align","fontWeight","rowCount","count","onPageChange","onPageSizeChange","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/org-marketing-overview-card.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n type AglynOrgBilling,\n checkEntitlement,\n pluginDocsHelp,\n} from '@aglyn/aglyn'\nimport { AppLink, CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { TABLE_PAGE_SIZE_DEFAULT } from '@aglyn/shared-ui-jsx/const/table-pagination'\nimport {\n Alert,\n Divider,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n Typography,\n} from '@mui/material'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport { useMemo, useState } from 'react'\nimport { campaignSendsQuery } from './campaign-queries'\nimport {\n readSendFigures,\n readSiteExperimentFigures,\n readSiteOverlayFigures,\n type SiteExperimentFigures,\n type SiteOverlayFigures,\n} from './marketing-aggregates'\nimport { useMarketingOrgMount } from './marketing-org-mount'\nimport { orgSiteSectionHref } from './org-site-picker-button'\nimport { useOneShotRead, useOrgSiteReads } from './use-one-shot-reads'\n\n/**\n * How many sites the Overview reads figures for.\n *\n * The one read on this card that grows with the organization: four\n * aggregations a site. Capped, the landing section of the org hub costs four\n * org-wide aggregations and four for each site up to this number, however\n * many sites there are; the sites past it are counted in a note rather than\n * read.\n */\nexport const ORG_OVERVIEW_SITE_CAP = 25\n\n/** One site's figures: `null` for a channel the plan does not carry. */\ninterface SiteFigures {\n overlays: SiteOverlayFigures | null\n tests: SiteExperimentFigures | null\n}\n\n/** A figure as a cell prints it: a dash for one nobody has measured. */\nconst figure = (value: number | null | undefined): string =>\n value == null ? '—' : value.toLocaleString()\n\n/**\n * A column's total, or `null` unless EVERY site in it answered. A total over\n * the sites that happened to answer would print as the organization's figure\n * while leaving some of it out.\n */\nfunction totalOf(values: ReadonlyArray<number | null | undefined>): number | null {\n let total = 0\n for (const value of values) {\n if (value == null) return null\n total += value\n }\n return total\n}\n\nexport interface OrgMarketingOverviewCardProps {\n /** The org's plan, for which channels it carries. */\n org?: Partial<AglynOrgBilling>\n}\n\n/**\n * Marketing at a glance, over every site — the landing section of the\n * organization's Marketing hub.\n *\n * The email figures come from ONE collection: campaign sends belong to the\n * organization (`orgs/{orgId}/campaigns`), so four aggregations over it\n * answer for every site at once. Overlays and A/B tests are a SITE's —\n * drawn on its pages, splitting its traffic — so their figures are read per\n * site, each site one row, and each row opens that site's own Overview.\n *\n * A channel the plan does not carry is left out rather than read: an\n * organization without overlays or A/B testing pays for no per-site reads\n * at all, and its landing is the email figures alone.\n */\nexport function OrgMarketingOverviewCard(props: OrgMarketingOverviewCardProps) {\n const { org } = props\n const mount = useMarketingOrgMount()\n const firestore = useFirestore()\n const orgId = mount?.orgId ?? null\n const overlaysOn = checkEntitlement(org, 'marketingOverlays')\n const testsOn = checkEntitlement(org, 'abTesting')\n\n const email = useOneShotRead(() => {\n const sends = campaignSendsQuery(firestore, orgId, null)\n return sends ? readSendFigures(sends) : null\n }, [firestore, orgId])\n\n const allSites = useMemo(() => mount?.hosts ?? [], [mount])\n const sites = useMemo(\n () => allSites.slice(0, ORG_OVERVIEW_SITE_CAP),\n [allSites],\n )\n const perSite = useOrgSiteReads<SiteFigures>(\n overlaysOn || testsOn ? sites.map((site) => site.id) : [],\n (hostId) =>\n Promise.all([\n overlaysOn ? readSiteOverlayFigures(firestore, hostId) : null,\n testsOn ? readSiteExperimentFigures(firestore, hostId) : null,\n ]).then(([overlays, tests]) => ({ overlays, tests })),\n [firestore, overlaysOn, testsOn],\n )\n\n const rows = useMemo(\n () =>\n sites.map((site) => {\n const answer = perSite.reads.get(site.id)?.value ?? null\n return {\n id: site.id,\n name: site.name || site.subdomain || site.id,\n href: mount ? orgSiteSectionHref(mount, site.id, 'overview') : null,\n views: answer?.overlays?.views ?? null,\n clicks: answer?.overlays?.clicks ?? null,\n running: answer?.tests?.running ?? null,\n decided: answer?.tests?.decided ?? null,\n }\n }),\n [sites, perSite.reads, mount],\n )\n const totals = useMemo(\n () => ({\n views: totalOf(rows.map((row) => row.views)),\n clicks: totalOf(rows.map((row) => row.clicks)),\n running: totalOf(rows.map((row) => row.running)),\n decided: totalOf(rows.map((row) => row.decided)),\n }),\n [rows],\n )\n\n const [page, setPage] = useState(0)\n const [pageSize, setPageSize] = useState(TABLE_PAGE_SIZE_DEFAULT)\n const pageRows = rows.slice(page * pageSize, (page + 1) * pageSize)\n const truncated = allSites.length > sites.length\n\n const sends = email.value\n const emailMetrics: Array<{ label: string; value: string }> = [\n { label: 'Emails sent', value: figure(sends?.sent) },\n {\n label: 'Opens / clicks',\n value: `${figure(sends?.opens)} / ${figure(sends?.clicks)}`,\n },\n ...(sends?.scheduled\n ? [{ label: 'Scheduled sends', value: figure(sends.scheduled) }]\n : []),\n ]\n\n return (\n <CardDisplay\n header={'At a glance'}\n subheader={'Every site in this organization'}\n help={pluginDocsHelp('marketingOverlays', {\n anchor: '#across-your-sites',\n })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n <Stack\n useFlexGap\n direction=\"row\"\n spacing={3}\n sx={{ flexWrap: 'wrap', rowGap: 1.5 }}\n >\n {emailMetrics.map((metric) => (\n <Stack key={metric.label} spacing={0}>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {metric.label}\n </Typography>\n <Typography variant=\"h6\">{metric.value}</Typography>\n </Stack>\n ))}\n </Stack>\n\n {overlaysOn || testsOn ? (\n <>\n <Divider />\n {mount && !mount.hostsReady ? null : !sites.length ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'This organization has no sites yet, so there are no ' +\n 'overlays or A/B tests to count.'}\n </Typography>\n ) : (\n <Stack spacing={1}>\n {truncated ? (\n <Alert severity=\"info\">\n {`This organization has ${allSites.length.toLocaleString()} ` +\n `sites. The table and its totals cover the first ` +\n `${ORG_OVERVIEW_SITE_CAP.toLocaleString()}; each other ` +\n \"site's own Marketing page has its figures.\"}\n </Alert>\n ) : null}\n <ScrollTable\n size=\"small\"\n aria-label=\"Figures by site\"\n // Below md the figure columns scroll sideways rather than\n // folding each two-word header onto three lines.\n sx={{ '& th': { whiteSpace: { xs: 'nowrap', md: 'normal' } } }}\n >\n <TableHead>\n <TableRow>\n <TableCell>{'Site'}</TableCell>\n {overlaysOn ? (\n <>\n <TableCell align=\"right\">{'Overlay views'}</TableCell>\n <TableCell align=\"right\">{'Overlay clicks'}</TableCell>\n </>\n ) : null}\n {testsOn ? (\n <>\n <TableCell align=\"right\">{'Tests running'}</TableCell>\n <TableCell align=\"right\">{'Tests decided'}</TableCell>\n </>\n ) : null}\n </TableRow>\n </TableHead>\n <TableBody>\n {pageRows.map((row) => (\n <TableRow key={row.id}>\n <TableCell>\n {row.href ? (\n <AppLink href={row.href}>{row.name}</AppLink>\n ) : (\n row.name\n )}\n </TableCell>\n {overlaysOn ? (\n <>\n <TableCell align=\"right\">{figure(row.views)}</TableCell>\n <TableCell align=\"right\">{figure(row.clicks)}</TableCell>\n </>\n ) : null}\n {testsOn ? (\n <>\n <TableCell align=\"right\">{figure(row.running)}</TableCell>\n <TableCell align=\"right\">{figure(row.decided)}</TableCell>\n </>\n ) : null}\n </TableRow>\n ))}\n {/*\n THE TOTALS, under every page: they are the sum of every\n site the card read, not of the page on screen, and say\n which when not every site was read.\n */}\n <TableRow>\n <TableCell sx={{ fontWeight: 600 }}>\n {truncated\n ? `These ${sites.length.toLocaleString()} sites`\n : 'All sites'}\n </TableCell>\n {overlaysOn ? (\n <>\n <TableCell align=\"right\" sx={{ fontWeight: 600 }}>\n {figure(totals.views)}\n </TableCell>\n <TableCell align=\"right\" sx={{ fontWeight: 600 }}>\n {figure(totals.clicks)}\n </TableCell>\n </>\n ) : null}\n {testsOn ? (\n <>\n <TableCell align=\"right\" sx={{ fontWeight: 600 }}>\n {figure(totals.running)}\n </TableCell>\n <TableCell align=\"right\" sx={{ fontWeight: 600 }}>\n {figure(totals.decided)}\n </TableCell>\n </>\n ) : null}\n </TableRow>\n </TableBody>\n </ScrollTable>\n <ListPagination\n page={page}\n pageSize={pageSize}\n rowCount={pageRows.length}\n count={rows.length}\n onPageChange={setPage}\n onPageSizeChange={setPageSize}\n />\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Overlay views and clicks are lifetime counts. A dash is ' +\n 'a figure that has not arrived or could not be read — ' +\n 'never a zero.'}\n </Typography>\n </Stack>\n )}\n </>\n ) : null}\n </Stack>\n </CardDisplay>\n )\n}\nOrgMarketingOverviewCard.displayName = 'OrgMarketingOverviewCard'\n\nexport default OrgMarketingOverviewCard\n"],"names":["checkEntitlement","pluginDocsHelp","AppLink","CardDisplay","ListPagination","ScrollTable","TABLE_PAGE_SIZE_DEFAULT","Alert","Divider","Stack","TableBody","TableCell","TableHead","TableRow","Typography","useFirestore","useMemo","useState","campaignSendsQuery","readSendFigures","readSiteExperimentFigures","readSiteOverlayFigures","useMarketingOrgMount","orgSiteSectionHref","useOneShotRead","useOrgSiteReads","ORG_OVERVIEW_SITE_CAP","figure","value","toLocaleString","totalOf","values","total","OrgMarketingOverviewCard","props","org","mount","firestore","orgId","overlaysOn","testsOn","email","sends","allSites","hosts","sites","slice","perSite","map","site","id","hostId","Promise","all","then","overlays","tests","rows","answer","reads","get","name","subdomain","href","views","clicks","running","decided","totals","row","page","setPage","pageSize","setPageSize","pageRows","truncated","length","emailMetrics","label","sent","opens","scheduled","header","subheader","help","anchor","contentGutterX","contentGutterY","spacing","useFlexGap","direction","sx","flexWrap","rowGap","metric","variant","color","hostsReady","severity","size","aria-label","whiteSpace","xs","md","align","fontWeight","rowCount","count","onPageChange","onPageSizeChange","displayName"],"mappings":"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"}
@@ -226,6 +226,7 @@ const STATUS_CHIP = {
226
226
  children: /*#__PURE__*/ _jsx(TableCell, {
227
227
  colSpan: COLUMNS,
228
228
  children: /*#__PURE__*/ _jsxs(Stack, {
229
+ useFlexGap: true,
229
230
  direction: "row",
230
231
  spacing: 2,
231
232
  sx: {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/org-overlays-card.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n type AglynOrgBilling,\n checkEntitlement,\n pluginDocsHelp,\n} from '@aglyn/aglyn'\nimport { compareOverlayPrecedence, overlayStatus, type HostOverlay } from '../model'\nimport { AppLink, CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n Chip,\n Stack,\n Switch,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n Typography,\n} from '@mui/material'\nimport {\n collection,\n doc,\n getDoc,\n getDocs,\n limit,\n orderBy,\n query,\n type Firestore,\n} from 'firebase/firestore'\nimport { Fragment, useState } from 'react'\nimport { useFirestore, useUser } from '@aglyn/tenant-feature-instance'\nimport { writeSiteWideChange } from '@aglyn/tenant-feature-instance/hooks/helpers/site-wide-change'\nimport { ceilingedWindow } from '@aglyn/tenant-feature-instance/hooks/host-collection-queries'\nimport { useMarketingOrgMount } from './marketing-org-mount'\nimport {\n OrgSitePickerButton,\n orgSiteSectionHref,\n} from './org-site-picker-button'\nimport {\n overlayDisplayName,\n overlayEngagementLabel,\n overlayPagesLabel,\n overlayWindowLabel,\n} from './overlay-labels'\nimport { ORG_SITES_PER_PAGE, useOrgSiteReads } from './use-one-shot-reads'\n\n/**\n * How many overlays are read for each site.\n *\n * Smaller than the site's own ceiling on purpose: a site runs a handful of\n * bars and popups, a page here holds a page of sites' worth of them, and a\n * site with more says so on its row and links to its own list, where every\n * one of them is — and where the order they take can be changed.\n */\nexport const ORG_OVERLAYS_PER_SITE = 10\n\ntype OverlayRow = HostOverlay & { $id: string }\n\n/** What one site's group of rows is drawn from. */\ninterface SiteOverlays {\n /** In the order the site shows them: see `compareOverlayPrecedence`. */\n rows: OverlayRow[]\n /** The site has more overlays than were read. */\n truncated: boolean\n /**\n * Whether the site's always-on default bar and popup would show — the ones\n * a matching overlay takes priority over. `null` when the site document\n * could not be read, which is drawn as nothing rather than as \"off\".\n */\n defaults: { bar: boolean; popup: boolean } | null\n}\n\n/**\n * One site's overlays and its defaults: the overlays ordered and ceilinged\n * the way the site's own list reads them — `name` is on every overlay its\n * only creator writes — and the host document for the defaults, which the\n * console shell already listens to for its site list.\n */\nasync function readSiteOverlays(\n firestore: Firestore,\n hostId: string,\n): Promise<SiteOverlays> {\n const [overlays, host] = await Promise.all([\n getDocs(\n query(\n collection(firestore, 'hosts', hostId, 'overlays'),\n orderBy('name'),\n limit(ORG_OVERLAYS_PER_SITE + 1),\n ),\n ),\n getDoc(doc(firestore, 'hosts', hostId)).catch(() => null),\n ])\n const { rows, truncated } = ceilingedWindow(\n overlays.docs.map(\n (entry) => ({ ...(entry.data() as HostOverlay), $id: entry.id }) as OverlayRow,\n ),\n ORG_OVERLAYS_PER_SITE,\n )\n const site = host?.exists() ? (host.data() as Record<string, any>) : null\n return {\n // In the order the site shows them. A window the ceiling cut short is\n // the first overlays by name, and the site's line says so.\n rows: [...rows].sort(compareOverlayPrecedence),\n truncated,\n // The same test the page enricher applies before it draws either one.\n defaults: site\n ? {\n bar: Boolean(site.announcementBar?.enabled && site.announcementBar?.text),\n popup: Boolean(site.popup?.enabled && site.popup?.body),\n }\n : null,\n }\n}\n\nconst STATUS_CHIP: Record<\n ReturnType<typeof overlayStatus>,\n { label: string; color: 'default' | 'info' | 'success' }\n> = {\n off: { label: 'Off', color: 'default' },\n scheduled: { label: 'Scheduled', color: 'info' },\n live: { label: 'Live', color: 'success' },\n}\n\n/** Columns in a row, so a site's own line can span all of them. */\nconst COLUMNS = 7\n\nexport interface OrgOverlaysCardProps {\n /** The org's plan, for the `marketingOverlays` entitlement. */\n org?: Partial<AglynOrgBilling>\n}\n\n/**\n * Every site's announcement bars and popups, on the organization's\n * Marketing hub.\n *\n * READ-ONLY but for the switch. An overlay is drawn on one site's pages and\n * written against that site's variables, screens and order, so its editor\n * stays on the site: each row's Edit and the New button open the site's own\n * Overlays section. What the org hub adds is the view across sites — which\n * of them are showing what, and their lifetime engagement — and the one\n * change that needs no editor, turning an overlay on or off.\n *\n * Grouped by site, each group in the order the site shows its overlays,\n * under a line naming the site and whether its default bar and popup are\n * on. No 14-day analytics row: that is a document read per day per site,\n * and it lives on the site.\n */\nexport function OrgOverlaysCard(props: OrgOverlaysCardProps) {\n const { org } = props\n const mount = useMarketingOrgMount()\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const entitled = checkEntitlement(org, 'marketingOverlays')\n\n const sites = mount?.hosts ?? []\n const [page, setPage] = useState(0)\n const pageSites = sites.slice(\n page * ORG_SITES_PER_PAGE,\n (page + 1) * ORG_SITES_PER_PAGE,\n )\n const { reads, patch } = useOrgSiteReads<SiteOverlays>(\n entitled ? pageSites.map((site) => site.id) : [],\n (hostId) => readSiteOverlays(firestore, hostId),\n [firestore, entitled],\n )\n\n /**\n * One field, merged, on the row's own site — the write the site's own\n * switch makes, and one the rules allow every editor of that site.\n */\n const handleToggle = async (hostId: string, overlay: OverlayRow) => {\n const enabled = overlay.enabled === false\n try {\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.set(\n doc(firestore, 'hosts', hostId, 'overlays', overlay.$id),\n { enabled },\n { merge: true },\n ),\n })\n patch(hostId, (site) => ({\n ...site,\n rows: site.rows.map((row) =>\n row.$id === overlay.$id ? { ...row, enabled } : row,\n ),\n }))\n } catch (error) {\n console.error(error)\n enqueueSnackbar('The overlay could not be switched', {\n variant: 'error',\n })\n }\n }\n\n return (\n <CardDisplay\n header=\"Announcement bars & popups\"\n subheader=\"Every site in this organization\"\n help={pluginDocsHelp('marketingOverlays', {\n anchor: '#across-your-sites',\n })}\n contentGutterX\n contentGutterY\n contentBordered=\"all\"\n >\n {!entitled ? (\n <Alert severity=\"info\">\n {'Announcement bars and popups are included from the Starter ' +\n 'plan — see Billing to upgrade.'}\n </Alert>\n ) : !mount || !mount.hostsReady ? null : !sites.length ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'This organization has no sites yet. Overlays are drawn on a ' +\n 'site’s pages, so they are created on a site.'}\n </Typography>\n ) : (\n <Stack spacing={2}>\n <Stack\n direction=\"row\"\n spacing={2}\n sx={{ alignItems: 'center', justifyContent: 'space-between' }}\n >\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Each site shows the first matching bar and popup in its own ' +\n 'order. Edit and create them on the site; switch them on or ' +\n 'off here.'}\n </Typography>\n <OrgSitePickerButton\n mount={mount}\n section=\"overlays\"\n label=\"New overlay\"\n />\n </Stack>\n <ScrollTable size=\"small\" aria-label=\"Overlays by site\">\n <TableHead>\n <TableRow>\n <TableCell>{'Overlay'}</TableCell>\n <TableCell>{'Kind'}</TableCell>\n <TableCell>{'Status'}</TableCell>\n <TableCell>{'Window'}</TableCell>\n <TableCell>{'Pages'}</TableCell>\n <TableCell>{'Engagement'}</TableCell>\n <TableCell align=\"right\">{'Actions'}</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {pageSites.map((site) => {\n const read = reads.get(site.id)\n const answer = read?.value ?? null\n const href = orgSiteSectionHref(mount, site.id, 'overlays')\n const name = site.name || site.subdomain || site.id\n return (\n <Fragment key={site.id}>\n {/* The site's own line: its name, its defaults, and what was not read. */}\n <TableRow>\n <TableCell colSpan={COLUMNS}>\n <Stack\n direction=\"row\"\n spacing={2}\n sx={{ alignItems: 'baseline', flexWrap: 'wrap' }}\n >\n <Typography variant=\"subtitle2\">\n {href ? <AppLink href={href}>{name}</AppLink> : name}\n </Typography>\n {answer?.defaults ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`Default bar ${answer.defaults.bar ? 'on' : 'off'} · ` +\n `default popup ${answer.defaults.popup ? 'on' : 'off'}`}\n </Typography>\n ) : null}\n {answer?.truncated ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`More than ${ORG_OVERLAYS_PER_SITE} on this ` +\n 'site — these are the first by name; the ' +\n 'site’s own list has every one.'}\n </Typography>\n ) : null}\n </Stack>\n </TableCell>\n </TableRow>\n {read?.status === 'error' ? (\n <TableRow>\n <TableCell colSpan={COLUMNS}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'This site’s overlays could not be read.'}\n </Typography>\n </TableCell>\n </TableRow>\n ) : answer && !answer.rows.length ? (\n <TableRow>\n <TableCell colSpan={COLUMNS}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No overlays on this site.'}\n </Typography>\n </TableCell>\n </TableRow>\n ) : (\n (answer?.rows ?? []).map((overlay) => {\n const status = STATUS_CHIP[overlayStatus(overlay, Date.now())]\n return (\n <TableRow key={overlay.$id}>\n <TableCell>{overlayDisplayName(overlay)}</TableCell>\n <TableCell>\n {overlay.kind === 'bar' ? 'Bar' : 'Popup'}\n </TableCell>\n <TableCell>\n <Chip\n size=\"small\"\n color={status.color}\n label={status.label}\n />\n </TableCell>\n <TableCell>{overlayWindowLabel(overlay)}</TableCell>\n <TableCell>{overlayPagesLabel(overlay)}</TableCell>\n <TableCell>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {overlayEngagementLabel(overlay.stats)}\n </Typography>\n </TableCell>\n <TableCell align=\"right\">\n <Switch\n size=\"small\"\n checked={overlay.enabled !== false}\n onChange={() => void handleToggle(site.id, overlay)}\n slotProps={{\n input: {\n 'aria-label': `Show ${overlayDisplayName(overlay)} on ${name}`,\n },\n }}\n />\n {/* A site the shell could not address has no Overlays section to open. */}\n {href ? (\n <AppLink\n componentVariant=\"button\"\n href={href}\n size=\"small\"\n color=\"primary\"\n aria-label={`Edit ${overlayDisplayName(overlay)} on ${name}`}\n >\n {'Edit'}\n </AppLink>\n ) : null}\n </TableCell>\n </TableRow>\n )\n })\n )}\n </Fragment>\n )\n })}\n </TableBody>\n </ScrollTable>\n <ListPagination\n page={page}\n pageSize={ORG_SITES_PER_PAGE}\n rowCount={pageSites.length}\n count={sites.length}\n onPageChange={setPage}\n labelDisplayedRows={({ from, to, count }) =>\n `Sites ${from}–${to} of ${count}`\n }\n />\n </Stack>\n )}\n </CardDisplay>\n )\n}\nOrgOverlaysCard.displayName = 'OrgOverlaysCard'\n\nexport default OrgOverlaysCard\n"],"names":["checkEntitlement","pluginDocsHelp","compareOverlayPrecedence","overlayStatus","AppLink","CardDisplay","ListPagination","ScrollTable","useSnackbar","Alert","Chip","Stack","Switch","TableBody","TableCell","TableHead","TableRow","Typography","collection","doc","getDoc","getDocs","limit","orderBy","query","Fragment","useState","useFirestore","useUser","writeSiteWideChange","ceilingedWindow","useMarketingOrgMount","OrgSitePickerButton","orgSiteSectionHref","overlayDisplayName","overlayEngagementLabel","overlayPagesLabel","overlayWindowLabel","ORG_SITES_PER_PAGE","useOrgSiteReads","ORG_OVERLAYS_PER_SITE","readSiteOverlays","firestore","hostId","site","overlays","host","Promise","all","catch","rows","truncated","docs","map","entry","data","$id","id","exists","sort","defaults","bar","Boolean","announcementBar","enabled","text","popup","body","STATUS_CHIP","off","label","color","scheduled","live","COLUMNS","OrgOverlaysCard","props","org","mount","user","enqueueSnackbar","entitled","sites","hosts","page","setPage","pageSites","slice","reads","patch","handleToggle","overlay","write","batch","set","merge","row","error","console","variant","header","subheader","help","anchor","contentGutterX","contentGutterY","contentBordered","severity","hostsReady","length","spacing","direction","sx","alignItems","justifyContent","section","size","aria-label","align","read","get","answer","value","href","name","subdomain","colSpan","flexWrap","status","Date","now","kind","stats","checked","onChange","slotProps","input","componentVariant","pageSize","rowCount","count","onPageChange","labelDisplayedRows","from","to","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/org-overlays-card.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n type AglynOrgBilling,\n checkEntitlement,\n pluginDocsHelp,\n} from '@aglyn/aglyn'\nimport { compareOverlayPrecedence, overlayStatus, type HostOverlay } from '../model'\nimport { AppLink, CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n Chip,\n Stack,\n Switch,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n Typography,\n} from '@mui/material'\nimport {\n collection,\n doc,\n getDoc,\n getDocs,\n limit,\n orderBy,\n query,\n type Firestore,\n} from 'firebase/firestore'\nimport { Fragment, useState } from 'react'\nimport { useFirestore, useUser } from '@aglyn/tenant-feature-instance'\nimport { writeSiteWideChange } from '@aglyn/tenant-feature-instance/hooks/helpers/site-wide-change'\nimport { ceilingedWindow } from '@aglyn/tenant-feature-instance/hooks/host-collection-queries'\nimport { useMarketingOrgMount } from './marketing-org-mount'\nimport {\n OrgSitePickerButton,\n orgSiteSectionHref,\n} from './org-site-picker-button'\nimport {\n overlayDisplayName,\n overlayEngagementLabel,\n overlayPagesLabel,\n overlayWindowLabel,\n} from './overlay-labels'\nimport { ORG_SITES_PER_PAGE, useOrgSiteReads } from './use-one-shot-reads'\n\n/**\n * How many overlays are read for each site.\n *\n * Smaller than the site's own ceiling on purpose: a site runs a handful of\n * bars and popups, a page here holds a page of sites' worth of them, and a\n * site with more says so on its row and links to its own list, where every\n * one of them is — and where the order they take can be changed.\n */\nexport const ORG_OVERLAYS_PER_SITE = 10\n\ntype OverlayRow = HostOverlay & { $id: string }\n\n/** What one site's group of rows is drawn from. */\ninterface SiteOverlays {\n /** In the order the site shows them: see `compareOverlayPrecedence`. */\n rows: OverlayRow[]\n /** The site has more overlays than were read. */\n truncated: boolean\n /**\n * Whether the site's always-on default bar and popup would show — the ones\n * a matching overlay takes priority over. `null` when the site document\n * could not be read, which is drawn as nothing rather than as \"off\".\n */\n defaults: { bar: boolean; popup: boolean } | null\n}\n\n/**\n * One site's overlays and its defaults: the overlays ordered and ceilinged\n * the way the site's own list reads them — `name` is on every overlay its\n * only creator writes — and the host document for the defaults, which the\n * console shell already listens to for its site list.\n */\nasync function readSiteOverlays(\n firestore: Firestore,\n hostId: string,\n): Promise<SiteOverlays> {\n const [overlays, host] = await Promise.all([\n getDocs(\n query(\n collection(firestore, 'hosts', hostId, 'overlays'),\n orderBy('name'),\n limit(ORG_OVERLAYS_PER_SITE + 1),\n ),\n ),\n getDoc(doc(firestore, 'hosts', hostId)).catch(() => null),\n ])\n const { rows, truncated } = ceilingedWindow(\n overlays.docs.map(\n (entry) => ({ ...(entry.data() as HostOverlay), $id: entry.id }) as OverlayRow,\n ),\n ORG_OVERLAYS_PER_SITE,\n )\n const site = host?.exists() ? (host.data() as Record<string, any>) : null\n return {\n // In the order the site shows them. A window the ceiling cut short is\n // the first overlays by name, and the site's line says so.\n rows: [...rows].sort(compareOverlayPrecedence),\n truncated,\n // The same test the page enricher applies before it draws either one.\n defaults: site\n ? {\n bar: Boolean(site.announcementBar?.enabled && site.announcementBar?.text),\n popup: Boolean(site.popup?.enabled && site.popup?.body),\n }\n : null,\n }\n}\n\nconst STATUS_CHIP: Record<\n ReturnType<typeof overlayStatus>,\n { label: string; color: 'default' | 'info' | 'success' }\n> = {\n off: { label: 'Off', color: 'default' },\n scheduled: { label: 'Scheduled', color: 'info' },\n live: { label: 'Live', color: 'success' },\n}\n\n/** Columns in a row, so a site's own line can span all of them. */\nconst COLUMNS = 7\n\nexport interface OrgOverlaysCardProps {\n /** The org's plan, for the `marketingOverlays` entitlement. */\n org?: Partial<AglynOrgBilling>\n}\n\n/**\n * Every site's announcement bars and popups, on the organization's\n * Marketing hub.\n *\n * READ-ONLY but for the switch. An overlay is drawn on one site's pages and\n * written against that site's variables, screens and order, so its editor\n * stays on the site: each row's Edit and the New button open the site's own\n * Overlays section. What the org hub adds is the view across sites — which\n * of them are showing what, and their lifetime engagement — and the one\n * change that needs no editor, turning an overlay on or off.\n *\n * Grouped by site, each group in the order the site shows its overlays,\n * under a line naming the site and whether its default bar and popup are\n * on. No 14-day analytics row: that is a document read per day per site,\n * and it lives on the site.\n */\nexport function OrgOverlaysCard(props: OrgOverlaysCardProps) {\n const { org } = props\n const mount = useMarketingOrgMount()\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const entitled = checkEntitlement(org, 'marketingOverlays')\n\n const sites = mount?.hosts ?? []\n const [page, setPage] = useState(0)\n const pageSites = sites.slice(\n page * ORG_SITES_PER_PAGE,\n (page + 1) * ORG_SITES_PER_PAGE,\n )\n const { reads, patch } = useOrgSiteReads<SiteOverlays>(\n entitled ? pageSites.map((site) => site.id) : [],\n (hostId) => readSiteOverlays(firestore, hostId),\n [firestore, entitled],\n )\n\n /**\n * One field, merged, on the row's own site — the write the site's own\n * switch makes, and one the rules allow every editor of that site.\n */\n const handleToggle = async (hostId: string, overlay: OverlayRow) => {\n const enabled = overlay.enabled === false\n try {\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.set(\n doc(firestore, 'hosts', hostId, 'overlays', overlay.$id),\n { enabled },\n { merge: true },\n ),\n })\n patch(hostId, (site) => ({\n ...site,\n rows: site.rows.map((row) =>\n row.$id === overlay.$id ? { ...row, enabled } : row,\n ),\n }))\n } catch (error) {\n console.error(error)\n enqueueSnackbar('The overlay could not be switched', {\n variant: 'error',\n })\n }\n }\n\n return (\n <CardDisplay\n header=\"Announcement bars & popups\"\n subheader=\"Every site in this organization\"\n help={pluginDocsHelp('marketingOverlays', {\n anchor: '#across-your-sites',\n })}\n contentGutterX\n contentGutterY\n contentBordered=\"all\"\n >\n {!entitled ? (\n <Alert severity=\"info\">\n {'Announcement bars and popups are included from the Starter ' +\n 'plan — see Billing to upgrade.'}\n </Alert>\n ) : !mount || !mount.hostsReady ? null : !sites.length ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'This organization has no sites yet. Overlays are drawn on a ' +\n 'site’s pages, so they are created on a site.'}\n </Typography>\n ) : (\n <Stack spacing={2}>\n <Stack\n direction=\"row\"\n spacing={2}\n sx={{ alignItems: 'center', justifyContent: 'space-between' }}\n >\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Each site shows the first matching bar and popup in its own ' +\n 'order. Edit and create them on the site; switch them on or ' +\n 'off here.'}\n </Typography>\n <OrgSitePickerButton\n mount={mount}\n section=\"overlays\"\n label=\"New overlay\"\n />\n </Stack>\n <ScrollTable size=\"small\" aria-label=\"Overlays by site\">\n <TableHead>\n <TableRow>\n <TableCell>{'Overlay'}</TableCell>\n <TableCell>{'Kind'}</TableCell>\n <TableCell>{'Status'}</TableCell>\n <TableCell>{'Window'}</TableCell>\n <TableCell>{'Pages'}</TableCell>\n <TableCell>{'Engagement'}</TableCell>\n <TableCell align=\"right\">{'Actions'}</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {pageSites.map((site) => {\n const read = reads.get(site.id)\n const answer = read?.value ?? null\n const href = orgSiteSectionHref(mount, site.id, 'overlays')\n const name = site.name || site.subdomain || site.id\n return (\n <Fragment key={site.id}>\n {/* The site's own line: its name, its defaults, and what was not read. */}\n <TableRow>\n <TableCell colSpan={COLUMNS}>\n <Stack\n useFlexGap\n direction=\"row\"\n spacing={2}\n sx={{ alignItems: 'baseline', flexWrap: 'wrap' }}\n >\n <Typography variant=\"subtitle2\">\n {href ? <AppLink href={href}>{name}</AppLink> : name}\n </Typography>\n {answer?.defaults ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`Default bar ${answer.defaults.bar ? 'on' : 'off'} · ` +\n `default popup ${answer.defaults.popup ? 'on' : 'off'}`}\n </Typography>\n ) : null}\n {answer?.truncated ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`More than ${ORG_OVERLAYS_PER_SITE} on this ` +\n 'site — these are the first by name; the ' +\n 'site’s own list has every one.'}\n </Typography>\n ) : null}\n </Stack>\n </TableCell>\n </TableRow>\n {read?.status === 'error' ? (\n <TableRow>\n <TableCell colSpan={COLUMNS}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'This site’s overlays could not be read.'}\n </Typography>\n </TableCell>\n </TableRow>\n ) : answer && !answer.rows.length ? (\n <TableRow>\n <TableCell colSpan={COLUMNS}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No overlays on this site.'}\n </Typography>\n </TableCell>\n </TableRow>\n ) : (\n (answer?.rows ?? []).map((overlay) => {\n const status = STATUS_CHIP[overlayStatus(overlay, Date.now())]\n return (\n <TableRow key={overlay.$id}>\n <TableCell>{overlayDisplayName(overlay)}</TableCell>\n <TableCell>\n {overlay.kind === 'bar' ? 'Bar' : 'Popup'}\n </TableCell>\n <TableCell>\n <Chip\n size=\"small\"\n color={status.color}\n label={status.label}\n />\n </TableCell>\n <TableCell>{overlayWindowLabel(overlay)}</TableCell>\n <TableCell>{overlayPagesLabel(overlay)}</TableCell>\n <TableCell>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {overlayEngagementLabel(overlay.stats)}\n </Typography>\n </TableCell>\n <TableCell align=\"right\">\n <Switch\n size=\"small\"\n checked={overlay.enabled !== false}\n onChange={() => void handleToggle(site.id, overlay)}\n slotProps={{\n input: {\n 'aria-label': `Show ${overlayDisplayName(overlay)} on ${name}`,\n },\n }}\n />\n {/* A site the shell could not address has no Overlays section to open. */}\n {href ? (\n <AppLink\n componentVariant=\"button\"\n href={href}\n size=\"small\"\n color=\"primary\"\n aria-label={`Edit ${overlayDisplayName(overlay)} on ${name}`}\n >\n {'Edit'}\n </AppLink>\n ) : null}\n </TableCell>\n </TableRow>\n )\n })\n )}\n </Fragment>\n )\n })}\n </TableBody>\n </ScrollTable>\n <ListPagination\n page={page}\n pageSize={ORG_SITES_PER_PAGE}\n rowCount={pageSites.length}\n count={sites.length}\n onPageChange={setPage}\n labelDisplayedRows={({ from, to, count }) =>\n `Sites ${from}–${to} of ${count}`\n }\n />\n </Stack>\n )}\n </CardDisplay>\n )\n}\nOrgOverlaysCard.displayName = 'OrgOverlaysCard'\n\nexport default OrgOverlaysCard\n"],"names":["checkEntitlement","pluginDocsHelp","compareOverlayPrecedence","overlayStatus","AppLink","CardDisplay","ListPagination","ScrollTable","useSnackbar","Alert","Chip","Stack","Switch","TableBody","TableCell","TableHead","TableRow","Typography","collection","doc","getDoc","getDocs","limit","orderBy","query","Fragment","useState","useFirestore","useUser","writeSiteWideChange","ceilingedWindow","useMarketingOrgMount","OrgSitePickerButton","orgSiteSectionHref","overlayDisplayName","overlayEngagementLabel","overlayPagesLabel","overlayWindowLabel","ORG_SITES_PER_PAGE","useOrgSiteReads","ORG_OVERLAYS_PER_SITE","readSiteOverlays","firestore","hostId","site","overlays","host","Promise","all","catch","rows","truncated","docs","map","entry","data","$id","id","exists","sort","defaults","bar","Boolean","announcementBar","enabled","text","popup","body","STATUS_CHIP","off","label","color","scheduled","live","COLUMNS","OrgOverlaysCard","props","org","mount","user","enqueueSnackbar","entitled","sites","hosts","page","setPage","pageSites","slice","reads","patch","handleToggle","overlay","write","batch","set","merge","row","error","console","variant","header","subheader","help","anchor","contentGutterX","contentGutterY","contentBordered","severity","hostsReady","length","spacing","direction","sx","alignItems","justifyContent","section","size","aria-label","align","read","get","answer","value","href","name","subdomain","colSpan","useFlexGap","flexWrap","status","Date","now","kind","stats","checked","onChange","slotProps","input","componentVariant","pageSize","rowCount","count","onPageChange","labelDisplayedRows","from","to","displayName"],"mappings":"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"}
@@ -246,6 +246,7 @@ const fromLocalInput = (value)=>value ? new Date(value).getTime() : undefined;
246
246
  fullWidth: true
247
247
  }),
248
248
  /*#__PURE__*/ _jsxs(Stack, {
249
+ useFlexGap: true,
249
250
  direction: "row",
250
251
  spacing: 1,
251
252
  sx: {
@@ -318,6 +319,7 @@ const fromLocalInput = (value)=>value ? new Date(value).getTime() : undefined;
318
319
  ]
319
320
  }),
320
321
  /*#__PURE__*/ _jsxs(Stack, {
322
+ useFlexGap: true,
321
323
  direction: "row",
322
324
  spacing: 1,
323
325
  sx: {
@@ -352,6 +354,7 @@ const fromLocalInput = (value)=>value ? new Date(value).getTime() : undefined;
352
354
  ]
353
355
  }),
354
356
  /*#__PURE__*/ _jsxs(Stack, {
357
+ useFlexGap: true,
355
358
  direction: "row",
356
359
  spacing: 1,
357
360
  sx: {
@@ -402,6 +405,7 @@ const fromLocalInput = (value)=>value ? new Date(value).getTime() : undefined;
402
405
  ]
403
406
  }),
404
407
  /*#__PURE__*/ _jsxs(Stack, {
408
+ useFlexGap: true,
405
409
  direction: "row",
406
410
  spacing: 1,
407
411
  sx: {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/popup-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n type AglynOrgBilling,\n checkEntitlement,\n type HostPopup,\n inheritedMediaAlt,\n MEDIA_ALT_MAX_LENGTH,\n pluginDocsHelp,\n useMediaPicker,\n} from '@aglyn/aglyn'\nimport { CardDisplay, useLoading } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n Button,\n MenuItem,\n Stack,\n Switch,\n TextField,\n Typography,\n} from '@mui/material'\nimport { doc } from 'firebase/firestore'\nimport { useEffect, useState } from 'react'\nimport {\n useFirestore,\n useFirestoreDoc,\n useHostActivityLogger,\n useUser,\n writeGuardedBySeed,\n} from '@aglyn/tenant-feature-instance'\nimport { writeSiteWideChange } from '@aglyn/tenant-feature-instance/hooks/helpers/site-wide-change'\nimport OverlayStatsRow from './overlay-stats-row.component'\nimport {\n OVERLAY_COPY_HELPER_TEXT,\n shownAsTypedHelperText,\n useOverlayCopyEditor,\n} from './use-overlay-copy-editor'\n\nexport interface PopupCardProps {\n hostId: string\n /** Resolved entitlement source (AGL-395). */\n org?: Partial<AglynOrgBilling>\n}\n\nconst TRIGGERS: Array<{ value: NonNullable<HostPopup['trigger']>; label: string }> = [\n { value: 'delay', label: 'After a delay (seconds)' },\n { value: 'scroll', label: 'At scroll depth (%)' },\n { value: 'exit', label: 'On exit intent (desktop)' },\n]\n\nconst toLocalInput = (ms?: number) => {\n if (!ms) return ''\n const date = new Date(ms - new Date().getTimezoneOffset() * 60000)\n return date.toISOString().slice(0, 16)\n}\nconst fromLocalInput = (value: string) =>\n value ? new Date(value).getTime() : undefined\n\n/**\n * Promotional popup editor (AGL-196): one popup per host, persisted on the\n * host doc; the tenant render handles triggers, scheduling, and\n * frequency capping. marketingOverlays-gated (Starter+). The headline and\n * body are edited through `useOverlayCopyEditor`, like the bar's text\n * (AGL-2885).\n */\nexport function PopupCard(props: PopupCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { org } = props\n const { enqueueSnackbar } = useSnackbar()\n const { queueLoading } = useLoading()\n const logActivity = useHostActivityLogger(hostId)\n const {\n data: host,\n status: hostStatus,\n /**\n * The host doc this form is seeded from is unconfirmed by the server\n * (AGL-1358). The save writes the whole `popup` map — a nested map value\n * REPLACES atomically, so `updateDoc` protects only the host doc's other\n * fields, never the popup's own — and every field of it comes off this\n * seed. Against a cached read, retyping the headline restores yesterday's\n * body, link, schedule and enabled flag on a live site.\n */\n fromCache: hostFromCache,\n } = useFirestoreDoc<any>(\n () => doc(firestore, 'hosts', hostId),\n [firestore, hostId],\n { idField: '$id' },\n )\n const entitled = checkEntitlement(org, 'marketingOverlays')\n\n const saved = (host?.popup ?? {}) as HostPopup\n const [draft, setDraft] = useState<HostPopup>(saved)\n const savedKey = JSON.stringify(saved)\n useEffect(() => {\n setDraft(JSON.parse(savedKey))\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [savedKey])\n\n // The console media browser is provided by the shell (AGL-395).\n const { pickMedia } = useMediaPicker()\n // The headline and body are held in their stored form; the fields show them\n // as typed.\n const copy = useOverlayCopyEditor({\n hostId,\n host,\n usesTokens:\n entitled &&\n [saved.headline, saved.body, draft.headline, draft.body].some((text) =>\n text?.includes('{{'),\n ),\n })\n // Compared as it would be stored, so a name typed before the variables\n // arrived still counts as the change it becomes on save.\n const dirty =\n JSON.stringify({\n ...draft,\n headline: copy.stored(draft.headline),\n body: copy.stored(draft.body),\n }) !== savedKey\n const patch = (partial: Partial<HostPopup>) =>\n setDraft((previous) => ({ ...previous, ...partial }))\n const headlineShownAsTyped = copy.shownAsTyped(draft.headline)\n const bodyShownAsTyped = copy.shownAsTyped(draft.body)\n\n const handleSave = async () => {\n const dequeue = queueLoading()\n try {\n /**\n * Refuse a save whose seed the server never confirmed (AGL-1358).\n *\n * There is no create path to exempt here, and that is the point: unlike\n * a new row at a fresh uid, this writes a FIXED document path, so a\n * blank `draft` — what `host?.popup ?? {}` yields when the cache has\n * never seen a popup — is not harmless. It replaces whatever popup the\n * server really holds with an empty one. Both the populated and the\n * empty seed are guarded for the same reason.\n *\n * The activity log rides inside the guard: a logged \"Updated popup\"\n * whose write never happened leaves the history disagreeing with the\n * popup it claims to explain.\n *\n * The guard WRAPS the write — an early return is a shape you can keep\n * while losing the protection.\n */\n const verdict = await writeGuardedBySeed(\n {\n subject: 'popup',\n unreadable: hostStatus === 'error',\n fromCache: hostFromCache,\n },\n async () => {\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.update(doc(firestore, 'hosts', hostId), {\n popup: {\n enabled: Boolean(draft.enabled),\n headline: copy.storedWithin(draft.headline, 120),\n body: copy.storedWithin(draft.body, 1000),\n imageUrl: (draft.imageUrl ?? '').trim(),\n // AGL-1896. Capped at the same length the media library saves\n // alt through, so a value defaulted from an asset and a value\n // typed here cannot diverge.\n imageAlt: (draft.imageAlt ?? '')\n .trim()\n .slice(0, MEDIA_ALT_MAX_LENGTH),\n ctaLabel: (draft.ctaLabel ?? '').slice(0, 60),\n ctaHref: (draft.ctaHref ?? '').trim(),\n collectEmail: Boolean(draft.collectEmail),\n trigger: draft.trigger ?? 'delay',\n triggerValue: Math.max(0, Number(draft.triggerValue ?? 3)),\n frequencyDays: Math.max(1, Number(draft.frequencyDays ?? 7)),\n startAtMs: draft.startAtMs ?? null,\n endAtMs: draft.endAtMs ?? null,\n },\n }),\n })\n logActivity('Updated popup', { type: 'host', id: hostId })\n },\n )\n // A refusal leaves every typed value on screen. A save that silently\n // does nothing sends the user back to retype a form that will be\n // refused again just as quietly.\n if (!verdict.ok) {\n return void enqueueSnackbar(verdict.message, {\n variant: 'warning',\n persist: false,\n })\n }\n enqueueSnackbar('Popup saved', { variant: 'success', persist: false })\n } catch (error) {\n console.error(error)\n enqueueSnackbar('An error has occurred', {\n variant: 'error',\n allowDuplicate: true,\n })\n } finally {\n dequeue()\n }\n }\n\n return (\n <CardDisplay\n header=\"Promotional popup\"\n help={pluginDocsHelp('marketingOverlays', {\n anchor: '#promotional-popups',\n excerpt:\n 'The site-wide default popup. Its only frequency cap is a number ' +\n 'of days — the once-per-session choice is on the overlays card.',\n })}\n contentGutterX\n contentGutterY\n >\n {!entitled ? (\n <Alert severity=\"info\">\n {'Popups are included from the Starter plan — see Billing to ' +\n 'upgrade.'}\n </Alert>\n ) : (\n <Stack spacing={2}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Switch\n checked={Boolean(draft.enabled)}\n onChange={(event) => patch({ enabled: event.target.checked })}\n />\n <Typography variant=\"body2\">\n {draft.enabled ? 'Active on your published site' : 'Off'}\n </Typography>\n </Stack>\n <TextField\n label=\"Headline\"\n size=\"small\"\n value={copy.editable(draft.headline)}\n onChange={(event) =>\n patch({ headline: copy.stored(event.target.value) })\n }\n error={headlineShownAsTyped.length > 0}\n helperText={shownAsTypedHelperText(headlineShownAsTyped)}\n fullWidth\n />\n <TextField\n label=\"Body\"\n size=\"small\"\n value={copy.editable(draft.body)}\n onChange={(event) =>\n patch({ body: copy.stored(event.target.value) })\n }\n error={bodyShownAsTyped.length > 0}\n helperText={\n shownAsTypedHelperText(bodyShownAsTyped) ?? OVERLAY_COPY_HELPER_TEXT\n }\n multiline\n minRows={2}\n fullWidth\n />\n <Stack direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', rowGap: 1 }}>\n <TextField\n label=\"Image URL (optional)\"\n size=\"small\"\n placeholder=\"Pick from the media library\"\n value={draft.imageUrl ?? ''}\n onChange={(event) => patch({ imageUrl: event.target.value })}\n sx={{ flex: 1, minWidth: 220 }}\n />\n <Button\n size=\"small\"\n onClick={() =>\n void (async () => {\n const media = await pickMedia?.()\n if (!media) return\n // Alt defaults from the asset (AGL-1896), never clobbering\n // one the author already typed for THIS popup. Undefined\n // when the asset has no alt or the field is already\n // filled, and the spread then leaves `imageAlt` alone.\n const inheritedAlt = inheritedMediaAlt({\n placementAlt: draft.imageAlt,\n assetAlt: media.alt,\n })\n patch({\n imageUrl: media.url,\n ...(inheritedAlt ? { imageAlt: inheritedAlt } : {}),\n })\n })()\n }\n >\n {'Browse media'}\n </Button>\n </Stack>\n {/* AGL-1896: the popup image rendered `alt=\"\"` with no field\n behind it. Offered whenever there is an image to describe. */}\n {(draft.imageUrl ?? '').trim() ? (\n <TextField\n label=\"Image alt text\"\n size=\"small\"\n value={draft.imageAlt ?? ''}\n onChange={(event) => patch({ imageAlt: event.target.value })}\n helperText={\n 'Describes the image for screen readers. Filled in from the ' +\n 'media library when you pick an image that has alt text. ' +\n 'Leave empty only if the image is purely decorative.'\n }\n />\n ) : null}\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Switch\n checked={Boolean(draft.collectEmail)}\n onChange={(event) =>\n patch({ collectEmail: event.target.checked })\n }\n />\n <Typography variant=\"body2\">\n {'Collect emails (submissions land in your Inbox and Contacts)'}\n </Typography>\n </Stack>\n <Stack direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', rowGap: 1 }}>\n <TextField\n label=\"Button label\"\n size=\"small\"\n value={draft.ctaLabel ?? ''}\n onChange={(event) => patch({ ctaLabel: event.target.value })}\n sx={{ width: 160 }}\n />\n <TextField\n label=\"Button link\"\n size=\"small\"\n placeholder=\"/sale or https://…\"\n value={draft.ctaHref ?? ''}\n onChange={(event) => patch({ ctaHref: event.target.value })}\n sx={{ flex: 1, minWidth: 180 }}\n />\n </Stack>\n <Stack direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', rowGap: 1 }}>\n <TextField\n select\n label=\"Show\"\n size=\"small\"\n value={draft.trigger ?? 'delay'}\n onChange={(event) =>\n patch({ trigger: event.target.value as HostPopup['trigger'] })\n }\n sx={{ minWidth: 200 }}\n >\n {TRIGGERS.map((option) => (\n <MenuItem key={option.value} value={option.value}>\n {option.label}\n </MenuItem>\n ))}\n </TextField>\n {draft.trigger !== 'exit' ? (\n <TextField\n label={draft.trigger === 'scroll' ? 'Depth %' : 'Seconds'}\n size=\"small\"\n type=\"number\"\n value={draft.triggerValue ?? 3}\n onChange={(event) =>\n patch({ triggerValue: Number(event.target.value) })\n }\n sx={{ width: 110 }}\n />\n ) : null}\n <TextField\n label=\"Re-show after (days)\"\n size=\"small\"\n type=\"number\"\n value={draft.frequencyDays ?? 7}\n onChange={(event) =>\n patch({ frequencyDays: Number(event.target.value) })\n }\n sx={{ width: 160 }}\n />\n </Stack>\n <Stack direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', rowGap: 1 }}>\n <TextField\n label=\"Start (optional)\"\n size=\"small\"\n type=\"datetime-local\"\n value={toLocalInput(draft.startAtMs)}\n onChange={(event) =>\n patch({ startAtMs: fromLocalInput(event.target.value) })\n }\n slotProps={{ inputLabel: { shrink: true } }}\n sx={{ minWidth: 200 }}\n />\n <TextField\n label=\"End (optional)\"\n size=\"small\"\n type=\"datetime-local\"\n value={toLocalInput(draft.endAtMs)}\n onChange={(event) =>\n patch({ endAtMs: fromLocalInput(event.target.value) })\n }\n slotProps={{ inputLabel: { shrink: true } }}\n sx={{ minWidth: 200 }}\n />\n </Stack>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={\n !dirty ||\n (Boolean(draft.enabled) && !draft.body?.trim()) ||\n copy.pending(draft.headline, draft.body)\n }\n onClick={handleSave}\n sx={{ alignSelf: 'flex-start' }}\n >\n {'Save'}\n </Button>\n <OverlayStatsRow\n hostId={hostId}\n impressionKey=\"popupImpression\"\n actionKey=\"popupClick\"\n actionLabel=\"clicks\"\n />\n </Stack>\n )}\n </CardDisplay>\n )\n}\nPopupCard.displayName = 'PopupCard'\n\nexport default PopupCard\n"],"names":["checkEntitlement","inheritedMediaAlt","MEDIA_ALT_MAX_LENGTH","pluginDocsHelp","useMediaPicker","CardDisplay","useLoading","useSnackbar","Alert","Button","MenuItem","Stack","Switch","TextField","Typography","doc","useEffect","useState","useFirestore","useFirestoreDoc","useHostActivityLogger","useUser","writeGuardedBySeed","writeSiteWideChange","OverlayStatsRow","OVERLAY_COPY_HELPER_TEXT","shownAsTypedHelperText","useOverlayCopyEditor","TRIGGERS","value","label","toLocalInput","ms","date","Date","getTimezoneOffset","toISOString","slice","fromLocalInput","getTime","undefined","PopupCard","props","draft","hostId","firestore","data","user","org","enqueueSnackbar","queueLoading","logActivity","host","status","hostStatus","fromCache","hostFromCache","idField","entitled","saved","popup","setDraft","savedKey","JSON","stringify","parse","pickMedia","copy","usesTokens","headline","body","some","text","includes","dirty","stored","patch","partial","previous","headlineShownAsTyped","shownAsTyped","bodyShownAsTyped","handleSave","dequeue","verdict","subject","unreadable","write","batch","update","enabled","Boolean","storedWithin","imageUrl","trim","imageAlt","ctaLabel","ctaHref","collectEmail","trigger","triggerValue","Math","max","Number","frequencyDays","startAtMs","endAtMs","type","id","ok","message","variant","persist","error","console","allowDuplicate","header","help","anchor","excerpt","contentGutterX","contentGutterY","severity","spacing","direction","sx","alignItems","checked","onChange","event","target","size","editable","length","helperText","fullWidth","multiline","minRows","flexWrap","rowGap","placeholder","flex","minWidth","onClick","media","inheritedAlt","placementAlt","assetAlt","alt","url","width","select","map","option","slotProps","inputLabel","shrink","color","disabled","pending","alignSelf","impressionKey","actionKey","actionLabel","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/popup-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {\n type AglynOrgBilling,\n checkEntitlement,\n type HostPopup,\n inheritedMediaAlt,\n MEDIA_ALT_MAX_LENGTH,\n pluginDocsHelp,\n useMediaPicker,\n} from '@aglyn/aglyn'\nimport { CardDisplay, useLoading } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n Button,\n MenuItem,\n Stack,\n Switch,\n TextField,\n Typography,\n} from '@mui/material'\nimport { doc } from 'firebase/firestore'\nimport { useEffect, useState } from 'react'\nimport {\n useFirestore,\n useFirestoreDoc,\n useHostActivityLogger,\n useUser,\n writeGuardedBySeed,\n} from '@aglyn/tenant-feature-instance'\nimport { writeSiteWideChange } from '@aglyn/tenant-feature-instance/hooks/helpers/site-wide-change'\nimport OverlayStatsRow from './overlay-stats-row.component'\nimport {\n OVERLAY_COPY_HELPER_TEXT,\n shownAsTypedHelperText,\n useOverlayCopyEditor,\n} from './use-overlay-copy-editor'\n\nexport interface PopupCardProps {\n hostId: string\n /** Resolved entitlement source (AGL-395). */\n org?: Partial<AglynOrgBilling>\n}\n\nconst TRIGGERS: Array<{ value: NonNullable<HostPopup['trigger']>; label: string }> = [\n { value: 'delay', label: 'After a delay (seconds)' },\n { value: 'scroll', label: 'At scroll depth (%)' },\n { value: 'exit', label: 'On exit intent (desktop)' },\n]\n\nconst toLocalInput = (ms?: number) => {\n if (!ms) return ''\n const date = new Date(ms - new Date().getTimezoneOffset() * 60000)\n return date.toISOString().slice(0, 16)\n}\nconst fromLocalInput = (value: string) =>\n value ? new Date(value).getTime() : undefined\n\n/**\n * Promotional popup editor (AGL-196): one popup per host, persisted on the\n * host doc; the tenant render handles triggers, scheduling, and\n * frequency capping. marketingOverlays-gated (Starter+). The headline and\n * body are edited through `useOverlayCopyEditor`, like the bar's text\n * (AGL-2885).\n */\nexport function PopupCard(props: PopupCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { org } = props\n const { enqueueSnackbar } = useSnackbar()\n const { queueLoading } = useLoading()\n const logActivity = useHostActivityLogger(hostId)\n const {\n data: host,\n status: hostStatus,\n /**\n * The host doc this form is seeded from is unconfirmed by the server\n * (AGL-1358). The save writes the whole `popup` map — a nested map value\n * REPLACES atomically, so `updateDoc` protects only the host doc's other\n * fields, never the popup's own — and every field of it comes off this\n * seed. Against a cached read, retyping the headline restores yesterday's\n * body, link, schedule and enabled flag on a live site.\n */\n fromCache: hostFromCache,\n } = useFirestoreDoc<any>(\n () => doc(firestore, 'hosts', hostId),\n [firestore, hostId],\n { idField: '$id' },\n )\n const entitled = checkEntitlement(org, 'marketingOverlays')\n\n const saved = (host?.popup ?? {}) as HostPopup\n const [draft, setDraft] = useState<HostPopup>(saved)\n const savedKey = JSON.stringify(saved)\n useEffect(() => {\n setDraft(JSON.parse(savedKey))\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [savedKey])\n\n // The console media browser is provided by the shell (AGL-395).\n const { pickMedia } = useMediaPicker()\n // The headline and body are held in their stored form; the fields show them\n // as typed.\n const copy = useOverlayCopyEditor({\n hostId,\n host,\n usesTokens:\n entitled &&\n [saved.headline, saved.body, draft.headline, draft.body].some((text) =>\n text?.includes('{{'),\n ),\n })\n // Compared as it would be stored, so a name typed before the variables\n // arrived still counts as the change it becomes on save.\n const dirty =\n JSON.stringify({\n ...draft,\n headline: copy.stored(draft.headline),\n body: copy.stored(draft.body),\n }) !== savedKey\n const patch = (partial: Partial<HostPopup>) =>\n setDraft((previous) => ({ ...previous, ...partial }))\n const headlineShownAsTyped = copy.shownAsTyped(draft.headline)\n const bodyShownAsTyped = copy.shownAsTyped(draft.body)\n\n const handleSave = async () => {\n const dequeue = queueLoading()\n try {\n /**\n * Refuse a save whose seed the server never confirmed (AGL-1358).\n *\n * There is no create path to exempt here, and that is the point: unlike\n * a new row at a fresh uid, this writes a FIXED document path, so a\n * blank `draft` — what `host?.popup ?? {}` yields when the cache has\n * never seen a popup — is not harmless. It replaces whatever popup the\n * server really holds with an empty one. Both the populated and the\n * empty seed are guarded for the same reason.\n *\n * The activity log rides inside the guard: a logged \"Updated popup\"\n * whose write never happened leaves the history disagreeing with the\n * popup it claims to explain.\n *\n * The guard WRAPS the write — an early return is a shape you can keep\n * while losing the protection.\n */\n const verdict = await writeGuardedBySeed(\n {\n subject: 'popup',\n unreadable: hostStatus === 'error',\n fromCache: hostFromCache,\n },\n async () => {\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.update(doc(firestore, 'hosts', hostId), {\n popup: {\n enabled: Boolean(draft.enabled),\n headline: copy.storedWithin(draft.headline, 120),\n body: copy.storedWithin(draft.body, 1000),\n imageUrl: (draft.imageUrl ?? '').trim(),\n // AGL-1896. Capped at the same length the media library saves\n // alt through, so a value defaulted from an asset and a value\n // typed here cannot diverge.\n imageAlt: (draft.imageAlt ?? '')\n .trim()\n .slice(0, MEDIA_ALT_MAX_LENGTH),\n ctaLabel: (draft.ctaLabel ?? '').slice(0, 60),\n ctaHref: (draft.ctaHref ?? '').trim(),\n collectEmail: Boolean(draft.collectEmail),\n trigger: draft.trigger ?? 'delay',\n triggerValue: Math.max(0, Number(draft.triggerValue ?? 3)),\n frequencyDays: Math.max(1, Number(draft.frequencyDays ?? 7)),\n startAtMs: draft.startAtMs ?? null,\n endAtMs: draft.endAtMs ?? null,\n },\n }),\n })\n logActivity('Updated popup', { type: 'host', id: hostId })\n },\n )\n // A refusal leaves every typed value on screen. A save that silently\n // does nothing sends the user back to retype a form that will be\n // refused again just as quietly.\n if (!verdict.ok) {\n return void enqueueSnackbar(verdict.message, {\n variant: 'warning',\n persist: false,\n })\n }\n enqueueSnackbar('Popup saved', { variant: 'success', persist: false })\n } catch (error) {\n console.error(error)\n enqueueSnackbar('An error has occurred', {\n variant: 'error',\n allowDuplicate: true,\n })\n } finally {\n dequeue()\n }\n }\n\n return (\n <CardDisplay\n header=\"Promotional popup\"\n help={pluginDocsHelp('marketingOverlays', {\n anchor: '#promotional-popups',\n excerpt:\n 'The site-wide default popup. Its only frequency cap is a number ' +\n 'of days — the once-per-session choice is on the overlays card.',\n })}\n contentGutterX\n contentGutterY\n >\n {!entitled ? (\n <Alert severity=\"info\">\n {'Popups are included from the Starter plan — see Billing to ' +\n 'upgrade.'}\n </Alert>\n ) : (\n <Stack spacing={2}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Switch\n checked={Boolean(draft.enabled)}\n onChange={(event) => patch({ enabled: event.target.checked })}\n />\n <Typography variant=\"body2\">\n {draft.enabled ? 'Active on your published site' : 'Off'}\n </Typography>\n </Stack>\n <TextField\n label=\"Headline\"\n size=\"small\"\n value={copy.editable(draft.headline)}\n onChange={(event) =>\n patch({ headline: copy.stored(event.target.value) })\n }\n error={headlineShownAsTyped.length > 0}\n helperText={shownAsTypedHelperText(headlineShownAsTyped)}\n fullWidth\n />\n <TextField\n label=\"Body\"\n size=\"small\"\n value={copy.editable(draft.body)}\n onChange={(event) =>\n patch({ body: copy.stored(event.target.value) })\n }\n error={bodyShownAsTyped.length > 0}\n helperText={\n shownAsTypedHelperText(bodyShownAsTyped) ?? OVERLAY_COPY_HELPER_TEXT\n }\n multiline\n minRows={2}\n fullWidth\n />\n <Stack useFlexGap direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', rowGap: 1 }}>\n <TextField\n label=\"Image URL (optional)\"\n size=\"small\"\n placeholder=\"Pick from the media library\"\n value={draft.imageUrl ?? ''}\n onChange={(event) => patch({ imageUrl: event.target.value })}\n sx={{ flex: 1, minWidth: 220 }}\n />\n <Button\n size=\"small\"\n onClick={() =>\n void (async () => {\n const media = await pickMedia?.()\n if (!media) return\n // Alt defaults from the asset (AGL-1896), never clobbering\n // one the author already typed for THIS popup. Undefined\n // when the asset has no alt or the field is already\n // filled, and the spread then leaves `imageAlt` alone.\n const inheritedAlt = inheritedMediaAlt({\n placementAlt: draft.imageAlt,\n assetAlt: media.alt,\n })\n patch({\n imageUrl: media.url,\n ...(inheritedAlt ? { imageAlt: inheritedAlt } : {}),\n })\n })()\n }\n >\n {'Browse media'}\n </Button>\n </Stack>\n {/* AGL-1896: the popup image rendered `alt=\"\"` with no field\n behind it. Offered whenever there is an image to describe. */}\n {(draft.imageUrl ?? '').trim() ? (\n <TextField\n label=\"Image alt text\"\n size=\"small\"\n value={draft.imageAlt ?? ''}\n onChange={(event) => patch({ imageAlt: event.target.value })}\n helperText={\n 'Describes the image for screen readers. Filled in from the ' +\n 'media library when you pick an image that has alt text. ' +\n 'Leave empty only if the image is purely decorative.'\n }\n />\n ) : null}\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Switch\n checked={Boolean(draft.collectEmail)}\n onChange={(event) =>\n patch({ collectEmail: event.target.checked })\n }\n />\n <Typography variant=\"body2\">\n {'Collect emails (submissions land in your Inbox and Contacts)'}\n </Typography>\n </Stack>\n <Stack useFlexGap direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', rowGap: 1 }}>\n <TextField\n label=\"Button label\"\n size=\"small\"\n value={draft.ctaLabel ?? ''}\n onChange={(event) => patch({ ctaLabel: event.target.value })}\n sx={{ width: 160 }}\n />\n <TextField\n label=\"Button link\"\n size=\"small\"\n placeholder=\"/sale or https://…\"\n value={draft.ctaHref ?? ''}\n onChange={(event) => patch({ ctaHref: event.target.value })}\n sx={{ flex: 1, minWidth: 180 }}\n />\n </Stack>\n <Stack useFlexGap direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', rowGap: 1 }}>\n <TextField\n select\n label=\"Show\"\n size=\"small\"\n value={draft.trigger ?? 'delay'}\n onChange={(event) =>\n patch({ trigger: event.target.value as HostPopup['trigger'] })\n }\n sx={{ minWidth: 200 }}\n >\n {TRIGGERS.map((option) => (\n <MenuItem key={option.value} value={option.value}>\n {option.label}\n </MenuItem>\n ))}\n </TextField>\n {draft.trigger !== 'exit' ? (\n <TextField\n label={draft.trigger === 'scroll' ? 'Depth %' : 'Seconds'}\n size=\"small\"\n type=\"number\"\n value={draft.triggerValue ?? 3}\n onChange={(event) =>\n patch({ triggerValue: Number(event.target.value) })\n }\n sx={{ width: 110 }}\n />\n ) : null}\n <TextField\n label=\"Re-show after (days)\"\n size=\"small\"\n type=\"number\"\n value={draft.frequencyDays ?? 7}\n onChange={(event) =>\n patch({ frequencyDays: Number(event.target.value) })\n }\n sx={{ width: 160 }}\n />\n </Stack>\n <Stack useFlexGap direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', rowGap: 1 }}>\n <TextField\n label=\"Start (optional)\"\n size=\"small\"\n type=\"datetime-local\"\n value={toLocalInput(draft.startAtMs)}\n onChange={(event) =>\n patch({ startAtMs: fromLocalInput(event.target.value) })\n }\n slotProps={{ inputLabel: { shrink: true } }}\n sx={{ minWidth: 200 }}\n />\n <TextField\n label=\"End (optional)\"\n size=\"small\"\n type=\"datetime-local\"\n value={toLocalInput(draft.endAtMs)}\n onChange={(event) =>\n patch({ endAtMs: fromLocalInput(event.target.value) })\n }\n slotProps={{ inputLabel: { shrink: true } }}\n sx={{ minWidth: 200 }}\n />\n </Stack>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={\n !dirty ||\n (Boolean(draft.enabled) && !draft.body?.trim()) ||\n copy.pending(draft.headline, draft.body)\n }\n onClick={handleSave}\n sx={{ alignSelf: 'flex-start' }}\n >\n {'Save'}\n </Button>\n <OverlayStatsRow\n hostId={hostId}\n impressionKey=\"popupImpression\"\n actionKey=\"popupClick\"\n actionLabel=\"clicks\"\n />\n </Stack>\n )}\n </CardDisplay>\n )\n}\nPopupCard.displayName = 'PopupCard'\n\nexport default PopupCard\n"],"names":["checkEntitlement","inheritedMediaAlt","MEDIA_ALT_MAX_LENGTH","pluginDocsHelp","useMediaPicker","CardDisplay","useLoading","useSnackbar","Alert","Button","MenuItem","Stack","Switch","TextField","Typography","doc","useEffect","useState","useFirestore","useFirestoreDoc","useHostActivityLogger","useUser","writeGuardedBySeed","writeSiteWideChange","OverlayStatsRow","OVERLAY_COPY_HELPER_TEXT","shownAsTypedHelperText","useOverlayCopyEditor","TRIGGERS","value","label","toLocalInput","ms","date","Date","getTimezoneOffset","toISOString","slice","fromLocalInput","getTime","undefined","PopupCard","props","draft","hostId","firestore","data","user","org","enqueueSnackbar","queueLoading","logActivity","host","status","hostStatus","fromCache","hostFromCache","idField","entitled","saved","popup","setDraft","savedKey","JSON","stringify","parse","pickMedia","copy","usesTokens","headline","body","some","text","includes","dirty","stored","patch","partial","previous","headlineShownAsTyped","shownAsTyped","bodyShownAsTyped","handleSave","dequeue","verdict","subject","unreadable","write","batch","update","enabled","Boolean","storedWithin","imageUrl","trim","imageAlt","ctaLabel","ctaHref","collectEmail","trigger","triggerValue","Math","max","Number","frequencyDays","startAtMs","endAtMs","type","id","ok","message","variant","persist","error","console","allowDuplicate","header","help","anchor","excerpt","contentGutterX","contentGutterY","severity","spacing","direction","sx","alignItems","checked","onChange","event","target","size","editable","length","helperText","fullWidth","multiline","minRows","useFlexGap","flexWrap","rowGap","placeholder","flex","minWidth","onClick","media","inheritedAlt","placementAlt","assetAlt","alt","url","width","select","map","option","slotProps","inputLabel","shrink","color","disabled","pending","alignSelf","impressionKey","actionKey","actionLabel","displayName"],"mappings":"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"}
@@ -342,6 +342,9 @@ const chips = (items)=>/*#__PURE__*/ _jsx(Stack, {
342
342
  /*#__PURE__*/ _jsxs(Tabs, {
343
343
  value: tab,
344
344
  onChange: (_event, next)=>setTab(next),
345
+ variant: "scrollable",
346
+ scrollButtons: "auto",
347
+ allowScrollButtonsMobile: true,
345
348
  children: [
346
349
  /*#__PURE__*/ _jsx(Tab, {
347
350
  value: "sends",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/staff-org-email-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 { buildRoute, Route } from '@aglyn/aglyn/app-utils/console-routes'\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { mdiEyeOutline } from '@aglyn/shared-data-mdi'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport {\n ListRowActions,\n ListTable,\n listActionsColumn,\n} from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport { TABLE_ROW_HEIGHT } from '@aglyn/shared-ui-jsx/const/table-pagination'\nimport { useFirestore, usePagedCollection } from '@aglyn/tenant-feature-instance'\nimport {\n Box,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Stack,\n Tab,\n Tabs,\n Typography,\n} from '@mui/material'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport { collection, documentId, limit, orderBy, query } from 'firebase/firestore'\nimport { type ReactNode, useMemo, useState } from 'react'\n\n/*\n * AN ORGANIZATION'S EMAIL WORK, AS STAFF SEE IT (AGL-3380).\n *\n * The staff organization page is about a customer the staffer is not a\n * member of, so nothing here sends, schedules or edits. Each tab is a paged\n * walk of one collection in document-id order. A send opens the message it\n * stored, drawn in a frame that runs nothing, or the design it was composed\n * from in the staff preview.\n */\n\ntype Row = { $id: string } & Record<string, any>\n\nconst whenOf = (value: any): string => {\n if (typeof value?.toDate === 'function') return value.toDate().toLocaleString()\n if (typeof value?.seconds === 'number') return new Date(value.seconds * 1000).toLocaleString()\n if (typeof value === 'number') return new Date(value).toLocaleString()\n return '—'\n}\n\n/** When a send went, or is due: the sent stamp, else the schedule, else its creation. */\nconst sendWhen = (row: Row): string =>\n row['sentAt']\n ? whenOf(row['sentAt'])\n : row['sendAtMs']\n ? `due ${whenOf(row['sendAtMs'])}`\n : whenOf(row['createdAtMs'])\n\n/** The staff preview of the design a send was composed from, if it names one. */\nconst designPreviewHref = (row: Row): string | undefined =>\n typeof row['templateScreenId'] === 'string' &&\n row['templateScreenId'] &&\n typeof row['hostId'] === 'string' &&\n row['hostId']\n ? buildRoute(Route.ADMIN_SITE_PREVIEW, {\n hostId: row['hostId'],\n kind: 'screen',\n docId: row['templateScreenId'],\n })\n : undefined\n\n/**\n * A stored message body, in a frame with `sandbox` and no permissions: a\n * customer's HTML is only ever pixels on the staff page.\n */\nfunction MessageDialog(props: { row: Row | null; onClose: () => void }) {\n const { row, onClose } = props\n return (\n <Dialog open={Boolean(row)} onClose={onClose} maxWidth=\"md\" fullWidth>\n <DialogTitle>{row ? String(row['subject'] || row.$id) : ''}</DialogTitle>\n <DialogContent>\n {row?.['preheader'] ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {String(row['preheader'])}\n </Typography>\n ) : null}\n <Box\n component=\"iframe\"\n title=\"Message\"\n sandbox=\"\"\n srcDoc={String(row?.['body'] ?? '')}\n sx={{ mt: 1, width: '100%', height: 560, border: 1, borderColor: 'divider', bgcolor: '#fff' }}\n />\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose}>{'Close'}</Button>\n </DialogActions>\n </Dialog>\n )\n}\n\n/** One paged table of an organization subcollection. */\nfunction OrgCollectionTable(props: {\n orgId: string\n collectionId: string\n columns: GridColDef[]\n emptyLabel: string\n onOpen?: (row: Row) => void\n}) {\n const { orgId, collectionId, columns, emptyLabel, onOpen } = props\n const firestore = useFirestore()\n const paged = usePagedCollection<Row>(\n (pageLimit) =>\n orgId\n ? query(\n collection(firestore, 'orgs', orgId, collectionId),\n orderBy(documentId()),\n limit(pageLimit),\n )\n : null,\n [firestore, orgId, collectionId],\n { idField: '$id' },\n )\n const byId = useMemo(() => new Map(paged.rows.map((row) => [row.$id, row])), [paged.rows])\n return (\n <Stack spacing={1}>\n <ListTable\n rows={paged.rows}\n columns={columns}\n loading={paged.status === 'loading'}\n disableColumnSorting\n noRowsLabel={emptyLabel}\n onOpen={\n onOpen\n ? (id) => {\n const row = byId.get(String(id))\n if (row) onOpen(row)\n }\n : undefined\n }\n hideFooter\n rowHeight={TABLE_ROW_HEIGHT}\n />\n <ListPagination\n page={paged.page}\n pageSize={paged.pageSize}\n rowCount={paged.rows.length}\n hasMore={paged.hasMore}\n onPageChange={paged.setPage}\n onPageSizeChange={paged.setPageSize}\n />\n </Stack>\n )\n}\n\nconst chips = (items: Array<{ label: string; color?: 'success' | 'warning' | 'error' }>): ReactNode => (\n <Stack direction=\"row\" spacing={0.5} useFlexGap sx={{ alignItems: 'center', height: '100%', flexWrap: 'wrap' }}>\n {items.map((item) => (\n <Chip\n key={item.label}\n size=\"small\"\n label={item.label}\n color={item.color ?? 'default'}\n variant={item.color ? 'filled' : 'outlined'}\n />\n ))}\n </Stack>\n)\n\ntype EmailTab = 'sends' | 'campaigns'\n\n/**\n * The staff organization page's email card (`adminOrgDetail` zone): every\n * campaign send with its state and numbers, and the campaign containers\n * with the sites each runs on.\n */\nexport function StaffOrgEmailCard(props: { orgId: string }) {\n const { orgId } = props\n const [tab, setTab] = useState<EmailTab>('sends')\n const [message, setMessage] = useState<Row | null>(null)\n\n const sendColumns: GridColDef[] = useMemo(\n () => [\n {\n field: 'subject',\n headerName: 'Subject',\n flex: 1.4,\n minWidth: 200,\n valueGetter: (_value, row: Row) => String(row['subject'] || '(no subject)'),\n renderCell: ({ row }: { row: Row }) => (\n <Stack sx={{ justifyContent: 'center', height: '100%', lineHeight: 1.25 }}>\n <Typography variant=\"body2\" noWrap sx={{ lineHeight: 1.25 }}>\n {String(row['subject'] || '(no subject)')}\n </Typography>\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n noWrap\n sx={{ fontFamily: 'monospace', lineHeight: 1.25 }}\n >\n {String(row['hostId'] ?? 'no site')}\n </Typography>\n </Stack>\n ),\n },\n {\n field: 'status',\n headerName: 'Status',\n flex: 1.4,\n minWidth: 220,\n valueGetter: (_value, row: Row) => String(row['status'] ?? 'draft'),\n renderCell: ({ row }: { row: Row }) => {\n const stats = row['stats'] ?? {}\n return chips([\n {\n label: String(row['status'] ?? 'draft'),\n color: row['status'] === 'sent' ? 'success' : row['status'] === 'failed' ? 'error' : undefined,\n },\n ...(row['staffReview']?.state === 'held'\n ? [{ label: 'held for review', color: 'warning' as const }]\n : []),\n ...(typeof stats.sent === 'number' ? [{ label: `${stats.sent} sent` }] : []),\n ...(typeof stats.uniqueOpens === 'number' ? [{ label: `${stats.uniqueOpens} opened` }] : []),\n ...(typeof stats.uniqueClicks === 'number' ? [{ label: `${stats.uniqueClicks} clicked` }] : []),\n ...(stats.bounced ? [{ label: `${stats.bounced} bounced`, color: 'error' as const }] : []),\n ])\n },\n },\n {\n field: 'audience',\n headerName: 'Audience',\n flex: 0.6,\n minWidth: 110,\n valueGetter: (_value, row: Row) => String(row['audience'] ?? '—'),\n },\n {\n field: 'when',\n headerName: 'When',\n flex: 0.9,\n minWidth: 160,\n valueGetter: (_value, row: Row) => sendWhen(row),\n },\n listActionsColumn(\n (row: Row) => {\n const design = designPreviewHref(row)\n return (\n <ListRowActions\n label={String(row['subject'] || row.$id)}\n quick={{\n icon: mdiEyeOutline.path,\n label: 'View message',\n onClick: () => setMessage(row),\n unavailableReason: row['body'] ? undefined : 'No message body was stored',\n }}\n items={[\n {\n key: 'design',\n label: 'Open design preview',\n href: design,\n external: true,\n disabled: !design,\n disabledReason: design ? undefined : 'This send was not composed from a design',\n },\n {\n key: 'site',\n label: 'Open site',\n href: row['hostId']\n ? buildRoute(Route.ADMIN_SITE_DETAIL, { hostId: String(row['hostId']) })\n : undefined,\n disabled: !row['hostId'],\n disabledReason: row['hostId'] ? undefined : 'This send names no site',\n },\n ]}\n />\n )\n },\n { width: 110 },\n ),\n ],\n [],\n )\n\n const campaignColumns: GridColDef[] = useMemo(\n () => [\n {\n field: 'name',\n headerName: 'Campaign',\n flex: 1.4,\n minWidth: 200,\n valueGetter: (_value, row: Row) => String(row['name'] || row.$id),\n },\n {\n field: 'visibleTo',\n headerName: 'Runs on',\n flex: 1.2,\n minWidth: 180,\n valueGetter: (_value, row: Row) =>\n (Array.isArray(row['visibleTo']) ? row['visibleTo'] : []).join(', '),\n renderCell: ({ row }: { row: Row }) =>\n chips([\n ...(Array.isArray(row['visibleTo']) ? (row['visibleTo'] as string[]) : []).map((token) => ({\n label: token === 'org' ? 'every site' : token.replace(/^host:/, ''),\n })),\n ...(row['deletedAt'] ? [{ label: 'deleted', color: 'error' as const }] : []),\n ]),\n },\n {\n field: 'window',\n headerName: 'Window',\n flex: 1,\n minWidth: 180,\n valueGetter: (_value, row: Row) =>\n row['startAtMs'] || row['endAtMs']\n ? `${whenOf(row['startAtMs'])} – ${row['endAtMs'] ? whenOf(row['endAtMs']) : 'open'}`\n : '—',\n },\n ],\n [],\n )\n\n return (\n <>\n <CardDisplay\n header={'Email campaigns'}\n help={pluginDocsHelp('staffConsole', {\n anchor: '#staff-org-email',\n excerpt:\n \"The organization's campaign sends and campaigns. A send opens its stored message or its design preview.\",\n })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n <Tabs value={tab} onChange={(_event, next: EmailTab) => setTab(next)}>\n <Tab value=\"sends\" label=\"Sends\" />\n <Tab value=\"campaigns\" label=\"Campaigns\" />\n </Tabs>\n {tab === 'sends' ? (\n <OrgCollectionTable\n key=\"sends\"\n orgId={orgId}\n collectionId=\"campaigns\"\n columns={sendColumns}\n emptyLabel=\"This organization has sent no campaigns\"\n onOpen={(row) => (row['body'] ? setMessage(row) : undefined)}\n />\n ) : (\n <OrgCollectionTable\n key=\"campaigns\"\n orgId={orgId}\n collectionId=\"emailCampaigns\"\n columns={campaignColumns}\n emptyLabel=\"This organization has no email campaigns\"\n />\n )}\n </Stack>\n </CardDisplay>\n <MessageDialog row={message} onClose={() => setMessage(null)} />\n </>\n )\n}\n"],"names":["buildRoute","Route","pluginDocsHelp","mdiEyeOutline","CardDisplay","ListPagination","ListRowActions","ListTable","listActionsColumn","TABLE_ROW_HEIGHT","useFirestore","usePagedCollection","Box","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","Stack","Tab","Tabs","Typography","collection","documentId","limit","orderBy","query","useMemo","useState","whenOf","value","toDate","toLocaleString","seconds","Date","sendWhen","row","designPreviewHref","ADMIN_SITE_PREVIEW","hostId","kind","docId","undefined","MessageDialog","props","onClose","open","Boolean","maxWidth","fullWidth","String","$id","variant","color","component","title","sandbox","srcDoc","sx","mt","width","height","border","borderColor","bgcolor","onClick","OrgCollectionTable","orgId","collectionId","columns","emptyLabel","onOpen","firestore","paged","pageLimit","idField","byId","Map","rows","map","spacing","loading","status","disableColumnSorting","noRowsLabel","id","get","hideFooter","rowHeight","page","pageSize","rowCount","length","hasMore","onPageChange","setPage","onPageSizeChange","setPageSize","chips","items","direction","useFlexGap","alignItems","flexWrap","item","size","label","StaffOrgEmailCard","tab","setTab","message","setMessage","sendColumns","field","headerName","flex","minWidth","valueGetter","_value","renderCell","justifyContent","lineHeight","noWrap","fontFamily","stats","state","sent","uniqueOpens","uniqueClicks","bounced","design","quick","icon","path","unavailableReason","key","href","external","disabled","disabledReason","ADMIN_SITE_DETAIL","campaignColumns","Array","isArray","join","token","replace","header","help","anchor","excerpt","contentGutterX","contentGutterY","onChange","_event","next"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SAASA,UAAU,EAAEC,KAAK,QAAQ,wCAAuC;AACzE,SAASC,cAAc,QAAQ,mCAAkC;AACjE,SAASC,aAAa,QAAQ,yBAAwB;AACtD,SAASC,WAAW,QAAQ,uBAAsB;AAClD,SAASC,cAAc,QAAQ,4DAA2D;AAC1F,SACEC,cAAc,EACdC,SAAS,EACTC,iBAAiB,QACZ,uDAAsD;AAC7D,SAASC,gBAAgB,QAAQ,8CAA6C;AAC9E,SAASC,YAAY,EAAEC,kBAAkB,QAAQ,iCAAgC;AACjF,SACEC,GAAG,EACHC,MAAM,EACNC,IAAI,EACJC,MAAM,EACNC,aAAa,EACbC,aAAa,EACbC,WAAW,EACXC,KAAK,EACLC,GAAG,EACHC,IAAI,EACJC,UAAU,QACL,gBAAe;AAEtB,SAASC,UAAU,EAAEC,UAAU,EAAEC,KAAK,EAAEC,OAAO,EAAEC,KAAK,QAAQ,qBAAoB;AAClF,SAAyBC,OAAO,EAAEC,QAAQ,QAAQ,QAAO;AAczD,MAAMC,SAAS,CAACC;IACd,IAAI,QAAOA,yBAAAA,MAAOC,MAAM,MAAK,YAAY,OAAOD,MAAMC,MAAM,GAAGC,cAAc;IAC7E,IAAI,QAAOF,yBAAAA,MAAOG,OAAO,MAAK,UAAU,OAAO,IAAIC,KAAKJ,MAAMG,OAAO,GAAG,MAAMD,cAAc;IAC5F,IAAI,OAAOF,UAAU,UAAU,OAAO,IAAII,KAAKJ,OAAOE,cAAc;IACpE,OAAO;AACT;AAEA,uFAAuF,GACvF,MAAMG,WAAW,CAACC,MAChBA,GAAG,CAAC,SAAS,GACTP,OAAOO,GAAG,CAAC,SAAS,IACpBA,GAAG,CAAC,WAAW,GACb,CAAC,IAAI,EAAEP,OAAOO,GAAG,CAAC,WAAW,GAAG,GAChCP,OAAOO,GAAG,CAAC,cAAc;AAEjC,+EAA+E,GAC/E,MAAMC,oBAAoB,CAACD,MACzB,OAAOA,GAAG,CAAC,mBAAmB,KAAK,YACnCA,GAAG,CAAC,mBAAmB,IACvB,OAAOA,GAAG,CAAC,SAAS,KAAK,YACzBA,GAAG,CAAC,SAAS,GACTrC,WAAWC,MAAMsC,kBAAkB,EAAE;QACnCC,QAAQH,GAAG,CAAC,SAAS;QACrBI,MAAM;QACNC,OAAOL,GAAG,CAAC,mBAAmB;IAChC,KACAM;AAEN;;;CAGC,GACD,SAASC,cAAcC,KAA+C;;IACpE,MAAM,EAAER,GAAG,EAAES,OAAO,EAAE,GAAGD;IACzB,qBACE,MAAC9B;QAAOgC,MAAMC,QAAQX;QAAMS,SAASA;QAASG,UAAS;QAAKC,SAAS;;0BACnE,KAAChC;0BAAamB,MAAMc,OAAOd,GAAG,CAAC,UAAU,IAAIA,IAAIe,GAAG,IAAI;;0BACxD,MAACnC;;oBACEoB,CAAAA,uBAAAA,GAAK,CAAC,YAAY,kBACjB,KAACf;wBAAW+B,SAAQ;wBAAUC,OAAM;kCACjCH,OAAOd,GAAG,CAAC,YAAY;yBAExB;kCACJ,KAACzB;wBACC2C,WAAU;wBACVC,OAAM;wBACNC,SAAQ;wBACRC,QAAQP,eAAOd,uBAAAA,GAAK,CAAC,OAAO,mBAAI;wBAChCsB,IAAI;4BAAEC,IAAI;4BAAGC,OAAO;4BAAQC,QAAQ;4BAAKC,QAAQ;4BAAGC,aAAa;4BAAWC,SAAS;wBAAO;;;;0BAGhG,KAACjD;0BACC,cAAA,KAACH;oBAAOqD,SAASpB;8BAAU;;;;;AAInC;AAEA,sDAAsD,GACtD,SAASqB,mBAAmBtB,KAM3B;IACC,MAAM,EAAEuB,KAAK,EAAEC,YAAY,EAAEC,OAAO,EAAEC,UAAU,EAAEC,MAAM,EAAE,GAAG3B;IAC7D,MAAM4B,YAAY/D;IAClB,MAAMgE,QAAQ/D,mBACZ,CAACgE,YACCP,QACIzC,MACEJ,WAAWkD,WAAW,QAAQL,OAAOC,eACrC3C,QAAQF,eACRC,MAAMkD,cAER,MACN;QAACF;QAAWL;QAAOC;KAAa,EAChC;QAAEO,SAAS;IAAM;IAEnB,MAAMC,OAAOjD,QAAQ,IAAM,IAAIkD,IAAIJ,MAAMK,IAAI,CAACC,GAAG,CAAC,CAAC3C,MAAQ;gBAACA,IAAIe,GAAG;gBAAEf;aAAI,IAAI;QAACqC,MAAMK,IAAI;KAAC;IACzF,qBACE,MAAC5D;QAAM8D,SAAS;;0BACd,KAAC1E;gBACCwE,MAAML,MAAMK,IAAI;gBAChBT,SAASA;gBACTY,SAASR,MAAMS,MAAM,KAAK;gBAC1BC,oBAAoB;gBACpBC,aAAad;gBACbC,QACEA,SACI,CAACc;oBACC,MAAMjD,MAAMwC,KAAKU,GAAG,CAACpC,OAAOmC;oBAC5B,IAAIjD,KAAKmC,OAAOnC;gBAClB,IACAM;gBAEN6C,UAAU;gBACVC,WAAWhF;;0BAEb,KAACJ;gBACCqF,MAAMhB,MAAMgB,IAAI;gBAChBC,UAAUjB,MAAMiB,QAAQ;gBACxBC,UAAUlB,MAAMK,IAAI,CAACc,MAAM;gBAC3BC,SAASpB,MAAMoB,OAAO;gBACtBC,cAAcrB,MAAMsB,OAAO;gBAC3BC,kBAAkBvB,MAAMwB,WAAW;;;;AAI3C;AAEA,MAAMC,QAAQ,CAACC,sBACb,KAACjF;QAAMkF,WAAU;QAAMpB,SAAS;QAAKqB,UAAU;QAAC3C,IAAI;YAAE4C,YAAY;YAAUzC,QAAQ;YAAQ0C,UAAU;QAAO;kBAC1GJ,MAAMpB,GAAG,CAAC,CAACyB;gBAKDA;iCAJT,KAAC3F;gBAEC4F,MAAK;gBACLC,OAAOF,KAAKE,KAAK;gBACjBrD,KAAK,GAAEmD,cAAAA,KAAKnD,KAAK,YAAVmD,cAAc;gBACrBpD,SAASoD,KAAKnD,KAAK,GAAG,WAAW;eAJ5BmD,KAAKE,KAAK;;;AAYvB;;;;CAIC,GACD,OAAO,SAASC,kBAAkB/D,KAAwB;IACxD,MAAM,EAAEuB,KAAK,EAAE,GAAGvB;IAClB,MAAM,CAACgE,KAAKC,OAAO,GAAGjF,SAAmB;IACzC,MAAM,CAACkF,SAASC,WAAW,GAAGnF,SAAqB;IAEnD,MAAMoF,cAA4BrF,QAChC,IAAM;YACJ;gBACEsF,OAAO;gBACPC,YAAY;gBACZC,MAAM;gBACNC,UAAU;gBACVC,aAAa,CAACC,QAAQlF,MAAac,OAAOd,GAAG,CAAC,UAAU,IAAI;gBAC5DmF,YAAY,CAAC,EAAEnF,GAAG,EAAgB;wBAWpBA;yCAVZ,MAAClB;wBAAMwC,IAAI;4BAAE8D,gBAAgB;4BAAU3D,QAAQ;4BAAQ4D,YAAY;wBAAK;;0CACtE,KAACpG;gCAAW+B,SAAQ;gCAAQsE,MAAM;gCAAChE,IAAI;oCAAE+D,YAAY;gCAAK;0CACvDvE,OAAOd,GAAG,CAAC,UAAU,IAAI;;0CAE5B,KAACf;gCACC+B,SAAQ;gCACRC,OAAM;gCACNqE,MAAM;gCACNhE,IAAI;oCAAEiE,YAAY;oCAAaF,YAAY;gCAAK;0CAE/CvE,QAAOd,cAAAA,GAAG,CAAC,SAAS,YAAbA,cAAiB;;;;;YAIjC;YACA;gBACE6E,OAAO;gBACPC,YAAY;gBACZC,MAAM;gBACNC,UAAU;gBACVC,aAAa,CAACC,QAAQlF;wBAAoBA;2BAAPc,QAAOd,cAAAA,GAAG,CAAC,SAAS,YAAbA,cAAiB;;gBAC3DmF,YAAY,CAAC,EAAEnF,GAAG,EAAgB;wBAClBA,YAGIA;wBAGZA;oBANN,MAAMwF,SAAQxF,aAAAA,GAAG,CAAC,QAAQ,YAAZA,aAAgB,CAAC;oBAC/B,OAAO8D,MAAM;wBACX;4BACEQ,OAAOxD,QAAOd,cAAAA,GAAG,CAAC,SAAS,YAAbA,cAAiB;4BAC/BiB,OAAOjB,GAAG,CAAC,SAAS,KAAK,SAAS,YAAYA,GAAG,CAAC,SAAS,KAAK,WAAW,UAAUM;wBACvF;2BACIN,EAAAA,mBAAAA,GAAG,CAAC,cAAc,qBAAlBA,iBAAoByF,KAAK,MAAK,SAC9B;4BAAC;gCAAEnB,OAAO;gCAAmBrD,OAAO;4BAAmB;yBAAE,GACzD,EAAE;2BACF,OAAOuE,MAAME,IAAI,KAAK,WAAW;4BAAC;gCAAEpB,OAAO,GAAGkB,MAAME,IAAI,CAAC,KAAK,CAAC;4BAAC;yBAAE,GAAG,EAAE;2BACvE,OAAOF,MAAMG,WAAW,KAAK,WAAW;4BAAC;gCAAErB,OAAO,GAAGkB,MAAMG,WAAW,CAAC,OAAO,CAAC;4BAAC;yBAAE,GAAG,EAAE;2BACvF,OAAOH,MAAMI,YAAY,KAAK,WAAW;4BAAC;gCAAEtB,OAAO,GAAGkB,MAAMI,YAAY,CAAC,QAAQ,CAAC;4BAAC;yBAAE,GAAG,EAAE;2BAC1FJ,MAAMK,OAAO,GAAG;4BAAC;gCAAEvB,OAAO,GAAGkB,MAAMK,OAAO,CAAC,QAAQ,CAAC;gCAAE5E,OAAO;4BAAiB;yBAAE,GAAG,EAAE;qBAC1F;gBACH;YACF;YACA;gBACE4D,OAAO;gBACPC,YAAY;gBACZC,MAAM;gBACNC,UAAU;gBACVC,aAAa,CAACC,QAAQlF;wBAAoBA;2BAAPc,QAAOd,gBAAAA,GAAG,CAAC,WAAW,YAAfA,gBAAmB;;YAC/D;YACA;gBACE6E,OAAO;gBACPC,YAAY;gBACZC,MAAM;gBACNC,UAAU;gBACVC,aAAa,CAACC,QAAQlF,MAAaD,SAASC;YAC9C;YACA7B,kBACE,CAAC6B;gBACC,MAAM8F,SAAS7F,kBAAkBD;gBACjC,qBACE,KAAC/B;oBACCqG,OAAOxD,OAAOd,GAAG,CAAC,UAAU,IAAIA,IAAIe,GAAG;oBACvCgF,OAAO;wBACLC,MAAMlI,cAAcmI,IAAI;wBACxB3B,OAAO;wBACPzC,SAAS,IAAM8C,WAAW3E;wBAC1BkG,mBAAmBlG,GAAG,CAAC,OAAO,GAAGM,YAAY;oBAC/C;oBACAyD,OAAO;wBACL;4BACEoC,KAAK;4BACL7B,OAAO;4BACP8B,MAAMN;4BACNO,UAAU;4BACVC,UAAU,CAACR;4BACXS,gBAAgBT,SAASxF,YAAY;wBACvC;wBACA;4BACE6F,KAAK;4BACL7B,OAAO;4BACP8B,MAAMpG,GAAG,CAAC,SAAS,GACfrC,WAAWC,MAAM4I,iBAAiB,EAAE;gCAAErG,QAAQW,OAAOd,GAAG,CAAC,SAAS;4BAAE,KACpEM;4BACJgG,UAAU,CAACtG,GAAG,CAAC,SAAS;4BACxBuG,gBAAgBvG,GAAG,CAAC,SAAS,GAAGM,YAAY;wBAC9C;qBACD;;YAGP,GACA;gBAAEkB,OAAO;YAAI;SAEhB,EACD,EAAE;IAGJ,MAAMiF,kBAAgClH,QACpC,IAAM;YACJ;gBACEsF,OAAO;gBACPC,YAAY;gBACZC,MAAM;gBACNC,UAAU;gBACVC,aAAa,CAACC,QAAQlF,MAAac,OAAOd,GAAG,CAAC,OAAO,IAAIA,IAAIe,GAAG;YAClE;YACA;gBACE8D,OAAO;gBACPC,YAAY;gBACZC,MAAM;gBACNC,UAAU;gBACVC,aAAa,CAACC,QAAQlF,MACpB,AAAC0G,CAAAA,MAAMC,OAAO,CAAC3G,GAAG,CAAC,YAAY,IAAIA,GAAG,CAAC,YAAY,GAAG,EAAE,AAAD,EAAG4G,IAAI,CAAC;gBACjEzB,YAAY,CAAC,EAAEnF,GAAG,EAAgB,GAChC8D,MAAM;2BACD,AAAC4C,CAAAA,MAAMC,OAAO,CAAC3G,GAAG,CAAC,YAAY,IAAKA,GAAG,CAAC,YAAY,GAAgB,EAAE,AAAD,EAAG2C,GAAG,CAAC,CAACkE,QAAW,CAAA;gCACzFvC,OAAOuC,UAAU,QAAQ,eAAeA,MAAMC,OAAO,CAAC,UAAU;4BAClE,CAAA;2BACI9G,GAAG,CAAC,YAAY,GAAG;4BAAC;gCAAEsE,OAAO;gCAAWrD,OAAO;4BAAiB;yBAAE,GAAG,EAAE;qBAC5E;YACL;YACA;gBACE4D,OAAO;gBACPC,YAAY;gBACZC,MAAM;gBACNC,UAAU;gBACVC,aAAa,CAACC,QAAQlF,MACpBA,GAAG,CAAC,YAAY,IAAIA,GAAG,CAAC,UAAU,GAC9B,GAAGP,OAAOO,GAAG,CAAC,YAAY,EAAE,GAAG,EAAEA,GAAG,CAAC,UAAU,GAAGP,OAAOO,GAAG,CAAC,UAAU,IAAI,QAAQ,GACnF;YACR;SACD,EACD,EAAE;IAGJ,qBACE;;0BACE,KAACjC;gBACCgJ,QAAQ;gBACRC,MAAMnJ,eAAe,gBAAgB;oBACnCoJ,QAAQ;oBACRC,SACE;gBACJ;gBACAC,cAAc;gBACdC,cAAc;0BAEd,cAAA,MAACtI;oBAAM8D,SAAS;;sCACd,MAAC5D;4BAAKU,OAAO8E;4BAAK6C,UAAU,CAACC,QAAQC,OAAmB9C,OAAO8C;;8CAC7D,KAACxI;oCAAIW,OAAM;oCAAQ4E,OAAM;;8CACzB,KAACvF;oCAAIW,OAAM;oCAAY4E,OAAM;;;;wBAE9BE,QAAQ,wBACP,KAAC1C;4BAECC,OAAOA;4BACPC,cAAa;4BACbC,SAAS2C;4BACT1C,YAAW;4BACXC,QAAQ,CAACnC,MAASA,GAAG,CAAC,OAAO,GAAG2E,WAAW3E,OAAOM;2BAL9C,yBAQN,KAACwB;4BAECC,OAAOA;4BACPC,cAAa;4BACbC,SAASwE;4BACTvE,YAAW;2BAJP;;;;0BASZ,KAAC3B;gBAAcP,KAAK0E;gBAASjE,SAAS,IAAMkE,WAAW;;;;AAG7D"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/marketing/src/lib/components/staff-org-email-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 { buildRoute, Route } from '@aglyn/aglyn/app-utils/console-routes'\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { mdiEyeOutline } from '@aglyn/shared-data-mdi'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport {\n ListRowActions,\n ListTable,\n listActionsColumn,\n} from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport { TABLE_ROW_HEIGHT } from '@aglyn/shared-ui-jsx/const/table-pagination'\nimport { useFirestore, usePagedCollection } from '@aglyn/tenant-feature-instance'\nimport {\n Box,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Stack,\n Tab,\n Tabs,\n Typography,\n} from '@mui/material'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport { collection, documentId, limit, orderBy, query } from 'firebase/firestore'\nimport { type ReactNode, useMemo, useState } from 'react'\n\n/*\n * AN ORGANIZATION'S EMAIL WORK, AS STAFF SEE IT (AGL-3380).\n *\n * The staff organization page is about a customer the staffer is not a\n * member of, so nothing here sends, schedules or edits. Each tab is a paged\n * walk of one collection in document-id order. A send opens the message it\n * stored, drawn in a frame that runs nothing, or the design it was composed\n * from in the staff preview.\n */\n\ntype Row = { $id: string } & Record<string, any>\n\nconst whenOf = (value: any): string => {\n if (typeof value?.toDate === 'function') return value.toDate().toLocaleString()\n if (typeof value?.seconds === 'number') return new Date(value.seconds * 1000).toLocaleString()\n if (typeof value === 'number') return new Date(value).toLocaleString()\n return '—'\n}\n\n/** When a send went, or is due: the sent stamp, else the schedule, else its creation. */\nconst sendWhen = (row: Row): string =>\n row['sentAt']\n ? whenOf(row['sentAt'])\n : row['sendAtMs']\n ? `due ${whenOf(row['sendAtMs'])}`\n : whenOf(row['createdAtMs'])\n\n/** The staff preview of the design a send was composed from, if it names one. */\nconst designPreviewHref = (row: Row): string | undefined =>\n typeof row['templateScreenId'] === 'string' &&\n row['templateScreenId'] &&\n typeof row['hostId'] === 'string' &&\n row['hostId']\n ? buildRoute(Route.ADMIN_SITE_PREVIEW, {\n hostId: row['hostId'],\n kind: 'screen',\n docId: row['templateScreenId'],\n })\n : undefined\n\n/**\n * A stored message body, in a frame with `sandbox` and no permissions: a\n * customer's HTML is only ever pixels on the staff page.\n */\nfunction MessageDialog(props: { row: Row | null; onClose: () => void }) {\n const { row, onClose } = props\n return (\n <Dialog open={Boolean(row)} onClose={onClose} maxWidth=\"md\" fullWidth>\n <DialogTitle>{row ? String(row['subject'] || row.$id) : ''}</DialogTitle>\n <DialogContent>\n {row?.['preheader'] ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {String(row['preheader'])}\n </Typography>\n ) : null}\n <Box\n component=\"iframe\"\n title=\"Message\"\n sandbox=\"\"\n srcDoc={String(row?.['body'] ?? '')}\n sx={{ mt: 1, width: '100%', height: 560, border: 1, borderColor: 'divider', bgcolor: '#fff' }}\n />\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose}>{'Close'}</Button>\n </DialogActions>\n </Dialog>\n )\n}\n\n/** One paged table of an organization subcollection. */\nfunction OrgCollectionTable(props: {\n orgId: string\n collectionId: string\n columns: GridColDef[]\n emptyLabel: string\n onOpen?: (row: Row) => void\n}) {\n const { orgId, collectionId, columns, emptyLabel, onOpen } = props\n const firestore = useFirestore()\n const paged = usePagedCollection<Row>(\n (pageLimit) =>\n orgId\n ? query(\n collection(firestore, 'orgs', orgId, collectionId),\n orderBy(documentId()),\n limit(pageLimit),\n )\n : null,\n [firestore, orgId, collectionId],\n { idField: '$id' },\n )\n const byId = useMemo(() => new Map(paged.rows.map((row) => [row.$id, row])), [paged.rows])\n return (\n <Stack spacing={1}>\n <ListTable\n rows={paged.rows}\n columns={columns}\n loading={paged.status === 'loading'}\n disableColumnSorting\n noRowsLabel={emptyLabel}\n onOpen={\n onOpen\n ? (id) => {\n const row = byId.get(String(id))\n if (row) onOpen(row)\n }\n : undefined\n }\n hideFooter\n rowHeight={TABLE_ROW_HEIGHT}\n />\n <ListPagination\n page={paged.page}\n pageSize={paged.pageSize}\n rowCount={paged.rows.length}\n hasMore={paged.hasMore}\n onPageChange={paged.setPage}\n onPageSizeChange={paged.setPageSize}\n />\n </Stack>\n )\n}\n\nconst chips = (items: Array<{ label: string; color?: 'success' | 'warning' | 'error' }>): ReactNode => (\n <Stack direction=\"row\" spacing={0.5} useFlexGap sx={{ alignItems: 'center', height: '100%', flexWrap: 'wrap' }}>\n {items.map((item) => (\n <Chip\n key={item.label}\n size=\"small\"\n label={item.label}\n color={item.color ?? 'default'}\n variant={item.color ? 'filled' : 'outlined'}\n />\n ))}\n </Stack>\n)\n\ntype EmailTab = 'sends' | 'campaigns'\n\n/**\n * The staff organization page's email card (`adminOrgDetail` zone): every\n * campaign send with its state and numbers, and the campaign containers\n * with the sites each runs on.\n */\nexport function StaffOrgEmailCard(props: { orgId: string }) {\n const { orgId } = props\n const [tab, setTab] = useState<EmailTab>('sends')\n const [message, setMessage] = useState<Row | null>(null)\n\n const sendColumns: GridColDef[] = useMemo(\n () => [\n {\n field: 'subject',\n headerName: 'Subject',\n flex: 1.4,\n minWidth: 200,\n valueGetter: (_value, row: Row) => String(row['subject'] || '(no subject)'),\n renderCell: ({ row }: { row: Row }) => (\n <Stack sx={{ justifyContent: 'center', height: '100%', lineHeight: 1.25 }}>\n <Typography variant=\"body2\" noWrap sx={{ lineHeight: 1.25 }}>\n {String(row['subject'] || '(no subject)')}\n </Typography>\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n noWrap\n sx={{ fontFamily: 'monospace', lineHeight: 1.25 }}\n >\n {String(row['hostId'] ?? 'no site')}\n </Typography>\n </Stack>\n ),\n },\n {\n field: 'status',\n headerName: 'Status',\n flex: 1.4,\n minWidth: 220,\n valueGetter: (_value, row: Row) => String(row['status'] ?? 'draft'),\n renderCell: ({ row }: { row: Row }) => {\n const stats = row['stats'] ?? {}\n return chips([\n {\n label: String(row['status'] ?? 'draft'),\n color: row['status'] === 'sent' ? 'success' : row['status'] === 'failed' ? 'error' : undefined,\n },\n ...(row['staffReview']?.state === 'held'\n ? [{ label: 'held for review', color: 'warning' as const }]\n : []),\n ...(typeof stats.sent === 'number' ? [{ label: `${stats.sent} sent` }] : []),\n ...(typeof stats.uniqueOpens === 'number' ? [{ label: `${stats.uniqueOpens} opened` }] : []),\n ...(typeof stats.uniqueClicks === 'number' ? [{ label: `${stats.uniqueClicks} clicked` }] : []),\n ...(stats.bounced ? [{ label: `${stats.bounced} bounced`, color: 'error' as const }] : []),\n ])\n },\n },\n {\n field: 'audience',\n headerName: 'Audience',\n flex: 0.6,\n minWidth: 110,\n valueGetter: (_value, row: Row) => String(row['audience'] ?? '—'),\n },\n {\n field: 'when',\n headerName: 'When',\n flex: 0.9,\n minWidth: 160,\n valueGetter: (_value, row: Row) => sendWhen(row),\n },\n listActionsColumn(\n (row: Row) => {\n const design = designPreviewHref(row)\n return (\n <ListRowActions\n label={String(row['subject'] || row.$id)}\n quick={{\n icon: mdiEyeOutline.path,\n label: 'View message',\n onClick: () => setMessage(row),\n unavailableReason: row['body'] ? undefined : 'No message body was stored',\n }}\n items={[\n {\n key: 'design',\n label: 'Open design preview',\n href: design,\n external: true,\n disabled: !design,\n disabledReason: design ? undefined : 'This send was not composed from a design',\n },\n {\n key: 'site',\n label: 'Open site',\n href: row['hostId']\n ? buildRoute(Route.ADMIN_SITE_DETAIL, { hostId: String(row['hostId']) })\n : undefined,\n disabled: !row['hostId'],\n disabledReason: row['hostId'] ? undefined : 'This send names no site',\n },\n ]}\n />\n )\n },\n { width: 110 },\n ),\n ],\n [],\n )\n\n const campaignColumns: GridColDef[] = useMemo(\n () => [\n {\n field: 'name',\n headerName: 'Campaign',\n flex: 1.4,\n minWidth: 200,\n valueGetter: (_value, row: Row) => String(row['name'] || row.$id),\n },\n {\n field: 'visibleTo',\n headerName: 'Runs on',\n flex: 1.2,\n minWidth: 180,\n valueGetter: (_value, row: Row) =>\n (Array.isArray(row['visibleTo']) ? row['visibleTo'] : []).join(', '),\n renderCell: ({ row }: { row: Row }) =>\n chips([\n ...(Array.isArray(row['visibleTo']) ? (row['visibleTo'] as string[]) : []).map((token) => ({\n label: token === 'org' ? 'every site' : token.replace(/^host:/, ''),\n })),\n ...(row['deletedAt'] ? [{ label: 'deleted', color: 'error' as const }] : []),\n ]),\n },\n {\n field: 'window',\n headerName: 'Window',\n flex: 1,\n minWidth: 180,\n valueGetter: (_value, row: Row) =>\n row['startAtMs'] || row['endAtMs']\n ? `${whenOf(row['startAtMs'])} – ${row['endAtMs'] ? whenOf(row['endAtMs']) : 'open'}`\n : '—',\n },\n ],\n [],\n )\n\n return (\n <>\n <CardDisplay\n header={'Email campaigns'}\n help={pluginDocsHelp('staffConsole', {\n anchor: '#staff-org-email',\n excerpt:\n \"The organization's campaign sends and campaigns. A send opens its stored message or its design preview.\",\n })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n <Tabs\n value={tab}\n onChange={(_event, next: EmailTab) => setTab(next)}\n variant=\"scrollable\"\n scrollButtons=\"auto\"\n allowScrollButtonsMobile\n >\n\n <Tab value=\"sends\" label=\"Sends\" />\n <Tab value=\"campaigns\" label=\"Campaigns\" />\n </Tabs>\n {tab === 'sends' ? (\n <OrgCollectionTable\n key=\"sends\"\n orgId={orgId}\n collectionId=\"campaigns\"\n columns={sendColumns}\n emptyLabel=\"This organization has sent no campaigns\"\n onOpen={(row) => (row['body'] ? setMessage(row) : undefined)}\n />\n ) : (\n <OrgCollectionTable\n key=\"campaigns\"\n orgId={orgId}\n collectionId=\"emailCampaigns\"\n columns={campaignColumns}\n emptyLabel=\"This organization has no email campaigns\"\n />\n )}\n </Stack>\n </CardDisplay>\n <MessageDialog row={message} onClose={() => setMessage(null)} />\n </>\n )\n}\n"],"names":["buildRoute","Route","pluginDocsHelp","mdiEyeOutline","CardDisplay","ListPagination","ListRowActions","ListTable","listActionsColumn","TABLE_ROW_HEIGHT","useFirestore","usePagedCollection","Box","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","Stack","Tab","Tabs","Typography","collection","documentId","limit","orderBy","query","useMemo","useState","whenOf","value","toDate","toLocaleString","seconds","Date","sendWhen","row","designPreviewHref","ADMIN_SITE_PREVIEW","hostId","kind","docId","undefined","MessageDialog","props","onClose","open","Boolean","maxWidth","fullWidth","String","$id","variant","color","component","title","sandbox","srcDoc","sx","mt","width","height","border","borderColor","bgcolor","onClick","OrgCollectionTable","orgId","collectionId","columns","emptyLabel","onOpen","firestore","paged","pageLimit","idField","byId","Map","rows","map","spacing","loading","status","disableColumnSorting","noRowsLabel","id","get","hideFooter","rowHeight","page","pageSize","rowCount","length","hasMore","onPageChange","setPage","onPageSizeChange","setPageSize","chips","items","direction","useFlexGap","alignItems","flexWrap","item","size","label","StaffOrgEmailCard","tab","setTab","message","setMessage","sendColumns","field","headerName","flex","minWidth","valueGetter","_value","renderCell","justifyContent","lineHeight","noWrap","fontFamily","stats","state","sent","uniqueOpens","uniqueClicks","bounced","design","quick","icon","path","unavailableReason","key","href","external","disabled","disabledReason","ADMIN_SITE_DETAIL","campaignColumns","Array","isArray","join","token","replace","header","help","anchor","excerpt","contentGutterX","contentGutterY","onChange","_event","next","scrollButtons","allowScrollButtonsMobile"],"mappings":"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"}