From b51d4efaa1e5e0417d4306c02797f424938766cb Mon Sep 17 00:00:00 2001 From: nsfisis Date: Wed, 31 Dec 2025 02:38:05 +0900 Subject: feat(client): add NoteTypesPage for note type management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement Phase 6 of the roadmap - NoteType list page with CRUD modals: - NoteTypesPage displays all user's note types with templates and reversible badge - CreateNoteTypeModal for creating new note types with templates - EditNoteTypeModal for updating existing note types - DeleteNoteTypeModal with constraint warning - Navigation link from HomePage header - Comprehensive tests for all components (65 new tests) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/client/pages/NoteTypesPage.tsx | 272 +++++++++++++++++++++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100644 src/client/pages/NoteTypesPage.tsx (limited to 'src/client/pages/NoteTypesPage.tsx') diff --git a/src/client/pages/NoteTypesPage.tsx b/src/client/pages/NoteTypesPage.tsx new file mode 100644 index 0000000..0a34f5b --- /dev/null +++ b/src/client/pages/NoteTypesPage.tsx @@ -0,0 +1,272 @@ +import { + faArrowLeft, + faBoxOpen, + faLayerGroup, + faPen, + faPlus, + faSpinner, + faTrash, +} from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { useCallback, useEffect, useState } from "react"; +import { Link } from "wouter"; +import { ApiClientError, apiClient } from "../api"; +import { CreateNoteTypeModal } from "../components/CreateNoteTypeModal"; +import { DeleteNoteTypeModal } from "../components/DeleteNoteTypeModal"; +import { EditNoteTypeModal } from "../components/EditNoteTypeModal"; + +interface NoteType { + id: string; + name: string; + frontTemplate: string; + backTemplate: string; + isReversible: boolean; + createdAt: string; + updatedAt: string; +} + +export function NoteTypesPage() { + const [noteTypes, setNoteTypes] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); + const [editingNoteType, setEditingNoteType] = useState(null); + const [deletingNoteType, setDeletingNoteType] = useState( + null, + ); + + const fetchNoteTypes = useCallback(async () => { + setIsLoading(true); + setError(null); + + try { + const authHeader = apiClient.getAuthHeader(); + if (!authHeader) { + throw new ApiClientError("Not authenticated", 401); + } + + const res = await fetch("/api/note-types", { + headers: authHeader, + }); + + if (!res.ok) { + const errorBody = await res.json().catch(() => ({})); + throw new ApiClientError( + (errorBody as { error?: string }).error || + `Request failed with status ${res.status}`, + res.status, + ); + } + + const data = await res.json(); + setNoteTypes(data.noteTypes); + } catch (err) { + if (err instanceof ApiClientError) { + setError(err.message); + } else { + setError("Failed to load note types. Please try again."); + } + } finally { + setIsLoading(false); + } + }, []); + + useEffect(() => { + fetchNoteTypes(); + }, [fetchNoteTypes]); + + return ( +
+ {/* Header */} +
+
+
+ +
+
+
+ + {/* Main Content */} +
+ {/* Section Header */} +
+

+ Note types define how your cards are structured +

+ +
+ + {/* Loading State */} + {isLoading && ( +
+
+ )} + + {/* Error State */} + {error && ( +
+ {error} + +
+ )} + + {/* Empty State */} + {!isLoading && !error && noteTypes.length === 0 && ( +
+
+
+

+ No note types yet +

+

+ Create a note type to define how your cards are structured +

+ +
+ )} + + {/* Note Type List */} + {!isLoading && !error && noteTypes.length > 0 && ( +
+ {noteTypes.map((noteType, index) => ( +
+
+
+
+
+
+ + Front: {noteType.frontTemplate} + + + Back: {noteType.backTemplate} + + {noteType.isReversible && ( + + Reversible + + )} +
+
+
+ + +
+
+
+ ))} +
+ )} +
+ + {/* Modals */} + setIsCreateModalOpen(false)} + onNoteTypeCreated={fetchNoteTypes} + /> + + setEditingNoteType(null)} + onNoteTypeUpdated={fetchNoteTypes} + /> + + setDeletingNoteType(null)} + onNoteTypeDeleted={fetchNoteTypes} + /> +
+ ); +} -- cgit v1.2.3-70-g09d2