@liiift-studio/sanity-visitor-insights 0.16.0 → 0.18.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, D as DiagnosticReport, J as JourneyData, c as MeasurementHealthData, T as TypefaceInterestData } from './ranges-DvICo5H2.mjs';
4
- export { C as CaptureBasis, d as CaptureEstimate, e as CaptureModel, f as CheckStatus, g as Coverage, h as DailyPoint, i as DateRange, j as DiagnosticCheck, E as EmailCampaign, k as EventCutover, l as JourneyOutcome, m as JourneyStep, L as LandingPage, P as PREEXISTING, n as REPORT_NAMES, o as ReportError, S as SiteAnalyticsConfig, p as SourceName, q as SourceRow, r as SourceStatus, s as TypefaceInterestRow, U as UnavailableReason, t as coverageForRange, u as estimated, v as isReportName, w as ok, x as partial, y as previousRange, z as resolveRange, B as unavailable, F as validateSiteConfig, G as valueOrNull } from './ranges-DvICo5H2.mjs';
3
+ import { R as ReportEnvelope, a as ReportName, b as RangeKey, M as MetricValue, A as AcquisitionData, D as DiagnosticReport, J as JourneyData, c as MeasurementHealthData, T as TypefaceInterestData } from './ranges-B_OrmcdP.mjs';
4
+ export { C as CaptureBasis, d as CaptureEstimate, e as CaptureModel, f as CheckStatus, g as Coverage, h as CrossSourceDay, i as DailyPoint, j as DateRange, k as DiagnosticCheck, E as EmailCampaign, l as EventCutover, m as JourneyOutcome, 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-B_OrmcdP.mjs';
5
5
 
6
6
  /**
7
7
  * The Visitor Insights Studio tool.
@@ -162,6 +162,33 @@ interface ComparisonBarProps {
162
162
  * Studio bundle and the theme-token bridging that a chart library would need for light and dark.
163
163
  */
164
164
  declare function ComparisonBar({ label, metric, max, tone }: ComparisonBarProps): React.ReactElement;
165
+ /** One bar of a proportion chart. */
166
+ interface ProportionBar {
167
+ key: string;
168
+ label: string;
169
+ sublabel?: string;
170
+ value: number;
171
+ }
172
+ /** Props for ProportionChart. */
173
+ interface ProportionChartProps {
174
+ bars: ProportionBar[];
175
+ /** How to write each value out. */
176
+ format: (value: number) => string;
177
+ /** What the bars sum to, named — a share is meaningless without its denominator stated. */
178
+ totalLabel: string;
179
+ }
180
+ /**
181
+ * A ranked part-to-whole bar chart.
182
+ *
183
+ * For a breakdown whose rows sum to something meaningful — licence revenue by tier, say. Each bar
184
+ * carries its share of the total AND its absolute value, because a share alone hides that the
185
+ * leading row might be two orders, and an absolute alone hides that it is most of the business.
186
+ *
187
+ * Bars scale against the SUM rather than against the largest row, so the widths read as shares of
188
+ * the whole. Scaling to the max would make the top row full-width whatever it was worth, which is
189
+ * the same misreading the funnel avoids by anchoring to its entry step.
190
+ */
191
+ declare function ProportionChart({ bars, format, totalLabel }: ProportionChartProps): React.ReactElement | null;
165
192
  /** One rung of the funnel, already filtered to steps that are actually measured. */
166
193
  interface FunnelStage {
167
194
  key: string;
@@ -285,6 +312,97 @@ interface SortableTableProps<Row> {
285
312
  */
286
313
  declare function SortableTable<Row>({ caption, columns, rows, rowKey, initialSort, filterPlaceholder, filterOn, exportName, truncatedNote, }: SortableTableProps<Row>): React.ReactElement;
287
314
 
315
+ /**
316
+ * One time axis, every source stacked against it.
317
+ *
318
+ * This is the only view in the tool that answers a question no single source can: did the thing we
319
+ * did move the thing we care about. A campaign goes out on Tuesday — did traffic rise, did revenue
320
+ * follow, and did GA4 even see it. Vercel knows the traffic, Sanity knows the money, Mailchimp
321
+ * knows the send date, and GA4 knows a lossy fraction of the behaviour in between.
322
+ *
323
+ * SMALL MULTIPLES, NOT A DUAL AXIS. Putting pageviews and revenue on one pair of axes requires
324
+ * choosing a scale factor between them, and whatever is chosen manufactures a visual correlation
325
+ * that the data did not claim — two lines can be made to cross, diverge or track by nothing more
326
+ * than the ratio picked. Stacked rows sharing one x axis show the same co-movement and assert
327
+ * nothing about relative magnitude, because each row carries its own y axis and its own units.
328
+ *
329
+ * COMPLETENESS IS DRAWN. A series from a source that misses things is dashed and carries a shaded
330
+ * band up to its estimated true value; a complete source is a solid line. The reader learns which
331
+ * numbers are facts and which are a fifth of the facts without being told twice.
332
+ *
333
+ * d3 is used for scales and path generation only — pure functions in, path strings out. No
334
+ * d3-selection, so nothing here touches the DOM and the whole chart renders under
335
+ * renderToStaticMarkup, which is how the tests exercise it.
336
+ */
337
+
338
+ /** One point on one series. `value` null where the source reported nothing for that day. */
339
+ interface SeriesPoint {
340
+ date: string;
341
+ value: number | null;
342
+ }
343
+ /** How a value should be written out. */
344
+ type SeriesUnit = 'count' | 'money' | 'percent';
345
+ /**
346
+ * One row of the chart: one answer, with what a lossier source saw underneath it.
347
+ *
348
+ * A row shows a SINGLE line by default. Two peer lines make the reader reconcile before they get
349
+ * an answer, and at a glance a foundry owner wants "how much", not "here are two measurements that
350
+ * disagree". The disagreement is still drawn — as a filled region, and in full on hover — because
351
+ * hiding it entirely would switch off the alarm: the 24 August collapse was visible precisely
352
+ * because two lines came apart.
353
+ */
354
+ interface Series {
355
+ key: string;
356
+ label: string;
357
+ /** Which upstream the LINE came from. */
358
+ source: 'GA4' | 'Vercel' | 'Sanity' | 'Mailchimp';
359
+ /** Whether the line's source sees everything. Drives the stroke and the wording. */
360
+ complete: boolean;
361
+ unit: SeriesUnit;
362
+ points: SeriesPoint[];
363
+ /**
364
+ * What a lossier source saw of the same thing.
365
+ *
366
+ * Drawn as a filled region between the two, NOT as a symmetric uncertainty band. There is no
367
+ * doubt about the traffic here: Vercel counted it server-side. What is uncertain is how much of
368
+ * it the analytics could see, and the honest way to draw that is the area it missed — a
369
+ * quantity, visible to scale, rather than a percentage on another tab.
370
+ */
371
+ shortfall?: {
372
+ label: string;
373
+ source: Series['source'];
374
+ points: SeriesPoint[];
375
+ };
376
+ /**
377
+ * Multiplier from observed to estimated-true, where the line itself is the lossy source.
378
+ *
379
+ * This one IS a symmetric band, because it is genuine uncertainty rather than a known blind
380
+ * spot. The two must not look alike: one says "we do not know exactly", the other says "we know
381
+ * exactly, and this much was invisible".
382
+ */
383
+ grossUpFactor?: number;
384
+ }
385
+ /** A dated event drawn through every row, e.g. a campaign send. */
386
+ interface TimelineMarker {
387
+ date: string;
388
+ label: string;
389
+ detail?: string;
390
+ }
391
+ /** Props for CrossSourceTimeline. */
392
+ interface CrossSourceTimelineProps {
393
+ series: Series[];
394
+ markers?: TimelineMarker[];
395
+ /** ISO 4217 code for any `money` series. */
396
+ currency?: string | null;
397
+ }
398
+ /**
399
+ * The cross-source timeline.
400
+ *
401
+ * Renders nothing rather than an empty frame when there is not enough to plot — two points cannot
402
+ * show a shape, and an axis with one dot on it invites a reading it cannot support.
403
+ */
404
+ declare function CrossSourceTimeline({ series, markers, currency }: CrossSourceTimelineProps): React.ReactElement | null;
405
+
288
406
  /**
289
407
  * The four report panels.
290
408
  *
@@ -375,4 +493,4 @@ interface VisitorInsightsPluginOptions {
375
493
  */
376
494
  declare const visitorInsights: sanity.Plugin<VisitorInsightsPluginOptions>;
377
495
 
378
- export { AcquisitionData, AcquisitionPanel, ComparisonBar, Delta, type DeltaProps, DiagnosticReport, DiagnosticsPanel, FunnelChart, JourneyData, JourneyPanel, MeasurementHealthData, MeasurementHealthPanel, MetricFigure, MetricValue, NoticeList, RangeKey, ReportEnvelope, ReportName, type ReportState, type SortColumn, SortableTable, type SortableTableProps, TrendChart, TypefaceInterestData, TypefaceInterestPanel, type UseReportOptions, type VisitorInsightsPluginOptions, VisitorInsightsTool, type VisitorInsightsToolProps, visitorInsights as default, formatCount, formatMoney, formatPercent, useReport, visitorInsights };
496
+ export { AcquisitionData, AcquisitionPanel, ComparisonBar, CrossSourceTimeline, type CrossSourceTimelineProps, Delta, type DeltaProps, DiagnosticReport, DiagnosticsPanel, FunnelChart, JourneyData, JourneyPanel, MeasurementHealthData, MeasurementHealthPanel, MetricFigure, MetricValue, NoticeList, type ProportionBar, ProportionChart, type ProportionChartProps, RangeKey, ReportEnvelope, ReportName, type ReportState, type Series, type SeriesPoint, type SortColumn, SortableTable, type SortableTableProps, type TimelineMarker, TrendChart, TypefaceInterestData, TypefaceInterestPanel, type UseReportOptions, type VisitorInsightsPluginOptions, VisitorInsightsTool, type VisitorInsightsToolProps, visitorInsights as default, formatCount, formatMoney, formatPercent, useReport, visitorInsights };
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, D as DiagnosticReport, J as JourneyData, c as MeasurementHealthData, T as TypefaceInterestData } from './ranges-DvICo5H2.js';
4
- export { C as CaptureBasis, d as CaptureEstimate, e as CaptureModel, f as CheckStatus, g as Coverage, h as DailyPoint, i as DateRange, j as DiagnosticCheck, E as EmailCampaign, k as EventCutover, l as JourneyOutcome, m as JourneyStep, L as LandingPage, P as PREEXISTING, n as REPORT_NAMES, o as ReportError, S as SiteAnalyticsConfig, p as SourceName, q as SourceRow, r as SourceStatus, s as TypefaceInterestRow, U as UnavailableReason, t as coverageForRange, u as estimated, v as isReportName, w as ok, x as partial, y as previousRange, z as resolveRange, B as unavailable, F as validateSiteConfig, G as valueOrNull } from './ranges-DvICo5H2.js';
3
+ import { R as ReportEnvelope, a as ReportName, b as RangeKey, M as MetricValue, A as AcquisitionData, D as DiagnosticReport, J as JourneyData, c as MeasurementHealthData, T as TypefaceInterestData } from './ranges-B_OrmcdP.js';
4
+ export { C as CaptureBasis, d as CaptureEstimate, e as CaptureModel, f as CheckStatus, g as Coverage, h as CrossSourceDay, i as DailyPoint, j as DateRange, k as DiagnosticCheck, E as EmailCampaign, l as EventCutover, m as JourneyOutcome, 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-B_OrmcdP.js';
5
5
 
6
6
  /**
7
7
  * The Visitor Insights Studio tool.
@@ -162,6 +162,33 @@ interface ComparisonBarProps {
162
162
  * Studio bundle and the theme-token bridging that a chart library would need for light and dark.
163
163
  */
164
164
  declare function ComparisonBar({ label, metric, max, tone }: ComparisonBarProps): React.ReactElement;
165
+ /** One bar of a proportion chart. */
166
+ interface ProportionBar {
167
+ key: string;
168
+ label: string;
169
+ sublabel?: string;
170
+ value: number;
171
+ }
172
+ /** Props for ProportionChart. */
173
+ interface ProportionChartProps {
174
+ bars: ProportionBar[];
175
+ /** How to write each value out. */
176
+ format: (value: number) => string;
177
+ /** What the bars sum to, named — a share is meaningless without its denominator stated. */
178
+ totalLabel: string;
179
+ }
180
+ /**
181
+ * A ranked part-to-whole bar chart.
182
+ *
183
+ * For a breakdown whose rows sum to something meaningful — licence revenue by tier, say. Each bar
184
+ * carries its share of the total AND its absolute value, because a share alone hides that the
185
+ * leading row might be two orders, and an absolute alone hides that it is most of the business.
186
+ *
187
+ * Bars scale against the SUM rather than against the largest row, so the widths read as shares of
188
+ * the whole. Scaling to the max would make the top row full-width whatever it was worth, which is
189
+ * the same misreading the funnel avoids by anchoring to its entry step.
190
+ */
191
+ declare function ProportionChart({ bars, format, totalLabel }: ProportionChartProps): React.ReactElement | null;
165
192
  /** One rung of the funnel, already filtered to steps that are actually measured. */
166
193
  interface FunnelStage {
167
194
  key: string;
@@ -285,6 +312,97 @@ interface SortableTableProps<Row> {
285
312
  */
286
313
  declare function SortableTable<Row>({ caption, columns, rows, rowKey, initialSort, filterPlaceholder, filterOn, exportName, truncatedNote, }: SortableTableProps<Row>): React.ReactElement;
287
314
 
315
+ /**
316
+ * One time axis, every source stacked against it.
317
+ *
318
+ * This is the only view in the tool that answers a question no single source can: did the thing we
319
+ * did move the thing we care about. A campaign goes out on Tuesday — did traffic rise, did revenue
320
+ * follow, and did GA4 even see it. Vercel knows the traffic, Sanity knows the money, Mailchimp
321
+ * knows the send date, and GA4 knows a lossy fraction of the behaviour in between.
322
+ *
323
+ * SMALL MULTIPLES, NOT A DUAL AXIS. Putting pageviews and revenue on one pair of axes requires
324
+ * choosing a scale factor between them, and whatever is chosen manufactures a visual correlation
325
+ * that the data did not claim — two lines can be made to cross, diverge or track by nothing more
326
+ * than the ratio picked. Stacked rows sharing one x axis show the same co-movement and assert
327
+ * nothing about relative magnitude, because each row carries its own y axis and its own units.
328
+ *
329
+ * COMPLETENESS IS DRAWN. A series from a source that misses things is dashed and carries a shaded
330
+ * band up to its estimated true value; a complete source is a solid line. The reader learns which
331
+ * numbers are facts and which are a fifth of the facts without being told twice.
332
+ *
333
+ * d3 is used for scales and path generation only — pure functions in, path strings out. No
334
+ * d3-selection, so nothing here touches the DOM and the whole chart renders under
335
+ * renderToStaticMarkup, which is how the tests exercise it.
336
+ */
337
+
338
+ /** One point on one series. `value` null where the source reported nothing for that day. */
339
+ interface SeriesPoint {
340
+ date: string;
341
+ value: number | null;
342
+ }
343
+ /** How a value should be written out. */
344
+ type SeriesUnit = 'count' | 'money' | 'percent';
345
+ /**
346
+ * One row of the chart: one answer, with what a lossier source saw underneath it.
347
+ *
348
+ * A row shows a SINGLE line by default. Two peer lines make the reader reconcile before they get
349
+ * an answer, and at a glance a foundry owner wants "how much", not "here are two measurements that
350
+ * disagree". The disagreement is still drawn — as a filled region, and in full on hover — because
351
+ * hiding it entirely would switch off the alarm: the 24 August collapse was visible precisely
352
+ * because two lines came apart.
353
+ */
354
+ interface Series {
355
+ key: string;
356
+ label: string;
357
+ /** Which upstream the LINE came from. */
358
+ source: 'GA4' | 'Vercel' | 'Sanity' | 'Mailchimp';
359
+ /** Whether the line's source sees everything. Drives the stroke and the wording. */
360
+ complete: boolean;
361
+ unit: SeriesUnit;
362
+ points: SeriesPoint[];
363
+ /**
364
+ * What a lossier source saw of the same thing.
365
+ *
366
+ * Drawn as a filled region between the two, NOT as a symmetric uncertainty band. There is no
367
+ * doubt about the traffic here: Vercel counted it server-side. What is uncertain is how much of
368
+ * it the analytics could see, and the honest way to draw that is the area it missed — a
369
+ * quantity, visible to scale, rather than a percentage on another tab.
370
+ */
371
+ shortfall?: {
372
+ label: string;
373
+ source: Series['source'];
374
+ points: SeriesPoint[];
375
+ };
376
+ /**
377
+ * Multiplier from observed to estimated-true, where the line itself is the lossy source.
378
+ *
379
+ * This one IS a symmetric band, because it is genuine uncertainty rather than a known blind
380
+ * spot. The two must not look alike: one says "we do not know exactly", the other says "we know
381
+ * exactly, and this much was invisible".
382
+ */
383
+ grossUpFactor?: number;
384
+ }
385
+ /** A dated event drawn through every row, e.g. a campaign send. */
386
+ interface TimelineMarker {
387
+ date: string;
388
+ label: string;
389
+ detail?: string;
390
+ }
391
+ /** Props for CrossSourceTimeline. */
392
+ interface CrossSourceTimelineProps {
393
+ series: Series[];
394
+ markers?: TimelineMarker[];
395
+ /** ISO 4217 code for any `money` series. */
396
+ currency?: string | null;
397
+ }
398
+ /**
399
+ * The cross-source timeline.
400
+ *
401
+ * Renders nothing rather than an empty frame when there is not enough to plot — two points cannot
402
+ * show a shape, and an axis with one dot on it invites a reading it cannot support.
403
+ */
404
+ declare function CrossSourceTimeline({ series, markers, currency }: CrossSourceTimelineProps): React.ReactElement | null;
405
+
288
406
  /**
289
407
  * The four report panels.
290
408
  *
@@ -375,4 +493,4 @@ interface VisitorInsightsPluginOptions {
375
493
  */
376
494
  declare const visitorInsights: sanity.Plugin<VisitorInsightsPluginOptions>;
377
495
 
378
- export { AcquisitionData, AcquisitionPanel, ComparisonBar, Delta, type DeltaProps, DiagnosticReport, DiagnosticsPanel, FunnelChart, JourneyData, JourneyPanel, MeasurementHealthData, MeasurementHealthPanel, MetricFigure, MetricValue, NoticeList, RangeKey, ReportEnvelope, ReportName, type ReportState, type SortColumn, SortableTable, type SortableTableProps, TrendChart, TypefaceInterestData, TypefaceInterestPanel, type UseReportOptions, type VisitorInsightsPluginOptions, VisitorInsightsTool, type VisitorInsightsToolProps, visitorInsights as default, formatCount, formatMoney, formatPercent, useReport, visitorInsights };
496
+ export { AcquisitionData, AcquisitionPanel, ComparisonBar, CrossSourceTimeline, type CrossSourceTimelineProps, Delta, type DeltaProps, DiagnosticReport, DiagnosticsPanel, FunnelChart, JourneyData, JourneyPanel, MeasurementHealthData, MeasurementHealthPanel, MetricFigure, MetricValue, NoticeList, type ProportionBar, ProportionChart, type ProportionChartProps, RangeKey, ReportEnvelope, ReportName, type ReportState, type Series, type SeriesPoint, type SortColumn, SortableTable, type SortableTableProps, type TimelineMarker, TrendChart, TypefaceInterestData, TypefaceInterestPanel, type UseReportOptions, type VisitorInsightsPluginOptions, VisitorInsightsTool, type VisitorInsightsToolProps, visitorInsights as default, formatCount, formatMoney, formatPercent, useReport, visitorInsights };