From 8dbe5c2a1d8dc15bbdd6810b2582c680e1c0bb9b Mon Sep 17 00:00:00 2001 From: nsfisis Date: Thu, 1 Jan 2026 22:06:40 +0900 Subject: feat(import): add CSV bulk import for notes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add client-side CSV parsing and bulk import API endpoint for importing notes from CSV files. Supports quoted fields, newlines in values, and escaped quotes. - New POST /api/decks/{deckId}/notes/import endpoint for bulk creation - CSV parser with RFC 4180 compliance - Multi-phase import modal (upload → validate → preview → import) - Client-side validation with per-row error reporting 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/client/pages/DeckDetailPage.tsx | 50 ++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 12 deletions(-) (limited to 'src/client/pages') diff --git a/src/client/pages/DeckDetailPage.tsx b/src/client/pages/DeckDetailPage.tsx index d018d1f..3741111 100644 --- a/src/client/pages/DeckDetailPage.tsx +++ b/src/client/pages/DeckDetailPage.tsx @@ -2,6 +2,7 @@ import { faChevronLeft, faCirclePlay, faFile, + faFileImport, faLayerGroup, faPen, faPlus, @@ -17,6 +18,7 @@ import { DeleteCardModal } from "../components/DeleteCardModal"; import { DeleteNoteModal } from "../components/DeleteNoteModal"; import { EditCardModal } from "../components/EditCardModal"; import { EditNoteModal } from "../components/EditNoteModal"; +import { ImportNotesModal } from "../components/ImportNotesModal"; interface Card { id: string; @@ -183,6 +185,7 @@ export function DeckDetailPage() { const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); + const [isImportModalOpen, setIsImportModalOpen] = useState(false); const [editingCard, setEditingCard] = useState(null); const [editingNoteId, setEditingNoteId] = useState(null); const [deletingCard, setDeletingCard] = useState(null); @@ -397,18 +400,32 @@ export function DeckDetailPage() { Cards{" "} ({cards.length}) - +
+ + +
{/* Empty State */} @@ -471,6 +488,15 @@ export function DeckDetailPage() { /> )} + {deckId && ( + setIsImportModalOpen(false)} + onImportComplete={fetchCards} + /> + )} + {deckId && (