# Sessões da Câmara Municipal — Arquitetura

> Módulo `sessoes` (tabela `modulos`, id 40) na `v6`.
> **Nenhum código dedicado**: o módulo é uma *parametrização* do CRUD genérico
> de conteúdos (`contents`) mais uma view SQL de leitura para o CMS.

---

## 1. Visão geral das camadas

O módulo tem **duas faces** que compartilham a mesma fonte de dados:

```
                    ┌──────────────────────────────┐
   PORTAL (público)│  routes/web.php:113-114      │
                    └──────────────┬───────────────┘
                                   │
                    Portal\SessionsController@index / @show
                                   │
                 ┌─────────────────┴──────────────────┐
                 │                                    │
        App\Prefecture\Content            App\Prefecture\Archive
        App\Prefecture\CategoryContent    App\Prefecture\Link
                 │                                    │
                 └────────► PostgreSQL: contents / category_contents / archives / links

                    ┌──────────────────────────────┐
     CMS (AdminLTE) │  routes/basepro.php            │
                    └──────────────┬───────────────┘
                                   │
        Prefecture\ContentController@genericIndex / @genericCreate / @genericEdit
        Prefecture\ContentController@store / @update / @genericDestroy
        Prefecture\DatatableController@get_sessoes
                                   │
                 ┌─────────────────┴──────────────────┐
                 │                                    │
        App\Prefecture\Content (Eloquent)     view SQL `sessoes`
        App\Prefecture\Archive                (DB::table('sessoes')->get())
        App\Prefecture\Link
```

Não existe camada de Repository específica para sessões. O `ContentController`
recebe `IContentRepository` no construtor
(`v6/app/Http/Controllers/Prefecture/ContentController.php:58-68`), mas o
`store`/`update` de sessões grava **direto via Eloquent**
(`Content::create`, `Archive::create`, `Link::create`).

## 2. Fluxo da requisição — portal, listagem

1. `GET /sessoes` casa em `v6/routes/web.php:113`
   (`Route::get('/sessoes', 'Portal\SessionsController@index')->name('sessoes.index')`).
2. Middleware do grupo: `check.site.manutention` (aberto em `v6/routes/web.php:8`).
3. `SessionsController@index` (`v6/app/Http/Controllers/Portal/SessionsController.php:21`):
   - localiza a categoria por `CategoryContent::where('slug', 'sessoes')->first()`;
   - consulta `Content::with('archives')` filtrando `category_contents_id`,
     `publication_date <= date('Y-m-d H:i:s')` e `status = true`;
   - ordena por `publication_date DESC` e pagina 12 por página;
   - resolve o tema com `getTemplateActived()` e renderiza
     `portal.layouts.{layout}.sessoes.index`.
4. A view itera `$sessions` (uma `LengthAwarePaginator`) e monta os cards.

Trecho real do controller (`v6/app/Http/Controllers/Portal/SessionsController.php:27-32`):

```php
$sessions = Content::with('archives')
    ->where('category_contents_id', $category->id)
    ->where('publication_date', '<=', date('Y-m-d H:i:s'))
    ->where('status', true)
    ->orderBy('publication_date', 'DESC')
    ->paginate(12);
```

## 3. Fluxo da requisição — portal, detalhe

1. `GET /sessoes/{slug}` casa em `v6/routes/web.php:114` (nome `sessoes.show`).
2. `SessionsController@show` (`SessionsController.php:42-53`):
   - busca o conteúdo **restrito à categoria `sessoes`** via
     `whereHas('categoryContents', closure)` e `firstOrFail()` (404 se não achar);
   - carrega a galeria com `Archive::where('contents_id', $session->id)->where('highlight', null)->get()`;
   - calcula `$linkVideo = embedVideo($session->linksUnique->link)`.
3. A view `portal.layouts.{layout}.sessoes.show` renderiza destaque, texto,
   galeria (se `galery === true`) e o `<iframe>` do vídeo.

Trecho real (`v6/app/Http/Controllers/Portal/SessionsController.php:42-53`):

```php
$session = Content::whereHas('categoryContents', function ($query) use ($slug) {
    return $query->where('slug', 'sessoes');
})->where('slug', $slug)->firstOrFail();

$archives = Archive::where('contents_id', $session->id)->where('highlight', null)->get();
$linkVideo = isset($session->linksUnique) ? embedVideo($session->linksUnique->link): null;
```

## 4. Fluxo da requisição — CMS

| Passo | Arquivo | O que ocorre |
|---|---|---|
| 1 | `v6/routes/basepro.php:235` | `GET /cms/conteudos/{slug}` → `ContentController@genericIndex`. |
| 2 | `v6/app/Http/Controllers/Prefecture/ContentController.php:109` | `if ($slug === 'sessoes') return $this->returnIndex('Sessões', $slug);` |
| 3 | `ContentController.php:121-126` | `returnIndex()` chama `generateLog($title)`, `menuClicks($slug)` e renderiza `prefecture.contents.sessoes.index`. |
| 4 | `v6/public/assets/js/custom/contents/sessoes/datatables.js` | `runDatatables('sessoes', collums)` monta a tabela e faz AJAX em `/cms/datatable/sessoes`. |
| 5 | `v6/routes/basepro.php:398` | `GET /cms/datatable/sessoes` → `Prefecture\DatatableController@get_sessoes`. |
| 6 | `v6/app/Http/Controllers/Prefecture/DatatableController.php:28-32` | `DB::table('sessoes')->get()` (a **view**) e `struct_views($sessoes, 'sessoes')` anexa as flags `edit`/`delete`/`active`/`inactivate`. |
| 7 | `v6/routes/basepro.php:236-237` | `GET /cms/conteudos/cadastrar/sessoes` e `GET /cms/conteudos/editar/sessoes/{id}` → `genericCreate`/`genericEdit` → `genericGenerate` → `returnData` (`ContentController.php:197-201` para o slug `sessoes`). |
| 8 | `v6/routes/basepro.php:174` | `Route::resource('conteudo', 'Prefecture\ContentController')` → `POST /cms/conteudo` (`store`), `PUT /cms/conteudo/{id}` (`update`), `DELETE /cms/conteudo/{id}` (`destroy`). |
| 9 | `v6/app/Http/Requests/ContentRequest.php` | Valida pelo `switch ($slug)`, caso `sessoes`. |

Trecho real do dispatch por slug (`v6/app/Http/Controllers/Prefecture/ContentController.php:197-201`):

```php
if ($slug === 'sessoes') {
    return $this->returnData('Sessão', $slug, null, [
        'categoryContent' => $this->categoryContent->where('slug', 'sessoes')->whereNull('category_contents_id')->first()
    ], $id);
}
```

## 5. Diagrama de classes / responsabilidades

```mermaid
graph TD
    R1["routes/web.php:113-114"] --> SC["Portal\SessionsController<br/>v6/app/Http/Controllers/Portal/SessionsController.php"]
    R2["routes/basepro.php:235-237, 174, 398"] --> CC["Prefecture\ContentController<br/>v6/app/Http/Controllers/Prefecture/ContentController.php"]
    R2 --> DC["Prefecture\DatatableController<br/>v6/app/Http/Controllers/Prefecture/DatatableController.php"]
    SC --> C["App\Prefecture\Content<br/>v6/app/Http/Model/Prefecture/Content.php"]
    SC --> CAT["App\Prefecture\CategoryContent<br/>v6/app/Http/Model/Prefecture/CategoryContent.php"]
    SC --> AR["App\Prefecture\Archive<br/>v6/app/Http/Model/Prefecture/Archive.php"]
    SC --> LNK["App\Prefecture\Link<br/>v6/app/Http/Model/Prefecture/Link.php"]
    SC --> EV["embedVideo()<br/>v6/app/Helpers/functions.php:607"]
    CC --> REQ["App\Http\Requests\ContentRequest<br/>v6/app/Http/Requests/ContentRequest.php"]
    CC --> C
    CC --> AR
    CC --> LNK
    DC --> VIEW["view SQL sessoes<br/>v6/database/migrations/2024_09_30_004936_create_view_sessoes.php"]
    C --> PRES["App\Http\Presenters\ContentPresenter<br/>v6/app/Http/Presenters/ContentPresenter.php"]
```

## 6. Inventário de classes e arquivos

| Classe / arquivo | Caminho | Papel no módulo |
|---|---|---|
| `Portal\SessionsController` | `v6/app/Http/Controllers/Portal/SessionsController.php` | Listagem pública (pagina 12) e página de detalhe; calcula o *embed* do vídeo. |
| `Admin\Controller` | `v6/app/Http/Controllers/Admin/Controller.php` | Base dos controllers de portal; `getTemplateActived()` (linhas 39-49) escolhe o layout e aborta 404 `view_inexistente` se o tema não tiver `home/index`. |
| `Prefecture\ContentController` | `v6/app/Http/Controllers/Prefecture/ContentController.php` | CRUD genérico. Dispatch por slug: `genericIndex` (91), `genericCreate` (140), `genericEdit` (158), `genericGenerate` (163), `returnIndex` (121), `returnData` (275), `store` (616), `update` (937), `genericDestroy` (1369), `setStatus` (1548). |
| `Prefecture\DatatableController` | `v6/app/Http/Controllers/Prefecture/DatatableController.php` | `get_sessoes()` (28-32) alimenta a lista do CMS a partir da view SQL; `struct_views()` (504) injeta as flags de permissão. |
| `Prefecture\Controller` | `v6/app/Http/Controllers/Prefecture/Controller.php` | Base do CMS; registra `check.route.permission` no construtor. |
| `App\Http\Requests\ContentRequest` | `v6/app/Http/Requests/ContentRequest.php` | Regras de validação; `switch ($slug)` com o caso `sessoes`. |
| `App\Prefecture\Content` | `v6/app/Http/Model/Prefecture/Content.php` | Eloquent da tabela `contents`; relações `archives()`, `categoryContents()`, `linksUnique()`. |
| `App\Prefecture\CategoryContent` | `v6/app/Http/Model/Prefecture/CategoryContent.php` | Árvore de categorias (`category_contents_id`); raiz `sessoes`. |
| `App\Prefecture\Archive` | `v6/app/Http/Model/Prefecture/Archive.php` | Anexos (destaque e galeria). |
| `App\Prefecture\Link` | `v6/app/Http/Model/Prefecture/Link.php` | Link único (`linksUnique`) usado como vídeo. |
| `App\Http\Presenters\ContentPresenter` | `v6/app/Http/Presenters/ContentPresenter.php` | `getArchiveContentHighlightOrThumbnail()` (imagem de destaque ou thumbnail do vídeo) e `FormatDateDayMonthYear()` (usados nos cards). |
| `App\Http\Middleware\CheckRoutePermission` | `v6/app/Http/Middleware/CheckRoutePermission.php` | Autorização por rota do CMS; exige `sessoes.index`, `sessoes.create`, `sessoes.edit`, `sessoes.delete` conforme o segmento da URL. |
| `App\Http\Traits\AppTrait` | `v6/app/Http/Traits/AppTrait.php` | `slugfy()` (758) e `generateLog()` (770). |
| `App\Http\Traits\FileTrait` | `v6/app/Http/Traits/FileTrait.php` | Manipulação de arquivos (destaque e galeria). |
| `App\Providers\MenuServiceProvider` | `v6/app/Providers/MenuServiceProvider.php` | `case 'sessoes'` (51-53) monta o item de menu do CMS. |
| `App\Prefecture\Configuration` | `v6/app/Http/Model/Prefecture/Configuration.php` | Chave `layout` (tema ativo) lida por `getTemplateActived()`. |

## 7. Dependências externas

| Pacote (`v6/composer.json`) | Versão | Uso no módulo |
|---|---|---|
| `laravel/framework` | `5.5.*` | Framework, Eloquent, `SoftDeletes`, paginação. |
| `cartalyst/sentinel` | `2.0.*` | Sessão de login e permissões (`has_access`). |
| `yajra/laravel-datatables` | `^1.5` | DataTable server-side do CMS (`/cms/datatable/sessoes`). |
| `laracasts/presenter` | `^0.2.2` | `ContentPresenter` (destaque/thumbnail e data formatada). |
| `intervention/image` | `^2.7` | Compressão das imagens de destaque e da galeria. |
| `laracasts/flash` | `3.0.*` | Mensagens de sucesso/erro do CMS. |

Front-end involved: **jQuery + DataTables + Bootstrap 4** (portal),
**AdminLTE 1.22 / Bootstrap 3** (CMS), **Summernote** (texto rico),
**lightGallery** (galeria do portal), **AddThis** (barra de compartilhamento).

## 8. Onde o módulo se encaixa na plataforma

| Camada compartilhada | Elemento | Caminho |
|---|---|---|
| Layout do portal | Temas `modelo1`..`modelo6` | `v6/resources/views/portal/layouts/` |
| Widget da home | Bloco "Sessões" (4 itens) | `v6/resources/views/portal/layouts/modelo5/home/sessoes/sessoes-list.blade.php` |
| Home controller | Montagem de `$sessions` | `v6/app/Http/Controllers/Portal/PortalController.php:145-150` |
| Layout editor | Chave `layouts.sessoes` / `layouts.sessoes-list` | `v6/database/seeds/AddSessoesToLayout.php` |
| Permissões | Recurso `sessoes` (modulo 40) | `v6/database/seeds/ResourcesTableSeeder.php:486-493` |
| Menu do CMS | Item "Sessões" | `v6/app/Providers/MenuServiceProvider.php:51-53` |
| Telemetria | `menuClicks('sessoes')` | `v6/app/Helpers/functions.php:865` |
| Breadcrumbs | `category-content` e `content` | `v6/routes/breadcrumbs.php:65-74` |
