@byline/admin 4.15.0 → 4.17.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.
Files changed (51) hide show
  1. package/dist/abilities.js +2 -0
  2. package/dist/fields/relation/relation-field.js +3 -2
  3. package/dist/fields/relation/relation-many-field.js +3 -2
  4. package/dist/fields/relation/relation-picker.d.ts +2 -2
  5. package/dist/fields/relation/relation-summary.d.ts +2 -2
  6. package/dist/forms/document-actions.js +30 -25
  7. package/dist/forms/form-renderer.d.ts +17 -3
  8. package/dist/forms/form-renderer.js +18 -9
  9. package/dist/forms/tree-placement-widget.js +3 -2
  10. package/dist/forms/use-form-layout.d.ts +3 -3
  11. package/dist/index.d.ts +1 -0
  12. package/dist/index.js +1 -0
  13. package/dist/modules/analytics/abilities.d.ts +14 -0
  14. package/dist/modules/analytics/abilities.js +21 -0
  15. package/dist/modules/analytics/components/dashboard.d.ts +20 -0
  16. package/dist/modules/analytics/components/dashboard.js +308 -0
  17. package/dist/modules/analytics/components/dashboard.module.js +29 -0
  18. package/dist/modules/analytics/components/dashboard.test.node.d.ts +8 -0
  19. package/dist/modules/analytics/components/dashboard_module.css +268 -0
  20. package/dist/modules/analytics/components/timeseries.d.ts +59 -0
  21. package/dist/modules/analytics/components/timeseries.js +253 -0
  22. package/dist/modules/analytics/components/timeseries.module.js +17 -0
  23. package/dist/modules/analytics/components/timeseries_module.css +126 -0
  24. package/dist/modules/analytics/index.d.ts +9 -0
  25. package/dist/modules/analytics/index.js +1 -0
  26. package/dist/modules/analytics/types.d.ts +20 -0
  27. package/dist/modules/analytics/types.js +1 -0
  28. package/package.json +21 -11
  29. package/src/abilities.ts +2 -0
  30. package/src/fields/relation/relation-field.tsx +5 -3
  31. package/src/fields/relation/relation-many-field.tsx +5 -3
  32. package/src/fields/relation/relation-picker.tsx +2 -2
  33. package/src/fields/relation/relation-summary.tsx +2 -2
  34. package/src/forms/document-actions.test.tsx +157 -0
  35. package/src/forms/document-actions.tsx +93 -74
  36. package/src/forms/form-renderer-capabilities.test.tsx +155 -0
  37. package/src/forms/form-renderer-submit.test.tsx +194 -0
  38. package/src/forms/form-renderer.tsx +59 -25
  39. package/src/forms/tree-placement-widget.tsx +3 -2
  40. package/src/forms/use-form-layout.ts +3 -3
  41. package/src/index.ts +1 -0
  42. package/src/modules/analytics/abilities.ts +33 -0
  43. package/src/modules/analytics/components/dashboard.module.css +330 -0
  44. package/src/modules/analytics/components/dashboard.test.node.ts +193 -0
  45. package/src/modules/analytics/components/dashboard.tsx +371 -0
  46. package/src/modules/analytics/components/timeseries.module.css +151 -0
  47. package/src/modules/analytics/components/timeseries.tsx +332 -0
  48. package/src/modules/analytics/index.ts +14 -0
  49. package/src/modules/analytics/types.ts +31 -0
  50. package/dist/forms/__probe-nested.test.node.d.ts +0 -1
  51. package/dist/forms/__probe-two.test.node.d.ts +0 -1
@@ -0,0 +1,308 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useMemo, useState } from "react";
4
+ import { ANALYTICS_DASHBOARD_PERIODS, ANALYTICS_OVERFLOW_KEY, isAnalyticsDashboardPeriod } from "@byline/analytics/config";
5
+ import { ANALYTICS_IGNORE_STORAGE_KEY } from "@byline/analytics-agent";
6
+ import { useTranslation } from "@byline/i18n/react";
7
+ import { Button, Card, Container, Section, Select } from "@byline/ui/react";
8
+ import clsx from "clsx";
9
+ import dashboard_module from "./dashboard.module.js";
10
+ import { AnalyticsTimeseries, resolveAnalyticsChartGranularity } from "./timeseries.js";
11
+ function AnalyticsDashboard({ data, period, onPeriodChange }) {
12
+ const { locale, t } = useTranslation('byline-admin');
13
+ const [excluded, setExcluded] = useState(false);
14
+ const numbers = useMemo(()=>new Intl.NumberFormat(locale), [
15
+ locale
16
+ ]);
17
+ useEffect(()=>{
18
+ try {
19
+ setExcluded(null != localStorage.getItem(ANALYTICS_IGNORE_STORAGE_KEY));
20
+ } catch {
21
+ setExcluded(false);
22
+ }
23
+ }, []);
24
+ const toggleExclusion = ()=>{
25
+ try {
26
+ if (excluded) localStorage.removeItem(ANALYTICS_IGNORE_STORAGE_KEY);
27
+ else localStorage.setItem(ANALYTICS_IGNORE_STORAGE_KEY, '1');
28
+ setExcluded(!excluded);
29
+ } catch {}
30
+ };
31
+ const periodItems = ANALYTICS_DASHBOARD_PERIODS.map((value)=>({
32
+ value: String(value),
33
+ label: 'number' == typeof value ? t('analytics.period.days', {
34
+ count: value
35
+ }) : t(`analytics.period.${value}`)
36
+ }));
37
+ const { views, visitors, downloads } = data.summary;
38
+ const days = data.summary.timeseries.length;
39
+ const chartGranularity = resolveAnalyticsChartGranularity(period, days);
40
+ return /*#__PURE__*/ jsx(Section, {
41
+ children: /*#__PURE__*/ jsxs(Container, {
42
+ children: [
43
+ /*#__PURE__*/ jsxs("header", {
44
+ className: clsx('byline-analytics-header', dashboard_module.header),
45
+ children: [
46
+ /*#__PURE__*/ jsxs("div", {
47
+ children: [
48
+ /*#__PURE__*/ jsx("h1", {
49
+ className: clsx('byline-analytics-title', dashboard_module.title),
50
+ children: t('analytics.title')
51
+ }),
52
+ /*#__PURE__*/ jsx("p", {
53
+ className: clsx('muted', 'byline-analytics-help', dashboard_module.help),
54
+ children: t('analytics.dailyUniquesHelp')
55
+ })
56
+ ]
57
+ }),
58
+ /*#__PURE__*/ jsxs("div", {
59
+ className: clsx('byline-analytics-controls', dashboard_module.controls),
60
+ children: [
61
+ /*#__PURE__*/ jsx(Select, {
62
+ id: "analytics-period",
63
+ name: "analytics-period",
64
+ "aria-label": t('analytics.period.label'),
65
+ size: "sm",
66
+ value: String(period),
67
+ items: periodItems,
68
+ onValueChange: (value)=>{
69
+ const next = 'ytd' === value || 'all' === value ? value : Number(value);
70
+ if (isAnalyticsDashboardPeriod(next)) onPeriodChange(next);
71
+ }
72
+ }),
73
+ /*#__PURE__*/ jsx(Button, {
74
+ type: "button",
75
+ size: "sm",
76
+ "aria-pressed": excluded,
77
+ title: t('analytics.exclusion.help'),
78
+ onClick: toggleExclusion,
79
+ children: excluded ? t('analytics.exclusion.include') : t('analytics.exclusion.exclude')
80
+ })
81
+ ]
82
+ })
83
+ ]
84
+ }),
85
+ /*#__PURE__*/ jsxs("div", {
86
+ className: clsx('byline-analytics-stats', dashboard_module.stats),
87
+ children: [
88
+ /*#__PURE__*/ jsx(StatTile, {
89
+ tone: "views",
90
+ label: t('analytics.stats.views'),
91
+ value: numbers.format(views),
92
+ foot: t('analytics.stats.perDay', {
93
+ count: 0 === days ? 0 : Math.round(views / days)
94
+ })
95
+ }),
96
+ /*#__PURE__*/ jsx(StatTile, {
97
+ tone: "visitors",
98
+ label: t('analytics.stats.dailyUniques'),
99
+ value: numbers.format(visitors),
100
+ foot: t('analytics.stats.sumOfDays', {
101
+ count: days
102
+ })
103
+ }),
104
+ /*#__PURE__*/ jsx(StatTile, {
105
+ tone: "downloads",
106
+ label: t('analytics.stats.downloads'),
107
+ value: numbers.format(downloads),
108
+ foot: t('analytics.stats.shareOfViews', {
109
+ share: formatShare(downloads, views, locale)
110
+ })
111
+ })
112
+ ]
113
+ }),
114
+ /*#__PURE__*/ jsxs(Card, {
115
+ className: clsx('byline-analytics-chart-card', dashboard_module.chartCard),
116
+ children: [
117
+ /*#__PURE__*/ jsx(Card.Header, {
118
+ children: /*#__PURE__*/ jsx(Card.Title, {
119
+ children: 'day' === chartGranularity ? t('analytics.chart.perDay') : 'seven-day' === chartGranularity ? t('analytics.chart.perSevenDays') : t('analytics.chart.perMonth')
120
+ })
121
+ }),
122
+ /*#__PURE__*/ jsx(Card.Content, {
123
+ children: /*#__PURE__*/ jsx(AnalyticsTimeseries, {
124
+ days: data.summary.timeseries,
125
+ granularity: chartGranularity,
126
+ locale: locale
127
+ })
128
+ })
129
+ ]
130
+ }),
131
+ /*#__PURE__*/ jsxs("div", {
132
+ className: clsx('byline-analytics-lists', dashboard_module.lists),
133
+ children: [
134
+ /*#__PURE__*/ jsx(RankedList, {
135
+ title: t('analytics.sections.pages'),
136
+ caption: t('analytics.columns.viewsAndUniques'),
137
+ tone: "views",
138
+ locale: locale,
139
+ rows: data.pages.rows.map(toPathRow),
140
+ total: data.pages.total,
141
+ coverageFrom: partialCoverageFrom(data.range.from, data.coverage.pathsFrom)
142
+ }),
143
+ /*#__PURE__*/ jsxs("div", {
144
+ className: dashboard_module.stack,
145
+ children: [
146
+ /*#__PURE__*/ jsx(RankedList, {
147
+ title: t('analytics.sections.referrers'),
148
+ tone: "visitors",
149
+ locale: locale,
150
+ total: data.referrers.total,
151
+ coverageFrom: partialCoverageFrom(data.range.from, data.coverage.referrersFrom),
152
+ rows: data.referrers.rows.map((row)=>({
153
+ key: row.referrerHost,
154
+ label: row.referrerHost,
155
+ value: row.views,
156
+ visitors: row.visitors,
157
+ overflow: row.referrerHost === ANALYTICS_OVERFLOW_KEY
158
+ }))
159
+ }),
160
+ /*#__PURE__*/ jsx(RankedList, {
161
+ title: t('analytics.sections.countries'),
162
+ tone: "visitors",
163
+ locale: locale,
164
+ rows: data.countries.map((row)=>({
165
+ key: row.country,
166
+ label: row.country,
167
+ value: row.views,
168
+ visitors: row.visitors,
169
+ overflow: false
170
+ }))
171
+ })
172
+ ]
173
+ })
174
+ ]
175
+ }),
176
+ /*#__PURE__*/ jsx(RankedList, {
177
+ title: t('analytics.sections.downloads'),
178
+ caption: t('analytics.columns.clicksAndUniques'),
179
+ tone: "downloads",
180
+ locale: locale,
181
+ rows: data.downloads.rows.map(toPathRow),
182
+ total: data.downloads.total,
183
+ coverageFrom: partialCoverageFrom(data.range.from, data.coverage.pathsFrom)
184
+ })
185
+ ]
186
+ })
187
+ });
188
+ }
189
+ const TONE_TILE = {
190
+ views: dashboard_module.toneViews,
191
+ visitors: dashboard_module.toneVisitors,
192
+ downloads: dashboard_module.toneDownloads
193
+ };
194
+ const TONE_BAR = {
195
+ views: dashboard_module.barViews,
196
+ visitors: dashboard_module.barVisitors,
197
+ downloads: dashboard_module.barDownloads
198
+ };
199
+ function StatTile({ tone, label, value, foot }) {
200
+ return /*#__PURE__*/ jsxs("div", {
201
+ className: clsx('byline-analytics-stat', dashboard_module.stat, TONE_TILE[tone]),
202
+ children: [
203
+ /*#__PURE__*/ jsx("span", {
204
+ className: clsx('byline-analytics-stat-label', dashboard_module.statLabel),
205
+ children: label
206
+ }),
207
+ /*#__PURE__*/ jsx("span", {
208
+ className: clsx('byline-analytics-stat-value', dashboard_module.statValue),
209
+ children: value
210
+ }),
211
+ /*#__PURE__*/ jsx("span", {
212
+ className: clsx('byline-analytics-stat-foot', dashboard_module.statFoot),
213
+ children: foot
214
+ })
215
+ ]
216
+ });
217
+ }
218
+ function toPathRow(row) {
219
+ return {
220
+ key: row.path,
221
+ label: row.path,
222
+ value: row.views,
223
+ visitors: row.visitors,
224
+ overflow: row.path === ANALYTICS_OVERFLOW_KEY
225
+ };
226
+ }
227
+ function RankedList({ title, caption, rows, tone, locale, total, coverageFrom }) {
228
+ const { t } = useTranslation('byline-admin');
229
+ const numbers = useMemo(()=>new Intl.NumberFormat(locale), [
230
+ locale
231
+ ]);
232
+ const ceiling = Math.max(1, ...rows.map((row)=>row.value));
233
+ const truncated = null != total && total > rows.length;
234
+ const coverageDate = useMemo(()=>new Intl.DateTimeFormat(locale, {
235
+ dateStyle: 'medium',
236
+ timeZone: 'UTC'
237
+ }), [
238
+ locale
239
+ ]);
240
+ const description = [
241
+ truncated ? t('analytics.topOf', {
242
+ shown: rows.length,
243
+ total
244
+ }) : caption,
245
+ null == coverageFrom ? void 0 : t('analytics.coverage.since', {
246
+ date: coverageDate.format(new Date(`${coverageFrom}T00:00:00.000Z`))
247
+ })
248
+ ].filter((value)=>null != value).join(' · ');
249
+ return /*#__PURE__*/ jsxs(Card, {
250
+ className: clsx('byline-analytics-list', dashboard_module.list),
251
+ children: [
252
+ /*#__PURE__*/ jsxs(Card.Header, {
253
+ children: [
254
+ /*#__PURE__*/ jsx(Card.Title, {
255
+ children: title
256
+ }),
257
+ description.length > 0 && /*#__PURE__*/ jsx(Card.Description, {
258
+ children: description
259
+ })
260
+ ]
261
+ }),
262
+ /*#__PURE__*/ jsx(Card.Content, {
263
+ children: 0 === rows.length ? /*#__PURE__*/ jsx("p", {
264
+ className: "muted",
265
+ children: t('analytics.empty')
266
+ }) : /*#__PURE__*/ jsx("ol", {
267
+ className: clsx('byline-analytics-ranking', dashboard_module.ranking),
268
+ children: rows.map((row)=>/*#__PURE__*/ jsxs("li", {
269
+ className: clsx('byline-analytics-ranking-row', dashboard_module.rankingRow, TONE_BAR[tone], row.overflow && dashboard_module.rankingOverflow),
270
+ style: {
271
+ '--byline-analytics-share': `${shareWidth(row.value, ceiling)}%`
272
+ },
273
+ children: [
274
+ /*#__PURE__*/ jsx("span", {
275
+ className: dashboard_module.rankingLabel,
276
+ title: row.key,
277
+ children: row.overflow ? t('analytics.overflow') : row.label
278
+ }),
279
+ /*#__PURE__*/ jsx("span", {
280
+ className: dashboard_module.rankingValue,
281
+ children: numbers.format(row.value)
282
+ }),
283
+ /*#__PURE__*/ jsx("span", {
284
+ className: dashboard_module.rankingVisitors,
285
+ children: numbers.format(row.visitors)
286
+ })
287
+ ]
288
+ }, row.key))
289
+ })
290
+ })
291
+ ]
292
+ });
293
+ }
294
+ function shareWidth(value, ceiling) {
295
+ if (!Number.isFinite(value) || value <= 0 || ceiling <= 0) return 0;
296
+ return Math.max(3, Math.min(100, value / ceiling * 100));
297
+ }
298
+ function formatShare(part, whole, locale) {
299
+ const percent = new Intl.NumberFormat(locale, {
300
+ style: 'percent',
301
+ maximumFractionDigits: 1
302
+ });
303
+ return percent.format(whole <= 0 ? 0 : part / whole);
304
+ }
305
+ function partialCoverageFrom(rangeFrom, coverageFrom) {
306
+ return null != coverageFrom && coverageFrom > rangeFrom ? coverageFrom : void 0;
307
+ }
308
+ export { AnalyticsDashboard, formatShare, partialCoverageFrom, shareWidth };
@@ -0,0 +1,29 @@
1
+ import "./dashboard_module.css";
2
+ const dashboard_module = {
3
+ header: "header-zAoAGd",
4
+ title: "title-ZhxB2D",
5
+ help: "help-HT9JOI",
6
+ chartCard: "chartCard-NaXVMm",
7
+ list: "list-sR__nc",
8
+ controls: "controls-_Alpbi",
9
+ stats: "stats-u3kcTH",
10
+ stat: "stat-KhSBeS",
11
+ statLabel: "statLabel-X0_tdu",
12
+ statValue: "statValue-D32ine",
13
+ statFoot: "statFoot-yvtZiS",
14
+ toneViews: "toneViews-QGWabX",
15
+ toneVisitors: "toneVisitors-GTLZDo",
16
+ toneDownloads: "toneDownloads-WuDN7n",
17
+ lists: "lists-BLqFfy",
18
+ stack: "stack-Yzch8f",
19
+ ranking: "ranking-yfNSnw",
20
+ rankingRow: "rankingRow-CH1cHn",
21
+ barViews: "barViews-E6VuaR",
22
+ barVisitors: "barVisitors-eIANXw",
23
+ barDownloads: "barDownloads-Z50NxT",
24
+ rankingOverflow: "rankingOverflow-hUQMKU",
25
+ rankingLabel: "rankingLabel-wsuniC",
26
+ rankingValue: "rankingValue-i_vmgf",
27
+ rankingVisitors: "rankingVisitors-NZl_jj"
28
+ };
29
+ export default dashboard_module;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ export {};
@@ -0,0 +1,268 @@
1
+ :is(.header-zAoAGd, .byline-analytics-header) {
2
+ border-bottom: var(--border-width-thin) var(--border-style-solid) var(--gray-200);
3
+ flex-wrap: wrap;
4
+ justify-content: space-between;
5
+ align-items: flex-end;
6
+ gap: 1rem 1.5rem;
7
+ margin-bottom: 1.25rem;
8
+ padding-bottom: 1rem;
9
+ display: flex;
10
+ }
11
+
12
+ :is(.title-ZhxB2D, .byline-analytics-title) {
13
+ letter-spacing: -.02em;
14
+ margin: 0;
15
+ font-size: 1.875rem;
16
+ font-weight: 650;
17
+ }
18
+
19
+ :is(.help-HT9JOI, .byline-analytics-help) {
20
+ max-width: 48ch;
21
+ margin: .35rem 0 0;
22
+ font-size: .8125rem;
23
+ }
24
+
25
+ :is(.chartCard-NaXVMm .byline-card-title, .list-sR__nc .byline-card-title) {
26
+ letter-spacing: normal;
27
+ font-size: 1.25rem;
28
+ font-weight: 600;
29
+ }
30
+
31
+ :is(.chartCard-NaXVMm .byline-card-description, .list-sR__nc .byline-card-description) {
32
+ font-size: .75rem;
33
+ }
34
+
35
+ :is(.controls-_Alpbi, .byline-analytics-controls) {
36
+ flex-wrap: wrap;
37
+ align-items: center;
38
+ gap: .75rem;
39
+ display: flex;
40
+ }
41
+
42
+ :is(.stats-u3kcTH, .byline-analytics-stats) {
43
+ grid-template-columns: repeat(3, minmax(0, 1fr));
44
+ gap: .75rem;
45
+ margin-bottom: .75rem;
46
+ display: grid;
47
+ }
48
+
49
+ :is(.stat-KhSBeS, .byline-analytics-stat) {
50
+ border-radius: .375rem;
51
+ flex-direction: column;
52
+ gap: .5rem;
53
+ min-width: 0;
54
+ padding: 1rem 1.125rem 1.125rem;
55
+ display: flex;
56
+ }
57
+
58
+ :is(.statLabel-X0_tdu, .byline-analytics-stat-label) {
59
+ text-transform: uppercase;
60
+ letter-spacing: .1em;
61
+ font-size: .625rem;
62
+ font-weight: 600;
63
+ line-height: 1;
64
+ }
65
+
66
+ :is(.statValue-D32ine, .byline-analytics-stat-value) {
67
+ font-variant-numeric: tabular-nums;
68
+ letter-spacing: -.02em;
69
+ font-size: clamp(1.75rem, 4.5vw, 2.5rem);
70
+ font-weight: 700;
71
+ line-height: 1;
72
+ }
73
+
74
+ :is(.statFoot-yvtZiS, .byline-analytics-stat-foot) {
75
+ font-variant-numeric: tabular-nums;
76
+ color: var(--gray-600);
77
+ font-size: .6875rem;
78
+ }
79
+
80
+ .toneViews-QGWabX {
81
+ background-color: #f0f9ff;
82
+ }
83
+
84
+ .toneViews-QGWabX .statLabel-X0_tdu, .toneViews-QGWabX .statValue-D32ine {
85
+ color: #0369a1;
86
+ }
87
+
88
+ .toneVisitors-GTLZDo {
89
+ background-color: #f5f3ff;
90
+ }
91
+
92
+ .toneVisitors-GTLZDo .statLabel-X0_tdu, .toneVisitors-GTLZDo .statValue-D32ine {
93
+ color: #6d28d9;
94
+ }
95
+
96
+ .toneDownloads-WuDN7n {
97
+ background-color: #ecfdf5;
98
+ }
99
+
100
+ .toneDownloads-WuDN7n .statLabel-X0_tdu, .toneDownloads-WuDN7n .statValue-D32ine {
101
+ color: #047857;
102
+ }
103
+
104
+ :is(:is([data-theme="dark"], .dark) .statFoot-yvtZiS, :is([data-theme="dark"], .dark) .byline-analytics-stat-foot) {
105
+ color: var(--gray-400);
106
+ }
107
+
108
+ :is([data-theme="dark"], .dark) .toneViews-QGWabX {
109
+ background-color: #0c4a6e47;
110
+ }
111
+
112
+ :is([data-theme="dark"], .dark) .toneViews-QGWabX .statLabel-X0_tdu, :is([data-theme="dark"], .dark) .toneViews-QGWabX .statValue-D32ine {
113
+ color: #7dd3fc;
114
+ }
115
+
116
+ :is([data-theme="dark"], .dark) .toneVisitors-GTLZDo {
117
+ background-color: #4c1d954d;
118
+ }
119
+
120
+ :is([data-theme="dark"], .dark) .toneVisitors-GTLZDo .statLabel-X0_tdu, :is([data-theme="dark"], .dark) .toneVisitors-GTLZDo .statValue-D32ine {
121
+ color: #c4b5fd;
122
+ }
123
+
124
+ :is([data-theme="dark"], .dark) .toneDownloads-WuDN7n {
125
+ background-color: #064e3b47;
126
+ }
127
+
128
+ :is([data-theme="dark"], .dark) .toneDownloads-WuDN7n .statLabel-X0_tdu, :is([data-theme="dark"], .dark) .toneDownloads-WuDN7n .statValue-D32ine {
129
+ color: #6ee7b7;
130
+ }
131
+
132
+ :is(.chartCard-NaXVMm, .byline-analytics-chart-card) {
133
+ margin-bottom: .75rem;
134
+ }
135
+
136
+ :is(.lists-BLqFfy, .byline-analytics-lists) {
137
+ grid-template-columns: 1.55fr 1fr;
138
+ align-items: start;
139
+ gap: .75rem;
140
+ margin-bottom: .75rem;
141
+ display: grid;
142
+ }
143
+
144
+ .stack-Yzch8f {
145
+ flex-direction: column;
146
+ gap: .75rem;
147
+ display: flex;
148
+ }
149
+
150
+ :is(.ranking-yfNSnw, .byline-analytics-ranking) {
151
+ flex-direction: column;
152
+ gap: 1px;
153
+ margin: 0;
154
+ padding: 0;
155
+ list-style: none;
156
+ display: flex;
157
+ }
158
+
159
+ :is(.rankingRow-CH1cHn, .byline-analytics-ranking-row) {
160
+ isolation: isolate;
161
+ border-radius: .25rem;
162
+ grid-template-columns: minmax(0, 1fr) auto auto;
163
+ align-items: center;
164
+ gap: .75rem;
165
+ padding: .45rem .625rem;
166
+ display: grid;
167
+ position: relative;
168
+ }
169
+
170
+ .rankingRow-CH1cHn:before {
171
+ content: "";
172
+ z-index: -1;
173
+ width: var(--byline-analytics-share, 0%);
174
+ background-color: var(--byline-analytics-bar, transparent);
175
+ opacity: .16;
176
+ border-radius: .25rem;
177
+ position: absolute;
178
+ inset: 0 auto 0 0;
179
+ }
180
+
181
+ .barViews-E6VuaR {
182
+ --byline-analytics-bar: #38bdf8;
183
+ }
184
+
185
+ .barVisitors-eIANXw {
186
+ --byline-analytics-bar: #7c3aed;
187
+ }
188
+
189
+ .barDownloads-Z50NxT {
190
+ --byline-analytics-bar: #10b981;
191
+ }
192
+
193
+ .rankingOverflow-hUQMKU {
194
+ border: var(--border-width-thin) dashed var(--gray-300);
195
+ }
196
+
197
+ .rankingOverflow-hUQMKU:before {
198
+ opacity: .07;
199
+ }
200
+
201
+ .rankingOverflow-hUQMKU .rankingLabel-wsuniC {
202
+ color: var(--gray-600);
203
+ font-style: italic;
204
+ }
205
+
206
+ .rankingLabel-wsuniC {
207
+ text-overflow: ellipsis;
208
+ white-space: nowrap;
209
+ min-width: 0;
210
+ font-size: .8125rem;
211
+ overflow: hidden;
212
+ }
213
+
214
+ .rankingValue-i_vmgf {
215
+ font-variant-numeric: tabular-nums;
216
+ font-size: .8125rem;
217
+ font-weight: 600;
218
+ }
219
+
220
+ .rankingVisitors-NZl_jj {
221
+ font-variant-numeric: tabular-nums;
222
+ text-align: end;
223
+ min-width: 3ch;
224
+ color: var(--gray-500);
225
+ font-size: .6875rem;
226
+ }
227
+
228
+ :is(:is([data-theme="dark"], .dark) .header-zAoAGd, :is([data-theme="dark"], .dark) .byline-analytics-header) {
229
+ border-bottom-color: var(--gray-700);
230
+ }
231
+
232
+ :is([data-theme="dark"], .dark) .rankingVisitors-NZl_jj {
233
+ color: var(--gray-400);
234
+ }
235
+
236
+ :is([data-theme="dark"], .dark) .rankingOverflow-hUQMKU {
237
+ border-color: var(--gray-600);
238
+ }
239
+
240
+ :is([data-theme="dark"], .dark) .rankingOverflow-hUQMKU .rankingLabel-wsuniC {
241
+ color: var(--gray-400);
242
+ }
243
+
244
+ :is([data-theme="dark"], .dark) .barVisitors-eIANXw {
245
+ --byline-analytics-bar: #a78bfa;
246
+ }
247
+
248
+ :is([data-theme="dark"], .dark) .barDownloads-Z50NxT {
249
+ --byline-analytics-bar: #34d399;
250
+ }
251
+
252
+ @media (max-width: 900px) {
253
+ :is(.lists-BLqFfy, .byline-analytics-lists) {
254
+ grid-template-columns: 1fr;
255
+ }
256
+ }
257
+
258
+ @media (max-width: 720px) {
259
+ :is(.header-zAoAGd, .byline-analytics-header) {
260
+ flex-direction: column;
261
+ align-items: stretch;
262
+ }
263
+
264
+ :is(.stats-u3kcTH, .byline-analytics-stats) {
265
+ grid-template-columns: 1fr;
266
+ }
267
+ }
268
+
@@ -0,0 +1,59 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ /**
9
+ * The analytics timeseries — one column per explicit UTC reporting bucket.
10
+ *
11
+ * A column rather than a continuous line is a claim about the data, not a
12
+ * style: the rows behind this chart are sums of daily aggregates and visitor
13
+ * hashes rotate at UTC midnight, so nothing is measured between two points.
14
+ * The dashboard selects the granularity and passes it into this renderer;
15
+ * the chart never guesses from the number of rows.
16
+ */
17
+ import type React from 'react';
18
+ import type { AnalyticsSummaryDay } from '@byline/analytics';
19
+ import type { AnalyticsDashboardPeriod } from '@byline/analytics/config';
20
+ export type AnalyticsChartGranularity = 'day' | 'seven-day' | 'month';
21
+ export interface AnalyticsChartBucket {
22
+ from: string;
23
+ to: string;
24
+ granularity: AnalyticsChartGranularity;
25
+ /** Actual number of daily rows, including partial first or last buckets. */
26
+ dayCount: number;
27
+ views: number;
28
+ /** Sum of the bucket's daily-unique visitor values. */
29
+ visitors: number;
30
+ downloads: number;
31
+ }
32
+ export interface AnalyticsColumn {
33
+ index: number;
34
+ /** Full-height mark: that bucket's page views. */
35
+ x: number;
36
+ width: number;
37
+ y: number;
38
+ height: number;
39
+ /** Inset mark: summed daily uniques, always no greater than views. */
40
+ insetX: number;
41
+ insetWidth: number;
42
+ insetY: number;
43
+ insetHeight: number;
44
+ /** Full-height transparent target, so narrow columns stay easy to hit. */
45
+ hitX: number;
46
+ hitWidth: number;
47
+ }
48
+ /** Resolve chart density outside the renderer so the chosen width is explicit. */
49
+ export declare function resolveAnalyticsChartGranularity(period: AnalyticsDashboardPeriod, dayCount: number): AnalyticsChartGranularity;
50
+ /** Combine complete daily query rows using the caller-selected granularity. */
51
+ export declare function bucketAnalyticsTimeseries(days: readonly AnalyticsSummaryDay[], granularity: AnalyticsChartGranularity): readonly AnalyticsChartBucket[];
52
+ /** Project reporting buckets into column geometry. */
53
+ export declare function buildAnalyticsColumns(buckets: readonly Pick<AnalyticsChartBucket, 'views' | 'visitors'>[]): readonly AnalyticsColumn[];
54
+ export interface AnalyticsTimeseriesProps {
55
+ days: readonly AnalyticsSummaryDay[];
56
+ granularity: AnalyticsChartGranularity;
57
+ locale: string;
58
+ }
59
+ export declare function AnalyticsTimeseries({ days, granularity, locale, }: AnalyticsTimeseriesProps): React.JSX.Element;