fix: portal now reads attendance and lessons from school_data JSON; restore amber color for justified absences

This commit is contained in:
Sidney 2026-05-06 21:58:04 -03:00
parent 5c49093ed0
commit a667368ce3
2 changed files with 9 additions and 23 deletions

View File

@ -290,21 +290,11 @@ app.get('/api/portal/notas', authMiddleware, async (req, res) => {
} }
}); });
// GET /api/portal/frequencia (Relacional) // GET /api/portal/frequencia (Leitura direta do school_data — mesma fonte do Manager)
app.get('/api/portal/frequencia', authMiddleware, async (req, res) => { app.get('/api/portal/frequencia', authMiddleware, async (req, res) => {
try { try {
const { rows: dbAttendance } = await pool.query( const schoolData = await getSchoolData();
'SELECT id, aluno_id as "studentId", turma_id as "classId", data as "rawDate", foto as "photo", verificado as "verified", tipo as "type", justificativa as "justification", justificativa_aceita as "justificationAccepted" FROM frequencias WHERE aluno_id = $1', const attendance = (schoolData.attendance || []).filter(a => a.studentId === req.user.studentId);
[req.user.studentId]
);
// Converter datas para ISO string COM timezone (formato idêntico ao Manager JSON)
// O pg driver retorna TIMESTAMPTZ como Date objects do JS, e toISOString() preserva o UTC correto.
// O browser então faz new Date(isoString) e converte para hora local automaticamente.
const attendance = dbAttendance.map(row => ({
...row,
date: row.rawDate instanceof Date ? row.rawDate.toISOString() : (row.rawDate || ''),
rawDate: undefined
}));
res.json({ attendance }); res.json({ attendance });
} catch (err) { } catch (err) {
console.error('Frequencia error:', err); console.error('Frequencia error:', err);
@ -424,21 +414,17 @@ app.get('/api/portal/config', (req, res) => {
}); });
}); });
// GET /api/portal/aulas // GET /api/portal/aulas (Leitura direta do school_data — mesma fonte do Manager)
app.get('/api/portal/aulas', authMiddleware, async (req, res) => { app.get('/api/portal/aulas', authMiddleware, async (req, res) => {
try { try {
const schoolData = await getSchoolData(); const schoolData = await getSchoolData();
const student = (schoolData.students || []).find(s => s.id === req.user.studentId); const student = (schoolData.students || []).find(s => s.id === req.user.studentId);
if (!student) return res.json({ lessons: [] }); if (!student) return res.json({ lessons: [] });
const { rows: turmasData } = await pool.query( // Obter turmas do aluno a partir do JSON (mesma lógica do Manager)
'SELECT DISTINCT turma_id FROM frequencias WHERE aluno_id = $1',
[req.user.studentId]
);
const studentClassIds = new Set([ const studentClassIds = new Set([
student.classId, student.classId,
...turmasData.map(r => r.turma_id) ...(schoolData.attendance || []).filter(a => a.studentId === req.user.studentId).map(a => a.classId)
].filter(Boolean)); ].filter(Boolean));
const parseDateHelper = (dStr) => { const parseDateHelper = (dStr) => {

View File

@ -548,8 +548,8 @@ export default function Frequencia() {
<CheckCircle2 size={16} /> Presente <CheckCircle2 size={16} /> Presente
</span> </span>
) : isJustificationAccepted ? ( ) : isJustificationAccepted ? (
<span style={{ display: 'flex', alignItems: 'center', gap: 6, color: 'var(--color-success)', fontWeight: 600 }}> <span style={{ display: 'flex', alignItems: 'center', gap: 6, color: '#f59e0b', fontWeight: 600 }}>
<CheckCircle2 size={16} /> Falta Justificada <AlertTriangle size={16} /> Falta Justificada
</span> </span>
) : hasJustification ? ( ) : hasJustification ? (
<span style={{ display: 'flex', alignItems: 'center', gap: 6, color: '#f59e0b', fontWeight: 500 }}> <span style={{ display: 'flex', alignItems: 'center', gap: 6, color: '#f59e0b', fontWeight: 500 }}>
@ -603,7 +603,7 @@ export default function Frequencia() {
</td> </td>
<td> <td>
{justText ? ( {justText ? (
<span style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: '0.8125rem', color: isJustificationAccepted ? 'var(--color-success)' : '#f59e0b', fontWeight: isJustificationAccepted ? 600 : 500 }}> <span style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: '0.8125rem', color: '#f59e0b', fontWeight: 600 }}>
<FileText size={14} color="currentColor" /> <FileText size={14} color="currentColor" />
{isJustificationAccepted ? 'Justificativa Aceita' : 'Em Análise'} {isJustificationAccepted ? 'Justificativa Aceita' : 'Em Análise'}
</span> </span>