TenantAtlas/apps/platform/app/Filament/Widgets/Tenant/TenantVerificationReport.php
Ahmed Darrazi 34230be79d
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 4m21s
feat: unify provider-backed action dispatch gating
2026-04-20 08:47:08 +02:00

211 lines
7.0 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Filament\Widgets\Tenant;
use App\Filament\Resources\TenantResource\Pages\ViewTenant;
use App\Filament\Support\VerificationReportChangeIndicator;
use App\Filament\Support\VerificationReportViewer;
use App\Models\OperationRun;
use App\Models\Tenant;
use App\Models\User;
use App\Services\Tenants\TenantOperabilityService;
use App\Services\Verification\StartVerification;
use App\Support\Auth\Capabilities;
use App\Support\Auth\UiTooltips;
use App\Support\OperationRunLinks;
use App\Support\OperationRunStatus;
use App\Support\OpsUx\OperationUxPresenter;
use App\Support\OpsUx\ProviderOperationStartResultPresenter;
use App\Support\OpsUx\OpsUxBrowserEvents;
use Filament\Actions\Action;
use Filament\Facades\Filament;
use Filament\Notifications\Notification;
use Filament\Widgets\Widget;
class TenantVerificationReport extends Widget
{
protected static bool $isLazy = false;
protected string $view = 'filament.widgets.tenant.tenant-verification-report';
public ?Tenant $record = null;
private function resolveTenant(): ?Tenant
{
$tenant = Filament::getTenant();
if ($tenant instanceof Tenant) {
return $tenant;
}
return $this->record instanceof Tenant ? $this->record : null;
}
public function startVerification(StartVerification $verification): void
{
$user = auth()->user();
if (! $user instanceof User) {
abort(403);
}
$tenant = $this->resolveTenant();
if (! $tenant instanceof Tenant) {
abort(404);
}
if (! $user->canAccessTenant($tenant)) {
abort(404);
}
$result = $verification->providerConnectionCheckForTenant(
tenant: $tenant,
initiator: $user,
extraContext: [
'surface' => [
'kind' => 'tenant_verification_widget',
],
],
);
$runUrl = OperationRunLinks::tenantlessView($result->run);
$notification = app(ProviderOperationStartResultPresenter::class)->notification(
result: $result,
blockedTitle: 'Verification blocked',
runUrl: $runUrl,
);
if ($result->status === 'scope_busy') {
OpsUxBrowserEvents::dispatchRunEnqueued($this);
$notification->send();
return;
}
if ($result->status === 'deduped') {
OpsUxBrowserEvents::dispatchRunEnqueued($this);
$notification->send();
return;
}
if ($result->status === 'blocked') {
$notification->send();
return;
}
OpsUxBrowserEvents::dispatchRunEnqueued($this);
$notification->send();
}
/**
* @return array<string, mixed>
*/
protected function getViewData(): array
{
$tenant = $this->resolveTenant();
if (! $tenant instanceof Tenant) {
return [
'tenant' => null,
'run' => null,
'runData' => null,
'runUrl' => null,
'report' => null,
'isInProgress' => false,
'canStart' => false,
'startTooltip' => null,
'rerunHint' => null,
];
}
$run = OperationRun::query()
->where('tenant_id', (int) $tenant->getKey())
->where('type', 'provider.connection.check')
->orderByDesc('id')
->first();
$report = $run instanceof OperationRun
? VerificationReportViewer::report($run)
: null;
$changeIndicator = $run instanceof OperationRun
? VerificationReportChangeIndicator::forRun($run)
: null;
$previousRunUrl = is_array($changeIndicator) && is_numeric($changeIndicator['previous_report_id'] ?? null)
? OperationRunLinks::tenantlessView((int) $changeIndicator['previous_report_id'])
: null;
$isInProgress = $run instanceof OperationRun
&& (string) $run->status !== OperationRunStatus::Completed->value;
$user = auth()->user();
$isTenantMember = $user instanceof User && $user->canAccessTenant($tenant);
$canOperate = app(TenantOperabilityService::class)->decisionFor($tenant)->canOperate;
$canStart = $isTenantMember
&& $canOperate
&& $user->can(Capabilities::PROVIDER_RUN, $tenant);
$lifecycleNotice = $isTenantMember && ! $canOperate
? 'Verification can be started from tenant management only while the tenant is active. Consent and connection configuration remain separate from this stored verification report.'
: null;
$runData = null;
if ($run instanceof OperationRun) {
$context = is_array($run->context ?? null) ? $run->context : [];
$targetScope = $context['target_scope'] ?? [];
$targetScope = is_array($targetScope) ? $targetScope : [];
$runData = [
'id' => (int) $run->getKey(),
'type' => (string) $run->type,
'status' => (string) $run->status,
'outcome' => (string) $run->outcome,
'initiator_name' => (string) $run->initiator_name,
'started_at' => $run->started_at?->toJSON(),
'completed_at' => $run->completed_at?->toJSON(),
'target_scope' => $targetScope,
'failures' => is_array($run->failure_summary ?? null) ? $run->failure_summary : [],
];
}
return [
'tenant' => $tenant,
'run' => $run,
'runData' => $runData,
'runUrl' => $run instanceof OperationRun ? OperationRunLinks::tenantlessView($run) : null,
'report' => $report,
'surface' => $run instanceof OperationRun
? VerificationReportViewer::surface($run, [], [
'hostKind' => 'tenant_widget',
'changeIndicator' => $changeIndicator,
'previousRunUrl' => $previousRunUrl,
'hostVariation' => [
'ownsNoRunState' => true,
'ownsActiveState' => true,
'supportsAssist' => false,
'supportsAcknowledge' => false,
'supportsTechnicalDetailsTrigger' => false,
],
])
: [],
'redactionNotes' => VerificationReportViewer::redactionNotes($report),
'isInProgress' => $isInProgress,
'showStartAction' => ! ($run instanceof OperationRun) && $isTenantMember && $canOperate,
'canStart' => $canStart,
'startTooltip' => $isTenantMember && $canOperate && ! $canStart ? UiTooltips::insufficientPermission() : null,
'lifecycleNotice' => $lifecycleNotice,
'rerunHint' => $run instanceof OperationRun && $canStart
? ViewTenant::verificationHeaderActionHint()
: null,
];
}
}