# Acesso Rápido — Rotas e Controllers

## 1. Rotas do Portal (público)

Arquivo: `v6/routes/web.php`. Todas as rotas do portal estão dentro do grupo
`Route::middleware(['check.site.manutention'])` (`v6/routes/web.php:7`).

| Método | URI | Nome da rota | Controller@action | Linha |
|---|---|---|---|---|
| ANY | `/acesso-rapido/{slug}` | `acesso-rapido.index` | `Portal\QuickAccessController@index` | `v6/routes/web.php:89-91` |
| ANY | `/acessos-rapido/{slug}` | `acessos-rapido.index` | `Portal\QuickAccessController@getIndexStatic` | `v6/routes/web.php:93-95` |

Código real:

```php
// v6/routes/web.php:89-95
Route::prefix('acesso-rapido')->group(function () {
    Route::any('/{slug}', 'Portal\QuickAccessController@index')->name('acesso-rapido.index');
});

Route::prefix('acessos-rapido')->group(function () {
    Route::any('/{slug}', 'Portal\QuickAccessController@getIndexStatic')->name('acessos-rapido.index');
});
```

> **Dois prefixos diferentes para o mesmo conceito:** `acesso-rapido` (singular) resolve
> *conteúdo de categoria*; `acessos-rapido` (plural) resolve *página estática*. Ambos
> caem no mesmo controller e, na prática, o singular faz fallback para o plural.

### 1.1 Por que `Route::any`?

Nenhuma justificativa funcional existe no código: as páginas são somente leitura
(GET). `Route::any` faz o controller responder também a POST/PUT/DELETE no mesmo
caminho, o que:

- permite que um `POST` acione `updateClickStaticPage()` e incremente cliques;
- cria superfície de CSRF desnecessária (o grupo `web` aplica `VerifyCsrfToken` a
  requisições POST, mas o controller do portal não aplica `csrf` explicitamente);
- mascara erros de digitação de URL (ex.: `/acessos-rapid/ouvidoria`).

### 1.2 Rotas do portal que **redirecionam** para o módulo

| Origem | Destino | Trecho |
|---|---|---|
| `/acesso-rapido/ouvidoria` | `/acessos-rapido/solicitar-informacao` | `v6/app/Http/Controllers/Portal/QuickAccessController.php:31-33` |
| `/pagina/perguntas-frequentes` (`conteudo.portal.list`) | `acesso-rapido.index` com `perguntas-frequentes` | `v6/app/Http/Controllers/Portal/PortalController.php:323` |
| `/pagina/manual-de-navegacao` | `acesso-rapido.index` com `manual-de-navegacao` | `v6/app/Http/Controllers/Portal/PortalController.php:297` |
| `/pagina/solicitar-informacao` | `acesso-rapido.index` com `solicitar-informacao` | `v6/app/Http/Controllers/Portal/PortalController.php:298` |
| `/pagina/acompanhe-sua-solicitacao` | `acesso-rapido.index` com `acompanhe-sua-solicitacao` | `v6/app/Http/Controllers/Portal/PortalController.php:299` |

### 1.3 Endpoints de apoio consumidos pelas telas do módulo

| Método | URI | Nome | Controller |
|---|---|---|---|
| POST | `/informacao-solicitada-ajax` | `requested-information-ajax` | `Portal\InformationController@storeRequestInformacaoAjax` (`v6/routes/web.php:17`) |
| POST | `/cms/ajax/get/information/by/protocol` | `ajax.get.information.by.protocol` | `Portal\InformationController@getInformationByProtocol` (`v6/routes/basepro.php:117`) |
| POST | `/cms/ajax/get/answer/by/information` | `ajax.get.answer.by.information` | `Portal\InformationController@getAnswerByInformation` (`v6/routes/basepro.php:119`) |
| POST | `/cms/ajax/get/answer/by/ombudsman` | `ajax.get.answer.by.ombudsman` | `Portal\OmbudsmanController@getAnswerByOmbudsman` (`v6/routes/basepro.php:120`) |
| POST | `/cms/ajax/get/acidade/by/state` | `ajax.get.city.by.state` | `Admin\CityController@ajaxGetCitiesByStateId` (`v6/routes/basepro.php:115`) |

> Os dois últimos são **rotas do CMS** (prefixo `/cms/ajax/`) chamadas por Blades do
> **portal**. Funcionam porque o prefixo `cms` é apenas um prefixo de URL, sem
> autenticação associada ao grupo; o controller `Admin\CityController` e o
> `Portal\InformationController` não exigem login.

## 2. Rotas do CMS (prefixo `/cms`)

Arquivo: `v6/routes/basepro.php`. O grupo `Route::group(['prefix' => 'cms', 'before' => 'csrf'], function () {`
começa em `v6/routes/basepro.php:35` e envolve o restante do arquivo.

| Método | URI | Nome da rota | Controller@action | Linha |
|---|---|---|---|---|
| GET | `/cms/conteudos/acesso-rapido` | `listar-conteudo` | `Prefecture\ContentController@genericIndex` | `v6/routes/basepro.php:235` |
| GET | `/cms/conteudos/cadastrar/acesso-rapido` | `cadastrar-conteudo` | `Prefecture\ContentController@genericCreate` | `v6/routes/basepro.php:236` |
| GET | `/cms/conteudos/editar/acesso-rapido/{id}` | `editar-conteudo` | `Prefecture\ContentController@genericEdit` | `v6/routes/basepro.php:237` |
| DELETE | `/cms/conteudo/delete/{id}` | `generic.conteudo.delete` | `Prefecture\ContentController@genericDestroy` | `v6/routes/basepro.php:223` |
| GET | `/cms/datatable/acesso_rapido` | `datatable.acesso_rapido` | `Prefecture\DatatableController@get_acesso_rapido` | `v6/routes/basepro.php:430` |
| POST | `/cms/ajax/ajust-banners` | `ajust.banners` | `Prefecture\ContentController@ajustOrder` | `v6/routes/basepro.php:137` |
| POST | `/cms/set-status` | `set.status` | `Prefecture\ContentController@setStatus` | `v6/routes/basepro.php:347` |
| GET/POST | `/cms/modulos`, `/cms/modulos` | `modulos.index`, `modulos.update` | `Admin\ModulosController@index/update` | `v6/routes/basepro.php:40-41` |

> A rota de listagem do módulo **não é exclusiva**: `listar-conteudo` atende todos os
> slugs de `contents` (notícias, banners, legislação, etc.); o controller decide o
> destino por uma cadeia de `if` (`v6/app/Http/Controllers/Prefecture/ContentController.php:97-121`).
> Para `acesso-rapido` o ramo é `if ($slug === 'acesso-rapido') return $this->returnIndex('Acesso Rápido', $slug);` (linha 105).

## 3. Controllers

### 3.1 `Portal\QuickAccessController`

Arquivo: `v6/app/Http/Controllers/Portal/QuickAccessController.php` (73 linhas).

| Membro | Detalhe |
|---|---|
| Herança | `extends App\Http\Controllers\Admin\Controller` |
| Traits | `NewsTrait`, `GetInformationsPrefectureTrait` |
| Propriedades | `public $view`, `protected $content`, `protected $categoryContent` |
| Construtor | `__construct(Content $content, CategoryContent $categoryContent)`; monta `$this->view = "portal.layouts.{$this->getTemplateActived()}.informacoes-instituicao.acesso-rapido.";` |
| `index($slug1)` | Redireciona `ouvidoria`; busca conteúdos da categoria; delega a `getIndexStatic` quando vazio. |
| `getIndexStatic($slug1)` | Carrega página estática, arquivos, PDF, estados/cidades; renderiza. |

Código real do método `index`:

```php
public function index($slug1)
{
    if ($slug1 === 'ouvidoria') {
        return redirect()->to(url('/acessos-rapido/solicitar-informacao'));
    }

    $contentCategoriesNews = $this->getContentNews();
    $contents = $this->getContentByCategory($slug1);

    if (!count($contents)) {
        return $this->getIndexStatic($slug1);
    }

    return view($this->view . $slug1, compact('contentCategoriesNews','contents'));
}
```

> O nome do método mudou de `getIndex` (v5) para `index` (v6). Como a v6 usa
> `Route::any('/{slug}', 'Portal\QuickAccessController@index')->name('acesso-rapido.index')`, o nome da rota e o do método
> passaram a coincidir. Ver `08-GAPS-E-DIVIDAS.md`.

### 3.2 `Prefecture\DatatableController@get_acesso_rapido`

Arquivo: `v6/app/Http/Controllers/Prefecture/DatatableController.php:234-238`.

```php
public function get_acesso_rapido()
{
    $links = DB::table("acesso_rapido")->get();
    return DataTables::of($this->struct_views($links, 'acesso-rapido', true, false))->make(true);
}
```

Observações:

- `struct_views()` (linha 504) aceita **2** parâmetros; a chamada passa **4**
  (`'acesso-rapido', true, false`). PHP aceita argumentos extras, que são ignorados.
- `struct_views` anexa por item: `edit` (`has_access("acesso-rapido.edit")`),
  `delete` (`has_access("acesso-rapido.delete")`), `active` (`has_access("acesso-rapido.restore")`),
  `inactivate` (`has_access("acesso-rapido.inactivate")`).
- A leitura é via **view SQL** `acesso_rapido` (`DB::table`), não via Eloquent.
- Não há paginação server-side: `->get()` traz a tabela inteira.

### 3.3 `Prefecture\ContentController` (trechos usados pelo módulo)

| Método | Linha | Papel no módulo |
|---|---|---|
| `genericIndex` | 91 | Roteia o slug `acesso-rapido` para `returnIndex('Acesso Rápido', $slug)` (linha 105). |
| `returnIndex` | 121 | `generateLog($title)`, `menuClicks($slug)` (incrementa `menu_orders.clicks` da linha `acesso-rapido`) e renderiza `prefecture.contents.$slug.index`. |
| `genericCreate` / `genericEdit` | 140 / 158 | Delegam para `genericGenerate`. |
| `genericGenerate` | 163 | `if ($slug === 'acesso-rapido') return $this->returnData('Acesso Rápido', $slug, null, [], $id);` (linha 176). |
| `returnData` | 275 | Monta o formulário (view `prefecture.contents.$slug.form`). |
| `ajustOrder` | 1498 | `reorder(new Content(), $request->sortable_data)` — grava `order = $index + 1`. |
| `setStatus` | 1548 | Alterna `contents.status`. |
| `genericDestroy` | 1369 | Exclusão definitiva (rota `generic.conteudo.delete`). |

## 4. Validação (Form Requests)

**O módulo não possui Form Request próprio.** A validação do formulário do CMS vem
do `ContentRequest` genérico, compartilhado por todos os tipos de conteúdo.
`ContentRequest` é aplicado no `ContentController@store` (linha 616) e
`@update` (linha 937).

O `ContentRequest` tem um `case 'acesso-rapido'` próprio
(`v6/app/Http/Requests/ContentRequest.php:193-202`):

```php
case 'acesso-rapido':
    $rules = [
        "title" => "required|max:25",
        "text" => "required|max:50",
        "category_contents_id" => "required|numeric",
        "icon" => "required",
        "link" => "required|max:2999",
        "isBlank" => "",
    ];
    break;
```

| Campo | Regra no servidor | Limite no form do CMS |
|---|---|---|
| `title` | `required|max:25` | `maxlen = 25` |
| `text` | `required|max:50` | `maxlen = 50` |
| `category_contents_id` | `required|numeric` | campo oculto |
| `icon` | `required` | `maxlen = 120` |
| `link` | `required|max:2999` | `maxlen = 300` |
| `isBlank` | sem regra (string vazia = regra inválida, ignorada pelo Laravel) | checkbox "Abrir em nova guia" |
| `status` | **sem regra** (checkbox "Ativar no portal") | checkbox |

> `status` e `order` não são validados: o operador pode gravar `status` ausente
> (vira `null`, e o item some do portal porque a consulta do composer exige
> `where('status', true)`).

> O slug da categoria é resolvido pelo `ContentRequest` a partir de
> `category_contents_id` (`CategoryContent::find($request->get('category_contents_id'))->slug`,
> `v6/app/Http/Requests/ContentRequest.php:37`) — o que exige que o
> `category_contents_id` enviado aponte para a categoria `acesso-rapido`; caso
> contrário caem no `default` (que não valida `icon` nem `link`).

## 5. API Resources / presenters

**Não há API Resource nem presenter para o módulo.** O controller do portal entrega
Blade com arrays associativos; o CMS entrega JSON do `yajra/laravel-datatables-oracle`
(`->make(true)`), com o formato padrão:

```json
{
  "draw": 1,
  "recordsTotal": 3,
  "recordsFiltered": 3,
  "data": [
    { "value": 1, "titulo": "Perguntas Frequentes", "id": 179,
      "status_name": "Ativado", "status": true,
      "edit": true, "delete": true, "active": true, "inactivate": true }
  ]
}
```

## 6. Permissões exigidas

O CMS usa o par `resources.slug` + `permissions.slug` para montar as permissões
`{recurso}.{acao}` (ver `v6/resources/views/admin/role/form.blade.php:47-88`).
Para este módulo: `acesso-rapido.index`, `.create`, `.edit`, `.delete`, `.inactive`,
`.inactivate`, `.restore`, `.show`.

| Recurso | `resources.id` | `modulo_id` |
|---|---|---|
| `acesso-rapido` | 69 | 49 |

Estado no seed de papéis (`v6/database/seeds/RolesTableSeeder.php:110`): o papel
**`administrador`** tem todas as permissões de `acesso-rapido.*` iguais a `false`;
o papel **`super-admin`** (linha 16) ignora a checagem.

Autorização efetiva por rota (`v6/app/Http/Middleware/CheckRoutePermission.php`):

| Rota | Segmento calculado | Permissão exigida |
|---|---|---|
| `listar-conteudo` (`/cms/conteudos/acesso-rapido`) | `acesso-rapido.index` | `acesso-rapido.index` |
| `cadastrar-conteudo` (`/cms/conteudos/cadastrar/acesso-rapido`) | `acesso-rapido.create` | `acesso-rapido.create` |
| `editar-conteudo` (`/cms/conteudos/editar/acesso-rapido/{id}`) | `acesso-rapido.edit` | `acesso-rapido.edit` |
| `datatable.acesso_rapido` | — | `datatable` está em `$openRoutes`: **aberta** para qualquer logado |
| `ajust.banners` | — | `ajax` está em `$openRoutes`: **aberta** |
| `set.status` | — | `set.status` -> precisa de `set.status` ou `set-status.index` |
| `generic.conteudo.delete` | — | renomeado explicitamente para `conteudo.delete` |

> `has_access()` (`v6/app/Library/Helper/appHelpers.php:22`) retorna `true` para
> `super-admin` e `false` para usuário não autenticado.

## 7. Ausência de API REST

Não há rota em `v6/routes/api.php` para o módulo. O portal é 100% server-rendered
e o CMS usa DataTable server-side.
