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,11 +1,12 @@
1
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
2
  import { useState, useEffect } from "react";
3
+ import { useIntl } from "react-intl";
3
4
  import { Flex, Loader, Typography, Button, Box, Badge, Accordion, Grid, SingleSelect, SingleSelectOption, Divider, Alert, TextInput, Toggle, NumberInput, Checkbox, Tabs } from "@strapi/design-system";
4
5
  import { useFetchClient, useNotification } from "@strapi/strapi/admin";
5
6
  import { Check, Information, Cog, Trash, Shield, Code, Duplicate, Mail } from "@strapi/icons";
6
7
  import styled, { css, keyframes } from "styled-components";
7
- import { a as pluginId } from "./index-DBRS3kt5.mjs";
8
- import { u as useLicense } from "./useLicense-DSLL9n3Y.mjs";
8
+ import { a as pluginId, g as getTranslation } from "./index-DQO9bNP7.mjs";
9
+ import { u as useLicense } from "./useLicense-DxbD4Wf8.mjs";
9
10
  const theme = {
10
11
  colors: {
11
12
  primary: { 600: "#0284C7", 700: "#075985", 100: "#E0F2FE", 50: "#F0F9FF" },
@@ -13,7 +14,6 @@ const theme = {
13
14
  danger: { 600: "#DC2626" },
14
15
  neutral: { 0: "#FFFFFF", 200: "#E5E7EB", 400: "#9CA3AF", 700: "#374151" }
15
16
  },
16
- shadows: { sm: "0 1px 3px rgba(0,0,0,0.1)" },
17
17
  borderRadius: { md: "8px", lg: "12px" }
18
18
  };
19
19
  const fadeIn = keyframes`
@@ -33,9 +33,9 @@ const StickySaveBar = styled(Box)`
33
33
  position: sticky;
34
34
  top: 0;
35
35
  z-index: 10;
36
- background: white;
37
- border-bottom: 1px solid ${theme.colors.neutral[200]};
38
- box-shadow: ${theme.shadows.sm};
36
+ background: ${(props) => props.theme.colors.neutral0};
37
+ border-bottom: 1px solid ${(props) => props.theme.colors.neutral200};
38
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
39
39
  `;
40
40
  const ToggleCard = styled(Box)`
41
41
  background: ${(props) => props.$active ? "linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%)" : "linear-gradient(135deg, #fafafa 0%, #f3f4f6 100%)"};
@@ -181,12 +181,12 @@ const validateTemplate = (template, templateType) => {
181
181
  };
182
182
  const getDefaultTemplates = () => ({
183
183
  suspiciousLogin: {
184
- subject: "🚨 Suspicious Login Alert - Session Manager",
184
+ subject: "[ALERT] Suspicious Login Alert - Session Manager",
185
185
  html: `
186
186
  <html>
187
187
  <body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
188
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>
189
+ <h2 style="color: #dc2626;">[ALERT] Suspicious Login Detected</h2>
190
190
  <p>A potentially suspicious login was detected for your account.</p>
191
191
 
192
192
  <div style="background: white; padding: 15px; border-radius: 8px; margin: 20px 0;">
@@ -221,7 +221,7 @@ const getDefaultTemplates = () => ({
221
221
  </div>
222
222
  </body>
223
223
  </html>`,
224
- text: `🚨 Suspicious Login Detected
224
+ text: `[ALERT] Suspicious Login Detected
225
225
 
226
226
  A potentially suspicious login was detected for your account.
227
227
 
@@ -236,12 +236,12 @@ Login Details:
236
236
  Security: VPN={{reason.isVpn}}, Proxy={{reason.isProxy}}, Threat={{reason.isThreat}}, Score={{reason.securityScore}}/100`
237
237
  },
238
238
  newLocation: {
239
- subject: "📍 New Location Login Detected",
239
+ subject: "[LOCATION] New Location Login Detected",
240
240
  html: `
241
241
  <html>
242
242
  <body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
243
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>
244
+ <h2 style="color: #0284c7;">[LOCATION] Login from New Location</h2>
245
245
  <p>Your account was accessed from a new location.</p>
246
246
 
247
247
  <div style="background: white; padding: 15px; border-radius: 8px; margin: 20px 0;">
@@ -264,7 +264,7 @@ Security: VPN={{reason.isVpn}}, Proxy={{reason.isProxy}}, Threat={{reason.isThre
264
264
  </div>
265
265
  </body>
266
266
  </html>`,
267
- text: `📍 Login from New Location
267
+ text: `[LOCATION] Login from New Location
268
268
 
269
269
  Your account was accessed from a new location.
270
270
 
@@ -279,12 +279,12 @@ New Location Details:
279
279
  If this was you, no action is needed.`
280
280
  },
281
281
  vpnProxy: {
282
- subject: "⚠️ VPN/Proxy Login Detected",
282
+ subject: "[WARNING] VPN/Proxy Login Detected",
283
283
  html: `
284
284
  <html>
285
285
  <body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
286
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>
287
+ <h2 style="color: #d97706;">[WARNING] VPN/Proxy Detected</h2>
288
288
  <p>A login from a VPN or proxy service was detected on your account.</p>
289
289
 
290
290
  <div style="background: white; padding: 15px; border-radius: 8px; margin: 20px 0;">
@@ -309,7 +309,7 @@ If this was you, no action is needed.`
309
309
  </div>
310
310
  </body>
311
311
  </html>`,
312
- text: `⚠️ VPN/Proxy Detected
312
+ text: `[WARNING] VPN/Proxy Detected
313
313
 
314
314
  A login from a VPN or proxy service was detected on your account.
315
315
 
@@ -333,9 +333,11 @@ const generateSecureKey = () => {
333
333
  return key;
334
334
  };
335
335
  const SettingsPage = () => {
336
+ const { formatMessage } = useIntl();
336
337
  const { get, post, put } = useFetchClient();
337
338
  const { toggleNotification } = useNotification();
338
339
  const { isPremium, isAdvanced, isEnterprise } = useLicense();
340
+ const t = (id, defaultMessage, values) => formatMessage({ id: getTranslation(id), defaultMessage }, values);
339
341
  const [loading, setLoading] = useState(true);
340
342
  const [saving, setSaving] = useState(false);
341
343
  const [hasChanges, setHasChanges] = useState(false);
@@ -401,7 +403,7 @@ const SettingsPage = () => {
401
403
  console.error("[Settings] Error loading from backend:", err);
402
404
  toggleNotification({
403
405
  type: "warning",
404
- message: "Could not load settings from server. Using defaults."
406
+ message: t("notifications.warning.settingsLoad", "Could not load settings from server. Using defaults.")
405
407
  });
406
408
  setSettings((prev) => ({ ...prev, emailTemplates: getDefaultTemplates() }));
407
409
  } finally {
@@ -419,7 +421,7 @@ const SettingsPage = () => {
419
421
  if (response?.data?.success) {
420
422
  toggleNotification({
421
423
  type: "success",
422
- message: "Settings saved successfully to database!"
424
+ message: t("notifications.success.saved", "Settings saved successfully to database!")
423
425
  });
424
426
  setHasChanges(false);
425
427
  try {
@@ -434,7 +436,7 @@ const SettingsPage = () => {
434
436
  console.error("[Settings] Error saving:", err);
435
437
  toggleNotification({
436
438
  type: "danger",
437
- message: "Failed to save settings to server"
439
+ message: t("notifications.error.save", "Failed to save settings to server")
438
440
  });
439
441
  } finally {
440
442
  setSaving(false);
@@ -445,7 +447,7 @@ const SettingsPage = () => {
445
447
  setHasChanges(false);
446
448
  };
447
449
  const handleCleanInactive = async () => {
448
- if (!confirm("⚠️ WARNING: This will permanently delete ALL inactive sessions.\n\nContinue?")) {
450
+ if (!confirm(t("settings.general.danger.confirm", "[WARNING] This will permanently delete ALL inactive sessions.\n\nContinue?"))) {
449
451
  return;
450
452
  }
451
453
  setCleaning(true);
@@ -453,28 +455,31 @@ const SettingsPage = () => {
453
455
  const { data } = await post(`/${pluginId}/sessions/clean-inactive`);
454
456
  toggleNotification({
455
457
  type: "success",
456
- message: `Successfully deleted ${data.deletedCount} inactive sessions!`
458
+ message: t("notifications.success.cleaned", "Successfully deleted {count} inactive sessions!", { count: data.deletedCount })
457
459
  });
458
460
  } catch (err) {
459
461
  toggleNotification({
460
462
  type: "danger",
461
- message: "Failed to delete inactive sessions"
463
+ message: t("notifications.error.clean", "Failed to delete inactive sessions")
462
464
  });
463
465
  } finally {
464
466
  setCleaning(false);
465
467
  }
466
468
  };
467
469
  if (loading) {
468
- return /* @__PURE__ */ jsx(Flex, { justifyContent: "center", padding: 8, children: /* @__PURE__ */ jsx(Loader, { children: "Loading settings..." }) });
470
+ return /* @__PURE__ */ jsx(Flex, { justifyContent: "center", padding: 8, children: /* @__PURE__ */ jsx(Loader, { children: t("common.loading", "Loading...") }) });
469
471
  }
470
472
  return /* @__PURE__ */ jsxs(Container, { children: [
471
473
  /* @__PURE__ */ jsx(StickySaveBar, { paddingTop: 5, paddingBottom: 5, paddingLeft: 6, paddingRight: 6, children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
472
474
  /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 1, alignItems: "flex-start", children: [
473
- /* @__PURE__ */ jsx(Typography, { variant: "alpha", fontWeight: "bold", style: { fontSize: "24px" }, children: "⚙️ Session Manager Settings" }),
474
- /* @__PURE__ */ jsx(Typography, { variant: "epsilon", textColor: "neutral600", children: "Configure session tracking, security, and email notifications" })
475
+ /* @__PURE__ */ jsxs(Typography, { variant: "alpha", fontWeight: "bold", style: { fontSize: "24px" }, children: [
476
+ "⚙️ ",
477
+ t("settings.title", "Session Manager Settings")
478
+ ] }),
479
+ /* @__PURE__ */ jsx(Typography, { variant: "epsilon", textColor: "neutral600", children: t("settings.subtitle", "Configure session tracking, security, and email notifications") })
475
480
  ] }),
476
481
  /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
477
- hasChanges && /* @__PURE__ */ jsx(Button, { onClick: handleReset, variant: "tertiary", size: "L", children: "Reset" }),
482
+ hasChanges && /* @__PURE__ */ jsx(Button, { onClick: handleReset, variant: "tertiary", size: "L", children: t("settings.reset", "Reset") }),
478
483
  /* @__PURE__ */ jsx(
479
484
  Button,
480
485
  {
@@ -502,7 +507,7 @@ const SettingsPage = () => {
502
507
  e.currentTarget.style.transform = "translateY(0)";
503
508
  e.currentTarget.style.boxShadow = hasChanges && !saving ? "0 4px 12px rgba(102, 126, 234, 0.4)" : "none";
504
509
  },
505
- children: saving ? "Saving..." : hasChanges ? "Save Changes" : "No Changes"
510
+ children: saving ? t("settings.saving", "Saving...") : hasChanges ? t("settings.save", "Save Changes") : t("settings.noChanges", "No Changes")
506
511
  }
507
512
  )
508
513
  ] })
@@ -511,19 +516,22 @@ const SettingsPage = () => {
511
516
  /* @__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: [
512
517
  /* @__PURE__ */ jsx(Information, { style: { width: "20px", height: "20px", color: "#0284C7" } }),
513
518
  /* @__PURE__ */ jsxs(Box, { children: [
514
- /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", textColor: "primary700", style: { marginBottom: "4px" }, children: "Current License Status" }),
519
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", textColor: "primary700", style: { marginBottom: "4px" }, children: t("settings.license.title", "Current License Status") }),
515
520
  /* @__PURE__ */ jsxs(Flex, { gap: 3, children: [
516
521
  /* @__PURE__ */ jsxs(Badge, { backgroundColor: isPremium ? "success100" : "neutral100", textColor: isPremium ? "success700" : "neutral600", children: [
517
522
  isPremium ? "✓" : "✗",
518
- " Premium"
523
+ " ",
524
+ t("settings.license.premium", "Premium")
519
525
  ] }),
520
526
  /* @__PURE__ */ jsxs(Badge, { backgroundColor: isAdvanced ? "primary100" : "neutral100", textColor: isAdvanced ? "primary700" : "neutral600", children: [
521
527
  isAdvanced ? "✓" : "✗",
522
- " Advanced"
528
+ " ",
529
+ t("settings.license.advanced", "Advanced")
523
530
  ] }),
524
531
  /* @__PURE__ */ jsxs(Badge, { backgroundColor: isEnterprise ? "secondary100" : "neutral100", textColor: isEnterprise ? "secondary700" : "neutral600", children: [
525
532
  isEnterprise ? "✓" : "✗",
526
- " Enterprise"
533
+ " ",
534
+ t("settings.license.enterprise", "Enterprise")
527
535
  ] })
528
536
  ] })
529
537
  ] })
@@ -534,114 +542,99 @@ const SettingsPage = () => {
534
542
  Accordion.Trigger,
535
543
  {
536
544
  icon: Cog,
537
- description: "Basic session tracking configuration",
538
- children: "General Settings"
545
+ description: t("settings.general.description", "Basic session tracking configuration"),
546
+ children: t("settings.general.title", "General Settings")
539
547
  }
540
548
  ) }),
541
549
  /* @__PURE__ */ jsx(Accordion.Content, { children: /* @__PURE__ */ jsxs(Box, { padding: 6, children: [
542
- /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "16px", display: "block", color: theme.colors.neutral[700] }, children: "⏱️ SESSION TIMEOUT" }),
550
+ /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "16px", display: "block", color: theme.colors.neutral[700] }, children: t("settings.general.timeout.title", "SESSION TIMEOUT") }),
543
551
  /* @__PURE__ */ jsxs(Grid.Root, { gap: 6, style: { marginBottom: "32px" }, children: [
544
552
  /* @__PURE__ */ jsx(Grid.Item, { col: 6, s: 12, children: /* @__PURE__ */ jsxs(Box, { children: [
545
- /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: "Inactivity Timeout" }),
553
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: t("settings.general.timeout.inactivity", "Inactivity Timeout") }),
546
554
  /* @__PURE__ */ jsxs(
547
555
  SingleSelect,
548
556
  {
549
557
  value: String(settings.inactivityTimeout),
550
558
  onChange: (value) => handleChange("inactivityTimeout", parseInt(value)),
551
559
  children: [
552
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "5", children: "5 minutes (Very Strict)" }),
553
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "10", children: "10 minutes (Strict)" }),
554
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "15", children: "15 minutes (Recommended)" }),
555
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "30", children: "30 minutes (Moderate)" }),
556
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "60", children: "1 hour (Relaxed)" }),
557
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "120", children: "2 hours (Very Relaxed)" })
560
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "5", children: t("settings.general.timeout.5min", "5 minutes (Very Strict)") }),
561
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "10", children: t("settings.general.timeout.10min", "10 minutes (Strict)") }),
562
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "15", children: t("settings.general.timeout.15min", "15 minutes (Recommended)") }),
563
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "30", children: t("settings.general.timeout.30min", "30 minutes (Moderate)") }),
564
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "60", children: t("settings.general.timeout.1hour", "1 hour (Relaxed)") }),
565
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "120", children: t("settings.general.timeout.2hours", "2 hours (Very Relaxed)") })
558
566
  ]
559
567
  }
560
568
  ),
561
- /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "11px", marginTop: "8px" }, children: [
562
- "Sessions inactive for more than ",
563
- settings.inactivityTimeout,
564
- " minutes will be marked as offline"
565
- ] })
569
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "11px", marginTop: "8px" }, children: t("settings.general.timeout.inactivityHint", "Sessions inactive for more than {minutes} minutes will be marked as offline", { minutes: settings.inactivityTimeout }) })
566
570
  ] }) }),
567
571
  /* @__PURE__ */ jsx(Grid.Item, { col: 6, s: 12, children: /* @__PURE__ */ jsxs(Box, { children: [
568
- /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: "Last Seen Rate Limit" }),
572
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: t("settings.general.rateLimit.title", "Last Seen Rate Limit") }),
569
573
  /* @__PURE__ */ jsxs(
570
574
  SingleSelect,
571
575
  {
572
576
  value: String(settings.lastSeenRateLimit),
573
577
  onChange: (value) => handleChange("lastSeenRateLimit", parseInt(value)),
574
578
  children: [
575
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "10", children: "10 seconds" }),
576
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "30", children: "30 seconds (Recommended)" }),
577
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "60", children: "1 minute" }),
578
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "120", children: "2 minutes" }),
579
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "300", children: "5 minutes" })
579
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "10", children: t("settings.general.rateLimit.10sec", "10 seconds") }),
580
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "30", children: t("settings.general.rateLimit.30sec", "30 seconds (Recommended)") }),
581
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "60", children: t("settings.general.rateLimit.1min", "1 minute") }),
582
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "120", children: t("settings.general.rateLimit.2min", "2 minutes") }),
583
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "300", children: t("settings.general.rateLimit.5min", "5 minutes") })
580
584
  ]
581
585
  }
582
586
  ),
583
- /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "11px", marginTop: "8px" }, children: [
584
- "Prevents excessive database writes. Updates throttled to once every ",
585
- settings.lastSeenRateLimit,
586
- " seconds"
587
- ] })
587
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "11px", marginTop: "8px" }, children: t("settings.general.rateLimit.hint", "Prevents excessive database writes. Updates throttled to once every {seconds} seconds", { seconds: settings.lastSeenRateLimit }) })
588
588
  ] }) })
589
589
  ] }),
590
590
  /* @__PURE__ */ jsx(Divider, { style: { marginBottom: "24px" } }),
591
- /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "16px", display: "block", color: theme.colors.neutral[700] }, children: "🧹 AUTO-CLEANUP & RETENTION" }),
591
+ /* @__PURE__ */ jsxs(Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "16px", display: "block", color: theme.colors.neutral[700] }, children: [
592
+ "🧹 ",
593
+ t("settings.general.cleanup.title", "AUTO-CLEANUP & RETENTION")
594
+ ] }),
592
595
  /* @__PURE__ */ jsxs(Grid.Root, { gap: 6, children: [
593
596
  /* @__PURE__ */ jsx(Grid.Item, { col: 6, s: 12, children: /* @__PURE__ */ jsxs(Box, { children: [
594
- /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: "Cleanup Interval" }),
597
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: t("settings.general.cleanup.interval", "Cleanup Interval") }),
595
598
  /* @__PURE__ */ jsxs(
596
599
  SingleSelect,
597
600
  {
598
601
  value: String(settings.cleanupInterval),
599
602
  onChange: (value) => handleChange("cleanupInterval", parseInt(value)),
600
603
  children: [
601
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "15", children: "15 minutes" }),
602
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "30", children: "30 minutes (Recommended)" }),
603
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "60", children: "1 hour" }),
604
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "120", children: "2 hours" })
604
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "15", children: t("settings.general.cleanup.15min", "15 minutes") }),
605
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "30", children: t("settings.general.cleanup.30min", "30 minutes (Recommended)") }),
606
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "60", children: t("settings.general.cleanup.1hour", "1 hour") }),
607
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "120", children: t("settings.general.cleanup.2hours", "2 hours") })
605
608
  ]
606
609
  }
607
610
  ),
608
- /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "11px", marginTop: "8px" }, children: [
609
- "Inactive sessions are automatically cleaned every ",
610
- settings.cleanupInterval,
611
- " minutes"
612
- ] })
611
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "11px", marginTop: "8px" }, children: t("settings.general.cleanup.intervalHint", "Inactive sessions are automatically cleaned every {minutes} minutes", { minutes: settings.cleanupInterval }) })
613
612
  ] }) }),
614
613
  /* @__PURE__ */ jsx(Grid.Item, { col: 6, s: 12, children: /* @__PURE__ */ jsxs(Box, { children: [
615
- /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: "Retention Period" }),
614
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: t("settings.general.retention.title", "Retention Period") }),
616
615
  /* @__PURE__ */ jsxs(
617
616
  SingleSelect,
618
617
  {
619
618
  value: String(settings.retentionDays),
620
619
  onChange: (value) => handleChange("retentionDays", parseInt(value)),
621
620
  children: [
622
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "7", children: "7 days" }),
623
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "30", children: "30 days" }),
624
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "60", children: "60 days" }),
625
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "90", children: "90 days (Recommended)" }),
626
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "180", children: "180 days" }),
627
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "365", children: "1 year" }),
628
- /* @__PURE__ */ jsx(SingleSelectOption, { value: "-1", children: "Forever" })
621
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "7", children: t("settings.general.retention.7days", "7 days") }),
622
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "30", children: t("settings.general.retention.30days", "30 days") }),
623
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "60", children: t("settings.general.retention.60days", "60 days") }),
624
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "90", children: t("settings.general.retention.90days", "90 days (Recommended)") }),
625
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "180", children: t("settings.general.retention.180days", "180 days") }),
626
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "365", children: t("settings.general.retention.1year", "1 year") }),
627
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "-1", children: t("settings.general.retention.forever", "Forever") })
629
628
  ]
630
629
  }
631
630
  ),
632
- /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "11px", marginTop: "8px" }, children: [
633
- "Old sessions deleted after ",
634
- settings.retentionDays === -1 ? "never" : `${settings.retentionDays} days`
635
- ] })
631
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "11px", marginTop: "8px" }, children: settings.retentionDays === -1 ? t("settings.general.retention.hintNever", "Old sessions deleted after never") : t("settings.general.retention.hint", "Old sessions deleted after {days}", { days: `${settings.retentionDays} days` }) })
636
632
  ] }) }),
637
633
  /* @__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: [
638
634
  /* @__PURE__ */ jsx(Trash, { style: { width: "18px", height: "18px", color: theme.colors.danger[600], flexShrink: 0, marginTop: "2px" } }),
639
635
  /* @__PURE__ */ jsxs(Box, { style: { flex: 1 }, children: [
640
- /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", textColor: "danger700", style: { marginBottom: "8px", display: "block" }, children: "Danger Zone" }),
641
- /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "danger600", style: { fontSize: "13px", lineHeight: "1.7" }, children: [
642
- /* @__PURE__ */ jsx("strong", { children: "Clean All Inactive:" }),
643
- " Permanently deletes all inactive sessions. This cannot be undone."
644
- ] })
636
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", textColor: "danger700", style: { marginBottom: "8px", display: "block" }, children: t("settings.general.danger.title", "Danger Zone") }),
637
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "danger600", style: { fontSize: "13px", lineHeight: "1.7" }, children: t("settings.general.danger.description", "Clean All Inactive: Permanently deletes all inactive sessions. This cannot be undone.") })
645
638
  ] }),
646
639
  /* @__PURE__ */ jsx(
647
640
  Button,
@@ -652,7 +645,7 @@ const SettingsPage = () => {
652
645
  variant: "danger",
653
646
  size: "S",
654
647
  style: { flexShrink: 0 },
655
- children: "Clean Now"
648
+ children: t("settings.general.danger.cleanNow", "Clean Now")
656
649
  }
657
650
  )
658
651
  ] }) }) })
@@ -664,12 +657,12 @@ const SettingsPage = () => {
664
657
  Accordion.Trigger,
665
658
  {
666
659
  icon: Shield,
667
- description: "Security policies and threat protection",
668
- children: "Security Settings"
660
+ description: t("settings.security.description", "Security policies and threat protection"),
661
+ children: t("settings.security.title", "Security Settings")
669
662
  }
670
663
  ) }),
671
664
  /* @__PURE__ */ jsx(Accordion.Content, { children: /* @__PURE__ */ jsxs(Box, { padding: 6, children: [
672
- /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "16px", display: "block", color: theme.colors.neutral[700] }, children: "🔒 SECURITY OPTIONS" }),
665
+ /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "16px", display: "block", color: theme.colors.neutral[700] }, children: t("settings.security.options", "SECURITY OPTIONS") }),
673
666
  /* @__PURE__ */ jsx(
674
667
  Box,
675
668
  {
@@ -684,32 +677,26 @@ const SettingsPage = () => {
684
677
  children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 4, children: [
685
678
  /* @__PURE__ */ jsxs(Flex, { alignItems: "center", gap: 3, children: [
686
679
  /* @__PURE__ */ jsx(Shield, { style: { width: 24, height: 24, color: theme.colors.primary[600] } }),
687
- /* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "bold", children: "JWT Encryption Key Generator" })
680
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "bold", children: t("settings.security.encryption.title", "JWT Encryption Key Generator") })
688
681
  ] }),
689
- /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "neutral600", style: { lineHeight: 1.6 }, children: "Generate a secure 32-character encryption key for JWT token storage. This key is used to encrypt tokens before saving them to the database." }),
690
- /* @__PURE__ */ jsxs(
682
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "neutral600", style: { lineHeight: 1.6 }, children: t("settings.security.encryption.description", "Generate a secure 32-character encryption key for JWT token storage. This key is used to encrypt tokens before saving them to the database.") }),
683
+ /* @__PURE__ */ jsx(
691
684
  Alert,
692
685
  {
693
686
  variant: "default",
694
- title: "Important",
687
+ title: t("settings.security.encryption.important", "Important"),
695
688
  style: { marginTop: 8 },
696
- children: [
697
- "Add this key to your ",
698
- /* @__PURE__ */ jsx("code", { children: ".env" }),
699
- " file as ",
700
- /* @__PURE__ */ jsx("strong", { children: "SESSION_ENCRYPTION_KEY" }),
701
- " for production."
702
- ]
689
+ children: t("settings.security.encryption.envHint", "Add this key to your .env file as SESSION_ENCRYPTION_KEY for production.")
703
690
  }
704
691
  ),
705
692
  /* @__PURE__ */ jsxs(Flex, { gap: 3, alignItems: "flex-end", children: [
706
693
  /* @__PURE__ */ jsx(Box, { style: { flex: 1 }, children: /* @__PURE__ */ jsx(
707
694
  TextInput,
708
695
  {
709
- label: "Generated Encryption Key",
696
+ label: t("settings.security.encryption.label", "Generated Encryption Key"),
710
697
  value: encryptionKey,
711
698
  onChange: (e) => setEncryptionKey(e.target.value),
712
- placeholder: "Click 'Generate Key' to create a secure key",
699
+ placeholder: t("settings.security.encryption.placeholder", "Click 'Generate Key' to create a secure key"),
713
700
  type: showEncryptionKey ? "text" : "password"
714
701
  }
715
702
  ) }),
@@ -719,7 +706,7 @@ const SettingsPage = () => {
719
706
  variant: "secondary",
720
707
  onClick: () => setShowEncryptionKey(!showEncryptionKey),
721
708
  size: "L",
722
- children: showEncryptionKey ? "Hide" : "Show"
709
+ children: showEncryptionKey ? t("settings.security.encryption.hide", "Hide") : t("settings.security.encryption.show", "Show")
723
710
  }
724
711
  )
725
712
  ] }),
@@ -735,11 +722,11 @@ const SettingsPage = () => {
735
722
  setShowEncryptionKey(true);
736
723
  toggleNotification({
737
724
  type: "success",
738
- message: "32-character encryption key generated!"
725
+ message: t("notifications.success.keyGenerated", "32-character encryption key generated!")
739
726
  });
740
727
  },
741
728
  size: "L",
742
- children: "Generate Key"
729
+ children: t("settings.security.encryption.generate", "Generate Key")
743
730
  }
744
731
  ),
745
732
  /* @__PURE__ */ jsx(
@@ -752,13 +739,13 @@ const SettingsPage = () => {
752
739
  navigator.clipboard.writeText(encryptionKey);
753
740
  toggleNotification({
754
741
  type: "success",
755
- message: "Encryption key copied to clipboard!"
742
+ message: t("notifications.success.keyCopied", "Encryption key copied to clipboard!")
756
743
  });
757
744
  }
758
745
  },
759
746
  disabled: !encryptionKey,
760
747
  size: "L",
761
- children: "Copy to Clipboard"
748
+ children: t("settings.security.encryption.copy", "Copy to Clipboard")
762
749
  }
763
750
  ),
764
751
  /* @__PURE__ */ jsx(
@@ -772,13 +759,13 @@ const SettingsPage = () => {
772
759
  navigator.clipboard.writeText(envLine);
773
760
  toggleNotification({
774
761
  type: "success",
775
- message: "Copied as .env format!"
762
+ message: t("notifications.success.envCopied", "Copied as .env format!")
776
763
  });
777
764
  }
778
765
  },
779
766
  disabled: !encryptionKey,
780
767
  size: "L",
781
- children: "Copy for .env"
768
+ children: t("settings.security.encryption.copyEnv", "Copy for .env")
782
769
  }
783
770
  )
784
771
  ] }),
@@ -795,7 +782,7 @@ const SettingsPage = () => {
795
782
  wordBreak: "break-all"
796
783
  },
797
784
  children: [
798
- /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", style: { marginBottom: 8, display: "block" }, children: "Add to .env file:" }),
785
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", style: { marginBottom: 8, display: "block" }, children: t("settings.security.encryption.envLabel", "Add to .env file:") }),
799
786
  /* @__PURE__ */ jsxs("code", { style: { color: theme.colors.primary[700] }, children: [
800
787
  "SESSION_ENCRYPTION_KEY=",
801
788
  encryptionKey
@@ -828,10 +815,10 @@ const SettingsPage = () => {
828
815
  fontWeight: "bold",
829
816
  textColor: settings.blockSuspiciousSessions ? "success700" : "neutral800",
830
817
  style: { fontSize: "16px" },
831
- children: "Block Suspicious Sessions"
818
+ children: t("settings.security.blockSuspicious.title", "Block Suspicious Sessions")
832
819
  }
833
820
  ),
834
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "13px", lineHeight: "1.6" }, children: "Automatically block sessions from VPNs, proxies, or threat IPs" })
821
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "13px", lineHeight: "1.6" }, children: t("settings.security.blockSuspicious.description", "Automatically block sessions from VPNs, proxies, or threat IPs") })
835
822
  ] })
836
823
  ] })
837
824
  }
@@ -858,10 +845,10 @@ const SettingsPage = () => {
858
845
  fontWeight: "bold",
859
846
  textColor: settings.enableGeolocation ? "success700" : "neutral800",
860
847
  style: { fontSize: "16px" },
861
- children: "IP Geolocation"
848
+ children: t("settings.security.geolocation.title", "IP Geolocation")
862
849
  }
863
850
  ),
864
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "13px", lineHeight: "1.6" }, children: "Fetch location data for each session (Premium)" })
851
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "13px", lineHeight: "1.6" }, children: t("settings.security.geolocation.description", "Fetch location data for each session (Premium)") })
865
852
  ] })
866
853
  ] })
867
854
  }
@@ -887,10 +874,10 @@ const SettingsPage = () => {
887
874
  fontWeight: "bold",
888
875
  textColor: settings.enableSecurityScoring ? "success700" : "neutral800",
889
876
  style: { fontSize: "16px" },
890
- children: "Security Scoring"
877
+ children: t("settings.security.scoring.title", "Security Scoring")
891
878
  }
892
879
  ),
893
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "13px", lineHeight: "1.6" }, children: "Calculate security scores and detect threats (Premium)" })
880
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "13px", lineHeight: "1.6" }, children: t("settings.security.scoring.description", "Calculate security scores and detect threats (Premium)") })
894
881
  ] })
895
882
  ] })
896
883
  }
@@ -898,7 +885,10 @@ const SettingsPage = () => {
898
885
  ] })
899
886
  ] }) }),
900
887
  /* @__PURE__ */ jsx(Grid.Root, { gap: 6, children: /* @__PURE__ */ jsx(Grid.Item, { col: 6, s: 12, children: /* @__PURE__ */ jsxs(Box, { children: [
901
- /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: "🚫 Max Failed Login Attempts" }),
888
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: [
889
+ "🚫 ",
890
+ t("settings.security.maxFailed.title", "Max Failed Login Attempts")
891
+ ] }),
902
892
  /* @__PURE__ */ jsx(
903
893
  NumberInput,
904
894
  {
@@ -908,11 +898,7 @@ const SettingsPage = () => {
908
898
  max: 20
909
899
  }
910
900
  ),
911
- /* @__PURE__ */ jsx(Box, { padding: 2, background: "warning50", style: { borderRadius: "4px", marginTop: "8px" }, children: /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "warning700", style: { fontSize: "11px" }, children: [
912
- "User will be blocked after ",
913
- settings.maxFailedLogins,
914
- " failed attempts"
915
- ] }) })
901
+ /* @__PURE__ */ jsx(Box, { padding: 2, background: "warning50", style: { borderRadius: "4px", marginTop: "8px" }, children: /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "warning700", style: { fontSize: "11px" }, children: t("settings.security.maxFailed.hint", "User will be blocked after {count} failed attempts", { count: settings.maxFailedLogins }) }) })
916
902
  ] }) }) })
917
903
  ] }) })
918
904
  ] }),
@@ -921,14 +907,17 @@ const SettingsPage = () => {
921
907
  Accordion.Trigger,
922
908
  {
923
909
  icon: Mail,
924
- description: "Email alerts for security events",
925
- children: "Email Notifications (Advanced)"
910
+ description: t("settings.email.description", "Email alerts for security events"),
911
+ children: t("settings.email.title", "Email Notifications (Advanced)")
926
912
  }
927
913
  ) }),
928
914
  /* @__PURE__ */ jsx(Accordion.Content, { children: /* @__PURE__ */ jsxs(Box, { padding: 6, children: [
929
915
  /* @__PURE__ */ jsxs(Box, { background: "neutral100", padding: 5, style: { borderRadius: theme.borderRadius.md, marginBottom: "32px" }, children: [
930
- /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "8px", display: "block", textAlign: "center", color: theme.colors.neutral[700] }, children: "📧 EMAIL ALERTS" }),
931
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { marginBottom: "20px", display: "block", textAlign: "center", fontSize: "12px" }, children: "Send security alerts to users via email" }),
916
+ /* @__PURE__ */ jsxs(Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "8px", display: "block", textAlign: "center", color: theme.colors.neutral[700] }, children: [
917
+ "📧 ",
918
+ t("settings.email.alerts.title", "EMAIL ALERTS")
919
+ ] }),
920
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { marginBottom: "20px", display: "block", textAlign: "center", fontSize: "12px" }, children: t("settings.email.alerts.subtitle", "Send security alerts to users via email") }),
932
921
  /* @__PURE__ */ jsx(Grid.Root, { gap: 4, children: /* @__PURE__ */ jsx(Grid.Item, { col: 12, children: /* @__PURE__ */ jsx(
933
922
  ToggleCard,
934
923
  {
@@ -950,17 +939,20 @@ const SettingsPage = () => {
950
939
  fontWeight: "bold",
951
940
  textColor: settings.enableEmailAlerts ? "success700" : "neutral800",
952
941
  style: { fontSize: "16px" },
953
- children: "Enable Email Alerts"
942
+ children: t("settings.email.enable.title", "Enable Email Alerts")
954
943
  }
955
944
  ),
956
- /* @__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" })
945
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "13px", lineHeight: "1.6" }, children: t("settings.email.enable.description", "Send security alerts for suspicious logins, new locations, and VPN/Proxy usage") })
957
946
  ] })
958
947
  ] })
959
948
  }
960
949
  ) }) })
961
950
  ] }),
962
951
  settings.enableEmailAlerts && /* @__PURE__ */ jsxs(Fragment, { children: [
963
- /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "16px", display: "block", color: theme.colors.neutral[700] }, children: "⚙️ ALERT TYPES" }),
952
+ /* @__PURE__ */ jsxs(Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "16px", display: "block", color: theme.colors.neutral[700] }, children: [
953
+ "⚙️ ",
954
+ t("settings.email.types.title", "ALERT TYPES")
955
+ ] }),
964
956
  /* @__PURE__ */ jsxs(Grid.Root, { gap: 4, style: { marginBottom: "32px" }, children: [
965
957
  /* @__PURE__ */ jsx(Grid.Item, { col: 4, s: 12, children: /* @__PURE__ */ jsx(
966
958
  Box,
@@ -979,7 +971,7 @@ const SettingsPage = () => {
979
971
  {
980
972
  checked: settings.alertOnSuspiciousLogin,
981
973
  onChange: () => handleChange("alertOnSuspiciousLogin", !settings.alertOnSuspiciousLogin),
982
- children: /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "semiBold", style: { fontSize: "14px" }, children: "🚨 Suspicious Login" })
974
+ children: /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "semiBold", style: { fontSize: "14px" }, children: t("settings.email.types.suspicious", "Suspicious Login") })
983
975
  }
984
976
  )
985
977
  }
@@ -1001,7 +993,7 @@ const SettingsPage = () => {
1001
993
  {
1002
994
  checked: settings.alertOnNewLocation,
1003
995
  onChange: () => handleChange("alertOnNewLocation", !settings.alertOnNewLocation),
1004
- children: /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "semiBold", style: { fontSize: "14px" }, children: "📍 New Location" })
996
+ children: /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "semiBold", style: { fontSize: "14px" }, children: t("settings.email.types.newLocation", "New Location") })
1005
997
  }
1006
998
  )
1007
999
  }
@@ -1023,24 +1015,24 @@ const SettingsPage = () => {
1023
1015
  {
1024
1016
  checked: settings.alertOnVpnProxy,
1025
1017
  onChange: () => handleChange("alertOnVpnProxy", !settings.alertOnVpnProxy),
1026
- children: /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "semiBold", style: { fontSize: "14px" }, children: "⚠️ VPN/Proxy" })
1018
+ children: /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "semiBold", style: { fontSize: "14px" }, children: t("settings.email.types.vpnProxy", "VPN/Proxy") })
1027
1019
  }
1028
1020
  )
1029
1021
  }
1030
1022
  ) })
1031
1023
  ] }),
1032
1024
  /* @__PURE__ */ jsx(Divider, { style: { marginBottom: "24px" } }),
1033
- /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "8px", display: "block", color: theme.colors.neutral[700] }, children: "📝 EMAIL TEMPLATES" }),
1034
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { marginBottom: "20px", display: "block", fontSize: "12px" }, children: "Customize email notification templates with dynamic variables" }),
1025
+ /* @__PURE__ */ jsx(Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "8px", display: "block", color: theme.colors.neutral[700] }, children: t("settings.email.templates.title", "EMAIL TEMPLATES") }),
1026
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { marginBottom: "20px", display: "block", fontSize: "12px" }, children: t("settings.email.templates.subtitle", "Customize email notification templates with dynamic variables") }),
1035
1027
  /* @__PURE__ */ jsxs(Tabs.Root, { value: activeTemplateTab, onValueChange: setActiveTemplateTab, children: [
1036
1028
  /* @__PURE__ */ jsxs(Tabs.List, { "aria-label": "Email Templates", children: [
1037
- /* @__PURE__ */ jsx(Tabs.Trigger, { value: "suspiciousLogin", children: "🚨 Suspicious Login" }),
1038
- /* @__PURE__ */ jsx(Tabs.Trigger, { value: "newLocation", children: "📍 New Location" }),
1039
- /* @__PURE__ */ jsx(Tabs.Trigger, { value: "vpnProxy", children: "⚠️ VPN/Proxy" })
1029
+ /* @__PURE__ */ jsx(Tabs.Trigger, { value: "suspiciousLogin", children: t("settings.email.templates.tab.suspicious", "Suspicious Login") }),
1030
+ /* @__PURE__ */ jsx(Tabs.Trigger, { value: "newLocation", children: t("settings.email.templates.tab.newLocation", "New Location") }),
1031
+ /* @__PURE__ */ jsx(Tabs.Trigger, { value: "vpnProxy", children: t("settings.email.templates.tab.vpnProxy", "VPN/Proxy") })
1040
1032
  ] }),
1041
1033
  Object.keys(settings.emailTemplates).map((templateKey) => /* @__PURE__ */ jsx(Tabs.Content, { value: templateKey, children: /* @__PURE__ */ jsxs(Box, { paddingTop: 4, children: [
1042
1034
  /* @__PURE__ */ jsxs(Box, { style: { marginBottom: "24px" }, children: [
1043
- /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: "✉️ Email Subject" }),
1035
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: t("settings.email.templates.subject", "Email Subject") }),
1044
1036
  /* @__PURE__ */ jsx(
1045
1037
  TextInput,
1046
1038
  {
@@ -1050,7 +1042,7 @@ const SettingsPage = () => {
1050
1042
  newTemplates[templateKey].subject = e.target.value;
1051
1043
  handleChange("emailTemplates", newTemplates);
1052
1044
  },
1053
- placeholder: "Enter email subject..."
1045
+ placeholder: t("settings.email.templates.subjectPlaceholder", "Enter email subject...")
1054
1046
  }
1055
1047
  )
1056
1048
  ] }),
@@ -1063,7 +1055,7 @@ const SettingsPage = () => {
1063
1055
  children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
1064
1056
  /* @__PURE__ */ jsxs(Flex, { alignItems: "center", gap: 2, children: [
1065
1057
  /* @__PURE__ */ jsx(Code, { style: { width: "16px", height: "16px", color: theme.colors.primary[600] } }),
1066
- /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", textColor: "primary600", children: "Available Variables (click to copy)" })
1058
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", textColor: "primary600", children: t("settings.email.templates.variables", "Available Variables (click to copy)") })
1067
1059
  ] }),
1068
1060
  /* @__PURE__ */ jsx(Flex, { gap: 2, wrap: "wrap", children: TEMPLATE_VARIABLES[templateKey].map(({ var: variable, desc }) => /* @__PURE__ */ jsx(
1069
1061
  Button,
@@ -1072,7 +1064,7 @@ const SettingsPage = () => {
1072
1064
  variant: "tertiary",
1073
1065
  onClick: () => {
1074
1066
  navigator.clipboard.writeText(variable);
1075
- toggleNotification({ type: "success", message: `${variable} copied!` });
1067
+ toggleNotification({ type: "success", message: t("notifications.success.variableCopied", "{variable} copied!", { variable }) });
1076
1068
  },
1077
1069
  style: {
1078
1070
  fontFamily: "monospace",
@@ -1096,10 +1088,13 @@ const SettingsPage = () => {
1096
1088
  children: [
1097
1089
  /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", style: { marginBottom: "16px" }, children: [
1098
1090
  /* @__PURE__ */ jsxs(Flex, { alignItems: "center", gap: 2, children: [
1099
- /* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "bold", style: { fontSize: "18px" }, children: "🎨 HTML Template" }),
1100
- /* @__PURE__ */ jsx(Badge, { variant: "success", children: "Main Template" })
1091
+ /* @__PURE__ */ jsxs(Typography, { variant: "delta", fontWeight: "bold", style: { fontSize: "18px" }, children: [
1092
+ "🎨 ",
1093
+ t("settings.email.templates.html.title", "HTML Template")
1094
+ ] }),
1095
+ /* @__PURE__ */ jsx(Badge, { variant: "success", children: t("settings.email.templates.html.badge", "Main Template") })
1101
1096
  ] }),
1102
- /* @__PURE__ */ jsx(
1097
+ /* @__PURE__ */ jsxs(
1103
1098
  Button,
1104
1099
  {
1105
1100
  variant: "tertiary",
@@ -1109,17 +1104,16 @@ const SettingsPage = () => {
1109
1104
  const newTemplates = { ...settings.emailTemplates };
1110
1105
  newTemplates[templateKey].html = defaultTemplates[templateKey].html;
1111
1106
  handleChange("emailTemplates", newTemplates);
1112
- toggleNotification({ type: "success", message: "Default HTML template loaded!" });
1107
+ toggleNotification({ type: "success", message: t("notifications.success.defaultLoaded", "Default template loaded!") });
1113
1108
  },
1114
- children: "📋 Load Default"
1109
+ children: [
1110
+ "📋 ",
1111
+ t("settings.email.templates.html.loadDefault", "Load Default")
1112
+ ]
1115
1113
  }
1116
1114
  )
1117
1115
  ] }),
1118
- /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", style: { marginBottom: "16px", display: "block", fontSize: "14px" }, children: [
1119
- "HTML template for email notifications. Use variables like ",
1120
- /* @__PURE__ */ jsx("code", { children: "{{user.email}}" }),
1121
- " for dynamic content."
1122
- ] }),
1116
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { marginBottom: "16px", display: "block", fontSize: "14px" }, children: t("settings.email.templates.html.description", "HTML template for email notifications. Use variables like {{user.email}} for dynamic content.") }),
1123
1117
  /* @__PURE__ */ jsxs(
1124
1118
  Box,
1125
1119
  {
@@ -1175,19 +1169,22 @@ const SettingsPage = () => {
1175
1169
  }
1176
1170
  ),
1177
1171
  /* @__PURE__ */ jsxs(Flex, { gap: 2, style: { marginTop: "12px" }, wrap: "wrap", children: [
1178
- /* @__PURE__ */ jsx(
1172
+ /* @__PURE__ */ jsxs(
1179
1173
  Button,
1180
1174
  {
1181
1175
  variant: "secondary",
1182
1176
  size: "S",
1183
1177
  onClick: () => {
1184
1178
  navigator.clipboard.writeText(settings.emailTemplates[templateKey].html);
1185
- toggleNotification({ type: "success", message: "HTML template copied!" });
1179
+ toggleNotification({ type: "success", message: t("notifications.success.htmlCopied", "HTML template copied!") });
1186
1180
  },
1187
- children: "📋 Copy Template"
1181
+ children: [
1182
+ "📋 ",
1183
+ t("settings.email.templates.html.copy", "Copy Template")
1184
+ ]
1188
1185
  }
1189
1186
  ),
1190
- /* @__PURE__ */ jsx(
1187
+ /* @__PURE__ */ jsxs(
1191
1188
  Button,
1192
1189
  {
1193
1190
  variant: "tertiary",
@@ -1196,13 +1193,16 @@ const SettingsPage = () => {
1196
1193
  const validation = validateTemplate(settings.emailTemplates[templateKey].html, templateKey);
1197
1194
  toggleNotification({
1198
1195
  type: validation.isValid ? "success" : "warning",
1199
- message: validation.isValid ? `✓ Template valid! Found ${validation.foundVars.length}/${validation.totalAvailable} variables.` : "⚠️ No variables found. Add at least one variable."
1196
+ message: validation.isValid ? t("notifications.success.validated", "Template valid! Found {found}/{total} variables.", { found: validation.foundVars.length, total: validation.totalAvailable }) : t("notifications.warning.noVariables", "[WARNING] No variables found. Add at least one variable.")
1200
1197
  });
1201
1198
  },
1202
- children: "✓ Validate"
1199
+ children: [
1200
+ "✓ ",
1201
+ t("settings.email.templates.html.validate", "Validate")
1202
+ ]
1203
1203
  }
1204
1204
  ),
1205
- /* @__PURE__ */ jsx(
1205
+ /* @__PURE__ */ jsxs(
1206
1206
  Button,
1207
1207
  {
1208
1208
  variant: "tertiary",
@@ -1212,10 +1212,13 @@ const SettingsPage = () => {
1212
1212
  const chars = settings.emailTemplates[templateKey].html.length;
1213
1213
  toggleNotification({
1214
1214
  type: "info",
1215
- message: `Template has ${lines} lines and ${chars} characters`
1215
+ message: t("notifications.info.templateStats", "Template has {lines} lines and {chars} characters", { lines, chars })
1216
1216
  });
1217
1217
  },
1218
- children: "ℹ️ Template Info"
1218
+ children: [
1219
+ "ℹ️ ",
1220
+ t("settings.email.templates.html.info", "Template Info")
1221
+ ]
1219
1222
  }
1220
1223
  )
1221
1224
  ] })
@@ -1231,10 +1234,13 @@ const SettingsPage = () => {
1231
1234
  children: [
1232
1235
  /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", style: { marginBottom: "16px" }, children: [
1233
1236
  /* @__PURE__ */ jsxs(Flex, { alignItems: "center", gap: 2, children: [
1234
- /* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "bold", style: { fontSize: "18px" }, children: "📄 Text Template" }),
1235
- /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: "Fallback" })
1237
+ /* @__PURE__ */ jsxs(Typography, { variant: "delta", fontWeight: "bold", style: { fontSize: "18px" }, children: [
1238
+ "📄 ",
1239
+ t("settings.email.templates.text.title", "Text Template")
1240
+ ] }),
1241
+ /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: t("settings.email.templates.text.badge", "Fallback") })
1236
1242
  ] }),
1237
- /* @__PURE__ */ jsx(
1243
+ /* @__PURE__ */ jsxs(
1238
1244
  Button,
1239
1245
  {
1240
1246
  variant: "tertiary",
@@ -1244,13 +1250,16 @@ const SettingsPage = () => {
1244
1250
  const newTemplates = { ...settings.emailTemplates };
1245
1251
  newTemplates[templateKey].text = defaultTemplates[templateKey].text;
1246
1252
  handleChange("emailTemplates", newTemplates);
1247
- toggleNotification({ type: "success", message: "Default text template loaded!" });
1253
+ toggleNotification({ type: "success", message: t("notifications.success.defaultLoaded", "Default template loaded!") });
1248
1254
  },
1249
- children: "📋 Load Default"
1255
+ children: [
1256
+ "📋 ",
1257
+ t("settings.email.templates.text.loadDefault", "Load Default")
1258
+ ]
1250
1259
  }
1251
1260
  )
1252
1261
  ] }),
1253
- /* @__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" }),
1262
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { marginBottom: "16px", display: "block", fontSize: "14px" }, children: t("settings.email.templates.text.description", "Plain text version (no HTML) as fallback for older email clients") }),
1254
1263
  /* @__PURE__ */ jsxs(
1255
1264
  Box,
1256
1265
  {
@@ -1305,16 +1314,19 @@ const SettingsPage = () => {
1305
1314
  ]
1306
1315
  }
1307
1316
  ),
1308
- /* @__PURE__ */ jsx(Flex, { gap: 2, style: { marginTop: "12px" }, wrap: "wrap", children: /* @__PURE__ */ jsx(
1317
+ /* @__PURE__ */ jsx(Flex, { gap: 2, style: { marginTop: "12px" }, wrap: "wrap", children: /* @__PURE__ */ jsxs(
1309
1318
  Button,
1310
1319
  {
1311
1320
  variant: "secondary",
1312
1321
  size: "S",
1313
1322
  onClick: () => {
1314
1323
  navigator.clipboard.writeText(settings.emailTemplates[templateKey].text);
1315
- toggleNotification({ type: "success", message: "Text template copied!" });
1324
+ toggleNotification({ type: "success", message: t("notifications.success.textCopied", "Text template copied!") });
1316
1325
  },
1317
- children: "📋 Copy Template"
1326
+ children: [
1327
+ "📋 ",
1328
+ t("settings.email.templates.text.copy", "Copy Template")
1329
+ ]
1318
1330
  }
1319
1331
  ) })
1320
1332
  ]
@@ -1330,8 +1342,8 @@ const SettingsPage = () => {
1330
1342
  Accordion.Trigger,
1331
1343
  {
1332
1344
  icon: Code,
1333
- description: "Discord & Slack integration",
1334
- children: "Webhook Integration (Advanced)"
1345
+ description: t("settings.webhooks.description", "Discord & Slack integration"),
1346
+ children: t("settings.webhooks.title", "Webhook Integration (Advanced)")
1335
1347
  }
1336
1348
  ) }),
1337
1349
  /* @__PURE__ */ jsx(Accordion.Content, { children: /* @__PURE__ */ jsxs(Box, { padding: 6, children: [
@@ -1356,17 +1368,20 @@ const SettingsPage = () => {
1356
1368
  fontWeight: "bold",
1357
1369
  textColor: settings.enableWebhooks ? "success700" : "neutral800",
1358
1370
  style: { fontSize: "16px" },
1359
- children: "Enable Webhooks"
1371
+ children: t("settings.webhooks.enable.title", "Enable Webhooks")
1360
1372
  }
1361
1373
  ),
1362
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "13px", lineHeight: "1.6" }, children: "Send session events to Discord, Slack, or custom endpoints" })
1374
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "13px", lineHeight: "1.6" }, children: t("settings.webhooks.enable.description", "Send session events to Discord, Slack, or custom endpoints") })
1363
1375
  ] })
1364
1376
  ] })
1365
1377
  }
1366
1378
  ) }) }) }),
1367
1379
  settings.enableWebhooks && /* @__PURE__ */ jsxs(Grid.Root, { gap: 6, children: [
1368
1380
  /* @__PURE__ */ jsx(Grid.Item, { col: 12, children: /* @__PURE__ */ jsxs(Box, { children: [
1369
- /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "12px", display: "block" }, children: "🔗 Discord Webhook URL" }),
1381
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "12px", display: "block" }, children: [
1382
+ "🔗 ",
1383
+ t("settings.webhooks.discord.title", "Discord Webhook URL")
1384
+ ] }),
1370
1385
  /* @__PURE__ */ jsx(
1371
1386
  Box,
1372
1387
  {
@@ -1379,7 +1394,7 @@ const SettingsPage = () => {
1379
1394
  children: /* @__PURE__ */ jsx(
1380
1395
  "textarea",
1381
1396
  {
1382
- placeholder: "https://discord.com/api/webhooks/123456789/abcdefghijklmnopqrstuvwxyz...",
1397
+ placeholder: t("settings.webhooks.discord.placeholder", "https://discord.com/api/webhooks/123456789/abcdefghijklmnopqrstuvwxyz..."),
1383
1398
  value: settings.discordWebhookUrl,
1384
1399
  onChange: (e) => handleChange("discordWebhookUrl", e.target.value),
1385
1400
  rows: 3,
@@ -1401,15 +1416,15 @@ const SettingsPage = () => {
1401
1416
  }
1402
1417
  ),
1403
1418
  /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", style: { marginTop: "10px" }, children: [
1404
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "12px" }, children: "💡 Optional: Post session alerts to your Discord channel" }),
1405
- settings.discordWebhookUrl && /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "primary600", style: { fontSize: "11px", fontFamily: "monospace" }, children: [
1406
- settings.discordWebhookUrl.length,
1407
- " characters"
1408
- ] })
1419
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "12px" }, children: t("settings.webhooks.discord.hint", "Optional: Post session alerts to your Discord channel") }),
1420
+ settings.discordWebhookUrl && /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "primary600", style: { fontSize: "11px", fontFamily: "monospace" }, children: t("settings.webhooks.characters", "{count} characters", { count: settings.discordWebhookUrl.length }) })
1409
1421
  ] })
1410
1422
  ] }) }),
1411
1423
  /* @__PURE__ */ jsx(Grid.Item, { col: 12, children: /* @__PURE__ */ jsxs(Box, { children: [
1412
- /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "12px", display: "block" }, children: "💬 Slack Webhook URL" }),
1424
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "12px", display: "block" }, children: [
1425
+ "💬 ",
1426
+ t("settings.webhooks.slack.title", "Slack Webhook URL")
1427
+ ] }),
1413
1428
  /* @__PURE__ */ jsx(
1414
1429
  Box,
1415
1430
  {
@@ -1422,7 +1437,7 @@ const SettingsPage = () => {
1422
1437
  children: /* @__PURE__ */ jsx(
1423
1438
  "textarea",
1424
1439
  {
1425
- placeholder: "https://hooks.slack.com/services/XXXX/XXXX/XXXX",
1440
+ placeholder: t("settings.webhooks.slack.placeholder", "https://hooks.slack.com/services/XXXX/XXXX/XXXX"),
1426
1441
  value: settings.slackWebhookUrl,
1427
1442
  onChange: (e) => handleChange("slackWebhookUrl", e.target.value),
1428
1443
  rows: 3,
@@ -1444,11 +1459,8 @@ const SettingsPage = () => {
1444
1459
  }
1445
1460
  ),
1446
1461
  /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", style: { marginTop: "10px" }, children: [
1447
- /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "12px" }, children: "💡 Optional: Post session alerts to your Slack workspace" }),
1448
- settings.slackWebhookUrl && /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "primary600", style: { fontSize: "11px", fontFamily: "monospace" }, children: [
1449
- settings.slackWebhookUrl.length,
1450
- " characters"
1451
- ] })
1462
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "12px" }, children: t("settings.webhooks.slack.hint", "Optional: Post session alerts to your Slack workspace") }),
1463
+ settings.slackWebhookUrl && /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "primary600", style: { fontSize: "11px", fontFamily: "monospace" }, children: t("settings.webhooks.characters", "{count} characters", { count: settings.slackWebhookUrl.length }) })
1452
1464
  ] })
1453
1465
  ] }) })
1454
1466
  ] })
@@ -1458,8 +1470,8 @@ const SettingsPage = () => {
1458
1470
  /* @__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: [
1459
1471
  /* @__PURE__ */ jsx(Check, { style: { width: "20px", height: "20px", color: theme.colors.success[600], flexShrink: 0, marginTop: "2px" } }),
1460
1472
  /* @__PURE__ */ jsxs(Box, { style: { flex: 1 }, children: [
1461
- /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", style: { marginBottom: "8px", display: "block", color: theme.colors.primary[700] }, children: " Database-Backed Settings" }),
1462
- /* @__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." })
1473
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", style: { marginBottom: "8px", display: "block", color: theme.colors.primary[700] }, children: t("settings.footer.title", "Database-Backed Settings") }),
1474
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "primary700", style: { fontSize: "13px", lineHeight: "1.8" }, children: t("settings.footer.description", "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.") })
1463
1475
  ] })
1464
1476
  ] }) })
1465
1477
  ] })