'evidence_overview', 'surfaceType' => 'simple_monitoring', 'stateFields' => [ [ 'stateKey' => 'environment_id', 'stateClass' => 'contextual_prefilter', 'carrier' => 'query_param', 'queryRole' => 'durable_restorable', 'shareable' => true, 'restorableOnRefresh' => true, 'tenantSensitive' => true, 'invalidFallback' => 'discard_and_continue', ], [ 'stateKey' => 'search', 'stateClass' => 'contextual_prefilter', 'carrier' => 'query_param', 'queryRole' => 'durable_restorable', 'shareable' => true, 'restorableOnRefresh' => true, 'tenantSensitive' => false, 'invalidFallback' => 'discard_and_continue', ], [ 'stateKey' => 'tableFilters', 'stateClass' => 'shareable_restorable', 'carrier' => 'session', 'queryRole' => 'unsupported', 'shareable' => false, 'restorableOnRefresh' => true, 'tenantSensitive' => true, 'invalidFallback' => 'discard_and_continue', ], [ 'stateKey' => 'tableSort', 'stateClass' => 'shareable_restorable', 'carrier' => 'session', 'queryRole' => 'unsupported', 'shareable' => false, 'restorableOnRefresh' => true, 'tenantSensitive' => false, 'invalidFallback' => 'discard_and_continue', ], ], 'hydrationRule' => [ 'precedenceOrder' => ['query', 'session', 'default'], 'appliesOnInitialMountOnly' => true, 'activeStateBecomesAuthoritativeAfterMount' => true, 'clearsOnTenantSwitch' => ['environment_id', 'managed_environment_id'], 'invalidRequestedStateFallback' => 'discard_and_continue', ], 'inspectContract' => [ 'primaryModel' => 'none', 'selectedStateKey' => null, 'openedBy' => ['row_navigation'], 'presentation' => 'navigate_to_canonical_detail', 'shareable' => false, 'invalidSelectionFallback' => 'discard_and_continue', ], 'shareableStateKeys' => ['environment_id', 'search'], 'localOnlyStateKeys' => [], ]; protected static bool $isDiscovered = false; protected static bool $shouldRegisterNavigation = false; protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-shield-check'; protected static string|UnitEnum|null $navigationGroup = 'Monitoring'; protected static ?string $title = 'Evidence Overview'; protected string $view = 'filament.pages.monitoring.evidence-overview'; /** * @var list> */ public array $rows = []; /** * @var array|null */ private ?array $accessibleTenants = null; private ?Collection $cachedSnapshots = null; public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration { return ActionSurfaceDeclaration::forPage(ActionSurfaceProfile::ListOnlyReadOnly, ActionSurfaceType::ReadOnlyRegistryReport) ->satisfy(ActionSurfaceSlot::ListHeader, 'The overview header exposes a clear-filters action when a tenant prefilter is active.') ->satisfy(ActionSurfaceSlot::InspectAffordance, ActionSurfaceInspectAffordance::ClickableRow->value) ->exempt(ActionSurfaceSlot::ListRowMoreMenu, 'The overview exposes a single drill-down link per row without a More menu.') ->exempt(ActionSurfaceSlot::ListBulkMoreGroup, 'The overview does not expose bulk actions.') ->satisfy(ActionSurfaceSlot::ListEmptyState, 'The empty state explains the current scope and offers a clear-filters CTA.'); } /** * @return array */ public static function monitoringPageStateContract(): array { return self::MONITORING_PAGE_STATE_CONTRACT; } public function mount(): void { $this->authorizeWorkspaceAccess(); $this->resetWorkspaceHubEnvironmentFilterStateForCleanEntry(request()); $this->seedTableStateFromQuery(); $this->mountInteractsWithTable(); $this->resetWorkspaceHubEnvironmentFilterStateForCleanEntry(request()); $this->rows = $this->rowsForState($this->tableFilters ?? [], $this->tableSearch)->values()->all(); } public function table(Table $table): Table { return $table ->defaultSort('tenant_name') ->defaultPaginationPageOption(25) ->paginated(TablePaginationProfiles::customPage()) ->persistFiltersInSession() ->persistSearchInSession() ->persistSortInSession() ->searchable() ->searchPlaceholder('Search evidence or next step') ->records(function ( ?string $sortColumn, ?string $sortDirection, ?string $search, array $filters, int $page, int $recordsPerPage ): LengthAwarePaginator { $rows = $this->rowsForState($filters, $search); $rows = $this->sortRows($rows, $sortColumn, $sortDirection); return $this->paginateRows($rows, $page, $recordsPerPage); }) ->filters([ SelectFilter::make('managed_environment_id') ->label('Environment') ->options(fn (): array => $this->tenantFilterOptions()) ->searchable(), ]) ->columns([ TextColumn::make('tenant_name') ->label('Environment') ->sortable(), TextColumn::make('artifact_truth_label') ->label('Outcome') ->badge() ->color(fn (array $record): string => (string) ($record['artifact_truth_color'] ?? 'gray')) ->icon(fn (array $record): ?string => is_string($record['artifact_truth_icon'] ?? null) ? $record['artifact_truth_icon'] : null) ->description(fn (array $record): ?string => is_string($record['artifact_truth_explanation'] ?? null) ? $record['artifact_truth_explanation'] : null) ->sortable() ->wrap(), TextColumn::make('generated_at') ->label('Generated') ->placeholder('—') ->sortable(), TextColumn::make('next_step') ->label('Next step') ->wrap(), ]) ->recordUrl(fn ($record): ?string => is_array($record) ? (is_string($record['view_url'] ?? null) ? $record['view_url'] : null) : null) ->actions([]) ->bulkActions([]) ->emptyStateHeading('No evidence snapshots in this scope') ->emptyStateDescription(fn (): string => $this->hasActiveOverviewFilters() ? 'Clear the current filters to return to the full workspace evidence overview.' : 'Adjust filters or create an environment snapshot to populate the workspace overview.') ->emptyStateActions([ Action::make('clear_filters') ->label('Clear filters') ->icon('heroicon-o-x-mark') ->color('gray') ->visible(fn (): bool => $this->hasActiveOverviewFilters()) ->action(fn (): mixed => $this->clearOverviewFilters()), ]); } /** * @return array */ protected function getHeaderActions(): array { return [ Action::make('clear_filters') ->label('Clear filters') ->color('gray') ->visible(fn (): bool => $this->hasActiveOverviewFilters()) ->action(fn (): mixed => $this->clearOverviewFilters()), ]; } public function clearOverviewFilters(): void { $this->tableFilters = [ 'managed_environment_id' => ['value' => null], ]; $this->tableDeferredFilters = $this->tableFilters; $this->tableSearch = ''; $this->rows = $this->rowsForState($this->tableFilters, $this->tableSearch)->values()->all(); session()->forget($this->getTableFiltersSessionKey()); session()->put($this->getTableSearchSessionKey(), $this->tableSearch); $this->clearWorkspaceHubEnvironmentFilterState(request()); $this->redirectToCleanWorkspaceHubUrl($this->overviewUrl(), request()); } /** * @return array{label: string, clear_url: string}|null */ public function environmentFilterChip(): ?array { $tenant = $this->filteredTenant(); if (! $tenant instanceof ManagedEnvironment) { return null; } return [ 'label' => (string) $tenant->name, 'clear_url' => $this->cleanWorkspaceHubUrl($this->overviewUrl()), ]; } /** * @return array */ public function evidenceDisclosurePayload(): array { $snapshots = $this->scopedSnapshots(); $filteredTenant = $this->filteredTenant(); $primarySnapshot = $this->primarySnapshotForScope($snapshots, $filteredTenant); $primaryTenant = $filteredTenant instanceof ManagedEnvironment ? $filteredTenant : $primarySnapshot?->tenant; $primaryReviewPack = $primarySnapshot instanceof EvidenceSnapshot ? $this->latestReviewPackForSnapshot($primarySnapshot) : null; $primaryStoredReport = $primaryTenant instanceof ManagedEnvironment ? $this->latestStoredReportForTenant($primaryTenant) : null; $primaryReview = $primarySnapshot instanceof EvidenceSnapshot ? $this->latestEnvironmentReviewForSnapshot($primarySnapshot) : null; $primaryOperationRun = $this->primaryOperationRun($primarySnapshot, $primaryReviewPack); $decisionState = $this->evidenceReviewPackDecisionState( $primarySnapshot, $primaryReviewPack, $primaryStoredReport, $primaryReview, $primaryTenant, $primaryOperationRun, ); $primaryAction = $this->primaryEvidenceAction( $decisionState, $primarySnapshot, $primaryReviewPack, $primaryStoredReport, $primaryReview, $primaryTenant, $primaryOperationRun, ); $decisionCard = $this->evidenceReviewPackDecisionCard( $decisionState, $primarySnapshot, $primaryReviewPack, $primaryStoredReport, $primaryReview, $primaryTenant, $primaryOperationRun, $primaryAction, ); return [ 'scope_label' => $this->evidenceScopeLabel(), 'scope_description' => $this->evidenceScopeDescription($snapshots), 'snapshot_count' => $snapshots->count(), 'primary_title' => $primaryTenant instanceof ManagedEnvironment ? $primaryTenant->name : 'Select an environment to evaluate evidence readiness', 'primary_summary' => $primarySnapshot instanceof EvidenceSnapshot ? $this->productSafeEvidenceReason($this->snapshotOutcome($primarySnapshot)->primaryReason) : ($primaryTenant instanceof ManagedEnvironment ? 'No evidence snapshot has been generated for the active workspace scope.' : 'Choose an authorized environment from the scope control before building a customer-safe proof path.'), 'primary_proof_state' => $this->primaryProofState( $primarySnapshot, $primaryReviewPack, $primaryStoredReport, $primaryOperationRun, ), 'decision_card' => $decisionCard, 'readiness_flow' => $this->evidenceReviewPackReadinessFlow( $decisionState, $primarySnapshot, $primaryReviewPack, $primaryStoredReport, $primaryReview, $primaryTenant, ), 'primary_action' => $primaryAction, 'review_pack_coverage' => $this->reviewPackCoverageSummary($primaryReviewPack), 'proof_items' => $this->evidenceReviewPackProofItems( $primarySnapshot, $primaryReviewPack, $primaryStoredReport, $primaryReview, $primaryTenant, $primaryOperationRun, ), 'cards' => [ $this->snapshotProofCard($primarySnapshot), $this->reviewPackProofCard($primaryReviewPack, $primarySnapshot), $this->storedReportProofCard($primaryStoredReport, $primaryTenant), $this->operationProofCard($primaryOperationRun), ], 'path_items' => [ $this->snapshotPathItem($primarySnapshot), $this->reviewPackPathItem($primaryReviewPack, $primarySnapshot), $this->storedReportPathItem($primaryStoredReport, $primaryTenant), $this->operationPathItem($primaryOperationRun), ], ]; } /** * @param Collection $snapshots */ private function primarySnapshotForScope(Collection $snapshots, ?ManagedEnvironment $filteredTenant): ?EvidenceSnapshot { if ($filteredTenant instanceof ManagedEnvironment) { return $this->latestEvidenceSnapshotForTenant($filteredTenant) ?? $snapshots->first(); } return $snapshots->first(); } private function latestEvidenceSnapshotForTenant(ManagedEnvironment $tenant): ?EvidenceSnapshot { return EvidenceSnapshot::query() ->with([ 'tenant', 'operationRun', 'reviewPacks.operationRun', 'reviewPacks.environmentReview.currentExportReviewPack', 'items', ]) ->where('workspace_id', (int) $tenant->workspace_id) ->where('managed_environment_id', (int) $tenant->getKey()) ->whereIn('status', [ EvidenceSnapshotStatus::Queued->value, EvidenceSnapshotStatus::Generating->value, EvidenceSnapshotStatus::Active->value, EvidenceSnapshotStatus::Failed->value, EvidenceSnapshotStatus::Expired->value, ]) ->orderByRaw('COALESCE(generated_at, created_at) DESC') ->first(); } private function latestEnvironmentReviewForSnapshot(EvidenceSnapshot $snapshot): ?EnvironmentReview { return EnvironmentReview::query() ->with(['currentExportReviewPack.operationRun', 'operationRun']) ->where('workspace_id', (int) $snapshot->workspace_id) ->where('managed_environment_id', (int) $snapshot->managed_environment_id) ->where('evidence_snapshot_id', (int) $snapshot->getKey()) ->orderByRaw('COALESCE(generated_at, created_at) DESC') ->first(); } private function evidenceReviewPackDecisionState( ?EvidenceSnapshot $snapshot, ?ReviewPack $reviewPack, ?StoredReport $storedReport, ?EnvironmentReview $review, ?ManagedEnvironment $tenant, ?OperationRun $operationRun, ): string { if (! $tenant instanceof ManagedEnvironment && ! $snapshot instanceof EvidenceSnapshot) { return 'source_unavailable'; } if (! $snapshot instanceof EvidenceSnapshot) { return 'no_snapshot'; } $snapshotStatus = (string) $snapshot->status; if (in_array($snapshotStatus, [EvidenceSnapshotStatus::Queued->value, EvidenceSnapshotStatus::Generating->value], true)) { return 'snapshot_generating'; } if ($snapshotStatus === EvidenceSnapshotStatus::Failed->value) { return 'snapshot_failed'; } if ($this->snapshotIsStale($snapshot)) { return 'snapshot_stale'; } if ($reviewPack instanceof ReviewPack) { $packStatus = (string) $reviewPack->status; if (in_array($packStatus, [ReviewPackStatus::Queued->value, ReviewPackStatus::Generating->value], true)) { return 'pack_generating'; } if ($packStatus === ReviewPackStatus::Failed->value) { return 'pack_failed'; } } if (! $storedReport instanceof StoredReport) { return 'report_missing'; } if (! $reviewPack instanceof ReviewPack) { return 'pack_required'; } if (! $this->reviewPackHasExportArtifact($reviewPack)) { return 'export_unavailable'; } if ($this->customerSafeOutputReady($review, $reviewPack)) { return $this->canDownloadReviewPack($reviewPack, $tenant) ? 'export_available' : 'customer_safe_ready'; } return $operationRun instanceof OperationRun && (string) $operationRun->outcome === OperationRunOutcome::Failed->value ? 'pack_failed' : 'customer_review_required'; } /** * @param array{label:string,url:string}|null $primaryAction * @return array */ private function evidenceReviewPackDecisionCard( string $state, ?EvidenceSnapshot $snapshot, ?ReviewPack $reviewPack, ?StoredReport $storedReport, ?EnvironmentReview $review, ?ManagedEnvironment $tenant, ?OperationRun $operationRun, ?array $primaryAction, ): array { $base = match ($state) { 'no_snapshot' => [ 'status' => 'Evidence snapshot required', 'tone' => 'warning', 'reason' => 'No evidence snapshot is available for the selected review scope.', 'impact' => 'Review pack output cannot be trusted or exported yet.', ], 'snapshot_generating' => [ 'status' => 'Evidence generation in progress', 'tone' => 'info', 'reason' => 'Evidence snapshot generation is currently running.', 'impact' => 'Review pack output is not final yet.', ], 'snapshot_failed' => [ 'status' => 'Evidence generation failed', 'tone' => 'danger', 'reason' => 'Evidence snapshot generation ended with errors.', 'impact' => 'Review pack output cannot be generated from this evidence yet.', ], 'snapshot_stale' => [ 'status' => 'Evidence refresh required', 'tone' => 'warning', 'reason' => 'Evidence exists, but its freshness is outside the acceptable window or the snapshot is expired or stale.', 'impact' => 'Review pack output should not be treated as current until evidence is refreshed.', ], 'report_missing' => [ 'status' => 'Stored report required', 'tone' => 'warning', 'reason' => 'Evidence snapshot exists, but no stored report is available for this review output.', 'impact' => 'Evidence is present but not yet packaged for consumption.', ], 'pack_required' => [ 'status' => 'Review pack required', 'tone' => 'warning', 'reason' => 'Stored report exists, but a review pack has not been generated.', 'impact' => 'Customer-safe delivery is not ready yet.', ], 'pack_generating' => [ 'status' => 'Review pack generation in progress', 'tone' => 'info', 'reason' => 'Review pack generation is currently running.', 'impact' => 'Customer output is not final yet.', ], 'pack_failed' => [ 'status' => 'Review pack generation failed', 'tone' => 'danger', 'reason' => 'Review pack generation ended with errors.', 'impact' => 'Customer-safe output cannot be generated from this pack yet.', ], 'customer_review_required' => [ 'status' => 'Customer-safe review required', 'tone' => 'warning', 'reason' => 'A review pack exists, but customer-safe output has not been confirmed by repo-backed review/package readiness.', 'impact' => 'Do not share the pack externally until it has been reviewed.', ], 'customer_safe_ready' => [ 'status' => 'Customer-safe output ready', 'tone' => 'success', 'reason' => 'Review pack output is backed by a published review and its current export pack.', 'impact' => 'Authorized users can use the pack; external sharing remains governed by workspace policy.', ], 'export_available' => [ 'status' => 'Review pack export available', 'tone' => 'success', 'reason' => 'A generated export artifact is available for authorized download.', 'impact' => 'Download is available from this surface; external sharing remains governed by workspace policy.', ], 'export_unavailable' => [ 'status' => 'Export unavailable', 'tone' => 'warning', 'reason' => 'No generated export artifact is available, the pack is not ready, the file is missing or expired, or external delivery is not configured.', 'impact' => 'Evidence package cannot be downloaded or delivered from this surface yet.', ], default => [ 'status' => 'Evidence source unavailable', 'tone' => 'gray', 'reason' => 'No repo-backed source data is selected for this proof scope.', 'impact' => 'No customer or auditor consumption decision should rely on this surface yet.', ], }; return $base + [ 'question' => 'Is this evidence package ready for customer or auditor consumption?', 'statusLabel' => 'Status', 'reasonLabel' => 'Reason', 'impactLabel' => 'Impact', 'nextActionLabel' => 'Primary next action', 'evidenceLabel' => 'Evidence path', 'evidence' => $this->decisionEvidenceSummary($snapshot, $reviewPack, $storedReport, $review, $tenant, $operationRun), 'actionLabel' => $primaryAction['label'] ?? ($state === 'source_unavailable' ? 'Select environment scope' : 'No authorized action available'), 'actionUrl' => $primaryAction['url'] ?? null, 'helperText' => $this->decisionActionHelper($state, $tenant, $primaryAction), 'actionDescription' => $this->decisionActionDescription($state, $primaryAction), ]; } /** * @return list> */ private function evidenceReviewPackReadinessFlow( string $state, ?EvidenceSnapshot $snapshot, ?ReviewPack $reviewPack, ?StoredReport $storedReport, ?EnvironmentReview $review, ?ManagedEnvironment $tenant, ): array { $sourceState = $tenant instanceof ManagedEnvironment ? 'Available' : 'Unavailable'; $snapshotState = $this->snapshotFlowState($snapshot); $storedReportState = $this->storedReportFlowState($snapshot, $storedReport); $reviewPackState = $this->reviewPackFlowState($snapshot, $storedReport, $reviewPack); $customerSafeState = $this->customerSafeFlowState($snapshot, $reviewPack, $review); $exportState = $this->exportFlowState($snapshot, $reviewPack, $tenant); return [ $this->flowStep('Source data selected', $sourceState, $sourceState === 'Available' ? 'Environment scope is established from the workspace context.' : 'Select an authorized environment before building customer-safe evidence.', $this->flowTone($sourceState), $state === 'source_unavailable'), $this->flowStep('Evidence snapshot', $snapshotState, $this->snapshotFlowDescription($snapshotState), $this->flowTone($snapshotState), in_array($state, ['no_snapshot', 'snapshot_generating', 'snapshot_failed', 'snapshot_stale'], true)), $this->flowStep('Stored report', $storedReportState, $this->storedReportFlowDescription($storedReportState), $this->flowTone($storedReportState), $state === 'report_missing'), $this->flowStep('Review pack', $reviewPackState, $this->reviewPackFlowDescription($reviewPackState), $this->flowTone($reviewPackState), in_array($state, ['pack_required', 'pack_generating', 'pack_failed'], true)), $this->flowStep('Customer-safe output', $customerSafeState, $this->customerSafeFlowDescription($customerSafeState), $this->flowTone($customerSafeState), $state === 'customer_review_required'), $this->flowStep('Export / delivery', $exportState, $this->exportFlowDescription($exportState), $this->flowTone($exportState), in_array($state, ['export_available', 'export_unavailable'], true)), ]; } /** * @return array{description:string,items:list} */ private function reviewPackCoverageSummary(?ReviewPack $reviewPack): array { if (! $reviewPack instanceof ReviewPack) { return [ 'description' => 'Review pack coverage details are not available for this artifact.', 'items' => [], ]; } $summary = is_array($reviewPack->summary) ? $reviewPack->summary : []; $items = []; foreach ([ 'finding_count' => 'Findings included', 'report_count' => 'Reports included', 'operation_count' => 'Operations included', 'section_count' => 'Review sections', ] as $key => $label) { if (array_key_exists($key, $summary) && is_numeric($summary[$key])) { $items[] = ['label' => $label, 'value' => (string) ((int) $summary[$key])]; } } $requiredDimensions = data_get($summary, 'evidence_resolution.required_dimensions'); if (is_array($requiredDimensions)) { $items[] = ['label' => 'Evidence dimensions', 'value' => (string) count($requiredDimensions)]; } if (filled($reviewPack->file_path)) { $items[] = ['label' => 'Generated files', 'value' => '1']; } return [ 'description' => $items === [] ? 'Review pack coverage details are not available for this artifact.' : 'Coverage values are derived from the generated review-pack summary and file metadata.', 'items' => $items, ]; } /** * @return list> */ private function evidenceReviewPackProofItems( ?EvidenceSnapshot $snapshot, ?ReviewPack $reviewPack, ?StoredReport $storedReport, ?EnvironmentReview $review, ?ManagedEnvironment $tenant, ?OperationRun $operationRun, ): array { $operationDescription = $operationRun instanceof OperationRun ? sprintf( '%s · %s · Started %s · Completed %s · Requested by %s', (string) $operationRun->type, Str::headline((string) $operationRun->outcome), $operationRun->started_at?->toDateTimeString() ?? '—', $operationRun->completed_at?->toDateTimeString() ?? '—', (string) ($operationRun->initiator_name ?: 'Unknown'), ) : 'Operation proof unavailable. No generation operation is linked to this artifact.'; return [ $this->proofItem('Source data', $tenant instanceof ManagedEnvironment ? 'Available' : 'Unavailable', $tenant instanceof ManagedEnvironment ? 'Workspace and environment scope are established.' : 'No environment scope is selected.', $tenant instanceof ManagedEnvironment ? 'success' : 'gray'), $this->proofItemFromCard($this->snapshotProofCard($snapshot)), $this->proofItemFromCard($this->storedReportProofCard($storedReport, $tenant)), $this->proofItemFromCard($this->reviewPackProofCard($reviewPack, $snapshot)), $this->proofItem('Operation proof', $operationRun instanceof OperationRun ? $this->operationProofState($operationRun) : 'Unavailable', $operationDescription, $operationRun instanceof OperationRun ? $this->operationProofTone($operationRun) : 'gray', $operationRun instanceof OperationRun ? OperationRunLinks::tenantlessView($operationRun) : null, $operationRun instanceof OperationRun ? OperationRunLinks::openLabel() : null), $this->proofItem('Export artifact', $this->exportFlowState($snapshot, $reviewPack, $tenant), $this->exportProofDescription($reviewPack, $tenant), $this->flowTone($this->exportFlowState($snapshot, $reviewPack, $tenant)), $this->canDownloadReviewPack($reviewPack, $tenant) ? app(ReviewPackService::class)->generateDownloadUrl($reviewPack) : null, $this->canDownloadReviewPack($reviewPack, $tenant) ? 'Download export' : null), $this->proofItem('Customer-safe state', $this->customerSafeFlowState($snapshot, $reviewPack, $review), $this->customerSafeFlowDescription($this->customerSafeFlowState($snapshot, $reviewPack, $review)), $this->flowTone($this->customerSafeFlowState($snapshot, $reviewPack, $review)), $tenant instanceof ManagedEnvironment ? CustomerReviewWorkspace::environmentFilterUrl($tenant) : null, $tenant instanceof ManagedEnvironment ? 'Open customer workspace' : null), $this->proofItem('Diagnostics', 'Collapsed', 'Raw report metadata, raw evidence payloads, generation diagnostics, export diagnostics, provider diagnostics, stack traces, and internal exceptions stay collapsed by default.', 'gray'), ]; } /** * @return array{label:string,state:string,description:string,tone:string,currentBlocker:bool} */ private function flowStep(string $label, string $state, string $description, string $tone, bool $currentBlocker = false): array { return [ 'label' => $label, 'state' => $state, 'description' => $description, 'tone' => $tone, 'currentBlocker' => $currentBlocker, ]; } private function snapshotFlowState(?EvidenceSnapshot $snapshot): string { if (! $snapshot instanceof EvidenceSnapshot) { return 'Missing'; } return match ((string) $snapshot->status) { EvidenceSnapshotStatus::Queued->value, EvidenceSnapshotStatus::Generating->value => 'Generating', EvidenceSnapshotStatus::Failed->value => 'Failed', default => $this->snapshotIsStale($snapshot) ? 'Stale' : 'Available', }; } private function storedReportFlowState(?EvidenceSnapshot $snapshot, ?StoredReport $storedReport): string { if (! $snapshot instanceof EvidenceSnapshot || in_array($this->snapshotFlowState($snapshot), ['Generating', 'Failed'], true)) { return 'Unavailable'; } return $storedReport instanceof StoredReport ? 'Available' : 'Missing'; } private function reviewPackFlowState(?EvidenceSnapshot $snapshot, ?StoredReport $storedReport, ?ReviewPack $reviewPack): string { if ($reviewPack instanceof ReviewPack) { return match ((string) $reviewPack->status) { ReviewPackStatus::Queued->value, ReviewPackStatus::Generating->value => 'Generating', ReviewPackStatus::Failed->value => 'Failed', ReviewPackStatus::Ready->value => 'Available', default => 'Unavailable', }; } if (! $snapshot instanceof EvidenceSnapshot) { return 'Unavailable'; } return $storedReport instanceof StoredReport ? 'Required' : 'Unavailable'; } private function customerSafeFlowState(?EvidenceSnapshot $snapshot, ?ReviewPack $reviewPack, ?EnvironmentReview $review): string { if (! $snapshot instanceof EvidenceSnapshot) { return 'Not ready'; } if (! $reviewPack instanceof ReviewPack || ! $reviewPack->isReady()) { return 'Not ready'; } return $this->customerSafeOutputReady($review, $reviewPack) ? 'Ready' : 'Needs review'; } private function exportFlowState(?EvidenceSnapshot $snapshot, ?ReviewPack $reviewPack, ?ManagedEnvironment $tenant): string { if (! $snapshot instanceof EvidenceSnapshot || ! $reviewPack instanceof ReviewPack) { return 'Unavailable'; } if ((string) $reviewPack->status === ReviewPackStatus::Failed->value) { return 'Failed'; } if (! $reviewPack->isReady()) { return 'Unavailable'; } return $this->reviewPackHasExportArtifact($reviewPack) && $this->canDownloadReviewPack($reviewPack, $tenant) ? 'Available' : 'Required'; } private function snapshotFlowDescription(string $state): string { return match ($state) { 'Available' => 'Snapshot proof exists.', 'Missing' => 'No snapshot in scope.', 'Generating' => 'Generation is running.', 'Failed' => 'Generation failed.', 'Stale' => 'Evidence is stale or expired.', default => 'Evidence snapshot state is unavailable.', }; } private function storedReportFlowDescription(string $state): string { return match ($state) { 'Available' => 'Stored report exists.', 'Missing' => 'No report for this output.', default => 'Depends on snapshot availability.', }; } private function reviewPackFlowDescription(string $state): string { return match ($state) { 'Available' => 'Review pack exists.', 'Required' => 'Generate a review pack.', 'Generating' => 'Generation is running.', 'Failed' => 'Generation failed.', default => 'Blocked by earlier proof.', }; } private function customerSafeFlowDescription(string $state): string { return match ($state) { 'Ready' => 'Published review backs current export pack.', 'Needs review' => 'Readiness is not confirmed.', 'Not ready' => 'Customer-safe output is not ready.', default => 'Customer-safe output readiness is unavailable.', }; } private function exportFlowDescription(string $state): string { return match ($state) { 'Available' => 'Authorized download is available.', 'Required' => 'Export file is missing or unauthorized.', 'Failed' => 'The linked review-pack export failed.', default => 'No generated export is available.', }; } private function flowTone(string $state): string { return match ($state) { 'Available', 'Ready', 'Generated' => 'success', 'Generating' => 'info', 'Missing', 'Required', 'Stale', 'Needs review', 'Not ready' => 'warning', 'Failed' => 'danger', default => 'gray', }; } private function snapshotIsStale(EvidenceSnapshot $snapshot): bool { if ((string) $snapshot->status === EvidenceSnapshotStatus::Expired->value) { return true; } if ($snapshot->expires_at instanceof \DateTimeInterface && $snapshot->expires_at->isPast()) { return true; } return $snapshot->completenessState() === EvidenceCompletenessState::Stale || (int) data_get($snapshot->summary ?? [], 'stale_dimensions', 0) > 0; } private function reviewPackHasExportArtifact(?ReviewPack $reviewPack): bool { if (! $reviewPack instanceof ReviewPack || ! $reviewPack->isReady()) { return false; } if ($reviewPack->expires_at instanceof \DateTimeInterface && $reviewPack->expires_at->isPast()) { return false; } return filled($reviewPack->file_disk) && filled($reviewPack->file_path); } private function customerSafeOutputReady(?EnvironmentReview $review, ?ReviewPack $reviewPack): bool { if (! $review instanceof EnvironmentReview || ! $reviewPack instanceof ReviewPack) { return false; } return (string) $review->status === EnvironmentReviewStatus::Published->value && (int) $review->current_export_review_pack_id === (int) $reviewPack->getKey() && $this->reviewPackHasExportArtifact($reviewPack); } private function canDownloadReviewPack(?ReviewPack $reviewPack, ?ManagedEnvironment $tenant): bool { $user = auth()->user(); return $reviewPack instanceof ReviewPack && $tenant instanceof ManagedEnvironment && $user instanceof User && $this->reviewPackHasExportArtifact($reviewPack) && (int) $reviewPack->managed_environment_id === (int) $tenant->getKey() && $user->can(Capabilities::REVIEW_PACK_VIEW, $tenant); } /** * @return array */ private function proofItem( string $label, string $state, string $description, string $color, ?string $url = null, ?string $actionLabel = null, ): array { return [ 'label' => $label, 'state' => $state, 'description' => $description, 'color' => $color, 'url' => $url, 'actionLabel' => $actionLabel, ]; } /** * @param array $card * @return array */ private function proofItemFromCard(array $card): array { return $this->proofItem( (string) $card['label'], (string) ($card['path_state'] ?? $card['value']), (string) $card['description'], (string) $card['color'], is_string($card['url'] ?? null) ? $card['url'] : null, is_string($card['url'] ?? null) ? 'Open proof' : null, ); } private function operationProofState(OperationRun $operationRun): string { if ((string) $operationRun->outcome === OperationRunOutcome::Failed->value) { return 'Failed'; } return in_array((string) $operationRun->status, [OperationRunStatus::Queued->value, OperationRunStatus::Running->value], true) ? 'Generating' : 'Available'; } private function operationProofTone(OperationRun $operationRun): string { return $this->flowTone($this->operationProofState($operationRun)); } private function exportProofDescription(?ReviewPack $reviewPack, ?ManagedEnvironment $tenant): string { if (! $reviewPack instanceof ReviewPack) { return 'No review pack is linked, so no export artifact is available.'; } if (! $this->reviewPackHasExportArtifact($reviewPack)) { return 'External delivery is not configured or the review pack file metadata is missing, expired, or not ready.'; } return $this->canDownloadReviewPack($reviewPack, $tenant) ? 'Signed download is available for authorized users.' : 'A file-backed export exists, but this user cannot download it from the current scope.'; } private function decisionEvidenceSummary( ?EvidenceSnapshot $snapshot, ?ReviewPack $reviewPack, ?StoredReport $storedReport, ?EnvironmentReview $review, ?ManagedEnvironment $tenant, ?OperationRun $operationRun, ): string { $parts = []; $parts[] = $tenant instanceof ManagedEnvironment ? 'Environment scope selected' : 'No environment selected'; $parts[] = 'Snapshot: '.$this->snapshotFlowState($snapshot); $parts[] = 'Stored report: '.$this->storedReportFlowState($snapshot, $storedReport); $parts[] = 'Review pack: '.$this->reviewPackFlowState($snapshot, $storedReport, $reviewPack); $parts[] = 'Customer-safe output: '.$this->customerSafeFlowState($snapshot, $reviewPack, $review); $parts[] = 'Export: '.$this->exportFlowState($snapshot, $reviewPack, $tenant); if ($operationRun instanceof OperationRun) { $parts[] = OperationRunLinks::identifier($operationRun); } return implode(' · ', $parts); } /** * @param array{label:string,url:string}|null $primaryAction */ private function decisionActionHelper(string $state, ?ManagedEnvironment $tenant, ?array $primaryAction): ?string { if ($primaryAction !== null) { return null; } if (! $tenant instanceof ManagedEnvironment) { return 'Use the Environment scope control in the top bar to choose an authorized environment.'; } return match ($state) { 'no_snapshot' => 'Evidence generation requires evidence management capability.', 'pack_required' => 'Review pack generation requires review-pack management capability.', 'export_available' => 'Download requires review-pack view capability.', default => 'No authorized repo-backed primary action is available for this state.', }; } /** * @param array{label:string,url:string}|null $primaryAction */ private function decisionActionDescription(string $state, ?array $primaryAction): string { if ($primaryAction === null) { return $state === 'source_unavailable' ? 'Choose an environment before evaluating evidence, reports, review packs, or export readiness.' : 'No capability-backed action is available for the current state.'; } return match ($state) { 'no_snapshot' => 'Creates the evidence snapshot required before reports or review packs can be trusted.', 'snapshot_generating', 'pack_generating' => 'Opens the linked operation so progress can be verified before using the output.', 'snapshot_failed', 'pack_failed' => 'Opens failed operation proof before retrying or sharing any output.', 'snapshot_stale' => 'Opens the stale snapshot so evidence can be refreshed from the correct scope.', 'report_missing' => 'Opens snapshot proof; report generation remains on the supported report surface.', 'pack_required' => 'Opens the environment review-pack surface where generation stays capability-gated.', 'customer_review_required' => 'Opens the customer review workspace before any external sharing decision.', 'customer_safe_ready' => 'Opens the customer review workspace that backs the readiness claim.', 'export_available' => 'Downloads the signed export for authorized users.', 'export_unavailable' => 'Opens review-pack proof to inspect missing, expired, or unauthorized export state.', default => 'Opens the most relevant repo-backed proof surface for this state.', }; } /** * @return Collection */ private function scopedSnapshots(): Collection { $snapshotIds = $this->rowsForState($this->tableFilters ?? [], $this->tableSearch) ->pluck('snapshot_id') ->map(static fn (mixed $snapshotId): int => (int) $snapshotId) ->all(); if ($snapshotIds === []) { return collect(); } return $this->latestAccessibleSnapshots() ->filter(static fn (EvidenceSnapshot $snapshot): bool => in_array((int) $snapshot->getKey(), $snapshotIds, true)) ->values(); } /** * @param Collection $snapshots */ private function evidenceScopeDescription(Collection $snapshots): string { $tenant = $this->filteredTenant(); if ($tenant instanceof ManagedEnvironment) { return 'Filtered to '.$tenant->name.'. Proof states below are derived from records directly attributed to this environment.'; } if ($snapshots->isEmpty()) { return 'Workspace-wide proof view. No accessible evidence snapshot currently matches the active search or filters.'; } return sprintf( 'Workspace-wide proof view across %d accessible environment%s.', $snapshots->count(), $snapshots->count() === 1 ? '' : 's', ); } private function evidenceScopeLabel(): string { $tenant = $this->filteredTenant(); return $tenant instanceof ManagedEnvironment ? 'Environment proof scope' : 'Workspace proof scope'; } private function latestReviewPackForSnapshot(EvidenceSnapshot $snapshot): ?ReviewPack { $reviewPack = $snapshot->reviewPacks ->sortByDesc(fn (ReviewPack $pack): int => $pack->generated_at?->getTimestamp() ?? $pack->created_at?->getTimestamp() ?? 0) ->first(); return $reviewPack instanceof ReviewPack ? $reviewPack : null; } private function latestStoredReportForTenant(ManagedEnvironment $tenant): ?StoredReport { $user = auth()->user(); if (! $user instanceof User) { return null; } $visibleReportTypes = collect(StoredReportResource::supportedReportTypes()) ->filter(function (string $reportType) use ($tenant, $user): bool { $capability = StoredReportResource::capabilityForReportType($reportType); return is_string($capability) && $user->can($capability, $tenant); }) ->values() ->all(); if ($visibleReportTypes === []) { return null; } return StoredReport::query() ->where('workspace_id', (int) $tenant->workspace_id) ->where('managed_environment_id', (int) $tenant->getKey()) ->whereIn('report_type', $visibleReportTypes) ->latest('created_at') ->first(); } private function primaryOperationRun(?EvidenceSnapshot $snapshot, ?ReviewPack $reviewPack): ?OperationRun { $run = $reviewPack?->operationRun; if ( $reviewPack instanceof ReviewPack && $run instanceof OperationRun && in_array((string) $reviewPack->status, [ReviewPackStatus::Queued->value, ReviewPackStatus::Generating->value, ReviewPackStatus::Failed->value], true) && $this->canViewOperationRun($run) ) { return $run; } $run = $snapshot?->operationRun; if ($run instanceof OperationRun && $this->canViewOperationRun($run)) { return $run; } $run = $reviewPack?->operationRun; return $run instanceof OperationRun && $this->canViewOperationRun($run) ? $run : null; } private function canViewOperationRun(OperationRun $run): bool { $user = auth()->user(); return $user instanceof User && Gate::forUser($user)->allows('view', $run); } /** * @return array{label:string,url:string}|null */ private function primaryEvidenceAction( string $state, ?EvidenceSnapshot $snapshot, ?ReviewPack $reviewPack, ?StoredReport $storedReport, ?EnvironmentReview $review, ?ManagedEnvironment $tenant, ?OperationRun $operationRun, ): ?array { if (in_array($state, ['snapshot_generating', 'snapshot_failed', 'pack_generating', 'pack_failed'], true) && $operationRun instanceof OperationRun) { return [ 'label' => $state === 'snapshot_generating' || $state === 'pack_generating' ? 'View operation progress' : 'Review operation', 'url' => OperationRunLinks::tenantlessView($operationRun), ]; } if ($state === 'no_snapshot' && $tenant instanceof ManagedEnvironment && $this->canManageEvidence($tenant)) { return [ 'label' => 'Generate evidence snapshot', 'url' => EvidenceSnapshotResource::getUrl('index', tenant: $tenant, panel: 'admin'), ]; } if ($state === 'snapshot_stale' && $snapshot instanceof EvidenceSnapshot && $snapshot->tenant instanceof ManagedEnvironment && $this->canManageEvidence($snapshot->tenant)) { return [ 'label' => 'Refresh evidence snapshot', 'url' => EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $snapshot->tenant, panel: 'admin'), ]; } if ($state === 'pack_required' && $tenant instanceof ManagedEnvironment && $this->canManageReviewPacks($tenant)) { return [ 'label' => 'Generate review pack', 'url' => ReviewPackResource::getUrl('index', tenant: $tenant, panel: 'admin'), ]; } if ($snapshot instanceof EvidenceSnapshot && $this->isEmptyEvidenceSnapshot($snapshot) && $snapshot->tenant instanceof ManagedEnvironment) { return [ 'label' => 'Open evidence snapshot', 'url' => EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $snapshot->tenant, panel: 'admin'), ]; } if ($state === 'customer_review_required' && $tenant instanceof ManagedEnvironment) { return [ 'label' => 'Review customer output', 'url' => CustomerReviewWorkspace::environmentFilterUrl($tenant), ]; } if ($state === 'export_available' && $this->canDownloadReviewPack($reviewPack, $tenant)) { return [ 'label' => 'Download export', 'url' => app(ReviewPackService::class)->generateDownloadUrl($reviewPack), ]; } if ($state === 'customer_safe_ready' && $tenant instanceof ManagedEnvironment) { return [ 'label' => 'Open customer workspace', 'url' => CustomerReviewWorkspace::environmentFilterUrl($tenant), ]; } if ($state === 'report_missing' && $snapshot instanceof EvidenceSnapshot && $snapshot->tenant instanceof ManagedEnvironment) { return [ 'label' => 'Open evidence snapshot', 'url' => EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $snapshot->tenant, panel: 'admin'), ]; } if ($state === 'export_unavailable' && $reviewPack instanceof ReviewPack && $reviewPack->tenant instanceof ManagedEnvironment) { return [ 'label' => 'Open review pack', 'url' => ReviewPackResource::getUrl('view', ['record' => $reviewPack], tenant: $reviewPack->tenant, panel: 'admin'), ]; } if ($review instanceof EnvironmentReview && $tenant instanceof ManagedEnvironment) { return [ 'label' => 'Open customer workspace', 'url' => CustomerReviewWorkspace::environmentFilterUrl($tenant), ]; } if ($snapshot instanceof EvidenceSnapshot && $snapshot->tenant instanceof ManagedEnvironment) { return [ 'label' => 'Open evidence snapshot', 'url' => EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $snapshot->tenant, panel: 'admin'), ]; } if ($reviewPack instanceof ReviewPack && $reviewPack->tenant instanceof ManagedEnvironment) { return [ 'label' => 'Open review pack', 'url' => ReviewPackResource::getUrl('view', ['record' => $reviewPack], tenant: $reviewPack->tenant, panel: 'admin'), ]; } if ($storedReport instanceof StoredReport && $storedReport->tenant instanceof ManagedEnvironment) { return [ 'label' => 'Open stored report', 'url' => StoredReportResource::getUrl('view', ['record' => $storedReport], tenant: $storedReport->tenant, panel: 'admin'), ]; } if ($operationRun instanceof OperationRun) { return [ 'label' => OperationRunLinks::openLabel(), 'url' => OperationRunLinks::tenantlessView($operationRun), ]; } return null; } private function canManageEvidence(ManagedEnvironment $tenant): bool { $user = auth()->user(); return $user instanceof User && $user->can(Capabilities::EVIDENCE_MANAGE, $tenant); } private function canManageReviewPacks(ManagedEnvironment $tenant): bool { $user = auth()->user(); return $user instanceof User && $user->can(Capabilities::REVIEW_PACK_MANAGE, $tenant); } /** * @return array */ private function snapshotProofCard(?EvidenceSnapshot $snapshot): array { if (! $snapshot instanceof EvidenceSnapshot) { return $this->unavailableProofCard( 'Evidence snapshot', 'Not generated', 'No active evidence snapshot is available in this scope.', 'gray', ); } $outcome = $this->snapshotOutcome($snapshot); $isEmptySnapshot = $this->isEmptyEvidenceSnapshot($snapshot); return [ 'label' => 'Evidence snapshot', 'value' => $isEmptySnapshot ? 'Proof incomplete' : $outcome->primaryLabel, 'path_state' => $isEmptySnapshot ? 'Empty' : $outcome->primaryLabel, 'description' => $isEmptySnapshot ? 'A proof record exists, but no usable captured evidence is available yet.' : $this->productSafeEvidenceReason($outcome->primaryReason), 'color' => $outcome->primaryBadge->color, 'url' => $snapshot->tenant instanceof ManagedEnvironment ? EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $snapshot->tenant, panel: 'admin') : null, 'meta' => $snapshot->generated_at?->diffForHumans() ?? 'Freshness unavailable', ]; } /** * @return array */ private function reviewPackProofCard(?ReviewPack $reviewPack, ?EvidenceSnapshot $snapshot): array { if (! $reviewPack instanceof ReviewPack) { return $this->unavailableProofCard( 'Review pack', $snapshot instanceof EvidenceSnapshot ? 'Not generated' : 'Not applicable', $snapshot instanceof EvidenceSnapshot ? 'No review pack has been generated from the current evidence snapshot.' : 'A review pack requires an evidence snapshot first.', 'gray', ); } return [ 'label' => 'Review pack', 'value' => BadgeRenderer::label(BadgeDomain::ReviewPackStatus)((string) $reviewPack->status), 'description' => $reviewPack->isReady() ? 'Customer-review artifact exists for this evidence path.' : 'Review pack exists but is not ready for sharing.', 'color' => BadgeRenderer::color(BadgeDomain::ReviewPackStatus)((string) $reviewPack->status), 'url' => $reviewPack->tenant instanceof ManagedEnvironment ? ReviewPackResource::getUrl('view', ['record' => $reviewPack], tenant: $reviewPack->tenant, panel: 'admin') : null, 'meta' => $reviewPack->generated_at?->diffForHumans() ?? 'Generated time unavailable', ]; } /** * @return array */ private function storedReportProofCard(?StoredReport $storedReport, ?ManagedEnvironment $tenant): array { if (! $tenant instanceof ManagedEnvironment) { return $this->unavailableProofCard( 'Stored report / export', 'Not applicable', 'Stored report availability is evaluated after an evidence scope exists.', 'gray', ); } if (! $storedReport instanceof StoredReport) { return $this->unavailableProofCard( 'Stored report / export', 'Unavailable', 'No repo-supported stored report is available for this environment scope.', 'gray', ); } return [ 'label' => 'Stored report / export', 'value' => 'Available', 'description' => StoredReportResource::reportFamilyReportLabel((string) $storedReport->report_type), 'color' => 'success', 'url' => StoredReportResource::getUrl('view', ['record' => $storedReport], tenant: $tenant, panel: 'admin'), 'meta' => $storedReport->created_at?->diffForHumans() ?? 'Report time unavailable', ]; } /** * @return array */ private function operationProofCard(?OperationRun $operationRun): array { if (! $operationRun instanceof OperationRun) { return $this->unavailableProofCard( 'Operation proof', 'Unavailable', 'No authorized operation run is linked to the current proof path.', 'gray', ); } return [ 'label' => 'Operation proof', 'value' => 'Available', 'description' => OperationRunLinks::identifier($operationRun), 'color' => 'info', 'url' => OperationRunLinks::tenantlessView($operationRun), 'meta' => $operationRun->completed_at?->diffForHumans() ?? $operationRun->created_at?->diffForHumans() ?? 'Run time unavailable', ]; } /** * @return array{label:string,value:string,description:string,color:string,url:null,meta:string} */ private function unavailableProofCard(string $label, string $value, string $description, string $color): array { return [ 'label' => $label, 'value' => $value, 'description' => $description, 'color' => $color, 'url' => null, 'meta' => 'Derived from current repo truth', ]; } /** * @return array */ private function snapshotPathItem(?EvidenceSnapshot $snapshot): array { return $this->pathItemFromCard($this->snapshotProofCard($snapshot)); } /** * @return array */ private function reviewPackPathItem(?ReviewPack $reviewPack, ?EvidenceSnapshot $snapshot): array { return $this->pathItemFromCard($this->reviewPackProofCard($reviewPack, $snapshot)); } /** * @return array */ private function storedReportPathItem(?StoredReport $storedReport, ?ManagedEnvironment $tenant): array { return $this->pathItemFromCard($this->storedReportProofCard($storedReport, $tenant)); } /** * @return array */ private function operationPathItem(?OperationRun $operationRun): array { return $this->pathItemFromCard($this->operationProofCard($operationRun)); } /** * @param array $card * @return array */ private function pathItemFromCard(array $card): array { return [ 'label' => (string) $card['label'], 'state' => (string) ($card['path_state'] ?? $card['value']), 'description' => (string) $card['description'], 'color' => (string) $card['color'], 'url' => is_string($card['url'] ?? null) ? $card['url'] : null, ]; } /** * @return array{label:string,color:string,reason:string,impact:string}|null */ private function primaryProofState( ?EvidenceSnapshot $snapshot, ?ReviewPack $reviewPack, ?StoredReport $storedReport, ?OperationRun $operationRun, ): ?array { if (! $snapshot instanceof EvidenceSnapshot || ! $this->isEmptyEvidenceSnapshot($snapshot)) { return null; } return [ 'label' => 'Proof incomplete', 'color' => 'warning', 'reason' => 'Primary evidence snapshot is empty.', 'impact' => $this->emptySnapshotImpact($reviewPack, $storedReport, $operationRun), ]; } private function emptySnapshotImpact(?ReviewPack $reviewPack, ?StoredReport $storedReport, ?OperationRun $operationRun): string { if ($reviewPack instanceof ReviewPack && $storedReport instanceof StoredReport && $operationRun instanceof OperationRun) { return 'Supporting proof exists through the review pack, stored report, and operation record.'; } return 'Supporting proof is limited; use the available evidence path items before relying on this snapshot.'; } private function isEmptyEvidenceSnapshot(EvidenceSnapshot $snapshot): bool { return $this->snapshotTruth($snapshot)->contentState === 'empty'; } private function productSafeEvidenceReason(string $reason): string { return $reason === 'The artifact row exists, but it does not contain usable captured content.' ? 'A proof record exists, but no usable captured evidence is available yet.' : $reason; } private function snapshotTruth(EvidenceSnapshot $snapshot, bool $fresh = false): ArtifactTruthEnvelope { $presenter = app(ArtifactTruthPresenter::class); return $fresh ? $presenter->forEvidenceSnapshotFresh($snapshot) : $presenter->forEvidenceSnapshot($snapshot); } private function snapshotOutcome(EvidenceSnapshot $snapshot, bool $fresh = false): CompressedGovernanceOutcome { $presenter = app(ArtifactTruthPresenter::class); return $presenter->compressedOutcomeFor($snapshot, SurfaceCompressionContext::evidenceOverview(), $fresh) ?? $presenter->compressedOutcomeFromEnvelope( $this->snapshotTruth($snapshot, $fresh), SurfaceCompressionContext::evidenceOverview(), ); } private function authorizeWorkspaceAccess(): void { $user = auth()->user(); if (! $user instanceof User) { throw new AuthenticationException; } app(WorkspaceContext::class)->currentWorkspaceForMemberOrFail($user, request()); } /** * @return array */ private function accessibleTenants(): array { if (is_array($this->accessibleTenants)) { return $this->accessibleTenants; } $user = auth()->user(); if (! $user instanceof User) { return $this->accessibleTenants = []; } $workspaceId = $this->workspaceId(); return $this->accessibleTenants = $user->accessibleManagedEnvironmentsQuery($workspaceId) ->orderBy('managed_environments.name') ->get() ->filter(fn (ManagedEnvironment $tenant): bool => (int) $tenant->workspace_id === $workspaceId && $user->can('evidence.view', $tenant)) ->values() ->all(); } /** * @return array */ private function tenantFilterOptions(): array { return collect($this->accessibleTenants()) ->mapWithKeys(static fn (ManagedEnvironment $tenant): array => [ (string) $tenant->getKey() => $tenant->name, ]) ->all(); } /** * @param array $filters * @return Collection> */ private function rowsForState(array $filters = [], ?string $search = null): Collection { $rows = $this->baseRows(); $tenantFilter = $this->normalizeTenantFilter($filters['managed_environment_id']['value'] ?? data_get($this->tableFilters, 'managed_environment_id.value')); $normalizedSearch = Str::lower(trim((string) ($search ?? $this->tableSearch))); if ($tenantFilter !== null) { $rows = $rows->where('managed_environment_id', $tenantFilter); } if ($normalizedSearch === '') { return $rows; } return $rows->filter(function (array $row) use ($normalizedSearch): bool { $haystack = implode(' ', [ (string) ($row['tenant_name'] ?? ''), (string) ($row['artifact_truth_label'] ?? ''), (string) ($row['artifact_truth_explanation'] ?? ''), (string) ($row['freshness_label'] ?? ''), (string) ($row['next_step'] ?? ''), ]); return str_contains(Str::lower($haystack), $normalizedSearch); }); } /** * @return Collection> */ private function baseRows(): Collection { $snapshots = $this->latestAccessibleSnapshots(); $currentReviewTenantIds = $this->currentReviewTenantIds($snapshots); return $snapshots->mapWithKeys(function (EvidenceSnapshot $snapshot) use ($currentReviewTenantIds): array { return [(string) $snapshot->getKey() => $this->rowForSnapshot($snapshot, $currentReviewTenantIds)]; }); } /** * @return Collection */ private function latestAccessibleSnapshots(): Collection { if ($this->cachedSnapshots instanceof Collection) { return $this->cachedSnapshots; } $tenantIds = collect($this->accessibleTenants()) ->map(static fn (ManagedEnvironment $tenant): int => (int) $tenant->getKey()) ->all(); $query = EvidenceSnapshot::query() ->with([ 'tenant', 'operationRun', 'reviewPacks.operationRun', 'reviewPacks.environmentReview.currentExportReviewPack', 'items', ]) ->where('workspace_id', $this->workspaceId()) ->where('status', 'active') ->latest('generated_at'); if ($tenantIds === []) { $query->whereRaw('1 = 0'); } else { $query->whereIn('managed_environment_id', $tenantIds); } return $this->cachedSnapshots = $query->get()->unique('managed_environment_id')->values(); } /** * @param Collection $snapshots * @return array */ private function currentReviewTenantIds(Collection $snapshots): array { return EnvironmentReview::query() ->where('workspace_id', $this->workspaceId()) ->whereIn('managed_environment_id', $snapshots->pluck('managed_environment_id')->map(static fn (mixed $tenantId): int => (int) $tenantId)->all()) ->whereIn('status', [ EnvironmentReviewStatus::Draft->value, EnvironmentReviewStatus::Ready->value, EnvironmentReviewStatus::Published->value, ]) ->pluck('managed_environment_id') ->mapWithKeys(static fn (mixed $tenantId): array => [(int) $tenantId => true]) ->all(); } /** * @param array $currentReviewTenantIds * @return array */ private function rowForSnapshot(EvidenceSnapshot $snapshot, array $currentReviewTenantIds): array { $truth = $this->snapshotTruth($snapshot); $outcome = $this->snapshotOutcome($snapshot); $tenantId = (int) $snapshot->managed_environment_id; $hasCurrentReview = $currentReviewTenantIds[$tenantId] ?? false; $nextStep = ! $hasCurrentReview && $truth->contentState === 'trusted' && $truth->freshnessState === 'current' ? 'Create a current review from this evidence snapshot' : $outcome->nextActionText; return [ 'tenant_name' => $snapshot->tenant?->name ?? 'Unknown tenant', 'managed_environment_id' => $tenantId, 'snapshot_id' => (int) $snapshot->getKey(), 'generated_at' => $snapshot->generated_at?->toDateTimeString(), 'artifact_truth_label' => $truth->contentState === 'empty' ? 'Proof incomplete' : $outcome->primaryLabel, 'artifact_truth_color' => $outcome->primaryBadge->color, 'artifact_truth_icon' => $outcome->primaryBadge->icon, 'artifact_truth_explanation' => $this->productSafeEvidenceReason($outcome->primaryReason), 'artifact_truth' => [ 'label' => $truth->contentState === 'empty' ? 'Proof incomplete' : $outcome->primaryLabel, 'color' => $outcome->primaryBadge->color, 'icon' => $outcome->primaryBadge->icon, 'explanation' => $this->productSafeEvidenceReason($outcome->primaryReason), ], 'next_step' => $nextStep, 'view_url' => $snapshot->tenant ? EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $snapshot->tenant) : null, ]; } /** * @param Collection> $rows * @return Collection> */ private function sortRows(Collection $rows, ?string $sortColumn, ?string $sortDirection): Collection { $sortColumn = in_array($sortColumn, ['tenant_name', 'artifact_truth_label', 'generated_at'], true) ? $sortColumn : 'tenant_name'; $descending = Str::lower((string) ($sortDirection ?? 'asc')) === 'desc'; $records = $rows->all(); uasort($records, static function (array $left, array $right) use ($sortColumn, $descending): int { $comparison = strnatcasecmp((string) ($left[$sortColumn] ?? ''), (string) ($right[$sortColumn] ?? '')); if ($comparison === 0) { $comparison = strnatcasecmp((string) ($left['tenant_name'] ?? ''), (string) ($right['tenant_name'] ?? '')); } return $descending ? ($comparison * -1) : $comparison; }); return collect($records); } /** * @param Collection> $rows */ private function paginateRows(Collection $rows, int $page, int $recordsPerPage): LengthAwarePaginator { return new LengthAwarePaginator( items: $rows->forPage($page, $recordsPerPage), total: $rows->count(), perPage: $recordsPerPage, currentPage: $page, ); } private function seedTableStateFromQuery(): void { $query = request()->query(); if (array_key_exists('search', $query)) { $this->tableSearch = trim((string) request()->query('search', '')); } if (! array_key_exists('environment_id', $query)) { return; } $workspace = $this->workspace(); if (! $workspace instanceof Workspace) { return; } $filter = WorkspaceHubEnvironmentFilter::fromRequest(request(), $workspace); if (! $filter instanceof WorkspaceHubEnvironmentFilter) { return; } $tenantFilter = $this->normalizeTenantFilter($filter->environmentId()); if ($tenantFilter === null) { throw new NotFoundHttpException; } $this->tableFilters = [ 'managed_environment_id' => ['value' => (string) $tenantFilter], ]; $this->tableDeferredFilters = $this->tableFilters; } private function normalizeTenantFilter(mixed $value): ?int { if (! is_numeric($value)) { return null; } $requestedTenantId = (int) $value; $allowedTenantIds = collect($this->accessibleTenants()) ->map(static fn (ManagedEnvironment $tenant): int => (int) $tenant->getKey()) ->all(); return in_array($requestedTenantId, $allowedTenantIds, true) ? $requestedTenantId : null; } private function hasActiveOverviewFilters(): bool { return filled(data_get($this->tableFilters, 'managed_environment_id.value')) || trim((string) $this->tableSearch) !== ''; } private function overviewUrl(array $overrides = []): string { return route( 'admin.evidence.overview', array_filter($overrides, static fn (mixed $value): bool => $value !== null && $value !== '' && $value !== []), ); } private function workspaceId(): int { $user = auth()->user(); if (! $user instanceof User) { throw new AuthenticationException; } return (int) app(WorkspaceContext::class) ->currentWorkspaceForMemberOrFail($user, request()) ->getKey(); } private function workspace(): ?Workspace { $user = auth()->user(); if (! $user instanceof User) { throw new AuthenticationException; } return app(WorkspaceContext::class)->currentWorkspaceForMemberOrFail($user, request()); } private function filteredTenant(): ?ManagedEnvironment { $tenantId = $this->normalizeTenantFilter(data_get($this->tableFilters, 'managed_environment_id.value')); if (! is_int($tenantId)) { return null; } foreach ($this->accessibleTenants() as $tenant) { if ((int) $tenant->getKey() === $tenantId) { return $tenant; } } return null; } }