# Legislação — Views e UX

> **Slug:** `Legislacao` · Views no namespace `v2-legislacao::`
> (registrado por convenção a partir de `V2/Modules/Publicacoes/Legislacao/Views`).

---

## 1. Inventário de Blades

| Arquivo | Função | Estende/inclui |
|---|---|---|
| `Views/admin/index.blade.php` | Listagem do CMS com filtros, ordenação, ações e componente de importação. | `v2-cms::layouts.app`, `v2-cms::partials.*` |
| `Views/admin/create.blade.php` | Casca do formulário de criação. | `admin/form` |
| `Views/admin/edit.blade.php` | Casca do formulário de edição + painel de anexos. | `admin/form`, `admin/includes/attachments`, `attachments-modals` |
| `Views/admin/form.blade.php` | Campos da norma, bloco de novos anexos e JS do editor de linhas. | Componente `v2-service-contentricheditor::components.forms.content-rich-editor` |
| `Views/admin/categories.blade.php` | Tabela de categorias e modal criar/editar. | `v2-cms::layouts.app` |
| `Views/admin/includes/attachments.blade.php` | Lista de anexos existentes com download, edição e exclusão. | — |
| `Views/admin/includes/attachments-modals.blade.php` | Modais de upload e de edição de metadados + helpers JS. | — |
| `Views/admin/includes/new-file-row.blade.php` | Linha repetível de novo anexo (old input). | — |
| `Views/portal/index.blade.php` | Busca e listagem pública. | `v2-portal::app-layout` |
| `Views/portal/show.blade.php` | Detalhe público com conteúdo, informações e anexos. | `v2-portal::app-layout` |

## 2. Layouts

| Superfície | Layout | Observação |
|---|---|---|
| CMS | `v2-cms::layouts.app` | Sidebar/breadcrumb do CMS; injeta Bootstrap Icons e Bootstrap JS (modais). |
| Portal | `v2-portal::app-layout` | Layout público com seções `main-header-area` e `main-section-area`. |

O módulo não define layout próprio nem tema por dado. No V7, layout é dado
(ADR-016) e a view deve usar componentes DaisyUI, não Bootstrap.

## 3. Listagem do CMS (`admin/index.blade.php`)

- Monta `$columns` declarativamente; cada coluna pode ter `sortable`, `type` e
  `render` (closures que devolvem HTML).
- Status usa `v2-cms::partials.status-badge` (`:7-9`).
- Ações por linha:
  - link "ver no portal" quando há slug (`:13-18`), aberto em nova aba;
  - editar;
  - excluir via `data-v2-confirm*`, que o JS global transforma em modal de
    confirmação (`:22-34`).
- Filtro: busca textual, select de categoria preenchido com **todas** as
  categorias (inclusive inativas, pois usa `ordered()` sem `active()`), e o
  componente `filter-card` sem intervalo de datas.
- O componente `v2-service-excelimporter::components.excel-transpiler` é
  incluído com três rotas (template, preview, process) dentro de `@v2can`.
- A tabela delega paginação e estado vazio ao partial `v2-cms::partials.table`.

## 4. Formulário da norma (`admin/form.blade.php`)

Campos, na ordem:

| Campo | Controle | Regra de UI |
|---|---|---|
| `title` | texto, obrigatório | largura 8 colunas. |
| `v2_legislation_category_id` | select | opção vazia "Selecione"; atalho para gerenciar categorias. |
| `number` | texto | livre (preserva zeros). |
| `year` | number | sem `min/max` no HTML (validação no request). |
| `publication_date` | `datetime-local` | convertido para `Y-m-d\TH:i` na edição (`:31`). |
| `summary` | textarea 2 linhas | — |
| `content` | componente do editor rico | valor inicial `{"type":"doc","content":[]}`. |
| `file_url` | texto | link externo opcional, com aviso para preferir anexos. |
| `is_active` | hidden `0` + checkbox `1` | garante envio de falso. |
| `files[i][file|title|view_pdf]` | linhas repetíveis | JS cria/remove linhas; `accept` limita extensões visuais. |

### 4.1 JS embutido

`form.blade.php:71-113` injeta um `<script>` em `@push('scripts')` que:

1. Cria uma linha nova a partir de template string ao clicar em "Adicionar arquivo".
2. Remove a linha ao clicar na lixeira, por delegação de evento.
3. Usa o índice `container.children.length` como nome do campo.

É JavaScript **inline**, sem módulo, sem build e sem CSP-compatibilidade. No V7
isso deve virar módulo Vite (ADR-022 exige CSP sem inline).

## 5. Painel de anexos (`admin/includes/attachments*`)

- `attachments.blade.php` lista `$item->files()->orderBy('order')->get()`,
  exibindo nome, MIME, tamanho em KB, badge "Ver em PDF" quando aplicável e
  botões de abrir/baixar, editar e excluir.
- O ícone de arraste (`bi-grip-vertical`) é **decorativo**: não há implementação
  de drag-and-drop no módulo. A rota `files-reorder` existe, mas nenhuma view a
  chama.
- `attachments-modals.blade.php` contém:
  - modal de upload com `accept=".pdf,.zip,.rar,.doc,.docx,.odt,.xls,.xlsx"`;
  - modal de edição de metadados;
  - script inline que move os modais para `document.body` (contorna
    `content-visibility` do layout);
  - `toggleViewPdfByFile()` que mostra a opção apenas para `.pdf`;
  - `editLegislationFile(id, title, description, viewPdf, isPdf)` que preenche o form e abre o modal;
  - uso de `addslashes()` para embutir título/descrição em atributos `onclick`.
- `view_pdf` é uma intenção, não uma garantia: o portal só abre em nova aba
  quando `view_pdf && is_pdf`.

## 6. Categorias (`admin/categories.blade.php`)

- Tabela com nome, slug, descrição, contagem de itens (`items_count`), ordem e
  ações.
- Modal único alterna criar/editar por `data-mode`; o `action` do form é
  reescrito no evento `show.bs.modal` (`:175-193`).
- Exclusão usa `confirm()` nativo e submete um form oculto por id
  (`:82-91`, `:196-200`).
- O modal alerta que os itens ficarão sem categoria.

## 7. Portal — listagem (`portal/index.blade.php`)

- Breadcrumb com microdados Schema.org (`BreadcrumbList`).
- Título e subtítulo fixos.
- Formulário GET com quatro filtros: `q`, `category` (slug), `dt_inicial`,
  `dt_final`; botão Filtrar e botão Limpar.
- Contador `role="status" aria-live="polite"`.
- Cada item é um `list-group-item` com badge `número/ano`, badge de categoria,
  título, resumo truncado em 140 caracteres por `str_limit()` e data formatada.
- Paginação preserva a query string via `appends()` no controller.

Ponto de atenção: o filtro de categoria oferece apenas categorias ativas, mas a
consulta aceita qualquer slug — uma URL manual com categoria inativa ainda
filtra e mostra itens.

## 8. Portal — detalhe (`portal/show.blade.php`)

- Header: badge número/ano, título e data de publicação.
- Coluna principal: resumo em itálico e conteúdo renderizado com `{!! !!}`.
  O comentário da view afirma que o HTML é sanitizado na gravação; **não é**
  (ver `05-REGRAS-E-PORTABILIDADE.md`).
- Coluna lateral:
  - cartão "Informações" (categoria, número, publicação, status);
  - cartão "Anexos": PDF com `view_pdf` abre em nova aba; os demais usam a rota
    de download por id; descrição aparece abaixo do link;
  - botão "Baixar documento principal" quando `file_url` existe, apontando para
    `main_file_url`;
  - botão de retorno à listagem.

## 9. JS, Ajax e dependências de front

| Recurso | Onde | Como funciona |
|---|---|---|
| Bootstrap JS (modais) | layout do CMS | `data-bs-toggle`/`new bootstrap.Modal`. |
| `data-v2-confirm` | `admin/index.blade.php` | JS global do CMS converte atributos em modal de confirmação. |
| Importador | `excel-transpiler` | Componente do service ExcelImporter; faz upload, preview e processa stream NDJSON. |
| Editor rico | componente do ContentRichEditor | TipTap vendorizado; o campo oculto carrega o AST JSON. |
| Reordenação | — | Sem JS ativo; rota pronta, UI ausente. |

O módulo **não** faz polling, não usa jQuery e não há endpoints Ajax próprios
além do import (que é do service) e do `reorderFiles`.

## 10. Exportações

- **Única exportação:** o template XLSX da importação
  (`ImportExcelTemplateGenerator::download()`).
- Não há exportação da listagem em CSV/XLS/PDF, nem dados abertos. O portal V7
  deve ganhar exportação por ADR-018.

## 11. O que cada perfil vê

| Perfil | Visão |
|---|---|
| Cidadão | Lista publicada, detalhe, download de anexos publicados. Itens futuros/inativos não são listados nem acessíveis. |
| Operador com `read` | Listagem e categorias em modo leitura; sem botões de escrita (bloco `@v2can`). |
| Operador com `write` | Tudo acima + criar/editar/excluir, categorias, anexos e importação. |

## 12. Acessibilidade e UX — estado atual

- Breadcrumbs com `aria-label`, listas semânticas e `aria-live` no contador
  estão presentes.
- Botões de ícone usam `aria-label` na listagem e no painel de anexos.
- Problemas: `onclick` inline, `confirm()` nativo, `addslashes()` em atributo,
  ausência de foco gerenciado nos modais e conteúdo principal com `{!! !!}`.
- O formulário não exibe resumo de erros; cada campo mostra apenas
  `invalid-feedback` quando há erro (somente título tem esse bloco explícito).
- Não há estado de carregamento no upload avulso: a página recarrega.
