diff --git a/component-registry.json b/component-registry.json index 6187ba9..281ba4a 100644 --- a/component-registry.json +++ b/component-registry.json @@ -236,6 +236,15 @@ "category": "showcase", "description": "JSON UI system demonstration" }, + { + "name": "JSONConversionShowcase", + "path": "@/components/JSONConversionShowcase", + "export": "JSONConversionShowcase", + "type": "feature", + "preload": false, + "category": "showcase", + "description": "JSON conversion showcase overview" + }, { "name": "SchemaEditor", "path": "@/components/SchemaEditorPage", diff --git a/e2e/visual-regression.spec.ts b/e2e/visual-regression.spec.ts new file mode 100644 index 0000000..9ba8ccc --- /dev/null +++ b/e2e/visual-regression.spec.ts @@ -0,0 +1,16 @@ +import { test, expect } from '@playwright/test' + +test.describe('visual regression', () => { + test('json conversion showcase', async ({ page }) => { + await page.goto('/json-conversion-showcase') + await page.waitForLoadState('networkidle') + await page.waitForFunction(() => { + const root = document.querySelector('#root') + return root && root.textContent && root.textContent.length > 0 + }) + await page.addStyleTag({ + content: '* { transition: none !important; animation: none !important; }', + }) + await expect(page).toHaveScreenshot('json-conversion-showcase.png', { fullPage: true }) + }) +}) diff --git a/src/components/JSONConversionShowcase.tsx b/src/components/JSONConversionShowcase.tsx new file mode 100644 index 0000000..ec08a57 --- /dev/null +++ b/src/components/JSONConversionShowcase.tsx @@ -0,0 +1,9 @@ +import { PageRenderer } from '@/lib/json-ui/page-renderer' +import conversionShowcaseSchema from '@/config/pages/json-conversion-showcase.json' +import { PageSchema } from '@/types/json-ui' + +export function JSONConversionShowcase() { + const schema = conversionShowcaseSchema as PageSchema + + return +} diff --git a/src/components/molecules/DataSourceEditorDialog.tsx b/src/components/molecules/DataSourceEditorDialog.tsx index ceec274..20de4ae 100644 --- a/src/components/molecules/DataSourceEditorDialog.tsx +++ b/src/components/molecules/DataSourceEditorDialog.tsx @@ -6,9 +6,8 @@ import { DataSourceIdField } from '@/components/molecules/data-source-editor/Dat import { KvSourceFields } from '@/components/molecules/data-source-editor/KvSourceFields' import { StaticSourceFields } from '@/components/molecules/data-source-editor/StaticSourceFields' import { ComputedSourceFields } from '@/components/molecules/data-source-editor/ComputedSourceFields' -import { useDataSourceEditor } from '@/hooks/data/use-data-source-editor' import dataSourceEditorCopy from '@/data/data-source-editor-dialog.json' -import { useDataSourceEditor } from '@/hooks/use-data-source-editor' +import { useDataSourceEditor } from '@/hooks/data/use-data-source-editor' interface DataSourceEditorDialogProps { open: boolean diff --git a/src/config/default-pages.json b/src/config/default-pages.json index 10ec0c9..e0cc663 100644 --- a/src/config/default-pages.json +++ b/src/config/default-pages.json @@ -235,6 +235,16 @@ "type": "single" } }, + { + "id": "json-conversion-showcase", + "title": "JSON Conversion Showcase", + "description": "JSON conversion showcase overview", + "icon": "BookOpen", + "component": "JSONConversionShowcase", + "layout": { + "type": "single" + } + }, { "id": "sass", "title": "Sass Styles", diff --git a/src/config/orchestration/component-registry.ts b/src/config/orchestration/component-registry.ts index ff93835..cc58a1c 100644 --- a/src/config/orchestration/component-registry.ts +++ b/src/config/orchestration/component-registry.ts @@ -27,6 +27,7 @@ import { PWASettings } from '@/components/PWASettings' import { FaviconDesigner } from '@/components/FaviconDesigner' import { FeatureIdeaCloud } from '@/components/FeatureIdeaCloud' import { JSONUIShowcase } from '@/components/JSONUIShowcase' +import { JSONConversionShowcase } from '@/components/JSONConversionShowcase' export const ComponentRegistry: Record> = { Button, @@ -61,6 +62,7 @@ export const ComponentRegistry: Record> = { FaviconDesigner, FeatureIdeaCloud, JSONUIShowcase, + JSONConversionShowcase, } export function getComponent(name: string): ComponentType | null { diff --git a/src/config/pages.json b/src/config/pages.json index 99dc30b..6727b1b 100644 --- a/src/config/pages.json +++ b/src/config/pages.json @@ -365,6 +365,15 @@ "order": 22, "props": {} }, + { + "id": "json-conversion-showcase", + "title": "JSON Conversion Showcase", + "icon": "BookOpen", + "component": "JSONConversionShowcase", + "enabled": true, + "order": 22.1, + "props": {} + }, { "id": "schema-editor", "title": "Schema Editor", diff --git a/src/config/pages/json-conversion-showcase.json b/src/config/pages/json-conversion-showcase.json new file mode 100644 index 0000000..c3acaf1 --- /dev/null +++ b/src/config/pages/json-conversion-showcase.json @@ -0,0 +1,341 @@ +{ + "id": "json-conversion-showcase", + "name": "JSON Conversion Showcase", + "layout": { + "type": "single" + }, + "dataSources": [ + { + "id": "batches", + "type": "static", + "defaultValue": [ + { + "title": "Phase 1", + "summary": "Models, Component Trees, Workflows", + "stats": [ + { "label": "Pages Converted", "value": "3" }, + { "label": "JSON Schema Lines", "value": "~900" }, + { "label": "Code Reduction", "value": "~60%" } + ], + "pages": [ + { + "name": "Models Designer", + "pageId": "models-json", + "schema": "src/config/pages/model-designer.json", + "dataStore": "app-models" + }, + { + "name": "Component Trees Manager", + "pageId": "component-trees-json", + "schema": "src/config/pages/component-tree.json", + "dataStore": "app-component-trees" + }, + { + "name": "Workflows Designer", + "pageId": "workflows-json", + "schema": "src/config/pages/workflow-designer.json", + "dataStore": "app-workflows" + } + ], + "seedHighlights": [ + "3 model records (User, Post, Comment)", + "2 component trees (Dashboard, Profile)", + "3 workflow definitions (Registration, Processing, Payment)" + ] + }, + { + "title": "Phase 2", + "summary": "Lambdas, Styling, Flask API", + "stats": [ + { "label": "Pages Converted", "value": "3" }, + { "label": "JSON Schema Lines", "value": "~2,100" }, + { "label": "Seed Records", "value": "8" } + ], + "pages": [ + { + "name": "Lambda Designer", + "pageId": "lambdas-json", + "schema": "src/config/pages/lambda-designer.json", + "dataStore": "app-lambdas" + }, + { + "name": "Style Designer", + "pageId": "styling-json", + "schema": "src/config/pages/style-designer.json", + "dataStore": "app-theme" + }, + { + "name": "Flask API Designer", + "pageId": "flask-json", + "schema": "src/config/pages/flask-designer.json", + "dataStore": "app-flask-config" + } + ], + "seedHighlights": [ + "3 serverless functions seeded with triggers", + "2 theme variants with custom palettes", + "3 Flask blueprints covering 7 endpoints" + ] + } + ] + }, + { + "id": "patterns", + "type": "static", + "defaultValue": [ + "Sidebar + detail layout pattern", + "Computed counters for live totals", + "KV-backed data sources for persistence", + "Declarative empty states and actions" + ] + } + ], + "components": [ + { + "id": "root", + "type": "div", + "props": { + "className": "h-full overflow-auto bg-background p-6" + }, + "children": [ + { + "id": "header", + "type": "div", + "props": { "className": "space-y-2" }, + "children": [ + { + "id": "title", + "type": "Heading", + "props": { + "className": "text-3xl font-bold", + "children": "JSON Conversion Showcase" + } + }, + { + "id": "subtitle", + "type": "Text", + "props": { + "className": "text-muted-foreground", + "children": "One-page overview of JSON conversion phases and their deliverables." + } + } + ] + }, + { + "id": "phase-grid", + "type": "div", + "props": { "className": "mt-8 space-y-8" }, + "loop": { + "source": "batches", + "itemVar": "batch" + }, + "children": [ + { + "id": "phase-card", + "type": "Card", + "children": [ + { + "id": "phase-header", + "type": "CardHeader", + "children": [ + { + "id": "phase-title", + "type": "CardTitle", + "bindings": { + "children": { "source": "batch", "sourceType": "bindings", "path": "title" } + } + }, + { + "id": "phase-description", + "type": "CardDescription", + "bindings": { + "children": { "source": "batch", "sourceType": "bindings", "path": "summary" } + } + } + ] + }, + { + "id": "phase-content", + "type": "CardContent", + "props": { "className": "space-y-6" }, + "children": [ + { + "id": "stats-grid", + "type": "div", + "props": { + "className": "grid gap-3 md:grid-cols-3" + }, + "loop": { + "source": "batch.stats", + "itemVar": "stat" + }, + "children": [ + { + "id": "stat-item", + "type": "div", + "props": { "className": "rounded-lg border border-border p-3" }, + "children": [ + { + "id": "stat-label", + "type": "Text", + "props": { "className": "text-xs text-muted-foreground" }, + "bindings": { + "children": { "source": "stat", "sourceType": "bindings", "path": "label" } + } + }, + { + "id": "stat-value", + "type": "Text", + "props": { "className": "text-lg font-semibold" }, + "bindings": { + "children": { "source": "stat", "sourceType": "bindings", "path": "value" } + } + } + ] + } + ] + }, + { + "id": "pages-section", + "type": "div", + "props": { "className": "space-y-3" }, + "children": [ + { + "id": "pages-title", + "type": "Heading", + "props": { "className": "text-base font-semibold", "children": "Converted Pages" } + }, + { + "id": "pages-grid", + "type": "div", + "props": { "className": "grid gap-3 md:grid-cols-3" }, + "loop": { + "source": "batch.pages", + "itemVar": "page" + }, + "children": [ + { + "id": "page-item", + "type": "Card", + "children": [ + { + "id": "page-item-header", + "type": "CardHeader", + "children": [ + { + "id": "page-item-title", + "type": "CardTitle", + "bindings": { + "children": { "source": "page", "sourceType": "bindings", "path": "name" } + } + }, + { + "id": "page-item-description", + "type": "CardDescription", + "bindings": { + "children": { "source": "page", "sourceType": "bindings", "path": "schema" } + } + } + ] + }, + { + "id": "page-item-content", + "type": "CardContent", + "props": { "className": "space-y-2" }, + "children": [ + { + "id": "page-id", + "type": "Badge", + "bindings": { + "children": { "source": "page", "sourceType": "bindings", "path": "pageId" } + } + }, + { + "id": "page-store", + "type": "Text", + "props": { "className": "text-xs text-muted-foreground" }, + "bindings": { + "children": { "source": "page", "sourceType": "bindings", "path": "dataStore" } + } + } + ] + } + ] + } + ] + } + ] + }, + { + "id": "seed-section", + "type": "div", + "props": { "className": "space-y-2" }, + "children": [ + { + "id": "seed-title", + "type": "Heading", + "props": { "className": "text-base font-semibold", "children": "Seed Data Highlights" } + }, + { + "id": "seed-list", + "type": "div", + "props": { "className": "space-y-1" }, + "loop": { + "source": "batch.seedHighlights", + "itemVar": "seed" + }, + "children": [ + { + "id": "seed-item", + "type": "Text", + "props": { "className": "text-sm" }, + "bindings": { + "children": { "source": "seed", "sourceType": "bindings" } + } + } + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "id": "patterns-section", + "type": "div", + "props": { "className": "mt-8" }, + "children": [ + { + "id": "patterns-title", + "type": "Heading", + "props": { "className": "text-xl font-semibold", "children": "Reusable Patterns" } + }, + { + "id": "patterns-list", + "type": "div", + "props": { "className": "mt-3 space-y-2" }, + "loop": { + "source": "patterns", + "itemVar": "pattern" + }, + "children": [ + { + "id": "pattern-item", + "type": "Text", + "props": { "className": "text-sm" }, + "bindings": { + "children": { "source": "pattern", "sourceType": "bindings" } + } + } + ] + } + ] + } + ] + } + ], + "globalActions": [] +} diff --git a/src/lib/navigation-config.tsx b/src/lib/navigation-config.tsx index 4a0dd42..306b62a 100644 --- a/src/lib/navigation-config.tsx +++ b/src/lib/navigation-config.tsx @@ -153,6 +153,11 @@ export const tabInfo: Record = { icon: , description: 'JSON-driven UI examples', }, + 'json-conversion-showcase': { + title: 'JSON Conversion Showcase', + icon: , + description: 'JSON conversion showcase overview', + }, 'atomic-library': { title: 'Atomic Components', icon: , @@ -332,6 +337,12 @@ export const navigationGroups: NavigationGroup[] = [ value: 'docs', featureKey: 'documentation', }, + { + id: 'json-conversion-showcase', + label: 'JSON Conversion Showcase', + icon: , + value: 'json-conversion-showcase', + }, { id: 'settings', label: 'Settings',