update header dependency
This commit is contained in:
parent
8348d3b36f
commit
07a4fd3bbc
184
dist/chunks/permission-DL7ciWwc.js
vendored
184
dist/chunks/permission-DL7ciWwc.js
vendored
@ -1,184 +0,0 @@
|
|||||||
import { jsx as y } from "react/jsx-runtime";
|
|
||||||
import { createContext as L, useState as K, useRef as O, useCallback as h, useEffect as k, useMemo as T, useContext as R } from "react";
|
|
||||||
import { useQuery as b } from "@tanstack/react-query";
|
|
||||||
import F from "keycloak-js";
|
|
||||||
let x = null, r = null, E = null, C = !1;
|
|
||||||
const G = L(null), _ = ({ url: e, realm: s, clientId: t }) => (r || (r = new F({
|
|
||||||
url: e,
|
|
||||||
realm: s,
|
|
||||||
clientId: t
|
|
||||||
})), r), j = (e) => (x = e, x), ee = () => C, J = () => !!(r != null && r.authenticated), Q = () => (r == null ? void 0 : r.token) ?? null, te = async (e = 30) => r ? r.updateToken(e) : !1, U = (e) => r == null ? void 0 : r.logout(e), re = (e) => r == null ? void 0 : r.login(e), W = ({ config: e, initOptions: s = {} } = {}) => {
|
|
||||||
const t = e ?? x;
|
|
||||||
if (!(t != null && t.url) || !(t != null && t.realm) || !(t != null && t.clientId))
|
|
||||||
throw new Error("Keycloak config is required.");
|
|
||||||
j(t);
|
|
||||||
const n = _(t);
|
|
||||||
return E || (E = n.init({
|
|
||||||
onLoad: "login-required",
|
|
||||||
checkLoginIframe: !1,
|
|
||||||
flow: "implicit",
|
|
||||||
...s
|
|
||||||
}).then((o) => (C = !0, o)).catch((o) => {
|
|
||||||
throw C = !0, o;
|
|
||||||
})), E;
|
|
||||||
}, se = ({
|
|
||||||
children: e,
|
|
||||||
config: s,
|
|
||||||
initOptions: t,
|
|
||||||
loading: n = null
|
|
||||||
}) => {
|
|
||||||
const [o, m] = K(!1), [p, w] = K(!1), [u, c] = K(null), l = O(!1), i = h(() => {
|
|
||||||
r && (w(!!r.authenticated), c(r.token ?? null));
|
|
||||||
}, []);
|
|
||||||
k(() => (l.current = !0, W({ config: s, initOptions: t }).then((a) => {
|
|
||||||
l.current && (w(!!a), c((r == null ? void 0 : r.token) ?? null), m(!0));
|
|
||||||
}).catch(() => {
|
|
||||||
l.current && (w(!1), c(null), m(!0));
|
|
||||||
}), () => {
|
|
||||||
l.current = !1;
|
|
||||||
}), [s, t]);
|
|
||||||
const P = h(() => r == null ? void 0 : r.login(), []), d = h((a) => r == null ? void 0 : r.logout(a), []), f = h(
|
|
||||||
async (a = 30) => {
|
|
||||||
if (!r) return !1;
|
|
||||||
const z = await r.updateToken(a);
|
|
||||||
return i(), z;
|
|
||||||
},
|
|
||||||
[i]
|
|
||||||
), S = T(
|
|
||||||
() => ({
|
|
||||||
initialized: o,
|
|
||||||
authenticated: p,
|
|
||||||
token: u,
|
|
||||||
login: P,
|
|
||||||
logout: d,
|
|
||||||
updateToken: f
|
|
||||||
}),
|
|
||||||
[o, p, u, P, d, f]
|
|
||||||
);
|
|
||||||
return o ? /* @__PURE__ */ y(G.Provider, { value: S, children: e }) : n;
|
|
||||||
}, B = L(null), v = "permissions", Y = (e) => (e == null ? void 0 : e.storageKey) || v, ne = ({
|
|
||||||
user: e = null,
|
|
||||||
children: s,
|
|
||||||
config: t,
|
|
||||||
loading: n = /* @__PURE__ */ y("div", { children: "Loading permissions..." }),
|
|
||||||
errorFallback: o = /* @__PURE__ */ y("div", { children: "Error loading permissions" })
|
|
||||||
}) => {
|
|
||||||
const m = Y(t), [p, w] = K(
|
|
||||||
() => $(m)
|
|
||||||
), { data: u, isLoading: c, error: l } = V(t), i = u ?? p;
|
|
||||||
k(() => {
|
|
||||||
u && (D(m, u), w(u));
|
|
||||||
}, [u, m]);
|
|
||||||
const P = T(
|
|
||||||
() => I(i),
|
|
||||||
[i]
|
|
||||||
), d = h(
|
|
||||||
(a = "") => M(P, a),
|
|
||||||
[P]
|
|
||||||
), f = h((a = "") => !d(a), [d]), S = T(
|
|
||||||
() => ({
|
|
||||||
user: e,
|
|
||||||
can: d,
|
|
||||||
cannot: f,
|
|
||||||
permissions: i,
|
|
||||||
isLoading: c,
|
|
||||||
error: l
|
|
||||||
}),
|
|
||||||
[e, d, f, i, c, l]
|
|
||||||
);
|
|
||||||
return l && !i ? o : c && !i ? n : /* @__PURE__ */ y(B.Provider, { value: S, children: s });
|
|
||||||
}, $ = (e = v) => {
|
|
||||||
if (!(typeof window > "u"))
|
|
||||||
try {
|
|
||||||
const s = localStorage.getItem(e);
|
|
||||||
return s ? JSON.parse(s) : void 0;
|
|
||||||
} catch {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}, D = (e = v, s) => {
|
|
||||||
if (!(typeof window > "u"))
|
|
||||||
try {
|
|
||||||
localStorage.setItem(e, JSON.stringify(s));
|
|
||||||
} catch {
|
|
||||||
}
|
|
||||||
}, oe = (e = v) => {
|
|
||||||
if (!(typeof window > "u"))
|
|
||||||
try {
|
|
||||||
localStorage.removeItem(e);
|
|
||||||
} catch {
|
|
||||||
}
|
|
||||||
}, I = (e) => e ? new Set(e.map((s) => s.name)) : /* @__PURE__ */ new Set(), M = (e, s = "") => {
|
|
||||||
if (!s) return !1;
|
|
||||||
if (e.has(s) || [...e].some((n) => n.startsWith(s))) return !0;
|
|
||||||
let t = s;
|
|
||||||
for (; t.includes(":"); )
|
|
||||||
if (t = t.slice(0, t.lastIndexOf(":")), e.has(`${t}:*`)) return !0;
|
|
||||||
return !1;
|
|
||||||
}, q = {
|
|
||||||
all: ["permissions"],
|
|
||||||
list: (e) => [...q.all, e]
|
|
||||||
}, N = async (e) => {
|
|
||||||
if (!(e != null && e.url) || !(e != null && e.clientId))
|
|
||||||
throw new Error("Permission config requires url and clientId.");
|
|
||||||
const s = Q(), t = new URL(e.url);
|
|
||||||
t.searchParams.set("client_id", e.clientId);
|
|
||||||
const n = await fetch(t.toString(), {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
...s ? { Authorization: `Bearer ${s}` } : {}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (!n.ok)
|
|
||||||
throw new Error("Failed to load permissions");
|
|
||||||
return n.json();
|
|
||||||
}, A = () => {
|
|
||||||
const e = R(B);
|
|
||||||
if (!e)
|
|
||||||
throw new Error("usePermission must be used within PermissionProvider");
|
|
||||||
return e;
|
|
||||||
}, V = (e) => b({
|
|
||||||
queryKey: q.list(e == null ? void 0 : e.clientId),
|
|
||||||
queryFn: () => N(e),
|
|
||||||
enabled: !!(e != null && e.url) && !!(e != null && e.clientId) && J(),
|
|
||||||
staleTime: 1 / 0,
|
|
||||||
refetchOnWindowFocus: !1,
|
|
||||||
refetchOnReconnect: !1
|
|
||||||
}), ie = ({
|
|
||||||
perm: e,
|
|
||||||
children: s,
|
|
||||||
fallback: t = /* @__PURE__ */ y("div", { children: "You are not allowed to do this" })
|
|
||||||
}) => {
|
|
||||||
const { can: n } = A();
|
|
||||||
return n(e) ? s : t;
|
|
||||||
}, ae = ({ children: e }) => {
|
|
||||||
const { can: s, permissions: t } = A();
|
|
||||||
return t && s("admin") ? e : null;
|
|
||||||
}, ue = ({ children: e, forceNoAccess: s = !1 }) => {
|
|
||||||
const { can: t, isLoading: n } = A();
|
|
||||||
return k(() => {
|
|
||||||
n || (s || !t("panel_access")) && U();
|
|
||||||
}, [n, t, s]), e;
|
|
||||||
};
|
|
||||||
export {
|
|
||||||
ae as A,
|
|
||||||
ie as C,
|
|
||||||
se as K,
|
|
||||||
ne as P,
|
|
||||||
te as a,
|
|
||||||
J as b,
|
|
||||||
W as c,
|
|
||||||
ue as d,
|
|
||||||
M as e,
|
|
||||||
oe as f,
|
|
||||||
Q as g,
|
|
||||||
I as h,
|
|
||||||
ee as i,
|
|
||||||
$ as j,
|
|
||||||
N as k,
|
|
||||||
U as l,
|
|
||||||
re as m,
|
|
||||||
V as n,
|
|
||||||
q as p,
|
|
||||||
D as s,
|
|
||||||
A as u
|
|
||||||
};
|
|
||||||
17
dist/core-runtime.js
vendored
Normal file
17
dist/core-runtime.js
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { createContext as e, useContext as n } from "react";
|
||||||
|
const s = e(null), i = () => {
|
||||||
|
const t = n(s);
|
||||||
|
if (!t)
|
||||||
|
throw new Error("usePermission must be used within PermissionProvider");
|
||||||
|
return t;
|
||||||
|
};
|
||||||
|
let o = null;
|
||||||
|
const u = (t) => {
|
||||||
|
o = typeof t == "function" ? t : null;
|
||||||
|
}, c = (t) => o == null ? void 0 : o(t);
|
||||||
|
export {
|
||||||
|
s as PermissionContext,
|
||||||
|
c as logoutKeycloakFromRuntime,
|
||||||
|
u as setKeycloakLogoutHandler,
|
||||||
|
i as usePermission
|
||||||
|
};
|
||||||
669
dist/core.js
vendored
669
dist/core.js
vendored
File diff suppressed because one or more lines are too long
251
dist/layout.js
vendored
251
dist/layout.js
vendored
File diff suppressed because one or more lines are too long
@ -1,4 +1,5 @@
|
|||||||
import Keycloak from "keycloak-js";
|
import Keycloak from "keycloak-js";
|
||||||
|
import { setKeycloakLogoutHandler } from "./runtime.jsx";
|
||||||
import {
|
import {
|
||||||
createContext,
|
createContext,
|
||||||
useCallback,
|
useCallback,
|
||||||
@ -63,6 +64,8 @@ export const logoutKeycloak = (options) => {
|
|||||||
return keycloakInstance?.logout(options);
|
return keycloakInstance?.logout(options);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
setKeycloakLogoutHandler(logoutKeycloak);
|
||||||
|
|
||||||
export const loginKeycloak = (options) => {
|
export const loginKeycloak = (options) => {
|
||||||
return keycloakInstance?.login(options);
|
return keycloakInstance?.login(options);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,22 +1,17 @@
|
|||||||
import {
|
import {
|
||||||
createContext,
|
|
||||||
useCallback,
|
useCallback,
|
||||||
useContext,
|
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { PermissionContext, usePermission } from "./runtime.jsx";
|
||||||
import {
|
import {
|
||||||
getKeycloakToken,
|
getKeycloakToken,
|
||||||
isKeycloakAuthenticated,
|
isKeycloakAuthenticated,
|
||||||
logoutKeycloak,
|
logoutKeycloak,
|
||||||
} from "./keycloak.jsx";
|
} from "./keycloak.jsx";
|
||||||
|
|
||||||
// contexts
|
|
||||||
|
|
||||||
const PermissionContext = createContext(null);
|
|
||||||
|
|
||||||
// storage keys
|
// storage keys
|
||||||
|
|
||||||
const DEFAULT_STORAGE_KEY = "permissions";
|
const DEFAULT_STORAGE_KEY = "permissions";
|
||||||
@ -178,15 +173,7 @@ export const getPermissions = async (config) => {
|
|||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
|
|
||||||
export const usePermission = () => {
|
export { usePermission } from "./runtime.jsx";
|
||||||
const context = useContext(PermissionContext);
|
|
||||||
|
|
||||||
if (!context) {
|
|
||||||
throw new Error("usePermission must be used within PermissionProvider");
|
|
||||||
}
|
|
||||||
|
|
||||||
return context;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const usePermissionsQuery = (config) => {
|
export const usePermissionsQuery = (config) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
|
|||||||
27
src/core/runtime.jsx
Normal file
27
src/core/runtime.jsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { createContext, useContext } from "react";
|
||||||
|
|
||||||
|
// Lightweight shared runtime for layout/core integration.
|
||||||
|
// IMPORTANT: keep this module free of optional dependencies such as
|
||||||
|
// @tanstack/react-query and keycloak-js. `uikit/layout` imports it directly.
|
||||||
|
|
||||||
|
export const PermissionContext = createContext(null);
|
||||||
|
|
||||||
|
export const usePermission = () => {
|
||||||
|
const context = useContext(PermissionContext);
|
||||||
|
|
||||||
|
if (!context) {
|
||||||
|
throw new Error("usePermission must be used within PermissionProvider");
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
|
let keycloakLogoutHandler = null;
|
||||||
|
|
||||||
|
export const setKeycloakLogoutHandler = (handler) => {
|
||||||
|
keycloakLogoutHandler = typeof handler === "function" ? handler : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const logoutKeycloakFromRuntime = (options) => {
|
||||||
|
return keycloakLogoutHandler?.(options);
|
||||||
|
};
|
||||||
@ -20,42 +20,12 @@ import {
|
|||||||
useDisclosure,
|
useDisclosure,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { Children, forwardRef, isValidElement, useRef } from "react";
|
import { Children, forwardRef, isValidElement, useRef } from "react";
|
||||||
import { logoutKeycloak } from "../core/keycloak.jsx";
|
import { FiMoreHorizontal } from "react-icons/fi/index.js";
|
||||||
import { usePermission } from "../core/permission.jsx";
|
import { IoExit } from "react-icons/io5/index.js";
|
||||||
|
import {
|
||||||
const EllipsisIcon = (props) => (
|
logoutKeycloakFromRuntime,
|
||||||
<svg
|
usePermission,
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
} from "../core/runtime.jsx";
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="3"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<circle cx="12" cy="12" r="1" />
|
|
||||||
<circle cx="19" cy="12" r="1" />
|
|
||||||
<circle cx="5" cy="12" r="1" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
|
|
||||||
const LogOutIcon = (props) => (
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="3"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<path d="m16 17 5-5-5-5" />
|
|
||||||
<path d="M21 12H9" />
|
|
||||||
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
|
|
||||||
const normalizeSidebarPath = (value = "") => {
|
const normalizeSidebarPath = (value = "") => {
|
||||||
const path = String(value).split(/[?#]/)[0] || "";
|
const path = String(value).split(/[?#]/)[0] || "";
|
||||||
@ -101,34 +71,11 @@ const DesktopNavItem = ({
|
|||||||
onNavigate,
|
onNavigate,
|
||||||
}) => {
|
}) => {
|
||||||
const commonProps = {
|
const commonProps = {
|
||||||
variant: "ghost",
|
variant: isActive ? "solid" : "ghost",
|
||||||
position: "relative",
|
colorScheme: isActive ? "blue" : undefined,
|
||||||
bg: "transparent",
|
|
||||||
|
|
||||||
_before: isActive
|
|
||||||
? {
|
|
||||||
content: '""',
|
|
||||||
position: "absolute",
|
|
||||||
top: 0,
|
|
||||||
bottom: 0,
|
|
||||||
insetInlineStart: 0,
|
|
||||||
insetInlineEnd: 0,
|
|
||||||
bg: "gray.200",
|
|
||||||
borderRadius: "md",
|
|
||||||
zIndex: 0,
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
|
|
||||||
sx: {
|
|
||||||
"> svg": {
|
|
||||||
position: "relative",
|
|
||||||
zIndex: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
icon: <Icon as={item.icon} />,
|
icon: <Icon as={item.icon} />,
|
||||||
fontSize: "xl",
|
fontSize: "xl",
|
||||||
size: { base: "sm", lg: "md" },
|
size: { base: "sm", lg: "lg" },
|
||||||
"aria-label": item.title,
|
"aria-label": item.title,
|
||||||
"aria-current": isActive ? "page" : undefined,
|
"aria-current": isActive ? "page" : undefined,
|
||||||
};
|
};
|
||||||
@ -149,7 +96,11 @@ const DesktopNavItem = ({
|
|||||||
: { as: "a", href: item.path };
|
: { as: "a", href: item.path };
|
||||||
|
|
||||||
content = (
|
content = (
|
||||||
<IconButton {...commonProps} {...linkProps} onClick={onNavigate} />
|
<IconButton
|
||||||
|
{...commonProps}
|
||||||
|
{...linkProps}
|
||||||
|
onClick={onNavigate}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,7 +211,8 @@ const SidebarRoot = ({
|
|||||||
);
|
);
|
||||||
const hasExit = Boolean(exitElement);
|
const hasExit = Boolean(exitElement);
|
||||||
const logoutTitle = exitElement?.props?.title || "خروج";
|
const logoutTitle = exitElement?.props?.title || "خروج";
|
||||||
const logoutIcon = exitElement?.props?.icon || LogOutIcon;
|
const logoutIcon = exitElement?.props?.icon || IoExit;
|
||||||
|
|
||||||
|
|
||||||
const resolvedCurrentPath =
|
const resolvedCurrentPath =
|
||||||
currentPath ??
|
currentPath ??
|
||||||
@ -272,7 +224,9 @@ const SidebarRoot = ({
|
|||||||
const secondaryItems = items.slice(primaryMobileCount);
|
const secondaryItems = items.slice(primaryMobileCount);
|
||||||
|
|
||||||
const disabled = (item) => {
|
const disabled = (item) => {
|
||||||
const permissions = Array.isArray(item?.perm) ? item.perm : [item?.perm];
|
const permissions = Array.isArray(item?.perm)
|
||||||
|
? item.perm
|
||||||
|
: [item?.perm];
|
||||||
|
|
||||||
return permissions.filter(Boolean).some((permission) => cannot(permission));
|
return permissions.filter(Boolean).some((permission) => cannot(permission));
|
||||||
};
|
};
|
||||||
@ -288,7 +242,7 @@ const SidebarRoot = ({
|
|||||||
|
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
onClose();
|
onClose();
|
||||||
logoutKeycloak();
|
logoutKeycloakFromRuntime();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -401,7 +355,7 @@ const SidebarRoot = ({
|
|||||||
color={isSecondaryItemActive ? "blue.600" : "gray.600"}
|
color={isSecondaryItemActive ? "blue.600" : "gray.600"}
|
||||||
bg={isSecondaryItemActive ? "blue.50" : "transparent"}
|
bg={isSecondaryItemActive ? "blue.50" : "transparent"}
|
||||||
>
|
>
|
||||||
<Icon as={EllipsisIcon} boxSize={5} />
|
<Icon as={FiMoreHorizontal} boxSize={5} />
|
||||||
<Text
|
<Text
|
||||||
fontSize="10px"
|
fontSize="10px"
|
||||||
fontWeight={isSecondaryItemActive ? "bold" : "medium"}
|
fontWeight={isSecondaryItemActive ? "bold" : "medium"}
|
||||||
|
|||||||
@ -96,6 +96,8 @@ export default defineConfig(({ mode }) => {
|
|||||||
|
|
||||||
core: path.resolve(process.cwd(), "src/core/index.js"),
|
core: path.resolve(process.cwd(), "src/core/index.js"),
|
||||||
|
|
||||||
|
"core-runtime": path.resolve(process.cwd(), "src/core/runtime.jsx"),
|
||||||
|
|
||||||
table: path.resolve(process.cwd(), "src/table/index.js"),
|
table: path.resolve(process.cwd(), "src/table/index.js"),
|
||||||
|
|
||||||
form: path.resolve(process.cwd(), "src/form/index.js"),
|
form: path.resolve(process.cwd(), "src/form/index.js"),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user