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>
98 lines
4.5 KiB
Markdown
98 lines
4.5 KiB
Markdown
## 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`.
|