@liiift-studio/sanity-visitor-insights 0.8.1 → 0.9.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
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
@@ -51,7 +61,7 @@ var import_sanity2 = require("sanity");
51
61
  var import_icons = require("@liiift-studio/sanity-ui-compat/icons");
52
62
 
53
63
  // src/studio/VisitorInsightsTool.tsx
54
- var import_react2 = require("react");
64
+ var import_react3 = require("react");
55
65
  var import_sanity_ui_compat3 = require("@liiift-studio/sanity-ui-compat");
56
66
 
57
67
  // src/studio/useReport.ts
@@ -106,6 +116,7 @@ function useReport({ apiBaseUrl, report, range }) {
106
116
  }
107
117
 
108
118
  // src/studio/Figure.tsx
119
+ var import_react2 = __toESM(require("react"));
109
120
  var import_sanity_ui_compat = require("@liiift-studio/sanity-ui-compat");
110
121
  var import_jsx_runtime = require("react/jsx-runtime");
111
122
  var REASON_TEXT = {
@@ -141,7 +152,7 @@ function ComparisonBar({ label, metric, max, tone = "default" }) {
141
152
  const value = metric.status === "unavailable" ? null : metric.value;
142
153
  const width = value !== null && max > 0 ? Math.max(2, value / max * 100) : 0;
143
154
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_sanity_ui_compat.Stack, { space: 2, children: [
144
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_sanity_ui_compat.Flex, { align: "center", justify: "space-between", gap: 3, children: [
155
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: barHeader, children: [
145
156
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sanity_ui_compat.Text, { size: 1, weight: "medium", children: label }),
146
157
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MetricFigure, { metric, label, size: 1 })
147
158
  ] }),
@@ -164,24 +175,69 @@ function ComparisonBar({ label, metric, max, tone = "default" }) {
164
175
  )
165
176
  ] });
166
177
  }
178
+ var chartFrame = { position: "relative", width: "100%" };
179
+ var readout = {
180
+ position: "absolute",
181
+ top: 0,
182
+ right: 0,
183
+ padding: "6px 10px",
184
+ borderRadius: 3,
185
+ background: "var(--card-bg-color, rgba(0,0,0,0.55))",
186
+ border: "1px solid var(--card-border-color, rgba(128,128,128,0.35))",
187
+ pointerEvents: "none",
188
+ maxWidth: "70%"
189
+ };
190
+ var barHeader = {
191
+ display: "flex",
192
+ alignItems: "center",
193
+ justifyContent: "space-between",
194
+ gap: 12,
195
+ flexWrap: "wrap"
196
+ };
197
+ var legendRow = {
198
+ display: "flex",
199
+ gap: 12,
200
+ alignItems: "center",
201
+ flexWrap: "wrap"
202
+ };
203
+ var noticeRow = {
204
+ display: "flex",
205
+ gap: 12,
206
+ alignItems: "flex-start",
207
+ flexWrap: "wrap"
208
+ };
209
+ var noticeBadge = { flex: "0 0 auto" };
167
210
  function NoticeList({ notices }) {
168
211
  if (notices.length === 0) return null;
169
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "grid", gap: 8 }, children: notices.map((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)(import_sanity_ui_compat.Flex, { gap: 3, align: "flex-start", children: [
170
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sanity_ui_compat.Badge, { tone: "caution", fontSize: 0, children: "Caveat" }),
212
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "grid", gap: 8 }, children: notices.map((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: [
213
+ /* @__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" }) }),
171
214
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sanity_ui_compat.Text, { size: 1, children: notice })
172
215
  ] }) }) }, notice)) });
173
216
  }
217
+ var CHART = { w: 760, h: 240, left: 52, right: 12, top: 16, bottom: 30 };
218
+ function niceCeiling(value) {
219
+ if (value <= 0) return 1;
220
+ const magnitude = 10 ** Math.floor(Math.log10(value));
221
+ const normalised = value / magnitude;
222
+ const step = normalised <= 1 ? 1 : normalised <= 2 ? 2 : normalised <= 5 ? 5 : 10;
223
+ return step * magnitude;
224
+ }
225
+ function shortDate(iso) {
226
+ const parsed = /* @__PURE__ */ new Date(`${iso}T00:00:00Z`);
227
+ if (Number.isNaN(parsed.getTime())) return iso;
228
+ return `${parsed.getUTCDate()} ${parsed.toLocaleString("en-GB", { month: "short", timeZone: "UTC" })}`;
229
+ }
174
230
  function TrendChart({ points, label = "Daily pageviews, GA4 against Vercel" }) {
231
+ const [hovered, setHovered] = import_react2.default.useState(null);
175
232
  if (points.length < 3) return null;
176
- const W = 720;
177
- const H = 132;
178
- const PAD = 4;
179
233
  const values = points.flatMap((p) => [p.ga4, p.vercel]).filter((v) => typeof v === "number");
180
234
  if (values.length === 0) return null;
181
- const max = Math.max(...values, 1);
182
- const x = (i) => PAD + i / Math.max(1, points.length - 1) * (W - PAD * 2);
183
- const y = (v) => H - PAD - v / max * (H - PAD * 2);
184
- const path = (pick) => {
235
+ const max = niceCeiling(Math.max(...values, 1));
236
+ const plotW = CHART.w - CHART.left - CHART.right;
237
+ const plotH = CHART.h - CHART.top - CHART.bottom;
238
+ const x = (i) => CHART.left + i / Math.max(1, points.length - 1) * plotW;
239
+ const y = (v) => CHART.top + plotH - v / max * plotH;
240
+ const line = (pick) => {
185
241
  let d = "";
186
242
  let penDown = false;
187
243
  points.forEach((p, i) => {
@@ -195,40 +251,145 @@ function TrendChart({ points, label = "Daily pageviews, GA4 against Vercel" }) {
195
251
  });
196
252
  return d.trim();
197
253
  };
198
- const vercelPath = path((p) => p.vercel);
199
- const ga4Path = path((p) => p.ga4);
200
- const first = points[0]?.date ?? "";
201
- const last = points[points.length - 1]?.date ?? "";
202
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_sanity_ui_compat.Stack, { space: 2, children: [
203
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sanity_ui_compat.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
204
- "svg",
205
- {
206
- viewBox: `0 0 ${W} ${H}`,
207
- width: "100%",
208
- height: H,
209
- role: "img",
210
- "aria-label": `${label}, ${first} to ${last}. Peak ${Math.round(max)} pageviews in a day.`,
211
- preserveAspectRatio: "none",
212
- style: { display: "block", overflow: "visible" },
213
- children: [
214
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: vercelPath, fill: "none", stroke: "currentColor", strokeOpacity: "0.85", strokeWidth: "2", vectorEffect: "non-scaling-stroke" }),
215
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: ga4Path, fill: "none", stroke: "currentColor", strokeOpacity: "0.85", strokeWidth: "2", strokeDasharray: "5 3", vectorEffect: "non-scaling-stroke" })
216
- ]
217
- }
218
- ) }),
219
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_sanity_ui_compat.Flex, { gap: 3, align: "center", wrap: "wrap", children: [
254
+ const bands = [];
255
+ let run = [];
256
+ const flushRun = () => {
257
+ if (run.length > 1) {
258
+ const top = run.map((i) => `${x(i).toFixed(1)} ${y(points[i].vercel).toFixed(1)}`);
259
+ const bottom = [...run].reverse().map((i) => `${x(i).toFixed(1)} ${y(points[i].ga4).toFixed(1)}`);
260
+ bands.push(`M${top.join(" L")} L${bottom.join(" L")} Z`);
261
+ }
262
+ run = [];
263
+ };
264
+ points.forEach((p, i) => {
265
+ if (typeof p.ga4 === "number" && typeof p.vercel === "number") run.push(i);
266
+ else flushRun();
267
+ });
268
+ flushRun();
269
+ const ticks = [0, 0.25, 0.5, 0.75, 1].map((f) => ({ value: max * f, y: y(max * f) }));
270
+ const labelEvery = Math.max(1, Math.ceil(points.length / 6));
271
+ const dateTicks = points.map((p, i) => ({ i, date: p.date })).filter(({ i }) => i % labelEvery === 0 || i === points.length - 1);
272
+ const active = hovered !== null ? points[hovered] : null;
273
+ const activeGap = active && typeof active.ga4 === "number" && typeof active.vercel === "number" && active.vercel > 0 ? (active.vercel - active.ga4) / active.vercel : null;
274
+ const onKeyDown = (event) => {
275
+ const last = points.length - 1;
276
+ const current = hovered ?? 0;
277
+ const move = (next) => {
278
+ event.preventDefault();
279
+ setHovered(Math.max(0, Math.min(last, next)));
280
+ };
281
+ if (event.key === "ArrowRight") return move(hovered === null ? 0 : current + 1);
282
+ if (event.key === "ArrowLeft") return move(hovered === null ? last : current - 1);
283
+ if (event.key === "Home") return move(0);
284
+ if (event.key === "End") return move(last);
285
+ if (event.key === "Escape") {
286
+ setHovered(null);
287
+ return;
288
+ }
289
+ };
290
+ const onMove = (event) => {
291
+ const box = event.currentTarget.getBoundingClientRect();
292
+ if (box.width === 0) return;
293
+ const svgX = (event.clientX - box.left) / box.width * CHART.w;
294
+ const ratio = (svgX - CHART.left) / plotW;
295
+ const index = Math.round(ratio * (points.length - 1));
296
+ setHovered(index >= 0 && index < points.length ? index : null);
297
+ };
298
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_sanity_ui_compat.Stack, { space: 3, children: [
299
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: chartFrame, children: [
300
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
301
+ "svg",
302
+ {
303
+ viewBox: `0 0 ${CHART.w} ${CHART.h}`,
304
+ width: "100%",
305
+ style: { display: "block", height: "auto" },
306
+ role: "img",
307
+ "aria-label": `${label}, ${points[0]?.date} to ${points[points.length - 1]?.date}. Peak ${formatCount(max)} pageviews in a day.`,
308
+ onMouseMove: onMove,
309
+ onMouseLeave: () => setHovered(null),
310
+ onKeyDown,
311
+ onBlur: () => setHovered(null),
312
+ tabIndex: 0,
313
+ "aria-describedby": active ? "trend-readout" : void 0,
314
+ children: [
315
+ ticks.map((tick) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { children: [
316
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
317
+ "line",
318
+ {
319
+ x1: CHART.left,
320
+ x2: CHART.w - CHART.right,
321
+ y1: tick.y,
322
+ y2: tick.y,
323
+ stroke: "currentColor",
324
+ strokeOpacity: tick.value === 0 ? 0.35 : 0.12,
325
+ strokeWidth: "1"
326
+ }
327
+ ),
328
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
329
+ "text",
330
+ {
331
+ x: CHART.left - 8,
332
+ y: tick.y + 4,
333
+ textAnchor: "end",
334
+ fontSize: "11",
335
+ fill: "currentColor",
336
+ fillOpacity: "0.55",
337
+ children: formatCount(tick.value)
338
+ }
339
+ )
340
+ ] }, tick.value)),
341
+ dateTicks.map(({ i, date }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
342
+ "text",
343
+ {
344
+ x: x(i),
345
+ y: CHART.h - 10,
346
+ textAnchor: "middle",
347
+ fontSize: "11",
348
+ fill: "currentColor",
349
+ fillOpacity: "0.55",
350
+ children: shortDate(date)
351
+ },
352
+ date
353
+ )),
354
+ bands.map((d) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d, fill: "currentColor", fillOpacity: "0.09", stroke: "none" }, d.slice(0, 24))),
355
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: line((p) => p.vercel), fill: "none", stroke: "currentColor", strokeOpacity: "0.9", strokeWidth: "2" }),
356
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: line((p) => p.ga4), fill: "none", stroke: "currentColor", strokeOpacity: "0.9", strokeWidth: "2", strokeDasharray: "5 3" }),
357
+ hovered !== null && active && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { children: [
358
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
359
+ "line",
360
+ {
361
+ x1: x(hovered),
362
+ x2: x(hovered),
363
+ y1: CHART.top,
364
+ y2: CHART.top + plotH,
365
+ stroke: "currentColor",
366
+ strokeOpacity: "0.4",
367
+ strokeWidth: "1"
368
+ }
369
+ ),
370
+ typeof active.vercel === "number" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: x(hovered), cy: y(active.vercel), r: "4", fill: "currentColor" }),
371
+ typeof active.ga4 === "number" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: x(hovered), cy: y(active.ga4), r: "4", fill: "currentColor" })
372
+ ] })
373
+ ]
374
+ }
375
+ ),
376
+ active && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: readout, id: "trend-readout", role: "status", "aria-live": "polite", children: [
377
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sanity_ui_compat.Text, { size: 1, weight: "medium", children: shortDate(active.date) }),
378
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_sanity_ui_compat.Text, { size: 1, muted: true, children: [
379
+ "Vercel ",
380
+ typeof active.vercel === "number" ? formatCount(active.vercel) : "\u2014",
381
+ " \xB7 ",
382
+ "GA4 ",
383
+ typeof active.ga4 === "number" ? formatCount(active.ga4) : "\u2014",
384
+ activeGap !== null ? ` \xB7 gap ${formatPercent(activeGap, 0)}` : ""
385
+ ] })
386
+ ] })
387
+ ] }),
388
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: legendRow, children: [
220
389
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sanity_ui_compat.Text, { size: 0, muted: true, children: "\u2501\u2501 Vercel" }),
221
390
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sanity_ui_compat.Text, { size: 0, muted: true, children: "\u2504\u2504 GA4" }),
222
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_sanity_ui_compat.Text, { size: 0, muted: true, children: [
223
- first,
224
- " to ",
225
- last
226
- ] }),
227
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_sanity_ui_compat.Text, { size: 0, muted: true, children: [
228
- "peak ",
229
- formatCount(max),
230
- "/day"
231
- ] })
391
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sanity_ui_compat.Text, { size: 0, muted: true, children: "shaded band is the gap" }),
392
+ !active && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sanity_ui_compat.Text, { size: 0, muted: true, children: "hover, or focus and use arrow keys, for a day" })
232
393
  ] })
233
394
  ] });
234
395
  }
@@ -243,6 +404,11 @@ function maxOf(metrics) {
243
404
  return metrics.reduce((max, metric) => metric.status === "unavailable" ? max : Math.max(max, metric.value), 0);
244
405
  }
245
406
  var tableWrap = { overflowX: "auto", width: "100%" };
407
+ var cardGrid = {
408
+ display: "grid",
409
+ gridTemplateColumns: "repeat(auto-fit, minmax(min(14rem, 100%), 1fr))",
410
+ gap: 16
411
+ };
246
412
  var table = { width: "100%", borderCollapse: "collapse", minWidth: 420 };
247
413
  var cell = { padding: "8px 12px", textAlign: "left", borderBottom: "1px solid var(--card-border-color)" };
248
414
  var numericCell = { ...cell, textAlign: "right" };
@@ -267,7 +433,7 @@ function MeasurementHealthPanel({ data }) {
267
433
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_sanity_ui_compat2.Stack, { space: 3, children: [
268
434
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sanity_ui_compat2.Heading, { size: 1, children: "Context" }),
269
435
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sanity_ui_compat2.Text, { size: 1, muted: true, children: "Different units to the figures above, and to each other. Shown for scale, never differenced." }),
270
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_sanity_ui_compat2.Grid, { columns: [1, 3], gap: 4, children: [
436
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: cardGrid, children: [
271
437
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sanity_ui_compat2.Card, { padding: 3, radius: 2, tone: "transparent", border: true, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_sanity_ui_compat2.Stack, { space: 3, children: [
272
438
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sanity_ui_compat2.Label, { size: 1, muted: true, children: "GA4 sessions" }),
273
439
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MetricFigure, { metric: data.ga4Sessions, label: "GA4 sessions" })
@@ -286,7 +452,7 @@ function MeasurementHealthPanel({ data }) {
286
452
  }
287
453
  function AcquisitionPanel({ data }) {
288
454
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_sanity_ui_compat2.Stack, { space: 4, children: [
289
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_sanity_ui_compat2.Grid, { columns: [1, 3], gap: 4, children: [
455
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: cardGrid, children: [
290
456
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sanity_ui_compat2.Card, { padding: 3, radius: 2, tone: "transparent", border: true, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_sanity_ui_compat2.Stack, { space: 3, children: [
291
457
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sanity_ui_compat2.Label, { size: 1, muted: true, children: "Sessions" }),
292
458
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sanity_ui_compat2.Text, { size: 4, children: formatCount(data.totalSessions) })
@@ -308,7 +474,7 @@ function AcquisitionPanel({ data }) {
308
474
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("th", { scope: "col", style: numericCell, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sanity_ui_compat2.Label, { size: 1, muted: true, children: "Sessions" }) })
309
475
  ] }) }),
310
476
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tbody", { children: (data.rows ?? []).map((row) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("tr", { children: [
311
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("th", { scope: "row", style: cell, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_sanity_ui_compat2.Flex, { gap: 2, align: "center", children: [
477
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("th", { scope: "row", style: cell, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }, children: [
312
478
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sanity_ui_compat2.Text, { size: 1, children: row.source }),
313
479
  row.designIndustry && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sanity_ui_compat2.Badge, { tone: "primary", fontSize: 0, children: "design industry" }),
314
480
  row.unattributed && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sanity_ui_compat2.Badge, { tone: "caution", fontSize: 0, children: "unattributed" })
@@ -412,8 +578,8 @@ var PANELS = [
412
578
  { report: "diagnostics", label: "Diagnostics", blurb: "What to fix before trusting the numbers above" }
413
579
  ];
414
580
  function RangeSelector({ value, onChange }) {
415
- const refs = (0, import_react2.useRef)([]);
416
- const onKeyDown = (0, import_react2.useCallback)(
581
+ const refs = (0, import_react3.useRef)([]);
582
+ const onKeyDown = (0, import_react3.useCallback)(
417
583
  (event) => {
418
584
  const currentIndex = RANGES.findIndex((r) => r.key === value);
419
585
  let nextIndex = null;
@@ -469,8 +635,8 @@ function SourceStatusRow({ sources }) {
469
635
  ] }) });
470
636
  }
471
637
  function PanelTabs({ value, onChange }) {
472
- const refs = (0, import_react2.useRef)([]);
473
- const onKeyDown = (0, import_react2.useCallback)(
638
+ const refs = (0, import_react3.useRef)([]);
639
+ const onKeyDown = (0, import_react3.useCallback)(
474
640
  (event) => {
475
641
  const currentIndex = PANELS.findIndex((p) => p.report === value);
476
642
  let nextIndex = null;
@@ -546,8 +712,8 @@ function ReportPanel({ report, apiBaseUrl, range }) {
546
712
  function VisitorInsightsTool(props) {
547
713
  const apiBaseUrl = props.tool?.options?.apiBaseUrl ?? props.apiBaseUrl ?? "";
548
714
  const siteLabel = props.tool?.options?.siteLabel ?? props.siteLabel ?? "";
549
- const [range, setRange] = (0, import_react2.useState)("week");
550
- const [activePanel, setActivePanel] = (0, import_react2.useState)("measurement-health");
715
+ const [range, setRange] = (0, import_react3.useState)("week");
716
+ const [activePanel, setActivePanel] = (0, import_react3.useState)("measurement-health");
551
717
  const active = PANELS.find((p) => p.report === activePanel) ?? PANELS[0];
552
718
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Container, { width: 4, padding: 4, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Stack, { space: 5, children: [
553
719
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Flex, { align: "flex-start", justify: "space-between", gap: 4, wrap: "wrap", children: [