navigationContext(); if ($navigationContext?->backLinkLabel !== null && $navigationContext->backLinkUrl !== null) { $actions[] = Action::make('return_to_decision_register') ->label($navigationContext->backLinkLabel) ->icon('heroicon-o-arrow-left') ->color('gray') ->url($navigationContext->backLinkUrl); } return array_merge($actions, [ Action::make('renew_exception') ->label($renewRule->canonicalLabel) ->icon('heroicon-o-arrow-path') ->color('primary') ->visible(fn (): bool => $this->canManageRecord() && $this->getRecord() instanceof FindingException && $this->getRecord()->canBeRenewed()) ->fillForm(fn (): array => [ 'owner_user_id' => $this->getRecord() instanceof FindingException ? $this->getRecord()->owner_user_id : null, ]) ->requiresConfirmation() ->modalHeading($renewRule->modalHeading) ->modalDescription($renewRule->modalDescription) ->form([ Select::make('owner_user_id') ->label('Owner') ->required() ->options(fn (): array => FindingExceptionResource::canViewAny() ? $this->tenantMemberOptions() : []) ->searchable(), Textarea::make('request_reason') ->label('Renewal reason') ->rows(4) ->required() ->maxLength(2000), DateTimePicker::make('review_due_at') ->label('Review due at') ->required() ->seconds(false), DateTimePicker::make('expires_at') ->label('Requested expiry') ->seconds(false), Repeater::make('evidence_references') ->label('Evidence references') ->schema([ TextInput::make('label') ->label('Label') ->required() ->maxLength(255), TextInput::make('source_type') ->label('Source type') ->required() ->maxLength(255), TextInput::make('source_id') ->label('Source ID') ->maxLength(255), TextInput::make('source_fingerprint') ->label('Fingerprint') ->maxLength(255), DateTimePicker::make('measured_at') ->label('Measured at') ->seconds(false), ]) ->defaultItems(0) ->collapsed(), ]) ->action(function (array $data, FindingExceptionService $service) use ($renewRule): void { $record = $this->getRecord(); $user = auth()->user(); if (! $record instanceof FindingException || ! $user instanceof User) { abort(404); } try { $service->renew($record, $user, $data); } catch (InvalidArgumentException $exception) { Notification::make() ->title('Renewal request failed') ->body($exception->getMessage()) ->danger() ->send(); return; } Notification::make() ->title($renewRule->successTitle) ->success() ->send(); $this->refreshFormData(['status', 'current_validity_state', 'review_due_at']); }), Action::make('revoke_exception') ->label($revokeRule->canonicalLabel) ->icon('heroicon-o-no-symbol') ->color('danger') ->visible(fn (): bool => $this->canManageRecord() && $this->getRecord() instanceof FindingException && $this->getRecord()->canBeRevoked()) ->requiresConfirmation() ->modalHeading($revokeRule->modalHeading) ->modalDescription($revokeRule->modalDescription) ->form([ Textarea::make('revocation_reason') ->label('Revocation reason') ->rows(4) ->required() ->maxLength(2000), ]) ->action(function (array $data, FindingExceptionService $service) use ($revokeRule): void { $record = $this->getRecord(); $user = auth()->user(); if (! $record instanceof FindingException || ! $user instanceof User) { abort(404); } try { $service->revoke($record, $user, $data); } catch (InvalidArgumentException $exception) { Notification::make() ->title('Exception revocation failed') ->body($exception->getMessage()) ->danger() ->send(); return; } Notification::make() ->title($revokeRule->successTitle) ->success() ->send(); $this->refreshFormData(['status', 'current_validity_state', 'revocation_reason', 'revoked_at']); }), ]); } public function getSubheading(): ?string { $navigationContext = $this->navigationContext(); if ($navigationContext?->sourceSurface === 'governance.decision_register') { return 'Opened from the workspace decision register. Use the back action to return to the same register scope.'; } return null; } /** * @return array */ private function tenantMemberOptions(): array { $record = $this->getRecord(); if (! $record instanceof FindingException) { return []; } $tenant = $record->tenant; if (! $tenant instanceof ManagedEnvironment) { return []; } /** @var ManagedEnvironmentAccessScopeResolver $scopeResolver */ $scopeResolver = app(ManagedEnvironmentAccessScopeResolver::class); return User::query() ->whereHas('workspaceMemberships', fn (Builder $query): Builder => $query->where('workspace_id', (int) $tenant->workspace_id)) ->orderBy('name') ->orderBy('email') ->get(['id', 'name', 'email']) ->filter(fn (User $user): bool => $scopeResolver->canAccess($user, $tenant)) ->mapWithKeys(fn (User $user): array => [ (int) $user->id => trim((string) ($user->name ?: $user->email)), ]) ->all(); } private function canManageRecord(): bool { $record = $this->getRecord(); $user = auth()->user(); return $record instanceof FindingException && $record->tenant instanceof ManagedEnvironment && $user instanceof User && $user->canAccessTenant($record->tenant) && $user->can(Capabilities::FINDING_EXCEPTION_MANAGE, $record->tenant); } private function navigationContext(): ?CanonicalNavigationContext { return CanonicalNavigationContext::fromRequest(request()); } }