1- import { useState , useEffect , useRef } from 'react' ;
1+ import { Routes , Route , Navigate } from 'react-router-dom ' ;
22import AppList from './pages/AppList' ;
33import Login from './pages/Login' ;
4- import Dashboard from './pages/Dashboard' ;
4+ import AppDashboard from './pages/AppDashboard' ; // New App Home
55import Settings from './pages/Settings' ;
66import Organization from './pages/Organization' ;
77import { AuthProvider , useAuth } from './context/AuthContext' ;
8- import { AppSidebar } from './components/app-sidebar' ;
9- import { SidebarProvider , SidebarInset , SidebarTrigger , Separator , Breadcrumb , BreadcrumbItem , BreadcrumbList , BreadcrumbPage , Avatar , AvatarFallback , AvatarImage , DropdownMenu , DropdownMenuTrigger , DropdownMenuContent , DropdownMenuLabel , DropdownMenuSeparator , DropdownMenuGroup , DropdownMenuItem } from '@objectos/ui' ;
10- import { LogOut , Settings as SettingsIcon , Building , Bell } from 'lucide-react' ;
8+ import { MainLayout } from './layouts/MainLayout' ;
9+ import { WorkspaceLayout } from './layouts/WorkspaceLayout' ;
10+ import { ObjectListRoute } from './pages/objects/ObjectListRoute' ;
11+ import { ObjectDetailRoute } from './pages/objects/ObjectDetailRoute' ;
12+ import * as paths from './routes' ;
1113
1214function AppContent ( ) {
13- const { user, loading, signOut } = useAuth ( ) ;
14- const [ currentPath , setCurrentPath ] = useState ( window . location . pathname ) ;
15-
16- const [ currentAppMetadata , setCurrentAppMetadata ] = useState < any > ( null ) ;
17- const lastFetchedApp = useRef < string | null > ( null ) ;
18-
19- // Fetch App Metadata when entering an app
20- useEffect ( ( ) => {
21- const parts = currentPath . split ( '/' ) ;
22- if ( parts [ 1 ] === 'app' && parts [ 2 ] ) {
23- const appName = parts [ 2 ] ;
24-
25- // Avoid re-fetching if we already have this app loaded
26- if ( lastFetchedApp . current === appName ) {
27- return ;
28- }
29-
30- lastFetchedApp . current = appName ;
31-
32- fetch ( `/api/metadata/app/${ appName } ` )
33- . then ( res => {
34- if ( ! res . ok ) throw new Error ( 'App not found' ) ;
35- return res . json ( ) ;
36- } )
37- . then ( data => {
38- setCurrentAppMetadata ( data ) ;
39- // Ensure ref matches confirmed loaded data ID/Name if needed, but keeping it simple
40- } )
41- . catch ( err => {
42- console . error ( err ) ;
43- setCurrentAppMetadata ( null ) ;
44- // Reset ref on error so we can try again if user refreshes or navs away and back
45- lastFetchedApp . current = null ;
46- } ) ;
47- } else {
48- if ( lastFetchedApp . current ) {
49- setCurrentAppMetadata ( null ) ;
50- lastFetchedApp . current = null ;
51- }
52- }
53- } , [ currentPath ] ) ; // Remove currentAppMetadata from dependency
54-
55- useEffect ( ( ) => {
56- const handlePopState = ( ) => setCurrentPath ( window . location . pathname ) ;
57- window . addEventListener ( 'popstate' , handlePopState ) ;
58-
59- // Custom event for navigation
60- const handlePushState = ( ) => setCurrentPath ( window . location . pathname ) ;
61- window . addEventListener ( 'pushstate' , handlePushState ) ;
62-
63- return ( ) => {
64- window . removeEventListener ( 'popstate' , handlePopState ) ;
65- window . removeEventListener ( 'pushstate' , handlePushState ) ;
66- } ;
67- } , [ ] ) ;
15+ const { user, loading } = useAuth ( ) ;
6816
6917 if ( loading ) {
7018 return (
@@ -76,116 +24,43 @@ function AppContent() {
7624
7725 // Auth Routing
7826 if ( ! user ) {
79- if ( currentPath !== '/login' ) {
80- window . history . pushState ( { } , '' , '/login' ) ;
81- return < Login /> ;
82- }
83- return < Login /> ;
84- }
85-
86- if ( currentPath === '/login' ) {
87- window . history . pushState ( { } , '' , '/' ) ;
88- setCurrentPath ( '/' ) ;
89- }
90-
91- // Main Layout
92- if ( currentPath === '/' || currentPath === '/apps' ) {
9327 return (
94- < div className = "flex flex-col min-h-screen w-full bg-background" >
95- < header className = "flex h-16 shrink-0 items-center gap-4 border-b px-6 bg-card sticky top-0 z-50" >
96- < div className = "flex items-center gap-2 font-bold text-lg" >
97- < span > ObjectOS</ span >
98- </ div >
99- < div className = "ml-auto flex items-center gap-2" >
100- { /* User Menu */ }
101- < DropdownMenu >
102- < DropdownMenuTrigger asChild >
103- < button className = "flex items-center gap-2 outline-none" >
104- < Avatar className = "h-8 w-8 rounded-lg cursor-pointer hover:opacity-80 transition-opacity" >
105- < AvatarImage src = { user ?. image } alt = { user ?. name } />
106- < AvatarFallback className = "rounded-lg" > CN</ AvatarFallback >
107- </ Avatar >
108- </ button >
109- </ DropdownMenuTrigger >
110- < DropdownMenuContent className = "w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg" side = "bottom" align = "end" sideOffset = { 4 } >
111- < DropdownMenuLabel className = "p-0 font-normal" >
112- < div className = "flex items-center gap-2 px-1 py-1.5 text-left text-sm" >
113- < Avatar className = "h-8 w-8 rounded-lg" >
114- < AvatarImage src = { user ?. image } alt = { user ?. name } />
115- < AvatarFallback className = "rounded-lg" > CN</ AvatarFallback >
116- </ Avatar >
117- < div className = "grid flex-1 text-left text-sm leading-tight" >
118- < span className = "truncate font-semibold" > { user ?. name } </ span >
119- < span className = "truncate text-xs" > { user ?. email } </ span >
120- </ div >
121- </ div >
122- </ DropdownMenuLabel >
123- < DropdownMenuSeparator />
124- < DropdownMenuGroup >
125- < DropdownMenuItem >
126- < Building className = "mr-2 h-4 w-4" />
127- Organization
128- </ DropdownMenuItem >
129- < DropdownMenuItem >
130- < SettingsIcon className = "mr-2 h-4 w-4" />
131- Settings
132- </ DropdownMenuItem >
133- < DropdownMenuItem >
134- < Bell className = "mr-2 h-4 w-4" />
135- Notifications
136- </ DropdownMenuItem >
137- </ DropdownMenuGroup >
138- < DropdownMenuSeparator />
139- < DropdownMenuItem onClick = { signOut } >
140- < LogOut className = "mr-2 h-4 w-4" />
141- Log out
142- </ DropdownMenuItem >
143- </ DropdownMenuContent >
144- </ DropdownMenu >
145- </ div >
146- </ header >
147- < div className = "flex flex-1 flex-col gap-4 p-8 overflow-y-auto" >
148- < div className = "max-w-7xl mx-auto w-full" >
149- < h1 className = "text-2xl font-bold mb-6" > Apps</ h1 >
150- < AppList />
151- </ div >
152- </ div >
153- </ div >
28+ < Routes >
29+ < Route path = { paths . LOGIN } element = { < Login /> } />
30+ < Route path = "*" element = { < Navigate to = { paths . LOGIN } replace /> } />
31+ </ Routes >
15432 ) ;
15533 }
15634
15735 return (
158- < SidebarProvider >
159- < AppSidebar objects = { { } } appMetadata = { currentAppMetadata } />
160- < SidebarInset >
161- < header className = "flex h-12 shrink-0 items-center gap-2 border-b px-4 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12" >
162- < SidebarTrigger className = "-ml-1" />
163- < Separator orientation = "vertical" className = "mr-2 h-4" />
164- < Breadcrumb >
165- < BreadcrumbList >
166- < BreadcrumbItem >
167- < BreadcrumbPage >
168- { ( ( ) => {
169- if ( currentPath === '/settings' ) return 'Settings' ;
170- if ( currentPath === '/organization' ) return 'Organization' ;
171-
172- const parts = currentPath . split ( '/' ) ;
173- if ( parts [ 1 ] === 'app' ) {
174- return `App: ${ parts [ 2 ] } ` ;
175- }
176- return 'Dashboard' ;
177- } ) ( ) }
178- </ BreadcrumbPage >
179- </ BreadcrumbItem >
180- </ BreadcrumbList >
181- </ Breadcrumb >
182- </ header >
183- < div className = "flex flex-1 flex-col gap-4 p-4" >
184- { currentPath === '/settings' ? < Settings /> :
185- currentPath === '/organization' ? < Organization /> : < Dashboard /> }
186- </ div >
187- </ SidebarInset >
188- </ SidebarProvider >
36+ < Routes >
37+ < Route path = { paths . LOGIN } element = { < Navigate to = "/" replace /> } />
38+
39+ { /* Main App Selection Layout */ }
40+ < Route element = { < MainLayout /> } >
41+ < Route path = { paths . ROOT } element = { < AppList /> } />
42+ < Route path = { paths . APPS } element = { < AppList /> } />
43+ </ Route >
44+
45+ { /* Workspace/Dashboard Layout */ }
46+ < Route element = { < WorkspaceLayout /> } >
47+ { /* The App Home Dashboard showing menu shortcuts */ }
48+ < Route path = { paths . APP_ROOT } element = { < AppDashboard /> } />
49+
50+ { /* Object Routes */ }
51+ < Route path = { paths . APP_OBJECT_LIST } element = { < ObjectListRoute /> } />
52+ < Route path = { paths . APP_OBJECT_DETAIL } element = { < ObjectDetailRoute /> } />
53+ { /* Legacy/Compat routes support */ }
54+ < Route path = "/app/:appName/object/:objectName/:recordId" element = { < ObjectDetailRoute /> } />
55+
56+ { /* Global/Standard Routes */ }
57+ < Route path = { paths . SETTINGS } element = { < Settings /> } />
58+ < Route path = { paths . ORGANIZATION } element = { < Organization /> } />
59+ </ Route >
60+
61+ { /* Fallback */ }
62+ < Route path = "*" element = { < Navigate to = "/" replace /> } />
63+ </ Routes >
18964 ) ;
19065}
19166
0 commit comments