@open-mercato/ui 0.6.6-develop.5505.1.f08e81a6fe → 0.6.6-develop.5523.1.e223ca1915
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/backend/AppShell.js
CHANGED
|
@@ -73,6 +73,10 @@ function resolveInjectedMenuLabel(item, t) {
|
|
|
73
73
|
if (item.label && item.label.includes(".")) return t(item.label, item.id);
|
|
74
74
|
return item.label ?? item.id;
|
|
75
75
|
}
|
|
76
|
+
function shouldBypassLogoOptimization(src) {
|
|
77
|
+
const value = src ?? "";
|
|
78
|
+
return /^https?:\/\//.test(value) || /^\/api\/attachments\/(?:image|file)\//.test(value);
|
|
79
|
+
}
|
|
76
80
|
function mergeSidebarItemsWithInjected(items, injectedItems, t) {
|
|
77
81
|
if (injectedItems.length === 0) return items;
|
|
78
82
|
const builtInById = /* @__PURE__ */ new Map();
|
|
@@ -293,6 +297,9 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
293
297
|
const { items: topbarInjectedMenuItems } = useInjectedMenuItems("menu:topbar:actions");
|
|
294
298
|
useEventBridge();
|
|
295
299
|
const resolvedProductName = productName ?? t("appShell.productName");
|
|
300
|
+
const resolvedLogo = chromePayload?.brand?.logo?.src ? chromePayload.brand.logo : logo;
|
|
301
|
+
const resolvedBrandName = chromePayload?.brand?.logo?.src ? chromePayload.brand.name ?? resolvedProductName : resolvedProductName;
|
|
302
|
+
const resolvedLogoBypassesOptimization = shouldBypassLogoOptimization(resolvedLogo?.src);
|
|
296
303
|
const [mobileOpen, setMobileOpen] = React.useState(false);
|
|
297
304
|
const [mobileDrawerView, setMobileDrawerView] = React.useState("auto");
|
|
298
305
|
React.useEffect(() => {
|
|
@@ -491,8 +498,8 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
491
498
|
className: `flex items-center gap-3 rounded-xl transition-colors hover:bg-muted ${compact ? "p-2 justify-center" : "p-3"}`,
|
|
492
499
|
"aria-label": t("appShell.goToDashboard"),
|
|
493
500
|
children: [
|
|
494
|
-
/* @__PURE__ */ jsx(Image, { src:
|
|
495
|
-
!compact && /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium text-foreground", children:
|
|
501
|
+
/* @__PURE__ */ jsx(Image, { src: resolvedLogo?.src ?? "/open-mercato.svg", alt: resolvedLogo?.alt ?? resolvedBrandName, width: 40, height: 40, className: "rounded-full shrink-0", unoptimized: resolvedLogoBypassesOptimization ? true : void 0 }),
|
|
502
|
+
!compact && /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium text-foreground", children: resolvedBrandName })
|
|
496
503
|
]
|
|
497
504
|
}
|
|
498
505
|
) }),
|
|
@@ -594,8 +601,8 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
594
601
|
className: `flex items-center gap-3 rounded-xl transition-colors hover:bg-muted ${compact ? "p-2 justify-center" : "p-3"}`,
|
|
595
602
|
"aria-label": t("appShell.goToDashboard"),
|
|
596
603
|
children: [
|
|
597
|
-
/* @__PURE__ */ jsx(Image, { src:
|
|
598
|
-
!compact && /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium text-foreground", children:
|
|
604
|
+
/* @__PURE__ */ jsx(Image, { src: resolvedLogo?.src ?? "/open-mercato.svg", alt: resolvedLogo?.alt ?? resolvedBrandName, width: 40, height: 40, className: "rounded-full shrink-0", unoptimized: resolvedLogoBypassesOptimization ? true : void 0 }),
|
|
605
|
+
!compact && /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium text-foreground", children: resolvedBrandName })
|
|
599
606
|
]
|
|
600
607
|
}
|
|
601
608
|
) }) : null,
|
|
@@ -654,8 +661,8 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
654
661
|
className: `flex items-center gap-3 rounded-xl transition-colors hover:bg-muted ${compact ? "p-2 justify-center" : "p-3"}`,
|
|
655
662
|
"aria-label": t("appShell.goToDashboard"),
|
|
656
663
|
children: [
|
|
657
|
-
/* @__PURE__ */ jsx(Image, { src:
|
|
658
|
-
!compact && /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium text-foreground", children:
|
|
664
|
+
/* @__PURE__ */ jsx(Image, { src: resolvedLogo?.src ?? "/open-mercato.svg", alt: resolvedLogo?.alt ?? resolvedBrandName, width: 40, height: 40, className: "rounded-full shrink-0", unoptimized: resolvedLogoBypassesOptimization ? true : void 0 }),
|
|
665
|
+
!compact && /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium text-foreground", children: resolvedBrandName })
|
|
659
666
|
]
|
|
660
667
|
}
|
|
661
668
|
) }),
|
|
@@ -1057,8 +1064,8 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
1057
1064
|
/* @__PURE__ */ jsxs("aside", { className: "absolute left-0 top-0 flex h-full w-[280px] max-w-[85vw] flex-col bg-background border-r shadow-lg overflow-hidden", children: [
|
|
1058
1065
|
/* @__PURE__ */ jsxs("div", { className: "shrink-0 flex items-center justify-between gap-2 border-b px-4 py-3", children: [
|
|
1059
1066
|
/* @__PURE__ */ jsxs(Link, { href: "/backend", className: "flex items-center gap-2 min-w-0 text-sm font-semibold", onClick: () => setMobileOpen(false), "aria-label": t("appShell.goToDashboard"), children: [
|
|
1060
|
-
/* @__PURE__ */ jsx(Image, { src:
|
|
1061
|
-
/* @__PURE__ */ jsx("span", { className: "truncate", children:
|
|
1067
|
+
/* @__PURE__ */ jsx(Image, { src: resolvedLogo?.src ?? "/open-mercato.svg", alt: resolvedLogo?.alt ?? resolvedBrandName, width: 28, height: 28, className: "rounded shrink-0", unoptimized: resolvedLogoBypassesOptimization ? true : void 0 }),
|
|
1068
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: resolvedBrandName })
|
|
1062
1069
|
] }),
|
|
1063
1070
|
/* @__PURE__ */ jsx(IconButton, { variant: "ghost", size: "sm", onClick: () => setMobileOpen(false), "aria-label": t("appShell.closeMenu"), children: /* @__PURE__ */ jsx(X, { className: "size-4" }) })
|
|
1064
1071
|
] }),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/backend/AppShell.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { createContext, useContext } from 'react'\nimport Link from 'next/link'\nimport Image from 'next/image'\nimport { ChevronDown, ChevronLeft, Home, PanelLeftClose, PanelLeftOpen, Search, X } from 'lucide-react'\nimport { Button } from '../primitives/button'\nimport {\n Breadcrumb as BreadcrumbNav,\n BreadcrumbEllipsis,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n} from '../primitives/breadcrumb'\nimport { IconButton } from '../primitives/icon-button'\nimport { Input } from '../primitives/input'\nimport { SearchInput } from '../primitives/search-input'\nimport { Checkbox } from '../primitives/checkbox'\nimport { Separator } from '../primitives/separator'\nimport { FlashMessages } from './FlashMessages'\nimport { QueryProvider } from '../theme/QueryProvider'\nimport { usePathname, useSearchParams } from 'next/navigation'\nimport { apiCall } from './utils/apiCall'\nimport { LastOperationBanner } from './operations/LastOperationBanner'\nimport { RecordConflictBanner } from './conflicts/RecordConflictBanner'\nimport { dismissRecordConflict } from './conflicts/store'\nimport { ProgressTopBar } from './progress/ProgressTopBar'\nimport { UpgradeActionBanner } from './upgrades/UpgradeActionBanner'\nimport { PartialIndexBanner } from './indexes/PartialIndexBanner'\nimport { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'\nimport { slugifySidebarId } from '@open-mercato/shared/modules/navigation/sidebarPreferences'\nimport { cloneSidebarGroups } from './sidebar/customization-helpers'\nimport type { SectionNavGroup } from './section-page/types'\nimport { InjectionSpot } from './injection/InjectionSpot'\nimport type { InjectionMenuItem } from '@open-mercato/shared/modules/widgets/injection'\nimport { LEGACY_GLOBAL_MUTATION_INJECTION_SPOT_ID } from './injection/mutationEvents'\nimport { mergeMenuItems } from './injection/mergeMenuItems'\nimport { useInjectedMenuItems } from './injection/useInjectedMenuItems'\nimport { resolveInjectedIcon } from './injection/resolveInjectedIcon'\nimport { useEventBridge } from './injection/eventBridge'\nimport { StatusBadgeInjectionSpot } from './injection/StatusBadgeInjectionSpot'\nimport { UmesDevToolsPanel } from './devtools'\nimport { AiDockProvider } from '../ai/AiDock'\nimport { AiChatSessionsProvider } from '../ai/AiChatSessions'\nimport { AiAssistantLauncher } from '../ai/AiAssistantLauncher'\nimport { BackendChromeProvider, useBackendChrome } from './BackendChromeProvider'\nimport {\n BACKEND_LAYOUT_FOOTER_INJECTION_SPOT_ID,\n BACKEND_LAYOUT_TOP_INJECTION_SPOT_ID,\n BACKEND_RECORD_CURRENT_INJECTION_SPOT_ID,\n BACKEND_SIDEBAR_FOOTER_INJECTION_SPOT_ID,\n BACKEND_SIDEBAR_TOP_INJECTION_SPOT_ID,\n BACKEND_SIDEBAR_NAV_FOOTER_INJECTION_SPOT_ID,\n BACKEND_SIDEBAR_NAV_INJECTION_SPOT_ID,\n BACKEND_TOPBAR_ACTIONS_INJECTION_SPOT_ID,\n GLOBAL_HEADER_STATUS_INDICATORS_INJECTION_SPOT_ID,\n GLOBAL_SIDEBAR_STATUS_BADGES_INJECTION_SPOT_ID,\n} from './injection/spotIds'\n\nexport type ShellLogo = {\n src: string\n alt?: string\n}\n\nexport type AppShellProps = {\n productName?: string\n logo?: ShellLogo\n email?: string\n canManageUpgradeActions?: boolean\n groups: {\n id?: string\n name: string\n defaultName?: string\n items: {\n id?: string\n href: string\n title: string\n defaultTitle?: string\n icon?: React.ReactNode\n iconName?: string\n iconMarkup?: string\n enabled?: boolean\n hidden?: boolean\n pageContext?: 'main' | 'admin' | 'settings' | 'profile'\n children?: {\n id?: string\n href: string\n title: string\n defaultTitle?: string\n icon?: React.ReactNode\n iconName?: string\n iconMarkup?: string\n enabled?: boolean\n hidden?: boolean\n pageContext?: 'main' | 'admin' | 'settings' | 'profile'\n }[]\n }[]\n }[]\n children: React.ReactNode\n rightHeaderSlot?: React.ReactNode\n sidebarCollapsedDefault?: boolean\n currentTitle?: string\n breadcrumb?: Array<{ label: string; href?: string }>\n // Optional: full admin nav API to refresh sidebar client-side\n adminNavApi?: string\n version?: string\n settingsSectionTitle?: string\n settingsPathPrefixes?: string[]\n settingsSections?: SectionNavGroup[]\n profileSections?: SectionNavGroup[]\n profileSectionTitle?: string\n profilePathPrefixes?: string[]\n mobileSidebarSlot?: React.ReactNode\n /**\n * How long (ms) to keep successfully completed progress operations visible\n * before auto-hiding. Pass `false` or `0` to disable. Defaults to 10 000 ms.\n */\n progressCompletedAutoHideMs?: number | false\n}\n\ntype Breadcrumb = Array<{ label: string; href?: string }>\n\ntype SidebarGroup = AppShellProps['groups'][number]\ntype SidebarItem = SidebarGroup['items'][number]\n\nfunction convertInjectedMenuItemToSidebarItem(item: InjectionMenuItem, title: string): SidebarItem | null {\n if (!item.href) return null\n return {\n id: item.id,\n href: item.href,\n title,\n defaultTitle: title,\n icon: resolveInjectedIcon(item.icon) ?? undefined,\n iconName: item.icon,\n enabled: true,\n hidden: false,\n pageContext: 'main',\n }\n}\n\nfunction resolveInjectedMenuLabel(\n item: { id: string; label?: string; labelKey?: string },\n t: (key: string, fallback?: string) => string,\n): string {\n if (item.labelKey && item.label) return t(item.labelKey, item.label)\n if (item.labelKey) return t(item.labelKey, item.id)\n if (item.label && item.label.includes('.')) return t(item.label, item.id)\n return item.label ?? item.id\n}\n\nfunction mergeSidebarItemsWithInjected(\n items: SidebarItem[],\n injectedItems: InjectionMenuItem[],\n t: (key: string, fallback?: string) => string,\n): SidebarItem[] {\n if (injectedItems.length === 0) return items\n\n const builtInById = new Map<string, SidebarItem>()\n for (const item of items) {\n builtInById.set(item.id ?? item.href, item)\n }\n\n const merged = mergeMenuItems(\n items.map((item) => ({\n id: item.id ?? item.href,\n })),\n injectedItems,\n )\n\n const result: SidebarItem[] = []\n for (const entry of merged) {\n if (entry.source === 'built-in') {\n const original = builtInById.get(entry.id)\n if (original) result.push(original)\n continue\n }\n const translatedLabel = resolveInjectedMenuLabel(\n { id: entry.id, label: entry.label, labelKey: entry.labelKey },\n t,\n )\n const converted = convertInjectedMenuItemToSidebarItem(\n {\n id: entry.id,\n label: translatedLabel,\n icon: entry.icon,\n href: entry.href,\n },\n translatedLabel,\n )\n if (converted) result.push(converted)\n }\n\n return result\n}\n\nfunction mergeSidebarGroupsWithInjected(\n groups: SidebarGroup[],\n injectedItems: InjectionMenuItem[],\n t: (key: string, fallback?: string) => string,\n): SidebarGroup[] {\n if (injectedItems.length === 0) return groups\n\n const injectedByGroup = new Map<string, InjectionMenuItem[]>()\n const ungrouped: InjectionMenuItem[] = []\n\n for (const item of injectedItems) {\n if (item.groupId && item.groupId.trim().length > 0) {\n const groupItems = injectedByGroup.get(item.groupId) ?? []\n groupItems.push(item)\n injectedByGroup.set(item.groupId, groupItems)\n continue\n }\n ungrouped.push(item)\n }\n\n const nextGroups = groups.map((group, index) => {\n const groupId = group.id || resolveGroupKey(group)\n const groupInjected = [\n ...(injectedByGroup.get(groupId) ?? []),\n ...(index === 0 ? ungrouped : []),\n ]\n return {\n ...group,\n items: mergeSidebarItemsWithInjected(group.items, groupInjected, t),\n }\n })\n\n const existingIds = new Set(nextGroups.map((group) => group.id || resolveGroupKey(group)))\n for (const [groupId, items] of injectedByGroup.entries()) {\n if (existingIds.has(groupId)) continue\n const first = items[0]\n const label = first.groupLabelKey\n ? t(first.groupLabelKey, first.groupLabel ?? groupId)\n : (first.groupLabel ?? groupId)\n const groupItems = mergeSidebarItemsWithInjected([], items, t)\n if (groupItems.length === 0) continue\n nextGroups.push({\n id: groupId,\n name: label,\n defaultName: label,\n items: groupItems,\n })\n }\n\n return nextGroups\n}\n\nfunction mergeSectionGroupsWithInjected(\n sections: SectionNavGroup[],\n injectedItems: InjectionMenuItem[],\n t: (key: string, fallback?: string) => string,\n): SectionNavGroup[] {\n if (injectedItems.length === 0) return sections\n const byGroup = new Map<string, InjectionMenuItem[]>()\n for (const item of injectedItems) {\n const groupId = item.groupId && item.groupId.trim().length > 0 ? item.groupId : 'injected'\n const bucket = byGroup.get(groupId) ?? []\n bucket.push(item)\n byGroup.set(groupId, bucket)\n }\n\n const nextSections = sections.map((section) => {\n const sectionItems = byGroup.get(section.id) ?? []\n if (sectionItems.length === 0) return section\n const mergedItems = mergeMenuItems(\n section.items.map((item) => ({ id: item.id, item })),\n sectionItems,\n ).flatMap((item) => {\n if (item.source === 'built-in') {\n const original = section.items.find((entry) => entry.id === item.id)\n return original ? [original] : []\n }\n if (!item.href) return []\n const label = resolveInjectedMenuLabel(item, t)\n return [{\n id: item.id,\n label,\n href: item.href,\n icon: resolveInjectedIcon(item.icon) ?? undefined,\n }]\n })\n return {\n ...section,\n items: mergedItems,\n }\n })\n\n for (const [sectionId, sectionItems] of byGroup.entries()) {\n const exists = nextSections.some((section) => section.id === sectionId)\n if (exists) continue\n const first = sectionItems[0]\n const label = first.groupLabelKey\n ? t(first.groupLabelKey, first.groupLabel ?? sectionId)\n : (first.groupLabel ?? sectionId)\n const items = sectionItems.flatMap((item) => {\n if (!item.href) return []\n const itemLabel = resolveInjectedMenuLabel(item, t)\n return [{\n id: item.id,\n label: itemLabel,\n href: item.href,\n icon: resolveInjectedIcon(item.icon) ?? undefined,\n }]\n })\n if (items.length === 0) continue\n nextSections.push({ id: sectionId, label, items })\n }\n\n return nextSections\n}\n\nfunction resolveGroupKey(group: SidebarGroup): string {\n if (group.id && group.id.length) return group.id\n if (group.defaultName && group.defaultName.length) return slugifySidebarId(group.defaultName)\n return slugifySidebarId(group.name)\n}\n\nfunction resolveItemKey(item: { id?: string; href: string }): string {\n const candidate = item.id?.trim()\n if (candidate && candidate.length > 0) return candidate\n return item.href\n}\n\nfunction SerializedIcon({ markup }: { markup: string }) {\n return <span aria-hidden=\"true\" dangerouslySetInnerHTML={{ __html: markup }} />\n}\n\nfunction renderIcon(\n icon: React.ReactNode | undefined,\n iconName: string | undefined,\n iconMarkup: string | undefined,\n fallback: React.ReactNode,\n) {\n if (icon) return icon\n if (iconName) {\n const resolved = resolveInjectedIcon(iconName)\n if (resolved) return resolved\n }\n if (iconMarkup) return <SerializedIcon markup={iconMarkup} />\n return fallback\n}\n\nconst HeaderContext = createContext<{\n setBreadcrumb: (b?: Breadcrumb) => void\n setTitle: (t?: string) => void\n} | null>(null)\n\nexport function ApplyBreadcrumb({ breadcrumb, title, titleKey }: { breadcrumb?: Array<{ label: string; href?: string; labelKey?: string }>; title?: string; titleKey?: string }) {\n const ctx = useContext(HeaderContext)\n const t = useT()\n const resolvedBreadcrumb = React.useMemo<Breadcrumb | undefined>(() => {\n if (!breadcrumb) return undefined\n return breadcrumb.map(({ label, labelKey, href }) => {\n const translated = labelKey ? t(labelKey) : undefined\n const finalLabel = translated && translated !== labelKey ? translated : label\n return {\n href,\n label: finalLabel,\n }\n })\n }, [breadcrumb, t])\n const resolvedTitle = React.useMemo(() => {\n if (!titleKey) return title\n const translated = t(titleKey)\n if (translated && translated !== titleKey) return translated\n return title\n }, [titleKey, title, t])\n React.useEffect(() => {\n ctx?.setBreadcrumb(resolvedBreadcrumb)\n if (resolvedTitle !== undefined) ctx?.setTitle(resolvedTitle)\n }, [ctx, resolvedBreadcrumb, resolvedTitle])\n return null\n}\n\nconst DefaultIcon = (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M8 6h13M8 12h13M8 18h13\"/>\n <path d=\"M3 6h.01M3 12h.01M3 18h.01\"/>\n </svg>\n)\n\n// DataTable icon used for dynamic custom entity records links\nconst DataTableIcon = (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"16\" rx=\"2\" ry=\"2\"/>\n <line x1=\"3\" y1=\"8\" x2=\"21\" y2=\"8\"/>\n <line x1=\"9\" y1=\"8\" x2=\"9\" y2=\"20\"/>\n <line x1=\"15\" y1=\"8\" x2=\"15\" y2=\"20\"/>\n </svg>\n)\n\nconst CustomizeIcon = (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n <path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.05.05a2 2 0 1 1-2.83 2.83l-.05-.05A1.65 1.65 0 0 0 15 19.4a1.65 1.65 0 0 0-1 .6 1.65 1.65 0 0 0-.33 1.82l-.05.05a2 2 0 1 1-2.83-2.83l.05-.05A1.65 1.65 0 0 0 9 15a1.65 1.65 0 0 0-1-.6 1.65 1.65 0 0 0-1.82.33l-.05.05a2 2 0 1 1-2.83-2.83l.05-.05A1.65 1.65 0 0 0 4.6 9 1.65 1.65 0 0 0 4 8a1.65 1.65 0 0 0-.6-1.82l-.05-.05a2 2 0 1 1 2.83-2.83l.05.05A1.65 1.65 0 0 0 9 4.6a1.65 1.65 0 0 0 1-.6 1.65 1.65 0 0 0 .33-1.82l.05-.05a2 2 0 1 1 2.83 2.83l-.05.05A1.65 1.65 0 0 0 15 9a1.65 1.65 0 0 0 1 .6 1.65 1.65 0 0 0 1.82-.33l.05-.05a2 2 0 1 1 2.83 2.83l-.05.05A1.65 1.65 0 0 0 19.4 15z\" />\n </svg>\n)\n\nconst BackArrowIcon = (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M19 12H5M12 19l-7-7 7-7\" />\n </svg>\n)\n\nfunction Chevron({ open }: { open: boolean }) {\n return (\n <svg className={`transition-transform ${open ? 'rotate-180' : ''}`} width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\"><path d=\"M6 9l6 6 6-6\"/></svg>\n )\n}\n\nexport function AppShell(props: AppShellProps) {\n return (\n <QueryProvider>\n <BackendChromeProvider adminNavApi={props.adminNavApi}>\n <AiChatSessionsProvider>\n <AiDockProvider>\n <AppShellBody {...props} />\n </AiDockProvider>\n </AiChatSessionsProvider>\n </BackendChromeProvider>\n </QueryProvider>\n )\n}\n\nfunction AppShellBody({ productName, logo, email, canManageUpgradeActions = false, groups, rightHeaderSlot, children, sidebarCollapsedDefault = false, currentTitle, breadcrumb, version, settingsSectionTitle, settingsPathPrefixes = [], settingsSections, profileSections, profileSectionTitle, profilePathPrefixes = [], mobileSidebarSlot, progressCompletedAutoHideMs }: AppShellProps) {\n const pathname = usePathname()\n const searchParams = useSearchParams()\n const t = useT()\n const locale = useLocale()\n const { payload: chromePayload, isReady: isChromeReady, isLoading: isChromeLoading } = useBackendChrome()\n const resolvedGroups = React.useMemo(\n () => cloneSidebarGroups(chromePayload?.groups ?? groups),\n [chromePayload?.groups, groups],\n )\n const resolvedSettingsSections = chromePayload?.settingsSections ?? settingsSections\n const resolvedSettingsPathPrefixes = chromePayload?.settingsPathPrefixes ?? settingsPathPrefixes\n const resolvedProfileSections = chromePayload?.profileSections ?? profileSections\n const resolvedProfilePathPrefixes = chromePayload?.profilePathPrefixes ?? profilePathPrefixes\n const { items: mainSidebarInjectedMenuItems } = useInjectedMenuItems('menu:sidebar:main')\n const { items: settingsSidebarInjectedMenuItems } = useInjectedMenuItems('menu:sidebar:settings')\n const { items: profileSidebarInjectedMenuItems } = useInjectedMenuItems('menu:sidebar:profile')\n const { items: topbarInjectedMenuItems } = useInjectedMenuItems('menu:topbar:actions')\n useEventBridge() // SSE DOM Event Bridge \u2014 singleton SSE connection for real-time server events\n const resolvedProductName = productName ?? t('appShell.productName')\n const [mobileOpen, setMobileOpen] = React.useState(false)\n // When the mobile drawer opens on a settings/profile route, it follows the\n // section sidebar by default. Set to 'main' to force-show the main nav even\n // when the route is in a section context. Reset on close.\n const [mobileDrawerView, setMobileDrawerView] = React.useState<'auto' | 'main'>('auto')\n // Clear the persistent record-conflict bar when the route changes. The\n // conflict is scoped to the record the user was editing, so navigating to an\n // unrelated page should dismiss it instead of carrying a stale \"Record\n // changed\" bar across modules.\n React.useEffect(() => {\n dismissRecordConflict()\n }, [pathname])\n React.useEffect(() => {\n if (!mobileOpen) setMobileDrawerView('auto')\n }, [mobileOpen])\n // Initialize from server-provided prop only to avoid hydration flicker\n const [collapsed, setCollapsed] = React.useState(sidebarCollapsedDefault)\n // Maintain internal nav state so we can augment it client-side\n const [navGroups, setNavGroups] = React.useState(resolvedGroups)\n const [openGroups, setOpenGroups] = React.useState<Record<string, boolean>>(() =>\n Object.fromEntries(resolvedGroups.map((g) => [resolveGroupKey(g), true])) as Record<string, boolean>\n )\n const [headerTitle, setHeaderTitle] = React.useState<string | undefined>(currentTitle)\n const [headerBreadcrumb, setHeaderBreadcrumb] = React.useState<Breadcrumb | undefined>(breadcrumb)\n const [navQuery, setNavQuery] = React.useState('')\n const navQueryNorm = navQuery.trim().toLowerCase()\n const navQueryActive = navQueryNorm.length > 0\n const matchesQuery = React.useCallback((label: string | undefined) => {\n if (!navQueryActive) return true\n if (!label) return false\n return label.toLowerCase().includes(navQueryNorm)\n }, [navQueryActive, navQueryNorm])\n const effectiveCollapsed = collapsed\n const expandedSidebarWidth = '240px'\n\n // Track scroll position of the desktop sidebar's inner scroll container so we can\n // flip the affordance chevron between down/up (and hide it entirely when content\n // fits without scrolling). The inner div is rendered deep in renderSidebar /\n // renderSectionSidebar \u2014 we tag it with `data-sidebar-scroll=\"true\"` and look it\n // up via the aside ref so we don't have to thread refs through the JSX tree.\n const sidebarAsideRef = React.useRef<HTMLElement>(null)\n const [sidebarScrollState, setSidebarScrollState] = React.useState<'down' | 'up' | 'none'>('down')\n const sidebarScrollIntentRef = React.useRef<'top' | 'bottom' | null>(null)\n\n // Click-to-scroll handler for the sidebar affordance chevron (#1803). Resolves the\n // scroll target lazily through the aside ref so we don't have to thread refs into\n // renderSidebar; respects `prefers-reduced-motion` by falling back to instant\n // scrolling when the user has opted out of smooth motion.\n const handleSidebarChevronScroll = React.useCallback((target: 'top' | 'bottom') => {\n const aside = sidebarAsideRef.current\n if (!aside) return\n const scrollTarget = aside.querySelector<HTMLElement>('[data-sidebar-scroll=\"true\"]')\n if (!scrollTarget) return\n const prefersReducedMotion =\n typeof window !== 'undefined' &&\n typeof window.matchMedia === 'function' &&\n window.matchMedia('(prefers-reduced-motion: reduce)').matches\n const behavior: ScrollBehavior = prefersReducedMotion ? 'auto' : 'smooth'\n const maxScrollTop = Math.max(0, scrollTarget.scrollHeight - scrollTarget.clientHeight)\n if (maxScrollTop <= 1) {\n sidebarScrollIntentRef.current = null\n setSidebarScrollState('none')\n return\n }\n sidebarScrollIntentRef.current = target\n setSidebarScrollState(target === 'bottom' ? 'up' : 'down')\n scrollTarget.scrollTo({\n top: target === 'top' ? 0 : maxScrollTop,\n behavior,\n })\n }, [])\n React.useEffect(() => {\n const aside = sidebarAsideRef.current\n if (!aside) return\n const target = aside.querySelector<HTMLElement>('[data-sidebar-scroll=\"true\"]')\n if (!target) return\n const update = () => {\n const { scrollTop, scrollHeight, clientHeight } = target\n const canScroll = scrollHeight > clientHeight + 1\n if (!canScroll) {\n sidebarScrollIntentRef.current = null\n setSidebarScrollState('none')\n return\n }\n const maxScrollTop = Math.max(0, scrollHeight - clientHeight)\n const atTop = scrollTop <= 8\n const atBottom = scrollTop >= maxScrollTop - 8\n const scrollIntent = sidebarScrollIntentRef.current\n if (scrollIntent === 'bottom') {\n if (atBottom) sidebarScrollIntentRef.current = null\n setSidebarScrollState('up')\n return\n }\n if (scrollIntent === 'top') {\n if (atTop) sidebarScrollIntentRef.current = null\n setSidebarScrollState('down')\n return\n }\n setSidebarScrollState(atBottom ? 'up' : 'down')\n }\n update()\n target.addEventListener('scroll', update, { passive: true })\n const ro = typeof ResizeObserver !== 'undefined' ? new ResizeObserver(update) : null\n ro?.observe(target)\n return () => {\n target.removeEventListener('scroll', update)\n ro?.disconnect()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [pathname, effectiveCollapsed])\n const injectionContext = React.useMemo(\n () => ({\n path: pathname ?? '',\n query: searchParams?.toString() ?? '',\n }),\n [pathname, searchParams],\n )\n\n const isOnSettingsPath = React.useMemo(() => {\n if (!pathname) return false\n if (pathname === '/backend/settings') return true\n return resolvedSettingsPathPrefixes.some((prefix) => pathname.startsWith(prefix))\n }, [pathname, resolvedSettingsPathPrefixes])\n\n const isOnProfilePath = React.useMemo(() => {\n if (!pathname) return false\n if (pathname === '/backend/profile') return true\n return resolvedProfilePathPrefixes.some((prefix) => pathname.startsWith(prefix))\n }, [pathname, resolvedProfilePathPrefixes])\n\n const sidebarMode: 'main' | 'settings' | 'profile' =\n isOnSettingsPath ? 'settings' :\n isOnProfilePath ? 'profile' :\n 'main'\n\n const mainNavGroupsWithInjected = React.useMemo(\n () => mergeSidebarGroupsWithInjected(navGroups, mainSidebarInjectedMenuItems, t),\n [mainSidebarInjectedMenuItems, navGroups, t],\n )\n\n // Lock body scroll when mobile drawer is open so touch scroll stays in the drawer\n React.useEffect(() => {\n if (!mobileOpen || typeof document === 'undefined') return\n const prev = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n return () => {\n document.body.style.overflow = prev\n }\n }, [mobileOpen])\n\n React.useEffect(() => {\n try {\n const savedOpen = typeof window !== 'undefined' ? localStorage.getItem('om:sidebarOpenGroups') : null\n if (!savedOpen) return\n const parsed = JSON.parse(savedOpen) as Record<string, boolean>\n setOpenGroups((prev) => {\n const next = { ...prev }\n for (const group of resolvedGroups) {\n const key = resolveGroupKey(group)\n if (key in parsed) next[key] = !!parsed[key]\n else if (group.name in parsed) next[key] = !!parsed[group.name]\n }\n return next\n })\n } catch {\n // ignore localStorage errors to avoid breaking hydration\n }\n }, [resolvedGroups])\n\n const toggleGroup = (groupId: string) => setOpenGroups((prev) => ({ ...prev, [groupId]: prev[groupId] === false }))\n\n const asideWidth = effectiveCollapsed ? '80px' : expandedSidebarWidth\n // Use min-h-svh so the border extends with tall content; no overflow so sticky bottom works\n const asideClassesBase = `border-r bg-background py-4`;\n\n // Persist collapse state to localStorage and cookie. Both writes can throw in\n // private/incognito mode (storage blocked) or when cookies are disabled \u2014\n // the persisted preference is purely a UX nice-to-have, never functional, so\n // swallow the failure and let the component fall back to the default state.\n React.useEffect(() => {\n try { localStorage.setItem('om:sidebarCollapsed', collapsed ? '1' : '0') } catch { /* localStorage blocked (private mode) \u2014 non-critical */ }\n try {\n document.cookie = `om_sidebar_collapsed=${collapsed ? '1' : '0'}; path=/; max-age=31536000; samesite=lax`\n } catch { /* cookies disabled \u2014 non-critical */ }\n }, [collapsed])\n\n // Two-level sidebar (Option B): when entering settings/profile mode, force the\n // main sidebar to collapsed (icons only) so the section sub-nav can sit beside\n // it; restore the user's previous expansion when returning to the main mode.\n // Initial ref is 'main' so direct mounts on /backend/settings also auto-collapse.\n const collapsedBeforeSectionRef = React.useRef<boolean | null>(null)\n const previousSidebarModeRef = React.useRef<'main' | 'settings' | 'profile'>('main')\n React.useEffect(() => {\n const previous = previousSidebarModeRef.current\n if (previous === 'main' && sidebarMode !== 'main') {\n collapsedBeforeSectionRef.current = collapsed\n if (!collapsed) setCollapsed(true)\n } else if (previous !== 'main' && sidebarMode === 'main' && collapsedBeforeSectionRef.current !== null) {\n const restoreTo = collapsedBeforeSectionRef.current\n collapsedBeforeSectionRef.current = null\n if (collapsed !== restoreTo) setCollapsed(restoreTo)\n }\n previousSidebarModeRef.current = sidebarMode\n }, [sidebarMode, collapsed])\n React.useEffect(() => {\n try { localStorage.setItem('om:sidebarOpenGroups', JSON.stringify(openGroups)) } catch { /* localStorage blocked (private mode) \u2014 non-critical */ }\n }, [openGroups])\n\n // Ensure current route's group is expanded on load\n React.useEffect(() => {\n const activeGroup = navGroups.find((g) => g.items.some((i) => pathname?.startsWith(i.href)))\n if (!activeGroup) return\n const key = resolveGroupKey(activeGroup)\n setOpenGroups((prev) => (prev[key] === false ? { ...prev, [key]: true } : prev))\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [pathname, navGroups])\n // Keep header state in sync with props (server-side updates)\n React.useEffect(() => {\n setHeaderTitle(currentTitle)\n setHeaderBreadcrumb(breadcrumb)\n }, [currentTitle, breadcrumb])\n // Clear breadcrumb on client-side navigation so stale state doesn't persist;\n // the new page's ApplyBreadcrumb (if any) will set the correct values\n const prevPathname = React.useRef(pathname)\n React.useEffect(() => {\n if (pathname !== prevPathname.current) {\n prevPathname.current = pathname\n setHeaderTitle(undefined)\n setHeaderBreadcrumb(undefined)\n }\n }, [pathname])\n\n // Keep navGroups in sync when server-provided groups change\n React.useEffect(() => {\n setNavGroups(cloneSidebarGroups(resolvedGroups))\n }, [resolvedGroups])\n\n function renderSectionSidebar(\n sections: SectionNavGroup[],\n title: string,\n compact: boolean,\n hideHeader?: boolean,\n hideSearch?: boolean\n ) {\n const sortedSections = [...sections].sort((a, b) => (a.order ?? 0) - (b.order ?? 0))\n const lastVisibleIndex = sortedSections.length - 1\n\n return (\n <div className=\"flex h-full flex-col gap-3\">\n {!hideHeader && (\n <div className=\"mb-2\">\n <Link\n href=\"/backend\"\n className={`flex items-center gap-3 rounded-xl transition-colors hover:bg-muted ${compact ? 'p-2 justify-center' : 'p-3'}`}\n aria-label={t('appShell.goToDashboard')}\n >\n <Image src={logo?.src ?? \"/open-mercato.svg\"} alt={logo?.alt ?? resolvedProductName} width={40} height={40} className=\"rounded-full shrink-0\" />\n {!compact && <span className=\"truncate text-sm font-medium text-foreground\">{resolvedProductName}</span>}\n </Link>\n </div>\n )}\n {!compact && !hideSearch && (\n <SearchInput\n value={navQuery}\n onChange={setNavQuery}\n placeholder={t('appShell.searchNavPlaceholder', 'Search...')}\n aria-label={t('appShell.searchNavAria', 'Search navigation')}\n clearLabel={t('appShell.searchNavClear', 'Clear search')}\n className=\"mb-2\"\n />\n )}\n <div data-sidebar-scroll=\"true\" className={`flex flex-1 flex-col gap-3 overflow-y-auto scrollbar-hide pr-1 ${compact ? '-ml-2 pl-2' : '-ml-3 pl-3'}`}>\n <nav className=\"flex flex-col gap-2\">\n {sortedSections.map((section, sectionIndex) => {\n const sectionNavQueryActive = hideSearch ? false : navQueryActive\n const matchesItemQuery = (item: typeof section.items[number]): boolean => {\n if (!sectionNavQueryActive) return true\n const label = item.labelKey ? t(item.labelKey, item.label) : item.label\n if (matchesQuery(label)) return true\n return Array.isArray(item.children) && item.children.some(matchesItemQuery)\n }\n const visibleItems = sectionNavQueryActive\n ? section.items.filter(matchesItemQuery)\n : section.items\n if (visibleItems.length === 0) return null\n const sortedItems = [...visibleItems].sort((a, b) => (a.order ?? 0) - (b.order ?? 0))\n const sectionLabel = section.labelKey ? t(section.labelKey, section.label) : section.label\n const sectionKey = `settings:${section.id}`\n const open = openGroups[sectionKey] !== false\n const sortSectionItems = (items: typeof section.items = []) =>\n [...items].sort((a, b) => (a.order ?? 0) - (b.order ?? 0))\n const filterChildren = (children: typeof section.items | undefined) => {\n if (!children) return [] as typeof section.items\n if (!sectionNavQueryActive) return [...children]\n return children.filter(matchesItemQuery)\n }\n\n const renderSectionItem = (item: (typeof section.items)[number], depth = 0): React.ReactNode => {\n const label = item.labelKey ? t(item.labelKey, item.label) : item.label\n const childItems = sortSectionItems(filterChildren(item.children))\n const isOnItemBranch = !!pathname && (\n pathname === item.href ||\n pathname.startsWith(`${item.href}/`)\n )\n const hasActiveChild = !!(pathname && childItems.some((child) => (\n pathname === child.href ||\n pathname.startsWith(`${child.href}/`)\n )))\n const showChildren = childItems.length > 0 && (isOnItemBranch || sectionNavQueryActive)\n const isActive = isOnItemBranch || hasActiveChild\n const base = compact ? 'w-10 h-10 justify-center' : 'w-full py-2 gap-2'\n const spacingStyle = !compact\n ? {\n paddingLeft: `${12 + depth * 16}px`,\n paddingRight: '12px',\n }\n : undefined\n\n return (\n <React.Fragment key={item.id}>\n <Link\n href={item.href}\n className={`relative text-sm font-medium rounded-lg inline-flex items-center ${base} ${\n isActive\n ? 'bg-muted text-foreground'\n : 'text-muted-foreground hover:bg-muted'\n }`}\n style={spacingStyle}\n title={compact ? label : undefined}\n data-menu-item-id={item.id}\n onClick={() => setMobileOpen(false)}\n >\n {isActive && (\n <span aria-hidden className={`absolute ${compact ? 'left-[-20px]' : 'left-[-12px]'} top-2 w-1 h-5 rounded-r bg-foreground`} />\n )}\n <span className=\"flex items-center justify-center shrink-0\">\n {renderIcon(\n item.icon,\n item.iconName,\n item.iconMarkup,\n item.href.includes('/backend/entities/user/') && item.href.endsWith('/records') ? DataTableIcon : DefaultIcon,\n )}\n </span>\n {!compact && <span className=\"truncate\">{label}</span>}\n </Link>\n {showChildren ? childItems.map((child) => renderSectionItem(child, depth + 1)) : null}\n </React.Fragment>\n )\n }\n\n return (\n <div key={section.id}>\n {!compact && (\n <Button\n variant=\"muted\"\n onClick={() => toggleGroup(sectionKey)}\n className=\"w-full px-1 justify-between flex text-xs font-medium uppercase tracking-wider text-muted-foreground/70 py-1\"\n aria-expanded={open}\n >\n <span>{sectionLabel}</span>\n <Chevron open={open} />\n </Button>\n )}\n {(open || compact) && (\n <div className={`flex flex-col ${compact ? 'items-center' : ''} gap-1`}>\n {sortedItems.map((item) => renderSectionItem(item))}\n </div>\n )}\n {sectionIndex !== lastVisibleIndex && <div className={`my-2 border-t ${compact ? '-ml-2 -mr-3' : '-ml-3 -mr-4'}`} />}\n </div>\n )\n })}\n </nav>\n </div>\n </div>\n )\n }\n\n function renderSidebar(compact: boolean, hideHeader?: boolean, forceMainOnly?: boolean) {\n if (!isChromeReady && isChromeLoading) {\n return (\n <div className=\"flex flex-col min-h-full gap-3\" data-testid=\"backend-chrome-loading\">\n {!hideHeader ? (\n <div className=\"mb-2\">\n <Link\n href=\"/backend\"\n className={`flex items-center gap-3 rounded-xl transition-colors hover:bg-muted ${compact ? 'p-2 justify-center' : 'p-3'}`}\n aria-label={t('appShell.goToDashboard')}\n >\n <Image src={logo?.src ?? \"/open-mercato.svg\"} alt={logo?.alt ?? resolvedProductName} width={40} height={40} className=\"rounded-full shrink-0\" />\n {!compact && <span className=\"truncate text-sm font-medium text-foreground\">{resolvedProductName}</span>}\n </Link>\n </div>\n ) : null}\n <div className=\"flex flex-1 flex-col gap-3 pr-1\">\n <div className=\"space-y-3\">\n <div className=\"h-8 rounded bg-muted/50\" />\n <div className=\"space-y-2 pl-1\">\n <div className=\"h-8 rounded bg-muted/50\" />\n <div className=\"h-8 rounded bg-muted/50\" />\n <div className=\"h-8 rounded bg-muted/50\" />\n </div>\n </div>\n <div className=\"space-y-3\">\n <div className=\"h-8 rounded bg-muted/50\" />\n <div className=\"space-y-2 pl-1\">\n <div className=\"h-8 rounded bg-muted/50\" />\n <div className=\"h-8 rounded bg-muted/50\" />\n </div>\n </div>\n </div>\n </div>\n )\n }\n\n if (!forceMainOnly && sidebarMode === 'settings' && resolvedSettingsSections && resolvedSettingsSections.length > 0) {\n const mergedSettingsSections = mergeSectionGroupsWithInjected(\n resolvedSettingsSections,\n settingsSidebarInjectedMenuItems,\n t,\n )\n return renderSectionSidebar(\n mergedSettingsSections,\n settingsSectionTitle ?? t('backend.nav.settings', 'Settings'),\n compact,\n hideHeader\n )\n }\n\n if (!forceMainOnly && sidebarMode === 'profile' && resolvedProfileSections && resolvedProfileSections.length > 0) {\n const mergedProfileSections = mergeSectionGroupsWithInjected(\n resolvedProfileSections,\n profileSidebarInjectedMenuItems,\n t,\n )\n return renderSectionSidebar(\n mergedProfileSections,\n profileSectionTitle ?? t('backend.nav.profile', 'Profile'),\n compact,\n hideHeader\n )\n }\n\n const isMobileVariant = !!hideHeader\n const shouldRenderSidebarInjectionSpots = !isMobileVariant\n\n return (\n <div className=\"flex h-full flex-col gap-3\">\n {!hideHeader && (\n <div className=\"mb-2\">\n <Link\n href=\"/backend\"\n className={`flex items-center gap-3 rounded-xl transition-colors hover:bg-muted ${compact ? 'p-2 justify-center' : 'p-3'}`}\n aria-label={t('appShell.goToDashboard')}\n >\n <Image src={logo?.src ?? \"/open-mercato.svg\"} alt={logo?.alt ?? resolvedProductName} width={40} height={40} className=\"rounded-full shrink-0\" />\n {!compact && <span className=\"truncate text-sm font-medium text-foreground\">{resolvedProductName}</span>}\n </Link>\n </div>\n )}\n {shouldRenderSidebarInjectionSpots ? (\n <InjectionSpot\n spotId={BACKEND_SIDEBAR_TOP_INJECTION_SPOT_ID}\n context={injectionContext}\n />\n ) : null}\n {!compact && (\n <SearchInput\n value={navQuery}\n onChange={setNavQuery}\n placeholder={t('appShell.searchNavPlaceholder', 'Search...')}\n aria-label={t('appShell.searchNavAria', 'Search navigation')}\n clearLabel={t('appShell.searchNavClear', 'Clear search')}\n className=\"mb-2\"\n />\n )}\n <div data-sidebar-scroll=\"true\" className={`flex flex-1 flex-col gap-3 overflow-y-auto scrollbar-hide pr-1 ${compact ? '-ml-2 pl-2' : '-ml-3 pl-3'}`}>\n {(() => {\n const isSettingsPath = (href: string) => {\n if (href === '/backend/settings') return true\n return resolvedSettingsPathPrefixes.some((prefix) => href.startsWith(prefix))\n }\n\n const isMainItem = (item: SidebarItem) => {\n if (item.pageContext && item.pageContext !== 'main') return false\n if (isSettingsPath(item.href)) return false\n return true\n }\n\n const mainGroups = mainNavGroupsWithInjected.map((g) => ({\n ...g,\n items: g.items.filter((item) => isMainItem(item) && item.hidden !== true),\n })).filter((g) => g.items.length > 0)\n\n const mainLastVisibleGroupIndex = (() => {\n for (let idx = mainGroups.length - 1; idx >= 0; idx -= 1) {\n if (mainGroups[idx].items.some((item) => item.hidden !== true)) return idx\n }\n return -1\n })()\n\n return (\n <>\n <nav className=\"flex flex-col gap-2\" data-testid=\"sidebar\">\n {shouldRenderSidebarInjectionSpots ? (\n <InjectionSpot\n spotId={BACKEND_SIDEBAR_NAV_INJECTION_SPOT_ID}\n context={injectionContext}\n />\n ) : null}\n {mainGroups.map((g, gi) => {\n const groupId = resolveGroupKey(g)\n const open = navQueryActive ? true : openGroups[groupId] !== false\n const visibleItems = g.items.filter((item) => {\n if (item.hidden === true) return false\n if (!navQueryActive) return true\n if (matchesQuery(item.title)) return true\n const itemChildren = (item.children ?? []).filter((c) => c.hidden !== true)\n return itemChildren.some((c) => matchesQuery(c.title))\n })\n if (visibleItems.length === 0) return null\n return (\n <div key={groupId}>\n {!compact && (\n <Button\n variant=\"muted\"\n onClick={() => toggleGroup(groupId)}\n className=\"w-full px-1 justify-between flex text-xs font-medium uppercase tracking-wider text-muted-foreground/70 py-1\"\n aria-expanded={open}\n >\n <span>{g.name}</span>\n <Chevron open={open} />\n </Button>\n )}\n {(open || compact) && (\n <div className={`flex flex-col ${compact ? 'items-center' : ''} gap-1`}>\n {visibleItems.map((i) => {\n const allChildItems = (i.children ?? []).filter((child) => child.hidden !== true)\n const matchingChildItems = navQueryActive\n ? allChildItems.filter((c) => matchesQuery(c.title))\n : allChildItems\n const childItems = navQueryActive ? matchingChildItems : allChildItems\n const showChildren = navQueryActive\n ? matchingChildItems.length > 0\n : (!!pathname && allChildItems.length > 0 && pathname.startsWith(i.href))\n const hasActiveChild = !!(pathname && allChildItems.some((c) => pathname.startsWith(c.href)))\n const isParentActive = (pathname === i.href) || (!navQueryActive && showChildren && !hasActiveChild)\n const base = compact ? 'w-10 h-10 justify-center' : 'w-full px-3 py-2 gap-2'\n return (\n <React.Fragment key={i.href}>\n <Link\n href={i.href}\n className={`relative text-sm font-medium rounded-lg inline-flex items-center ${base} ${\n isParentActive ? 'bg-muted text-foreground' : 'text-muted-foreground hover:bg-muted'\n } ${i.enabled === false ? 'pointer-events-none opacity-50' : ''}`}\n aria-disabled={i.enabled === false}\n title={compact ? i.title : undefined}\n data-menu-item-id={i.id ?? i.href}\n onClick={() => setMobileOpen(false)}\n >\n {isParentActive ? (\n <span aria-hidden className={`absolute ${compact ? 'left-[-20px]' : 'left-[-12px]'} top-2 w-1 h-5 rounded-r bg-foreground`} />\n ) : null}\n <span className=\"flex items-center justify-center shrink-0\">\n {renderIcon(\n i.icon,\n i.iconName,\n i.iconMarkup,\n DefaultIcon,\n )}\n </span>\n {!compact && <span>{i.title}</span>}\n </Link>\n {showChildren ? (\n <div className={`relative flex flex-col ${compact ? 'items-center' : ''} gap-1`}>\n {!compact && (\n <span aria-hidden className=\"pointer-events-none absolute left-1.5 top-1 bottom-1 w-px bg-border\" />\n )}\n {childItems.map((c) => {\n const childActive = pathname?.startsWith(c.href)\n const childBase = compact ? 'w-10 h-8 justify-center' : 'w-full pl-5 pr-3 py-2 gap-2'\n return (\n <Link\n key={c.href}\n href={c.href}\n className={`relative text-sm font-medium rounded-lg inline-flex items-center ${childBase} ${\n childActive ? 'bg-muted text-foreground' : 'text-muted-foreground hover:bg-muted'\n } ${c.enabled === false ? 'pointer-events-none opacity-50' : ''}`}\n aria-disabled={c.enabled === false}\n title={compact ? c.title : undefined}\n data-menu-item-id={c.id ?? c.href}\n onClick={() => setMobileOpen(false)}\n >\n {childActive ? (\n <span aria-hidden className={`absolute ${compact ? 'left-[-20px]' : 'left-[-12px]'} top-2 w-1 h-5 rounded-r bg-foreground`} />\n ) : null}\n <span className=\"flex items-center justify-center shrink-0\">\n {renderIcon(\n c.icon,\n c.iconName,\n c.iconMarkup,\n c.href.includes('/backend/entities/user/') && c.href.endsWith('/records') ? DataTableIcon : DefaultIcon,\n )}\n </span>\n {!compact && <span>{c.title}</span>}\n </Link>\n )\n })}\n </div>\n ) : null}\n </React.Fragment>\n )\n })}\n </div>\n )}\n {gi !== mainLastVisibleGroupIndex && <div className={`my-2 border-t ${compact ? '-ml-2 -mr-3' : '-ml-3 -mr-4'}`} />}\n </div>\n )\n })}\n </nav>\n </>\n )\n })()}\n </div>\n <div className=\"sticky bottom-0 bg-background pb-1\">\n {shouldRenderSidebarInjectionSpots ? (\n <InjectionSpot\n spotId={BACKEND_SIDEBAR_NAV_FOOTER_INJECTION_SPOT_ID}\n context={injectionContext}\n />\n ) : null}\n {shouldRenderSidebarInjectionSpots ? (\n <StatusBadgeInjectionSpot\n spotId={GLOBAL_SIDEBAR_STATUS_BADGES_INJECTION_SPOT_ID}\n context={injectionContext}\n />\n ) : null}\n {shouldRenderSidebarInjectionSpots ? (\n <InjectionSpot\n spotId={BACKEND_SIDEBAR_FOOTER_INJECTION_SPOT_ID}\n context={injectionContext}\n />\n ) : null}\n </div>\n </div>\n )\n }\n\n function renderSectionAside() {\n let sections: SectionNavGroup[] | null = null\n let title = ''\n if (sidebarMode === 'settings' && resolvedSettingsSections && resolvedSettingsSections.length > 0) {\n sections = mergeSectionGroupsWithInjected(\n resolvedSettingsSections,\n settingsSidebarInjectedMenuItems,\n t,\n )\n title = settingsSectionTitle ?? t('backend.nav.settings', 'Settings')\n } else if (sidebarMode === 'profile' && resolvedProfileSections && resolvedProfileSections.length > 0) {\n sections = mergeSectionGroupsWithInjected(\n resolvedProfileSections,\n profileSidebarInjectedMenuItems,\n t,\n )\n title = profileSectionTitle ?? t('backend.nav.profile', 'Profile')\n }\n if (!sections) return null\n return (\n <div className=\"flex h-full flex-col gap-2\">\n <Link\n href=\"/backend\"\n className=\"inline-flex items-center gap-2 rounded-lg px-2 py-2 text-sm font-semibold text-foreground transition-colors hover:bg-muted\"\n data-testid=\"appshell-section-back-to-main\"\n aria-label={t('backend.nav.backToMain', 'Back to Main')}\n >\n <ChevronLeft className=\"size-4 shrink-0\" aria-hidden />\n <span className=\"truncate\">{title}</span>\n </Link>\n <div className=\"min-h-0 flex-1\">\n {renderSectionSidebar(sections, title, false, true, true)}\n </div>\n </div>\n )\n }\n\n const isSectionView =\n (sidebarMode === 'settings' && !!resolvedSettingsSections && resolvedSettingsSections.length > 0) ||\n (sidebarMode === 'profile' && !!resolvedProfileSections && resolvedProfileSections.length > 0)\n const gridColsClass = isSectionView\n ? (effectiveCollapsed ? 'lg:grid-cols-[80px_240px_1fr]' : 'lg:grid-cols-[240px_240px_1fr]')\n : (effectiveCollapsed ? 'lg:grid-cols-[80px_1fr]' : 'lg:grid-cols-[240px_1fr]')\n const headerCtxValue = React.useMemo(() => ({\n setBreadcrumb: setHeaderBreadcrumb,\n setTitle: setHeaderTitle,\n }), [])\n const renderedTopbarInjectedActions = React.useMemo(\n () =>\n topbarInjectedMenuItems.map((item) => {\n const label = resolveInjectedMenuLabel(item, t)\n if (item.href) {\n return (\n <Link\n key={item.id}\n href={item.href}\n className=\"inline-flex items-center rounded border px-2 py-1 text-xs hover:bg-accent hover:text-accent-foreground\"\n data-menu-item-id={item.id}\n >\n {label}\n </Link>\n )\n }\n return (\n <Button\n key={item.id}\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n data-menu-item-id={item.id}\n onClick={() => item.onClick?.()}\n >\n {label}\n </Button>\n )\n }),\n [t, topbarInjectedMenuItems],\n )\n\n return (\n <HeaderContext.Provider value={headerCtxValue}>\n <div\n className={`relative min-h-svh lg:grid transition-[grid-template-columns] duration-200 ease-out ${gridColsClass}`}\n style={{ '--topbar-height': '61px' } as React.CSSProperties}\n >\n {/* Desktop sidebar collapse/expand toggle \u2014 sits on the divider line between\n sidebar and content, like Notion/Vercel. Hidden on mobile (hamburger in\n topbar handles the drawer). */}\n <button\n type=\"button\"\n onClick={() => setCollapsed((c) => !c)}\n aria-label={t('appShell.toggleSidebar')}\n className=\"hidden lg:flex fixed top-4 z-dropdown size-7 items-center justify-center rounded-md border bg-background text-muted-foreground shadow-sm transition-all hover:text-foreground hover:bg-muted focus:outline-none focus-visible:shadow-focus\"\n style={{ left: `calc(${asideWidth} - 14px)` }}\n >\n {effectiveCollapsed ? <PanelLeftOpen className=\"size-4\" /> : <PanelLeftClose className=\"size-4\" />}\n </button>\n {/* Desktop main sidebar */}\n <aside ref={sidebarAsideRef} className={`${asideClassesBase} ${effectiveCollapsed ? 'px-2' : 'px-3'} hidden lg:block lg:sticky lg:top-0 lg:h-svh lg:self-start lg:overflow-hidden lg:relative transition-[width,padding] duration-200 ease-out`} style={{ width: asideWidth }}>\n {renderSidebar(effectiveCollapsed, false, isSectionView)}\n {/* Scroll affordance \u2014 gradient fade + clickable chevron that flips up when\n the user reaches the bottom and disappears when nothing is scrollable\n (#1803). Clicking the chevron scrolls the inner sidebar container to\n top/bottom (`prefers-reduced-motion: reduce` collapses to instant\n scrolling). The wrapper is `pointer-events-none` so the gradient fade\n doesn't block hover/click on the rendered nav items behind it; the\n IconButton restores `pointer-events-auto` so it stays interactive. */}\n {sidebarScrollState !== 'none' ? (\n <div\n className=\"pointer-events-none absolute inset-x-0 bottom-0 flex h-10 items-end justify-center bg-gradient-to-t from-background via-background/80 to-transparent pb-1.5\"\n >\n {/* The IconButton owns hover/focus affordance; the inner span owns the\n rotate transition so it doesn't fight with the animate-bounce\n keyframes (both target `transform`). */}\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n data-testid=\"sidebar-scroll-chevron\"\n data-sidebar-scroll-chevron={sidebarScrollState}\n aria-label={\n sidebarScrollState === 'up'\n ? t('ui.sidebar.chevron.scrollTop', 'Scroll to top')\n : t('ui.sidebar.chevron.scrollBottom', 'Scroll to bottom')\n }\n className=\"pointer-events-auto text-muted-foreground/70 hover:text-foreground\"\n onClick={() => handleSidebarChevronScroll(sidebarScrollState === 'up' ? 'top' : 'bottom')}\n >\n <span\n className={`inline-flex transition-transform duration-300 ${sidebarScrollState === 'up' ? 'rotate-180' : ''}`}\n >\n <ChevronDown className=\"size-4 animate-bounce\" />\n </span>\n </IconButton>\n </div>\n ) : null}\n </aside>\n\n {/* Desktop section sidebar (Option B two-level) \u2014 sits beside the main sidebar\n when the user is on settings/profile routes. Mobile drawer keeps the\n original swap behavior to fit the narrow width. */}\n {isSectionView ? (\n <aside\n className={`${asideClassesBase} px-3 hidden lg:block lg:sticky lg:top-0 lg:h-svh lg:self-start lg:overflow-hidden lg:relative`}\n style={{ width: '240px' }}\n data-testid=\"appshell-section-sidebar\"\n >\n {renderSectionAside()}\n {/* Static bottom fade \u2014 covers the native iOS scroll indicator and signals\n that the section list is scrollable. Same look as the main sidebar's\n affordance but without the chevron / scroll-state machinery. */}\n <div\n aria-hidden\n className=\"pointer-events-none absolute inset-x-0 bottom-0 h-10 bg-gradient-to-t from-background via-background/80 to-transparent\"\n />\n </aside>\n ) : null}\n\n <div className=\"flex min-h-svh flex-col min-w-0\">\n <header className=\"sticky top-0 z-sticky border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/80 px-3 sm:px-4 lg:px-6 py-3 flex items-center justify-between gap-2 sm:gap-3\">\n <div\n data-testid=\"backend-chrome-ready\"\n data-ready={isChromeReady ? 'true' : 'false'}\n className=\"hidden\"\n />\n <div className=\"flex items-center gap-2 min-w-0\">\n {/* Mobile menu button */}\n <IconButton variant=\"ghost\" size=\"sm\" className=\"lg:hidden\" aria-label={t('appShell.openMenu')} onClick={() => setMobileOpen(true)}>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\"><path d=\"M3 6h18M3 12h18M3 18h18\"/></svg>\n </IconButton>\n {/* Header breadcrumb: always starts with Dashboard */}\n {(() => {\n const dashboardLabel = t('dashboard.title')\n const root: Breadcrumb = [{ label: dashboardLabel, href: '/backend' }]\n let rest: Breadcrumb = []\n if (headerBreadcrumb && headerBreadcrumb.length) {\n const first = headerBreadcrumb[0]\n const dup = first && (first.href === '/backend' || first.label === dashboardLabel || first.label?.toLowerCase() === 'dashboard')\n rest = dup ? headerBreadcrumb.slice(1) : headerBreadcrumb\n } else if (headerTitle) {\n rest = [{ label: headerTitle }]\n }\n const items = [...root, ...rest]\n if (items.length === 0) return null\n const home = items[0]\n const current = items.length > 1 ? items[items.length - 1] : null\n const mid = items.slice(1, -1)\n const hasMid = mid.length > 0\n return (\n <BreadcrumbNav divider=\"arrow\" className=\"ml-2 lg:ml-3 text-sm\">\n <BreadcrumbList className=\"[&_[data-slot=breadcrumb-separator]_svg]:size-4\">\n <BreadcrumbItem>\n {home.href && current ? (\n <BreadcrumbLink asChild aria-label={home.label}>\n <Link href={home.href}>\n <Home className=\"size-4\" aria-hidden=\"true\" />\n </Link>\n </BreadcrumbLink>\n ) : (\n <BreadcrumbPage aria-label={home.label}>\n <Home className=\"size-4\" aria-hidden=\"true\" />\n </BreadcrumbPage>\n )}\n </BreadcrumbItem>\n {current ? (\n <>\n {hasMid ? (\n <>\n <BreadcrumbSeparator className=\"md:hidden\" />\n <BreadcrumbItem className=\"md:hidden\">\n <BreadcrumbEllipsis aria-label={t('appShell.breadcrumb.collapsed', { count: mid.length })} />\n </BreadcrumbItem>\n {mid.map((b, i) => (\n <React.Fragment key={`mid-${i}`}>\n <BreadcrumbSeparator className=\"hidden md:inline-flex\" />\n <BreadcrumbItem className=\"hidden md:inline-flex\">\n {b.href ? (\n <BreadcrumbLink asChild title={b.label}>\n <Link href={b.href}>{b.label}</Link>\n </BreadcrumbLink>\n ) : (\n <BreadcrumbLink title={b.label} aria-disabled=\"true\" tabIndex={-1}>\n {b.label}\n </BreadcrumbLink>\n )}\n </BreadcrumbItem>\n </React.Fragment>\n ))}\n </>\n ) : null}\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbPage title={current.label}>{current.label}</BreadcrumbPage>\n </BreadcrumbItem>\n </>\n ) : null}\n </BreadcrumbList>\n </BreadcrumbNav>\n )\n })()}\n </div>\n <div className=\"flex items-center gap-1.5 sm:gap-2 md:gap-3 text-sm shrink-0\">\n <StatusBadgeInjectionSpot\n spotId={GLOBAL_HEADER_STATUS_INDICATORS_INJECTION_SPOT_ID}\n context={injectionContext}\n />\n <InjectionSpot\n spotId={BACKEND_TOPBAR_ACTIONS_INJECTION_SPOT_ID}\n context={injectionContext}\n />\n {renderedTopbarInjectedActions}\n <AiAssistantLauncher variant=\"topbar\" />\n {rightHeaderSlot ? (\n rightHeaderSlot\n ) : (\n <span className=\"opacity-80\">{email || t('appShell.userFallback')}</span>\n )}\n </div>\n </header>\n <ProgressTopBar t={t} className=\"sticky top-0 z-sticky\" completedAutoHideMs={progressCompletedAutoHideMs} />\n <main className=\"flex-1 p-4 lg:p-6 mx-auto w-full max-w-screen-2xl\">\n <InjectionSpot spotId={BACKEND_LAYOUT_TOP_INJECTION_SPOT_ID} context={injectionContext} />\n <FlashMessages />\n <PartialIndexBanner />\n {canManageUpgradeActions ? <UpgradeActionBanner /> : null}\n <LastOperationBanner />\n <RecordConflictBanner />\n <InjectionSpot spotId={BACKEND_RECORD_CURRENT_INJECTION_SPOT_ID} context={injectionContext} />\n <InjectionSpot\n spotId={LEGACY_GLOBAL_MUTATION_INJECTION_SPOT_ID}\n context={injectionContext}\n />\n <div id=\"om-top-banners\" className=\"mb-3 space-y-2\" />\n {children}\n <InjectionSpot spotId={BACKEND_LAYOUT_FOOTER_INJECTION_SPOT_ID} context={injectionContext} />\n </main>\n <footer className=\"border-t bg-background/80 backdrop-blur supports-[backdrop-filter]:bg-background/80 px-4 py-3 flex flex-wrap items-center justify-end gap-4\">\n {version ? (\n <span className=\"text-xs text-muted-foreground\">\n {t('appShell.version', { version })}\n </span>\n ) : null}\n <nav className=\"flex items-center gap-3 text-xs text-muted-foreground\">\n <Link href=\"/terms\" className=\"transition hover:text-foreground\">\n {t('common.terms')}\n </Link>\n <Link href=\"/privacy\" className=\"transition hover:text-foreground\">\n {t('common.privacy')}\n </Link>\n </nav>\n </footer>\n </div>\n\n {/* Mobile drawer */}\n {mobileOpen && (\n <div className=\"lg:hidden fixed inset-0 z-modal\">\n <div className=\"absolute inset-0 bg-black/50 backdrop-blur-sm\" onClick={() => setMobileOpen(false)} aria-hidden=\"true\" />\n <aside className=\"absolute left-0 top-0 flex h-full w-[280px] max-w-[85vw] flex-col bg-background border-r shadow-lg overflow-hidden\">\n <div className=\"shrink-0 flex items-center justify-between gap-2 border-b px-4 py-3\">\n <Link href=\"/backend\" className=\"flex items-center gap-2 min-w-0 text-sm font-semibold\" onClick={() => setMobileOpen(false)} aria-label={t('appShell.goToDashboard')}>\n <Image src={logo?.src ?? \"/open-mercato.svg\"} alt={logo?.alt ?? resolvedProductName} width={28} height={28} className=\"rounded shrink-0\" />\n <span className=\"truncate\">{resolvedProductName}</span>\n </Link>\n <IconButton variant=\"ghost\" size=\"sm\" onClick={() => setMobileOpen(false)} aria-label={t('appShell.closeMenu')}>\n <X className=\"size-4\" />\n </IconButton>\n </div>\n {mobileSidebarSlot && (\n <div className=\"shrink-0 border-b px-3 py-2\">\n {mobileSidebarSlot}\n </div>\n )}\n {sidebarMode !== 'main' ? (\n <div className=\"shrink-0 flex items-center gap-5 border-b px-4 pt-3 pb-0\" role=\"tablist\">\n {([\n { id: 'main' as const, label: t('backend.nav.main', 'Main') },\n {\n id: 'section' as const,\n label:\n sidebarMode === 'settings'\n ? settingsSectionTitle ?? t('backend.nav.settings', 'Settings')\n : profileSectionTitle ?? t('backend.nav.profile', 'Profile'),\n },\n ]).map((tab) => {\n const isActive =\n tab.id === 'main' ? mobileDrawerView === 'main' : mobileDrawerView === 'auto'\n const tabId = `mobile-drawer-tab-${tab.id}`\n return (\n <button\n key={tab.id}\n id={tabId}\n type=\"button\"\n role=\"tab\"\n aria-selected={isActive}\n aria-controls=\"mobile-drawer-tabpanel\"\n onClick={() => setMobileDrawerView(tab.id === 'main' ? 'main' : 'auto')}\n className=\"relative inline-flex items-center pb-2 text-sm font-medium leading-5 tracking-tight transition-colors focus:outline-none data-[active=true]:text-foreground data-[active=false]:text-muted-foreground hover:text-foreground\"\n data-active={isActive}\n >\n <span>{tab.label}</span>\n {isActive ? (\n <span\n className=\"absolute -bottom-px left-0 right-0 h-0.5 bg-foreground\"\n aria-hidden=\"true\"\n />\n ) : null}\n </button>\n )\n })}\n </div>\n ) : null}\n <div\n id=\"mobile-drawer-tabpanel\"\n role={sidebarMode !== 'main' ? 'tabpanel' : undefined}\n aria-labelledby={\n sidebarMode !== 'main'\n ? `mobile-drawer-tab-${mobileDrawerView === 'main' ? 'main' : 'section'}`\n : undefined\n }\n className=\"min-h-0 flex-1 overflow-y-auto overflow-x-hidden p-3\"\n >\n {/* Force expanded sidebar in mobile drawer, hide its header and collapse toggle */}\n {renderSidebar(false, true, mobileDrawerView === 'main')}\n </div>\n </aside>\n </div>\n )}\n </div>\n <UmesDevToolsPanel />\n </HeaderContext.Provider>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { createContext, useContext } from 'react'\nimport Link from 'next/link'\nimport Image from 'next/image'\nimport { ChevronDown, ChevronLeft, Home, PanelLeftClose, PanelLeftOpen, Search, X } from 'lucide-react'\nimport { Button } from '../primitives/button'\nimport {\n Breadcrumb as BreadcrumbNav,\n BreadcrumbEllipsis,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n} from '../primitives/breadcrumb'\nimport { IconButton } from '../primitives/icon-button'\nimport { Input } from '../primitives/input'\nimport { SearchInput } from '../primitives/search-input'\nimport { Checkbox } from '../primitives/checkbox'\nimport { Separator } from '../primitives/separator'\nimport { FlashMessages } from './FlashMessages'\nimport { QueryProvider } from '../theme/QueryProvider'\nimport { usePathname, useSearchParams } from 'next/navigation'\nimport { apiCall } from './utils/apiCall'\nimport { LastOperationBanner } from './operations/LastOperationBanner'\nimport { RecordConflictBanner } from './conflicts/RecordConflictBanner'\nimport { dismissRecordConflict } from './conflicts/store'\nimport { ProgressTopBar } from './progress/ProgressTopBar'\nimport { UpgradeActionBanner } from './upgrades/UpgradeActionBanner'\nimport { PartialIndexBanner } from './indexes/PartialIndexBanner'\nimport { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'\nimport { slugifySidebarId } from '@open-mercato/shared/modules/navigation/sidebarPreferences'\nimport { cloneSidebarGroups } from './sidebar/customization-helpers'\nimport type { SectionNavGroup } from './section-page/types'\nimport { InjectionSpot } from './injection/InjectionSpot'\nimport type { InjectionMenuItem } from '@open-mercato/shared/modules/widgets/injection'\nimport { LEGACY_GLOBAL_MUTATION_INJECTION_SPOT_ID } from './injection/mutationEvents'\nimport { mergeMenuItems } from './injection/mergeMenuItems'\nimport { useInjectedMenuItems } from './injection/useInjectedMenuItems'\nimport { resolveInjectedIcon } from './injection/resolveInjectedIcon'\nimport { useEventBridge } from './injection/eventBridge'\nimport { StatusBadgeInjectionSpot } from './injection/StatusBadgeInjectionSpot'\nimport { UmesDevToolsPanel } from './devtools'\nimport { AiDockProvider } from '../ai/AiDock'\nimport { AiChatSessionsProvider } from '../ai/AiChatSessions'\nimport { AiAssistantLauncher } from '../ai/AiAssistantLauncher'\nimport { BackendChromeProvider, useBackendChrome } from './BackendChromeProvider'\nimport {\n BACKEND_LAYOUT_FOOTER_INJECTION_SPOT_ID,\n BACKEND_LAYOUT_TOP_INJECTION_SPOT_ID,\n BACKEND_RECORD_CURRENT_INJECTION_SPOT_ID,\n BACKEND_SIDEBAR_FOOTER_INJECTION_SPOT_ID,\n BACKEND_SIDEBAR_TOP_INJECTION_SPOT_ID,\n BACKEND_SIDEBAR_NAV_FOOTER_INJECTION_SPOT_ID,\n BACKEND_SIDEBAR_NAV_INJECTION_SPOT_ID,\n BACKEND_TOPBAR_ACTIONS_INJECTION_SPOT_ID,\n GLOBAL_HEADER_STATUS_INDICATORS_INJECTION_SPOT_ID,\n GLOBAL_SIDEBAR_STATUS_BADGES_INJECTION_SPOT_ID,\n} from './injection/spotIds'\n\nexport type ShellLogo = {\n src: string\n alt?: string\n}\n\nexport type AppShellProps = {\n productName?: string\n logo?: ShellLogo\n email?: string\n canManageUpgradeActions?: boolean\n groups: {\n id?: string\n name: string\n defaultName?: string\n items: {\n id?: string\n href: string\n title: string\n defaultTitle?: string\n icon?: React.ReactNode\n iconName?: string\n iconMarkup?: string\n enabled?: boolean\n hidden?: boolean\n pageContext?: 'main' | 'admin' | 'settings' | 'profile'\n children?: {\n id?: string\n href: string\n title: string\n defaultTitle?: string\n icon?: React.ReactNode\n iconName?: string\n iconMarkup?: string\n enabled?: boolean\n hidden?: boolean\n pageContext?: 'main' | 'admin' | 'settings' | 'profile'\n }[]\n }[]\n }[]\n children: React.ReactNode\n rightHeaderSlot?: React.ReactNode\n sidebarCollapsedDefault?: boolean\n currentTitle?: string\n breadcrumb?: Array<{ label: string; href?: string }>\n // Optional: full admin nav API to refresh sidebar client-side\n adminNavApi?: string\n version?: string\n settingsSectionTitle?: string\n settingsPathPrefixes?: string[]\n settingsSections?: SectionNavGroup[]\n profileSections?: SectionNavGroup[]\n profileSectionTitle?: string\n profilePathPrefixes?: string[]\n mobileSidebarSlot?: React.ReactNode\n /**\n * How long (ms) to keep successfully completed progress operations visible\n * before auto-hiding. Pass `false` or `0` to disable. Defaults to 10 000 ms.\n */\n progressCompletedAutoHideMs?: number | false\n}\n\ntype Breadcrumb = Array<{ label: string; href?: string }>\n\ntype SidebarGroup = AppShellProps['groups'][number]\ntype SidebarItem = SidebarGroup['items'][number]\n\nfunction convertInjectedMenuItemToSidebarItem(item: InjectionMenuItem, title: string): SidebarItem | null {\n if (!item.href) return null\n return {\n id: item.id,\n href: item.href,\n title,\n defaultTitle: title,\n icon: resolveInjectedIcon(item.icon) ?? undefined,\n iconName: item.icon,\n enabled: true,\n hidden: false,\n pageContext: 'main',\n }\n}\n\nfunction resolveInjectedMenuLabel(\n item: { id: string; label?: string; labelKey?: string },\n t: (key: string, fallback?: string) => string,\n): string {\n if (item.labelKey && item.label) return t(item.labelKey, item.label)\n if (item.labelKey) return t(item.labelKey, item.id)\n if (item.label && item.label.includes('.')) return t(item.label, item.id)\n return item.label ?? item.id\n}\n\nfunction shouldBypassLogoOptimization(src?: string | null): boolean {\n const value = src ?? ''\n return /^https?:\\/\\//.test(value) || /^\\/api\\/attachments\\/(?:image|file)\\//.test(value)\n}\n\nfunction mergeSidebarItemsWithInjected(\n items: SidebarItem[],\n injectedItems: InjectionMenuItem[],\n t: (key: string, fallback?: string) => string,\n): SidebarItem[] {\n if (injectedItems.length === 0) return items\n\n const builtInById = new Map<string, SidebarItem>()\n for (const item of items) {\n builtInById.set(item.id ?? item.href, item)\n }\n\n const merged = mergeMenuItems(\n items.map((item) => ({\n id: item.id ?? item.href,\n })),\n injectedItems,\n )\n\n const result: SidebarItem[] = []\n for (const entry of merged) {\n if (entry.source === 'built-in') {\n const original = builtInById.get(entry.id)\n if (original) result.push(original)\n continue\n }\n const translatedLabel = resolveInjectedMenuLabel(\n { id: entry.id, label: entry.label, labelKey: entry.labelKey },\n t,\n )\n const converted = convertInjectedMenuItemToSidebarItem(\n {\n id: entry.id,\n label: translatedLabel,\n icon: entry.icon,\n href: entry.href,\n },\n translatedLabel,\n )\n if (converted) result.push(converted)\n }\n\n return result\n}\n\nfunction mergeSidebarGroupsWithInjected(\n groups: SidebarGroup[],\n injectedItems: InjectionMenuItem[],\n t: (key: string, fallback?: string) => string,\n): SidebarGroup[] {\n if (injectedItems.length === 0) return groups\n\n const injectedByGroup = new Map<string, InjectionMenuItem[]>()\n const ungrouped: InjectionMenuItem[] = []\n\n for (const item of injectedItems) {\n if (item.groupId && item.groupId.trim().length > 0) {\n const groupItems = injectedByGroup.get(item.groupId) ?? []\n groupItems.push(item)\n injectedByGroup.set(item.groupId, groupItems)\n continue\n }\n ungrouped.push(item)\n }\n\n const nextGroups = groups.map((group, index) => {\n const groupId = group.id || resolveGroupKey(group)\n const groupInjected = [\n ...(injectedByGroup.get(groupId) ?? []),\n ...(index === 0 ? ungrouped : []),\n ]\n return {\n ...group,\n items: mergeSidebarItemsWithInjected(group.items, groupInjected, t),\n }\n })\n\n const existingIds = new Set(nextGroups.map((group) => group.id || resolveGroupKey(group)))\n for (const [groupId, items] of injectedByGroup.entries()) {\n if (existingIds.has(groupId)) continue\n const first = items[0]\n const label = first.groupLabelKey\n ? t(first.groupLabelKey, first.groupLabel ?? groupId)\n : (first.groupLabel ?? groupId)\n const groupItems = mergeSidebarItemsWithInjected([], items, t)\n if (groupItems.length === 0) continue\n nextGroups.push({\n id: groupId,\n name: label,\n defaultName: label,\n items: groupItems,\n })\n }\n\n return nextGroups\n}\n\nfunction mergeSectionGroupsWithInjected(\n sections: SectionNavGroup[],\n injectedItems: InjectionMenuItem[],\n t: (key: string, fallback?: string) => string,\n): SectionNavGroup[] {\n if (injectedItems.length === 0) return sections\n const byGroup = new Map<string, InjectionMenuItem[]>()\n for (const item of injectedItems) {\n const groupId = item.groupId && item.groupId.trim().length > 0 ? item.groupId : 'injected'\n const bucket = byGroup.get(groupId) ?? []\n bucket.push(item)\n byGroup.set(groupId, bucket)\n }\n\n const nextSections = sections.map((section) => {\n const sectionItems = byGroup.get(section.id) ?? []\n if (sectionItems.length === 0) return section\n const mergedItems = mergeMenuItems(\n section.items.map((item) => ({ id: item.id, item })),\n sectionItems,\n ).flatMap((item) => {\n if (item.source === 'built-in') {\n const original = section.items.find((entry) => entry.id === item.id)\n return original ? [original] : []\n }\n if (!item.href) return []\n const label = resolveInjectedMenuLabel(item, t)\n return [{\n id: item.id,\n label,\n href: item.href,\n icon: resolveInjectedIcon(item.icon) ?? undefined,\n }]\n })\n return {\n ...section,\n items: mergedItems,\n }\n })\n\n for (const [sectionId, sectionItems] of byGroup.entries()) {\n const exists = nextSections.some((section) => section.id === sectionId)\n if (exists) continue\n const first = sectionItems[0]\n const label = first.groupLabelKey\n ? t(first.groupLabelKey, first.groupLabel ?? sectionId)\n : (first.groupLabel ?? sectionId)\n const items = sectionItems.flatMap((item) => {\n if (!item.href) return []\n const itemLabel = resolveInjectedMenuLabel(item, t)\n return [{\n id: item.id,\n label: itemLabel,\n href: item.href,\n icon: resolveInjectedIcon(item.icon) ?? undefined,\n }]\n })\n if (items.length === 0) continue\n nextSections.push({ id: sectionId, label, items })\n }\n\n return nextSections\n}\n\nfunction resolveGroupKey(group: SidebarGroup): string {\n if (group.id && group.id.length) return group.id\n if (group.defaultName && group.defaultName.length) return slugifySidebarId(group.defaultName)\n return slugifySidebarId(group.name)\n}\n\nfunction resolveItemKey(item: { id?: string; href: string }): string {\n const candidate = item.id?.trim()\n if (candidate && candidate.length > 0) return candidate\n return item.href\n}\n\nfunction SerializedIcon({ markup }: { markup: string }) {\n return <span aria-hidden=\"true\" dangerouslySetInnerHTML={{ __html: markup }} />\n}\n\nfunction renderIcon(\n icon: React.ReactNode | undefined,\n iconName: string | undefined,\n iconMarkup: string | undefined,\n fallback: React.ReactNode,\n) {\n if (icon) return icon\n if (iconName) {\n const resolved = resolveInjectedIcon(iconName)\n if (resolved) return resolved\n }\n if (iconMarkup) return <SerializedIcon markup={iconMarkup} />\n return fallback\n}\n\nconst HeaderContext = createContext<{\n setBreadcrumb: (b?: Breadcrumb) => void\n setTitle: (t?: string) => void\n} | null>(null)\n\nexport function ApplyBreadcrumb({ breadcrumb, title, titleKey }: { breadcrumb?: Array<{ label: string; href?: string; labelKey?: string }>; title?: string; titleKey?: string }) {\n const ctx = useContext(HeaderContext)\n const t = useT()\n const resolvedBreadcrumb = React.useMemo<Breadcrumb | undefined>(() => {\n if (!breadcrumb) return undefined\n return breadcrumb.map(({ label, labelKey, href }) => {\n const translated = labelKey ? t(labelKey) : undefined\n const finalLabel = translated && translated !== labelKey ? translated : label\n return {\n href,\n label: finalLabel,\n }\n })\n }, [breadcrumb, t])\n const resolvedTitle = React.useMemo(() => {\n if (!titleKey) return title\n const translated = t(titleKey)\n if (translated && translated !== titleKey) return translated\n return title\n }, [titleKey, title, t])\n React.useEffect(() => {\n ctx?.setBreadcrumb(resolvedBreadcrumb)\n if (resolvedTitle !== undefined) ctx?.setTitle(resolvedTitle)\n }, [ctx, resolvedBreadcrumb, resolvedTitle])\n return null\n}\n\nconst DefaultIcon = (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M8 6h13M8 12h13M8 18h13\"/>\n <path d=\"M3 6h.01M3 12h.01M3 18h.01\"/>\n </svg>\n)\n\n// DataTable icon used for dynamic custom entity records links\nconst DataTableIcon = (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"16\" rx=\"2\" ry=\"2\"/>\n <line x1=\"3\" y1=\"8\" x2=\"21\" y2=\"8\"/>\n <line x1=\"9\" y1=\"8\" x2=\"9\" y2=\"20\"/>\n <line x1=\"15\" y1=\"8\" x2=\"15\" y2=\"20\"/>\n </svg>\n)\n\nconst CustomizeIcon = (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n <path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.05.05a2 2 0 1 1-2.83 2.83l-.05-.05A1.65 1.65 0 0 0 15 19.4a1.65 1.65 0 0 0-1 .6 1.65 1.65 0 0 0-.33 1.82l-.05.05a2 2 0 1 1-2.83-2.83l.05-.05A1.65 1.65 0 0 0 9 15a1.65 1.65 0 0 0-1-.6 1.65 1.65 0 0 0-1.82.33l-.05.05a2 2 0 1 1-2.83-2.83l.05-.05A1.65 1.65 0 0 0 4.6 9 1.65 1.65 0 0 0 4 8a1.65 1.65 0 0 0-.6-1.82l-.05-.05a2 2 0 1 1 2.83-2.83l.05.05A1.65 1.65 0 0 0 9 4.6a1.65 1.65 0 0 0 1-.6 1.65 1.65 0 0 0 .33-1.82l.05-.05a2 2 0 1 1 2.83 2.83l-.05.05A1.65 1.65 0 0 0 15 9a1.65 1.65 0 0 0 1 .6 1.65 1.65 0 0 0 1.82-.33l.05-.05a2 2 0 1 1 2.83 2.83l-.05.05A1.65 1.65 0 0 0 19.4 15z\" />\n </svg>\n)\n\nconst BackArrowIcon = (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M19 12H5M12 19l-7-7 7-7\" />\n </svg>\n)\n\nfunction Chevron({ open }: { open: boolean }) {\n return (\n <svg className={`transition-transform ${open ? 'rotate-180' : ''}`} width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\"><path d=\"M6 9l6 6 6-6\"/></svg>\n )\n}\n\nexport function AppShell(props: AppShellProps) {\n return (\n <QueryProvider>\n <BackendChromeProvider adminNavApi={props.adminNavApi}>\n <AiChatSessionsProvider>\n <AiDockProvider>\n <AppShellBody {...props} />\n </AiDockProvider>\n </AiChatSessionsProvider>\n </BackendChromeProvider>\n </QueryProvider>\n )\n}\n\nfunction AppShellBody({ productName, logo, email, canManageUpgradeActions = false, groups, rightHeaderSlot, children, sidebarCollapsedDefault = false, currentTitle, breadcrumb, version, settingsSectionTitle, settingsPathPrefixes = [], settingsSections, profileSections, profileSectionTitle, profilePathPrefixes = [], mobileSidebarSlot, progressCompletedAutoHideMs }: AppShellProps) {\n const pathname = usePathname()\n const searchParams = useSearchParams()\n const t = useT()\n const locale = useLocale()\n const { payload: chromePayload, isReady: isChromeReady, isLoading: isChromeLoading } = useBackendChrome()\n const resolvedGroups = React.useMemo(\n () => cloneSidebarGroups(chromePayload?.groups ?? groups),\n [chromePayload?.groups, groups],\n )\n const resolvedSettingsSections = chromePayload?.settingsSections ?? settingsSections\n const resolvedSettingsPathPrefixes = chromePayload?.settingsPathPrefixes ?? settingsPathPrefixes\n const resolvedProfileSections = chromePayload?.profileSections ?? profileSections\n const resolvedProfilePathPrefixes = chromePayload?.profilePathPrefixes ?? profilePathPrefixes\n const { items: mainSidebarInjectedMenuItems } = useInjectedMenuItems('menu:sidebar:main')\n const { items: settingsSidebarInjectedMenuItems } = useInjectedMenuItems('menu:sidebar:settings')\n const { items: profileSidebarInjectedMenuItems } = useInjectedMenuItems('menu:sidebar:profile')\n const { items: topbarInjectedMenuItems } = useInjectedMenuItems('menu:topbar:actions')\n useEventBridge() // SSE DOM Event Bridge \u2014 singleton SSE connection for real-time server events\n const resolvedProductName = productName ?? t('appShell.productName')\n const resolvedLogo = chromePayload?.brand?.logo?.src ? chromePayload.brand.logo : logo\n const resolvedBrandName = chromePayload?.brand?.logo?.src\n ? chromePayload.brand.name ?? resolvedProductName\n : resolvedProductName\n const resolvedLogoBypassesOptimization = shouldBypassLogoOptimization(resolvedLogo?.src)\n const [mobileOpen, setMobileOpen] = React.useState(false)\n // When the mobile drawer opens on a settings/profile route, it follows the\n // section sidebar by default. Set to 'main' to force-show the main nav even\n // when the route is in a section context. Reset on close.\n const [mobileDrawerView, setMobileDrawerView] = React.useState<'auto' | 'main'>('auto')\n // Clear the persistent record-conflict bar when the route changes. The\n // conflict is scoped to the record the user was editing, so navigating to an\n // unrelated page should dismiss it instead of carrying a stale \"Record\n // changed\" bar across modules.\n React.useEffect(() => {\n dismissRecordConflict()\n }, [pathname])\n React.useEffect(() => {\n if (!mobileOpen) setMobileDrawerView('auto')\n }, [mobileOpen])\n // Initialize from server-provided prop only to avoid hydration flicker\n const [collapsed, setCollapsed] = React.useState(sidebarCollapsedDefault)\n // Maintain internal nav state so we can augment it client-side\n const [navGroups, setNavGroups] = React.useState(resolvedGroups)\n const [openGroups, setOpenGroups] = React.useState<Record<string, boolean>>(() =>\n Object.fromEntries(resolvedGroups.map((g) => [resolveGroupKey(g), true])) as Record<string, boolean>\n )\n const [headerTitle, setHeaderTitle] = React.useState<string | undefined>(currentTitle)\n const [headerBreadcrumb, setHeaderBreadcrumb] = React.useState<Breadcrumb | undefined>(breadcrumb)\n const [navQuery, setNavQuery] = React.useState('')\n const navQueryNorm = navQuery.trim().toLowerCase()\n const navQueryActive = navQueryNorm.length > 0\n const matchesQuery = React.useCallback((label: string | undefined) => {\n if (!navQueryActive) return true\n if (!label) return false\n return label.toLowerCase().includes(navQueryNorm)\n }, [navQueryActive, navQueryNorm])\n const effectiveCollapsed = collapsed\n const expandedSidebarWidth = '240px'\n\n // Track scroll position of the desktop sidebar's inner scroll container so we can\n // flip the affordance chevron between down/up (and hide it entirely when content\n // fits without scrolling). The inner div is rendered deep in renderSidebar /\n // renderSectionSidebar \u2014 we tag it with `data-sidebar-scroll=\"true\"` and look it\n // up via the aside ref so we don't have to thread refs through the JSX tree.\n const sidebarAsideRef = React.useRef<HTMLElement>(null)\n const [sidebarScrollState, setSidebarScrollState] = React.useState<'down' | 'up' | 'none'>('down')\n const sidebarScrollIntentRef = React.useRef<'top' | 'bottom' | null>(null)\n\n // Click-to-scroll handler for the sidebar affordance chevron (#1803). Resolves the\n // scroll target lazily through the aside ref so we don't have to thread refs into\n // renderSidebar; respects `prefers-reduced-motion` by falling back to instant\n // scrolling when the user has opted out of smooth motion.\n const handleSidebarChevronScroll = React.useCallback((target: 'top' | 'bottom') => {\n const aside = sidebarAsideRef.current\n if (!aside) return\n const scrollTarget = aside.querySelector<HTMLElement>('[data-sidebar-scroll=\"true\"]')\n if (!scrollTarget) return\n const prefersReducedMotion =\n typeof window !== 'undefined' &&\n typeof window.matchMedia === 'function' &&\n window.matchMedia('(prefers-reduced-motion: reduce)').matches\n const behavior: ScrollBehavior = prefersReducedMotion ? 'auto' : 'smooth'\n const maxScrollTop = Math.max(0, scrollTarget.scrollHeight - scrollTarget.clientHeight)\n if (maxScrollTop <= 1) {\n sidebarScrollIntentRef.current = null\n setSidebarScrollState('none')\n return\n }\n sidebarScrollIntentRef.current = target\n setSidebarScrollState(target === 'bottom' ? 'up' : 'down')\n scrollTarget.scrollTo({\n top: target === 'top' ? 0 : maxScrollTop,\n behavior,\n })\n }, [])\n React.useEffect(() => {\n const aside = sidebarAsideRef.current\n if (!aside) return\n const target = aside.querySelector<HTMLElement>('[data-sidebar-scroll=\"true\"]')\n if (!target) return\n const update = () => {\n const { scrollTop, scrollHeight, clientHeight } = target\n const canScroll = scrollHeight > clientHeight + 1\n if (!canScroll) {\n sidebarScrollIntentRef.current = null\n setSidebarScrollState('none')\n return\n }\n const maxScrollTop = Math.max(0, scrollHeight - clientHeight)\n const atTop = scrollTop <= 8\n const atBottom = scrollTop >= maxScrollTop - 8\n const scrollIntent = sidebarScrollIntentRef.current\n if (scrollIntent === 'bottom') {\n if (atBottom) sidebarScrollIntentRef.current = null\n setSidebarScrollState('up')\n return\n }\n if (scrollIntent === 'top') {\n if (atTop) sidebarScrollIntentRef.current = null\n setSidebarScrollState('down')\n return\n }\n setSidebarScrollState(atBottom ? 'up' : 'down')\n }\n update()\n target.addEventListener('scroll', update, { passive: true })\n const ro = typeof ResizeObserver !== 'undefined' ? new ResizeObserver(update) : null\n ro?.observe(target)\n return () => {\n target.removeEventListener('scroll', update)\n ro?.disconnect()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [pathname, effectiveCollapsed])\n const injectionContext = React.useMemo(\n () => ({\n path: pathname ?? '',\n query: searchParams?.toString() ?? '',\n }),\n [pathname, searchParams],\n )\n\n const isOnSettingsPath = React.useMemo(() => {\n if (!pathname) return false\n if (pathname === '/backend/settings') return true\n return resolvedSettingsPathPrefixes.some((prefix) => pathname.startsWith(prefix))\n }, [pathname, resolvedSettingsPathPrefixes])\n\n const isOnProfilePath = React.useMemo(() => {\n if (!pathname) return false\n if (pathname === '/backend/profile') return true\n return resolvedProfilePathPrefixes.some((prefix) => pathname.startsWith(prefix))\n }, [pathname, resolvedProfilePathPrefixes])\n\n const sidebarMode: 'main' | 'settings' | 'profile' =\n isOnSettingsPath ? 'settings' :\n isOnProfilePath ? 'profile' :\n 'main'\n\n const mainNavGroupsWithInjected = React.useMemo(\n () => mergeSidebarGroupsWithInjected(navGroups, mainSidebarInjectedMenuItems, t),\n [mainSidebarInjectedMenuItems, navGroups, t],\n )\n\n // Lock body scroll when mobile drawer is open so touch scroll stays in the drawer\n React.useEffect(() => {\n if (!mobileOpen || typeof document === 'undefined') return\n const prev = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n return () => {\n document.body.style.overflow = prev\n }\n }, [mobileOpen])\n\n React.useEffect(() => {\n try {\n const savedOpen = typeof window !== 'undefined' ? localStorage.getItem('om:sidebarOpenGroups') : null\n if (!savedOpen) return\n const parsed = JSON.parse(savedOpen) as Record<string, boolean>\n setOpenGroups((prev) => {\n const next = { ...prev }\n for (const group of resolvedGroups) {\n const key = resolveGroupKey(group)\n if (key in parsed) next[key] = !!parsed[key]\n else if (group.name in parsed) next[key] = !!parsed[group.name]\n }\n return next\n })\n } catch {\n // ignore localStorage errors to avoid breaking hydration\n }\n }, [resolvedGroups])\n\n const toggleGroup = (groupId: string) => setOpenGroups((prev) => ({ ...prev, [groupId]: prev[groupId] === false }))\n\n const asideWidth = effectiveCollapsed ? '80px' : expandedSidebarWidth\n // Use min-h-svh so the border extends with tall content; no overflow so sticky bottom works\n const asideClassesBase = `border-r bg-background py-4`;\n\n // Persist collapse state to localStorage and cookie. Both writes can throw in\n // private/incognito mode (storage blocked) or when cookies are disabled \u2014\n // the persisted preference is purely a UX nice-to-have, never functional, so\n // swallow the failure and let the component fall back to the default state.\n React.useEffect(() => {\n try { localStorage.setItem('om:sidebarCollapsed', collapsed ? '1' : '0') } catch { /* localStorage blocked (private mode) \u2014 non-critical */ }\n try {\n document.cookie = `om_sidebar_collapsed=${collapsed ? '1' : '0'}; path=/; max-age=31536000; samesite=lax`\n } catch { /* cookies disabled \u2014 non-critical */ }\n }, [collapsed])\n\n // Two-level sidebar (Option B): when entering settings/profile mode, force the\n // main sidebar to collapsed (icons only) so the section sub-nav can sit beside\n // it; restore the user's previous expansion when returning to the main mode.\n // Initial ref is 'main' so direct mounts on /backend/settings also auto-collapse.\n const collapsedBeforeSectionRef = React.useRef<boolean | null>(null)\n const previousSidebarModeRef = React.useRef<'main' | 'settings' | 'profile'>('main')\n React.useEffect(() => {\n const previous = previousSidebarModeRef.current\n if (previous === 'main' && sidebarMode !== 'main') {\n collapsedBeforeSectionRef.current = collapsed\n if (!collapsed) setCollapsed(true)\n } else if (previous !== 'main' && sidebarMode === 'main' && collapsedBeforeSectionRef.current !== null) {\n const restoreTo = collapsedBeforeSectionRef.current\n collapsedBeforeSectionRef.current = null\n if (collapsed !== restoreTo) setCollapsed(restoreTo)\n }\n previousSidebarModeRef.current = sidebarMode\n }, [sidebarMode, collapsed])\n React.useEffect(() => {\n try { localStorage.setItem('om:sidebarOpenGroups', JSON.stringify(openGroups)) } catch { /* localStorage blocked (private mode) \u2014 non-critical */ }\n }, [openGroups])\n\n // Ensure current route's group is expanded on load\n React.useEffect(() => {\n const activeGroup = navGroups.find((g) => g.items.some((i) => pathname?.startsWith(i.href)))\n if (!activeGroup) return\n const key = resolveGroupKey(activeGroup)\n setOpenGroups((prev) => (prev[key] === false ? { ...prev, [key]: true } : prev))\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [pathname, navGroups])\n // Keep header state in sync with props (server-side updates)\n React.useEffect(() => {\n setHeaderTitle(currentTitle)\n setHeaderBreadcrumb(breadcrumb)\n }, [currentTitle, breadcrumb])\n // Clear breadcrumb on client-side navigation so stale state doesn't persist;\n // the new page's ApplyBreadcrumb (if any) will set the correct values\n const prevPathname = React.useRef(pathname)\n React.useEffect(() => {\n if (pathname !== prevPathname.current) {\n prevPathname.current = pathname\n setHeaderTitle(undefined)\n setHeaderBreadcrumb(undefined)\n }\n }, [pathname])\n\n // Keep navGroups in sync when server-provided groups change\n React.useEffect(() => {\n setNavGroups(cloneSidebarGroups(resolvedGroups))\n }, [resolvedGroups])\n\n function renderSectionSidebar(\n sections: SectionNavGroup[],\n title: string,\n compact: boolean,\n hideHeader?: boolean,\n hideSearch?: boolean\n ) {\n const sortedSections = [...sections].sort((a, b) => (a.order ?? 0) - (b.order ?? 0))\n const lastVisibleIndex = sortedSections.length - 1\n\n return (\n <div className=\"flex h-full flex-col gap-3\">\n {!hideHeader && (\n <div className=\"mb-2\">\n <Link\n href=\"/backend\"\n className={`flex items-center gap-3 rounded-xl transition-colors hover:bg-muted ${compact ? 'p-2 justify-center' : 'p-3'}`}\n aria-label={t('appShell.goToDashboard')}\n >\n <Image src={resolvedLogo?.src ?? \"/open-mercato.svg\"} alt={resolvedLogo?.alt ?? resolvedBrandName} width={40} height={40} className=\"rounded-full shrink-0\" unoptimized={resolvedLogoBypassesOptimization ? true : undefined} />\n {!compact && <span className=\"truncate text-sm font-medium text-foreground\">{resolvedBrandName}</span>}\n </Link>\n </div>\n )}\n {!compact && !hideSearch && (\n <SearchInput\n value={navQuery}\n onChange={setNavQuery}\n placeholder={t('appShell.searchNavPlaceholder', 'Search...')}\n aria-label={t('appShell.searchNavAria', 'Search navigation')}\n clearLabel={t('appShell.searchNavClear', 'Clear search')}\n className=\"mb-2\"\n />\n )}\n <div data-sidebar-scroll=\"true\" className={`flex flex-1 flex-col gap-3 overflow-y-auto scrollbar-hide pr-1 ${compact ? '-ml-2 pl-2' : '-ml-3 pl-3'}`}>\n <nav className=\"flex flex-col gap-2\">\n {sortedSections.map((section, sectionIndex) => {\n const sectionNavQueryActive = hideSearch ? false : navQueryActive\n const matchesItemQuery = (item: typeof section.items[number]): boolean => {\n if (!sectionNavQueryActive) return true\n const label = item.labelKey ? t(item.labelKey, item.label) : item.label\n if (matchesQuery(label)) return true\n return Array.isArray(item.children) && item.children.some(matchesItemQuery)\n }\n const visibleItems = sectionNavQueryActive\n ? section.items.filter(matchesItemQuery)\n : section.items\n if (visibleItems.length === 0) return null\n const sortedItems = [...visibleItems].sort((a, b) => (a.order ?? 0) - (b.order ?? 0))\n const sectionLabel = section.labelKey ? t(section.labelKey, section.label) : section.label\n const sectionKey = `settings:${section.id}`\n const open = openGroups[sectionKey] !== false\n const sortSectionItems = (items: typeof section.items = []) =>\n [...items].sort((a, b) => (a.order ?? 0) - (b.order ?? 0))\n const filterChildren = (children: typeof section.items | undefined) => {\n if (!children) return [] as typeof section.items\n if (!sectionNavQueryActive) return [...children]\n return children.filter(matchesItemQuery)\n }\n\n const renderSectionItem = (item: (typeof section.items)[number], depth = 0): React.ReactNode => {\n const label = item.labelKey ? t(item.labelKey, item.label) : item.label\n const childItems = sortSectionItems(filterChildren(item.children))\n const isOnItemBranch = !!pathname && (\n pathname === item.href ||\n pathname.startsWith(`${item.href}/`)\n )\n const hasActiveChild = !!(pathname && childItems.some((child) => (\n pathname === child.href ||\n pathname.startsWith(`${child.href}/`)\n )))\n const showChildren = childItems.length > 0 && (isOnItemBranch || sectionNavQueryActive)\n const isActive = isOnItemBranch || hasActiveChild\n const base = compact ? 'w-10 h-10 justify-center' : 'w-full py-2 gap-2'\n const spacingStyle = !compact\n ? {\n paddingLeft: `${12 + depth * 16}px`,\n paddingRight: '12px',\n }\n : undefined\n\n return (\n <React.Fragment key={item.id}>\n <Link\n href={item.href}\n className={`relative text-sm font-medium rounded-lg inline-flex items-center ${base} ${\n isActive\n ? 'bg-muted text-foreground'\n : 'text-muted-foreground hover:bg-muted'\n }`}\n style={spacingStyle}\n title={compact ? label : undefined}\n data-menu-item-id={item.id}\n onClick={() => setMobileOpen(false)}\n >\n {isActive && (\n <span aria-hidden className={`absolute ${compact ? 'left-[-20px]' : 'left-[-12px]'} top-2 w-1 h-5 rounded-r bg-foreground`} />\n )}\n <span className=\"flex items-center justify-center shrink-0\">\n {renderIcon(\n item.icon,\n item.iconName,\n item.iconMarkup,\n item.href.includes('/backend/entities/user/') && item.href.endsWith('/records') ? DataTableIcon : DefaultIcon,\n )}\n </span>\n {!compact && <span className=\"truncate\">{label}</span>}\n </Link>\n {showChildren ? childItems.map((child) => renderSectionItem(child, depth + 1)) : null}\n </React.Fragment>\n )\n }\n\n return (\n <div key={section.id}>\n {!compact && (\n <Button\n variant=\"muted\"\n onClick={() => toggleGroup(sectionKey)}\n className=\"w-full px-1 justify-between flex text-xs font-medium uppercase tracking-wider text-muted-foreground/70 py-1\"\n aria-expanded={open}\n >\n <span>{sectionLabel}</span>\n <Chevron open={open} />\n </Button>\n )}\n {(open || compact) && (\n <div className={`flex flex-col ${compact ? 'items-center' : ''} gap-1`}>\n {sortedItems.map((item) => renderSectionItem(item))}\n </div>\n )}\n {sectionIndex !== lastVisibleIndex && <div className={`my-2 border-t ${compact ? '-ml-2 -mr-3' : '-ml-3 -mr-4'}`} />}\n </div>\n )\n })}\n </nav>\n </div>\n </div>\n )\n }\n\n function renderSidebar(compact: boolean, hideHeader?: boolean, forceMainOnly?: boolean) {\n if (!isChromeReady && isChromeLoading) {\n return (\n <div className=\"flex flex-col min-h-full gap-3\" data-testid=\"backend-chrome-loading\">\n {!hideHeader ? (\n <div className=\"mb-2\">\n <Link\n href=\"/backend\"\n className={`flex items-center gap-3 rounded-xl transition-colors hover:bg-muted ${compact ? 'p-2 justify-center' : 'p-3'}`}\n aria-label={t('appShell.goToDashboard')}\n >\n <Image src={resolvedLogo?.src ?? \"/open-mercato.svg\"} alt={resolvedLogo?.alt ?? resolvedBrandName} width={40} height={40} className=\"rounded-full shrink-0\" unoptimized={resolvedLogoBypassesOptimization ? true : undefined} />\n {!compact && <span className=\"truncate text-sm font-medium text-foreground\">{resolvedBrandName}</span>}\n </Link>\n </div>\n ) : null}\n <div className=\"flex flex-1 flex-col gap-3 pr-1\">\n <div className=\"space-y-3\">\n <div className=\"h-8 rounded bg-muted/50\" />\n <div className=\"space-y-2 pl-1\">\n <div className=\"h-8 rounded bg-muted/50\" />\n <div className=\"h-8 rounded bg-muted/50\" />\n <div className=\"h-8 rounded bg-muted/50\" />\n </div>\n </div>\n <div className=\"space-y-3\">\n <div className=\"h-8 rounded bg-muted/50\" />\n <div className=\"space-y-2 pl-1\">\n <div className=\"h-8 rounded bg-muted/50\" />\n <div className=\"h-8 rounded bg-muted/50\" />\n </div>\n </div>\n </div>\n </div>\n )\n }\n\n if (!forceMainOnly && sidebarMode === 'settings' && resolvedSettingsSections && resolvedSettingsSections.length > 0) {\n const mergedSettingsSections = mergeSectionGroupsWithInjected(\n resolvedSettingsSections,\n settingsSidebarInjectedMenuItems,\n t,\n )\n return renderSectionSidebar(\n mergedSettingsSections,\n settingsSectionTitle ?? t('backend.nav.settings', 'Settings'),\n compact,\n hideHeader\n )\n }\n\n if (!forceMainOnly && sidebarMode === 'profile' && resolvedProfileSections && resolvedProfileSections.length > 0) {\n const mergedProfileSections = mergeSectionGroupsWithInjected(\n resolvedProfileSections,\n profileSidebarInjectedMenuItems,\n t,\n )\n return renderSectionSidebar(\n mergedProfileSections,\n profileSectionTitle ?? t('backend.nav.profile', 'Profile'),\n compact,\n hideHeader\n )\n }\n\n const isMobileVariant = !!hideHeader\n const shouldRenderSidebarInjectionSpots = !isMobileVariant\n\n return (\n <div className=\"flex h-full flex-col gap-3\">\n {!hideHeader && (\n <div className=\"mb-2\">\n <Link\n href=\"/backend\"\n className={`flex items-center gap-3 rounded-xl transition-colors hover:bg-muted ${compact ? 'p-2 justify-center' : 'p-3'}`}\n aria-label={t('appShell.goToDashboard')}\n >\n <Image src={resolvedLogo?.src ?? \"/open-mercato.svg\"} alt={resolvedLogo?.alt ?? resolvedBrandName} width={40} height={40} className=\"rounded-full shrink-0\" unoptimized={resolvedLogoBypassesOptimization ? true : undefined} />\n {!compact && <span className=\"truncate text-sm font-medium text-foreground\">{resolvedBrandName}</span>}\n </Link>\n </div>\n )}\n {shouldRenderSidebarInjectionSpots ? (\n <InjectionSpot\n spotId={BACKEND_SIDEBAR_TOP_INJECTION_SPOT_ID}\n context={injectionContext}\n />\n ) : null}\n {!compact && (\n <SearchInput\n value={navQuery}\n onChange={setNavQuery}\n placeholder={t('appShell.searchNavPlaceholder', 'Search...')}\n aria-label={t('appShell.searchNavAria', 'Search navigation')}\n clearLabel={t('appShell.searchNavClear', 'Clear search')}\n className=\"mb-2\"\n />\n )}\n <div data-sidebar-scroll=\"true\" className={`flex flex-1 flex-col gap-3 overflow-y-auto scrollbar-hide pr-1 ${compact ? '-ml-2 pl-2' : '-ml-3 pl-3'}`}>\n {(() => {\n const isSettingsPath = (href: string) => {\n if (href === '/backend/settings') return true\n return resolvedSettingsPathPrefixes.some((prefix) => href.startsWith(prefix))\n }\n\n const isMainItem = (item: SidebarItem) => {\n if (item.pageContext && item.pageContext !== 'main') return false\n if (isSettingsPath(item.href)) return false\n return true\n }\n\n const mainGroups = mainNavGroupsWithInjected.map((g) => ({\n ...g,\n items: g.items.filter((item) => isMainItem(item) && item.hidden !== true),\n })).filter((g) => g.items.length > 0)\n\n const mainLastVisibleGroupIndex = (() => {\n for (let idx = mainGroups.length - 1; idx >= 0; idx -= 1) {\n if (mainGroups[idx].items.some((item) => item.hidden !== true)) return idx\n }\n return -1\n })()\n\n return (\n <>\n <nav className=\"flex flex-col gap-2\" data-testid=\"sidebar\">\n {shouldRenderSidebarInjectionSpots ? (\n <InjectionSpot\n spotId={BACKEND_SIDEBAR_NAV_INJECTION_SPOT_ID}\n context={injectionContext}\n />\n ) : null}\n {mainGroups.map((g, gi) => {\n const groupId = resolveGroupKey(g)\n const open = navQueryActive ? true : openGroups[groupId] !== false\n const visibleItems = g.items.filter((item) => {\n if (item.hidden === true) return false\n if (!navQueryActive) return true\n if (matchesQuery(item.title)) return true\n const itemChildren = (item.children ?? []).filter((c) => c.hidden !== true)\n return itemChildren.some((c) => matchesQuery(c.title))\n })\n if (visibleItems.length === 0) return null\n return (\n <div key={groupId}>\n {!compact && (\n <Button\n variant=\"muted\"\n onClick={() => toggleGroup(groupId)}\n className=\"w-full px-1 justify-between flex text-xs font-medium uppercase tracking-wider text-muted-foreground/70 py-1\"\n aria-expanded={open}\n >\n <span>{g.name}</span>\n <Chevron open={open} />\n </Button>\n )}\n {(open || compact) && (\n <div className={`flex flex-col ${compact ? 'items-center' : ''} gap-1`}>\n {visibleItems.map((i) => {\n const allChildItems = (i.children ?? []).filter((child) => child.hidden !== true)\n const matchingChildItems = navQueryActive\n ? allChildItems.filter((c) => matchesQuery(c.title))\n : allChildItems\n const childItems = navQueryActive ? matchingChildItems : allChildItems\n const showChildren = navQueryActive\n ? matchingChildItems.length > 0\n : (!!pathname && allChildItems.length > 0 && pathname.startsWith(i.href))\n const hasActiveChild = !!(pathname && allChildItems.some((c) => pathname.startsWith(c.href)))\n const isParentActive = (pathname === i.href) || (!navQueryActive && showChildren && !hasActiveChild)\n const base = compact ? 'w-10 h-10 justify-center' : 'w-full px-3 py-2 gap-2'\n return (\n <React.Fragment key={i.href}>\n <Link\n href={i.href}\n className={`relative text-sm font-medium rounded-lg inline-flex items-center ${base} ${\n isParentActive ? 'bg-muted text-foreground' : 'text-muted-foreground hover:bg-muted'\n } ${i.enabled === false ? 'pointer-events-none opacity-50' : ''}`}\n aria-disabled={i.enabled === false}\n title={compact ? i.title : undefined}\n data-menu-item-id={i.id ?? i.href}\n onClick={() => setMobileOpen(false)}\n >\n {isParentActive ? (\n <span aria-hidden className={`absolute ${compact ? 'left-[-20px]' : 'left-[-12px]'} top-2 w-1 h-5 rounded-r bg-foreground`} />\n ) : null}\n <span className=\"flex items-center justify-center shrink-0\">\n {renderIcon(\n i.icon,\n i.iconName,\n i.iconMarkup,\n DefaultIcon,\n )}\n </span>\n {!compact && <span>{i.title}</span>}\n </Link>\n {showChildren ? (\n <div className={`relative flex flex-col ${compact ? 'items-center' : ''} gap-1`}>\n {!compact && (\n <span aria-hidden className=\"pointer-events-none absolute left-1.5 top-1 bottom-1 w-px bg-border\" />\n )}\n {childItems.map((c) => {\n const childActive = pathname?.startsWith(c.href)\n const childBase = compact ? 'w-10 h-8 justify-center' : 'w-full pl-5 pr-3 py-2 gap-2'\n return (\n <Link\n key={c.href}\n href={c.href}\n className={`relative text-sm font-medium rounded-lg inline-flex items-center ${childBase} ${\n childActive ? 'bg-muted text-foreground' : 'text-muted-foreground hover:bg-muted'\n } ${c.enabled === false ? 'pointer-events-none opacity-50' : ''}`}\n aria-disabled={c.enabled === false}\n title={compact ? c.title : undefined}\n data-menu-item-id={c.id ?? c.href}\n onClick={() => setMobileOpen(false)}\n >\n {childActive ? (\n <span aria-hidden className={`absolute ${compact ? 'left-[-20px]' : 'left-[-12px]'} top-2 w-1 h-5 rounded-r bg-foreground`} />\n ) : null}\n <span className=\"flex items-center justify-center shrink-0\">\n {renderIcon(\n c.icon,\n c.iconName,\n c.iconMarkup,\n c.href.includes('/backend/entities/user/') && c.href.endsWith('/records') ? DataTableIcon : DefaultIcon,\n )}\n </span>\n {!compact && <span>{c.title}</span>}\n </Link>\n )\n })}\n </div>\n ) : null}\n </React.Fragment>\n )\n })}\n </div>\n )}\n {gi !== mainLastVisibleGroupIndex && <div className={`my-2 border-t ${compact ? '-ml-2 -mr-3' : '-ml-3 -mr-4'}`} />}\n </div>\n )\n })}\n </nav>\n </>\n )\n })()}\n </div>\n <div className=\"sticky bottom-0 bg-background pb-1\">\n {shouldRenderSidebarInjectionSpots ? (\n <InjectionSpot\n spotId={BACKEND_SIDEBAR_NAV_FOOTER_INJECTION_SPOT_ID}\n context={injectionContext}\n />\n ) : null}\n {shouldRenderSidebarInjectionSpots ? (\n <StatusBadgeInjectionSpot\n spotId={GLOBAL_SIDEBAR_STATUS_BADGES_INJECTION_SPOT_ID}\n context={injectionContext}\n />\n ) : null}\n {shouldRenderSidebarInjectionSpots ? (\n <InjectionSpot\n spotId={BACKEND_SIDEBAR_FOOTER_INJECTION_SPOT_ID}\n context={injectionContext}\n />\n ) : null}\n </div>\n </div>\n )\n }\n\n function renderSectionAside() {\n let sections: SectionNavGroup[] | null = null\n let title = ''\n if (sidebarMode === 'settings' && resolvedSettingsSections && resolvedSettingsSections.length > 0) {\n sections = mergeSectionGroupsWithInjected(\n resolvedSettingsSections,\n settingsSidebarInjectedMenuItems,\n t,\n )\n title = settingsSectionTitle ?? t('backend.nav.settings', 'Settings')\n } else if (sidebarMode === 'profile' && resolvedProfileSections && resolvedProfileSections.length > 0) {\n sections = mergeSectionGroupsWithInjected(\n resolvedProfileSections,\n profileSidebarInjectedMenuItems,\n t,\n )\n title = profileSectionTitle ?? t('backend.nav.profile', 'Profile')\n }\n if (!sections) return null\n return (\n <div className=\"flex h-full flex-col gap-2\">\n <Link\n href=\"/backend\"\n className=\"inline-flex items-center gap-2 rounded-lg px-2 py-2 text-sm font-semibold text-foreground transition-colors hover:bg-muted\"\n data-testid=\"appshell-section-back-to-main\"\n aria-label={t('backend.nav.backToMain', 'Back to Main')}\n >\n <ChevronLeft className=\"size-4 shrink-0\" aria-hidden />\n <span className=\"truncate\">{title}</span>\n </Link>\n <div className=\"min-h-0 flex-1\">\n {renderSectionSidebar(sections, title, false, true, true)}\n </div>\n </div>\n )\n }\n\n const isSectionView =\n (sidebarMode === 'settings' && !!resolvedSettingsSections && resolvedSettingsSections.length > 0) ||\n (sidebarMode === 'profile' && !!resolvedProfileSections && resolvedProfileSections.length > 0)\n const gridColsClass = isSectionView\n ? (effectiveCollapsed ? 'lg:grid-cols-[80px_240px_1fr]' : 'lg:grid-cols-[240px_240px_1fr]')\n : (effectiveCollapsed ? 'lg:grid-cols-[80px_1fr]' : 'lg:grid-cols-[240px_1fr]')\n const headerCtxValue = React.useMemo(() => ({\n setBreadcrumb: setHeaderBreadcrumb,\n setTitle: setHeaderTitle,\n }), [])\n const renderedTopbarInjectedActions = React.useMemo(\n () =>\n topbarInjectedMenuItems.map((item) => {\n const label = resolveInjectedMenuLabel(item, t)\n if (item.href) {\n return (\n <Link\n key={item.id}\n href={item.href}\n className=\"inline-flex items-center rounded border px-2 py-1 text-xs hover:bg-accent hover:text-accent-foreground\"\n data-menu-item-id={item.id}\n >\n {label}\n </Link>\n )\n }\n return (\n <Button\n key={item.id}\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n data-menu-item-id={item.id}\n onClick={() => item.onClick?.()}\n >\n {label}\n </Button>\n )\n }),\n [t, topbarInjectedMenuItems],\n )\n\n return (\n <HeaderContext.Provider value={headerCtxValue}>\n <div\n className={`relative min-h-svh lg:grid transition-[grid-template-columns] duration-200 ease-out ${gridColsClass}`}\n style={{ '--topbar-height': '61px' } as React.CSSProperties}\n >\n {/* Desktop sidebar collapse/expand toggle \u2014 sits on the divider line between\n sidebar and content, like Notion/Vercel. Hidden on mobile (hamburger in\n topbar handles the drawer). */}\n <button\n type=\"button\"\n onClick={() => setCollapsed((c) => !c)}\n aria-label={t('appShell.toggleSidebar')}\n className=\"hidden lg:flex fixed top-4 z-dropdown size-7 items-center justify-center rounded-md border bg-background text-muted-foreground shadow-sm transition-all hover:text-foreground hover:bg-muted focus:outline-none focus-visible:shadow-focus\"\n style={{ left: `calc(${asideWidth} - 14px)` }}\n >\n {effectiveCollapsed ? <PanelLeftOpen className=\"size-4\" /> : <PanelLeftClose className=\"size-4\" />}\n </button>\n {/* Desktop main sidebar */}\n <aside ref={sidebarAsideRef} className={`${asideClassesBase} ${effectiveCollapsed ? 'px-2' : 'px-3'} hidden lg:block lg:sticky lg:top-0 lg:h-svh lg:self-start lg:overflow-hidden lg:relative transition-[width,padding] duration-200 ease-out`} style={{ width: asideWidth }}>\n {renderSidebar(effectiveCollapsed, false, isSectionView)}\n {/* Scroll affordance \u2014 gradient fade + clickable chevron that flips up when\n the user reaches the bottom and disappears when nothing is scrollable\n (#1803). Clicking the chevron scrolls the inner sidebar container to\n top/bottom (`prefers-reduced-motion: reduce` collapses to instant\n scrolling). The wrapper is `pointer-events-none` so the gradient fade\n doesn't block hover/click on the rendered nav items behind it; the\n IconButton restores `pointer-events-auto` so it stays interactive. */}\n {sidebarScrollState !== 'none' ? (\n <div\n className=\"pointer-events-none absolute inset-x-0 bottom-0 flex h-10 items-end justify-center bg-gradient-to-t from-background via-background/80 to-transparent pb-1.5\"\n >\n {/* The IconButton owns hover/focus affordance; the inner span owns the\n rotate transition so it doesn't fight with the animate-bounce\n keyframes (both target `transform`). */}\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n data-testid=\"sidebar-scroll-chevron\"\n data-sidebar-scroll-chevron={sidebarScrollState}\n aria-label={\n sidebarScrollState === 'up'\n ? t('ui.sidebar.chevron.scrollTop', 'Scroll to top')\n : t('ui.sidebar.chevron.scrollBottom', 'Scroll to bottom')\n }\n className=\"pointer-events-auto text-muted-foreground/70 hover:text-foreground\"\n onClick={() => handleSidebarChevronScroll(sidebarScrollState === 'up' ? 'top' : 'bottom')}\n >\n <span\n className={`inline-flex transition-transform duration-300 ${sidebarScrollState === 'up' ? 'rotate-180' : ''}`}\n >\n <ChevronDown className=\"size-4 animate-bounce\" />\n </span>\n </IconButton>\n </div>\n ) : null}\n </aside>\n\n {/* Desktop section sidebar (Option B two-level) \u2014 sits beside the main sidebar\n when the user is on settings/profile routes. Mobile drawer keeps the\n original swap behavior to fit the narrow width. */}\n {isSectionView ? (\n <aside\n className={`${asideClassesBase} px-3 hidden lg:block lg:sticky lg:top-0 lg:h-svh lg:self-start lg:overflow-hidden lg:relative`}\n style={{ width: '240px' }}\n data-testid=\"appshell-section-sidebar\"\n >\n {renderSectionAside()}\n {/* Static bottom fade \u2014 covers the native iOS scroll indicator and signals\n that the section list is scrollable. Same look as the main sidebar's\n affordance but without the chevron / scroll-state machinery. */}\n <div\n aria-hidden\n className=\"pointer-events-none absolute inset-x-0 bottom-0 h-10 bg-gradient-to-t from-background via-background/80 to-transparent\"\n />\n </aside>\n ) : null}\n\n <div className=\"flex min-h-svh flex-col min-w-0\">\n <header className=\"sticky top-0 z-sticky border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/80 px-3 sm:px-4 lg:px-6 py-3 flex items-center justify-between gap-2 sm:gap-3\">\n <div\n data-testid=\"backend-chrome-ready\"\n data-ready={isChromeReady ? 'true' : 'false'}\n className=\"hidden\"\n />\n <div className=\"flex items-center gap-2 min-w-0\">\n {/* Mobile menu button */}\n <IconButton variant=\"ghost\" size=\"sm\" className=\"lg:hidden\" aria-label={t('appShell.openMenu')} onClick={() => setMobileOpen(true)}>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\"><path d=\"M3 6h18M3 12h18M3 18h18\"/></svg>\n </IconButton>\n {/* Header breadcrumb: always starts with Dashboard */}\n {(() => {\n const dashboardLabel = t('dashboard.title')\n const root: Breadcrumb = [{ label: dashboardLabel, href: '/backend' }]\n let rest: Breadcrumb = []\n if (headerBreadcrumb && headerBreadcrumb.length) {\n const first = headerBreadcrumb[0]\n const dup = first && (first.href === '/backend' || first.label === dashboardLabel || first.label?.toLowerCase() === 'dashboard')\n rest = dup ? headerBreadcrumb.slice(1) : headerBreadcrumb\n } else if (headerTitle) {\n rest = [{ label: headerTitle }]\n }\n const items = [...root, ...rest]\n if (items.length === 0) return null\n const home = items[0]\n const current = items.length > 1 ? items[items.length - 1] : null\n const mid = items.slice(1, -1)\n const hasMid = mid.length > 0\n return (\n <BreadcrumbNav divider=\"arrow\" className=\"ml-2 lg:ml-3 text-sm\">\n <BreadcrumbList className=\"[&_[data-slot=breadcrumb-separator]_svg]:size-4\">\n <BreadcrumbItem>\n {home.href && current ? (\n <BreadcrumbLink asChild aria-label={home.label}>\n <Link href={home.href}>\n <Home className=\"size-4\" aria-hidden=\"true\" />\n </Link>\n </BreadcrumbLink>\n ) : (\n <BreadcrumbPage aria-label={home.label}>\n <Home className=\"size-4\" aria-hidden=\"true\" />\n </BreadcrumbPage>\n )}\n </BreadcrumbItem>\n {current ? (\n <>\n {hasMid ? (\n <>\n <BreadcrumbSeparator className=\"md:hidden\" />\n <BreadcrumbItem className=\"md:hidden\">\n <BreadcrumbEllipsis aria-label={t('appShell.breadcrumb.collapsed', { count: mid.length })} />\n </BreadcrumbItem>\n {mid.map((b, i) => (\n <React.Fragment key={`mid-${i}`}>\n <BreadcrumbSeparator className=\"hidden md:inline-flex\" />\n <BreadcrumbItem className=\"hidden md:inline-flex\">\n {b.href ? (\n <BreadcrumbLink asChild title={b.label}>\n <Link href={b.href}>{b.label}</Link>\n </BreadcrumbLink>\n ) : (\n <BreadcrumbLink title={b.label} aria-disabled=\"true\" tabIndex={-1}>\n {b.label}\n </BreadcrumbLink>\n )}\n </BreadcrumbItem>\n </React.Fragment>\n ))}\n </>\n ) : null}\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbPage title={current.label}>{current.label}</BreadcrumbPage>\n </BreadcrumbItem>\n </>\n ) : null}\n </BreadcrumbList>\n </BreadcrumbNav>\n )\n })()}\n </div>\n <div className=\"flex items-center gap-1.5 sm:gap-2 md:gap-3 text-sm shrink-0\">\n <StatusBadgeInjectionSpot\n spotId={GLOBAL_HEADER_STATUS_INDICATORS_INJECTION_SPOT_ID}\n context={injectionContext}\n />\n <InjectionSpot\n spotId={BACKEND_TOPBAR_ACTIONS_INJECTION_SPOT_ID}\n context={injectionContext}\n />\n {renderedTopbarInjectedActions}\n <AiAssistantLauncher variant=\"topbar\" />\n {rightHeaderSlot ? (\n rightHeaderSlot\n ) : (\n <span className=\"opacity-80\">{email || t('appShell.userFallback')}</span>\n )}\n </div>\n </header>\n <ProgressTopBar t={t} className=\"sticky top-0 z-sticky\" completedAutoHideMs={progressCompletedAutoHideMs} />\n <main className=\"flex-1 p-4 lg:p-6 mx-auto w-full max-w-screen-2xl\">\n <InjectionSpot spotId={BACKEND_LAYOUT_TOP_INJECTION_SPOT_ID} context={injectionContext} />\n <FlashMessages />\n <PartialIndexBanner />\n {canManageUpgradeActions ? <UpgradeActionBanner /> : null}\n <LastOperationBanner />\n <RecordConflictBanner />\n <InjectionSpot spotId={BACKEND_RECORD_CURRENT_INJECTION_SPOT_ID} context={injectionContext} />\n <InjectionSpot\n spotId={LEGACY_GLOBAL_MUTATION_INJECTION_SPOT_ID}\n context={injectionContext}\n />\n <div id=\"om-top-banners\" className=\"mb-3 space-y-2\" />\n {children}\n <InjectionSpot spotId={BACKEND_LAYOUT_FOOTER_INJECTION_SPOT_ID} context={injectionContext} />\n </main>\n <footer className=\"border-t bg-background/80 backdrop-blur supports-[backdrop-filter]:bg-background/80 px-4 py-3 flex flex-wrap items-center justify-end gap-4\">\n {version ? (\n <span className=\"text-xs text-muted-foreground\">\n {t('appShell.version', { version })}\n </span>\n ) : null}\n <nav className=\"flex items-center gap-3 text-xs text-muted-foreground\">\n <Link href=\"/terms\" className=\"transition hover:text-foreground\">\n {t('common.terms')}\n </Link>\n <Link href=\"/privacy\" className=\"transition hover:text-foreground\">\n {t('common.privacy')}\n </Link>\n </nav>\n </footer>\n </div>\n\n {/* Mobile drawer */}\n {mobileOpen && (\n <div className=\"lg:hidden fixed inset-0 z-modal\">\n <div className=\"absolute inset-0 bg-black/50 backdrop-blur-sm\" onClick={() => setMobileOpen(false)} aria-hidden=\"true\" />\n <aside className=\"absolute left-0 top-0 flex h-full w-[280px] max-w-[85vw] flex-col bg-background border-r shadow-lg overflow-hidden\">\n <div className=\"shrink-0 flex items-center justify-between gap-2 border-b px-4 py-3\">\n <Link href=\"/backend\" className=\"flex items-center gap-2 min-w-0 text-sm font-semibold\" onClick={() => setMobileOpen(false)} aria-label={t('appShell.goToDashboard')}>\n <Image src={resolvedLogo?.src ?? \"/open-mercato.svg\"} alt={resolvedLogo?.alt ?? resolvedBrandName} width={28} height={28} className=\"rounded shrink-0\" unoptimized={resolvedLogoBypassesOptimization ? true : undefined} />\n <span className=\"truncate\">{resolvedBrandName}</span>\n </Link>\n <IconButton variant=\"ghost\" size=\"sm\" onClick={() => setMobileOpen(false)} aria-label={t('appShell.closeMenu')}>\n <X className=\"size-4\" />\n </IconButton>\n </div>\n {mobileSidebarSlot && (\n <div className=\"shrink-0 border-b px-3 py-2\">\n {mobileSidebarSlot}\n </div>\n )}\n {sidebarMode !== 'main' ? (\n <div className=\"shrink-0 flex items-center gap-5 border-b px-4 pt-3 pb-0\" role=\"tablist\">\n {([\n { id: 'main' as const, label: t('backend.nav.main', 'Main') },\n {\n id: 'section' as const,\n label:\n sidebarMode === 'settings'\n ? settingsSectionTitle ?? t('backend.nav.settings', 'Settings')\n : profileSectionTitle ?? t('backend.nav.profile', 'Profile'),\n },\n ]).map((tab) => {\n const isActive =\n tab.id === 'main' ? mobileDrawerView === 'main' : mobileDrawerView === 'auto'\n const tabId = `mobile-drawer-tab-${tab.id}`\n return (\n <button\n key={tab.id}\n id={tabId}\n type=\"button\"\n role=\"tab\"\n aria-selected={isActive}\n aria-controls=\"mobile-drawer-tabpanel\"\n onClick={() => setMobileDrawerView(tab.id === 'main' ? 'main' : 'auto')}\n className=\"relative inline-flex items-center pb-2 text-sm font-medium leading-5 tracking-tight transition-colors focus:outline-none data-[active=true]:text-foreground data-[active=false]:text-muted-foreground hover:text-foreground\"\n data-active={isActive}\n >\n <span>{tab.label}</span>\n {isActive ? (\n <span\n className=\"absolute -bottom-px left-0 right-0 h-0.5 bg-foreground\"\n aria-hidden=\"true\"\n />\n ) : null}\n </button>\n )\n })}\n </div>\n ) : null}\n <div\n id=\"mobile-drawer-tabpanel\"\n role={sidebarMode !== 'main' ? 'tabpanel' : undefined}\n aria-labelledby={\n sidebarMode !== 'main'\n ? `mobile-drawer-tab-${mobileDrawerView === 'main' ? 'main' : 'section'}`\n : undefined\n }\n className=\"min-h-0 flex-1 overflow-y-auto overflow-x-hidden p-3\"\n >\n {/* Force expanded sidebar in mobile drawer, hide its header and collapse toggle */}\n {renderSidebar(false, true, mobileDrawerView === 'main')}\n </div>\n </aside>\n </div>\n )}\n </div>\n <UmesDevToolsPanel />\n </HeaderContext.Provider>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["children"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/ui",
|
|
3
|
-
"version": "0.6.6-develop.
|
|
3
|
+
"version": "0.6.6-develop.5523.1.e223ca1915",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -154,13 +154,13 @@
|
|
|
154
154
|
"remark-gfm": "^4.0.1"
|
|
155
155
|
},
|
|
156
156
|
"peerDependencies": {
|
|
157
|
-
"@open-mercato/shared": "0.6.6-develop.
|
|
157
|
+
"@open-mercato/shared": "0.6.6-develop.5523.1.e223ca1915",
|
|
158
158
|
"react": ">=18.0.0",
|
|
159
159
|
"react-dom": ">=18.0.0",
|
|
160
160
|
"react-is": ">=18.0.0"
|
|
161
161
|
},
|
|
162
162
|
"devDependencies": {
|
|
163
|
-
"@open-mercato/shared": "0.6.6-develop.
|
|
163
|
+
"@open-mercato/shared": "0.6.6-develop.5523.1.e223ca1915",
|
|
164
164
|
"@testing-library/dom": "^10.4.1",
|
|
165
165
|
"@testing-library/jest-dom": "^6.9.1",
|
|
166
166
|
"@testing-library/react": "^16.3.1",
|
package/src/backend/AppShell.tsx
CHANGED
|
@@ -150,6 +150,11 @@ function resolveInjectedMenuLabel(
|
|
|
150
150
|
return item.label ?? item.id
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
function shouldBypassLogoOptimization(src?: string | null): boolean {
|
|
154
|
+
const value = src ?? ''
|
|
155
|
+
return /^https?:\/\//.test(value) || /^\/api\/attachments\/(?:image|file)\//.test(value)
|
|
156
|
+
}
|
|
157
|
+
|
|
153
158
|
function mergeSidebarItemsWithInjected(
|
|
154
159
|
items: SidebarItem[],
|
|
155
160
|
injectedItems: InjectionMenuItem[],
|
|
@@ -444,6 +449,11 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
444
449
|
const { items: topbarInjectedMenuItems } = useInjectedMenuItems('menu:topbar:actions')
|
|
445
450
|
useEventBridge() // SSE DOM Event Bridge — singleton SSE connection for real-time server events
|
|
446
451
|
const resolvedProductName = productName ?? t('appShell.productName')
|
|
452
|
+
const resolvedLogo = chromePayload?.brand?.logo?.src ? chromePayload.brand.logo : logo
|
|
453
|
+
const resolvedBrandName = chromePayload?.brand?.logo?.src
|
|
454
|
+
? chromePayload.brand.name ?? resolvedProductName
|
|
455
|
+
: resolvedProductName
|
|
456
|
+
const resolvedLogoBypassesOptimization = shouldBypassLogoOptimization(resolvedLogo?.src)
|
|
447
457
|
const [mobileOpen, setMobileOpen] = React.useState(false)
|
|
448
458
|
// When the mobile drawer opens on a settings/profile route, it follows the
|
|
449
459
|
// section sidebar by default. Set to 'main' to force-show the main nav even
|
|
@@ -700,8 +710,8 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
700
710
|
className={`flex items-center gap-3 rounded-xl transition-colors hover:bg-muted ${compact ? 'p-2 justify-center' : 'p-3'}`}
|
|
701
711
|
aria-label={t('appShell.goToDashboard')}
|
|
702
712
|
>
|
|
703
|
-
<Image src={
|
|
704
|
-
{!compact && <span className="truncate text-sm font-medium text-foreground">{
|
|
713
|
+
<Image src={resolvedLogo?.src ?? "/open-mercato.svg"} alt={resolvedLogo?.alt ?? resolvedBrandName} width={40} height={40} className="rounded-full shrink-0" unoptimized={resolvedLogoBypassesOptimization ? true : undefined} />
|
|
714
|
+
{!compact && <span className="truncate text-sm font-medium text-foreground">{resolvedBrandName}</span>}
|
|
705
715
|
</Link>
|
|
706
716
|
</div>
|
|
707
717
|
)}
|
|
@@ -833,8 +843,8 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
833
843
|
className={`flex items-center gap-3 rounded-xl transition-colors hover:bg-muted ${compact ? 'p-2 justify-center' : 'p-3'}`}
|
|
834
844
|
aria-label={t('appShell.goToDashboard')}
|
|
835
845
|
>
|
|
836
|
-
<Image src={
|
|
837
|
-
{!compact && <span className="truncate text-sm font-medium text-foreground">{
|
|
846
|
+
<Image src={resolvedLogo?.src ?? "/open-mercato.svg"} alt={resolvedLogo?.alt ?? resolvedBrandName} width={40} height={40} className="rounded-full shrink-0" unoptimized={resolvedLogoBypassesOptimization ? true : undefined} />
|
|
847
|
+
{!compact && <span className="truncate text-sm font-medium text-foreground">{resolvedBrandName}</span>}
|
|
838
848
|
</Link>
|
|
839
849
|
</div>
|
|
840
850
|
) : null}
|
|
@@ -899,8 +909,8 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
899
909
|
className={`flex items-center gap-3 rounded-xl transition-colors hover:bg-muted ${compact ? 'p-2 justify-center' : 'p-3'}`}
|
|
900
910
|
aria-label={t('appShell.goToDashboard')}
|
|
901
911
|
>
|
|
902
|
-
<Image src={
|
|
903
|
-
{!compact && <span className="truncate text-sm font-medium text-foreground">{
|
|
912
|
+
<Image src={resolvedLogo?.src ?? "/open-mercato.svg"} alt={resolvedLogo?.alt ?? resolvedBrandName} width={40} height={40} className="rounded-full shrink-0" unoptimized={resolvedLogoBypassesOptimization ? true : undefined} />
|
|
913
|
+
{!compact && <span className="truncate text-sm font-medium text-foreground">{resolvedBrandName}</span>}
|
|
904
914
|
</Link>
|
|
905
915
|
</div>
|
|
906
916
|
)}
|
|
@@ -1392,8 +1402,8 @@ function AppShellBody({ productName, logo, email, canManageUpgradeActions = fals
|
|
|
1392
1402
|
<aside className="absolute left-0 top-0 flex h-full w-[280px] max-w-[85vw] flex-col bg-background border-r shadow-lg overflow-hidden">
|
|
1393
1403
|
<div className="shrink-0 flex items-center justify-between gap-2 border-b px-4 py-3">
|
|
1394
1404
|
<Link href="/backend" className="flex items-center gap-2 min-w-0 text-sm font-semibold" onClick={() => setMobileOpen(false)} aria-label={t('appShell.goToDashboard')}>
|
|
1395
|
-
<Image src={
|
|
1396
|
-
<span className="truncate">{
|
|
1405
|
+
<Image src={resolvedLogo?.src ?? "/open-mercato.svg"} alt={resolvedLogo?.alt ?? resolvedBrandName} width={28} height={28} className="rounded shrink-0" unoptimized={resolvedLogoBypassesOptimization ? true : undefined} />
|
|
1406
|
+
<span className="truncate">{resolvedBrandName}</span>
|
|
1397
1407
|
</Link>
|
|
1398
1408
|
<IconButton variant="ghost" size="sm" onClick={() => setMobileOpen(false)} aria-label={t('appShell.closeMenu')}>
|
|
1399
1409
|
<X className="size-4" />
|
|
@@ -19,7 +19,13 @@ jest.mock('next/link', () => {
|
|
|
19
19
|
))
|
|
20
20
|
})
|
|
21
21
|
|
|
22
|
-
jest.mock('next/image', () =>
|
|
22
|
+
jest.mock('next/image', () => {
|
|
23
|
+
const React = require('react')
|
|
24
|
+
return (props: any) => {
|
|
25
|
+
const { unoptimized, ...rest } = props
|
|
26
|
+
return <img alt={rest.alt} data-unoptimized={unoptimized ? 'true' : 'false'} {...rest} />
|
|
27
|
+
}
|
|
28
|
+
})
|
|
23
29
|
|
|
24
30
|
jest.mock('next/navigation', () => ({
|
|
25
31
|
usePathname: () => mockPathname,
|
|
@@ -201,6 +207,60 @@ describe('AppShell', () => {
|
|
|
201
207
|
)
|
|
202
208
|
})
|
|
203
209
|
|
|
210
|
+
it('uses backend chrome brand logo when the selected organization has one', async () => {
|
|
211
|
+
const previousFetch = global.fetch
|
|
212
|
+
const previousWindowFetch = window.fetch
|
|
213
|
+
const previousOriginalFetch = (window as Window & { __omOriginalFetch?: typeof fetch }).__omOriginalFetch
|
|
214
|
+
const fetchMock = jest.fn().mockResolvedValue(
|
|
215
|
+
new Response(JSON.stringify({
|
|
216
|
+
brand: {
|
|
217
|
+
name: 'Acme',
|
|
218
|
+
logo: {
|
|
219
|
+
src: '/api/attachments/image/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/acme.png?width=320',
|
|
220
|
+
alt: 'Acme logo',
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
groups,
|
|
224
|
+
settingsSections: [],
|
|
225
|
+
settingsPathPrefixes: [],
|
|
226
|
+
profileSections: [],
|
|
227
|
+
profilePathPrefixes: [],
|
|
228
|
+
grantedFeatures: [],
|
|
229
|
+
roles: [],
|
|
230
|
+
}), { status: 200, headers: { 'content-type': 'application/json' } }),
|
|
231
|
+
) as typeof fetch
|
|
232
|
+
global.fetch = fetchMock
|
|
233
|
+
window.fetch = fetchMock
|
|
234
|
+
;(window as Window & { __omOriginalFetch?: typeof fetch }).__omOriginalFetch = fetchMock
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
renderWithProviders(
|
|
238
|
+
<AppShell
|
|
239
|
+
email="demo@example.com"
|
|
240
|
+
groups={[]}
|
|
241
|
+
adminNavApi="/api/auth/admin/nav-brand-logo"
|
|
242
|
+
>
|
|
243
|
+
<div>Child content</div>
|
|
244
|
+
</AppShell>,
|
|
245
|
+
{ dict },
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
await waitFor(() => {
|
|
249
|
+
const logo = screen.getByAltText('Acme logo')
|
|
250
|
+
expect(logo).toHaveAttribute(
|
|
251
|
+
'src',
|
|
252
|
+
'/api/attachments/image/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/acme.png?width=320',
|
|
253
|
+
)
|
|
254
|
+
expect(logo).toHaveAttribute('data-unoptimized', 'true')
|
|
255
|
+
})
|
|
256
|
+
expect(screen.getByText('Acme')).toBeInTheDocument()
|
|
257
|
+
} finally {
|
|
258
|
+
global.fetch = previousFetch
|
|
259
|
+
window.fetch = previousWindowFetch
|
|
260
|
+
;(window as Window & { __omOriginalFetch?: typeof fetch }).__omOriginalFetch = previousOriginalFetch
|
|
261
|
+
}
|
|
262
|
+
})
|
|
263
|
+
|
|
204
264
|
it('renders nested settings links when settings parent route is active', async () => {
|
|
205
265
|
mockPathname = '/backend/entities/user'
|
|
206
266
|
|