# Telefones Úteis — Regras de Negócio e Portabilidade

## 1. Regras de negócio observáveis

| # | Regra | Onde está |
|---|---|---|
| R1 | Um telefone é um `contents` cuja categoria é `telefones-uteis`; não existe entidade própria | `v6/app/Http/Traits/GetInformationsPrefectureTrait.php:28-33` |
| R2 | O título é obrigatório, com no máximo 120 caracteres | `v6/app/Http/Requests/ContentRequest.php:288-297` |
| R3 | O número fica no campo `text` e é obrigatório | formulário `v6/resources/views/prefecture/contents/telefones-uteis/form.blade.php` |
| R4 | O `category_contents_id` é enviado hidden e validado como numérico | mesmo formulário |
| R5 | A listagem do CMS mostra apenas conteúdos não apagados (`deleted_at IS NULL`) | view `telefones_uteis` |
| R6 | A listagem do CMS ordena do mais novo para o mais antigo (`created_at DESC`) | view `telefones_uteis` |
| R7 | A listagem do portal **não** aplica filtro de `status` — telefones “desativados” continuam públicos | `UsefulPhonesController@getIndex` |
| R8 | O portal não pagina nem ordena: a ordem é a do `SELECT` (sem `ORDER BY`) | `GetInformationsPrefectureTrait::getContentByCategory` |
| R9 | A busca do portal é client-side e case-insensitive (exceto no layout `default`) | views do portal |
| R10 | O telefone não é validado como número no servidor (máscara é só CSS/cliente) | `advanced-phone` no input |
| R11 | Exclusão em lote via `ajax.generic.content.deleteAll` | `v6/resources/views/prefecture/contents/telefones-uteis/index.blade.php` |

## 2. Acoplamentos

### Dependências de outros módulos

| Módulo | Dependência | Motivo |
|---|---|---|
| Conteúdo genérico | `Prefecture\ContentController`, `ContentRequest`, `prefecture.contents.*` | o telefone é um conteúdo comum |
| Categorias | `category_contents` (slug) | classificação do registro |
| Layout/Configuração | `configurations.key = 'layout'` | escolhe a view do portal |
| Menu do CMS | `MenuServiceProvider` (item `Telefones Úteis`) | entrada do operador |
| Permissões | `resources`/`roles` com `telefones-uteis.*` | controle de ações |
| Log de atividade | `activity()` (`spatie/laravel-activitylog` ou similar, via `generateLog`) | trilha de auditoria |

### Dependências deste módulo em outros módulos

| Consumidor | Uso |
|---|---|
| `MenuServiceProvider` | item de menu “Telefones Úteis” |
| `v6/routes/breadcrumbs.php:235` | breadcrumb público |
| `v6/database/seeds/ResourcesTableSeeder.php` / `RolesTableSeeder.php` | permissões `telefones-uteis.*` |
| `v6/app/Http/Controllers/Prefecture/DatatableController.php:122` | endpoint de DataTable |

## 3. Armadilhas e riscos técnicos

| # | Armadilha | Evidência | Impacto |
|---|---|---|---|
| A1 | O portal busca `contents` diretamente, o CMS busca a view SQL: **dois caminhos de leitura para o mesmo dado** | `UsefulPhonesController` vs `DatatableController@get_telefones_uteis` | regras podem divergir silenciosamente |
| A2 | A view SQL não expõe `text`; se o telefone mudar, a view continua igual | migration `2023_03_16_093714` | relatórios não mostram o número |
| A3 | A view SQL depende de `row_number()`, função do PostgreSQL 9+/10 — não portável para MySQL | mesma migration | migração da v7 para MySQL exigiria reescrita |
| A4 | Layout `default` não tem `informacoes-instituicao/telefones-uteis` | `v6/resources/views/portal/layouts/default/pages/contents/telefones-uteis/index.blade.php` é o único caminho do layout default | erro 500 ao publicar com layout `default` |
| A5 | A view `default` usa `$category`, que o controller não envia | `Breadcrumbs::render('category-content', $category)` | variável indefinida |
| A6 | Busca do portal é case-sensitive apenas no layout `default` | comparação `indexOf` vs `toUpperCase` | UX inconsistente |
| A7 | `ContentController@store` tem um branch morto para telefone (`$request->get('phone')`) | `v6/app/Http/Controllers/Prefecture/ContentController.php:662-663` | mantém a dúvida sobre o nome correto do campo |
| A8 | `ContentRequest` declara a mensagem `phone.required` sem regra correspondente | `v6/app/Http/Requests/ContentRequest.php:337` | resíduo de código morto |
| A9 | Sem filtro de `status` no portal, um telefone desativado continua publicado | `getContentByCategory` | privacidade/erro operacional |
| A10 | Sem paginação nem ordenação no portal | idem | volume alto degrada a página |
| A11 | O `slug` é gerado a partir do título e recebe sufixo numérico em caso de colisão (`"pub" . $id . "-"` no update) | `ContentController@store` / `update` | slugs imprevisíveis |
| A12 | O número do telefone é gravado como texto livre em `contents.text`, sem coluna dedicada | schema | busca por telefone exige `ILIKE` sobre `text` |
| A13 | O portal não escapa explicitamente o `text` (Blade escapa por padrão, mas o dado pode conter HTML vindo do CMS) | view do portal | risco baixo, mas a origem é livre |
| A14 | O `title` do portal é renderizado sem limitação de tamanho, apesar do `max:120` | view do portal | inconsistência de layout |
| A15 | Tabelas `phones`/`phones_type` têm nomes e propósito parecidos com o módulo, mas são de outro fluxo | `v6/app/Http/Model/Admin/Phone.php` | risco de modelagem errada na v7 |

## 4. Código duplicado entre v5 e v6

| Trecho | v5 | v6 | Observação |
|---|---|---|---|
| `UsefulPhonesController` | `v5/app/Http/Controllers/Portal/UsefulPhonesController.php` | `v6/app/Http/Controllers/Portal/UsefulPhonesController.php` | idêntico, exceto o caminho da view (`informacoes-prefeitura` -> `informacoes-instituicao`) |
| View do portal | `v5/resources/views/portal/layouts/modelo5/informacoes-prefeitura/telefones-uteis/index.blade.php` | `v6/resources/views/portal/layouts/modelo5/informacoes-instituicao/telefones-uteis/index.blade.php` | renomeada de pasta |
| Migration da view | `v5/database/migrations/2023_03_16_093713_create_view_to_telefones_uteis.php` | `v6/database/migrations/2023_03_16_093714_create_view_to_telefones_uteis.php` | mesmo SQL, nome com 1 segundo de diferença |
| DataTable | `v5/app/Http/Controllers/Prefecture/DatatableController.php` | `v6/app/Http/Controllers/Prefecture/DatatableController.php` | mesmo método `get_telefones_uteis` |
| Formulário do CMS | `v5/resources/views/prefecture/contents/telefones-uteis/` | `v6/resources/views/prefecture/contents/telefones-uteis/` | mesma estrutura |

Trecho que mudou de forma relevante (v5 -> v6):

```php
// v5/app/Http/Controllers/Portal/UsefulPhonesController.php
return view('portal.layouts.'.$this->getTemplateActived().'.informacoes-prefeitura.telefones-uteis.index',
    compact('contents'));

// v6/app/Http/Controllers/Portal/UsefulPhonesController.php
return view('portal.layouts.'.$this->getTemplateActived().'.informacoes-instituicao.telefones-uteis.index',
    compact('contents'));
```

## 5. Onde a v6 divergiu da v5 (e por quê)

- **Reorganização do painel “Informações da Instituição”**: a pasta `informacoes-prefeitura` foi renomeada para `informacoes-instituicao` na v6, acompanhando a reclassificação de “Prefeitura” para “Instituição” que atingiu o módulo `a-instituicao`.
- **Consolidação de módulos no CMS**: `modulos` e `resources` passaram a ser a fonte de verdade do menu (migration `2024_11_01_150206_create_modulos_table.php`).
- **Busca client-side** idêntica nas duas versões; nenhuma melhoria funcional de filtro.

## 5.1 Checklist de paridade v6 -> v7

| Verificação | Como conferir |
|---|---|
| Contagem de telefones exibida no portal igual à da v6 | comparar `count(contents da categoria)` com `count(useful_phones)` |
| Título e número continuam visíveis como campos separados | inspeção da view do portal |
| Ordem do CMS preservada (mais novo primeiro) | conferir coluna `value` na tabela do CMS |
| Exportação Excel/PDF com as mesmas colunas | exportar nos dois sistemas e comparar |
| Permissões `telefones-uteis.*` equivalentes | comparar `resources`/`roles` antigos com os novos |
| Rota pública e breadcrumb preservados | `GET /telefones-uteis` respondendo 200 |

## 6. Regras de equivalência funcional (o que não pode mudar na v7)

1. O título e o número continuam sendo **dois campos separados**, com o número exibido como texto.
2. A listagem pública continua mostrando **todos** os registros da categoria (a menos que o negócio decida o contrário).
3. A ordenação do CMS continua sendo “mais novo primeiro” ou passa a ser explicitamente definida.
4. O Item de menu e o breadcrumb continuam apontando para as mesmas URLs públicas (`/telefones-uteis`).
5. A máscara de telefone continua no cliente, com validação equivalente no servidor.
