# Glossário — Views e UX

> Views do CMS (AdminLTE 1.22 / Bootstrap 3) e do portal (Bootstrap 4), com um arquivo
> por layout — o conteúdo do glossário está duplicado em 8 arquivos de portal.

---

## 1. CMS — `v6/resources/views/prefecture/contents/glossario/`

| Arquivo | Função | Observações |
|---|---|---|
| `index.blade.php` | Listagem com DataTable `#tabela_glossario` | Colunas: (checkbox de lote), ID, Título, Ações; botão "Cadastrar Conteúdo" (`cadastrar/glossario`) |
| `create.blade.php` | Formulário de cadastro | `Form::open(['route' => ['conteudo.index']])` + `@include('prefecture.contents.glossario.form')` |
| `edit.blade.php` | Formulário de edição | `Form::model($content, ['route' => ['conteudo.update', $content->id], 'files' => true, 'method' => 'PUT'])` |
| `form.blade.php` | Campos | `category_contents_id` (hidden), **Título** (input, maxlen 120), **Texto** (textarea 12x8) |

Trecho do formulário (real):

```blade
{{-- v6/resources/views/prefecture/contents/glossario/form.blade.php --}}
<div class="form-group {{ $errors->has('title') ? 'has-error' : '' }}">
    @include('components.layout.errors')
    <input type="hidden" name="category_contents_id" value="{{ $categoryContent->id }}">

    @include('components.inputs.input', [
        'id' => 'title',
        'titulo' => 'Título',
        'maxlen' => '120',
        'placeholder' => 'Título',
        'mandatory' => true,
        'data' => isset($content) ? $content->title : old('title'),
    ])
</div>

@include('components.inputs.textarea', [
    'id' => 'text',
    'titulo' => 'Texto',
    'placeholder' => 'Texto',
    'mandatory' => true,
    'cols' => 12,
    'rows' => 8,
    'data' => isset($content) ? $content->text : old('text'),
])
```

O formulário **não** expõe status, data de publicação, galeria, ordem nem link — o
verbete é o mínimo: termo + definição.

## 2. CMS — JavaScript do DataTable

Arquivo: `v6/public/assets/js/custom/contents/glossario/datatable.js`

```js
let collums = [
    {
        data: 'titulo',
        render: function (data) {
            return `<div class="p-10-0">${data}</div>`;
        }
    },
    {
        data: null,
        name: "actions",
        orderable: false,
        render: function (data) {
            return `<div class='flex justify-center vertical-middle'>`
                + createBTN(data.id, `editar/glossario/${data.id}`, data.edit, data.delete)
                + `</div>`;
        }
    }
];

runDatatables('glossario', collums, true, 'ASC', null, true);
```

- `runDatatables('glossario', collums, true, 'ASC', null, true)` monta o DataTable com `ajax.url = '/cms/datatable/glossario'`
  (`v6/public/assets/js/custom/utilities/table.js:181-184`), ou seja, a rota nomeada
  `datatable.glossario`.
- A ordenação `'ASC'` é aplicada à view, que já vem ordenada por `created_at DESC`.
- `createBTN` usa as flags `data.edit` / `data.delete` vindas de `struct_views`.

Scripts auxiliares: `assets/js/custom/utilities/deleteAll.js` (exclusão em lote),
carregado em `index.blade.php:54`.

## 3. Portal — arquivos por layout

| Layout | Arquivo | Conteúdo |
|---|---|---|
| modelo1..modelo6 | `v6/resources/views/portal/layouts/modelo1/transparencia/glossario.blade.php` (idem em modelo2..modelo6) | Índice A–Z + `<ul class="contentGlossary">` preenchido por AJAX |
| newsbit | `v6/resources/views/portal/layouts/newsbit/transparencia/glossario.blade.php` | Mesma ideia, com CSS próprio (`ol#myTab`, `<dl>`) |
| modelo1..modelo6 | `v6/resources/views/portal/layouts/modelo1/glossario/show.blade.php` (idem em modelo2..modelo6) | Verbete individual (resultado da busca) |
| default | `v6/resources/views/portal/layouts/default/pages/contents/glossario/show.blade.php` | Verbete individual + índice A–Z server-side (`$contents`, `$azRange`) |

> O layout `default` **não** possui `portal/layouts/default/transparencia/glossario.blade.php`,
> que é a view requerida por `GlossaryController@index`. Com o template `default` ativo,
> `/glossario` não resolve a view.

## 4. Portal — o "letter ajax" em detalhe

Modelo1 (mesmo padrão nos demais modelos):

```blade
{{-- v6/resources/views/portal/layouts/modelo1/transparencia/glossario.blade.php --}}
<div class="btn-group mb-3  mx-auto d-table bg-color-2 text-center rounded" style="width: 100%;" role="group"
    aria-label="Letras">
    @foreach ($azRange as $letter)
        <button type="button" id="{{ $letter }}" style="cursor:pointer;"
            class="btn-letter btn bg-color-2 opacity text-light"
            onclick="getGlossarioByLetter('{{ $letter }}');">
            {{ $letter }}
        </button>
    @endforeach
</div>
<ul class="list-unstyled text-secondary contentGlossary">
</ul>
```

```js
$(document).ready(function() {
    getGlossarioByLetter('all');
});

function getGlossarioByLetter(letter) {
    activeLetter(letter);

    $.ajax({
        type: 'GET',
        url: `glossario-ajax/${letter}`,
        success: function(data) {
            $(".contentGlossary").html('');
            var listGlossary = ``;
            $.each(data['contentGlossary'], function(index, item) {
                listGlossary += `<li>
                                <h5><strong>${item['title']}</strong></h5>
                                <p>
                                    ${item['text']}
                                </p>
                                <hr>
                            </li>`;
            });
            if (listGlossary == '') {
                listGlossary = `<li>Não foi encontrado glossário para essa letra</li>`
            }
            $(".contentGlossary").append(listGlossary);
        },
        error: function() {
            // Erro
        }
    });
}
```

Características do padrão:

| Aspecto | Comportamento |
|---|---|
| Endpoint | `GET glossario-ajax/{letra}` (URL **relativa**: funciona na raiz, quebra em subpastas) |
| Carga inicial | `all` -> string vazia -> todos os verbetes |
| Feedback de vazio | "Não foi encontrado glossário para essa letra" |
| Tratamento de erro | Bloco `error` vazio: falha silenciosa, lista fica vazia |
| Estado ativo | Classe `bg-color-active` alternada por `activeLetter(letter)` |
| Renderização | HTML montado por concatenação de strings (sem escaping) |

## 5. Busca global no portal

- `v6/app/Http/Controllers/Portal/PortalController.php:265` e `:251` incluem `'glossario'`
  na lista de slugs buscáveis.
- `v6/app/Http/Filter/FilterSearchAllSite.php:47-66` faz a busca com
  `translate(lower(title), 'àáâãäéèëêíìïîóòõöôúùüûçÇ', 'aaaaaeeeeiiiiooooouuuucc') ilike '%termo%'`
  — aqui **sim** há normalização de acentos e ausência de distinção de maiúsculas.
- `PortalController@getViewToBeReturned` (linhas 391-402) mapeia o id da categoria pai
  para a view: `11 => 'glossario.show'`, `12 => 'perguntas-frequentes.show'`.
- As views `glossario/show.blade.php` existem em modelo1..modelo6; **não** existem em
  `default` nem em `newsbit` (esses possuem apenas `pages/contents/glossario`), o que quebra o
  clique no resultado da busca nesses templates.

Exemplo de item de resultado (modelo1/glossario/show.blade.php):

```blade
<div class="text-secondary text-content p-b-50">{!! $content->text !!}</div>
```

## 6. O que o usuário final vê

1. `/glossario`: título "Glossário", índice A–Z centralizado, lista vazia até o AJAX responder.
2. Clique na letra: a lista é substituída pelos verbetes que começam com a letra
   (comparação sensível a maiúsculas, ver `05-REGRAS-E-PORTABILIDADE.md`).
3. Busca global: o resultado abre o verbete em página própria com breadcrumb
   "Home > Glossário > <termo>".

## 7. Exportações

O portal **não** exporta verbetes. No CMS, o DataTable do glossário herdou de
`runDatatables` os botões de exportação **Excel** e **PDF**
(`v6/public/assets/js/custom/utilities/table.js:29-33` e bloco `pdf` a partir da linha 37),
que operam sobre os dados já carregados (exportação client-side do DataTables), com o
título `Prefeitura de {cidade} - {data}`.
