# Documentação do Front-end — Dashboard Operacional

> Painel de operação logística (separação + carregamento) construído em **React 18 + Vite + TypeScript + Tailwind + shadcn/ui** com gráficos em **Recharts**. Os dados são gerados por um banco mock determinístico em memória (PRNG semeada, ~365 dias úteis), consultados por funções "evento" puras e renderizados por componentes "uiux" puros, conectados pelas camadas "contexto".

---

## 1. Arquitetura

A organização do código segue uma convenção própria em `src/palavra/`:

| Camada | Pasta | Responsabilidade |
|---|---|---|
| **BancoDeDados** | `palavra/BancoDeDados/` | Mock em memória, interfaces de domínio (Pessoa, Rota, Carregamento, DiaOperacional), middleware de query. |
| **Eventos** | `palavra/Eventos/` | Funções puras de consulta e cálculo (KPIs, rankings, séries temporais, scores). |
| **Variaveis** | `palavra/Variaveis/` | Estado global leve (período, tema, visão). |
| **Uiux** | `palavra/Uiux/` | Componentes 100% apresentacionais (recebem dados via props). |
| **Contexto** | `palavra/Contexto/` | Conecta estado + eventos → componentes Uiux. |

Fluxo: **Estado** (`range.state`) → **Contexto** chama **Evento** → calcula sobre **BancoDeDados** → entrega ao **Uiux**.

---

## 2. Modelo de dados (resumo)

- **Pessoa** — separador/conferente com `perfil` (excelente / bom / mediano / ruim), `ritmo`, `taxaErro`, `mixErro` (trocado/falta/sobra).
- **RotaSeparacao** — rota separada por equipe num dia: `itens`, `cubagem` (m³), `tempoMin` (25–45), `modoEquipe` (única/dividida), `liderId`, `participacoes[]`, `pendenteOvernight`.
- **ParticipacaoSeparacao** — fatia individual de um separador na rota: `itens`, `cubagem`, `tempoPorItemSeg`, `erros`, `errosPorTipo`.
- **Carregamento** — embarque por conferente: `tempoMin`, `retrabalho` (bool), `errosMotorista[]` (item_faltando, item_trocado, avaria_transporte, endereco_errado).
- **DiaOperacional** — `{ data, rotas[], carregamentos[] }`.

Tipos de erro de separação: `trocado` ("Separado errado"), `falta` ("Quantidade a Menos"), `sobra` ("Quantidade a Mais").

---

## 3. Layout do Dashboard

Todas as visualizações compartilham um **filtro de período** global (DateRange `from..to`). O dashboard tem 3 abas:

1. **Visão Geral** — fluxo macro + top performers
2. **Separação** — desempenho de separadores e erros
3. **Carregamento** — desempenho de conferentes e retrabalho

No topo, fixa, fica a **Grade de KPIs** (6 cards).

---

## 4. KPIs do topo (`GradeKpis`)

Calculados em `obterKpis(range)` agregando todos os dias do período.

| KPI | Fórmula | Origem |
|---|---|---|
| **Separações** | `Σ rotas` no período | `dia.rotas.length` |
| **Carregamentos** | `Σ carregamentos` no período | `dia.carregamentos.length` |
| **Itens Separados** | `Σ rota.itens` | soma de itens das rotas |
| **Cubagem** | `Σ rota.cubagem` (m³) | soma das cubagens |
| **Tempo Sep→Carg** | mediana de `(min(mli.iniciado_em) carg − max(mli.concluido_em) sep)` por `romaneio_id` | `MissaoLogisticaSonda::leadTimeSepCarregamento` |
| **Pendências Overnight** | `Σ rotas com pendenteOvernight=true` + cubagem associada | rotas separadas e não embarcadas no dia |

**Cores semafóricas:**
- Tempo Sep→Carg: `≤30min verde`, `≤120min amarelo`, senão vermelho. Valores negativos (paralelismo — conferente começa antes do separador terminar) também são verde.
- Overnight: `=0 verde`, `≤3 amarelo`, senão vermelho.
- Demais cards: borda azul (`info`), neutros.

Cada card aceita um **sparkline** opcional (mini-série diária extraída de `obterFluxoDiario`).

---

## 5. Aba "Visão Geral" (`PainelVisaoGeral`)

Componentes:

- **Fluxo: Separações vs Carregamentos** — `AreaChart` (recharts) com 2 séries diárias. Quando `>60` pontos, agrega por semana (`ww/yy`).
  - Fonte: `obterFluxoDiario(range)` → `{ data, separacoes, carregamentos, overnight }`.
- **Pendências Overnight** — `BarChart` da série diária de overnight.
  - Fonte: `obterOvernightSerie(range)` → `{ data, overnight, cubagem }`.
- **Top 3 Separadores** e **Top 3 Conferentes** — primeiros 3 do ranking ordenado por score, com `ScoreBadge` colorido.

---

## 6. Aba "Separação" (`PainelSeparacao`)

### 6.1 Cards superiores
| Card | Fonte | Cor da borda |
|---|---|---|
| Tempo Médio (seg/item) | `obterTempoMedioPorItem(range).medio` | azul (`info`) |
| Tipos de Erro de Separação (Pie) | `obterErrosSeparacao(range)` — totais por trocado/falta/sobra | vermelha (`danger`) |
| Itens Separados, Maior Cubagem, Maior Assertividade, Menor Tempo | derivados do ranking | azul; o card "Maior Assertividade" usa cor semafórica do valor |

### 6.2 Ranking Geral de Separadores (`RankingList`)
Fonte: `obterRankingSeparadores(range)` → para cada separador acumula:
- itens, cubagem, tempo médio por item (seg), assertividade (%), rotas participadas, vezes líder.

**Assertividade:** `((itens − itens_acusados_no_carregamento) / itens) × 100`

Onde `itens_acusados_no_carregamento` = itens da separação cujo par `(pedido_id, produto_id)` aparece em algum item de carregamento (`tipo_missao_id=3`, mesmo `block`) com `separou_errado = 1`. Comparação `quantidade_bipada == quantidade_esperada` **não** é usada (escalas distintas: `esperada` é geralmente 1 linha lógica; `bipada` é a quantidade real). Itens com `status='ocorrencia'` ficam fora do denominador.

**Tempo médio por item:** `Σ(tempoPorItemSeg × itens) / Σ itens`

**Score do separador** (`ScoreCalculator::scoreSeparador`): cada métrica é normalizada 0–100 (com inversão para "menor é melhor"), e combinada com pesos:

```
score = assertividade*0.4 + velocidade*0.4 + volume*0.2
```

Onde `velocidade = max(0, 1 − tempoSegItem/tempoRef) × 100` (`tempoRef` = mediana do período) e `volume = min(1, itensTotal/200) × 100`.

A cor do card no ranking segue a faixa de **assertividade**:
- `≥95% verde · ≥88% amarelo · senão vermelho` (`corDaAssertividade`).

### 6.3 Erros de Separação
- **Erros de Separação por Mês** — barras mensais (borda vermelha).
- **Erros de Separação — Comparativo Mensal** (`obterErrosSeparacaoComparativo`) — compara o período atual com o mesmo período N meses atrás:
  ```
  variacao = (totalAtual − totalAnterior) / totalAnterior × 100
  ```
  Renderiza linha "atual" vs "anterior" alinhadas dia a dia.
- **Separadores com Mais Erros** — tabela paginada com breakdown por tipo de erro.

---

## 7. Aba "Carregamento" (`PainelCarregamento`)

### 7.1 Cards superiores
| Card | Fonte | Cor |
|---|---|---|
| Tempo Médio de Carregamento (min) | `obterTempoMedioCarregamento(range)` | azul |
| Retrabalho | `obterRetrabalho(range).indice` | vermelha |
| Mais Rápido / Maior Cubagem / Maior Assertividade | derivados do ranking | warning / azul / verde |

### 7.2 Ranking Geral de Conferentes
Fonte: `obterRankingConferentes(range)` (`RankingConferentesLaboratorio`) — acumula por conferente:
- carregamentos, cubagem (escala = `SUM(cubagem * quantidade_bipada)`), tempo médio (min/carga), erros (ocorrências), retrabalhos (itens com `separou_errado=1`).

**"erros" e "retrabalhos" do conferente** vêm hoje de `missao_logistica_itens` filtrados por `tipo_missao_id=3` (carregamento), não mais da tabela `erros_motorista` (que ficou vazia em produção).

**Score do conferente** (`ScoreCalculator::scoreConferente`):

```
score = acurácia*0.6 + baixoRetrabalho*0.4
```

Onde `acurácia = (1 − erros/cargas) × 100` e `baixoRetrabalho = (1 − retrabalho_pct/100) × 100`.

### 7.3 Retrabalho — duas definições distintas
1. **Retrabalho do separador** (`RetrabalhoLaboratorio`) — `(itens com solicitou_recomeco=1) / total de itens concluídos`. Mede separadores que se interromperam.
2. **Retrabalho do conferente** (`RankingConferentes`) — itens com `separou_errado=1` na missão de carregamento. Mede o conferente acusando má separação.

São fenômenos distintos. Os dois aparecem no UI com o rótulo "retrabalho" — não confunda.

- **Comparativo de Erros (Carregamento)** — análoga à de separação, via `obterErrosCarregamentoComparativo`.
- **Erros por Conferente** — `obterErrosPorConferente`: ocorrências de carregamento (`missao_logistica_itens.status='ocorrencia'` em `tipo_missao_id=3`), agrupadas por `ml.finalizada_por`, com pivot pelos tipos vindos do app (`dados_coletados->>'ocorrencia_tipo'`): falta_produto, outro_estoque, avaria, outros (bucket pra tipos novos).

---

## 8. Componentes de apoio

- **CardKpi** — card padronizado com `borderTone` (info/success/warning/danger), ícone-chip tonal, slot de sparkline ou children.
- **Sparkline** — mini gráfico baseado em recharts, alimentado por séries `{ v: number }`.
- **ScoreBadge** — badge colorido pelo score (`corDoScore`: ≥70 verde, ≥45 amarelo, senão vermelho).
- **RankingList** — lista posicional com pódio, cor por tom configurável e suporte a `borderTone="success|warning|danger|info"`.
- **TabelaPaginada** — tabela genérica com colunas tipadas, paginação client-side.
- **FiltroPeriodo** — DateRange picker que alimenta o estado global `range.state`.

---

## 9. Resumo das fórmulas

| Métrica | Fórmula |
|---|---|
| Tempo Sep→Carg (mediana) | `mediana(min(mli_carg.iniciado_em) − max(mli_sep.concluido_em))` por `romaneio_id` |
| Tempo médio/item (seg) | `Σ(tempoPorItemSeg × itens) / Σ itens` |
| Assertividade separador | `(itens − itens com `separou_errado=1` no carregamento p/ mesmo (pedido,produto,block)) / itens × 100` |
| Score conferente | `acurácia*0.6 + baixoRetrabalho*0.4` (`ScoreCalculator::scoreConferente`) |
| Retrabalho separador | `itens com solicitou_recomeco=1 / total de itens concluídos × 100` (`RetrabalhoLaboratorio`) |
| Retrabalho conferente | `itens com separou_errado=1 / cargas` (`RankingConferentes`) — métrica distinta da acima |
| Variação comparativa | `(atual − anterior) / anterior × 100` |
| Score separador | `0.4·assert + 0.4·velocidade + 0.2·volume` (normalizados, `ScoreCalculator::scoreSeparador`) |
| Cor por score | ≥70 verde · ≥45 amarelo · senão vermelho |
| Cor por assertividade | ≥95 verde · ≥88 amarelo · senão vermelho |

---

## 10. Estado, tema e roteamento

- **Tema** claro/escuro via `tema.state` + `AlternadorTema`, tokens HSL semânticos em `index.css` (info, success, warning, danger, primary, accent, surface-card etc.).
- **Período** global em `range.state` (default: últimos 30 dias úteis).
- **Rotas** registradas em `palavra/BancoDeDados/Rotas/rotas.registro.ts`; entrada em `App.tsx`.
- **Middleware de query** (`aplicarMiddleware`) intercepta cada chamada de evento — útil para futuro logging/cache/troca por backend real.
