@liiift-studio/sanity-visitor-insights 0.60.0 → 0.62.0
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.js +41 -35
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +41 -35
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/studio/Figure.tsx +8 -4
- package/src/studio/VisitorInsightsTool.tsx +13 -8
- package/src/studio/panels.test.tsx +82 -9
- package/src/studio/panels.tsx +38 -10
package/dist/index.js
CHANGED
|
@@ -348,10 +348,14 @@ var import_jsx_runtime = require("react/jsx-runtime");
|
|
|
348
348
|
var REASON_TEXT = {
|
|
349
349
|
not_instrumented: "Not tracked on this site",
|
|
350
350
|
before_cutover: "Not tracked during this period",
|
|
351
|
-
|
|
351
|
+
// "Withheld ... for privacy" reads as a legal hold on the reader's own data. What actually
|
|
352
|
+
// happened is that too few people did the thing for Google to report it.
|
|
353
|
+
suppressed: "Google hid it: too few people",
|
|
352
354
|
outage: "Not recorded during part of this period",
|
|
353
355
|
source_error: "Source did not respond",
|
|
354
|
-
|
|
356
|
+
// "Does not apply" reads as a choice someone made. It is not — there is simply nothing here to
|
|
357
|
+
// compute it from.
|
|
358
|
+
not_applicable: "Nothing here to work this out from",
|
|
355
359
|
// Its own reason, because it was borrowing not_applicable — so a figure that is temporarily
|
|
356
360
|
// missing rendered "Does not apply to this site. This site's API route predates this figure."
|
|
357
361
|
// Two flatly contradictory sentences, and a hurried reader takes the first: this foundry has no
|
|
@@ -463,7 +467,7 @@ function MetricFigure({ metric, label, size = 4, unit = "count" }) {
|
|
|
463
467
|
metric.note
|
|
464
468
|
] })
|
|
465
469
|
] }),
|
|
466
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sanity_ui_compat.Badge, { tone: "caution", fontSize: 0, children: "
|
|
470
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sanity_ui_compat.Badge, { tone: "caution", fontSize: 0, children: "Some orders only" })
|
|
467
471
|
] });
|
|
468
472
|
}
|
|
469
473
|
if (metric.status === "estimated") {
|
|
@@ -735,7 +739,7 @@ function NoticeList({ notices }) {
|
|
|
735
739
|
const alwaysShown = notices.slice(0, 2);
|
|
736
740
|
const hidden = notices.slice(2);
|
|
737
741
|
const item = (notice) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sanity_ui_compat.Card, { padding: 3, radius: 2, tone: "caution", border: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: noticeRow, children: [
|
|
738
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: noticeBadge, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sanity_ui_compat.Badge, { tone: "caution", fontSize: 0, children: "
|
|
742
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: noticeBadge, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sanity_ui_compat.Badge, { tone: "caution", fontSize: 0, children: "Note" }) }),
|
|
739
743
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sanity_ui_compat.Text, { size: 1, children: notice })
|
|
740
744
|
] }) }) }, notice);
|
|
741
745
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_sanity_ui_compat.Stack, { space: 2, children: [
|
|
@@ -2066,7 +2070,7 @@ function OverviewPanel({ data, previous, onBrush }) {
|
|
|
2066
2070
|
*/
|
|
2067
2071
|
...(data.crossSource ?? []).some((d) => d.vercelPageviews !== null && d.ga4Pageviews !== null) ? [{
|
|
2068
2072
|
key: "coverage",
|
|
2069
|
-
label: "GA4
|
|
2073
|
+
label: "Share GA4 saw",
|
|
2070
2074
|
source: "GA4",
|
|
2071
2075
|
complete: true,
|
|
2072
2076
|
unit: "percent",
|
|
@@ -2163,7 +2167,7 @@ function OverviewPanel({ data, previous, onBrush }) {
|
|
|
2163
2167
|
* worst. Sorting on it is how a reader dates a collapse.
|
|
2164
2168
|
*/
|
|
2165
2169
|
key: "coverage",
|
|
2166
|
-
label: "GA4
|
|
2170
|
+
label: "Share GA4 saw",
|
|
2167
2171
|
numeric: true,
|
|
2168
2172
|
sortValue: (d) => coverageOf(d),
|
|
2169
2173
|
// The FORMATTED value, matching the cell. Passing the raw ratio through
|
|
@@ -2285,16 +2289,21 @@ function Verdict({ data, previous }) {
|
|
|
2285
2289
|
parts.push(Math.abs(change) < 5e-3 ? `${label} flat` : `${label} little changed (${change > 0 ? "+" : "\u2212"}${formatPercent(Math.abs(change), 0)})`);
|
|
2286
2290
|
} else if (unit === "money") {
|
|
2287
2291
|
parts.push(`${label} ${change > 0 ? "up" : "down"} to ${formatMoney(a, data.currency ?? null)} from ${formatMoney(b, data.currency ?? null)}`);
|
|
2292
|
+
} else if (Math.abs(b) < MIN_DELTA_BASE) {
|
|
2293
|
+
parts.push(`${label} ${formatCount(a)}, was ${formatCount(b)}`);
|
|
2288
2294
|
} else parts.push(`${label} ${change > 0 ? "up" : "down"} ${formatPercent(Math.abs(change), 0)}`);
|
|
2289
2295
|
};
|
|
2290
2296
|
say("Revenue", data.revenue, previous?.revenue, "money");
|
|
2297
|
+
say("Orders", data.orders, previous?.orders, "count");
|
|
2291
2298
|
say("Traffic", data.vercelPageviews, previous?.vercelPageviews, "count");
|
|
2292
2299
|
const sends = data.campaigns?.length ?? 0;
|
|
2293
2300
|
const shortfall = data.shortfallRatio;
|
|
2294
2301
|
const seeing = shortfall !== null && shortfall !== void 0 ? `GA4 is seeing ${formatPercent(1 - shortfall, 0)} of your traffic` : null;
|
|
2295
2302
|
const broken = shortfall === null || shortfall === void 0 ? data.capture?.discrepancy ? "measurement disagrees between sources" : null : shortfall > 0.6 ? `${seeing} \u2014 treat its figures as broken` : shortfall > 0.35 ? `${seeing} \u2014 its figures are undercounts, not what happened` : data.capture?.discrepancy ? "measurement disagrees between sources" : null;
|
|
2296
2303
|
if (broken) parts.push(broken);
|
|
2297
|
-
if (parts.length === 0)
|
|
2304
|
+
if (parts.length === 0) {
|
|
2305
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Card, { padding: 3, radius: 2, tone: "transparent", border: true, style: { borderLeftWidth: 3, borderLeftStyle: "solid" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 4, children: "No figures arrived for this window." }) });
|
|
2306
|
+
}
|
|
2298
2307
|
const noticeable = shortfall !== null && shortfall !== void 0 && shortfall > 0.2;
|
|
2299
2308
|
const closing = broken || !noticeable ? "" : ` \xB7 ${seeing}`;
|
|
2300
2309
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
@@ -2340,7 +2349,7 @@ function DataHealthPanel({ data, diagnostics }) {
|
|
|
2340
2349
|
((data.capture?.estimates?.length ?? 0) > 0 || data.estimatedSessions?.status === "estimated") && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Stack, { space: 3, children: [
|
|
2341
2350
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SectionTitle, { title: "How much GA4 is seeing" }),
|
|
2342
2351
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: cardGrid, children: (data.capture?.estimates ?? []).map((estimate) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Card, { padding: 3, radius: 2, tone: "transparent", border: true, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Stack, { space: 3, children: [
|
|
2343
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Label, { size: 1, muted: true, children: estimate.basis === "orders" ? "
|
|
2352
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Label, { size: 1, muted: true, children: estimate.basis === "orders" ? "Checked against your orders" : estimate.basis === "email" ? "Checked against email clicks" : "Checked against Vercel" }),
|
|
2344
2353
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 4, children: formatPercent(estimate.rate, 0) }),
|
|
2345
2354
|
estimate.rate > 1.05 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 0, children: "Above 100%: GA4 is counting more than the source it is measured against. That is usually a tag firing twice, not extra traffic." }),
|
|
2346
2355
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Text, { size: 0, muted: true, children: [
|
|
@@ -2444,7 +2453,7 @@ function AcquisitionPanel({ data, previous }) {
|
|
|
2444
2453
|
] })
|
|
2445
2454
|
] }) }),
|
|
2446
2455
|
unattributedShare !== null && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Card, { padding: 3, radius: 2, tone: "transparent", border: true, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Stack, { space: 3, children: [
|
|
2447
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Label, { size: 1, muted: true, children: "
|
|
2456
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Label, { size: 1, muted: true, children: "No source" }),
|
|
2448
2457
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: figureRow, children: [
|
|
2449
2458
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 4, children: formatPercent(unattributedShare, 1) }),
|
|
2450
2459
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
@@ -2495,7 +2504,7 @@ function AcquisitionPanel({ data, previous }) {
|
|
|
2495
2504
|
}
|
|
2496
2505
|
) : row.source }),
|
|
2497
2506
|
row.designIndustry && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Badge, { tone: "primary", fontSize: 0, children: "Design" }),
|
|
2498
|
-
row.unattributed && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Badge, { tone: "caution", fontSize: 0, children: "
|
|
2507
|
+
row.unattributed && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Badge, { tone: "caution", fontSize: 0, children: "No source" })
|
|
2499
2508
|
] })
|
|
2500
2509
|
},
|
|
2501
2510
|
{
|
|
@@ -2681,7 +2690,7 @@ function JourneyPanel({ data }) {
|
|
|
2681
2690
|
const tracked = data.measurement === "sequence";
|
|
2682
2691
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Stack, { space: 4, children: [
|
|
2683
2692
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Card, { padding: 3, radius: 2, tone: tracked ? "transparent" : "caution", border: true, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Stack, { space: 2, children: [
|
|
2684
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, weight: "medium", children: tracked ? "Tracked
|
|
2693
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, weight: "medium", children: tracked ? "Tracked path" : "Not a tracked path" }),
|
|
2685
2694
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, muted: tracked, children: data.approximationNote })
|
|
2686
2695
|
] }) }),
|
|
2687
2696
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SegmentTable, { segments, dimension: data.segmentDimension ?? "segment" }),
|
|
@@ -3081,11 +3090,11 @@ var RANGES = [
|
|
|
3081
3090
|
];
|
|
3082
3091
|
var GA4_ONLY_TABS = ["acquisition", "journey", "typeface-interest"];
|
|
3083
3092
|
var PANELS = [
|
|
3084
|
-
{ id: "overview", report: "measurement-health", label: "Overview"
|
|
3085
|
-
{ id: "acquisition", report: "acquisition", label: "Acquisition"
|
|
3086
|
-
{ id: "journey", report: "journey", label: "Journey"
|
|
3087
|
-
{ id: "typeface-interest", report: "typeface-interest", label: "Typeface interest"
|
|
3088
|
-
{ id: "data-health", report: "measurement-health", label: "Data health"
|
|
3093
|
+
{ id: "overview", report: "measurement-health", label: "Overview" },
|
|
3094
|
+
{ id: "acquisition", report: "acquisition", label: "Acquisition" },
|
|
3095
|
+
{ id: "journey", report: "journey", label: "Journey" },
|
|
3096
|
+
{ id: "typeface-interest", report: "typeface-interest", label: "Typeface interest" },
|
|
3097
|
+
{ id: "data-health", report: "measurement-health", label: "Data health" }
|
|
3089
3098
|
];
|
|
3090
3099
|
function RangeSelector({
|
|
3091
3100
|
value,
|
|
@@ -3491,7 +3500,7 @@ function VisitorInsightsTool(props) {
|
|
|
3491
3500
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_sanity_ui_compat4.Heading, { size: 2, children: "Visitor insights" }),
|
|
3492
3501
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_sanity_ui_compat4.Stack, { space: 2, children: [
|
|
3493
3502
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RangeSelector, { value: range, custom, onChange: setRange, onCustomChange: setCustom }),
|
|
3494
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(BasisSelector, { value: compare, onChange: setCompare })
|
|
3503
|
+
COMPARED_TABS.includes(activePanel) && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(BasisSelector, { value: compare, onChange: setCompare })
|
|
3495
3504
|
] })
|
|
3496
3505
|
] }),
|
|
3497
3506
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PanelTabs, { value: activePanel, onChange: setActivePanel }),
|
|
@@ -3551,25 +3560,22 @@ function VisitorInsightsTool(props) {
|
|
|
3551
3560
|
"aria-labelledby": `tab-${activePanel}`,
|
|
3552
3561
|
tabIndex: 0,
|
|
3553
3562
|
style: { position: "relative" },
|
|
3554
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime4.
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
setCustom({ start, end });
|
|
3568
|
-
setRange("custom");
|
|
3569
|
-
}
|
|
3563
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_sanity_ui_compat4.Stack, { space: 4, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PanelBoundary, { resetKey: `${activePanel}|${range}|${custom.start}|${custom.end}`, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3564
|
+
ReportPanel,
|
|
3565
|
+
{
|
|
3566
|
+
tabId: active?.id ?? "overview",
|
|
3567
|
+
report: active?.report ?? "measurement-health",
|
|
3568
|
+
apiBaseUrl,
|
|
3569
|
+
range,
|
|
3570
|
+
custom,
|
|
3571
|
+
compare,
|
|
3572
|
+
onBrush: (start, end) => {
|
|
3573
|
+
if (range !== "custom") setRangeBeforeBrush(range);
|
|
3574
|
+
setCustom({ start, end });
|
|
3575
|
+
setRange("custom");
|
|
3570
3576
|
}
|
|
3571
|
-
|
|
3572
|
-
|
|
3577
|
+
}
|
|
3578
|
+
) }) })
|
|
3573
3579
|
}
|
|
3574
3580
|
)
|
|
3575
3581
|
] }) })
|