import { faChevronLeft, faCirclePlay, faFile, faPen, faPlus, faSpinner, faTrash, } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useCallback, useEffect, useState } from "react"; import { Link, useParams } from "wouter"; import { ApiClientError, apiClient } from "../api"; import { CreateNoteModal } from "../components/CreateNoteModal"; import { DeleteCardModal } from "../components/DeleteCardModal"; import { EditCardModal } from "../components/EditCardModal"; import { EditNoteModal } from "../components/EditNoteModal"; interface Card { id: string; deckId: string; noteId: string | null; isReversed: boolean | null; front: string; back: string; state: number; due: string; reps: number; lapses: number; createdAt: string; updatedAt: string; } interface Deck { id: string; name: string; description: string | null; } const CardStateLabels: Record = { 0: "New", 1: "Learning", 2: "Review", 3: "Relearning", }; const CardStateColors: Record = { 0: "bg-info/10 text-info", 1: "bg-warning/10 text-warning", 2: "bg-success/10 text-success", 3: "bg-error/10 text-error", }; export function DeckDetailPage() { const { deckId } = useParams<{ deckId: string }>(); const [deck, setDeck] = useState(null); const [cards, setCards] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); const [editingCard, setEditingCard] = useState(null); const [editingNoteId, setEditingNoteId] = useState(null); const [deletingCard, setDeletingCard] = useState(null); const fetchDeck = useCallback(async () => { if (!deckId) return; const authHeader = apiClient.getAuthHeader(); if (!authHeader) { throw new ApiClientError("Not authenticated", 401); } const res = await fetch(`/api/decks/${deckId}`, { 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(); setDeck(data.deck); }, [deckId]); const fetchCards = useCallback(async () => { if (!deckId) return; const authHeader = apiClient.getAuthHeader(); if (!authHeader) { throw new ApiClientError("Not authenticated", 401); } const res = await fetch(`/api/decks/${deckId}/cards`, { 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(); setCards(data.cards); }, [deckId]); const fetchData = useCallback(async () => { setIsLoading(true); setError(null); try { await Promise.all([fetchDeck(), fetchCards()]); } catch (err) { if (err instanceof ApiClientError) { setError(err.message); } else { setError("Failed to load data. Please try again."); } } finally { setIsLoading(false); } }, [fetchDeck, fetchCards]); useEffect(() => { fetchData(); }, [fetchData]); if (!deckId) { return (

Invalid deck ID

Back to decks
); } return (
{/* Header */}
{/* Main Content */}
{/* Loading State */} {isLoading && (
)} {/* Error State */} {error && (
{error}
)} {/* Deck Content */} {!isLoading && !error && deck && (
{/* Deck Header */}

{deck.name}

{deck.description && (

{deck.description}

)}
{/* Study Button */}
{/* Cards Section */}

Cards{" "} ({cards.length})

{/* Empty State */} {cards.length === 0 && (

No cards yet

Add notes to start studying

)} {/* Card List */} {cards.length > 0 && (
{cards.map((card, index) => (
{/* Front/Back Preview */}
Front

{card.front}

Back

{card.back}

{/* Card Stats */}
{CardStateLabels[card.state] || "Unknown"} {card.isReversed && ( Reversed )} {card.reps} reviews {card.lapses > 0 && ( {card.lapses} lapses )}
{/* Actions */}
))}
)}
)}
{/* Modals */} {deckId && ( setIsCreateModalOpen(false)} onNoteCreated={fetchCards} /> )} {deckId && ( setEditingCard(null)} onCardUpdated={fetchCards} /> )} {deckId && ( setEditingNoteId(null)} onNoteUpdated={fetchCards} /> )} {deckId && ( setDeletingCard(null)} onCardDeleted={fetchCards} /> )}
); }