# Unidades de Atendimento — Arquitetura

> Módulo **novo na v6**. Não existe equivalente na `v5`
> (`grep -r "unidades-de-atendimento\|ServiceUnit" v5/` → nenhum resultado).

---

## 1. Visão geral

O módulo não possui camada própria de domínio. Ele é uma **specialização por
slug** de três mecanismos genéricos da plataforma:

1. **Portal**: `Portal\ServiceUnitsController` — rotas próprias `/unidades-de-atendimento`
   e `/unidades-de-atendimento/{slug}` (`v6/routes/web.php:116-117`).
2. **CMS**: `Prefecture\ContentController@genericIndex|genericCreate|genericEdit`
   — resolve a view pelo slug (`v6/routes/basepro.php:235-237`).
3. **Leitura tabular do CMS**: `Prefecture\DatatableController@get_unidades_de_atendimento`
   — consulta a view SQL `unidades_de_atendimento` (`v6/routes/basepro.php:461`).

## 2. Fluxo de requisição — Portal (index)

```
GET /unidades-de-atendimento
  └─ web.php:116  Route::get → Portal\ServiceUnitsController@index
       └─ Admin\Controller (getTemplateActived)  [v6/app/Http/Controllers/Admin/Controller.php:39]
            └─ Configuration::where('key','layout')->first()->value  → "modelo1"
       └─ CategoryContent::where('slug','unidades-de-atendimento')->first()
       └─ Content::whereNull('deleted_at')
              ->where('category_contents_id', $category->id)
              ->with('archives')
              ->orderBy('publication_date','desc')->orderBy('id','desc')
              ->paginate(12)
       └─ view('portal.layouts.{layout}.unidades-de-atendimento.index',
               compact('contents','category'))
```

## 3. Fluxo de requisição — Portal (show)

```
GET /unidades-de-atendimento/{slug}
  └─ web.php:117  Route::get → Portal\ServiceUnitsController@show
       └─ Content::whereHas('categoryContents', fn($q) => $q->where('slug','unidades-de-atendimento'))
                     ->where('slug',$slug)
                     ->with('archives')
                     ->firstOrFail()          → 404 se não existir
       └─ Archive::where('contents_id',$content->id)->where('highlight',null)->get()
       └─ view('portal.layouts.{layout}.unidades-de-atendimento.show',
               compact('content','archives'))
```

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

```
GET /cms/conteudos/unidades-de-atendimento
  └─ basepro.php:235 → ContentController@genericIndex('unidades-de-atendimento')
       └─ returnIndex('Unidades de Atendimento', $slug)
            ├─ generateLog($title)              [log de atividade]
            ├─ menuClicks('unidades-de-atendimento')
            └─ view('prefecture.contents.unidades-de-atendimento.index', ['title' => $title])

GET /cms/conteudos/cadastrar/unidades-de-atendimento
  └─ basepro.php:236 → ContentController@genericCreate → genericGenerate
       └─ returnData('Unidades de Atendimento', $slug, null,
             ['categoryContent' => CategoryContent::where('slug','unidades-de-atendimento')
                                    ->whereNull('category_contents_id')->first()], $id)
       └─ view('prefecture.contents.unidades-de-atendimento.create')

GET /cms/conteudos/editar/unidades-de-atendimento/{id}
  └─ basepro.php:237 → ContentController@genericEdit → genericGenerate(slug, id)
       └─ view('prefecture.contents.unidades-de-atendimento.edit')
```

Persistência (criar/editar) usa o **resource route** genérico:

```
POST   /cms/conteudo          → ContentController@store   (ContentRequest)  → "conteudo.store"
PUT    /cms/conteudo/{id}     → ContentController@update  (ContentRequest)  → "conteudo.update"
DELETE /cms/conteudo/{id}     → ContentController@destroy
DELETE /cms/conteudo/delete/{id} → ContentController@genericDestroy  ("generic.conteudo.delete")
POST   /cms/conteudo/deleteAll   → ContentController@genericDeleteAll  ("ajax.generic.content.deleteAll")
```
(`v6/routes/basepro.php:141`, `:174`, `:223`)

## 5. Fluxo da DataTable do CMS

```
GET /cms/datatable/unidades-de-atendimento
  └─ basepro.php:461 → DatatableController@get_unidades_de_atendimento
       └─ DB::table('unidades_de_atendimento')->get()      ← view SQL
       └─ struct_views($unidades, 'unidades_de_atendimento')
            ├─ edit      = has_access('unidades_de_atendimento.edit')
            ├─ delete    = has_access('unidades_de_atendimento.delete')
            ├─ active    = has_access('unidades_de_atendimento.restore')
            └─ inactivate = has_access('unidades_de_atendimento.inactivate')
       └─ DataTables::of($this->struct_views($unidades, 'unidades_de_atendimento'))->make(true)
```
Consumido por `v6/public/assets/js/custom/contents/unidades-de-atendimento/datatable.js`
(via `runDatatables()` em `v6/public/assets/js/custom/utilities/table.js:3`, que faz
`ajax: { url: '/cms/datatable/' + url }` em `:183`).

## 6. Diagrama (Mermaid)

```mermaid
flowchart TD
  subgraph Portal
    R1[GET /unidades-de-atendimento] --> C1[Portal\ServiceUnitsController@index]
    R2[GET /unidades-de-atendimento/{slug}] --> C2[Portal\ServiceUnitsController@show]
  end
  subgraph CMS
    R3[GET /cms/conteudos/unidades-de-atendimento] --> C3[ContentController@genericIndex]
    R4[GET /cms/conteudos/cadastrar/{slug}] --> C4[ContentController@genericCreate]
    R4b[GET /cms/conteudos/editar/{slug}/{id}] --> C4b[ContentController@genericEdit]
    R5[GET /cms/datatable/unidades-de-atendimento] --> C5[DatatableController@get_unidades_de_atendimento]
    R6[POST /cms/conteudo] --> C6[ContentController@store]
    R6b[PUT /cms/conteudo/{id}] --> C6b[ContentController@update]
    R6c[DELETE /cms/conteudo/{id}] --> C6c[ContentController@destroy]
  end
  C1 --> M1[(contents + category_contents + archives)]
  C2 --> M1
  C4 --> M1
  C4b --> M1
  C6 --> M1
  C6b --> M1
  C6c --> M1
  C5 --> V[(view unidades_de_atendimento)]
  C3 --> VW[prefecture.contents.unidades-de-atendimento.index]
  C1 --> VW1[portal.layouts.{layout}.unidades-de-atendimento.index]
  C2 --> VW2[portal.layouts.{layout}.unidades-de-atendimento.show]
```

## 7. Classes envolvidas

| Classe | Arquivo | Papel |
|---|---|---|
| `App\Http\Controllers\Portal\ServiceUnitsController` | `v6/app/Http/Controllers/Portal/ServiceUnitsController.php` | Listagem e detalhe público. |
| `App\Http\Controllers\Admin\Controller` | `v6/app/Http/Controllers/Admin/Controller.php` | Base do controller portal; `getTemplateActived()` (`:39`). |
| `App\Http\Traits\GetInformationsPrefectureTrait` | `v6/app/Http/Traits/GetInformationsPrefectureTrait.php` | Segunda implementação de `getTemplateActived()` (`:92`). |
| `App\Http\Controllers\Prefecture\ContentController` | `v6/app/Http/Controllers/Prefecture/ContentController.php` | CMS genérico (listar/cadastrar/editar/salvar/excluir). |
| `App\Http\Controllers\Prefecture\DatatableController` | `v6/app/Http/Controllers/Prefecture/DatatableController.php` | Fonte JSON da DataTable (`:474-478`), `struct_views()` (`:504-520`). |
| `App\Http\Requests\ContentRequest` | `v6/app/Http/Requests/ContentRequest.php` | Validação de `title`, `category_contents_id`, `archive.*`, `archive_highlight` (regra `default`, `:308-314`). |
| `App\Prefecture\Content` | `v6/app/Http/Model/Prefecture/Content.php` | Model dos registros (tabela `contents`, `SoftDeletes`, `PresentableTrait`). |
| `App\Prefecture\CategoryContent` | `v6/app/Http/Model/Prefecture/CategoryContent.php` | Model da categoria (tabela `category_contents`). |
| `App\Prefecture\Archive` | `v6/app/Http/Model/Prefecture/Archive.php` | Model dos arquivos (tabela `archives`). |
| `App\Prefecture\Link` | `v6/app/Http/Model/Prefecture/Link.php` | Link único (vídeo) ligado por `Content::linksUnique()`. |
| `App\Http\Presenters\ContentPresenter` | `v6/app/Http/Presenters/ContentPresenter.php` | `getArchiveContentHighlightOrThumbnail()` (`:89`), usado por `modelo3`/`modelo4`. |
| `App\Repositories\Contracts\IContentRepository` | `v6/app/Repositories/Contracts/IContentRepository.php` | Injetado no `ContentController` para `destroyAll()`. |
| `App\Prefecture\Configuration` | `v6/app/Http/Model/Prefecture/Configuration.php` | Fonte do layout ativo (`key = 'layout'`). |
| `App\Prefecture\MenuOrder` | `v6/app/Http/Model/Prefecture/MenuOrder.php` | Registro do slug com `modulo_id = 47` (contador de cliques). |

## 8. Dependências externas

| Pacote | Uso no módulo |
|---|---|
| `laravel/framework` 5.5.50 | Roteamento, Eloquent, paginação, Blade. |
| `yajra/laravel-datatables-oracle` | Resposta JSON da listagem do CMS (`DataTables::of($this->struct_views($unidades, 'unidades_de_atendimento'))->make(true)`). |
| `laracasts/presenter` | `PresentableTrait` em `Content`/`CategoryContent`. |
| `cartalyst/sentinel` | `has_access()`, `Sentinel::check()`, `Sentinel::inRole('super-admin')`. |
| `intervention/image` | Recorte/edição da imagem de destaque no CMS (`select-image.js` + `cropper-min.js`). |
| `youtubeurl`/helper próprio | `normalizeVideoLink()` para o campo de vídeo (aplicado em `ContentController@store`). |

## 9. Fronteiras e responsabilidades

- **O módulo não tem service, repository ou policy própria.** Toda a lógica de
  persistência é herdada do `ContentController` genérico.
- **A view SQL é somente leitura** e é o único ponto de acoplamento do CMS com
  uma estrutura específica do módulo (nomes de colunas `titulo`, `data_e_hora`,
  `galeria`).
- **Middleware do portal**: apenas o grupo padrão de `v6/routes/web.php` (sem
  `check.logged`); o CMS exige `check.route.permission`, aplicado no construtor
  do `ContentController` (`v6/app/Http/Controllers/Prefecture/ContentController.php:60`).
