# Formulários Dinâmicos — Resumo Executivo

> **Versão analisada:** `v6` (código idêntico à `v5`, ver `08-GAPS-E-DIVIDAS.md`)
> **Slug do módulo:** `formularios-dinamicos` — tabela `modulos`, id **52** ("Formulários Dinâmicos", `status = f`)
> **Natureza:** Ambos (CMS para montar o questionário + Portal público para o cidadão responder)
> **Estado:** OK v6 — módulo herdado da v5 sem alterações de lógica

---

## 1. Objetivo

O módulo é um **motor de formulários configuráveis**: o operador da prefeitura monta, sem tocar em código, um questionário composto por *categorias* (seções) → *perguntas* (`asks`) → *tipos* (texto, seleção, arquivo, radio, intervalo, checkbox, nível de satisfação) → *estilos* (máscaras de CPF/CNPJ/CEP/data/dinheiro/telefone) → *parâmetros* (`config` JSON). O cidadão acessa o portal por um link permanente (`/formularios/{slug}`), preenche e envia; as respostas ficam gravadas como **JSON** em `filled_questionnaires.answers` e podem ser exportadas para planilha (XLSX) pelo CMS.

## 2. Efeito / Impacto

| Faixa | Efeito |
|---|---|
| Cidadão | Preenche questionários públicos (sem login) com máscaras, upload de anexos, validação de obrigatoriedade no navegador e, se configurado, reCAPTCHA. Pode ver o resultado agregado se o questionário tiver `has_result`. |
| Prefeitura (CMS) | Cria/edita questionários com arrastar-e-soltar de campos e categorias, hierarquia de categorias (pai/filho), tipos e estilos de pergunta, ordem e largura (`size` colunas Bootstrap). Consulta respostas por painel, individual e exporta planilha. |
| Transparência e legalidade | `activity()` registra exclusão em lote de questionários e de respostas. `spatie/laravel-activitylog` + `cartalyst/sentinel` identificam o operador responsável. |
| Operação técnica | Zero de lógica de formulário fora do padrão: tipos e estilos são linhas de catálogo (`ask_types`, `ask_styles`) populadas por seed; a "programação" de cada pergunta fica no `config` JSON da linha `asks`. |

## 3. Comportamento

Regras de negócio observáveis:

- Um **questionário** (`questionnaires`) tem `status` (habilitado), `has_result` (exibe resultados), `title`, `description` (HTML do Summernote), `submit_info` (aviso pós-envio), `published_at`, `slug` (link permanente) e `softDeletes`.
- **Categorias** (`ask_categories`) pertencem a um questionário e podem ter `parent_id` (hierarquia de 1 nível usada no portal). Cada categoria deve conter **ao menos uma pergunta** — o controller recusa o save caso contrário.
- **Perguntas** (`asks`) têm `identifier` único (usado como *name* do input no HTML), `title`, `placeholder`, `description`, `required`, `size` (largem Bootstrap), `order`, `ask_type_id`, `ask_style_id` e `config` (JSON).
- O `config` só tem conteúdo por tipo: `{options:[{value,text}]}` para SELECT/MULTI_SELECT/RADIO, `{isImg:bool}` para FILE/MULTI_FILE, `{min,max}` para RANGE.
- Ao salvar o questionário, perguntas removidas **não** podem ter respostas já gravadas: o controller procura o `ask_id` dentro do JSON `answers` e devolve 422 com o título da pergunta conflictante.
- Respostas são gravadas em `filled_questionnaires.answers` como lista `[{ask_id, value}]`; `value` pode ser escalar ou array (múltipla escolha / múltiplos arquivos).
- Arquivos são gravados em disco **antes** do envio do questionário (`POST /formularios/ask-file`) e ficam "pendentes" (`ask_files.filled_questionnaire_id = null`) até que a resposta os referencie.
- Categorias órfãs (sem `questionnaire_id`) e com mais de 1 dia de criação são apagadas por um job de limpeza dentro do laço de save do questionário.
- Exclusão de questionário é *soft delete*; a exclusão em lote registra log de atividade.

## 4. Como o Usuário Interage

**Operador (CMS `/cms/formularios-dinamicos`)**:

1. "Lista de Formulários" — DataTable alimentado pela view SQL `listagem_questionarios`; colunas ID, Título, Qtd. de Campos, Data de Publicação, Data de Criação, Ações (editar, ativar/desativar, habilitar/desabilitar resultados, excluir, excluir em lote).
2. "Cadastrar Formulário" — form com Habilitar formulário, Título, Data de publicação, Descrição (Summernote), Aviso sobre o envio, lista de categorias e botão **Adicionar Campo**.
3. Modal de pergunta — escolhe tipo, estilo, título, placeholder, descrição, obrigatoriedade, largura, valor padrão, e os campos extras do tipo (opções, `isImg`, `min`/`max`).
4. "Formulários Preenchidos" — DataTable da view `listagem_questionarios_preenchidos`; por questionário abre o painel de respostas (resumo por pergunta, painel individual por resposta, exportar planilha).

**Cidadão (Portal)**:

- `/formularios/{slug}` exibe título, descrição, os campos agrupados por categoria (fieldset/legend), botão **Salvar** e, se `has_result`, botão **Resultado**.
- Após enviar, o JS faz `POST /formularios/fill-questionnaire/save` e mostra a mensagem de sucesso/erro no topo (`#handleMessages`).
- `/formularios/resultado/{slug}` exibe gráficos (pizza) para perguntas de escolha/radio/satisfação; texto, arquivos, intervalo e checkbox só aparecem para usuário logado (Sentinel) — comportamento herdado da v5.

## 5. Detalhes e Particularidades Técnicas

- **Sem Repository/Service**: o módulo fala direto com Eloquent a partir dos controllers. A camada de "Actions" (`v6/app/Http/Controllers/Prefecture/DynamicForms/Questionnaire/Actions/` e `v6/app/Http/Controllers/Prefecture/DynamicForms/FilledQuestionnaire/Actions/`) é usada como unitário de trabalho: o controller principal faz `return XController::handle($request)`.
- **Form Requests** com `authorize() => true` (autorização real fica no middleware `check.route.permission` por nome de rota).
- **Resources** JSON para o painel do CMS; o portal é renderizado server-side em Blade, não por API.
- **JS crítico no navegador**: validação de obrigatoriedade, máscaras (jQuery Mask) e validação de CPF/CNPJ/Data acontecem **só no cliente**; o servidor salva o que vier.
- Pacotes: `maatwebsite/excel` (exportação), `spatie/laravel-activitylog` (log de exclusão), `laracasts/flash` (mensagens), `cartalyst/sentinel` (usuário do CMS e do portal).

## 6. Estrutura de Dados

| Tabela | Papel | Chave |
|---|---|---|
| `questionnaires` | Questionário (com soft delete) | `id`, `identifier` único |
| `ask_categories` | Seção do questionário (com `parent_id`) | `id` |
| `asks` | Pergunta (com `config` JSON) | `id`, `identifier` único |
| `ask_types` | Catálogo de 10 tipos | `id` |
| `ask_styles` | Catálogo de 6 estilos de máscara | `id` |
| `filled_questionnaires` | Resposta (com `answers` JSON e `ip`) | `id` |
| `ask_files` | Anexo enviado pelo cidadão | `id` |

Views SQL: `listagem_questionarios` e `listagem_questionarios_preenchidos` (detalhes em `02-MODELAGEM.md`).

## 7. Como Testar

- CMS: criar questionário com 2 categorias e 3 perguntas (texto com máscara de CPF, escolha múltipla com opções, arquivo) → salvar → conferir o `config` gravado em `asks.config`.
- Portal: abrir `/formularios/{slug}`, enviar vazio (deve bloquear com "Hás campos inválidos"), enviar com arquivo e confirmar `ask_files.filled_questionnaire_id` preenchido.
- Exportar: `/cms/formularios-dinamicos/questionarios-preenchidos/{id}/respostas/exportar-planilha` e abrir o XLSX gerado em `storage/app/public/content/formularios-dinamicos/exports/{id}/exported-data.xlsx`.
- Detalhes e casos completos em `07-TESTES.md`.

## 8. Migração para Laravel Moderno

- `asks.config` e `filled_questionnaires.answers` devem sair de JSON " livre" para colunas tipadas (`answers` → tabela `answers` normalizada é o caminho recomendado; `config` → colunas `min`, `max`, `is_img` + tabela `ask_options`).
- `identifier` gerado com `time().rand().uniqid()` deve virar `Str::uuid()`.
- `str_slug()` (Laravel 5.5) → `Str::slug()`.
- Tipos/estilos → enums PHP 8.1 (`enum AskType: int`), com `match` no lugar dos `if`s de Blade.
- Regras de obrigatoriedade/CPF/CNPJ/data devem ser replicadas no servidor (hoje são só client-side).
- `exportSpreadSheet` deve virar job em fila; `Excel::store` síncrono é lento.
- Detalhes em `06-MIGRACAO-MODERNA.md`.

## 9. Resumo Geral

O módulo `formularios-dinamicos` implementa um construtor visual de questionários no CMS e o preenchimento público no portal. A estrutura é `questionnaire → ask_categories (hierárquicas) → asks`, e cada pergunta aponta para um tipo de catálogo e, opcionalmente, para um estilo de máscara. O "código" de cada pergunta viaja no campo JSON `config`, interpretado tanto pelo JS do CMS (montagem do formulário) quanto pelas views Blade do portal (geração do HTML) e pelo `ResponsesExport` (planilha). O cidadão envia as respostas por AJAX para `/formularios/fill-questionnaire/save`; o servidor grava um array `[{ask_id, value}]` em `filled_questionnaires.answers`, sem validação estrutural, e vincula os anexos previamente gravados em `ask_files`. O painel do CMS lista questionários e respostas via views SQL e exporta tudo para XLSX. O código PHP é byte-a-byte idêntico entre v5 e v6; a v6 só atualizou o layout do portal e as versões de cache dos scripts. Os principais riscos para a reconstrução são o JSON sem esquema, a validação só no cliente, o identificador frágil, o `ON DELETE CASCADE` de estilo nullable, o upload de arquivo antes do vínculo com a resposta (arquivo órfão) e a exportação síncrona.

---

## Índice do dossiê

| Arquivo | Conteúdo |
|---|---|
| `00-RESUMO.md` | Este documento |
| `01-ARQUITETURA.md` | Camadas, classes e o diagrama de fluxo |
| `02-MODELAGEM.md` | Estrutura de dados, views SQL, migrations e seeds |
| `03-ROTAS-E-CONTROLADORES.md` | Rotas CMS/portal, controllers, requests, resources |
| `04-VIEWS-E-UX.md` | Blades do CMS e do portal, JS, assets, exportação |
| `05-REGRAS-E-PORTABILIDADE.md` | Regras não óbvias, armadilhas e acoplamentos |
| `06-MIGRACAO-MODERNA.md` | Plano de reconstrução em Laravel moderno |
| `07-TESTES.md` | Estratégia e casos de teste |
| `08-GAPS-E-DIVIDAS.md` | Pendências, riscos, comparativo v5 vs v6 |
| `09-MOTOR-DE-PERGUNTAS.md` | Tipos, estilos, `config` e como cada um é interpretado |
| `10-FLUXO-DE-PREENCHIMENTO-PORTAL.md` | Etapas 1 a 4: GET do questionário → upload → validação no cliente |
| `11-RESPOSTAS-E-EXPORTACAO.md` | Formato das respostas, painel do CMS e planilha XLSX |
| `12-GRAVACAO-E-RESULTADO.md` | Etapas 5 e 6: gravação da resposta, tela de resultado, diagrama do fluxo e checklist de equivalência |
