@liiift-studio/sanity-visitor-insights 0.78.0 → 0.79.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-BgcKmdw3.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-BgcKmdw3.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-C7y7-GWi.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 LibraryCoverageData, 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 StyleDemandData, w as TimelineEvent, x as TypefaceInterestRow, U as UnavailableReason, y as coverageForRange, z as estimated, B as isReportName, F as ok, G as partial, H as previousRange, I as resolveRange, K as unavailable, N as validateSiteConfig, O as valueOrNull } from './ranges-C7y7-GWi.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-BgcKmdw3.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-BgcKmdw3.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-C7y7-GWi.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 LibraryCoverageData, 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 StyleDemandData, w as TimelineEvent, x as TypefaceInterestRow, U as UnavailableReason, y as coverageForRange, z as estimated, B as isReportName, F as ok, G as partial, H as previousRange, I as resolveRange, K as unavailable, N as validateSiteConfig, O as valueOrNull } from './ranges-C7y7-GWi.js';
5
5
 
6
6
  /**
7
7
  * Data hook for the Studio panels.
package/dist/index.js CHANGED
@@ -3053,8 +3053,8 @@ function JourneyPanel({ data }) {
3053
3053
  ]
3054
3054
  }
3055
3055
  ),
3056
- spread(segments) && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, children: spread(segments) }),
3057
3056
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FunnelChart, { stages, measurement: data.measurement ?? "independent-totals" }),
3057
+ spread(segments) && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, children: spread(segments) }),
3058
3058
  (data.outcomes?.length ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3059
3059
  Section,
3060
3060
  {
@@ -3172,6 +3172,22 @@ function visitorsPerOrder(data) {
3172
3172
  }
3173
3173
  return ok(Math.round(visitors / orders));
3174
3174
  }
3175
+ var WEIGHTS_SHOWN = 12;
3176
+ function leadingFamily(demand) {
3177
+ if (demand.licences <= 0) return null;
3178
+ const ranked = Object.entries(demand.familyLicences).sort((a, b) => b[1] - a[1]);
3179
+ const top = ranked[0];
3180
+ if (!top) return null;
3181
+ return { family: top[0], share: top[1] / demand.licences };
3182
+ }
3183
+ function styleDemandSubtitle(demand) {
3184
+ return `${formatCount(demand.licences)} style licences across ${formatCount(demand.orders)} orders \u2014 your whole order history, not the range above, because a month of style-level counts is too thin to rank.`;
3185
+ }
3186
+ function libraryCoverageSubtitle(coverage) {
3187
+ const unreleasedStyles = coverage.unreleased.reduce((sum, f) => sum + f.styles, 0);
3188
+ const released = coverage.styles - unreleasedStyles;
3189
+ return `${formatCount(coverage.everLicensed)} of ${formatCount(released)} released styles have been licensed at least once.`;
3190
+ }
3175
3191
  function TypefaceInterestPanel({ data }) {
3176
3192
  const benchmark = catalogueRate(data.rows ?? []);
3177
3193
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: panelStack, children: [
@@ -3314,6 +3330,73 @@ function TypefaceInterestPanel({ data }) {
3314
3330
  }
3315
3331
  )
3316
3332
  ] }),
3333
+ (data.styleDemand?.weights.length ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
3334
+ Section,
3335
+ {
3336
+ title: "Which weights sell",
3337
+ subtitle: styleDemandSubtitle(data.styleDemand),
3338
+ children: [
3339
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3340
+ ProportionChart,
3341
+ {
3342
+ bars: (data.styleDemand?.weights ?? []).slice(0, WEIGHTS_SHOWN).map((row) => ({
3343
+ key: row.weight,
3344
+ label: row.weight,
3345
+ sublabel: row.families === 1 ? "one family only" : `across ${formatCount(row.families)} families`,
3346
+ value: row.licences
3347
+ })),
3348
+ format: (value) => `${formatCount(value)} licences`,
3349
+ totalLabel: "Style licences in the weights shown"
3350
+ }
3351
+ ),
3352
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: stackPair, children: [
3353
+ (data.styleDemand?.weights.length ?? 0) > WEIGHTS_SHOWN && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Text, { size: 0, muted: true, children: [
3354
+ formatCount(data.styleDemand.weights.length - WEIGHTS_SHOWN),
3355
+ " quieter",
3356
+ " ",
3357
+ "weights are not shown."
3358
+ ] }),
3359
+ leadingFamily(data.styleDemand) && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Text, { size: 1, children: [
3360
+ leadingFamily(data.styleDemand)?.family,
3361
+ " accounts for",
3362
+ " ",
3363
+ formatPercent(leadingFamily(data.styleDemand)?.share ?? 0, 0),
3364
+ " of every style ever licensed."
3365
+ ] }),
3366
+ (data.styleDemand?.orders ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Text, { size: 0, muted: true, children: [
3367
+ formatPercent(data.styleDemand.singleStyleOrders / data.styleDemand.orders, 0),
3368
+ " ",
3369
+ "of orders licensed exactly one style."
3370
+ ] })
3371
+ ] })
3372
+ ]
3373
+ }
3374
+ ),
3375
+ data.libraryCoverage && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
3376
+ Section,
3377
+ {
3378
+ title: "What the library holds",
3379
+ tone: "secondary",
3380
+ subtitle: libraryCoverageSubtitle(data.libraryCoverage),
3381
+ children: [
3382
+ data.libraryCoverage.gaps.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Text, { size: 1, children: [
3383
+ "Never licensed, in families that sell:",
3384
+ " ",
3385
+ data.libraryCoverage.gaps.map((g) => `${g.family} (${formatCount(g.styles)})`).join(", "),
3386
+ "."
3387
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, children: "Every style in every selling family has been licensed at least once." }),
3388
+ data.libraryCoverage.unreleased.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sanity_ui_compat3.Text, { size: 0, muted: true, children: [
3389
+ "Not counted above:",
3390
+ " ",
3391
+ data.libraryCoverage.unreleased.map((u) => `${u.family} (${formatCount(u.styles)} styles)`).join(", "),
3392
+ " ",
3393
+ "\u2014 no style from ",
3394
+ data.libraryCoverage.unreleased.length === 1 ? "it" : "them",
3395
+ " has ever sold, which reads as unreleased rather than unwanted."
3396
+ ] })
3397
+ ]
3398
+ }
3399
+ ),
3317
3400
  (data.licences?.length ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: stackBlock, children: [
3318
3401
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SectionTitle, { title: "How licences sell" }),
3319
3402
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sanity_ui_compat3.Text, { size: 1, muted: true, children: "From your orders, so exact. Bars are orders; the money beside them is that order total split evenly across the licences on it, so it is a share rather than a price." }),
@@ -3655,7 +3738,7 @@ function PanelTabs({ value, onChange }) {
3655
3738
  }
3656
3739
  function CoverageRibbon({ ratio }) {
3657
3740
  if (ratio === null) {
3658
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_sanity_ui_compat4.Text, { size: 0, muted: true, children: "Every figure on this tab comes from Google Analytics. How much of your traffic it is seeing has not been checked for this window \u2014 open Overview or Data health to measure it." });
3741
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_sanity_ui_compat4.Text, { size: 0, muted: true, children: "Figures marked (GA4) on this tab come from Google Analytics. How much of your traffic it is seeing has not been checked for this window \u2014 open Overview to measure it." });
3659
3742
  }
3660
3743
  if (ratio < 0.2) return null;
3661
3744
  const seen = Math.round((1 - ratio) * 100);