mirror of
https://github.com/johndoe6345789/low-code-react-app-b.git
synced 2026-04-26 14:44:55 +00:00
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:
45
src/components/app/AppBootstrap.tsx
Normal file
45
src/components/app/AppBootstrap.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
89
src/components/app/AppDialogs.tsx
Normal file
89
src/components/app/AppDialogs.tsx
Normal 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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
92
src/components/app/AppLayout.tsx
Normal file
92
src/components/app/AppLayout.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
77
src/components/app/AppMainPanel.tsx
Normal file
77
src/components/app/AppMainPanel.tsx
Normal 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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
42
src/components/app/AppRouterBootstrap.tsx
Normal file
42
src/components/app/AppRouterBootstrap.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
79
src/components/app/AppRouterLayout.tsx
Normal file
79
src/components/app/AppRouterLayout.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
16
src/components/app/LoadingScreen.tsx
Normal file
16
src/components/app/LoadingScreen.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user