@liiift-studio/sanity-visitor-insights 0.64.0 → 0.65.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 +94 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +94 -13
- package/dist/index.mjs.map +1 -1
- package/dist/server.js.map +1 -1
- package/dist/server.mjs.map +1 -1
- package/package.json +1 -1
- package/src/core/capture.ts +1 -1
- package/src/studio/Figure.tsx +136 -0
- package/src/studio/palette.ts +3 -0
- package/src/studio/panels.test.tsx +68 -1
- package/src/studio/panels.tsx +15 -30
package/dist/index.js
CHANGED
|
@@ -307,7 +307,10 @@ var MARKS = {
|
|
|
307
307
|
"bar.fill": { key: "vercel", alpha: 1, role: "data", note: "Every proportion, comparison and funnel bar" },
|
|
308
308
|
"bar.track": { key: "neutral", alpha: 0.12, role: "furniture", note: "The rail a bar sits in" },
|
|
309
309
|
"bar.lost": { key: "revenue", alpha: 1, role: "data", note: "People who did not continue past a funnel rung" },
|
|
310
|
-
"bar.seen": { key: "ga4Pageviews", alpha: 1, role: "data", note: "The share of a complete count that the lossy source saw" }
|
|
310
|
+
"bar.seen": { key: "ga4Pageviews", alpha: 1, role: "data", note: "The share of a complete count that the lossy source saw" },
|
|
311
|
+
"estimate.dot": { key: "ga4Pageviews", alpha: 1, role: "data", note: "One independent estimate of how much GA4 sees" },
|
|
312
|
+
"estimate.interval": { key: "ga4Pageviews", alpha: 0.35, role: "fill", boundary: "estimate.dot", note: "How wide the sample leaves that estimate \u2014 the dot is its own boundary" },
|
|
313
|
+
"estimate.rule": { key: "neutral", alpha: 0.45, role: "furniture", note: "The 100% reference a capture rate is read against" }
|
|
311
314
|
};
|
|
312
315
|
function mark(name) {
|
|
313
316
|
const m = MARKS[name];
|
|
@@ -846,6 +849,50 @@ function ContainmentBar({
|
|
|
846
849
|
] })
|
|
847
850
|
] });
|
|
848
851
|
}
|
|
852
|
+
function EstimateDotPlot({
|
|
853
|
+
estimates,
|
|
854
|
+
labelFor,
|
|
855
|
+
intervalFor
|
|
856
|
+
}) {
|
|
857
|
+
if (estimates.length === 0) return null;
|
|
858
|
+
const intervals = estimates.map((e) => intervalFor(e));
|
|
859
|
+
const top = Math.max(1, ...estimates.map((e) => e.rate), ...intervals.map((i) => i.high));
|
|
860
|
+
const pct = (v) => `${Math.max(0, Math.min(1, v / top)) * 100}%`;
|
|
861
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_sanity_ui_compat.Stack, { space: 3, children: [
|
|
862
|
+
estimates.map((estimate, index) => {
|
|
863
|
+
const interval = intervals[index];
|
|
864
|
+
const wide = interval.high - interval.low > 0.4;
|
|
865
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_sanity_ui_compat.Stack, { space: 2, children: [
|
|
866
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: barHeader, children: [
|
|
867
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sanity_ui_compat.Text, { size: 1, children: labelFor(estimate.basis) }),
|
|
868
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sanity_ui_compat.Text, { size: 1, weight: "medium", children: formatPercent(estimate.rate, 0) })
|
|
869
|
+
] }),
|
|
870
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "aria-hidden": "true", style: estimateTrack, children: [
|
|
871
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...estimateRule, left: pct(1) } }),
|
|
872
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
873
|
+
"div",
|
|
874
|
+
{
|
|
875
|
+
style: {
|
|
876
|
+
...estimateInterval,
|
|
877
|
+
left: pct(interval.low),
|
|
878
|
+
width: `calc(${pct(interval.high)} - ${pct(interval.low)})`
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
),
|
|
882
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...estimateDot, left: pct(estimate.rate) } })
|
|
883
|
+
] }),
|
|
884
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_sanity_ui_compat.Text, { size: 0, muted: true, children: [
|
|
885
|
+
formatCount(estimate.observed),
|
|
886
|
+
" of ",
|
|
887
|
+
formatCount(estimate.actual),
|
|
888
|
+
wide && " \u2014 too small a sample to lean on",
|
|
889
|
+
estimate.rate > 1.05 && " \u2014 over 100%, so GA4 is counting more than the source it is checked against. That is usually a tag firing twice, not extra traffic."
|
|
890
|
+
] })
|
|
891
|
+
] }, estimate.basis);
|
|
892
|
+
}),
|
|
893
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sanity_ui_compat.Text, { size: 0, muted: true, children: "Each is a separate way of asking the same question. Where they disagree, the spread is the answer\u2019s uncertainty \u2014 the rule marks 100%." })
|
|
894
|
+
] });
|
|
895
|
+
}
|
|
849
896
|
function SortableTable({
|
|
850
897
|
caption,
|
|
851
898
|
columns,
|
|
@@ -1063,6 +1110,35 @@ var splitGrid = {
|
|
|
1063
1110
|
gap: 24,
|
|
1064
1111
|
alignItems: "start"
|
|
1065
1112
|
};
|
|
1113
|
+
var estimateTrack = {
|
|
1114
|
+
position: "relative",
|
|
1115
|
+
height: 18,
|
|
1116
|
+
borderRadius: 2,
|
|
1117
|
+
background: mark("bar.track")
|
|
1118
|
+
};
|
|
1119
|
+
var estimateRule = {
|
|
1120
|
+
position: "absolute",
|
|
1121
|
+
top: 0,
|
|
1122
|
+
bottom: 0,
|
|
1123
|
+
width: 1,
|
|
1124
|
+
background: mark("estimate.rule")
|
|
1125
|
+
};
|
|
1126
|
+
var estimateInterval = {
|
|
1127
|
+
position: "absolute",
|
|
1128
|
+
top: 6,
|
|
1129
|
+
height: 6,
|
|
1130
|
+
borderRadius: 3,
|
|
1131
|
+
background: mark("estimate.interval")
|
|
1132
|
+
};
|
|
1133
|
+
var estimateDot = {
|
|
1134
|
+
position: "absolute",
|
|
1135
|
+
top: 4,
|
|
1136
|
+
width: 10,
|
|
1137
|
+
height: 10,
|
|
1138
|
+
marginLeft: -5,
|
|
1139
|
+
borderRadius: "50%",
|
|
1140
|
+
background: mark("estimate.dot")
|
|
1141
|
+
};
|
|
1066
1142
|
var filterInput = {
|
|
1067
1143
|
font: "inherit",
|
|
1068
1144
|
fontSize: "0.85em",
|
|
@@ -1884,6 +1960,15 @@ function describeRank(rank) {
|
|
|
1884
1960
|
return `${ordinal(rank.rank)} best of the last ${rank.of} weeks`;
|
|
1885
1961
|
}
|
|
1886
1962
|
|
|
1963
|
+
// src/core/capture.ts
|
|
1964
|
+
function samplingInterval(estimate) {
|
|
1965
|
+
const n = estimate.actual;
|
|
1966
|
+
if (!Number.isFinite(n) || n <= 0) return { low: estimate.rate, high: estimate.rate };
|
|
1967
|
+
const p = Math.min(1, Math.max(0, estimate.rate));
|
|
1968
|
+
const error = 2 * Math.sqrt(Math.max(p * (1 - p), 0.02) / n);
|
|
1969
|
+
return { low: Math.max(0, estimate.rate - error), high: estimate.rate + error };
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1887
1972
|
// src/studio/panels.tsx
|
|
1888
1973
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1889
1974
|
function metricSortValue(metric) {
|
|
@@ -2387,18 +2472,14 @@ function DataHealthPanel({ data, diagnostics }) {
|
|
|
2387
2472
|
),
|
|
2388
2473
|
((data.capture?.estimates?.length ?? 0) > 0 || data.estimatedSessions?.status === "estimated") && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Stack, { space: 3, children: [
|
|
2389
2474
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SectionTitle, { title: "How much GA4 is seeing" }),
|
|
2390
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
". ",
|
|
2399
|
-
estimate.note
|
|
2400
|
-
] })
|
|
2401
|
-
] }) }, estimate.basis)) }),
|
|
2475
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2476
|
+
EstimateDotPlot,
|
|
2477
|
+
{
|
|
2478
|
+
estimates: data.capture?.estimates ?? [],
|
|
2479
|
+
labelFor: (basis) => basis === "orders" ? "Checked against your orders" : basis === "email" ? "Checked against email clicks" : "Checked against Vercel",
|
|
2480
|
+
intervalFor: samplingInterval
|
|
2481
|
+
}
|
|
2482
|
+
),
|
|
2402
2483
|
data.capture?.discrepancy && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Card, { padding: 3, radius: 2, tone: "caution", border: true, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Stack, { space: 2, children: [
|
|
2403
2484
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, weight: "medium", children: "The sources disagree, and that is informative" }),
|
|
2404
2485
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, children: data.capture.discrepancy })
|