@@ -7,9 +7,13 @@ import {
77 LogOut ,
88 Sparkles ,
99 Search ,
10+ Check ,
11+ Briefcase ,
12+ CheckSquare ,
1013} from "lucide-react"
1114import { useState , useEffect } from "react"
1215import { ObjectStackClient } from '@objectstack/client' ;
16+ import type { AppPackage } from "@/mocks/browser" ;
1317
1418import {
1519 Sidebar ,
@@ -38,13 +42,21 @@ import {
3842 DropdownMenuTrigger ,
3943} from "@/components/ui/dropdown-menu"
4044
45+ const APP_ICONS : Record < string , React . ElementType > = {
46+ 'check-square' : CheckSquare ,
47+ 'briefcase' : Briefcase ,
48+ } ;
49+
4150interface AppSidebarProps extends React . ComponentProps < typeof Sidebar > {
4251 client : ObjectStackClient | null ;
4352 selectedObject : string | null ;
4453 onSelectObject : ( name : string ) => void ;
54+ apps : AppPackage [ ] ;
55+ selectedApp : AppPackage | null ;
56+ onSelectApp : ( app : AppPackage ) => void ;
4557}
4658
47- export function AppSidebar ( { client, selectedObject, onSelectObject, ...props } : AppSidebarProps ) {
59+ export function AppSidebar ( { client, selectedObject, onSelectObject, apps , selectedApp , onSelectApp , ...props } : AppSidebarProps ) {
4860 const [ objects , setObjects ] = useState < any [ ] > ( [ ] ) ;
4961 const [ loading , setLoading ] = useState ( false ) ;
5062 const [ searchQuery , setSearchQuery ] = useState ( "" ) ;
@@ -73,24 +85,72 @@ export function AppSidebar({ client, selectedObject, onSelectObject, ...props }:
7385 loadObjects ( ) ;
7486 } , [ client ] ) ;
7587
76- const filteredObjects = objects . filter ( obj =>
88+ // Filter objects by selected app's registered objects
89+ const appObjectNames = selectedApp
90+ ? ( selectedApp . config . objects || [ ] ) . map ( ( o : any ) => o . name )
91+ : [ ] ;
92+
93+ const appObjects = selectedApp
94+ ? objects . filter ( obj => appObjectNames . includes ( obj . name ) )
95+ : objects ;
96+
97+ const filteredObjects = appObjects . filter ( obj =>
7798 ! searchQuery ||
7899 obj . label ?. toLowerCase ( ) . includes ( searchQuery . toLowerCase ( ) ) ||
79100 obj . name ?. toLowerCase ( ) . includes ( searchQuery . toLowerCase ( ) )
80101 ) ;
81102
103+ const AppIcon = selectedApp ?. icon ? ( APP_ICONS [ selectedApp . icon ] || Sparkles ) : Sparkles ;
104+
82105 return (
83106 < Sidebar { ...props } >
84107 < SidebarHeader className = "border-b" >
85- < div className = "flex items-center gap-2 px-2 py-1.5" >
86- < div className = "flex h-8 w-8 items-center justify-center rounded-lg bg-primary text-primary-foreground" >
87- < Sparkles className = "h-4 w-4" />
88- </ div >
89- < div className = "flex flex-col gap-0.5 leading-none" >
90- < span className = "font-semibold text-sm" > ObjectStack</ span >
91- < span className = "text-xs text-muted-foreground" > Platform Console</ span >
92- </ div >
93- </ div >
108+ { /* App Switcher */ }
109+ < DropdownMenu >
110+ < DropdownMenuTrigger asChild >
111+ < button className = "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left hover:bg-sidebar-accent transition-colors" >
112+ < div className = "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-primary text-primary-foreground" >
113+ < AppIcon className = "h-4 w-4" />
114+ </ div >
115+ < div className = "flex flex-1 flex-col gap-0.5 leading-none overflow-hidden" >
116+ < span className = "truncate font-semibold text-sm" >
117+ { selectedApp ? selectedApp . label : 'ObjectStack' }
118+ </ span >
119+ < span className = "truncate text-xs text-muted-foreground" >
120+ { selectedApp ? selectedApp . description : 'Select an app' }
121+ </ span >
122+ </ div >
123+ < ChevronsUpDown className = "ml-auto h-4 w-4 shrink-0 text-muted-foreground" />
124+ </ button >
125+ </ DropdownMenuTrigger >
126+ < DropdownMenuContent className = "w-[--radix-dropdown-menu-trigger-width] min-w-64" align = "start" sideOffset = { 4 } >
127+ < DropdownMenuLabel > Applications</ DropdownMenuLabel >
128+ < DropdownMenuSeparator />
129+ { apps . map ( ( app ) => {
130+ const Icon = app . icon ? ( APP_ICONS [ app . icon ] || Package ) : Package ;
131+ return (
132+ < DropdownMenuItem
133+ key = { app . id }
134+ onClick = { ( ) => onSelectApp ( app ) }
135+ className = "gap-2 py-2"
136+ >
137+ < div className = "flex h-6 w-6 shrink-0 items-center justify-center rounded bg-primary/10 text-primary" >
138+ < Icon className = "h-3.5 w-3.5" />
139+ </ div >
140+ < div className = "flex flex-1 flex-col leading-tight" >
141+ < span className = "text-sm font-medium" > { app . label } </ span >
142+ { app . description && (
143+ < span className = "text-xs text-muted-foreground" > { app . description } </ span >
144+ ) }
145+ </ div >
146+ { selectedApp ?. id === app . id && (
147+ < Check className = "h-4 w-4 text-primary" />
148+ ) }
149+ </ DropdownMenuItem >
150+ ) ;
151+ } ) }
152+ </ DropdownMenuContent >
153+ </ DropdownMenu >
94154 </ SidebarHeader >
95155
96156 < SidebarContent >
0 commit comments