@eintrek/erp-shell 0.1.30 → 0.1.32
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/dist/auth/permission-provider.d.ts +3 -1
- package/dist/index.esm.js +27 -6
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +27 -6
- package/dist/index.js.map +1 -1
- package/dist/navigation/nav-user.d.ts +3 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -901,7 +901,7 @@ function NavUser({ organization, avatarSrc, profileHref, selectOrganizationHref
|
|
|
901
901
|
setShowAppsMenu(!showAppsMenu);
|
|
902
902
|
setShowThemeMenu(false);
|
|
903
903
|
setShowLanguageMenu(false);
|
|
904
|
-
}, className: "flex w-full items-center gap-3 px-3 py-2.5 text-sm text-sidebar-foreground hover:bg-sidebar-accent rounded-md transition-colors", children: [jsxRuntime.jsx(lucideReact.LayoutGrid, { className: "h-4 w-4" }), jsxRuntime.jsx("span", { className: "flex-1 text-left", children: "\u0E41\u0E2D\u0E1B\u0E1E\u0E25\u0E34\u0E40\u0E04\u0E0A\u0E31\u0E19" }), jsxRuntime.jsx(lucideReact.ChevronUp, { className: `h-4 w-4 transition-transform ${showAppsMenu ? "rotate-180" : ""}` })] }), showAppsMenu && (jsxRuntime.jsx("div", { className: "mt-1 ml-4 space-y-1", children: visibleSystemLinks.map(link => (jsxRuntime.jsxs("button", { type: "button", onClick: () => handleSystemLink(link.href), className: "flex w-full items-center gap-3 px-3 py-2 text-sm text-sidebar-foreground hover:bg-sidebar-accent rounded-md transition-colors", children: [link.icon ?? jsxRuntime.jsx(lucideReact.ExternalLink, { className: "h-4 w-4" }), jsxRuntime.jsx("span", { className: "flex-1 text-left", children: link.label })] }, `${link.label}:${link.href}`))) }))] })), jsxRuntime.jsx("button", { onClick: handleSignOut, disabled: isSigningOut, className: "flex w-full items-center gap-3 px-3 py-2.5 text-sm text-sidebar-foreground hover:bg-sidebar-accent rounded-md transition-colors disabled:opacity-50 disabled:cursor-not-allowed mt-1", children: isSigningOut ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }), jsxRuntime.jsx("span", { className: "flex-1 text-left", children: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E2D\u0E2D\u0E01\u0E08\u0E32\u0E01\u0E23\u0E30\u0E1A\u0E1A..." })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(lucideReact.LogOut, { className: "h-4 w-4" }), jsxRuntime.jsx("span", { className: "flex-1 text-left", children: "\u0E2D\u0E2D\u0E01\u0E08\u0E32\u0E01\u0E23\u0E30\u0E1A\u0E1A" })] })) })] })] })) : null;
|
|
904
|
+
}, className: "flex w-full items-center gap-3 px-3 py-2.5 text-sm text-sidebar-foreground hover:bg-sidebar-accent rounded-md transition-colors", children: [jsxRuntime.jsx(lucideReact.LayoutGrid, { className: "h-4 w-4" }), jsxRuntime.jsx("span", { className: "flex-1 text-left", children: "\u0E41\u0E2D\u0E1B\u0E1E\u0E25\u0E34\u0E40\u0E04\u0E0A\u0E31\u0E19" }), jsxRuntime.jsx(lucideReact.ChevronUp, { className: `h-4 w-4 transition-transform ${showAppsMenu ? "rotate-180" : ""}` })] }), showAppsMenu && (jsxRuntime.jsx("div", { className: "mt-1 ml-4 space-y-1", children: visibleSystemLinks.map(link => (jsxRuntime.jsxs("button", { type: "button", onClick: () => handleSystemLink(link.href), className: "flex w-full items-center gap-3 px-3 py-2 text-sm text-sidebar-foreground hover:bg-sidebar-accent rounded-md transition-colors", children: [link.icon ?? jsxRuntime.jsx(lucideReact.ExternalLink, { className: "h-4 w-4" }), jsxRuntime.jsx("span", { className: "flex-1 text-left", children: link.label }), link.active && (jsxRuntime.jsx("div", { className: "h-2 w-2 rounded-full bg-primary" }))] }, `${link.label}:${link.href}`))) }))] })), jsxRuntime.jsx("button", { onClick: handleSignOut, disabled: isSigningOut, className: "flex w-full items-center gap-3 px-3 py-2.5 text-sm text-sidebar-foreground hover:bg-sidebar-accent rounded-md transition-colors disabled:opacity-50 disabled:cursor-not-allowed mt-1", children: isSigningOut ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }), jsxRuntime.jsx("span", { className: "flex-1 text-left", children: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E2D\u0E2D\u0E01\u0E08\u0E32\u0E01\u0E23\u0E30\u0E1A\u0E1A..." })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(lucideReact.LogOut, { className: "h-4 w-4" }), jsxRuntime.jsx("span", { className: "flex-1 text-left", children: "\u0E2D\u0E2D\u0E01\u0E08\u0E32\u0E01\u0E23\u0E30\u0E1A\u0E1A" })] })) })] })] })) : null;
|
|
905
905
|
return (jsxRuntime.jsx("div", { className: "p-2", children: jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsxs("button", { ref: buttonRef, onClick: () => setIsOpen(!isOpen), className: "flex w-full items-center gap-2 p-2 rounded-md transition-colors", children: [jsxRuntime.jsxs(erpTheme.Avatar, { className: "h-8 w-8 rounded-lg", children: [user.avatar ? (jsxRuntime.jsx(erpTheme.AvatarImage, { src: user.avatar, alt: user.name })) : null, jsxRuntime.jsx(erpTheme.AvatarFallback, { className: "rounded-lg bg-primary text-primary-foreground w-8 h-8", children: getInitials(user.name) })] }), jsxRuntime.jsxs("div", { className: "grid flex-1 text-left text-sm leading-tight", children: [jsxRuntime.jsx("span", { className: "truncate font-medium", title: user.name, children: user.name }), jsxRuntime.jsx("span", { className: "truncate text-xs text-sidebar-foreground/70", title: user.email, children: user.email })] }), jsxRuntime.jsx(lucideReact.ChevronUp, { className: `ml-auto size-4 transition-transform ${isOpen ? "rotate-180" : ""}` })] }), typeof window !== "undefined" &&
|
|
906
906
|
reactDom.createPortal(dropdownContent, document.body)] }) }));
|
|
907
907
|
}
|
|
@@ -1368,7 +1368,9 @@ function DeniedCard() {
|
|
|
1368
1368
|
* over the optional `fallbackRulesMap`), passes the resolved required roles
|
|
1369
1369
|
* into the host's `usePermission` probe, and renders one of:
|
|
1370
1370
|
*
|
|
1371
|
-
* 1. rules / probe still loading → `null` (let Suspense /
|
|
1371
|
+
* 1. rules / probe still loading → `null` on FIRST load only (let Suspense /
|
|
1372
|
+
* loading.tsx show); once this subtree has been granted access, a later
|
|
1373
|
+
* loading state keeps the children mounted — see the note below
|
|
1372
1374
|
* 2. no rule covers this path → deny (fail closed)
|
|
1373
1375
|
* 3. rule is explicit public (`[]`) → children
|
|
1374
1376
|
* 4. probe says no access → deny card
|
|
@@ -1388,7 +1390,25 @@ function PermissionProvider({ children, usePermission, fallbackRulesMap, }) {
|
|
|
1388
1390
|
const { hasPermission, isLoading } = usePermission({
|
|
1389
1391
|
requiredRoles: lookup.roles,
|
|
1390
1392
|
});
|
|
1391
|
-
|
|
1393
|
+
// Returning null unmounts the page. That is fine before anything has been
|
|
1394
|
+
// shown, and destructive afterwards: next-auth flips its status back to
|
|
1395
|
+
// "loading" on every session update() — one runs every 4 minutes — and a
|
|
1396
|
+
// rules hook that reports loading on that transient took the page down with
|
|
1397
|
+
// it. Users filling a long form watched everything they had typed vanish,
|
|
1398
|
+
// which reads as a spontaneous page refresh.
|
|
1399
|
+
//
|
|
1400
|
+
// So a loading state only blanks the subtree until access has been granted
|
|
1401
|
+
// once. After that the children stay mounted while the check re-settles; the
|
|
1402
|
+
// API still enforces access on every request, and a genuine loss of
|
|
1403
|
+
// permission surfaces as a denied response rather than a wiped form.
|
|
1404
|
+
//
|
|
1405
|
+
// The grant is remembered per pathname, not per mount. This provider lives in
|
|
1406
|
+
// the layout, so it survives every in-app navigation — a bare boolean would
|
|
1407
|
+
// carry one page's grant onto the next route and flash a page the user may
|
|
1408
|
+
// not be allowed to see while its own check is still running.
|
|
1409
|
+
const grantedForPathRef = React.useRef(null);
|
|
1410
|
+
const grantedHere = grantedForPathRef.current === pathname;
|
|
1411
|
+
if (rulesLoading && !grantedHere) {
|
|
1392
1412
|
return null;
|
|
1393
1413
|
}
|
|
1394
1414
|
// Unknown route → deny. Only explicit `required_child_groups: []` is public.
|
|
@@ -1396,15 +1416,16 @@ function PermissionProvider({ children, usePermission, fallbackRulesMap, }) {
|
|
|
1396
1416
|
return jsxRuntime.jsx(DeniedCard, {});
|
|
1397
1417
|
}
|
|
1398
1418
|
if (lookup.roles.length === 0) {
|
|
1419
|
+
grantedForPathRef.current = pathname;
|
|
1399
1420
|
return jsxRuntime.jsx(jsxRuntime.Fragment, { children: children });
|
|
1400
1421
|
}
|
|
1401
|
-
|
|
1402
|
-
if (isLoadingCombined) {
|
|
1422
|
+
if (isLoading && !grantedHere) {
|
|
1403
1423
|
return null;
|
|
1404
1424
|
}
|
|
1405
|
-
if (!hasPermission) {
|
|
1425
|
+
if (!hasPermission && !isLoading) {
|
|
1406
1426
|
return jsxRuntime.jsx(DeniedCard, {});
|
|
1407
1427
|
}
|
|
1428
|
+
grantedForPathRef.current = pathname;
|
|
1408
1429
|
return jsxRuntime.jsx(jsxRuntime.Fragment, { children: children });
|
|
1409
1430
|
}
|
|
1410
1431
|
|