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

Calendário Interativo da Agenda do Aluno

Marco M1: Portal do Aluno (STUDENT)

📅27 de julho de 2026
👤Wellington Santiago (via ZCode)
🔧Commits: 2415003

📄 Documento de Execução — Card 7c: Calendário Interativo da Agenda

Campo Valor
Card Trello [M1] Calendário Interativo da Agenda do Aluno
Marco M1 — Portal do Aluno (STUDENT)
Data execução 28/07/2026
Responsável Wellington Santiago (via ZCode)
Doc anterior Card 7b: Motor de Gamificação
Próximo card Card 8: Home da Família (M2)
Commit 2415003

🎯 1. O que foi implementado

Página /aluno/agenda com calendário mensal interativo que agrega 4 fontes de evento em uma visão unificada. O aluno vê tudo que professores e direção definiram: prazos de tarefas, provas, eventos gerais da escola (feriados, reuniões, eventos sociais) e comunicados da turma. Tudo somente leitura — o aluno não cria eventos (escopo de criação fica para LOCAL/MASTER no futuro).

Os 4 tipos de evento agregados

Tipo Fonte (schema) Cor no calendário Origem
🟡 Tarefa Task.dueDate âmbar (--warning-500) Professores criam tarefas
🔴 Prova Exam.availableFrom vermelho (--danger-500) Professores criam provas
🔵 Evento CalendarEvent azul (--accent-500) Direção (LOCAL/MASTER)
🟢 Comunicado Announcement.publishedAt verde (--success-500) Direção/Professores

Componentes da feature

  1. Procedure tRPC student.calendar.month (src/server/trpc/routers/student.ts):

    • Input: { year, month } (1-12). Calcula range [1º dia do mês, último dia].
    • Resolve student.branchId + courseIds das matrículas ACTIVE do aluno.
    • Roda 4 queries em paralelo (Promise.all) dentro do range:
      • Tasks: dueDate in range, status PUBLISHED, subject.courseId in matrículas
      • Exams: availableFrom in range, isPublished, subject.courseId in matrículas
      • CalendarEvents: startDate in range, OR: [branchId do aluno, branchId null (global)]
      • Announcements: publishedAt in range, status PUBLISHED, filtro branch, targetRoles inclui STUDENT
    • Filtra announcements que NÃO são para STUDENT (targetRoles pode ser null = público).
    • Normaliza tudo em CalendarItem[] com type, title, dateKey (YYYY-MM-DD), meta (subject, time, isAllDay) e href (link para detalhe da tarefa).
    • Retorna { items, byDateKey: Record<dateKey, CalendarItem[]> } agrupado por dia.
  2. Componente Calendar estendido (src/components/ui/Calendar.tsx):

    • Antes: markers?: Record<string, string> (1 ponto por dia).
    • Agora: markers?: Record<string, string | string[]> (até 4 pontos coloridos por dia, um por tipo). Mantém compatibilidade (string vira [string]).
    • Acessibilidade: aria-label agora inclui contagem ("3 evento(s)").
  3. Página /aluno/agenda (src/app/(dashboard)/aluno/agenda/page.tsx):

    • Layout grid 2 colunas (desktop) / empilhado (mobile):
      • Esquerda: <Calendar> navegável + botão "Hoje" + lenda dos 4 tipos.
      • Direita: painel "Eventos do dia" com lista de CalendarItems do dia selecionado.
    • Cada item no painel: ícone (Lucide por tipo) + borda esquerda colorida + título + meta (disciplina, horário) + descrição + link "Ver tarefa →" se aplicável.
    • Estado vazio: "Nenhum evento neste dia."
    • Suspense + ErrorBoundary + .Skeleton estático (padrão das outras páginas).
    • Trocar de mês no Calendar dispara nova query student.calendar.month automaticamente.
  4. Navegação corrigida + botão novo:

    • StudentShell: item "Calendário" do top-nav apontava para /aluno/frequencia (BUG desde o Card 4) → agora aponta para /aluno/agenda.
    • /aluno/tarefas: botão "Ver agenda" no header (link para /aluno/agenda).
    • /aluno/frequencia continua acessível pelo bottom-nav mobile ("Frequência").
  5. i18n: seção student.calendar (12 chaves: title, today, eventsOfDay, noEvents, legendTask/Exam/Event/Announcement, allDay, viewTask, loading, error) + viewAgenda em student.tasks. PT/EN/ES.

Bug corrigido durante o card

  • StudentShell top-nav: item t('calendar') apontava para /aluno/frequencia desde o Card 4 (placeholder que nunca foi corrigido). Agora /aluno/agenda.

✅ Critérios de aceite validados

# Critério Status
1 Página /aluno/agenda carrega com calendário mensal navegável
2 Dias com eventos mostram pontinhos coloridos por tipo (até 4)
3 Clicar num dia mostra a lista de eventos daquele dia
4 Trocar de mês busca os eventos do novo mês
5 4 fontes agregadas (tarefas, provas, eventos, comunicados)
6 Filtragem por branch do aluno (global + da unidade)
7 Item "Calendário" do menu aponta para /aluno/agenda (bug corrigido)
8 Botão "Ver agenda" na página de tarefas
9 i18n PT/EN/ES
10 Build passa + lint OK
11 Deploy DEV funcional

Validação real da procedure (autenticada, julho/2026)

Itens no mês: 5 | Dias com eventos: 4
Por tipo: { EVENT: 4, ANNOUNCEMENT: 1 }
  [EVENT]        2026-07-08 - Reunião de Pais e Mestres
  [EVENT]        2026-07-15 - Feriado Nacional — Sem aulas
  [EVENT]        2026-07-22 - Mostra de Arte dos Alunos
  [EVENT]        2026-07-28 - Prazo final — Entregas do Bimestre
  [ANNOUNCEMENT] 2026-07-28 - Reunião de pais e mestres

📁 2. Arquivos criados/modificados

Criados

Arquivo Função
src/app/(dashboard)/aluno/agenda/page.tsx Página da agenda (calendário + painel dia + lenda)
scripts/seed_agenda_demo.js Seed: 4 CalendarEvents demo (reunião, feriado global, mostra de arte, prazo)

Modificados

Arquivo Mudança
src/server/trpc/routers/student.ts +sub-router calendar com procedure month (agrega 4 fontes)
src/components/ui/Calendar.tsx markers agora aceita string | string[]; renderiza até 4 pontos por dia
src/components/layout/StudentShell.tsx Corrigido bug: item "Calendário" → /aluno/agenda (antes /frequencia)
src/app/(dashboard)/aluno/tarefas/page.tsx +botão "Ver agenda" no header
src/styles/tarefas-page.module.css +classe .agendaLink
messages/{pt-BR,en-US,es-ES}.json +seção student.calendar (12 chaves) + student.tasks.viewAgenda

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

Nenhuma mudança de schema. A feature reusa 4 modelos já existentes:

  • Task.dueDate (DateTime?) — prazo de entrega
  • Exam.availableFrom (DateTime?) — abertura da prova
  • CalendarEvent (startDate, endDate, isAllDay, type, branchId) — eventos gerais
  • Announcement (publishedAt, targetRoles, branchId) — comunicados

ℹ️ CalendarEvent.type continua String? (livre). O seed usa valores como REUNIAO, FERIADO, EVENTO_SOCIAL, PRAZO. Se no futuro quiser padronizar cores por type, introduzir um enum CalendarEventType (sugestão para o Card 21).


🔌 4. Handoff para o próximo card

API pública

  • student.calendar.month({ year, month }){ items: CalendarItem[], byDateKey: Record<string, CalendarItem[]> }
    • CalendarItem.type: 'TASK' | 'EXAM' | 'EVENT' | 'ANNOUNCEMENT'
    • CalendarItem.dateKey: YYYY-MM-DD (chave para agrupar no calendário)
    • CalendarItem.href: link para detalhe (ex: /aluno/tarefas/{id})

Componente Calendar reutilizável

  • <Calendar value={date} onChange={fn} markers={Record<string, string[]>} initialMonth={date} />
  • markers é Record<YYYY-MM-DD, string[]> onde cada string é uma cor CSS var.
  • Cap de 4 pontos por dia (visual). Mais que 4 tipos: trunca.

Triggers para próximos cards

  • Card 21 (Comunicados & Avisos da Unidade): quando LOCAL/MASTER cria Announcement ou CalendarEvent, eles aparecem automaticamente no calendário do aluno (sem código extra). Criar mutations CRUD de CalendarEvent + Announcement lá.
  • Card 12/13 (Diário/Chamada): quando professor publica Task ou Exam, eles aparecem automaticamente no calendário dos alunos matriculados (filtro por courseId).
  • Card 9 (Acompanhamento PARENT): pais veem agenda dos filhos — reusar student.calendar.month com studentUserId do Student child (ajustar procedure).

Lições aprendidas

  1. Agregação cross-model no procedure: em vez de N endpoints, uma procedure que junta múltiplas tabelas em um shape comum (CalendarItem) simplifica muito o frontend. O custo de 4 queries paralelas (Promise.all) é aceitável para um calendário mensal.
  2. Componente Calendar extensível: o markers como Record<string, string[]> permite múltiplas cores por dia sem mudar a API para quem já usava string.
  3. Login do NextAuth usa campo login (não email): aceita email OU matrícula. Testes de auth devem usar login=.... Anotado para sessões futuras.

📋 5. Deploy DEV

  • URL: https://sistemaescolar.wellka.com.br/aluno/agenda
  • Login demo: aluno@genioon.com.br / Aluno@2026
  • Commit: 2415003
  • PM2: genioon-dev restart ✓ (online)
  • Seed: node scripts/seed_agenda_demo.js criou 4 CalendarEvents:
    • Reunião de Pais (branch da unidade, dia 8)
    • Feriado Nacional (global branchId null, dia 15)
    • Mostra de Arte dos Alunos (branch, dia 22)
    • Prazo final do Bimestre (branch, dia 28)

🧠 6. Contexto gerado para próximas etapas

Para Card 21 (Comunicados & Avisos)

  • Criar mutations local.calendarEvent.{create,update,delete} + local.announcement.{create,publish}
  • Formulário de criação com campos: title, description, startDate, endDate, isAllDay, type, branchId
  • Ao salvar, o evento aparece automaticamente na agenda dos alunos (sem código extra no frontend)

Para Card 8 (Home da Família)

  • Pais veem agenda dos filhos: ajustar student.calendar.month para aceitar studentUserId opcional (default = aluno autenticado; se PARENT, passar userId do filho)

Para Card 9 (Acompanhamento PARENT)

  • Replicar /aluno/agenda para /pais/agenda reusando os mesmos componentes (Calendar, painel)

🎯 7. Próximo card

Card 8 [M2] Home da Família (Mobile)

  • Spec: docs/execucao/08-m2-home-familia.md
  • Foco: portal PARENT mobile-first, onde responsáveis veem tudo do filho
  • Reuso: XPBar/PatentBadge (progresso do filho), TaskCard (tarefas do filho), e agora Calendar/agenda do filho (ajustar procedure para aceitar studentUserId)
← Voltar para a visão geral