strapi-plugin-oidc 1.7.6 → 1.8.1

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.
@@ -3,11 +3,11 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const jsxRuntime = require("react/jsx-runtime");
4
4
  const reactRouterDom = require("react-router-dom");
5
5
  const admin = require("@strapi/strapi/admin");
6
- const react = require("react");
6
+ const React = require("react");
7
7
  const designSystem = require("@strapi/design-system");
8
8
  const icons = require("@strapi/icons");
9
9
  const reactIntl = require("react-intl");
10
- const index = require("./index-CrnGXADu.js");
10
+ const index = require("./index-Dk6TYtio.js");
11
11
  const styled = require("styled-components");
12
12
  const lucideReact = require("lucide-react");
13
13
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
@@ -125,8 +125,8 @@ function TagChip({ label, onRemove }) {
125
125
  ] });
126
126
  }
127
127
  function useTagState({ value, onChange }) {
128
- const [inputValue, setInputValue] = react.useState("");
129
- const inputRef = react.useRef(null);
128
+ const [inputValue, setInputValue] = React.useState("");
129
+ const inputRef = React.useRef(null);
130
130
  const addTag = (tag, predicate) => {
131
131
  const trimmed = tag.trim();
132
132
  if (trimmed && !value.includes(trimmed) && (!predicate || predicate(trimmed))) {
@@ -261,11 +261,11 @@ function ComboboxTagInput({ value, onChange, placeholder, startIcon, options })
261
261
  value,
262
262
  onChange
263
263
  });
264
- const [showDropdown, setShowDropdown] = react.useState(false);
265
- const [activeIndex, setActiveIndex] = react.useState(-1);
266
- const wrapperRef = react.useRef(null);
267
- const listboxId = react.useId();
268
- const optionIdPrefix = react.useId();
264
+ const [showDropdown, setShowDropdown] = React.useState(false);
265
+ const [activeIndex, setActiveIndex] = React.useState(-1);
266
+ const wrapperRef = React.useRef(null);
267
+ const listboxId = React.useId();
268
+ const optionIdPrefix = React.useId();
269
269
  const filteredOptions = options.filter(
270
270
  (opt) => !value.includes(opt) && opt.toLowerCase().includes(inputValue.toLowerCase())
271
271
  );
@@ -333,7 +333,7 @@ function ComboboxTagInput({ value, onChange, placeholder, startIcon, options })
333
333
  setShowDropdown(true);
334
334
  setActiveIndex(-1);
335
335
  };
336
- react.useEffect(() => {
336
+ React.useEffect(() => {
337
337
  const handleClickOutside = (e) => {
338
338
  if (wrapperRef.current && !wrapperRef.current.contains(e.target)) {
339
339
  setShowDropdown(false);
@@ -546,13 +546,13 @@ const DAY_NAMES = Array.from(
546
546
  );
547
547
  function TagDateInput({ value = [], onChange, placeholder, startIcon }) {
548
548
  const { formatMessage } = reactIntl.useIntl();
549
- const [isOpen, setIsOpen] = react.useState(false);
550
- const [viewDate, setViewDate] = react.useState(() => /* @__PURE__ */ new Date());
551
- const [pendingDates, setPendingDates] = react.useState([]);
549
+ const [isOpen, setIsOpen] = React.useState(false);
550
+ const [viewDate, setViewDate] = React.useState(() => /* @__PURE__ */ new Date());
551
+ const [pendingDates, setPendingDates] = React.useState([]);
552
552
  const today = /* @__PURE__ */ new Date();
553
- const wrapperRef = react.useRef(null);
554
- const dialogId = react.useId();
555
- const dialogLabelId = react.useId();
553
+ const wrapperRef = React.useRef(null);
554
+ const dialogId = React.useId();
555
+ const dialogLabelId = React.useId();
556
556
  const year = viewDate.getFullYear();
557
557
  const month = viewDate.getMonth();
558
558
  const daysInMonth = getDaysInMonth(year, month);
@@ -594,7 +594,7 @@ function TagDateInput({ value = [], onChange, placeholder, startIcon }) {
594
594
  const removeTag = (indexToRemove) => {
595
595
  onChange(value.filter((_, i) => i !== indexToRemove));
596
596
  };
597
- react.useEffect(() => {
597
+ React.useEffect(() => {
598
598
  const handleClickOutside = (e) => {
599
599
  if (wrapperRef.current && !wrapperRef.current.contains(e.target)) {
600
600
  setIsOpen(false);
@@ -863,14 +863,14 @@ function Whitelist({
863
863
  onImport,
864
864
  onExport
865
865
  }) {
866
- const [email, setEmail] = react.useState("");
867
- const [page, setPage] = react.useState(1);
866
+ const [email, setEmail] = React.useState("");
867
+ const [page, setPage] = React.useState(1);
868
868
  const { formatMessage } = reactIntl.useIntl();
869
869
  const { toggleNotification } = admin.useNotification();
870
- const fileInputRef = react.useRef(null);
870
+ const fileInputRef = React.useRef(null);
871
871
  const pageCount = Math.ceil(users.length / PAGE_SIZE$1) || 1;
872
872
  const paginatedUsers = users.slice((page - 1) * PAGE_SIZE$1, page * PAGE_SIZE$1);
873
- const onSaveEmail = react.useCallback(() => {
873
+ const onSaveEmail = React.useCallback(() => {
874
874
  const emailText = email.trim();
875
875
  if (users.some((user) => user.email === emailText)) {
876
876
  toggleNotification({
@@ -882,7 +882,7 @@ function Whitelist({
882
882
  setEmail("");
883
883
  }
884
884
  }, [email, users, onSave, formatMessage, toggleNotification]);
885
- const handleImport = react.useCallback(
885
+ const handleImport = React.useCallback(
886
886
  async (e) => {
887
887
  const file = e.target.files?.[0];
888
888
  if (!fileInputRef.current || !file) return;
@@ -1018,7 +1018,7 @@ function Whitelist({
1018
1018
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("whitelist.table.created")) }),
1019
1019
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { style: { paddingRight: 0 }, children: " " })
1020
1020
  ] }) }),
1021
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: users.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tr, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { colSpan: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", padding: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: formatMessage(index.getTrad("whitelist.table.empty")) }) }) }) }) : paginatedUsers.map((user, index$1) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
1021
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: users.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tr, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { colSpan: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", alignItems: "center", style: { minHeight: "80px" }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: formatMessage(index.getTrad("whitelist.table.empty")) }) }) }) }) : paginatedUsers.map((user, index$1) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
1022
1022
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: index$1 + 1 + (page - 1) * PAGE_SIZE$1 }),
1023
1023
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: user.email }),
1024
1024
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(LocalizedDate, { date: user.createdAt, options: { second: "2-digit" } }) }),
@@ -3615,11 +3615,14 @@ const AUDIT_ACTIONS = [
3615
3615
  "nonce_mismatch",
3616
3616
  "token_exchange_failed",
3617
3617
  "whitelist_rejected",
3618
+ "email_not_verified",
3619
+ "id_token_invalid",
3618
3620
  "logout",
3619
3621
  "session_expired",
3620
3622
  "user_created"
3621
3623
  ];
3622
3624
  const PAGE_SIZE = 10;
3625
+ const MIN_SPINNER_MS = 400;
3623
3626
  const DETAILS_TEXT_STYLE = {
3624
3627
  display: "block",
3625
3628
  overflow: "hidden",
@@ -3650,8 +3653,8 @@ function buildQueryString(params) {
3650
3653
  );
3651
3654
  }
3652
3655
  function useDebounced(value, delay = 300) {
3653
- const [debounced, setDebounced] = react.useState(value);
3654
- react.useEffect(() => {
3656
+ const [debounced, setDebounced] = React.useState(value);
3657
+ React.useEffect(() => {
3655
3658
  const id = setTimeout(() => setDebounced(value), delay);
3656
3659
  return () => clearTimeout(id);
3657
3660
  }, [value, delay]);
@@ -3661,40 +3664,47 @@ function AuditLog({ title } = {}) {
3661
3664
  const { formatMessage } = reactIntl.useIntl();
3662
3665
  const { get: get2, del } = admin.useFetchClient();
3663
3666
  const { toggleNotification } = admin.useNotification();
3664
- const [records, setRecords] = react.useState([]);
3665
- const [pagination, setPagination] = react.useState({
3667
+ const [records, setRecords] = React.useState([]);
3668
+ const [pagination, setPagination] = React.useState({
3666
3669
  page: 1,
3667
3670
  pageSize: PAGE_SIZE,
3668
3671
  total: 0,
3669
3672
  pageCount: 1
3670
3673
  });
3671
- const [page, setPage] = react.useState(1);
3672
- const [loading, setLoading] = react.useState(false);
3673
- const [filters, setFilters] = react.useState({});
3674
+ const [page, setPage] = React.useState(1);
3675
+ const [loading, setLoading] = React.useState(true);
3676
+ const [filters, setFilters] = React.useState({});
3677
+ const fetchGenRef = React.useRef(0);
3674
3678
  const debouncedFilters = useDebounced(filters);
3675
- const fetchLogs = react.useCallback(
3679
+ const fetchLogs = React.useCallback(
3676
3680
  async (p, f) => {
3681
+ const gen = ++fetchGenRef.current;
3677
3682
  setLoading(true);
3683
+ const startTime = Date.now();
3684
+ let newRecords = [];
3685
+ let newPagination = { page: p, pageSize: PAGE_SIZE, total: 0, pageCount: 1 };
3678
3686
  try {
3679
- const queryString = buildQueryString({
3680
- filters: f,
3681
- page: p,
3682
- pageSize: PAGE_SIZE
3683
- });
3687
+ const queryString = buildQueryString({ filters: f, page: p, pageSize: PAGE_SIZE });
3684
3688
  const response = await get2(`/strapi-plugin-oidc/audit-logs?${queryString}`);
3685
- setRecords(response.data.results ?? []);
3686
- setPagination(
3687
- response.data.pagination ?? { page: p, pageSize: PAGE_SIZE, total: 0, pageCount: 1 }
3688
- );
3689
+ newRecords = response.data.results ?? [];
3690
+ newPagination = response.data.pagination ?? {
3691
+ page: p,
3692
+ pageSize: PAGE_SIZE,
3693
+ total: 0,
3694
+ pageCount: 1
3695
+ };
3689
3696
  } catch {
3690
- setRecords([]);
3691
- } finally {
3692
- setLoading(false);
3693
3697
  }
3698
+ const remaining = MIN_SPINNER_MS - (Date.now() - startTime);
3699
+ if (remaining > 0) await new Promise((r) => setTimeout(r, remaining));
3700
+ if (gen !== fetchGenRef.current) return;
3701
+ setRecords(newRecords);
3702
+ setPagination(newPagination);
3703
+ setLoading(false);
3694
3704
  },
3695
3705
  [get2]
3696
3706
  );
3697
- react.useEffect(() => {
3707
+ React.useEffect(() => {
3698
3708
  fetchLogs(page, debouncedFilters);
3699
3709
  }, [fetchLogs, page, debouncedFilters]);
3700
3710
  const handleClearAll = async () => {
@@ -3867,37 +3877,54 @@ function AuditLog({ title } = {}) {
3867
3877
  ]
3868
3878
  }
3869
3879
  ),
3870
- /* @__PURE__ */ jsxRuntime.jsxs(CustomTable, { colCount: 5, rowCount: records.length, children: [
3871
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
3872
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.timestamp")) }),
3873
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.action")) }),
3874
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.email")) }),
3875
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.ip")) }),
3876
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.details")) })
3877
- ] }) }),
3878
- /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tbody, { children: [
3879
- loading && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tr, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { colSpan: 5, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", padding: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: formatMessage(index.getTrad("auditlog.loading")) }) }) }) }),
3880
- !loading && records.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tr, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { colSpan: 5, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", padding: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: hasActiveFilters ? formatMessage(index.getTrad("auditlog.filters.empty")) : formatMessage(index.getTrad("auditlog.table.empty")) }) }) }) }),
3881
- !loading && records.map((record) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
3882
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: /* @__PURE__ */ jsxRuntime.jsx(LocalizedDate, { date: record.createdAt, options: { second: "2-digit" } }) }) }),
3883
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
3884
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: record.action }),
3885
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { label: formatMessage(index.getTrad(`auditlog.action.${record.action}`)), children: /* @__PURE__ */ jsxRuntime.jsx(
3886
- icons.Information,
3887
- {
3888
- "aria-hidden": true,
3889
- style: { cursor: "help" },
3890
- width: "1.4rem",
3891
- height: "1.4rem",
3892
- fill: "primary600"
3893
- }
3894
- ) })
3895
- ] }) }),
3896
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: record.email ?? "—" }) }),
3897
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: record.ip ?? "—" }) }),
3898
- /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { maxWidth: "200px" }, children: record.details ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { label: record.details, side: "top", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", textColor: "neutral600", style: DETAILS_TEXT_STYLE, children: record.details }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", textColor: "neutral600", children: "—" }) })
3899
- ] }, record.id))
3900
- ] })
3880
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", width: "100%" }, children: [
3881
+ /* @__PURE__ */ jsxRuntime.jsxs(CustomTable, { colCount: 5, rowCount: records.length, children: [
3882
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
3883
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.timestamp")) }),
3884
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.action")) }),
3885
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.email")) }),
3886
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.ip")) }),
3887
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage(index.getTrad("auditlog.table.details")) })
3888
+ ] }) }),
3889
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tbody, { children: [
3890
+ records.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tr, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { colSpan: 5, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", alignItems: "center", style: { minHeight: "80px" }, children: loading ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { small: true }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: hasActiveFilters ? formatMessage(index.getTrad("auditlog.filters.empty")) : formatMessage(index.getTrad("auditlog.table.empty")) }) }) }) }),
3891
+ records.map((record) => /* @__PURE__ */ jsxRuntime.jsxs(
3892
+ designSystem.Tr,
3893
+ {
3894
+ style: { opacity: loading ? 0.4 : 1, transition: "opacity 0.15s" },
3895
+ children: [
3896
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: /* @__PURE__ */ jsxRuntime.jsx(LocalizedDate, { date: record.createdAt, options: { second: "2-digit" } }) }) }),
3897
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
3898
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: record.action }),
3899
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { label: formatMessage(index.getTrad(`auditlog.action.${record.action}`)), children: /* @__PURE__ */ jsxRuntime.jsx(
3900
+ icons.Information,
3901
+ {
3902
+ "aria-hidden": true,
3903
+ style: { cursor: "help" },
3904
+ width: "1.4rem",
3905
+ height: "1.4rem",
3906
+ fill: "primary600"
3907
+ }
3908
+ ) })
3909
+ ] }) }),
3910
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: record.email ?? "—" }) }),
3911
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", children: record.ip ?? "—" }) }),
3912
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { maxWidth: "200px" }, children: record.details ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { label: record.details, side: "top", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", textColor: "neutral600", style: DETAILS_TEXT_STYLE, children: record.details }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", textColor: "neutral600", children: "—" }) })
3913
+ ]
3914
+ },
3915
+ record.id
3916
+ ))
3917
+ ] })
3918
+ ] }),
3919
+ loading && records.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
3920
+ designSystem.Flex,
3921
+ {
3922
+ justifyContent: "center",
3923
+ alignItems: "center",
3924
+ style: { position: "absolute", inset: 0, pointerEvents: "none" },
3925
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { small: true })
3926
+ }
3927
+ )
3901
3928
  ] }),
3902
3929
  /* @__PURE__ */ jsxRuntime.jsx(
3903
3930
  TablePagination,
@@ -4171,8 +4198,8 @@ function isDirtyArray(a, b) {
4171
4198
  }
4172
4199
  function useOidcSettings() {
4173
4200
  const { get: get2, put, post } = admin.useFetchClient();
4174
- const [state, dispatch] = react.useReducer(reducer, initialState);
4175
- react.useEffect(() => {
4201
+ const [state, dispatch] = React.useReducer(reducer, initialState);
4202
+ React.useEffect(() => {
4176
4203
  get2(`/strapi-plugin-oidc/oidc-roles`).then((response) => {
4177
4204
  dispatch({ type: "hydrate/oidcRoles", oidcRoles: response.data });
4178
4205
  });
@@ -4193,19 +4220,19 @@ function useOidcSettings() {
4193
4220
  });
4194
4221
  });
4195
4222
  }, [get2]);
4196
- const onChangeRole = react.useCallback((values, oidcId) => {
4223
+ const onChangeRole = React.useCallback((values, oidcId) => {
4197
4224
  dispatch({ type: "patch/oidcRole", oidcId, values });
4198
4225
  }, []);
4199
- const onRegisterWhitelist = react.useCallback((email) => {
4226
+ const onRegisterWhitelist = React.useCallback((email) => {
4200
4227
  dispatch({ type: "user/add", email });
4201
4228
  }, []);
4202
- const onDeleteWhitelist = react.useCallback((email) => {
4229
+ const onDeleteWhitelist = React.useCallback((email) => {
4203
4230
  dispatch({ type: "user/delete", email });
4204
4231
  }, []);
4205
- const onDeleteAll = react.useCallback(() => {
4232
+ const onDeleteAll = React.useCallback(() => {
4206
4233
  dispatch({ type: "users/clear" });
4207
4234
  }, []);
4208
- const onImport = react.useCallback(
4235
+ const onImport = React.useCallback(
4209
4236
  async (emails) => {
4210
4237
  const response = await post("/strapi-plugin-oidc/whitelist/import", {
4211
4238
  users: emails.map((e) => ({ email: e }))
@@ -4217,22 +4244,22 @@ function useOidcSettings() {
4217
4244
  },
4218
4245
  [post, get2]
4219
4246
  );
4220
- const onExport = react.useCallback(async () => {
4247
+ const onExport = React.useCallback(async () => {
4221
4248
  const response = await get2("/strapi-plugin-oidc/whitelist/export");
4222
4249
  const data = response.data;
4223
4250
  downloadJson("strapi-oidc-whitelist", data);
4224
4251
  }, [get2]);
4225
- const onToggleWhitelist = react.useCallback((e) => {
4252
+ const onToggleWhitelist = React.useCallback((e) => {
4226
4253
  dispatch({ type: "toggle/useWhitelist", value: e.target.checked });
4227
4254
  }, []);
4228
- const onToggleEnforce = react.useCallback((e) => {
4255
+ const onToggleEnforce = React.useCallback((e) => {
4229
4256
  dispatch({ type: "toggle/enforceOIDC", value: e.target.checked });
4230
4257
  }, []);
4231
- const isDirty = react.useMemo(
4258
+ const isDirty = React.useMemo(
4232
4259
  () => isDirtyPrimitive(state.current.useWhitelist, state.initial.useWhitelist) || isDirtyPrimitive(state.current.enforceOIDC, state.initial.enforceOIDC) || isDirtyArray(state.current.oidcRoles, state.initial.oidcRoles) || isDirtyArray(state.current.users, state.initial.users),
4233
4260
  [state.current, state.initial]
4234
4261
  );
4235
- const onSaveAll = react.useCallback(async () => {
4262
+ const onSaveAll = React.useCallback(async () => {
4236
4263
  dispatch({ type: "loading", value: true });
4237
4264
  try {
4238
4265
  await Promise.all([
@@ -4407,7 +4434,7 @@ function HomePage$1() {
4407
4434
  ] }) })
4408
4435
  ] });
4409
4436
  }
4410
- const HomePage = react.memo(HomePage$1);
4437
+ const HomePage = React.memo(HomePage$1);
4411
4438
  function App() {
4412
4439
  return /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsxs(reactRouterDom.Routes, { children: [
4413
4440
  /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { index: true, element: /* @__PURE__ */ jsxRuntime.jsx(HomePage, {}) }),
@@ -2,10 +2,10 @@ import { jsxs, Fragment, jsx } from "react/jsx-runtime";
2
2
  import { useBlocker, Routes, Route } from "react-router-dom";
3
3
  import { useNotification, useFetchClient, Page, Layouts } from "@strapi/strapi/admin";
4
4
  import { useState, useRef, useId, useEffect, useCallback, useReducer, useMemo, memo } from "react";
5
- import { Typography, Flex, Box, MultiSelect, MultiSelectOption, Button, Dialog, Table, Pagination, PreviousLink, NextLink, PageLink, Field, Divider, Thead, Tr, Th, Tbody, Td, IconButton, Tooltip, Alert } from "@strapi/design-system";
5
+ import { Typography, Flex, Box, MultiSelect, MultiSelectOption, Button, Dialog, Table, Pagination, PreviousLink, NextLink, PageLink, Field, Divider, Thead, Tr, Th, Tbody, Td, IconButton, Loader, Tooltip, Alert } from "@strapi/design-system";
6
6
  import { Cross, WarningCircle, Plus, Download, Upload, Trash, Calendar, Mail, Information } from "@strapi/icons";
7
7
  import { useIntl } from "react-intl";
8
- import { g as getTrad } from "./index-DRJ6Ty2J.mjs";
8
+ import { g as getTrad } from "./index-Bb9-aYb4.mjs";
9
9
  import styled from "styled-components";
10
10
  import { Filter, ClipboardList, Server } from "lucide-react";
11
11
  function Role({ oidcRoles, roles, onChangeRole }) {
@@ -1014,7 +1014,7 @@ function Whitelist({
1014
1014
  /* @__PURE__ */ jsx(Th, { children: formatMessage(getTrad("whitelist.table.created")) }),
1015
1015
  /* @__PURE__ */ jsx(Th, { style: { paddingRight: 0 }, children: " " })
1016
1016
  ] }) }),
1017
- /* @__PURE__ */ jsx(Tbody, { children: users.length === 0 ? /* @__PURE__ */ jsx(Tr, { children: /* @__PURE__ */ jsx(Td, { colSpan: 4, children: /* @__PURE__ */ jsx(Flex, { justifyContent: "center", padding: 4, children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", children: formatMessage(getTrad("whitelist.table.empty")) }) }) }) }) : paginatedUsers.map((user, index) => /* @__PURE__ */ jsxs(Tr, { children: [
1017
+ /* @__PURE__ */ jsx(Tbody, { children: users.length === 0 ? /* @__PURE__ */ jsx(Tr, { children: /* @__PURE__ */ jsx(Td, { colSpan: 4, children: /* @__PURE__ */ jsx(Flex, { justifyContent: "center", alignItems: "center", style: { minHeight: "80px" }, children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", children: formatMessage(getTrad("whitelist.table.empty")) }) }) }) }) : paginatedUsers.map((user, index) => /* @__PURE__ */ jsxs(Tr, { children: [
1018
1018
  /* @__PURE__ */ jsx(Td, { children: index + 1 + (page - 1) * PAGE_SIZE$1 }),
1019
1019
  /* @__PURE__ */ jsx(Td, { children: user.email }),
1020
1020
  /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(LocalizedDate, { date: user.createdAt, options: { second: "2-digit" } }) }),
@@ -3611,11 +3611,14 @@ const AUDIT_ACTIONS = [
3611
3611
  "nonce_mismatch",
3612
3612
  "token_exchange_failed",
3613
3613
  "whitelist_rejected",
3614
+ "email_not_verified",
3615
+ "id_token_invalid",
3614
3616
  "logout",
3615
3617
  "session_expired",
3616
3618
  "user_created"
3617
3619
  ];
3618
3620
  const PAGE_SIZE = 10;
3621
+ const MIN_SPINNER_MS = 400;
3619
3622
  const DETAILS_TEXT_STYLE = {
3620
3623
  display: "block",
3621
3624
  overflow: "hidden",
@@ -3665,28 +3668,35 @@ function AuditLog({ title } = {}) {
3665
3668
  pageCount: 1
3666
3669
  });
3667
3670
  const [page, setPage] = useState(1);
3668
- const [loading, setLoading] = useState(false);
3671
+ const [loading, setLoading] = useState(true);
3669
3672
  const [filters, setFilters] = useState({});
3673
+ const fetchGenRef = useRef(0);
3670
3674
  const debouncedFilters = useDebounced(filters);
3671
3675
  const fetchLogs = useCallback(
3672
3676
  async (p, f) => {
3677
+ const gen = ++fetchGenRef.current;
3673
3678
  setLoading(true);
3679
+ const startTime = Date.now();
3680
+ let newRecords = [];
3681
+ let newPagination = { page: p, pageSize: PAGE_SIZE, total: 0, pageCount: 1 };
3674
3682
  try {
3675
- const queryString = buildQueryString({
3676
- filters: f,
3677
- page: p,
3678
- pageSize: PAGE_SIZE
3679
- });
3683
+ const queryString = buildQueryString({ filters: f, page: p, pageSize: PAGE_SIZE });
3680
3684
  const response = await get2(`/strapi-plugin-oidc/audit-logs?${queryString}`);
3681
- setRecords(response.data.results ?? []);
3682
- setPagination(
3683
- response.data.pagination ?? { page: p, pageSize: PAGE_SIZE, total: 0, pageCount: 1 }
3684
- );
3685
+ newRecords = response.data.results ?? [];
3686
+ newPagination = response.data.pagination ?? {
3687
+ page: p,
3688
+ pageSize: PAGE_SIZE,
3689
+ total: 0,
3690
+ pageCount: 1
3691
+ };
3685
3692
  } catch {
3686
- setRecords([]);
3687
- } finally {
3688
- setLoading(false);
3689
3693
  }
3694
+ const remaining = MIN_SPINNER_MS - (Date.now() - startTime);
3695
+ if (remaining > 0) await new Promise((r) => setTimeout(r, remaining));
3696
+ if (gen !== fetchGenRef.current) return;
3697
+ setRecords(newRecords);
3698
+ setPagination(newPagination);
3699
+ setLoading(false);
3690
3700
  },
3691
3701
  [get2]
3692
3702
  );
@@ -3863,37 +3873,54 @@ function AuditLog({ title } = {}) {
3863
3873
  ]
3864
3874
  }
3865
3875
  ),
3866
- /* @__PURE__ */ jsxs(CustomTable, { colCount: 5, rowCount: records.length, children: [
3867
- /* @__PURE__ */ jsx(Thead, { children: /* @__PURE__ */ jsxs(Tr, { children: [
3868
- /* @__PURE__ */ jsx(Th, { children: formatMessage(getTrad("auditlog.table.timestamp")) }),
3869
- /* @__PURE__ */ jsx(Th, { children: formatMessage(getTrad("auditlog.table.action")) }),
3870
- /* @__PURE__ */ jsx(Th, { children: formatMessage(getTrad("auditlog.table.email")) }),
3871
- /* @__PURE__ */ jsx(Th, { children: formatMessage(getTrad("auditlog.table.ip")) }),
3872
- /* @__PURE__ */ jsx(Th, { children: formatMessage(getTrad("auditlog.table.details")) })
3873
- ] }) }),
3874
- /* @__PURE__ */ jsxs(Tbody, { children: [
3875
- loading && /* @__PURE__ */ jsx(Tr, { children: /* @__PURE__ */ jsx(Td, { colSpan: 5, children: /* @__PURE__ */ jsx(Flex, { justifyContent: "center", padding: 4, children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", children: formatMessage(getTrad("auditlog.loading")) }) }) }) }),
3876
- !loading && records.length === 0 && /* @__PURE__ */ jsx(Tr, { children: /* @__PURE__ */ jsx(Td, { colSpan: 5, children: /* @__PURE__ */ jsx(Flex, { justifyContent: "center", padding: 4, children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", children: hasActiveFilters ? formatMessage(getTrad("auditlog.filters.empty")) : formatMessage(getTrad("auditlog.table.empty")) }) }) }) }),
3877
- !loading && records.map((record) => /* @__PURE__ */ jsxs(Tr, { children: [
3878
- /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { variant: "omega", children: /* @__PURE__ */ jsx(LocalizedDate, { date: record.createdAt, options: { second: "2-digit" } }) }) }),
3879
- /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
3880
- /* @__PURE__ */ jsx(Typography, { variant: "omega", children: record.action }),
3881
- /* @__PURE__ */ jsx(Tooltip, { label: formatMessage(getTrad(`auditlog.action.${record.action}`)), children: /* @__PURE__ */ jsx(
3882
- Information,
3883
- {
3884
- "aria-hidden": true,
3885
- style: { cursor: "help" },
3886
- width: "1.4rem",
3887
- height: "1.4rem",
3888
- fill: "primary600"
3889
- }
3890
- ) })
3891
- ] }) }),
3892
- /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { variant: "omega", children: record.email ?? "—" }) }),
3893
- /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { variant: "omega", children: record.ip ?? "—" }) }),
3894
- /* @__PURE__ */ jsx(Td, { style: { maxWidth: "200px" }, children: record.details ? /* @__PURE__ */ jsx(Tooltip, { label: record.details, side: "top", children: /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "neutral600", style: DETAILS_TEXT_STYLE, children: record.details }) }) : /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "neutral600", children: "—" }) })
3895
- ] }, record.id))
3896
- ] })
3876
+ /* @__PURE__ */ jsxs("div", { style: { position: "relative", width: "100%" }, children: [
3877
+ /* @__PURE__ */ jsxs(CustomTable, { colCount: 5, rowCount: records.length, children: [
3878
+ /* @__PURE__ */ jsx(Thead, { children: /* @__PURE__ */ jsxs(Tr, { children: [
3879
+ /* @__PURE__ */ jsx(Th, { children: formatMessage(getTrad("auditlog.table.timestamp")) }),
3880
+ /* @__PURE__ */ jsx(Th, { children: formatMessage(getTrad("auditlog.table.action")) }),
3881
+ /* @__PURE__ */ jsx(Th, { children: formatMessage(getTrad("auditlog.table.email")) }),
3882
+ /* @__PURE__ */ jsx(Th, { children: formatMessage(getTrad("auditlog.table.ip")) }),
3883
+ /* @__PURE__ */ jsx(Th, { children: formatMessage(getTrad("auditlog.table.details")) })
3884
+ ] }) }),
3885
+ /* @__PURE__ */ jsxs(Tbody, { children: [
3886
+ records.length === 0 && /* @__PURE__ */ jsx(Tr, { children: /* @__PURE__ */ jsx(Td, { colSpan: 5, children: /* @__PURE__ */ jsx(Flex, { justifyContent: "center", alignItems: "center", style: { minHeight: "80px" }, children: loading ? /* @__PURE__ */ jsx(Loader, { small: true }) : /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", children: hasActiveFilters ? formatMessage(getTrad("auditlog.filters.empty")) : formatMessage(getTrad("auditlog.table.empty")) }) }) }) }),
3887
+ records.map((record) => /* @__PURE__ */ jsxs(
3888
+ Tr,
3889
+ {
3890
+ style: { opacity: loading ? 0.4 : 1, transition: "opacity 0.15s" },
3891
+ children: [
3892
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { variant: "omega", children: /* @__PURE__ */ jsx(LocalizedDate, { date: record.createdAt, options: { second: "2-digit" } }) }) }),
3893
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
3894
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", children: record.action }),
3895
+ /* @__PURE__ */ jsx(Tooltip, { label: formatMessage(getTrad(`auditlog.action.${record.action}`)), children: /* @__PURE__ */ jsx(
3896
+ Information,
3897
+ {
3898
+ "aria-hidden": true,
3899
+ style: { cursor: "help" },
3900
+ width: "1.4rem",
3901
+ height: "1.4rem",
3902
+ fill: "primary600"
3903
+ }
3904
+ ) })
3905
+ ] }) }),
3906
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { variant: "omega", children: record.email ?? "—" }) }),
3907
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { variant: "omega", children: record.ip ?? "—" }) }),
3908
+ /* @__PURE__ */ jsx(Td, { style: { maxWidth: "200px" }, children: record.details ? /* @__PURE__ */ jsx(Tooltip, { label: record.details, side: "top", children: /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "neutral600", style: DETAILS_TEXT_STYLE, children: record.details }) }) : /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "neutral600", children: "—" }) })
3909
+ ]
3910
+ },
3911
+ record.id
3912
+ ))
3913
+ ] })
3914
+ ] }),
3915
+ loading && records.length > 0 && /* @__PURE__ */ jsx(
3916
+ Flex,
3917
+ {
3918
+ justifyContent: "center",
3919
+ alignItems: "center",
3920
+ style: { position: "absolute", inset: 0, pointerEvents: "none" },
3921
+ children: /* @__PURE__ */ jsx(Loader, { small: true })
3922
+ }
3923
+ )
3897
3924
  ] }),
3898
3925
  /* @__PURE__ */ jsx(
3899
3926
  TablePagination,