# EdicaoRota — Notificação de Sessão Expirada para o Gestor — 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:** Quando a sessão do motorista expira, criar imediatamente um card de aviso em Solicitações Logística com nome da rota, motorista, horário de envio e horário de expiração, com botão único "Dispensar".

**Architecture:** Três mudanças independentes e sequenciais: (1) `buscarRotaMeta()` na Sonda passa 4 campos extras ao template; (2) a ação `expirar_sessao` no controller chama `EspelhoSolicitacaoGlobalAdapter::criar()` imediatamente; (3) o template `missao_solicitacoes.php` renderiza variante visual laranja para cards sem proposta do motorista.

**Tech Stack:** PHP 8.x, PostgreSQL (via `_rawSql`), HTML/CSS inline no padrão Genezes

---

## Mapa de arquivos

| Arquivo | Ação |
|---|---|
| `componentes/Missoes/Logistica/sinais/Edicaorota/Sondas/EdicaoRotaSonda.php` | Modify — adicionar campos ao SELECT e ao mapa de retorno de `buscarRotaMeta()` |
| `componentes/Missoes/Logistica/sinais/Edicaorota/Edicaorota.php` | Modify — adicionar trigger `EspelhoSolicitacaoGlobalAdapter::criar()` na ação `expirar_sessao` |
| `componentes/Missoes/Logistica/sinais/Central/forms/missao_solicitacoes.php` | Modify — variante visual laranja + botão "Dispensar" para cards de expiração |

---

## Task 1: Estender `buscarRotaMeta()` com campos do motorista e timestamps

**Files:**
- Modify: `componentes/Missoes/Logistica/sinais/Edicaorota/Sondas/EdicaoRotaSonda.php:486-545`

- [ ] **Step 1: Ler o método atual**

```bash
sed -n '486,545p' componentes/Missoes/Logistica/sinais/Edicaorota/Sondas/EdicaoRotaSonda.php
```

Confirmar que o SELECT termina com `COALESCE(rc.codigo, v.placa) AS placa` antes do `FROM`.

- [ ] **Step 2: Adicionar os 4 campos ao SELECT**

Localizar a linha:

```
                COALESCE(rc.codigo,    v.placa)             AS placa
```

Substituir por:

```
                COALESCE(rc.codigo,    v.placa)             AS placa,
                er.identificador_motorista,
                er.block_data                               AS enviado_em,
                er.expira_em,
                (er.rota_proposta IS NULL)                  AS sem_proposta
```

- [ ] **Step 3: Adicionar os campos ao mapa de retorno**

Localizar o bloco do `$map[$eid]`:

```php
            $map[$eid] = [
                'nome'  => $row['nome_rota']  !== null ? (string) $row['nome_rota']  : null,
                'cor'   => $row['cor_rota']   !== null ? (string) $row['cor_rota']   : null,
                'placa' => $row['placa']      !== null ? (string) $row['placa']      : null,
            ];
```

Substituir por:

```php
            $map[$eid] = [
                'nome'                    => $row['nome_rota'] !== null ? (string) $row['nome_rota'] : null,
                'cor'                     => $row['cor_rota']  !== null ? (string) $row['cor_rota']  : null,
                'placa'                   => $row['placa']     !== null ? (string) $row['placa']     : null,
                'identificador_motorista' => isset($row['identificador_motorista']) && $row['identificador_motorista'] !== null ? (string) $row['identificador_motorista'] : null,
                'enviado_em'              => isset($row['enviado_em'])  && $row['enviado_em']  !== null ? (string) $row['enviado_em']  : null,
                'expira_em'               => isset($row['expira_em'])   && $row['expira_em']   !== null ? (string) $row['expira_em']   : null,
                'sem_proposta'            => !empty($row['sem_proposta']),
            ];
```

- [ ] **Step 4: Verificar sintaxe PHP**

```bash
php -l componentes/Missoes/Logistica/sinais/Edicaorota/Sondas/EdicaoRotaSonda.php
```

Esperado: `No syntax errors detected`

- [ ] **Step 5: Commit**

```bash
git add componentes/Missoes/Logistica/sinais/Edicaorota/Sondas/EdicaoRotaSonda.php
git commit -m "feat(edicaorota): add motorista, timestamps, sem_proposta to buscarRotaMeta"
```

---

## Task 2: Trigger imediato do espelho na ação `expirar_sessao`

**Files:**
- Modify: `componentes/Missoes/Logistica/sinais/Edicaorota/Edicaorota.php:147-156`

- [ ] **Step 1: Ler o bloco `expirar_sessao` atual**

```bash
sed -n '140,165p' componentes/Missoes/Logistica/sinais/Edicaorota/Edicaorota.php
```

Confirmar que o bloco tem esta estrutura:
```php
} elseif ($acao === 'expirar_sessao') {
    if (session_status() === PHP_SESSION_NONE) { session_start(); }
    if (!empty($_SESSION['edicao_rota_' . $token])) {
        unset($_SESSION['edicao_rota_' . $token]);
    }
    while (ob_get_level() > 0) { ob_end_clean(); }
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode(['ok' => true]);
    exit;
}
```

- [ ] **Step 2: Adicionar o trigger do espelho após o unset**

Localizar o bloco exato:

```php
            } elseif ($acao === 'expirar_sessao') {
                if (session_status() === PHP_SESSION_NONE) { session_start(); }
                if (!empty($_SESSION['edicao_rota_' . $token])) {
                    unset($_SESSION['edicao_rota_' . $token]);
                }
                while (ob_get_level() > 0) { ob_end_clean(); }
                header('Content-Type: application/json; charset=utf-8');
                echo json_encode(['ok' => true]);
                exit;
```

Substituir por:

```php
            } elseif ($acao === 'expirar_sessao') {
                if (session_status() === PHP_SESSION_NONE) { session_start(); }
                if (!empty($_SESSION['edicao_rota_' . $token])) {
                    unset($_SESSION['edicao_rota_' . $token]);
                }
                $edicaoExp = (new EdicaoRotaSonda())->findByToken($token);
                if ($edicaoExp !== null) {
                    \GalaxiaMissoes\Logistica\sinais\Edicaorota\Adapters\Solicitacoes\EspelhoSolicitacaoGlobalAdapter::criar($edicaoExp);
                }
                while (ob_get_level() > 0) { ob_end_clean(); }
                header('Content-Type: application/json; charset=utf-8');
                echo json_encode(['ok' => true]);
                exit;
```

Nota: `EdicaoRotaSonda` já tem `use` no arquivo (linha 34). `EspelhoSolicitacaoGlobalAdapter` é referenciado pelo namespace completo (best-effort, idempotente; não deve lançar).

- [ ] **Step 3: Verificar sintaxe PHP**

```bash
php -l componentes/Missoes/Logistica/sinais/Edicaorota/Edicaorota.php
```

Esperado: `No syntax errors detected`

- [ ] **Step 4: Commit**

```bash
git add componentes/Missoes/Logistica/sinais/Edicaorota/Edicaorota.php
git commit -m "feat(edicaorota): trigger espelho solicitacao imediatamente ao expirar sessao"
```

---

## Task 3: Card de expiração no template `missao_solicitacoes.php`

Esta tarefa tem três sub-modificações independentes dentro do mesmo arquivo, aplicadas em sequência. Leia o arquivo inteiro antes de começar.

**Files:**
- Modify: `componentes/Missoes/Logistica/sinais/Central/forms/missao_solicitacoes.php:15-30` (vars block)
- Modify: `componentes/Missoes/Logistica/sinais/Central/forms/missao_solicitacoes.php` (header section)
- Modify: `componentes/Missoes/Logistica/sinais/Central/forms/missao_solicitacoes.php` (motivo block)
- Modify: `componentes/Missoes/Logistica/sinais/Central/forms/missao_solicitacoes.php` (buttons block)

### 3a — Variáveis no bloco `<?php` do foreach

- [ ] **Step 1: Localizar e substituir o bloco de variáveis do card**

Localizar (linhas ~15-24 do arquivo, dentro do `foreach`):

```php
              $tipoRef    = strtolower((string) ($s->referencia_tipo ?? ''));
              $isEdicaoRota = $tipoRef === 'edicao_rota';
              $rotaMeta   = $s->rotaMeta ?? null;
              $rotaNome   = $rotaMeta['nome']  ?? null;
              $rotaCor    = $rotaMeta['cor']   ?? null;
              $rotaPlaca  = $rotaMeta['placa'] ?? null;
              // Cor da barra lateral: usa cor da rota quando disponível,
              // senão cai pro tom de marca padrão (global = roxo).
              $corLateral = $rotaCor ?: (!empty($s->is_global) ? '#8b5cf6' : 'var(--g-cor-marca-500)');
```

Substituir por:

```php
              $tipoRef      = strtolower((string) ($s->referencia_tipo ?? ''));
              $isEdicaoRota = $tipoRef === 'edicao_rota';
              $rotaMeta     = $s->rotaMeta ?? null;
              $rotaNome     = $rotaMeta['nome']  ?? null;
              $rotaCor      = $rotaMeta['cor']   ?? null;
              $rotaPlaca    = $rotaMeta['placa'] ?? null;
              $semProposta  = $isEdicaoRota && !empty($rotaMeta['sem_proposta']);
              // Expiração: barra laranja; proposta/outros: cor da rota ou tom de marca.
              $corLateral   = $semProposta ? '#f97316' : ($rotaCor ?: (!empty($s->is_global) ? '#8b5cf6' : 'var(--g-cor-marca-500)'));
              $fmtHoraPtBr  = static function (?string $ts): ?string {
                  if ($ts === null) return null;
                  $t = strtotime($ts);
                  if ($t === false) return null;
                  $dias = ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'];
                  return $dias[(int) date('w', $t)] . ' ' . date('d/m', $t) . ' às ' . date('H:i', $t);
              };
```

### 3b — Header do card (ícone, cor e texto do tipo)

- [ ] **Step 2: Localizar o div do header**

Localizar este bloco exato (dentro da `<div class="d-flex justify-content-between ...`):

```php
                        <div style="font-size: 1.05rem; color: <?= !empty($s->is_global) ? '#8b5cf6' : 'var(--g-texto-marca)' ?>; font-weight: 800; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 4px; display: flex; align-items: center; gap: 0.5rem;">
                            <i class="fas <?= !empty($s->is_global) ? 'fa-globe-americas' : 'fa-satellite' ?>"></i>
                            <?php
                            $tipoLabel = htmlspecialchars($s->tipo);
                            try {
                                $tipoEnum = \GalaxiaConstelacoes\Logistica\MissaoLogistica\Enums\TipoSolicitacao::tryFrom($s->tipo);
                                if ($tipoEnum) $tipoLabel = $tipoEnum->label();
                            } catch (\Exception $e) {
                            }
                            ?>
                            SOLICITAÇÃO <?= !empty($s->is_global) ? 'GLOBAL' : $tipoLabel ?>
                        </div>
```

Substituir por:

```php
                        <div style="font-size: 1.05rem; color: <?= $semProposta ? '#f97316' : (!empty($s->is_global) ? '#8b5cf6' : 'var(--g-texto-marca)') ?>; font-weight: 800; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 4px; display: flex; align-items: center; gap: 0.5rem;">
                            <i class="fas <?= $semProposta ? 'fa-clock' : (!empty($s->is_global) ? 'fa-globe-americas' : 'fa-satellite') ?>"></i>
                            <?php if (!$semProposta):
                                $tipoLabel = htmlspecialchars($s->tipo);
                                try {
                                    $tipoEnum = \GalaxiaConstelacoes\Logistica\MissaoLogistica\Enums\TipoSolicitacao::tryFrom($s->tipo);
                                    if ($tipoEnum) $tipoLabel = $tipoEnum->label();
                                } catch (\Exception $e) {
                                }
                            endif; ?>
                            <?= $semProposta ? 'SESSÃO EXPIRADA' : ('SOLICITAÇÃO ' . (!empty($s->is_global) ? 'GLOBAL' : $tipoLabel)) ?>
                        </div>
```

### 3c — Corpo do card (motivo-wrapper)

- [ ] **Step 3: Localizar o bloco `motivo-wrapper` para edicao_rota global**

Dentro da `<div class="motivo-wrapper mb-3" ...>`, localizar o trecho que trata `is_global + isEdicaoRota`:

```php
                    <?php if (!empty($s->is_global)): ?>
                        <?php if ($isEdicaoRota): ?>
                            <?php // Para edicao_rota o nome+cor da rota já está no header do card. ?>
                            <?php if (!empty($s->motivo)): ?>
                                "<?= htmlspecialchars($s->motivo) ?>"
                            <?php else: ?>
                                <span style="opacity: 0.6;">Sem motivo informado.</span>
                            <?php endif; ?>
```

Substituir por:

```php
                    <?php if (!empty($s->is_global)): ?>
                        <?php if ($isEdicaoRota): ?>
                            <?php if ($semProposta): ?>
                                <div style="display: flex; flex-direction: column; gap: 0.55rem; font-style: normal;">
                                    <?php $motNome = $rotaMeta['identificador_motorista'] ?? null; if ($motNome): ?>
                                    <div style="display: flex; gap: 0.5rem; align-items: baseline;">
                                        <span style="font-size: 0.65rem; text-transform: uppercase; font-weight: 800; color: #fff; letter-spacing: 0.5px; min-width: 88px;">Motorista:</span>
                                        <span style="color: #e2e8f0; font-weight: 600;"><?= htmlspecialchars($motNome) ?></span>
                                    </div>
                                    <?php endif; ?>
                                    <?php $envFmt = $fmtHoraPtBr($rotaMeta['enviado_em'] ?? null); if ($envFmt): ?>
                                    <div style="display: flex; gap: 0.5rem; align-items: baseline;">
                                        <span style="font-size: 0.65rem; text-transform: uppercase; font-weight: 800; color: #fff; letter-spacing: 0.5px; min-width: 88px;">Enviado em:</span>
                                        <span style="color: #e2e8f0;"><?= htmlspecialchars($envFmt) ?></span>
                                    </div>
                                    <?php endif; ?>
                                    <?php $expFmt = $fmtHoraPtBr($rotaMeta['expira_em'] ?? null); if ($expFmt): ?>
                                    <div style="display: flex; gap: 0.5rem; align-items: baseline;">
                                        <span style="font-size: 0.65rem; text-transform: uppercase; font-weight: 800; color: #f97316; letter-spacing: 0.5px; min-width: 88px;">Expirou em:</span>
                                        <span style="color: #f97316; font-weight: 600;"><?= htmlspecialchars($expFmt) ?></span>
                                    </div>
                                    <?php endif; ?>
                                </div>
                            <?php elseif (!empty($s->motivo)): ?>
                                "<?= htmlspecialchars($s->motivo) ?>"
                            <?php else: ?>
                                <span style="opacity: 0.6;">Sem motivo informado.</span>
                            <?php endif; ?>
```

Nota: O `endif` do `if ($isEdicaoRota)` que fecha o bloco original já está no arquivo e não deve ser duplicado.

### 3d — Também esconder o label "Transcrição do Motivo:" para cards de expiração

- [ ] **Step 4: Localizar o label da seção de motivo**

Localizar:

```php
                    <div style="font-size: 0.65rem; color: #fff; text-transform: uppercase; font-weight: 800; margin-bottom: 8px; letter-spacing: 0.5px;">Transcrição do Motivo:</div>
```

Substituir por:

```php
                    <div style="font-size: 0.65rem; color: #fff; text-transform: uppercase; font-weight: 800; margin-bottom: 8px; letter-spacing: 0.5px;"><?= $semProposta ? 'Informações da Sessão:' : 'Transcrição do Motivo:' ?></div>
```

### 3e — Botões: substituir Rejeitar/Aprovar por Dispensar para cards de expiração

- [ ] **Step 5: Localizar o bloco de botões**

Localizar:

```php
                <div class="row g-2">
                    <div class="col-6">
                        <button class="g-btn-rejeitar w-100 py-2 fw-bold"
                            data-confirm-msg="Deseja REJEITAR esta solicitação?"
                            data-prompt-msg="Motivo da rejeição (Opcional):"
                            data-motive-required="false"
                            data-galaxia-action="<?= $galaxia->setConstelacao('Logistica')
                                                        ->setEstrela('MissaoLogistica')
                                                        ->setSatelite('MissoesSolicitacoes')
                                                        ->setOpcao('responderSolicitacao')
                                                        ->acaoElemento('solicitacao_id=' . $s->id . '&aprovado=0&resposta={motivo}' . (!empty($s->is_global) ? '&is_global=1' : '')); ?>">
                            <i class="fas fa-times-circle me-1"></i> Rejeitar
                        </button>
                    </div>
                    <div class="col-6">
                        <button class="g-btn-aprovar w-100 py-2 fw-bold"
                            data-confirm-msg="Deseja APROVAR esta solicitação? A ação será executada imediatamente."
                            data-prompt-msg="Observação (Opcional):"
                            data-motive-required="false"
                            data-galaxia-action="<?= $galaxia->setConstelacao('Logistica')
                                                        ->setEstrela('MissaoLogistica')
                                                        ->setSatelite('MissoesSolicitacoes')
                                                        ->setOpcao('responderSolicitacao')
                                                        ->acaoElemento('solicitacao_id=' . $s->id . '&aprovado=1&resposta={motivo}' . (!empty($s->is_global) ? '&is_global=1' : '')); ?>">
                            <i class="fas fa-check-double me-1"></i> Aprovar
                        </button>
                    </div>
                </div>
```

Substituir por:

```php
                <?php if ($semProposta): ?>
                <div class="row g-2">
                    <div class="col-12">
                        <button class="g-btn-rejeitar w-100 py-2 fw-bold"
                            data-confirm-msg="Dispensar este aviso de sessão expirada?"
                            data-prompt-msg=""
                            data-motive-required="false"
                            data-galaxia-action="<?= $galaxia->setConstelacao('Logistica')
                                                        ->setEstrela('MissaoLogistica')
                                                        ->setSatelite('MissoesSolicitacoes')
                                                        ->setOpcao('responderSolicitacao')
                                                        ->acaoElemento('solicitacao_id=' . $s->id . '&aprovado=0&resposta=dispensado&is_global=1'); ?>">
                            <i class="fas fa-check me-1"></i> Dispensar
                        </button>
                    </div>
                </div>
                <?php else: ?>
                <div class="row g-2">
                    <div class="col-6">
                        <button class="g-btn-rejeitar w-100 py-2 fw-bold"
                            data-confirm-msg="Deseja REJEITAR esta solicitação?"
                            data-prompt-msg="Motivo da rejeição (Opcional):"
                            data-motive-required="false"
                            data-galaxia-action="<?= $galaxia->setConstelacao('Logistica')
                                                        ->setEstrela('MissaoLogistica')
                                                        ->setSatelite('MissoesSolicitacoes')
                                                        ->setOpcao('responderSolicitacao')
                                                        ->acaoElemento('solicitacao_id=' . $s->id . '&aprovado=0&resposta={motivo}' . (!empty($s->is_global) ? '&is_global=1' : '')); ?>">
                            <i class="fas fa-times-circle me-1"></i> Rejeitar
                        </button>
                    </div>
                    <div class="col-6">
                        <button class="g-btn-aprovar w-100 py-2 fw-bold"
                            data-confirm-msg="Deseja APROVAR esta solicitação? A ação será executada imediatamente."
                            data-prompt-msg="Observação (Opcional):"
                            data-motive-required="false"
                            data-galaxia-action="<?= $galaxia->setConstelacao('Logistica')
                                                        ->setEstrela('MissaoLogistica')
                                                        ->setSatelite('MissoesSolicitacoes')
                                                        ->setOpcao('responderSolicitacao')
                                                        ->acaoElemento('solicitacao_id=' . $s->id . '&aprovado=1&resposta={motivo}' . (!empty($s->is_global) ? '&is_global=1' : '')); ?>">
                            <i class="fas fa-check-double me-1"></i> Aprovar
                        </button>
                    </div>
                </div>
                <?php endif; ?>
```

- [ ] **Step 6: Verificar sintaxe PHP**

```bash
php -l componentes/Missoes/Logistica/sinais/Central/forms/missao_solicitacoes.php
```

Esperado: `No syntax errors detected`

- [ ] **Step 7: Commit**

```bash
git add componentes/Missoes/Logistica/sinais/Central/forms/missao_solicitacoes.php
git commit -m "feat(solicitacoes): add expiry card variant with orange style and Dispensar button"
```
