# EdicaoRota — Notificação de Sessão Expirada para o Gestor

**Data:** 2026-05-26
**Status:** Aprovado para implementação

## Contexto

Quando a sessão do motorista expira (timer chega a zero), o front-end já exibe a tela "Sessão Expirada" para o motorista. O gestor, porém, não tem visibilidade imediata de que o motorista não editou a rota dentro do prazo.

O sistema já cria um "espelho" em `solicitacoes_globais` para edições expiradas via `EspelhoSolicitacaoGlobalAdapter`, mas isso ocorre de forma **lazy** (somente quando o gestor abre o painel Central). O card que aparece em Solicitações Logística não distingue visualmente expiração sem proposta de proposta submetida, e não exibe motorista, horário de envio nem horário de expiração.

## Objetivo

1. Criar o card em `solicitacoes_globais` **imediatamente** quando a sessão do motorista expira (trigger via ação `expirar_sessao`).
2. Exibir o card na tela de Solicitações Logística com visual distinto (laranja) e informações: nome da rota, motorista, horário de envio, horário de expiração.
3. Oferecer ao gestor um único botão "Dispensar" (sem Aprovar/Rejeitar) para reconhecer e remover o card.

## Arquivos Afetados

```
componentes/Missoes/Logistica/sinais/Edicaorota/
  Edicaorota.php                                     ← trigger imediato em expirar_sessao
  Sondas/EdicaoRotaSonda.php                         ← buscarRotaMeta() ampliado

componentes/Missoes/Logistica/sinais/Central/
  forms/missao_solicitacoes.php                      ← card variant para expiração
```

## Design

### Seção 1 — Fluxo de dados

#### 1a. `EdicaoRotaSonda::buscarRotaMeta()` — campos adicionais

O método já consulta `edicoes_rota er` com LEFT JOINs para romaneios e veículos. Acrescentar quatro campos ao SELECT:

```sql
er.identificador_motorista,
er.block_data          AS enviado_em,
er.expira_em,
(er.rota_proposta IS NULL) AS sem_proposta
```

O mapa de retorno passa a incluir (por edicao_id):

```php
[
    'nome'                   => ...,
    'cor'                    => ...,
    'placa'                  => ...,
    'identificador_motorista' => ...,   // string|null
    'enviado_em'             => ...,   // string timestamp|null  (block_data)
    'expira_em'              => ...,   // string timestamp|null
    'sem_proposta'           => ...,   // bool — true = expirou sem envio
]
```

#### 1b. `Edicaorota.php::expirar_sessao` — trigger imediato

Após limpar `$_SESSION['edicao_rota_' . $token]`, carregar a edição pelo token e criar o espelho:

```php
$sondaEd = new EdicaoRotaSonda();
$edicao  = $sondaEd->findByToken($token);
if ($edicao !== null) {
    EspelhoSolicitacaoGlobalAdapter::criar($edicao);
}
```

O adapter já é idempotente: se o espelho existir, a chamada é no-op. O lazy sync em `Central::Sinal_missaoSolicitacoes()` permanece inalterado como safety net para edge cases (motorista fechou a aba sem JS, perda de conexão, etc.).

#### 1c. `ListarSolicitacoesLaboratorio`

Sem alteração. Já repassa `rotaMeta` ao template; os novos campos fluem automaticamente.

---

### Seção 2 — Card de expiração (`missao_solicitacoes.php`)

No loop de solicitações, quando `$isEdicaoRota === true`:

```php
$semProposta = (bool) ($rotaMeta['sem_proposta'] ?? false);
```

**Se `$semProposta === true`** → renderizar variante de expiração:

- **Barra lateral:** `#f97316` (laranja)
- **Header:** `fa-clock` + "SESSÃO EXPIRADA" em laranja (em vez de cor de marca / "SOLICITAÇÃO GLOBAL")
- **Corpo — informações estruturadas** (em vez de `motivo` em texto livre):
  - Linha "Motorista:" com `identificador_motorista`
  - Linha "Enviado em:" com `block_data` formatado em PT-BR (ex: "seg 26/05 às 14:30")
  - Linha "Expirou em:" com `expira_em` formatado igual (ex: "seg 26/05 às 15:00")
- **Botões:** substituir os dois botões Rejeitar/Aprovar por um único botão "Dispensar" que chama `responderSolicitacao` com `aprovado=0` (marca solicitação como `rejeitada`, remove do painel)

**Se `$semProposta === false`** → markup existente inalterado (card de proposta do motorista).

---

## Fora do Escopo

- Notificação push/socket em tempo real ao gestor (o card aparece na próxima abertura do painel)
- Novo status "dispensada" na enum `SolicitacaoStatus` (usar `rejeitada` como cancelamento)
- Alteração do fluxo de aprovação/rejeição de propostas submetidas pelo motorista
- Atualização do campo `edicoes_rota.status` ao dispensar (a edição permanece com status `aguardando_envio` ou `em_andamento` — a solicitação funciona como aviso; o gestor pode tomar ação separada se necessário)

## Critérios de Aceitação

1. Quando o timer do motorista chega a zero, o AJAX `expirar_sessao` cria o espelho em `solicitacoes_globais` imediatamente
2. O card aparece em Solicitações Logística com barra laranja e header "SESSÃO EXPIRADA"
3. O card exibe: nome da rota, nome do motorista, horário de envio formatado, horário de expiração formatado
4. O botão "Dispensar" remove o card do painel (marca como rejeitada)
5. Cards de proposta submetida pelo motorista permanecem sem alteração visual ou funcional
6. Se `expira_em` for `null` ou `sem_proposta` for `false`, o card usa o layout atual (sem variante laranja)
7. Se o motorista fechar a aba sem JS, o lazy sync do Central ainda cria o espelho na próxima abertura do painel
