# Publicações — Resumo Executivo

> **Versão analisada:** `v6` (o módulo existe na `v5`; a v6 **acrescentou** a rota de exportação)
> **Slug do módulo:** `publicacoes` (tabela `modulos`, id 15 — `v6/database/migrations/2024_11_01_150206_create_modulos_table.php:114-118`)
> **Natureza:** Ambos (CMS em `/cms/...` + Portal público + API de dados abertos)
> **Estado:** migrado v5 -> v6 (com acréscimo de exportação e de subcategorias)

---

## 1. Objetivo

Publicações é o repositório documental do município: guarda editais,
manuais, mensagens oficiais, concursos, campanhas, horários de
funcionamento, documentos, diário oficial e portal do contribuinte.
Cada documento pertence a uma **subcategoria** de "Publicações"
(`category_contents.category_contents_id = 3`) e é exibido no portal com
filtro por **ano**, **mês** e **texto**, paginação Ajax e **exportação** em
XLS, PDF, CSV ou TXT. Serve ao cidadão que procura um documento oficial e ao
operador do CMS que publica, categoriza e versiona esses arquivos.

## 2. Efeito / Impacto

| Faixa | Efeito |
|---|---|
| Cidadão | Encontra documentos por categoria, ano, mês e palavra-chave; baixa o resultado em XLS/PDF/CSV/TXT; abre o documento com anexos e PDF inline. |
| Prefeitura (CMS) | Publica documentos com texto rico, anexos, "Link Permanente" (`/publicacoes/{categoria}/{slug}`) e escolha de layout (com/sem barra lateral). |
| Transparência e legalidade | Repositório oficial com data de publicação, categorização e trilha de auditoria; a API `GET /api/v1/publicacoes` publica os dados abertos; a exportação em lote dá resposta a solicitações formais. |
| Operação técnica | Reaproveita o `ContentController` genérico; a listagem do CMS é uma datatable montada em PHP (não há view SQL usada por este módulo no CMS). |

## 3. Comportamento

- **Publicação** = linha de `contents` cuja categoria é **filha** da raiz
  "Publicações" (id 3). Subcategorias reais: `documentos` (13), `editais`
  (14), `horarios-funcionarios` (15), `manuais` (16), `mensario-oficial`
  (17), `concurso-publico` (18), `campanhas` (19), `diario-oficial` (20),
  `portal-do-contribuinte` (21).
- O portal **ignora** documentos com `publication_date` futura
  (`publication_date <= now`) e documentos com soft delete.
- O filtro por **mês** só é aplicado quando há **ano** selecionado
  (`when($request->mes, ...)` e `when(empty($request->mes), ...)`) — sem ano,
  o filtro de mês é ignorado.
- A busca textual usa `translate(...) ilike` sobre `title` **e** `text`,
  sem accent-insensitive nos dois sentidos de forma idêntica à API.
- O **Link Permanente** é `/publicacoes/{slug-da-categoria}/{slug-do-conteudo}`;
  ele só é exibido/validado na edição (`slug` obrigatória apenas em
  `conteudo.update`).
- O campo `layout` (1 = com barra lateral, 2 = sem) controla a renderização
  do documento.

## 4. Como o Usuário Interage

**Operador (CMS, `/cms/conteudos/publicacoes`)**

| Ação | Endpoint | Resultado |
|---|---|---|
| Listar | `GET /cms/conteudos/publicacoes` | Datatable com Título, Categoria, Data de publicação |
| Criar | `GET /cms/conteudos/cadastrar/publicacoes` | Formulário (categoria, título, data, layout, texto, arquivos) |
| Salvar | `POST /cms/conteudo` | Grava conteúdo + anexos em transação |
| Editar | `GET /cms/conteudos/editar/publicacoes/{id}` | Formulário com "Link Permanente" e link "Acessar" |
| Categorias | `GET /cms/categorias/publicacoes` | CRUD das subcategorias |
| Excluir em lote | `POST /cms/conteudo/deleteAll` | Soft delete |

**Cidadão (portal)**

| Tela | Rota | Conteúdo |
|---|---|---|
| Entrada | `/publicacoes` | Redireciona para a primeira subcategoria (`order`) |
| Lista da categoria | `/publicacoes/{categoria}` | Documentos paginados (12/página) + filtros ano/mês/texto |
| Documento | `/publicacoes/{categoria}/{documento}` | Texto, anexos, PDF inline, barra lateral opcional |
| Exportação | `/publicacoes/export?sub_category=...&type=...` | XLS, PDF, CSV ou TXT |

## 5. Detalhes e Particularidades Técnicas

- O CMS **não** usa repository para conteúdo: `IContentRepository` é uma
  interface vazia (`v6/app/Repositories/Contracts/IContentRepository.php`).
- A listagem do CMS (`DatatableController::get_publicacoes`) carrega **todos**
  os conteúdos das subcategorias com `getAllDescendants()` e monta um array
  `stdClass` em memória — sem paginação server-side.
- O `export` reconstrói a categoria a partir de `$request->sub_category` e
  **aborta com 404** se a categoria não existir.
- A exportação PDF usa `\PDF::loadView('exports.contents')` com a cor
  `configurations.cor_1`; XLS/CSV/TXT usam `Maatwebsite\Excel`.
- As rotas do portal usam `Route::get` (não `Route::any`), diferentemente do
  módulo Notícias.

## 6. Estrutura de Dados

| Objeto | Tipo | Observação |
|---|---|---|
| `contents` | tabela | Reutilizada; categoria filha de 3; `layout` = 1 ou 2 |
| `category_contents` | tabela | Raiz "Publicações" (id 3) + 9 subcategorias (13-21) |
| `archives` | tabela | Anexos do documento (`contents_id`), com `view_pdf` para o PDF inline |
| `links` | tabela | Não utilizada por este módulo (uso de vídeo é do módulo Notícias) |
| `publicacoes` | **view SQL** | `v6/database/migrations/2024_11_28_100323_update_view_publicacoes.php` — usada por outros consumidores, **não** pela datatable do CMS |
| `contents` / `category_contents` | seeds | `v6/database/seeds/CategoryContentTableSeeder.php` (ids 3 e 13-21) |

## 7. Como Testar

1. Publicar um documento com data futura -> ausente em `/publicacoes`.
2. Filtrar por ano sem mês -> mês ignorado; filtrar por ano+mês -> ambos aplicados.
3. Buscar por texto com acento -> o resultado deve incluir o título sem acento.
4. Exportar em XLS/PDF/CSV/TXT -> arquivo com o nome
   `publicacoes_<categoria>_<Ymd_His>.<ext>`.
5. Exportar com `sub_category` inexistente -> HTTP 404.
6. Abrir o documento -> `clicks` incrementa.

## 8. Migração para Laravel Moderno

Modelos `Publication` + `PublicationCategory`, `StorePublicationRequest`,
`PublicationPolicy`, `PublicationController` com `index/show/export`,
`PublicationResource` para a API, `PublicationExport` (Jobs), componentes
Blade `<x-publication-list>` e `<x-publication-document>`, e paginação real
(server-side) no CMS.

## 9. Resumo Geral

Publicações é o repositório documental do portal, construído sobre a mesma
tabela `contents` e o mesmo `ContentController` genérico do módulo Notícias.
A raiz "Publicações" é a categoria 3 de `category_contents`, e as
subcategorias (Documentos, Editais, Horários Funcionários, Manuais,
Mensário Oficial, Concurso Público, Campanhas, Diário Oficial e Portal do
Contribuinte) são suas filhas. No portal, o visitante navega por
`/publicacoes/{categoria}`, filtra por ano, mês e texto, e pode exportar o
resultado em XLS, PDF, CSV ou TXT pela rota `publicacoes.export` — capaz
existente apenas na v6. No CMS, a listagem é uma datatable montada em PHP
(sem view SQL própria, ao contrário de Notícias), o formulário exige
categoria, título, data e layout, e o "Link Permanente" é a combinação da
subcategoria com a slug do documento. Os principais problemas conhecidos são
o SQL interpolado (`whereRaw` com `translate(...) ilike`) sem bindings, o
carregamento integral da tabela para a datatable do CMS, o filtro de mês
condicional ao ano, o fato de a exportação reconstruir a categoria a partir
de um parâmetro de query e a breadcrumb `view-publicacoes` apontar para a
rota `conteudo.portal.view`, que redireciona para a lista da categoria em
vez do documento.

---

## Í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 |
