# Relatório Estatístico da Ouvidoria — Views e UX

> Todas as views citadas são da `v5`.

---

## 1. CMS — tela de listagem

**Arquivo:** `v5/resources/views/prefecture/relatorio-estatistico-ouvidoria/index.blade.php`
**Layout:** `@extends('admin.layouts.app')` → AdminLTE 1.22 (`v5/resources/views/admin/layouts/app.blade.php`)

Estrutura da tela:

| Bloco | Conteúdo |
|---|---|
| Cabeçalho | "Relatórios Estatísticos (Ouvidoria)" + botão **Cadastrar** (verde), exibido apenas se `has_access(['relatorio-estatistico-ouvidoria-cms.create'])` |
| Tabela | `<table id="tabela_relatorio-estatistico-ouvidoria">` com colunas: Nº (5%), Ano (10%), Descrição, Documento (12%, centralizado), Ações (12%, centralizado) |
| Corpo | `<tbody></tbody>` vazio — preenchido pelo DataTable via Ajax |
| Scripts | `assets/js/custom/config/relatorio-estatistico-ouvidoria/datatable.js` |

### 1.1 DataTable do CMS

`v5/public/assets/js/custom/config/relatorio-estatistico-ouvidoria/datatable.js`

```js
let collums = [
    { data: 'ano', render: ... },
    { data: 'descricao', render: ... },
    { data: 'arquivo', orderable: false, render: ... },   // ícone de PDF + download
    { data: null, orderable: false, className: 'text-center column-actions', render: ... }
];

runDatatables('relatorio-estatistico-ouvidoria', collums);
```

Características herdadas de `runDatatables()` (`v5/public/assets/js/custom/utilities/table.js`):

- Ajax em `GET /cms/datatable/relatorio-estatistico-ouvidoria` (JSON do Yajra);
- botões de exportação **Excel / PDF / CSV / TXT** (gerados no cliente, com título
  "Prefeitura de <cidade> - <data>");
- `stateSave: true`, ordenação pela primeira coluna, `dom: 'Blfrtip'`;
- a coluna "Nº" (campo `value` injetado pelo controller) é **não ordenável** e **não exportável**
  (`columns: ':visible:not(.column-actions)'`).

A coluna de ações renderiza, via `createBTN(id, url, data.edit, data.delete, delete_url)`:

| Botão | URL | Visibilidade |
|---|---|---|
| Editar | `relatorios-estatisticos-ouvidoria/editar/{id}` | `data.edit` (permissão `.edit`) |
| Excluir | `relatorios-estatisticos-ouvidoria/excluir/{id}` | `data.delete` (permissão `.delete`) |

> As URLs são **relativas** ao path da página corrente (`/cms/relatorios-estatisticos-ouvidoria`),
> e por isso resolvem para `/cms/relatorios-estatisticos-ouvidoria/editar/{id}` e
> `/cms/relatorios-estatisticos-ouvidoria/excluir/{id}`. A exclusão é executada por
> `myFetch(url, 'DELETE')` (`v5/public/assets/js/custom/utilities/functions.js:64-88`),
> acionada por `addDelete()` em `v5/public/assets/js/custom/utilities/table.js`.
> O mesmo padrão relativo é usado pelos módulos irmãos
> (`v5/public/assets/js/custom/config/obras-paralisadas/datatable.js`,
> `v5/public/assets/js/custom/config/concursos-pss/datatable.js`).

---

## 2. CMS — cadastro e edição

| Arquivo | Papel |
|---|---|
| `v5/resources/views/prefecture/relatorio-estatistico-ouvidoria/create.blade.php` | Wrapper do formulário de cadastro (`Form::open(['route' => 'relatorio-estatistico-ouvidoria-cms.store', 'files' => true, 'id' => 'form'])`) |
| `v5/resources/views/prefecture/relatorio-estatistico-ouvidoria/edit.blade.php` | Wrapper de edição (`Form::model($relatorio, ['route' => ['relatorio-estatistico-ouvidoria-cms.update', $relatorio->id], 'files' => true, 'method' => 'PUT'])`) |
| `v5/resources/views/prefecture/relatorio-estatistico-ouvidoria/form.blade.php` | Campos comuns aos dois |

Ambos os wrappers disablem o botão no envio e exibem um GIF de carregamento:

```blade
<button class="btn btn-primary btn-flat" id="btn-submit" onclick="desabilitaBotao()">
    {{ trans('adminlte::adminlte.register') }}
</button>
```

### 2.1 Campos do formulário

| Campo | Componente | Regras de UI | Observações |
|---|---|---|---|
| Ano | `@include('components.inputs.input')` (`v5/resources/views/components/inputs/input.blade.php`) | `maxlen` 10, placeholder `date('Y')` | JS remove não-dígitos e corta em 4 caracteres: `this.value.replace(/\D/g, '').slice(0, 4)` |
| Descrição | `@include('components.inputs.input')` | `maxlen` 255 | Valor anterior recuperado com `old()` |
| Arquivo PDF | `<input type="file" name="arquivo" accept=".pdf">` | opcional | Na edição, exibe o arquivo atual com botões de visualizar (`/storage/{arquivo}`) e download |

Bloco de erros no topo do form:

```blade
@if ($errors->any())
    <ul class="list-unstyled">
        @foreach ($errors->all() as $error)
            <li class="font-weight-bold bg-red text-light px-2 my-1">{{ $error }}</li>
        @endforeach
    </ul>
@endif
```

---

## 3. Portal — página pública

**Rota:** `/relatorio-estatistico-ouvidoria`
**View:** `portal.layouts.{template}.transparencia-fiscal.relatorio-estatistico-ouvidoria.index`,
onde `{template}` vem de `configurations.layout` (`getTemplateActived()`,
`v5/app/Http/Traits/GetInformationsPrefectureTrait.php:92-102`).

Existem **seis cópias** da página, uma por template:

| Arquivo | Observação |
|---|---|
| `v5/resources/views/portal/layouts/modelo1/transparencia-fiscal/relatorio-estatistico-ouvidoria/index.blade.php` | referência; id do container `#relatorio-estatistico`, sidebar quando `$content->layout == 1` |
| `v5/resources/views/portal/layouts/modelo2/transparencia-fiscal/relatorio-estatistico-ouvidoria/index.blade.php` | mesma estrutura, indentação e layout próprios |
| `v5/resources/views/portal/layouts/modelo3/transparencia-fiscal/relatorio-estatistico-ouvidoria/index.blade.php` | idem |
| `v5/resources/views/portal/layouts/modelo4/transparencia-fiscal/relatorio-estatistico-ouvidoria/index.blade.php` | idem (container com classe `row`) |
| `v5/resources/views/portal/layouts/modelo5/transparencia-fiscal/relatorio-estatistico-ouvidoria/index.blade.php` | idem |
| `v5/resources/views/portal/layouts/modelo6/transparencia-fiscal/relatorio-estatistico-ouvidoria/index.blade.php` | idem |

> O template `default` **não** possui essa pasta: se `configurations.layout` for `default`,
> a rota quebra com "view not found" (`portal.layouts.default.transparencia-fiscal.relatorio-estatistico-ouvidoria.index`).

### 3.1 Componentes incluídos (idênticos nos 6 templates)

| Include | Arquivo | O que renderiza |
|---|---|---|
| `portal.components.relatorio-estatistico.table` | `v5/resources/views/portal/components/relatorio-estatistico/table.blade.php` | Tabela **Ano / Descrição / Documento** com os PDFs cadastrados no CMS (ícone de visualizar + download em `/storage/{arquivo}`) |
| `portal.components.relatorio-estatistico-ouvidoria.stats` | `v5/resources/views/portal/components/relatorio-estatistico-ouvidoria/stats.blade.php` | 3 cartões de resumo + 3 gráficos de pizza (`#chart_div`, `#chart_tipo`, `#chart_genero`) |
| `portal.components.relatorio-estatistico-export-button` | `v5/resources/views/portal/components/relatorio-estatistico-export-button.blade.php` | Botões **xls / pdf / csv / txt** + função JS `exportarRelatorioEstatistico(type, url)` |
| `portal.components.view_pdf.files-list` | `v5/resources/views/portal/components/view_pdf/files-list.blade.php` | Arquivos anexados à página estática (exibidos quando há `archives` ou `view_pdf`) |
| `...relatorio-estatistico-ouvidoria.js` | `v5/resources/views/portal/layouts/modelo1/transparencia-fiscal/relatorio-estatistico-ouvidoria/js.blade.php` (e o equivalente em modelo2..modelo6) | Google Charts, modal Ajax e DataTable da listagem |

### 3.2 O que o cidadão vê, em ordem

1. Título "Ouvidoria - Relatório Estatístico" e borda decorativa.
2. Tabela com os relatórios PDF por ano de gestão (somente se houver registros).
3. Texto da página estática (`strip_tags($content->text)`).
4. Formulário de filtro: seletor "Ano de Gestão" + botão **Filtrar** + botão **Limpar filtro**.
5. Aviso "Nenhum registro encontrado." quando não há solicitações.
6. Cartões: **Pedidos Recebidos**, **Pedidos Atendidos**, **Pedidos Indeferidos**.
7. Três gráficos de pizza: **Status das Solicitações**, **Por Tipo de Manifestação**,
   **Por Gênero dos Solicitantes**.
8. Botões de exportação (xls, pdf, csv, txt).
9. Acordeão "Listagem completa das solicitações" (Status, Protocolo, Solicitante,
   Data do pedido, Solicitação) — exibido apenas se `full_list_requests_disabled` != 1.
10. Modal "Solicitação" com protocolo, descrição e respostas.
11. Bloco de arquivos da página estática.

### 3.3 Cartões de resumo

`v5/resources/views/portal/components/relatorio-estatistico-ouvidoria/stats.blade.php`

```php
$totalRecebidos   = $ombudsmanRequests->count();
$totalAtendidos   = $atendido_no_prazo + $atendido_apos_prazo;
$totalIndeferidos = $indeferido_no_prazo + $indeferido_apos_prazo;
```

> "Recebidos" é o total de solicitações **do filtro de ano** (lista completa), enquanto
> "Atendidos"/"Indeferidos" vêm dos contadores de prazo (que, para status 2 e 3, só contam
> solicitações **com resposta registrada**). Os dois universos não são exatamente o mesmo.

---

## 4. JavaScript e Ajax do portal

### 4.1 Gráfico de status (`js.blade.php`)

`google.charts.load('current', {'packages':['corechart']})` e um `PieChart` com 8 fatias
(4 status × dentro/fora do prazo). As fatias com valor `0` são removidas por
`.filter(function(r) { return r[1] > 0; })`. Se não houver linhas, o desenho é abortado.

### 4.2 Gráficos por tipo e por gênero (`charts-js.blade.php`)

```js
var porTipo    = @json($porTipo);
var porAssunto = @json($porAssunto);
var porGenero  = @json($porGenero);
```

- `porTipo` e `porGenero` alimentam os gráficos `#chart_tipo` e `#chart_genero`;
- **`porAssunto` é calculado no controller e serializado na view, mas não é exibido**
  (não existe `#chart_assunto` em nenhuma view) — dado e JS morto;
- o bloco é abortado quando `totalRecebidos === 0`.

### 4.3 Modal de solicitação (Ajax)

```js
$.ajax({
    type:'POST',
    url: "{{route('get.ombudsman.answers')}}",
    data: {_token: token, id: id},
    success: function (data) { ... }
});
```

Endpoint: `POST /ajax/get-answers/ouvidoria` (nome `get.ombudsman.answers`).
A resposta traz `data.request.protocol`, `data.request.mask_description` e
`data.answers[]` (texto e data de cada resposta). O nome do solicitante não é exibido.

### 4.4 Listagem completa

DataTable inicializada sobre `#solicitations` com `paging: true`, `searching: false`,
`ordering: false`, `info: false` e i18n em pt-BR
(`//cdn.datatables.net/plug-ins/1.10.16/i18n/Portuguese-Brasil.json`).
O botão `#btn-list` alterna a classe `d-none` da tabela.

### 4.5 Exportação

`exportarRelatorioEstatistico(type, url)` lê o `select[name="ano"]` da página e redireciona:

```js
window.location.href = url + '?' + params.toString();  // url = /relatorio-estatistico-ouvidoria/exportacao
```

Formatos: `xls`, `pdf`, `csv`, `txt`.

---

## 5. Exportações (visão geral)

| Formato | Nome do arquivo | Motor | Origem dos dados |
|---|---|---|---|
| XLSX | `ouvidoria-relatorio-estatistico.xlsx` | `maatwebsite/excel` + `OmbudsmanRequestsExport` (`FromView`) | `v5/resources/views/exports/ombudsman-requests.blade.php` |
| CSV | `ouvidoria-relatorio-estatistico.csv` | idem | idem |
| PDF | `ouvidoria-relatorio-estatistico.pdf` | `barryvdh/laravel-dompdf` (`PDF::loadView`) | idem (mesma view Blade) |
| TXT | `ouvidoria-relatorio-estatistico.txt` | PHP puro (`exportOmbudsmanRequestsTxt`) | colunas Status, Protocolo, Solicitante, Data do pedido |

Todas as exportações usam o **mesmo conjunto filtrado** do ano selecionado e aplicam a
máscara de protocolo e nome (`masked_protocol`, `masked_name`).

---

## 6. View órfã (não referenciada)

`v5/resources/views/portal/layouts/modelo1/informacoes-prefeitura/acesso-rapido/relatorio-estatistico-ouvidoria.blade.php`

Apesar do nome, o arquivo é uma **cópia da página de LAI** (Informação solicita ao
município): exibe "LAI - Relatório Estatístico", usa `$informationRequests`,
`$emTramitacao_no_prazo` e `date('Y')..1990` no seletor de ano. Nenhum `include` ou
`view()` no repositório a referencia (verificado por `grep -rn` em `v5/resources/views` e
`v5/app`) — é **código morto**. Ver `08-GAPS-E-DIVIDAS.md`.

---

## 7. Recursos visuais e assets

| Recurso | Origem |
|---|---|
| Google Charts | CDN `https://www.gstatic.com/charts/loader.js` (sem fallback offline) |
| DataTables (portal) | `assets/components/DataTables/datatables.min.js` |
| DataTables (CMS) | `assets/plugins/DataTables/datatables.min.js` + Buttons (carregados pelo layout AdminLTE) |
| Ícones | Font Awesome (`fa fa-chart-pie`, `fa fa-file-pdf-o`, `fa fa-download`, `fa fa-eye`) |
| Spinner do modal | CSS inline com animation `sk-waveStretchDelay` (definida no `<style>` da view do portal) |
