diff --git a/src/config/default-pages.json b/src/config/default-pages.json index e0cc663..f808134 100644 --- a/src/config/default-pages.json +++ b/src/config/default-pages.json @@ -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", diff --git a/src/config/page-loader.ts b/src/config/page-loader.ts index f3bc453..56edf6e 100644 --- a/src/config/page-loader.ts +++ b/src/config/page-loader.ts @@ -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 diff --git a/src/config/page-schema.ts b/src/config/page-schema.ts index ce49807..de2e7dc 100644 --- a/src/config/page-schema.ts +++ b/src/config/page-schema.ts @@ -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(), diff --git a/src/config/pages.json b/src/config/pages.json index 6727b1b..36979dd 100644 --- a/src/config/pages.json +++ b/src/config/pages.json @@ -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", diff --git a/src/config/validate-config.ts b/src/config/validate-config.ts index 63bb357..c8b58ea 100644 --- a/src/config/validate-config.ts +++ b/src/config/validate-config.ts @@ -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', diff --git a/src/lib/route-preload-manager.ts b/src/lib/route-preload-manager.ts index 9856242..12638ac 100644 --- a/src/lib/route-preload-manager.ts +++ b/src/lib/route-preload-manager.ts @@ -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) diff --git a/src/router/routes.tsx b/src/router/routes.tsx index 2431aa3..e21de7a 100644 --- a/src/router/routes.tsx +++ b/src/router/routes.tsx @@ -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: + } + } + + 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: + element: page.component + ? + : } }) 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: - }) + console.log('[ROUTES] ✅ Adding root route from JSON config:', rootPage.type ?? 'component') + if (rootPage.type === 'json' && rootPage.schemaPath) { + routes.push({ + path: '/', + element: + }) + } else { + const props = rootPage.props + ? resolveProps(rootPage.props, stateContext, actionContext) + : {} + + routes.push({ + path: '/', + element: rootPage.component + ? + : + }) + } } else { console.log('[ROUTES] ⚠️ No root page in config, redirecting to /dashboard') routes.push({ diff --git a/src/types/page-config.ts b/src/types/page-config.ts index ba33b45..b08bc84 100644 --- a/src/types/page-config.ts +++ b/src/types/page-config.ts @@ -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