diff --git a/src/components/phonics/AudioPlayer.tsx b/src/components/phonics/AudioPlayer.tsx index 09efbf7..d6da96e 100644 --- a/src/components/phonics/AudioPlayer.tsx +++ b/src/components/phonics/AudioPlayer.tsx @@ -50,6 +50,7 @@ export function AudioPlayer({ word, disabled }: AudioPlayerProps) { className="gap-2" onClick={playAudio} disabled={disabled || isLoading} + trackingId="audio-player-toggle" > {isLoading ? ( diff --git a/src/components/phonics/ExerciseCard.tsx b/src/components/phonics/ExerciseCard.tsx index 143ce9a..871776d 100644 --- a/src/components/phonics/ExerciseCard.tsx +++ b/src/components/phonics/ExerciseCard.tsx @@ -2,19 +2,20 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Progress } from "@/components/ui/progress"; -import { Clock, Star } from "lucide-react"; -import type { PhonicsExercise, PhonicsProgress } from "@/types/phonics"; +import { Clock, Star, Timer } from "lucide-react"; +import { cn } from "@/lib/utils"; +import type { PhonicsExercise, StudentPhonicsProgress } from "@/types/phonics"; interface ExerciseCardProps { exercise: PhonicsExercise; - progress?: PhonicsProgress; + progress?: StudentPhonicsProgress; onStart: (exerciseId: string) => void; } export function ExerciseCard({ exercise, progress, onStart }: ExerciseCardProps) { const isCompleted = progress?.completed; const stars = progress?.stars || 0; - const progressValue = progress ? (progress.bestScore * 100) : 0; + const progressValue = progress ? (progress.best_score * 100) : 0; return ( @@ -34,7 +35,7 @@ export function ExerciseCard({ exercise, progress, onStart }: ExerciseCardProps)
- {Math.ceil(exercise.estimatedTimeSeconds / 60)} min + {Math.ceil((exercise.estimated_time_seconds ?? 0) / 60)} min
{Array.from({ length: 3 }).map((_, i) => ( @@ -56,10 +57,11 @@ export function ExerciseCard({ exercise, progress, onStart }: ExerciseCardProps)
)} - diff --git a/src/components/phonics/ExerciseGrid.tsx b/src/components/phonics/ExerciseGrid.tsx index 6c16d70..4e3b068 100644 --- a/src/components/phonics/ExerciseGrid.tsx +++ b/src/components/phonics/ExerciseGrid.tsx @@ -39,7 +39,7 @@ export function ExerciseGrid({ categoryId, studentId, onSelectExercise }: Exerci p.exerciseId === exercise.id)} + progress={progress?.find((p) => p.exercise_id === exercise.id)} onStart={onSelectExercise} /> ))} diff --git a/src/components/phonics/ExercisePlayer.tsx b/src/components/phonics/ExercisePlayer.tsx index a215564..93fb04b 100644 --- a/src/components/phonics/ExercisePlayer.tsx +++ b/src/components/phonics/ExercisePlayer.tsx @@ -2,35 +2,36 @@ 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 type { PhonicsExercise, UpdateProgressParams } from "@/types/phonics"; import { cn } from "@/lib/utils"; interface ExercisePlayerProps { exercise: PhonicsExercise; - studentId: string; - onComplete: () => void; + student_id: string; + onComplete: (result: { + score: number; + stars: number; + xp_earned: number; + completed: boolean; + }) => void; onExit: () => void; } export function ExercisePlayer({ exercise, - studentId, + student_id, onComplete, onExit }: ExercisePlayerProps) { const [currentStep, setCurrentStep] = useState(0); const [score, setScore] = useState(0); const [timeSpent, setTimeSpent] = useState(0); - const [answers, setAnswers] = useState([]); - const [mistakes, setMistakes] = useState([]); const [showFeedback, setShowFeedback] = useState(false); const [lastAnswerCorrect, setLastAnswerCorrect] = useState(null); - const { data: exerciseWords, isLoading } = useExerciseWords(exercise.id); const updateProgress = useUpdatePhonicsProgress(); useEffect(() => { @@ -47,16 +48,16 @@ export function ExercisePlayer({ 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) { + // Filtra apenas as palavras corretas + const correctWords = exercise.words?.filter(w => w.is_correct_answer) || []; + + if (currentStep < correctWords.length - 1) { setCurrentStep((prev) => prev + 1); } else { handleComplete(); @@ -64,24 +65,36 @@ export function ExercisePlayer({ }; const handleComplete = async () => { - const finalScore = score / (exerciseWords?.length || 1); + // Filtra apenas as palavras corretas + const correctWords = exercise.words?.filter(w => w.is_correct_answer) || []; + const finalScore = score / correctWords.length; const stars = Math.ceil(finalScore * 3); + const xp_earned = Math.round(finalScore * exercise.points); + const completed = finalScore >= exercise.required_score; - await updateProgress.mutateAsync({ - studentId, - exerciseId: exercise.id, - attempts: 1, - bestScore: finalScore, - lastScore: finalScore, - completed: finalScore >= exercise.requiredScore, + const updateParams: UpdateProgressParams = { + student_id, + exercise_id: exercise.id, + best_score: finalScore, + last_score: finalScore, + completed, stars, - xpEarned: Math.round(finalScore * exercise.points) - }); + xp_earned, + time_spent_seconds: timeSpent, + correct_answers_count: score, + total_answers_count: correctWords.length + }; - onComplete(); + await updateProgress.mutateAsync(updateParams); + onComplete({ + score: finalScore, + stars, + xp_earned, + completed + }); }; - if (isLoading || !exerciseWords?.length) { + if (!exercise.words?.length) { return ( @@ -93,8 +106,21 @@ export function ExercisePlayer({ ); } - const progress = ((currentStep + 1) / exerciseWords.length) * 100; - const currentWord = exerciseWords[currentStep].word as unknown as PhonicsWord; + // Filtra apenas as palavras corretas e ordena por order_index + const correctWords = exercise.words + .filter(w => w.is_correct_answer) + .sort((a, b) => (a.order_index || 0) - (b.order_index || 0)); + + // Pega a palavra atual + const currentWord = correctWords[currentStep]; + + // Pega as opções (incluindo a palavra correta) + const options = exercise.words + .filter(w => w.order_index === currentWord.order_index) + .map(w => w.word) + .sort(() => Math.random() - 0.5); + + const progress = ((currentStep + 1) / correctWords.length) * 100; return ( {Math.floor(timeSpent / 60)}:{(timeSpent % 60).toString().padStart(2, '0')}
- @@ -121,13 +147,13 @@ export function ExercisePlayer({
- Exercício {currentStep + 1} de {exerciseWords.length} + Exercício {currentStep + 1} de {correctWords.length}
diff --git a/src/components/phonics/exercises/AlliterationExercise.tsx b/src/components/phonics/exercises/AlliterationExercise.tsx index 06a4175..2f8c2c3 100644 --- a/src/components/phonics/exercises/AlliterationExercise.tsx +++ b/src/components/phonics/exercises/AlliterationExercise.tsx @@ -1,46 +1,40 @@ import { Button } from "@/components/ui/button"; -import { BaseExercise, type BaseExerciseProps } from "./BaseExercise"; +import { Card } from "@/components/ui/card"; import { cn } from "@/lib/utils"; +import type { PhonicsWord } from "@/types/phonics"; -interface AlliterationExerciseProps extends BaseExerciseProps { - options: Array<{ - word: string; - hasSameInitialSound: boolean; - }>; +interface AlliterationExerciseProps { + currentWord: PhonicsWord; + options: PhonicsWord[]; + onAnswer: (word: string, isCorrect: boolean) => void; + disabled?: boolean; } -export function AlliterationExercise({ - currentWord, - onAnswer, - options, - disabled -}: AlliterationExerciseProps) { +export function AlliterationExercise({ currentWord, options, onAnswer, disabled }: AlliterationExerciseProps) { return ( -
- - -
-
- Qual palavra começa com o mesmo som que {currentWord.word}? -
+
+
+

Qual palavra começa com o mesmo som?

+
{currentWord.word}
+
-
- {options.map((option) => ( - - ))} -
+
+ {options.map((option) => ( + + ))}
); diff --git a/src/components/phonics/exercises/ExerciseFactory.tsx b/src/components/phonics/exercises/ExerciseFactory.tsx index 420dfaf..2d5fe60 100644 --- a/src/components/phonics/exercises/ExerciseFactory.tsx +++ b/src/components/phonics/exercises/ExerciseFactory.tsx @@ -1,20 +1,21 @@ -import type { PhonicsExerciseType, PhonicsWord } from "@/types/phonics"; import { RhymeExercise } from "./RhymeExercise"; import { AlliterationExercise } from "./AlliterationExercise"; import { SyllablesExercise } from "./SyllablesExercise"; -import { SoundMatchExercise } from "./SoundMatchExercise"; +import { InitialSoundExercise } from "./InitialSoundExercise"; +import { FinalSoundExercise } from "./FinalSoundExercise"; +import type { PhonicsWord } from "@/types/phonics"; interface ExerciseFactoryProps { - type: PhonicsExerciseType; + type_id: string; currentWord: PhonicsWord; - options: any; // Tipo específico para cada exercício + options: PhonicsWord[]; onAnswer: (word: string, isCorrect: boolean) => void; disabled?: boolean; } -export function ExerciseFactory({ type, currentWord, options, onAnswer, disabled }: ExerciseFactoryProps) { - switch (type) { - case 'rhyme': +export function ExerciseFactory({ type_id, currentWord, options, onAnswer, disabled }: ExerciseFactoryProps) { + switch (type_id) { + case '1': // Rima return ( ); - - case 'alliteration': + case '2': // Aliteração return ( ); - - case 'syllables': + case '3': // Sílabas return ( - ); - - case 'initial_sound': - return ( - ); - - case 'final_sound': + case '4': // Som Inicial return ( - + ); + case '5': // Som Final + return ( + ); - default: - return ( -
- Tipo de exercício não implementado: {type} -
- ); + return null; } } \ No newline at end of file diff --git a/src/components/phonics/exercises/FinalSoundExercise.tsx b/src/components/phonics/exercises/FinalSoundExercise.tsx new file mode 100644 index 0000000..c3f6eea --- /dev/null +++ b/src/components/phonics/exercises/FinalSoundExercise.tsx @@ -0,0 +1,41 @@ +import { Button } from "@/components/ui/button"; +import { Card } from "@/components/ui/card"; +import { cn } from "@/lib/utils"; +import type { PhonicsWord } from "@/types/phonics"; + +interface FinalSoundExerciseProps { + currentWord: PhonicsWord; + options: PhonicsWord[]; + onAnswer: (word: string, isCorrect: boolean) => void; + disabled?: boolean; +} + +export function FinalSoundExercise({ currentWord, options, onAnswer, disabled }: FinalSoundExerciseProps) { + return ( +
+
+

Qual palavra termina com o mesmo som?

+
{currentWord.word}
+
+ +
+ {options.map((option) => ( + + ))} +
+
+ ); +} \ No newline at end of file diff --git a/src/components/phonics/exercises/InitialSoundExercise.tsx b/src/components/phonics/exercises/InitialSoundExercise.tsx new file mode 100644 index 0000000..4ee78d2 --- /dev/null +++ b/src/components/phonics/exercises/InitialSoundExercise.tsx @@ -0,0 +1,41 @@ +import { Button } from "@/components/ui/button"; +import { Card } from "@/components/ui/card"; +import { cn } from "@/lib/utils"; +import type { PhonicsWord } from "@/types/phonics"; + +interface InitialSoundExerciseProps { + currentWord: PhonicsWord; + options: PhonicsWord[]; + onAnswer: (word: string, isCorrect: boolean) => void; + disabled?: boolean; +} + +export function InitialSoundExercise({ currentWord, options, onAnswer, disabled }: InitialSoundExerciseProps) { + return ( +
+
+

Qual palavra começa com o mesmo som?

+
{currentWord.word}
+
+ +
+ {options.map((option) => ( + + ))} +
+
+ ); +} \ No newline at end of file diff --git a/src/components/phonics/exercises/RhymeExercise.tsx b/src/components/phonics/exercises/RhymeExercise.tsx index d0ed69d..3f76f2e 100644 --- a/src/components/phonics/exercises/RhymeExercise.tsx +++ b/src/components/phonics/exercises/RhymeExercise.tsx @@ -1,41 +1,40 @@ import { Button } from "@/components/ui/button"; -import { BaseExercise, type BaseExerciseProps } from "./BaseExercise"; +import { Card } from "@/components/ui/card"; import { cn } from "@/lib/utils"; +import type { PhonicsWord } from "@/types/phonics"; -interface RhymeExerciseProps extends BaseExerciseProps { - options: Array<{ - word: string; - isRhyme: boolean; - }>; +interface RhymeExerciseProps { + currentWord: PhonicsWord; + options: PhonicsWord[]; + onAnswer: (word: string, isCorrect: boolean) => void; + disabled?: boolean; } -export function RhymeExercise({ currentWord, onAnswer, options, disabled }: RhymeExerciseProps) { +export function RhymeExercise({ currentWord, options, onAnswer, disabled }: RhymeExerciseProps) { return ( -
- - -
-
- Qual palavra rima com {currentWord.word}? -
+
+
+

Qual palavra rima com:

+
{currentWord.word}
+
-
- {options.map((option) => ( - - ))} -
+
+ {options.map((option) => ( + + ))}
); diff --git a/src/components/phonics/exercises/SoundMatchExercise.tsx b/src/components/phonics/exercises/SoundMatchExercise.tsx index 8e6728b..764a31d 100644 --- a/src/components/phonics/exercises/SoundMatchExercise.tsx +++ b/src/components/phonics/exercises/SoundMatchExercise.tsx @@ -42,6 +42,7 @@ export function SoundMatchExercise({ disabled && option.hasMatchingSound && "border-green-500 bg-green-50", disabled && !option.hasMatchingSound && "border-red-500 bg-red-50" )} + trackingId={`sound-match-option-${option.word}`} > {option.word} diff --git a/src/components/phonics/exercises/SyllablesExercise.tsx b/src/components/phonics/exercises/SyllablesExercise.tsx index b05d85c..904f216 100644 --- a/src/components/phonics/exercises/SyllablesExercise.tsx +++ b/src/components/phonics/exercises/SyllablesExercise.tsx @@ -1,80 +1,40 @@ -import { useState } from "react"; import { Button } from "@/components/ui/button"; -import { BaseExercise, type BaseExerciseProps } from "./BaseExercise"; +import { Card } from "@/components/ui/card"; import { cn } from "@/lib/utils"; +import type { PhonicsWord } from "@/types/phonics"; -interface SyllablesExerciseProps extends BaseExerciseProps { - syllables: string[]; - correctOrder: number[]; +interface SyllablesExerciseProps { + currentWord: PhonicsWord; + options: PhonicsWord[]; + onAnswer: (word: string, isCorrect: boolean) => void; + disabled?: boolean; } -export function SyllablesExercise({ - currentWord, - onAnswer, - syllables, - correctOrder, - disabled -}: SyllablesExerciseProps) { - const [selectedSyllables, setSelectedSyllables] = useState([]); - - const handleSyllableClick = (index: number) => { - if (selectedSyllables.includes(index)) { - setSelectedSyllables(prev => prev.filter(i => i !== index)); - } else { - setSelectedSyllables(prev => [...prev, index]); - } - }; - - const handleCheck = () => { - const isCorrect = selectedSyllables.every( - (syllableIndex, position) => syllableIndex === correctOrder[position] - ); - onAnswer(currentWord.word, isCorrect); - }; - +export function SyllablesExercise({ currentWord, options, onAnswer, disabled }: SyllablesExerciseProps) { return ( -
- - -
-
- Selecione as sílabas na ordem correta -
+
+
+

Quantas sílabas tem a palavra?

+
{currentWord.word}
+
-
- {syllables.map((syllable, index) => ( - - ))} -
- - {selectedSyllables.length > 0 && !disabled && ( -
- -
- )} - - {selectedSyllables.length > 0 && ( -
- {syllables.filter((_, i) => selectedSyllables.includes(i)).join("-")} -
- )} +
+ {options.map((option) => ( + + ))}
); diff --git a/src/hooks/phonics/useExerciseAttempt.ts b/src/hooks/phonics/useExerciseAttempt.ts index c875f95..e3fdd65 100644 --- a/src/hooks/phonics/useExerciseAttempt.ts +++ b/src/hooks/phonics/useExerciseAttempt.ts @@ -1,14 +1,23 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { supabase } from '@/lib/supabase'; -import type { PhonicsAttempt } from '@/types/phonics'; +import type { StudentPhonicsAttempt, PhonicsWord } from '@/types/phonics'; + +interface ExerciseWordResponse { + word: PhonicsWord; +} + +interface ExerciseWordWithOptions { + word: PhonicsWord; + options: PhonicsWord[]; +} export function useExerciseAttempt() { const queryClient = useQueryClient(); return useMutation({ - mutationFn: async (attempt: Omit) => { + mutationFn: async (attempt: Omit) => { const { data, error } = await supabase - .from('phonics_student_attempts') + .from('student_phonics_attempts') .insert(attempt) .select() .single(); @@ -18,7 +27,7 @@ export function useExerciseAttempt() { }, onSuccess: (data, variables) => { queryClient.invalidateQueries({ - queryKey: ['phonics-progress', variables.studentId] + queryKey: ['phonics-progress', variables.student_id] }); } }); @@ -28,17 +37,55 @@ export function useExerciseWords(exerciseId: string) { return useQuery({ queryKey: ['exercise-words', exerciseId], queryFn: async () => { - const { data, error } = await supabase + // Primeiro, busca a palavra correta e suas opções + const { data: exerciseWords, error: exerciseWordsError } = await supabase .from('phonics_exercise_words') - .select(` - *, - word:phonics_words(*) - `) + .select('word:phonics_words!inner(*)') .eq('exercise_id', exerciseId) + .eq('is_correct_answer', true) .order('order_index', { ascending: true }); + if (exerciseWordsError) throw exerciseWordsError; + + // Para cada palavra correta, busca suas opções + const wordsWithOptions = await Promise.all( + ((exerciseWords as unknown) as ExerciseWordResponse[]).map(async (exerciseWord) => { + const { data: options, error: optionsError } = await supabase + .from('phonics_exercise_words') + .select('word:phonics_words!inner(*)') + .eq('exercise_id', exerciseId) + .neq('word.id', exerciseWord.word.id) + .limit(3); + + if (optionsError) throw optionsError; + + const optionWords = ((options as unknown) as ExerciseWordResponse[]).map(o => o.word); + + return { + word: exerciseWord.word, + options: [exerciseWord.word, ...optionWords].sort(() => Math.random() - 0.5) + } satisfies ExerciseWordWithOptions; + }) + ); + + return wordsWithOptions; + }, + enabled: !!exerciseId + }); +} + +export function useExerciseAttempts(exerciseId: string) { + return useQuery({ + queryKey: ['exercise-attempts', exerciseId], + queryFn: async () => { + const { data, error } = await supabase + .from('student_phonics_attempts') + .select('*') + .eq('exercise_id', exerciseId); + if (error) throw error; - return data; - } + return data as StudentPhonicsAttempt[]; + }, + enabled: !!exerciseId }); } \ No newline at end of file diff --git a/src/hooks/phonics/usePhonicsExercises.ts b/src/hooks/phonics/usePhonicsExercises.ts index f03c72d..35a4487 100644 --- a/src/hooks/phonics/usePhonicsExercises.ts +++ b/src/hooks/phonics/usePhonicsExercises.ts @@ -10,9 +10,16 @@ export function usePhonicsExercises(categoryId?: string) { .from('phonics_exercises') .select(` *, - category:phonics_exercise_categories(name), - type:phonics_exercise_types(name), - words:phonics_exercise_words( + category:phonics_categories( + id, + name, + description, + level + ), + words:phonics_exercise_words!inner( + id, + is_correct_answer, + order_index, word:phonics_words(*) ) `) @@ -36,7 +43,7 @@ export function usePhonicsCategories() { queryKey: ['phonics-categories'], queryFn: async () => { const { data, error } = await supabase - .from('phonics_exercise_categories') + .from('phonics_categories') .select('*') .order('order_index', { ascending: true }); diff --git a/src/hooks/phonics/usePhonicsProgress.ts b/src/hooks/phonics/usePhonicsProgress.ts index 2101dc7..1186eb4 100644 --- a/src/hooks/phonics/usePhonicsProgress.ts +++ b/src/hooks/phonics/usePhonicsProgress.ts @@ -1,6 +1,6 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { supabase } from '@/lib/supabase'; -import type { StudentPhonicsProgress } from '@/types/phonics'; +import type { StudentPhonicsProgress, UpdateProgressParams } from '@/types/phonics'; export function usePhonicsProgress(studentId: string) { return useQuery({ @@ -22,57 +22,49 @@ export function useUpdatePhonicsProgress() { const queryClient = useQueryClient(); return useMutation({ - mutationFn: async ({ - studentId, - exerciseId, - score, - timeSpent - }: { - studentId: string; - exerciseId: string; - score: number; - timeSpent: number; - }) => { - // Primeiro, registra a tentativa - const { data: attemptData, error: attemptError } = await supabase - .from('student_phonics_attempts') - .insert({ - student_id: studentId, - exercise_id: exerciseId, - score, - time_spent_seconds: timeSpent - }) - .select() + mutationFn: async (params: UpdateProgressParams) => { + // Primeiro, busca o progresso atual para calcular os valores acumulados + const { data: currentProgress } = await supabase + .from('student_phonics_progress') + .select('total_time_spent_seconds, correct_answers_count, total_answers_count') + .eq('student_id', params.student_id) + .eq('exercise_id', params.exercise_id) .single(); - if (attemptError) throw attemptError; + // Calcula os novos valores acumulados + const total_time_spent_seconds = (currentProgress?.total_time_spent_seconds || 0) + params.time_spent_seconds; + const correct_answers_count = (currentProgress?.correct_answers_count || 0) + params.correct_answers_count; + const total_answers_count = (currentProgress?.total_answers_count || 0) + params.total_answers_count; - // Depois, atualiza ou cria o progresso - const { data: progressData, error: progressError } = await supabase + // Atualiza o progresso + const { data, error } = await supabase .from('student_phonics_progress') .upsert({ - student_id: studentId, - exercise_id: exerciseId, - attempts: 1, - best_score: score, - last_score: score, - completed: score >= 0.7, - completed_at: score >= 0.7 ? new Date().toISOString() : null, - stars: Math.ceil(score * 3), - xp_earned: Math.ceil(score * 100) + student_id: params.student_id, + exercise_id: params.exercise_id, + best_score: params.best_score, + last_score: params.last_score, + completed: params.completed, + completed_at: params.completed ? new Date().toISOString() : null, + stars: params.stars, + xp_earned: params.xp_earned, + attempts: 'COALESCE(attempts, 0) + 1', + total_time_spent_seconds, + correct_answers_count, + total_answers_count, + last_attempt_at: new Date().toISOString(), + updated_at: new Date().toISOString() }, { - onConflict: 'student_id,exercise_id', - ignoreDuplicates: false + onConflict: 'student_id,exercise_id' }) .select() .single(); - if (progressError) throw progressError; - - return progressData; + if (error) throw error; + return data as StudentPhonicsProgress; }, - onSuccess: (_, { studentId }) => { - queryClient.invalidateQueries({ queryKey: ['phonics-progress', studentId] }); + onSuccess: (_, { student_id }) => { + queryClient.invalidateQueries({ queryKey: ['phonics-progress', student_id] }); } }); } \ No newline at end of file diff --git a/src/pages/student-dashboard/PhonicsPage.tsx b/src/pages/student-dashboard/PhonicsPage.tsx index a00f205..c5521bb 100644 --- a/src/pages/student-dashboard/PhonicsPage.tsx +++ b/src/pages/student-dashboard/PhonicsPage.tsx @@ -24,7 +24,7 @@ export function PhonicsPage() { return ( setSelectedExercise(undefined)} /> diff --git a/src/pages/student-dashboard/PhonicsProgressPage.tsx b/src/pages/student-dashboard/PhonicsProgressPage.tsx index 8df42a6..7a7c7db 100644 --- a/src/pages/student-dashboard/PhonicsProgressPage.tsx +++ b/src/pages/student-dashboard/PhonicsProgressPage.tsx @@ -15,7 +15,7 @@ export function PhonicsProgressPage() { const totalExercises = exercises.length; const completedExercises = progress.filter(p => p.completed).length; const totalStars = progress.reduce((acc, p) => acc + p.stars, 0); - const totalXP = progress.reduce((acc, p) => acc + p.xpEarned, 0); + const totalXP = progress.reduce((acc, p) => acc + p.xp_earned, 0); const completionRate = (completedExercises / totalExercises) * 100; return ( @@ -74,8 +74,8 @@ export function PhonicsProgressPage() {
{exercises.map((exercise) => { - const exerciseProgress = progress.find(p => p.exerciseId === exercise.id); - const progressValue = exerciseProgress ? exerciseProgress.bestScore * 100 : 0; + const exerciseProgress = progress.find(p => p.exercise_id === exercise.id); + const progressValue = exerciseProgress ? exerciseProgress.best_score * 100 : 0; return (
diff --git a/src/types/phonics.ts b/src/types/phonics.ts index b8001b1..546bceb 100644 --- a/src/types/phonics.ts +++ b/src/types/phonics.ts @@ -1,9 +1,5 @@ -export interface PhonicsExerciseType { - id: string; - name: string; - description: string | null; - created_at: string; -} +// Tipos de Exercícios +export type PhonicsExerciseType = 'rhyme' | 'alliteration' | 'syllables' | 'initial_sound' | 'final_sound'; export interface PhonicsExerciseCategory { id: string; @@ -28,6 +24,18 @@ export interface PhonicsExercise { required_score: number; created_at: string; updated_at: string; + category?: { + id: string; + name: string; + description: string | null; + level: number; + }; + words?: Array<{ + id: string; + is_correct_answer: boolean; + order_index: number | null; + word: PhonicsWord; + }>; } export interface PhonicsWord { @@ -47,6 +55,58 @@ export interface PhonicsExerciseWord { created_at: string; } +export interface StudentPhonicsAttemptAnswer { + id: string; + attempt_id: string; + word_id: string; + is_correct: boolean; + answer_text: string | null; + time_taken_seconds?: number; + created_at: string; +} + +export interface StudentPhonicsAttempt { + id: string; + student_id: string; + exercise_id: string; + score: number; + time_spent_seconds: number | null; + answers: StudentPhonicsAttemptAnswer[]; + created_at: string; +} + +export interface StudentPhonicsProgress { + id: string; + student_id: string; + exercise_id: string; + best_score: number; + last_score: number; + completed: boolean; + completed_at: string | null; + stars: number; + xp_earned: number; + attempts: number; + total_time_spent_seconds: number; + correct_answers_count: number; + total_answers_count: number; + last_attempt_at: string | null; + created_at: string; + updated_at: string; +} + +export interface UpdateProgressParams { + student_id: string; + exercise_id: string; + best_score: number; + last_score: number; + completed: boolean; + stars: number; + xp_earned: number; + time_spent_seconds: number; + correct_answers_count: number; + total_answers_count: number; +} + export interface MediaType { id: string; name: string; @@ -64,39 +124,6 @@ export interface PhonicsExerciseMedia { created_at: string; } -export interface StudentPhonicsProgress { - id: string; - student_id: string; - exercise_id: string; - attempts: number; - best_score: number; - last_score: number; - completed: boolean; - completed_at: string | null; - stars: number; - xp_earned: number; - created_at: string; - updated_at: string; -} - -export interface StudentPhonicsAttempt { - id: string; - student_id: string; - exercise_id: string; - score: number; - time_spent_seconds: number | null; - created_at: string; -} - -export interface StudentPhonicsAttemptAnswer { - id: string; - attempt_id: string; - word_id: string; - is_correct: boolean; - answer_text: string | null; - created_at: string; -} - export interface AchievementType { id: string; name: string;