# Refatoração Dashboard Logística → API V2 — Plano técnico

## Resumo de decisão arquitetural

Não há novo Sinal nem nova Sonda. Estende-se **`Plataforma/sinais/Api`** com 12 handlers em `Handlers/Dashboard/` e 12 rotas `/api/v1/dashboard/*` em `Routes.php`, no mesmo padrão de `EdicaoRota`/`Otp`. O Sinal `Logistica/sinais/Dashboard` é refatorado in-place: `Dashboard.php` vira dispatcher fino que só serve o shell (`renderDashboard` resumido) e perde `filtrarDashboard`/`detalhesMissao`; o `dashboard.js` passa a falar exclusivamente com `/api/v1/dashboard/*` via `fetch` + JWT. `DashboardLaboratorio` é mantido como **dependência estável** e passa a ser consumido pelos handlers (mesmo papel que `CriarEdicaoRotaLaboratorio` para `EdicaoRota/CriarHandler`). Render de fragmentos HTML (kanban/veículos/rotas que hoje saem do PHP) migra para o JS — handlers devolvem JSON puro. Nenhuma migration; nenhuma alteração de schema.

## Sinal de referência

- **Imitar**:
  - `componentes/Missoes/Plataforma/sinais/Api/Handlers/EdicaoRota/CriarHandler.php` — modelo de handler thin que delega ao Laboratório existente e usa `adaptarRespostaLegacy`.
  - `componentes/Missoes/Plataforma/sinais/Api/Routes.php` linhas 23-34 — bloco de rotas EdicaoRota como template do bloco Dashboard.
  - `componentes/Missoes/Plataforma/sinais/Api/Api.php` linhas 70-120 — dispatcher methods `edicaoRotaXxx` como template para `dashboardXxx`.
  - `componentes/Missoes/Logistica/sinais/Edicaorota/Validators/CriarEdicaoRotaValidator.php` — formato dos Validators (sem framework próprio: classe simples, método `validar()` retorna array de erros).
  - `componentes/Missoes/Plataforma/sinais/Api/OpenApi/spec.php` (bloco `/edicao-rota`) — registro OpenAPI.
- **Não imitar**:
  - `componentes/Missoes/Logistica/sinais/DashboardApi/` — caminho paralelo (`/labs/apis/...`) que esta feature explicitamente substitui; serve só como fonte dos Laboratórios já consumidos pelo `DashboardLaboratorio`.
  - `componentes/Missoes/Logistica/sinais/Edicaorota/Callbacks/Registry` — Dashboard não tem múltiplos módulos consumidores; **não** há justificativa para Registry/Adapter aqui.
  - Sinais legados (Designacoes/Pessoas) — referência V1.

## Estrutura proposta

### Novos arquivos

```
componentes/Missoes/Plataforma/sinais/Api/Handlers/Dashboard/
├── ResumoOperacionalHandler.php      # GET  /api/v1/dashboard/resumo
├── ListarMissoesHandler.php          # GET  /api/v1/dashboard/missoes
├── DesempenhoHandler.php             # GET  /api/v1/dashboard/desempenho
├── AlertasHandler.php                # GET  /api/v1/dashboard/alertas
├── TiposMissaoHandler.php            # GET  /api/v1/dashboard/tipos-missao
├── ExtratoHandler.php                # GET  /api/v1/dashboard/extrato
├── VisaoGeralDiaHandler.php          # GET  /api/v1/dashboard/visao-geral
├── VeiculosStatusHandler.php         # GET  /api/v1/dashboard/veiculos
├── RotasDoDiaHandler.php             # GET  /api/v1/dashboard/rotas-dia
├── FiltrarHandler.php                # GET  /api/v1/dashboard/filtrar (agregador)
├── AcaoMissaoHandler.php             # POST /api/v1/dashboard/missoes/{id}/acao
└── DetalhesMissaoHandler.php         # GET  /api/v1/dashboard/missoes/{id}

componentes/Missoes/Logistica/sinais/Dashboard/Validators/
├── FiltroDashboardValidator.php      # status, tipo_missao_id, data/dia_efetivo
├── AcaoMissaoValidator.php           # missao_id (>0), acao ∈ {pausar,retomar,cancelar,iniciar}
└── DetalhesMissaoValidator.php       # missao_id (>0)

componentes/Missoes/Logistica/sinais/Dashboard/Testes/
├── Handlers/
│   ├── ResumoOperacionalHandlerTest.php
│   ├── ListarMissoesHandlerTest.php
│   ├── FiltrarHandlerTest.php
│   ├── AcaoMissaoHandlerTest.php
│   ├── DetalhesMissaoHandlerTest.php
│   └── (...handlers restantes — happy path + 1 erro de validação cada)
└── Fluxos/
    ├── FiltrarDashboardE2ETest.php
    └── AcaoMissaoE2ETest.php

componentes/Missoes/Logistica/sinais/DashboardApi/
└── DEPRECATED.md                     # marcador AC6.1 apontando para /api/v1/dashboard/*
```

### Arquivos alterados

- `componentes/Missoes/Plataforma/sinais/Api/Routes.php` — adicionar bloco `// Dashboard` com 12 rotas (ver tabela).
- `componentes/Missoes/Plataforma/sinais/Api/Api.php` — `use` dos 12 handlers + 12 métodos thin `dashboardXxx()` chamando `dispatcher()->run(...)`.
- `componentes/Missoes/Plataforma/sinais/Api/OpenApi/spec.php` — bloco `/dashboard/*` análogo ao `/edicao-rota/*`.
- `componentes/Missoes/Logistica/sinais/Dashboard/Dashboard.php` — remover `filtrarDashboard()` e `detalhesMissao()` (e branches `case 'filtrar'/'detalhesMissao'` em `start()`); manter apenas `renderDashboard()` (renomeada para `renderShell()` mentalmente) servindo o shell. Conservar Luz components (`Luz_MissaoCard`, `Luz_AlertaItem`) — mas o card do kanban passa a ser gerado client-side em JS, então `Luz_MissaoCard` pode ser mantido apenas se houver outro caller; caso contrário deprecar com comentário (sem remover nesta feature). AC3.1 exige zero `'<tr` `'<div` `'<td` `'<li` no controller — `renderShell` continua só chamando `$galaxiaRoute->visual('html/dashboard',...)`, nada de string-HTML.
- `componentes/Missoes/Logistica/sinais/Dashboard/html/dashboard.js` — substituir cada `sendToGalaxia` por `fetch('/api/v1/dashboard/...', { headers: { Authorization: 'Bearer '+jwt } })`; mover render de cards do kanban, linhas de veículos e cartões de rota para funções JS (template strings). Helper único `apiGet(path,query)` / `apiPost(path,body)` no topo. Tratamento padronizado para 401/403/4xx/5xx (toast + log).
- `componentes/Missoes/Logistica/sinais/Dashboard/html/dashboard.php` — eliminar dependência dos campos `html_kanban`/`html_veiculos`/`html_rotas` (já que filtragem agora hidrata via API). O shell inicial pode renderizar SSR mínimo do que já vem em `$dadosVisualizacao` (RN 9), ou apresentar skeleton e deixar o JS hidratar — decisão: **manter SSR atual** dos 9 blocos para não regredir TTI; só os fragmentos antes montados em PHP-string saem.
- `componentes/Missoes/Logistica/sinais/Dashboard/Laboratorios/DashboardLaboratorio.php` — alterações pontuais somente se necessário para que os handlers consumam-no de forma stateless (`$galaxia` opcional/null tolerável; já é). Não reescrever.
- `linguagens/{pt-br,en,es}.json` — apenas se os fragmentos de erro novos do JS exigirem chaves (ex.: `dashboard_erro_carregar`); não migrar strings preexistentes (fora de escopo per spec).

## Sondas e schema

**N/A.** Feature é refatoração arquitetural; nenhuma migration nesta entrega. `DashboardLaboratorio` segue lendo via Sondas/Laboratórios da `DashboardApi` (já V2).

## Endpoints / fluxo

Todas as rotas sob `/api/v1/dashboard/*`, autenticadas por JWT (`AuthJwtMiddleware`), com `customerControl`/`companyControl` aplicados pelo Dispatcher e respeitados por `DashboardLaboratorio`.

| Método | Rota | Handler | Laboratório (método consumido) | Validator | Auth |
|---|---|---|---|---|---|
| GET  | /api/v1/dashboard/resumo               | ResumoOperacionalHandler | DashboardLaboratorio::resumoOperacional | FiltroDashboardValidator | JWT |
| GET  | /api/v1/dashboard/missoes              | ListarMissoesHandler     | DashboardLaboratorio::listarMissoes     | FiltroDashboardValidator | JWT |
| GET  | /api/v1/dashboard/desempenho           | DesempenhoHandler        | DashboardLaboratorio::desempenho        | FiltroDashboardValidator | JWT |
| GET  | /api/v1/dashboard/alertas              | AlertasHandler           | DashboardLaboratorio::alertas           | FiltroDashboardValidator | JWT |
| GET  | /api/v1/dashboard/tipos-missao         | TiposMissaoHandler       | DashboardLaboratorio::tiposMissao       | (sem input)              | JWT |
| GET  | /api/v1/dashboard/extrato              | ExtratoHandler           | DashboardLaboratorio::extrato           | FiltroDashboardValidator | JWT |
| GET  | /api/v1/dashboard/visao-geral          | VisaoGeralDiaHandler     | DashboardLaboratorio::visaoGeralDia     | FiltroDashboardValidator | JWT |
| GET  | /api/v1/dashboard/veiculos             | VeiculosStatusHandler    | DashboardLaboratorio::veiculosStatus    | FiltroDashboardValidator | JWT |
| GET  | /api/v1/dashboard/rotas-dia            | RotasDoDiaHandler        | DashboardLaboratorio::rotasDoDia        | FiltroDashboardValidator | JWT |
| GET  | /api/v1/dashboard/filtrar              | FiltrarHandler           | (agrega 7 chamadas paralelas)           | FiltroDashboardValidator | JWT |
| GET  | /api/v1/dashboard/missoes/{id}         | DetalhesMissaoHandler    | DashboardLaboratorio::listarMissoes(['missao_id'=>$id]) | DetalhesMissaoValidator | JWT |
| POST | /api/v1/dashboard/missoes/{id}/acao    | AcaoMissaoHandler        | DashboardLaboratorio::acaoMissao        | AcaoMissaoValidator      | JWT |

Handlers seguem o template do `EdicaoRota/CriarHandler`:
1. Coletam input de `$req->query`/`$req->body`/`$req->params`.
2. Rodam Validator dedicado; se erros, devolvem 422 via `$res->json(['error'=>['code'=>'invalid_input','message'=>$erro]], 422)`.
3. Instanciam `DashboardLaboratorio` (sem `$galaxia`, `null` tolerável) e chamam o método correspondente.
4. Devolvem JSON puro: `$res->json(['data'=>$lab->result, 'request_id'=>$req->requestId], 200)` ou `adaptarRespostaLegacy` se a saída do laboratório usar envelope `notification_class`.

`FiltrarHandler` é o agregador que substitui o antigo `Dashboard::filtrarDashboard` — mas devolve **JSON puro** (sem `html_kanban`/`html_veiculos`/`html_rotas`). O JS reconstrói o DOM a partir dos arrays.

## Padrões de extensibilidade

**N/A.** Não há múltiplos módulos consumindo o Dashboard nem estratégias plugáveis. Introduzir Registry/Adapter seria padrão prematuro contra a regra "não invente strategies para 1 fluxo". O que existe de "Adapter" no `DashboardLaboratorio` (adaptação dos labs da `DashboardApi`) **já está pronto** e fora do escopo desta feature.

## Ordem de implementação (sequencial)

1. **Validators** (`FiltroDashboardValidator`, `AcaoMissaoValidator`, `DetalhesMissaoValidator`) — puros, fáceis de testar isolados.
2. **Handlers** em `Plataforma/sinais/Api/Handlers/Dashboard/` — começar pelos GETs simples (TiposMissao, ResumoOperacional) e terminar em `FiltrarHandler` e `AcaoMissaoHandler`.
3. **Wiring**: `Api.php` (12 métodos thin) + `Routes.php` (bloco `// Dashboard`) + `OpenApi/spec.php` (bloco `/dashboard/*`).
4. **Smoke test API** via curl/dev: `GET /api/v1/dashboard/resumo?dia_efetivo=YYYY-MM-DD` com JWT — confirmar JSON e shape igual ao `DashboardLaboratorio::resumoOperacional()['data']`.
5. **Refactor `dashboard.js`** — helper `apiGet/apiPost` + migrar cada `sendToGalaxia` em ordem: `filtrar` → `executarAcaoMissao` → `abrirDetalhesMissao` → carregamento inicial dos 9 blocos (se RN 9 mudar). Mover montagem de HTML (kanban card, linha de veículo, cartão de rota) para funções JS.
6. **Refactor `Dashboard.php`** — remover `filtrarDashboard`/`detalhesMissao` e branches do `start()`; deixar só `renderShell` que chama `visual('html/dashboard',...)`.
7. **Marker de deprecação** em `Logistica/sinais/DashboardApi/DEPRECATED.md`.
8. **Testes** em `Logistica/sinais/Dashboard/Testes/` (handlers + 2 e2e) — rodar contra `dev_gx_goldie`.
9. **QA visual** na rota `/missao/logistica/dashboard` confirmando paridade.

## Riscos e decisões não-óbvias

1. **Paridade visual do kanban/veículos/rotas movida para JS** — risco de drift entre o template PHP atual e o template JS novo (espaçamento, cores, ícones). Mitigação: portar as `match`/`if` de cor 1:1 das linhas 202-213 e 248-250 de `Dashboard.php` para constantes JS; QA com print antes/depois.
2. **TTI inicial** — RN 9 diz que blocos podem carregar via API após load. Decisão: **manter SSR atual** dos 9 blocos (`renderShell` continua passando arrays para a view) para não regredir TTI, e usar `/api/v1/dashboard/filtrar` apenas em interações pós-load. Caso o time prefira modo "shell + 9 fetches", a estrutura de handlers já suporta — basta o JS chamar tudo no `$(document).ready`.
3. **`acaoMissao` autorização** — hoje o backend confia que quem está logado pode operar a missão; a API expõe a mesma operação para qualquer JWT válido do tenant. Mitigação: handler valida `customerControl`/`companyControl` ao buscar a missão antes de aplicar a ação (não confiar só no JWT). `AcaoMissaoValidator` rejeita ações fora da allowlist (`pausar/retomar/cancelar/iniciar`).
4. **`FiltrarHandler` agregador vs N chamadas** — devolver tudo num único request preserva atomicidade visual do filtro mas concentra latência. Optamos por manter um único endpoint `/dashboard/filtrar` para não regredir UX (igual hoje); endpoints individuais existem para uso futuro/granular.
5. **`DashboardLaboratorio` aceita `$galaxia=null`?** — confirmado no construtor (`$galaxia = null`) e no uso interno (`new XxxLaboratorio($this->galaxia, ...)`); os labs da `DashboardApi` toleram `null`. Validar no smoke test.
6. **`/openapi.json` cresce** — adicionar bloco `/dashboard/*` requer manutenção; sem isso AC4.3 ainda passa, mas deixamos a doc desatualizada. Incluir no escopo.

## Breaking changes

- `Dashboard::filtrarDashboard()` e `Dashboard::detalhesMissao()` deixam de existir (AC3.2). Qualquer caller externo (não há, pelo grep) quebraria — verificar com `grep -rn "filtrarDashboard\|detalhesMissao" componentes/` antes de remover; se houver, transformar em stub que faz `trigger_error('deprecado, use /api/v1/dashboard/*')` e retorna vazio.
- `dashboard.js` deixa de usar `sendToGalaxia` e o canal `Observatorio` do `Dashboard.php` (`acaoMissao`/`filtrar`/`listarMissoes`) torna-se código morto — remover apenas as branches relacionadas; manter o método `Observatorio` se outros callers usarem (não usam, pelo controller — é específico do dashboard).
- Nenhuma alteração de banco. Nenhum impacto em outros sinais.

## Plano de teste

- **Unit (Validators)**: `FiltroDashboardValidator` rejeita `data` mal formada; `AcaoMissaoValidator` rejeita ação fora da allowlist e `missao_id<=0`; `DetalhesMissaoValidator` rejeita `missao_id<=0`.
- **Unit (Handlers)**: cada um dos 12 — caminho feliz (mock do `DashboardLaboratorio` ou fixtures em `dev_gx_goldie`) + um caminho de erro (input inválido → 422; missão inexistente → 404 ou domínio retornando vazio).
- **Integration (e2e)** em `dev_gx_goldie`:
  - `FiltrarDashboardE2ETest` — POST de filtros via `/api/v1/dashboard/filtrar` retorna a mesma forma de payload que o legacy entregava (sem os 3 campos `html_*`).
  - `AcaoMissaoE2ETest` — `POST /api/v1/dashboard/missoes/{id}/acao` com `acao=pausar` realmente pausa a missão e devolve estado atualizado.
- **Smoke API**: 12 chamadas `curl` autenticadas contra `dev_gx_goldie` cobrindo cada rota.
- **Manual**: gestor (ou QA) abre `/missao/logistica/dashboard`, filtra por status/tipo/data, executa `pausar/retomar/cancelar/iniciar` em uma missão, abre detalhes — paridade visual e funcional confirmada (AC1.1–AC1.5). DevTools Network mostra apenas calls a `/api/v1/dashboard/*` com header `Authorization`.
- **Verificações automáticas (gates da spec)**:
  - `grep -c sendToGalaxia componentes/Missoes/Logistica/sinais/Dashboard/html/dashboard.js` deve dar `0` (AC2.1).
  - `grep -E "'<tr|'<div|'<td|'<li" componentes/Missoes/Logistica/sinais/Dashboard/Dashboard.php` deve dar `0` (AC3.1, métrica).
  - Contagem de rotas `/dashboard/` em `Routes.php` ≥ 10 (AC4.3).

## Out of scope (confirmando spec)

- Redesenho de UX, novas visualizações, novos filtros ou novas ações.
- Reescrita do `DashboardLaboratorio` ou dos labs em `DashboardApi/Laboratorios/` (apenas adaptações mínimas).
- Outros sinais de Logística (Edicaorota, Board, Central, Armazem, Mapas).
- Tornar `/api/v1` pública — segue interna, gateada por `Config::DEV_USER_IDS` para `/docs` e `/openapi.json`.
- Remoção do sinal `Logistica/sinais/DashboardApi/` — apenas marcar `DEPRECATED.md`. Remoção definitiva fica como débito técnico para próximo PRD.
- Remoção de `Logistica/sinais/Armazem copy/`.
- Migração completa de i18n das strings preexistentes — apenas não introduzir piora.
- Alterações de schema/migrations.

**Adicionais técnicos fora de escopo:**
- Não introduzir Registry/Adapter no Dashboard (sem caso de uso real).
- Não estender `BaseHandler` com novos métodos genéricos só para Dashboard — usar o `adaptarRespostaLegacy` existente.
- Não tocar nos middlewares (`AuthJwtMiddleware`, `RateLimitMiddleware` etc.) — herdam o comportamento padrão como EdicaoRota.
