# Pauta do Dia — Rotas e Controllers

> **Slug:** `pauta-do-dia` · **Módulo:** `modulos.id = 42` · **Novo na v6**
> Rotas públicas em `v6/routes/web.php`; rotas do CMS em `v6/routes/basepro.php`.

---

## 1. Rotas do Portal (público)

Arquivo: `v6/routes/web.php`, dentro do grupo
`Route::middleware(['check.site.manutention'])` (aberto na linha 8).

| Método | URI | Nome da rota | Controller | Linha |
|---|---|---|---|---|
| GET | `/pauta-do-dia` | `pauta-do-dia.index` | `Portal\PautaDoDiaController@index` | `v6/routes/web.php:49` |
| GET | `/pauta-do-dia/{slug}` | `pauta-do-dia.show` | `Portal\PautaDoDiaController@show` | `v6/routes/web.php:50` |

Middleware aplicado: apenas `check.site.manutention` (herdado do grupo).
Sem autenticação, sem `check.logged`, sem `check.route.permission`.

Links gerados na view: `route('pauta-do-dia.show', $pauta->slug)`.

## 2. Rotas do CMS (`/cms`)

Arquivo: `v6/routes/basepro.php`, dentro do grupo
`Route::group(['prefix' => 'cms', 'before' => 'csrf'], ...)` (linha 35).

| Método | URI | Nome da rota | Controller | Linha |
|---|---|---|---|---|
| GET | `/cms/conteudos/pauta-do-dia` | `listar-conteudo` | `Prefecture\ContentController@genericIndex` | `v6/routes/basepro.php:235` |
| GET | `/cms/conteudos/cadastrar/pauta-do-dia` | `cadastrar-conteudo` | `Prefecture\ContentController@genericCreate` | `v6/routes/basepro.php:236` |
| GET | `/cms/conteudos/editar/pauta-do-dia/{id}` | `editar-conteudo` | `Prefecture\ContentController@genericEdit` | `v6/routes/basepro.php:237` |
| GET | `/cms/datatable/pauta-do-dia` | (sem nome; grupo `datatable`) | `Prefecture\DatatableController@get_pauta_do_dia` | `v6/routes/basepro.php:459` |
| POST | `/cms/conteudo` | `conteudo.store` | `Prefecture\ContentController@store` (linha 616) | `v6/routes/basepro.php:174` (`Route::resource('conteudo', ...)`) |
| GET | `/cms/conteudo/{id}/edit` | `conteudo.edit` | `Prefecture\ContentController@edit` (linha 353) | `v6/routes/basepro.php:174` |
| PUT/PATCH | `/cms/conteudo/{id}` | `conteudo.update` | `Prefecture\ContentController@update` (linha 937) | `v6/routes/basepro.php:174` |
| DELETE | `/cms/conteudo/{id}` | `conteudo.destroy` | `Prefecture\ContentController@destroy` (linha 1320) | `v6/routes/basepro.php:174` |
| POST | `/cms/conteudo/deleteAll` | `ajax.generic.content.deleteAll` | `Prefecture\ContentController@genericDeleteAll` | `v6/routes/basepro.php:141` |
| DELETE | `/cms/conteudo/delete/{id}` | `generic.conteudo.delete` | `Prefecture\ContentController@genericDestroy` | `v6/routes/basepro.php:223` |
| POST | `/cms/modulos` | `modulos.update` | `Admin\ModulosController@update` | `v6/routes/basepro.php:41` (habilita/desabilita o módulo 42) |

O botão de exclusão individual do JavaScript usa `DELETE /cms/conteudo/{id}`
(rota `conteudo.destroy`), via `addDelete()` em
`v6/public/assets/js/custom/utilities/table.js:466`.

## 3. Controller do Portal

Arquivo: `v6/app/Http/Controllers/Portal/PautaDoDiaController.php`
(classe `App\Http\Controllers\Portal\PautaDoDiaController`, estende
`App\Http\Controllers\Admin\Controller`).

### `index()` — linha 14

```php
public function index()
{
    $category = CategoryContent::where('slug', 'pauta-do-dia')->whereNull('category_contents_id')->first();

    $pautas = Content::whereHas('categoryContents', function ($query) {
        $query->where('slug', 'pauta-do-dia');
    })->orderByRaw("publication_date::date DESC, COALESCE(NULLIF(regexp_replace(title, '[^0-9]', '', 'g'), ''), '0')::numeric DESC, id DESC")->paginate();

    return view("portal.layouts.{$this->getTemplateActived()}.pauta-do-dia.index", compact('category', 'pautas'));
}
```

| Elemento | Comportamento |
|---|---|
| `$category` | Categoria raiz "Pautas do Dia", usada apenas no breadcrumb. |
| Filtro | `whereHas('categoryContents')` → `contents.category_contents_id` = id da categoria `pauta-do-dia`. |
| Ordenação | `publication_date::date DESC`, depois número extraído do título DESC, depois `id DESC`. |
| Paginação | `paginate()` **sem argumento** → 15 itens por página (padrão do Laravel 5.5). |
| View | `portal.layouts.{tema}.pauta-do-dia.index`, com `tema` vindo de `getTemplateActived()`. |

### `show()` — linha 25

```php
public function show(string $slug)
{
    $pauta = Content::whereHas('categoryContents', function ($query) {
        $query->where('slug', 'pauta-do-dia');
    })->where('slug', $slug)->firstOrFail();

    $archives = Archive::where('contents_id', $pauta->id)->where('highlight', null)->get();
    $view_pdf = Archive::where('contents_id', $pauta->id)->where('view_pdf', true)->first();

    return view("portal.layouts.{$this->getTemplateActived()}.pauta-do-dia.show", compact('pauta', 'archives', 'view_pdf'));
}
```

| Elemento | Comportamento |
|---|---|
| Busca | Por `slug` **e** categoria `pauta-do-dia` (uma pauta de outra categoria não é acessível por esta URL). |
| Slug inexistente | `firstOrFail()` → resposta **404**. |
| `$archives` | Anexos com `highlight = null` (não nulos, apenas não marcados) — o `where('highlight', null)` é gerado como `highlight IS NULL` pelo Eloquent. |
| `$view_pdf` | Primeiro anexo com `view_pdf = true` (apenas um é exibido). |

> Observação: `$pauta->archives` (relação Eloquent) também é usada na view
> para a imagem de destaque (`highlight = 1`), o que provoca uma consulta
> extra (lazy loading).

## 4. Controller do CMS — CRUD genérico

Arquivo: `v6/app/Http/Controllers/Prefecture/ContentController.php`.
Não existe controller dedicado ao módulo: o slug `pauta-do-dia` é tratado
nos métodos genéricos.

| Método | Linha | Comportamento específico do módulo |
|---|---|---|
| `genericIndex($slug)` | 112 | `if ($slug === 'pauta-do-dia') return $this->returnIndex('Pauta do dia', $slug);` — chama `generateLog('Pauta do dia')` e `menuClicks('pauta-do-dia')` e renderiza `prefecture.contents.pauta-do-dia.index`. |
| `returnData(...)` | 235 | `if ($slug === 'pauta-do-dia')` → `returnData('Pauta do Dia', $slug, null, ['categoryContent' => CategoryContent::where('slug','pauta-do-dia')->whereNull('category_contents_id')->first()], $id);` — montar o formulário. |
| `store(ContentRequest)` | 616 | Grava `contents` com `category_contents_id` da categoria; gera `slug` a partir do título (`generateSlug`/`slugfy`), resolve colisão acrescentando `-{maxid}`; move imagens do Summernote para `storage/content/{slug}/{id}/`; grava `archives`; registra atividade. |
| `update(ContentRequest, $id)` | 937 | Atualiza o conteúdo; mantém o `slug` informado, resolve colisão com `pub{id}-` ou `-{id}`; regra específica: `if ($categoria_pai == 1 && $slug_atual != $dataSlug)` (categoria de nível raiz). |
| `genericDestroy($id)` | 1369 | Soft delete do conteúdo, remove itens de menu referenciando o conteúdo e registra atividade. |
| `genericDeleteAll` | 70 | Exclusão em lote via `contentRepository->destroyAll($request->toDelete)`. |
| `setStatus` | 1548 | Alterna o estado `status` do conteúdo. |

### Ações de menu e permissões

- `v6/app/Providers/MenuServiceProvider.php:142-144` — insere o item
  "Pauta do Dia" apontando para `/cms/conteudos/pauta-do-dia`.
- `v6/app/Library/Helper/appHelpers.php:22` — `has_access($access)`: exige
  login; `super-admin` passa; array → `hasAnyAccess`, string → `hasAccess`.

Permissões usadas pelo módulo (padrão `<módulo>.<ação>`):

| Permissão | Onde é exigida |
|---|---|
| `pauta-do-dia.create` | Botão "Cadastrar Pauta do Dia" (`v6/resources/views/prefecture/contents/pauta-do-dia/index.blade.php:11`). |
| `pauta-do-dia.delete` | Botão de exclusão em lote (`index.blade.php:17`) e flag `delete` da view do CMS. |
| `pauta-do-dia.edit` | Flag `edit` da view do CMS (`struct_views`); botão "Editar". |
| `pauta-do-dia.restore` / `pauta-do-dia.inactivate` | Flags `active`/`inactivate` geradas por `struct_views` (não usados pela view deste módulo). |
| `pauta-do-dia.index` | Derivada pelo middleware a partir do segmento da URL (`/cms/conteudos/pauta-do-dia`). |

O middleware `v6/app/Http/Middleware/CheckRoutePermission.php` traduz o
segmento da rota em permissão: `cadastrar` → `<segmento>.create`,
`editar` → `<segmento>.edit`, demais → `<segmento>.index`. Rotas cujo nome
contém `datatable` estão na lista de rotas abertas do middleware.

## 5. Controller do CMS — DataTable

Arquivo: `v6/app/Http/Controllers/Prefecture/DatatableController.php`.

```php
public function get_pauta_do_dia()
{
    $pautasDoDia = DB::table("pauta_do_dia")->get();
    return DataTables::of($this->struct_views($pautasDoDia, 'pauta-do-dia'))->make(true);
}
```

| Ponto | Detalhe |
|---|---|
| Origem dos dados | View SQL `pauta_do_dia` (5 colunas: `value`, `titulo`, `data_e_hora`, `id`, `slug`). |
| Leitura | `->get()` carrega **todas** as linhas; a paginação do DataTable é aplicada em memória pelo Yajra. |
| `struct_views($data, 'pauta-do-dia')` (linha 504) | Añade as flags `edit`, `delete`, `active`, `inactivate` conforme `has_access`. |
| Coluna "ID" | Exibe `value` (o `row_number()` da view), não `contents.id`. |
| `make(true)` | Resposta JSON no formato do DataTables. |

## 6. Form Request e validação

Arquivo: `v6/app/Http/Requests/ContentRequest.php`. O slug é deduzido do campo
oculto `category_slug` (ou de `category_contents_id`) e o `switch` escolhe as
regras (linha 262):

```php
case 'pauta-do-dia' :

    $rules = [
        "title" => "required|max:120",
        "text" => "required",
        "publication_date" => "required",
        "link" => "max:200",
        "archive_highlight" => "image|max:5000",
        "archive.*.0" => "mimes:jpg,jpeg,png,bmp,gif,rar,zip,doc,docx,pdf,odt,xls,xlsx,xlsb,xslm,ppt,pptx,ods,wav,mp3,wma,mp4,mpg4,mkv,flv",
    ];
    break;
```

| Campo | Regra | Observação |
|---|---|---|
| `title` | `required|max:120` | O formulário exibe "Máximo de 150 caracteres" (`maxlen` 150): **divergência entre validação e dica**. |
| `text` | `required` | Corpo HTML do Summernote. |
| `publication_date` | `required` | Data **e hora** (máscara `d/m/Y H:i`). |
| `link` | `max:200` | Campo opcional (link externo). |
| `archive_highlight` | `image|max:5000` | Imagem de destaque até 5 MB. |
| `archive.*.0` | `mimes:...` | Anexos: documentos, áudios e vídeos; máximo 128 MB (aviso no formulário). |
| — | pré-validação em `rules()` | Extensões de anexo verificadas antes do `switch`, com mensagem própria. |

`authorize()` retorna `true`; a autorização real é feita pelo middleware
`check.route.permission` e pelos `has_access()` das views.

## 7. Auditoria (activity log)

Todas as operações do CMS passam por `activity()` (registro na tabela de
atividades, exibida em `/cms/log`):

| Operação | Registro |
|---|---|
| Abrir a listagem | `generateLog('Pauta do dia')` em `returnIndex`. |
| Abrir o formulário | `generateLog($title, 'create'|'edit')` em `returnData`. |
| Criar conteúdo | `activity()->performedOn($content)->causedBy(Sentinel::getUser())->withProperty('Detalhe', 'Foi criado um conteúdo - ID: ... - Título: ...')->log('Cadastro de conteúdo')` (`ContentController`, em `store`). |
| Criar link | `log('Cadastro de link')`. |
| Criar arquivo | `log('Cadastro de arquivo')` (inclusive a imagem de destaque). |
| Excluir | `log('Exclusão de conteúdo')` em `genericDestroy`. |

## 8. API

Não há endpoint JSON próprio do módulo na `v6`. O CMS expõe apenas a
listagem DataTable (`GET /cms/datatable/pauta-do-dia`), consumida por AJAX
pelo `datatables.js`. O portal é 100 % HTML server-side.

## 9. Mapa de responsabilidades (resumo)

| Rota | Controller | Origem dos dados | Saída |
|---|---|---|---|
| `GET /pauta-do-dia` | `Portal\PautaDoDiaController@index` | `contents` (Eloquent) | HTML paginado |
| `GET /pauta-do-dia/{slug}` | `Portal\PautaDoDiaController@show` | `contents` + `archives` | HTML |
| `GET /cms/conteudos/pauta-do-dia` | `Prefecture\ContentController@genericIndex` | — | HTML (listagem AdminLTE) |
| `GET /cms/datatable/pauta-do-dia` | `Prefecture\DatatableController@get_pauta_do_dia` | view `pauta_do_dia` | JSON DataTables |
| `GET /cms/conteudos/cadastrar/pauta-do-dia` | `Prefecture\ContentController@genericCreate` | `category_contents` | HTML (formulário) |
| `POST /cms/conteudo` | `Prefecture\ContentController@store` | `contents` + `archives` | redirect + flash |
| `PUT /cms/conteudo/{id}` | `Prefecture\ContentController@update` | `contents` + `archives` | redirect + flash |
| `DELETE /cms/conteudo/{id}` | `Prefecture\ContentController@destroy` | `contents` | JSON (`success`/`erro`) |
