TenantAtlas/app/Filament/Resources/BaselineSnapshotResource/Pages/ViewBaselineSnapshot.php
2026-03-10 09:26:42 +01:00

156 lines
6.6 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Filament\Resources\BaselineSnapshotResource\Pages;
use App\Filament\Resources\BaselineSnapshotResource;
use App\Models\BaselineSnapshot;
use App\Models\User;
use App\Models\Workspace;
use App\Services\Auth\WorkspaceCapabilityResolver;
use App\Services\Baselines\SnapshotRendering\BaselineSnapshotPresenter;
use App\Support\Auth\Capabilities;
use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer;
use Filament\Infolists\Components\TextEntry;
use Filament\Infolists\Components\ViewEntry;
use Filament\Resources\Pages\ViewRecord;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
class ViewBaselineSnapshot extends ViewRecord
{
protected static string $resource = BaselineSnapshotResource::class;
/**
* @var array<string, mixed>
*/
public array $presentedSnapshot = [];
public function mount(int|string $record): void
{
parent::mount($record);
$snapshot = $this->getRecord();
if ($snapshot instanceof BaselineSnapshot) {
$this->presentedSnapshot = app(BaselineSnapshotPresenter::class)
->present($snapshot)
->toArray();
}
}
protected function authorizeAccess(): void
{
$user = auth()->user();
$snapshot = $this->getRecord();
$workspace = BaselineSnapshotResource::resolveWorkspace();
if (! $user instanceof User || ! $snapshot instanceof BaselineSnapshot || ! $workspace instanceof Workspace) {
abort(404);
}
if ((int) $snapshot->workspace_id !== (int) $workspace->getKey()) {
abort(404);
}
/** @var WorkspaceCapabilityResolver $resolver */
$resolver = app(WorkspaceCapabilityResolver::class);
if (! $resolver->isMember($user, $workspace)) {
abort(404);
}
if (! $resolver->can($user, $workspace, Capabilities::WORKSPACE_BASELINES_VIEW)) {
abort(403);
}
}
public function infolist(Schema $schema): Schema
{
return $schema
->schema([
Section::make('Snapshot')
->schema([
TextEntry::make('snapshot_id')
->label('Snapshot')
->state(function (): string {
$snapshotId = data_get($this->presentedSnapshot, 'snapshot.snapshotId');
return is_numeric($snapshotId) ? '#'.$snapshotId : '—';
}),
TextEntry::make('baseline_profile_name')
->label('Baseline')
->state(fn (): string => data_get($this->presentedSnapshot, 'snapshot.baselineProfileName', '—'))
->placeholder('—'),
TextEntry::make('captured_at')
->label('Captured')
->state(fn (): ?string => data_get($this->presentedSnapshot, 'snapshot.capturedAt'))
->dateTime()
->placeholder('—'),
TextEntry::make('state_label')
->label('State')
->badge()
->state(fn (): string => data_get($this->presentedSnapshot, 'snapshot.stateLabel', 'Complete'))
->color(fn (string $state): string => $state === 'Captured with gaps' ? 'warning' : 'success'),
TextEntry::make('overall_fidelity')
->label('Overall fidelity')
->badge()
->state(fn (): ?string => data_get($this->presentedSnapshot, 'snapshot.overallFidelity'))
->formatStateUsing(BadgeRenderer::label(BadgeDomain::BaselineSnapshotFidelity))
->color(BadgeRenderer::color(BadgeDomain::BaselineSnapshotFidelity))
->icon(BadgeRenderer::icon(BadgeDomain::BaselineSnapshotFidelity)),
TextEntry::make('fidelity_summary')
->label('Evidence mix')
->state(fn (): string => data_get($this->presentedSnapshot, 'snapshot.fidelitySummary', 'Content 0, Meta 0')),
TextEntry::make('overall_gap_count')
->label('Evidence gaps')
->state(fn (): int => (int) data_get($this->presentedSnapshot, 'snapshot.overallGapCount', 0)),
TextEntry::make('snapshot_identity_hash')
->label('Identity hash')
->state(fn (): ?string => data_get($this->presentedSnapshot, 'snapshot.snapshotIdentityHash'))
->copyable()
->placeholder('—')
->columnSpanFull(),
])
->columns(2)
->columnSpanFull(),
Section::make('Coverage summary')
->schema([
ViewEntry::make('summary_rows')
->label('')
->view('filament.infolists.entries.baseline-snapshot-summary-table')
->state(fn (): array => data_get($this->presentedSnapshot, 'summaryRows', []))
->columnSpanFull(),
])
->columnSpanFull(),
Section::make('Captured policy types')
->schema([
ViewEntry::make('groups')
->label('')
->view('filament.infolists.entries.baseline-snapshot-groups')
->state(fn (): array => data_get($this->presentedSnapshot, 'groups', []))
->columnSpanFull(),
])
->columnSpanFull(),
Section::make('Technical detail')
->schema([
ViewEntry::make('technical_detail')
->label('')
->view('filament.infolists.entries.baseline-snapshot-technical-detail')
->state(fn (): array => data_get($this->presentedSnapshot, 'technicalDetail', []))
->columnSpanFull(),
])
->collapsible()
->collapsed()
->columnSpanFull(),
]);
}
protected function getHeaderActions(): array
{
return [];
}
}