import { useAtomValue, useSetAtom } from "jotai"; import { type FormEvent, useEffect, useState } from "react"; import { noteTypesAtom, syncActionAtom } from "../atoms"; import { localDeckRepository } from "../db/repositories"; interface Deck { id: string; name: string; description: string | null; defaultNoteTypeId: string | null; } interface EditDeckModalProps { isOpen: boolean; deck: Deck | null; onClose: () => void; onDeckUpdated: () => void; } export function EditDeckModal(props: EditDeckModalProps) { if (!props.isOpen || !props.deck) { return null; } // Render the body only when actually open so the suspense-driven note types // query does not fire on every host render (e.g. HomePage keeps the modal // mounted at all times). return ; } interface EditDeckModalContentProps extends EditDeckModalProps { deck: Deck; } function EditDeckModalContent({ deck, onClose, onDeckUpdated, }: EditDeckModalContentProps) { const [name, setName] = useState(deck.name); const [description, setDescription] = useState(deck.description ?? ""); const [defaultNoteTypeId, setDefaultNoteTypeId] = useState( deck.defaultNoteTypeId, ); const [error, setError] = useState(null); const [isSubmitting, setIsSubmitting] = useState(false); const noteTypesQuery = useAtomValue(noteTypesAtom); const noteTypes = noteTypesQuery.data ?? []; const triggerSync = useSetAtom(syncActionAtom); useEffect(() => { setName(deck.name); setDescription(deck.description ?? ""); setDefaultNoteTypeId(deck.defaultNoteTypeId); setError(null); }, [deck]); const handleClose = () => { setError(null); onClose(); }; const handleSubmit = async (e: FormEvent) => { e.preventDefault(); setError(null); setIsSubmitting(true); try { const updated = await localDeckRepository.update(deck.id, { name: name.trim(), description: description.trim() || null, defaultNoteTypeId: defaultNoteTypeId || null, }); if (!updated) { setError("Deck not found."); return; } onDeckUpdated(); onClose(); void triggerSync().catch(() => {}); } catch { setError("Failed to update deck. Please try again."); } finally { setIsSubmitting(false); } }; return ( { if (e.target === e.currentTarget) { handleClose(); } }} onKeyDown={(e) => { if (e.key === "Escape") { handleClose(); } }} > Edit Deck {error && ( {error} )} Name setName(e.target.value)} required maxLength={255} disabled={isSubmitting} className="w-full px-4 py-2.5 bg-ivory border border-border rounded-lg text-slate placeholder-muted transition-all duration-200 hover:border-muted focus:border-primary focus:ring-2 focus:ring-primary/10 disabled:opacity-50 disabled:cursor-not-allowed" /> Description{" "} (optional) setDescription(e.target.value)} maxLength={1000} disabled={isSubmitting} rows={3} className="w-full px-4 py-2.5 bg-ivory border border-border rounded-lg text-slate placeholder-muted transition-all duration-200 hover:border-muted focus:border-primary focus:ring-2 focus:ring-primary/10 disabled:opacity-50 disabled:cursor-not-allowed resize-none" /> Default Note Type{" "} (optional) setDefaultNoteTypeId(e.target.value || null)} disabled={isSubmitting} className="w-full px-4 py-2.5 bg-ivory border border-border rounded-lg text-slate transition-all duration-200 hover:border-muted focus:border-primary focus:ring-2 focus:ring-primary/10 disabled:opacity-50 disabled:cursor-not-allowed" > None {noteTypes.map((nt) => ( {nt.name} ))} Cancel {isSubmitting ? "Saving..." : "Save Changes"} ); }