import { useEffect, useState } from 'react'; import { useAuth } from '../context/AuthContext'; import { BookOpen, FileText } from 'lucide-react'; import type { Grade, Subject } from '../types'; interface GradeWithSubject extends Grade { subjectName: string; examTitle?: string; evaluationType?: string; maxScore?: number; periodName?: string; correctCount?: number; wrongCount?: number; } export default function Notas() { const { token } = useAuth(); const [grades, setGrades] = useState([]); const [allSubjects, setAllSubjects] = useState([]); const [periods, setPeriods] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { const fetchData = async () => { try { const res = await fetch('/api/portal/notas', { headers: { Authorization: `Bearer ${token}` }, }); const data = await res.json(); setGrades(data.grades || []); setPeriods(data.periods || []); setAllSubjects(data.allSubjects || []); } catch (err) { console.error(err); } finally { setLoading(false); } }; if (token) fetchData(); }, [token]); if (loading) { return (
); } const displaySubjects = allSubjects.length > 0 ? allSubjects : [...new Set(grades.map(g => g.subjectId))].map(id => ({ id, name: grades.find(g => String(g.subjectId) === String(id))?.subjectName || id })); // General average logic const calculateGeneralAverage = () => { if (displaySubjects.length === 0 || grades.length === 0) return 0; let totalSum = 0; let totalCount = 0; displaySubjects.forEach(subject => { const subjectId = typeof subject === 'string' ? subject : subject.id; const subjectGrades = grades.filter(g => String(g.subjectId) === String(subjectId)); if (subjectGrades.length === 0) return; const periodValues: Record = {}; subjectGrades.forEach(g => { const periodKey = String(g.periodName || g.period); if (!periodValues[periodKey]) periodValues[periodKey] = []; periodValues[periodKey].push(g.value); }); const periodAvgs: number[] = []; Object.values(periodValues).forEach(values => { if (values.length > 0) { const sum = values.reduce((a, b) => a + b, 0); periodAvgs.push(sum / values.length); } }); if (periodAvgs.length > 0) { const subjectAvg = periodAvgs.reduce((a, b) => a + b, 0) / periodAvgs.length; totalSum += subjectAvg; totalCount++; } }); return totalCount > 0 ? totalSum / totalCount : 0; }; const totalAvg = calculateGeneralAverage(); const getGradeColor = (value: number, maxScore: number = 10) => { const percentage = (value / maxScore) * 10; if (percentage >= 7) return 'var(--color-success)'; if (percentage >= 5) return 'var(--color-warning)'; return 'var(--color-danger)'; }; const getBgColor = (value: number, maxScore: number = 10) => { const percentage = (value / maxScore) * 10; if (percentage >= 7) return 'var(--bg-success-alpha)'; if (percentage >= 5) return 'var(--bg-warning-alpha)'; return 'var(--bg-danger-alpha)'; }; return (

Notas & Boletim

Acompanhe seu desempenho detalhado por disciplina

Média Geral (Estimada)

0 ? '3rem' : '1.25rem', fontWeight: 800, color: 'var(--color-text-primary)', lineHeight: 1.2, marginTop: 0, marginBottom: 0, whiteSpace: 'nowrap' }}> {totalAvg > 0 ? totalAvg.toFixed(1) : 'Aguardando notas...'}

{displaySubjects.length === 0 ? (

Nenhuma matéria cadastrada no curso

) : (
{displaySubjects.map((subject, idx) => { const subjectId = typeof subject === 'string' ? subject : subject.id; const subjectName = typeof subject === 'string' ? subject : subject.name; const subjectGrades = grades.filter(g => String(g.subjectId) === String(subjectId)); // Calculate Subject Average const periodValues: Record = {}; subjectGrades.forEach(g => { const periodKey = String(g.periodName || g.period); if (!periodValues[periodKey]) periodValues[periodKey] = []; periodValues[periodKey].push(g.value); }); const pAvgs: number[] = []; Object.values(periodValues).forEach(values => { if (values.length > 0) { pAvgs.push(values.reduce((a, b) => a + b, 0) / values.length); } }); const subjectAvg = pAvgs.length > 0 ? pAvgs.reduce((a, b) => a + b, 0) / pAvgs.length : null; return (

{subjectName}

{subjectAvg !== null && (
MÉDIA: {subjectAvg.toFixed(1)}
)}
{periods.map(period => { const periodGrades = subjectGrades.filter(g => String(g.periodName || g.period) === String(period)); if (periodGrades.length === 0) return null; const periodAvg = periodGrades.length > 0 ? periodGrades.reduce((sum, g) => sum + g.value, 0) / periodGrades.length : 0; return (

{period}

Média do Período: {periodAvg.toFixed(1)}
{periodGrades.map((grade) => { const isActivity = grade.evaluationType === 'activity'; const maxScore = grade.maxScore ?? 10; const isDirect = !(grade as any).examId; return (
{isDirect ? 'Lançamento Direto (Professor)' : grade.examTitle || 'Avaliação sem título'}
{!isDirect && (
{isActivity ? 'ATIVIDADE' : 'PROVA'} • VALE: {maxScore} PTS {grade.correctCount !== undefined && grade.wrongCount !== undefined && ( <> | {grade.correctCount} Acertos {grade.wrongCount} Erros )}
)}
{grade.value.toFixed(1)}
); })}
); })} {subjectGrades.length === 0 && (
Nenhuma nota lançada para esta disciplina.
)}
); })}
)} {/* Legend */}
Bom Desempenho Atenção Baixo Desempenho
); }