fix: resolve reference errors causing white screens and clean up typescript typings on portal
This commit is contained in:
parent
9ba2e11e27
commit
5862a8374d
|
|
@ -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] **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] **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] **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.
|
- [ ] Próximo Passo: Iniciar a migração do módulo Financeiro para 100% SQL seguindo o padrão do Boletim.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.`,
|
message: `Identificamos ${atrasadas.length} ${atrasadas.length === 1 ? 'parcela atrasada' : 'parcelas atrasadas'}. Regularize agora para evitar suspensões.`,
|
||||||
read: false,
|
read: false,
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
type: 'alert'
|
type: 'alert',
|
||||||
|
studentId: ''
|
||||||
};
|
};
|
||||||
allNotifs = [overdueNotif, ...allNotifs];
|
allNotifs = [overdueNotif, ...allNotifs];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -757,9 +757,9 @@ export default function Avaliacoes() {
|
||||||
<h3 style={{ fontSize: '1.05rem', fontWeight: 700, marginBottom: 4, paddingRight: isDone ? 90 : 0 }}>
|
<h3 style={{ fontSize: '1.05rem', fontWeight: 700, marginBottom: 4, paddingRight: isDone ? 90 : 0 }}>
|
||||||
{exam.title}
|
{exam.title}
|
||||||
</h3>
|
</h3>
|
||||||
{exam.description && (
|
{(exam as any).description && (
|
||||||
<p style={{ fontSize: '0.8rem', color: 'var(--color-text-secondary)', lineHeight: 1.4 }}>
|
<p style={{ fontSize: '0.8rem', color: 'var(--color-text-secondary)', lineHeight: 1.4 }}>
|
||||||
{exam.description}
|
{(exam as any).description}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -83,8 +83,9 @@ export default function Dashboard() {
|
||||||
if (lesson.status === 'cancelled') return;
|
if (lesson.status === 'cancelled') return;
|
||||||
validLessonsCount++;
|
validLessonsCount++;
|
||||||
|
|
||||||
const lessonFullISO = new Date(parseLessonDateTime(lesson.date, lesson.startTime || '00:00:00')).toISOString();
|
const lessonMs = parseLessonDateTime(lesson.date, lesson.startTime || '00:00:00');
|
||||||
const lessonStartMs = 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 lessonEndMs = parseLessonDateTime(lesson.date, lesson.endTime || '00:00:00', lesson.endTime ? 0 : 60);
|
||||||
|
|
||||||
const atts = data.attendance.filter(a => {
|
const atts = data.attendance.filter(a => {
|
||||||
|
|
@ -422,7 +423,7 @@ export default function Dashboard() {
|
||||||
{frequencyPercent}%
|
{frequencyPercent}%
|
||||||
</h3>
|
</h3>
|
||||||
<p style={{ fontSize: '0.8125rem', color: 'var(--color-text-secondary)', marginTop: '0.375rem' }}>
|
<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>
|
</p>
|
||||||
<div style={{
|
<div style={{
|
||||||
marginTop: '0.75rem', height: 6, borderRadius: 3,
|
marginTop: '0.75rem', height: 6, borderRadius: 3,
|
||||||
|
|
|
||||||
|
|
@ -145,8 +145,9 @@ export default function Frequencia() {
|
||||||
|
|
||||||
// Merge and Categorize
|
// Merge and Categorize
|
||||||
const processedItems = deduplicatedLessons.map(lesson => {
|
const processedItems = deduplicatedLessons.map(lesson => {
|
||||||
const lessonFullISO = new Date(parseLessonDateTime(lesson.date, lesson.startTime || '00:00:00')).toISOString();
|
const lessonMs = parseLessonDateTime(lesson.date, lesson.startTime || '00:00:00');
|
||||||
const lessonStartMs = 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 lessonEndMs = parseLessonDateTime(lesson.date, lesson.endTime || '00:00:00', lesson.endTime ? 0 : 60);
|
||||||
|
|
||||||
// No Manager, ele procura o primeiro registro válido
|
// 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)
|
// Check window (uses new 24h before/after logic)
|
||||||
if (!isLessonWithinJustificationWindow(l, now)) return false;
|
if (!isLessonWithinJustificationWindow(l, now)) return false;
|
||||||
|
|
||||||
const lessonFullISO = new Date(parseLessonDateTime(l.date, l.startTime || '00:00:00')).toISOString();
|
const lMs = parseLessonDateTime(l.date, l.startTime || '00:00:00');
|
||||||
const lessonStartMs = 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);
|
const lessonEndMs = parseLessonDateTime(l.date, l.endTime || '00:00:00', l.endTime ? 0 : 60);
|
||||||
|
|
||||||
// Find if THIS SPECIFIC lesson has attendance/justification
|
// Find if THIS SPECIFIC lesson has attendance/justification
|
||||||
|
|
@ -600,7 +602,9 @@ export default function Frequencia() {
|
||||||
) : canJustify ? (
|
) : canJustify ? (
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
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);
|
openJustifyModal(timestamp);
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -706,7 +710,9 @@ export default function Frequencia() {
|
||||||
return (isNaN(msB) ? 0 : msB) - (isNaN(msA) ? 0 : msA);
|
return (isNaN(msB) ? 0 : msB) - (isNaN(msA) ? 0 : msA);
|
||||||
})
|
})
|
||||||
.map(l => {
|
.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 (
|
return (
|
||||||
<option key={l.id} value={ts}>
|
<option key={l.id} value={ts}>
|
||||||
{formatDateFull(l.date)}{l.startTime ? ` — ${l.startTime.substring(0, 5)}` : ''}{l.endTime ? ` às ${l.endTime.substring(0, 5)}` : ''}
|
{formatDateFull(l.date)}{l.startTime ? ` — ${l.startTime.substring(0, 5)}` : ''}{l.endTime ? ` às ${l.endTime.substring(0, 5)}` : ''}
|
||||||
|
|
|
||||||
|
|
@ -213,7 +213,7 @@ export default function Notas() {
|
||||||
{periodGrades.map((grade) => {
|
{periodGrades.map((grade) => {
|
||||||
const isActivity = grade.evaluationType === 'activity';
|
const isActivity = grade.evaluationType === 'activity';
|
||||||
const maxScore = grade.maxScore ?? 10;
|
const maxScore = grade.maxScore ?? 10;
|
||||||
const isDirect = !grade.examId;
|
const isDirect = !(grade as any).examId;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={grade.id} style={{
|
<div key={grade.id} style={{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue