@@ -5,11 +5,12 @@ import { SiteHeader } from "@/components/site-header"
55import { SidebarProvider } from "@/components/ui/sidebar"
66import { ObjectDataTable } from './components/ObjectDataTable' ;
77import { ObjectDataForm } from './components/ObjectDataForm' ;
8+ import { PackageManager } from './components/PackageManager' ;
89import { Toaster } from "@/components/ui/toaster"
910import { Card , CardContent , CardHeader , CardTitle , CardDescription } from "@/components/ui/card"
1011import { Database , Layers , Sparkles , Zap } from 'lucide-react' ;
1112import { getApiBaseUrl , config } from './lib/config' ;
12- import { appPackages , type AppPackage } from './mocks/browser' ;
13+ import type { AppPackage } from './mocks/browser' ;
1314
1415function DashboardWelcome ( ) {
1516 return (
@@ -119,13 +120,15 @@ function DashboardWelcome() {
119120
120121export default function App ( ) {
121122 const [ client , setClient ] = useState < ObjectStackClient | null > ( null ) ;
123+ const [ apps , setApps ] = useState < AppPackage [ ] > ( [ ] ) ;
124+ const [ selectedApp , setSelectedApp ] = useState < AppPackage | null > ( null ) ;
122125 const [ selectedObject , setSelectedObject ] = useState < string | null > ( null ) ;
126+ const [ selectedView , setSelectedView ] = useState < 'dashboard' | 'packages' | 'object' > ( 'dashboard' ) ;
123127 const [ editingRecord , setEditingRecord ] = useState < any > ( null ) ;
124128 const [ showForm , setShowForm ] = useState ( false ) ;
125- const [ selectedApp , setSelectedApp ] = useState < AppPackage > ( appPackages [ 0 ] ) ;
126129
130+ // 1. Create client
127131 useEffect ( ( ) => {
128- // Use the configured API base URL based on runtime mode (MSW or Server)
129132 const baseUrl = getApiBaseUrl ( ) ;
130133 console . log ( `[App] Connecting to API: ${ baseUrl } (mode: ${ config . mode } )` ) ;
131134
@@ -135,6 +138,32 @@ export default function App() {
135138 setClient ( newClient ) ;
136139 } , [ ] ) ;
137140
141+ // 2. Fetch app list from the server API (not hardcoded)
142+ useEffect ( ( ) => {
143+ if ( ! client ) return ;
144+ let mounted = true ;
145+
146+ async function loadApps ( ) {
147+ try {
148+ // Spec: GET /api/v1/meta/apps → GetMetaItemsResponse = { type: 'apps', items: AppSchema[] }
149+ const result : any = await client ! . meta . getItems ( 'apps' ) ;
150+ const items : AppPackage [ ] = result ?. items || result ?. value || ( Array . isArray ( result ) ? result : [ ] ) ;
151+
152+ console . log ( '[App] Fetched apps from API:' , items . map ( ( a : any ) => a . label || a . name ) ) ;
153+
154+ if ( mounted && items . length > 0 ) {
155+ setApps ( items ) ;
156+ setSelectedApp ( items [ 0 ] ) ;
157+ }
158+ } catch ( err ) {
159+ console . error ( '[App] Failed to fetch apps from API:' , err ) ;
160+ }
161+ }
162+
163+ loadApps ( ) ;
164+ return ( ) => { mounted = false ; } ;
165+ } , [ client ] ) ;
166+
138167 function handleEdit ( record : any ) {
139168 setEditingRecord ( record ) ;
140169 setShowForm ( true ) ;
@@ -157,6 +186,24 @@ export default function App() {
157186 function handleSelectApp ( app : AppPackage ) {
158187 setSelectedApp ( app ) ;
159188 setSelectedObject ( null ) ;
189+ setSelectedView ( 'dashboard' ) ;
190+ setShowForm ( false ) ;
191+ setEditingRecord ( null ) ;
192+ }
193+
194+ function handleSelectObject ( name : string ) {
195+ if ( name ) {
196+ setSelectedObject ( name ) ;
197+ setSelectedView ( 'object' ) ;
198+ } else {
199+ setSelectedObject ( null ) ;
200+ setSelectedView ( 'dashboard' ) ;
201+ }
202+ }
203+
204+ function handleSelectView ( view : 'dashboard' | 'packages' ) {
205+ setSelectedView ( view ) ;
206+ setSelectedObject ( null ) ;
160207 setShowForm ( false ) ;
161208 setEditingRecord ( null ) ;
162209 }
@@ -166,15 +213,17 @@ export default function App() {
166213 < AppSidebar
167214 client = { client }
168215 selectedObject = { selectedObject }
169- onSelectObject = { ( name ) => setSelectedObject ( name || null ) }
170- apps = { appPackages }
216+ onSelectObject = { handleSelectObject }
217+ apps = { apps }
171218 selectedApp = { selectedApp }
172219 onSelectApp = { handleSelectApp }
220+ onSelectView = { handleSelectView }
221+ selectedView = { selectedView }
173222 />
174223 < main className = "flex min-w-0 flex-1 flex-col bg-background" >
175- < SiteHeader selectedObject = { selectedObject } appLabel = { selectedApp ?. label } />
224+ < SiteHeader selectedObject = { selectedObject } appLabel = { selectedApp ?. label || selectedApp ?. name } />
176225 < div className = "flex flex-1 flex-col overflow-hidden" >
177- { selectedObject ? (
226+ { selectedView === 'object' && selectedObject ? (
178227 < div className = "flex flex-1 flex-col gap-4 p-4" >
179228 { client && (
180229 < ObjectDataTable
@@ -184,6 +233,8 @@ export default function App() {
184233 />
185234 ) }
186235 </ div >
236+ ) : selectedView === 'packages' ? (
237+ client && < PackageManager client = { client } />
187238 ) : (
188239 < DashboardWelcome />
189240 ) }
0 commit comments