@orsetra/shared-ui 1.9.1 → 1.9.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.
@@ -4,7 +4,7 @@ import { useState, useEffect } from "react"
4
4
  import { usePathname, useSearchParams, useParams } 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, type MainMenuItem, resolveIcon } from "./sidebar/data"
7
+ import { type SidebarMenus, type MainMenuItem, type SidebarMenuMeta, resolveIcon } from "./sidebar/data"
8
8
  import { Button } from "../ui"
9
9
  import { Logo } from "../ui/logo"
10
10
  import { getMenuFromPath } from "../../lib/menu-utils"
@@ -50,6 +50,7 @@ function LayoutContent({
50
50
  const [mainMenuItems, setMainMenuItems] = useState<MainMenuItem[]>([])
51
51
  const [fetchedSidebarMenus, setFetchedSidebarMenus] = useState<SidebarMenus>({})
52
52
  const [sectionLabels, setSectionLabels] = useState<Record<string, string>>({})
53
+ const [sidebarMenuMeta, setSidebarMenuMeta] = useState<Record<string, SidebarMenuMeta>>({})
53
54
 
54
55
  useEffect(() => {
55
56
  if (!fetchMenus) return
@@ -69,6 +70,7 @@ function LayoutContent({
69
70
 
70
71
  const menus: SidebarMenus = {}
71
72
  const labels: Record<string, string> = {}
73
+ const meta: Record<string, SidebarMenuMeta> = {}
72
74
  Object.entries(subMenus).forEach(([key, subMenu]) => {
73
75
  menus[key] = subMenu.items.map((item) => ({
74
76
  id: item.id,
@@ -78,9 +80,11 @@ function LayoutContent({
78
80
  icon: resolveIcon(item.icon),
79
81
  }))
80
82
  labels[key] = subMenu.header
83
+ meta[key] = { header: subMenu.header, description: subMenu.description }
81
84
  })
82
85
  setFetchedSidebarMenus(menus)
83
86
  setSectionLabels(labels)
87
+ setSidebarMenuMeta(meta)
84
88
  })
85
89
  // eslint-disable-next-line react-hooks/exhaustive-deps
86
90
  }, [])
@@ -148,6 +152,7 @@ function LayoutContent({
148
152
  mode={isHidden ? "expanded" : mode}
149
153
  sidebarMenus={effectiveSidebarMenus}
150
154
  mainMenuItems={mainMenuItems}
155
+ sidebarMenuMeta={sidebarMenuMeta}
151
156
  />
152
157
 
153
158
  <main className="flex-1 bg-ibm-gray-10 min-h-0 overflow-auto">
@@ -88,9 +88,15 @@ export interface ApiSubMenuItem {
88
88
  }
89
89
 
90
90
  export interface ApiSubMenu {
91
- id: string
92
- header: string
93
- items: ApiSubMenuItem[]
91
+ id: string
92
+ header: string
93
+ description?: string
94
+ items: ApiSubMenuItem[]
95
+ }
96
+
97
+ export interface SidebarMenuMeta {
98
+ header?: string
99
+ description?: string
94
100
  }
95
101
 
96
102
  export interface ApiMenuData {
@@ -13,14 +13,14 @@ export interface FlyoutDefaultLink {
13
13
  }
14
14
 
15
15
  interface MainSidebarFlyoutProps {
16
- item: MainMenuItem | null
17
- subItems: SubMenuItem[]
18
- onMouseEnter: () => void
19
- onMouseLeave: () => void
20
- main_base_url?: string
21
- defaultTitle?: string
22
- defaultDescription?: string
23
- defaultLinks?: FlyoutDefaultLink[]
16
+ item: MainMenuItem | null
17
+ subItems: SubMenuItem[]
18
+ onMouseEnter: () => void
19
+ onMouseLeave: () => void
20
+ main_base_url?: string
21
+ defaultTitle?: string
22
+ defaultDescription?: string
23
+ defaultLinks?: FlyoutDefaultLink[]
24
24
  }
25
25
 
26
26
  export function MainSidebarFlyout({
@@ -92,35 +92,26 @@ export function MainSidebarFlyout({
92
92
  </nav>
93
93
  </>
94
94
  ) : (
95
- /* ── Default state: welcome + CTA + doc links ───────────────────── */
96
- <div className="flex flex-col h-full">
97
- {/* Header */}
98
- <div className="px-5 pt-6 pb-5 border-b border-ui-border flex-shrink-0">
99
- <div className="flex items-center gap-2.5 mb-3">
100
- <div className="h-8 w-8 rounded-md bg-interactive flex items-center justify-center flex-shrink-0">
101
- <Home className="h-4 w-4 text-white" />
102
- </div>
103
- <span className="text-sm font-semibold text-text-primary truncate">{defaultTitle}</span>
104
- </div>
105
-
95
+ /* ── Default state: centré verticalement ────────────────────────── */
96
+ <div className="flex flex-col h-full items-center justify-center px-6 gap-6">
97
+ {/* Titre + texte + CTA */}
98
+ <div className="flex flex-col items-center gap-3 w-full">
99
+ <span className="text-sm font-semibold text-text-primary text-center">{defaultTitle}</span>
106
100
  {defaultDescription && (
107
- <p className="text-xs text-text-secondary leading-relaxed mb-4">
108
- {defaultDescription}
109
- </p>
101
+ <p className="text-xs text-text-secondary text-center leading-relaxed">{defaultDescription}</p>
110
102
  )}
111
-
112
103
  <a
113
104
  href={homePath}
114
- className="flex items-center justify-center gap-2 w-full px-3 py-2 bg-interactive hover:bg-interactive-hover text-white text-sm font-medium transition-colors"
105
+ className="flex items-center justify-center gap-2 w-full px-3 py-2 border border-ui-border text-text-secondary hover:text-text-primary hover:bg-ui-background text-sm font-medium transition-colors"
115
106
  >
116
107
  <Home className="h-4 w-4 flex-shrink-0" />
117
108
  Tableau de bord
118
109
  </a>
119
110
  </div>
120
111
 
121
- {/* Doc / resource links */}
112
+ {/* Liens ressources */}
122
113
  {defaultLinks.length > 0 && (
123
- <nav className="flex-1 overflow-y-auto px-2 lg:px-3 py-3 space-y-0.5">
114
+ <nav className="w-full space-y-0.5">
124
115
  <p className="px-2 pb-1 text-[10px] font-semibold text-text-secondary uppercase tracking-widest">
125
116
  Ressources
126
117
  </p>
@@ -132,7 +123,7 @@ export function MainSidebarFlyout({
132
123
  key={i}
133
124
  href={link.href}
134
125
  {...(isExternal ? { target: "_blank", rel: "noopener noreferrer" } : {})}
135
- className="flex items-start gap-x-3 px-2 lg:px-3 py-2 border-l-4 border-transparent hover:bg-ui-background hover:border-interactive transition-colors"
126
+ className="flex items-start gap-x-3 px-2 py-2 border-l-4 border-transparent hover:bg-ui-background hover:border-interactive transition-colors"
136
127
  >
137
128
  <LinkIcon className="h-4 w-4 flex-shrink-0 mt-0.5 text-text-secondary" />
138
129
  <div className="min-w-0 flex-1">
@@ -4,7 +4,9 @@ import * as React from "react"
4
4
  import { cn } from "../../../lib/utils"
5
5
  import { Button } from "../../ui/button"
6
6
  import { Logo } from "../../ui/logo"
7
- import { type MainMenuItem, type SidebarMenus } from "./data"
7
+ import { type MainMenuItem, type SidebarMenus, type SidebarMenuMeta } from "./data"
8
+
9
+ export type { SidebarMenuMeta }
8
10
  import { X, Menu, ChevronDown, ChevronRight } from "lucide-react"
9
11
  import { useIsMobile } from "../../../hooks/use-mobile"
10
12
  import { MainSidebarFlyout, type FlyoutDefaultLink } from "./main-sidebar-flyout"
@@ -23,9 +25,8 @@ interface MainSidebarProps {
23
25
  onSecondarySidebarOpen?: () => void
24
26
  sidebarMenus?: SidebarMenus
25
27
  mainMenuItems?: MainMenuItem[]
26
- flyoutDefaultTitle?: string
27
- flyoutDefaultDescription?: string
28
- flyoutDefaultLinks?: FlyoutDefaultLink[]
28
+ /** Per-submenu metadata (header + description) derived from the API menu response. */
29
+ sidebarMenuMeta?: Record<string, SidebarMenuMeta>
29
30
  }
30
31
 
31
32
  export function MainSidebar({
@@ -38,9 +39,7 @@ export function MainSidebar({
38
39
  onSecondarySidebarOpen,
39
40
  sidebarMenus = {},
40
41
  mainMenuItems = [],
41
- flyoutDefaultTitle,
42
- flyoutDefaultDescription,
43
- flyoutDefaultLinks = [],
42
+ sidebarMenuMeta = {},
44
43
  }: MainSidebarProps) {
45
44
  const isMinimized = mode === 'minimized'
46
45
  const isMobile = useIsMobile()
@@ -153,6 +152,17 @@ export function MainSidebar({
153
152
  }
154
153
  }
155
154
 
155
+ // Default flyout content derived from the "main" submenu in the loaded menu data
156
+ const mainMeta = sidebarMenuMeta["main"] ?? {}
157
+ const defaultTitle = mainMeta.header ?? "Camel X Platform"
158
+ const defaultDesc = mainMeta.description ?? undefined
159
+ const defaultLinks: FlyoutDefaultLink[] = (sidebarMenus["main"] ?? []).map(sub => ({
160
+ label: sub.name,
161
+ href: sub.href,
162
+ description: sub.description,
163
+ icon: sub.icon,
164
+ }))
165
+
156
166
  const handleSubMenuClick = (e: React.MouseEvent, menuId: string, href: string) => {
157
167
  e.preventDefault()
158
168
  window.location.href = buildSubItemHref(menuId, href)
@@ -302,9 +312,9 @@ export function MainSidebar({
302
312
  onMouseEnter={cancelCloseTimer}
303
313
  onMouseLeave={startCloseTimer}
304
314
  main_base_url={main_base_url}
305
- defaultTitle={flyoutDefaultTitle}
306
- defaultDescription={flyoutDefaultDescription}
307
- defaultLinks={flyoutDefaultLinks}
315
+ defaultTitle={defaultTitle}
316
+ defaultDescription={defaultDesc}
317
+ defaultLinks={defaultLinks}
308
318
  />
309
319
  )}
310
320
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orsetra/shared-ui",
3
- "version": "1.9.1",
3
+ "version": "1.9.2",
4
4
  "description": "Shared UI components for Orsetra platform",
5
5
  "main": "./index.ts",
6
6
  "types": "./index.ts",