*/ protected function getViewData(): array { $tenant = Filament::getTenant(); if (! $tenant instanceof Tenant) { return [ 'pollingInterval' => null, 'items' => [], 'healthyChecks' => [], ]; } $tenantId = (int) $tenant->getKey(); $aggregate = $this->governanceAggregate($tenant); $compareAssessment = $aggregate->summaryAssessment; $items = []; $overdueOpenCount = $aggregate->overdueOpenFindingsCount; $lapsedGovernanceCount = $aggregate->lapsedGovernanceCount; $expiringGovernanceCount = $aggregate->expiringGovernanceCount; $highSeverityCount = $aggregate->highSeverityActiveFindingsCount; $operationsFollowUpCount = (int) OperationRun::query() ->where('tenant_id', $tenantId) ->dashboardNeedsFollowUp() ->count(); $activeRuns = (int) OperationRun::query() ->where('tenant_id', $tenantId) ->healthyActive() ->count(); if ($lapsedGovernanceCount > 0) { $items[] = [ 'key' => 'lapsed_governance', 'title' => 'Lapsed accepted-risk governance', 'body' => "{$lapsedGovernanceCount} accepted-risk finding(s) no longer have valid supporting governance.", 'badge' => 'Governance', 'badgeColor' => 'danger', ...$this->findingsAction( $tenant, 'Open findings', [ 'tab' => 'risk_accepted', 'governance_validity' => FindingException::VALIDITY_MISSING_SUPPORT, ], ), ]; } if ($overdueOpenCount > 0) { $items[] = [ 'key' => 'overdue_findings', 'title' => 'Overdue findings', 'body' => "{$overdueOpenCount} open finding(s) are overdue and still need workflow follow-up.", 'badge' => 'Findings', 'badgeColor' => 'danger', ...$this->findingsAction( $tenant, 'Open findings', ['tab' => 'overdue'], ), ]; } if ($expiringGovernanceCount > 0) { $items[] = [ 'key' => 'expiring_governance', 'title' => 'Expiring accepted-risk governance', 'body' => "{$expiringGovernanceCount} accepted-risk finding(s) need governance review soon.", 'badge' => 'Governance', 'badgeColor' => 'warning', ...$this->findingsAction( $tenant, 'Open findings', [ 'tab' => 'risk_accepted', 'governance_validity' => FindingException::VALIDITY_EXPIRING, ], ), ]; } if ($highSeverityCount > 0) { $items[] = [ 'key' => 'high_severity_active_findings', 'title' => 'High severity active findings', 'body' => "{$highSeverityCount} high or critical finding(s) are still active.", 'badge' => 'Findings', 'badgeColor' => 'danger', ...$this->findingsAction( $tenant, 'Open findings', [ 'tab' => 'needs_action', 'high_severity' => 1, ], ), ]; } if ($compareAssessment->stateFamily !== 'positive') { $items[] = [ 'key' => 'baseline_compare_posture', 'title' => 'Baseline compare posture', 'body' => $compareAssessment->headline, 'supportingMessage' => $compareAssessment->supportingMessage, 'badge' => 'Baseline', 'badgeColor' => $compareAssessment->tone, 'actionLabel' => 'Open Baseline Compare', 'actionUrl' => BaselineCompareLanding::getUrl(panel: 'tenant', tenant: $tenant), ]; } if ($operationsFollowUpCount > 0) { $items[] = [ 'key' => 'operations_follow_up', 'title' => 'Operations need follow-up', 'body' => "{$operationsFollowUpCount} run(s) failed, completed with warnings, or look stalled.", 'badge' => 'Operations', 'badgeColor' => 'danger', 'actionLabel' => 'Open operations', 'actionUrl' => OperationRunLinks::index($tenant, activeTab: 'blocked'), ]; } $healthyChecks = []; if ($items === []) { $healthyChecks = [ [ 'title' => 'Baseline compare looks trustworthy', 'body' => $aggregate->headline, ], [ 'title' => 'No overdue findings', 'body' => 'No open findings are currently overdue for this tenant.', ], [ 'title' => 'Accepted-risk governance is healthy', 'body' => 'No accepted-risk findings currently need governance follow-up.', ], [ 'title' => 'No high severity active findings', 'body' => 'No high severity findings are currently open for this tenant.', ], $activeRuns > 0 ? [ 'title' => 'Operations are active', 'body' => "{$activeRuns} run(s) are active, but nothing currently needs follow-up.", ] : [ 'title' => 'No active operations', 'body' => 'Nothing is currently running for this tenant.', ], ]; } return [ 'pollingInterval' => ActiveRuns::existForTenant($tenant) ? '10s' : null, 'items' => $items, 'healthyChecks' => $healthyChecks, ]; } /** * @param array $parameters * @return array */ private function findingsAction(Tenant $tenant, string $label, array $parameters): array { $url = $this->canOpenFindings($tenant) ? FindingResource::getUrl('index', $parameters, panel: 'tenant', tenant: $tenant) : null; return [ 'actionLabel' => $label, 'actionUrl' => $url, 'actionDisabled' => $url === null, 'helperText' => $url === null ? UiTooltips::INSUFFICIENT_PERMISSION : null, ]; } private function canOpenFindings(Tenant $tenant): bool { $user = auth()->user(); return $user instanceof User && $user->canAccessTenant($tenant) && $user->can(Capabilities::TENANT_FINDINGS_VIEW, $tenant); } private function governanceAggregate(Tenant $tenant): TenantGovernanceAggregate { /** @var TenantGovernanceAggregateResolver $resolver */ $resolver = app(TenantGovernanceAggregateResolver::class); /** @var TenantGovernanceAggregate $aggregate */ $aggregate = $resolver->forTenant($tenant); return $aggregate; } }