Compare commits

...

1 Commits

Author SHA1 Message Date
1dfd891e24 Add JSON page config support 2026-01-18 17:16:30 +00:00
8 changed files with 92 additions and 16 deletions

View File

@@ -245,6 +245,17 @@
"type": "single"
}
},
{
"id": "json-ui-schema",
"title": "JSON UI Schema",
"description": "Schema-driven JSON UI page",
"icon": "Code",
"type": "json",
"schemaPath": "json-ui-showcase-page.json",
"layout": {
"type": "single"
}
},
{
"id": "sass",
"title": "Sass Styles",

View File

@@ -23,7 +23,9 @@ export interface PageConfig {
id: string
title: string
icon: string
component: string
type?: 'component' | 'json'
component?: string
schemaPath?: string
enabled: boolean
isRoot?: boolean
toggleKey?: string

View File

@@ -22,7 +22,9 @@ export const SimplePageConfigSchema = z.object({
id: z.string(),
title: z.string(),
icon: z.string(),
component: z.string(),
type: z.enum(['component', 'json']).optional(),
component: z.string().optional(),
schemaPath: z.string().optional(),
enabled: z.boolean(),
toggleKey: z.string().optional(),
shortcut: z.string().optional(),
@@ -70,7 +72,9 @@ export const PageConfigSchema = z.object({
title: z.string(),
description: z.string(),
icon: z.string(),
component: z.string(),
type: z.enum(['component', 'json']).optional(),
component: z.string().optional(),
schemaPath: z.string().optional(),
layout: LayoutConfigSchema,
features: z.array(FeatureConfigSchema).optional(),
permissions: z.array(z.string()).optional(),

View File

@@ -365,6 +365,16 @@
"order": 22,
"props": {}
},
{
"id": "json-ui-schema",
"title": "JSON UI Schema",
"icon": "Code",
"type": "json",
"schemaPath": "json-ui-showcase-page.json",
"enabled": true,
"order": 22.05,
"props": {}
},
{
"id": "json-conversion-showcase",
"title": "JSON Conversion Showcase",

View File

@@ -30,6 +30,8 @@ export function validatePageConfig(): ValidationError[] {
]
pagesConfig.pages.forEach((page: PageConfig) => {
const pageType = page.type ?? 'component'
if (!page.id) {
errors.push({
page: page.title || 'Unknown',
@@ -57,7 +59,16 @@ export function validatePageConfig(): ValidationError[] {
})
}
if (!page.component) {
if (page.type && !['component', 'json'].includes(page.type)) {
errors.push({
page: page.id || 'Unknown',
field: 'type',
message: `Unknown page type: ${page.type}. Expected "component" or "json".`,
severity: 'error',
})
}
if (pageType === 'component' && !page.component) {
errors.push({
page: page.id || 'Unknown',
field: 'component',
@@ -66,6 +77,15 @@ export function validatePageConfig(): ValidationError[] {
})
}
if (pageType === 'json' && !page.schemaPath) {
errors.push({
page: page.id || 'Unknown',
field: 'schemaPath',
message: 'schemaPath is required for JSON pages',
severity: 'error',
})
}
if (!page.icon) {
errors.push({
page: page.id || 'Unknown',

View File

@@ -79,6 +79,12 @@ export class RoutePreloadManager {
}
try {
if (page.type === 'json') {
console.log(`[PRELOAD_MGR] 🧩 Skipping component preload for JSON page: ${pageId}`)
this.preloadedRoutes.add(pageId)
return
}
const componentName = page.component as ComponentName
console.log(`[PRELOAD_MGR] 🚀 Preloading ${pageId}${componentName}`)
preloadComponentByName(componentName)

View File

@@ -2,6 +2,7 @@ import { lazy, Suspense } from 'react'
import { RouteObject, Navigate } from 'react-router-dom'
import { LoadingFallback } from '@/components/molecules'
import { NotFoundPage } from '@/components/NotFoundPage'
import { JSONSchemaPageLoader } from '@/components/JSONSchemaPageLoader'
import { getEnabledPages, resolveProps } from '@/config/page-loader'
import { ComponentRegistry } from '@/lib/component-registry'
import { FeatureToggles } from '@/types/project'
@@ -80,12 +81,14 @@ export function createRoutes(
console.log('[ROUTES] 📄 Enabled pages details:', JSON.stringify(enabledPages.map(p => ({
id: p.id,
component: p.component,
type: p.type,
schemaPath: p.schemaPath,
isRoot: p.isRoot,
enabled: p.enabled
})), null, 2))
const rootPage = enabledPages.find(p => p.isRoot)
console.log('[ROUTES] 🏠 Root page search result:', rootPage ? `Found: ${rootPage.id} (${rootPage.component})` : 'NOT FOUND - will redirect to /dashboard')
console.log('[ROUTES] 🏠 Root page search result:', rootPage ? `Found: ${rootPage.id} (${rootPage.type ?? 'component'})` : 'NOT FOUND - will redirect to /dashboard')
const routes: RouteObject[] = enabledPages
.filter(p => !p.isRoot)
@@ -96,7 +99,14 @@ export function createRoutes(
? resolveProps(page.props, stateContext, actionContext)
: {}
if (page.requiresResizable && page.resizableConfig) {
if (page.type === 'json' && page.schemaPath) {
return {
path: `/${page.id}`,
element: <JSONSchemaPageLoader schemaPath={page.schemaPath} />
}
}
if (page.requiresResizable && page.resizableConfig && page.component) {
console.log('[ROUTES] 🔀 Page requires resizable layout:', page.id)
const config = page.resizableConfig
const leftProps = resolveProps(config.leftProps, stateContext, actionContext)
@@ -117,20 +127,31 @@ export function createRoutes(
return {
path: `/${page.id}`,
element: <LazyComponent componentName={page.component} props={props} />
element: page.component
? <LazyComponent componentName={page.component} props={props} />
: <LoadingFallback message={`Component not configured for ${page.id}`} />
}
})
if (rootPage) {
console.log('[ROUTES] ✅ Adding root route from JSON config:', rootPage.component)
const props = rootPage.props
? resolveProps(rootPage.props, stateContext, actionContext)
: {}
routes.push({
path: '/',
element: <LazyComponent componentName={rootPage.component} props={props} />
})
console.log('[ROUTES] ✅ Adding root route from JSON config:', rootPage.type ?? 'component')
if (rootPage.type === 'json' && rootPage.schemaPath) {
routes.push({
path: '/',
element: <JSONSchemaPageLoader schemaPath={rootPage.schemaPath} />
})
} else {
const props = rootPage.props
? resolveProps(rootPage.props, stateContext, actionContext)
: {}
routes.push({
path: '/',
element: rootPage.component
? <LazyComponent componentName={rootPage.component} props={props} />
: <LoadingFallback message="Root component not configured" />
})
}
} else {
console.log('[ROUTES] ⚠️ No root page in config, redirecting to /dashboard')
routes.push({

View File

@@ -5,7 +5,9 @@ export interface PageConfig {
id: string
title: string
icon: string
type?: 'component' | 'json'
component?: string
schemaPath?: string
schema?: string
enabled: boolean
isRoot?: boolean