"use client"; import { useEffect, useRef, useState, useTransition } from "react"; type EditableCellProps = { value: string; onSave: (next: string) => Promise<{ ok: boolean; error?: string }>; label?: string; isCurrentlyEditing?: boolean; onEditStart?: () => void; onEditEnd?: () => void; }; export default function EditableCell({ value, onSave, label, isCurrentlyEditing, onEditStart, onEditEnd, }: EditableCellProps) { const [editing, setEditing] = useState(false); const [draft, setDraft] = useState(value); const [error, setError] = useState(null); const [isPending, startTransition] = useTransition(); const inputRef = useRef(null); // Keep draft in sync if the underlying value changes from outside // (auto-refresh, server revalidation) while we are NOT actively editing. useEffect(() => { if (!editing) setDraft(value); }, [value, editing]); // Auto-clear an error after 3 seconds. useEffect(() => { if (!error) return; const id = setTimeout(() => setError(null), 3000); return () => clearTimeout(id); }, [error]); function begin() { setDraft(value); setEditing(true); onEditStart?.(); requestAnimationFrame(() => { inputRef.current?.focus(); inputRef.current?.select(); }); } function cancel() { setEditing(false); setDraft(value); setError(null); onEditEnd?.(); } function commit() { const next = draft; if (next === value) { cancel(); return; } startTransition(async () => { const result = await onSave(next); if (result.ok) { setEditing(false); setError(null); onEditEnd?.(); } else { setError(result.error ?? "Save failed"); } }); } if (!editing) { return ( ); } return (
setDraft(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); commit(); } else if (e.key === "Escape") { e.preventDefault(); cancel(); } }} disabled={isPending} className="w-full min-w-0 rounded-sm border-2 border-yellow-400 bg-white px-1 py-0.5 font-mono text-sm text-zinc-900 outline-none disabled:opacity-60" />
{error && (

{error}

)}
); }