@orsetra/shared-ui 1.1.1 → 1.1.2
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.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Layout components exports
|
|
2
2
|
export { MainSidebar, type SidebarMode } from './sidebar/main-sidebar'
|
|
3
|
-
export {
|
|
3
|
+
export { resolveIcon, type MainMenuItem, type SubMenuItem, type SidebarMenus, type MenuApiResponse, type ApiMenuData, type ApiMainMenuItem, type ApiSubMenuItem, type ApiSubMenu } from './sidebar/data'
|
|
4
4
|
export {
|
|
5
5
|
Sidebar,
|
|
6
6
|
SidebarContent,
|
|
@@ -4,7 +4,7 @@ import { useState, useEffect } from "react"
|
|
|
4
4
|
import { usePathname } from "next/navigation"
|
|
5
5
|
import { MainSidebar, type SidebarMode } from "./sidebar/main-sidebar"
|
|
6
6
|
import { Sidebar, SidebarProvider, useSidebar } from "./sidebar/sidebar"
|
|
7
|
-
import { type SidebarMenus } from "./sidebar/data"
|
|
7
|
+
import { type SidebarMenus, type MainMenuItem, type MenuApiResponse, resolveIcon } from "./sidebar/data"
|
|
8
8
|
import { UserMenu, Button, type UserMenuConfig } from "../ui"
|
|
9
9
|
import { getMenuFromPath } from "../../lib/menu-utils"
|
|
10
10
|
import { useIsMobile } from "../../hooks/use-mobile"
|
|
@@ -13,14 +13,15 @@ import { Menu } from "lucide-react"
|
|
|
13
13
|
interface LayoutContainerProps {
|
|
14
14
|
main_base_url: string
|
|
15
15
|
children: React.ReactNode
|
|
16
|
-
sidebarMenus
|
|
16
|
+
sidebarMenus?: SidebarMenus
|
|
17
17
|
user?: { profile?: { email?: string; preferred_username?: string } } | null
|
|
18
18
|
onSignOut?: () => void
|
|
19
19
|
mode?: SidebarMode
|
|
20
20
|
userMenuConfig?: UserMenuConfig
|
|
21
|
+
fetchMenus?: () => Promise<MenuApiResponse>
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
function LayoutContent({ children, sidebarMenus, user, onSignOut, mode = 'expanded', userMenuConfig, main_base_url }: LayoutContainerProps) {
|
|
24
|
+
function LayoutContent({ children, sidebarMenus = {}, user, onSignOut, mode = 'expanded', userMenuConfig, main_base_url, fetchMenus }: LayoutContainerProps) {
|
|
24
25
|
const pathname = usePathname()
|
|
25
26
|
const { setOpen } = useSidebar()
|
|
26
27
|
const isMobile = useIsMobile()
|
|
@@ -30,6 +31,45 @@ function LayoutContent({ children, sidebarMenus, user, onSignOut, mode = 'expand
|
|
|
30
31
|
// Force hidden mode on mobile
|
|
31
32
|
const isHidden = mode === 'hidden' || isMobile
|
|
32
33
|
|
|
34
|
+
const [mainMenuItems, setMainMenuItems] = useState<MainMenuItem[]>([])
|
|
35
|
+
const [fetchedSidebarMenus, setFetchedSidebarMenus] = useState<SidebarMenus>({})
|
|
36
|
+
const [sectionLabels, setSectionLabels] = useState<Record<string, string>>({})
|
|
37
|
+
|
|
38
|
+
// Load menus from API when fetchMenus is provided
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (!fetchMenus) return
|
|
41
|
+
fetchMenus().then((response) => {
|
|
42
|
+
if (!response.success) return
|
|
43
|
+
const { main, subMenus } = response.data
|
|
44
|
+
|
|
45
|
+
setMainMenuItems(
|
|
46
|
+
main.map((item) => ({
|
|
47
|
+
id: item.id,
|
|
48
|
+
label: item.label,
|
|
49
|
+
icon: resolveIcon(item.icon),
|
|
50
|
+
}))
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
const menus: SidebarMenus = {}
|
|
54
|
+
const labels: Record<string, string> = {}
|
|
55
|
+
Object.entries(subMenus).forEach(([key, subMenu]) => {
|
|
56
|
+
menus[key] = subMenu.items.map((item) => ({
|
|
57
|
+
id: item.id,
|
|
58
|
+
name: item.label,
|
|
59
|
+
href: item.href,
|
|
60
|
+
icon: resolveIcon(item.icon),
|
|
61
|
+
}))
|
|
62
|
+
labels[key] = subMenu.header
|
|
63
|
+
})
|
|
64
|
+
setFetchedSidebarMenus(menus)
|
|
65
|
+
setSectionLabels(labels)
|
|
66
|
+
})
|
|
67
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
68
|
+
}, [])
|
|
69
|
+
|
|
70
|
+
// Effective sidebarMenus: fetched provides section keys, prop provides items key (micro apps)
|
|
71
|
+
const effectiveSidebarMenus: SidebarMenus = { ...fetchedSidebarMenus, ...sidebarMenus }
|
|
72
|
+
|
|
33
73
|
// Close sidebar on route change (mobile)
|
|
34
74
|
useEffect(() => {
|
|
35
75
|
if (isMobile) {
|
|
@@ -54,9 +94,6 @@ function LayoutContent({ children, sidebarMenus, user, onSignOut, mode = 'expand
|
|
|
54
94
|
setOpen(true)
|
|
55
95
|
}
|
|
56
96
|
|
|
57
|
-
const module = pathname.split('/')[1] || 'overview'
|
|
58
|
-
const sidebarItems = sidebarMenus[module] || []
|
|
59
|
-
|
|
60
97
|
return (
|
|
61
98
|
<div className="flex h-screen w-full bg-white">
|
|
62
99
|
{/* Desktop sidebar - hidden on mobile (isHidden is true on mobile) */}
|
|
@@ -64,13 +101,14 @@ function LayoutContent({ children, sidebarMenus, user, onSignOut, mode = 'expand
|
|
|
64
101
|
<Sidebar
|
|
65
102
|
currentMenu={currentMenu}
|
|
66
103
|
onMainMenuToggle={handleMainSidebarToggle}
|
|
67
|
-
sidebarMenus={
|
|
104
|
+
sidebarMenus={effectiveSidebarMenus}
|
|
68
105
|
main_base_url={main_base_url}
|
|
106
|
+
sectionLabels={sectionLabels}
|
|
69
107
|
/>
|
|
70
108
|
)}
|
|
71
|
-
|
|
109
|
+
|
|
72
110
|
{/* MainSidebar - always available, opens as overlay when isHidden */}
|
|
73
|
-
<MainSidebar
|
|
111
|
+
<MainSidebar
|
|
74
112
|
main_base_url={main_base_url}
|
|
75
113
|
isOpen={isMainSidebarOpen}
|
|
76
114
|
onToggle={handleMainSidebarToggle}
|
|
@@ -78,9 +116,10 @@ function LayoutContent({ children, sidebarMenus, user, onSignOut, mode = 'expand
|
|
|
78
116
|
currentMenu={currentMenu}
|
|
79
117
|
onSecondarySidebarOpen={handleSecondarySidebarOpen}
|
|
80
118
|
mode={isHidden ? "expanded" : mode}
|
|
81
|
-
sidebarMenus={
|
|
119
|
+
sidebarMenus={effectiveSidebarMenus}
|
|
120
|
+
mainMenuItems={mainMenuItems}
|
|
82
121
|
/>
|
|
83
|
-
|
|
122
|
+
|
|
84
123
|
<div className="flex-1 flex flex-col min-w-0">
|
|
85
124
|
<header className="h-14 bg-gray-50 border-b border-ui-border flex-shrink-0">
|
|
86
125
|
<div className="h-full px-4 md:px-6 flex items-center justify-between">
|
|
@@ -98,14 +137,14 @@ function LayoutContent({ children, sidebarMenus, user, onSignOut, mode = 'expand
|
|
|
98
137
|
) : (
|
|
99
138
|
<div />
|
|
100
139
|
)}
|
|
101
|
-
<UserMenu
|
|
102
|
-
username={user?.profile?.email || user?.profile?.preferred_username}
|
|
140
|
+
<UserMenu
|
|
141
|
+
username={user?.profile?.email || user?.profile?.preferred_username}
|
|
103
142
|
onSignOut={onSignOut || (() => {})}
|
|
104
143
|
menuConfig={userMenuConfig}
|
|
105
144
|
/>
|
|
106
145
|
</div>
|
|
107
146
|
</header>
|
|
108
|
-
|
|
147
|
+
|
|
109
148
|
<main className="flex-1 overflow-auto">
|
|
110
149
|
{children}
|
|
111
150
|
</main>
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
1
|
import { type LucideIcon } from "lucide-react"
|
|
4
2
|
import {
|
|
5
3
|
Shield,
|
|
@@ -7,13 +5,30 @@ import {
|
|
|
7
5
|
Settings,
|
|
8
6
|
Rocket,
|
|
9
7
|
LineChart,
|
|
8
|
+
Library,
|
|
9
|
+
Component,
|
|
10
|
+
FolderGit2,
|
|
11
|
+
Network,
|
|
12
|
+
Key,
|
|
13
|
+
Globe,
|
|
14
|
+
Activity,
|
|
15
|
+
Database,
|
|
16
|
+
Users,
|
|
17
|
+
Plug,
|
|
18
|
+
Box,
|
|
19
|
+
Monitor,
|
|
20
|
+
Server,
|
|
21
|
+
Lock,
|
|
22
|
+
Home,
|
|
23
|
+
FileText,
|
|
24
|
+
Code,
|
|
25
|
+
Cloud,
|
|
10
26
|
} from "lucide-react"
|
|
11
27
|
|
|
12
|
-
// Types pour les menus
|
|
13
28
|
export interface MainMenuItem {
|
|
14
29
|
id: string
|
|
15
30
|
label: string
|
|
16
|
-
icon:
|
|
31
|
+
icon: LucideIcon
|
|
17
32
|
}
|
|
18
33
|
|
|
19
34
|
export interface SubMenuItem {
|
|
@@ -27,42 +42,62 @@ export interface SidebarMenus {
|
|
|
27
42
|
[key: string]: SubMenuItem[]
|
|
28
43
|
}
|
|
29
44
|
|
|
45
|
+
// API response types
|
|
46
|
+
export interface ApiMainMenuItem {
|
|
47
|
+
id: string
|
|
48
|
+
label: string
|
|
49
|
+
icon: string
|
|
50
|
+
}
|
|
30
51
|
|
|
31
|
-
export
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
{
|
|
38
|
-
id: "runtime",
|
|
39
|
-
label: "Runtime",
|
|
40
|
-
icon: <Package className="h-6 w-6" />
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
id: "monitoring",
|
|
44
|
-
label: "Monitoring",
|
|
45
|
-
icon: <LineChart className="h-6 w-6" />
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
id: "api-manager",
|
|
49
|
-
label: "API Manager",
|
|
50
|
-
icon: <Settings className="h-6 w-6" />
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
id: "access",
|
|
54
|
-
label: "Access Manager",
|
|
55
|
-
icon: <Shield className="h-6 w-6" />
|
|
56
|
-
},
|
|
52
|
+
export interface ApiSubMenuItem {
|
|
53
|
+
id: string
|
|
54
|
+
label: string
|
|
55
|
+
href: string
|
|
56
|
+
icon: string
|
|
57
|
+
}
|
|
57
58
|
|
|
58
|
-
|
|
59
|
+
export interface ApiSubMenu {
|
|
60
|
+
id: string
|
|
61
|
+
header: string
|
|
62
|
+
items: ApiSubMenuItem[]
|
|
63
|
+
}
|
|
59
64
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
"runtime": "Runtime",
|
|
64
|
-
"monitoring": "Monitoring",
|
|
65
|
-
"api-manager": "API Manager",
|
|
66
|
-
"access": "Access Manager",
|
|
65
|
+
export interface ApiMenuData {
|
|
66
|
+
main: ApiMainMenuItem[]
|
|
67
|
+
subMenus: Record<string, ApiSubMenu>
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
export interface MenuApiResponse {
|
|
71
|
+
success: boolean
|
|
72
|
+
data: ApiMenuData
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const ICON_MAP: Record<string, LucideIcon> = {
|
|
76
|
+
Shield,
|
|
77
|
+
Package,
|
|
78
|
+
Settings,
|
|
79
|
+
Rocket,
|
|
80
|
+
LineChart,
|
|
81
|
+
Library,
|
|
82
|
+
Component,
|
|
83
|
+
FolderGit2,
|
|
84
|
+
Network,
|
|
85
|
+
Key,
|
|
86
|
+
Globe,
|
|
87
|
+
Activity,
|
|
88
|
+
Database,
|
|
89
|
+
Users,
|
|
90
|
+
Plug,
|
|
91
|
+
Box,
|
|
92
|
+
Monitor,
|
|
93
|
+
Server,
|
|
94
|
+
Lock,
|
|
95
|
+
Home,
|
|
96
|
+
FileText,
|
|
97
|
+
Code,
|
|
98
|
+
Cloud,
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function resolveIcon(name: string): LucideIcon {
|
|
102
|
+
return ICON_MAP[name] || Box
|
|
103
|
+
}
|
|
@@ -3,12 +3,10 @@
|
|
|
3
3
|
import * as React from "react"
|
|
4
4
|
import { cn } from "../../../lib/utils"
|
|
5
5
|
|
|
6
|
-
import { useRouter } from "next/navigation"
|
|
7
6
|
import { Button } from "../../ui/button"
|
|
8
7
|
import { Logo } from "../../ui/logo"
|
|
9
|
-
import {
|
|
8
|
+
import { type MainMenuItem, type SidebarMenus } from "./data"
|
|
10
9
|
import { X, Menu } from "lucide-react"
|
|
11
|
-
import Link from "next/link"
|
|
12
10
|
|
|
13
11
|
export type SidebarMode = 'expanded' | 'minimized' | 'hidden'
|
|
14
12
|
|
|
@@ -21,57 +19,55 @@ interface MainSidebarProps {
|
|
|
21
19
|
mode?: SidebarMode
|
|
22
20
|
onSecondarySidebarOpen?: () => void
|
|
23
21
|
sidebarMenus?: SidebarMenus
|
|
22
|
+
mainMenuItems?: MainMenuItem[]
|
|
24
23
|
}
|
|
25
24
|
|
|
26
|
-
export function MainSidebar({
|
|
25
|
+
export function MainSidebar({
|
|
27
26
|
main_base_url,
|
|
28
|
-
isOpen,
|
|
29
|
-
onToggle,
|
|
30
|
-
onMenuSelect,
|
|
27
|
+
isOpen,
|
|
28
|
+
onToggle,
|
|
29
|
+
onMenuSelect,
|
|
31
30
|
currentMenu,
|
|
32
31
|
mode = 'expanded',
|
|
33
32
|
onSecondarySidebarOpen,
|
|
34
|
-
sidebarMenus = {}
|
|
33
|
+
sidebarMenus = {},
|
|
34
|
+
mainMenuItems = [],
|
|
35
35
|
}: MainSidebarProps) {
|
|
36
36
|
const isMinimized = mode === 'minimized'
|
|
37
|
-
const router = useRouter()
|
|
38
37
|
const [hoveredMenu, setHoveredMenu] = React.useState<string | null>(null)
|
|
39
38
|
|
|
40
39
|
const handleMenuClick = (menuId: string) => {
|
|
41
40
|
onMenuSelect(menuId)
|
|
42
|
-
|
|
41
|
+
|
|
43
42
|
if (!isMinimized && onSecondarySidebarOpen) {
|
|
44
43
|
onSecondarySidebarOpen()
|
|
45
44
|
}
|
|
46
|
-
|
|
45
|
+
|
|
47
46
|
const subMenus = sidebarMenus[menuId]
|
|
48
47
|
if (subMenus && subMenus.length > 0) {
|
|
49
48
|
const targetUrl = subMenus[0].href
|
|
50
|
-
const path = targetUrl.startsWith('http://') || targetUrl.startsWith('https://')
|
|
51
|
-
? new URL(targetUrl).pathname
|
|
49
|
+
const path = targetUrl.startsWith('http://') || targetUrl.startsWith('https://')
|
|
50
|
+
? new URL(targetUrl).pathname
|
|
52
51
|
: targetUrl
|
|
53
52
|
window.location.href = path
|
|
54
53
|
} else {
|
|
55
54
|
const targetUrl = `${main_base_url}/${menuId}`
|
|
56
|
-
const path = targetUrl.startsWith('http://') || targetUrl.startsWith('https://')
|
|
57
|
-
? new URL(targetUrl).pathname
|
|
55
|
+
const path = targetUrl.startsWith('http://') || targetUrl.startsWith('https://')
|
|
56
|
+
? new URL(targetUrl).pathname
|
|
58
57
|
: targetUrl
|
|
59
58
|
window.location.href = path
|
|
60
59
|
}
|
|
61
|
-
|
|
60
|
+
|
|
62
61
|
if (!isMinimized) {
|
|
63
|
-
onToggle()
|
|
62
|
+
onToggle()
|
|
64
63
|
}
|
|
65
64
|
}
|
|
66
65
|
|
|
67
66
|
const handleSubMenuClick = (e: React.MouseEvent, href: string) => {
|
|
68
67
|
e.preventDefault()
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
? new URL(href).pathname
|
|
68
|
+
const path = href.startsWith('http://') || href.startsWith('https://')
|
|
69
|
+
? new URL(href).pathname
|
|
72
70
|
: href
|
|
73
|
-
console.log('handleSubMenuClick - path:', path)
|
|
74
|
-
console.log('Redirecting to:', path)
|
|
75
71
|
window.location.href = path
|
|
76
72
|
setHoveredMenu(null)
|
|
77
73
|
}
|
|
@@ -79,7 +75,7 @@ export function MainSidebar({
|
|
|
79
75
|
return (
|
|
80
76
|
<>
|
|
81
77
|
{isOpen && !isMinimized && (
|
|
82
|
-
<div
|
|
78
|
+
<div
|
|
83
79
|
className="fixed inset-0 bg-black/50 z-40"
|
|
84
80
|
onClick={onToggle}
|
|
85
81
|
/>
|
|
@@ -88,8 +84,8 @@ export function MainSidebar({
|
|
|
88
84
|
<div className={cn(
|
|
89
85
|
"fixed left-0 top-0 h-full bg-white border-r border-ui-border z-50 transform transition-all duration-300 ease-in-out",
|
|
90
86
|
isMinimized ? "w-16" : "w-64 shadow-xl",
|
|
91
|
-
isMinimized
|
|
92
|
-
? "translate-x-0"
|
|
87
|
+
isMinimized
|
|
88
|
+
? "translate-x-0"
|
|
93
89
|
: (isOpen ? "translate-x-0" : "-translate-x-full")
|
|
94
90
|
)}>
|
|
95
91
|
<div className={cn(
|
|
@@ -116,59 +112,58 @@ export function MainSidebar({
|
|
|
116
112
|
isMinimized ? "p-2" : "p-3"
|
|
117
113
|
)}>
|
|
118
114
|
<nav className="space-y-2">
|
|
119
|
-
{
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
onClick={() => handleMenuClick(item.id)}
|
|
128
|
-
className={cn(
|
|
129
|
-
"w-full flex items-center text-left rounded-lg transition-all duration-200 font-medium",
|
|
130
|
-
isMinimized
|
|
131
|
-
? "justify-center p-3"
|
|
132
|
-
: "gap-3 px-3 py-3",
|
|
133
|
-
currentMenu === item.id
|
|
134
|
-
? "bg-interactive/10 text-interactive"
|
|
135
|
-
: "text-text-primary hover:bg-ui-background hover:text-text-primary"
|
|
136
|
-
)}
|
|
137
|
-
title={isMinimized ? item.label : undefined}
|
|
115
|
+
{mainMenuItems.map((item) => {
|
|
116
|
+
const Icon = item.icon
|
|
117
|
+
return (
|
|
118
|
+
<div
|
|
119
|
+
key={item.id}
|
|
120
|
+
className="relative"
|
|
121
|
+
onMouseEnter={() => isMinimized && setHoveredMenu(item.id)}
|
|
122
|
+
onMouseLeave={() => isMinimized && setHoveredMenu(null)}
|
|
138
123
|
>
|
|
139
|
-
<
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
124
|
+
<button
|
|
125
|
+
onClick={() => handleMenuClick(item.id)}
|
|
126
|
+
className={cn(
|
|
127
|
+
"w-full flex items-center text-left rounded-lg transition-all duration-200 font-medium",
|
|
128
|
+
isMinimized
|
|
129
|
+
? "justify-center p-3"
|
|
130
|
+
: "gap-3 px-3 py-3",
|
|
131
|
+
currentMenu === item.id
|
|
132
|
+
? "bg-interactive/10 text-interactive"
|
|
133
|
+
: "text-text-primary hover:bg-ui-background hover:text-text-primary"
|
|
134
|
+
)}
|
|
135
|
+
title={isMinimized ? item.label : undefined}
|
|
136
|
+
>
|
|
137
|
+
<Icon className={cn(
|
|
138
|
+
"h-6 w-6",
|
|
139
|
+
currentMenu === item.id ? "text-interactive" : "text-text-secondary"
|
|
140
|
+
)} />
|
|
141
|
+
{!isMinimized && <span className="text-base">{item.label}</span>}
|
|
142
|
+
</button>
|
|
149
143
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
144
|
+
{isMinimized && hoveredMenu === item.id && sidebarMenus[item.id] && sidebarMenus[item.id].length > 0 && (
|
|
145
|
+
<div className="absolute left-full top-0 ml-2 bg-white border border-ui-border rounded-lg shadow-xl z-50 min-w-[200px] py-2">
|
|
146
|
+
<div className="px-4 py-2 border-b border-ui-border">
|
|
147
|
+
<h3 className="text-sm font-semibold text-text-primary">{item.label}</h3>
|
|
148
|
+
</div>
|
|
149
|
+
<div className="py-1">
|
|
150
|
+
{sidebarMenus[item.id].map((subItem) => (
|
|
151
|
+
<a
|
|
152
|
+
key={subItem.id}
|
|
153
|
+
href={subItem.href}
|
|
154
|
+
onClick={(e) => handleSubMenuClick(e, subItem.href)}
|
|
155
|
+
className="flex items-center gap-3 px-4 py-2 text-sm text-text-secondary hover:bg-ui-background hover:text-text-primary transition-colors no-underline"
|
|
156
|
+
>
|
|
157
|
+
<subItem.icon className="h-4 w-4" />
|
|
158
|
+
{subItem.name}
|
|
159
|
+
</a>
|
|
160
|
+
))}
|
|
161
|
+
</div>
|
|
154
162
|
</div>
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
href={subItem.href}
|
|
160
|
-
onClick={(e) => handleSubMenuClick(e, subItem.href)}
|
|
161
|
-
className="flex items-center gap-3 px-4 py-2 text-sm text-text-secondary hover:bg-ui-background hover:text-text-primary transition-colors no-underline"
|
|
162
|
-
>
|
|
163
|
-
<subItem.icon className="h-4 w-4" />
|
|
164
|
-
{subItem.name}
|
|
165
|
-
</a>
|
|
166
|
-
))}
|
|
167
|
-
</div>
|
|
168
|
-
</div>
|
|
169
|
-
)}
|
|
170
|
-
</div>
|
|
171
|
-
))}
|
|
163
|
+
)}
|
|
164
|
+
</div>
|
|
165
|
+
)
|
|
166
|
+
})}
|
|
172
167
|
</nav>
|
|
173
168
|
</div>
|
|
174
169
|
</div>
|
|
@@ -188,4 +183,4 @@ export function MainSidebarToggle({ onClick }: { onClick: () => void }) {
|
|
|
188
183
|
<Menu className="h-4 w-4" />
|
|
189
184
|
</Button>
|
|
190
185
|
)
|
|
191
|
-
}
|
|
186
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import * as React from "react"
|
|
4
4
|
import { Slot } from "@radix-ui/react-slot"
|
|
5
5
|
import { VariantProps, cva } from "class-variance-authority"
|
|
6
|
-
import {
|
|
6
|
+
import { PanelLeft } from "lucide-react"
|
|
7
7
|
import Link from "next/link"
|
|
8
8
|
import { usePathname } from "next/navigation"
|
|
9
9
|
|
|
@@ -19,17 +19,14 @@ import {
|
|
|
19
19
|
TooltipTrigger,
|
|
20
20
|
} from "../../ui/tooltip"
|
|
21
21
|
import {
|
|
22
|
-
Shield,
|
|
23
22
|
Settings,
|
|
24
|
-
Package,
|
|
25
|
-
Rocket,
|
|
26
23
|
Menu,
|
|
27
24
|
Building2,
|
|
28
25
|
User,
|
|
29
26
|
ChevronUp,
|
|
30
27
|
} from "lucide-react"
|
|
31
28
|
import { Logo } from "../../ui/logo"
|
|
32
|
-
import {
|
|
29
|
+
import { type SidebarMenus, type SubMenuItem } from "./data"
|
|
33
30
|
import { Skeleton } from "../skeleton"
|
|
34
31
|
|
|
35
32
|
const SIDEBAR_COOKIE_NAME = "sidebar:state"
|
|
@@ -174,11 +171,12 @@ interface SidebarProps {
|
|
|
174
171
|
onMainMenuToggle?: () => void
|
|
175
172
|
sidebarMenus?: SidebarMenus
|
|
176
173
|
main_base_url?: string
|
|
174
|
+
sectionLabels?: Record<string, string>
|
|
177
175
|
}
|
|
178
176
|
|
|
179
177
|
|
|
180
178
|
|
|
181
|
-
function Sidebar({ currentMenu, onMainMenuToggle, sidebarMenus = {}, main_base_url = "" }: SidebarProps = {}) {
|
|
179
|
+
function Sidebar({ currentMenu, onMainMenuToggle, sidebarMenus = {}, main_base_url = "", sectionLabels = {} }: SidebarProps = {}) {
|
|
182
180
|
const pathname = usePathname()
|
|
183
181
|
const { state } = useSidebar()
|
|
184
182
|
const [settingsOpen, setSettingsOpen] = React.useState(false)
|
|
@@ -223,18 +221,11 @@ function Sidebar({ currentMenu, onMainMenuToggle, sidebarMenus = {}, main_base_u
|
|
|
223
221
|
</div>
|
|
224
222
|
|
|
225
223
|
{/* En-tête du menu principal sélectionné */}
|
|
226
|
-
{currentMenu &&
|
|
224
|
+
{currentMenu && sectionLabels[currentMenu] && (
|
|
227
225
|
<div className="px-4 py-3 border-b border-ui-border">
|
|
228
|
-
<
|
|
229
|
-
{currentMenu
|
|
230
|
-
|
|
231
|
-
{currentMenu === "api-manager" && <Settings className="h-4 w-4 text-interactive" />}
|
|
232
|
-
{currentMenu === "access-manager" && <Shield className="h-4 w-4 text-interactive" />}
|
|
233
|
-
{currentMenu === "monitoring" && <LineChart className="h-4 w-4 text-interactive" />}
|
|
234
|
-
<h2 className="text-sm font-semibold text-text-primary">
|
|
235
|
-
{menuLabels[currentMenu]}
|
|
236
|
-
</h2>
|
|
237
|
-
</div>
|
|
226
|
+
<h2 className="text-sm font-semibold text-text-primary">
|
|
227
|
+
{sectionLabels[currentMenu]}
|
|
228
|
+
</h2>
|
|
238
229
|
</div>
|
|
239
230
|
)}
|
|
240
231
|
|