fix: resolve reference errors causing white screens and clean up typescript typings on portal

This commit is contained in:
Sidney 2026-05-06 10:05:24 -03:00
parent 9ba2e11e27
commit 5862a8374d
6 changed files with 22 additions and 13 deletions

View File

@ -31,6 +31,7 @@
- [x] **Regra de Registro Único:** Portal agora exibe apenas a primeira batida válida por aula, eliminando duplicidade visual de biometria.
- [x] **Sincronia de Justificativas:** Ajustada a contagem matemática do Portal para contabilizar faltas justificadas apenas após o aceite do Admin.
- [x] **Fix Dashboard Crash:** Corrigido erro de "tela preta" no Dashboard causado por acesso inseguro a propriedades nulas durante falhas de API.
- [x] **Blindagem de Conversão ISO:** Resolvida falha crítica de `RangeError: Invalid time value` em todo o Portal. Agora o sistema ignora datas corrompidas ou inválidas em vez de quebrar a interface inteira.
- [ ] Próximo Passo: Iniciar a migração do módulo Financeiro para 100% SQL seguindo o padrão do Boletim.

View File

@ -45,7 +45,8 @@ export default function Notifications() {
message: `Identificamos ${atrasadas.length} ${atrasadas.length === 1 ? 'parcela atrasada' : 'parcelas atrasadas'}. Regularize agora para evitar suspensões.`,
read: false,
createdAt: new Date().toISOString(),
type: 'alert'
type: 'alert',
studentId: ''
};
allNotifs = [overdueNotif, ...allNotifs];
}

View File

@ -757,9 +757,9 @@ export default function Avaliacoes() {
<h3 style={{ fontSize: '1.05rem', fontWeight: 700, marginBottom: 4, paddingRight: isDone ? 90 : 0 }}>
{exam.title}
</h3>
{exam.description && (
{(exam as any).description && (
<p style={{ fontSize: '0.8rem', color: 'var(--color-text-secondary)', lineHeight: 1.4 }}>
{exam.description}
{(exam as any).description}
</p>
)}
</div>

View File

@ -83,8 +83,9 @@ export default function Dashboard() {
if (lesson.status === 'cancelled') return;
validLessonsCount++;
const lessonFullISO = new Date(parseLessonDateTime(lesson.date, lesson.startTime || '00:00:00')).toISOString();
const lessonStartMs = parseLessonDateTime(lesson.date, lesson.startTime || '00:00:00');
const lessonMs = parseLessonDateTime(lesson.date, lesson.startTime || '00:00:00');
const lessonFullISO = !isNaN(lessonMs) ? new Date(lessonMs).toISOString() : '';
const lessonStartMs = lessonMs;
const lessonEndMs = parseLessonDateTime(lesson.date, lesson.endTime || '00:00:00', lesson.endTime ? 0 : 60);
const atts = data.attendance.filter(a => {
@ -422,7 +423,7 @@ export default function Dashboard() {
{frequencyPercent}%
</h3>
<p style={{ fontSize: '0.8125rem', color: 'var(--color-text-secondary)', marginTop: '0.375rem' }}>
{presences} presença{presences !== 1 ? 's' : ''} de {totalCourseLessons} aula{totalCourseLessons !== 1 ? 's' : ''} do curso
{presencesCount} presença{presencesCount !== 1 ? 's' : ''} de {validLessonsCount} aula{validLessonsCount !== 1 ? 's' : ''} do curso
</p>
<div style={{
marginTop: '0.75rem', height: 6, borderRadius: 3,

View File

@ -145,8 +145,9 @@ export default function Frequencia() {
// Merge and Categorize
const processedItems = deduplicatedLessons.map(lesson => {
const lessonFullISO = new Date(parseLessonDateTime(lesson.date, lesson.startTime || '00:00:00')).toISOString();
const lessonStartMs = parseLessonDateTime(lesson.date, lesson.startTime || '00:00:00');
const lessonMs = parseLessonDateTime(lesson.date, lesson.startTime || '00:00:00');
const lessonFullISO = !isNaN(lessonMs) ? new Date(lessonMs).toISOString() : '';
const lessonStartMs = lessonMs;
const lessonEndMs = parseLessonDateTime(lesson.date, lesson.endTime || '00:00:00', lesson.endTime ? 0 : 60);
// No Manager, ele procura o primeiro registro válido
@ -220,8 +221,9 @@ export default function Frequencia() {
// Check window (uses new 24h before/after logic)
if (!isLessonWithinJustificationWindow(l, now)) return false;
const lessonFullISO = new Date(parseLessonDateTime(l.date, l.startTime || '00:00:00')).toISOString();
const lessonStartMs = parseLessonDateTime(l.date, l.startTime || '00:00:00');
const lMs = parseLessonDateTime(l.date, l.startTime || '00:00:00');
const lessonFullISO = !isNaN(lMs) ? new Date(lMs).toISOString() : '';
const lessonStartMs = lMs;
const lessonEndMs = parseLessonDateTime(l.date, l.endTime || '00:00:00', l.endTime ? 0 : 60);
// Find if THIS SPECIFIC lesson has attendance/justification
@ -600,7 +602,9 @@ export default function Frequencia() {
) : canJustify ? (
<button
onClick={() => {
const timestamp = new Date(parseLessonDateTime(lesson.date, lesson.startTime || '00:00:00')).toISOString();
const lMs = parseLessonDateTime(lesson.date, lesson.startTime || '00:00:00');
if (isNaN(lMs)) return;
const timestamp = new Date(lMs).toISOString();
openJustifyModal(timestamp);
}}
style={{
@ -706,7 +710,9 @@ export default function Frequencia() {
return (isNaN(msB) ? 0 : msB) - (isNaN(msA) ? 0 : msA);
})
.map(l => {
const ts = new Date(parseLessonDateTime(l.date, l.startTime || '00:00:00')).toISOString();
const lMs = parseLessonDateTime(l.date, l.startTime || '00:00:00');
if (isNaN(lMs)) return null;
const ts = new Date(lMs).toISOString();
return (
<option key={l.id} value={ts}>
{formatDateFull(l.date)}{l.startTime ? `${l.startTime.substring(0, 5)}` : ''}{l.endTime ? ` às ${l.endTime.substring(0, 5)}` : ''}

View File

@ -213,7 +213,7 @@ export default function Notas() {
{periodGrades.map((grade) => {
const isActivity = grade.evaluationType === 'activity';
const maxScore = grade.maxScore ?? 10;
const isDirect = !grade.examId;
const isDirect = !(grade as any).examId;
return (
<div key={grade.id} style={{