import React, { useState, useEffect } from 'react' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Button } from '@/components/ui/button' import { X, Plus } from 'lucide-react' interface KeyValueInputProps { id: string label: string value: Record | undefined onChange: (value: Record | undefined) => void description?: string disabled?: boolean className?: string keyPlaceholder?: string valuePlaceholder?: string addButtonText?: string helperText?: string allowEmptyValues?: boolean // If true, entries with empty values are considered valid } interface KeyValuePair { key: string value: string } const KeyValueInput: React.FC = ({ id, label, value, onChange, description, disabled = false, className, keyPlaceholder = 'Key', valuePlaceholder = 'Value', addButtonText = 'Add Entry', helperText, allowEmptyValues = false }) => { // Convert the value object to an array of key-value pairs for editing const pairsFromValue = value ? Object.entries(value).map(([key, val]) => ({ key, value: val })) : [] const [pairs, setPairs] = useState( pairsFromValue.length > 0 ? pairsFromValue : [{ key: '', value: '' }] ) // Sync internal state when value prop changes useEffect(() => { const newPairsFromValue = value ? Object.entries(value).map(([key, val]) => ({ key, value: val })) : [] if (newPairsFromValue.length > 0) { setPairs(newPairsFromValue) } else if (!value) { // Reset to single empty row if value is explicitly undefined/null setPairs([{ key: '', value: '' }]) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [value]) // Update parent component when pairs change const updateParent = (newPairs: KeyValuePair[]) => { // Filter based on validation rules const validPairs = allowEmptyValues ? newPairs.filter(pair => pair.key.trim() !== '') : newPairs.filter(pair => pair.key.trim() !== '' && pair.value.trim() !== '') if (validPairs.length === 0) { onChange(undefined) } else { const pairsObject = validPairs.reduce((acc, pair) => { acc[pair.key.trim()] = pair.value.trim() return acc }, {} as Record) onChange(pairsObject) } } const handleKeyChange = (index: number, newKey: string) => { const newPairs = [...pairs] newPairs[index].key = newKey setPairs(newPairs) updateParent(newPairs) } const handleValueChange = (index: number, newValue: string) => { const newPairs = [...pairs] newPairs[index].value = newValue setPairs(newPairs) updateParent(newPairs) } const addPair = () => { const newPairs = [...pairs, { key: '', value: '' }] setPairs(newPairs) } const removePair = (index: number) => { if (pairs.length === 1) { // Reset to empty if it's the last one const newPairs = [{ key: '', value: '' }] setPairs(newPairs) updateParent(newPairs) } else { const newPairs = pairs.filter((_, i) => i !== index) setPairs(newPairs) updateParent(newPairs) } } return (
{pairs.map((pair, index) => (
handleKeyChange(index, e.target.value)} disabled={disabled} className="flex-1" /> handleValueChange(index, e.target.value)} disabled={disabled} className="flex-1" />
))}
{description && (

{description}

)} {helperText && (

{helperText}

)}
) } export default KeyValueInput