"use client"; import { useState, useEffect } from "react"; import { CardResponse } from "@/lib/types/cards"; import { cn } from "@/lib/utils"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; import { Progress } from "@/components/ui/progress"; import { processReview, gradeCard } from "@/actions/srs"; import "./cards.css"; interface StudyCardProps { card: CardResponse; userId: number; onComplete: (newCard: CardResponse) => void; onSkip?: () => void; } export default function StudyCard({ card, userId, onComplete, onSkip, }: StudyCardProps) { const [isFlipped, setIsFlipped] = useState(false); const [startTime, setStartTime] = useState(0); const [isSubmitting, setIsSubmitting] = useState(false); // Reset the timer when a new card is shown useEffect(() => { setIsFlipped(false); setStartTime(Date.now()); }, [card.id]); // Toggle card flip const flipCard = () => { if (!isFlipped) { setIsFlipped(true); } }; // Calculate time spent on card in milliseconds const getReviewTime = () => { return Date.now() - startTime; }; // Handle card grading (Good/Again) const handleGrade = async (isCorrect: boolean) => { if (isSubmitting) return; setIsSubmitting(true); try { const result = await gradeCard(userId, card.id, isCorrect); if ("error" in result) { console.error("Error grading card:", result.error); } else { onComplete(result as CardResponse); } } catch (error) { console.error("Error processing review:", error); } finally { setIsSubmitting(false); } }; // Handle detailed grading with accuracy level const handleDetailedGrade = async (accuracy: number) => { if (isSubmitting) return; setIsSubmitting(true); try { const reviewTime = getReviewTime(); const result = await processReview(userId, card.id, accuracy, reviewTime); if ("error" in result) { console.error("Error processing review:", result.error); } else { onComplete(result as CardResponse); } } catch (error) { console.error("Error processing review:", error); } finally { setIsSubmitting(false); } }; // Calculate progress percentage for the card const getProgressPercentage = () => { const { interval, easeFactor } = card.progress; // Assuming max interval is 365 days and max ease factor is 4.0 const intervalProgress = Math.min(interval / 365, 1) * 70; // 70% weight to interval const easeProgress = Math.min((easeFactor - 1) / 3, 1) * 30; // 30% weight to ease factor return intervalProgress + easeProgress; }; // Format content based on card type const formatCardContent = (content: string, isBack: boolean = false) => { // You can add more sophisticated formatting here based on card type return content; }; // Render IPA pronunciation if available const renderIPA = () => { if (card.expression.ipa && card.expression.ipa.length > 0) { return (
/{card.expression.ipa[0]!.ipa}/
); } return null; }; // Render senses/meanings if available const renderSenses = () => { if (card.expression.senses && card.expression.senses.length > 0) { return (
{card.expression.senses.map((sense, index) => (
{sense.pos && ( {sense.pos} )} {sense.senses && sense.senses.map((subsense, i) => (
{subsense.glosses && subsense.glosses.map((gloss, j) => (
{j + 1}. {gloss}
))}
))}
))}
); } return null; }; // Show bookmarked status if applicable const renderBookmarked = () => { if (card.expression.isBookmarked) { return
; } return null; }; return (
{/* Front of card */}
{renderBookmarked()}
{card.expression.spelling}
{!isFlipped && renderIPA()}
{formatCardContent(card.text)}
{card.note && (
{card.note}
)} {!isFlipped && (
Click to flip
)}
{/* Back of card */}
{renderBookmarked()}
{card.expression.spelling}
{renderIPA()}
{formatCardContent(card.text, true)}
{card.note && (
{card.note}
)} {renderSenses()}
How well did you remember this?
{/* Optional: Detailed grading */}
{/* Progress bar */}
Interval: {card.progress.interval} days Ease: {card.progress.easeFactor.toFixed(1)}
{/* Skip button */} {onSkip && ( )}
); }