Origin e deploy passam a documentar git.hellomanoel.com; GitHub/GHCR ficam como legado/backup. Co-authored-by: Cursor <cursoragent@cursor.com>
2597 lines
69 KiB
Markdown
2597 lines
69 KiB
Markdown
# SPEC.md — MVP da Plataforma de Assessoria de Eventos
|
||
|
||
> **Documento normativo para desenvolvimento orientado por especificação.**
|
||
> Este arquivo é a fonte de verdade para agentes como Cursor, Claude Code e outros agentes de implementação.
|
||
|
||
---
|
||
|
||
## 0. Metadados
|
||
|
||
| Campo | Valor |
|
||
|---|---|
|
||
| Produto | Plataforma de Assessoria de Eventos |
|
||
| Tipo | Aplicação web single-tenant |
|
||
| Estágio | MVP |
|
||
| Status da especificação | Aprovada para implementação |
|
||
| Idioma da interface | Português do Brasil (`pt-BR`) |
|
||
| Timezone padrão | `America/Sao_Paulo` |
|
||
| Cidade de atuação | São Paulo (capital) |
|
||
| Moeda | BRL, sem conversão entre moedas |
|
||
| Princípio principal | YAGNI — implementar somente o necessário para validar o produto |
|
||
| Arquitetura | Monólito modular Laravel |
|
||
| Área interna | Filament |
|
||
| Área pública | Blade + JS vanilla progressivo (Livewire é dependência do Filament, não usada no site público — ver ADR-015) |
|
||
| Servidor de aplicação | FrankenPHP em modo regular |
|
||
| Banco de dados | PostgreSQL |
|
||
| Testes | Pest e Pest Browser/Playwright |
|
||
|
||
### 0.1 Vocabulário normativo
|
||
|
||
- **MUST / DEVE**: requisito obrigatório.
|
||
- **MUST NOT / NÃO DEVE**: comportamento proibido.
|
||
- **SHOULD / DEVERIA**: recomendação forte, dispensável apenas com justificativa registrada.
|
||
- **MAY / PODE**: opção permitida.
|
||
|
||
### 0.2 Ordem de precedência
|
||
|
||
Em caso de conflito, seguir esta ordem:
|
||
|
||
1. Instrução explícita mais recente do responsável pelo produto.
|
||
2. Este `SPEC.md`.
|
||
3. ADRs aceitos no repositório.
|
||
4. Testes automatizados existentes.
|
||
5. Convenções já consolidadas no código.
|
||
6. Preferência do agente ou da biblioteca.
|
||
|
||
O agente **NÃO DEVE** alterar silenciosamente uma decisão deste documento. Uma alteração de escopo ou arquitetura deve atualizar esta especificação ou criar uma ADR.
|
||
|
||
Mudanças incrementais são planejadas em `openspec/changes/` e, após arquivadas, este documento DEVE ser revalidado para incorporar decisões ratificadas (back-sync). Este arquivo permanece a fonte de verdade do produto.
|
||
|
||
---
|
||
|
||
## 1. Contrato de operação para agentes
|
||
|
||
Antes de implementar qualquer mudança, o agente DEVE:
|
||
|
||
1. Ler este arquivo integralmente.
|
||
2. Inspecionar a estrutura atual do repositório.
|
||
3. Identificar o requisito funcional pelo ID.
|
||
4. Listar os arquivos que pretende criar ou alterar.
|
||
5. Implementar uma fatia vertical pequena e funcional.
|
||
6. Criar ou atualizar os testes correspondentes.
|
||
7. Executar os gates de qualidade aplicáveis.
|
||
8. Informar o que foi concluído, o que permanece pendente e qualquer desvio da especificação.
|
||
|
||
### 1.1 Regras de comportamento do agente
|
||
|
||
O agente:
|
||
|
||
- DEVE priorizar a solução mais simples que satisfaça os critérios de aceite.
|
||
- DEVE reutilizar recursos nativos de Laravel, Filament e Livewire antes de adicionar dependências.
|
||
- DEVE manter Filament Resources e componentes Livewire finos.
|
||
- DEVE colocar regras de negócio em classes testáveis de domínio ou aplicação.
|
||
- DEVE adicionar testes antes de marcar um requisito como concluído.
|
||
- DEVE preservar compatibilidade com PostgreSQL e com o contêiner de produção.
|
||
- DEVE trabalhar em uma fase do backlog por vez, salvo instrução explícita.
|
||
- NÃO DEVE criar funcionalidades listadas em “Fora do MVP”.
|
||
- NÃO DEVE introduzir microserviços, uma SPA separada, API pública, Redis ou mensageria externa.
|
||
- NÃO DEVE criar abstrações genéricas sem ao menos dois usos concretos.
|
||
- NÃO DEVE criar `RepositoryInterface`, `BaseService`, `BaseAction` ou “helpers” genéricos por antecipação.
|
||
- NÃO DEVE colocar regras financeiras diretamente em views, Resources, Models observers ou callbacks de formulário.
|
||
- NÃO DEVE usar `float` para dinheiro.
|
||
- NÃO DEVE persistir status derivados que possam ser calculados corretamente a partir dos dados fonte.
|
||
|
||
### 1.2 Entrega incremental
|
||
|
||
Cada fatia vertical deve, quando aplicável, incluir:
|
||
|
||
- migration;
|
||
- enum ou regra de domínio;
|
||
- model e relacionamentos;
|
||
- Action/Query;
|
||
- Policy;
|
||
- interface Filament ou Livewire;
|
||
- factories e seed mínimo;
|
||
- testes unitários e/ou feature;
|
||
- teste browser quando a jornada for crítica;
|
||
- documentação curta de qualquer decisão não óbvia.
|
||
|
||
---
|
||
|
||
## 2. Visão do produto
|
||
|
||
O MVP é uma aplicação única para uma assessora de eventos ou uma equipe pequena. O produto possui duas superfícies integradas:
|
||
|
||
1. **Site público premium**, usado para apresentar a marca, construir confiança e converter visitantes em leads.
|
||
2. **Área interna operacional**, usada para gerenciar leads, eventos, tarefas, fornecedores, orçamento, pagamentos manuais, documentos e conteúdo do site.
|
||
|
||
### 2.1 Hipótese de produto
|
||
|
||
Uma assessora adotará o sistema quando ele:
|
||
|
||
- ajudar a vender melhor;
|
||
- reduzir planilhas e informações dispersas;
|
||
- mostrar claramente o que precisa de atenção;
|
||
- organizar o evento sem exigir uma mudança radical no fluxo de trabalho;
|
||
- transmitir profissionalismo aos possíveis clientes.
|
||
|
||
### 2.2 Resultados que o MVP precisa provar
|
||
|
||
- O site converte visitantes em leads qualificados.
|
||
- A assessora acompanha o pipeline sem planilhas externas.
|
||
- Um lead conquistado é convertido em evento sem redigitação.
|
||
- A assessora gerencia tarefas, fornecedores, orçamento e pagamentos manuais.
|
||
- O dashboard destaca pendências reais do dia.
|
||
- O site é visualmente forte, rápido, acessível e responsivo.
|
||
- A aplicação pode ser implantada e atualizada com segurança por pipeline automatizado.
|
||
|
||
### 2.3 Métricas iniciais
|
||
|
||
| Dimensão | Métrica | Meta inicial |
|
||
|---|---|---|
|
||
| Aquisição | Conclusão do formulário de briefing | Medir baseline e melhorar por iteração |
|
||
| Comercial | Leads ativos sem próxima ação | `0` ao fim de cada dia útil |
|
||
| Operação | Tarefas vencidas sem responsável | `0` |
|
||
| Financeiro | Itens de orçamento sem valor ou status | Menos de 5% por evento ativo |
|
||
| Qualidade | Jornadas E2E críticas passando | 100% antes de deploy |
|
||
| Confiabilidade | Erros não tratados | Alerta imediato e tendência decrescente |
|
||
|
||
---
|
||
|
||
## 3. Personas e acesso
|
||
|
||
### 3.1 Visitante
|
||
|
||
Objetivo: entender os serviços, confiar na assessora, ver eventos reais e solicitar contato.
|
||
|
||
Permissões:
|
||
|
||
- acessar apenas as rotas públicas;
|
||
- enviar o formulário de briefing;
|
||
- não possui conta nem área autenticada.
|
||
|
||
### 3.2 Assessora administradora
|
||
|
||
Objetivo: gerenciar toda a operação e o conteúdo do site.
|
||
|
||
Permissões:
|
||
|
||
- acesso integral ao Filament;
|
||
- gerenciar usuários internos;
|
||
- gerenciar configurações e conteúdos públicos;
|
||
- criar, atualizar e excluir entidades operacionais conforme Policies;
|
||
- visualizar auditoria.
|
||
|
||
### 3.3 Assistente
|
||
|
||
Objetivo: atualizar a rotina operacional dos eventos.
|
||
|
||
Permissões:
|
||
|
||
- acessar leads, eventos, tarefas, fornecedores, orçamento, pagamentos e documentos;
|
||
- não gerenciar usuários;
|
||
- não alterar configurações globais do site;
|
||
- não acessar ou apagar auditoria;
|
||
- não executar exclusões críticas sem autorização.
|
||
|
||
### 3.4 Papéis
|
||
|
||
Usar enum simples:
|
||
|
||
```php
|
||
enum UserRole: string
|
||
{
|
||
case Admin = 'admin';
|
||
case Assistant = 'assistant';
|
||
}
|
||
```
|
||
|
||
Não instalar sistema de permissões granular no MVP.
|
||
|
||
---
|
||
|
||
## 4. Escopo
|
||
|
||
> **Recorte vigente do lançamento (ADR-016).** O escopo aprovado para o lançamento de 31/08/2026 é o **site institucional**: Fases 0 e 1. As Fases 2 a 5 — CRM de leads, conversão de lead em evento, eventos, tarefas, fornecedores, orçamento, pagamentos manuais, documentos, dashboard orientado a exceções e auditoria — permanecem especificadas neste documento mas ficam **adiadas**, sem data.
|
||
>
|
||
> A §4.1 abaixo descreve o produto completo, não o recorte do lançamento. Os itens marcados como adiados estão fora do que se constrói agora. Ver §18 para a divisão por fase e §23 para as condições de conclusão de cada recorte.
|
||
|
||
### 4.1 Incluído no MVP
|
||
|
||
No recorte do lançamento (Fases 0–1):
|
||
|
||
- site público;
|
||
- CMS interno do site;
|
||
- formulário de briefing;
|
||
- usuários internos e papéis simples;
|
||
- SEO básico;
|
||
- acessibilidade automatizada;
|
||
- CI/CD e deploy em contêiner com FrankenPHP.
|
||
|
||
Adiados para depois do lançamento (Fases 2–5, ver ADR-016):
|
||
|
||
- CRM de leads;
|
||
- conversão de lead em evento;
|
||
- cadastro e visão consolidada de eventos;
|
||
- checklist e tarefas;
|
||
- diretório e vínculo de fornecedores;
|
||
- orçamento por evento;
|
||
- pagamentos inseridos manualmente;
|
||
- documentos vinculados a leads e eventos;
|
||
- dashboard orientado a exceções;
|
||
- notificações internas e por e-mail para novos leads;
|
||
- auditoria de ações críticas.
|
||
|
||
Adiado **não** é o mesmo que fora do MVP: os itens acima seguem especificados neste documento e continuam sendo o produto pretendido. A §4.2 lista o que **NÃO DEVE** ser implementado em nenhum momento.
|
||
|
||
### 4.2 Fora do MVP
|
||
|
||
Os itens abaixo **NÃO DEVEM** ser implementados:
|
||
|
||
- portal ou login para cliente final;
|
||
- convites digitais;
|
||
- RSVP;
|
||
- gestão de convidados;
|
||
- check-in ou QR Code;
|
||
- mapa de mesas;
|
||
- pagamento online;
|
||
- Pix, boleto, cartão ou gateway de pagamento;
|
||
- conciliação bancária;
|
||
- emissão fiscal;
|
||
- assinatura eletrônica;
|
||
- geração jurídica de contratos;
|
||
- cobrança automática;
|
||
- integração oficial com WhatsApp;
|
||
- chat interno;
|
||
- comunicação omnichannel;
|
||
- multi-tenancy;
|
||
- SaaS para múltiplas assessorias;
|
||
- aplicativo nativo;
|
||
- PWA offline avançada;
|
||
- push notifications;
|
||
- marketplace de fornecedores;
|
||
- avaliações públicas de fornecedores;
|
||
- IA generativa;
|
||
- transcrição ou resumo automático de reuniões;
|
||
- leitura automática de contratos;
|
||
- microserviços;
|
||
- event sourcing;
|
||
- CQRS arquitetural;
|
||
- Kafka, RabbitMQ, SQS ou Redis obrigatório;
|
||
- mecanismo externo de busca;
|
||
- API pública;
|
||
- editor visual de páginas semelhante a page builder;
|
||
- Kanban com drag-and-drop para leads;
|
||
- editor de templates de checklist.
|
||
|
||
### 4.3 Regra de mudança de escopo
|
||
|
||
Uma funcionalidade fora do MVP só poderá entrar quando:
|
||
|
||
1. desbloquear uma jornada já definida; ou
|
||
2. resolver um problema observado em uso real; e
|
||
3. tiver critério de aceite e impacto técnico documentados.
|
||
|
||
“Pode ser útil no futuro” não é justificativa suficiente.
|
||
|
||
---
|
||
|
||
## 5. Arquitetura de informação
|
||
|
||
### 5.1 Rotas públicas
|
||
|
||
| Método | Rota | Nome sugerido | Finalidade |
|
||
|---|---|---|---|
|
||
| GET | `/` | `home` | Home editorial |
|
||
| GET | `/servicos` | `services.index` | Lista de serviços publicados |
|
||
| GET | `/portfolio` | `portfolio.index` | Lista de casos publicados |
|
||
| GET | `/portfolio/{slug}` | `portfolio.show` | Detalhe de caso |
|
||
| GET | `/sobre` | `about` | História, método e credenciais |
|
||
| GET | `/contato` | `contact` | Briefing de contato |
|
||
| GET | `/privacidade` | `privacy` | Política de privacidade |
|
||
| GET | `/sitemap.xml` | `sitemap` | Sitemap público |
|
||
| GET | `/robots.txt` | `robots` | Política de crawling |
|
||
| GET | `/up` | `health` | Healthcheck sem autenticação |
|
||
|
||
### 5.2 Painel interno
|
||
|
||
Prefixo: `/admin`.
|
||
|
||
Navegação:
|
||
|
||
```text
|
||
Dashboard
|
||
Comercial
|
||
├── Leads
|
||
└── Atividades
|
||
Eventos
|
||
├── Eventos
|
||
├── Tarefas
|
||
├── Fornecedores
|
||
├── Orçamento e pagamentos
|
||
└── Documentos
|
||
Conteúdo do site
|
||
├── Serviços
|
||
├── Portfólio
|
||
├── Depoimentos
|
||
└── Configurações
|
||
Administração
|
||
├── Usuários
|
||
└── Auditoria
|
||
```
|
||
|
||
### 5.3 Páginas customizadas obrigatórias
|
||
|
||
- Dashboard: `Filament Page` customizada com widgets orientados a exceção.
|
||
- Detalhe do evento: página customizada do Resource com resumo operacional.
|
||
- Briefing público: Blade + Controller (`POST /contato`), ver §11.2.
|
||
- Home: Blade com componentes de design reutilizáveis.
|
||
|
||
### 5.4 Decisões de UX YAGNI
|
||
|
||
- Pipeline de leads será tabela com filtros e ações rápidas.
|
||
- Não criar Kanban no MVP.
|
||
- Conteúdo da home terá estrutura fixa; textos e itens serão editáveis.
|
||
- Não criar page builder genérico.
|
||
- Ações destrutivas precisam de confirmação.
|
||
- O dashboard deve priorizar pendências, não gráficos decorativos.
|
||
|
||
---
|
||
|
||
## 6. Design e experiência do site público
|
||
|
||
O site público é requisito funcional, não apenas acabamento visual.
|
||
|
||
### 6.1 Princípios visuais
|
||
|
||
- aparência editorial e premium;
|
||
- fotografia grande e bem tratada;
|
||
- tipografia com hierarquia clara;
|
||
- espaçamento generoso;
|
||
- poucos elementos simultâneos;
|
||
- transições sutis;
|
||
- contraste acessível;
|
||
- mobile-first;
|
||
- CTAs claros;
|
||
- sem aparência de template administrativo.
|
||
|
||
### 6.2 Estrutura da home
|
||
|
||
A home DEVE conter, nesta ordem aproximada:
|
||
|
||
1. header e navegação;
|
||
2. hero com proposta de valor e CTA;
|
||
3. manifesto da marca;
|
||
4. resumo dos serviços em destaque;
|
||
5. casos selecionados do portfólio;
|
||
6. método de trabalho (4 passos);
|
||
7. depoimentos;
|
||
8. posicionamento/perfil;
|
||
9. CTA final para briefing;
|
||
10. footer com contato, redes e links legais.
|
||
|
||
A ordem pode variar apenas se a revisão de UX justificar a mudança.
|
||
|
||
### 6.3 Design tokens mínimos
|
||
|
||
Centralizar tokens de:
|
||
|
||
- famílias tipográficas;
|
||
- escala de fonte;
|
||
- espaçamentos;
|
||
- raio de borda;
|
||
- largura de container;
|
||
- cores de fundo, texto, borda, destaque e estados;
|
||
- sombras;
|
||
- duração e easing de transições.
|
||
|
||
Não espalhar valores visuais arbitrários por componentes.
|
||
|
||
O MVP adota o sistema de design **Heritage Editorial** (ver DESIGN.md e ADR-013):
|
||
|
||
- tipografia serifada auto-hospedada (EB Garamond) com escala de display a label;
|
||
- paleta papel/oliva/sálvia/tinta com superfícies tonais; hierarquia sem sombras de card;
|
||
- raio de borda zero para superfícies interativas e de conteúdo;
|
||
- largura de container 1120px e ritmo de espaçamento de 8px;
|
||
- `prefers-reduced-motion` respeitado;
|
||
- contraste WCAG AA (tinta sobre papel e oliva sobre papel).
|
||
|
||
### 6.4 Requisitos de mídia
|
||
|
||
- Imagens públicas DEVERÃO possuir texto alternativo.
|
||
- A aplicação DEVERÁ validar extensão, MIME e tamanho.
|
||
- A aplicação DEVERÁ gerar ou servir variantes responsivas.
|
||
- Imagens fora da primeira dobra DEVERÃO usar lazy loading.
|
||
- Dimensões DEVERÃO ser reservadas para evitar layout shift.
|
||
- Não armazenar blobs de imagem no PostgreSQL.
|
||
- Não depender do disco efêmero do contêiner em produção.
|
||
- O logotipo da marca DEVE possuir texto alternativo e variantes claro/escuro; `site_settings.logo_path` sobrescreve o asset padrão quando preenchido.
|
||
|
||
### 6.5 Acessibilidade
|
||
|
||
- navegação completa por teclado;
|
||
- foco visível;
|
||
- labels associados aos campos;
|
||
- mensagens de erro ligadas ao campo correspondente;
|
||
- landmarks semânticos;
|
||
- um único `h1` por página;
|
||
- ordem coerente de headings;
|
||
- contraste compatível com WCAG AA;
|
||
- suporte a `prefers-reduced-motion`;
|
||
- nenhuma issue automatizada crítica ou séria nas jornadas cobertas.
|
||
|
||
### 6.6 Performance e SEO
|
||
|
||
Metas:
|
||
|
||
| Métrica | Meta |
|
||
|---|---|
|
||
| LCP | ≤ 2,5 s nas páginas principais |
|
||
| CLS | ≤ 0,1 |
|
||
| INP | ≤ 200 ms |
|
||
| Erros JS no console | 0 nas jornadas críticas |
|
||
|
||
SEO obrigatório:
|
||
|
||
- title e meta description por página;
|
||
- canonical;
|
||
- Open Graph;
|
||
- sitemap;
|
||
- robots;
|
||
- slug legível;
|
||
- conteúdo publicado somente quando `published_at` estiver preenchido;
|
||
- dados estruturados básicos quando aplicáveis;
|
||
- páginas de portfólio indexáveis.
|
||
|
||
---
|
||
|
||
## 7. Requisitos funcionais
|
||
|
||
### 7.1 Site público e CMS
|
||
|
||
#### WEB-01 — Home editorial
|
||
|
||
**Prioridade:** P0
|
||
|
||
A home deve apresentar proposta, serviços, casos, método, depoimentos e CTA.
|
||
|
||
**Aceite:**
|
||
|
||
```gherkin
|
||
Given que existem serviços, casos e depoimentos publicados
|
||
When um visitante acessa a home
|
||
Then o conteúdo publicado é exibido na ordem configurada
|
||
And a página funciona em viewport desktop e mobile
|
||
And não existem erros JavaScript no console
|
||
And o CTA leva ao briefing
|
||
```
|
||
|
||
#### WEB-02 — Serviços
|
||
|
||
**Prioridade:** P0
|
||
|
||
Campos mínimos:
|
||
|
||
- título;
|
||
- slug;
|
||
- resumo;
|
||
- descrição;
|
||
- imagem de capa opcional;
|
||
- texto alternativo;
|
||
- ordem;
|
||
- destaque;
|
||
- `published_at`.
|
||
|
||
**Regras:**
|
||
|
||
- slug deve ser único;
|
||
- serviço não publicado não aparece no site;
|
||
- exclusão deve exigir confirmação;
|
||
- admin pode gerenciar; assistant não pode.
|
||
|
||
#### WEB-03 — Portfólio
|
||
|
||
**Prioridade:** P0
|
||
|
||
Campos mínimos:
|
||
|
||
- título;
|
||
- slug;
|
||
- resumo;
|
||
- tipo de evento;
|
||
- cidade/local opcional;
|
||
- data do evento opcional;
|
||
- desafio;
|
||
- solução;
|
||
- resultado opcional;
|
||
- imagem de capa;
|
||
- galeria ordenada;
|
||
- destaque;
|
||
- `published_at`;
|
||
- metadados SEO opcionais.
|
||
|
||
**Aceite:**
|
||
|
||
```gherkin
|
||
Given um caso salvo como rascunho
|
||
When a assessora preenche os campos obrigatórios e publica o caso
|
||
Then a rota pública pelo slug passa a responder 200
|
||
And o caso aparece na listagem pública
|
||
And as imagens possuem dimensões reservadas e textos alternativos
|
||
```
|
||
|
||
#### WEB-04 — Depoimentos
|
||
|
||
**Prioridade:** P0
|
||
|
||
Campos:
|
||
|
||
- texto;
|
||
- nome da pessoa;
|
||
- contexto ou tipo de evento opcional;
|
||
- fotografia opcional;
|
||
- ordem;
|
||
- destaque;
|
||
- `published_at`.
|
||
|
||
#### WEB-05 — Briefing de contato
|
||
|
||
**Prioridade:** P0
|
||
|
||
Campos públicos:
|
||
|
||
- nome completo;
|
||
- e-mail;
|
||
- telefone/WhatsApp;
|
||
- tipo de evento;
|
||
- data ou período desejado opcional;
|
||
- cidade;
|
||
- número estimado de convidados opcional;
|
||
- serviço de interesse opcional;
|
||
- mensagem/principal preocupação;
|
||
- origem de marketing capturada quando disponível;
|
||
- aceite do aviso de privacidade.
|
||
|
||
**Regras:**
|
||
|
||
- validar no servidor;
|
||
- usar honeypot e rate limiting;
|
||
- impedir duplo envio acidental;
|
||
- enviar e-mail de confirmação quando o serviço de e-mail estiver configurado;
|
||
- exibir sucesso sem revelar dados internos.
|
||
|
||
> **Estado atual (Fases 0–1):** o formulário usa Blade + Controller e envia apenas e-mails informativos (para a assessoria e confirmação ao visitante), sem criar Lead. A criação de Lead (status `new`, origem `website`), a notificação aos administradores e o registro de aceite de privacidade entram na **Fase 2**, quando o formulário passa a criar o Lead via `CaptureWebsiteLead`. A falha de e-mail não pode apagar dados já criados.
|
||
|
||
**Aceite:**
|
||
|
||
```gherkin
|
||
Given um visitante no formulário de briefing
|
||
When ele envia dados válidos uma única vez
|
||
Then um lead é criado com status Novo
|
||
And a tela mostra confirmação
|
||
And a assessora recebe notificação interna
|
||
And uma nova submissão causada por duplo clique não cria lead duplicado
|
||
```
|
||
|
||
#### WEB-06 — Configurações do site
|
||
|
||
**Prioridade:** P0
|
||
|
||
Singleton gerenciável apenas por admin:
|
||
|
||
- nome da marca;
|
||
- texto e CTA do hero;
|
||
- resumo institucional;
|
||
- e-mail;
|
||
- telefone;
|
||
- endereço/cidade opcional;
|
||
- links sociais;
|
||
- title padrão;
|
||
- description padrão;
|
||
- imagem Open Graph padrão;
|
||
- scripts de analytics opcionais, desabilitados por padrão.
|
||
|
||
Não criar sistema genérico de chave/valor se um singleton tipado resolver.
|
||
|
||
#### WEB-07 — Páginas institucionais
|
||
|
||
**Prioridade:** P1
|
||
|
||
- Sobre;
|
||
- Política de privacidade;
|
||
- página 404 com identidade visual;
|
||
- página 500 segura e sem stack trace em produção.
|
||
|
||
---
|
||
|
||
### 7.2 Leads e CRM
|
||
|
||
#### CRM-01 — Cadastro de lead
|
||
|
||
**Prioridade:** P0
|
||
|
||
Lead pode ser criado:
|
||
|
||
- automaticamente pelo briefing;
|
||
- manualmente no Filament.
|
||
|
||
Campos:
|
||
|
||
- nome;
|
||
- e-mail;
|
||
- telefone;
|
||
- tipo de evento;
|
||
- data estimada;
|
||
- cidade;
|
||
- convidados estimados;
|
||
- mensagem;
|
||
- origem;
|
||
- status;
|
||
- próxima ação em;
|
||
- tipo da próxima ação;
|
||
- observação da próxima ação;
|
||
- motivo da perda;
|
||
- `converted_event_id` ou relacionamento equivalente;
|
||
- timestamps.
|
||
|
||
#### CRM-02 — Pipeline
|
||
|
||
**Prioridade:** P0
|
||
|
||
Estados permitidos:
|
||
|
||
```php
|
||
enum LeadStatus: string
|
||
{
|
||
case New = 'new';
|
||
case Contacted = 'contacted';
|
||
case Meeting = 'meeting';
|
||
case Proposal = 'proposal';
|
||
case Negotiation = 'negotiation';
|
||
case Won = 'won';
|
||
case Lost = 'lost';
|
||
}
|
||
```
|
||
|
||
Transições permitidas no MVP:
|
||
|
||
- qualquer estado ativo pode avançar ou retroceder para outro estado ativo;
|
||
- `won` somente por conversão bem-sucedida ou ação administrativa explícita;
|
||
- `lost` exige motivo;
|
||
- lead convertido não pode voltar para estado ativo sem operação manual administrativa documentada;
|
||
- lead perdido pode ser reaberto por admin, limpando motivo de perda e criando auditoria.
|
||
|
||
#### CRM-03 — Próxima ação
|
||
|
||
**Prioridade:** P0
|
||
|
||
- lead ativo pode possuir data, tipo e observação;
|
||
- lead ativo sem próxima ação deve aparecer no dashboard;
|
||
- próxima ação passada deve aparecer como atrasada;
|
||
- lead `won` ou `lost` não precisa de próxima ação.
|
||
|
||
#### CRM-04 — Histórico
|
||
|
||
**Prioridade:** P0
|
||
|
||
Registrar atividades:
|
||
|
||
- nota manual;
|
||
- alteração de status;
|
||
- alteração de próxima ação;
|
||
- criação pelo site;
|
||
- conversão em evento;
|
||
- marcação como perdido ou reaberto.
|
||
|
||
O histórico deve ser cronológico e imutável pela interface comum. Admin pode remover somente em operação excepcional auditada.
|
||
|
||
#### CRM-05 — Busca e filtros
|
||
|
||
**Prioridade:** P0
|
||
|
||
Filtrar ou buscar por:
|
||
|
||
- nome;
|
||
- e-mail;
|
||
- telefone;
|
||
- status;
|
||
- origem;
|
||
- tipo de evento;
|
||
- data estimada;
|
||
- próxima ação;
|
||
- leads sem próxima ação.
|
||
|
||
#### CRM-06 — Conversão em evento
|
||
|
||
**Prioridade:** P0
|
||
|
||
A conversão:
|
||
|
||
- DEVE ser idempotente;
|
||
- DEVE executar em transação;
|
||
- DEVE criar somente um evento;
|
||
- DEVE copiar dados relevantes do lead;
|
||
- DEVE vincular lead e evento;
|
||
- DEVE criar checklist padrão;
|
||
- DEVE alterar lead para `won`;
|
||
- DEVE criar atividade e auditoria;
|
||
- DEVE rejeitar uma segunda conversão.
|
||
|
||
**Aceite:**
|
||
|
||
```gherkin
|
||
Given um lead ainda não convertido
|
||
When a assessora executa Converter em evento
|
||
Then um único evento é criado
|
||
And o checklist padrão é criado
|
||
And o lead é marcado como Conquistado
|
||
And uma segunda execução não cria outro evento
|
||
```
|
||
|
||
#### CRM-07 — Perda do lead
|
||
|
||
**Prioridade:** P0
|
||
|
||
- motivo é obrigatório;
|
||
- próxima ação deve ser removida;
|
||
- mudança deve aparecer no histórico;
|
||
- motivo não é visível em área pública.
|
||
|
||
---
|
||
|
||
### 7.3 Eventos
|
||
|
||
#### EVT-01 — Cadastro de evento
|
||
|
||
**Prioridade:** P0
|
||
|
||
Campos:
|
||
|
||
- título;
|
||
- tipo;
|
||
- lead de origem opcional;
|
||
- nome do cliente;
|
||
- e-mail do cliente;
|
||
- telefone do cliente;
|
||
- data e hora de início opcionais durante planejamento;
|
||
- data e hora de fim opcionais;
|
||
- local;
|
||
- cidade;
|
||
- status;
|
||
- orçamento-alvo em centavos opcional;
|
||
- resumo;
|
||
- observações internas;
|
||
- responsável interno opcional.
|
||
|
||
#### EVT-02 — Estados do evento
|
||
|
||
```php
|
||
enum EventStatus: string
|
||
{
|
||
case Planning = 'planning';
|
||
case Confirmed = 'confirmed';
|
||
case InProgress = 'in_progress';
|
||
case Completed = 'completed';
|
||
case Cancelled = 'cancelled';
|
||
}
|
||
```
|
||
|
||
Regras:
|
||
|
||
- novo evento começa em `planning`;
|
||
- `confirmed` exige data de início;
|
||
- `in_progress` exige data de início;
|
||
- `completed` registra `completed_at`;
|
||
- `cancelled` registra motivo opcional e auditoria;
|
||
- evento cancelado não aparece entre próximos eventos ativos;
|
||
- reabertura de evento concluído ou cancelado exige admin e auditoria.
|
||
|
||
#### EVT-03 — Visão consolidada
|
||
|
||
**Prioridade:** P0
|
||
|
||
A página do evento deve mostrar:
|
||
|
||
- resumo e status;
|
||
- cliente e contatos;
|
||
- dias restantes;
|
||
- próximas tarefas;
|
||
- tarefas vencidas;
|
||
- fornecedores vinculados;
|
||
- totais financeiros;
|
||
- próximos pagamentos;
|
||
- documentos recentes;
|
||
- atalhos para criar tarefa, vínculo de fornecedor, item de orçamento, pagamento e documento.
|
||
|
||
#### EVT-04 — Listagem e filtros
|
||
|
||
**Prioridade:** P0
|
||
|
||
Filtros:
|
||
|
||
- status;
|
||
- tipo;
|
||
- período;
|
||
- responsável;
|
||
- eventos nos próximos 7, 30 e 90 dias;
|
||
- eventos sem data;
|
||
- busca por título, cliente, cidade ou local.
|
||
|
||
---
|
||
|
||
### 7.4 Tarefas
|
||
|
||
#### TSK-01 — Checklist do evento
|
||
|
||
**Prioridade:** P0
|
||
|
||
Campos:
|
||
|
||
- evento;
|
||
- título;
|
||
- descrição opcional;
|
||
- responsável opcional;
|
||
- prazo opcional;
|
||
- prioridade;
|
||
- status;
|
||
- posição/ordem;
|
||
- `completed_at`;
|
||
- `completed_by`.
|
||
|
||
Enums:
|
||
|
||
```php
|
||
enum TaskPriority: string
|
||
{
|
||
case Low = 'low';
|
||
case Medium = 'medium';
|
||
case High = 'high';
|
||
case Critical = 'critical';
|
||
}
|
||
|
||
enum TaskStatus: string
|
||
{
|
||
case Pending = 'pending';
|
||
case InProgress = 'in_progress';
|
||
case Completed = 'completed';
|
||
}
|
||
```
|
||
|
||
#### TSK-02 — Regras de conclusão
|
||
|
||
- concluir tarefa preenche `completed_at` e `completed_by`;
|
||
- reabrir tarefa limpa ambos;
|
||
- tarefa concluída não é considerada vencida;
|
||
- tarefa pendente com prazo anterior a hoje é vencida;
|
||
- tarefa sem responsável é explicitamente sinalizada;
|
||
- exclusão exige confirmação.
|
||
|
||
#### TSK-03 — Checklist padrão
|
||
|
||
**Prioridade:** P0
|
||
|
||
O MVP deve possuir um checklist padrão definido em seed ou configuração de código versionada.
|
||
|
||
- checklist deve ser criado na conversão de lead;
|
||
- tarefas podem possuir prazo relativo à data do evento quando esta existir;
|
||
- se a data do evento ainda não existir, tarefas relativas permanecem sem prazo;
|
||
- ao definir posteriormente a data do evento, a aplicação pode calcular prazos ausentes uma única vez;
|
||
- editor de templates fica fora do MVP.
|
||
|
||
Checklist inicial sugerido:
|
||
|
||
1. confirmar briefing e escopo;
|
||
2. definir data e local;
|
||
3. definir orçamento-alvo;
|
||
4. mapear fornecedores essenciais;
|
||
5. confirmar fornecedores selecionados;
|
||
6. revisar cronograma geral;
|
||
7. realizar reunião final;
|
||
8. confirmar contatos operacionais;
|
||
9. revisar pagamentos pendentes;
|
||
10. concluir evento e registrar aprendizados.
|
||
|
||
#### TSK-04 — Visão global de tarefas
|
||
|
||
**Prioridade:** P1
|
||
|
||
Listar tarefas de todos os eventos com filtros por:
|
||
|
||
- responsável;
|
||
- status;
|
||
- prioridade;
|
||
- evento;
|
||
- vencidas;
|
||
- hoje;
|
||
- próximos 7 dias;
|
||
- sem responsável.
|
||
|
||
---
|
||
|
||
### 7.5 Fornecedores
|
||
|
||
#### VEN-01 — Diretório
|
||
|
||
**Prioridade:** P0
|
||
|
||
Campos:
|
||
|
||
- nome;
|
||
- categoria;
|
||
- nome do contato opcional;
|
||
- e-mail opcional;
|
||
- telefone opcional;
|
||
- cidade/região;
|
||
- site ou rede social opcional;
|
||
- observações internas;
|
||
- ativo/inativo.
|
||
|
||
Categorias devem usar enum ou tabela fixa simples. Não criar CRUD de categorias no MVP.
|
||
|
||
Categorias iniciais:
|
||
|
||
- espaço;
|
||
- buffet;
|
||
- bebidas;
|
||
- decoração;
|
||
- fotografia;
|
||
- filmagem;
|
||
- música/DJ;
|
||
- som e iluminação;
|
||
- mobiliário;
|
||
- celebrante/cerimonial;
|
||
- segurança;
|
||
- limpeza;
|
||
- transporte;
|
||
- hospedagem;
|
||
- gráfica/comunicação;
|
||
- outros.
|
||
|
||
#### VEN-02 — Vínculo ao evento
|
||
|
||
**Prioridade:** P0
|
||
|
||
Campos:
|
||
|
||
- evento;
|
||
- fornecedor;
|
||
- status;
|
||
- contato específico opcional;
|
||
- observações;
|
||
- valor de referência opcional em centavos.
|
||
|
||
Status:
|
||
|
||
```php
|
||
enum EventVendorStatus: string
|
||
{
|
||
case Quoted = 'quoted';
|
||
case Selected = 'selected';
|
||
case Contracted = 'contracted';
|
||
case Discarded = 'discarded';
|
||
}
|
||
```
|
||
|
||
Um fornecedor não deve ser vinculado duas vezes ao mesmo evento na mesma categoria sem justificativa explícita. Usar constraint adequada conforme o modelo final.
|
||
|
||
---
|
||
|
||
### 7.6 Orçamento e pagamentos manuais
|
||
|
||
#### FIN-01 — Item de orçamento
|
||
|
||
**Prioridade:** P0
|
||
|
||
Campos:
|
||
|
||
- evento;
|
||
- categoria;
|
||
- descrição;
|
||
- fornecedor opcional;
|
||
- valor planejado em centavos opcional;
|
||
- valor acordado em centavos opcional;
|
||
- status;
|
||
- observações;
|
||
- posição/ordem.
|
||
|
||
Status:
|
||
|
||
```php
|
||
enum BudgetItemStatus: string
|
||
{
|
||
case Planned = 'planned';
|
||
case Quoted = 'quoted';
|
||
case Approved = 'approved';
|
||
case Contracted = 'contracted';
|
||
case Cancelled = 'cancelled';
|
||
}
|
||
```
|
||
|
||
#### FIN-02 — Dinheiro
|
||
|
||
Regras obrigatórias:
|
||
|
||
- todos os valores monetários são BRL;
|
||
- armazenar valores em centavos com `BIGINT`;
|
||
- nunca usar `float` em domínio, Action ou teste;
|
||
- formatar na UI como moeda brasileira;
|
||
- entradas de formulário devem ser convertidas de string decimal para centavos;
|
||
- valores não podem ser negativos;
|
||
- não implementar conversão de moeda.
|
||
|
||
Criar Value Object pequeno somente se ele reduzir duplicação e bugs reais:
|
||
|
||
```php
|
||
final readonly class Money
|
||
{
|
||
public function __construct(public int $cents) {}
|
||
}
|
||
```
|
||
|
||
#### FIN-03 — Pagamento manual
|
||
|
||
**Prioridade:** P0
|
||
|
||
Campos:
|
||
|
||
- evento;
|
||
- item de orçamento opcional;
|
||
- descrição;
|
||
- valor em centavos;
|
||
- vencimento;
|
||
- data de pagamento opcional;
|
||
- método opcional;
|
||
- comprovante opcional;
|
||
- observações;
|
||
- criado por;
|
||
- atualizado por.
|
||
|
||
Métodos permitidos:
|
||
|
||
- Pix;
|
||
- transferência;
|
||
- dinheiro;
|
||
- cartão;
|
||
- boleto;
|
||
- outro.
|
||
|
||
O método é apenas informativo. Nenhum pagamento é processado pelo sistema.
|
||
|
||
#### FIN-04 — Status derivado do pagamento
|
||
|
||
Não persistir status quando puder ser derivado:
|
||
|
||
```text
|
||
paid_at != null => paid
|
||
paid_at == null e due_date < hoje => overdue
|
||
paid_at == null e due_date >= hoje => pending
|
||
```
|
||
|
||
“Vencendo em breve” é uma apresentação calculada para vencimentos entre hoje e hoje + 7 dias.
|
||
|
||
#### FIN-05 — Totais do evento
|
||
|
||
Calcular:
|
||
|
||
- planejado: soma de `planned_amount_cents` não cancelados;
|
||
- acordado: soma de `agreed_amount_cents` não cancelados;
|
||
- pago: soma de pagamentos com `paid_at`;
|
||
- pendente: soma de pagamentos sem `paid_at`;
|
||
- vencido: soma de pagamentos sem `paid_at` e com vencimento passado;
|
||
- saldo do acordado: `max(acordado - pago, 0)`.
|
||
|
||
Não criar tabela de totais materializados no MVP. Usar Query dedicada e índices adequados.
|
||
|
||
#### FIN-06 — Consistência financeira
|
||
|
||
- pagamento não pode ter valor zero ou negativo;
|
||
- data de pagamento pode ser anterior, igual ou posterior ao vencimento;
|
||
- comprovante é opcional;
|
||
- alteração e exclusão de pagamento geram auditoria;
|
||
- exclusão deve ser restrita a admin ou possuir confirmação reforçada;
|
||
- a soma de pagamentos pode superar o valor acordado; a UI deve sinalizar, não bloquear, pois ajustes reais podem existir;
|
||
- nenhum módulo contábil ou fiscal será criado.
|
||
|
||
**Aceite:**
|
||
|
||
```gherkin
|
||
Given um evento com item acordado de R$ 3.000,00
|
||
And dois pagamentos manuais de R$ 1.000,00 e R$ 2.000,00
|
||
When ambos possuem data de pagamento
|
||
Then o total pago é R$ 3.000,00
|
||
And o saldo acordado é R$ 0,00
|
||
And nenhum pagamento aparece como pendente ou vencido
|
||
```
|
||
|
||
---
|
||
|
||
### 7.7 Documentos
|
||
|
||
#### DOC-01 — Upload vinculado
|
||
|
||
**Prioridade:** P1
|
||
|
||
Documentos podem pertencer a:
|
||
|
||
- lead; ou
|
||
- evento.
|
||
|
||
Campos:
|
||
|
||
- nome exibido;
|
||
- path privado;
|
||
- MIME;
|
||
- tamanho;
|
||
- categoria;
|
||
- observação;
|
||
- usuário que enviou;
|
||
- timestamps.
|
||
|
||
Regras:
|
||
|
||
- arquivo deve ser privado por padrão;
|
||
- download requer autenticação e Policy;
|
||
- validar allowlist de tipos;
|
||
- limite inicial configurável, padrão 10 MB;
|
||
- nome original não deve determinar o path físico;
|
||
- não armazenar arquivos no banco;
|
||
- exclusão gera auditoria.
|
||
|
||
Categorias iniciais:
|
||
|
||
- briefing;
|
||
- proposta;
|
||
- contrato;
|
||
- orçamento;
|
||
- comprovante;
|
||
- referência visual;
|
||
- cronograma;
|
||
- outro.
|
||
|
||
---
|
||
|
||
### 7.8 Dashboard e notificações
|
||
|
||
#### DASH-01 — Dashboard orientado a atenção
|
||
|
||
**Prioridade:** P0
|
||
|
||
O dashboard deve responder: “O que precisa da minha atenção hoje?”
|
||
|
||
Widgets obrigatórios:
|
||
|
||
- leads novos;
|
||
- leads ativos sem próxima ação;
|
||
- próximas ações vencidas;
|
||
- eventos nos próximos 7, 30 e 90 dias;
|
||
- tarefas vencidas;
|
||
- tarefas de hoje;
|
||
- tarefas sem responsável;
|
||
- pagamentos vencidos;
|
||
- pagamentos vencendo em até 7 dias;
|
||
- atalhos para novo lead, evento, fornecedor e caso de portfólio.
|
||
|
||
Cada item deve ser acionável e levar ao registro correspondente.
|
||
|
||
#### DASH-02 — Notificação de novo lead
|
||
|
||
**Prioridade:** P0
|
||
|
||
- criar database notification para admins;
|
||
- enviar e-mail assíncrono quando configurado;
|
||
- usar database queue;
|
||
- falha de notificação não pode reverter a criação do lead;
|
||
- job deve ser idempotente quando possível.
|
||
|
||
#### DASH-03 — Lembrete diário
|
||
|
||
**Prioridade:** P1
|
||
|
||
Scheduler diário pode criar notificação resumida com:
|
||
|
||
- próximas ações vencidas;
|
||
- tarefas vencidas;
|
||
- pagamentos vencidos.
|
||
|
||
Não enviar resumo se não houver pendências.
|
||
|
||
---
|
||
|
||
### 7.9 Administração e auditoria
|
||
|
||
#### ADM-01 — Usuários internos
|
||
|
||
**Prioridade:** P0
|
||
|
||
- admin cria e desativa usuários;
|
||
- e-mail único;
|
||
- papel `admin` ou `assistant`;
|
||
- usuário inativo não autentica;
|
||
- reset de senha seguro;
|
||
- acesso ao Filament restrito explicitamente.
|
||
|
||
#### ADM-02 — Auditoria
|
||
|
||
**Prioridade:** P0
|
||
|
||
Ações auditáveis:
|
||
|
||
- conversão de lead;
|
||
- marcação de lead perdido ou reaberto;
|
||
- mudança financeira;
|
||
- exclusão de pagamento;
|
||
- exclusão de documento;
|
||
- publicação ou despublicação de conteúdo;
|
||
- exclusão de entidade principal;
|
||
- mudança de papel ou ativação de usuário;
|
||
- reabertura de evento concluído/cancelado.
|
||
|
||
Campos mínimos:
|
||
|
||
- usuário opcional;
|
||
- ação;
|
||
- tipo da entidade;
|
||
- ID da entidade;
|
||
- metadados JSON sem dados sensíveis desnecessários;
|
||
- IP opcional;
|
||
- request ID;
|
||
- timestamp.
|
||
|
||
Auditoria não precisa ser um event sourcing. Registrar apenas operações críticas.
|
||
|
||
---
|
||
|
||
## 8. Modelo de domínio e banco de dados
|
||
|
||
### 8.1 Módulos
|
||
|
||
| Módulo | Responsabilidade |
|
||
|---|---|
|
||
| Marketing | Conteúdo público e conversão |
|
||
| CRM | Leads, atividades e conversão |
|
||
| Events | Eventos, tarefas e documentos |
|
||
| Vendors | Fornecedores e vínculos |
|
||
| Finance | Orçamento e pagamentos manuais |
|
||
| Identity | Usuários, acesso e auditoria |
|
||
|
||
### 8.2 Tabelas obrigatórias
|
||
|
||
#### `users`
|
||
|
||
- `id` bigint PK;
|
||
- `name` varchar;
|
||
- `email` varchar unique;
|
||
- `email_verified_at` timestamp nullable;
|
||
- `password` varchar;
|
||
- `role` varchar indexed;
|
||
- `is_active` boolean default true indexed;
|
||
- remember token;
|
||
- timestamps.
|
||
|
||
#### `site_settings`
|
||
|
||
Singleton:
|
||
|
||
- `id` bigint PK;
|
||
- `brand_name`;
|
||
- `logo_path` nullable;
|
||
- `logo_alt` nullable;
|
||
- `hero_eyebrow` nullable;
|
||
- `hero_title`;
|
||
- `hero_subtitle`;
|
||
- `hero_cta_label`;
|
||
- `hero_cta_secondary_label` nullable;
|
||
- `hero_note` nullable;
|
||
- `manifesto_title`;
|
||
- `manifesto_lead`;
|
||
- `manifesto_body`;
|
||
- `method_steps` jsonb (4 passos tipados);
|
||
- `principles` jsonb (lista tipada);
|
||
- `about_summary` nullable;
|
||
- `email`;
|
||
- `phone`;
|
||
- `city` nullable;
|
||
- `social_links` jsonb;
|
||
- `default_meta_title`;
|
||
- `default_meta_description`;
|
||
- `default_og_image_path` nullable;
|
||
- `default_og_image_alt` nullable;
|
||
- `analytics_enabled` boolean default false;
|
||
- timestamps.
|
||
|
||
#### `services`
|
||
|
||
- `id`;
|
||
- `title`;
|
||
- `slug` unique;
|
||
- `summary`;
|
||
- `description` text;
|
||
- `cover_image_path` nullable;
|
||
- `cover_image_alt` nullable;
|
||
- `sort_order` integer indexed;
|
||
- `is_featured` boolean indexed;
|
||
- `published_at` timestamp nullable indexed;
|
||
- timestamps;
|
||
- soft delete opcional somente se recuperação for necessária.
|
||
|
||
#### `portfolio_cases`
|
||
|
||
- `id`;
|
||
- `title`;
|
||
- `slug` unique;
|
||
- `summary`;
|
||
- `event_type`;
|
||
- `city` nullable;
|
||
- `venue` nullable;
|
||
- `event_date` date nullable;
|
||
- `challenge` text;
|
||
- `solution` text;
|
||
- `result` text nullable;
|
||
- `cover_image_path`;
|
||
- `cover_image_alt`;
|
||
- `is_featured` boolean indexed;
|
||
- `sort_order` integer indexed;
|
||
- `published_at` timestamp nullable indexed;
|
||
- `meta_title` nullable;
|
||
- `meta_description` nullable;
|
||
- timestamps.
|
||
|
||
#### `portfolio_images`
|
||
|
||
- `id`;
|
||
- `portfolio_case_id` FK cascade;
|
||
- `path`;
|
||
- `alt_text`;
|
||
- `caption` nullable;
|
||
- `sort_order` integer;
|
||
- timestamps;
|
||
- index composto por `portfolio_case_id, sort_order`.
|
||
|
||
#### `testimonials`
|
||
|
||
- `id`;
|
||
- `quote` text;
|
||
- `author_name`;
|
||
- `context` nullable;
|
||
- `photo_path` nullable;
|
||
- `photo_alt` nullable;
|
||
- `sort_order` integer;
|
||
- `is_featured` boolean indexed;
|
||
- `published_at` timestamp nullable indexed;
|
||
- timestamps.
|
||
|
||
#### `leads`
|
||
|
||
- `id`;
|
||
- `name`;
|
||
- `email` nullable indexed;
|
||
- `phone` nullable indexed;
|
||
- `event_type`;
|
||
- `estimated_event_date` date nullable indexed;
|
||
- `city` nullable;
|
||
- `estimated_guests` integer nullable;
|
||
- `message` text nullable;
|
||
- `source` varchar indexed;
|
||
- `status` varchar indexed;
|
||
- `next_action_at` timestamp nullable indexed;
|
||
- `next_action_type` nullable;
|
||
- `next_action_notes` text nullable;
|
||
- `lost_reason` text nullable;
|
||
- `converted_event_id` nullable unique;
|
||
- `privacy_notice_accepted_at` timestamp nullable;
|
||
- `marketing_metadata` jsonb nullable;
|
||
- timestamps;
|
||
- soft deletes recomendados para recuperação operacional.
|
||
|
||
#### `lead_activities`
|
||
|
||
- `id`;
|
||
- `lead_id` FK cascade;
|
||
- `user_id` FK nullable set null;
|
||
- `type` varchar indexed;
|
||
- `description` text;
|
||
- `metadata` jsonb nullable;
|
||
- `created_at`;
|
||
- sem `updated_at` quando possível, pois atividade é append-only.
|
||
|
||
#### `events`
|
||
|
||
- `id`;
|
||
- `lead_id` FK nullable unique;
|
||
- `title`;
|
||
- `event_type`;
|
||
- `client_name`;
|
||
- `client_email` nullable;
|
||
- `client_phone` nullable;
|
||
- `starts_at` timestamp nullable indexed;
|
||
- `ends_at` timestamp nullable;
|
||
- `venue` nullable;
|
||
- `city` nullable;
|
||
- `status` varchar indexed;
|
||
- `target_budget_cents` bigint nullable;
|
||
- `summary` text nullable;
|
||
- `internal_notes` text nullable;
|
||
- `owner_user_id` FK nullable indexed;
|
||
- `completed_at` timestamp nullable;
|
||
- timestamps;
|
||
- soft deletes recomendados.
|
||
|
||
#### `event_tasks`
|
||
|
||
- `id`;
|
||
- `event_id` FK cascade indexed;
|
||
- `title`;
|
||
- `description` text nullable;
|
||
- `assigned_user_id` FK nullable indexed;
|
||
- `due_at` timestamp nullable indexed;
|
||
- `priority` varchar indexed;
|
||
- `status` varchar indexed;
|
||
- `sort_order` integer;
|
||
- `relative_due_days` integer nullable;
|
||
- `completed_at` timestamp nullable;
|
||
- `completed_by_user_id` FK nullable;
|
||
- timestamps;
|
||
- index composto por `event_id, status, due_at`.
|
||
|
||
#### `vendors`
|
||
|
||
- `id`;
|
||
- `name` indexed;
|
||
- `category` varchar indexed;
|
||
- `contact_name` nullable;
|
||
- `email` nullable;
|
||
- `phone` nullable;
|
||
- `city_region` nullable indexed;
|
||
- `website_url` nullable;
|
||
- `notes` text nullable;
|
||
- `is_active` boolean default true indexed;
|
||
- timestamps;
|
||
- soft deletes opcionais.
|
||
|
||
#### `event_vendors`
|
||
|
||
- `id`;
|
||
- `event_id` FK cascade indexed;
|
||
- `vendor_id` FK restrict indexed;
|
||
- `category` varchar indexed;
|
||
- `status` varchar indexed;
|
||
- `contact_override` nullable;
|
||
- `reference_amount_cents` bigint nullable;
|
||
- `notes` text nullable;
|
||
- timestamps;
|
||
- índice composto por `event_id, category, status`.
|
||
|
||
#### `budget_items`
|
||
|
||
- `id`;
|
||
- `event_id` FK cascade indexed;
|
||
- `vendor_id` FK nullable set null;
|
||
- `category` varchar indexed;
|
||
- `description`;
|
||
- `planned_amount_cents` bigint nullable;
|
||
- `agreed_amount_cents` bigint nullable;
|
||
- `status` varchar indexed;
|
||
- `notes` text nullable;
|
||
- `sort_order` integer;
|
||
- timestamps;
|
||
- checks de valores não negativos quando suportado.
|
||
|
||
#### `payments`
|
||
|
||
- `id`;
|
||
- `event_id` FK cascade indexed;
|
||
- `budget_item_id` FK nullable set null indexed;
|
||
- `description`;
|
||
- `amount_cents` bigint;
|
||
- `due_date` date indexed;
|
||
- `paid_at` timestamp nullable indexed;
|
||
- `method` varchar nullable;
|
||
- `proof_path` nullable;
|
||
- `notes` text nullable;
|
||
- `created_by_user_id` FK;
|
||
- `updated_by_user_id` FK;
|
||
- timestamps;
|
||
- check `amount_cents > 0`.
|
||
|
||
#### `documents`
|
||
|
||
- `id`;
|
||
- `lead_id` FK nullable cascade;
|
||
- `event_id` FK nullable cascade;
|
||
- `display_name`;
|
||
- `path`;
|
||
- `mime_type`;
|
||
- `size_bytes` bigint;
|
||
- `category` varchar indexed;
|
||
- `notes` text nullable;
|
||
- `uploaded_by_user_id` FK;
|
||
- timestamps;
|
||
- constraint: exatamente um entre `lead_id` e `event_id` deve ser preenchido.
|
||
|
||
#### `audit_logs`
|
||
|
||
- `id`;
|
||
- `user_id` FK nullable set null indexed;
|
||
- `action` varchar indexed;
|
||
- `auditable_type` varchar indexed;
|
||
- `auditable_id` bigint indexed;
|
||
- `metadata` jsonb nullable;
|
||
- `ip_address` nullable;
|
||
- `request_id` nullable indexed;
|
||
- `created_at`.
|
||
|
||
#### Infraestrutura Laravel
|
||
|
||
Usar tabelas padrão conforme recursos habilitados:
|
||
|
||
- notifications;
|
||
- jobs;
|
||
- job_batches, somente se necessário;
|
||
- failed_jobs;
|
||
- password reset tokens;
|
||
- sessions, caso sessão em banco seja escolhida.
|
||
|
||
### 8.3 Integridade e transações
|
||
|
||
Usar transação em:
|
||
|
||
- conversão de lead em evento;
|
||
- criação de evento com checklist;
|
||
- alterações financeiras que gerem múltiplas gravações;
|
||
- exclusões críticas com auditoria;
|
||
- operações que atualizam estado e histórico juntos.
|
||
|
||
Constraints de banco devem proteger:
|
||
|
||
- e-mail único de usuário;
|
||
- slug único;
|
||
- lead convertido uma única vez;
|
||
- evento originado de um único lead;
|
||
- valores financeiros válidos;
|
||
- relacionamentos obrigatórios;
|
||
- documento pertencendo a um único owner lógico.
|
||
|
||
---
|
||
|
||
## 9. Arquitetura técnica
|
||
|
||
### 9.1 Stack
|
||
|
||
| Camada | Tecnologia |
|
||
|---|---|
|
||
| Runtime | PHP com versão minor fixada no Docker |
|
||
| Framework | Laravel 13 |
|
||
| Admin | Filament 5 |
|
||
| UI pública | Blade + Tailwind + JS vanilla progressivo (sem framework reativo; Livewire 4 confinado ao Filament — ver ADR-015 e §22) |
|
||
| Banco | PostgreSQL |
|
||
| Servidor | FrankenPHP + Caddy |
|
||
| Assets | Vite |
|
||
| Testes | Pest 4 + Pest Browser/Playwright |
|
||
| Arquivos | Laravel Filesystem + Cloudflare R2 (S3-compatible) em produção |
|
||
| Fila | Database queue |
|
||
| Scheduler | Laravel Scheduler em processo separado |
|
||
|
||
Caso o projeto seja criado com versões posteriores, o agente deve manter compatibilidade entre as versões instaladas e registrar a decisão. Não fazer upgrade de major version no meio da implementação sem ADR.
|
||
|
||
### 9.2 Estilo arquitetural
|
||
|
||
Monólito modular com dependências orientadas:
|
||
|
||
```text
|
||
Interface (Filament / Livewire / HTTP)
|
||
↓
|
||
Application (Actions / Queries / Data)
|
||
↓
|
||
Domain (Enums / Value Objects / Rules)
|
||
↓
|
||
Infrastructure (Eloquent / Mail / Filesystem / Queue)
|
||
```
|
||
|
||
### 9.3 Regras arquiteturais
|
||
|
||
- Domain NÃO DEVE depender de Filament ou Livewire.
|
||
- Filament e Livewire chamam Actions e Queries.
|
||
- Eloquent Models podem existir em `app/Models` e ser usados por Actions/Queries.
|
||
- Não criar repositórios genéricos.
|
||
- Queries de leitura podem usar Eloquent diretamente.
|
||
- Actions devem representar casos de uso, não CRUD trivial.
|
||
- Enums representam estados estáveis.
|
||
- Value Objects somente para conceitos com regra real, como dinheiro.
|
||
- Eventos Laravel somente para efeitos colaterais claros.
|
||
- Policies controlam autorização.
|
||
- Observers devem ser evitados para regras críticas por esconderem fluxo.
|
||
- Soft delete deve ser aplicado seletivamente.
|
||
- Todo arquivo PHP próprio deve usar `declare(strict_types=1);` quando compatível.
|
||
|
||
### 9.4 Estrutura sugerida
|
||
|
||
```text
|
||
app/
|
||
├── Application/
|
||
│ ├── Actions/
|
||
│ │ ├── CRM/
|
||
│ │ ├── Events/
|
||
│ │ ├── Finance/
|
||
│ │ ├── Marketing/
|
||
│ │ └── Vendors/
|
||
│ ├── Data/
|
||
│ └── Queries/
|
||
├── Domain/
|
||
│ ├── CRM/
|
||
│ ├── Events/
|
||
│ ├── Finance/
|
||
│ ├── Marketing/
|
||
│ ├── Vendors/
|
||
│ └── Identity/
|
||
├── Filament/
|
||
│ ├── Pages/
|
||
│ ├── Resources/
|
||
│ └── Widgets/
|
||
├── Livewire/
|
||
│ └── PublicSite/
|
||
├── Models/
|
||
├── Notifications/
|
||
├── Policies/
|
||
└── Support/
|
||
|
||
resources/views/
|
||
├── components/
|
||
├── layouts/
|
||
├── livewire/public-site/
|
||
└── pages/
|
||
|
||
tests/
|
||
├── Architecture/
|
||
├── Browser/
|
||
├── Feature/
|
||
└── Unit/
|
||
```
|
||
|
||
Não criar diretórios vazios antecipadamente. Criar quando o primeiro uso existir.
|
||
|
||
### 9.5 Actions mínimas esperadas
|
||
|
||
- `CaptureWebsiteLead`;
|
||
- `CreateManualLead` somente se CRUD padrão não for suficiente;
|
||
- `UpdateLeadStatus`;
|
||
- `ScheduleLeadNextAction`;
|
||
- `MarkLeadAsLost`;
|
||
- `ReopenLead`;
|
||
- `ConvertLeadToEvent`;
|
||
- `CreateDefaultEventChecklist`;
|
||
- `CompleteEventTask`;
|
||
- `ReopenEventTask`;
|
||
- `RecordManualPayment`;
|
||
- `UpdateManualPayment`;
|
||
- `DeleteManualPayment`;
|
||
- `PublishPortfolioCase`;
|
||
- `UnpublishPortfolioCase`;
|
||
- `StorePrivateDocument`;
|
||
- `WriteAuditLog` ou serviço equivalente, sem transformar auditoria em event sourcing.
|
||
|
||
Não criar Action para todo `create/update` se o Filament Resource puder salvar de forma segura sem regra adicional.
|
||
|
||
### 9.6 Queries mínimas esperadas
|
||
|
||
- `GetDashboardAttentionItems` ou queries menores por widget;
|
||
- `GetEventFinancialSummary`;
|
||
- `GetUpcomingEvents`;
|
||
- `GetOverdueTasks`;
|
||
- `GetLeadsWithoutNextAction`;
|
||
- `GetOverduePayments`;
|
||
- `GetDueSoonPayments`.
|
||
|
||
### 9.7 Fluxos técnicos
|
||
|
||
#### Briefing
|
||
|
||
```text
|
||
Livewire valida
|
||
→ CaptureWebsiteLead
|
||
→ Lead salvo em transação
|
||
→ LeadActivity criada
|
||
→ NewLeadCaptured disparado após commit
|
||
→ Database notification e e-mail em fila
|
||
→ confirmação exibida
|
||
```
|
||
|
||
#### Conversão
|
||
|
||
```text
|
||
Filament Action autoriza
|
||
→ ConvertLeadToEvent
|
||
→ lock/checagem idempotente
|
||
→ cria Event
|
||
→ cria checklist
|
||
→ atualiza Lead
|
||
→ cria atividade e auditoria
|
||
→ commit
|
||
```
|
||
|
||
#### Pagamento
|
||
|
||
```text
|
||
Filament form valida
|
||
→ RecordManualPayment
|
||
→ converte string monetária para centavos
|
||
→ salva Payment
|
||
→ cria auditoria
|
||
→ Query financeira recalcula leitura
|
||
```
|
||
|
||
#### Publicação de caso
|
||
|
||
```text
|
||
Filament salva conteúdo
|
||
→ valida imagem e alt text
|
||
→ publica por published_at
|
||
→ rota pública passa a exibir
|
||
→ sitemap inclui slug
|
||
```
|
||
|
||
---
|
||
|
||
## 10. Interface Filament
|
||
|
||
### 10.1 Resources obrigatórios
|
||
|
||
- `LeadResource`;
|
||
- `EventResource`;
|
||
- `EventTaskResource` ou relation manager + visão global;
|
||
- `VendorResource`;
|
||
- `BudgetItemResource` preferencialmente dentro do evento;
|
||
- `PaymentResource` preferencialmente dentro do evento + visão global;
|
||
- `DocumentResource` ou managers vinculados;
|
||
- `ServiceResource`;
|
||
- `PortfolioCaseResource`;
|
||
- `TestimonialResource`;
|
||
- `SiteSettingResource` ou página singleton;
|
||
- `UserResource`;
|
||
- `AuditLogResource` somente leitura.
|
||
|
||
### 10.2 Regras de Resource
|
||
|
||
- tabelas sempre paginadas;
|
||
- busca somente em campos úteis;
|
||
- filtros indexados;
|
||
- eager loading explícito para evitar N+1;
|
||
- ações de estado devem chamar Actions;
|
||
- labels e mensagens em pt-BR;
|
||
- valores monetários formatados em BRL;
|
||
- datas apresentadas no timezone do produto;
|
||
- ações destrutivas confirmadas;
|
||
- Policies aplicadas e testadas;
|
||
- campos internos nunca expostos em rotas públicas.
|
||
|
||
### 10.3 Dashboard
|
||
|
||
Widgets devem executar queries pequenas e independentes. Não fazer uma consulta gigante que carregue todos os registros.
|
||
|
||
### 10.4 Detalhe do evento
|
||
|
||
A página deve combinar, sem virar uma única classe monolítica:
|
||
|
||
- header do evento;
|
||
- cards de atenção;
|
||
- resumo financeiro;
|
||
- tarefas;
|
||
- fornecedores;
|
||
- documentos;
|
||
- ações rápidas.
|
||
|
||
Usar componentes/Widgets menores e testáveis.
|
||
|
||
---
|
||
|
||
## 11. Site público e interatividade
|
||
|
||
### 11.1 Estado atual e componentes candidatos
|
||
|
||
O site público **não usa Livewire nem Alpine hoje**. As páginas são Blade renderizado no servidor mais JavaScript vanilla progressivo (`resources/js/app.js` e `resources/js/motion.js`). O `layouts.public` carrega apenas `@vite(['resources/css/app.css', 'resources/js/app.js'])` — nenhum `@livewireScripts`. O pacote `livewire/livewire` existe no projeto apenas como dependência transitiva de `filament/support` e opera somente dentro do painel `/admin`.
|
||
|
||
Se surgir a necessidade de consulta dinâmica, os candidatos naturais a Livewire seriam `FeaturedPortfolioCases` e `PublishedServices`. Essa adoção está condicionada ao gatilho registrado em §22.
|
||
|
||
Não transformar seções estáticas em componentes Livewire. Usar Blade quando não houver estado ou interação.
|
||
|
||
### 11.2 Formulário de briefing
|
||
|
||
> **Estado atual:** o formulário é implementado em Blade + Controller (`POST /contato`, `ContactBriefingRequest`), conforme WEB-05, e essa é a abordagem aceita — não um estágio provisório. Os requisitos abaixo valem independentemente da tecnologia; a criação de Lead segue para a Fase 2.
|
||
|
||
O formulário deve:
|
||
|
||
- ter estado tipado ou Form Object quando útil;
|
||
- validar no servidor;
|
||
- possuir loading state;
|
||
- desabilitar botão durante submissão;
|
||
- preservar acessibilidade;
|
||
- limpar dados após sucesso;
|
||
- evitar exposição de exceção;
|
||
- suportar teste de submissão sem navegador (feature test);
|
||
- suportar jornada E2E em navegador real.
|
||
|
||
### 11.3 JavaScript
|
||
|
||
- manter o site público em JS vanilla progressivo;
|
||
- não introduzir framework SPA;
|
||
- não usar dependência JS quando CSS e HTML resolverem;
|
||
- toda interação crítica deve funcionar sem estado global complexo;
|
||
- Alpine só entra junto com Livewire, se o gatilho de §22 disparar.
|
||
|
||
---
|
||
|
||
## 12. Segurança e privacidade
|
||
|
||
### 12.1 Autenticação
|
||
|
||
- login interno;
|
||
- e-mail verificado;
|
||
- reset seguro;
|
||
- senhas tratadas pelo Laravel;
|
||
- cookie `secure`, `httpOnly` e `sameSite` em produção;
|
||
- usuário inativo bloqueado;
|
||
- acesso Filament validado por `canAccessPanel` ou mecanismo equivalente.
|
||
|
||
2FA pode ser adicionado apenas se suportado de forma simples pelo stack escolhido. Não bloquear o MVP por isso.
|
||
|
||
### 12.2 Autorização
|
||
|
||
Policies obrigatórias para entidades internas.
|
||
|
||
Admin:
|
||
|
||
- acesso total.
|
||
|
||
Assistant:
|
||
|
||
- operação cotidiana;
|
||
- sem usuários, configurações globais e auditoria;
|
||
- exclusões críticas restritas.
|
||
|
||
### 12.3 Formulário público
|
||
|
||
- CSRF;
|
||
- rate limit;
|
||
- honeypot;
|
||
- validação server-side;
|
||
- limite de tamanho de campos;
|
||
- normalização de e-mail e telefone;
|
||
- proteção contra mass assignment;
|
||
- logs sem conteúdo integral da mensagem do visitante.
|
||
|
||
### 12.4 Uploads
|
||
|
||
- allowlist de MIME e extensão;
|
||
- limite de tamanho;
|
||
- path aleatório;
|
||
- arquivos privados por padrão;
|
||
- imagens públicas com validação e alt text;
|
||
- não confiar no nome original;
|
||
- download autorizado por Policy;
|
||
- proteção contra path traversal.
|
||
|
||
### 12.5 LGPD
|
||
|
||
- coletar apenas dados necessários;
|
||
- exibir aviso de privacidade no briefing;
|
||
- registrar aceite do aviso;
|
||
- não tratar consentimento como base universal;
|
||
- definir rotina administrativa para correção e exclusão quando cabível;
|
||
- evitar dados sensíveis no MVP;
|
||
- não registrar dados pessoais desnecessários em logs ou auditoria;
|
||
- documentar retenção de leads perdidos antes do lançamento.
|
||
|
||
### 12.6 Dependências
|
||
|
||
- `composer.lock` e lock de frontend versionados;
|
||
- `composer audit` no CI;
|
||
- `npm audit` conforme política definida no projeto;
|
||
- nenhuma dependência abandonada ou sem uso;
|
||
- justificar dependências grandes em PR/ADR.
|
||
|
||
---
|
||
|
||
## 13. Estratégia de testes
|
||
|
||
### 13.1 Objetivo
|
||
|
||
A pirâmide deve possuir:
|
||
|
||
1. muitos testes unitários rápidos;
|
||
2. testes feature/integration suficientes para Laravel, PostgreSQL, Livewire e Filament;
|
||
3. poucos testes E2E cobrindo jornadas críticas;
|
||
4. testes E2E, de acessibilidade, motion e smoke nas jornadas públicas mais importantes.
|
||
|
||
A distribuição é por intenção, não por percentual rígido.
|
||
|
||
### 13.2 Unit tests
|
||
|
||
Não inicializam Laravel nem banco quando possível.
|
||
|
||
Cobertura obrigatória:
|
||
|
||
- conversão e formatação de dinheiro;
|
||
- rejeição de valores negativos;
|
||
- status derivado de pagamento;
|
||
- cálculo de totais financeiros;
|
||
- transições de lead;
|
||
- invariantes de conversão;
|
||
- conclusão e reabertura de tarefa;
|
||
- cálculo de vencimento;
|
||
- regras de publicação quando extraídas para domínio.
|
||
|
||
Exemplo esperado:
|
||
|
||
```php
|
||
it('marks an unpaid past-due payment as overdue', function () {
|
||
$payment = PaymentSchedule::from(
|
||
amount: new Money(150000),
|
||
dueDate: new DateTimeImmutable('2026-07-01'),
|
||
paidAt: null,
|
||
);
|
||
|
||
expect($payment->statusAt(new DateTimeImmutable('2026-07-27')))
|
||
->toBe(PaymentStatus::Overdue);
|
||
});
|
||
```
|
||
|
||
### 13.3 Feature e integration tests
|
||
|
||
Devem inicializar Laravel e usar PostgreSQL real no CI.
|
||
|
||
Cobertura obrigatória:
|
||
|
||
- migrations e constraints;
|
||
- Actions;
|
||
- Policies;
|
||
- conversão transacional e idempotente;
|
||
- criação de checklist;
|
||
- validação do briefing;
|
||
- notificações;
|
||
- upload e autorização de download;
|
||
- queries financeiras;
|
||
- filtros principais;
|
||
- Resources e Actions Filament;
|
||
- componentes Livewire;
|
||
- publicação e despublicação de conteúdo;
|
||
- autenticação e papéis.
|
||
|
||
Usar `Mail::fake`, `Notification::fake`, `Storage::fake` e `Queue::fake` quando o objetivo não for testar a integração externa.
|
||
|
||
### 13.4 E2E browser
|
||
|
||
Executar com Chromium headless contra a aplicação servida por FrankenPHP.
|
||
|
||
Jornadas obrigatórias:
|
||
|
||
| ID | Jornada | Resultado esperado |
|
||
|---|---|---|
|
||
| E2E-01 | Visitante envia briefing | Lead criado, sucesso visível, sem erro de console |
|
||
| E2E-02 | Assessora autentica e trata lead | Login, filtro, status e próxima ação funcionam |
|
||
| E2E-03 | Assessora converte lead | Evento e checklist criados uma única vez |
|
||
| E2E-04 | Assessora registra orçamento e pagamento | Totais atualizados e pagamento visível |
|
||
| E2E-05 | Assessora publica caso | Conteúdo aparece na rota pública |
|
||
|
||
Não duplicar todas as combinações no browser. Casos de borda ficam em unit/feature.
|
||
|
||
Em falha, CI deve publicar:
|
||
|
||
- screenshot;
|
||
- trace ou vídeo quando suportado;
|
||
- logs da aplicação;
|
||
- logs do browser;
|
||
- HTML report quando disponível.
|
||
|
||
### 13.5 Testes de arquitetura
|
||
|
||
Criar regras Pest Architecture:
|
||
|
||
```php
|
||
arch()
|
||
->expect('App\\Domain')
|
||
->toUseStrictTypes()
|
||
->not->toUse(['dd', 'dump', 'die']);
|
||
|
||
arch()
|
||
->expect('App\\Domain')
|
||
->not->toDependOn(['App\\Filament', 'App\\Livewire']);
|
||
```
|
||
|
||
Adicionar regras conforme a estrutura real, sem tornar a suíte excessivamente frágil.
|
||
|
||
### 13.6 Cobertura
|
||
|
||
- meta de 80% para `Domain` e `Application`;
|
||
- não medir views, migrations, código gerado e framework;
|
||
- cobertura não substitui critérios de aceite;
|
||
- queda de cobertura em módulo crítico deve bloquear merge.
|
||
|
||
### 13.7 Acessibilidade automatizada
|
||
|
||
Smoke/browser tests devem verificar:
|
||
|
||
- ausência de issues críticas ou sérias;
|
||
- foco inicial coerente quando modal abrir;
|
||
- labels de formulário;
|
||
- landmarks e headings básicos;
|
||
- ausência de erro de console.
|
||
|
||
Revisão manual mínima antes do lançamento:
|
||
|
||
- navegação por teclado;
|
||
- foco visível;
|
||
- contraste;
|
||
- zoom a 200%;
|
||
- leitor de tela nos fluxos de briefing e login.
|
||
|
||
### 13.8 Comandos padronizados
|
||
|
||
O projeto deve expor scripts equivalentes:
|
||
|
||
```bash
|
||
composer test:unit
|
||
composer test:feature
|
||
composer test:browser
|
||
composer test
|
||
composer quality
|
||
```
|
||
|
||
Composição esperada:
|
||
|
||
```text
|
||
test:unit → Unit + Architecture
|
||
test:feature → Feature + Livewire + Filament
|
||
test:browser → E2E + acessibilidade + motion + smoke
|
||
quality → Pint check + PHPStan/Larastan + audits + testes
|
||
```
|
||
|
||
---
|
||
|
||
## 14. CI/CD
|
||
|
||
### 14.1 Gates de pull request
|
||
|
||
| Job | Responsabilidade | Bloqueia merge |
|
||
|---|---|---:|
|
||
| `static` | Pint, PHPStan/Larastan, Composer validate, Composer e npm audit | Sim |
|
||
| `unit` | Unitários, arquitetura e cobertura | Sim |
|
||
| `feature` | PostgreSQL, migrations, Livewire, Filament e integração | Sim |
|
||
| `browser` | Vite, FrankenPHP, E2E, smoke, acessibilidade e motion | Sim |
|
||
| `container` | Build da imagem final e healthcheck | Sim |
|
||
|
||
### 14.2 Regras do pipeline
|
||
|
||
- todo PR executa todos os gates;
|
||
- usar cache de Composer, npm e browser de forma segura;
|
||
- banco de integração deve ser PostgreSQL;
|
||
- CI não deve usar SQLite como substituto;
|
||
- browser tests usam o mesmo artefato ou imagem próxima da produção;
|
||
- falhas devem publicar artefatos diagnósticos;
|
||
- migrations devem ser executadas em banco vazio;
|
||
- seed de demonstração deve ser executável;
|
||
- secrets nunca ficam no repositório ou na imagem.
|
||
|
||
### 14.3 Branches e ambientes
|
||
|
||
- PR: testes e preview opcional;
|
||
- `main`: build imutável por SHA publicado no registry Gitea (`git.hellomanoel.com`) e deploy automático em staging via Dokploy;
|
||
- staging: Dokploy Compose executa migração, healthcheck `/up` e smoke pós-deploy (`/up`, `/`, `/admin/login`);
|
||
- produção: promoção da mesma imagem aprovada, sem rebuild (retag do digest em `:production`);
|
||
- produção requer aprovação humana explícita no MVP (`workflow_dispatch` com confirmação);
|
||
- rollback usa imagem anterior (SHA anterior, sem rebuild);
|
||
- migrations devem ser backward-compatible quando possível.
|
||
|
||
### 14.4 Definition of Done
|
||
|
||
Uma história só está concluída quando:
|
||
|
||
- critérios de aceite estão atendidos;
|
||
- código passa por Pint;
|
||
- PHPStan/Larastan está verde;
|
||
- unit e feature tests cobrem regras relevantes;
|
||
- E2E foi criado ou atualizado para fluxo crítico;
|
||
- acessibilidade automatizada não possui issues críticas/sérias;
|
||
- migration foi testada;
|
||
- autorização foi verificada;
|
||
- logs não expõem PII desnecessária;
|
||
- factories e seed foram atualizados;
|
||
- staging e smoke pós-deploy estão verdes;
|
||
- documentação afetada foi atualizada.
|
||
|
||
---
|
||
|
||
## 15. Deploy com FrankenPHP
|
||
|
||
### 15.1 Decisão
|
||
|
||
Usar FrankenPHP em modo regular no MVP.
|
||
|
||
**NÃO ativar worker mode inicialmente.**
|
||
|
||
Worker mode somente poderá ser ativado após:
|
||
|
||
- profiling demonstrar benefício relevante;
|
||
- testes de persistência de estado;
|
||
- testes de memória;
|
||
- suíte de regressão verde;
|
||
- ADR aprovada.
|
||
|
||
### 15.2 Processos
|
||
|
||
| Processo | Imagem | Comando/função |
|
||
|---|---|---|
|
||
| web | imagem da aplicação | FrankenPHP/Caddy servindo `public/` |
|
||
| queue | mesma imagem | `php artisan queue:work --sleep=2 --tries=3` |
|
||
| scheduler | mesma imagem | `php artisan schedule:work` ou cron `schedule:run` |
|
||
| database | serviço gerenciado | PostgreSQL com backup |
|
||
| files | serviço gerenciado | bucket S3-compatible |
|
||
|
||
### 15.3 Dockerfile
|
||
|
||
Usar multi-stage build:
|
||
|
||
1. Composer dependencies;
|
||
2. frontend assets;
|
||
3. runtime FrankenPHP.
|
||
|
||
Requisitos:
|
||
|
||
- versão PHP fixada;
|
||
- extensões PHP explícitas;
|
||
- `composer install --no-dev --prefer-dist --no-interaction --classmap-authoritative` em produção;
|
||
- `npm ci`;
|
||
- build Vite;
|
||
- usuário não-root quando suportado;
|
||
- nenhum secret em layer;
|
||
- healthcheck;
|
||
- imagem reproduzível;
|
||
- caches Laravel criados no entrypoint/deploy quando dependem do ambiente.
|
||
|
||
Não executar `php artisan config:cache` em uma stage que não possui as variáveis finais quando isso congelar configuração incorreta.
|
||
|
||
### 15.4 Variáveis de ambiente mínimas
|
||
|
||
```text
|
||
APP_NAME
|
||
APP_ENV
|
||
APP_KEY
|
||
APP_DEBUG=false
|
||
APP_URL
|
||
APP_LOCALE=pt_BR
|
||
APP_FALLBACK_LOCALE=pt_BR
|
||
APP_TIMEZONE=America/Sao_Paulo
|
||
|
||
DB_CONNECTION=pgsql
|
||
DB_HOST
|
||
DB_PORT
|
||
DB_DATABASE
|
||
DB_USERNAME
|
||
DB_PASSWORD
|
||
|
||
CACHE_STORE=database ou file conforme ambiente
|
||
QUEUE_CONNECTION=database
|
||
SESSION_DRIVER=database ou cookie conforme decisão
|
||
|
||
FILESYSTEM_DISK=r2 em produção
|
||
R2_ACCESS_KEY_ID
|
||
R2_SECRET_ACCESS_KEY
|
||
R2_BUCKET
|
||
R2_ENDPOINT
|
||
R2_URL (domínio próprio opcional)
|
||
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_DEFAULT_REGION, AWS_BUCKET, AWS_ENDPOINT, AWS_USE_PATH_STYLE_ENDPOINT — opcionais, apenas se o disk s3 for usado
|
||
|
||
MAIL_MAILER=resend em produção (mailer nativo Laravel)
|
||
RESEND_API_KEY
|
||
MAIL_FROM_ADDRESS
|
||
MAIL_FROM_NAME
|
||
```
|
||
|
||
### 15.5 Healthcheck
|
||
|
||
`GET /up` deve:
|
||
|
||
- responder rapidamente;
|
||
- não exigir autenticação;
|
||
- não expor segredo;
|
||
- retornar falha quando a aplicação não inicializar;
|
||
- opcionalmente validar conexão simples com banco em check separado, sem tornar o endpoint caro.
|
||
|
||
---
|
||
|
||
## 16. Observabilidade, backup e operação
|
||
|
||
### 16.1 Logs
|
||
|
||
Logs estruturados devem incluir quando disponível:
|
||
|
||
- request ID;
|
||
- user ID;
|
||
- rota;
|
||
- ação;
|
||
- tipo e ID de entidade;
|
||
- duração;
|
||
- nível e exceção.
|
||
|
||
Não registrar:
|
||
|
||
- senha;
|
||
- token;
|
||
- conteúdo integral de comprovante;
|
||
- conteúdo integral de mensagem pessoal;
|
||
- dados pessoais sem necessidade.
|
||
|
||
### 16.2 Monitoramento mínimo
|
||
|
||
- disponibilidade da home;
|
||
- disponibilidade do briefing;
|
||
- disponibilidade do login;
|
||
- erros não tratados;
|
||
- falhas repetidas de queue;
|
||
- falha de backup;
|
||
- smoke pós-deploy.
|
||
|
||
Não construir stack própria de observabilidade distribuída no MVP.
|
||
|
||
### 16.3 Backup
|
||
|
||
| Item | Política inicial |
|
||
|---|---|
|
||
| PostgreSQL | backup diário automático |
|
||
| Retenção | mínimo 14 dias |
|
||
| Arquivos | versionamento ou lifecycle do bucket |
|
||
| RPO | até 24 horas |
|
||
| RTO | até 4 horas |
|
||
| Teste de restauração | trimestral |
|
||
|
||
O procedimento de restauração deve ser documentado antes da produção.
|
||
|
||
---
|
||
|
||
## 17. Seeds, factories e dados determinísticos
|
||
|
||
### 17.1 Factory obrigatória
|
||
|
||
Criar factories para:
|
||
|
||
- User;
|
||
- Lead;
|
||
- LeadActivity;
|
||
- Event;
|
||
- EventTask;
|
||
- Vendor;
|
||
- EventVendor;
|
||
- BudgetItem;
|
||
- Payment;
|
||
- Service;
|
||
- PortfolioCase;
|
||
- Testimonial;
|
||
- Document, quando viável sem arquivo real.
|
||
|
||
### 17.2 Seed de demonstração
|
||
|
||
O seed deve criar:
|
||
|
||
- um admin;
|
||
- um assistant;
|
||
- configurações do site;
|
||
- 3 serviços;
|
||
- 3 casos de portfólio;
|
||
- depoimentos reais: os 5 casais de `depoimentos.md` (Jeniffer e Maick, Quesia e Jhonata, Milena e Weslley, Raquel e Pedro, Victoria e Pedro), preservando texto e datas; autores fictícios de demonstração removidos; em produção permanecem não publicados até autorização explícita de publicação;
|
||
- leads em estados variados;
|
||
- 2 eventos futuros e 1 concluído;
|
||
- tarefas vencidas e futuras;
|
||
- fornecedores;
|
||
- itens de orçamento;
|
||
- pagamentos pagos, pendentes e vencidos.
|
||
|
||
As credenciais de desenvolvimento devem ser documentadas apenas em ambiente local e nunca usadas em produção.
|
||
|
||
### 17.3 Determinismo
|
||
|
||
Para visual e browser tests:
|
||
|
||
- usar datas fixas;
|
||
- usar imagens fixture versionadas ou placeholders estáveis;
|
||
- não depender de rede externa;
|
||
- não usar conteúdo randômico não seedado;
|
||
- congelar horário.
|
||
|
||
---
|
||
|
||
## 18. Backlog de implementação
|
||
|
||
> **Recorte vigente (ADR-016).** Fases 0 e 1 são o escopo do lançamento e estão concluídas. **Fases 2 a 5 estão adiadas, sem data.** Não iniciar nenhuma delas sem uma decisão nova do responsável pelo produto — a §1.1 manda trabalhar uma fase por vez, e a fase corrente é o acabamento e a publicação do site.
|
||
|
||
O agente deve implementar na sequência, salvo instrução explícita.
|
||
|
||
### Fase 0 — Fundação
|
||
|
||
**Entregas:**
|
||
|
||
- [x] projeto Laravel;
|
||
- [x] PostgreSQL local e CI;
|
||
- [x] Filament instalado e autenticado;
|
||
- [x] Livewire configurado;
|
||
- [x] Tailwind/Vite;
|
||
- [x] FrankenPHP e Docker Compose local;
|
||
- [x] papéis admin/assistant;
|
||
- [x] Pint;
|
||
- [x] PHPStan/Larastan;
|
||
- [x] Pest;
|
||
- [x] Pest Browser;
|
||
- [x] Architecture tests;
|
||
- [x] pipeline CI inicial;
|
||
- [x] design tokens mínimos;
|
||
- [x] healthcheck;
|
||
- [x] seed de admin local.
|
||
- [x] verificação de e-mail e reset seguro (MustVerifyEmail);
|
||
- [x] npm audit no `composer quality` e no job `static`;
|
||
- [x] gate de cobertura `Domain`/`Application` ≥ 80%;
|
||
- [x] serviço de aplicação FrankenPHP no Compose local;
|
||
- [x] hello-world implantado em staging (critério de saída) — run [31395107465](https://github.com/manoel-freitas/amore-site/actions/runs/31395107465) em `7e68c0e`, com `Dokploy deployment succeeded` e smoke verde em `/up`, `/` e `/admin/login`.
|
||
|
||
> Os itens pendentes acima são tratados pela mudança OpenSpec `complete-foundation-parity`; o critério de saída da fase só é atingido com staging implantado.
|
||
|
||
**Critério de saída:** pipeline verde e hello-world implantado em staging.
|
||
|
||
### Fase 1 — Site e CMS
|
||
|
||
- [x] `site_settings`;
|
||
- [x] serviços;
|
||
- [x] portfólio e galeria;
|
||
- [x] depoimentos;
|
||
- [x] home;
|
||
- [x] listagem e detalhe de serviços;
|
||
- [x] listagem e detalhe de portfólio;
|
||
- [x] sobre;
|
||
- [x] privacidade;
|
||
- [x] SEO;
|
||
- [x] mídia otimizada;
|
||
- [x] testes de acessibilidade.
|
||
|
||
**Critério de saída:** conteúdo gerenciável no Filament, testes browser funcionais, de acessibilidade e motion aprovados, e revisão humana do site público no PR.
|
||
|
||
### Fase 2 — Leads — ADIADA (ADR-016)
|
||
|
||
- [ ] migration/model/factory de Lead;
|
||
- [ ] LeadActivity;
|
||
- [ ] briefing Livewire;
|
||
- [ ] proteção contra abuso;
|
||
- [ ] notificação de novo lead;
|
||
- [ ] LeadResource;
|
||
- [ ] filtros;
|
||
- [ ] próxima ação;
|
||
- [ ] histórico;
|
||
- [ ] perda e reabertura;
|
||
- [ ] testes unit/feature/browser;
|
||
- [ ] E2E-01 e E2E-02.
|
||
|
||
**Critério de saída:** jornada visitante → lead → tratamento interna totalmente verde.
|
||
|
||
### Fase 3 — Eventos e tarefas — ADIADA (ADR-016)
|
||
|
||
- [ ] Event;
|
||
- [ ] EventTask;
|
||
- [ ] conversão idempotente;
|
||
- [ ] checklist padrão;
|
||
- [ ] EventResource;
|
||
- [ ] detalhe customizado;
|
||
- [ ] tarefas por evento;
|
||
- [ ] visão global de tarefas;
|
||
- [ ] dashboard operacional inicial;
|
||
- [ ] E2E-03.
|
||
|
||
**Critério de saída:** lead é convertido e evento pode ser administrado.
|
||
|
||
### Fase 4 — Fornecedores e financeiro — ADIADA (ADR-016)
|
||
|
||
- [ ] Vendor;
|
||
- [ ] EventVendor;
|
||
- [ ] BudgetItem;
|
||
- [ ] Payment;
|
||
- [ ] Money/conversão para centavos;
|
||
- [ ] resumo financeiro;
|
||
- [ ] pagamentos pagos, pendentes e vencidos;
|
||
- [ ] comprovante opcional;
|
||
- [ ] auditoria financeira;
|
||
- [ ] widgets financeiros;
|
||
- [ ] E2E-04.
|
||
|
||
**Critério de saída:** assessora acompanha orçamento e pagamentos sem integração bancária.
|
||
|
||
### Fase 5 — Documentos, hardening e lançamento — ADIADA (ADR-016)
|
||
|
||
- [ ] documentos privados;
|
||
- [ ] auditoria completa;
|
||
- [ ] scheduler de resumo;
|
||
- [ ] performance e índices;
|
||
- [ ] revisão N+1;
|
||
- [ ] revisão de Policies;
|
||
- [ ] revisão LGPD;
|
||
- [ ] backup e restore documentados;
|
||
- [ ] monitoramento;
|
||
- [ ] smoke pós-deploy;
|
||
- [ ] revisão manual de acessibilidade;
|
||
- [ ] E2E-05;
|
||
- [ ] seed de demonstração completo;
|
||
- [ ] documentação operacional.
|
||
|
||
**Critério de saída:** Definition of Done integral e produção aprovada.
|
||
|
||
---
|
||
|
||
## 19. Critérios de aceite transversais
|
||
|
||
Toda tela interna deve:
|
||
|
||
- exigir autenticação;
|
||
- aplicar Policy;
|
||
- estar em pt-BR;
|
||
- exibir estados vazios úteis;
|
||
- possuir validação server-side;
|
||
- indicar sucesso e falha;
|
||
- ser navegável por teclado;
|
||
- não executar N+1 conhecido;
|
||
- paginar listas de crescimento aberto;
|
||
- possuir teste proporcional ao risco.
|
||
|
||
Toda rota pública deve:
|
||
|
||
- funcionar sem autenticação;
|
||
- não expor conteúdo não publicado;
|
||
- possuir title e description;
|
||
- usar layout responsivo;
|
||
- não emitir erro de console;
|
||
- respeitar acessibilidade básica;
|
||
- ter comportamento de erro seguro.
|
||
|
||
Toda operação financeira deve:
|
||
|
||
- usar centavos inteiros;
|
||
- ser validada;
|
||
- ser autorizada;
|
||
- gerar auditoria quando crítica;
|
||
- possuir testes unitários e feature.
|
||
|
||
---
|
||
|
||
## 20. Riscos e mitigação
|
||
|
||
| Risco | Mitigação obrigatória |
|
||
|---|---|
|
||
| Filament concentrar domínio | Resources finos, Actions e Queries testáveis |
|
||
| Site parecer genérico | design tokens, conteúdo real, fotografia e revisão humana |
|
||
| Escopo crescer | lista de não objetivos e mudança somente com hipótese real |
|
||
| Financeiro virar contabilidade | limitar a orçamento e pagamentos manuais |
|
||
| Worker mode vazar estado | manter modo regular até benchmark e ADR |
|
||
| Upload degradar performance | limites, variantes, storage externo |
|
||
| SQLite esconder diferenças | PostgreSQL em integração e CI |
|
||
| Regras escondidas em callbacks | Actions explícitas e testes de caso de uso |
|
||
| Dashboard ficar lento | queries independentes, índices e agregações pequenas |
|
||
|
||
---
|
||
|
||
## 21. ADRs iniciais
|
||
|
||
| ADR | Decisão | Status |
|
||
|---|---|---|
|
||
| ADR-001 | Monólito modular Laravel, sem microserviços | Aceita |
|
||
| ADR-002 | Filament para área interna (Livewire é dependência interna do Filament); site público em Blade — ver ADR-015 | Aceita |
|
||
| ADR-003 | PostgreSQL como único banco transacional | Aceita |
|
||
| ADR-004 | Pagamentos somente manuais | Aceita |
|
||
| ADR-005 | Pest unifica unit, feature e browser | Aceita |
|
||
| ADR-006 | FrankenPHP regular mode; worker mode adiado | Aceita |
|
||
| ADR-007 | Single-tenant; SaaS e portal do cliente adiados | Aceita |
|
||
| ADR-008 | Database queue; Redis adiado | Aceita |
|
||
| ADR-009 | Dinheiro em BRL armazenado como centavos inteiros | Aceita |
|
||
| ADR-010 | Home com estrutura fixa e CMS tipado, sem page builder | Aceita |
|
||
| ADR-011 | Cloudflare R2 (S3-compatible) como storage de objetos em produção | Aceita |
|
||
| ADR-012 | E-mail transacional via Resend (mailer nativo Laravel) | Aceita |
|
||
| ADR-013 | Design system Heritage Editorial para o site público | Aceita |
|
||
| ADR-014 | Deploy via Dokploy Compose com imagem imutável por SHA no registry Gitea | Aceita |
|
||
| ADR-015 | Site público permanece Blade + JS vanilla; Livewire e Alpine ficam restritos ao Filament até o gatilho de §22. Emenda o texto da ADR-002 | Aceita |
|
||
| ADR-016 | Lançamento de 31/08/2026 entrega apenas o site institucional (Fases 0–1); Fases 2–5 seguem especificadas e adiadas, sem data | Aceita |
|
||
|
||
---
|
||
|
||
## 22. Gatilhos objetivos para evolução
|
||
|
||
| Evolução | Gatilho |
|
||
|---|---|
|
||
| Portal do cliente | clientes pedem repetidamente visibilidade e aprovação fora do WhatsApp |
|
||
| RSVP | volume relevante de eventos exige gestão de convidados |
|
||
| Pagamento online | cobrança manual causa esforço ou inadimplência mensurável |
|
||
| Multi-tenancy | uma segunda assessoria aceita pagar e usar o produto |
|
||
| Redis | database queue não atende volume ou confiabilidade |
|
||
| FrankenPHP worker | benchmark prova ganho e testes de estado/memória passam |
|
||
| API pública | existe consumidor real e contrato de integração |
|
||
| Kanban | tabela de leads demonstra limitação frequente observada |
|
||
| Editor de checklist | diferentes tipos de evento exigem manutenção frequente do seed |
|
||
| Livewire no site público | portfólio ou serviços exigem consulta ou filtro dinâmico que Blade + JS vanilla não resolvem de forma simples |
|
||
|
||
---
|
||
|
||
## 23. Condições de conclusão do MVP
|
||
|
||
O MVP está concluído somente quando:
|
||
|
||
A ADR-016 divide estas condições em dois recortes. Cada um se fecha por conta própria; o segundo não bloqueia o lançamento.
|
||
|
||
### 23.1 Lançamento do site (Fases 0–1)
|
||
|
||
O lançamento está concluído somente quando:
|
||
|
||
- site público está publicado e visualmente aprovado;
|
||
- assessora edita os conteúdos essenciais sem desenvolvedor;
|
||
- briefing envia o pedido de proposta de forma segura, com proteção contra abuso e aceite de privacidade registrado;
|
||
- usuários e Policies estão corretos;
|
||
- testes unit, feature, browser funcionais e arquitetura estão verdes;
|
||
- CI bloqueia regressões;
|
||
- imagem FrankenPHP é reproduzível;
|
||
- staging e produção usam a mesma imagem promovida;
|
||
- backup, restauração e monitoramento estão documentados;
|
||
- nenhum item explicitamente fora do MVP (§4.2) foi introduzido.
|
||
|
||
Note a diferença em relação à versão anterior desta seção: o critério do briefing é **enviar o pedido**, não "criar leads". Criar Lead é Fase 2 e está adiado; o formulário atual envia e-mail e não persiste nada.
|
||
|
||
### 23.2 Produto completo (Fases 2–5, adiado)
|
||
|
||
Além de tudo em §23.1:
|
||
|
||
- pipeline e próxima ação funcionam;
|
||
- briefing cria leads de forma segura e persistente;
|
||
- lead é convertido uma única vez em evento;
|
||
- checklist é criado automaticamente;
|
||
- evento possui visão consolidada;
|
||
- fornecedores podem ser cadastrados e vinculados;
|
||
- orçamento e pagamentos manuais possuem totais consistentes;
|
||
- dashboard mostra pendências do dia;
|
||
- auditoria registra operações críticas;
|
||
- jornadas E2E das fases correspondentes estão verdes.
|
||
|
||
---
|
||
|
||
## 24. Formato esperado de reporte do agente
|
||
|
||
Ao finalizar uma tarefa, o agente deve responder no formato:
|
||
|
||
```text
|
||
Requisito implementado: <ID e nome>
|
||
|
||
Alterações:
|
||
- <arquivo ou módulo>
|
||
- <arquivo ou módulo>
|
||
|
||
Testes adicionados/atualizados:
|
||
- <teste>
|
||
- <teste>
|
||
|
||
Comandos executados:
|
||
- <comando e resultado>
|
||
|
||
Critérios de aceite:
|
||
- [x] <critério>
|
||
- [x] <critério>
|
||
|
||
Pendências ou desvios:
|
||
- Nenhum
|
||
```
|
||
|
||
Caso exista desvio:
|
||
|
||
```text
|
||
Desvio da SPEC:
|
||
- Decisão afetada:
|
||
- Motivo:
|
||
- Alternativa escolhida:
|
||
- Impacto:
|
||
- ADR criada/atualizada:
|
||
```
|
||
|
||
---
|
||
|
||
## 25. Primeira instrução recomendada ao agente
|
||
|
||
```text
|
||
Leia integralmente o SPEC.md e inspecione o repositório. Implemente apenas a Fase 0 — Fundação. Antes de alterar arquivos, apresente um plano curto relacionando cada mudança aos requisitos da fase. Não avance para a Fase 1. Ao terminar, execute todos os gates disponíveis, reporte os resultados e marque somente os itens comprovadamente concluídos.
|
||
```
|