mirror of
https://github.com/johndoe6345789/low-code-react-app-b.git
synced 2026-04-24 13:44:54 +00:00
Generated by Spark: Got weird problem - Publish to https://low-code-react-app-b--johndoe6345789.github.app/ works fine but preview just shows Purple dot and loading.. + white page
This commit is contained in:
186
src/App.tsx
186
src/App.tsx
@@ -83,10 +83,24 @@ function App() {
|
||||
const [shortcutsOpen, setShortcutsOpen] = useState(false)
|
||||
const [lastSaved] = useState<number | null>(Date.now())
|
||||
const [errorCount] = useState(0)
|
||||
const [appReady, setAppReady] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
setAppReady(true)
|
||||
}, 100)
|
||||
|
||||
loadSeedData()
|
||||
}, [])
|
||||
.catch(err => {
|
||||
console.error('Seed data loading failed:', err)
|
||||
})
|
||||
.finally(() => {
|
||||
clearTimeout(timer)
|
||||
setAppReady(true)
|
||||
})
|
||||
|
||||
return () => clearTimeout(timer)
|
||||
}, [loadSeedData])
|
||||
|
||||
const pageConfig = useMemo(() => getPageConfig(), [])
|
||||
const enabledPages = useMemo(() => getEnabledPages(featureToggles), [featureToggles])
|
||||
@@ -186,92 +200,112 @@ function App() {
|
||||
}
|
||||
|
||||
const renderPageContent = (page: any) => {
|
||||
const Component = componentMap[page.component]
|
||||
if (!Component) {
|
||||
return <LoadingFallback message={`Component ${page.component} not found`} />
|
||||
}
|
||||
|
||||
if (page.requiresResizable && page.resizableConfig) {
|
||||
const config = page.resizableConfig
|
||||
const LeftComponent = componentMap[config.leftComponent]
|
||||
const RightComponent = Component
|
||||
|
||||
if (!LeftComponent) {
|
||||
return <LoadingFallback message={`Component ${config.leftComponent} not found`} />
|
||||
try {
|
||||
const Component = componentMap[page.component]
|
||||
if (!Component) {
|
||||
return <LoadingFallback message={`Component ${page.component} not found`} />
|
||||
}
|
||||
|
||||
const stateContext = {
|
||||
files,
|
||||
models,
|
||||
components,
|
||||
componentTrees,
|
||||
workflows,
|
||||
lambdas,
|
||||
theme,
|
||||
playwrightTests,
|
||||
storybookStories,
|
||||
unitTests,
|
||||
flaskConfig,
|
||||
nextjsConfig,
|
||||
npmSettings,
|
||||
featureToggles,
|
||||
activeFileId,
|
||||
if (page.requiresResizable && page.resizableConfig) {
|
||||
const config = page.resizableConfig
|
||||
const LeftComponent = componentMap[config.leftComponent]
|
||||
const RightComponent = Component
|
||||
|
||||
if (!LeftComponent) {
|
||||
return <LoadingFallback message={`Component ${config.leftComponent} not found`} />
|
||||
}
|
||||
|
||||
const stateContext = {
|
||||
files,
|
||||
models,
|
||||
components,
|
||||
componentTrees,
|
||||
workflows,
|
||||
lambdas,
|
||||
theme,
|
||||
playwrightTests,
|
||||
storybookStories,
|
||||
unitTests,
|
||||
flaskConfig,
|
||||
nextjsConfig,
|
||||
npmSettings,
|
||||
featureToggles,
|
||||
activeFileId,
|
||||
}
|
||||
|
||||
const actionContext = {
|
||||
handleFileChange,
|
||||
setActiveFileId,
|
||||
handleFileClose,
|
||||
handleFileAdd,
|
||||
setModels,
|
||||
setComponents,
|
||||
setComponentTrees,
|
||||
setWorkflows,
|
||||
setLambdas,
|
||||
setTheme,
|
||||
setPlaywrightTests,
|
||||
setStorybookStories,
|
||||
setUnitTests,
|
||||
setFlaskConfig,
|
||||
setNextjsConfig,
|
||||
setNpmSettings,
|
||||
setFeatureToggles,
|
||||
}
|
||||
|
||||
const leftProps = resolveProps(config.leftProps, stateContext, actionContext)
|
||||
const rightProps = getPropsForComponent(page.id)
|
||||
|
||||
return (
|
||||
<ResizablePanelGroup direction="horizontal">
|
||||
<ResizablePanel
|
||||
defaultSize={config.leftPanel.defaultSize}
|
||||
minSize={config.leftPanel.minSize}
|
||||
maxSize={config.leftPanel.maxSize}
|
||||
>
|
||||
<Suspense fallback={<LoadingFallback message={`Loading ${config.leftComponent.toLowerCase()}...`} />}>
|
||||
<LeftComponent {...leftProps} />
|
||||
</Suspense>
|
||||
</ResizablePanel>
|
||||
<ResizableHandle />
|
||||
<ResizablePanel defaultSize={config.rightPanel.defaultSize}>
|
||||
<Suspense fallback={<LoadingFallback message={`Loading ${page.title.toLowerCase()}...`} />}>
|
||||
<RightComponent {...rightProps} />
|
||||
</Suspense>
|
||||
</ResizablePanel>
|
||||
</ResizablePanelGroup>
|
||||
)
|
||||
}
|
||||
|
||||
const actionContext = {
|
||||
handleFileChange,
|
||||
setActiveFileId,
|
||||
handleFileClose,
|
||||
handleFileAdd,
|
||||
setModels,
|
||||
setComponents,
|
||||
setComponentTrees,
|
||||
setWorkflows,
|
||||
setLambdas,
|
||||
setTheme,
|
||||
setPlaywrightTests,
|
||||
setStorybookStories,
|
||||
setUnitTests,
|
||||
setFlaskConfig,
|
||||
setNextjsConfig,
|
||||
setNpmSettings,
|
||||
setFeatureToggles,
|
||||
}
|
||||
|
||||
const leftProps = resolveProps(config.leftProps, stateContext, actionContext)
|
||||
const rightProps = getPropsForComponent(page.id)
|
||||
|
||||
const props = getPropsForComponent(page.id)
|
||||
return (
|
||||
<ResizablePanelGroup direction="horizontal">
|
||||
<ResizablePanel
|
||||
defaultSize={config.leftPanel.defaultSize}
|
||||
minSize={config.leftPanel.minSize}
|
||||
maxSize={config.leftPanel.maxSize}
|
||||
>
|
||||
<Suspense fallback={<LoadingFallback message={`Loading ${config.leftComponent.toLowerCase()}...`} />}>
|
||||
<LeftComponent {...leftProps} />
|
||||
</Suspense>
|
||||
</ResizablePanel>
|
||||
<ResizableHandle />
|
||||
<ResizablePanel defaultSize={config.rightPanel.defaultSize}>
|
||||
<Suspense fallback={<LoadingFallback message={`Loading ${page.title.toLowerCase()}...`} />}>
|
||||
<RightComponent {...rightProps} />
|
||||
</Suspense>
|
||||
</ResizablePanel>
|
||||
</ResizablePanelGroup>
|
||||
<Suspense fallback={<LoadingFallback message={`Loading ${page.title.toLowerCase()}...`} />}>
|
||||
<Component {...props} />
|
||||
</Suspense>
|
||||
)
|
||||
} catch (error) {
|
||||
console.error(`Failed to render page ${page.id}:`, error)
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<div className="text-center">
|
||||
<p className="text-destructive font-semibold">Failed to load {page.title}</p>
|
||||
<p className="text-sm text-muted-foreground mt-2">Check console for details</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const props = getPropsForComponent(page.id)
|
||||
return (
|
||||
<Suspense fallback={<LoadingFallback message={`Loading ${page.title.toLowerCase()}...`} />}>
|
||||
<Component {...props} />
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-screen flex flex-col bg-background">
|
||||
{!appReady && (
|
||||
<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">Loading CodeForge...</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<Suspense fallback={<div className="h-1 bg-primary animate-pulse" />}>
|
||||
<PWAStatusBar />
|
||||
</Suspense>
|
||||
|
||||
@@ -9,6 +9,8 @@ interface ErrorFallbackProps {
|
||||
}
|
||||
|
||||
export const ErrorFallback = ({ error, resetErrorBoundary }: ErrorFallbackProps) => {
|
||||
console.error('ErrorFallback caught:', error);
|
||||
|
||||
if (import.meta.env.DEV) throw error;
|
||||
|
||||
return (
|
||||
@@ -27,6 +29,11 @@ export const ErrorFallback = ({ error, resetErrorBoundary }: ErrorFallbackProps)
|
||||
<pre className="text-xs text-destructive bg-muted/50 p-3 rounded border overflow-auto max-h-32">
|
||||
{error.message}
|
||||
</pre>
|
||||
{error.stack && (
|
||||
<pre className="text-xs text-muted-foreground bg-muted/50 p-3 rounded border overflow-auto max-h-48 mt-2">
|
||||
{error.stack}
|
||||
</pre>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Button
|
||||
|
||||
@@ -83,26 +83,38 @@ export function resolveProps(propConfig: PropConfig | undefined, stateContext: R
|
||||
|
||||
const resolvedProps: Record<string, any> = {}
|
||||
|
||||
if (propConfig.state) {
|
||||
for (const stateKey of propConfig.state) {
|
||||
const [propName, contextKey] = stateKey.includes(':')
|
||||
? stateKey.split(':')
|
||||
: [stateKey, stateKey]
|
||||
|
||||
if (stateContext[contextKey] !== undefined) {
|
||||
resolvedProps[propName] = stateContext[contextKey]
|
||||
try {
|
||||
if (propConfig.state) {
|
||||
for (const stateKey of propConfig.state) {
|
||||
try {
|
||||
const [propName, contextKey] = stateKey.includes(':')
|
||||
? stateKey.split(':')
|
||||
: [stateKey, stateKey]
|
||||
|
||||
if (stateContext[contextKey] !== undefined) {
|
||||
resolvedProps[propName] = stateContext[contextKey]
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(`Failed to resolve state prop: ${stateKey}`, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (propConfig.actions) {
|
||||
for (const actionKey of propConfig.actions) {
|
||||
const [propName, contextKey] = actionKey.split(':')
|
||||
|
||||
if (actionContext[contextKey]) {
|
||||
resolvedProps[propName] = actionContext[contextKey]
|
||||
|
||||
if (propConfig.actions) {
|
||||
for (const actionKey of propConfig.actions) {
|
||||
try {
|
||||
const [propName, contextKey] = actionKey.split(':')
|
||||
|
||||
if (actionContext[contextKey]) {
|
||||
resolvedProps[propName] = actionContext[contextKey]
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(`Failed to resolve action prop: ${actionKey}`, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to resolve props:', err)
|
||||
}
|
||||
|
||||
return resolvedProps
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import seedDataConfig from '@/config/seed-data.json'
|
||||
|
||||
export function useSeedData() {
|
||||
const [isLoaded, setIsLoaded] = useState(false)
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
|
||||
const loadSeedData = async () => {
|
||||
const loadSeedData = useCallback(async () => {
|
||||
if (isLoading || isLoaded) return
|
||||
|
||||
setIsLoading(true)
|
||||
try {
|
||||
if (!window.spark?.kv) {
|
||||
console.warn('Spark KV not available, skipping seed data')
|
||||
return
|
||||
}
|
||||
|
||||
const keys = await window.spark.kv.keys()
|
||||
|
||||
for (const [key, value] of Object.entries(seedDataConfig)) {
|
||||
@@ -21,14 +26,20 @@ export function useSeedData() {
|
||||
setIsLoaded(true)
|
||||
} catch (error) {
|
||||
console.error('Failed to load seed data:', error)
|
||||
setIsLoaded(true)
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
}, [isLoading, isLoaded])
|
||||
|
||||
const resetSeedData = async () => {
|
||||
const resetSeedData = useCallback(async () => {
|
||||
setIsLoading(true)
|
||||
try {
|
||||
if (!window.spark?.kv) {
|
||||
console.warn('Spark KV not available')
|
||||
return
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(seedDataConfig)) {
|
||||
await window.spark.kv.set(key, value)
|
||||
}
|
||||
@@ -38,11 +49,16 @@ export function useSeedData() {
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
const clearAllData = async () => {
|
||||
const clearAllData = useCallback(async () => {
|
||||
setIsLoading(true)
|
||||
try {
|
||||
if (!window.spark?.kv) {
|
||||
console.warn('Spark KV not available')
|
||||
return
|
||||
}
|
||||
|
||||
const keys = await window.spark.kv.keys()
|
||||
for (const key of keys) {
|
||||
await window.spark.kv.delete(key)
|
||||
@@ -53,7 +69,7 @@ export function useSeedData() {
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
return {
|
||||
isLoaded,
|
||||
|
||||
Reference in New Issue
Block a user