@litemetrics/ui 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,1360 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __esm = (fn, res) => function __init() {
7
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
+ };
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
22
+
23
+ // src/utils/worldmap-data.ts
24
+ var numericToISO2;
25
+ var init_worldmap_data = __esm({
26
+ "src/utils/worldmap-data.ts"() {
27
+ "use strict";
28
+ numericToISO2 = {
29
+ "004": "AF",
30
+ "008": "AL",
31
+ "012": "DZ",
32
+ "016": "AS",
33
+ "020": "AD",
34
+ "024": "AO",
35
+ "028": "AG",
36
+ "031": "AZ",
37
+ "032": "AR",
38
+ "036": "AU",
39
+ "040": "AT",
40
+ "044": "BS",
41
+ "048": "BH",
42
+ "050": "BD",
43
+ "051": "AM",
44
+ "056": "BE",
45
+ "060": "BM",
46
+ "064": "BT",
47
+ "068": "BO",
48
+ "070": "BA",
49
+ "072": "BW",
50
+ "076": "BR",
51
+ "084": "BZ",
52
+ "090": "SB",
53
+ "096": "BN",
54
+ "100": "BG",
55
+ "104": "MM",
56
+ "108": "BI",
57
+ "112": "BY",
58
+ "116": "KH",
59
+ "120": "CM",
60
+ "124": "CA",
61
+ "140": "CF",
62
+ "144": "LK",
63
+ "148": "TD",
64
+ "152": "CL",
65
+ "156": "CN",
66
+ "158": "TW",
67
+ "170": "CO",
68
+ "174": "KM",
69
+ "178": "CG",
70
+ "180": "CD",
71
+ "188": "CR",
72
+ "191": "HR",
73
+ "192": "CU",
74
+ "196": "CY",
75
+ "203": "CZ",
76
+ "204": "BJ",
77
+ "208": "DK",
78
+ "214": "DO",
79
+ "218": "EC",
80
+ "222": "SV",
81
+ "226": "GQ",
82
+ "231": "ET",
83
+ "232": "ER",
84
+ "233": "EE",
85
+ "242": "FJ",
86
+ "246": "FI",
87
+ "250": "FR",
88
+ "262": "DJ",
89
+ "266": "GA",
90
+ "268": "GE",
91
+ "270": "GM",
92
+ "275": "PS",
93
+ "276": "DE",
94
+ "288": "GH",
95
+ "300": "GR",
96
+ "308": "GD",
97
+ "320": "GT",
98
+ "324": "GN",
99
+ "328": "GY",
100
+ "332": "HT",
101
+ "340": "HN",
102
+ "348": "HU",
103
+ "352": "IS",
104
+ "356": "IN",
105
+ "360": "ID",
106
+ "364": "IR",
107
+ "368": "IQ",
108
+ "372": "IE",
109
+ "376": "IL",
110
+ "380": "IT",
111
+ "384": "CI",
112
+ "388": "JM",
113
+ "392": "JP",
114
+ "398": "KZ",
115
+ "400": "JO",
116
+ "404": "KE",
117
+ "408": "KP",
118
+ "410": "KR",
119
+ "414": "KW",
120
+ "417": "KG",
121
+ "418": "LA",
122
+ "422": "LB",
123
+ "426": "LS",
124
+ "428": "LV",
125
+ "430": "LR",
126
+ "434": "LY",
127
+ "440": "LT",
128
+ "442": "LU",
129
+ "450": "MG",
130
+ "454": "MW",
131
+ "458": "MY",
132
+ "462": "MV",
133
+ "466": "ML",
134
+ "470": "MT",
135
+ "478": "MR",
136
+ "480": "MU",
137
+ "484": "MX",
138
+ "496": "MN",
139
+ "498": "MD",
140
+ "499": "ME",
141
+ "504": "MA",
142
+ "508": "MZ",
143
+ "512": "OM",
144
+ "516": "NA",
145
+ "524": "NP",
146
+ "528": "NL",
147
+ "540": "NC",
148
+ "548": "VU",
149
+ "554": "NZ",
150
+ "558": "NI",
151
+ "562": "NE",
152
+ "566": "NG",
153
+ "578": "NO",
154
+ "586": "PK",
155
+ "591": "PA",
156
+ "598": "PG",
157
+ "600": "PY",
158
+ "604": "PE",
159
+ "608": "PH",
160
+ "616": "PL",
161
+ "620": "PT",
162
+ "624": "GW",
163
+ "626": "TL",
164
+ "630": "PR",
165
+ "634": "QA",
166
+ "642": "RO",
167
+ "643": "RU",
168
+ "646": "RW",
169
+ "662": "LC",
170
+ "670": "VC",
171
+ "678": "ST",
172
+ "682": "SA",
173
+ "686": "SN",
174
+ "688": "RS",
175
+ "694": "SL",
176
+ "702": "SG",
177
+ "703": "SK",
178
+ "704": "VN",
179
+ "705": "SI",
180
+ "706": "SO",
181
+ "710": "ZA",
182
+ "716": "ZW",
183
+ "724": "ES",
184
+ "728": "SS",
185
+ "729": "SD",
186
+ "732": "EH",
187
+ "740": "SR",
188
+ "748": "SZ",
189
+ "752": "SE",
190
+ "756": "CH",
191
+ "760": "SY",
192
+ "762": "TJ",
193
+ "764": "TH",
194
+ "768": "TG",
195
+ "780": "TT",
196
+ "784": "AE",
197
+ "788": "TN",
198
+ "792": "TR",
199
+ "795": "TM",
200
+ "800": "UG",
201
+ "804": "UA",
202
+ "807": "MK",
203
+ "818": "EG",
204
+ "826": "GB",
205
+ "834": "TZ",
206
+ "840": "US",
207
+ "854": "BF",
208
+ "858": "UY",
209
+ "860": "UZ",
210
+ "862": "VE",
211
+ "887": "YE",
212
+ "894": "ZM"
213
+ };
214
+ }
215
+ });
216
+
217
+ // src/widgets/WorldMapInner.tsx
218
+ var WorldMapInner_exports = {};
219
+ __export(WorldMapInner_exports, {
220
+ default: () => WorldMapInner
221
+ });
222
+ function WorldMapInner({ geoUrl: geoUrl2, countryData, getColor, setTooltip, strokeColor = "rgb(228, 228, 231)", hoverColor = "rgb(129, 140, 248)" }) {
223
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "relative", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
224
+ import_react_simple_maps.ComposableMap,
225
+ {
226
+ projectionConfig: { scale: 147, center: [0, 20] },
227
+ style: { width: "100%", height: "auto" },
228
+ height: 380,
229
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_simple_maps.ZoomableGroup, { children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_simple_maps.Geographies, { geography: geoUrl2, children: ({ geographies }) => geographies.map((geo) => {
230
+ const rawIso = geo.properties.ISO_A2;
231
+ const iso = rawIso && rawIso !== "-99" ? rawIso : numericToISO2[geo.id] || geo.id;
232
+ const value = countryData[iso] || 0;
233
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
234
+ import_react_simple_maps.Geography,
235
+ {
236
+ geography: geo,
237
+ fill: getColor(iso),
238
+ stroke: strokeColor,
239
+ strokeWidth: 0.5,
240
+ style: {
241
+ default: { outline: "none" },
242
+ hover: { outline: "none", fill: hoverColor, cursor: "pointer" },
243
+ pressed: { outline: "none" }
244
+ },
245
+ onMouseEnter: (e) => {
246
+ const name = geo.properties.name || iso;
247
+ setTooltip({ name, value, x: e.clientX, y: e.clientY });
248
+ },
249
+ onMouseLeave: () => setTooltip(null)
250
+ },
251
+ geo.rsmKey
252
+ );
253
+ }) }) })
254
+ }
255
+ ) });
256
+ }
257
+ var import_react_simple_maps, import_jsx_runtime19;
258
+ var init_WorldMapInner = __esm({
259
+ "src/widgets/WorldMapInner.tsx"() {
260
+ "use strict";
261
+ import_react_simple_maps = require("react-simple-maps");
262
+ init_worldmap_data();
263
+ import_jsx_runtime19 = require("react/jsx-runtime");
264
+ }
265
+ });
266
+
267
+ // src/index.ts
268
+ var index_exports = {};
269
+ __export(index_exports, {
270
+ AnalyticsDashboard: () => AnalyticsDashboard,
271
+ BrowsersChart: () => BrowsersChart,
272
+ DateRangePicker: () => DateRangePicker,
273
+ DevicesChart: () => DevicesChart,
274
+ ExportButton: () => ExportButton,
275
+ LitemetricsProvider: () => LitemetricsProvider,
276
+ PeriodSelector: () => PeriodSelector,
277
+ PieChartCard: () => PieChartCard,
278
+ StatCard: () => StatCard,
279
+ StatCards: () => StatCards,
280
+ TimeSeriesChart: () => TimeSeriesChart,
281
+ TopBrowsers: () => TopBrowsers,
282
+ TopCountries: () => TopCountries,
283
+ TopDevices: () => TopDevices,
284
+ TopEvents: () => TopEvents,
285
+ TopList: () => TopList,
286
+ TopPages: () => TopPages,
287
+ TopReferrers: () => TopReferrers,
288
+ WorldMap: () => WorldMap,
289
+ countryToFlag: () => countryToFlag,
290
+ cssVar: () => cssVar,
291
+ darkTheme: () => darkTheme,
292
+ defaultTheme: () => defaultTheme,
293
+ formatDate: () => formatDate,
294
+ formatTooltipDate: () => formatTooltipDate,
295
+ getBrowserIcon: () => getBrowserIcon,
296
+ getDeviceIcon: () => getDeviceIcon,
297
+ getOSIcon: () => getOSIcon,
298
+ getPieColors: () => getPieColors,
299
+ getReferrerIcon: () => getReferrerIcon,
300
+ queryKeys: () => queryKeys,
301
+ useLitemetricsUI: () => useLitemetricsUI,
302
+ useOverview: () => useOverview,
303
+ useStats: () => useStats,
304
+ useThemeColors: () => useThemeColors,
305
+ useTimeSeries: () => useTimeSeries
306
+ });
307
+ module.exports = __toCommonJS(index_exports);
308
+
309
+ // src/theme/index.ts
310
+ var KEY_TO_VAR = {
311
+ bg: "--lm-bg",
312
+ bgSecondary: "--lm-bg-secondary",
313
+ bgTertiary: "--lm-bg-tertiary",
314
+ border: "--lm-border",
315
+ borderHover: "--lm-border-hover",
316
+ text: "--lm-text",
317
+ textSecondary: "--lm-text-secondary",
318
+ textTertiary: "--lm-text-tertiary",
319
+ textMuted: "--lm-text-muted",
320
+ accent: "--lm-accent",
321
+ accentLight: "--lm-accent-light",
322
+ accentText: "--lm-accent-text",
323
+ accentHover: "--lm-accent-hover",
324
+ positive: "--lm-positive",
325
+ negative: "--lm-negative",
326
+ chartStroke: "--lm-chart-stroke",
327
+ chartFill: "--lm-chart-fill",
328
+ chartGrid: "--lm-chart-grid",
329
+ chartAxis: "--lm-chart-axis",
330
+ tooltipBg: "--lm-tooltip-bg",
331
+ tooltipText: "--lm-tooltip-text",
332
+ tooltipMuted: "--lm-tooltip-muted",
333
+ bar: "--lm-bar",
334
+ barHover: "--lm-bar-hover",
335
+ pie1: "--lm-pie-1",
336
+ pie2: "--lm-pie-2",
337
+ pie3: "--lm-pie-3",
338
+ pie4: "--lm-pie-4",
339
+ pie5: "--lm-pie-5",
340
+ pie6: "--lm-pie-6",
341
+ pie7: "--lm-pie-7",
342
+ pie8: "--lm-pie-8",
343
+ mapEmpty: "--lm-map-empty",
344
+ mapStroke: "--lm-map-stroke",
345
+ mapHover: "--lm-map-hover"
346
+ };
347
+ var defaultTheme = {
348
+ bg: "255 255 255",
349
+ bgSecondary: "250 250 250",
350
+ bgTertiary: "244 244 245",
351
+ border: "228 228 231",
352
+ borderHover: "212 212 216",
353
+ text: "24 24 27",
354
+ textSecondary: "113 113 122",
355
+ textTertiary: "161 161 170",
356
+ textMuted: "212 212 216",
357
+ accent: "99 102 241",
358
+ accentLight: "238 242 255",
359
+ accentText: "67 56 202",
360
+ accentHover: "129 140 248",
361
+ positive: "5 150 105",
362
+ negative: "239 68 68",
363
+ chartStroke: "99 102 241",
364
+ chartFill: "99 102 241",
365
+ chartGrid: "244 244 245",
366
+ chartAxis: "161 161 170",
367
+ tooltipBg: "24 24 27",
368
+ tooltipText: "255 255 255",
369
+ tooltipMuted: "161 161 170",
370
+ bar: "238 242 255",
371
+ barHover: "224 231 255",
372
+ pie1: "99 102 241",
373
+ pie2: "139 92 246",
374
+ pie3: "59 130 246",
375
+ pie4: "20 184 166",
376
+ pie5: "16 185 129",
377
+ pie6: "245 158 11",
378
+ pie7: "239 68 68",
379
+ pie8: "236 72 153",
380
+ mapEmpty: "244 244 245",
381
+ mapStroke: "228 228 231",
382
+ mapHover: "129 140 248"
383
+ };
384
+ var darkTheme = {
385
+ bg: "24 24 27",
386
+ bgSecondary: "39 39 42",
387
+ bgTertiary: "63 63 70",
388
+ border: "63 63 70",
389
+ borderHover: "82 82 91",
390
+ text: "244 244 245",
391
+ textSecondary: "161 161 170",
392
+ textTertiary: "113 113 122",
393
+ textMuted: "82 82 91",
394
+ accent: "129 140 248",
395
+ accentLight: "30 27 75",
396
+ accentText: "165 180 252",
397
+ accentHover: "165 180 252",
398
+ positive: "52 211 153",
399
+ negative: "248 113 113",
400
+ chartStroke: "129 140 248",
401
+ chartFill: "129 140 248",
402
+ chartGrid: "63 63 70",
403
+ chartAxis: "113 113 122",
404
+ tooltipBg: "244 244 245",
405
+ tooltipText: "24 24 27",
406
+ tooltipMuted: "113 113 122",
407
+ bar: "30 27 75",
408
+ barHover: "49 46 129",
409
+ pie1: "129 140 248",
410
+ pie2: "167 139 250",
411
+ pie3: "96 165 250",
412
+ pie4: "45 212 191",
413
+ pie5: "52 211 153",
414
+ pie6: "251 191 36",
415
+ pie7: "248 113 113",
416
+ pie8: "244 114 182",
417
+ mapEmpty: "39 39 42",
418
+ mapStroke: "63 63 70",
419
+ mapHover: "165 180 252"
420
+ };
421
+ function themeToCSS(theme) {
422
+ return Object.entries(theme).filter(([, v]) => v !== void 0).map(([key, value]) => `${KEY_TO_VAR[key]}: ${value};`).join("\n ");
423
+ }
424
+ function buildStyleSheet(light, dark) {
425
+ const mergedLight = { ...defaultTheme, ...light };
426
+ const mergedDark = { ...darkTheme, ...dark };
427
+ return `:root, .litemetrics-light {
428
+ ${themeToCSS(mergedLight)}
429
+ }
430
+
431
+ .dark, .litemetrics-dark {
432
+ ${themeToCSS(mergedDark)}
433
+ }`;
434
+ }
435
+ var STYLE_ID = "litemetrics-ui-theme";
436
+
437
+ // src/context/LitemetricsUIContext.tsx
438
+ var import_react = require("react");
439
+ var LitemetricsUIContext = (0, import_react.createContext)(null);
440
+
441
+ // src/context/LitemetricsProvider.tsx
442
+ var import_react2 = require("react");
443
+ var import_react_query = require("@tanstack/react-query");
444
+ var import_client = require("@litemetrics/client");
445
+ var import_jsx_runtime = require("react/jsx-runtime");
446
+ function LitemetricsProvider({
447
+ baseUrl,
448
+ siteId,
449
+ secretKey,
450
+ defaultPeriod = "7d",
451
+ queryClient: externalQueryClient,
452
+ staleTime = 3e4,
453
+ theme,
454
+ darkTheme: darkThemeProp,
455
+ children
456
+ }) {
457
+ const [period, setPeriod] = (0, import_react2.useState)(defaultPeriod);
458
+ const [dateFrom, setDateFrom] = (0, import_react2.useState)("");
459
+ const [dateTo, setDateTo] = (0, import_react2.useState)("");
460
+ const client = (0, import_react2.useMemo)(
461
+ () => (0, import_client.createClient)({ baseUrl, siteId, secretKey }),
462
+ [baseUrl, siteId, secretKey]
463
+ );
464
+ const internalQueryClient = (0, import_react2.useMemo)(
465
+ () => new import_react_query.QueryClient({ defaultOptions: { queries: { staleTime } } }),
466
+ [staleTime]
467
+ );
468
+ const queryClient = externalQueryClient ?? internalQueryClient;
469
+ (0, import_react2.useEffect)(() => {
470
+ const css = buildStyleSheet(theme, darkThemeProp);
471
+ let el = document.getElementById(STYLE_ID);
472
+ if (!el) {
473
+ el = document.createElement("style");
474
+ el.id = STYLE_ID;
475
+ document.head.appendChild(el);
476
+ }
477
+ el.textContent = css;
478
+ return () => {
479
+ el?.remove();
480
+ };
481
+ }, [theme, darkThemeProp]);
482
+ const value = (0, import_react2.useMemo)(
483
+ () => ({ client, siteId, period, setPeriod, dateFrom, setDateFrom, dateTo, setDateTo, staleTime }),
484
+ [client, siteId, period, dateFrom, dateTo, staleTime]
485
+ );
486
+ const content = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LitemetricsUIContext.Provider, { value, children });
487
+ if (externalQueryClient) {
488
+ return content;
489
+ }
490
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_query.QueryClientProvider, { client: queryClient, children: content });
491
+ }
492
+
493
+ // src/hooks/queryKeys.ts
494
+ var PREFIX = "litemetrics-ui";
495
+ var queryKeys = {
496
+ stats: (siteId, metric, period, dateFrom, dateTo) => [PREFIX, "stats", siteId, metric, period, dateFrom, dateTo],
497
+ timeSeries: (siteId, metric, period) => [PREFIX, "timeSeries", siteId, metric, period],
498
+ overview: (siteId, period, dateFrom, dateTo) => [PREFIX, "overview", siteId, period, dateFrom, dateTo],
499
+ worldMap: (siteId, period) => [PREFIX, "worldMap", siteId, period]
500
+ };
501
+
502
+ // src/hooks/useLitemetricsUI.ts
503
+ var import_react3 = require("react");
504
+ function useLitemetricsUI() {
505
+ const ctx = (0, import_react3.useContext)(LitemetricsUIContext);
506
+ if (!ctx) {
507
+ throw new Error("useLitemetricsUI must be used within a <LitemetricsProvider>");
508
+ }
509
+ return ctx;
510
+ }
511
+
512
+ // src/hooks/useStats.ts
513
+ var import_react_query2 = require("@tanstack/react-query");
514
+ function useStats(metric, options) {
515
+ const { client, siteId, period: ctxPeriod, dateFrom, dateTo, staleTime } = useLitemetricsUI();
516
+ const period = options?.period ?? ctxPeriod;
517
+ const statsOptions = period === "custom" && dateFrom && dateTo ? { period, dateFrom: new Date(dateFrom).toISOString(), dateTo: (/* @__PURE__ */ new Date(dateTo + "T23:59:59")).toISOString(), limit: options?.limit, compare: true } : { period, limit: options?.limit, compare: true };
518
+ return (0, import_react_query2.useQuery)({
519
+ queryKey: queryKeys.stats(siteId, metric, period, dateFrom, dateTo),
520
+ queryFn: async () => {
521
+ client.setSiteId(siteId);
522
+ return client.getStats(metric, statsOptions);
523
+ },
524
+ staleTime,
525
+ enabled: options?.enabled !== false && (period !== "custom" || !!dateFrom && !!dateTo)
526
+ });
527
+ }
528
+
529
+ // src/hooks/useTimeSeries.ts
530
+ var import_react_query3 = require("@tanstack/react-query");
531
+ function useTimeSeries(metric, options) {
532
+ const { client, siteId, period: ctxPeriod, staleTime } = useLitemetricsUI();
533
+ const period = options?.period ?? ctxPeriod;
534
+ return (0, import_react_query3.useQuery)({
535
+ queryKey: queryKeys.timeSeries(siteId, metric, period),
536
+ queryFn: async () => {
537
+ client.setSiteId(siteId);
538
+ return client.getTimeSeries(metric, { period });
539
+ },
540
+ staleTime,
541
+ enabled: options?.enabled !== false
542
+ });
543
+ }
544
+
545
+ // src/hooks/useOverview.ts
546
+ var import_react_query4 = require("@tanstack/react-query");
547
+ function useOverview(options) {
548
+ const { client, siteId, period: ctxPeriod, dateFrom, dateTo, staleTime } = useLitemetricsUI();
549
+ const period = options?.period ?? ctxPeriod;
550
+ const metrics2 = options?.metrics ?? ["pageviews", "visitors", "sessions", "events"];
551
+ const statsOptions = period === "custom" && dateFrom && dateTo ? { period, dateFrom: new Date(dateFrom).toISOString(), dateTo: (/* @__PURE__ */ new Date(dateTo + "T23:59:59")).toISOString(), compare: true } : { period, compare: true };
552
+ return (0, import_react_query4.useQuery)({
553
+ queryKey: queryKeys.overview(siteId, period, dateFrom, dateTo),
554
+ queryFn: async () => {
555
+ client.setSiteId(siteId);
556
+ return client.getOverview(metrics2, statsOptions);
557
+ },
558
+ staleTime,
559
+ enabled: options?.enabled !== false && (period !== "custom" || !!dateFrom && !!dateTo)
560
+ });
561
+ }
562
+
563
+ // src/hooks/useThemeColors.ts
564
+ var import_react4 = require("react");
565
+
566
+ // src/utils/theme.ts
567
+ function cssVar(name, fallback) {
568
+ if (typeof document === "undefined") return fallback ? `rgb(${fallback})` : "";
569
+ const raw = getComputedStyle(document.documentElement).getPropertyValue(name).trim();
570
+ if (!raw) return fallback ? `rgb(${fallback})` : "";
571
+ if (/^\d+\s+\d+\s+\d+$/.test(raw)) return `rgb(${raw.replace(/ /g, ", ")})`;
572
+ return raw;
573
+ }
574
+ var DEFAULT_PIE = [
575
+ "99 102 241",
576
+ "139 92 246",
577
+ "59 130 246",
578
+ "20 184 166",
579
+ "16 185 129",
580
+ "245 158 11",
581
+ "239 68 68",
582
+ "236 72 153"
583
+ ];
584
+ function getPieColors() {
585
+ return Array.from(
586
+ { length: 8 },
587
+ (_, i) => cssVar(`--lm-pie-${i + 1}`, DEFAULT_PIE[i])
588
+ );
589
+ }
590
+
591
+ // src/hooks/useThemeColors.ts
592
+ function useThemeColors() {
593
+ const [, setTick] = (0, import_react4.useState)(0);
594
+ (0, import_react4.useEffect)(() => {
595
+ if (typeof document === "undefined") return;
596
+ const target = document.documentElement;
597
+ const observer = new MutationObserver((mutations) => {
598
+ for (const m of mutations) {
599
+ if (m.type === "attributes" && (m.attributeName === "class" || m.attributeName === "style")) {
600
+ setTick((t) => t + 1);
601
+ break;
602
+ }
603
+ }
604
+ });
605
+ observer.observe(target, { attributes: true, attributeFilter: ["class", "style"] });
606
+ return () => observer.disconnect();
607
+ }, []);
608
+ const get = (0, import_react4.useCallback)((varName, fallback) => cssVar(varName, fallback), []);
609
+ return { get };
610
+ }
611
+
612
+ // src/components/StatCard.tsx
613
+ var import_jsx_runtime2 = require("react/jsx-runtime");
614
+ function StatCard({ title, value, loading, changePercent, className }) {
615
+ const hasComparison = changePercent !== void 0 && changePercent !== null;
616
+ const isPositive = hasComparison && changePercent > 0;
617
+ const isNegative = hasComparison && changePercent < 0;
618
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `rounded-xl bg-[rgb(var(--lm-bg))] border border-[rgb(var(--lm-border))] p-6 hover:border-[rgb(var(--lm-border-hover))] hover:shadow-md hover:-translate-y-0.5 transition-all duration-200 ${className ?? ""}`, children: [
619
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs font-medium text-[rgb(var(--lm-text-tertiary))] uppercase tracking-wide mb-1", children: title }),
620
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "h-8 w-20 bg-[rgb(var(--lm-bg-tertiary))] rounded animate-pulse" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-end gap-2", children: [
621
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-3xl font-semibold tabular-nums text-[rgb(var(--lm-text))]", children: typeof value === "number" ? value.toLocaleString() : value }),
622
+ hasComparison && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
623
+ "span",
624
+ {
625
+ className: `text-xs font-medium pb-0.5 ${isPositive ? "text-[rgb(var(--lm-positive))]" : isNegative ? "text-[rgb(var(--lm-negative))]" : "text-[rgb(var(--lm-text-tertiary))]"}`,
626
+ children: [
627
+ isPositive && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { className: "w-3 h-3 inline mr-0.5 -mt-0.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 15l7-7 7 7" }) }),
628
+ isNegative && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { className: "w-3 h-3 inline mr-0.5 -mt-0.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) }),
629
+ changePercent === 0 ? "0%" : `${changePercent > 0 ? "+" : ""}${changePercent.toFixed(1)}%`
630
+ ]
631
+ }
632
+ )
633
+ ] })
634
+ ] });
635
+ }
636
+
637
+ // src/components/TopList.tsx
638
+ var import_react5 = require("react");
639
+
640
+ // src/utils/icons.tsx
641
+ var import_si = require("react-icons/si");
642
+ var import_si2 = require("react-icons/si");
643
+ var import_fa6 = require("react-icons/fa6");
644
+ var import_hi = require("react-icons/hi");
645
+ var import_jsx_runtime3 = require("react/jsx-runtime");
646
+ var iconClass = "w-4 h-4 flex-shrink-0";
647
+ function getBrowserIcon(browser) {
648
+ const b = browser.toLowerCase();
649
+ if (b.includes("chrome") && !b.includes("edge")) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_si.SiGooglechrome, { className: `${iconClass} text-[#4285F4]` });
650
+ if (b.includes("firefox")) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_si.SiFirefox, { className: `${iconClass} text-[#FF7139]` });
651
+ if (b.includes("safari") && !b.includes("chrome")) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_si.SiSafari, { className: `${iconClass} text-[#006CFF]` });
652
+ if (b.includes("edge")) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_fa6.FaEdge, { className: `${iconClass} text-[#0078D7]` });
653
+ if (b.includes("opera")) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_si.SiOpera, { className: `${iconClass} text-[#FF1B2D]` });
654
+ if (b.includes("ie") || b.includes("internet explorer")) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_fa6.FaInternetExplorer, { className: `${iconClass} text-blue-400` });
655
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_fa6.FaGlobe, { className: `${iconClass} text-zinc-400` });
656
+ }
657
+ function getOSIcon(os) {
658
+ const o = os.toLowerCase();
659
+ if (o.includes("windows")) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_fa6.FaWindows, { className: `${iconClass} text-[#0078D4]` });
660
+ if (o.includes("mac") || o.includes("ios")) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_si.SiApple, { className: `${iconClass} text-zinc-700` });
661
+ if (o.includes("linux")) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_si.SiLinux, { className: `${iconClass} text-[#FCC624]` });
662
+ if (o.includes("android")) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_si.SiAndroid, { className: `${iconClass} text-[#34A853]` });
663
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_hi.HiOutlineDesktopComputer, { className: `${iconClass} text-zinc-400` });
664
+ }
665
+ function getDeviceIcon(type) {
666
+ const t = type.toLowerCase();
667
+ if (t.includes("mobile")) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_hi.HiOutlineDeviceMobile, { className: `${iconClass} text-zinc-500` });
668
+ if (t.includes("tablet")) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_hi.HiOutlineDeviceTablet, { className: `${iconClass} text-zinc-500` });
669
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_hi.HiOutlineDesktopComputer, { className: `${iconClass} text-zinc-500` });
670
+ }
671
+ function getReferrerIcon(referrer) {
672
+ const r = referrer.toLowerCase();
673
+ if (r.includes("google")) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_si2.SiGoogle, { className: `${iconClass} text-[#4285F4]` });
674
+ if (r.includes("instagram")) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_si2.SiInstagram, { className: `${iconClass} text-[#E4405F]` });
675
+ if (r.includes("twitter") || r.includes("t.co") || r.includes("x.com")) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_si2.SiX, { className: `${iconClass} text-zinc-800` });
676
+ if (r.includes("facebook") || r.includes("fb.com")) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_si2.SiFacebook, { className: `${iconClass} text-[#1877F2]` });
677
+ if (r.includes("linkedin")) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_si2.SiLinkedin, { className: `${iconClass} text-[#0A66C2]` });
678
+ if (r.includes("youtube")) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_si2.SiYoutube, { className: `${iconClass} text-[#FF0000]` });
679
+ if (r.includes("github")) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_si2.SiGithub, { className: `${iconClass} text-zinc-800` });
680
+ if (r.includes("reddit")) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_si2.SiReddit, { className: `${iconClass} text-[#FF4500]` });
681
+ if (r.includes("pinterest")) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_si2.SiPinterest, { className: `${iconClass} text-[#BD081C]` });
682
+ if (r.includes("tiktok")) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_si2.SiTiktok, { className: `${iconClass} text-zinc-800` });
683
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_fa6.FaGlobe, { className: `${iconClass} text-zinc-400` });
684
+ }
685
+ function countryToFlag(code) {
686
+ if (!code || code.length !== 2) return "";
687
+ const upper = code.toUpperCase();
688
+ return String.fromCodePoint(
689
+ ...Array.from(upper).map((c) => 127462 + c.charCodeAt(0) - 65)
690
+ );
691
+ }
692
+
693
+ // src/components/TopList.tsx
694
+ var import_jsx_runtime4 = require("react/jsx-runtime");
695
+ function getIcon(type, key) {
696
+ if (!type) return null;
697
+ switch (type) {
698
+ case "pages":
699
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { className: "w-4 h-4 flex-shrink-0 text-[rgb(var(--lm-text-tertiary))]", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" }) });
700
+ case "referrers":
701
+ return getReferrerIcon(key);
702
+ case "countries": {
703
+ const flag = countryToFlag(key);
704
+ return flag ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-sm flex-shrink-0 leading-none", children: flag }) : null;
705
+ }
706
+ case "events":
707
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { className: "w-4 h-4 flex-shrink-0 text-amber-500", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M13 10V3L4 14h7v7l9-11h-7z" }) });
708
+ case "browsers":
709
+ return getBrowserIcon(key);
710
+ case "devices":
711
+ return getDeviceIcon(key);
712
+ }
713
+ }
714
+ function TopList({ title, data, loading, type, className }) {
715
+ const [tooltip, setTooltip] = (0, import_react5.useState)(null);
716
+ const maxValue = data ? Math.max(...data.map((d) => d.value), 1) : 1;
717
+ const totalValue = data ? data.reduce((sum, d) => sum + d.value, 0) : 0;
718
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `rounded-xl bg-[rgb(var(--lm-bg))] border border-[rgb(var(--lm-border))] p-5 hover:shadow-sm transition-all duration-200 ${className ?? ""}`, children: [
719
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { className: "text-xs font-medium text-[rgb(var(--lm-text-tertiary))] uppercase tracking-wide mb-4", children: title }),
720
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "space-y-3", children: [...Array(5)].map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "h-7 bg-[rgb(var(--lm-bg-secondary))] rounded animate-pulse" }, i)) }) : !data || data.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "py-8 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-[rgb(var(--lm-text-muted))] text-sm", children: "No data yet" }) }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "space-y-1", children: data.map((item) => {
721
+ const pct = totalValue > 0 ? Math.round(item.value / totalValue * 100) : 0;
722
+ const icon = getIcon(type, item.key);
723
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
724
+ "div",
725
+ {
726
+ className: "relative group",
727
+ onMouseEnter: (e) => {
728
+ const rect = e.currentTarget.getBoundingClientRect();
729
+ setTooltip({ key: item.key, value: item.value, pct, x: rect.left + rect.width / 2, y: rect.top });
730
+ },
731
+ onMouseLeave: () => setTooltip(null),
732
+ children: [
733
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
734
+ "div",
735
+ {
736
+ className: "absolute inset-0 bg-[rgb(var(--lm-bar))] rounded transition-all group-hover:bg-[rgb(var(--lm-bar-hover))]",
737
+ style: { width: `${item.value / maxValue * 100}%` }
738
+ }
739
+ ),
740
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "relative flex items-center justify-between px-2.5 py-1.5 text-sm", children: [
741
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2 truncate mr-3", children: [
742
+ icon,
743
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "truncate text-[rgb(var(--lm-text-secondary))]", children: item.key || "(direct)" })
744
+ ] }),
745
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2 flex-shrink-0", children: [
746
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-[rgb(var(--lm-text-secondary))] tabular-nums text-xs font-medium", children: item.value.toLocaleString() }),
747
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "text-[rgb(var(--lm-text-tertiary))] tabular-nums text-xs w-8 text-right", children: [
748
+ pct,
749
+ "%"
750
+ ] })
751
+ ] })
752
+ ] })
753
+ ]
754
+ },
755
+ item.key
756
+ );
757
+ }) }),
758
+ tooltip && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
759
+ "div",
760
+ {
761
+ className: "fixed z-50 bg-[rgb(var(--lm-tooltip-bg))] text-[rgb(var(--lm-tooltip-text))] text-xs rounded-lg px-3 py-2 shadow-lg pointer-events-none max-w-xs",
762
+ style: { left: tooltip.x, top: tooltip.y - 8, transform: "translate(-50%, -100%)" },
763
+ children: [
764
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "font-medium break-all", children: tooltip.key || "(direct)" }),
765
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "text-[rgb(var(--lm-tooltip-muted))] mt-0.5", children: [
766
+ tooltip.value.toLocaleString(),
767
+ " \xB7 ",
768
+ tooltip.pct,
769
+ "%"
770
+ ] })
771
+ ]
772
+ }
773
+ )
774
+ ] });
775
+ }
776
+
777
+ // src/components/PieChartCard.tsx
778
+ var import_recharts = require("recharts");
779
+ var import_jsx_runtime5 = require("react/jsx-runtime");
780
+ function PieChartCard({ title, data, loading, className }) {
781
+ const total = data.reduce((sum, d) => sum + d.value, 0);
782
+ const colors = getPieColors();
783
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `rounded-xl bg-[rgb(var(--lm-bg))] border border-[rgb(var(--lm-border))] p-5 ${className ?? ""}`, children: [
784
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h3", { className: "text-xs font-medium text-[rgb(var(--lm-text-tertiary))] uppercase tracking-wide mb-4", children: title }),
785
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "h-48 bg-[rgb(var(--lm-bg-secondary))] rounded-lg animate-pulse" }) : data.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "h-48 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-[rgb(var(--lm-text-muted))] text-sm", children: "No data yet" }) }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-4", children: [
786
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "w-36 h-36 flex-shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_recharts.PieChart, { children: [
787
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
788
+ import_recharts.Pie,
789
+ {
790
+ data,
791
+ cx: "50%",
792
+ cy: "50%",
793
+ innerRadius: 35,
794
+ outerRadius: 55,
795
+ dataKey: "value",
796
+ strokeWidth: 2,
797
+ stroke: cssVar("--lm-bg", "255 255 255"),
798
+ children: data.map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_recharts.Cell, { fill: colors[index % colors.length] }, index))
799
+ }
800
+ ),
801
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
802
+ import_recharts.Tooltip,
803
+ {
804
+ content: ({ active, payload }) => {
805
+ if (!active || !payload?.[0]) return null;
806
+ const item = payload[0];
807
+ const pct = total > 0 ? Math.round(item.value / total * 100) : 0;
808
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "bg-[rgb(var(--lm-tooltip-bg))] text-[rgb(var(--lm-tooltip-text))] text-xs rounded-lg px-3 py-2 shadow-lg", children: [
809
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "font-medium", children: item.name }),
810
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "text-[rgb(var(--lm-tooltip-muted))]", children: [
811
+ item.value.toLocaleString(),
812
+ " (",
813
+ pct,
814
+ "%)"
815
+ ] })
816
+ ] });
817
+ }
818
+ }
819
+ )
820
+ ] }) }) }),
821
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex-1 space-y-1.5 min-w-0", children: data.slice(0, 6).map((item, i) => {
822
+ const pct = total > 0 ? Math.round(item.value / total * 100) : 0;
823
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2 text-sm", children: [
824
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
825
+ "span",
826
+ {
827
+ className: "w-2.5 h-2.5 rounded-full flex-shrink-0",
828
+ style: { backgroundColor: colors[i % colors.length] }
829
+ }
830
+ ),
831
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-[rgb(var(--lm-text-secondary))] truncate", children: item.name || "(unknown)" }),
832
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "text-[rgb(var(--lm-text-tertiary))] ml-auto flex-shrink-0 tabular-nums text-xs", children: [
833
+ pct,
834
+ "%"
835
+ ] })
836
+ ] }, item.name);
837
+ }) })
838
+ ] })
839
+ ] });
840
+ }
841
+
842
+ // src/components/PeriodSelector.tsx
843
+ var import_react6 = require("react");
844
+
845
+ // src/components/DateRangePicker.tsx
846
+ var import_jsx_runtime6 = require("react/jsx-runtime");
847
+ function DateRangePicker({ startDate, endDate, onStartChange, onEndChange, className }) {
848
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: `flex items-center gap-2 ${className ?? ""}`, children: [
849
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
850
+ "input",
851
+ {
852
+ type: "date",
853
+ value: startDate,
854
+ onChange: (e) => onStartChange(e.target.value),
855
+ max: endDate || void 0,
856
+ className: "bg-[rgb(var(--lm-bg))] border border-[rgb(var(--lm-border))] rounded-lg px-2.5 py-1.5 text-sm text-[rgb(var(--lm-text))] focus:outline-none focus:border-[rgb(var(--lm-accent))]"
857
+ }
858
+ ),
859
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-[rgb(var(--lm-text-tertiary))] text-sm", children: "to" }),
860
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
861
+ "input",
862
+ {
863
+ type: "date",
864
+ value: endDate,
865
+ onChange: (e) => onEndChange(e.target.value),
866
+ min: startDate || void 0,
867
+ max: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
868
+ className: "bg-[rgb(var(--lm-bg))] border border-[rgb(var(--lm-border))] rounded-lg px-2.5 py-1.5 text-sm text-[rgb(var(--lm-text))] focus:outline-none focus:border-[rgb(var(--lm-accent))]"
869
+ }
870
+ )
871
+ ] });
872
+ }
873
+
874
+ // src/components/PeriodSelector.tsx
875
+ var import_jsx_runtime7 = require("react/jsx-runtime");
876
+ var periods = [
877
+ { value: "1h", label: "1H" },
878
+ { value: "24h", label: "24H" },
879
+ { value: "7d", label: "7D" },
880
+ { value: "30d", label: "30D" },
881
+ { value: "90d", label: "90D" },
882
+ { value: "custom", label: "Custom" }
883
+ ];
884
+ function PeriodSelector({ value, onChange, dateFrom, dateTo, onDateFromChange, onDateToChange, className }) {
885
+ const [showCustom, setShowCustom] = (0, import_react6.useState)(value === "custom");
886
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `flex items-center gap-3 ${className ?? ""}`, children: [
887
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex gap-1 bg-[rgb(var(--lm-bg-tertiary))] rounded-lg p-1 border border-[rgb(var(--lm-border))]", children: periods.map((p) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
888
+ "button",
889
+ {
890
+ onClick: () => {
891
+ onChange(p.value);
892
+ setShowCustom(p.value === "custom");
893
+ },
894
+ className: `px-3 py-1.5 text-sm rounded-md transition-colors ${value === p.value ? "bg-[rgb(var(--lm-bg))] text-[rgb(var(--lm-text))] shadow-sm" : "text-[rgb(var(--lm-text-secondary))] hover:text-[rgb(var(--lm-text))]"}`,
895
+ children: p.label
896
+ },
897
+ p.value
898
+ )) }),
899
+ showCustom && onDateFromChange && onDateToChange && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
900
+ DateRangePicker,
901
+ {
902
+ startDate: dateFrom || "",
903
+ endDate: dateTo || "",
904
+ onStartChange: onDateFromChange,
905
+ onEndChange: onDateToChange
906
+ }
907
+ )
908
+ ] });
909
+ }
910
+
911
+ // src/components/ExportButton.tsx
912
+ var import_react7 = require("react");
913
+ var import_jsx_runtime8 = require("react/jsx-runtime");
914
+ function ExportButton({ data, filename, className }) {
915
+ const [open, setOpen] = (0, import_react7.useState)(false);
916
+ const ref = (0, import_react7.useRef)(null);
917
+ (0, import_react7.useEffect)(() => {
918
+ const handler = (e) => {
919
+ if (ref.current && !ref.current.contains(e.target)) setOpen(false);
920
+ };
921
+ document.addEventListener("mousedown", handler);
922
+ return () => document.removeEventListener("mousedown", handler);
923
+ }, []);
924
+ if (!data || data.length === 0) return null;
925
+ function download(format) {
926
+ let content;
927
+ let mimeType;
928
+ let ext;
929
+ const headers = Object.keys(data[0]);
930
+ switch (format) {
931
+ case "csv": {
932
+ const rows = [
933
+ headers.join(","),
934
+ ...data.map(
935
+ (row) => headers.map((h) => {
936
+ const val = row[h];
937
+ const str = val === null || val === void 0 ? "" : String(val);
938
+ return str.includes(",") || str.includes('"') || str.includes("\n") ? `"${str.replace(/"/g, '""')}"` : str;
939
+ }).join(",")
940
+ )
941
+ ];
942
+ content = rows.join("\n");
943
+ mimeType = "text/csv";
944
+ ext = "csv";
945
+ break;
946
+ }
947
+ case "json": {
948
+ content = JSON.stringify(data, null, 2);
949
+ mimeType = "application/json";
950
+ ext = "json";
951
+ break;
952
+ }
953
+ case "markdown": {
954
+ const headerRow = "| " + headers.join(" | ") + " |";
955
+ const separator = "| " + headers.map(() => "---").join(" | ") + " |";
956
+ const bodyRows = data.map(
957
+ (row) => "| " + headers.map((h) => String(row[h] ?? "")).join(" | ") + " |"
958
+ );
959
+ content = [headerRow, separator, ...bodyRows].join("\n");
960
+ mimeType = "text/markdown";
961
+ ext = "md";
962
+ break;
963
+ }
964
+ }
965
+ const blob = new Blob([content], { type: mimeType });
966
+ const url = URL.createObjectURL(blob);
967
+ const a = document.createElement("a");
968
+ a.href = url;
969
+ a.download = `${filename}.${ext}`;
970
+ document.body.appendChild(a);
971
+ a.click();
972
+ document.body.removeChild(a);
973
+ URL.revokeObjectURL(url);
974
+ setOpen(false);
975
+ }
976
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { ref, className: `relative ${className ?? ""}`, children: [
977
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
978
+ "button",
979
+ {
980
+ onClick: () => setOpen(!open),
981
+ className: "flex items-center gap-1.5 px-3 py-1.5 text-sm border border-[rgb(var(--lm-border))] rounded-lg hover:border-[rgb(var(--lm-border-hover))] transition-colors text-[rgb(var(--lm-text-secondary))]",
982
+ children: [
983
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" }) }),
984
+ "Export"
985
+ ]
986
+ }
987
+ ),
988
+ open && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "absolute top-full mt-1 right-0 w-36 bg-[rgb(var(--lm-bg))] border border-[rgb(var(--lm-border))] rounded-lg shadow-lg z-50 py-1", children: [
989
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { onClick: () => download("csv"), className: "w-full text-left px-3 py-2 text-sm hover:bg-[rgb(var(--lm-bg-secondary))] text-[rgb(var(--lm-text-secondary))]", children: "CSV" }),
990
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { onClick: () => download("json"), className: "w-full text-left px-3 py-2 text-sm hover:bg-[rgb(var(--lm-bg-secondary))] text-[rgb(var(--lm-text-secondary))]", children: "JSON" }),
991
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { onClick: () => download("markdown"), className: "w-full text-left px-3 py-2 text-sm hover:bg-[rgb(var(--lm-bg-secondary))] text-[rgb(var(--lm-text-secondary))]", children: "Markdown" })
992
+ ] })
993
+ ] });
994
+ }
995
+
996
+ // src/widgets/StatCards.tsx
997
+ var import_jsx_runtime9 = require("react/jsx-runtime");
998
+ function StatCards({ period, className }) {
999
+ const { data: overview, isLoading: loading } = useOverview({ period });
1000
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: `grid grid-cols-2 md:grid-cols-4 gap-5 ${className ?? ""}`, children: [
1001
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1002
+ StatCard,
1003
+ {
1004
+ title: "Pageviews",
1005
+ value: overview?.pageviews?.total ?? 0,
1006
+ changePercent: overview?.pageviews?.changePercent,
1007
+ loading
1008
+ }
1009
+ ),
1010
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1011
+ StatCard,
1012
+ {
1013
+ title: "Visitors",
1014
+ value: overview?.visitors?.total ?? 0,
1015
+ changePercent: overview?.visitors?.changePercent,
1016
+ loading
1017
+ }
1018
+ ),
1019
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1020
+ StatCard,
1021
+ {
1022
+ title: "Sessions",
1023
+ value: overview?.sessions?.total ?? 0,
1024
+ changePercent: overview?.sessions?.changePercent,
1025
+ loading
1026
+ }
1027
+ ),
1028
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1029
+ StatCard,
1030
+ {
1031
+ title: "Events",
1032
+ value: overview?.events?.total ?? 0,
1033
+ changePercent: overview?.events?.changePercent,
1034
+ loading
1035
+ }
1036
+ )
1037
+ ] });
1038
+ }
1039
+
1040
+ // src/widgets/TimeSeriesChart.tsx
1041
+ var import_react8 = require("react");
1042
+ var import_recharts2 = require("recharts");
1043
+
1044
+ // src/utils/formatters.ts
1045
+ function formatDate(iso, period) {
1046
+ const d = new Date(iso);
1047
+ if (period === "1h" || period === "24h") {
1048
+ return d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
1049
+ }
1050
+ return d.toLocaleDateString([], { month: "short", day: "numeric" });
1051
+ }
1052
+ function formatTooltipDate(iso, period) {
1053
+ const d = new Date(iso);
1054
+ if (period === "1h" || period === "24h") {
1055
+ return d.toLocaleString([], { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
1056
+ }
1057
+ return d.toLocaleDateString([], { weekday: "short", month: "short", day: "numeric" });
1058
+ }
1059
+
1060
+ // src/widgets/TimeSeriesChart.tsx
1061
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1062
+ var metrics = [
1063
+ { value: "pageviews", label: "Pageviews" },
1064
+ { value: "visitors", label: "Visitors" },
1065
+ { value: "sessions", label: "Sessions" }
1066
+ ];
1067
+ function TimeSeriesChart({ defaultMetric = "pageviews", period: periodProp, className }) {
1068
+ const [metric, setMetric] = (0, import_react8.useState)(defaultMetric);
1069
+ const { period: ctxPeriod } = useLitemetricsUI();
1070
+ const period = periodProp ?? ctxPeriod;
1071
+ const { get } = useThemeColors();
1072
+ const { data: result, isLoading: loading } = useTimeSeries(metric, { period });
1073
+ const data = result?.data ?? [];
1074
+ const strokeColor = get("--lm-chart-stroke", "99 102 241");
1075
+ const fillColor = get("--lm-chart-fill", "99 102 241");
1076
+ const gridColor = get("--lm-chart-grid", "244 244 245");
1077
+ const axisColor = get("--lm-chart-axis", "161 161 170");
1078
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: `rounded-xl bg-[rgb(var(--lm-bg))] border border-[rgb(var(--lm-border))] p-6 ${className ?? ""}`, children: [
1079
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center justify-between mb-4", children: [
1080
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h3", { className: "text-sm font-medium text-[rgb(var(--lm-text-secondary))]", children: "Overview" }),
1081
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex gap-1", children: metrics.map((m) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1082
+ "button",
1083
+ {
1084
+ onClick: () => setMetric(m.value),
1085
+ className: `px-2.5 py-1 text-xs rounded-md transition-colors ${metric === m.value ? "bg-[rgb(var(--lm-accent-light))] text-[rgb(var(--lm-accent-text))] font-medium" : "text-[rgb(var(--lm-text-tertiary))] hover:text-[rgb(var(--lm-text-secondary))]"}`,
1086
+ children: m.label
1087
+ },
1088
+ m.value
1089
+ )) })
1090
+ ] }),
1091
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "h-64", children: loading ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "h-full bg-[rgb(var(--lm-bg-secondary))] rounded-lg animate-pulse" }) : data.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "h-full flex items-center justify-center text-[rgb(var(--lm-text-tertiary))] text-sm", children: "No data for this period" }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_recharts2.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_recharts2.AreaChart, { data, margin: { top: 4, right: 4, bottom: 0, left: -20 }, children: [
1092
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("linearGradient", { id: "lm-chartGradient", x1: "0", y1: "0", x2: "0", y2: "1", children: [
1093
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("stop", { offset: "0%", stopColor: fillColor, stopOpacity: 0.2 }),
1094
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("stop", { offset: "100%", stopColor: fillColor, stopOpacity: 0 })
1095
+ ] }) }),
1096
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_recharts2.CartesianGrid, { strokeDasharray: "3 3", stroke: gridColor, vertical: false }),
1097
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1098
+ import_recharts2.XAxis,
1099
+ {
1100
+ dataKey: "date",
1101
+ tickFormatter: (v) => formatDate(v, period),
1102
+ tick: { fontSize: 11, fill: axisColor },
1103
+ tickLine: false,
1104
+ axisLine: { stroke: gridColor },
1105
+ interval: "preserveStartEnd",
1106
+ minTickGap: 40
1107
+ }
1108
+ ),
1109
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1110
+ import_recharts2.YAxis,
1111
+ {
1112
+ tick: { fontSize: 11, fill: axisColor },
1113
+ tickLine: false,
1114
+ axisLine: false,
1115
+ allowDecimals: false
1116
+ }
1117
+ ),
1118
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1119
+ import_recharts2.Tooltip,
1120
+ {
1121
+ content: ({ active, payload }) => {
1122
+ if (!active || !payload?.[0]) return null;
1123
+ const point = payload[0].payload;
1124
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bg-[rgb(var(--lm-tooltip-bg))] text-[rgb(var(--lm-tooltip-text))] text-xs rounded-lg px-3 py-2 shadow-lg", children: [
1125
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-[rgb(var(--lm-tooltip-muted))] mb-0.5", children: formatTooltipDate(point.date, period) }),
1126
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("p", { className: "font-medium", children: [
1127
+ point.value.toLocaleString(),
1128
+ " ",
1129
+ metric
1130
+ ] })
1131
+ ] });
1132
+ }
1133
+ }
1134
+ ),
1135
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1136
+ import_recharts2.Area,
1137
+ {
1138
+ type: "monotone",
1139
+ dataKey: "value",
1140
+ stroke: strokeColor,
1141
+ strokeWidth: 2,
1142
+ fill: "url(#lm-chartGradient)"
1143
+ }
1144
+ )
1145
+ ] }) }) })
1146
+ ] });
1147
+ }
1148
+
1149
+ // src/widgets/TopPages.tsx
1150
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1151
+ function TopPages({ period, limit = 10, className }) {
1152
+ const { data, isLoading } = useStats("top_pages", { period, limit });
1153
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(TopList, { title: "Pages", type: "pages", data: data?.data ?? null, loading: isLoading, className });
1154
+ }
1155
+
1156
+ // src/widgets/TopReferrers.tsx
1157
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1158
+ function TopReferrers({ period, limit = 10, className }) {
1159
+ const { data, isLoading } = useStats("top_referrers", { period, limit });
1160
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TopList, { title: "Referrers", type: "referrers", data: data?.data ?? null, loading: isLoading, className });
1161
+ }
1162
+
1163
+ // src/widgets/TopCountries.tsx
1164
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1165
+ function TopCountries({ period, limit = 10, className }) {
1166
+ const { data, isLoading } = useStats("top_countries", { period, limit });
1167
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TopList, { title: "Countries", type: "countries", data: data?.data ?? null, loading: isLoading, className });
1168
+ }
1169
+
1170
+ // src/widgets/TopEvents.tsx
1171
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1172
+ function TopEvents({ period, limit = 10, className }) {
1173
+ const { data, isLoading } = useStats("top_events", { period, limit });
1174
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(TopList, { title: "Events", type: "events", data: data?.data ?? null, loading: isLoading, className });
1175
+ }
1176
+
1177
+ // src/widgets/TopBrowsers.tsx
1178
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1179
+ function TopBrowsers({ period, limit = 10, className }) {
1180
+ const { data, isLoading } = useStats("top_browsers", { period, limit });
1181
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TopList, { title: "Browsers", type: "browsers", data: data?.data ?? null, loading: isLoading, className });
1182
+ }
1183
+
1184
+ // src/widgets/TopDevices.tsx
1185
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1186
+ function TopDevices({ period, limit = 10, className }) {
1187
+ const { data, isLoading } = useStats("top_devices", { period, limit });
1188
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(TopList, { title: "Devices", type: "devices", data: data?.data ?? null, loading: isLoading, className });
1189
+ }
1190
+
1191
+ // src/widgets/BrowsersChart.tsx
1192
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1193
+ function BrowsersChart({ period, className }) {
1194
+ const { data, isLoading } = useStats("top_browsers", { period });
1195
+ const pieData = (data?.data ?? []).map((d) => ({ name: d.key, value: d.value }));
1196
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(PieChartCard, { title: "Browsers", data: pieData, loading: isLoading, className });
1197
+ }
1198
+
1199
+ // src/widgets/DevicesChart.tsx
1200
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1201
+ function DevicesChart({ period, className }) {
1202
+ const { data, isLoading } = useStats("top_devices", { period });
1203
+ const pieData = (data?.data ?? []).map((d) => ({ name: d.key, value: d.value }));
1204
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(PieChartCard, { title: "Devices", data: pieData, loading: isLoading, className });
1205
+ }
1206
+
1207
+ // src/widgets/WorldMap.tsx
1208
+ var import_react9 = require("react");
1209
+ var import_jsx_runtime20 = require("react/jsx-runtime");
1210
+ var geoUrl = "https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json";
1211
+ var WorldMapInner2 = (0, import_react9.lazy)(() => Promise.resolve().then(() => (init_WorldMapInner(), WorldMapInner_exports)));
1212
+ var WorldMap = (0, import_react9.memo)(function WorldMap2({ period, className }) {
1213
+ const [tooltip, setTooltip] = (0, import_react9.useState)(null);
1214
+ const { get } = useThemeColors();
1215
+ const { data: statsResult, isLoading: loading } = useStats("top_countries", { period, limit: 200 });
1216
+ const countryData = {};
1217
+ if (statsResult?.data) {
1218
+ for (const d of statsResult.data) {
1219
+ countryData[d.key] = d.value;
1220
+ }
1221
+ }
1222
+ const maxValue = Math.max(...Object.values(countryData), 1);
1223
+ function parseRGB(triplet) {
1224
+ const parts = triplet.trim().split(/\s+/).map(Number);
1225
+ return [parts[0] || 0, parts[1] || 0, parts[2] || 0];
1226
+ }
1227
+ function getColor(iso) {
1228
+ const value = countryData[iso] || 0;
1229
+ const emptyColor = get("--lm-map-empty", "244 244 245");
1230
+ if (value === 0) return emptyColor;
1231
+ const intensity = Math.min(value / maxValue, 1);
1232
+ const emptyRaw = getComputedStyle(document.documentElement).getPropertyValue("--lm-map-empty").trim() || "244 244 245";
1233
+ const accentRaw = getComputedStyle(document.documentElement).getPropertyValue("--lm-accent").trim() || "99 102 241";
1234
+ const [er, eg, eb] = parseRGB(emptyRaw);
1235
+ const [ar, ag, ab] = parseRGB(accentRaw);
1236
+ const r = Math.round(er + intensity * (ar - er));
1237
+ const g = Math.round(eg + intensity * (ag - eg));
1238
+ const b = Math.round(eb + intensity * (ab - eb));
1239
+ return `rgb(${r}, ${g}, ${b})`;
1240
+ }
1241
+ const strokeColor = get("--lm-map-stroke", "228 228 231");
1242
+ const hoverColor = get("--lm-map-hover", "129 140 248");
1243
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: `rounded-xl bg-[rgb(var(--lm-bg))] border border-[rgb(var(--lm-border))] p-5 ${className ?? ""}`, children: [
1244
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("h3", { className: "text-xs font-medium text-[rgb(var(--lm-text-tertiary))] uppercase tracking-wide mb-3", children: "Visitors by Country" }),
1245
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "h-64 bg-[rgb(var(--lm-bg-secondary))] rounded-lg animate-pulse" }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react9.Suspense, { fallback: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "h-64 bg-[rgb(var(--lm-bg-secondary))] rounded-lg flex items-center justify-center text-[rgb(var(--lm-text-tertiary))] text-sm", children: "Loading map..." }), children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1246
+ WorldMapInner2,
1247
+ {
1248
+ geoUrl,
1249
+ countryData,
1250
+ getColor,
1251
+ setTooltip,
1252
+ strokeColor,
1253
+ hoverColor
1254
+ }
1255
+ ) }),
1256
+ tooltip && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
1257
+ "div",
1258
+ {
1259
+ className: "fixed z-50 bg-[rgb(var(--lm-tooltip-bg))] text-[rgb(var(--lm-tooltip-text))] text-xs rounded-lg px-3 py-2 shadow-lg pointer-events-none",
1260
+ style: { left: tooltip.x + 10, top: tooltip.y - 30 },
1261
+ children: [
1262
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "font-medium", children: tooltip.name }),
1263
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("p", { className: "text-[rgb(var(--lm-tooltip-muted))]", children: [
1264
+ tooltip.value.toLocaleString(),
1265
+ " visitor",
1266
+ tooltip.value !== 1 ? "s" : ""
1267
+ ] })
1268
+ ]
1269
+ }
1270
+ )
1271
+ ] });
1272
+ });
1273
+
1274
+ // src/widgets/AnalyticsDashboard.tsx
1275
+ var import_jsx_runtime21 = require("react/jsx-runtime");
1276
+ function AnalyticsDashboard({
1277
+ showPeriodSelector = true,
1278
+ showExport = true,
1279
+ showWorldMap = true,
1280
+ showPieCharts = true,
1281
+ period,
1282
+ className
1283
+ }) {
1284
+ const { period: ctxPeriod, setPeriod, dateFrom, setDateFrom, dateTo, setDateTo } = useLitemetricsUI();
1285
+ const effectivePeriod = period ?? ctxPeriod;
1286
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className, children: [
1287
+ (showPeriodSelector || showExport) && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center justify-between mb-6 flex-wrap gap-3", children: [
1288
+ showPeriodSelector && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1289
+ PeriodSelector,
1290
+ {
1291
+ value: effectivePeriod,
1292
+ onChange: setPeriod,
1293
+ dateFrom,
1294
+ dateTo,
1295
+ onDateFromChange: setDateFrom,
1296
+ onDateToChange: setDateTo
1297
+ }
1298
+ ),
1299
+ showExport && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ExportButton, { data: [], filename: `analytics-${effectivePeriod}` })
1300
+ ] }),
1301
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TimeSeriesChart, { period: effectivePeriod, className: "mb-6" }),
1302
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(StatCards, { period: effectivePeriod, className: "mb-6" }),
1303
+ showWorldMap && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(WorldMap, { period: effectivePeriod, className: "mb-6" }),
1304
+ showPieCharts && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-5 mb-6", children: [
1305
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(BrowsersChart, { period: effectivePeriod }),
1306
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DevicesChart, { period: effectivePeriod })
1307
+ ] }),
1308
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-5", children: [
1309
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TopPages, { period: effectivePeriod }),
1310
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TopReferrers, { period: effectivePeriod }),
1311
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TopCountries, { period: effectivePeriod }),
1312
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TopEvents, { period: effectivePeriod }),
1313
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TopBrowsers, { period: effectivePeriod }),
1314
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TopDevices, { period: effectivePeriod })
1315
+ ] })
1316
+ ] });
1317
+ }
1318
+
1319
+ // src/utils/index.ts
1320
+ init_worldmap_data();
1321
+ // Annotate the CommonJS export names for ESM import in node:
1322
+ 0 && (module.exports = {
1323
+ AnalyticsDashboard,
1324
+ BrowsersChart,
1325
+ DateRangePicker,
1326
+ DevicesChart,
1327
+ ExportButton,
1328
+ LitemetricsProvider,
1329
+ PeriodSelector,
1330
+ PieChartCard,
1331
+ StatCard,
1332
+ StatCards,
1333
+ TimeSeriesChart,
1334
+ TopBrowsers,
1335
+ TopCountries,
1336
+ TopDevices,
1337
+ TopEvents,
1338
+ TopList,
1339
+ TopPages,
1340
+ TopReferrers,
1341
+ WorldMap,
1342
+ countryToFlag,
1343
+ cssVar,
1344
+ darkTheme,
1345
+ defaultTheme,
1346
+ formatDate,
1347
+ formatTooltipDate,
1348
+ getBrowserIcon,
1349
+ getDeviceIcon,
1350
+ getOSIcon,
1351
+ getPieColors,
1352
+ getReferrerIcon,
1353
+ queryKeys,
1354
+ useLitemetricsUI,
1355
+ useOverview,
1356
+ useStats,
1357
+ useThemeColors,
1358
+ useTimeSeries
1359
+ });
1360
+ //# sourceMappingURL=index.cjs.map