@liiift-studio/sanity-visitor-insights 0.55.0 → 0.57.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
@@ -1785,6 +1785,33 @@ var legendRow = {
1785
1785
  alignItems: "center"
1786
1786
  };
1787
1787
 
1788
+ // src/core/rank.ts
1789
+ var MIN_WEEKS_TO_RANK = 4;
1790
+ function weeklyRank(series) {
1791
+ if (series.length < 7 * MIN_WEEKS_TO_RANK) return null;
1792
+ const weeks = [];
1793
+ for (let end = series.length; end - 7 >= 0; end -= 7) {
1794
+ const slice = series.slice(end - 7, end);
1795
+ weeks.push(slice.some((d) => d.value === null) ? null : slice.reduce((sum, d) => sum + d.value, 0));
1796
+ }
1797
+ const latest = weeks[0];
1798
+ if (latest === null || latest === void 0) return null;
1799
+ const comparable = weeks.filter((w) => w !== null);
1800
+ if (comparable.length < MIN_WEEKS_TO_RANK) return null;
1801
+ const better = comparable.filter((w) => w > latest).length;
1802
+ return { rank: better + 1, of: comparable.length, value: latest };
1803
+ }
1804
+ function describeRank(rank) {
1805
+ const ordinal = (n) => {
1806
+ const tens = n % 100;
1807
+ if (tens >= 11 && tens <= 13) return `${n}th`;
1808
+ return `${n}${["th", "st", "nd", "rd"][n % 10] ?? "th"}`;
1809
+ };
1810
+ if (rank.rank === 1) return `best of the last ${rank.of} weeks`;
1811
+ if (rank.rank === rank.of) return `lowest of the last ${rank.of} weeks`;
1812
+ return `${ordinal(rank.rank)} best of the last ${rank.of} weeks`;
1813
+ }
1814
+
1788
1815
  // src/types.ts
1789
1816
  function estimated(value, low, high, basis) {
1790
1817
  return { status: "estimated", value, low, high, basis };
@@ -1887,14 +1914,16 @@ function OverviewPanel({ data, previous, onBrush }) {
1887
1914
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: figureRow, children: [
1888
1915
  metricOr(data.revenue, OLDER_ROUTE).status === "ok" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 4, children: formatMoney(data.revenue.value, data.currency ?? null) }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MetricFigure, { metric: metricOr(data.revenue, OLDER_ROUTE), label: "Revenue" }),
1889
1916
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Delta, { current: metricSortValue(data.revenue), previous: metricSortValue(previous?.revenue) })
1890
- ] })
1917
+ ] }),
1918
+ rankLine(data.crossSource, (d) => d.revenue) && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 0, muted: true, children: rankLine(data.crossSource, (d) => d.revenue) })
1891
1919
  ] }) }),
1892
1920
  /* @__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: [
1893
1921
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Label, { size: 1, muted: true, children: "Orders" }),
1894
1922
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: figureRow, children: [
1895
1923
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MetricFigure, { metric: data.orders, label: "Orders" }),
1896
1924
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Delta, { current: metricSortValue(data.orders), previous: metricSortValue(previous?.orders) })
1897
- ] })
1925
+ ] }),
1926
+ rankLine(data.crossSource, (d) => d.orders) && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 0, muted: true, children: rankLine(data.crossSource, (d) => d.orders) })
1898
1927
  ] }) }),
1899
1928
  /* @__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: [
1900
1929
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Label, { size: 1, muted: true, children: "Traffic" }),
@@ -1936,7 +1965,7 @@ function OverviewPanel({ data, previous, onBrush }) {
1936
1965
  average.status === "partial" && average.note && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 0, muted: true, children: average.note })
1937
1966
  ] });
1938
1967
  })(),
1939
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 0, muted: true, children: "From your own orders, so it is exact \u2014 but only across the orders that carry an amount, which is what the revenue figure covers." })
1968
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 0, muted: true, children: "From your orders, so exact." })
1940
1969
  ] }) }),
1941
1970
  /* @__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: [
1942
1971
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Label, { size: 1, muted: true, children: "Visitors per order" }),
@@ -2537,6 +2566,11 @@ function shortListNote(truncated, withheld, tail) {
2537
2566
  const cause = truncated && withheld ? "GA4 returned only its top rows, and withheld others with too few people to report" : truncated ? "GA4 returned only its top rows" : "GA4 withheld rows with too few people to report";
2538
2567
  return `${cause}, so this list is shorter than reality. ${tail}`;
2539
2568
  }
2569
+ function rankLine(series, pick) {
2570
+ if (!series || series.length === 0) return null;
2571
+ const ranked = weeklyRank(series.map((d) => ({ date: d.date, value: pick(d) })));
2572
+ return ranked ? describeRank(ranked) : null;
2573
+ }
2540
2574
  function JourneyPanel({ data }) {
2541
2575
  const [segmentKey, setSegmentKey] = import_react4.default.useState(null);
2542
2576
  const segments = data.segments ?? [];
@@ -2856,7 +2890,7 @@ function TypefaceInterestPanel({ data }) {
2856
2890
  ] }),
2857
2891
  (data.licences?.length ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Stack, { space: 3, children: [
2858
2892
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SectionTitle, { title: "How licences sell" }),
2859
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, muted: true, children: "From the orders themselves, so this is exact and unaffected by whatever GA4 is doing. Tier and term are separate rows, because they are the two variables in the pricing question and a tier that sells well at one year may not at perpetual." }),
2893
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, muted: true, children: "From your orders, so exact. Tier and term are separate rows, because they are the two variables in the pricing question and a tier that sells well at one year may not at perpetual." }),
2860
2894
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2861
2895
  ProportionChart,
2862
2896
  {
@@ -3183,14 +3217,11 @@ function CoverageRibbon({ ratio }) {
3183
3217
  const seen = Math.round((1 - ratio) * 100);
3184
3218
  const multiplier = seen > 0 ? Math.round(100 / seen) : null;
3185
3219
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_sanity_ui_compat4.Card, { padding: 3, radius: 2, tone: ratio > 0.6 ? "critical" : "caution", border: true, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_sanity_ui_compat4.Text, { size: 1, children: [
3186
- "Most figures on this tab come from Google Analytics, which is seeing roughly ",
3220
+ "Google Analytics is seeing about ",
3187
3221
  seen,
3188
- "% of this site\u2019s traffic in this window \u2014 so the real numbers are",
3189
- " ",
3190
- multiplier === null ? "many times" : `several times`,
3191
- " these",
3192
- multiplier !== null && multiplier > 1 ? ` (very roughly ${multiplier}\xD7)` : "",
3193
- ". Figures drawn from your orders \u2014 anything labelled as coming from Sanity \u2014 are exact and must not be scaled up. Data health explains the rest, and how uncertain it is."
3222
+ "% of your traffic here, so treat the figures below as roughly ",
3223
+ multiplier === null ? "many times" : `${multiplier}\xD7`,
3224
+ " low. Anything marked (orders) is exact \u2014 do not scale it."
3194
3225
  ] }) });
3195
3226
  }
3196
3227
  function ReadyReport({