Convert TypeScript schemas to JSON with compute functions

Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-01-17 22:38:31 +00:00
parent 369f1a30c0
commit f82c7aa081
14 changed files with 1162 additions and 9 deletions

View File

@@ -0,0 +1,116 @@
# Schema Conversion Summary
## Overview
Successfully converted TypeScript schema files to JSON format with extracted compute functions.
## Files Created
### JSON Schemas
1. **`src/schemas/analytics-dashboard.json`** - Converted from `dashboard-schema.ts`
- Contains the analytics dashboard with user management
- Compute functions: `computeFilteredUsers`, `computeStats`, `updateFilterQuery`, `transformFilteredUsers`, `transformUserList`
2. **`src/schemas/todo-list.json`** - Split from `page-schemas.ts`
- Todo list application schema
- Compute functions: `computeTodoStats`, `updateNewTodo`, `computeAddTodo`, `checkCanAddTodo`
3. **`src/schemas/dashboard-simple.json`** - Split from `page-schemas.ts`
- Simple dashboard with static stats
- No compute functions (pure static data)
4. **`src/schemas/new-molecules-showcase.json`** - Split from `page-schemas.ts`
- Showcase of new molecular components
- No compute functions (pure static data)
### TypeScript Support Files
5. **`src/schemas/compute-functions.ts`** - Exported compute functions
- `computeFilteredUsers` - Filters users by search query
- `computeStats` - Calculates user statistics (total, active, inactive)
- `computeTodoStats` - Calculates todo statistics (total, completed, remaining)
- `computeAddTodo` - Creates new todo item
- `updateFilterQuery` - Event handler for filter input
- `updateNewTodo` - Event handler for todo input
- `checkCanAddTodo` - Condition checker for add button
- `transformFilteredUsers` - Transform function for badge display
- `transformUserList` - Transform function for rendering user cards
6. **`src/schemas/schema-loader.ts`** - Hydration utility
- `hydrateSchema()` - Converts JSON schemas to runtime schemas
- Replaces string function identifiers with actual functions
- Handles compute functions in dataSources, events, actions, and bindings
## Updated Files
### Component Files
- **`src/components/DashboardDemoPage.tsx`**
- Changed from importing TS schema to importing JSON + hydration
- **`src/components/JSONUIShowcasePage.tsx`**
- Changed from importing TS schemas to importing JSON + hydration
### Configuration
- **`tsconfig.json`**
- Added `"resolveJsonModule": true` to enable JSON imports
### Documentation
- **`docs/ARCHITECTURE.md`** - Updated file structure and example code
- **`docs/JSON_UI_GUIDE.md`** - Updated references to schema files
- **`docs/IMPLEMENTATION_SUMMARY.md`** - Updated file list
- **`docs/JSON_UI_ENHANCEMENT_SUMMARY.md`** - Updated schema file name
## How It Works
### 1. JSON Schema Format
Compute functions are represented as string identifiers in JSON:
```json
{
"id": "stats",
"type": "computed",
"compute": "computeStats",
"dependencies": ["users"]
}
```
### 2. Hydration Process
The `hydrateSchema()` function replaces string identifiers with actual functions:
```typescript
import { hydrateSchema } from '@/schemas/schema-loader'
import analyticsDashboardJson from '@/schemas/analytics-dashboard.json'
const schema = hydrateSchema(analyticsDashboardJson)
```
### 3. Usage in Components
```typescript
export function DashboardDemoPage() {
return <PageRenderer schema={schema} />
}
```
## Benefits
1. **Pure JSON** - Schemas are now pure JSON files, making them easier to:
- Store in databases
- Transmit over APIs
- Edit with JSON tools
- Version control and diff
2. **Separation of Concerns** - Logic is separated from structure:
- JSON defines the UI structure
- TypeScript contains the compute logic
- Schema loader connects them at runtime
3. **Type Safety** - TypeScript functions remain type-safe and testable
4. **Maintainability** - Compute functions are centralized and reusable
## Old Files (Can be removed)
- `src/schemas/dashboard-schema.ts` (replaced by `analytics-dashboard.json`)
- `src/schemas/page-schemas.ts` (split into 3 JSON files)
Note: Keep `src/schemas/ui-schema.ts` as it contains Zod validation schemas, not UI schemas.
## Testing
- Build completed successfully with `npm run build`
- All TypeScript errors resolved
- JSON imports working correctly

View File

@@ -45,7 +45,10 @@ This project demonstrates a comprehensive JSON-driven UI architecture with atomi
```typescript
import { PageRenderer } from '@/lib/json-ui/page-renderer'
import { dashboardSchema } from '@/schemas/dashboard-schema'
import { hydrateSchema } from '@/schemas/schema-loader'
import analyticsDashboardJson from '@/schemas/analytics-dashboard.json'
const dashboardSchema = hydrateSchema(analyticsDashboardJson)
export function DashboardPage() {
return <PageRenderer schema={dashboardSchema} />
@@ -439,8 +442,12 @@ src/
│ ├── component-renderer.tsx
│ └── component-registry.tsx
├── schemas/ # JSON page schemas
│ ├── dashboard-schema.ts
── page-schemas.ts
│ ├── analytics-dashboard.json
── todo-list.json
│ ├── dashboard-simple.json
│ ├── new-molecules-showcase.json
│ ├── compute-functions.ts
│ └── schema-loader.ts
└── types/
└── json-ui.ts # TypeScript types
```

View File

@@ -163,7 +163,12 @@ src/
│ └── json-ui/
│ └── component-registry.tsx [MODIFIED]
├── schemas/
── dashboard-schema.ts [NEW]
── analytics-dashboard.json [NEW]
│ ├── todo-list.json [NEW]
│ ├── dashboard-simple.json [NEW]
│ ├── new-molecules-showcase.json [NEW]
│ ├── compute-functions.ts [NEW]
│ └── schema-loader.ts [NEW]
├── types/
│ └── json-ui.ts [MODIFIED]
├── App.simple-json-demo.tsx [NEW]

View File

@@ -59,7 +59,7 @@ Enhanced the JSON-driven UI system by creating additional custom hooks, atomic c
## JSON Page Schema Created
### Analytics Dashboard Schema (/src/schemas/dashboard-schema.ts)
### Analytics Dashboard Schema (/src/schemas/analytics-dashboard.json)
Comprehensive JSON-driven page demonstrating:
- **Data Sources**:

View File

@@ -524,7 +524,7 @@ events: [{
## Example: Complete Todo App
See `/src/schemas/page-schemas.ts` for a full working example with:
See `/src/schemas/todo-list.json` for a full working example with:
- KV persistence
- Computed statistics
- CRUD operations
@@ -577,7 +577,9 @@ See `/src/schemas/page-schemas.ts` for a full working example with:
## Resources
- **Type Definitions**: `/src/types/json-ui.ts`
- **Page Schemas**: `/src/schemas/page-schemas.ts`
- **JSON Schemas**: `/src/schemas/*.json`
- **Compute Functions**: `/src/schemas/compute-functions.ts`
- **Schema Loader**: `/src/schemas/schema-loader.ts`
- **Custom Hooks**: `/src/hooks/data/` and `/src/hooks/ui/`
- **Atomic Components**: `/src/components/atoms/`
- **Component Registry**: `/src/lib/json-ui/component-registry.ts`

View File

@@ -1,5 +1,8 @@
import { PageRenderer } from '@/lib/json-ui/page-renderer'
import { dashboardSchema } from '@/schemas/dashboard-schema'
import { hydrateSchema } from '@/schemas/schema-loader'
import analyticsDashboardJson from '@/schemas/analytics-dashboard.json'
const dashboardSchema = hydrateSchema(analyticsDashboardJson)
export function DashboardDemoPage() {
return <PageRenderer schema={dashboardSchema} />

View File

@@ -2,7 +2,12 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { AtomicComponentDemo } from '@/components/AtomicComponentDemo'
import { DashboardDemoPage } from '@/components/DashboardDemoPage'
import { PageRenderer } from '@/lib/json-ui/page-renderer'
import { todoListSchema, newMoleculesShowcaseSchema } from '@/schemas/page-schemas'
import { hydrateSchema } from '@/schemas/schema-loader'
import todoListJson from '@/schemas/todo-list.json'
import newMoleculesShowcaseJson from '@/schemas/new-molecules-showcase.json'
const todoListSchema = hydrateSchema(todoListJson)
const newMoleculesShowcaseSchema = hydrateSchema(newMoleculesShowcaseJson)
export function JSONUIShowcasePage() {
return (

View File

@@ -0,0 +1,256 @@
{
"id": "analytics-dashboard",
"name": "Analytics Dashboard",
"layout": {
"type": "single"
},
"dataSources": [
{
"id": "users",
"type": "kv",
"key": "dashboard-users",
"defaultValue": [
{ "id": 1, "name": "Alice Johnson", "email": "alice@example.com", "status": "active", "joined": "2024-01-15" },
{ "id": 2, "name": "Bob Smith", "email": "bob@example.com", "status": "active", "joined": "2024-02-20" },
{ "id": 3, "name": "Charlie Brown", "email": "charlie@example.com", "status": "inactive", "joined": "2023-12-10" }
]
},
{
"id": "filterQuery",
"type": "static",
"defaultValue": ""
},
{
"id": "filteredUsers",
"type": "computed",
"compute": "computeFilteredUsers",
"dependencies": ["users", "filterQuery"]
},
{
"id": "stats",
"type": "computed",
"compute": "computeStats",
"dependencies": ["users"]
}
],
"components": [
{
"id": "root",
"type": "div",
"props": {
"className": "h-full overflow-auto p-6 bg-gradient-to-br from-background via-background to-accent/5"
},
"children": [
{
"id": "header",
"type": "div",
"props": { "className": "mb-8" },
"children": [
{
"id": "title",
"type": "Heading",
"props": {
"className": "text-4xl font-bold mb-2 bg-gradient-to-r from-primary to-accent bg-clip-text text-transparent",
"children": "Analytics Dashboard"
}
},
{
"id": "subtitle",
"type": "Text",
"props": {
"className": "text-muted-foreground text-lg",
"children": "Monitor your user activity and key metrics"
}
}
]
},
{
"id": "metrics-row",
"type": "div",
"props": { "className": "grid grid-cols-1 md:grid-cols-3 gap-6 mb-8" },
"children": [
{
"id": "metric-total",
"type": "Card",
"props": { "className": "bg-gradient-to-br from-primary/10 to-primary/5 border-primary/20" },
"children": [
{
"id": "metric-total-content",
"type": "CardContent",
"props": { "className": "pt-6" },
"children": [
{
"id": "metric-total-label",
"type": "div",
"props": { "className": "text-sm font-medium text-muted-foreground mb-2", "children": "Total Users" }
},
{
"id": "metric-total-value",
"type": "div",
"props": { "className": "text-4xl font-bold text-primary" },
"bindings": {
"children": { "source": "stats", "path": "total" }
}
},
{
"id": "metric-total-description",
"type": "div",
"props": { "className": "text-xs text-muted-foreground mt-2", "children": "Registered accounts" }
}
]
}
]
},
{
"id": "metric-active",
"type": "Card",
"props": { "className": "bg-gradient-to-br from-green-500/10 to-green-500/5 border-green-500/20" },
"children": [
{
"id": "metric-active-content",
"type": "CardContent",
"props": { "className": "pt-6" },
"children": [
{
"id": "metric-active-label",
"type": "div",
"props": { "className": "text-sm font-medium text-muted-foreground mb-2", "children": "Active Users" }
},
{
"id": "metric-active-value",
"type": "div",
"props": { "className": "text-4xl font-bold text-green-600" },
"bindings": {
"children": { "source": "stats", "path": "active" }
}
},
{
"id": "metric-active-description",
"type": "div",
"props": { "className": "text-xs text-muted-foreground mt-2", "children": "Currently engaged" }
}
]
}
]
},
{
"id": "metric-inactive",
"type": "Card",
"props": { "className": "bg-gradient-to-br from-orange-500/10 to-orange-500/5 border-orange-500/20" },
"children": [
{
"id": "metric-inactive-content",
"type": "CardContent",
"props": { "className": "pt-6" },
"children": [
{
"id": "metric-inactive-label",
"type": "div",
"props": { "className": "text-sm font-medium text-muted-foreground mb-2", "children": "Inactive Users" }
},
{
"id": "metric-inactive-value",
"type": "div",
"props": { "className": "text-4xl font-bold text-orange-600" },
"bindings": {
"children": { "source": "stats", "path": "inactive" }
}
},
{
"id": "metric-inactive-description",
"type": "div",
"props": { "className": "text-xs text-muted-foreground mt-2", "children": "Need re-engagement" }
}
]
}
]
}
]
},
{
"id": "users-section",
"type": "Card",
"props": { "className": "bg-card/50 backdrop-blur" },
"children": [
{
"id": "users-header",
"type": "CardHeader",
"children": [
{
"id": "users-title-row",
"type": "div",
"props": { "className": "flex items-center justify-between" },
"children": [
{
"id": "users-title",
"type": "CardTitle",
"props": { "children": "User Directory" }
},
{
"id": "users-badge",
"type": "Badge",
"props": { "variant": "secondary" },
"bindings": {
"children": {
"source": "filteredUsers",
"transform": "transformFilteredUsers"
}
}
}
]
},
{
"id": "users-description",
"type": "CardDescription",
"props": { "children": "Manage and filter your user base" }
}
]
},
{
"id": "users-content",
"type": "CardContent",
"children": [
{
"id": "filter-row",
"type": "div",
"props": { "className": "mb-6" },
"children": [
{
"id": "filter-input",
"type": "Input",
"props": { "placeholder": "Search users by name or email..." },
"events": [
{
"event": "onChange",
"actions": [
{
"id": "update-filter",
"type": "set-value",
"target": "filterQuery",
"compute": "updateFilterQuery"
}
]
}
]
}
]
},
{
"id": "users-list",
"type": "div",
"props": { "className": "space-y-4" },
"bindings": {
"children": {
"source": "filteredUsers",
"transform": "transformUserList"
}
}
}
]
}
]
}
]
}
]
}

View File

@@ -0,0 +1,88 @@
export const computeFilteredUsers = (data: any) => {
const query = (data.filterQuery || '').toLowerCase()
if (!query) return data.users || []
return (data.users || []).filter((user: any) =>
user.name.toLowerCase().includes(query) ||
user.email.toLowerCase().includes(query)
)
}
export const computeStats = (data: any) => ({
total: data.users?.length || 0,
active: data.users?.filter((u: any) => u.status === 'active').length || 0,
inactive: data.users?.filter((u: any) => u.status === 'inactive').length || 0,
})
export const computeTodoStats = (data: any) => ({
total: data.todos?.length || 0,
completed: data.todos?.filter((t: any) => t.completed).length || 0,
remaining: data.todos?.filter((t: any) => !t.completed).length || 0,
})
export const computeAddTodo = (data: any) => ({
id: Date.now(),
text: data.newTodo,
completed: false,
})
export const updateFilterQuery = (_: any, event: any) => event.target.value
export const updateNewTodo = (data: any, event: any) => event.target.value
export const checkCanAddTodo = (data: any) => data.newTodo?.trim().length > 0
export const transformFilteredUsers = (users: any[]) => `${users.length} users`
export const transformUserList = (users: any[]) => users.map((user: any) => ({
type: 'Card',
id: `user-${user.id}`,
props: {
className: 'bg-background/50 hover:bg-background/80 transition-colors border-l-4 border-l-primary',
},
children: [
{
type: 'CardContent',
id: `user-content-${user.id}`,
props: { className: 'pt-6' },
children: [
{
type: 'div',
id: `user-row-${user.id}`,
props: { className: 'flex items-start justify-between' },
children: [
{
type: 'div',
id: `user-info-${user.id}`,
props: { className: 'flex-1' },
children: [
{
type: 'div',
id: `user-name-${user.id}`,
props: { className: 'font-semibold text-lg mb-1', children: user.name },
},
{
type: 'div',
id: `user-email-${user.id}`,
props: { className: 'text-sm text-muted-foreground', children: user.email },
},
{
type: 'div',
id: `user-joined-${user.id}`,
props: { className: 'text-xs text-muted-foreground mt-2', children: `Joined ${user.joined}` },
},
],
},
{
type: 'Badge',
id: `user-status-${user.id}`,
props: {
variant: user.status === 'active' ? 'default' : 'secondary',
children: user.status,
},
},
],
},
],
},
],
}))

View File

@@ -0,0 +1,23 @@
{
"id": "dashboard",
"name": "Dashboard",
"layout": {
"type": "grid",
"columns": 2,
"gap": 4
},
"dataSources": [
{
"id": "stats",
"type": "static",
"defaultValue": {
"users": 1247,
"revenue": 45230,
"orders": 892,
"conversion": 3.2
}
}
],
"components": [],
"globalActions": []
}

View File

@@ -0,0 +1,303 @@
{
"id": "new-molecules-showcase",
"name": "New Molecules Showcase",
"layout": {
"type": "single"
},
"dataSources": [
{
"id": "itemCount",
"type": "static",
"defaultValue": 42
},
{
"id": "isLoading",
"type": "static",
"defaultValue": false
}
],
"components": [
{
"id": "root",
"type": "div",
"props": {
"className": "h-full overflow-auto p-8 bg-background"
},
"children": [
{
"id": "page-header",
"type": "div",
"props": { "className": "mb-8" },
"children": [
{
"id": "page-title",
"type": "Heading",
"props": {
"level": 1,
"className": "text-4xl font-bold mb-2",
"children": "New JSON-Compatible Molecules"
}
},
{
"id": "page-description",
"type": "Text",
"props": {
"className": "text-muted-foreground text-lg",
"children": "Showcasing the newly added molecular components"
}
}
]
},
{
"id": "showcase-grid",
"type": "Grid",
"props": { "cols": 2, "gap": "lg", "className": "max-w-5xl" },
"children": [
{
"id": "branding-card",
"type": "Card",
"children": [
{
"id": "branding-header",
"type": "CardHeader",
"children": [
{
"id": "branding-title",
"type": "CardTitle",
"props": { "children": "AppBranding" }
},
{
"id": "branding-description",
"type": "CardDescription",
"props": { "children": "Application branding with logo, title, and subtitle" }
}
]
},
{
"id": "branding-content",
"type": "CardContent",
"children": [
{
"id": "branding-demo",
"type": "AppBranding",
"props": {
"title": "My Amazing App",
"subtitle": "Built with JSON-Powered Components"
}
}
]
}
]
},
{
"id": "label-badge-card",
"type": "Card",
"children": [
{
"id": "label-badge-header",
"type": "CardHeader",
"children": [
{
"id": "label-badge-title",
"type": "CardTitle",
"props": { "children": "LabelWithBadge" }
},
{
"id": "label-badge-description",
"type": "CardDescription",
"props": { "children": "Label with optional badge indicator" }
}
]
},
{
"id": "label-badge-content",
"type": "CardContent",
"props": { "className": "space-y-3" },
"children": [
{
"id": "label-badge-demo-1",
"type": "LabelWithBadge",
"props": {
"label": "Total Items"
},
"bindings": {
"badge": { "source": "itemCount" }
}
},
{
"id": "label-badge-demo-2",
"type": "LabelWithBadge",
"props": {
"label": "Warning",
"badge": "3",
"badgeVariant": "destructive"
}
},
{
"id": "label-badge-demo-3",
"type": "LabelWithBadge",
"props": {
"label": "Success",
"badge": "New",
"badgeVariant": "default"
}
}
]
}
]
},
{
"id": "empty-state-card",
"type": "Card",
"children": [
{
"id": "empty-state-header",
"type": "CardHeader",
"children": [
{
"id": "empty-state-title",
"type": "CardTitle",
"props": { "children": "EmptyEditorState" }
},
{
"id": "empty-state-description",
"type": "CardDescription",
"props": { "children": "Empty state display for editor contexts" }
}
]
},
{
"id": "empty-state-content",
"type": "CardContent",
"props": { "className": "h-48" },
"children": [
{
"id": "empty-state-demo",
"type": "EmptyEditorState",
"props": {}
}
]
}
]
},
{
"id": "loading-states-card",
"type": "Card",
"children": [
{
"id": "loading-states-header",
"type": "CardHeader",
"children": [
{
"id": "loading-states-title",
"type": "CardTitle",
"props": { "children": "Loading States" }
},
{
"id": "loading-states-description",
"type": "CardDescription",
"props": { "children": "LoadingFallback and LoadingState components" }
}
]
},
{
"id": "loading-states-content",
"type": "CardContent",
"props": { "className": "space-y-4" },
"children": [
{
"id": "loading-fallback-wrapper",
"type": "div",
"props": { "className": "h-24 border border-border rounded-md" },
"children": [
{
"id": "loading-fallback-demo",
"type": "LoadingFallback",
"props": {
"message": "Loading your data..."
}
}
]
},
{
"id": "loading-state-demo",
"type": "LoadingState",
"props": {
"message": "Processing request...",
"size": "sm"
}
}
]
}
]
},
{
"id": "nav-header-card",
"type": "Card",
"props": { "className": "col-span-2" },
"children": [
{
"id": "nav-header-header",
"type": "CardHeader",
"children": [
{
"id": "nav-header-title",
"type": "CardTitle",
"props": { "children": "NavigationGroupHeader" }
},
{
"id": "nav-header-description",
"type": "CardDescription",
"props": { "children": "Collapsible navigation group header (Note: requires Collapsible wrapper in production)" }
}
]
},
{
"id": "nav-header-content",
"type": "CardContent",
"children": [
{
"id": "nav-header-demo",
"type": "NavigationGroupHeader",
"props": {
"label": "Components",
"count": 24,
"isExpanded": true
}
}
]
}
]
}
]
},
{
"id": "info-section",
"type": "Alert",
"props": {
"className": "max-w-5xl mt-8"
},
"children": [
{
"id": "info-title",
"type": "div",
"props": {
"className": "font-semibold mb-2",
"children": "✅ Successfully Added to JSON Registry"
}
},
{
"id": "info-text",
"type": "div",
"props": {
"className": "text-sm",
"children": "All components shown above are now available in the JSON UI component registry and can be used in JSON schemas."
}
}
]
}
]
}
],
"globalActions": []
}

View File

@@ -0,0 +1,89 @@
import { PageSchema } from '@/types/json-ui'
import * as computeFunctions from './compute-functions'
type ComputeFunctionMap = typeof computeFunctions
export function hydrateSchema(jsonSchema: any): PageSchema {
const schema = { ...jsonSchema }
if (schema.dataSources) {
schema.dataSources = schema.dataSources.map((ds: any) => {
if (ds.type === 'computed' && typeof ds.compute === 'string') {
const functionName = ds.compute as keyof ComputeFunctionMap
const computeFunction = computeFunctions[functionName]
if (!computeFunction) {
console.warn(`Compute function "${functionName}" not found`)
}
return {
...ds,
compute: computeFunction || (() => null)
}
}
return ds
})
}
if (schema.components) {
schema.components = hydrateComponents(schema.components)
}
return schema as PageSchema
}
function hydrateComponents(components: any[]): any[] {
return components.map(component => {
const hydratedComponent = { ...component }
if (component.events) {
hydratedComponent.events = component.events.map((event: any) => {
const hydratedEvent = { ...event }
if (event.condition && typeof event.condition === 'string') {
const functionName = event.condition as keyof ComputeFunctionMap
const conditionFunction = computeFunctions[functionName]
hydratedEvent.condition = conditionFunction || (() => false)
}
if (event.actions) {
hydratedEvent.actions = event.actions.map((action: any) => {
if (action.compute && typeof action.compute === 'string') {
const functionName = action.compute as keyof ComputeFunctionMap
const computeFunction = computeFunctions[functionName]
return {
...action,
compute: computeFunction || (() => null)
}
}
return action
})
}
return hydratedEvent
})
}
if (component.bindings) {
const hydratedBindings: Record<string, any> = {}
for (const [key, binding] of Object.entries(component.bindings)) {
const b = binding as any
if (b.transform && typeof b.transform === 'string') {
const functionName = b.transform as keyof ComputeFunctionMap
const transformFunction = computeFunctions[functionName]
hydratedBindings[key] = {
...b,
transform: transformFunction || ((x: any) => x)
}
} else {
hydratedBindings[key] = b
}
}
hydratedComponent.bindings = hydratedBindings
}
if (component.children) {
hydratedComponent.children = hydrateComponents(component.children)
}
return hydratedComponent
})
}

255
src/schemas/todo-list.json Normal file
View File

@@ -0,0 +1,255 @@
{
"id": "todo-list",
"name": "Todo List",
"layout": {
"type": "single"
},
"dataSources": [
{
"id": "todos",
"type": "kv",
"key": "app-todos",
"defaultValue": [
{ "id": 1, "text": "Learn JSON-driven UI", "completed": true },
{ "id": 2, "text": "Build atomic components", "completed": false },
{ "id": 3, "text": "Create custom hooks", "completed": false }
]
},
{
"id": "newTodo",
"type": "static",
"defaultValue": ""
},
{
"id": "stats",
"type": "computed",
"compute": "computeTodoStats",
"dependencies": ["todos"]
}
],
"components": [
{
"id": "root",
"type": "div",
"props": {
"className": "h-full overflow-auto p-6 bg-gradient-to-br from-background via-background to-primary/5"
},
"children": [
{
"id": "header",
"type": "div",
"props": { "className": "mb-6" },
"children": [
{
"id": "title",
"type": "Heading",
"props": {
"className": "text-4xl font-bold mb-2 bg-gradient-to-r from-primary to-accent bg-clip-text text-transparent",
"children": "Task Manager"
}
},
{
"id": "subtitle",
"type": "Text",
"props": {
"className": "text-muted-foreground",
"children": "Built entirely from JSON schema"
}
}
]
},
{
"id": "stats-row",
"type": "div",
"props": { "className": "grid grid-cols-1 md:grid-cols-3 gap-4 mb-6 max-w-3xl" },
"children": [
{
"id": "stat-total",
"type": "Card",
"props": { "className": "bg-card/50 backdrop-blur" },
"children": [
{
"id": "stat-total-content",
"type": "CardContent",
"props": { "className": "pt-6" },
"children": [
{
"id": "stat-total-label",
"type": "div",
"props": { "className": "text-sm text-muted-foreground mb-1", "children": "Total Tasks" }
},
{
"id": "stat-total-value",
"type": "div",
"props": { "className": "text-3xl font-bold" },
"bindings": {
"children": { "source": "stats", "path": "total" }
}
}
]
}
]
},
{
"id": "stat-completed",
"type": "Card",
"props": { "className": "bg-accent/10 backdrop-blur border-accent/20" },
"children": [
{
"id": "stat-completed-content",
"type": "CardContent",
"props": { "className": "pt-6" },
"children": [
{
"id": "stat-completed-label",
"type": "div",
"props": { "className": "text-sm text-muted-foreground mb-1", "children": "Completed" }
},
{
"id": "stat-completed-value",
"type": "div",
"props": { "className": "text-3xl font-bold text-accent" },
"bindings": {
"children": { "source": "stats", "path": "completed" }
}
}
]
}
]
},
{
"id": "stat-remaining",
"type": "Card",
"props": { "className": "bg-primary/5 backdrop-blur border-primary/20" },
"children": [
{
"id": "stat-remaining-content",
"type": "CardContent",
"props": { "className": "pt-6" },
"children": [
{
"id": "stat-remaining-label",
"type": "div",
"props": { "className": "text-sm text-muted-foreground mb-1", "children": "Remaining" }
},
{
"id": "stat-remaining-value",
"type": "div",
"props": { "className": "text-3xl font-bold text-primary" },
"bindings": {
"children": { "source": "stats", "path": "remaining" }
}
}
]
}
]
}
]
},
{
"id": "main-card",
"type": "Card",
"props": { "className": "max-w-3xl" },
"children": [
{
"id": "card-header",
"type": "CardHeader",
"children": [
{
"id": "card-title",
"type": "CardTitle",
"props": { "children": "Your Tasks" }
},
{
"id": "card-description",
"type": "CardDescription",
"props": { "children": "Manage your daily tasks efficiently" }
}
]
},
{
"id": "card-content",
"type": "CardContent",
"props": { "className": "space-y-4" },
"children": [
{
"id": "input-group",
"type": "div",
"props": { "className": "flex gap-2" },
"children": [
{
"id": "todo-input",
"type": "Input",
"props": {
"placeholder": "What needs to be done?"
},
"bindings": {
"value": { "source": "newTodo" }
},
"events": [
{
"event": "change",
"actions": [
{
"id": "update-input",
"type": "set-value",
"target": "newTodo",
"compute": "updateNewTodo"
}
]
}
]
},
{
"id": "add-button",
"type": "Button",
"props": { "children": "Add Task" },
"events": [
{
"event": "click",
"actions": [
{
"id": "add-todo",
"type": "create",
"target": "todos",
"compute": "computeAddTodo"
},
{
"id": "clear-input",
"type": "set-value",
"target": "newTodo",
"value": ""
},
{
"id": "show-success",
"type": "show-toast",
"message": "Task added successfully!",
"variant": "success"
}
],
"condition": "checkCanAddTodo"
}
]
}
]
},
{
"id": "separator",
"type": "Separator",
"props": { "className": "my-4" }
},
{
"id": "todo-list",
"type": "div",
"props": { "className": "space-y-2" },
"children": []
}
]
}
]
}
]
}
],
"globalActions": []
}

View File

@@ -11,6 +11,7 @@
"module": "ESNext",
"skipLibCheck": true,
"strictNullChecks": true,
"resolveJsonModule": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,