# 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 0–2 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