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.
- package/README.md +2 -2
- package/admin/src/components/LicenseGuard.jsx +6 -6
- package/admin/src/components/OnlineUsersWidget.jsx +11 -7
- package/admin/src/components/SessionDetailModal.jsx +45 -41
- package/admin/src/components/SessionInfoCard.jsx +3 -3
- package/admin/src/components/SessionInfoPanel.jsx +31 -21
- package/admin/src/index.js +9 -0
- package/admin/src/pages/Analytics.jsx +2 -2
- package/admin/src/pages/HomePage.jsx +129 -165
- package/admin/src/pages/License.jsx +5 -5
- package/admin/src/pages/Settings.jsx +148 -144
- package/admin/src/pages/SettingsNew.jsx +21 -21
- package/admin/src/pages/UpgradePage.jsx +448 -0
- package/admin/src/pluginId.js +1 -0
- package/admin/src/translations/de.json +294 -15
- package/admin/src/translations/en.json +293 -14
- package/admin/src/translations/es.json +284 -18
- package/admin/src/translations/fr.json +284 -18
- package/admin/src/translations/pt.json +284 -18
- package/admin/src/utils/parseUserAgent.js +6 -6
- package/admin/src/utils/theme.js +85 -0
- package/dist/_chunks/{Analytics-mYu_uGwU.mjs → Analytics-DTE_zmRV.mjs} +4 -4
- package/dist/_chunks/{Analytics-ioaeEh-E.js → Analytics-lw_JaOVy.js} +4 -4
- package/dist/_chunks/{App-DdnUYWbC.js → App-DDKYCjKw.js} +221 -216
- package/dist/_chunks/{App-BXpIS12l.mjs → App-DJW1ZNl5.mjs} +221 -216
- package/dist/_chunks/{License-C03C2j9P.mjs → License-DaOFuImm.mjs} +6 -10
- package/dist/_chunks/{License-DZYrOgcx.js → License-Tk-6UfPl.js} +6 -10
- package/dist/_chunks/{OnlineUsersWidget-B8JS1xZu.js → OnlineUsersWidget-C1qTpsws.js} +11 -7
- package/dist/_chunks/{OnlineUsersWidget-ArMl0nen.mjs → OnlineUsersWidget-CADphbXG.mjs} +11 -7
- package/dist/_chunks/{Settings-0ocB3qHk.mjs → Settings-C9xvckgq.mjs} +200 -188
- package/dist/_chunks/{Settings-C6_CqpCC.js → Settings-DyEAuTNQ.js} +200 -188
- package/dist/_chunks/UpgradePage-Dssk8A0Z.js +354 -0
- package/dist/_chunks/UpgradePage-cINvE9zY.mjs +352 -0
- package/dist/_chunks/de-CDA1V0rF.mjs +292 -0
- package/dist/_chunks/de-I-Q-pWqu.js +292 -0
- package/dist/_chunks/en-Bd7_h-4e.js +292 -0
- package/dist/_chunks/en-DzmOCyzQ.mjs +292 -0
- package/dist/_chunks/es-BcAx18XG.js +277 -0
- package/dist/_chunks/es-Cx-SN6qV.mjs +277 -0
- package/dist/_chunks/fr-DCzYMuJ-.js +277 -0
- package/dist/_chunks/fr-DXlXE5Eo.mjs +277 -0
- package/dist/_chunks/{index-DC8Y0qxx.js → index-CWcvrfXc.js} +52 -49
- package/dist/_chunks/{index-DBRS3kt5.mjs → index-DQO9bNP7.mjs} +52 -49
- package/dist/_chunks/pt-21-MAb72.js +277 -0
- package/dist/_chunks/pt-zsdTSjba.mjs +277 -0
- package/dist/_chunks/{useLicense-qgGfMvse.js → useLicense-DtvJOszr.js} +1 -1
- package/dist/_chunks/{useLicense-DSLL9n3Y.mjs → useLicense-DxbD4Wf8.mjs} +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +142 -33
- package/dist/server/index.mjs +142 -33
- package/package.json +1 -1
- package/server/src/bootstrap.js +76 -4
- package/server/src/controllers/session.js +59 -9
- package/server/src/middlewares/last-seen.js +5 -4
- package/server/src/routes/content-api.js +11 -2
- package/server/src/services/notifications.js +10 -10
- package/server/src/services/session.js +24 -4
- package/dist/_chunks/de-BxFx1pwE.js +0 -23
- package/dist/_chunks/de-CdO3s01z.mjs +0 -23
- package/dist/_chunks/en-CsPpPJL3.mjs +0 -23
- package/dist/_chunks/en-RqmpDHdS.js +0 -23
- package/dist/_chunks/es-CuLHazN1.js +0 -23
- package/dist/_chunks/es-Dkmjhy9c.mjs +0 -23
- package/dist/_chunks/fr-BAJp2yhI.js +0 -23
- package/dist/_chunks/fr-Bssg_3UF.mjs +0 -23
- package/dist/_chunks/pt-BAP9cKs3.js +0 -23
- package/dist/_chunks/pt-BVNoNcuY.mjs +0 -23
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const jsxRuntime = require("react/jsx-runtime");
|
|
4
4
|
const react = require("react");
|
|
5
|
+
const reactIntl = require("react-intl");
|
|
5
6
|
const designSystem = require("@strapi/design-system");
|
|
6
7
|
const admin = require("@strapi/strapi/admin");
|
|
7
8
|
const icons = require("@strapi/icons");
|
|
8
9
|
const styled = require("styled-components");
|
|
9
|
-
const index = require("./index-
|
|
10
|
-
const useLicense = require("./useLicense-
|
|
10
|
+
const index = require("./index-CWcvrfXc.js");
|
|
11
|
+
const useLicense = require("./useLicense-DtvJOszr.js");
|
|
11
12
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
12
13
|
const styled__default = /* @__PURE__ */ _interopDefault(styled);
|
|
13
14
|
const theme = {
|
|
@@ -17,7 +18,6 @@ const theme = {
|
|
|
17
18
|
danger: { 600: "#DC2626" },
|
|
18
19
|
neutral: { 0: "#FFFFFF", 200: "#E5E7EB", 400: "#9CA3AF", 700: "#374151" }
|
|
19
20
|
},
|
|
20
|
-
shadows: { sm: "0 1px 3px rgba(0,0,0,0.1)" },
|
|
21
21
|
borderRadius: { md: "8px", lg: "12px" }
|
|
22
22
|
};
|
|
23
23
|
const fadeIn = styled.keyframes`
|
|
@@ -37,9 +37,9 @@ const StickySaveBar = styled__default.default(designSystem.Box)`
|
|
|
37
37
|
position: sticky;
|
|
38
38
|
top: 0;
|
|
39
39
|
z-index: 10;
|
|
40
|
-
background:
|
|
41
|
-
border-bottom: 1px solid ${theme.colors.
|
|
42
|
-
box-shadow:
|
|
40
|
+
background: ${(props) => props.theme.colors.neutral0};
|
|
41
|
+
border-bottom: 1px solid ${(props) => props.theme.colors.neutral200};
|
|
42
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
43
43
|
`;
|
|
44
44
|
const ToggleCard = styled__default.default(designSystem.Box)`
|
|
45
45
|
background: ${(props) => props.$active ? "linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%)" : "linear-gradient(135deg, #fafafa 0%, #f3f4f6 100%)"};
|
|
@@ -185,12 +185,12 @@ const validateTemplate = (template, templateType) => {
|
|
|
185
185
|
};
|
|
186
186
|
const getDefaultTemplates = () => ({
|
|
187
187
|
suspiciousLogin: {
|
|
188
|
-
subject: "
|
|
188
|
+
subject: "[ALERT] Suspicious Login Alert - Session Manager",
|
|
189
189
|
html: `
|
|
190
190
|
<html>
|
|
191
191
|
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
|
|
192
192
|
<div style="max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f9fafb; border-radius: 10px;">
|
|
193
|
-
<h2 style="color: #dc2626;"
|
|
193
|
+
<h2 style="color: #dc2626;">[ALERT] Suspicious Login Detected</h2>
|
|
194
194
|
<p>A potentially suspicious login was detected for your account.</p>
|
|
195
195
|
|
|
196
196
|
<div style="background: white; padding: 15px; border-radius: 8px; margin: 20px 0;">
|
|
@@ -225,7 +225,7 @@ const getDefaultTemplates = () => ({
|
|
|
225
225
|
</div>
|
|
226
226
|
</body>
|
|
227
227
|
</html>`,
|
|
228
|
-
text:
|
|
228
|
+
text: `[ALERT] Suspicious Login Detected
|
|
229
229
|
|
|
230
230
|
A potentially suspicious login was detected for your account.
|
|
231
231
|
|
|
@@ -240,12 +240,12 @@ Login Details:
|
|
|
240
240
|
Security: VPN={{reason.isVpn}}, Proxy={{reason.isProxy}}, Threat={{reason.isThreat}}, Score={{reason.securityScore}}/100`
|
|
241
241
|
},
|
|
242
242
|
newLocation: {
|
|
243
|
-
subject: "
|
|
243
|
+
subject: "[LOCATION] New Location Login Detected",
|
|
244
244
|
html: `
|
|
245
245
|
<html>
|
|
246
246
|
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
|
|
247
247
|
<div style="max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f0f9ff; border-radius: 10px;">
|
|
248
|
-
<h2 style="color: #0284c7;"
|
|
248
|
+
<h2 style="color: #0284c7;">[LOCATION] Login from New Location</h2>
|
|
249
249
|
<p>Your account was accessed from a new location.</p>
|
|
250
250
|
|
|
251
251
|
<div style="background: white; padding: 15px; border-radius: 8px; margin: 20px 0;">
|
|
@@ -268,7 +268,7 @@ Security: VPN={{reason.isVpn}}, Proxy={{reason.isProxy}}, Threat={{reason.isThre
|
|
|
268
268
|
</div>
|
|
269
269
|
</body>
|
|
270
270
|
</html>`,
|
|
271
|
-
text:
|
|
271
|
+
text: `[LOCATION] Login from New Location
|
|
272
272
|
|
|
273
273
|
Your account was accessed from a new location.
|
|
274
274
|
|
|
@@ -283,12 +283,12 @@ New Location Details:
|
|
|
283
283
|
If this was you, no action is needed.`
|
|
284
284
|
},
|
|
285
285
|
vpnProxy: {
|
|
286
|
-
subject: "
|
|
286
|
+
subject: "[WARNING] VPN/Proxy Login Detected",
|
|
287
287
|
html: `
|
|
288
288
|
<html>
|
|
289
289
|
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
|
|
290
290
|
<div style="max-width: 600px; margin: 0 auto; padding: 20px; background-color: #fffbeb; border-radius: 10px;">
|
|
291
|
-
<h2 style="color: #d97706;"
|
|
291
|
+
<h2 style="color: #d97706;">[WARNING] VPN/Proxy Detected</h2>
|
|
292
292
|
<p>A login from a VPN or proxy service was detected on your account.</p>
|
|
293
293
|
|
|
294
294
|
<div style="background: white; padding: 15px; border-radius: 8px; margin: 20px 0;">
|
|
@@ -313,7 +313,7 @@ If this was you, no action is needed.`
|
|
|
313
313
|
</div>
|
|
314
314
|
</body>
|
|
315
315
|
</html>`,
|
|
316
|
-
text:
|
|
316
|
+
text: `[WARNING] VPN/Proxy Detected
|
|
317
317
|
|
|
318
318
|
A login from a VPN or proxy service was detected on your account.
|
|
319
319
|
|
|
@@ -337,9 +337,11 @@ const generateSecureKey = () => {
|
|
|
337
337
|
return key;
|
|
338
338
|
};
|
|
339
339
|
const SettingsPage = () => {
|
|
340
|
+
const { formatMessage } = reactIntl.useIntl();
|
|
340
341
|
const { get, post, put } = admin.useFetchClient();
|
|
341
342
|
const { toggleNotification } = admin.useNotification();
|
|
342
343
|
const { isPremium, isAdvanced, isEnterprise } = useLicense.useLicense();
|
|
344
|
+
const t = (id, defaultMessage, values) => formatMessage({ id: index.getTranslation(id), defaultMessage }, values);
|
|
343
345
|
const [loading, setLoading] = react.useState(true);
|
|
344
346
|
const [saving, setSaving] = react.useState(false);
|
|
345
347
|
const [hasChanges, setHasChanges] = react.useState(false);
|
|
@@ -405,7 +407,7 @@ const SettingsPage = () => {
|
|
|
405
407
|
console.error("[Settings] Error loading from backend:", err);
|
|
406
408
|
toggleNotification({
|
|
407
409
|
type: "warning",
|
|
408
|
-
message: "Could not load settings from server. Using defaults."
|
|
410
|
+
message: t("notifications.warning.settingsLoad", "Could not load settings from server. Using defaults.")
|
|
409
411
|
});
|
|
410
412
|
setSettings((prev) => ({ ...prev, emailTemplates: getDefaultTemplates() }));
|
|
411
413
|
} finally {
|
|
@@ -423,7 +425,7 @@ const SettingsPage = () => {
|
|
|
423
425
|
if (response?.data?.success) {
|
|
424
426
|
toggleNotification({
|
|
425
427
|
type: "success",
|
|
426
|
-
message: "Settings saved successfully to database!"
|
|
428
|
+
message: t("notifications.success.saved", "Settings saved successfully to database!")
|
|
427
429
|
});
|
|
428
430
|
setHasChanges(false);
|
|
429
431
|
try {
|
|
@@ -438,7 +440,7 @@ const SettingsPage = () => {
|
|
|
438
440
|
console.error("[Settings] Error saving:", err);
|
|
439
441
|
toggleNotification({
|
|
440
442
|
type: "danger",
|
|
441
|
-
message: "Failed to save settings to server"
|
|
443
|
+
message: t("notifications.error.save", "Failed to save settings to server")
|
|
442
444
|
});
|
|
443
445
|
} finally {
|
|
444
446
|
setSaving(false);
|
|
@@ -449,7 +451,7 @@ const SettingsPage = () => {
|
|
|
449
451
|
setHasChanges(false);
|
|
450
452
|
};
|
|
451
453
|
const handleCleanInactive = async () => {
|
|
452
|
-
if (!confirm("
|
|
454
|
+
if (!confirm(t("settings.general.danger.confirm", "[WARNING] This will permanently delete ALL inactive sessions.\n\nContinue?"))) {
|
|
453
455
|
return;
|
|
454
456
|
}
|
|
455
457
|
setCleaning(true);
|
|
@@ -457,28 +459,31 @@ const SettingsPage = () => {
|
|
|
457
459
|
const { data } = await post(`/${index.pluginId}/sessions/clean-inactive`);
|
|
458
460
|
toggleNotification({
|
|
459
461
|
type: "success",
|
|
460
|
-
message:
|
|
462
|
+
message: t("notifications.success.cleaned", "Successfully deleted {count} inactive sessions!", { count: data.deletedCount })
|
|
461
463
|
});
|
|
462
464
|
} catch (err) {
|
|
463
465
|
toggleNotification({
|
|
464
466
|
type: "danger",
|
|
465
|
-
message: "Failed to delete inactive sessions"
|
|
467
|
+
message: t("notifications.error.clean", "Failed to delete inactive sessions")
|
|
466
468
|
});
|
|
467
469
|
} finally {
|
|
468
470
|
setCleaning(false);
|
|
469
471
|
}
|
|
470
472
|
};
|
|
471
473
|
if (loading) {
|
|
472
|
-
return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", padding: 8, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { children: "Loading
|
|
474
|
+
return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", padding: 8, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { children: t("common.loading", "Loading...") }) });
|
|
473
475
|
}
|
|
474
476
|
return /* @__PURE__ */ jsxRuntime.jsxs(Container, { children: [
|
|
475
477
|
/* @__PURE__ */ jsxRuntime.jsx(StickySaveBar, { paddingTop: 5, paddingBottom: 5, paddingLeft: 6, paddingRight: 6, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
|
|
476
478
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 1, alignItems: "flex-start", children: [
|
|
477
|
-
/* @__PURE__ */ jsxRuntime.
|
|
478
|
-
|
|
479
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "alpha", fontWeight: "bold", style: { fontSize: "24px" }, children: [
|
|
480
|
+
"⚙️ ",
|
|
481
|
+
t("settings.title", "Session Manager Settings")
|
|
482
|
+
] }),
|
|
483
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "epsilon", textColor: "neutral600", children: t("settings.subtitle", "Configure session tracking, security, and email notifications") })
|
|
479
484
|
] }),
|
|
480
485
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
|
|
481
|
-
hasChanges && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleReset, variant: "tertiary", size: "L", children: "Reset" }),
|
|
486
|
+
hasChanges && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: handleReset, variant: "tertiary", size: "L", children: t("settings.reset", "Reset") }),
|
|
482
487
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
483
488
|
designSystem.Button,
|
|
484
489
|
{
|
|
@@ -506,7 +511,7 @@ const SettingsPage = () => {
|
|
|
506
511
|
e.currentTarget.style.transform = "translateY(0)";
|
|
507
512
|
e.currentTarget.style.boxShadow = hasChanges && !saving ? "0 4px 12px rgba(102, 126, 234, 0.4)" : "none";
|
|
508
513
|
},
|
|
509
|
-
children: saving ? "Saving..." : hasChanges ? "Save Changes" : "No Changes"
|
|
514
|
+
children: saving ? t("settings.saving", "Saving...") : hasChanges ? t("settings.save", "Save Changes") : t("settings.noChanges", "No Changes")
|
|
510
515
|
}
|
|
511
516
|
)
|
|
512
517
|
] })
|
|
@@ -515,19 +520,22 @@ const SettingsPage = () => {
|
|
|
515
520
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 4, background: "primary50", hasRadius: true, style: { marginBottom: "24px", border: "1px solid #bae6fd" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "center", children: [
|
|
516
521
|
/* @__PURE__ */ jsxRuntime.jsx(icons.Information, { style: { width: "20px", height: "20px", color: "#0284C7" } }),
|
|
517
522
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
|
|
518
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", textColor: "primary700", style: { marginBottom: "4px" }, children: "Current License Status" }),
|
|
523
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", textColor: "primary700", style: { marginBottom: "4px" }, children: t("settings.license.title", "Current License Status") }),
|
|
519
524
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, children: [
|
|
520
525
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: isPremium ? "success100" : "neutral100", textColor: isPremium ? "success700" : "neutral600", children: [
|
|
521
526
|
isPremium ? "✓" : "✗",
|
|
522
|
-
"
|
|
527
|
+
" ",
|
|
528
|
+
t("settings.license.premium", "Premium")
|
|
523
529
|
] }),
|
|
524
530
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: isAdvanced ? "primary100" : "neutral100", textColor: isAdvanced ? "primary700" : "neutral600", children: [
|
|
525
531
|
isAdvanced ? "✓" : "✗",
|
|
526
|
-
"
|
|
532
|
+
" ",
|
|
533
|
+
t("settings.license.advanced", "Advanced")
|
|
527
534
|
] }),
|
|
528
535
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: isEnterprise ? "secondary100" : "neutral100", textColor: isEnterprise ? "secondary700" : "neutral600", children: [
|
|
529
536
|
isEnterprise ? "✓" : "✗",
|
|
530
|
-
"
|
|
537
|
+
" ",
|
|
538
|
+
t("settings.license.enterprise", "Enterprise")
|
|
531
539
|
] })
|
|
532
540
|
] })
|
|
533
541
|
] })
|
|
@@ -538,114 +546,99 @@ const SettingsPage = () => {
|
|
|
538
546
|
designSystem.Accordion.Trigger,
|
|
539
547
|
{
|
|
540
548
|
icon: icons.Cog,
|
|
541
|
-
description: "Basic session tracking configuration",
|
|
542
|
-
children: "General Settings"
|
|
549
|
+
description: t("settings.general.description", "Basic session tracking configuration"),
|
|
550
|
+
children: t("settings.general.title", "General Settings")
|
|
543
551
|
}
|
|
544
552
|
) }),
|
|
545
553
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Accordion.Content, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 6, children: [
|
|
546
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "16px", display: "block", color: theme.colors.neutral[700] }, children: "
|
|
554
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "16px", display: "block", color: theme.colors.neutral[700] }, children: t("settings.general.timeout.title", "SESSION TIMEOUT") }),
|
|
547
555
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Grid.Root, { gap: 6, style: { marginBottom: "32px" }, children: [
|
|
548
556
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 6, s: 12, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
|
|
549
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: "Inactivity Timeout" }),
|
|
557
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: t("settings.general.timeout.inactivity", "Inactivity Timeout") }),
|
|
550
558
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
551
559
|
designSystem.SingleSelect,
|
|
552
560
|
{
|
|
553
561
|
value: String(settings.inactivityTimeout),
|
|
554
562
|
onChange: (value) => handleChange("inactivityTimeout", parseInt(value)),
|
|
555
563
|
children: [
|
|
556
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "5", children: "5 minutes (Very Strict)" }),
|
|
557
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "10", children: "10 minutes (Strict)" }),
|
|
558
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "15", children: "15 minutes (Recommended)" }),
|
|
559
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "30", children: "30 minutes (Moderate)" }),
|
|
560
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "60", children: "1 hour (Relaxed)" }),
|
|
561
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "120", children: "2 hours (Very Relaxed)" })
|
|
564
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "5", children: t("settings.general.timeout.5min", "5 minutes (Very Strict)") }),
|
|
565
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "10", children: t("settings.general.timeout.10min", "10 minutes (Strict)") }),
|
|
566
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "15", children: t("settings.general.timeout.15min", "15 minutes (Recommended)") }),
|
|
567
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "30", children: t("settings.general.timeout.30min", "30 minutes (Moderate)") }),
|
|
568
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "60", children: t("settings.general.timeout.1hour", "1 hour (Relaxed)") }),
|
|
569
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "120", children: t("settings.general.timeout.2hours", "2 hours (Very Relaxed)") })
|
|
562
570
|
]
|
|
563
571
|
}
|
|
564
572
|
),
|
|
565
|
-
/* @__PURE__ */ jsxRuntime.
|
|
566
|
-
"Sessions inactive for more than ",
|
|
567
|
-
settings.inactivityTimeout,
|
|
568
|
-
" minutes will be marked as offline"
|
|
569
|
-
] })
|
|
573
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.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 }) })
|
|
570
574
|
] }) }),
|
|
571
575
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 6, s: 12, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
|
|
572
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: "Last Seen Rate Limit" }),
|
|
576
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: t("settings.general.rateLimit.title", "Last Seen Rate Limit") }),
|
|
573
577
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
574
578
|
designSystem.SingleSelect,
|
|
575
579
|
{
|
|
576
580
|
value: String(settings.lastSeenRateLimit),
|
|
577
581
|
onChange: (value) => handleChange("lastSeenRateLimit", parseInt(value)),
|
|
578
582
|
children: [
|
|
579
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "10", children: "10 seconds" }),
|
|
580
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "30", children: "30 seconds (Recommended)" }),
|
|
581
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "60", children: "1 minute" }),
|
|
582
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "120", children: "2 minutes" }),
|
|
583
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "300", children: "5 minutes" })
|
|
583
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "10", children: t("settings.general.rateLimit.10sec", "10 seconds") }),
|
|
584
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "30", children: t("settings.general.rateLimit.30sec", "30 seconds (Recommended)") }),
|
|
585
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "60", children: t("settings.general.rateLimit.1min", "1 minute") }),
|
|
586
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "120", children: t("settings.general.rateLimit.2min", "2 minutes") }),
|
|
587
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "300", children: t("settings.general.rateLimit.5min", "5 minutes") })
|
|
584
588
|
]
|
|
585
589
|
}
|
|
586
590
|
),
|
|
587
|
-
/* @__PURE__ */ jsxRuntime.
|
|
588
|
-
"Prevents excessive database writes. Updates throttled to once every ",
|
|
589
|
-
settings.lastSeenRateLimit,
|
|
590
|
-
" seconds"
|
|
591
|
-
] })
|
|
591
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.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 }) })
|
|
592
592
|
] }) })
|
|
593
593
|
] }),
|
|
594
594
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Divider, { style: { marginBottom: "24px" } }),
|
|
595
|
-
/* @__PURE__ */ jsxRuntime.
|
|
595
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "16px", display: "block", color: theme.colors.neutral[700] }, children: [
|
|
596
|
+
"🧹 ",
|
|
597
|
+
t("settings.general.cleanup.title", "AUTO-CLEANUP & RETENTION")
|
|
598
|
+
] }),
|
|
596
599
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Grid.Root, { gap: 6, children: [
|
|
597
600
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 6, s: 12, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
|
|
598
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: "Cleanup Interval" }),
|
|
601
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: t("settings.general.cleanup.interval", "Cleanup Interval") }),
|
|
599
602
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
600
603
|
designSystem.SingleSelect,
|
|
601
604
|
{
|
|
602
605
|
value: String(settings.cleanupInterval),
|
|
603
606
|
onChange: (value) => handleChange("cleanupInterval", parseInt(value)),
|
|
604
607
|
children: [
|
|
605
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "15", children: "15 minutes" }),
|
|
606
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "30", children: "30 minutes (Recommended)" }),
|
|
607
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "60", children: "1 hour" }),
|
|
608
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "120", children: "2 hours" })
|
|
608
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "15", children: t("settings.general.cleanup.15min", "15 minutes") }),
|
|
609
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "30", children: t("settings.general.cleanup.30min", "30 minutes (Recommended)") }),
|
|
610
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "60", children: t("settings.general.cleanup.1hour", "1 hour") }),
|
|
611
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "120", children: t("settings.general.cleanup.2hours", "2 hours") })
|
|
609
612
|
]
|
|
610
613
|
}
|
|
611
614
|
),
|
|
612
|
-
/* @__PURE__ */ jsxRuntime.
|
|
613
|
-
"Inactive sessions are automatically cleaned every ",
|
|
614
|
-
settings.cleanupInterval,
|
|
615
|
-
" minutes"
|
|
616
|
-
] })
|
|
615
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.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 }) })
|
|
617
616
|
] }) }),
|
|
618
617
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 6, s: 12, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
|
|
619
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: "Retention Period" }),
|
|
618
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: t("settings.general.retention.title", "Retention Period") }),
|
|
620
619
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
621
620
|
designSystem.SingleSelect,
|
|
622
621
|
{
|
|
623
622
|
value: String(settings.retentionDays),
|
|
624
623
|
onChange: (value) => handleChange("retentionDays", parseInt(value)),
|
|
625
624
|
children: [
|
|
626
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "7", children: "7 days" }),
|
|
627
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "30", children: "30 days" }),
|
|
628
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "60", children: "60 days" }),
|
|
629
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "90", children: "90 days (Recommended)" }),
|
|
630
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "180", children: "180 days" }),
|
|
631
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "365", children: "1 year" }),
|
|
632
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "-1", children: "Forever" })
|
|
625
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "7", children: t("settings.general.retention.7days", "7 days") }),
|
|
626
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "30", children: t("settings.general.retention.30days", "30 days") }),
|
|
627
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "60", children: t("settings.general.retention.60days", "60 days") }),
|
|
628
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "90", children: t("settings.general.retention.90days", "90 days (Recommended)") }),
|
|
629
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "180", children: t("settings.general.retention.180days", "180 days") }),
|
|
630
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "365", children: t("settings.general.retention.1year", "1 year") }),
|
|
631
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "-1", children: t("settings.general.retention.forever", "Forever") })
|
|
633
632
|
]
|
|
634
633
|
}
|
|
635
634
|
),
|
|
636
|
-
/* @__PURE__ */ jsxRuntime.
|
|
637
|
-
"Old sessions deleted after ",
|
|
638
|
-
settings.retentionDays === -1 ? "never" : `${settings.retentionDays} days`
|
|
639
|
-
] })
|
|
635
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.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` }) })
|
|
640
636
|
] }) }),
|
|
641
637
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 12, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 4, background: "danger100", style: { borderRadius: theme.borderRadius.md, border: `2px solid ${theme.colors.danger[200]}` }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "flex-start", children: [
|
|
642
638
|
/* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { style: { width: "18px", height: "18px", color: theme.colors.danger[600], flexShrink: 0, marginTop: "2px" } }),
|
|
643
639
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: { flex: 1 }, children: [
|
|
644
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", textColor: "danger700", style: { marginBottom: "8px", display: "block" }, children: "Danger Zone" }),
|
|
645
|
-
/* @__PURE__ */ jsxRuntime.
|
|
646
|
-
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Clean All Inactive:" }),
|
|
647
|
-
" Permanently deletes all inactive sessions. This cannot be undone."
|
|
648
|
-
] })
|
|
640
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", textColor: "danger700", style: { marginBottom: "8px", display: "block" }, children: t("settings.general.danger.title", "Danger Zone") }),
|
|
641
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.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.") })
|
|
649
642
|
] }),
|
|
650
643
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
651
644
|
designSystem.Button,
|
|
@@ -656,7 +649,7 @@ const SettingsPage = () => {
|
|
|
656
649
|
variant: "danger",
|
|
657
650
|
size: "S",
|
|
658
651
|
style: { flexShrink: 0 },
|
|
659
|
-
children: "Clean Now"
|
|
652
|
+
children: t("settings.general.danger.cleanNow", "Clean Now")
|
|
660
653
|
}
|
|
661
654
|
)
|
|
662
655
|
] }) }) })
|
|
@@ -668,12 +661,12 @@ const SettingsPage = () => {
|
|
|
668
661
|
designSystem.Accordion.Trigger,
|
|
669
662
|
{
|
|
670
663
|
icon: icons.Shield,
|
|
671
|
-
description: "Security policies and threat protection",
|
|
672
|
-
children: "Security Settings"
|
|
664
|
+
description: t("settings.security.description", "Security policies and threat protection"),
|
|
665
|
+
children: t("settings.security.title", "Security Settings")
|
|
673
666
|
}
|
|
674
667
|
) }),
|
|
675
668
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Accordion.Content, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 6, children: [
|
|
676
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "16px", display: "block", color: theme.colors.neutral[700] }, children: "
|
|
669
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "16px", display: "block", color: theme.colors.neutral[700] }, children: t("settings.security.options", "SECURITY OPTIONS") }),
|
|
677
670
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
678
671
|
designSystem.Box,
|
|
679
672
|
{
|
|
@@ -688,32 +681,26 @@ const SettingsPage = () => {
|
|
|
688
681
|
children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 4, children: [
|
|
689
682
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 3, children: [
|
|
690
683
|
/* @__PURE__ */ jsxRuntime.jsx(icons.Shield, { style: { width: 24, height: 24, color: theme.colors.primary[600] } }),
|
|
691
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", fontWeight: "bold", children: "JWT Encryption Key Generator" })
|
|
684
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", fontWeight: "bold", children: t("settings.security.encryption.title", "JWT Encryption Key Generator") })
|
|
692
685
|
] }),
|
|
693
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.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." }),
|
|
694
|
-
/* @__PURE__ */ jsxRuntime.
|
|
686
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.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.") }),
|
|
687
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
695
688
|
designSystem.Alert,
|
|
696
689
|
{
|
|
697
690
|
variant: "default",
|
|
698
|
-
title: "Important",
|
|
691
|
+
title: t("settings.security.encryption.important", "Important"),
|
|
699
692
|
style: { marginTop: 8 },
|
|
700
|
-
children:
|
|
701
|
-
"Add this key to your ",
|
|
702
|
-
/* @__PURE__ */ jsxRuntime.jsx("code", { children: ".env" }),
|
|
703
|
-
" file as ",
|
|
704
|
-
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: "SESSION_ENCRYPTION_KEY" }),
|
|
705
|
-
" for production."
|
|
706
|
-
]
|
|
693
|
+
children: t("settings.security.encryption.envHint", "Add this key to your .env file as SESSION_ENCRYPTION_KEY for production.")
|
|
707
694
|
}
|
|
708
695
|
),
|
|
709
696
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "flex-end", children: [
|
|
710
697
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1 }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
711
698
|
designSystem.TextInput,
|
|
712
699
|
{
|
|
713
|
-
label: "Generated Encryption Key",
|
|
700
|
+
label: t("settings.security.encryption.label", "Generated Encryption Key"),
|
|
714
701
|
value: encryptionKey,
|
|
715
702
|
onChange: (e) => setEncryptionKey(e.target.value),
|
|
716
|
-
placeholder: "Click 'Generate Key' to create a secure key",
|
|
703
|
+
placeholder: t("settings.security.encryption.placeholder", "Click 'Generate Key' to create a secure key"),
|
|
717
704
|
type: showEncryptionKey ? "text" : "password"
|
|
718
705
|
}
|
|
719
706
|
) }),
|
|
@@ -723,7 +710,7 @@ const SettingsPage = () => {
|
|
|
723
710
|
variant: "secondary",
|
|
724
711
|
onClick: () => setShowEncryptionKey(!showEncryptionKey),
|
|
725
712
|
size: "L",
|
|
726
|
-
children: showEncryptionKey ? "Hide" : "Show"
|
|
713
|
+
children: showEncryptionKey ? t("settings.security.encryption.hide", "Hide") : t("settings.security.encryption.show", "Show")
|
|
727
714
|
}
|
|
728
715
|
)
|
|
729
716
|
] }),
|
|
@@ -739,11 +726,11 @@ const SettingsPage = () => {
|
|
|
739
726
|
setShowEncryptionKey(true);
|
|
740
727
|
toggleNotification({
|
|
741
728
|
type: "success",
|
|
742
|
-
message: "32-character encryption key generated!"
|
|
729
|
+
message: t("notifications.success.keyGenerated", "32-character encryption key generated!")
|
|
743
730
|
});
|
|
744
731
|
},
|
|
745
732
|
size: "L",
|
|
746
|
-
children: "Generate Key"
|
|
733
|
+
children: t("settings.security.encryption.generate", "Generate Key")
|
|
747
734
|
}
|
|
748
735
|
),
|
|
749
736
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -756,13 +743,13 @@ const SettingsPage = () => {
|
|
|
756
743
|
navigator.clipboard.writeText(encryptionKey);
|
|
757
744
|
toggleNotification({
|
|
758
745
|
type: "success",
|
|
759
|
-
message: "Encryption key copied to clipboard!"
|
|
746
|
+
message: t("notifications.success.keyCopied", "Encryption key copied to clipboard!")
|
|
760
747
|
});
|
|
761
748
|
}
|
|
762
749
|
},
|
|
763
750
|
disabled: !encryptionKey,
|
|
764
751
|
size: "L",
|
|
765
|
-
children: "Copy to Clipboard"
|
|
752
|
+
children: t("settings.security.encryption.copy", "Copy to Clipboard")
|
|
766
753
|
}
|
|
767
754
|
),
|
|
768
755
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -776,13 +763,13 @@ const SettingsPage = () => {
|
|
|
776
763
|
navigator.clipboard.writeText(envLine);
|
|
777
764
|
toggleNotification({
|
|
778
765
|
type: "success",
|
|
779
|
-
message: "Copied as .env format!"
|
|
766
|
+
message: t("notifications.success.envCopied", "Copied as .env format!")
|
|
780
767
|
});
|
|
781
768
|
}
|
|
782
769
|
},
|
|
783
770
|
disabled: !encryptionKey,
|
|
784
771
|
size: "L",
|
|
785
|
-
children: "Copy for .env"
|
|
772
|
+
children: t("settings.security.encryption.copyEnv", "Copy for .env")
|
|
786
773
|
}
|
|
787
774
|
)
|
|
788
775
|
] }),
|
|
@@ -799,7 +786,7 @@ const SettingsPage = () => {
|
|
|
799
786
|
wordBreak: "break-all"
|
|
800
787
|
},
|
|
801
788
|
children: [
|
|
802
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", style: { marginBottom: 8, display: "block" }, children: "Add to .env file:" }),
|
|
789
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", style: { marginBottom: 8, display: "block" }, children: t("settings.security.encryption.envLabel", "Add to .env file:") }),
|
|
803
790
|
/* @__PURE__ */ jsxRuntime.jsxs("code", { style: { color: theme.colors.primary[700] }, children: [
|
|
804
791
|
"SESSION_ENCRYPTION_KEY=",
|
|
805
792
|
encryptionKey
|
|
@@ -832,10 +819,10 @@ const SettingsPage = () => {
|
|
|
832
819
|
fontWeight: "bold",
|
|
833
820
|
textColor: settings.blockSuspiciousSessions ? "success700" : "neutral800",
|
|
834
821
|
style: { fontSize: "16px" },
|
|
835
|
-
children: "Block Suspicious Sessions"
|
|
822
|
+
children: t("settings.security.blockSuspicious.title", "Block Suspicious Sessions")
|
|
836
823
|
}
|
|
837
824
|
),
|
|
838
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "13px", lineHeight: "1.6" }, children: "Automatically block sessions from VPNs, proxies, or threat IPs" })
|
|
825
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.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") })
|
|
839
826
|
] })
|
|
840
827
|
] })
|
|
841
828
|
}
|
|
@@ -862,10 +849,10 @@ const SettingsPage = () => {
|
|
|
862
849
|
fontWeight: "bold",
|
|
863
850
|
textColor: settings.enableGeolocation ? "success700" : "neutral800",
|
|
864
851
|
style: { fontSize: "16px" },
|
|
865
|
-
children: "IP Geolocation"
|
|
852
|
+
children: t("settings.security.geolocation.title", "IP Geolocation")
|
|
866
853
|
}
|
|
867
854
|
),
|
|
868
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "13px", lineHeight: "1.6" }, children: "Fetch location data for each session (Premium)" })
|
|
855
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "13px", lineHeight: "1.6" }, children: t("settings.security.geolocation.description", "Fetch location data for each session (Premium)") })
|
|
869
856
|
] })
|
|
870
857
|
] })
|
|
871
858
|
}
|
|
@@ -891,10 +878,10 @@ const SettingsPage = () => {
|
|
|
891
878
|
fontWeight: "bold",
|
|
892
879
|
textColor: settings.enableSecurityScoring ? "success700" : "neutral800",
|
|
893
880
|
style: { fontSize: "16px" },
|
|
894
|
-
children: "Security Scoring"
|
|
881
|
+
children: t("settings.security.scoring.title", "Security Scoring")
|
|
895
882
|
}
|
|
896
883
|
),
|
|
897
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "13px", lineHeight: "1.6" }, children: "Calculate security scores and detect threats (Premium)" })
|
|
884
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "13px", lineHeight: "1.6" }, children: t("settings.security.scoring.description", "Calculate security scores and detect threats (Premium)") })
|
|
898
885
|
] })
|
|
899
886
|
] })
|
|
900
887
|
}
|
|
@@ -902,7 +889,10 @@ const SettingsPage = () => {
|
|
|
902
889
|
] })
|
|
903
890
|
] }) }),
|
|
904
891
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Root, { gap: 6, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 6, s: 12, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
|
|
905
|
-
/* @__PURE__ */ jsxRuntime.
|
|
892
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: [
|
|
893
|
+
"🚫 ",
|
|
894
|
+
t("settings.security.maxFailed.title", "Max Failed Login Attempts")
|
|
895
|
+
] }),
|
|
906
896
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
907
897
|
designSystem.NumberInput,
|
|
908
898
|
{
|
|
@@ -912,11 +902,7 @@ const SettingsPage = () => {
|
|
|
912
902
|
max: 20
|
|
913
903
|
}
|
|
914
904
|
),
|
|
915
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 2, background: "warning50", style: { borderRadius: "4px", marginTop: "8px" }, children: /* @__PURE__ */ jsxRuntime.
|
|
916
|
-
"User will be blocked after ",
|
|
917
|
-
settings.maxFailedLogins,
|
|
918
|
-
" failed attempts"
|
|
919
|
-
] }) })
|
|
905
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 2, background: "warning50", style: { borderRadius: "4px", marginTop: "8px" }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.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 }) }) })
|
|
920
906
|
] }) }) })
|
|
921
907
|
] }) })
|
|
922
908
|
] }),
|
|
@@ -925,14 +911,17 @@ const SettingsPage = () => {
|
|
|
925
911
|
designSystem.Accordion.Trigger,
|
|
926
912
|
{
|
|
927
913
|
icon: icons.Mail,
|
|
928
|
-
description: "Email alerts for security events",
|
|
929
|
-
children: "Email Notifications (Advanced)"
|
|
914
|
+
description: t("settings.email.description", "Email alerts for security events"),
|
|
915
|
+
children: t("settings.email.title", "Email Notifications (Advanced)")
|
|
930
916
|
}
|
|
931
917
|
) }),
|
|
932
918
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Accordion.Content, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 6, children: [
|
|
933
919
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { background: "neutral100", padding: 5, style: { borderRadius: theme.borderRadius.md, marginBottom: "32px" }, children: [
|
|
934
|
-
/* @__PURE__ */ jsxRuntime.
|
|
935
|
-
|
|
920
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "8px", display: "block", textAlign: "center", color: theme.colors.neutral[700] }, children: [
|
|
921
|
+
"📧 ",
|
|
922
|
+
t("settings.email.alerts.title", "EMAIL ALERTS")
|
|
923
|
+
] }),
|
|
924
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.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") }),
|
|
936
925
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Root, { gap: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 12, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
937
926
|
ToggleCard,
|
|
938
927
|
{
|
|
@@ -954,17 +943,20 @@ const SettingsPage = () => {
|
|
|
954
943
|
fontWeight: "bold",
|
|
955
944
|
textColor: settings.enableEmailAlerts ? "success700" : "neutral800",
|
|
956
945
|
style: { fontSize: "16px" },
|
|
957
|
-
children: "Enable Email Alerts"
|
|
946
|
+
children: t("settings.email.enable.title", "Enable Email Alerts")
|
|
958
947
|
}
|
|
959
948
|
),
|
|
960
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "13px", lineHeight: "1.6" }, children: "Send security alerts for suspicious logins, new locations, and VPN/Proxy usage" })
|
|
949
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.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") })
|
|
961
950
|
] })
|
|
962
951
|
] })
|
|
963
952
|
}
|
|
964
953
|
) }) })
|
|
965
954
|
] }),
|
|
966
955
|
settings.enableEmailAlerts && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
967
|
-
/* @__PURE__ */ jsxRuntime.
|
|
956
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "16px", display: "block", color: theme.colors.neutral[700] }, children: [
|
|
957
|
+
"⚙️ ",
|
|
958
|
+
t("settings.email.types.title", "ALERT TYPES")
|
|
959
|
+
] }),
|
|
968
960
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Grid.Root, { gap: 4, style: { marginBottom: "32px" }, children: [
|
|
969
961
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 4, s: 12, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
970
962
|
designSystem.Box,
|
|
@@ -983,7 +975,7 @@ const SettingsPage = () => {
|
|
|
983
975
|
{
|
|
984
976
|
checked: settings.alertOnSuspiciousLogin,
|
|
985
977
|
onChange: () => handleChange("alertOnSuspiciousLogin", !settings.alertOnSuspiciousLogin),
|
|
986
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "semiBold", style: { fontSize: "14px" }, children: "
|
|
978
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "semiBold", style: { fontSize: "14px" }, children: t("settings.email.types.suspicious", "Suspicious Login") })
|
|
987
979
|
}
|
|
988
980
|
)
|
|
989
981
|
}
|
|
@@ -1005,7 +997,7 @@ const SettingsPage = () => {
|
|
|
1005
997
|
{
|
|
1006
998
|
checked: settings.alertOnNewLocation,
|
|
1007
999
|
onChange: () => handleChange("alertOnNewLocation", !settings.alertOnNewLocation),
|
|
1008
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "semiBold", style: { fontSize: "14px" }, children: "
|
|
1000
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "semiBold", style: { fontSize: "14px" }, children: t("settings.email.types.newLocation", "New Location") })
|
|
1009
1001
|
}
|
|
1010
1002
|
)
|
|
1011
1003
|
}
|
|
@@ -1027,24 +1019,24 @@ const SettingsPage = () => {
|
|
|
1027
1019
|
{
|
|
1028
1020
|
checked: settings.alertOnVpnProxy,
|
|
1029
1021
|
onChange: () => handleChange("alertOnVpnProxy", !settings.alertOnVpnProxy),
|
|
1030
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "semiBold", style: { fontSize: "14px" }, children: "
|
|
1022
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "semiBold", style: { fontSize: "14px" }, children: t("settings.email.types.vpnProxy", "VPN/Proxy") })
|
|
1031
1023
|
}
|
|
1032
1024
|
)
|
|
1033
1025
|
}
|
|
1034
1026
|
) })
|
|
1035
1027
|
] }),
|
|
1036
1028
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Divider, { style: { marginBottom: "24px" } }),
|
|
1037
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "8px", display: "block", color: theme.colors.neutral[700] }, children: "
|
|
1038
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { marginBottom: "20px", display: "block", fontSize: "12px" }, children: "Customize email notification templates with dynamic variables" }),
|
|
1029
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", fontWeight: "bold", style: { marginBottom: "8px", display: "block", color: theme.colors.neutral[700] }, children: t("settings.email.templates.title", "EMAIL TEMPLATES") }),
|
|
1030
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.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") }),
|
|
1039
1031
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tabs.Root, { value: activeTemplateTab, onValueChange: setActiveTemplateTab, children: [
|
|
1040
1032
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tabs.List, { "aria-label": "Email Templates", children: [
|
|
1041
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Trigger, { value: "suspiciousLogin", children: "
|
|
1042
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Trigger, { value: "newLocation", children: "
|
|
1043
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Trigger, { value: "vpnProxy", children: "
|
|
1033
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Trigger, { value: "suspiciousLogin", children: t("settings.email.templates.tab.suspicious", "Suspicious Login") }),
|
|
1034
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Trigger, { value: "newLocation", children: t("settings.email.templates.tab.newLocation", "New Location") }),
|
|
1035
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Trigger, { value: "vpnProxy", children: t("settings.email.templates.tab.vpnProxy", "VPN/Proxy") })
|
|
1044
1036
|
] }),
|
|
1045
1037
|
Object.keys(settings.emailTemplates).map((templateKey) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Content, { value: templateKey, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { paddingTop: 4, children: [
|
|
1046
1038
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: { marginBottom: "24px" }, children: [
|
|
1047
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: "
|
|
1039
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: t("settings.email.templates.subject", "Email Subject") }),
|
|
1048
1040
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1049
1041
|
designSystem.TextInput,
|
|
1050
1042
|
{
|
|
@@ -1054,7 +1046,7 @@ const SettingsPage = () => {
|
|
|
1054
1046
|
newTemplates[templateKey].subject = e.target.value;
|
|
1055
1047
|
handleChange("emailTemplates", newTemplates);
|
|
1056
1048
|
},
|
|
1057
|
-
placeholder: "Enter email subject..."
|
|
1049
|
+
placeholder: t("settings.email.templates.subjectPlaceholder", "Enter email subject...")
|
|
1058
1050
|
}
|
|
1059
1051
|
)
|
|
1060
1052
|
] }),
|
|
@@ -1067,7 +1059,7 @@ const SettingsPage = () => {
|
|
|
1067
1059
|
children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, children: [
|
|
1068
1060
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, children: [
|
|
1069
1061
|
/* @__PURE__ */ jsxRuntime.jsx(icons.Code, { style: { width: "16px", height: "16px", color: theme.colors.primary[600] } }),
|
|
1070
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", textColor: "primary600", children: "Available Variables (click to copy)" })
|
|
1062
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", textColor: "primary600", children: t("settings.email.templates.variables", "Available Variables (click to copy)") })
|
|
1071
1063
|
] }),
|
|
1072
1064
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, wrap: "wrap", children: TEMPLATE_VARIABLES[templateKey].map(({ var: variable, desc }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1073
1065
|
designSystem.Button,
|
|
@@ -1076,7 +1068,7 @@ const SettingsPage = () => {
|
|
|
1076
1068
|
variant: "tertiary",
|
|
1077
1069
|
onClick: () => {
|
|
1078
1070
|
navigator.clipboard.writeText(variable);
|
|
1079
|
-
toggleNotification({ type: "success", message:
|
|
1071
|
+
toggleNotification({ type: "success", message: t("notifications.success.variableCopied", "{variable} copied!", { variable }) });
|
|
1080
1072
|
},
|
|
1081
1073
|
style: {
|
|
1082
1074
|
fontFamily: "monospace",
|
|
@@ -1100,10 +1092,13 @@ const SettingsPage = () => {
|
|
|
1100
1092
|
children: [
|
|
1101
1093
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", style: { marginBottom: "16px" }, children: [
|
|
1102
1094
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, children: [
|
|
1103
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1104
|
-
|
|
1095
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "delta", fontWeight: "bold", style: { fontSize: "18px" }, children: [
|
|
1096
|
+
"🎨 ",
|
|
1097
|
+
t("settings.email.templates.html.title", "HTML Template")
|
|
1098
|
+
] }),
|
|
1099
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { variant: "success", children: t("settings.email.templates.html.badge", "Main Template") })
|
|
1105
1100
|
] }),
|
|
1106
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1101
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1107
1102
|
designSystem.Button,
|
|
1108
1103
|
{
|
|
1109
1104
|
variant: "tertiary",
|
|
@@ -1113,17 +1108,16 @@ const SettingsPage = () => {
|
|
|
1113
1108
|
const newTemplates = { ...settings.emailTemplates };
|
|
1114
1109
|
newTemplates[templateKey].html = defaultTemplates[templateKey].html;
|
|
1115
1110
|
handleChange("emailTemplates", newTemplates);
|
|
1116
|
-
toggleNotification({ type: "success", message: "Default
|
|
1111
|
+
toggleNotification({ type: "success", message: t("notifications.success.defaultLoaded", "Default template loaded!") });
|
|
1117
1112
|
},
|
|
1118
|
-
children:
|
|
1113
|
+
children: [
|
|
1114
|
+
"📋 ",
|
|
1115
|
+
t("settings.email.templates.html.loadDefault", "Load Default")
|
|
1116
|
+
]
|
|
1119
1117
|
}
|
|
1120
1118
|
)
|
|
1121
1119
|
] }),
|
|
1122
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1123
|
-
"HTML template for email notifications. Use variables like ",
|
|
1124
|
-
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "{{user.email}}" }),
|
|
1125
|
-
" for dynamic content."
|
|
1126
|
-
] }),
|
|
1120
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.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.") }),
|
|
1127
1121
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1128
1122
|
designSystem.Box,
|
|
1129
1123
|
{
|
|
@@ -1179,19 +1173,22 @@ const SettingsPage = () => {
|
|
|
1179
1173
|
}
|
|
1180
1174
|
),
|
|
1181
1175
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, style: { marginTop: "12px" }, wrap: "wrap", children: [
|
|
1182
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1176
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1183
1177
|
designSystem.Button,
|
|
1184
1178
|
{
|
|
1185
1179
|
variant: "secondary",
|
|
1186
1180
|
size: "S",
|
|
1187
1181
|
onClick: () => {
|
|
1188
1182
|
navigator.clipboard.writeText(settings.emailTemplates[templateKey].html);
|
|
1189
|
-
toggleNotification({ type: "success", message: "HTML template copied!" });
|
|
1183
|
+
toggleNotification({ type: "success", message: t("notifications.success.htmlCopied", "HTML template copied!") });
|
|
1190
1184
|
},
|
|
1191
|
-
children:
|
|
1185
|
+
children: [
|
|
1186
|
+
"📋 ",
|
|
1187
|
+
t("settings.email.templates.html.copy", "Copy Template")
|
|
1188
|
+
]
|
|
1192
1189
|
}
|
|
1193
1190
|
),
|
|
1194
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1191
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1195
1192
|
designSystem.Button,
|
|
1196
1193
|
{
|
|
1197
1194
|
variant: "tertiary",
|
|
@@ -1200,13 +1197,16 @@ const SettingsPage = () => {
|
|
|
1200
1197
|
const validation = validateTemplate(settings.emailTemplates[templateKey].html, templateKey);
|
|
1201
1198
|
toggleNotification({
|
|
1202
1199
|
type: validation.isValid ? "success" : "warning",
|
|
1203
|
-
message: validation.isValid ?
|
|
1200
|
+
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.")
|
|
1204
1201
|
});
|
|
1205
1202
|
},
|
|
1206
|
-
children:
|
|
1203
|
+
children: [
|
|
1204
|
+
"✓ ",
|
|
1205
|
+
t("settings.email.templates.html.validate", "Validate")
|
|
1206
|
+
]
|
|
1207
1207
|
}
|
|
1208
1208
|
),
|
|
1209
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1209
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1210
1210
|
designSystem.Button,
|
|
1211
1211
|
{
|
|
1212
1212
|
variant: "tertiary",
|
|
@@ -1216,10 +1216,13 @@ const SettingsPage = () => {
|
|
|
1216
1216
|
const chars = settings.emailTemplates[templateKey].html.length;
|
|
1217
1217
|
toggleNotification({
|
|
1218
1218
|
type: "info",
|
|
1219
|
-
message:
|
|
1219
|
+
message: t("notifications.info.templateStats", "Template has {lines} lines and {chars} characters", { lines, chars })
|
|
1220
1220
|
});
|
|
1221
1221
|
},
|
|
1222
|
-
children:
|
|
1222
|
+
children: [
|
|
1223
|
+
"ℹ️ ",
|
|
1224
|
+
t("settings.email.templates.html.info", "Template Info")
|
|
1225
|
+
]
|
|
1223
1226
|
}
|
|
1224
1227
|
)
|
|
1225
1228
|
] })
|
|
@@ -1235,10 +1238,13 @@ const SettingsPage = () => {
|
|
|
1235
1238
|
children: [
|
|
1236
1239
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", style: { marginBottom: "16px" }, children: [
|
|
1237
1240
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, children: [
|
|
1238
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1239
|
-
|
|
1241
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "delta", fontWeight: "bold", style: { fontSize: "18px" }, children: [
|
|
1242
|
+
"📄 ",
|
|
1243
|
+
t("settings.email.templates.text.title", "Text Template")
|
|
1244
|
+
] }),
|
|
1245
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { variant: "secondary", children: t("settings.email.templates.text.badge", "Fallback") })
|
|
1240
1246
|
] }),
|
|
1241
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1247
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1242
1248
|
designSystem.Button,
|
|
1243
1249
|
{
|
|
1244
1250
|
variant: "tertiary",
|
|
@@ -1248,13 +1254,16 @@ const SettingsPage = () => {
|
|
|
1248
1254
|
const newTemplates = { ...settings.emailTemplates };
|
|
1249
1255
|
newTemplates[templateKey].text = defaultTemplates[templateKey].text;
|
|
1250
1256
|
handleChange("emailTemplates", newTemplates);
|
|
1251
|
-
toggleNotification({ type: "success", message: "Default
|
|
1257
|
+
toggleNotification({ type: "success", message: t("notifications.success.defaultLoaded", "Default template loaded!") });
|
|
1252
1258
|
},
|
|
1253
|
-
children:
|
|
1259
|
+
children: [
|
|
1260
|
+
"📋 ",
|
|
1261
|
+
t("settings.email.templates.text.loadDefault", "Load Default")
|
|
1262
|
+
]
|
|
1254
1263
|
}
|
|
1255
1264
|
)
|
|
1256
1265
|
] }),
|
|
1257
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { marginBottom: "16px", display: "block", fontSize: "14px" }, children: "Plain text version (no HTML) as fallback for older email clients" }),
|
|
1266
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.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") }),
|
|
1258
1267
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1259
1268
|
designSystem.Box,
|
|
1260
1269
|
{
|
|
@@ -1309,16 +1318,19 @@ const SettingsPage = () => {
|
|
|
1309
1318
|
]
|
|
1310
1319
|
}
|
|
1311
1320
|
),
|
|
1312
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, style: { marginTop: "12px" }, wrap: "wrap", children: /* @__PURE__ */ jsxRuntime.
|
|
1321
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, style: { marginTop: "12px" }, wrap: "wrap", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1313
1322
|
designSystem.Button,
|
|
1314
1323
|
{
|
|
1315
1324
|
variant: "secondary",
|
|
1316
1325
|
size: "S",
|
|
1317
1326
|
onClick: () => {
|
|
1318
1327
|
navigator.clipboard.writeText(settings.emailTemplates[templateKey].text);
|
|
1319
|
-
toggleNotification({ type: "success", message: "Text template copied!" });
|
|
1328
|
+
toggleNotification({ type: "success", message: t("notifications.success.textCopied", "Text template copied!") });
|
|
1320
1329
|
},
|
|
1321
|
-
children:
|
|
1330
|
+
children: [
|
|
1331
|
+
"📋 ",
|
|
1332
|
+
t("settings.email.templates.text.copy", "Copy Template")
|
|
1333
|
+
]
|
|
1322
1334
|
}
|
|
1323
1335
|
) })
|
|
1324
1336
|
]
|
|
@@ -1334,8 +1346,8 @@ const SettingsPage = () => {
|
|
|
1334
1346
|
designSystem.Accordion.Trigger,
|
|
1335
1347
|
{
|
|
1336
1348
|
icon: icons.Code,
|
|
1337
|
-
description: "Discord & Slack integration",
|
|
1338
|
-
children: "Webhook Integration (Advanced)"
|
|
1349
|
+
description: t("settings.webhooks.description", "Discord & Slack integration"),
|
|
1350
|
+
children: t("settings.webhooks.title", "Webhook Integration (Advanced)")
|
|
1339
1351
|
}
|
|
1340
1352
|
) }),
|
|
1341
1353
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Accordion.Content, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 6, children: [
|
|
@@ -1360,17 +1372,20 @@ const SettingsPage = () => {
|
|
|
1360
1372
|
fontWeight: "bold",
|
|
1361
1373
|
textColor: settings.enableWebhooks ? "success700" : "neutral800",
|
|
1362
1374
|
style: { fontSize: "16px" },
|
|
1363
|
-
children: "Enable Webhooks"
|
|
1375
|
+
children: t("settings.webhooks.enable.title", "Enable Webhooks")
|
|
1364
1376
|
}
|
|
1365
1377
|
),
|
|
1366
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "13px", lineHeight: "1.6" }, children: "Send session events to Discord, Slack, or custom endpoints" })
|
|
1378
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.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") })
|
|
1367
1379
|
] })
|
|
1368
1380
|
] })
|
|
1369
1381
|
}
|
|
1370
1382
|
) }) }) }),
|
|
1371
1383
|
settings.enableWebhooks && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Grid.Root, { gap: 6, children: [
|
|
1372
1384
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 12, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
|
|
1373
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1385
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "12px", display: "block" }, children: [
|
|
1386
|
+
"🔗 ",
|
|
1387
|
+
t("settings.webhooks.discord.title", "Discord Webhook URL")
|
|
1388
|
+
] }),
|
|
1374
1389
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1375
1390
|
designSystem.Box,
|
|
1376
1391
|
{
|
|
@@ -1383,7 +1398,7 @@ const SettingsPage = () => {
|
|
|
1383
1398
|
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1384
1399
|
"textarea",
|
|
1385
1400
|
{
|
|
1386
|
-
placeholder: "https://discord.com/api/webhooks/123456789/abcdefghijklmnopqrstuvwxyz...",
|
|
1401
|
+
placeholder: t("settings.webhooks.discord.placeholder", "https://discord.com/api/webhooks/123456789/abcdefghijklmnopqrstuvwxyz..."),
|
|
1387
1402
|
value: settings.discordWebhookUrl,
|
|
1388
1403
|
onChange: (e) => handleChange("discordWebhookUrl", e.target.value),
|
|
1389
1404
|
rows: 3,
|
|
@@ -1405,15 +1420,15 @@ const SettingsPage = () => {
|
|
|
1405
1420
|
}
|
|
1406
1421
|
),
|
|
1407
1422
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", style: { marginTop: "10px" }, children: [
|
|
1408
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "12px" }, children: "
|
|
1409
|
-
settings.discordWebhookUrl && /* @__PURE__ */ jsxRuntime.
|
|
1410
|
-
settings.discordWebhookUrl.length,
|
|
1411
|
-
" characters"
|
|
1412
|
-
] })
|
|
1423
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "12px" }, children: t("settings.webhooks.discord.hint", "Optional: Post session alerts to your Discord channel") }),
|
|
1424
|
+
settings.discordWebhookUrl && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "primary600", style: { fontSize: "11px", fontFamily: "monospace" }, children: t("settings.webhooks.characters", "{count} characters", { count: settings.discordWebhookUrl.length }) })
|
|
1413
1425
|
] })
|
|
1414
1426
|
] }) }),
|
|
1415
1427
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 12, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
|
|
1416
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1428
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "12px", display: "block" }, children: [
|
|
1429
|
+
"💬 ",
|
|
1430
|
+
t("settings.webhooks.slack.title", "Slack Webhook URL")
|
|
1431
|
+
] }),
|
|
1417
1432
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1418
1433
|
designSystem.Box,
|
|
1419
1434
|
{
|
|
@@ -1426,7 +1441,7 @@ const SettingsPage = () => {
|
|
|
1426
1441
|
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1427
1442
|
"textarea",
|
|
1428
1443
|
{
|
|
1429
|
-
placeholder: "https://hooks.slack.com/services/XXXX/XXXX/XXXX",
|
|
1444
|
+
placeholder: t("settings.webhooks.slack.placeholder", "https://hooks.slack.com/services/XXXX/XXXX/XXXX"),
|
|
1430
1445
|
value: settings.slackWebhookUrl,
|
|
1431
1446
|
onChange: (e) => handleChange("slackWebhookUrl", e.target.value),
|
|
1432
1447
|
rows: 3,
|
|
@@ -1448,11 +1463,8 @@ const SettingsPage = () => {
|
|
|
1448
1463
|
}
|
|
1449
1464
|
),
|
|
1450
1465
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", style: { marginTop: "10px" }, children: [
|
|
1451
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "12px" }, children: "
|
|
1452
|
-
settings.slackWebhookUrl && /* @__PURE__ */ jsxRuntime.
|
|
1453
|
-
settings.slackWebhookUrl.length,
|
|
1454
|
-
" characters"
|
|
1455
|
-
] })
|
|
1466
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "12px" }, children: t("settings.webhooks.slack.hint", "Optional: Post session alerts to your Slack workspace") }),
|
|
1467
|
+
settings.slackWebhookUrl && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "primary600", style: { fontSize: "11px", fontFamily: "monospace" }, children: t("settings.webhooks.characters", "{count} characters", { count: settings.slackWebhookUrl.length }) })
|
|
1456
1468
|
] })
|
|
1457
1469
|
] }) })
|
|
1458
1470
|
] })
|
|
@@ -1462,8 +1474,8 @@ const SettingsPage = () => {
|
|
|
1462
1474
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 5, background: "primary100", style: { borderRadius: theme.borderRadius.md, marginTop: "32px", border: "2px solid #BAE6FD" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, alignItems: "flex-start", children: [
|
|
1463
1475
|
/* @__PURE__ */ jsxRuntime.jsx(icons.Check, { style: { width: "20px", height: "20px", color: theme.colors.success[600], flexShrink: 0, marginTop: "2px" } }),
|
|
1464
1476
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: { flex: 1 }, children: [
|
|
1465
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", style: { marginBottom: "8px", display: "block", color: theme.colors.primary[700] }, children: "
|
|
1466
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.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." })
|
|
1477
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", style: { marginBottom: "8px", display: "block", color: theme.colors.primary[700] }, children: t("settings.footer.title", "Database-Backed Settings") }),
|
|
1478
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.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.") })
|
|
1467
1479
|
] })
|
|
1468
1480
|
] }) })
|
|
1469
1481
|
] })
|