✅ EXECUÇÃO — 15/08/2026
Implementado e validado em produção (homolog). Commits: 2062f6c (fundação: 21 tabelas), 080c857 (features), bbfa277 (lint). Deploy CI verde.
Validação ao vivo: página(s) da feature respondendo 200 com a role correta + procedures tRPC testadas via HTTP (respostas JSON corretas). Ver relatório da sessão.
Detalhes de implementação e decisões: conforme o design acima deste documento (schema, procedures e páginas seguidos conforme especificado; ajustes menores documentados no commit).
📄 Card 47 [M8] — Enquetes (Forms com Trava de Tela)
📚 Referência SULTS: https://www.sults.com.br/produtos/enquete Pesquisa e coleta de dados com bloqueio de tela no login (máxima adesão sem cobrança manual), modelos reutilizáveis e dashboards em tempo real. Estilo Google Forms corporativo.
🎯 Objetivo
Criar o módulo Enquetes — pesquisa com pais/professores/alunos/colaboradores com:
- Tipos de campo: texto livre, múltipla escolha, seleção única, escala de avaliação
- Modelos reutilizáveis (salva como template)
- Trava de tela no login: formulário em tela cheia — colaborador responde pra acessar
- Segmentação por filial/role
- Dashboards em tempo real (pizza/barra)
- Exportação Excel
📚 Documentação de Referência
docs/DESIGN_SYSTEM.md§6 — componentes nativosprisma/schema.prisma— novos models
🛠️ Especificação Técnica
Schema Prisma (novo)
model Survey {
id String @id @default(cuid())
title String
description String?
status SurveyStatus @default(DRAFT) // DRAFT, ACTIVE, CLOSED
isBlocking Boolean @default(false) // trava de tela
isTemplate Boolean @default(false) // template reutilizável
targetScope NewsScope @default(ALL) // reusa enum do Card 44
branches String[] // filiais-alvo
roles String[] // roles-alvo
questions SurveyQuestion[]
responses SurveyResponse[]
startsAt DateTime?
endsAt DateTime?
createdBy String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model SurveyQuestion {
id String @id @default(cuid())
surveyId String
survey Survey @relation(fields: [surveyId], references: [id])
text String
type QuestionType // TEXT, SINGLE_CHOICE, MULTI_CHOICE, SCALE, RATING
required Boolean @default(true)
options String[] // para CHOICE
scaleMin Int? // para SCALE
scaleMax Int?
order Int @default(0)
}
model SurveyResponse {
id String @id @default(cuid())
surveyId String
survey Survey @relation(fields: [surveyId], references: [id])
respondentId String
respondent User @relation(fields: [respondentId], references: [id])
answers Json // {questionId: value}
submittedAt DateTime @default(now())
@@unique([surveyId, respondentId])
}
enum SurveyStatus { DRAFT ACTIVE CLOSED }
enum QuestionType { TEXT SINGLE_CHOICE MULTI_CHOICE SCALE RATING }
Rotas (page.tsx)
/master/enquetes— painel admin (CRUD + criar + agendar)/master/enquetes/[id]/resultados— dashboards (Recharts)- Componente
<SurveyGate />— wrapper que bloqueia navegação se há enquete pendente
Procedures tRPC (master.surveys, student.surveys, parent.surveys, etc.)
create/update/delete/publish/closesaveAsTemplate/createFromTemplatependingForUser(retorna enquetes pendentes para o usuário logado)submit(registra resposta)results(agregado por questão com %)
Lógica de trava de tela
- Hook
useSurveyGate(): ao montar AppShell, verifica se hápendingForUsercomisBlocking=true - Se sim, renderiza
<SurveyGate />em tela cheia — usuário responde pra liberar - Não bloqueante:
isBlocking=false→ só badge + lembrete
✅ Critérios de Aceite
- MASTER cria/edita enquetes com 5 tipos de pergunta
isBlocking=truetrava a tela no próximo login do usuário-alvo- Salvar como template + criar a partir de template
- Dashboards em tempo real (pizza/barra) por questão
- Exportação Excel das respostas
- Segmentação: por filial/role
- Build passa + lint OK
🔌 Handoff
- Hook
useSurveyGate()plugado no AppShell - Procedures
*.surveysem cada router de role - Cron
survey-reminder(Job Bull): lembrete diário se pendente
🎯 Próximo
Card 48: LGPD Editor de Termos Versionado