# Configurações da Instituição — Views e UX

> Tela única do CMS: `/cms/configuracoes/editar/configuracoes`.

---

## 1. Arquivos Blade

| Arquivo | Função |
|---|---|
| `v6/resources/views/prefecture/configurations/configuracoes/edit.blade.php` | Casca da tela: cabeçalho, abas, botões de ação, inclui o form. |
| `v6/resources/views/prefecture/configurations/configuracoes/form.blade.php` | Corpo do formulário, com as 8 abas (conteúdo de cada `tab-pane`). |
| `v6/resources/views/prefecture/configurations/configuracoes/loop_config.blade.php` | Renderiza **um** campo de configuração conforme o `type`. |
| `v6/resources/views/vendor/adminlte/page.blade.php` | Menu lateral do CMS (entrada “Configurações”). |
| `v6/resources/views/admin/modulos/form.blade.php` | Tela de módulos, com o seletor de `structure_type`. |
| `v6/resources/views/portal/manutencao/index.blade.php` | Página de manutenção exibida quando `pagina_em_manutencao` = 1. |
| `v6/resources/views/portal/layouts/modelo1/selos/index.blade.php` | Renderização pública dos selos institucionais. |

## 2. Estrutura da tela

`edit.blade.php` estende `admin.layouts.app` (AdminLTE 1.22) e monta as abas
como `<li class="nav-link" id="dados-options" data-toggle="tab" href="#divOptions"
onclick="setDisplay('options')">`. A aba ativa é
determinada por `session('display')`, devolvida pelo controller após o save.

| Aba | `display` | Conteúdo | Visibilidade |
|---|---|---|---|
| Institucional | `principal` | `order <= 11` + `order IN (68,69)` + `order > 11 AND order <= 15` | sempre |
| SEO | `seo` | `order > 16 AND order <= 18` + bloco Google Analytics | sempre (bloco GA só com `dashboard-config.index`) |
| Imagens | `images` | `order >= 19 AND order <= 23` | sempre |
| Opções | `options` | `order >= 24 AND order <= 37` **ou** key em {`full_list_requests_disabled`, `information_request_recaptcha`, `gemini_api_key`, `diario_oficial_segregacao_funcoes`} | sempre |
| Portal de Transparência Público | `apis` | `order >= 80 AND order <= 82` | sempre |
| Selos | `selos` | JSON de `selos_institucionais` | sempre |
| Mail | `email` | `order >= 38 AND order <= 45` | só com `has_access(['mail_recaptcha'])` |
| Dashboard | `dashboard` | chaves da categoria `dashboard` + 3 chaves de GA | só com `has_access(['dashboard-config.index'])` |

Trecho real da montagem da aba Institucional
(`v6/resources/views/prefecture/configurations/configuracoes/form.blade.php`):

```php
@php
    // Pega até o 11, depois 68 e 69, depois o restante até 15
    $ate11 = $configurations->where('order', '<=', 11)->sortBy('order');
    $especial = $configurations->whereIn('order', [68, 69])->sortBy('order');
    $restante = $configurations->where('order', '>', 11)->where('order', '<=', 15)->sortBy('order');
    $prefeitura = $ate11->concat($especial)->concat($restante);
@endphp
@foreach ($prefeitura as $configuration)
    @include('prefecture.configurations.configuracoes.loop_config')
@endforeach
```

E da aba Opções:

```php
@php
    $opcoes =  $configurations->filter(function($item) {
        return ($item->order >= 24 && $item->order <= 37) || $item->key === 'full_list_requests_disabled' || $item->key === 'information_request_recaptcha'
        || $item->key === 'gemini_api_key' || $item->key === 'diario_oficial_segregacao_funcoes'; })->sortBy('id')->sortBy('order');
@endphp
```

> `information_request_recaptcha` e `diario_oficial_segregacao_funcoes` **não
> existem** na tabela `configurations` do banco atual — a clauses as espera,
> mas nada é renderizado.

## 3. Renderização de um campo (`loop_config.blade.php`)

| `type` | HTML gerado | Observações |
|---|---|---|
| `file` | preview `<img>` + input de arquivo | Usa `file_exist('storage/'.$value)`; senão `url($value)`. |
| `textarea` | `<textarea maxlength="{{ max_input }}">` | `rows=6`, `cols=30`. |
| `checkbox` | `<input type="hidden" value="0">` + checkbox `value="1"` | Padrão do Laravel para não desmarcado enviar 0. |
| `select` | `<select>` com casos especiais | `estado` (lista `states`), `structure_type` (prefeitura/camara), `mail_driver` (8 opções). |
| `cnpj` | input `type="text"` com classe `cnpj` | Máscara JS. |
| default | `<input type="{{ type }}">` | `telefone`/`telefone_2` recebem a classe `advanced-phone`. |

Duas chaves são **excluídas** do render genérico:

```php
@if ($configuration->key != 'structure_type' && $configuration->key != 'email_suporte')
```

- `structure_type` (order 0) é editado na tela de módulos
  (`v6/resources/views/admin/modulos/form.blade.php`).
- `email_suporte` (order 12) é fixo de infraestrutura e não é editável.

O bloco `select` de `structure_type` oferece apenas **duas** opções
(`prefeitura`, `camara`), apesar de o módulo `modulos` aceitar outros perfis.

## 4. Aba de Selos

A aba renderiza o JSON de `selos_institucionais` em um `<input type="hidden" id="selos-existentes">`
e monta a lista via JS.

- JS: `v6/public/assets/js/custom/contents/selos-institucionais/selos.js` (179 linhas).
- Campos por selo: `img` (arquivo), `img_atual` (hidden), `url`, `nova_guia`,
  `posicao` (`acima`/`abaixo`/`ambos`), `titulo`, `descricao`.
- Preview default: `/storage/img/exemplos/sem-imagem-min.png`.
- Envio: `selos_institucionais[<índice>][<campo>]`, tratado por
  `updateByCategory`.
- Arquivos gravados no disco `public`, pasta `selos/`.

## 5. JavaScript e assets

| Asset | Uso |
|---|---|
| `v6/public/assets/js/custom/contents/selos-institucionais/selos.js` | CRUD da lista de selos na tela. |
| `v6/public/assets/plugins/vanilla-masker/vanilla-masker.min.js` | Máscaras de telefone e CNPJ. |

Funções inline definidas em `form.blade.php`:

```js
const btnDisplay = document.querySelector('#display')

function setDisplay(display) {
    btnDisplay.value = display;
}

setImage("input#logo", "#image-default-logo img");
setImage("input#favicon", "#image-default-favicon img");
setImage("input#logo_rodape", "#image-default-logo_rodape img");
setImage("input#aviso_noticias", "#image-default-aviso_noticias img");
setImage("input#imagem-acesso-rapido", "#image-default-imagem-acesso-rapido img");
```

O `input#display` é o mecanismo de persistência da aba ativa: cada link de aba
chama `setDisplay('xxx')` no `onclick`, e o valor volta no POST como campo
oculto.

Não há Ajax na gravação: o envio é um POST tradicional com `enctype="multipart/form-data"`.

## 6. Menu lateral do CMS

`v6/resources/views/vendor/adminlte/page.blade.php` (linhas 228-262):

```php
@if (has_access(['configuracoes.index']) && $modulos['configuracoes-da-instituicao'])
    <li>
        <a href="{{ route('edit.configuracoes.by.category', 'configuracoes') }}">
            <h4 class="ml-0">
                <i class="fa  fa-university pr-2"></i>
                Configurações
            </h4>
        </a>
    </li>
@endif
```

A entrada exige **duas** condições: a permissão `configuracoes.index` e o
módulo `configuracoes-da-instituicao` com `status` ativo (via
`$modulos['configuracoes-da-instituicao']`, injetado por
`v6/app/Providers/ModulosServiceProvider.php`).

> **Risco verificado:** os composers de `ModulosServiceProvider` cobrem apenas
> `admin.layouts.dashboard` e `admin.layouts.dashboard-legacy` (que estendem
> `admin.layouts.app`). As views `admin.layouts.app` e `adminlte::page` recebem
> o evento `composing:` **sem** a variável `modulos`. Consequência: em todas as
> telas do CMS **exceto** o dashboard, `$modulos['configuracoes-da-instituicao']`
> é indefinido e o menu “Configurações” **não aparece**.

## 7. O que o cidadão vê no portal

| Chave | Efeito no portal |
|---|---|
| `titulo_site`, `descricao`, `palavras_chaves` | `<title>` e metatags (`v6/resources/views/portal/layouts/modelo1/layout/head.blade.php`, `v6/resources/views/portal/layouts/default/master.blade.php`). |
| `logo`, `favicon` | Cabeçalho e ícone do portal. |
| `incorporacao_head`, `incorporacao_rodape` | HTML bruto injetado no `<head>` e no fim do portal (`{!! ... !!}`). |
| `codigo_tempo` | Bloco de previsão do tempo (`v6/resources/views/components/weather-box.blade.php`). |
| `expediente`, `endereço` | Rodapé e página de contato. |
| `noticias` = 0 | Portal não renderiza a home de notícias (`v6/app/Http/Controllers/Portal/PortalController.php`). |
| `ultimas_noticias` = 0 | Oculta a seção de últimas notícias (`v6/app/Helpers/functions.php`, `getStatusDisplayNews`). |
| `newsletters` = 0 | Oculta o formulário de newsletter (`getStatusDisplayNewsletters`). |
| `links_rapidos` = 0 | Oculta a seção de acesso rápido (`v6/resources/views/portal/layouts/modelo1/home/acesso-rapido.blade.php`). |
| `contact_link` = 0 | Oculta o botão de contato no cabeçalho (`v6/app/Providers/MenuServiceProvider.php`). |
| `menu_interativo` | Habilita o menu interativo do portal. |
| `numero_versao` = 1 | Mostra a versão no rodapé (`v6/app/Providers/FooterServiceProvider.php`). |
| `pagina_em_manutencao` = 1 | Visitante não logado recebe `portal.manutencao.index` (`v6/app/Http/Middleware/CheckSiteManutention.php`). |
| `nome_prefeito` | Assinatura em respostas de perguntas (`v6/app/Http/Controllers/Prefecture/QuestionAnswersController.php`). |
| `email_lai` | Destinatário do formulário LAI (`v6/app/Http/Controllers/Portal/InformationController.php`). |
| `response_time` | Prazo de resposta exibido na Ouvidoria (`v6/app/Http/Controllers/Portal/OmbudsmanController.php`). |
| `full_list_requests_disabled` = 1 | Oculta a listagem completa de solicitações (Ouvidoria e Transparência). |
| `selos_institucionais` | Selos exibidos em `GET /selos` (`v6/app/Http/Controllers/Portal/SelosController.php`). |
| `portal_transparencia_*` | Habilita a busca no portal fiscal externo (`v6/app/Services/PortalTransparenciaService.php`). |

## 8. Exportações

Não há exportação (Excel/PDF/CSV) própria deste módulo. A listagem genérica
`GET /cms/datatable/configuracoes` (Yajra DataTables) é usada pela tela de
configurações da categoria `outros` e lista **todas** as categorias.

## 9. Layouts do portal afetados

O portal usa `modelo1`..`modelo6` (selecionados pela chave `layout`, categoria
`temas`). Este módulo não troca o layout, mas define o **conteúdo** que os
layouts exibem (head, rodapé, home, acesso rápido, contato). O
`v6/app/Http/Controllers/Admin/Controller.php` (`getTemplateActived()`, linha 39)
lê a chave `layout` e, se a view `portal.layouts.{template}.home.index` não
existir, grava `pagina_em_manutencao = 1` e aborta com 404.
