@liiift-studio/sanity-visitor-insights 0.77.0 → 0.78.1
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/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +146 -162
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +146 -162
- package/dist/index.mjs.map +1 -1
- package/dist/{ranges-B2W_3Z0t.d.mts → ranges-BgcKmdw3.d.mts} +9 -5
- package/dist/{ranges-B2W_3Z0t.d.ts → ranges-BgcKmdw3.d.ts} +9 -5
- package/dist/server.d.mts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +31 -31
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +31 -31
- package/dist/server.mjs.map +1 -1
- package/package.json +1 -1
- package/src/reportData.ts +13 -5
- package/src/server/reports/acquisition.ts +40 -1
- package/src/server/reports/journey.ts +0 -34
- package/src/server/reports/reports.test.ts +45 -3
- package/src/studio/VisitorInsightsTool.tsx +82 -43
- package/src/studio/panels.test.tsx +156 -2
- package/src/studio/panels.tsx +48 -73
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as sanity from 'sanity';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import { R as ReportEnvelope, a as ReportName, b as RangeKey, M as MetricValue, A as AcquisitionData, c as MeasurementHealthData, D as DiagnosticReport, J as JourneyData, T as TypefaceInterestData } from './ranges-
|
|
4
|
-
export { C as CaptureBasis, d as CaptureEstimate, e as CaptureModel, f as CheckStatus, g as Coverage, h as CrossSourceDay, i as DateRange, j as DiagnosticCheck, E as EmailCampaign, k as EventCutover, l as JourneyOutcome, m as JourneySegment, n as JourneyStep, L as LandingPage, o as LicenceTierRow, P as PREEXISTING, p as REPORT_NAMES, q as ReportError, S as SiteAnalyticsConfig, r as SourceName, s as SourceRow, t as SourceStatus, u as TimelineEvent, v as TypefaceInterestRow, U as UnavailableReason, w as coverageForRange, x as estimated, y as isReportName, z as ok, B as partial, F as previousRange, G as resolveRange, H as unavailable, I as validateSiteConfig, K as valueOrNull } from './ranges-
|
|
3
|
+
import { R as ReportEnvelope, a as ReportName, b as RangeKey, M as MetricValue, A as AcquisitionData, c as MeasurementHealthData, D as DiagnosticReport, J as JourneyData, T as TypefaceInterestData } from './ranges-BgcKmdw3.mjs';
|
|
4
|
+
export { C as CaptureBasis, d as CaptureEstimate, e as CaptureModel, f as CheckStatus, g as Coverage, h as CrossSourceDay, i as DateRange, j as DiagnosticCheck, E as EmailCampaign, k as EventCutover, l as JourneyOutcome, m as JourneySegment, n as JourneyStep, L as LandingPage, o as LicenceTierRow, P as PREEXISTING, p as REPORT_NAMES, q as ReportError, S as SiteAnalyticsConfig, r as SourceName, s as SourceRow, t as SourceStatus, u as TimelineEvent, v as TypefaceInterestRow, U as UnavailableReason, w as coverageForRange, x as estimated, y as isReportName, z as ok, B as partial, F as previousRange, G as resolveRange, H as unavailable, I as validateSiteConfig, K as valueOrNull } from './ranges-BgcKmdw3.mjs';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Data hook for the Studio panels.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as sanity from 'sanity';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import { R as ReportEnvelope, a as ReportName, b as RangeKey, M as MetricValue, A as AcquisitionData, c as MeasurementHealthData, D as DiagnosticReport, J as JourneyData, T as TypefaceInterestData } from './ranges-
|
|
4
|
-
export { C as CaptureBasis, d as CaptureEstimate, e as CaptureModel, f as CheckStatus, g as Coverage, h as CrossSourceDay, i as DateRange, j as DiagnosticCheck, E as EmailCampaign, k as EventCutover, l as JourneyOutcome, m as JourneySegment, n as JourneyStep, L as LandingPage, o as LicenceTierRow, P as PREEXISTING, p as REPORT_NAMES, q as ReportError, S as SiteAnalyticsConfig, r as SourceName, s as SourceRow, t as SourceStatus, u as TimelineEvent, v as TypefaceInterestRow, U as UnavailableReason, w as coverageForRange, x as estimated, y as isReportName, z as ok, B as partial, F as previousRange, G as resolveRange, H as unavailable, I as validateSiteConfig, K as valueOrNull } from './ranges-
|
|
3
|
+
import { R as ReportEnvelope, a as ReportName, b as RangeKey, M as MetricValue, A as AcquisitionData, c as MeasurementHealthData, D as DiagnosticReport, J as JourneyData, T as TypefaceInterestData } from './ranges-BgcKmdw3.js';
|
|
4
|
+
export { C as CaptureBasis, d as CaptureEstimate, e as CaptureModel, f as CheckStatus, g as Coverage, h as CrossSourceDay, i as DateRange, j as DiagnosticCheck, E as EmailCampaign, k as EventCutover, l as JourneyOutcome, m as JourneySegment, n as JourneyStep, L as LandingPage, o as LicenceTierRow, P as PREEXISTING, p as REPORT_NAMES, q as ReportError, S as SiteAnalyticsConfig, r as SourceName, s as SourceRow, t as SourceStatus, u as TimelineEvent, v as TypefaceInterestRow, U as UnavailableReason, w as coverageForRange, x as estimated, y as isReportName, z as ok, B as partial, F as previousRange, G as resolveRange, H as unavailable, I as validateSiteConfig, K as valueOrNull } from './ranges-BgcKmdw3.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Data hook for the Studio panels.
|
package/dist/index.js
CHANGED
|
@@ -1331,12 +1331,6 @@ var sectionToggle = {
|
|
|
1331
1331
|
cursor: "pointer",
|
|
1332
1332
|
flex: "0 0 auto"
|
|
1333
1333
|
};
|
|
1334
|
-
var splitGrid = {
|
|
1335
|
-
display: "grid",
|
|
1336
|
-
gridTemplateColumns: "repeat(auto-fit, minmax(min(26rem, 100%), 1fr))",
|
|
1337
|
-
gap: 24,
|
|
1338
|
-
alignItems: "start"
|
|
1339
|
-
};
|
|
1340
1334
|
var estimateTrack = {
|
|
1341
1335
|
position: "relative",
|
|
1342
1336
|
height: 18,
|
|
@@ -2519,84 +2513,90 @@ function OverviewPanel({ data, previous, onBrush }) {
|
|
|
2519
2513
|
}
|
|
2520
2514
|
)
|
|
2521
2515
|
] }),
|
|
2522
|
-
(data.campaigns?.length ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
"
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
rowKey: (c) => `${c.sentAt}-${c.title}`,
|
|
2537
|
-
filterOn: (c) => `${c.title} ${c.subject}`,
|
|
2538
|
-
filterPlaceholder: "Filter campaigns",
|
|
2539
|
-
exportName: "email-campaigns",
|
|
2540
|
-
columns: [
|
|
2541
|
-
{
|
|
2542
|
-
key: "title",
|
|
2543
|
-
label: "Campaign",
|
|
2544
|
-
sortValue: (c) => c.title,
|
|
2545
|
-
render: (c) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Stack, { space: 1, children: [
|
|
2546
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, children: c.title }),
|
|
2547
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 0, muted: true, children: c.sentAt.slice(0, 10) })
|
|
2548
|
-
] })
|
|
2549
|
-
},
|
|
2550
|
-
{ key: "sent", label: "Sent", numeric: true, sortValue: (c) => c.sent, render: (c) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, children: formatCount(c.sent) }) },
|
|
2551
|
-
// No Opens column. The caption above the table told the reader, in the tool's
|
|
2552
|
-
// own words, to "sort on clicks, not opens" because Apple Mail Privacy
|
|
2553
|
-
// Protection fetches images on the recipient's behalf. A column whose own
|
|
2554
|
-
// caption instructs you not to read it should not be a column — and the
|
|
2555
|
-
// warning gets shorter as a result.
|
|
2556
|
-
{ key: "clicks", label: "Clicks", numeric: true, sortValue: (c) => c.clicks, render: (c) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, children: formatCount(c.clicks) }) },
|
|
2557
|
-
{
|
|
2558
|
-
key: "unsub",
|
|
2559
|
-
label: "Unsubscribed",
|
|
2560
|
-
numeric: true,
|
|
2561
|
-
sortValue: (c) => c.unsubscribed,
|
|
2562
|
-
render: (c) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, muted: true, children: formatCount(c.unsubscribed) })
|
|
2563
|
-
},
|
|
2564
|
-
{
|
|
2565
|
-
key: "ordersAfter",
|
|
2566
|
-
label: "Orders after",
|
|
2567
|
-
numeric: true,
|
|
2568
|
-
sortValue: (c) => c.ordersAfter ?? null,
|
|
2569
|
-
exportValue: (c) => c.ordersAfter ?? null,
|
|
2570
|
-
render: (c) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Text, { size: 1, children: [
|
|
2571
|
-
c.ordersAfter === null || c.ordersAfter === void 0 ? "\u2014" : formatCount(c.ordersAfter),
|
|
2572
|
-
c.windowComplete === false && c.ordersAfter !== null && c.ordersAfter !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { as: "span", size: 0, muted: true, children: " so far" })
|
|
2573
|
-
] })
|
|
2574
|
-
},
|
|
2516
|
+
(data.campaigns?.length ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
2517
|
+
Section,
|
|
2518
|
+
{
|
|
2519
|
+
title: "Email campaigns",
|
|
2520
|
+
subtitle: "What your order book recorded after each send \u2014 what happened next, not what the send caused.",
|
|
2521
|
+
children: [
|
|
2522
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, muted: true, children: "Clicks are distinct subscribers. Opens are not shown: Apple Mail fetches images on the recipient\u2019s behalf, so an open is often the mail client rather than a person." }),
|
|
2523
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Text, { size: 1, muted: true, children: [
|
|
2524
|
+
"The last two columns count what your order book records in the ",
|
|
2525
|
+
SEND_WINDOW_DAYS,
|
|
2526
|
+
" days after each send, ending early if another send lands first. They are what happened next, not what the send caused \u2014 but unlike anything GA4 can tell you about email, both the send times and the order times are exact."
|
|
2527
|
+
] }),
|
|
2528
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2529
|
+
SortableTable,
|
|
2575
2530
|
{
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
formatMoney(c.revenueAfter, data.currency ?? null)
|
|
2531
|
+
caption: "Email campaigns by clicks",
|
|
2532
|
+
initialSort: "clicks",
|
|
2533
|
+
rows: data.campaigns ?? [],
|
|
2534
|
+
rowKey: (c) => `${c.sentAt}-${c.title}`,
|
|
2535
|
+
filterOn: (c) => `${c.title} ${c.subject}`,
|
|
2536
|
+
filterPlaceholder: "Filter campaigns",
|
|
2537
|
+
exportName: "email-campaigns",
|
|
2538
|
+
columns: [
|
|
2539
|
+
{
|
|
2540
|
+
key: "title",
|
|
2541
|
+
label: "Campaign",
|
|
2542
|
+
sortValue: (c) => c.title,
|
|
2543
|
+
render: (c) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Stack, { space: 1, children: [
|
|
2544
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, children: c.title }),
|
|
2545
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 0, muted: true, children: c.sentAt.slice(0, 10) })
|
|
2592
2546
|
] })
|
|
2593
|
-
|
|
2594
|
-
|
|
2547
|
+
},
|
|
2548
|
+
{ key: "sent", label: "Sent", numeric: true, sortValue: (c) => c.sent, render: (c) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, children: formatCount(c.sent) }) },
|
|
2549
|
+
// No Opens column. The caption above the table told the reader, in the tool's
|
|
2550
|
+
// own words, to "sort on clicks, not opens" because Apple Mail Privacy
|
|
2551
|
+
// Protection fetches images on the recipient's behalf. A column whose own
|
|
2552
|
+
// caption instructs you not to read it should not be a column — and the
|
|
2553
|
+
// warning gets shorter as a result.
|
|
2554
|
+
{ key: "clicks", label: "Clicks", numeric: true, sortValue: (c) => c.clicks, render: (c) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, children: formatCount(c.clicks) }) },
|
|
2555
|
+
{
|
|
2556
|
+
key: "unsub",
|
|
2557
|
+
label: "Unsubscribed",
|
|
2558
|
+
numeric: true,
|
|
2559
|
+
sortValue: (c) => c.unsubscribed,
|
|
2560
|
+
render: (c) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, muted: true, children: formatCount(c.unsubscribed) })
|
|
2561
|
+
},
|
|
2562
|
+
{
|
|
2563
|
+
key: "ordersAfter",
|
|
2564
|
+
label: "Orders after",
|
|
2565
|
+
numeric: true,
|
|
2566
|
+
sortValue: (c) => c.ordersAfter ?? null,
|
|
2567
|
+
exportValue: (c) => c.ordersAfter ?? null,
|
|
2568
|
+
render: (c) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Text, { size: 1, children: [
|
|
2569
|
+
c.ordersAfter === null || c.ordersAfter === void 0 ? "\u2014" : formatCount(c.ordersAfter),
|
|
2570
|
+
c.windowComplete === false && c.ordersAfter !== null && c.ordersAfter !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { as: "span", size: 0, muted: true, children: " so far" })
|
|
2571
|
+
] })
|
|
2572
|
+
},
|
|
2573
|
+
{
|
|
2574
|
+
key: "revenuePerThousand",
|
|
2575
|
+
label: "Per 1,000 sent",
|
|
2576
|
+
numeric: true,
|
|
2577
|
+
// Per thousand, not per send: a foundry's sends run to thousands of addresses
|
|
2578
|
+
// against a handful of orders, so per-send lands at fractions of a cent and
|
|
2579
|
+
// every campaign renders as the same rounded zero.
|
|
2580
|
+
sortValue: (c) => revenuePerThousandSent(c),
|
|
2581
|
+
exportValue: (c) => revenuePerThousandSent(c),
|
|
2582
|
+
render: (c) => {
|
|
2583
|
+
const value = revenuePerThousandSent(c);
|
|
2584
|
+
if (value === null) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, children: "\u2014" });
|
|
2585
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Stack, { space: 1, children: [
|
|
2586
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, children: formatMoney(value, data.currency ?? null) }),
|
|
2587
|
+
c.revenueAfter !== null && c.revenueAfter !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Text, { size: 0, muted: true, children: [
|
|
2588
|
+
"from ",
|
|
2589
|
+
formatMoney(c.revenueAfter, data.currency ?? null)
|
|
2590
|
+
] })
|
|
2591
|
+
] });
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
]
|
|
2595
2595
|
}
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2596
|
+
)
|
|
2597
|
+
]
|
|
2598
|
+
}
|
|
2599
|
+
)
|
|
2600
2600
|
] });
|
|
2601
2601
|
}
|
|
2602
2602
|
function Verdict({ data, previous }) {
|
|
@@ -2790,56 +2790,15 @@ function DataHealthPanel({ data, diagnostics }) {
|
|
|
2790
2790
|
] });
|
|
2791
2791
|
}
|
|
2792
2792
|
function AcquisitionPanel({ data, previous }) {
|
|
2793
|
-
const designShare = finiteOrNull(data.designIndustryShare);
|
|
2794
|
-
const unattributedShare = finiteOrNull(data.unattributedShare);
|
|
2795
2793
|
const sessions = finiteOrNull(data.totalSessions);
|
|
2796
2794
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: panelStack, children: [
|
|
2797
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.
|
|
2798
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.
|
|
2799
|
-
|
|
2800
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
] }) }),
|
|
2805
|
-
designShare !== null && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Card, { padding: 3, radius: 2, tone: "transparent", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: stackBlock, children: [
|
|
2806
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Label, { size: 1, muted: true, children: "From design-industry referrers" }),
|
|
2807
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: figureRow, children: [
|
|
2808
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Text, { size: 4, children: [
|
|
2809
|
-
data.rowsTruncated ? "at least " : "",
|
|
2810
|
-
formatPercent(designShare, 1)
|
|
2811
|
-
] }),
|
|
2812
|
-
!data.rowsTruncated && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2813
|
-
Delta,
|
|
2814
|
-
{
|
|
2815
|
-
current: designShare * 100,
|
|
2816
|
-
previous: finiteOrNull(previous?.designIndustryShare) !== null ? previous.designIndustryShare * 100 : null,
|
|
2817
|
-
unit: "percent"
|
|
2818
|
-
}
|
|
2819
|
-
)
|
|
2820
|
-
] }),
|
|
2821
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Text, { size: 0, muted: true, children: [
|
|
2822
|
-
"Share of sessions from a known design-press referrer.",
|
|
2823
|
-
data.rowsTruncated && " GA4 held more sources than are listed here, so the real share is higher."
|
|
2824
|
-
] })
|
|
2825
|
-
] }) }),
|
|
2826
|
-
unattributedShare !== null && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Card, { padding: 3, radius: 2, tone: "transparent", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: stackBlock, children: [
|
|
2827
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Label, { size: 1, muted: true, children: "No source" }),
|
|
2828
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: figureRow, children: [
|
|
2829
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 4, children: formatPercent(unattributedShare, 1) }),
|
|
2830
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2831
|
-
Delta,
|
|
2832
|
-
{
|
|
2833
|
-
current: unattributedShare * 100,
|
|
2834
|
-
previous: finiteOrNull(previous?.unattributedShare) !== null ? previous.unattributedShare * 100 : null,
|
|
2835
|
-
riseIsGood: false,
|
|
2836
|
-
unit: "percent"
|
|
2837
|
-
}
|
|
2838
|
-
)
|
|
2839
|
-
] }),
|
|
2840
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 0, muted: true, children: "Direct visits plus rows GA4 could not attribute." })
|
|
2841
|
-
] }) })
|
|
2842
|
-
] }),
|
|
2795
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: cardGrid, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Card, { padding: 3, radius: 2, tone: "transparent", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: stackBlock, children: [
|
|
2796
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Label, { size: 1, muted: true, children: "Sessions" }),
|
|
2797
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: figureRow, children: [
|
|
2798
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 4, children: sessions === null ? "\u2014" : formatCount(sessions) }),
|
|
2799
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Delta, { current: sessions, previous: finiteOrNull(previous?.totalSessions) })
|
|
2800
|
+
] })
|
|
2801
|
+
] }) }) }),
|
|
2843
2802
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: stackBlock, children: [
|
|
2844
2803
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SectionTitle, { title: "Traffic sources" }),
|
|
2845
2804
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, muted: true, children: "Sort or filter to find a source. Excluding a row hides it from this table." }),
|
|
@@ -2981,7 +2940,16 @@ function AcquisitionPanel({ data, previous }) {
|
|
|
2981
2940
|
" of those sales; the rest came from sources outside this list, so this column does not add up to the whole."
|
|
2982
2941
|
] })
|
|
2983
2942
|
] })
|
|
2984
|
-
] })
|
|
2943
|
+
] }),
|
|
2944
|
+
(data.topLandingPages?.length ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2945
|
+
Section,
|
|
2946
|
+
{
|
|
2947
|
+
title: "Where sessions began",
|
|
2948
|
+
tone: "secondary",
|
|
2949
|
+
subtitle: "The page a visit started on, busiest first.",
|
|
2950
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(LandingPagesTable, { rows: data.topLandingPages ?? [] })
|
|
2951
|
+
}
|
|
2952
|
+
)
|
|
2985
2953
|
] });
|
|
2986
2954
|
}
|
|
2987
2955
|
function firstStepRate(steps) {
|
|
@@ -3085,23 +3053,20 @@ function JourneyPanel({ data }) {
|
|
|
3085
3053
|
]
|
|
3086
3054
|
}
|
|
3087
3055
|
),
|
|
3088
|
-
spread(segments) && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, children: spread(segments) }),
|
|
3089
3056
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FunnelChart, { stages, measurement: data.measurement ?? "independent-totals" }),
|
|
3090
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
(data.topLandingPages?.length ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Section, { title: "Where sessions began", subtitle: "The page a visit started on, busiest first.", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(LandingPagesTable, { rows: data.topLandingPages ?? [] }) })
|
|
3104
|
-
] }),
|
|
3057
|
+
spread(segments) && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, children: spread(segments) }),
|
|
3058
|
+
(data.outcomes?.length ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3059
|
+
Section,
|
|
3060
|
+
{
|
|
3061
|
+
title: "Other outcomes",
|
|
3062
|
+
subtitle: "Successful outcomes that are not a licence sale, counted over the same window as the funnel but not a step within it.",
|
|
3063
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: cardGrid, children: (data.outcomes ?? []).map((outcome) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Card, { padding: 3, radius: 2, tone: "transparent", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: stackBlock, children: [
|
|
3064
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Label, { size: 1, muted: true, children: outcome.label }),
|
|
3065
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MetricFigure, { metric: outcome.count, label: outcome.label, size: 4 }),
|
|
3066
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 0, muted: true, children: outcome.note })
|
|
3067
|
+
] }) }, outcome.key)) })
|
|
3068
|
+
}
|
|
3069
|
+
),
|
|
3105
3070
|
hiddenSteps.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Text, { size: 1, muted: true, children: [
|
|
3106
3071
|
"Not shown: ",
|
|
3107
3072
|
hiddenSteps.map((step) => step.label.toLowerCase()).join(", "),
|
|
@@ -3508,9 +3473,8 @@ var GA4_ONLY_TABS = ["acquisition", "journey", "typeface-interest"];
|
|
|
3508
3473
|
var PANELS = [
|
|
3509
3474
|
{ id: "overview", report: "measurement-health", label: "Overview" },
|
|
3510
3475
|
{ id: "acquisition", report: "acquisition", label: "Acquisition" },
|
|
3511
|
-
{ id: "journey", report: "journey", label: "Journey" },
|
|
3512
3476
|
{ id: "typeface-interest", report: "typeface-interest", label: "Typeface interest" },
|
|
3513
|
-
{ id: "
|
|
3477
|
+
{ id: "journey", report: "journey", label: "Journey" }
|
|
3514
3478
|
];
|
|
3515
3479
|
function RangeSelector({
|
|
3516
3480
|
value,
|
|
@@ -3691,7 +3655,7 @@ function PanelTabs({ value, onChange }) {
|
|
|
3691
3655
|
}
|
|
3692
3656
|
function CoverageRibbon({ ratio }) {
|
|
3693
3657
|
if (ratio === null) {
|
|
3694
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_sanity_ui_compat4.Text, { size: 0, muted: true, children: "
|
|
3658
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_sanity_ui_compat4.Text, { size: 0, muted: true, children: "Figures marked (GA4) on this tab come from Google Analytics. How much of your traffic it is seeing has not been checked for this window \u2014 open Overview to measure it." });
|
|
3695
3659
|
}
|
|
3696
3660
|
if (ratio < 0.2) return null;
|
|
3697
3661
|
const seen = Math.round((1 - ratio) * 100);
|
|
@@ -3730,20 +3694,32 @@ function ReadyReport({
|
|
|
3730
3694
|
envelope.comparison.provisional && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: " This window\u2019s last days are still being processed by GA4, so changes read low." })
|
|
3731
3695
|
] }),
|
|
3732
3696
|
GA4_ONLY_TABS.includes(tabId) && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CoverageRibbon, { ratio: knownShortfall(apiBaseUrl, range, range === "custom" ? custom : void 0) }),
|
|
3733
|
-
tabId === "overview" && /* @__PURE__ */ (0, import_jsx_runtime4.
|
|
3734
|
-
|
|
3735
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.
|
|
3736
|
-
|
|
3697
|
+
tabId === "overview" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: panelStack, children: [
|
|
3698
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(OverviewPanel, { data: envelope.data, previous: envelope.comparison?.data, onBrush }),
|
|
3699
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
3700
|
+
Section,
|
|
3737
3701
|
{
|
|
3738
|
-
|
|
3739
|
-
|
|
3702
|
+
title: "Can I trust these figures",
|
|
3703
|
+
tone: "secondary",
|
|
3704
|
+
subtitle: trustSubtitle(envelope.data),
|
|
3705
|
+
collapsible: true,
|
|
3706
|
+
defaultOpen: false,
|
|
3707
|
+
children: [
|
|
3708
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3709
|
+
DataHealthPanel,
|
|
3710
|
+
{
|
|
3711
|
+
data: envelope.data,
|
|
3712
|
+
diagnostics: diagnostics.status === "ready" ? diagnostics.envelope.data : void 0
|
|
3713
|
+
}
|
|
3714
|
+
),
|
|
3715
|
+
diagnostics.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_sanity_ui_compat4.Text, { size: 1, muted: true, children: "Checking configuration\u2026" }),
|
|
3716
|
+
diagnostics.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_sanity_ui_compat4.Card, { padding: 3, radius: 2, tone: "caution", border: true, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_sanity_ui_compat4.Text, { size: 1, children: [
|
|
3717
|
+
"Configuration checks could not run: ",
|
|
3718
|
+
diagnostics.message
|
|
3719
|
+
] }) })
|
|
3720
|
+
]
|
|
3740
3721
|
}
|
|
3741
|
-
)
|
|
3742
|
-
diagnostics.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_sanity_ui_compat4.Text, { size: 1, muted: true, children: "Checking configuration\u2026" }),
|
|
3743
|
-
diagnostics.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_sanity_ui_compat4.Card, { padding: 3, radius: 2, tone: "caution", border: true, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_sanity_ui_compat4.Text, { size: 1, children: [
|
|
3744
|
-
"Configuration checks could not run: ",
|
|
3745
|
-
diagnostics.message
|
|
3746
|
-
] }) })
|
|
3722
|
+
)
|
|
3747
3723
|
] }),
|
|
3748
3724
|
tabId === "acquisition" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AcquisitionPanel, { data: envelope.data, previous: envelope.comparison?.data }),
|
|
3749
3725
|
tabId === "journey" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(JourneyPanel, { data: envelope.data }),
|
|
@@ -3789,6 +3765,14 @@ function BasisSelector({
|
|
|
3789
3765
|
)
|
|
3790
3766
|
] });
|
|
3791
3767
|
}
|
|
3768
|
+
function trustSubtitle(data) {
|
|
3769
|
+
const shortfall = data?.shortfallRatio;
|
|
3770
|
+
if (shortfall === null || shortfall === void 0 || !Number.isFinite(shortfall)) {
|
|
3771
|
+
return "How much of your traffic Google Analytics is seeing, and what it is missing.";
|
|
3772
|
+
}
|
|
3773
|
+
const seeing = Math.round((1 - shortfall) * 100);
|
|
3774
|
+
return shortfall >= 0 ? `Google Analytics is seeing about ${seeing}% of your traffic. Open for how that was measured.` : "Google Analytics is counting more than your server does. Open for how that was measured.";
|
|
3775
|
+
}
|
|
3792
3776
|
function ReportPanel({
|
|
3793
3777
|
tabId,
|
|
3794
3778
|
report,
|
|
@@ -3803,7 +3787,7 @@ function ReportPanel({
|
|
|
3803
3787
|
apiBaseUrl,
|
|
3804
3788
|
report: "diagnostics",
|
|
3805
3789
|
range: "week",
|
|
3806
|
-
enabled: tabId === "
|
|
3790
|
+
enabled: tabId === "overview"
|
|
3807
3791
|
}).state;
|
|
3808
3792
|
const liveMessage = state.status === "loading" ? "Loading report" : state.status === "ready" ? `${PANELS.find((p) => p.id === tabId)?.label ?? tabId} updated for ${range === "custom" ? `${custom.start} to ${custom.end}` : `the selected ${range}`}` : state.status === "error" ? state.disabled ? "Visitor insights is switched off for this site" : `Report failed: ${state.message}` : "";
|
|
3809
3793
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_sanity_ui_compat4.Stack, { space: 4, children: [
|