diff --git a/app/Domains/Tenant/Models/Tenant.php b/app/Domains/Tenant/Models/Tenant.php index c84121b..5963e9e 100644 --- a/app/Domains/Tenant/Models/Tenant.php +++ b/app/Domains/Tenant/Models/Tenant.php @@ -63,6 +63,14 @@ class Tenant extends Model return $this->belongsTo(Attachment::class, 'footer_logo_id'); } + /** + * @return BelongsTo + */ + public function heroBgImage(): BelongsTo + { + return $this->belongsTo(Attachment::class, 'hero_bg_image_id'); + } + public function productos(): HasMany { return $this->hasMany(Product::class, 'tenant_codigo', 'codigo'); diff --git a/app/Domains/Tenant/Requests/StoreTenantRequest.php b/app/Domains/Tenant/Requests/StoreTenantRequest.php index 988ad14..9ee976c 100644 --- a/app/Domains/Tenant/Requests/StoreTenantRequest.php +++ b/app/Domains/Tenant/Requests/StoreTenantRequest.php @@ -61,8 +61,8 @@ class StoreTenantRequest extends FormRequest 'footer_bg_color' => ['required', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'], 'header_logo' => $logoRule, 'footer_logo' => $logoRule, + 'hero_bg_image' => ['nullable', new ImageOrBase64Rule()], 'hero_config' => ['nullable', 'array'], - 'hero_config.background_image' => ['nullable', 'string'], 'hero_config.title_html' => ['nullable', 'string'], 'hero_config.description_html' => ['nullable', 'string'], 'hero_config.button_text' => ['nullable', 'string'], diff --git a/app/Domains/Tenant/Requests/UpdateTenantRequest.php b/app/Domains/Tenant/Requests/UpdateTenantRequest.php index 30eb081..219761e 100644 --- a/app/Domains/Tenant/Requests/UpdateTenantRequest.php +++ b/app/Domains/Tenant/Requests/UpdateTenantRequest.php @@ -72,8 +72,8 @@ class UpdateTenantRequest extends FormRequest 'footer_bg_color' => ['nullable', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'], 'header_logo' => $logoRule, 'footer_logo' => $logoRule, + 'hero_bg_image' => ['nullable', new ImageOrBase64Rule()], 'hero_config' => ['nullable', 'array'], - 'hero_config.background_image' => ['nullable', 'string'], 'hero_config.title_html' => ['nullable', 'string'], 'hero_config.description_html' => ['nullable', 'string'], 'hero_config.button_text' => ['nullable', 'string'], diff --git a/app/Domains/Tenant/Resources/TenantResource.php b/app/Domains/Tenant/Resources/TenantResource.php index 9cc78b0..9fae254 100644 --- a/app/Domains/Tenant/Resources/TenantResource.php +++ b/app/Domains/Tenant/Resources/TenantResource.php @@ -15,6 +15,12 @@ class TenantResource extends JsonResource */ public function toArray(Request $request): array { + $heroConfig = $this->hero_config; + if (is_array($heroConfig)) { + $heroConfig['background_image'] = $this->heroBgImage?->getTemporaryUrl(1440); + unset($heroConfig['background_image_id']); + } + return [ 'id' => $this->id, 'codigo' => $this->codigo, @@ -29,7 +35,7 @@ class TenantResource extends JsonResource // 1 day 'header_logo' => $this->headerLogo?->getTemporaryUrl(1440), 'footer_logo' => $this->footerLogo?->getTemporaryUrl(1440 ), - 'hero_config' => $this->hero_config, + 'hero_config' => $heroConfig, 'event_config' => $this->event_config, 'menues' => $this->whenLoaded('menues'), ]; diff --git a/app/Domains/Tenant/Services/TenantService.php b/app/Domains/Tenant/Services/TenantService.php index 3357101..57e62f1 100644 --- a/app/Domains/Tenant/Services/TenantService.php +++ b/app/Domains/Tenant/Services/TenantService.php @@ -24,8 +24,9 @@ class TenantService return DB::transaction(function () use ($data): Tenant { $headerLogo = $data['header_logo'] ?? null; $footerLogo = $data['footer_logo'] ?? null; + $heroBgImage = $data['hero_bg_image'] ?? null; - unset($data['header_logo'], $data['footer_logo']); + unset($data['header_logo'], $data['footer_logo'], $data['hero_bg_image']); $headerAttachmentId = null; if ($headerLogo) { @@ -52,6 +53,18 @@ class TenantService $data['header_logo_id'] = $headerAttachmentId; $data['footer_logo_id'] = $footerAttachmentId; + if ($heroBgImage) { + $attachment = Str::isUuid($heroBgImage) + ? \App\Domains\Attachable\Models\Attachment::query()->where('key', $heroBgImage)->first() + : $this->attachmentService->store($heroBgImage, 'tenants'); + + if ($attachment) { + $heroConfig = $data['hero_config'] ?? []; + $heroConfig['background_image_id'] = $attachment->id; + $data['hero_config'] = $heroConfig; + } + } + /** @var Tenant $tenant */ $tenant = Tenant::query()->create($data); @@ -71,10 +84,14 @@ class TenantService return DB::transaction(function () use ($tenant, $data): Tenant { $hasHeaderLogoKey = array_key_exists('header_logo', $data); $hasFooterLogoKey = array_key_exists('footer_logo', $data); + $hasHeroBgImageKey = array_key_exists('hero_bg_image', $data); $headerLogo = $data['header_logo'] ?? null; $footerLogo = $data['footer_logo'] ?? null; + $heroBgImage = $data['hero_bg_image'] ?? null; - unset($data['header_logo'], $data['footer_logo']); + unset($data['header_logo'], $data['footer_logo'], $data['hero_bg_image']); + + $oldHeroBgId = $tenant->hero_bg_image_id; $tenant->fill($data); @@ -110,6 +127,28 @@ class TenantService } } + $currentHeroConfig = $tenant->hero_config ?? []; + if ($hasHeroBgImageKey) { + if ($heroBgImage) { + $attachment = Str::isUuid($heroBgImage) + ? \App\Domains\Attachable\Models\Attachment::query()->where('key', $heroBgImage)->first() + : $this->attachmentService->store($heroBgImage, 'tenants'); + + if ($attachment) { + $currentHeroConfig['background_image_id'] = $attachment->id; + } else { + unset($currentHeroConfig['background_image_id']); + } + } else { + unset($currentHeroConfig['background_image_id']); + } + } else { + if ($oldHeroBgId) { + $currentHeroConfig['background_image_id'] = $oldHeroBgId; + } + } + $tenant->hero_config = empty($currentHeroConfig) ? null : $currentHeroConfig; + $tenant->save(); return $tenant; diff --git a/database/migrations/2026_07_14_135000_add_hero_bg_image_id_virtual_column_to_tenants_table.php b/database/migrations/2026_07_14_135000_add_hero_bg_image_id_virtual_column_to_tenants_table.php new file mode 100644 index 0000000..e89a67a --- /dev/null +++ b/database/migrations/2026_07_14_135000_add_hero_bg_image_id_virtual_column_to_tenants_table.php @@ -0,0 +1,31 @@ +unsignedBigInteger('hero_bg_image_id') + ->virtualAs('hero_config->>"$.background_image_id"') + ->nullable() + ->after('footer_bg_color'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('tenants', function (Blueprint $table) { + $table->dropColumn('hero_bg_image_id'); + }); + } +}; diff --git a/database/seeders/TenantSeeder.php b/database/seeders/TenantSeeder.php index c25931e..55a77b8 100644 --- a/database/seeders/TenantSeeder.php +++ b/database/seeders/TenantSeeder.php @@ -100,6 +100,11 @@ class TenantSeeder extends Seeder $attachmentService->delete($existingFiesta->footerLogo); } catch (\Throwable $e) {} } + if ($existingFiesta->heroBgImage) { + try { + $attachmentService->delete($existingFiesta->heroBgImage); + } catch (\Throwable $e) {} + } $existingFiesta->delete(); } @@ -111,6 +116,7 @@ class TenantSeeder extends Seeder $fiestaHeaderImagePath = public_path('images/futbol_infantil_header.png'); $fiestaFooterImagePath = public_path('images/futbol_infantil_footer.png'); + $fiestaHeroBgImagePath = public_path('images/futbol_infantil_hero.jpg'); if (! file_exists($fiestaHeaderImagePath)) { throw new \RuntimeException("Image not found at path: {$fiestaHeaderImagePath}"); @@ -120,6 +126,10 @@ class TenantSeeder extends Seeder throw new \RuntimeException("Image not found at path: {$fiestaFooterImagePath}"); } + if (! file_exists($fiestaHeroBgImagePath)) { + throw new \RuntimeException("Image not found at path: {$fiestaHeroBgImagePath}"); + } + $fiestaHeaderLogo = new UploadedFile( $fiestaHeaderImagePath, 'futbol_infantil_header.png', @@ -136,6 +146,14 @@ class TenantSeeder extends Seeder true ); + $fiestaHeroBgImage = new UploadedFile( + $fiestaHeroBgImagePath, + 'futbol_infantil_hero.jpg', + 'image/jpeg', + null, + true + ); + $this->tenantService->create([ 'codigo' => 'fiesta_futbol_infantil', 'nombre' => 'Fiesta Fútbol Infantil', @@ -148,6 +166,7 @@ class TenantSeeder extends Seeder 'footer_bg_color' => '#015327', 'header_logo' => $fiestaHeaderLogo, 'footer_logo' => $fiestaFooterLogo, + 'hero_bg_image' => $fiestaHeroBgImage, 'hero_config' => [ 'title_html' => 'ASEGURÁ TU LUGAR', 'description_html' => 'Comprá tu entrada oficial en segundos de forma 100% segura. Preparate para vivir la experiencia completa.', diff --git a/public/images/futbol_infantil_hero.jpg b/public/images/futbol_infantil_hero.jpg new file mode 100644 index 0000000..c636725 Binary files /dev/null and b/public/images/futbol_infantil_hero.jpg differ