*/ protected function getViewData(): array { $tenant = Filament::getTenant(); if (! $tenant instanceof Tenant) { return [ 'pollingInterval' => null, 'backupPosture' => $this->emptyStatPayload('Backup posture'), 'recoveryEvidence' => $this->emptyStatPayload('Recovery evidence'), ]; } $backupHealth = $this->backupHealthAssessment($tenant); $backupHealthAction = $this->resolveBackupHealthAction($tenant, $backupHealth->primaryActionTarget); $recoveryEvidence = $this->recoveryEvidence($tenant); $recoveryAction = $this->resolveRecoveryAction($tenant, $recoveryEvidence); return [ 'pollingInterval' => ActiveRuns::pollingIntervalForTenant($tenant), 'backupPosture' => [ 'label' => 'Backup posture', 'value' => TenantRecoveryTriagePresentation::backupPostureLabel($backupHealth), 'description' => TenantRecoveryTriagePresentation::backupPostureDescription($backupHealth, $backupHealthAction['helperText']), 'color' => TenantRecoveryTriagePresentation::backupPostureTone($backupHealth), 'url' => $backupHealthAction['actionUrl'], ], 'recoveryEvidence' => [ 'label' => 'Recovery evidence', 'value' => TenantRecoveryTriagePresentation::recoveryEvidenceLabel($recoveryEvidence), 'description' => TenantRecoveryTriagePresentation::recoveryEvidenceDescription($recoveryEvidence, $recoveryAction['helperText']), 'color' => TenantRecoveryTriagePresentation::recoveryEvidenceTone($recoveryEvidence), 'url' => $recoveryAction['actionUrl'], ], ]; } /** * @return array{label: string, value: string, description: string|null, color: string, url: string|null} */ private function emptyStatPayload(string $label): array { return [ 'label' => $label, 'value' => '—', 'description' => null, 'color' => 'gray', 'url' => null, ]; } private function backupHealthAssessment(Tenant $tenant): TenantBackupHealthAssessment { /** @var TenantBackupHealthResolver $resolver */ $resolver = app(TenantBackupHealthResolver::class); return $resolver->assess($tenant); } /** * @return array */ private function recoveryEvidence(Tenant $tenant): array { /** @var RestoreSafetyResolver $resolver */ $resolver = app(RestoreSafetyResolver::class); return $resolver->dashboardRecoveryEvidence($tenant); } /** * @return array{actionUrl: string|null, helperText: string|null} */ private function resolveBackupHealthAction(Tenant $tenant, ?BackupHealthActionTarget $target): array { if (! $target instanceof BackupHealthActionTarget) { return ['actionUrl' => null, 'helperText' => null]; } if (! $this->canOpenBackupSurfaces($tenant)) { return ['actionUrl' => null, 'helperText' => UiTooltips::INSUFFICIENT_PERMISSION]; } return match ($target->surface) { BackupHealthActionTarget::SURFACE_BACKUP_SETS_INDEX => [ 'actionUrl' => BackupSetResource::getUrl('index', [ 'backup_health_reason' => $target->reason, ], panel: 'tenant', tenant: $tenant), 'helperText' => null, ], BackupHealthActionTarget::SURFACE_BACKUP_SCHEDULES_INDEX => [ 'actionUrl' => BackupScheduleResource::getUrl('index', [ 'backup_health_reason' => $target->reason, ], panel: 'tenant', tenant: $tenant), 'helperText' => null, ], BackupHealthActionTarget::SURFACE_BACKUP_SET_VIEW => $this->resolveBackupSetAction($tenant, $target), default => ['actionUrl' => null, 'helperText' => null], }; } /** * @return array{actionUrl: string|null, helperText: string|null} */ private function resolveBackupSetAction(Tenant $tenant, BackupHealthActionTarget $target): array { if (! is_numeric($target->recordId)) { return [ 'actionUrl' => BackupSetResource::getUrl('index', [ 'backup_health_reason' => $target->reason, ], panel: 'tenant', tenant: $tenant), 'helperText' => 'The latest backup detail is no longer available.', ]; } try { BackupSetResource::resolveScopedRecordOrFail($target->recordId); return [ 'actionUrl' => BackupSetResource::getUrl('view', [ 'record' => $target->recordId, 'backup_health_reason' => $target->reason, ], panel: 'tenant', tenant: $tenant), 'helperText' => null, ]; } catch (ModelNotFoundException) { return [ 'actionUrl' => BackupSetResource::getUrl('index', [ 'backup_health_reason' => $target->reason, ], panel: 'tenant', tenant: $tenant), 'helperText' => 'The latest backup detail is no longer available.', ]; } } /** * @param array $recoveryEvidence * @return array{actionUrl: string|null, helperText: string|null} */ private function resolveRecoveryAction(Tenant $tenant, array $recoveryEvidence): array { if (! $this->canOpenRestoreHistory($tenant)) { return ['actionUrl' => null, 'helperText' => UiTooltips::INSUFFICIENT_PERMISSION]; } $reason = is_string($recoveryEvidence['reason'] ?? null) && $recoveryEvidence['reason'] !== '' ? $recoveryEvidence['reason'] : 'no_history'; $latestRun = $recoveryEvidence['latest_relevant_restore_run'] ?? null; if (! $latestRun instanceof RestoreRun) { return [ 'actionUrl' => $this->restoreRunListUrl($tenant, $reason), 'helperText' => null, ]; } try { RestoreRunResource::resolveScopedRecordOrFail($latestRun->getKey()); return [ 'actionUrl' => RestoreRunResource::getUrl('view', [ 'record' => (int) $latestRun->getKey(), 'recovery_posture_reason' => $reason, ], panel: 'tenant', tenant: $tenant), 'helperText' => null, ]; } catch (ModelNotFoundException) { return [ 'actionUrl' => $this->restoreRunListUrl($tenant, $reason), 'helperText' => 'The latest restore detail is no longer available.', ]; } } private function canOpenBackupSurfaces(Tenant $tenant): bool { $user = auth()->user(); return $user instanceof User && $user->canAccessTenant($tenant) && $user->can(Capabilities::TENANT_VIEW, $tenant); } private function canOpenRestoreHistory(Tenant $tenant): bool { $user = auth()->user(); return $user instanceof User && $user->canAccessTenant($tenant) && $user->can(Capabilities::TENANT_VIEW, $tenant); } private function restoreRunListUrl(Tenant $tenant, string $reason): string { return RestoreRunResource::getUrl('index', [ 'recovery_posture_reason' => $reason, ], panel: 'tenant', tenant: $tenant); } }