@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 +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +85 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +85 -2
- package/dist/index.mjs.map +1 -1
- package/dist/{ranges-BgcKmdw3.d.mts → ranges-C7y7-GWi.d.mts} +47 -1
- package/dist/{ranges-BgcKmdw3.d.ts → ranges-C7y7-GWi.d.ts} +47 -1
- package/dist/server.d.mts +4 -4
- package/dist/server.d.ts +4 -4
- package/dist/server.js +112 -4
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +112 -4
- package/dist/server.mjs.map +1 -1
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/{vercel-D0tEOU9d.d.mts → vercel-5mnf2RIU.d.mts} +0 -8
- package/dist/{vercel-D0tEOU9d.d.ts → vercel-5mnf2RIU.d.ts} +0 -8
- package/package.json +1 -1
- package/src/core/siteConfig.ts +8 -0
- package/src/reportData.ts +30 -0
- package/src/server/orders.ts +211 -3
- package/src/server/reports/reports.test.ts +184 -1
- package/src/server/reports/typefaceInterest.ts +23 -2
- package/src/studio/VisitorInsightsTool.tsx +26 -22
- package/src/studio/panels.test.tsx +175 -0
- package/src/studio/panels.tsx +143 -3
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-
|
|
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,
|
|
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-
|
|
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,
|
|
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: "
|
|
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);
|