# Canal OTP "Exibir em tela" — Design

**Data:** 2026-05-08
**Escopo:** Renomear o canal OTP `teste` para `exibir_tela`, remover o gate de devs, fixar o modal que sumia, e adicionar botões de copiar.

## Contexto

No modal "Liberar rota para motorista" (`Logistica/sinais/Central/html/central.php`, L.2460-2513) o atendente escolhe um canal de envio de OTP. Hoje há um canal `teste` que captura o código gerado e o devolve em claro na resposta da API — útil pra dev validar fluxo, mas:

1. **Bug**: o modal fecha sozinho 1.2s depois (L.4099) **independente do canal escolhido** — o painel com código/token/link aparece e some antes do atendente conseguir copiar.
2. **Gate dev**: o handler só devolve `codigo_teste` se o usuário estiver em `Config::DEV_USER_IDS` (`GerarOtpHandler.php:40`). Em produção, o atendente não vê e não consegue passar manualmente pro motorista.
3. **Nomenclatura**: "teste" sugere fluxo descartável; o caso de uso real é "exibir em tela pra repassar manualmente".

## Decisões (alinhadas com o usuário)

- **Audiência**: liberar pra qualquer usuário autenticado com acesso à Central — remove o gate `Config::isDev`.
- **Comportamento do modal**: quando `canal === 'exibir_tela'`, modal **não** fecha sozinho; outros canais (whatsapp/email/sms/log) continuam fechando após 1.2s. Adicionar botão "Copiar" em código, token e link (mesmo padrão do `#acompanhar-link-copiar` em L.2535).
- **Rename completo**: `TesteCanal` → `ExibirTelaCanal`, canal name `teste` → `exibir_tela`, chaves de resposta `codigo_teste*` → `codigo_exibido*`, option HTML value e label.

## Mudanças por arquivo

### Backend — Mensageria/Otp

**`componentes/Missoes/Mensageria/sinais/Otp/Canais/TesteCanal.php`**
- Renomear arquivo → `ExibirTelaCanal.php`
- `class TesteCanal` → `class ExibirTelaCanal`
- `nome()` retorna `'exibir_tela'`
- `error_log` tag: `[Otp][TesteCanal]` → `[Otp][ExibirTelaCanal]`
- Atualizar docblock da classe (deixa de ser "canal de teste/integração", vira "canal que exibe o código na UI pra repasse manual ao destinatário")

### Backend — Plataforma/Api

**`componentes/Missoes/Plataforma/sinais/Api/Bootstrap.php`** (L.12, L.63-67)
- `use ...\Canais\TesteCanal` → `use ...\Canais\ExibirTelaCanal`
- Comentário "Canal 'teste' — captura..." → "Canal 'exibir_tela' — captura o código..."
- `OtpCanalRegistry::temRegistro('teste')` → `temRegistro('exibir_tela')`
- `OtpCanalRegistry::registrar('teste', TesteCanal::class)` → `registrar('exibir_tela', ExibirTelaCanal::class)`

**`componentes/Missoes/Plataforma/sinais/Api/Handlers/EdicaoRota/GerarOtpHandler.php`**
- `use ...\Canais\TesteCanal` → `use ...\Canais\ExibirTelaCanal`
- Remover `use ...\Api\Config` se ficar sem outro uso (verificar)
- `TesteCanal::reset()` → `ExibirTelaCanal::reset()`
- L.40: condição passa a `if ($canal === 'exibir_tela' && ExibirTelaCanal::$ultimoCodigo !== null)` — **remove `Config::isDev($userId)`**
- L.41-42: chaves de resposta:
  - `codigo_teste` → `codigo_exibido`
  - `codigo_teste_dest` → `codigo_exibido_dest`
- Remover linha `$userId = (int) ($req->jwtClaims['user'] ?? 0);` se não usada
- Atualizar docblock (L.15-20) refletindo novo canal/comportamento

**`componentes/Missoes/Plataforma/sinais/Api/OpenApi/spec.php`**
- L.299: `'enum' => ['whatsapp', 'email', 'sms', 'log', 'exibir_tela']`
- L.314: descrição → "Quando canal=exibir_tela, resposta inclui `codigo_exibido` (e `codigo_exibido_dest`) em claro pra repasse manual ao destinatário."

### Frontend — Logistica/Central

**`componentes/Missoes/Logistica/sinais/Central/html/central.php`**

- **L.2494** (option no select):
  ```html
  <option value="exibir_tela">Exibir em tela — mostra código, token e link</option>
  ```

- **L.3998-4007** (handler do `change` no select):
  - Trocar `if ($(this).val() === 'teste')` → `=== 'exibir_tela'`
  - **Remover o pre-fill** de `+5511999990000` e `motorista.teste@example.com` — era atalho de dev. Fluxo de produção exige dados reais.

- **L.4066-4088** (render do painel quando canal é exibir_tela):
  - `const codigoTeste = gerarResp?.data?.codigo_teste` → `const codigoExibido = gerarResp?.data?.codigo_exibido`
  - `if (canal === 'teste' && codigoTeste)` → `=== 'exibir_tela' && codigoExibido`
  - HTML do feedback: cada uma das 3 linhas (Código / Token / Link) ganha um botão "Copiar" no padrão visual de `#acompanhar-link-copiar` (L.2535-2537):
    ```html
    <button class="g-botao g-botao-contorno text-[11px] py-0.5 px-2 copiar-exibir-tela"
            data-copiar="{value}" type="button">
      <i class="fas fa-copy mr-1"></i>Copiar
    </button>
    ```
  - Handler `$(document).on('click', '.copiar-exibir-tela', ...)` usando `navigator.clipboard.writeText` com fallback pra `document.execCommand('copy')` em browsers antigos. Feedback breve no botão (`Copiado!` por ~1.5s).
  - Remover o aviso "(...A tela do motorista será conectada à API na próxima fase — por enquanto pode usar /api/v1/docs com o token e o código acima.)" — era texto dev. Substituir por instrução curta: "Copie e repasse os dados ao motorista. O modal não fecha sozinho — feche em 'Cancelar'/×."

- **L.4099** (auto-close):
  ```js
  if (canal !== 'exibir_tela') {
      setTimeout(() => $('#liberar-motorista-modal').addClass('hidden'), 1200);
  }
  ```

### Tests

**`componentes/Missoes/Plataforma/sinais/Api/Testes/Handlers/CanalTesteIntegrationTest.php`**
- Renomear arquivo → `CanalExibirTelaIntegrationTest.php`
- Cenários (`Config::DEV_USER_IDS = [1]` no setup atual; teste sobrescreve via `GENESIS_API_DEV_USERS='1'`):
  1. `canal=exibir_tela` + user dev (id=1) → response devolve `codigo_exibido` em claro com 6 dígitos.
  2. `canal=exibir_tela` + user **não-dev** (id=99) → response devolve `codigo_exibido` (gate removido — comportamento idêntico ao dev).
  3. `canal=log` + user dev → response **não** contém `codigo_exibido`.
- Atualizar todas as referências `codigo_teste` → `codigo_exibido`, `'teste'` → `'exibir_tela'`, `TesteCanal` → `ExibirTelaCanal`.
- **Remover** o cenário antigo "canal=teste + não-dev → não expõe" (gate não existe mais).
- Persistência: `$canalPersistido === 'exibir_tela'`.

### i18n

Atualmente o option em L.2494 está hardcoded em português. Manter hardcoded por consistência com o resto do modal (que também não passa por i18n no momento — verificar se `central.php` consome `linguagens/*.json` em algum ponto antes de adicionar chaves).

Se o restante do modal não usa i18n, **não** introduzir i18n só pra esse option — escopo cresce sem ganho. Decisão tomada na implementação.

## Pontos de atenção

- **Migração de DB**: nenhuma. `verificacoes_otp.canal` é `VARCHAR(20)` e aceita qualquer valor. Registros antigos com `canal='teste'` continuam válidos (são histórico). Só novos registros virão com `'exibir_tela'`.
- **API externa**: a `/api/v1` é interna ao Genesis (memory `project_api_interna.md`). Não há consumidor externo a quem `'teste'` precisa continuar funcionando — rename é seguro.
- **Segurança pós-gate**: removido o `Config::isDev`, qualquer JWT válido com permissão de chamar `/api/v1/edicao-rota/{token}/otp/gerar` recebe o código em claro **se** explicitamente passar `canal=exibir_tela` no body. Canal default (`canal_padrao` configurado) **não** vaza — só quem optar explicitamente.
- **OtpConfig.php** (`Mensageria/sinais/Otp/Configuracao/OtpConfig.php`): hoje `canal_padrao` default é `'log'`. Não tocar — se algum sinal tiver `canal_padrao='teste'` em config local, vai quebrar (não há nenhum hoje, validei via grep).

## Critérios de aceite

1. No modal "Liberar rota para motorista", o option mostra "Exibir em tela — mostra código, token e link" e tem `value="exibir_tela"`.
2. Selecionar "Exibir em tela", preencher dados reais e clicar "Enviar para o motorista":
   - Modal **continua aberto** após confirmar.
   - Painel verde mostra: Edição #N / Código (6 dígitos) / Token / Link do motorista, cada um com botão "Copiar" funcional.
   - Modal só fecha quando atendente clica em ×, "Cancelar".
3. Selecionar outro canal (whatsapp/email/sms/log) e confirmar:
   - Comportamento atual preservado: feedback breve, modal fecha após 1.2s.
4. Endpoint `POST /api/v1/edicao-rota/{token}/otp/gerar` com `body.canal='exibir_tela'`:
   - Resposta inclui `data.codigo_exibido` (6 dígitos) e `data.codigo_exibido_dest` (string do destinatário).
   - Funciona pra **qualquer** usuário com JWT válido (não só `DEV_USER_IDS`).
5. Endpoint com `body.canal='log'` (ou outro): resposta **não** contém `codigo_exibido*`.
6. `OpenApi/spec.php` reflete o novo enum e a nova descrição.
7. Test integração novo (`CanalExibirTelaIntegrationTest.php`) passa cobrindo cenários 1-3 acima.

## Fora de escopo

- i18n do modal "Liberar rota" (escopo isolado, decisão de não introduzir parcialmente).
- Persistir o código exibido pra consulta posterior no modal de Acompanhamento (segurança: OTP não fica em claro em DB; quem quer reaver, gera novo).
- Mudanças em outros canais (`log`, `whatsapp`, `email`, `sms`).
- Renomear referências históricas em comentários `.md` em `tasks/` (documentação de histórico — manter como registro).
