@eintrek/erp-shell 0.1.25 → 0.1.26

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.
@@ -0,0 +1,22 @@
1
+ export interface AccessDeniedProps {
2
+ /** Bold headline. */
3
+ title?: string;
4
+ /** Secondary explanation — pass the page-specific denied reason here. */
5
+ message?: string;
6
+ /** Where "กลับไปหน้าหลัก" navigates (default "/"). */
7
+ homeHref?: string;
8
+ homeLabel?: string;
9
+ /** When set, shows the "ติดต่อฝ่ายสนับสนุน" button (URL or mailto:). */
10
+ supportHref?: string;
11
+ supportLabel?: string;
12
+ /** Fill the surrounding content area and vertically center (default true). */
13
+ fullScreen?: boolean;
14
+ className?: string;
15
+ }
16
+ /**
17
+ * Central "no access" screen shared by every policy check (route-level
18
+ * PermissionProvider and page-level PermissionGateShell). One look everywhere:
19
+ * an illustration, a 403 headline, the page-specific reason, and recovery
20
+ * actions.
21
+ */
22
+ export declare function AccessDenied({ title, message, homeHref, homeLabel, supportHref, supportLabel, fullScreen, className, }: AccessDeniedProps): import("react/jsx-runtime").JSX.Element;
package/dist/index.d.ts CHANGED
@@ -19,6 +19,7 @@ export { OrganizationSwitcher, type OrganizationSwitcherProps, type Organization
19
19
  export { SidebarHeader } from "./navigation/sidebar-header";
20
20
  export { AppSidebar, type AppSidebarProps } from "./navigation/app-sidebar";
21
21
  export { AppLayoutShell, type AppLayoutShellProps, type AppLayoutShellOrganization, } from "./navigation/app-layout-shell";
22
+ export { AccessDenied, type AccessDeniedProps } from "./auth/access-denied";
22
23
  export { PermissionGateShell, type PermissionGateShellProps, type PermissionGateSkeletonVariant, } from "./auth/permission-gate-shell";
23
24
  export { PermissionProvider, type PermissionProviderProps, type PermissionProbe, type UsePermissionHook, } from "./auth/permission-provider";
24
25
  export { getRequiredChildGroups, normalizePathnameForPermissionRules, resolveUiRoutePermission, type UiRoutePermissionLookup, } from "./auth/permission-utils";
package/dist/index.esm.js CHANGED
@@ -5,7 +5,7 @@ import { useSession, signOut } from 'next-auth/react';
5
5
  import { useQueryClient, MutationCache, QueryCache, QueryClient, QueryClientProvider } from '@tanstack/react-query';
6
6
  import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
7
7
  import { cn, Button, Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbSeparator, BreadcrumbPage, Avatar, AvatarImage, AvatarFallback, Collapsible, CollapsibleTrigger, CollapsibleContent, Badge, Skeleton, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuItem, DropdownMenuSeparator, Card, CardHeader, CardContent } from '@eintrek/erp-theme';
8
- import { PanelLeftClose, Menu, ChevronRight, Building2, ExternalLink, Palette, ChevronUp, Sun, Moon, Monitor, Globe, User, Loader2, LogOut, ChevronDown, ChevronsUpDown, Plus, AlertCircle } from 'lucide-react';
8
+ import { PanelLeftClose, Menu, ChevronRight, Building2, ExternalLink, Palette, ChevronUp, Sun, Moon, Monitor, Globe, User, Loader2, LogOut, ChevronDown, ChevronsUpDown, Plus, Sparkles, Ban, Home, Headset } from 'lucide-react';
9
9
  import { create } from 'zustand';
10
10
  import { persist, createJSONStorage } from 'zustand/middleware';
11
11
  import { useRouter, usePathname, useParams } from 'next/navigation';
@@ -1151,6 +1151,27 @@ function cx(...parts) {
1151
1151
  return parts.filter(Boolean).join(" ");
1152
1152
  }
1153
1153
 
1154
+ /**
1155
+ * Central "no access" screen shared by every policy check (route-level
1156
+ * PermissionProvider and page-level PermissionGateShell). One look everywhere:
1157
+ * an illustration, a 403 headline, the page-specific reason, and recovery
1158
+ * actions.
1159
+ */
1160
+ function AccessDenied({ title = "ขออภัย, คุณไม่มีสิทธิ์เข้าถึงหน้านี้ (403)", message = "คุณไม่มีสิทธิ์เข้าถึงหน้านี้ ขออภัยในความไม่สะดวก", homeHref = "/", homeLabel = "กลับไปหน้าหลัก", supportHref, supportLabel = "ติดต่อฝ่ายสนับสนุน", fullScreen = true, className, }) {
1161
+ const go = (href) => {
1162
+ if (typeof window !== "undefined")
1163
+ window.location.href = href;
1164
+ };
1165
+ const wrapperClass = [
1166
+ "flex min-w-0 flex-1 items-center justify-center p-4",
1167
+ fullScreen ? "min-h-[70vh]" : "",
1168
+ className ?? "",
1169
+ ]
1170
+ .filter(Boolean)
1171
+ .join(" ");
1172
+ return (jsx("div", { className: wrapperClass, children: jsxs("div", { className: "relative w-full max-w-3xl overflow-hidden rounded-2xl border border-border/60 bg-card/60 p-8 shadow-sm backdrop-blur sm:p-10", children: [jsx(Sparkles, { className: "pointer-events-none absolute right-6 top-6 h-5 w-5 text-primary/40" }), jsxs("div", { className: "flex flex-col items-center gap-8 text-center sm:flex-row sm:text-left", children: [jsxs("div", { className: "relative shrink-0", children: [jsx("div", { className: "absolute inset-0 rounded-full bg-destructive/20 blur-2xl" }), jsx("div", { className: "relative flex h-28 w-28 items-center justify-center rounded-3xl border border-border/60 bg-gradient-to-br from-muted/40 to-background shadow-inner", children: jsx("div", { className: "flex h-16 w-16 items-center justify-center rounded-full border border-destructive/40 bg-destructive/10", children: jsx(Ban, { className: "h-8 w-8 text-destructive", strokeWidth: 2.5 }) }) })] }), jsxs("div", { className: "min-w-0 flex-1", children: [jsx("h1", { className: "text-xl font-bold text-foreground sm:text-2xl", children: title }), message ? (jsx("p", { className: "mt-3 whitespace-pre-line text-sm leading-relaxed text-muted-foreground sm:text-base", children: message })) : null, jsxs("div", { className: "mt-6 flex flex-col gap-3 sm:flex-row", children: [jsxs(Button, { type: "button", onClick: () => go(homeHref), className: "w-full sm:w-auto", children: [jsx(Home, { className: "mr-2 h-4 w-4" }), homeLabel] }), supportHref ? (jsxs(Button, { type: "button", variant: "outline", onClick: () => go(supportHref), className: "w-full sm:w-auto", children: [jsx(Headset, { className: "mr-2 h-4 w-4" }), supportLabel] })) : null] })] })] })] }) }));
1173
+ }
1174
+
1154
1175
  const shellClassName = "flex min-w-0 flex-1 flex-col gap-6 overflow-x-hidden";
1155
1176
  /**
1156
1177
  * Shared layout for “กำลังตรวจสอบสิทธิ์” (skeleton) and “ไม่มีสิทธิ์” (message).
@@ -1161,7 +1182,7 @@ function PermissionGateShell({ policyLoading, hasPolicyAccess, deniedMessage, ch
1161
1182
  return (jsx("div", { className: shellClassName, children: jsx("div", { className: skeletonGridClassName, children: Array.from({ length: skeletonCount }).map((_, i) => (jsx(Card, { children: skeletonVariant === "kpi" ? (jsxs(Fragment$1, { children: [jsxs(CardHeader, { className: "flex flex-row items-center justify-between pb-2", children: [jsx(Skeleton, { className: "h-4 w-24" }), jsx(Skeleton, { className: "h-4 w-4 rounded" })] }), jsxs(CardContent, { children: [jsx(Skeleton, { className: "h-8 w-32 mb-2" }), jsx(Skeleton, { className: "h-3 w-20" })] })] })) : (jsxs(Fragment$1, { children: [jsx(CardHeader, { className: "pb-2", children: jsx(Skeleton, { className: "h-4 w-24" }) }), jsx(CardContent, { children: jsx(Skeleton, { className: "h-8 w-16" }) })] })) }, i))) }) }));
1162
1183
  }
1163
1184
  if (!hasPolicyAccess) {
1164
- return (jsx("div", { className: shellClassName, children: jsx(Card, { children: jsx(CardContent, { className: "text-sm text-muted-foreground", children: deniedMessage }) }) }));
1185
+ return (jsx("div", { className: shellClassName, children: jsx(AccessDenied, { message: deniedMessage }) }));
1165
1186
  }
1166
1187
  return jsx(Fragment$1, { children: children });
1167
1188
  }
@@ -1297,7 +1318,7 @@ function getRequiredChildGroups(pathname, rulesMap) {
1297
1318
  }
1298
1319
 
1299
1320
  function DeniedCard() {
1300
- return (jsx(Card, { className: "p-6", children: jsxs("div", { className: "flex flex-col items-center justify-center gap-3 text-center", children: [jsx(AlertCircle, { className: "h-8 w-8 text-destructive" }), jsxs("div", { className: "space-y-1", children: [jsx("h3", { className: "text-lg font-semibold text-destructive", children: "\u0E44\u0E21\u0E48\u0E21\u0E35\u0E2A\u0E34\u0E17\u0E18\u0E34\u0E4C\u0E40\u0E02\u0E49\u0E32\u0E16\u0E36\u0E07" }), jsx("p", { className: "text-sm text-muted-foreground", children: "\u0E04\u0E38\u0E13\u0E44\u0E21\u0E48\u0E21\u0E35\u0E2A\u0E34\u0E17\u0E18\u0E34\u0E4C\u0E43\u0E19\u0E01\u0E32\u0E23\u0E40\u0E02\u0E49\u0E32\u0E16\u0E36\u0E07\u0E2B\u0E19\u0E49\u0E32\u0E19\u0E35\u0E49 \u0E2B\u0E32\u0E01\u0E04\u0E34\u0E14\u0E27\u0E48\u0E32\u0E04\u0E27\u0E23\u0E40\u0E02\u0E49\u0E32\u0E16\u0E36\u0E07\u0E44\u0E14\u0E49 \u0E01\u0E23\u0E38\u0E13\u0E32\u0E15\u0E34\u0E14\u0E15\u0E48\u0E2D\u0E1C\u0E39\u0E49\u0E14\u0E39\u0E41\u0E25\u0E23\u0E30\u0E1A\u0E1A\u0E02\u0E2D\u0E07\u0E2D\u0E07\u0E04\u0E4C\u0E01\u0E23" })] })] }) }));
1321
+ return (jsx(AccessDenied, { message: "\u0E04\u0E38\u0E13\u0E44\u0E21\u0E48\u0E21\u0E35\u0E2A\u0E34\u0E17\u0E18\u0E34\u0E4C\u0E40\u0E02\u0E49\u0E32\u0E16\u0E36\u0E07\u0E2B\u0E19\u0E49\u0E32\u0E19\u0E35\u0E49 \u0E2B\u0E32\u0E01\u0E04\u0E34\u0E14\u0E27\u0E48\u0E32\u0E04\u0E27\u0E23\u0E40\u0E02\u0E49\u0E32\u0E16\u0E36\u0E07\u0E44\u0E14\u0E49 \u0E01\u0E23\u0E38\u0E13\u0E32\u0E15\u0E34\u0E14\u0E15\u0E48\u0E2D\u0E1C\u0E39\u0E49\u0E14\u0E39\u0E41\u0E25\u0E23\u0E30\u0E1A\u0E1A\u0E02\u0E2D\u0E07\u0E2D\u0E07\u0E04\u0E4C\u0E01\u0E23" }));
1301
1322
  }
1302
1323
  /**
1303
1324
  * Permission gate for a page subtree. Looks up the current pathname in the
@@ -1837,5 +1858,5 @@ const getAxiosErrorMessage = (error) => {
1837
1858
  return String(error);
1838
1859
  };
1839
1860
 
1840
- export { ApiError, AppBreadcrumb, AppLayoutShell, AppSidebar, FeatureFlagsProvider, HeaderBackground, NavMain, NavTools, NavUser, OrganizationSwitcher, PermissionGateShell, PermissionProvider, PermissionRulesProvider, ReactQueryProvider, SessionBackedProfileCacheSeeder, SessionRefreshTrigger, SidebarContent, SidebarFooter, SidebarHeader, SidebarProvider, SidebarTrigger, TapMenu, ThemeProvider, instance as axiosInstance, buildUrlWithCode, canAccessNavPermissionRoute, clearAccessTokenCache, clearOrganizationId, decodeJWT, filterNavigationModulesByPermission, filterNavigationToolsByPermission, getAccessToken, getActiveModule, getActiveSubModule, getAxiosErrorMessage, getOrganizationId, getQueryClientConfig, getRequiredChildGroups, isLikelyTransientNetworkError, isPathActive, normalizePathnameForPermissionRules, resolveUiRoutePermission, runGlobalRequestPrecheck, runWithTransientRetry, setOrganizationId, toApiError, unwrapApiResponse, updateOrganizationId, useClearOrgOnUserChange, useFeatureFlag, useFeatureFlags, useInvalidateQueriesOnOrgChange, useKeycloakRoles, useOrganizationIdState, usePermissionRules, useSessionRefresh, useSidebar, useSidebarToggle };
1861
+ export { AccessDenied, ApiError, AppBreadcrumb, AppLayoutShell, AppSidebar, FeatureFlagsProvider, HeaderBackground, NavMain, NavTools, NavUser, OrganizationSwitcher, PermissionGateShell, PermissionProvider, PermissionRulesProvider, ReactQueryProvider, SessionBackedProfileCacheSeeder, SessionRefreshTrigger, SidebarContent, SidebarFooter, SidebarHeader, SidebarProvider, SidebarTrigger, TapMenu, ThemeProvider, instance as axiosInstance, buildUrlWithCode, canAccessNavPermissionRoute, clearAccessTokenCache, clearOrganizationId, decodeJWT, filterNavigationModulesByPermission, filterNavigationToolsByPermission, getAccessToken, getActiveModule, getActiveSubModule, getAxiosErrorMessage, getOrganizationId, getQueryClientConfig, getRequiredChildGroups, isLikelyTransientNetworkError, isPathActive, normalizePathnameForPermissionRules, resolveUiRoutePermission, runGlobalRequestPrecheck, runWithTransientRetry, setOrganizationId, toApiError, unwrapApiResponse, updateOrganizationId, useClearOrgOnUserChange, useFeatureFlag, useFeatureFlags, useInvalidateQueriesOnOrgChange, useKeycloakRoles, useOrganizationIdState, usePermissionRules, useSessionRefresh, useSidebar, useSidebarToggle };
1841
1862
  //# sourceMappingURL=index.esm.js.map