@eintrek/erp-shell 0.1.46 → 0.1.48

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
@@ -438,44 +438,49 @@ function useSidebar() {
438
438
  }
439
439
  return context;
440
440
  }
441
+ /**
442
+ * Below this width the sidebar is a drawer over the page rather than a docked
443
+ * column (AppLayoutShell uses the same query).
444
+ */
445
+ const DRAWER_QUERY = "(max-width: 1024px)";
446
+ const isDrawerLayout = () => typeof window !== "undefined" && window.matchMedia(DRAWER_QUERY).matches;
447
+ /** Whether the docked sidebar was left open, as remembered in this browser. */
448
+ function rememberedDockedOpen(fallback) {
449
+ if (typeof window === "undefined")
450
+ return fallback;
451
+ const saved = localStorage.getItem("sidebar-open");
452
+ return saved !== null ? saved === "true" : fallback;
453
+ }
441
454
  /**
442
455
  * Sidebar Provider Component
443
- * Provides sidebar state management through React Context
444
- * Persists state to localStorage
456
+ * Provides sidebar state management through React Context.
457
+ *
458
+ * What is remembered (localStorage) is whether the DOCKED sidebar was open.
459
+ * A drawer always starts closed — a remembered desktop preference, or a
460
+ * drawer someone left open, is not a request to cover the page on load.
445
461
  */
446
462
  function SidebarProvider({ children, defaultOpen = true, }) {
447
- // Initialize from localStorage or default
448
- const [open, setOpenState] = React.useState(() => {
449
- if (typeof window !== "undefined") {
450
- const saved = localStorage.getItem("sidebar-open");
451
- return saved !== null ? saved === "true" : defaultOpen;
452
- }
453
- return defaultOpen;
454
- });
463
+ const [open, setOpenState] = React.useState(() => isDrawerLayout() ? false : rememberedDockedOpen(defaultOpen));
455
464
  const [isMobile, setIsMobile] = React.useState(false);
456
- // Persist to localStorage
465
+ // Remember the docked sidebar only; opening the drawer changes nothing.
457
466
  React.useEffect(() => {
458
- if (typeof window !== "undefined") {
467
+ if (typeof window !== "undefined" && !isDrawerLayout()) {
459
468
  localStorage.setItem("sidebar-open", String(open));
460
469
  }
461
470
  }, [open]);
462
- // Handle mobile detection.
463
- //
464
- // Crossing into a narrow viewport closes the sidebar. One `open` state
465
- // serves both layouts, and on desktop it is remembered as true — so
466
- // narrowing the window turned that into a drawer covering the whole page,
467
- // which had to be dismissed before anything underneath could be reached.
468
- // A remembered desktop preference is not a request to open a drawer.
471
+ // Crossing into the drawer layout closes it; crossing back restores the
472
+ // docked sidebar as it was left.
469
473
  React.useEffect(() => {
470
- let wasMobile = null;
471
- const checkMobile = () => {
472
- const nowMobile = window.innerWidth < 768;
473
- setIsMobile(nowMobile);
474
- if (nowMobile && wasMobile === false) {
475
- setOpenState(false);
476
- }
477
- wasMobile = nowMobile;
474
+ const drawer = window.matchMedia(DRAWER_QUERY);
475
+ const onChange = () => {
476
+ setOpenState(drawer.matches ? false : rememberedDockedOpen(defaultOpen));
478
477
  };
478
+ drawer.addEventListener("change", onChange);
479
+ return () => drawer.removeEventListener("change", onChange);
480
+ }, [defaultOpen]);
481
+ // Phone width — the organisation switcher opens its menu from the bottom.
482
+ React.useEffect(() => {
483
+ const checkMobile = () => setIsMobile(window.innerWidth < 768);
479
484
  checkMobile();
480
485
  window.addEventListener("resize", checkMobile);
481
486
  return () => window.removeEventListener("resize", checkMobile);
@@ -1095,52 +1100,88 @@ function NavMain({ items, orgCode, closeOnNavigation = false, approvalCounts, ch
1095
1100
  setOpen(false);
1096
1101
  };
1097
1102
  const code = orgCode ?? params?.code;
1103
+ // Which module (and sub-item) the current page belongs to.
1104
+ const states = items.map(item => {
1105
+ const itemUrl = buildUrlWithCode(code, item.url);
1106
+ // Most specific (longest) matching sub-item URL so only one link is active.
1107
+ const activeSubItemUrl = item.subModules?.reduce((best, subItem) => {
1108
+ const subItemUrl = buildUrlWithCode(code, subItem.url);
1109
+ const matches = isPathActive(pathname, subItemUrl) ||
1110
+ isPathActive(pathname, subItem.url);
1111
+ const subPageMatches = subItem.subPages?.some(subPage => isPathActive(pathname, buildUrlWithCode(code, subPage.url)) ||
1112
+ isPathActive(pathname, subPage.url));
1113
+ if (!matches && !subPageMatches)
1114
+ return best;
1115
+ const candidateUrl = subItemUrl;
1116
+ if (!best)
1117
+ return candidateUrl;
1118
+ return candidateUrl.length > best.length ? candidateUrl : best;
1119
+ }, null) ?? null;
1120
+ const hasActiveSubItem = !!activeSubItemUrl ||
1121
+ !!item.subModules?.some(subItem => subItem.subPages?.some(subPage => isPathActive(pathname, buildUrlWithCode(code, subPage.url)) ||
1122
+ isPathActive(pathname, subPage.url)));
1123
+ // Parent only active when path matches its URL exactly AND no child
1124
+ // is active (so parent+child don't both light up).
1125
+ const isItemActive = (workspace ? samePage(pathname, itemUrl) : pathname === itemUrl) &&
1126
+ !hasActiveSubItem;
1127
+ return { itemUrl, activeSubItemUrl, hasActiveSubItem, isItemActive };
1128
+ });
1129
+ // A group opens when its page becomes current and otherwise stays as the
1130
+ // user left it. defaultOpen alone decided once, on the first render — and
1131
+ // on a direct load the sub-items arrive after permissions do, so the group
1132
+ // holding the current page stayed shut.
1133
+ const activeKey = items
1134
+ .filter((_, i) => states[i].isItemActive || states[i].hasActiveSubItem)
1135
+ .map(item => item.id)
1136
+ .join(",");
1137
+ const [openIds, setOpenIds] = React__namespace.useState(() => new Set(activeKey ? activeKey.split(",") : []));
1138
+ React__namespace.useEffect(() => {
1139
+ if (!activeKey)
1140
+ return;
1141
+ setOpenIds(prev => {
1142
+ const ids = activeKey.split(",");
1143
+ if (ids.every(id => prev.has(id)))
1144
+ return prev;
1145
+ const next = new Set(prev);
1146
+ ids.forEach(id => next.add(id));
1147
+ return next;
1148
+ });
1149
+ }, [activeKey]);
1150
+ const setItemOpen = (id, open) => setOpenIds(prev => {
1151
+ if (prev.has(id) === open)
1152
+ return prev;
1153
+ const next = new Set(prev);
1154
+ if (open)
1155
+ next.add(id);
1156
+ else
1157
+ next.delete(id);
1158
+ return next;
1159
+ });
1098
1160
  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
1161
  const groupHeading = workspace &&
1100
1162
  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;
1103
- const itemUrl = buildUrlWithCode(code, item.url);
1104
- // Most specific (longest) matching sub-item URL so only one link is active.
1105
- const activeSubItemUrl = item.subModules?.reduce((best, subItem) => {
1106
- const subItemUrl = buildUrlWithCode(code, subItem.url);
1107
- const matches = isPathActive(pathname, subItemUrl) ||
1108
- isPathActive(pathname, subItem.url);
1109
- const subPageMatches = subItem.subPages?.some(subPage => isPathActive(pathname, buildUrlWithCode(code, subPage.url)) ||
1110
- isPathActive(pathname, subPage.url));
1111
- if (!matches && !subPageMatches)
1112
- return best;
1113
- const candidateUrl = subItemUrl;
1114
- if (!best)
1115
- return candidateUrl;
1116
- return candidateUrl.length > best.length ? candidateUrl : best;
1117
- }, null) ?? null;
1118
- const hasActiveSubItem = !!activeSubItemUrl ||
1119
- !!item.subModules?.some(subItem => subItem.subPages?.some(subPage => isPathActive(pathname, buildUrlWithCode(code, subPage.url)) ||
1120
- isPathActive(pathname, subPage.url)));
1121
- // Parent only active when path matches its URL exactly AND no child
1122
- // is active (so parent+child don't both light up).
1123
- const isItemActive = (workspace ? samePage(pathname, itemUrl) : pathname === itemUrl) &&
1124
- !hasActiveSubItem;
1163
+ item.group !== items[index - 1]?.group ? (jsxRuntime.jsx("li", { className: "list-none px-3 pb-2.5 text-xs text-muted-foreground" +
1164
+ (index > 0 ? " pt-5" : ""), children: item.group }, `group-${item.id}`)) : null;
1165
+ const { itemUrl, activeSubItemUrl, isItemActive } = states[index];
1125
1166
  const IconComp = item.icon;
1126
1167
  const workspaceButton = workspace
1127
- ? "h-auto gap-3 rounded-[10px] px-3.5 py-[11px] text-sm " +
1168
+ ? "h-auto gap-3 rounded-[10px] px-3 py-2.5 text-[13px] " +
1128
1169
  (isItemActive
1129
1170
  ? "bg-sidebar-accent font-bold text-sidebar-accent-foreground"
1130
1171
  : "font-medium text-sidebar-foreground/70")
1131
1172
  : "";
1132
1173
  const icon = workspace && IconComp ? (jsxRuntime.jsx(IconComp, { className: "size-[18px] shrink-0" })) : null;
1133
1174
  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 " +
1175
+ return (jsxRuntime.jsxs(React__namespace.Fragment, { children: [groupHeading, jsxRuntime.jsx(erpTheme.Collapsible, { asChild: true, open: openIds.has(item.id), onOpenChange: open => setItemOpen(item.id, open), 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
1176
  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
1177
  (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"
1178
+ ? "mx-0 mt-1 mb-3 ml-[21px] gap-0.5 border-l border-sidebar-border pl-2.5 [&_a]:h-auto [&_a]:gap-[11px] [&_a]:px-3 [&_a]:py-2 [&_a]:text-[13px] [&_svg]:!size-[17px]"
1138
1179
  : undefined, children: item.subModules.map(subItem => {
1139
1180
  const subItemUrl = buildUrlWithCode(code, subItem.url);
1140
1181
  const isSubItemActive = subItemUrl === activeSubItemUrl;
1141
1182
  const SubIcon = subItem.icon;
1142
1183
  const approvalCount = approvalCounts && subItem.approvalCountKey
1143
- ? approvalCounts[subItem.approvalCountKey] ?? 0
1184
+ ? (approvalCounts[subItem.approvalCountKey] ?? 0)
1144
1185
  : 0;
1145
1186
  if (subItem.isCollapsible &&
1146
1187
  subItem.subPages &&
@@ -1155,7 +1196,12 @@ function NavMain({ items, orgCode, closeOnNavigation = false, approvalCounts, ch
1155
1196
  : "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
1197
  }) }) })] }) }, subItem.id));
1157
1198
  }
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));
1199
+ return (jsxRuntime.jsx(SidebarMenuSubItem, { children: jsxRuntime.jsx(SidebarMenuSubButton, { asChild: true, className: "w-full" +
1200
+ (workspace && isSubItemActive
1201
+ ? " font-semibold"
1202
+ : ""), 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
1203
+ ? "99+"
1204
+ : approvalCount }))] }) }) }, subItem.id));
1159
1205
  }) }) }))] }) })] }, item.id));
1160
1206
  }) }) }));
1161
1207
  }
@@ -1408,7 +1454,13 @@ function AppLayoutShell({ children, sidebar, loadingComponent, breadcrumb, permi
1408
1454
  : "";
1409
1455
  const needsLeftGutter = workspace || !(open && !isSidebarModalLayout);
1410
1456
  const wrap = permissionProvider ?? ((c) => c);
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}`) })] }) })] }));
1457
+ 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-[18px] py-3 text-[var(--header-fg)] sm:px-6 lg:min-h-[88px] lg:px-10", children: [isSidebarModalLayout ? (
1458
+ // As in the design: the mark, then a menu button, and the
1459
+ // organisation on the right — no trail, no title, so the
1460
+ // organisation's name has the room it needs on a phone.
1461
+ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [mobileBrand ? (jsxRuntime.jsx("div", { className: "shrink-0", children: mobileBrand })) : null, jsxRuntime.jsx("button", { type: "button", onClick: () => setOpen(!open), "aria-label": open ? "ปิดเมนู" : "เปิดเมนู", className: "grid size-[34px] shrink-0 place-items-center rounded-lg text-[var(--header-fg-muted)] transition-colors hover:bg-[var(--header-hover)] hover:text-[var(--header-fg)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--header-fg-muted)]", children: jsxRuntime.jsx(lucideReact.LayoutGrid, { className: "size-5", strokeWidth: 1.65 }) })] })) : (jsxRuntime.jsx(SidebarTrigger, { className: "shrink-0 text-[var(--header-fg-muted)] hover:bg-[var(--header-hover)] hover:text-[var(--header-fg)] [&_svg]:!text-current" })), 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 max-sm:hidden" })] }), 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: workspace
1462
+ ? "relative z-10 px-[18px] pt-[26px] pb-4 sm:px-6 lg:px-10 lg:pt-[38px]"
1463
+ : cx("relative z-10 pr-6", needsLeftGutter && "pl-6"), children: orgContextIsStale ? loadingComponent : wrap(children) }, `org-${contentOrgKey}`) })] }) })] }));
1412
1464
  }
1413
1465
  /** Local tiny classnames helper — avoids pulling clsx/tailwind-merge into
1414
1466
  * the shell just for the layout file. Falsy values are skipped. */