# Menus do Site — Views, UX e Front-end

> Views do CMS: `v6/resources/views/prefecture/menus/**` e
> `v6/resources/views/prefecture/menus-itens/**`.
> Views do portal: `v6/resources/views/portal/layouts/modelo{1..6}/**`.

---

## 1. Telas do CMS

### 1.1 Lista de menus — `/cms/menu`

| Arquivo | Linhas | Função |
|---|---|---|
| `v6/resources/views/prefecture/menus/index.blade.php` | 38 | Casca da tela: `box box-success` com o `<table id="tabela_menus">` (colunas ID, Nome, Slug, Ações) e carrega o JS do DataTable |
| `v6/public/assets/js/custom/contents/menus/datatable.js` | 33 | Define as colunas do DataTable e os botões por linha |

O `tbody` da tabela fica **vazio** no Blade: quem preenche e o DataTable, via
`GET /cms/datatable/menus` (view SQL `menus_site`).

Colunas e acoes do JS (`v6/public/assets/js/custom/contents/menus/datatable.js`):

```js
{
    data: null,
    name: "actions",
    orderable: false,
    render: function (data) {
        return `<div class='flex justify-center vertical-middle'>`
            + createBTN(data.id, `/cms/menu/${data.id}/edit`, data.edit, false)
            + createSingleButton(data.id, `/cms/menu-item/${data.id}`, data.edit, 'glyphicon glyphicon-tasks', 'Ajustar itens')
            + `</div>`;
    }
}
```

- "Editar" -> `/cms/menu/{id}/edit` (visivel conforme a flag `edit`, calculada por `has_access('menu.edit')` no `struct_views`);
- "Ajustar itens" -> `/cms/menu-item/{id}` (o `{id}` e o **id do menu**, usado como parametro de `show`).

Exportacoes: o `runDatatables` (`v6/public/assets/js/custom/utilities/table.js:3`)
adiciona os botoes **Excel, PDF, CSV e TXT** (DataTables Buttons, client-side),
com o titulo `Prefeitura de <cidade> - <data>`.

### 1.2 Formulario de menu

| Arquivo | Linhas | Função |
|---|---|---|
| `v6/resources/views/prefecture/menus/form.blade.php` | 15 | Unico campo: `name` (via `components.inputs.input`), com `old('name')` |
| `v6/resources/views/prefecture/menus/create.blade.php` | 45 | Wrapper de cadastro (nao usado: `MenuController@create` redireciona) |
| `v6/resources/views/prefecture/menus/edit.blade.php` | 33 | Wrapper de edicao (`Form::model($menu, route menu.update, method PUT`) |
| `v6/resources/views/prefecture/menus/show.blade.php` | 24 | Painel de detalhe (orfa: `MenuController@show` nao renderiza) |

O `slug` **nao aparece no formulario**: e gerado no servidor
(`str_slug($request['name'])`, `v6/app/Repositories/Eloquent/Prefecture/Menu/MenuRepository.php:15-19`).
`status_create` tambem nao e editavel.

Problema: `create.blade.php` abre o form com `['route' => ['menu.index']]` — o
formulaire de criacao aponta para a listagem, nao para `menu.store`.

### 1.3 Tela de itens — `/cms/menu-item/{idDoMenu}`

| Arquivo | Linhas | Função |
|---|---|---|
| `v6/resources/views/prefecture/menus-itens/show.blade.php` | 604 | **Tela principal do modulo**: formulario de novo item (coluna esquerda) + arvore arrastavel (coluna direita) |
| `v6/resources/views/prefecture/menus-itens/form.blade.php` | 328 | Formulario de edicao de um item (reconstroi categoria/subcategoria/conteudo) |
| `v6/resources/views/prefecture/menus-itens/edit.blade.php` | 32 | Wrapper do formulario de edicao |
| `v6/resources/views/prefecture/menus-itens/create.blade.php` | 30 | Wrapper de criacao (orfa: nao ha `create` no controller) |
| `v6/resources/views/prefecture/menus-itens/index.blade.php` | 77 | Orfa (ver §5) |

#### Coluna esquerda — formulario de novo item

Campos: `name`, `second_name` ("Sobrenome"), `icon` (com *iconpicker* de ionicons),
radio de tipo (`1` Pagina Estatica / `2` Conteudo (Categorias) / `0` Personalizado),
select de `menus_items_id` (Item Pai), checkbox `isBlank` ("Abrir em nova guia"),
campo hidden `menus_id` e campo hidden `order` (com `MenuItem::max('order') + 1`).

Os blocos `link`, `static_page` e `link_content` sao preenchidos via JS quando o
radio muda (`#link-custom`, `#link-static`, `#link-content`).

#### Coluna direita — arvore arrastavel

A arvore e materializada com **tres `foreach` aninhados** no Blade
(`show.blade.php:186-360`), um por nivel:

```blade
@php
    $subItems1 = $subItems
        ->with('links', 'staticPages')
        ->where('menus_items_id', $itemMenu->id)
        ->orderBy('order')
        ->get();
@endphp
```

Cada nivel repete a mesma consulta: e um **N+1** (1 consulta por item raiz, 1 por
item de 2o nivel, 1 por item de 3o nivel). O Blade ainda injeta o model
direto (`@inject('subItems', 'App\Prefecture\MenuItem')`) e usa
`@php` dentro do template — modelo anti-pattern a eliminar na v7.

Acoes disponiveis por item:

| Acao | Como funciona |
|---|---|
| Arrastar | jQuery UI Sortable na classe `.sortable`; ao soltar, envia **todos** os `.item-list` (todos os niveis juntos) para `POST /cms/ajust-items-lvl-1` |
| Ativar/desativar | `<input type="checkbox" class="toggle-checkbox" data-url="{{ route('menu-item.toggle', id) }}">` + `$.ajax` POST |
| Editar | link para `menu-item.edit` |
| Excluir | `Form::open(route('menu-item.destroy'))` com confirmacao via `bootstrap-confirmation` |
| Ver destino | *tooltip* com a URL calculada em Blade (replica a logica do presenter, mas **nao identica** — ver §4) |

O item raiz mostra o badge **"Mega Menu"** quando `is_mega_menu` e verdadeiro
(`show.blade.php:196-199`). O toggle de ativacao **nao aparece** para o menu
"Atalhos" (`@if ($menu->name !== 'Atalhos')`).

### 1.4 Edicao de item — `/cms/menu-item/{id}/edit`

`menus-itens/edit.blade.php` + `menus-itens/form.blade.php`. O controller
`edit()` (`v6/app/Http/Controllers/Prefecture/MenuItemController.php:149-241`)
reconstroi o estado do formulario:

- `typeLink`, `typeStatic`, `typeContent`, `typeCat`, `typeSubCat`;
- `categorySelected`, `subcategory`, `subcategories`, `contents`, `contentSelected`;
- `allItemsMenu` (itens do mesmo menu, exceto o editado) para o select "Item Pais";
- `menus` (lista de todos os menus) — exibido **desabilitado** no form.

O `form.blade.php` usa `$menuItem->link`, coluna que **nao existe** na tabela
`menu_items` da v6 (resultado: valor vazio).

---

## 2. Consumo no portal (o que o cidadao ve)

### 2.1 Menu principal

| Layout | Arquivo |
|---|---|
| modelo1 | `v6/resources/views/portal/layouts/modelo1/layout/nav.blade.php:115-171` |
| modelo2 | `v6/resources/views/portal/layouts/modelo2/layout/nav.blade.php:104` |
| modelo3 | `v6/resources/views/portal/layouts/modelo3/layout/nav.blade.php:142` |
| modelo4 | `v6/resources/views/portal/layouts/modelo4/layout/nav.blade.php:120` |
| modelo5 | `v6/resources/views/portal/layouts/modelo5/layout/header/nav-header/menu-principal.blade.php:36` |
| modelo6 | `v6/resources/views/portal/layouts/modelo6/layout/header/nav.blade.php:89` |

Padrao comum (modelo1, linhas 115-124):

```blade
@foreach ($menuHeader->where('is_active', true) as $menuHead)
    @php
        $active = $menuHead->subItens->where('is_active', true);
        $isMega = (bool) $menuHead->is_mega_menu;
    @endphp
    <li class="nav-item {{ $active->count() > 0 ? 'dropdown' : '' }} {{ $isMega ? 'mega-menu-item' : '' }}">
        <a href="{{ $isMega ? 'javascript:;' : $menuHead->present()->getLink }}"
```

Regras de exibicao:

- Itens com `is_active = false` somem do menu (filtro no `where`).
- Itens **filhos** inativos somem, mas o **pai** continua aparecendo (o filtro e
  aplicado a cada nivel).
- Um item raiz sem filhos ativos fica sem classe `dropdown` (sem submenu).
- O submenu tem no maximo **2 subniveis** (nivel 1 do `menuHeader` + 2 niveis de
  `subItens`).
- Item com `is_mega_menu`:
  - o `href` vira `javascript:;`;
  - ganha `data-toggle="dropdown" data-display="static"`;
  - recebe um icone `fa-th-large`;
  - o submenu e um `div.mega-dropdown-menu > div.mega-menu-flex`, com uma coluna
    por subitem, renderizada por
    `v6/resources/views/portal/components/mega-menu-col.blade.php`.

### 2.2 Acesso rapido

`$menuRapido` (menu 3, apenas ativos). Renderizado em:

- `v6/resources/views/portal/layouts/modelo1/layout/header.blade.php:77`
- `v6/resources/views/portal/layouts/modelo2/layout/header.blade.php:116`
- `v6/resources/views/portal/layouts/modelo3/home/acesso-rapido.blade.php:116`
- `v6/resources/views/portal/layouts/modelo4/home/acesso-rapido.blade.php:121`
- `v6/resources/views/portal/layouts/modelo6/layout/header/acesso-rapido.blade.php:11`

### 2.3 Atalhos do topo

`$atalhos` (menu 4) e usado **por nome**, nao por posicao:

```blade
href="{{ $atalhos->where('name', 'Telefones')->first()->links->link ?? '/telefones-uteis' }}"
```

Nomes usados: `Telefones`, `Ouvidoria`, `Contato`, `Acessibilidade`
(`v6/resources/views/portal/layouts/modelo1/layout/header.blade.php:47-56`).
Se o item "Telefones" nao existir no menu 4, cai no fallback `/telefones-uteis`.

> Risco: os nomes sao **magic strings**. Renomear o item no CMS quebra o atalho
> silenciosamente.

### 2.4 Rodape

`$menus` injetado por `v6/app/Providers/FooterServiceProvider.php:64-80`:
`$menuUp` (menu 1, itens sem `category_contents_id` ou com `category_contents_id = 2`)
e `$menuDown` (menu 2, raiz). Consumido por
`v6/resources/views/portal/layouts/modelo5/layout/footer/menu-footer.blade.php`
e equivalentes dos outros modelos.

### 2.5 Grade "Servicos" do modelo5 (ver `09-TAGS-DE-MENU.md`)

`v6/resources/views/portal/layouts/modelo5/home/transparencia/transparencia-fiscal-grid.blade.php:34-49`
— quando o editor de layout escolheu o modulo `menus`, a home do modelo5 renderiza
os **itens raiz do menu escolhido** como uma grade de cards (com `present()->getLink`
e `present()->targetLink`).

---

## 3. JavaScript e Ajax

| Endpoint | Metodo | Chamado de | Funcao |
|---|---|---|---|
| `/cms/datatable/menus` | GET | `v6/public/assets/js/custom/contents/menus/datatable.js` | Lista os menus |
| `/cms/ajust-items-lvl-1` | POST | inline em `menus-itens/show.blade.php` (`route('ajust.items.lvl.1')`) | Persiste a nova ordem |
| `/cms/get/subcategory/by/category` | POST | inline em `menus-itens/show.blade.php` (`route('ajax.get.subcategory.by.category')`) | Devolve subcategorias **ou** conteudos da categoria escolhida (rota em `v6/routes/basepro.php:136`, handler `Prefecture\ContentController@getSubCategory` em `v6/app/Http/Controllers/Prefecture/ContentController.php:1411-1418`) |
| `/cms/get/qtd-subitems/by/item` | POST | inline em `menus-itens/show.blade.php` (`route('ajax.get.qtd.sub.items')`) | Quantidade de filhos de um item (feedback, sem efeito funcional) |
| `/menu-item/{id}/toggle` | POST | inline em `menus-itens/show.blade.php` (`.toggle-checkbox`) | Ativa/desativa o item |

Detalhes:

- O *sortable* envia **todos** os ids da arvore (niveis 1, 2 e 3 misturados) em
  `sortable_data`; o controller renumera `order` de 1 em 1 **sem considerar o
  nivel** — a ordem e global por menu, nao por submenu.
- A renumeracao e feita com um `save()` por item (N escritas, sem transacao).
- O `toggle` envia `is_active` no corpo, mas o controller **ignora** o valor
  enviado e apenas inverte o estado atual.
- O `toggle` reverte o checkbox em caso de erro via JS (`checkbox.prop('checked', !checkbox.prop('checked'))`).
- Nao ha *polling* neste modulo.

---

## 4. Divergencia entre o tooltip do CMS e o presenter

O tooltip do destino no CMS (`menus-itens/show.blade.php:203-212`) reimplementa a
URL **em Blade**, e nao usa o presenter:

```blade
data-title="Link: @if ($itemMenu->type == 0) {{ $itemMenu->links->link }}
@elseif($itemMenu->type == 1) {{ url('pagina/' . $itemMenu->staticPages->slug) }}
@elseif($itemMenu->type == 2) {{ url('portal/' . $itemMenu->categoryContent->slug) }}{{ $itemMenu->content->slug or '' }}"
```

Diferencas em relacao ao `MenuItemPresenter::getLink()`:

| Caso | Presenter (portal) | Tooltip (CMS) |
|---|---|---|
| `type = 1` | `url($slug)` (se `static_pages_id = 4`) ou `url('acesso-rapido/'.$slug)` | `url('pagina/'.$slug)` |
| `type = 2` com categoria pai | `url('portal/'.$slugPai.'/'.$slug)` | `url('portal/'.$slugPai.'/'.$slug)` (parecido) |
| `type = 2` sem categoria | `url('/'.$slug)` | `url('portal/'.$slug)` |

Ou seja: **o link exibido no CMS pode nao ser o link efetivo no portal**.

---

## 5. Views orfas e nao referenciadas

| Arquivo | Situacao |
|---|---|
| `v6/resources/views/prefecture/menus-itens/index.blade.php` | Nao ha `index()` em `MenuItemController`; a rota `menu-item.index` geraria 500. A view usa `$menusItens`, variavel inexistente |
| `v6/resources/views/prefecture/menus-itens/create.blade.php` | Nao ha `create()` no controller |
| `v6/resources/views/prefecture/menus/show.blade.php` | `MenuController@show` tem corpo vazio |
| `v6/resources/views/prefecture/menus/create.blade.php` | `MenuController@create` redireciona para `/cms/menu` |
| `v5/resources/views/portal/layouts/modelo5/layout/menus/menu_tags.blade.php` | **Somente na v5** — nao existe na v6 (ver `09-TAGS-DE-MENU.md`) |

---

## 6. Layouts do portal usados

O layout ativo vem da chave `layout` da tabela `configurations`
(`getTemplateActived()` em `v6/app/Http/Traits/GetInformationsPrefectureTrait.php:92`
e `v6/app/Http/Controllers/Admin/Controller.php:39`). As views do portal sao
`portal.layouts.{layout}.*`; existem `modelo1`..`modelo6` e `default`.

Os *view composers* do modulo cobrem `modelo1`..`modelo6`; o layout `default`
**nao** recebe `$menuHeader`, `$menuRapido` nem `$atalhos` — quem usa `default`
nao tem menu injetado.

---

## 7. Exportacoes

- O DataTable de menus exporta **Excel, PDF, CSV e TXT** pelo cliente
  (`v6/public/assets/js/custom/utilities/table.js:28-165`). Nao ha exportacao
  server-side neste modulo.
- O conteudo exportado vem da view `menus_site`: `value`, `id`, `name`, `slug`,
  `edit`, `delete`, `active`, `inactivate`.

---

## 8. Acessibilidade e UX (o que o operador ve)

- O *toggle* de ativacao usa `<label class="toggle">` + checkbox com `data-url`;
  o `id` do item fica em `data-id` (nao usado pelo JS).
- A exclusao usa `bootstrap-confirmation` com `btnOkLabel: 'Sim'` e
  `btnCancelLabel: 'Nao'`.
- O formulario mostra erros em uma lista vermelha no topo do `box-body`.
- O icone e escolhido por *iconpicker* de ionicons 2.0.0
  (`v6/public/assets/plugins/iconpicker-custom/js/iconpicker-custom.js`, CSS em
  `v6/public/assets/plugins/iconpicker-custom/css/ionicons2.0.0.css`).
- Nao ha confirmacao ao **desativar** um item (apenas o interruptor), o que
  pode tirar itens do portal sem que o operador perceba o efeito imediato.
