story-generator/src/components/phonics/ExercisePlayer.tsx

147 lines
4.6 KiB
TypeScript

import { useState, useEffect } from "react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Progress } from "@/components/ui/progress";
import { useExerciseWords } from "@/hooks/phonics/useExerciseAttempt";
import { useUpdatePhonicsProgress } from "@/hooks/phonics/usePhonicsProgress";
import { ExerciseFactory } from "./exercises/ExerciseFactory";
import { Timer } from "lucide-react";
import type { PhonicsExercise, PhonicsWord } from "@/types/phonics";
import { cn } from "@/lib/utils";
interface ExercisePlayerProps {
exercise: PhonicsExercise;
studentId: string;
onComplete: () => void;
onExit: () => void;
}
export function ExercisePlayer({
exercise,
studentId,
onComplete,
onExit
}: ExercisePlayerProps) {
const [currentStep, setCurrentStep] = useState(0);
const [score, setScore] = useState(0);
const [timeSpent, setTimeSpent] = useState(0);
const [answers, setAnswers] = useState<string[]>([]);
const [mistakes, setMistakes] = useState<string[]>([]);
const [showFeedback, setShowFeedback] = useState(false);
const [lastAnswerCorrect, setLastAnswerCorrect] = useState<boolean | null>(null);
const { data: exerciseWords, isLoading } = useExerciseWords(exercise.id);
const updateProgress = useUpdatePhonicsProgress();
useEffect(() => {
const timer = setInterval(() => {
setTimeSpent((prev) => prev + 1);
}, 1000);
return () => clearInterval(timer);
}, []);
const handleAnswer = async (word: string, isCorrect: boolean) => {
setLastAnswerCorrect(isCorrect);
setShowFeedback(true);
if (isCorrect) {
setScore((prev) => prev + 1);
setAnswers((prev) => [...prev, word]);
} else {
setMistakes((prev) => [...prev, word]);
}
// Aguardar feedback antes de prosseguir
await new Promise(resolve => setTimeout(resolve, 1500));
setShowFeedback(false);
if (currentStep < (exerciseWords?.length || 0) - 1) {
setCurrentStep((prev) => prev + 1);
} else {
handleComplete();
}
};
const handleComplete = async () => {
const finalScore = score / (exerciseWords?.length || 1);
const stars = Math.ceil(finalScore * 3);
await updateProgress.mutateAsync({
studentId,
exerciseId: exercise.id,
attempts: 1,
bestScore: finalScore,
lastScore: finalScore,
completed: finalScore >= exercise.requiredScore,
stars,
xpEarned: Math.round(finalScore * exercise.points)
});
onComplete();
};
if (isLoading || !exerciseWords?.length) {
return (
<Card className="w-full max-w-2xl mx-auto">
<CardContent className="py-8">
<div className="text-center text-muted-foreground">
Carregando exercício...
</div>
</CardContent>
</Card>
);
}
const progress = ((currentStep + 1) / exerciseWords.length) * 100;
const currentWord = exerciseWords[currentStep].word as unknown as PhonicsWord;
return (
<Card className={cn(
"w-full max-w-2xl mx-auto transition-colors duration-500",
showFeedback && lastAnswerCorrect && "bg-green-50",
showFeedback && !lastAnswerCorrect && "bg-red-50"
)}>
<CardHeader>
<div className="flex justify-between items-center">
<CardTitle>{exercise.title}</CardTitle>
<div className="flex items-center gap-4">
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<Timer className="w-4 h-4" />
<span>{Math.floor(timeSpent / 60)}:{(timeSpent % 60).toString().padStart(2, '0')}</span>
</div>
<Button variant="outline" size="sm" onClick={onExit}>
Sair
</Button>
</div>
</div>
<Progress value={progress} className="h-2" />
</CardHeader>
<CardContent>
<div className="space-y-6">
<div className="text-center text-muted-foreground mb-8">
Exercício {currentStep + 1} de {exerciseWords.length}
</div>
<ExerciseFactory
type={exercise.exerciseType}
currentWord={currentWord}
options={exerciseWords[currentStep].options}
onAnswer={handleAnswer}
disabled={showFeedback}
/>
{showFeedback && (
<div className={cn(
"text-center text-lg font-medium py-4 rounded-lg",
lastAnswerCorrect ? "text-green-600" : "text-red-600"
)}>
{lastAnswerCorrect ? "Muito bem!" : "Tente novamente na próxima!"}
</div>
)}
</div>
</CardContent>
</Card>
);
}