# Páginas Estáticas — Resumo Executivo

> **Versão analisada:** `v6` (existe na `v5`; a v6 acrescenta a sanitização de link externo e o redirecionamento `is_external`)
> **Slug do módulo:** `paginas-estaticas` (tabela `modulos`, id 28 — `v6/database/migrations/2024_11_01_150206_create_modulos_table.php:192-196`); o `Resource` do CMS usa o slug `pagina-estatica` (singular)
> **Natureza:** Ambos (CMS em `/cms/...` + Portal público)
> **Estado:** migrado v5 -> v6 (módulo praticamente estável)

---

## 1. Objetivo

Páginas Estáticas é o mecanismo de páginas de conteúdo institucional
servidas na **raiz do domínio** (`/{slug}`): institutional, Services,
legislação municipal, secretarias, Links Úteis, Acessibilidade, ouvidoria
etc. São textos HTML sem data de publicação, sem categoria e sem listagem —
cada página tem uma URL própria e pode ser colocada no menu do portal.
Também permite **redirecionar** a URL para um link externo (ex.: um portal
externo) e anexar arquivos ou uma galeria de imagens.

## 2. Efeito / Impacto

| Faixa | Efeito |
|---|---|
| Cidadão | Acessa páginas institucionais por URL curta na raiz (`/institucional`, `/links-uteis`); o contador de cliques é incrementado a cada visita; páginas com `is_external` redirecionam para o destino configurado. |
| Prefeitura (CMS) | Cria e edita páginas com editor de texto rico, galeria, PDF inline, escolha de layout (com/sem barra lateral) e link externo; mantém as páginas de sistema (protegidas contra exclusão). |
| Transparência e legalidade | Páginas institucionais e de acesso à informação têm versão auditável (`activity()`), com log de criação, edição e exclusão. |
| Operação técnica | Uma tabela (`static_pages`) com soft delete, slug em nível de raiz e um `catch-all` de rota; risco de conflito com as rotas do portal. |

## 3. Comportamento

- **Página do sistema** (`status_create = 1`): criada pelo seed
  (`v6/database/seeds/StaticPagesTableSeeder.php`), **não** pode ser excluída
  (`destroy` e `deleteByLote` verificam `status_create`).
- **Link Permanente** = a própria slug, servida na **raiz** do domínio
  (`/{slug}`), e não em `/pagina/{slug}`.
- **Slug**: gerada do título (`Str::slug($title, '-')` no controller;
  `slugfy(str_limit($title, 50))` no repository) e desambiguada com
  `-<dmyHis>` se colidir com outra página **ou com uma
  `category_contents.slug`**.
- **Redirecionamento externo**: com `is_external = true`, o portal executa
  `Redirect::to($content->linksUnique()->pluck('link')[0])` **antes** de
  qualquer renderização.
- **Slugs reservados**: `contato`, `ouvidoria`, `manual-de-navegacao`,
  `solicitar-informacao`, `acompanhe-sua-solicitacao` e
  `relatorio-estatistico` são interceptados por um `switch` e redirecionam
  para os controllers específicos.
- **Layout**: `1` = com barra lateral, `2` = sem barra lateral.

## 4. Como o Usuário Interage

**Operador (CMS, `/cms/pagina-estatica`)**

| Ação | Endpoint | Resultado |
|---|---|---|
| Listar | `GET /cms/pagina-estatica` | Datatable (`paginas_estaticas`) com ID, Título, Ações |
| Criar | `GET /cms/pagina-estatica/create` | Formulário (título, link, redirecionamento externo, layout, texto, galeria, arquivos) |
| Salvar | `POST /cms/pagina-estatica` | Grava página + link + anexos em transação |
| Editar | `GET /cms/pagina-estatica/{id}/edit` ou `GET /cms/pagina-estatica/editar/{id}` | Formulário preenchido, com "Link Permanente" e botão "Ir para galeria" |
| Excluir | `DELETE /cms/pagina-estatica/{id}` | Soft delete (bloqueado se `status_create = 1`) |
| Excluir em lote | `POST /cms/pagina-estatica/delete-por-lote` | Soft delete em lote (ignora páginas de sistema) |
| Upload de galeria | `POST /cms/ajax-static-upload-files` | Grava em `storage/statics/{slug}/galeria/` |
| Excluir arquivo | `POST /cms/ajax-static-delete-files-gallery` | Remove o arquivo do disco e do banco |

**Cidadão (portal)**

| Tela | Rota | Resultado |
|---|---|---|
| Página | `/{slug}` ou `/pagina/{slug}` | Conteúdo HTML, galeria, PDF inline, vídeo e barra lateral opcional |
| Página externa | `/{slug}` com `is_external` | Redirecionamento HTTP para a URL configurada |
| Slug reservado | `/{slug}` | Redirecionamento para o módulo correspondente |

## 5. Detalhes e Particularidades Técnicas

- Camadas: `Route` -> `Middleware` -> `StaticPageController` ->
  `StaticPageRequest` -> `StaticPageRepository` -> `StaticPage` ->
  `Archive`/`Link` -> Blade.
- O `StaticPageRepository::structureUpInsert()` devolve o **Request inteiro**
  para `updateOrCreate()` — a mass assignment depende do `$fillable` do
  modelo, e não de uma lista explícita de campos.
- A rota do portal é **duplicada**: `v6/routes/web.php:227`
  (`/pagina/{slug}`) e `v6/routes/basepro.php:548` (`/{slug}`), ambas com o
  nome `static.show`. O Laravel registra as duas; a **segunda** sobrescreve o
  nome, e `route('static.show', 'x')` gera `http://localhost/x`.
- A view SQL `paginas_estaticas` **não** expõe a coluna `galery`, mas o JS do
  CMS usa `data.galery` para montar o botão de galeria — o botão fica
  sempre desabilitado.

## 6. Estrutura de Dados

| Objeto | Tipo | Observação |
|---|---|---|
| `static_pages` | tabela | `key`, `title`, `text`, `access`, `status_create`, `galery`, `slug`, `clicks`, `layout`, `is_external`, soft delete |
| `archives` | tabela | `static_pages_id` — anexos e galeria da página |
| `links` | tabela | `static_pages_id` — link externo / vídeo |
| `menu_items` | tabela | `static_pages_id` — entrada de menu do portal |
| `paginas_estaticas` | **view SQL** | Fonte da datatable do CMS |
| `static_pages` | seeds | `v6/database/seeds/StaticPagesTableSeeder.php`, `v6/database/seeds/RestoreDefaultStaticPagesSeeder.php` |

## 7. Como Testar

1. Criar uma página -> ela responde em `/{slug}`.
2. Marcar "Redirecionar para Link Externo" -> `/{slug}` redireciona para a URL.
3. Tentar excluir uma página de sistema (`status_create = 1`) -> bloqueado.
4. Excluir uma página com item de menu -> o item de menu também sai.
5. Abrir a página 3 vezes -> `clicks` = 3.
6. Acessar `/pagina/{slug}` e `/{slug}` -> as duas rotas funcionam.

## 8. Migração para Laravel Moderno

`StaticPage` com `$fillable` enxuto e casts, `StaticPageRequest` com regras
condicionais (`ConditionalRequiredSlug`, `ConditionalRequiredLink`),
`StaticPagePolicy`, rota canônica `/paginas/{slug}` com **redirect**
permanentemente a partir de `/{slug}`, `StaticPageResource`, componente
`<x-portal.static-page>` e menu dinâmico.

## 9. Resumo Geral

Páginas Estáticas é o único módulo do portal que publica conteúdo **na raiz
do domínio**: cada página tem uma slug que vira a URL `/{slug}`, o que
explica a existence de uma rota `catch-all` em
`v6/routes/basepro.php:548`. O conteúdo é armazenado em `static_pages`
(texto HTML, `layout`, `galery`, `clicks`, `is_external`), os anexos em
`archives` e o link externo/vídeo em `links`. No CMS, o operador cria a
página, escolhe entre layout com ou sem barra lateral, ativa a galeria,
anexa arquivos e pode marcar a página como redirecionamento externo; as
páginas de sistema (criadas por seed, com `status_create = 1`) são
protegidas contra exclusão. Os principais passivos são a duplicação do nome
de rota `static.show` (que faz `route('static.show', $slug)` gerar a URL da
raiz em vez de `/pagina/{slug}`), a view SQL `paginas_estaticas` ter
perdido a coluna `galery` enquanto o JavaScript do CMS ainda a consome (botão
de galeria sempre desabilitado), a mass assignment do Request inteiro no
`StaticPageRepository` e o uso de `$_SERVER['REQUEST_URI']` para descobrir o
id da página dentro do repository.

---

## Índice do dossiê

| Arquivo | Conteúdo |
|---|---|
| `00-RESUMO.md` | Este documento |
| `01-ARQUITETURA.md` | Camadas, classes e o diagrama de fluxo |
| `02-MODELAGEM.md` | Estrutura de dados detalhada |
| `03-ROTAS-E-CONTROLADORES.md` | Rotas, controllers, requests, resources |
| `04-VIEWS-E-UX.md` | Telas, componentes, JS, assets |
| `05-REGRAS-E-PORTABILIDADE.md` | Particularidades e armadilhas |
| `06-MIGRACAO-MODERNA.md` | Plano de reconstrução em Laravel moderno |
| `07-TESTES.md` | Estratégia e casos de teste |
| `08-GAPS-E-DIVIDAS.md` | Pendências, riscos, comparativo v5 vs v6 |
