# Log da Migração Massiva SQL-First (Sessão Completa)

## Visão Geral
Nesta sessão de trabalho, realizamos o "core" da transição do sistema EduManager, saindo do modelo puramente JSON (`school_data.json`) e consolidando 4 módulos centrais diretamente no Banco de Dados Relacional PostgreSQL.

## Módulos Migrados

### Fase 4: Gestão de Alunos e Autenticação
- **Backend Manager**: CRUD no `database.js` para tabela `alunos`, com rotas `/api/alunos` e `/api/alunos/:id/rematricular` no `server.selfhosted.js` que executam operações no PostgreSQL e realizam sincronização reversa em tempo real no legado `school_data.json` para total compatibilidade legacy.
- **Frontend Manager (`Students.tsx`)**: Completamente refatorado para ler e gravar diretamente nas rotas relacionais `/api/alunos`, eliminando 100% das chamadas `dbService.saveData` nesta tela, mantendo apenas o estado em memória sincronizado para continuidade UX.
- **Portal do Aluno**: Login (`/api/portal/login`) e perfil (`/api/portal/me`) reescritos para consultar a tabela `alunos` no PostgreSQL.


### Fase 5: Avaliações e Provas
- Backend e Frontend conectados às tabelas `provas` e `questoes_provas`.

### Fase 6: Cronograma e Aulas
- **Backend Manager**: Queries em lote (`insertAulas`, `getAulasByTurma`, `getAllAulas`, `deleteAulas`).
- **Frontend Manager (`LessonSchedule.tsx`)**: Consome `fetch('/api/aulas')` em estado próprio (`dbLessons`).
- **Frontend Manager (`Classes.tsx`)**: Ao criar/editar turma e gerar cronograma, agora envia aulas via `POST /api/aulas/lote` para o PostgreSQL.
- **Portal do Aluno**: Rota `/api/portal/aulas` faz `SELECT FROM aulas WHERE turma_id = ANY(...)`.

### Fase 7: Contratos e Modelos
- **Backend Manager**: CRUD para `contratos` e `modelos_contrato`.
- **Frontend Manager (`Contracts.tsx`)**: Consome `/api/contratos` e `/api/modelos-contrato`.
- **Portal do Aluno**: Rota `/api/portal/contratos` faz `SELECT FROM contratos WHERE aluno_id = $1`.

## Bugs Encontrados e Corrigidos

### 🐛 Bug 1: Tela Branca na Aba Alunos (`Students.tsx`)
- **Causa**: Referência circular no `useState` — `useState<any[]>(dbClasses || [])` referenciava a si mesma.
- **Fix**: Alterado para `useState<any[]>(data?.classes || [])`.

### 🐛 Bug 2: Dados Não Migrados para PostgreSQL
- **Causa**: A rotina `syncJsonToRelationalTables` só roda quando o Manager salva o JSON via `PUT /api/school-data`. Como a migração era nova, os dados nunca foram sincronizados.
- **Fix**: Criado script `migrate_aulas_contratos.cjs` que conecta diretamente ao PostgreSQL de produção e roda os INSERTs.
- **Resultado**: 109 aulas, 9 contratos, 1 modelo e 89 frequências migrados com sucesso.

### 🐛 Bug 3: Aulas Órfãs (FK Constraint)
- **Causa**: 57 aulas no JSON pertenciam a uma turma deletada (`d48b268c-...`), causando violação de FK.
- **Fix**: O script filtra aulas cujo `classId` não existe na tabela `turmas`.

### 🐛 Bug 4: Contratos Não Apareciam no Manager
- **Causa**: A função `getContratos()` retornava o campo como `date`, mas o frontend (`Contracts.tsx`) esperava `createdAt`.
- **Fix**: Corrigido o mapeamento em `database.js` para retornar `createdAt: r.created_at_fmt`.

### 🐛 Bug 5: Classes.tsx Não Salvava Aulas no PostgreSQL
- **Causa**: Ao criar/editar turma com cronograma, as aulas geradas eram salvas **apenas no JSON** (`data.lessons`), nunca no banco.
- **Fix**: Adicionado `fetch('/api/aulas/lote')` no `Classes.tsx` para enviar as aulas geradas ao PostgreSQL.

## Estado Final do Banco de Dados
| Tabela | Registros |
|---|---|
| alunos | 9 |
| aulas | 109 |
| contratos | 9 |
| frequencias | 89 |
| modelos_contrato | 1 |
| provas | 3 |
| turmas | 3 |

## Padrões de Arquitetura
1. **Reverse Sync (Mão Dupla)**: Frontend salva no SQL via API e mantém backup no JSON.
2. **Fallback Gradual**: Portal prioriza SQL, invoca JSON apenas se banco retornar vazio.
3. **Migração de Dados**: Executada via script Node.js conectando diretamente ao PostgreSQL de produção (host: `150.230.87.131`, user: `edumanager`).
### Fase 8: Consolidação de Frequência, Registro de Matrículas e Contratos Relacionais
- **Justificativa Única Rígida (Portal)**: Implementação de regra de validação rígida impedindo que o aluno envie mais de uma justificativa por aula. O Portal do Aluno agora exibe um aviso dinâmico informando que a aula já possui justificativa e desativa o envio. O backend valida a existência e retorna erro HTTP 400.
- **Sincronização Direta de Contratos (Manager)**: O fluxo de salvamento de matrículas foi aprimorado para disparar uma chamada de `POST /api/contratos` em tempo real ao salvar o aluno, escrevendo a emissão do contrato diretamente no PostgreSQL na VPS.
- **Refinamento do Modal de Alunos (Manager)**: O checkbox "Gerar Taxa" foi inteiramente removido do modal de matrícula. O checkbox "Gerar Contrato Automático" agora vem selecionado (`true`) por padrão em novos cadastros e desmarcado (`false`) ao editar matrículas para prevenir duplicações acidentais.
- **Relatório de Frequência Geral em PDF (Manager)**: Refatoração do exportador de PDF no módulo Registro de Frequência para gerar uma Ficha de Frequência Geral (Roster de todos os alunos ativos da turma, com contagem acumulada de presenças, faltas, justificativas e porcentagem de frequência de cada um) de modo a corresponder perfeitamente ao que o administrador visualiza na modal, eliminando downloads vazios.

### Fase 9: Endurecimento da Pré-Matrícula, Upload de Panfleto Promocional e Proteção contra Duplicatas
- **Folder Promocional Dinâmico e Reordenável (Banner)**: Refatorado o folder promocional (flyer) para ser tratado de forma 100% dinâmica como um campo personalizado especial (`tipo: 'banner'`) dentro do construtor de formulários. O administrador pode fazer o upload da imagem promocional diretamente no construtor (no modal de novo campo ou de edição), excluir e reordenar sua posição de exibição livremente em relação aos outros campos.
- **Renderização Pública Inline Premium**: A página pública de pré-matrícula renderiza a imagem do banner inline de forma premium, responsiva e exatamente na posição sequencial configurada na listagem de campos do banco.
- **Prevenção de Duplicatas e Spam**: Bloqueio de envios duplicados no backend comparando e-mail e telefone limpos de caracteres não-numéricos (para total equivalência).
- **Tratamento de Estado Rascunho (`draft`)**: Se a configuração estiver em rascunho, exibe automaticamente uma página informativa de "Vagas Lotadas - Novas vagas em breve" para proteger a integridade operacional antes do lançamento oficial.
- **Gestão de Campos Personalizados com Arquivos**: Implementação de tipo `file` para campos customizados no formulário do aluno, com upload assíncrono transparente via `FormData` e exibição em formato de hiperlink nos detalhes da inscrição para o administrador.
- **Preservação de Histórico de Campos Personalizados (Soft Delete)**: Correção de bug onde a exclusão de um campo (ex: Sexo) fazia com que o rótulo do campo sumisse e exibisse o código UUID bruto na listagem de inscritos e na exportação de CSV. Implementado Soft Delete (`ativo = false`) na exclusão e criado um mapeador inteligente com dicionário de fallbacks (`getFriendlyLabel`) que recupera os rótulos de campos excluídos ou legados em tempo real.
- **Correção de Máscaras de Entrada (Telefone/CPF/CEP)**: Correção de bug em teclados móveis (Android Gboard/iOS Safari) onde caracteres digitados desapareciam ou geravam loops repetitivos (ex: `(( () ()- 0-`). O problema era causado por double event binding (dois ouvintes `input` concorrentes ativos no mesmo elemento). Centralizada a máscara no ouvinte global de formulário e aprimorada a máscara de telefone para suportar nativamente tanto números fixos (10 dígitos: `(XX) XXXX-XXXX`) quanto móveis (11 dígitos: `(XX) XXXXX-XXXX`).
- **Exclusão Automática Pós-Conversão**: Implementada a exclusão automática da inscrição de pré-matrícula quando a mesma é concluída com sucesso e convertida em matrícula ativa de aluno no sistema. Captura o ID da pré-matrícula original de forma resiliente e dispara uma requisição `DELETE` contra `/api/prematricula/inscricoes/:id`, limpando instantaneamente a lista de pendências da aba de Pré-Matrícula sem requerer intervenção manual.
- **Favicon Dinâmico e Logos Arredondados**: Implementação de crop dinâmico em tempo de execução usando canvas HTML5 para gerar favicons perfeitamente circulares (`rounded-full`) a partir do logotipo cadastrado nas configurações da escola. O favicon circular dinâmico é atualizado automaticamente na aba do navegador tanto no painel administrativo do gestor quanto na página pública de pré-matrícula. Adicionalmente, todos os logotipos exibidos no painel do administrador (Sidebar) e na página pública foram estilizados com bordas elegantes, sombras sutis e arredondamento perfeito (`rounded-full w-8 h-8 object-cover`), elevando o alinhamento visual e a identidade estética premium da plataforma.
- **Favicon Dinâmico e Suporte PWA no Portal do Aluno**: Estendi a geração de favicons perfeitamente circulares (`rounded-full`) via canvas HTML5 para o Portal do Aluno, assegurando correspondência e marca unificada em toda a experiência do estudante. O logotipo da escola também foi estilizado como um círculo perfeito (`rounded-full object-cover`) na barra lateral (`Sidebar.tsx`) e na tela de Login do Portal (`Login.tsx`). Além disso, implementei suporte nativo e dinâmico a PWA (Progressive Web App) no Portal do Aluno: a API agora gera dinamicamente o arquivo `manifest.json` com o nome personalizado e logotipo da escola, registra o Service Worker (`sw.js`) em segundo plano para atender aos critérios de instalação de navegadores e renderiza um botão premium de glassmorphism verde pulsante e micro-animado ("Instalar App") na Sidebar no momento em que o aplicativo se torna instalável, permitindo aos alunos instalar o EduManager em seus celulares/computadores como um aplicativo nativo.
- **Correção da Persistência do Valor (Pontuação Máxima) das Avaliações**: Corrigido bug crítico no módulo de avaliações (Provas/Atividades) onde alterações na pontuação máxima ("Valor" ou `maxScore`) não eram salvas no banco de dados e resetavam de volta para 10.0 ao recarregar a página. A falha ocorria devido à ausência da coluna `max_score` nas operações de INSERT e UPDATE da camada de serviço (`database.js`), bem como a falta de mapeamento do campo no mapeador de retorno (`getProvas`) e no estado do React (`Exams.tsx` e `ReportCard.tsx`). Mapeei dinamicamente a propriedade `maxScore` em todos os fluxos da aplicação (Manager e Portal do Aluno), encapsulada pela conversão estrita `Number(p.max_score)` em conformidade com a Regra 19 de integridade numérica, garantindo que as pontuações e notas correspondentes no Boletim Escolar e Portal do Aluno reflitam precisamente o valor personalizado no banco de dados.
- **Nova Regra de Cálculo de Notas (Soma Capped 10 & Média por Período)**: Refatorei por completo a lógica de cálculo de notas do boletim escolar no Manager (`ReportCard.tsx`) e no Portal do Aluno (`Notas.tsx`). Agora, a nota de um aluno em um período para uma determinada disciplina é calculada pela **SOMA** das notas obtidas em todas as avaliações (provas e atividades) vinculadas àquele período, **limitada ao valor máximo de 10.0** (cap a 10). A Média Geral do aluno passou a ser calculada a nível de período: primeiro é calculada a média aritmética de todas as disciplinas daquele período específico e, caso existam múltiplos períodos com notas, a Média Geral do aluno é calculada pela média aritmética dos períodos lançados; caso exista apenas um único período ativo, a média geral é a média aritmética das disciplinas daquele único período.

