From d47d1a014a71ae65cbbf1b384eed87c6fe078b07 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sat, 2 May 2026 11:58:13 +0900 Subject: feat(note-types): make note type CRUD work fully offline-first MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CreateNoteTypeModal, DeleteNoteTypeModal, and the NoteTypeEditor (which covers field add/edit/delete/reorder) now write through the local IndexedDB repositories and fire-and-forget syncActionAtom, mirroring the deck-CRUD pattern. The dead EditNoteTypeModal — never imported — is removed. The local hasNotes / hasNoteFieldValues guards mirror the server's delete-time checks so a note type with attached notes, or a field with saved values, can't be silently soft-deleted offline. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/client/components/EditNoteTypeModal.tsx | 226 ---------------------------- 1 file changed, 226 deletions(-) delete mode 100644 src/client/components/EditNoteTypeModal.tsx (limited to 'src/client/components/EditNoteTypeModal.tsx') diff --git a/src/client/components/EditNoteTypeModal.tsx b/src/client/components/EditNoteTypeModal.tsx deleted file mode 100644 index 5916ff0..0000000 --- a/src/client/components/EditNoteTypeModal.tsx +++ /dev/null @@ -1,226 +0,0 @@ -import { useAtomValue } from "jotai"; -import { type FormEvent, useEffect, useState } from "react"; -import { ApiClientError, apiClient } from "../api"; -import { isOnlineAtom } from "../atoms"; - -interface NoteType { - id: string; - name: string; - frontTemplate: string; - backTemplate: string; - isReversible: boolean; -} - -interface EditNoteTypeModalProps { - isOpen: boolean; - noteType: NoteType | null; - onClose: () => void; - onNoteTypeUpdated: () => void; -} - -export function EditNoteTypeModal({ - isOpen, - noteType, - onClose, - onNoteTypeUpdated, -}: EditNoteTypeModalProps) { - const [name, setName] = useState(""); - const [frontTemplate, setFrontTemplate] = useState(""); - const [backTemplate, setBackTemplate] = useState(""); - const [isReversible, setIsReversible] = useState(false); - const [error, setError] = useState(null); - const [isSubmitting, setIsSubmitting] = useState(false); - const isOnline = useAtomValue(isOnlineAtom); - - // Sync form state when noteType changes - useEffect(() => { - if (noteType) { - setName(noteType.name); - setFrontTemplate(noteType.frontTemplate); - setBackTemplate(noteType.backTemplate); - setIsReversible(noteType.isReversible); - setError(null); - } - }, [noteType]); - - const handleClose = () => { - setError(null); - onClose(); - }; - - const handleSubmit = async (e: FormEvent) => { - e.preventDefault(); - if (!noteType) return; - - setError(null); - setIsSubmitting(true); - - try { - const res = await apiClient.rpc.api["note-types"][":id"].$put({ - param: { id: noteType.id }, - json: { - name: name.trim(), - frontTemplate: frontTemplate.trim(), - backTemplate: backTemplate.trim(), - isReversible, - }, - }); - await apiClient.handleResponse(res); - - onNoteTypeUpdated(); - onClose(); - } catch (err) { - if (err instanceof ApiClientError) { - setError(err.message); - } else { - setError("Failed to update note type. Please try again."); - } - } finally { - setIsSubmitting(false); - } - }; - - if (!isOpen || !noteType) { - return null; - } - - return ( -
{ - if (e.target === e.currentTarget) { - handleClose(); - } - }} - onKeyDown={(e) => { - if (e.key === "Escape") { - handleClose(); - } - }} - > -
-
-

- Edit Note Type -

- -
- {error && ( -
- {error} -
- )} - -
- - 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" - /> -
- -
- -