// View/Schema Designer page (US-033) // Visual schema designer for creating custom views import { useState, useEffect, useCallback, useMemo } from 'react' import { useParams, useNavigate } from 'react-router-dom' import apiClient from '@/api/client' import { useWorkspace } from '@/contexts/WorkspaceContext' import Button from '@/components/Button' import Input from '@/components/Input' import GroupMultiSelect from '@/components/GroupMultiSelect' // Field type definitions matching backend const FIELD_TYPES = [ { value: 'text', label: 'Text', icon: 'T' }, { value: 'number', label: 'Number', icon: '#' }, { value: 'email', label: 'Email', icon: '@' }, { value: 'url', label: 'URL', icon: '🔗' }, { value: 'date', label: 'Date', icon: '📅' }, { value: 'datetime', label: 'Date & Time', icon: '🕐' }, { value: 'boolean', label: 'Boolean', icon: '✓' }, { value: 'select', label: 'Select', icon: '▼' }, { value: 'reference', label: 'Reference', icon: '→' }, ] as const const WIDGET_TYPES = [ { value: '', label: 'Auto' }, { value: 'textarea', label: 'Text Area' }, { value: 'select', label: 'Dropdown' }, { value: 'radio', label: 'Radio Buttons' }, { value: 'checkbox', label: 'Checkbox' }, { value: 'color', label: 'Color Picker' }, ] as const interface SchemaField { _id: string // Client-side ID for React keys name: string type: string label: string description: string required: boolean display_order: number widget: string group: string default_value?: unknown options?: string[] reference_collection?: string } interface ViewSchema { title?: string description?: string layout?: unknown fields?: Omit[] // Full view detail has fields field_count?: number // List view has field_count } interface View { id: string name: string collection_name: string workspace_id: string schema: ViewSchema settings?: Record created_at: string updated_at: string } interface Collection { name: string schema?: Record is_system: boolean } // Field editor component function FieldEditor({ field, onUpdate, onDelete, onMoveUp, onMoveDown, isFirst, isLast, collections, }: { field: SchemaField onUpdate: (field: SchemaField) => void onDelete: () => void onMoveUp: () => void onMoveDown: () => void isFirst: boolean isLast: boolean collections: Collection[] }) { const [isExpanded, setIsExpanded] = useState(false) const [optionsInput, setOptionsInput] = useState(field.options?.join('\n') || '') const handleOptionsChange = (value: string) => { setOptionsInput(value) const options = value.split('\n').filter(Boolean) onUpdate({ ...field, options: options.length > 0 ? options : undefined }) } return (
{/* Field header */}
{/* Drag handle placeholder */}
{/* Move buttons */}
{/* Type badge */} {FIELD_TYPES.find((t) => t.value === field.type)?.icon || '?'} {/* Field name and label */}
{field.name} {field.label && field.label !== field.name && ( ({field.label}) )}
{/* Required badge */} {field.required && ( Required )} {/* Type selector */} {/* Expand/collapse */} {/* Delete button */}
{/* Field properties (expanded) */} {isExpanded && (
onUpdate({ ...field, label: e.target.value })} placeholder={field.name} /> onUpdate({ ...field, description: e.target.value })} placeholder="Help text for this field" />
onUpdate({ ...field, group: e.target.value })} placeholder="e.g., Basic Info, Contact" /> {field.type === 'select' && (