# Comitê de Investimentos — Arquitetura

## 1. Visão geral das camadas

O módulo **não possui model, repository, service ou Form Request próprios**. Todo
o conhecimento de domínio é resolvido por models genéricos e por um despacho
(baseado em `switch` sobre o slug) dentro de controllers genéricos do CMS.

| Camada | Classe / arquivo | Papel |
|---|---|---|
| Rota (portal) | `v6/routes/web.php:219-221` | 3 rotas GET públicas |
| Controller (portal) | `v6/app/Http/Controllers/Portal/ComiteDeInvestimentosController.php` | `index`, `show`, `view` |
| Controller (CMS) | `v6/app/Http/Controllers/Prefecture/ContentController.php` | `genericIndex`, `genericCreate`, `genericEdit`, `store`, `update`, `genericDestroy` |
| Controller (CMS) | `v6/app/Http/Controllers/Prefecture/CategoryContentController.php` | CRUD de subcategorias do comitê |
| Controller (CMS) | `v6/app/Http/Controllers/Prefecture/DatatableController.php:40-44` | `get_comite_de_investimentos` |
| Validação | `v6/app/Http/Requests/ContentRequest.php` | Regras por slug; comitê cai no `default` |
| Repositório | `v6/app/Repositories/Eloquent/Prefecture/Content/ContentRepositoryDefaultImpl.php` | Insert/update/delete de `contents` |
| Model | `v6/app/Http/Model/Prefecture/Content.php` | Atas (`contents`) |
| Model | `v6/app/Http/Model/Prefecture/CategoryContent.php` | Categorias e subcategorias |
| Model | `v6/app/Http/Model/Prefecture/Archive.php` | Anexos (`archives`) |
| View (portal) | `v6/resources/views/portal/layouts/modelo1/comite-de-investimentos/*.blade.php` | `list`, `pub-nav`, `show`, `view` (6 modelos) |
| View (CMS) | `v6/resources/views/prefecture/contents/comite-de-investimentos/*.blade.php` | `index`, `create`, `edit`, `form` |
| View (CMS) | `v6/resources/views/prefecture/category-contents/comite-de-investimentos/*.blade.php` | `create`, `edit`, `form` |
| JS (CMS) | `v6/public/assets/js/custom/contents/comite-de-investimentos/datatables.js` | Colunas do DataTable |
| SQL | `v6/database/migrations/2024_10_16_011540_create_view_comite_de_investimentos.php` | Cria a view |
| SQL | `v6/database/migrations/2024_11_29_103842_update_view_comite_de_investimentos.php` | Amplia a view |

## 2. Fluxo portal — listagem (`show`)

```mermaid
flowchart TD
    A[GET /comite-de-investimentos/{slug}] --> B[Portal\ComiteDeInvestimentosController@show]
    B --> C[CategoryContent: categoria-pai slug=comite-de-investimentos<br/>category_contents_id IS NULL]
    C --> D[CategoryContent: filhas order ASC]
    D --> E{Categoria da URL existe?}
    E -- não --> F[redirect /publicacoes]
    E -- sim --> G[Content: publication_date <= now<br/>category_contents_id = filho]
    G --> H[DB::select: anos disponíveis]
    H --> I{request com parâmetros?}
    I -- sim --> J[DB::select: meses do ano]
    I -- não --> K
    J --> K[Filtros: whereYear / whereMonth / whereRaw ilike]
    K --> L{paginate 12}
    L -- request ajax --> M[Response::json(view publicacoes.list)]
    L -- normal --> N[view comite-de-investimentos.show]
```

Trecho crítico do controller
(`v6/app/Http/Controllers/Portal/ComiteDeInvestimentosController.php:25-49`):

```php
$comite_category_id = CategoryContent::where('slug', 'comite-de-investimentos')->where('category_contents_id', null)->first()->id;

$categories = CategoryContent::where('category_contents_id', $comite_category_id)->orderBy('order', 'asc')->get();

$category = $categories->where('slug', $slug)->first();

if (empty($category) && !isset($category))
    return redirect('/publicacoes');
```

## 3. Fluxo portal — leitura (`view`)

```mermaid
flowchart TD
    A[GET /comite-de-investimentos/{slug1}/{slug2}] --> B[Content::where slug = slug2]
    B --> C{publication_date > agora?}
    C -- sim --> D[redirect portal.comite-de-investimentos.index]
    C -- não --> E[Archive: highlight IS NULL]
    E --> F[Archive: view_pdf = true (first)]
    F --> G[content.clicks + 1 e update]
    G --> H[view comite-de-investimentos.view]
```

Referência: `v6/app/Http/Controllers/Portal/ComiteDeInvestimentosController.php:114-133`.

## 4. Fluxo CMS — listagem e cadastro

```mermaid
flowchart TD
    A[GET /cms/conteudos/comite-de-investimentos] --> B[ContentController@genericIndex]
    B --> C{slug == comite-de-investimentos}
    C --> D[returnIndex: menuClicks + log]
    D --> E[view prefecture.contents.comite-de-investimentos.index]
    E --> F[DataTable ajax GET /cms/datatable/comite_de_investimentos]
    F --> G[DatatableController@get_comite_de_investimentos]
    G --> H[DB::table('comite_de_investimentos')]
    H --> I[struct_views injeta edit/delete/active/inactivate]
    I --> J[DataTables::of(registros)->make(true)]

    K[GET /cms/conteudos/cadastrar/comite-de-investimentos] --> L[ContentController@genericGenerate]
    L --> M[case comite-de-investimentos]
    M --> N[returnData: categoryContent pai + categoryContentSelect]
    N --> O[view prefecture.contents.comite-de-investimentos.create]
    O --> P[POST /cms/conteudo -> ContentController@store]
```

## 5. Classes envolvidas — papel detalhado

| Classe | Caminho | Papel |
|---|---|---|
| `App\Http\Controllers\Portal\ComiteDeInvestimentosController` | `v6/app/Http/Controllers/Portal/ComiteDeInvestimentosController.php` | Publicação das atas; monta filtros; incrementa cliques |
| `App\Http\Controllers\Prefecture\ContentController` | `v6/app/Http/Controllers/Prefecture/ContentController.php` | CRUD genérico de `contents`; `switch` de slug na linha 113 (`comite-de-investimentos`) e na linha 226 (dados do formulário) |
| `App\Http\Controllers\Prefecture\CategoryContentController` | `v6/app/Http/Controllers/Prefecture/CategoryContentController.php` | CRUD genérico de `category_contents`; `case 'comite-de-investimentos'` nas linhas 275 e 388 |
| `App\Http\Controllers\Prefecture\DatatableController` | `v6/app/Http/Controllers/Prefecture/DatatableController.php` | DataTable genérico sobre view SQL |
| `App\Prefecture\Content` | `v6/app/Http/Model/Prefecture/Content.php` | Eloquent da tabela `contents`; `SoftDeletes`; relação `categoryContents()`; presenter `ContentPresenter` |
| `App\Prefecture\CategoryContent` | `v6/app/Http/Model/Prefecture/CategoryContent.php` | Eloquent de `category_contents`; auto-relacionamento `categoryContents()` |
| `App\Prefecture\Archive` | `v6/app/Http/Model/Prefecture/Archive.php` | Eloquent de `archives`; colunas `highlight` e `view_pdf` |
| `App\Prefecture\Configuration` | `v6/app/Http/Model/Prefecture/Configuration.php` | Chave `layout` define o tema do portal |
| `App\Http\Model\Admin\Modulos` | `v6/app/Http/Model/Admin/Modulos.php` | Registro do módulo (id 48) |

## 6. Middleware

| Middleware | Onde | Efeito |
|---|---|---|
| `check.route.permission` | `v6/app/Http/Controllers/Prefecture/ContentController.php:62` e `v6/app/Http/Controllers/Prefecture/CategoryContentController.php` | Exige login Sentinel e permissão textual |
| `check.logged` | grupo `/cms` em `v6/routes/basepro.php` | Exige sessão ativa no CMS |
| `csrf` | grupo `/cms` (`before => csrf`) | Token em formulários do CMS |
| (nenhum) | rotas do portal em `v6/routes/web.php:219-221` | Acesso público |

O middleware de permissão (`v6/app/Http/Middleware/CheckRoutePermission.php`)
remove o prefixo `painel.` e converte `_` em `-` no nome da rota, de modo que
`painel.feriados-municipais.index` vira `feriados-municipais.index`. Para o
comitê, o acesso é resolvido pelo nome da rota (`conteudo.index`,
`listar-conteudo`, `cadastrar-conteudo`, `editar-conteudo`) e pelo segmento da
URL, ambos com prefixo `comite-de-investimentos`.

## 7. Dependências externas (composer)

| Pacote | Uso no módulo |
|---|---|
| `laravel/framework` | Roteamento, Eloquent, Blade, paginação, `Response::json` |
| `yajra/laravel-datatables-oracle` | `DataTables::of(registros)->make(true)` no DataTable do CMS |
| `cartalyst/sentinel` | Autenticação e `hasAnyAccess` |
| `laracasts/presenter` | `ContentPresenter` / `CategoryContentPresenter` |
| `nesbot/carbon` | Manipulação de `publication_date` |
| `intervention/image` | Tratamento de imagens dentro do fluxo de `contents` |

## 8. Onde o módulo encosta em outros módulos

- **`contents` / `category_contents`**: núcleo compartilhado com Notícias,
  Publicações, Requerimentos, Atas e Pontos, Pauta do Dia, Transmissões.
- **`publicacoes`**: o controller redireciona para `/publicacoes` quando a
  categoria da URL não existe e o AJAX renderiza `publicacoes.list`.
- **`archives`**: anexos compartilhados com Diário Oficial e demais módulos.
- **`modulos` / `menu_orders`**: habilitação do menu do CMS.
- **`resources`**: catálogo de permissões (`resources.modulo_id = 48`).
