@open-mercato/ui 0.6.2-develop.3372.1.de9a169973 → 0.6.2-develop.3380.1.bb14509041
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/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +1 -0
- package/dist/ai/AiIcon.js +1 -1
- package/dist/ai/AiIcon.js.map +2 -2
- package/dist/backend/AppShell.js +223 -153
- package/dist/backend/AppShell.js.map +2 -2
- package/dist/backend/ProfileDropdown.js +101 -79
- package/dist/backend/ProfileDropdown.js.map +2 -2
- package/dist/backend/notifications/NotificationCountBadge.js +1 -1
- package/dist/backend/notifications/NotificationCountBadge.js.map +2 -2
- package/dist/backend/notifications/NotificationItem.js +67 -50
- package/dist/backend/notifications/NotificationItem.js.map +2 -2
- package/dist/backend/notifications/NotificationPanel.js +108 -85
- package/dist/backend/notifications/NotificationPanel.js.map +2 -2
- package/dist/primitives/breadcrumb.js +161 -0
- package/dist/primitives/breadcrumb.js.map +7 -0
- package/dist/primitives/sheet.js +114 -0
- package/dist/primitives/sheet.js.map +7 -0
- package/package.json +3 -3
- package/src/ai/AiIcon.tsx +1 -1
- package/src/backend/AppShell.tsx +151 -49
- package/src/backend/ProfileDropdown.tsx +115 -88
- package/src/backend/__tests__/AppShell.test.tsx +8 -0
- package/src/backend/notifications/NotificationCountBadge.tsx +1 -1
- package/src/backend/notifications/NotificationItem.tsx +97 -104
- package/src/backend/notifications/NotificationPanel.tsx +126 -102
- package/src/primitives/__tests__/breadcrumb.test.tsx +298 -0
- package/src/primitives/__tests__/sheet.test.tsx +152 -0
- package/src/primitives/breadcrumb.tsx +185 -0
- package/src/primitives/sheet.tsx +165 -0
package/.turbo/turbo-build.log
CHANGED
package/AGENTS.md
CHANGED
|
@@ -42,6 +42,7 @@ When you need… use this. Details (variants, sizes, props, MUST rules) live in
|
|
|
42
42
|
| User / entity avatar | `Avatar`, `AvatarStack` | `@open-mercato/ui/primitives/avatar` |
|
|
43
43
|
| Keyboard shortcut keys | `Kbd`, `KbdShortcut` | `@open-mercato/ui/primitives/kbd` |
|
|
44
44
|
| Entity tag pill | `Tag` (with `TagMap`) | `@open-mercato/ui/primitives/tag` |
|
|
45
|
+
| Breadcrumb navigation (DS-aligned, slash/arrow/dot divider, ARIA correct) | `Breadcrumb` (with `BreadcrumbList` / `BreadcrumbItem` / `BreadcrumbLink` / `BreadcrumbPage` / `BreadcrumbStatic` / `BreadcrumbSeparator` / `BreadcrumbEllipsis`) | `@open-mercato/ui/primitives/breadcrumb` |
|
|
45
46
|
| Wrap a `<Link>` as button | `Button asChild` / `IconButton asChild` | — |
|
|
46
47
|
|
|
47
48
|
## Critical MUST rules (top of mind)
|
package/dist/ai/AiIcon.js
CHANGED
package/dist/ai/AiIcon.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/ai/AiIcon.tsx"],
|
|
4
|
-
"sourcesContent": ["import { Sparkles, type LucideProps } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\nexport function AiIcon({ className, ...props }: LucideProps) {\n return (\n <Sparkles\n aria-hidden\n {...props}\n className={cn(className, 'text-
|
|
5
|
-
"mappings": "AAKI;AALJ,SAAS,gBAAkC;AAC3C,SAAS,UAAU;AAEZ,SAAS,OAAO,EAAE,WAAW,GAAG,MAAM,GAAgB;AAC3D,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAW;AAAA,MACV,GAAG;AAAA,MACJ,WAAW,GAAG,WAAW,
|
|
4
|
+
"sourcesContent": ["import { Sparkles, type LucideProps } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\nexport function AiIcon({ className, ...props }: LucideProps) {\n return (\n <Sparkles\n aria-hidden\n {...props}\n className={cn(className, 'text-accent-indigo')}\n />\n )\n}\n"],
|
|
5
|
+
"mappings": "AAKI;AALJ,SAAS,gBAAkC;AAC3C,SAAS,UAAU;AAEZ,SAAS,OAAO,EAAE,WAAW,GAAG,MAAM,GAAgB;AAC3D,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAW;AAAA,MACV,GAAG;AAAA,MACJ,WAAW,GAAG,WAAW,oBAAoB;AAAA;AAAA,EAC/C;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/backend/AppShell.js
CHANGED
|
@@ -4,8 +4,17 @@ import * as React from "react";
|
|
|
4
4
|
import { createContext, useContext } from "react";
|
|
5
5
|
import Link from "next/link";
|
|
6
6
|
import Image from "next/image";
|
|
7
|
-
import { ChevronDown, ChevronLeft } from "lucide-react";
|
|
7
|
+
import { ChevronDown, ChevronLeft, Home, PanelLeftClose, PanelLeftOpen, X } from "lucide-react";
|
|
8
8
|
import { Button } from "../primitives/button.js";
|
|
9
|
+
import {
|
|
10
|
+
Breadcrumb as BreadcrumbNav,
|
|
11
|
+
BreadcrumbEllipsis,
|
|
12
|
+
BreadcrumbItem,
|
|
13
|
+
BreadcrumbLink,
|
|
14
|
+
BreadcrumbList,
|
|
15
|
+
BreadcrumbPage,
|
|
16
|
+
BreadcrumbSeparator
|
|
17
|
+
} from "../primitives/breadcrumb.js";
|
|
9
18
|
import { IconButton } from "../primitives/icon-button.js";
|
|
10
19
|
import { SearchInput } from "../primitives/search-input.js";
|
|
11
20
|
import { FlashMessages } from "./FlashMessages.js";
|
|
@@ -283,6 +292,10 @@ function AppShellBody({ productName, logo, email, groups, rightHeaderSlot, child
|
|
|
283
292
|
useEventBridge();
|
|
284
293
|
const resolvedProductName = productName ?? t("appShell.productName");
|
|
285
294
|
const [mobileOpen, setMobileOpen] = React.useState(false);
|
|
295
|
+
const [mobileDrawerView, setMobileDrawerView] = React.useState("auto");
|
|
296
|
+
React.useEffect(() => {
|
|
297
|
+
if (!mobileOpen) setMobileDrawerView("auto");
|
|
298
|
+
}, [mobileOpen]);
|
|
286
299
|
const [collapsed, setCollapsed] = React.useState(sidebarCollapsedDefault);
|
|
287
300
|
const [navGroups, setNavGroups] = React.useState(resolvedGroups);
|
|
288
301
|
const [openGroups, setOpenGroups] = React.useState(
|
|
@@ -474,7 +487,7 @@ function AppShellBody({ productName, logo, email, groups, rightHeaderSlot, child
|
|
|
474
487
|
"aria-label": t("appShell.goToDashboard"),
|
|
475
488
|
children: [
|
|
476
489
|
/* @__PURE__ */ jsx(Image, { src: logo?.src ?? "/open-mercato.svg", alt: logo?.alt ?? resolvedProductName, width: 40, height: 40, className: "rounded-full shrink-0" }),
|
|
477
|
-
!compact && /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: resolvedProductName })
|
|
490
|
+
!compact && /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium text-foreground", children: resolvedProductName })
|
|
478
491
|
]
|
|
479
492
|
}
|
|
480
493
|
) }),
|
|
@@ -577,7 +590,7 @@ function AppShellBody({ productName, logo, email, groups, rightHeaderSlot, child
|
|
|
577
590
|
"aria-label": t("appShell.goToDashboard"),
|
|
578
591
|
children: [
|
|
579
592
|
/* @__PURE__ */ jsx(Image, { src: logo?.src ?? "/open-mercato.svg", alt: logo?.alt ?? resolvedProductName, width: 40, height: 40, className: "rounded-full shrink-0" }),
|
|
580
|
-
!compact && /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: resolvedProductName })
|
|
593
|
+
!compact && /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium text-foreground", children: resolvedProductName })
|
|
581
594
|
]
|
|
582
595
|
}
|
|
583
596
|
) }) : null,
|
|
@@ -637,7 +650,7 @@ function AppShellBody({ productName, logo, email, groups, rightHeaderSlot, child
|
|
|
637
650
|
"aria-label": t("appShell.goToDashboard"),
|
|
638
651
|
children: [
|
|
639
652
|
/* @__PURE__ */ jsx(Image, { src: logo?.src ?? "/open-mercato.svg", alt: logo?.alt ?? resolvedProductName, width: 40, height: 40, className: "rounded-full shrink-0" }),
|
|
640
|
-
!compact && /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: resolvedProductName })
|
|
653
|
+
!compact && /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium text-foreground", children: resolvedProductName })
|
|
641
654
|
]
|
|
642
655
|
}
|
|
643
656
|
) }),
|
|
@@ -866,7 +879,6 @@ function AppShellBody({ productName, logo, email, groups, rightHeaderSlot, child
|
|
|
866
879
|
type: "button",
|
|
867
880
|
variant: "outline",
|
|
868
881
|
size: "sm",
|
|
869
|
-
className: "h-7 text-xs",
|
|
870
882
|
"data-menu-item-id": item.id,
|
|
871
883
|
onClick: () => item.onClick?.(),
|
|
872
884
|
children: label
|
|
@@ -877,165 +889,223 @@ function AppShellBody({ productName, logo, email, groups, rightHeaderSlot, child
|
|
|
877
889
|
[t, topbarInjectedMenuItems]
|
|
878
890
|
);
|
|
879
891
|
return /* @__PURE__ */ jsxs(HeaderContext.Provider, { value: headerCtxValue, children: [
|
|
880
|
-
/* @__PURE__ */ jsxs(
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
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",
|
|
887
|
-
children: /* @__PURE__ */ jsx(
|
|
888
|
-
IconButton,
|
|
889
|
-
{
|
|
890
|
-
type: "button",
|
|
891
|
-
variant: "ghost",
|
|
892
|
-
size: "sm",
|
|
893
|
-
"data-testid": "sidebar-scroll-chevron",
|
|
894
|
-
"data-sidebar-scroll-chevron": sidebarScrollState,
|
|
895
|
-
"aria-label": sidebarScrollState === "up" ? t("ui.sidebar.chevron.scrollTop", "Scroll to top") : t("ui.sidebar.chevron.scrollBottom", "Scroll to bottom"),
|
|
896
|
-
className: "pointer-events-auto text-muted-foreground/70 hover:text-foreground",
|
|
897
|
-
onClick: () => handleSidebarChevronScroll(sidebarScrollState === "up" ? "top" : "bottom"),
|
|
898
|
-
children: /* @__PURE__ */ jsx(
|
|
899
|
-
"span",
|
|
900
|
-
{
|
|
901
|
-
className: `inline-flex transition-transform duration-300 ${sidebarScrollState === "up" ? "rotate-180" : ""}`,
|
|
902
|
-
children: /* @__PURE__ */ jsx(ChevronDown, { className: "size-4 animate-bounce" })
|
|
903
|
-
}
|
|
904
|
-
)
|
|
905
|
-
}
|
|
906
|
-
)
|
|
907
|
-
}
|
|
908
|
-
) : null
|
|
909
|
-
] }),
|
|
910
|
-
isSectionView ? /* @__PURE__ */ jsxs(
|
|
911
|
-
"aside",
|
|
912
|
-
{
|
|
913
|
-
className: `${asideClassesBase} px-3 hidden lg:block lg:sticky lg:top-0 lg:h-svh lg:self-start lg:overflow-hidden lg:relative`,
|
|
914
|
-
style: { width: "240px" },
|
|
915
|
-
"data-testid": "appshell-section-sidebar",
|
|
916
|
-
children: [
|
|
917
|
-
renderSectionAside(),
|
|
918
|
-
/* @__PURE__ */ jsx(
|
|
919
|
-
"div",
|
|
920
|
-
{
|
|
921
|
-
"aria-hidden": true,
|
|
922
|
-
className: "pointer-events-none absolute inset-x-0 bottom-0 h-10 bg-gradient-to-t from-background via-background/80 to-transparent"
|
|
923
|
-
}
|
|
924
|
-
)
|
|
925
|
-
]
|
|
926
|
-
}
|
|
927
|
-
) : null,
|
|
928
|
-
/* @__PURE__ */ jsxs("div", { className: "flex min-h-svh flex-col min-w-0", children: [
|
|
929
|
-
/* @__PURE__ */ jsxs("header", { className: "border-b bg-background/80 px-3 lg:px-4 py-2 lg:py-3 flex items-center justify-between gap-2", children: [
|
|
892
|
+
/* @__PURE__ */ jsxs(
|
|
893
|
+
"div",
|
|
894
|
+
{
|
|
895
|
+
className: `relative min-h-svh lg:grid transition-[grid-template-columns] duration-200 ease-out ${gridColsClass}`,
|
|
896
|
+
style: { "--topbar-height": "61px" },
|
|
897
|
+
children: [
|
|
930
898
|
/* @__PURE__ */ jsx(
|
|
931
|
-
"
|
|
899
|
+
"button",
|
|
932
900
|
{
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
901
|
+
type: "button",
|
|
902
|
+
onClick: () => setCollapsed((c) => !c),
|
|
903
|
+
"aria-label": t("appShell.toggleSidebar"),
|
|
904
|
+
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",
|
|
905
|
+
style: { left: `calc(${asideWidth} - 14px)` },
|
|
906
|
+
children: effectiveCollapsed ? /* @__PURE__ */ jsx(PanelLeftOpen, { className: "size-4" }) : /* @__PURE__ */ jsx(PanelLeftClose, { className: "size-4" })
|
|
936
907
|
}
|
|
937
908
|
),
|
|
938
|
-
/* @__PURE__ */ jsxs("
|
|
939
|
-
|
|
940
|
-
/* @__PURE__ */ jsx(
|
|
941
|
-
|
|
909
|
+
/* @__PURE__ */ jsxs("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 }, children: [
|
|
910
|
+
renderSidebar(effectiveCollapsed, false, isSectionView),
|
|
911
|
+
sidebarScrollState !== "none" ? /* @__PURE__ */ jsx(
|
|
912
|
+
"div",
|
|
942
913
|
{
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
914
|
+
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",
|
|
915
|
+
children: /* @__PURE__ */ jsx(
|
|
916
|
+
IconButton,
|
|
917
|
+
{
|
|
918
|
+
type: "button",
|
|
919
|
+
variant: "ghost",
|
|
920
|
+
size: "sm",
|
|
921
|
+
"data-testid": "sidebar-scroll-chevron",
|
|
922
|
+
"data-sidebar-scroll-chevron": sidebarScrollState,
|
|
923
|
+
"aria-label": sidebarScrollState === "up" ? t("ui.sidebar.chevron.scrollTop", "Scroll to top") : t("ui.sidebar.chevron.scrollBottom", "Scroll to bottom"),
|
|
924
|
+
className: "pointer-events-auto text-muted-foreground/70 hover:text-foreground",
|
|
925
|
+
onClick: () => handleSidebarChevronScroll(sidebarScrollState === "up" ? "top" : "bottom"),
|
|
926
|
+
children: /* @__PURE__ */ jsx(
|
|
927
|
+
"span",
|
|
928
|
+
{
|
|
929
|
+
className: `inline-flex transition-transform duration-300 ${sidebarScrollState === "up" ? "rotate-180" : ""}`,
|
|
930
|
+
children: /* @__PURE__ */ jsx(ChevronDown, { className: "size-4 animate-bounce" })
|
|
931
|
+
}
|
|
932
|
+
)
|
|
933
|
+
}
|
|
934
|
+
)
|
|
964
935
|
}
|
|
965
|
-
|
|
966
|
-
const lastIndex = items.length - 1;
|
|
967
|
-
return /* @__PURE__ */ jsx("nav", { className: "flex items-center gap-2 text-sm min-w-0", children: items.map((b, i) => {
|
|
968
|
-
const isLast = i === lastIndex;
|
|
969
|
-
const hiddenOnMobile = !isLast ? "hidden md:inline" : "";
|
|
970
|
-
return /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
971
|
-
i > 0 && /* @__PURE__ */ jsx("span", { className: `text-muted-foreground hidden md:inline`, children: "/" }),
|
|
972
|
-
b.href ? /* @__PURE__ */ jsx(Link, { href: b.href, className: `text-muted-foreground hover:text-foreground ${hiddenOnMobile}`, children: b.label }) : /* @__PURE__ */ jsx("span", { className: `font-medium truncate max-w-[45vw] md:max-w-[60vw]`, children: b.label })
|
|
973
|
-
] }, i);
|
|
974
|
-
}) });
|
|
975
|
-
})()
|
|
936
|
+
) : null
|
|
976
937
|
] }),
|
|
977
|
-
/* @__PURE__ */ jsxs(
|
|
978
|
-
|
|
979
|
-
StatusBadgeInjectionSpot,
|
|
980
|
-
{
|
|
981
|
-
spotId: GLOBAL_HEADER_STATUS_INDICATORS_INJECTION_SPOT_ID,
|
|
982
|
-
context: injectionContext
|
|
983
|
-
}
|
|
984
|
-
),
|
|
985
|
-
/* @__PURE__ */ jsx(
|
|
986
|
-
InjectionSpot,
|
|
987
|
-
{
|
|
988
|
-
spotId: BACKEND_TOPBAR_ACTIONS_INJECTION_SPOT_ID,
|
|
989
|
-
context: injectionContext
|
|
990
|
-
}
|
|
991
|
-
),
|
|
992
|
-
renderedTopbarInjectedActions,
|
|
993
|
-
/* @__PURE__ */ jsx(AiAssistantLauncher, { variant: "topbar" }),
|
|
994
|
-
rightHeaderSlot ? rightHeaderSlot : /* @__PURE__ */ jsx("span", { className: "opacity-80", children: email || t("appShell.userFallback") })
|
|
995
|
-
] })
|
|
996
|
-
] }),
|
|
997
|
-
/* @__PURE__ */ jsx(ProgressTopBar, { t, className: "sticky top-0 z-sticky" }),
|
|
998
|
-
/* @__PURE__ */ jsxs("main", { className: "flex-1 p-4 lg:p-6 mx-auto w-full max-w-screen-2xl", children: [
|
|
999
|
-
/* @__PURE__ */ jsx(InjectionSpot, { spotId: BACKEND_LAYOUT_TOP_INJECTION_SPOT_ID, context: injectionContext }),
|
|
1000
|
-
/* @__PURE__ */ jsx(FlashMessages, {}),
|
|
1001
|
-
/* @__PURE__ */ jsx(PartialIndexBanner, {}),
|
|
1002
|
-
/* @__PURE__ */ jsx(UpgradeActionBanner, {}),
|
|
1003
|
-
/* @__PURE__ */ jsx(LastOperationBanner, {}),
|
|
1004
|
-
/* @__PURE__ */ jsx(InjectionSpot, { spotId: BACKEND_RECORD_CURRENT_INJECTION_SPOT_ID, context: injectionContext }),
|
|
1005
|
-
/* @__PURE__ */ jsx(
|
|
1006
|
-
InjectionSpot,
|
|
938
|
+
isSectionView ? /* @__PURE__ */ jsxs(
|
|
939
|
+
"aside",
|
|
1007
940
|
{
|
|
1008
|
-
|
|
1009
|
-
|
|
941
|
+
className: `${asideClassesBase} px-3 hidden lg:block lg:sticky lg:top-0 lg:h-svh lg:self-start lg:overflow-hidden lg:relative`,
|
|
942
|
+
style: { width: "240px" },
|
|
943
|
+
"data-testid": "appshell-section-sidebar",
|
|
944
|
+
children: [
|
|
945
|
+
renderSectionAside(),
|
|
946
|
+
/* @__PURE__ */ jsx(
|
|
947
|
+
"div",
|
|
948
|
+
{
|
|
949
|
+
"aria-hidden": true,
|
|
950
|
+
className: "pointer-events-none absolute inset-x-0 bottom-0 h-10 bg-gradient-to-t from-background via-background/80 to-transparent"
|
|
951
|
+
}
|
|
952
|
+
)
|
|
953
|
+
]
|
|
1010
954
|
}
|
|
1011
|
-
),
|
|
1012
|
-
/* @__PURE__ */
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
955
|
+
) : null,
|
|
956
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-h-svh flex-col min-w-0", children: [
|
|
957
|
+
/* @__PURE__ */ jsxs("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", children: [
|
|
958
|
+
/* @__PURE__ */ jsx(
|
|
959
|
+
"div",
|
|
960
|
+
{
|
|
961
|
+
"data-testid": "backend-chrome-ready",
|
|
962
|
+
"data-ready": isChromeReady ? "true" : "false",
|
|
963
|
+
className: "hidden"
|
|
964
|
+
}
|
|
965
|
+
),
|
|
966
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
|
|
967
|
+
/* @__PURE__ */ jsx(IconButton, { variant: "ghost", size: "sm", className: "lg:hidden", "aria-label": t("appShell.openMenu"), onClick: () => setMobileOpen(true), children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx("path", { d: "M3 6h18M3 12h18M3 18h18" }) }) }),
|
|
968
|
+
(() => {
|
|
969
|
+
const dashboardLabel = t("dashboard.title");
|
|
970
|
+
const root = [{ label: dashboardLabel, href: "/backend" }];
|
|
971
|
+
let rest = [];
|
|
972
|
+
if (headerBreadcrumb && headerBreadcrumb.length) {
|
|
973
|
+
const first = headerBreadcrumb[0];
|
|
974
|
+
const dup = first && (first.href === "/backend" || first.label === dashboardLabel || first.label?.toLowerCase() === "dashboard");
|
|
975
|
+
rest = dup ? headerBreadcrumb.slice(1) : headerBreadcrumb;
|
|
976
|
+
} else if (headerTitle) {
|
|
977
|
+
rest = [{ label: headerTitle }];
|
|
978
|
+
}
|
|
979
|
+
const items = [...root, ...rest];
|
|
980
|
+
if (items.length === 0) return null;
|
|
981
|
+
const home = items[0];
|
|
982
|
+
const current = items.length > 1 ? items[items.length - 1] : null;
|
|
983
|
+
const mid = items.slice(1, -1);
|
|
984
|
+
const hasMid = mid.length > 0;
|
|
985
|
+
return /* @__PURE__ */ jsx(BreadcrumbNav, { divider: "arrow", className: "ml-2 lg:ml-3 text-sm", children: /* @__PURE__ */ jsxs(BreadcrumbList, { className: "[&_[data-slot=breadcrumb-separator]_svg]:size-4", children: [
|
|
986
|
+
/* @__PURE__ */ jsx(BreadcrumbItem, { children: home.href && current ? /* @__PURE__ */ jsx(BreadcrumbLink, { asChild: true, "aria-label": home.label, children: /* @__PURE__ */ jsx(Link, { href: home.href, children: /* @__PURE__ */ jsx(Home, { className: "size-4", "aria-hidden": "true" }) }) }) : /* @__PURE__ */ jsx(BreadcrumbPage, { "aria-label": home.label, children: /* @__PURE__ */ jsx(Home, { className: "size-4", "aria-hidden": "true" }) }) }),
|
|
987
|
+
current ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
988
|
+
hasMid ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
989
|
+
/* @__PURE__ */ jsx(BreadcrumbSeparator, { className: "md:hidden" }),
|
|
990
|
+
/* @__PURE__ */ jsx(BreadcrumbItem, { className: "md:hidden", children: /* @__PURE__ */ jsx(BreadcrumbEllipsis, { "aria-label": t("appShell.breadcrumb.collapsed", { count: mid.length }) }) }),
|
|
991
|
+
mid.map((b, i) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
992
|
+
/* @__PURE__ */ jsx(BreadcrumbSeparator, { className: "hidden md:inline-flex" }),
|
|
993
|
+
/* @__PURE__ */ jsx(BreadcrumbItem, { className: "hidden md:inline-flex", children: b.href ? /* @__PURE__ */ jsx(BreadcrumbLink, { asChild: true, title: b.label, children: /* @__PURE__ */ jsx(Link, { href: b.href, children: b.label }) }) : /* @__PURE__ */ jsx(BreadcrumbLink, { title: b.label, "aria-disabled": "true", tabIndex: -1, children: b.label }) })
|
|
994
|
+
] }, `mid-${i}`))
|
|
995
|
+
] }) : null,
|
|
996
|
+
/* @__PURE__ */ jsx(BreadcrumbSeparator, {}),
|
|
997
|
+
/* @__PURE__ */ jsx(BreadcrumbItem, { children: /* @__PURE__ */ jsx(BreadcrumbPage, { title: current.label, children: current.label }) })
|
|
998
|
+
] }) : null
|
|
999
|
+
] }) });
|
|
1000
|
+
})()
|
|
1001
|
+
] }),
|
|
1002
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 sm:gap-2 md:gap-3 text-sm shrink-0", children: [
|
|
1003
|
+
/* @__PURE__ */ jsx(
|
|
1004
|
+
StatusBadgeInjectionSpot,
|
|
1005
|
+
{
|
|
1006
|
+
spotId: GLOBAL_HEADER_STATUS_INDICATORS_INJECTION_SPOT_ID,
|
|
1007
|
+
context: injectionContext
|
|
1008
|
+
}
|
|
1009
|
+
),
|
|
1010
|
+
/* @__PURE__ */ jsx(
|
|
1011
|
+
InjectionSpot,
|
|
1012
|
+
{
|
|
1013
|
+
spotId: BACKEND_TOPBAR_ACTIONS_INJECTION_SPOT_ID,
|
|
1014
|
+
context: injectionContext
|
|
1015
|
+
}
|
|
1016
|
+
),
|
|
1017
|
+
renderedTopbarInjectedActions,
|
|
1018
|
+
/* @__PURE__ */ jsx(AiAssistantLauncher, { variant: "topbar" }),
|
|
1019
|
+
rightHeaderSlot ? rightHeaderSlot : /* @__PURE__ */ jsx("span", { className: "opacity-80", children: email || t("appShell.userFallback") })
|
|
1020
|
+
] })
|
|
1031
1021
|
] }),
|
|
1032
|
-
/* @__PURE__ */ jsx(
|
|
1022
|
+
/* @__PURE__ */ jsx(ProgressTopBar, { t, className: "sticky top-0 z-sticky" }),
|
|
1023
|
+
/* @__PURE__ */ jsxs("main", { className: "flex-1 p-4 lg:p-6 mx-auto w-full max-w-screen-2xl", children: [
|
|
1024
|
+
/* @__PURE__ */ jsx(InjectionSpot, { spotId: BACKEND_LAYOUT_TOP_INJECTION_SPOT_ID, context: injectionContext }),
|
|
1025
|
+
/* @__PURE__ */ jsx(FlashMessages, {}),
|
|
1026
|
+
/* @__PURE__ */ jsx(PartialIndexBanner, {}),
|
|
1027
|
+
/* @__PURE__ */ jsx(UpgradeActionBanner, {}),
|
|
1028
|
+
/* @__PURE__ */ jsx(LastOperationBanner, {}),
|
|
1029
|
+
/* @__PURE__ */ jsx(InjectionSpot, { spotId: BACKEND_RECORD_CURRENT_INJECTION_SPOT_ID, context: injectionContext }),
|
|
1030
|
+
/* @__PURE__ */ jsx(
|
|
1031
|
+
InjectionSpot,
|
|
1032
|
+
{
|
|
1033
|
+
spotId: LEGACY_GLOBAL_MUTATION_INJECTION_SPOT_ID,
|
|
1034
|
+
context: injectionContext
|
|
1035
|
+
}
|
|
1036
|
+
),
|
|
1037
|
+
/* @__PURE__ */ jsx("div", { id: "om-top-banners", className: "mb-3 space-y-2" }),
|
|
1038
|
+
children,
|
|
1039
|
+
/* @__PURE__ */ jsx(InjectionSpot, { spotId: BACKEND_LAYOUT_FOOTER_INJECTION_SPOT_ID, context: injectionContext })
|
|
1040
|
+
] }),
|
|
1041
|
+
/* @__PURE__ */ jsxs("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", children: [
|
|
1042
|
+
version ? /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: t("appShell.version", { version }) }) : null,
|
|
1043
|
+
/* @__PURE__ */ jsxs("nav", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: [
|
|
1044
|
+
/* @__PURE__ */ jsx(Link, { href: "/terms", className: "transition hover:text-foreground", children: t("common.terms") }),
|
|
1045
|
+
/* @__PURE__ */ jsx(Link, { href: "/privacy", className: "transition hover:text-foreground", children: t("common.privacy") })
|
|
1046
|
+
] })
|
|
1047
|
+
] })
|
|
1033
1048
|
] }),
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1049
|
+
mobileOpen && /* @__PURE__ */ jsxs("div", { className: "lg:hidden fixed inset-0 z-modal", children: [
|
|
1050
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/50 backdrop-blur-sm", onClick: () => setMobileOpen(false), "aria-hidden": "true" }),
|
|
1051
|
+
/* @__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: [
|
|
1052
|
+
/* @__PURE__ */ jsxs("div", { className: "shrink-0 flex items-center justify-between gap-2 border-b px-4 py-3", children: [
|
|
1053
|
+
/* @__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: [
|
|
1054
|
+
/* @__PURE__ */ jsx(Image, { src: logo?.src ?? "/open-mercato.svg", alt: logo?.alt ?? resolvedProductName, width: 28, height: 28, className: "rounded shrink-0" }),
|
|
1055
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: resolvedProductName })
|
|
1056
|
+
] }),
|
|
1057
|
+
/* @__PURE__ */ jsx(IconButton, { variant: "ghost", size: "sm", onClick: () => setMobileOpen(false), "aria-label": t("appShell.closeMenu"), children: /* @__PURE__ */ jsx(X, { className: "size-4" }) })
|
|
1058
|
+
] }),
|
|
1059
|
+
mobileSidebarSlot && /* @__PURE__ */ jsx("div", { className: "shrink-0 border-b px-3 py-2", children: mobileSidebarSlot }),
|
|
1060
|
+
sidebarMode !== "main" ? /* @__PURE__ */ jsx("div", { className: "shrink-0 flex items-center gap-5 border-b px-4 pt-3 pb-0", role: "tablist", children: [
|
|
1061
|
+
{ id: "main", label: t("backend.nav.main", "Main") },
|
|
1062
|
+
{
|
|
1063
|
+
id: "section",
|
|
1064
|
+
label: sidebarMode === "settings" ? settingsSectionTitle ?? t("backend.nav.settings", "Settings") : profileSectionTitle ?? t("backend.nav.profile", "Profile")
|
|
1065
|
+
}
|
|
1066
|
+
].map((tab) => {
|
|
1067
|
+
const isActive = tab.id === "main" ? mobileDrawerView === "main" : mobileDrawerView === "auto";
|
|
1068
|
+
const tabId = `mobile-drawer-tab-${tab.id}`;
|
|
1069
|
+
return /* @__PURE__ */ jsxs(
|
|
1070
|
+
"button",
|
|
1071
|
+
{
|
|
1072
|
+
id: tabId,
|
|
1073
|
+
type: "button",
|
|
1074
|
+
role: "tab",
|
|
1075
|
+
"aria-selected": isActive,
|
|
1076
|
+
"aria-controls": "mobile-drawer-tabpanel",
|
|
1077
|
+
onClick: () => setMobileDrawerView(tab.id === "main" ? "main" : "auto"),
|
|
1078
|
+
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",
|
|
1079
|
+
"data-active": isActive,
|
|
1080
|
+
children: [
|
|
1081
|
+
/* @__PURE__ */ jsx("span", { children: tab.label }),
|
|
1082
|
+
isActive ? /* @__PURE__ */ jsx(
|
|
1083
|
+
"span",
|
|
1084
|
+
{
|
|
1085
|
+
className: "absolute -bottom-px left-0 right-0 h-0.5 bg-foreground",
|
|
1086
|
+
"aria-hidden": "true"
|
|
1087
|
+
}
|
|
1088
|
+
) : null
|
|
1089
|
+
]
|
|
1090
|
+
},
|
|
1091
|
+
tab.id
|
|
1092
|
+
);
|
|
1093
|
+
}) }) : null,
|
|
1094
|
+
/* @__PURE__ */ jsx(
|
|
1095
|
+
"div",
|
|
1096
|
+
{
|
|
1097
|
+
id: "mobile-drawer-tabpanel",
|
|
1098
|
+
role: sidebarMode !== "main" ? "tabpanel" : void 0,
|
|
1099
|
+
"aria-labelledby": sidebarMode !== "main" ? `mobile-drawer-tab-${mobileDrawerView === "main" ? "main" : "section"}` : void 0,
|
|
1100
|
+
className: "min-h-0 flex-1 overflow-y-auto overflow-x-hidden p-3",
|
|
1101
|
+
children: renderSidebar(false, true, mobileDrawerView === "main")
|
|
1102
|
+
}
|
|
1103
|
+
)
|
|
1104
|
+
] })
|
|
1105
|
+
] })
|
|
1106
|
+
]
|
|
1107
|
+
}
|
|
1108
|
+
),
|
|
1039
1109
|
/* @__PURE__ */ jsx(UmesDevToolsPanel, {})
|
|
1040
1110
|
] });
|
|
1041
1111
|
}
|