From 38b8fc0e9927c4146b4c8b309b2bcc644abd63d0 Mon Sep 17 00:00:00 2001
From: nsfisis <nsfisis@gmail.com>
Date: Wed, 25 Feb 2026 23:02:35 +0900
Subject: feat(decks): add default note type setting per deck

Allow each deck to specify a default note type that is auto-selected
when creating new notes. Includes DB schema migration, server API
updates, sync layer support, and UI for editing the default in the
deck settings modal.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---
 src/client/components/EditDeckModal.tsx | 60 ++++++++++++++++++++++++++++++++-
 1 file changed, 59 insertions(+), 1 deletion(-)

(limited to 'src/client/components/EditDeckModal.tsx')

diff --git a/src/client/components/EditDeckModal.tsx b/src/client/components/EditDeckModal.tsx
index 8e95295..9a79de8 100644
--- a/src/client/components/EditDeckModal.tsx
+++ b/src/client/components/EditDeckModal.tsx
@@ -1,10 +1,16 @@
-import { type FormEvent, useEffect, useState } from "react";
+import { type FormEvent, useCallback, useEffect, useState } from "react";
 import { ApiClientError, apiClient } from "../api";
 
 interface Deck {
 	id: string;
 	name: string;
 	description: string | null;
+	defaultNoteTypeId: string | null;
+}
+
+interface NoteTypeSummary {
+	id: string;
+	name: string;
 }
 
 interface EditDeckModalProps {
@@ -22,18 +28,45 @@ export function EditDeckModal({
 }: EditDeckModalProps) {
 	const [name, setName] = useState("");
 	const [description, setDescription] = useState("");
+	const [defaultNoteTypeId, setDefaultNoteTypeId] = useState<string | null>(
+		null,
+	);
+	const [noteTypes, setNoteTypes] = useState<NoteTypeSummary[]>([]);
+	const [isLoadingNoteTypes, setIsLoadingNoteTypes] = useState(false);
 	const [error, setError] = useState<string | null>(null);
 	const [isSubmitting, setIsSubmitting] = useState(false);
 
+	const fetchNoteTypes = useCallback(async () => {
+		setIsLoadingNoteTypes(true);
+		try {
+			const res = await apiClient.rpc.api["note-types"].$get();
+			const data = await apiClient.handleResponse<{
+				noteTypes: NoteTypeSummary[];
+			}>(res);
+			setNoteTypes(data.noteTypes);
+		} catch {
+			// Non-critical: note type list is optional
+		} finally {
+			setIsLoadingNoteTypes(false);
+		}
+	}, []);
+
 	// Sync form state when deck changes
 	useEffect(() => {
 		if (deck) {
 			setName(deck.name);
 			setDescription(deck.description ?? "");
+			setDefaultNoteTypeId(deck.defaultNoteTypeId);
 			setError(null);
 		}
 	}, [deck]);
 
+	useEffect(() => {
+		if (isOpen) {
+			fetchNoteTypes();
+		}
+	}, [isOpen, fetchNoteTypes]);
+
 	const handleClose = () => {
 		setError(null);
 		onClose();
@@ -52,6 +85,7 @@ export function EditDeckModal({
 				json: {
 					name: name.trim(),
 					description: description.trim() || null,
+					defaultNoteTypeId: defaultNoteTypeId || null,
 				},
 			});
 			await apiClient.handleResponse(res);
@@ -147,6 +181,30 @@ export function EditDeckModal({
 							/>
 						</div>
 
+						<div>
+							<label
+								htmlFor="edit-deck-default-note-type"
+								className="block text-sm font-medium text-slate mb-1.5"
+							>
+								Default Note Type{" "}
+								<span className="text-muted font-normal">(optional)</span>
+							</label>
+							<select
+								id="edit-deck-default-note-type"
+								value={defaultNoteTypeId ?? ""}
+								onChange={(e) => setDefaultNoteTypeId(e.target.value || null)}
+								disabled={isSubmitting || isLoadingNoteTypes}
+								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"
+							>
+								<option value="">None</option>
+								{noteTypes.map((nt) => (
+									<option key={nt.id} value={nt.id}>
+										{nt.name}
+									</option>
+								))}
+							</select>
+						</div>
+
 						<div className="flex gap-3 justify-end pt-2">
 							<button
 								type="button"
-- 
cgit v1.3-1-g0d28

