Files
Projeto-Portaria/prompts/FASE-3-web-visitante.md
2026-07-22 15:55:55 -03:00

4.5 KiB
Raw Permalink Blame History

FASE 3 — Web do visitante

Objetivo

PWA do visitante completa: QR → aviso → permissões → captura → destino → espera narrada → resultado. Ambos os funis (visita e entrega). Sem videochamada ainda.

Pré-requisitos

FASES 02 concluídas. Ler docs/04-DESIGN-SYSTEM-UX.md §3 e docs/03-FLUXOS-E-CONTRATOS.md §2 e §3.

Contexto que determina cada decisão

O visitante está em pé na calçada, no sol, com pressa, em 4G, e não vai instalar nada. Tudo aqui deriva disso: bundle mínimo, uma ação por tela, tipografia grande, contraste alto.

Tarefas

1. Base

Vite + React 19 + TS em web/visitor. Roteamento por /v/{gateId}?s={sig}. Tokens de shared-ui. PWA com manifest, sem service worker de cache agressivo — o visitante usa uma vez e o cache velho causaria mais problema que benefício.

Orçamento: 200KB gzip, com gate em CI. O SDK do LiveKit entra por import() dinâmico apenas na FASE 4, na tela de chamada — ele sozinho tem o tamanho de todo o resto do app.

2. Telas

Cinco telas conforme 04-DESIGN-SYSTEM-UX.md §3, uma ação cada.

Tela 1 — Aviso de tratamento. Não é modal de consentimento com checkbox. A base legal é legítimo interesse; o que se faz é informar com clareza. Texto de 06-LGPD-E-SEGURANCA.md §1, com link "Saiba mais". O botão diz "Continuar", não "Aceito".

Tela 2 — Escolha do funil. 📦 Entrega primeiro, porque é a maioria do volume. 👤 Visita abaixo.

Tela 3 — Permissões e captura. Solicita câmera e localização, com explicação antes do prompt nativo — pedir permissão sem contexto é a principal causa de negativa. Captura foto do rosto (visita) ou do pacote (entrega). Comprime no cliente para no máximo 1600px no lado maior.

Permissão negada: localização é bloqueante (403 LOCATION_REQUIRED), com instrução de como reabilitar. Câmera negada em entrega degrada para "sem foto"; em visita, é bloqueante.

Tela 4 — Destino. Bloco e unidade, campos livres. Nenhum autocomplete, nenhuma sugestão, nenhuma validação de existência — é a busca cega, e um autocomplete a destruiria. Nome do visitante obrigatório; documento e telefone opcionais.

Tela 5 — Espera narrada. A tela onde o produto se ganha ou se perde:

TOCANDO        "Chamando o morador..."           barra 0→20s
ESCALONADA     "Tentando outros moradores..."    barra 0→15s
FILA_OPERADOR  "Transferindo para a portaria"    + posição na fila
RECADO         "Grave um recado em vídeo"        → gravação

Nunca um spinner mudo. Um spinner de 35 segundos é indistinguível de um app quebrado.

Tela 6 — Resultado. Autorizado: PIN de 6 dígitos em display, QR grande, contagem regressiva de validade. Negado: mensagem clara e sem tom acusatório. Expirado: instrução de contingência (botão físico da portaria).

3. Estado e conexão

Estado da visita via WebSocket com fallback automático para polling de 3s se o WS cair ou não conectar em 5s. O visitante em 4G instável é o caso comum, não a exceção.

Reconexão com backoff exponencial e teto de 10s. Estado da sessão em sessionStorage, para que refresh acidental não perca a visita.

4. Gravação de recado

MediaRecorder, 30s máximo, preview antes de enviar, upload com barra de progresso e retry.

5. Erros

Todo erro exibido vem do detail do problem+json, em português e acionável: "Aproxime-se da entrada e tente novamente", nunca OUTSIDE_GEOFENCE.

6. Acessibilidade e legibilidade em campo

  • Alvos de toque ≥ 48×48px, sem exceção
  • Corpo de texto ≥ 16px
  • Contraste ≥ 4.5:1, verificado em CI
  • Apenas extremos da escala neutra — sol direto derruba tons médios
  • prefers-reduced-motion respeitado
  • lang="pt-BR", textos externalizados para i18n futura
  • Funciona em Safari iOS 15+ e Chrome Android 100+

Critérios de aceite

  • Bundle ≤ 200KB gzip (gate de CI)
  • TTI < 2s em 4G simulado (Lighthouse throttling)
  • Lighthouse: Performance ≥ 90, Acessibilidade = 100
  • Fluxo de entrega completo em ≤ 4 toques
  • Tela 4 não tem autocomplete nem validação de unidade
  • WS derrubado à força → polling assume e o fluxo continua
  • Localização negada → mensagem acionável, não erro técnico
  • Testado em iPhone e Android reais, sob sol
  • Todos os textos de erro em português, sem código técnico

Não faça nesta fase

  • Videochamada (FASE 4)
  • Service worker com cache de assets versionados
  • Qualquer tela de admin