UniPass
Produto · Painel web

Gestão

Painel administrativo (Next.js + Tailwind) usado por gestores municipais e operadores de frota das prefeituras. Voltagem institucional da IDV Unipass: cor de marca só em ação/navegação, superfícies neutras, sem mosaico cheio nem fonte rounded — ver arquitetura do ecossistema.

1. Visão geral

O Gestão é a superfície onde gestores municipais e operadores de frota administram todo o ciclo operacional do transporte universitário: cadastros base (estudantes, motoristas, veículos, rotas), planejamento e publicação da operação diária, disciplina, comunicação, relatórios e governança (auditoria, backup). Estudantes e motoristas não acessam o Gestão — usam os apps mobile dedicados; se um usuário com esses papéis tentar entrar, é redirecionado automaticamente para o app correspondente.

Papéis que usam o Gestão

Admin Gestor Operador

Papéis que NÃO usam o Gestão

Motorista Estudante — redirecionados ao app mobile

2. Controle de acesso (RBAC)

Atenção — divergência doc-vs-código

Documentação institucional (regra global de segurança do harness, o CLAUDE.md e a apresentação institucional) descreve um modelo "3-gate RBAC" (status + role + isolamento de tenant). O código-fonte real (sgtu-gestao/src/lib/rbac.ts) documenta explicitamente que hoje é 2-gate: o terceiro gate (isolamento por tenant/prefeitura) foi removido quando o sistema virou single-tenant por instância/VPS (commit 9444858) e o contexto de tenant nunca chegou a ser populado. Esta wiki descreve o comportamento real — use "2-gate" na documentação voltada ao usuário final, não "3-gate".

2.1 Gate 1 — Estado da entidade

Cada tipo de entidade de negócio (viagem, veículo, rota, estudante, motorista, manutenção, dia de operação, confirmação, calendário) tem sua própria tabela de "estado atual → ações permitidas". Exemplo: uma viagem concluída só permite visualizar; uma viagem rascunho permite ver, editar, excluir, cancelar e atribuir motorista/veículo.

2.2 Gate 2 — Papel do usuário

PapelAções permitidas (regra geral do painel)
AdminTodas: ver, criar, editar, excluir, aprovar, cancelar, iniciar, concluir, atribuir
OperadorVer, criar, editar, excluir, cancelar, iniciar, concluir, atribuir — sem aprovar
GestorVer, criar, editar, aprovar, cancelar — sem excluir, iniciar, concluir ou atribuir

A ação final permitida é a interseção dos dois gates — precisa passar pelo estado da entidade E pelo papel do usuário.

2.3 Bloqueio de rota inteira (nível de página)

Grupo de rotasQuem acessaRotas
Admin-onlyAdmin/auditoria, /backup
Admin + GestorAdmin Gestor/usuarios, /configuracoes, /operacao-dia
Qualquer staffAdmin Gestor OperadorEstudantes, Motoristas, Veículos, Rotas, Pontos, Instituições, Calendários, Comunicação, Centro Operacional, Agenda, Relatórios, Dashboard, Notificações
Exceção — menu e permissão de acesso divergem

Em /usuarios e /configuracoes, o gestor tem permissão de acessar por URL direta (o proxy libera), mas o link não aparece no menu lateral para esse papel (o item de navegação é restrito a admin). Em /configuracoes especificamente, o gestor até chega na tela, mas quase todo campo aparece travado (disabled) com aviso "Apenas administradores podem editar...". Trate isso como comportamento real do sistema ao escrever o tutorial — não como bug a ignorar.

2.4 Autenticação e sessão

Token JWT carrega apenas user_id e role — sem tenant_id (confirma o 2-gate). Quatro cookies:

CookieHttpOnlyPropósito
sgtu-access-tokenSimJWT de acesso (15 min)
sgtu-refresh-tokenSim, path restrito a /api/auth/refreshJWT de renovação (7 dias)
sgtu-user-infoNão (assinado)E-mail/papel para trilha de auditoria — lido sem decodificar o JWT
sgtu-auth-tokenApenas em modo mock de desenvolvimento
Regra de negócio

Fail-closed deliberado: se o cookie de papel estiver ausente ou corrompido, o sistema não assume "quase autenticado" — limpa a sessão inteira e força novo login. Login aceita e-mail ou CPF (para estudantes/motoristas, que fazem login por CPF quando acessam via web em contexto de suporte).

3. Mapa de módulos

Cada tela do painel mapeia a um bounded context do backend. Referência para localizar regra de negócio ↔ tela.

Bounded context (backend)Telas do Gestão
FrotaVeículos, Motoristas
Rede de RotasRotas, Pontos de Embarque, Instituições, Calendários
EstudantesEstudantes
Operação DiáriaAgenda, Operação do Dia, Centro Operacional
DisciplinaAba Disciplina do Dashboard + Ocorrências/Penalidades (via perfil do estudante)
ComunicaçãoComunicação, Notificações
Tenant/AuthUsuários, Perfil
BackupBackup
AnalyticsDashboard, Relatórios, Auditoria (trilha própria via django-auditlog)

4. Cadastros base

4.1 Estudantes

Propósito: cadastro do beneficiário do transporte — dados pessoais, vínculo acadêmico (IES/curso/turno) e vínculo de transporte (rota/ponto). Todo estudante é obrigatoriamente também uma conta de usuário; o backend cria a conta automaticamente.

Ações

Listar (KPI cards clicáveis como filtro), criar, editar, desativar (soft delete), importar CSV (wizard de 4 passos), exportar CSV, reset de senha, gerenciar vínculo de rota, aprovar documentos

Identificador único

card_token gerado automaticamente (SGTU-{16 hex}) — é a base da carteirinha digital

Regra de negócio — GAP-PRD-002 (corrigida)

Um estudante pode ter vínculo ativo em mais de uma rota (ex.: manhã + tarde), mas nunca dois vínculos ativos no mesmo turno — constraint de unicidade condicional garante isso desde 2026-07-04. Antes disso era possível cadastrar 2 rotas no mesmo turno silenciosamente, corrompendo contagem de vagas e manifesto — já corrigido, mas cite como exemplo de por que essa regra existe.

Validações-chave: CPF com dígito verificador real (não só formato); idade entre 14–80 anos quando data de nascimento informada; telefone 10/11 dígitos; CEP 8 dígitos; ao confirmar presença "fora do manifesto", justificativa do operador com no mínimo 10 caracteres.

Exceção — cadastro intencionalmente parcial

O botão de submit no cadastro diz "Cadastrar e Completar Perfil" — é esperado que o gestor crie um registro enxuto primeiro (nome, CPF, IES) e complete endereço/vínculo depois. Ações em massa (ativar/inativar/exportar seleção) não estão implementadas no backend — o sistema avisa honestamente "estará disponível em breve" em vez de simular sucesso.

4.2 Motoristas

Propósito: cadastro de motoristas e monitores — CNH, disponibilidade semanal por turno, indisponibilidades pontuais. Monitor é um Motorista com tipo="monitor", não uma entidade separada.

Ações

Listar, criar, editar, excluir, reset de senha, exportar CSV, CRUD de indisponibilidades embutido

Validação de CNH

11 dígitos + categoria compatível com o tipo de veículo (ônibus exige D/E/AD/AE)

Regra de negócio

Motorista recebe acesso ao app usando o CPF como senha no primeiro login, com troca obrigatória. CNH já vencida não pode ser cadastrada na criação (o seletor de data trava o mínimo em hoje). Badge de expiração (vencida / vence em ≤30 dias / válida) é calculado no cliente a partir da data de validade.

Exceção — substituição sem republicar o dia

Um motorista pode ser substituído numa viagem já publicada sem precisar republicar o dia inteiro. O sistema exige um motivo (doença, falta, CNH vencida, férias, licença, outro), registra quem aprovou e notifica o motorista assíncronamente. O ranking de "reservas disponíveis" pondera compatibilidade de CNH, disponibilidade no turno e ausência de conflito de escala.

4.3 Veículos

Propósito: cadastro da frota — identificação, especificações, status, motorista padrão por turno, documentos de conformidade e manutenção.

Placa

Aceita formato antigo (ABC1234) e Mercosul (ABC1D23)

Capacidade

1 a 60 lugares · Ano de fabricação: 1990 até ano atual + 1

Exceção — funcionalidades parcialmente conectadas

As abas de Manutenção e Abastecimento existem no backend (com regras completas), mas o frontend atual não tem formulário de criação conectado para essas duas telas — o gestor consegue ver, mas ainda não cadastrar diretamente pelo painel. Documentos de conformidade (CRLV, seguro, vistoria) não fazem parte do formulário principal — precisam salvar o veículo primeiro e depois anexar via a aba Conformidade.

Um motorista só pode ser "padrão" de um veículo por turno, e um veículo só tem um motorista padrão por turno (constraint de unicidade condicional).

4.4 Rotas

Propósito: o módulo de cadastro com a lógica mais densa — trajetos conectando pontos de embarque a instituições, com sequência ordenada de paradas, turno, motorista padrão, dias de funcionamento e cutoff de confirmação.

Conceito — Cutoff

Prazo máximo para o estudante confirmar presença no dia seguinte. Pode ser um horário explícito (ex.: 14h, 1 dia antes) ou derivado automaticamente de "3h antes do primeiro ponto da rota" quando não configurado. cutoff_desabilitado=true (modo piloto) aceita confirmações a qualquer momento, sem prazo.

Regra de negócio — estrutura de paradas (F8)

Toda rota precisa ter no mínimo 2 pontos, com exatamente 1 origem e 1 destino marcados — o restante são pontos intermediários (cada um precisa ter embarque OU desembarque marcado, ao menos um). A primeira parada (ordem 1) é travada automaticamente como origem; o destino é um toggle exclusivo (marcar um desmarca o anterior). O editor de paradas usa drag-and-drop com suporte a teclado, e o botão de submit fica travado até a contagem "Origem 1/1, Destino 1/1" estar correta — esta validação é espelhada no backend (o cliente falha rápido, mas o servidor revalida do zero).

Exceção

O texto de ajuda "horários devem ser sequenciais" é apenas informativo — não é validado nem no cliente nem no servidor; é possível salvar uma rota com horários fora de ordem. Um campo legado (tipo de parada) ainda é mantido para compatibilidade com consumidores antigos (app mobile anterior à unificação, analytics) durante a janela de transição.

4.5 Pontos de Embarque

Propósito: locais físicos georreferenciados usados como blocos de construção das rotas.

Regra de negócio

Formulário combina busca de endereço (Google Places) com mapa interativo (marcador arrastável com geocodificação reversa) — os dois lados ficam sincronizados. Coordenadas manuais existem como campo recolhido (escape hatch), fluxo primário é visual. Ao mover/digitar um ponto, o sistema varre todos os pontos cadastrados e avisa (sem bloquear) se houver outro ponto a 100 metros ou menos — proteção contra duplicidade geográfica.

Exceção

Excluir um ponto vinculado a rotas é bloqueado (o sistema lista até 5 rotas que o usam — é preciso desvincular antes). Desativar um ponto (em vez de excluir) faz ele parar de aparecer nas rotas — efeito colateral não-óbvio que precisa estar no tutorial.

4.6 Instituições (IES)

Propósito: universidades/campi atendidos — endereço, janelas de operação (horários de entrada/saída por turno) e regras/restrições locais.

Janela de operação: início e fim no formato HH:MM, fim sempre depois do início, tolerância de 0 a 60 minutos. Regras de instituição só podem ser criadas ou apagadas — nunca editadas (força recriar em vez de alterar histórico).

4.7 Calendários

Propósito: calendário acadêmico por instituição — datas de semestre e períodos internos (aulas, provas, recesso, férias, feriado, evento) que determinam quando o transporte opera.

Exceção

Cada período tem datas de início/fim validadas contra o calendário-pai e contra si mesmo (3 níveis de checagem), mas não existe validação de sobreposição entre períodos irmãos — é possível cadastrar "aulas" e "recesso" cobrindo a mesma data sem erro.

4.8 Usuários (staff)

Propósito: contas de staff do painel (admin/gestor/operador) — não gerencia motorista/estudante, que são criados pelos próprios formulários de cadastro (auto-provisionamento de conta).

Restrito a Admin + Gestor (gestor acessa por URL mas não vê o item no menu — ver §2.3).

5. Operação diária

Núcleo operacional do sistema — três telas com papéis distintos e complementares no mesmo ciclo de vida do Dia de Operação (rascunho → publicado → em andamento → concluído).

5.1 Agenda

Visão semanal (grade ou lista) de viagens programadas e disponibilidade de motoristas — somente leitura e navegação, sem CRUD. Clicar numa viagem leva para Operação do Dia na data correspondente. Acessível a todos os papéis staff (diferente de Operação do Dia).

5.2 Operação do Dia

Propósito: onde o Dia de Operação é efetivamente produzido — planejamento e publicação das viagens. Restrito a Admin + Gestor (operador não acessa nem por URL direta).

Nota de RBAC relevante a este módulo: a ação aprovar é o que distingue os dois papéis com acesso — o gestor a possui, o operador não (ver §2.2). Ao escrever o tutorial, confirmar com o time o que exatamente o gestor aprova no ciclo do Dia de Operação, pois o gate existe na matriz de permissão mas o passo de aprovação não está exposto de forma explícita na narrativa da tela.

Publicar

Individual ou em lote — o botão "Publicar todas" só habilita quando há rotas elegíveis (ainda não publicadas)

Override de motorista

Pontual (só hoje) ou permanente (vira o padrão da rota)

Confirmações

Drawer por rota para confirmar/cancelar presença de estudantes individualmente

Cutoff

Countdown ao vivo, com opção de estender em 1h ou desabilitar (ambas via confirmação explícita)

Caso de uso — publicar o dia operacional

Um gestor abre Operação do Dia pela manhã, revisa alertas (rotas sem motorista, cutoffs vencendo), publica em lote as rotas prontas, e usa o override de motorista pontual para uma rota cujo motorista titular avisou atraso. Estudantes confirmados aparecem no drawer de confirmações; o gestor pode adicionar um estudante "fora do manifesto" mediante justificativa.

5.3 Centro Operacional

Propósito: painel de monitoramento ao vivo do dia — visão consolidada com KPIs, alertas e acesso rápido ao embarque de cada rota. É o "cockpit" durante a operação: consome o resultado da publicação, não produz. Acessível a todos os papéis staff.

Alterna entre visão Lista e Mapa; abrir o drawer de embarque de uma rota reaproveita os mesmos schemas de validação do módulo de embarque do app motorista.

6. Disciplina

Propósito: ocorrências disciplinares do estudante (comportamental, vandalismo, no-show recorrente, fraude) e penalidades formais decorrentes (advertência, suspensão temporária, suspensão definitiva). Gerido a partir do perfil do estudante e da aba Disciplina do Dashboard (rankings Top 10 de no-show/walk-in).

Regra de negócio — máquina de estado com guarda contra re-transição

Penalidade: ativa → cumprida | revogada. Só pode transicionar a partir de "ativa" — uma penalidade já finalizada não pode ser retransicionada (o Dia de Operação e a Viagem também têm ciclos de estado, mas é a penalidade que tem a guarda explícita contra reversão de estado terminal). Uma penalidade pode agregar várias ocorrências (M2M). O estudante pode "reconhecer" a própria penalidade (confirmar ciência) — única ação de escrita que um estudante executa sobre si mesmo neste domínio; a flag de reconhecimento é idempotente (chamar de novo não tem efeito colateral).

Conceito importante: "Ocorrência" tem dois significados no sistema — OcorrenciaTempoReal (incidente operacional durante a viagem: mecânico, acidente, atraso) é uma entidade separada de Ocorrencia (disciplinar, sobre conduta do estudante). O tutorial de usuário final deve sempre qualificar qual é qual.

7. Comunicação & notificações

7.1 Comunicação

Propósito: envio de avisos às famílias/estudantes (atraso, troca de veículo, cancelamento, mudança de ponto, cutoff, penalidade, informativo) via WhatsApp, push, SMS ou e-mail, com templates reutilizáveis e histórico com taxa de entrega.

Caso de uso — avisar sobre atraso

Operador seleciona audiência em cascata (Instituição → Rota → Turno, com contagem de destinatários recalculada a cada filtro), escolhe canal, seleciona um template já filtrado por esse canal (trocar de canal invalida templates de outro canal, mas preserva o texto digitado), confere no preview em formato de mensagem de app, e envia.

Exceção

A entrega real (integração com push/SMS/e-mail/WhatsApp) ainda não está implementada no backend — o modelo de dados existe, mas o envio efetivo é uma fase futura. Documentar como "em desenvolvimento", não como funcionalidade completa.

7.2 Notificações

Propósito: diferente de Comunicação — são alertas gerados automaticamente pelo sistema (documento vencendo/vencido, manutenção pendente, CNH vencendo, capacidade excedida, no-show recorrente, sistema/operação/rota/comunicação), não mensagens enviadas manualmente. Visível a todos os papéis staff.

Cada alerta tem estado de leitura por usuário — um mesmo alerta pode estar lido para um gestor e não-lido para outro. Descarte é definitivo. Abrir/ler um alerta o marca como lido para aquele usuário.

8. Relatórios & dashboard

8.1 Dashboard

Painel inicial pós-login com 4 abas: Hoje (operacional do dia), Operação (histórico de execução/pontualidade), Disciplina (no-show/walk-in/penalidades, rankings), Conformidade (frota/CNH/manutenção).

8.2 Relatórios

Seis relatórios operacionais e de prestação de contas: Consolidado Mensal, Plano vs. Execução, Frequência e no-show, Infrações e ocorrências, Manutenção de frota, Conformidade da frota. Exportação em CSV e impressão/PDF (sem XLSX). Filtros persistidos na URL.

RNF — LGPD nas exportações

Exportações de relatórios não mascaram CPF/telefone/e-mail (política de zero-masking se estende às exportações, não só à tela) — consistente com a postura LGPD do produto (ver §9.2).

9. Governança

9.1 Auditoria

Propósito: trilha somente-leitura de quem fez o quê (acessou, alterou, excluiu) em cada cadastro. Restrito a Admin.

Exportação CSV busca o dataset completo (até 10 mil linhas) independente da paginação da tela — "exportar" nunca reflete só a página visível, sempre o filtro completo.

9.2 Backup e restauração

Atenção — operação irreversível com salvaguarda

Restaurado é Admin-only. Fluxo de 2 passos obrigatórios: confirmar (gera token de uso único, TTL de 5 min) → executar (token consumido, lock de concorrência — só 1 restore por vez no sistema inteiro). Antes de qualquer restauração, um backup de segurança automático é gerado, permitindo reverter uma restauração já aplicada mas indesejada para o estado anterior (o botão "Reverter" fica disponível para restaurações concluídas com sucesso). Download local de backup não persiste no servidor — não pode ser rebaixado depois do fechamento do navegador.

9.3 Configurações e Perfil

Organização (razão social/CNPJ/contato), tema visual com preview ao vivo, configurações operacionais chave-valor (cutoff padrão, vagas padrão, dias de antecedência), preferências de alertas automáticos. Restrito a Admin/Gestor no acesso, mas a maioria dos campos fica travada (read-only) para o gestor — ver §2.3.

10. Requisitos não-funcionais

10.1 Segurança

RNF — headers e CSRF

CSP, HSTS (fora de localhost), X-Content-Type-Options, X-Frame-Options (DENY), Referrer-Policy, Permissions-Policy em toda resposta não-estática. CSP usa unsafe-inline sem nonce — lacuna de hardening aceita conscientemente (instrumentar nonce exigiria mudar todo Script inline do Next.js); mitigação no backlog. CSRF valida Origin (com fallback Referer) em todo POST/PUT/PATCH/DELETE, fail-closed em produção.

RNF — rate limiting

Tiers por escopo: login não-autenticado 10/min, ações pesadas (analytics/embarque/planejamento) 20/min, limite global 60/min, restauração de backup 2/hora. Login por CPF (estudante/motorista) tem throttle adicional de 5 tentativas/15min tanto por IP quanto por CPF (hash, não CPF em claro) — mitigação anti-enumeração.

10.2 LGPD — zero-masking

RNF — política deliberada, não descuido

CPF, telefone, e-mail e nome são exibidos completos em toda a UI, logs e exportações — decisão de produto para rastreabilidade e auditoria, com conformidade via consentimento + registro de operações, não via mascaramento. Apenas credenciais (senha, token, secret) são redigidas em log. Única exceção pontual: a chave de cache de rate-limit por CPF usa hash, porque chaves de cache Redis são visíveis a ferramentas de infraestrutura fora do pipeline de log da aplicação — não é uma mudança de política, é escopo técnico específico.

10.3 Acessibilidade

WCAG 2.2 AA como gate interno do projeto (contraste 4.5:1, badges com ícone+texto nunca só cor, navegação por teclado). A página pública de Acessibilidade declara conformidade AAA (contraste 7:1) — acima do que o gate interno garante; sinalizar essa distância ao escrever conteúdo institucional público.

11. Limitações e exceções conhecidas (transversais)

Single-tenant por instância

O sistema já foi multi-tenant; hoje cada VPS/banco serve uma prefeitura. Não existe isolamento de município na mesma instância. Qualquer menção a "tenant" em documentação antiga é resíduo.

Coordenadas ainda são latitude/longitude simples

Apesar do PostGIS estar na stack, Ponto de Embarque e Instituição ainda usam campos numéricos simples, não o tipo geoespacial nativo — migração pendente, não afeta a UI hoje mas limita queries espaciais avançadas.

Erros de autenticação são deliberadamente genéricos

A mensagem de erro de login não distingue "senha errada" de "conta não provisionada" — proteção contra enumeração de CPFs válidos. É comportamento esperado, não um bug de UX a "corrigir" no tutorial.

12. Casos de uso adicionais

Cadastrar uma nova rota do zero

Gestor cria a instituição de destino (se ainda não existir) → cadastra os pontos de embarque no mapa → cria a rota, define turno e adiciona os pontos ao editor via arrastar-e-soltar, marcando origem/destino → define motorista padrão, dias de funcionamento e horário de cutoff → salva; a validação trava o botão até existir exatamente 1 origem e 1 destino.

Restaurar o sistema após incidente

Admin acessa Backup, seleciona um backup enviado ao Drive com status "sucesso", confirma a intenção (gera token de 5 min) e executa a restauração — o sistema gera automaticamente um backup de segurança do estado atual antes de aplicar, permitindo reverter para o estado anterior caso a restauração, embora concluída, traga dados indesejados.

Importar estudantes em lote no início do semestre

Gestor abre o wizard de importação em Estudantes → sobe um CSV (até 2.000 linhas) → sistema mapeia colunas automaticamente por nome → gestor revisa o preview e corrige mapeamentos → sistema valida (CPF duplicado no banco ou dentro do próprio arquivo, e-mail vazio) → confirma → acompanha o progresso via polling da tarefa assíncrona → recebe o resultado final com linhas aceitas/rejeitadas.

13. Fora de escopo / gaps de implementação conhecidos

  • Ações em massa sobre estudantes (ativar/inativar/exportar seleção) — modelado na UI, não conectado ao backend
  • Cadastro de manutenção e abastecimento de veículo — regras completas no backend, sem formulário de criação no painel
  • Entrega real de comunicação (push/SMS/e-mail/WhatsApp) — modelo de dados existe, envio efetivo é fase futura
  • Biometria facial, roteirização automática, módulo financeiro completo, integração nativa com WhatsApp/SMS, multi-tenant SaaS
  • src/components/features/student/ — componentes de carteirinha/QR/confirmação já construídos no frontend mas não referenciados por nenhuma página (provável portal do aluno futuro)