@openrails/contentkit-ui 0.104.0-rc.1 → 0.104.0-rc.2

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 (210) hide show
  1. package/README.md +6 -6
  2. package/dist/client/beacons.js +4 -1
  3. package/dist/client/beacons.js.map +1 -1
  4. package/dist/client/contents.d.ts +7 -1
  5. package/dist/client/contents.js +8 -0
  6. package/dist/client/contents.js.map +1 -1
  7. package/dist/client/discovery.d.ts +6 -2
  8. package/dist/client/discovery.js +1 -1
  9. package/dist/client/discovery.js.map +1 -1
  10. package/dist/client/generated/routes.d.ts +41 -28
  11. package/dist/client/generated/routes.js +63 -61
  12. package/dist/client/generated/routes.js.map +1 -1
  13. package/dist/client/generated/wire.d.ts +36 -74
  14. package/dist/client/generated/wire.js.map +1 -1
  15. package/dist/client/index.d.ts +1 -1
  16. package/dist/client/media/windows.d.ts +2 -0
  17. package/dist/client/media/windows.js +4 -1
  18. package/dist/client/media/windows.js.map +1 -1
  19. package/dist/client/stats.d.ts +4 -20
  20. package/dist/client/stats.js +0 -11
  21. package/dist/client/stats.js.map +1 -1
  22. package/dist/client/versions.d.ts +8 -6
  23. package/dist/client/versions.js +15 -10
  24. package/dist/client/versions.js.map +1 -1
  25. package/dist/components/admin/bulk-drafts.d.ts +17 -0
  26. package/dist/components/admin/bulk-drafts.js +41 -0
  27. package/dist/components/admin/bulk-drafts.js.map +1 -0
  28. package/dist/components/admin/content-admin.d.ts +1 -1
  29. package/dist/components/admin/content-admin.js.map +1 -1
  30. package/dist/components/admin/content-manager.d.ts +11 -3
  31. package/dist/components/admin/content-manager.js +739 -380
  32. package/dist/components/admin/content-manager.js.map +1 -1
  33. package/dist/components/admin/group-manager.d.ts +8 -3
  34. package/dist/components/admin/group-manager.js +218 -146
  35. package/dist/components/admin/group-manager.js.map +1 -1
  36. package/dist/components/admin/page-notices.d.ts +16 -0
  37. package/dist/components/admin/page-notices.js +113 -0
  38. package/dist/components/admin/page-notices.js.map +1 -0
  39. package/dist/components/admin/page-picker.d.ts +38 -0
  40. package/dist/components/admin/page-picker.js +159 -0
  41. package/dist/components/admin/page-picker.js.map +1 -0
  42. package/dist/components/admin/reader-content-editor.js +537 -500
  43. package/dist/components/admin/reader-content-editor.js.map +1 -1
  44. package/dist/components/admin/reader-manifest.js +181 -179
  45. package/dist/components/admin/reader-manifest.js.map +1 -1
  46. package/dist/components/admin/shell.d.ts +10 -2
  47. package/dist/components/admin/shell.js +114 -42
  48. package/dist/components/admin/shell.js.map +1 -1
  49. package/dist/components/admin/stats-dashboard.d.ts +4 -9
  50. package/dist/components/admin/stats-dashboard.js +300 -202
  51. package/dist/components/admin/stats-dashboard.js.map +1 -1
  52. package/dist/components/admin/upload-queue.d.ts +14 -0
  53. package/dist/components/admin/upload-queue.js +115 -98
  54. package/dist/components/admin/upload-queue.js.map +1 -1
  55. package/dist/components/content/card.d.ts +31 -3
  56. package/dist/components/content/card.js +324 -158
  57. package/dist/components/content/card.js.map +1 -1
  58. package/dist/components/content/daily-releases.js +14 -12
  59. package/dist/components/content/daily-releases.js.map +1 -1
  60. package/dist/components/content/editor-save.js +27 -25
  61. package/dist/components/content/editor-save.js.map +1 -1
  62. package/dist/components/content/editors.js +274 -253
  63. package/dist/components/content/editors.js.map +1 -1
  64. package/dist/components/content/favorites.d.ts +1 -1
  65. package/dist/components/content/favorites.js +36 -22
  66. package/dist/components/content/favorites.js.map +1 -1
  67. package/dist/components/content/grid.d.ts +11 -4
  68. package/dist/components/content/grid.js +52 -43
  69. package/dist/components/content/grid.js.map +1 -1
  70. package/dist/components/content/history.js +83 -63
  71. package/dist/components/content/history.js.map +1 -1
  72. package/dist/components/content/index.d.ts +5 -3
  73. package/dist/components/content/listing.d.ts +14 -3
  74. package/dist/components/content/listing.js +65 -56
  75. package/dist/components/content/listing.js.map +1 -1
  76. package/dist/components/content/offer-fields.js +6 -6
  77. package/dist/components/content/paged-grid.d.ts +10 -0
  78. package/dist/components/content/paged-grid.js +116 -0
  79. package/dist/components/content/paged-grid.js.map +1 -0
  80. package/dist/components/content/pagination.d.ts +19 -0
  81. package/dist/components/content/pagination.js +97 -0
  82. package/dist/components/content/pagination.js.map +1 -0
  83. package/dist/components/content/paywall.d.ts +9 -0
  84. package/dist/components/content/paywall.js +27 -3
  85. package/dist/components/content/paywall.js.map +1 -1
  86. package/dist/components/content/post-list.d.ts +37 -0
  87. package/dist/components/content/post-list.js +203 -0
  88. package/dist/components/content/post-list.js.map +1 -0
  89. package/dist/components/content/reader-settings.d.ts +2 -0
  90. package/dist/components/content/reader-settings.js +1 -1
  91. package/dist/components/content/reader-settings.js.map +1 -1
  92. package/dist/components/groups/banner.d.ts +6 -2
  93. package/dist/components/groups/banner.js +79 -61
  94. package/dist/components/groups/banner.js.map +1 -1
  95. package/dist/components/groups/directory.d.ts +12 -2
  96. package/dist/components/groups/directory.js +168 -105
  97. package/dist/components/groups/directory.js.map +1 -1
  98. package/dist/components/groups/editor.js +10 -10
  99. package/dist/components/groups/editor.js.map +1 -1
  100. package/dist/components/groups/entity-chips.d.ts +61 -0
  101. package/dist/components/groups/entity-chips.js +382 -0
  102. package/dist/components/groups/entity-chips.js.map +1 -0
  103. package/dist/components/groups/group-members.d.ts +29 -0
  104. package/dist/components/groups/group-members.js +474 -0
  105. package/dist/components/groups/group-members.js.map +1 -0
  106. package/dist/components/groups/header.js +53 -45
  107. package/dist/components/groups/header.js.map +1 -1
  108. package/dist/components/groups/index.d.ts +3 -1
  109. package/dist/components/groups/memberships.d.ts +3 -36
  110. package/dist/components/groups/memberships.js +105 -480
  111. package/dist/components/groups/memberships.js.map +1 -1
  112. package/dist/components/groups/scoped.d.ts +23 -0
  113. package/dist/components/groups/scoped.js +92 -0
  114. package/dist/components/groups/scoped.js.map +1 -0
  115. package/dist/components/media-folder-editor.d.ts +5 -0
  116. package/dist/components/media-folder-editor.js +418 -230
  117. package/dist/components/media-folder-editor.js.map +1 -1
  118. package/dist/components/sortable-list.d.ts +5 -1
  119. package/dist/components/sortable-list.js +97 -81
  120. package/dist/components/sortable-list.js.map +1 -1
  121. package/dist/index.d.ts +1 -1
  122. package/dist/index.js +41 -37
  123. package/dist/locales/admin/de.js +78 -45
  124. package/dist/locales/admin/de.js.map +1 -1
  125. package/dist/locales/admin/en.d.ts +74 -41
  126. package/dist/locales/admin/en.js +78 -45
  127. package/dist/locales/admin/en.js.map +1 -1
  128. package/dist/locales/admin/es.js +78 -45
  129. package/dist/locales/admin/es.js.map +1 -1
  130. package/dist/locales/admin/ja.js +78 -45
  131. package/dist/locales/admin/ja.js.map +1 -1
  132. package/dist/locales/admin/ko.js +78 -45
  133. package/dist/locales/admin/ko.js.map +1 -1
  134. package/dist/locales/admin/zh.js +78 -45
  135. package/dist/locales/admin/zh.js.map +1 -1
  136. package/dist/locales/de.js +1 -0
  137. package/dist/locales/de.js.map +1 -1
  138. package/dist/locales/discovery/de.js +25 -1
  139. package/dist/locales/discovery/de.js.map +1 -1
  140. package/dist/locales/discovery/en.d.ts +24 -0
  141. package/dist/locales/discovery/en.js +25 -1
  142. package/dist/locales/discovery/en.js.map +1 -1
  143. package/dist/locales/discovery/es.js +25 -1
  144. package/dist/locales/discovery/es.js.map +1 -1
  145. package/dist/locales/discovery/ja.js +25 -1
  146. package/dist/locales/discovery/ja.js.map +1 -1
  147. package/dist/locales/discovery/ko.js +25 -1
  148. package/dist/locales/discovery/ko.js.map +1 -1
  149. package/dist/locales/discovery/zh.js +25 -1
  150. package/dist/locales/discovery/zh.js.map +1 -1
  151. package/dist/locales/en.d.ts +119 -43
  152. package/dist/locales/en.js +1 -0
  153. package/dist/locales/en.js.map +1 -1
  154. package/dist/locales/es.js +1 -0
  155. package/dist/locales/es.js.map +1 -1
  156. package/dist/locales/groups/de.js +22 -4
  157. package/dist/locales/groups/de.js.map +1 -1
  158. package/dist/locales/groups/en.d.ts +20 -2
  159. package/dist/locales/groups/en.js +22 -4
  160. package/dist/locales/groups/en.js.map +1 -1
  161. package/dist/locales/groups/es.js +22 -4
  162. package/dist/locales/groups/es.js.map +1 -1
  163. package/dist/locales/groups/ja.js +22 -4
  164. package/dist/locales/groups/ja.js.map +1 -1
  165. package/dist/locales/groups/ko.js +22 -4
  166. package/dist/locales/groups/ko.js.map +1 -1
  167. package/dist/locales/groups/zh.js +22 -4
  168. package/dist/locales/groups/zh.js.map +1 -1
  169. package/dist/locales/ja.js +1 -0
  170. package/dist/locales/ja.js.map +1 -1
  171. package/dist/locales/ko.js +1 -0
  172. package/dist/locales/ko.js.map +1 -1
  173. package/dist/locales/zh.js +1 -0
  174. package/dist/locales/zh.js.map +1 -1
  175. package/dist/react/discovery.d.ts +16 -0
  176. package/dist/react/discovery.js +69 -37
  177. package/dist/react/discovery.js.map +1 -1
  178. package/dist/react/groups.d.ts +19 -3
  179. package/dist/react/groups.js +49 -22
  180. package/dist/react/groups.js.map +1 -1
  181. package/dist/react/index.d.ts +6 -4
  182. package/dist/react/page-notices.d.ts +13 -0
  183. package/dist/react/page-notices.js +38 -0
  184. package/dist/react/page-notices.js.map +1 -0
  185. package/dist/react/post-reads.d.ts +23 -0
  186. package/dist/react/post-reads.js +90 -0
  187. package/dist/react/post-reads.js.map +1 -0
  188. package/dist/react/remembered-sort.d.ts +5 -0
  189. package/dist/react/remembered-sort.js +29 -0
  190. package/dist/react/remembered-sort.js.map +1 -0
  191. package/dist/react/resources.js +2 -2
  192. package/dist/react/resources.js.map +1 -1
  193. package/dist/react/stats.d.ts +3 -7
  194. package/dist/react/stats.js +2 -6
  195. package/dist/react/stats.js.map +1 -1
  196. package/dist/react/use-engagement.d.ts +2 -0
  197. package/dist/react/use-engagement.js +8 -3
  198. package/dist/react/use-engagement.js.map +1 -1
  199. package/dist/react.js +29 -27
  200. package/dist/styles.css +1 -1
  201. package/dist/urls/index.d.ts +1 -1
  202. package/dist/urls.js +2 -2
  203. package/dist/urls.js.map +1 -1
  204. package/package.json +1 -1
  205. package/dist/components/admin/page-review.d.ts +0 -13
  206. package/dist/components/admin/page-review.js +0 -225
  207. package/dist/components/admin/page-review.js.map +0 -1
  208. package/dist/react/page-review.d.ts +0 -14
  209. package/dist/react/page-review.js +0 -37
  210. package/dist/react/page-review.js.map +0 -1
@@ -3,253 +3,351 @@ import { formatDuration as t } from "../../client/gallery.js";
3
3
  import { Select as n, SelectContent as r, SelectItem as i, SelectTrigger as a, SelectValue as o } from "../../ui/select.js";
4
4
  import { AdminRoot as s, LoadFailed as c, NoAccess as l, Rows as u } from "./shell.js";
5
5
  import { useFlowClient as d } from "../../react/admin.js";
6
- import { useActivity as f, useTopContents as p } from "../../react/stats.js";
7
- import { useState as m } from "react";
8
- import { Fragment as h, jsx as g, jsxs as _ } from "react/jsx-runtime";
6
+ import { useActivity as f } from "../../react/stats.js";
7
+ import { useState as p } from "react";
8
+ import { Fragment as m, jsx as h, jsxs as g } from "react/jsx-runtime";
9
+ import { cn as _ } from "cn";
9
10
  //#region src/components/admin/stats-dashboard.tsx
10
11
  var v = (e) => e.map((e) => typeof e == "string" ? {
11
12
  value: e,
12
13
  label: e
13
14
  } : e), y = [
14
- 7,
15
+ 10,
15
16
  30,
16
17
  90
17
- ], b = ["readers", "comments"], x = [
18
- "7d",
19
- "30d",
20
- "90d",
21
- "365d",
22
- "all"
23
- ], S = (e) => e?.code === "forbidden" || e?.code === "unauthorized";
24
- function C({ client: e, baseUrl: t, auth: n, guard: r, appearance: i, className: a, language: o, messages: c, ...l }) {
18
+ ], b = (e) => e?.code === "forbidden" || e?.code === "unauthorized", x = [
19
+ {
20
+ key: "anon",
21
+ fill: "bg-[#2a78d6] dark:bg-[#3987e5]"
22
+ },
23
+ {
24
+ key: "free",
25
+ fill: "bg-[#eb6834] dark:bg-[#d95926]"
26
+ },
27
+ {
28
+ key: "premium",
29
+ fill: "bg-[#1baf7a] dark:bg-[#199e70]"
30
+ }
31
+ ];
32
+ function S({ client: e, baseUrl: t, auth: n, guard: r, appearance: i, className: a, language: o, messages: c, ...l }) {
25
33
  let u = d({
26
34
  client: e,
27
35
  baseUrl: t,
28
36
  auth: n
29
37
  });
30
- return /* @__PURE__ */ g(s, {
38
+ return /* @__PURE__ */ h(s, {
31
39
  flow: "stats-dashboard",
32
40
  appearance: i,
33
41
  className: a,
34
42
  language: o,
35
43
  messages: c,
36
44
  guard: r,
37
- children: /* @__PURE__ */ g(w, {
45
+ children: /* @__PURE__ */ h(C, {
38
46
  ...l,
39
47
  client: u
40
48
  })
41
49
  });
42
50
  }
43
- function w({ kinds: n, contentHref: r, limit: i = 10, client: a }) {
44
- let { t: o } = e(), s = v(n), [d, C] = m(s[0]?.value ?? ""), [w, E] = m(7), [D, O] = m("readers"), [k, A] = m("30d"), j = f(d ? {
45
- kind: d,
46
- days: w
47
- } : null, { client: a }), M = p(d ? {
48
- kind: d,
49
- metric: D,
50
- period: k,
51
- limit: i
52
- } : null, { client: a });
53
- if (S(j.error) || S(M.error)) return /* @__PURE__ */ g(l, { children: o("stats.noAccess") });
54
- let N = j.data?.days ?? [], P = Math.max(1, ...N.map((e) => e.readers)), F = N.reduce((e, t) => e + t.active_ms, 0);
55
- return /* @__PURE__ */ _(h, { children: [
56
- /* @__PURE__ */ _("div", {
51
+ function C({ kinds: t, client: n }) {
52
+ let { t: r } = e(), i = v(t), [a, o] = p(i[0]?.value ?? ""), [s, d] = p(y[0]), _ = f(a ? {
53
+ kind: a,
54
+ days: s
55
+ } : null, { client: n });
56
+ if (b(_.error)) return /* @__PURE__ */ h(l, { children: r("stats.noAccess") });
57
+ let x = [..._.data?.days ?? []].reverse();
58
+ return /* @__PURE__ */ g(m, { children: [/* @__PURE__ */ g("div", {
59
+ className: "flex flex-wrap items-center justify-between gap-2",
60
+ children: [/* @__PURE__ */ h("h2", {
61
+ className: "text-base font-semibold",
62
+ children: r("stats.title")
63
+ }), i.length > 1 && /* @__PURE__ */ h(D, {
64
+ label: r("stats.kind"),
65
+ value: a,
66
+ onChange: o,
67
+ options: i
68
+ })]
69
+ }), /* @__PURE__ */ g("section", {
70
+ className: "grid gap-3",
71
+ "data-ckui": "stats-activity",
72
+ children: [/* @__PURE__ */ g("div", {
57
73
  className: "flex flex-wrap items-center justify-between gap-2",
58
- children: [/* @__PURE__ */ g("h2", {
59
- className: "text-base font-semibold",
60
- children: o("stats.title")
61
- }), s.length > 1 && /* @__PURE__ */ g(T, {
62
- label: o("stats.kind"),
63
- value: d,
64
- onChange: C,
65
- options: s
66
- })]
67
- }),
68
- /* @__PURE__ */ _("section", {
69
- className: "grid gap-3",
70
- "data-ckui": "stats-activity",
71
- children: [/* @__PURE__ */ _("div", {
72
- className: "flex flex-wrap items-center justify-between gap-2",
73
- children: [/* @__PURE__ */ g("h3", {
74
+ children: [/* @__PURE__ */ g("div", {
75
+ className: "grid gap-0.5",
76
+ children: [/* @__PURE__ */ h("h3", {
74
77
  className: "font-medium",
75
- children: o("stats.activity")
76
- }), /* @__PURE__ */ g(T, {
77
- label: o("stats.days"),
78
- value: String(w),
79
- onChange: (e) => E(Number(e)),
80
- options: y.map((e) => ({
81
- value: String(e),
82
- label: o("stats.lastDays", { count: e })
83
- }))
78
+ children: r("stats.activity")
79
+ }), /* @__PURE__ */ h("p", {
80
+ className: "text-xs text-muted-foreground",
81
+ children: r("stats.visitorsNote")
84
82
  })]
85
- }), j.unavailable ? /* @__PURE__ */ g("p", {
86
- className: "text-muted-foreground",
87
- role: "status",
88
- children: o("stats.unavailable")
89
- }) : j.error ? /* @__PURE__ */ g(c, {
90
- error: j.error,
91
- message: o("stats.loadFailed"),
92
- retry: j.reload
93
- }) : j.loading && !j.data ? /* @__PURE__ */ g(u, { count: 2 }) : N.length ? /* @__PURE__ */ _("table", {
94
- className: "w-full text-left tabular-nums",
95
- "aria-busy": j.loading,
83
+ }), /* @__PURE__ */ h(D, {
84
+ label: r("stats.days"),
85
+ value: String(s),
86
+ onChange: (e) => d(Number(e)),
87
+ options: y.map((e) => ({
88
+ value: String(e),
89
+ label: r("stats.lastDays", { count: e })
90
+ }))
91
+ })]
92
+ }), _.unavailable ? /* @__PURE__ */ h("p", {
93
+ className: "text-muted-foreground",
94
+ role: "status",
95
+ children: r("stats.unavailable")
96
+ }) : _.error ? /* @__PURE__ */ h(c, {
97
+ error: _.error,
98
+ message: r("stats.loadFailed"),
99
+ retry: _.reload
100
+ }) : _.loading && !_.data ? /* @__PURE__ */ h(u, { count: 3 }) : /* @__PURE__ */ g("div", {
101
+ className: "grid gap-4",
102
+ "aria-busy": _.loading,
103
+ children: [
104
+ /* @__PURE__ */ h(w, { day: x.at(-1) }),
105
+ /* @__PURE__ */ h(T, { rows: x }),
106
+ /* @__PURE__ */ h(E, { rows: x })
107
+ ]
108
+ })]
109
+ })] });
110
+ }
111
+ function w({ day: n }) {
112
+ let { t: r } = e(), i = [{
113
+ label: r("stats.visitors"),
114
+ value: n?.readers ?? 0,
115
+ fill: void 0
116
+ }, ...x.map((e) => ({
117
+ label: r(`stats.${e.key}`),
118
+ value: n?.[e.key] ?? 0,
119
+ fill: e.fill
120
+ }))];
121
+ return /* @__PURE__ */ g("div", {
122
+ className: "grid gap-2",
123
+ "data-ckui": "stats-today",
124
+ children: [/* @__PURE__ */ g("p", {
125
+ className: "text-xs font-medium text-muted-foreground",
126
+ children: [r("stats.today"), n && n.active_ms > 0 && /* @__PURE__ */ g("span", {
127
+ className: "font-normal",
96
128
  children: [
97
- /* @__PURE__ */ g("thead", {
98
- className: "text-xs text-muted-foreground",
99
- children: /* @__PURE__ */ _("tr", { children: [
100
- /* @__PURE__ */ g("th", {
101
- className: "py-1 font-medium",
102
- children: o("stats.date")
103
- }),
104
- /* @__PURE__ */ g("th", {
105
- className: "py-1 font-medium",
106
- children: o("stats.readers")
107
- }),
108
- /* @__PURE__ */ g("th", {
109
- className: "py-1 text-end font-medium",
110
- children: o("stats.users")
111
- }),
112
- /* @__PURE__ */ g("th", {
113
- className: "py-1 text-end font-medium",
114
- children: o("stats.activeTime")
115
- })
116
- ] })
117
- }),
118
- /* @__PURE__ */ g("tbody", { children: N.map((e) => /* @__PURE__ */ _("tr", {
119
- className: "border-t border-border",
120
- "data-ckui": "stats-day",
121
- children: [
122
- /* @__PURE__ */ g("td", {
123
- className: "py-1.5 pe-3 whitespace-nowrap",
124
- children: e.date
125
- }),
126
- /* @__PURE__ */ g("td", {
127
- className: "w-1/2 py-1.5 pe-3",
128
- children: /* @__PURE__ */ _("span", {
129
- className: "flex items-center gap-2",
130
- children: [/* @__PURE__ */ g("span", {
131
- "aria-hidden": !0,
132
- className: "h-2 rounded-full bg-primary",
133
- style: { width: `${Math.max(2, e.readers / P * 100)}%` }
134
- }), e.readers]
135
- })
136
- }),
137
- /* @__PURE__ */ g("td", {
138
- className: "py-1.5 text-end",
139
- children: e.users
140
- }),
141
- /* @__PURE__ */ g("td", {
142
- className: "py-1.5 text-end",
143
- children: t(Math.floor(e.active_ms / 1e3))
144
- })
145
- ]
146
- }, e.date)) }),
147
- /* @__PURE__ */ g("tfoot", { children: /* @__PURE__ */ _("tr", {
148
- className: "border-t border-border font-medium",
149
- children: [/* @__PURE__ */ g("th", {
150
- scope: "row",
151
- colSpan: 3,
152
- className: "py-1.5 font-medium",
153
- children: o("stats.totalActiveTime")
154
- }), /* @__PURE__ */ g("td", {
155
- className: "py-1.5 text-end",
156
- "data-ckui": "stats-active-total",
157
- children: t(Math.floor(F / 1e3))
158
- })]
159
- }) })
129
+ " · ",
130
+ r("stats.activeTime"),
131
+ " ",
132
+ t(Math.floor(n.active_ms / 1e3))
160
133
  ]
161
- }) : /* @__PURE__ */ g("p", {
162
- className: "text-muted-foreground",
163
- children: o("stats.noActivity")
164
134
  })]
165
- }),
166
- /* @__PURE__ */ _("section", {
167
- className: "grid gap-3",
168
- "data-ckui": "stats-top",
169
- children: [/* @__PURE__ */ _("div", {
170
- className: "flex flex-wrap items-center justify-between gap-2",
171
- children: [/* @__PURE__ */ g("h3", {
172
- className: "font-medium",
173
- children: o("stats.top")
174
- }), /* @__PURE__ */ _("div", {
175
- className: "flex flex-wrap gap-2",
176
- children: [/* @__PURE__ */ g(T, {
177
- label: o("stats.metric"),
178
- value: D,
179
- onChange: (e) => O(e),
180
- options: b.map((e) => ({
181
- value: e,
182
- label: o(`stats.metric_${e}`)
183
- }))
184
- }), /* @__PURE__ */ g(T, {
185
- label: o("stats.period"),
186
- value: k,
187
- onChange: (e) => A(e),
188
- options: x.map((e) => ({
189
- value: e,
190
- label: o(`stats.period_${e}`)
191
- }))
192
- })]
135
+ }), /* @__PURE__ */ h("dl", {
136
+ className: "grid grid-cols-2 gap-2 sm:grid-cols-4",
137
+ children: i.map((e) => /* @__PURE__ */ g("div", {
138
+ className: "grid gap-0.5 rounded-lg border border-border p-3",
139
+ children: [/* @__PURE__ */ g("dt", {
140
+ className: "flex items-center gap-1.5 text-xs text-muted-foreground",
141
+ children: [e.fill && /* @__PURE__ */ h("span", {
142
+ "aria-hidden": !0,
143
+ className: _("size-2.5 rounded-sm", e.fill)
144
+ }), e.label]
145
+ }), /* @__PURE__ */ h("dd", {
146
+ className: "text-xl font-semibold tabular-nums",
147
+ children: e.value
193
148
  })]
194
- }), M.unavailable ? /* @__PURE__ */ g("p", {
195
- className: "text-muted-foreground",
196
- role: "status",
197
- children: o("stats.unavailable")
198
- }) : M.error ? /* @__PURE__ */ g(c, {
199
- error: M.error,
200
- message: o("stats.loadFailed"),
201
- retry: M.reload
202
- }) : M.loading && !M.data ? /* @__PURE__ */ g(u, { count: 3 }) : M.data?.contents.length ? /* @__PURE__ */ g("ol", {
203
- className: "grid divide-y divide-border rounded-lg border border-border",
204
- "aria-busy": M.loading,
205
- children: M.data.contents.map((e, t) => {
206
- let n = r ? r(e) : e.path;
207
- return /* @__PURE__ */ _("li", {
208
- className: "flex items-center gap-3 p-2.5",
209
- "data-ckui": "stats-ranked",
149
+ }, e.label))
150
+ })]
151
+ });
152
+ }
153
+ function T({ rows: t }) {
154
+ let n = e(), { t: r } = n, [i, a] = p(null), o = Math.max(0, ...t.map((e) => e.readers)), s = o || 1, c = Math.ceil(t.length / 10), l = new Intl.DateTimeFormat(n.language, {
155
+ month: "short",
156
+ day: "numeric",
157
+ timeZone: "UTC"
158
+ }), u = new Intl.DateTimeFormat(n.language, {
159
+ dateStyle: "medium",
160
+ timeZone: "UTC"
161
+ }), d = (e) => /* @__PURE__ */ new Date(`${e}T00:00:00Z`), f = i === null ? void 0 : t[i];
162
+ return /* @__PURE__ */ g("figure", {
163
+ className: "grid gap-2",
164
+ "data-ckui": "stats-chart",
165
+ children: [
166
+ /* @__PURE__ */ g("figcaption", {
167
+ className: "flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-muted-foreground",
168
+ children: [x.map((e) => /* @__PURE__ */ g("span", {
169
+ className: "flex items-center gap-1.5",
170
+ children: [/* @__PURE__ */ h("span", {
171
+ "aria-hidden": !0,
172
+ className: _("size-2.5 rounded-sm", e.fill)
173
+ }), r(`stats.${e.key}`)]
174
+ }, e.key)), /* @__PURE__ */ h("span", {
175
+ className: "ms-auto tabular-nums",
176
+ children: r("stats.peak", { count: o })
177
+ })]
178
+ }),
179
+ /* @__PURE__ */ g("div", {
180
+ className: "relative",
181
+ children: [
182
+ /* @__PURE__ */ h("div", {
183
+ className: "pointer-events-none absolute inset-x-0 top-0 border-t border-dashed border-border",
184
+ "aria-hidden": !0
185
+ }),
186
+ /* @__PURE__ */ h("div", {
187
+ className: "pointer-events-none absolute inset-x-0 top-1/2 border-t border-dashed border-border/60",
188
+ "aria-hidden": !0
189
+ }),
190
+ /* @__PURE__ */ h("ol", {
191
+ className: "relative flex h-48 items-end gap-1 border-b border-border",
192
+ onMouseLeave: () => a(null),
193
+ children: t.map((e, t) => /* @__PURE__ */ h("li", {
194
+ tabIndex: 0,
195
+ "data-ckui": "stats-day",
196
+ "aria-label": `${u.format(d(e.date))}: ${r("stats.visitors")} ${e.readers}, ${r("stats.anon")} ${e.anon}, ${r("stats.free")} ${e.free}, ${r("stats.premium")} ${e.premium}`,
197
+ className: "flex h-full min-w-0 flex-1 cursor-default flex-col justify-end rounded-sm outline-none hover:bg-muted/40 focus-visible:bg-muted/40 focus-visible:ring-1 focus-visible:ring-ring",
198
+ onMouseEnter: () => a(t),
199
+ onFocus: () => a(t),
200
+ onBlur: () => a(null),
201
+ children: /* @__PURE__ */ h("span", {
202
+ className: "mx-auto flex w-full max-w-10 flex-col-reverse gap-[2px]",
203
+ style: { height: `${e.readers / s * 100}%` },
204
+ children: x.map((t, n) => {
205
+ let r = e[t.key];
206
+ if (!r) return null;
207
+ let i = x.slice(n + 1).every((t) => !e[t.key]);
208
+ return /* @__PURE__ */ h("span", {
209
+ className: _("min-h-[2px] w-full", t.fill, i && "rounded-t-[4px]"),
210
+ style: {
211
+ flexGrow: r,
212
+ flexBasis: 0
213
+ }
214
+ }, t.key);
215
+ })
216
+ })
217
+ }, e.date))
218
+ }),
219
+ f && i !== null && /* @__PURE__ */ g("div", {
220
+ role: "tooltip",
221
+ className: "pointer-events-none absolute -top-2 z-10 grid min-w-36 -translate-y-full gap-1 rounded-md border border-border bg-popover p-2 text-xs text-popover-foreground shadow-md",
222
+ style: { left: `clamp(0%, ${(i + .5) / t.length * 100}% - 4.5rem, calc(100% - 9rem))` },
210
223
  children: [
211
- /* @__PURE__ */ g("span", {
212
- className: "w-6 text-end text-muted-foreground tabular-nums",
213
- children: t + 1
224
+ /* @__PURE__ */ h("p", {
225
+ className: "font-medium",
226
+ children: u.format(d(f.date))
214
227
  }),
215
- n ? /* @__PURE__ */ g("a", {
216
- className: "min-w-0 flex-1 break-words hover:underline",
217
- href: n,
218
- children: e.title
219
- }) : /* @__PURE__ */ g("span", {
220
- className: "min-w-0 flex-1 break-words",
221
- children: e.title
228
+ /* @__PURE__ */ g("p", {
229
+ className: "flex justify-between gap-3 tabular-nums",
230
+ children: [/* @__PURE__ */ h("span", { children: r("stats.visitors") }), /* @__PURE__ */ h("span", {
231
+ className: "font-semibold",
232
+ children: f.readers
233
+ })]
222
234
  }),
223
- /* @__PURE__ */ g("span", {
224
- className: "tabular-nums",
225
- "data-ckui": "stats-value",
226
- children: e.value
227
- })
235
+ [...x].reverse().map((e) => /* @__PURE__ */ g("p", {
236
+ className: "flex items-center justify-between gap-3 tabular-nums text-muted-foreground",
237
+ children: [/* @__PURE__ */ g("span", {
238
+ className: "flex items-center gap-1.5",
239
+ children: [/* @__PURE__ */ h("span", {
240
+ "aria-hidden": !0,
241
+ className: _("size-2 rounded-sm", e.fill)
242
+ }), r(`stats.${e.key}`)]
243
+ }), /* @__PURE__ */ h("span", {
244
+ className: "text-popover-foreground",
245
+ children: f[e.key]
246
+ })]
247
+ }, e.key))
228
248
  ]
229
- }, e.id);
230
- })
231
- }) : /* @__PURE__ */ g("p", {
249
+ })
250
+ ]
251
+ }),
252
+ /* @__PURE__ */ h("ol", {
253
+ "aria-hidden": !0,
254
+ className: "flex gap-1 text-[11px] text-muted-foreground tabular-nums",
255
+ children: t.map((e, n) => /* @__PURE__ */ h("li", {
256
+ className: "min-w-0 flex-1 overflow-visible text-center whitespace-nowrap",
257
+ children: (t.length - 1 - n) % c == 0 ? l.format(d(e.date)) : ""
258
+ }, e.date))
259
+ }),
260
+ o === 0 && /* @__PURE__ */ h("p", {
232
261
  className: "text-muted-foreground",
233
- children: o("stats.noTop")
234
- })]
235
- })
236
- ] });
262
+ children: r("stats.noActivity")
263
+ })
264
+ ]
265
+ });
266
+ }
267
+ function E({ rows: n }) {
268
+ let { t: r } = e();
269
+ return /* @__PURE__ */ g("details", {
270
+ className: "text-sm",
271
+ children: [/* @__PURE__ */ h("summary", {
272
+ className: "cursor-pointer text-xs text-muted-foreground",
273
+ children: r("stats.showTable")
274
+ }), /* @__PURE__ */ g("table", {
275
+ className: "mt-2 w-full text-left tabular-nums",
276
+ children: [/* @__PURE__ */ h("thead", {
277
+ className: "text-xs text-muted-foreground",
278
+ children: /* @__PURE__ */ g("tr", { children: [
279
+ /* @__PURE__ */ h("th", {
280
+ className: "py-1 font-medium",
281
+ children: r("stats.date")
282
+ }),
283
+ /* @__PURE__ */ h("th", {
284
+ className: "py-1 text-end font-medium",
285
+ children: r("stats.visitors")
286
+ }),
287
+ /* @__PURE__ */ h("th", {
288
+ className: "py-1 text-end font-medium",
289
+ children: r("stats.anon")
290
+ }),
291
+ /* @__PURE__ */ h("th", {
292
+ className: "py-1 text-end font-medium",
293
+ children: r("stats.free")
294
+ }),
295
+ /* @__PURE__ */ h("th", {
296
+ className: "py-1 text-end font-medium",
297
+ children: r("stats.premium")
298
+ }),
299
+ /* @__PURE__ */ h("th", {
300
+ className: "py-1 text-end font-medium",
301
+ children: r("stats.activeTime")
302
+ })
303
+ ] })
304
+ }), /* @__PURE__ */ h("tbody", { children: [...n].reverse().map((e) => /* @__PURE__ */ g("tr", {
305
+ className: "border-t border-border",
306
+ children: [
307
+ /* @__PURE__ */ h("td", {
308
+ className: "py-1.5 pe-3 whitespace-nowrap",
309
+ children: e.date
310
+ }),
311
+ /* @__PURE__ */ h("td", {
312
+ className: "py-1.5 text-end",
313
+ children: e.readers
314
+ }),
315
+ /* @__PURE__ */ h("td", {
316
+ className: "py-1.5 text-end",
317
+ children: e.anon
318
+ }),
319
+ /* @__PURE__ */ h("td", {
320
+ className: "py-1.5 text-end",
321
+ children: e.free
322
+ }),
323
+ /* @__PURE__ */ h("td", {
324
+ className: "py-1.5 text-end",
325
+ children: e.premium
326
+ }),
327
+ /* @__PURE__ */ h("td", {
328
+ className: "py-1.5 text-end",
329
+ children: t(Math.floor(e.active_ms / 1e3))
330
+ })
331
+ ]
332
+ }, e.date)) })]
333
+ })]
334
+ });
237
335
  }
238
- function T({ label: e, value: t, onChange: s, options: c }) {
239
- return /* @__PURE__ */ _(n, {
336
+ function D({ label: e, value: t, onChange: s, options: c }) {
337
+ return /* @__PURE__ */ g(n, {
240
338
  value: t,
241
339
  onValueChange: (e) => s(String(e)),
242
- children: [/* @__PURE__ */ g(a, {
340
+ children: [/* @__PURE__ */ h(a, {
243
341
  size: "sm",
244
342
  "aria-label": e,
245
- children: /* @__PURE__ */ g(o, { children: (e) => c.find((t) => t.value === e)?.label ?? e })
246
- }), /* @__PURE__ */ g(r, { children: c.map((e) => /* @__PURE__ */ g(i, {
343
+ children: /* @__PURE__ */ h(o, { children: (e) => c.find((t) => t.value === e)?.label ?? e })
344
+ }), /* @__PURE__ */ h(r, { children: c.map((e) => /* @__PURE__ */ h(i, {
247
345
  value: e.value,
248
346
  children: e.label
249
347
  }, e.value)) })]
250
348
  });
251
349
  }
252
350
  //#endregion
253
- export { C as StatsDashboard };
351
+ export { S as StatsDashboard };
254
352
 
255
353
  //# sourceMappingURL=stats-dashboard.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"stats-dashboard.js","names":[],"sources":["../../../src/components/admin/stats-dashboard.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { ContentKitError } from \"../../client/errors.js\";\nimport type { Period } from \"../../client/discovery.js\";\nimport { formatDuration } from \"../../client/gallery.js\";\nimport type { TopQuery } from \"../../client/stats.js\";\nimport type { RankedContent } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useFlowClient } from \"../../react/admin.js\";\nimport { useActivity, useTopContents } from \"../../react/stats.js\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { AdminRoot, LoadFailed, NoAccess, Rows, type AdminFlowProps } from \"./shell.js\";\n\ntype Choice = string | { value: string; label: string };\ntype Metric = NonNullable<TopQuery[\"metric\"]>;\n\nexport interface StatsDashboardProps extends AdminFlowProps {\n /** Content kinds offered: codes, or codes with labels; the first shows first. */\n kinds: readonly Choice[];\n /** A ranked item's link; default its path. */\n contentHref?: (content: RankedContent) => string | undefined;\n /** Rows in the ranking. Default 10. */\n limit?: number;\n}\n\nconst choices = (list: readonly Choice[]) => list.map((c) => (typeof c === \"string\" ? { value: c, label: c } : c));\nconst DAYS = [7, 30, 90] as const;\nconst METRICS: readonly Metric[] = [\"readers\", \"comments\"];\nconst PERIODS: readonly Period[] = [\"7d\", \"30d\", \"90d\", \"365d\", \"all\"];\nconst refused = (e?: ContentKitError) => e?.code === \"forbidden\" || e?.code === \"unauthorized\";\n\n/**\n * Staff statistics from measured attention: daily distinct people and focused\n * active time, plus content ranked by distinct engaged people or comments.\n * Daily people are never summed into a misleading period-unique total.\n * Comments remain available without the signal plane.\n */\nexport function StatsDashboard({ client: own, baseUrl, auth, guard, appearance, className, language, messages, ...p }: StatsDashboardProps) {\n const client = useFlowClient({ client: own, baseUrl, auth });\n return (\n <AdminRoot flow=\"stats-dashboard\" appearance={appearance} className={className} language={language} messages={messages} guard={guard}>\n <Dashboard {...p} client={client} />\n </AdminRoot>\n );\n}\n\ntype Inner = Omit<StatsDashboardProps, \"client\" | \"baseUrl\" | \"auth\" | \"guard\" | \"appearance\" | \"className\" | \"language\" | \"messages\"> & { client: ContentKitClient };\n\nfunction Dashboard({ kinds, contentHref, limit = 10, client }: Inner) {\n const { t } = useMessages();\n const options = choices(kinds);\n const [kind, setKind] = useState(options[0]?.value ?? \"\");\n const [days, setDays] = useState<number>(7);\n const [metric, setMetric] = useState<Metric>(\"readers\");\n const [period, setPeriod] = useState<Period>(\"30d\");\n const activity = useActivity(kind ? { kind, days } : null, { client });\n const top = useTopContents(kind ? { kind, metric, period, limit } : null, { client });\n if (refused(activity.error) || refused(top.error)) return <NoAccess>{t(\"stats.noAccess\")}</NoAccess>;\n const rows = activity.data?.days ?? [];\n const most = Math.max(1, ...rows.map((d) => d.readers));\n const totalActiveMS = rows.reduce((n, d) => n + d.active_ms, 0);\n return (\n <>\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <h2 className=\"text-base font-semibold\">{t(\"stats.title\")}</h2>\n {options.length > 1 && <Pick label={t(\"stats.kind\")} value={kind} onChange={setKind} options={options} />}\n </div>\n\n <section className=\"grid gap-3\" data-ckui=\"stats-activity\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <h3 className=\"font-medium\">{t(\"stats.activity\")}</h3>\n <Pick\n label={t(\"stats.days\")}\n value={String(days)}\n onChange={(v) => setDays(Number(v))}\n options={DAYS.map((d) => ({ value: String(d), label: t(\"stats.lastDays\", { count: d }) }))}\n />\n </div>\n {activity.unavailable ? (\n <p className=\"text-muted-foreground\" role=\"status\">\n {t(\"stats.unavailable\")}\n </p>\n ) : activity.error ? (\n <LoadFailed error={activity.error} message={t(\"stats.loadFailed\")} retry={activity.reload} />\n ) : activity.loading && !activity.data ? (\n <Rows count={2} />\n ) : !rows.length ? (\n <p className=\"text-muted-foreground\">{t(\"stats.noActivity\")}</p>\n ) : (\n <table className=\"w-full text-left tabular-nums\" aria-busy={activity.loading}>\n <thead className=\"text-xs text-muted-foreground\">\n <tr>\n <th className=\"py-1 font-medium\">{t(\"stats.date\")}</th>\n <th className=\"py-1 font-medium\">{t(\"stats.readers\")}</th>\n <th className=\"py-1 text-end font-medium\">{t(\"stats.users\")}</th>\n <th className=\"py-1 text-end font-medium\">{t(\"stats.activeTime\")}</th>\n </tr>\n </thead>\n <tbody>\n {rows.map((d) => (\n <tr key={d.date} className=\"border-t border-border\" data-ckui=\"stats-day\">\n <td className=\"py-1.5 pe-3 whitespace-nowrap\">{d.date}</td>\n <td className=\"w-1/2 py-1.5 pe-3\">\n <span className=\"flex items-center gap-2\">\n <span aria-hidden className=\"h-2 rounded-full bg-primary\" style={{ width: `${Math.max(2, (d.readers / most) * 100)}%` }} />\n {d.readers}\n </span>\n </td>\n <td className=\"py-1.5 text-end\">{d.users}</td>\n <td className=\"py-1.5 text-end\">{formatDuration(Math.floor(d.active_ms / 1000))}</td>\n </tr>\n ))}\n </tbody>\n <tfoot>\n <tr className=\"border-t border-border font-medium\">\n <th scope=\"row\" colSpan={3} className=\"py-1.5 font-medium\">\n {t(\"stats.totalActiveTime\")}\n </th>\n <td className=\"py-1.5 text-end\" data-ckui=\"stats-active-total\">\n {formatDuration(Math.floor(totalActiveMS / 1000))}\n </td>\n </tr>\n </tfoot>\n </table>\n )}\n </section>\n\n <section className=\"grid gap-3\" data-ckui=\"stats-top\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <h3 className=\"font-medium\">{t(\"stats.top\")}</h3>\n <div className=\"flex flex-wrap gap-2\">\n <Pick label={t(\"stats.metric\")} value={metric} onChange={(v) => setMetric(v as Metric)} options={METRICS.map((x) => ({ value: x, label: t(`stats.metric_${x}`) }))} />\n <Pick label={t(\"stats.period\")} value={period} onChange={(v) => setPeriod(v as Period)} options={PERIODS.map((x) => ({ value: x, label: t(`stats.period_${x}`) }))} />\n </div>\n </div>\n {top.unavailable ? (\n <p className=\"text-muted-foreground\" role=\"status\">\n {t(\"stats.unavailable\")}\n </p>\n ) : top.error ? (\n <LoadFailed error={top.error} message={t(\"stats.loadFailed\")} retry={top.reload} />\n ) : top.loading && !top.data ? (\n <Rows count={3} />\n ) : !top.data?.contents.length ? (\n <p className=\"text-muted-foreground\">{t(\"stats.noTop\")}</p>\n ) : (\n <ol className=\"grid divide-y divide-border rounded-lg border border-border\" aria-busy={top.loading}>\n {top.data.contents.map((c, i) => {\n const href = contentHref ? contentHref(c) : c.path;\n return (\n <li key={c.id} className=\"flex items-center gap-3 p-2.5\" data-ckui=\"stats-ranked\">\n <span className=\"w-6 text-end text-muted-foreground tabular-nums\">{i + 1}</span>\n {href ? (\n <a className=\"min-w-0 flex-1 break-words hover:underline\" href={href}>\n {c.title}\n </a>\n ) : (\n <span className=\"min-w-0 flex-1 break-words\">{c.title}</span>\n )}\n <span className=\"tabular-nums\" data-ckui=\"stats-value\">\n {c.value}\n </span>\n </li>\n );\n })}\n </ol>\n )}\n </section>\n </>\n );\n}\n\nfunction Pick({ label, value, onChange, options }: { label: string; value: string; onChange: (v: string) => void; options: { value: string; label: string }[] }) {\n return (\n <Select value={value} onValueChange={(v) => onChange(String(v))}>\n <SelectTrigger size=\"sm\" aria-label={label}>\n <SelectValue>{(v: string) => options.find((o) => o.value === v)?.label ?? v}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {options.map((o) => (\n <SelectItem key={o.value} value={o.value}>\n {o.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"stats-dashboard.js","names":[],"sources":["../../../src/components/admin/stats-dashboard.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport { useState } from \"react\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { ContentKitError } from \"../../client/errors.js\";\nimport { formatDuration } from \"../../client/gallery.js\";\nimport type { ActivityDay } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useFlowClient } from \"../../react/admin.js\";\nimport { useActivity } from \"../../react/stats.js\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { AdminRoot, LoadFailed, NoAccess, Rows, type AdminFlowProps } from \"./shell.js\";\n\ntype Choice = string | { value: string; label: string };\n\nexport interface StatsDashboardProps extends AdminFlowProps {\n /** Content kinds offered: codes, or codes with labels; the first shows first. */\n kinds: readonly Choice[];\n}\n\nconst choices = (list: readonly Choice[]) => list.map((c) => (typeof c === \"string\" ? { value: c, label: c } : c));\nconst DAYS = [10, 30, 90] as const;\nconst refused = (e?: ContentKitError) => e?.code === \"forbidden\" || e?.code === \"unauthorized\";\n\n// Validated categorical slots (light / dark steps), stacked bottom to top.\nconst AUDIENCES = [\n { key: \"anon\", fill: \"bg-[#2a78d6] dark:bg-[#3987e5]\" },\n { key: \"free\", fill: \"bg-[#eb6834] dark:bg-[#d95926]\" },\n { key: \"premium\", fill: \"bg-[#1baf7a] dark:bg-[#199e70]\" },\n] as const;\n\n/**\n * Staff statistics from measured attention: distinct people per UTC day who\n * read or played at least one item, by audience (anonymous, free, premium).\n * People are distinct within a day only, so days are never summed into a\n * period-unique total.\n */\nexport function StatsDashboard({ client: own, baseUrl, auth, guard, appearance, className, language, messages, ...p }: StatsDashboardProps) {\n const client = useFlowClient({ client: own, baseUrl, auth });\n return (\n <AdminRoot flow=\"stats-dashboard\" appearance={appearance} className={className} language={language} messages={messages} guard={guard}>\n <Dashboard {...p} client={client} />\n </AdminRoot>\n );\n}\n\ntype Inner = Omit<StatsDashboardProps, \"client\" | \"baseUrl\" | \"auth\" | \"guard\" | \"appearance\" | \"className\" | \"language\" | \"messages\"> & { client: ContentKitClient };\n\nfunction Dashboard({ kinds, client }: Inner) {\n const { t } = useMessages();\n const options = choices(kinds);\n const [kind, setKind] = useState(options[0]?.value ?? \"\");\n const [days, setDays] = useState<number>(DAYS[0]);\n const activity = useActivity(kind ? { kind, days } : null, { client });\n if (refused(activity.error)) return <NoAccess>{t(\"stats.noAccess\")}</NoAccess>;\n // Newest first from the server; the chart reads oldest to newest.\n const rows = [...(activity.data?.days ?? [])].reverse();\n return (\n <>\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <h2 className=\"text-base font-semibold\">{t(\"stats.title\")}</h2>\n {options.length > 1 && <Pick label={t(\"stats.kind\")} value={kind} onChange={setKind} options={options} />}\n </div>\n\n <section className=\"grid gap-3\" data-ckui=\"stats-activity\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <div className=\"grid gap-0.5\">\n <h3 className=\"font-medium\">{t(\"stats.activity\")}</h3>\n <p className=\"text-xs text-muted-foreground\">{t(\"stats.visitorsNote\")}</p>\n </div>\n <Pick\n label={t(\"stats.days\")}\n value={String(days)}\n onChange={(v) => setDays(Number(v))}\n options={DAYS.map((d) => ({ value: String(d), label: t(\"stats.lastDays\", { count: d }) }))}\n />\n </div>\n {activity.unavailable ? (\n <p className=\"text-muted-foreground\" role=\"status\">\n {t(\"stats.unavailable\")}\n </p>\n ) : activity.error ? (\n <LoadFailed error={activity.error} message={t(\"stats.loadFailed\")} retry={activity.reload} />\n ) : activity.loading && !activity.data ? (\n <Rows count={3} />\n ) : (\n <div className=\"grid gap-4\" aria-busy={activity.loading}>\n <Today day={rows.at(-1)} />\n <Chart rows={rows} />\n <DayTable rows={rows} />\n </div>\n )}\n </section>\n </>\n );\n}\n\nfunction Today({ day }: { day?: ActivityDay }) {\n const { t } = useMessages();\n const tiles = [\n { label: t(\"stats.visitors\"), value: day?.readers ?? 0, fill: undefined },\n ...AUDIENCES.map((a) => ({ label: t(`stats.${a.key}`), value: day?.[a.key] ?? 0, fill: a.fill })),\n ];\n return (\n <div className=\"grid gap-2\" data-ckui=\"stats-today\">\n <p className=\"text-xs font-medium text-muted-foreground\">\n {t(\"stats.today\")}\n {day && day.active_ms > 0 && (\n <span className=\"font-normal\">\n {\" · \"}\n {t(\"stats.activeTime\")} {formatDuration(Math.floor(day.active_ms / 1000))}\n </span>\n )}\n </p>\n <dl className=\"grid grid-cols-2 gap-2 sm:grid-cols-4\">\n {tiles.map((tile) => (\n <div key={tile.label} className=\"grid gap-0.5 rounded-lg border border-border p-3\">\n <dt className=\"flex items-center gap-1.5 text-xs text-muted-foreground\">\n {tile.fill && <span aria-hidden className={cn(\"size-2.5 rounded-sm\", tile.fill)} />}\n {tile.label}\n </dt>\n <dd className=\"text-xl font-semibold tabular-nums\">{tile.value}</dd>\n </div>\n ))}\n </dl>\n </div>\n );\n}\n\nfunction Chart({ rows }: { rows: ActivityDay[] }) {\n const m = useMessages();\n const { t } = m;\n const [active, setActive] = useState<number | null>(null);\n const most = Math.max(0, ...rows.map((d) => d.readers));\n const scale = most || 1;\n const every = Math.ceil(rows.length / 10);\n const short = new Intl.DateTimeFormat(m.language, { month: \"short\", day: \"numeric\", timeZone: \"UTC\" });\n const long = new Intl.DateTimeFormat(m.language, { dateStyle: \"medium\", timeZone: \"UTC\" });\n const at = (date: string) => new Date(`${date}T00:00:00Z`);\n const shown = active === null ? undefined : rows[active];\n return (\n <figure className=\"grid gap-2\" data-ckui=\"stats-chart\">\n <figcaption className=\"flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-muted-foreground\">\n {AUDIENCES.map((a) => (\n <span key={a.key} className=\"flex items-center gap-1.5\">\n <span aria-hidden className={cn(\"size-2.5 rounded-sm\", a.fill)} />\n {t(`stats.${a.key}`)}\n </span>\n ))}\n <span className=\"ms-auto tabular-nums\">{t(\"stats.peak\", { count: most })}</span>\n </figcaption>\n <div className=\"relative\">\n <div className=\"pointer-events-none absolute inset-x-0 top-0 border-t border-dashed border-border\" aria-hidden />\n <div className=\"pointer-events-none absolute inset-x-0 top-1/2 border-t border-dashed border-border/60\" aria-hidden />\n <ol className=\"relative flex h-48 items-end gap-1 border-b border-border\" onMouseLeave={() => setActive(null)}>\n {rows.map((d, i) => (\n <li\n key={d.date}\n tabIndex={0}\n data-ckui=\"stats-day\"\n aria-label={`${long.format(at(d.date))}: ${t(\"stats.visitors\")} ${d.readers}, ${t(\"stats.anon\")} ${d.anon}, ${t(\"stats.free\")} ${d.free}, ${t(\"stats.premium\")} ${d.premium}`}\n className=\"flex h-full min-w-0 flex-1 cursor-default flex-col justify-end rounded-sm outline-none hover:bg-muted/40 focus-visible:bg-muted/40 focus-visible:ring-1 focus-visible:ring-ring\"\n onMouseEnter={() => setActive(i)}\n onFocus={() => setActive(i)}\n onBlur={() => setActive(null)}\n >\n {/* 2px surface gaps between segments; the top segment carries the 4px rounded end. */}\n <span className=\"mx-auto flex w-full max-w-10 flex-col-reverse gap-[2px]\" style={{ height: `${(d.readers / scale) * 100}%` }}>\n {AUDIENCES.map((a, j) => {\n const value = d[a.key];\n if (!value) return null;\n const top = AUDIENCES.slice(j + 1).every((b) => !d[b.key]);\n return <span key={a.key} className={cn(\"min-h-[2px] w-full\", a.fill, top && \"rounded-t-[4px]\")} style={{ flexGrow: value, flexBasis: 0 }} />;\n })}\n </span>\n </li>\n ))}\n </ol>\n {shown && active !== null && (\n <div\n role=\"tooltip\"\n className=\"pointer-events-none absolute -top-2 z-10 grid min-w-36 -translate-y-full gap-1 rounded-md border border-border bg-popover p-2 text-xs text-popover-foreground shadow-md\"\n style={{ left: `clamp(0%, ${((active + 0.5) / rows.length) * 100}% - 4.5rem, calc(100% - 9rem))` }}\n >\n <p className=\"font-medium\">{long.format(at(shown.date))}</p>\n <p className=\"flex justify-between gap-3 tabular-nums\">\n <span>{t(\"stats.visitors\")}</span>\n <span className=\"font-semibold\">{shown.readers}</span>\n </p>\n {[...AUDIENCES].reverse().map((a) => (\n <p key={a.key} className=\"flex items-center justify-between gap-3 tabular-nums text-muted-foreground\">\n <span className=\"flex items-center gap-1.5\">\n <span aria-hidden className={cn(\"size-2 rounded-sm\", a.fill)} />\n {t(`stats.${a.key}`)}\n </span>\n <span className=\"text-popover-foreground\">{shown[a.key]}</span>\n </p>\n ))}\n </div>\n )}\n </div>\n <ol aria-hidden className=\"flex gap-1 text-[11px] text-muted-foreground tabular-nums\">\n {rows.map((d, i) => (\n <li key={d.date} className=\"min-w-0 flex-1 overflow-visible text-center whitespace-nowrap\">\n {(rows.length - 1 - i) % every === 0 ? short.format(at(d.date)) : \"\"}\n </li>\n ))}\n </ol>\n {most === 0 && <p className=\"text-muted-foreground\">{t(\"stats.noActivity\")}</p>}\n </figure>\n );\n}\n\nfunction DayTable({ rows }: { rows: ActivityDay[] }) {\n const { t } = useMessages();\n return (\n <details className=\"text-sm\">\n <summary className=\"cursor-pointer text-xs text-muted-foreground\">{t(\"stats.showTable\")}</summary>\n <table className=\"mt-2 w-full text-left tabular-nums\">\n <thead className=\"text-xs text-muted-foreground\">\n <tr>\n <th className=\"py-1 font-medium\">{t(\"stats.date\")}</th>\n <th className=\"py-1 text-end font-medium\">{t(\"stats.visitors\")}</th>\n <th className=\"py-1 text-end font-medium\">{t(\"stats.anon\")}</th>\n <th className=\"py-1 text-end font-medium\">{t(\"stats.free\")}</th>\n <th className=\"py-1 text-end font-medium\">{t(\"stats.premium\")}</th>\n <th className=\"py-1 text-end font-medium\">{t(\"stats.activeTime\")}</th>\n </tr>\n </thead>\n <tbody>\n {[...rows].reverse().map((d) => (\n <tr key={d.date} className=\"border-t border-border\">\n <td className=\"py-1.5 pe-3 whitespace-nowrap\">{d.date}</td>\n <td className=\"py-1.5 text-end\">{d.readers}</td>\n <td className=\"py-1.5 text-end\">{d.anon}</td>\n <td className=\"py-1.5 text-end\">{d.free}</td>\n <td className=\"py-1.5 text-end\">{d.premium}</td>\n <td className=\"py-1.5 text-end\">{formatDuration(Math.floor(d.active_ms / 1000))}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </details>\n );\n}\n\nfunction Pick({ label, value, onChange, options }: { label: string; value: string; onChange: (v: string) => void; options: { value: string; label: string }[] }) {\n return (\n <Select value={value} onValueChange={(v) => onChange(String(v))}>\n <SelectTrigger size=\"sm\" aria-label={label}>\n <SelectValue>{(v: string) => options.find((o) => o.value === v)?.label ?? v}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {options.map((o) => (\n <SelectItem key={o.value} value={o.value}>\n {o.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n}\n"],"mappings":"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"}