diff --git a/audit-report.json b/audit-report.json index 4c0351c..67ddd8a 100644 --- a/audit-report.json +++ b/audit-report.json @@ -1,9 +1,9 @@ { - "timestamp": "2026-01-21T05:05:25.613Z", + "timestamp": "2026-01-21T05:06:08.176Z", "issues": [], "stats": { "totalJsonFiles": 337, - "totalTsxFiles": 364, + "totalTsxFiles": 363, "registryEntries": 402, "orphanedJson": 0, "duplicates": 0, diff --git a/json-components-registry.json b/json-components-registry.json index 25d09a5..810aa52 100644 --- a/json-components-registry.json +++ b/json-components-registry.json @@ -4866,6 +4866,25 @@ "status": "supported", "source": "components", "jsonCompatible": true + }, + { + "type": "DataBindingDesigner", + "name": "Data Binding Designer", + "category": "page", + "canHaveChildren": true, + "description": "Designer for data binding configuration", + "status": "supported", + "source": "custom", + "jsonCompatible": true, + "metadata": { + "conversionDate": "2026-01-21", + "phase": "Batch C migration", + "autoGenerated": false + }, + "load": { + "path": "@/lib/json-ui/json-components", + "export": "DataBindingDesigner" + } } ], "statistics": { diff --git a/src/components/json-definitions/conflict-details-dialog.json b/src/components/json-definitions/conflict-details-dialog.json new file mode 100644 index 0000000..7e694ae --- /dev/null +++ b/src/components/json-definitions/conflict-details-dialog.json @@ -0,0 +1,669 @@ +{ + "id": "conflict-details-dialog", + "type": "Dialog", + "bindings": { + "open": "open", + "onOpenChange": "onOpenChange" + }, + "children": [ + { + "id": "conflict-details-content", + "type": "DialogContent", + "props": { + "className": "max-w-4xl max-h-[90vh] flex flex-col" + }, + "children": [ + { + "id": "conflict-details-header", + "type": "DialogHeader", + "children": [ + { + "id": "conflict-details-title", + "type": "DialogTitle", + "bindings": { + "children": "conflict.id" + }, + "props": { + "className": "font-mono text-lg" + } + }, + { + "id": "conflict-details-description", + "type": "DialogDescription", + "props": { + "className": "flex items-center gap-2" + }, + "children": [ + { + "id": "conflict-details-entity-badge", + "type": "Badge", + "bindings": { + "children": "conflict.entityType" + }, + "props": { + "variant": "outline" + } + }, + { + "id": "conflict-details-id", + "type": "span", + "bindings": { + "children": "conflict.id" + } + } + ] + } + ] + }, + { + "id": "conflict-version-summary", + "type": "div", + "props": { + "className": "grid grid-cols-2 gap-4 py-2" + }, + "children": [ + { + "id": "conflict-local-summary", + "type": "div", + "props": { + "className": "flex items-center gap-2" + }, + "children": [ + { + "id": "conflict-summary-local-icon", + "type": "Database", + "props": { + "size": 20, + "className": "text-primary" + } + }, + { + "id": "conflict-summary-local-content", + "type": "div", + "props": { + "className": "flex-1" + }, + "children": [ + { + "id": "conflict-summary-local-label", + "type": "div", + "props": { + "className": "text-sm font-medium", + "children": "Local Version" + } + }, + { + "id": "conflict-summary-local-time", + "type": "div", + "props": { + "className": "flex items-center gap-1.5 text-xs text-muted-foreground" + }, + "children": [ + { + "id": "conflict-summary-local-clock", + "type": "Clock", + "props": { + "size": 12 + } + }, + { + "id": "conflict-summary-local-timestamp", + "type": "span", + "bindings": { + "children": { + "source": "conflict.localTimestamp", + "transform": "new Date(data).toLocaleString()" + } + } + } + ] + } + ] + }, + { + "id": "conflict-summary-local-newer", + "type": "Badge", + "conditional": { + "if": "dialogState.isLocalNewer" + }, + "props": { + "variant": "secondary", + "className": "text-xs", + "children": "Newer" + } + } + ] + }, + { + "id": "conflict-remote-summary", + "type": "div", + "props": { + "className": "flex items-center gap-2" + }, + "children": [ + { + "id": "conflict-summary-remote-icon", + "type": "Cloud", + "props": { + "size": 20, + "className": "text-accent" + } + }, + { + "id": "conflict-summary-remote-content", + "type": "div", + "props": { + "className": "flex-1" + }, + "children": [ + { + "id": "conflict-summary-remote-label", + "type": "div", + "props": { + "className": "text-sm font-medium", + "children": "Remote Version" + } + }, + { + "id": "conflict-summary-remote-time", + "type": "div", + "props": { + "className": "flex items-center gap-1.5 text-xs text-muted-foreground" + }, + "children": [ + { + "id": "conflict-summary-remote-clock", + "type": "Clock", + "props": { + "size": 12 + } + }, + { + "id": "conflict-summary-remote-timestamp", + "type": "span", + "bindings": { + "children": { + "source": "conflict.remoteTimestamp", + "transform": "new Date(data).toLocaleString()" + } + } + } + ] + } + ] + }, + { + "id": "conflict-summary-remote-newer", + "type": "Badge", + "conditional": { + "if": "!dialogState.isLocalNewer" + }, + "props": { + "variant": "secondary", + "className": "text-xs", + "children": "Newer" + } + } + ] + } + ] + }, + { + "id": "conflict-separator-1", + "type": "Separator" + }, + { + "id": "conflict-tabs-container", + "type": "Tabs", + "bindings": { + "value": "dialogState.activeTab", + "onValueChange": { + "source": "dialogState.setActiveTab", + "transform": "(val) => data(val)" + } + }, + "props": { + "className": "flex-1 flex flex-col min-h-0" + }, + "children": [ + { + "id": "conflict-tabs-list", + "type": "TabsList", + "props": { + "className": "grid grid-cols-3 w-full" + }, + "children": [ + { + "id": "conflict-tab-diff", + "type": "TabsTrigger", + "bindings": { + "children": { + "source": "dialogState.conflictingKeys.length", + "transform": "`Differences (${data})`" + } + }, + "props": { + "value": "diff", + "className": "gap-2" + } + }, + { + "id": "conflict-tab-local", + "type": "TabsTrigger", + "props": { + "value": "local", + "className": "gap-2", + "children": "Local" + } + }, + { + "id": "conflict-tab-remote", + "type": "TabsTrigger", + "props": { + "value": "remote", + "className": "gap-2", + "children": "Remote" + } + } + ] + }, + { + "id": "conflict-tab-diff-content", + "type": "TabsContent", + "props": { + "value": "diff", + "className": "flex-1 min-h-0" + }, + "children": [ + { + "id": "conflict-diff-scroll", + "type": "ScrollArea", + "props": { + "className": "h-[400px] rounded-md border" + }, + "children": [ + { + "id": "conflict-diff-container", + "type": "div", + "props": { + "className": "p-4 space-y-2" + }, + "children": [ + { + "id": "conflict-diff-items", + "type": "map", + "bindings": { + "data": "dialogState.diff", + "item": "diffItem" + }, + "children": [ + { + "id": "conflict-diff-item", + "type": "div", + "bindings": { + "className": { + "source": "diffItem.isDifferent", + "transform": "data ? 'p-3 rounded-md border border-destructive/30 bg-destructive/5' : 'p-3 rounded-md border bg-muted/30'" + } + }, + "children": [ + { + "id": "conflict-diff-item-header", + "type": "div", + "props": { + "className": "flex items-center justify-between mb-2" + }, + "children": [ + { + "id": "conflict-diff-item-key", + "type": "span", + "bindings": { + "children": "diffItem.key" + }, + "props": { + "className": "font-mono text-sm font-medium" + } + }, + { + "id": "conflict-diff-item-conflict-badge", + "type": "Badge", + "conditional": { + "if": "diffItem.isDifferent" + }, + "props": { + "variant": "destructive", + "className": "text-xs", + "children": "Conflict" + } + }, + { + "id": "conflict-diff-item-match-badge", + "type": "Badge", + "conditional": { + "if": "!diffItem.isDifferent" + }, + "props": { + "variant": "secondary", + "className": "text-xs" + }, + "children": [ + { + "id": "conflict-diff-item-match-icon", + "type": "CheckCircle", + "props": { + "size": 12 + } + }, + { + "id": "conflict-diff-item-match-text", + "type": "span", + "props": { + "children": "Match" + } + } + ] + } + ] + }, + { + "id": "conflict-diff-item-content", + "type": "div", + "props": { + "className": "grid grid-cols-2 gap-3 text-xs font-mono" + }, + "children": [ + { + "id": "conflict-diff-local", + "type": "div", + "children": [ + { + "id": "conflict-diff-local-label", + "type": "div", + "props": { + "className": "text-muted-foreground mb-1", + "children": "Local" + } + }, + { + "id": "conflict-diff-local-value", + "type": "div", + "conditional": { + "if": "diffItem.onlyInLocal" + }, + "children": [ + { + "id": "conflict-diff-local-only-badge", + "type": "Badge", + "props": { + "variant": "outline", + "className": "text-xs", + "children": "Only in Local" + } + } + ] + }, + { + "id": "conflict-diff-local-json", + "type": "pre", + "conditional": { + "if": "!diffItem.onlyInLocal" + }, + "bindings": { + "children": { + "source": "diffItem.localValue", + "transform": "JSON.stringify(data, null, 2)" + } + }, + "props": { + "className": "whitespace-pre-wrap break-words" + } + } + ] + }, + { + "id": "conflict-diff-remote", + "type": "div", + "children": [ + { + "id": "conflict-diff-remote-label", + "type": "div", + "props": { + "className": "text-muted-foreground mb-1", + "children": "Remote" + } + }, + { + "id": "conflict-diff-remote-value", + "type": "div", + "conditional": { + "if": "diffItem.onlyInRemote" + }, + "children": [ + { + "id": "conflict-diff-remote-only-badge", + "type": "Badge", + "props": { + "variant": "outline", + "className": "text-xs", + "children": "Only in Remote" + } + } + ] + }, + { + "id": "conflict-diff-remote-json", + "type": "pre", + "conditional": { + "if": "!diffItem.onlyInRemote" + }, + "bindings": { + "children": { + "source": "diffItem.remoteValue", + "transform": "JSON.stringify(data, null, 2)" + } + }, + "props": { + "className": "whitespace-pre-wrap break-words" + } + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "id": "conflict-tab-local-content", + "type": "TabsContent", + "props": { + "value": "local", + "className": "flex-1 min-h-0" + }, + "children": [ + { + "id": "conflict-local-scroll", + "type": "ScrollArea", + "props": { + "className": "h-[400px] rounded-md border" + }, + "children": [ + { + "id": "conflict-local-json", + "type": "pre", + "bindings": { + "children": "dialogState.localJson" + }, + "props": { + "className": "p-4 text-xs font-mono" + } + } + ] + } + ] + }, + { + "id": "conflict-tab-remote-content", + "type": "TabsContent", + "props": { + "value": "remote", + "className": "flex-1 min-h-0" + }, + "children": [ + { + "id": "conflict-remote-scroll", + "type": "ScrollArea", + "props": { + "className": "h-[400px] rounded-md border" + }, + "children": [ + { + "id": "conflict-remote-json", + "type": "pre", + "bindings": { + "children": "dialogState.remoteJson" + }, + "props": { + "className": "p-4 text-xs font-mono" + } + } + ] + } + ] + } + ] + }, + { + "id": "conflict-separator-2", + "type": "Separator" + }, + { + "id": "conflict-resolution-footer", + "type": "div", + "props": { + "className": "flex justify-between gap-2" + }, + "children": [ + { + "id": "conflict-cancel-button", + "type": "Button", + "bindings": { + "onClick": { + "source": "onOpenChange", + "transform": "() => data(false)" + } + }, + "props": { + "variant": "outline", + "children": "Cancel" + } + }, + { + "id": "conflict-resolution-buttons", + "type": "div", + "props": { + "className": "flex gap-2" + }, + "children": [ + { + "id": "conflict-resolution-local", + "type": "Button", + "bindings": { + "onClick": { + "source": "onResolve", + "transform": "() => { data(conflict.id, 'local'); onOpenChange(false); }" + }, + "disabled": "isResolving" + }, + "props": { + "variant": "outline" + }, + "children": [ + { + "id": "conflict-resolution-local-icon", + "type": "Database", + "props": { + "size": 16 + } + }, + { + "id": "conflict-resolution-local-text", + "type": "span", + "props": { + "children": "Keep Local" + } + } + ] + }, + { + "id": "conflict-resolution-remote", + "type": "Button", + "bindings": { + "onClick": { + "source": "onResolve", + "transform": "() => { data(conflict.id, 'remote'); onOpenChange(false); }" + }, + "disabled": "isResolving" + }, + "props": { + "variant": "outline" + }, + "children": [ + { + "id": "conflict-resolution-remote-icon", + "type": "Cloud", + "props": { + "size": 16 + } + }, + { + "id": "conflict-resolution-remote-text", + "type": "span", + "props": { + "children": "Keep Remote" + } + } + ] + }, + { + "id": "conflict-resolution-merge", + "type": "Button", + "bindings": { + "onClick": { + "source": "onResolve", + "transform": "() => { data(conflict.id, 'merge'); onOpenChange(false); }" + }, + "disabled": "isResolving" + }, + "children": [ + { + "id": "conflict-resolution-merge-icon", + "type": "ArrowsLeftRight", + "props": { + "size": 16 + } + }, + { + "id": "conflict-resolution-merge-text", + "type": "span", + "props": { + "children": "Merge Both" + } + } + ] + } + ] + } + ] + } + ] + } + ] +} diff --git a/src/components/json-definitions/data-binding-designer.json b/src/components/json-definitions/data-binding-designer.json new file mode 100644 index 0000000..74abfc3 --- /dev/null +++ b/src/components/json-definitions/data-binding-designer.json @@ -0,0 +1,18 @@ +{ + "id": "data-binding-designer", + "type": "div", + "className": "h-full overflow-auto", + "children": [ + { + "id": "data-binding-content", + "type": "div", + "className": "p-6 space-y-6", + "bindings": { + "children": { + "source": "content", + "transform": "data" + } + } + } + ] +} diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 19d59f2..287c70a 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -45,5 +45,7 @@ export { useNavigationMenu } from './use-navigation-menu' export { useDataSourceManagerState } from './use-data-source-manager-state' export { useConflictResolution } from './use-conflict-resolution' export { useConflictCard } from './use-conflict-card' +export { useConflictDetailsDialog } from './use-conflict-details-dialog' export { useDocumentationView } from './use-documentation-view' export { useDockerBuildDebugger } from './use-docker-build-debugger' +export { useDataBindingDesigner } from './use-data-binding-designer' diff --git a/src/hooks/use-data-binding-designer.ts b/src/hooks/use-data-binding-designer.ts new file mode 100644 index 0000000..c2d0444 --- /dev/null +++ b/src/hooks/use-data-binding-designer.ts @@ -0,0 +1,36 @@ +import { useState } from 'react' +import { DataSource, UIComponent } from '@/types/json-ui' +import dataBindingCopy from '@/data/data-binding-designer.json' + +export function useDataBindingDesigner() { + const [dataSources, setDataSources] = useState( + dataBindingCopy.seed.dataSources as DataSource[], + ) + + const [mockComponents] = useState(dataBindingCopy.seed.components) + + const [selectedComponent, setSelectedComponent] = useState(null) + const [bindingDialogOpen, setBindingDialogOpen] = useState(false) + + const handleEditBinding = (component: UIComponent) => { + setSelectedComponent(component) + setBindingDialogOpen(true) + } + + const handleSaveBinding = (updatedComponent: UIComponent) => { + console.log('Updated component bindings:', updatedComponent) + } + + return { + dataSources, + setDataSources, + mockComponents, + selectedComponent, + setSelectedComponent, + bindingDialogOpen, + setBindingDialogOpen, + handleEditBinding, + handleSaveBinding, + dataBindingCopy, + } +} diff --git a/src/lib/json-ui/hooks-registry.ts b/src/lib/json-ui/hooks-registry.ts index 84b175b..acadda3 100644 --- a/src/lib/json-ui/hooks-registry.ts +++ b/src/lib/json-ui/hooks-registry.ts @@ -26,6 +26,7 @@ import { useConflictCard } from '@/hooks/use-conflict-card' import { useConflictDetailsDialog } from '@/hooks/use-conflict-details-dialog' import { useDocumentationView } from '@/hooks/use-documentation-view' import { useDockerBuildDebugger } from '@/hooks/use-docker-build-debugger' +import { useDataBindingDesigner } from '@/hooks/use-data-binding-designer' export interface HookRegistry { [key: string]: (...args: any[]) => any @@ -59,6 +60,7 @@ export const hooksRegistry: HookRegistry = { useConflictDetailsDialog, useDocumentationView, useDockerBuildDebugger, + useDataBindingDesigner, // Add more hooks here as needed } diff --git a/src/lib/json-ui/interfaces/data-binding-designer.ts b/src/lib/json-ui/interfaces/data-binding-designer.ts new file mode 100644 index 0000000..43d51b9 --- /dev/null +++ b/src/lib/json-ui/interfaces/data-binding-designer.ts @@ -0,0 +1,4 @@ +export interface DataBindingDesignerProps { + // Stateful component with internal state management + // No props required +} diff --git a/src/lib/json-ui/interfaces/index.ts b/src/lib/json-ui/interfaces/index.ts index 6c8ed1a..b4b4424 100644 --- a/src/lib/json-ui/interfaces/index.ts +++ b/src/lib/json-ui/interfaces/index.ts @@ -234,3 +234,4 @@ export * from './storage-settings-panel' export * from './feature-toggle-settings' export * from './documentation-view' export * from './docker-build-debugger' +export * from './data-binding-designer' diff --git a/src/lib/json-ui/json-components.ts b/src/lib/json-ui/json-components.ts index 4dbfae0..8a34171 100644 --- a/src/lib/json-ui/json-components.ts +++ b/src/lib/json-ui/json-components.ts @@ -226,6 +226,7 @@ import type { PWAUpdatePromptProps, PWAInstallPromptProps, ConflictCardProps, + ConflictDetailsDialogProps, ConflictIndicatorProps, ErrorPanelProps, PreviewDialogProps, @@ -252,6 +253,7 @@ import type { FeatureToggleSettingsProps, DocumentationViewProps, DockerBuildDebuggerProps, + DataBindingDesignerProps, } from './interfaces' // Import JSON definitions @@ -475,6 +477,7 @@ import pwaStatusBarDef from '@/components/json-definitions/pwa-status-bar.json' import pwaUpdatePromptDef from '@/components/json-definitions/pwa-update-prompt.json' import pwaInstallPromptDef from '@/components/json-definitions/pwa-install-prompt.json' import conflictCardDef from '@/components/json-definitions/conflict-card.json' +import conflictDetailsDialogDef from '@/components/json-definitions/conflict-details-dialog.json' import conflictIndicatorDef from '@/components/json-definitions/conflict-indicator.json' import errorPanelDef from '@/components/json-definitions/error-panel.json' import previewDialogDef from '@/components/json-definitions/preview-dialog.json' @@ -501,6 +504,7 @@ import storageSettingsPanelDef from '@/components/json-definitions/storage-setti import featureToggleSettingsDef from '@/components/json-definitions/feature-toggle-settings.json' import documentationViewDef from '@/components/json-definitions/documentation-view.json' import dockerBuildDebuggerDef from '@/components/json-definitions/docker-build-debugger.json' +import dataBindingDesignerDef from '@/components/json-definitions/data-binding-designer.json' // Create pure JSON components (no hooks) export const BindingIndicator = createJsonComponent(bindingIndicatorDef) @@ -958,4 +962,13 @@ export const DockerBuildDebugger = createJsonComponentWithHooks(dataBindingDesignerDef, { + hooks: { + designerState: { + hookName: 'useDataBindingDesigner', + args: () => [] + } + } +}) + // All components converted to pure JSON! 🎉 diff --git a/src/types/json-ui-component-types.ts b/src/types/json-ui-component-types.ts index a2b00e9..e14455b 100644 --- a/src/types/json-ui-component-types.ts +++ b/src/types/json-ui-component-types.ts @@ -387,6 +387,7 @@ export const jsonUIComponentTypes = [ "ComponentTreeDemoPage", "JsonFlaskDesigner", "JsonStyleDesigner", + "DataBindingDesigner", ] as const export type JSONUIComponentType = typeof jsonUIComponentTypes[number]