214 lines
7.3 KiB
PHP
214 lines
7.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Filament\Pages\TenantConfiguration;
|
|
|
|
use App\Filament\Concerns\ResolvesPanelTenantContext;
|
|
use App\Models\ManagedEnvironment;
|
|
use App\Models\User;
|
|
use App\Models\Workspace;
|
|
use App\Services\Auth\ManagedEnvironmentAccessScopeResolver;
|
|
use App\Services\TenantConfiguration\CoverageV2ReadinessReadModel;
|
|
use App\Support\Auth\Capabilities;
|
|
use App\Support\Navigation\NavigationScope;
|
|
use App\Support\Ui\ActionSurface\ActionSurfaceDeclaration;
|
|
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceInspectAffordance;
|
|
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile;
|
|
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceSlot;
|
|
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceType;
|
|
use BackedEnum;
|
|
use Filament\Facades\Filament;
|
|
use Filament\Pages\Page;
|
|
use Illuminate\Contracts\Support\Htmlable;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Livewire\Attributes\Locked;
|
|
use UnitEnum;
|
|
|
|
class CoverageV2Readiness extends Page
|
|
{
|
|
use ResolvesPanelTenantContext;
|
|
|
|
protected static bool $isDiscovered = false;
|
|
|
|
protected static bool $shouldRegisterNavigation = false;
|
|
|
|
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-shield-check';
|
|
|
|
protected static ?int $navigationSort = 4;
|
|
|
|
protected static string|UnitEnum|null $navigationGroup = 'Inventory';
|
|
|
|
protected static ?string $navigationLabel = 'Coverage v2';
|
|
|
|
protected static ?string $slug = 'workspaces/{workspace}/environments/{environment}/tenant-configuration/coverage-v2';
|
|
|
|
protected static ?string $title = 'Coverage v2 Readiness';
|
|
|
|
protected string $view = 'filament.pages.tenant-configuration.coverage-v2-readiness';
|
|
|
|
#[Locked]
|
|
public ?int $environmentId = null;
|
|
|
|
public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration
|
|
{
|
|
return ActionSurfaceDeclaration::forPage(ActionSurfaceProfile::ListOnlyReadOnly, ActionSurfaceType::ReadOnlyRegistryReport)
|
|
->exempt(ActionSurfaceSlot::ListHeader, 'Coverage v2 Readiness is read-only and exposes no page header actions.')
|
|
->satisfy(ActionSurfaceSlot::InspectAffordance, ActionSurfaceInspectAffordance::PrimaryLinkColumn->value)
|
|
->withPrimaryLinkColumnReason('Only the primary name/resource column opens the read-only inspect slide-over; full-row click would conflict with dense registry comparison columns.')
|
|
->exempt(ActionSurfaceSlot::ListRowMoreMenu, 'The surface uses primary link columns for read-only inspect details and no secondary row menu.')
|
|
->exempt(ActionSurfaceSlot::ListBulkMoreGroup, 'Coverage v2 Readiness does not expose bulk actions.')
|
|
->satisfy(ActionSurfaceSlot::ListEmptyState, 'Each table has a scoped no-data state for empty Coverage v2 registry or resource rows.');
|
|
}
|
|
|
|
public static function canAccess(): bool
|
|
{
|
|
$environment = static::resolveTenantContextForCurrentPanel();
|
|
$user = auth()->user();
|
|
|
|
if (! $environment instanceof ManagedEnvironment || ! $user instanceof User) {
|
|
return false;
|
|
}
|
|
|
|
return app(ManagedEnvironmentAccessScopeResolver::class)
|
|
->decision($user, $environment, Capabilities::EVIDENCE_VIEW)
|
|
->allowed();
|
|
}
|
|
|
|
public static function shouldRegisterNavigation(): bool
|
|
{
|
|
return NavigationScope::shouldRegisterEnvironmentNavigation()
|
|
&& parent::shouldRegisterNavigation();
|
|
}
|
|
|
|
public function mount(Workspace|int|string|null $workspace = null, ManagedEnvironment|int|string|null $environment = null): void
|
|
{
|
|
$resolvedEnvironment = $environment instanceof ManagedEnvironment
|
|
? $environment
|
|
: static::resolveTenantContextForCurrentPanel();
|
|
|
|
if (! $resolvedEnvironment instanceof ManagedEnvironment || ! is_numeric($resolvedEnvironment->workspace_id)) {
|
|
abort(404);
|
|
}
|
|
|
|
if ($workspace instanceof Workspace && (int) $workspace->getKey() !== (int) $resolvedEnvironment->workspace_id) {
|
|
abort(404);
|
|
}
|
|
|
|
if ((is_int($workspace) || is_string($workspace)) && is_numeric($workspace) && (int) $workspace !== (int) $resolvedEnvironment->workspace_id) {
|
|
abort(404);
|
|
}
|
|
|
|
$user = auth()->user();
|
|
|
|
if (! $user instanceof User) {
|
|
abort(403);
|
|
}
|
|
|
|
$decision = app(ManagedEnvironmentAccessScopeResolver::class)
|
|
->decision($user, $resolvedEnvironment, Capabilities::EVIDENCE_VIEW);
|
|
|
|
if (! $decision->allowed()) {
|
|
abort($decision->denialHttpStatus ?? 403);
|
|
}
|
|
|
|
$this->environmentId = (int) $resolvedEnvironment->getKey();
|
|
}
|
|
|
|
public function getTitle(): string|Htmlable
|
|
{
|
|
return 'Coverage v2 Readiness';
|
|
}
|
|
|
|
public function environment(): ManagedEnvironment
|
|
{
|
|
$environment = ManagedEnvironment::query()
|
|
->with('workspace')
|
|
->whereKey($this->environmentId)
|
|
->first();
|
|
|
|
if (! $environment instanceof ManagedEnvironment) {
|
|
abort(404);
|
|
}
|
|
|
|
return $environment;
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function readinessSummary(): array
|
|
{
|
|
return app(CoverageV2ReadinessReadModel::class)
|
|
->summary($this->environment());
|
|
}
|
|
|
|
/**
|
|
* @param array<mixed> $parameters
|
|
*/
|
|
public static function getUrl(array $parameters = [], bool $isAbsolute = true, ?string $panel = null, ?Model $tenant = null): string
|
|
{
|
|
$panelId = $panel ?? Filament::getCurrentOrDefaultPanel()?->getId() ?? 'admin';
|
|
|
|
if ($panelId !== 'admin') {
|
|
return parent::getUrl($parameters, $isAbsolute, $panelId, $tenant);
|
|
}
|
|
|
|
$environment = static::resolveUrlEnvironment($parameters, $tenant);
|
|
|
|
if (! $environment instanceof ManagedEnvironment) {
|
|
return url('/admin');
|
|
}
|
|
|
|
$workspace = static::resolveUrlWorkspace($environment, $parameters);
|
|
|
|
if (! $workspace instanceof Workspace && ! is_int($workspace) && ! is_string($workspace)) {
|
|
return url('/admin');
|
|
}
|
|
|
|
$parameters['environment'] ??= $environment;
|
|
$parameters['workspace'] ??= $workspace;
|
|
unset($parameters['tenant']);
|
|
|
|
return parent::getUrl($parameters, $isAbsolute, $panelId, null);
|
|
}
|
|
|
|
/**
|
|
* @param array<mixed> $parameters
|
|
*/
|
|
private static function resolveUrlEnvironment(array $parameters, ?Model $tenant = null): ?ManagedEnvironment
|
|
{
|
|
$parameterTenant = $parameters['tenant'] ?? $parameters['environment'] ?? null;
|
|
|
|
if ($parameterTenant instanceof ManagedEnvironment) {
|
|
return $parameterTenant;
|
|
}
|
|
|
|
if ($tenant instanceof ManagedEnvironment) {
|
|
return $tenant;
|
|
}
|
|
|
|
return static::resolveTenantContextForCurrentPanel();
|
|
}
|
|
|
|
/**
|
|
* @param array<mixed> $parameters
|
|
*/
|
|
private static function resolveUrlWorkspace(ManagedEnvironment $environment, array $parameters): Workspace|string|int|null
|
|
{
|
|
$workspace = $parameters['workspace'] ?? null;
|
|
|
|
if ($workspace instanceof Workspace || is_string($workspace) || is_int($workspace)) {
|
|
return $workspace;
|
|
}
|
|
|
|
$workspace = $environment->workspace;
|
|
|
|
if ($workspace instanceof Workspace) {
|
|
return $workspace;
|
|
}
|
|
|
|
return $environment->workspace()->first();
|
|
}
|
|
}
|