@liiift-studio/sanity-visitor-insights 0.67.0 → 0.68.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
@@ -940,6 +940,34 @@ function SurvivalLines({
940
940
  ] }, segment.key)) })
941
941
  ] });
942
942
  }
943
+ function RatioFigure({
944
+ value,
945
+ format,
946
+ parts,
947
+ size = 4,
948
+ unavailable: unavailable2
949
+ }) {
950
+ const [showParts, setShowParts] = import_react2.default.useState(false);
951
+ const canToggle = value !== null && parts.every((p) => p.value !== null);
952
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sanity_ui_compat.Stack, { space: 2, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: ratioRow, children: [
953
+ showParts && canToggle ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sanity_ui_compat.Stack, { space: 1, children: parts.map((part) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_sanity_ui_compat.Text, { size: size > 1 ? 2 : size, children: [
954
+ (part.format ?? formatCount)(part.value),
955
+ " ",
956
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sanity_ui_compat.Text, { as: "span", size: 0, muted: true, children: part.label })
957
+ ] }, part.label)) }) : value === null ? unavailable2 : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sanity_ui_compat.Text, { size, children: format(value) }),
958
+ canToggle && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
959
+ "button",
960
+ {
961
+ type: "button",
962
+ style: ratioToggle,
963
+ "aria-pressed": showParts,
964
+ "aria-label": showParts ? "Show the rate" : "Show the counts behind this rate",
965
+ onClick: () => setShowParts((was) => !was),
966
+ children: showParts ? "rate" : "counts"
967
+ }
968
+ )
969
+ ] }) });
970
+ }
943
971
  function SortableTable({
944
972
  caption,
945
973
  columns,
@@ -1218,6 +1246,24 @@ var shiftNow = {
1218
1246
  background: mark("shift.now")
1219
1247
  };
1220
1248
  var survivalLegend = { display: "flex", gap: 14, flexWrap: "wrap" };
1249
+ var ratioRow = {
1250
+ display: "flex",
1251
+ alignItems: "baseline",
1252
+ gap: 10,
1253
+ flexWrap: "wrap"
1254
+ };
1255
+ var ratioToggle = {
1256
+ appearance: "none",
1257
+ background: "transparent",
1258
+ border: "1px solid var(--card-border-color, rgba(128,128,128,0.3))",
1259
+ borderRadius: 3,
1260
+ color: "inherit",
1261
+ font: "inherit",
1262
+ fontSize: "0.72em",
1263
+ padding: "1px 7px",
1264
+ cursor: "pointer",
1265
+ opacity: 0.75
1266
+ };
1221
1267
  var filterInput = {
1222
1268
  font: "inherit",
1223
1269
  fontSize: "0.85em",
@@ -2147,21 +2193,36 @@ function OverviewPanel({ data, previous, onBrush }) {
2147
2193
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: cardGrid, children: [
2148
2194
  /* @__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: [
2149
2195
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Label, { size: 1, muted: true, children: "Average order" }),
2150
- (() => {
2151
- const average = averageOrderValue(data);
2152
- const value = metricSortValue(average);
2153
- if (value === null) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MetricFigure, { metric: average, label: "Average order value" });
2154
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Stack, { space: 2, children: [
2155
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: figureRow, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 4, children: formatMoney(value, data.currency ?? null) }) }),
2156
- average.status === "partial" && average.note && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 0, muted: true, children: average.note })
2157
- ] });
2158
- })(),
2196
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2197
+ RatioFigure,
2198
+ {
2199
+ value: metricSortValue(averageOrderValue(data)),
2200
+ format: (v) => formatMoney(v, data.currency ?? null),
2201
+ parts: [
2202
+ { label: "taken", value: metricSortValue(data.revenue), format: (v) => formatMoney(v, data.currency ?? null) },
2203
+ { label: "orders with an amount", value: finiteOrNull(data.ordersWithTotal) }
2204
+ ],
2205
+ unavailable: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MetricFigure, { metric: averageOrderValue(data), label: "Average order value" })
2206
+ }
2207
+ ),
2208
+ averageOrderValue(data).status === "partial" && averageOrderValue(data).note && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 0, muted: true, children: averageOrderValue(data).note }),
2159
2209
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 0, muted: true, children: "From your orders, so exact." })
2160
2210
  ] }) }),
2161
2211
  /* @__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: [
2162
2212
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Label, { size: 1, muted: true, children: "Visitors per order" }),
2163
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MetricFigure, { metric: visitorsPerOrder(data), label: "Visitors per order" }),
2164
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 0, muted: true, children: "Against Vercel\u2019s visitor count, not Google Analytics\u2019 \u2014 dividing by GA4 would flatter this by whatever share it is missing. Read it as a ceiling: some of those visitors are crawlers or repeat devices." })
2213
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2214
+ RatioFigure,
2215
+ {
2216
+ value: metricSortValue(visitorsPerOrder(data)),
2217
+ format: (v) => formatCount(v),
2218
+ parts: [
2219
+ { label: "visitors", value: metricSortValue(data.vercelVisitors) },
2220
+ { label: "orders", value: metricSortValue(data.orders) }
2221
+ ],
2222
+ unavailable: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MetricFigure, { metric: visitorsPerOrder(data), label: "Visitors per order" })
2223
+ }
2224
+ ),
2225
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 0, muted: true, children: "Vercel\u2019s visitor count. A ceiling \u2014 some are bots or the same person twice." })
2165
2226
  ] }) })
2166
2227
  ] }),
2167
2228
  (data.crossSource?.length ?? 0) >= 3 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Stack, { space: 3, children: [
@@ -2435,7 +2496,14 @@ function OverviewPanel({ data, previous, onBrush }) {
2435
2496
  exportValue: (c) => revenuePerThousandSent(c),
2436
2497
  render: (c) => {
2437
2498
  const value = revenuePerThousandSent(c);
2438
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, children: value === null ? "\u2014" : formatMoney(value, data.currency ?? null) });
2499
+ if (value === null) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, children: "\u2014" });
2500
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Stack, { space: 1, children: [
2501
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, children: formatMoney(value, data.currency ?? null) }),
2502
+ c.revenueAfter !== null && c.revenueAfter !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Text, { size: 0, muted: true, children: [
2503
+ "from ",
2504
+ formatMoney(c.revenueAfter, data.currency ?? null)
2505
+ ] })
2506
+ ] });
2439
2507
  }
2440
2508
  }
2441
2509
  ]