# Ouvidoria — Views e UX

## 1. Views do CMS — `v6/resources/views/prefecture/ombudsman/`

Todas estendem `admin.layouts.app` (AdminLTE 1.22 / Bootstrap 3).

| Diretório | Arquivo | Função |
|---|---|---|
| `ombudsman/` | `index.blade.php` | **Órfã.** Markup de "Lista de Chamados de Ouvidoria" mas com JS de **Feriados Municipais** (`tabela_feriados_municipais`, `contents/feriados-municipais/datatable.js`) e `has_access(['feriados-municipais.delete'])`. Não referenciada por controller. |
| `ombudsman/` | `edit.blade.php` | **Órfã.** Formulário "Configurações de Ouvidoria" (endereço, telefone, horário, e-mail, responsável, prazo, descrição). Sem controller associado. |
| `ombudsman/` | `form.blade.php` | **Órfã.** Partial dos campos de configuração. |
| `ombudsman/request/` | `index.blade.php` | Listagem de chamados (DataTable `tabela_chamados-da-ouvidoria`); inclui `modal_show` e `modal_send`; carrega `modals.js` e `datatable.js`. |
| `ombudsman/request/` | `modal_show.blade.php` | Modal "Detalhes do Chamado" (preenchido por JS). |
| `ombudsman/request/` | `modal_send.blade.php` | Modal "Enviar Chamado" — select de assunto (`title_subject`), campo `new_email`, `ombudsman_request_id`; POST `enviar_chamado`. |
| `ombudsman/request/answer/` | `create.blade.php` | Tela de **resposta** (~360 linhas): cabeçalho com os dados do chamado, **histórico de tramitação** (logs), editor Bootstrap-WYSIHTML5, respostas prontas, anexos (`archive[]`), POST `enviar_resposta` com `type=1`. |
| `ombudsman/request/answer/` | `reject.blade.php` | Tela de **indeferimento** (~229 linhas): mesmos dados, campo `rejected`, POST `enviar_resposta` com `type=0`. |
| `ombudsman/subject/` | `index`, `create`, `edit`, `form` | CRUD de Assuntos — `form.blade.php` com select de **Setor/Órgão** (`sector_id`), título, e-mail, responsável. |
| `ombudsman/sector/` | `index`, `edit`, `form` | CRUD de Setores/Órgãos — `form.blade.php` com título e checkbox "Ativo". Não há `create.blade.php` (o `create` redireciona para o índice). |
| `ombudsman/origin/` | `index`, `create`, `edit`, `form` | CRUD de Origens de Chamado |
| `ombudsman/manifestation-type/` | `index`, `create`, `edit`, `form` | CRUD de Tipos de Chamado |
| `ombudsman/response-method/` | `index`, `create`, `edit`, `form` | CRUD de Formas de Resposta |
| `ombudsman/canned-response/` | `index`, `create`, `edit`, `form` | CRUD de Respostas Prontas (com editor Summernote/WYSIHTML5) |

### 1.1 Tela de listagem de chamados

`v6/resources/views/prefecture/ombudsman/request/index.blade.php` — colunas do DataTable:

| Coluna | Campo da view `chamados_ouvidoria` |
|---|---|
| ID | `value` (row_number) |
| Protocolo | `protocolo` |
| Tipo de Chamado | `tipo_chamado` |
| Assunto | `assunto` |
| Setor | `setor` |
| Modalidade | `modalidade` |
| Status | `status` |
| Data do Chamado | `data_criacao` |
| Ações | — |

### 1.2 Tela de resposta — histórico de tramitação

`v6/resources/views/prefecture/ombudsman/request/answer/create.blade.php` renderiza `$request_logs` (carregado pelo controller) com, para cada log: data, usuário (`$log->user->name`), setor origem/destino (`$log->sectorFrom->title` / `$log->sectorTo->title`), assunto origem/destino e observação.

> **Bug de view verificado:** o bloco exibe `{{$log->subjectFrom->title}}` e `{{$log->subjectTo->title}}` **sem** o `optional()` — enquanto setor usa `optional($log->sectorFrom)->title ?? '-'`. Se um log tiver `subject_from_id` nulo (o que ocorre quando o `subject_id` do chamado é alterado), o acesso a `$log->subjectFrom->title` sobre `null` gera **erro fatal** na renderização da tela de resposta. Ver `08-GAPS-E-DIVIDAS.md`.

## 2. Views de e-mail — `v6/resources/views/prefecture/emails/`

| Diretório | Arquivo | Quando é usado |
|---|---|---|
| `ombudsman-requests/` | `email_user.blade.php` | Confirmação ao cidadão, no ato do envio |
| `ombudsman-requests/` | `email_admin.blade.php` | Aviso ao setor responsável (destinatário = e-mail do assunto) |
| `ombudsman-requests/` | `email_header.blade.php`, `email_footer.blade.php` | Cabeçalho e rodapé comuns |
| `ombudsman-send-requests/` | `email_admin.blade.php` | Reenvio do chamado após troca de assunto/setor |
| `ombudsman-send-requests/` | `email_header.blade.php`, `email_footer.blade.php` | — |
| `ombudsman-answer/` | `email_answer_attach.blade.php` | Resposta **com** anexo |
| `ombudsman-answer/` | `email_answer_no_attach.blade.php` | Resposta **sem** anexo |
| `ombudsman-answer/` | `email_rejected.blade.php` | Indeferimento |
| `ombudsman-answer/` | `email_to_extend.blade.php` | Prorrogação de prazo |
| `ombudsman-answer/` | `email_header.blade.php`, `email_footer.blade.php` | — |

> **Diretório `teste/`** — dentro de `portal/layouts/modelo{1,2,5,6}/informacoes-instituicao/ouvidoria/teste/` há `email_admin.blade.php`, `email_header.blade.php`, `email_footer.blade.php` em **modelos diferentes** (modelo1, modelo2, modelo5 e modelo6 — o modelo3 e o modelo4 não têm). São **templates de teste** no portal, sem uso pelo controller (o controller usa sempre `prefecture.emails.*`). Ver `08-GAPS-E-DIVIDAS.md`.

## 3. Views do Portal

### 3.1 Página da Ouvidoria — `informacoes-instituicao/ouvidoria/`

| Arquivo (6 modelos) | Função |
|---|---|
| `index.blade.php` | Página institucional: título "Ouvidoria", texto da página estática (`{!! $contents->text !!}`) e, se não houver link externo, o include de `form-contact.blade.php`. |
| `form-contact.blade.php` | Formulário de manifestação (POST `ouvidoria.store`). |
| `email-admin/email_header.blade.php`, `email_template.blade.php`, `email_footer.blade.php` | Template de e-mail administrativo por modelo (não usado pelo controller, que usa `prefecture.emails.*`). |

### 3.2 Formulário de manifestação — `form-contact.blade.php`

Campos, na ordem:

| Campo | Tipo | Observação |
|---|---|---|
| `modality` | select | "Selecione a modalidade do chamado" — opções do array `$modalities` (Identificado / Sigiloso / Anônimo) |
| `type_id` | select | Tipos de chamado com `status = true` |
| `subject_id` | select | Assuntos agrupados por `sector_name` em `<optgroup>` |
| `response_method_id` | select | Formas de resposta |
| `origin_id` | select | Origens |
| `email`, `name`, `phone` | inputs | Desabilitados pelo JS quando a modalidade é anônima |
| `address`, `number`, `neighborhood`, `reference_point`, `complement` | inputs | Idem |
| `description` | textarea | "Mensagem:" (6 linhas) |
| `token` | input | "Digite um token para poder contestar a resposta posteriormente" — **obrigatório** pela validação do controller |
| captcha | `{!! Recaptcha::render() !!}` | Exibido, mas **não validado** (regra comentada) |
| — | `components.inputs.archive` | Bloco de anexos (apesar de o controller do portal **não** processar uploads) |

### 3.3 Consulta por protocolo — `informacoes-instituicao/acesso-rapido/consulta-solicitacao-ouvidoria.blade.php`

Estrutura da tabela exibida: Status (com cor por `status_id`), Protocolo, Solicitante, Data do pedido, Descrição do pedido, lista de respostas e contestações.

Lógica de cores do status (JS inline):

| `status_id` | Rótulo | Cor |
|---|---|---|
| 1 | EM TRAMITAÇÃO | `#FDC02F` |
| 2 | ATENDIDO | `#30A64A` |
| 4 | PRORROGADO | `#25A2B7` |
| 3 | INDEFERIDO | `#DA3849` |

> O bloco de contestações **exibe `response.description` e `response.name` em claro** — o endpoint `ajax.get.ombudsman.by.protocol` devolve o model completo. Ver `08-GAPS-E-DIVIDAS.md` (segurança/LGPD).

### 3.4 Relatório estatístico — `transparencia-fiscal/relatorio-estatistico-ouvidoria/`

| Arquivo (6 modelos) | Função |
|---|---|
| `index.blade.php` | Página do relatório: título, filtros (ano), indicadores (em tramitação / prorrogado / indeferido / atendido, cada um em "no prazo" e "após o prazo"), gráfico e tabela. |
| `js.blade.php` | Script do gráfico (Google Charts) e das exportações. |

> **Layout divergente verificado:** o modelo **não** tem `relatorio-estatistico-ouvidoria/js.blade.php` — os modelos 1, 2, 3, 4, 5 e 6 têm. Já `ouvidoria-solicitada.blade.php` existe nos 6.

### 3.5 Confirmação de envio — `transparencia-fiscal/ouvidoria-solicitada.blade.php`

Mostra o protocolo gerado e o prazo de resposta (`configurations.response_time` — valor atual no banco: `20`).

### 3.6 Layout `default` e `newsbit`

| Layout | Arquivos | Observação |
|---|---|---|
| `default/pages/statics/ouvidoria/` | `contact.blade.php`, `form-contact.blade.php`, `tutorial.blade.php`, `email_admin.blade.php`, `email_header.blade.php`, `email_footer.blade.php` | Página estática padrão de Ouvidoria/Contato — usada como **fallback** pelo `getStaticPageData('ouvidoria') ?: getStaticPageData('contato')` |
| `newsbit/informacoes-instituicao/ouvidoria/` | `index.blade.php` | Versão newsbit — **sem** `form-contact.blade.php` nesse layout. [NÃO VERIFICADO] em runtime o que acontece ao renderizar. |

## 4. JavaScript

### 4.1 CMS — `v6/public/assets/js/custom/contents/ouvidoria/`

| Arquivo | Endpoints chamados |
|---|---|
| `chamados-da-ouvidoria/datatable.js` | `GET /cms/datatable/chamados-da-ouvidoria`; botões de ação apontam para `chamado-da-ouvidoria/responder/{id}` e `chamado-da-ouvidoria/prorrogar/{id}` |
| `chamados-da-ouvidoria/modals.js` | `GET /cms/chamado-da-ouvidoria/{id}` (2×) — preenche o modal de detalhes |
| `chamados-da-ouvidoria/modal_show.js` | `GET /cms/chamado-da-ouvidoria/{id}`; redireciona para `chamado-da-ouvidoria/responder/{id}` e `chamado-da-ouvidoria/indeferir/{id}` |
| `chamados-da-ouvidoria/modal_send.js` | `POST chamado-da-ouvidoria/enviar` com `{new_email, ombudsman_request_id, title_subject}` |
| `respostas-da-ouvidoria/responder_chamado.js` | `GET /cms/respostas-prontas/api` — injeta as respostas prontas no menu do WYSIHTML5; gerencia as linhas de anexo (`archive[]`) |
| `respostas-da-ouvidoria/indeferir_chamado.js` | `GET /cms/respostas-prontas/api` |
| `assuntos/datatable.js` | `GET /cms/datatable/assuntos` |
| `setores-orgaos/datatable.js` | `GET /cms/datatable/setores-orgaos` |
| `tipos-de-chamado/datatable.js` | `GET /cms/datatable/tipos-de-chamado` |
| `formas-de-resposta/datatable.js` | `GET /cms/datatable/formas-de-resposta` |
| `respostas-prontas/datatable.js` | `GET /cms/datatable/respostas-prontas` |
| `origens-de-chamado/datatable.js` | `GET /cms/datatable/origens-de-chamado` |

### 4.2 Portal — `v6/public/assets/js/custom/portal/ouvidoria.js`

**Sem AJAX.** Apenas habilita/desabilita os campos pessoais conforme a modalidade:

```js
modalitySelect.addEventListener('change', function(e) {
    if (e.target.value == 'anonimo') {
        nameInput.disabled = true;   nameInput.value = '';
        emailInput.disabled = true;  emailInput.value = '';
        phoneInput.disabled = true;  phoneInput.value = '';
        addressInput.disabled = true; addressInput.value = '';
        neighborhoodInput.disabled = true; neighborhoodInput.value = '';
        numberInput.disabled = true; numberInput.value = '';
        referenceInput.disabled = true; referenceInput.value = '';
        complementInput.disabled = true; complementInput.value = '';
    } else { /* reabilita */ }
});
```

> **Risco verificado:** o valor comparado é `'anonimo'` (sem acento). O array `$modalities` do controller usa a chave `'anonimo'` — consistente. Mas `modality` é validado como `integer|not_in:0` no controller (§3.3 de `03-ROTAS-E-CONTROLADORES.md`). O comportamento real precisa de teste em ambiente.

### 4.3 AJAX embutido nas views do portal

| View | Endpoint |
|---|---|
| `consulta-solicitacao-ouvidoria.blade.php` | `POST {{ route('ajax.get.ombudsman.by.protocol') }}` |
| `consulta-solicitacao-ouvidoria.blade.php` | `POST {{ route('ajax.get.answer.by.ombudsman') }}` |
| `consulta-solicitacao-ouvidoria.blade.php` | `POST {{ route('new.message.ombudsman') }}` (contestação) |
| `relatorio-estatistico-ouvidoria/js.blade.php` | `POST {{ route('get.ombudsman.answers') }}` |
| `relatorio-estatistico-ouvidoria/js.blade.php` | `window.location` para `relatorio-estatistico-ouvidoria?...&type=xls\|pdf\|csv\|txt` |

## 5. Exportações

### 5.1 Relatório estatístico (portal)

`Portal\OmbudsmanController::export($ombudsmanRequests, $type)`:

| `type` | Formato | Implementação |
|---|---|---|
| `xls` | `.xlsx` | `Excel::download(new OmbudsmanRequestsExport($ombudsmanRequests), 'relatorio-estatistico-ouvidoria.xlsx')` |
| `csv` | `.csv` | `Excel::download(..., 'relatorio-estatistico-ouvidoria.csv')` |
| `pdf` | `.pdf` | `PDF::loadView('exports.ombudsman-requests', ['ombudsmanRequests' => …])->download('relatorio-estatistico-ouvidoria.pdf')` |
| qualquer outro | `.txt` | `exportTxt()` — texto formatado com `Content-Type: plain/txt` e `Content-Disposition: attachment` |

### 5.2 `OmbudsmanRequestsExport`

`v6/app/Exports/OmbudsmanRequestsExport.php` implementa `FromView` e renderiza `resources/views/exports/ombudsman-requests.blade.php` — uma tabela HTML com 4 colunas:

| Coluna | Valor |
|---|---|
| Status | `$ombudsmanRequest->status->name` |
| Protocolo | `$ombudsmanRequest->masked_protocol` |
| Solicitante | `$ombudsmanRequest->masked_name` |
| Data do pedido | `date('d/m/Y', strtotime($ombudsmanRequest->created_at))` |

> O export **não** inclui a descrição da manifestação (por decisão de LGPD), nem tipo, assunto, setor ou modalidade.

### 5.3 Exportação dos DataTables do CMS

Herdada de `runDatatables` (`v6/public/assets/js/custom/utilities/table.js`): Excel, PDF (landscape A4), CSV e TXT das colunas visíveis.

> **Risco de LGPD verificado:** a exportação do DataTable de chamados usa a view `chamados_ouvidoria`, que expõe `protocolo` (em claro), `descricao_resumida` (100 primeiros caracteres da descrição) e `endereco` do cidadão. O botão de exportar aparece na listagem do CMS.

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

### 6.1 Cidadão

1. Acessa `/ouvidoria` → lê o texto institucional → preenche o formulário.
2. Escolhe modalidade, tipo, assunto, forma de resposta e origem; escreve a mensagem; define um token de contestação.
3. Envia → recebe a página "Ouvidoria Solicitada" com o protocolo e o prazo.
4. Mais tarde, em "Consultar Solicitação de Ouvidoria", informa o protocolo → vê status, solicitante, data, descrição e as respostas.
5. Se discordar, usa o token para registrar até 2 contestações.
6. No "Relatório Estatístico da Ouvidoria", escolhe o ano → vê os indicadores e baixa a lista (xls/csv/pdf/txt).

### 6.2 Operador (CMS)

1. `/cms/chamados-da-ouvidoria` → DataTable com colunas e 4 botões de ação por linha.
2. **Visualizar** → modal com os dados completos do chamado (inclusive IP e browser).
3. **Enviar** → modal para trocar o assunto e o destinatário do e-mail (registra log `forward`).
4. **Responder** → tela com os dados, o histórico de tramitação, o editor WYSIHTML5 com respostas prontas e anexos.
5. **Prorrogar** → muda o status para 4 e notifica o cidadão.
6. CRUDs de apoio para manter os cadastros que alimentam o formulário do portal.

## 7. Assets

| Asset | Situação |
|---|---|
| `v6/public/assets/js/custom/contents/ouvidoria/**` (11 arquivos) | existem |
| `v6/public/assets/js/custom/portal/ouvidoria.js` | existe |
| `assets/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.min.js` | referenciado em `answer/create.blade.php`; **existe** em `v6/public/assets/plugins/bootstrap-wysihtml5/` |
| `assets/js/custom/utilities/deleteAll.js` | existe (usado pelas views de índice) |
| `assets/js/custom/utilities/table.js` | existe (DataTables genérico) |

## 8. Layouts do portal usados

| Modelo | Ouvidoria | Relatório estatístico | `home/index` (exigido por `getTemplateActived`) |
|---|---|---|---|
| `modelo1` | sim | sim | sim |
| `modelo2` | sim | sim | sim |
| `modelo3` | sim | sim | sim |
| `modelo4` | sim | sim | sim |
| `modelo5` | sim | sim | sim |
| `modelo6` | sim | sim | sim |
| `newsbit` | **só `index.blade.php`** (sem `form-contact`) | **não** | **não** → 404 |
| `default` | `pages/statics/ouvidoria/` (6 arquivos) | **não** | **não** → 404 |

> **Risco operacional verificado:** o layout ativo no banco é `modelo1` (`configurations.layout`). Trocar para `default` ou `newsbit` quebra o módulo — no `default` faltam os templates de layout e o relatório; no `newsbit` falta o `form-contact`.
