mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-25 22:34:56 +00:00
Grouped 100+ docs into categories: - architecture/ - System design, DBAL, component architecture - analysis/ - Status reports, assessments, migration analysis - guides/ - Quick references, how-tos, integration guides - implementation/ - Implementation details, migration guides - packages/ - Package-specific docs (forum, notifications, etc) - phases/ - Phase completion summaries and deliverables - testing/ - E2E tests, Playwright, test architecture - workflow/ - Workflow engine documentation Root level retains: README, ROADMAP, AGENTS, CONTRACT, CLAUDE, PROMPT Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
16 KiB
16 KiB
Component Migration Guide
Guide for adding declarative JSON components to existing packages.
Quick Start
Step 1: Create Component Folder
mkdir -p packages/my_package/component
Step 2: Create Metadata File
cat > packages/my_package/component/metadata.json << 'EOF'
{
"entityType": "component",
"description": "My package components",
"components": [
{ "id": "comp_my_button", "name": "My Button" },
{ "id": "comp_my_card", "name": "My Card" }
]
}
EOF
Step 3: Define Components
cat > packages/my_package/component/components.json << 'EOF'
[
{
"id": "comp_my_button",
"name": "My Button",
"category": "form",
"template": {
"type": "Button",
"props": { "variant": "{{variant}}" },
"children": "{{label}}"
},
"props": { "label": "Click", "variant": "primary" }
},
{
"id": "comp_my_card",
"name": "My Card",
"category": "layout",
"template": {
"type": "Card",
"children": { "type": "CardContent", "children": "{{content}}" }
},
"props": { "content": "Card content" }
}
]
EOF
Step 4: Bootstrap
POST http://localhost:3000/api/bootstrap
Components now available in database and can be used in pages.
Detailed Examples
Example 1: Basic Button Component
Scenario: You have a custom button style and want to make it customizable.
Before (Hardcoded):
// packages/my_package/src/components/MyButton.tsx
export function MyButton({ label }: { label: string }) {
return (
<Button variant="contained" color="primary">
{label}
</Button>
)
}
// Usage in page.tsx
return <MyButton label="Click Me" />
After (Declarative):
// packages/my_package/component/components.json
[
{
"id": "comp_my_button",
"name": "My Button",
"description": "Custom branded button",
"category": "form",
"version": "1.0.0",
"isPublished": true,
"schema": {
"type": "object",
"properties": {
"label": { "type": "string", "default": "Click" },
"variant": {
"type": "string",
"enum": ["contained", "outlined", "text"],
"default": "contained"
},
"color": {
"type": "string",
"enum": ["primary", "secondary", "error"],
"default": "primary"
},
"disabled": { "type": "boolean", "default": false }
}
},
"template": {
"type": "Button",
"props": {
"variant": "{{variant}}",
"color": "{{color}}",
"disabled": "{{disabled}}"
},
"children": "{{label}}"
},
"props": {
"label": "Click Me",
"variant": "contained",
"color": "primary",
"disabled": false
},
"events": [
{ "name": "onClick", "description": "Fired when button clicked" }
]
}
]
Usage in page:
// packages/my_package/page-config/page.json
[
{
"id": "page_example",
"path": "/example",
"title": "Example",
"component": "example_root",
"componentTree": {
"type": "Box",
"children": {
"type": "comp_my_button",
"props": {
"label": "Save Changes",
"variant": "contained",
"color": "primary"
}
}
}
}
]
Benefits:
- Customizable from admin panel
- No code changes to update UI
- Consistent styling across app
- Can be used in multiple pages
Example 2: Complex Card Component
Before (Hardcoded):
// packages/dashboard/src/components/DashboardCard.tsx
export function DashboardCard({
title,
icon,
content,
footer
}: {
title: string
icon: React.ReactNode
content: string
footer?: string
}) {
return (
<Card>
<CardHeader
avatar={<Avatar>{icon}</Avatar>}
title={title}
/>
<CardContent>
<Typography>{content}</Typography>
</CardContent>
{footer && (
<CardActions>
<Typography variant="caption">{footer}</Typography>
</CardActions>
)}
</Card>
)
}
After (Declarative):
[
{
"id": "comp_dashboard_card",
"name": "Dashboard Card",
"description": "Card for dashboard widgets",
"category": "layout",
"schema": {
"type": "object",
"properties": {
"title": { "type": "string" },
"icon": { "type": "string" },
"content": { "type": "string" },
"footer": { "type": ["string", "null"] },
"showIcon": { "type": "boolean", "default": true }
},
"required": ["title", "content"]
},
"template": {
"type": "Card",
"children": [
{
"type": "CardHeader",
"props": { "title": "{{title}}" },
"children": {
"type": "conditional",
"condition": "{{showIcon}}",
"then": {
"type": "Avatar",
"children": "{{icon}}"
}
}
},
{
"type": "CardContent",
"children": {
"type": "Typography",
"children": "{{content}}"
}
},
{
"type": "CardActions",
"children": {
"type": "conditional",
"condition": "{{footer}}",
"then": {
"type": "Typography",
"props": { "variant": "caption" },
"children": "{{footer}}"
}
}
}
]
},
"props": {
"title": "Widget",
"icon": "Star",
"content": "Widget content",
"footer": null,
"showIcon": true
}
}
]
Example 3: Form Component
Before (Hardcoded):
export function LoginForm() {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
return (
<Box sx={{ maxWidth: 400 }}>
<TextField
label="Email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
fullWidth
/>
<TextField
label="Password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
fullWidth
sx={{ mt: 2 }}
/>
<Button variant="contained" fullWidth sx={{ mt: 3 }}>
Sign In
</Button>
</Box>
)
}
After (Declarative):
[
{
"id": "comp_login_form",
"name": "Login Form",
"category": "form",
"template": {
"type": "Box",
"style": { "maxWidth": "400px", "margin": "0 auto" },
"children": [
{
"type": "FormField",
"props": { "label": "Email" },
"children": {
"type": "TextField",
"props": {
"type": "email",
"placeholder": "{{emailPlaceholder}}",
"fullWidth": true
}
}
},
{
"type": "FormField",
"props": { "label": "Password" },
"children": {
"type": "TextField",
"props": {
"type": "password",
"placeholder": "{{passwordPlaceholder}}",
"fullWidth": true
}
}
},
{
"type": "Button",
"props": {
"variant": "contained",
"fullWidth": true,
"disabled": "{{isLoading}}"
},
"children": "{{isLoading ? 'Signing in...' : 'Sign In'}}"
}
]
},
"props": {
"emailPlaceholder": "you@example.com",
"passwordPlaceholder": "••••••••",
"isLoading": false
}
}
]
Example 4: Data Table Component
Before (Hardcoded):
export function UserTable({ users }: { users: User[] }) {
return (
<TableContainer>
<Table>
<TableHead>
<TableRow>
<TableCell>Name</TableCell>
<TableCell>Email</TableCell>
<TableCell>Role</TableCell>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.id}>
<TableCell>{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell>{user.role}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)
}
After (Declarative):
[
{
"id": "comp_user_table",
"name": "User Table",
"category": "table",
"template": {
"type": "TableContainer",
"children": {
"type": "Table",
"children": [
{
"type": "TableHead",
"children": {
"type": "TableRow",
"children": [
{ "type": "TableCell", "children": "Name" },
{ "type": "TableCell", "children": "Email" },
{ "type": "TableCell", "children": "Role" }
]
}
},
{
"type": "TableBody",
"children": [
{
"type": "TableRow",
"children": [
{ "type": "TableCell", "children": "{{users[0].name}}" },
{ "type": "TableCell", "children": "{{users[0].email}}" },
{ "type": "TableCell", "children": "{{users[0].role}}" }
]
}
]
}
]
}
}
}
]
Note: Full iteration support would require template loops (future enhancement).
Example 5: Feature Grid Component
Before (Hardcoded):
export function FeatureGrid() {
const features = [
{ icon: '⚡', title: 'Fast', desc: 'Lightning quick' },
{ icon: '🔒', title: 'Secure', desc: 'Enterprise security' },
{ icon: '📈', title: 'Scalable', desc: 'Grow without limits' },
]
return (
<Grid container spacing={2}>
{features.map((feature) => (
<Grid item xs={12} md={4} key={feature.title}>
<Card>
<CardContent>
<div style={{ fontSize: '2rem' }}>{feature.icon}</div>
<Typography variant="h6">{feature.title}</Typography>
<Typography variant="body2">{feature.desc}</Typography>
</CardContent>
</Card>
</Grid>
))}
</Grid>
)
}
After (Declarative):
[
{
"id": "comp_feature_grid",
"name": "Feature Grid",
"category": "layout",
"template": {
"type": "Grid",
"props": { "container": true, "spacing": 2 },
"children": [
{
"type": "Grid",
"props": { "item": true, "xs": 12, "md": 4 },
"children": {
"type": "Card",
"children": {
"type": "CardContent",
"children": [
{
"type": "Box",
"style": { "fontSize": "2rem", "marginBottom": "1rem" },
"children": "⚡"
},
{
"type": "Typography",
"props": { "variant": "h6" },
"children": "Fast"
},
{
"type": "Typography",
"props": { "variant": "body2" },
"children": "Lightning quick"
}
]
}
}
},
{
"type": "Grid",
"props": { "item": true, "xs": 12, "md": 4 },
"children": {
"type": "Card",
"children": {
"type": "CardContent",
"children": [
{
"type": "Box",
"style": { "fontSize": "2rem", "marginBottom": "1rem" },
"children": "🔒"
},
{
"type": "Typography",
"props": { "variant": "h6" },
"children": "Secure"
},
{
"type": "Typography",
"props": { "variant": "body2" },
"children": "Enterprise security"
}
]
}
}
},
{
"type": "Grid",
"props": { "item": true, "xs": 12, "md": 4 },
"children": {
"type": "Card",
"children": {
"type": "CardContent",
"children": [
{
"type": "Box",
"style": { "fontSize": "2rem", "marginBottom": "1rem" },
"children": "📈"
},
{
"type": "Typography",
"props": { "variant": "h6" },
"children": "Scalable"
},
{
"type": "Typography",
"props": { "variant": "body2" },
"children": "Grow without limits"
}
]
}
}
}
]
}
}
]
Migration Checklist
- Create
/packages/[package]/component/folder - Create
metadata.jsonwith entity type and component list - Create
components.jsonwith all component definitions - Define schema for each component (props validation)
- Define template (component tree structure)
- Set default props
- Document events (if component emits events)
- Test with
POST /api/bootstrap - Verify components load in database:
db.components.list() - Test rendering in a page
- Update page definitions to reference new components
- Remove hardcoded components from TypeScript
- Commit changes
Common Patterns
Pattern 1: Conditional Display
{
"type": "conditional",
"condition": "{{isLoading}}",
"then": {
"type": "Spinner"
},
"else": {
"type": "Button",
"children": "Save"
}
}
Pattern 2: Styled Box
{
"type": "Box",
"style": {
"padding": "2rem",
"backgroundColor": "{{backgroundColor}}",
"borderRadius": "8px"
},
"children": "{{children}}"
}
Pattern 3: Form Field Wrapper
{
"type": "FormField",
"props": {
"label": "{{label}}",
"required": "{{required}}"
},
"children": {
"type": "TextField",
"props": {
"placeholder": "{{placeholder}}",
"value": "{{value}}"
}
}
}
Pattern 4: Card with Actions
{
"type": "Card",
"children": [
{
"type": "CardContent",
"children": "{{content}}"
},
{
"type": "CardActions",
"children": [
{
"type": "Button",
"props": { "variant": "text" },
"children": "Cancel"
},
{
"type": "Button",
"props": { "variant": "contained" },
"children": "Save"
}
]
}
]
}
Pattern 5: Responsive Grid
{
"type": "Grid",
"props": { "container": true, "spacing": 2 },
"children": [
{
"type": "Grid",
"props": {
"item": true,
"xs": 12,
"sm": 6,
"md": 4,
"lg": 3
},
"children": "{{item}}"
}
]
}
Troubleshooting
Components Not Loading After Bootstrap
-
Check metadata.json exists:
ls packages/my_package/component/metadata.json -
Check components.json exists:
ls packages/my_package/component/components.json -
Check JSON is valid:
npx json-schema-validator packages/my_package/component/components.json \ schemas/package-schemas/component.schema.json -
Check database:
npm --prefix dbal/development run db:studio # Query ComponentConfig table
Template Expressions Not Evaluating
-
Check component receives props:
{ "template": { "type": "Button", "props": { "label": "{{label}}" // Props passed to renderJSONComponent } }, "props": { "label": "Default Label" // Optional default } } -
Check expression syntax:
// ✅ CORRECT "{{props.title}}" "{{isLoading ? 'Saving...' : 'Save'}}" "{{!disabled}}" // ❌ WRONG "{title}" "{{props.title + ' extra'}}" "{{doSomething()}}"
Component Not Found in Registry
# List all available components
node -e "console.log(require('@/lib/fakemui-registry').FAKEMUI_REGISTRY)"
# Check if fakemui import works
npm --prefix frontends/nextjs run typecheck
Last Updated: 2026-01-16 Version: 1.0.0 Status: Production Ready