@liiift-studio/sanity-visitor-insights 0.57.0 → 0.58.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 CHANGED
@@ -306,7 +306,8 @@ var MARKS = {
306
306
  "chart.grid": { key: "neutral", alpha: 0.18, role: "furniture", note: "Grid rules" },
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
- "bar.lost": { key: "revenue", alpha: 1, role: "data", note: "People who did not continue past a funnel rung" }
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
311
  };
311
312
  function mark(name) {
312
313
  const m = MARKS[name];
@@ -315,6 +316,33 @@ function mark(name) {
315
316
  return seriesFill(m.key, m.alpha);
316
317
  }
317
318
 
319
+ // src/types.ts
320
+ function estimated(value, low, high, basis) {
321
+ return { status: "estimated", value, low, high, basis };
322
+ }
323
+ function ok(value) {
324
+ return { status: "ok", value };
325
+ }
326
+ function unavailable(reason, detail) {
327
+ return { status: "unavailable", reason, detail };
328
+ }
329
+ function partial(value, coveredFrom, note) {
330
+ return { status: "partial", value, coveredFrom, note };
331
+ }
332
+ function valueOrNull(metric) {
333
+ return metric.status === "unavailable" ? null : metric.value;
334
+ }
335
+ var REPORT_NAMES = [
336
+ "measurement-health",
337
+ "acquisition",
338
+ "journey",
339
+ "typeface-interest",
340
+ "diagnostics"
341
+ ];
342
+ function isReportName(value) {
343
+ return typeof value === "string" && REPORT_NAMES.includes(value);
344
+ }
345
+
318
346
  // src/studio/Figure.tsx
319
347
  var import_jsx_runtime = require("react/jsx-runtime");
320
348
  var REASON_TEXT = {
@@ -773,6 +801,47 @@ function SectionTitle({
773
801
  }) {
774
802
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: sectionHeader, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sanity_ui_compat.Heading, { size: tone === "primary" ? 1 : 0, style: tone === "primary" ? primaryTitle : secondaryTitle, children: title }) });
775
803
  }
804
+ function isContainment(whole, part) {
805
+ const w = valueOrNull(whole);
806
+ const p = valueOrNull(part);
807
+ return w !== null && p !== null && w > 0 && p <= w;
808
+ }
809
+ function ContainmentBar({
810
+ wholeLabel,
811
+ whole,
812
+ partLabel,
813
+ part,
814
+ missingLabel
815
+ }) {
816
+ const wholeValue = valueOrNull(whole);
817
+ const partValue = valueOrNull(part);
818
+ if (!isContainment(whole, part)) return null;
819
+ if (wholeValue === null || partValue === null) return null;
820
+ const share = partValue / wholeValue;
821
+ const missing = wholeValue - partValue;
822
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_sanity_ui_compat.Stack, { space: 2, children: [
823
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: barHeader, children: [
824
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sanity_ui_compat.Text, { size: 1, children: wholeLabel }),
825
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sanity_ui_compat.Text, { size: 1, weight: "medium", children: formatCount(wholeValue) })
826
+ ] }),
827
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "aria-hidden": "true", style: { ...barTrack, background: mark("bar.fill"), height: 14 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { height: "100%", width: `${share * 100}%`, background: mark("bar.seen"), borderRadius: 2 } }) }),
828
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: barHeader, children: [
829
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_sanity_ui_compat.Text, { size: 0, muted: true, children: [
830
+ partLabel,
831
+ ": ",
832
+ formatCount(partValue),
833
+ " (",
834
+ formatPercent(share, 0),
835
+ ")"
836
+ ] }),
837
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_sanity_ui_compat.Text, { size: 0, muted: true, children: [
838
+ formatCount(missing),
839
+ " ",
840
+ missingLabel
841
+ ] })
842
+ ] })
843
+ ] });
844
+ }
776
845
  function SortableTable({
777
846
  caption,
778
847
  columns,
@@ -1812,33 +1881,6 @@ function describeRank(rank) {
1812
1881
  return `${ordinal(rank.rank)} best of the last ${rank.of} weeks`;
1813
1882
  }
1814
1883
 
1815
- // src/types.ts
1816
- function estimated(value, low, high, basis) {
1817
- return { status: "estimated", value, low, high, basis };
1818
- }
1819
- function ok(value) {
1820
- return { status: "ok", value };
1821
- }
1822
- function unavailable(reason, detail) {
1823
- return { status: "unavailable", reason, detail };
1824
- }
1825
- function partial(value, coveredFrom, note) {
1826
- return { status: "partial", value, coveredFrom, note };
1827
- }
1828
- function valueOrNull(metric) {
1829
- return metric.status === "unavailable" ? null : metric.value;
1830
- }
1831
- var REPORT_NAMES = [
1832
- "measurement-health",
1833
- "acquisition",
1834
- "journey",
1835
- "typeface-interest",
1836
- "diagnostics"
1837
- ];
1838
- function isReportName(value) {
1839
- return typeof value === "string" && REPORT_NAMES.includes(value);
1840
- }
1841
-
1842
1884
  // src/studio/panels.tsx
1843
1885
  var import_jsx_runtime3 = require("react/jsx-runtime");
1844
1886
  function metricSortValue(metric) {
@@ -2269,12 +2311,24 @@ function Verdict({ data, previous }) {
2269
2311
  }
2270
2312
  function DataHealthPanel({ data, diagnostics }) {
2271
2313
  const pageviewMax = maxOf([data.ga4Pageviews, data.vercelPageviews]);
2314
+ const contained = isContainment(data.vercelPageviews, data.ga4Pageviews);
2272
2315
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Stack, { space: 4, children: [
2273
2316
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Stack, { space: 3, children: [
2274
2317
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SectionTitle, { title: "Pageviews, source against source" }),
2275
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, muted: true, children: "The same unit on both sides. Vercel is cookieless and ungated; GA4 is consent-gated and blockable, so GA4 seeing fewer is expected." }),
2276
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ComparisonBar, { label: "Vercel pageviews", metric: data.vercelPageviews, max: pageviewMax, outOf: "Blocked less than GA4, but not immune to it." }),
2277
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ComparisonBar, { label: "GA4 pageviews", metric: data.ga4Pageviews, max: pageviewMax, outOf: "A subset of the bar above, not a rival measurement." })
2318
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, muted: true, children: "Both counting pageviews. Google Analytics is blockable, so seeing fewer is normal." }),
2319
+ contained ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2320
+ ContainmentBar,
2321
+ {
2322
+ wholeLabel: "Pageviews Vercel counted",
2323
+ whole: data.vercelPageviews,
2324
+ partLabel: "Seen by Google Analytics",
2325
+ part: data.ga4Pageviews,
2326
+ missingLabel: "it missed"
2327
+ }
2328
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
2329
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ComparisonBar, { label: "Vercel pageviews", metric: data.vercelPageviews, max: pageviewMax }),
2330
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ComparisonBar, { label: "GA4 pageviews", metric: data.ga4Pageviews, max: pageviewMax })
2331
+ ] })
2278
2332
  ] }),
2279
2333
  /* @__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: 2, children: [
2280
2334
  data.shortfallRatio !== null && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, weight: "semibold", children: data.shortfallRatio >= 0 ? `GA4 saw ${formatPercent(data.shortfallRatio, 1)} fewer pageviews than Vercel` : `GA4 saw ${formatPercent(-data.shortfallRatio, 1)} more pageviews than Vercel` }),