@aglyn/plugins-marketing 1.0.0-beta.233 → 1.0.0-beta.235

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 (26) hide show
  1. package/package.json +16 -16
  2. package/src/lib/components/campaign-conversions-card.d.ts +7 -0
  3. package/src/lib/components/campaign-conversions-card.js +4 -5
  4. package/src/lib/components/campaign-conversions-card.js.map +1 -1
  5. package/src/lib/components/campaign-detail-card.js +3 -4
  6. package/src/lib/components/campaign-detail-card.js.map +1 -1
  7. package/src/lib/components/campaign-glance-card.component.js +1 -2
  8. package/src/lib/components/campaign-glance-card.component.js.map +1 -1
  9. package/src/lib/components/campaign-report-card.js +1 -2
  10. package/src/lib/components/campaign-report-card.js.map +1 -1
  11. package/src/lib/components/campaigns-card.js +2 -2
  12. package/src/lib/components/campaigns-card.js.map +1 -1
  13. package/src/lib/components/email-detail.js +2 -4
  14. package/src/lib/components/email-detail.js.map +1 -1
  15. package/src/lib/components/email-recipients-card.js +1 -2
  16. package/src/lib/components/email-recipients-card.js.map +1 -1
  17. package/src/lib/components/email-template-report-card.js +3 -4
  18. package/src/lib/components/email-template-report-card.js.map +1 -1
  19. package/src/lib/components/emails-list-card.js +3 -4
  20. package/src/lib/components/emails-list-card.js.map +1 -1
  21. package/src/lib/components/org-conversions-section.js +2 -5
  22. package/src/lib/components/org-conversions-section.js.map +1 -1
  23. package/src/lib/components/org-marketing-overview-card.js +1 -1
  24. package/src/lib/components/org-marketing-overview-card.js.map +1 -1
  25. package/src/lib/components/org-overlays-card.js +1 -1
  26. package/src/lib/components/org-overlays-card.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 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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"}
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', { anchor: '#org-marketing-overview' })}\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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"}
@@ -146,7 +146,7 @@ const STATUS_CHIP = {
146
146
  header: "Announcement bars & popups",
147
147
  subheader: "Every site in this organization",
148
148
  help: pluginDocsHelp('marketingOverlays', {
149
- anchor: '#across-your-sites'
149
+ anchor: '#org-overlays'
150
150
  }),
151
151
  contentGutterX: true,
152
152
  contentGutterY: true,
@@ -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 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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"}
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', { anchor: '#org-overlays' })}\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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"}