feat: implement CMS for content management (WEB-02/03/04/06)
Introduces Filament management for site settings, services, portfolio cases, and testimonials with admin-only policies, validated media uploads, and deterministic seeding. Includes comprehensive design and task documentation for the implementation. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
## Context
|
||||
|
||||
Fase 0 entregou Laravel 13, Filament 5 em `/admin`, design tokens, autenticação interna e pipeline CI. O repositório possui apenas home placeholder em [routes/web.php](../../routes/web.php). Esta change implementa a **metade CMS** da Fase 1 (SPEC §18): persistência e gestão Filament de conteúdo, sem rotas públicas.
|
||||
|
||||
Padrões existentes a seguir:
|
||||
|
||||
- Resources Filament com form/table separados ([app/Filament/Resources/Users/](../../app/Filament/Resources/Users/))
|
||||
- Policies admin-only ([app/Policies/UserPolicy.php](../../app/Policies/UserPolicy.php))
|
||||
- `declare(strict_types=1);` em todo PHP próprio
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Assessora admin gerencia settings, serviços, portfólio e depoimentos no Filament.
|
||||
- Dados persistidos conforme SPEC §8.2, prontos para consumo pela change `build-public-site`.
|
||||
- Feature tests locais em PostgreSQL (jsonb, constraints reais).
|
||||
- Seed determinístico parcial para desenvolvimento e testes futuros.
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- Rotas públicas, home, SEO, sitemap, snapshots visuais, briefing.
|
||||
- Actions de publicação com auditoria (`PublishPortfolioCase` etc.) — adiadas até Fase 5.
|
||||
- Variantes responsivas de imagem, page builder, categorias dinâmicas.
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. Publicação via `published_at` único
|
||||
|
||||
**Decisão:** `published_at` nullable timestamp é a única fonte de verdade. Scope `published()` no model (`whereNotNull('published_at')`). Sem coluna `is_published`.
|
||||
|
||||
**Alternativa rejeitada:** boolean `is_published` + `published_at` — viola SPEC §1.1 (não persistir status derivável).
|
||||
|
||||
### 2. Mídia em disco `public` local, S3 em produção
|
||||
|
||||
**Decisão:** uploads de capa/galeria/foto usam disco Laravel configurável via `FILESYSTEM_DISK`. Local: `public` + `php artisan storage:link`. Produção: `s3`. Path gerado com UUID/hash, nunca nome original.
|
||||
|
||||
**Alternativa rejeitada:** blobs no PostgreSQL — proibido por SPEC §6.4.
|
||||
|
||||
### 3. Validação de mídia centralizada
|
||||
|
||||
**Decisão:** classe em `app/Support/` (ex.: `PublicImageUploadRules`) com allowlist MIME (`image/jpeg`, `image/png`, `image/webp`), extensões correspondentes, limite 10 MB, alt text obrigatório quando imagem presente. Reutilizada nos Resources Filament.
|
||||
|
||||
### 4. Sem Actions de domínio nesta fase
|
||||
|
||||
**Decisão:** Filament Resources salvam diretamente via Eloquent. Actions (`PublishPortfolioCase`, `UnpublishPortfolioCase`) só serão criadas quando auditoria (Fase 5) exigir efeito colateral transacional.
|
||||
|
||||
**Rationale:** SPEC §9.5 — não criar Action para CRUD sem regra adicional.
|
||||
|
||||
### 5. Auditoria de publicação adiada
|
||||
|
||||
**Decisão:** publicar/despublicar **não** grava `audit_logs` nesta change.
|
||||
|
||||
**Desvio documentado:** ADM-02 lista publicação como auditável, mas tabela `audit_logs` é Fase 5. Retomar em change de hardening.
|
||||
|
||||
### 6. Autorização admin-only para CMS
|
||||
|
||||
**Decisão:** Policies espelham `UserPolicy` — `$user->isAdmin()` para view/create/update/delete em conteúdo do site. Assistant recebe 403 em todos os Resources de conteúdo.
|
||||
|
||||
**Referência:** WEB-02 — "admin pode gerenciar; assistant não pode".
|
||||
|
||||
### 7. Site settings como singleton Filament Page
|
||||
|
||||
**Decisão:** model `SiteSetting` com método `instance()` (primeiro registro ou create default). Filament `ManageSiteSettings` page (não Resource de lista) — evita CRUD genérico de chave/valor (SPEC §WEB-06).
|
||||
|
||||
### 8. Slug automático a partir do título
|
||||
|
||||
**Decisão:** slug gerado no model (observer ou mutator) com `Str::slug()`, unique constraint no banco. Admin pode editar slug no form.
|
||||
|
||||
### 9. Navegação Filament
|
||||
|
||||
**Decisão:** grupo `Conteúdo do site` com sort order conforme SPEC §5.2:
|
||||
|
||||
1. Configurações (singleton page)
|
||||
2. Serviços
|
||||
3. Portfólio
|
||||
4. Depoimentos
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
| Risco | Mitigação |
|
||||
|---|---|
|
||||
| Uploads locais sem S3 em dev | `Storage::fake()` nos testes; documentar `storage:link` no README |
|
||||
| Galeria ordenada complexa no Filament | Relation manager com `sort_order` e reorderable |
|
||||
| jsonb `social_links` difere SQLite | phpunit.xml já migrado para PostgreSQL |
|
||||
| Conteúdo criado sem rotas públicas | seed + testes Filament validam CMS; rotas vêm em `build-public-site` |
|
||||
| Auditoria ausente em publicação | desvio explícito; retomar Fase 5 |
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. Rodar migrations em ordem: `site_settings` → `services` → `portfolio_cases` + `portfolio_images` → `testimonials`.
|
||||
2. Executar seed de conteúdo após migrations.
|
||||
3. Sem rollback de dados em produção (greenfield); `migrate:rollback` suportado em dev.
|
||||
|
||||
## Open Questions
|
||||
|
||||
- Nenhuma bloqueante. Fontes tipográficas finais da home ficam para `build-public-site`.
|
||||
Reference in New Issue
Block a user