From f243847216279cbd43879de8b5ef6dcceb3a2f1d Mon Sep 17 00:00:00 2001 From: polwex Date: Thu, 29 May 2025 14:08:02 +0700 Subject: lets see --- src/components/Flashcard/StudyCard.tsx | 265 +++++++++++++++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 src/components/Flashcard/StudyCard.tsx (limited to 'src/components/Flashcard/StudyCard.tsx') diff --git a/src/components/Flashcard/StudyCard.tsx b/src/components/Flashcard/StudyCard.tsx new file mode 100644 index 0000000..4e554b4 --- /dev/null +++ b/src/components/Flashcard/StudyCard.tsx @@ -0,0 +1,265 @@ +"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 && ( + + )} +
+ ); +} \ No newline at end of file -- cgit v1.2.3