TenantAtlas/app/Filament/Pages/Workspaces/ManagedTenantsLanding.php
ahmido 641bb4afde feat: implement tenant lifecycle operability semantics (#172)
## Summary
- implement Spec 143 tenant lifecycle, operability, and tenant-context semantics across chooser, tenant management, onboarding, and canonical operation viewers
- add centralized tenant lifecycle and operability support types, audit action coverage, and lifecycle-aware badge and action handling
- add feature and unit coverage for tenant chooser eligibility, global search scoping, canonical operation access, onboarding authorization, and lifecycle presentation

## Testing
- vendor/bin/sail artisan test --compact
- vendor/bin/sail bin pint --dirty --format agent

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #172
2026-03-15 09:08:36 +00:00

101 lines
2.6 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Filament\Pages\Workspaces;
use App\Filament\Pages\ChooseTenant;
use App\Filament\Resources\TenantResource;
use App\Models\Tenant;
use App\Models\User;
use App\Models\Workspace;
use App\Services\Tenants\TenantOperabilityService;
use Filament\Pages\Page;
use Illuminate\Database\Eloquent\Collection;
class ManagedTenantsLanding extends Page
{
protected static bool $shouldRegisterNavigation = false;
protected static bool $isDiscovered = false;
protected static string $layout = 'filament-panels::components.layout.simple';
protected static ?string $title = 'Managed tenants';
protected string $view = 'filament.pages.workspaces.managed-tenants-landing';
public Workspace $workspace;
/**
* The Filament simple layout renders the topbar by default, which includes
* lazy-loaded database notifications. On this workspace-scoped landing page,
* those background Livewire requests currently 404.
*/
protected function getLayoutData(): array
{
return [
'hasTopbar' => false,
];
}
public function mount(Workspace $workspace): void
{
$this->workspace = $workspace;
}
/**
* @return Collection<int, Tenant>
*/
public function getTenants(): Collection
{
$user = auth()->user();
if (! $user instanceof User) {
return Tenant::query()->whereRaw('1 = 0')->get();
}
$tenantIds = $user->tenantMemberships()
->pluck('tenant_id');
return Tenant::query()
->withTrashed()
->whereIn('id', $tenantIds)
->where('workspace_id', $this->workspace->getKey())
->orderBy('name')
->get()
->filter(fn (Tenant $tenant): bool => app(TenantOperabilityService::class)->canViewTenantSurface($tenant))
->values();
}
public function goToChooseTenant(): void
{
$this->redirect(ChooseTenant::getUrl());
}
public function openTenant(int $tenantId): void
{
$user = auth()->user();
if (! $user instanceof User) {
abort(403);
}
$tenant = Tenant::query()
->withTrashed()
->where('workspace_id', $this->workspace->getKey())
->whereKey($tenantId)
->first();
if (! $tenant instanceof Tenant) {
abort(404);
}
if (! $user->canAccessTenant($tenant)) {
abort(404);
}
$this->redirect(TenantResource::getUrl('view', ['record' => $tenant]));
}
}