diff --git a/json-components-registry.json b/json-components-registry.json index 6794fef..dfd535a 100644 --- a/json-components-registry.json +++ b/json-components-registry.json @@ -2,7 +2,7 @@ "$schema": "./schemas/json-components-registry-schema.json", "version": "2.0.0", "description": "Registry of all components in the application", - "lastUpdated": "2026-01-18T12:05:00.000Z", + "lastUpdated": "2026-01-18T12:29:35.000Z", "categories": { "layout": "Layout and container components", "input": "Form inputs and interactive controls", @@ -74,9 +74,22 @@ "category": "layout", "canHaveChildren": true, "description": "ComponentBindingDialog component", - "status": "maybe-json-compatible", + "status": "supported", "source": "molecules", - "jsonCompatible": true + "jsonCompatible": false, + "wrapperRequired": true, + "wrapperComponent": "ComponentBindingDialogWrapper" + }, + { + "type": "ComponentBindingDialogWrapper", + "name": "ComponentBindingDialogWrapper", + "category": "layout", + "canHaveChildren": true, + "description": "JSON wrapper for component binding dialog with props-driven bindings", + "status": "json-compatible", + "source": "wrappers", + "jsonCompatible": true, + "wrapperFor": "ComponentBindingDialog" }, { "type": "Container", @@ -103,9 +116,22 @@ "category": "layout", "canHaveChildren": true, "description": "DataSourceEditorDialog component", - "status": "maybe-json-compatible", + "status": "supported", "source": "molecules", - "jsonCompatible": true + "jsonCompatible": false, + "wrapperRequired": true, + "wrapperComponent": "DataSourceEditorDialogWrapper" + }, + { + "type": "DataSourceEditorDialogWrapper", + "name": "DataSourceEditorDialogWrapper", + "category": "layout", + "canHaveChildren": true, + "description": "JSON wrapper for data source editor dialog with props-driven fields", + "status": "json-compatible", + "source": "wrappers", + "jsonCompatible": true, + "wrapperFor": "DataSourceEditorDialog" }, { "type": "Dialog", @@ -1080,7 +1106,20 @@ "description": "GitHubBuildStatus component", "status": "supported", "source": "molecules", - "jsonCompatible": false + "jsonCompatible": false, + "wrapperRequired": true, + "wrapperComponent": "GitHubBuildStatusWrapper" + }, + { + "type": "GitHubBuildStatusWrapper", + "name": "GitHubBuildStatusWrapper", + "category": "feedback", + "canHaveChildren": false, + "description": "JSON wrapper for props-driven GitHub build status summary", + "status": "json-compatible", + "source": "wrappers", + "jsonCompatible": true, + "wrapperFor": "GitHubBuildStatus" }, { "type": "InfoBox", @@ -1236,30 +1275,69 @@ "name": "LazyBarChart", "category": "data", "canHaveChildren": true, + "description": "Lazy-loaded Recharts bar chart with runtime library loading", + "status": "supported", + "source": "molecules", + "jsonCompatible": false, + "wrapperRequired": true, + "wrapperComponent": "LazyBarChartWrapper" + }, + { + "type": "LazyBarChartWrapper", + "name": "LazyBarChartWrapper", + "category": "data", + "canHaveChildren": true, "description": "JSON wrapper for a props-driven bar chart (no lazy hooks)", "status": "json-compatible", - "source": "molecules", - "jsonCompatible": true + "source": "wrappers", + "jsonCompatible": true, + "wrapperFor": "LazyBarChart" }, { "type": "LazyD3BarChart", "name": "LazyD3BarChart", "category": "data", "canHaveChildren": true, + "description": "Lazy-loaded D3 bar chart with runtime library loading", + "status": "supported", + "source": "molecules", + "jsonCompatible": false, + "wrapperRequired": true, + "wrapperComponent": "LazyD3BarChartWrapper" + }, + { + "type": "LazyD3BarChartWrapper", + "name": "LazyD3BarChartWrapper", + "category": "data", + "canHaveChildren": true, "description": "JSON wrapper for a simple SVG bar chart (no D3 hooks)", "status": "json-compatible", - "source": "molecules", - "jsonCompatible": true + "source": "wrappers", + "jsonCompatible": true, + "wrapperFor": "LazyD3BarChart" }, { "type": "LazyLineChart", "name": "LazyLineChart", "category": "data", "canHaveChildren": true, + "description": "Lazy-loaded Recharts line chart with runtime library loading", + "status": "supported", + "source": "molecules", + "jsonCompatible": false, + "wrapperRequired": true, + "wrapperComponent": "LazyLineChartWrapper" + }, + { + "type": "LazyLineChartWrapper", + "name": "LazyLineChartWrapper", + "category": "data", + "canHaveChildren": true, "description": "JSON wrapper for a props-driven line chart (no lazy hooks)", "status": "json-compatible", - "source": "molecules", - "jsonCompatible": true + "source": "wrappers", + "jsonCompatible": true, + "wrapperFor": "LazyLineChart" }, { "type": "List", @@ -1302,10 +1380,23 @@ "name": "SeedDataManager", "category": "data", "canHaveChildren": true, + "description": "Seed data management with app-level hook state", + "status": "supported", + "source": "molecules", + "jsonCompatible": false, + "wrapperRequired": true, + "wrapperComponent": "SeedDataManagerWrapper" + }, + { + "type": "SeedDataManagerWrapper", + "name": "SeedDataManagerWrapper", + "category": "data", + "canHaveChildren": true, "description": "JSON wrapper for seed data management with props-driven state", "status": "json-compatible", - "source": "molecules", - "jsonCompatible": true + "source": "wrappers", + "jsonCompatible": true, + "wrapperFor": "SeedDataManager" }, { "type": "StatCard", @@ -1565,9 +1656,22 @@ "category": "custom", "canHaveChildren": true, "description": "ComponentTree component", - "status": "maybe-json-compatible", + "status": "supported", "source": "molecules", - "jsonCompatible": true + "jsonCompatible": false, + "wrapperRequired": true, + "wrapperComponent": "ComponentTreeWrapper" + }, + { + "type": "ComponentTreeWrapper", + "name": "ComponentTreeWrapper", + "category": "custom", + "canHaveChildren": true, + "description": "JSON wrapper for a props-driven component tree view", + "status": "json-compatible", + "source": "wrappers", + "jsonCompatible": true, + "wrapperFor": "ComponentTree" }, { "type": "ComponentTreeNode", @@ -1809,10 +1913,23 @@ "name": "SaveIndicator", "category": "custom", "canHaveChildren": true, + "description": "Save status indicator with hook-driven state", + "status": "supported", + "source": "molecules", + "jsonCompatible": false, + "wrapperRequired": true, + "wrapperComponent": "SaveIndicatorWrapper" + }, + { + "type": "SaveIndicatorWrapper", + "name": "SaveIndicatorWrapper", + "category": "custom", + "canHaveChildren": true, "description": "JSON wrapper for save status indicator with pure-props API", "status": "json-compatible", - "source": "molecules", - "jsonCompatible": true + "source": "wrappers", + "jsonCompatible": true, + "wrapperFor": "SaveIndicator" }, { "type": "SchemaEditorCanvas", @@ -1979,10 +2096,23 @@ "name": "StorageSettings", "category": "custom", "canHaveChildren": true, + "description": "Storage settings controls with hook-driven state", + "status": "supported", + "source": "molecules", + "jsonCompatible": false, + "wrapperRequired": true, + "wrapperComponent": "StorageSettingsWrapper" + }, + { + "type": "StorageSettingsWrapper", + "name": "StorageSettingsWrapper", + "category": "custom", + "canHaveChildren": true, "description": "JSON wrapper for storage settings controls with props-driven state", "status": "json-compatible", - "source": "molecules", - "jsonCompatible": true + "source": "wrappers", + "jsonCompatible": true, + "wrapperFor": "StorageSettings" }, { "type": "Timestamp", diff --git a/src/lib/json-ui/component-registry.ts b/src/lib/json-ui/component-registry.ts index 2912d38..8b3d05c 100644 --- a/src/lib/json-ui/component-registry.ts +++ b/src/lib/json-ui/component-registry.ts @@ -21,6 +21,10 @@ import * as AtomComponents from '@/components/atoms' import * as MoleculeComponents from '@/components/molecules' import { BreadcrumbWrapper, + ComponentBindingDialogWrapper, + ComponentTreeWrapper, + DataSourceEditorDialogWrapper, + GitHubBuildStatusWrapper, LazyBarChartWrapper, LazyD3BarChartWrapper, LazyLineChartWrapper, @@ -168,12 +172,27 @@ export const moleculeComponents: UIComponentRegistry = buildRegistryFromNames( export const jsonWrapperComponents: UIComponentRegistry = { Breadcrumb: BreadcrumbWrapper, + BreadcrumbWrapper: BreadcrumbWrapper, SaveIndicator: SaveIndicatorWrapper, + SaveIndicatorWrapper: SaveIndicatorWrapper, LazyBarChart: LazyBarChartWrapper, + LazyBarChartWrapper: LazyBarChartWrapper, LazyLineChart: LazyLineChartWrapper, + LazyLineChartWrapper: LazyLineChartWrapper, LazyD3BarChart: LazyD3BarChartWrapper, + LazyD3BarChartWrapper: LazyD3BarChartWrapper, SeedDataManager: SeedDataManagerWrapper, + SeedDataManagerWrapper: SeedDataManagerWrapper, StorageSettings: StorageSettingsWrapper, + StorageSettingsWrapper: StorageSettingsWrapper, + GitHubBuildStatus: GitHubBuildStatusWrapper, + GitHubBuildStatusWrapper: GitHubBuildStatusWrapper, + ComponentBindingDialog: ComponentBindingDialogWrapper, + ComponentBindingDialogWrapper: ComponentBindingDialogWrapper, + DataSourceEditorDialog: DataSourceEditorDialogWrapper, + DataSourceEditorDialogWrapper: DataSourceEditorDialogWrapper, + ComponentTree: ComponentTreeWrapper, + ComponentTreeWrapper: ComponentTreeWrapper, } export const iconComponents: UIComponentRegistry = { diff --git a/src/lib/json-ui/wrappers/ComponentBindingDialogWrapper.tsx b/src/lib/json-ui/wrappers/ComponentBindingDialogWrapper.tsx new file mode 100644 index 0000000..d741482 --- /dev/null +++ b/src/lib/json-ui/wrappers/ComponentBindingDialogWrapper.tsx @@ -0,0 +1,75 @@ +import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog' +import { Button } from '@/components/ui/button' +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' +import { cn } from '@/lib/utils' +import type { ComponentBindingDialogWrapperProps } from './interfaces' + +export function ComponentBindingDialogWrapper({ + open = false, + title = 'Component Bindings', + description = 'Connect component props to data sources.', + componentType, + componentId, + bindings = [], + onBindingChange, + onSave, + onCancel, + onOpenChange, + className, +}: ComponentBindingDialogWrapperProps) { + return ( + + + + {title} + {description} + + + {(componentType || componentId) && ( +
+ {componentType && ( +
+ Component: + {componentType} +
+ )} + {componentId && ( +
+ ID: + {componentId} +
+ )} +
+ )} + +
+ {bindings.length === 0 ? ( +

No bindings configured.

+ ) : ( + bindings.map((binding) => ( +
+ + onBindingChange?.(binding.id, event.target.value)} + /> +
+ )) + )} +
+ + + + + +
+
+ ) +} diff --git a/src/lib/json-ui/wrappers/ComponentTreeWrapper.tsx b/src/lib/json-ui/wrappers/ComponentTreeWrapper.tsx new file mode 100644 index 0000000..36bc508 --- /dev/null +++ b/src/lib/json-ui/wrappers/ComponentTreeWrapper.tsx @@ -0,0 +1,55 @@ +import { cn } from '@/lib/utils' +import type { UIComponent } from '@/types/json-ui' +import type { ComponentTreeWrapperProps } from './interfaces' + +const renderTreeNodes = ( + components: UIComponent[], + depth: number, + selectedId: string | null, + onSelect?: (id: string) => void +) => { + return components.map((component) => { + const hasChildren = Array.isArray(component.children) && component.children.length > 0 + const isSelected = selectedId === component.id + + return ( +
+ + {hasChildren && ( +
+ {renderTreeNodes(component.children as UIComponent[], depth + 1, selectedId, onSelect)} +
+ )} +
+ ) + }) +} + +export function ComponentTreeWrapper({ + components = [], + selectedId = null, + emptyMessage = 'No components available.', + onSelect, + className, +}: ComponentTreeWrapperProps) { + return ( +
+ {components.length === 0 ? ( +

{emptyMessage}

+ ) : ( +
{renderTreeNodes(components, 0, selectedId, onSelect)}
+ )} +
+ ) +} diff --git a/src/lib/json-ui/wrappers/DataSourceEditorDialogWrapper.tsx b/src/lib/json-ui/wrappers/DataSourceEditorDialogWrapper.tsx new file mode 100644 index 0000000..eedd4bb --- /dev/null +++ b/src/lib/json-ui/wrappers/DataSourceEditorDialogWrapper.tsx @@ -0,0 +1,59 @@ +import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog' +import { Button } from '@/components/ui/button' +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' +import { cn } from '@/lib/utils' +import type { DataSourceEditorDialogWrapperProps } from './interfaces' + +export function DataSourceEditorDialogWrapper({ + open = false, + title = 'Data Source', + description = 'Update data source details and fields.', + fields = [], + onFieldChange, + onSave, + onCancel, + onOpenChange, + className, +}: DataSourceEditorDialogWrapperProps) { + return ( + + + + {title} + {description} + + +
+ {fields.length === 0 ? ( +

No fields configured.

+ ) : ( + fields.map((field) => ( +
+ + onFieldChange?.(field.id, event.target.value)} + /> + {field.helperText && ( +

{field.helperText}

+ )} +
+ )) + )} +
+ + + + + +
+
+ ) +} diff --git a/src/lib/json-ui/wrappers/GitHubBuildStatusWrapper.tsx b/src/lib/json-ui/wrappers/GitHubBuildStatusWrapper.tsx new file mode 100644 index 0000000..5343004 --- /dev/null +++ b/src/lib/json-ui/wrappers/GitHubBuildStatusWrapper.tsx @@ -0,0 +1,127 @@ +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' +import { Badge } from '@/components/ui/badge' +import { Button } from '@/components/ui/button' +import { cn } from '@/lib/utils' +import { + ArrowSquareOut, + CheckCircle, + Clock, + WarningCircle, + XCircle, +} from '@phosphor-icons/react' +import type { GitHubBuildStatusWrapperProps, GitHubBuildStatusWorkflowItem } from './interfaces' + +const getStatusBadge = (workflow: GitHubBuildStatusWorkflowItem) => { + if (workflow.status === 'completed') { + if (workflow.conclusion === 'success') { + return Success + } + if (workflow.conclusion === 'failure') { + return Failed + } + if (workflow.conclusion === 'cancelled') { + return Cancelled + } + } + + return ( + + Running + + ) +} + +const getStatusIcon = (workflow: GitHubBuildStatusWorkflowItem) => { + if (workflow.status === 'completed') { + if (workflow.conclusion === 'success') { + return + } + if (workflow.conclusion === 'failure') { + return + } + if (workflow.conclusion === 'cancelled') { + return + } + } + + return +} + +export function GitHubBuildStatusWrapper({ + title = 'GitHub Build Status', + description = 'Latest workflow runs and status badges.', + workflows = [], + isLoading = false, + errorMessage, + emptyMessage = 'No workflows to display yet.', + footerLinkLabel = 'View on GitHub', + footerLinkUrl, + className, +}: GitHubBuildStatusWrapperProps) { + return ( + + + + + {title} + + {description} + + + {isLoading &&

Loading workflows…

} + + {!isLoading && errorMessage && ( +
+ + {errorMessage} +
+ )} + + {!isLoading && !errorMessage && workflows.length === 0 && ( +

{emptyMessage}

+ )} + + {!isLoading && !errorMessage && workflows.length > 0 && ( +
+ {workflows.map((workflow) => ( +
+
+ {getStatusIcon(workflow)} +
+
+

{workflow.name}

+ {getStatusBadge(workflow)} +
+
+ {[workflow.branch, workflow.updatedAt, workflow.event] + .filter(Boolean) + .join(' • ')} +
+
+
+ {workflow.url && ( + + )} +
+ ))} +
+ )} + + {footerLinkUrl && ( + + )} +
+
+ ) +} diff --git a/src/lib/json-ui/wrappers/index.ts b/src/lib/json-ui/wrappers/index.ts index 3e06399..b0273a2 100644 --- a/src/lib/json-ui/wrappers/index.ts +++ b/src/lib/json-ui/wrappers/index.ts @@ -5,3 +5,7 @@ export { LazyLineChartWrapper } from './LazyLineChartWrapper' export { LazyD3BarChartWrapper } from './LazyD3BarChartWrapper' export { SeedDataManagerWrapper } from './SeedDataManagerWrapper' export { StorageSettingsWrapper } from './StorageSettingsWrapper' +export { GitHubBuildStatusWrapper } from './GitHubBuildStatusWrapper' +export { ComponentBindingDialogWrapper } from './ComponentBindingDialogWrapper' +export { DataSourceEditorDialogWrapper } from './DataSourceEditorDialogWrapper' +export { ComponentTreeWrapper } from './ComponentTreeWrapper' diff --git a/src/lib/json-ui/wrappers/interfaces.ts b/src/lib/json-ui/wrappers/interfaces.ts index 3096b76..f7914c9 100644 --- a/src/lib/json-ui/wrappers/interfaces.ts +++ b/src/lib/json-ui/wrappers/interfaces.ts @@ -1,4 +1,5 @@ import type { StorageBackendKey } from '@/components/storage/storageSettingsConfig' +import type { UIComponent } from '@/types/json-ui' export interface BreadcrumbItem { label: string @@ -89,3 +90,75 @@ export interface StorageSettingsWrapperProps { onExport?: () => void onImport?: () => void } + +export interface GitHubBuildStatusWorkflowItem { + id: string + name: string + status?: string + conclusion?: string | null + branch?: string + updatedAt?: string + event?: string + url?: string +} + +export interface GitHubBuildStatusWrapperProps { + title?: string + description?: string + workflows?: GitHubBuildStatusWorkflowItem[] + isLoading?: boolean + errorMessage?: string + emptyMessage?: string + footerLinkLabel?: string + footerLinkUrl?: string + className?: string +} + +export interface ComponentBindingField { + id: string + label: string + value?: string + placeholder?: string +} + +export interface ComponentBindingDialogWrapperProps { + open?: boolean + title?: string + description?: string + componentType?: string + componentId?: string + bindings?: ComponentBindingField[] + onBindingChange?: (id: string, value: string) => void + onSave?: () => void + onCancel?: () => void + onOpenChange?: (open: boolean) => void + className?: string +} + +export interface DataSourceField { + id: string + label: string + value?: string + placeholder?: string + helperText?: string +} + +export interface DataSourceEditorDialogWrapperProps { + open?: boolean + title?: string + description?: string + fields?: DataSourceField[] + onFieldChange?: (id: string, value: string) => void + onSave?: () => void + onCancel?: () => void + onOpenChange?: (open: boolean) => void + className?: string +} + +export interface ComponentTreeWrapperProps { + components?: UIComponent[] + selectedId?: string | null + emptyMessage?: string + onSelect?: (id: string) => void + className?: string +}