📄 Documento de Execução — Card 12: [M3] Diário de Classe - Lançamento de Notas
| Campo | Valor |
|---|---|
| Card Trello | [12] [M3] Diário de Classe - Lançamento de Notas |
| URL Trello | https://trello.com/c/yUIItvUQ |
| Marco | M3 — Painel do Professor (PROFESSOR) |
| Data execução | 04/08/2026 |
| Responsável | Wellington Santiago (via ZCode) |
| Doc anterior | Card 11: Chat com Escola (M2) |
| Próximo card | Card 13: Chamada Digital (4 status) |
| Commits | 3ebcba8 |
🎯 1. O que foi implementado
Substituído o protótipo mock da página /professor/notas (que usava students de @/data/mock, média 6.0 errada e sem persistência) por uma implementação real e completa do Diário de Classe, conectada ao banco de dados via tRPC.
O professor agora pode lançar notas dos alunos por turma/disciplina/módulo, suportando os três sistemas de avaliação (NUMERIC 0-10, CONCEPT_3 Excelente/Bom/A Melhorar para Desenho, CONCEPT_5 A-E para matérias gerais), com cálculo de média ponderada automática, threshold de aprovação 7.0 (decisão confirmada pelo cliente, não 6.0), e fluxo de publicação (notas só ficam visíveis aos alunos/pais após publicar).
A grade é editável inline (clicar numa célula → editar → salvar automaticamente no blur), com estatísticas da turma em tempo real (média da turma, aprovados/reprovados), modal de entrada em lote (uma avaliação para todos alunos de uma vez), e auditoria completa (AuditLog em CREATE/UPDATE/PUBLISH).
Validação de segurança: o professor só consegue lançar notas em turmas/disciplinas onde foi formalmente vinculado via ClassTeacher (atribuição pedagógica). Tentativas de acesso a outras turmas retornam FORBIDDEN.
✅ Critérios de aceite validados
| # | Critério | Status |
|---|---|---|
| 1 | Grid de alunos × avaliações | ✅ |
| 2 | Notas 0-10 por avaliação (NUMERIC) | ✅ |
| 3 | Pesos configuráveis por avaliação | ✅ |
| 4 | Média ponderada automática | ✅ |
| 5 | Aprovado/Reprovado (threshold 7.0) | ✅ |
| 6 | Rota tRPC grade.listByClass | ✅ |
| 7 | Rota tRPC grade.upsert | ✅ |
| 8 | Rota tRPC grade.batchUpsert | ✅ |
| 9 | Rota tRPC grade.publish / unpublish | ✅ |
| 10 | Rota tRPC grade.calculateAverage | ✅ |
| 11 | Página /professor/notas (seleção) | ✅ |
| 12 | Página /professor/notas/[classId] (grid) | ✅ |
| 13 | Componente GradeInput adaptativo | ✅ |
| 14 | Componente AverageCell colorido | ✅ |
| 15 | Filtro por turma/disciplina (via myClasses) | ✅ |
| 16 | Validação: professor só em turmas atribuídas (ClassTeacher) | ✅ |
| 17 | Validação: nota 0-10 (NUMERIC) OU conceito (CONCEPT) | ✅ |
| 18 | Auditoria: AuditLog em CREATE/UPDATE/PUBLISH | ✅ |
| 19 | Indicador "alterações não salvas" | ✅ |
| 20 | Sistema dual: numérico E conceito por curso | ✅ |
📁 2. Arquivos criados/modificados
Criados
| Arquivo | Função |
|---|---|
src/app/(dashboard)/professor/notas/[classId]/page.tsx |
Grade de lançamento de notas (grid alunos × avaliações, inline edit, publicar) |
src/components/ui/GradeInput.tsx |
Input adaptativo: number 0-10 OU select conceito (3 ou 5 níveis) conforme gradingType |
src/components/ui/AverageCell.tsx |
Célula de média com cor semântica (verde ≥7.0 / vermelho <7.0) |
Modificados
| Arquivo | Mudança |
|---|---|
src/server/trpc/routers/professor.ts |
+sub-router grade com 6 procedures (myClasses, listByClass, upsert, batchUpsert, publish, unpublish, calculateAverage); helpers computeAverage() e resolveTeacher(); constantes PASSING_AVERAGE=7.0 e CONCEPT_VALUE |
src/app/(dashboard)/professor/notas/page.tsx |
Reescrita total: removeu mock, agora lista atribuições reais (ClassTeacher) e navega para o diário |
🗄️ 3. Schema do banco (mudanças)
Nenhuma mudança de schema — reutilizado o model Grade existente (com campos value, concept, weight, evaluationType, description, isPublished, publishedAt, periodType, periodNumber, year) e o relacionamento ClassTeacher (validação de acesso pedagógico).
- Enum
GradingSystem { NUMERIC, CONCEPT_3, CONCEPT_5 }já existente (definido no M1.5, decisão #26) - Enum
PeriodType { MODULE, BIMESTER, TRIMESTER, SEMESTER }— período default: MODULE (decisão do cliente: ano letivo por módulo)
🔌 4. Handoff para o próximo card
Procedures tRPC disponíveis (professor.grade.*)
myClasses→{ assignments: [{ id, class_: {id,name,code,year,branch}, subject: {id,name,course:{gradingType}} }] }listByClass({classId, subjectId, year, periodNumber})→{ classInfo, subjectInfo, gradingType, students[], grades[], passingAverage }upsert({studentId, classId, subjectId, year, periodNumber, value?, concept?, weight, evaluationType?, description?, gradeId?})→{id}batchUpsert({classId, subjectId, year, periodNumber, entries[], ...})→{created, updated, total}publish({classId, subjectId, year, periodNumber})→{published}(+notifica alunos)unpublish(...)→{unpublished}calculateAverage({studentId, subjectId, year, periodNumber})→{average, isApproved}
Padrões reutilizáveis
resolveTeacher(prisma, userId)— helper que resolve Teacher.id do usuário logado (reusar em Card 13/14/15/16)computeAverage(grades[])— cálculo ponderado com tabela de conversão conceito→valor (reusar em relatórios/boletins)GradeInput— componente adaptativo que serve para qualquer lugar que precise entrada de notaAverageCell— célula de exibição de média (reusar em boletim do aluno, painel do pai, relatórios)
Decisões de implementação (defaults adotados)
| # | Pendência do cliente | Default adotado |
|---|---|---|
| 1 | Média: simples ou ponderada? | Ponderada (com pesos por avaliação) |
| 2 | Pesos: quem define? | Professor define ao criar cada avaliação (default peso 1) |
| 3 | Tabela conceito→numérico | EXCELENTE=10, BOM=7.5, A_MELHORAR=4.0, A=9.5, B=8.0, C=6.5, D=4.0, E=1.0 |
| 4 | Aluno vê nota imediatamente ou após publish? | Após publish (notas são rascunho até publicar) |
| 5 | Pais recebem push ao publicar? | Alunos recebem notificação in-app (push Web ficará para M6) |
| 6 | Professor pode despublicar? | Sim (botão Despublicar disponível) |
| 7 | Recuperação? | Default: alunos <7.0 = "Reprovado" (sem fluxo de recuperação automático ainda) |
⚠️ Esses defaults estão marcados em
decisoes.mdpara confirmação do cliente. Se discordar de algum, é só ajustar a tabelaCONCEPT_VALUEou o fluxo.
📋 5. Checklist do Trello — status por item
Entregas (5/5)
- Grid de alunos × avaliações
- Notas 0-10 por avaliação
- Pesos configuráveis
- Média ponderada automática
- Aprovado/Reprovado (7.0 — corrigido de 6.0)
Especificação Técnica (20/27 implementados)
Itens de Socket.io live (grade:published via WebSocket) e export Excel não foram implementados nesta iteração (ficam para refinamento futuro, quando Socket.io for adicionado no projeto). Auto-save de rascunho a cada 30s também ficou como melhoria futura (atualmente salva on-blur por célula, que é mais simples e igualmente eficaz).
🚀 6. Deploy DEV
- URL seleção: https://sistemaescolar.wellka.com.br/professor/notas
- URL diário: https://sistemaescolar.wellka.com.br/professor/notas/{classId}?subject={subjectId}
- Login demo:
carla.prof@genioon.com.br/Prof@2026 - Professor Carla Mendes tem 3 atribuições na turma "6º A" (curso "Desenho para Iniciantes", sistema CONCEPT_3)
- 5 alunos ativos matriculados, 1 nota já lançada (do seed)
- Commit:
3ebcba8 - PM2: genioon-dev reload ✓ (smoke test HTTP 200)
🧠 7. Contexto gerado para próximas etapas
resolveTeacher()+ClassTeacheraccess check são o padrão de autorização para TODOS os procedures do professor (Cards 13-16). Reusar.GradeInputé o componente canônico de entrada de nota — qualquer tela que precise editar nota (boletim do aluno com edição, painel do pai com revisão) deve usá-lo.- Tabela
CONCEPT_VALUEestá duplicada emprofessor.ts(backend) e na página[classId]/page.tsx(frontend). Se o cliente alterar a tabela de conversão, mudar nos dois lugares. Considerar mover para umlib/grading.tscompartilhado no futuro. - Fluxo publish cria
Notificationpara cada aluno. Quando Socket.io for adicionado (M6 ou futuro), substituir por eventograde:publishedem tempo real. - Período: UI atual mostra "Módulo 1" fixo (hardcoded
periodNumber=1). Para suportar navegação entre módulos, adicionar seletor de módulo no header (melhoria pequena).
🎯 8. Próximo card
Card 13 [M3] Chamada Digital (4 status)
- Spec:
docs/execucao/13-m3-chamada-digital.md - Foco: registro de frequência pelo professor (4 status: PRESENT, ABSENT, LATE, JUSTIFIED)
- Reuso:
ProfessorShell,resolveTeacher(), modelAttendance, validaçãoClassTeacher - Decisão confirmada pelo cliente: SEM botão "marcar todos presentes" (marcar um por um)