TenantAtlas/app/Filament/Widgets/Tenant/TenantVerificationReport.php

224 lines
6.7 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Filament\Widgets\Tenant;
use App\Filament\Support\VerificationReportViewer;
use App\Models\OperationRun;
use App\Models\Tenant;
use App\Models\User;
use App\Services\Verification\StartVerification;
use App\Support\Auth\Capabilities;
use App\Support\Auth\UiTooltips;
use App\Support\OperationRunLinks;
use App\Support\OperationRunStatus;
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);
if ($result->status === 'scope_busy') {
Notification::make()
->title('Another operation is already running')
->body('Please wait for the active run to finish.')
->warning()
->actions([
Action::make('view_run')
->label('View run')
->url($runUrl),
])
->send();
return;
}
if ($result->status === 'deduped') {
Notification::make()
->title('Verification already running')
->body('A verification run is already queued or running.')
->warning()
->actions([
Action::make('view_run')
->label('View run')
->url($runUrl),
])
->send();
return;
}
if ($result->status === 'blocked') {
$reasonCode = is_string($result->run->context['reason_code'] ?? null)
? (string) $result->run->context['reason_code']
: 'unknown_error';
$actions = [
Action::make('view_run')
->label('View run')
->url($runUrl),
];
$nextSteps = $result->run->context['next_steps'] ?? [];
$nextSteps = is_array($nextSteps) ? $nextSteps : [];
foreach ($nextSteps as $index => $step) {
if (! is_array($step)) {
continue;
}
$label = is_string($step['label'] ?? null) ? trim((string) $step['label']) : '';
$url = is_string($step['url'] ?? null) ? trim((string) $step['url']) : '';
if ($label === '' || $url === '') {
continue;
}
$actions[] = Action::make('next_step_'.$index)
->label($label)
->url($url);
break;
}
Notification::make()
->title('Verification blocked')
->body("Blocked by provider configuration ({$reasonCode}).")
->warning()
->actions($actions)
->send();
return;
}
Notification::make()
->title('Verification started')
->success()
->actions([
Action::make('view_run')
->label('View run')
->url($runUrl),
])
->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,
];
}
$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;
$isInProgress = $run instanceof OperationRun
&& (string) $run->status !== OperationRunStatus::Completed->value;
$user = auth()->user();
$isTenantMember = $user instanceof User && $user->canAccessTenant($tenant);
$canStart = $isTenantMember
&& $user->can(Capabilities::PROVIDER_RUN, $tenant);
$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,
'isInProgress' => $isInProgress,
'canStart' => $canStart,
'startTooltip' => $isTenantMember && ! $canStart ? UiTooltips::insufficientPermission() : null,
];
}
}