import React, { useState } from 'react'; import { Button } from './button'; import { Input } from './input'; import { Label } from './label'; import { Plus, Trash2, Eye, EyeOff } from 'lucide-react'; interface KeyValuePair { key: string; value: string; id: string; } interface KeyValueEditorProps { label?: string; placeholder?: { key?: string; value?: string; }; pairs: KeyValuePair[]; onChange: (pairs: KeyValuePair[]) => void; disabled?: boolean; className?: string; keyLabel?: string; valueLabel?: string; maskSensitiveValues?: boolean; } const SENSITIVE_KEY_PATTERNS = [ /\bapi[_-]?key\b/i, /\bapikey\b/i, /\bsecret\b/i, /\btoken\b/i, /\bpassword\b/i, /\bauthorization\b/i, /\bauth[_-]?token\b/i, /\bbearer\b/i, /\bcredential\b/i, /\bclient[_-]?secret\b/i, ]; export function KeyValueEditor({ label = 'Key-Value Pairs', placeholder = { key: 'Key', value: 'Value' }, pairs, onChange, disabled = false, className = '', keyLabel = 'Key', valueLabel = 'Value', maskSensitiveValues = true, }: KeyValueEditorProps) { const [visibleValues, setVisibleValues] = useState>(new Set()); const isSensitiveKey = (key: string): boolean => { if (!maskSensitiveValues || !key) return false; return SENSITIVE_KEY_PATTERNS.some((pattern) => pattern.test(key)); }; const toggleValueVisibility = (id: string) => { setVisibleValues((prev) => { const newSet = new Set(prev); if (newSet.has(id)) { newSet.delete(id); } else { newSet.add(id); } return newSet; }); }; const addPair = () => { const newPair: KeyValuePair = { key: '', value: '', id: `kv-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`, }; onChange([...pairs, newPair]); }; const removePair = (id: string) => { const filteredPairs = pairs.filter((pair) => pair.id !== id); // Allow removing all pairs - don't force an empty pair onChange(filteredPairs); }; const updatePair = (id: string, field: 'key' | 'value', newValue: string) => { onChange(pairs.map((pair) => (pair.id === id ? { ...pair, [field]: newValue } : pair))); }; return (
{label && }
{/* Header row - only show if there are pairs */} {pairs.length > 0 && (
{keyLabel}
{valueLabel}
)} {/* Key-value pair rows */} {pairs.map((pair) => { const isSensitive = isSensitiveKey(pair.key); const isVisible = visibleValues.has(pair.id); return (
updatePair(pair.id, 'key', e.target.value)} disabled={disabled} className="col-span-5" />
updatePair(pair.id, 'value', e.target.value)} disabled={disabled} className="pr-10" /> {isSensitive && pair.value && ( )}
); })}
{/* Add button */}
); }