# Notícias — Resumo Executivo

> **Versão analisada:** `v6` (módulo também existe na `v5`, com pequenas diferenças — ver `08-GAPS-E-DIVIDAS.md`)
> **Slug do módulo:** `noticias` (tabela `modulos`, id 14 — `v6/database/migrations/2024_11_01_150206_create_modulos_table.php:108-112`)
> **Natureza:** Ambos (CMS em `/cms/...` + Portal público + API de dados abertos)
> **Estado:** migrado v5 -> v6 (com regressões e acréscimos pontuais)

---

## 1. Objetivo

O módulo Notícias é o canal editorial do portal municipal: publica matérias
com texto rico, imagem de destaque, galeria de arquivos, categorização em
subcategorias (editais, secretarias, órgãos, etc.) e agendamento de data de
publicação. Atende o cidadão que busca se informar sobre a atuação da
prefeitura e o operador do CMS que redige, agenda, destaca e mede a
repercussão das matérias. É também a fonte de dados do carrossel de destaques
da home e do bloco "Últimas notícias" de todos os layouts do portal.

## 2. Efeito / Impacto

| Faixa | Efeito |
|---|---|
| Cidadão | Lê matérias por categoria, com paginação, filtro por data e por layout do portal; a abertura de cada matéria incrementa o contador de visualizações exibido no CMS. |
| Prefeitura (CMS) | Redige, agenda, publica, destaca, categoriza e arquiva matérias; a listagem é uma datatable alimentada pela view SQL `noticias`. |
| Transparência e legalidade | Publicações futuras ficam ocultas no portal (filtro `publication_date <= now`); o contador de cliques e o status "Publicado/Rascunho" são evidenceáveis no CMS; a API `GET /api/v1/noticias` publica os dados abertos. |
| Operação técnica | Reaproveita 100% o `ContentController` genérico por slug de categoria; cada módulo do CMS é uma linha em `category_contents` — acrescentar um tipo novo é criar categoria + view, não código. |

## 3. Comportamento

- Uma **notícia** é uma linha de `contents` cuja `category_contents_id` aponta
  para uma **subcategoria** de "Notícias" (`category_contents.category_contents_id = 1`).
  Conteúdos gravados diretamente na categoria 1 **não** aparecem nas listagens
  do portal (`v6/app/Http/Model/Prefecture/Content.php:212-224`).
- **Publicação** é controlada por três chaves independentes: `status`
  (rascunho/publicado), `publication_date` (agendamento) e `deleted_at`
  (soft delete). O portal só enxerga `status = true` **e**
  `publication_date <= now`.
- **Destaque** (`highlights`) marca a matéria para o carrossel; **carousel**
  marca a matéria para o carrossel rotativo do modelo5; `clicks` conta
  aberturas da matéria.
- **Categorias** são filhas da categoria "Notícias" e possuem
  `quantidade_itens` (quantos itens aparecem na home) e `layout` (1..3, usado
  por `getLayoutContentActived()`).
- **Arquivos**: uma imagem de destaque (`archives.highlight = 1`) e uma galeria
  (`archives.highlight IS NULL`), gravados em
  `storage/content/<categoria-pai>/<slug>/<id>/{destaque,galeria,arquivos}/`.
- **Link único** (`links` com `contents_id`) guarda vídeo (YouTube/Vimeo)
  usado pela view de exibição via `embedVideo()`.

## 4. Como o Usuário Interage

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

| Ação | Endpoint | Resultado |
|---|---|---|
| Listar | `GET /cms/conteudos/noticias` | Datatable (view `noticias`) com colunas Título, Status, Visualizações, Data, Categoria, Destaque |
| Criar | `GET /cms/conteudos/cadastrar/noticias` | Formulário com categoria, título, linha fina, texto (Summernote), data, autor, fonte, destaque, galeria, secretaria, órgão |
| Salvar | `POST /cms/conteudo` | Grava conteúdo + destaque + galeria + link em transação única |
| Editar | `GET /cms/conteudos/editar/noticias/{id}` | Formulário pré-preenchido |
| Marcar destaque | `POST /cms/marcar-destaque` | Alterna `highlights`/`carousel` e volta à listagem |
| Ativar/desativar | `POST /cms/set-status` | Alterna `status` |
| Excluir em lote | `POST /cms/conteudo/deleteAll` | Soft delete em lote |
| Reordenar | `POST /cms/ajust-banners` | Persiste a nova ordem (`order`) |

**Cidadão (portal)**

| Tela | Rota | Conteúdo |
|---|---|---|
| Home de notícias | `/noticias` | Grade por categoria (layout 1..3) ou lista simples |
| Notícia | `/noticia/{slug}` | Matéria + galeria + vídeo + PDF inline |
| Categoria | `/noticias/categoria/{slug}` | Lista paginada da categoria |
| Ajax | `/noticias-ajax/{slug}` | Bloco "carregar mais" por categoria |

## 5. Detalhes e Particularidades Técnicas

- Camadas: `Route` -> `Middleware` -> `Controller` (`ContentController` /
  `NewsController`) -> `FormRequest` (`ContentRequest`) -> `Model` (`Content`,
  `CategoryContent`, `Archive`) -> `View` Blade -> `DB` (PostgreSQL).
- O CMS **não** usa repository para conteúdo: `IContentRepository` é uma
  interface **vazia** (`v6/app/Repositories/Contracts/IContentRepository.php`)
  e `ContentRepositoryDefaultImpl::structureUpInsert()` retorna `[]` — todo o
  trabalho está no controller.
- **Vazamento de estado por sessão**: os scopes `byCategoryContentNews` e
  `byLimit` gravam em `session()` (`categorySlugSession`, `limit`) para
  carregar o valor para dentro de `whereHas` (`Content.php:212-243`). Isso
  contamina requisições seguintes do mesmo visitante.
- `scopeByStatusNoticias` retorna dentro do `foreach`, portanto avalia
  **apenas o primeiro registro** do lote (`Content.php:145-160`).
- `limit()` aplicado antes de `paginate()` é inefetivo: o `paginate()`
  sobrescreve o limite (`v6/app/Http/Traits/NewsTrait.php:30-40`).

## 6. Estrutura de Dados

| Objeto | Tipo | Observação |
|---|---|---|
| `contents` | tabela | `SoftDeletes`; FK `category_contents_id`; `clicks`, `highlights`, `galery`, `status`, `carousel`, `publication_date`, `slug`, `linha_fina`, `referencia` |
| `category_contents` | tabela | Auto-referenciada (`category_contents_id` = pai); `quantidade_itens`, `layout`, `display_type`, `custom_icon` |
| `archives` | tabela | `contents_id` / `static_pages_id`; `highlight` (1 = imagem de destaque, NULL = galeria); `view_pdf` |
| `links` | tabela | `contents_id` / `static_pages_id`; `isBlank` |
| `noticias` | **view SQL** | Fonte da datatable do CMS; filtrada por `cc.category_contents_id = 1` |
| `contents` / `category_contents` | seeds | `v6/database/seeds/CategoryContentTableSeeder.php`, `v6/database/seeds/ContentsTableSeeder.php` |

## 7. Como Testar

1. Criar matéria com data futura -> deve **não** aparecer em `/noticias`.
2. Criar matéria com `status = false` -> aparece no CMS como "Rascunho", não no portal.
3. Abrir `/noticia/{slug}` -> `clicks` incrementa e aparece na coluna "Visualizações".
4. Alternar "destaque" na listagem -> a matéria entra/sai do carrossel da home.
5. Navegar entre duas categorias via Ajax -> a segunda listagem **não** pode herdar o filtro da primeira (bug de sessão a monitorar).

## 8. Migração para Laravel Moderno

Reescrever como `Content` + `ContentCategory` (modelos enxutos), `FormRequest`
por tipo (`NewsRequest`), `Policy` por `modulo_id`/`resources`, endpoints
`/api/v1/noticias` com `JsonResource` paginado, componentes Blade
`<x-noticia-card>`, e remover o uso de `session()` como canal de estado.

## 9. Resumo Geral

Notícias é o módulo editorial do portal municipal e o único que reaproveita
integralmente o `ContentController` genérico por slug de categoria: o CMS
resolve "Notícias", "Publicações", "Legislação", "Glossário" etc. pelo mesmo
código, variando apenas a view e as regras de validação do `ContentRequest`.
O conteúdo vive em `contents`, é classificado por `category_contents`
(auto-referenciada, com "Notícias" como id 1), anexa arquivos em `archives` e
guarda vídeo em `links`. No portal, a listagem é filtrada por `status`,
`publication_date` e `deleted_at`, agrupada por subcategoria e ordenada por
data de publicação; no CMS, é uma datatable alimentada pela view SQL
`noticias`. As fraquezas conhecidas são o uso de `session()` dentro de scopes
Eloquent (vazamento de estado), o scope `byStatusNoticias` que só avalia o
primeiro registro, o `limit()` anulado pelo `paginate()`, o contador de
cliques atualizado com read-modify-write (perda de concorrência) e a
duplicação de nomes de rota `static.show` no portal. Todos esses pontos estão
detalhados em `05-REGRAS-E-PORTABILIDADE.md` e `08-GAPS-E-DIVIDAS.md`.

---

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