mirror of
https://github.com/lucasrcsantana/story-generator.git
synced 2025-12-16 21:37:51 +00:00
feat: aprimora métricas do dashboard do aluno - Calcula métricas usando todas as histórias e gravações - Adiciona novas métricas detalhadas - Implementa tooltips explicativos - Separa consultas de métricas e exibição
This commit is contained in:
parent
69dbb5fa48
commit
7e93a59609
15
CHANGELOG.md
15
CHANGELOG.md
@ -201,3 +201,18 @@ e este projeto adere ao [Semantic Versioning](https://semver.org/lang/pt-BR/).
|
||||
- Adicionada tipagem forte para dados de idioma
|
||||
- Implementada validação robusta de códigos de idioma
|
||||
- Melhorada a estrutura de componentes para suportar dados dinâmicos
|
||||
|
||||
## [1.5.0] - 2024-03-19
|
||||
|
||||
### Modificado
|
||||
- Aprimoramento no cálculo de métricas do dashboard do aluno:
|
||||
- Métricas agora são calculadas considerando todas as histórias e gravações do aluno
|
||||
- Adicionadas novas métricas detalhadas: pronúncia, precisão, compreensão, velocidade, pausas e erros
|
||||
- Melhorias na interface com tooltips explicativos para cada métrica
|
||||
- Separação entre dados para métricas (todas as histórias) e exibição (6 mais recentes)
|
||||
|
||||
### Técnico
|
||||
- Refatoração da busca de dados no StudentDashboardPage:
|
||||
- Separação entre consulta de métricas e consulta de exibição
|
||||
- Otimização no cálculo de médias das métricas
|
||||
- Melhoria na organização do código com comentários explicativos
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Plus, BookOpen, Clock, TrendingUp, Award } from 'lucide-react';
|
||||
import { Plus, BookOpen, Clock, TrendingUp, Award, Mic, Target, Brain, Gauge, Pause, XCircle, HelpCircle } from 'lucide-react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { supabase } from '../../lib/supabase';
|
||||
import type { Story, Student } from '../../types/database';
|
||||
@ -9,6 +9,12 @@ interface DashboardMetrics {
|
||||
averageReadingFluency: number;
|
||||
totalReadingTime: number;
|
||||
currentLevel: number;
|
||||
averagePronunciation: number;
|
||||
averageAccuracy: number;
|
||||
averageComprehension: number;
|
||||
averageWordsPerMinute: number;
|
||||
averagePauses: number;
|
||||
averageErrors: number;
|
||||
}
|
||||
|
||||
export function StudentDashboardPage() {
|
||||
@ -18,7 +24,13 @@ export function StudentDashboardPage() {
|
||||
totalStories: 0,
|
||||
averageReadingFluency: 0,
|
||||
totalReadingTime: 0,
|
||||
currentLevel: 1
|
||||
currentLevel: 1,
|
||||
averagePronunciation: 0,
|
||||
averageAccuracy: 0,
|
||||
averageComprehension: 0,
|
||||
averageWordsPerMinute: 0,
|
||||
averagePauses: 0,
|
||||
averageErrors: 0
|
||||
});
|
||||
const [loading, setLoading] = React.useState(true);
|
||||
const [error, setError] = React.useState<string | null>(null);
|
||||
@ -44,8 +56,16 @@ export function StudentDashboardPage() {
|
||||
if (studentError) throw studentError;
|
||||
setStudent(studentData);
|
||||
|
||||
// Buscar histórias do aluno
|
||||
const { data: storiesData, error: storiesError } = await supabase
|
||||
// Buscar todas as histórias do aluno para cálculo de métricas
|
||||
const { data: allStoriesData, error: allStoriesError } = await supabase
|
||||
.from('stories')
|
||||
.select('id')
|
||||
.eq('student_id', session.user.id);
|
||||
|
||||
if (allStoriesError) throw allStoriesError;
|
||||
|
||||
// Buscar histórias recentes para exibição
|
||||
const { data: recentStoriesData, error: storiesError } = await supabase
|
||||
.from('stories')
|
||||
.select('*')
|
||||
.eq('student_id', session.user.id)
|
||||
@ -54,15 +74,52 @@ export function StudentDashboardPage() {
|
||||
|
||||
if (storiesError) throw storiesError;
|
||||
|
||||
// Calcular métricas (exemplo simplificado)
|
||||
setMetrics({
|
||||
totalStories: storiesData?.length || 0,
|
||||
averageReadingFluency: 85,
|
||||
totalReadingTime: 120,
|
||||
currentLevel: 3
|
||||
});
|
||||
// Buscar todas as gravações completadas do aluno
|
||||
const { data: recordings, error: recordingsError } = await supabase
|
||||
.from('story_recordings')
|
||||
.select('*')
|
||||
.eq('status', 'completed')
|
||||
.in('story_id', allStoriesData.map(story => story.id));
|
||||
|
||||
// Buscar histórias recentes com a capa definida
|
||||
if (recordingsError) throw recordingsError;
|
||||
|
||||
// Calcular métricas baseadas nas gravações
|
||||
if (recordings && recordings.length > 0) {
|
||||
const totalRecordings = recordings.length;
|
||||
const metricsSum = recordings.reduce((acc, recording) => ({
|
||||
fluency: acc.fluency + recording.fluency_score,
|
||||
pronunciation: acc.pronunciation + recording.pronunciation_score,
|
||||
accuracy: acc.accuracy + recording.accuracy_score,
|
||||
comprehension: acc.comprehension + recording.comprehension_score,
|
||||
wordsPerMinute: acc.wordsPerMinute + recording.words_per_minute,
|
||||
pauses: acc.pauses + recording.pause_count,
|
||||
errors: acc.errors + recording.error_count
|
||||
}), {
|
||||
fluency: 0,
|
||||
pronunciation: 0,
|
||||
accuracy: 0,
|
||||
comprehension: 0,
|
||||
wordsPerMinute: 0,
|
||||
pauses: 0,
|
||||
errors: 0
|
||||
});
|
||||
|
||||
// Calcular médias
|
||||
setMetrics({
|
||||
totalStories: allStoriesData.length, // Usando o total de histórias
|
||||
averageReadingFluency: Math.round(metricsSum.fluency / totalRecordings),
|
||||
totalReadingTime: recordings.length * 2, // Exemplo: 2 minutos por gravação
|
||||
currentLevel: Math.ceil(metricsSum.fluency / (totalRecordings * 20)), // Exemplo: nível baseado na fluência
|
||||
averagePronunciation: Math.round(metricsSum.pronunciation / totalRecordings),
|
||||
averageAccuracy: Math.round(metricsSum.accuracy / totalRecordings),
|
||||
averageComprehension: Math.round(metricsSum.comprehension / totalRecordings),
|
||||
averageWordsPerMinute: Math.round(metricsSum.wordsPerMinute / totalRecordings),
|
||||
averagePauses: Math.round(metricsSum.pauses / totalRecordings),
|
||||
averageErrors: Math.round(metricsSum.errors / totalRecordings)
|
||||
});
|
||||
}
|
||||
|
||||
// Buscar histórias recentes com a capa definida (mantendo o limite de 6 para exibição)
|
||||
const { data, error } = await supabase
|
||||
.from('stories')
|
||||
.select(`
|
||||
@ -228,6 +285,139 @@ export function StudentDashboardPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Métricas de Leitura */}
|
||||
<div className="mb-8">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<h2 className="text-lg font-semibold text-gray-900">Métricas Detalhadas de Leitura</h2>
|
||||
<div
|
||||
className="text-gray-400 hover:text-gray-600 cursor-help transition-colors"
|
||||
title="Estas métricas são calculadas com base em todas as suas gravações de leitura, fornecendo uma visão detalhada do seu progresso"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-6 gap-6">
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6" title="Avalia a qualidade da sua pronúncia durante a leitura, considerando a clareza e correção dos sons das palavras">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="p-3 bg-indigo-100 rounded-lg">
|
||||
<Mic className="h-6 w-6 text-indigo-600" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex items-center gap-1">
|
||||
<p className="text-sm text-gray-500">Pronúncia Média</p>
|
||||
<div
|
||||
className="text-gray-400 hover:text-gray-600 cursor-help transition-colors"
|
||||
title="Avalia a qualidade da sua pronúncia durante a leitura, considerando a clareza e correção dos sons das palavras"
|
||||
>
|
||||
<HelpCircle className="h-4 w-4" />
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-2xl font-bold text-gray-900">{metrics.averagePronunciation}%</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6" title="Indica o quão preciso você é ao ler as palavras, sem trocas ou omissões de letras e sílabas">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="p-3 bg-pink-100 rounded-lg">
|
||||
<Target className="h-6 w-6 text-pink-600" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex items-center gap-1">
|
||||
<p className="text-sm text-gray-500">Precisão na Leitura</p>
|
||||
<div
|
||||
className="text-gray-400 hover:text-gray-600 cursor-help transition-colors"
|
||||
title="Indica o quão preciso você é ao ler as palavras, sem trocas ou omissões de letras e sílabas"
|
||||
>
|
||||
<HelpCircle className="h-4 w-4" />
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-2xl font-bold text-gray-900">{metrics.averageAccuracy}%</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6" title="Avalia seu nível de entendimento do texto durante a leitura, baseado no ritmo e entonação adequados">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="p-3 bg-orange-100 rounded-lg">
|
||||
<Brain className="h-6 w-6 text-orange-600" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex items-center gap-1">
|
||||
<p className="text-sm text-gray-500">Compreensão do Texto</p>
|
||||
<div
|
||||
className="text-gray-400 hover:text-gray-600 cursor-help transition-colors"
|
||||
title="Avalia seu nível de entendimento do texto durante a leitura, baseado no ritmo e entonação adequados"
|
||||
>
|
||||
<HelpCircle className="h-4 w-4" />
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-2xl font-bold text-gray-900">{metrics.averageComprehension}%</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6" title="Média de palavras lidas por minuto (WPM), indicando a velocidade e fluidez da sua leitura">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="p-3 bg-cyan-100 rounded-lg">
|
||||
<Gauge className="h-6 w-6 text-cyan-600" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex items-center gap-1">
|
||||
<p className="text-sm text-gray-500">Velocidade de Leitura</p>
|
||||
<div
|
||||
className="text-gray-400 hover:text-gray-600 cursor-help transition-colors"
|
||||
title="Média de palavras lidas por minuto (WPM), indicando a velocidade e fluidez da sua leitura"
|
||||
>
|
||||
<HelpCircle className="h-4 w-4" />
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-2xl font-bold text-gray-900">{metrics.averageWordsPerMinute} WPM</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6" title="Média de pausas não planejadas durante a leitura, indicando momentos de hesitação">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="p-3 bg-amber-100 rounded-lg">
|
||||
<Pause className="h-6 w-6 text-amber-600" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex items-center gap-1">
|
||||
<p className="text-sm text-gray-500">Pausas na Leitura</p>
|
||||
<div
|
||||
className="text-gray-400 hover:text-gray-600 cursor-help transition-colors"
|
||||
title="Média de pausas não planejadas durante a leitura, indicando momentos de hesitação"
|
||||
>
|
||||
<HelpCircle className="h-4 w-4" />
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-2xl font-bold text-gray-900">{metrics.averagePauses}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6" title="Média de erros cometidos durante a leitura, como trocas, omissões ou adições de palavras">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="p-3 bg-red-100 rounded-lg">
|
||||
<XCircle className="h-6 w-6 text-red-600" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex items-center gap-1">
|
||||
<p className="text-sm text-gray-500">Erros de Leitura</p>
|
||||
<div
|
||||
className="text-gray-400 hover:text-gray-600 cursor-help transition-colors"
|
||||
title="Média de erros cometidos durante a leitura, como trocas, omissões ou adições de palavras"
|
||||
>
|
||||
<HelpCircle className="h-4 w-4" />
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-2xl font-bold text-gray-900">{metrics.averageErrors}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Histórias Recentes */}
|
||||
<div>
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user