# Servidor — Views e UX

> **Módulo:** `servidor` (tabela `modulos`, id 35)

---

## 1. Views do CMS

Diretório: `v6/resources/views/prefecture/contents/servidor/`

| Arquivo | Função |
|---|---|
| `index.blade.php` | Tela "Lista de Servidor": cabeçalho com o botão "Cadastrar Servidor", botão de exclusão em lote e o esqueleto `<table id="tabela_servidor">`. |
| `create.blade.php` | Formulário de **cadastro e edição** (a edição reutiliza esta view). |
| `form.blade.php` | Partial com os campos, compartilhado por create e edit. |

> **Não existe `edit.blade.php`.** `ServidorController@edit()` renderiza
> `prefecture.contents.servidor.create` — a distinção entre "cadastrar" e "editar" é
> feita por `isset($data)` dentro da view.

Todas estendem `admin.layouts.app` (AdminLTE 1.22).

### 1.1 `index.blade.php`

| Elemento | Detalhe |
|---|---|
| Título | "Lista de {{ $title }}" (`$title = 'Servidor'`) |
| Botão "Cadastrar Servidor" | `@if (has_access(['servidor.create']))` → `route('servidor.create')` |
| Botão "Excluir Registro(s)" | `@if (has_access(['servidor.delete']))` → `@component('prefecture.components.deleteByLoteSubmitButton', ['route' => 'servidor.destroyAll'])` |
| Tabela | `<table id="tabela_servidor" class="table table-striped w-100">` |
| Colunas | checkbox mestre (só com `has_access(['servidor.delete'])`), ID (5%), Servidor (5%), CPF, Ações |
| Scripts | `assets/js/custom/utilities/deleteAll.js`, `assets/js/custom/utilities/table.js`, `assets/js/custom/contents/servidor/datatable.js` |

> Bug cosmético: na linha do `table.js` há um `/` literal depois da tag `<script>`
> (</script>/), que é impresso na página como texto.

### 1.2 `create.blade.php`

```blade
@php $route = !isset($data) ? ['servidor.store'] : ['servidor.update', $data->id] @endphp

{{ Form::open(['route' => $route, 'files' => true]) }}

@isset($data)
    {{ method_field('PUT') }}
@endisset

{!! csrf_field() !!}
@include('prefecture.contents.servidor.form')
```

- Título: `{{ isset($data) ? "Editar $title" : "Cadastrar $title" }}`.
- `method_field('PUT')` só quando é edição.
- Botão "Voltar" → `route('servidor.index')`.

### 1.3 `form.blade.php` — campos

Todos os campos usam os componentes `components/inputs/input.blade.php` e
`components/inputs/select.blade.php`.

**Legendagem "Dados cadastrais"**

| Campo | `id`/`name` | Tamanho | Obrigatório | Máscara | Origem do valor |
|---|---|---|---|---|---|
| Nome | `name` | col-sm-8 | sim | — | `$data->name` ?? `old('name')` |
| CPF | `cpf` | col-sm-4 | sim | `cpf` | `$data->cpf` ?? `old('cpf')` |
| Identidade | `identidade` | col-sm-3 | sim | — | `$data->identidade` ?? `old('identidade')` |
| Matrícula | `matricula` | col-sm-3 | sim | — | `$data->matricula` ?? `old('matricula')` |
| Cargo | `cargo` | col-sm-3 | sim | — | `$data->cargo` ?? `old('cargo')` |
| Secretaria | `secretaria` | col-sm-3 | não | — | select com `$secretarias` |

**Legendagem "Dados bancários"**

| Campo | Tamanho | Obrigatório |
|---|---|---|
| Banco | col-sm-4 | não |
| Conta | col-sm-4 | não |
| Agência | col-sm-4 | não |

Observações:

- A máscara `cpf` é aplicada pela classe CSS `mask cpf` no `<input>`; o treatment
  acontece por JavaScript do AdminLTE/jQuery Mask no navegador.
- A identidade recebe máscara `00.000.000-0` via jQuery no bloco `@section('other-scripts')`
  do próprio `form.blade.php`.
- O `select` de secretaria (`components/inputs/select.blade.php`) tem uma option inicial
  `value='0'` com o texto "Selecione"; as demais opções usam `$content->id` como value e
  `$content->name` como rótulo (por isso o controller carrega `['id', 'title as name']`).
- O componente `select` também sabe formatar CPF (`{{ mask(str_replace(['.', '-'], '', $content->cpf), '###.###.###-##') }}`) quando o objeto
  tem `cpf` — usado no formulário de Requerimento de Diárias.
- Inclui `@include('components.layout.errors')` e `@include('components.layout.mandatory_help')`.
- Carrega Summernote (`assets/plugins/summernote/summernote.min.js` etc.) **sem que
  exista qualquer campo de texto rico** no formulário — carregamento desnecessário.

## 2. JavaScript

| Arquivo | Papel |
|---|---|
| `v6/public/assets/js/custom/contents/servidor/datatable.js` | Colunas e inicialização da DataTable do CMS. |
| `v6/public/assets/js/custom/utilities/table.js` | `runDatatables()`, `createBTN()`, `addDelete()`, `addDeleteAll()`, botões de export Excel/PDF. |
| `v6/public/assets/js/custom/utilities/deleteAll.js` | `submitDeleteAll()` — envia `toDelete[]` para `servidor.destroyAll`. |
| `v6/public/assets/js/custom/utilities/functions.js` | `myFetch()` — `fetch` com `X-CSRF-TOKEN`. |

### `datatable.js` (fonte)

```js
let collums = [
    { data: 'name', render: function (data) { return `<div class='p-10-0' >${data}</div>`; } },
    { data: 'cpf',  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, `servidor/${data.id}/edit`, data.edit, data.delete, `servidor/${data.id}`)
                + `</div>`;
        }
    }
];

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

- `runDatatables('servidor', $colunas, true, 'ASC', null, true)` monta o AJAX em `/cms/datatable/servidor`
  (`table.js:181-185`).
- Os botões de editar/excluir usam **URLs relativas** (`servidor/{id}/edit`,
  `servidor/{id}`), resolvidas pelo navegador a partir de `/cms/servidor` →
  `/cms/servidor/{id}/edit` e `/cms/servidor/{id}`.
- O botão de lixeira usa o atributo `target` do botão como URL do DELETE
  (`table.js:466-489`), com `myFetch(url, 'DELETE')` — casa exatamente com a rota
  `DELETE /cms/servidor/{servidor}` do resource.
- A coluna `value` (row_number da view) é inserida automaticamente por `runDatatables`
  e aparece na coluna "ID" — ou seja, a coluna **não** mostra o `servidor.id`, mas uma
  numeração sequencial por data de criação.
- `deleteAll = true` insere a coluna de checkbox em lote.
- Não há polling: a tabela só recarrega após uma ação (`datatable.ajax.reload()`).

### Exclusão em lote (payload)

`v6/public/assets/js/custom/utilities/deleteAll.js`:

```js
const toDelete = $('input[name="toDeleteAll[]"]:checked').map(function () {
    return this.value
}).get();

$.ajax({
    url: routeDelete,               // /cms/servidor/destroyAll
    method: 'POST',
    dataType: 'json',
    data: {
        _token: $('meta[name="csrf-token"]').attr('content'),
        toDelete: toDelete
    },
    success: function (response) {
        Swal.fire('Registro(s) deletados com sucesso!', '', 'success');
    }
});
```

O controller lê `$request->toDelete` — array de ids.

## 3. Exportação

O módulo **não possui exportação própria** (ao contrário do módulo `newsletters`, que
tem uma rota `export`). O que existe são os botões **Excel** e **PDF** padrão do
DataTables, gerados por `runDatatables` (`table.js:30-120`), que exportam as colunas
visíveis da tabela — portanto apenas `value`, `name` e `cpf` (a view `servidores` não
expõe cargo, secretaria nem dados bancários). O título gerado é
`Prefeitura de {cidade} - {data}`.

## 4. O que o usuário final vê (portal)

O módulo não tem tela própria no portal. A única interação indireta é a busca pública
de requerimentos de diárias:

- Rota: `ANY /requerimento-de-diarias` (`v6/routes/web.php:211`).
- Controller: `Portal\BuscaDiariasController@index`.
- Filtros: `req_name` (nome do servidor), `cpf`, `num_diaria`, `data_saida`, `data_retorno`.
- Os nomes e CPFs são comparados com `whereRaw` usando `translate(lower(campo), 'àáâãäéèëêíìïîóòõöôúùüûçÇ', 'aaaaaeeeeiiiiooooouuuucc') ilike`
  para ignorar acentuação; o resultado é um `pluck('id')` de servidores usado em
  `whereIn('id_servidor', $ids)`.
- A view é `portal.layouts.busca-diarias.show`, com paginação de 10 registros.

## 5. O que o operador vê (CMS)

1. Menu lateral "Servidores" (ícone `users`) → `/cms/servidor`.
2. Lista com ID, Servidor, CPF e Ações; ordenação ascendente; botões de Excel/PDF.
3. Botão "Cadastrar Servidor" → formulário com duas legendas.
4. Edição pela ação de lápis da linha → **a mesma tela de cadastro**, com os valores
   preenchidos e o `PUT` implícito.
5. Exclusão pela lixeira da linha (confirmação via `confirmation` plugin) ou em lote
   (seleção múltipla + confirmação via SweetAlert).

> O `index.blade.php` **não** exibe cargo, secretaria, matrícula, identidade nem dados
> bancários: para ver qualquer um deles é preciso abrir o registro.
