diff --git a/app/Domains/Core/Tenant/Controllers/AdminApp/HelpMenuController.php b/app/Domains/Core/Tenant/Controllers/AdminApp/HelpMenuController.php new file mode 100644 index 00000000..e359b4a7 --- /dev/null +++ b/app/Domains/Core/Tenant/Controllers/AdminApp/HelpMenuController.php @@ -0,0 +1,90 @@ +user()->tenant()->firstOrFail(); + $menus = $tenant->menues() + ->whereIn('menues.code', self::MENU_CODES) + ->get() + ->keyBy('code'); + + return response()->json([ + 'data' => collect(self::MENU_CODES)->mapWithKeys( + fn (string $code): array => [$code => [ + 'code' => $code, + 'is_enabled' => (bool) ($menus->get($code)?->pivot?->is_enabled ?? false), + 'static_content' => $menus->get($code)?->pivot?->static_content + ?? $this->emptyContent($code), + ]] + ), + ]); + } + + public function toggle( + Request $request, + string $menuCode, + ): JsonResponse { + abort_unless(in_array($menuCode, self::MENU_CODES, true), 404); + + $tenant = $request->user()->tenant()->firstOrFail(); + $tenantMenu = $tenant->menues() + ->where('menues.code', $menuCode) + ->firstOrFail(); + $isEnabled = ! $tenantMenu->pivot->is_enabled; + + $tenant->menues()->updateExistingPivot($menuCode, [ + 'is_enabled' => $isEnabled, + ]); + + return response()->json([ + 'data' => [ + 'code' => $tenantMenu->code, + 'is_enabled' => $isEnabled, + ], + ]); + } + + public function updateContent( + UpdateHelpMenuContentRequest $request, + string $menuCode, + ): JsonResponse { + abort_unless(in_array($menuCode, self::MENU_CODES, true), 404); + + $tenant = $request->user()->tenant()->firstOrFail(); + $tenantMenu = $tenant->menues() + ->where('menues.code', $menuCode) + ->firstOrFail(); + $staticContent = $request->validated('static_content'); + + $tenant->menues()->updateExistingPivot($menuCode, [ + 'static_content' => $staticContent, + ]); + + return response()->json([ + 'data' => [ + 'code' => $tenantMenu->code, + 'is_enabled' => (bool) $tenantMenu->pivot->is_enabled, + 'static_content' => $staticContent, + ], + ]); + } + + private function emptyContent(string $menuCode): array + { + return $menuCode === 'help.faq' ? [] : ['html' => '']; + } +} diff --git a/app/Domains/Core/Tenant/Requests/AdminApp/UpdateHelpMenuContentRequest.php b/app/Domains/Core/Tenant/Requests/AdminApp/UpdateHelpMenuContentRequest.php new file mode 100644 index 00000000..a303ab12 --- /dev/null +++ b/app/Domains/Core/Tenant/Requests/AdminApp/UpdateHelpMenuContentRequest.php @@ -0,0 +1,31 @@ + */ + public function rules(): array + { + return match ($this->route('menuCode')) { + 'help.faq' => [ + 'static_content' => ['required', 'array'], + 'static_content.*.pregunta' => ['required', 'string'], + 'static_content.*.respuesta' => ['required', 'string'], + 'static_content.*.is_active' => ['required', 'boolean'], + ], + 'help.terms-and-conditions' => [ + 'static_content' => ['required', 'array'], + 'static_content.html' => ['present', 'nullable', 'string'], + ], + default => [], + }; + } +} diff --git a/app/Domains/Core/Tenant/routes/adminapp.php b/app/Domains/Core/Tenant/routes/adminapp.php index 99f744da..aa35510b 100644 --- a/app/Domains/Core/Tenant/routes/adminapp.php +++ b/app/Domains/Core/Tenant/routes/adminapp.php @@ -1,6 +1,7 @@ group(function (): void { Route::get('brand', [BrandController::class, 'show']); Route::put('brand', [BrandController::class, 'update']); + Route::get('help-menus', [HelpMenuController::class, 'index']); + Route::patch('help-menus/{menuCode}/toggle', [HelpMenuController::class, 'toggle']); + Route::put('help-menus/{menuCode}/content', [HelpMenuController::class, 'updateContent']); Route::get('website-extras', [WebsiteExtraController::class, 'show']); Route::get('website-extras/{websiteExtraCode}', [WebsiteExtraController::class, 'showExtra']); Route::put('website-extras/{websiteExtraCode}', [WebsiteExtraController::class, 'update']); diff --git a/tests/Feature/Tenant/AdminAppHelpMenuControllerTest.php b/tests/Feature/Tenant/AdminAppHelpMenuControllerTest.php new file mode 100644 index 00000000..118a697e --- /dev/null +++ b/tests/Feature/Tenant/AdminAppHelpMenuControllerTest.php @@ -0,0 +1,176 @@ +seed(AuthorizationSeeder::class); + } + + public function test_authentication_is_required(): void + { + $this->getJson('/api/v1/adminapp/tenant/help-menus') + ->assertUnauthorized(); + } + + public function test_adminapp_user_can_read_help_menu_states(): void + { + [$tenant] = $this->tenantWithHelpMenus(); + Sanctum::actingAs($this->createAdminAppUser($tenant)); + + $response = $this->getJson('/api/v1/adminapp/tenant/help-menus') + ->assertOk(); + + $states = $response->json('data'); + $this->assertFalse($states['help.faq']['is_enabled']); + $this->assertTrue($states['help.terms-and-conditions']['is_enabled']); + $this->assertSame('¿Cómo compro?', $states['help.faq']['static_content'][0]['pregunta']); + $this->assertSame(['html' => ''], $states['help.terms-and-conditions']['static_content']); + $this->assertArrayNotHasKey('help.contact', $states); + } + + public function test_toggling_a_help_menu_preserves_its_static_content(): void + { + [$tenant, $faq, $content] = $this->tenantWithHelpMenus(); + Sanctum::actingAs($this->createAdminAppUser($tenant)); + + $this->patchJson('/api/v1/adminapp/tenant/help-menus/help.faq/toggle') + ->assertOk() + ->assertJsonPath('data.code', 'help.faq') + ->assertJsonPath('data.is_enabled', true); + + $pivot = $tenant->menues()->where('menues.code', $faq->code)->firstOrFail()->pivot; + + $this->assertTrue($pivot->is_enabled); + $this->assertSame($content, $pivot->static_content); + } + + public function test_an_unrelated_menu_cannot_be_toggled_from_the_help_endpoint(): void + { + [$tenant] = $this->tenantWithHelpMenus(); + Sanctum::actingAs($this->createAdminAppUser($tenant)); + + $this->patchJson('/api/v1/adminapp/tenant/help-menus/catalog/toggle') + ->assertNotFound(); + } + + public function test_contact_menu_cannot_be_toggled(): void + { + [$tenant] = $this->tenantWithHelpMenus(); + Sanctum::actingAs($this->createAdminAppUser($tenant)); + + $this->patchJson('/api/v1/adminapp/tenant/help-menus/help.contact/toggle') + ->assertNotFound(); + + $this->assertTrue( + $tenant->menues()->where('menues.code', 'help.contact')->firstOrFail()->pivot->is_enabled + ); + } + + public function test_adminapp_user_can_update_faq_content_without_changing_its_state(): void + { + [$tenant] = $this->tenantWithHelpMenus(); + Sanctum::actingAs($this->createAdminAppUser($tenant)); + $content = [[ + 'pregunta' => '¿Cuál es el horario?', + 'respuesta' => 'De lunes a viernes.', + 'is_active' => true, + ]]; + + $this->putJson('/api/v1/adminapp/tenant/help-menus/help.faq/content', [ + 'static_content' => $content, + ]) + ->assertOk() + ->assertJsonPath('data.is_enabled', false) + ->assertJsonPath('data.static_content.0.pregunta', '¿Cuál es el horario?'); + + $pivot = $tenant->menues()->where('menues.code', 'help.faq')->firstOrFail()->pivot; + $this->assertFalse($pivot->is_enabled); + $this->assertSame($content, $pivot->static_content); + } + + public function test_adminapp_user_can_update_terms_content(): void + { + [$tenant] = $this->tenantWithHelpMenus(); + Sanctum::actingAs($this->createAdminAppUser($tenant)); + + $this->putJson('/api/v1/adminapp/tenant/help-menus/help.terms-and-conditions/content', [ + 'static_content' => ['html' => '
Nuevos términos
'], + ]) + ->assertOk() + ->assertJsonPath('data.static_content.html', 'Nuevos términos
'); + + $this->assertSame( + ['html' => 'Nuevos términos
'], + $tenant->menues() + ->where('menues.code', 'help.terms-and-conditions') + ->firstOrFail() + ->pivot + ->static_content + ); + } + + /** @return array{Tenant, Menu, array