# Transmissões — Gaps, Dúvidas e Mapa v5 → v6

> Versão analisada: `v6`. O módulo é **novo na v6**.

## 1. Bugs e defeitos encontrados

| # | Severidade | Descrição | Evidência | Efeito para o município |
|---|---|---|---|---|
| B1 | **Alta** | O ID cru do YouTube, aceito e anunciado no formulário, **não funciona**: é gravado como `/YbJOTdZBX1g` e o widget exibe "Transmissão não disponível". | `v6/app/Helpers/functions.php:672-690` (`normalizeVideoLink`) + `v6/app/Http/Controllers/Prefecture/ContentController.php:595-608` (`sanitizeLink`) + parser nos Blades de tema | O operador cola o ID, o CMS aceita, e o público não vê nada. |
| B2 | **Alta** | O toggle de status **não** desativa as demais transmissões: o filtro `->where('order', $content->order)` usa `order = NULL` (o formulário não tem o campo), e em PostgreSQL `= NULL` nunca casa. | `v6/app/Http/Controllers/Prefecture/ContentController.php:1571-1573` | Podem coexistir duas transmissões ativas; o portal exibe uma delas arbitrariamente. |
| B3 | **Média** | O `update` desativa os ativos da categoria **incondicionalmente**, sem verificar o novo `status`. | `v6/app/Http/Controllers/Prefecture/ContentController.php:1000-1002` (comparar com `anuncios` e `super-banners`, que verificam `status == 1`) | Editar uma transmissão desativada **derruba a transmissão que estava no ar**. |
| B4 | **Média** | O widget consome o **primeiro** `status = true` sem `orderBy`; a ordem depende do plano de execução do banco. | `v6/resources/views/portal/layouts/modelo1/layout/sidebar/transmissao.blade.php` (e os outros 5) | Com B2, o vídeo exibido pode trocar de forma inexplicável entre requisições. |
| B5 | **Média** | `$transmissao->linksUnique->link` é acessado sem checar nulo (`linksUnique` é `hasOne`). | idem | Conteúdo sem `Link` provoca erro fatal na página (portal inteiro cai). |
| B6 | **Baixa** | Código morto: o modelo1 consulta a transmissão em `banners.blade.php` e nunca a renderiza. | `v6/resources/views/portal/layouts/modelo1/layout/sidebar/banners.blade.php:13-15` | Query wasted por página no tema1. |
| B7 | **Baixa** | A view SQL usa subselect escalar sobre `category_contents.slug`; duplicar o slug quebra a view. | `v6/database/migrations/2024_10_01_180234_create_view_transmissoes.php:30-32` | Falha em runtime do DataTable do CMS. |
| B8 | **Baixa** | `isBlank` é validado (`ContentRequest`) mas não existe no formulário. | `v6/app/Http/Requests/ContentRequest.php:145` vs `v6/resources/views/prefecture/contents/transmissoes/form.blade.php` | Campo morto; o `Link` sempre recebe `isBlank = 0`. |
| B9 | **Baixa** | O `update` de conteúdo reescreve o `slug` do registro para `pub{id}-{slug}` (a checagem de unicidade sempre encontra o próprio registro). | `v6/app/Http/Controllers/Prefecture/ContentController.php:975-995` | Slugs artificiais em `contents`. |
| B10 | **Baixa** | O `iframe` não tem `title`/`aria-label`; não há texto alternativo nem transcrição. | Blades de tema | Barreira de acessibilidade. |
| B11 | **Informativo** | O cache `Cache::rememberForever('modulos')` não é invalidado ao alternar o módulo em `/cms/modulos`. | `v6/app/Providers/ModulosServiceProvider.php:22-24` vs `v6/app/Http/Controllers/Admin/ModulosController.php` | A mudança de status do módulo só vale após limpar o cache. |

## 2. Funcionalidades incompletas ou mortas

| Item | Situação |
|---|---|
| Página pública de transmissões | **Não existe** — não há listagem, página de sessão Arquivada ou histórico. |
| Agendamento / início-fim da sessão | Inexistente. Não há `starts_at`, `ends_at` nem conceito de "no ar". |
| Métrica de audiência | Inexistente. `menu_orders.clicks` conta acessos ao CMS, não visualizações. |
| Integração com o módulo `sessoes` | Inexistente: a transmissão não se vincula a uma sessão, ata ou pauta. |
| Integração com `tv-camara` | Inexistente, apesar do rótulo "Video (Youtube)". |
| Suporte a múltiplos vídeos / replay | Inexistente: o modelo é "um vídeo ativo". |
| Regras de validação de URL | Inexistente: o `ContentRequest` só checa `required|max:200`. |
| Uso do helper `embedVideo()` | **Não utilizado** pelo widget, embora exista e seja mais robusto (`v6/app/Helpers/functions.php:607`). |
| Auditoria do toggle de status | Não há `activity()->log()` no `setStatus` (apenas no `store`/`update` de conteúdo e de link). |
| Exportação da listagem | Existe via Yajra (Excel/CSV/PDF) herdada de `runDatatables`, mas o export **não inclui o link do vídeo** (a view SQL não o expõe). |

## 3. Itens da v5 que foram removidos na v6 (ou o contrário)

Nada. O módulo **não existia na v5** — ver a seção 5.

## 4. Riscos de dados (migração de schema)

| Risco | Detalhe | Mitigação na v7 |
|---|---|---|
| Tabela `contents` compartilhada | A transmissão é um `contents` qualquer; nada impede que um administrador mude a categoria do registro e o faça sumir do módulo. | Tabela dedicada `transmissions`. |
| Subselect escalar na view | Duplicação de `category_contents.slug` derruba a view e o DataTable. | `UNIQUE (slug)` em `category_contents` + tabela dedicada. |
| `links` com cardinalidade 1:N | A relação `linksUnique()` é `hasOne`, mas a tabela permite vários links por conteúdo; o `->first()` implícito na tela de edição pode não ser o mesmo exibido no portal. | `video_url` diretamente na tabela `transmissions`. |
| `contents.order` NULL | Origem do bug B2 e da inconsistência de ordenação. | `starts_at`/`ends_at` explícitos e ordenação determinística. |
| `status` booleano sem semântica | "Ativado" hoje significa "é a transmissão no ar"; não há como agendar. | `status` (publicado) + `is_live` + `starts_at`/`ends_at`. |
| `softDeletes` em `contents` e `links` | `Link` **não** usa `SoftDeletes` (trait comentado em `v6/app/Http/Model/Prefecture/Link.php`), enquanto a migration cria `deleted_at` — linhas órfãs ficam na tabela. | `SoftDeletes` consistente. |

## 5. Mapa v5 → v6

### 5.1 (a) O módulo não existe na v5

Verificação executada em `/home/one/projects/maxima/v7/v5`:

```bash
grep -riE "transmiss" v5/routes/ v5/app/ v5/resources/ v5/database/
```

**Resultado: nenhuma ocorrência** relacionada a transmissões (os únicos achados
em `v5/` são falsos positivos em código de terceiros:
`v5/public/assets/plugins/vlibras/vlibras-plugin.js` e
`v5/vendor/laravel/framework/src/Illuminate/Mail/Transport/SparkPostTransport.php`).

| Artefato | v5 | v6 |
|---|---|---|
| Rota no portal | ausente | ausente (o módulo não tem rota pública) |
| Rota no CMS | ausente | `conteudos/transmissoes`, `conteudo.store/update`, `transmissoes.status`, `datatable.transmissoes` |
| Migration de view SQL | ausente | `v6/database/migrations/2024_10_01_180234_create_view_transmissoes.php` |
| Migration de tabela própria | ausente | ausente (reaproveita `contents`/`links`) |
| Model | ausente | `App\Prefecture\Content` / `CategoryContent` / `Link` (genéricos) |
| Controller dedicado | ausente | ausente (CRUD genérico `Prefecture\ContentController`) |
| View de portal | ausente | 6 Blades de tema (widget na lateral) |
| Views de CMS | ausentes | `v6/resources/views/prefecture/contents/transmissoes/{index,create,edit,form}.blade.php` |
| JS | ausente | `v6/public/assets/js/custom/contents/transmissoes/datatables.js` |
| Seed | ausente | `CategoryContentTableSeeder` (id 36), `MenuOrderSeeder`, `ResourcesTableSeeder`, migration de `modulos` (id 44) |

### 5.2 (b) Lacuna funcional reprezentada na v5

Na v5 o portal **não oferecia nenhuma transmissão ao vivo das sessões da Câmara**:

- Não havia player de vídeo de sessão em nenhum tema.
- O cidadão que quisesse acompanhar a sessão em vídeo tinha de sair do portal e
  procurar o canal/stream por conta própria.
- Não havia qualquer instrumento administrativo para publicar um vídeo de
  sessão, agendar o início da transmissão ou encerrá-la.
- Não havia, portanto, canal oficial de acompanhamento das deliberações em
  tempo real — uma lacuna relevante de transparência e de acesso à information.

Em resumo: a v5 era um portal de **informação estática**; a v6 passa a oferecer
**transmissão ao vivo** como funcionalidade de primeira classe.

### 5.3 (c) O que precisa ser decidido / replicado na v7

| Decisão | Recomendação | Responsável |
|---|---|---|
| Manter o player **na lateral** ou mover para uma página própria "Transmissão"? | Manter na lateral **e** criar página própria com histórico/replay. | Produto/Comunicação |
| O ID puro do YouTube deve funcionar? | **Sim** — implementar no `VideoUrlResolver` (B1). | Engenharia |
| Ativar pelo toggle deve desativar as demais? | **Sim** — mover a regra para o modelo/serviço e corrigir o filtro por `order` (B2). | Engenharia |
| Edição de registro desativado deve derrubar a transmissão no ar? | **Não** — desativar apenas o registro editado (B3). | Engenharia |
| Adicionar agenda (`starts_at`/`ends_at`) e status "no ar" automático? | **Sim** — habilita "ao vivo" e agendamento de sessão. | Produto |
| Vincular a transmissão a uma sessão (`sessoes`)? | Recomendável — hoje não há qualquer vínculo. | Produto |
| Medir audiência (visualizações/cliques no player)? | Recomendável — hoje a telemetria só mede o CMS. | Produto |
| Manter o `embedVideo()` existente ou reescrever? | Reescrever como serviço único e usá-lo também em outros módulos que hoje duplicam o parser. | Engenharia |
| Suportar outros provedores além de YouTube? | O helper `embedVideo()` já cobre Vimeo/Facebook; decidir se o CMS deve limitar a YouTube. | Produto |
| Desligar o módulo por padrão na v7? | Manter o padrão desligado e a chave de feature flag por município. | Operação |

## 6. Perguntas em aberto para o negócio

1. A transmissão deve ser **embutida** (YouTube) ou o município quer **streaming
   próprio** (HLS/RTMP) para não depender de plataforma de terceiros?
2. Quem é autorizado a **encerrar** a transmissão no ar — apenas o gestor do
   CMS ou também a Câmara?
3. Deve haver **gravação/arquivo** das sessões disponíveis após o fim da
   transmissão?
4. Em caso de transmissão embedada de **terceiros** (Facebook, Vimeo), há
   exigência de que o conteúdo esteja **publicamente acessível**?
5. A faixa "Transmissão" deve exibir **"AO VIVO"** de forma textual (acessibilidade)
   em vez de depender apenas da animação `piscar`?
6. O módulo deve ser exibido **somente nos dias de sessão**?
7. Qual o **número máximo** de transmissões que devem ser mantidas no histórico?
