Entrar como:FuncionalidadesDoc
Pular para o conteúdo
← Voltar para a visão geral
✅ ConcluídoCard #11 · M2

Chat com Escola

Marco M2: Portal da Família (PARENT · PWA)

📅02 de agosto de 2026
👤Wellington Santiago (via ZCode)
🔧Commits: 21d321d

📄 Documento de Execução — Card 11: [M2] Chat com Escola

Campo Valor
Card Trello [11] [M2] Chat com Escola
URL Trello https://trello.com/c/furmJLCa
Marco M2 — Portal da Família (último card do M2)
Data execução 03/08/2026
Responsável Wellington Santiago (via ZCode)
Doc anterior Card 10: Financeiro - PIX & Boletos
Próximo card Card 12: Diário de Classe (M3)
Commit 21d321d

🎯 1. O que foi implementado

Chat bidirecional entre responsável e professores da escola. Diferente dos comunicados (one-way, Card 16/21), o chat é conversação 1:1 com histórico persistente. A página /pais/chat (antes mockada com respostas simuladas) agora renderiza dados reais via tRPC.

5 procedures tRPC em parent.chat

  1. conversations — lista de conversas do responsável com última mensagem, timestamp relativo e contagem de não lidas. Resolve nome/avatar/role do outro participante.
  2. messages({ conversationId, cursor?, limit? }) — histórico paginado (cursor-based, 30 por página). Valida participação antes de retornar.
  3. send({ conversationId?, content, attachmentUrl?, recipientId? }) — mutation: envia mensagem. Se não houver conversationId, cria nova conversa com recipientId (ou reutiliza existente).
  4. markRead({ conversationId }) — mutation: marca todas as mensagens não-minhas como lidas (set readAt).
  5. contacts — lista de professores disponíveis para iniciar nova conversa (vinculados às turmas dos filhos do responsável).

Página /pais/chat reescrita

  • Lista de conversas: avatar + nome + última msg + timestamp + badge não lidas
  • Thread de mensagens: bolhas (saíntes accent / entrantes cinza), separadores de data (Hoje/Ontem/data), read receipts (CheckCheck lida / Check enviada), anexos como link
  • Composer: input texto + anexo URL + botão enviar (disabled se vazio)
  • Modal Nova Conversa: lista professores dos filhos (nome + disciplina + unidade)
  • Polling: conversas a cada 5s, mensagens a cada 3s (quase-tempo-real sem Socket.io)

Estratégia: Polling em vez de Socket.io

Socket.io + redis-adapter já estão instalados, mas subir um server na porta 3001 adiciona complexidade de infra (processo separado, proxy Nginx, etc.). Para MVP, polling via tRPC (5s/3s) é suficiente e mais simples de manter. Quando precisar de tempo real instantâneo, plugar Socket.io sem mudar a interface.

Decisões do cliente aplicadas (6/7 respondidas)

Decisão Aplicação
Pai conversa com professores dos filhos contacts lista só professores das turmas dos filhos
Direção pode monitorar ⏳ Card 21 (LOCAL acessa conversas da unidade)
Histórico permanente ✅ sem expiração (schema não tem TTL)
Anexos: img, PDF, doc ✅ campo attachmentUrl (S3 signed URL futuro)
Push para nova mensagem ✅ infra pronta (Card 8), envio no Card 29/31
Pai pode iniciar conversa ✅ botão "Nova conversa" + modal de contatos
Bloquear fora horário comercial? ❌ pendente — default: 24h sem bloqueio (alinhado com "Horário de envio: 24h")

✅ Critérios de aceite validados

# Critério Status
1 Responsável abre conversa
2 Mensagens enviadas/recebidas ✅ (polling 3s)
3 Anexos via S3 ⏳ (link URL, S3 signed URL futuro)
4 Badge de não lidas atualizado
5 Histórico persiste e pagina
6 Secretaria vê mensagens (Card 21)
7 Build passa + lint OK

Validação real (03/08/2026)

conversations: 1 conversa (Carla Mendes, 1 não lida)
messages: 5 mensagens (4 lidas, 1 não lida)
  PROF: Olá! Tudo bem com a Maria? Ela está evoluindo muito... ✓ lida
  EU: Olá, prof! Tudo sim, obrigado... ✓ lida
  PROF: Que ótimo! Esta semana vamos começar perspectiva... ✓ lida
  EU: Perfeito! Ela já falou sobre isso... ✓ lida
  PROF: Aproveitando, lembrete: até sexta... ✗ não lida

📁 2. Arquivos criados/modificados

Criados

Arquivo Função
scripts/seed_card11_chat.js Seed: conversa responsável↔professor + 5 mensagens (1 não lida)

Modificados

Arquivo Mudança
src/server/trpc/routers/parent.ts +sub-router chat (5 procedures)
src/app/(dashboard)/pais/chat/page.tsx Reescrita: mock → dados reais (lista + thread + composer + modal nova conversa)
messages/{pt-BR,en-US,es-ES}.json +seção parent.chat (16 chaves)

🗄️ 3. Schema do banco (mudanças)

Nenhuma. Reutiliza ChatConversation (participantIds Json [2], lastMessageAt) + ChatMessage (content, attachmentUrl, readAt, senderId).


🔌 4. Handoff para o próximo card

API pública (tRPC)

  • parent.chat.conversations{ conversations: [{ id, otherName, otherAvatarUrl, otherRole, lastMessage, lastMessageAt, unreadCount }] }
  • parent.chat.messages({ conversationId, cursor?, limit? }){ messages: [...], nextCursor }
  • parent.chat.send({ conversationId?, content, attachmentUrl?, recipientId? }){ message, conversationId } (mutation)
  • parent.chat.markRead({ conversationId }){ updated } (mutation)
  • parent.chat.contacts{ contacts: [{ userId, name, avatarUrl, subject, branchName }] }

Triggers para próximos cards

  • Card 21 (Comunicados & Avisos da Unidade): LOCAL/MASTER acessa conversas da unidade (monitorar chat). Criar local.chat.list com filtro por branch.
  • Card 29 (Bot OpenAI): bot pode responder automaticamente no chat fora do horário.
  • Socket.io upgrade: quando precisar de tempo real instantâneo, criar server na porta 3001 + Redis adapter. Events: chat:message, chat:typing. Salas: chat:{conversationId}.

Lições aprendidas

  1. Polling first, Socket later: polling 3-5s via tRPC é suficiente para MVP e evita complexidade de infra. O usuário não percebe a diferença para a maioria dos casos de uso escolar.
  2. participantIds como Json array: o schema usa Json para participantIds em vez de tabela de junção. Simples para 2 participantes, mas para grupo (3+) seria melhor tabela N:N.

📋 5. Deploy DEV


🎯 6. Próximo card

Card 12 [M3] Diário de Classe - Lançamento de Notas

  • Spec: docs/execucao/12-m3-diario-classe.md
  • Foco: painel do PROFESSOR (M3 começa) — lançar notas por turma/disciplina
  • Reuso: Grade model, GradeTable componente, professorProcedure (RBAC)
  • M2 completo (Cards 8-11)! 🎉
← Voltar para a visão geral