diff --git a/app/Services/Care/SpecialtyModuleService.php b/app/Services/Care/SpecialtyModuleService.php index 8d0d07b..38ef9d0 100644 --- a/app/Services/Care/SpecialtyModuleService.php +++ b/app/Services/Care/SpecialtyModuleService.php @@ -180,6 +180,32 @@ class SpecialtyModuleService return $out; } + /** + * Whether the sidebar should show the Specialty nav group. + * + * Desk specialists only receive their own matching module(s), so a + * Specialty → Dentistry (etc.) group is redundant — they use dashboard + * cards / direct routes instead. GPs, nurses, and other multi-module + * roles keep the group for referral and cross-module access. + */ + public function shouldShowSpecialtyNav(Organization $organization, ?Member $member): bool + { + if (! $member) { + return false; + } + + $modules = $this->enabledModulesForMember($organization, $member); + if ($modules === []) { + return false; + } + + if ($this->isDeskSpecialist($organization, $member)) { + return false; + } + + return true; + } + /** * @return 'general'|'limited'|'restricted' */ diff --git a/resources/views/partials/sidebar.blade.php b/resources/views/partials/sidebar.blade.php index d1aab9f..7f84716 100644 --- a/resources/views/partials/sidebar.blade.php +++ b/resources/views/partials/sidebar.blade.php @@ -78,22 +78,25 @@ } // Specialty modules — role-gated clinical surfaces for floor staff. + // Desk specialists skip this group (redundant single-desk list); dashboard cards remain. if ($permissions->handlesFloorCare($member) && $organization) { $specialtyService = app(\App\Services\Care\SpecialtyModuleService::class); $specialtyService->ensureDefaultModulesProvisioned( $organization, (string) $organization->owner_ref, ); - foreach ($specialtyService->enabledModulesForMember($organization, $member) as $item) { - $key = $item['key']; - $label = $item['definition']['nav_label'] ?? $item['definition']['label'] ?? $key; - $nav[] = [ - 'name' => $label, - 'route' => route('care.specialty.workspace', $key), - 'active' => request()->routeIs('care.specialty.*') && request()->route('module') === $key, - 'icon' => $specialtyService->iconSvgPath($key), - 'group' => 'specialty', - ]; + if ($specialtyService->shouldShowSpecialtyNav($organization, $member)) { + foreach ($specialtyService->enabledModulesForMember($organization, $member) as $item) { + $key = $item['key']; + $label = $item['definition']['nav_label'] ?? $item['definition']['label'] ?? $key; + $nav[] = [ + 'name' => $label, + 'route' => route('care.specialty.workspace', $key), + 'active' => request()->routeIs('care.specialty.*') && request()->route('module') === $key, + 'icon' => $specialtyService->iconSvgPath($key), + 'group' => 'specialty', + ]; + } } } } diff --git a/tests/Feature/CareSpecialtyAccessTest.php b/tests/Feature/CareSpecialtyAccessTest.php index 90c5f6a..0036e5a 100644 --- a/tests/Feature/CareSpecialtyAccessTest.php +++ b/tests/Feature/CareSpecialtyAccessTest.php @@ -189,15 +189,45 @@ class CareSpecialtyAccessTest extends TestCase $this->assertNotContains('blood_bank', $enabledKeys); $this->assertNotContains('cardiology', $enabledKeys); + // Single-desk specialists: no Specialty sidebar group (dashboard cards remain). + $this->assertFalse($this->modules->shouldShowSpecialtyNav($this->organization, $member)); + $this->actingAs($doctor) ->get(route('care.dashboard')) ->assertOk() ->assertSee('Dentistry') + ->assertSee('Specialty modules') + ->assertDontSee('>Specialty

', false) ->assertDontSee('Emergency') ->assertDontSee('Cardiology') ->assertDontSee('Blood Bank'); } + public function test_gp_keeps_specialty_sidebar_nav(): void + { + [$doctor, $member] = $this->makeStaff('doctor', 'gp-nav'); + Practitioner::create([ + 'owner_ref' => $this->owner->public_id, + 'organization_id' => $this->organization->id, + 'branch_id' => $this->branch->id, + 'member_id' => $member->id, + 'user_ref' => $doctor->public_id, + 'name' => 'Dr GP Nav', + 'specialty' => 'General Practice', + 'is_active' => true, + ]); + + $this->assertFalse($this->modules->isDeskSpecialist($this->organization, $member)); + $this->assertTrue($this->modules->shouldShowSpecialtyNav($this->organization, $member)); + + $this->actingAs($doctor) + ->get(route('care.dashboard')) + ->assertOk() + ->assertSee('>Specialty

', false) + ->assertSee('Emergency') + ->assertSee('Dentistry'); + } + public function test_dentistry_specialty_does_not_match_ent_keyword(): void { [$doctor, $member] = $this->makeStaff('doctor', 'dentist-ent-trap'); @@ -340,10 +370,12 @@ class CareSpecialtyAccessTest extends TestCase $this->assertContains('infusion', $enabledForMember); $this->assertNotContains('cardiology', $enabledForMember); $this->assertNotContains('dentistry', $enabledForMember); + $this->assertTrue($this->modules->shouldShowSpecialtyNav($this->organization, $pharmacistMember)); $this->actingAs($pharmacistUser) ->get(route('care.dashboard')) ->assertOk() + ->assertSee('>Specialty

', false) ->assertSee('Emergency') ->assertDontSee('Cardiology'); }