@eintrek/erp-shell 0.1.45 → 0.1.46

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/index.d.ts CHANGED
@@ -21,6 +21,9 @@ export { OrganizationSwitcher, type OrganizationSwitcherProps, type Organization
21
21
  export { SidebarHeader } from "./navigation/sidebar-header";
22
22
  export { AppSidebar, type AppSidebarProps } from "./navigation/app-sidebar";
23
23
  export { AppLayoutShell, type AppLayoutShellProps, type AppLayoutShellOrganization, } from "./navigation/app-layout-shell";
24
+ export { type ShellChrome, WORKSPACE_HEADER_VARS } from "./navigation/chrome";
25
+ export { WorkspaceBrand, type WorkspaceBrandProps } from "./navigation/workspace-brand";
26
+ export { SidebarHelpCard, SidebarHelpLink, type SidebarHelpCardProps, type SidebarHelpLinkProps, } from "./navigation/sidebar-help-card";
24
27
  export { AccessDenied, type AccessDeniedProps } from "./auth/access-denied";
25
28
  export { EmployeeRequiredGate, EMPLOYEE_RECORD_REQUIRED, EMPLOYEE_RECORD_REQUIRED_EVENT, EMPLOYEE_RECORD_OK_EVENT, } from "./auth/employee-required-gate";
26
29
  export { PermissionGateShell, type PermissionGateShellProps, type PermissionGateSkeletonVariant, } from "./auth/permission-gate-shell";
package/dist/index.esm.js CHANGED
@@ -4,8 +4,8 @@ import React__default, { useRef, useEffect, useMemo, useState, useCallback, crea
4
4
  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
- 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, Palette, ChevronUp, Sun, Moon, Monitor, Globe, User, LayoutGrid, ExternalLink, Loader2, LogOut, Calculator, Warehouse, FolderKanban, Users, ChevronDown, ChevronsUpDown, Plus, Ban, Home, Headset, ContactRound, UserPlus } from 'lucide-react';
7
+ import { cn, Button, Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbSeparator, BreadcrumbPage, Avatar, AvatarImage, AvatarFallback, Collapsible, CollapsibleTrigger, CollapsibleContent, Badge, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuItem, DropdownMenuSeparator, Skeleton, Card, CardHeader, CardContent } from '@eintrek/erp-theme';
8
+ import { PanelLeftClose, Menu, ChevronRight, Building2, Palette, ChevronUp, Sun, Moon, Monitor, Globe, User, LayoutGrid, ExternalLink, Loader2, LogOut, ChevronsUpDown, Calculator, Warehouse, FolderKanban, Users, ChevronDown, Plus, ArrowRight, Compass, ArrowUpRight, Ban, Home, Headset, ContactRound, UserPlus } 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';
@@ -776,14 +776,14 @@ function AppBreadcrumb({ labels = {}, homeLabel = "แดชบอร์ด", ro
776
776
  if (segments.length === 0) {
777
777
  return jsx("h1", { className: "text-xl font-semibold", children: homeLabel });
778
778
  }
779
- return (jsx(Breadcrumb, { children: jsxs(BreadcrumbList, { children: [jsx(BreadcrumbItem, { children: jsx(BreadcrumbLink, { asChild: true, children: jsx(Link, { href: "/", className: "font-thai text-[14px] font-normal text-white/80 hover:text-white transition-colors", children: rootLabel }) }) }), segments.map((segment, index) => {
779
+ return (jsx(Breadcrumb, { children: jsxs(BreadcrumbList, { children: [jsx(BreadcrumbItem, { children: jsx(BreadcrumbLink, { asChild: true, children: jsx(Link, { href: "/", className: "font-thai text-[14px] font-normal text-[var(--header-fg-muted,rgb(255_255_255/0.8))] hover:text-[var(--header-fg,white)] transition-colors", children: rootLabel }) }) }), segments.map((segment, index) => {
780
780
  const href = "/" + segments.slice(0, index + 1).join("/");
781
781
  const isLast = index === segments.length - 1;
782
782
  // A record id is not a place. Falling back to the raw segment put a
783
783
  // 36-character uuid in the trail, which pushed everything before it
784
784
  // off a phone and told the reader nothing they could use.
785
785
  const label = labels[segment] ?? (isRecordID(segment) ? "รายละเอียด" : segment);
786
- return (jsxs(Fragment, { children: [jsx(BreadcrumbSeparator, { className: "text-white/80", children: jsx(ChevronRight, { strokeWidth: 2, className: "h-4 w-4" }) }), jsx(BreadcrumbItem, { children: isLast ? (jsx(BreadcrumbPage, { className: "font-thai text-[14px] font-normal text-white/95 leading-none", children: decodeURIComponent(label) })) : (jsx(BreadcrumbLink, { asChild: true, children: jsx(Link, { href: href, className: "font-thai text-[14px] font-normal text-white/80 hover:text-white transition-colors leading-none", children: decodeURIComponent(label) }) })) })] }, `${segment}-${index}`));
786
+ return (jsxs(Fragment, { children: [jsx(BreadcrumbSeparator, { className: "text-[var(--header-fg-muted,rgb(255_255_255/0.8))]", children: jsx(ChevronRight, { strokeWidth: 2, className: "h-4 w-4" }) }), jsx(BreadcrumbItem, { children: isLast ? (jsx(BreadcrumbPage, { className: "font-thai text-[14px] font-normal text-[var(--header-fg,rgb(255_255_255/0.95))] leading-none", children: decodeURIComponent(label) })) : (jsx(BreadcrumbLink, { asChild: true, children: jsx(Link, { href: href, className: "font-thai text-[14px] font-normal text-[var(--header-fg-muted,rgb(255_255_255/0.8))] hover:text-[var(--header-fg,white)] transition-colors leading-none", children: decodeURIComponent(label) }) })) })] }, `${segment}-${index}`));
787
787
  })] }) }));
788
788
  }
789
789
  /**
@@ -801,7 +801,7 @@ function isRecordID(segment) {
801
801
  function isExternalHref(href) {
802
802
  return /^https?:\/\//i.test(href) || href.startsWith("//");
803
803
  }
804
- function NavUser({ organization, avatarSrc, profileHref, selectOrganizationHref = "/select-organization", signInRedirectHref = "/auth/signin", systemLinks, showSelectOrganization = true, showLanguageSwitch = true, } = {}) {
804
+ function NavUser({ organization, avatarSrc, profileHref, selectOrganizationHref = "/select-organization", signInRedirectHref = "/auth/signin", systemLinks, showSelectOrganization = true, showLanguageSwitch = true, variant = "brand", } = {}) {
805
805
  const { data: session, status } = useSession();
806
806
  const router = useRouter();
807
807
  const { theme, setTheme } = useTheme();
@@ -924,6 +924,10 @@ function NavUser({ organization, avatarSrc, profileHref, selectOrganizationHref
924
924
  setShowThemeMenu(false);
925
925
  setShowLanguageMenu(false);
926
926
  }, 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: [jsx(LayoutGrid, { className: "h-4 w-4" }), jsx("span", { className: "flex-1 text-left", children: "\u0E41\u0E2D\u0E1B\u0E1E\u0E25\u0E34\u0E40\u0E04\u0E0A\u0E31\u0E19" }), jsx(ChevronUp, { className: `h-4 w-4 transition-transform ${showAppsMenu ? "rotate-180" : ""}` })] }), showAppsMenu && (jsx("div", { className: "mt-1 ml-4 space-y-1", children: visibleSystemLinks.map(link => (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 ?? jsx(ExternalLink, { className: "h-4 w-4" }), jsx("span", { className: "flex-1 text-left", children: link.label }), link.active && (jsx("div", { className: "h-2 w-2 rounded-full bg-primary" }))] }, `${link.label}:${link.href}`))) }))] })), 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 ? (jsxs(Fragment$1, { children: [jsx(Loader2, { className: "h-4 w-4 animate-spin" }), jsx("span", { className: "flex-1 text-left", children: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E2D\u0E2D\u0E01\u0E08\u0E32\u0E01\u0E23\u0E30\u0E1A\u0E1A..." })] })) : (jsxs(Fragment$1, { children: [jsx(LogOut, { className: "h-4 w-4" }), jsx("span", { className: "flex-1 text-left", children: "\u0E2D\u0E2D\u0E01\u0E08\u0E32\u0E01\u0E23\u0E30\u0E1A\u0E1A" })] })) })] })] })) : null;
927
+ if (variant === "workspace") {
928
+ return (jsx("div", { className: "mt-3.5 border-t border-sidebar-border pt-3", children: jsxs("div", { className: "relative", children: [jsxs("button", { ref: buttonRef, onClick: () => setIsOpen(!isOpen), "aria-expanded": isOpen, className: "flex w-full items-center gap-[11px] rounded-lg px-3 py-1.5 text-left transition-colors hover:bg-sidebar-accent/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sidebar-ring", children: [jsxs(Avatar, { className: "size-[34px] shrink-0 rounded-full border-[3px] border-sidebar-accent", children: [user.avatar ? (jsx(AvatarImage, { src: user.avatar, alt: user.name })) : null, jsx(AvatarFallback, { className: "rounded-full bg-sidebar-accent text-sm font-medium text-sidebar-accent-foreground", children: getInitials(user.name) })] }), jsxs("span", { className: "grid min-w-0 flex-1 leading-tight", children: [jsx("strong", { className: "truncate text-[13px] font-semibold", title: user.name, children: user.name }), jsx("small", { className: "truncate text-[11px] text-muted-foreground", title: user.email, children: user.email })] }), jsx(ChevronsUpDown, { className: "size-3.5 shrink-0 text-muted-foreground" })] }), typeof window !== "undefined" &&
929
+ createPortal(dropdownContent, document.body)] }) }));
930
+ }
927
931
  return (jsx("div", { className: "p-2", children: jsxs("div", { className: "relative", children: [jsxs("button", { ref: buttonRef, onClick: () => setIsOpen(!isOpen), className: "flex w-full items-center gap-2 p-2 rounded-md transition-colors", children: [jsxs(Avatar, { className: "h-8 w-8 rounded-lg", children: [user.avatar ? (jsx(AvatarImage, { src: user.avatar, alt: user.name })) : null, jsx(AvatarFallback, { className: "rounded-lg bg-primary text-primary-foreground w-8 h-8", children: getInitials(user.name) })] }), jsxs("div", { className: "grid flex-1 text-left text-sm leading-tight", children: [jsx("span", { className: "truncate font-medium", title: user.name, children: user.name }), jsx("span", { className: "truncate text-xs text-sidebar-foreground/70", title: user.email, children: user.email })] }), jsx(ChevronUp, { className: `ml-auto size-4 transition-transform ${isOpen ? "rotate-180" : ""}` })] }), typeof window !== "undefined" &&
928
932
  createPortal(dropdownContent, document.body)] }) }));
929
933
  }
@@ -1055,7 +1059,15 @@ function isPathActive(pathname, url) {
1055
1059
  return false;
1056
1060
  }
1057
1061
 
1058
- function NavMain({ items, orgCode, closeOnNavigation = false, approvalCounts, }) {
1062
+ /** "/O1/" and "/O1" are the same page; a query string never is. */
1063
+ function samePage(pathname, url) {
1064
+ if (url.includes("?"))
1065
+ return false;
1066
+ const strip = (p) => (p.length > 1 ? p.replace(/\/+$/, "") : p);
1067
+ return strip(pathname) === strip(url);
1068
+ }
1069
+ function NavMain({ items, orgCode, closeOnNavigation = false, approvalCounts, chrome = "brand", }) {
1070
+ const workspace = chrome === "workspace";
1059
1071
  const params = useParams();
1060
1072
  const pathname = usePathname();
1061
1073
  const { setOpen } = useSidebar();
@@ -1064,7 +1076,11 @@ function NavMain({ items, orgCode, closeOnNavigation = false, approvalCounts, })
1064
1076
  setOpen(false);
1065
1077
  };
1066
1078
  const code = orgCode ?? params?.code;
1067
- return (jsx(SidebarGroup, { "data-sidebar": true, children: jsx(SidebarMenu, { children: items.map(item => {
1079
+ return (jsx(SidebarGroup, { "data-sidebar": true, className: workspace ? "p-0" : undefined, children: jsx(SidebarMenu, { className: workspace ? "gap-[3px]" : undefined, children: items.map((item, index) => {
1080
+ const groupHeading = workspace &&
1081
+ item.group &&
1082
+ item.group !== items[index - 1]?.group ? (jsx("li", { className: "list-none px-[15px] pb-2.5 text-xs text-muted-foreground" +
1083
+ (index > 0 ? " pt-8" : ""), children: item.group }, `group-${item.id}`)) : null;
1068
1084
  const itemUrl = buildUrlWithCode(code, item.url);
1069
1085
  // Most specific (longest) matching sub-item URL so only one link is active.
1070
1086
  const activeSubItemUrl = item.subModules?.reduce((best, subItem) => {
@@ -1085,30 +1101,43 @@ function NavMain({ items, orgCode, closeOnNavigation = false, approvalCounts, })
1085
1101
  isPathActive(pathname, subPage.url)));
1086
1102
  // Parent only active when path matches its URL exactly AND no child
1087
1103
  // is active (so parent+child don't both light up).
1088
- const isItemActive = pathname === itemUrl && !hasActiveSubItem;
1089
- item.icon;
1090
- return (jsx(Collapsible, { asChild: true, defaultOpen: isItemActive || hasActiveSubItem, className: "group/collapsible", children: jsxs(SidebarMenuItem, { children: [jsx(CollapsibleTrigger, { asChild: true, children: jsx(SidebarMenuButton, { asChild: !item.subModules || item.subModules.length === 0, isActive: isItemActive, className: " data-[state=open]:text-sidebar-accent-foreground", children: item.subModules && item.subModules.length > 0 ? (jsxs(Fragment$1, { children: [jsx("span", { id: item.id, children: item.titleTh }), jsx(ChevronRight, { className: "ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" })] })) : (jsx(Link, { href: itemUrl, prefetch: false, className: "flex items-center gap-2 w-full min-w-0 flex-1", onClick: handleNavLinkClick, children: jsx("span", { id: item.id, className: "flex-1 truncate", title: item.titleTh, children: item.titleTh }) })) }) }), item.subModules && item.subModules.length > 0 && (jsx(CollapsibleContent, { children: jsx(SidebarMenuSub, { children: item.subModules.map(subItem => {
1091
- const subItemUrl = buildUrlWithCode(code, subItem.url);
1092
- const isSubItemActive = subItemUrl === activeSubItemUrl;
1093
- const SubIcon = subItem.icon;
1094
- const approvalCount = approvalCounts && subItem.approvalCountKey
1095
- ? approvalCounts[subItem.approvalCountKey] ?? 0
1096
- : 0;
1097
- if (subItem.isCollapsible &&
1098
- subItem.subPages &&
1099
- subItem.subPages.length > 0) {
1100
- const hasActiveSubPage = subItem.subPages.some(subPage => isPathActive(pathname, buildUrlWithCode(code, subPage.url)));
1101
- return (jsx(Collapsible, { asChild: true, defaultOpen: isSubItemActive || hasActiveSubPage, className: "group/sub-collapsible", children: jsxs(SidebarMenuSubItem, { children: [jsx(CollapsibleTrigger, { asChild: true, children: jsxs(SidebarMenuSubButton, { className: "w-full", isActive: isSubItemActive || hasActiveSubPage, children: [SubIcon && (jsx(SubIcon, { className: "sidebar-submodule-icon flex-shrink-0" })), jsx("span", { className: "flex-1 truncate", title: subItem.titleTh, children: subItem.titleTh }), jsx(ChevronDown, { className: "ml-auto size-4 transition-transform duration-200 group-data-[state=open]/sub-collapsible:rotate-180" })] }) }), jsx(CollapsibleContent, { children: jsx(SidebarMenuSub, { className: "ml-4", children: subItem.subPages.map(subPage => {
1102
- const subPageUrl = buildUrlWithCode(code, subPage.url);
1103
- const isSubPageActive = isPathActive(pathname, subPageUrl);
1104
- const SubPageIcon = subPage.icon;
1105
- return (jsx(SidebarMenuSubItem, { children: jsx(SidebarMenuSubButton, { asChild: true, className: `font-thai w-full text-sm ${isSubPageActive
1106
- ? "text-sidebar-accent-foreground font-medium"
1107
- : "text-sidebar-foreground"}`, isActive: false, children: jsxs(Link, { href: subPageUrl, prefetch: false, className: "w-full flex items-center", onClick: handleNavLinkClick, children: [SubPageIcon && (jsx(SubPageIcon, { className: "sidebar-subpage-icon flex-shrink-0" })), jsx("span", { className: "flex-1 truncate", title: subPage.titleTh, children: subPage.titleTh })] }) }) }, subPage.id));
1108
- }) }) })] }) }, subItem.id));
1109
- }
1110
- return (jsx(SidebarMenuSubItem, { children: jsx(SidebarMenuSubButton, { asChild: true, className: "w-full", isActive: isSubItemActive, children: jsxs(Link, { href: subItemUrl, prefetch: false, className: "w-full flex items-center", onClick: handleNavLinkClick, children: [SubIcon && (jsx(SubIcon, { className: "sidebar-submodule-icon flex-shrink-0" })), jsx("span", { className: "flex-1 truncate", title: subItem.titleTh, children: subItem.titleTh }), approvalCount > 0 && (jsx(Badge, { variant: "destructive", className: "ml-auto h-5 min-w-5 px-1.5 text-xs font-medium tabular-nums", children: approvalCount > 99 ? "99+" : approvalCount }))] }) }) }, subItem.id));
1111
- }) }) }))] }) }, item.id));
1104
+ const isItemActive = (workspace ? samePage(pathname, itemUrl) : pathname === itemUrl) &&
1105
+ !hasActiveSubItem;
1106
+ const IconComp = item.icon;
1107
+ const workspaceButton = workspace
1108
+ ? "h-auto gap-3 rounded-[10px] px-3.5 py-[11px] text-sm " +
1109
+ (isItemActive
1110
+ ? "bg-sidebar-accent font-bold text-sidebar-accent-foreground"
1111
+ : "font-medium text-sidebar-foreground/70")
1112
+ : "";
1113
+ const icon = workspace && IconComp ? (jsx(IconComp, { className: "size-[18px] shrink-0" })) : null;
1114
+ const badge = workspace && item.badge !== undefined && item.badge !== "" ? (jsx("span", { className: "ml-auto min-w-5 rounded-[5px] bg-primary/15 px-1 text-center text-[10px] leading-5 font-bold tabular-nums text-primary", children: item.badge })) : null;
1115
+ return (jsxs(React.Fragment, { children: [groupHeading, jsx(Collapsible, { asChild: true, defaultOpen: isItemActive || hasActiveSubItem, className: "group/collapsible", children: jsxs(SidebarMenuItem, { children: [jsx(CollapsibleTrigger, { asChild: true, children: jsx(SidebarMenuButton, { asChild: !item.subModules || item.subModules.length === 0, isActive: isItemActive, className: "data-[state=open]:text-sidebar-accent-foreground " +
1116
+ workspaceButton, children: item.subModules && item.subModules.length > 0 ? (jsxs(Fragment$1, { children: [icon, jsx("span", { id: item.id, children: item.titleTh }), jsx(ChevronRight, { className: "ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" })] })) : (jsxs(Link, { href: itemUrl, prefetch: false, className: "flex items-center w-full min-w-0 flex-1 " +
1117
+ (workspace ? "gap-3" : "gap-2"), onClick: handleNavLinkClick, "aria-current": isItemActive ? "page" : undefined, children: [icon, jsx("span", { id: item.id, className: "flex-1 truncate", title: item.titleTh, children: item.titleTh }), badge] })) }) }), item.subModules && item.subModules.length > 0 && (jsx(CollapsibleContent, { children: jsx(SidebarMenuSub, { className: workspace
1118
+ ? "mx-0 ml-[23px] mt-1 border-l border-sidebar-border pl-2.5 [&_a]:text-[13px] [&_svg]:!size-4"
1119
+ : undefined, children: item.subModules.map(subItem => {
1120
+ const subItemUrl = buildUrlWithCode(code, subItem.url);
1121
+ const isSubItemActive = subItemUrl === activeSubItemUrl;
1122
+ const SubIcon = subItem.icon;
1123
+ const approvalCount = approvalCounts && subItem.approvalCountKey
1124
+ ? approvalCounts[subItem.approvalCountKey] ?? 0
1125
+ : 0;
1126
+ if (subItem.isCollapsible &&
1127
+ subItem.subPages &&
1128
+ subItem.subPages.length > 0) {
1129
+ const hasActiveSubPage = subItem.subPages.some(subPage => isPathActive(pathname, buildUrlWithCode(code, subPage.url)));
1130
+ return (jsx(Collapsible, { asChild: true, defaultOpen: isSubItemActive || hasActiveSubPage, className: "group/sub-collapsible", children: jsxs(SidebarMenuSubItem, { children: [jsx(CollapsibleTrigger, { asChild: true, children: jsxs(SidebarMenuSubButton, { className: "w-full", isActive: isSubItemActive || hasActiveSubPage, children: [SubIcon && (jsx(SubIcon, { className: "sidebar-submodule-icon flex-shrink-0" })), jsx("span", { className: "flex-1 truncate", title: subItem.titleTh, children: subItem.titleTh }), jsx(ChevronDown, { className: "ml-auto size-4 transition-transform duration-200 group-data-[state=open]/sub-collapsible:rotate-180" })] }) }), jsx(CollapsibleContent, { children: jsx(SidebarMenuSub, { className: "ml-4", children: subItem.subPages.map(subPage => {
1131
+ const subPageUrl = buildUrlWithCode(code, subPage.url);
1132
+ const isSubPageActive = isPathActive(pathname, subPageUrl);
1133
+ const SubPageIcon = subPage.icon;
1134
+ return (jsx(SidebarMenuSubItem, { children: jsx(SidebarMenuSubButton, { asChild: true, className: `font-thai w-full text-sm ${isSubPageActive
1135
+ ? "text-sidebar-accent-foreground font-medium"
1136
+ : "text-sidebar-foreground"}`, isActive: false, children: jsxs(Link, { href: subPageUrl, prefetch: false, className: "w-full flex items-center", onClick: handleNavLinkClick, children: [SubPageIcon && (jsx(SubPageIcon, { className: "sidebar-subpage-icon flex-shrink-0" })), jsx("span", { className: "flex-1 truncate", title: subPage.titleTh, children: subPage.titleTh })] }) }) }, subPage.id));
1137
+ }) }) })] }) }, subItem.id));
1138
+ }
1139
+ return (jsx(SidebarMenuSubItem, { children: jsx(SidebarMenuSubButton, { asChild: true, className: "w-full", isActive: isSubItemActive, children: jsxs(Link, { href: subItemUrl, prefetch: false, className: "w-full flex items-center", onClick: handleNavLinkClick, children: [SubIcon && (jsx(SubIcon, { className: "sidebar-submodule-icon flex-shrink-0" })), jsx("span", { className: "flex-1 truncate", title: subItem.titleTh, children: subItem.titleTh }), approvalCount > 0 && (jsx(Badge, { variant: "destructive", className: "ml-auto h-5 min-w-5 px-1.5 text-xs font-medium tabular-nums", children: approvalCount > 99 ? "99+" : approvalCount }))] }) }) }, subItem.id));
1140
+ }) }) }))] }) })] }, item.id));
1112
1141
  }) }) }));
1113
1142
  }
1114
1143
 
@@ -1116,14 +1145,16 @@ function OrgMark({ logoUrl, size = "md", }) {
1116
1145
  const [failed, setFailed] = useState(false);
1117
1146
  const box = size === "sm"
1118
1147
  ? "flex size-6 items-center justify-center rounded-md border overflow-hidden bg-background"
1119
- : "flex aspect-square size-8 shrink-0 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground overflow-hidden";
1148
+ : size === "header"
1149
+ ? "flex aspect-square size-8 shrink-0 items-center justify-center rounded-[9px] border border-[color:color-mix(in_oklab,var(--header-fg,currentColor)_28%,transparent)] bg-[color-mix(in_oklab,var(--header-fg,currentColor)_10%,transparent)] text-[var(--header-fg,currentColor)] overflow-hidden max-sm:size-7"
1150
+ : "flex aspect-square size-8 shrink-0 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground overflow-hidden";
1120
1151
  const icon = size === "sm" ? "size-3.5 shrink-0" : "size-4";
1121
1152
  if (logoUrl && !failed) {
1122
1153
  return (jsx("div", { className: cn(box, size === "md" && "bg-white"), children: jsx("img", { src: logoUrl, alt: "", className: "size-full object-contain p-0.5", onError: () => setFailed(true) }) }));
1123
1154
  }
1124
1155
  return (jsx("div", { className: box, children: jsx(Building2, { className: icon }) }));
1125
1156
  }
1126
- function OrganizationSwitcher({ activeOrganization, isContextLoading = false, organizations, isLoadingOrgs = false, setOrganizationCookie, registerOrganizationHref = "/register-organization", className, }) {
1157
+ function OrganizationSwitcher({ activeOrganization, isContextLoading = false, organizations, isLoadingOrgs = false, setOrganizationCookie, registerOrganizationHref = "/register-organization", placement = "sidebar", className, }) {
1127
1158
  const { isMobile } = useSidebarToggle();
1128
1159
  const router = useRouter();
1129
1160
  const params = useParams();
@@ -1164,6 +1195,14 @@ function OrganizationSwitcher({ activeOrganization, isContextLoading = false, or
1164
1195
  });
1165
1196
  };
1166
1197
  const isLoading = isLoadingOrgs || isContextLoading;
1198
+ if (placement === "header") {
1199
+ const face = (interactive) => (jsxs(Fragment$1, { children: [jsx(OrgMark, { logoUrl: activeOrganization?.logoUrl, size: "header" }), jsxs("span", { className: "min-w-0 text-left", children: [jsx("small", { className: "block text-[10px] leading-tight text-[var(--header-fg-muted,currentColor)] max-sm:text-[9px]", children: "\u0E2D\u0E07\u0E04\u0E4C\u0E01\u0E23\u0E17\u0E35\u0E48\u0E01\u0E33\u0E25\u0E31\u0E07\u0E43\u0E0A\u0E49\u0E07\u0E32\u0E19" }), isLoading ? (jsx(Skeleton, { className: "mt-1 h-3.5 w-28" })) : (jsx("strong", { className: "block max-w-[220px] truncate text-xs font-semibold text-[var(--header-fg,currentColor)] max-sm:max-w-[140px] max-sm:text-[11px]", children: activeOrganization?.name || "เลือกองค์กร" }))] }), interactive ? (isPending ? (jsx(Loader2, { className: "ml-1 size-3.5 shrink-0 animate-spin text-[var(--header-fg-muted,currentColor)]" })) : (jsx(ChevronsUpDown, { className: "ml-1 size-3.5 shrink-0 text-[var(--header-fg-muted,currentColor)]" }))) : null] }));
1200
+ const faceClass = cn("flex items-center gap-2.5 rounded-lg py-1.5 max-sm:gap-2", className);
1201
+ if (isLoading || organizations.length <= 1) {
1202
+ return jsx("div", { className: faceClass, children: face(false) });
1203
+ }
1204
+ return (jsxs(DropdownMenu, { children: [jsx(DropdownMenuTrigger, { className: cn(faceClass, "px-1.5 outline-none transition-colors hover:bg-[var(--header-hover,var(--accent))] focus-visible:ring-2 focus-visible:ring-[var(--header-fg-muted,var(--ring))] data-[state=open]:bg-[var(--header-hover,var(--accent))]"), "aria-label": "\u0E40\u0E1B\u0E25\u0E35\u0E48\u0E22\u0E19\u0E2D\u0E07\u0E04\u0E4C\u0E01\u0E23", children: face(true) }), jsxs(DropdownMenuContent, { className: "min-w-64 rounded-lg", align: "end", side: "bottom", sideOffset: 6, children: [jsx(DropdownMenuLabel, { className: "text-xs text-muted-foreground", children: "\u0E2D\u0E07\u0E04\u0E4C\u0E01\u0E23" }), organizations.map(org => (jsxs(DropdownMenuItem, { onClick: () => handleOrganizationChange(org), disabled: isPending, className: cn("gap-2 p-2 cursor-pointer", String(activeOrganization?.id) === String(org.id) && "bg-accent"), children: [jsx(OrgMark, { logoUrl: org.logoUrl, size: "sm" }), jsx("span", { className: "truncate", children: org.name })] }, org.id))), jsx(DropdownMenuSeparator, {}), jsxs(DropdownMenuItem, { className: "gap-2 p-2 cursor-pointer", onClick: () => router.push(registerOrganizationHref), children: [jsx("div", { className: "flex size-6 items-center justify-center rounded-md border bg-transparent", children: jsx(Plus, { className: "size-4" }) }), jsx("span", { className: "text-muted-foreground font-medium", children: "\u0E40\u0E1E\u0E34\u0E48\u0E21\u0E2D\u0E07\u0E04\u0E4C\u0E01\u0E23" })] })] })] }));
1205
+ }
1167
1206
  if (isLoading) {
1168
1207
  return (jsx(SidebarMenu, { className: className, children: jsx(SidebarMenuItem, { children: jsxs("div", { className: "flex items-center gap-2 p-2", children: [jsx(Skeleton, { className: "size-8 rounded-lg" }), jsx(Skeleton, { className: "h-4 w-24" })] }) }) }));
1169
1208
  }
@@ -1178,7 +1217,7 @@ function SidebarHeader({ children, className }) {
1178
1217
  return jsx("div", { className: cn("px-2 py-3", className), children: children });
1179
1218
  }
1180
1219
 
1181
- function AppSidebar({ modules, tools, activeModule, orgCode, approvalCounts, closeOnNavigation = false, header, footer, className, }) {
1220
+ function AppSidebar({ modules, tools, activeModule, orgCode, approvalCounts, closeOnNavigation = false, header, footer, bottom, chrome = "brand", className, }) {
1182
1221
  const { open } = useSidebar();
1183
1222
  const modulesWithActiveState = React.useMemo(() => modules.map(m => ({
1184
1223
  ...m,
@@ -1186,9 +1225,27 @@ function AppSidebar({ modules, tools, activeModule, orgCode, approvalCounts, clo
1186
1225
  })), [modules, activeModule]);
1187
1226
  if (!open)
1188
1227
  return null;
1228
+ if (chrome === "workspace") {
1229
+ return (jsxs("div", { className: cn("flex h-screen w-[244px] shrink-0 flex-col overflow-hidden border-r border-sidebar-border bg-sidebar text-sidebar-foreground",
1230
+ // As a drawer (≤1024px) it floats over the page and needs the lift.
1231
+ "max-[1024px]:shadow-2xl", className), children: [header ? jsx("div", { className: "shrink-0 px-[18px] pt-8 pb-6", children: header }) : null, jsxs(SidebarContent, { className: "flex-1 overflow-y-auto px-[18px]", children: [jsx(NavMain, { items: modulesWithActiveState, orgCode: orgCode, approvalCounts: approvalCounts, closeOnNavigation: closeOnNavigation, chrome: "workspace" }), jsx(NavTools, { tools: tools, closeOnNavigation: closeOnNavigation })] }), bottom ? jsx("div", { className: "shrink-0 px-[18px] pt-6", children: bottom }) : null, jsx("div", { className: "shrink-0 px-[18px] pb-3", children: footer })] }));
1232
+ }
1189
1233
  return (jsxs("div", { className: cn("bg-sidebar shrink-0 flex flex-col overflow-hidden shadow-2xl h-screen w-[var(--sidebar-width-mobile)]", "!w-76 xl:rounded-b-4xl xl:rounded-tl-4xl xl:m-5 xl:h-[calc(100vh-2.5rem)]", className), children: [jsx(SidebarHeader, { children: header }), jsxs(SidebarContent, { className: "pl-2 pr-2 flex-1 overflow-y-auto", children: [jsx(NavMain, { items: modulesWithActiveState, orgCode: orgCode, approvalCounts: approvalCounts, closeOnNavigation: closeOnNavigation }), jsx(NavTools, { tools: tools, closeOnNavigation: closeOnNavigation })] }), jsx(SidebarFooter, { className: "pl-2 pr-2", children: footer })] }));
1190
1234
  }
1191
1235
 
1236
+ /**
1237
+ * Header ink as CSS variables. Header-borne pieces — breadcrumb, sidebar
1238
+ * trigger, the org switcher, the app's own page title — read these, with
1239
+ * white as the fallback, so the brand chrome looks exactly as before.
1240
+ */
1241
+ const WORKSPACE_HEADER_VARS = {
1242
+ "--header-fg": "#fff",
1243
+ "--header-fg-muted": "rgb(255 255 255 / 0.78)",
1244
+ "--header-hover": "rgb(255 255 255 / 0.12)",
1245
+ "--header-title-px": "0px",
1246
+ "--header-title-py": "0px",
1247
+ };
1248
+
1192
1249
  /**
1193
1250
  * Top-level app chrome: sticky header, sidebar slot, content gutter and the
1194
1251
  * session/org-context plumbing that both Accounting and HR need. Apps wrap
@@ -1201,7 +1258,7 @@ function AppSidebar({ modules, tools, activeModule, orgCode, approvalCounts, clo
1201
1258
  * an erp-shell bump — domain components must NOT add their own root padding
1202
1259
  * or content gets indented twice.
1203
1260
  */
1204
- function AppLayoutShell({ children, sidebar, loadingComponent, breadcrumb, permissionProvider, organizationId, setOrganizationId, organizations, signInUrl, selectOrganizationUrl, }) {
1261
+ function AppLayoutShell({ children, sidebar, loadingComponent, breadcrumb, permissionProvider, organizationId, setOrganizationId, organizations, signInUrl, selectOrganizationUrl, chrome = "brand", headerEnd, mobileBrand, }) {
1205
1262
  const { data: session, status } = useSession();
1206
1263
  const { open, setOpen } = useSidebar();
1207
1264
  const router = useRouter();
@@ -1324,10 +1381,15 @@ function AppLayoutShell({ children, sidebar, loadingComponent, breadcrumb, permi
1324
1381
  }
1325
1382
  // When the sidebar is docked-open it consumes 21.5rem on the left; otherwise
1326
1383
  // the content takes the full width and we add a left gutter inside.
1327
- const contentMargin = open && !isSidebarModalLayout ? "ml-[21.5rem]" : "";
1328
- const needsLeftGutter = !(open && !isSidebarModalLayout);
1384
+ const workspace = chrome === "workspace";
1385
+ const contentMargin = open && !isSidebarModalLayout
1386
+ ? workspace
1387
+ ? "ml-[244px]"
1388
+ : "ml-[21.5rem]"
1389
+ : "";
1390
+ const needsLeftGutter = workspace || !(open && !isSidebarModalLayout);
1329
1391
  const wrap = permissionProvider ?? ((c) => c);
1330
- return (jsxs("div", { className: "flex min-h-screen w-full bg-background", children: [open && (jsx("div", { className: "fixed left-0 top-0 z-50 h-screen", children: sidebar })), open && isSidebarModalLayout && (jsx("button", { type: "button", "aria-label": "\u0E1B\u0E34\u0E14\u0E40\u0E21\u0E19\u0E39", className: "fixed inset-0 z-40 bg-black/50", onClick: () => setOpen(false) })), jsx("div", { className: cx("relative flex min-w-0 flex-1 flex-col transition-all", contentMargin), children: jsxs("main", { className: "flex flex-1 flex-col min-h-screen gap-4", children: [jsxs("div", { className: "sticky top-0 z-30 shrink-0 w-full", children: [jsx(HeaderBackground, {}), jsxs("header", { className: "relative shrink-0 gap-2 py-4 h-auto min-h-[123.5px] w-full z-10", children: [jsxs("div", { className: "flex w-full items-center gap-2 px-3 sm:px-4 lg:px-6", children: [jsx(SidebarTrigger, { className: "shrink-0 text-white hover:bg-white/10" }), jsx("div", { className: "flex-1 min-w-0", children: breadcrumb })] }), jsx("div", { id: "header-title" })] })] }), jsx(Suspense, { fallback: jsx("div", { className: "relative z-10 min-h-[calc(100vh-8rem)]", children: jsx("div", { className: "flex h-full items-center justify-center", children: jsxs("div", { className: "space-y-4 text-center", children: [jsx("div", { className: "mx-auto h-8 w-8 animate-spin rounded-full border-b-2 border-primary" }), jsx("p", { className: "text-sm text-muted-foreground", children: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E42\u0E2B\u0E25\u0E14..." })] }) }) }), children: jsx("div", { className: cx("relative z-10 pr-6", needsLeftGutter && "pl-6"), children: orgContextIsStale ? loadingComponent : wrap(children) }, `org-${contentOrgKey}`) })] }) })] }));
1392
+ return (jsxs("div", { className: "flex min-h-screen w-full bg-background", children: [open && (jsx("div", { className: "fixed left-0 top-0 z-50 h-screen", children: sidebar })), open && isSidebarModalLayout && (jsx("button", { type: "button", "aria-label": "\u0E1B\u0E34\u0E14\u0E40\u0E21\u0E19\u0E39", className: "fixed inset-0 z-40 bg-black/50", onClick: () => setOpen(false) })), jsx("div", { className: cx("relative flex min-w-0 flex-1 flex-col transition-all", contentMargin), children: jsxs("main", { className: cx("flex flex-1 flex-col min-h-screen", workspace ? "gap-0" : "gap-4"), children: [workspace ? (jsxs("div", { className: "sticky top-0 z-30 shrink-0 w-full", style: WORKSPACE_HEADER_VARS, children: [jsx(HeaderBackground, { className: "rounded-none" }), jsxs("header", { className: "relative z-10 flex min-h-[72px] w-full items-center gap-3 px-4 py-3 text-[var(--header-fg)] sm:px-6 lg:min-h-[88px] lg:px-10", children: [jsx(SidebarTrigger, { className: "shrink-0 text-[var(--header-fg-muted)] hover:bg-[var(--header-hover)] hover:text-[var(--header-fg)] [&_svg]:!text-current" }), isSidebarModalLayout && mobileBrand ? (jsx("div", { className: "shrink-0", children: mobileBrand })) : null, jsxs("div", { className: "flex min-w-0 flex-1 flex-col justify-center gap-0.5", children: [jsx("div", { className: "hidden min-w-0 sm:block", children: breadcrumb }), jsx("div", { id: "header-title", className: "min-w-0" })] }), headerEnd ? (jsx("div", { className: "flex shrink-0 items-center gap-3 sm:gap-5", children: headerEnd })) : null] })] })) : (jsxs("div", { className: "sticky top-0 z-30 shrink-0 w-full", children: [jsx(HeaderBackground, {}), jsxs("header", { className: "relative shrink-0 gap-2 py-4 h-auto min-h-[123.5px] w-full z-10", children: [jsxs("div", { className: "flex w-full items-center gap-2 px-3 sm:px-4 lg:px-6", children: [jsx(SidebarTrigger, { className: "shrink-0 text-white hover:bg-white/10" }), jsx("div", { className: "flex-1 min-w-0", children: breadcrumb }), headerEnd ? (jsx("div", { className: "flex shrink-0 items-center gap-3", children: headerEnd })) : null] }), jsx("div", { id: "header-title" })] })] })), jsx(Suspense, { fallback: jsx("div", { className: "relative z-10 min-h-[calc(100vh-8rem)]", children: jsx("div", { className: "flex h-full items-center justify-center", children: jsxs("div", { className: "space-y-4 text-center", children: [jsx("div", { className: "mx-auto h-8 w-8 animate-spin rounded-full border-b-2 border-primary" }), jsx("p", { className: "text-sm text-muted-foreground", children: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E42\u0E2B\u0E25\u0E14..." })] }) }) }), children: jsx("div", { className: cx("relative z-10 pr-6", needsLeftGutter && "pl-6", workspace && "pt-4 sm:pr-6 lg:pt-6 lg:pr-10 lg:pl-10"), children: orgContextIsStale ? loadingComponent : wrap(children) }, `org-${contentOrgKey}`) })] }) })] }));
1331
1393
  }
1332
1394
  /** Local tiny classnames helper — avoids pulling clsx/tailwind-merge into
1333
1395
  * the shell just for the layout file. Falsy values are skipped. */
@@ -1335,6 +1397,37 @@ function cx(...parts) {
1335
1397
  return parts.filter(Boolean).join(" ");
1336
1398
  }
1337
1399
 
1400
+ /**
1401
+ * The product mark at the top of the workspace sidebar: the app icon, the
1402
+ * name, and a spaced caption beneath it.
1403
+ */
1404
+ function WorkspaceBrand({ name, caption, mark, href, compact = false, className, }) {
1405
+ const body = (jsxs(Fragment$1, { children: [jsx("span", { className: cn("grid shrink-0 place-items-center overflow-hidden rounded-[14px] [&>img]:size-full [&>img]:object-cover", compact ? "size-9 rounded-[11px]" : "size-11"), children: mark }), compact ? (jsx("span", { className: "sr-only", children: name })) : (jsxs("span", { className: "min-w-0", children: [jsx("span", { className: "block truncate text-[25px] leading-tight font-extrabold tracking-[-0.05em]", children: name }), caption ? (jsx("small", { className: "-mt-0.5 block truncate text-[9px] font-semibold tracking-[0.29em] text-muted-foreground", children: caption })) : null] }))] }));
1406
+ const cls = cn("flex items-center gap-[11px] rounded-lg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", !compact && "px-4", className);
1407
+ return href ? (jsx(Link, { href: href, className: cls, "aria-label": compact ? name : undefined, children: body })) : (jsx("div", { className: cls, children: body }));
1408
+ }
1409
+
1410
+ function isExternal(href) {
1411
+ return /^https?:\/\//i.test(href);
1412
+ }
1413
+ /**
1414
+ * The card at the foot of the workspace sidebar that points someone who
1415
+ * does not know where to start at the place that will tell them.
1416
+ */
1417
+ function SidebarHelpCard({ title, description, actionLabel, href, icon, className, }) {
1418
+ const external = isExternal(href);
1419
+ const action = (jsxs(Fragment$1, { children: [actionLabel, jsx(ArrowRight, { className: "size-[15px]" })] }));
1420
+ const actionClass = "mt-3 flex w-full items-center justify-between text-xs font-semibold text-primary hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded";
1421
+ return (jsxs("div", { className: cn("mx-1.5 mb-5 rounded-[13px] border border-primary/10 bg-gradient-to-br from-primary/[0.06] to-primary/[0.02] px-3.5 py-4", className), children: [jsx("span", { className: "mb-[7px] block text-primary/70", children: icon ?? jsx(Compass, { className: "size-6", strokeWidth: 1.6 }) }), jsx("strong", { className: "block text-xs font-semibold", children: title }), description ? (jsx("p", { className: "mt-[7px] text-xs leading-[1.7] text-muted-foreground", children: description })) : null, external ? (jsx("a", { href: href, target: "_blank", rel: "noopener noreferrer", className: actionClass, children: action })) : (jsx(Link, { href: href, className: actionClass, children: action }))] }));
1422
+ }
1423
+ /** A quiet row under the help card — the manual, the support chat. */
1424
+ function SidebarHelpLink({ label, href, icon, className }) {
1425
+ const external = isExternal(href);
1426
+ const cls = cn("flex w-full items-center gap-3 rounded-[10px] px-3.5 py-[11px] text-xs font-medium text-sidebar-foreground/70 transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", className);
1427
+ const body = (jsxs(Fragment$1, { children: [icon ? jsx("span", { className: "shrink-0 [&>svg]:size-[18px]", children: icon }) : null, jsx("span", { className: "flex-1 truncate", children: label }), external ? jsx(ArrowUpRight, { className: "size-4 shrink-0" }) : null] }));
1428
+ return external ? (jsx("a", { href: href, target: "_blank", rel: "noopener noreferrer", className: cls, children: body })) : (jsx(Link, { href: href, className: cls, children: body }));
1429
+ }
1430
+
1338
1431
  /**
1339
1432
  * Central "no access" screen shared by every policy check (route-level
1340
1433
  * PermissionProvider and page-level PermissionGateShell). One look everywhere:
@@ -2145,5 +2238,5 @@ const getAxiosErrorMessage = (error) => {
2145
2238
  return String(error);
2146
2239
  };
2147
2240
 
2148
- export { AccessDenied, ApiError, AppBreadcrumb, AppLayoutShell, AppSidebar, EMPLOYEE_RECORD_OK_EVENT, EMPLOYEE_RECORD_REQUIRED, EMPLOYEE_RECORD_REQUIRED_EVENT, EmployeeRequiredGate, FeatureFlagsProvider, HeaderBackground, NavMain, NavTools, NavUser, OrganizationSwitcher, PLAN_FEATURES, PermissionGateShell, PermissionProvider, PermissionRulesProvider, ReactQueryProvider, SessionBackedProfileCacheSeeder, SessionRefreshTrigger, SidebarContent, SidebarFooter, SidebarHeader, SidebarProvider, SidebarTrigger, TapMenu, ThemeProvider, instance as axiosInstance, buildSystemLinks, buildUrlWithCode, canAccessNavPermissionRoute, clearAccessTokenCache, clearOrganizationId, decodeJWT, filterNavigationModulesByPermission, filterNavigationToolsByPermission, getAccessToken, getActiveModule, getActiveSubModule, getAxiosErrorMessage, getOrganizationId, getQueryClientConfig, getRequiredChildGroups, hasPlanFeature, isLikelyTransientNetworkError, isPathActive, normalizePathnameForPermissionRules, resolveUiRoutePermission, runGlobalRequestPrecheck, runWithTransientRetry, setOrganizationId, toApiError, unwrapApiResponse, updateOrganizationId, useClearOrgOnUserChange, useFeatureFlag, useFeatureFlags, useInvalidateQueriesOnOrgChange, useKeycloakRoles, useOrganizationIdState, usePermissionRules, useSessionRefresh, useSidebar, useSidebarToggle };
2241
+ export { AccessDenied, ApiError, AppBreadcrumb, AppLayoutShell, AppSidebar, EMPLOYEE_RECORD_OK_EVENT, EMPLOYEE_RECORD_REQUIRED, EMPLOYEE_RECORD_REQUIRED_EVENT, EmployeeRequiredGate, FeatureFlagsProvider, HeaderBackground, NavMain, NavTools, NavUser, OrganizationSwitcher, PLAN_FEATURES, PermissionGateShell, PermissionProvider, PermissionRulesProvider, ReactQueryProvider, SessionBackedProfileCacheSeeder, SessionRefreshTrigger, SidebarContent, SidebarFooter, SidebarHeader, SidebarHelpCard, SidebarHelpLink, SidebarProvider, SidebarTrigger, TapMenu, ThemeProvider, WORKSPACE_HEADER_VARS, WorkspaceBrand, instance as axiosInstance, buildSystemLinks, buildUrlWithCode, canAccessNavPermissionRoute, clearAccessTokenCache, clearOrganizationId, decodeJWT, filterNavigationModulesByPermission, filterNavigationToolsByPermission, getAccessToken, getActiveModule, getActiveSubModule, getAxiosErrorMessage, getOrganizationId, getQueryClientConfig, getRequiredChildGroups, hasPlanFeature, isLikelyTransientNetworkError, isPathActive, normalizePathnameForPermissionRules, resolveUiRoutePermission, runGlobalRequestPrecheck, runWithTransientRetry, setOrganizationId, toApiError, unwrapApiResponse, updateOrganizationId, useClearOrgOnUserChange, useFeatureFlag, useFeatureFlags, useInvalidateQueriesOnOrgChange, useKeycloakRoles, useOrganizationIdState, usePermissionRules, useSessionRefresh, useSidebar, useSidebarToggle };
2149
2242
  //# sourceMappingURL=index.esm.js.map