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