# Transmissões — Rotas e Controllers

> Versão analisada: `v6`. Módulo **novo na v6**.

## 1. Portal (público): **nenhuma rota própria**

O módulo **não expõe nenhuma rota pública**. Não existe
`/transmissoes`, nem `/transmissao`, nem rota em `v6/routes/web.php`.

```
$ grep -n "transmiss" v6/routes/web.php
(sem resultados)
```

A transmissão aparece como **widget de layout**,renderizado dentro das páginas
que já existem (home, notícias, sessoes, transparência, etc.), todas dentro do
grupo `Route::middleware(['check.site.manutention'])` (`v6/routes/web.php:8`).

| Item | Valor |
|---|---|
| Rotas públicas do módulo | **nenhuma** |
| Controller público do módulo | **nenhum** |
| Ponto de entrada | `@include` do widget dentro das views de sidebar/banners de cada tema |
| Filtro de exibição | `$modulos['transmissoes']` (flag em `modulos.status`) |

A rota mais próxima, e que não pertence a este módulo, é a listagem de sessões:
`v6/routes/web.php:113-114` (`Portal\SessionsController@index` / `@show`).

## 2. CMS (`/cms`)

Todas as rotas abaixo vivem no grupo
`Route::group(['prefix' => 'cms', 'before' => 'csrf'], ...)`
(`v6/routes/basepro.php:35`) e exigem o middleware `check.logged`.

### 2.1 Rotas

| Método | URI | Nome | Controller@ação | Origem |
|---|---|---|---|---|
| GET | `/cms/conteudos/transmissoes` | `listar-conteudo` | `Prefecture\ContentController@genericIndex` | `v6/routes/basepro.php:235` |
| GET | `/cms/conteudos/cadastrar/transmissoes` | `cadastrar-conteudo` | `Prefecture\ContentController@genericCreate` | `v6/routes/basepro.php:236` |
| GET | `/cms/conteudos/editar/transmissoes/{id}` | `editar-conteudo` | `Prefecture\ContentController@genericEdit` | `v6/routes/basepro.php:237` |
| POST | `/cms/conteudo` | `conteudo.store` | `Prefecture\ContentController@store` | `v6/routes/basepro.php:174` (`Route::resource('conteudo', ...)`) |
| PUT | `/cms/conteudo/{id}` | `conteudo.update` | `Prefecture\ContentController@update` | idem |
| DELETE | `/cms/conteudo/{id}` | `conteudo.destroy` | `Prefecture\ContentController@destroy` | idem |
| POST | `/cms/transmissoes/{id}/status` | `transmissoes.status` | `Prefecture\ContentController@setStatus` | `v6/routes/basepro.php:55` |
| GET | `/cms/datatable/transmissoes` | `datatable.transmissoes` | `Prefecture\DatatableController@get_transmissoes` | `v6/routes/basepro.php:402` |
| GET | `/cms/modulos` | `modulos.index` | `Admin\ModulosController@index` | `v6/routes/basepro.php:40` (liga/desliga o módulo) |
| POST | `/cms/modulos` | `modulos.update` | `Admin\ModulosController@update` | `v6/routes/basepro.php:41` |

Rotas genéricas de apoio:

| Método | URI | Nome | Controller@ação |
|---|---|---|---|
| POST | `/cms/ajax-upload-content-files` | `ajax.upload.content.files` | `Prefecture\ContentController@ajaxUploadContentFiles` |
| POST | `/cms/conteudo/deleteAll` | `ajax.generic.content.deleteAll` | `Prefecture\ContentController@genericDeleteAll` |

### 2.2 Observação sobre `transmissoes.status`

A rota declara `{id}`, mas `setStatus()` lê o identificador do **corpo** da
requisição:

```php
    public function setStatus(Request $request): RedirectResponse
    {
        $content = $this->table->findOrFail($request->contents_id);
```

O JS envia `POST {contents_id: value.id}`
(`v6/public/assets/js/custom/utilities/table.js:514`). Portanto o `{id}` da URL
é **decorativo** e o corpo da requisição é a fonte real do registro.

## 3. Controllers

### 3.1 `Prefecture\ContentController` — ganchos por slug

`v6/app/Http/Controllers/Prefecture/ContentController.php`

| Linha | Contexto | Código / efeito |
|---|---|---|
| 110 | `genericIndex` | `if ($slug === 'transmissoes') return $this->returnIndex('Transmissões', $slug);` |
| 256-260 | `genericGenerate` | `returnData('Transmissões', $slug, null, ['categoryContent' => $this->categoryContent->where('slug','transmissoes')->whereNull('category_contents_id')->first()], $id);` |
| 678-680 | `store` | `if ($slug === 'transmissoes' && $request->get('status') == 1) { Content::where('category_contents_id', $request->get('category_contents_id'))->where('status', true)->update(['status' => false]); }` |
| 1000-1002 | `update` | `if ($slug === 'transmissoes') { $this->table->where('category_contents_id', ...)->where('status', true)->update(['status' => false]); }` |
| 1571-1573 | `setStatus` | `if ($slug === 'transmissoes') { $this->table->where('category_contents_id', $content->category_contents_id)->where('order', $content->order)->where('status', true)->update(['status' => false]); }` |

`returnIndex()` (linha 121-126) chama `menuClicks($slug)` e renderiza
`prefecture.contents.transmissoes.index`. `returnData()` (linha 275-312) chama
`menuClicks($slug)`, monta `$categoryContent` e renderiza
`prefecture.contents.transmissoes.create` ou `.edit`.

### 3.2 `Prefecture\DatatableController@get_transmissoes`

`v6/app/Http/Controllers/Prefecture/DatatableController.php:51-55`

```php
    public function get_transmissoes()
    {
        $transmissoes = DB::table('transmissoes')->get();
        return DataTables::of($this->struct_views($transmissoes, 'transmissoes'))->make(true);
    }
```

`struct_views()` (linhas 504-519) anexa as flags `edit`, `delete`, `active` e
`inactivate` conforme `has_access()`.

### 3.3 `Admin\ModulosController`

`v6/app/Http/Controllers/Admin/ModulosController.php` — `index()` lista os
módulos e `update()` alterna `modulos.status`. É a porta de entrada para
ligar o módulo (padrão: desligado).

## 4. Form Request e validação

`v6/app/Http/Requests/ContentRequest.php`, `case 'transmissoes'` (linhas 139-147):

```php
            case 'transmissoes' :

                $rules = [
                    "title" => "required|max:120",
                    "category_contents_id" => "required|numeric",
                    "link" => "required|max:200",
                    "status" => "boolean",
                    "isBlank" => "boolean",
                ];

                break;
```

| Campo | Regra | Efeito observável |
|---|---|---|
| `title` | `required|max:120` | Título obrigatório, truncado em 120 caracteres pelo HTML e validado no servidor. |
| `category_contents_id` | `required|numeric` | hidden no formulário, preenchido com o id da categoria (36). |
| `link` | `required|max:200` | Link do vídeo obrigatório, até 200 caracteres. |
| `status` | `boolean` | Checkbox "Ativar/Desativar". |
| `isBlank` | `boolean` | Está nas regras mas **não há campo** correspondente no formulário do módulo. |

**Não há validação de formato de URL nem de domínio**: qualquer texto é aceito
como "link"; a falha só aparece no front (mensagem "Transmissão não disponível").

## 5. Permissões

| Permissão | Onde é exigida | Efeito |
|---|---|---|
| `transmissoes.create` | `v6/resources/views/prefecture/contents/transmissoes/index.blade.php` | Exibe o botão "Cadastrar Transmissão". |
| `transmissoes.delete` | idem (bloco de exclusão em lote) | Exibe o checkbox mestre e o botão de exclusão em lote. |
| `transmissoes.edit` | `struct_views()` (`DatatableController.php:506`) | Exibe o botão "editar" na linha da DataTable. |
| `transmissoes.delete` | `struct_views()` (linha 511) | Exibe o botão "excluir" na linha da DataTable. |
| `transmissoes.restore` / `transmissoes.inactivate` | `struct_views()` (linhas 514-515) | Controlam o botão de status (toggle). |
| `check.route.permission` | `v6/app/Http/Middleware/CheckRoutePermission.php` | Redireciona com aviso quando o usuário não tem acesso à rota. |

O helper `has_access()` (`v6/app/Library/Helper/appHelpers.php:22`) delega a
`Sentinel::check()->hasAccess()` / `hasAnyAccess()`; `super-admin` passa
incondicionalmente.

O `resources` de permissão é criado por
`v6/database/seeds/ResourcesTableSeeder.php:549-556` com `modulo_id = 44`.

## 6. Telemetria

`menuClicks('transmissoes')` é chamado em `returnIndex()`
(`ContentController.php:124`) e em `returnData()` (`:277`). A função
(`v6/app/Helpers/functions.php:865-871`) localiza o `menu_orders` pelo slug e
incrementa `clicks`.

```php
function menuClicks($slug)
{
    $menuOrder = MenuOrder::where('slug', $slug)->first();
    if ($menuOrder != null) {
        $menuOrder->clicks++;
        $menuOrder->update();
    }
}
```

A telemetria mede **acessos à tela do CMS**, e não a exibição do player no
portal (não há contagem de audiências).

## 7. API / Resources JSON

O módulo **não expõe API pública**. A única saída JSON é a resposta
server-side do DataTables do CMS (`Yajra`), consumida por
`v6/public/assets/js/custom/contents/transmissoes/datatables.js`.

Formato dos objetos entregues ao JS:

| Campo | Origem |
|---|---|
| `value` | `row_number()` da view |
| `titulo` | `contents.title` |
| `status_name_transmissao` | `'Ativado'` / `'Desativado'` |
| `status` | booleano |
| `id` | `contents.id` |
| `edit`, `delete`, `active`, `inactivate` | flags de `struct_views()` |
