aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/client/pages/StudyPage.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/pages/StudyPage.tsx')
-rw-r--r--src/client/pages/StudyPage.tsx47
1 files changed, 38 insertions, 9 deletions
diff --git a/src/client/pages/StudyPage.tsx b/src/client/pages/StudyPage.tsx
index 5bd31c0..bdaf7e3 100644
--- a/src/client/pages/StudyPage.tsx
+++ b/src/client/pages/StudyPage.tsx
@@ -5,13 +5,16 @@ import {
faSpinner,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { useCallback, useEffect, useRef, useState } from "react";
+import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Link, useParams } from "wouter";
import { ApiClientError, apiClient } from "../api";
+import { renderCard } from "../utils/templateRenderer";
interface Card {
id: string;
deckId: string;
+ noteId: string | null;
+ isReversed: boolean | null;
front: string;
back: string;
state: number;
@@ -20,6 +23,13 @@ interface Card {
difficulty: number;
reps: number;
lapses: number;
+ /** Note type templates for rendering (null for legacy cards) */
+ noteType: {
+ frontTemplate: string;
+ backTemplate: string;
+ } | null;
+ /** Field values as a name-value map for template rendering */
+ fieldValuesMap: Record<string, string>;
}
interface Deck {
@@ -222,6 +232,30 @@ export function StudyPage() {
return () => window.removeEventListener("keydown", handleKeyDown);
}, [handleKeyDown]);
+ const currentCard = cards[currentIndex];
+ const isSessionComplete = currentIndex >= cards.length && cards.length > 0;
+ const hasNoCards = !isLoading && cards.length === 0;
+ const remainingCards = cards.length - currentIndex;
+
+ // Compute rendered card content for both legacy and note-based cards
+ const cardContent = useMemo(() => {
+ if (!currentCard) return null;
+
+ // Note-based card: use template rendering
+ if (currentCard.noteType && currentCard.fieldValuesMap) {
+ const rendered = renderCard({
+ frontTemplate: currentCard.noteType.frontTemplate,
+ backTemplate: currentCard.noteType.backTemplate,
+ fieldValues: currentCard.fieldValuesMap,
+ isReversed: currentCard.isReversed ?? false,
+ });
+ return { front: rendered.front, back: rendered.back };
+ }
+
+ // Legacy card: use front/back directly
+ return { front: currentCard.front, back: currentCard.back };
+ }, [currentCard]);
+
if (!deckId) {
return (
<div className="min-h-screen bg-cream flex items-center justify-center">
@@ -238,11 +272,6 @@ export function StudyPage() {
);
}
- const currentCard = cards[currentIndex];
- const isSessionComplete = currentIndex >= cards.length && cards.length > 0;
- const hasNoCards = !isLoading && cards.length === 0;
- const remainingCards = cards.length - currentIndex;
-
return (
<div className="min-h-screen bg-cream flex flex-col">
{/* Header */}
@@ -383,7 +412,7 @@ export function StudyPage() {
)}
{/* Active Study Card */}
- {currentCard && !isSessionComplete && (
+ {currentCard && cardContent && !isSessionComplete && (
<div data-testid="study-card" className="flex-1 flex flex-col">
{/* Card */}
<button
@@ -406,7 +435,7 @@ export function StudyPage() {
data-testid="card-front"
className="text-xl md:text-2xl text-ink font-medium whitespace-pre-wrap break-words leading-relaxed"
>
- {currentCard.front}
+ {cardContent.front}
</p>
<p className="mt-8 text-muted text-sm flex items-center gap-2">
<kbd className="px-2 py-0.5 bg-ivory rounded text-xs font-mono">
@@ -420,7 +449,7 @@ export function StudyPage() {
data-testid="card-back"
className="text-xl md:text-2xl text-ink font-medium whitespace-pre-wrap break-words leading-relaxed animate-fade-in"
>
- {currentCard.back}
+ {cardContent.back}
</p>
)}
</button>