@orsetra/shared-ui 1.0.19 → 1.0.22

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.
@@ -5,7 +5,7 @@ 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
7
  import { type SidebarMenus } from "./sidebar/data"
8
- import { UserMenu, Button } from "../ui"
8
+ import { UserMenu, Button, type UserMenuConfig } from "../ui"
9
9
  import { getMenuFromPath } from "../../lib/menu-utils"
10
10
  import { useIsMobile } from "../../hooks/use-mobile"
11
11
  import { Menu } from "lucide-react"
@@ -16,9 +16,10 @@ interface LayoutContainerProps {
16
16
  user?: { profile?: { email?: string; preferred_username?: string } } | null
17
17
  onSignOut?: () => void
18
18
  mode?: SidebarMode
19
+ userMenuConfig?: UserMenuConfig
19
20
  }
20
21
 
21
- function LayoutContent({ children, sidebarMenus, user, onSignOut, mode = 'expanded' }: LayoutContainerProps) {
22
+ function LayoutContent({ children, sidebarMenus, user, onSignOut, mode = 'expanded', userMenuConfig }: LayoutContainerProps) {
22
23
  const pathname = usePathname()
23
24
  const { setOpen } = useSidebar()
24
25
  const isMobile = useIsMobile()
@@ -96,7 +97,8 @@ function LayoutContent({ children, sidebarMenus, user, onSignOut, mode = 'expand
96
97
  )}
97
98
  <UserMenu
98
99
  username={user?.profile?.email || user?.profile?.preferred_username}
99
- onSignOut={onSignOut || (() => {})}
100
+ onSignOut={onSignOut || (() => {})}
101
+ menuConfig={userMenuConfig}
100
102
  />
101
103
  </div>
102
104
  </header>
@@ -13,7 +13,7 @@ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElemen
13
13
  }
14
14
 
15
15
  const base =
16
- 'inline-flex items-center justify-center font-semibold rounded-md transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-ibm-blue-60 focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none select-none'
16
+ 'inline-flex items-center justify-center font-semibold rounded-none transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-ibm-blue-60 focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none select-none'
17
17
 
18
18
  export const variants: Record<ButtonVariant, string> = {
19
19
  primary:
@@ -5,7 +5,7 @@ export { Avatar, AvatarImage, AvatarFallback } from './avatar'
5
5
  export { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator } from './dropdown-menu'
6
6
  export { Separator } from './separator'
7
7
  export { Logo } from './logo'
8
- export { UserMenu } from './user-menu'
8
+ export { UserMenu, type UserMenuItem, type UserMenuConfig } from './user-menu'
9
9
  export { Input } from './input'
10
10
  export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from './tooltip'
11
11
  export { PageHeader } from './page-header'
@@ -1,7 +1,7 @@
1
1
  "use client"
2
2
 
3
3
  import { useRouter } from "next/navigation"
4
- import { LogOut, Settings, Building2, User, ChevronDown } from "lucide-react"
4
+ import { LogOut, ChevronDown, type LucideIcon } from "lucide-react"
5
5
  import Avatar from "react-avatar"
6
6
  import { Button } from "./button"
7
7
  import {
@@ -12,15 +12,30 @@ import {
12
12
  DropdownMenuSeparator,
13
13
  DropdownMenuTrigger,
14
14
  } from "./dropdown-menu"
15
+
16
+ // Type for configurable user menu items
17
+ export interface UserMenuItem {
18
+ id: string
19
+ label: string
20
+ href: string
21
+ icon: LucideIcon
22
+ }
23
+
24
+ export interface UserMenuConfig {
25
+ items: UserMenuItem[]
26
+ }
27
+
15
28
  interface UserMenuProps {
16
29
  username?: string
17
30
  onSignOut: () => void
31
+ menuConfig?: UserMenuConfig
18
32
  }
19
33
 
20
- export function UserMenu({ username, onSignOut }: UserMenuProps) {
34
+ export function UserMenu({ username, onSignOut, menuConfig }: UserMenuProps) {
21
35
  const router = useRouter()
22
36
 
23
37
  const displayName = username || "User"
38
+ const menuItems = menuConfig?.items || []
24
39
 
25
40
  return (
26
41
  <DropdownMenu>
@@ -50,28 +65,24 @@ export function UserMenu({ username, onSignOut }: UserMenuProps) {
50
65
  <p className="text-sm font-medium leading-none">{displayName}</p>
51
66
  </div>
52
67
  </DropdownMenuLabel>
53
- <DropdownMenuSeparator />
54
- <DropdownMenuItem
55
- className="cursor-pointer"
56
- onClick={() => router.push('/profile')}
57
- >
58
- <User className="mr-2 h-4 w-4" />
59
- <span>Profile</span>
60
- </DropdownMenuItem>
61
- <DropdownMenuItem
62
- className="cursor-pointer"
63
- onClick={() => router.push('/organization')}
64
- >
65
- <Building2 className="mr-2 h-4 w-4" />
66
- <span>Organization</span>
67
- </DropdownMenuItem>
68
- <DropdownMenuItem
69
- className="cursor-pointer"
70
- onClick={() => router.push('/settings')}
71
- >
72
- <Settings className="mr-2 h-4 w-4" />
73
- <span>Settings</span>
74
- </DropdownMenuItem>
68
+ {menuItems.length > 0 && (
69
+ <>
70
+ <DropdownMenuSeparator />
71
+ {menuItems.map((item) => {
72
+ const Icon = item.icon
73
+ return (
74
+ <DropdownMenuItem
75
+ key={item.id}
76
+ className="cursor-pointer"
77
+ onClick={() => router.push(item.href)}
78
+ >
79
+ <Icon className="mr-2 h-4 w-4" />
80
+ <span>{item.label}</span>
81
+ </DropdownMenuItem>
82
+ )
83
+ })}
84
+ </>
85
+ )}
75
86
  <DropdownMenuSeparator />
76
87
  <DropdownMenuItem
77
88
  className="cursor-pointer text-red-600 focus:text-red-600 focus:bg-red-50"
@@ -1,14 +1,13 @@
1
- import HttpClient from './http-client';
1
+ import HttpClient, { type AuthHeadersProvider } from './http-client';
2
2
 
3
3
  /**
4
4
  * Classe de base pour tous les services qui utilisent HttpClient
5
- * L'organization ID est automatiquement ajouté aux headers via les custom attributes AWS Amplify
6
5
  */
7
6
  export abstract class BaseService {
8
7
  protected httpClient: HttpClient;
9
8
 
10
- constructor(baseUrl: string) {
11
- this.httpClient = HttpClient.getInstance(baseUrl);
9
+ constructor(baseUrl: string, authHeadersProvider?: AuthHeadersProvider) {
10
+ this.httpClient = HttpClient.getInstance(baseUrl, authHeadersProvider);
12
11
  }
13
12
 
14
13
  protected handleServiceError(error: any): Error {
@@ -16,3 +15,5 @@ export abstract class BaseService {
16
15
  return error
17
16
  }
18
17
  }
18
+
19
+ export type { AuthHeadersProvider };
@@ -1,14 +1,18 @@
1
1
  // Simplified HTTP client without AWS Amplify dependencies
2
2
 
3
+ export type AuthHeadersProvider = () => Promise<Record<string, string>> | Record<string, string>;
4
+
3
5
  class HttpClient {
4
6
  private baseUrl: string;
7
+ private authHeadersProvider?: AuthHeadersProvider;
5
8
 
6
- private constructor(baseUrl: string) {
7
- this.baseUrl = baseUrl || '';
9
+ private constructor(baseUrl: string, authHeadersProvider?: AuthHeadersProvider) {
10
+ this.baseUrl = baseUrl || '';
11
+ this.authHeadersProvider = authHeadersProvider;
8
12
  }
9
13
 
10
- public static getInstance(baseUrl: string): HttpClient {
11
- return new HttpClient(baseUrl);
14
+ public static getInstance(baseUrl: string, authHeadersProvider?: AuthHeadersProvider): HttpClient {
15
+ return new HttpClient(baseUrl, authHeadersProvider);
12
16
  }
13
17
 
14
18
  private getFullUrl(path: string): string {
@@ -20,15 +24,15 @@ class HttpClient {
20
24
  const headers = new Headers();
21
25
  headers.set('Content-Type', 'application/json');
22
26
 
23
- try {
24
- // TODO: Add authentication token and account context if needed
25
- // For now, just return basic headers
26
- } catch (error) {
27
- console.warn('Failed to get auth headers:', error);
28
- }
29
-
30
- if (!headers.has('Content-Type')) {
31
- headers.set('Content-Type', 'application/json');
27
+ if (this.authHeadersProvider) {
28
+ try {
29
+ const authHeaders = await this.authHeadersProvider();
30
+ Object.entries(authHeaders).forEach(([key, value]) => {
31
+ headers.set(key, value);
32
+ });
33
+ } catch (error) {
34
+ console.warn('Failed to get auth headers:', error);
35
+ }
32
36
  }
33
37
 
34
38
  return headers;
@@ -138,8 +142,8 @@ class HttpClient {
138
142
  }
139
143
  }
140
144
 
141
- export function useHttpClient(baseUrl: string) {
142
- return HttpClient.getInstance(baseUrl);
145
+ export function useHttpClient(baseUrl: string, authHeadersProvider?: AuthHeadersProvider) {
146
+ return HttpClient.getInstance(baseUrl, authHeadersProvider);
143
147
  }
144
148
 
145
149
  export default HttpClient;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orsetra/shared-ui",
3
- "version": "1.0.19",
3
+ "version": "1.0.22",
4
4
  "description": "Shared UI components for Orsetra platform",
5
5
  "main": "./index.ts",
6
6
  "types": "./index.ts",
@@ -92,4 +92,4 @@
92
92
  "next": "^16.0.7",
93
93
  "typescript": "^5"
94
94
  }
95
- }
95
+ }