Merge pull request #43 from johndoe6345789/codex/refactor-app-components-into-separate-files

Refactor app layout components and centralize shortcut strings
This commit is contained in:
2026-01-18 00:34:37 +00:00
committed by GitHub
13 changed files with 692 additions and 825 deletions

View File

@@ -0,0 +1,45 @@
import { useEffect, useState } from 'react'
import { BrowserRouter } from 'react-router-dom'
import AppLayout from '@/components/app/AppLayout'
import LoadingScreen from '@/components/app/LoadingScreen'
import { useComponentTreeLoader } from '@/hooks/use-component-tree-loader'
import { useSeedData } from '@/hooks/data/use-seed-data'
import { preloadCriticalComponents } from '@/lib/component-registry'
export default function AppBootstrap() {
const { loadSeedData } = useSeedData()
const { loadComponentTrees } = useComponentTreeLoader()
const [appReady, setAppReady] = useState(false)
useEffect(() => {
const timer = setTimeout(() => {
setAppReady(true)
}, 100)
loadSeedData()
.then(() => loadComponentTrees())
.catch(err => {
console.error('[APP_ROUTER] ❌ Seed data loading failed:', err)
})
.finally(() => {
clearTimeout(timer)
setAppReady(true)
preloadCriticalComponents()
})
return () => {
clearTimeout(timer)
}
}, [loadSeedData, loadComponentTrees])
if (!appReady) {
return <LoadingScreen />
}
return (
<BrowserRouter>
<AppLayout />
</BrowserRouter>
)
}

View File

@@ -0,0 +1,89 @@
import { Suspense } from 'react'
import { DialogRegistry, PWARegistry } from '@/lib/component-registry'
import type {
ComponentNode,
ComponentTree,
Lambda,
PlaywrightTest,
PrismaModel,
ProjectFile,
StorybookStory,
UnitTest,
Workflow,
} from '@/types/project'
const { GlobalSearch, KeyboardShortcutsDialog, PreviewDialog } = DialogRegistry
const { PWAInstallPrompt } = PWARegistry
interface AppDialogsProps {
searchOpen: boolean
onSearchOpenChange: (open: boolean) => void
shortcutsOpen: boolean
onShortcutsOpenChange: (open: boolean) => void
previewOpen: boolean
onPreviewOpenChange: (open: boolean) => void
files: ProjectFile[]
models: PrismaModel[]
components: ComponentNode[]
componentTrees: ComponentTree[]
workflows: Workflow[]
lambdas: Lambda[]
playwrightTests: PlaywrightTest[]
storybookStories: StorybookStory[]
unitTests: UnitTest[]
onNavigate: (page: string) => void
onFileSelect: (fileId: string) => void
}
export default function AppDialogs({
searchOpen,
onSearchOpenChange,
shortcutsOpen,
onShortcutsOpenChange,
previewOpen,
onPreviewOpenChange,
files,
models,
components,
componentTrees,
workflows,
lambdas,
playwrightTests,
storybookStories,
unitTests,
onNavigate,
onFileSelect,
}: AppDialogsProps) {
return (
<>
<Suspense fallback={null}>
<GlobalSearch
open={searchOpen}
onOpenChange={onSearchOpenChange}
files={files}
models={models}
components={components}
componentTrees={componentTrees}
workflows={workflows}
lambdas={lambdas}
playwrightTests={playwrightTests}
storybookStories={storybookStories}
unitTests={unitTests}
onNavigate={onNavigate}
onFileSelect={onFileSelect}
/>
</Suspense>
<Suspense fallback={null}>
<KeyboardShortcutsDialog open={shortcutsOpen} onOpenChange={onShortcutsOpenChange} />
</Suspense>
<Suspense fallback={null}>
<PreviewDialog open={previewOpen} onOpenChange={onPreviewOpenChange} />
</Suspense>
<Suspense fallback={null}>
<PWAInstallPrompt />
</Suspense>
</>
)
}

View File

@@ -0,0 +1,92 @@
import { useState } from 'react'
import { toast } from 'sonner'
import AppDialogs from '@/components/app/AppDialogs'
import AppMainPanel from '@/components/app/AppMainPanel'
import { NavigationMenu } from '@/components/organisms/NavigationMenu'
import { SidebarInset, SidebarProvider } from '@/components/ui/sidebar'
import appStrings from '@/data/app-shortcuts.json'
import useAppNavigation from '@/hooks/use-app-navigation'
import useAppProject from '@/hooks/use-app-project'
import useAppShortcuts from '@/hooks/use-app-shortcuts'
export default function AppLayout() {
const { currentPage, navigateToPage } = useAppNavigation()
const {
files,
models,
components,
componentTrees,
workflows,
lambdas,
playwrightTests,
storybookStories,
unitTests,
featureToggles,
fileOps,
currentProject,
handleProjectLoad,
stateContext,
actionContext,
} = useAppProject()
const { searchOpen, setSearchOpen, shortcutsOpen, setShortcutsOpen, previewOpen, setPreviewOpen } =
useAppShortcuts({ featureToggles, navigateToPage })
const [lastSaved] = useState<number | null>(() => Date.now())
const [errorCount] = useState(0)
return (
<SidebarProvider defaultOpen={true}>
<NavigationMenu
activeTab={currentPage}
onTabChange={navigateToPage}
featureToggles={featureToggles}
errorCount={errorCount}
/>
<SidebarInset>
<div className="h-screen flex flex-col bg-background">
<AppMainPanel
currentPage={currentPage}
navigateToPage={navigateToPage}
featureToggles={featureToggles}
errorCount={errorCount}
lastSaved={lastSaved}
currentProject={currentProject}
onProjectLoad={handleProjectLoad}
onSearch={() => setSearchOpen(true)}
onShowShortcuts={() => setShortcutsOpen(true)}
onGenerateAI={() => toast.info(appStrings.messages.aiComingSoon)}
onExport={() => toast.info(appStrings.messages.exportComingSoon)}
onPreview={() => setPreviewOpen(true)}
onShowErrors={() => navigateToPage('errors')}
stateContext={stateContext}
actionContext={actionContext}
/>
</div>
</SidebarInset>
<AppDialogs
searchOpen={searchOpen}
onSearchOpenChange={setSearchOpen}
shortcutsOpen={shortcutsOpen}
onShortcutsOpenChange={setShortcutsOpen}
previewOpen={previewOpen}
onPreviewOpenChange={setPreviewOpen}
files={files}
models={models}
components={components}
componentTrees={componentTrees}
workflows={workflows}
lambdas={lambdas}
playwrightTests={playwrightTests}
storybookStories={storybookStories}
unitTests={unitTests}
onNavigate={navigateToPage}
onFileSelect={(fileId) => {
fileOps.setActiveFileId(fileId)
navigateToPage('code')
}}
/>
</SidebarProvider>
)
}

View File

@@ -0,0 +1,77 @@
import { Suspense } from 'react'
import { AppHeader } from '@/components/organisms'
import { PWARegistry } from '@/lib/component-registry'
import { RouterProvider } from '@/router'
import type { FeatureToggles, Project } from '@/types/project'
const { PWAUpdatePrompt, PWAStatusBar } = PWARegistry
interface AppMainPanelProps {
currentPage: string
navigateToPage: (page: string) => void
featureToggles: FeatureToggles
errorCount: number
lastSaved: number | null
currentProject: Project
onProjectLoad: (project: Project) => void
onSearch: () => void
onShowShortcuts: () => void
onGenerateAI: () => void
onExport: () => void
onPreview: () => void
onShowErrors: () => void
stateContext: any
actionContext: any
}
export default function AppMainPanel({
currentPage,
navigateToPage,
featureToggles,
errorCount,
lastSaved,
currentProject,
onProjectLoad,
onSearch,
onShowShortcuts,
onGenerateAI,
onExport,
onPreview,
onShowErrors,
stateContext,
actionContext,
}: AppMainPanelProps) {
return (
<>
<Suspense fallback={<div className="h-1 bg-primary animate-pulse" />}>
<PWAStatusBar />
</Suspense>
<Suspense fallback={null}>
<PWAUpdatePrompt />
</Suspense>
<AppHeader
activeTab={currentPage}
onTabChange={navigateToPage}
featureToggles={featureToggles}
errorCount={errorCount}
lastSaved={lastSaved}
currentProject={currentProject}
onProjectLoad={onProjectLoad}
onSearch={onSearch}
onShowShortcuts={onShowShortcuts}
onGenerateAI={onGenerateAI}
onExport={onExport}
onPreview={onPreview}
onShowErrors={onShowErrors}
/>
<div className="flex-1 overflow-hidden">
<RouterProvider
featureToggles={featureToggles}
stateContext={stateContext}
actionContext={actionContext}
/>
</div>
</>
)
}

View File

@@ -0,0 +1,42 @@
import { useEffect, useState } from 'react'
import { BrowserRouter } from 'react-router-dom'
import AppRouterLayout from '@/components/app/AppRouterLayout'
import LoadingScreen from '@/components/app/LoadingScreen'
import { useSeedData } from '@/hooks/data/use-seed-data'
import { preloadCriticalComponents } from '@/lib/component-registry'
export default function AppRouterBootstrap() {
const { loadSeedData } = useSeedData()
const [appReady, setAppReady] = useState(false)
useEffect(() => {
const timer = setTimeout(() => {
setAppReady(true)
}, 100)
loadSeedData()
.catch(err => {
console.error('[APP_ROUTER] ❌ Seed data loading failed:', err)
})
.finally(() => {
clearTimeout(timer)
setAppReady(true)
preloadCriticalComponents()
})
return () => {
clearTimeout(timer)
}
}, [loadSeedData])
if (!appReady) {
return <LoadingScreen />
}
return (
<BrowserRouter>
<AppRouterLayout />
</BrowserRouter>
)
}

View File

@@ -0,0 +1,79 @@
import { useState } from 'react'
import { toast } from 'sonner'
import AppDialogs from '@/components/app/AppDialogs'
import AppMainPanel from '@/components/app/AppMainPanel'
import appStrings from '@/data/app-shortcuts.json'
import useAppNavigation from '@/hooks/use-app-navigation'
import useAppProject from '@/hooks/use-app-project'
import useAppShortcuts from '@/hooks/use-app-shortcuts'
export default function AppRouterLayout() {
const { currentPage, navigateToPage } = useAppNavigation()
const {
files,
models,
components,
componentTrees,
workflows,
lambdas,
playwrightTests,
storybookStories,
unitTests,
featureToggles,
fileOps,
currentProject,
handleProjectLoad,
stateContext,
actionContext,
} = useAppProject()
const { searchOpen, setSearchOpen, shortcutsOpen, setShortcutsOpen, previewOpen, setPreviewOpen } =
useAppShortcuts({ featureToggles, navigateToPage })
const [lastSaved] = useState<number | null>(() => Date.now())
const [errorCount] = useState(0)
return (
<div className="h-screen flex flex-col bg-background">
<AppMainPanel
currentPage={currentPage}
navigateToPage={navigateToPage}
featureToggles={featureToggles}
errorCount={errorCount}
lastSaved={lastSaved}
currentProject={currentProject}
onProjectLoad={handleProjectLoad}
onSearch={() => setSearchOpen(true)}
onShowShortcuts={() => setShortcutsOpen(true)}
onGenerateAI={() => toast.info(appStrings.messages.aiComingSoon)}
onExport={() => toast.info(appStrings.messages.exportComingSoon)}
onPreview={() => setPreviewOpen(true)}
onShowErrors={() => navigateToPage('errors')}
stateContext={stateContext}
actionContext={actionContext}
/>
<AppDialogs
searchOpen={searchOpen}
onSearchOpenChange={setSearchOpen}
shortcutsOpen={shortcutsOpen}
onShortcutsOpenChange={setShortcutsOpen}
previewOpen={previewOpen}
onPreviewOpenChange={setPreviewOpen}
files={files}
models={models}
components={components}
componentTrees={componentTrees}
workflows={workflows}
lambdas={lambdas}
playwrightTests={playwrightTests}
storybookStories={storybookStories}
unitTests={unitTests}
onNavigate={navigateToPage}
onFileSelect={(fileId) => {
fileOps.setActiveFileId(fileId)
navigateToPage('code')
}}
/>
</div>
)
}

View File

@@ -0,0 +1,16 @@
import appStrings from '@/data/app-shortcuts.json'
interface LoadingScreenProps {
message?: string
}
export default function LoadingScreen({ message = appStrings.messages.loading }: LoadingScreenProps) {
return (
<div className="fixed inset-0 bg-background z-50 flex items-center justify-center">
<div className="flex flex-col items-center gap-4">
<div className="w-12 h-12 border-4 border-primary border-t-transparent rounded-full animate-spin" />
<p className="text-sm text-muted-foreground">{message}</p>
</div>
</div>
)
}