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,1322 @@
1
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
+ import { useState, useEffect } from "react";
3
+ import { Flex, Loader, Typography, Button, Box, Badge, Accordion, Grid, SingleSelect, SingleSelectOption, Divider, Toggle, NumberInput, Checkbox, Tabs, TextInput } from "@strapi/design-system";
4
+ import { useFetchClient, useNotification } from "@strapi/strapi/admin";
5
+ import { Check, Information, Cog, Trash, Shield, Mail, Code } from "@strapi/icons";
6
+ import styled, { keyframes } from "styled-components";
7
+ import { a as pluginId } from "./index-BGCs2pNv.mjs";
8
+ import { u as useLicense } from "./useLicense-Dw_-SJHP.mjs";
9
+ const theme = {
10
+ colors: {
11
+ primary: { 600: "#0284C7", 700: "#075985" },
12
+ success: { 600: "#16A34A", 700: "#15803D" },
13
+ danger: { 600: "#DC2626" },
14
+ neutral: { 200: "#E5E7EB", 400: "#9CA3AF", 700: "#374151" }
15
+ },
16
+ shadows: { sm: "0 1px 3px rgba(0,0,0,0.1)" },
17
+ borderRadius: { md: "8px", lg: "12px" }
18
+ };
19
+ const fadeIn = keyframes`
20
+ from { opacity: 0; transform: translateY(10px); }
21
+ to { opacity: 1; transform: translateY(0); }
22
+ `;
23
+ keyframes`
24
+ 0% { background-position: -200% 0; }
25
+ 100% { background-position: 200% 0; }
26
+ `;
27
+ const Container = styled(Box)`
28
+ animation: ${fadeIn} 0.5s;
29
+ max-width: 1400px;
30
+ margin: 0 auto;
31
+ `;
32
+ const StickySaveBar = styled(Box)`
33
+ position: sticky;
34
+ top: 0;
35
+ z-index: 10;
36
+ background: white;
37
+ border-bottom: 1px solid ${theme.colors.neutral[200]};
38
+ box-shadow: ${theme.shadows.sm};
39
+ `;
40
+ const ToggleCard = styled(Box)`
41
+ background: ${(props) => props.$active ? "linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%)" : "linear-gradient(135deg, #fafafa 0%, #f3f4f6 100%)"};
42
+ border-radius: ${theme.borderRadius.lg};
43
+ padding: 24px;
44
+ min-height: 120px;
45
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
46
+ border: 2px solid ${(props) => props.$active ? theme.colors.success[600] : theme.colors.neutral[200]};
47
+ box-shadow: ${(props) => props.$active ? "0 4px 20px rgba(34, 197, 94, 0.15)" : "0 2px 8px rgba(0, 0, 0, 0.06)"};
48
+ position: relative;
49
+ cursor: pointer;
50
+ display: flex;
51
+ align-items: center;
52
+
53
+ &:hover {
54
+ transform: translateY(-4px);
55
+ box-shadow: ${(props) => props.$active ? "0 8px 30px rgba(34, 197, 94, 0.25)" : "0 6px 16px rgba(0, 0, 0, 0.12)"};
56
+ border-color: ${(props) => props.$active ? theme.colors.success[700] : theme.colors.neutral[300]};
57
+ }
58
+
59
+ &:active {
60
+ transform: translateY(-2px);
61
+ }
62
+
63
+ ${(props) => props.$active && `
64
+ &::before {
65
+ content: 'ACTIVE';
66
+ position: absolute;
67
+ top: 12px;
68
+ right: 12px;
69
+ background: ${theme.colors.success[600]};
70
+ color: white;
71
+ padding: 4px 10px;
72
+ border-radius: 6px;
73
+ font-size: 10px;
74
+ font-weight: 700;
75
+ letter-spacing: 0.5px;
76
+ box-shadow: 0 2px 6px rgba(22, 163, 74, 0.3);
77
+ }
78
+ `}
79
+
80
+ ${(props) => !props.$active && `
81
+ &::before {
82
+ content: 'INACTIVE';
83
+ position: absolute;
84
+ top: 12px;
85
+ right: 12px;
86
+ background: ${theme.colors.neutral[400]};
87
+ color: white;
88
+ padding: 4px 10px;
89
+ border-radius: 6px;
90
+ font-size: 10px;
91
+ font-weight: 700;
92
+ letter-spacing: 0.5px;
93
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
94
+ }
95
+ `}
96
+ `;
97
+ const GreenToggle = styled.div`
98
+ ${(props) => props.$isActive && `
99
+ button[role="switch"] {
100
+ background-color: #16A34A !important;
101
+ border-color: #16A34A !important;
102
+
103
+ &:hover {
104
+ background-color: #15803D !important;
105
+ border-color: #15803D !important;
106
+ }
107
+
108
+ &:focus {
109
+ background-color: #16A34A !important;
110
+ border-color: #16A34A !important;
111
+ box-shadow: 0 0 0 3px rgba(22, 163, 74, 0.2) !important;
112
+ }
113
+ }
114
+
115
+ /* Toggle handle */
116
+ button[role="switch"] > span {
117
+ background-color: white !important;
118
+ }
119
+ `}
120
+
121
+ ${(props) => !props.$isActive && `
122
+ button[role="switch"] {
123
+ background-color: #E5E7EB;
124
+
125
+ &:hover {
126
+ background-color: #D1D5DB;
127
+ }
128
+ }
129
+ `}
130
+ `;
131
+ const TEMPLATE_VARIABLES = {
132
+ suspiciousLogin: [
133
+ { var: "{{user.email}}", desc: "User email address" },
134
+ { var: "{{user.username}}", desc: "Username" },
135
+ { var: "{{session.loginTime}}", desc: "Login timestamp" },
136
+ { var: "{{session.ipAddress}}", desc: "IP address" },
137
+ { var: "{{geo.city}}", desc: "City (if available)" },
138
+ { var: "{{geo.country}}", desc: "Country (if available)" },
139
+ { var: "{{geo.timezone}}", desc: "Timezone (if available)" },
140
+ { var: "{{session.userAgent}}", desc: "Browser/Device info" },
141
+ { var: "{{reason.isVpn}}", desc: "VPN detected (true/false)" },
142
+ { var: "{{reason.isProxy}}", desc: "Proxy detected (true/false)" },
143
+ { var: "{{reason.isThreat}}", desc: "Threat detected (true/false)" },
144
+ { var: "{{reason.securityScore}}", desc: "Security score (0-100)" }
145
+ ],
146
+ newLocation: [
147
+ { var: "{{user.email}}", desc: "User email address" },
148
+ { var: "{{user.username}}", desc: "Username" },
149
+ { var: "{{session.loginTime}}", desc: "Login timestamp" },
150
+ { var: "{{session.ipAddress}}", desc: "IP address" },
151
+ { var: "{{geo.city}}", desc: "City" },
152
+ { var: "{{geo.country}}", desc: "Country" },
153
+ { var: "{{geo.timezone}}", desc: "Timezone" },
154
+ { var: "{{session.userAgent}}", desc: "Browser/Device info" }
155
+ ],
156
+ vpnProxy: [
157
+ { var: "{{user.email}}", desc: "User email address" },
158
+ { var: "{{user.username}}", desc: "Username" },
159
+ { var: "{{session.loginTime}}", desc: "Login timestamp" },
160
+ { var: "{{session.ipAddress}}", desc: "IP address" },
161
+ { var: "{{geo.city}}", desc: "City (if available)" },
162
+ { var: "{{geo.country}}", desc: "Country (if available)" },
163
+ { var: "{{session.userAgent}}", desc: "Browser/Device info" },
164
+ { var: "{{reason.isVpn}}", desc: "VPN detected (true/false)" },
165
+ { var: "{{reason.isProxy}}", desc: "Proxy detected (true/false)" }
166
+ ]
167
+ };
168
+ const validateTemplate = (template, templateType) => {
169
+ const requiredVars = TEMPLATE_VARIABLES[templateType];
170
+ const foundVars = [];
171
+ requiredVars.forEach(({ var: variable }) => {
172
+ if (template.includes(variable)) {
173
+ foundVars.push(variable);
174
+ }
175
+ });
176
+ return {
177
+ isValid: foundVars.length > 0,
178
+ foundVars,
179
+ totalAvailable: requiredVars.length
180
+ };
181
+ };
182
+ const getDefaultTemplates = () => ({
183
+ suspiciousLogin: {
184
+ subject: "🚨 Suspicious Login Alert - Session Manager",
185
+ html: `
186
+ <html>
187
+ <body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
188
+ <div style="max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f9fafb; border-radius: 10px;">
189
+ <h2 style="color: #dc2626;">🚨 Suspicious Login Detected</h2>
190
+ <p>A potentially suspicious login was detected for your account.</p>
191
+
192
+ <div style="background: white; padding: 15px; border-radius: 8px; margin: 20px 0;">
193
+ <h3 style="margin-top: 0;">Account Information:</h3>
194
+ <ul>
195
+ <li><strong>Email:</strong> {{user.email}}</li>
196
+ <li><strong>Username:</strong> {{user.username}}</li>
197
+ </ul>
198
+
199
+ <h3>Login Details:</h3>
200
+ <ul>
201
+ <li><strong>Time:</strong> {{session.loginTime}}</li>
202
+ <li><strong>IP Address:</strong> {{session.ipAddress}}</li>
203
+ <li><strong>Location:</strong> {{geo.city}}, {{geo.country}}</li>
204
+ <li><strong>Timezone:</strong> {{geo.timezone}}</li>
205
+ <li><strong>Device:</strong> {{session.userAgent}}</li>
206
+ </ul>
207
+
208
+ <h3 style="color: #dc2626;">Security Alert:</h3>
209
+ <ul>
210
+ <li>VPN Detected: {{reason.isVpn}}</li>
211
+ <li>Proxy Detected: {{reason.isProxy}}</li>
212
+ <li>Threat Detected: {{reason.isThreat}}</li>
213
+ <li>Security Score: {{reason.securityScore}}/100</li>
214
+ </ul>
215
+ </div>
216
+
217
+ <p>If this was you, you can safely ignore this email. If you don't recognize this activity, please secure your account immediately.</p>
218
+
219
+ <hr style="border: none; border-top: 1px solid #e5e7eb; margin: 20px 0;"/>
220
+ <p style="color: #666; font-size: 12px;">This is an automated security notification from Magic Session Manager.</p>
221
+ </div>
222
+ </body>
223
+ </html>`,
224
+ text: `🚨 Suspicious Login Detected
225
+
226
+ A potentially suspicious login was detected for your account.
227
+
228
+ Account: {{user.email}}
229
+ Username: {{user.username}}
230
+
231
+ Login Details:
232
+ - Time: {{session.loginTime}}
233
+ - IP: {{session.ipAddress}}
234
+ - Location: {{geo.city}}, {{geo.country}}
235
+
236
+ Security: VPN={{reason.isVpn}}, Proxy={{reason.isProxy}}, Threat={{reason.isThreat}}, Score={{reason.securityScore}}/100`
237
+ },
238
+ newLocation: {
239
+ subject: "📍 New Location Login Detected",
240
+ html: `
241
+ <html>
242
+ <body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
243
+ <div style="max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f0f9ff; border-radius: 10px;">
244
+ <h2 style="color: #0284c7;">📍 Login from New Location</h2>
245
+ <p>Your account was accessed from a new location.</p>
246
+
247
+ <div style="background: white; padding: 15px; border-radius: 8px; margin: 20px 0;">
248
+ <h3 style="margin-top: 0;">Account:</h3>
249
+ <p><strong>{{user.email}}</strong></p>
250
+
251
+ <h3>New Location Details:</h3>
252
+ <ul>
253
+ <li><strong>Time:</strong> {{session.loginTime}}</li>
254
+ <li><strong>Location:</strong> {{geo.city}}, {{geo.country}}</li>
255
+ <li><strong>IP Address:</strong> {{session.ipAddress}}</li>
256
+ <li><strong>Device:</strong> {{session.userAgent}}</li>
257
+ </ul>
258
+ </div>
259
+
260
+ <p>If this was you, no action is needed. If you don't recognize this login, please secure your account.</p>
261
+
262
+ <hr style="border: none; border-top: 1px solid #e5e7eb; margin: 20px 0;"/>
263
+ <p style="color: #666; font-size: 12px;">Magic Session Manager notification</p>
264
+ </div>
265
+ </body>
266
+ </html>`,
267
+ text: `📍 Login from New Location
268
+
269
+ Your account was accessed from a new location.
270
+
271
+ Account: {{user.email}}
272
+
273
+ New Location Details:
274
+ - Time: {{session.loginTime}}
275
+ - Location: {{geo.city}}, {{geo.country}}
276
+ - IP Address: {{session.ipAddress}}
277
+ - Device: {{session.userAgent}}
278
+
279
+ If this was you, no action is needed.`
280
+ },
281
+ vpnProxy: {
282
+ subject: "⚠️ VPN/Proxy Login Detected",
283
+ html: `
284
+ <html>
285
+ <body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
286
+ <div style="max-width: 600px; margin: 0 auto; padding: 20px; background-color: #fffbeb; border-radius: 10px;">
287
+ <h2 style="color: #d97706;">⚠️ VPN/Proxy Detected</h2>
288
+ <p>A login from a VPN or proxy service was detected on your account.</p>
289
+
290
+ <div style="background: white; padding: 15px; border-radius: 8px; margin: 20px 0;">
291
+ <h3 style="margin-top: 0;">Account:</h3>
292
+ <p><strong>{{user.email}}</strong></p>
293
+
294
+ <h3>Login Details:</h3>
295
+ <ul>
296
+ <li><strong>Time:</strong> {{session.loginTime}}</li>
297
+ <li><strong>IP Address:</strong> {{session.ipAddress}}</li>
298
+ <li><strong>Location:</strong> {{geo.city}}, {{geo.country}}</li>
299
+ <li><strong>Device:</strong> {{session.userAgent}}</li>
300
+ <li><strong>VPN:</strong> {{reason.isVpn}}</li>
301
+ <li><strong>Proxy:</strong> {{reason.isProxy}}</li>
302
+ </ul>
303
+ </div>
304
+
305
+ <p>VPN/Proxy usage may indicate suspicious activity. If this was you, you can safely ignore this email.</p>
306
+
307
+ <hr style="border: none; border-top: 1px solid #e5e7eb; margin: 20px 0;"/>
308
+ <p style="color: #666; font-size: 12px;">Magic Session Manager notification</p>
309
+ </div>
310
+ </body>
311
+ </html>`,
312
+ text: `⚠️ VPN/Proxy Detected
313
+
314
+ A login from a VPN or proxy service was detected on your account.
315
+
316
+ Account: {{user.email}}
317
+
318
+ Login Details:
319
+ - Time: {{session.loginTime}}
320
+ - IP Address: {{session.ipAddress}}
321
+ - Location: {{geo.city}}, {{geo.country}}
322
+ - VPN: {{reason.isVpn}}, Proxy: {{reason.isProxy}}`
323
+ }
324
+ });
325
+ const SettingsPage = () => {
326
+ const { get, post, put } = useFetchClient();
327
+ const { toggleNotification } = useNotification();
328
+ const { isPremium, isAdvanced, isEnterprise } = useLicense();
329
+ const [loading, setLoading] = useState(true);
330
+ const [saving, setSaving] = useState(false);
331
+ const [hasChanges, setHasChanges] = useState(false);
332
+ const [cleaning, setCleaning] = useState(false);
333
+ const [activeTemplateTab, setActiveTemplateTab] = useState("suspiciousLogin");
334
+ const [settings, setSettings] = useState({
335
+ inactivityTimeout: 15,
336
+ cleanupInterval: 30,
337
+ lastSeenRateLimit: 30,
338
+ retentionDays: 90,
339
+ enableGeolocation: true,
340
+ enableSecurityScoring: true,
341
+ blockSuspiciousSessions: false,
342
+ maxFailedLogins: 5,
343
+ enableEmailAlerts: false,
344
+ alertOnSuspiciousLogin: true,
345
+ alertOnNewLocation: true,
346
+ alertOnVpnProxy: true,
347
+ enableWebhooks: false,
348
+ discordWebhookUrl: "",
349
+ slackWebhookUrl: "",
350
+ enableGeofencing: false,
351
+ allowedCountries: [],
352
+ blockedCountries: [],
353
+ emailTemplates: {
354
+ suspiciousLogin: { subject: "", html: "", text: "" },
355
+ newLocation: { subject: "", html: "", text: "" },
356
+ vpnProxy: { subject: "", html: "", text: "" }
357
+ }
358
+ });
359
+ useEffect(() => {
360
+ fetchSettings();
361
+ }, []);
362
+ const fetchSettings = async () => {
363
+ setLoading(true);
364
+ try {
365
+ const response = await get(`/${pluginId}/settings`);
366
+ if (response?.data?.settings) {
367
+ const loadedSettings = response.data.settings;
368
+ if (!loadedSettings.emailTemplates || Object.keys(loadedSettings.emailTemplates).length === 0) {
369
+ loadedSettings.emailTemplates = getDefaultTemplates();
370
+ } else {
371
+ const defaultTemplates = getDefaultTemplates();
372
+ Object.keys(defaultTemplates).forEach((key) => {
373
+ if (!loadedSettings.emailTemplates[key]) {
374
+ loadedSettings.emailTemplates[key] = defaultTemplates[key];
375
+ } else {
376
+ loadedSettings.emailTemplates[key] = {
377
+ subject: loadedSettings.emailTemplates[key].subject || defaultTemplates[key].subject,
378
+ html: loadedSettings.emailTemplates[key].html || defaultTemplates[key].html,
379
+ text: loadedSettings.emailTemplates[key].text || defaultTemplates[key].text
380
+ };
381
+ }
382
+ });
383
+ }
384
+ setSettings(loadedSettings);
385
+ } else {
386
+ setSettings((prev) => ({ ...prev, emailTemplates: getDefaultTemplates() }));
387
+ }
388
+ } catch (err) {
389
+ console.error("[Settings] Error loading from backend:", err);
390
+ toggleNotification({
391
+ type: "warning",
392
+ message: "Could not load settings from server. Using defaults."
393
+ });
394
+ setSettings((prev) => ({ ...prev, emailTemplates: getDefaultTemplates() }));
395
+ } finally {
396
+ setLoading(false);
397
+ }
398
+ };
399
+ const handleChange = (key, value) => {
400
+ setSettings((prev) => ({ ...prev, [key]: value }));
401
+ setHasChanges(true);
402
+ };
403
+ const handleSave = async () => {
404
+ setSaving(true);
405
+ try {
406
+ const response = await put(`/${pluginId}/settings`, settings);
407
+ if (response?.data?.success) {
408
+ toggleNotification({
409
+ type: "success",
410
+ message: "Settings saved successfully to database!"
411
+ });
412
+ setHasChanges(false);
413
+ try {
414
+ localStorage.setItem(`${pluginId}-settings`, JSON.stringify(settings));
415
+ } catch (localErr) {
416
+ console.warn("[Settings] Could not save to localStorage:", localErr);
417
+ }
418
+ } else {
419
+ throw new Error("Save failed");
420
+ }
421
+ } catch (err) {
422
+ console.error("[Settings] Error saving:", err);
423
+ toggleNotification({
424
+ type: "danger",
425
+ message: "Failed to save settings to server"
426
+ });
427
+ } finally {
428
+ setSaving(false);
429
+ }
430
+ };
431
+ const handleReset = () => {
432
+ fetchSettings();
433
+ setHasChanges(false);
434
+ };
435
+ const handleCleanInactive = async () => {
436
+ if (!confirm("⚠️ WARNING: This will permanently delete ALL inactive sessions.\n\nContinue?")) {
437
+ return;
438
+ }
439
+ setCleaning(true);
440
+ try {
441
+ const { data } = await post(`/${pluginId}/sessions/clean-inactive`);
442
+ toggleNotification({
443
+ type: "success",
444
+ message: `Successfully deleted ${data.deletedCount} inactive sessions!`
445
+ });
446
+ } catch (err) {
447
+ toggleNotification({
448
+ type: "danger",
449
+ message: "Failed to delete inactive sessions"
450
+ });
451
+ } finally {
452
+ setCleaning(false);
453
+ }
454
+ };
455
+ if (loading) {
456
+ return /* @__PURE__ */ jsx(Flex, { justifyContent: "center", padding: 8, children: /* @__PURE__ */ jsx(Loader, { children: "Loading settings..." }) });
457
+ }
458
+ return /* @__PURE__ */ jsxs(Container, { children: [
459
+ /* @__PURE__ */ jsx(StickySaveBar, { paddingTop: 5, paddingBottom: 5, paddingLeft: 6, paddingRight: 6, children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
460
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 1, alignItems: "flex-start", children: [
461
+ /* @__PURE__ */ jsx(Typography, { variant: "alpha", fontWeight: "bold", style: { fontSize: "24px" }, children: "⚙️ Session Manager Settings" }),
462
+ /* @__PURE__ */ jsx(Typography, { variant: "epsilon", textColor: "neutral600", children: "Configure session tracking, security, and email notifications" })
463
+ ] }),
464
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
465
+ hasChanges && /* @__PURE__ */ jsx(Button, { onClick: handleReset, variant: "tertiary", size: "L", children: "Reset" }),
466
+ /* @__PURE__ */ jsx(
467
+ Button,
468
+ {
469
+ onClick: handleSave,
470
+ loading: saving,
471
+ startIcon: /* @__PURE__ */ jsx(Check, {}),
472
+ size: "L",
473
+ disabled: !hasChanges || saving,
474
+ style: {
475
+ background: hasChanges && !saving ? "linear-gradient(135deg, #667eea 0%, #764ba2 100%)" : "#e5e7eb",
476
+ color: hasChanges && !saving ? "white" : "#9ca3af",
477
+ fontWeight: "600",
478
+ padding: "12px 24px",
479
+ border: "none",
480
+ boxShadow: hasChanges && !saving ? "0 4px 12px rgba(102, 126, 234, 0.4)" : "none",
481
+ transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
482
+ },
483
+ onMouseEnter: (e) => {
484
+ if (hasChanges && !saving) {
485
+ e.currentTarget.style.transform = "translateY(-2px)";
486
+ e.currentTarget.style.boxShadow = "0 8px 20px rgba(102, 126, 234, 0.5)";
487
+ }
488
+ },
489
+ onMouseLeave: (e) => {
490
+ e.currentTarget.style.transform = "translateY(0)";
491
+ e.currentTarget.style.boxShadow = hasChanges && !saving ? "0 4px 12px rgba(102, 126, 234, 0.4)" : "none";
492
+ },
493
+ children: saving ? "Saving..." : hasChanges ? "Save Changes" : "No Changes"
494
+ }
495
+ )
496
+ ] })
497
+ ] }) }),
498
+ /* @__PURE__ */ jsxs(Box, { paddingTop: 6, paddingLeft: 6, paddingRight: 6, paddingBottom: 10, children: [
499
+ /* @__PURE__ */ jsx(Box, { padding: 4, background: "primary50", hasRadius: true, style: { marginBottom: "24px", border: "1px solid #bae6fd" }, children: /* @__PURE__ */ jsxs(Flex, { gap: 3, alignItems: "center", children: [
500
+ /* @__PURE__ */ jsx(Information, { style: { width: "20px", height: "20px", color: "#0284C7" } }),
501
+ /* @__PURE__ */ jsxs(Box, { children: [
502
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", textColor: "primary700", style: { marginBottom: "4px" }, children: "Current License Status" }),
503
+ /* @__PURE__ */ jsxs(Flex, { gap: 3, children: [
504
+ /* @__PURE__ */ jsxs(Badge, { backgroundColor: isPremium ? "success100" : "neutral100", textColor: isPremium ? "success700" : "neutral600", children: [
505
+ isPremium ? "✓" : "✗",
506
+ " Premium"
507
+ ] }),
508
+ /* @__PURE__ */ jsxs(Badge, { backgroundColor: isAdvanced ? "primary100" : "neutral100", textColor: isAdvanced ? "primary700" : "neutral600", children: [
509
+ isAdvanced ? "✓" : "✗",
510
+ " Advanced"
511
+ ] }),
512
+ /* @__PURE__ */ jsxs(Badge, { backgroundColor: isEnterprise ? "secondary100" : "neutral100", textColor: isEnterprise ? "secondary700" : "neutral600", children: [
513
+ isEnterprise ? "✓" : "✗",
514
+ " Enterprise"
515
+ ] })
516
+ ] })
517
+ ] })
518
+ ] }) }),
519
+ /* @__PURE__ */ jsxs(Accordion.Root, { type: "multiple", defaultValue: ["general", "security", "email"], children: [
520
+ /* @__PURE__ */ jsxs(Accordion.Item, { value: "general", children: [
521
+ /* @__PURE__ */ jsx(Accordion.Header, { children: /* @__PURE__ */ jsx(
522
+ Accordion.Trigger,
523
+ {
524
+ icon: Cog,
525
+ description: "Basic session tracking configuration",
526
+ children: "General Settings"
527
+ }
528
+ ) }),
529
+ /* @__PURE__ */ jsx(Accordion.Content, { children: /* @__PURE__ */ jsxs(Box, { padding: 6, children: [
530
+ /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "16px", display: "block", color: theme.colors.neutral[700] }, children: "⏱️ SESSION TIMEOUT" }),
531
+ /* @__PURE__ */ jsxs(Grid.Root, { gap: 6, style: { marginBottom: "32px" }, children: [
532
+ /* @__PURE__ */ jsx(Grid.Item, { col: 6, s: 12, children: /* @__PURE__ */ jsxs(Box, { children: [
533
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: "Inactivity Timeout" }),
534
+ /* @__PURE__ */ jsxs(
535
+ SingleSelect,
536
+ {
537
+ value: String(settings.inactivityTimeout),
538
+ onChange: (value) => handleChange("inactivityTimeout", parseInt(value)),
539
+ children: [
540
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "5", children: "5 minutes (Very Strict)" }),
541
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "10", children: "10 minutes (Strict)" }),
542
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "15", children: "15 minutes (Recommended)" }),
543
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "30", children: "30 minutes (Moderate)" }),
544
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "60", children: "1 hour (Relaxed)" }),
545
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "120", children: "2 hours (Very Relaxed)" })
546
+ ]
547
+ }
548
+ ),
549
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "11px", marginTop: "8px" }, children: [
550
+ "Sessions inactive for more than ",
551
+ settings.inactivityTimeout,
552
+ " minutes will be marked as offline"
553
+ ] })
554
+ ] }) }),
555
+ /* @__PURE__ */ jsx(Grid.Item, { col: 6, s: 12, children: /* @__PURE__ */ jsxs(Box, { children: [
556
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: "Last Seen Rate Limit" }),
557
+ /* @__PURE__ */ jsxs(
558
+ SingleSelect,
559
+ {
560
+ value: String(settings.lastSeenRateLimit),
561
+ onChange: (value) => handleChange("lastSeenRateLimit", parseInt(value)),
562
+ children: [
563
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "10", children: "10 seconds" }),
564
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "30", children: "30 seconds (Recommended)" }),
565
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "60", children: "1 minute" }),
566
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "120", children: "2 minutes" }),
567
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "300", children: "5 minutes" })
568
+ ]
569
+ }
570
+ ),
571
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "11px", marginTop: "8px" }, children: [
572
+ "Prevents excessive database writes. Updates throttled to once every ",
573
+ settings.lastSeenRateLimit,
574
+ " seconds"
575
+ ] })
576
+ ] }) })
577
+ ] }),
578
+ /* @__PURE__ */ jsx(Divider, { style: { marginBottom: "24px" } }),
579
+ /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "16px", display: "block", color: theme.colors.neutral[700] }, children: "🧹 AUTO-CLEANUP & RETENTION" }),
580
+ /* @__PURE__ */ jsxs(Grid.Root, { gap: 6, children: [
581
+ /* @__PURE__ */ jsx(Grid.Item, { col: 6, s: 12, children: /* @__PURE__ */ jsxs(Box, { children: [
582
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: "Cleanup Interval" }),
583
+ /* @__PURE__ */ jsxs(
584
+ SingleSelect,
585
+ {
586
+ value: String(settings.cleanupInterval),
587
+ onChange: (value) => handleChange("cleanupInterval", parseInt(value)),
588
+ children: [
589
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "15", children: "15 minutes" }),
590
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "30", children: "30 minutes (Recommended)" }),
591
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "60", children: "1 hour" }),
592
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "120", children: "2 hours" })
593
+ ]
594
+ }
595
+ ),
596
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "11px", marginTop: "8px" }, children: [
597
+ "Inactive sessions are automatically cleaned every ",
598
+ settings.cleanupInterval,
599
+ " minutes"
600
+ ] })
601
+ ] }) }),
602
+ /* @__PURE__ */ jsx(Grid.Item, { col: 6, s: 12, children: /* @__PURE__ */ jsxs(Box, { children: [
603
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: "Retention Period" }),
604
+ /* @__PURE__ */ jsxs(
605
+ SingleSelect,
606
+ {
607
+ value: String(settings.retentionDays),
608
+ onChange: (value) => handleChange("retentionDays", parseInt(value)),
609
+ children: [
610
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "7", children: "7 days" }),
611
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "30", children: "30 days" }),
612
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "60", children: "60 days" }),
613
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "90", children: "90 days (Recommended)" }),
614
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "180", children: "180 days" }),
615
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "365", children: "1 year" }),
616
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "-1", children: "Forever" })
617
+ ]
618
+ }
619
+ ),
620
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "11px", marginTop: "8px" }, children: [
621
+ "Old sessions deleted after ",
622
+ settings.retentionDays === -1 ? "never" : `${settings.retentionDays} days`
623
+ ] })
624
+ ] }) }),
625
+ /* @__PURE__ */ jsx(Grid.Item, { col: 12, children: /* @__PURE__ */ jsx(Box, { padding: 4, background: "danger100", style: { borderRadius: theme.borderRadius.md, border: `2px solid ${theme.colors.danger[200]}` }, children: /* @__PURE__ */ jsxs(Flex, { gap: 3, alignItems: "flex-start", children: [
626
+ /* @__PURE__ */ jsx(Trash, { style: { width: "18px", height: "18px", color: theme.colors.danger[600], flexShrink: 0, marginTop: "2px" } }),
627
+ /* @__PURE__ */ jsxs(Box, { style: { flex: 1 }, children: [
628
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", textColor: "danger700", style: { marginBottom: "8px", display: "block" }, children: "Danger Zone" }),
629
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "danger600", style: { fontSize: "13px", lineHeight: "1.7" }, children: [
630
+ /* @__PURE__ */ jsx("strong", { children: "Clean All Inactive:" }),
631
+ " Permanently deletes all inactive sessions. This cannot be undone."
632
+ ] })
633
+ ] }),
634
+ /* @__PURE__ */ jsx(
635
+ Button,
636
+ {
637
+ onClick: handleCleanInactive,
638
+ loading: cleaning,
639
+ startIcon: /* @__PURE__ */ jsx(Trash, {}),
640
+ variant: "danger",
641
+ size: "S",
642
+ style: { flexShrink: 0 },
643
+ children: "Clean Now"
644
+ }
645
+ )
646
+ ] }) }) })
647
+ ] })
648
+ ] }) })
649
+ ] }),
650
+ /* @__PURE__ */ jsxs(Accordion.Item, { value: "security", children: [
651
+ /* @__PURE__ */ jsx(Accordion.Header, { children: /* @__PURE__ */ jsx(
652
+ Accordion.Trigger,
653
+ {
654
+ icon: Shield,
655
+ description: "Security policies and threat protection",
656
+ children: "Security Settings"
657
+ }
658
+ ) }),
659
+ /* @__PURE__ */ jsx(Accordion.Content, { children: /* @__PURE__ */ jsxs(Box, { padding: 6, children: [
660
+ /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "16px", display: "block", color: theme.colors.neutral[700] }, children: "🔒 SECURITY OPTIONS" }),
661
+ /* @__PURE__ */ jsx(Box, { background: "neutral100", padding: 5, style: { borderRadius: theme.borderRadius.md, marginBottom: "32px" }, children: /* @__PURE__ */ jsxs(Grid.Root, { gap: 4, children: [
662
+ /* @__PURE__ */ jsx(Grid.Item, { col: 6, s: 12, children: /* @__PURE__ */ jsx(
663
+ ToggleCard,
664
+ {
665
+ $active: settings.blockSuspiciousSessions,
666
+ onClick: () => handleChange("blockSuspiciousSessions", !settings.blockSuspiciousSessions),
667
+ children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, style: { width: "100%" }, alignItems: "center", children: [
668
+ /* @__PURE__ */ jsx(GreenToggle, { $isActive: settings.blockSuspiciousSessions, children: /* @__PURE__ */ jsx(
669
+ Toggle,
670
+ {
671
+ checked: settings.blockSuspiciousSessions,
672
+ onChange: () => handleChange("blockSuspiciousSessions", !settings.blockSuspiciousSessions)
673
+ }
674
+ ) }),
675
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, alignItems: "center", style: { textAlign: "center" }, children: [
676
+ /* @__PURE__ */ jsx(
677
+ Typography,
678
+ {
679
+ variant: "delta",
680
+ fontWeight: "bold",
681
+ textColor: settings.blockSuspiciousSessions ? "success700" : "neutral800",
682
+ style: { fontSize: "16px" },
683
+ children: "Block Suspicious Sessions"
684
+ }
685
+ ),
686
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "13px", lineHeight: "1.6" }, children: "Automatically block sessions from VPNs, proxies, or threat IPs" })
687
+ ] })
688
+ ] })
689
+ }
690
+ ) }),
691
+ isPremium && /* @__PURE__ */ jsxs(Fragment, { children: [
692
+ /* @__PURE__ */ jsx(Grid.Item, { col: 6, s: 12, children: /* @__PURE__ */ jsx(
693
+ ToggleCard,
694
+ {
695
+ $active: settings.enableGeolocation,
696
+ onClick: () => handleChange("enableGeolocation", !settings.enableGeolocation),
697
+ children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, style: { width: "100%" }, alignItems: "center", children: [
698
+ /* @__PURE__ */ jsx(GreenToggle, { $isActive: settings.enableGeolocation, children: /* @__PURE__ */ jsx(
699
+ Toggle,
700
+ {
701
+ checked: settings.enableGeolocation,
702
+ onChange: () => handleChange("enableGeolocation", !settings.enableGeolocation)
703
+ }
704
+ ) }),
705
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, alignItems: "center", style: { textAlign: "center" }, children: [
706
+ /* @__PURE__ */ jsx(
707
+ Typography,
708
+ {
709
+ variant: "delta",
710
+ fontWeight: "bold",
711
+ textColor: settings.enableGeolocation ? "success700" : "neutral800",
712
+ style: { fontSize: "16px" },
713
+ children: "IP Geolocation"
714
+ }
715
+ ),
716
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "13px", lineHeight: "1.6" }, children: "Fetch location data for each session (Premium)" })
717
+ ] })
718
+ ] })
719
+ }
720
+ ) }),
721
+ /* @__PURE__ */ jsx(Grid.Item, { col: 6, s: 12, children: /* @__PURE__ */ jsx(
722
+ ToggleCard,
723
+ {
724
+ $active: settings.enableSecurityScoring,
725
+ onClick: () => handleChange("enableSecurityScoring", !settings.enableSecurityScoring),
726
+ children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, style: { width: "100%" }, alignItems: "center", children: [
727
+ /* @__PURE__ */ jsx(GreenToggle, { $isActive: settings.enableSecurityScoring, children: /* @__PURE__ */ jsx(
728
+ Toggle,
729
+ {
730
+ checked: settings.enableSecurityScoring,
731
+ onChange: () => handleChange("enableSecurityScoring", !settings.enableSecurityScoring)
732
+ }
733
+ ) }),
734
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, alignItems: "center", style: { textAlign: "center" }, children: [
735
+ /* @__PURE__ */ jsx(
736
+ Typography,
737
+ {
738
+ variant: "delta",
739
+ fontWeight: "bold",
740
+ textColor: settings.enableSecurityScoring ? "success700" : "neutral800",
741
+ style: { fontSize: "16px" },
742
+ children: "Security Scoring"
743
+ }
744
+ ),
745
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "13px", lineHeight: "1.6" }, children: "Calculate security scores and detect threats (Premium)" })
746
+ ] })
747
+ ] })
748
+ }
749
+ ) })
750
+ ] })
751
+ ] }) }),
752
+ /* @__PURE__ */ jsx(Grid.Root, { gap: 6, children: /* @__PURE__ */ jsx(Grid.Item, { col: 6, s: 12, children: /* @__PURE__ */ jsxs(Box, { children: [
753
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: "🚫 Max Failed Login Attempts" }),
754
+ /* @__PURE__ */ jsx(
755
+ NumberInput,
756
+ {
757
+ value: settings.maxFailedLogins,
758
+ onValueChange: (val) => handleChange("maxFailedLogins", val),
759
+ min: 1,
760
+ max: 20
761
+ }
762
+ ),
763
+ /* @__PURE__ */ jsx(Box, { padding: 2, background: "warning50", style: { borderRadius: "4px", marginTop: "8px" }, children: /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "warning700", style: { fontSize: "11px" }, children: [
764
+ "User will be blocked after ",
765
+ settings.maxFailedLogins,
766
+ " failed attempts"
767
+ ] }) })
768
+ ] }) }) })
769
+ ] }) })
770
+ ] }),
771
+ isAdvanced && /* @__PURE__ */ jsxs(Accordion.Item, { value: "email", children: [
772
+ /* @__PURE__ */ jsx(Accordion.Header, { children: /* @__PURE__ */ jsx(
773
+ Accordion.Trigger,
774
+ {
775
+ icon: Mail,
776
+ description: "Email alerts for security events",
777
+ children: "Email Notifications (Advanced)"
778
+ }
779
+ ) }),
780
+ /* @__PURE__ */ jsx(Accordion.Content, { children: /* @__PURE__ */ jsxs(Box, { padding: 6, children: [
781
+ /* @__PURE__ */ jsxs(Box, { background: "neutral100", padding: 5, style: { borderRadius: theme.borderRadius.md, marginBottom: "32px" }, children: [
782
+ /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "8px", display: "block", textAlign: "center", color: theme.colors.neutral[700] }, children: "📧 EMAIL ALERTS" }),
783
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { marginBottom: "20px", display: "block", textAlign: "center", fontSize: "12px" }, children: "Send security alerts to users via email" }),
784
+ /* @__PURE__ */ jsx(Grid.Root, { gap: 4, children: /* @__PURE__ */ jsx(Grid.Item, { col: 12, children: /* @__PURE__ */ jsx(
785
+ ToggleCard,
786
+ {
787
+ $active: settings.enableEmailAlerts,
788
+ onClick: () => handleChange("enableEmailAlerts", !settings.enableEmailAlerts),
789
+ children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, style: { width: "100%" }, alignItems: "center", children: [
790
+ /* @__PURE__ */ jsx(GreenToggle, { $isActive: settings.enableEmailAlerts, children: /* @__PURE__ */ jsx(
791
+ Toggle,
792
+ {
793
+ checked: settings.enableEmailAlerts,
794
+ onChange: () => handleChange("enableEmailAlerts", !settings.enableEmailAlerts)
795
+ }
796
+ ) }),
797
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, alignItems: "center", style: { textAlign: "center" }, children: [
798
+ /* @__PURE__ */ jsx(
799
+ Typography,
800
+ {
801
+ variant: "delta",
802
+ fontWeight: "bold",
803
+ textColor: settings.enableEmailAlerts ? "success700" : "neutral800",
804
+ style: { fontSize: "16px" },
805
+ children: "Enable Email Alerts"
806
+ }
807
+ ),
808
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "13px", lineHeight: "1.6" }, children: "Send security alerts for suspicious logins, new locations, and VPN/Proxy usage" })
809
+ ] })
810
+ ] })
811
+ }
812
+ ) }) })
813
+ ] }),
814
+ settings.enableEmailAlerts && /* @__PURE__ */ jsxs(Fragment, { children: [
815
+ /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "16px", display: "block", color: theme.colors.neutral[700] }, children: "⚙️ ALERT TYPES" }),
816
+ /* @__PURE__ */ jsxs(Grid.Root, { gap: 4, style: { marginBottom: "32px" }, children: [
817
+ /* @__PURE__ */ jsx(Grid.Item, { col: 4, s: 12, children: /* @__PURE__ */ jsx(
818
+ Box,
819
+ {
820
+ padding: 4,
821
+ background: settings.alertOnSuspiciousLogin ? "danger50" : "neutral50",
822
+ style: {
823
+ borderRadius: theme.borderRadius.md,
824
+ border: `2px solid ${settings.alertOnSuspiciousLogin ? "#fecaca" : "#E5E7EB"}`,
825
+ transition: "all 0.2s",
826
+ cursor: "pointer"
827
+ },
828
+ onClick: () => handleChange("alertOnSuspiciousLogin", !settings.alertOnSuspiciousLogin),
829
+ children: /* @__PURE__ */ jsx(
830
+ Checkbox,
831
+ {
832
+ checked: settings.alertOnSuspiciousLogin,
833
+ onChange: () => handleChange("alertOnSuspiciousLogin", !settings.alertOnSuspiciousLogin),
834
+ children: /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "semiBold", style: { fontSize: "14px" }, children: "🚨 Suspicious Login" })
835
+ }
836
+ )
837
+ }
838
+ ) }),
839
+ /* @__PURE__ */ jsx(Grid.Item, { col: 4, s: 12, children: /* @__PURE__ */ jsx(
840
+ Box,
841
+ {
842
+ padding: 4,
843
+ background: settings.alertOnNewLocation ? "primary50" : "neutral50",
844
+ style: {
845
+ borderRadius: theme.borderRadius.md,
846
+ border: `2px solid ${settings.alertOnNewLocation ? "#bae6fd" : "#E5E7EB"}`,
847
+ transition: "all 0.2s",
848
+ cursor: "pointer"
849
+ },
850
+ onClick: () => handleChange("alertOnNewLocation", !settings.alertOnNewLocation),
851
+ children: /* @__PURE__ */ jsx(
852
+ Checkbox,
853
+ {
854
+ checked: settings.alertOnNewLocation,
855
+ onChange: () => handleChange("alertOnNewLocation", !settings.alertOnNewLocation),
856
+ children: /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "semiBold", style: { fontSize: "14px" }, children: "📍 New Location" })
857
+ }
858
+ )
859
+ }
860
+ ) }),
861
+ /* @__PURE__ */ jsx(Grid.Item, { col: 4, s: 12, children: /* @__PURE__ */ jsx(
862
+ Box,
863
+ {
864
+ padding: 4,
865
+ background: settings.alertOnVpnProxy ? "warning50" : "neutral50",
866
+ style: {
867
+ borderRadius: theme.borderRadius.md,
868
+ border: `2px solid ${settings.alertOnVpnProxy ? "#fde68a" : "#E5E7EB"}`,
869
+ transition: "all 0.2s",
870
+ cursor: "pointer"
871
+ },
872
+ onClick: () => handleChange("alertOnVpnProxy", !settings.alertOnVpnProxy),
873
+ children: /* @__PURE__ */ jsx(
874
+ Checkbox,
875
+ {
876
+ checked: settings.alertOnVpnProxy,
877
+ onChange: () => handleChange("alertOnVpnProxy", !settings.alertOnVpnProxy),
878
+ children: /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "semiBold", style: { fontSize: "14px" }, children: "⚠️ VPN/Proxy" })
879
+ }
880
+ )
881
+ }
882
+ ) })
883
+ ] }),
884
+ /* @__PURE__ */ jsx(Divider, { style: { marginBottom: "24px" } }),
885
+ /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "8px", display: "block", color: theme.colors.neutral[700] }, children: "📝 EMAIL TEMPLATES" }),
886
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { marginBottom: "20px", display: "block", fontSize: "12px" }, children: "Customize email notification templates with dynamic variables" }),
887
+ /* @__PURE__ */ jsxs(Tabs.Root, { value: activeTemplateTab, onValueChange: setActiveTemplateTab, children: [
888
+ /* @__PURE__ */ jsxs(Tabs.List, { "aria-label": "Email Templates", children: [
889
+ /* @__PURE__ */ jsx(Tabs.Trigger, { value: "suspiciousLogin", children: "🚨 Suspicious Login" }),
890
+ /* @__PURE__ */ jsx(Tabs.Trigger, { value: "newLocation", children: "📍 New Location" }),
891
+ /* @__PURE__ */ jsx(Tabs.Trigger, { value: "vpnProxy", children: "⚠️ VPN/Proxy" })
892
+ ] }),
893
+ Object.keys(settings.emailTemplates).map((templateKey) => /* @__PURE__ */ jsx(Tabs.Content, { value: templateKey, children: /* @__PURE__ */ jsxs(Box, { paddingTop: 4, children: [
894
+ /* @__PURE__ */ jsxs(Box, { style: { marginBottom: "24px" }, children: [
895
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: "✉️ Email Subject" }),
896
+ /* @__PURE__ */ jsx(
897
+ TextInput,
898
+ {
899
+ value: settings.emailTemplates[templateKey].subject,
900
+ onChange: (e) => {
901
+ const newTemplates = { ...settings.emailTemplates };
902
+ newTemplates[templateKey].subject = e.target.value;
903
+ handleChange("emailTemplates", newTemplates);
904
+ },
905
+ placeholder: "Enter email subject..."
906
+ }
907
+ )
908
+ ] }),
909
+ /* @__PURE__ */ jsx(
910
+ Box,
911
+ {
912
+ padding: 3,
913
+ background: "primary100",
914
+ style: { borderRadius: theme.borderRadius.md, marginBottom: "20px", border: "2px solid #BAE6FD" },
915
+ children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
916
+ /* @__PURE__ */ jsxs(Flex, { alignItems: "center", gap: 2, children: [
917
+ /* @__PURE__ */ jsx(Code, { style: { width: "16px", height: "16px", color: theme.colors.primary[600] } }),
918
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", textColor: "primary600", children: "Available Variables (click to copy)" })
919
+ ] }),
920
+ /* @__PURE__ */ jsx(Flex, { gap: 2, wrap: "wrap", children: TEMPLATE_VARIABLES[templateKey].map(({ var: variable, desc }) => /* @__PURE__ */ jsx(
921
+ Button,
922
+ {
923
+ size: "S",
924
+ variant: "tertiary",
925
+ onClick: () => {
926
+ navigator.clipboard.writeText(variable);
927
+ toggleNotification({ type: "success", message: `${variable} copied!` });
928
+ },
929
+ style: {
930
+ fontFamily: "monospace",
931
+ fontSize: "11px",
932
+ padding: "4px 8px"
933
+ },
934
+ title: desc,
935
+ children: variable
936
+ },
937
+ variable
938
+ )) })
939
+ ] })
940
+ }
941
+ ),
942
+ /* @__PURE__ */ jsxs(
943
+ Box,
944
+ {
945
+ background: "neutral0",
946
+ padding: 6,
947
+ style: { borderRadius: theme.borderRadius.lg, border: "2px solid #E5E7EB", width: "100%", marginBottom: "24px" },
948
+ children: [
949
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", style: { marginBottom: "16px" }, children: [
950
+ /* @__PURE__ */ jsxs(Flex, { alignItems: "center", gap: 2, children: [
951
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "bold", style: { fontSize: "18px" }, children: "🎨 HTML Template" }),
952
+ /* @__PURE__ */ jsx(Badge, { variant: "success", children: "Main Template" })
953
+ ] }),
954
+ /* @__PURE__ */ jsx(
955
+ Button,
956
+ {
957
+ variant: "tertiary",
958
+ size: "S",
959
+ onClick: () => {
960
+ const defaultTemplates = getDefaultTemplates();
961
+ const newTemplates = { ...settings.emailTemplates };
962
+ newTemplates[templateKey].html = defaultTemplates[templateKey].html;
963
+ handleChange("emailTemplates", newTemplates);
964
+ toggleNotification({ type: "success", message: "Default HTML template loaded!" });
965
+ },
966
+ children: "📋 Load Default"
967
+ }
968
+ )
969
+ ] }),
970
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", style: { marginBottom: "16px", display: "block", fontSize: "14px" }, children: [
971
+ "HTML template for email notifications. Use variables like ",
972
+ /* @__PURE__ */ jsx("code", { children: "{{user.email}}" }),
973
+ " for dynamic content."
974
+ ] }),
975
+ /* @__PURE__ */ jsxs(
976
+ Box,
977
+ {
978
+ style: {
979
+ border: "2px solid #E5E7EB",
980
+ borderRadius: "6px",
981
+ overflow: "hidden",
982
+ background: "#1e1e1e",
983
+ height: "500px",
984
+ display: "flex",
985
+ flexDirection: "column"
986
+ },
987
+ children: [
988
+ /* @__PURE__ */ jsx(
989
+ Box,
990
+ {
991
+ padding: 2,
992
+ background: "neutral700",
993
+ style: { borderBottom: "1px solid #333", flexShrink: 0 },
994
+ children: /* @__PURE__ */ jsx(Typography, { variant: "omega", style: { color: "#888", fontSize: "11px", fontFamily: "monospace" }, children: "template.html" })
995
+ }
996
+ ),
997
+ /* @__PURE__ */ jsx(
998
+ "textarea",
999
+ {
1000
+ value: settings.emailTemplates[templateKey].html,
1001
+ onChange: (e) => {
1002
+ const newTemplates = { ...settings.emailTemplates };
1003
+ newTemplates[templateKey].html = e.target.value;
1004
+ handleChange("emailTemplates", newTemplates);
1005
+ },
1006
+ style: {
1007
+ fontFamily: 'Monaco, Consolas, "Courier New", monospace',
1008
+ height: "100%",
1009
+ fontSize: "14px",
1010
+ lineHeight: "1.8",
1011
+ background: "#1e1e1e",
1012
+ color: "#d4d4d4",
1013
+ border: "none",
1014
+ padding: "20px",
1015
+ resize: "none",
1016
+ width: "100%",
1017
+ boxSizing: "border-box",
1018
+ outline: "none",
1019
+ margin: 0,
1020
+ display: "block",
1021
+ overflow: "auto"
1022
+ },
1023
+ placeholder: "Enter HTML template with variables like {{user.email}}..."
1024
+ }
1025
+ )
1026
+ ]
1027
+ }
1028
+ ),
1029
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, style: { marginTop: "12px" }, wrap: "wrap", children: [
1030
+ /* @__PURE__ */ jsx(
1031
+ Button,
1032
+ {
1033
+ variant: "secondary",
1034
+ size: "S",
1035
+ onClick: () => {
1036
+ navigator.clipboard.writeText(settings.emailTemplates[templateKey].html);
1037
+ toggleNotification({ type: "success", message: "HTML template copied!" });
1038
+ },
1039
+ children: "📋 Copy Template"
1040
+ }
1041
+ ),
1042
+ /* @__PURE__ */ jsx(
1043
+ Button,
1044
+ {
1045
+ variant: "tertiary",
1046
+ size: "S",
1047
+ onClick: () => {
1048
+ const validation = validateTemplate(settings.emailTemplates[templateKey].html, templateKey);
1049
+ toggleNotification({
1050
+ type: validation.isValid ? "success" : "warning",
1051
+ message: validation.isValid ? `✓ Template valid! Found ${validation.foundVars.length}/${validation.totalAvailable} variables.` : "⚠️ No variables found. Add at least one variable."
1052
+ });
1053
+ },
1054
+ children: "✓ Validate"
1055
+ }
1056
+ ),
1057
+ /* @__PURE__ */ jsx(
1058
+ Button,
1059
+ {
1060
+ variant: "tertiary",
1061
+ size: "S",
1062
+ onClick: () => {
1063
+ const lines = settings.emailTemplates[templateKey].html.split("\n").length;
1064
+ const chars = settings.emailTemplates[templateKey].html.length;
1065
+ toggleNotification({
1066
+ type: "info",
1067
+ message: `Template has ${lines} lines and ${chars} characters`
1068
+ });
1069
+ },
1070
+ children: "ℹ️ Template Info"
1071
+ }
1072
+ )
1073
+ ] })
1074
+ ]
1075
+ }
1076
+ ),
1077
+ /* @__PURE__ */ jsxs(
1078
+ Box,
1079
+ {
1080
+ background: "neutral0",
1081
+ padding: 6,
1082
+ style: { borderRadius: theme.borderRadius.lg, border: "2px solid #E5E7EB", width: "100%", marginBottom: "24px" },
1083
+ children: [
1084
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", style: { marginBottom: "16px" }, children: [
1085
+ /* @__PURE__ */ jsxs(Flex, { alignItems: "center", gap: 2, children: [
1086
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "bold", style: { fontSize: "18px" }, children: "📄 Text Template" }),
1087
+ /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: "Fallback" })
1088
+ ] }),
1089
+ /* @__PURE__ */ jsx(
1090
+ Button,
1091
+ {
1092
+ variant: "tertiary",
1093
+ size: "S",
1094
+ onClick: () => {
1095
+ const defaultTemplates = getDefaultTemplates();
1096
+ const newTemplates = { ...settings.emailTemplates };
1097
+ newTemplates[templateKey].text = defaultTemplates[templateKey].text;
1098
+ handleChange("emailTemplates", newTemplates);
1099
+ toggleNotification({ type: "success", message: "Default text template loaded!" });
1100
+ },
1101
+ children: "📋 Load Default"
1102
+ }
1103
+ )
1104
+ ] }),
1105
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { marginBottom: "16px", display: "block", fontSize: "14px" }, children: "Plain text version (no HTML) as fallback for older email clients" }),
1106
+ /* @__PURE__ */ jsxs(
1107
+ Box,
1108
+ {
1109
+ style: {
1110
+ border: "2px solid #E5E7EB",
1111
+ borderRadius: "6px",
1112
+ overflow: "hidden",
1113
+ background: "#1e1e1e",
1114
+ height: "300px",
1115
+ display: "flex",
1116
+ flexDirection: "column"
1117
+ },
1118
+ children: [
1119
+ /* @__PURE__ */ jsx(
1120
+ Box,
1121
+ {
1122
+ padding: 2,
1123
+ background: "neutral700",
1124
+ style: { borderBottom: "1px solid #333", flexShrink: 0 },
1125
+ children: /* @__PURE__ */ jsx(Typography, { variant: "omega", style: { color: "#888", fontSize: "11px", fontFamily: "monospace" }, children: "template.txt" })
1126
+ }
1127
+ ),
1128
+ /* @__PURE__ */ jsx(
1129
+ "textarea",
1130
+ {
1131
+ value: settings.emailTemplates[templateKey].text,
1132
+ onChange: (e) => {
1133
+ const newTemplates = { ...settings.emailTemplates };
1134
+ newTemplates[templateKey].text = e.target.value;
1135
+ handleChange("emailTemplates", newTemplates);
1136
+ },
1137
+ style: {
1138
+ fontFamily: 'Monaco, Consolas, "Courier New", monospace',
1139
+ height: "100%",
1140
+ fontSize: "14px",
1141
+ lineHeight: "1.8",
1142
+ background: "#1e1e1e",
1143
+ color: "#d4d4d4",
1144
+ border: "none",
1145
+ padding: "20px",
1146
+ resize: "none",
1147
+ width: "100%",
1148
+ boxSizing: "border-box",
1149
+ outline: "none",
1150
+ margin: 0,
1151
+ display: "block",
1152
+ overflow: "auto"
1153
+ },
1154
+ placeholder: "Plain text version (no HTML)..."
1155
+ }
1156
+ )
1157
+ ]
1158
+ }
1159
+ ),
1160
+ /* @__PURE__ */ jsx(Flex, { gap: 2, style: { marginTop: "12px" }, wrap: "wrap", children: /* @__PURE__ */ jsx(
1161
+ Button,
1162
+ {
1163
+ variant: "secondary",
1164
+ size: "S",
1165
+ onClick: () => {
1166
+ navigator.clipboard.writeText(settings.emailTemplates[templateKey].text);
1167
+ toggleNotification({ type: "success", message: "Text template copied!" });
1168
+ },
1169
+ children: "📋 Copy Template"
1170
+ }
1171
+ ) })
1172
+ ]
1173
+ }
1174
+ )
1175
+ ] }) }, templateKey))
1176
+ ] })
1177
+ ] })
1178
+ ] }) })
1179
+ ] }),
1180
+ isAdvanced && /* @__PURE__ */ jsxs(Accordion.Item, { value: "webhooks", children: [
1181
+ /* @__PURE__ */ jsx(Accordion.Header, { children: /* @__PURE__ */ jsx(
1182
+ Accordion.Trigger,
1183
+ {
1184
+ icon: Code,
1185
+ description: "Discord & Slack integration",
1186
+ children: "Webhook Integration (Advanced)"
1187
+ }
1188
+ ) }),
1189
+ /* @__PURE__ */ jsx(Accordion.Content, { children: /* @__PURE__ */ jsxs(Box, { padding: 6, children: [
1190
+ /* @__PURE__ */ jsx(Box, { background: "neutral100", padding: 5, style: { borderRadius: theme.borderRadius.md, marginBottom: "32px" }, children: /* @__PURE__ */ jsx(Grid.Root, { gap: 4, children: /* @__PURE__ */ jsx(Grid.Item, { col: 12, children: /* @__PURE__ */ jsx(
1191
+ ToggleCard,
1192
+ {
1193
+ $active: settings.enableWebhooks,
1194
+ onClick: () => handleChange("enableWebhooks", !settings.enableWebhooks),
1195
+ children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, style: { width: "100%" }, alignItems: "center", children: [
1196
+ /* @__PURE__ */ jsx(GreenToggle, { $isActive: settings.enableWebhooks, children: /* @__PURE__ */ jsx(
1197
+ Toggle,
1198
+ {
1199
+ checked: settings.enableWebhooks,
1200
+ onChange: () => handleChange("enableWebhooks", !settings.enableWebhooks)
1201
+ }
1202
+ ) }),
1203
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, alignItems: "center", style: { textAlign: "center" }, children: [
1204
+ /* @__PURE__ */ jsx(
1205
+ Typography,
1206
+ {
1207
+ variant: "delta",
1208
+ fontWeight: "bold",
1209
+ textColor: settings.enableWebhooks ? "success700" : "neutral800",
1210
+ style: { fontSize: "16px" },
1211
+ children: "Enable Webhooks"
1212
+ }
1213
+ ),
1214
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "13px", lineHeight: "1.6" }, children: "Send session events to Discord, Slack, or custom endpoints" })
1215
+ ] })
1216
+ ] })
1217
+ }
1218
+ ) }) }) }),
1219
+ settings.enableWebhooks && /* @__PURE__ */ jsxs(Grid.Root, { gap: 6, children: [
1220
+ /* @__PURE__ */ jsx(Grid.Item, { col: 12, children: /* @__PURE__ */ jsxs(Box, { children: [
1221
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "12px", display: "block" }, children: "🔗 Discord Webhook URL" }),
1222
+ /* @__PURE__ */ jsx(
1223
+ Box,
1224
+ {
1225
+ style: {
1226
+ border: "2px solid #E5E7EB",
1227
+ borderRadius: theme.borderRadius.md,
1228
+ overflow: "hidden",
1229
+ background: "#fafafa"
1230
+ },
1231
+ children: /* @__PURE__ */ jsx(
1232
+ "textarea",
1233
+ {
1234
+ placeholder: "https://discord.com/api/webhooks/123456789/abcdefghijklmnopqrstuvwxyz...",
1235
+ value: settings.discordWebhookUrl,
1236
+ onChange: (e) => handleChange("discordWebhookUrl", e.target.value),
1237
+ rows: 3,
1238
+ style: {
1239
+ width: "100%",
1240
+ padding: "14px 18px",
1241
+ border: "none",
1242
+ outline: "none",
1243
+ fontFamily: "Monaco, Consolas, monospace",
1244
+ fontSize: "14px",
1245
+ lineHeight: "1.8",
1246
+ color: "#374151",
1247
+ background: "white",
1248
+ resize: "vertical",
1249
+ minHeight: "80px"
1250
+ }
1251
+ }
1252
+ )
1253
+ }
1254
+ ),
1255
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", style: { marginTop: "10px" }, children: [
1256
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "12px" }, children: "💡 Optional: Post session alerts to your Discord channel" }),
1257
+ settings.discordWebhookUrl && /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "primary600", style: { fontSize: "11px", fontFamily: "monospace" }, children: [
1258
+ settings.discordWebhookUrl.length,
1259
+ " characters"
1260
+ ] })
1261
+ ] })
1262
+ ] }) }),
1263
+ /* @__PURE__ */ jsx(Grid.Item, { col: 12, children: /* @__PURE__ */ jsxs(Box, { children: [
1264
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "12px", display: "block" }, children: "💬 Slack Webhook URL" }),
1265
+ /* @__PURE__ */ jsx(
1266
+ Box,
1267
+ {
1268
+ style: {
1269
+ border: "2px solid #E5E7EB",
1270
+ borderRadius: theme.borderRadius.md,
1271
+ overflow: "hidden",
1272
+ background: "#fafafa"
1273
+ },
1274
+ children: /* @__PURE__ */ jsx(
1275
+ "textarea",
1276
+ {
1277
+ placeholder: "https://hooks.slack.com/services/XXXX/XXXX/XXXX",
1278
+ value: settings.slackWebhookUrl,
1279
+ onChange: (e) => handleChange("slackWebhookUrl", e.target.value),
1280
+ rows: 3,
1281
+ style: {
1282
+ width: "100%",
1283
+ padding: "14px 18px",
1284
+ border: "none",
1285
+ outline: "none",
1286
+ fontFamily: "Monaco, Consolas, monospace",
1287
+ fontSize: "14px",
1288
+ lineHeight: "1.8",
1289
+ color: "#374151",
1290
+ background: "white",
1291
+ resize: "vertical",
1292
+ minHeight: "80px"
1293
+ }
1294
+ }
1295
+ )
1296
+ }
1297
+ ),
1298
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", style: { marginTop: "10px" }, children: [
1299
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "12px" }, children: "💡 Optional: Post session alerts to your Slack workspace" }),
1300
+ settings.slackWebhookUrl && /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "primary600", style: { fontSize: "11px", fontFamily: "monospace" }, children: [
1301
+ settings.slackWebhookUrl.length,
1302
+ " characters"
1303
+ ] })
1304
+ ] })
1305
+ ] }) })
1306
+ ] })
1307
+ ] }) })
1308
+ ] })
1309
+ ] }),
1310
+ /* @__PURE__ */ jsx(Box, { padding: 5, background: "primary100", style: { borderRadius: theme.borderRadius.md, marginTop: "32px", border: "2px solid #BAE6FD" }, children: /* @__PURE__ */ jsxs(Flex, { gap: 3, alignItems: "flex-start", children: [
1311
+ /* @__PURE__ */ jsx(Check, { style: { width: "20px", height: "20px", color: theme.colors.success[600], flexShrink: 0, marginTop: "2px" } }),
1312
+ /* @__PURE__ */ jsxs(Box, { style: { flex: 1 }, children: [
1313
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", style: { marginBottom: "8px", display: "block", color: theme.colors.primary[700] }, children: "✨ Database-Backed Settings" }),
1314
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "primary700", style: { fontSize: "13px", lineHeight: "1.8" }, children: "All settings are stored in your Strapi database and shared across all admin users. Changes take effect immediately - no server restart required! Email templates, webhooks, and security options are all managed from this interface." })
1315
+ ] })
1316
+ ] }) })
1317
+ ] })
1318
+ ] });
1319
+ };
1320
+ export {
1321
+ SettingsPage as default
1322
+ };