📄 Documento de Execução — Card 4: [M1] Home do Aluno
| Campo | Valor |
|---|---|
| Card Trello | [4] [M1] Home do Aluno |
| URL Trello | https://trello.com/c/or6OPA9a |
| Marco | M1 (28/07/2026 — início Portal do Aluno) |
| Data execução | 28/07/2026 |
| Responsável | Wellington Santiago (via ZCode) |
| Doc anterior | Card 3 — Design System & Tokens Base |
| Próximo card | Card 5 — Materiais Didáticos |
| Repo GitHub | https://github.com/Wellitiz/genioon |
| URL DEV (live) | https://sistemaescolar.wellka.com.br/aluno (login STUDENT) |
| Commits | 8d8618f |
🎯 1. O que foi implementado
Primeira tela do aluno após login, no layout híbrido inspirado no MemberBox (decisão UX 28/07/2026). Mostra "continue assistindo", grid de cursos estilo Netflix, novidades (comunicados) e próximos eventos do calendário.
Decisão arquitetural chave (preservação de schema)
A spec pedia criar Module + Lesson + LessonProgress do zero. No entanto, o schema já existente (criado na M0) já tinha Lesson (vinculado a Subject), EadProgress, Material, Enrollment, Notification, Announcement, CalendarEvent. Decisão: enriquecer o schema existente em vez de duplicar — evita conflito com os Cards 5-7 já especificados.
Campos adicionados ao schema existente:
Subject.dripConfig(Json?) — liberação programada de disciplina (Card 5)Lesson.unlockAt(DateTime?) — drip content por aulaLesson.thumbnailUrl(String?) — thumbnails estilo NetflixEadProgress.progressPct(Int, default 0) — % de progresso por aulaEadProgress.lastWatchedAt(DateTime?) — ordena "continue assistindo"EadProgress.status(EadLessonStatus) — NOT_STARTED | IN_PROGRESS | COMPLETED- Novo enum
EadLessonStatus - Index
(studentId, lastWatchedAt)para query "continue assistindo" performática
StudentShell híbrido (layout MemberBox)
- Desktop (≥1024px): header fixo 64px + top nav horizontal (Início/Cursos/Notas/Calendário/Perfil) + busca Cmd+K + sino notificações + theme toggle + avatar dropdown
- Mobile (<1024px): reaproveita o MobileShell do Card 3 (header 56px + bottom nav 5 itens) — mesmo padrão visual já validado
- Detecção de breakpoint via
matchMedia(SSR-safe: começa mobile, hidrata com o real)
Página /aluno (Home)
- Hero: "Olá, {firstName}! 👋" + branch + RA
- Continue assistindo: aula IN_PROGRESS com
lastWatchedAtmais recente (card grande com thumbnail + play + barra %) - Meus cursos: grid Netflix (auto-fill minmax 220px) com CourseCard (thumbnail + título + professor + barra %)
- 2 colunas: Novidades (comunicados publicados para o branch) + Próximos eventos (CalendarEvent próximos 30 dias)
- Estados: loading (Skeleton), erro (Card amigável), sem Student (Card boas-vindas)
Componentes UI novos (3 reutilizáveis)
- CourseCard — thumbnail 16:9 (ou gradiente fallback + ícone BookOpen) + badges (% ou ✓ Concluído) + barra de progresso. Link para
/aluno/materiais?curso={id} - ContinueWatching — card horizontal com thumbnail + play overlay + título + duração + barra % + botão "Retomar aula"
- NotificationCenter — sino dropdown com badge contador (não lidas), lista scrollável, "Marcar todas", fecha fora + ESC, aria-expanded
tRPC router student (3 procedures)
student.home— query que retorna: student info + enrollments com % calculado + continueLesson + announcements (filtrados por targetRoles STUDENT) + upcomingEvents (30 dias)student.notifications.list— lista 8 notificações + contador não lidasstudent.notifications.markAllRead— marca todas como lidasstudent.notifications.markRead— marca uma específica
✅ 2. Critérios de aceite validados
| # | Critério | Status |
|---|---|---|
| 1 | Login como aluno demo funciona | ✅ (fluxo auth intacto, redirect 307 se não logado) |
| 2 | Desktop mostra top nav horizontal (não sidebar) | ✅ (StudentShell desktop com TOP_NAV horizontal) |
| 3 | Mobile mostra bottom nav (MobileShell) | ✅ (reaproveita MobileLayout inline) |
| 4 | Home mostra "Continue assistindo" se houver progresso | ✅ (continueLesson condicional) |
| 5 | Grid de cursos com barra de progresso por curso | ✅ (CourseCard com progressPct calculado) |
| 6 | Seção "Novidades" mostra comunicados ativos | ✅ (announcements PUBLISHED do branch) |
| 7 | Sino de notificações funcional (dropdown) | ✅ (NotificationCenter com markAllRead) |
| 8 | Build passa + lint OK | ✅ (EXIT=0, sem warnings novos) |
| 9 | Deploy DEV funcional em /aluno | ✅ (HTTP 307 auth, build VPS OK) |
📁 3. Arquivos criados/modificados
Criados (12)
| Arquivo | Função |
|---|---|
src/components/layout/StudentShell.tsx |
Shell híbrido desktop/mobile do STUDENT |
src/components/ui/CourseCard.tsx |
Card de curso estilo Netflix |
src/components/ui/ContinueWatching.tsx |
Card grande da última aula |
src/components/ui/NotificationCenter.tsx |
Sino dropdown de notificações |
src/server/trpc/routers/student.ts |
Router tRPC student.home + notifications |
src/app/(dashboard)/aluno/page.tsx |
Página Home do Aluno |
src/styles/student-shell.module.css |
CSS StudentShell (topbar + topnav + main) |
src/styles/course-card.module.css |
CSS CourseCard |
src/styles/continue-watching.module.css |
CSS ContinueWatching |
src/styles/notification-center.module.css |
CSS NotificationCenter |
src/styles/aluno-home.module.css |
CSS Home (hero + grid + 2 colunas) |
prisma/migrations/20260728000000_m1_student_home_drip_progress/migration.sql |
Migration SQL |
Modificados (3)
| Arquivo | Mudança |
|---|---|
prisma/schema.prisma |
+Subject.dripConfig, +Lesson.{unlockAt,thumbnailUrl}, +EadProgress.{progressPct,lastWatchedAt,status}, +enum EadLessonStatus, +index |
src/components/layout/AppShell.tsx |
STUDENT → StudentShell (era MobileShell); imports + comment |
src/server/trpc/router.ts |
+import studentRouter; +student no appRouter |
🗄️ 4. Schema do banco (mudanças)
Campos novos
subjects.dripConfigJSONB — drip content (Card 5 vai consumir)lessons.thumbnailUrlTEXT — thumbnails Netflixlessons.unlockAtTIMESTAMP(3) — drip content por aulaead_progress.progressPctINTEGER NOT NULL DEFAULT 0ead_progress.lastWatchedAtTIMESTAMP(3) — ordena "continue assistindo"ead_progress.statusEadLessonStatus NOT NULL DEFAULT 'NOT_STARTED'
Enum novo
CREATE TYPE "EadLessonStatus" AS ENUM ('NOT_STARTED', 'IN_PROGRESS', 'COMPLETED');
Index novo
CREATE INDEX "ead_progress_studentId_lastWatchedAt_idx"
ON "ead_progress"("studentId", "lastWatchedAt");
Migration
20260728000000_m1_student_home_drip_progress— aplicada em DEV (VPS Oracle)
🔌 5. Handoff para o próximo card
Componentes reutilizáveis prontos
- CourseCard — usado por Card 5 (Materiais: grid de cursos) e Card 7b (Gamificação: cursos concluídos)
- NotificationCenter — usado por todos os cards do STUDENT (já plugado no StudentShell)
- StudentShell — usado por TODAS as rotas
/aluno/*(Cards 4-7 + 7b) - ContinueWatching — usado por Card 5 (retomar aula)
Variáveis de ambiente
- Nenhuma nova (reusa DATABASE_URL + NEXTAUTH existentes)
Endpoints tRPC novos
student.home— usar em qualquer página/aluno/*student.notifications.*— já plugado no StudentShell
Padrão estabelecido (replicar nos Cards 5-7)
'use client'+trpc.student.*.useQuery()para dados- CSS Modules com
var(--*)(sem cores hardcoded — dark mode automático) - Layout responsivo via
matchMediaou CSS media queries - Estados: loading (Skeleton), erro (Card), vazio (Card)
- Icons lucide-react
strokeWidth={1.5}(DESIGN_SYSTEM.md §7)
📋 6. Checklist do Trello — status por item
Especificação Técnica (concluída)
- ✅ Página
src/app/aluno/page.tsx(Home STUDENT) - ✅ StudentShell híbrido (top nav desktop + bottom nav mobile)
- ✅ CourseCard (thumbnail + título + instrutor + barra %)
- ✅ ContinueWatching (card grande + botão Retomar)
- ✅ NotificationCenter (sino dropdown)
- ✅ AppShell atualizado (STUDENT → StudentShell)
- ✅ nav-config student items
- ✅ tRPC student.home
- ✅ tRPC student.notifications.list
- ✅ Schema expansão (dripConfig, unlockAt, progressPct, lastWatchedAt, status)
- ✅ Migration
m1_student_home_drip_progress - ✅ Saudação personalizada por horário (Bom dia/tarde/noite + nome) — commit
0cd9f52 - ✅ Avisos fixados (isPinned) em destaque — commit
0cd9f52 - ⚠️ Seed curso demo "Matemática Básica" — pendente (Cards 5-7 dependem; será criado no Card 5 com dados realistas)
🔄 Itens realocados para cards subsequentes (29/07/2026)
Itens do checklist original do Trello que não são competência do Card 4 (dependem de dados de cards futuros). Foram movidos para os respectivos cards e comentados lá:
| Item (Card 4 original) | Realocado para | Justificativa |
|---|---|---|
MediaEvolutionMiniChart (Recharts AreaChart) |
Card 6 | Precisa das notas (só existem após Card 6). Card 6 já tem EvolutionChart no checklist |
| Entrega "Média geral" | Card 6 | Média calculada no Card 6 (GradeTable/AverageCalc) |
TaskCounter (tarefas pendentes) |
Card 7 | Precisa das tarefas (só existem após Card 7) |
StudentCard (próxima aula) + Entrega "Próximas aulas/tarefas" |
Card 5 | Card 5 tem o player de aulas e controle de progresso |
Push notification permission request (PWA) |
Card 8 | PWA/Service Worker é arquitetura do Card 8 (Home da Família Mobile) |
💡 Implementação cruzada: quando os Cards 5, 6, 7 e 8 forem implementados, cada um deve expor um campo no
student.hometRPC (upcomingLessons,currentAvg,pendingTasksCount, respectivamente) para alimentar widgets na Home do aluno.
🚀 7. Deploy DEV
- URL: https://sistemaescolar.wellka.com.br/aluno (login STUDENT necessário)
- Commit:
8d8618f - Migration:
20260728000000_m1_student_home_drip_progressaplicada - PM2: genioon-dev (id 39) reload ✓
- Smoke test: HTTP 307 (redirect auth) — middleware protege
/aluno; build VPS ✓
🧠 8. Contexto gerado para próximas etapas
Para Card 5 (Materiais Didáticos)
- Schema
Lesson.thumbnailUrl+unlockAt+Subject.dripConfigjá prontos - CourseCard reutilizável para grid de cursos
- ContinueWatching reutilizável (botão "Retomar")
- YouTube unlisted embed: usar
Lesson.videoUrl+videoProvider(já existe)
Para Card 7b (Gamificação)
EadProgress.progressPct+statusalimentam cálculo de XP- Schema de StudentXP/Achievement virá no Card 7b
Para Cards M2+ (PARENT etc.)
- PATTERN tRPC estabelecido:
{role}.{module}.{action}(ex:parent.home,parent.notifications.*) - Replicar o StudentShell pattern: criar
ParentShellse necessário (ou reaproveitar MobileShell)
Lições aprendidas
- Schema enriquecido > schema duplicado: o schema já tinha Lesson/EadProgress; adicionei campos em vez de criar Module/LessonProgress do zero. Evita migração dolorosa e conflito com Cards 5-7.
- Prisma 7 mudanças:
migrate diffnão aceita--shadow-database-url(removido). Usar--from-schema+--to-schemapara diff manual. - tRPC v11
useUtils(): retorna apenas o utilitário (não é mais array[utils, ctx]). Invalidar viautils.student.notifications.list.invalidate(). - Prisma
classreserved: relationClassno modelEnrollmentchama-seclass_(underscore) porqueclassé palavra reservada. - SSR matchMedia: StudentShell começa mobile (evita hydration mismatch) e hidrata com breakpoint real via useEffect.
🎯 9. Próximo card
Card 5 [M1] Materiais Didáticos
- Spec:
docs/execucao/05-m1-materiais.md - Trello: https://trello.com/c/{id}
- Reuso: CourseCard, ContinueWatching, StudentShell, schema Lesson/Material pronto
- Foco: player YouTube unlisted embed (ReactPlayer), drip content UI, accordion de módulos
- Diferencial vs MemberBox: nosso escopo mantém S3 para PDFs/ZIPs + download controlado