@liiift-studio/sanity-visitor-insights 0.59.0 → 0.61.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.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-DBbZ0tg7.mjs';
4
- export { C as CaptureBasis, d as CaptureEstimate, e as CaptureModel, f as CheckStatus, g as Coverage, h as CrossSourceDay, i as DailyPoint, j as DateRange, k as DiagnosticCheck, E as EmailCampaign, l as EventCutover, m as JourneyOutcome, n as JourneySegment, o as JourneyStep, L as LandingPage, p as LicenceTierRow, P as PREEXISTING, q as REPORT_NAMES, r as ReportError, S as SiteAnalyticsConfig, s as SourceName, t as SourceRow, u as SourceStatus, v as TimelineEvent, w as TypefaceInterestRow, U as UnavailableReason, x as coverageForRange, y as estimated, z as isReportName, B as ok, F as partial, G as previousRange, H as resolveRange, I as unavailable, K as validateSiteConfig, N as valueOrNull } from './ranges-DBbZ0tg7.mjs';
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-CMADS44I.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-CMADS44I.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-DBbZ0tg7.js';
4
- export { C as CaptureBasis, d as CaptureEstimate, e as CaptureModel, f as CheckStatus, g as Coverage, h as CrossSourceDay, i as DailyPoint, j as DateRange, k as DiagnosticCheck, E as EmailCampaign, l as EventCutover, m as JourneyOutcome, n as JourneySegment, o as JourneyStep, L as LandingPage, p as LicenceTierRow, P as PREEXISTING, q as REPORT_NAMES, r as ReportError, S as SiteAnalyticsConfig, s as SourceName, t as SourceRow, u as SourceStatus, v as TimelineEvent, w as TypefaceInterestRow, U as UnavailableReason, x as coverageForRange, y as estimated, z as isReportName, B as ok, F as partial, G as previousRange, H as resolveRange, I as unavailable, K as validateSiteConfig, N as valueOrNull } from './ranges-DBbZ0tg7.js';
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-CMADS44I.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-CMADS44I.js';
5
5
 
6
6
  /**
7
7
  * Data hook for the Studio panels.
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
- suppressed: "Withheld by GA4 for privacy",
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
- not_applicable: "Does not apply to this site",
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: "Partial" })
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: "Caveat" }) }),
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 coverage",
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 coverage",
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
@@ -2199,7 +2203,7 @@ function OverviewPanel({ data, previous, onBrush }) {
2199
2203
  ] }),
2200
2204
  (data.campaigns?.length ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Stack, { space: 3, children: [
2201
2205
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SectionTitle, { title: "Email campaigns" }),
2202
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, muted: true, children: "Clicks are distinct subscribers. Opens are inflated by Apple Mail Privacy Protection, which fetches images on the recipient\u2019s behalf \u2014 so sort on clicks, not opens." }),
2206
+ /* @__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." }),
2203
2207
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Text, { size: 1, muted: true, children: [
2204
2208
  "The last two columns count what your order book records in the ",
2205
2209
  SEND_WINDOW_DAYS,
@@ -2226,7 +2230,11 @@ function OverviewPanel({ data, previous, onBrush }) {
2226
2230
  ] })
2227
2231
  },
2228
2232
  { 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) }) },
2229
- { key: "opens", label: "Opens", numeric: true, sortValue: (c) => c.opens, render: (c) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, muted: true, children: formatCount(c.opens) }) },
2233
+ // No Opens column. The caption above the table told the reader, in the tool's
2234
+ // own words, to "sort on clicks, not opens" because Apple Mail Privacy
2235
+ // Protection fetches images on the recipient's behalf. A column whose own
2236
+ // caption instructs you not to read it should not be a column — and the
2237
+ // warning gets shorter as a result.
2230
2238
  { 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) }) },
2231
2239
  {
2232
2240
  key: "unsub",
@@ -2336,7 +2344,7 @@ function DataHealthPanel({ data, diagnostics }) {
2336
2344
  ((data.capture?.estimates?.length ?? 0) > 0 || data.estimatedSessions?.status === "estimated") && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Stack, { space: 3, children: [
2337
2345
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SectionTitle, { title: "How much GA4 is seeing" }),
2338
2346
  /* @__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: [
2339
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Label, { size: 1, muted: true, children: estimate.basis === "orders" ? "Measured against orders" : estimate.basis === "email" ? "Measured against email clicks" : "Measured against Vercel" }),
2347
+ /* @__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" }),
2340
2348
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 4, children: formatPercent(estimate.rate, 0) }),
2341
2349
  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." }),
2342
2350
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Text, { size: 0, muted: true, children: [
@@ -2440,7 +2448,7 @@ function AcquisitionPanel({ data, previous }) {
2440
2448
  ] })
2441
2449
  ] }) }),
2442
2450
  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: [
2443
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Label, { size: 1, muted: true, children: "Unattributed" }),
2451
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Label, { size: 1, muted: true, children: "No source" }),
2444
2452
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: figureRow, children: [
2445
2453
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 4, children: formatPercent(unattributedShare, 1) }),
2446
2454
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
@@ -2491,7 +2499,7 @@ function AcquisitionPanel({ data, previous }) {
2491
2499
  }
2492
2500
  ) : row.source }),
2493
2501
  row.designIndustry && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Badge, { tone: "primary", fontSize: 0, children: "Design" }),
2494
- row.unattributed && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Badge, { tone: "caution", fontSize: 0, children: "Unattributed" })
2502
+ row.unattributed && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Badge, { tone: "caution", fontSize: 0, children: "No source" })
2495
2503
  ] })
2496
2504
  },
2497
2505
  {
@@ -2677,7 +2685,7 @@ function JourneyPanel({ data }) {
2677
2685
  const tracked = data.measurement === "sequence";
2678
2686
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Stack, { space: 4, children: [
2679
2687
  /* @__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: [
2680
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, weight: "medium", children: tracked ? "Tracked funnel" : "Independent per-step totals" }),
2688
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, weight: "medium", children: tracked ? "Tracked path" : "Not a tracked path" }),
2681
2689
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, muted: tracked, children: data.approximationNote })
2682
2690
  ] }) }),
2683
2691
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SegmentTable, { segments, dimension: data.segmentDimension ?? "segment" }),
@@ -2929,18 +2937,12 @@ function TypefaceInterestPanel({ data }) {
2929
2937
  "\xD7 catalogue"
2930
2938
  ] });
2931
2939
  }
2932
- },
2933
- {
2934
- key: "testRate",
2935
- label: "Test rate",
2936
- numeric: true,
2937
- sortValue: (row) => finiteOrNull(row.testRate),
2938
- exportValue: (row) => {
2939
- const r = finiteOrNull(row.testRate);
2940
- return r === null ? null : formatPercent(r, 1);
2941
- },
2942
- render: (row) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, muted: true, "aria-label": finiteOrNull(row.testRate) === null ? `${row.typeface} test rate unavailable` : void 0, children: finiteOrNull(row.testRate) === null ? "\u2014" : formatPercent(row.testRate, 1) })
2943
2940
  }
2941
+ // No Test rate column. It was tested ÷ viewed, rendered muted, from the two
2942
+ // columns immediately to its left — and typefaceInterest's own note says that
2943
+ // where a site maps several tester events onto the step (TDF names three) the
2944
+ // numerator is not a person count, so the ratio ranks page layout as much as
2945
+ // interest. A reader who wants it can see both terms side by side.
2944
2946
  ]
2945
2947
  }
2946
2948
  )