# Dashboard Operacional — Implementação Frontend

## ACs verificados

| AC | Status |
|----|--------|
| AC2.6 — Período > 365 dias exibe erro | Validado no `DashboardFiltro.init()` — rejeita com toast |
| AC3.4 — KPIs ficam visíveis ao trocar de aba | KPI grade está fora das seções de aba; sempre visível |
| AC7.2 — Trocar idioma → labels mudam | Todas as strings usam `show()` no PHP; i18n nas 3 línguas |
| AC8.5 — Zero chamada a Lab no boot | `Dashboard.php` não chama nenhum Lab; `DashboardBoot` é só `from/to/tab` |
| Erros motorista vazios — tratamento graceful | `_renderErrosConferente` e `_renderErrosCarregamento` mostram "Sem registros no período" se `total === 0` ou array vazio |
| URL persistida (F5-safe) | `DashboardRouter.atualizar()` usa `window.history.replaceState` com `?from&to&tab` |
| Lazy fetch por aba | `data-loaded="false"` → só fetcha ao ativar aba; não refetcha se já carregada |

---

## Views criadas / reescritas

- `componentes/Missoes/Logistica/sinais/Dashboard/html/dashboard.php` — Shell HTML completo: topbar com título + DateRange picker + tabs (Visão Geral / Separação / Carregamento) + grade de 6 KPIs com sparkline containers + 3 seções de aba com todos os containers de chart/tabela
- `componentes/Missoes/Logistica/sinais/Dashboard/html/dashboard.js` — JS pareado: reescrito do zero, limpa todo o código kanban legado

---

## Nenhum form criado

A feature é leitura pura. Não há ações de escrita via form.

---

## i18n

Chaves adicionadas nas 3 línguas (`pt-br.json`, `en.json`, `es.json`). Total: 58 chaves novas.

Solicitacao path: `componentes/Missoes/Logistica/sinais/Dashboard/html/dashboard.php`

| Chave (hash MD5 do texto pt) | Texto pt-br | EN | ES |
|---|---|---|---|
| `d734c31d...` | Dashboard Operacional | Operational Dashboard | Dashboard Operacional |
| `2084c04f...` | De | From | Desde |
| `af7d7962...` | Até | Until | Hasta |
| `f0ed2dc3...` | Aplicar | Apply | Aplicar |
| `5dcdce5b...` | Separações | Separations | Separaciones |
| `5eba42ab...` | Carregamentos | Loadings | Cargas |
| `fafe71a8...` | Itens Separados | Picked Items | Ítems Separados |
| `06c725fd...` | Cubagem (m³) | Volume (m³) | Cubicaje (m³) |
| `f883e6d8...` | Eficiência de Fluxo | Flow Efficiency | Eficiencia de Flujo |
| `ba2db9d4...` | carreg. / sep. | load. / sep. | carg. / sep. |
| `35c30b03...` | Pendências Overnight | Overnight Pending | Pendientes Overnight |
| `cd28583c...` | Visão Geral | Overview | Resumen General |
| `8c2c5ddf...` | Separação | Separation | Separación |
| `bcf59a52...` | Carregamento | Loading | Carga |
| `66b5e5af...` | Fluxo: Separações vs Carregamentos | Flow: Separations vs Loadings | Flujo: Separaciones vs Cargas |
| `4715e65c...` | Pendências Overnight por Dia | Overnight Pending by Day | Pendientes Overnight por Día |
| `4a83b1f6...` | Top 3 Separadores | Top 3 Pickers | Top 3 Separadores |
| `abc27477...` | Top 3 Conferentes | Top 3 Checkers | Top 3 Conferentes |
| `8e5468f0...` | Tempo Médio por Item | Avg Time per Item | Tiempo Medio por Ítem |
| `2856a761...` | seg/item | sec/item | seg/ítem |
| `a0063116...` | Tipos de Erro de Separação | Separation Error Types | Tipos de Error de Separación |
| `8c90a7a4...` | Maior Assertividade | Highest Assertiveness | Mayor Asertividad |
| `facde037...` | Ranking Geral de Separadores | General Pickers Ranking | Ranking General de Separadores |
| `8ecab17f...` | Pos. | Pos. | Pos. |
| `c0289e98...` | Nome | Name | Nombre |
| `097138191...` | Rotas | Routes | Rutas |
| `82b7a5e9...` | Itens | Items | Ítems |
| `56be56de...` | Cubagem | Volume | Cubicaje |
| `6b37709e...` | Tempo (s/item) | Time (s/item) | Tiempo (s/ítem) |
| `fb706753...` | Assertividade | Assertiveness | Asertividad |
| `cd7e4607...` | Líder | Leader | Líder |
| `5dd135d1...` | Score | Score | Score |
| `d6fbe28c...` | Erros de Separação por Mês | Monthly Separation Errors | Errores de Separación por Mes |
| `1773cb20...` | Comparativo Mensal — Erros de Separação | Monthly Comparison — Separation Errors | Comparativo Mensual — Errores de Separación |
| `7698f3da...` | Separadores com Mais Erros | Pickers with Most Errors | Separadores con Más Errores |
| `96b01412...` | Total | Total | Total |
| `a3bd3b19...` | Trocado | Swapped | Trocado |
| `7e49997a...` | Falta | Missing | Falta |
| `4d3bd86d...` | Sobra | Excess | Sobra |
| `ce7c5141...` | Tempo Médio de Carregamento | Avg Loading Time | Tiempo Medio de Carga |
| `041188d7...` | segundos/carga | seconds/load | segundos/carga |
| `b0133f42...` | Índice de Retrabalho | Rework Rate | Índice de Retrabajo |
| `4da1404c...` | das cargas com retrabalho | of loads with rework | de las cargas con retrabajo |
| `2bb690a7...` | Erros de Carregamento | Loading Errors | Errores de Carga |
| `cb9198c4...` | Ranking Geral de Conferentes | General Checkers Ranking | Ranking General de Conferentes |
| `d32dada1...` | Cargas | Loads | Cargas |
| `19f5afd8...` | Erros | Errors | Errores |
| `f7a04a3e...` | Retrabalho | Rework | Retrabajo |
| `877722d6...` | Ret. % | Ret. % | Ret. % |
| `683de96c...` | Tempo (s/carga) | Time (s/load) | Tiempo (s/carga) |
| `2a621ee4...` | Índice de Retrabalho — Série | Rework Rate — Series | Índice de Retrabajo — Serie |
| `9dd06054...` | Comparativo Mensal — Erros de Carregamento | Monthly Comparison — Loading Errors | Comparativo Mensual — Errores de Carga |
| `6d2bc793...` | Erros por Conferente | Errors by Checker | Errores por Conferente |
| `54e41fe5...` | Item Faltando | Missing Item | Ítem Faltante |
| `f7a2a2f9...` | Item Trocado | Swapped Item | Ítem Trocado |
| `2d2fec86...` | Avaria Transporte | Transport Damage | Avería Transporte |
| `a41bb34a...` | Endereço Errado | Wrong Address | Dirección Incorrecta |
| `7ecae179...` | Sem registros no período | No records in period | Sin registros en el período |

---

## Endpoints consumidos

Todos os GETs via `DashboardApi.get(path, query)` → `/api/v1/dashboard/<path>`.

| Endpoint | Onde consumido | Parâmetros |
|---|---|---|
| `kpis` | `DashboardKpis.carregar()` | `from`, `to` |
| `fluxo-diario` | `DashboardAbas.visaoGeral.carregar()` | `from`, `to` |
| `overnight-serie` | `DashboardAbas.visaoGeral.carregar()` | `from`, `to` |
| `ranking-separadores` | Visão Geral (top3) + Aba Separação | `from`, `to`, `page`, `perPage` |
| `ranking-conferentes` | Visão Geral (top3) + Aba Carregamento | `from`, `to`, `page`, `perPage` |
| `tempo-medio-item` | Aba Separação | `from`, `to` |
| `erros-separacao` | Aba Separação (pie) | `from`, `to` |
| `erros-separacao/comparativo` | Aba Separação | `from`, `to`, `mesesAtras=1` |
| `erros-separacao/por-pessoa` | Aba Separação (tabela paginada) | `from`, `to`, `page`, `perPage` |
| `tempo-medio-carregamento` | Aba Carregamento | `from`, `to` |
| `retrabalho` | Aba Carregamento (série + índice) | `from`, `to` |
| `erros-carregamento` | Aba Carregamento (breakdown) | `from`, `to` |
| `erros-carregamento/comparativo` | Aba Carregamento | `from`, `to`, `mesesAtras=1` |
| `erros-por-conferente` | Aba Carregamento (tabela paginada) | `from`, `to`, `page`, `perPage` |

---

## Dependências de assets

| Asset | Origem | Carregamento |
|---|---|---|
| ApexCharts | CDN `cdn.jsdelivr.net/npm/apexcharts` | `<script>` no topo do `dashboard.php` (mesmo padrão de `Designacoes/html/designacoes_lista.php`) |
| flowbite-datepicker | `node_modules/flowbite-datepicker/dist/` | CSS + JS incluídos localmente via `/node_modules/...` |

**Nota sobre flowbite**: O `DashboardFiltro.init()` usa `Datepicker` (classe individual do flowbite). Se `Datepicker` não estiver disponível no window (e.g., path errado de `/node_modules/`), os inputs `text` continuam funcionais como campos de data manual — degradação graceful sem quebrar a página.

---

## Decisões de implementação

1. **ApexCharts via CDN** — mesmo padrão estabelecido em `Designacoes/html/designacoes_lista.php` e `designacao_painel.php`. Alternativa (bundle local) não foi usada para manter consistência.

2. **Sem `show()` no JS** — strings de UI renderizadas diretamente no PHP shell; o JS só manipula valores numéricos e dados da API (sem `show()` client-side, que não existe no contexto JS).

3. **`erros-carregamento` graceful** — se `data.disponivel === false` ou `data.total === 0`, renderiza mensagem "Sem registros no período" em vez de tabela vazia ou erro. Alinhado com a decisão do backend (tabela `erros_motorista` existe mas pode não ter dados alimentados ainda).

4. **Detecção de agregação semanal** — `_isAgregadoPorSemana()` no AreaChart do fluxo detecta automaticamente quando a série tem pontos espaçados ≥ 7 dias (backend agrega por semana quando range > 60 dias — RN16).

5. **Paginação client-driven** — os rankings e tabelas de erros usam paginação orientada a servidor (passa `page` e `perPage` na querystring), não paginação client-side pura. Isso alinha com os endpoints que retornam `total`, `page`, `perPage`.

6. **Sparklines nas abas Separação e Carregamento** — o techplan menciona sparklines apenas nos KPIs do topo. A aba de "Erros de Separação por Mês" recebe um `BarChart` mensal via `chart-erros-sep-mes` mas o endpoint correspondente não está listado nos endpoints disponíveis do `03-backend.md`. Esse container está no HTML mas não há fetch para ele — o backend precisará adicionar esse endpoint em rodada futura, ou o container ficará com skeleton até lá. Os demais gráficos batem 1:1 com os endpoints disponíveis.

---

## Smoke test sugerido para o Tester

1. Abrir `/missao/logistica/dashboard` sem querystring → deve renderizar a página com skeleton nos KPIs e na aba "Visão Geral" ativa.

2. Aguardar ~2s → KPIs devem preencher com valores (separações, carregamentos, itens, cubagem, eficiência, overnight). Cards de eficiência e overnight devem ter cor semafórica na barra superior.

3. Clicar na aba "Separação" → seção deve abrir e iniciar fetch; tempo médio, pie de erros e ranking devem aparecer.

4. Clicar na aba "Carregamento" → idem para conferentes e retrabalho.

5. Clicar na aba "Visão Geral" novamente → NÃO deve refazer fetch (dados já carregados — `data-loaded="true"`).

6. Alterar as datas no DateRange e clicar "Aplicar" → todos os dados devem recarregar; todas as abas voltam a `data-loaded="false"`.

7. Tentar período de 400 dias (ex: `from=2024-01-01` `to=2025-03-15`) → clicar "Aplicar" deve mostrar toast de erro "O período não pode exceder 365 dias." sem recarregar dados.

8. Pressionar F5 com `?from=2025-01-01&to=2025-03-31&tab=separacao` na URL → página deve abrir na aba Separação com o período restaurado.

9. Trocar idioma do Galaxia para EN → labels das tabs, KPIs e tabelas devem aparecer em inglês.

10. Com `erros_motorista` vazia (provável em dev sem dados): aba Carregamento → seção "Erros de Carregamento" deve mostrar "Sem registros no período" sem console error.
