Implements Spec 114 System Console Control Tower pages, widgets, triage actions, directory views, and enterprise polish (badges, repair workspace owners table, health indicator).
117 lines
4.2 KiB
PHP
117 lines
4.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Filament\System\Pages\Directory;
|
|
|
|
use App\Models\OperationRun;
|
|
use App\Models\PlatformUser;
|
|
use App\Models\Tenant;
|
|
use App\Models\Workspace;
|
|
use App\Support\Auth\PlatformCapabilities;
|
|
use App\Support\Badges\BadgeDomain;
|
|
use App\Support\Badges\BadgeRenderer;
|
|
use App\Support\OperationRunOutcome;
|
|
use App\Support\OperationRunStatus;
|
|
use App\Support\System\SystemDirectoryLinks;
|
|
use Filament\Pages\Page;
|
|
use Filament\Tables\Columns\TextColumn;
|
|
use Filament\Tables\Concerns\InteractsWithTable;
|
|
use Filament\Tables\Contracts\HasTable;
|
|
use Filament\Tables\Table;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
|
|
class Workspaces extends Page implements HasTable
|
|
{
|
|
use InteractsWithTable;
|
|
|
|
protected static ?string $navigationLabel = 'Workspaces';
|
|
|
|
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-building-office-2';
|
|
|
|
protected static string|\UnitEnum|null $navigationGroup = 'Directory';
|
|
|
|
protected static ?string $slug = 'directory/workspaces';
|
|
|
|
protected string $view = 'filament.system.pages.directory.workspaces';
|
|
|
|
public static function canAccess(): bool
|
|
{
|
|
$user = auth('platform')->user();
|
|
|
|
return $user instanceof PlatformUser
|
|
&& $user->hasCapability(PlatformCapabilities::DIRECTORY_VIEW);
|
|
}
|
|
|
|
public function mount(): void
|
|
{
|
|
$this->mountInteractsWithTable();
|
|
}
|
|
|
|
public function table(Table $table): Table
|
|
{
|
|
return $table
|
|
->defaultSort('name')
|
|
->query(function (): Builder {
|
|
return Workspace::query()
|
|
->withCount([
|
|
'tenants',
|
|
'tenants as onboarding_tenants_count' => fn (Builder $query): Builder => $query->where('status', Tenant::STATUS_ONBOARDING),
|
|
]);
|
|
})
|
|
->columns([
|
|
TextColumn::make('name')
|
|
->label('Workspace')
|
|
->searchable(),
|
|
TextColumn::make('tenants_count')
|
|
->label('Tenants'),
|
|
TextColumn::make('health')
|
|
->label('Health')
|
|
->state(fn (Workspace $record): string => $this->healthForWorkspace($record))
|
|
->badge()
|
|
->formatStateUsing(BadgeRenderer::label(BadgeDomain::SystemHealth))
|
|
->color(BadgeRenderer::color(BadgeDomain::SystemHealth))
|
|
->icon(BadgeRenderer::icon(BadgeDomain::SystemHealth))
|
|
->iconColor(BadgeRenderer::iconColor(BadgeDomain::SystemHealth)),
|
|
TextColumn::make('failed_runs_24h')
|
|
->label('Failed (24h)')
|
|
->state(fn (Workspace $record): int => (int) OperationRun::query()
|
|
->where('workspace_id', (int) $record->getKey())
|
|
->where('created_at', '>=', now()->subDay())
|
|
->where('status', OperationRunStatus::Completed->value)
|
|
->where('outcome', OperationRunOutcome::Failed->value)
|
|
->count()),
|
|
])
|
|
->recordUrl(fn (Workspace $record): string => SystemDirectoryLinks::workspaceDetail($record))
|
|
->emptyStateHeading('No workspaces found')
|
|
->emptyStateDescription('Workspace inventory will appear here once workspaces are created.');
|
|
}
|
|
|
|
private function healthForWorkspace(Workspace $workspace): string
|
|
{
|
|
$tenantsCount = (int) ($workspace->getAttribute('tenants_count') ?? 0);
|
|
$onboardingTenantsCount = (int) ($workspace->getAttribute('onboarding_tenants_count') ?? 0);
|
|
|
|
if ($tenantsCount === 0) {
|
|
return 'unknown';
|
|
}
|
|
|
|
$hasRecentFailures = OperationRun::query()
|
|
->where('workspace_id', (int) $workspace->getKey())
|
|
->where('created_at', '>=', now()->subDay())
|
|
->where('status', OperationRunStatus::Completed->value)
|
|
->where('outcome', OperationRunOutcome::Failed->value)
|
|
->exists();
|
|
|
|
if ($hasRecentFailures) {
|
|
return 'critical';
|
|
}
|
|
|
|
if ($onboardingTenantsCount > 0) {
|
|
return 'warn';
|
|
}
|
|
|
|
return 'ok';
|
|
}
|
|
}
|