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
@@ -206,12 +206,12 @@ const validateTemplate = (template, templateType) => {
206
206
  // Get default email templates
207
207
  const getDefaultTemplates = () => ({
208
208
  suspiciousLogin: {
209
- subject: '🚨 Suspicious Login Alert - Session Manager',
209
+ subject: '[ALERT] Suspicious Login Alert - Session Manager',
210
210
  html: `
211
211
  <html>
212
212
  <body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
213
213
  <div style="max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f9fafb; border-radius: 10px;">
214
- <h2 style="color: #dc2626;">🚨 Suspicious Login Detected</h2>
214
+ <h2 style="color: #dc2626;">[ALERT] Suspicious Login Detected</h2>
215
215
  <p>A potentially suspicious login was detected for your account.</p>
216
216
 
217
217
  <div style="background: white; padding: 15px; border-radius: 8px; margin: 20px 0;">
@@ -246,7 +246,7 @@ const getDefaultTemplates = () => ({
246
246
  </div>
247
247
  </body>
248
248
  </html>`,
249
- text: `🚨 Suspicious Login Detected
249
+ text: `[ALERT] Suspicious Login Detected
250
250
 
251
251
  A potentially suspicious login was detected for your account.
252
252
 
@@ -261,12 +261,12 @@ Login Details:
261
261
  Security: VPN={{reason.isVpn}}, Proxy={{reason.isProxy}}, Threat={{reason.isThreat}}, Score={{reason.securityScore}}/100`,
262
262
  },
263
263
  newLocation: {
264
- subject: '📍 New Location Login Detected',
264
+ subject: '[LOCATION] New Location Login Detected',
265
265
  html: `
266
266
  <html>
267
267
  <body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
268
268
  <div style="max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f0f9ff; border-radius: 10px;">
269
- <h2 style="color: #0284c7;">📍 Login from New Location</h2>
269
+ <h2 style="color: #0284c7;">[LOCATION] Login from New Location</h2>
270
270
  <p>Your account was accessed from a new location.</p>
271
271
 
272
272
  <div style="background: white; padding: 15px; border-radius: 8px; margin: 20px 0;">
@@ -289,7 +289,7 @@ Security: VPN={{reason.isVpn}}, Proxy={{reason.isProxy}}, Threat={{reason.isThre
289
289
  </div>
290
290
  </body>
291
291
  </html>`,
292
- text: `📍 Login from New Location
292
+ text: `[LOCATION] Login from New Location
293
293
 
294
294
  Your account was accessed from a new location.
295
295
 
@@ -304,12 +304,12 @@ New Location Details:
304
304
  If this was you, no action is needed.`,
305
305
  },
306
306
  vpnProxy: {
307
- subject: '⚠️ VPN/Proxy Login Detected',
307
+ subject: '[WARNING] VPN/Proxy Login Detected',
308
308
  html: `
309
309
  <html>
310
310
  <body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
311
311
  <div style="max-width: 600px; margin: 0 auto; padding: 20px; background-color: #fffbeb; border-radius: 10px;">
312
- <h2 style="color: #d97706;">⚠️ VPN/Proxy Detected</h2>
312
+ <h2 style="color: #d97706;">[WARNING] VPN/Proxy Detected</h2>
313
313
  <p>A login from a VPN or proxy service was detected on your account.</p>
314
314
 
315
315
  <div style="background: white; padding: 15px; border-radius: 8px; margin: 20px 0;">
@@ -334,7 +334,7 @@ If this was you, no action is needed.`,
334
334
  </div>
335
335
  </body>
336
336
  </html>`,
337
- text: `⚠️ VPN/Proxy Detected
337
+ text: `[WARNING] VPN/Proxy Detected
338
338
 
339
339
  A login from a VPN or proxy service was detected on your account.
340
340
 
@@ -444,7 +444,7 @@ const SettingsPage = () => {
444
444
  };
445
445
 
446
446
  const handleCleanInactive = async () => {
447
- if (!confirm('⚠️ WARNING: This will permanently delete ALL inactive sessions.\n\nContinue?')) {
447
+ if (!confirm('[WARNING] This will permanently delete ALL inactive sessions.\n\nContinue?')) {
448
448
  return;
449
449
  }
450
450
 
@@ -553,7 +553,7 @@ const SettingsPage = () => {
553
553
 
554
554
  {/* Session Timeout */}
555
555
  <Typography variant="sigma" fontWeight="bold" style={{ marginBottom: '16px', display: 'block', color: theme.colors.neutral[700] }}>
556
- ⏱️ SESSION TIMEOUT
556
+ SESSION TIMEOUT
557
557
  </Typography>
558
558
  <Grid.Root gap={6} style={{ marginBottom: '32px' }}>
559
559
  <Grid.Item col={6} s={12}>
@@ -694,7 +694,7 @@ const SettingsPage = () => {
694
694
  <Box padding={6}>
695
695
 
696
696
  <Typography variant="sigma" fontWeight="bold" style={{ marginBottom: '16px', display: 'block', color: theme.colors.neutral[700] }}>
697
- 🔒 SECURITY OPTIONS
697
+ SECURITY OPTIONS
698
698
  </Typography>
699
699
 
700
700
  {/* Feature Toggles */}
@@ -866,7 +866,7 @@ const SettingsPage = () => {
866
866
  checked={settings.alertOnSuspiciousLogin}
867
867
  onChange={() => handleChange('alertOnSuspiciousLogin', !settings.alertOnSuspiciousLogin)}
868
868
  >
869
- 🚨 Suspicious Login
869
+ Suspicious Login
870
870
  </Checkbox>
871
871
  </Box>
872
872
  </Grid.Item>
@@ -876,7 +876,7 @@ const SettingsPage = () => {
876
876
  checked={settings.alertOnNewLocation}
877
877
  onChange={() => handleChange('alertOnNewLocation', !settings.alertOnNewLocation)}
878
878
  >
879
- 📍 New Location
879
+ New Location
880
880
  </Checkbox>
881
881
  </Box>
882
882
  </Grid.Item>
@@ -886,7 +886,7 @@ const SettingsPage = () => {
886
886
  checked={settings.alertOnVpnProxy}
887
887
  onChange={() => handleChange('alertOnVpnProxy', !settings.alertOnVpnProxy)}
888
888
  >
889
- ⚠️ VPN/Proxy
889
+ VPN/Proxy
890
890
  </Checkbox>
891
891
  </Box>
892
892
  </Grid.Item>
@@ -895,7 +895,7 @@ const SettingsPage = () => {
895
895
  {/* Email Templates */}
896
896
  <Divider style={{ marginBottom: '24px' }} />
897
897
  <Typography variant="sigma" fontWeight="bold" style={{ marginBottom: '8px', display: 'block', color: theme.colors.neutral[700] }}>
898
- 📝 EMAIL TEMPLATES
898
+ EMAIL TEMPLATES
899
899
  </Typography>
900
900
  <Typography variant="pi" textColor="neutral600" style={{ marginBottom: '20px', display: 'block', fontSize: '12px' }}>
901
901
  Customize email notification templates with dynamic variables
@@ -904,9 +904,9 @@ const SettingsPage = () => {
904
904
  {/* Template Tabs */}
905
905
  <Tabs.Root value={activeTemplateTab} onValueChange={setActiveTemplateTab}>
906
906
  <Tabs.List aria-label="Email Templates">
907
- <Tabs.Trigger value="suspiciousLogin">🚨 Suspicious Login</Tabs.Trigger>
908
- <Tabs.Trigger value="newLocation">📍 New Location</Tabs.Trigger>
909
- <Tabs.Trigger value="vpnProxy">⚠️ VPN/Proxy</Tabs.Trigger>
907
+ <Tabs.Trigger value="suspiciousLogin">Suspicious Login</Tabs.Trigger>
908
+ <Tabs.Trigger value="newLocation">New Location</Tabs.Trigger>
909
+ <Tabs.Trigger value="vpnProxy">VPN/Proxy</Tabs.Trigger>
910
910
  </Tabs.List>
911
911
 
912
912
  {Object.keys(settings.emailTemplates).map((templateKey) => (
@@ -915,7 +915,7 @@ const SettingsPage = () => {
915
915
  {/* Subject */}
916
916
  <Box style={{ marginBottom: '24px' }}>
917
917
  <Typography variant="pi" fontWeight="bold" style={{ marginBottom: '8px', display: 'block' }}>
918
- ✉️ Email Subject
918
+ Email Subject
919
919
  </Typography>
920
920
  <TextInput
921
921
  value={settings.emailTemplates[templateKey].subject}
@@ -1151,7 +1151,7 @@ const SettingsPage = () => {
1151
1151
  <Information style={{ width: '20px', height: '20px', color: theme.colors.neutral[600], flexShrink: 0, marginTop: '2px' }} />
1152
1152
  <Box style={{ flex: 1 }}>
1153
1153
  <Typography variant="omega" fontWeight="bold" style={{ marginBottom: '8px', display: 'block' }}>
1154
- 💡 How to Apply These Settings
1154
+ How to Apply These Settings
1155
1155
  </Typography>
1156
1156
  <Typography variant="pi" textColor="neutral600" style={{ fontSize: '13px', lineHeight: '1.8' }}>
1157
1157
  Settings are saved in your browser. To apply permanently, copy the config below and paste it into{' '}
@@ -0,0 +1,448 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { useFetchClient, useNotification } from '@strapi/strapi/admin';
3
+ import styled from 'styled-components';
4
+ import {
5
+ Box,
6
+ Button,
7
+ Flex,
8
+ Typography,
9
+ Badge,
10
+ } from '@strapi/design-system';
11
+ import {
12
+ Check as CheckIcon,
13
+ Cross as XMarkIcon,
14
+ Sparkle as SparklesIcon,
15
+ Lightning as BoltIcon,
16
+ Rocket as RocketLaunchIcon,
17
+ } from '@strapi/icons';
18
+ import pluginId from '../pluginId';
19
+
20
+ const Container = styled(Box)`
21
+ padding: 32px;
22
+ max-width: 1400px;
23
+ margin: 0 auto;
24
+ `;
25
+
26
+ const Header = styled(Box)`
27
+ text-align: center;
28
+ margin-bottom: 48px;
29
+ display: flex;
30
+ flex-direction: column;
31
+ align-items: center;
32
+ gap: 8px;
33
+ `;
34
+
35
+ const Title = styled(Typography)`
36
+ font-size: 2.5rem;
37
+ font-weight: 700;
38
+ margin-bottom: 8px;
39
+ background: linear-gradient(135deg, #0EA5E9, #A855F7);
40
+ -webkit-background-clip: text;
41
+ -webkit-text-fill-color: transparent;
42
+ display: block;
43
+ `;
44
+
45
+ const Subtitle = styled(Typography)`
46
+ font-size: 1.125rem;
47
+ color: ${props => props.theme.colors.neutral600};
48
+ line-height: 1.6;
49
+ display: block;
50
+ `;
51
+
52
+ const TierGrid = styled(Flex)`
53
+ gap: 32px;
54
+ margin: 0 auto 48px;
55
+ max-width: 1080px;
56
+ justify-content: center;
57
+ flex-wrap: wrap;
58
+ align-items: stretch;
59
+ `;
60
+
61
+ const TierWrapper = styled(Box)`
62
+ flex: 1;
63
+ min-width: 280px;
64
+ max-width: 340px;
65
+ display: flex;
66
+ `;
67
+
68
+ const TierCard = styled(Box)`
69
+ background: ${props => props.theme.colors.neutral0};
70
+ border-radius: 16px;
71
+ padding: 32px;
72
+ border: 2px solid ${props => props.$featured ? '#0EA5E9' : props.theme.colors.neutral200};
73
+ position: relative;
74
+ transition: all 0.3s ease;
75
+ box-shadow: ${props => props.$featured
76
+ ? '0 20px 25px -5px rgba(14, 165, 233, 0.25), 0 8px 10px -6px rgba(14, 165, 233, 0.2)'
77
+ : '0 10px 15px -3px rgba(15, 23, 42, 0.08), 0 4px 6px -4px rgba(15, 23, 42, 0.05)'};
78
+ display: flex;
79
+ flex-direction: column;
80
+ width: 100%;
81
+
82
+ &:hover {
83
+ transform: translateY(-4px);
84
+ box-shadow: 0 20px 25px -5px rgba(15, 23, 42, 0.15), 0 8px 10px -6px rgba(15, 23, 42, 0.1);
85
+ }
86
+ `;
87
+
88
+ const PopularBadge = styled(Badge)`
89
+ position: absolute;
90
+ top: -12px;
91
+ right: 24px;
92
+ background: linear-gradient(135deg, #0EA5E9, #0284C7);
93
+ color: white;
94
+ padding: 4px 16px;
95
+ font-size: 12px;
96
+ font-weight: 600;
97
+ `;
98
+
99
+ const TierIcon = styled(Box)`
100
+ width: 48px;
101
+ height: 48px;
102
+ border-radius: 12px;
103
+ display: flex;
104
+ align-items: center;
105
+ justify-content: center;
106
+ margin-bottom: 16px;
107
+ background: ${props => props.$color};
108
+
109
+ svg {
110
+ width: 28px;
111
+ height: 28px;
112
+ color: white;
113
+ }
114
+ `;
115
+
116
+ const TierName = styled(Typography)`
117
+ font-size: 1.5rem;
118
+ font-weight: 700;
119
+ margin-bottom: 8px;
120
+ `;
121
+
122
+ const TierPrice = styled(Typography)`
123
+ font-size: 2rem;
124
+ font-weight: 800;
125
+ margin-bottom: 4px;
126
+ `;
127
+
128
+ const TierDescription = styled(Typography)`
129
+ color: ${props => props.theme.colors.neutral600};
130
+ margin-bottom: 24px;
131
+ `;
132
+
133
+ const FeatureList = styled(Box)`
134
+ margin-bottom: 24px;
135
+ flex: 1;
136
+ `;
137
+
138
+ const Feature = styled(Flex)`
139
+ gap: 12px;
140
+ margin-bottom: 12px;
141
+ align-items: flex-start;
142
+ `;
143
+
144
+ const FeatureIcon = styled(Box)`
145
+ width: 20px;
146
+ height: 20px;
147
+ border-radius: 50%;
148
+ display: flex;
149
+ align-items: center;
150
+ justify-content: center;
151
+ flex-shrink: 0;
152
+ margin-top: 2px;
153
+
154
+ ${props => props.$included ? `
155
+ background: #DCFCE7;
156
+ svg { color: #16A34A; }
157
+ ` : `
158
+ background: #FEE2E2;
159
+ svg { color: #DC2626; }
160
+ `}
161
+ `;
162
+
163
+ const UpgradeButton = styled(Button)`
164
+ width: 100%;
165
+ height: 48px;
166
+ font-weight: 600;
167
+ font-size: 15px;
168
+ background: ${props => props.$gradient};
169
+ border: none;
170
+ color: white;
171
+
172
+ &:hover {
173
+ transform: translateY(-2px);
174
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
175
+ }
176
+ `;
177
+
178
+ const CurrentPlanBadge = styled(Badge)`
179
+ width: 100%;
180
+ height: 48px;
181
+ display: flex;
182
+ align-items: center;
183
+ justify-content: center;
184
+ background: ${props => props.theme.colors.neutral100};
185
+ color: ${props => props.theme.colors.neutral600};
186
+ font-weight: 600;
187
+ font-size: 15px;
188
+ `;
189
+
190
+ const LimitsBox = styled(Box)`
191
+ background: ${props => props.theme.colors.neutral100};
192
+ border-radius: 8px;
193
+ padding: 12px;
194
+ margin-bottom: 20px;
195
+ `;
196
+
197
+ /**
198
+ * Upgrade page for Magic Sessionmanager with pricing tiers
199
+ */
200
+ const UpgradePage = () => {
201
+ const { get } = useFetchClient();
202
+ const { toggleNotification } = useNotification();
203
+ const [currentTier, setCurrentTier] = useState('free');
204
+ const [limits, setLimits] = useState(null);
205
+ const [loading, setLoading] = useState(true);
206
+
207
+ useEffect(() => {
208
+ fetchLicenseInfo();
209
+ }, []);
210
+
211
+ /**
212
+ * Fetches current license information
213
+ */
214
+ const fetchLicenseInfo = async () => {
215
+ try {
216
+ const response = await get(`/${pluginId}/license/status`);
217
+ const licenseData = response.data || {};
218
+
219
+ let tier = 'free';
220
+ if (licenseData.data?.features?.advanced) {
221
+ tier = 'advanced';
222
+ } else if (licenseData.data?.features?.premium) {
223
+ tier = 'premium';
224
+ }
225
+
226
+ setCurrentTier(tier);
227
+ setLimits(licenseData.limits);
228
+ setLoading(false);
229
+ } catch (error) {
230
+ console.error('[magic-sessionmanager] Failed to fetch license info:', error);
231
+ setLoading(false);
232
+ }
233
+ };
234
+
235
+ /**
236
+ * Returns rank of tier for comparison
237
+ */
238
+ const getTierRank = (tierId) => {
239
+ const ranks = {
240
+ 'free': 0,
241
+ 'premium': 1,
242
+ 'advanced': 2,
243
+ };
244
+ return ranks[tierId] || 0;
245
+ };
246
+
247
+ /**
248
+ * Returns button text based on tier comparison
249
+ */
250
+ const getButtonText = (tierId) => {
251
+ const currentRank = getTierRank(currentTier);
252
+ const targetRank = getTierRank(tierId);
253
+
254
+ if (currentRank === targetRank) {
255
+ return 'Current Plan';
256
+ } else if (targetRank > currentRank) {
257
+ return 'Upgrade Now';
258
+ } else {
259
+ return 'Downgrade';
260
+ }
261
+ };
262
+
263
+ const tiers = [
264
+ {
265
+ id: 'free',
266
+ name: 'FREE',
267
+ price: '$0',
268
+ period: 'forever',
269
+ description: 'Perfect for small projects and testing',
270
+ icon: <SparklesIcon />,
271
+ color: 'linear-gradient(135deg, #6B7280, #4B5563)',
272
+ features: [
273
+ { name: 'Session Tracking', included: true },
274
+ { name: 'Force Logout', included: true },
275
+ { name: 'Dashboard Widget', included: true },
276
+ { name: 'Device Detection', included: true },
277
+ { name: 'Basic Monitoring', included: true },
278
+ { name: 'IP Geolocation', included: false },
279
+ { name: 'VPN/Proxy Detection', included: false },
280
+ { name: 'Threat Analysis', included: false },
281
+ { name: 'Auto-blocking', included: false },
282
+ ],
283
+ limits: {
284
+ sessions: 'Unlimited',
285
+ support: 'Community',
286
+ }
287
+ },
288
+ {
289
+ id: 'premium',
290
+ name: 'PREMIUM',
291
+ price: '$14.50',
292
+ period: '/month',
293
+ description: 'Enhanced security for growing teams',
294
+ icon: <BoltIcon />,
295
+ color: 'linear-gradient(135deg, #8B5CF6, #7C3AED)',
296
+ featured: true,
297
+ features: [
298
+ { name: 'Session Tracking', included: true },
299
+ { name: 'Force Logout', included: true },
300
+ { name: 'Dashboard Widget', included: true },
301
+ { name: 'Device Detection', included: true },
302
+ { name: 'Basic Monitoring', included: true },
303
+ { name: 'IP Geolocation', included: true },
304
+ { name: 'VPN/Proxy Detection', included: true },
305
+ { name: 'Security Risk Scoring', included: true },
306
+ { name: 'Extended Logging', included: true },
307
+ { name: 'Priority Support', included: true },
308
+ ],
309
+ limits: {
310
+ sessions: 'Unlimited',
311
+ support: 'Priority',
312
+ }
313
+ },
314
+ {
315
+ id: 'advanced',
316
+ name: 'ADVANCED',
317
+ price: '$39.50',
318
+ period: '/month',
319
+ description: 'Maximum security for enterprises',
320
+ icon: <RocketLaunchIcon />,
321
+ color: 'linear-gradient(135deg, #0EA5E9, #0284C7)',
322
+ features: [
323
+ { name: 'Session Tracking', included: true },
324
+ { name: 'Force Logout', included: true },
325
+ { name: 'Dashboard Widget', included: true },
326
+ { name: 'Device Detection', included: true },
327
+ { name: 'Basic Monitoring', included: true },
328
+ { name: 'IP Geolocation', included: true },
329
+ { name: 'VPN/Proxy Detection', included: true },
330
+ { name: 'Security Risk Scoring', included: true },
331
+ { name: 'Extended Logging', included: true },
332
+ { name: 'Threat Analysis', included: true },
333
+ { name: 'Auto-blocking', included: true },
334
+ { name: 'Email/Webhook Alerts', included: true },
335
+ { name: 'Geo-fencing', included: true },
336
+ { name: 'Priority + Phone Support', included: true },
337
+ ],
338
+ limits: {
339
+ sessions: 'Unlimited',
340
+ support: 'Priority + Phone',
341
+ }
342
+ }
343
+ ];
344
+
345
+ /**
346
+ * Handle upgrade button click
347
+ */
348
+ const handleUpgrade = (tierId) => {
349
+ window.open('https://store.magicdx.dev/', '_blank');
350
+ };
351
+
352
+ if (loading) {
353
+ return (
354
+ <Container>
355
+ <Flex justifyContent="center" alignItems="center" style={{ minHeight: '400px' }}>
356
+ <Typography>Loading license information...</Typography>
357
+ </Flex>
358
+ </Container>
359
+ );
360
+ }
361
+
362
+ return (
363
+ <Container>
364
+ <Header>
365
+ <Title variant="alpha">Choose Your Plan</Title>
366
+ <Subtitle variant="omega">
367
+ Unlock powerful session management features for your Strapi application
368
+ </Subtitle>
369
+ </Header>
370
+
371
+ <TierGrid>
372
+ {tiers.map((tier) => (
373
+ <TierWrapper key={tier.id}>
374
+ <TierCard $featured={tier.featured}>
375
+ {tier.featured && <PopularBadge>MOST POPULAR</PopularBadge>}
376
+
377
+ <TierIcon $color={tier.color}>
378
+ {tier.icon}
379
+ </TierIcon>
380
+
381
+ <TierName variant="beta">{tier.name}</TierName>
382
+
383
+ <Flex alignItems="baseline" gap={1}>
384
+ <TierPrice variant="alpha">{tier.price}</TierPrice>
385
+ <Typography variant="omega" style={{ color: '#6B7280' }}>
386
+ {tier.period}
387
+ </Typography>
388
+ </Flex>
389
+
390
+ <TierDescription variant="omega">
391
+ {tier.description}
392
+ </TierDescription>
393
+
394
+ <LimitsBox>
395
+ <Flex direction="column" gap={2}>
396
+ <Typography variant="pi" style={{ fontSize: '13px' }}>
397
+ <strong>Sessions:</strong> {tier.limits.sessions}
398
+ </Typography>
399
+ <Typography variant="pi" style={{ fontSize: '13px' }}>
400
+ <strong>Support:</strong> {tier.limits.support}
401
+ </Typography>
402
+ </Flex>
403
+ </LimitsBox>
404
+
405
+ <FeatureList>
406
+ {tier.features.map((feature, index) => (
407
+ <Feature key={index}>
408
+ <FeatureIcon $included={feature.included}>
409
+ {feature.included ? (
410
+ <CheckIcon style={{ width: 14, height: 14 }} />
411
+ ) : (
412
+ <XMarkIcon style={{ width: 14, height: 14 }} />
413
+ )}
414
+ </FeatureIcon>
415
+ <Typography
416
+ variant="omega"
417
+ style={{
418
+ fontSize: '14px',
419
+ color: feature.included ? '#374151' : '#9CA3AF',
420
+ textDecoration: feature.included ? 'none' : 'line-through'
421
+ }}
422
+ >
423
+ {feature.name}
424
+ </Typography>
425
+ </Feature>
426
+ ))}
427
+ </FeatureList>
428
+
429
+ {currentTier === tier.id ? (
430
+ <CurrentPlanBadge>CURRENT PLAN</CurrentPlanBadge>
431
+ ) : (
432
+ <UpgradeButton
433
+ onClick={() => handleUpgrade(tier.id)}
434
+ $gradient={tier.color}
435
+ >
436
+ {getButtonText(tier.id)}
437
+ </UpgradeButton>
438
+ )}
439
+ </TierCard>
440
+ </TierWrapper>
441
+ ))}
442
+ </TierGrid>
443
+ </Container>
444
+ );
445
+ };
446
+
447
+ export default UpgradePage;
448
+
@@ -1,3 +1,4 @@
1
1
  const pluginId = 'magic-sessionmanager';
2
2
 
3
+ export const PLUGIN_ID = pluginId;
3
4
  export default pluginId;