@orsetra/shared-ui 1.0.13 → 1.0.15

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,5 +1,6 @@
1
1
  // Layout components exports
2
2
  export { MainSidebar, type SidebarMode } from './sidebar/main-sidebar'
3
+ export { menuItems, menuLabels, type MainMenuItem, type SubMenuItem, type SidebarMenus } from './sidebar/data'
3
4
  export {
4
5
  Sidebar,
5
6
  SidebarContent,
@@ -28,4 +29,4 @@ export {
28
29
  } from './sidebar/sidebar'
29
30
  // Skeleton is exported from ../ui to avoid duplicate exports
30
31
  export { RootLayoutWrapper, ibmPlexSans, ibmPlexMono } from './root-layout-wrapper'
31
- export { LayoutContainer, type SidebarMenus } from './layout-container'
32
+ export { LayoutContainer } from './layout-container'
@@ -2,16 +2,14 @@
2
2
 
3
3
  import { useState, useEffect } from "react"
4
4
  import { usePathname } from "next/navigation"
5
- import { MainSidebar, Sidebar, SidebarProvider, useSidebar, type SidebarMode } from "./index"
5
+ import { MainSidebar, type SidebarMode } from "./sidebar/main-sidebar"
6
+ import { Sidebar, SidebarProvider, useSidebar } from "./sidebar/sidebar"
7
+ import { type SidebarMenus } from "./sidebar/data"
6
8
  import { UserMenu, Button } from "../ui"
7
9
  import { getMenuFromPath } from "../../lib/menu-utils"
8
10
  import { useIsMobile } from "../../hooks/use-mobile"
9
11
  import { Menu } from "lucide-react"
10
12
 
11
- export interface SidebarMenus {
12
- [key: string]: any[]
13
- }
14
-
15
13
  interface LayoutContainerProps {
16
14
  children: React.ReactNode
17
15
  sidebarMenus: SidebarMenus
@@ -64,6 +62,7 @@ function LayoutContent({ children, sidebarMenus, user, onSignOut, mode = 'expand
64
62
  <Sidebar
65
63
  currentMenu={currentMenu}
66
64
  onMainMenuToggle={handleMainSidebarToggle}
65
+ sidebarMenus={sidebarMenus}
67
66
  />
68
67
  )}
69
68
 
@@ -75,6 +74,7 @@ function LayoutContent({ children, sidebarMenus, user, onSignOut, mode = 'expand
75
74
  currentMenu={currentMenu}
76
75
  onSecondarySidebarOpen={handleSecondarySidebarOpen}
77
76
  mode={isHidden ? "expanded" : mode}
77
+ sidebarMenus={sidebarMenus}
78
78
  />
79
79
 
80
80
  <div className="flex-1 flex flex-col min-w-0">
@@ -1,23 +1,33 @@
1
1
  "use client"
2
2
 
3
+ import { type LucideIcon } from "lucide-react"
3
4
  import {
4
- Network,
5
- Server,
6
- Box,
7
5
  Shield,
8
- CloudCog,
9
- Globe,
10
- Database,
11
- Library,
12
6
  Package,
13
- Users,
14
- FileText,
15
- Key,
16
7
  Settings,
17
8
  Rocket,
18
9
  } from "lucide-react"
19
10
 
20
- export const menuItems = [
11
+ // Types pour les menus
12
+ export interface MainMenuItem {
13
+ id: string
14
+ label: string
15
+ icon: React.ReactNode
16
+ }
17
+
18
+ export interface SubMenuItem {
19
+ id: string
20
+ name: string
21
+ href: string
22
+ icon: LucideIcon
23
+ }
24
+
25
+ export interface SidebarMenus {
26
+ [key: string]: SubMenuItem[]
27
+ }
28
+
29
+ // Menu principal (centralisé) - Structure commune à toutes les apps
30
+ export const menuItems: MainMenuItem[] = [
21
31
  {
22
32
  id: "assets",
23
33
  label: "Assets",
@@ -40,49 +50,11 @@ export const menuItems = [
40
50
  }
41
51
  ]
42
52
 
43
- // Mapping des menus principaux vers leurs sous-menus
44
- export const menuSubItems = {
45
- "assets": [
46
- { id: "library", name: "Bibliothèque", href: "/assets", icon: Library },
47
- { id: "projects", name: "Projets", href: "/projects", icon: Rocket },
48
- ],
49
- "applications": [
50
- { id: "applications", name: "Applications", href: "/applications", icon: Package },
51
- { id: "vpc", name: "Réseaux", href: "/vpcs", icon: Network },
52
- { id: "secrets", name: "Secrets", href: "/secrets", icon: Key },
53
- { id: "environments", name: "Environnements", href: "/environments", icon: Globe }
54
- ],
55
- "api-manager": [
56
- { id: "apis", name: "APIs", href: "/api-manager/apis", icon: Database },
57
- { id: "gateway", name: "API Gateway", href: "/api-manager/gateway", icon: Globe }
58
- ],
59
- "access-manager": [
60
- { id: "users", name: "Utilisateurs", href: "/access-manager/users", icon: Users },
61
- { id: "groups", name: "Groupes", href: "/access-manager/groups", icon: Users },
62
- { id: "roles", name: "Rôles IAM", href: "/access-manager/roles", icon: Shield },
63
- { id: "policies", name: "Politiques", href: "/access-manager/policies", icon: FileText },
64
- { id: "resource-servers", name: "Serveurs OAuth", href: "/access-manager/resource-servers", icon: Server },
65
- { id: "identity-providers", name: "Fournisseurs d'identité", href: "/access-manager/identity-providers", icon: Key }
66
- ]
53
+ // Labels des menus principaux pour l'affichage
54
+ export const menuLabels: Record<string, string> = {
55
+ "assets": "Assets",
56
+ "applications": "Applications",
57
+ "api-manager": "API Manager",
58
+ "access-manager": "Access Manager"
67
59
  }
68
60
 
69
- // Menu par défaut quand aucun menu principal n'est sélectionné
70
- export const defaultNavigation = [
71
-
72
- {
73
- name: "Applications",
74
- href: "/applications",
75
- icon: Box,
76
- },
77
- {
78
- name: "API Manager",
79
- href: "/api-manager",
80
- icon: CloudCog,
81
- },
82
- {
83
- name: "Assets",
84
- href: "/assets",
85
- icon: Server,
86
- },
87
- ]
88
-
@@ -6,7 +6,7 @@ import { cn } from "../../../lib/utils"
6
6
  import { useRouter } from "next/navigation"
7
7
  import { Button } from "../../ui/button"
8
8
  import { Logo } from "../../ui/logo"
9
- import { menuItems, menuSubItems } from "./data"
9
+ import { menuItems, type SidebarMenus } from "./data"
10
10
  import { X, Menu } from "lucide-react"
11
11
  import Link from "next/link"
12
12
 
@@ -19,6 +19,7 @@ interface MainSidebarProps {
19
19
  currentMenu?: string
20
20
  mode?: SidebarMode
21
21
  onSecondarySidebarOpen?: () => void
22
+ sidebarMenus?: SidebarMenus
22
23
  }
23
24
 
24
25
  export function MainSidebar({
@@ -27,7 +28,8 @@ export function MainSidebar({
27
28
  onMenuSelect,
28
29
  currentMenu,
29
30
  mode = 'expanded',
30
- onSecondarySidebarOpen
31
+ onSecondarySidebarOpen,
32
+ sidebarMenus = {}
31
33
  }: MainSidebarProps) {
32
34
  const isMinimized = mode === 'minimized'
33
35
  const router = useRouter()
@@ -41,8 +43,8 @@ export function MainSidebar({
41
43
  onSecondarySidebarOpen()
42
44
  }
43
45
 
44
- // Rediriger vers le premier sous-menu
45
- const subMenus = menuSubItems[menuId as keyof typeof menuSubItems]
46
+ // Rediriger vers le premier sous-menu (utilise les menus fournis par l'app)
47
+ const subMenus = sidebarMenus[menuId]
46
48
  if (subMenus && subMenus.length > 0) {
47
49
  router.push(subMenus[0].href)
48
50
  }
@@ -133,13 +135,13 @@ export function MainSidebar({
133
135
  </button>
134
136
 
135
137
  {/* Submenu au survol en mode minimized */}
136
- {isMinimized && hoveredMenu === item.id && menuSubItems[item.id as keyof typeof menuSubItems] && (
138
+ {isMinimized && hoveredMenu === item.id && sidebarMenus[item.id] && sidebarMenus[item.id].length > 0 && (
137
139
  <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">
138
140
  <div className="px-4 py-2 border-b border-ui-border">
139
141
  <h3 className="text-sm font-semibold text-text-primary">{item.label}</h3>
140
142
  </div>
141
143
  <div className="py-1">
142
- {menuSubItems[item.id as keyof typeof menuSubItems].map((subItem) => (
144
+ {sidebarMenus[item.id].map((subItem) => (
143
145
  <Link
144
146
  key={subItem.id}
145
147
  href={subItem.href}
@@ -26,7 +26,7 @@ import {
26
26
  Menu
27
27
  } from "lucide-react"
28
28
  import { Logo } from "../../ui/logo"
29
- import { defaultNavigation, menuSubItems } from "./data"
29
+ import { menuLabels, type SidebarMenus, type SubMenuItem } from "./data"
30
30
  import { Skeleton } from "../skeleton"
31
31
 
32
32
  const SIDEBAR_COOKIE_NAME = "sidebar:state"
@@ -169,18 +169,19 @@ SidebarProvider.displayName = "SidebarProvider"
169
169
  interface SidebarProps {
170
170
  currentMenu?: string
171
171
  onMainMenuToggle?: () => void
172
+ sidebarMenus?: SidebarMenus
172
173
  }
173
174
 
174
175
 
175
176
 
176
- function Sidebar({ currentMenu, onMainMenuToggle }: SidebarProps = {}) {
177
+ function Sidebar({ currentMenu, onMainMenuToggle, sidebarMenus = {} }: SidebarProps = {}) {
177
178
  const pathname = usePathname()
178
179
  const { state } = useSidebar()
179
180
 
180
- // Déterminer quelle navigation afficher
181
- const currentNavigation = currentMenu && menuSubItems[currentMenu as keyof typeof menuSubItems]
182
- ? menuSubItems[currentMenu as keyof typeof menuSubItems]
183
- : defaultNavigation
181
+ // Déterminer quelle navigation afficher à partir des menus fournis par l'app
182
+ const currentNavigation: SubMenuItem[] = currentMenu && sidebarMenus[currentMenu]
183
+ ? sidebarMenus[currentMenu]
184
+ : []
184
185
 
185
186
  return (
186
187
  <div className="h-screen sticky top-0 flex flex-col bg-white border-r border-ui-border min-w-[var(--sidebar-width-icon)] transition-[width] duration-200"
@@ -202,20 +203,15 @@ function Sidebar({ currentMenu, onMainMenuToggle }: SidebarProps = {}) {
202
203
  </div>
203
204
 
204
205
  {/* En-tête du menu principal sélectionné */}
205
- {currentMenu && (
206
+ {currentMenu && menuLabels[currentMenu] && (
206
207
  <div className="px-4 py-3 border-b border-ui-border">
207
208
  <div className="flex items-center gap-2">
208
209
  {currentMenu === "assets" && <Rocket className="h-4 w-4 text-interactive" />}
209
- {currentMenu === "projects" && <Rocket className="h-4 w-4 text-interactive" />}
210
210
  {currentMenu === "applications" && <Package className="h-4 w-4 text-interactive" />}
211
211
  {currentMenu === "api-manager" && <Settings className="h-4 w-4 text-interactive" />}
212
212
  {currentMenu === "access-manager" && <Shield className="h-4 w-4 text-interactive" />}
213
213
  <h2 className="text-sm font-semibold text-text-primary">
214
- {currentMenu === "assets" && "Assets"}
215
- {currentMenu === "projects" && "Projects"}
216
- {currentMenu === "applications" && "Applications"}
217
- {currentMenu === "api-manager" && "API Manager"}
218
- {currentMenu === "access-manager" && "Access Manager"}
214
+ {menuLabels[currentMenu]}
219
215
  </h2>
220
216
  </div>
221
217
  </div>
@@ -7,11 +7,12 @@ import { Button } from "./button"
7
7
 
8
8
  interface EditableTitleProps {
9
9
  value: string
10
- onSave: (newValue: string) => void
10
+ onSave?: (newValue: string) => void
11
11
  className?: string
12
+ editable?: boolean
12
13
  }
13
14
 
14
- export function EditableTitle({ value, onSave, className = "" }: EditableTitleProps) {
15
+ export function EditableTitle({ value, onSave, className = "", editable = true }: EditableTitleProps) {
15
16
  const [isEditing, setIsEditing] = useState(false)
16
17
  const [isHovered, setIsHovered] = useState(false)
17
18
  const [editValue, setEditValue] = useState(value)
@@ -25,7 +26,7 @@ export function EditableTitle({ value, onSave, className = "" }: EditableTitlePr
25
26
  }, [isEditing])
26
27
 
27
28
  const handleSave = () => {
28
- if (editValue.trim() && editValue !== value) {
29
+ if (editValue.trim() && editValue !== value && onSave) {
29
30
  onSave(editValue.trim())
30
31
  }
31
32
  setIsEditing(false)
@@ -56,7 +57,7 @@ export function EditableTitle({ value, onSave, className = "" }: EditableTitlePr
56
57
  />
57
58
  <Button
58
59
  size="sm"
59
- variant="default"
60
+ variant="primary"
60
61
  onClick={handleSave}
61
62
  className="h-8 w-8 p-0"
62
63
  >
@@ -74,6 +75,14 @@ export function EditableTitle({ value, onSave, className = "" }: EditableTitlePr
74
75
  )
75
76
  }
76
77
 
78
+ if (!editable) {
79
+ return (
80
+ <div className={`flex items-center gap-2 ${className}`}>
81
+ <h2 className="text-base font-semibold text-text-primary">{value}</h2>
82
+ </div>
83
+ )
84
+ }
85
+
77
86
  return (
78
87
  <div
79
88
  className={`flex items-center gap-2 group cursor-pointer ${className}`}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orsetra/shared-ui",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "Shared UI components for Orsetra platform",
5
5
  "main": "./index.ts",
6
6
  "types": "./index.ts",
@@ -35,30 +35,12 @@
35
35
  "tailwind"
36
36
  ],
37
37
  "peerDependencies": {
38
+ "next": "^14.0.0 || ^15.0.0",
38
39
  "react": "^18.0.0 || ^19.0.0",
39
- "react-dom": "^18.0.0 || ^19.0.0",
40
- "next": "^14.0.0 || ^15.0.0"
40
+ "react-dom": "^18.0.0 || ^19.0.0"
41
41
  },
42
42
  "dependencies": {
43
- "react-avatar": "^5.0.3",
44
- "react-easy-crop": "^5.0.8",
45
- "react-hook-form": "^7.54.0",
46
- "react-resizable-panels": "^2.1.7",
47
43
  "@hookform/resolvers": "^3.9.1",
48
- "zod": "^3.24.1",
49
- "clsx": "^2.1.1",
50
- "tailwind-merge": "^2.5.5",
51
- "class-variance-authority": "^0.7.1",
52
- "lucide-react": "^0.454.0",
53
- "react-day-picker": "8.10.1",
54
- "embla-carousel-react": "8.5.1",
55
- "cmdk": "1.0.4",
56
- "recharts": "^2.15.0",
57
- "date-fns": "4.1.0",
58
- "input-otp": "1.4.1",
59
- "vaul": "^1.1.1",
60
- "next-themes": "^0.4.4",
61
- "sonner": "^1.7.1",
62
44
  "@radix-ui/react-accordion": "1.2.2",
63
45
  "@radix-ui/react-alert-dialog": "1.1.4",
64
46
  "@radix-ui/react-aspect-ratio": "1.1.1",
@@ -85,11 +67,29 @@
85
67
  "@radix-ui/react-toast": "1.2.4",
86
68
  "@radix-ui/react-toggle": "1.1.1",
87
69
  "@radix-ui/react-toggle-group": "1.1.1",
88
- "@radix-ui/react-tooltip": "1.1.6"
70
+ "@radix-ui/react-tooltip": "1.1.6",
71
+ "class-variance-authority": "^0.7.1",
72
+ "clsx": "^2.1.1",
73
+ "cmdk": "1.0.4",
74
+ "date-fns": "4.1.0",
75
+ "embla-carousel-react": "8.5.1",
76
+ "input-otp": "1.4.1",
77
+ "lucide-react": "^0.454.0",
78
+ "next-themes": "^0.4.4",
79
+ "react-avatar": "^5.0.3",
80
+ "react-day-picker": "8.10.1",
81
+ "react-easy-crop": "^5.0.8",
82
+ "react-hook-form": "^7.54.0",
83
+ "react-resizable-panels": "^2.1.7",
84
+ "recharts": "^2.15.0",
85
+ "sonner": "^1.7.1",
86
+ "tailwind-merge": "^2.5.5",
87
+ "vaul": "^1.1.1",
88
+ "zod": "^3.24.1"
89
89
  },
90
90
  "devDependencies": {
91
91
  "@types/react": "^19",
92
- "next": "^15.0.0",
92
+ "next": "^16.0.7",
93
93
  "typescript": "^5"
94
94
  }
95
- }
95
+ }