@aglyn/plugins-marketing 1.0.0-beta.179 → 1.0.0-beta.180
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/package.json +15 -15
- package/src/lib/components/campaign-conversions-card.js +22 -4
- package/src/lib/components/campaign-conversions-card.js.map +1 -1
- package/src/lib/components/campaign-detail-card.js +3 -4
- package/src/lib/components/campaign-detail-card.js.map +1 -1
- package/src/lib/components/campaign-report-card.js +3 -3
- package/src/lib/components/campaign-report-card.js.map +1 -1
- package/src/lib/components/email-compose-card.d.ts +5 -2
- package/src/lib/components/email-compose-card.js +2 -3
- package/src/lib/components/email-compose-card.js.map +1 -1
- package/src/lib/components/email-detail.d.ts +5 -2
- package/src/lib/components/email-detail.js +4 -4
- package/src/lib/components/email-detail.js.map +1 -1
- package/src/lib/components/email-messages-widget.d.ts +17 -2
- package/src/lib/components/email-messages-widget.js +33 -2
- package/src/lib/components/email-messages-widget.js.map +1 -1
- package/src/lib/components/emails-list-card.d.ts +9 -6
- package/src/lib/components/emails-list-card.js +9 -9
- package/src/lib/components/emails-list-card.js.map +1 -1
- package/src/lib/components/host-experiments-card.component.d.ts +2 -0
- package/src/lib/components/host-experiments-card.component.js +20 -25
- package/src/lib/components/host-experiments-card.component.js.map +1 -1
- package/src/lib/components/host-marketing-summary-card.component.d.ts +15 -0
- package/src/lib/components/host-marketing-summary-card.component.js +59 -68
- package/src/lib/components/host-marketing-summary-card.component.js.map +1 -1
- package/src/lib/components/host-overlays-card.component.js +15 -37
- package/src/lib/components/host-overlays-card.component.js.map +1 -1
- package/src/lib/components/inbox-campaigns-widget.d.ts +23 -0
- package/src/lib/components/inbox-campaigns-widget.js +63 -0
- package/src/lib/components/inbox-campaigns-widget.js.map +1 -0
- package/src/lib/components/marketing-aggregates.d.ts +95 -0
- package/src/lib/components/marketing-aggregates.js +114 -0
- package/src/lib/components/marketing-aggregates.js.map +1 -0
- package/src/lib/components/marketing-console-page.js +32 -27
- package/src/lib/components/marketing-console-page.js.map +1 -1
- package/src/lib/components/marketing-console-sections.d.ts +24 -13
- package/src/lib/components/marketing-console-sections.js +37 -12
- package/src/lib/components/marketing-console-sections.js.map +1 -1
- package/src/lib/components/org-conversions-section.d.ts +32 -0
- package/src/lib/components/org-conversions-section.js +130 -0
- package/src/lib/components/org-conversions-section.js.map +1 -0
- package/src/lib/components/org-experiments-card.d.ts +32 -0
- package/src/lib/components/org-experiments-card.js +407 -0
- package/src/lib/components/org-experiments-card.js.map +1 -0
- package/src/lib/components/org-marketing-overview-card.d.ts +34 -0
- package/src/lib/components/org-marketing-overview-card.js +342 -0
- package/src/lib/components/org-marketing-overview-card.js.map +1 -0
- package/src/lib/components/org-overlays-card.d.ts +35 -0
- package/src/lib/components/org-overlays-card.js +345 -0
- package/src/lib/components/org-overlays-card.js.map +1 -0
- package/src/lib/components/org-site-picker-button.d.ts +26 -0
- package/src/lib/components/org-site-picker-button.js +104 -0
- package/src/lib/components/org-site-picker-button.js.map +1 -0
- package/src/lib/components/overlay-labels.d.ts +38 -0
- package/src/lib/components/overlay-labels.js +55 -0
- package/src/lib/components/overlay-labels.js.map +1 -0
- package/src/lib/components/use-emails-hub-path.d.ts +17 -8
- package/src/lib/components/use-emails-hub-path.js +29 -11
- package/src/lib/components/use-emails-hub-path.js.map +1 -1
- package/src/lib/components/use-one-shot-reads.d.ts +55 -0
- package/src/lib/components/use-one-shot-reads.js +168 -0
- package/src/lib/components/use-one-shot-reads.js.map +1 -0
- package/src/lib/model/experiments.d.ts +35 -0
- package/src/lib/model/experiments.js +34 -0
- package/src/lib/model/experiments.js.map +1 -1
- package/src/lib/model/overlays.d.ts +13 -2
- package/src/lib/model/overlays.js +18 -6
- package/src/lib/model/overlays.js.map +1 -1
- package/src/lib/plugin.js +13 -8
- package/src/lib/plugin.js.map +1 -1
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ 'use client';
|
|
17
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
18
|
+
import { checkEntitlement, pluginDocsHelp } from "@aglyn/aglyn";
|
|
19
|
+
import { AppLink, CardDisplay } from "@aglyn/shared-ui-jsx";
|
|
20
|
+
import { ListPagination } from "@aglyn/shared-ui-jsx/components/list-pagination.component";
|
|
21
|
+
import { ScrollTable } from "@aglyn/shared-ui-jsx/components/scroll-table.component";
|
|
22
|
+
import { TABLE_PAGE_SIZE_DEFAULT } from "@aglyn/shared-ui-jsx/const/table-pagination";
|
|
23
|
+
import { Alert, Divider, Stack, TableBody, TableCell, TableHead, TableRow, Typography } from "@mui/material";
|
|
24
|
+
import { useFirestore } from "@aglyn/tenant-feature-instance";
|
|
25
|
+
import { useMemo, useState } from "react";
|
|
26
|
+
import { campaignSendsQuery } from "./campaign-queries.js";
|
|
27
|
+
import { readSendFigures, readSiteExperimentFigures, readSiteOverlayFigures } from "./marketing-aggregates.js";
|
|
28
|
+
import { useMarketingOrgMount } from "./marketing-org-mount.js";
|
|
29
|
+
import { orgSiteSectionHref } from "./org-site-picker-button.js";
|
|
30
|
+
import { useOneShotRead, useOrgSiteReads } from "./use-one-shot-reads.js";
|
|
31
|
+
/**
|
|
32
|
+
* How many sites the Overview reads figures for.
|
|
33
|
+
*
|
|
34
|
+
* The one read on this card that grows with the organization: four
|
|
35
|
+
* aggregations a site. Capped, the landing section of the org hub costs four
|
|
36
|
+
* org-wide aggregations and four for each site up to this number, however
|
|
37
|
+
* many sites there are; the sites past it are counted in a note rather than
|
|
38
|
+
* read.
|
|
39
|
+
*/ export const ORG_OVERVIEW_SITE_CAP = 25;
|
|
40
|
+
/** A figure as a cell prints it: a dash for one nobody has measured. */ const figure = (value)=>value == null ? '—' : value.toLocaleString();
|
|
41
|
+
/**
|
|
42
|
+
* A column's total, or `null` unless EVERY site in it answered. A total over
|
|
43
|
+
* the sites that happened to answer would print as the organization's figure
|
|
44
|
+
* while leaving some of it out.
|
|
45
|
+
*/ function totalOf(values) {
|
|
46
|
+
let total = 0;
|
|
47
|
+
for (const value of values){
|
|
48
|
+
if (value == null) return null;
|
|
49
|
+
total += value;
|
|
50
|
+
}
|
|
51
|
+
return total;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Marketing at a glance, over every site — the landing section of the
|
|
55
|
+
* organization's Marketing hub.
|
|
56
|
+
*
|
|
57
|
+
* The email figures come from ONE collection: campaign sends belong to the
|
|
58
|
+
* organization (`orgs/{orgId}/campaigns`), so four aggregations over it
|
|
59
|
+
* answer for every site at once. Overlays and A/B tests are a SITE's —
|
|
60
|
+
* drawn on its pages, splitting its traffic — so their figures are read per
|
|
61
|
+
* site, each site one row, and each row opens that site's own Overview.
|
|
62
|
+
*
|
|
63
|
+
* A channel the plan does not carry is left out rather than read: an
|
|
64
|
+
* organization without overlays or A/B testing pays for no per-site reads
|
|
65
|
+
* at all, and its landing is the email figures alone.
|
|
66
|
+
*/ export function OrgMarketingOverviewCard(props) {
|
|
67
|
+
var _ref;
|
|
68
|
+
const { org } = props;
|
|
69
|
+
const mount = useMarketingOrgMount();
|
|
70
|
+
const firestore = useFirestore();
|
|
71
|
+
const orgId = (_ref = mount == null ? void 0 : mount.orgId) != null ? _ref : null;
|
|
72
|
+
const overlaysOn = checkEntitlement(org, 'marketingOverlays');
|
|
73
|
+
const testsOn = checkEntitlement(org, 'abTesting');
|
|
74
|
+
const email = useOneShotRead(()=>{
|
|
75
|
+
const sends = campaignSendsQuery(firestore, orgId, null);
|
|
76
|
+
return sends ? readSendFigures(sends) : null;
|
|
77
|
+
}, [
|
|
78
|
+
firestore,
|
|
79
|
+
orgId
|
|
80
|
+
]);
|
|
81
|
+
const allSites = useMemo(()=>{
|
|
82
|
+
var _ref;
|
|
83
|
+
return (_ref = mount == null ? void 0 : mount.hosts) != null ? _ref : [];
|
|
84
|
+
}, [
|
|
85
|
+
mount
|
|
86
|
+
]);
|
|
87
|
+
const sites = useMemo(()=>allSites.slice(0, ORG_OVERVIEW_SITE_CAP), [
|
|
88
|
+
allSites
|
|
89
|
+
]);
|
|
90
|
+
const perSite = useOrgSiteReads(overlaysOn || testsOn ? sites.map((site)=>site.id) : [], (hostId)=>Promise.all([
|
|
91
|
+
overlaysOn ? readSiteOverlayFigures(firestore, hostId) : null,
|
|
92
|
+
testsOn ? readSiteExperimentFigures(firestore, hostId) : null
|
|
93
|
+
]).then(([overlays, tests])=>({
|
|
94
|
+
overlays,
|
|
95
|
+
tests
|
|
96
|
+
})), [
|
|
97
|
+
firestore,
|
|
98
|
+
overlaysOn,
|
|
99
|
+
testsOn
|
|
100
|
+
]);
|
|
101
|
+
const rows = useMemo(()=>sites.map((site)=>{
|
|
102
|
+
var _ref, _ref1, _ref2, _ref3, _ref4;
|
|
103
|
+
var _perSite_reads_get, _answer_overlays, _answer_overlays1, _answer_tests, _answer_tests1;
|
|
104
|
+
const answer = (_ref = (_perSite_reads_get = perSite.reads.get(site.id)) == null ? void 0 : _perSite_reads_get.value) != null ? _ref : null;
|
|
105
|
+
return {
|
|
106
|
+
id: site.id,
|
|
107
|
+
name: site.name || site.subdomain || site.id,
|
|
108
|
+
href: mount ? orgSiteSectionHref(mount, site.id, 'overview') : null,
|
|
109
|
+
views: (_ref1 = answer == null ? void 0 : (_answer_overlays = answer.overlays) == null ? void 0 : _answer_overlays.views) != null ? _ref1 : null,
|
|
110
|
+
clicks: (_ref2 = answer == null ? void 0 : (_answer_overlays1 = answer.overlays) == null ? void 0 : _answer_overlays1.clicks) != null ? _ref2 : null,
|
|
111
|
+
running: (_ref3 = answer == null ? void 0 : (_answer_tests = answer.tests) == null ? void 0 : _answer_tests.running) != null ? _ref3 : null,
|
|
112
|
+
decided: (_ref4 = answer == null ? void 0 : (_answer_tests1 = answer.tests) == null ? void 0 : _answer_tests1.decided) != null ? _ref4 : null
|
|
113
|
+
};
|
|
114
|
+
}), [
|
|
115
|
+
sites,
|
|
116
|
+
perSite.reads,
|
|
117
|
+
mount
|
|
118
|
+
]);
|
|
119
|
+
const totals = useMemo(()=>({
|
|
120
|
+
views: totalOf(rows.map((row)=>row.views)),
|
|
121
|
+
clicks: totalOf(rows.map((row)=>row.clicks)),
|
|
122
|
+
running: totalOf(rows.map((row)=>row.running)),
|
|
123
|
+
decided: totalOf(rows.map((row)=>row.decided))
|
|
124
|
+
}), [
|
|
125
|
+
rows
|
|
126
|
+
]);
|
|
127
|
+
const [page, setPage] = useState(0);
|
|
128
|
+
const [pageSize, setPageSize] = useState(TABLE_PAGE_SIZE_DEFAULT);
|
|
129
|
+
const pageRows = rows.slice(page * pageSize, (page + 1) * pageSize);
|
|
130
|
+
const truncated = allSites.length > sites.length;
|
|
131
|
+
const sends = email.value;
|
|
132
|
+
const emailMetrics = [
|
|
133
|
+
{
|
|
134
|
+
label: 'Emails sent',
|
|
135
|
+
value: figure(sends == null ? void 0 : sends.sent)
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
label: 'Opens / clicks',
|
|
139
|
+
value: `${figure(sends == null ? void 0 : sends.opens)} / ${figure(sends == null ? void 0 : sends.clicks)}`
|
|
140
|
+
},
|
|
141
|
+
...(sends == null ? void 0 : sends.scheduled) ? [
|
|
142
|
+
{
|
|
143
|
+
label: 'Scheduled sends',
|
|
144
|
+
value: figure(sends.scheduled)
|
|
145
|
+
}
|
|
146
|
+
] : []
|
|
147
|
+
];
|
|
148
|
+
return /*#__PURE__*/ _jsx(CardDisplay, {
|
|
149
|
+
header: 'At a glance',
|
|
150
|
+
subheader: 'Every site in this organization',
|
|
151
|
+
help: pluginDocsHelp('marketingOverlays', {
|
|
152
|
+
anchor: '#across-your-sites'
|
|
153
|
+
}),
|
|
154
|
+
contentGutterX: true,
|
|
155
|
+
contentGutterY: true,
|
|
156
|
+
children: /*#__PURE__*/ _jsxs(Stack, {
|
|
157
|
+
spacing: 2,
|
|
158
|
+
children: [
|
|
159
|
+
/*#__PURE__*/ _jsx(Stack, {
|
|
160
|
+
direction: "row",
|
|
161
|
+
spacing: 3,
|
|
162
|
+
sx: {
|
|
163
|
+
flexWrap: 'wrap',
|
|
164
|
+
rowGap: 1.5
|
|
165
|
+
},
|
|
166
|
+
children: emailMetrics.map((metric)=>/*#__PURE__*/ _jsxs(Stack, {
|
|
167
|
+
spacing: 0,
|
|
168
|
+
children: [
|
|
169
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
170
|
+
variant: "caption",
|
|
171
|
+
color: "text.secondary",
|
|
172
|
+
children: metric.label
|
|
173
|
+
}),
|
|
174
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
175
|
+
variant: "h6",
|
|
176
|
+
children: metric.value
|
|
177
|
+
})
|
|
178
|
+
]
|
|
179
|
+
}, metric.label))
|
|
180
|
+
}),
|
|
181
|
+
overlaysOn || testsOn ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
182
|
+
children: [
|
|
183
|
+
/*#__PURE__*/ _jsx(Divider, {}),
|
|
184
|
+
mount && !mount.hostsReady ? null : !sites.length ? /*#__PURE__*/ _jsx(Typography, {
|
|
185
|
+
variant: "body2",
|
|
186
|
+
color: "text.secondary",
|
|
187
|
+
children: 'This organization has no sites yet, so there are no ' + 'overlays or A/B tests to count.'
|
|
188
|
+
}) : /*#__PURE__*/ _jsxs(Stack, {
|
|
189
|
+
spacing: 1,
|
|
190
|
+
children: [
|
|
191
|
+
truncated ? /*#__PURE__*/ _jsx(Alert, {
|
|
192
|
+
severity: "info",
|
|
193
|
+
children: `This organization has ${allSites.length.toLocaleString()} ` + `sites. The table and its totals cover the first ` + `${ORG_OVERVIEW_SITE_CAP.toLocaleString()}; each other ` + "site's own Marketing page has its figures."
|
|
194
|
+
}) : null,
|
|
195
|
+
/*#__PURE__*/ _jsxs(ScrollTable, {
|
|
196
|
+
size: "small",
|
|
197
|
+
"aria-label": "Figures by site",
|
|
198
|
+
children: [
|
|
199
|
+
/*#__PURE__*/ _jsx(TableHead, {
|
|
200
|
+
children: /*#__PURE__*/ _jsxs(TableRow, {
|
|
201
|
+
children: [
|
|
202
|
+
/*#__PURE__*/ _jsx(TableCell, {
|
|
203
|
+
children: 'Site'
|
|
204
|
+
}),
|
|
205
|
+
overlaysOn ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
206
|
+
children: [
|
|
207
|
+
/*#__PURE__*/ _jsx(TableCell, {
|
|
208
|
+
align: "right",
|
|
209
|
+
children: 'Overlay views'
|
|
210
|
+
}),
|
|
211
|
+
/*#__PURE__*/ _jsx(TableCell, {
|
|
212
|
+
align: "right",
|
|
213
|
+
children: 'Overlay clicks'
|
|
214
|
+
})
|
|
215
|
+
]
|
|
216
|
+
}) : null,
|
|
217
|
+
testsOn ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
218
|
+
children: [
|
|
219
|
+
/*#__PURE__*/ _jsx(TableCell, {
|
|
220
|
+
align: "right",
|
|
221
|
+
children: 'Tests running'
|
|
222
|
+
}),
|
|
223
|
+
/*#__PURE__*/ _jsx(TableCell, {
|
|
224
|
+
align: "right",
|
|
225
|
+
children: 'Tests decided'
|
|
226
|
+
})
|
|
227
|
+
]
|
|
228
|
+
}) : null
|
|
229
|
+
]
|
|
230
|
+
})
|
|
231
|
+
}),
|
|
232
|
+
/*#__PURE__*/ _jsxs(TableBody, {
|
|
233
|
+
children: [
|
|
234
|
+
pageRows.map((row)=>/*#__PURE__*/ _jsxs(TableRow, {
|
|
235
|
+
children: [
|
|
236
|
+
/*#__PURE__*/ _jsx(TableCell, {
|
|
237
|
+
children: row.href ? /*#__PURE__*/ _jsx(AppLink, {
|
|
238
|
+
href: row.href,
|
|
239
|
+
children: row.name
|
|
240
|
+
}) : row.name
|
|
241
|
+
}),
|
|
242
|
+
overlaysOn ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
243
|
+
children: [
|
|
244
|
+
/*#__PURE__*/ _jsx(TableCell, {
|
|
245
|
+
align: "right",
|
|
246
|
+
children: figure(row.views)
|
|
247
|
+
}),
|
|
248
|
+
/*#__PURE__*/ _jsx(TableCell, {
|
|
249
|
+
align: "right",
|
|
250
|
+
children: figure(row.clicks)
|
|
251
|
+
})
|
|
252
|
+
]
|
|
253
|
+
}) : null,
|
|
254
|
+
testsOn ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
255
|
+
children: [
|
|
256
|
+
/*#__PURE__*/ _jsx(TableCell, {
|
|
257
|
+
align: "right",
|
|
258
|
+
children: figure(row.running)
|
|
259
|
+
}),
|
|
260
|
+
/*#__PURE__*/ _jsx(TableCell, {
|
|
261
|
+
align: "right",
|
|
262
|
+
children: figure(row.decided)
|
|
263
|
+
})
|
|
264
|
+
]
|
|
265
|
+
}) : null
|
|
266
|
+
]
|
|
267
|
+
}, row.id)),
|
|
268
|
+
/*#__PURE__*/ _jsxs(TableRow, {
|
|
269
|
+
children: [
|
|
270
|
+
/*#__PURE__*/ _jsx(TableCell, {
|
|
271
|
+
sx: {
|
|
272
|
+
fontWeight: 600
|
|
273
|
+
},
|
|
274
|
+
children: truncated ? `These ${sites.length.toLocaleString()} sites` : 'All sites'
|
|
275
|
+
}),
|
|
276
|
+
overlaysOn ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
277
|
+
children: [
|
|
278
|
+
/*#__PURE__*/ _jsx(TableCell, {
|
|
279
|
+
align: "right",
|
|
280
|
+
sx: {
|
|
281
|
+
fontWeight: 600
|
|
282
|
+
},
|
|
283
|
+
children: figure(totals.views)
|
|
284
|
+
}),
|
|
285
|
+
/*#__PURE__*/ _jsx(TableCell, {
|
|
286
|
+
align: "right",
|
|
287
|
+
sx: {
|
|
288
|
+
fontWeight: 600
|
|
289
|
+
},
|
|
290
|
+
children: figure(totals.clicks)
|
|
291
|
+
})
|
|
292
|
+
]
|
|
293
|
+
}) : null,
|
|
294
|
+
testsOn ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
295
|
+
children: [
|
|
296
|
+
/*#__PURE__*/ _jsx(TableCell, {
|
|
297
|
+
align: "right",
|
|
298
|
+
sx: {
|
|
299
|
+
fontWeight: 600
|
|
300
|
+
},
|
|
301
|
+
children: figure(totals.running)
|
|
302
|
+
}),
|
|
303
|
+
/*#__PURE__*/ _jsx(TableCell, {
|
|
304
|
+
align: "right",
|
|
305
|
+
sx: {
|
|
306
|
+
fontWeight: 600
|
|
307
|
+
},
|
|
308
|
+
children: figure(totals.decided)
|
|
309
|
+
})
|
|
310
|
+
]
|
|
311
|
+
}) : null
|
|
312
|
+
]
|
|
313
|
+
})
|
|
314
|
+
]
|
|
315
|
+
})
|
|
316
|
+
]
|
|
317
|
+
}),
|
|
318
|
+
/*#__PURE__*/ _jsx(ListPagination, {
|
|
319
|
+
page: page,
|
|
320
|
+
pageSize: pageSize,
|
|
321
|
+
rowCount: pageRows.length,
|
|
322
|
+
count: rows.length,
|
|
323
|
+
onPageChange: setPage,
|
|
324
|
+
onPageSizeChange: setPageSize
|
|
325
|
+
}),
|
|
326
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
327
|
+
variant: "caption",
|
|
328
|
+
color: "text.secondary",
|
|
329
|
+
children: 'Overlay views and clicks are lifetime counts. A dash is ' + 'a figure that has not arrived or could not be read — ' + 'never a zero.'
|
|
330
|
+
})
|
|
331
|
+
]
|
|
332
|
+
})
|
|
333
|
+
]
|
|
334
|
+
}) : null
|
|
335
|
+
]
|
|
336
|
+
})
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
OrgMarketingOverviewCard.displayName = 'OrgMarketingOverviewCard';
|
|
340
|
+
export default OrgMarketingOverviewCard;
|
|
341
|
+
|
|
342
|
+
//# sourceMappingURL=org-marketing-overview-card.js.map
|
|
@@ -0,0 +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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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type AglynOrgBilling } from '@aglyn/aglyn';
|
|
2
|
+
/**
|
|
3
|
+
* How many overlays are read for each site.
|
|
4
|
+
*
|
|
5
|
+
* Smaller than the site's own ceiling on purpose: a site runs a handful of
|
|
6
|
+
* bars and popups, a page here holds a page of sites' worth of them, and a
|
|
7
|
+
* site with more says so on its row and links to its own list, where every
|
|
8
|
+
* one of them is — and where the order they take can be changed.
|
|
9
|
+
*/
|
|
10
|
+
export declare const ORG_OVERLAYS_PER_SITE = 10;
|
|
11
|
+
export interface OrgOverlaysCardProps {
|
|
12
|
+
/** The org's plan, for the `marketingOverlays` entitlement. */
|
|
13
|
+
org?: Partial<AglynOrgBilling>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Every site's announcement bars and popups, on the organization's
|
|
17
|
+
* Marketing hub.
|
|
18
|
+
*
|
|
19
|
+
* READ-ONLY but for the switch. An overlay is drawn on one site's pages and
|
|
20
|
+
* written against that site's variables, screens and order, so its editor
|
|
21
|
+
* stays on the site: each row's Edit and the New button open the site's own
|
|
22
|
+
* Overlays section. What the org hub adds is the view across sites — which
|
|
23
|
+
* of them are showing what, and their lifetime engagement — and the one
|
|
24
|
+
* change that needs no editor, turning an overlay on or off.
|
|
25
|
+
*
|
|
26
|
+
* Grouped by site, each group in the order the site shows its overlays,
|
|
27
|
+
* under a line naming the site and whether its default bar and popup are
|
|
28
|
+
* on. No 14-day analytics row: that is a document read per day per site,
|
|
29
|
+
* and it lives on the site.
|
|
30
|
+
*/
|
|
31
|
+
export declare function OrgOverlaysCard(props: OrgOverlaysCardProps): import("react").JSX.Element;
|
|
32
|
+
export declare namespace OrgOverlaysCard {
|
|
33
|
+
var displayName: string;
|
|
34
|
+
}
|
|
35
|
+
export default OrgOverlaysCard;
|