fix: enforce subject and period selection when publishing exams to ensure report card sync

This commit is contained in:
Sidney 2026-04-30 11:57:35 -03:00
parent 94fe187998
commit c75d27f198
3 changed files with 40 additions and 6 deletions

View File

@ -19,6 +19,12 @@
> Ao realizar a migração completa dos dados do sistema legado 'schoodat' para o nosso banco de dados local Postgres, **é terminantemente proibido alterar, resetar ou re-hashear as senhas existentes.**
> As credenciais devem ser mantidas exatamente como estão para garantir que o acesso dos usuários não seja interrompido.
## 🚨 Regras de Fluxo de Trabalho (CRÍTICO)
> [!CAUTION]
> **Git Push Proibido Sem Demanda Explícita:**
> NUNCA execute `git add`, `git commit` ou `git push` sem que o USUÁRIO solicite explicitamente. Alterações devem ser feitas nos arquivos, mas o envio ao repositório remoto é uma ação exclusiva do usuário. Aguarde sempre o comando direto do usuário para realizar qualquer operação de versionamento.
## 📜 Padrões de Desenvolvimento
1. **Design System:** Estética Premium, Dark Mode por padrão (ou glassmorphism), micro-animações e ausência de placeholders.
2. **Segurança:** Todas as rotas sensíveis devem validar o token JWT local (via secrets do ambiente). Proibido usar Supabase SDK para lógica de autenticação ou sincronização no frontend.

View File

@ -1,5 +1,7 @@
# MEMORY.md - Contexto de Desenvolvimento
> **🚨 REGRA ABSOLUTA:** NUNCA execute `git add/commit/push` sem que o usuário peça explicitamente. Alterações nos arquivos são livres, mas versionamento é ação EXCLUSIVA do usuário.
## 📅 Estado Atual (30/04/2026)
- [x] **Automação de Mensagens (Cron Jobs):** Implementados dois disparadores independentes (`preventivo` e `atrasado`) via `node-cron`.
@ -10,8 +12,10 @@
- [x] **Auto-Initialization DB:** Script de boot que garante a existência das colunas `overdue_warnings_count` e `last_overdue_warning_at` na tabela `alunos_cobrancas`.
- [x] **Correção de Crash no Portal:** Resolvido erro de `.toFixed()` que quebrava as abas de "Avaliações" e "Notas" devido ao retorno de tipos `NUMERIC` do PostgreSQL como strings.
- [x] **Persistência de UI (Mensagens):** Integrada chamada ao `updateData` ao salvar agendamentos, garantindo que o estado do toggle não seja perdido ao trocar de aba no Manager.
- [x] **Sincronização de Boletim (Provas):** Adicionada validação rigorosa no Manager (`Exams.tsx`) para impedir a publicação de provas sem vínculo com `subjectId` e `periodId`. Inserido alerta visual para provas legadas desconectadas, garantindo que as notas feitas no Portal sejam corretamente injetadas no Boletim.
- [ ] Próximo Passo: Monitorar o log de disparos automáticos (`[Cron]`) e validar a taxa de entrega via Evolution API.
## 📅 Histórico Anterior (22/04/2026)
- [x] Correção do "Bug da Tela Preta" na câmera ao alternar para câmera traseira no celular.

View File

@ -1,6 +1,6 @@
import React, { useState, useRef } from 'react';
import { SchoolData, Exam, Question } from '../types';
import { FileText, Plus, Search, BookOpen, Upload, Trash2, ArrowLeft, Save, CheckCircle, Image as ImageIcon, X, RefreshCw, Lock, Unlock } from 'lucide-react';
import { FileText, Plus, Search, BookOpen, Upload, Trash2, ArrowLeft, Save, CheckCircle, Image as ImageIcon, X, RefreshCw, Lock, Unlock, AlertTriangle } from 'lucide-react';
import { uploadExamImage } from '../services/supabase';
import { useDialog } from '../DialogContext';
import { dbService } from '../services/dbService';
@ -165,7 +165,16 @@ const Exams: React.FC<ExamsProps> = ({ data, updateData }) => {
if (!editingExam) return;
if (!editingExam.title || !editingExam.classId) {
alert('Preencha o título e a turma antes de salvar.');
showAlert('Atenção', 'Preencha o título e a turma antes de salvar.', 'warning');
return;
}
if (status === 'published' && (!editingExam.subjectId || !editingExam.periodId)) {
showAlert(
'Vínculo Obrigatório',
'Para PUBLICAR a avaliação e permitir que as notas entrem no Boletim Escolar, você precisa vincular uma Disciplina e um Período.',
'warning'
);
return;
}
@ -266,7 +275,9 @@ const Exams: React.FC<ExamsProps> = ({ data, updateData }) => {
/>
</div>
<div>
<label className="block text-sm font-bold text-slate-700 mb-2">Disciplina (Boletim)</label>
<label className="block text-sm font-bold text-slate-700 mb-2">
Disciplina (Boletim) <span className="text-amber-500" title="Obrigatório para publicar">*</span>
</label>
<select
value={editingExam.subjectId || ''}
onChange={e => setEditingExam({ ...editingExam, subjectId: e.target.value || undefined })}
@ -277,10 +288,12 @@ const Exams: React.FC<ExamsProps> = ({ data, updateData }) => {
<option key={s.id} value={s.id}>{s.name}</option>
))}
</select>
<p className="text-[11px] text-slate-400 mt-1.5">Vincule a uma disciplina do Boletim Escolar para lançar notas automaticamente.</p>
<p className="text-[11px] text-amber-600 mt-1.5 font-semibold">Obrigatório para Publicar. A nota irá automaticamente para o boletim.</p>
</div>
<div>
<label className="block text-sm font-bold text-slate-700 mb-2">Período (Boletim)</label>
<label className="block text-sm font-bold text-slate-700 mb-2">
Período (Boletim) <span className="text-amber-500" title="Obrigatório para publicar">*</span>
</label>
<select
value={editingExam.periodId || ''}
onChange={e => setEditingExam({ ...editingExam, periodId: e.target.value || undefined })}
@ -291,7 +304,7 @@ const Exams: React.FC<ExamsProps> = ({ data, updateData }) => {
<option key={p.id} value={p.id}>{p.name}</option>
))}
</select>
<p className="text-[11px] text-slate-400 mt-1.5">Vincule a um período para que a nota apareça no campo correto do boletim.</p>
<p className="text-[11px] text-amber-600 mt-1.5 font-semibold">Obrigatório para Publicar. Define em qual coluna do boletim a nota entra.</p>
</div>
</div>
</div>
@ -536,6 +549,17 @@ const Exams: React.FC<ExamsProps> = ({ data, updateData }) => {
{(data.periods || []).find(p => p.id === exam.periodId)?.name || '—'}
</p>
)}
{/* ALERTA DE BOLETIM */}
{exam.status === 'published' && (!exam.subjectId || !exam.periodId) && (
<div className="flex items-start gap-2 bg-amber-50 p-3 rounded-xl border border-amber-200 mt-3">
<AlertTriangle size={16} className="text-amber-500 shrink-0 mt-0.5" />
<p className="text-[11px] font-medium text-amber-700 leading-tight">
<strong>Boletim Desconectado!</strong><br />
As notas desta avaliação não aparecerão no boletim do aluno porque faltou vincular a Disciplina ou o Período. Edite a prova para corrigir.
</p>
</div>
)}
</div>
<div className="border-t border-slate-100 pt-4 flex items-center justify-between">
<div className="flex items-center gap-2">