openRenderedReportAction([ 'source_surface' => CustomerReviewWorkspace::SOURCE_SURFACE, CustomerReviewWorkspace::DETAIL_CONTEXT_QUERY_KEY => 1, 'review_id' => $this->record->environment_review_id, 'tenant_filter_id' => request()->query('tenant_filter_id'), 'interpretation_version' => $this->record->environmentReview?->controlInterpretationVersion(), ], 'primary'), Actions\Action::make('download') ->label(fn (): string => ReviewPackResource::downloadActionLabelFor($this->record)) ->icon('heroicon-o-arrow-down-tray') ->color('gray') ->visible(fn (): bool => $this->record->status === ReviewPackStatus::Ready->value) ->url(fn (): string => app(ReviewPackService::class)->generateDownloadUrl($this->record, [ 'source_surface' => CustomerReviewWorkspace::SOURCE_SURFACE, ])) ->openUrlInNewTab(), ]; } $regenerateAction = UiEnforcement::forAction( Actions\Action::make('regenerate') ->label('Regenerate review pack') ->icon('heroicon-o-arrow-path') ->color('gray') ->disabled(fn (): bool => ReviewPackResource::reviewPackGenerationBlocked($this->record->tenant)) ->requiresConfirmation() ->modalDescription('This will generate a new review pack with the same options. The current pack will remain available until it expires.') ->action(function (array $data): void { /** @var ReviewPack $record */ $record = $this->record; $options = array_merge($record->options ?? [], [ 'include_pii' => (bool) ($data['include_pii'] ?? ($record->options['include_pii'] ?? true)), 'include_operations' => (bool) ($data['include_operations'] ?? ($record->options['include_operations'] ?? true)), ]); $this->regenerateReviewPack($options); }) ->form(function (): array { /** @var ReviewPack $record */ $record = $this->record; $currentOptions = $record->options ?? []; return [ Section::make('Pack options') ->schema([ Toggle::make('include_pii') ->label('Include PII') ->helperText('Include personally identifiable information in the export.') ->default((bool) ($currentOptions['include_pii'] ?? true)), Toggle::make('include_operations') ->label('Include operations') ->helperText('Include recent operation history in the export.') ->default((bool) ($currentOptions['include_operations'] ?? true)), ]), ]; }) ) ->requireCapability(Capabilities::REVIEW_PACK_MANAGE) ->preserveDisabled() ->apply(); $regenerateAction->tooltip(fn (): ?string => ReviewPackResource::reviewPackGenerationActionTooltip($this->record->tenant)); $readyManagementReportPdf = $this->readyManagementReportPdf(); return [ $this->openRenderedReportAction([ 'source_surface' => 'review_pack', 'review_id' => $this->record->environment_review_id, 'tenant_filter_id' => request()->query('tenant_filter_id'), 'interpretation_version' => $this->record->environmentReview?->controlInterpretationVersion(), ], $readyManagementReportPdf instanceof StoredReport ? 'gray' : 'primary'), $readyManagementReportPdf instanceof StoredReport ? $this->downloadManagementReportPdfAction($readyManagementReportPdf) : $this->downloadReviewPackAction(), Actions\ActionGroup::make($readyManagementReportPdf instanceof StoredReport ? [ $this->downloadReviewPackAction(), $regenerateAction, ] : [ $this->managementReportPdfOverflowAction(), $regenerateAction, ]) ->label(__('More')) ->icon('heroicon-m-ellipsis-vertical') ->color('gray') ->button() ->dropdownWidth(Width::Medium) ->dropdownPlacement('bottom-end') ->tooltip(__('More actions')), ]; } /** * @param array $data */ private function regenerateReviewPack(array $data): void { /** @var ReviewPack $record */ $record = $this->record; $record->loadMissing(['environmentReview', 'tenant']); $review = $record->environmentReview; if (! $review instanceof EnvironmentReview) { ReviewPackResource::executeGeneration($data); return; } $user = auth()->user(); if (! $user instanceof User) { Notification::make()->danger()->title(__('localization.review.unable_export_missing_context'))->send(); return; } $service = app(ReviewPackService::class); if ($service->checkActiveRunForReview($review)) { OperationUxPresenter::alreadyQueuedToast(OperationRunType::ReviewPackGenerate->value) ->body(__('localization.review.export_already_queued_body')) ->send(); return; } $options = [ 'include_pii' => (bool) ($data['include_pii'] ?? true), 'include_operations' => (bool) ($data['include_operations'] ?? true), ]; try { $reviewPack = $service->generateFromReview($review, $user, $options); } catch (WorkspaceEntitlementBlockedException $exception) { Notification::make() ->warning() ->title(__('localization.review.executive_pack_export_unavailable')) ->body($exception->getMessage()) ->send(); return; } catch (ReviewPackEvidenceResolutionException $exception) { $reasons = $exception->result->reasons; Notification::make() ->danger() ->title(__('localization.review.unable_export_executive_pack')) ->body($reasons === [] ? $exception->getMessage() : implode(' ', $reasons)) ->send(); return; } if (! $reviewPack->wasRecentlyCreated) { Notification::make() ->success() ->title(__('localization.review.executive_pack_already_available')) ->body(__('localization.review.executive_pack_already_available_body')) ->actions([ Actions\Action::make('view_pack') ->label(__('localization.review.view_pack')) ->url(ReviewPackResource::getUrl('view', ['record' => $reviewPack], tenant: $review->tenant)), ]) ->send(); return; } OperationUxPresenter::queuedToast(OperationRunType::ReviewPackGenerate->value) ->body(__('localization.review.executive_pack_generating_background')) ->send(); } private function downloadReviewPackAction(): Actions\Action { return Actions\Action::make('download') ->label(fn (): string => ReviewPackResource::downloadActionLabelFor($this->record)) ->icon('heroicon-o-arrow-down-tray') ->color('gray') ->visible(fn (): bool => $this->record->status === ReviewPackStatus::Ready->value) ->url(fn (): string => app(ReviewPackService::class)->generateDownloadUrl($this->record)) ->openUrlInNewTab(); } private function downloadManagementReportPdfAction(StoredReport $readyReport): Actions\Action { return Actions\Action::make('download_management_report_pdf') ->label(__('localization.review.download_management_report_pdf')) ->icon('heroicon-o-document-arrow-down') ->color('primary') ->url(fn (): string => app(ManagementReportPdfService::class)->generateDownloadUrl($readyReport, [ 'source_surface' => 'review_pack', ])) ->openUrlInNewTab(); } private function managementReportPdfOverflowAction(): Actions\Action { $activeRun = $this->activeManagementReportPdfOperation(); if ($activeRun instanceof OperationRun) { return Actions\Action::make('open_management_report_pdf_operation') ->label(__('localization.review.open_management_report_pdf_operation')) ->icon('heroicon-o-clock') ->color('gray') ->url(fn (): string => $this->record->tenant ? OperationRunLinks::view($activeRun, $this->record->tenant) : url('/admin')) ->openUrlInNewTab(); } $action = UiEnforcement::forAction( Actions\Action::make('generate_management_report_pdf') ->label(fn (): string => $this->managementReportPdfActionLabel()) ->icon('heroicon-o-document-arrow-down') ->color(fn (): string => $this->managementReportPdfTooltip() === null ? 'primary' : 'gray') ->requiresConfirmation() ->modalDescription(__('localization.review.generate_management_report_pdf_confirmation')) ->modalSubmitActionLabel(__('localization.review.generate_management_report_pdf_submit')) ->disabled(fn (): bool => (bool) (app(ManagementReportPdfService::class)->generationDecision($this->record)['is_blocked'] ?? false)) ->tooltip(fn (): ?string => $this->managementReportPdfTooltip()) ->action(function (): void { $user = auth()->user(); if (! $user instanceof User) { abort(403); } $result = app(ManagementReportPdfService::class)->startGeneration($this->record, $user); $mode = (string) ($result['mode'] ?? ''); if ($mode === 'blocked') { Notification::make() ->warning() ->title(__('localization.review.management_report_pdf_blocked')) ->body((string) data_get($result, 'decision.reason', __('localization.review.management_report_pdf_blocked_default'))) ->send(); return; } if ($mode === 'ready') { Notification::make() ->success() ->title(__('localization.review.management_report_pdf_ready')) ->body(__('localization.review.management_report_pdf_ready_body')) ->send(); return; } if ($mode === 'active') { OperationUxPresenter::alreadyRunningToast(OperationRunType::ManagementReportGenerate->value)->send(); return; } OperationUxPresenter::queuedToast(OperationRunType::ManagementReportGenerate->value)->send(); }), ) ->requireCapability(Capabilities::REVIEW_PACK_MANAGE) ->preserveDisabled() ->apply(); return $action; } private function readyManagementReportPdf(): ?StoredReport { return app(ManagementReportPdfService::class)->findReadyReport($this->record); } private function activeManagementReportPdfOperation(): ?OperationRun { $report = app(ManagementReportPdfService::class)->findActiveReport($this->record); return $report?->operationRun; } private function managementReportPdfTooltip(): ?string { $decision = app(ManagementReportPdfService::class)->generationDecision($this->record); return (bool) ($decision['is_blocked'] ?? false) ? (string) ($decision['reason'] ?? __('localization.review.management_report_pdf_blocked_default')) : null; } private function managementReportPdfActionLabel(): string { return $this->managementReportPdfTooltip() === null ? __('localization.review.generate_management_report_pdf') : __('localization.review.management_report_pdf_blocked'); } /** * @param array $parameters */ private function openRenderedReportAction(array $parameters = [], string $color = 'primary'): Actions\Action { return Actions\Action::make('open_rendered_report') ->label(fn (): string => \App\Filament\Resources\EnvironmentReviewResource::renderedReportActionLabelFor($this->record->environmentReview)) ->icon('heroicon-o-document-text') ->color($color) ->visible(fn (): bool => $this->canOpenRenderedReport()) ->url(fn (): string => app(ReviewPackService::class)->generateRenderedReportUrl( $this->record, $this->renderedReportParameters($parameters), )) ->openUrlInNewTab(); } /** * @param array $parameters * @return array */ private function renderedReportParameters(array $parameters): array { $parameters = array_filter($parameters, static fn (mixed $value): bool => $value !== null && $value !== ''); $review = $this->record->environmentReview; if (! $review instanceof EnvironmentReview) { return $parameters; } if (! array_key_exists(ReportProfileRegistry::QUERY_PARAMETER, $parameters)) { $guidance = ReviewPackOutputResolutionGuidance::fromReadiness( ReviewPackOutputResolutionGuidance::readinessForReview($review), ); $parameters[ReportProfileRegistry::QUERY_PARAMETER] = ReportProfileRegistry::defaultForRenderedReportState( (string) ($guidance['state'] ?? ReviewPackOutputResolutionGuidance::STATE_UNKNOWN), array_key_exists(CustomerReviewWorkspace::DETAIL_CONTEXT_QUERY_KEY, $parameters), ); } return $parameters; } private function canOpenRenderedReport(): bool { /** @var ReviewPack $record */ $record = $this->record; if ($record->status !== ReviewPackStatus::Ready->value) { return false; } if ($record->expires_at !== null && $record->expires_at->isPast()) { return false; } if (! $record->environmentReview instanceof \App\Models\EnvironmentReview) { return false; } return (int) ($record->environmentReview->current_export_review_pack_id ?? 0) === (int) $record->getKey(); } }