import { useEffect, useState } from 'react'; import { KVAPI } from '../api/kvApi'; import { useSchema } from '../api/schemaApi'; import { ALL_TYPES, describeType, detectType } from './typeHeuristics'; import { lookupSchemaForKey } from './schemaLookup'; import { LeafValueEditor } from './leafRenderers'; import { FieldType } from './types'; interface Props { // Full Redis key path of the selected leaf. selectedKey: string; // Bumped after a delete or external change to force refetch. reloadToken: number; onSaved: () => void; onDeleted: () => void; onError: (msg: string) => void; } export function KVLeafEditor({ selectedKey, reloadToken, onSaved, onDeleted, onError }: Props) { const [original, setOriginal] = useState(''); const [draft, setDraft] = useState(''); const [exists, setExists] = useState(true); const [redisType, setRedisType] = useState(''); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); // Manual override for the inferred type. "auto" defers to schema → heuristic. const [typeOverride, setTypeOverride] = useState('auto'); const { schema } = useSchema(); const schemaLookup = lookupSchemaForKey(schema, selectedKey); // Schema is authoritative when it has an opinion; heuristics fill the gap. const detected = schemaLookup.fieldType ?? detectType(selectedKey, original); const effectiveType = typeOverride === 'auto' ? detected : typeOverride; // Reset override when the selected key changes — the previous override // doesn't apply to a different leaf. useEffect(() => { setTypeOverride('auto'); }, [selectedKey]); useEffect(() => { let cancelled = false; setLoading(true); KVAPI.get(selectedKey) .then((r) => { if (cancelled) return; setOriginal(r.value); setDraft(r.value); setExists(r.exists); setRedisType(r.type); }) .catch((e) => onError(e.message)) .finally(() => { if (!cancelled) setLoading(false); }); return () => { cancelled = true; }; }, [selectedKey, reloadToken, onError]); const dirty = draft !== original; const editable = exists ? redisType === 'string' || redisType === '' : true; const save = async () => { setSaving(true); try { await KVAPI.put(selectedKey, draft); setOriginal(draft); setExists(true); setRedisType('string'); onSaved(); } catch (e: any) { onError(e.message); } finally { setSaving(false); } }; const del = async () => { if (!confirm(`Delete ${selectedKey}?`)) return; setSaving(true); try { await KVAPI.del(selectedKey); onDeleted(); } catch (e: any) { onError(e.message); } finally { setSaving(false); } }; if (loading) { return
loading…
; } if (!editable) { return (
Key has Redis type {redisType} — only string-typed values are editable here.
); } return (
{!exists && new key — save to create}
{exists && ( )}
); } function KeyHeader({ keyPath }: { keyPath: string }) { return (
{keyPath}
); } // Leaf value rendering moved to leafRenderers.tsx (LeafValueEditor) // so the file-detail view can reuse it per row.