# Cubagem da Carga no Carregamento App — Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Exibir a cubagem total da carga e o progresso bipado em relação à capacidade do caminhão vinculado no badge do veículo no app de carregamento mobile, atualizando em tempo real.

**Architecture:** PHP calcula `$cubagemCargaTotal` e `$cubagemBipadaInicial` iterando os itens já carregados na view (sem query extra). O Presenter expõe `cubagem_unit` por item no `jsItensData`. O JS mantém `statsIncremental.cubagemBipada` de forma incremental e atualiza a barra + fração no `atualizarBadges()`.

**Tech Stack:** PHP 8, JavaScript (vanilla + jQuery), CSS custom properties (`--app-sucesso/aviso/perigo`)

---

## Mapa de arquivos

| Arquivo | Ação |
|---|---|
| `componentes/Missoes/Logistica/sinais/App/html/carregamento/carregamento_v2.css` | Adicionar CSS das novas classes do badge |
| `componentes/Constelacoes/Logistica/MissaoLogistica/Presenters/CarregamentoPresenter.php` | Adicionar campo `cubagem_unit` por item no `$jsItensData` |
| `componentes/Missoes/Logistica/sinais/App/html/shared/components/v2/execucao_bipagem.php` | Calcular totais, emitir hidden inputs, expandir badge HTML |
| `componentes/Missoes/Logistica/sinais/App/html/carregamento/item.js` | Inicializar, atualizar incremental e renderizar cubagem no DOM |

---

### Task 1: CSS — estilos da barra de cubagem

**Files:**
- Modify: `componentes/Missoes/Logistica/sinais/App/html/carregamento/carregamento_v2.css`

- [ ] **Abrir o arquivo e localizar o final do bloco `.carga-badge-veiculo .veiculo-desc` (linha ~434)**

O bloco termina com `}` antes de `.carga-badge-sync`. Adicione as novas classes logo após esse `}`:

```css
.veiculo-cubagem-row {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 3px;
}

.veiculo-cubagem-bar {
    flex: 1;
    height: 3px;
    background: rgba(255, 255, 255, 0.1);
    border-radius: 2px;
    overflow: hidden;
    min-width: 40px;
}

.veiculo-cubagem-bar-fill {
    height: 100%;
    border-radius: 2px;
    background: var(--app-sucesso);
    transition: width 0.4s ease, background 0.4s ease;
}

.veiculo-cubagem-frac {
    color: var(--app-texto-opaco);
    font-size: 0.62rem;
    white-space: nowrap;
    font-family: 'JetBrains Mono', monospace;
}
```

- [ ] **Confirmar que o arquivo foi salvo sem erros de sintaxe (abrindo no browser ou verificando que não há `{` sem fechar)**

- [ ] **Commit**

```bash
git add componentes/Missoes/Logistica/sinais/App/html/carregamento/carregamento_v2.css
git commit -m "feat(carregamento): CSS da barra de cubagem no badge do veículo"
```

---

### Task 2: Presenter — expor `cubagem_unit` por item

**Files:**
- Modify: `componentes/Constelacoes/Logistica/MissaoLogistica/Presenters/CarregamentoPresenter.php`

O `CarregamentoPresenter` tem um loop que itera `$jsItensData` (já montado pelo `MissaoPresenter`) e enriquece cada item com campos extras como `separador_nome`, `localizacao` etc. Precisamos adicionar `cubagem_unit` nesse mesmo loop.

- [ ] **Localizar o bloco de atribuição no loop (em torno da linha 40 do arquivo):**

```php
$itemJson['localizacao'] = $item->localizacao_codigo ?? '';
$itemJson['separador_nome'] = $informacaoSeparacao['separador_nome'] ?? null;
$itemJson['separado_em'] = $informacaoSeparacao['separado_em'] ?? null;
$itemJson['ocorrencia_tipo'] = $informacaoSeparacao['ocorrencia_tipo'] ?? null;
```

- [ ] **Adicionar decodificação de `dados_adicionais` e o novo campo logo antes da linha `$itemJson['localizacao']`:**

```php
$daItem = is_string($item->dados_adicionais ?? '')
    ? json_decode($item->dados_adicionais, true)
    : (array)($item->dados_adicionais ?? []);
$itemJson['cubagem_unit'] = floatval($daItem['cubagem'] ?? 0);
$itemJson['localizacao'] = $item->localizacao_codigo ?? '';
$itemJson['separador_nome'] = $informacaoSeparacao['separador_nome'] ?? null;
$itemJson['separado_em'] = $informacaoSeparacao['separado_em'] ?? null;
$itemJson['ocorrencia_tipo'] = $informacaoSeparacao['ocorrencia_tipo'] ?? null;
```

> **Por quê decodificar aqui em vez de reaproveitar?** O `MissaoPresenter::montarJsonFrontendBase` já decodifica `dados_adicionais` internamente, mas não expõe o resultado. O Presenter de Carregamento tem acesso ao `$item` original, então decodifica de novo (é O(1) por item, sem custo relevante).

- [ ] **Commit**

```bash
git add componentes/Constelacoes/Logistica/MissaoLogistica/Presenters/CarregamentoPresenter.php
git commit -m "feat(carregamento): expor cubagem_unit por item no jsItensData"
```

---

### Task 3: PHP view — calcular cubagem e expandir badge

**Files:**
- Modify: `componentes/Missoes/Logistica/sinais/App/html/shared/components/v2/execucao_bipagem.php`

Este arquivo é o componente compartilhado de bipagem. Todas as mudanças aqui devem ser condicionais a `$cubagemCargaTotal > 0 && $cubagemVeiculo > 0` para não impactar o retorno e outros tipos de missão que não têm veículo ou cubagem.

#### Passo 3a — substituir o bloco do badge do veículo

- [ ] **Localizar o bloco atual (linhas ~416–432):**

```php
<?php
$configs = json_decode($missao->configuracoes ?? '{}', true);
$veiculo = $configs['veiculo_snapshot'] ?? null;
?>
<?php if ($veiculo): ?>
    <div class="carga-badge-veiculo">
        <div class="veiculo-top">
            <span class="v-placa"><i class="bi <?= $icone ?>"></i> <?= htmlspecialchars($veiculo['placa'] ?? '---') ?></span>
            <?php if (!empty($veiculo['cubagem_total'])): ?>
                <span class="v-cubagem"><?= number_format($veiculo['cubagem_total'], 2, ',', '.') ?>m³</span>
            <?php endif; ?>
        </div>
        <?php if (!empty($veiculo['descricao'])): ?>
            <div class="veiculo-desc"><?= htmlspecialchars($veiculo['descricao']) ?></div>
        <?php endif; ?>
    </div>
<?php endif; ?>
```

- [ ] **Substituir pelo novo bloco:**

```php
<?php
$configs = json_decode($missao->configuracoes ?? '{}', true);
$veiculo = $configs['veiculo_snapshot'] ?? null;

$cubagemCargaTotal = 0;
$cubagemBipadaInicial = 0;
foreach ($todosItens as $_cubagemItem) {
    $_da = is_string($_cubagemItem->dados_adicionais ?? '')
        ? json_decode($_cubagemItem->dados_adicionais, true)
        : (array)($_cubagemItem->dados_adicionais ?? []);
    $_unit = floatval($_da['cubagem'] ?? 0);
    $cubagemCargaTotal    += $_unit * floatval($_cubagemItem->quantidade ?? 0);
    $cubagemBipadaInicial += $_unit * floatval($_cubagemItem->quantidade_bipada ?? 0);
}
$cubagemVeiculo = floatval($veiculo['cubagem_total'] ?? 0);
$cubagemPctInicial = ($cubagemVeiculo > 0 && $cubagemCargaTotal > 0)
    ? min(100, ($cubagemBipadaInicial / $cubagemVeiculo) * 100)
    : 0;
$cubagemBarCor = $cubagemPctInicial >= 95
    ? 'var(--app-perigo)'
    : ($cubagemPctInicial >= 80 ? 'var(--app-aviso)' : 'var(--app-sucesso)');
?>
<?php if ($veiculo): ?>
    <div class="carga-badge-veiculo">
        <div class="veiculo-top">
            <span class="v-placa"><i class="bi <?= $icone ?>"></i> <?= htmlspecialchars($veiculo['placa'] ?? '---') ?></span>
            <?php if (!empty($veiculo['cubagem_total'])): ?>
                <span class="v-cubagem"><?= number_format($veiculo['cubagem_total'], 2, ',', '.') ?>m³</span>
            <?php endif; ?>
        </div>
        <?php if (!empty($veiculo['descricao'])): ?>
            <div class="veiculo-desc"><?= htmlspecialchars($veiculo['descricao']) ?></div>
        <?php endif; ?>
        <?php if ($cubagemCargaTotal > 0 && $cubagemVeiculo > 0): ?>
            <div class="veiculo-cubagem-row">
                <div class="veiculo-cubagem-bar">
                    <div class="veiculo-cubagem-bar-fill" id="veiculo-cubagem-bar-fill"
                         style="width: <?= round($cubagemPctInicial, 1) ?>%; background: <?= $cubagemBarCor ?>;"></div>
                </div>
                <span class="veiculo-cubagem-frac" id="veiculo-cubagem-frac"><?= number_format($cubagemBipadaInicial, 1, ',', '.') ?>&nbsp;/&nbsp;<?= number_format($cubagemVeiculo, 1, ',', '.') ?>m³</span>
            </div>
        <?php endif; ?>
    </div>
<?php endif; ?>
```

> **Variáveis prefixadas com `$_`** no loop para não colidir com `$item` e `$da` usados mais abaixo no mesmo bloco.

#### Passo 3b — adicionar hidden inputs para o JS

- [ ] **Localizar o bloco de hidden inputs (linhas ~1020–1023):**

```php
<!-- Dados para JS -->
<input type="hidden" id="data-missao-id" value="<?= $missaoIdAtual ?>">
<input type="hidden" id="data-timer-start" value="<?= $timerStartTime ?>">
<input type="hidden" id="data-validar-codigo-conf" value="<?= $validarCodigo ? '1' : '0' ?>">
```

- [ ] **Adicionar as duas novas linhas logo abaixo:**

```php
<!-- Dados para JS -->
<input type="hidden" id="data-missao-id" value="<?= $missaoIdAtual ?>">
<input type="hidden" id="data-timer-start" value="<?= $timerStartTime ?>">
<input type="hidden" id="data-validar-codigo-conf" value="<?= $validarCodigo ? '1' : '0' ?>">
<input type="hidden" id="data-cubagem-carga-total" value="<?= round($cubagemCargaTotal, 4) ?>">
<input type="hidden" id="data-cubagem-veiculo" value="<?= round($cubagemVeiculo, 4) ?>">
```

- [ ] **Commit**

```bash
git add componentes/Missoes/Logistica/sinais/App/html/shared/components/v2/execucao_bipagem.php
git commit -m "feat(carregamento): badge do veículo exibe cubagem da carga vs capacidade"
```

---

### Task 4: JS — statsIncremental de cubagem e atualização de DOM

**Files:**
- Modify: `componentes/Missoes/Logistica/sinais/App/html/carregamento/item.js`

#### Passo 4a — inicializar `cubagemBipada` em `inicializarStatsFixos`

- [ ] **Localizar `inicializarStatsFixos` (~linha 932). O bloco atual termina em:**

```js
this.statsIncremental.concluidosCount = itens.filter(i => i.qtd_bipada >= i.qtd_total && i.qtd_total > 0).length;
this.statsIncremental.finalizadosCount = itens.filter(i => i.status === 'finalizado' || i.status === 'ocorrencia').length;
this.statsIncremental.totalXP = itens.reduce((acc, i) => acc + (i.xp_acumulado || 0), 0);
```

- [ ] **Adicionar linha logo após `totalXP`:**

```js
this.statsIncremental.concluidosCount = itens.filter(i => i.qtd_bipada >= i.qtd_total && i.qtd_total > 0).length;
this.statsIncremental.finalizadosCount = itens.filter(i => i.status === 'finalizado' || i.status === 'ocorrencia').length;
this.statsIncremental.totalXP = itens.reduce((acc, i) => acc + (i.xp_acumulado || 0), 0);
this.statsIncremental.cubagemBipada = itens.reduce((acc, i) => acc + (i.cubagem_unit || 0) * (i.qtd_bipada || 0), 0);
```

#### Passo 4b — atualizar `cubagemBipada` no bloco de delta incremental

- [ ] **Localizar o bloco de atualização incremental (~linha 2707):**

```js
this.statsIncremental.totalBipado += ultimoDelta;
this.statsIncremental.totalXP += (ultimoDelta > 0 ? 1 : -1);
```

- [ ] **Adicionar linha logo após `totalBipado`:**

```js
this.statsIncremental.totalBipado += ultimoDelta;
this.statsIncremental.cubagemBipada += ultimoDelta * (item.cubagem_unit || 0);
this.statsIncremental.totalXP += (ultimoDelta > 0 ? 1 : -1);
```

#### Passo 4c — atualizar DOM em `atualizarBadges`

- [ ] **Localizar `atualizarBadges` (~linha 4129). O método atual é:**

```js
atualizarBadges: function () {
    // [OTIMIZAÇÃO] O(1) usando statsIncremental
    const concluidos = this.statsIncremental.concluidosCount;
    const total = this.statsIncremental.totalItens;
    const pendentes = total - concluidos;
    const progresso = total > 0 ? (concluidos / total) * 100 : 0;

    $('#pendentes-count').text(pendentes);
    $('#concluidos-count').text(concluidos);
    $('#fab-badge').text(pendentes);
    $('#progresso-geral-fill').css('width', `${progresso}%`);
},
```

- [ ] **Substituir pelo novo método:**

```js
atualizarBadges: function () {
    // [OTIMIZAÇÃO] O(1) usando statsIncremental
    const concluidos = this.statsIncremental.concluidosCount;
    const total = this.statsIncremental.totalItens;
    const pendentes = total - concluidos;
    const progresso = total > 0 ? (concluidos / total) * 100 : 0;

    $('#pendentes-count').text(pendentes);
    $('#concluidos-count').text(concluidos);
    $('#fab-badge').text(pendentes);
    $('#progresso-geral-fill').css('width', `${progresso}%`);

    const cubagemVeiculo = parseFloat(document.getElementById('data-cubagem-veiculo')?.value || 0);
    const barFill = document.getElementById('veiculo-cubagem-bar-fill');
    const fracEl = document.getElementById('veiculo-cubagem-frac');
    if (barFill && fracEl && cubagemVeiculo > 0) {
        const bipada = this.statsIncremental.cubagemBipada;
        const pct = Math.min(100, (bipada / cubagemVeiculo) * 100);
        barFill.style.width = `${pct.toFixed(1)}%`;
        barFill.style.background = pct >= 95 ? 'var(--app-perigo)'
            : (pct >= 80 ? 'var(--app-aviso)' : 'var(--app-sucesso)');
        fracEl.textContent = `${bipada.toFixed(1).replace('.', ',')} / ${cubagemVeiculo.toFixed(1).replace('.', ',')}m³`;
    }
},
```

- [ ] **Commit**

```bash
git add componentes/Missoes/Logistica/sinais/App/html/carregamento/item.js
git commit -m "feat(carregamento): atualizar barra de cubagem em tempo real via statsIncremental"
```

---

## Critérios de verificação manual

Após as 4 tasks, abrir o app de carregamento com uma missão que tenha caminhão vinculado (`veiculo_snapshot` em `configuracoes`) e produtos com cubagem em `dados_adicionais['cubagem']`:

- [ ] Badge do veículo mostra a barra + fração abaixo da placa
- [ ] A fração inicial reflete o que já estava bipado antes de abrir a tela
- [ ] Ao bipar um item, a barra avança e a fração atualiza sem reload
- [ ] A barra fica amarela ao atingir 80% e vermelha ao atingir 95%
- [ ] Missão sem veículo vinculado: nenhum elemento novo aparece
- [ ] Tela de retorno (usa o mesmo `execucao_bipagem.php`): sem alteração visual
