strapi-plugin-magic-sessionmanager 1.0.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.
@@ -0,0 +1,598 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect } from "react";
3
+ import { useFetchClient } from "@strapi/strapi/admin";
4
+ import styled, { keyframes } from "styled-components";
5
+ import { Loader, Typography, Box, Flex, Badge } from "@strapi/design-system";
6
+ import { ChartBubble, Crown, User, Clock, Monitor } from "@strapi/icons";
7
+ import { a as pluginId } from "./index-BGCs2pNv.mjs";
8
+ import { u as useLicense } from "./useLicense-Dw_-SJHP.mjs";
9
+ const theme = {
10
+ colors: {
11
+ primary: { 100: "#E0F2FE", 500: "#0EA5E9", 600: "#0284C7" },
12
+ secondary: { 100: "#EDE9FE", 500: "#A855F7", 600: "#9333EA" },
13
+ success: { 100: "#DCFCE7", 500: "#22C55E", 600: "#16A34A" },
14
+ warning: { 100: "#FEF3C7", 500: "#F59E0B", 600: "#D97706" },
15
+ neutral: { 0: "#FFFFFF", 100: "#F3F4F6", 200: "#E5E7EB", 600: "#4B5563", 700: "#374151", 800: "#1F2937" }
16
+ },
17
+ shadows: {
18
+ sm: "0 1px 3px 0 rgba(0, 0, 0, 0.1)",
19
+ md: "0 4px 6px -1px rgba(0, 0, 0, 0.1)",
20
+ lg: "0 10px 15px -3px rgba(0, 0, 0, 0.1)",
21
+ xl: "0 20px 25px -5px rgba(0, 0, 0, 0.1)"
22
+ },
23
+ transitions: {
24
+ normal: "300ms cubic-bezier(0.4, 0, 0.2, 1)",
25
+ slow: "500ms cubic-bezier(0.4, 0, 0.2, 1)"
26
+ },
27
+ spacing: { xl: "32px", "2xl": "48px" },
28
+ borderRadius: { lg: "12px", xl: "16px" }
29
+ };
30
+ const fadeIn = keyframes`
31
+ from { opacity: 0; transform: translateY(20px); }
32
+ to { opacity: 1; transform: translateY(0); }
33
+ `;
34
+ const slideIn = keyframes`
35
+ from { opacity: 0; transform: translateX(-20px); }
36
+ to { opacity: 1; transform: translateX(0); }
37
+ `;
38
+ const shimmer = keyframes`
39
+ 0% { background-position: -200% 0; }
40
+ 100% { background-position: 200% 0; }
41
+ `;
42
+ const float = keyframes`
43
+ 0%, 100% { transform: translateY(0px); }
44
+ 50% { transform: translateY(-8px); }
45
+ `;
46
+ const pulse = keyframes`
47
+ 0%, 100% { opacity: 1; }
48
+ 50% { opacity: 0.8; }
49
+ `;
50
+ const growBar = keyframes`
51
+ from { width: 0; }
52
+ to { width: var(--bar-width); }
53
+ `;
54
+ const Container = styled(Box)`
55
+ animation: ${fadeIn} 0.6s;
56
+ min-height: 100vh;
57
+ max-width: 1440px;
58
+ margin: 0 auto;
59
+ padding: ${theme.spacing.xl} 24px 0;
60
+ `;
61
+ const Header = styled(Box)`
62
+ background: linear-gradient(135deg, ${theme.colors.primary[600]} 0%, ${theme.colors.secondary[600]} 100%);
63
+ border-radius: ${theme.borderRadius.xl};
64
+ padding: ${theme.spacing.xl} ${theme.spacing["2xl"]};
65
+ margin-bottom: ${theme.spacing.xl};
66
+ position: relative;
67
+ overflow: hidden;
68
+ box-shadow: ${theme.shadows.xl};
69
+
70
+ &::before {
71
+ content: '';
72
+ position: absolute;
73
+ top: 0;
74
+ left: -100%;
75
+ width: 200%;
76
+ height: 100%;
77
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.15), transparent);
78
+ animation: ${shimmer} 3s infinite;
79
+ }
80
+
81
+ &::after {
82
+ content: '';
83
+ position: absolute;
84
+ top: 0;
85
+ right: 0;
86
+ width: 100%;
87
+ height: 100%;
88
+ background-image: radial-gradient(circle at 20% 80%, transparent 50%, rgba(255, 255, 255, 0.1) 50%);
89
+ background-size: 15px 15px;
90
+ opacity: 0.3;
91
+ }
92
+ `;
93
+ const HeaderContent = styled(Flex)`
94
+ position: relative;
95
+ z-index: 1;
96
+ `;
97
+ const Title = styled(Typography)`
98
+ color: ${theme.colors.neutral[0]};
99
+ font-size: 2.25rem;
100
+ font-weight: 700;
101
+ letter-spacing: -0.025em;
102
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
103
+ display: flex;
104
+ align-items: center;
105
+ gap: 16px;
106
+ margin-bottom: 8px;
107
+
108
+ svg {
109
+ width: 32px;
110
+ height: 32px;
111
+ animation: ${float} 3s ease-in-out infinite;
112
+ }
113
+ `;
114
+ const Subtitle = styled(Typography)`
115
+ color: rgba(255, 255, 255, 0.95);
116
+ font-size: 1rem;
117
+ font-weight: 400;
118
+ letter-spacing: 0.01em;
119
+ `;
120
+ const StatsGrid = styled.div`
121
+ display: grid;
122
+ grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
123
+ gap: 24px;
124
+ margin-bottom: 40px;
125
+ `;
126
+ const StatCard = styled(Box)`
127
+ background: ${theme.colors.neutral[0]};
128
+ border-radius: ${theme.borderRadius.lg};
129
+ padding: 32px;
130
+ position: relative;
131
+ overflow: hidden;
132
+ transition: all ${theme.transitions.normal};
133
+ animation: ${fadeIn} ${theme.transitions.slow} backwards;
134
+ animation-delay: ${(props) => props.$delay || "0s"};
135
+ box-shadow: ${theme.shadows.sm};
136
+ border: 1px solid ${theme.colors.neutral[200]};
137
+ text-align: center;
138
+ display: flex;
139
+ flex-direction: column;
140
+ align-items: center;
141
+ justify-content: center;
142
+
143
+ &:hover {
144
+ transform: translateY(-6px);
145
+ box-shadow: ${theme.shadows.xl};
146
+ border-color: ${(props) => props.$borderColor || theme.colors.primary[500]};
147
+
148
+ .stat-icon {
149
+ transform: scale(1.15) rotate(5deg);
150
+ }
151
+
152
+ .stat-value {
153
+ transform: scale(1.08);
154
+ color: ${(props) => props.$accentColor || theme.colors.primary[600]};
155
+ }
156
+ }
157
+ `;
158
+ const StatIcon = styled(Box)`
159
+ width: 80px;
160
+ height: 80px;
161
+ border-radius: ${theme.borderRadius.lg};
162
+ display: flex;
163
+ align-items: center;
164
+ justify-content: center;
165
+ background: ${(props) => props.$bg || theme.colors.primary[100]};
166
+ transition: all ${theme.transitions.normal};
167
+ margin: 0 auto 24px;
168
+ box-shadow: ${theme.shadows.sm};
169
+
170
+ svg {
171
+ width: 40px;
172
+ height: 40px;
173
+ color: ${(props) => props.$color || theme.colors.primary[600]};
174
+ }
175
+ `;
176
+ const StatValue = styled(Typography)`
177
+ font-size: 3.5rem;
178
+ font-weight: 700;
179
+ color: ${theme.colors.neutral[800]};
180
+ line-height: 1;
181
+ margin-bottom: 12px;
182
+ transition: all ${theme.transitions.normal};
183
+ text-align: center;
184
+ `;
185
+ const StatLabel = styled(Typography)`
186
+ font-size: 1rem;
187
+ color: ${theme.colors.neutral[600]};
188
+ font-weight: 500;
189
+ text-align: center;
190
+ `;
191
+ const ChartCard = styled(Box)`
192
+ background: ${theme.colors.neutral[0]};
193
+ border-radius: ${theme.borderRadius.lg};
194
+ padding: 36px;
195
+ box-shadow: ${theme.shadows.md};
196
+ border: 1px solid ${theme.colors.neutral[200]};
197
+ margin-bottom: 28px;
198
+ animation: ${slideIn} ${theme.transitions.slow};
199
+ transition: all ${theme.transitions.normal};
200
+
201
+ &:hover {
202
+ box-shadow: ${theme.shadows.lg};
203
+ border-color: ${theme.colors.primary[200]};
204
+ }
205
+ `;
206
+ const ChartTitle = styled(Typography)`
207
+ font-size: 1.25rem;
208
+ font-weight: 700;
209
+ color: ${theme.colors.neutral[800]};
210
+ margin-bottom: 24px;
211
+ display: flex;
212
+ align-items: center;
213
+ gap: 12px;
214
+
215
+ svg {
216
+ width: 24px;
217
+ height: 24px;
218
+ color: ${theme.colors.primary[600]};
219
+ }
220
+ `;
221
+ const BarChart = styled.div`
222
+ display: flex;
223
+ flex-direction: column;
224
+ gap: 20px;
225
+ `;
226
+ const BarRow = styled.div`
227
+ display: flex;
228
+ align-items: center;
229
+ gap: 20px;
230
+ animation: ${fadeIn} 0.6s backwards;
231
+ animation-delay: ${(props) => props.$delay || "0s"};
232
+ `;
233
+ const BarLabel = styled(Typography)`
234
+ min-width: 110px;
235
+ font-size: 15px;
236
+ font-weight: 600;
237
+ color: ${theme.colors.neutral[700]};
238
+ `;
239
+ const BarContainer = styled.div`
240
+ flex: 1;
241
+ height: 40px;
242
+ background: ${theme.colors.neutral[100]};
243
+ border-radius: 10px;
244
+ overflow: hidden;
245
+ position: relative;
246
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.06);
247
+ `;
248
+ const BarFill = styled.div`
249
+ height: 100%;
250
+ background: linear-gradient(90deg, ${(props) => props.$color1 || theme.colors.primary[500]}, ${(props) => props.$color2 || theme.colors.primary[600]});
251
+ border-radius: 10px;
252
+ --bar-width: ${(props) => props.$percentage || 0}%;
253
+ animation: ${growBar} 1s cubic-bezier(0.4, 0, 0.2, 1) forwards;
254
+ animation-delay: ${(props) => props.$delay || "0s"};
255
+ display: flex;
256
+ align-items: center;
257
+ justify-content: flex-end;
258
+ padding-right: 16px;
259
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
260
+ `;
261
+ const BarValue = styled(Typography)`
262
+ color: white;
263
+ font-size: 15px;
264
+ font-weight: 700;
265
+ text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
266
+ `;
267
+ const LoadingOverlay = styled.div`
268
+ display: flex;
269
+ flex-direction: column;
270
+ align-items: center;
271
+ justify-content: center;
272
+ min-height: 400px;
273
+ gap: 24px;
274
+
275
+ .loader-icon {
276
+ animation: ${pulse} 2s ease-in-out infinite;
277
+ }
278
+ `;
279
+ const AnalyticsPage = () => {
280
+ const { get } = useFetchClient();
281
+ const { isPremium, loading: licenseLoading } = useLicense();
282
+ const [loading, setLoading] = useState(true);
283
+ const [analytics, setAnalytics] = useState(null);
284
+ useEffect(() => {
285
+ if (!licenseLoading) {
286
+ fetchAnalytics();
287
+ }
288
+ }, [licenseLoading]);
289
+ const fetchAnalytics = async () => {
290
+ setLoading(true);
291
+ try {
292
+ const { data } = await get(`/${pluginId}/sessions`);
293
+ const sessions = data.data || [];
294
+ const now = Date.now();
295
+ const dayAgo = now - 24 * 60 * 60 * 1e3;
296
+ const weekAgo = now - 7 * 24 * 60 * 60 * 1e3;
297
+ const todayLogins = sessions.filter((s) => new Date(s.loginTime).getTime() > dayAgo);
298
+ const weekLogins = sessions.filter((s) => new Date(s.loginTime).getTime() > weekAgo);
299
+ const devices = {};
300
+ const browsers = {};
301
+ const operatingSystems = {};
302
+ const countries = {};
303
+ const loginHours = Array(24).fill(0);
304
+ const uniqueUsers = /* @__PURE__ */ new Set();
305
+ const uniqueIPs = /* @__PURE__ */ new Set();
306
+ sessions.forEach((session) => {
307
+ const ua = session.userAgent.toLowerCase();
308
+ if (ua.includes("mobile") || ua.includes("android") || ua.includes("iphone")) {
309
+ devices["Mobile"] = (devices["Mobile"] || 0) + 1;
310
+ } else if (ua.includes("tablet") || ua.includes("ipad")) {
311
+ devices["Tablet"] = (devices["Tablet"] || 0) + 1;
312
+ } else {
313
+ devices["Desktop"] = (devices["Desktop"] || 0) + 1;
314
+ }
315
+ if (ua.includes("chrome") && !ua.includes("edg")) browsers["Chrome"] = (browsers["Chrome"] || 0) + 1;
316
+ else if (ua.includes("firefox")) browsers["Firefox"] = (browsers["Firefox"] || 0) + 1;
317
+ else if (ua.includes("safari") && !ua.includes("chrome")) browsers["Safari"] = (browsers["Safari"] || 0) + 1;
318
+ else if (ua.includes("edg")) browsers["Edge"] = (browsers["Edge"] || 0) + 1;
319
+ else if (ua.includes("postman") || ua.includes("curl")) browsers["API Client"] = (browsers["API Client"] || 0) + 1;
320
+ else browsers["Other"] = (browsers["Other"] || 0) + 1;
321
+ if (ua.includes("windows") || ua.includes("win32") || ua.includes("win64")) {
322
+ operatingSystems["Windows"] = (operatingSystems["Windows"] || 0) + 1;
323
+ } else if (ua.includes("mac") || ua.includes("darwin")) {
324
+ operatingSystems["macOS"] = (operatingSystems["macOS"] || 0) + 1;
325
+ } else if (ua.includes("linux")) {
326
+ operatingSystems["Linux"] = (operatingSystems["Linux"] || 0) + 1;
327
+ } else if (ua.includes("android")) {
328
+ operatingSystems["Android"] = (operatingSystems["Android"] || 0) + 1;
329
+ } else if (ua.includes("ios") || ua.includes("iphone") || ua.includes("ipad")) {
330
+ operatingSystems["iOS"] = (operatingSystems["iOS"] || 0) + 1;
331
+ } else {
332
+ operatingSystems["Other"] = (operatingSystems["Other"] || 0) + 1;
333
+ }
334
+ const loginHour = new Date(session.loginTime).getHours();
335
+ loginHours[loginHour]++;
336
+ if (session.user?.id) uniqueUsers.add(session.user.id);
337
+ if (session.ipAddress) uniqueIPs.add(session.ipAddress);
338
+ });
339
+ const peakHour = loginHours.indexOf(Math.max(...loginHours));
340
+ const loggedOut = sessions.filter((s) => !s.isActive && s.logoutTime).length;
341
+ const terminated = sessions.filter((s) => !s.isActive && !s.logoutTime).length;
342
+ const mobileCount = (devices["Mobile"] || 0) + (devices["Tablet"] || 0);
343
+ const desktopCount = devices["Desktop"] || 0;
344
+ const mobileRatio = sessions.length > 0 ? Math.round(mobileCount / sessions.length * 100) : 0;
345
+ setAnalytics({
346
+ totalSessions: sessions.length,
347
+ activeSessions: sessions.filter((s) => s.isActive && s.isTrulyActive).length,
348
+ todayLogins: todayLogins.length,
349
+ weekLogins: weekLogins.length,
350
+ devices,
351
+ browsers,
352
+ operatingSystems,
353
+ loginHours,
354
+ peakHour,
355
+ uniqueUsers: uniqueUsers.size,
356
+ uniqueIPs: uniqueIPs.size,
357
+ loggedOut,
358
+ terminated,
359
+ mobileRatio,
360
+ avgSessionDuration: sessions.length > 0 ? Math.floor(sessions.reduce((sum, s) => sum + (s.minutesSinceActive || 0), 0) / sessions.length) : 0
361
+ });
362
+ } catch (err) {
363
+ console.error("[Analytics] Error:", err);
364
+ } finally {
365
+ setLoading(false);
366
+ }
367
+ };
368
+ if (licenseLoading) {
369
+ return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsxs(LoadingOverlay, { children: [
370
+ /* @__PURE__ */ jsx(ChartBubble, { className: "loader-icon", style: { width: "64px", height: "64px", color: theme.colors.primary[600] } }),
371
+ /* @__PURE__ */ jsx(Loader, { children: "Checking license..." }),
372
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Please wait while we verify your premium access" })
373
+ ] }) });
374
+ }
375
+ if (!isPremium) {
376
+ return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsx(Box, { padding: 8, children: /* @__PURE__ */ jsxs(
377
+ Box,
378
+ {
379
+ padding: 10,
380
+ style: {
381
+ background: "linear-gradient(135deg, #fef3c7 0%, #fed7aa 100%)",
382
+ borderRadius: "20px",
383
+ border: "3px solid #fbbf24",
384
+ textAlign: "center",
385
+ boxShadow: "0 20px 40px rgba(245, 158, 11, 0.2)",
386
+ maxWidth: "800px",
387
+ margin: "60px auto",
388
+ position: "relative",
389
+ overflow: "hidden"
390
+ },
391
+ children: [
392
+ /* @__PURE__ */ jsx("div", { style: {
393
+ position: "absolute",
394
+ top: 0,
395
+ left: 0,
396
+ right: 0,
397
+ bottom: 0,
398
+ backgroundImage: "radial-gradient(circle at 20% 80%, transparent 50%, rgba(255, 255, 255, 0.1) 50%)",
399
+ backgroundSize: "20px 20px",
400
+ opacity: 0.5,
401
+ zIndex: 0
402
+ } }),
403
+ /* @__PURE__ */ jsxs("div", { style: { position: "relative", zIndex: 1 }, children: [
404
+ /* @__PURE__ */ jsx(Crown, { style: {
405
+ width: "96px",
406
+ height: "96px",
407
+ color: "#d97706",
408
+ margin: "0 auto 32px",
409
+ display: "block",
410
+ animation: `${float} 3s ease-in-out infinite`
411
+ } }),
412
+ /* @__PURE__ */ jsx(
413
+ Typography,
414
+ {
415
+ variant: "alpha",
416
+ style: {
417
+ color: "#92400e",
418
+ fontWeight: "700",
419
+ marginBottom: "24px",
420
+ fontSize: "36px",
421
+ display: "block",
422
+ textShadow: "0 2px 4px rgba(0, 0, 0, 0.1)"
423
+ },
424
+ children: "📊 Analytics Dashboard"
425
+ }
426
+ ),
427
+ /* @__PURE__ */ jsx(
428
+ Typography,
429
+ {
430
+ variant: "omega",
431
+ style: {
432
+ color: "#78350f",
433
+ lineHeight: "1.9",
434
+ marginBottom: "44px",
435
+ fontSize: "17px",
436
+ display: "block",
437
+ maxWidth: "620px",
438
+ margin: "0 auto 44px"
439
+ },
440
+ children: "Unlock premium analytics to get powerful insights about your user sessions, device statistics, browser trends, and activity patterns"
441
+ }
442
+ ),
443
+ /* @__PURE__ */ jsx(
444
+ "button",
445
+ {
446
+ onClick: () => window.open("https://magicapi.fitlex.me", "_blank"),
447
+ style: {
448
+ background: "linear-gradient(135deg, #f59e0b 0%, #d97706 100%)",
449
+ color: "white",
450
+ border: "none",
451
+ padding: "16px 48px",
452
+ borderRadius: "12px",
453
+ fontSize: "17px",
454
+ fontWeight: "700",
455
+ cursor: "pointer",
456
+ boxShadow: "0 6px 16px rgba(245, 158, 11, 0.4)",
457
+ transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
458
+ },
459
+ onMouseEnter: (e) => {
460
+ e.currentTarget.style.transform = "translateY(-3px) scale(1.05)";
461
+ e.currentTarget.style.boxShadow = "0 12px 24px rgba(245, 158, 11, 0.5)";
462
+ },
463
+ onMouseLeave: (e) => {
464
+ e.currentTarget.style.transform = "translateY(0) scale(1)";
465
+ e.currentTarget.style.boxShadow = "0 6px 16px rgba(245, 158, 11, 0.4)";
466
+ },
467
+ children: "✨ Upgrade to Premium"
468
+ }
469
+ )
470
+ ] })
471
+ ]
472
+ }
473
+ ) }) });
474
+ }
475
+ if (loading) {
476
+ return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsxs(LoadingOverlay, { children: [
477
+ /* @__PURE__ */ jsx(ChartBubble, { className: "loader-icon", style: { width: "64px", height: "64px", color: theme.colors.primary[600] } }),
478
+ /* @__PURE__ */ jsx(Loader, { children: "Loading analytics data..." })
479
+ ] }) });
480
+ }
481
+ const maxDevices = Math.max(...Object.values(analytics?.devices || {}), 1);
482
+ const maxBrowsers = Math.max(...Object.values(analytics?.browsers || {}), 1);
483
+ Math.max(...Object.values(analytics?.operatingSystems || {}), 1);
484
+ Math.max(...analytics?.loginHours || [], 1);
485
+ const deviceColors = {
486
+ "Desktop": [theme.colors.primary[500], theme.colors.primary[600]],
487
+ "Mobile": [theme.colors.success[500], theme.colors.success[600]],
488
+ "Tablet": [theme.colors.warning[500], theme.colors.warning[600]]
489
+ };
490
+ const browserColors = {
491
+ "Chrome": [theme.colors.success[500], theme.colors.success[600]],
492
+ "Firefox": [theme.colors.warning[500], theme.colors.warning[600]],
493
+ "Safari": [theme.colors.primary[500], theme.colors.primary[600]],
494
+ "Edge": [theme.colors.secondary[500], theme.colors.secondary[600]],
495
+ "API Client": [theme.colors.neutral[600], theme.colors.neutral[700]],
496
+ "Other": [theme.colors.neutral[500], theme.colors.neutral[600]]
497
+ };
498
+ return /* @__PURE__ */ jsxs(Container, { children: [
499
+ /* @__PURE__ */ jsx(Header, { children: /* @__PURE__ */ jsxs(HeaderContent, { direction: "column", alignItems: "flex-start", gap: 2, children: [
500
+ /* @__PURE__ */ jsxs(Title, { children: [
501
+ /* @__PURE__ */ jsx(ChartBubble, {}),
502
+ " Session Analytics"
503
+ ] }),
504
+ /* @__PURE__ */ jsx(Subtitle, { children: "Comprehensive insights and statistics about user sessions" })
505
+ ] }) }),
506
+ /* @__PURE__ */ jsxs(StatsGrid, { children: [
507
+ /* @__PURE__ */ jsxs(StatCard, { $delay: "0.1s", $borderColor: theme.colors.primary[500], $accentColor: theme.colors.primary[600], children: [
508
+ /* @__PURE__ */ jsx(StatIcon, { className: "stat-icon", $bg: theme.colors.primary[100], $color: theme.colors.primary[600], children: /* @__PURE__ */ jsx(ChartBubble, {}) }),
509
+ /* @__PURE__ */ jsx(StatValue, { className: "stat-value", children: analytics?.totalSessions || 0 }),
510
+ /* @__PURE__ */ jsx(StatLabel, { children: "Total Sessions" })
511
+ ] }),
512
+ /* @__PURE__ */ jsxs(StatCard, { $delay: "0.2s", $borderColor: theme.colors.success[500], $accentColor: theme.colors.success[600], children: [
513
+ /* @__PURE__ */ jsx(StatIcon, { className: "stat-icon", $bg: theme.colors.success[100], $color: theme.colors.success[600], children: /* @__PURE__ */ jsx(User, {}) }),
514
+ /* @__PURE__ */ jsx(StatValue, { className: "stat-value", children: analytics?.activeSessions || 0 }),
515
+ /* @__PURE__ */ jsx(StatLabel, { children: "Active Now" })
516
+ ] }),
517
+ /* @__PURE__ */ jsxs(StatCard, { $delay: "0.3s", $borderColor: theme.colors.warning[500], $accentColor: theme.colors.warning[600], children: [
518
+ /* @__PURE__ */ jsx(StatIcon, { className: "stat-icon", $bg: theme.colors.warning[100], $color: theme.colors.warning[600], children: /* @__PURE__ */ jsx(Clock, {}) }),
519
+ /* @__PURE__ */ jsx(StatValue, { className: "stat-value", children: analytics?.todayLogins || 0 }),
520
+ /* @__PURE__ */ jsx(StatLabel, { children: "Today's Logins" })
521
+ ] }),
522
+ /* @__PURE__ */ jsxs(StatCard, { $delay: "0.4s", $borderColor: theme.colors.secondary[500], $accentColor: theme.colors.secondary[600], children: [
523
+ /* @__PURE__ */ jsx(StatIcon, { className: "stat-icon", $bg: theme.colors.secondary[100], $color: theme.colors.secondary[600], children: /* @__PURE__ */ jsx(Clock, {}) }),
524
+ /* @__PURE__ */ jsx(StatValue, { className: "stat-value", children: analytics?.weekLogins || 0 }),
525
+ /* @__PURE__ */ jsx(StatLabel, { children: "This Week" })
526
+ ] })
527
+ ] }),
528
+ /* @__PURE__ */ jsxs(Flex, { gap: 4, wrap: "wrap", style: { marginBottom: "28px" }, children: [
529
+ /* @__PURE__ */ jsx(Box, { style: { flex: 1, minWidth: "450px" }, children: /* @__PURE__ */ jsxs(ChartCard, { children: [
530
+ /* @__PURE__ */ jsxs(ChartTitle, { children: [
531
+ /* @__PURE__ */ jsx(Monitor, {}),
532
+ "Device Distribution"
533
+ ] }),
534
+ /* @__PURE__ */ jsx(BarChart, { children: analytics?.devices && Object.entries(analytics.devices).sort(([, a], [, b]) => b - a).map(([device, count], idx) => /* @__PURE__ */ jsxs(BarRow, { $delay: `${0.5 + idx * 0.1}s`, children: [
535
+ /* @__PURE__ */ jsx(BarLabel, { children: device }),
536
+ /* @__PURE__ */ jsx(BarContainer, { children: /* @__PURE__ */ jsx(
537
+ BarFill,
538
+ {
539
+ $percentage: count / maxDevices * 100,
540
+ $color1: deviceColors[device]?.[0] || theme.colors.neutral[500],
541
+ $color2: deviceColors[device]?.[1] || theme.colors.neutral[600],
542
+ $delay: `${0.5 + idx * 0.1}s`,
543
+ children: /* @__PURE__ */ jsx(BarValue, { children: count })
544
+ }
545
+ ) })
546
+ ] }, device)) })
547
+ ] }) }),
548
+ /* @__PURE__ */ jsx(Box, { style: { flex: 1, minWidth: "450px" }, children: /* @__PURE__ */ jsxs(ChartCard, { children: [
549
+ /* @__PURE__ */ jsxs(ChartTitle, { children: [
550
+ /* @__PURE__ */ jsx(Monitor, {}),
551
+ "Browser Usage"
552
+ ] }),
553
+ /* @__PURE__ */ jsx(BarChart, { children: analytics?.browsers && Object.entries(analytics.browsers).sort(([, a], [, b]) => b - a).map(([browser, count], idx) => /* @__PURE__ */ jsxs(BarRow, { $delay: `${0.5 + idx * 0.1}s`, children: [
554
+ /* @__PURE__ */ jsx(BarLabel, { children: browser }),
555
+ /* @__PURE__ */ jsx(BarContainer, { children: /* @__PURE__ */ jsx(
556
+ BarFill,
557
+ {
558
+ $percentage: count / maxBrowsers * 100,
559
+ $color1: browserColors[browser]?.[0] || theme.colors.neutral[500],
560
+ $color2: browserColors[browser]?.[1] || theme.colors.neutral[600],
561
+ $delay: `${0.5 + idx * 0.1}s`,
562
+ children: /* @__PURE__ */ jsx(BarValue, { children: count })
563
+ }
564
+ ) })
565
+ ] }, browser)) })
566
+ ] }) })
567
+ ] }),
568
+ /* @__PURE__ */ jsxs(ChartCard, { children: [
569
+ /* @__PURE__ */ jsxs(Flex, { alignItems: "center", justifyContent: "space-between", children: [
570
+ /* @__PURE__ */ jsxs(ChartTitle, { style: { marginBottom: 0 }, children: [
571
+ /* @__PURE__ */ jsx(Clock, {}),
572
+ "Average Session Duration"
573
+ ] }),
574
+ /* @__PURE__ */ jsxs(
575
+ Badge,
576
+ {
577
+ backgroundColor: "primary600",
578
+ textColor: "neutral0",
579
+ style: {
580
+ fontSize: "18px",
581
+ fontWeight: "700",
582
+ padding: "12px 24px",
583
+ boxShadow: theme.shadows.md
584
+ },
585
+ children: [
586
+ analytics?.avgSessionDuration || 0,
587
+ " minutes"
588
+ ]
589
+ }
590
+ )
591
+ ] }),
592
+ analytics?.avgSessionDuration > 0 && /* @__PURE__ */ jsx(Box, { marginTop: 5, padding: 5, background: "primary50", hasRadius: true, style: { border: `1px solid ${theme.colors.primary[100]}` }, children: /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "primary700", style: { fontSize: "14px", lineHeight: "1.8", fontWeight: "500" }, children: "ℹ️ Average time between login and last activity across all sessions. Lower values indicate more frequent activity, higher values may indicate idle or abandoned sessions." }) })
593
+ ] })
594
+ ] });
595
+ };
596
+ export {
597
+ AnalyticsPage as default
598
+ };