@eintrek/erp-shell 0.1.44 → 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.js CHANGED
@@ -795,14 +795,14 @@ function AppBreadcrumb({ labels = {}, homeLabel = "แดชบอร์ด", ro
795
795
  if (segments.length === 0) {
796
796
  return jsxRuntime.jsx("h1", { className: "text-xl font-semibold", children: homeLabel });
797
797
  }
798
- return (jsxRuntime.jsx(erpTheme.Breadcrumb, { children: jsxRuntime.jsxs(erpTheme.BreadcrumbList, { children: [jsxRuntime.jsx(erpTheme.BreadcrumbItem, { children: jsxRuntime.jsx(erpTheme.BreadcrumbLink, { asChild: true, children: jsxRuntime.jsx(Link, { href: "/", className: "font-thai text-[14px] font-normal text-white/80 hover:text-white transition-colors", children: rootLabel }) }) }), segments.map((segment, index) => {
798
+ return (jsxRuntime.jsx(erpTheme.Breadcrumb, { children: jsxRuntime.jsxs(erpTheme.BreadcrumbList, { children: [jsxRuntime.jsx(erpTheme.BreadcrumbItem, { children: jsxRuntime.jsx(erpTheme.BreadcrumbLink, { asChild: true, children: jsxRuntime.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) => {
799
799
  const href = "/" + segments.slice(0, index + 1).join("/");
800
800
  const isLast = index === segments.length - 1;
801
801
  // A record id is not a place. Falling back to the raw segment put a
802
802
  // 36-character uuid in the trail, which pushed everything before it
803
803
  // off a phone and told the reader nothing they could use.
804
804
  const label = labels[segment] ?? (isRecordID(segment) ? "รายละเอียด" : segment);
805
- return (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx(erpTheme.BreadcrumbSeparator, { className: "text-white/80", children: jsxRuntime.jsx(lucideReact.ChevronRight, { strokeWidth: 2, className: "h-4 w-4" }) }), jsxRuntime.jsx(erpTheme.BreadcrumbItem, { children: isLast ? (jsxRuntime.jsx(erpTheme.BreadcrumbPage, { className: "font-thai text-[14px] font-normal text-white/95 leading-none", children: decodeURIComponent(label) })) : (jsxRuntime.jsx(erpTheme.BreadcrumbLink, { asChild: true, children: jsxRuntime.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}`));
805
+ return (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx(erpTheme.BreadcrumbSeparator, { className: "text-[var(--header-fg-muted,rgb(255_255_255/0.8))]", children: jsxRuntime.jsx(lucideReact.ChevronRight, { strokeWidth: 2, className: "h-4 w-4" }) }), jsxRuntime.jsx(erpTheme.BreadcrumbItem, { children: isLast ? (jsxRuntime.jsx(erpTheme.BreadcrumbPage, { className: "font-thai text-[14px] font-normal text-[var(--header-fg,rgb(255_255_255/0.95))] leading-none", children: decodeURIComponent(label) })) : (jsxRuntime.jsx(erpTheme.BreadcrumbLink, { asChild: true, children: jsxRuntime.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}`));
806
806
  })] }) }));
807
807
  }
808
808
  /**
@@ -820,7 +820,7 @@ function isRecordID(segment) {
820
820
  function isExternalHref(href) {
821
821
  return /^https?:\/\//i.test(href) || href.startsWith("//");
822
822
  }
823
- function NavUser({ organization, avatarSrc, profileHref, selectOrganizationHref = "/select-organization", signInRedirectHref = "/auth/signin", systemLinks, showSelectOrganization = true, showLanguageSwitch = true, } = {}) {
823
+ function NavUser({ organization, avatarSrc, profileHref, selectOrganizationHref = "/select-organization", signInRedirectHref = "/auth/signin", systemLinks, showSelectOrganization = true, showLanguageSwitch = true, variant = "brand", } = {}) {
824
824
  const { data: session, status } = react.useSession();
825
825
  const router = navigation.useRouter();
826
826
  const { theme, setTheme } = nextThemes.useTheme();
@@ -943,6 +943,10 @@ function NavUser({ organization, avatarSrc, profileHref, selectOrganizationHref
943
943
  setShowThemeMenu(false);
944
944
  setShowLanguageMenu(false);
945
945
  }, 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;
946
+ if (variant === "workspace") {
947
+ return (jsxRuntime.jsx("div", { className: "mt-3.5 border-t border-sidebar-border pt-3", children: jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.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: [jsxRuntime.jsxs(erpTheme.Avatar, { className: "size-[34px] shrink-0 rounded-full border-[3px] border-sidebar-accent", children: [user.avatar ? (jsxRuntime.jsx(erpTheme.AvatarImage, { src: user.avatar, alt: user.name })) : null, jsxRuntime.jsx(erpTheme.AvatarFallback, { className: "rounded-full bg-sidebar-accent text-sm font-medium text-sidebar-accent-foreground", children: getInitials(user.name) })] }), jsxRuntime.jsxs("span", { className: "grid min-w-0 flex-1 leading-tight", children: [jsxRuntime.jsx("strong", { className: "truncate text-[13px] font-semibold", title: user.name, children: user.name }), jsxRuntime.jsx("small", { className: "truncate text-[11px] text-muted-foreground", title: user.email, children: user.email })] }), jsxRuntime.jsx(lucideReact.ChevronsUpDown, { className: "size-3.5 shrink-0 text-muted-foreground" })] }), typeof window !== "undefined" &&
948
+ reactDom.createPortal(dropdownContent, document.body)] }) }));
949
+ }
946
950
  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" &&
947
951
  reactDom.createPortal(dropdownContent, document.body)] }) }));
948
952
  }
@@ -1074,7 +1078,15 @@ function isPathActive(pathname, url) {
1074
1078
  return false;
1075
1079
  }
1076
1080
 
1077
- function NavMain({ items, orgCode, closeOnNavigation = false, approvalCounts, }) {
1081
+ /** "/O1/" and "/O1" are the same page; a query string never is. */
1082
+ function samePage(pathname, url) {
1083
+ if (url.includes("?"))
1084
+ return false;
1085
+ const strip = (p) => (p.length > 1 ? p.replace(/\/+$/, "") : p);
1086
+ return strip(pathname) === strip(url);
1087
+ }
1088
+ function NavMain({ items, orgCode, closeOnNavigation = false, approvalCounts, chrome = "brand", }) {
1089
+ const workspace = chrome === "workspace";
1078
1090
  const params = navigation.useParams();
1079
1091
  const pathname = navigation.usePathname();
1080
1092
  const { setOpen } = useSidebar();
@@ -1083,7 +1095,11 @@ function NavMain({ items, orgCode, closeOnNavigation = false, approvalCounts, })
1083
1095
  setOpen(false);
1084
1096
  };
1085
1097
  const code = orgCode ?? params?.code;
1086
- return (jsxRuntime.jsx(SidebarGroup, { "data-sidebar": true, children: jsxRuntime.jsx(SidebarMenu, { children: items.map(item => {
1098
+ return (jsxRuntime.jsx(SidebarGroup, { "data-sidebar": true, className: workspace ? "p-0" : undefined, children: jsxRuntime.jsx(SidebarMenu, { className: workspace ? "gap-[3px]" : undefined, children: items.map((item, index) => {
1099
+ const groupHeading = workspace &&
1100
+ item.group &&
1101
+ item.group !== items[index - 1]?.group ? (jsxRuntime.jsx("li", { className: "list-none px-[15px] pb-2.5 text-xs text-muted-foreground" +
1102
+ (index > 0 ? " pt-8" : ""), children: item.group }, `group-${item.id}`)) : null;
1087
1103
  const itemUrl = buildUrlWithCode(code, item.url);
1088
1104
  // Most specific (longest) matching sub-item URL so only one link is active.
1089
1105
  const activeSubItemUrl = item.subModules?.reduce((best, subItem) => {
@@ -1104,30 +1120,43 @@ function NavMain({ items, orgCode, closeOnNavigation = false, approvalCounts, })
1104
1120
  isPathActive(pathname, subPage.url)));
1105
1121
  // Parent only active when path matches its URL exactly AND no child
1106
1122
  // is active (so parent+child don't both light up).
1107
- const isItemActive = pathname === itemUrl && !hasActiveSubItem;
1108
- item.icon;
1109
- return (jsxRuntime.jsx(erpTheme.Collapsible, { asChild: true, defaultOpen: isItemActive || hasActiveSubItem, className: "group/collapsible", children: jsxRuntime.jsxs(SidebarMenuItem, { children: [jsxRuntime.jsx(erpTheme.CollapsibleTrigger, { asChild: true, children: jsxRuntime.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 ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { id: item.id, children: item.titleTh }), jsxRuntime.jsx(lucideReact.ChevronRight, { className: "ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" })] })) : (jsxRuntime.jsx(Link, { href: itemUrl, prefetch: false, className: "flex items-center gap-2 w-full min-w-0 flex-1", onClick: handleNavLinkClick, children: jsxRuntime.jsx("span", { id: item.id, className: "flex-1 truncate", title: item.titleTh, children: item.titleTh }) })) }) }), item.subModules && item.subModules.length > 0 && (jsxRuntime.jsx(erpTheme.CollapsibleContent, { children: jsxRuntime.jsx(SidebarMenuSub, { children: item.subModules.map(subItem => {
1110
- const subItemUrl = buildUrlWithCode(code, subItem.url);
1111
- const isSubItemActive = subItemUrl === activeSubItemUrl;
1112
- const SubIcon = subItem.icon;
1113
- const approvalCount = approvalCounts && subItem.approvalCountKey
1114
- ? approvalCounts[subItem.approvalCountKey] ?? 0
1115
- : 0;
1116
- if (subItem.isCollapsible &&
1117
- subItem.subPages &&
1118
- subItem.subPages.length > 0) {
1119
- const hasActiveSubPage = subItem.subPages.some(subPage => isPathActive(pathname, buildUrlWithCode(code, subPage.url)));
1120
- return (jsxRuntime.jsx(erpTheme.Collapsible, { asChild: true, defaultOpen: isSubItemActive || hasActiveSubPage, className: "group/sub-collapsible", children: jsxRuntime.jsxs(SidebarMenuSubItem, { children: [jsxRuntime.jsx(erpTheme.CollapsibleTrigger, { asChild: true, children: jsxRuntime.jsxs(SidebarMenuSubButton, { className: "w-full", isActive: isSubItemActive || hasActiveSubPage, children: [SubIcon && (jsxRuntime.jsx(SubIcon, { className: "sidebar-submodule-icon flex-shrink-0" })), jsxRuntime.jsx("span", { className: "flex-1 truncate", title: subItem.titleTh, children: subItem.titleTh }), jsxRuntime.jsx(lucideReact.ChevronDown, { className: "ml-auto size-4 transition-transform duration-200 group-data-[state=open]/sub-collapsible:rotate-180" })] }) }), jsxRuntime.jsx(erpTheme.CollapsibleContent, { children: jsxRuntime.jsx(SidebarMenuSub, { className: "ml-4", children: subItem.subPages.map(subPage => {
1121
- const subPageUrl = buildUrlWithCode(code, subPage.url);
1122
- const isSubPageActive = isPathActive(pathname, subPageUrl);
1123
- const SubPageIcon = subPage.icon;
1124
- return (jsxRuntime.jsx(SidebarMenuSubItem, { children: jsxRuntime.jsx(SidebarMenuSubButton, { asChild: true, className: `font-thai w-full text-sm ${isSubPageActive
1125
- ? "text-sidebar-accent-foreground font-medium"
1126
- : "text-sidebar-foreground"}`, isActive: false, children: jsxRuntime.jsxs(Link, { href: subPageUrl, prefetch: false, className: "w-full flex items-center", onClick: handleNavLinkClick, children: [SubPageIcon && (jsxRuntime.jsx(SubPageIcon, { className: "sidebar-subpage-icon flex-shrink-0" })), jsxRuntime.jsx("span", { className: "flex-1 truncate", title: subPage.titleTh, children: subPage.titleTh })] }) }) }, subPage.id));
1127
- }) }) })] }) }, subItem.id));
1128
- }
1129
- return (jsxRuntime.jsx(SidebarMenuSubItem, { children: jsxRuntime.jsx(SidebarMenuSubButton, { asChild: true, className: "w-full", isActive: isSubItemActive, children: jsxRuntime.jsxs(Link, { href: subItemUrl, prefetch: false, className: "w-full flex items-center", onClick: handleNavLinkClick, children: [SubIcon && (jsxRuntime.jsx(SubIcon, { className: "sidebar-submodule-icon flex-shrink-0" })), jsxRuntime.jsx("span", { className: "flex-1 truncate", title: subItem.titleTh, children: subItem.titleTh }), approvalCount > 0 && (jsxRuntime.jsx(erpTheme.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));
1130
- }) }) }))] }) }, item.id));
1123
+ const isItemActive = (workspace ? samePage(pathname, itemUrl) : pathname === itemUrl) &&
1124
+ !hasActiveSubItem;
1125
+ const IconComp = item.icon;
1126
+ const workspaceButton = workspace
1127
+ ? "h-auto gap-3 rounded-[10px] px-3.5 py-[11px] text-sm " +
1128
+ (isItemActive
1129
+ ? "bg-sidebar-accent font-bold text-sidebar-accent-foreground"
1130
+ : "font-medium text-sidebar-foreground/70")
1131
+ : "";
1132
+ const icon = workspace && IconComp ? (jsxRuntime.jsx(IconComp, { className: "size-[18px] shrink-0" })) : null;
1133
+ const badge = workspace && item.badge !== undefined && item.badge !== "" ? (jsxRuntime.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;
1134
+ return (jsxRuntime.jsxs(React__namespace.Fragment, { children: [groupHeading, jsxRuntime.jsx(erpTheme.Collapsible, { asChild: true, defaultOpen: isItemActive || hasActiveSubItem, className: "group/collapsible", children: jsxRuntime.jsxs(SidebarMenuItem, { children: [jsxRuntime.jsx(erpTheme.CollapsibleTrigger, { asChild: true, children: jsxRuntime.jsx(SidebarMenuButton, { asChild: !item.subModules || item.subModules.length === 0, isActive: isItemActive, className: "data-[state=open]:text-sidebar-accent-foreground " +
1135
+ workspaceButton, children: item.subModules && item.subModules.length > 0 ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [icon, jsxRuntime.jsx("span", { id: item.id, children: item.titleTh }), jsxRuntime.jsx(lucideReact.ChevronRight, { className: "ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" })] })) : (jsxRuntime.jsxs(Link, { href: itemUrl, prefetch: false, className: "flex items-center w-full min-w-0 flex-1 " +
1136
+ (workspace ? "gap-3" : "gap-2"), onClick: handleNavLinkClick, "aria-current": isItemActive ? "page" : undefined, children: [icon, jsxRuntime.jsx("span", { id: item.id, className: "flex-1 truncate", title: item.titleTh, children: item.titleTh }), badge] })) }) }), item.subModules && item.subModules.length > 0 && (jsxRuntime.jsx(erpTheme.CollapsibleContent, { children: jsxRuntime.jsx(SidebarMenuSub, { className: workspace
1137
+ ? "mx-0 ml-[23px] mt-1 border-l border-sidebar-border pl-2.5 [&_a]:text-[13px] [&_svg]:!size-4"
1138
+ : undefined, children: item.subModules.map(subItem => {
1139
+ const subItemUrl = buildUrlWithCode(code, subItem.url);
1140
+ const isSubItemActive = subItemUrl === activeSubItemUrl;
1141
+ const SubIcon = subItem.icon;
1142
+ const approvalCount = approvalCounts && subItem.approvalCountKey
1143
+ ? approvalCounts[subItem.approvalCountKey] ?? 0
1144
+ : 0;
1145
+ if (subItem.isCollapsible &&
1146
+ subItem.subPages &&
1147
+ subItem.subPages.length > 0) {
1148
+ const hasActiveSubPage = subItem.subPages.some(subPage => isPathActive(pathname, buildUrlWithCode(code, subPage.url)));
1149
+ return (jsxRuntime.jsx(erpTheme.Collapsible, { asChild: true, defaultOpen: isSubItemActive || hasActiveSubPage, className: "group/sub-collapsible", children: jsxRuntime.jsxs(SidebarMenuSubItem, { children: [jsxRuntime.jsx(erpTheme.CollapsibleTrigger, { asChild: true, children: jsxRuntime.jsxs(SidebarMenuSubButton, { className: "w-full", isActive: isSubItemActive || hasActiveSubPage, children: [SubIcon && (jsxRuntime.jsx(SubIcon, { className: "sidebar-submodule-icon flex-shrink-0" })), jsxRuntime.jsx("span", { className: "flex-1 truncate", title: subItem.titleTh, children: subItem.titleTh }), jsxRuntime.jsx(lucideReact.ChevronDown, { className: "ml-auto size-4 transition-transform duration-200 group-data-[state=open]/sub-collapsible:rotate-180" })] }) }), jsxRuntime.jsx(erpTheme.CollapsibleContent, { children: jsxRuntime.jsx(SidebarMenuSub, { className: "ml-4", children: subItem.subPages.map(subPage => {
1150
+ const subPageUrl = buildUrlWithCode(code, subPage.url);
1151
+ const isSubPageActive = isPathActive(pathname, subPageUrl);
1152
+ const SubPageIcon = subPage.icon;
1153
+ return (jsxRuntime.jsx(SidebarMenuSubItem, { children: jsxRuntime.jsx(SidebarMenuSubButton, { asChild: true, className: `font-thai w-full text-sm ${isSubPageActive
1154
+ ? "text-sidebar-accent-foreground font-medium"
1155
+ : "text-sidebar-foreground"}`, isActive: false, children: jsxRuntime.jsxs(Link, { href: subPageUrl, prefetch: false, className: "w-full flex items-center", onClick: handleNavLinkClick, children: [SubPageIcon && (jsxRuntime.jsx(SubPageIcon, { className: "sidebar-subpage-icon flex-shrink-0" })), jsxRuntime.jsx("span", { className: "flex-1 truncate", title: subPage.titleTh, children: subPage.titleTh })] }) }) }, subPage.id));
1156
+ }) }) })] }) }, subItem.id));
1157
+ }
1158
+ return (jsxRuntime.jsx(SidebarMenuSubItem, { children: jsxRuntime.jsx(SidebarMenuSubButton, { asChild: true, className: "w-full", isActive: isSubItemActive, children: jsxRuntime.jsxs(Link, { href: subItemUrl, prefetch: false, className: "w-full flex items-center", onClick: handleNavLinkClick, children: [SubIcon && (jsxRuntime.jsx(SubIcon, { className: "sidebar-submodule-icon flex-shrink-0" })), jsxRuntime.jsx("span", { className: "flex-1 truncate", title: subItem.titleTh, children: subItem.titleTh }), approvalCount > 0 && (jsxRuntime.jsx(erpTheme.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));
1159
+ }) }) }))] }) })] }, item.id));
1131
1160
  }) }) }));
1132
1161
  }
1133
1162
 
@@ -1135,14 +1164,16 @@ function OrgMark({ logoUrl, size = "md", }) {
1135
1164
  const [failed, setFailed] = React.useState(false);
1136
1165
  const box = size === "sm"
1137
1166
  ? "flex size-6 items-center justify-center rounded-md border overflow-hidden bg-background"
1138
- : "flex aspect-square size-8 shrink-0 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground overflow-hidden";
1167
+ : size === "header"
1168
+ ? "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"
1169
+ : "flex aspect-square size-8 shrink-0 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground overflow-hidden";
1139
1170
  const icon = size === "sm" ? "size-3.5 shrink-0" : "size-4";
1140
1171
  if (logoUrl && !failed) {
1141
1172
  return (jsxRuntime.jsx("div", { className: erpTheme.cn(box, size === "md" && "bg-white"), children: jsxRuntime.jsx("img", { src: logoUrl, alt: "", className: "size-full object-contain p-0.5", onError: () => setFailed(true) }) }));
1142
1173
  }
1143
1174
  return (jsxRuntime.jsx("div", { className: box, children: jsxRuntime.jsx(lucideReact.Building2, { className: icon }) }));
1144
1175
  }
1145
- function OrganizationSwitcher({ activeOrganization, isContextLoading = false, organizations, isLoadingOrgs = false, setOrganizationCookie, registerOrganizationHref = "/register-organization", className, }) {
1176
+ function OrganizationSwitcher({ activeOrganization, isContextLoading = false, organizations, isLoadingOrgs = false, setOrganizationCookie, registerOrganizationHref = "/register-organization", placement = "sidebar", className, }) {
1146
1177
  const { isMobile } = useSidebarToggle();
1147
1178
  const router = navigation.useRouter();
1148
1179
  const params = navigation.useParams();
@@ -1183,6 +1214,14 @@ function OrganizationSwitcher({ activeOrganization, isContextLoading = false, or
1183
1214
  });
1184
1215
  };
1185
1216
  const isLoading = isLoadingOrgs || isContextLoading;
1217
+ if (placement === "header") {
1218
+ const face = (interactive) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(OrgMark, { logoUrl: activeOrganization?.logoUrl, size: "header" }), jsxRuntime.jsxs("span", { className: "min-w-0 text-left", children: [jsxRuntime.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 ? (jsxRuntime.jsx(erpTheme.Skeleton, { className: "mt-1 h-3.5 w-28" })) : (jsxRuntime.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 ? (jsxRuntime.jsx(lucideReact.Loader2, { className: "ml-1 size-3.5 shrink-0 animate-spin text-[var(--header-fg-muted,currentColor)]" })) : (jsxRuntime.jsx(lucideReact.ChevronsUpDown, { className: "ml-1 size-3.5 shrink-0 text-[var(--header-fg-muted,currentColor)]" }))) : null] }));
1219
+ const faceClass = erpTheme.cn("flex items-center gap-2.5 rounded-lg py-1.5 max-sm:gap-2", className);
1220
+ if (isLoading || organizations.length <= 1) {
1221
+ return jsxRuntime.jsx("div", { className: faceClass, children: face(false) });
1222
+ }
1223
+ return (jsxRuntime.jsxs(erpTheme.DropdownMenu, { children: [jsxRuntime.jsx(erpTheme.DropdownMenuTrigger, { className: erpTheme.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) }), jsxRuntime.jsxs(erpTheme.DropdownMenuContent, { className: "min-w-64 rounded-lg", align: "end", side: "bottom", sideOffset: 6, children: [jsxRuntime.jsx(erpTheme.DropdownMenuLabel, { className: "text-xs text-muted-foreground", children: "\u0E2D\u0E07\u0E04\u0E4C\u0E01\u0E23" }), organizations.map(org => (jsxRuntime.jsxs(erpTheme.DropdownMenuItem, { onClick: () => handleOrganizationChange(org), disabled: isPending, className: erpTheme.cn("gap-2 p-2 cursor-pointer", String(activeOrganization?.id) === String(org.id) && "bg-accent"), children: [jsxRuntime.jsx(OrgMark, { logoUrl: org.logoUrl, size: "sm" }), jsxRuntime.jsx("span", { className: "truncate", children: org.name })] }, org.id))), jsxRuntime.jsx(erpTheme.DropdownMenuSeparator, {}), jsxRuntime.jsxs(erpTheme.DropdownMenuItem, { className: "gap-2 p-2 cursor-pointer", onClick: () => router.push(registerOrganizationHref), children: [jsxRuntime.jsx("div", { className: "flex size-6 items-center justify-center rounded-md border bg-transparent", children: jsxRuntime.jsx(lucideReact.Plus, { className: "size-4" }) }), jsxRuntime.jsx("span", { className: "text-muted-foreground font-medium", children: "\u0E40\u0E1E\u0E34\u0E48\u0E21\u0E2D\u0E07\u0E04\u0E4C\u0E01\u0E23" })] })] })] }));
1224
+ }
1186
1225
  if (isLoading) {
1187
1226
  return (jsxRuntime.jsx(SidebarMenu, { className: className, children: jsxRuntime.jsx(SidebarMenuItem, { children: jsxRuntime.jsxs("div", { className: "flex items-center gap-2 p-2", children: [jsxRuntime.jsx(erpTheme.Skeleton, { className: "size-8 rounded-lg" }), jsxRuntime.jsx(erpTheme.Skeleton, { className: "h-4 w-24" })] }) }) }));
1188
1227
  }
@@ -1197,7 +1236,7 @@ function SidebarHeader({ children, className }) {
1197
1236
  return jsxRuntime.jsx("div", { className: erpTheme.cn("px-2 py-3", className), children: children });
1198
1237
  }
1199
1238
 
1200
- function AppSidebar({ modules, tools, activeModule, orgCode, approvalCounts, closeOnNavigation = false, header, footer, className, }) {
1239
+ function AppSidebar({ modules, tools, activeModule, orgCode, approvalCounts, closeOnNavigation = false, header, footer, bottom, chrome = "brand", className, }) {
1201
1240
  const { open } = useSidebar();
1202
1241
  const modulesWithActiveState = React__namespace.useMemo(() => modules.map(m => ({
1203
1242
  ...m,
@@ -1205,9 +1244,27 @@ function AppSidebar({ modules, tools, activeModule, orgCode, approvalCounts, clo
1205
1244
  })), [modules, activeModule]);
1206
1245
  if (!open)
1207
1246
  return null;
1247
+ if (chrome === "workspace") {
1248
+ return (jsxRuntime.jsxs("div", { className: erpTheme.cn("flex h-screen w-[244px] shrink-0 flex-col overflow-hidden border-r border-sidebar-border bg-sidebar text-sidebar-foreground",
1249
+ // As a drawer (≤1024px) it floats over the page and needs the lift.
1250
+ "max-[1024px]:shadow-2xl", className), children: [header ? jsxRuntime.jsx("div", { className: "shrink-0 px-[18px] pt-8 pb-6", children: header }) : null, jsxRuntime.jsxs(SidebarContent, { className: "flex-1 overflow-y-auto px-[18px]", children: [jsxRuntime.jsx(NavMain, { items: modulesWithActiveState, orgCode: orgCode, approvalCounts: approvalCounts, closeOnNavigation: closeOnNavigation, chrome: "workspace" }), jsxRuntime.jsx(NavTools, { tools: tools, closeOnNavigation: closeOnNavigation })] }), bottom ? jsxRuntime.jsx("div", { className: "shrink-0 px-[18px] pt-6", children: bottom }) : null, jsxRuntime.jsx("div", { className: "shrink-0 px-[18px] pb-3", children: footer })] }));
1251
+ }
1208
1252
  return (jsxRuntime.jsxs("div", { className: erpTheme.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: [jsxRuntime.jsx(SidebarHeader, { children: header }), jsxRuntime.jsxs(SidebarContent, { className: "pl-2 pr-2 flex-1 overflow-y-auto", children: [jsxRuntime.jsx(NavMain, { items: modulesWithActiveState, orgCode: orgCode, approvalCounts: approvalCounts, closeOnNavigation: closeOnNavigation }), jsxRuntime.jsx(NavTools, { tools: tools, closeOnNavigation: closeOnNavigation })] }), jsxRuntime.jsx(SidebarFooter, { className: "pl-2 pr-2", children: footer })] }));
1209
1253
  }
1210
1254
 
1255
+ /**
1256
+ * Header ink as CSS variables. Header-borne pieces — breadcrumb, sidebar
1257
+ * trigger, the org switcher, the app's own page title — read these, with
1258
+ * white as the fallback, so the brand chrome looks exactly as before.
1259
+ */
1260
+ const WORKSPACE_HEADER_VARS = {
1261
+ "--header-fg": "#fff",
1262
+ "--header-fg-muted": "rgb(255 255 255 / 0.78)",
1263
+ "--header-hover": "rgb(255 255 255 / 0.12)",
1264
+ "--header-title-px": "0px",
1265
+ "--header-title-py": "0px",
1266
+ };
1267
+
1211
1268
  /**
1212
1269
  * Top-level app chrome: sticky header, sidebar slot, content gutter and the
1213
1270
  * session/org-context plumbing that both Accounting and HR need. Apps wrap
@@ -1220,7 +1277,7 @@ function AppSidebar({ modules, tools, activeModule, orgCode, approvalCounts, clo
1220
1277
  * an erp-shell bump — domain components must NOT add their own root padding
1221
1278
  * or content gets indented twice.
1222
1279
  */
1223
- function AppLayoutShell({ children, sidebar, loadingComponent, breadcrumb, permissionProvider, organizationId, setOrganizationId, organizations, signInUrl, selectOrganizationUrl, }) {
1280
+ function AppLayoutShell({ children, sidebar, loadingComponent, breadcrumb, permissionProvider, organizationId, setOrganizationId, organizations, signInUrl, selectOrganizationUrl, chrome = "brand", headerEnd, mobileBrand, }) {
1224
1281
  const { data: session, status } = react.useSession();
1225
1282
  const { open, setOpen } = useSidebar();
1226
1283
  const router = navigation.useRouter();
@@ -1343,10 +1400,15 @@ function AppLayoutShell({ children, sidebar, loadingComponent, breadcrumb, permi
1343
1400
  }
1344
1401
  // When the sidebar is docked-open it consumes 21.5rem on the left; otherwise
1345
1402
  // the content takes the full width and we add a left gutter inside.
1346
- const contentMargin = open && !isSidebarModalLayout ? "ml-[21.5rem]" : "";
1347
- const needsLeftGutter = !(open && !isSidebarModalLayout);
1403
+ const workspace = chrome === "workspace";
1404
+ const contentMargin = open && !isSidebarModalLayout
1405
+ ? workspace
1406
+ ? "ml-[244px]"
1407
+ : "ml-[21.5rem]"
1408
+ : "";
1409
+ const needsLeftGutter = workspace || !(open && !isSidebarModalLayout);
1348
1410
  const wrap = permissionProvider ?? ((c) => c);
1349
- return (jsxRuntime.jsxs("div", { className: "flex min-h-screen w-full bg-background", children: [open && (jsxRuntime.jsx("div", { className: "fixed left-0 top-0 z-50 h-screen", children: sidebar })), open && isSidebarModalLayout && (jsxRuntime.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) })), jsxRuntime.jsx("div", { className: cx("relative flex min-w-0 flex-1 flex-col transition-all", contentMargin), children: jsxRuntime.jsxs("main", { className: "flex flex-1 flex-col min-h-screen gap-4", children: [jsxRuntime.jsxs("div", { className: "sticky top-0 z-30 shrink-0 w-full", children: [jsxRuntime.jsx(HeaderBackground, {}), jsxRuntime.jsxs("header", { className: "relative shrink-0 gap-2 py-4 h-auto min-h-[123.5px] w-full z-10", children: [jsxRuntime.jsxs("div", { className: "flex w-full items-center gap-2 px-3 sm:px-4 lg:px-6", children: [jsxRuntime.jsx(SidebarTrigger, { className: "shrink-0 text-white hover:bg-white/10" }), jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: breadcrumb })] }), jsxRuntime.jsx("div", { id: "header-title" })] })] }), jsxRuntime.jsx(React.Suspense, { fallback: jsxRuntime.jsx("div", { className: "relative z-10 min-h-[calc(100vh-8rem)]", children: jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: jsxRuntime.jsxs("div", { className: "space-y-4 text-center", children: [jsxRuntime.jsx("div", { className: "mx-auto h-8 w-8 animate-spin rounded-full border-b-2 border-primary" }), jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E42\u0E2B\u0E25\u0E14..." })] }) }) }), children: jsxRuntime.jsx("div", { className: cx("relative z-10 pr-6", needsLeftGutter && "pl-6"), children: orgContextIsStale ? loadingComponent : wrap(children) }, `org-${contentOrgKey}`) })] }) })] }));
1411
+ return (jsxRuntime.jsxs("div", { className: "flex min-h-screen w-full bg-background", children: [open && (jsxRuntime.jsx("div", { className: "fixed left-0 top-0 z-50 h-screen", children: sidebar })), open && isSidebarModalLayout && (jsxRuntime.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) })), jsxRuntime.jsx("div", { className: cx("relative flex min-w-0 flex-1 flex-col transition-all", contentMargin), children: jsxRuntime.jsxs("main", { className: cx("flex flex-1 flex-col min-h-screen", workspace ? "gap-0" : "gap-4"), children: [workspace ? (jsxRuntime.jsxs("div", { className: "sticky top-0 z-30 shrink-0 w-full", style: WORKSPACE_HEADER_VARS, children: [jsxRuntime.jsx(HeaderBackground, { className: "rounded-none" }), jsxRuntime.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: [jsxRuntime.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 ? (jsxRuntime.jsx("div", { className: "shrink-0", children: mobileBrand })) : null, jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 flex-col justify-center gap-0.5", children: [jsxRuntime.jsx("div", { className: "hidden min-w-0 sm:block", children: breadcrumb }), jsxRuntime.jsx("div", { id: "header-title", className: "min-w-0" })] }), headerEnd ? (jsxRuntime.jsx("div", { className: "flex shrink-0 items-center gap-3 sm:gap-5", children: headerEnd })) : null] })] })) : (jsxRuntime.jsxs("div", { className: "sticky top-0 z-30 shrink-0 w-full", children: [jsxRuntime.jsx(HeaderBackground, {}), jsxRuntime.jsxs("header", { className: "relative shrink-0 gap-2 py-4 h-auto min-h-[123.5px] w-full z-10", children: [jsxRuntime.jsxs("div", { className: "flex w-full items-center gap-2 px-3 sm:px-4 lg:px-6", children: [jsxRuntime.jsx(SidebarTrigger, { className: "shrink-0 text-white hover:bg-white/10" }), jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: breadcrumb }), headerEnd ? (jsxRuntime.jsx("div", { className: "flex shrink-0 items-center gap-3", children: headerEnd })) : null] }), jsxRuntime.jsx("div", { id: "header-title" })] })] })), jsxRuntime.jsx(React.Suspense, { fallback: jsxRuntime.jsx("div", { className: "relative z-10 min-h-[calc(100vh-8rem)]", children: jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: jsxRuntime.jsxs("div", { className: "space-y-4 text-center", children: [jsxRuntime.jsx("div", { className: "mx-auto h-8 w-8 animate-spin rounded-full border-b-2 border-primary" }), jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E42\u0E2B\u0E25\u0E14..." })] }) }) }), children: jsxRuntime.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}`) })] }) })] }));
1350
1412
  }
1351
1413
  /** Local tiny classnames helper — avoids pulling clsx/tailwind-merge into
1352
1414
  * the shell just for the layout file. Falsy values are skipped. */
@@ -1354,6 +1416,37 @@ function cx(...parts) {
1354
1416
  return parts.filter(Boolean).join(" ");
1355
1417
  }
1356
1418
 
1419
+ /**
1420
+ * The product mark at the top of the workspace sidebar: the app icon, the
1421
+ * name, and a spaced caption beneath it.
1422
+ */
1423
+ function WorkspaceBrand({ name, caption, mark, href, compact = false, className, }) {
1424
+ const body = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { className: erpTheme.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 ? (jsxRuntime.jsx("span", { className: "sr-only", children: name })) : (jsxRuntime.jsxs("span", { className: "min-w-0", children: [jsxRuntime.jsx("span", { className: "block truncate text-[25px] leading-tight font-extrabold tracking-[-0.05em]", children: name }), caption ? (jsxRuntime.jsx("small", { className: "-mt-0.5 block truncate text-[9px] font-semibold tracking-[0.29em] text-muted-foreground", children: caption })) : null] }))] }));
1425
+ const cls = erpTheme.cn("flex items-center gap-[11px] rounded-lg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", !compact && "px-4", className);
1426
+ return href ? (jsxRuntime.jsx(Link, { href: href, className: cls, "aria-label": compact ? name : undefined, children: body })) : (jsxRuntime.jsx("div", { className: cls, children: body }));
1427
+ }
1428
+
1429
+ function isExternal(href) {
1430
+ return /^https?:\/\//i.test(href);
1431
+ }
1432
+ /**
1433
+ * The card at the foot of the workspace sidebar that points someone who
1434
+ * does not know where to start at the place that will tell them.
1435
+ */
1436
+ function SidebarHelpCard({ title, description, actionLabel, href, icon, className, }) {
1437
+ const external = isExternal(href);
1438
+ const action = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [actionLabel, jsxRuntime.jsx(lucideReact.ArrowRight, { className: "size-[15px]" })] }));
1439
+ 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";
1440
+ return (jsxRuntime.jsxs("div", { className: erpTheme.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: [jsxRuntime.jsx("span", { className: "mb-[7px] block text-primary/70", children: icon ?? jsxRuntime.jsx(lucideReact.Compass, { className: "size-6", strokeWidth: 1.6 }) }), jsxRuntime.jsx("strong", { className: "block text-xs font-semibold", children: title }), description ? (jsxRuntime.jsx("p", { className: "mt-[7px] text-xs leading-[1.7] text-muted-foreground", children: description })) : null, external ? (jsxRuntime.jsx("a", { href: href, target: "_blank", rel: "noopener noreferrer", className: actionClass, children: action })) : (jsxRuntime.jsx(Link, { href: href, className: actionClass, children: action }))] }));
1441
+ }
1442
+ /** A quiet row under the help card — the manual, the support chat. */
1443
+ function SidebarHelpLink({ label, href, icon, className }) {
1444
+ const external = isExternal(href);
1445
+ const cls = erpTheme.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);
1446
+ const body = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [icon ? jsxRuntime.jsx("span", { className: "shrink-0 [&>svg]:size-[18px]", children: icon }) : null, jsxRuntime.jsx("span", { className: "flex-1 truncate", children: label }), external ? jsxRuntime.jsx(lucideReact.ArrowUpRight, { className: "size-4 shrink-0" }) : null] }));
1447
+ return external ? (jsxRuntime.jsx("a", { href: href, target: "_blank", rel: "noopener noreferrer", className: cls, children: body })) : (jsxRuntime.jsx(Link, { href: href, className: cls, children: body }));
1448
+ }
1449
+
1357
1450
  /**
1358
1451
  * Central "no access" screen shared by every policy check (route-level
1359
1452
  * PermissionProvider and page-level PermissionGateShell). One look everywhere:
@@ -1669,27 +1762,40 @@ function writeThemeCookie(value) {
1669
1762
  /**
1670
1763
  * Bridges next-themes (per-origin localStorage) with a cookie scoped to the
1671
1764
  * parent domain, so the light/dark/system choice is shared across the
1672
- * accounting / HR / platform subdomains. On load it adopts the shared cookie;
1673
- * on every later change it writes the cookie back.
1765
+ * accounting / HR / platform subdomains.
1766
+ *
1767
+ * Local storage wins when it already has a value (H57): adopting the shared
1768
+ * cookie first used to overwrite an explicit choice — including one set via
1769
+ * DevTools — and write `theme=light` back on every load whenever the cookie
1770
+ * still said light. Push the local value into the cookie instead so sibling
1771
+ * apps catch up.
1674
1772
  */
1675
1773
  function ThemeCookieSync() {
1676
1774
  const { theme, setTheme } = nextThemes.useTheme();
1677
1775
  const skipFirstWrite = React__namespace.useRef(true);
1678
1776
  React__namespace.useEffect(() => {
1777
+ const local = typeof window !== "undefined"
1778
+ ? window.localStorage.getItem("theme")
1779
+ : null;
1679
1780
  const cookie = readThemeCookie();
1680
- if (cookie) {
1781
+ if (local) {
1782
+ if (local !== theme)
1783
+ setTheme(local);
1784
+ writeThemeCookie(local);
1785
+ }
1786
+ else if (cookie) {
1681
1787
  if (cookie !== theme)
1682
1788
  setTheme(cookie);
1683
1789
  }
1684
1790
  else if (theme) {
1685
- writeThemeCookie(theme); // seed the shared cookie the first time
1791
+ writeThemeCookie(theme);
1686
1792
  }
1687
1793
  // Run once on mount — `theme` is intentionally not a dependency here.
1688
1794
  // eslint-disable-next-line react-hooks/exhaustive-deps
1689
1795
  }, []);
1690
1796
  React__namespace.useEffect(() => {
1691
1797
  // Skip the initial render; only persist genuine changes (incl. the
1692
- // setTheme() triggered by adopting the cookie above — harmless re-write).
1798
+ // setTheme() triggered by adopting storage above — harmless re-write).
1693
1799
  if (skipFirstWrite.current) {
1694
1800
  skipFirstWrite.current = false;
1695
1801
  return;
@@ -2180,10 +2286,14 @@ exports.SessionRefreshTrigger = SessionRefreshTrigger;
2180
2286
  exports.SidebarContent = SidebarContent;
2181
2287
  exports.SidebarFooter = SidebarFooter;
2182
2288
  exports.SidebarHeader = SidebarHeader;
2289
+ exports.SidebarHelpCard = SidebarHelpCard;
2290
+ exports.SidebarHelpLink = SidebarHelpLink;
2183
2291
  exports.SidebarProvider = SidebarProvider;
2184
2292
  exports.SidebarTrigger = SidebarTrigger;
2185
2293
  exports.TapMenu = TapMenu;
2186
2294
  exports.ThemeProvider = ThemeProvider;
2295
+ exports.WORKSPACE_HEADER_VARS = WORKSPACE_HEADER_VARS;
2296
+ exports.WorkspaceBrand = WorkspaceBrand;
2187
2297
  exports.axiosInstance = instance;
2188
2298
  exports.buildSystemLinks = buildSystemLinks;
2189
2299
  exports.buildUrlWithCode = buildUrlWithCode;