mirror of
https://github.com/johndoe6345789/low-code-react-app-b.git
synced 2026-04-25 06:04:54 +00:00
Compare commits
1 Commits
copilot/re
...
codex/crea
| Author | SHA1 | Date | |
|---|---|---|---|
| 5a989b38db |
@@ -847,7 +847,7 @@
|
|||||||
"canHaveChildren": false,
|
"canHaveChildren": false,
|
||||||
"description": "Navigation breadcrumb trail",
|
"description": "Navigation breadcrumb trail",
|
||||||
"status": "json-compatible",
|
"status": "json-compatible",
|
||||||
"source": "molecules",
|
"source": "json-ui-wrappers",
|
||||||
"jsonCompatible": true
|
"jsonCompatible": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1257,7 +1257,7 @@
|
|||||||
"canHaveChildren": true,
|
"canHaveChildren": true,
|
||||||
"description": "LazyBarChart component",
|
"description": "LazyBarChart component",
|
||||||
"status": "json-compatible",
|
"status": "json-compatible",
|
||||||
"source": "molecules",
|
"source": "json-ui-wrappers",
|
||||||
"jsonCompatible": true
|
"jsonCompatible": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1267,7 +1267,7 @@
|
|||||||
"canHaveChildren": true,
|
"canHaveChildren": true,
|
||||||
"description": "LazyD3BarChart component",
|
"description": "LazyD3BarChart component",
|
||||||
"status": "json-compatible",
|
"status": "json-compatible",
|
||||||
"source": "molecules",
|
"source": "json-ui-wrappers",
|
||||||
"jsonCompatible": true
|
"jsonCompatible": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1277,7 +1277,7 @@
|
|||||||
"canHaveChildren": true,
|
"canHaveChildren": true,
|
||||||
"description": "LazyLineChart component",
|
"description": "LazyLineChart component",
|
||||||
"status": "json-compatible",
|
"status": "json-compatible",
|
||||||
"source": "molecules",
|
"source": "json-ui-wrappers",
|
||||||
"jsonCompatible": true
|
"jsonCompatible": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1319,11 +1319,11 @@
|
|||||||
{
|
{
|
||||||
"type": "SeedDataManager",
|
"type": "SeedDataManager",
|
||||||
"name": "SeedDataManager",
|
"name": "SeedDataManager",
|
||||||
"category": "data",
|
"category": "custom",
|
||||||
"canHaveChildren": true,
|
"canHaveChildren": true,
|
||||||
"description": "SeedDataManager component",
|
"description": "SeedDataManager component",
|
||||||
"status": "json-compatible",
|
"status": "json-compatible",
|
||||||
"source": "molecules",
|
"source": "json-ui-wrappers",
|
||||||
"jsonCompatible": true
|
"jsonCompatible": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1826,11 +1826,11 @@
|
|||||||
{
|
{
|
||||||
"type": "SaveIndicator",
|
"type": "SaveIndicator",
|
||||||
"name": "SaveIndicator",
|
"name": "SaveIndicator",
|
||||||
"category": "custom",
|
"category": "feedback",
|
||||||
"canHaveChildren": true,
|
"canHaveChildren": true,
|
||||||
"description": "SaveIndicator component",
|
"description": "SaveIndicator component",
|
||||||
"status": "json-compatible",
|
"status": "json-compatible",
|
||||||
"source": "molecules",
|
"source": "json-ui-wrappers",
|
||||||
"jsonCompatible": true
|
"jsonCompatible": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2000,7 +2000,7 @@
|
|||||||
"canHaveChildren": true,
|
"canHaveChildren": true,
|
||||||
"description": "StorageSettings component",
|
"description": "StorageSettings component",
|
||||||
"status": "json-compatible",
|
"status": "json-compatible",
|
||||||
"source": "molecules",
|
"source": "json-ui-wrappers",
|
||||||
"jsonCompatible": true
|
"jsonCompatible": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -224,6 +224,19 @@ export const componentDefinitions: ComponentDefinition[] = [
|
|||||||
canHaveChildren: true,
|
canHaveChildren: true,
|
||||||
defaultProps: { href: '#', children: 'Link' }
|
defaultProps: { href: '#', children: 'Link' }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'Breadcrumb',
|
||||||
|
label: 'Breadcrumb',
|
||||||
|
category: 'navigation',
|
||||||
|
icon: 'Path',
|
||||||
|
defaultProps: {
|
||||||
|
items: [
|
||||||
|
{ label: 'Home', href: '/' },
|
||||||
|
{ label: 'Section', href: '/section' },
|
||||||
|
{ label: 'Current Page' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
// Feedback Components
|
// Feedback Components
|
||||||
{
|
{
|
||||||
type: 'Alert',
|
type: 'Alert',
|
||||||
@@ -256,6 +269,13 @@ export const componentDefinitions: ComponentDefinition[] = [
|
|||||||
icon: 'Circle',
|
icon: 'Circle',
|
||||||
defaultProps: { status: 'active', children: 'Active' }
|
defaultProps: { status: 'active', children: 'Active' }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'SaveIndicator',
|
||||||
|
label: 'Save Indicator',
|
||||||
|
category: 'feedback',
|
||||||
|
icon: 'FloppyDisk',
|
||||||
|
defaultProps: { status: 'saved', label: 'Saved' }
|
||||||
|
},
|
||||||
// Data Components
|
// Data Components
|
||||||
{
|
{
|
||||||
type: 'List',
|
type: 'List',
|
||||||
@@ -285,6 +305,46 @@ export const componentDefinitions: ComponentDefinition[] = [
|
|||||||
icon: 'ChartBar',
|
icon: 'ChartBar',
|
||||||
defaultProps: { title: 'Metric', value: '0' }
|
defaultProps: { title: 'Metric', value: '0' }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'LazyBarChart',
|
||||||
|
label: 'Bar Chart',
|
||||||
|
category: 'data',
|
||||||
|
icon: 'ChartBar',
|
||||||
|
defaultProps: {
|
||||||
|
data: [
|
||||||
|
{ label: 'Jan', value: 30 },
|
||||||
|
{ label: 'Feb', value: 45 },
|
||||||
|
],
|
||||||
|
xKey: 'label',
|
||||||
|
yKey: 'value',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'LazyLineChart',
|
||||||
|
label: 'Line Chart',
|
||||||
|
category: 'data',
|
||||||
|
icon: 'ChartLine',
|
||||||
|
defaultProps: {
|
||||||
|
data: [
|
||||||
|
{ label: 'Jan', value: 10 },
|
||||||
|
{ label: 'Feb', value: 25 },
|
||||||
|
],
|
||||||
|
xKey: 'label',
|
||||||
|
yKey: 'value',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'LazyD3BarChart',
|
||||||
|
label: 'D3 Bar Chart',
|
||||||
|
category: 'data',
|
||||||
|
icon: 'ChartBar',
|
||||||
|
defaultProps: {
|
||||||
|
data: [
|
||||||
|
{ label: 'A', value: 12 },
|
||||||
|
{ label: 'B', value: 18 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
// Custom Components
|
// Custom Components
|
||||||
{
|
{
|
||||||
type: 'DataCard',
|
type: 'DataCard',
|
||||||
@@ -308,6 +368,23 @@ export const componentDefinitions: ComponentDefinition[] = [
|
|||||||
canHaveChildren: true,
|
canHaveChildren: true,
|
||||||
defaultProps: { actions: [] }
|
defaultProps: { actions: [] }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'SeedDataManager',
|
||||||
|
label: 'Seed Data Manager',
|
||||||
|
category: 'custom',
|
||||||
|
icon: 'Database',
|
||||||
|
defaultProps: { isLoaded: false, isLoading: false }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'StorageSettings',
|
||||||
|
label: 'Storage Settings',
|
||||||
|
category: 'custom',
|
||||||
|
icon: 'Gear',
|
||||||
|
defaultProps: {
|
||||||
|
backend: 'indexeddb',
|
||||||
|
flaskUrl: 'http://localhost:5001',
|
||||||
|
},
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
export function getCategoryComponents(category: string): ComponentDefinition[] {
|
export function getCategoryComponents(category: string): ComponentDefinition[] {
|
||||||
|
|||||||
@@ -19,6 +19,13 @@ import { Progress } from '@/components/ui/progress'
|
|||||||
import { Avatar as ShadcnAvatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
import { Avatar as ShadcnAvatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
||||||
import * as AtomComponents from '@/components/atoms'
|
import * as AtomComponents from '@/components/atoms'
|
||||||
import * as MoleculeComponents from '@/components/molecules'
|
import * as MoleculeComponents from '@/components/molecules'
|
||||||
|
import { Breadcrumb } from './wrappers/Breadcrumb'
|
||||||
|
import { SaveIndicator } from './wrappers/SaveIndicator'
|
||||||
|
import { LazyBarChart } from './wrappers/LazyBarChart'
|
||||||
|
import { LazyLineChart } from './wrappers/LazyLineChart'
|
||||||
|
import { LazyD3BarChart } from './wrappers/LazyD3BarChart'
|
||||||
|
import { SeedDataManager } from './wrappers/SeedDataManager'
|
||||||
|
import { StorageSettings } from './wrappers/StorageSettings'
|
||||||
import jsonComponentsRegistry from '../../../json-components-registry.json'
|
import jsonComponentsRegistry from '../../../json-components-registry.json'
|
||||||
import {
|
import {
|
||||||
ArrowLeft, ArrowRight, Check, X, Plus, Minus, MagnifyingGlass,
|
ArrowLeft, ArrowRight, Check, X, Plus, Minus, MagnifyingGlass,
|
||||||
@@ -68,6 +75,10 @@ const moleculeRegistryNames = jsonRegistryEntries
|
|||||||
.filter((entry) => entry.source === 'molecules')
|
.filter((entry) => entry.source === 'molecules')
|
||||||
.map((entry) => entry.export ?? entry.name ?? entry.type)
|
.map((entry) => entry.export ?? entry.name ?? entry.type)
|
||||||
.filter((name): name is string => Boolean(name))
|
.filter((name): name is string => Boolean(name))
|
||||||
|
const wrapperRegistryNames = jsonRegistryEntries
|
||||||
|
.filter((entry) => entry.source === 'json-ui-wrappers')
|
||||||
|
.map((entry) => entry.export ?? entry.name ?? entry.type)
|
||||||
|
.filter((name): name is string => Boolean(name))
|
||||||
|
|
||||||
export const primitiveComponents: UIComponentRegistry = {
|
export const primitiveComponents: UIComponentRegistry = {
|
||||||
div: 'div' as any,
|
div: 'div' as any,
|
||||||
@@ -146,6 +157,19 @@ export const moleculeComponents: UIComponentRegistry = buildRegistryFromNames(
|
|||||||
MoleculeComponents as Record<string, ComponentType<any>>
|
MoleculeComponents as Record<string, ComponentType<any>>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
export const wrapperComponents: UIComponentRegistry = buildRegistryFromNames(
|
||||||
|
wrapperRegistryNames,
|
||||||
|
{
|
||||||
|
Breadcrumb,
|
||||||
|
SaveIndicator,
|
||||||
|
LazyBarChart,
|
||||||
|
LazyLineChart,
|
||||||
|
LazyD3BarChart,
|
||||||
|
SeedDataManager,
|
||||||
|
StorageSettings,
|
||||||
|
} as Record<string, ComponentType<any>>
|
||||||
|
)
|
||||||
|
|
||||||
export const iconComponents: UIComponentRegistry = {
|
export const iconComponents: UIComponentRegistry = {
|
||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
ArrowRight,
|
ArrowRight,
|
||||||
@@ -192,6 +216,7 @@ export const uiComponentRegistry: UIComponentRegistry = {
|
|||||||
...shadcnComponents,
|
...shadcnComponents,
|
||||||
...atomComponents,
|
...atomComponents,
|
||||||
...moleculeComponents,
|
...moleculeComponents,
|
||||||
|
...wrapperComponents,
|
||||||
...iconComponents,
|
...iconComponents,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -147,6 +147,9 @@ export function JSONUIRenderer({
|
|||||||
|
|
||||||
if (component.bindings) {
|
if (component.bindings) {
|
||||||
Object.entries(component.bindings).forEach(([propName, binding]) => {
|
Object.entries(component.bindings).forEach(([propName, binding]) => {
|
||||||
|
if (propName === 'children') {
|
||||||
|
return
|
||||||
|
}
|
||||||
props[propName] = resolveDataBinding(binding, dataMap, renderContext)
|
props[propName] = resolveDataBinding(binding, dataMap, renderContext)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -200,14 +203,17 @@ export function JSONUIRenderer({
|
|||||||
|
|
||||||
const props = resolveProps(renderContext)
|
const props = resolveProps(renderContext)
|
||||||
applyEventHandlers(props, renderContext)
|
applyEventHandlers(props, renderContext)
|
||||||
|
const boundChildren = component.bindings?.children
|
||||||
|
? resolveDataBinding(component.bindings.children, dataMap, renderContext)
|
||||||
|
: component.children
|
||||||
|
|
||||||
if (typeof Component === 'string') {
|
if (typeof Component === 'string') {
|
||||||
return React.createElement(Component, props, renderChildren(component.children, renderContext))
|
return React.createElement(Component, props, renderChildren(boundChildren, renderContext))
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Component {...props}>
|
<Component {...props}>
|
||||||
{renderChildren(component.children, renderContext)}
|
{renderChildren(boundChildren, renderContext)}
|
||||||
</Component>
|
</Component>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -231,7 +237,10 @@ export function JSONUIRenderer({
|
|||||||
...(component.loop!.indexVar ? { [component.loop!.indexVar]: index } : {}),
|
...(component.loop!.indexVar ? { [component.loop!.indexVar]: index } : {}),
|
||||||
}
|
}
|
||||||
|
|
||||||
let content = renderChildren(component.children, loopContext)
|
const loopChildrenBinding = component.bindings?.children
|
||||||
|
? resolveDataBinding(component.bindings.children, dataMap, loopContext)
|
||||||
|
: component.children
|
||||||
|
let content = renderChildren(loopChildrenBinding, loopContext)
|
||||||
|
|
||||||
if (component.conditional) {
|
if (component.conditional) {
|
||||||
const conditionMet = evaluateCondition(component.conditional.if, { ...dataMap, ...loopContext })
|
const conditionMet = evaluateCondition(component.conditional.if, { ...dataMap, ...loopContext })
|
||||||
|
|||||||
29
src/lib/json-ui/wrappers/Breadcrumb.tsx
Normal file
29
src/lib/json-ui/wrappers/Breadcrumb.tsx
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { JSONUIRenderer } from '@/lib/json-ui'
|
||||||
|
import type { UIComponent } from '@/lib/json-ui/types'
|
||||||
|
import breadcrumbDefinition from './definitions/breadcrumb.json'
|
||||||
|
|
||||||
|
export interface BreadcrumbItem {
|
||||||
|
label: string
|
||||||
|
href?: string
|
||||||
|
onClick?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BreadcrumbProps {
|
||||||
|
items: BreadcrumbItem[]
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const breadcrumbComponent = breadcrumbDefinition as UIComponent
|
||||||
|
|
||||||
|
export function Breadcrumb({ items, className }: BreadcrumbProps) {
|
||||||
|
if (!items?.length) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<JSONUIRenderer
|
||||||
|
component={breadcrumbComponent}
|
||||||
|
dataMap={{ items, className }}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
49
src/lib/json-ui/wrappers/LazyBarChart.tsx
Normal file
49
src/lib/json-ui/wrappers/LazyBarChart.tsx
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import {
|
||||||
|
Bar,
|
||||||
|
BarChart,
|
||||||
|
CartesianGrid,
|
||||||
|
Legend,
|
||||||
|
ResponsiveContainer,
|
||||||
|
Tooltip,
|
||||||
|
XAxis,
|
||||||
|
YAxis,
|
||||||
|
} from 'recharts'
|
||||||
|
|
||||||
|
export interface LazyBarChartProps {
|
||||||
|
data: Array<Record<string, any>>
|
||||||
|
xKey: string
|
||||||
|
yKey: string
|
||||||
|
width?: number | string
|
||||||
|
height?: number
|
||||||
|
color?: string
|
||||||
|
showLegend?: boolean
|
||||||
|
showGrid?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LazyBarChart({
|
||||||
|
data,
|
||||||
|
xKey,
|
||||||
|
yKey,
|
||||||
|
width = 600,
|
||||||
|
height = 300,
|
||||||
|
color = '#8884d8',
|
||||||
|
showLegend = true,
|
||||||
|
showGrid = true,
|
||||||
|
}: LazyBarChartProps) {
|
||||||
|
if (!data?.length) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ResponsiveContainer width={width} height={height}>
|
||||||
|
<BarChart data={data}>
|
||||||
|
{showGrid && <CartesianGrid strokeDasharray="3 3" />}
|
||||||
|
<XAxis dataKey={xKey} />
|
||||||
|
<YAxis />
|
||||||
|
<Tooltip />
|
||||||
|
{showLegend && <Legend />}
|
||||||
|
<Bar dataKey={yKey} fill={color} />
|
||||||
|
</BarChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
96
src/lib/json-ui/wrappers/LazyD3BarChart.tsx
Normal file
96
src/lib/json-ui/wrappers/LazyD3BarChart.tsx
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import { max, scaleBand, scaleLinear } from 'd3'
|
||||||
|
|
||||||
|
export interface LazyD3BarChartProps {
|
||||||
|
data: Array<{ label: string; value: number }>
|
||||||
|
width?: number
|
||||||
|
height?: number
|
||||||
|
color?: string
|
||||||
|
showAxes?: boolean
|
||||||
|
showGrid?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LazyD3BarChart({
|
||||||
|
data,
|
||||||
|
width = 600,
|
||||||
|
height = 300,
|
||||||
|
color = '#8884d8',
|
||||||
|
showAxes = true,
|
||||||
|
showGrid = true,
|
||||||
|
}: LazyD3BarChartProps) {
|
||||||
|
if (!data?.length) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const margin = { top: 20, right: 20, bottom: 30, left: 40 }
|
||||||
|
const chartWidth = Math.max(width - margin.left - margin.right, 0)
|
||||||
|
const chartHeight = Math.max(height - margin.top - margin.bottom, 0)
|
||||||
|
|
||||||
|
const maxValue = max(data, (d) => d.value) ?? 0
|
||||||
|
const xScale = scaleBand()
|
||||||
|
.domain(data.map((d) => d.label))
|
||||||
|
.range([0, chartWidth])
|
||||||
|
.padding(0.1)
|
||||||
|
|
||||||
|
const yScale = scaleLinear()
|
||||||
|
.domain([0, maxValue])
|
||||||
|
.nice()
|
||||||
|
.range([chartHeight, 0])
|
||||||
|
|
||||||
|
const yTicks = yScale.ticks(4)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<svg width={width} height={height}>
|
||||||
|
<g transform={`translate(${margin.left},${margin.top})`}>
|
||||||
|
{showGrid &&
|
||||||
|
yTicks.map((tick) => (
|
||||||
|
<line
|
||||||
|
key={`grid-${tick}`}
|
||||||
|
x1={0}
|
||||||
|
x2={chartWidth}
|
||||||
|
y1={yScale(tick)}
|
||||||
|
y2={yScale(tick)}
|
||||||
|
stroke="currentColor"
|
||||||
|
opacity={0.1}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{data.map((entry) => (
|
||||||
|
<rect
|
||||||
|
key={entry.label}
|
||||||
|
x={xScale(entry.label) ?? 0}
|
||||||
|
y={yScale(entry.value)}
|
||||||
|
width={xScale.bandwidth()}
|
||||||
|
height={chartHeight - yScale(entry.value)}
|
||||||
|
fill={color}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{showAxes && (
|
||||||
|
<>
|
||||||
|
<line x1={0} x2={chartWidth} y1={chartHeight} y2={chartHeight} stroke="currentColor" />
|
||||||
|
<line x1={0} x2={0} y1={0} y2={chartHeight} stroke="currentColor" />
|
||||||
|
{yTicks.map((tick) => (
|
||||||
|
<g key={`tick-${tick}`} transform={`translate(0,${yScale(tick)})`}>
|
||||||
|
<line x1={-4} x2={0} y1={0} y2={0} stroke="currentColor" />
|
||||||
|
<text x={-8} y={4} textAnchor="end" className="text-[10px] fill-muted-foreground">
|
||||||
|
{tick}
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
))}
|
||||||
|
{data.map((entry) => (
|
||||||
|
<text
|
||||||
|
key={`label-${entry.label}`}
|
||||||
|
x={(xScale(entry.label) ?? 0) + xScale.bandwidth() / 2}
|
||||||
|
y={chartHeight + 16}
|
||||||
|
textAnchor="middle"
|
||||||
|
className="text-[10px] fill-muted-foreground"
|
||||||
|
>
|
||||||
|
{entry.label}
|
||||||
|
</text>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
49
src/lib/json-ui/wrappers/LazyLineChart.tsx
Normal file
49
src/lib/json-ui/wrappers/LazyLineChart.tsx
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import {
|
||||||
|
CartesianGrid,
|
||||||
|
Legend,
|
||||||
|
Line,
|
||||||
|
LineChart,
|
||||||
|
ResponsiveContainer,
|
||||||
|
Tooltip,
|
||||||
|
XAxis,
|
||||||
|
YAxis,
|
||||||
|
} from 'recharts'
|
||||||
|
|
||||||
|
export interface LazyLineChartProps {
|
||||||
|
data: Array<Record<string, any>>
|
||||||
|
xKey: string
|
||||||
|
yKey: string
|
||||||
|
width?: number | string
|
||||||
|
height?: number
|
||||||
|
color?: string
|
||||||
|
showLegend?: boolean
|
||||||
|
showGrid?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LazyLineChart({
|
||||||
|
data,
|
||||||
|
xKey,
|
||||||
|
yKey,
|
||||||
|
width = 600,
|
||||||
|
height = 300,
|
||||||
|
color = '#8884d8',
|
||||||
|
showLegend = true,
|
||||||
|
showGrid = true,
|
||||||
|
}: LazyLineChartProps) {
|
||||||
|
if (!data?.length) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ResponsiveContainer width={width} height={height}>
|
||||||
|
<LineChart data={data}>
|
||||||
|
{showGrid && <CartesianGrid strokeDasharray="3 3" />}
|
||||||
|
<XAxis dataKey={xKey} />
|
||||||
|
<YAxis />
|
||||||
|
<Tooltip />
|
||||||
|
{showLegend && <Legend />}
|
||||||
|
<Line type="monotone" dataKey={yKey} stroke={color} />
|
||||||
|
</LineChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
41
src/lib/json-ui/wrappers/SaveIndicator.tsx
Normal file
41
src/lib/json-ui/wrappers/SaveIndicator.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { JSONUIRenderer } from '@/lib/json-ui'
|
||||||
|
import type { UIComponent } from '@/lib/json-ui/types'
|
||||||
|
import saveIndicatorDefinition from './definitions/save-indicator.json'
|
||||||
|
|
||||||
|
export interface SaveIndicatorProps {
|
||||||
|
status?: 'saved' | 'synced'
|
||||||
|
label?: string
|
||||||
|
showLabel?: boolean
|
||||||
|
animate?: boolean
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveIndicatorComponent = saveIndicatorDefinition as UIComponent
|
||||||
|
|
||||||
|
export function SaveIndicator({
|
||||||
|
status = 'saved',
|
||||||
|
label,
|
||||||
|
showLabel = true,
|
||||||
|
animate,
|
||||||
|
className,
|
||||||
|
}: SaveIndicatorProps) {
|
||||||
|
if (!status) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolvedLabel = label ?? (status === 'saved' ? 'Saved' : 'Synced')
|
||||||
|
const shouldAnimate = animate ?? status === 'saved'
|
||||||
|
|
||||||
|
return (
|
||||||
|
<JSONUIRenderer
|
||||||
|
component={saveIndicatorComponent}
|
||||||
|
dataMap={{
|
||||||
|
status,
|
||||||
|
label: resolvedLabel,
|
||||||
|
showLabel,
|
||||||
|
animate: shouldAnimate,
|
||||||
|
className,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
108
src/lib/json-ui/wrappers/SeedDataManager.tsx
Normal file
108
src/lib/json-ui/wrappers/SeedDataManager.tsx
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import { JSONUIRenderer } from '@/lib/json-ui'
|
||||||
|
import type { UIComponent } from '@/lib/json-ui/types'
|
||||||
|
import seedDataManagerDefinition from './definitions/seed-data-manager.json'
|
||||||
|
|
||||||
|
interface SeedDataManagerCopy {
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
statusLoaded: string
|
||||||
|
buttons: {
|
||||||
|
load: string
|
||||||
|
reset: string
|
||||||
|
clear: string
|
||||||
|
loadingLoad: string
|
||||||
|
loadingReset: string
|
||||||
|
loadingClear: string
|
||||||
|
}
|
||||||
|
help: {
|
||||||
|
load: string
|
||||||
|
reset: string
|
||||||
|
clear: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultCopy: SeedDataManagerCopy = {
|
||||||
|
title: 'Seed Data Management',
|
||||||
|
description: 'Load, reset, or clear application seed data from the database',
|
||||||
|
statusLoaded: 'Seed data is loaded and available',
|
||||||
|
buttons: {
|
||||||
|
load: 'Load Seed Data',
|
||||||
|
reset: 'Reset to Defaults',
|
||||||
|
clear: 'Clear All Data',
|
||||||
|
loadingLoad: 'Loading...',
|
||||||
|
loadingReset: 'Resetting...',
|
||||||
|
loadingClear: 'Clearing...',
|
||||||
|
},
|
||||||
|
help: {
|
||||||
|
load: 'Populates database with initial data if not already loaded',
|
||||||
|
reset: 'Overwrites all data with fresh seed data',
|
||||||
|
clear: 'Removes all data from the database (destructive action)',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SeedDataManagerProps {
|
||||||
|
isLoaded?: boolean
|
||||||
|
isLoading?: boolean
|
||||||
|
onLoadSeedData?: () => void
|
||||||
|
onResetSeedData?: () => void
|
||||||
|
onClearAllData?: () => void
|
||||||
|
copy?: Partial<SeedDataManagerCopy>
|
||||||
|
}
|
||||||
|
|
||||||
|
const seedDataManagerComponent = seedDataManagerDefinition as UIComponent
|
||||||
|
|
||||||
|
export function SeedDataManager({
|
||||||
|
isLoaded = false,
|
||||||
|
isLoading = false,
|
||||||
|
onLoadSeedData,
|
||||||
|
onResetSeedData,
|
||||||
|
onClearAllData,
|
||||||
|
copy,
|
||||||
|
}: SeedDataManagerProps) {
|
||||||
|
const resolvedCopy: SeedDataManagerCopy = {
|
||||||
|
...defaultCopy,
|
||||||
|
...copy,
|
||||||
|
buttons: {
|
||||||
|
...defaultCopy.buttons,
|
||||||
|
...copy?.buttons,
|
||||||
|
},
|
||||||
|
help: {
|
||||||
|
...defaultCopy.help,
|
||||||
|
...copy?.help,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadDisabled = !onLoadSeedData || isLoading || isLoaded
|
||||||
|
const resetDisabled = !onResetSeedData || isLoading
|
||||||
|
const clearDisabled = !onClearAllData || isLoading
|
||||||
|
|
||||||
|
return (
|
||||||
|
<JSONUIRenderer
|
||||||
|
component={seedDataManagerComponent}
|
||||||
|
dataMap={{
|
||||||
|
title: resolvedCopy.title,
|
||||||
|
description: resolvedCopy.description,
|
||||||
|
statusLoaded: resolvedCopy.statusLoaded,
|
||||||
|
onLoadSeedData,
|
||||||
|
onResetSeedData,
|
||||||
|
onClearAllData,
|
||||||
|
isLoaded,
|
||||||
|
loadDisabled,
|
||||||
|
resetDisabled,
|
||||||
|
clearDisabled,
|
||||||
|
loadButtonText: isLoading
|
||||||
|
? resolvedCopy.buttons.loadingLoad
|
||||||
|
: resolvedCopy.buttons.load,
|
||||||
|
resetButtonText: isLoading
|
||||||
|
? resolvedCopy.buttons.loadingReset
|
||||||
|
: resolvedCopy.buttons.reset,
|
||||||
|
clearButtonText: isLoading
|
||||||
|
? resolvedCopy.buttons.loadingClear
|
||||||
|
: resolvedCopy.buttons.clear,
|
||||||
|
helpLoad: `Load Seed Data: ${resolvedCopy.help.load}`,
|
||||||
|
helpReset: `Reset to Defaults: ${resolvedCopy.help.reset}`,
|
||||||
|
helpClear: `Clear All Data: ${resolvedCopy.help.clear}`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
117
src/lib/json-ui/wrappers/StorageSettings.tsx
Normal file
117
src/lib/json-ui/wrappers/StorageSettings.tsx
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
import type { ChangeEvent } from 'react'
|
||||||
|
import { JSONUIRenderer } from '@/lib/json-ui'
|
||||||
|
import type { UIComponent } from '@/lib/json-ui/types'
|
||||||
|
import {
|
||||||
|
storageSettingsCopy,
|
||||||
|
getBackendCopy,
|
||||||
|
type StorageBackendKey,
|
||||||
|
} from '@/components/storage/storageSettingsConfig'
|
||||||
|
import storageSettingsDefinition from './definitions/storage-settings.json'
|
||||||
|
|
||||||
|
const defaultCopy = storageSettingsCopy.molecule
|
||||||
|
|
||||||
|
type StorageSettingsCopy = typeof defaultCopy
|
||||||
|
|
||||||
|
export interface StorageSettingsProps {
|
||||||
|
backend: StorageBackendKey | null
|
||||||
|
isLoading?: boolean
|
||||||
|
flaskUrl?: string
|
||||||
|
isSwitching?: boolean
|
||||||
|
onFlaskUrlChange?: (value: string) => void
|
||||||
|
onSwitchToFlask?: () => void
|
||||||
|
onSwitchToIndexedDB?: () => void
|
||||||
|
onSwitchToSQLite?: () => void
|
||||||
|
isExporting?: boolean
|
||||||
|
isImporting?: boolean
|
||||||
|
onExport?: () => void
|
||||||
|
onImport?: () => void
|
||||||
|
copy?: Partial<StorageSettingsCopy>
|
||||||
|
}
|
||||||
|
|
||||||
|
const storageSettingsComponent = storageSettingsDefinition as UIComponent
|
||||||
|
|
||||||
|
export function StorageSettings({
|
||||||
|
backend,
|
||||||
|
isLoading = false,
|
||||||
|
flaskUrl = defaultCopy.flaskUrlPlaceholder,
|
||||||
|
isSwitching = false,
|
||||||
|
onFlaskUrlChange,
|
||||||
|
onSwitchToFlask,
|
||||||
|
onSwitchToIndexedDB,
|
||||||
|
onSwitchToSQLite,
|
||||||
|
isExporting = false,
|
||||||
|
isImporting = false,
|
||||||
|
onExport,
|
||||||
|
onImport,
|
||||||
|
copy,
|
||||||
|
}: StorageSettingsProps) {
|
||||||
|
const resolvedCopy: StorageSettingsCopy = {
|
||||||
|
...defaultCopy,
|
||||||
|
...copy,
|
||||||
|
buttons: {
|
||||||
|
...defaultCopy.buttons,
|
||||||
|
...copy?.buttons,
|
||||||
|
},
|
||||||
|
backendDetails: {
|
||||||
|
...defaultCopy.backendDetails,
|
||||||
|
...copy?.backendDetails,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const backendCopy = getBackendCopy(backend)
|
||||||
|
|
||||||
|
const handleFlaskUrlChange = onFlaskUrlChange
|
||||||
|
? (event: ChangeEvent<HTMLInputElement>) => onFlaskUrlChange(event.target.value)
|
||||||
|
: undefined
|
||||||
|
|
||||||
|
return (
|
||||||
|
<JSONUIRenderer
|
||||||
|
component={storageSettingsComponent}
|
||||||
|
dataMap={{
|
||||||
|
title: resolvedCopy.title,
|
||||||
|
description: resolvedCopy.description,
|
||||||
|
currentBackendLabel: resolvedCopy.currentBackendLabel,
|
||||||
|
backendBadge: backendCopy.moleculeLabel,
|
||||||
|
flaskUrlLabel: resolvedCopy.flaskUrlLabel,
|
||||||
|
flaskUrlPlaceholder: resolvedCopy.flaskUrlPlaceholder,
|
||||||
|
flaskHelp: resolvedCopy.flaskHelp,
|
||||||
|
flaskDetails: resolvedCopy.backendDetails.flask,
|
||||||
|
indexedDbDetails: resolvedCopy.backendDetails.indexeddb,
|
||||||
|
sqliteDetails: resolvedCopy.backendDetails.sqlite,
|
||||||
|
flaskUrl,
|
||||||
|
onFlaskUrlChange: handleFlaskUrlChange,
|
||||||
|
flaskUrlDisabled: isSwitching || isLoading,
|
||||||
|
onSwitchToFlask,
|
||||||
|
flaskButtonDisabled: !onSwitchToFlask || isSwitching || isLoading || backend === 'flask',
|
||||||
|
flaskButtonVariant: backend === 'flask' ? 'secondary' : 'default',
|
||||||
|
flaskButtonLabel:
|
||||||
|
backend === 'flask'
|
||||||
|
? resolvedCopy.buttons.flaskActive
|
||||||
|
: resolvedCopy.buttons.flaskUse,
|
||||||
|
onSwitchToIndexedDB,
|
||||||
|
indexedDbDisabled: !onSwitchToIndexedDB || isSwitching || isLoading || backend === 'indexeddb',
|
||||||
|
indexedDbVariant: backend === 'indexeddb' ? 'secondary' : 'outline',
|
||||||
|
indexedDbLabel:
|
||||||
|
backend === 'indexeddb'
|
||||||
|
? resolvedCopy.buttons.indexeddbActive
|
||||||
|
: resolvedCopy.buttons.indexeddbUse,
|
||||||
|
onSwitchToSQLite,
|
||||||
|
sqliteDisabled: !onSwitchToSQLite || isSwitching || isLoading || backend === 'sqlite',
|
||||||
|
sqliteVariant: backend === 'sqlite' ? 'secondary' : 'outline',
|
||||||
|
sqliteLabel:
|
||||||
|
backend === 'sqlite'
|
||||||
|
? resolvedCopy.buttons.sqliteActive
|
||||||
|
: resolvedCopy.buttons.sqliteUse,
|
||||||
|
dataTitle: resolvedCopy.dataTitle,
|
||||||
|
dataDescription: resolvedCopy.dataDescription,
|
||||||
|
dataHelp: resolvedCopy.dataHelp,
|
||||||
|
onExport,
|
||||||
|
exportDisabled: !onExport || isExporting,
|
||||||
|
exportLabel: resolvedCopy.buttons.export,
|
||||||
|
onImport,
|
||||||
|
importDisabled: !onImport || isImporting,
|
||||||
|
importLabel: resolvedCopy.buttons.import,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
106
src/lib/json-ui/wrappers/definitions/breadcrumb.json
Normal file
106
src/lib/json-ui/wrappers/definitions/breadcrumb.json
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
{
|
||||||
|
"id": "breadcrumb-root",
|
||||||
|
"type": "nav",
|
||||||
|
"className": "overflow-x-auto",
|
||||||
|
"props": {
|
||||||
|
"aria-label": "Breadcrumb"
|
||||||
|
},
|
||||||
|
"bindings": {
|
||||||
|
"className": {
|
||||||
|
"source": "className"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "breadcrumb-row",
|
||||||
|
"type": "div",
|
||||||
|
"className": "flex items-center gap-2 text-sm",
|
||||||
|
"loop": {
|
||||||
|
"source": "items",
|
||||||
|
"itemVar": "item",
|
||||||
|
"indexVar": "itemIndex"
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "breadcrumb-link-last",
|
||||||
|
"type": "Link",
|
||||||
|
"conditional": {
|
||||||
|
"if": "(item.href || item.onClick) && itemIndex === items.length - 1"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"variant": "default",
|
||||||
|
"className": "text-foreground font-medium"
|
||||||
|
},
|
||||||
|
"bindings": {
|
||||||
|
"href": {
|
||||||
|
"source": "item.href"
|
||||||
|
},
|
||||||
|
"onClick": {
|
||||||
|
"source": "item.onClick"
|
||||||
|
},
|
||||||
|
"children": {
|
||||||
|
"source": "item.label"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "breadcrumb-link",
|
||||||
|
"type": "Link",
|
||||||
|
"conditional": {
|
||||||
|
"if": "(item.href || item.onClick) && itemIndex < items.length - 1"
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"variant": "muted",
|
||||||
|
"className": "text-muted-foreground hover:text-foreground"
|
||||||
|
},
|
||||||
|
"bindings": {
|
||||||
|
"href": {
|
||||||
|
"source": "item.href"
|
||||||
|
},
|
||||||
|
"onClick": {
|
||||||
|
"source": "item.onClick"
|
||||||
|
},
|
||||||
|
"children": {
|
||||||
|
"source": "item.label"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "breadcrumb-text-last",
|
||||||
|
"type": "span",
|
||||||
|
"conditional": {
|
||||||
|
"if": "!item.href && !item.onClick && itemIndex === items.length - 1"
|
||||||
|
},
|
||||||
|
"className": "text-foreground font-medium",
|
||||||
|
"bindings": {
|
||||||
|
"children": {
|
||||||
|
"source": "item.label"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "breadcrumb-text",
|
||||||
|
"type": "span",
|
||||||
|
"conditional": {
|
||||||
|
"if": "!item.href && !item.onClick && itemIndex < items.length - 1"
|
||||||
|
},
|
||||||
|
"className": "text-muted-foreground",
|
||||||
|
"bindings": {
|
||||||
|
"children": {
|
||||||
|
"source": "item.label"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "breadcrumb-separator",
|
||||||
|
"type": "span",
|
||||||
|
"conditional": {
|
||||||
|
"if": "itemIndex < items.length - 1"
|
||||||
|
},
|
||||||
|
"className": "text-muted-foreground",
|
||||||
|
"children": "/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
36
src/lib/json-ui/wrappers/definitions/save-indicator.json
Normal file
36
src/lib/json-ui/wrappers/definitions/save-indicator.json
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"id": "save-indicator-root",
|
||||||
|
"type": "div",
|
||||||
|
"className": "flex items-center gap-1.5 text-xs text-muted-foreground",
|
||||||
|
"bindings": {
|
||||||
|
"className": {
|
||||||
|
"source": "className"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "save-indicator-icon",
|
||||||
|
"type": "StatusIcon",
|
||||||
|
"bindings": {
|
||||||
|
"type": {
|
||||||
|
"source": "status"
|
||||||
|
},
|
||||||
|
"animate": {
|
||||||
|
"source": "animate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "save-indicator-label",
|
||||||
|
"type": "span",
|
||||||
|
"conditional": {
|
||||||
|
"if": "showLabel"
|
||||||
|
},
|
||||||
|
"bindings": {
|
||||||
|
"children": {
|
||||||
|
"source": "label"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
157
src/lib/json-ui/wrappers/definitions/seed-data-manager.json
Normal file
157
src/lib/json-ui/wrappers/definitions/seed-data-manager.json
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
{
|
||||||
|
"id": "seed-data-manager",
|
||||||
|
"type": "Card",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "seed-data-header",
|
||||||
|
"type": "CardHeader",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "seed-data-title",
|
||||||
|
"type": "CardTitle",
|
||||||
|
"bindings": {
|
||||||
|
"children": {
|
||||||
|
"source": "title"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "seed-data-description",
|
||||||
|
"type": "CardDescription",
|
||||||
|
"bindings": {
|
||||||
|
"children": {
|
||||||
|
"source": "description"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "seed-data-content",
|
||||||
|
"type": "CardContent",
|
||||||
|
"className": "flex flex-col gap-4",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "seed-data-alert",
|
||||||
|
"type": "Alert",
|
||||||
|
"conditional": {
|
||||||
|
"if": "isLoaded"
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "seed-data-alert-description",
|
||||||
|
"type": "AlertDescription",
|
||||||
|
"bindings": {
|
||||||
|
"children": {
|
||||||
|
"source": "statusLoaded"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "seed-data-actions-block",
|
||||||
|
"type": "div",
|
||||||
|
"className": "flex flex-col gap-3",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "seed-data-actions",
|
||||||
|
"type": "div",
|
||||||
|
"className": "flex gap-2 flex-wrap",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "seed-data-load",
|
||||||
|
"type": "Button",
|
||||||
|
"props": {
|
||||||
|
"variant": "default"
|
||||||
|
},
|
||||||
|
"bindings": {
|
||||||
|
"onClick": {
|
||||||
|
"source": "onLoadSeedData"
|
||||||
|
},
|
||||||
|
"disabled": {
|
||||||
|
"source": "loadDisabled"
|
||||||
|
},
|
||||||
|
"children": {
|
||||||
|
"source": "loadButtonText"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "seed-data-reset",
|
||||||
|
"type": "Button",
|
||||||
|
"props": {
|
||||||
|
"variant": "outline"
|
||||||
|
},
|
||||||
|
"bindings": {
|
||||||
|
"onClick": {
|
||||||
|
"source": "onResetSeedData"
|
||||||
|
},
|
||||||
|
"disabled": {
|
||||||
|
"source": "resetDisabled"
|
||||||
|
},
|
||||||
|
"children": {
|
||||||
|
"source": "resetButtonText"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "seed-data-clear",
|
||||||
|
"type": "Button",
|
||||||
|
"props": {
|
||||||
|
"variant": "destructive"
|
||||||
|
},
|
||||||
|
"bindings": {
|
||||||
|
"onClick": {
|
||||||
|
"source": "onClearAllData"
|
||||||
|
},
|
||||||
|
"disabled": {
|
||||||
|
"source": "clearDisabled"
|
||||||
|
},
|
||||||
|
"children": {
|
||||||
|
"source": "clearButtonText"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "seed-data-help",
|
||||||
|
"type": "div",
|
||||||
|
"className": "text-sm text-muted-foreground space-y-1",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "seed-data-help-load",
|
||||||
|
"type": "p",
|
||||||
|
"bindings": {
|
||||||
|
"children": {
|
||||||
|
"source": "helpLoad"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "seed-data-help-reset",
|
||||||
|
"type": "p",
|
||||||
|
"bindings": {
|
||||||
|
"children": {
|
||||||
|
"source": "helpReset"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "seed-data-help-clear",
|
||||||
|
"type": "p",
|
||||||
|
"bindings": {
|
||||||
|
"children": {
|
||||||
|
"source": "helpClear"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
331
src/lib/json-ui/wrappers/definitions/storage-settings.json
Normal file
331
src/lib/json-ui/wrappers/definitions/storage-settings.json
Normal file
@@ -0,0 +1,331 @@
|
|||||||
|
{
|
||||||
|
"id": "storage-settings-root",
|
||||||
|
"type": "div",
|
||||||
|
"className": "space-y-6",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "storage-backend-card",
|
||||||
|
"type": "Card",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "storage-backend-header",
|
||||||
|
"type": "CardHeader",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "storage-backend-title",
|
||||||
|
"type": "CardTitle",
|
||||||
|
"bindings": {
|
||||||
|
"children": {
|
||||||
|
"source": "title"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "storage-backend-description",
|
||||||
|
"type": "CardDescription",
|
||||||
|
"bindings": {
|
||||||
|
"children": {
|
||||||
|
"source": "description"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "storage-backend-content",
|
||||||
|
"type": "CardContent",
|
||||||
|
"className": "space-y-4",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "storage-backend-current",
|
||||||
|
"type": "div",
|
||||||
|
"className": "flex items-center gap-2",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "storage-backend-current-label",
|
||||||
|
"type": "span",
|
||||||
|
"className": "text-sm text-muted-foreground",
|
||||||
|
"bindings": {
|
||||||
|
"children": {
|
||||||
|
"source": "currentBackendLabel"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "storage-backend-current-badge",
|
||||||
|
"type": "Badge",
|
||||||
|
"props": {
|
||||||
|
"variant": "secondary",
|
||||||
|
"className": "flex items-center gap-1"
|
||||||
|
},
|
||||||
|
"bindings": {
|
||||||
|
"children": {
|
||||||
|
"source": "backendBadge"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "storage-backend-form",
|
||||||
|
"type": "div",
|
||||||
|
"className": "grid gap-4",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "storage-backend-flask",
|
||||||
|
"type": "div",
|
||||||
|
"className": "space-y-2",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "storage-backend-flask-label",
|
||||||
|
"type": "Label",
|
||||||
|
"props": {
|
||||||
|
"htmlFor": "flask-url"
|
||||||
|
},
|
||||||
|
"bindings": {
|
||||||
|
"children": {
|
||||||
|
"source": "flaskUrlLabel"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "storage-backend-flask-row",
|
||||||
|
"type": "div",
|
||||||
|
"className": "flex gap-2",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "storage-backend-flask-input",
|
||||||
|
"type": "Input",
|
||||||
|
"bindings": {
|
||||||
|
"value": {
|
||||||
|
"source": "flaskUrl"
|
||||||
|
},
|
||||||
|
"onChange": {
|
||||||
|
"source": "onFlaskUrlChange"
|
||||||
|
},
|
||||||
|
"disabled": {
|
||||||
|
"source": "flaskUrlDisabled"
|
||||||
|
},
|
||||||
|
"placeholder": {
|
||||||
|
"source": "flaskUrlPlaceholder"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"props": {
|
||||||
|
"id": "flask-url"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "storage-backend-flask-button",
|
||||||
|
"type": "Button",
|
||||||
|
"bindings": {
|
||||||
|
"onClick": {
|
||||||
|
"source": "onSwitchToFlask"
|
||||||
|
},
|
||||||
|
"disabled": {
|
||||||
|
"source": "flaskButtonDisabled"
|
||||||
|
},
|
||||||
|
"variant": {
|
||||||
|
"source": "flaskButtonVariant"
|
||||||
|
},
|
||||||
|
"children": {
|
||||||
|
"source": "flaskButtonLabel"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "storage-backend-flask-help",
|
||||||
|
"type": "p",
|
||||||
|
"className": "text-xs text-muted-foreground",
|
||||||
|
"bindings": {
|
||||||
|
"children": {
|
||||||
|
"source": "flaskHelp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "storage-backend-options",
|
||||||
|
"type": "div",
|
||||||
|
"className": "flex gap-2",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "storage-backend-indexeddb",
|
||||||
|
"type": "Button",
|
||||||
|
"props": {
|
||||||
|
"className": "flex-1"
|
||||||
|
},
|
||||||
|
"bindings": {
|
||||||
|
"onClick": {
|
||||||
|
"source": "onSwitchToIndexedDB"
|
||||||
|
},
|
||||||
|
"disabled": {
|
||||||
|
"source": "indexedDbDisabled"
|
||||||
|
},
|
||||||
|
"variant": {
|
||||||
|
"source": "indexedDbVariant"
|
||||||
|
},
|
||||||
|
"children": {
|
||||||
|
"source": "indexedDbLabel"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "storage-backend-sqlite",
|
||||||
|
"type": "Button",
|
||||||
|
"props": {
|
||||||
|
"className": "flex-1"
|
||||||
|
},
|
||||||
|
"bindings": {
|
||||||
|
"onClick": {
|
||||||
|
"source": "onSwitchToSQLite"
|
||||||
|
},
|
||||||
|
"disabled": {
|
||||||
|
"source": "sqliteDisabled"
|
||||||
|
},
|
||||||
|
"variant": {
|
||||||
|
"source": "sqliteVariant"
|
||||||
|
},
|
||||||
|
"children": {
|
||||||
|
"source": "sqliteLabel"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "storage-backend-details",
|
||||||
|
"type": "div",
|
||||||
|
"className": "text-xs text-muted-foreground space-y-1",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "storage-backend-details-indexeddb",
|
||||||
|
"type": "p",
|
||||||
|
"bindings": {
|
||||||
|
"children": {
|
||||||
|
"source": "indexedDbDetails"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "storage-backend-details-sqlite",
|
||||||
|
"type": "p",
|
||||||
|
"bindings": {
|
||||||
|
"children": {
|
||||||
|
"source": "sqliteDetails"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "storage-backend-details-flask",
|
||||||
|
"type": "p",
|
||||||
|
"bindings": {
|
||||||
|
"children": {
|
||||||
|
"source": "flaskDetails"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "storage-data-card",
|
||||||
|
"type": "Card",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "storage-data-header",
|
||||||
|
"type": "CardHeader",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "storage-data-title",
|
||||||
|
"type": "CardTitle",
|
||||||
|
"bindings": {
|
||||||
|
"children": {
|
||||||
|
"source": "dataTitle"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "storage-data-description",
|
||||||
|
"type": "CardDescription",
|
||||||
|
"bindings": {
|
||||||
|
"children": {
|
||||||
|
"source": "dataDescription"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "storage-data-content",
|
||||||
|
"type": "CardContent",
|
||||||
|
"className": "space-y-4",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "storage-data-actions",
|
||||||
|
"type": "div",
|
||||||
|
"className": "flex gap-2",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "storage-data-export",
|
||||||
|
"type": "Button",
|
||||||
|
"props": {
|
||||||
|
"variant": "outline",
|
||||||
|
"className": "flex-1"
|
||||||
|
},
|
||||||
|
"bindings": {
|
||||||
|
"onClick": {
|
||||||
|
"source": "onExport"
|
||||||
|
},
|
||||||
|
"disabled": {
|
||||||
|
"source": "exportDisabled"
|
||||||
|
},
|
||||||
|
"children": {
|
||||||
|
"source": "exportLabel"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "storage-data-import",
|
||||||
|
"type": "Button",
|
||||||
|
"props": {
|
||||||
|
"variant": "outline",
|
||||||
|
"className": "flex-1"
|
||||||
|
},
|
||||||
|
"bindings": {
|
||||||
|
"onClick": {
|
||||||
|
"source": "onImport"
|
||||||
|
},
|
||||||
|
"disabled": {
|
||||||
|
"source": "importDisabled"
|
||||||
|
},
|
||||||
|
"children": {
|
||||||
|
"source": "importLabel"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "storage-data-help",
|
||||||
|
"type": "p",
|
||||||
|
"className": "text-xs text-muted-foreground",
|
||||||
|
"bindings": {
|
||||||
|
"children": {
|
||||||
|
"source": "dataHelp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ export type ComponentType =
|
|||||||
| 'Alert' | 'InfoBox' | 'EmptyState' | 'StatusBadge'
|
| 'Alert' | 'InfoBox' | 'EmptyState' | 'StatusBadge'
|
||||||
| 'Table' | 'KeyValue' | 'StatCard' | 'DataCard' | 'SearchInput' | 'ActionBar'
|
| 'Table' | 'KeyValue' | 'StatCard' | 'DataCard' | 'SearchInput' | 'ActionBar'
|
||||||
| 'AppBranding' | 'LabelWithBadge' | 'EmptyEditorState' | 'LoadingFallback' | 'LoadingState' | 'NavigationGroupHeader'
|
| 'AppBranding' | 'LabelWithBadge' | 'EmptyEditorState' | 'LoadingFallback' | 'LoadingState' | 'NavigationGroupHeader'
|
||||||
|
| 'Breadcrumb' | 'SaveIndicator' | 'LazyBarChart' | 'LazyD3BarChart' | 'LazyLineChart' | 'SeedDataManager' | 'StorageSettings'
|
||||||
|
|
||||||
export type ActionType =
|
export type ActionType =
|
||||||
| 'create' | 'update' | 'delete' | 'navigate'
|
| 'create' | 'update' | 'delete' | 'navigate'
|
||||||
|
|||||||
Reference in New Issue
Block a user