4, '!@lg' => 4, ]; protected ?string $pollingInterval = null; /** * @return array */ protected function getStats(): array { $needsAttention = $this->summaryCount( fn (Builder $query): Builder => $query->dashboardNeedsFollowUp(), ); $activeOperations = $this->summaryCount( fn (Builder $query): Builder => $query->active(), ); $failedOrBlocked = $this->summaryCount(fn (Builder $query): Builder => $query ->where('status', OperationRunStatus::Completed->value) ->whereIn('outcome', [ OperationRunOutcome::Failed->value, OperationRunOutcome::Blocked->value, ])); $completedRecently = $this->summaryCount(fn (Builder $query): Builder => $query ->where('status', OperationRunStatus::Completed->value) ->where('completed_at', '>=', now()->subDay())); return [ $this->workbenchStat( key: 'needs-attention', label: 'Needs attention', value: $needsAttention, description: 'Failed, blocked, partial, or stale OperationRuns in scope.', color: $needsAttention > 0 ? 'warning' : 'gray', descriptionIcon: 'heroicon-m-exclamation-triangle', ), $this->workbenchStat( key: 'active-operations', label: 'Active operations', value: $activeOperations, description: 'Queued or running records with trusted progress only.', color: 'info', descriptionIcon: 'heroicon-m-bolt', ), $this->workbenchStat( key: 'failed-or-blocked', label: 'Failed or blocked', value: $failedOrBlocked, description: 'Terminal execution records that need review before retrying.', color: 'danger', descriptionIcon: 'heroicon-m-no-symbol', ), $this->workbenchStat( key: 'completed-recently', label: 'Completed recently', value: $completedRecently, description: 'Recent execution results, not environment or governance health.', color: 'success', descriptionIcon: 'heroicon-m-check-circle', ), ]; } private function workbenchStat( string $key, string $label, int $value, string $description, string $color, string $descriptionIcon, ): Stat { return Stat::make($label, (string) $value) ->description($description) ->descriptionIcon($descriptionIcon, IconPosition::Before) ->color($color) ->extraAttributes([ 'data-testid' => 'operations-workbench-stat-'.$key, 'data-stat-key' => $key, 'data-stat-value' => (string) $value, 'data-stat-color' => $color, 'data-stat-label' => $label, ]); } private function summaryCount(callable $scope): int { $query = $this->scopedOperationRunQuery(); if (! $query instanceof Builder) { return 0; } return (int) $scope($query)->count(); } private function scopedOperationRunQuery(): ?Builder { $workspace = app(WorkspaceContext::class)->currentWorkspace(request()); $user = auth()->user(); if (! $workspace instanceof Workspace || ! $user instanceof User) { return null; } $workspaceId = (int) $workspace->getKey(); $environmentFilter = WorkspaceHubEnvironmentFilter::fromRequest(request(), $workspace); $allowedEnvironmentIds = app(ManagedEnvironmentAccessScopeResolver::class) ->allowedManagedEnvironmentIdsForWorkspace($user, $workspaceId); return OperationRun::query() ->where('workspace_id', $workspaceId) ->when( $allowedEnvironmentIds !== null, function (Builder $query) use ($allowedEnvironmentIds): Builder { return $query->where(function (Builder $query) use ($allowedEnvironmentIds): void { $query->whereNull('managed_environment_id'); if ($allowedEnvironmentIds !== []) { $query->orWhereIn( 'managed_environment_id', array_values(array_unique(array_map('intval', $allowedEnvironmentIds))), ); } }); }, ) ->when( $environmentFilter instanceof WorkspaceHubEnvironmentFilter, fn (Builder $query): Builder => $environmentFilter->applyToQuery($query), ); } }