# Admin config UI — backend + frontend

## Arquivos criados

- `componentes/Missoes/Logistica/sinais/Admin/Laboratorios/ScanCotaConfig.php` — Laboratório V2 com opções `salvar` e `leitura`; gate dev-only em todas as opções; valida pct (numeric, 0–100) e toggles (0/1); persiste via `ScanCotaConfigSonda::salvarConfig()`; retorna `scanCotaConfig` no payload de sucesso.

## Arquivos alterados

- `componentes/Missoes/Logistica/sinais/Admin/Admin.php` — adicionou `use` para `ScanCotaConfigSonda` e `ApiConfig`; novo método público `scanCotaConfig()`; nova entrada no `start()`: `if ($data['sinalGxData'] == 'scan-cota-config') return $this->scanCotaConfig($data);`.
- `linguagens/pt-br.json` — 7 novas chaves `scan_cota_config_*` para a tela de admin.
- `linguagens/en.json` — idem, traduções em inglês.
- `linguagens/es.json` — idem, traduções em espanhol.

## Desvios do techplan

Nenhum. O techplan aprovado descrevia apenas o conjunto de arquivos e a lógica; tudo seguido à risca.

## Comandos de verificação executados

- `php -l componentes/Missoes/Logistica/sinais/Admin/Laboratorios/ScanCotaConfig.php` → OK
- `php -l componentes/Missoes/Logistica/sinais/Admin/Admin.php` → OK
- `php -r "json_decode(file_get_contents('linguagens/pt-br.json'))..."` → OK
- `php -r "json_decode(file_get_contents('linguagens/en.json'))..."` → OK
- `php -r "json_decode(file_get_contents('linguagens/es.json'))..."` → OK

## Contrato para o frontend

### URL de acesso

```
logistica/admin/scan-cota-config
```

O controller `Admin::start()` detecta `sinalGxData == 'scan-cota-config'` e despacha para `scanCotaConfig()`. Se o usuário não estiver na allowlist `Config::DEV_USER_IDS`, a chamada redireciona para `goldie_url()` antes de renderizar qualquer HTML.

### Variáveis disponíveis na view `html/scan_cota_config.php`

| Variável | Tipo | Descrição |
|---|---|---|
| `$data['scanCotaConfig']` | `object` | Config atual da empresa (saída de `ScanCotaConfigSonda::obterConfig()`). Campos: `ativo`, `pct`, `ativo_separacao`, `ativo_carregamento`, `block_empresa_id`. Se a empresa não tem linha, todos os campos chegam com valores default desligados (`ativo=0`, `pct=0.00`). |
| `$data['empresaId']` | `int` | ID da empresa atual (derivado de `principalEmpresa->empresa_id`). Informativo; o backend recomputa a partir da sessão no save. |
| `$data['formSalvarScanCota']` | `string` (HTML) | HTML completo do formulário Galaxia V2 já montado via `visualFormulario`. Inclui header/footer de form, botão "Salvar configuração" e os campos hidden de roteamento. O frontend só precisa incorporar este HTML no template e renderizar os inputs visíveis por cima. |
| `$galaxia` | `GalaxiaRoute` | Instância normal da rota, disponível em toda view Galaxia. |

### Campos do formulário de save

O formulário `forms/scan_cota_config.php` (a criar pelo frontend) deve emitir os inputs abaixo dentro do formulário cujo HTML-wrapper veio em `$data['formSalvarScanCota']`:

| Campo | Tipo HTML | Descrição | Restrição |
|---|---|---|---|
| `ativo` | `hidden` ou `checkbox` | Master on/off | `0` ou `1` |
| `pct` | `number` ou `text` | Percentual mínimo (%) | `0.00` a `100.00`, numérico |
| `ativo_separacao` | `hidden` ou `checkbox` | Toggle separação | `0` ou `1` |
| `ativo_carregamento` | `hidden` ou `checkbox` | Toggle carregamento | `0` ou `1` |

Para checkboxes sem `value`, envie um `<input type="hidden" name="campo" value="0">` antes do checkbox com `value="1"` (padrão HTML para boolean checkbox).

### Como o save funciona (fluxo Galaxia V2)

1. O submit do form (gerado por `visualFormulario`) faz POST via AJAX Galaxia V2.
2. O framework roteia para `GalaxiaMissoes\Logistica\sinais\Admin\Laboratorios\ScanCotaConfig` com opcao `salvar`.
3. O Laboratório valida, persiste e retorna o payload abaixo.
4. A resposta chega no callback JS do Galaxia como JSON.

### Payload de resposta do save (JSON Galaxia)

**Sucesso:**
```json
{
  "status": "success",
  "message": "Configuração salva com sucesso.",
  "scanCotaConfig": {
    "id": 1,
    "ativo": 1,
    "pct": "10.00",
    "ativo_separacao": 1,
    "ativo_carregamento": 0,
    "block_empresa_id": 42
  }
}
```

**Erro de validação ou permissão:**
```json
{
  "status": "error",
  "message": "<mensagem localizada>"
}
```

O campo `scanCotaConfig` no payload de sucesso pode ser usado pelo JS para atualizar a UI sem reload de página.

### Strings i18n disponíveis para a view

Use `show('chave')` no PHP ou o mecanismo de i18n do Galaxia JS para todas as strings.

| Chave | pt-BR |
|---|---|
| `scan_cota_config_titulo` | Configuração de cota mínima de bipagem |
| `scan_cota_config_master` | Cota mínima de bipagem (label master toggle) |
| `scan_cota_config_pct` | Percentual mínimo de itens por scanner (%) |
| `scan_cota_config_separacao` | Aplicar na separação |
| `scan_cota_config_carregamento` | Aplicar no carregamento |
| `scan_cota_config_btn_salvar` | Salvar configuração |
| `scan_cota_config_salvo_sucesso` | Configuração salva com sucesso. |
| `scan_cota_config_erro_salvar` | Erro ao salvar a configuração. Tente novamente. |
| `scan_cota_config_erro_pct_invalido` | O percentual informado não é válido. |
| `scan_cota_config_erro_pct_fora_range` | O percentual deve estar entre 0 e 100. |
| `scan_cota_config_aviso_futuro` | Atenção: alterações aqui valem apenas para missões criadas a partir de agora. Missões já geradas mantêm a configuração do momento da criação. |

### Arquivos que o frontend deve criar (esta etapa)

```
componentes/Missoes/Logistica/sinais/Admin/
├── forms/
│   └── scan_cota_config.php   ← inputs do formulário (dentro do wrapper gerado por visualFormulario)
└── html/
    └── scan_cota_config.php   ← view principal da tela (usa $data['formSalvarScanCota'] + $data['scanCotaConfig'])
    └── scan_cota_config.js    ← (opcional) JS da tela (lido automaticamente por visualFormulario se existir)
```

### Notas de implementação da view

- A view deve exibir o aviso `scan_cota_config_aviso_futuro` de forma proeminente antes do formulário.
- O campo `pct` deve aceitar decimais (`step="0.01"`), min=0, max=100.
- Os toggles `ativo`, `ativo_separacao`, `ativo_carregamento` são booleanos; usar checkbox ou toggle switch.
- Ao submeter, o JS pode ler `response.scanCotaConfig` para refletir os valores salvos na UI sem reload.
- A tela é dev-only; não há link para ela no menu principal. Acesso direto via URL.

---

## Implementação frontend

### Views criadas

- `componentes/Missoes/Logistica/sinais/Admin/html/scan_cota_config.php` — view principal da tela: breadcrumb, badge DEV ONLY, alerta `scan_cota_config_aviso_futuro`, card que incorpora `$data['formSalvarScanCota']` (wrapper gerado pelo controller), botão voltar via `$galaxia->proximaRota('logistica/admin')`.
- `componentes/Missoes/Logistica/sinais/Admin/html/scan_cota_config.js` — JS pareado: desabilita sub-toggles quando master `ativo` está desligado; escuta `ajaxComplete` para atualizar inputs com `response.scanCotaConfig` após save bem-sucedido (sem reload).

### Forms

- `componentes/Missoes/Logistica/sinais/Admin/forms/scan_cota_config.php` — inputs do formulário: switch Metronic para `ativo`, `input[number]` com `step=0.01 min=0 max=100` para `pct`, switches para `ativo_separacao` e `ativo_carregamento`. Checkboxes seguem o padrão hidden+checkbox para envio correto de booleanos. Pré-preenchimento via `$scanCotaConfig`.

### Descoberta — link na index do Admin

- `componentes/Missoes/Logistica/sinais/Admin/html/admin.php` — adicionado link "Cota Bipagem" no cluster de botões fixos no canto superior direito, ao lado de "Ver Grupos". O gate já está no controller; o link aparece para todos mas a rota redireciona não-devs.

### i18n

Todas as chaves `scan_cota_config_*` já provisionadas pelo backend. Nenhuma chave nova adicionada nesta etapa.

### Endpoints consumidos

- POST via formulário Galaxia V2 gerado por `visualFormulario('forms/scan_cota_config')` → `Laboratorios/ScanCotaConfig` opcao `salvar`.
  - Payload enviado: `ativo`, `pct`, `ativo_separacao`, `ativo_carregamento` (+ campos hidden de roteamento do framework).
  - Resposta sucesso: `{ notification_class: 'success', scanCotaConfig: { id, ativo, pct, ativo_separacao, ativo_carregamento, block_empresa_id } }`.
  - Resposta erro: `{ notification_class: 'error', notification_html: '<msg localizada>' }`.
  - O framework (`NotificarToastr`) exibe o toastr automaticamente. O JS pareado só atualiza os inputs sem reload.

### Smoke test sugerido

1. Acessar `logistica/admin/scan-cota-config` com usuário na allowlist `Config::DEV_USER_IDS` → deve renderizar a view com breadcrumb, alerta amarelo e card de formulário.
2. Verificar que os inputs chegam pré-preenchidos com a config atual da empresa.
3. Alterar `pct` para um valor válido (ex.: `15.50`), marcar os toggles e clicar em "Salvar configuração" → deve aparecer toastr verde com texto `scan_cota_config_salvo_sucesso` e os inputs devem refletir os valores salvos sem reload.
4. Submeter com `pct = 150` (fora do range) → deve aparecer toastr de erro com `scan_cota_config_erro_pct_fora_range`.
5. Desmarcar o master toggle `ativo` → os sub-toggles `ativo_separacao` e `ativo_carregamento` devem ficar com opacidade reduzida e desabilitados.
6. Acessar `logistica/admin` → deve exibir o link "Cota Bipagem" no canto superior direito.
7. Acessar `logistica/admin/scan-cota-config` com usuário fora da allowlist → deve redirecionar para `goldie_url()` sem renderizar HTML.
