# ENQUETES — Resumo Executivo

> **Versão analisada:** `v6` (o módulo existe **idêntico** na `v5`)
> **Slug do módulo:** `enquetes` (tabela `modulos`, `id = 38`)
> **Natureza:** Ambos (CMS para criação/apuração + Portal público para votação)
> **Estado:** OK v6 — sem divergência funcional entre `v5` e `v6`

---

## 1. Objetivo

Enquetes de opinião pública de **escolha única**: o operador (CMS) cadastra uma
pergunta escrita, uma lista de opções de resposta e um par de datas
(publicação / expiração). O cidadão vota em uma das opções pelo portal público.
Ao encerrar, o portal exibe o percentual de votos de cada opção. A enquete é,
na prática, uma **pesquisa de múltipla escolha com apuração por contagem**.

Serve à prefeitura para medir satisfação/opinião sobre um tema específico
(e exemplo: consulta sobre um serviço municipal), com publicação no site.

## 2. Efeito / Impacto

| Faixa | Efeito |
|---|---|
| Cidadão | Vota em enquetes publicadas sem cadastro; recebe o percentual do resultado após o encerramento. Não há identificação do votante além do IP/User-Agent gravados em servidor. |
| Prefeitura (CMS) | Cria, edita, publica/rascunha e exclui enquetes; acompanha a quantidade de respostas de cada enquete na listagem. |
| Transparência e legalidade | As enquetes são um instrumento de opinião, sem valor de consulta popular formal; a apuração é por contagem simples de `survey_results`, sem ponderação, sem auditoria de rascunho e sem integridade verificável do voto (ver `08-GAPS-E-DIVIDAS.md`). |
| Operação técnica | Módulo simples (3 tabelas + 1 view SQL), sem filas, sem e-mail, sem API pública. Todo o cálculo de percentual é feito em PHP, dentro de um `foreach` na view do controller. |

## 3. Comportamento

- **Publicação:** uma enquete só aparece no portal se `status = true` e
  `publication_date <= agora` (`v6/app/Http/Controllers/Portal/SurveyController.php:18`).
- **Expiração:** quando `final_date <= agora`, a rota de votação redireciona para
  a rota de resultado (`v6/app/Http/Controllers/Portal/SurveyController.php:49-51`).
- **Voto único (declarado):** o portal compara o IP do visitante com o IP do
  **primeiro** registro de `survey_results` da enquete; se bater, exibe
  "Você já votou!" (`v6/app/Http/Controllers/Portal/SurveyController.php:57-58`).
  Não há restrição no banco que impeça votos duplicados ou por IP compartilhado.
- **Apuração:** para cada opção, `count(survey_results da opção) / total de votos
  da enquete * 100`, formatado com 2 casas
  (`v6/app/Http/Controllers/Portal/SurveyController.php:70-79`).
- **Edição bloqueada após votos:** a tela de edição desabilita o formulário
  quando a enquete já possui resultado (`v6/app/Http/Controllers/Prefecture/SurveyController.php:105-106`);
  essa trava é **apenas de interface**.
- **Exclusão em lote:** a listagem do CMS permite excluir várias enquetes
  via POST para `ajax.survey.deleteByLote`.
- **Exclusão de uma enquete** apaga também suas opções de resposta; a exclusão
  em lote **não** apaga as opções.

## 4. Como o Usuário Interage

**Operador (CMS — `/cms/enquete`)**

1. "Cadastrar Enquete" → formulário com *Link Permanente*, *Data de publicação*,
   *Data de expiração*, *Pergunta*, checkbox *Publicar* e um construtor dinâmico
   de *Opção(ões) de resposta*.
2. "Editar" → mesmo formulário; bloqueado (botão e inputs desabilitados) se
   a enquete já tiver votos.
3. Listagem em DataTable alimentada pela view SQL `enquetes`
   (colunas: Pergunta, Qtd. Resposta, Data de publicação, Data de encerramento).

**Cidadão (Portal)**

1. `/enquetes` — lista de enquetes; cada item mostra "Encerra em" (ou
   "Encerrada em") e o botão "Votar" ou "Ver Resultado".
2. `/enquetes/{slug}` — mostra a pergunta, "Publicado em", o formulário de radio
   e o botão "Votar". Após o clique, o voto é enviado por Ajax e a área do
   formulário é trocada por "Voto salvo com sucesso!".
3. `/enquetes/encerrada/{slug}` — mostra o percentual de cada opção com barra
   de progresso.
4. Barra lateral (`enquetes_sidebar`) presente nos 6 templates do portal
   (`modelo1`..`modelo6`) com a enquete ativa mais recente e voto embutido.

## 5. Detalhes e Particularidades Técnicas

- **Camadas:** `Route` → `Prefecture\SurveyController` →
  `SurveyRequest` (FormRequest) → `ISurveyRepository`/`SurveyRepository` →
  `ISurveyAnswerRepository`/`SurveyAnswerRepository` → `App\Prefecture\Survey`
  / `SurveyAnswer` → PostgreSQL.
- O portal **não** usa repository: `Portal\SurveyController` grava direto com
  `SurveyResult::create($request->all())` (`v6/app/Http/Controllers/Portal/SurveyController.php:99`).
- O controller do portal herda de `App\Http\Controllers\Admin\Controller`
  (base do CMS) para poder chamar `getTemplateActived()` — acoplamento indevido.
- O tema do portal é escolhido por `Configuration` chave `layout`; os templates
  `modelo1`..`modelo6` têm a sidebar de enquetes; o diretório `default` **não**
  possui `home/index` nem `layout/layout` e, se for o layout ativo, o portal
  responde 404 (`v6/app/Http/Controllers/Admin/Controller.php:43-45`).
- O status de exibição da enquete no portal depende do módulo `enquetes` estar
  ligado em `modulos.status` (cache `rememberForever` em
  `v6/app/Providers/ModulosServiceProvider.php:22-24`).
- Dependências: `laracasts/flash` (mensagens), `spatie/laravel-activitylog`
  (auditoria), `yajra/laravel-datatables` (listagem), `cartalyst/sentinel`
  (autenticação/permissões).

## 6. Estrutura de Dados

| Objeto | Papel |
|---|---|
| `surveys` | Enquete (pergunta, datas, status, slug). Soft delete. |
| `survey_answers` | Opções de resposta de uma enquete. Soft delete. |
| `survey_results` | Votos (enquete + opção + IP + user-agent). **Sem** soft delete. |
| view `enquetes` | Listagem para o DataTable do CMS: `value`, `pergunta`, `qtd_respostas`, `data_encerramento`, `data_publicacao`, `id`. |
| `question_types`, `questions`, `options`, `option_categories`, `option_category_has_options` | **Não pertencem a este módulo.** São do módulo *Revisões* (`v6/app/Http/Model/Prefecture/Questions.php`, `v6/app/Http/Model/Prefecture/Options.php`). As enquetes usam apenas `survey_answers` como lista de opções. |
| Seeds | Nenhum seed é específico de enquetes. `QuestionTypesSeeder`, `OptionsSeeder`, `OptionCategoriesSeeder` e `OptionsCategoryHasOptionSeeder` alimentam o módulo *Revisões*. |

## 7. Como Testar

1. Criar enquete no CMS com 2 opções, publicar e definir expiração futura.
2. `GET /enquetes` → a enquete deve aparecer; `GET /enquetes/{slug}` → formulário.
3. `POST /send-survey-ajax` sem `survey_answer_id` → JSON `[400, "Por favor, marque uma das opções"]`.
4. `POST /send-survey-ajax` com `survey_id` + `survey_answer_id` → JSON `[201, "Voto salvo com sucesso! Obrigado por sua participação!"]`.
5. Repetir o passo 4 → **deve** ser aceito (não há trava real de voto único).
6. Expirar a enquete → `GET /enquetes/{slug}` redireciona para
   `/enquetes/encerrada/{slug}` e a soma dos percentuais deve ser 100%.

## 8. Migração para Laravel Moderno

- `Survey` / `SurveyAnswer` / `SurveyResult` como Eloquent enxuto com casts
  (`status` → bool, `final_date`/`publication_date` → `datetime`), `SoftDeletes`
  só onde já existe.
- `SurveyRequest` → Form Requests separados (`StoreSurveyRequest`,
  `UpdateSurveyRequest`) com `Rule::date()->after()` para as datas e
  validação cruzada `final_date > publication_date`.
- `SurveyRepository` genérico (com `dd($th)`) → Policies + Form Requests +
  `SurveyService` com transação explícita para enquete + opções.
- Apuração → consulta agregada única (`GROUP BY survey_answer_id`) em vez do
  `foreach` com N+1 atual; cachear resultado de enquete encerrada.
- Voto único → chave única parcial em `survey_results` (ou tabela de
  "votos por dispositivo") + validação de que `survey_answer_id` pertence a
  `survey_id` + reuso do motor de formulários dinâmicos já existente no
  portal para o formulário de votação.

## 9. Resumo Geral

O módulo Enquetes implementa uma enquete de escolha única em três tabelas
(`surveys`, `survey_answers`, `survey_results`) e uma view SQL de listagem
(`enquetes`). O CMS cadastra pergunta, opções e janela de validade; o portal
lista, coleta o voto por Ajax (`send-survey-ajax`) e, após o encerramento,
mostra o percentual de cada opção calculada em PHP. Não existe integração com
o motor de formulários dinâmicos (`App\Prefecture\DynamicForms\*`), que é
completamente independente — apesar de ambos estarem no mesmo portal. As tabelas
`question_types`, `questions`, `options`, `option_categories` e
`option_category_has_options` **não** pertencem a este módulo, mas ao de
*Revisões*. O principal passivo do legado é a integridade do voto: não há
restrição de unicidade, não se valida o vínculo entre enquete e opção, aceita-se
voto após o encerramento e o "já votei" só compara o primeiro registro de
votos da enquete.

---

## Í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 detalhada |
| `03-ROTAS-E-CONTROLADORES.md` | Rotas, controllers, requests, resources |
| `04-VIEWS-E-UX.md` | Telas, componentes, JS, assets |
| `05-REGRAS-E-PORTABILIDADE.md` | Particularidades e armadilhas |
| `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 |
