Files
amare/database/seeders/VisualContentSeeder.php
Manoel Freitas 3dc1f449ee feat: ship public site with SEO and visuals (#3)
* feat: ship public site with SEO and visuals

Publish CMS content on public routes with responsive media,
accessibility checks, and deterministic visual baselines.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: make browser visual CI deterministic for media

Mount host public storage into FrankenPHP so seeded fixtures are served,
and replace PNG-as-JPG fixtures with real JPEGs so Chromium can render them.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: serve public media on same-origin storage paths

Pest Browser hosts on 127.0.0.1:port while Storage::url used
http://localhost, so screenshots captured broken images. Use relative
/storage URLs for media and absolutize only OG tags via url().

Co-authored-by: Cursor <cursoragent@cursor.com>

* test: refresh visual baselines from CI Ubuntu screenshots

Media now loads on same-origin /storage paths, so baselines must
capture the rendered fixtures. Use full-page snapshots from the CI
runner to keep Pest's exact snapshot match stable across environments.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 10:12:24 -03:00

173 lines
6.7 KiB
PHP

<?php
declare(strict_types=1);
namespace Database\Seeders;
use App\Models\PortfolioCase;
use App\Models\PortfolioImage;
use App\Models\Service;
use App\Models\SiteSetting;
use App\Models\Testimonial;
use Illuminate\Database\Seeder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
/**
* Deterministic content for visual regression. Keep separate from ContentSeeder demo data.
*/
class VisualContentSeeder extends Seeder
{
public const FROZEN_NOW = '2026-03-15T12:00:00-03:00';
private const SEED_TIMESTAMP = '2026-03-15 12:00:00';
public function run(): void
{
Carbon::setTestNow(Carbon::parse(self::FROZEN_NOW));
$this->seedSiteSettings();
$this->seedServices();
$this->seedPortfolioCases();
$this->seedTestimonials();
}
private function seedSiteSettings(): void
{
SiteSetting::query()->updateOrCreate([], [
'brand_name' => 'Amare Assessoria',
'hero_eyebrow' => 'Assessoria de eventos',
'hero_title' => 'Celebrações com propósito',
'hero_subtitle' => 'Planejamento completo para casamentos e eventos corporativos em Fortaleza.',
'hero_cta_label' => 'Solicitar orçamento',
'about_summary' => 'Assessoria boutique especializada em experiências memoráveis.',
'email' => 'contato@amare.local',
'phone' => '(85) 99999-9999',
'city' => 'Fortaleza, CE',
'social_links' => [
'instagram' => 'https://instagram.com/amare',
],
'default_meta_title' => 'Amare Assessoria de Eventos',
'default_meta_description' => 'Assessoria premium para casamentos e eventos corporativos.',
'default_og_image_path' => $this->copyFixture('og-default.jpg', 'visual/og/og-default.jpg'),
'default_og_image_alt' => 'Identidade visual da Amare Assessoria de Eventos',
'analytics_enabled' => false,
'analytics_script' => null,
]);
}
private function seedServices(): void
{
foreach ([
[
'title' => 'Casamentos',
'slug' => 'casamentos',
'summary' => 'Planejamento completo do grande dia.',
'description' => 'Do briefing ao último brinde, cuidamos de cada detalhe do casamento.',
'sort_order' => 1,
'is_featured' => true,
],
[
'title' => 'Eventos corporativos',
'slug' => 'eventos-corporativos',
'summary' => 'Experiências alinhadas à marca.',
'description' => 'Lançamentos, convenções e encontros corporativos com operação impecável.',
'sort_order' => 2,
'is_featured' => true,
],
] as $service) {
Service::query()->updateOrCreate(
['slug' => $service['slug']],
[
...$service,
'cover_image_path' => $this->copyFixture('service-cover.jpg', 'visual/services/'.$service['slug'].'.jpg'),
'cover_image_alt' => 'Capa do serviço '.$service['title'],
'published_at' => Carbon::parse(self::SEED_TIMESTAMP),
],
);
}
}
private function seedPortfolioCases(): void
{
$cases = [
[
'title' => 'Casamento Ana e Lucas',
'slug' => 'casamento-ana-lucas',
'summary' => 'Cerimônia ao ar livre em Fortaleza.',
'event_type' => 'Casamento',
'city' => 'Fortaleza',
'venue' => 'Espaço Jardim Atlântico',
'event_date' => '2025-11-20',
'challenge' => 'Integrar cerimônia e recepção em áreas distintas.',
'solution' => 'Operação sincronizada com sinalização e timing detalhado.',
'result' => 'Experiência fluida para 180 convidados.',
'sort_order' => 1,
],
[
'title' => 'Lançamento Verano',
'slug' => 'lancamento-verano',
'summary' => 'Evento corporativo de lançamento de coleção.',
'event_type' => 'Corporativo',
'city' => 'Fortaleza',
'venue' => 'Centro de Convenções',
'event_date' => '2025-09-10',
'challenge' => 'Ativar marca em ambiente multiestação.',
'solution' => 'Fluxo de convidados e fornecedores com cronograma minuto a minuto.',
'result' => 'Alta percepção de marca e cobertura de imprensa.',
'sort_order' => 2,
],
];
foreach ($cases as $caseData) {
$case = PortfolioCase::query()->updateOrCreate(
['slug' => $caseData['slug']],
[
...$caseData,
'cover_image_path' => $this->copyFixture('portfolio-cover.jpg', 'visual/portfolio/'.$caseData['slug'].'-cover.jpg'),
'cover_image_alt' => 'Capa do caso '.$caseData['title'],
'is_featured' => true,
'published_at' => Carbon::parse(self::SEED_TIMESTAMP),
],
);
PortfolioImage::query()->where('portfolio_case_id', $case->id)->delete();
PortfolioImage::query()->create([
'portfolio_case_id' => $case->id,
'path' => $this->copyFixture('gallery.jpg', 'visual/portfolio/'.$caseData['slug'].'-gallery-1.jpg'),
'alt_text' => 'Galeria '.$caseData['title'].' 1',
'caption' => 'Detalhe da decoração',
'sort_order' => 1,
]);
}
}
private function seedTestimonials(): void
{
Testimonial::query()->updateOrCreate(
[
'author_name' => 'Ana Souza',
'quote' => 'A Amare transformou nosso casamento em uma experiência inesquecível.',
],
[
'context' => 'Noiva',
'sort_order' => 1,
'is_featured' => true,
'photo_path' => $this->copyFixture('testimonial.jpg', 'visual/testimonials/ana-souza.jpg'),
'photo_alt' => 'Foto de Ana Souza',
'published_at' => Carbon::parse(self::SEED_TIMESTAMP),
],
);
}
private function copyFixture(string $fixtureName, string $destination): string
{
$source = base_path('tests/fixtures/images/'.$fixtureName);
Storage::disk('public')->put($destination, File::get($source));
return $destination;
}
}