From 2f50468cc5a0162eb9f7d1e5ae0bff06a867ccce Mon Sep 17 00:00:00 2001 From: Sidney Date: Wed, 29 Apr 2026 08:52:42 -0300 Subject: [PATCH] =?UTF-8?q?feat:=20trava=20de=20duplicidade=20financeira,?= =?UTF-8?q?=20boletim=20multiavalia=C3=A7=C3=B5es=20e=20melhorias=20no=20p?= =?UTF-8?q?ortal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MEMORY.md | 19 +- manager/components/Exams.tsx | 144 +++++++----- manager/components/Finance.tsx | 22 +- manager/components/ReportCard.tsx | 374 ++++++++++++++++++------------ portal/prompt_portal_aluno.md | 14 ++ portal/server.js | 2 +- portal/server.selfhosted.js | 29 ++- portal/src/pages/Avaliacoes.tsx | 20 +- portal/src/pages/Notas.tsx | 189 ++++++++------- 9 files changed, 518 insertions(+), 295 deletions(-) diff --git a/MEMORY.md b/MEMORY.md index cf5ea6f..bcab768 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -12,13 +12,11 @@ - [x] Correção das Imagens de Prova: Normalização das URLs nas questões de avaliações (Portal e Manager). - [x] Estabilização de CI/CD: Transição para `runs-on: self-hosted` (ARM64 nativo) eliminando lentidão e crashes do QEMU. - [x] Correção do Sino de Notificações: Botões sempre visíveis, suporte a anexo via chave `arquivo` e exibição do **Motivo da Falta** direto na lista do sino. -- [x] **Mensagens e Automação Financeira:** Implementado sistema seletivo de disparos (Atrasados vs Preventivos) com botões independentes e lógica de servidor desacoplada. -- [x] **Configuração Contextual:** Configurações de automação (dias antes/depois, repetições) movidas da sidebar global para dentro dos modais de cada modelo de mensagem. -- [x] **Refinamento de UX/UI:** Correção de modais de Frequência para padrão `bg-transparent` e adição de cor `indigo` para identificação de lembretes preventivos. -- [x] Registro de Frequência: Implementado ícone de olho para abrir modal com texto completo da justificativa e botão de aprovação rápida. -- [x] Padronização Visual: Modais atualizados para `bg-transparent` (sem escurecimento/blur) com `shadow-2xl` para efeito de flutuação premium. -- [x] Backup & Sincronia: Portal agora envia metadados de justificativa filtráveis pelo Gerenciador. -- [ ] Próximo Passo: Analisar logs de comportamento dos disparos preventivos em larga escala. +- [x] **Segurança Financeira:** Implementado estado de carregamento (`isCreating`) no botão de gerar cobranças para impedir disparos duplicados ao Asaas por cliques múltiplos. +- [x] **Boletim & Avaliações:** Refatoração completa do sistema de notas para suportar múltiplas avaliações por período, integrando notas diretas e notas vindas de provas/atividades online. +- [x] **Sincronia Portal/Manager:** Ajustada a submissão de provas no Portal para calcular notas via `maxScore` e injetar automaticamente no boletim do Gerenciador via `examId`. +- [x] **Padronização de Servidor:** Confirmado o uso de `server.selfhosted.js` em ambos os apps como ponto de entrada para garantir 100% de funcionalidades locais. +- [ ] Próximo Passo: Monitorar o desempenho das submissões de provas simultâneas no Portal. ### 💳 Módulo Financeiro (Portal do Aluno) - **Funcionalidades Implementadas:** @@ -29,6 +27,13 @@ - Visualização de recibos via link externo ou modal de impressão local. - **Onde paramos:** O sistema de filtros e ordenação está funcional, sincronizando com os parâmetros da URL. +### 📝 Módulo de Avaliações (Portal do Aluno) +- **Funcionalidades Implementadas:** + - Tela de realização de provas e atividades online com cronômetro e suporte a imagens de apoio (MinIO). + - **Autocorreção 100% Automática:** O backend do portal (`server.js`) recebe as respostas, compara com o gabarito (`correctOptionIndex`), calcula o percentual de acertos e a nota proporcional ao peso da prova (`finalScore`). + - **Lançamento Automático no Boletim:** A nota calculada é salva no PostgreSQL (`provas_submissoes`) e injetada instantaneamente na tabela de notas (`grades`) do `school_data`. + - Bloqueio inteligente contra dupla submissão da mesma prova. + ### ⚙️ Módulo de Configurações e Infra (Manager) - **Arquitetura de Armazenamento:** Implementada a transição para **Self-Hosted Storage (MinIO)**. - Extração de Base64 concluída com sucesso via `migrate_images_to_minio.ts`. diff --git a/manager/components/Exams.tsx b/manager/components/Exams.tsx index 5c3e9ac..1361900 100644 --- a/manager/components/Exams.tsx +++ b/manager/components/Exams.tsx @@ -20,7 +20,7 @@ const Exams: React.FC = ({ data, updateData }) => { try { const match = url.match(/^https?:\/\/[^\/]+\/(.+)$/); if (match) return `/storage/${match[1]}`; - } catch(e) {} + } catch (e) { } return url; }; @@ -38,8 +38,10 @@ const Exams: React.FC = ({ data, updateData }) => { classId: data.classes[0]?.id || '', durationMinutes: 60, status: 'draft', - questions: [] - }); + questions: [], + evaluationType: 'exam', + maxScore: 10 + } as any); setCurrentView('builder'); }; @@ -98,7 +100,7 @@ const Exams: React.FC = ({ data, updateData }) => { if (!editingExam) return; const newQuestions = [...editingExam.questions]; newQuestions[qIndex].options.splice(oIndex, 1); - + // Adjust correctOptionIndex if needed if (newQuestions[qIndex].correctOptionIndex >= newQuestions[qIndex].options.length) { newQuestions[qIndex].correctOptionIndex = Math.max(0, newQuestions[qIndex].options.length - 1); @@ -111,7 +113,7 @@ const Exams: React.FC = ({ data, updateData }) => { const handleImageUpload = async (qIndex: number, event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (!file) return; - + setIsUploading(true); try { const url = await uploadExamImage(file); @@ -134,7 +136,7 @@ const Exams: React.FC = ({ data, updateData }) => { const handleSave = (status: 'draft' | 'published') => { if (!editingExam) return; - + if (!editingExam.title || !editingExam.classId) { alert('Preencha o título e a turma antes de salvar.'); return; @@ -143,7 +145,7 @@ const Exams: React.FC = ({ data, updateData }) => { const finalExam = { ...editingExam, status }; const currentExams = data.exams || []; const existingIndex = currentExams.findIndex(e => e.id === finalExam.id); - + let newExams; if (existingIndex >= 0) { newExams = [...currentExams]; @@ -151,7 +153,7 @@ const Exams: React.FC = ({ data, updateData }) => { } else { newExams = [...currentExams, finalExam]; } - + updateData({ exams: newExams }); setCurrentView('list'); setEditingExam(null); @@ -161,7 +163,7 @@ const Exams: React.FC = ({ data, updateData }) => { return (
-