@orsetra/shared-ui 1.0.0 → 1.0.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.
Files changed (77) hide show
  1. package/components/layout/index.ts +31 -0
  2. package/components/layout/layout-container.tsx +85 -0
  3. package/components/layout/root-layout-wrapper.tsx +33 -0
  4. package/components/layout/sidebar/data.tsx +88 -0
  5. package/components/layout/sidebar/main-sidebar.tsx +177 -0
  6. package/components/layout/sidebar/sidebar.tsx +750 -0
  7. package/components/layout/skeleton.tsx +15 -0
  8. package/components/ui/accordion.tsx +58 -0
  9. package/components/ui/alert-dialog.tsx +133 -0
  10. package/components/ui/alert.tsx +59 -0
  11. package/components/ui/aspect-ratio.tsx +7 -0
  12. package/components/ui/assets-header.tsx +50 -0
  13. package/components/ui/avatar.tsx +50 -0
  14. package/components/ui/badge.tsx +54 -0
  15. package/components/ui/breadcrumb.tsx +115 -0
  16. package/components/ui/button.tsx +83 -0
  17. package/components/ui/calendar.tsx +66 -0
  18. package/components/ui/card.tsx +79 -0
  19. package/components/ui/carousel.tsx +262 -0
  20. package/components/ui/certificate-editor.tsx +445 -0
  21. package/components/ui/chart.tsx +365 -0
  22. package/components/ui/checkbox.tsx +30 -0
  23. package/components/ui/collapsible.tsx +11 -0
  24. package/components/ui/command.tsx +153 -0
  25. package/components/ui/context-menu.tsx +200 -0
  26. package/components/ui/dialog.tsx +122 -0
  27. package/components/ui/drawer.tsx +118 -0
  28. package/components/ui/dropdown-menu.tsx +200 -0
  29. package/components/ui/environment-settings.tsx +173 -0
  30. package/components/ui/environment-variables-config.tsx +175 -0
  31. package/components/ui/file-import.tsx +177 -0
  32. package/components/ui/form.tsx +178 -0
  33. package/components/ui/hover-card.tsx +29 -0
  34. package/components/ui/index.ts +54 -0
  35. package/components/ui/input-otp.tsx +71 -0
  36. package/components/ui/input.tsx +23 -0
  37. package/components/ui/label.tsx +26 -0
  38. package/components/ui/logo.tsx +17 -0
  39. package/components/ui/menubar.tsx +236 -0
  40. package/components/ui/navigation-menu.tsx +128 -0
  41. package/components/ui/page-header.tsx +35 -0
  42. package/components/ui/pagination.tsx +112 -0
  43. package/components/ui/popover.tsx +31 -0
  44. package/components/ui/process-status.tsx +98 -0
  45. package/components/ui/progress.tsx +31 -0
  46. package/components/ui/radio-group.tsx +44 -0
  47. package/components/ui/resizable.tsx +45 -0
  48. package/components/ui/resource-settings.tsx +227 -0
  49. package/components/ui/scroll-area.tsx +48 -0
  50. package/components/ui/search-input.tsx +26 -0
  51. package/components/ui/secret-explorer.tsx +274 -0
  52. package/components/ui/secret-properties-editor.tsx +642 -0
  53. package/components/ui/select.tsx +162 -0
  54. package/components/ui/selected-asset.tsx +120 -0
  55. package/components/ui/separator.tsx +31 -0
  56. package/components/ui/sheet.tsx +140 -0
  57. package/components/ui/skeleton.tsx +15 -0
  58. package/components/ui/slider.tsx +28 -0
  59. package/components/ui/sonner.tsx +31 -0
  60. package/components/ui/switch.tsx +29 -0
  61. package/components/ui/table.tsx +117 -0
  62. package/components/ui/tabs.tsx +55 -0
  63. package/components/ui/textarea.tsx +22 -0
  64. package/components/ui/toast.tsx +131 -0
  65. package/components/ui/toaster.tsx +35 -0
  66. package/components/ui/toggle-group.tsx +61 -0
  67. package/components/ui/toggle.tsx +45 -0
  68. package/components/ui/tooltip.tsx +30 -0
  69. package/components/ui/user-menu.tsx +86 -0
  70. package/hooks/index.ts +5 -0
  71. package/hooks/use-auth.ts +10 -0
  72. package/hooks/use-mobile.ts +19 -0
  73. package/hooks/use-toast.ts +8 -0
  74. package/hooks/use-websocket.tsx +76 -0
  75. package/index.ts +11 -1
  76. package/lib/menu-utils.ts +48 -0
  77. package/package.json +36 -8
@@ -0,0 +1,31 @@
1
+ // Layout components exports
2
+ export { MainSidebar } from './sidebar/main-sidebar'
3
+ export {
4
+ Sidebar,
5
+ SidebarContent,
6
+ SidebarFooter,
7
+ SidebarGroup,
8
+ SidebarGroupAction,
9
+ SidebarGroupContent,
10
+ SidebarGroupLabel,
11
+ SidebarHeader,
12
+ SidebarInput,
13
+ SidebarInset,
14
+ SidebarMenu,
15
+ SidebarMenuAction,
16
+ SidebarMenuBadge,
17
+ SidebarMenuButton,
18
+ SidebarMenuItem,
19
+ SidebarMenuSkeleton,
20
+ SidebarMenuSub,
21
+ SidebarMenuSubButton,
22
+ SidebarMenuSubItem,
23
+ SidebarProvider,
24
+ SidebarRail,
25
+ SidebarSeparator,
26
+ SidebarTrigger,
27
+ useSidebar
28
+ } from './sidebar/sidebar'
29
+ // Skeleton is exported from ../ui to avoid duplicate exports
30
+ export { RootLayoutWrapper, ibmPlexSans, ibmPlexMono } from './root-layout-wrapper'
31
+ export { LayoutContainer, type SidebarMenus } from './layout-container'
@@ -0,0 +1,85 @@
1
+ "use client"
2
+
3
+ import { useState, useEffect } from "react"
4
+ import { usePathname } from "next/navigation"
5
+ import { MainSidebar, Sidebar, SidebarProvider, useSidebar } from "./index"
6
+ import { UserMenu } from "../ui"
7
+ import { getMenuFromPath } from "../../lib/menu-utils"
8
+
9
+ export interface SidebarMenus {
10
+ [key: string]: any[]
11
+ }
12
+
13
+ interface LayoutContainerProps {
14
+ children: React.ReactNode
15
+ sidebarMenus: SidebarMenus
16
+ user?: { profile?: { email?: string; preferred_username?: string } } | null
17
+ onSignOut?: () => void
18
+ }
19
+
20
+ function LayoutContent({ children, sidebarMenus, user, onSignOut }: LayoutContainerProps) {
21
+ const pathname = usePathname()
22
+ const { setOpen } = useSidebar()
23
+ const [isMainSidebarOpen, setIsMainSidebarOpen] = useState(false)
24
+ const [currentMenu, setCurrentMenu] = useState<string>("overview")
25
+
26
+ useEffect(() => {
27
+ const contextualMenu = getMenuFromPath(pathname)
28
+ setCurrentMenu(contextualMenu)
29
+ }, [pathname])
30
+
31
+ const handleMainSidebarToggle = () => {
32
+ setIsMainSidebarOpen(!isMainSidebarOpen)
33
+ }
34
+
35
+ const handleMenuSelect = (menu: string) => {
36
+ setCurrentMenu(menu)
37
+ }
38
+
39
+ const handleSecondarySidebarOpen = () => {
40
+ setOpen(true)
41
+ }
42
+
43
+ const module = pathname.split('/')[1] || 'overview'
44
+ const sidebarItems = sidebarMenus[module] || []
45
+
46
+ return (
47
+ <div className="flex h-screen w-full bg-white">
48
+ <Sidebar
49
+ currentMenu={currentMenu}
50
+ onMainMenuToggle={handleMainSidebarToggle}
51
+ />
52
+
53
+ <MainSidebar
54
+ isOpen={isMainSidebarOpen}
55
+ onToggle={handleMainSidebarToggle}
56
+ onMenuSelect={handleMenuSelect}
57
+ currentMenu={currentMenu}
58
+ onSecondarySidebarOpen={handleSecondarySidebarOpen}
59
+ />
60
+
61
+ <div className="flex-1 flex flex-col">
62
+ <header className="h-14 bg-white border-b border-ui-border flex-shrink-0">
63
+ <div className="h-full px-6 flex items-center justify-end">
64
+ <UserMenu
65
+ username={user?.profile?.email || user?.profile?.preferred_username}
66
+ onSignOut={onSignOut || (() => {})}
67
+ />
68
+ </div>
69
+ </header>
70
+
71
+ <main className="flex-1 overflow-auto">
72
+ {children}
73
+ </main>
74
+ </div>
75
+ </div>
76
+ )
77
+ }
78
+
79
+ export function LayoutContainer(props: LayoutContainerProps) {
80
+ return (
81
+ <SidebarProvider defaultOpen={true}>
82
+ <LayoutContent {...props} />
83
+ </SidebarProvider>
84
+ )
85
+ }
@@ -0,0 +1,33 @@
1
+ "use client"
2
+
3
+ import { IBM_Plex_Sans, IBM_Plex_Mono } from "next/font/google"
4
+
5
+ const ibmPlexSans = IBM_Plex_Sans({
6
+ subsets: ["latin"],
7
+ weight: ["100", "200", "300", "400", "500", "600", "700"],
8
+ variable: "--font-ibm-plex-sans",
9
+ display: "swap",
10
+ })
11
+
12
+ const ibmPlexMono = IBM_Plex_Mono({
13
+ subsets: ["latin"],
14
+ weight: ["100", "200", "300", "400", "500", "600", "700"],
15
+ variable: "--font-ibm-plex-mono",
16
+ display: "swap",
17
+ })
18
+
19
+ interface RootLayoutWrapperProps {
20
+ children: React.ReactNode
21
+ }
22
+
23
+ export function RootLayoutWrapper({ children }: RootLayoutWrapperProps) {
24
+ return (
25
+ <html lang="en" className={`${ibmPlexSans.variable} ${ibmPlexMono.variable}`} suppressHydrationWarning>
26
+ <body className={`${ibmPlexSans.className} text-base`} suppressHydrationWarning>
27
+ {children}
28
+ </body>
29
+ </html>
30
+ )
31
+ }
32
+
33
+ export { ibmPlexSans, ibmPlexMono }
@@ -0,0 +1,88 @@
1
+ "use client"
2
+
3
+ import {
4
+ Network,
5
+ Server,
6
+ Box,
7
+ Shield,
8
+ CloudCog,
9
+ Globe,
10
+ Database,
11
+ Library,
12
+ Package,
13
+ Users,
14
+ FileText,
15
+ Key,
16
+ Settings,
17
+ Rocket,
18
+ } from "lucide-react"
19
+
20
+ export const menuItems = [
21
+ {
22
+ id: "assets",
23
+ label: "Assets",
24
+ icon: <Rocket className="h-6 w-6" />
25
+ },
26
+ {
27
+ id: "applications",
28
+ label: "Applications",
29
+ icon: <Package className="h-6 w-6" />
30
+ },
31
+ {
32
+ id: "api-manager",
33
+ label: "API Manager",
34
+ icon: <Settings className="h-6 w-6" />
35
+ },
36
+ {
37
+ id: "access-manager",
38
+ label: "Access Manager",
39
+ icon: <Shield className="h-6 w-6" />
40
+ }
41
+ ]
42
+
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
+ ]
67
+ }
68
+
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
+
@@ -0,0 +1,177 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { cn } from "../../../lib/utils"
5
+
6
+ import { useRouter } from "next/navigation"
7
+ import { Button } from "../../ui/button"
8
+ import { Logo } from "../../ui/logo"
9
+ import { menuItems, menuSubItems } from "./data"
10
+ import { X, Menu } from "lucide-react"
11
+ import Link from "next/link"
12
+
13
+ export type SidebarMode = 'expanded' | 'minimized'
14
+
15
+ interface MainSidebarProps {
16
+ isOpen: boolean
17
+ onToggle: () => void
18
+ onMenuSelect: (menu: string) => void
19
+ currentMenu?: string
20
+ mode?: SidebarMode
21
+ onSecondarySidebarOpen?: () => void
22
+ }
23
+
24
+ export function MainSidebar({
25
+ isOpen,
26
+ onToggle,
27
+ onMenuSelect,
28
+ currentMenu,
29
+ mode = 'expanded',
30
+ onSecondarySidebarOpen
31
+ }: MainSidebarProps) {
32
+ const isMinimized = mode === 'minimized'
33
+ const router = useRouter()
34
+ const [hoveredMenu, setHoveredMenu] = React.useState<string | null>(null)
35
+
36
+ const handleMenuClick = (menuId: string) => {
37
+ onMenuSelect(menuId)
38
+
39
+ // En mode minimized, ne pas ouvrir le sidebar secondaire
40
+ if (!isMinimized && onSecondarySidebarOpen) {
41
+ onSecondarySidebarOpen()
42
+ }
43
+
44
+ // Rediriger vers le premier sous-menu
45
+ const subMenus = menuSubItems[menuId as keyof typeof menuSubItems]
46
+ if (subMenus && subMenus.length > 0) {
47
+ router.push(subMenus[0].href)
48
+ }
49
+
50
+ if (!isMinimized) {
51
+ onToggle() // Fermer le menu principal après sélection (seulement en mode expanded)
52
+ }
53
+ }
54
+
55
+ const handleSubMenuClick = (href: string) => {
56
+ router.push(href)
57
+ setHoveredMenu(null)
58
+ }
59
+
60
+ return (
61
+ <>
62
+ {/* Overlay - seulement en mode expanded */}
63
+ {isOpen && !isMinimized && (
64
+ <div
65
+ className="fixed inset-0 bg-black/50 z-40"
66
+ onClick={onToggle}
67
+ />
68
+ )}
69
+
70
+ {/* Main Sidebar */}
71
+ <div className={cn(
72
+ "fixed left-0 top-0 h-full bg-white border-r border-ui-border z-50 transform transition-all duration-300 ease-in-out",
73
+ isMinimized ? "w-16" : "w-64 shadow-xl",
74
+ isMinimized
75
+ ? "translate-x-0"
76
+ : (isOpen ? "translate-x-0" : "-translate-x-full")
77
+ )}>
78
+ {/* Header avec logo Orchestra */}
79
+ <div className={cn(
80
+ "flex items-center h-16 border-b border-ui-border bg-white",
81
+ isMinimized ? "justify-center px-2" : "justify-between px-4"
82
+ )}>
83
+ {!isMinimized && <Logo className="text-2xl font-semibold text-text-primary" />}
84
+ {isMinimized ? (
85
+ <Logo className="text-xl font-semibold text-text-primary" iconOnly />
86
+ ) : (
87
+ <Button
88
+ variant="ghost"
89
+ size="sm"
90
+ onClick={onToggle}
91
+ className="h-8 w-8 p-0 hover:bg-ui-background text-text-secondary"
92
+ >
93
+ <X className="h-4 w-4" />
94
+ </Button>
95
+ )}
96
+ </div>
97
+
98
+ {/* Menu Items */}
99
+ <div className={cn(
100
+ "overflow-y-auto h-full",
101
+ isMinimized ? "p-2" : "p-3"
102
+ )}>
103
+ <nav className="space-y-2">
104
+ {menuItems.map((item) => (
105
+ <div
106
+ key={item.id}
107
+ className="relative"
108
+ onMouseEnter={() => isMinimized && setHoveredMenu(item.id)}
109
+ onMouseLeave={() => isMinimized && setHoveredMenu(null)}
110
+ >
111
+ <button
112
+ onClick={() => handleMenuClick(item.id)}
113
+ className={cn(
114
+ "w-full flex items-center text-left rounded-lg transition-all duration-200 font-medium",
115
+ isMinimized
116
+ ? "justify-center p-3"
117
+ : "gap-3 px-3 py-3",
118
+ currentMenu === item.id
119
+ ? "bg-interactive/10 text-interactive"
120
+ : "text-text-primary hover:bg-ui-background hover:text-text-primary"
121
+ )}
122
+ title={isMinimized ? item.label : undefined}
123
+ >
124
+ <div className={cn(
125
+ "flex items-center justify-center",
126
+ currentMenu === item.id
127
+ ? "text-interactive"
128
+ : "text-text-secondary"
129
+ )}>
130
+ {item.icon}
131
+ </div>
132
+ {!isMinimized && <span className="text-base">{item.label}</span>}
133
+ </button>
134
+
135
+ {/* Submenu au survol en mode minimized */}
136
+ {isMinimized && hoveredMenu === item.id && menuSubItems[item.id as keyof typeof menuSubItems] && (
137
+ <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
+ <div className="px-4 py-2 border-b border-ui-border">
139
+ <h3 className="text-sm font-semibold text-text-primary">{item.label}</h3>
140
+ </div>
141
+ <div className="py-1">
142
+ {menuSubItems[item.id as keyof typeof menuSubItems].map((subItem) => (
143
+ <Link
144
+ key={subItem.id}
145
+ href={subItem.href}
146
+ onClick={() => handleSubMenuClick(subItem.href)}
147
+ 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"
148
+ >
149
+ <subItem.icon className="h-4 w-4" />
150
+ {subItem.name}
151
+ </Link>
152
+ ))}
153
+ </div>
154
+ </div>
155
+ )}
156
+ </div>
157
+ ))}
158
+ </nav>
159
+ </div>
160
+ </div>
161
+ </>
162
+ )
163
+ }
164
+
165
+ export function MainSidebarToggle({ onClick }: { onClick: () => void }) {
166
+ return (
167
+ <Button
168
+ variant="ghost"
169
+ size="sm"
170
+ onClick={onClick}
171
+ className="h-8 w-8 p-0 hover:bg-ui-background text-text-secondary"
172
+ title="Ouvrir le menu principal"
173
+ >
174
+ <Menu className="h-4 w-4" />
175
+ </Button>
176
+ )
177
+ }