From ffbf342d52cc94144e28b2d2a5113e54a5031691 Mon Sep 17 00:00:00 2001 From: Ahmed Darrazi Date: Fri, 6 Feb 2026 21:44:16 +0100 Subject: [PATCH] feat(spec-077): topbar workspace+tenant dropdowns --- .../filament/partials/context-bar.blade.php | 90 +++++++++++++++---- .../Monitoring/HeaderContextBarTest.php | 33 ++++++- 2 files changed, 101 insertions(+), 22 deletions(-) diff --git a/resources/views/filament/partials/context-bar.blade.php b/resources/views/filament/partials/context-bar.blade.php index 948f282..4814a60 100644 --- a/resources/views/filament/partials/context-bar.blade.php +++ b/resources/views/filament/partials/context-bar.blade.php @@ -2,6 +2,9 @@ use App\Filament\Pages\ChooseWorkspace; use App\Models\Tenant; use App\Models\User; + use App\Models\WorkspaceMembership; + use App\Services\Auth\WorkspaceRoleCapabilityMap; + use App\Support\Auth\Capabilities; use App\Support\Workspaces\WorkspaceContext; use Filament\Facades\Filament; @@ -12,9 +15,29 @@ $user = auth()->user(); + $canSeeAllWorkspaceTenants = false; + if ($user instanceof User && $workspace) { + $roles = WorkspaceRoleCapabilityMap::rolesWithCapability(Capabilities::WORKSPACE_MEMBERSHIP_MANAGE); + + $canSeeAllWorkspaceTenants = WorkspaceMembership::query() + ->where('workspace_id', (int) $workspace->getKey()) + ->where('user_id', (int) $user->getKey()) + ->whereIn('role', $roles) + ->exists(); + } + $tenants = collect(); - if ($user instanceof User) { - $tenants = collect($user->getTenants(Filament::getCurrentOrDefaultPanel())); + if ($user instanceof User && $workspace) { + if ($canSeeAllWorkspaceTenants) { + $tenants = Tenant::query() + ->where('workspace_id', (int) $workspace->getKey()) + ->orderBy('name') + ->get(); + } else { + $tenants = collect($user->getTenants(Filament::getCurrentOrDefaultPanel())) + ->filter(fn ($tenant): bool => $tenant instanceof Tenant && (int) $tenant->workspace_id === (int) $workspace->getKey()) + ->values(); + } } $currentTenant = Filament::getTenant(); @@ -25,41 +48,72 @@ @endphp
- - Workspace: - {{ $workspace?->name ?? '—' }} - + + + + {{ $workspace?->name ?? 'Select workspace' }} + + + + + + Switch workspace + + + @if ($canSeeAllWorkspaceTenants) + + Manage workspaces + + @endif + +
- + {{ $currentTenantName ?? 'Select tenant' }} +
-
Tenant context
+
+ Tenant context + @if ($canSeeAllWorkspaceTenants) + · all workspace tenants + @endif +
@if (! $workspace)
Choose a workspace first.
@elseif ($tenants->isEmpty()) -
No tenants you can access in this workspace.
+
+ {{ $canSeeAllWorkspaceTenants ? 'No tenants exist in this workspace.' : 'No tenants you can access in this workspace.' }} +
@else
diff --git a/tests/Feature/Monitoring/HeaderContextBarTest.php b/tests/Feature/Monitoring/HeaderContextBarTest.php index 25334d8..5e08df9 100644 --- a/tests/Feature/Monitoring/HeaderContextBarTest.php +++ b/tests/Feature/Monitoring/HeaderContextBarTest.php @@ -11,6 +11,8 @@ $tenant = Tenant::factory()->create(['status' => 'active']); [$user, $tenant] = createUserWithTenant($tenant, role: 'owner'); + $workspaceName = $tenant->workspace?->name; + Filament::setTenant(null, true); $this->actingAs($user) @@ -22,9 +24,10 @@ ]) ->get('/admin/operations') ->assertOk() - ->assertSee('Workspace:') - ->assertSee('Tenant:') - ->assertSee('Select tenant…') + ->assertSee($workspaceName ?? 'Select workspace') + ->assertSee('Select tenant') + ->assertSee('Search tenants…') + ->assertSee('Switch workspace') ->assertSee('admin/select-tenant') ->assertSee('Clear tenant context') ->assertSee($tenant->getFilamentName()); @@ -39,7 +42,7 @@ it('filters the header tenant picker to tenants the user can access', function (): void { $tenantA = Tenant::factory()->create(['status' => 'active']); - [$user, $tenantA] = createUserWithTenant($tenantA, role: 'owner'); + [$user, $tenantA] = createUserWithTenant($tenantA, role: 'owner', workspaceRole: 'readonly'); $tenantB = Tenant::factory()->create([ 'status' => 'active', @@ -59,6 +62,28 @@ ->assertDontSee($tenantB->getFilamentName()); }); +it('shows all workspace tenants in the header tenant picker for workspace owners', function (): void { + $tenantA = Tenant::factory()->create(['status' => 'active']); + [$user, $tenantA] = createUserWithTenant($tenantA, role: 'owner', workspaceRole: 'owner'); + + $tenantB = Tenant::factory()->create([ + 'status' => 'active', + 'workspace_id' => (int) $tenantA->workspace_id, + 'name' => 'ZZZ-UNASSIGNED-TENANT-NAME-12345', + ]); + + Filament::setTenant(null, true); + + $this->actingAs($user) + ->withSession([ + WorkspaceContext::SESSION_KEY => (int) $tenantA->workspace_id, + ]) + ->get('/admin/operations') + ->assertOk() + ->assertSee($tenantA->getFilamentName()) + ->assertSee($tenantB->getFilamentName()); +}); + it('does not implicitly switch tenant when opening canonical operation deep links', function (): void { $tenantA = Tenant::factory()->create(['status' => 'active']); [$user, $tenantA] = createUserWithTenant($tenantA, role: 'owner');