@pablo2410/shared-ui 0.4.0 → 0.5.0

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.
@@ -106,52 +106,56 @@ declare function useSidebarResize(config: {
106
106
  };
107
107
 
108
108
  interface DashboardLayoutUser {
109
- name: string;
109
+ name?: string;
110
110
  email?: string;
111
111
  role?: string;
112
112
  avatarUrl?: string;
113
113
  }
114
+ interface DashboardLayoutEnterprise {
115
+ name: string;
116
+ code?: string;
117
+ }
114
118
  interface DashboardLayoutProps {
115
- /** Service/module name displayed in the sidebar header */
119
+ /** Service/module name shown in the sidebar brand header (e.g. "OEE Manager"). */
116
120
  serviceName: string;
117
- /** Optional service icon (ReactNode) */
121
+ /** Service icon (a lucide icon element), shown in the sidebar brand + header. */
118
122
  serviceIcon?: ReactNode;
119
- /** Menu sections with items */
123
+ /** Primary navigation sections. */
120
124
  menuSections: MenuSection[];
121
- /** Currently active path/route */
125
+ /** Admin-only navigation sections, rendered when `isAdmin` is true. */
126
+ adminSections?: MenuSection[];
127
+ /** Whether the current user may see `adminSections`. */
128
+ isAdmin?: boolean;
129
+ /** Current route (the app's router supplies this). */
122
130
  activePath: string;
123
- /** Handler when a menu item is clicked */
131
+ /** Navigate handler (the app's router supplies this). */
124
132
  onNavigate: (path: string) => void;
125
- /** Current user info */
133
+ /** Optional override for active-item detection. */
134
+ isActive?: (itemPath: string, activePath: string) => boolean;
135
+ /** URL for the "← Service Hub" link at the top of the nav (full-page nav). */
136
+ serviceHubUrl?: string;
137
+ /** Footer "Settings" route. Omit to hide the footer. */
138
+ settingsPath?: string;
139
+ /** Authenticated user (drives the header user menu). */
126
140
  user?: DashboardLayoutUser | null;
127
- /** Whether the user is authenticated */
128
- isAuthenticated?: boolean;
129
- /** Whether auth state is loading */
130
- isLoading?: boolean;
131
- /** Logout handler */
141
+ /** Sign-out handler invoked from the user menu. */
132
142
  onLogout?: () => void;
133
- /** Login handler / redirect */
134
- onLogin?: () => void;
135
- /** Show "Back to Portal" link (default: false) */
136
- showBackToPortal?: boolean;
137
- /** Portal URL for "Back to Portal" link */
138
- portalUrl?: string;
139
- /** Whether user has admin role */
140
- isAdmin?: boolean;
141
- /** Admin menu sections (shown only for admins) */
142
- adminSections?: MenuSection[];
143
- /** Optional hierarchy navigator to render in the sidebar */
143
+ /** "User Settings" target in the user menu (full-page nav). */
144
+ userSettingsUrl?: string;
145
+ /** App-provided hierarchy navigator rendered in the header breadcrumb slot. */
144
146
  hierarchyNavigator?: ReactNode;
145
- /** Optional reporting toolbar to render in the header */
147
+ /** Selected enterprise drives the header enterprise badge. */
148
+ enterprise?: DashboardLayoutEnterprise | null;
149
+ /** App-provided reporting toolbar rendered on the right of the header. */
146
150
  reportingToolbar?: ReactNode;
147
- /** Loading skeleton to show while auth is loading */
148
- loadingSkeleton?: ReactNode;
149
- /** Main content */
151
+ /** Whether the sidebar starts expanded. Defaults to `!isMobile`. */
152
+ defaultOpen?: boolean;
153
+ /** Extra footer rendered fixed at the bottom (e.g. a debug/health bar). */
154
+ footer?: ReactNode;
155
+ /** Main content. */
150
156
  children: ReactNode;
151
- /** Additional CSS class for the outer container */
152
- className?: string;
153
157
  }
154
- declare function DashboardLayout({ serviceName, serviceIcon, menuSections, activePath, onNavigate, user, isAuthenticated, isLoading, onLogout, onLogin, showBackToPortal, portalUrl, isAdmin, adminSections, hierarchyNavigator, reportingToolbar, loadingSkeleton, children, className, }: DashboardLayoutProps): react_jsx_runtime.JSX.Element | null;
158
+ declare function DashboardLayout(props: DashboardLayoutProps): react_jsx_runtime.JSX.Element;
155
159
 
156
160
  interface DashboardLayoutSkeletonProps {
157
161
  className?: string;
@@ -353,4 +357,4 @@ declare const SERVICE_CONFIGS: {
353
357
  };
354
358
  type ServiceKey = keyof typeof SERVICE_CONFIGS;
355
359
 
356
- export { DashboardLayout, type DashboardLayoutProps, DashboardLayoutSkeleton, type DashboardLayoutSkeletonProps, type DashboardLayoutUser, FOOTER_SERVICES, type FooterService, type MenuItem, type MenuItemConfig, type MenuSection, type MenuSectionConfig, SERVICE_CONFIGS, type ServiceConfig, ServiceFooter, type ServiceFooterProps, type ServiceKey, type ServiceLayoutConfig, SharedFooter, type SharedFooterProps, SharedPageHeader, type SharedPageHeaderProps, type SharedSidebarConfig, type SharedSidebarProps, type SharedSidebarConfig as SidebarConfig, actionManagerConfig, businessHubConfig, defineServiceLayout, getInitials, isAdminRole, isMenuItemActive, oeeManagerConfig, policyDeploymentConfig, sqdcpConfig, useSidebarResize };
360
+ export { DashboardLayout, type DashboardLayoutEnterprise, type DashboardLayoutProps, DashboardLayoutSkeleton, type DashboardLayoutSkeletonProps, type DashboardLayoutUser, FOOTER_SERVICES, type FooterService, type MenuItem, type MenuItemConfig, type MenuSection, type MenuSectionConfig, SERVICE_CONFIGS, type ServiceConfig, ServiceFooter, type ServiceFooterProps, type ServiceKey, type ServiceLayoutConfig, SharedFooter, type SharedFooterProps, SharedPageHeader, type SharedPageHeaderProps, type SharedSidebarConfig, type SharedSidebarProps, type SharedSidebarConfig as SidebarConfig, actionManagerConfig, businessHubConfig, defineServiceLayout, getInitials, isAdminRole, isMenuItemActive, oeeManagerConfig, policyDeploymentConfig, sqdcpConfig, useSidebarResize };
@@ -1,6 +1,27 @@
1
1
  import {
2
2
  cn
3
3
  } from "../chunk-JT3XLKKD.js";
4
+ import {
5
+ Avatar,
6
+ AvatarFallback,
7
+ DropdownMenu,
8
+ DropdownMenuContent,
9
+ DropdownMenuItem,
10
+ DropdownMenuSeparator,
11
+ DropdownMenuTrigger,
12
+ Sidebar,
13
+ SidebarContent,
14
+ SidebarFooter,
15
+ SidebarHeader,
16
+ SidebarInset,
17
+ SidebarMenu,
18
+ SidebarMenuButton,
19
+ SidebarMenuItem,
20
+ SidebarProvider,
21
+ SidebarTrigger,
22
+ useIsMobile,
23
+ useSidebar
24
+ } from "../chunk-LTUYIBMA.js";
4
25
 
5
26
  // src/layout/SharedSidebar.tsx
6
27
  import { useEffect, useRef, useState } from "react";
@@ -64,278 +85,215 @@ function useSidebarResize(config) {
64
85
  }
65
86
 
66
87
  // src/layout/DashboardLayout.tsx
67
- import {
68
- ChevronLeft,
69
- ChevronRight,
70
- LogOut,
71
- Menu,
72
- X,
73
- ExternalLink
74
- } from "lucide-react";
75
- import {
76
- useState as useState2,
77
- useCallback
78
- } from "react";
88
+ import { ArrowLeft, LogOut, PanelLeft, Settings } from "lucide-react";
79
89
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
80
- function SidebarItem({
81
- item,
82
- isActive,
83
- collapsed,
84
- onNavigate
90
+ var DEFAULT_SERVICE_HUB_URL = "https://portal.oplytics.digital/app";
91
+ var DEFAULT_USER_SETTINGS_URL = "https://portal.oplytics.digital/account";
92
+ function UserMenu({
93
+ user,
94
+ onLogout,
95
+ userSettingsUrl = DEFAULT_USER_SETTINGS_URL
85
96
  }) {
86
- const Icon = item.icon;
87
- return /* @__PURE__ */ jsxs(
88
- "button",
89
- {
90
- onClick: () => onNavigate(item.path),
91
- className: cn(
92
- "w-full flex items-center gap-3 rounded-lg transition-all duration-200",
93
- "text-sm font-medium outline-none",
94
- "focus-visible:ring-1 focus-visible:ring-[#8C34E9]",
95
- collapsed ? "px-3 py-2.5 justify-center" : "px-3 py-2",
96
- isActive ? "bg-[#8C34E9]/15 text-[#A855F7] border-l-2 border-[#8C34E9]" : "text-[#8890A0] hover:bg-[#1E2738] hover:text-[#E2E8F0]"
97
+ const initials = (user.name || "U").split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2);
98
+ return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
99
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs("button", { className: "flex items-center gap-2 rounded-md px-2 py-1 hover:bg-[#1E2738] transition-colors outline-none focus-visible:ring-1 focus-visible:ring-[#8C34E9]", children: [
100
+ /* @__PURE__ */ jsx(Avatar, { className: "h-7 w-7 border border-[#2A2A3E]", children: /* @__PURE__ */ jsx(AvatarFallback, { className: "text-[10px] font-medium bg-[#8C34E9]/20 text-[#C084FC]", children: initials }) }),
101
+ /* @__PURE__ */ jsx("span", { className: "hidden md:block text-xs text-[#E2E8F0] truncate max-w-[100px]", children: user.name || "User" })
102
+ ] }) }),
103
+ /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", className: "w-48 bg-[#0D1220] border-[#1E2738]", children: [
104
+ /* @__PURE__ */ jsxs("div", { className: "px-3 py-2 border-b border-[#1E2738]", children: [
105
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-[#E2E8F0] truncate", children: user.name }),
106
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] text-[#596475] truncate", children: user.role })
107
+ ] }),
108
+ /* @__PURE__ */ jsxs(
109
+ DropdownMenuItem,
110
+ {
111
+ onClick: () => {
112
+ window.location.href = userSettingsUrl;
113
+ },
114
+ className: "text-xs text-[#E2E8F0] hover:bg-[#1E2738] cursor-pointer focus:bg-[#1E2738]",
115
+ children: [
116
+ /* @__PURE__ */ jsx(Settings, { className: "h-3.5 w-3.5 mr-2 text-[#8890A0]" }),
117
+ "User Settings"
118
+ ]
119
+ }
97
120
  ),
98
- title: collapsed ? item.label : void 0,
99
- children: [
100
- Icon && /* @__PURE__ */ jsx(Icon, { className: cn("shrink-0", collapsed ? "h-5 w-5" : "h-4 w-4") }),
101
- !collapsed && /* @__PURE__ */ jsx("span", { className: "truncate", children: item.label }),
102
- !collapsed && item.badge && /* @__PURE__ */ jsx("span", { className: "ml-auto shrink-0 text-[10px] font-semibold px-1.5 py-0.5 rounded-full bg-[#8C34E9]/20 text-[#A855F7]", children: item.badge })
103
- ]
104
- }
105
- );
121
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, { className: "bg-[#1E2738]" }),
122
+ /* @__PURE__ */ jsxs(
123
+ DropdownMenuItem,
124
+ {
125
+ onClick: () => onLogout?.(),
126
+ className: "text-xs text-[#EF4444] hover:bg-[#EF4444]/10 cursor-pointer focus:bg-[#EF4444]/10 focus:text-[#EF4444]",
127
+ children: [
128
+ /* @__PURE__ */ jsx(LogOut, { className: "h-3.5 w-3.5 mr-2" }),
129
+ "Sign Out"
130
+ ]
131
+ }
132
+ )
133
+ ] })
134
+ ] });
135
+ }
136
+ function EnterpriseBadge({ enterprise }) {
137
+ if (!enterprise) return null;
138
+ return /* @__PURE__ */ jsxs("div", { className: "hidden lg:flex items-center gap-1.5 px-2 py-1 rounded-md bg-[#8C34E9]/10 border border-[#8C34E9]/20", children: [
139
+ /* @__PURE__ */ jsx("div", { className: "h-4 w-4 rounded-sm bg-[#8C34E9]/30 flex items-center justify-center", children: /* @__PURE__ */ jsx("span", { className: "text-[8px] font-bold text-[#C084FC]", children: enterprise.name.charAt(0).toUpperCase() }) }),
140
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium text-[#C084FC] truncate max-w-[80px]", children: enterprise.code || enterprise.name })
141
+ ] });
106
142
  }
107
- function SidebarSection({
143
+ function NavSection({
108
144
  section,
109
- activePath,
110
- collapsed,
145
+ marginTop,
146
+ isCollapsed,
147
+ resolveActive,
111
148
  onNavigate
112
149
  }) {
113
- return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
114
- !collapsed && section.title && /* @__PURE__ */ jsx("p", { className: "px-3 py-1 text-[10px] font-semibold uppercase tracking-wider text-[#596475]", children: section.title }),
115
- collapsed && section.title && /* @__PURE__ */ jsx("div", { className: "mx-auto w-6 border-t border-[#1E2738] my-2" }),
116
- section.items.map((item) => /* @__PURE__ */ jsx(
117
- SidebarItem,
118
- {
119
- item,
120
- isActive: isMenuItemActive(item.path, activePath),
121
- collapsed,
122
- onNavigate
123
- },
124
- item.path
125
- ))
150
+ const label = section.title ?? section.label;
151
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
152
+ label && /* @__PURE__ */ jsx("div", { className: cn("px-4 py-2", marginTop && "mt-2"), children: !isCollapsed && /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wider", children: label }) }),
153
+ /* @__PURE__ */ jsx(SidebarMenu, { className: "px-2 py-1", children: section.items.map((item) => {
154
+ const active = resolveActive(item.path);
155
+ const Icon = item.icon;
156
+ return /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(
157
+ SidebarMenuButton,
158
+ {
159
+ isActive: active,
160
+ onClick: () => onNavigate(item.path),
161
+ tooltip: item.label,
162
+ className: "h-10 transition-all font-normal",
163
+ children: [
164
+ Icon && /* @__PURE__ */ jsx(Icon, { className: cn("h-4 w-4", active && "text-primary") }),
165
+ /* @__PURE__ */ jsx("span", { children: item.label })
166
+ ]
167
+ }
168
+ ) }, item.path);
169
+ }) })
126
170
  ] });
127
171
  }
128
- function UserProfile({
129
- user,
130
- collapsed,
131
- onLogout
132
- }) {
133
- const initials = getInitials(user.name);
134
- return /* @__PURE__ */ jsxs(
135
- "div",
136
- {
137
- className: cn(
138
- "border-t border-[#1E2738] p-3",
139
- collapsed ? "flex flex-col items-center gap-2" : "flex items-center gap-3"
140
- ),
141
- children: [
142
- user.avatarUrl ? /* @__PURE__ */ jsx(
143
- "img",
144
- {
145
- src: user.avatarUrl,
146
- alt: user.name,
147
- className: "h-8 w-8 rounded-full shrink-0 object-cover"
148
- }
149
- ) : /* @__PURE__ */ jsx("div", { className: "h-8 w-8 rounded-full shrink-0 bg-[#8C34E9]/20 flex items-center justify-center", children: /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-[#A855F7]", children: initials }) }),
150
- !collapsed && /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
151
- /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-[#E2E8F0] truncate", children: user.name }),
152
- user.email && /* @__PURE__ */ jsx("p", { className: "text-[10px] text-[#596475] truncate", children: user.email }),
153
- user.role && /* @__PURE__ */ jsx("p", { className: "text-[10px] text-[#8C34E9] capitalize", children: user.role })
154
- ] }),
155
- onLogout && /* @__PURE__ */ jsx(
156
- "button",
157
- {
158
- onClick: onLogout,
159
- className: cn(
160
- "shrink-0 p-1.5 rounded-md",
161
- "text-[#596475] hover:text-[#E2E8F0] hover:bg-[#1E2738]",
162
- "transition-colors cursor-pointer",
163
- "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[#8C34E9]"
164
- ),
165
- title: "Sign out",
166
- children: /* @__PURE__ */ jsx(LogOut, { className: "h-4 w-4" })
167
- }
168
- )
169
- ]
170
- }
171
- );
172
- }
173
- function DashboardLayout({
172
+ function DashboardShell({
174
173
  serviceName,
175
174
  serviceIcon,
176
175
  menuSections,
176
+ adminSections,
177
+ isAdmin = false,
177
178
  activePath,
178
179
  onNavigate,
180
+ isActive,
181
+ serviceHubUrl = DEFAULT_SERVICE_HUB_URL,
182
+ settingsPath,
179
183
  user,
180
- isAuthenticated = true,
181
- isLoading = false,
182
184
  onLogout,
183
- onLogin,
184
- showBackToPortal = false,
185
- portalUrl = "https://portal.oplytics.digital",
186
- isAdmin = false,
187
- adminSections,
185
+ userSettingsUrl,
188
186
  hierarchyNavigator,
187
+ enterprise,
189
188
  reportingToolbar,
190
- loadingSkeleton,
191
- children,
192
- className
189
+ footer,
190
+ children
193
191
  }) {
194
- const [collapsed, setCollapsed] = useState2(false);
195
- const [mobileOpen, setMobileOpen] = useState2(false);
196
- const { width: sidebarWidth } = useSidebarResize({
197
- storageKey: `oplytics-sidebar-${serviceName}`,
198
- defaultWidth: collapsed ? 72 : 260,
199
- minWidth: 72,
200
- maxWidth: 480
201
- });
202
- const toggleCollapse = useCallback(() => setCollapsed((c) => !c), []);
203
- const toggleMobile = useCallback(() => setMobileOpen((o) => !o), []);
204
- const closeMobile = useCallback(() => setMobileOpen(false), []);
205
- const handleNavigate = useCallback(
206
- (path) => {
207
- onNavigate(path);
208
- closeMobile();
209
- },
210
- [onNavigate, closeMobile]
211
- );
212
- if (isLoading && loadingSkeleton) {
213
- return /* @__PURE__ */ jsx(Fragment, { children: loadingSkeleton });
214
- }
215
- if (!isLoading && !isAuthenticated && onLogin) {
216
- onLogin();
217
- return loadingSkeleton ? /* @__PURE__ */ jsx(Fragment, { children: loadingSkeleton }) : null;
218
- }
219
- return /* @__PURE__ */ jsxs("div", { className: cn("min-h-screen flex", "bg-[#0A0E1A]", className), children: [
220
- mobileOpen && /* @__PURE__ */ jsx(
221
- "div",
222
- {
223
- className: "fixed inset-0 z-40 bg-black/60 lg:hidden",
224
- onClick: closeMobile
225
- }
226
- ),
227
- /* @__PURE__ */ jsxs(
228
- "aside",
229
- {
230
- className: cn(
231
- "fixed top-0 left-0 h-full z-50 flex flex-col",
232
- "bg-[#0D1220] border-r border-[#1E2738]",
233
- "transition-all duration-300",
234
- // Mobile: slide in/out
235
- "lg:relative lg:translate-x-0",
236
- mobileOpen ? "translate-x-0" : "-translate-x-full"
237
- ),
238
- style: { width: sidebarWidth },
239
- children: [
240
- /* @__PURE__ */ jsxs("div", { className: cn(
241
- "flex items-center gap-2 p-4 border-b border-[#1E2738]",
242
- collapsed && "justify-center px-2"
243
- ), children: [
244
- serviceIcon && /* @__PURE__ */ jsx("div", { className: "shrink-0 text-[#8C34E9]", children: serviceIcon }),
245
- !collapsed && /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-[#E2E8F0] truncate font-[Montserrat,sans-serif]", children: serviceName }),
246
- /* @__PURE__ */ jsx(
247
- "button",
248
- {
249
- onClick: toggleCollapse,
250
- className: cn(
251
- "hidden lg:flex shrink-0 p-1 rounded-md ml-auto",
252
- "text-[#596475] hover:text-[#E2E8F0] hover:bg-[#1E2738]",
253
- "transition-colors cursor-pointer",
254
- collapsed && "ml-0"
255
- ),
256
- children: collapsed ? /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
257
- }
258
- ),
259
- /* @__PURE__ */ jsx(
260
- "button",
261
- {
262
- onClick: closeMobile,
263
- className: "lg:hidden shrink-0 p-1 rounded-md ml-auto text-[#596475] hover:text-[#E2E8F0]",
264
- children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
265
- }
266
- )
267
- ] }),
268
- hierarchyNavigator && !collapsed && /* @__PURE__ */ jsx("div", { className: "px-3 py-2 border-b border-[#1E2738]", children: hierarchyNavigator }),
269
- showBackToPortal && /* @__PURE__ */ jsx("div", { className: cn("px-3 py-2", collapsed && "px-2"), children: /* @__PURE__ */ jsxs(
270
- "a",
271
- {
272
- href: portalUrl,
273
- className: cn(
274
- "flex items-center gap-2 px-3 py-1.5 rounded-md text-xs",
275
- "text-[#596475] hover:text-[#8890A0] hover:bg-[#1E2738]",
276
- "transition-colors",
277
- collapsed && "justify-center px-2"
278
- ),
279
- children: [
280
- /* @__PURE__ */ jsx(ExternalLink, { className: "h-3.5 w-3.5 shrink-0" }),
281
- !collapsed && /* @__PURE__ */ jsx("span", { children: "Back to Portal" })
282
- ]
283
- }
284
- ) }),
285
- /* @__PURE__ */ jsxs("nav", { className: "flex-1 overflow-y-auto px-3 py-3 space-y-4", children: [
286
- menuSections.map((section, idx) => /* @__PURE__ */ jsx(
287
- SidebarSection,
288
- {
289
- section,
290
- activePath,
291
- collapsed,
292
- onNavigate: handleNavigate
293
- },
294
- section.title || `section-${idx}`
295
- )),
296
- isAdmin && adminSections && adminSections.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
297
- !collapsed && /* @__PURE__ */ jsx("div", { className: "mx-3 border-t border-[#1E2738] my-2" }),
298
- adminSections.map((section, idx) => /* @__PURE__ */ jsx(
299
- SidebarSection,
300
- {
301
- section,
302
- activePath,
303
- collapsed,
304
- onNavigate: handleNavigate
305
- },
306
- section.title || `admin-${idx}`
307
- ))
308
- ] })
309
- ] }),
310
- user && /* @__PURE__ */ jsx(
311
- UserProfile,
312
- {
313
- user,
314
- collapsed,
315
- onLogout
316
- }
317
- )
318
- ]
319
- }
320
- ),
321
- /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col min-w-0", children: [
322
- /* @__PURE__ */ jsxs("header", { className: "lg:hidden flex items-center gap-3 px-4 py-2.5 border-b border-[#1E2738] bg-[#0D1220]", children: [
192
+ const { toggleSidebar, state } = useSidebar();
193
+ const isCollapsed = state === "collapsed";
194
+ const navigate = onNavigate;
195
+ const resolveActive = (path) => isActive ? isActive(path, activePath) : isMenuItemActive(path, activePath);
196
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
197
+ /* @__PURE__ */ jsxs(Sidebar, { collapsible: "icon", className: "border-r-0", children: [
198
+ /* @__PURE__ */ jsx(SidebarHeader, { className: "h-16 justify-center", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-2 transition-all w-full", children: [
323
199
  /* @__PURE__ */ jsx(
324
200
  "button",
325
201
  {
326
- onClick: toggleMobile,
327
- className: "p-1.5 rounded-md text-[#8890A0] hover:text-[#E2E8F0] hover:bg-[#1E2738]",
328
- children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
202
+ onClick: toggleSidebar,
203
+ className: "h-8 w-8 flex items-center justify-center hover:bg-accent rounded-lg transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-ring shrink-0",
204
+ "aria-label": "Toggle navigation",
205
+ children: /* @__PURE__ */ jsx(PanelLeft, { className: "h-4 w-4 text-muted-foreground" })
329
206
  }
330
207
  ),
331
- /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-[#E2E8F0] font-[Montserrat,sans-serif]", children: serviceName }),
332
- reportingToolbar && /* @__PURE__ */ jsx("div", { className: "ml-auto", children: reportingToolbar })
208
+ !isCollapsed ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
209
+ serviceIcon && /* @__PURE__ */ jsx("span", { className: "text-[#8C34E9] shrink-0", children: serviceIcon }),
210
+ /* @__PURE__ */ jsx("span", { className: "font-semibold tracking-tight truncate", children: serviceName })
211
+ ] }) : null
212
+ ] }) }),
213
+ /* @__PURE__ */ jsxs(SidebarContent, { className: "gap-0", children: [
214
+ serviceHubUrl && /* @__PURE__ */ jsx(SidebarMenu, { className: "px-2 pt-2 pb-1", children: /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(
215
+ SidebarMenuButton,
216
+ {
217
+ onClick: () => {
218
+ window.location.href = serviceHubUrl;
219
+ },
220
+ tooltip: "Back to Service Hub",
221
+ className: "h-10 transition-all font-normal text-muted-foreground hover:text-foreground",
222
+ children: [
223
+ /* @__PURE__ */ jsx(ArrowLeft, { className: "h-4 w-4" }),
224
+ /* @__PURE__ */ jsx("span", { children: "Service Hub" })
225
+ ]
226
+ }
227
+ ) }) }),
228
+ menuSections.map((section, i) => /* @__PURE__ */ jsx(
229
+ NavSection,
230
+ {
231
+ section,
232
+ isCollapsed,
233
+ resolveActive,
234
+ onNavigate: navigate
235
+ },
236
+ section.title ?? section.label ?? `section-${i}`
237
+ )),
238
+ isAdmin && adminSections?.map((section, i) => /* @__PURE__ */ jsx(
239
+ NavSection,
240
+ {
241
+ section,
242
+ marginTop: true,
243
+ isCollapsed,
244
+ resolveActive,
245
+ onNavigate: navigate
246
+ },
247
+ section.title ?? section.label ?? `admin-${i}`
248
+ ))
333
249
  ] }),
334
- reportingToolbar && /* @__PURE__ */ jsx("div", { className: "hidden lg:flex items-center justify-end px-4 py-1.5 border-b border-[#1E2738] bg-[#0D1220]/50", children: reportingToolbar }),
335
- /* @__PURE__ */ jsx("main", { className: "flex-1 overflow-y-auto", children })
336
- ] })
250
+ settingsPath && /* @__PURE__ */ jsx(SidebarFooter, { className: "p-2", children: /* @__PURE__ */ jsx(SidebarMenu, { children: /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(
251
+ SidebarMenuButton,
252
+ {
253
+ onClick: () => navigate(settingsPath),
254
+ tooltip: "Settings",
255
+ className: "h-10 transition-all font-normal text-muted-foreground hover:text-foreground",
256
+ children: [
257
+ /* @__PURE__ */ jsx(Settings, { className: "h-4 w-4" }),
258
+ /* @__PURE__ */ jsx("span", { children: "Settings" })
259
+ ]
260
+ }
261
+ ) }) }) })
262
+ ] }),
263
+ /* @__PURE__ */ jsxs(SidebarInset, { children: [
264
+ /* @__PURE__ */ jsxs(
265
+ "header",
266
+ {
267
+ className: cn(
268
+ "flex items-center justify-between h-14 px-4",
269
+ "bg-[#0D1220]/95 border-b border-[#1E2738]",
270
+ "backdrop-blur supports-[backdrop-filter]:backdrop-blur",
271
+ "sticky top-0 z-40"
272
+ ),
273
+ children: [
274
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0 flex-1", children: [
275
+ /* @__PURE__ */ jsx(SidebarTrigger, { className: "h-9 w-9 rounded-lg bg-background shrink-0 md:hidden" }),
276
+ serviceIcon && /* @__PURE__ */ jsx("span", { className: "text-[#8C34E9] shrink-0 hidden sm:block", children: serviceIcon }),
277
+ hierarchyNavigator && /* @__PURE__ */ jsx("div", { className: "min-w-0 overflow-x-auto scrollbar-none", children: hierarchyNavigator })
278
+ ] }),
279
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [
280
+ reportingToolbar,
281
+ /* @__PURE__ */ jsx(EnterpriseBadge, { enterprise }),
282
+ /* @__PURE__ */ jsx("div", { className: "w-px h-6 bg-[#1E2738] hidden md:block" }),
283
+ user && /* @__PURE__ */ jsx(UserMenu, { user, onLogout, userSettingsUrl })
284
+ ] })
285
+ ]
286
+ }
287
+ ),
288
+ /* @__PURE__ */ jsx("main", { className: "flex-1 p-4 md:p-6 pb-14", children })
289
+ ] }),
290
+ footer
337
291
  ] });
338
292
  }
293
+ function DashboardLayout(props) {
294
+ const isMobile = useIsMobile();
295
+ return /* @__PURE__ */ jsx(SidebarProvider, { defaultOpen: props.defaultOpen ?? !isMobile, children: /* @__PURE__ */ jsx(DashboardShell, { ...props }) });
296
+ }
339
297
 
340
298
  // src/layout/DashboardLayoutSkeleton.tsx
341
299
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
@@ -398,7 +356,7 @@ function DashboardLayoutSkeleton({ className }) {
398
356
  }
399
357
 
400
358
  // src/layout/SharedPageHeader.tsx
401
- import { ChevronLeft as ChevronLeft2 } from "lucide-react";
359
+ import { ChevronLeft } from "lucide-react";
402
360
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
403
361
  function SharedPageHeader({
404
362
  title,
@@ -436,7 +394,7 @@ function SharedPageHeader({
436
394
  "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[#8C34E9]"
437
395
  ),
438
396
  "aria-label": "Go back",
439
- children: /* @__PURE__ */ jsx3(ChevronLeft2, { className: compact ? "h-4 w-4" : "h-5 w-5" })
397
+ children: /* @__PURE__ */ jsx3(ChevronLeft, { className: compact ? "h-4 w-4" : "h-5 w-5" })
440
398
  }
441
399
  ),
442
400
  icon && /* @__PURE__ */ jsx3("div", { className: "shrink-0 text-[#8C34E9]", children: icon }),