import { Button } from '@shadcn/ui/button'; import { CircleMinusIcon, CirclePlusIcon } from 'lucide-react'; import { useEffect, useState } from 'react'; import { Combobox } from './combobox'; import { EnhancedInput, EnhancedInputProps } from './enhanced-input'; interface FieldConfig extends Omit { name: string; type: 'text' | 'number' | 'select'; options?: { label: string; value: string }[]; internal?: boolean; calculateValue?: (value: Record) => any; } interface ObjectInputProps { value: T; onChange: (value: T) => void; fields: FieldConfig[]; } export function ObjectInput>({ value, onChange, fields, }: ObjectInputProps) { const [internalState, setInternalState] = useState(value); useEffect(() => { setInternalState(value); }, [value]); const updateField = (key: keyof T, fieldValue: string | number) => { let updatedInternalState = { ...internalState, [key]: fieldValue }; fields.forEach((field) => { if (field.calculateValue && field.name === key) { const newValue = field.calculateValue(updatedInternalState); updatedInternalState = newValue; } }); setInternalState(updatedInternalState); const filteredValue = Object.keys(updatedInternalState).reduce((acc, fieldKey) => { const field = fields.find((f) => f.name === fieldKey); if (field && !field.internal) { acc[fieldKey as keyof T] = updatedInternalState[fieldKey as keyof T]; } return acc; }, {} as T); onChange(filteredValue); }; return (
{fields.map(({ name, type, options, ...fieldProps }) => (
{type === 'select' && options ? ( placeholder={fieldProps.placeholder} options={options} value={internalState[name]} onChange={(fieldValue) => { updateField(name, fieldValue); }} /> ) : ( updateField(name, fieldValue)} type={type} {...fieldProps} /> )}
))}
); } interface ArrayInputProps { value?: T[]; onChange: (value: T[]) => void; fields: FieldConfig[]; } export function ArrayInput>({ value = [], onChange, fields, }: ArrayInputProps) { const initializeDefaultItem = (): T => fields.reduce((acc, field) => { acc[field.name as keyof T] = undefined as T[keyof T]; return acc; }, {} as T); const [displayItems, setDisplayItems] = useState(() => { return value.length > 0 ? value : [initializeDefaultItem()]; }); const isItemModified = (item: T): boolean => fields.some((field) => { const val = item[field.name]; return val !== undefined && val !== null && val !== ''; }); const handleItemChange = (index: number, updatedItem: T) => { const newDisplayItems = [...displayItems]; newDisplayItems[index] = updatedItem; setDisplayItems(newDisplayItems); const modifiedItems = newDisplayItems.filter(isItemModified); onChange(modifiedItems); }; const createField = () => { setDisplayItems([...displayItems, initializeDefaultItem()]); }; const deleteField = (index: number) => { const newDisplayItems = displayItems.filter((_, i) => i !== index); setDisplayItems(newDisplayItems); const modifiedItems = newDisplayItems.filter(isItemModified); onChange(modifiedItems); }; return (
{displayItems.map((item, index) => (
handleItemChange(index, updatedItem)} fields={fields} />
{displayItems.length > 1 && ( )} {index === displayItems.length - 1 && ( )}
))}
); }