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

91 lines
4.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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