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,401 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect } from "react";
3
+ import { Loader, Box, Alert, Flex, Typography, Button, Badge, Accordion } from "@strapi/design-system";
4
+ import { useFetchClient, useNotification } from "@strapi/strapi/admin";
5
+ import { ArrowClockwise, Duplicate, Download, User, Shield, Sparkle, ChartBubble } from "@strapi/icons";
6
+ import styled, { keyframes } from "styled-components";
7
+ import { a as pluginId } from "./index-BGCs2pNv.mjs";
8
+ const theme = {
9
+ colors: {
10
+ neutral: { 200: "#E5E7EB" }
11
+ },
12
+ shadows: { sm: "0 1px 3px rgba(0,0,0,0.1)" },
13
+ borderRadius: { lg: "12px" }
14
+ };
15
+ const fadeIn = keyframes`
16
+ from { opacity: 0; transform: translateY(10px); }
17
+ to { opacity: 1; transform: translateY(0); }
18
+ `;
19
+ const shimmer = keyframes`
20
+ 0% { background-position: -200% 0; }
21
+ 100% { background-position: 200% 0; }
22
+ `;
23
+ const Container = styled(Box)`
24
+ animation: ${fadeIn} 0.5s;
25
+ max-width: 1400px;
26
+ margin: 0 auto;
27
+ `;
28
+ const StickySaveBar = styled(Box)`
29
+ position: sticky;
30
+ top: 0;
31
+ z-index: 10;
32
+ background: white;
33
+ border-bottom: 1px solid ${theme.colors.neutral[200]};
34
+ box-shadow: ${theme.shadows.sm};
35
+ `;
36
+ const LicenseKeyBanner = styled(Box)`
37
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
38
+ border-radius: ${theme.borderRadius.lg};
39
+ padding: 28px 32px;
40
+ color: white;
41
+ position: relative;
42
+ overflow: hidden;
43
+ box-shadow: 0 4px 20px rgba(102, 126, 234, 0.25);
44
+ margin-bottom: 24px;
45
+
46
+ &::after {
47
+ content: '';
48
+ position: absolute;
49
+ top: -50%;
50
+ right: -50%;
51
+ width: 200%;
52
+ height: 200%;
53
+ background: linear-gradient(
54
+ 45deg,
55
+ transparent,
56
+ rgba(255, 255, 255, 0.08),
57
+ transparent
58
+ );
59
+ animation: ${shimmer} 3s infinite;
60
+ pointer-events: none;
61
+ z-index: 0;
62
+ }
63
+
64
+ & > * {
65
+ position: relative;
66
+ z-index: 1;
67
+ }
68
+ `;
69
+ const LoaderContainer = styled(Flex)`
70
+ min-height: 400px;
71
+ align-items: center;
72
+ justify-content: center;
73
+ flex-direction: column;
74
+ gap: 16px;
75
+ `;
76
+ const LicensePage = () => {
77
+ const { get } = useFetchClient();
78
+ const { toggleNotification } = useNotification();
79
+ const [loading, setLoading] = useState(true);
80
+ const [licenseData, setLicenseData] = useState(null);
81
+ const [error, setError] = useState(null);
82
+ const fetchLicenseStatus = async () => {
83
+ setLoading(true);
84
+ setError(null);
85
+ try {
86
+ const response = await get(`/${pluginId}/license/status`);
87
+ setLicenseData(response.data);
88
+ } catch (err) {
89
+ console.error("[magic-sessionmanager/License] Error fetching license:", err);
90
+ setError("Failed to load license information");
91
+ } finally {
92
+ setLoading(false);
93
+ }
94
+ };
95
+ const handleCopyLicenseKey = async () => {
96
+ try {
97
+ await navigator.clipboard.writeText(licenseData?.data?.licenseKey || "");
98
+ toggleNotification({
99
+ type: "success",
100
+ message: "License key copied to clipboard!"
101
+ });
102
+ } catch (err) {
103
+ toggleNotification({
104
+ type: "danger",
105
+ message: "Failed to copy license key"
106
+ });
107
+ }
108
+ };
109
+ const handleDownloadLicenseKey = () => {
110
+ try {
111
+ const data2 = licenseData?.data || {};
112
+ const licenseKey = data2.licenseKey || "";
113
+ const email = data2.email || "N/A";
114
+ const firstName = data2.firstName || "";
115
+ const lastName = data2.lastName || "";
116
+ const fullName = `${firstName} ${lastName}`.trim() || "N/A";
117
+ const content = `Magic Session Manager - License Key
118
+ ═══════════════════════════════════════
119
+
120
+ License Key: ${licenseKey}
121
+
122
+ License Holder Information:
123
+ ──────────────────────────────────────
124
+ Name: ${fullName}
125
+ Email: ${email}
126
+
127
+ License Status:
128
+ ──────────────────────────────────────
129
+ Status: ${data2.isActive ? "ACTIVE" : "INACTIVE"}
130
+ Expires: ${data2.expiresAt ? new Date(data2.expiresAt).toLocaleDateString() : "Never"}
131
+
132
+ Features:
133
+ ──────────────────────────────────────
134
+ Premium: ${data2.features?.premium ? "Enabled" : "Disabled"}
135
+ Advanced: ${data2.features?.advanced ? "Enabled" : "Disabled"}
136
+ Enterprise: ${data2.features?.enterprise ? "Enabled" : "Disabled"}
137
+
138
+ ═══════════════════════════════════════
139
+ Generated: ${(/* @__PURE__ */ new Date()).toLocaleString()}
140
+ `;
141
+ const blob = new Blob([content], { type: "text/plain" });
142
+ const url = window.URL.createObjectURL(blob);
143
+ const link = document.createElement("a");
144
+ link.href = url;
145
+ link.download = `session-manager-license-${licenseKey.substring(0, 8)}.txt`;
146
+ document.body.appendChild(link);
147
+ link.click();
148
+ document.body.removeChild(link);
149
+ window.URL.revokeObjectURL(url);
150
+ toggleNotification({
151
+ type: "success",
152
+ message: "License key downloaded successfully!"
153
+ });
154
+ } catch (err) {
155
+ toggleNotification({
156
+ type: "danger",
157
+ message: "Failed to download license key"
158
+ });
159
+ }
160
+ };
161
+ useEffect(() => {
162
+ fetchLicenseStatus();
163
+ }, []);
164
+ if (loading) {
165
+ return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsx(LoaderContainer, { children: /* @__PURE__ */ jsx(Loader, { children: "Loading license information..." }) }) });
166
+ }
167
+ if (error) {
168
+ return /* @__PURE__ */ jsx(Container, { children: /* @__PURE__ */ jsx(Box, { padding: 8, children: /* @__PURE__ */ jsx(Alert, { variant: "danger", title: "Error", closeLabel: "Close", children: error }) }) });
169
+ }
170
+ const isValid = licenseData?.valid;
171
+ const isDemo = licenseData?.demo;
172
+ const data = licenseData?.data || {};
173
+ return /* @__PURE__ */ jsxs(Container, { children: [
174
+ /* @__PURE__ */ jsx(StickySaveBar, { paddingTop: 5, paddingBottom: 5, paddingLeft: 6, paddingRight: 6, children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
175
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 1, children: [
176
+ /* @__PURE__ */ jsx(Typography, { variant: "alpha", fontWeight: "bold", children: "License Management" }),
177
+ /* @__PURE__ */ jsx(Typography, { variant: "epsilon", textColor: "neutral600", children: "View your Session Manager plugin license" })
178
+ ] }),
179
+ /* @__PURE__ */ jsx(
180
+ Button,
181
+ {
182
+ startIcon: /* @__PURE__ */ jsx(ArrowClockwise, {}),
183
+ onClick: fetchLicenseStatus,
184
+ size: "L",
185
+ style: {
186
+ background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
187
+ color: "white",
188
+ fontWeight: "600",
189
+ border: "none"
190
+ },
191
+ children: "Refresh Status"
192
+ }
193
+ )
194
+ ] }) }),
195
+ /* @__PURE__ */ jsxs(Box, { paddingTop: 6, paddingLeft: 6, paddingRight: 6, paddingBottom: 10, children: [
196
+ isDemo ? /* @__PURE__ */ jsx(Alert, { variant: "warning", title: "Demo Mode", closeLabel: "Close", children: "You're using the demo version. Create a license to unlock all features." }) : isValid ? /* @__PURE__ */ jsx(Alert, { variant: "success", title: "License Active", closeLabel: "Close", children: "Your license is active and all features are unlocked." }) : /* @__PURE__ */ jsx(Alert, { variant: "danger", title: "License Issue", closeLabel: "Close", children: "There's an issue with your license. Please check your license status." }),
197
+ data.licenseKey && /* @__PURE__ */ jsx(Box, { marginTop: 6, children: /* @__PURE__ */ jsx(LicenseKeyBanner, { children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "flex-start", children: [
198
+ /* @__PURE__ */ jsxs(Box, { style: { flex: 1 }, children: [
199
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", style: { color: "rgba(255,255,255,0.8)", marginBottom: "12px", textTransform: "uppercase", fontSize: "11px", letterSpacing: "0.5px", display: "block" }, children: "License Key" }),
200
+ /* @__PURE__ */ jsx(Typography, { style: { color: "white", fontFamily: "monospace", fontSize: "28px", fontWeight: "bold", wordBreak: "break-all", marginBottom: "16px" }, children: data.licenseKey }),
201
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
202
+ /* @__PURE__ */ jsx(
203
+ Button,
204
+ {
205
+ onClick: handleCopyLicenseKey,
206
+ startIcon: /* @__PURE__ */ jsx(Duplicate, {}),
207
+ size: "S",
208
+ variant: "secondary",
209
+ style: {
210
+ backgroundColor: "rgba(255,255,255,0.2)",
211
+ color: "white",
212
+ border: "1px solid rgba(255,255,255,0.3)",
213
+ fontWeight: "600"
214
+ },
215
+ children: "Copy Key"
216
+ }
217
+ ),
218
+ /* @__PURE__ */ jsx(
219
+ Button,
220
+ {
221
+ onClick: handleDownloadLicenseKey,
222
+ startIcon: /* @__PURE__ */ jsx(Download, {}),
223
+ size: "S",
224
+ variant: "secondary",
225
+ style: {
226
+ backgroundColor: "rgba(255,255,255,0.2)",
227
+ color: "white",
228
+ border: "1px solid rgba(255,255,255,0.3)",
229
+ fontWeight: "600"
230
+ },
231
+ children: "Download as TXT"
232
+ }
233
+ )
234
+ ] })
235
+ ] }),
236
+ /* @__PURE__ */ jsx(
237
+ Badge,
238
+ {
239
+ backgroundColor: data.isActive ? "success100" : "danger100",
240
+ textColor: data.isActive ? "success700" : "danger700",
241
+ style: { fontSize: "11px", fontWeight: "700", padding: "6px 12px", marginLeft: "16px", flexShrink: 0 },
242
+ children: data.isActive ? "ACTIVE" : "INACTIVE"
243
+ }
244
+ )
245
+ ] }) }) }),
246
+ /* @__PURE__ */ jsx(Box, { marginTop: 6, children: /* @__PURE__ */ jsxs(Accordion.Root, { defaultValue: "account", collapsible: true, children: [
247
+ /* @__PURE__ */ jsxs(Accordion.Item, { value: "account", children: [
248
+ /* @__PURE__ */ jsx(Accordion.Header, { children: /* @__PURE__ */ jsx(Accordion.Trigger, { icon: User, children: "Account Information" }) }),
249
+ /* @__PURE__ */ jsx(Accordion.Content, { children: /* @__PURE__ */ jsx(Box, { padding: 6, children: /* @__PURE__ */ jsxs(Flex, { gap: 8, wrap: "wrap", children: [
250
+ /* @__PURE__ */ jsxs(Box, { style: { flex: "1", minWidth: "200px" }, children: [
251
+ /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", textTransform: "uppercase", style: { marginBottom: "8px", display: "block" }, children: "Email Address" }),
252
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "semiBold", children: data.email || "Not provided" })
253
+ ] }),
254
+ /* @__PURE__ */ jsxs(Box, { style: { flex: "1", minWidth: "200px" }, children: [
255
+ /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", textTransform: "uppercase", style: { marginBottom: "8px", display: "block" }, children: "License Holder" }),
256
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "semiBold", children: data.firstName && data.lastName ? `${data.firstName} ${data.lastName}` : "Not specified" })
257
+ ] })
258
+ ] }) }) })
259
+ ] }),
260
+ /* @__PURE__ */ jsxs(Accordion.Item, { value: "details", children: [
261
+ /* @__PURE__ */ jsx(Accordion.Header, { children: /* @__PURE__ */ jsx(Accordion.Trigger, { icon: Shield, children: "License Details" }) }),
262
+ /* @__PURE__ */ jsx(Accordion.Content, { children: /* @__PURE__ */ jsx(Box, { padding: 6, children: /* @__PURE__ */ jsxs(Flex, { gap: 8, wrap: "wrap", children: [
263
+ /* @__PURE__ */ jsxs(Box, { style: { flex: "1", minWidth: "180px" }, children: [
264
+ /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", textTransform: "uppercase", style: { marginBottom: "8px", display: "block" }, children: data.isExpired ? "Expired On" : "Expires On" }),
265
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "semiBold", children: data.expiresAt ? new Date(data.expiresAt).toLocaleDateString("en-US", {
266
+ year: "numeric",
267
+ month: "long",
268
+ day: "numeric"
269
+ }) : "Never" })
270
+ ] }),
271
+ /* @__PURE__ */ jsxs(Box, { style: { flex: "1", minWidth: "180px" }, children: [
272
+ /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", textTransform: "uppercase", style: { marginBottom: "8px", display: "block" }, children: "Device Name" }),
273
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "semiBold", children: data.deviceName || "Unknown" })
274
+ ] }),
275
+ /* @__PURE__ */ jsxs(Box, { style: { flex: "1", minWidth: "180px" }, children: [
276
+ /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", textTransform: "uppercase", style: { marginBottom: "8px", display: "block" }, children: "IP Address" }),
277
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "semiBold", children: data.ipAddress || "Not detected" })
278
+ ] })
279
+ ] }) }) })
280
+ ] }),
281
+ /* @__PURE__ */ jsxs(Accordion.Item, { value: "features", children: [
282
+ /* @__PURE__ */ jsx(Accordion.Header, { children: /* @__PURE__ */ jsx(Accordion.Trigger, { icon: Sparkle, children: "Features & Capabilities" }) }),
283
+ /* @__PURE__ */ jsx(Accordion.Content, { children: /* @__PURE__ */ jsxs(Box, { padding: 6, children: [
284
+ /* @__PURE__ */ jsxs(Flex, { gap: 3, style: { marginBottom: "32px" }, children: [
285
+ /* @__PURE__ */ jsxs(
286
+ Badge,
287
+ {
288
+ backgroundColor: data.features?.premium ? "success100" : "neutral100",
289
+ textColor: data.features?.premium ? "success700" : "neutral600",
290
+ style: {
291
+ fontSize: "13px",
292
+ fontWeight: "700",
293
+ padding: "8px 16px",
294
+ border: data.features?.premium ? "2px solid #dcfce7" : "2px solid #e5e7eb"
295
+ },
296
+ children: [
297
+ data.features?.premium ? "✓" : "✗",
298
+ " PREMIUM FEATURES"
299
+ ]
300
+ }
301
+ ),
302
+ /* @__PURE__ */ jsxs(
303
+ Badge,
304
+ {
305
+ backgroundColor: data.features?.advanced ? "primary100" : "neutral100",
306
+ textColor: data.features?.advanced ? "primary700" : "neutral600",
307
+ style: {
308
+ fontSize: "13px",
309
+ fontWeight: "700",
310
+ padding: "8px 16px",
311
+ border: data.features?.advanced ? "2px solid #bae6fd" : "2px solid #e5e7eb"
312
+ },
313
+ children: [
314
+ data.features?.advanced ? "✓" : "✗",
315
+ " ADVANCED FEATURES"
316
+ ]
317
+ }
318
+ ),
319
+ /* @__PURE__ */ jsxs(
320
+ Badge,
321
+ {
322
+ backgroundColor: data.features?.enterprise ? "secondary100" : "neutral100",
323
+ textColor: data.features?.enterprise ? "secondary700" : "neutral600",
324
+ style: {
325
+ fontSize: "13px",
326
+ fontWeight: "700",
327
+ padding: "8px 16px",
328
+ border: data.features?.enterprise ? "2px solid #ddd6fe" : "2px solid #e5e7eb"
329
+ },
330
+ children: [
331
+ data.features?.enterprise ? "✓" : "✗",
332
+ " ENTERPRISE FEATURES"
333
+ ]
334
+ }
335
+ )
336
+ ] }),
337
+ data.features?.premium && /* @__PURE__ */ jsxs(Box, { marginBottom: 5, padding: 5, background: "success50", hasRadius: true, style: { border: "2px solid #dcfce7" }, children: [
338
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "bold", textColor: "success700", style: { marginBottom: "16px", display: "flex", alignItems: "center", gap: "8px" }, children: "✨ Premium Features Active" }),
339
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
340
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "success700", style: { fontSize: "14px", display: "flex", alignItems: "center", gap: "8px" }, children: "✓ IP Geolocation Tracking (Country, City, Timezone)" }),
341
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "success700", style: { fontSize: "14px", display: "flex", alignItems: "center", gap: "8px" }, children: "✓ Security Risk Scoring (0-100)" }),
342
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "success700", style: { fontSize: "14px", display: "flex", alignItems: "center", gap: "8px" }, children: "✓ VPN Detection" }),
343
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "success700", style: { fontSize: "14px", display: "flex", alignItems: "center", gap: "8px" }, children: "✓ Proxy Detection" }),
344
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "success700", style: { fontSize: "14px", display: "flex", alignItems: "center", gap: "8px" }, children: "✓ Threat Analysis" }),
345
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "success700", style: { fontSize: "14px", display: "flex", alignItems: "center", gap: "8px" }, children: "✓ Advanced Session Analytics" })
346
+ ] })
347
+ ] }),
348
+ data.features?.advanced && /* @__PURE__ */ jsxs(Box, { marginBottom: 5, padding: 5, background: "primary50", hasRadius: true, style: { border: "2px solid #bae6fd" }, children: [
349
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "bold", textColor: "primary700", style: { marginBottom: "16px", display: "flex", alignItems: "center", gap: "8px" }, children: "🚀 Advanced Features Active" }),
350
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
351
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "primary700", style: { fontSize: "14px", display: "flex", alignItems: "center", gap: "8px" }, children: "✓ Email Notifications & Alerts" }),
352
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "primary700", style: { fontSize: "14px", display: "flex", alignItems: "center", gap: "8px" }, children: "✓ Webhook Integration (Discord/Slack)" }),
353
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "primary700", style: { fontSize: "14px", display: "flex", alignItems: "center", gap: "8px" }, children: "✓ Auto-blocking Suspicious Sessions" }),
354
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "primary700", style: { fontSize: "14px", display: "flex", alignItems: "center", gap: "8px" }, children: "✓ Geo-fencing (Country-based Access Control)" }),
355
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "primary700", style: { fontSize: "14px", display: "flex", alignItems: "center", gap: "8px" }, children: "✓ Enhanced Analytics Dashboard" })
356
+ ] })
357
+ ] }),
358
+ data.features?.enterprise && /* @__PURE__ */ jsxs(Box, { padding: 5, background: "secondary50", hasRadius: true, style: { border: "2px solid #ddd6fe" }, children: [
359
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "bold", textColor: "secondary700", style: { marginBottom: "16px", display: "flex", alignItems: "center", gap: "8px" }, children: "🏢 Enterprise Features Active" }),
360
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
361
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "secondary700", style: { fontSize: "14px", display: "flex", alignItems: "center", gap: "8px" }, children: "✓ Multi-tenant Support" }),
362
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "secondary700", style: { fontSize: "14px", display: "flex", alignItems: "center", gap: "8px" }, children: "✓ Compliance Reports (GDPR, SOC2)" }),
363
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "secondary700", style: { fontSize: "14px", display: "flex", alignItems: "center", gap: "8px" }, children: "✓ Custom Security Rules Engine" }),
364
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "secondary700", style: { fontSize: "14px", display: "flex", alignItems: "center", gap: "8px" }, children: "✓ ML-based Anomaly Detection" }),
365
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "secondary700", style: { fontSize: "14px", display: "flex", alignItems: "center", gap: "8px" }, children: "✓ Priority Support" })
366
+ ] })
367
+ ] })
368
+ ] }) })
369
+ ] }),
370
+ /* @__PURE__ */ jsxs(Accordion.Item, { value: "status", children: [
371
+ /* @__PURE__ */ jsx(Accordion.Header, { children: /* @__PURE__ */ jsx(Accordion.Trigger, { icon: ChartBubble, children: "System Status" }) }),
372
+ /* @__PURE__ */ jsx(Accordion.Content, { children: /* @__PURE__ */ jsx(Box, { padding: 6, children: /* @__PURE__ */ jsxs(Flex, { gap: 8, wrap: "wrap", children: [
373
+ /* @__PURE__ */ jsxs(Box, { style: { flex: "1", minWidth: "150px" }, children: [
374
+ /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", textTransform: "uppercase", style: { marginBottom: "8px", display: "block" }, children: "License Status" }),
375
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "semiBold", children: data.isActive ? "Active" : "Inactive" })
376
+ ] }),
377
+ /* @__PURE__ */ jsxs(Box, { style: { flex: "1", minWidth: "150px" }, children: [
378
+ /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", textTransform: "uppercase", style: { marginBottom: "8px", display: "block" }, children: "Connection" }),
379
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "semiBold", children: data.isOnline ? "Online" : "Offline" })
380
+ ] }),
381
+ /* @__PURE__ */ jsxs(Box, { style: { flex: "1", minWidth: "150px" }, children: [
382
+ /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", textTransform: "uppercase", style: { marginBottom: "8px", display: "block" }, children: "Last Sync" }),
383
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "semiBold", children: data.lastPingAt ? new Date(data.lastPingAt).toLocaleTimeString() : "Never" })
384
+ ] }),
385
+ /* @__PURE__ */ jsxs(Box, { style: { flex: "1", minWidth: "150px" }, children: [
386
+ /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", textTransform: "uppercase", style: { marginBottom: "8px", display: "block" }, children: "Device Limit" }),
387
+ /* @__PURE__ */ jsxs(Typography, { variant: "omega", fontWeight: "semiBold", children: [
388
+ data.currentDevices || 0,
389
+ " / ",
390
+ data.maxDevices || 1
391
+ ] })
392
+ ] })
393
+ ] }) }) })
394
+ ] })
395
+ ] }) })
396
+ ] })
397
+ ] });
398
+ };
399
+ export {
400
+ LicensePage as default
401
+ };
@@ -0,0 +1,174 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useState, useCallback, useEffect } from "react";
3
+ import { Box, Typography, Flex, Grid } from "@strapi/design-system";
4
+ import { Check, Cross, Clock, User } from "@strapi/icons";
5
+ import { useFetchClient } from "@strapi/strapi/admin";
6
+ const OnlineUsersWidget = () => {
7
+ const { get } = useFetchClient();
8
+ const [stats, setStats] = useState({
9
+ onlineNow: 0,
10
+ offline: 0,
11
+ last15min: 0,
12
+ last30min: 0,
13
+ totalUsers: 0,
14
+ blocked: 0
15
+ });
16
+ const [loading, setLoading] = useState(true);
17
+ const fetchStats = useCallback(async () => {
18
+ try {
19
+ const { data } = await get("/magic-sessionmanager/sessions");
20
+ const sessions = data.data || [];
21
+ const now = Date.now();
22
+ const fifteenMin = 15 * 60 * 1e3;
23
+ const thirtyMin = 30 * 60 * 1e3;
24
+ const onlineNow = /* @__PURE__ */ new Set();
25
+ const last15min = /* @__PURE__ */ new Set();
26
+ const last30min = /* @__PURE__ */ new Set();
27
+ sessions.forEach((session) => {
28
+ const userId = session.user?.id;
29
+ if (!userId) return;
30
+ const lastActive = session.lastActive ? new Date(session.lastActive) : new Date(session.loginTime);
31
+ const timeSinceActive = now - lastActive.getTime();
32
+ if (timeSinceActive < fifteenMin) {
33
+ onlineNow.add(userId);
34
+ last15min.add(userId);
35
+ last30min.add(userId);
36
+ } else if (timeSinceActive < thirtyMin) {
37
+ last15min.add(userId);
38
+ last30min.add(userId);
39
+ }
40
+ });
41
+ try {
42
+ const { data: usersData } = await get("/content-manager/collection-types/plugin::users-permissions.user?pageSize=1");
43
+ const totalUsers = usersData?.pagination?.total || 0;
44
+ const { data: blockedData } = await get("/content-manager/collection-types/plugin::users-permissions.user?filters[$and][0][blocked][$eq]=true&pageSize=1");
45
+ const blockedUsers = blockedData?.pagination?.total || 0;
46
+ setStats({
47
+ onlineNow: onlineNow.size,
48
+ last15min: last15min.size,
49
+ last30min: last30min.size,
50
+ offline: totalUsers - onlineNow.size,
51
+ totalUsers,
52
+ blocked: blockedUsers
53
+ });
54
+ } catch (err) {
55
+ console.error("[OnlineUsersWidget] Error fetching user count:", err);
56
+ setStats({
57
+ onlineNow: onlineNow.size,
58
+ last15min: last15min.size,
59
+ last30min: last30min.size,
60
+ offline: 0,
61
+ totalUsers: onlineNow.size,
62
+ blocked: 0
63
+ });
64
+ }
65
+ } catch (err) {
66
+ console.error("[OnlineUsersWidget] Error:", err);
67
+ } finally {
68
+ setLoading(false);
69
+ }
70
+ }, [get]);
71
+ useEffect(() => {
72
+ fetchStats();
73
+ const interval = setInterval(fetchStats, 3e4);
74
+ return () => clearInterval(interval);
75
+ }, [fetchStats]);
76
+ const StatCard = ({ icon: Icon, label, value, color }) => /* @__PURE__ */ jsx(
77
+ Box,
78
+ {
79
+ as: "a",
80
+ padding: 4,
81
+ background: "neutral0",
82
+ hasRadius: true,
83
+ shadow: "tableShadow",
84
+ style: {
85
+ textDecoration: "none",
86
+ cursor: "default",
87
+ transition: "box-shadow 0.2s",
88
+ border: "1px solid #f0f0ff"
89
+ },
90
+ children: /* @__PURE__ */ jsx(Flex, { justifyContent: "space-between", alignItems: "flex-start", children: /* @__PURE__ */ jsxs(Flex, { gap: 3, alignItems: "center", children: [
91
+ /* @__PURE__ */ jsx(
92
+ Box,
93
+ {
94
+ padding: 2,
95
+ background: `${color}100`,
96
+ hasRadius: true,
97
+ style: {
98
+ display: "flex",
99
+ alignItems: "center",
100
+ justifyContent: "center"
101
+ },
102
+ children: /* @__PURE__ */ jsx(Icon, { width: "16px", height: "16px", fill: `${color}600` })
103
+ }
104
+ ),
105
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 1, alignItems: "flex-start", children: [
106
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: label }),
107
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "bold", textColor: "neutral800", children: value })
108
+ ] })
109
+ ] }) })
110
+ }
111
+ );
112
+ if (loading) {
113
+ return /* @__PURE__ */ jsx(Box, { padding: 4, children: /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Loading..." }) });
114
+ }
115
+ return /* @__PURE__ */ jsx(Box, { padding: 0, children: /* @__PURE__ */ jsx(Flex, { direction: "column", gap: 3, children: /* @__PURE__ */ jsxs(Grid.Root, { gap: 3, gridCols: 2, children: [
116
+ /* @__PURE__ */ jsx(Grid.Item, { col: 1, children: /* @__PURE__ */ jsx(
117
+ StatCard,
118
+ {
119
+ icon: Check,
120
+ label: "Online Now",
121
+ value: stats.onlineNow,
122
+ color: "success"
123
+ }
124
+ ) }),
125
+ /* @__PURE__ */ jsx(Grid.Item, { col: 1, children: /* @__PURE__ */ jsx(
126
+ StatCard,
127
+ {
128
+ icon: Cross,
129
+ label: "Offline",
130
+ value: stats.offline,
131
+ color: "neutral"
132
+ }
133
+ ) }),
134
+ /* @__PURE__ */ jsx(Grid.Item, { col: 1, children: /* @__PURE__ */ jsx(
135
+ StatCard,
136
+ {
137
+ icon: Clock,
138
+ label: "Last 15 min",
139
+ value: stats.last15min,
140
+ color: "primary"
141
+ }
142
+ ) }),
143
+ /* @__PURE__ */ jsx(Grid.Item, { col: 1, children: /* @__PURE__ */ jsx(
144
+ StatCard,
145
+ {
146
+ icon: Clock,
147
+ label: "Last 30 min",
148
+ value: stats.last30min,
149
+ color: "secondary"
150
+ }
151
+ ) }),
152
+ /* @__PURE__ */ jsx(Grid.Item, { col: 1, children: /* @__PURE__ */ jsx(
153
+ StatCard,
154
+ {
155
+ icon: User,
156
+ label: "Total Users",
157
+ value: stats.totalUsers,
158
+ color: "neutral"
159
+ }
160
+ ) }),
161
+ /* @__PURE__ */ jsx(Grid.Item, { col: 1, children: /* @__PURE__ */ jsx(
162
+ StatCard,
163
+ {
164
+ icon: Cross,
165
+ label: "Blocked",
166
+ value: stats.blocked,
167
+ color: "danger"
168
+ }
169
+ ) })
170
+ ] }) }) });
171
+ };
172
+ export {
173
+ OnlineUsersWidget as default
174
+ };