# PRD — Pontos em aberto do Central (implicações de UX)

> **Status:** planejamento / inventário priorizado.
> **Módulo:** Central de Logística — `Constelacoes/Logistica/Central/Satelites/Central.php` (satélite V1) + sinal `Missoes/Logistica/sinais/Central/` + `controladores/_Src/maps.js`.
> **Referências de linha:** conferidas contra o código atual (há mudanças não commitadas — revalidar antes de pegar cada item).

---

## Contexto

Nas últimas sprints o Central ganhou uma camada de sincronização em tempo real via Scaledrone (WebSocket). A maior parte do PRD anterior (`PRD/central-socket-sync/`) **já foi implementada** — confirmação de rota, criação/exclusão de rota, os 10 toggles de status, visibilidade e aprovação de portaria já sincronizam entre navegadores sem F5. O `markers_update` (que reescrevia a imagem base e apagava o overlay de cor da rota) foi **removido** por ser nocivo.

Mapeando o **estado real do código** (não o dos docs, que estão defasados), sobraram pontos em aberto que ainda impactam a experiência do operador. Os mais graves são desincronizações que podem gerar **alocação duplicada de pedido** entre dois operadores no mesmo painel.

### Achado: docs desatualizados

`PRD/central-socket-sync/00-prd.md` ainda marca **P1–P4 como "pendente"**, mas estão **implementados** em código (broadcasts `route_update`/`route_delete`, handler `CentralSocketHandler`, reset da flag `alterada`). Atualizar esse doc é o primeiro item de higiene — senão alguém reimplementa o que já existe.

### Infra reaproveitável (não inventar caminho novo)

- `LogisticaSocketTrait::broadcastRouteUpdates($rotas, $normalizar)` — payload completo (`normalizar=true`) ou parcial verbatim (`normalizar=false`).
- `LogisticaSocketTrait::broadcastStatusRota($rotaId, array $campos)` — `route_update` parcial por `rota_id`.
- `LogisticaSocketTrait::broadcastRotaRemovida($rotaId)` — `route_delete` para board + central.
- Frontend `CentralSocketHandler` (`maps.js`) já processa `route_update` / `route_delete` / `route_sequence_updated` e reconcilia `rotasGeradas` + visual dos markers.

---

## 🔴 P0 — Desincronização que gera alocação duplicada (integridade de dados)

### P0-1 · `removerRomaneioPedido` não faz broadcast estruturado

- **Onde:** `Constelacoes/Logistica/Central/Satelites/Central.php:1288-1361`
- **Problema:** remove um pedido da rota (pedido volta a "disponível", cubagem do veículo cai), mas emite **só** `chamarFuncao('alterarImagemDosMarkers')` + `goToUpdateMissaoSinal('ConteudoListagemDeRomaneios')` em `galaxia_logistica_central`. **Não** emite `route_update`/`route_delete`.
- **Impacto de UX:** no navegador B o pedido continua dentro da rota (`rotasGeradas`), a cubagem fica errada e o marker segue "alocado". Quando B salvar, ele pode re-adicionar o pedido → **alocação duplicada** do mesmo pedido em duas rotas. Além disso, o `alterarImagemDosMarkers` é a mesma função base-image que causou o bug do "marker volta pro foguetinho" (apaga o overlay de cor).
- **Severidade:** P0 (corrupção silenciosa de alocação).
- **Abordagem proposta:** após o `success`, emitir broadcast estruturado via trait:
  - se a rota ficou **vazia** → `broadcastRotaRemovida($rotaId)`;
  - se ainda tem pedidos → `broadcastStatusRota($rotaId, ['pedidos' => <lista atualizada>, 'cubagem' => <novo total>])` (`normalizar=false` para não sobrescrever os demais campos).
  - Remover/substituir o `alterarImagemDosMarkers` nocivo; o revert do marker do pedido liberado já acontece pelo caminho estruturado (`updateAllMarkerVisuals`).
- **Critérios de aceite:**
  - [ ] Remover um pedido no navegador A reflete no B **sem F5** (pedido sai da rota em B).
  - [ ] Em B, o marker do pedido removido volta para "disponível" **sem perder a cor das demais rotas**.
  - [ ] A cubagem do veículo na rota fica igual em A e B.
  - [ ] Salvar em B depois do remoto **não** re-adiciona o pedido (sem dupla alocação).

### P0-2 · `removerRomaneioVeiculo` não faz broadcast estruturado

- **Onde:** `Constelacoes/Logistica/Central/Satelites/Central.php:1363-1434`
- **Problema:** desvincula o veículo e cascateia a remoção de **todos** os pedidos (todos voltam a "disponível"), mas — igual ao P0-1 — só emite `alterarImagemDosMarkers` + `ConteudoListagemDeRomaneios`. Sem `route_delete`.
- **Impacto de UX:** no navegador B a rota inteira continua existindo como fantasma, com os pedidos marcados como alocados quando na verdade já estão livres. Risco alto de B alocar os mesmos pedidos em outra rota → **dupla alocação em massa**.
- **Severidade:** P0.
- **Abordagem proposta:** após o `success`, emitir `broadcastRotaRemovida($rotaId)` (a rota deixa de existir sem o veículo) + deixar o revert dos markers dos pedidos liberados pelo caminho estruturado. Remover o `alterarImagemDosMarkers`.
- **Critérios de aceite:**
  - [ ] Desvincular o veículo no navegador A remove a rota no B **sem F5**.
  - [ ] Em B, todos os pedidos da rota voltam a "disponível" no mapa.
  - [ ] B consegue alocar esses pedidos numa nova rota sem conflito/dupla alocação.

---

## 🟠 P1 — Estado de rota fica defasado entre navegadores

### P1-3 · `salvarRoteirizacao` não emite `route_update`

- **Onde:** `Constelacoes/Logistica/Central/Satelites/Central.php:859-949`
- **Problema:** salva a sequência otimizada + métricas do veículo (km_total, custo_total, minutos_total) e a `sequencia_roteirizacao` de cada pedido, mas emite só `goToUpdateMissaoSinal('ConteudoListagemDeRomaneios')` — nenhum `route_update` com a nova sequência.
- **Impacto de UX:** no navegador B a linha/ordem da rota no mapa e as métricas continuam com o estado antigo até o F5. Dois operadores veem roteirizações diferentes da mesma rota.
- **Severidade:** P1 (sem risco de dupla alocação, mas confunde decisão operacional).
- **Abordagem proposta:** após persistir, emitir `broadcastStatusRota($rotaId, ['sequencia' => <ordem>, 'km_total' => ..., 'custo_total' => ..., 'minutos_total' => ...])`; o `CentralSocketHandler` já sabe processar `route_update` parcial. Avaliar reuso do `route_sequence_updated` (hoje só disparado pela Edicaorota) se a reordenação precisar redesenhar a linha.
- **Critérios de aceite:**
  - [ ] Salvar roteirização em A atualiza ordem/métricas em B **sem F5**.
  - [ ] Métricas (km, custo, tempo) idênticas em A e B após o evento.

### P1-4 · Portaria só atualiza num botão de teste manual

- **Onde:** `Missoes/Logistica/sinais/Central/html/central_portaria.php:761-783` (`#btn-teste-refresh-portaria` + `window.__refreshPortariaTeste()`), marcado `<!-- [TESTE TEMPORÁRIO] ... Remover ao trocar pelo timer -->`.
- **Problema:** não há `setInterval`/polling de portaria. Pedidos novos gravados pelo N8N direto no banco **não aparecem** sem F5 ou clique no botão laranja de teste.
- **Impacto de UX:** operador da portaria não vê chegadas em tempo real; precisa lembrar de atualizar manualmente. O botão `[TESTE TEMPORÁRIO]` está visível em produção.
- **Severidade:** P1.
- **Abordagem proposta:** a infra já está pronta e provada (action `listarPortaria` no satélite + `processarDadosMapa` no front, via mecanismo de formulário Galaxia). Trocar o botão por um `setInterval` (~45s) que submete `.formularioListarPortaria`; pausar o timer quando a aba não está visível (`document.hidden`) para não martelar o backend; remover `#btn-teste-refresh-portaria` e a marcação temporária.
- **Critérios de aceite:**
  - [ ] Pedido novo da portaria aparece sozinho em ≤ ~45s, sem F5.
  - [ ] Botão `[TESTE TEMPORÁRIO]` removido da UI.
  - [ ] Timer pausa com a aba em background e retoma ao focar.
  - [ ] `processarDadosMapa` faz re-sync completo (substitui a lista) sem duplicar itens já renderizados.

---

## 🟡 P2 — Falta de feedback / resiliência (clareza pro operador)

### P2-5 · Falha de socket é silenciosa

- **Onde:** `controladores/_Src/maps.js:44-59` (`drone.on('open', error)` / `drone.on('error')` só fazem `console.log`).
- **Problema:** se o WebSocket cai (rede, Scaledrone fora), nada é mostrado. O operador acredita estar sincronizado enquanto vê tela velha.
- **Impacto de UX:** decisões de alocação tomadas sobre estado defasado sem o operador saber que perdeu o tempo real — amplifica o risco dos P0.
- **Severidade:** P2.
- **Abordagem proposta:** nos handlers `drone.on('error')` / `drone.on('close')`, exibir um badge/toast persistente "Sincronização offline — atualize a página" e limpá-lo no `open`/reconexão. Reuso do padrão de toast já presente no módulo.
- **Critérios de aceite:**
  - [ ] Queda do socket mostra aviso visível de "offline".
  - [ ] Reconexão limpa o aviso automaticamente.

### P2-6 · Aprovação de portaria sem toast de sucesso/erro

- **Onde:** `Missoes/Logistica/sinais/Central/html/central_portaria.php:489-507` (`executarAprovacao()` só aplica opacidade 50% por 5s).
- **Problema:** se o submit de aprovação falhar, o operador não recebe nenhuma mensagem — só vê a opacidade voltar.
- **Impacto de UX:** operador não sabe se a portaria foi de fato aprovada; pode reaprovar ou seguir achando que deu certo.
- **Severidade:** P2.
- **Abordagem proposta:** adicionar toast de sucesso/erro ao retorno de `executarAprovacao()` (já existe padrão de toast no módulo). Manter o dedup contra self-echo (`window.__portariaAprovRecentes`).
- **Critérios de aceite:**
  - [ ] Aprovação bem-sucedida mostra toast de sucesso.
  - [ ] Falha mostra toast de erro com motivo.

---

## 🟢 P3 — i18n (operadores não-PT)

### P3-7 · ~22 alertas em PT-BR hardcoded (sem `show()`)

- **Onde:**
  - `controladores/_Src/maps.js`: 2169, 2187, 2190, 6476, 7121, 7978, 8009, 8044, 8606, 8737, 9427, 10400, 10433, 10453.
  - `Missoes/Logistica/sinais/Central/html/central_portaria.php`: 310, 501, 505.
  - `Missoes/Logistica/sinais/Central/html/central.php`: 5290, 5294, 6248, 6299.
- **Problema:** strings de alerta/toast fixas em português; tenants ES/EN veem PT.
- **Impacto de UX:** mensagens de erro/validação ilegíveis para operadores não-PT.
- **Severidade:** P3.
- **Abordagem proposta:** envolver com `show("texto PT literal")` (chave = md5 do texto, conforme padrão Galaxia i18n) e deixar o auto-harvest popular `linguagens/{pt-br,en,es}.json`; revisar as 3 línguas no browser por setor. Para os literais em JS, garantir que `show()` esteja disponível no escopo ou passar as strings já traduzidas do PHP.
- **Critérios de aceite:**
  - [ ] As ~22 strings passam por `show()` / i18n.
  - [ ] Conferido em ES e EN no browser que nenhuma cai em PT.

---

## ⚪ Apêndice — Higiene de código (não-UX, mas vale registrar)

- **Docs defasados:** atualizar `PRD/central-socket-sync/00-prd.md` marcando P1–P4 como concluídos (estão implementados).
- **Arquivos cópia/legado** (poluem o VCS, risco de editar o errado):
  - `Missoes/Logistica/sinais/Central/copy Central.php`
  - `Missoes/Logistica/sinais/Central/html/romaneio copy.js`
  - `Missoes/Logistica/sinais/Central/sinais/.oldMapasTecnosoft/`
  - `Missoes/Logistica/sinais/Central/sinais/AutomatizarRotas/html/dados_uso copy.js`
- **Endpoint morto:** `pegarPedidosTecnosoft` (`Constelacoes/Logistica/Central/Satelites/Central.php:2282`) — case no switch (linhas ~65-68) mas sem caller na UI. Decidir remover ou documentar. (`filtrarNovosPedidosDisponiveis` **não** é morto: é chamado pelo cron `crons/crompedidos_logistica.php:20`.)
- **Toggle silencioso (opcional):** `handleRouteUpdate`/`handleRouteDelete` (`maps.js`) aplicam a mudança vinda de um peer sem nenhum aviso. Avaliar um toast sutil ("Rota #X atualizada/removida por outro operador") para o operador não se assustar com a rota mudando sozinha sob o cursor.

---

## Sequência sugerida de execução

1. **P0-1 e P0-2** (integridade — pega o risco de dupla alocação primeiro).
2. **P1-4** (portaria timer — alto valor percebido, infra pronta, baixo risco).
3. **P1-3** (roteirização sync).
4. **P2-5 / P2-6** (feedback).
5. **P3-7** (i18n) + **apêndice de higiene** (oportunístico).

## Verificação (end-to-end)

- **P0/P1 sync:** abrir o Central em 2 navegadores no mesmo dia/block (ambiente `dev_gx_goldie` / `dev-galpaotms.goldieapp.com.br`); executar a ação em A; confirmar que B reflete sem F5 e que não há dupla alocação ao salvar em B.
- **P1-4 portaria:** semear um `processos_status_movimentacoes` pendente (portaria_status=0) com a aba aberta; confirmar que aparece sozinho dentro do intervalo do timer.
- **P2:** derrubar o socket (offline no devtools) e confirmar o aviso; forçar erro na aprovação e confirmar o toast.
- **P3:** trocar idioma do tenant para ES/EN e varrer os fluxos com as strings listadas.
- **Testes automatizados:** os broadcasts dos P0/P1 devem ganhar cobertura no padrão já existente (`Constelacoes/Logistica/Central/Testes/*IntegrationTest.php` no `dev_gx_goldie` + unit hermético em `LogisticaSocketBroadcastTddTest.php`).
