# JSON-Driven UI & Atomic Components ## Overview This project demonstrates a comprehensive JSON-driven UI architecture with atomic component design and custom React hooks. Build entire applications from declarative JSON schemas while maintaining clean, maintainable code through small, focused components. ## Key Features ### 🎯 JSON-Driven UI System - Define complete page layouts using JSON schemas - Automatic data source management (KV persistence, computed values, static data) - Declarative action system (CRUD, navigation, toasts) - Component bindings and event handlers - Type-safe schema definitions ### 🧩 Atomic Component Library - **Atoms** (< 50 LOC): Basic building blocks - `Heading`, `Text`, `List`, `Grid` - `StatusBadge` - **Molecules** (50-100 LOC): Composed components - `DataCard`, `SearchInput`, `ActionBar` - **Organisms** (100-150 LOC): Complex compositions - All components follow strict LOC limits for maintainability ### 🪝 Custom React Hooks #### Data Management - **`useCRUD`** - Complete CRUD operations with KV persistence - **`useSearch`** - Multi-field search with filtering - **`useFilter`** - Advanced filtering with multiple operators - **`useSort`** - Multi-key sorting with direction toggle - **`useJSONData`** - Flexible data management - **`usePagination`** - Pagination logic - **`useLocalStorage`** - Browser storage management #### UI State - **`useDialog`** - Dialog/modal state management - **`useToggle`** - Boolean state with helpers - **`useForm`** - Complete form handling with validation - **`useActionExecutor`** - Execute JSON-defined actions ## Quick Start ### Using JSON Schemas ```typescript import { PageRenderer } from '@/lib/json-ui/page-renderer' import { dashboardSchema } from '@/schemas/dashboard-schema' export function DashboardPage() { return } ``` ### Using Atomic Components ```typescript import { DataCard, SearchInput, ActionBar } from '@/components/molecules' import { Grid, Heading } from '@/components/atoms' import { useCRUD, useSearch } from '@/hooks/data' export function MyPage() { const { items, create, remove } = useCRUD({ key: 'my-items', defaultValue: [], persist: true }) const { query, setQuery, filtered } = useSearch({ items, searchFields: ['name', 'description'] }) return (
My Page create({...}) } ]} />
) } ``` ### Using Custom Hooks ```typescript import { useForm } from '@/hooks/ui' interface FormData { name: string email: string } export function MyForm() { const form = useForm({ initialValues: { name: '', email: '' }, validate: (values) => { const errors: any = {} if (!values.name) errors.name = 'Name is required' if (!values.email) errors.email = 'Email is required' return errors }, onSubmit: async (values) => { console.log('Form submitted:', values) } }) return (
{form.errors.name && {form.errors.name}}
) } ``` ## JSON Schema Structure ```typescript const mySchema: PageSchema = { id: 'my-page', name: 'My Page', layout: { type: 'single' }, // Data sources dataSources: [ { id: 'items', type: 'kv', // Persisted to KV store key: 'my-items', defaultValue: [] }, { id: 'stats', type: 'computed', // Computed from other data compute: (data) => ({ total: data.items?.length || 0 }), dependencies: ['items'] } ], // UI components components: [ { id: 'root', type: 'div', props: { className: 'p-6' }, children: [ { id: 'title', type: 'Heading', props: { level: 1, children: 'My Page' } }, { id: 'stat-card', type: 'DataCard', props: { title: 'Total Items' }, bindings: { value: { source: 'stats', path: 'total' } } }, { id: 'add-button', type: 'Button', props: { children: 'Add Item' }, events: [ { event: 'click', actions: [ { id: 'create-item', type: 'create', target: 'items', compute: (data) => ({ id: Date.now(), name: 'New Item' }) }, { id: 'show-toast', type: 'show-toast', message: 'Item added!', variant: 'success' } ] } ] } ] } ] } ``` ## Available Components ### Atoms - `Heading` - Semantic headings (h1-h6) - `Text` - Text with variants (body, caption, muted, small) - `List` - Render lists from arrays - `Grid` - Responsive grid layouts - `StatusBadge` - Colored status indicators ### Molecules - `DataCard` - Stat cards with icon, trend, loading states - `SearchInput` - Search with clear button - `ActionBar` - Title with action buttons ### Shadcn UI Components All shadcn components are available: `Button`, `Card`, `Input`, `Dialog`, `Tabs`, `Badge`, `Progress`, etc. ## Action Types ### CRUD Actions - **create**: Add new items to data source - **update**: Modify existing data - **delete**: Remove items from data source ### UI Actions - **show-toast**: Display notification (success, error, info, warning) - **navigate**: Navigate to different route - **open-dialog**: Open a dialog - **close-dialog**: Close a dialog ### Value Actions - **set-value**: Set data source value - **toggle-value**: Toggle boolean value - **increment**: Increment numeric value - **decrement**: Decrement numeric value ## Data Source Types ### KV (Persistent) Persists data between sessions using Spark's KV store. ```typescript { id: 'todos', type: 'kv', key: 'app-todos', defaultValue: [] } ``` ### Static (Session Only) Data lives only in memory, reset on page reload. ```typescript { id: 'searchQuery', type: 'static', defaultValue: '' } ``` ### Computed (Derived) Automatically recomputes when dependencies change. ```typescript { id: 'stats', type: 'computed', compute: (data) => ({ total: data.todos.length, completed: data.todos.filter(t => t.completed).length }), dependencies: ['todos'] } ``` ## Hook Examples ### useCRUD ```typescript const { items, create, read, update, remove, clear } = useCRUD({ key: 'todos', defaultValue: [], persist: true }) // ✅ CORRECT - Use functional updates create({ id: Date.now(), text: 'New todo' }) update(todoId, { completed: true }) remove(todoId) ``` ### useSearch ```typescript const { query, setQuery, filtered, hasQuery, resultCount } = useSearch({ items: todos, searchFields: ['text', 'tags'], caseSensitive: false }) ``` ### useFilter ```typescript const { filtered, filters, addFilter, removeFilter, clearFilters } = useFilter({ items: todos, initialFilters: [ { field: 'status', operator: 'equals', value: 'active' } ] }) addFilter({ field: 'priority', operator: 'in', value: ['high', 'medium'] }) ``` ### useForm ```typescript const form = useForm({ initialValues: { name: '', email: '' }, validate: (values) => { const errors: any = {} if (!values.name) errors.name = 'Required' return errors }, onSubmit: async (values) => { // Handle submission } }) // Use in JSX ``` ## Demo Pages 1. **AtomicComponentDemo** - Shows all atomic components and hooks in action 2. **DashboardDemoPage** - Complete JSON-driven dashboard with projects 3. **JSONUIPage** - Original JSON UI examples ## Best Practices ### 1. Keep Components Small - Atoms: < 50 LOC - Molecules: 50-100 LOC - Organisms: 100-150 LOC ### 2. Extract Logic to Hooks ❌ Bad: Logic in component ```typescript const [items, setItems] = useState([]) const addItem = () => setItems([...items, newItem]) ``` ✅ Good: Logic in hook ```typescript const { items, create } = useCRUD({ key: 'items', defaultValue: [] }) ``` ### 3. Use Computed Data Sources ❌ Bad: Computing in render ```typescript const completed = todos.filter(t => t.completed).length ``` ✅ Good: Computed data source ```typescript { id: 'stats', type: 'computed', compute: (data) => ({ completed: data.todos.filter(t => t.completed).length }), dependencies: ['todos'] } ``` ### 4. Compose Atomic Components Build complex UIs from simple atoms: ```typescript Dashboard ``` ## File Structure ``` src/ ├── components/ │ ├── atoms/ # < 50 LOC components │ │ ├── Heading.tsx │ │ ├── Text.tsx │ │ ├── List.tsx │ │ ├── Grid.tsx │ │ └── StatusBadge.tsx │ ├── molecules/ # 50-100 LOC components │ │ ├── DataCard.tsx │ │ ├── SearchInput.tsx │ │ └── ActionBar.tsx │ └── ui/ # shadcn components ├── hooks/ │ ├── data/ # Data management hooks │ │ ├── use-crud.ts │ │ ├── use-search.ts │ │ ├── use-filter.ts │ │ └── use-sort.ts │ └── ui/ # UI state hooks │ ├── use-dialog.ts │ ├── use-toggle.ts │ └── use-form.ts ├── lib/ │ └── json-ui/ # JSON UI system │ ├── page-renderer.tsx │ ├── component-renderer.tsx │ └── component-registry.tsx ├── schemas/ # JSON page schemas │ ├── dashboard-schema.ts │ └── page-schemas.ts └── types/ └── json-ui.ts # TypeScript types ``` ## Documentation - **JSON_UI_GUIDE.md** - Complete JSON UI documentation - **PRD.md** - Product requirements and design decisions ## Contributing When adding new components or hooks: 1. Follow LOC limits (atoms < 50, molecules < 100, organisms < 150) 2. Add TypeScript types 3. Update component registry if adding UI components 4. Document in README 5. Add examples --- **Built with React, TypeScript, Tailwind CSS, and Shadcn UI**