# Acesso Rápido — Modelagem de Dados

O módulo **não possui tabela própria**. Ele se apoia em tabelas genéricas da
plataforma (`category_contents`, `contents`, `links`) e em páginas estáticas
(`static_pages`, `archives`). A listagem do CMS lê uma **view SQL** dedicada.

## 1. Registro do módulo

| Tabela | Colunas relevantes | Registro (consulta somente-leitura em `prefeituras_v5`) |
|---|---|---|
| `modulos` | `id`, `name`, `slug`, `status` | `id = 49`, `name = 'Seção de links rápidos'`, `slug = 'acesso-rapido'`, `status = true` |
| `resources` | `id`, `slug`, `name`, `description`, `modulo_id` | `id = 69`, `slug = 'acesso-rapido'`, `name = 'Acesso Rápido'`, `description = 'Funcionalidade Acesso Rápidos'`, `modulo_id = 49` |
| `menu_orders` | `id`, `slug`, `clicks`, `modulo_id` | `id = 38`, `slug = 'acesso-rapido'`, `clicks = 0`, `modulo_id = 49` |

Seed da tabela `modulos` (`v6/database/migrations/2024_11_01_150206_create_modulos_table.php:316-321`):

```php
// ID 49
DB::table('modulos')->insert([
    'name' => 'Seção de links rápidos',
    'slug' => 'acesso-rapido',
    'status' => 1,
]);
```

`resources` é populado por `v6/database/seeds/ResourcesTableSeeder.php:603-609`:

```php
Resource::updateOrCreate(['slug' => 'acesso-rapido'],
    [
        'name' => 'Acesso Rápido',
        'description' => 'Funcionalidade Acesso Rápidos',
        'modulo_id' => 49,
        'created_at' => date("Y-m-d H:i:s")
    ]
);
```

## 2. `category_contents` — categoria dos atalhos

O módulo usa a categoria cujo `slug` é `acesso-rapido`.

| Coluna | Tipo (PostgreSQL) | Observação |
|---|---|---|
| `id` | `integer` (serial) | PK. No banco atual: `id = 45`. |
| `name` | `varchar(191)` | "Acesso Rápido" |
| `slug` | `varchar(191)` | `acesso-rapido` — **chave de negócio** usada por todas as consultas do módulo. |
| `status_create` | `boolean` | `true` (permite criação de conteúdo na categoria). |
| `order` | `integer` | `1`. |
| `category_contents_id` | `integer` nullable | categoria pai (nulo para esta categoria). |

Consulta real:

```sql
select id,name,slug,status_create,"order"
from category_contents where slug = 'acesso-rapido';
--  id |     name      |     slug     | status_create | order
--  45 | Acesso Rápido | acesso-rapido | t             |     1
```

## 3. `contents` — os atalhos

Colunas usadas pelo módulo (dump real de `contents`):

| Coluna | Tipo | Null | Papel no módulo |
|---|---|---|---|
| `id` | `integer` (serial) | não | PK; idem `acesso_rapido.id`. |
| `title` | `varchar(191)` | sim | Título do card (máx. 25 caracteres no form do CMS). |
| `text` | `text` | sim | Descrição do card; renderizada com `{!! !!}` (HTML cru do Summernote). |
| `icon` | `varchar(191)` | sim | Classe CSS de ícone (ex.: `fas fa-question-circle`). |
| `order` | `integer` | sim | Posição no bloco; base do drag-and-drop do CMS. |
| `status` | `boolean` | sim | `true` = exibir no portal. |
| `slug` | `varchar(191)` | sim | **Não usado** pelo módulo (os atalhosseedados ficam com `slug` nulo). |
| `clicks` | `integer` | sim | Não é incrementado para atalhos (apenas `static_pages.clicks` é). |
| `category_contents_id` | `integer` | não | FK -> `category_contents.id` (45 para acesso rápido). |
| `deleted_at` | `timestamp` | sim | Soft delete. |
| `created_at` / `updated_at` | `timestamp` | sim | Eloquent `timestamps`. |

Conteúdo atual (consulta somente-leitura):

```sql
select id,title,slug,icon,"order",status
from contents where category_contents_id = 45 order by "order";
--  id  |         title         | slug |          icon          | order | status
--  179 | Perguntas Frequentes  |      | fas fa-question-circle |     1 | t
--  180 | Central de Licitações |      | fas fa-newspaper       |     2 | t
--  181 | Ouvidoria             |      | fas fa-comments        |     3 | t
```

> As três linhas são exatamente as criadas por
> `v6/database/seeds/AcessoRapidoSeeder.php`.

## 4. `links` — destino do atalho

| Coluna | Tipo | Papel |
|---|---|---|
| `id` | `integer` (serial) | PK |
| `link` | `varchar(3000)` | URL de destino do card. |
| `title`, `alt`, `name` | `varchar(191)` | rótulos alternativos. |
| `isBlank` | `boolean` | `true` = abrir em nova aba (`target="_blank"` no card da home). |
| `status_create` | `boolean` | flag de criação. |
| `contents_id` | `integer` nullable | FK -> `contents.id` (o atalho). |
| `static_pages_id` | `integer` nullable | FK -> `static_pages.id` (página estática). |
| `deleted_at` | `timestamp` | soft delete. |

Relacionamento no model: `Content::linksUnique()` ->
`hasOne(Link::class, 'contents_id', 'id')` (`v6/app/Http/Model/Prefecture/Content.php:116`).

Dados atuais:

```sql
select id,title,link,"isBlank",contents_id
from links where contents_id in (179,180,181);
--  40 | Perguntas Frequentes  | /acesso-rapido/perguntas-frequentes | f | 179
--  41 | Central de Licitações | /licitacoes                         | f | 180
--  42 | Ouvidoria             | /pagina/contato                     | f | 181
```

> O seed da v6 usa `/pagina/contato` para "Ouvidoria", enquanto o controller do portal
> redireciona `/acesso-rapido/ouvidoria` para `/acessos-rapido/solicitar-informacao`.
> Os dois destinos coexistem (o link do card e a rota do controller).

## 5. `static_pages` — páginas do bloco "estático"

O bloco de acesso rápido também serve páginas estáticas, carregadas por
`getStaticPageData($slug)`:

```php
// v6/app/Http/Traits/GetInformationsPrefectureTrait.php:60-63
public function getStaticPageData($param)
{
    return StaticPage::with('linksUnique')->where('slug', $param)->first(['id', 'title', 'text', 'layout']);
}
```

| Coluna | Tipo | Papel |
|---|---|---|
| `id` | `integer` (serial) | PK; usado em `archives.static_pages_id`. |
| `slug` | `varchar(191)` | Chave da URL (`/acessos-rapido/{slug}`). |
| `title` | `varchar(191)` | Título exibido no cabeçalho da página. |
| `text` | `text` | Corpo HTML da página. |
| `layout` | `integer` **not null** | `1` = página com sidebar; valor diferente = largura cheia. Usado em `$contents->layout == 1` nos Blades. |
| `clicks` | `integer` | Incrementado a cada visita (`updateClickStaticPage`). |
| `is_external` | `boolean` not null | Usado por `PortalController@staticShow` (caminho alternativo, fora do módulo). |
| `galery` | `boolean` | Galeria de imagens. |

Páginas estáticas usadas pelo módulo (consulta somente-leitura):

```sql
select id,slug,title,layout,clicks from static_pages
where slug in ('ouvidoria','solicitar-informacao','acompanhe-sua-solicitacao','manual-de-navegacao');
--  id |           slug            |           title           | layout | clicks
--   2 | ouvidoria                 | Ouvidoria                 |      1 |
--   3 | solicitar-informacao      | Solicitar Informação      |      1 |
--   4 | acompanhe-sua-solicitacao | Acompanhe sua Solicitacão |      1 |
--   6 | manual-de-navegacao       | Manual de Navegação       |      1 |
--   8 | manual-de-navegacao       | Manual de Navegação       |      1 | 17
```

> **Risco de dado:** existem **duas** linhas com `slug = 'manual-de-navegacao'`
> (ids 6 e 8). `->first()` sempre devolve a de menor id (6), cuja coluna `clicks`
> está nula. A linha 8 é a que acumula cliques. O contador exibido ao operador
> não corresponde ao conteúdo exibido.

## 6. `archives` — anexos das páginas estáticas

Carregado em `getIndexStatic()`:

```php
$archives = Archive::where('static_pages_id', $contents->id)->where('highlight', null)->get();
$view_pdf = Archive::where('static_pages_id', $contents->id)->where('view_pdf', true)->first();
```

- `highlight = null` -> anexos comuns (galeria/arquivos).
- `view_pdf = true` -> o PDF "principal" da página, exibido pelo componente
  `portal.components.view_pdf.files-list`.

## 7. View SQL `acesso_rapido`

É a fonte de dados da listagem do CMS.

### 7.1 Definição atual (migrations)

`v6/database/migrations/2024_01_08_103853_update_view_acesso_rapido_2.php`:

```sql
CREATE OR REPLACE VIEW acesso_rapido AS
SELECT row_number() OVER (ORDER BY c.order asc) AS value,
    c.title AS titulo,
    c.id as id,
    CASE
        WHEN c.status IS TRUE THEN 'Ativado'::text
        ELSE 'Desativado'::text
    END AS status_name,
    c.status
FROM contents c
JOIN category_contents cc ON cc.id = c.category_contents_id
WHERE c.deleted_at IS NULL AND cc.slug = 'acesso-rapido'
ORDER BY c.order asc;
```

### 7.2 Histórico da view (mapa de migrations)

| Migration | Efeito |
|---|---|
| `v6/database/migrations/2023_04_10_153741_create_view_to_acesso_rapido.php` | Cria a view com `row_number()` por `created_at DESC`, apenas `value`, `titulo`, `id`. |
| `v6/database/migrations/2023_06_15_101623_update_view_acesso_rapido.php` | Acrescenta `status_name`/`status`; **atualiza `contents.status = true`** para todos os itens da categoria (o seed não setava status). |
| `v6/database/migrations/2024_01_08_103853_update_view_acesso_rapido_2.php` | Troca a ordenação para `c.order asc`. |

As três migrations são **byte-idênticas** entre `v5/` e `v6/`.

### 7.3 Conteúdo atual da view

```sql
select * from acesso_rapido;
--  value |        titulo         | id  | status_name | status
--      1 | Perguntas Frequentes  | 179 | Ativado     | t
--      2 | Central de Licitações | 180 | Ativado     | t
--      3 | Ouvidoria             | 181 | Ativado     | t
```

> A view **não** expõe `contents.text`, `contents.icon` nem o `links.link`.
> O CMS mostra apenas o título; o ícone e a URL só aparecem no portal.

## 8. Estrutura de menu (`menus` + `menu_items`) — "Estrutura A" do portal

A barra de atalhos do cabeçalho vem de `menus.id = 3` e seus `menu_items`.

| Tabela | Colunas relevantes | Registro |
|---|---|---|
| `menus` | `id`, `slug`, `name` | `id = 3`, `slug = 'acesso-rapido'`, `name = 'Acesso Rápido'` (criado por `v6/database/seeds/MenusTableSeeder.php:27-32`) |
| `menu_items` | `id`, `name`, `second_name`, `order`, `type`, `icon`, `is_active`, `menus_id`, `links_id`, `static_pages_id`, `category_contents_id`, `contents_id` | 5 itens (ids 93-97) com `menus_id = 3` |

Tipos de `menu_item` (convenção declarada em
`v6/database/seeds/MenuItemsTableSeeder.php:9-13`):

| `type` | Significado | Destino resolvido por `MenuItemPresenter::getLink()` |
|---|---|---|
| 0 | Link (`links_id`) | `links.link` |
| 1 | Página estática (`static_pages_id`) | `url('acesso-rapido/{slug}')` — exceto `static_pages_id = 4`, que usa `url({slug})` |
| 2 | Conteúdo / categoria (`contents_id` / `category_contents_id`) | `url('publicacoes/{slug_categoria}{slug_conteudo}')`, `url('legislacao/{slug_categoria}{slug_conteudo}')` e `url('acesso-rapido/{slug_categoria}{slug_conteudo}')`, conforme a categoria |

Consulta real dos itens:

```sql
select id, name, "order", type, static_pages_id, category_contents_id
from menu_items where menus_id = 3 order by "order";
--  id |       name       | order | type | static_pages_id | category_contents_id
--  93 | Perguntas        |     1 |    2 |                 | 12
--  94 | Manual de        |     2 |    1 |               5 |
--  95 | Solicitar        |     3 |    1 |               2 |
--  96 | Acompanhe        |     4 |    1 |               3 |
--  97 | Transparência    |     5 |    2 |                 | 25
```

> Os nomes são curtos porque o `MenuItem` tem duas colunas: `name` + `second_name`
> ("Manual de" + "Navegação"). Os `static_pages_id` estão **errados** (ver
> `08-GAPS-E-DIVIDAS.md`, bug B18).

## 9. Configurações que afetam o módulo

| `configurations.key` | Uso | Valor na base |
|---|---|---|
| `links_rapidos` | Habilita a exibição do bloco na home (`== '1'`). | `1` |
| `layout` | Layout do portal; define o prefixo das views. | `modelo1` |
| `pagina_em_manutencao` | Marcado como `1` se a view do layout não existir. | `0` |

## 10. Seeds

### 10.1 `v6/database/seeds/AcessoRapidoSeeder.php`

Registrado em `v6/database/seeds/DatabaseSeeder.php:50`
(`$this->call(AcessoRapidoSeeder::class);`).

Cria:

1. `category_contents` — "Acesso Rápido" / `acesso-rapido` / `status_create = true` / `order = 1`.
2. Três `contents` (ids 179/180/181) — "Perguntas Frequentes"
   (`fas fa-question-circle`), "Central de Licitações" (`fas fa-newspaper`),
   "Ouvidoria" (`fas fa-comments`), com `order` 1/2/3 e `status = true`.
3. Três `links` (ids 40/41/42) apontando para
   `/acesso-rapido/perguntas-frequentes`, `/licitacoes` e `/pagina/contato`.

> O seed é **não-idempotente**: roda `insert` sem `updateOrCreate`; executá-lo duas
> vezes duplica a categoria, os conteúdos e os links.

### 10.2 `MenuAcessoRapidoSeeder.php` — **não existe na v6**

Confirmado por listagem de `v6/database/seeds/` e por `grep -rn "MenuAcessoRapido" v6/ v5/`:

```
v5/database/seeds/MenuAcessoRapidoSeeder.php:6://class MenuAcessoRapidoSeeder extends Seeder
```

Na `v5` o arquivo existe, porém **todo o corpo está comentado** (classe morta). Na `v6`
o arquivo **foi removido**. Detalhes em `08-GAPS-E-DIVIDAS.md`.

## 11. Migrations do módulo

| Migration | Arquivo | Efeito |
|---|---|---|
| `2023_04_10_153741` | `v6/database/migrations/2023_04_10_153741_create_view_to_acesso_rapido.php` | Cria a view `acesso_rapido`. |
| `2023_06_15_101623` | `v6/database/migrations/2023_06_15_101623_update_view_acesso_rapido.php` | View + `status = true` nos itens. |
| `2024_01_08_103853` | `v6/database/migrations/2024_01_08_103853_update_view_acesso_rapido_2.php` | View ordenada por `order`. |
| `2024_11_01_150206` | `v6/database/migrations/2024_11_01_150206_create_modulos_table.php` | Registra o módulo (id 49). |
| `2026_05_19_104500` | `v6/database/migrations/2026_05_19_104500_sync_acesso_rapido_modelo_5.php` | Registra o elemento `acesso-rapido` no layout `modelo5` (`layouts.key = 'acesso-rapido'`, `status = 0`) e injeta o HTML de preview em `layouts.tags`. |
