# Pauta do Dia — Views e UX

> **Slug:** `pauta-do-dia` · **Módulo:** `modulos.id = 42` · **Novo na v6**
> Views do portal: `v6/resources/views/portal/layouts/modelo1..modelo6/pauta-do-dia/`
> Views do CMS: `v6/resources/views/prefecture/contents/pauta-do-dia/`

---

## 1. Inventário de arquivos Blade

| Arquivo | Papel |
|---|---|
| `v6/resources/views/portal/layouts/modelo1/pauta-do-dia/index.blade.php` | Listagem pública (tema 1). |
| `v6/resources/views/portal/layouts/modelo2/pauta-do-dia/index.blade.php` | Listagem pública (tema 2). |
| `v6/resources/views/portal/layouts/modelo3/pauta-do-dia/index.blade.php` | Listagem pública (tema 3). |
| `v6/resources/views/portal/layouts/modelo4/pauta-do-dia/index.blade.php` | Listagem pública (tema 4). |
| `v6/resources/views/portal/layouts/modelo5/pauta-do-dia/index.blade.php` | Listagem pública (tema 5). |
| `v6/resources/views/portal/layouts/modelo6/pauta-do-dia/index.blade.php` | Listagem pública (tema 6). |
| `v6/resources/views/portal/layouts/modelo1/pauta-do-dia/show.blade.php` | Detalhe público (tema 1). |
| `v6/resources/views/portal/layouts/modelo2/pauta-do-dia/show.blade.php` | Detalhe público (tema 2). |
| `v6/resources/views/portal/layouts/modelo3/pauta-do-dia/show.blade.php` | Detalhe público (tema 3). |
| `v6/resources/views/portal/layouts/modelo4/pauta-do-dia/show.blade.php` | Detalhe público (tema 4). |
| `v6/resources/views/portal/layouts/modelo5/pauta-do-dia/show.blade.php` | Detalhe público (tema 5). |
| `v6/resources/views/portal/layouts/modelo6/pauta-do-dia/show.blade.php` | Detalhe público (tema 6). |
| `v6/resources/views/portal/components/view_pdf/files-list.blade.php` | Componente de anexos/PDF (usado no detalhe). |
| `v6/resources/views/prefecture/contents/pauta-do-dia/index.blade.php` | Listagem no CMS (AdminLTE). |
| `v6/resources/views/prefecture/contents/pauta-do-dia/create.blade.php` | Formulário de cadastro (wrapper). |
| `v6/resources/views/prefecture/contents/pauta-do-dia/edit.blade.php` | Formulário de edição (wrapper). |
| `v6/resources/views/prefecture/contents/pauta-do-dia/form.blade.php` | Campos comuns do formulário. |
| `v6/resources/views/prefecture/contents/pauta-do-dia/show.blade.php` | Página de "Funcionalidade" (descrição da funcionalidade). |

> Os seis temas do portal replicam o mesmo conteúdo com marcação diferente;
> o tema ativo vem de `Configuration` key `layout`, resolvido por
> `getTemplateActived()` em `v6/app/Http/Controllers/Admin/Controller.php:39`.

## 2. Portal — listagem (`index.blade.php`)

Estrutura (modelo 1, `v6/resources/views/portal/layouts/modelo1/pauta-do-dia/index.blade.php`):

- `@extends('portal.layouts.modelo1.layout.layout')` — layout do tema;
  `@section('breadcrumb')` com `Breadcrumbs::view(... , 'category-content', $category)`.
- Título fixo: `<p class="h4 text-uppercase txt-color-2 font-or">Pautas do dia</p>`.
- Se `$pautas->items()` não estiver vazio, lista `<ul class="list-group">` com
  um `<li>` por pauta:

```blade
<a href="{{route('pauta-do-dia.show', $pauta->slug)}}" class="text-secondary m-0 p-1  ">
    <div class='d-table-cell align-top'>
        <h4 class='d-inline-block mr-2 mb-0'><i class='far fa-folder-open'></i></h4>
    </div>
    <div class='d-table-cell'>
        <h6 title="{{$pauta->title}}"><span> {{date('d/m/Y', strtotime($pauta->publication_date))}} - </span>{{$pauta->title}}</h6>
        <div class='small content-pub'>
            {{ str_limit(strip_tags(stripslashes($pauta->text)), 200) }}
        </div>
    </div>
</a>
```

| Elemento | Comportamento observável |
|---|---|
| Data exibida | `date('d/m/Y', ...)` de `publication_date` — **só o dia**, sem hora. |
| Título | Texto puro do operador (ex.: "19ª Sessão"). |
| Resumo | `str_limit(strip_tags(stripslashes($pauta->text)), 200)` — remove HTML e barras, corta em 200 caracteres (o `str_limit` do Laravel 5.5 acrescenta reticências). |
| Link | `route('pauta-do-dia.show', $pauta->slug)`. |
| Paginação | `{{ $pautas->links() }}` no rodapé da coluna central. |
| Estado vazio | `<p class="text-muted">Nenhuma registro encontrado.</p>` (redundância de concordância no texto original). |
| Coluna lateral | `@include('portal.layouts.modelo1.layout.sidebar.layout')`. |

## 3. Portal — detalhe (`show.blade.php`)

`v6/resources/views/portal/layouts/modelo1/pauta-do-dia/show.blade.php`:

| Bloco | Código (resumo) | Observação |
|---|---|---|
| Título | `<p class="h4 text-uppercase txt-color-2 font-or">{{$pauta->title}}</p>` | — |
| Metadados | `@if(!empty($pauta->source)) Do(a) <span>{{$pauta->source}}</span> @endif @if(!empty($pauta->author)) por <span>{{$pauta->author}}</span> @endif Publicado em <span>{{date('d/m/Y', ...)}}</span> às <span>{{date('H:i', ...)}}</span>` | Data **e hora** aqui, ao contrário da listagem. |
| Compartilhamento | `<div class="addthis_inline_share_toolbox mb-3"></div>` | AddThis (serviço externo). |
| Imagem de destaque | `@if(!empty($pauta->archives->where('highlight', 1)->first()))` + `<img class="img-fluid" src="{{url($data_webp)}}" data-webp='...'>` | Consulta a relação `archives` (lazy loading) — 1 query extra. |
| Corpo | `<div class="text-secondary text-content">{!! $pauta->text !!}</div>` | **HTML sem escaping** — o conteúdo vem do Summernote do CMS. |
| Anexos | `@if (count($archives) > 0 || $view_pdf != null) @include('portal.components.view_pdf.files-list') @endif` | Inclui o componente compartilhado. |

O breadcrumb do detalhe usa `Breadcrumbs::view(..., 'content', $pauta)`.

## 4. Componente de anexos `files-list.blade.php`

Arquivo: `v6/resources/views/portal/components/view_pdf/files-list.blade.php`.

| Comportamento | Detalhe |
|---|---|
| Detecção de mobile | `preg_match('/(iPhone|iPad|iPod|Android|webOS|BlackBerry|Windows Phone)/i', request()->header('User-Agent'))` define `$isMobile`. |
| PDF embutido | Se `$view_pdf` existe e não é mobile: imagem (`image/jpeg`, `image/png`) via `getLargFile()` ou `iframe` 16:9 (`embed-responsive`) com `src="/{{ $view_pdf->path }}"`. |
| Filtro de anexos | Extensões permitidas: `rar, zip, doc, docx, pdf, odt, xls, xlsx, xlsb, xslm, ppt, pptx`. No desktop, anexos com `view_pdf = true` **não** entram na lista de download (já estão no iframe); no mobile, também são removidos os `highlight`. |
| Download | Cada arquivo vira `<a href="{{ url($archive->path) }}" target="_blank">` exibindo `$archive->description or $archive->name`. Só é listado se `file_exist($archive->path)` (verificação no disco, caminho **relativo** ao `public/`). |
| Sem arquivos | Exibe "Nenhum arquivo encontrado" quando o contador fica abaixo de 1. |
| Bloco `$answer` | Trecho de reaproveitamento de outro módulo (`$answer->archive`), inerte neste módulo. |

## 5. CMS — listagem (`index.blade.php`)

`v6/resources/views/prefecture/contents/pauta-do-dia/index.blade.php`:

| Elemento | Detalhe |
|---|---|
| Layout | `@extends('admin.layouts.app')` (AdminLTE 1.22). |
| Cabeçalho | `<h3 class="box-title align-middle">Lista de Pautas do Dia</h3>`. |
| Botão cadastrar | `@if (has_access(['pauta-do-dia.create']))` → link `cadastrar/pauta-do-dia` (URL relativa a `/cms/conteudos/`). |
| Exclusão em lote | `@if (has_access(['pauta-do-dia.delete']))` → componente `prefecture.components.deleteByLoteSubmitButton` com `route => 'ajax.generic.content.deleteAll'`. |
| Tabela | `<table id="tabela_pauta-do-dia" class="table table-striped w-100">` com colunas: seleção (checkbox mestre), **ID**, **Título**, **Data e Hora** (`text-center`), **Ações** (`column-actions`). |
| Scripts | `assets/js/custom/utilities/deleteAll.js` e `assets/js/custom/contents/pauta-do-dia/datatables.js`. |

## 6. CMS — JavaScript e ordenação customizada

Arquivo: `v6/public/assets/js/custom/contents/pauta-do-dia/datatables.js`.

### Colunas

| # | `data` | Render | Ordenável |
|---|---|---|---|
| 0 | `null` | coluna de seleção em lote (injetada por `runDatatables`, `table.js:16-25`) | **não** |
| 1 | `value` | injetado por `runDatatables` (`table.js:12-16`) → `<div class="p-10-0">${data}</div>` — corresponde à coluna "ID" do cabeçalho | sim |
| 2 | `titulo` | `<div class='vertical-middle' style='padding:8px'>${data}</div>` | sim (alfabética) |
| 3 | `data_e_hora` | ordena pela chave customizada; exibe `<span class="d-none">${sortVal}</span>${data}` | sim (chave customizada) |
| 4 | `null` | ações: Facebook + editar/excluir | **não** |

### `converterDataPauta(data, title, id)`

```js
function converterDataPauta(data, title, id) {
    if (!data) return '';

    // Formato esperado do banco (View): "DD/MM/YYYY às HH:MI"
    const partes = data.trim().split(' às ');
    const dataPartes = partes[0].split('/');

    if (dataPartes.length !== 3) return data;

    const dia = dataPartes[0].padStart(2, '0');
    const mes = dataPartes[1].padStart(2, '0');
    const ano = dataPartes[2];

    // Extraímos apenas os números do título (ex: "19ª Sessão" -> "19")
    // Isso garante que a Sessão 19 venha antes da 18 no mesmo dia,
    // mesmo que o horário/ID estejam invertidos no banco.
    const titleNumbers = (title || '').replace(/\D/g, '') || '0';
    const titleNumPad = String(titleNumbers).padStart(10, '0');

    // ID como critério final de desempate
    const idPad = String(id || 0).padStart(10, '0');

    // Chave de ordenação: Data + Números do Título + ID
    return `${ano}${mes}${dia}${titleNumPad}${idPad}`;
}
```

A chave final tem 8 + 10 + 10 = **28 caracteres** (`AAAAMMDD` + números do
título com zero à esquerda + id com zero à esquerda) e é usada **apenas** na
ordenação (`type === 'sort'`); na exibição, a chave fica oculta em um
`<span class="d-none">` e o texto formatado é mostrado.

### Chamada de `runDatatables`

```js
runDatatables('pauta-do-dia', collums, true, 'DESC', null, true, null, 3);
```

Parâmetros (`v6/public/assets/js/custom/utilities/table.js:3`):
`url`, `columns`, `ordenavel = true`, `type_order = 'DESC'`,
`callback = null`, `deleteAll = true`, `customButtons = null`,
`order_column = 3`.

- `ordenavel = true` habilita a ordenação server-side pelo Yajra.
- `order_column = 3` → a ordenação inicial (DESC) usa a coluna de índice 3,
  isto é, **`data_e_hora`**, ou seja, a chave `converterDataPauta`.
- `deleteAll = true` injeta a coluna de seleção em lote e o botão de
  exclusão collective.

### Botões de ação

```js
createSingleButton(data.id, `https://www.facebook.com/sharer/sharer.php?u=${window.location.origin}/pauta-do-dia/${data.slug}`, true, 'fa fa-facebook text-primary', 'Compartilhar no facebook', '_blank')
+ createBTN(data.id, `editar/pauta-do-dia/${data.id}`, data.edit, data.delete)
```

- O botão de Facebook abre em nova aba a URL de compartilhamento com o
  endereço público `/pauta-do-dia/{slug}` (usando `window.location.origin`).
- `createBTN` (linha 227 de `table.js`) só renderiza o botão de editar se
  `data.edit` for verdadeiro e o de excluir se `data.delete` for verdadeiro —
  flags vindas de `struct_views()`.
- O botão de excluir usa `data-toggle="delete_btn"` e é tratado por
  `addDelete()` (`table.js:466`), que faz `myFetch('/cms/conteudo/{id}', 'DELETE')`.

### Exportação

`runDatatables` registra os botões padrão de exportação para **Excel** e
**PDF** (`extend: 'excel'` e `extend: 'pdf'`, com `columns: ':visible:not(.column-actions)'`),
com título `Prefeitura de {cidade} - {data}`. A exportação respeita a
ordenação e os filtros ativos do DataTable.

## 7. A ordenação customizada existe **duas vezes**

| Aspecto | Portal (PHP) | CMS (JavaScript) |
|---|---|---|
| Arquivo | `v6/app/Http/Controllers/Portal/PautaDoDiaController.php:20` | `v6/public/assets/js/custom/contents/pauta-do-dia/datatables.js` |
| Mecanismo | `orderByRaw("publication_date::date DESC, COALESCE(NULLIF(regexp_replace(title, '[^0-9]', '', 'g'), ''), '0')::numeric DESC, id DESC")` | `converterDataPauta()` → chave `AAAAMMDD` + dígitos do título (10) + id (10) |
| Nº extraído do título | `regexp_replace(title, '[^0-9]', '', 'g')` (remove tudo que não é dígito, inclusive "ª" e o ano) | `title.replace(/\D/g, '')` (mesma regra) |
| Desempate | `id DESC` | id com zero à esquerda, crescente dentro da chave (equivalente) |
| Direção do número | Descendente (19 antes de 18) | Descendente (a chave ordena decrescentemente no `type_order = 'DESC'`) |

As duas implementações devem concordar, mas **não há teste automatizado**
garantindo isso: qualquer alteração em uma delas sem alterar a outra
produz listas diferentes entre o portal e o CMS.

> Observação sobre a direção: a ordenação numérica é **decrescente**, ou
> seja, dentro do mesmo dia a pauta "19ª Sessão" aparece **antes** da
> "18ª Sessão". Esse é o comportamento efetivamente implementado (e o
> comentário do JS afirma essa intenção).

## 8. CMS — formulário

`v6/resources/views/prefecture/contents/pauta-do-dia/form.blade.php`:

| Campo | Componente | Observações |
|---|---|---|
| `category_slug` / `category_contents_id` | `<input type="hidden">` | Necessários para a validação (`ContentRequest`) e para gravar a categoria. |
| Link Permanente (`slug`) | `components.inputs.input` | Exibe `url('pauta-do-dia')}/` como prefixo; botão "Acessar" abre `/pauta-do-dia/{slug}/` em nova aba. |
| Título | `components.inputs.input` | `maxlen` 150 no HTML; validação real `max:120`. |
| Data de publicação | `components.inputs.date` | `classes: 'input-date-hour'`, máscara `br-data-tempo`, formato `d/m/Y H:i`; valor padrão = agora. |
| Texto | `components.inputs.textarea` | Máscara `summernote` (editor rico). |
| Arquivos | `components.inputs.archive` | `delete_function => 'deleteHighlightArchive'`; botão "Adicionar Arquivo" (`assets/js/custom/utilities/helpers/add_archive.js`); aviso de tamanho máximo 128 MB. |
| Erros | `components.layout.errors` | — |

`create.blade.php` e `edit.blade.php` são wrappers: abrem o formulário
(`Form::open` com `route => ['conteudo.index']` no cadastro;
`Form::model($content, ['route' => ['conteudo.update', $content->id], 'method' => 'PUT']`
na edição), incluem o `form.blade.php` e exibem o botão de salvar. O
`edit.blade.php` inclui ainda os scripts de galeria de imagens
(`assets/js/custom/contents/midias/index.js`) e a função
`deleteHighlightArchive()` (POST em `route('ajax.delete.files')`).

`show.blade.php` do CMS exibe apenas a ficha da **funcionalidade** (nome,
descrição e data de criação a partir de `$functionality`) — não é a
visualização de uma pauta.

## 9. O que o cidadão vê (portal) x o que o operador vê (CMS)

| Cidadão | Operador |
|---|---|
| Lista paginada com data, título e resumo de 200 caracteres | DataTable com seleção em lote, "ID", título, data/hora ordenável e ações |
| Página de detalhe com autor/fonte, data/hora, AddThis, imagem de destaque, texto e anexos | Formulário com slug, título, data/hora, texto rico, destaque e anexos |
| Sem edição, sem busca, sem filtros | Exclusão individual e em lote, exportação Excel/PDF, log de auditoria |
| 404 para slug inexistente | Mensagens de flash (`flash(...)->success()/error()`) após gravar/excluir |
