strapi-plugin-magic-sessionmanager 4.0.0 → 4.0.2

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 (68) hide show
  1. package/README.md +2 -2
  2. package/admin/src/components/LicenseGuard.jsx +6 -6
  3. package/admin/src/components/OnlineUsersWidget.jsx +11 -7
  4. package/admin/src/components/SessionDetailModal.jsx +45 -41
  5. package/admin/src/components/SessionInfoCard.jsx +3 -3
  6. package/admin/src/components/SessionInfoPanel.jsx +31 -21
  7. package/admin/src/index.js +9 -0
  8. package/admin/src/pages/Analytics.jsx +2 -2
  9. package/admin/src/pages/HomePage.jsx +129 -165
  10. package/admin/src/pages/License.jsx +5 -5
  11. package/admin/src/pages/Settings.jsx +148 -144
  12. package/admin/src/pages/SettingsNew.jsx +21 -21
  13. package/admin/src/pages/UpgradePage.jsx +448 -0
  14. package/admin/src/pluginId.js +1 -0
  15. package/admin/src/translations/de.json +294 -15
  16. package/admin/src/translations/en.json +293 -14
  17. package/admin/src/translations/es.json +284 -18
  18. package/admin/src/translations/fr.json +284 -18
  19. package/admin/src/translations/pt.json +284 -18
  20. package/admin/src/utils/parseUserAgent.js +6 -6
  21. package/admin/src/utils/theme.js +85 -0
  22. package/dist/_chunks/{Analytics-mYu_uGwU.mjs → Analytics-DTE_zmRV.mjs} +4 -4
  23. package/dist/_chunks/{Analytics-ioaeEh-E.js → Analytics-lw_JaOVy.js} +4 -4
  24. package/dist/_chunks/{App-DdnUYWbC.js → App-DDKYCjKw.js} +221 -216
  25. package/dist/_chunks/{App-BXpIS12l.mjs → App-DJW1ZNl5.mjs} +221 -216
  26. package/dist/_chunks/{License-C03C2j9P.mjs → License-DaOFuImm.mjs} +6 -10
  27. package/dist/_chunks/{License-DZYrOgcx.js → License-Tk-6UfPl.js} +6 -10
  28. package/dist/_chunks/{OnlineUsersWidget-B8JS1xZu.js → OnlineUsersWidget-C1qTpsws.js} +11 -7
  29. package/dist/_chunks/{OnlineUsersWidget-ArMl0nen.mjs → OnlineUsersWidget-CADphbXG.mjs} +11 -7
  30. package/dist/_chunks/{Settings-0ocB3qHk.mjs → Settings-C9xvckgq.mjs} +200 -188
  31. package/dist/_chunks/{Settings-C6_CqpCC.js → Settings-DyEAuTNQ.js} +200 -188
  32. package/dist/_chunks/UpgradePage-Dssk8A0Z.js +354 -0
  33. package/dist/_chunks/UpgradePage-cINvE9zY.mjs +352 -0
  34. package/dist/_chunks/de-CDA1V0rF.mjs +292 -0
  35. package/dist/_chunks/de-I-Q-pWqu.js +292 -0
  36. package/dist/_chunks/en-Bd7_h-4e.js +292 -0
  37. package/dist/_chunks/en-DzmOCyzQ.mjs +292 -0
  38. package/dist/_chunks/es-BcAx18XG.js +277 -0
  39. package/dist/_chunks/es-Cx-SN6qV.mjs +277 -0
  40. package/dist/_chunks/fr-DCzYMuJ-.js +277 -0
  41. package/dist/_chunks/fr-DXlXE5Eo.mjs +277 -0
  42. package/dist/_chunks/{index-DC8Y0qxx.js → index-CWcvrfXc.js} +52 -49
  43. package/dist/_chunks/{index-DBRS3kt5.mjs → index-DQO9bNP7.mjs} +52 -49
  44. package/dist/_chunks/pt-21-MAb72.js +277 -0
  45. package/dist/_chunks/pt-zsdTSjba.mjs +277 -0
  46. package/dist/_chunks/{useLicense-qgGfMvse.js → useLicense-DtvJOszr.js} +1 -1
  47. package/dist/_chunks/{useLicense-DSLL9n3Y.mjs → useLicense-DxbD4Wf8.mjs} +1 -1
  48. package/dist/admin/index.js +1 -1
  49. package/dist/admin/index.mjs +1 -1
  50. package/dist/server/index.js +142 -33
  51. package/dist/server/index.mjs +142 -33
  52. package/package.json +1 -1
  53. package/server/src/bootstrap.js +76 -4
  54. package/server/src/controllers/session.js +59 -9
  55. package/server/src/middlewares/last-seen.js +5 -4
  56. package/server/src/routes/content-api.js +11 -2
  57. package/server/src/services/notifications.js +10 -10
  58. package/server/src/services/session.js +24 -4
  59. package/dist/_chunks/de-BxFx1pwE.js +0 -23
  60. package/dist/_chunks/de-CdO3s01z.mjs +0 -23
  61. package/dist/_chunks/en-CsPpPJL3.mjs +0 -23
  62. package/dist/_chunks/en-RqmpDHdS.js +0 -23
  63. package/dist/_chunks/es-CuLHazN1.js +0 -23
  64. package/dist/_chunks/es-Dkmjhy9c.mjs +0 -23
  65. package/dist/_chunks/fr-BAJp2yhI.js +0 -23
  66. package/dist/_chunks/fr-Bssg_3UF.mjs +0 -23
  67. package/dist/_chunks/pt-BAP9cKs3.js +0 -23
  68. package/dist/_chunks/pt-BVNoNcuY.mjs +0 -23
@@ -1,6 +1,9 @@
1
1
  import { useState, useEffect } from 'react';
2
+ import { useIntl } from 'react-intl';
2
3
  import { useFetchClient, useNotification } from '@strapi/strapi/admin';
3
4
  import styled, { keyframes, css } from 'styled-components';
5
+ import { getTranslation } from '../utils/getTranslation';
6
+ import { theme } from '../utils/theme';
4
7
  import {
5
8
  Box,
6
9
  Button,
@@ -36,72 +39,6 @@ import parseUserAgent from '../utils/parseUserAgent';
36
39
  import SessionDetailModal from '../components/SessionDetailModal';
37
40
  import { useLicense } from '../hooks/useLicense';
38
41
 
39
- // ================ THEME ================
40
- const theme = {
41
- colors: {
42
- primary: {
43
- 50: '#F0F9FF',
44
- 100: '#E0F2FE',
45
- 500: '#0EA5E9',
46
- 600: '#0284C7',
47
- 700: '#0369A1',
48
- },
49
- secondary: {
50
- 500: '#A855F7',
51
- 600: '#9333EA',
52
- },
53
- success: {
54
- 100: '#DCFCE7',
55
- 500: '#22C55E',
56
- 600: '#16A34A',
57
- 700: '#15803D',
58
- },
59
- warning: {
60
- 100: '#FEF3C7',
61
- 500: '#F59E0B',
62
- 600: '#D97706',
63
- },
64
- danger: {
65
- 100: '#FEE2E2',
66
- 500: '#EF4444',
67
- 600: '#DC2626',
68
- },
69
- neutral: {
70
- 0: '#FFFFFF',
71
- 50: '#F9FAFB',
72
- 100: '#F3F4F6',
73
- 200: '#E5E7EB',
74
- 600: '#4B5563',
75
- 700: '#374151',
76
- 800: '#1F2937',
77
- }
78
- },
79
- shadows: {
80
- sm: '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1)',
81
- md: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)',
82
- lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1)',
83
- xl: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1)',
84
- },
85
- transitions: {
86
- fast: '150ms cubic-bezier(0.4, 0, 0.2, 1)',
87
- normal: '300ms cubic-bezier(0.4, 0, 0.2, 1)',
88
- slow: '500ms cubic-bezier(0.4, 0, 0.2, 1)',
89
- },
90
- spacing: {
91
- xs: '4px',
92
- sm: '8px',
93
- md: '16px',
94
- lg: '24px',
95
- xl: '32px',
96
- '2xl': '48px',
97
- },
98
- borderRadius: {
99
- md: '8px',
100
- lg: '12px',
101
- xl: '16px',
102
- }
103
- };
104
-
105
42
  // ================ ANIMATIONS ================
106
43
  const fadeIn = keyframes`
107
44
  from { opacity: 0; transform: translateY(10px); }
@@ -203,7 +140,7 @@ const HeaderContent = styled(Flex)`
203
140
  `;
204
141
 
205
142
  const Title = styled(Typography)`
206
- color: ${theme.colors.neutral[0]};
143
+ color: white;
207
144
  font-size: 2rem;
208
145
  font-weight: 700;
209
146
  letter-spacing: -0.025em;
@@ -258,7 +195,7 @@ const StatsGrid = styled.div`
258
195
  `;
259
196
 
260
197
  const StatCard = styled(Box)`
261
- background: ${theme.colors.neutral[0]};
198
+ background: ${props => props.theme.colors.neutral0};
262
199
  border-radius: ${theme.borderRadius.lg};
263
200
  padding: 28px ${theme.spacing.lg};
264
201
  position: relative;
@@ -267,7 +204,7 @@ const StatCard = styled(Box)`
267
204
  ${css`animation: ${fadeIn} ${theme.transitions.slow} backwards;`}
268
205
  animation-delay: ${props => props.$delay || '0s'};
269
206
  box-shadow: ${theme.shadows.sm};
270
- border: 1px solid ${theme.colors.neutral[200]};
207
+ border: 1px solid ${props => props.theme.colors.neutral200};
271
208
  min-width: 200px;
272
209
  flex: 1;
273
210
  text-align: center;
@@ -288,7 +225,7 @@ const StatCard = styled(Box)`
288
225
  &:hover {
289
226
  transform: translateY(-6px);
290
227
  box-shadow: ${theme.shadows.xl};
291
- border-color: ${props => props.$color || theme.colors.primary[500]};
228
+ border-color: ${props => props.$color || props.theme.colors.primary600};
292
229
 
293
230
  .stat-icon {
294
231
  transform: scale(1.15) rotate(5deg);
@@ -296,7 +233,7 @@ const StatCard = styled(Box)`
296
233
 
297
234
  .stat-value {
298
235
  transform: scale(1.08);
299
- color: ${props => props.$color || theme.colors.primary[600]};
236
+ color: ${props => props.$color || props.theme.colors.primary600};
300
237
  }
301
238
  }
302
239
  `;
@@ -308,7 +245,7 @@ const StatIcon = styled(Box)`
308
245
  display: flex;
309
246
  align-items: center;
310
247
  justify-content: center;
311
- background: ${props => props.$bg || theme.colors.primary[100]};
248
+ background: ${props => props.$bg || props.theme.colors.primary100};
312
249
  transition: all ${theme.transitions.normal};
313
250
  margin: 0 auto 20px;
314
251
  box-shadow: ${theme.shadows.sm};
@@ -316,7 +253,7 @@ const StatIcon = styled(Box)`
316
253
  svg {
317
254
  width: 34px;
318
255
  height: 34px;
319
- color: ${props => props.$color || theme.colors.primary[600]};
256
+ color: ${props => props.$color || props.theme.colors.primary600};
320
257
  }
321
258
 
322
259
  @media screen and (max-width: ${breakpoints.mobile}) {
@@ -334,7 +271,7 @@ const StatIcon = styled(Box)`
334
271
  const StatValue = styled(Typography)`
335
272
  font-size: 2.75rem;
336
273
  font-weight: 700;
337
- color: ${theme.colors.neutral[800]};
274
+ color: ${props => props.theme.colors.neutral800};
338
275
  line-height: 1;
339
276
  margin-bottom: 10px;
340
277
  transition: all ${theme.transitions.normal};
@@ -348,7 +285,7 @@ const StatValue = styled(Typography)`
348
285
 
349
286
  const StatLabel = styled(Typography)`
350
287
  font-size: 0.95rem;
351
- color: ${theme.colors.neutral[600]};
288
+ color: ${props => props.theme.colors.neutral600};
352
289
  font-weight: 500;
353
290
  letter-spacing: 0.025em;
354
291
  text-align: center;
@@ -359,22 +296,22 @@ const StatLabel = styled(Typography)`
359
296
  `;
360
297
 
361
298
  const DataTable = styled(Box)`
362
- background: ${theme.colors.neutral[0]};
299
+ background: ${props => props.theme.colors.neutral0};
363
300
  border-radius: ${theme.borderRadius.lg};
364
301
  overflow: hidden;
365
302
  box-shadow: ${theme.shadows.sm};
366
- border: 1px solid ${theme.colors.neutral[200]};
303
+ border: 1px solid ${props => props.theme.colors.neutral200};
367
304
  margin-bottom: ${theme.spacing.xl};
368
305
  `;
369
306
 
370
307
  const StyledTable = styled(Table)`
371
308
  thead {
372
- background: ${theme.colors.neutral[50]};
373
- border-bottom: 2px solid ${theme.colors.neutral[200]};
309
+ background: ${props => props.theme.colors.neutral100};
310
+ border-bottom: 2px solid ${props => props.theme.colors.neutral200};
374
311
 
375
312
  th {
376
313
  font-weight: 600;
377
- color: ${theme.colors.neutral[700]};
314
+ color: ${props => props.theme.colors.neutral800};
378
315
  font-size: 0.875rem;
379
316
  text-transform: uppercase;
380
317
  letter-spacing: 0.025em;
@@ -384,14 +321,14 @@ const StyledTable = styled(Table)`
384
321
 
385
322
  tbody tr {
386
323
  transition: all ${theme.transitions.fast};
387
- border-bottom: 1px solid ${theme.colors.neutral[100]};
324
+ border-bottom: 1px solid ${props => props.theme.colors.neutral150};
388
325
 
389
326
  &:last-child {
390
327
  border-bottom: none;
391
328
  }
392
329
 
393
330
  &:hover {
394
- background: ${theme.colors.neutral[50]};
331
+ background: ${props => props.theme.colors.primary100};
395
332
 
396
333
  .action-buttons {
397
334
  opacity: 1;
@@ -400,7 +337,7 @@ const StyledTable = styled(Table)`
400
337
 
401
338
  td {
402
339
  padding: ${theme.spacing.lg} ${theme.spacing.lg};
403
- color: ${theme.colors.neutral[700]};
340
+ color: ${props => props.theme.colors.neutral800};
404
341
  vertical-align: middle;
405
342
  }
406
343
  }
@@ -410,19 +347,19 @@ const OnlineIndicator = styled.div`
410
347
  width: 10px;
411
348
  height: 10px;
412
349
  border-radius: 50%;
413
- background: ${props => props.$online ? theme.colors.success[500] : theme.colors.neutral[400]};
350
+ background: ${props => props.$online ? theme.colors.success[500] : props.theme.colors.neutral400};
414
351
  display: inline-block;
415
352
  margin-right: 8px;
416
353
  ${css`animation: ${props => props.$online ? pulse : 'none'} 2s ease-in-out infinite;`}
417
354
  `;
418
355
 
419
356
  const FilterBar = styled(Flex)`
420
- background: ${theme.colors.neutral[0]};
357
+ background: ${props => props.theme.colors.neutral0};
421
358
  padding: ${theme.spacing.md} ${theme.spacing.lg};
422
359
  border-radius: ${theme.borderRadius.lg};
423
360
  margin-bottom: ${theme.spacing.lg};
424
361
  box-shadow: ${theme.shadows.sm};
425
- border: 1px solid ${theme.colors.neutral[200]};
362
+ border: 1px solid ${props => props.theme.colors.neutral200};
426
363
  gap: ${theme.spacing.md};
427
364
  align-items: center;
428
365
  `;
@@ -439,28 +376,28 @@ const SearchIcon = styled(Search)`
439
376
  left: 12px;
440
377
  width: 16px;
441
378
  height: 16px;
442
- color: ${theme.colors.neutral[600]};
379
+ color: ${props => props.theme.colors.neutral600};
443
380
  pointer-events: none;
444
381
  `;
445
382
 
446
383
  const StyledSearchInput = styled.input`
447
384
  width: 100%;
448
385
  padding: ${theme.spacing.sm} ${theme.spacing.sm} ${theme.spacing.sm} 36px;
449
- border: 1px solid ${theme.colors.neutral[200]};
386
+ border: 1px solid ${props => props.theme.colors.neutral200};
450
387
  border-radius: ${theme.borderRadius.md};
451
388
  font-size: 0.875rem;
452
389
  transition: all ${theme.transitions.fast};
453
- background: ${theme.colors.neutral[0]};
454
- color: ${theme.colors.neutral[800]};
390
+ background: ${props => props.theme.colors.neutral0};
391
+ color: ${props => props.theme.colors.neutral800};
455
392
 
456
393
  &:focus {
457
394
  outline: none;
458
- border-color: ${theme.colors.primary[500]};
459
- box-shadow: 0 0 0 3px ${theme.colors.primary[100]};
395
+ border-color: ${props => props.theme.colors.primary600};
396
+ box-shadow: 0 0 0 3px ${props => props.theme.colors.primary100};
460
397
  }
461
398
 
462
399
  &::placeholder {
463
- color: ${theme.colors.neutral[600]};
400
+ color: ${props => props.theme.colors.neutral500};
464
401
  }
465
402
  `;
466
403
 
@@ -475,14 +412,42 @@ const ClickableRow = styled(Tr)`
475
412
  cursor: pointer;
476
413
 
477
414
  &:hover {
478
- background: ${theme.colors.primary[50]} !important;
415
+ background: ${props => props.theme.colors.primary100} !important;
479
416
  }
480
417
  `;
481
418
 
419
+ // Empty state background that works in dark mode
420
+ const EmptyStateBox = styled(Box)`
421
+ background: ${props => props.theme.colors.neutral0};
422
+ border-radius: ${theme.borderRadius.xl};
423
+ border: 2px dashed ${props => props.theme.colors.neutral300};
424
+ padding: 80px 32px;
425
+ text-align: center;
426
+ position: relative;
427
+ overflow: hidden;
428
+ min-height: 400px;
429
+ display: flex;
430
+ align-items: center;
431
+ justify-content: center;
432
+ `;
433
+
434
+ const EmptyStateGradient = styled.div`
435
+ position: absolute;
436
+ top: 0;
437
+ left: 0;
438
+ right: 0;
439
+ bottom: 0;
440
+ background: linear-gradient(135deg, ${theme.colors.primary[50]} 0%, ${theme.colors.secondary[50]} 100%);
441
+ opacity: 0.3;
442
+ z-index: 0;
443
+ `;
444
+
482
445
  const HomePage = () => {
446
+ const { formatMessage } = useIntl();
483
447
  const { get, post, del } = useFetchClient();
484
448
  const { toggleNotification } = useNotification();
485
449
  const { isPremium } = useLicense();
450
+ const t = (id, defaultMessage, values) => formatMessage({ id: getTranslation(id), defaultMessage }, values);
486
451
  const [sessions, setSessions] = useState([]);
487
452
  const [loading, setLoading] = useState(true);
488
453
  const [filterStatus, setFilterStatus] = useState('active'); // Default: Active Only
@@ -518,7 +483,7 @@ const HomePage = () => {
518
483
  };
519
484
 
520
485
  const handleTerminateSession = async (sessionId) => {
521
- if (!confirm('Are you sure you want to terminate this session?\n\nThis will set isActive to false (user will be logged out).')) {
486
+ if (!confirm(t('homepage.confirm.terminate', 'Are you sure you want to terminate this session?\n\nThis will set isActive to false (user will be logged out).'))) {
522
487
  return;
523
488
  }
524
489
 
@@ -531,7 +496,7 @@ const HomePage = () => {
531
496
  };
532
497
 
533
498
  const handleDeleteSession = async (sessionId) => {
534
- if (!confirm('⚠️ WARNING: This will PERMANENTLY delete this session from the database!\n\nThis action cannot be undone.\n\nAre you sure?')) {
499
+ if (!confirm(t('homepage.confirm.delete', '[WARNING] This will PERMANENTLY delete this session from the database!\n\nThis action cannot be undone.\n\nAre you sure?'))) {
535
500
  return;
536
501
  }
537
502
 
@@ -540,13 +505,13 @@ const HomePage = () => {
540
505
  fetchSessions();
541
506
  toggleNotification({
542
507
  type: 'success',
543
- message: 'Session permanently deleted',
508
+ message: t('notifications.success.deleted', 'Session permanently deleted'),
544
509
  });
545
510
  } catch (err) {
546
511
  console.error('[SessionManager] Error deleting session:', err);
547
512
  toggleNotification({
548
513
  type: 'danger',
549
- message: 'Failed to delete session',
514
+ message: t('notifications.error.delete', 'Failed to delete session'),
550
515
  });
551
516
  }
552
517
  };
@@ -555,7 +520,7 @@ const HomePage = () => {
555
520
  if (!isPremium) {
556
521
  toggleNotification({
557
522
  type: 'warning',
558
- message: 'Premium license required for export functionality',
523
+ message: t('notifications.warning.premiumRequired', 'Premium license required for export functionality'),
559
524
  });
560
525
  return;
561
526
  }
@@ -600,13 +565,13 @@ const HomePage = () => {
600
565
 
601
566
  toggleNotification({
602
567
  type: 'success',
603
- message: `Exported ${filteredSessions.length} sessions to CSV`,
568
+ message: t('notifications.success.exported', 'Exported {count} sessions to {format}', { count: filteredSessions.length, format: 'CSV' }),
604
569
  });
605
570
  } catch (err) {
606
571
  console.error('[SessionManager] Export error:', err);
607
572
  toggleNotification({
608
573
  type: 'danger',
609
- message: 'Failed to export sessions',
574
+ message: t('notifications.error.export', 'Failed to export sessions'),
610
575
  });
611
576
  }
612
577
  };
@@ -615,7 +580,7 @@ const HomePage = () => {
615
580
  if (!isPremium) {
616
581
  toggleNotification({
617
582
  type: 'warning',
618
- message: 'Premium license required for export functionality',
583
+ message: t('notifications.warning.premiumRequired', 'Premium license required for export functionality'),
619
584
  });
620
585
  return;
621
586
  }
@@ -661,13 +626,13 @@ const HomePage = () => {
661
626
 
662
627
  toggleNotification({
663
628
  type: 'success',
664
- message: `Exported ${filteredSessions.length} sessions to JSON`,
629
+ message: t('notifications.success.exported', 'Exported {count} sessions to {format}', { count: filteredSessions.length, format: 'JSON' }),
665
630
  });
666
631
  } catch (err) {
667
632
  console.error('[SessionManager] Export error:', err);
668
633
  toggleNotification({
669
634
  type: 'danger',
670
- message: 'Failed to export sessions',
635
+ message: t('notifications.error.export', 'Failed to export sessions'),
671
636
  });
672
637
  }
673
638
  };
@@ -741,10 +706,10 @@ const HomePage = () => {
741
706
  <HeaderContent justifyContent="space-between" alignItems="center">
742
707
  <Flex direction="column" alignItems="flex-start" gap={2}>
743
708
  <Title>
744
- <Monitor /> Session Manager
709
+ <Monitor /> {t('homepage.title', 'Session Manager')}
745
710
  </Title>
746
711
  <Subtitle>
747
- Monitor and manage user sessions in real-time
712
+ {t('homepage.subtitle', 'Monitor and manage user sessions in real-time')}
748
713
  </Subtitle>
749
714
  </Flex>
750
715
 
@@ -762,7 +727,7 @@ const HomePage = () => {
762
727
  fontWeight: '600',
763
728
  }}
764
729
  >
765
- Export CSV
730
+ {t('homepage.export.csv', 'Export CSV')}
766
731
  </Button>
767
732
  <Button
768
733
  onClick={handleExportJSON}
@@ -776,7 +741,7 @@ const HomePage = () => {
776
741
  fontWeight: '600',
777
742
  }}
778
743
  >
779
- Export JSON
744
+ {t('homepage.export.json', 'Export JSON')}
780
745
  </Button>
781
746
  </Flex>
782
747
  )}
@@ -790,7 +755,7 @@ const HomePage = () => {
790
755
  <Check />
791
756
  </StatIcon>
792
757
  <StatValue className="stat-value">{activeSessions.length}</StatValue>
793
- <StatLabel>Active</StatLabel>
758
+ <StatLabel>{t('homepage.stats.active', 'Active')}</StatLabel>
794
759
  </StatCard>
795
760
 
796
761
  <StatCard $delay="0.2s" $color={theme.colors.warning[500]}>
@@ -798,7 +763,7 @@ const HomePage = () => {
798
763
  <Clock />
799
764
  </StatIcon>
800
765
  <StatValue className="stat-value">{idleSessions.length}</StatValue>
801
- <StatLabel>Idle</StatLabel>
766
+ <StatLabel>{t('homepage.stats.idle', 'Idle')}</StatLabel>
802
767
  </StatCard>
803
768
 
804
769
  <StatCard $delay="0.3s" $color={theme.colors.danger[500]}>
@@ -806,30 +771,30 @@ const HomePage = () => {
806
771
  <Cross />
807
772
  </StatIcon>
808
773
  <StatValue className="stat-value">{loggedOutSessions.length}</StatValue>
809
- <StatLabel>Logged Out</StatLabel>
774
+ <StatLabel>{t('homepage.stats.loggedOut', 'Logged Out')}</StatLabel>
810
775
  </StatCard>
811
776
 
812
- <StatCard $delay="0.4s" $color={theme.colors.neutral[600]}>
813
- <StatIcon className="stat-icon" $bg={theme.colors.neutral[100]} $color={theme.colors.neutral[600]}>
777
+ <StatCard $delay="0.4s" $color="#4B5563">
778
+ <StatIcon className="stat-icon" $bg="#F3F4F6" $color="#4B5563">
814
779
  <Cross />
815
780
  </StatIcon>
816
781
  <StatValue className="stat-value">{terminatedSessions.length}</StatValue>
817
- <StatLabel>Terminated</StatLabel>
782
+ <StatLabel>{t('homepage.stats.terminated', 'Terminated')}</StatLabel>
818
783
  </StatCard>
819
784
 
820
- <StatCard $delay="0.5s" $color={theme.colors.secondary[500]}>
821
- <StatIcon className="stat-icon" $bg={theme.colors.secondary[100]} $color={theme.colors.secondary[600]}>
785
+ <StatCard $delay="0.5s" $color="#A855F7">
786
+ <StatIcon className="stat-icon" $bg="#EDE9FE" $color="#9333EA">
822
787
  <User />
823
788
  </StatIcon>
824
789
  <StatValue className="stat-value">{sessions.length}</StatValue>
825
- <StatLabel>Total</StatLabel>
790
+ <StatLabel>{t('homepage.stats.total', 'Total')}</StatLabel>
826
791
  </StatCard>
827
792
  </StatsGrid>
828
793
 
829
794
  {/* Loading */}
830
795
  {loading && (
831
796
  <Flex justifyContent="center" padding={8}>
832
- <Loader>Loading sessions...</Loader>
797
+ <Loader>{t('homepage.loading', 'Loading sessions...')}</Loader>
833
798
  </Flex>
834
799
  )}
835
800
 
@@ -837,8 +802,8 @@ const HomePage = () => {
837
802
  {!loading && sessions.length > 0 && (
838
803
  <Box>
839
804
  <Box style={{ marginBottom: theme.spacing.md }}>
840
- <Typography variant="delta" style={{ marginBottom: theme.spacing.md, color: theme.colors.neutral[700] }}>
841
- 📊 All Sessions
805
+ <Typography variant="delta" textColor="neutral700" style={{ marginBottom: theme.spacing.md }}>
806
+ {t('homepage.allSessions', 'All Sessions')}
842
807
  </Typography>
843
808
  </Box>
844
809
 
@@ -850,7 +815,7 @@ const HomePage = () => {
850
815
  <StyledSearchInput
851
816
  value={searchQuery}
852
817
  onChange={(e) => setSearchQuery(e.target.value)}
853
- placeholder="Search by user, IP address, or device..."
818
+ placeholder={t('homepage.search.placeholder', 'Search by user, IP address, or device...')}
854
819
  type="text"
855
820
  />
856
821
  </SearchInputWrapper>
@@ -863,11 +828,11 @@ const HomePage = () => {
863
828
  placeholder="Filter"
864
829
  size="S"
865
830
  >
866
- <SingleSelectOption value="all">All Sessions</SingleSelectOption>
867
- <SingleSelectOption value="active">🟢 Active (&lt; 15 min)</SingleSelectOption>
868
- <SingleSelectOption value="idle">🟡 Idle (&gt; 15 min)</SingleSelectOption>
869
- <SingleSelectOption value="loggedout">🔴 Logged Out</SingleSelectOption>
870
- <SingleSelectOption value="terminated">⚫ Terminated</SingleSelectOption>
831
+ <SingleSelectOption value="all">{t('homepage.filter.all', 'All Sessions')}</SingleSelectOption>
832
+ <SingleSelectOption value="active">{t('homepage.filter.active', 'Active (less than 15 min)')}</SingleSelectOption>
833
+ <SingleSelectOption value="idle">{t('homepage.filter.idle', 'Idle (more than 15 min)')}</SingleSelectOption>
834
+ <SingleSelectOption value="loggedout">{t('homepage.filter.loggedout', 'Logged Out')}</SingleSelectOption>
835
+ <SingleSelectOption value="terminated">{t('homepage.filter.terminated', 'Terminated')}</SingleSelectOption>
871
836
  </SingleSelect>
872
837
  </Box>
873
838
 
@@ -879,10 +844,10 @@ const HomePage = () => {
879
844
  placeholder="Entries"
880
845
  size="S"
881
846
  >
882
- <SingleSelectOption value="10">10 entries</SingleSelectOption>
883
- <SingleSelectOption value="25">25 entries</SingleSelectOption>
884
- <SingleSelectOption value="50">50 entries</SingleSelectOption>
885
- <SingleSelectOption value="100">100 entries</SingleSelectOption>
847
+ <SingleSelectOption value="10">{t('homepage.entries.10', '10 entries')}</SingleSelectOption>
848
+ <SingleSelectOption value="25">{t('homepage.entries.25', '25 entries')}</SingleSelectOption>
849
+ <SingleSelectOption value="50">{t('homepage.entries.50', '50 entries')}</SingleSelectOption>
850
+ <SingleSelectOption value="100">{t('homepage.entries.100', '100 entries')}</SingleSelectOption>
886
851
  </SingleSelect>
887
852
  </Box>
888
853
  </FilterBar>
@@ -890,8 +855,10 @@ const HomePage = () => {
890
855
  {/* Results count */}
891
856
  <Box style={{ marginBottom: theme.spacing.md }}>
892
857
  <Typography variant="pi" textColor="neutral600">
893
- Showing {filteredSessions.length} of {sessions.length} sessions
894
- {searchQuery && ` (filtered by "${searchQuery}")`}
858
+ {searchQuery
859
+ ? t('homepage.showingFiltered', 'Showing {count} of {total} sessions (filtered by "{query}")', { count: filteredSessions.length, total: sessions.length, query: searchQuery })
860
+ : t('homepage.showing', 'Showing {count} of {total} sessions', { count: filteredSessions.length, total: sessions.length })
861
+ }
895
862
  </Typography>
896
863
  </Box>
897
864
 
@@ -901,13 +868,13 @@ const HomePage = () => {
901
868
  <StyledTable>
902
869
  <Thead>
903
870
  <Tr>
904
- <Th>Status</Th>
905
- <Th>User</Th>
906
- <Th>Device</Th>
907
- <Th>IP Address</Th>
908
- <Th>Login Time</Th>
909
- <Th>Last Active</Th>
910
- <Th>Actions</Th>
871
+ <Th>{t('homepage.table.status', 'Status')}</Th>
872
+ <Th>{t('homepage.table.user', 'User')}</Th>
873
+ <Th>{t('homepage.table.device', 'Device')}</Th>
874
+ <Th>{t('homepage.table.ipAddress', 'IP Address')}</Th>
875
+ <Th>{t('homepage.table.loginTime', 'Login Time')}</Th>
876
+ <Th>{t('homepage.table.lastActive', 'Last Active')}</Th>
877
+ <Th>{t('homepage.table.actions', 'Actions')}</Th>
911
878
  </Tr>
912
879
  </Thead>
913
880
  <Tbody>
@@ -921,26 +888,26 @@ const HomePage = () => {
921
888
  active: {
922
889
  bg: theme.colors.success[50],
923
890
  badgeColor: 'success600',
924
- label: '🟢 Active',
891
+ label: t('homepage.status.active', 'Active'),
925
892
  indicator: true
926
893
  },
927
894
  idle: {
928
895
  bg: theme.colors.warning[50],
929
896
  badgeColor: 'warning600',
930
- label: '🟡 Idle',
897
+ label: t('homepage.status.idle', 'Idle'),
931
898
  indicator: false
932
899
  },
933
900
  loggedout: {
934
901
  bg: theme.colors.danger[50],
935
902
  badgeColor: 'danger600',
936
- label: '🔴 Logged Out',
903
+ label: t('homepage.status.loggedOut', 'Logged Out'),
937
904
  indicator: false,
938
905
  opacity: 0.7
939
906
  },
940
907
  terminated: {
941
- bg: theme.colors.neutral[100],
908
+ bg: '#F3F4F6',
942
909
  badgeColor: 'neutral600',
943
- label: ' Terminated',
910
+ label: t('homepage.status.terminated', 'Terminated'),
944
911
  indicator: false,
945
912
  opacity: 0.6
946
913
  },
@@ -975,7 +942,7 @@ const HomePage = () => {
975
942
  <Td>
976
943
  <Flex direction="column" alignItems="flex-start">
977
944
  <Typography fontWeight="semiBold" ellipsis>
978
- {session.user?.username || session.user?.email || 'Unknown'}
945
+ {session.user?.username || session.user?.email || t('homepage.user.unknown', 'Unknown')}
979
946
  </Typography>
980
947
  {session.user?.email && session.user?.username && (
981
948
  <Typography variant="pi" textColor="neutral600" ellipsis>
@@ -1021,7 +988,7 @@ const HomePage = () => {
1021
988
  {new Date(session.lastActive || session.loginTime).toLocaleString()}
1022
989
  </Typography>
1023
990
  <Typography variant="pi" textColor={sessionStatus === 'active' ? 'success600' : 'neutral500'}>
1024
- {session.minutesSinceActive} min ago
991
+ {t('homepage.time.minAgo', '{minutes} min ago', { minutes: session.minutesSinceActive })}
1025
992
  </Typography>
1026
993
  </Flex>
1027
994
  </Td>
@@ -1036,7 +1003,7 @@ const HomePage = () => {
1036
1003
  e.stopPropagation();
1037
1004
  handleSessionClick(session);
1038
1005
  }}
1039
- title="View Details"
1006
+ title={t('homepage.actions.viewDetails', 'View Details')}
1040
1007
  >
1041
1008
  <Eye />
1042
1009
  </Button>
@@ -1048,7 +1015,7 @@ const HomePage = () => {
1048
1015
  handleTerminateSession(session.id);
1049
1016
  }}
1050
1017
  disabled={sessionStatus !== 'active' && sessionStatus !== 'idle'}
1051
- title={session.isActive ? "Terminate (Logout)" : "Already inactive"}
1018
+ title={session.isActive ? t('homepage.actions.terminate', 'Terminate (Logout)') : t('homepage.actions.alreadyInactive', 'Already inactive')}
1052
1019
  >
1053
1020
  <Cross />
1054
1021
  </Button>
@@ -1059,7 +1026,7 @@ const HomePage = () => {
1059
1026
  e.stopPropagation();
1060
1027
  handleDeleteSession(session.id);
1061
1028
  }}
1062
- title="Delete Permanently"
1029
+ title={t('homepage.actions.deletePermanently', 'Delete Permanently')}
1063
1030
  >
1064
1031
  <Trash />
1065
1032
  </Button>
@@ -1074,10 +1041,10 @@ const HomePage = () => {
1074
1041
  ) : (
1075
1042
  /* No results found */
1076
1043
  <Box
1044
+ background="neutral0"
1077
1045
  style={{
1078
- background: theme.colors.neutral[0],
1079
1046
  borderRadius: theme.borderRadius.xl,
1080
- border: `2px dashed ${theme.colors.neutral[200]}`,
1047
+ border: '2px dashed #E5E7EB',
1081
1048
  padding: '60px 32px',
1082
1049
  textAlign: 'center',
1083
1050
  position: 'relative',
@@ -1122,19 +1089,19 @@ const HomePage = () => {
1122
1089
  boxShadow: theme.shadows.xl,
1123
1090
  }}
1124
1091
  >
1125
- <Search style={{ width: '50px', height: '50px', color: theme.colors.primary[600] }} />
1092
+ <Search style={{ width: '50px', height: '50px', color: '#0284C7' }} />
1126
1093
  </Box>
1127
1094
 
1128
1095
  <Typography
1129
1096
  variant="alpha"
1097
+ textColor="neutral800"
1130
1098
  style={{
1131
1099
  fontSize: '1.5rem',
1132
1100
  fontWeight: '700',
1133
- color: theme.colors.neutral[800],
1134
1101
  marginBottom: '4px',
1135
1102
  }}
1136
1103
  >
1137
- No sessions found
1104
+ {t('homepage.noResults.title', 'No sessions found')}
1138
1105
  </Typography>
1139
1106
 
1140
1107
  <Typography
@@ -1146,7 +1113,7 @@ const HomePage = () => {
1146
1113
  lineHeight: '1.6',
1147
1114
  }}
1148
1115
  >
1149
- Try adjusting your search query or filters to find sessions
1116
+ {t('homepage.noResults.description', 'Try adjusting your search query or filters to find sessions')}
1150
1117
  </Typography>
1151
1118
  </Flex>
1152
1119
  </Box>
@@ -1157,10 +1124,10 @@ const HomePage = () => {
1157
1124
  {/* Empty State */}
1158
1125
  {!loading && sessions.length === 0 && (
1159
1126
  <Box
1127
+ background="neutral0"
1160
1128
  style={{
1161
- background: theme.colors.neutral[0],
1162
1129
  borderRadius: theme.borderRadius.xl,
1163
- border: `2px dashed ${theme.colors.neutral[200]}`,
1130
+ border: '2px dashed #E5E7EB',
1164
1131
  padding: '80px 32px',
1165
1132
  textAlign: 'center',
1166
1133
  position: 'relative',
@@ -1185,10 +1152,7 @@ const HomePage = () => {
1185
1152
  }}
1186
1153
  />
1187
1154
 
1188
- {/* Floating Emoji */}
1189
- <FloatingEmoji>
1190
- 💻
1191
- </FloatingEmoji>
1155
+ {/* Floating Icon (removed emoji) */}
1192
1156
 
1193
1157
  <Flex direction="column" alignItems="center" gap={6} style={{ position: 'relative', zIndex: 1 }}>
1194
1158
  <Box
@@ -1203,19 +1167,19 @@ const HomePage = () => {
1203
1167
  boxShadow: theme.shadows.xl,
1204
1168
  }}
1205
1169
  >
1206
- <Monitor style={{ width: '60px', height: '60px', color: theme.colors.primary[600] }} />
1170
+ <Monitor style={{ width: '60px', height: '60px', color: '#0284C7' }} />
1207
1171
  </Box>
1208
1172
 
1209
1173
  <Typography
1210
1174
  variant="alpha"
1175
+ textColor="neutral800"
1211
1176
  style={{
1212
1177
  fontSize: '1.75rem',
1213
1178
  fontWeight: '700',
1214
- color: theme.colors.neutral[800],
1215
1179
  marginBottom: '8px',
1216
1180
  }}
1217
1181
  >
1218
- No sessions yet
1182
+ {t('homepage.empty.title', 'No sessions yet')}
1219
1183
  </Typography>
1220
1184
 
1221
1185
  <Typography
@@ -1227,7 +1191,7 @@ const HomePage = () => {
1227
1191
  lineHeight: '1.6',
1228
1192
  }}
1229
1193
  >
1230
- Sessions will appear here when users log in to your application
1194
+ {t('homepage.empty.description', 'Sessions will appear here when users log in to your application')}
1231
1195
  </Typography>
1232
1196
  </Flex>
1233
1197
  </Box>