TenantAtlas/app/Filament/Pages/Monitoring/Operations.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

188 lines
6.3 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Filament\Pages\Monitoring;
use App\Filament\Resources\OperationRunResource;
use App\Filament\Widgets\Operations\OperationsKpiHeader;
use App\Models\OperationRun;
use App\Models\Tenant;
use App\Support\Filament\CanonicalAdminTenantFilterState;
use App\Support\Navigation\CanonicalNavigationContext;
use App\Support\OperateHub\OperateHubShell;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use App\Support\Workspaces\WorkspaceContext;
use BackedEnum;
use Filament\Actions\Action;
use Filament\Facades\Filament;
use Filament\Forms\Concerns\InteractsWithForms;
use Filament\Forms\Contracts\HasForms;
use Filament\Pages\Page;
use Filament\Tables\Concerns\InteractsWithTable;
use Filament\Tables\Contracts\HasTable;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use UnitEnum;
class Operations extends Page implements HasForms, HasTable
{
use InteractsWithForms;
use InteractsWithTable;
public string $activeTab = 'all';
/**
* @var array<string, mixed>|null
*/
public ?array $navigationContextPayload = null;
protected static bool $isDiscovered = false;
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-queue-list';
protected static string|UnitEnum|null $navigationGroup = 'Monitoring';
protected static ?string $title = 'Operations';
// Must be non-static
protected string $view = 'filament.pages.monitoring.operations';
public function mount(): void
{
$this->navigationContextPayload = is_array(request()->query('nav')) ? request()->query('nav') : null;
app(CanonicalAdminTenantFilterState::class)->sync(
$this->getTableFiltersSessionKey(),
['type', 'initiator_name'],
request(),
);
$this->mountInteractsWithTable();
}
protected function getHeaderWidgets(): array
{
return [
OperationsKpiHeader::class,
];
}
/**
* @return array<Action>
*/
protected function getHeaderActions(): array
{
$operateHubShell = app(OperateHubShell::class);
$navigationContext = $this->navigationContext();
$actions = [
Action::make('operate_hub_scope_operations')
->label($operateHubShell->scopeLabel(request()))
->color('gray')
->disabled(),
];
$activeTenant = $operateHubShell->activeEntitledTenant(request());
if ($navigationContext?->backLinkLabel !== null && $navigationContext->backLinkUrl !== null) {
$actions[] = Action::make('operate_hub_back_to_origin_operations')
->label($navigationContext->backLinkLabel)
->icon('heroicon-o-arrow-left')
->color('gray')
->url($navigationContext->backLinkUrl);
} elseif ($activeTenant instanceof Tenant) {
$actions[] = Action::make('operate_hub_back_to_tenant_operations')
->label('Back to '.$activeTenant->name)
->icon('heroicon-o-arrow-left')
->color('gray')
->url(\App\Filament\Pages\TenantDashboard::getUrl(panel: 'tenant', tenant: $activeTenant));
}
if ($activeTenant instanceof Tenant) {
$actions[] = Action::make('operate_hub_show_all_tenants')
->label('Show all tenants')
->color('gray')
->action(function (): void {
Filament::setTenant(null, true);
app(WorkspaceContext::class)->clearLastTenantId(request());
$this->removeTableFilter('tenant_id');
$this->redirect('/admin/operations');
});
}
return $actions;
}
private function navigationContext(): ?CanonicalNavigationContext
{
if (! is_array($this->navigationContextPayload)) {
return CanonicalNavigationContext::fromRequest(request());
}
$request = request()->duplicate(query: ['nav' => $this->navigationContextPayload]);
return CanonicalNavigationContext::fromRequest($request);
}
public function updatedActiveTab(): void
{
$this->resetPage();
}
public function table(Table $table): Table
{
return OperationRunResource::table($table)
->query(function (): Builder {
$workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request());
$tenantFilter = data_get($this->tableFilters, 'tenant_id.value');
if (! is_numeric($tenantFilter)) {
$tenantFilter = data_get(session()->get($this->getTableFiltersSessionKey(), []), 'tenant_id.value');
}
$query = OperationRun::query()
->with('user')
->latest('id')
->when(
$workspaceId,
fn (Builder $query): Builder => $query->where('workspace_id', (int) $workspaceId),
)
->when(
! $workspaceId,
fn (Builder $query): Builder => $query->whereRaw('1 = 0'),
)
->when(
is_numeric($tenantFilter),
fn (Builder $query): Builder => $query->where('tenant_id', (int) $tenantFilter),
);
return $this->applyActiveTab($query);
});
}
private function applyActiveTab(Builder $query): Builder
{
return match ($this->activeTab) {
'active' => $query->whereIn('status', [
OperationRunStatus::Queued->value,
OperationRunStatus::Running->value,
]),
'succeeded' => $query
->where('status', OperationRunStatus::Completed->value)
->where('outcome', OperationRunOutcome::Succeeded->value),
'partial' => $query
->where('status', OperationRunStatus::Completed->value)
->where('outcome', OperationRunOutcome::PartiallySucceeded->value),
'failed' => $query
->where('status', OperationRunStatus::Completed->value)
->where('outcome', OperationRunOutcome::Failed->value),
default => $query,
};
}
}