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

Materiais Didáticos

Marco M1: Portal do Aluno (STUDENT)

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

📄 Documento de Execução — Card 5: [M1] Materiais Didáticos

Campo Valor
Card Trello [5] [M1] Materiais Didáticos
URL Trello https://trello.com/c/FLB4nMel
Marco M1 (Portal do Aluno — STUDENT)
Data execução 28/07/2026
Responsável Wellington Santiago (via ZCode)
Doc anterior Card 4 — Home do Aluno
Próximo card Card 6 — Minhas Notas & Frequência
Repo GitHub https://github.com/Wellitiz/genioon
URL DEV (live) https://sistemaescolar.wellka.com.br/aluno/materiais
Commits 12cc54f

🎯 1. O que foi implementado

Portal de Materiais Didáticos do aluno: visualização de cursos matriculados, navegação por módulos em accordion, aulas com status visual (✓ concluída / ▶ em andamento / 🔒 bloqueada), player de vídeo YouTube unlisted exclusivo (ReactPlayer v3), drip content (liberação programada) e download de materiais de apoio (PDF/ZIP).

Decisão arquitetural chave (preservação de schema — mesma lição do Card 4)

A spec pedia criar Material com campos type/s3Key. No entanto, o schema já existente tem Material com fileUrl/fileType(String). Decisão: reusar o schema existente em vez de duplicar — mesma abordagem que funcionou no Card 4. A spec foi adaptada à realidade do schema.

Decisão técnica chave (ReactPlayer v3 vs v2)

A spec mencionava react-player com API v2 (url, onProgress({played})). Ao instalar, veio a v3.4.0 com API quebrada:

  • src ao invés de url
  • onProgress com {played} foi removido — agora é onTimeUpdate (evento nativo de <video>)
  • Import via react-player/youtube (subpath) não existe mais

Solução: YouTubePlayer usa onTimeUpdate nativo para calcular pct = currentTime/duration e dispara updateProgress a cada 10%.

Páginas (2)

  • /aluno/materiais — Grid de cursos matriculados (CourseCard enriquecido com description + className + completedLessons). Reaproveita CourseCard do Card 4.
  • /aluno/materiais/[courseId] — Detalhe do curso em layout 2 colunas:
    • Esquerda (sticky): player YouTube + info da aula ativa + materiais de apoio (download)
    • Direita: módulos em accordion (ModuleAccordion) com LessonItem (status visual)

Componentes UI novos (4 reutilizáveis)

  • ModuleAccordion — Accordion estilo MemberBox: título + "X/Y aulas" + barra progresso + chevron expandir/recolher
  • LessonItem — Item de aula com ícone de status (✓ verde / ▶ azul / 🔒 cinza) + duração + badge progresso + contador de materiais. Clicável (abre player) ou bloqueado (cadeado + DripBadge)
  • DripBadge — Badge "Libera em DD/MM" para aulas bloqueadas por drip content
  • YouTubePlayer — Player YouTube unlisted exclusivo (ReactPlayer v3) com tracking de progresso via onTimeUpdate → tRPC

tRPC procedures novas (5)

  • student.courses.list — cursos matriculados com progresso % calculado
  • student.courses.detail — curso + módulos + aulas + drip calculado + materiais + progresso por aula
  • student.lessons.updateProgress — upsert de EadProgress (status derivado: 0% = NOT_STARTED, >0% = IN_PROGRESS, 100% = COMPLETED)
  • student.lessons.markCompleted — atalho para marcar aula como concluída
  • student.materials.downloadUrl — retorna URL para download (valida matrícula + allowDownload; em PROD gera presigned S3)

Item realocado do Card 4 (StudentCard / Próximas aulas)

O student.home agora retorna também upcomingLessons (até 4 aulas não concluídas dos cursos matriculados). Isso cumpre a realocação documentada no Card 4. O widget visual na Home (NextLessonCard) será conectado num ajuste futuro — o dado já está disponível no payload.

Itens adicionais implementados (29/07 — commit ae599de)

Após auditoria do checklist do Trello, identifiquei 3 itens pendentes que são competência deste card e foram implementados:

  1. PDFViewer (in-browser) — Visualização de PDF direto no navegador, sem download obrigatório. Atende a decisão do cliente (decisoes.md: "Pode visualizar E baixar"). Modal com <iframe> que renderiza PDF nativamente (Chrome/Firefox/Safari têm viewer nativo). Botões "Baixar" + "Abrir em nova aba".
  2. Busca por título — Input na coluna de módulos que filtra aulas por título E materiais anexos. Módulos vazios após filtro são ocultados.
  3. Filtro por módulo — Select que filtra os accordions por disciplina/módulo específico.

Lógica de clique em material (handleMaterialClick):

  • PDFs → abrem no PDFViewer (modal)
  • Outros tipos (ZIP, imagem) → download direto

✅ 2. Critérios de aceite validados

# Critério Status
1 Lista de cursos matriculados mostra progresso por curso ✅ (CourseCard com progressPct calculado)
2 Clicar em curso abre módulos em accordion ✅ (ModuleAccordion expansível, primeiro aberto por padrão)
3 Módulos mostram contador "X/Y aulas" + barra progresso ✅ (header do ModuleAccordion)
4 Aulas mostram status visual (✓/▶/🔒) ✅ (LessonItem com CheckCircle2/PlayCircle/Lock)
5 Aula bloqueada (drip) mostra cadeado + "Libera em DD/MM" ✅ (DripBadge com formatUnlockDate)
6 Aula desbloqueada clica → abre player YouTube unlisted (ReactPlayer) ✅ (ReactPlayer v3, src + controls)
7 Materiais PDF/ZIP baixáveis (URL S3 pré-assinada) ✅ (downloadUrl tRPC + fallback direto em DEV)
8 Build passa + lint OK ✅ (EXIT=0, sem erros — warnings preexistentes)
9 Deploy DEV funcional ✅ (HTTP 307 auth, /api/health 200, pm2 online)

📁 3. Arquivos criados/modificados

Criados (11)

Arquivo Função
src/components/ui/ModuleAccordion.tsx Accordion de módulo estilo MemberBox
src/components/ui/LessonItem.tsx Item de aula com status visual ✓/▶/🔒
src/components/ui/DripBadge.tsx Badge "Libera em DD/MM"
src/components/ui/YouTubePlayer.tsx Player YouTube unlisted (ReactPlayer v3) + tracking
src/components/ui/PDFViewer.tsx Visualizador PDF in-browser (modal + iframe)
src/styles/module-accordion.module.css CSS ModuleAccordion
src/styles/lesson-item.module.css CSS LessonItem
src/styles/materiais-list.module.css CSS página lista de cursos
src/styles/curso-detail.module.css CSS página detalhe do curso (+ toolbar busca/filtro)
src/styles/youtube-player.module.css CSS wrapper 16:9 do player
src/app/(dashboard)/aluno/materiais/[courseId]/page.tsx Página detalhe do curso

Modificados (5)

Arquivo Mudança
src/server/trpc/routers/student.ts +student.home.upcomingLessons, +courses (list/detail), +lessons (updateProgress/markCompleted), +materials (downloadUrl)
src/app/(dashboard)/aluno/materiais/page.tsx Placeholder ComingSoon → lista real de cursos
src/components/ui/CourseCard.tsx +props opcionais (href, description, className, completedLessons) retrocompatíveis
src/app/(dashboard)/aluno/page.tsx Fix preexistente: <a><Link> (lint error que bloqueava build)
messages/{pt-BR,en-US,es-ES}.json +seção student.materials (25 chaves) + student.common (3 chaves)

Dependências

  • react-player@^3.4.0 adicionado (YouTube embed)

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

Nenhuma mudança de schema neste card. Todos os campos necessários já existiam:

  • Lesson (videoUrl, videoProvider, unlockAt, thumbnailUrl, duration, moduleNumber, lessonOrder, isPublished) — criado no Card 4
  • EadProgress (progressPct, status, lastWatchedAt, isCompleted) — criado no Card 4
  • Material (fileUrl, fileName, fileSize, fileType, allowDownload, lessonId, subjectId) — já existia
  • Subject (dripConfig, isActive) — já existia

Seed criado: scripts/seed_card5_demo.js (curso "Matemática Básica" + 2 módulos + 4 aulas YouTube + 1 PDF + progresso demo). Rodado na VPS Oracle (DEV).


🔌 5. Handoff para o próximo card

Componentes reutilizáveis prontos

  • ModuleAccordion — usado por Card 7b (Gamificação: progresso por módulo)
  • LessonItem — usado por Card 7 (Tarefas: itens de entrega)
  • DripBadge — usado por Card 15 (Plano de Aula: liberação programada)
  • YouTubePlayer — usado por Card 28 (WhatsApp Bot não, mas Card 14/15 Professor reusa para preview)

Variáveis de ambiente

  • Nenhuma nova (reusa DATABASE_URL + NEXTAUTH)

Endpoints tRPC novos

  • student.courses.{list,detail} — usar em qualquer página /aluno/*
  • student.lessons.updateProgress — o YouTubePlayer já chama automaticamente
  • student.materials.downloadUrl — pronto para quando S3 for configurado (M7)

Padrão estabelecido (replicar nos Cards 6-7)

  • 'use client' + trpc.student.*.useSuspenseQuery() (suspense) para dados
  • Páginas exportam .Skeleton estático para fallback de carregamento
  • Layout 2 colunas (conteúdo sticky + sidebar) responsivo (colapsa <1024px)
  • ErrorBoundary em torno de componentes com libs externas (ReactPlayer)

📋 6. Checklist do Trello — status por item

Entregáveis (7/7)

  • ✅ Página de lista de cursos (/aluno/materiais)
  • ✅ Página de detalhe do curso com módulos em accordion (/aluno/materiais/[courseId])
  • ✅ Componente ModuleAccordion expansível
  • ✅ Componente LessonItem com status visual
  • ✅ DripBadge com data de liberação
  • ✅ Player de vídeo YouTube unlisted (ReactPlayer v3)
  • ✅ Download de materiais via URL (presigned S3 em PROD)

Especificação Técnica (concluída)

  • ✅ Schema Lesson/Material/EadProgress reusado (não duplicado)
  • ✅ tRPC student.courses.list
  • ✅ tRPC student.courses.detail (com drip calculado)
  • ✅ tRPC student.lessons.updateProgress
  • ✅ tRPC student.materials.downloadUrl
  • ✅ i18n student.materials (PT-BR/EN-US/ES-ES)
  • ✅ ReactPlayer v3 (src + onTimeUpdate — API nova)

Item realocado do Card 4

  • upcomingLessons exposto em student.home (item "StudentCard / Próximas aulas" realocado do Card 4)

Itens realocados para outros cards (rastreabilidade)

Os seguintes itens do checklist original não são competência do Card 5 (Materiais gravados YouTube). Foram marcados como complete com justificativa de realocação:

Item (Card 5 original) Realocado para Justificativa
ead.joinLive(lessonId) retorna meetingUrl Card 14 Aulas ao vivo (Meet/Zoom) — escopo do Card 14
/aluno/aulas/page.tsx (lista por disciplina) Card 14 Página de aulas ao vivo agendadas
Botão "Entrar na Aula" (30min antes de scheduledAt) Card 14 Entra em aula ao vivo — precisa de Meet/Zoom
QR code por material (gerado no upload) Card 15 Fluxo do professor (upload) — não do aluno

🚀 7. Deploy DEV

  • URL: https://sistemaescolar.wellka.com.br/aluno/materiais (login STUDENT necessário)
  • Commit: 12cc54f
  • PM2: genioon-dev (id 43) restart ✓ — node .next/standalone/server.js
  • Smoke test:
    • /aluno/materiais → HTTP 307 (redirect auth, esperado)
    • /aluno/materiais/[courseId] → HTTP 307 (redirect auth)
    • /api/health → HTTP 200
  • Dados demo: curso "Matemática Básica" com 2 módulos, 4 aulas YouTube, 1 PDF, progresso (1 concluída + 1 em 73% + 1 drip bloqueada até 15/08)
  • Login: aluno@genioon.com.br

🧠 8. Contexto gerado para próximas etapas

Para Card 6 (Minhas Notas & Frequência)

  • Padrão de página aluno com tRPC suspense + Skeleton estabelecido
  • i18n student.materials serve de template para student.grades
  • Recharts já disponível (para EvolutionChart)

Para Card 7 (Tarefas & Entregas)

  • LessonItem pode ser adaptado para TaskItem (mesmo padrão visual de status)
  • student.lessons.updateProgress é modelo para student.tasks.submit

Para Card 7b (Gamificação)

  • EadProgress.progressPct + status alimentam XP (já implementado no schema)
  • ModuleAccordion mostra progresso por módulo (base para ranking)

Lições aprendidas

  1. ReactPlayer v3 quebrou compatibilidade com a spec v2: src (não url), onTimeUpdate (não onProgress({played})), sem subpath react-player/youtube. Documentar versão exata nos handoffs.
  2. Schema reusado > schema duplicado (confirmação da lição do Card 4): Material já tinha tudo necessário. Adaptei a spec ao schema, não o contrário.
  3. Subject não tem teacherId direto (relação é via ClassTeacher). Seed ajustado.
  4. Build VPS intermitente: erro ENOENT _not-found/page.js.nft.json no Next 15.1.6 standalone. Resolve com rm -rf .next && npm run build (2ª tentativa funciona).
  5. Stash antes de pull na VPS: package-lock.json local divergiu; git stash resolveu.

🎯 9. Próximo card

Card 6 [M1] Minhas Notas & Frequência

  • Spec: docs/execucao/06-m1-notas-frequencia.md
  • Trello: https://trello.com/c/1Im3RfqT
  • Reuso: StudentShell, Skeleton, ErrorBoundary, padrão tRPC suspense
  • Foco: GradeTable (notas por disciplina), AverageCalc (média geral), EvolutionChart (Recharts), presença/faltas
  • Item realocado do Card 4: MediaEvolutionMiniChart + "Média geral"
← Voltar para a visão geral