@coursebuilder/analytics 1.1.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 (56) hide show
  1. package/LICENSE +21 -0
  2. package/dist/api/index.d.ts +158 -0
  3. package/dist/api/index.js +317 -0
  4. package/dist/api/index.js.map +1 -0
  5. package/dist/catalog.d.ts +14 -0
  6. package/dist/catalog.js +209 -0
  7. package/dist/catalog.js.map +1 -0
  8. package/dist/components/index.d.ts +172 -0
  9. package/dist/components/index.js +1258 -0
  10. package/dist/components/index.js.map +1 -0
  11. package/dist/engine.d.ts +20 -0
  12. package/dist/engine.js +350 -0
  13. package/dist/engine.js.map +1 -0
  14. package/dist/index.d.ts +3 -0
  15. package/dist/index.js +353 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/providers/database.d.ts +79 -0
  18. package/dist/providers/database.js +533 -0
  19. package/dist/providers/database.js.map +1 -0
  20. package/dist/providers/derived.d.ts +45 -0
  21. package/dist/providers/derived.js +32 -0
  22. package/dist/providers/derived.js.map +1 -0
  23. package/dist/providers/ga4.d.ts +43 -0
  24. package/dist/providers/ga4.js +220 -0
  25. package/dist/providers/ga4.js.map +1 -0
  26. package/dist/providers/index.d.ts +8 -0
  27. package/dist/providers/index.js +1239 -0
  28. package/dist/providers/index.js.map +1 -0
  29. package/dist/providers/mux.d.ts +103 -0
  30. package/dist/providers/mux.js +241 -0
  31. package/dist/providers/mux.js.map +1 -0
  32. package/dist/providers/survey.d.ts +102 -0
  33. package/dist/providers/survey.js +233 -0
  34. package/dist/providers/survey.js.map +1 -0
  35. package/dist/types.d.ts +303 -0
  36. package/dist/types.js +1 -0
  37. package/dist/types.js.map +1 -0
  38. package/package.json +101 -0
  39. package/src/api/catalog-handler.ts +321 -0
  40. package/src/api/index.ts +4 -0
  41. package/src/api/token-handler.ts +71 -0
  42. package/src/catalog.ts +223 -0
  43. package/src/components/country-chart.tsx +114 -0
  44. package/src/components/index.ts +5 -0
  45. package/src/components/omnibus-dashboard.tsx +1460 -0
  46. package/src/components/revenue-chart.tsx +251 -0
  47. package/src/components/use-chart-colors.ts +75 -0
  48. package/src/engine.ts +201 -0
  49. package/src/index.ts +7 -0
  50. package/src/providers/database.ts +795 -0
  51. package/src/providers/derived.ts +79 -0
  52. package/src/providers/ga4.ts +173 -0
  53. package/src/providers/index.ts +44 -0
  54. package/src/providers/mux.ts +438 -0
  55. package/src/providers/survey.ts +487 -0
  56. package/src/types.ts +333 -0
@@ -0,0 +1,1258 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
4
+ // src/components/omnibus-dashboard.tsx
5
+ import React3, { useState as useState2, useTransition } from "react";
6
+ import { CheckIcon, ChevronDownIcon, ClipboardIcon, ClipboardListIcon, ClockIcon, DollarSignIcon, ExternalLinkIcon, FilmIcon, GlobeIcon, LinkIcon, Loader2Icon, MousePointerClickIcon, PlayIcon, ShoppingCartIcon, TrendingUpIcon } from "lucide-react";
7
+ import { parseAsStringLiteral, useQueryState } from "nuqs";
8
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@coursebuilder/ui";
9
+
10
+ // src/components/country-chart.tsx
11
+ import React from "react";
12
+ import { Bar, BarChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";
13
+
14
+ // src/components/use-chart-colors.ts
15
+ import { useEffect, useState } from "react";
16
+ var FALLBACK = {
17
+ primary: "#d4a053",
18
+ primaryMuted: "rgba(212, 160, 83, 0.2)",
19
+ foreground: "#e5e5e5",
20
+ mutedForeground: "#999",
21
+ gridLine: "#333",
22
+ cardBg: "#1a1a1a",
23
+ hoverBg: "#222"
24
+ };
25
+ function getCSSVar(name) {
26
+ return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
27
+ }
28
+ __name(getCSSVar, "getCSSVar");
29
+ function oklchToUsable(raw) {
30
+ if (!raw)
31
+ return "";
32
+ return raw.startsWith("oklch") ? raw : raw;
33
+ }
34
+ __name(oklchToUsable, "oklchToUsable");
35
+ function useChartColors() {
36
+ const [colors, setColors] = useState(FALLBACK);
37
+ useEffect(() => {
38
+ function update() {
39
+ const primary = getCSSVar("--primary");
40
+ const muted = getCSSVar("--muted");
41
+ const mutedFg = getCSSVar("--muted-foreground");
42
+ const fg = getCSSVar("--foreground");
43
+ const border = getCSSVar("--border");
44
+ const card = getCSSVar("--card");
45
+ if (!primary)
46
+ return;
47
+ setColors({
48
+ primary: oklchToUsable(primary),
49
+ primaryMuted: oklchToUsable(primary).replace(")", " / 0.2)"),
50
+ foreground: oklchToUsable(fg),
51
+ mutedForeground: oklchToUsable(mutedFg),
52
+ gridLine: oklchToUsable(border),
53
+ cardBg: oklchToUsable(card),
54
+ hoverBg: oklchToUsable(muted)
55
+ });
56
+ }
57
+ __name(update, "update");
58
+ update();
59
+ const observer = new MutationObserver(update);
60
+ observer.observe(document.documentElement, {
61
+ attributes: true,
62
+ attributeFilter: [
63
+ "class"
64
+ ]
65
+ });
66
+ return () => observer.disconnect();
67
+ }, []);
68
+ return colors;
69
+ }
70
+ __name(useChartColors, "useChartColors");
71
+
72
+ // src/components/country-chart.tsx
73
+ function countryFlag(code) {
74
+ if (!code || code.length !== 2)
75
+ return "\u{1F30D}";
76
+ const offset = 127462;
77
+ const a = code.toUpperCase().charCodeAt(0) - 65 + offset;
78
+ const b = code.toUpperCase().charCodeAt(1) - 65 + offset;
79
+ return String.fromCodePoint(a, b);
80
+ }
81
+ __name(countryFlag, "countryFlag");
82
+ function formatCurrency(value) {
83
+ if (value >= 1e3)
84
+ return `$${(value / 1e3).toFixed(1)}k`;
85
+ return `$${value.toFixed(0)}`;
86
+ }
87
+ __name(formatCurrency, "formatCurrency");
88
+ function CustomTooltip({ active, payload }) {
89
+ if (!active || !payload?.length)
90
+ return null;
91
+ const d = payload[0]?.payload;
92
+ if (!d)
93
+ return null;
94
+ return /* @__PURE__ */ React.createElement("div", {
95
+ className: "border-border/50 bg-card rounded-lg border px-3 py-2 shadow-xl"
96
+ }, /* @__PURE__ */ React.createElement("p", {
97
+ className: "text-foreground text-sm font-medium"
98
+ }, countryFlag(d.country), " ", d.country), /* @__PURE__ */ React.createElement("p", {
99
+ className: "text-foreground text-lg font-bold tabular-nums"
100
+ }, "$", d.revenue.toLocaleString(void 0, {
101
+ minimumFractionDigits: 0,
102
+ maximumFractionDigits: 0
103
+ })), /* @__PURE__ */ React.createElement("p", {
104
+ className: "text-muted-foreground text-xs"
105
+ }, d.count, " purchases"));
106
+ }
107
+ __name(CustomTooltip, "CustomTooltip");
108
+ function CountryChart({ data }) {
109
+ const colors = useChartColors();
110
+ const chartData = data.slice(0, 10).map((d) => ({
111
+ ...d,
112
+ label: `${countryFlag(d.country)} ${d.country}`
113
+ }));
114
+ return /* @__PURE__ */ React.createElement("div", {
115
+ className: "h-[280px] w-full"
116
+ }, /* @__PURE__ */ React.createElement(ResponsiveContainer, {
117
+ width: "100%",
118
+ height: "100%"
119
+ }, /* @__PURE__ */ React.createElement(BarChart, {
120
+ data: chartData,
121
+ layout: "vertical",
122
+ margin: {
123
+ top: 4,
124
+ right: 8,
125
+ left: 4,
126
+ bottom: 4
127
+ }
128
+ }, /* @__PURE__ */ React.createElement(CartesianGrid, {
129
+ strokeDasharray: "3 3",
130
+ stroke: colors.gridLine,
131
+ horizontal: false
132
+ }), /* @__PURE__ */ React.createElement(XAxis, {
133
+ type: "number",
134
+ tick: {
135
+ fill: colors.mutedForeground,
136
+ fontSize: 11
137
+ },
138
+ axisLine: {
139
+ stroke: colors.gridLine
140
+ },
141
+ tickLine: false,
142
+ tickFormatter: formatCurrency
143
+ }), /* @__PURE__ */ React.createElement(YAxis, {
144
+ type: "category",
145
+ dataKey: "label",
146
+ tick: {
147
+ fill: colors.mutedForeground,
148
+ fontSize: 12
149
+ },
150
+ axisLine: false,
151
+ tickLine: false,
152
+ width: 60
153
+ }), /* @__PURE__ */ React.createElement(Tooltip, {
154
+ content: /* @__PURE__ */ React.createElement(CustomTooltip, null),
155
+ cursor: {
156
+ fill: colors.hoverBg
157
+ }
158
+ }), /* @__PURE__ */ React.createElement(Bar, {
159
+ dataKey: "revenue",
160
+ radius: [
161
+ 0,
162
+ 4,
163
+ 4,
164
+ 0
165
+ ],
166
+ maxBarSize: 24,
167
+ fill: "#22c55e",
168
+ fillOpacity: 0.7
169
+ }))));
170
+ }
171
+ __name(CountryChart, "CountryChart");
172
+
173
+ // src/components/revenue-chart.tsx
174
+ import React2, { useMemo } from "react";
175
+ import { Area, AreaChart, CartesianGrid as CartesianGrid2, ResponsiveContainer as ResponsiveContainer2, Tooltip as Tooltip2, XAxis as XAxis2, YAxis as YAxis2 } from "recharts";
176
+ function formatDate(dateStr) {
177
+ const d = /* @__PURE__ */ new Date(dateStr + "T00:00:00");
178
+ return d.toLocaleDateString("en-US", {
179
+ month: "short",
180
+ day: "numeric"
181
+ });
182
+ }
183
+ __name(formatDate, "formatDate");
184
+ function formatCurrency2(value) {
185
+ if (value >= 1e3)
186
+ return `$${(value / 1e3).toFixed(1)}k`;
187
+ return `$${value.toFixed(0)}`;
188
+ }
189
+ __name(formatCurrency2, "formatCurrency");
190
+ function CustomTooltip2({ active, payload, label }) {
191
+ if (!active || !payload?.length || !label)
192
+ return null;
193
+ const merged = payload[0]?.payload;
194
+ if (!merged)
195
+ return null;
196
+ const current = merged.revenue ?? 0;
197
+ const previous = merged.prevRevenue ?? 0;
198
+ const hasPrev = previous > 0;
199
+ let delta = "";
200
+ let deltaColor = "";
201
+ if (hasPrev && current > 0) {
202
+ const pct = (current - previous) / previous * 100;
203
+ delta = pct >= 0 ? `+${pct.toFixed(0)}%` : `${pct.toFixed(0)}%`;
204
+ deltaColor = pct >= 0 ? "text-emerald-500" : "text-red-400";
205
+ }
206
+ return /* @__PURE__ */ React2.createElement("div", {
207
+ className: "border-border/50 bg-card rounded-lg border px-3 py-2 shadow-xl"
208
+ }, /* @__PURE__ */ React2.createElement("p", {
209
+ className: "text-muted-foreground text-xs"
210
+ }, formatDate(label)), /* @__PURE__ */ React2.createElement("div", {
211
+ className: "mt-1 flex items-baseline gap-2"
212
+ }, /* @__PURE__ */ React2.createElement("p", {
213
+ className: "text-foreground text-lg font-bold tabular-nums"
214
+ }, "$", current.toLocaleString(void 0, {
215
+ minimumFractionDigits: 0,
216
+ maximumFractionDigits: 0
217
+ })), delta && /* @__PURE__ */ React2.createElement("span", {
218
+ className: `text-xs font-semibold ${deltaColor}`
219
+ }, delta)), /* @__PURE__ */ React2.createElement("p", {
220
+ className: "text-muted-foreground text-xs"
221
+ }, merged.count ?? 0, " purchases"), hasPrev && /* @__PURE__ */ React2.createElement("p", {
222
+ className: "text-muted-foreground border-border/30 mt-1 border-t pt-1 text-[11px]"
223
+ }, "prev: $", previous.toLocaleString(void 0, {
224
+ minimumFractionDigits: 0,
225
+ maximumFractionDigits: 0
226
+ }), " ", "\xB7 ", merged.prevCount ?? 0, " purchases"));
227
+ }
228
+ __name(CustomTooltip2, "CustomTooltip");
229
+ function RevenueChart({ data, previousData = [] }) {
230
+ const colors = useChartColors();
231
+ const merged = useMemo(() => {
232
+ if (previousData.length === 0) {
233
+ return data.map((d) => ({
234
+ ...d,
235
+ prevRevenue: null,
236
+ prevCount: null
237
+ }));
238
+ }
239
+ return data.map((d, i) => {
240
+ const prev = previousData[i];
241
+ return {
242
+ ...d,
243
+ prevRevenue: prev?.revenue ?? null,
244
+ prevCount: prev?.count ?? null
245
+ };
246
+ });
247
+ }, [
248
+ data,
249
+ previousData
250
+ ]);
251
+ const maxRevenue = useMemo(() => Math.max(...merged.map((d) => d.revenue), ...merged.map((d) => d.prevRevenue ?? 0), 0), [
252
+ merged
253
+ ]);
254
+ const nonZeroVals = useMemo(() => merged.map((d) => d.revenue).filter((v) => v > 0).sort((a, b) => a - b), [
255
+ merged
256
+ ]);
257
+ const useLog = useMemo(() => {
258
+ if (nonZeroVals.length < 3)
259
+ return false;
260
+ const median = nonZeroVals[Math.floor(nonZeroVals.length / 2)];
261
+ return maxRevenue > median * 10;
262
+ }, [
263
+ nonZeroVals,
264
+ maxRevenue
265
+ ]);
266
+ const logFloor = nonZeroVals.length > 0 ? Math.floor(nonZeroVals[0] * 0.7) : 100;
267
+ const hasPrev = previousData.length > 0;
268
+ return /* @__PURE__ */ React2.createElement("div", {
269
+ className: "h-[280px] w-full"
270
+ }, /* @__PURE__ */ React2.createElement(ResponsiveContainer2, {
271
+ width: "100%",
272
+ height: "100%"
273
+ }, /* @__PURE__ */ React2.createElement(AreaChart, {
274
+ data: merged,
275
+ margin: {
276
+ top: 8,
277
+ right: 8,
278
+ left: -12,
279
+ bottom: 0
280
+ }
281
+ }, /* @__PURE__ */ React2.createElement("defs", null, /* @__PURE__ */ React2.createElement("linearGradient", {
282
+ id: "revenueGradient",
283
+ x1: "0",
284
+ y1: "0",
285
+ x2: "0",
286
+ y2: "1"
287
+ }, /* @__PURE__ */ React2.createElement("stop", {
288
+ offset: "0%",
289
+ stopColor: "#22c55e",
290
+ stopOpacity: 0.3
291
+ }), /* @__PURE__ */ React2.createElement("stop", {
292
+ offset: "100%",
293
+ stopColor: "#22c55e",
294
+ stopOpacity: 0
295
+ }))), /* @__PURE__ */ React2.createElement(CartesianGrid2, {
296
+ strokeDasharray: "3 3",
297
+ stroke: colors.gridLine,
298
+ vertical: false
299
+ }), /* @__PURE__ */ React2.createElement(XAxis2, {
300
+ dataKey: "date",
301
+ tickFormatter: formatDate,
302
+ tick: {
303
+ fill: colors.mutedForeground,
304
+ fontSize: 11
305
+ },
306
+ axisLine: {
307
+ stroke: colors.gridLine
308
+ },
309
+ tickLine: false,
310
+ interval: "preserveStartEnd",
311
+ minTickGap: 40
312
+ }), /* @__PURE__ */ React2.createElement(YAxis2, {
313
+ tick: {
314
+ fill: colors.mutedForeground,
315
+ fontSize: 11
316
+ },
317
+ axisLine: false,
318
+ tickLine: false,
319
+ scale: useLog ? "log" : "auto",
320
+ domain: useLog ? [
321
+ logFloor,
322
+ "auto"
323
+ ] : [
324
+ 0,
325
+ Math.ceil(maxRevenue * 1.1)
326
+ ],
327
+ allowDataOverflow: useLog,
328
+ tickFormatter: formatCurrency2
329
+ }), /* @__PURE__ */ React2.createElement(Tooltip2, {
330
+ content: /* @__PURE__ */ React2.createElement(CustomTooltip2, null),
331
+ cursor: {
332
+ stroke: "#22c55e",
333
+ strokeWidth: 1,
334
+ strokeDasharray: "4 4"
335
+ }
336
+ }), hasPrev && /* @__PURE__ */ React2.createElement(Area, {
337
+ type: "linear",
338
+ dataKey: "prevRevenue",
339
+ stroke: colors.mutedForeground,
340
+ strokeWidth: 1.5,
341
+ strokeDasharray: "4 3",
342
+ fill: "none",
343
+ dot: false,
344
+ activeDot: false,
345
+ connectNulls: true
346
+ }), /* @__PURE__ */ React2.createElement(Area, {
347
+ type: "linear",
348
+ dataKey: "revenue",
349
+ stroke: "#22c55e",
350
+ strokeWidth: 2,
351
+ fill: "url(#revenueGradient)",
352
+ dot: false,
353
+ activeDot: {
354
+ r: 5,
355
+ fill: "#22c55e",
356
+ stroke: colors.cardBg,
357
+ strokeWidth: 2
358
+ }
359
+ }))), hasPrev && /* @__PURE__ */ React2.createElement("div", {
360
+ className: "mt-2 flex items-center gap-4 text-[11px]"
361
+ }, /* @__PURE__ */ React2.createElement("span", {
362
+ className: "flex items-center gap-1.5"
363
+ }, /* @__PURE__ */ React2.createElement("span", {
364
+ className: "inline-block h-0.5 w-4 rounded bg-emerald-500"
365
+ }), /* @__PURE__ */ React2.createElement("span", {
366
+ className: "text-muted-foreground"
367
+ }, "Current period")), /* @__PURE__ */ React2.createElement("span", {
368
+ className: "flex items-center gap-1.5"
369
+ }, /* @__PURE__ */ React2.createElement("span", {
370
+ className: "inline-block h-0.5 w-4 rounded",
371
+ style: {
372
+ background: colors.mutedForeground,
373
+ backgroundImage: "repeating-linear-gradient(90deg, transparent, transparent 3px, currentColor 3px, currentColor 5px)"
374
+ }
375
+ }), /* @__PURE__ */ React2.createElement("span", {
376
+ className: "text-muted-foreground"
377
+ }, "Previous period"))));
378
+ }
379
+ __name(RevenueChart, "RevenueChart");
380
+
381
+ // src/components/omnibus-dashboard.tsx
382
+ function fmt$(v) {
383
+ return new Intl.NumberFormat("en-US", {
384
+ style: "currency",
385
+ currency: "USD",
386
+ minimumFractionDigits: 0,
387
+ maximumFractionDigits: 0
388
+ }).format(v);
389
+ }
390
+ __name(fmt$, "fmt$");
391
+ function fmtK(v) {
392
+ if (v >= 1e6)
393
+ return `${(v / 1e6).toFixed(1)}M`;
394
+ if (v >= 1e3)
395
+ return `${(v / 1e3).toFixed(1)}K`;
396
+ return v.toLocaleString();
397
+ }
398
+ __name(fmtK, "fmtK");
399
+ function fmtWatchMs(ms) {
400
+ const hours = ms / 1e3 / 60 / 60;
401
+ if (hours >= 1e3)
402
+ return `${(hours / 1e3).toFixed(1)}k hrs`;
403
+ if (hours >= 1)
404
+ return `${hours.toFixed(0)} hrs`;
405
+ return `${(ms / 1e3 / 60).toFixed(0)} min`;
406
+ }
407
+ __name(fmtWatchMs, "fmtWatchMs");
408
+ function fmtAgo(date) {
409
+ const ms = Date.now() - new Date(date).getTime();
410
+ const h = Math.floor(ms / 36e5);
411
+ if (h < 1)
412
+ return "just now";
413
+ if (h < 24)
414
+ return `${h}h ago`;
415
+ const d = Math.floor(h / 24);
416
+ return d === 1 ? "yesterday" : `${d}d ago`;
417
+ }
418
+ __name(fmtAgo, "fmtAgo");
419
+ function countryFlag2(code) {
420
+ if (!code || code.length !== 2)
421
+ return "\u{1F30D}";
422
+ const offset = 127462;
423
+ const a = code.toUpperCase().charCodeAt(0) - 65 + offset;
424
+ const b = code.toUpperCase().charCodeAt(1) - 65 + offset;
425
+ return String.fromCodePoint(a, b);
426
+ }
427
+ __name(countryFlag2, "countryFlag");
428
+ function Stat({ label, value, sub, icon: Icon, accent }) {
429
+ return /* @__PURE__ */ React3.createElement("div", {
430
+ className: `rounded-xl border p-4 transition-[transform,box-shadow] duration-[160ms] ease-[cubic-bezier(0.23,1,0.32,1)] active:scale-[0.97] motion-safe:hover:-translate-y-0.5 motion-safe:hover:shadow-md [@media(hover:hover)]:cursor-default ${accent ? "border-emerald-500/20 bg-emerald-500/[0.03]" : "border-border/50 bg-card/50"}`
431
+ }, /* @__PURE__ */ React3.createElement("div", {
432
+ className: "flex items-center justify-between"
433
+ }, /* @__PURE__ */ React3.createElement("span", {
434
+ className: "text-muted-foreground text-[11px] font-medium uppercase tracking-wider"
435
+ }, label), /* @__PURE__ */ React3.createElement(Icon, {
436
+ className: "text-muted-foreground/40 group-hover:text-muted-foreground/70 h-3.5 w-3.5 transition-colors duration-200"
437
+ })), /* @__PURE__ */ React3.createElement("div", {
438
+ className: "mt-1.5"
439
+ }, /* @__PURE__ */ React3.createElement("span", {
440
+ className: "text-foreground text-xl font-bold tabular-nums tracking-tight"
441
+ }, value)), sub && /* @__PURE__ */ React3.createElement("p", {
442
+ className: "text-muted-foreground mt-0.5 text-[11px] leading-relaxed"
443
+ }, sub));
444
+ }
445
+ __name(Stat, "Stat");
446
+ function AgentApiCard({ appName = "aihero" }) {
447
+ const [state, setState] = useState2("idle");
448
+ const baseUrl = typeof window !== "undefined" ? window.location.origin : "https://www.aihero.dev";
449
+ const endpoint = `${baseUrl}/api/analytics`;
450
+ const handleCopy = /* @__PURE__ */ __name(async () => {
451
+ setState("generating");
452
+ try {
453
+ const [tokenRes, catalogRes] = await Promise.all([
454
+ fetch("/api/analytics/token", {
455
+ method: "POST"
456
+ }),
457
+ fetch("/api/analytics").then((r) => r.json()).catch(() => null)
458
+ ]);
459
+ if (!tokenRes.ok)
460
+ throw new Error("Failed to generate token");
461
+ const { token, ttlLabel, expiresAt } = await tokenRes.json();
462
+ const surfaces = catalogRes?.surfaces ?? [];
463
+ const categories = /* @__PURE__ */ new Map();
464
+ for (const s of surfaces) {
465
+ categories.set(s.category, (categories.get(s.category) ?? 0) + 1);
466
+ }
467
+ const categoryLine = [
468
+ ...categories.entries()
469
+ ].map(([cat, count]) => `${cat} (${count})`).join(", ");
470
+ const picks = [
471
+ {
472
+ name: "",
473
+ description: "surface catalog"
474
+ }
475
+ ];
476
+ const seen = /* @__PURE__ */ new Set();
477
+ const preferred = [
478
+ "summary",
479
+ "attribution/coverage",
480
+ "traffic",
481
+ "youtube/videos",
482
+ "surveys",
483
+ "correlation/traffic-revenue",
484
+ "correlation/youtube-revenue",
485
+ "attribution/email-campaigns"
486
+ ];
487
+ for (const name of preferred) {
488
+ const s = surfaces.find((x) => x.name === name);
489
+ if (s && !seen.has(s.category)) {
490
+ seen.add(s.category);
491
+ picks.push({
492
+ name: s.name,
493
+ description: s.description
494
+ });
495
+ }
496
+ }
497
+ for (const s of surfaces) {
498
+ if (!seen.has(s.category)) {
499
+ seen.add(s.category);
500
+ picks.push({
501
+ name: s.name,
502
+ description: s.description
503
+ });
504
+ }
505
+ }
506
+ const exampleLines = picks.map((p) => p.name ? `GET ${endpoint}?surface=${p.name}&range=30d \u2192 ${p.description}` : `GET ${endpoint} \u2192 ${p.description}`).join("\n");
507
+ const hasYouTube = surfaces.some((s) => s.category === "youtube");
508
+ const ytNote = hasYouTube ? `
509
+ Important:
510
+ - YouTube surfaces are useful for correlation and content analysis, not live dashboard ops
511
+ - YouTube Analytics data lags by about 48 hours, so call out the delay when interpreting fresh periods
512
+ ` : "";
513
+ const prompt = `# ${appName} Analytics API
514
+ Base: ${endpoint}
515
+ Auth: Bearer ${token}
516
+ Token expires: ${new Date(expiresAt).toLocaleString()} (${ttlLabel})
517
+
518
+ ${exampleLines}
519
+ ${ytNote}
520
+ Example:
521
+ curl -H "Authorization: Bearer ${token}" "${endpoint}?surface=summary&range=30d"
522
+
523
+ Categories: ${categoryLine}
524
+ Every response has contextual next_actions. Errors have codes + fix hints.`;
525
+ await navigator.clipboard.writeText(prompt);
526
+ setState("copied");
527
+ setTimeout(() => setState("idle"), 3e3);
528
+ } catch {
529
+ setState("idle");
530
+ }
531
+ }, "handleCopy");
532
+ return /* @__PURE__ */ React3.createElement("div", {
533
+ className: "border-border/30 flex flex-wrap items-center justify-between gap-2 rounded-lg border px-3 py-2 sm:px-4 sm:py-2.5"
534
+ }, /* @__PURE__ */ React3.createElement("div", {
535
+ className: "flex min-w-0 items-center gap-2 text-xs"
536
+ }, /* @__PURE__ */ React3.createElement("span", {
537
+ className: "text-muted-foreground/60 shrink-0"
538
+ }, "\u26A1"), /* @__PURE__ */ React3.createElement("a", {
539
+ href: "/api/analytics",
540
+ target: "_blank",
541
+ rel: "noopener noreferrer",
542
+ className: "text-muted-foreground hover:text-foreground shrink-0 font-medium underline-offset-2 transition-colors hover:underline"
543
+ }, "/api/analytics"), /* @__PURE__ */ React3.createElement("span", {
544
+ className: "text-muted-foreground/40 hidden sm:inline"
545
+ }, "\xB7"), /* @__PURE__ */ React3.createElement("span", {
546
+ className: "text-muted-foreground/60 hidden sm:inline"
547
+ }, "HATEOAS catalog")), /* @__PURE__ */ React3.createElement("button", {
548
+ onClick: handleCopy,
549
+ disabled: state === "generating",
550
+ className: `flex shrink-0 items-center gap-1.5 rounded-md px-3 py-1.5 text-[11px] font-semibold transition-[transform,background-color,color,box-shadow] duration-[160ms] ease-[cubic-bezier(0.23,1,0.32,1)] active:scale-[0.97] disabled:pointer-events-none ${state === "copied" ? "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400" : state === "generating" ? "bg-muted text-muted-foreground" : "bg-primary/10 text-primary hover:bg-primary/20 shadow-sm"}`
551
+ }, state === "generating" ? /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(Loader2Icon, {
552
+ className: "h-3 w-3 animate-spin"
553
+ }), "Generating token\u2026") : state === "copied" ? /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(CheckIcon, {
554
+ className: "h-3 w-3"
555
+ }), "Copied with token") : /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(ClipboardIcon, {
556
+ className: "h-3 w-3"
557
+ }), "Copy agent prompt")));
558
+ }
559
+ __name(AgentApiCard, "AgentApiCard");
560
+ function TopVideosCard({ label, subtitle, icon: Icon, iconColor, videos }) {
561
+ if (videos.length === 0)
562
+ return null;
563
+ return /* @__PURE__ */ React3.createElement(Card, {
564
+ className: "flex-1"
565
+ }, /* @__PURE__ */ React3.createElement(CardHeader, {
566
+ className: "pb-3"
567
+ }, /* @__PURE__ */ React3.createElement("div", {
568
+ className: "flex items-center gap-2.5"
569
+ }, /* @__PURE__ */ React3.createElement("div", {
570
+ className: `flex h-7 w-7 items-center justify-center rounded-lg ${iconColor}`
571
+ }, /* @__PURE__ */ React3.createElement(Icon, {
572
+ className: "h-3.5 w-3.5"
573
+ })), /* @__PURE__ */ React3.createElement("div", null, /* @__PURE__ */ React3.createElement(CardTitle, {
574
+ className: "text-sm font-semibold"
575
+ }, label), /* @__PURE__ */ React3.createElement(CardDescription, {
576
+ className: "text-[11px]"
577
+ }, subtitle)))), /* @__PURE__ */ React3.createElement(CardContent, {
578
+ className: "space-y-0 pt-0"
579
+ }, videos.map((v, i) => {
580
+ const Row = v.href ? "a" : "div";
581
+ const linkProps = v.href ? {
582
+ href: v.href,
583
+ target: "_blank",
584
+ rel: "noopener noreferrer"
585
+ } : {};
586
+ return /* @__PURE__ */ React3.createElement(Row, {
587
+ key: v.title,
588
+ ...linkProps,
589
+ className: "hover:bg-muted/40 group -mx-2 flex items-center gap-3 rounded-lg px-2 py-2.5 transition-[background-color] duration-[150ms] ease-[cubic-bezier(0.23,1,0.32,1)]"
590
+ }, /* @__PURE__ */ React3.createElement("span", {
591
+ className: "text-muted-foreground/50 w-4 text-right text-xs font-medium tabular-nums"
592
+ }, i + 1), v.thumbnailUrl ? /* @__PURE__ */ React3.createElement("img", {
593
+ src: v.thumbnailUrl,
594
+ alt: "",
595
+ width: 72,
596
+ height: 40,
597
+ loading: "lazy",
598
+ className: "hidden h-10 w-[72px] shrink-0 rounded object-cover sm:block"
599
+ }) : /* @__PURE__ */ React3.createElement("div", {
600
+ className: "bg-muted hidden h-10 w-[72px] shrink-0 items-center justify-center rounded sm:flex"
601
+ }, /* @__PURE__ */ React3.createElement(PlayIcon, {
602
+ className: "text-muted-foreground/40 h-4 w-4",
603
+ "aria-hidden": "true"
604
+ })), /* @__PURE__ */ React3.createElement("div", {
605
+ className: "min-w-0 flex-1"
606
+ }, /* @__PURE__ */ React3.createElement("p", {
607
+ className: "text-foreground/90 group-hover:text-foreground line-clamp-2 text-[13px] font-medium leading-snug transition-colors"
608
+ }, v.title), /* @__PURE__ */ React3.createElement("div", {
609
+ className: "text-muted-foreground mt-1 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-[11px]"
610
+ }, /* @__PURE__ */ React3.createElement("span", {
611
+ className: "flex items-center gap-1"
612
+ }, /* @__PURE__ */ React3.createElement(ClockIcon, {
613
+ className: "h-3 w-3",
614
+ "aria-hidden": "true"
615
+ }), v.watchTime), /* @__PURE__ */ React3.createElement("span", null, fmtK(v.views), " views"), v.badge && /* @__PURE__ */ React3.createElement("span", {
616
+ className: "text-emerald-600 dark:text-emerald-400"
617
+ }, v.badge))), v.href && /* @__PURE__ */ React3.createElement(ExternalLinkIcon, {
618
+ className: "text-muted-foreground/0 group-hover:text-muted-foreground/60 h-3.5 w-3.5 shrink-0 transition-colors",
619
+ "aria-hidden": "true"
620
+ }));
621
+ })));
622
+ }
623
+ __name(TopVideosCard, "TopVideosCard");
624
+ function ShortlinksCard({ shortlinks, totalClicks }) {
625
+ const [expanded, setExpanded] = useState2(false);
626
+ if (shortlinks.length === 0)
627
+ return null;
628
+ const visible = expanded ? shortlinks : shortlinks.slice(0, 10);
629
+ const hasMore = shortlinks.length > 10;
630
+ const totalSignups = shortlinks.reduce((s, l) => s + l.signups, 0);
631
+ const totalPurchases = shortlinks.reduce((s, l) => s + l.purchases, 0);
632
+ return /* @__PURE__ */ React3.createElement(Card, null, /* @__PURE__ */ React3.createElement(CardHeader, null, /* @__PURE__ */ React3.createElement("div", {
633
+ className: "flex items-center gap-2.5"
634
+ }, /* @__PURE__ */ React3.createElement(LinkIcon, {
635
+ className: "text-muted-foreground h-4 w-4"
636
+ }), /* @__PURE__ */ React3.createElement("div", null, /* @__PURE__ */ React3.createElement(CardTitle, {
637
+ className: "text-sm font-semibold"
638
+ }, "Top Shortlinks"), /* @__PURE__ */ React3.createElement(CardDescription, {
639
+ className: "text-[11px]"
640
+ }, fmtK(totalClicks), " clicks \xB7 ", totalSignups.toLocaleString(), " ", "signups \xB7 ", totalPurchases.toLocaleString(), " purchases")))), /* @__PURE__ */ React3.createElement(CardContent, null, /* @__PURE__ */ React3.createElement("div", {
641
+ className: "overflow-x-auto"
642
+ }, /* @__PURE__ */ React3.createElement("table", {
643
+ className: "w-full"
644
+ }, /* @__PURE__ */ React3.createElement("thead", null, /* @__PURE__ */ React3.createElement("tr", {
645
+ className: "text-muted-foreground border-border/50 border-b text-left text-[11px] uppercase tracking-wider"
646
+ }, /* @__PURE__ */ React3.createElement("th", {
647
+ className: "pb-2.5 pr-4 font-medium"
648
+ }, "Link"), /* @__PURE__ */ React3.createElement("th", {
649
+ className: "pb-2.5 pr-4 font-medium"
650
+ }, "Destination"), /* @__PURE__ */ React3.createElement("th", {
651
+ className: "pb-2.5 text-right font-medium"
652
+ }, "Clicks"), /* @__PURE__ */ React3.createElement("th", {
653
+ className: "hidden pb-2.5 text-right font-medium sm:table-cell"
654
+ }, "Signups"), /* @__PURE__ */ React3.createElement("th", {
655
+ className: "hidden pb-2.5 text-right font-medium sm:table-cell"
656
+ }, "Purchases"))), /* @__PURE__ */ React3.createElement("tbody", {
657
+ className: "divide-border/30 divide-y"
658
+ }, visible.map((link) => /* @__PURE__ */ React3.createElement("tr", {
659
+ key: link.shortlinkId,
660
+ className: "group"
661
+ }, /* @__PURE__ */ React3.createElement("td", {
662
+ className: "py-2.5 pr-4 text-sm font-medium"
663
+ }, "/s/", link.slug), /* @__PURE__ */ React3.createElement("td", {
664
+ className: "text-muted-foreground max-w-xs truncate py-2.5 pr-4 text-sm"
665
+ }, /* @__PURE__ */ React3.createElement("a", {
666
+ href: link.url,
667
+ target: "_blank",
668
+ rel: "noopener noreferrer",
669
+ className: "hover:text-foreground inline-flex items-center gap-1 transition-colors"
670
+ }, link.url.replace(/^https?:\/\/(www\.)?/, "").substring(0, 50), /* @__PURE__ */ React3.createElement(ExternalLinkIcon, {
671
+ className: "h-3 w-3 opacity-0 transition-opacity group-hover:opacity-100",
672
+ "aria-hidden": "true"
673
+ }))), /* @__PURE__ */ React3.createElement("td", {
674
+ className: "text-foreground py-2.5 text-right text-sm font-semibold tabular-nums"
675
+ }, link.clicks.toLocaleString()), /* @__PURE__ */ React3.createElement("td", {
676
+ className: "hidden py-2.5 text-right text-sm tabular-nums sm:table-cell"
677
+ }, link.signups > 0 ? link.signups.toLocaleString() : "\u2013"), /* @__PURE__ */ React3.createElement("td", {
678
+ className: `hidden py-2.5 text-right text-sm font-semibold tabular-nums sm:table-cell ${link.purchases > 0 ? "text-emerald-600 dark:text-emerald-400" : "text-muted-foreground"}`
679
+ }, link.purchases > 0 ? link.purchases.toLocaleString() : "\u2013")))))), hasMore && /* @__PURE__ */ React3.createElement("button", {
680
+ onClick: () => setExpanded(!expanded),
681
+ className: "text-muted-foreground hover:text-foreground mt-3 flex w-full items-center justify-center gap-1 text-xs transition-[color,transform] duration-[160ms] ease-[cubic-bezier(0.23,1,0.32,1)] active:scale-[0.97]"
682
+ }, /* @__PURE__ */ React3.createElement(ChevronDownIcon, {
683
+ className: `h-3 w-3 transition-transform duration-[200ms] ease-[cubic-bezier(0.23,1,0.32,1)] ${expanded ? "rotate-180" : ""}`
684
+ }), expanded ? "Show less" : `Show ${shortlinks.length - 10} more`)));
685
+ }
686
+ __name(ShortlinksCard, "ShortlinksCard");
687
+ var RANGES = [
688
+ {
689
+ value: "24h",
690
+ label: "24h"
691
+ },
692
+ {
693
+ value: "7d",
694
+ label: "7d"
695
+ },
696
+ {
697
+ value: "30d",
698
+ label: "30d"
699
+ },
700
+ {
701
+ value: "90d",
702
+ label: "90d"
703
+ },
704
+ {
705
+ value: "all",
706
+ label: "All"
707
+ }
708
+ ];
709
+ var rangeParser = parseAsStringLiteral([
710
+ "24h",
711
+ "7d",
712
+ "30d",
713
+ "90d",
714
+ "all"
715
+ ]).withDefault("30d");
716
+ function OmnibusDashboard({ data, initialRange, appName, surveyDrilldownHref }) {
717
+ const [range, setRange] = useQueryState("range", rangeParser);
718
+ const [isPending, startTransition] = useTransition();
719
+ const rangeLabel = range === "24h" ? "24 hours" : range === "7d" ? "7 days" : range === "90d" ? "90 days" : range === "all" ? "all time" : "30 days";
720
+ const signupCount = data.attribution.find((a) => a.type === "signup")?.count ?? 0;
721
+ const purchaseAttrCount = data.attribution.find((a) => a.type === "purchase")?.count ?? 0;
722
+ const totalClicks = data.shortlinks.reduce((s, l) => s + l.clicks, 0);
723
+ const hasRevenue = data.summary.totalRevenue > 0 || data.summary.purchaseCount > 0;
724
+ const hasRevenueChart = data.daily.some((d) => d.revenue > 0);
725
+ const hasByProduct = data.byProduct.length > 0;
726
+ const hasByCountry = data.byCountry.length > 0;
727
+ const hasRecentPurchases = data.recentPurchases.length > 0;
728
+ const hasAttribution = data.attribution.length > 0;
729
+ const hasAttributionCoverage = data.attributionCoverage != null;
730
+ const hasMux = data.mux != null;
731
+ const hasTraffic = data.traffic != null;
732
+ const hasSurveys = data.surveySegments != null && data.surveySegments.length > 0;
733
+ const hasSurveyCorrelation = data.surveyCorrelation != null;
734
+ const hasShortlinks = data.shortlinks.length > 0;
735
+ return /* @__PURE__ */ React3.createElement("div", {
736
+ className: "flex flex-col gap-5 lg:gap-7"
737
+ }, /* @__PURE__ */ React3.createElement("div", {
738
+ className: "flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between"
739
+ }, /* @__PURE__ */ React3.createElement("div", {
740
+ className: "min-w-0"
741
+ }, /* @__PURE__ */ React3.createElement("h1", {
742
+ className: "text-pretty text-xl font-bold tracking-tight sm:text-2xl"
743
+ }, "Analytics"), /* @__PURE__ */ React3.createElement("p", {
744
+ className: "text-muted-foreground truncate text-[13px]"
745
+ }, "Revenue \xB7 Attribution \xB7 Video \xB7 Traffic \xB7 Surveys \u2014 ", rangeLabel)), /* @__PURE__ */ React3.createElement("div", {
746
+ className: "flex shrink-0 items-center gap-2"
747
+ }, isPending && /* @__PURE__ */ React3.createElement("span", {
748
+ className: "text-muted-foreground animate-pulse text-xs"
749
+ }, "Loading\u2026"), /* @__PURE__ */ React3.createElement("div", {
750
+ className: "border-border/40 bg-muted/20 inline-flex items-center gap-0.5 overflow-x-auto rounded-lg border p-0.5"
751
+ }, RANGES.map(({ value, label }) => /* @__PURE__ */ React3.createElement("button", {
752
+ key: value,
753
+ onClick: () => startTransition(() => {
754
+ setRange(value);
755
+ }),
756
+ disabled: isPending,
757
+ className: `shrink-0 rounded-md px-2.5 py-1 text-[11px] font-medium transition-[background-color,color,transform] duration-[150ms] ease-[cubic-bezier(0.23,1,0.32,1)] active:scale-[0.95] ${range === value ? "bg-foreground text-background shadow-sm" : "text-muted-foreground hover:text-foreground"} ${isPending ? "cursor-wait opacity-60" : ""}`
758
+ }, label))))), /* @__PURE__ */ React3.createElement("div", {
759
+ className: `flex flex-col gap-5 transition-opacity duration-200 lg:gap-7 ${isPending ? "pointer-events-none opacity-50" : ""}`
760
+ }, /* @__PURE__ */ React3.createElement(AgentApiCard, {
761
+ appName
762
+ }), /* @__PURE__ */ React3.createElement("div", {
763
+ className: "grid grid-cols-2 gap-2.5 lg:grid-cols-3 xl:grid-cols-5"
764
+ }, hasRevenue && /* @__PURE__ */ React3.createElement(Stat, {
765
+ label: "Revenue",
766
+ value: fmt$(data.summary.totalRevenue),
767
+ sub: `${data.summary.purchaseCount} purchases \xB7 ${fmt$(data.summary.avgOrderValue)} avg`,
768
+ icon: DollarSignIcon,
769
+ accent: true
770
+ }), hasMux && /* @__PURE__ */ React3.createElement(Stat, {
771
+ label: "Site Watch Time",
772
+ value: fmtWatchMs(data.mux.overview.totalPlayingTimeMs),
773
+ sub: `${fmtK(data.mux.overview.totalViews)} views \xB7 ${fmtK(data.mux.overview.uniqueViewers)} viewers`,
774
+ icon: FilmIcon
775
+ }), hasTraffic && /* @__PURE__ */ React3.createElement(Stat, {
776
+ label: "Sessions",
777
+ value: fmtK(data.traffic.sessions),
778
+ sub: `${fmtK(data.traffic.totalUsers)} users \xB7 ${fmtK(data.traffic.pageviews)} pages`,
779
+ icon: GlobeIcon
780
+ }), purchaseAttrCount > 0 && /* @__PURE__ */ React3.createElement(Stat, {
781
+ label: "Conversions",
782
+ value: `${purchaseAttrCount.toLocaleString()}`,
783
+ sub: `${signupCount} signups \xB7 ${totalClicks > 0 ? (purchaseAttrCount / signupCount * 100).toFixed(1) : 0}% signup\u2192purchase`,
784
+ icon: TrendingUpIcon
785
+ }), hasShortlinks && /* @__PURE__ */ React3.createElement(Stat, {
786
+ label: "Link Clicks",
787
+ value: fmtK(totalClicks),
788
+ sub: `${data.shortlinks.length} active \xB7 ${signupCount} signups`,
789
+ icon: MousePointerClickIcon
790
+ }), hasSurveys && /* @__PURE__ */ React3.createElement(Stat, {
791
+ label: "Surveys",
792
+ value: `${data.surveySegments.length}`,
793
+ sub: `${data.surveySegments.reduce((s, q) => s + q.responses, 0).toLocaleString()} responses`,
794
+ icon: ClipboardListIcon
795
+ })), hasMux && data.mux.topVideos.length > 0 && /* @__PURE__ */ React3.createElement(TopVideosCard, {
796
+ label: "Site Videos",
797
+ subtitle: `${fmtWatchMs(data.mux.overview.totalPlayingTimeMs)} watch time \xB7 ${fmtK(data.mux.overview.uniqueViewers)} viewers`,
798
+ icon: FilmIcon,
799
+ iconColor: "bg-violet-500/10 text-violet-500",
800
+ videos: data.mux.topVideos.slice(0, 5).map((v) => ({
801
+ title: v.title,
802
+ thumbnailUrl: data.muxThumbnails[v.title] ?? null,
803
+ watchTime: fmtWatchMs(v.playingTimeMs),
804
+ views: v.views,
805
+ href: null
806
+ }))
807
+ }), purchaseAttrCount > 0 && /* @__PURE__ */ React3.createElement(Card, null, /* @__PURE__ */ React3.createElement(CardHeader, {
808
+ className: "pb-3"
809
+ }, /* @__PURE__ */ React3.createElement("div", {
810
+ className: "flex items-center gap-2.5"
811
+ }, /* @__PURE__ */ React3.createElement(LinkIcon, {
812
+ className: "text-muted-foreground h-4 w-4"
813
+ }), /* @__PURE__ */ React3.createElement("div", null, /* @__PURE__ */ React3.createElement(CardTitle, {
814
+ className: "text-sm font-semibold"
815
+ }, "Attribution Trail"), /* @__PURE__ */ React3.createElement(CardDescription, {
816
+ className: "text-[11px]"
817
+ }, "Shortlink conversions \xB7 first-touch UTMs accumulating")))), /* @__PURE__ */ React3.createElement(CardContent, null, /* @__PURE__ */ React3.createElement("div", {
818
+ className: "grid gap-3 sm:grid-cols-3"
819
+ }, /* @__PURE__ */ React3.createElement("div", {
820
+ className: "bg-muted/20 rounded-lg p-3.5"
821
+ }, /* @__PURE__ */ React3.createElement("span", {
822
+ className: "text-muted-foreground block text-[11px] font-medium uppercase tracking-wider"
823
+ }, "Click \u2192 Signup"), /* @__PURE__ */ React3.createElement("span", {
824
+ className: "text-foreground mt-1 block text-xl font-bold tabular-nums"
825
+ }, signupCount.toLocaleString())), /* @__PURE__ */ React3.createElement("div", {
826
+ className: "bg-muted/20 rounded-lg p-3.5"
827
+ }, /* @__PURE__ */ React3.createElement("span", {
828
+ className: "text-muted-foreground block text-[11px] font-medium uppercase tracking-wider"
829
+ }, "Click \u2192 Purchase"), /* @__PURE__ */ React3.createElement("span", {
830
+ className: "text-foreground mt-1 block text-xl font-bold tabular-nums"
831
+ }, purchaseAttrCount.toLocaleString())), /* @__PURE__ */ React3.createElement("div", {
832
+ className: "bg-muted/20 rounded-lg p-3.5"
833
+ }, /* @__PURE__ */ React3.createElement("span", {
834
+ className: "text-muted-foreground block text-[11px] font-medium uppercase tracking-wider"
835
+ }, "Conversion Rate"), /* @__PURE__ */ React3.createElement("span", {
836
+ className: "text-foreground mt-1 block text-xl font-bold tabular-nums"
837
+ }, signupCount > 0 ? `${(purchaseAttrCount / signupCount * 100).toFixed(1)}%` : "\u2014"))))), hasAttributionCoverage && data.attributionCoverage.totalPurchases > 0 && /* @__PURE__ */ React3.createElement(Card, null, /* @__PURE__ */ React3.createElement(CardHeader, {
838
+ className: "pb-3"
839
+ }, /* @__PURE__ */ React3.createElement("div", {
840
+ className: "flex items-center gap-2.5"
841
+ }, /* @__PURE__ */ React3.createElement(TrendingUpIcon, {
842
+ className: "text-muted-foreground h-4 w-4"
843
+ }), /* @__PURE__ */ React3.createElement("div", null, /* @__PURE__ */ React3.createElement(CardTitle, {
844
+ className: "text-sm font-semibold"
845
+ }, "Attribution Coverage"), /* @__PURE__ */ React3.createElement(CardDescription, {
846
+ className: "text-[11px]"
847
+ }, "Attributed vs dark revenue")))), /* @__PURE__ */ React3.createElement(CardContent, null, /* @__PURE__ */ React3.createElement("div", {
848
+ className: "grid gap-3 sm:grid-cols-3"
849
+ }, /* @__PURE__ */ React3.createElement("div", {
850
+ className: "bg-muted/20 rounded-lg p-3.5"
851
+ }, /* @__PURE__ */ React3.createElement("span", {
852
+ className: "text-muted-foreground block text-[11px] font-medium uppercase tracking-wider"
853
+ }, "Attributed"), /* @__PURE__ */ React3.createElement("span", {
854
+ className: "text-foreground mt-1 block text-xl font-bold tabular-nums"
855
+ }, fmt$(data.attributionCoverage.attributedRevenue)), /* @__PURE__ */ React3.createElement("span", {
856
+ className: "text-muted-foreground text-[11px]"
857
+ }, (data.attributionCoverage.attributionRate * 100).toFixed(1), "% of total")), /* @__PURE__ */ React3.createElement("div", {
858
+ className: "bg-muted/20 rounded-lg p-3.5"
859
+ }, /* @__PURE__ */ React3.createElement("span", {
860
+ className: "text-muted-foreground block text-[11px] font-medium uppercase tracking-wider"
861
+ }, "Dark / Unknown"), /* @__PURE__ */ React3.createElement("span", {
862
+ className: "text-foreground mt-1 block text-xl font-bold tabular-nums"
863
+ }, fmt$(data.attributionCoverage.unattributedRevenue))), /* @__PURE__ */ React3.createElement("div", {
864
+ className: "bg-muted/20 rounded-lg p-3.5"
865
+ }, /* @__PURE__ */ React3.createElement("span", {
866
+ className: "text-muted-foreground block text-[11px] font-medium uppercase tracking-wider"
867
+ }, "Total Purchases"), /* @__PURE__ */ React3.createElement("span", {
868
+ className: "text-foreground mt-1 block text-xl font-bold tabular-nums"
869
+ }, data.attributionCoverage.totalPurchases.toLocaleString()))))), hasSurveys && /* @__PURE__ */ React3.createElement(Card, null, /* @__PURE__ */ React3.createElement(CardHeader, {
870
+ className: "pb-3"
871
+ }, /* @__PURE__ */ React3.createElement("div", {
872
+ className: "flex items-center justify-between"
873
+ }, /* @__PURE__ */ React3.createElement("div", {
874
+ className: "flex items-center gap-2.5"
875
+ }, /* @__PURE__ */ React3.createElement("div", {
876
+ className: "flex h-7 w-7 items-center justify-center rounded-lg bg-indigo-500/10 text-indigo-500"
877
+ }, /* @__PURE__ */ React3.createElement(ClipboardListIcon, {
878
+ className: "h-3.5 w-3.5"
879
+ })), /* @__PURE__ */ React3.createElement("div", null, /* @__PURE__ */ React3.createElement(CardTitle, {
880
+ className: "text-sm font-semibold"
881
+ }, "Survey Segments"), /* @__PURE__ */ React3.createElement(CardDescription, {
882
+ className: "text-[11px]"
883
+ }, "How users self-categorize across survey questions"))), surveyDrilldownHref && /* @__PURE__ */ React3.createElement("a", {
884
+ href: surveyDrilldownHref,
885
+ className: "text-muted-foreground hover:text-foreground text-[11px] font-medium transition-colors"
886
+ }, "View all surveys \u2192"))), /* @__PURE__ */ React3.createElement(CardContent, {
887
+ className: "space-y-6"
888
+ }, data.surveySegments.slice(0, 8).map((q) => {
889
+ const totalForQuestion = q.answerDistribution.reduce((s, a) => s + a.count, 0);
890
+ return /* @__PURE__ */ React3.createElement("div", {
891
+ key: q.questionId
892
+ }, /* @__PURE__ */ React3.createElement("div", {
893
+ className: "mb-2.5 flex items-start justify-between gap-2"
894
+ }, /* @__PURE__ */ React3.createElement("p", {
895
+ className: "text-foreground text-[13px] font-medium leading-snug"
896
+ }, q.question), /* @__PURE__ */ React3.createElement("span", {
897
+ className: "text-muted-foreground shrink-0 text-[11px] tabular-nums"
898
+ }, totalForQuestion.toLocaleString(), " responses")), /* @__PURE__ */ React3.createElement("div", {
899
+ className: "space-y-1.5"
900
+ }, q.answerDistribution.slice(0, 6).map((a) => {
901
+ const pct = totalForQuestion > 0 ? a.count / totalForQuestion * 100 : 0;
902
+ return /* @__PURE__ */ React3.createElement("div", {
903
+ key: a.answer,
904
+ className: "group flex items-center gap-2.5"
905
+ }, /* @__PURE__ */ React3.createElement("span", {
906
+ className: "text-muted-foreground w-[120px] shrink-0 truncate text-right text-[12px]",
907
+ title: a.answer
908
+ }, a.answer), /* @__PURE__ */ React3.createElement("div", {
909
+ className: "bg-muted/50 relative h-5 flex-1 overflow-hidden rounded"
910
+ }, /* @__PURE__ */ React3.createElement("div", {
911
+ className: "absolute inset-y-0 left-0 rounded bg-indigo-500/20 transition-all duration-300",
912
+ style: {
913
+ width: `${Math.max(pct, 1)}%`
914
+ }
915
+ }), /* @__PURE__ */ React3.createElement("div", {
916
+ className: "relative flex h-full items-center px-2"
917
+ }, /* @__PURE__ */ React3.createElement("span", {
918
+ className: "text-foreground/70 text-[11px] font-medium tabular-nums"
919
+ }, pct.toFixed(0), "%"))), /* @__PURE__ */ React3.createElement("span", {
920
+ className: "text-muted-foreground w-10 shrink-0 text-right text-[11px] tabular-nums"
921
+ }, a.count.toLocaleString()));
922
+ }), q.answerDistribution.length > 6 && /* @__PURE__ */ React3.createElement("p", {
923
+ className: "text-muted-foreground/60 pl-[132px] text-[11px]"
924
+ }, "+", q.answerDistribution.length - 6, " more answers")));
925
+ }))), hasSurveyCorrelation && /* @__PURE__ */ React3.createElement(Card, null, /* @__PURE__ */ React3.createElement(CardHeader, {
926
+ className: "pb-3"
927
+ }, /* @__PURE__ */ React3.createElement("div", {
928
+ className: "flex items-center gap-2.5"
929
+ }, /* @__PURE__ */ React3.createElement("div", {
930
+ className: "flex h-7 w-7 items-center justify-center rounded-lg bg-emerald-500/10 text-emerald-500"
931
+ }, /* @__PURE__ */ React3.createElement(TrendingUpIcon, {
932
+ className: "h-3.5 w-3.5"
933
+ })), /* @__PURE__ */ React3.createElement("div", null, /* @__PURE__ */ React3.createElement(CardTitle, {
934
+ className: "text-sm font-semibold"
935
+ }, "Survey \u2192 Purchase Correlation"), /* @__PURE__ */ React3.createElement(CardDescription, {
936
+ className: "text-[11px]"
937
+ }, "Which survey answers predict purchases? We match survey respondents against the purchases table to find conversion rates per answer.")))), /* @__PURE__ */ React3.createElement(CardContent, {
938
+ className: "space-y-5"
939
+ }, /* @__PURE__ */ React3.createElement("p", {
940
+ className: "text-muted-foreground text-[12px] leading-relaxed"
941
+ }, "We look at every user who answered a survey and check if they ever made a purchase.", /* @__PURE__ */ React3.createElement("strong", {
942
+ className: "text-foreground"
943
+ }, " Conversion"), " = respondents who purchased / total respondents.", /* @__PURE__ */ React3.createElement("strong", {
944
+ className: "text-foreground"
945
+ }, " Baseline"), " = purchase rate of users who ", /* @__PURE__ */ React3.createElement("em", null, "never"), " took a survey, so you can see if survey-takers convert at a higher or lower rate.", /* @__PURE__ */ React3.createElement("strong", {
946
+ className: "text-foreground"
947
+ }, " Revenue"), " = total lifetime spend of respondents who purchased."), (() => {
948
+ const sc = data.surveyCorrelation;
949
+ const totalRespondents = sc.totalRespondents ?? 0;
950
+ const purchased = sc.respondentsWhoPurchased ?? 0;
951
+ const convRate = sc.overallConversionRate ?? 0;
952
+ const revenue = sc.totalRevenueFromRespondents ?? 0;
953
+ const baseline = sc.baselineConversionRate ?? 0;
954
+ const pre = sc.prePurchaseRespondents ?? 0;
955
+ const post = sc.postPurchaseRespondents ?? 0;
956
+ const never = sc.neverPurchasedRespondents ?? 0;
957
+ return /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement("div", {
958
+ className: "grid grid-cols-2 gap-2 sm:grid-cols-5"
959
+ }, /* @__PURE__ */ React3.createElement("div", {
960
+ className: "bg-muted/20 rounded-lg p-3"
961
+ }, /* @__PURE__ */ React3.createElement("span", {
962
+ className: "text-muted-foreground block text-[11px] font-medium uppercase tracking-wider"
963
+ }, "Respondents"), /* @__PURE__ */ React3.createElement("span", {
964
+ className: "text-foreground mt-1 block text-lg font-bold tabular-nums"
965
+ }, totalRespondents.toLocaleString()), /* @__PURE__ */ React3.createElement("span", {
966
+ className: "text-muted-foreground/70 text-[10px]"
967
+ }, "unique users who answered")), /* @__PURE__ */ React3.createElement("div", {
968
+ className: "bg-muted/20 rounded-lg p-3"
969
+ }, /* @__PURE__ */ React3.createElement("span", {
970
+ className: "text-muted-foreground block text-[11px] font-medium uppercase tracking-wider"
971
+ }, "Purchased"), /* @__PURE__ */ React3.createElement("span", {
972
+ className: "text-foreground mt-1 block text-lg font-bold tabular-nums"
973
+ }, purchased.toLocaleString()), /* @__PURE__ */ React3.createElement("span", {
974
+ className: "text-muted-foreground/70 text-[10px]"
975
+ }, "of those also bought")), /* @__PURE__ */ React3.createElement("div", {
976
+ className: "bg-muted/20 rounded-lg p-3"
977
+ }, /* @__PURE__ */ React3.createElement("span", {
978
+ className: "text-muted-foreground block text-[11px] font-medium uppercase tracking-wider"
979
+ }, "Conversion"), /* @__PURE__ */ React3.createElement("span", {
980
+ className: "text-foreground mt-1 block text-lg font-bold tabular-nums"
981
+ }, (convRate * 100).toFixed(1), "%"), /* @__PURE__ */ React3.createElement("span", {
982
+ className: "text-muted-foreground/70 text-[10px]"
983
+ }, "respondent \u2192 purchaser")), /* @__PURE__ */ React3.createElement("div", {
984
+ className: "bg-muted/20 rounded-lg p-3"
985
+ }, /* @__PURE__ */ React3.createElement("span", {
986
+ className: "text-muted-foreground block text-[11px] font-medium uppercase tracking-wider"
987
+ }, "Revenue"), /* @__PURE__ */ React3.createElement("span", {
988
+ className: "text-foreground mt-1 block text-lg font-bold tabular-nums"
989
+ }, fmt$(revenue)), /* @__PURE__ */ React3.createElement("span", {
990
+ className: "text-muted-foreground/70 text-[10px]"
991
+ }, "lifetime spend of purchasers")), /* @__PURE__ */ React3.createElement("div", {
992
+ className: "bg-muted/20 rounded-lg p-3"
993
+ }, /* @__PURE__ */ React3.createElement("span", {
994
+ className: "text-muted-foreground block text-[11px] font-medium uppercase tracking-wider"
995
+ }, "Baseline"), /* @__PURE__ */ React3.createElement("span", {
996
+ className: "text-foreground mt-1 block text-lg font-bold tabular-nums"
997
+ }, (baseline * 100).toFixed(1), "%"), /* @__PURE__ */ React3.createElement("span", {
998
+ className: "text-muted-foreground/70 text-[10px]"
999
+ }, "users who never took a survey"))), /* @__PURE__ */ React3.createElement("div", {
1000
+ className: "border-border/30 rounded-lg border px-4 py-3"
1001
+ }, /* @__PURE__ */ React3.createElement("p", {
1002
+ className: "text-muted-foreground mb-2 text-[11px]"
1003
+ }, "Of the ", purchased.toLocaleString(), " purchasers, when did they first respond relative to their first purchase?"), /* @__PURE__ */ React3.createElement("div", {
1004
+ className: "flex flex-wrap items-center gap-x-6 gap-y-2"
1005
+ }, /* @__PURE__ */ React3.createElement("div", {
1006
+ className: "flex items-center gap-2"
1007
+ }, /* @__PURE__ */ React3.createElement("span", {
1008
+ className: "inline-block h-2 w-2 rounded-full bg-emerald-500"
1009
+ }), /* @__PURE__ */ React3.createElement("span", {
1010
+ className: "text-[12px]"
1011
+ }, "Responded first, then bought:", " ", /* @__PURE__ */ React3.createElement("span", {
1012
+ className: "text-foreground font-semibold tabular-nums"
1013
+ }, pre.toLocaleString()))), /* @__PURE__ */ React3.createElement("div", {
1014
+ className: "flex items-center gap-2"
1015
+ }, /* @__PURE__ */ React3.createElement("span", {
1016
+ className: "inline-block h-2 w-2 rounded-full bg-blue-500"
1017
+ }), /* @__PURE__ */ React3.createElement("span", {
1018
+ className: "text-[12px]"
1019
+ }, "Bought first, then responded:", " ", /* @__PURE__ */ React3.createElement("span", {
1020
+ className: "text-foreground font-semibold tabular-nums"
1021
+ }, post.toLocaleString()))), /* @__PURE__ */ React3.createElement("div", {
1022
+ className: "flex items-center gap-2"
1023
+ }, /* @__PURE__ */ React3.createElement("span", {
1024
+ className: "inline-block h-2 w-2 rounded-full bg-gray-400"
1025
+ }), /* @__PURE__ */ React3.createElement("span", {
1026
+ className: "text-[12px]"
1027
+ }, "Responded but never bought:", " ", /* @__PURE__ */ React3.createElement("span", {
1028
+ className: "text-foreground font-semibold tabular-nums"
1029
+ }, never.toLocaleString()))))));
1030
+ })(), (() => {
1031
+ const allRows = data.surveyCorrelation.byQuestion ?? [];
1032
+ if (allRows.length === 0)
1033
+ return null;
1034
+ const seen = {};
1035
+ const questionIds = [];
1036
+ const answerCounts = {};
1037
+ for (const r of allRows) {
1038
+ if (!seen[r.questionId]) {
1039
+ seen[r.questionId] = true;
1040
+ questionIds.push(r.questionId);
1041
+ answerCounts[r.questionId] = 0;
1042
+ }
1043
+ answerCounts[r.questionId]++;
1044
+ }
1045
+ const filteredQIds = questionIds.filter((qId) => (answerCounts[qId] ?? 0) <= 10);
1046
+ if (filteredQIds.length === 0)
1047
+ return null;
1048
+ return /* @__PURE__ */ React3.createElement("div", {
1049
+ className: "space-y-6"
1050
+ }, /* @__PURE__ */ React3.createElement("p", {
1051
+ className: "text-muted-foreground text-[11px]"
1052
+ }, "Per answer: of everyone who chose this option, what % went on to purchase?"), filteredQIds.map((qId) => {
1053
+ const answers = allRows.filter((r) => r.questionId === qId && r.answer !== "[free text]").sort((a, b) => (b.respondents ?? 0) - (a.respondents ?? 0));
1054
+ if (answers.length === 0)
1055
+ return null;
1056
+ const title = answers[0]?.questionTitle ?? qId;
1057
+ return /* @__PURE__ */ React3.createElement("div", {
1058
+ key: qId
1059
+ }, /* @__PURE__ */ React3.createElement("p", {
1060
+ className: "text-foreground mb-2 text-[13px] font-semibold leading-snug"
1061
+ }, title), /* @__PURE__ */ React3.createElement("div", {
1062
+ className: "space-y-1.5"
1063
+ }, answers.map((row) => {
1064
+ const pct = (row.conversionRate ?? 0) * 100;
1065
+ const purchased = row.purchasers ?? 0;
1066
+ const responded = row.respondents ?? 0;
1067
+ return /* @__PURE__ */ React3.createElement("div", {
1068
+ key: row.answer,
1069
+ className: "group flex items-center gap-2.5"
1070
+ }, /* @__PURE__ */ React3.createElement("span", {
1071
+ className: "text-muted-foreground w-[120px] shrink-0 truncate text-right text-[12px]",
1072
+ title: row.answer
1073
+ }, row.answer), /* @__PURE__ */ React3.createElement("div", {
1074
+ className: "bg-muted/50 relative h-5 flex-1 overflow-hidden rounded"
1075
+ }, /* @__PURE__ */ React3.createElement("div", {
1076
+ className: "absolute inset-y-0 left-0 rounded bg-emerald-500/20 transition-all duration-300",
1077
+ style: {
1078
+ width: `${Math.max(pct, 1)}%`
1079
+ }
1080
+ }), /* @__PURE__ */ React3.createElement("div", {
1081
+ className: "relative flex h-full items-center px-2"
1082
+ }, /* @__PURE__ */ React3.createElement("span", {
1083
+ className: "text-foreground/70 text-[11px] font-medium tabular-nums"
1084
+ }, pct.toFixed(1), "%"))), /* @__PURE__ */ React3.createElement("span", {
1085
+ className: "text-muted-foreground w-12 shrink-0 text-right text-[11px] tabular-nums"
1086
+ }, purchased, "/", responded));
1087
+ })));
1088
+ }));
1089
+ })())), hasRevenueChart && /* @__PURE__ */ React3.createElement(Card, {
1090
+ className: "overflow-hidden"
1091
+ }, /* @__PURE__ */ React3.createElement(CardHeader, {
1092
+ className: "pb-3"
1093
+ }, /* @__PURE__ */ React3.createElement("div", {
1094
+ className: "flex items-center gap-2.5"
1095
+ }, /* @__PURE__ */ React3.createElement(TrendingUpIcon, {
1096
+ className: "text-muted-foreground h-4 w-4"
1097
+ }), /* @__PURE__ */ React3.createElement("div", null, /* @__PURE__ */ React3.createElement(CardTitle, {
1098
+ className: "text-sm font-semibold"
1099
+ }, "Daily Revenue"), /* @__PURE__ */ React3.createElement(CardDescription, {
1100
+ className: "text-[11px]"
1101
+ }, rangeLabel)))), /* @__PURE__ */ React3.createElement(CardContent, {
1102
+ className: "pb-3"
1103
+ }, /* @__PURE__ */ React3.createElement(RevenueChart, {
1104
+ data: data.daily,
1105
+ previousData: data.previousDaily
1106
+ }))), (hasByProduct || hasByCountry) && /* @__PURE__ */ React3.createElement("div", {
1107
+ className: "grid gap-3 md:grid-cols-2"
1108
+ }, hasByProduct && /* @__PURE__ */ React3.createElement(Card, null, /* @__PURE__ */ React3.createElement(CardHeader, {
1109
+ className: "pb-3"
1110
+ }, /* @__PURE__ */ React3.createElement("div", {
1111
+ className: "flex items-center gap-2.5"
1112
+ }, /* @__PURE__ */ React3.createElement(ShoppingCartIcon, {
1113
+ className: "text-muted-foreground h-4 w-4"
1114
+ }), /* @__PURE__ */ React3.createElement("div", null, /* @__PURE__ */ React3.createElement(CardTitle, {
1115
+ className: "text-sm font-semibold"
1116
+ }, "By Product"), /* @__PURE__ */ React3.createElement(CardDescription, {
1117
+ className: "text-[11px]"
1118
+ }, "Revenue breakdown")))), /* @__PURE__ */ React3.createElement(CardContent, null, /* @__PURE__ */ React3.createElement("div", {
1119
+ className: "flex flex-col gap-3"
1120
+ }, data.byProduct.map((p) => {
1121
+ const pct = data.summary.totalRevenue > 0 ? p.revenue / data.summary.totalRevenue * 100 : 0;
1122
+ return /* @__PURE__ */ React3.createElement("div", {
1123
+ key: p.productId,
1124
+ className: "space-y-1.5"
1125
+ }, /* @__PURE__ */ React3.createElement("div", {
1126
+ className: "flex items-center justify-between gap-2"
1127
+ }, /* @__PURE__ */ React3.createElement("span", {
1128
+ className: "truncate text-sm font-medium"
1129
+ }, p.productName), /* @__PURE__ */ React3.createElement("span", {
1130
+ className: "text-foreground shrink-0 text-sm font-semibold tabular-nums"
1131
+ }, fmt$(p.revenue))), /* @__PURE__ */ React3.createElement("div", {
1132
+ className: "flex items-center gap-2"
1133
+ }, /* @__PURE__ */ React3.createElement("div", {
1134
+ className: "bg-muted/50 relative h-1.5 flex-1 overflow-hidden rounded-full"
1135
+ }, /* @__PURE__ */ React3.createElement("div", {
1136
+ className: "absolute inset-y-0 left-0 rounded-full bg-emerald-500/60",
1137
+ style: {
1138
+ width: `${Math.min(pct, 100)}%`
1139
+ }
1140
+ })), /* @__PURE__ */ React3.createElement("span", {
1141
+ className: "text-muted-foreground text-[11px] tabular-nums"
1142
+ }, p.count)));
1143
+ })))), hasByCountry && /* @__PURE__ */ React3.createElement(Card, {
1144
+ className: "overflow-hidden"
1145
+ }, /* @__PURE__ */ React3.createElement(CardHeader, {
1146
+ className: "pb-3"
1147
+ }, /* @__PURE__ */ React3.createElement("div", {
1148
+ className: "flex items-center gap-2.5"
1149
+ }, /* @__PURE__ */ React3.createElement(GlobeIcon, {
1150
+ className: "text-muted-foreground h-4 w-4"
1151
+ }), /* @__PURE__ */ React3.createElement("div", null, /* @__PURE__ */ React3.createElement(CardTitle, {
1152
+ className: "text-sm font-semibold"
1153
+ }, "By Country"), /* @__PURE__ */ React3.createElement(CardDescription, {
1154
+ className: "text-[11px]"
1155
+ }, "Top 10 by revenue")))), /* @__PURE__ */ React3.createElement(CardContent, {
1156
+ className: "pb-3"
1157
+ }, /* @__PURE__ */ React3.createElement(CountryChart, {
1158
+ data: data.byCountry
1159
+ })))), hasMux && data.mux.topVideos.length > 5 && /* @__PURE__ */ React3.createElement(Card, null, /* @__PURE__ */ React3.createElement(CardHeader, {
1160
+ className: "pb-3"
1161
+ }, /* @__PURE__ */ React3.createElement("div", {
1162
+ className: "flex items-center gap-2.5"
1163
+ }, /* @__PURE__ */ React3.createElement(FilmIcon, {
1164
+ className: "text-muted-foreground h-4 w-4"
1165
+ }), /* @__PURE__ */ React3.createElement("div", null, /* @__PURE__ */ React3.createElement(CardTitle, {
1166
+ className: "text-sm font-semibold"
1167
+ }, "All Site Videos"), /* @__PURE__ */ React3.createElement(CardDescription, {
1168
+ className: "text-[11px]"
1169
+ }, "By watch time (Mux)")))), /* @__PURE__ */ React3.createElement(CardContent, null, /* @__PURE__ */ React3.createElement("div", {
1170
+ className: "overflow-x-auto"
1171
+ }, /* @__PURE__ */ React3.createElement("table", {
1172
+ className: "w-full"
1173
+ }, /* @__PURE__ */ React3.createElement("thead", null, /* @__PURE__ */ React3.createElement("tr", {
1174
+ className: "text-muted-foreground border-border/50 border-b text-left text-[11px] uppercase tracking-wider"
1175
+ }, /* @__PURE__ */ React3.createElement("th", {
1176
+ className: "pb-2.5 pr-4 font-medium"
1177
+ }, "#"), /* @__PURE__ */ React3.createElement("th", {
1178
+ className: "pb-2.5 pr-4 font-medium"
1179
+ }, "Title"), /* @__PURE__ */ React3.createElement("th", {
1180
+ className: "pb-2.5 pr-4 text-right font-medium"
1181
+ }, "Watch Time"), /* @__PURE__ */ React3.createElement("th", {
1182
+ className: "pb-2.5 text-right font-medium"
1183
+ }, "Views"))), /* @__PURE__ */ React3.createElement("tbody", {
1184
+ className: "divide-border/30 divide-y"
1185
+ }, data.mux.topVideos.slice(0, 10).map((v, i) => /* @__PURE__ */ React3.createElement("tr", {
1186
+ key: v.title,
1187
+ className: "group"
1188
+ }, /* @__PURE__ */ React3.createElement("td", {
1189
+ className: "text-muted-foreground py-2.5 pr-4 text-sm tabular-nums"
1190
+ }, i + 1), /* @__PURE__ */ React3.createElement("td", {
1191
+ className: "max-w-[300px] truncate py-2.5 pr-4 text-sm font-medium"
1192
+ }, v.title), /* @__PURE__ */ React3.createElement("td", {
1193
+ className: "text-foreground py-2.5 pr-4 text-right text-sm font-semibold tabular-nums"
1194
+ }, fmtWatchMs(v.playingTimeMs)), /* @__PURE__ */ React3.createElement("td", {
1195
+ className: "text-muted-foreground py-2.5 text-right text-sm tabular-nums"
1196
+ }, fmtK(v.views))))))))), hasShortlinks && /* @__PURE__ */ React3.createElement(ShortlinksCard, {
1197
+ shortlinks: data.shortlinks,
1198
+ totalClicks
1199
+ }), hasRecentPurchases && /* @__PURE__ */ React3.createElement(Card, null, /* @__PURE__ */ React3.createElement(CardHeader, {
1200
+ className: "pb-3"
1201
+ }, /* @__PURE__ */ React3.createElement("div", {
1202
+ className: "flex items-center justify-between"
1203
+ }, /* @__PURE__ */ React3.createElement("div", {
1204
+ className: "flex items-center gap-2.5"
1205
+ }, /* @__PURE__ */ React3.createElement("div", {
1206
+ className: "bg-muted flex h-6 w-6 items-center justify-center rounded-md"
1207
+ }, /* @__PURE__ */ React3.createElement(ShoppingCartIcon, {
1208
+ className: "text-muted-foreground h-3.5 w-3.5"
1209
+ })), /* @__PURE__ */ React3.createElement("div", null, /* @__PURE__ */ React3.createElement(CardTitle, {
1210
+ className: "text-sm font-semibold"
1211
+ }, "Team Purchases"), /* @__PURE__ */ React3.createElement(CardDescription, {
1212
+ className: "text-[11px]"
1213
+ }, "Multi-seat deals \xB7 sorted by amount"))), /* @__PURE__ */ React3.createElement("span", {
1214
+ className: "rounded-full bg-red-500/10 px-2 py-0.5 text-[10px] font-medium uppercase tracking-wider text-red-600 dark:text-red-400"
1215
+ }, "OH YEAH"))), /* @__PURE__ */ React3.createElement(CardContent, null, /* @__PURE__ */ React3.createElement("div", {
1216
+ className: "overflow-x-auto"
1217
+ }, /* @__PURE__ */ React3.createElement("table", {
1218
+ className: "w-full"
1219
+ }, /* @__PURE__ */ React3.createElement("thead", null, /* @__PURE__ */ React3.createElement("tr", {
1220
+ className: "text-muted-foreground border-border/50 border-b text-left text-[11px] uppercase tracking-wider"
1221
+ }, /* @__PURE__ */ React3.createElement("th", {
1222
+ className: "pb-2.5 pr-4 text-right font-medium"
1223
+ }, "Amount"), /* @__PURE__ */ React3.createElement("th", {
1224
+ className: "pb-2.5 pr-4 text-right font-medium"
1225
+ }, "Seats"), /* @__PURE__ */ React3.createElement("th", {
1226
+ className: "pb-2.5 pr-4 font-medium"
1227
+ }, "Product"), /* @__PURE__ */ React3.createElement("th", {
1228
+ className: "hidden pb-2.5 pr-4 font-medium sm:table-cell"
1229
+ }, "Buyer"), /* @__PURE__ */ React3.createElement("th", {
1230
+ className: "hidden pb-2.5 pr-4 font-medium sm:table-cell"
1231
+ }, "Country"), /* @__PURE__ */ React3.createElement("th", {
1232
+ className: "pb-2.5 pr-4 font-medium"
1233
+ }, "When"))), /* @__PURE__ */ React3.createElement("tbody", {
1234
+ className: "divide-border/30 divide-y"
1235
+ }, data.recentPurchases.map((p) => /* @__PURE__ */ React3.createElement("tr", {
1236
+ key: p.id
1237
+ }, /* @__PURE__ */ React3.createElement("td", {
1238
+ className: `py-2.5 pr-4 text-right text-sm font-semibold tabular-nums ${p.totalAmount > 0 ? "text-emerald-600 dark:text-emerald-400" : "text-muted-foreground"}`
1239
+ }, p.totalAmount > 0 ? fmt$(p.totalAmount) : "Free"), /* @__PURE__ */ React3.createElement("td", {
1240
+ className: "py-2.5 pr-4 text-right text-sm tabular-nums"
1241
+ }, p.seats ?? "\u2014"), /* @__PURE__ */ React3.createElement("td", {
1242
+ className: "max-w-[200px] truncate py-2.5 pr-4 text-sm font-medium"
1243
+ }, p.productName), /* @__PURE__ */ React3.createElement("td", {
1244
+ className: "text-muted-foreground hidden max-w-[150px] truncate py-2.5 pr-4 text-sm sm:table-cell"
1245
+ }, p.userName ?? p.userEmail ?? "\u2014"), /* @__PURE__ */ React3.createElement("td", {
1246
+ className: "text-muted-foreground hidden py-2.5 pr-4 text-sm sm:table-cell"
1247
+ }, p.country ? `${countryFlag2(p.country)} ${p.country}` : "\u2014"), /* @__PURE__ */ React3.createElement("td", {
1248
+ className: "text-muted-foreground whitespace-nowrap py-2.5 pr-4 text-sm"
1249
+ }, fmtAgo(p.createdAt)))))))))));
1250
+ }
1251
+ __name(OmnibusDashboard, "OmnibusDashboard");
1252
+ export {
1253
+ CountryChart,
1254
+ OmnibusDashboard,
1255
+ RevenueChart,
1256
+ useChartColors
1257
+ };
1258
+ //# sourceMappingURL=index.js.map