strapi-plugin-magic-sessionmanager 4.5.1 → 4.5.3

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.
@@ -4,8 +4,8 @@ import { useFetchClient } from "@strapi/strapi/admin";
4
4
  import styled, { css, keyframes } from "styled-components";
5
5
  import { Loader, Typography, Box, Flex, Badge } from "@strapi/design-system";
6
6
  import { ChartBubble, Crown, User, Clock, Monitor } from "@strapi/icons";
7
- import { a as pluginId } from "./index-BbbrBv3t.mjs";
8
- import { u as useLicense } from "./useLicense-Bszkymz3.mjs";
7
+ import { a as pluginId } from "./index-CwxKazpc.mjs";
8
+ import { u as useLicense } from "./useLicense-B9WW9s_d.mjs";
9
9
  const theme = {
10
10
  shadows: {
11
11
  sm: "0 1px 3px 0 rgba(0, 0, 0, 0.1)",
@@ -6,8 +6,8 @@ const admin = require("@strapi/strapi/admin");
6
6
  const styled = require("styled-components");
7
7
  const designSystem = require("@strapi/design-system");
8
8
  const icons = require("@strapi/icons");
9
- const index = require("./index-BRESWp1b.js");
10
- const useLicense = require("./useLicense-D_wQcoMn.js");
9
+ const index = require("./index-CTxGMDHr.js");
10
+ const useLicense = require("./useLicense-BEbtA_Zo.js");
11
11
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
12
12
  const styled__default = /* @__PURE__ */ _interopDefault(styled);
13
13
  const theme = {
@@ -3,10 +3,10 @@ import { useState, useEffect } from "react";
3
3
  import { useIntl } from "react-intl";
4
4
  import { useFetchClient, useNotification, Page } from "@strapi/strapi/admin";
5
5
  import styled, { css, keyframes } from "styled-components";
6
- import { p as parseUserAgent, a as pluginId$1, g as getTranslation } from "./index-BbbrBv3t.mjs";
6
+ import { p as parseUserAgent, a as pluginId$1, g as getTranslation } from "./index-CwxKazpc.mjs";
7
7
  import { Modal, Flex, Box, Typography, Divider, Button, Loader, SingleSelect, SingleSelectOption, Thead, Tr, Th, Tbody, Td, Table, TextInput } from "@strapi/design-system";
8
8
  import { Check, Information, Monitor, Server, Clock, Cross, Earth, Shield, Crown, Phone, Download, User, Eye, Trash, Search, Key } from "@strapi/icons";
9
- import { u as useLicense } from "./useLicense-Bszkymz3.mjs";
9
+ import { u as useLicense } from "./useLicense-B9WW9s_d.mjs";
10
10
  import { S as ShowHideButton, T as TertiaryButton, D as DangerButton, I as IconButtonPrimary, a as IconButtonWarning, b as IconButtonDanger } from "./StyledButtons-Cz8oYhmc.mjs";
11
11
  import { useNavigate } from "react-router-dom";
12
12
  const theme = {
@@ -5,10 +5,10 @@ const react = require("react");
5
5
  const reactIntl = require("react-intl");
6
6
  const admin = require("@strapi/strapi/admin");
7
7
  const styled = require("styled-components");
8
- const index = require("./index-BRESWp1b.js");
8
+ const index = require("./index-CTxGMDHr.js");
9
9
  const designSystem = require("@strapi/design-system");
10
10
  const icons = require("@strapi/icons");
11
- const useLicense = require("./useLicense-D_wQcoMn.js");
11
+ const useLicense = require("./useLicense-BEbtA_Zo.js");
12
12
  const StyledButtons = require("./StyledButtons-DDuxnYz8.js");
13
13
  const reactRouterDom = require("react-router-dom");
14
14
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
@@ -4,7 +4,7 @@ import { Loader, Box, Alert, Flex, Typography, Button, Badge, Accordion } from "
4
4
  import { useFetchClient, useNotification } from "@strapi/strapi/admin";
5
5
  import { ArrowClockwise, Duplicate, Download, User, Shield, Sparkle, ChartBubble } from "@strapi/icons";
6
6
  import styled, { css, keyframes } from "styled-components";
7
- import { a as pluginId } from "./index-BbbrBv3t.mjs";
7
+ import { a as pluginId } from "./index-CwxKazpc.mjs";
8
8
  const theme = {
9
9
  borderRadius: { lg: "12px" }
10
10
  };
@@ -6,7 +6,7 @@ const designSystem = require("@strapi/design-system");
6
6
  const admin = require("@strapi/strapi/admin");
7
7
  const icons = require("@strapi/icons");
8
8
  const styled = require("styled-components");
9
- const index = require("./index-BRESWp1b.js");
9
+ const index = require("./index-CTxGMDHr.js");
10
10
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
11
11
  const styled__default = /* @__PURE__ */ _interopDefault(styled);
12
12
  const theme = {
@@ -4,7 +4,7 @@ import { useIntl } from "react-intl";
4
4
  import { Box, Typography, Flex, Grid } from "@strapi/design-system";
5
5
  import { Check, Cross, Clock, User } from "@strapi/icons";
6
6
  import { useFetchClient } from "@strapi/strapi/admin";
7
- import { g as getTranslation } from "./index-BbbrBv3t.mjs";
7
+ import { g as getTranslation } from "./index-CwxKazpc.mjs";
8
8
  const OnlineUsersWidget = () => {
9
9
  const { formatMessage } = useIntl();
10
10
  const { get } = useFetchClient();
@@ -6,7 +6,7 @@ const reactIntl = require("react-intl");
6
6
  const designSystem = require("@strapi/design-system");
7
7
  const icons = require("@strapi/icons");
8
8
  const admin = require("@strapi/strapi/admin");
9
- const index = require("./index-BRESWp1b.js");
9
+ const index = require("./index-CTxGMDHr.js");
10
10
  const OnlineUsersWidget = () => {
11
11
  const { formatMessage } = reactIntl.useIntl();
12
12
  const { get } = admin.useFetchClient();
@@ -7,11 +7,85 @@ const designSystem = require("@strapi/design-system");
7
7
  const admin = require("@strapi/strapi/admin");
8
8
  const icons = require("@strapi/icons");
9
9
  const styled = require("styled-components");
10
- const index = require("./index-BRESWp1b.js");
11
- const useLicense = require("./useLicense-D_wQcoMn.js");
10
+ const index = require("./index-CTxGMDHr.js");
11
+ const useLicense = require("./useLicense-BEbtA_Zo.js");
12
12
  const StyledButtons = require("./StyledButtons-DDuxnYz8.js");
13
13
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
14
14
  const styled__default = /* @__PURE__ */ _interopDefault(styled);
15
+ const COUNTRIES = [
16
+ { code: "DE", name: "Germany", flag: "🇩🇪" },
17
+ { code: "AT", name: "Austria", flag: "🇦🇹" },
18
+ { code: "CH", name: "Switzerland", flag: "🇨🇭" },
19
+ { code: "US", name: "United States", flag: "🇺🇸" },
20
+ { code: "CA", name: "Canada", flag: "🇨🇦" },
21
+ { code: "MX", name: "Mexico", flag: "🇲🇽" },
22
+ { code: "GB", name: "United Kingdom", flag: "🇬🇧" },
23
+ { code: "IE", name: "Ireland", flag: "🇮🇪" },
24
+ { code: "FR", name: "France", flag: "🇫🇷" },
25
+ { code: "IT", name: "Italy", flag: "🇮🇹" },
26
+ { code: "ES", name: "Spain", flag: "🇪🇸" },
27
+ { code: "PT", name: "Portugal", flag: "🇵🇹" },
28
+ { code: "NL", name: "Netherlands", flag: "🇳🇱" },
29
+ { code: "BE", name: "Belgium", flag: "🇧🇪" },
30
+ { code: "LU", name: "Luxembourg", flag: "🇱🇺" },
31
+ { code: "SE", name: "Sweden", flag: "🇸🇪" },
32
+ { code: "NO", name: "Norway", flag: "🇳🇴" },
33
+ { code: "DK", name: "Denmark", flag: "🇩🇰" },
34
+ { code: "FI", name: "Finland", flag: "🇫🇮" },
35
+ { code: "PL", name: "Poland", flag: "🇵🇱" },
36
+ { code: "CZ", name: "Czech Republic", flag: "🇨🇿" },
37
+ { code: "SK", name: "Slovakia", flag: "🇸🇰" },
38
+ { code: "HU", name: "Hungary", flag: "🇭🇺" },
39
+ { code: "RO", name: "Romania", flag: "🇷🇴" },
40
+ { code: "BG", name: "Bulgaria", flag: "🇧🇬" },
41
+ { code: "GR", name: "Greece", flag: "🇬🇷" },
42
+ { code: "HR", name: "Croatia", flag: "🇭🇷" },
43
+ { code: "SI", name: "Slovenia", flag: "🇸🇮" },
44
+ { code: "EE", name: "Estonia", flag: "🇪🇪" },
45
+ { code: "LV", name: "Latvia", flag: "🇱🇻" },
46
+ { code: "LT", name: "Lithuania", flag: "🇱🇹" },
47
+ { code: "IS", name: "Iceland", flag: "🇮🇸" },
48
+ { code: "TR", name: "Turkey", flag: "🇹🇷" },
49
+ { code: "RU", name: "Russia", flag: "🇷🇺" },
50
+ { code: "UA", name: "Ukraine", flag: "🇺🇦" },
51
+ { code: "BR", name: "Brazil", flag: "🇧🇷" },
52
+ { code: "AR", name: "Argentina", flag: "🇦🇷" },
53
+ { code: "CL", name: "Chile", flag: "🇨🇱" },
54
+ { code: "CO", name: "Colombia", flag: "🇨🇴" },
55
+ { code: "AU", name: "Australia", flag: "🇦🇺" },
56
+ { code: "NZ", name: "New Zealand", flag: "🇳🇿" },
57
+ { code: "JP", name: "Japan", flag: "🇯🇵" },
58
+ { code: "KR", name: "South Korea", flag: "🇰🇷" },
59
+ { code: "CN", name: "China", flag: "🇨🇳" },
60
+ { code: "HK", name: "Hong Kong", flag: "🇭🇰" },
61
+ { code: "TW", name: "Taiwan", flag: "🇹🇼" },
62
+ { code: "SG", name: "Singapore", flag: "🇸🇬" },
63
+ { code: "IN", name: "India", flag: "🇮🇳" },
64
+ { code: "ID", name: "Indonesia", flag: "🇮🇩" },
65
+ { code: "TH", name: "Thailand", flag: "🇹🇭" },
66
+ { code: "VN", name: "Vietnam", flag: "🇻🇳" },
67
+ { code: "PH", name: "Philippines", flag: "🇵🇭" },
68
+ { code: "MY", name: "Malaysia", flag: "🇲🇾" },
69
+ { code: "AE", name: "United Arab Emirates", flag: "🇦🇪" },
70
+ { code: "SA", name: "Saudi Arabia", flag: "🇸🇦" },
71
+ { code: "IL", name: "Israel", flag: "🇮🇱" },
72
+ { code: "EG", name: "Egypt", flag: "🇪🇬" },
73
+ { code: "ZA", name: "South Africa", flag: "🇿🇦" },
74
+ { code: "NG", name: "Nigeria", flag: "🇳🇬" },
75
+ { code: "KE", name: "Kenya", flag: "🇰🇪" },
76
+ { code: "MA", name: "Morocco", flag: "🇲🇦" }
77
+ ];
78
+ const normalizeCountryCode = (raw) => {
79
+ if (typeof raw !== "string") return null;
80
+ const up = raw.trim().toUpperCase();
81
+ if (!/^[A-Z]{2}$/.test(up)) return null;
82
+ return up;
83
+ };
84
+ const formatCountry = (code) => {
85
+ if (!code) return "";
86
+ const match = COUNTRIES.find((c) => c.code === code);
87
+ return match ? `${match.flag} ${match.name} (${match.code})` : code;
88
+ };
15
89
  const theme = {
16
90
  borderRadius: { md: "8px", lg: "12px" }
17
91
  };
@@ -331,6 +405,167 @@ const generateSecureKey = () => {
331
405
  }
332
406
  return key;
333
407
  };
408
+ const GeofencingPanel = ({ settings, handleChange, t }) => {
409
+ const [pendingCode, setPendingCode] = react.useState("");
410
+ const mode = (() => {
411
+ if (Array.isArray(settings.allowedCountries) && settings.allowedCountries.length > 0) {
412
+ return "allowlist";
413
+ }
414
+ if (Array.isArray(settings.blockedCountries) && settings.blockedCountries.length > 0) {
415
+ return "blocklist";
416
+ }
417
+ return "allowlist";
418
+ })();
419
+ const activeField = mode === "allowlist" ? "allowedCountries" : "blockedCountries";
420
+ const activeList = Array.isArray(settings[activeField]) ? settings[activeField] : [];
421
+ const setMode = (newMode) => {
422
+ if (newMode === mode) return;
423
+ if (newMode === "allowlist") {
424
+ handleChange("blockedCountries", []);
425
+ } else {
426
+ handleChange("allowedCountries", []);
427
+ }
428
+ };
429
+ const addCode = (raw) => {
430
+ const code = normalizeCountryCode(raw);
431
+ if (!code) return;
432
+ if (activeList.includes(code)) return;
433
+ handleChange(activeField, [...activeList, code]);
434
+ setPendingCode("");
435
+ };
436
+ const removeCode = (code) => {
437
+ handleChange(activeField, activeList.filter((c) => c !== code));
438
+ };
439
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Grid.Root, { gap: 5, children: [
440
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 12, children: /* @__PURE__ */ jsxRuntime.jsx(
441
+ designSystem.Box,
442
+ {
443
+ padding: 3,
444
+ background: settings.enableGeofencing ? "success100" : "neutral100",
445
+ hasRadius: true,
446
+ style: { borderLeft: `4px solid ${settings.enableGeofencing ? "#16A34A" : "#9CA3AF"}` },
447
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
448
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
449
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", style: { display: "block", marginBottom: "4px" }, children: t("settings.geofencing.enable.title", "Enable Geofencing") }),
450
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "12px" }, children: t(
451
+ "settings.geofencing.enable.description",
452
+ "When enabled, the pre-login guard compares the caller's country against the configured list and rejects logins that do not match."
453
+ ) })
454
+ ] }),
455
+ /* @__PURE__ */ jsxRuntime.jsx(
456
+ designSystem.Toggle,
457
+ {
458
+ onLabel: "On",
459
+ offLabel: "Off",
460
+ checked: !!settings.enableGeofencing,
461
+ onChange: () => handleChange("enableGeofencing", !settings.enableGeofencing)
462
+ }
463
+ )
464
+ ] })
465
+ }
466
+ ) }),
467
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 12, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
468
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: t("settings.geofencing.mode.title", "Mode") }),
469
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
470
+ /* @__PURE__ */ jsxRuntime.jsx(
471
+ designSystem.Button,
472
+ {
473
+ variant: mode === "allowlist" ? "default" : "tertiary",
474
+ onClick: () => setMode("allowlist"),
475
+ disabled: !settings.enableGeofencing,
476
+ children: t("settings.geofencing.mode.allowlist", "Allowlist (only selected countries)")
477
+ }
478
+ ),
479
+ /* @__PURE__ */ jsxRuntime.jsx(
480
+ designSystem.Button,
481
+ {
482
+ variant: mode === "blocklist" ? "default" : "tertiary",
483
+ onClick: () => setMode("blocklist"),
484
+ disabled: !settings.enableGeofencing,
485
+ children: t("settings.geofencing.mode.blocklist", "Blocklist (block selected countries)")
486
+ }
487
+ )
488
+ ] }),
489
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "11px", marginTop: "8px" }, children: mode === "allowlist" ? t(
490
+ "settings.geofencing.mode.allowlist.hint",
491
+ "Only countries in the list may authenticate. Empty list = no geo restriction."
492
+ ) : t(
493
+ "settings.geofencing.mode.blocklist.hint",
494
+ "Every country EXCEPT those in the list may authenticate."
495
+ ) })
496
+ ] }) }),
497
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 12, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
498
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: mode === "allowlist" ? t("settings.geofencing.allowed.title", "Allowed Countries") : t("settings.geofencing.blocked.title", "Blocked Countries") }),
499
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, wrap: "wrap", style: { marginBottom: "12px", minHeight: "36px" }, children: activeList.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: t("settings.geofencing.empty", "No countries configured.") }) : activeList.map((code) => /* @__PURE__ */ jsxRuntime.jsxs(
500
+ designSystem.Badge,
501
+ {
502
+ onClick: settings.enableGeofencing ? () => removeCode(code) : void 0,
503
+ style: {
504
+ cursor: settings.enableGeofencing ? "pointer" : "not-allowed",
505
+ padding: "6px 10px",
506
+ background: mode === "allowlist" ? "#DCFCE7" : "#FEE2E2",
507
+ color: mode === "allowlist" ? "#15803D" : "#B91C1C"
508
+ },
509
+ children: [
510
+ formatCountry(code),
511
+ settings.enableGeofencing ? " ✕" : ""
512
+ ]
513
+ },
514
+ code
515
+ )) }),
516
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
517
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1 }, children: /* @__PURE__ */ jsxRuntime.jsx(
518
+ designSystem.SingleSelect,
519
+ {
520
+ disabled: !settings.enableGeofencing,
521
+ value: pendingCode || "",
522
+ onChange: (value) => {
523
+ setPendingCode(value);
524
+ if (value) addCode(value);
525
+ },
526
+ placeholder: t("settings.geofencing.pick", "Pick a country to add…"),
527
+ children: COUNTRIES.filter((c) => !activeList.includes(c.code)).map((c) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.SingleSelectOption, { value: c.code, children: [
528
+ c.flag,
529
+ " ",
530
+ c.name,
531
+ " (",
532
+ c.code,
533
+ ")"
534
+ ] }, c.code))
535
+ }
536
+ ) }),
537
+ /* @__PURE__ */ jsxRuntime.jsx(
538
+ designSystem.TextInput,
539
+ {
540
+ disabled: !settings.enableGeofencing,
541
+ placeholder: t("settings.geofencing.manual", "Or type ISO-2 (e.g. JP)"),
542
+ value: pendingCode.length <= 2 ? pendingCode : "",
543
+ onChange: (e) => setPendingCode(e.target.value.toUpperCase().slice(0, 2)),
544
+ style: { width: "180px" }
545
+ }
546
+ ),
547
+ /* @__PURE__ */ jsxRuntime.jsx(
548
+ designSystem.Button,
549
+ {
550
+ disabled: !settings.enableGeofencing || !normalizeCountryCode(pendingCode),
551
+ onClick: () => addCode(pendingCode),
552
+ children: t("settings.geofencing.add", "Add")
553
+ }
554
+ )
555
+ ] })
556
+ ] }) }),
557
+ !settings.enableGeofencing && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 12, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Alert, { variant: "default", closeLabel: "", title: "", onClose: () => {
558
+ }, children: t(
559
+ "settings.geofencing.disabled",
560
+ "Geofencing is currently disabled. Enable it above to enforce the configured country list."
561
+ ) }) }),
562
+ settings.enableGeofencing && !settings.enableGeolocation && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 12, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Alert, { variant: "warning", closeLabel: "", title: "", onClose: () => {
563
+ }, children: t(
564
+ "settings.geofencing.needsGeolocation",
565
+ "Geofencing requires Geolocation to be enabled (Security tab)."
566
+ ) }) })
567
+ ] });
568
+ };
334
569
  const SettingsPage = () => {
335
570
  const { formatMessage } = reactIntl.useIntl();
336
571
  const { get, post, put } = admin.useFetchClient();
@@ -1059,6 +1294,24 @@ const SettingsPage = () => {
1059
1294
  ] }) }) })
1060
1295
  ] }) })
1061
1296
  ] }),
1297
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Accordion.Item, { value: "geofencing", children: [
1298
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Accordion.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(
1299
+ designSystem.Accordion.Trigger,
1300
+ {
1301
+ icon: icons.Shield,
1302
+ description: t("settings.geofencing.description", "Allow or block sign-ins by country"),
1303
+ children: t("settings.geofencing.title", "Geofencing")
1304
+ }
1305
+ ) }),
1306
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Accordion.Content, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 6, children: /* @__PURE__ */ jsxRuntime.jsx(
1307
+ GeofencingPanel,
1308
+ {
1309
+ settings,
1310
+ handleChange,
1311
+ t
1312
+ }
1313
+ ) }) })
1314
+ ] }),
1062
1315
  isAdvanced && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Accordion.Item, { value: "email", children: [
1063
1316
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Accordion.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(
1064
1317
  designSystem.Accordion.Trigger,
@@ -5,9 +5,83 @@ import { Flex, Loader, Typography, Button, Box, Badge, Accordion, Grid, SingleSe
5
5
  import { useFetchClient, useNotification } from "@strapi/strapi/admin";
6
6
  import { Check, Information, Cog, Trash, Shield, Code, Duplicate, Clock, Mail } from "@strapi/icons";
7
7
  import styled, { css, keyframes } from "styled-components";
8
- import { a as pluginId, g as getTranslation } from "./index-BbbrBv3t.mjs";
9
- import { u as useLicense } from "./useLicense-Bszkymz3.mjs";
8
+ import { a as pluginId, g as getTranslation } from "./index-CwxKazpc.mjs";
9
+ import { u as useLicense } from "./useLicense-B9WW9s_d.mjs";
10
10
  import { D as DangerButton, S as ShowHideButton, G as GradientButton, C as CopyButton, T as TertiaryButton, c as SecondaryButton } from "./StyledButtons-Cz8oYhmc.mjs";
11
+ const COUNTRIES = [
12
+ { code: "DE", name: "Germany", flag: "🇩🇪" },
13
+ { code: "AT", name: "Austria", flag: "🇦🇹" },
14
+ { code: "CH", name: "Switzerland", flag: "🇨🇭" },
15
+ { code: "US", name: "United States", flag: "🇺🇸" },
16
+ { code: "CA", name: "Canada", flag: "🇨🇦" },
17
+ { code: "MX", name: "Mexico", flag: "🇲🇽" },
18
+ { code: "GB", name: "United Kingdom", flag: "🇬🇧" },
19
+ { code: "IE", name: "Ireland", flag: "🇮🇪" },
20
+ { code: "FR", name: "France", flag: "🇫🇷" },
21
+ { code: "IT", name: "Italy", flag: "🇮🇹" },
22
+ { code: "ES", name: "Spain", flag: "🇪🇸" },
23
+ { code: "PT", name: "Portugal", flag: "🇵🇹" },
24
+ { code: "NL", name: "Netherlands", flag: "🇳🇱" },
25
+ { code: "BE", name: "Belgium", flag: "🇧🇪" },
26
+ { code: "LU", name: "Luxembourg", flag: "🇱🇺" },
27
+ { code: "SE", name: "Sweden", flag: "🇸🇪" },
28
+ { code: "NO", name: "Norway", flag: "🇳🇴" },
29
+ { code: "DK", name: "Denmark", flag: "🇩🇰" },
30
+ { code: "FI", name: "Finland", flag: "🇫🇮" },
31
+ { code: "PL", name: "Poland", flag: "🇵🇱" },
32
+ { code: "CZ", name: "Czech Republic", flag: "🇨🇿" },
33
+ { code: "SK", name: "Slovakia", flag: "🇸🇰" },
34
+ { code: "HU", name: "Hungary", flag: "🇭🇺" },
35
+ { code: "RO", name: "Romania", flag: "🇷🇴" },
36
+ { code: "BG", name: "Bulgaria", flag: "🇧🇬" },
37
+ { code: "GR", name: "Greece", flag: "🇬🇷" },
38
+ { code: "HR", name: "Croatia", flag: "🇭🇷" },
39
+ { code: "SI", name: "Slovenia", flag: "🇸🇮" },
40
+ { code: "EE", name: "Estonia", flag: "🇪🇪" },
41
+ { code: "LV", name: "Latvia", flag: "🇱🇻" },
42
+ { code: "LT", name: "Lithuania", flag: "🇱🇹" },
43
+ { code: "IS", name: "Iceland", flag: "🇮🇸" },
44
+ { code: "TR", name: "Turkey", flag: "🇹🇷" },
45
+ { code: "RU", name: "Russia", flag: "🇷🇺" },
46
+ { code: "UA", name: "Ukraine", flag: "🇺🇦" },
47
+ { code: "BR", name: "Brazil", flag: "🇧🇷" },
48
+ { code: "AR", name: "Argentina", flag: "🇦🇷" },
49
+ { code: "CL", name: "Chile", flag: "🇨🇱" },
50
+ { code: "CO", name: "Colombia", flag: "🇨🇴" },
51
+ { code: "AU", name: "Australia", flag: "🇦🇺" },
52
+ { code: "NZ", name: "New Zealand", flag: "🇳🇿" },
53
+ { code: "JP", name: "Japan", flag: "🇯🇵" },
54
+ { code: "KR", name: "South Korea", flag: "🇰🇷" },
55
+ { code: "CN", name: "China", flag: "🇨🇳" },
56
+ { code: "HK", name: "Hong Kong", flag: "🇭🇰" },
57
+ { code: "TW", name: "Taiwan", flag: "🇹🇼" },
58
+ { code: "SG", name: "Singapore", flag: "🇸🇬" },
59
+ { code: "IN", name: "India", flag: "🇮🇳" },
60
+ { code: "ID", name: "Indonesia", flag: "🇮🇩" },
61
+ { code: "TH", name: "Thailand", flag: "🇹🇭" },
62
+ { code: "VN", name: "Vietnam", flag: "🇻🇳" },
63
+ { code: "PH", name: "Philippines", flag: "🇵🇭" },
64
+ { code: "MY", name: "Malaysia", flag: "🇲🇾" },
65
+ { code: "AE", name: "United Arab Emirates", flag: "🇦🇪" },
66
+ { code: "SA", name: "Saudi Arabia", flag: "🇸🇦" },
67
+ { code: "IL", name: "Israel", flag: "🇮🇱" },
68
+ { code: "EG", name: "Egypt", flag: "🇪🇬" },
69
+ { code: "ZA", name: "South Africa", flag: "🇿🇦" },
70
+ { code: "NG", name: "Nigeria", flag: "🇳🇬" },
71
+ { code: "KE", name: "Kenya", flag: "🇰🇪" },
72
+ { code: "MA", name: "Morocco", flag: "🇲🇦" }
73
+ ];
74
+ const normalizeCountryCode = (raw) => {
75
+ if (typeof raw !== "string") return null;
76
+ const up = raw.trim().toUpperCase();
77
+ if (!/^[A-Z]{2}$/.test(up)) return null;
78
+ return up;
79
+ };
80
+ const formatCountry = (code) => {
81
+ if (!code) return "";
82
+ const match = COUNTRIES.find((c) => c.code === code);
83
+ return match ? `${match.flag} ${match.name} (${match.code})` : code;
84
+ };
11
85
  const theme = {
12
86
  borderRadius: { md: "8px", lg: "12px" }
13
87
  };
@@ -327,6 +401,167 @@ const generateSecureKey = () => {
327
401
  }
328
402
  return key;
329
403
  };
404
+ const GeofencingPanel = ({ settings, handleChange, t }) => {
405
+ const [pendingCode, setPendingCode] = useState("");
406
+ const mode = (() => {
407
+ if (Array.isArray(settings.allowedCountries) && settings.allowedCountries.length > 0) {
408
+ return "allowlist";
409
+ }
410
+ if (Array.isArray(settings.blockedCountries) && settings.blockedCountries.length > 0) {
411
+ return "blocklist";
412
+ }
413
+ return "allowlist";
414
+ })();
415
+ const activeField = mode === "allowlist" ? "allowedCountries" : "blockedCountries";
416
+ const activeList = Array.isArray(settings[activeField]) ? settings[activeField] : [];
417
+ const setMode = (newMode) => {
418
+ if (newMode === mode) return;
419
+ if (newMode === "allowlist") {
420
+ handleChange("blockedCountries", []);
421
+ } else {
422
+ handleChange("allowedCountries", []);
423
+ }
424
+ };
425
+ const addCode = (raw) => {
426
+ const code = normalizeCountryCode(raw);
427
+ if (!code) return;
428
+ if (activeList.includes(code)) return;
429
+ handleChange(activeField, [...activeList, code]);
430
+ setPendingCode("");
431
+ };
432
+ const removeCode = (code) => {
433
+ handleChange(activeField, activeList.filter((c) => c !== code));
434
+ };
435
+ return /* @__PURE__ */ jsxs(Grid.Root, { gap: 5, children: [
436
+ /* @__PURE__ */ jsx(Grid.Item, { col: 12, children: /* @__PURE__ */ jsx(
437
+ Box,
438
+ {
439
+ padding: 3,
440
+ background: settings.enableGeofencing ? "success100" : "neutral100",
441
+ hasRadius: true,
442
+ style: { borderLeft: `4px solid ${settings.enableGeofencing ? "#16A34A" : "#9CA3AF"}` },
443
+ children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
444
+ /* @__PURE__ */ jsxs(Box, { children: [
445
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", style: { display: "block", marginBottom: "4px" }, children: t("settings.geofencing.enable.title", "Enable Geofencing") }),
446
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "12px" }, children: t(
447
+ "settings.geofencing.enable.description",
448
+ "When enabled, the pre-login guard compares the caller's country against the configured list and rejects logins that do not match."
449
+ ) })
450
+ ] }),
451
+ /* @__PURE__ */ jsx(
452
+ Toggle,
453
+ {
454
+ onLabel: "On",
455
+ offLabel: "Off",
456
+ checked: !!settings.enableGeofencing,
457
+ onChange: () => handleChange("enableGeofencing", !settings.enableGeofencing)
458
+ }
459
+ )
460
+ ] })
461
+ }
462
+ ) }),
463
+ /* @__PURE__ */ jsx(Grid.Item, { col: 12, children: /* @__PURE__ */ jsxs(Box, { children: [
464
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: t("settings.geofencing.mode.title", "Mode") }),
465
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
466
+ /* @__PURE__ */ jsx(
467
+ Button,
468
+ {
469
+ variant: mode === "allowlist" ? "default" : "tertiary",
470
+ onClick: () => setMode("allowlist"),
471
+ disabled: !settings.enableGeofencing,
472
+ children: t("settings.geofencing.mode.allowlist", "Allowlist (only selected countries)")
473
+ }
474
+ ),
475
+ /* @__PURE__ */ jsx(
476
+ Button,
477
+ {
478
+ variant: mode === "blocklist" ? "default" : "tertiary",
479
+ onClick: () => setMode("blocklist"),
480
+ disabled: !settings.enableGeofencing,
481
+ children: t("settings.geofencing.mode.blocklist", "Blocklist (block selected countries)")
482
+ }
483
+ )
484
+ ] }),
485
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", style: { fontSize: "11px", marginTop: "8px" }, children: mode === "allowlist" ? t(
486
+ "settings.geofencing.mode.allowlist.hint",
487
+ "Only countries in the list may authenticate. Empty list = no geo restriction."
488
+ ) : t(
489
+ "settings.geofencing.mode.blocklist.hint",
490
+ "Every country EXCEPT those in the list may authenticate."
491
+ ) })
492
+ ] }) }),
493
+ /* @__PURE__ */ jsx(Grid.Item, { col: 12, children: /* @__PURE__ */ jsxs(Box, { children: [
494
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", style: { marginBottom: "8px", display: "block" }, children: mode === "allowlist" ? t("settings.geofencing.allowed.title", "Allowed Countries") : t("settings.geofencing.blocked.title", "Blocked Countries") }),
495
+ /* @__PURE__ */ jsx(Flex, { gap: 2, wrap: "wrap", style: { marginBottom: "12px", minHeight: "36px" }, children: activeList.length === 0 ? /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral500", children: t("settings.geofencing.empty", "No countries configured.") }) : activeList.map((code) => /* @__PURE__ */ jsxs(
496
+ Badge,
497
+ {
498
+ onClick: settings.enableGeofencing ? () => removeCode(code) : void 0,
499
+ style: {
500
+ cursor: settings.enableGeofencing ? "pointer" : "not-allowed",
501
+ padding: "6px 10px",
502
+ background: mode === "allowlist" ? "#DCFCE7" : "#FEE2E2",
503
+ color: mode === "allowlist" ? "#15803D" : "#B91C1C"
504
+ },
505
+ children: [
506
+ formatCountry(code),
507
+ settings.enableGeofencing ? " ✕" : ""
508
+ ]
509
+ },
510
+ code
511
+ )) }),
512
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
513
+ /* @__PURE__ */ jsx(Box, { style: { flex: 1 }, children: /* @__PURE__ */ jsx(
514
+ SingleSelect,
515
+ {
516
+ disabled: !settings.enableGeofencing,
517
+ value: pendingCode || "",
518
+ onChange: (value) => {
519
+ setPendingCode(value);
520
+ if (value) addCode(value);
521
+ },
522
+ placeholder: t("settings.geofencing.pick", "Pick a country to add…"),
523
+ children: COUNTRIES.filter((c) => !activeList.includes(c.code)).map((c) => /* @__PURE__ */ jsxs(SingleSelectOption, { value: c.code, children: [
524
+ c.flag,
525
+ " ",
526
+ c.name,
527
+ " (",
528
+ c.code,
529
+ ")"
530
+ ] }, c.code))
531
+ }
532
+ ) }),
533
+ /* @__PURE__ */ jsx(
534
+ TextInput,
535
+ {
536
+ disabled: !settings.enableGeofencing,
537
+ placeholder: t("settings.geofencing.manual", "Or type ISO-2 (e.g. JP)"),
538
+ value: pendingCode.length <= 2 ? pendingCode : "",
539
+ onChange: (e) => setPendingCode(e.target.value.toUpperCase().slice(0, 2)),
540
+ style: { width: "180px" }
541
+ }
542
+ ),
543
+ /* @__PURE__ */ jsx(
544
+ Button,
545
+ {
546
+ disabled: !settings.enableGeofencing || !normalizeCountryCode(pendingCode),
547
+ onClick: () => addCode(pendingCode),
548
+ children: t("settings.geofencing.add", "Add")
549
+ }
550
+ )
551
+ ] })
552
+ ] }) }),
553
+ !settings.enableGeofencing && /* @__PURE__ */ jsx(Grid.Item, { col: 12, children: /* @__PURE__ */ jsx(Alert, { variant: "default", closeLabel: "", title: "", onClose: () => {
554
+ }, children: t(
555
+ "settings.geofencing.disabled",
556
+ "Geofencing is currently disabled. Enable it above to enforce the configured country list."
557
+ ) }) }),
558
+ settings.enableGeofencing && !settings.enableGeolocation && /* @__PURE__ */ jsx(Grid.Item, { col: 12, children: /* @__PURE__ */ jsx(Alert, { variant: "warning", closeLabel: "", title: "", onClose: () => {
559
+ }, children: t(
560
+ "settings.geofencing.needsGeolocation",
561
+ "Geofencing requires Geolocation to be enabled (Security tab)."
562
+ ) }) })
563
+ ] });
564
+ };
330
565
  const SettingsPage = () => {
331
566
  const { formatMessage } = useIntl();
332
567
  const { get, post, put } = useFetchClient();
@@ -1055,6 +1290,24 @@ const SettingsPage = () => {
1055
1290
  ] }) }) })
1056
1291
  ] }) })
1057
1292
  ] }),
1293
+ /* @__PURE__ */ jsxs(Accordion.Item, { value: "geofencing", children: [
1294
+ /* @__PURE__ */ jsx(Accordion.Header, { children: /* @__PURE__ */ jsx(
1295
+ Accordion.Trigger,
1296
+ {
1297
+ icon: Shield,
1298
+ description: t("settings.geofencing.description", "Allow or block sign-ins by country"),
1299
+ children: t("settings.geofencing.title", "Geofencing")
1300
+ }
1301
+ ) }),
1302
+ /* @__PURE__ */ jsx(Accordion.Content, { children: /* @__PURE__ */ jsx(Box, { padding: 6, children: /* @__PURE__ */ jsx(
1303
+ GeofencingPanel,
1304
+ {
1305
+ settings,
1306
+ handleChange,
1307
+ t
1308
+ }
1309
+ ) }) })
1310
+ ] }),
1058
1311
  isAdvanced && /* @__PURE__ */ jsxs(Accordion.Item, { value: "email", children: [
1059
1312
  /* @__PURE__ */ jsx(Accordion.Header, { children: /* @__PURE__ */ jsx(
1060
1313
  Accordion.Trigger,