diff --git a/app/Domains/Tenant/Controllers/AdminApp/WebsiteExtraController.php b/app/Domains/Tenant/Controllers/AdminApp/WebsiteExtraController.php index 277962d..835329b 100644 --- a/app/Domains/Tenant/Controllers/AdminApp/WebsiteExtraController.php +++ b/app/Domains/Tenant/Controllers/AdminApp/WebsiteExtraController.php @@ -65,7 +65,10 @@ class WebsiteExtraController extends Controller return WebsiteExtrasResource::make( $this->loadTenant($request->user()) - ); + )->additional([ + 'code' => 'tenant.website_extra_toggled', + 'message' => __('api.tenant.website_extra_toggled'), + ]); } private function loadTenant(User $user): Tenant diff --git a/app/Domains/Tenant/Services/WebsiteExtraService.php b/app/Domains/Tenant/Services/WebsiteExtraService.php index e27cfd5..610c377 100644 --- a/app/Domains/Tenant/Services/WebsiteExtraService.php +++ b/app/Domains/Tenant/Services/WebsiteExtraService.php @@ -174,7 +174,15 @@ class WebsiteExtraService $websiteExtra = $tenant->websiteExtras() ->where('website_type_extra_id', $definition->id) ->lockForUpdate() - ->firstOrFail(); + ->first(); + + if (! $websiteExtra) { + return $tenant->websiteExtras()->create([ + 'website_type_extra_id' => $definition->id, + 'config' => [], + 'is_enabled' => true, + ]); + } $websiteExtra->update([ 'is_enabled' => ! $websiteExtra->is_enabled, diff --git a/database/seeders/TenantSeeder.php b/database/seeders/TenantSeeder.php index 0a32d45..1882eed 100644 --- a/database/seeders/TenantSeeder.php +++ b/database/seeders/TenantSeeder.php @@ -120,7 +120,6 @@ class TenantSeeder extends Seeder 'extras' => [ 'heroConfig' => [ 'title_html' => '
Viví una jornada inolvidable de fútbol infantil.
', 'button_text' => 'Comprar entradas', 'button_href' => '/tickets', 'background_image_id' => $this->uploadedImage( @@ -133,10 +132,21 @@ class TenantSeeder extends Seeder 'location' => 'Rosario, Santa Fe', 'dates_text' => '9, 10, 11 y 12 de Octubre 2026', 'dates' => [ - '2026-12-05', - '2026-12-06', + [ + 'date' => '2026-12-05', + 'start_time' => '09:00', + 'end_time' => '18:00', + ], + [ + 'date' => '2026-12-06', + 'start_time' => '09:00', + 'end_time' => '18:00', + ], ], ], + 'additionalInfoConfig' => [ + 'description' => 'Viví una jornada inolvidable de fútbol infantil.
', + ], ], ]); } diff --git a/database/seeders/WebsiteTypeSeeder.php b/database/seeders/WebsiteTypeSeeder.php index 556fe7d..303f74d 100644 --- a/database/seeders/WebsiteTypeSeeder.php +++ b/database/seeders/WebsiteTypeSeeder.php @@ -107,7 +107,30 @@ class WebsiteTypeSeeder extends Seeder 'location' => 'nullable|string', 'dates_text' => 'nullable|string|max:255', 'dates' => 'nullable|array', - 'dates.*' => 'required|date_format:Y-m-d|distinct', + 'dates.*' => 'required|array:date,start_time,end_time', + 'dates.*.date' => 'required|date_format:Y-m-d|distinct', + 'dates.*.start_time' => 'required|date_format:H:i', + 'dates.*.end_time' => 'required|date_format:H:i', + 'contact' => 'nullable|array:whatsapp_url,instagram_url,facebook_url', + 'contact.whatsapp_url' => 'nullable|url|max:2048', + 'contact.instagram_url' => 'nullable|url|max:2048', + 'contact.facebook_url' => 'nullable|url|max:2048', + ], + 'transforms' => [], + ], + ], + ); + + $onTicket->extras()->updateOrCreate( + ['codigo' => 'additionalInfoConfig'], + [ + 'nombre' => 'Información adicional', + 'descripcion' => 'Información adicional visible en la página del evento.', + 'is_required' => false, + 'config_schema' => [ + 'request_rules' => [ + '$' => 'required|array', + 'description' => 'nullable|string', ], 'transforms' => [], ], diff --git a/lang/en/api.php b/lang/en/api.php index 9740577..9cdf268 100644 --- a/lang/en/api.php +++ b/lang/en/api.php @@ -94,6 +94,9 @@ return [ 'schema_required' => 'The schema is required for static menus.', 'not_found' => 'The specified menu does not exist.', ], + 'tenant' => [ + 'website_extra_toggled' => 'The section status was updated successfully.', + ], 'errors' => [ 'forbidden' => 'You do not have permission to perform this action.', 'not_found' => 'The requested resource was not found.', diff --git a/lang/es/api.php b/lang/es/api.php index 50e2416..cbcfaef 100644 --- a/lang/es/api.php +++ b/lang/es/api.php @@ -94,6 +94,9 @@ return [ 'schema_required' => 'El schema es obligatorio para los menús estáticos.', 'not_found' => 'El menú indicado no existe.', ], + 'tenant' => [ + 'website_extra_toggled' => 'El estado de la sección se actualizó correctamente.', + ], 'errors' => [ 'forbidden' => 'No tienes permiso para realizar esta acción.', 'not_found' => 'El recurso solicitado no fue encontrado.', diff --git a/tests/Feature/Seeders/TenantSeederTest.php b/tests/Feature/Seeders/TenantSeederTest.php index 106db33..06aa3f8 100644 --- a/tests/Feature/Seeders/TenantSeederTest.php +++ b/tests/Feature/Seeders/TenantSeederTest.php @@ -84,12 +84,13 @@ class TenantSeederTest extends TestCase $extras = $fiesta->websiteExtras->keyBy('websiteTypeExtra.codigo'); $this->assertEqualsCanonicalizing( - ['heroConfig', 'eventConfig'], + ['heroConfig', 'eventConfig', 'additionalInfoConfig'], $extras->keys()->all(), ); $heroConfig = $extras->get('heroConfig')->config; $this->assertSame('Viví una jornada inolvidable de fútbol infantil.
', + ], $extras->get('additionalInfoConfig')->config); } } diff --git a/tests/Feature/Seeders/WebsiteTypeSeederTest.php b/tests/Feature/Seeders/WebsiteTypeSeederTest.php index 4cffc3a..8531865 100644 --- a/tests/Feature/Seeders/WebsiteTypeSeederTest.php +++ b/tests/Feature/Seeders/WebsiteTypeSeederTest.php @@ -78,7 +78,7 @@ class WebsiteTypeSeederTest extends TestCase $this->assertNotSame($shopIt->site_logo, $onTicket->site_logo); $this->assertNotSame($shopIt->footer_logo, $onTicket->footer_logo); $this->assertEqualsCanonicalizing( - ['heroConfig', 'eventConfig'], + ['heroConfig', 'eventConfig', 'additionalInfoConfig'], $onTicket->extras->pluck('codigo')->all(), ); @@ -108,9 +108,27 @@ class WebsiteTypeSeederTest extends TestCase 'location' => 'nullable|string', 'dates_text' => 'nullable|string|max:255', 'dates' => 'nullable|array', - 'dates.*' => 'required|date_format:Y-m-d|distinct', + 'dates.*' => 'required|array:date,start_time,end_time', + 'dates.*.date' => 'required|date_format:Y-m-d|distinct', + 'dates.*.start_time' => 'required|date_format:H:i', + 'dates.*.end_time' => 'required|date_format:H:i', + 'contact' => 'nullable|array:whatsapp_url,instagram_url,facebook_url', + 'contact.whatsapp_url' => 'nullable|url|max:2048', + 'contact.instagram_url' => 'nullable|url|max:2048', + 'contact.facebook_url' => 'nullable|url|max:2048', ], 'transforms' => [], ], $eventSchema); + + $additionalInfoSchema = $onTicket->extras + ->firstWhere('codigo', 'additionalInfoConfig') + ->config_schema; + $this->assertSame([ + 'request_rules' => [ + '$' => 'required|array', + 'description' => 'nullable|string', + ], + 'transforms' => [], + ], $additionalInfoSchema); } } diff --git a/tests/Feature/Tenant/AdminAppWebsiteExtraControllerTest.php b/tests/Feature/Tenant/AdminAppWebsiteExtraControllerTest.php index 7a6f0ab..94f288f 100644 --- a/tests/Feature/Tenant/AdminAppWebsiteExtraControllerTest.php +++ b/tests/Feature/Tenant/AdminAppWebsiteExtraControllerTest.php @@ -195,6 +195,8 @@ class AdminAppWebsiteExtraControllerTest extends TestCase $this->patchJson('/api/v1/adminapp/tenant/website-extras/contactConfig/toggle') ->assertOk() + ->assertJsonPath('code', 'tenant.website_extra_toggled') + ->assertJsonPath('message', 'El estado de la sección se actualizó correctamente.') ->assertJsonPath('data.definitions.contactConfig.is_enabled', true); $this->assertTrue($websiteExtra->refresh()->is_enabled); @@ -206,13 +208,21 @@ class AdminAppWebsiteExtraControllerTest extends TestCase $this->assertFalse($websiteExtra->refresh()->is_enabled); } - public function test_toggle_returns_not_found_for_an_extra_without_a_tenant_value(): void + public function test_toggle_enables_an_extra_without_a_tenant_value(): void { $tenant = $this->createTenant('acme'); Sanctum::actingAs($this->createAdminAppUser($tenant)); $this->patchJson('/api/v1/adminapp/tenant/website-extras/contactConfig/toggle') - ->assertNotFound(); + ->assertOk() + ->assertJsonPath('data.definitions.contactConfig.is_enabled', true) + ->assertJsonPath('data.extras.contactConfig', []); + + $this->assertDatabaseHas('websites_extras', [ + 'website_code' => $tenant->codigo, + 'website_type_extra_id' => $this->websiteType->extras()->firstOrFail()->id, + 'is_enabled' => true, + ]); } private function createTenant(string $code): Tenant diff --git a/tests/Feature/Tenant/StoreTenantWithExtrasTest.php b/tests/Feature/Tenant/StoreTenantWithExtrasTest.php index 4bd1b3e..6e9ca4e 100644 --- a/tests/Feature/Tenant/StoreTenantWithExtrasTest.php +++ b/tests/Feature/Tenant/StoreTenantWithExtrasTest.php @@ -31,7 +31,18 @@ class StoreTenantWithExtrasTest extends TestCase 'title' => 'Festival', 'location' => 'Buenos Aires', 'dates_text' => '10 y 11 de octubre de 2026', - 'dates' => ['2026-10-10', '2026-10-11'], + 'dates' => [ + [ + 'date' => '2026-10-10', + 'start_time' => '09:00', + 'end_time' => '18:00', + ], + [ + 'date' => '2026-10-11', + 'start_time' => '10:00', + 'end_time' => '17:00', + ], + ], ], ], ])); @@ -52,7 +63,18 @@ class StoreTenantWithExtrasTest extends TestCase 'title' => 'Festival', 'location' => 'Buenos Aires', 'dates_text' => '10 y 11 de octubre de 2026', - 'dates' => ['2026-10-10', '2026-10-11'], + 'dates' => [ + [ + 'date' => '2026-10-10', + 'start_time' => '09:00', + 'end_time' => '18:00', + ], + [ + 'date' => '2026-10-11', + 'start_time' => '10:00', + 'end_time' => '17:00', + ], + ], ], $tenant->websiteExtras()->sole()->config ); @@ -80,12 +102,16 @@ class StoreTenantWithExtrasTest extends TestCase 'website_type_code' => 'onticket', 'extras' => [ 'eventConfig' => [ - 'dates' => ['not-a-date'], + 'dates' => [[ + 'date' => 'not-a-date', + 'start_time' => '09:00', + 'end_time' => '18:00', + ]], ], ], ])) ->assertUnprocessable() - ->assertJsonValidationErrors(['extras.eventConfig.dates.0']); + ->assertJsonValidationErrors(['extras.eventConfig.dates.0.date']); $this->assertDatabaseMissing('tenants', ['codigo' => 'festival']); }