# Obras Paralisadas — Arquitetura

> Módulo **existente apenas na v5**. Toda a análise abaixo referencia arquivos
> sob `v5/`.

---

## 1. Visão geral das camadas

O módulo não usa repository, service, job, evento de domínio nem API JSON
autenticada. O fluxo é o CRUD clássico do Laravel 5.5 com DataTable server-side
no CMS e view Blade paginada no portal.

```
                          ┌──────────────────────────────────────────────┐
                          │  v5/routes/basepro.php  (prefixo /cms)       │
                          │  grupo 'cms' com ['before' => 'csrf']         │
                          └───────────────────┬──────────────────────────┘
                                              │ 7 rotas obra-paralisada.*
                                              ▼
              ┌───────────────────────────────────────────────────────────┐
              │  Prefecture\Controller (base)                             │
              │  app/Http/Controllers/Prefecture/Controller.php:22        │
              │  construtor aplica $this->middleware('check.route.permission')
              └───────────────────┬───────────────────────────────────────┘
                                  │
        ┌─────────────────────────┼──────────────────────────┐
        ▼                         ▼                          ▼
  GET index/create/edit     POST store / PUT update    DELETE destroy
        │                    DELETE destroy-arquivo        │
        │                         │                          │
        ▼                         ▼                          ▼
  view('prefecture.        ObraParalisadaRequest      ObraParalisadaRequest
  obras-paralisadas.*')   (authorize() === true)      (idem)
                                  │                          │
                                  ▼                          ▼
                     App\Http\Model\Prefecture\ObraParalisada
                     (SoftDeletes, table obras_paralisadas)
                     hooks created/updating → str_slug(identificacao-id)
                                  │
                                  ▼
                     tabela `obras_paralisadas` (PostgreSQL 10)
                     coluna `arquivo` = JSON [{path, description}]
```

Fluxo do portal (separado, sem sessão e sem permissão):

```
  v5/routes/web.php:5   Route::middleware(['check.site.manutention'])->group(...)
  v5/routes/web.php:211 Route::any('obras-paralisadas')  → obras-paralisadas.index
  v5/routes/web.php:212 Route::get('obra-paralisada/{slug}') → obras-paralisadas.show
                                  │
                                  ▼
        App\Http\Controllers\Portal\ObrasParalisadasController
        (extends App\Http\Controllers\Admin\Controller)
        ├─ buildQuery(Request)  → filtro busca + orderBy('identificacao')
        ├─ (clone $query)->get()   → $allData (todos os registros)
        ├─ $query->paginate(15)     → $result
        └─ exportar(type, $allData) → csv | xls | txt
                                  │
                                  ▼
   view('portal.layouts.{layout}.obras.paralisadas.index')
        ├─ @include portal.layouts.{layout}.obras.paralisadas.filtro
        ├─ @include portal.components.obras-paralisadas.table
        └─ <script>var obrasParalisadas = {json_encode($allData)}</script>
```

## 2. Inventário de classes

| Classe | Caminho | Papel |
|---|---|---|
| `ObraParalisadaController` | `v5/app/Http/Controllers/Prefecture/ObraParalisadaController.php` | CRUD do CMS (index, create, store, edit, update, destroy, destroyArquivo). Usa `FileTrait`. |
| `ObrasParalisadasController` | `v5/app/Http/Controllers/Portal/ObrasParalisadasController.php` | Listagem pública com filtro, ficha por slug e exportações csv/xls/txt. |
| `Controller` (Prefecture) | `v5/app/Http/Controllers/Prefecture/Controller.php` | Base do CMS; no construtor aplica `check.route.permission` a **todas** as actions herdadas. |
| `Controller` (Admin) | `v5/app/Http/Controllers/Admin/Controller.php` | Base do portal; fornece `getTemplateActived()` que lê a configuração `layout`. |
| `DatatableController` | `v5/app/Http/Controllers/Prefecture/DatatableController.php:158-168` | Serve o JSON do DataTable do CMS (`get_obras_paralisadas`). Também estende `Prefecture\Controller`. |
| `ObraParalisada` | `v5/app/Http/Model/Prefecture/ObraParalisada.php` | Model Eloquent, `SoftDeletes`, tabela `obras_paralisadas`, hooks de slug. |
| `ObraParalisadaRequest` | `v5/app/Http/Requests/ObraParalisadaRequest.php` | Validação do formulário (regras + mensagens em pt-BR). |
| `FileTrait` | `v5/app/Http/Traits/FileTrait.php` | `saveUpload()`, `deleteFile()`, `getFileName()`, `compress()`. |
| `CreateObrasParalisadasTable` | `v5/database/migrations/2026_05_26_000003_create_obras_paralisadas_table.php` | Cria a tabela. |
| `MenuServiceProvider` | `v5/app/Providers/MenuServiceProvider.php:362-367` | Registra o item "Obras Paralisadas" no submenu "Obras". |
| `CheckRoutePermission` | `v5/app/Http/Middleware/CheckRoutePermission.php` | Autorização por rota/permissão; herdado pelo controller base do CMS. |
| `CheckLogged` | `v5/app/Http/Middleware/CheckLogged.php` | Exige sessão do Sentinel; declarado apenas em 3 das 7 rotas do CMS. |
| `CheckSiteManutention` | `v5/app/Http/Middleware/CheckSiteManutention.php` | Middleware do grupo público do portal. |
| `appHelpers` | `v5/app/Library/Helper/appHelpers.php:44-64` | `has_access()` e `resolve_permission_aliases()` (mapeia `obra-paralisada` → `obras`). |

## 3. Diagrama de fluxo (Mermaid)

```mermaid
flowchart TD
    subgraph CMS["CMS /cms/obras-paralisadas"]
        R1[GET index / create / edit<br/>check.logged]
        R2[POST store / PUT update<br/>DELETE destroy / destroy-arquivo<br/>sem check.logged]
        M1[check.route.permission<br/>(herdado de Prefecture\Controller)]
        V1[ObraParalisadaRequest]
        C1[ObraParalisadaController]
        D1[GET /cms/datatable/obra-paralisada<br/>DatatableController]
    end
    subgraph Portal["Portal público"]
        R3[GET /obras-paralisadas<br/>GET /obra-paralisada/{slug}]
        M2[check.site.manutention]
        C2[ObrasParalisadasController]
        V2[views modelo1..modelo6]
    end
    R1 --> M1 --> C1
    R2 --> M1 --> V1 --> C1
    C1 --> DB[(obras_paralisadas)]
    C1 --> FS[(storage/app/public/docs)]
    D1 --> DB
    R3 --> M2 --> C2
    C2 --> DB
    C2 --> V2
```

## 4. Fluxo detalhado do cadastro (CMS)

1. `POST /cms/obras-paralisadas` entra no grupo `['prefix' => 'cms', 'before' => 'csrf']`
   (`v5/routes/basepro.php:35`), portanto passa pelo token CSRF.
2. O construtor de `Prefecture\Controller` registra `check.route.permission`;
   sem sessão o middleware redireciona para `login`
   (`v5/app/Http/Middleware/CheckRoutePermission.php:29-32`).
3. `ObraParalisadaRequest` valida: `identificacao`, `motivo_paralisacao`,
   `responsavel`, `previsao_reinicio` obrigatórios; `arquivo` array de PDFs de
   até 10 MB; `arquivo_desc` array de textos.
4. O controller extrai apenas os quatro campos de texto com
   `$request->only([...])` e trata os arquivos em separado.
5. Cada arquivo vai para o disk `public`, subpasta `docs`, com nome gerado por
   `uniqid()` + 5 caracteres aleatórios + extensão original
   (`v5/app/Http/Traits/FileTrait.php:78-82`).
6. O array `[{path, description}]` é serializado em JSON e gravado na coluna
   `arquivo`.
7. `ObraParalisada::create($data)` dispara o hook `created`, que calcula
   `slug = str_slug(identificacao . '-' . $id)` e salva novamente.
8. Flash de sucesso e `redirect()->route('obra-paralisada.index')`.

## 5. Fluxo detalhado da listagem pública

1. `GET /obras-paralisadas?busca=...&type=...` — `Route::any`, então aceita
   qualquer verbo HTTP.
2. `buildQuery()` monta `ObraParalisada::query()->orderBy('identificacao')` e,
   se houver `busca`, aplica um `where` aninhado com dois `whereRaw`.
3. `$allData = (clone $query)->get()` carrega **todo** o resultado.
4. `$result = $query->paginate(15)`.
5. Se `type` estiver presente, `exportar()` devolve csv/xls/txt e aborta 404
   para tipos desconhecidos.
6. Sem `type`, a view do layout ativo é renderizada com `$title`, `$result`,
   `$busca` e `$allData`.

## 6. Dependências externas

| Pacote | Versão (v5) | Uso no módulo |
|---|---|---|
| `laravel/framework` | `5.5.*` | Base do módulo (rotas, Eloquent, Blade, sessão). |
| `yajra/laravel-datatables` | `^1.5` | `DataTables::of(...)->make(true)` em `DatatableController@get_obras_paralisadas`. |
| `cartalyst/sentinel` | `2.0.*` | Sessão e permissões (`Sentinel::check()`, `hasAnyAccess`). |
| `laracasts/flash` | `3.0.*` | Helper `flash('...')->success()` / `->error()`. |
| `laralabs/toaster` | `^2.0` | Toasts exibidos a partir do retorno JSON das actions de delete. |
| `intervention/image` | `^2.7` | Usado por `FileTrait::compress()`; **não** aplicado aos anexos deste módulo (`$compress = false`). |
| `jsPDF` + `jspdf-autotable` | vendorizados em `v5/public/assets/js/modulos/jspdf/` | Geração do PDF no navegador. |
| DataTables / jQuery | vendorizados em `v5/public/assets/` | Tabela do CMS. |

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

| Acoplamento | Detalhe |
|---|---|
| Recurso `obras` (`resources.id = 13`) | As permissões `obra-paralisada.*` são resolvidas para `obras.*` por `resolve_permission_aliases()` (`v5/app/Library/Helper/appHelpers.php:44-64`). Não existe `resources` com slug `obras-paralisadas`. |
| Menu "Obras" | O item é inserido no submenu do recurso `obras` (`v5/app/Providers/MenuServiceProvider.php:362-367`). |
| Módulo "Obras" (`works`) | Compartilham o nome no menu e o tema do AdminLTE, mas **nenhuma** tabela, rota ou model. O pivot `work_archives` e a tabela `archives` **não** são usados aqui. |
| Configuração `layout` | `getTemplateActived()` (`v5/app/Http/Controllers/Admin/Controller.php:39-49`) decide qual par de views `modelo1..modelo6` será usado no portal. |
