strapi-plugin-magic-sessionmanager 4.2.4 → 4.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/server/index.js +1 -1
  2. package/dist/server/index.mjs +1 -1
  3. package/package.json +1 -3
  4. package/admin/jsconfig.json +0 -10
  5. package/admin/src/components/Initializer.jsx +0 -11
  6. package/admin/src/components/LicenseGuard.jsx +0 -591
  7. package/admin/src/components/OnlineUsersWidget.jsx +0 -212
  8. package/admin/src/components/PluginIcon.jsx +0 -8
  9. package/admin/src/components/SessionDetailModal.jsx +0 -449
  10. package/admin/src/components/SessionInfoCard.jsx +0 -151
  11. package/admin/src/components/SessionInfoPanel.jsx +0 -385
  12. package/admin/src/components/index.jsx +0 -5
  13. package/admin/src/hooks/useLicense.js +0 -103
  14. package/admin/src/index.js +0 -149
  15. package/admin/src/pages/ActiveSessions.jsx +0 -12
  16. package/admin/src/pages/Analytics.jsx +0 -735
  17. package/admin/src/pages/App.jsx +0 -12
  18. package/admin/src/pages/HomePage.jsx +0 -1212
  19. package/admin/src/pages/License.jsx +0 -603
  20. package/admin/src/pages/Settings.jsx +0 -1646
  21. package/admin/src/pages/SettingsNew.jsx +0 -1204
  22. package/admin/src/pages/UpgradePage.jsx +0 -448
  23. package/admin/src/pages/index.jsx +0 -3
  24. package/admin/src/pluginId.js +0 -4
  25. package/admin/src/translations/de.json +0 -299
  26. package/admin/src/translations/en.json +0 -299
  27. package/admin/src/translations/es.json +0 -287
  28. package/admin/src/translations/fr.json +0 -287
  29. package/admin/src/translations/pt.json +0 -287
  30. package/admin/src/utils/getTranslation.js +0 -5
  31. package/admin/src/utils/index.js +0 -2
  32. package/admin/src/utils/parseUserAgent.js +0 -79
  33. package/admin/src/utils/theme.js +0 -85
  34. package/server/jsconfig.json +0 -10
  35. package/server/src/bootstrap.js +0 -492
  36. package/server/src/config/index.js +0 -23
  37. package/server/src/content-types/index.js +0 -9
  38. package/server/src/content-types/session/schema.json +0 -84
  39. package/server/src/controllers/controller.js +0 -11
  40. package/server/src/controllers/index.js +0 -11
  41. package/server/src/controllers/license.js +0 -266
  42. package/server/src/controllers/session.js +0 -433
  43. package/server/src/controllers/settings.js +0 -122
  44. package/server/src/destroy.js +0 -22
  45. package/server/src/index.js +0 -23
  46. package/server/src/middlewares/index.js +0 -5
  47. package/server/src/middlewares/last-seen.js +0 -62
  48. package/server/src/policies/index.js +0 -3
  49. package/server/src/register.js +0 -36
  50. package/server/src/routes/admin.js +0 -149
  51. package/server/src/routes/content-api.js +0 -60
  52. package/server/src/routes/index.js +0 -9
  53. package/server/src/services/geolocation.js +0 -182
  54. package/server/src/services/index.js +0 -13
  55. package/server/src/services/license-guard.js +0 -316
  56. package/server/src/services/notifications.js +0 -319
  57. package/server/src/services/service.js +0 -7
  58. package/server/src/services/session.js +0 -393
  59. package/server/src/utils/encryption.js +0 -121
  60. package/server/src/utils/getClientIp.js +0 -118
  61. package/server/src/utils/logger.js +0 -84
@@ -1,603 +0,0 @@
1
- import { useState, useEffect } from 'react';
2
- import {
3
- Box,
4
- Typography,
5
- Badge,
6
- Flex,
7
- Alert,
8
- Button,
9
- Loader,
10
- Accordion,
11
- } from '@strapi/design-system';
12
- import { useFetchClient, useNotification } from '@strapi/strapi/admin';
13
- import {
14
- ArrowClockwise,
15
- Key,
16
- User,
17
- Shield,
18
- Sparkle,
19
- ChartBubble,
20
- Duplicate,
21
- Download,
22
- } from '@strapi/icons';
23
- import styled, { keyframes, css } from 'styled-components';
24
- import pluginId from '../pluginId';
25
-
26
- // ================ THEME ================
27
- const theme = {
28
- colors: {
29
- primary: { 600: '#0284C7', 100: '#E0F2FE', 50: '#F0F9FF' },
30
- success: { 600: '#16A34A', 50: '#DCFCE7' },
31
- warning: { 50: '#FEF3C7' },
32
- danger: { 50: '#FEE2E2' },
33
- neutral: { 0: '#FFFFFF', 100: '#F3F4F6', 200: '#E5E7EB', 600: '#4B5563', 800: '#1F2937' }
34
- },
35
- shadows: { sm: '0 1px 3px rgba(0,0,0,0.1)' },
36
- borderRadius: { lg: '12px' }
37
- };
38
-
39
- // ================ ANIMATIONS ================
40
- const fadeIn = keyframes`
41
- from { opacity: 0; transform: translateY(10px); }
42
- to { opacity: 1; transform: translateY(0); }
43
- `;
44
-
45
- const shimmer = keyframes`
46
- 0% { background-position: -200% 0; }
47
- 100% { background-position: 200% 0; }
48
- `;
49
-
50
- // ================ STYLED COMPONENTS ================
51
- const Container = styled(Box)`
52
- ${css`animation: ${fadeIn} 0.5s;`}
53
- max-width: 1400px;
54
- margin: 0 auto;
55
- `;
56
-
57
- const StickySaveBar = styled(Box)`
58
- position: sticky;
59
- top: 0;
60
- z-index: 10;
61
- background: ${props => props.theme.colors.neutral0};
62
- border-bottom: 1px solid ${props => props.theme.colors.neutral200};
63
- box-shadow: 0 1px 3px rgba(0,0,0,0.1);
64
- `;
65
-
66
- const LicenseKeyBanner = styled(Box)`
67
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
68
- border-radius: ${theme.borderRadius.lg};
69
- padding: 28px 32px;
70
- color: white;
71
- position: relative;
72
- overflow: hidden;
73
- box-shadow: 0 4px 20px rgba(102, 126, 234, 0.25);
74
- margin-bottom: 24px;
75
-
76
- &::after {
77
- content: '';
78
- position: absolute;
79
- top: -50%;
80
- right: -50%;
81
- width: 200%;
82
- height: 200%;
83
- background: linear-gradient(
84
- 45deg,
85
- transparent,
86
- rgba(255, 255, 255, 0.08),
87
- transparent
88
- );
89
- ${css`animation: ${shimmer} 3s infinite;`}
90
- pointer-events: none;
91
- z-index: 0;
92
- }
93
-
94
- & > * {
95
- position: relative;
96
- z-index: 1;
97
- }
98
- `;
99
-
100
- const LoaderContainer = styled(Flex)`
101
- min-height: 400px;
102
- align-items: center;
103
- justify-content: center;
104
- flex-direction: column;
105
- gap: 16px;
106
- `;
107
-
108
- // ================ MAIN COMPONENT ================
109
- const LicensePage = () => {
110
- const { get } = useFetchClient();
111
- const { toggleNotification } = useNotification();
112
- const [loading, setLoading] = useState(true);
113
- const [licenseData, setLicenseData] = useState(null);
114
- const [error, setError] = useState(null);
115
-
116
- const fetchLicenseStatus = async () => {
117
- setLoading(true);
118
- setError(null);
119
-
120
- try {
121
- const response = await get(`/${pluginId}/license/status`);
122
- setLicenseData(response.data);
123
- } catch (err) {
124
- console.error('[magic-sessionmanager/License] Error fetching license:', err);
125
- setError('Failed to load license information');
126
- } finally {
127
- setLoading(false);
128
- }
129
- };
130
-
131
- const handleCopyLicenseKey = async () => {
132
- try {
133
- await navigator.clipboard.writeText(licenseData?.data?.licenseKey || '');
134
- toggleNotification({
135
- type: 'success',
136
- message: 'License key copied to clipboard!',
137
- });
138
- } catch (err) {
139
- toggleNotification({
140
- type: 'danger',
141
- message: 'Failed to copy license key',
142
- });
143
- }
144
- };
145
-
146
- const handleDownloadLicenseKey = () => {
147
- try {
148
- const data = licenseData?.data || {};
149
- const licenseKey = data.licenseKey || '';
150
- const email = data.email || 'N/A';
151
- const firstName = data.firstName || '';
152
- const lastName = data.lastName || '';
153
- const fullName = `${firstName} ${lastName}`.trim() || 'N/A';
154
-
155
- const content = `Magic Session Manager - License Key
156
- ═══════════════════════════════════════
157
-
158
- License Key: ${licenseKey}
159
-
160
- License Holder Information:
161
- ──────────────────────────────────────
162
- Name: ${fullName}
163
- Email: ${email}
164
-
165
- License Status:
166
- ──────────────────────────────────────
167
- Status: ${data.isActive ? 'ACTIVE' : 'INACTIVE'}
168
- Expires: ${data.expiresAt ? new Date(data.expiresAt).toLocaleDateString() : 'Never'}
169
-
170
- Features:
171
- ──────────────────────────────────────
172
- Premium: ${data.features?.premium ? 'Enabled' : 'Disabled'}
173
- Advanced: ${data.features?.advanced ? 'Enabled' : 'Disabled'}
174
- Enterprise: ${data.features?.enterprise ? 'Enabled' : 'Disabled'}
175
-
176
- ═══════════════════════════════════════
177
- Generated: ${new Date().toLocaleString()}
178
- `;
179
-
180
- const blob = new Blob([content], { type: 'text/plain' });
181
- const url = window.URL.createObjectURL(blob);
182
- const link = document.createElement('a');
183
- link.href = url;
184
- link.download = `session-manager-license-${licenseKey.substring(0, 8)}.txt`;
185
- document.body.appendChild(link);
186
- link.click();
187
- document.body.removeChild(link);
188
- window.URL.revokeObjectURL(url);
189
-
190
- toggleNotification({
191
- type: 'success',
192
- message: 'License key downloaded successfully!',
193
- });
194
- } catch (err) {
195
- toggleNotification({
196
- type: 'danger',
197
- message: 'Failed to download license key',
198
- });
199
- }
200
- };
201
-
202
- useEffect(() => {
203
- fetchLicenseStatus();
204
- }, []);
205
-
206
- if (loading) {
207
- return (
208
- <Container>
209
- <LoaderContainer>
210
- <Loader>Loading license information...</Loader>
211
- </LoaderContainer>
212
- </Container>
213
- );
214
- }
215
-
216
- if (error) {
217
- return (
218
- <Container>
219
- <Box padding={8}>
220
- <Alert variant="danger" title="Error" closeLabel="Close">
221
- {error}
222
- </Alert>
223
- </Box>
224
- </Container>
225
- );
226
- }
227
-
228
- const isValid = licenseData?.valid;
229
- const isDemo = licenseData?.demo;
230
- const data = licenseData?.data || {};
231
-
232
- return (
233
- <Container>
234
- {/* Sticky Header */}
235
- <StickySaveBar paddingTop={5} paddingBottom={5} paddingLeft={6} paddingRight={6}>
236
- <Flex justifyContent="space-between" alignItems="center">
237
- <Flex direction="column" gap={1}>
238
- <Typography variant="alpha" fontWeight="bold">
239
- License Management
240
- </Typography>
241
- <Typography variant="epsilon" textColor="neutral600">
242
- View your Session Manager plugin license
243
- </Typography>
244
- </Flex>
245
- <Button
246
- startIcon={<ArrowClockwise />}
247
- onClick={fetchLicenseStatus}
248
- size="L"
249
- style={{
250
- background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
251
- color: 'white',
252
- fontWeight: '600',
253
- border: 'none',
254
- }}
255
- >
256
- Refresh Status
257
- </Button>
258
- </Flex>
259
- </StickySaveBar>
260
-
261
- {/* Content */}
262
- <Box paddingTop={6} paddingLeft={6} paddingRight={6} paddingBottom={10}>
263
- {/* Status Alert */}
264
- {isDemo ? (
265
- <Alert variant="warning" title="Demo Mode" closeLabel="Close">
266
- You're using the demo version. Create a license to unlock all features.
267
- </Alert>
268
- ) : isValid ? (
269
- <Alert variant="success" title="License Active" closeLabel="Close">
270
- Your license is active and all features are unlocked.
271
- </Alert>
272
- ) : (
273
- <Alert variant="danger" title="License Issue" closeLabel="Close">
274
- There's an issue with your license. Please check your license status.
275
- </Alert>
276
- )}
277
-
278
- {/* License Key */}
279
- {data.licenseKey && (
280
- <Box marginTop={6}>
281
- <LicenseKeyBanner>
282
- <Flex justifyContent="space-between" alignItems="flex-start">
283
- <Box style={{ flex: 1 }}>
284
- <Typography variant="pi" style={{ color: 'rgba(255,255,255,0.8)', marginBottom: '12px', textTransform: 'uppercase', fontSize: '11px', letterSpacing: '0.5px', display: 'block' }}>
285
- License Key
286
- </Typography>
287
- <Typography style={{ color: 'white', fontFamily: 'monospace', fontSize: '28px', fontWeight: 'bold', wordBreak: 'break-all', marginBottom: '16px' }}>
288
- {data.licenseKey}
289
- </Typography>
290
- <Flex gap={2}>
291
- <Button
292
- onClick={handleCopyLicenseKey}
293
- startIcon={<Duplicate />}
294
- size="S"
295
- variant="secondary"
296
- style={{
297
- backgroundColor: 'rgba(255,255,255,0.2)',
298
- color: 'white',
299
- border: '1px solid rgba(255,255,255,0.3)',
300
- fontWeight: '600',
301
- }}
302
- >
303
- Copy Key
304
- </Button>
305
- <Button
306
- onClick={handleDownloadLicenseKey}
307
- startIcon={<Download />}
308
- size="S"
309
- variant="secondary"
310
- style={{
311
- backgroundColor: 'rgba(255,255,255,0.2)',
312
- color: 'white',
313
- border: '1px solid rgba(255,255,255,0.3)',
314
- fontWeight: '600',
315
- }}
316
- >
317
- Download as TXT
318
- </Button>
319
- </Flex>
320
- </Box>
321
- <Badge
322
- backgroundColor={data.isActive ? "success100" : "danger100"}
323
- textColor={data.isActive ? "success700" : "danger700"}
324
- style={{ fontSize: '11px', fontWeight: '700', padding: '6px 12px', marginLeft: '16px', flexShrink: 0 }}
325
- >
326
- {data.isActive ? 'ACTIVE' : 'INACTIVE'}
327
- </Badge>
328
- </Flex>
329
- </LicenseKeyBanner>
330
- </Box>
331
- )}
332
-
333
- {/* Details Section */}
334
- <Box marginTop={6}>
335
- <Accordion.Root defaultValue="account" collapsible>
336
- {/* Account Information */}
337
- <Accordion.Item value="account">
338
- <Accordion.Header>
339
- <Accordion.Trigger icon={User}>
340
- Account Information
341
- </Accordion.Trigger>
342
- </Accordion.Header>
343
- <Accordion.Content>
344
- <Box padding={6}>
345
- <Flex gap={8} wrap="wrap">
346
- <Box style={{ flex: '1', minWidth: '200px' }}>
347
- <Typography variant="sigma" textColor="neutral600" textTransform="uppercase" style={{ marginBottom: '8px', display: 'block' }}>
348
- Email Address
349
- </Typography>
350
- <Typography variant="omega" fontWeight="semiBold">
351
- {data.email || 'Not provided'}
352
- </Typography>
353
- </Box>
354
- <Box style={{ flex: '1', minWidth: '200px' }}>
355
- <Typography variant="sigma" textColor="neutral600" textTransform="uppercase" style={{ marginBottom: '8px', display: 'block' }}>
356
- License Holder
357
- </Typography>
358
- <Typography variant="omega" fontWeight="semiBold">
359
- {data.firstName && data.lastName
360
- ? `${data.firstName} ${data.lastName}`
361
- : 'Not specified'
362
- }
363
- </Typography>
364
- </Box>
365
- </Flex>
366
- </Box>
367
- </Accordion.Content>
368
- </Accordion.Item>
369
-
370
- {/* License Details */}
371
- <Accordion.Item value="details">
372
- <Accordion.Header>
373
- <Accordion.Trigger icon={Shield}>
374
- License Details
375
- </Accordion.Trigger>
376
- </Accordion.Header>
377
- <Accordion.Content>
378
- <Box padding={6}>
379
- <Flex gap={8} wrap="wrap">
380
- <Box style={{ flex: '1', minWidth: '180px' }}>
381
- <Typography variant="sigma" textColor="neutral600" textTransform="uppercase" style={{ marginBottom: '8px', display: 'block' }}>
382
- {data.isExpired ? 'Expired On' : 'Expires On'}
383
- </Typography>
384
- <Typography variant="omega" fontWeight="semiBold">
385
- {data.expiresAt
386
- ? new Date(data.expiresAt).toLocaleDateString('en-US', {
387
- year: 'numeric',
388
- month: 'long',
389
- day: 'numeric',
390
- })
391
- : 'Never'}
392
- </Typography>
393
- </Box>
394
- <Box style={{ flex: '1', minWidth: '180px' }}>
395
- <Typography variant="sigma" textColor="neutral600" textTransform="uppercase" style={{ marginBottom: '8px', display: 'block' }}>
396
- Device Name
397
- </Typography>
398
- <Typography variant="omega" fontWeight="semiBold">
399
- {data.deviceName || 'Unknown'}
400
- </Typography>
401
- </Box>
402
- <Box style={{ flex: '1', minWidth: '180px' }}>
403
- <Typography variant="sigma" textColor="neutral600" textTransform="uppercase" style={{ marginBottom: '8px', display: 'block' }}>
404
- IP Address
405
- </Typography>
406
- <Typography variant="omega" fontWeight="semiBold">
407
- {data.ipAddress || 'Not detected'}
408
- </Typography>
409
- </Box>
410
- </Flex>
411
- </Box>
412
- </Accordion.Content>
413
- </Accordion.Item>
414
-
415
- {/* Features - Beautiful Visual Display */}
416
- <Accordion.Item value="features">
417
- <Accordion.Header>
418
- <Accordion.Trigger icon={Sparkle}>
419
- Features & Capabilities
420
- </Accordion.Trigger>
421
- </Accordion.Header>
422
- <Accordion.Content>
423
- <Box padding={6}>
424
- {/* Feature Tier Badges */}
425
- <Flex gap={3} style={{ marginBottom: '32px' }}>
426
- <Badge
427
- backgroundColor={data.features?.premium ? "success100" : "neutral100"}
428
- textColor={data.features?.premium ? "success700" : "neutral600"}
429
- style={{
430
- fontSize: '13px',
431
- fontWeight: '700',
432
- padding: '8px 16px',
433
- border: data.features?.premium ? '2px solid #dcfce7' : '2px solid #e5e7eb'
434
- }}
435
- >
436
- {data.features?.premium ? '✓' : '✗'} PREMIUM FEATURES
437
- </Badge>
438
- <Badge
439
- backgroundColor={data.features?.advanced ? "primary100" : "neutral100"}
440
- textColor={data.features?.advanced ? "primary700" : "neutral600"}
441
- style={{
442
- fontSize: '13px',
443
- fontWeight: '700',
444
- padding: '8px 16px',
445
- border: data.features?.advanced ? '2px solid #bae6fd' : '2px solid #e5e7eb'
446
- }}
447
- >
448
- {data.features?.advanced ? '✓' : '✗'} ADVANCED FEATURES
449
- </Badge>
450
- <Badge
451
- backgroundColor={data.features?.enterprise ? "secondary100" : "neutral100"}
452
- textColor={data.features?.enterprise ? "secondary700" : "neutral600"}
453
- style={{
454
- fontSize: '13px',
455
- fontWeight: '700',
456
- padding: '8px 16px',
457
- border: data.features?.enterprise ? '2px solid #ddd6fe' : '2px solid #e5e7eb'
458
- }}
459
- >
460
- {data.features?.enterprise ? '✓' : '✗'} ENTERPRISE FEATURES
461
- </Badge>
462
- </Flex>
463
-
464
- {/* Premium Features List */}
465
- {data.features?.premium && (
466
- <Box marginBottom={5} padding={5} background="success50" hasRadius style={{ border: '2px solid #dcfce7' }}>
467
- <Typography variant="delta" fontWeight="bold" textColor="success700" style={{ marginBottom: '16px', display: 'flex', alignItems: 'center', gap: '8px' }}>
468
- Premium Features Active
469
- </Typography>
470
- <Flex direction="column" gap={2}>
471
- <Typography variant="omega" textColor="success700" style={{ fontSize: '14px', display: 'flex', alignItems: 'center', gap: '8px' }}>
472
- ✓ IP Geolocation Tracking (Country, City, Timezone)
473
- </Typography>
474
- <Typography variant="omega" textColor="success700" style={{ fontSize: '14px', display: 'flex', alignItems: 'center', gap: '8px' }}>
475
- ✓ Security Risk Scoring (0-100)
476
- </Typography>
477
- <Typography variant="omega" textColor="success700" style={{ fontSize: '14px', display: 'flex', alignItems: 'center', gap: '8px' }}>
478
- ✓ VPN Detection
479
- </Typography>
480
- <Typography variant="omega" textColor="success700" style={{ fontSize: '14px', display: 'flex', alignItems: 'center', gap: '8px' }}>
481
- ✓ Proxy Detection
482
- </Typography>
483
- <Typography variant="omega" textColor="success700" style={{ fontSize: '14px', display: 'flex', alignItems: 'center', gap: '8px' }}>
484
- ✓ Threat Analysis
485
- </Typography>
486
- <Typography variant="omega" textColor="success700" style={{ fontSize: '14px', display: 'flex', alignItems: 'center', gap: '8px' }}>
487
- ✓ Advanced Session Analytics
488
- </Typography>
489
- </Flex>
490
- </Box>
491
- )}
492
-
493
- {/* Advanced Features List */}
494
- {data.features?.advanced && (
495
- <Box marginBottom={5} padding={5} background="primary50" hasRadius style={{ border: '2px solid #bae6fd' }}>
496
- <Typography variant="delta" fontWeight="bold" textColor="primary700" style={{ marginBottom: '16px', display: 'flex', alignItems: 'center', gap: '8px' }}>
497
- Advanced Features Active
498
- </Typography>
499
- <Flex direction="column" gap={2}>
500
- <Typography variant="omega" textColor="primary700" style={{ fontSize: '14px', display: 'flex', alignItems: 'center', gap: '8px' }}>
501
- ✓ Email Notifications & Alerts
502
- </Typography>
503
- <Typography variant="omega" textColor="primary700" style={{ fontSize: '14px', display: 'flex', alignItems: 'center', gap: '8px' }}>
504
- ✓ Webhook Integration (Discord/Slack)
505
- </Typography>
506
- <Typography variant="omega" textColor="primary700" style={{ fontSize: '14px', display: 'flex', alignItems: 'center', gap: '8px' }}>
507
- ✓ Auto-blocking Suspicious Sessions
508
- </Typography>
509
- <Typography variant="omega" textColor="primary700" style={{ fontSize: '14px', display: 'flex', alignItems: 'center', gap: '8px' }}>
510
- ✓ Geo-fencing (Country-based Access Control)
511
- </Typography>
512
- <Typography variant="omega" textColor="primary700" style={{ fontSize: '14px', display: 'flex', alignItems: 'center', gap: '8px' }}>
513
- ✓ Enhanced Analytics Dashboard
514
- </Typography>
515
- </Flex>
516
- </Box>
517
- )}
518
-
519
- {/* Enterprise Features List */}
520
- {data.features?.enterprise && (
521
- <Box padding={5} background="secondary50" hasRadius style={{ border: '2px solid #ddd6fe' }}>
522
- <Typography variant="delta" fontWeight="bold" textColor="secondary700" style={{ marginBottom: '16px', display: 'flex', alignItems: 'center', gap: '8px' }}>
523
- 🏢 Enterprise Features Active
524
- </Typography>
525
- <Flex direction="column" gap={2}>
526
- <Typography variant="omega" textColor="secondary700" style={{ fontSize: '14px', display: 'flex', alignItems: 'center', gap: '8px' }}>
527
- ✓ Multi-tenant Support
528
- </Typography>
529
- <Typography variant="omega" textColor="secondary700" style={{ fontSize: '14px', display: 'flex', alignItems: 'center', gap: '8px' }}>
530
- ✓ Compliance Reports (GDPR, SOC2)
531
- </Typography>
532
- <Typography variant="omega" textColor="secondary700" style={{ fontSize: '14px', display: 'flex', alignItems: 'center', gap: '8px' }}>
533
- ✓ Custom Security Rules Engine
534
- </Typography>
535
- <Typography variant="omega" textColor="secondary700" style={{ fontSize: '14px', display: 'flex', alignItems: 'center', gap: '8px' }}>
536
- ✓ ML-based Anomaly Detection
537
- </Typography>
538
- <Typography variant="omega" textColor="secondary700" style={{ fontSize: '14px', display: 'flex', alignItems: 'center', gap: '8px' }}>
539
- ✓ Priority Support
540
- </Typography>
541
- </Flex>
542
- </Box>
543
- )}
544
- </Box>
545
- </Accordion.Content>
546
- </Accordion.Item>
547
-
548
- {/* System Status */}
549
- <Accordion.Item value="status">
550
- <Accordion.Header>
551
- <Accordion.Trigger icon={ChartBubble}>
552
- System Status
553
- </Accordion.Trigger>
554
- </Accordion.Header>
555
- <Accordion.Content>
556
- <Box padding={6}>
557
- <Flex gap={8} wrap="wrap">
558
- <Box style={{ flex: '1', minWidth: '150px' }}>
559
- <Typography variant="sigma" textColor="neutral600" textTransform="uppercase" style={{ marginBottom: '8px', display: 'block' }}>
560
- License Status
561
- </Typography>
562
- <Typography variant="omega" fontWeight="semiBold">
563
- {data.isActive ? 'Active' : 'Inactive'}
564
- </Typography>
565
- </Box>
566
- <Box style={{ flex: '1', minWidth: '150px' }}>
567
- <Typography variant="sigma" textColor="neutral600" textTransform="uppercase" style={{ marginBottom: '8px', display: 'block' }}>
568
- Connection
569
- </Typography>
570
- <Typography variant="omega" fontWeight="semiBold">
571
- {data.isOnline ? 'Online' : 'Offline'}
572
- </Typography>
573
- </Box>
574
- <Box style={{ flex: '1', minWidth: '150px' }}>
575
- <Typography variant="sigma" textColor="neutral600" textTransform="uppercase" style={{ marginBottom: '8px', display: 'block' }}>
576
- Last Sync
577
- </Typography>
578
- <Typography variant="omega" fontWeight="semiBold">
579
- {data.lastPingAt
580
- ? new Date(data.lastPingAt).toLocaleTimeString()
581
- : 'Never'}
582
- </Typography>
583
- </Box>
584
- <Box style={{ flex: '1', minWidth: '150px' }}>
585
- <Typography variant="sigma" textColor="neutral600" textTransform="uppercase" style={{ marginBottom: '8px', display: 'block' }}>
586
- Device Limit
587
- </Typography>
588
- <Typography variant="omega" fontWeight="semiBold">
589
- {data.currentDevices || 0} / {data.maxDevices || 1}
590
- </Typography>
591
- </Box>
592
- </Flex>
593
- </Box>
594
- </Accordion.Content>
595
- </Accordion.Item>
596
- </Accordion.Root>
597
- </Box>
598
- </Box>
599
- </Container>
600
- );
601
- };
602
-
603
- export default LicensePage;