diff --git a/app/Domains/Tenant/Models/Tenant.php b/app/Domains/Tenant/Models/Tenant.php index f8c07a2..dc50201 100644 --- a/app/Domains/Tenant/Models/Tenant.php +++ b/app/Domains/Tenant/Models/Tenant.php @@ -21,6 +21,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany; 'codigo', 'nombre', 'dominio', + 'site_title', 'primary_color', 'secondary_color', 'danger_color', @@ -29,6 +30,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany; 'footer_bg_color', 'header_logo_id', 'footer_logo_id', + 'favicon_id', 'header_bg_image_id', 'footer_bg_image_id', 'website_type_code', @@ -101,6 +103,14 @@ class Tenant extends Model return $this->belongsTo(Attachment::class, 'footer_logo_id'); } + /** + * @return BelongsTo + */ + public function favicon(): BelongsTo + { + return $this->belongsTo(Attachment::class, 'favicon_id'); + } + /** * @return BelongsTo */ diff --git a/app/Domains/Tenant/Models/WebsiteType.php b/app/Domains/Tenant/Models/WebsiteType.php index f971440..23500cc 100644 --- a/app/Domains/Tenant/Models/WebsiteType.php +++ b/app/Domains/Tenant/Models/WebsiteType.php @@ -14,6 +14,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany; 'nombre', 'dominio', 'scanner_domain', + 'site_title', 'primary_color', 'secondary_color', 'danger_color', @@ -27,6 +28,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany; 'login_header_footer_color', 'site_logo', 'footer_logo', + 'favicon_id', ])] class WebsiteType extends Model { @@ -50,6 +52,14 @@ class WebsiteType extends Model return $this->belongsTo(Attachment::class, 'footer_logo'); } + /** + * @return BelongsTo + */ + public function favicon(): BelongsTo + { + return $this->belongsTo(Attachment::class, 'favicon_id'); + } + /** * @return HasMany */ diff --git a/app/Domains/Tenant/Requests/StoreTenantRequest.php b/app/Domains/Tenant/Requests/StoreTenantRequest.php index 704a474..8c2c058 100644 --- a/app/Domains/Tenant/Requests/StoreTenantRequest.php +++ b/app/Domains/Tenant/Requests/StoreTenantRequest.php @@ -55,6 +55,7 @@ class StoreTenantRequest extends FormRequest 'max:255', Rule::unique('tenants', 'dominio'), ], + 'site_title' => ['sometimes', 'nullable', 'string', 'max:255'], 'primary_color' => ['required', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'], 'secondary_color' => ['required', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'], 'danger_color' => ['required', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'], @@ -63,6 +64,7 @@ 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, + 'favicon' => ['sometimes', 'nullable', new ImageOrBase64Rule], 'header_bg_image' => ['sometimes', 'nullable', new ImageOrBase64Rule], 'footer_bg_image' => ['sometimes', 'nullable', new ImageOrBase64Rule], 'social_media' => ['sometimes', 'array'], diff --git a/app/Domains/Tenant/Requests/UpdateTenantRequest.php b/app/Domains/Tenant/Requests/UpdateTenantRequest.php index 771941e..94319fd 100644 --- a/app/Domains/Tenant/Requests/UpdateTenantRequest.php +++ b/app/Domains/Tenant/Requests/UpdateTenantRequest.php @@ -65,6 +65,7 @@ class UpdateTenantRequest extends FormRequest 'max:255', Rule::unique('tenants', 'dominio')->ignore($tenant?->id), ], + 'site_title' => ['sometimes', 'nullable', 'string', 'max:255'], 'primary_color' => ['nullable', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'], 'secondary_color' => ['nullable', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'], 'danger_color' => ['nullable', 'string', 'regex:/^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/'], @@ -73,6 +74,7 @@ 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, + 'favicon' => ['sometimes', 'nullable', new ImageOrBase64Rule], 'header_bg_image' => ['sometimes', 'nullable', new ImageOrBase64Rule], 'footer_bg_image' => ['sometimes', 'nullable', new ImageOrBase64Rule], 'social_media' => ['sometimes', 'array'], diff --git a/app/Domains/Tenant/Resources/TenantResource.php b/app/Domains/Tenant/Resources/TenantResource.php index 75d626b..c17f4fd 100644 --- a/app/Domains/Tenant/Resources/TenantResource.php +++ b/app/Domains/Tenant/Resources/TenantResource.php @@ -26,6 +26,11 @@ class TenantResource extends JsonResource 'codigo' => $this->codigo, 'nombre' => $this->nombre, 'dominio' => $this->dominio, + 'site_title' => $this->site_title + ?? $this->websiteType?->site_title + ?? 'ShopitFront', + 'favicon' => ($this->favicon ?? $this->websiteType?->favicon) + ?->getTemporaryUrl(1440), 'primary_color' => $this->primary_color, 'secondary_color' => $this->secondary_color, 'danger_color' => $this->danger_color, diff --git a/app/Domains/Tenant/Services/TenantInformationService.php b/app/Domains/Tenant/Services/TenantInformationService.php index 1b4491a..c9ee138 100644 --- a/app/Domains/Tenant/Services/TenantInformationService.php +++ b/app/Domains/Tenant/Services/TenantInformationService.php @@ -13,6 +13,8 @@ class TenantInformationService private const DEFAULT_RELATIONS = [ 'headerLogo', 'footerLogo', + 'favicon', + 'websiteType.favicon', 'headerBackgroundImage', 'footerBackgroundImage', 'socialMedia', diff --git a/app/Domains/Tenant/Services/TenantService.php b/app/Domains/Tenant/Services/TenantService.php index de4890c..da73cb6 100644 --- a/app/Domains/Tenant/Services/TenantService.php +++ b/app/Domains/Tenant/Services/TenantService.php @@ -25,6 +25,7 @@ class TenantService return DB::transaction(function () use ($data): Tenant { $headerLogo = $data['header_logo'] ?? null; $footerLogo = $data['footer_logo'] ?? null; + $favicon = $data['favicon'] ?? null; $headerBackgroundImage = $data['header_bg_image'] ?? null; $footerBackgroundImage = $data['footer_bg_image'] ?? null; $socialMedia = $data['social_media'] ?? []; @@ -33,6 +34,7 @@ class TenantService unset( $data['header_logo'], $data['footer_logo'], + $data['favicon'], $data['header_bg_image'], $data['footer_bg_image'], $data['social_media'], @@ -63,6 +65,7 @@ class TenantService $data['header_logo_id'] = $headerAttachmentId; $data['footer_logo_id'] = $footerAttachmentId; + $data['favicon_id'] = $this->storeTenantImage($favicon); $data['header_bg_image_id'] = $this->storeTenantImage($headerBackgroundImage); $data['footer_bg_image_id'] = $this->storeTenantImage($footerBackgroundImage); @@ -85,11 +88,13 @@ class TenantService return DB::transaction(function () use ($tenant, $data): Tenant { $hasHeaderLogoKey = array_key_exists('header_logo', $data); $hasFooterLogoKey = array_key_exists('footer_logo', $data); + $hasFaviconKey = array_key_exists('favicon', $data); $hasHeaderBackgroundImageKey = array_key_exists('header_bg_image', $data); $hasFooterBackgroundImageKey = array_key_exists('footer_bg_image', $data); $hasSocialMediaKey = array_key_exists('social_media', $data); $headerLogo = $data['header_logo'] ?? null; $footerLogo = $data['footer_logo'] ?? null; + $favicon = $data['favicon'] ?? null; $headerBackgroundImage = $data['header_bg_image'] ?? null; $footerBackgroundImage = $data['footer_bg_image'] ?? null; $socialMedia = $data['social_media'] ?? []; @@ -97,6 +102,7 @@ class TenantService unset( $data['header_logo'], $data['footer_logo'], + $data['favicon'], $data['header_bg_image'], $data['footer_bg_image'], $data['social_media'] @@ -136,6 +142,10 @@ class TenantService } } + if ($hasFaviconKey) { + $tenant->favicon_id = $this->storeTenantImage($favicon); + } + if ($hasHeaderBackgroundImageKey) { $tenant->header_bg_image_id = $this->storeTenantImage($headerBackgroundImage); } diff --git a/app/Domains/Tenant/Services/WebsiteTypeService.php b/app/Domains/Tenant/Services/WebsiteTypeService.php index bce4f8b..f89d69f 100644 --- a/app/Domains/Tenant/Services/WebsiteTypeService.php +++ b/app/Domains/Tenant/Services/WebsiteTypeService.php @@ -46,14 +46,20 @@ class WebsiteTypeService return DB::transaction(function () use ($websiteType, $data): WebsiteType { $previousLogos = []; - foreach (['site_logo' => 'siteLogo', 'footer_logo' => 'footerLogo'] as $field => $relation) { + $attachmentFields = [ + 'site_logo' => ['relation' => 'siteLogo', 'column' => 'site_logo'], + 'footer_logo' => ['relation' => 'footerLogo', 'column' => 'footer_logo'], + 'favicon' => ['relation' => 'favicon', 'column' => 'favicon_id'], + ]; + + foreach ($attachmentFields as $field => $attachmentField) { if (! array_key_exists($field, $data)) { continue; } $logo = $data[$field]; $previousLogos[$field] = $websiteType->exists - ? $websiteType->{$relation}()->first() + ? $websiteType->{$attachmentField['relation']}()->first() : null; unset($data[$field]); @@ -65,17 +71,17 @@ class WebsiteTypeService : $this->attachmentService->store($logo, 'website-types'); } - $data[$field] = $attachment?->id; + $data[$attachmentField['column']] = $attachment?->id; } $websiteType->fill($data)->save(); - foreach ($previousLogos as $field => $previousLogo) { + foreach ($previousLogos as $previousLogo) { if ( $previousLogo instanceof Attachment - && $previousLogo->id !== $websiteType->{$field} && $previousLogo->id !== $websiteType->site_logo && $previousLogo->id !== $websiteType->footer_logo + && $previousLogo->id !== $websiteType->favicon_id ) { $this->attachmentService->delete($previousLogo); } diff --git a/database/migrations/2026_08_14_000000_add_browser_branding_to_tenants_and_website_type.php b/database/migrations/2026_08_14_000000_add_browser_branding_to_tenants_and_website_type.php new file mode 100644 index 0000000..578f7c9 --- /dev/null +++ b/database/migrations/2026_08_14_000000_add_browser_branding_to_tenants_and_website_type.php @@ -0,0 +1,55 @@ +string('site_title')->nullable()->after('scanner_domain'); + $table->unsignedBigInteger('favicon_id')->nullable()->after('footer_logo'); + + $table->foreign('favicon_id') + ->references('id') + ->on('attachments') + ->nullOnDelete(); + }); + + Schema::table('tenants', function (Blueprint $table): void { + $table->string('site_title')->nullable()->after('dominio'); + $table->unsignedBigInteger('favicon_id')->nullable()->after('footer_logo_id'); + + $table->foreign('favicon_id') + ->references('id') + ->on('attachments') + ->nullOnDelete(); + }); + + DB::table('website_type') + ->whereNull('site_title') + ->update(['site_title' => DB::raw('nombre')]); + } + + public function down(): void + { + Schema::table('tenants', function (Blueprint $table): void { + if (Schema::getConnection()->getDriverName() !== 'sqlite') { + $table->dropForeign(['favicon_id']); + } + + $table->dropColumn(['site_title', 'favicon_id']); + }); + + Schema::table('website_type', function (Blueprint $table): void { + if (Schema::getConnection()->getDriverName() !== 'sqlite') { + $table->dropForeign(['favicon_id']); + } + + $table->dropColumn(['site_title', 'favicon_id']); + }); + } +}; diff --git a/database/migrations/2026_08_14_010000_set_seeded_tenant_browser_branding.php b/database/migrations/2026_08_14_010000_set_seeded_tenant_browser_branding.php new file mode 100644 index 0000000..d819872 --- /dev/null +++ b/database/migrations/2026_08_14_010000_set_seeded_tenant_browser_branding.php @@ -0,0 +1,134 @@ + */ + private const BRANDING = [ + 'fiesta_futbol_infantil' => [ + 'title' => 'Fiesta Fútbol Infantil', + 'image' => 'images/tennants/fiesta_futbol_infantil/futbol_infantil_favicon.png', + 'filename' => 'futbol_infantil_favicon.png', + ], + 'desfile_pura_tendencia' => [ + 'title' => 'Desfile Pura Tendencia', + 'image' => 'images/tennants/desfile_pura_tendencia/pura_tendencia_favicon.png', + 'filename' => 'pura_tendencia_favicon.png', + ], + ]; + + /** @var list */ + private array $storedPaths = []; + + public function up(): void + { + try { + DB::transaction(function (): void { + foreach (self::BRANDING as $tenantCode => $branding) { + $tenant = DB::table('tenants') + ->where('codigo', $tenantCode) + ->first(['id', 'favicon_id']); + + if ($tenant === null) { + continue; + } + + $faviconId = $tenant->favicon_id; + $currentFilename = $faviconId === null + ? null + : DB::table('attachments')->where('id', $faviconId)->value('filename'); + + if ($currentFilename !== $branding['filename']) { + $faviconId = $this->storeFavicon( + $tenantCode, + $branding['image'], + $branding['filename'], + ); + } + + DB::table('tenants') + ->where('id', $tenant->id) + ->update([ + 'site_title' => $branding['title'], + 'favicon_id' => $faviconId, + 'updated_at' => now(), + ]); + } + }); + } catch (Throwable $throwable) { + Storage::disk('s3')->delete($this->storedPaths); + + throw $throwable; + } + } + + public function down(): void + { + foreach (self::BRANDING as $tenantCode => $branding) { + $tenant = DB::table('tenants') + ->where('codigo', $tenantCode) + ->first(['id', 'site_title', 'favicon_id']); + + if ($tenant === null) { + continue; + } + + $favicon = $tenant->favicon_id === null + ? null + : DB::table('attachments')->where('id', $tenant->favicon_id)->first(); + + DB::table('tenants') + ->where('id', $tenant->id) + ->update([ + 'site_title' => $tenant->site_title === $branding['title'] ? null : $tenant->site_title, + 'favicon_id' => $favicon?->filename === $branding['filename'] ? null : $tenant->favicon_id, + 'updated_at' => now(), + ]); + + if ($favicon?->filename === $branding['filename']) { + Storage::disk('s3')->delete($favicon->path); + DB::table('attachments')->where('id', $favicon->id)->delete(); + } + } + } + + private function storeFavicon(string $tenantCode, string $relativePath, string $filename): int + { + $sourcePath = public_path($relativePath); + + if (! is_file($sourcePath)) { + throw new RuntimeException("Favicon not found at path: {$sourcePath}"); + } + + $contents = file_get_contents($sourcePath); + + if ($contents === false) { + throw new RuntimeException("Could not read favicon at path: {$sourcePath}"); + } + + $key = (string) Str::uuid(); + $storedPath = "tenants/{$tenantCode}/{$key}.png"; + + if (! Storage::disk('s3')->put($storedPath, $contents)) { + throw new RuntimeException("Could not store favicon at path: {$storedPath}"); + } + + $this->storedPaths[] = $storedPath; + + return DB::table('attachments')->insertGetId([ + 'key' => $key, + 'path' => $storedPath, + 'filename' => $filename, + 'type' => 'image', + 'mime_type' => 'image/png', + 'extension' => 'png', + 'size' => strlen($contents), + 'created_at' => now(), + 'updated_at' => now(), + ]); + } +}; diff --git a/database/seeders/TenantSeeder.php b/database/seeders/TenantSeeder.php index c478e7d..62be038 100644 --- a/database/seeders/TenantSeeder.php +++ b/database/seeders/TenantSeeder.php @@ -104,6 +104,7 @@ class TenantSeeder extends Seeder 'codigo' => 'fiesta_futbol_infantil', 'nombre' => 'Fiesta Fútbol Infantil', 'dominio' => $fiestaDomain, + 'site_title' => 'Fiesta Fútbol Infantil', 'primary_color' => '#00973F', 'secondary_color' => '#A0A0A0', 'danger_color' => '#FF8888', @@ -121,6 +122,10 @@ class TenantSeeder extends Seeder 'images/tennants/fiesta_futbol_infantil/futbol_infantil_footer.png', 'futbol_infantil_footer.png', ), + 'favicon' => $this->uploadedImage( + 'images/tennants/fiesta_futbol_infantil/futbol_infantil_favicon.png', + 'futbol_infantil_favicon.png', + ), 'social_media' => self::SOCIAL_MEDIA, 'website_type_code' => 'onticket', 'extras' => [ @@ -229,6 +234,18 @@ class TenantSeeder extends Seeder } } + if ( + $tenant->favicon + && $tenant->favicon_id !== $tenant->header_logo_id + && $tenant->favicon_id !== $tenant->footer_logo_id + ) { + try { + $attachmentService->delete($tenant->favicon); + } catch (Throwable) { + // Ignore cleanup errors while recreating demo data. + } + } + $tenant->delete(); } diff --git a/database/seeders/WebsiteTypeSeeder.php b/database/seeders/WebsiteTypeSeeder.php index ade8463..0fdd8c5 100644 --- a/database/seeders/WebsiteTypeSeeder.php +++ b/database/seeders/WebsiteTypeSeeder.php @@ -32,6 +32,7 @@ class WebsiteTypeSeeder extends Seeder 'nombre' => 'ShopIt', 'dominio' => 'localhost', 'scanner_domain' => 'scanner.localhost', + 'site_title' => 'ShopIt', ...self::PRESENTATION, 'site_logo' => $this->onTicketLogo(), 'footer_logo' => $this->onTicketFooterLogo(), @@ -65,6 +66,7 @@ class WebsiteTypeSeeder extends Seeder 'nombre' => 'OnTicket', 'dominio' => 'onticket.localhost', 'scanner_domain' => 'scanner.onticket.localhost', + 'site_title' => 'OnTicket', ...self::PRESENTATION, 'site_logo' => $this->onTicketLogo(), 'footer_logo' => $this->onTicketFooterLogo(), diff --git a/public/images/tennants/desfile_pura_tendencia/pura_tendencia_favicon.png b/public/images/tennants/desfile_pura_tendencia/pura_tendencia_favicon.png new file mode 100644 index 0000000..f44998b Binary files /dev/null and b/public/images/tennants/desfile_pura_tendencia/pura_tendencia_favicon.png differ diff --git a/public/images/tennants/fiesta_futbol_infantil/futbol_infantil_favicon.png b/public/images/tennants/fiesta_futbol_infantil/futbol_infantil_favicon.png new file mode 100644 index 0000000..0a69833 Binary files /dev/null and b/public/images/tennants/fiesta_futbol_infantil/futbol_infantil_favicon.png differ diff --git a/tests/Feature/Migrations/CreateDesfilePuraTendenciaTenantTest.php b/tests/Feature/Migrations/CreateDesfilePuraTendenciaTenantTest.php index 08be0c9..4ed4463 100644 --- a/tests/Feature/Migrations/CreateDesfilePuraTendenciaTenantTest.php +++ b/tests/Feature/Migrations/CreateDesfilePuraTendenciaTenantTest.php @@ -36,6 +36,11 @@ class CreateDesfilePuraTendenciaTenantTest extends TestCase ); $footerBackgroundMigration->up(); + $browserBrandingMigration = require database_path( + 'migrations/2026_08_14_010000_set_seeded_tenant_browser_branding.php' + ); + $browserBrandingMigration->up(); + $this->assertDatabaseHas('tenants', [ 'codigo' => 'desfile_pura_tendencia', 'nombre' => 'Desfile Pura Tendencia', @@ -52,8 +57,25 @@ class CreateDesfilePuraTendenciaTenantTest extends TestCase 'footer_bg_color' => '#D4441C', 'display_categories' => false, 'display_seach_bar' => false, + 'site_title' => 'Desfile Pura Tendencia', ]); + $this->assertDatabaseHas('tenants', [ + 'codigo' => 'fiesta_futbol_infantil', + 'site_title' => 'Fiesta Fútbol Infantil', + ]); + + foreach ([ + 'fiesta_futbol_infantil' => 'futbol_infantil_favicon.png', + 'desfile_pura_tendencia' => 'pura_tendencia_favicon.png', + ] as $tenantCode => $faviconFilename) { + $faviconId = DB::table('tenants')->where('codigo', $tenantCode)->value('favicon_id'); + $favicon = DB::table('attachments')->where('id', $faviconId)->sole(); + + $this->assertSame($faviconFilename, $favicon->filename); + Storage::disk('s3')->assertExists($favicon->path); + } + $eventDate = DB::table('event_dates') ->where('tenant_code', 'desfile_pura_tendencia') ->sole(); @@ -104,6 +126,7 @@ class CreateDesfilePuraTendenciaTenantTest extends TestCase 'desfile_pura_tendencia_footer.png', 'desfile_pura_tendencia_hero.png', 'desfile_pura_tendencia_footer_background.png', + 'pura_tendencia_favicon.png', 'entrada_pasarela.png', ] as $filename) { $path = DB::table('attachments')->where('filename', $filename)->value('path'); diff --git a/tests/Feature/Tenant/BootstrapTenantControllerTest.php b/tests/Feature/Tenant/BootstrapTenantControllerTest.php index ef7b114..bacaf0c 100644 --- a/tests/Feature/Tenant/BootstrapTenantControllerTest.php +++ b/tests/Feature/Tenant/BootstrapTenantControllerTest.php @@ -127,6 +127,62 @@ class BootstrapTenantControllerTest extends TestCase ->assertJsonPath('data.dominio', 'acme.com'); } + public function test_it_resolves_browser_branding_from_the_tenant_before_the_website_type(): void + { + $typeFavicon = Attachment::query()->create([ + 'key' => (string) Str::uuid(), + 'path' => 'website-types/type-favicon.png', + 'filename' => 'type-favicon.png', + 'type' => AttachmentType::Image, + 'mime_type' => 'image/png', + ]); + $tenantFavicon = Attachment::query()->create([ + 'key' => (string) Str::uuid(), + 'path' => 'tenants/tenant-favicon.png', + 'filename' => 'tenant-favicon.png', + 'type' => AttachmentType::Image, + 'mime_type' => 'image/png', + ]); + $websiteType = WebsiteType::query()->create([ + 'codigo' => 'store', + 'nombre' => 'Tienda', + 'site_title' => 'Website type title', + 'favicon_id' => $typeFavicon->id, + ]); + $tenant = $this->createTenant([ + 'website_type_code' => $websiteType->codigo, + ]); + + $typeResponse = $this->getJson('/api/tenants/bootstrap/acme.com'); + + $typeResponse + ->assertOk() + ->assertJsonPath('data.site_title', 'Website type title'); + $this->assertStringContainsString($typeFavicon->key, $typeResponse->json('data.favicon')); + + $tenant->update([ + 'site_title' => 'Tenant title', + 'favicon_id' => $tenantFavicon->id, + ]); + + $tenantResponse = $this->getJson('/api/tenants/bootstrap/acme.com'); + + $tenantResponse + ->assertOk() + ->assertJsonPath('data.site_title', 'Tenant title'); + $this->assertStringContainsString($tenantFavicon->key, $tenantResponse->json('data.favicon')); + } + + public function test_it_uses_default_browser_branding_when_none_is_configured(): void + { + $this->createTenant(); + + $this->getJson('/api/tenants/bootstrap/acme.com') + ->assertOk() + ->assertJsonPath('data.site_title', 'ShopitFront') + ->assertJsonPath('data.favicon', null); + } + public function test_it_returns_only_the_tenant_categories_in_the_bootstrap(): void { $tenant = $this->createTenant(); @@ -755,21 +811,28 @@ class BootstrapTenantControllerTest extends TestCase 'footer_bg_color' => '#444444', 'header_logo' => $base64Image, 'footer_logo' => $ftrUuid, + 'site_title' => 'Acme Store', + 'favicon' => $base64Image, ]); $response->assertCreated(); - $tenant = Tenant::query()->with('headerLogo')->where('codigo', 'acme')->firstOrFail(); + $tenant = Tenant::query()->with(['headerLogo', 'favicon'])->where('codigo', 'acme')->firstOrFail(); $this->assertNotNull($tenant->header_logo_id); + $this->assertNotNull($tenant->favicon_id); $headerUrl = $response->json('data.header_logo'); + $faviconUrl = $response->json('data.favicon'); + $response->assertJsonPath('data.site_title', 'Acme Store'); $this->assertStringContainsString($tenant->headerLogo->key, $headerUrl); + $this->assertStringContainsString($tenant->favicon->key, $faviconUrl); $this->assertTrue( str_contains($headerUrl, 'Expires=') || str_contains($headerUrl, 'expiration=') || str_contains($headerUrl, 'X-Amz-Expires=') ); $this->assertDatabaseHas('attachments', ['key' => $tenant->headerLogo->key]); + $this->assertDatabaseHas('attachments', ['key' => $tenant->favicon->key]); } } diff --git a/tests/Feature/Tenant/WebsiteTypeServiceTest.php b/tests/Feature/Tenant/WebsiteTypeServiceTest.php index 94767ab..176f6e8 100644 --- a/tests/Feature/Tenant/WebsiteTypeServiceTest.php +++ b/tests/Feature/Tenant/WebsiteTypeServiceTest.php @@ -32,20 +32,26 @@ class WebsiteTypeServiceTest extends TestCase 'background_color' => '#f8f8f8', 'border_color' => '#dddddd', 'login_header_footer_color' => '#333333', + 'site_title' => 'Marketplace Central', 'site_logo' => UploadedFile::fake()->image('site-logo.png'), 'footer_logo' => UploadedFile::fake()->image('footer-logo.png'), + 'favicon' => UploadedFile::fake()->image('favicon.png', 32, 32), ]); $siteLogo = $websiteType->siteLogo()->firstOrFail(); $footerLogo = $websiteType->footerLogo()->firstOrFail(); + $favicon = $websiteType->favicon()->firstOrFail(); $this->assertSame('marketplace', $websiteType->codigo); $this->assertSame($siteLogo->id, $websiteType->site_logo); $this->assertSame($footerLogo->id, $websiteType->footer_logo); + $this->assertSame($favicon->id, $websiteType->favicon_id); $this->assertStringStartsWith('website-types/', $siteLogo->path); $this->assertStringStartsWith('website-types/', $footerLogo->path); + $this->assertStringStartsWith('website-types/', $favicon->path); Storage::disk('s3')->assertExists($siteLogo->path); Storage::disk('s3')->assertExists($footerLogo->path); + Storage::disk('s3')->assertExists($favicon->path); $this->assertDatabaseHas('website_type', [ 'codigo' => 'marketplace', 'dominio' => 'marketplace.test', @@ -57,8 +63,10 @@ class WebsiteTypeServiceTest extends TestCase 'background_color' => '#f8f8f8', 'border_color' => '#dddddd', 'login_header_footer_color' => '#333333', + 'site_title' => 'Marketplace Central', 'site_logo' => $siteLogo->id, 'footer_logo' => $footerLogo->id, + 'favicon_id' => $favicon->id, ]); } }