diff --git a/database/seeders/TenantSeeder.php b/database/seeders/TenantSeeder.php index 24f185b..d158e5d 100644 --- a/database/seeders/TenantSeeder.php +++ b/database/seeders/TenantSeeder.php @@ -8,6 +8,7 @@ use App\Domains\Client\Models\Client; use App\Domains\Tenant\Models\Tenant; use App\Domains\Tenant\Models\AdminWebsiteType; use App\Domains\Tenant\Services\TenantService; +use App\Domains\Tenant\Services\WebsiteExtraService; use Illuminate\Database\Seeder; use Illuminate\Http\UploadedFile; use RuntimeException; @@ -19,6 +20,17 @@ class TenantSeeder extends Seeder private const SONDER_CLIENT_CODE = 'sonder'; + private const ONTICKET_HERO_CAROUSEL_IMAGES = [ + 'onticket_hero_carousel_1.png', + 'onticket_hero_carousel_2.png', + 'onticket_hero_carousel_3.avif', + 'onticket_hero_carousel_4.jfif', + 'onticket_hero_carousel_5.jfif', + 'onticket_hero_carousel_6.jfif', + 'onticket_hero_carousel_7.avif', + 'onticket_hero_carousel_8.avif', + ]; + private const SOCIAL_MEDIA = [ [ 'code' => 'instagram', @@ -42,7 +54,10 @@ class TenantSeeder extends Seeder ], ]; - public function __construct(protected TenantService $tenantService) {} + public function __construct( + protected TenantService $tenantService, + protected WebsiteExtraService $websiteExtraService, + ) {} public function run(): void { @@ -107,6 +122,25 @@ class TenantSeeder extends Seeder $onTicketTenant->update(['storefront_website_type_code' => 'onticket_multi_event']); } + $onTicketTenant = Tenant::query()->where('codigo', 'onticket')->firstOrFail(); + + if ( + $onTicketTenant->storefront_website_type_code === 'onticket_multi_event' + && ! $onTicketTenant->websiteExtras()->whereHas('websiteTypeExtra', fn ($query) => $query->where('codigo', 'heroCarousel'))->exists() + ) { + $this->websiteExtraService->updateForTenant( + $onTicketTenant, + 'heroCarousel', + array_map( + fn (string $filename): UploadedFile => $this->uploadedImage( + "images/tennants/onticket/onticket_tennant_hero_carousel/{$filename}", + $filename, + ), + self::ONTICKET_HERO_CAROUSEL_IMAGES, + ), + ); + } + $this->deleteTenant('sonder'); Tenant::query() @@ -333,7 +367,8 @@ class TenantSeeder extends Seeder } $mimeType = match (strtolower(pathinfo($filename, PATHINFO_EXTENSION))) { - 'jpg', 'jpeg' => 'image/jpeg', + 'jpg', 'jpeg', 'jfif' => 'image/jpeg', + 'avif' => 'image/avif', 'gif' => 'image/gif', 'webp' => 'image/webp', 'svg' => 'image/svg+xml', diff --git a/database/seeders/WebsiteTypeSeeder.php b/database/seeders/WebsiteTypeSeeder.php index 5b6ded7..c6ebef2 100644 --- a/database/seeders/WebsiteTypeSeeder.php +++ b/database/seeders/WebsiteTypeSeeder.php @@ -10,6 +10,19 @@ use RuntimeException; class WebsiteTypeSeeder extends Seeder { + private const IMAGE_CAROUSEL_SCHEMA = [ + 'request_rules' => [ + '$' => 'required|array|max:10', + '$.*' => 'required|image_or_base64|distinct', + ], + 'transforms' => [ + '$.*' => [ + 'handler' => 'attachment', + 'attachment_type' => 'image', + ], + ], + ]; + private const PRESENTATION = [ 'primary_color' => '#FF7006', 'secondary_color' => '#777777', @@ -52,18 +65,7 @@ class WebsiteTypeSeeder extends Seeder 'nombre' => 'Carrusel principal', 'descripcion' => 'Listado de attachments que se muestran en el carousel principal.', 'is_required' => false, - 'config_schema' => [ - 'request_rules' => [ - '$' => 'required|array|max:10', - '$.*' => 'required|image_or_base64|distinct', - ], - 'transforms' => [ - '$.*' => [ - 'handler' => 'attachment', - 'attachment_type' => 'image', - ], - ], - ], + 'config_schema' => self::IMAGE_CAROUSEL_SCHEMA, ], ); @@ -86,11 +88,21 @@ class WebsiteTypeSeeder extends Seeder ['nombre' => 'OnTicket - evento único', 'header_type' => 'standard'], ); - StorefrontWebsiteType::query()->updateOrCreate( + $multiEventStorefront = StorefrontWebsiteType::query()->updateOrCreate( ['codigo' => 'onticket_multi_event'], ['nombre' => 'OnTicket - múltiples eventos', 'header_type' => 'immersive'], ); + $multiEventStorefront->extras()->updateOrCreate( + ['codigo' => 'heroCarousel'], + [ + 'nombre' => 'Carrusel del hero', + 'descripcion' => 'Lista ordenada de imágenes del hero.', + 'is_required' => false, + 'config_schema' => self::IMAGE_CAROUSEL_SCHEMA, + ], + ); + $onTicketStorefront->extras()->updateOrCreate( ['codigo' => 'heroConfig'], [ diff --git a/public/images/tennants/onticket/onticket_tennant_hero_carousel/onticket_hero_carousel_1.png b/public/images/tennants/onticket/onticket_tennant_hero_carousel/onticket_hero_carousel_1.png new file mode 100644 index 0000000..3156e6e Binary files /dev/null and b/public/images/tennants/onticket/onticket_tennant_hero_carousel/onticket_hero_carousel_1.png differ diff --git a/public/images/tennants/onticket/onticket_tennant_hero_carousel/onticket_hero_carousel_2.png b/public/images/tennants/onticket/onticket_tennant_hero_carousel/onticket_hero_carousel_2.png new file mode 100644 index 0000000..dce4a17 Binary files /dev/null and b/public/images/tennants/onticket/onticket_tennant_hero_carousel/onticket_hero_carousel_2.png differ diff --git a/public/images/tennants/onticket/onticket_tennant_hero_carousel/onticket_hero_carousel_3.avif b/public/images/tennants/onticket/onticket_tennant_hero_carousel/onticket_hero_carousel_3.avif new file mode 100644 index 0000000..94ec3f9 Binary files /dev/null and b/public/images/tennants/onticket/onticket_tennant_hero_carousel/onticket_hero_carousel_3.avif differ diff --git a/public/images/tennants/onticket/onticket_tennant_hero_carousel/onticket_hero_carousel_4.jfif b/public/images/tennants/onticket/onticket_tennant_hero_carousel/onticket_hero_carousel_4.jfif new file mode 100644 index 0000000..5a2c979 Binary files /dev/null and b/public/images/tennants/onticket/onticket_tennant_hero_carousel/onticket_hero_carousel_4.jfif differ diff --git a/public/images/tennants/onticket/onticket_tennant_hero_carousel/onticket_hero_carousel_5.jfif b/public/images/tennants/onticket/onticket_tennant_hero_carousel/onticket_hero_carousel_5.jfif new file mode 100644 index 0000000..5052ff4 Binary files /dev/null and b/public/images/tennants/onticket/onticket_tennant_hero_carousel/onticket_hero_carousel_5.jfif differ diff --git a/public/images/tennants/onticket/onticket_tennant_hero_carousel/onticket_hero_carousel_6.jfif b/public/images/tennants/onticket/onticket_tennant_hero_carousel/onticket_hero_carousel_6.jfif new file mode 100644 index 0000000..9457473 Binary files /dev/null and b/public/images/tennants/onticket/onticket_tennant_hero_carousel/onticket_hero_carousel_6.jfif differ diff --git a/public/images/tennants/onticket/onticket_tennant_hero_carousel/onticket_hero_carousel_7.avif b/public/images/tennants/onticket/onticket_tennant_hero_carousel/onticket_hero_carousel_7.avif new file mode 100644 index 0000000..d2cdf38 Binary files /dev/null and b/public/images/tennants/onticket/onticket_tennant_hero_carousel/onticket_hero_carousel_7.avif differ diff --git a/public/images/tennants/onticket/onticket_tennant_hero_carousel/onticket_hero_carousel_8.avif b/public/images/tennants/onticket/onticket_tennant_hero_carousel/onticket_hero_carousel_8.avif new file mode 100644 index 0000000..3eef8a3 Binary files /dev/null and b/public/images/tennants/onticket/onticket_tennant_hero_carousel/onticket_hero_carousel_8.avif differ diff --git a/tests/Feature/Seeders/TenantSeederTest.php b/tests/Feature/Seeders/TenantSeederTest.php index 180ef28..1a11016 100644 --- a/tests/Feature/Seeders/TenantSeederTest.php +++ b/tests/Feature/Seeders/TenantSeederTest.php @@ -2,6 +2,7 @@ namespace Tests\Feature\Seeders; +use App\Domains\Attachable\Models\Attachment; use App\Domains\Tenant\Models\Tenant; use App\Domains\Tenant\Models\AdminWebsiteType; use Database\Seeders\SocialMediaSeeder; @@ -47,6 +48,29 @@ class TenantSeederTest extends TestCase foreach ([$tenant->headerLogo, $tenant->footerLogo, $tenant->favicon, $tenant->headerBackgroundImage] as $attachment) { Storage::disk('s3')->assertExists($attachment->path); } + + $carousel = $tenant->websiteExtras() + ->whereHas('websiteTypeExtra', fn ($query) => $query->where('codigo', 'heroCarousel')) + ->sole(); + $imageIds = $carousel->config; + $this->assertCount(8, $imageIds); + $this->assertSame([ + 'onticket_hero_carousel_1.png', + 'onticket_hero_carousel_2.png', + 'onticket_hero_carousel_3.avif', + 'onticket_hero_carousel_4.jfif', + 'onticket_hero_carousel_5.jfif', + 'onticket_hero_carousel_6.jfif', + 'onticket_hero_carousel_7.avif', + 'onticket_hero_carousel_8.avif', + ], array_map(fn (int $id): string => Attachment::query()->findOrFail($id)->filename, $imageIds)); + + foreach ($imageIds as $id) { + Storage::disk('s3')->assertExists(Attachment::query()->findOrFail($id)->path); + } + + $this->seed(TenantSeeder::class); + $this->assertSame($imageIds, $carousel->fresh()->config); } public function test_it_assigns_social_media_to_both_tenants_in_order(): void diff --git a/tests/Feature/Seeders/WebsiteTypeSeederTest.php b/tests/Feature/Seeders/WebsiteTypeSeederTest.php index 34ed606..6f0cc3d 100644 --- a/tests/Feature/Seeders/WebsiteTypeSeederTest.php +++ b/tests/Feature/Seeders/WebsiteTypeSeederTest.php @@ -89,7 +89,8 @@ class WebsiteTypeSeederTest extends TestCase $this->assertSame('standard', $onTicketStorefront->header_type); $multiEventStorefront = StorefrontWebsiteType::query()->where('codigo', 'onticket_multi_event')->with('extras')->sole(); $this->assertSame('immersive', $multiEventStorefront->header_type); - $this->assertCount(0, $multiEventStorefront->extras); + $this->assertSame(['heroCarousel'], $multiEventStorefront->extras->pluck('codigo')->all()); + $this->assertSame($shopItStorefront->extras->sole()->config_schema, $multiEventStorefront->extras->sole()->config_schema); $this->assertEqualsCanonicalizing( ['heroConfig', 'eventConfig', 'additionalInfoConfig'], $onTicketStorefront->extras->pluck('codigo')->all(), diff --git a/tests/Feature/Tenant/BootstrapTenantControllerTest.php b/tests/Feature/Tenant/BootstrapTenantControllerTest.php index 65232b3..39b32f1 100644 --- a/tests/Feature/Tenant/BootstrapTenantControllerTest.php +++ b/tests/Feature/Tenant/BootstrapTenantControllerTest.php @@ -12,6 +12,7 @@ use App\Domains\Menu\Models\Menu; use App\Domains\Tenant\Models\Tenant; use App\Domains\Tenant\Models\AdminWebsiteType; use App\Domains\Tenant\Models\StorefrontWebsiteType; +use Database\Seeders\WebsiteTypeSeeder; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\Storage; @@ -333,6 +334,47 @@ class BootstrapTenantControllerTest extends TestCase ->assertJsonMissingPath('data.extras.banner'); } + public function test_it_returns_signed_hero_carousel_urls_from_stored_attachment_ids(): void + { + Storage::fake('s3'); + $this->seed(WebsiteTypeSeeder::class); + + $tenant = $this->createTenant(); + $tenant->update(['storefront_website_type_code' => 'onticket_multi_event']); + $definition = StorefrontWebsiteType::query() + ->where('codigo', 'onticket_multi_event') + ->firstOrFail() + ->extras() + ->where('codigo', 'heroCarousel') + ->firstOrFail(); + + $images = collect(['first.jpg', 'second.jpg'])->map(fn (string $filename): Attachment => Attachment::query()->create([ + 'key' => (string) Str::uuid(), + 'path' => "tenants/acme/extras/heroCarousel/{$filename}", + 'filename' => $filename, + 'type' => AttachmentType::Image, + 'mime_type' => 'image/jpeg', + 'extension' => 'jpg', + ])); + + $extra = $tenant->websiteExtras()->create([ + 'website_type_extra_id' => $definition->id, + 'config' => $images->pluck('id')->all(), + ]); + + $this->assertSame($images->pluck('id')->all(), $extra->fresh()->config); + + $response = $this->getJson('/api/tenants/bootstrap?dominio=acme.com&path=%2F')->assertOk(); + $response->assertJsonCount(2, 'data.extras.heroCarousel'); + + foreach ($images->values() as $index => $image) { + $response->assertJsonPath("data.extras.heroCarousel.{$index}", function (string $url) use ($image): bool { + return str_contains($url, $image->path) + && (str_contains($url, 'Expires=') || str_contains($url, 'X-Amz-Expires=')); + }); + } + } + public function test_the_bootstrap_returns_only_the_cropped_banner_url(): void { Storage::fake('s3');