@parhelia/core 0.4.12873 → 0.4.12877
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/config/config.js +9 -2
- package/dist/config/config.js.map +1 -1
- package/dist/editor/client/EditorShell.js +8 -3
- package/dist/editor/client/EditorShell.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +5 -0
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/reviews/commentTargetPicker.d.ts +4 -0
- package/dist/editor/reviews/commentTargetPicker.js +4 -0
- package/dist/editor/reviews/commentTargetPicker.js.map +1 -1
- package/dist/editor/services/analyticsService.d.ts +146 -0
- package/dist/editor/services/analyticsService.js +23 -0
- package/dist/editor/services/analyticsService.js.map +1 -0
- package/dist/editor/settings/panels/McpAccessPanel.d.ts +3 -3
- package/dist/editor/settings/panels/McpAccessPanel.js +18 -38
- package/dist/editor/settings/panels/McpAccessPanel.js.map +1 -1
- package/dist/editor/settings/panels/McpGrantList.d.ts +8 -0
- package/dist/editor/settings/panels/McpGrantList.js +21 -0
- package/dist/editor/settings/panels/McpGrantList.js.map +1 -0
- package/dist/editor/settings/panels/McpServerPanel.d.ts +7 -0
- package/dist/editor/settings/panels/McpServerPanel.js +84 -0
- package/dist/editor/settings/panels/McpServerPanel.js.map +1 -0
- package/dist/editor/settings/panels/index.d.ts +1 -0
- package/dist/editor/settings/panels/index.js +1 -0
- package/dist/editor/settings/panels/index.js.map +1 -1
- package/dist/editor/sidebar/analytics/PagePerformance.d.ts +6 -0
- package/dist/editor/sidebar/analytics/PagePerformance.js +188 -0
- package/dist/editor/sidebar/analytics/PagePerformance.js.map +1 -0
- package/dist/editor/sidebar/analytics/RangePicker.d.ts +10 -0
- package/dist/editor/sidebar/analytics/RangePicker.js +58 -0
- package/dist/editor/sidebar/analytics/RangePicker.js.map +1 -0
- package/dist/editor/sidebar/analytics/analyticsFormat.d.ts +10 -0
- package/dist/editor/sidebar/analytics/analyticsFormat.js +57 -0
- package/dist/editor/sidebar/analytics/analyticsFormat.js.map +1 -0
- package/dist/editor/sidebar/analytics/analyticsRange.d.ts +69 -0
- package/dist/editor/sidebar/analytics/analyticsRange.js +134 -0
- package/dist/editor/sidebar/analytics/analyticsRange.js.map +1 -0
- package/dist/editor/ui/PublishDialog.d.ts +5 -1
- package/dist/editor/ui/PublishDialog.js +98 -17
- package/dist/editor/ui/PublishDialog.js.map +1 -1
- package/dist/editor/ui/WorkboxItemsTable.d.ts +17 -2
- package/dist/editor/ui/WorkboxItemsTable.js +26 -12
- package/dist/editor/ui/WorkboxItemsTable.js.map +1 -1
- package/dist/editor/ui/publishReadiness.d.ts +11 -1
- package/dist/editor/ui/publishReadiness.js +34 -30
- package/dist/editor/ui/publishReadiness.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useEffect, useState } from "react";
|
|
4
|
+
import { ExternalLink, Info, RefreshCw, TrendingDown, TrendingUp, } from "lucide-react";
|
|
5
|
+
import { useEditContext } from "../../client/editContext";
|
|
6
|
+
import { usePageViewState } from "../../page-viewer/pageViewContext";
|
|
7
|
+
import { SimpleToolbar } from "../../ui/SimpleToolbar";
|
|
8
|
+
import { SimpleIconButton } from "../../ui/SimpleIconButton";
|
|
9
|
+
import { LoadingState } from "../../ui/LoadingState";
|
|
10
|
+
import { Alert, AlertDescription, AlertTitle, } from "../../../components/ui/alert";
|
|
11
|
+
import { cn } from "../../../lib/utils";
|
|
12
|
+
import { getPageAnalytics, } from "../../services/analyticsService";
|
|
13
|
+
import { changeTone, formatChange, formatCount, formatDate, formatMetricValue, share, } from "./analyticsFormat";
|
|
14
|
+
import { DEFAULT_RANGE, PUBLISH_WINDOW_MIN_DAYS, comparisonLabel, parseRange, publishWindows, serializeRange, toRangeParams, trendIndexOf, } from "./analyticsRange";
|
|
15
|
+
import { RangePicker } from "./RangePicker";
|
|
16
|
+
import { localStorageService } from "../../services/localStorageService";
|
|
17
|
+
const RANGE_STORAGE_KEY = "analytics.range";
|
|
18
|
+
/**
|
|
19
|
+
* "How much is this page used?" — GA4 metrics for the current item, site and language, plus
|
|
20
|
+
* Search Console when configured. Every number shows its definition and window; "no data" is
|
|
21
|
+
* never shown as zero.
|
|
22
|
+
*/
|
|
23
|
+
export function PagePerformance() {
|
|
24
|
+
const editContext = useEditContext();
|
|
25
|
+
const pageContext = editContext?.workspaceId === "editor"
|
|
26
|
+
? editContext.getActiveSlotContext()?.primaryPageViewContext
|
|
27
|
+
: undefined;
|
|
28
|
+
const siteName = usePageViewState(pageContext, (state) => state.site?.name);
|
|
29
|
+
const descriptor = editContext?.currentItemDescriptor;
|
|
30
|
+
const [report, setReport] = useState();
|
|
31
|
+
const [error, setError] = useState();
|
|
32
|
+
const [loading, setLoading] = useState(false);
|
|
33
|
+
const [reloadKey, setReloadKey] = useState(0);
|
|
34
|
+
const [selection, setSelection] = useState(() => parseRange(localStorageService.getString(RANGE_STORAGE_KEY)));
|
|
35
|
+
// Before/after leaves this view; remember where to go back to.
|
|
36
|
+
const [returnTo, setReturnTo] = useState(DEFAULT_RANGE);
|
|
37
|
+
const [lastCompleteDay, setLastCompleteDay] = useState();
|
|
38
|
+
const itemId = descriptor?.id;
|
|
39
|
+
const language = descriptor?.language;
|
|
40
|
+
const rangeParams = toRangeParams(selection, lastCompleteDay);
|
|
41
|
+
const rangeKey = rangeParams ? JSON.stringify(rangeParams) : null;
|
|
42
|
+
const changeSelection = useCallback((next) => {
|
|
43
|
+
if (next.kind === "publish" && selection.kind !== "publish")
|
|
44
|
+
setReturnTo(selection);
|
|
45
|
+
const stored = serializeRange(next);
|
|
46
|
+
if (stored)
|
|
47
|
+
localStorageService.setString(RANGE_STORAGE_KEY, stored);
|
|
48
|
+
setSelection(next);
|
|
49
|
+
}, [selection]);
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (!itemId || !language || !rangeKey)
|
|
52
|
+
return;
|
|
53
|
+
const controller = new AbortController();
|
|
54
|
+
setLoading(true);
|
|
55
|
+
setError(undefined);
|
|
56
|
+
getPageAnalytics(itemId, language, siteName, controller.signal, JSON.parse(rangeKey))
|
|
57
|
+
.then((data) => {
|
|
58
|
+
if (controller.signal.aborted)
|
|
59
|
+
return;
|
|
60
|
+
setReport(data);
|
|
61
|
+
if (data.window?.lastCompleteDay)
|
|
62
|
+
setLastCompleteDay(data.window.lastCompleteDay);
|
|
63
|
+
})
|
|
64
|
+
.catch((e) => {
|
|
65
|
+
if (!controller.signal.aborted) {
|
|
66
|
+
setReport(undefined);
|
|
67
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
.finally(() => {
|
|
71
|
+
if (!controller.signal.aborted)
|
|
72
|
+
setLoading(false);
|
|
73
|
+
});
|
|
74
|
+
return () => controller.abort();
|
|
75
|
+
}, [itemId, language, siteName, rangeKey, reloadKey]);
|
|
76
|
+
const reload = useCallback(() => setReloadKey((k) => k + 1), []);
|
|
77
|
+
const publishWait = selection.kind === "publish" && lastCompleteDay
|
|
78
|
+
? publishWindows(selection.publishDate, lastCompleteDay)
|
|
79
|
+
: undefined;
|
|
80
|
+
if (!editContext)
|
|
81
|
+
return null;
|
|
82
|
+
if (editContext.user?.isLimitedPreviewUser)
|
|
83
|
+
return null;
|
|
84
|
+
return (_jsxs("div", { className: "absolute inset-0 flex flex-col", "data-testid": "page-performance-panel", children: [_jsxs(SimpleToolbar, { children: [_jsx(RangePicker, { selection: selection, lastCompleteDay: lastCompleteDay, onChange: changeSelection }), _jsx("div", { className: "ml-auto" }), _jsx(SimpleIconButton, { onClick: reload, icon: _jsx(RefreshCw, { className: cn("h-4 w-4", loading && "animate-spin"), strokeWidth: 1 }), label: "Reload", "data-testid": "page-performance-reload" })] }), _jsxs("div", { className: "flex-1 overflow-y-auto p-3 text-xs", children: [selection.kind === "publish" && (_jsx(PublishComparisonNote, { publishDate: selection.publishDate, days: publishWait?.ok ? publishWait.days : undefined, onBack: () => changeSelection(returnTo) })), !itemId ? (_jsx(EmptyState, { children: "Open a page to see how it is used." })) : publishWait && !publishWait.ok ? (_jsxs(EmptyState, { children: ["Only ", publishWait.daysAvailable, " complete", " ", publishWait.daysAvailable === 1 ? "day" : "days", " since this publish. Before and after needs at least ", PUBLISH_WINDOW_MIN_DAYS, "; check back later."] })) : loading && !report ? (_jsx(LoadingState, { variant: "fill", message: "Loading analytics\u2026" })) : error ? (_jsxs(Alert, { variant: "destructive", className: "px-3 py-2 text-xs", children: [_jsx(AlertTitle, { children: "Analytics could not be loaded" }), _jsx(AlertDescription, { className: "text-xs", children: error })] })) : report ? (_jsx(ReportView, { report: report, onComparePublish: (publishDate) => changeSelection({ kind: "publish", publishDate }) })) : null] })] }));
|
|
85
|
+
}
|
|
86
|
+
function ReportView({ report, onComparePublish, }) {
|
|
87
|
+
const hasGa4 = report.status === "ok";
|
|
88
|
+
const window = report.window;
|
|
89
|
+
const weekly = window?.granularity === "week";
|
|
90
|
+
const compareLabel = window?.previous
|
|
91
|
+
? comparisonLabel(window.compare)
|
|
92
|
+
: undefined;
|
|
93
|
+
const publishesInRange = report.publishEvents.filter((p) => trendIndexOf(report.trend, p.date, window?.granularity) >= 0);
|
|
94
|
+
return (_jsxs("div", { className: "flex flex-col gap-4", children: [window && (_jsxs("p", { className: "text-neutral-grey-50 text-xs", "data-testid": "page-performance-window", children: [formatDate(window.current.startDate), " \u2013", " ", formatDate(window.current.endDate), window.previous && (_jsxs(_Fragment, { children: [" ", "vs ", formatDate(window.previous.startDate), " \u2013", " ", formatDate(window.previous.endDate)] }))] })), !hasGa4 && (_jsx(StatusAlert, { status: report.status, message: report.message, source: "GA4" })), hasGa4 && (_jsxs(_Fragment, { children: [_jsx("div", { className: "grid grid-cols-2 gap-2", children: report.headline.map((metric) => (_jsx(MetricTile, { metric: metric, compareLabel: compareLabel }, metric.key))) }), _jsxs(Section, { title: weekly ? "Weekly views" : "Daily views", children: [_jsx(TrendChart, { points: report.trend, publishDates: publishesInRange.map((p) => p.date), granularity: window?.granularity, onPublishClick: onComparePublish }), publishesInRange.length > 0 && window?.compare !== "custom" && (_jsxs("div", { className: "mt-1 flex flex-col gap-1", children: [_jsx("p", { className: "text-neutral-grey-50 text-xs", children: "Dashed marks: published from Parhelia. Context, not the cause of a change." }), publishesInRange.slice(0, 3).map((p) => (_jsxs("button", { type: "button", className: "text-highlight-100 self-start text-xs hover:underline", "data-testid": "page-performance-publish-compare", onClick: () => onComparePublish(p.date), children: ["Compare before and after the ", formatDate(p.date), " publish"] }, p.date)))] }))] }), report.keyEvents.length > 0 && (_jsx(Section, { title: "Key events on this page", children: _jsx("table", { className: "w-full text-xs", children: _jsx("tbody", { children: report.keyEvents.map((event) => (_jsxs("tr", { className: "border-border-default border-b last:border-0", children: [_jsx("td", { className: "py-1 font-mono", children: event.name }), _jsx("td", { className: "py-1 text-right", children: formatCount(event.current) }), compareLabel && (_jsxs("td", { className: "text-neutral-grey-50 py-1 pl-2 text-right", children: [compareLabel, " ", formatCount(event.previous ?? 0)] }))] }, event.name))) }) }) })), _jsx(Section, { title: "Traffic sources", children: _jsx(Breakdown, { rows: report.channels }) }), _jsx(Section, { title: "Devices", children: _jsx(Breakdown, { rows: report.devices }) }), report.countries.length > 0 && (_jsx(Section, { title: "Countries", children: _jsx(Breakdown, { rows: report.countries }) })), _jsx(Section, { title: "Sessions including this page", hint: "These describe whole sessions in which this page was viewed, not this page's effectiveness.", children: _jsx("div", { className: "flex flex-col gap-1", children: report.sessionContext.map((metric) => (_jsx(MetricRow, { metric: metric }, metric.key))) }) }), (report.scroll90Events ?? 0) > 0 ||
|
|
95
|
+
report.outboundClicks.length > 0 ? (_jsxs(Section, { title: "Interactions", children: [report.scroll90Events !== undefined && (_jsxs("div", { className: "flex justify-between text-xs", title: "Enhanced measurement fires scroll once, at 90% depth.", children: [_jsx("span", { children: "Scrolled to 90%" }), _jsx("span", { children: formatCount(report.scroll90Events) })] })), report.outboundClicks.length > 0 && (_jsx("div", { className: "text-neutral-grey-50 mt-1 text-xs", children: "Outbound clicks" })), report.outboundClicks.map((click) => (_jsxs("div", { className: "flex justify-between gap-2 text-xs", children: [_jsx("span", { className: "truncate", title: click.name, children: click.name }), _jsx("span", { children: formatCount(click.current) })] }, click.name)))] })) : null, _jsx(Quality, { report: report })] })), report.search && _jsx(SearchSection, { search: report.search }), report.clarity && _jsx(ClaritySection, { clarity: report.clarity }), _jsx(Footer, { report: report })] }));
|
|
96
|
+
}
|
|
97
|
+
function MetricTile({ metric, compareLabel, }) {
|
|
98
|
+
const tone = changeTone(metric);
|
|
99
|
+
const change = formatChange(metric.changePercent);
|
|
100
|
+
return (_jsxs("div", { className: "border-border-default rounded-md border p-2", title: `${metric.definition}${metric.denominator ? ` Divided by ${metric.denominator}.` : ""}`, "data-testid": `page-performance-metric-${metric.key}`, children: [_jsx("div", { className: "text-neutral-grey-50 text-xs", children: metric.label }), _jsx("div", { className: "text-lg font-medium", children: formatMetricValue(metric.current, metric.unit) }), _jsxs("div", { className: "flex items-center gap-1 text-xs", children: [change && (_jsxs("span", { className: cn("flex items-center gap-0.5", tone === "up" && "text-feedback-green", tone === "down" && "text-feedback-red", tone === "flat" && "text-neutral-grey-50"), children: [tone === "up" && (_jsx(TrendingUp, { className: "h-3 w-3", strokeWidth: 1.5 })), tone === "down" && (_jsx(TrendingDown, { className: "h-3 w-3", strokeWidth: 1.5 })), change] })), compareLabel && (_jsxs("span", { className: "text-neutral-grey-50", children: [compareLabel, " ", formatMetricValue(metric.previous, metric.unit)] }))] })] }));
|
|
101
|
+
}
|
|
102
|
+
function PublishComparisonNote({ publishDate, days, onBack, }) {
|
|
103
|
+
return (_jsxs("div", { className: "bg-neutral-grey-5 mb-3 flex flex-col gap-1 rounded-md px-3 py-2 text-xs", "data-testid": "page-performance-publish-note", children: [_jsxs("p", { children: [days ? `${days} days after` : "The days after", " the", " ", formatDate(publishDate), " publish, compared with the same number of days before. The publish day itself is left out of both."] }), _jsx("p", { className: "text-neutral-grey-50", children: "A difference is an association, not proof: seasonality, campaigns and other changes can move these numbers too." }), _jsx("button", { type: "button", className: "text-highlight-100 self-start hover:underline", "data-testid": "page-performance-publish-back", onClick: onBack, children: "Back to the previous range" })] }));
|
|
104
|
+
}
|
|
105
|
+
function MetricRow({ metric }) {
|
|
106
|
+
const change = formatChange(metric.changePercent);
|
|
107
|
+
return (_jsxs("div", { className: "flex justify-between gap-2 text-xs", title: metric.definition, children: [_jsx("span", { children: metric.label.replace(" (sessions including this page)", "") }), _jsxs("span", { children: [formatMetricValue(metric.current, metric.unit), change && (_jsxs("span", { className: "text-neutral-grey-50 ml-1", children: ["(", change, ")"] }))] })] }));
|
|
108
|
+
}
|
|
109
|
+
function TrendChart({ points, publishDates, granularity, onPublishClick, }) {
|
|
110
|
+
if (points.length === 0)
|
|
111
|
+
return _jsx(EmptyState, { children: "No data in this range." });
|
|
112
|
+
const width = 280;
|
|
113
|
+
const height = 64;
|
|
114
|
+
const max = Math.max(1, ...points.map((p) => p.views));
|
|
115
|
+
const step = points.length > 1 ? width / (points.length - 1) : width;
|
|
116
|
+
const coordinates = points.map((p, i) => [
|
|
117
|
+
i * step,
|
|
118
|
+
height - (p.views / max) * (height - 4) - 2,
|
|
119
|
+
]);
|
|
120
|
+
const line = coordinates
|
|
121
|
+
.map(([x, y]) => `${x.toFixed(1)},${y.toFixed(1)}`)
|
|
122
|
+
.join(" ");
|
|
123
|
+
const markers = publishDates
|
|
124
|
+
.map((date) => ({ date, index: trendIndexOf(points, date, granularity) }))
|
|
125
|
+
.filter((m) => m.index >= 0)
|
|
126
|
+
.map((m) => ({ date: m.date, x: m.index * step }));
|
|
127
|
+
const unit = granularity === "week" ? "week" : "day";
|
|
128
|
+
return (_jsxs("div", { children: [_jsxs("svg", { viewBox: `0 0 ${width} ${height}`, className: "text-highlight-100 h-16 w-full", preserveAspectRatio: "none", role: "img", "aria-label": unit === "week" ? "Weekly page views" : "Daily page views", children: [markers.map(({ date, x }) => (_jsx("line", { x1: x, x2: x, y1: 0, y2: height, stroke: "currentColor", strokeOpacity: 0.35, strokeDasharray: "2 2" }, date))), _jsx("polyline", { points: line, fill: "none", stroke: "currentColor", strokeWidth: 1.5, vectorEffect: "non-scaling-stroke" }), points.map((p, i) => (_jsx("rect", { x: i * step - step / 2, y: 0, width: step, height: height, fill: "transparent", children: _jsx("title", { children: `${unit === "week" ? "Week of " : ""}${formatDate(p.date)}: ${formatCount(p.views)} views, ${formatCount(p.users)} users` }) }, p.date))), onPublishClick &&
|
|
129
|
+
markers.map(({ date, x }) => (_jsx("rect", { x: x - 4, y: 0, width: 8, height: height, fill: "transparent", className: "cursor-pointer", "data-testid": "page-performance-publish-mark", onClick: () => onPublishClick(date), children: _jsx("title", { children: `Published ${formatDate(date)}: compare before and after` }) }, `hit-${date}`)))] }), _jsxs("div", { className: "text-neutral-grey-50 flex justify-between text-[10px]", children: [_jsx("span", { children: formatDate(points[0].date) }), _jsxs("span", { children: ["max ", formatCount(max), " views/", unit] }), _jsx("span", { children: formatDate(points[points.length - 1].date) })] })] }));
|
|
130
|
+
}
|
|
131
|
+
function Breakdown({ rows }) {
|
|
132
|
+
if (rows.length === 0)
|
|
133
|
+
return _jsx(EmptyState, { children: "No data." });
|
|
134
|
+
const total = rows.reduce((sum, r) => sum + r.views, 0);
|
|
135
|
+
return (_jsx("div", { className: "flex flex-col gap-1", children: rows.map((row) => (_jsxs("div", { className: "text-xs", title: `${formatCount(row.views)} views, ${formatCount(row.users)} users`, children: [_jsxs("div", { className: "flex justify-between", children: [_jsx("span", { className: "truncate", children: row.label }), _jsxs("span", { className: "text-neutral-grey-50", children: [formatCount(row.views), " \u00B7 ", share(row.views, total), "%"] })] }), _jsx("div", { className: "bg-neutral-grey-5 h-1 rounded", children: _jsx("div", { className: "bg-highlight-100 h-1 rounded", style: { width: `${share(row.views, total)}%` } }) })] }, row.label))) }));
|
|
136
|
+
}
|
|
137
|
+
function SearchSection({ search }) {
|
|
138
|
+
return (_jsx(Section, { title: "Google Search", hint: search.current
|
|
139
|
+
? `${formatDate(search.current.startDate)} – ${formatDate(search.current.endDate)}, final data`
|
|
140
|
+
: undefined, children: search.status !== "ok" ? (_jsx(StatusAlert, { status: search.status, message: search.message, source: "Search Console" })) : (_jsxs("div", { className: "flex flex-col gap-3", children: [_jsx("div", { className: "grid grid-cols-2 gap-2", children: search.totals.map((metric) => (_jsx(MetricTile, { metric: metric, compareLabel: search.previous ? "prev." : undefined }, metric.key))) }), search.topQueries.length > 0 && (_jsxs("div", { children: [_jsxs("table", { className: "w-full text-xs", "data-testid": "page-performance-queries", children: [_jsx("thead", { children: _jsxs("tr", { className: "text-neutral-grey-50 text-left", children: [_jsx("th", { className: "py-1 font-normal", children: "Top queries" }), _jsx("th", { className: "py-1 text-right font-normal", children: "Clicks" }), _jsx("th", { className: "py-1 text-right font-normal", children: "Impr." }), _jsx("th", { className: "py-1 text-right font-normal", children: "CTR" }), _jsx("th", { className: "py-1 text-right font-normal", children: "Pos." })] }) }), _jsx("tbody", { children: search.topQueries.map((q) => (_jsxs("tr", { className: "border-border-default border-t", children: [_jsx("td", { className: "max-w-0 truncate py-1 pr-1", title: q.query, children: q.query }), _jsx("td", { className: "py-1 text-right", children: formatCount(q.clicks) }), _jsx("td", { className: "py-1 text-right", children: formatCount(q.impressions) }), _jsx("td", { className: "py-1 text-right", children: formatMetricValue(q.ctr, "ratio") }), _jsx("td", { className: "py-1 text-right", children: formatMetricValue(q.position, "position") })] }, q.query))) })] }), _jsx("p", { className: "text-neutral-grey-50 mt-1 text-xs", children: "Top rows only: Google hides rare queries, so these do not add up to the totals." })] })), search.propertyNote && (_jsx("p", { className: "text-neutral-grey-50 text-xs", "data-testid": "page-performance-search-property-note", children: search.propertyNote })), search.searchConsoleUrl && (_jsx(ExternalLinkRow, { href: search.searchConsoleUrl, children: "Open in Search Console" }))] })) }));
|
|
141
|
+
}
|
|
142
|
+
/** Behaviour signals from Microsoft Clarity: the last few days only, so no comparison. */
|
|
143
|
+
function ClaritySection({ clarity }) {
|
|
144
|
+
return (_jsx(Section, { title: "Clarity", hint: `Last ${clarity.days} days, no comparison period. Shares are of this page's Clarity sessions.`, children: _jsxs("div", { className: "flex flex-col gap-2", "data-testid": "page-performance-clarity", children: [clarity.status !== "ok" ? (_jsx(StatusAlert, { status: clarity.status, message: clarity.message, source: "Clarity" })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex flex-col gap-1 text-xs", children: [_jsx(Row, { label: "Sessions", value: formatMetricValue(clarity.sessions, "count") }), _jsx(Row, { label: "Average scroll depth", value: clarity.averageScrollDepthPercent === undefined
|
|
145
|
+
? "—"
|
|
146
|
+
: `${Math.round(clarity.averageScrollDepthPercent)}%` }), clarity.activeTimeSeconds !== undefined && (_jsx(Row, { label: "Active time", value: formatMetricValue(clarity.activeTimeSeconds, "seconds") })), clarity.signals.map((signal) => (_jsx("div", { title: signal.definition, children: _jsx(Row, { label: signal.label, value: signal.sessionsPercent === undefined
|
|
147
|
+
? formatMetricValue(signal.count, "count")
|
|
148
|
+
: `${formatMetricValue(signal.sessionsPercent / 100, "ratio")} of sessions` }) }, signal.key)))] }), clarity.notes.map((note) => (_jsx("p", { className: "text-neutral-grey-50 text-xs", children: note }, note)))] })), _jsxs("div", { className: "flex flex-wrap gap-3", children: [clarity.heatmapUrl && (_jsx(ExternalLinkRow, { href: clarity.heatmapUrl, children: "Heatmap" })), clarity.recordingsUrl && (_jsx(ExternalLinkRow, { href: clarity.recordingsUrl, children: "Recordings" }))] })] }) }));
|
|
149
|
+
}
|
|
150
|
+
function Row({ label, value }) {
|
|
151
|
+
return (_jsxs("div", { className: "flex justify-between gap-2", children: [_jsx("span", { children: label }), _jsx("span", { children: value })] }));
|
|
152
|
+
}
|
|
153
|
+
function Quality({ report }) {
|
|
154
|
+
const notes = report.quality.notes;
|
|
155
|
+
const timeZoneNote = report.window && !report.window.timeZoneResolved
|
|
156
|
+
? "Dates are shown in UTC; the property's time zone is unknown on this server."
|
|
157
|
+
: null;
|
|
158
|
+
if (notes.length === 0 && !timeZoneNote)
|
|
159
|
+
return null;
|
|
160
|
+
return (_jsxs(Alert, { variant: "info", className: "px-3 py-2 text-xs", children: [_jsx(Info, { className: "h-4 w-4", strokeWidth: 1.5 }), _jsx(AlertTitle, { children: "About these numbers" }), _jsx(AlertDescription, { className: "text-xs", children: _jsxs("ul", { className: "list-disc pl-4", children: [notes.map((note) => (_jsx("li", { children: note }, note))), timeZoneNote && _jsx("li", { children: timeZoneNote })] }) })] }));
|
|
161
|
+
}
|
|
162
|
+
function StatusAlert({ status, message, source, }) {
|
|
163
|
+
const title = status === "not-configured"
|
|
164
|
+
? `${source} is not set up for this site`
|
|
165
|
+
: status === "no-url"
|
|
166
|
+
? "This page has no public URL"
|
|
167
|
+
: status === "no-data"
|
|
168
|
+
? `${source} has no data for this page`
|
|
169
|
+
: `${source} is unavailable`;
|
|
170
|
+
return (_jsxs(Alert, { variant: status === "error"
|
|
171
|
+
? "destructive"
|
|
172
|
+
: status === "no-data"
|
|
173
|
+
? "default"
|
|
174
|
+
: "warning", className: "px-3 py-2 text-xs", children: [_jsx(AlertTitle, { children: title }), message && (_jsx(AlertDescription, { className: "text-xs", children: message }))] }));
|
|
175
|
+
}
|
|
176
|
+
function Footer({ report }) {
|
|
177
|
+
return (_jsxs("div", { className: "text-neutral-grey-50 flex flex-col gap-1 border-t pt-2 text-xs", children: [report.page?.pageUrls && report.page.pageUrls.length > 0 && (_jsxs("div", { title: report.page.pageUrls.join("\n"), children: ["Matched URL:", " ", _jsx("span", { className: "break-all", children: report.page.pageUrls[0] }), report.page.host && _jsxs(_Fragment, { children: [" on host ", report.page.host] })] })), report.window?.lastCompleteDay && (_jsxs("div", { children: ["Data through ", formatDate(report.window.lastCompleteDay), " (today and yesterday are still being processed)."] })), report.ga4ReportUrl && (_jsx(ExternalLinkRow, { href: report.ga4ReportUrl, children: "Open Google Analytics" }))] }));
|
|
178
|
+
}
|
|
179
|
+
function ExternalLinkRow({ href, children, }) {
|
|
180
|
+
return (_jsxs("a", { href: href, target: "_blank", rel: "noopener noreferrer", className: "text-highlight-100 inline-flex items-center gap-1 text-xs hover:underline", children: [children, _jsx(ExternalLink, { className: "h-3 w-3", strokeWidth: 1.5 })] }));
|
|
181
|
+
}
|
|
182
|
+
function Section({ title, hint, children, }) {
|
|
183
|
+
return (_jsxs("section", { className: "flex flex-col gap-2", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-xs font-medium tracking-wide uppercase", children: title }), hint && _jsx("p", { className: "text-neutral-grey-50 text-xs", children: hint })] }), children] }));
|
|
184
|
+
}
|
|
185
|
+
function EmptyState({ children }) {
|
|
186
|
+
return (_jsx("div", { className: "text-neutral-grey-50 py-4 text-center text-xs", children: children }));
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=PagePerformance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PagePerformance.js","sourceRoot":"","sources":["../../../../src/editor/sidebar/analytics/PagePerformance.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type RangeSelection } from "./analyticsRange";
|
|
2
|
+
/**
|
|
3
|
+
* Range and comparison menus for the Performance panel toolbar. Presets end on the last complete
|
|
4
|
+
* day; "Custom range…" opens a calendar that stops there too.
|
|
5
|
+
*/
|
|
6
|
+
export declare function RangePicker({ selection, lastCompleteDay, onChange, }: {
|
|
7
|
+
selection: RangeSelection;
|
|
8
|
+
lastCompleteDay?: string;
|
|
9
|
+
onChange: (selection: RangeSelection) => void;
|
|
10
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { CalendarDays, Check, ChevronDown } from "lucide-react";
|
|
5
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "../../../components/ui/dropdown-menu";
|
|
6
|
+
import { Popover, PopoverAnchor, PopoverContent, } from "../../../components/ui/popover";
|
|
7
|
+
import { Calendar } from "../../../components/ui/calendar";
|
|
8
|
+
import { Button } from "../../../components/ui/button";
|
|
9
|
+
import { cn } from "../../../lib/utils";
|
|
10
|
+
import { formatDate } from "./analyticsFormat";
|
|
11
|
+
import { COMPARISONS, RANGE_PRESETS, addDays, daysBetween, parseIsoDate, rangeLabel, toIsoDate, } from "./analyticsRange";
|
|
12
|
+
const triggerClass = "text-neutral-grey-100 hover:bg-neutral-grey-5 flex h-6 items-center gap-1 rounded px-1.5 text-xs whitespace-nowrap";
|
|
13
|
+
/**
|
|
14
|
+
* Range and comparison menus for the Performance panel toolbar. Presets end on the last complete
|
|
15
|
+
* day; "Custom range…" opens a calendar that stops there too.
|
|
16
|
+
*/
|
|
17
|
+
export function RangePicker({ selection, lastCompleteDay, onChange, }) {
|
|
18
|
+
const [calendarOpen, setCalendarOpen] = useState(false);
|
|
19
|
+
const compare = selection.kind === "publish" ? "custom" : selection.compare;
|
|
20
|
+
const setCompare = (value) => {
|
|
21
|
+
if (selection.kind === "preset" || selection.kind === "custom")
|
|
22
|
+
onChange({ ...selection, compare: value });
|
|
23
|
+
};
|
|
24
|
+
return (_jsxs("div", { className: "flex min-w-0 items-center gap-0.5", children: [_jsxs(Popover, { open: calendarOpen, onOpenChange: setCalendarOpen, children: [_jsxs(DropdownMenu, { children: [_jsx(PopoverAnchor, { asChild: true, children: _jsxs(DropdownMenuTrigger, { className: triggerClass, "data-testid": "page-performance-range", children: [_jsx(CalendarDays, { className: "h-3.5 w-3.5", strokeWidth: 1.5 }), _jsx("span", { className: "truncate", children: rangeLabel(selection) }), _jsx(ChevronDown, { className: "h-3 w-3", strokeWidth: 1.5 })] }) }), _jsxs(DropdownMenuContent, { align: "start", className: "w-44 text-xs", children: [RANGE_PRESETS.map((preset) => {
|
|
25
|
+
const active = selection.kind === "preset" && selection.days === preset.days;
|
|
26
|
+
return (_jsxs(DropdownMenuItem, { className: "text-xs", "data-testid": `page-performance-range-${preset.days}`, onSelect: () => onChange({
|
|
27
|
+
kind: "preset",
|
|
28
|
+
days: preset.days,
|
|
29
|
+
compare: compare === "custom" ? "previous" : compare,
|
|
30
|
+
}), children: [_jsx(Check, { className: cn("h-3 w-3", !active && "invisible"), strokeWidth: 1.5 }), preset.label] }, preset.days));
|
|
31
|
+
}), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { className: "text-xs", "data-testid": "page-performance-range-custom",
|
|
32
|
+
// Open after the menu has closed and returned focus; otherwise that
|
|
33
|
+
// focus change counts as an outside interaction and closes the calendar.
|
|
34
|
+
onSelect: () => window.setTimeout(() => setCalendarOpen(true), 0), children: [_jsx(Check, { className: cn("h-3 w-3", selection.kind !== "custom" && "invisible"), strokeWidth: 1.5 }), "Custom range\u2026"] })] })] }), _jsx(PopoverContent, { align: "start", className: "w-auto p-0",
|
|
35
|
+
// The menu hands focus back to its trigger as it closes; that must not close the calendar.
|
|
36
|
+
onFocusOutside: (event) => event.preventDefault(), children: _jsx(CustomRangeCalendar, { selection: selection, lastCompleteDay: lastCompleteDay, onApply: (start, end) => {
|
|
37
|
+
setCalendarOpen(false);
|
|
38
|
+
onChange({
|
|
39
|
+
kind: "custom",
|
|
40
|
+
start,
|
|
41
|
+
end,
|
|
42
|
+
compare: compare === "custom" ? "previous" : compare,
|
|
43
|
+
});
|
|
44
|
+
} }) })] }), selection.kind !== "publish" && (_jsxs(DropdownMenu, { children: [_jsxs(DropdownMenuTrigger, { className: cn(triggerClass, "text-neutral-grey-50"), "data-testid": "page-performance-compare", children: [_jsx("span", { className: "truncate", children: COMPARISONS.find((c) => c.value === compare)?.label }), _jsx(ChevronDown, { className: "h-3 w-3", strokeWidth: 1.5 })] }), _jsx(DropdownMenuContent, { align: "start", className: "w-52 text-xs", children: COMPARISONS.map((option) => (_jsxs(DropdownMenuItem, { className: "text-xs", "data-testid": `page-performance-compare-${option.value}`, onSelect: () => setCompare(option.value), children: [_jsx(Check, { className: cn("h-3 w-3", option.value !== compare && "invisible"), strokeWidth: 1.5 }), option.label] }, option.value))) })] }))] }));
|
|
45
|
+
}
|
|
46
|
+
function CustomRangeCalendar({ selection, lastCompleteDay, onApply, }) {
|
|
47
|
+
const latest = lastCompleteDay ?? toIsoDate(new Date(Date.now() - 2 * 864e5));
|
|
48
|
+
const [range, setRange] = useState(() => selection.kind === "custom"
|
|
49
|
+
? { from: parseIsoDate(selection.start), to: parseIsoDate(selection.end) }
|
|
50
|
+
: { from: parseIsoDate(addDays(latest, -27)), to: parseIsoDate(latest) });
|
|
51
|
+
const from = range?.from ? toIsoDate(range.from) : undefined;
|
|
52
|
+
const to = range?.to ? toIsoDate(range.to) : from;
|
|
53
|
+
const days = from && to ? daysBetween(from, to) + 1 : 0;
|
|
54
|
+
return (_jsxs("div", { className: "flex flex-col", "data-testid": "page-performance-calendar", children: [_jsx(Calendar, { mode: "range", selected: range, onSelect: setRange, defaultMonth: parseIsoDate(latest), endMonth: parseIsoDate(latest), disabled: { after: parseIsoDate(latest) }, max: 730, timeZone: "UTC" }), _jsxs("div", { className: "border-border-default flex items-center justify-between gap-3 border-t px-3 py-2 text-xs", children: [_jsx("span", { className: "text-neutral-grey-50", children: from && to
|
|
55
|
+
? `${formatDate(from)} – ${formatDate(to)} · ${days} days`
|
|
56
|
+
: "Pick a start and an end day" }), _jsx(Button, { size: "sm", disabled: !from || !to, "data-testid": "page-performance-calendar-apply", onClick: () => from && to && onApply(from, to), children: "Apply" })] }), _jsxs("p", { className: "text-neutral-grey-50 px-3 pb-2 text-[11px]", children: ["Data ends ", formatDate(latest), ": today and yesterday are still being processed."] })] }));
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=RangePicker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RangePicker.js","sourceRoot":"","sources":["../../../../src/editor/sidebar/analytics/RangePicker.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AnalyticsMetric } from "../../services/analyticsService";
|
|
2
|
+
/** "No data" (undefined) is shown as an em dash, never as 0. */
|
|
3
|
+
export declare function formatMetricValue(value: number | undefined | null, unit: AnalyticsMetric["unit"]): string;
|
|
4
|
+
export declare function formatDuration(seconds: number): string;
|
|
5
|
+
export declare function formatChange(changePercent: number | undefined | null): string | null;
|
|
6
|
+
/** For average position a falling number is an improvement. */
|
|
7
|
+
export declare function changeTone(metric: Pick<AnalyticsMetric, "changePercent" | "unit">): "up" | "down" | "flat";
|
|
8
|
+
export declare function formatDate(isoDate: string | undefined): string;
|
|
9
|
+
export declare function formatCount(value: number): string;
|
|
10
|
+
export declare function share(value: number, total: number): number;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const integer = new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 });
|
|
2
|
+
const decimal = new Intl.NumberFormat("en-US", { maximumFractionDigits: 1 });
|
|
3
|
+
/** "No data" (undefined) is shown as an em dash, never as 0. */
|
|
4
|
+
export function formatMetricValue(value, unit) {
|
|
5
|
+
if (value === undefined || value === null || Number.isNaN(value))
|
|
6
|
+
return "—";
|
|
7
|
+
switch (unit) {
|
|
8
|
+
case "seconds":
|
|
9
|
+
return formatDuration(value);
|
|
10
|
+
case "ratio":
|
|
11
|
+
return `${decimal.format(value * 100)}%`;
|
|
12
|
+
case "position":
|
|
13
|
+
return decimal.format(value);
|
|
14
|
+
default:
|
|
15
|
+
return integer.format(value);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function formatDuration(seconds) {
|
|
19
|
+
if (seconds < 60)
|
|
20
|
+
return `${Math.round(seconds)}s`;
|
|
21
|
+
const minutes = Math.floor(seconds / 60);
|
|
22
|
+
const rest = Math.round(seconds % 60);
|
|
23
|
+
return rest === 0 ? `${minutes}m` : `${minutes}m ${rest}s`;
|
|
24
|
+
}
|
|
25
|
+
export function formatChange(changePercent) {
|
|
26
|
+
if (changePercent === undefined || changePercent === null)
|
|
27
|
+
return null;
|
|
28
|
+
const sign = changePercent > 0 ? "+" : "";
|
|
29
|
+
return `${sign}${decimal.format(changePercent)}%`;
|
|
30
|
+
}
|
|
31
|
+
/** For average position a falling number is an improvement. */
|
|
32
|
+
export function changeTone(metric) {
|
|
33
|
+
const change = metric.changePercent;
|
|
34
|
+
if (change === undefined || change === null || Math.abs(change) < 0.5)
|
|
35
|
+
return "flat";
|
|
36
|
+
const better = metric.unit === "position" ? change < 0 : change > 0;
|
|
37
|
+
return better ? "up" : "down";
|
|
38
|
+
}
|
|
39
|
+
export function formatDate(isoDate) {
|
|
40
|
+
if (!isoDate)
|
|
41
|
+
return "";
|
|
42
|
+
const [year, month, day] = isoDate.split("-").map(Number);
|
|
43
|
+
if (!year || !month || !day)
|
|
44
|
+
return isoDate;
|
|
45
|
+
return new Date(Date.UTC(year, month - 1, day)).toLocaleDateString(undefined, {
|
|
46
|
+
month: "short",
|
|
47
|
+
day: "numeric",
|
|
48
|
+
timeZone: "UTC",
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
export function formatCount(value) {
|
|
52
|
+
return integer.format(value);
|
|
53
|
+
}
|
|
54
|
+
export function share(value, total) {
|
|
55
|
+
return total > 0 ? Math.round((value / total) * 100) : 0;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=analyticsFormat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyticsFormat.js","sourceRoot":"","sources":["../../../../src/editor/sidebar/analytics/analyticsFormat.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { AnalyticsComparison, AnalyticsRangeParams } from "../../services/analyticsService";
|
|
2
|
+
export type RangeSelection = {
|
|
3
|
+
kind: "preset";
|
|
4
|
+
days: number;
|
|
5
|
+
compare: AnalyticsComparison;
|
|
6
|
+
} | {
|
|
7
|
+
kind: "custom";
|
|
8
|
+
start: string;
|
|
9
|
+
end: string;
|
|
10
|
+
compare: AnalyticsComparison;
|
|
11
|
+
}
|
|
12
|
+
/** Equal windows after and before a publish; the publish day itself is left out of both. */
|
|
13
|
+
| {
|
|
14
|
+
kind: "publish";
|
|
15
|
+
publishDate: string;
|
|
16
|
+
};
|
|
17
|
+
export declare const RANGE_PRESETS: readonly [{
|
|
18
|
+
readonly days: 7;
|
|
19
|
+
readonly label: "Last 7 days";
|
|
20
|
+
}, {
|
|
21
|
+
readonly days: 28;
|
|
22
|
+
readonly label: "Last 28 days";
|
|
23
|
+
}, {
|
|
24
|
+
readonly days: 90;
|
|
25
|
+
readonly label: "Last 90 days";
|
|
26
|
+
}, {
|
|
27
|
+
readonly days: 365;
|
|
28
|
+
readonly label: "Last 12 months";
|
|
29
|
+
}];
|
|
30
|
+
export declare const COMPARISONS: {
|
|
31
|
+
value: AnalyticsComparison;
|
|
32
|
+
label: string;
|
|
33
|
+
}[];
|
|
34
|
+
export declare const DEFAULT_RANGE: RangeSelection;
|
|
35
|
+
/** Before/after windows are at most two weeks each, and need three complete days after the publish. */
|
|
36
|
+
export declare const PUBLISH_WINDOW_MAX_DAYS = 14;
|
|
37
|
+
export declare const PUBLISH_WINDOW_MIN_DAYS = 3;
|
|
38
|
+
export declare function parseIsoDate(value: string): Date;
|
|
39
|
+
export declare function toIsoDate(date: Date): string;
|
|
40
|
+
export declare function addDays(isoDate: string, days: number): string;
|
|
41
|
+
export declare function daysBetween(fromIso: string, toIso: string): number;
|
|
42
|
+
export type PublishWindows = {
|
|
43
|
+
ok: true;
|
|
44
|
+
days: number;
|
|
45
|
+
start: string;
|
|
46
|
+
end: string;
|
|
47
|
+
compareStart: string;
|
|
48
|
+
compareEnd: string;
|
|
49
|
+
} | {
|
|
50
|
+
ok: false;
|
|
51
|
+
daysAvailable: number;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* After = the complete days following the publish day (at most two weeks), before = the same
|
|
55
|
+
* number of days ending the day before the publish. Leaving the publish day out keeps a half
|
|
56
|
+
* day of the old version out of the "after" numbers.
|
|
57
|
+
*/
|
|
58
|
+
export declare function publishWindows(publishDate: string, lastCompleteDay: string): PublishWindows;
|
|
59
|
+
/** Query parameters for the analytics endpoint; null when the selection can't be queried yet. */
|
|
60
|
+
export declare function toRangeParams(selection: RangeSelection, lastCompleteDay: string | undefined): AnalyticsRangeParams | null;
|
|
61
|
+
export declare function rangeLabel(selection: RangeSelection): string;
|
|
62
|
+
export declare function comparisonLabel(compare: AnalyticsComparison | undefined): "before" | "last year" | "prev.";
|
|
63
|
+
/** Only presets and custom ranges are remembered; a before/after view is a one-off look. */
|
|
64
|
+
export declare function serializeRange(selection: RangeSelection): string | null;
|
|
65
|
+
export declare function parseRange(value: string | null): RangeSelection;
|
|
66
|
+
/** Index of the trend point that holds a date: the same day, or the ISO week starting on that point. */
|
|
67
|
+
export declare function trendIndexOf(points: {
|
|
68
|
+
date: string;
|
|
69
|
+
}[], date: string, granularity: "day" | "week" | undefined): number;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
export const RANGE_PRESETS = [
|
|
2
|
+
{ days: 7, label: "Last 7 days" },
|
|
3
|
+
{ days: 28, label: "Last 28 days" },
|
|
4
|
+
{ days: 90, label: "Last 90 days" },
|
|
5
|
+
{ days: 365, label: "Last 12 months" },
|
|
6
|
+
];
|
|
7
|
+
export const COMPARISONS = [
|
|
8
|
+
{ value: "previous", label: "vs previous period" },
|
|
9
|
+
{ value: "year", label: "vs same period last year" },
|
|
10
|
+
{ value: "none", label: "No comparison" },
|
|
11
|
+
];
|
|
12
|
+
export const DEFAULT_RANGE = {
|
|
13
|
+
kind: "preset",
|
|
14
|
+
days: 28,
|
|
15
|
+
compare: "previous",
|
|
16
|
+
};
|
|
17
|
+
/** Before/after windows are at most two weeks each, and need three complete days after the publish. */
|
|
18
|
+
export const PUBLISH_WINDOW_MAX_DAYS = 14;
|
|
19
|
+
export const PUBLISH_WINDOW_MIN_DAYS = 3;
|
|
20
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
21
|
+
const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
22
|
+
export function parseIsoDate(value) {
|
|
23
|
+
const [y, m, d] = value.split("-").map(Number);
|
|
24
|
+
return new Date(Date.UTC(y, m - 1, d));
|
|
25
|
+
}
|
|
26
|
+
export function toIsoDate(date) {
|
|
27
|
+
return date.toISOString().slice(0, 10);
|
|
28
|
+
}
|
|
29
|
+
export function addDays(isoDate, days) {
|
|
30
|
+
return toIsoDate(new Date(parseIsoDate(isoDate).getTime() + days * DAY_MS));
|
|
31
|
+
}
|
|
32
|
+
export function daysBetween(fromIso, toIso) {
|
|
33
|
+
return Math.round((parseIsoDate(toIso).getTime() - parseIsoDate(fromIso).getTime()) / DAY_MS);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* After = the complete days following the publish day (at most two weeks), before = the same
|
|
37
|
+
* number of days ending the day before the publish. Leaving the publish day out keeps a half
|
|
38
|
+
* day of the old version out of the "after" numbers.
|
|
39
|
+
*/
|
|
40
|
+
export function publishWindows(publishDate, lastCompleteDay) {
|
|
41
|
+
const start = addDays(publishDate, 1);
|
|
42
|
+
const available = daysBetween(start, lastCompleteDay) + 1;
|
|
43
|
+
if (available < PUBLISH_WINDOW_MIN_DAYS)
|
|
44
|
+
return { ok: false, daysAvailable: Math.max(0, available) };
|
|
45
|
+
const days = Math.min(PUBLISH_WINDOW_MAX_DAYS, available);
|
|
46
|
+
return {
|
|
47
|
+
ok: true,
|
|
48
|
+
days,
|
|
49
|
+
start,
|
|
50
|
+
end: addDays(start, days - 1),
|
|
51
|
+
compareStart: addDays(publishDate, -days),
|
|
52
|
+
compareEnd: addDays(publishDate, -1),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** Query parameters for the analytics endpoint; null when the selection can't be queried yet. */
|
|
56
|
+
export function toRangeParams(selection, lastCompleteDay) {
|
|
57
|
+
switch (selection.kind) {
|
|
58
|
+
case "preset":
|
|
59
|
+
return { days: selection.days, compare: selection.compare };
|
|
60
|
+
case "custom":
|
|
61
|
+
return {
|
|
62
|
+
start: selection.start,
|
|
63
|
+
end: selection.end,
|
|
64
|
+
compare: selection.compare,
|
|
65
|
+
};
|
|
66
|
+
case "publish": {
|
|
67
|
+
if (!lastCompleteDay)
|
|
68
|
+
return null;
|
|
69
|
+
const windows = publishWindows(selection.publishDate, lastCompleteDay);
|
|
70
|
+
if (!windows.ok)
|
|
71
|
+
return null;
|
|
72
|
+
return {
|
|
73
|
+
start: windows.start,
|
|
74
|
+
end: windows.end,
|
|
75
|
+
compareStart: windows.compareStart,
|
|
76
|
+
compareEnd: windows.compareEnd,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export function rangeLabel(selection) {
|
|
82
|
+
if (selection.kind === "preset")
|
|
83
|
+
return (RANGE_PRESETS.find((p) => p.days === selection.days)?.label ??
|
|
84
|
+
`Last ${selection.days} days`);
|
|
85
|
+
if (selection.kind === "custom")
|
|
86
|
+
return "Custom range";
|
|
87
|
+
return "Before and after publish";
|
|
88
|
+
}
|
|
89
|
+
export function comparisonLabel(compare) {
|
|
90
|
+
if (compare === "year")
|
|
91
|
+
return "last year";
|
|
92
|
+
if (compare === "custom")
|
|
93
|
+
return "before";
|
|
94
|
+
return "prev.";
|
|
95
|
+
}
|
|
96
|
+
/** Only presets and custom ranges are remembered; a before/after view is a one-off look. */
|
|
97
|
+
export function serializeRange(selection) {
|
|
98
|
+
return selection.kind === "publish" ? null : JSON.stringify(selection);
|
|
99
|
+
}
|
|
100
|
+
export function parseRange(value) {
|
|
101
|
+
if (!value)
|
|
102
|
+
return DEFAULT_RANGE;
|
|
103
|
+
try {
|
|
104
|
+
const parsed = JSON.parse(value);
|
|
105
|
+
const compare = COMPARISONS.some((c) => c.value === parsed.compare)
|
|
106
|
+
? parsed.compare
|
|
107
|
+
: "previous";
|
|
108
|
+
if (parsed.kind === "preset" &&
|
|
109
|
+
typeof parsed.days === "number" &&
|
|
110
|
+
parsed.days >= 1 &&
|
|
111
|
+
parsed.days <= 730)
|
|
112
|
+
return { kind: "preset", days: parsed.days, compare };
|
|
113
|
+
if (parsed.kind === "custom" &&
|
|
114
|
+
typeof parsed.start === "string" &&
|
|
115
|
+
typeof parsed.end === "string" &&
|
|
116
|
+
ISO_DATE.test(parsed.start) &&
|
|
117
|
+
ISO_DATE.test(parsed.end))
|
|
118
|
+
return { kind: "custom", start: parsed.start, end: parsed.end, compare };
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// Fall through to the default.
|
|
122
|
+
}
|
|
123
|
+
return DEFAULT_RANGE;
|
|
124
|
+
}
|
|
125
|
+
/** Index of the trend point that holds a date: the same day, or the ISO week starting on that point. */
|
|
126
|
+
export function trendIndexOf(points, date, granularity) {
|
|
127
|
+
if (granularity !== "week")
|
|
128
|
+
return points.findIndex((p) => p.date === date);
|
|
129
|
+
return points.findIndex((p) => {
|
|
130
|
+
const offset = daysBetween(p.date, date);
|
|
131
|
+
return offset >= 0 && offset < 7;
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=analyticsRange.js.map
|