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,32 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Public content images are validated and stored securely
|
||||
|
||||
The system SHALL validate public content uploads (service covers, portfolio covers/gallery, testimonial photos, site OG image) per SPEC §6.4 and §12.4. Validation MUST enforce MIME allowlist (jpeg, png, webp), matching extensions, maximum size of 10 MB, and mandatory alt text when an image is uploaded.
|
||||
|
||||
#### Scenario: Invalid MIME is rejected
|
||||
|
||||
- **WHEN** an admin uploads a file with disallowed MIME type
|
||||
- **THEN** validation MUST fail with a pt-BR error message
|
||||
|
||||
#### Scenario: Oversized file is rejected
|
||||
|
||||
- **WHEN** an admin uploads an image exceeding 10 MB
|
||||
- **THEN** validation MUST fail
|
||||
|
||||
#### Scenario: Storage path is not derived from original filename
|
||||
|
||||
- **WHEN** an image is stored
|
||||
- **THEN** the physical path MUST be generated (UUID/hash based)
|
||||
- **AND** MUST NOT use the original upload filename as the storage key
|
||||
|
||||
#### Scenario: Alt text required with image
|
||||
|
||||
- **WHEN** an admin uploads a cover or gallery image without alt text
|
||||
- **THEN** validation MUST fail
|
||||
|
||||
#### Scenario: Images are not stored in PostgreSQL
|
||||
|
||||
- **WHEN** content with images is persisted
|
||||
- **THEN** only the filesystem path MUST be stored in the database
|
||||
- **AND** binary image data MUST NOT be written to PostgreSQL columns
|
||||
@@ -0,0 +1,31 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Portfolio cases support editorial content and ordered gallery
|
||||
|
||||
The system SHALL allow admins to manage portfolio cases (SPEC WEB-03) with title, unique slug, summary, event type, optional city/venue/event date, challenge, solution, optional result, cover image with alt text, featured flag, sort order, `published_at`, and optional SEO meta fields. Each case MUST support an ordered gallery via `portfolio_images` (path, alt text, optional caption, sort order).
|
||||
|
||||
#### Scenario: Draft case is not publicly visible
|
||||
|
||||
- **WHEN** a portfolio case has `published_at` null
|
||||
- **THEN** the `published()` scope MUST exclude it
|
||||
|
||||
#### Scenario: Published case meets acceptance criteria
|
||||
|
||||
- **WHEN** an admin fills required fields and sets `published_at`
|
||||
- **THEN** the case MUST be included in the `published()` scope
|
||||
- **AND** cover and gallery images MUST have alt text when present
|
||||
|
||||
#### Scenario: Gallery images maintain order
|
||||
|
||||
- **WHEN** an admin reorders gallery images in Filament
|
||||
- **THEN** `sort_order` MUST reflect the chosen order per `portfolio_case_id`
|
||||
|
||||
#### Scenario: Assistant cannot manage portfolio
|
||||
|
||||
- **WHEN** an assistant attempts to access portfolio cases
|
||||
- **THEN** access MUST be denied with HTTP 403
|
||||
|
||||
#### Scenario: Cascade delete removes gallery images
|
||||
|
||||
- **WHEN** a portfolio case is deleted
|
||||
- **THEN** associated `portfolio_images` records MUST be removed (FK cascade)
|
||||
@@ -0,0 +1,16 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Local feature tests use PostgreSQL
|
||||
|
||||
The system SHALL configure `phpunit.xml` so that the Feature test suite uses PostgreSQL with the same connection parameters as CI (`DB_CONNECTION=pgsql`, host, port, database `amare_test`, credentials). Local feature tests MUST NOT default to SQLite `:memory:`.
|
||||
|
||||
#### Scenario: Developer runs feature tests locally
|
||||
|
||||
- **WHEN** a developer runs `composer test:feature` with PostgreSQL available
|
||||
- **THEN** tests MUST execute against PostgreSQL
|
||||
- **AND** MUST NOT silently fall back to SQLite
|
||||
|
||||
#### Scenario: Feature tests exercise PostgreSQL-specific types
|
||||
|
||||
- **WHEN** feature tests persist records with jsonb columns (e.g., `social_links`)
|
||||
- **THEN** migrations and constraints MUST be validated against PostgreSQL semantics
|
||||
@@ -0,0 +1,30 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Services are managed in Filament with publication control
|
||||
|
||||
The system SHALL allow admins to create, update, and delete services (SPEC WEB-02). Each service MUST have title, unique slug, summary, description, optional cover image with alt text, sort order, featured flag, and `published_at`.
|
||||
|
||||
#### Scenario: Unpublished service is not publicly visible
|
||||
|
||||
- **WHEN** a service has `published_at` null
|
||||
- **THEN** the `published()` scope MUST exclude it from public queries
|
||||
|
||||
#### Scenario: Published service is queryable
|
||||
|
||||
- **WHEN** an admin sets `published_at` on a service with required fields
|
||||
- **THEN** the service MUST be included in the `published()` scope
|
||||
|
||||
#### Scenario: Slug uniqueness is enforced
|
||||
|
||||
- **WHEN** an admin attempts to save two services with the same slug
|
||||
- **THEN** validation or database constraint MUST reject the duplicate
|
||||
|
||||
#### Scenario: Assistant cannot manage services
|
||||
|
||||
- **WHEN** an assistant attempts to access the services Resource
|
||||
- **THEN** access MUST be denied with HTTP 403
|
||||
|
||||
#### Scenario: Delete requires confirmation
|
||||
|
||||
- **WHEN** an admin deletes a service in Filament
|
||||
- **THEN** the UI MUST require explicit confirmation before deletion
|
||||
@@ -0,0 +1,28 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Site settings singleton is manageable by admin only
|
||||
|
||||
The system SHALL persist site-wide settings in a `site_settings` table as a typed singleton (SPEC WEB-06, §8.2). Fields MUST include brand name, hero copy (eyebrow, title, subtitle, CTA label), about summary, contact email/phone/city, social links (jsonb), default meta title/description, default OG image path and alt text, and optional analytics fields disabled by default.
|
||||
|
||||
#### Scenario: Admin updates site settings
|
||||
|
||||
- **WHEN** an admin saves the site settings form in Filament
|
||||
- **THEN** the singleton record is updated
|
||||
- **AND** labels and validation messages are in pt-BR
|
||||
|
||||
#### Scenario: Assistant cannot access site settings
|
||||
|
||||
- **WHEN** an assistant navigates to site settings in Filament
|
||||
- **THEN** access MUST be denied with HTTP 403
|
||||
|
||||
#### Scenario: Default OG image requires alt text
|
||||
|
||||
- **WHEN** an admin uploads a default OG image without alt text
|
||||
- **THEN** validation MUST fail with a pt-BR error message
|
||||
- **AND** alt text MUST remain optional when no default OG image is present
|
||||
|
||||
#### Scenario: Singleton avoids generic key-value store
|
||||
|
||||
- **WHEN** site settings are stored
|
||||
- **THEN** the system MUST use typed columns on `site_settings`
|
||||
- **AND** MUST NOT introduce a generic key/value configuration table
|
||||
@@ -0,0 +1,25 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Testimonials are managed with publication control
|
||||
|
||||
The system SHALL allow admins to manage testimonials (SPEC WEB-04) with quote text, author name, optional context, optional photo with alt text, sort order, featured flag, and `published_at`.
|
||||
|
||||
#### Scenario: Unpublished testimonial is excluded
|
||||
|
||||
- **WHEN** a testimonial has `published_at` null
|
||||
- **THEN** the `published()` scope MUST exclude it
|
||||
|
||||
#### Scenario: Published testimonial is queryable
|
||||
|
||||
- **WHEN** an admin sets `published_at` with required quote and author name
|
||||
- **THEN** the testimonial MUST be included in the `published()` scope
|
||||
|
||||
#### Scenario: Assistant cannot manage testimonials
|
||||
|
||||
- **WHEN** an assistant attempts to access the testimonials Resource
|
||||
- **THEN** access MUST be denied with HTTP 403
|
||||
|
||||
#### Scenario: Featured testimonials are filterable
|
||||
|
||||
- **WHEN** content is queried with featured filter
|
||||
- **THEN** records with `is_featured` true MUST be retrievable independently of sort order
|
||||
Reference in New Issue
Block a user