App Motorista
Aplicativo (Expo/React Native) usado pelo motorista que conduz o transporte universitário — voltado à
operação de campo: iniciar viagem, embarcar e desembarcar estudantes, registrar ocorrências, encerrar a
viagem. Voltagem operacional da IDV Unipass: azul institucional #0047c4 como identidade e ação,
legibilidade acima de expressão, offline-first. Ver arquitetura do
ecossistema.
Existe um UX-spec de 2026-07-05 que propôs uma
"espinha operacional" de redesign. Esta página descreve o que está de fato implementado hoje
(verificado direto no código de sgtu-back e sgtu-app) — inclusive apontando onde a
proposta já virou realidade (ex.: o banner de viagem ativa persistente já existe) e onde há gaps ou
divergências entre a visão de produto e o código.
1. Visão geral
O App Motorista é a superfície de execução em campo. O motorista não acessa o painel Gestão — vê apenas as viagens atribuídas a ele. O trabalho central é conduzir a viagem em curso com segurança e registrar formalmente o que acontece em cada ponto (quem embarcou, quem desembarcou, ocorrências).
Plataformas
Android nativo (uso operacional real) + suporte PWA web no código. Sem iOS.
Papel
Motorista (inclui monitor) — login por CPF, primeiro acesso com troca de senha obrigatória
2. Conceitos específicos do domínio
| Termo | Definição |
|---|---|
| Viagem | Execução de uma rota num dia de operação, com veículo e motorista atribuídos. Unidade de operação em campo. |
| Manifesto | Lista de estudantes esperados na viagem (derivada dos vínculos ativos cruzados com as confirmações do dia). É a verdade operacional do embarque. |
| Evento de Ponto | Registro de chegada ou saída do veículo em um ponto de embarque, com timestamp (e geolocalização na chegada). |
| Registro de Presença | Registro individual de embarque ou desembarque de um estudante (nunca chamado "check-in"). |
| Ocorrência em Tempo Real | Incidente reportado durante a viagem (mecânico, acidente, atraso, indisciplina, superlotação...). Distinta da ocorrência disciplinar do estudante. |
| Integração (baldeação) | Transferência coordenada de estudantes entre veículos num ponto de integração no meio da operação. |
| Walk-in | Embarque de estudante sem vínculo ativo com a rota — bloqueado por padrão, liberável por override com justificativa. |
3. Navegação e viagem ativa
4 tabs: Início · Alertas (com badge de não-lidos) · Histórico · Perfil.
Quando há uma viagem em andamento, uma barra "AO VIVO" fica fixa acima da tab bar em todas as tabs — mostra a rota, a contagem de embarcados/esperados, e ao ser tocada retoma o fluxo de viagem de onde estava. O motorista nunca "perde" a viagem em curso ao navegar pelo app. (A tela Início oculta a viagem ativa da própria lista, delegando essa presença à barra persistente.)
4. Ciclo de vida da viagem
Estados da viagem: rascunho → publicada → em andamento → concluída (ou cancelada a partir de qualquer estado não-terminal). O motorista nunca vê viagens em rascunho — só a partir de publicada.
| De | Pode ir para |
|---|---|
| rascunho | publicada, cancelada |
| publicada | em andamento, cancelada |
| em andamento | concluída, cancelada |
| concluída / cancelada | — (terminal) |
Transição inválida é rejeitada pelo backend (erro 400). O fluxo operacional em campo é: pré-viagem → iniciar → percorrer pontos (chegada → embarque/desembarque/integração → saída) → resumo → encerrar.
Toda ação operacional (chegada/saída de ponto, embarque, desembarque, registro de ausência, integração, ocorrência) exige que a viagem esteja em andamento — o backend rejeita com erro 400 se a viagem ainda está publicada (não iniciada) ou já foi concluída/cancelada. Iniciar a viagem é o gate que libera tudo o mais.
5. Pré-viagem (checklist)
Propósito: inspecionar condições antes de iniciar. A UI apresenta 6 itens fixos (condição do veículo, documentação, combustível, pneus, limpeza, kit de segurança/extintor). O botão "Iniciar viagem" só habilita com todos os itens marcados ou um override justificado (≥10 caracteres), e sempre com o KM inicial válido.
O KM inicial informado ao iniciar a viagem precisa ser maior ou igual ao odômetro conhecido do veículo. Ao encerrar, o KM final precisa ser ≥ o maior valor entre o KM inicial e o odômetro atual do veículo, e o sistema sincroniza o odômetro do veículo se o valor for maior.
Os 6 itens do checklist e a obrigatoriedade de marcá-los são regra do app. O backend apenas persiste a lista que o app enviar (aceita qualquer lista de até 20 strings, sem exigir itens específicos). Não há uma lista canônica de itens obrigatórios no servidor — quem quiser mudar os itens do checklist mexe no app, não numa configuração de backend.
6. Embarque no ponto
Propósito: embarcar os estudantes esperados — o manifesto é a peça central da tela, com contagem viva de embarcados/esperados. Ações: confirmar embarque individual, ler QR pelo scanner, forçar embarque acima da capacidade (com registro de ocorrência de superlotação), registrar ausência de quem não apareceu.
O que autoriza um estudante a embarcar é ter vínculo ativo com a rota — não a confirmação prévia. Um estudante sem vínculo é um walk-in: o embarque é bloqueado (erro 422) a menos que o motorista faça override com justificativa — e cada override autorizado gera automaticamente uma ocorrência em tempo real (gravidade média) para rastreabilidade.
Embarcar acima da capacidade do veículo é bloqueado da mesma forma (erro 422) e liberável pelo mesmo override justificado — e o override também gera automaticamente uma ocorrência em tempo real, aqui de gravidade alta (walk-in é média). Se as duas violações ocorrerem juntas (estudante sem vínculo E veículo lotado), o sistema gera uma ocorrência para cada. Além disso, um mesmo estudante já embarcado que seja lido/confirmado de novo retorna erro 409 (proteção idempotente contra duplo-registro, não só na integração).
Se um estudante com vínculo ativo embarca sem ter confirmado presença naquele dia, o embarque não é bloqueado — é registrado normalmente, e o sistema apenas retorna um aviso informativo (com a contagem de reincidências dos últimos 30 dias) para alimentar as estatísticas de disciplina. Não gera penalidade automática.
O app tenta casar o conteúdo do QR contra três identificadores, nesta ordem: card_token, ID
do estudante ou CPF. Ressalva importante: o manifesto real vindo do backend não
inclui o card_token — então, contra dados de produção, só o ID e o CPF efetivamente casam; o
match por card_token só funciona com dados de mock. Documentar como "leitura por ID/CPF" até o
manifesto passar a carregar o token. A validação criptográfica real ocorre no backend.
6.1 O manifesto
O manifesto que o motorista recebe deriva dos vínculos ativos da rota (filtrados por data e dia da semana), cruzados com as confirmações do dia e os registros de presença já feitos na viagem. Cada item traz nome, CPF, foto, IES, curso, ponto de embarque, status da confirmação e status do embarque (pendente/embarcado/ausente).
O manifesto lista apenas estudantes com vínculo ativo. Um estudante que embarcou via override (walk-in, sem vínculo) fica registrado na viagem mas não aparece na lista do manifesto — a lista é construída a partir dos vínculos, não dos registros de presença.
7. Desembarque no ponto
Propósito: registrar o desembarque dos estudantes. Simétrico ao embarque: lista de quem desembarcar, confirmação individual, marcar "não desembarcou" com motivo obrigatório, e desembarque em lote ("Desembarcar todos") com confirmação.
Registrar desembarque de um estudante que não tem embarque prévio na viagem é bloqueado (erro 400 no individual; no lote, é tudo-ou-nada — se qualquer estudante da lista não embarcou, a operação inteira falha com 422 e nenhum registro é criado).
O motorista pode registrar desembarque em qualquer ponto — o backend não valida se o ponto de desembarque corresponde ao ponto de destino cadastrado no vínculo do estudante. Flexibilidade operacional deliberada (estudante pode pedir para descer antes), mas relevante documentar para quem espera uma checagem estrita.
8. Percurso e eventos de ponto
Ao chegar num ponto, o motorista marca a chegada (que grava geolocalização) e, ao terminar embarque/desembarque, marca a saída. A tela do ponto atual (hub do fluxo) mostra a timeline e oferece a ação contextual ao tipo do ponto (embarque, desembarque, integração, ou finalizar a viagem no ponto final).
O backend não impõe ordem: é possível marcar chegada no ponto 3 antes do ponto 2. A chegada valida apenas que o ponto pertence à rota da viagem, não a ordem em que foi percorrido. A saída de ponto não grava geolocalização (só a chegada grava).
9. Integração (baldeação)
Propósito: num ponto de integração, coordenar quem desce de um veículo e quem sobe em outro. A tela mostra um cronômetro de tolerância, os veículos de origem/destino, e três grupos: "descem aqui", "ficam no veículo", "sobem aqui".
A integração cria registros de desembarque (para quem desce) e de embarque (para quem sobe) de uma vez, com proteção contra duplo-embarque/duplo-desembarque (erro 409). No cadastro da rota, a baldeação é uma marcação ortogonal do ponto (independente de embarque/desembarque).
10. Ocorrências em tempo real
Durante a viagem, o motorista pode registrar ocorrências — com foto e geolocalização. Tipos: atraso significativo, veículo quebrado, acidente de trânsito, problema de saúde (estudante ou motorista), desvio de rota, indisciplina de estudante, superlotação, walk-in, condição climática, outro. Gravidade: baixa, média, alta, crítica.
Uma ocorrência de gravidade crítica dispara automaticamente um alerta de sistema e um log crítico no servidor. Ocorrências também podem ser criadas no momento do encerramento da viagem (o motorista vê a lista de ocorrências antes de confirmar que "não houve outras pendências") e automaticamente a cada override de walk-in autorizado.
11. Resumo e encerramento
Resumo: antes de encerrar, o motorista revê os totais (pontos percorridos, embarcados, desembarcados, ausentes, ocorrências) e a timeline de pontos. Fecho deliberadamente sóbrio — sem celebração/confete (condução exige foco, não recompensa).
Encerrar: exige KM final válido e a confirmação explícita de que não há outras pendências, com um diálogo de confirmação (ação irreversível — fecha a leg). O botão de encerrar é azul (conclusão), não vermelho (não é erro/falha).
12. Perfil e disponibilidade
O motorista vê seus dados (CNH, categoria, telefone), o veículo padrão, e edita sua grade de disponibilidade semanal (3 turnos × 5 dias úteis — seg a sex, sem fim de semana).
A grade de disponibilidade é um cadastro de janelas recorrentes — não dispara substituição automática. Não existe fluxo "não posso dirigir hoje → substituição automática": a substituição de motorista numa viagem e o registro de indisponibilidade (férias/atestado) são feitos apenas por admin/gestor/operador no painel Gestão, nunca pelo próprio motorista no app.
13. Controle de acesso
O motorista só enxerga e age sobre viagens atribuídas a ele — o backend filtra tudo pela sua identidade antes de qualquer resposta. Uma viagem de outro motorista simplesmente não existe para ele (retorna "não encontrado", não "acesso negado" — ele nem sabe que ela existe). Todo endpoint do app exige conta ativa + papel de motorista.
Alertas que o motorista recebe hoje (CNH/documento de veículo vencendo) chegam por serem de audiência geral, não por roteamento específico ao motorista dono — ver limitações.
14. Requisitos não-funcionais
14.1 Offline-first
As ações de campo (embarque, desembarque, chegada/saída de ponto, ocorrência, aviso, iniciar/encerrar viagem) são enfileiradas localmente e sincronizam quando a rede volta — o motorista opera num ponto sem sinal. Fila FIFO em armazenamento local, limite de 100 ações (as mais antigas são descartadas ao exceder), com indicador visual de pendências e estado "sincronizando".
Uma ação que falha com 401 (token expirado) é retida e retentada após renovar o token. Já um 403 é descartado deliberadamente — nesses endpoints, 403 significa um bloqueio permanente de conta/papel, não expiração; retê-lo perpetuaria uma ação irrecuperável na fila. O replay dispara na reconexão (sem backoff/contador de tentativas por ação); erros 4xx não-401 são descartados silenciosamente, sem retry manual pelo usuário.
14.2 GPS e rastreamento
A visão de produto menciona "GPS batch sync" e "mapa ao vivo" no Centro Operacional, mas isso não está implementado: o hook de rastreamento contínuo foi construído e depois removido (abril/2026) por falta de consumidor e de contrato de endpoint no backend. A única geolocalização registrada hoje é pontual — na chegada a um ponto e no momento de uma ocorrência —, nunca rastreamento contínuo da posição do veículo. Documentar o "mapa ao vivo" como visão de roadmap, não como funcionalidade atual.
14.3 Scanner
Câmera nativa no Android, biblioteca web para o PWA. Limitação: não há fallback de entrada manual do código quando o dispositivo não tem câmera (ex.: PWA em desktop) — o usuário fica preso na tela de scanner. Relevante se o PWA for usado fora de um smartphone.
14.4 Dark mode
O app tem um kill switch global que força modo claro em todas as telas, independentemente da preferência do sistema — decisão registrada no código porque o suporte a dark ainda é inconsistente entre telas. Não documentar dark mode como disponível.
14.5 Acessibilidade
Azul #0047c4 sobre branco tem contraste 7,78:1 (passa AA com folga). Status sempre por
ícone + texto + cor, nunca só cor — importante no manifesto (embarcado/ausente/pendente). Alvos de toque
generosos, adequados a operação sob pressão de tempo. Ponto de atenção interno: azul sobre fundo de badge
tintado (6,71:1) fica abaixo do alvo mais estrito de 7:1 que o projeto se impõe — passa AA, mas não o alvo
interno mais rigoroso.
15. Limitações e exceções conhecidas
- GPS batch / mapa ao vivo do Centro Operacional não implementado (hook removido em abril/2026)
- Checklist pré-viagem não tem itens obrigatórios definidos no servidor — validação é só no app
- Sem validação de sequência de pontos (pode-se marcar ponto fora de ordem)
- Desembarque não valida o ponto de destino cadastrado no vínculo do estudante
- Alertas de CNH/documento vencendo chegam ao motorista por audiência geral, não por roteamento dirigido ao dono
- Sem fallback de entrada manual no scanner quando não há câmera
- Dark mode desligado globalmente por decisão explícita
- Sem tratamento de segundo plano: se o SO suspender o app durante viagem longa, a sincronização depende de o app estar em primeiro plano na reconexão
- Resíduos de cor e uso de fonte tabular ainda pendentes em algumas telas (Perfil) — dívida de acabamento visual, não de função
- O contrato de embarque aceita método "biométrico" (além de manual/QR), mas não há caminho de UI ativo para biometria hoje — é opção reservada no backend, não funcionalidade disponível
16. Casos de uso
Motorista abre o app, vê a viagem do dia, faz o checklist pré-viagem e informa o KM inicial → inicia a viagem → em cada ponto, marca chegada, embarca os estudantes do manifesto (lendo QR ou confirmando na lista), marca saída → num ponto de integração, coordena quem desce e quem sobe → no ponto final, revê o resumo e encerra com o KM final. Tudo funciona sem sinal; sincroniza quando a rede volta.
O motorista tenta embarcar um estudante que não está no manifesto → o sistema bloqueia (walk-in) → o motorista decide autorizar mesmo assim, informando a justificativa → o embarque é registrado e uma ocorrência de gravidade média é criada automaticamente para a gestão auditar depois.
O veículo quebra no meio da rota → o motorista registra uma ocorrência (tipo "veículo quebrado", gravidade alta/crítica, com foto e localização) → se crítica, a gestão é notificada automaticamente → a ocorrência entra no resumo da viagem no encerramento.
17. Fora de escopo
- Rastreamento contínuo de GPS / mapa ao vivo (removido; visão de roadmap)
- Autosserviço de substituição/indisponibilidade pelo motorista
- Build iOS
- Dark mode (desligado)
- Gamificação (é domínio do app estudante — o motorista tem só stats informativos)