# Atas e Pontos de Frequência — Views e UX

> Versão analisada: `v6`. Módulo **novo na v6**. Slug `modulos`:
> `atas-e-pontos-de-frequencia` (id 46).

---

## 1. Telas do portal

As telas existem em **6 variantes de tema**, porque o controller monta o
nome da view a partir de `getTemplateActived()`:

```
v6/resources/views/portal/layouts/modelo1/atas-e-pontos/
v6/resources/views/portal/layouts/modelo2/atas-e-pontos/
v6/resources/views/portal/layouts/modelo3/atas-e-pontos/
v6/resources/views/portal/layouts/modelo4/atas-e-pontos/
v6/resources/views/portal/layouts/modelo5/atas-e-pontos/
v6/resources/views/portal/layouts/modelo6/atas-e-pontos/
```

Cada diretório contém os mesmos quatro arquivos:

| Arquivo (em cada `modeloN`) | Função |
|---|---|
| `show.blade.php` | Página da categoria: inclui o menu lateral, o bloco de listagem e todo o JavaScript de Ajax. |
| `list.blade.php` | Bloco de filtros + lista de documentos; é o fragmento HTML devolvido nas respostas Ajax. |
| `pub-nav.blade.php` | Menu lateral com as categorias filhas. |
| `view.blade.php` | Página do documento (texto + anexos). |

O tema ativo vem da chave `layout` da tabela `configuration`, lida em
`v6/app/Http/Controllers/Admin/Controller.php:39-49`.

## 2. `show.blade.php` — página da categoria

Estrutura (tomando `modelo1` como referência):

```blade
@section("breadcrumb")
    {{  Breadcrumbs::view('portal.layouts.modelo1.layout.breadcrumb','category-atas-e-pontos', $category) }}
@endsection

@section('content')
    <div class="col-md-12 mx-auto row">
        @include('portal.layouts.modelo1.atas-e-pontos.pub-nav')
        <div id="pub-content" class="col-md-8 text-center text-secondary">
            <div class="sk-spinner sk-spinner-wave"> ... </div>
            <div id="include-list">
                @include('portal.layouts.modelo1.atas-e-pontos.list')
            </div>
        </div>
    </div>
@endsection
```

Pontos de UX:

- O elemento `#pub-content` recebe a classe `sk-loading` durante as chamadas
  Ajax, exibindo um spinner (CSS `spinner.css` carregado no bloco
  `other-links`).
- O elemento `#include-list` é o alvo das substituições de HTML.
- A paginação é interceptada por JavaScript (`$(document).on('click',
  '.pagination a', ...)`) e o número de página é gravado no `location.hash`.

### 2.1 Padrão Ajax dos filtros

Funções definidas no bloco `other-scripts` de `show.blade.php`:

| Função | Endpoint | Parâmetros | Efeito |
|---|---|---|---|
| `getPosts(page)` | `?page=N` | `ano`, `mes`, `texto` lidos do DOM | Atualiza `#include-list` e grava `location.hash`. |
| `getPostsByAno()` | `?ano=YYYY` | — | Recarrega a lista; se o ano for vazio, `location.reload()`. |
| `getPostsByMes()` | `?ano=YYYY&mes=M` | — | Recarrega a lista. |
| `getPostsByTexto()` | `?ano=YYYY&mes=M&texto=TXT` | — | Recarrega a lista. |
| `apagaTexto()` | — | — | Limpa `#search` e refaz a busca. |
| `exibirBotaoCancelar()` | — | — | Exibe o botão de limpar (ao focar o campo). |

Em todas, o bloco `#pub-content` alterna a classe `sk-loading`, e o
`fail()` mostra `alert('As publicações não puderam ser carregadas.')`.

```js
function getPosts(page) {
    $('#pub-content').toggleClass('sk-loading');
    var ano = $('select[name="ano"]').val();
    var mes = $('select[name="mes"]').val();
    var text = $('input[id="search"]').val();

    $.ajax({
        url : '?page=' + page+'&ano=' + ano + '&mes=' + mes + '&texto=' + text,
        dataType: 'json',
    }).done(function (data) {
        $('#pub-content').toggleClass('sk-loading');
        $('#include-list').html(data);
        location.hash = page;
    }).fail(function () { ... });
}
```

Observação de segurança: a URL é montada por concatenação, sem
`encodeURIComponent` — caracteres especiais do texto chegam ao servidor
quebrando a query string.

## 3. `list.blade.php` — filtros e listagem

O fragmento contém (a) o cartão de filtros e (b) a lista.

### 3.1 Filtros

| Campo | Elemento | Origem dos valores | Evento |
|---|---|---|---|
| Ano | `<select name="ano" id="ano">` | `$anos` (SQL cru no controller); só exibe anos `<= date('Y')` | `onchange="getPostsByAno();"` |
| Mês | `<select name="mes" id="mes">` | `$meses` (SQL cru, preenchido apenas quando há ano selecionado); rótulo via `translateMonth($mes->mes)` | `onchange="getPostsByMes();"` |
| Texto | `<input name="text" id="search">` | digitação do usuário | `onchange="getPostsByTexto();"` + botão de limpar `apagaTexto()` |

Campos ocultos `sub_category` (`$category->slug`) e `category`
(`atas-e-pontos`) são enviados no formulário.

### 3.2 Lista

Cada item exibe:

- Ícone de pasta (`far fa-folder-open`).
- Data formatada `date('d/m/Y', strtotime($content->publication_date))` +
  título.
- Resumo: `str_limit(strip_tags(stripslashes($content->text)), 200)`.
- Link: `route('atas-e-pontos.view', [$category->slug, $content->slug])`.
- Paginação: `{{ $contents->links() }}` (12 itens por página).
- Quando vazio: "Nenhum resultado.".

## 4. `pub-nav.blade.php` — menu lateral

```blade
@foreach($categories as $cat)
    <a href="{{route('atas-e-pontos.show', $cat->slug)}}" id="{{$cat->slug}}"
       class="list-group-item list-group-item-light @if(isset($category) && ($cat->slug == $category->slug)) active @endif ">
        <li class="text-center text-md-left">{{$cat->name}}</li>
    </a>
@endforeach
```

A classe `scrollbar` é aplicada quando há mais de 14 categorias.

## 5. `view.blade.php` — documento

Elementos:

- `meta-tags` com Open Graph (`og:title`, `og:description`, `og:url`).
- Breadcrumb `view-atas-e-pontos`.
- Cabeçalho com o nome da categoria (`$content->categoryContents->name`), o
  título e a data/hora de publicação.
- Corpo: `{!! $content->text !!}` (HTML não escapado — conteúdo do CMS).
- Bloco de arquivos, condicional:

```blade
@if (count($archives) > 0 || $view_pdf != null)
    @include('portal.components.view_pdf.files-list')
@endif
```

## 6. Download e exibição dos anexos

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

### 6.1 Exibição embutida

- Em dispositivos móveis (detecção por `User-Agent`), o bloco embutido **não**
  é renderizado.
- Se `view_pdf->type` for `image/jpeg` ou `image/png`, exibe
  `<img src="{{ getLargFile("/$view_pdf->path") }}">`.
- Caso contrário, exibe um `<iframe>` responsivo 16:9 com
  `src="/{{ $view_pdf->path or '' }}"` e `allowfullscreen`.

### 6.2 Lista de downloads

Extensões permitidas (filtro em PHP na view):

```php
$allowedExtensions = ['rar', 'zip', 'doc', 'docx', 'pdf', 'odt', 'xls', 'xlsx', 'xlsb', 'xslm', 'ppt', 'pptx'];
```

Regras de exibição:

- Em desktop, são listados os anexos com extensão permitida, `highlight !== true`
  e `view_pdf !== true` (o arquivo exibido no iframe não aparece na lista).
- Em mobile, são listados os anexos com extensão permitida e `highlight !== true`
  (inclui o `view_pdf`).
- Só são listados arquivos existentes: `file_exist($archive->path)`
  (`v6/app/Helpers/functions.php:267`).
- Rótulo: `$archive->description or $archive->name`.
- Link de download: `<a href="{{ url($archive->path) }}" target="_blank">` — é um
  **link direto para o arquivo**, aberto em nova aba.

**Não existe rota de download própria** nem verificação de acesso ao arquivo:
qualquer pessoa com o caminho do arquivo o baixa. O caminho é derivado do
nome original do upload, o que o torna adivinhável.

### 6.3 Anexo `highlight`

O controller busca `Archive::where('contents_id', $content->id)->where('highlight', null)`.
O `highlight` serve para a peça de destaque (imagem de capa) e **não** aparece
na lista de downloads.

## 7. Telas do CMS (AdminLTE)

Diretório: `v6/resources/views/prefecture/contents/atas-e-pontos/`.

| Arquivo | Função |
|---|---|
| `index.blade.php` | Listagem: tabela `#tabela_atas_e_pontos`, colunas ID, Título, Categoria, Data de publicação, Ações. Botões "Cadastrar Conteúdo" e "Categorias de Atas e Pontos de Frequência" (ambos condicionados a `has_access(['atas-e-pontos.create'])`), exclusão em lote (`has_access(['atas-e-pontos.delete'])`). Carrega `assets/js/custom/utilities/deleteAll.js` e `assets/js/custom/contents/atas-e-pontos/datatable.js`. |
| `create.blade.php` | Envoltório do formulário de criação. |
| `edit.blade.php` | Envoltório do formulário de edição. |
| `form.blade.php` | Campos: categoria (select com as filhas), "Link Permanente" (`url("atas-e-pontos/$category->slug")/{slug}` com link "Acessar"), título, data de publicação (máscara `br-data-tempo`), texto (Summernote), bloco de anexos (`components.inputs.archive`) e botão "Adicionar Arquivo". |

Categorias: `v6/resources/views/prefecture/category-contents/atas-e-pontos/`
(`create.blade.php`, `edit.blade.php`, `form.blade.php`) — o formulário tem
apenas "Nome" e "Categoria Pai" (desabilitado).

## 8. JavaScript do DataTable (CMS)

Arquivo: `v6/public/assets/js/custom/contents/atas-e-pontos/datatable.js`.

Colunas: `titulo`, `categoria`, `data_publicacao` e a coluna de ações:

```js
return `<div class='flex justify-center vertical-middle'>`
    + createSingleButton(data.id + 'Link',`/atas-e-pontos/${data.categoria_slug}/${data.slug}`, true, 'glyphicon glyphicon-link text-primary form_link', 'Acessar conteúdo', '_blank')
    + createBTN(data.id, 'editar/atas-e-pontos/' + data.id, data.edit, data.delete)
    + `</div>`;
```

- "Acessar conteúdo" abre em nova aba a URL pública do documento.
- O botão de edição depende das flags `edit`/`delete` anexadas por
  `struct_views()`.

## 9. Exportações

O módulo **não** oferece exportação (CSV, Excel, PDF). O relatório disponível
é o print do navegador. O CMS só permite **exclusão em lote** via
`ajax.generic.content.deleteAll` e a seleção múltipla com checkbox mestre.

## 10. O que o usuário final vê vs. o que o operador vê

| Portal (cidadão) | CMS (operador) |
|---|---|
| Menu lateral com as categorias do módulo | Tabela com título, categoria, data e ações |
| Filtros de ano, mês e texto, com Ajax | Botões de cadastro e de gestão de categorias |
| Lista paginada (12 por página) com data, título e resumo de 200 caracteres | Exclusão em lote e edição |
| Documento com texto e downloads dos anexos | Formulário com categoria, título, data, texto e anexos |
| PDF de atas exibido em `<iframe>` (desktop) | Anexo marcado como `view_pdf` para exibição embutida |

## 11. Acessibilidade e responsividade observadas

- O layout do portal é Bootstrap 4 responsivo (`col-md-8`, `col-md-4`).
- Em mobile, o bloco `<iframe>` é suprimido e o arquivo passa a ser apenas um
  link de download — decisão de UX que evita o download automático no celular.
- Não há rótulos `<label>` associados aos selects de filtro (acessibilidade
  apenas por placeholder e `option` inicial "Ano"/"Mês").
- O texto da listagem é renderizado com `strip_tags`, sem marcação; o texto
  completo da view usa HTML sem escape.
