# Menus do Site — Gaps, Bugs, Riscos e Dividas

---

## 1. Bugs e erros conhecidos

| # | Severidade | Descricao | Evidencia |
|---|---|---|---|
| B1 | **Alta** | `POST /menu-item/{id}/toggle` e publico: nao exige login nem permissao. Qualquer visitante pode ativar/desativar itens de menu. **Herdado da v5** (rota em `v5/routes/web.php:198`) | `v6/routes/web.php:232`, dentro do grupo `Route::middleware(['check.site.manutention'])` (`v6/routes/web.php:7`) |
| B2 | **Alta** | `store`, `update` e `destroy` sao tratados como "rotas abertas" pelo middleware: qualquer usuario logado pode criar, alterar e excluir itens de menu, sem `menu.edit`/`menu.delete` | `v6/app/Http/Middleware/CheckRoutePermission.php:47-95` (`$resourceRoutes = ['edit' => 'update', 'create' => 'store', 'delete' => 'destroy']`) |
| B3 | **Alta** | `Menu::menuItems()` esta declarado como `belongsTo` — um menu tem varios itens | `v6/app/Http/Model/Prefecture/Menu.php:26-29` |
| B4 | **Alta** | `MenuItem` nao usa `SoftDeletes` (comentado), mas a tabela tem `deleted_at`. A exclusao e fisica e irreversivel | `v6/app/Http/Model/Prefecture/MenuItem.php:18` |
| B5 | **Alta** | Rotas `menu-item.index`, `menu-item.create` e `menu-item-by-menu` apontam para metodos inexistentes (`index`, `create`, `createByMenu`) -> erro 500. **Herdado da v5** (rotas em `v5/routes/basepro.php:157` e `:206`) | `v6/routes/basepro.php:171`, `:234`; `v6/app/Http/Controllers/Prefecture/MenuItemController.php` |
| B6 | **Media** | `Repository::upInsert` captura `\Throwable` e chama `dd($th)` — em producao, dump + die; o `catch (\Exception)` dos controllers nunca dispara | `v6/app/Repositories/Repository.php:61-63` |
| B7 | **Media** | `MenuController@store` e `@update` nao validam `name`; um POST vazio viola `menus.name NOT NULL` | `v6/app/Http/Controllers/Prefecture/MenuController.php:80`, `:137` |
| B8 | **Media** | O `Link::create` acontece dentro da transacao de `structureUpInsert`, mas o `updateOrCreate` do `menu_items` fica **fora** dela: se falhar, o `links` fica orfao | `v6/app/Repositories/Repository.php:52-64` + `v6/app/Repositories/Eloquent/Prefecture/MenuItem/MenuItemRepository.php:24-34` |
| B9 | **Media** | `MenuItemController@show` e `@edit` acessam `$menuItem` sem validar se o registro existe (`first()` pode devolver `null`) | `v6/app/Http/Controllers/Prefecture/MenuItemController.php:151` |
| B10 | **Media** | `MenuController@destroy` faz `Menu::find($id)` e chama `->delete()` sem checar `null` | `v6/app/Http/Controllers/Prefecture/MenuController.php:171-172` |
| B11 | **Media** | A renumeracao de `order` faz 1 `find` + 1 `save` por item, sem transacao: uma falha no meio deixa a ordem inconsistente | `v6/app/Http/Controllers/Prefecture/MenuItemController.php:66-76` |
| B12 | **Media** | `menuClicks('menu-items')` usa um slug **inexistente** em `menu_orders`: o contador de acessos do modulo de itens nunca e incremented | `v6/app/Http/Controllers/Prefecture/MenuItemController.php:38` vs. `v6/database/seeds/MenuOrderSeeder.php:187-193` |
| B13 | **Media** | `menu_items.order` e `NOT NULL` sem default no banco, mas `MenuItemRequest` remove `order` do request — um item novo sem `order` violaria a constraint. Na pratica o `order` nunca e enviado no `store` (o controller manda `order` no form, mas o request e filtrado) | `v6/app/Http/Requests/MenuItemRequest.php:32` |
| B14 | **Baixa** | O tooltip de destino no CMS reimplementa o calculo de URL em Blade e diverge do presenter (ex.: `url('pagina/' . $slug)` vs `url('acesso-rapido/' . $slug)`) | `v6/resources/views/prefecture/menus-itens/show.blade.php:203-212` vs `v6/app/Http/Presenters/MenuItemPresenter.php:12-60` |
| B15 | **Baixa** | `use App\Http\Presenters\SectionPresenter;` — a classe nao existe no repositorio | `v6/app/Http/Model/Prefecture/MenuItem.php:5` |
| B16 | **Baixa** | `menus-itens/form.blade.php` usa `$menuItem->link`, coluna que nao existe em `menu_items` na v6 | `v6/resources/views/prefecture/menus-itens/form.blade.php` |
| B17 | **Baixa** | `prefecture/menus/create.blade.php` abre o form com `route => ['menu.index']` (destino errado) | `v6/resources/views/prefecture/menus/create.blade.php:12` |
| B18 | **Baixa** | `MenuController@create` nunca renderiza o formulario: faz `redirect('/cms/menu')` | `v6/app/Http/Controllers/Prefecture/MenuController.php:63-77` |
| B19 | **Baixa** | `MenuItem::isFather()` tem nome invertido (retorna `true` para item **raiz**) | `v6/app/Http/Model/Prefecture/MenuItem.php:99`, `:110` |
| B20 | **Baixa** | As relacoes `menus()`, `links()`, `staticPages()`, `categoryContent()`, `content()` do `MenuItem` invertem as chaves (PK no lugar da FK e vice-versa) | `v6/app/Http/Model/Prefecture/MenuItem.php:43-79` |
| B21 | **Baixa** | `Link::menuItems()` e `Link::menuItem()` usam `belongsToMany` com uma unica chave — consulta invalida se usadas | `v6/app/Http/Model/Prefecture/Link.php:35`, `:62` |
| B22 | **Baixa** | `MenuController@index` carrega `Menu::all()` sem usar a variavel na view | `v6/app/Http/Controllers/Prefecture/MenuController.php:55` |
| B23 | **Baixa** | `MenuItemController@show` recebe `{menu_item}` na rota mas o metodo espera o **id do menu** | `v6/routes/basepro.php:171` vs `v6/app/Http/Controllers/Prefecture/MenuItemController.php:122` |

---

## 2. Funcionalidades incompletas ou mortas

| Item | Situacao |
|---|---|
| `v6/resources/views/prefecture/menus-itens/index.blade.php` | Orfa: nenhuma acao a alcanca; usa a variavel inexistente `$menusItens` |
| `v6/resources/views/prefecture/menus-itens/create.blade.php` | Orfa: nao ha `create()` no controller |
| `v6/resources/views/prefecture/menus/show.blade.php` | Orfa: `MenuController@show` tem corpo vazio |
| `v6/resources/views/prefecture/menus/create.blade.php` | Orfa (o controller redireciona) |
| Rota `menu-item-by-menu` | Orfa e quebrada (metodo inexistente) |
| `getQtdSubItemsByItems` | O endpoint retorna a quantidade, mas o JS so habilita/desabilita o botao — o valor **nao e usado** |
| `MenuItemRequest` — regra dos 6 itens | So se aplica na rota `menu-item.store`; na edicao, um item pode ficar sem pai mesmo com 6+ raizes |
| `getShortLink()` do presenter | Removeu (na v6) o caso `conselhos` que existe em `getLink()` — os dois metodos divergem |
| `status_create` de `menus` | Tem uso real (ordem da view `menus_site`), mas nao e editavel pela interface |
| `second_name` de `menu_items` | Exibido/edtado no formulario, mas **nao e usado** em nenhuma view do portal |
| `icon` de `menu_items` | Editavel no CMS e exibido no CMS; no portal, so o item mega menu usa icone fixo (`fa-th-large`). O icone do item nao e renderizado no menu principal |
| `iconpicker-custom` | Carregado no `menus-itens/show.blade.php` e no `form.blade.php`, mas o icone **nao aparece** no portal |
| `MenuOrder.clicks` | Incrementado apenas para o slug `menu`; os demais modulos dependem de `menuClicks` em seus proprios controllers |
| Filtro de `is_active` no `MenuServiceProvider` | Aplicado ao `menuRapido` (menu 3) e ao `menuHeader` (menu 1) **no Blade**; o rodape (`menus_id = 2`) **nao** filtra por `is_active` — um item desativado no rodape continua aparecendo |
| `MenuServiceProvider::boot()` | Retorna cedo se `runningInConsole()` — os *view composers* **nao sao registrados** em ambiente de console (CLI, testes, queue). Isso exige cuidado ao escrever testes de portal |

---

## 3. Itens que existem na v5 e foram removidos na v6

| Item v5 | Situacao na v6 | Impacto |
|---|---|---|
| `v5/database/seeds/AddMenuTagsToProductionSeeder.php` | **Nao existe na v6** | O menu de slug `tags` (id 5) nao e criado; a v6 nao tem essa linha em `menus` |
| `v5/database/seeds/AddMenuTagsWidgetSeeder.php` | **Nao existe na v6** | O widget `menu-tags-widget` nao e registrado em `layouts` |
| `v5/resources/views/portal/layouts/modelo5/layout/menus/menu_tags.blade.php` | **Nao existe na v6** | O widget de tags de menu nao tem renderizador no portal |
| `v5/database/seeds/AddToLayoutsNewTagWithMenu.php` | Substituido por `v6/database/seeds/AddToLayoutsNewTag.php` | Tags do editor de layout foram redefinidas |
| `v5/database/seeds/MenuAcessoRapidoSeeder.php` | **Nao existe na v6** | Coberto por `MenuItemsTableSeeder.php` |
| `v5/database/seeds/MenuOrderTableSeeder.php` | Renomeado para `MenuOrderSeeder.php` | — |
| `v5/database/seeds/OmbudsmanMenuOrderSeeder.php` | **Nao existe na v6** | A linha de `menu_orders` da ouvidoria vem de outra origem |
| `v5/database/seeds/AddItemsToMenuOrderTable.php` | **Nao existe na v6** | — |
| `v5/database/migrations/2023_02_13_141452_create_item_menu_order.php` (insere `menu_orders.slug = 'certificado'`) | **Nao existe na v6** | A linha existe no banco (`menu_orders.id = 26`), mas nao ha migration que a crie na v6 |
| `v5/database/migrations/2023_06_29_143227_add_itens_to_menu.php` (insere `revisoes-cliente`) | **Nao existe na v6** | — |
| `v5/database/migrations/2025_06_02_114951_add_is_active_to_menus_table.php` | Renomeada para `v6/database/migrations/2025_06_04_160404_add_is_active_to_menus_table.php` (conteudo identico) | — |
| `v5/database/migrations/2026_05_26_113000_insert_terceirizados_into_menu_and_resources.php` | **Nao existe na v6** | Verificar se esses recursos devem existir |
| `v5/database/migrations/2026_05_26_200002_insert_estagiario_menu_and_resource.php` | **Nao existe na v6** | idem |
| `v5/database/migrations/2026_05_27_000002_insert_concurso_pss_menu_and_resource.php` | **Nao existe na v6** | idem |

---

## 4. Itens novos na v6

| Item | Descricao |
|---|---|
| `v6/database/migrations/2024_11_04_094157_add_modulo_id_to_menu_order_table.php` | `menu_orders.modulo_id` -> `modulos.id` |
| `v6/database/migrations/2026_09_01_100000_fix_menu_orders_missing_modulo_id.php` | Preenche `modulo_id` nulo por correspondencia de `slug` (idempotente, nao reversivel) |
| `v6/database/migrations/2026_06_02_120000_add_is_mega_menu_to_menu_items_table.php` | `menu_items.is_mega_menu` |
| `v6/database/seeds/UpdateModuloSelectOptions.php` | Bloco `#modulos-select` no HTML da chave `tags` de `layouts`, com opcao "Menus do Site" |
| `v6/public/assets/js/custom/modelos_personalizados/module-selector.js` | Seletor de modulo + sub-select (menu) no editor de layout |
| `POST /menu-item/{id}/toggle` + coluna `is_active` | Ativacao/desativacao individual de item |
| Componente `v6/resources/views/portal/components/mega-menu-col.blade.php` | Colunas do mega menu |
| `MenuLinkResolver` equivalente no presenter v6 | `is_mega_menu` no `MenuItemPresenter` nao existe — o mega menu e decidido no Blade |
| `layouts.secretarias-grid` com formato `modulo:subId` | Escolha de menu para a grade da home do modelo5 |

---

## 5. Mapa v5 -> v6 (detalhado)

### 5.1 Modelos

| Aspecto | v5 | v6 |
|---|---|---|
| `Menu` | `v5/app/Http/Model/Prefecture/Menu.php` | **Identico** (diff vazio) — `SoftDeletes`, `$fillable = name, slug, status_create`, `menuItems()` como `belongsTo` (bug presente nas duas) |
| `MenuItem::$fillable` | inclui `link` | inclui `is_mega_menu` (sem `link`) |
| `MenuItem::isFather()` | `is_null($this->attributes['menu_items_id'])` (coluna inexistente — erro em runtime) | `is_null($this->attributes['menus_items_id'] ?? null)` (corrigido) |
| `MenuItem::getIsFatherAttribute()` | idem do bug acima | idem, corrigido |
| `MenuItem` demais metodos | — | Sem diferencas |
| `MenuOrder` | (nao comparado; mesmo modelo) | — |

### 5.2 Controllers

| Aspecto | v5 | v6 |
|---|---|---|
| `MenuController` | `v5/app/Http/Controllers/Prefecture/MenuController.php` | **Identico** (diff vazio) |
| `MenuItemController@store` / `@update` | sem `is_mega_menu` | injeta `is_mega_menu` (1/0) |
| `MenuItemController@edit` | logica de categoria com `else` aninhado; acessava `CategoryContent::find()->category_contents_id` sem checar `null`; variaveis `$typeLink/$typeStatic/$typeContent` inicializadas duas vezes | guarda `if ($categorySelectedObj)` + `switch ($menuItem->type)` explicito com `default` |
| `MenuItemController` (demais metodos) | — | Sem diferencas |

### 5.3 Presenters

| Caso | v5 | v6 |
|---|---|---|
| Pagina estatica | `url('acessos-rapido/' . $slug)` | `url('acesso-rapido/' . $slug)` |
| `static_pages_id = 4` | `url($slug)` | `url($slug)` (igual) |
| Categorias 3 e 7 | `url('a-prefeitura/' . $slug)` | `url('a-instituicao/' . $slug)` |
| `getLink` com `conselhos` | `url('portal/' . pai . '/' . slug)` | mantido, **sem** o `$content_slug` |
| `getShortLink` com `conselhos` | `pai . '/' . slug . $content_slug` | **removido** (cai no `else`: `'/' . $slug . $content_slug`) |
| `type = 2` com `contents_id` vazio | checava `!is_null($this->contents_id) && $this->contents_id !== ''` | usa apenas `isset($this->content->slug) && !empty($this->content->slug)` |

### 5.4 Views

| Arquivo | v5 | v6 |
|---|---|---|
| `prefecture/menus/{index,create,edit,form,show}.blade.php` | — | **Identicas** (diff de diretorio vazio) |
| `prefecture/menus-itens/index.blade.php` | versao anterior | versao anterior (orfa nas duas) |
| `prefecture/menus-itens/{create,edit}.blade.php` | — | **Identicas** (nao aparecem no `diff -rq`) |
| `prefecture/menus-itens/form.blade.php` | sem `is_mega_menu`; usa `optional()` para blindar `links`/`staticPages`/`categoryContent`/`content` | com `is_mega_menu` (checkbox) e `select2` no select de pai; **removeu os `optional()`** |
| `prefecture/menus-itens/show.blade.php` | ja tinha o interruptor de `is_active` e o `route('menu-item.toggle')`; usava `optional()` | badge "Mega Menu", layout com `d-flex`, e **removeu os `optional()`** |
| `portal/layouts/modelo5/layout/menus/menu_tags.blade.php` | **existe** | **nao existe** |

> **Regressao da v6:** a remocao dos `optional()` em
> `v6/resources/views/prefecture/menus-itens/show.blade.php` e
> `v6/resources/views/prefecture/menus-itens/form.blade.php` torna a tela
> vulneravel a `null` em `links`, `staticPages`, `categoryContent` e `content` —
> um item cujo destino foi removido quebra o Blade com erro 500. A v5 era
> tolerante a esse cenario.

### 5.5 Migrations

| Migration | v5 | v6 |
|---|---|---|
| `2018_04_09_194517_create_menus_table.php` | identica | identica |
| `2018_04_09_194931_create_menu_items_table.php` | identica | identica |
| `2021_02_05_170740_add_icon_to_menu_items_table.php` | identica | identica |
| `2022_04_08_101047_create_menu_order_table.php` | identica | identica |
| `2023_04_13_151009_create_view_to_menus.php` | identica | identica |
| `2024_04_02_000718_add_questionnaires_to_menu_order_and_resource_tables.php` | identica | identica |
| `2024_04_11_163017_add_filled_questionnaires_to_menu_order_and_resource_tables.php` | identica | identica |
| `2024_11_04_094157_add_modulo_id_to_menu_order_table.php` | **nao existe** | nova |
| `2025_06_04_160404_add_is_active_to_menus_table.php` | `2025_06_02_114951_add_is_active_to_menus_table.php` | renomeada (conteudo identico) |
| `2026_06_02_120000_add_is_mega_menu_to_menu_items_table.php` | **nao existe** | nova |
| `2026_09_01_100000_fix_menu_orders_missing_modulo_id.php` | **nao existe** | nova |

### 5.6 Seeds

| Seed | v5 | v6 |
|---|---|---|
| `MenusTableSeeder` | existe | existe |
| `MenuItemsTableSeeder` | existe | existe |
| `MenuOrderTableSeeder` / `MenuOrderSeeder` | primeiro | segundo (com `modulo_id`) |
| `AddLinkColumnMenuItemsTableSeeder` | existe | existe |
| `AlterColumnTableMenuItemsSeeder` | existe | existe |
| `UpdateMenuItemsSeeder` | existe | existe |
| `MenuLinkContatoSeeder` | **nao existe** | nova |
| `AddMenuTagsToProductionSeeder` | **existe** | **nao existe** |
| `AddMenuTagsWidgetSeeder` | **existe** | **nao existe** |
| `UpdateModuloSelectOptions` | **nao existe** | nova |
| `AddToLayoutsNewTag` | **nao existe** (existe `AddToLayoutsNewTagWithMenu`) | nova |
| `AddToLayoutsNewTagWithMenu` | **existe** | **nao existe** |

---

## 6. Riscos de dados (migracao de schema para a v7)

| # | Risco | Impacto | Mitigacao |
|---|---|---|---|
| D1 | `menu_items` tem `deleted_at` mas nunca e preenchido (sem `SoftDeletes`) | Migrar com `SoftDeletes` na v7 **nao** apaga nada (todos os `deleted_at` sao `NULL`); mas novos deletes passam a ser soft | Habilitar `SoftDeletes` e documentar |
| D2 | `menus.slug` nao tem unicidade | Dois menus com o mesmo `slug` quebram qualquer logica por slug (ex.: `tags`, `abas`) | Adicionar indice unico apos auditar duplicidades |
| D3 | `menus.slug` e reescrito a cada `update` | Referencias externas (URLs, `layouts.secretarias-grid` usa **id**, ok) podem quebrar | Tornar `slug` imutavel ou versionado |
| D4 | `menu_items.order` pode ter valores duplicados ou gaps | A ordenacao do portal fica imprevisivel | Normalizar por menu na migracao (1..N) |
| D5 | `menu_items.menus_id` aponta para menus deletados (orfaos) | Itens orfaos nao aparecem (a listagem usa `menus_site`, que filtra `deleted_at`) mas continuam no banco | Limpeza orfaos ou `restore` do menu |
| D6 | `links` orfaos criados pelo `MenuItemRepository` quando o `updateOrCreate` falha | Links sem item correspondente | Limpeza + `ON DELETE SET NULL` |
| D7 | `menu_orders.modulo_id` pode ser `NULL` (foi corrigido pela migration de 2026-09-01, mas novas installs Dependsem da migration) | O item do modulo nao aparece no menu lateral | Garantir a migration no fluxo de instalacao |
| D8 | `menu_items.links_id` pode apontar para `links` ja removidos | `presenter->getLink()` acessa `$this->links->link` e pode falhar | `nullsafe` + validacao no `MenuLinkResolver` |
| D9 | `menu_items.category_contents_id` / `contents_id` / `static_pages_id` apontando para registros removidos (as FKs permitem `null`, mas o codigo de exclusao em `CategoryContentController`/`ContentController`/`StaticPageController` trata isso) | Item sem destino no portal | Testes de integracao (I2-I4) |
| D10 | A view `menus_site` depende de `row_number()` (PostgreSQL) | Impede migrar para MySQL sem reescrever | Recriar a view ou eliminar |

---

## 7. Perguntas em aberto para o negocio

1. **O menu "Tags" deve existir na v7?** Na v5 ele era um menu especial (slug `tags`) consumido por um widget do editor de layout; na v6 foi substituido pelo seletor de modulo (`modulos-select` com a opcao "Menus do Site"). A pergunta equivalente para a v7 e: **o portal deve permitir escolher qualquer menu para virar a grade "Servicos" da home?** (a v6 responde sim, via `layouts.secretarias-grid`).
2. **O limite de 6 itens raiz por menu e uma regra de negocio ou um limite tecnico do layout?** Se for negocio, deve ser configuravel por menu.
3. **O icone do item de menu deve aparecer no portal?** Hoje o campo `icon` e editado no CMS mas nao e renderizado no menu principal (so o mega menu usa um icone fixo).
4. **O menu "Atalhos" deve ter o interruptor de ativacao?** Hoje ele e bloqueado por nome (`@if ($menu->name !== 'Atalhos')`), o que impede desativar um atalho especifico.
5. **A ordem do rodape deve respeitar `is_active`?** Hoje nao respeita.
6. **`clicks` (contador de acessos ao modulo) ainda tem utilidade?** O `MenuServiceProvider` ordena o menu lateral por `clicks` quando `menu_interativo` esta ativo — a ordenacao depende de um contador que so e incrementado para o slug `menu`.
7. **O grupo "Menu" da v5 (`AddMenuTagsWidgetSeeder`) deve ser recuperado?** O widget renderiza os itens do menu `tags` como etiquetas — util para o layout "A Instituicao".
8. **A profundidade de 3 niveis (raiz + 2) deve ser configuravel?** O portal hoje ignora o 4o nivel silenciosamente.

---

## 8. Resumo de dividas por severidade

| Severidade | Quantidade | Itens |
|---|---|---|
| Alta | 5 | B1, B2, B3, B4, B5 |
| Media | 8 | B6, B7, B8, B9, B10, B11, B12, B13 |
| Baixa | 10 | B14 a B23 |
| Funcionalidade morta | 5 | 4 views orfas + 1 rota quebrada |
| Ausente na v6 | 13 | seeds, view e migrations listados em §3 |
| Risco de dado | 10 | D1 a D10 |
