feat: clean up legacy tenant environment context #372

Merged
ahmido merged 1 commits from 317-legacy-tenant-environment-context-cleanup into platform-dev 2026-05-16 18:25:38 +00:00
227 changed files with 2639 additions and 1193 deletions

View File

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace App\Filament\Concerns;
use Filament\Facades\Filament;
use Illuminate\Database\Eloquent\Model;
trait CleansAdminTenantQueryParameter
{
/**
* @param array<string, 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') {
unset($parameters['tenant']);
return parent::getUrl($parameters, $isAbsolute, $panelId, null);
}
return parent::getUrl($parameters, $isAbsolute, $panelId, $tenant);
}
}

View File

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace App\Filament\Concerns;
use Filament\Facades\Filament;
use Illuminate\Database\Eloquent\Model;
trait CleansAdminTenantResourceQueryParameter
{
/**
* @param array<string, mixed> $parameters
*/
public static function getUrl(?string $name = null, array $parameters = [], bool $isAbsolute = true, ?string $panel = null, ?Model $tenant = null, bool $shouldGuessMissingParameters = false): string
{
$panelId = $panel ?? Filament::getCurrentOrDefaultPanel()?->getId() ?? 'admin';
if ($panelId === 'admin') {
unset($parameters['tenant']);
return parent::getUrl($name, $parameters, $isAbsolute, $panelId, null, $shouldGuessMissingParameters);
}
return parent::getUrl($name, $parameters, $isAbsolute, $panelId, $tenant, $shouldGuessMissingParameters);
}
}

View File

@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
namespace App\Filament\Concerns;
use App\Models\ManagedEnvironment;
use Filament\Facades\Filament;
use Illuminate\Database\Eloquent\Model;
trait UsesAdminEnvironmentFilterQueryParameter
{
/**
* @param array<string, 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 = $tenant instanceof ManagedEnvironment ? $tenant : null;
$parameterTenant = $parameters['tenant'] ?? null;
if (! $environment instanceof ManagedEnvironment && $parameterTenant instanceof ManagedEnvironment) {
$environment = $parameterTenant;
}
unset($parameters['tenant']);
$url = parent::getUrl($parameters, $isAbsolute, $panelId, null);
return $environment instanceof ManagedEnvironment
? static::withEnvironmentId($url, $environment)
: $url;
}
private static function withEnvironmentId(string $url, ManagedEnvironment $environment): string
{
$parts = parse_url($url);
if (! is_array($parts)) {
return $url;
}
$query = [];
parse_str((string) ($parts['query'] ?? ''), $query);
$query['environment_id'] = (int) $environment->getKey();
unset($query['tenant'], $query['tenant_id'], $query['managed_environment_id'], $query['tenant_scope'], $query['environment']);
$parts['query'] = http_build_query($query, '', '&', PHP_QUERY_RFC3986);
$rebuilt = '';
if (isset($parts['scheme'])) {
$rebuilt .= $parts['scheme'].'://';
}
if (isset($parts['host'])) {
$rebuilt .= $parts['host'];
}
if (isset($parts['port'])) {
$rebuilt .= ':'.$parts['port'];
}
$rebuilt .= $parts['path'] ?? '';
if (($parts['query'] ?? '') !== '') {
$rebuilt .= '?'.$parts['query'];
}
if (isset($parts['fragment'])) {
$rebuilt .= '#'.$parts['fragment'];
}
return $rebuilt;
}
}

View File

@ -10,7 +10,7 @@
use Filament\Panel; use Filament\Panel;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
trait WorkspaceScopedTenantRoutes trait WorkspaceScopedEnvironmentRoutes
{ {
public static function getSlug(?Panel $panel = null): string public static function getSlug(?Panel $panel = null): string
{ {
@ -28,7 +28,7 @@ public static function getUrl(?string $name = null, array $parameters = [], bool
return parent::getUrl($name, $parameters, $isAbsolute, $panelId, $tenant, $shouldGuessMissingParameters); return parent::getUrl($name, $parameters, $isAbsolute, $panelId, $tenant, $shouldGuessMissingParameters);
} }
$resolvedTenant = static::resolveWorkspaceScopedTenant($parameters, $tenant); $resolvedTenant = static::resolveWorkspaceScopedEnvironment($parameters, $tenant);
if (! $resolvedTenant instanceof ManagedEnvironment) { if (! $resolvedTenant instanceof ManagedEnvironment) {
return url('/admin'); return url('/admin');
@ -49,7 +49,7 @@ public static function getUrl(?string $name = null, array $parameters = [], bool
protected static function workspaceScopedSlug(string $slug, ?Panel $panel = null): string protected static function workspaceScopedSlug(string $slug, ?Panel $panel = null): string
{ {
if (! static::shouldUseWorkspaceScopedTenantRoutes($panel)) { if (! static::shouldUseWorkspaceScopedEnvironmentRoutes($panel)) {
return $slug; return $slug;
} }
@ -60,7 +60,7 @@ protected static function workspaceScopedSlug(string $slug, ?Panel $panel = null
: $prefix.ltrim($slug, '/'); : $prefix.ltrim($slug, '/');
} }
protected static function shouldUseWorkspaceScopedTenantRoutes(?Panel $panel = null): bool protected static function shouldUseWorkspaceScopedEnvironmentRoutes(?Panel $panel = null): bool
{ {
$panelId = $panel?->getId() ?? Filament::getCurrentOrDefaultPanel()?->getId() ?? 'admin'; $panelId = $panel?->getId() ?? Filament::getCurrentOrDefaultPanel()?->getId() ?? 'admin';
@ -70,7 +70,7 @@ protected static function shouldUseWorkspaceScopedTenantRoutes(?Panel $panel = n
/** /**
* @param array<string, mixed> $parameters * @param array<string, mixed> $parameters
*/ */
protected static function resolveWorkspaceScopedTenant(array $parameters, ?Model $tenant = null): ?ManagedEnvironment protected static function resolveWorkspaceScopedEnvironment(array $parameters, ?Model $tenant = null): ?ManagedEnvironment
{ {
$parameterTenant = $parameters['tenant'] ?? $parameters['environment'] ?? null; $parameterTenant = $parameters['tenant'] ?? $parameters['environment'] ?? null;
@ -85,7 +85,7 @@ protected static function resolveWorkspaceScopedTenant(array $parameters, ?Model
$record = $parameters['record'] ?? null; $record = $parameters['record'] ?? null;
if ($record instanceof Model) { if ($record instanceof Model) {
$relationshipName = static::workspaceScopedTenantRelationshipName(); $relationshipName = static::workspaceScopedEnvironmentRelationshipName();
if (method_exists($record, $relationshipName)) { if (method_exists($record, $relationshipName)) {
$recordTenant = $record->getRelationValue($relationshipName); $recordTenant = $record->getRelationValue($relationshipName);
@ -139,7 +139,7 @@ protected static function resolveWorkspaceScopedWorkspace(ManagedEnvironment $te
return $tenant->workspace()->first(); return $tenant->workspace()->first();
} }
protected static function workspaceScopedTenantRelationshipName(): string protected static function workspaceScopedEnvironmentRelationshipName(): string
{ {
$relationshipName = property_exists(static::class, 'tenantOwnershipRelationshipName') $relationshipName = property_exists(static::class, 'tenantOwnershipRelationshipName')
? static::$tenantOwnershipRelationshipName ? static::$tenantOwnershipRelationshipName

View File

@ -5,11 +5,12 @@
namespace App\Filament\Pages; namespace App\Filament\Pages;
use App\Filament\Concerns\ResolvesPanelTenantContext; use App\Filament\Concerns\ResolvesPanelTenantContext;
use App\Filament\Concerns\UsesAdminEnvironmentFilterQueryParameter;
use App\Filament\Resources\BaselineProfileResource; use App\Filament\Resources\BaselineProfileResource;
use App\Filament\Resources\FindingResource; use App\Filament\Resources\FindingResource;
use App\Models\BaselineProfile; use App\Models\BaselineProfile;
use App\Models\OperationRun;
use App\Models\ManagedEnvironment; use App\Models\ManagedEnvironment;
use App\Models\OperationRun;
use App\Models\User; use App\Models\User;
use App\Services\Auth\CapabilityResolver; use App\Services\Auth\CapabilityResolver;
use App\Services\Baselines\BaselineCompareService; use App\Services\Baselines\BaselineCompareService;
@ -17,16 +18,16 @@
use App\Support\Baselines\BaselineCaptureMode; use App\Support\Baselines\BaselineCaptureMode;
use App\Support\Baselines\BaselineCompareEvidenceGapDetails; use App\Support\Baselines\BaselineCompareEvidenceGapDetails;
use App\Support\Baselines\BaselineCompareStats; use App\Support\Baselines\BaselineCompareStats;
use App\Support\Navigation\CanonicalNavigationContext;
use App\Support\Navigation\NavigationScope;
use App\Support\Baselines\TenantGovernanceAggregate; use App\Support\Baselines\TenantGovernanceAggregate;
use App\Support\Baselines\TenantGovernanceAggregateResolver; use App\Support\Baselines\TenantGovernanceAggregateResolver;
use App\Support\Navigation\CanonicalNavigationContext;
use App\Support\Navigation\NavigationScope;
use App\Support\OperationRunLinks; use App\Support\OperationRunLinks;
use App\Support\OperationRunType; use App\Support\OperationRunType;
use App\Support\OpsUx\OperationUxPresenter; use App\Support\OpsUx\OperationUxPresenter;
use App\Support\OpsUx\OpsUxBrowserEvents; use App\Support\OpsUx\OpsUxBrowserEvents;
use App\Support\ReasonTranslation\ReasonPresenter;
use App\Support\Rbac\UiEnforcement; use App\Support\Rbac\UiEnforcement;
use App\Support\ReasonTranslation\ReasonPresenter;
use App\Support\Ui\ActionSurface\ActionSurfaceDeclaration; use App\Support\Ui\ActionSurface\ActionSurfaceDeclaration;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile; use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceSlot; use App\Support\Ui\ActionSurface\Enums\ActionSurfaceSlot;
@ -39,6 +40,7 @@
class BaselineCompareLanding extends Page class BaselineCompareLanding extends Page
{ {
use ResolvesPanelTenantContext; use ResolvesPanelTenantContext;
use UsesAdminEnvironmentFilterQueryParameter;
protected const MONITORING_PAGE_STATE_CONTRACT = [ protected const MONITORING_PAGE_STATE_CONTRACT = [
'surfaceKey' => 'baseline_compare_landing', 'surfaceKey' => 'baseline_compare_landing',

View File

@ -126,7 +126,7 @@ public function selectEnvironment(int $tenantId): void
$this->persistLastTenant($user, $tenant); $this->persistLastTenant($user, $tenant);
if (! $workspaceContext->rememberTenantContext($tenant, request())) { if (! $workspaceContext->rememberEnvironmentContext($tenant, request())) {
abort(404); abort(404);
} }

View File

@ -12,7 +12,7 @@
use App\Models\Workspace; use App\Models\Workspace;
use App\Services\Auth\WorkspaceCapabilityResolver; use App\Services\Auth\WorkspaceCapabilityResolver;
use App\Services\Findings\FindingAssignmentHygieneService; use App\Services\Findings\FindingAssignmentHygieneService;
use App\Support\Filament\CanonicalAdminTenantFilterState; use App\Support\Filament\CanonicalAdminEnvironmentFilterState;
use App\Support\Filament\TablePaginationProfiles; use App\Support\Filament\TablePaginationProfiles;
use App\Support\Navigation\CanonicalNavigationContext; use App\Support\Navigation\CanonicalNavigationContext;
use App\Support\OperateHub\OperateHubShell; use App\Support\OperateHub\OperateHubShell;
@ -78,7 +78,7 @@ public function mount(): void
$this->reasonFilter = $this->resolveRequestedReasonFilter(); $this->reasonFilter = $this->resolveRequestedReasonFilter();
$this->authorizePageAccess(); $this->authorizePageAccess();
app(CanonicalAdminTenantFilterState::class)->sync( app(CanonicalAdminEnvironmentFilterState::class)->sync(
$this->getTableFiltersSessionKey(), $this->getTableFiltersSessionKey(),
[], [],
request(), request(),
@ -486,14 +486,14 @@ private function filteredTenant(): ?ManagedEnvironment
private function activeVisibleTenant(): ?ManagedEnvironment private function activeVisibleTenant(): ?ManagedEnvironment
{ {
$activeTenant = app(OperateHubShell::class)->activeEntitledTenant(request()); $activeEnvironment = app(OperateHubShell::class)->activeEntitledTenant(request());
if (! $activeTenant instanceof ManagedEnvironment) { if (! $activeEnvironment instanceof ManagedEnvironment) {
return null; return null;
} }
foreach ($this->visibleTenants() as $tenant) { foreach ($this->visibleTenants() as $tenant) {
if ($tenant->is($activeTenant)) { if ($tenant->is($activeEnvironment)) {
return $tenant; return $tenant;
} }
} }
@ -509,9 +509,9 @@ private function tenantPrefilterSource(): string
return 'none'; return 'none';
} }
$activeTenant = $this->activeVisibleTenant(); $activeEnvironment = $this->activeVisibleTenant();
if ($activeTenant instanceof ManagedEnvironment && $activeTenant->is($tenant)) { if ($activeEnvironment instanceof ManagedEnvironment && $activeEnvironment->is($tenant)) {
return 'active_tenant_context'; return 'active_tenant_context';
} }

View File

@ -16,7 +16,7 @@
use App\Support\Auth\Capabilities; use App\Support\Auth\Capabilities;
use App\Support\Badges\BadgeDomain; use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer; use App\Support\Badges\BadgeRenderer;
use App\Support\Filament\CanonicalAdminTenantFilterState; use App\Support\Filament\CanonicalAdminEnvironmentFilterState;
use App\Support\Filament\TablePaginationProfiles; use App\Support\Filament\TablePaginationProfiles;
use App\Support\Navigation\CanonicalNavigationContext; use App\Support\Navigation\CanonicalNavigationContext;
use App\Support\OperateHub\OperateHubShell; use App\Support\OperateHub\OperateHubShell;
@ -92,7 +92,7 @@ public function mount(): void
$this->queueView = $this->resolveRequestedQueueView(); $this->queueView = $this->resolveRequestedQueueView();
$this->authorizePageAccess(); $this->authorizePageAccess();
app(CanonicalAdminTenantFilterState::class)->sync( app(CanonicalAdminEnvironmentFilterState::class)->sync(
$this->getTableFiltersSessionKey(), $this->getTableFiltersSessionKey(),
[], [],
request(), request(),
@ -494,12 +494,12 @@ private function filteredQueueQuery(
return $query return $query
->orderByRaw( ->orderByRaw(
"case 'case
when due_at is not null and due_at < ? then 0 when due_at is not null and due_at < ? then 0
when status = ? then 1 when status = ? then 1
when status = ? then 2 when status = ? then 2
else 3 else 3
end asc", end asc',
[now(), Finding::STATUS_REOPENED, Finding::STATUS_NEW], [now(), Finding::STATUS_REOPENED, Finding::STATUS_NEW],
) )
->orderByRaw('case when due_at is null then 1 else 0 end asc') ->orderByRaw('case when due_at is null then 1 else 0 end asc')
@ -605,14 +605,14 @@ private function filteredTenant(): ?ManagedEnvironment
private function activeVisibleTenant(): ?ManagedEnvironment private function activeVisibleTenant(): ?ManagedEnvironment
{ {
$activeTenant = app(OperateHubShell::class)->activeEntitledTenant(request()); $activeEnvironment = app(OperateHubShell::class)->activeEntitledTenant(request());
if (! $activeTenant instanceof ManagedEnvironment) { if (! $activeEnvironment instanceof ManagedEnvironment) {
return null; return null;
} }
foreach ($this->visibleTenants() as $tenant) { foreach ($this->visibleTenants() as $tenant) {
if ($tenant->is($activeTenant)) { if ($tenant->is($activeEnvironment)) {
return $tenant; return $tenant;
} }
} }
@ -628,9 +628,9 @@ private function tenantPrefilterSource(): string
return 'none'; return 'none';
} }
$activeTenant = $this->activeVisibleTenant(); $activeEnvironment = $this->activeVisibleTenant();
if ($activeTenant instanceof ManagedEnvironment && $activeTenant->is($tenant)) { if ($activeEnvironment instanceof ManagedEnvironment && $activeEnvironment->is($tenant)) {
return 'active_tenant_context'; return 'active_tenant_context';
} }

View File

@ -15,7 +15,7 @@
use App\Support\Auth\Capabilities; use App\Support\Auth\Capabilities;
use App\Support\Badges\BadgeDomain; use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer; use App\Support\Badges\BadgeRenderer;
use App\Support\Filament\CanonicalAdminTenantFilterState; use App\Support\Filament\CanonicalAdminEnvironmentFilterState;
use App\Support\Filament\TablePaginationProfiles; use App\Support\Filament\TablePaginationProfiles;
use App\Support\Navigation\CanonicalNavigationContext; use App\Support\Navigation\CanonicalNavigationContext;
use App\Support\OperateHub\OperateHubShell; use App\Support\OperateHub\OperateHubShell;
@ -84,7 +84,7 @@ public function mount(): void
{ {
$this->authorizePageAccess(); $this->authorizePageAccess();
app(CanonicalAdminTenantFilterState::class)->sync( app(CanonicalAdminEnvironmentFilterState::class)->sync(
$this->getTableFiltersSessionKey(), $this->getTableFiltersSessionKey(),
['overdue', 'reopened', 'high_severity'], ['overdue', 'reopened', 'high_severity'],
request(), request(),
@ -270,9 +270,9 @@ public function emptyState(): array
]; ];
} }
$activeTenant = $this->activeVisibleTenant(); $activeEnvironment = $this->activeVisibleTenant();
if ($activeTenant instanceof ManagedEnvironment) { if ($activeEnvironment instanceof ManagedEnvironment) {
return [ return [
'title' => 'No visible assigned findings right now', 'title' => 'No visible assigned findings right now',
'body' => 'Nothing currently assigned to you needs attention in the visible environment scope. You can still open environment findings for broader context.', 'body' => 'Nothing currently assigned to you needs attention in the visible environment scope. You can still open environment findings for broader context.',
@ -280,7 +280,7 @@ public function emptyState(): array
'action_name' => 'open_tenant_findings_empty', 'action_name' => 'open_tenant_findings_empty',
'action_label' => 'Open environment findings', 'action_label' => 'Open environment findings',
'action_kind' => 'url', 'action_kind' => 'url',
'action_url' => FindingResource::getUrl('index', tenant: $activeTenant), 'action_url' => FindingResource::getUrl('index', tenant: $activeEnvironment),
]; ];
} }
@ -561,14 +561,14 @@ private function filteredTenant(): ?ManagedEnvironment
private function activeVisibleTenant(): ?ManagedEnvironment private function activeVisibleTenant(): ?ManagedEnvironment
{ {
$activeTenant = app(OperateHubShell::class)->activeEntitledTenant(request()); $activeEnvironment = app(OperateHubShell::class)->activeEntitledTenant(request());
if (! $activeTenant instanceof ManagedEnvironment) { if (! $activeEnvironment instanceof ManagedEnvironment) {
return null; return null;
} }
foreach ($this->visibleTenants() as $tenant) { foreach ($this->visibleTenants() as $tenant) {
if ($tenant->is($activeTenant)) { if ($tenant->is($activeEnvironment)) {
return $tenant; return $tenant;
} }
} }
@ -584,9 +584,9 @@ private function tenantPrefilterSource(): string
return 'none'; return 'none';
} }
$activeTenant = $this->activeVisibleTenant(); $activeEnvironment = $this->activeVisibleTenant();
if ($activeTenant instanceof ManagedEnvironment && $activeTenant->is($tenant)) { if ($activeEnvironment instanceof ManagedEnvironment && $activeEnvironment->is($tenant)) {
return 'active_tenant_context'; return 'active_tenant_context';
} }

View File

@ -4,6 +4,7 @@
namespace App\Filament\Pages\Governance; namespace App\Filament\Pages\Governance;
use App\Filament\Concerns\CleansAdminTenantQueryParameter;
use App\Filament\Concerns\ClearsWorkspaceHubEnvironmentFilterState; use App\Filament\Concerns\ClearsWorkspaceHubEnvironmentFilterState;
use App\Filament\Resources\FindingExceptionResource; use App\Filament\Resources\FindingExceptionResource;
use App\Models\FindingException; use App\Models\FindingException;
@ -40,6 +41,7 @@
class DecisionRegister extends Page implements HasTable class DecisionRegister extends Page implements HasTable
{ {
use CleansAdminTenantQueryParameter;
use ClearsWorkspaceHubEnvironmentFilterState; use ClearsWorkspaceHubEnvironmentFilterState;
use InteractsWithTable; use InteractsWithTable;

View File

@ -4,6 +4,7 @@
namespace App\Filament\Pages\Governance; namespace App\Filament\Pages\Governance;
use App\Filament\Concerns\CleansAdminTenantQueryParameter;
use App\Models\ManagedEnvironment; use App\Models\ManagedEnvironment;
use App\Models\User; use App\Models\User;
use App\Models\Workspace; use App\Models\Workspace;
@ -29,6 +30,8 @@
class GovernanceInbox extends Page class GovernanceInbox extends Page
{ {
use CleansAdminTenantQueryParameter;
protected static bool $isDiscovered = false; protected static bool $isDiscovered = false;
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-inbox-stack'; protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-inbox-stack';

View File

@ -5,8 +5,8 @@
namespace App\Filament\Pages\Monitoring; namespace App\Filament\Pages\Monitoring;
use App\Models\AuditLog as AuditLogModel; use App\Models\AuditLog as AuditLogModel;
use App\Models\SupportAccessGrant;
use App\Models\ManagedEnvironment; use App\Models\ManagedEnvironment;
use App\Models\SupportAccessGrant;
use App\Models\User; use App\Models\User;
use App\Models\Workspace; use App\Models\Workspace;
use App\Services\Auth\WorkspaceCapabilityResolver; use App\Services\Auth\WorkspaceCapabilityResolver;
@ -14,7 +14,7 @@
use App\Support\Auth\Capabilities; use App\Support\Auth\Capabilities;
use App\Support\Badges\BadgeDomain; use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer; use App\Support\Badges\BadgeRenderer;
use App\Support\Filament\CanonicalAdminTenantFilterState; use App\Support\Filament\CanonicalAdminEnvironmentFilterState;
use App\Support\Filament\FilterOptionCatalog; use App\Support\Filament\FilterOptionCatalog;
use App\Support\Filament\FilterPresets; use App\Support\Filament\FilterPresets;
use App\Support\Filament\TablePaginationProfiles; use App\Support\Filament\TablePaginationProfiles;
@ -164,7 +164,7 @@ public function mount(): void
$this->supportAccessOnly = request()->boolean('supportAccess'); $this->supportAccessOnly = request()->boolean('supportAccess');
$requestedEventId = is_numeric(request()->query('event')) ? (int) request()->query('event') : null; $requestedEventId = is_numeric(request()->query('event')) ? (int) request()->query('event') : null;
app(CanonicalAdminTenantFilterState::class)->sync($this->getTableFiltersSessionKey(), request: request()); app(CanonicalAdminEnvironmentFilterState::class)->sync($this->getTableFiltersSessionKey(), request: request());
$this->mountInteractsWithTable(); $this->mountInteractsWithTable();
@ -616,14 +616,14 @@ private function tenantFilterOptions(): array
private function defaultTenantFilter(): ?string private function defaultTenantFilter(): ?string
{ {
$activeTenant = app(OperateHubShell::class)->activeEntitledTenant(request()); $activeEnvironment = app(OperateHubShell::class)->activeEntitledTenant(request());
if (! $activeTenant instanceof ManagedEnvironment) { if (! $activeEnvironment instanceof ManagedEnvironment) {
return null; return null;
} }
return array_key_exists((int) $activeTenant->getKey(), $this->authorizedTenants()) return array_key_exists((int) $activeEnvironment->getKey(), $this->authorizedTenants())
? (string) $activeTenant->getKey() ? (string) $activeEnvironment->getKey()
: null; : null;
} }

View File

@ -4,6 +4,7 @@
namespace App\Filament\Pages\Monitoring; namespace App\Filament\Pages\Monitoring;
use App\Filament\Concerns\CleansAdminTenantQueryParameter;
use App\Filament\Concerns\ClearsWorkspaceHubEnvironmentFilterState; use App\Filament\Concerns\ClearsWorkspaceHubEnvironmentFilterState;
use App\Filament\Resources\FindingExceptionResource; use App\Filament\Resources\FindingExceptionResource;
use App\Filament\Resources\FindingResource; use App\Filament\Resources\FindingResource;
@ -17,7 +18,7 @@
use App\Support\Auth\Capabilities; use App\Support\Auth\Capabilities;
use App\Support\Badges\BadgeDomain; use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer; use App\Support\Badges\BadgeRenderer;
use App\Support\Filament\CanonicalAdminTenantFilterState; use App\Support\Filament\CanonicalAdminEnvironmentFilterState;
use App\Support\Filament\FilterOptionCatalog; use App\Support\Filament\FilterOptionCatalog;
use App\Support\Filament\TablePaginationProfiles; use App\Support\Filament\TablePaginationProfiles;
use App\Support\Navigation\CanonicalNavigationContext; use App\Support\Navigation\CanonicalNavigationContext;
@ -52,6 +53,7 @@
class FindingExceptionsQueue extends Page implements HasTable class FindingExceptionsQueue extends Page implements HasTable
{ {
use CleansAdminTenantQueryParameter;
use ClearsWorkspaceHubEnvironmentFilterState; use ClearsWorkspaceHubEnvironmentFilterState;
use InteractsWithTable; use InteractsWithTable;
@ -653,7 +655,7 @@ private function filteredTenant(): ?ManagedEnvironment
private function currentTenantFilterId(): ?int private function currentTenantFilterId(): ?int
{ {
$tenantFilter = app(CanonicalAdminTenantFilterState::class)->currentFilterValue( $tenantFilter = app(CanonicalAdminEnvironmentFilterState::class)->currentFilterValue(
$this->getTableFiltersSessionKey(), $this->getTableFiltersSessionKey(),
$this->tableFilters ?? [], $this->tableFilters ?? [],
request(), request(),

View File

@ -13,7 +13,7 @@
use App\Models\Workspace; use App\Models\Workspace;
use App\Services\Auth\ManagedEnvironmentAccessScopeResolver; use App\Services\Auth\ManagedEnvironmentAccessScopeResolver;
use App\Services\Auth\WorkspaceCapabilityResolver; use App\Services\Auth\WorkspaceCapabilityResolver;
use App\Support\Filament\CanonicalAdminTenantFilterState; use App\Support\Filament\CanonicalAdminEnvironmentFilterState;
use App\Support\ManagedEnvironmentLinks; use App\Support\ManagedEnvironmentLinks;
use App\Support\Navigation\CanonicalNavigationContext; use App\Support\Navigation\CanonicalNavigationContext;
use App\Support\Navigation\WorkspaceHubEnvironmentFilter; use App\Support\Navigation\WorkspaceHubEnvironmentFilter;
@ -231,7 +231,7 @@ protected function getHeaderActions(): array
->disabled(), ->disabled(),
]; ];
$activeTenant = $this->currentTenantFilterId() === null $activeEnvironment = $this->currentTenantFilterId() === null
? $operateHubShell->activeEntitledTenant(request()) ? $operateHubShell->activeEntitledTenant(request())
: null; : null;
@ -241,22 +241,22 @@ protected function getHeaderActions(): array
->icon('heroicon-o-arrow-left') ->icon('heroicon-o-arrow-left')
->color('gray') ->color('gray')
->url($navigationContext->backLinkUrl); ->url($navigationContext->backLinkUrl);
} elseif ($activeTenant instanceof ManagedEnvironment) { } elseif ($activeEnvironment instanceof ManagedEnvironment) {
$actions[] = Action::make('operate_hub_back_to_tenant_operations') $actions[] = Action::make('operate_hub_back_to_tenant_operations')
->label('Back to '.$activeTenant->name) ->label('Back to '.$activeEnvironment->name)
->icon('heroicon-o-arrow-left') ->icon('heroicon-o-arrow-left')
->color('gray') ->color('gray')
->url(ManagedEnvironmentLinks::viewUrl($activeTenant)); ->url(ManagedEnvironmentLinks::viewUrl($activeEnvironment));
} }
if ($activeTenant instanceof ManagedEnvironment) { if ($activeEnvironment instanceof ManagedEnvironment) {
$actions[] = Action::make('operate_hub_show_all_tenants') $actions[] = Action::make('operate_hub_show_all_tenants')
->label(__('localization.shell.show_all_environments')) ->label(__('localization.shell.show_all_environments'))
->color('gray') ->color('gray')
->action(function (): void { ->action(function (): void {
Filament::setTenant(null, true); Filament::setTenant(null, true);
app(WorkspaceContext::class)->clearLastTenantId(request()); app(WorkspaceContext::class)->clearLastEnvironmentId(request());
$this->removeTableFilter('managed_environment_id'); $this->removeTableFilter('managed_environment_id');
@ -283,7 +283,7 @@ public function landingHierarchySummary(): array
$operateHubShell = app(OperateHubShell::class); $operateHubShell = app(OperateHubShell::class);
$navigationContext = $this->navigationContext(); $navigationContext = $this->navigationContext();
$filteredTenant = $this->filteredTenant(); $filteredTenant = $this->filteredTenant();
$activeTenant = $filteredTenant instanceof ManagedEnvironment $activeEnvironment = $filteredTenant instanceof ManagedEnvironment
? null ? null
: $operateHubShell->activeEntitledTenant(request()); : $operateHubShell->activeEntitledTenant(request());
@ -293,8 +293,8 @@ public function landingHierarchySummary(): array
if ($navigationContext?->backLinkLabel !== null && $navigationContext->backLinkUrl !== null) { if ($navigationContext?->backLinkLabel !== null && $navigationContext->backLinkUrl !== null) {
$returnLabel = $navigationContext->backLinkLabel; $returnLabel = $navigationContext->backLinkLabel;
$returnBody = 'Return to the originating monitoring surface without competing with the current tab, filters, or row inspection flow.'; $returnBody = 'Return to the originating monitoring surface without competing with the current tab, filters, or row inspection flow.';
} elseif ($activeTenant instanceof ManagedEnvironment) { } elseif ($activeEnvironment instanceof ManagedEnvironment) {
$returnLabel = 'Back to '.$activeTenant->name; $returnLabel = 'Back to '.$activeEnvironment->name;
$returnBody = 'Return to the tenant dashboard when you need tenant-specific context outside this workspace monitoring landing.'; $returnBody = 'Return to the tenant dashboard when you need tenant-specific context outside this workspace monitoring landing.';
} }
@ -302,13 +302,13 @@ public function landingHierarchySummary(): array
'scope_label' => $operateHubShell->scopeLabel(request()), 'scope_label' => $operateHubShell->scopeLabel(request()),
'scope_body' => $filteredTenant instanceof ManagedEnvironment 'scope_body' => $filteredTenant instanceof ManagedEnvironment
? 'The landing is workspace-scoped and filtered by an explicit environment filter.' ? 'The landing is workspace-scoped and filtered by an explicit environment filter.'
: ($activeTenant instanceof ManagedEnvironment : ($activeEnvironment instanceof ManagedEnvironment
? 'The landing is currently narrowed to one environment inside the active workspace.' ? 'The landing is currently narrowed to one environment inside the active workspace.'
: 'The landing is currently showing workspace-wide monitoring across all entitled environments.'), : 'The landing is currently showing workspace-wide monitoring across all entitled environments.'),
'return_label' => $returnLabel, 'return_label' => $returnLabel,
'return_body' => $returnBody, 'return_body' => $returnBody,
'scope_reset_label' => $activeTenant instanceof ManagedEnvironment ? __('localization.shell.show_all_environments') : null, 'scope_reset_label' => $activeEnvironment instanceof ManagedEnvironment ? __('localization.shell.show_all_environments') : null,
'scope_reset_body' => $activeTenant instanceof ManagedEnvironment 'scope_reset_body' => $activeEnvironment instanceof ManagedEnvironment
? 'Reset the landing back to workspace-wide monitoring when environment-specific context is no longer needed.' ? 'Reset the landing back to workspace-wide monitoring when environment-specific context is no longer needed.'
: null, : null,
'inspect_body' => 'Open a run from the table to enter the canonical monitoring detail viewer.', 'inspect_body' => 'Open a run from the table to enter the canonical monitoring detail viewer.',
@ -534,7 +534,7 @@ private function operationsUrl(array $overrides = []): string
private function currentTenantFilterId(): ?int private function currentTenantFilterId(): ?int
{ {
$tenantFilter = app(CanonicalAdminTenantFilterState::class)->currentFilterValue( $tenantFilter = app(CanonicalAdminEnvironmentFilterState::class)->currentFilterValue(
$this->getTableFiltersSessionKey(), $this->getTableFiltersSessionKey(),
$this->tableFilters ?? [], $this->tableFilters ?? [],
request(), request(),

View File

@ -5,9 +5,9 @@
namespace App\Filament\Pages\Operations; namespace App\Filament\Pages\Operations;
use App\Filament\Resources\OperationRunResource; use App\Filament\Resources\OperationRunResource;
use App\Models\ManagedEnvironment;
use App\Models\OperationRun; use App\Models\OperationRun;
use App\Models\SupportRequest; use App\Models\SupportRequest;
use App\Models\ManagedEnvironment;
use App\Models\User; use App\Models\User;
use App\Services\Audit\WorkspaceAuditLogger; use App\Services\Audit\WorkspaceAuditLogger;
use App\Services\Auth\CapabilityResolver; use App\Services\Auth\CapabilityResolver;
@ -27,10 +27,10 @@
use App\Support\OpsUx\RunDetailPolling; use App\Support\OpsUx\RunDetailPolling;
use App\Support\ProductTelemetry\ProductTelemetryRecorder; use App\Support\ProductTelemetry\ProductTelemetryRecorder;
use App\Support\ProductTelemetry\ProductUsageEventCatalog; use App\Support\ProductTelemetry\ProductUsageEventCatalog;
use App\Support\Rbac\UiEnforcement;
use App\Support\ReasonTranslation\ReasonPresenter; use App\Support\ReasonTranslation\ReasonPresenter;
use App\Support\RedactionIntegrity; use App\Support\RedactionIntegrity;
use App\Support\RestoreSafety\RestoreSafetyCopy; use App\Support\RestoreSafety\RestoreSafetyCopy;
use App\Support\Rbac\UiEnforcement;
use App\Support\SupportDiagnostics\SupportDiagnosticBundleBuilder; use App\Support\SupportDiagnostics\SupportDiagnosticBundleBuilder;
use App\Support\SupportRequests\ExternalSupportDeskHandoffService; use App\Support\SupportRequests\ExternalSupportDeskHandoffService;
use App\Support\SupportRequests\SupportRequestSubmissionService; use App\Support\SupportRequests\SupportRequestSubmissionService;
@ -46,15 +46,15 @@
use Filament\Actions\ActionGroup; use Filament\Actions\ActionGroup;
use Filament\Forms\Components\Placeholder; use Filament\Forms\Components\Placeholder;
use Filament\Forms\Components\Select; use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Textarea; use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Notifications\Notification; use Filament\Notifications\Notification;
use Filament\Pages\Page; use Filament\Pages\Page;
use Filament\Schemas\Components\EmbeddedSchema; use Filament\Schemas\Components\EmbeddedSchema;
use Filament\Schemas\Components\Utilities\Get; use Filament\Schemas\Components\Utilities\Get;
use Filament\Schemas\Schema; use Filament\Schemas\Schema;
use Illuminate\Contracts\View\View;
use Illuminate\Contracts\Support\Htmlable; use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Contracts\View\View;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Support\Str; use Illuminate\Support\Str;
@ -108,7 +108,7 @@ protected function getHeaderActions(): array
{ {
$operateHubShell = app(OperateHubShell::class); $operateHubShell = app(OperateHubShell::class);
$navigationContext = $this->navigationContext(); $navigationContext = $this->navigationContext();
$activeTenant = $operateHubShell->activeEntitledTenant(request()); $activeEnvironment = $operateHubShell->activeEntitledTenant(request());
$runTenantId = isset($this->run) ? (int) ($this->run->managed_environment_id ?? 0) : 0; $runTenantId = isset($this->run) ? (int) ($this->run->managed_environment_id ?? 0) : 0;
$actions = [ $actions = [
@ -123,11 +123,11 @@ protected function getHeaderActions(): array
->label($navigationContext->backLinkLabel) ->label($navigationContext->backLinkLabel)
->color('gray') ->color('gray')
->url($navigationContext->backLinkUrl); ->url($navigationContext->backLinkUrl);
} elseif ($activeTenant instanceof ManagedEnvironment && (int) $activeTenant->getKey() === $runTenantId) { } elseif ($activeEnvironment instanceof ManagedEnvironment && (int) $activeEnvironment->getKey() === $runTenantId) {
$actions[] = Action::make('operate_hub_back_to_tenant_run_detail') $actions[] = Action::make('operate_hub_back_to_tenant_run_detail')
->label('← Back to '.$activeTenant->name) ->label('← Back to '.$activeEnvironment->name)
->color('gray') ->color('gray')
->url(ManagedEnvironmentLinks::viewUrl($activeTenant)); ->url(ManagedEnvironmentLinks::viewUrl($activeEnvironment));
} else { } else {
$actions[] = Action::make('operate_hub_back_to_operations') $actions[] = Action::make('operate_hub_back_to_operations')
->label('Back to Operations') ->label('Back to Operations')
@ -135,7 +135,7 @@ protected function getHeaderActions(): array
->url(fn (): string => OperationRunLinks::index()); ->url(fn (): string => OperationRunLinks::index());
} }
if ($activeTenant instanceof ManagedEnvironment) { if ($activeEnvironment instanceof ManagedEnvironment) {
$actions[] = Action::make('operate_hub_show_all_operations') $actions[] = Action::make('operate_hub_show_all_operations')
->label('Show all operations') ->label('Show all operations')
->color('gray') ->color('gray')
@ -201,7 +201,7 @@ public function monitoringDetailSummary(): array
{ {
$operateHubShell = app(OperateHubShell::class); $operateHubShell = app(OperateHubShell::class);
$navigationContext = $this->navigationContext(); $navigationContext = $this->navigationContext();
$activeTenant = $operateHubShell->activeEntitledTenant(request()); $activeEnvironment = $operateHubShell->activeEntitledTenant(request());
$runTenantId = isset($this->run) ? (int) ($this->run->managed_environment_id ?? 0) : 0; $runTenantId = isset($this->run) ? (int) ($this->run->managed_environment_id ?? 0) : 0;
$navigationLabel = 'Back to Operations'; $navigationLabel = 'Back to Operations';
@ -210,8 +210,8 @@ public function monitoringDetailSummary(): array
if ($navigationContext?->backLinkLabel !== null && $navigationContext->backLinkUrl !== null) { if ($navigationContext?->backLinkLabel !== null && $navigationContext->backLinkUrl !== null) {
$navigationLabel = $navigationContext->backLinkLabel; $navigationLabel = $navigationContext->backLinkLabel;
$navigationBody = 'Return to the originating surface while keeping refresh and follow-up work separate from navigation.'; $navigationBody = 'Return to the originating surface while keeping refresh and follow-up work separate from navigation.';
} elseif ($activeTenant instanceof ManagedEnvironment && (int) $activeTenant->getKey() === $runTenantId) { } elseif ($activeEnvironment instanceof ManagedEnvironment && (int) $activeEnvironment->getKey() === $runTenantId) {
$navigationLabel = 'Back to '.$activeTenant->name; $navigationLabel = 'Back to '.$activeEnvironment->name;
$navigationBody = 'Return to the active tenant dashboard, then widen back to the workspace view only when you need broader monitoring context.'; $navigationBody = 'Return to the active tenant dashboard, then widen back to the workspace view only when you need broader monitoring context.';
} }
@ -727,15 +727,15 @@ public function canonicalContextBanner(): ?array
return null; return null;
} }
$activeTenant = app(OperateHubShell::class)->activeEntitledTenant(request()); $activeEnvironment = app(OperateHubShell::class)->activeEntitledTenant(request());
$runTenant = $this->run->tenant; $runTenant = $this->run->tenant;
if (! $runTenant instanceof ManagedEnvironment) { if (! $runTenant instanceof ManagedEnvironment) {
return [ return [
'tone' => 'slate', 'tone' => 'slate',
'title' => 'Workspace-level operation', 'title' => 'Workspace-level operation',
'body' => $activeTenant instanceof ManagedEnvironment 'body' => $activeEnvironment instanceof ManagedEnvironment
? 'This canonical workspace view is not tied to the current environment context ('.$activeTenant->name.').' ? 'This canonical workspace view is not tied to the current environment context ('.$activeEnvironment->name.').'
: 'This canonical workspace view is not tied to any environment.', : 'This canonical workspace view is not tied to any environment.',
]; ];
} }
@ -744,9 +744,9 @@ public function canonicalContextBanner(): ?array
$tone = 'sky'; $tone = 'sky';
$title = null; $title = null;
if ($activeTenant instanceof ManagedEnvironment && ! $activeTenant->is($runTenant)) { if ($activeEnvironment instanceof ManagedEnvironment && ! $activeEnvironment->is($runTenant)) {
$title = 'Current environment context differs from this operation'; $title = 'Current environment context differs from this operation';
array_unshift($messages, 'Current environment context: '.$activeTenant->name.'.'); array_unshift($messages, 'Current environment context: '.$activeEnvironment->name.'.');
$messages[] = 'This canonical workspace view remains valid without switching environment context.'; $messages[] = 'This canonical workspace view remains valid without switching environment context.';
} }
@ -760,7 +760,7 @@ public function canonicalContextBanner(): ?array
if ($referencedTenant->contextNote !== null) { if ($referencedTenant->contextNote !== null) {
$messages[] = $referencedTenant->contextNote; $messages[] = $referencedTenant->contextNote;
} }
} elseif (! $activeTenant instanceof ManagedEnvironment) { } elseif (! $activeEnvironment instanceof ManagedEnvironment) {
$title ??= 'Canonical workspace view'; $title ??= 'Canonical workspace view';
$messages[] = 'No environment context is currently selected.'; $messages[] = 'No environment context is currently selected.';
} }

View File

@ -4,6 +4,7 @@
namespace App\Filament\Pages\Reviews; namespace App\Filament\Pages\Reviews;
use App\Filament\Concerns\CleansAdminTenantQueryParameter;
use App\Filament\Concerns\ClearsWorkspaceHubEnvironmentFilterState; use App\Filament\Concerns\ClearsWorkspaceHubEnvironmentFilterState;
use App\Filament\Resources\EnvironmentReviewResource; use App\Filament\Resources\EnvironmentReviewResource;
use App\Models\EnvironmentReview; use App\Models\EnvironmentReview;
@ -50,6 +51,7 @@
class CustomerReviewWorkspace extends Page implements HasTable class CustomerReviewWorkspace extends Page implements HasTable
{ {
use CleansAdminTenantQueryParameter;
use ClearsWorkspaceHubEnvironmentFilterState; use ClearsWorkspaceHubEnvironmentFilterState;
use InteractsWithTable; use InteractsWithTable;
@ -99,7 +101,7 @@ public function getTitle(): string
return __('localization.review.customer_review_workspace'); return __('localization.review.customer_review_workspace');
} }
public static function tenantPrefilterUrl(ManagedEnvironment $tenant): string public static function environmentFilterUrl(ManagedEnvironment $tenant): string
{ {
return static::getUrl(panel: 'admin').'?'.http_build_query([ return static::getUrl(panel: 'admin').'?'.http_build_query([
'environment_id' => (int) $tenant->getKey(), 'environment_id' => (int) $tenant->getKey(),
@ -573,7 +575,7 @@ private function latestReviewUrl(ManagedEnvironment $tenant): ?string
static fn (mixed $value): bool => $value !== null && $value !== '', static fn (mixed $value): bool => $value !== null && $value !== '',
); );
return $this->appendQuery(EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $review], $tenant), $query); return $this->appendQuery(EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $review], $tenant), $query);
} }
private function reviewPackDownloadUrl(EnvironmentReview $review, ManagedEnvironment $tenant): ?string private function reviewPackDownloadUrl(EnvironmentReview $review, ManagedEnvironment $tenant): ?string

View File

@ -4,6 +4,7 @@
namespace App\Filament\Pages\Reviews; namespace App\Filament\Pages\Reviews;
use App\Filament\Concerns\CleansAdminTenantQueryParameter;
use App\Filament\Concerns\ClearsWorkspaceHubEnvironmentFilterState; use App\Filament\Concerns\ClearsWorkspaceHubEnvironmentFilterState;
use App\Filament\Resources\EnvironmentReviewResource; use App\Filament\Resources\EnvironmentReviewResource;
use App\Models\EnvironmentReview; use App\Models\EnvironmentReview;
@ -45,6 +46,7 @@
class ReviewRegister extends Page implements HasTable class ReviewRegister extends Page implements HasTable
{ {
use CleansAdminTenantQueryParameter;
use ClearsWorkspaceHubEnvironmentFilterState; use ClearsWorkspaceHubEnvironmentFilterState;
use InteractsWithTable; use InteractsWithTable;
@ -111,7 +113,7 @@ public function table(Table $table): Table
->persistFiltersInSession() ->persistFiltersInSession()
->persistSearchInSession() ->persistSearchInSession()
->persistSortInSession() ->persistSortInSession()
->recordUrl(fn (EnvironmentReview $record): string => EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $record], $record->tenant)) ->recordUrl(fn (EnvironmentReview $record): string => EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $record], $record->tenant))
->columns([ ->columns([
TextColumn::make('tenant.name')->label('Environment')->searchable(), TextColumn::make('tenant.name')->label('Environment')->searchable(),
TextColumn::make('status') TextColumn::make('status')

View File

@ -4,7 +4,6 @@
namespace App\Filament\Pages\Workspaces; namespace App\Filament\Pages\Workspaces;
use BackedEnum;
use App\Exceptions\Onboarding\OnboardingDraftConflictException; use App\Exceptions\Onboarding\OnboardingDraftConflictException;
use App\Exceptions\Onboarding\OnboardingDraftImmutableException; use App\Exceptions\Onboarding\OnboardingDraftImmutableException;
use App\Filament\Resources\ManagedEnvironmentResource; use App\Filament\Resources\ManagedEnvironmentResource;
@ -13,24 +12,22 @@
use App\Jobs\ProviderComplianceSnapshotJob; use App\Jobs\ProviderComplianceSnapshotJob;
use App\Jobs\ProviderConnectionHealthCheckJob; use App\Jobs\ProviderConnectionHealthCheckJob;
use App\Jobs\ProviderInventorySyncJob; use App\Jobs\ProviderInventorySyncJob;
use App\Models\OperationRun;
use App\Models\ProviderConnection;
use App\Models\ManagedEnvironment; use App\Models\ManagedEnvironment;
use App\Models\ManagedEnvironmentOnboardingSession; use App\Models\ManagedEnvironmentOnboardingSession;
use App\Models\OperationRun;
use App\Models\ProviderConnection;
use App\Models\User; use App\Models\User;
use App\Models\VerificationCheckAcknowledgement; use App\Models\VerificationCheckAcknowledgement;
use App\Models\Workspace; use App\Models\Workspace;
use App\Services\Audit\WorkspaceAuditLogger; use App\Services\Audit\WorkspaceAuditLogger;
use App\Services\Auth\ManagedEnvironmentMembershipManager; use App\Services\Auth\ManagedEnvironmentMembershipManager;
use App\Services\Entitlements\WorkspaceCommercialLifecycleResolver;
use App\Services\Intune\AuditLogger; use App\Services\Intune\AuditLogger;
use App\Services\Intune\ManagedEnvironmentRequiredPermissionsViewModelBuilder; use App\Services\Intune\ManagedEnvironmentRequiredPermissionsViewModelBuilder;
use App\Services\Onboarding\OnboardingDraftMutationService; use App\Services\Onboarding\OnboardingDraftMutationService;
use App\Services\Onboarding\OnboardingDraftResolver; use App\Services\Onboarding\OnboardingDraftResolver;
use App\Services\Onboarding\OnboardingDraftStageResolver; use App\Services\Onboarding\OnboardingDraftStageResolver;
use App\Services\Onboarding\OnboardingLifecycleService; use App\Services\Onboarding\OnboardingLifecycleService;
use App\Services\Entitlements\WorkspaceCommercialLifecycleResolver;
use App\Services\Entitlements\WorkspaceEntitlementResolver;
use App\Services\OperationRunService;
use App\Services\Providers\ProviderConnectionMutationService; use App\Services\Providers\ProviderConnectionMutationService;
use App\Services\Providers\ProviderOperationRegistry; use App\Services\Providers\ProviderOperationRegistry;
use App\Services\Providers\ProviderOperationStartGate; use App\Services\Providers\ProviderOperationStartGate;
@ -50,17 +47,16 @@
use App\Support\OperationRunLinks; use App\Support\OperationRunLinks;
use App\Support\OperationRunOutcome; use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus; use App\Support\OperationRunStatus;
use App\Support\OpsUx\OperationUxPresenter;
use App\Support\OpsUx\ProviderOperationStartResultPresenter;
use App\Support\OpsUx\OpsUxBrowserEvents; use App\Support\OpsUx\OpsUxBrowserEvents;
use App\Support\OpsUx\ProviderOperationStartResultPresenter;
use App\Support\ProductKnowledge\ContextualHelpResolver; use App\Support\ProductKnowledge\ContextualHelpResolver;
use App\Support\Providers\ProviderConnectionType; use App\Support\Providers\ProviderConnectionType;
use App\Support\Providers\ProviderConsentStatus; use App\Support\Providers\ProviderConsentStatus;
use App\Support\Providers\ProviderReasonCodes; use App\Support\Providers\ProviderReasonCodes;
use App\Support\Providers\ProviderVerificationStatus;
use App\Support\Providers\TargetScope\ProviderConnectionSurfaceSummary; use App\Support\Providers\TargetScope\ProviderConnectionSurfaceSummary;
use App\Support\Providers\TargetScope\ProviderConnectionTargetScopeDescriptor; use App\Support\Providers\TargetScope\ProviderConnectionTargetScopeDescriptor;
use App\Support\Providers\TargetScope\ProviderConnectionTargetScopeNormalizer; use App\Support\Providers\TargetScope\ProviderConnectionTargetScopeNormalizer;
use App\Support\Providers\ProviderVerificationStatus;
use App\Support\Tenants\TenantInteractionLane; use App\Support\Tenants\TenantInteractionLane;
use App\Support\Tenants\TenantLifecyclePresentation; use App\Support\Tenants\TenantLifecyclePresentation;
use App\Support\Tenants\TenantOperabilityQuestion; use App\Support\Tenants\TenantOperabilityQuestion;
@ -68,6 +64,7 @@
use App\Support\Verification\VerificationCheckStatus; use App\Support\Verification\VerificationCheckStatus;
use App\Support\Verification\VerificationReportOverall; use App\Support\Verification\VerificationReportOverall;
use App\Support\Workspaces\WorkspaceContext; use App\Support\Workspaces\WorkspaceContext;
use BackedEnum;
use Filament\Actions\Action; use Filament\Actions\Action;
use Filament\Forms\Components\CheckboxList; use Filament\Forms\Components\CheckboxList;
use Filament\Forms\Components\Radio; use Filament\Forms\Components\Radio;
@ -233,7 +230,7 @@ private function canViewLinkedEnvironment(): bool
return app(TenantOperabilityService::class)->outcomeFor( return app(TenantOperabilityService::class)->outcomeFor(
tenant: $tenant, tenant: $tenant,
question: TenantOperabilityQuestion::TenantBoundViewability, question: TenantOperabilityQuestion::EnvironmentBoundViewability,
actor: $user, actor: $user,
workspaceId: (int) $this->workspace->getKey(), workspaceId: (int) $this->workspace->getKey(),
lane: TenantInteractionLane::AdministrativeManagement, lane: TenantInteractionLane::AdministrativeManagement,
@ -1108,7 +1105,7 @@ private function readinessSupportingEvidenceSchema(array $payload, string $keyPr
$actions[] = Action::make($keyPrefix.'_required_permissions_assist') $actions[] = Action::make($keyPrefix.'_required_permissions_assist')
->label('View required permissions') ->label('View required permissions')
->color('gray') ->color('gray')
->url($requiredPermissionsUrl); ->url($requiredPermissionsUrl);
} }
if ($actions === []) { if ($actions === []) {
@ -1206,7 +1203,7 @@ private function readinessPermissionDiagnosticsSchema(array $payload, string $ke
* draft: array{id: int, environment_name: string, stage_label: string, draft_status_label: string, started_by: string, updated_by: string, last_updated_human: string}, * draft: array{id: int, environment_name: string, stage_label: string, draft_status_label: string, started_by: string, updated_by: string, last_updated_human: string},
* checkpoint: array{current_checkpoint: string|null, current_checkpoint_label: string|null, last_completed_checkpoint: string|null, lifecycle_state: string, lifecycle_label: string}, * checkpoint: array{current_checkpoint: string|null, current_checkpoint_label: string|null, last_completed_checkpoint: string|null, lifecycle_state: string, lifecycle_label: string},
* provider_summary: array<string, mixed>|null, * provider_summary: array<string, mixed>|null,
* verification: array{status: string, status_label: string, run_id: int|null, run_url: string|null, is_active: bool, has_report: bool, matches_selected_connection: bool|null, overall: string|null}, * verification: array{status: string, status_label: string, run_id: int|null, run_url: string|null, is_active: bool, has_report: bool, matches_selected_connection: bool|null, overall: string|null},
* verification_assist: array{is_visible: bool, reason: string}, * verification_assist: array{is_visible: bool, reason: string},
* permissions: array{overall: string|null, counts: array<string, int>, freshness: array{last_refreshed_at: string|null, is_stale: bool}, capability_groups: array<int, array<string, mixed>>, primary_capability_group: array<string, mixed>|null, missing_permissions: array{application: list<string>, delegated: list<string>}, required_permissions_url: string|null}|null, * permissions: array{overall: string|null, counts: array<string, int>, freshness: array{last_refreshed_at: string|null, is_stale: bool}, capability_groups: array<int, array<string, mixed>>, primary_capability_group: array<string, mixed>|null, missing_permissions: array{application: list<string>, delegated: list<string>}, required_permissions_url: string|null}|null,
* freshness: array{connection_recently_updated: bool, verification_mismatch: bool, permission_last_refreshed_at: string|null, permission_data_is_stale: bool, note: string}, * freshness: array{connection_recently_updated: bool, verification_mismatch: bool, permission_last_refreshed_at: string|null, permission_data_is_stale: bool, note: string},
@ -4195,7 +4192,7 @@ public function startBootstrap(array $operationTypes): void
draft: $this->onboardingSession, draft: $this->onboardingSession,
actor: $user, actor: $user,
expectedVersion: $this->expectedDraftVersion(), expectedVersion: $this->expectedDraftVersion(),
mutator: function (ManagedEnvironmentOnboardingSession $draft) use ($tenant, $connection, $types, $registry, $user, &$result): void { mutator: function (ManagedEnvironmentOnboardingSession $draft) use ($tenant, $connection, $types, $user, &$result): void {
$nextOperationType = $this->nextBootstrapOperationType($draft, $types, (int) $connection->getKey()); $nextOperationType = $this->nextBootstrapOperationType($draft, $types, (int) $connection->getKey());
if ($nextOperationType === null) { if ($nextOperationType === null) {

View File

@ -5,7 +5,7 @@
namespace App\Filament\Resources\AlertDeliveryResource\Pages; namespace App\Filament\Resources\AlertDeliveryResource\Pages;
use App\Filament\Resources\AlertDeliveryResource; use App\Filament\Resources\AlertDeliveryResource;
use App\Support\Filament\CanonicalAdminTenantFilterState; use App\Support\Filament\CanonicalAdminEnvironmentFilterState;
use App\Support\Navigation\CanonicalNavigationContext; use App\Support\Navigation\CanonicalNavigationContext;
use App\Support\OperateHub\OperateHubShell; use App\Support\OperateHub\OperateHubShell;
use Filament\Actions\Action; use Filament\Actions\Action;
@ -17,7 +17,7 @@ class ListAlertDeliveries extends ListRecords
public function mount(): void public function mount(): void
{ {
app(CanonicalAdminTenantFilterState::class)->sync($this->getTableFiltersSessionKey(), request: request()); app(CanonicalAdminEnvironmentFilterState::class)->sync($this->getTableFiltersSessionKey(), request: request());
parent::mount(); parent::mount();
} }

View File

@ -5,7 +5,7 @@
use App\Exceptions\InvalidPolicyTypeException; use App\Exceptions\InvalidPolicyTypeException;
use App\Filament\Concerns\InteractsWithTenantOwnedRecords; use App\Filament\Concerns\InteractsWithTenantOwnedRecords;
use App\Filament\Concerns\ResolvesPanelTenantContext; use App\Filament\Concerns\ResolvesPanelTenantContext;
use App\Filament\Concerns\WorkspaceScopedTenantRoutes; use App\Filament\Concerns\WorkspaceScopedEnvironmentRoutes;
use App\Filament\Resources\BackupScheduleResource\Pages; use App\Filament\Resources\BackupScheduleResource\Pages;
use App\Filament\Resources\BackupScheduleResource\RelationManagers\BackupScheduleOperationRunsRelationManager; use App\Filament\Resources\BackupScheduleResource\RelationManagers\BackupScheduleOperationRunsRelationManager;
use App\Jobs\RunBackupScheduleJob; use App\Jobs\RunBackupScheduleJob;
@ -43,7 +43,6 @@
use Filament\Actions\BulkAction; use Filament\Actions\BulkAction;
use Filament\Actions\BulkActionGroup; use Filament\Actions\BulkActionGroup;
use Filament\Actions\CreateAction; use Filament\Actions\CreateAction;
use Filament\Facades\Filament;
use Filament\Forms\Components\CheckboxList; use Filament\Forms\Components\CheckboxList;
use Filament\Forms\Components\Select; use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput; use Filament\Forms\Components\TextInput;
@ -70,7 +69,7 @@ class BackupScheduleResource extends Resource
{ {
use InteractsWithTenantOwnedRecords; use InteractsWithTenantOwnedRecords;
use ResolvesPanelTenantContext; use ResolvesPanelTenantContext;
use WorkspaceScopedTenantRoutes; use WorkspaceScopedEnvironmentRoutes;
protected static ?string $model = BackupSchedule::class; protected static ?string $model = BackupSchedule::class;

View File

@ -3,11 +3,9 @@
namespace App\Filament\Resources\BackupScheduleResource\Pages; namespace App\Filament\Resources\BackupScheduleResource\Pages;
use App\Filament\Resources\BackupScheduleResource; use App\Filament\Resources\BackupScheduleResource;
use App\Models\ManagedEnvironment;
use App\Support\BackupHealth\TenantBackupHealthAssessment; use App\Support\BackupHealth\TenantBackupHealthAssessment;
use App\Support\BackupHealth\TenantBackupHealthResolver; use App\Support\BackupHealth\TenantBackupHealthResolver;
use App\Support\Filament\CanonicalAdminTenantFilterState; use App\Support\Filament\CanonicalAdminEnvironmentFilterState;
use Filament\Facades\Filament;
use Filament\Resources\Pages\ListRecords; use Filament\Resources\Pages\ListRecords;
use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Database\Eloquent\ModelNotFoundException;
@ -34,7 +32,7 @@ public function mountAction(string $name, array $arguments = [], array $context
public function mount(): void public function mount(): void
{ {
$this->syncCanonicalAdminTenantFilterState(); $this->syncCanonicalAdminEnvironmentFilterState();
parent::mount(); parent::mount();
} }
@ -59,13 +57,13 @@ private function tableHasRecords(): bool
return $this->getTableRecords()->count() > 0; return $this->getTableRecords()->count() > 0;
} }
private function syncCanonicalAdminTenantFilterState(): void private function syncCanonicalAdminEnvironmentFilterState(): void
{ {
app(CanonicalAdminTenantFilterState::class)->sync( app(CanonicalAdminEnvironmentFilterState::class)->sync(
$this->getTableFiltersSessionKey(), $this->getTableFiltersSessionKey(),
tenantSensitiveFilters: [], environmentSensitiveFilters: [],
request: request(), request: request(),
tenantFilterName: null, environmentFilterName: null,
); );
} }
@ -75,9 +73,9 @@ public function getSubheading(): ?string
return null; return null;
} }
$tenant = BackupScheduleResource::panelTenantContext(); $tenant = BackupScheduleResource::panelTenantContext();
if ($tenant === null) { if ($tenant === null) {
return 'One or more enabled schedules need follow-up.'; return 'One or more enabled schedules need follow-up.';
} }

View File

@ -4,7 +4,7 @@
use App\Filament\Concerns\InteractsWithTenantOwnedRecords; use App\Filament\Concerns\InteractsWithTenantOwnedRecords;
use App\Filament\Concerns\ResolvesPanelTenantContext; use App\Filament\Concerns\ResolvesPanelTenantContext;
use App\Filament\Concerns\WorkspaceScopedTenantRoutes; use App\Filament\Concerns\WorkspaceScopedEnvironmentRoutes;
use App\Filament\Resources\BackupSetResource\Pages; use App\Filament\Resources\BackupSetResource\Pages;
use App\Filament\Resources\BackupSetResource\RelationManagers\BackupItemsRelationManager; use App\Filament\Resources\BackupSetResource\RelationManagers\BackupItemsRelationManager;
use App\Jobs\BulkBackupSetDeleteJob; use App\Jobs\BulkBackupSetDeleteJob;
@ -45,7 +45,6 @@
use Filament\Actions\ActionGroup; use Filament\Actions\ActionGroup;
use Filament\Actions\BulkAction; use Filament\Actions\BulkAction;
use Filament\Actions\BulkActionGroup; use Filament\Actions\BulkActionGroup;
use Filament\Facades\Filament;
use Filament\Forms; use Filament\Forms;
use Filament\Infolists; use Filament\Infolists;
use Filament\Notifications\Notification; use Filament\Notifications\Notification;
@ -64,7 +63,7 @@ class BackupSetResource extends Resource
{ {
use InteractsWithTenantOwnedRecords; use InteractsWithTenantOwnedRecords;
use ResolvesPanelTenantContext; use ResolvesPanelTenantContext;
use WorkspaceScopedTenantRoutes; use WorkspaceScopedEnvironmentRoutes;
protected static ?string $model = BackupSet::class; protected static ?string $model = BackupSet::class;

View File

@ -4,7 +4,7 @@
use App\Filament\Resources\BackupSetResource; use App\Filament\Resources\BackupSetResource;
use App\Support\BackupHealth\TenantBackupHealthAssessment; use App\Support\BackupHealth\TenantBackupHealthAssessment;
use App\Support\Filament\CanonicalAdminTenantFilterState; use App\Support\Filament\CanonicalAdminEnvironmentFilterState;
use Filament\Resources\Pages\ListRecords; use Filament\Resources\Pages\ListRecords;
class ListBackupSets extends ListRecords class ListBackupSets extends ListRecords
@ -13,10 +13,10 @@ class ListBackupSets extends ListRecords
public function mount(): void public function mount(): void
{ {
app(CanonicalAdminTenantFilterState::class)->sync( app(CanonicalAdminEnvironmentFilterState::class)->sync(
$this->getTableFiltersSessionKey(), $this->getTableFiltersSessionKey(),
request: request(), request: request(),
tenantFilterName: null, environmentFilterName: null,
); );
parent::mount(); parent::mount();

View File

@ -4,13 +4,14 @@
namespace App\Filament\Resources; namespace App\Filament\Resources;
use App\Filament\Concerns\CleansAdminTenantResourceQueryParameter;
use App\Filament\Pages\BaselineCompareMatrix; use App\Filament\Pages\BaselineCompareMatrix;
use App\Filament\Resources\BaselineProfileResource\Pages; use App\Filament\Resources\BaselineProfileResource\Pages;
use App\Models\BaselineProfile; use App\Models\BaselineProfile;
use App\Models\BaselineSnapshot; use App\Models\BaselineSnapshot;
use App\Models\BaselineTenantAssignment; use App\Models\BaselineTenantAssignment;
use App\Models\OperationRun;
use App\Models\ManagedEnvironment; use App\Models\ManagedEnvironment;
use App\Models\OperationRun;
use App\Models\User; use App\Models\User;
use App\Models\Workspace; use App\Models\Workspace;
use App\Services\Audit\WorkspaceAuditLogger; use App\Services\Audit\WorkspaceAuditLogger;
@ -21,11 +22,11 @@
use App\Support\Badges\BadgeCatalog; use App\Support\Badges\BadgeCatalog;
use App\Support\Badges\BadgeDomain; use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer; use App\Support\Badges\BadgeRenderer;
use App\Support\Baselines\BaselineScope;
use App\Support\Baselines\BaselineCaptureMode; use App\Support\Baselines\BaselineCaptureMode;
use App\Support\Baselines\BaselineFullContentRolloutGate; use App\Support\Baselines\BaselineFullContentRolloutGate;
use App\Support\Baselines\BaselineProfileStatus; use App\Support\Baselines\BaselineProfileStatus;
use App\Support\Baselines\BaselineReasonCodes; use App\Support\Baselines\BaselineReasonCodes;
use App\Support\Baselines\BaselineScope;
use App\Support\Baselines\Compare\CompareStrategyRegistry; use App\Support\Baselines\Compare\CompareStrategyRegistry;
use App\Support\Filament\FilterOptionCatalog; use App\Support\Filament\FilterOptionCatalog;
use App\Support\Inventory\InventoryPolicyTypeMeta; use App\Support\Inventory\InventoryPolicyTypeMeta;
@ -61,16 +62,18 @@
use Filament\Schemas\Schema; use Filament\Schemas\Schema;
use Filament\Tables\Columns\TextColumn; use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table; use Filament\Tables\Table;
use InvalidArgumentException;
use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Validation\Rule; use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Unique; use Illuminate\Validation\Rules\Unique;
use InvalidArgumentException;
use UnitEnum; use UnitEnum;
class BaselineProfileResource extends Resource class BaselineProfileResource extends Resource
{ {
use CleansAdminTenantResourceQueryParameter;
protected static bool $isDiscovered = false; protected static bool $isDiscovered = false;
protected static bool $isScopedToTenant = false; protected static bool $isScopedToTenant = false;

View File

@ -4,6 +4,7 @@
namespace App\Filament\Resources; namespace App\Filament\Resources;
use App\Filament\Concerns\CleansAdminTenantResourceQueryParameter;
use App\Filament\Resources\BaselineSnapshotResource\Pages; use App\Filament\Resources\BaselineSnapshotResource\Pages;
use App\Models\BaselineProfile; use App\Models\BaselineProfile;
use App\Models\BaselineSnapshot; use App\Models\BaselineSnapshot;
@ -25,9 +26,9 @@
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile; use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceSlot; use App\Support\Ui\ActionSurface\Enums\ActionSurfaceSlot;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceType; use App\Support\Ui\ActionSurface\Enums\ActionSurfaceType;
use App\Support\Ui\GovernanceArtifactTruth\CompressedGovernanceOutcome;
use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthEnvelope; use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthEnvelope;
use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthPresenter; use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthPresenter;
use App\Support\Ui\GovernanceArtifactTruth\CompressedGovernanceOutcome;
use App\Support\Ui\GovernanceArtifactTruth\SurfaceCompressionContext; use App\Support\Ui\GovernanceArtifactTruth\SurfaceCompressionContext;
use App\Support\Workspaces\WorkspaceContext; use App\Support\Workspaces\WorkspaceContext;
use BackedEnum; use BackedEnum;
@ -44,6 +45,8 @@
class BaselineSnapshotResource extends Resource class BaselineSnapshotResource extends Resource
{ {
use CleansAdminTenantResourceQueryParameter;
protected static bool $isDiscovered = false; protected static bool $isDiscovered = false;
protected static bool $isScopedToTenant = false; protected static bool $isScopedToTenant = false;

View File

@ -5,7 +5,7 @@
use App\Filament\Concerns\InteractsWithTenantOwnedRecords; use App\Filament\Concerns\InteractsWithTenantOwnedRecords;
use App\Filament\Concerns\ResolvesPanelTenantContext; use App\Filament\Concerns\ResolvesPanelTenantContext;
use App\Filament\Concerns\ScopesGlobalSearchToTenant; use App\Filament\Concerns\ScopesGlobalSearchToTenant;
use App\Filament\Concerns\WorkspaceScopedTenantRoutes; use App\Filament\Concerns\WorkspaceScopedEnvironmentRoutes;
use App\Filament\Resources\EntraGroupResource\Pages; use App\Filament\Resources\EntraGroupResource\Pages;
use App\Models\EntraGroup; use App\Models\EntraGroup;
use App\Models\ManagedEnvironment; use App\Models\ManagedEnvironment;
@ -39,7 +39,7 @@ class EntraGroupResource extends Resource
use InteractsWithTenantOwnedRecords; use InteractsWithTenantOwnedRecords;
use ResolvesPanelTenantContext; use ResolvesPanelTenantContext;
use ScopesGlobalSearchToTenant; use ScopesGlobalSearchToTenant;
use WorkspaceScopedTenantRoutes; use WorkspaceScopedEnvironmentRoutes;
protected static bool $isScopedToTenant = false; protected static bool $isScopedToTenant = false;

View File

@ -7,7 +7,7 @@
use App\Models\User; use App\Models\User;
use App\Services\Directory\EntraGroupSyncService; use App\Services\Directory\EntraGroupSyncService;
use App\Support\Auth\Capabilities; use App\Support\Auth\Capabilities;
use App\Support\Filament\CanonicalAdminTenantFilterState; use App\Support\Filament\CanonicalAdminEnvironmentFilterState;
use App\Support\OperationRunLinks; use App\Support\OperationRunLinks;
use App\Support\OpsUx\OpsUxBrowserEvents; use App\Support\OpsUx\OpsUxBrowserEvents;
use App\Support\OpsUx\ProviderOperationStartResultPresenter; use App\Support\OpsUx\ProviderOperationStartResultPresenter;
@ -22,10 +22,10 @@ class ListEntraGroups extends ListRecords
public function mount(): void public function mount(): void
{ {
app(CanonicalAdminTenantFilterState::class)->sync( app(CanonicalAdminEnvironmentFilterState::class)->sync(
$this->getTableFiltersSessionKey(), $this->getTableFiltersSessionKey(),
request: request(), request: request(),
tenantFilterName: null, environmentFilterName: null,
); );
if ( if (

View File

@ -4,47 +4,46 @@
namespace App\Filament\Resources; namespace App\Filament\Resources;
use App\Exceptions\Entitlements\WorkspaceEntitlementBlockedException;
use App\Filament\Concerns\InteractsWithTenantOwnedRecords; use App\Filament\Concerns\InteractsWithTenantOwnedRecords;
use App\Filament\Concerns\ResolvesPanelTenantContext; use App\Filament\Concerns\ResolvesPanelTenantContext;
use App\Filament\Concerns\WorkspaceScopedTenantRoutes; use App\Filament\Concerns\WorkspaceScopedEnvironmentRoutes;
use App\Filament\Pages\Reviews\CustomerReviewWorkspace; use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
use App\Filament\Resources\EnvironmentReviewResource\Pages; use App\Filament\Resources\EnvironmentReviewResource\Pages;
use App\Exceptions\Entitlements\WorkspaceEntitlementBlockedException;
use App\Models\EvidenceSnapshot;
use App\Models\ReviewPack;
use App\Models\ManagedEnvironment;
use App\Models\EnvironmentReview; use App\Models\EnvironmentReview;
use App\Models\EnvironmentReviewSection; use App\Models\EnvironmentReviewSection;
use App\Models\EvidenceSnapshot;
use App\Models\ManagedEnvironment;
use App\Models\ReviewPack;
use App\Models\User; use App\Models\User;
use App\Services\ReviewPackService;
use App\Services\EnvironmentReviews\EnvironmentReviewService; use App\Services\EnvironmentReviews\EnvironmentReviewService;
use App\Services\ReviewPackService;
use App\Support\Auth\Capabilities; use App\Support\Auth\Capabilities;
use App\Support\Auth\UiTooltips as AuthUiTooltips; use App\Support\Auth\UiTooltips as AuthUiTooltips;
use App\Support\Badges\BadgeCatalog; use App\Support\Badges\BadgeCatalog;
use App\Support\Badges\BadgeDomain; use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer; use App\Support\Badges\BadgeRenderer;
use App\Support\EnvironmentReviewCompletenessState;
use App\Support\EnvironmentReviewStatus;
use App\Support\Findings\FindingOutcomeSemantics; use App\Support\Findings\FindingOutcomeSemantics;
use App\Support\Navigation\NavigationScope; use App\Support\Navigation\NavigationScope;
use App\Support\OperationRunLinks; use App\Support\OperationRunLinks;
use App\Support\OperationRunType; use App\Support\OperationRunType;
use App\Support\OpsUx\OperationUxPresenter; use App\Support\OpsUx\OperationUxPresenter;
use App\Support\Rbac\UiEnforcement;
use App\Support\ReasonTranslation\ReasonPresenter; use App\Support\ReasonTranslation\ReasonPresenter;
use App\Support\ReviewPackStatus; use App\Support\ReviewPackStatus;
use App\Support\Rbac\UiEnforcement;
use App\Support\EnvironmentReviewCompletenessState;
use App\Support\EnvironmentReviewStatus;
use App\Support\Ui\ActionSurface\ActionSurfaceDeclaration; use App\Support\Ui\ActionSurface\ActionSurfaceDeclaration;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceInspectAffordance; use App\Support\Ui\ActionSurface\Enums\ActionSurfaceInspectAffordance;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile; use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceSlot; use App\Support\Ui\ActionSurface\Enums\ActionSurfaceSlot;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceType; use App\Support\Ui\ActionSurface\Enums\ActionSurfaceType;
use App\Support\Ui\GovernanceArtifactTruth\CompressedGovernanceOutcome;
use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthEnvelope; use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthEnvelope;
use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthPresenter; use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthPresenter;
use App\Support\Ui\GovernanceArtifactTruth\CompressedGovernanceOutcome;
use App\Support\Ui\GovernanceArtifactTruth\SurfaceCompressionContext; use App\Support\Ui\GovernanceArtifactTruth\SurfaceCompressionContext;
use BackedEnum; use BackedEnum;
use Filament\Actions; use Filament\Actions;
use Filament\Facades\Filament;
use Filament\Forms\Components\Select; use Filament\Forms\Components\Select;
use Filament\Infolists\Components\RepeatableEntry; use Filament\Infolists\Components\RepeatableEntry;
use Filament\Infolists\Components\TextEntry; use Filament\Infolists\Components\TextEntry;
@ -66,7 +65,7 @@ class EnvironmentReviewResource extends Resource
{ {
use InteractsWithTenantOwnedRecords; use InteractsWithTenantOwnedRecords;
use ResolvesPanelTenantContext; use ResolvesPanelTenantContext;
use WorkspaceScopedTenantRoutes; use WorkspaceScopedEnvironmentRoutes;
protected static bool $isDiscovered = false; protected static bool $isDiscovered = false;
@ -317,7 +316,7 @@ public static function table(Table $table): Table
->persistFiltersInSession() ->persistFiltersInSession()
->persistSearchInSession() ->persistSearchInSession()
->persistSortInSession() ->persistSortInSession()
->recordUrl(fn (EnvironmentReview $record): string => static::tenantScopedUrl('view', ['record' => $record], $record->tenant)) ->recordUrl(fn (EnvironmentReview $record): string => static::environmentScopedUrl('view', ['record' => $record], $record->tenant))
->columns([ ->columns([
Tables\Columns\TextColumn::make('status') Tables\Columns\TextColumn::make('status')
->badge() ->badge()
@ -464,7 +463,7 @@ public static function executeCreateReview(array $data): void
->actions([ ->actions([
Actions\Action::make('view_review') Actions\Action::make('view_review')
->label(__('localization.review.view_review')) ->label(__('localization.review.view_review'))
->url(static::tenantScopedUrl('view', ['record' => $review], $tenant)), ->url(static::environmentScopedUrl('view', ['record' => $review], $tenant)),
]) ])
->send(); ->send();
@ -613,11 +612,10 @@ public static function executeExport(EnvironmentReview $review): void
/** /**
* @param array<string, mixed> $parameters * @param array<string, mixed> $parameters
*/ */
public static function tenantScopedUrl( public static function environmentScopedUrl(
string $page = 'index', string $page = 'index',
array $parameters = [], array $parameters = [],
?ManagedEnvironment $tenant = null, ?ManagedEnvironment $tenant = null,
?string $panel = null,
): string { ): string {
$panelId = 'admin'; $panelId = 'admin';
@ -881,7 +879,7 @@ private static function summaryContextLinks(EnvironmentReview $record, bool $cus
$links[] = [ $links[] = [
'title' => __('localization.review.customer_workspace'), 'title' => __('localization.review.customer_workspace'),
'label' => __('localization.review.open_customer_workspace'), 'label' => __('localization.review.open_customer_workspace'),
'url' => CustomerReviewWorkspace::tenantPrefilterUrl($record->tenant), 'url' => CustomerReviewWorkspace::environmentFilterUrl($record->tenant),
'description' => __('localization.review.customer_workspace_description'), 'description' => __('localization.review.customer_workspace_description'),
]; ];
} }

View File

@ -6,19 +6,19 @@
use App\Filament\Pages\Reviews\CustomerReviewWorkspace; use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
use App\Filament\Resources\EnvironmentReviewResource; use App\Filament\Resources\EnvironmentReviewResource;
use App\Models\ReviewPack;
use App\Models\ManagedEnvironment;
use App\Models\EnvironmentReview; use App\Models\EnvironmentReview;
use App\Models\ManagedEnvironment;
use App\Models\ReviewPack;
use App\Models\User; use App\Models\User;
use App\Services\ReviewPackService;
use App\Services\Audit\WorkspaceAuditLogger; use App\Services\Audit\WorkspaceAuditLogger;
use App\Services\EnvironmentReviews\EnvironmentReviewLifecycleService; use App\Services\EnvironmentReviews\EnvironmentReviewLifecycleService;
use App\Services\EnvironmentReviews\EnvironmentReviewService; use App\Services\EnvironmentReviews\EnvironmentReviewService;
use App\Services\ReviewPackService;
use App\Support\Audit\AuditActionId; use App\Support\Audit\AuditActionId;
use App\Support\Auth\Capabilities; use App\Support\Auth\Capabilities;
use App\Support\EnvironmentReviewStatus;
use App\Support\Rbac\UiEnforcement; use App\Support\Rbac\UiEnforcement;
use App\Support\ReviewPackStatus; use App\Support\ReviewPackStatus;
use App\Support\EnvironmentReviewStatus;
use App\Support\Ui\GovernanceActions\GovernanceActionCatalog; use App\Support\Ui\GovernanceActions\GovernanceActionCatalog;
use Filament\Actions; use Filament\Actions;
use Filament\Forms\Components\Textarea; use Filament\Forms\Components\Textarea;
@ -302,7 +302,7 @@ private function createNextReviewAction(): Actions\Action
return; return;
} }
$this->redirect(EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $nextReview], $nextReview->tenant)); $this->redirect(EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $nextReview], $nextReview->tenant));
}), }),
) )
->requireCapability(Capabilities::ENVIRONMENT_REVIEW_MANAGE) ->requireCapability(Capabilities::ENVIRONMENT_REVIEW_MANAGE)

View File

@ -6,10 +6,9 @@
use App\Filament\Concerns\InteractsWithTenantOwnedRecords; use App\Filament\Concerns\InteractsWithTenantOwnedRecords;
use App\Filament\Concerns\ResolvesPanelTenantContext; use App\Filament\Concerns\ResolvesPanelTenantContext;
use App\Filament\Concerns\WorkspaceScopedTenantRoutes; use App\Filament\Concerns\WorkspaceScopedEnvironmentRoutes;
use App\Filament\Pages\Reviews\CustomerReviewWorkspace; use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
use App\Filament\Resources\EvidenceSnapshotResource\Pages; use App\Filament\Resources\EvidenceSnapshotResource\Pages;
use App\Filament\Resources\ReviewPackResource;
use App\Models\EvidenceSnapshot; use App\Models\EvidenceSnapshot;
use App\Models\EvidenceSnapshotItem; use App\Models\EvidenceSnapshotItem;
use App\Models\ManagedEnvironment; use App\Models\ManagedEnvironment;
@ -21,8 +20,8 @@
use App\Support\Badges\BadgeRenderer; use App\Support\Badges\BadgeRenderer;
use App\Support\Evidence\EvidenceCompletenessState; use App\Support\Evidence\EvidenceCompletenessState;
use App\Support\Evidence\EvidenceSnapshotStatus; use App\Support\Evidence\EvidenceSnapshotStatus;
use App\Support\Navigation\RelatedContextEntry;
use App\Support\Navigation\NavigationScope; use App\Support\Navigation\NavigationScope;
use App\Support\Navigation\RelatedContextEntry;
use App\Support\OperationCatalog; use App\Support\OperationCatalog;
use App\Support\OperationRunLinks; use App\Support\OperationRunLinks;
use App\Support\OperationRunOutcome; use App\Support\OperationRunOutcome;
@ -35,13 +34,12 @@
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceSlot; use App\Support\Ui\ActionSurface\Enums\ActionSurfaceSlot;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceType; use App\Support\Ui\ActionSurface\Enums\ActionSurfaceType;
use App\Support\Ui\GovernanceActions\GovernanceActionCatalog; use App\Support\Ui\GovernanceActions\GovernanceActionCatalog;
use App\Support\Ui\GovernanceArtifactTruth\CompressedGovernanceOutcome;
use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthEnvelope; use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthEnvelope;
use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthPresenter; use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthPresenter;
use App\Support\Ui\GovernanceArtifactTruth\CompressedGovernanceOutcome;
use App\Support\Ui\GovernanceArtifactTruth\SurfaceCompressionContext; use App\Support\Ui\GovernanceArtifactTruth\SurfaceCompressionContext;
use BackedEnum; use BackedEnum;
use Filament\Actions; use Filament\Actions;
use Filament\Facades\Filament;
use Filament\Forms\Components\Textarea; use Filament\Forms\Components\Textarea;
use Filament\Infolists\Components\RepeatableEntry; use Filament\Infolists\Components\RepeatableEntry;
use Filament\Infolists\Components\TextEntry; use Filament\Infolists\Components\TextEntry;
@ -66,7 +64,7 @@ class EvidenceSnapshotResource extends Resource
{ {
use InteractsWithTenantOwnedRecords; use InteractsWithTenantOwnedRecords;
use ResolvesPanelTenantContext; use ResolvesPanelTenantContext;
use WorkspaceScopedTenantRoutes; use WorkspaceScopedEnvironmentRoutes;
protected static ?string $model = EvidenceSnapshot::class; protected static ?string $model = EvidenceSnapshot::class;
@ -311,7 +309,7 @@ public static function relatedContextEntries(EvidenceSnapshot $record): array
label: 'Customer workspace', label: 'Customer workspace',
value: $record->tenant->name, value: $record->tenant->name,
secondaryValue: 'Open the customer-safe review workspace prefiltered to this tenant.', secondaryValue: 'Open the customer-safe review workspace prefiltered to this tenant.',
targetUrl: CustomerReviewWorkspace::tenantPrefilterUrl($record->tenant), targetUrl: CustomerReviewWorkspace::environmentFilterUrl($record->tenant),
targetKind: 'canonical_page', targetKind: 'canonical_page',
priority: 30, priority: 30,
actionLabel: 'Open customer workspace', actionLabel: 'Open customer workspace',

View File

@ -6,9 +6,8 @@
use App\Filament\Concerns\InteractsWithTenantOwnedRecords; use App\Filament\Concerns\InteractsWithTenantOwnedRecords;
use App\Filament\Concerns\ResolvesPanelTenantContext; use App\Filament\Concerns\ResolvesPanelTenantContext;
use App\Filament\Concerns\WorkspaceScopedTenantRoutes; use App\Filament\Concerns\WorkspaceScopedEnvironmentRoutes;
use App\Filament\Resources\FindingExceptionResource\Pages; use App\Filament\Resources\FindingExceptionResource\Pages;
use App\Filament\Resources\FindingResource;
use App\Models\FindingException; use App\Models\FindingException;
use App\Models\FindingExceptionEvidenceReference; use App\Models\FindingExceptionEvidenceReference;
use App\Models\ManagedEnvironment; use App\Models\ManagedEnvironment;
@ -31,7 +30,6 @@
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceSlot; use App\Support\Ui\ActionSurface\Enums\ActionSurfaceSlot;
use BackedEnum; use BackedEnum;
use Filament\Actions\Action; use Filament\Actions\Action;
use Filament\Facades\Filament;
use Filament\Forms\Components\DateTimePicker; use Filament\Forms\Components\DateTimePicker;
use Filament\Forms\Components\Repeater; use Filament\Forms\Components\Repeater;
use Filament\Forms\Components\Select; use Filament\Forms\Components\Select;
@ -56,7 +54,7 @@ class FindingExceptionResource extends Resource
{ {
use InteractsWithTenantOwnedRecords; use InteractsWithTenantOwnedRecords;
use ResolvesPanelTenantContext; use ResolvesPanelTenantContext;
use WorkspaceScopedTenantRoutes; use WorkspaceScopedEnvironmentRoutes;
protected static ?string $model = FindingException::class; protected static ?string $model = FindingException::class;

View File

@ -4,13 +4,13 @@
use App\Filament\Concerns\InteractsWithTenantOwnedRecords; use App\Filament\Concerns\InteractsWithTenantOwnedRecords;
use App\Filament\Concerns\ResolvesPanelTenantContext; use App\Filament\Concerns\ResolvesPanelTenantContext;
use App\Filament\Concerns\WorkspaceScopedTenantRoutes; use App\Filament\Concerns\WorkspaceScopedEnvironmentRoutes;
use App\Filament\Resources\FindingResource\Pages; use App\Filament\Resources\FindingResource\Pages;
use App\Filament\Support\NormalizedDiffSurface; use App\Filament\Support\NormalizedDiffSurface;
use App\Models\Finding; use App\Models\Finding;
use App\Models\FindingException; use App\Models\FindingException;
use App\Models\PolicyVersion;
use App\Models\ManagedEnvironment; use App\Models\ManagedEnvironment;
use App\Models\PolicyVersion;
use App\Models\User; use App\Models\User;
use App\Services\Auth\ManagedEnvironmentAccessScopeResolver; use App\Services\Auth\ManagedEnvironmentAccessScopeResolver;
use App\Services\Drift\DriftFindingDiffBuilder; use App\Services\Drift\DriftFindingDiffBuilder;
@ -22,8 +22,8 @@
use App\Support\Badges\BadgeRenderer; use App\Support\Badges\BadgeRenderer;
use App\Support\Filament\FilterOptionCatalog; use App\Support\Filament\FilterOptionCatalog;
use App\Support\Filament\FilterPresets; use App\Support\Filament\FilterPresets;
use App\Support\Findings\FindingOutcomeSemantics;
use App\Support\Filament\TablePaginationProfiles; use App\Support\Filament\TablePaginationProfiles;
use App\Support\Findings\FindingOutcomeSemantics;
use App\Support\Navigation\CanonicalNavigationContext; use App\Support\Navigation\CanonicalNavigationContext;
use App\Support\Navigation\CrossResourceNavigationMatrix; use App\Support\Navigation\CrossResourceNavigationMatrix;
use App\Support\Navigation\NavigationScope; use App\Support\Navigation\NavigationScope;
@ -69,7 +69,7 @@ class FindingResource extends Resource
{ {
use InteractsWithTenantOwnedRecords; use InteractsWithTenantOwnedRecords;
use ResolvesPanelTenantContext; use ResolvesPanelTenantContext;
use WorkspaceScopedTenantRoutes; use WorkspaceScopedEnvironmentRoutes;
protected static ?string $model = Finding::class; protected static ?string $model = Finding::class;
@ -1229,7 +1229,7 @@ public static function table(Table $table): Table
} }
} }
$body = "Resolved {$resolvedCount} finding".($resolvedCount === 1 ? '' : 's')." pending verification."; $body = "Resolved {$resolvedCount} finding".($resolvedCount === 1 ? '' : 's').' pending verification.';
if ($skippedCount > 0) { if ($skippedCount > 0) {
$body .= " Skipped {$skippedCount}."; $body .= " Skipped {$skippedCount}.";
} }

View File

@ -11,7 +11,7 @@
use App\Models\User; use App\Models\User;
use App\Services\Findings\FindingWorkflowService; use App\Services\Findings\FindingWorkflowService;
use App\Support\Auth\Capabilities; use App\Support\Auth\Capabilities;
use App\Support\Filament\CanonicalAdminTenantFilterState; use App\Support\Filament\CanonicalAdminEnvironmentFilterState;
use App\Support\Rbac\UiEnforcement; use App\Support\Rbac\UiEnforcement;
use App\Support\Rbac\UiTooltips; use App\Support\Rbac\UiTooltips;
use Filament\Actions; use Filament\Actions;
@ -49,7 +49,7 @@ public function mountAction(string $name, array $arguments = [], array $context
public function mount(): void public function mount(): void
{ {
$this->syncCanonicalAdminTenantFilterState(); $this->syncCanonicalAdminEnvironmentFilterState();
parent::mount(); parent::mount();
$this->applyRequestedDashboardPrefilter(); $this->applyRequestedDashboardPrefilter();
@ -264,13 +264,13 @@ protected function buildAllMatchingQuery(): Builder
return $query; return $query;
} }
private function syncCanonicalAdminTenantFilterState(): void private function syncCanonicalAdminEnvironmentFilterState(): void
{ {
app(CanonicalAdminTenantFilterState::class)->sync( app(CanonicalAdminEnvironmentFilterState::class)->sync(
$this->getTableFiltersSessionKey(), $this->getTableFiltersSessionKey(),
tenantSensitiveFilters: ['scope_key', 'run_ids'], environmentSensitiveFilters: ['scope_key', 'run_ids'],
request: request(), request: request(),
tenantFilterName: null, environmentFilterName: null,
); );
} }

View File

@ -5,7 +5,7 @@
use App\Filament\Clusters\Inventory\InventoryCluster; use App\Filament\Clusters\Inventory\InventoryCluster;
use App\Filament\Concerns\InteractsWithTenantOwnedRecords; use App\Filament\Concerns\InteractsWithTenantOwnedRecords;
use App\Filament\Concerns\ResolvesPanelTenantContext; use App\Filament\Concerns\ResolvesPanelTenantContext;
use App\Filament\Concerns\WorkspaceScopedTenantRoutes; use App\Filament\Concerns\WorkspaceScopedEnvironmentRoutes;
use App\Filament\Resources\InventoryItemResource\Pages; use App\Filament\Resources\InventoryItemResource\Pages;
use App\Models\InventoryItem; use App\Models\InventoryItem;
use App\Models\ManagedEnvironment; use App\Models\ManagedEnvironment;
@ -28,8 +28,8 @@
use Closure; use Closure;
use Filament\Facades\Filament; use Filament\Facades\Filament;
use Filament\Infolists\Components\TextEntry; use Filament\Infolists\Components\TextEntry;
use Filament\Panel;
use Filament\Infolists\Components\ViewEntry; use Filament\Infolists\Components\ViewEntry;
use Filament\Panel;
use Filament\Resources\Resource; use Filament\Resources\Resource;
use Filament\Schemas\Components\Section; use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema; use Filament\Schemas\Schema;
@ -37,15 +37,15 @@
use Filament\Tables\Table; use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use Illuminate\Support\Str;
use UnitEnum; use UnitEnum;
class InventoryItemResource extends Resource class InventoryItemResource extends Resource
{ {
use InteractsWithTenantOwnedRecords; use InteractsWithTenantOwnedRecords;
use ResolvesPanelTenantContext; use ResolvesPanelTenantContext;
use WorkspaceScopedTenantRoutes; use WorkspaceScopedEnvironmentRoutes;
protected static ?string $model = InventoryItem::class; protected static ?string $model = InventoryItem::class;

View File

@ -12,7 +12,7 @@
use App\Services\Inventory\InventorySyncService; use App\Services\Inventory\InventorySyncService;
use App\Services\OperationRunService; use App\Services\OperationRunService;
use App\Support\Auth\Capabilities; use App\Support\Auth\Capabilities;
use App\Support\Filament\CanonicalAdminTenantFilterState; use App\Support\Filament\CanonicalAdminEnvironmentFilterState;
use App\Support\Inventory\InventoryPolicyTypeMeta; use App\Support\Inventory\InventoryPolicyTypeMeta;
use App\Support\OperationRunLinks; use App\Support\OperationRunLinks;
use App\Support\OperationRunType; use App\Support\OperationRunType;
@ -28,8 +28,8 @@
use Filament\Forms\Components\Toggle; use Filament\Forms\Components\Toggle;
use Filament\Notifications\Notification; use Filament\Notifications\Notification;
use Filament\Resources\Pages\ListRecords; use Filament\Resources\Pages\ListRecords;
use Filament\Support\Enums\Width;
use Filament\Support\Enums\Size; use Filament\Support\Enums\Size;
use Filament\Support\Enums\Width;
class ListInventoryItems extends ListRecords class ListInventoryItems extends ListRecords
{ {
@ -41,16 +41,16 @@ class ListInventoryItems extends ListRecords
public function mount(): void public function mount(): void
{ {
app(CanonicalAdminTenantFilterState::class)->sync( app(CanonicalAdminEnvironmentFilterState::class)->sync(
$this->getTableFiltersSessionKey(), $this->getTableFiltersSessionKey(),
request: request(), request: request(),
tenantFilterName: null, environmentFilterName: null,
); );
$tenant = static::resolveTenantContextForCurrentPanel(); $tenant = static::resolveTenantContextForCurrentPanel();
if ($tenant instanceof ManagedEnvironment) { if ($tenant instanceof ManagedEnvironment) {
app(WorkspaceContext::class)->rememberTenantContext($tenant, request()); app(WorkspaceContext::class)->rememberEnvironmentContext($tenant, request());
} }
parent::mount(); parent::mount();

View File

@ -20,7 +20,7 @@ public function mount(int|string $record): void
$tenant = static::resolveTenantContextForCurrentPanel(); $tenant = static::resolveTenantContextForCurrentPanel();
if ($tenant instanceof ManagedEnvironment) { if ($tenant instanceof ManagedEnvironment) {
app(WorkspaceContext::class)->rememberTenantContext($tenant, request()); app(WorkspaceContext::class)->rememberEnvironmentContext($tenant, request());
} }
parent::mount($record); parent::mount($record);

View File

@ -191,19 +191,19 @@ public static function table(Table $table): Table
->all(); ->all();
}) })
->default(function (): ?string { ->default(function (): ?string {
$activeTenant = app(OperateHubShell::class)->activeEntitledTenant(request()); $activeEnvironment = app(OperateHubShell::class)->activeEntitledTenant(request());
if (! $activeTenant instanceof ManagedEnvironment) { if (! $activeEnvironment instanceof ManagedEnvironment) {
return null; return null;
} }
$workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(); $workspaceId = app(WorkspaceContext::class)->currentWorkspaceId();
if ($workspaceId === null || (int) $activeTenant->workspace_id !== (int) $workspaceId) { if ($workspaceId === null || (int) $activeEnvironment->workspace_id !== (int) $workspaceId) {
return null; return null;
} }
return (string) $activeTenant->getKey(); return (string) $activeEnvironment->getKey();
}) })
->searchable(), ->searchable(),
Tables\Filters\SelectFilter::make('type') Tables\Filters\SelectFilter::make('type')

View File

@ -5,7 +5,7 @@
use App\Filament\Concerns\InteractsWithTenantOwnedRecords; use App\Filament\Concerns\InteractsWithTenantOwnedRecords;
use App\Filament\Concerns\ResolvesPanelTenantContext; use App\Filament\Concerns\ResolvesPanelTenantContext;
use App\Filament\Concerns\ScopesGlobalSearchToTenant; use App\Filament\Concerns\ScopesGlobalSearchToTenant;
use App\Filament\Concerns\WorkspaceScopedTenantRoutes; use App\Filament\Concerns\WorkspaceScopedEnvironmentRoutes;
use App\Filament\Resources\PolicyResource\Pages; use App\Filament\Resources\PolicyResource\Pages;
use App\Filament\Resources\PolicyResource\RelationManagers\VersionsRelationManager; use App\Filament\Resources\PolicyResource\RelationManagers\VersionsRelationManager;
use App\Filament\Support\NormalizedSettingsSurface; use App\Filament\Support\NormalizedSettingsSurface;
@ -13,8 +13,8 @@
use App\Jobs\BulkPolicyExportJob; use App\Jobs\BulkPolicyExportJob;
use App\Jobs\BulkPolicyUnignoreJob; use App\Jobs\BulkPolicyUnignoreJob;
use App\Jobs\SyncPoliciesJob; use App\Jobs\SyncPoliciesJob;
use App\Models\Policy;
use App\Models\ManagedEnvironment; use App\Models\ManagedEnvironment;
use App\Models\Policy;
use App\Models\User; use App\Models\User;
use App\Services\Auth\CapabilityResolver; use App\Services\Auth\CapabilityResolver;
use App\Services\Intune\PolicyNormalizer; use App\Services\Intune\PolicyNormalizer;
@ -41,7 +41,6 @@
use Filament\Actions\ActionGroup; use Filament\Actions\ActionGroup;
use Filament\Actions\BulkAction; use Filament\Actions\BulkAction;
use Filament\Actions\BulkActionGroup; use Filament\Actions\BulkActionGroup;
use Filament\Facades\Filament;
use Filament\Forms; use Filament\Forms;
use Filament\Infolists\Components\TextEntry; use Filament\Infolists\Components\TextEntry;
use Filament\Infolists\Components\ViewEntry; use Filament\Infolists\Components\ViewEntry;
@ -63,7 +62,7 @@ class PolicyResource extends Resource
use InteractsWithTenantOwnedRecords; use InteractsWithTenantOwnedRecords;
use ResolvesPanelTenantContext; use ResolvesPanelTenantContext;
use ScopesGlobalSearchToTenant; use ScopesGlobalSearchToTenant;
use WorkspaceScopedTenantRoutes; use WorkspaceScopedEnvironmentRoutes;
protected static ?string $model = Policy::class; protected static ?string $model = Policy::class;

View File

@ -3,7 +3,7 @@
namespace App\Filament\Resources\PolicyResource\Pages; namespace App\Filament\Resources\PolicyResource\Pages;
use App\Filament\Resources\PolicyResource; use App\Filament\Resources\PolicyResource;
use App\Support\Filament\CanonicalAdminTenantFilterState; use App\Support\Filament\CanonicalAdminEnvironmentFilterState;
use Filament\Resources\Pages\ListRecords; use Filament\Resources\Pages\ListRecords;
class ListPolicies extends ListRecords class ListPolicies extends ListRecords
@ -12,7 +12,7 @@ class ListPolicies extends ListRecords
public function mount(): void public function mount(): void
{ {
$this->syncCanonicalAdminTenantFilterState(); $this->syncCanonicalAdminEnvironmentFilterState();
parent::mount(); parent::mount();
} }
@ -31,13 +31,13 @@ protected function getTableEmptyStateActions(): array
]; ];
} }
private function syncCanonicalAdminTenantFilterState(): void private function syncCanonicalAdminEnvironmentFilterState(): void
{ {
app(CanonicalAdminTenantFilterState::class)->sync( app(CanonicalAdminEnvironmentFilterState::class)->sync(
$this->getTableFiltersSessionKey(), $this->getTableFiltersSessionKey(),
tenantSensitiveFilters: [], environmentSensitiveFilters: [],
request: request(), request: request(),
tenantFilterName: null, environmentFilterName: null,
); );
} }
} }

View File

@ -5,7 +5,7 @@
use App\Filament\Concerns\InteractsWithTenantOwnedRecords; use App\Filament\Concerns\InteractsWithTenantOwnedRecords;
use App\Filament\Concerns\ResolvesPanelTenantContext; use App\Filament\Concerns\ResolvesPanelTenantContext;
use App\Filament\Concerns\ScopesGlobalSearchToTenant; use App\Filament\Concerns\ScopesGlobalSearchToTenant;
use App\Filament\Concerns\WorkspaceScopedTenantRoutes; use App\Filament\Concerns\WorkspaceScopedEnvironmentRoutes;
use App\Filament\Resources\PolicyVersionResource\Pages; use App\Filament\Resources\PolicyVersionResource\Pages;
use App\Filament\Support\NormalizedDiffSurface; use App\Filament\Support\NormalizedDiffSurface;
use App\Filament\Support\NormalizedSettingsSurface; use App\Filament\Support\NormalizedSettingsSurface;
@ -14,8 +14,8 @@
use App\Jobs\BulkPolicyVersionRestoreJob; use App\Jobs\BulkPolicyVersionRestoreJob;
use App\Models\BackupItem; use App\Models\BackupItem;
use App\Models\BackupSet; use App\Models\BackupSet;
use App\Models\PolicyVersion;
use App\Models\ManagedEnvironment; use App\Models\ManagedEnvironment;
use App\Models\PolicyVersion;
use App\Models\User; use App\Models\User;
use App\Services\Auth\CapabilityResolver; use App\Services\Auth\CapabilityResolver;
use App\Services\Intune\AuditLogger; use App\Services\Intune\AuditLogger;
@ -49,7 +49,6 @@
use Filament\Actions; use Filament\Actions;
use Filament\Actions\BulkAction; use Filament\Actions\BulkAction;
use Filament\Actions\BulkActionGroup; use Filament\Actions\BulkActionGroup;
use Filament\Facades\Filament;
use Filament\Forms; use Filament\Forms;
use Filament\Infolists; use Filament\Infolists;
use Filament\Notifications\Notification; use Filament\Notifications\Notification;
@ -71,7 +70,7 @@ class PolicyVersionResource extends Resource
use InteractsWithTenantOwnedRecords; use InteractsWithTenantOwnedRecords;
use ResolvesPanelTenantContext; use ResolvesPanelTenantContext;
use ScopesGlobalSearchToTenant; use ScopesGlobalSearchToTenant;
use WorkspaceScopedTenantRoutes; use WorkspaceScopedEnvironmentRoutes;
protected static ?string $model = PolicyVersion::class; protected static ?string $model = PolicyVersion::class;

View File

@ -3,7 +3,7 @@
namespace App\Filament\Resources\PolicyVersionResource\Pages; namespace App\Filament\Resources\PolicyVersionResource\Pages;
use App\Filament\Resources\PolicyVersionResource; use App\Filament\Resources\PolicyVersionResource;
use App\Support\Filament\CanonicalAdminTenantFilterState; use App\Support\Filament\CanonicalAdminEnvironmentFilterState;
use Filament\Resources\Pages\ListRecords; use Filament\Resources\Pages\ListRecords;
class ListPolicyVersions extends ListRecords class ListPolicyVersions extends ListRecords
@ -12,10 +12,10 @@ class ListPolicyVersions extends ListRecords
public function mount(): void public function mount(): void
{ {
app(CanonicalAdminTenantFilterState::class)->sync( app(CanonicalAdminEnvironmentFilterState::class)->sync(
$this->getTableFiltersSessionKey(), $this->getTableFiltersSessionKey(),
request: request(), request: request(),
tenantFilterName: null, environmentFilterName: null,
); );
parent::mount(); parent::mount();

View File

@ -203,20 +203,6 @@ public static function resolveRequestedEnvironment(): ?ManagedEnvironment
public static function resolveContextTenantExternalId(): ?string public static function resolveContextTenantExternalId(): ?string
{ {
$workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request());
$contextTenantId = app(WorkspaceContext::class)->lastTenantId(request());
if ($workspaceId !== null && $contextTenantId !== null) {
$tenant = ManagedEnvironment::query()
->whereKey($contextTenantId)
->where('workspace_id', (int) $workspaceId)
->first();
if ($tenant instanceof ManagedEnvironment) {
return (string) $tenant->slug;
}
}
$tenant = static::resolveTenantContextForCurrentPanel(); $tenant = static::resolveTenantContextForCurrentPanel();
if ($tenant instanceof ManagedEnvironment) { if ($tenant instanceof ManagedEnvironment) {
@ -228,7 +214,7 @@ public static function resolveContextTenantExternalId(): ?string
public static function resolveTenantForCreate(): ?ManagedEnvironment public static function resolveTenantForCreate(): ?ManagedEnvironment
{ {
$tenantExternalId = static::resolveRequestedTenantExternalId() ?? static::resolveContextTenantExternalId(); $tenantExternalId = static::resolveRequestedTenantExternalId();
if (! is_string($tenantExternalId) || $tenantExternalId === '') { if (! is_string($tenantExternalId) || $tenantExternalId === '') {
return null; return null;
@ -1676,11 +1662,6 @@ public static function getUrl(?string $name = null, array $parameters = [], bool
$tenantExternalId = null; $tenantExternalId = null;
$isIndexUrl = $name === null || $name === 'index'; $isIndexUrl = $name === null || $name === 'index';
if (array_key_exists('tenant', $parameters)) {
$tenantExternalId = static::normalizeTenantExternalId($parameters['tenant']);
unset($parameters['tenant']);
}
if ($tenantExternalId === null && $tenant instanceof ManagedEnvironment) { if ($tenantExternalId === null && $tenant instanceof ManagedEnvironment) {
$tenantExternalId = (string) $tenant->slug; $tenantExternalId = (string) $tenant->slug;
} }

View File

@ -6,7 +6,7 @@
use App\Exceptions\Hardening\ProviderAccessHardeningRequired; use App\Exceptions\Hardening\ProviderAccessHardeningRequired;
use App\Filament\Concerns\InteractsWithTenantOwnedRecords; use App\Filament\Concerns\InteractsWithTenantOwnedRecords;
use App\Filament\Concerns\ResolvesPanelTenantContext; use App\Filament\Concerns\ResolvesPanelTenantContext;
use App\Filament\Concerns\WorkspaceScopedTenantRoutes; use App\Filament\Concerns\WorkspaceScopedEnvironmentRoutes;
use App\Filament\Resources\RestoreRunResource\Pages; use App\Filament\Resources\RestoreRunResource\Pages;
use App\Jobs\BulkRestoreRunDeleteJob; use App\Jobs\BulkRestoreRunDeleteJob;
use App\Jobs\BulkRestoreRunForceDeleteJob; use App\Jobs\BulkRestoreRunForceDeleteJob;
@ -15,9 +15,9 @@
use App\Models\BackupItem; use App\Models\BackupItem;
use App\Models\BackupSet; use App\Models\BackupSet;
use App\Models\EntraGroup; use App\Models\EntraGroup;
use App\Models\ManagedEnvironment;
use App\Models\OperationRun; use App\Models\OperationRun;
use App\Models\RestoreRun; use App\Models\RestoreRun;
use App\Models\ManagedEnvironment;
use App\Models\User; use App\Models\User;
use App\Models\Workspace; use App\Models\Workspace;
use App\Rules\SkipOrUuidRule; use App\Rules\SkipOrUuidRule;
@ -32,21 +32,21 @@
use App\Services\Operations\BulkSelectionIdentity; use App\Services\Operations\BulkSelectionIdentity;
use App\Services\Providers\ProviderOperationStartGate; use App\Services\Providers\ProviderOperationStartGate;
use App\Services\Providers\ProviderOperationStartResult; use App\Services\Providers\ProviderOperationStartResult;
use App\Support\Audit\AuditActionId;
use App\Support\Auth\Capabilities; use App\Support\Auth\Capabilities;
use App\Support\BackupQuality\BackupQualityResolver; use App\Support\BackupQuality\BackupQualityResolver;
use App\Support\Audit\AuditActionId;
use App\Support\Badges\BadgeDomain; use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer; use App\Support\Badges\BadgeRenderer;
use App\Support\Filament\FilterOptionCatalog; use App\Support\Filament\FilterOptionCatalog;
use App\Support\Filament\FilterPresets; use App\Support\Filament\FilterPresets;
use App\Support\Navigation\NavigationScope; use App\Support\Navigation\NavigationScope;
use App\Support\OperationalControls\OperationalControlBlockedException;
use App\Support\OperationalControls\OperationalControlEvaluator;
use App\Support\OperationCatalog; use App\Support\OperationCatalog;
use App\Support\OperationRunLinks; use App\Support\OperationRunLinks;
use App\Support\OpsUx\OperationUxPresenter; use App\Support\OpsUx\OperationUxPresenter;
use App\Support\OpsUx\OpsUxBrowserEvents; use App\Support\OpsUx\OpsUxBrowserEvents;
use App\Support\OpsUx\ProviderOperationStartResultPresenter; use App\Support\OpsUx\ProviderOperationStartResultPresenter;
use App\Support\OperationalControls\OperationalControlBlockedException;
use App\Support\OperationalControls\OperationalControlEvaluator;
use App\Support\Rbac\UiEnforcement; use App\Support\Rbac\UiEnforcement;
use App\Support\RestoreRunIdempotency; use App\Support\RestoreRunIdempotency;
use App\Support\RestoreRunStatus; use App\Support\RestoreRunStatus;
@ -63,7 +63,6 @@
use Filament\Actions\ActionGroup; use Filament\Actions\ActionGroup;
use Filament\Actions\BulkAction; use Filament\Actions\BulkAction;
use Filament\Actions\BulkActionGroup; use Filament\Actions\BulkActionGroup;
use Filament\Facades\Filament;
use Filament\Forms; use Filament\Forms;
use Filament\Infolists; use Filament\Infolists;
use Filament\Notifications\Notification; use Filament\Notifications\Notification;
@ -80,7 +79,6 @@
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
@ -90,7 +88,7 @@ class RestoreRunResource extends Resource
{ {
use InteractsWithTenantOwnedRecords; use InteractsWithTenantOwnedRecords;
use ResolvesPanelTenantContext; use ResolvesPanelTenantContext;
use WorkspaceScopedTenantRoutes; use WorkspaceScopedEnvironmentRoutes;
protected static ?string $model = RestoreRun::class; protected static ?string $model = RestoreRun::class;

View File

@ -3,7 +3,7 @@
namespace App\Filament\Resources\RestoreRunResource\Pages; namespace App\Filament\Resources\RestoreRunResource\Pages;
use App\Filament\Resources\RestoreRunResource; use App\Filament\Resources\RestoreRunResource;
use App\Support\Filament\CanonicalAdminTenantFilterState; use App\Support\Filament\CanonicalAdminEnvironmentFilterState;
use Filament\Resources\Pages\ListRecords; use Filament\Resources\Pages\ListRecords;
use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Database\Eloquent\ModelNotFoundException;
@ -30,10 +30,10 @@ public function mountAction(string $name, array $arguments = [], array $context
public function mount(): void public function mount(): void
{ {
app(CanonicalAdminTenantFilterState::class)->sync( app(CanonicalAdminEnvironmentFilterState::class)->sync(
$this->getTableFiltersSessionKey(), $this->getTableFiltersSessionKey(),
request: request(), request: request(),
tenantFilterName: null, environmentFilterName: null,
); );
parent::mount(); parent::mount();

View File

@ -2,20 +2,19 @@
namespace App\Filament\Resources; namespace App\Filament\Resources;
use App\Filament\Concerns\ResolvesPanelTenantContext;
use App\Filament\Concerns\WorkspaceScopedTenantRoutes;
use App\Exceptions\Entitlements\WorkspaceEntitlementBlockedException; use App\Exceptions\Entitlements\WorkspaceEntitlementBlockedException;
use App\Exceptions\ReviewPackEvidenceResolutionException; use App\Exceptions\ReviewPackEvidenceResolutionException;
use App\Filament\Concerns\ResolvesPanelTenantContext;
use App\Filament\Concerns\WorkspaceScopedEnvironmentRoutes;
use App\Filament\Pages\Reviews\CustomerReviewWorkspace; use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
use App\Filament\Resources\EvidenceSnapshotResource as TenantEvidenceSnapshotResource; use App\Filament\Resources\EvidenceSnapshotResource as TenantEvidenceSnapshotResource;
use App\Filament\Resources\ReviewPackResource\Pages; use App\Filament\Resources\ReviewPackResource\Pages;
use App\Models\ReviewPack;
use App\Models\ManagedEnvironment; use App\Models\ManagedEnvironment;
use App\Models\ReviewPack;
use App\Models\User; use App\Models\User;
use App\Services\ReviewPackService; use App\Services\ReviewPackService;
use App\Support\Auth\Capabilities; use App\Support\Auth\Capabilities;
use App\Support\Auth\UiTooltips as AuthUiTooltips; use App\Support\Auth\UiTooltips as AuthUiTooltips;
use App\Support\Badges\BadgeCatalog;
use App\Support\Badges\BadgeDomain; use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer; use App\Support\Badges\BadgeRenderer;
use App\Support\Navigation\NavigationScope; use App\Support\Navigation\NavigationScope;
@ -28,13 +27,12 @@
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile; use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceSlot; use App\Support\Ui\ActionSurface\Enums\ActionSurfaceSlot;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceType; use App\Support\Ui\ActionSurface\Enums\ActionSurfaceType;
use App\Support\Ui\GovernanceArtifactTruth\CompressedGovernanceOutcome;
use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthEnvelope; use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthEnvelope;
use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthPresenter; use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthPresenter;
use App\Support\Ui\GovernanceArtifactTruth\CompressedGovernanceOutcome;
use App\Support\Ui\GovernanceArtifactTruth\SurfaceCompressionContext; use App\Support\Ui\GovernanceArtifactTruth\SurfaceCompressionContext;
use BackedEnum; use BackedEnum;
use Filament\Actions; use Filament\Actions;
use Filament\Facades\Filament;
use Filament\Forms\Components\Toggle; use Filament\Forms\Components\Toggle;
use Filament\Infolists\Components\TextEntry; use Filament\Infolists\Components\TextEntry;
use Filament\Infolists\Components\ViewEntry; use Filament\Infolists\Components\ViewEntry;
@ -52,7 +50,7 @@
class ReviewPackResource extends Resource class ReviewPackResource extends Resource
{ {
use ResolvesPanelTenantContext; use ResolvesPanelTenantContext;
use WorkspaceScopedTenantRoutes; use WorkspaceScopedEnvironmentRoutes;
protected static ?string $model = ReviewPack::class; protected static ?string $model = ReviewPack::class;
@ -204,14 +202,14 @@ public static function infolist(Schema $schema): Schema
->label('ManagedEnvironment review') ->label('ManagedEnvironment review')
->formatStateUsing(fn (?int $state): string => $state ? '#'.$state : '—') ->formatStateUsing(fn (?int $state): string => $state ? '#'.$state : '—')
->url(fn (ReviewPack $record): ?string => $record->environmentReview && $record->tenant ->url(fn (ReviewPack $record): ?string => $record->environmentReview && $record->tenant
? EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $record->environmentReview], $record->tenant) ? EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $record->environmentReview], $record->tenant)
: null) : null)
->placeholder('—'), ->placeholder('—'),
TextEntry::make('customer_workspace') TextEntry::make('customer_workspace')
->label('Customer workspace') ->label('Customer workspace')
->state(fn (): string => 'Open workspace') ->state(fn (): string => 'Open workspace')
->url(fn (ReviewPack $record): ?string => $record->tenant instanceof ManagedEnvironment ->url(fn (ReviewPack $record): ?string => $record->tenant instanceof ManagedEnvironment
? CustomerReviewWorkspace::tenantPrefilterUrl($record->tenant) ? CustomerReviewWorkspace::environmentFilterUrl($record->tenant)
: null) : null)
->placeholder('—'), ->placeholder('—'),
TextEntry::make('summary.review_status') TextEntry::make('summary.review_status')

View File

@ -6,10 +6,10 @@
use App\Filament\Concerns\InteractsWithTenantOwnedRecords; use App\Filament\Concerns\InteractsWithTenantOwnedRecords;
use App\Filament\Concerns\ResolvesPanelTenantContext; use App\Filament\Concerns\ResolvesPanelTenantContext;
use App\Filament\Concerns\WorkspaceScopedTenantRoutes; use App\Filament\Concerns\WorkspaceScopedEnvironmentRoutes;
use App\Filament\Resources\StoredReportResource\Pages; use App\Filament\Resources\StoredReportResource\Pages;
use App\Models\StoredReport;
use App\Models\ManagedEnvironment; use App\Models\ManagedEnvironment;
use App\Models\StoredReport;
use App\Models\User; use App\Models\User;
use App\Support\Auth\Capabilities; use App\Support\Auth\Capabilities;
use App\Support\Badges\BadgeDomain; use App\Support\Badges\BadgeDomain;
@ -45,7 +45,7 @@ class StoredReportResource extends Resource
{ {
use InteractsWithTenantOwnedRecords; use InteractsWithTenantOwnedRecords;
use ResolvesPanelTenantContext; use ResolvesPanelTenantContext;
use WorkspaceScopedTenantRoutes; use WorkspaceScopedEnvironmentRoutes;
/** /**
* @var array<string, string> * @var array<string, string>

View File

@ -6,9 +6,9 @@
use App\Exceptions\Entitlements\WorkspaceEntitlementBlockedException; use App\Exceptions\Entitlements\WorkspaceEntitlementBlockedException;
use App\Filament\Pages\Reviews\CustomerReviewWorkspace; use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
use App\Models\ManagedEnvironment;
use App\Models\OperationRun; use App\Models\OperationRun;
use App\Models\ReviewPack; use App\Models\ReviewPack;
use App\Models\ManagedEnvironment;
use App\Models\User; use App\Models\User;
use App\Services\ReviewPackService; use App\Services\ReviewPackService;
use App\Support\Auth\Capabilities; use App\Support\Auth\Capabilities;
@ -193,7 +193,7 @@ protected function getViewData(): array
'generationBlocked' => $generationBlocked, 'generationBlocked' => $generationBlocked,
'generationBlockReason' => $generationBlockReason, 'generationBlockReason' => $generationBlockReason,
'generationWarningReason' => $generationWarningReason, 'generationWarningReason' => $generationWarningReason,
'customerWorkspaceUrl' => $canView ? CustomerReviewWorkspace::tenantPrefilterUrl($tenant) : null, 'customerWorkspaceUrl' => $canView ? CustomerReviewWorkspace::environmentFilterUrl($tenant) : null,
'downloadUrl' => null, 'downloadUrl' => null,
'failedReason' => null, 'failedReason' => null,
'reviewUrl' => null, 'reviewUrl' => null,
@ -211,7 +211,7 @@ protected function getViewData(): array
$reviewUrl = null; $reviewUrl = null;
if ($latestPack->environmentReview && $canView) { if ($latestPack->environmentReview && $canView) {
$reviewUrl = \App\Filament\Resources\EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $latestPack->environmentReview], $tenant); $reviewUrl = \App\Filament\Resources\EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $latestPack->environmentReview], $tenant);
} }
$failedReason = null; $failedReason = null;
@ -245,7 +245,7 @@ protected function getViewData(): array
'generationBlocked' => $generationBlocked, 'generationBlocked' => $generationBlocked,
'generationBlockReason' => $generationBlockReason, 'generationBlockReason' => $generationBlockReason,
'generationWarningReason' => $generationWarningReason, 'generationWarningReason' => $generationWarningReason,
'customerWorkspaceUrl' => $canView ? CustomerReviewWorkspace::tenantPrefilterUrl($tenant) : null, 'customerWorkspaceUrl' => $canView ? CustomerReviewWorkspace::environmentFilterUrl($tenant) : null,
'downloadUrl' => $downloadUrl, 'downloadUrl' => $downloadUrl,
'failedReason' => $failedReason, 'failedReason' => $failedReason,
'failedReasonDetail' => $failedReasonDetail, 'failedReasonDetail' => $failedReasonDetail,

View File

@ -4,8 +4,8 @@
namespace App\Filament\Widgets\Operations; namespace App\Filament\Widgets\Operations;
use App\Models\OperationRun;
use App\Models\ManagedEnvironment; use App\Models\ManagedEnvironment;
use App\Models\OperationRun;
use App\Models\User; use App\Models\User;
use App\Services\Auth\ManagedEnvironmentAccessScopeResolver; use App\Services\Auth\ManagedEnvironmentAccessScopeResolver;
use App\Support\OperateHub\OperateHubShell; use App\Support\OperateHub\OperateHubShell;
@ -27,7 +27,7 @@ class OperationsKpiHeader extends StatsOverviewWidget
protected function getPollingInterval(): ?string protected function getPollingInterval(): ?string
{ {
$tenant = $this->activeTenant(); $tenant = $this->activeEnvironment();
if ($tenant instanceof ManagedEnvironment) { if ($tenant instanceof ManagedEnvironment) {
return ActiveRuns::existForTenant($tenant) ? '10s' : null; return ActiveRuns::existForTenant($tenant) ? '10s' : null;
@ -119,7 +119,7 @@ protected function getStats(): array
]; ];
} }
private function activeTenant(): ?ManagedEnvironment private function activeEnvironment(): ?ManagedEnvironment
{ {
$tenant = app(OperateHubShell::class)->activeEntitledTenant(request()); $tenant = app(OperateHubShell::class)->activeEntitledTenant(request());
@ -128,7 +128,7 @@ private function activeTenant(): ?ManagedEnvironment
private function scopedOperationRunQuery(): ?Builder private function scopedOperationRunQuery(): ?Builder
{ {
$tenant = $this->activeTenant(); $tenant = $this->activeEnvironment();
if ($tenant instanceof ManagedEnvironment) { if ($tenant instanceof ManagedEnvironment) {
return OperationRun::query() return OperationRun::query()

View File

@ -4,8 +4,8 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Support\Navigation\AdminSurfaceScope;
use App\Support\OperationRunLinks; use App\Support\OperationRunLinks;
use App\Support\Tenants\TenantPageCategory;
use App\Support\Workspaces\WorkspaceContext; use App\Support\Workspaces\WorkspaceContext;
use Filament\Facades\Filament; use Filament\Facades\Filament;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
@ -19,7 +19,7 @@ public function __invoke(Request $request): RedirectResponse
$workspaceContext = app(WorkspaceContext::class); $workspaceContext = app(WorkspaceContext::class);
$workspaceContext->clearRememberedTenantContext($request); $workspaceContext->clearRememberedEnvironmentContext($request);
$previousUrl = url()->previous(); $previousUrl = url()->previous();
@ -30,11 +30,11 @@ public function __invoke(Request $request): RedirectResponse
return redirect()->to(OperationRunLinks::index()); return redirect()->to(OperationRunLinks::index());
} }
if ($this->isTenantScopedEvidencePath($previousPath)) { if ($this->isEnvironmentScopedEvidencePath($previousPath)) {
return redirect()->route('admin.evidence.overview'); return redirect()->route('admin.evidence.overview');
} }
if (TenantPageCategory::fromPath($previousPath) === TenantPageCategory::TenantBound) { if (AdminSurfaceScope::fromPath($previousPath) === AdminSurfaceScope::EnvironmentBound) {
$workspace = $workspaceContext->currentWorkspace($request); $workspace = $workspaceContext->currentWorkspace($request);
if ($workspace !== null) { if ($workspace !== null) {
@ -51,7 +51,7 @@ public function __invoke(Request $request): RedirectResponse
return redirect()->to((string) $previousUrl); return redirect()->to((string) $previousUrl);
} }
private function isTenantScopedEvidencePath(string $previousPath): bool private function isEnvironmentScopedEvidencePath(string $previousPath): bool
{ {
if ($previousPath === '/admin/evidence') { if ($previousPath === '/admin/evidence') {
return true; return true;

View File

@ -49,7 +49,7 @@ public function __invoke(Request $request, ManagedEnvironment $environment): Red
$workspaceContext->setCurrentWorkspace($workspace, $user, $request); $workspaceContext->setCurrentWorkspace($workspace, $user, $request);
if (! $workspaceContext->rememberTenantContext($environment, $request)) { if (! $workspaceContext->rememberEnvironmentContext($environment, $request)) {
abort(404); abort(404);
} }

View File

@ -8,9 +8,9 @@
use App\Models\User; use App\Models\User;
use App\Models\UserTenantPreference; use App\Models\UserTenantPreference;
use App\Services\Tenants\TenantOperabilityService; use App\Services\Tenants\TenantOperabilityService;
use App\Support\ManagedEnvironmentLinks;
use App\Support\Tenants\TenantInteractionLane; use App\Support\Tenants\TenantInteractionLane;
use App\Support\Tenants\TenantOperabilityQuestion; use App\Support\Tenants\TenantOperabilityQuestion;
use App\Support\ManagedEnvironmentLinks;
use App\Support\Workspaces\WorkspaceContext; use App\Support\Workspaces\WorkspaceContext;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@ -63,7 +63,7 @@ public function __invoke(Request $request): RedirectResponse
$this->persistLastTenant($user, $tenant); $this->persistLastTenant($user, $tenant);
if (! app(WorkspaceContext::class)->rememberTenantContext($tenant, $request)) { if (! app(WorkspaceContext::class)->rememberEnvironmentContext($tenant, $request)) {
abort(404); abort(404);
} }

View File

@ -48,7 +48,7 @@ public function __invoke(Request $request): RedirectResponse
$prevWorkspaceId = $context->currentWorkspaceId($request); $prevWorkspaceId = $context->currentWorkspaceId($request);
$context->setCurrentWorkspace($workspace, $user, $request); $context->setCurrentWorkspace($workspace, $user, $request);
$context->rememberedTenant($request); $context->rememberedEnvironment($request);
Filament::setTenant(null, true); Filament::setTenant(null, true);
/** @var WorkspaceAuditLogger $auditLogger */ /** @var WorkspaceAuditLogger $auditLogger */

View File

@ -250,7 +250,7 @@ private function resolveCurrentTenant(): ManagedEnvironment
$tenant = Filament::getTenant(); $tenant = Filament::getTenant();
if (! $tenant instanceof ManagedEnvironment) { if (! $tenant instanceof ManagedEnvironment) {
$tenant = app(WorkspaceContext::class)->rememberedTenant(request()); $tenant = app(WorkspaceContext::class)->rememberedEnvironment(request());
} }
if (! $tenant instanceof ManagedEnvironment) { if (! $tenant instanceof ManagedEnvironment) {

View File

@ -11,8 +11,8 @@
use Filament\Models\Contracts\HasDefaultTenant; use Filament\Models\Contracts\HasDefaultTenant;
use Filament\Models\Contracts\HasTenants; use Filament\Models\Contracts\HasTenants;
use Filament\Panel; use Filament\Panel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
@ -194,10 +194,10 @@ public function getDefaultTenant(Panel $panel): ?Model
$operability = app(TenantOperabilityService::class); $operability = app(TenantOperabilityService::class);
$rememberedTenant = $workspaceContext->rememberedTenant(request()); $rememberedEnvironment = $workspaceContext->rememberedEnvironment(request());
if ($rememberedTenant instanceof ManagedEnvironment && $this->canAccessTenant($rememberedTenant)) { if ($rememberedEnvironment instanceof ManagedEnvironment && $this->canAccessTenant($rememberedEnvironment)) {
return $rememberedTenant; return $rememberedEnvironment;
} }
$tenantId = null; $tenantId = null;

View File

@ -120,7 +120,7 @@ private function resolvedTenant(): ?ManagedEnvironment
return null; return null;
} }
$tenantId = app(WorkspaceContext::class)->lastTenantId(request()); $tenantId = app(WorkspaceContext::class)->lastEnvironmentId(request());
if (! is_int($tenantId)) { if (! is_int($tenantId)) {
return null; return null;

View File

@ -109,7 +109,7 @@ private function authorizeForDraft(
$viewability = app(TenantOperabilityService::class)->outcomeFor( $viewability = app(TenantOperabilityService::class)->outcomeFor(
tenant: $tenant, tenant: $tenant,
question: TenantOperabilityQuestion::TenantBoundViewability, question: TenantOperabilityQuestion::EnvironmentBoundViewability,
actor: $user, actor: $user,
workspaceId: (int) $workspace->getKey(), workspaceId: (int) $workspace->getKey(),
lane: TenantInteractionLane::AdministrativeManagement, lane: TenantInteractionLane::AdministrativeManagement,

View File

@ -240,11 +240,11 @@ private function resolveCreateTenant(Workspace $workspace): ?ManagedEnvironment
$requestedEnvironmentId = request()->query('environment_id'); $requestedEnvironmentId = request()->query('environment_id');
if (! is_numeric($requestedEnvironmentId)) { if (! is_numeric($requestedEnvironmentId)) {
$lastTenantId = app(WorkspaceContext::class)->lastTenantId(request()); $lastEnvironmentId = app(WorkspaceContext::class)->lastEnvironmentId(request());
if (is_int($lastTenantId)) { if (is_int($lastEnvironmentId)) {
return ManagedEnvironment::query() return ManagedEnvironment::query()
->whereKey($lastTenantId) ->whereKey($lastEnvironmentId)
->where('workspace_id', (int) $workspace->getKey()) ->where('workspace_id', (int) $workspace->getKey())
->first(); ->first();
} }

View File

@ -2,35 +2,35 @@
namespace App\Providers\Filament; namespace App\Providers\Filament;
use App\Filament\Clusters\Inventory\InventoryCluster;
use App\Filament\Pages\Auth\Login; use App\Filament\Pages\Auth\Login;
use App\Filament\Pages\BaselineCompareLanding; use App\Filament\Pages\BaselineCompareLanding;
use App\Filament\Pages\ChooseEnvironment; use App\Filament\Pages\ChooseEnvironment;
use App\Filament\Pages\ChooseWorkspace; use App\Filament\Pages\ChooseWorkspace;
use App\Filament\Pages\CrossEnvironmentComparePage; use App\Filament\Pages\CrossEnvironmentComparePage;
use App\Filament\Pages\EnvironmentRequiredPermissions;
use App\Filament\Pages\Findings\FindingsHygieneReport; use App\Filament\Pages\Findings\FindingsHygieneReport;
use App\Filament\Pages\Findings\FindingsIntakeQueue; use App\Filament\Pages\Findings\FindingsIntakeQueue;
use App\Filament\Pages\Findings\MyFindingsInbox;
use App\Filament\Pages\Governance\DecisionRegister; use App\Filament\Pages\Governance\DecisionRegister;
use App\Filament\Pages\Governance\GovernanceInbox; use App\Filament\Pages\Governance\GovernanceInbox;
use App\Filament\Pages\Findings\MyFindingsInbox;
use App\Filament\Pages\InventoryCoverage; use App\Filament\Pages\InventoryCoverage;
use App\Filament\Pages\Monitoring\FindingExceptionsQueue; use App\Filament\Pages\Monitoring\FindingExceptionsQueue;
use App\Filament\Pages\NoAccess; use App\Filament\Pages\NoAccess;
use App\Filament\Pages\Reviews\ReviewRegister;
use App\Filament\Pages\Reviews\CustomerReviewWorkspace; use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
use App\Filament\Pages\Reviews\ReviewRegister;
use App\Filament\Pages\Settings\WorkspaceSettings; use App\Filament\Pages\Settings\WorkspaceSettings;
use App\Filament\Pages\EnvironmentRequiredPermissions;
use App\Filament\Pages\WorkspaceOverview; use App\Filament\Pages\WorkspaceOverview;
use App\Filament\Clusters\Inventory\InventoryCluster;
use App\Filament\Resources\AlertDeliveryResource; use App\Filament\Resources\AlertDeliveryResource;
use App\Filament\Resources\AlertDestinationResource; use App\Filament\Resources\AlertDestinationResource;
use App\Filament\Resources\AlertRuleResource; use App\Filament\Resources\AlertRuleResource;
use App\Filament\Resources\BaselineProfileResource; use App\Filament\Resources\BaselineProfileResource;
use App\Filament\Resources\BaselineSnapshotResource; use App\Filament\Resources\BaselineSnapshotResource;
use App\Filament\Resources\EntraGroupResource; use App\Filament\Resources\EntraGroupResource;
use App\Filament\Resources\EnvironmentReviewResource;
use App\Filament\Resources\InventoryItemResource; use App\Filament\Resources\InventoryItemResource;
use App\Filament\Resources\PolicyResource; use App\Filament\Resources\PolicyResource;
use App\Filament\Resources\ProviderConnectionResource; use App\Filament\Resources\ProviderConnectionResource;
use App\Filament\Resources\EnvironmentReviewResource;
use App\Filament\Resources\Workspaces\WorkspaceResource; use App\Filament\Resources\Workspaces\WorkspaceResource;
use App\Models\User; use App\Models\User;
use App\Models\Workspace; use App\Models\Workspace;
@ -39,14 +39,14 @@
use App\Services\Auth\WorkspaceRoleCapabilityMap; use App\Services\Auth\WorkspaceRoleCapabilityMap;
use App\Support\Auth\Capabilities; use App\Support\Auth\Capabilities;
use App\Support\Filament\PanelThemeAsset; use App\Support\Filament\PanelThemeAsset;
use App\Support\Navigation\AdminSurfaceScope;
use App\Support\Navigation\NavigationScope; use App\Support\Navigation\NavigationScope;
use App\Support\Navigation\WorkspaceHubRegistry; use App\Support\Navigation\WorkspaceHubRegistry;
use App\Support\OperationRunLinks; use App\Support\OperationRunLinks;
use App\Support\Tenants\TenantPageCategory;
use App\Support\Workspaces\WorkspaceContext; use App\Support\Workspaces\WorkspaceContext;
use Filament\FontProviders\LocalFontProvider;
use Filament\Http\Middleware\Authenticate; use Filament\Http\Middleware\Authenticate;
use Filament\Http\Middleware\AuthenticateSession; use Filament\Http\Middleware\AuthenticateSession;
use Filament\FontProviders\LocalFontProvider;
use Filament\Http\Middleware\DisableBladeIconComponents; use Filament\Http\Middleware\DisableBladeIconComponents;
use Filament\Http\Middleware\DispatchServingFilamentEvent; use Filament\Http\Middleware\DispatchServingFilamentEvent;
use Filament\Navigation\NavigationItem; use Filament\Navigation\NavigationItem;
@ -189,7 +189,7 @@ public function panel(Panel $panel): Panel
) )
->renderHook( ->renderHook(
PanelsRenderHook::PAGE_START, PanelsRenderHook::PAGE_START,
fn (): string => TenantPageCategory::fromRequest(request()) === TenantPageCategory::OnboardingWorkflow fn (): string => AdminSurfaceScope::fromRequest(request()) === AdminSurfaceScope::OnboardingWorkflow
|| request()->routeIs('admin.workspace.managed-environments.index', 'filament.admin.pages.choose-environment') || request()->routeIs('admin.workspace.managed-environments.index', 'filament.admin.pages.choose-environment')
? '' ? ''
: ((bool) config('tenantpilot.bulk_operations.progress_widget_enabled', true) : ((bool) config('tenantpilot.bulk_operations.progress_widget_enabled', true)
@ -242,7 +242,7 @@ public function panel(Panel $panel): Panel
SubstituteBindings::class, SubstituteBindings::class,
'ensure-correct-guard:web', 'ensure-correct-guard:web',
'ensure-workspace-selected', 'ensure-workspace-selected',
'ensure-filament-tenant-selected', 'ensure-environment-context-selected',
DisableBladeIconComponents::class, DisableBladeIconComponents::class,
DispatchServingFilamentEvent::class, DispatchServingFilamentEvent::class,
]) ])

View File

@ -106,7 +106,7 @@ public function evaluate(TenantOperabilityContext $context, TenantOperabilityQue
lifecycle: $lifecycle, lifecycle: $lifecycle,
lane: $context->lane, lane: $context->lane,
reasonCode: TenantOperabilityReasonCode::MissingCapability, reasonCode: TenantOperabilityReasonCode::MissingCapability,
discoverable: $question === TenantOperabilityQuestion::AdministrativeDiscoverability || $question === TenantOperabilityQuestion::TenantBoundViewability, discoverable: $question === TenantOperabilityQuestion::AdministrativeDiscoverability || $question === TenantOperabilityQuestion::EnvironmentBoundViewability,
requiredCapability: $context->requiredCapability, requiredCapability: $context->requiredCapability,
metadata: $this->metadata($context), metadata: $this->metadata($context),
); );
@ -169,7 +169,7 @@ public function evaluate(TenantOperabilityContext $context, TenantOperabilityQue
return match ($question) { return match ($question) {
TenantOperabilityQuestion::SelectorEligibility => $this->selectorEligibilityOutcome($context, $lifecycle), TenantOperabilityQuestion::SelectorEligibility => $this->selectorEligibilityOutcome($context, $lifecycle),
TenantOperabilityQuestion::RememberedContextValidity => $this->rememberedContextOutcome($context, $lifecycle), TenantOperabilityQuestion::RememberedContextValidity => $this->rememberedContextOutcome($context, $lifecycle),
TenantOperabilityQuestion::TenantBoundViewability => $this->tenantBoundViewabilityOutcome($context, $lifecycle), TenantOperabilityQuestion::EnvironmentBoundViewability => $this->tenantBoundViewabilityOutcome($context, $lifecycle),
TenantOperabilityQuestion::CanonicalLinkedRecordViewability => $this->canonicalViewabilityOutcome($context, $lifecycle), TenantOperabilityQuestion::CanonicalLinkedRecordViewability => $this->canonicalViewabilityOutcome($context, $lifecycle),
TenantOperabilityQuestion::ArchiveEligibility => $this->archiveEligibilityOutcome($context, $lifecycle), TenantOperabilityQuestion::ArchiveEligibility => $this->archiveEligibilityOutcome($context, $lifecycle),
TenantOperabilityQuestion::RestoreEligibility => $this->restoreEligibilityOutcome($context, $lifecycle), TenantOperabilityQuestion::RestoreEligibility => $this->restoreEligibilityOutcome($context, $lifecycle),
@ -375,11 +375,11 @@ private function rememberedContextOutcome(TenantOperabilityContext $context, Ten
private function tenantBoundViewabilityOutcome(TenantOperabilityContext $context, TenantLifecycle $lifecycle): TenantOperabilityOutcome private function tenantBoundViewabilityOutcome(TenantOperabilityContext $context, TenantLifecycle $lifecycle): TenantOperabilityOutcome
{ {
if ($context->lane !== TenantInteractionLane::AdministrativeManagement) { if ($context->lane !== TenantInteractionLane::AdministrativeManagement) {
return $this->wrongLaneOutcome($context, TenantOperabilityQuestion::TenantBoundViewability, $lifecycle); return $this->wrongLaneOutcome($context, TenantOperabilityQuestion::EnvironmentBoundViewability, $lifecycle);
} }
return TenantOperabilityOutcome::allow( return TenantOperabilityOutcome::allow(
question: TenantOperabilityQuestion::TenantBoundViewability, question: TenantOperabilityQuestion::EnvironmentBoundViewability,
lifecycle: $lifecycle, lifecycle: $lifecycle,
lane: $context->lane, lane: $context->lane,
discoverable: true, discoverable: true,

View File

@ -181,7 +181,7 @@ public function removeTenantFromWorkspace(ManagedEnvironment $tenant, User $acto
'is_current' => false, 'is_current' => false,
])->save(); ])->save();
app(WorkspaceContext::class)->clearRememberedTenantContext(); app(WorkspaceContext::class)->clearRememberedEnvironmentContext();
$this->auditLogger->logTenantLifecycleAction( $this->auditLogger->logTenantLifecycleAction(
tenant: $tenant, tenant: $tenant,

View File

@ -6,18 +6,18 @@
use App\Models\Finding; use App\Models\Finding;
use App\Models\FindingException; use App\Models\FindingException;
use App\Models\ManagedEnvironment;
use App\Models\ManagedEnvironmentOnboardingSession;
use App\Models\OperationRun; use App\Models\OperationRun;
use App\Models\PlatformUser; use App\Models\PlatformUser;
use App\Models\ProductUsageEvent; use App\Models\ProductUsageEvent;
use App\Models\ProviderConnection; use App\Models\ProviderConnection;
use App\Models\ReviewPack; use App\Models\ReviewPack;
use App\Models\ManagedEnvironment;
use App\Models\ManagedEnvironmentOnboardingSession;
use App\Models\Workspace; use App\Models\Workspace;
use App\Support\Auth\PlatformCapabilities;
use App\Support\Onboarding\OnboardingLifecycleState; use App\Support\Onboarding\OnboardingLifecycleState;
use App\Support\OperationRunOutcome; use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus; use App\Support\OperationRunStatus;
use App\Support\Auth\PlatformCapabilities;
use App\Support\ProductTelemetry\ProductUsageEventCatalog; use App\Support\ProductTelemetry\ProductUsageEventCatalog;
use App\Support\Providers\ProviderConsentStatus; use App\Support\Providers\ProviderConsentStatus;
use App\Support\Providers\ProviderVerificationStatus; use App\Support\Providers\ProviderVerificationStatus;
@ -68,7 +68,7 @@ public function summaries(SystemConsoleWindow|string|null $window = null, ?Carbo
->map(static fn (mixed $workspaceId): int => (int) $workspaceId) ->map(static fn (mixed $workspaceId): int => (int) $workspaceId)
->all(); ->all();
$activeTenants = ManagedEnvironment::query() $activeEnvironments = ManagedEnvironment::query()
->whereIn('workspace_id', $workspaceIds) ->whereIn('workspace_id', $workspaceIds)
->whereNull('deleted_at') ->whereNull('deleted_at')
->where('lifecycle_status', '!=', ManagedEnvironment::STATUS_ARCHIVED) ->where('lifecycle_status', '!=', ManagedEnvironment::STATUS_ARCHIVED)
@ -76,7 +76,7 @@ public function summaries(SystemConsoleWindow|string|null $window = null, ?Carbo
->orderBy('id') ->orderBy('id')
->get(['id', 'workspace_id', 'slug', 'name', 'lifecycle_status']); ->get(['id', 'workspace_id', 'slug', 'name', 'lifecycle_status']);
$tenantsByWorkspace = $activeTenants->groupBy(static fn (ManagedEnvironment $tenant): int => (int) $tenant->workspace_id); $tenantsByWorkspace = $activeEnvironments->groupBy(static fn (ManagedEnvironment $tenant): int => (int) $tenant->workspace_id);
$latestOnboardingSessions = ManagedEnvironmentOnboardingSession::query() $latestOnboardingSessions = ManagedEnvironmentOnboardingSession::query()
->whereIn('workspace_id', $workspaceIds) ->whereIn('workspace_id', $workspaceIds)

View File

@ -8,36 +8,35 @@
use App\Filament\Pages\Reviews\CustomerReviewWorkspace; use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
use App\Filament\Resources\BackupScheduleResource; use App\Filament\Resources\BackupScheduleResource;
use App\Filament\Resources\BackupSetResource; use App\Filament\Resources\BackupSetResource;
use App\Filament\Resources\EnvironmentReviewResource;
use App\Filament\Resources\EvidenceSnapshotResource; use App\Filament\Resources\EvidenceSnapshotResource;
use App\Filament\Resources\FindingExceptionResource; use App\Filament\Resources\FindingExceptionResource;
use App\Filament\Resources\FindingResource; use App\Filament\Resources\FindingResource;
use App\Filament\Resources\ReviewPackResource; use App\Filament\Resources\ReviewPackResource;
use App\Filament\Resources\EnvironmentReviewResource; use App\Models\EnvironmentReview;
use App\Models\EvidenceSnapshot; use App\Models\EvidenceSnapshot;
use App\Models\Finding; use App\Models\Finding;
use App\Models\FindingException; use App\Models\FindingException;
use App\Models\ManagedEnvironment;
use App\Models\OperationRun; use App\Models\OperationRun;
use App\Models\ProviderConnection; use App\Models\ProviderConnection;
use App\Models\ReviewPack; use App\Models\ReviewPack;
use App\Models\ManagedEnvironment;
use App\Models\EnvironmentReview;
use App\Models\User; use App\Models\User;
use App\Services\Intune\ManagedEnvironmentRequiredPermissionsViewModelBuilder; use App\Services\Intune\ManagedEnvironmentRequiredPermissionsViewModelBuilder;
use App\Support\Auth\Capabilities; use App\Support\Auth\Capabilities;
use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer;
use App\Support\BackupHealth\BackupHealthActionTarget; use App\Support\BackupHealth\BackupHealthActionTarget;
use App\Support\BackupHealth\TenantBackupHealthAssessment; use App\Support\BackupHealth\TenantBackupHealthAssessment;
use App\Support\BackupHealth\TenantBackupHealthResolver; use App\Support\BackupHealth\TenantBackupHealthResolver;
use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer;
use App\Support\Baselines\TenantGovernanceAggregate; use App\Support\Baselines\TenantGovernanceAggregate;
use App\Support\Baselines\TenantGovernanceAggregateResolver; use App\Support\Baselines\TenantGovernanceAggregateResolver;
use App\Support\Links\RequiredPermissionsLinks; use App\Support\Links\RequiredPermissionsLinks;
use App\Support\OperationCatalog; use App\Support\OperationCatalog;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunLinks; use App\Support\OperationRunLinks;
use App\Support\OperationRunOutcome;
use App\Support\OpsUx\ActiveRuns; use App\Support\OpsUx\ActiveRuns;
use App\Support\OpsUx\OperationUxPresenter; use App\Support\OpsUx\OperationUxPresenter;
use App\Support\RestoreSafety\RestoreResultAttention;
use App\Support\RestoreSafety\RestoreSafetyResolver; use App\Support\RestoreSafety\RestoreSafetyResolver;
use App\Support\Tenants\TenantRecoveryTriagePresentation; use App\Support\Tenants\TenantRecoveryTriagePresentation;
use App\Support\Verification\VerificationReportOverall; use App\Support\Verification\VerificationReportOverall;
@ -1084,8 +1083,7 @@ private function metricCard(
string $icon, string $icon,
array $action, array $action,
?array $chart = null, ?array $chart = null,
): array ): array {
{
return array_merge([ return array_merge([
'key' => $key, 'key' => $key,
'label' => $label, 'label' => $label,
@ -1165,8 +1163,8 @@ private function environmentReviewAction(ManagedEnvironment $tenant, ?User $user
if ($canOpen) { if ($canOpen) {
$url = $review instanceof EnvironmentReview $url = $review instanceof EnvironmentReview
? EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $review], $tenant) ? EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $review], $tenant)
: EnvironmentReviewResource::tenantScopedUrl('index', tenant: $tenant); : EnvironmentReviewResource::environmentScopedUrl('index', tenant: $tenant);
} }
return $this->actionPayload( return $this->actionPayload(
@ -1185,7 +1183,7 @@ private function continueReviewAction(ManagedEnvironment $tenant, ?User $user, E
return $this->actionPayload( return $this->actionPayload(
label: $this->overviewText('action_continue_review'), label: $this->overviewText('action_continue_review'),
url: $canContinue ? EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $review], $tenant) : null, url: $canContinue ? EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $review], $tenant) : null,
helperText: $canContinue ? null : $this->overviewText('helper_continue_review_requires_manage'), helperText: $canContinue ? null : $this->overviewText('helper_continue_review_requires_manage'),
); );
} }
@ -1234,7 +1232,7 @@ private function customerWorkspaceAction(ManagedEnvironment $tenant, ?User $user
if ($canOpenWorkspace) { if ($canOpenWorkspace) {
$url = $reviewPack instanceof ReviewPack && $user->can(Capabilities::REVIEW_PACK_VIEW, $tenant) $url = $reviewPack instanceof ReviewPack && $user->can(Capabilities::REVIEW_PACK_VIEW, $tenant)
? ReviewPackResource::getUrl('view', ['record' => $reviewPack], tenant: $tenant) ? ReviewPackResource::getUrl('view', ['record' => $reviewPack], tenant: $tenant)
: CustomerReviewWorkspace::tenantPrefilterUrl($tenant); : CustomerReviewWorkspace::environmentFilterUrl($tenant);
} }
return $this->actionPayload( return $this->actionPayload(

View File

@ -12,9 +12,9 @@
use Illuminate\Session\Store; use Illuminate\Session\Store;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
final class CanonicalAdminTenantFilterState final class CanonicalAdminEnvironmentFilterState
{ {
private const STATE_PREFIX = 'filament.admin_tenant_filter_state'; private const STATE_PREFIX = 'filament.admin_environment_filter_state';
public function __construct( public function __construct(
private readonly OperateHubShell $operateHubShell, private readonly OperateHubShell $operateHubShell,
@ -25,20 +25,20 @@ public function currentFilterValue(
string $filtersSessionKey, string $filtersSessionKey,
?array $tableFilters = null, ?array $tableFilters = null,
?Request $request = null, ?Request $request = null,
?string $tenantFilterName = 'managed_environment_id', ?string $environmentFilterName = 'managed_environment_id',
): ?string { ): ?string {
if ($tenantFilterName === null) { if ($environmentFilterName === null) {
return null; return null;
} }
$tableFilterValue = data_get($tableFilters ?? [], "{$tenantFilterName}.value"); $tableFilterValue = data_get($tableFilters ?? [], "{$environmentFilterName}.value");
if (is_scalar($tableFilterValue) && (string) $tableFilterValue !== '') { if (is_scalar($tableFilterValue) && (string) $tableFilterValue !== '') {
return (string) $tableFilterValue; return (string) $tableFilterValue;
} }
$persistedFilters = $this->session($request)->get($filtersSessionKey, []); $persistedFilters = $this->session($request)->get($filtersSessionKey, []);
$persistedValue = data_get(is_array($persistedFilters) ? $persistedFilters : [], "{$tenantFilterName}.value"); $persistedValue = data_get(is_array($persistedFilters) ? $persistedFilters : [], "{$environmentFilterName}.value");
if (! is_scalar($persistedValue) || (string) $persistedValue === '') { if (! is_scalar($persistedValue) || (string) $persistedValue === '') {
return null; return null;
@ -48,14 +48,14 @@ public function currentFilterValue(
} }
/** /**
* @param array<int, string> $tenantSensitiveFilters * @param array<int, string> $environmentSensitiveFilters
*/ */
public function sync( public function sync(
string $filtersSessionKey, string $filtersSessionKey,
array $tenantSensitiveFilters = [], array $environmentSensitiveFilters = [],
?Request $request = null, ?Request $request = null,
?string $tenantFilterName = 'managed_environment_id', ?string $environmentFilterName = 'managed_environment_id',
string $tenantAttribute = 'id', string $environmentAttribute = 'id',
): void { ): void {
$session = $this->session($request); $session = $this->session($request);
@ -73,27 +73,27 @@ public function sync(
); );
} }
$activeTenant = $this->operateHubShell->activeEntitledTenant($request); $activeEnvironment = $this->operateHubShell->activeEntitledTenant($request);
$resolvedTenantId = $activeTenant instanceof ManagedEnvironment ? (string) $activeTenant->getKey() : null; $resolvedEnvironmentId = $activeEnvironment instanceof ManagedEnvironment ? (string) $activeEnvironment->getKey() : null;
$stateKey = $this->stateKey($filtersSessionKey); $stateKey = $this->stateKey($filtersSessionKey);
$previousTenantId = $session->get($stateKey); $previousEnvironmentId = $session->get($stateKey);
if ($previousTenantId !== $resolvedTenantId) { if ($previousEnvironmentId !== $resolvedEnvironmentId) {
foreach ($tenantSensitiveFilters as $filterName) { foreach ($environmentSensitiveFilters as $filterName) {
Arr::forget($persistedFilters, $filterName); Arr::forget($persistedFilters, $filterName);
} }
} }
if ($tenantFilterName !== null) { if ($environmentFilterName !== null) {
$tenantFilterValue = match ($tenantAttribute) { $environmentFilterValue = match ($environmentAttribute) {
'external_id' => $activeTenant?->external_id, 'external_id' => $activeEnvironment?->external_id,
default => $resolvedTenantId, default => $resolvedEnvironmentId,
}; };
if ($tenantFilterValue !== null) { if ($environmentFilterValue !== null) {
data_set($persistedFilters, "{$tenantFilterName}.value", $tenantFilterValue); data_set($persistedFilters, "{$environmentFilterName}.value", $environmentFilterValue);
} else { } else {
Arr::forget($persistedFilters, $tenantFilterName); Arr::forget($persistedFilters, $environmentFilterName);
} }
} }
@ -103,7 +103,7 @@ public function sync(
$session->put($filtersSessionKey, $persistedFilters); $session->put($filtersSessionKey, $persistedFilters);
} }
$session->put($stateKey, $resolvedTenantId); $session->put($stateKey, $resolvedEnvironmentId);
} }
/** /**

View File

@ -9,24 +9,23 @@
use App\Filament\Pages\Monitoring\FindingExceptionsQueue; use App\Filament\Pages\Monitoring\FindingExceptionsQueue;
use App\Filament\Pages\Reviews\CustomerReviewWorkspace; use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
use App\Filament\Resources\AlertDeliveryResource; use App\Filament\Resources\AlertDeliveryResource;
use App\Filament\Resources\EnvironmentReviewResource;
use App\Filament\Resources\FindingExceptionResource; use App\Filament\Resources\FindingExceptionResource;
use App\Filament\Resources\FindingResource; use App\Filament\Resources\FindingResource;
use App\Filament\Resources\EnvironmentReviewResource;
use App\Models\AlertDelivery; use App\Models\AlertDelivery;
use App\Models\Finding; use App\Models\Finding;
use App\Models\FindingException; use App\Models\FindingException;
use App\Models\OperationRun;
use App\Models\ManagedEnvironment; use App\Models\ManagedEnvironment;
use App\Models\ManagedEnvironmentTriageReview; use App\Models\ManagedEnvironmentTriageReview;
use App\Models\OperationRun;
use App\Models\User; use App\Models\User;
use App\Models\Workspace; use App\Models\Workspace;
use App\Services\EnvironmentReviews\EnvironmentReviewRegisterService; use App\Services\EnvironmentReviews\EnvironmentReviewRegisterService;
use App\Support\Auth\Capabilities;
use App\Support\BackupHealth\TenantBackupHealthResolver; use App\Support\BackupHealth\TenantBackupHealthResolver;
use App\Support\Navigation\CanonicalNavigationContext; use App\Support\Navigation\CanonicalNavigationContext;
use App\Support\OperationRunLinks; use App\Support\OperationRunLinks;
use App\Support\PortfolioTriage\PortfolioArrivalContextToken;
use App\Support\PortfolioTriage\ManagedEnvironmentTriageReviewStateResolver; use App\Support\PortfolioTriage\ManagedEnvironmentTriageReviewStateResolver;
use App\Support\PortfolioTriage\PortfolioArrivalContextToken;
use App\Support\RestoreSafety\RestoreSafetyResolver; use App\Support\RestoreSafety\RestoreSafetyResolver;
use Illuminate\Support\Str; use Illuminate\Support\Str;
@ -550,7 +549,7 @@ private function reviewFollowUpSection(
'summary' => $this->reviewSummary($followUpCount, $changedCount), 'summary' => $this->reviewSummary($followUpCount, $changedCount),
'dominant_action_label' => 'Open customer review workspace', 'dominant_action_label' => 'Open customer review workspace',
'dominant_action_url' => $selectedTenant instanceof ManagedEnvironment 'dominant_action_url' => $selectedTenant instanceof ManagedEnvironment
? $this->appendQuery(CustomerReviewWorkspace::tenantPrefilterUrl($selectedTenant), $navigationContext?->toQuery() ?? []) ? $this->appendQuery(CustomerReviewWorkspace::environmentFilterUrl($selectedTenant), $navigationContext?->toQuery() ?? [])
: $this->appendQuery(CustomerReviewWorkspace::getUrl(panel: 'admin'), $navigationContext?->toQuery() ?? []), : $this->appendQuery(CustomerReviewWorkspace::getUrl(panel: 'admin'), $navigationContext?->toQuery() ?? []),
'entries' => array_slice($rawEntries, 0, self::PREVIEW_LIMIT), 'entries' => array_slice($rawEntries, 0, self::PREVIEW_LIMIT),
'empty_state' => $selectedTenant instanceof ManagedEnvironment 'empty_state' => $selectedTenant instanceof ManagedEnvironment
@ -609,12 +608,12 @@ private function orderedIntakeFindingsQuery(\Illuminate\Database\Eloquent\Builde
{ {
return $query return $query
->orderByRaw( ->orderByRaw(
"case 'case
when due_at is not null and due_at < ? then 0 when due_at is not null and due_at < ? then 0
when status = ? then 1 when status = ? then 1
when status = ? then 2 when status = ? then 2
else 3 else 3
end asc", end asc',
[now(), Finding::STATUS_REOPENED, Finding::STATUS_NEW], [now(), Finding::STATUS_REOPENED, Finding::STATUS_NEW],
) )
->orderByRaw('case when due_at is null then 1 else 0 end asc') ->orderByRaw('case when due_at is null then 1 else 0 end asc')
@ -724,13 +723,13 @@ private function orderedFindingExceptionsQuery(\Illuminate\Database\Eloquent\Bui
{ {
return $query return $query
->orderByRaw( ->orderByRaw(
"case 'case
when status = ? then 0 when status = ? then 0
when current_validity_state = ? then 1 when current_validity_state = ? then 1
when current_validity_state = ? then 2 when current_validity_state = ? then 2
when current_validity_state = ? then 3 when current_validity_state = ? then 3
else 4 else 4
end asc", end asc',
[ [
FindingException::STATUS_PENDING, FindingException::STATUS_PENDING,
FindingException::VALIDITY_EXPIRED, FindingException::VALIDITY_EXPIRED,
@ -909,8 +908,8 @@ private function reviewEntry(
: null, : null,
])); ]));
$destinationUrl = $latestPublishedReview !== null $destinationUrl = $latestPublishedReview !== null
? EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $latestPublishedReview], $tenant) ? EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $latestPublishedReview], $tenant)
: CustomerReviewWorkspace::tenantPrefilterUrl($tenant); : CustomerReviewWorkspace::environmentFilterUrl($tenant);
return [ return [
'family_key' => 'review_follow_up', 'family_key' => 'review_follow_up',

View File

@ -4,11 +4,11 @@
use App\Models\ManagedEnvironment; use App\Models\ManagedEnvironment;
use App\Models\User; use App\Models\User;
use App\Support\Navigation\AdminSurfaceScope;
use App\Support\Navigation\NavigationScope; use App\Support\Navigation\NavigationScope;
use App\Support\Navigation\WorkspaceHubRegistry; use App\Support\Navigation\WorkspaceHubRegistry;
use App\Support\Navigation\WorkspaceSidebarNavigation; use App\Support\Navigation\WorkspaceSidebarNavigation;
use App\Support\OperateHub\OperateHubShell; use App\Support\OperateHub\OperateHubShell;
use App\Support\Tenants\TenantPageCategory;
use App\Support\Workspaces\WorkspaceContext; use App\Support\Workspaces\WorkspaceContext;
use Closure; use Closure;
use Filament\Facades\Filament; use Filament\Facades\Filament;
@ -16,7 +16,7 @@
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
class EnsureFilamentTenantSelected class EnsureEnvironmentContextSelected
{ {
/** /**
* @param Closure(Request): Response $next * @param Closure(Request): Response $next
@ -100,7 +100,7 @@ public function handle(Request $request, Closure $next): Response
return redirect()->route('filament.admin.pages.choose-environment'); return redirect()->route('filament.admin.pages.choose-environment');
} }
if ($resolvedContext->pageCategory === TenantPageCategory::TenantBound && ! $resolvedContext->hasTenant()) { if ($resolvedContext->pageCategory === AdminSurfaceScope::EnvironmentBound && ! $resolvedContext->hasTenant()) {
abort(404); abort(404);
} }
@ -116,7 +116,7 @@ public function handle(Request $request, Closure $next): Response
$resolvedContext->hasTenant() $resolvedContext->hasTenant()
&& ( && (
! $this->isWorkspaceScopedPageWithTenant($path) ! $this->isWorkspaceScopedPageWithTenant($path)
&& $resolvedContext->pageCategory === TenantPageCategory::TenantBound && $resolvedContext->pageCategory === AdminSurfaceScope::EnvironmentBound
) )
) { ) {
Filament::setTenant($resolvedContext->tenant, true); Filament::setTenant($resolvedContext->tenant, true);
@ -175,7 +175,7 @@ private function isLivewireUpdatePath(string $path): bool
private function isCanonicalWorkspaceRecordViewerPath(string $path): bool private function isCanonicalWorkspaceRecordViewerPath(string $path): bool
{ {
return TenantPageCategory::fromPath($path) === TenantPageCategory::CanonicalWorkspaceRecordViewer; return AdminSurfaceScope::fromPath($path) === AdminSurfaceScope::CanonicalWorkspaceRecordViewer;
} }
private function requestHasExplicitTenantHint(Request $request): bool private function requestHasExplicitTenantHint(Request $request): bool

View File

@ -2,18 +2,18 @@
declare(strict_types=1); declare(strict_types=1);
namespace App\Support\Tenants; namespace App\Support\Navigation;
use App\Support\Navigation\WorkspaceHubRegistry; use App\Support\Tenants\TenantInteractionLane;
use Illuminate\Http\Request; use Illuminate\Http\Request;
enum TenantPageCategory: string enum AdminSurfaceScope: string
{ {
case WorkspaceWideSurface = 'workspace_wide_surface'; case WorkspaceWideSurface = 'workspace_wide_surface';
case WorkspaceScoped = 'workspace_scoped'; case WorkspaceScoped = 'workspace_scoped';
case WorkspaceChooserException = 'workspace_chooser_exception'; case WorkspaceChooserException = 'workspace_chooser_exception';
case TenantBound = 'tenant_bound'; case EnvironmentBound = 'environment_bound';
case TenantScopedEvidence = 'tenant_scoped_evidence'; case EnvironmentScopedEvidence = 'environment_scoped_evidence';
case OnboardingWorkflow = 'onboarding_workflow'; case OnboardingWorkflow = 'onboarding_workflow';
case CanonicalWorkspaceRecordViewer = 'canonical_workspace_record_viewer'; case CanonicalWorkspaceRecordViewer = 'canonical_workspace_record_viewer';
@ -46,7 +46,7 @@ public static function fromPath(string $path): self
str_starts_with($normalizedPath, '/admin/evidence/') str_starts_with($normalizedPath, '/admin/evidence/')
&& ! str_starts_with($normalizedPath, '/admin/evidence/overview') && ! str_starts_with($normalizedPath, '/admin/evidence/overview')
) { ) {
return self::TenantScopedEvidence; return self::EnvironmentScopedEvidence;
} }
if (preg_match('#^/admin/onboarding(?:/[^/]+)?$#', $normalizedPath) === 1) { if (preg_match('#^/admin/onboarding(?:/[^/]+)?$#', $normalizedPath) === 1) {
@ -54,13 +54,13 @@ public static function fromPath(string $path): self
} }
if (preg_match('#^/admin/workspaces/[^/]+/environments/[^/]+(?:/|$)#', $normalizedPath) === 1) { if (preg_match('#^/admin/workspaces/[^/]+/environments/[^/]+(?:/|$)#', $normalizedPath) === 1) {
return self::TenantBound; return self::EnvironmentBound;
} }
return self::WorkspaceScoped; return self::WorkspaceScoped;
} }
public function allowsQueryTenantHints(): bool public function allowsQueryEnvironmentHints(): bool
{ {
return match ($this) { return match ($this) {
self::WorkspaceWideSurface, self::WorkspaceScoped, self::OnboardingWorkflow => true, self::WorkspaceWideSurface, self::WorkspaceScoped, self::OnboardingWorkflow => true,
@ -68,7 +68,7 @@ public function allowsQueryTenantHints(): bool
}; };
} }
public function allowsRememberedTenantRestore(): bool public function allowsRememberedEnvironmentRestore(): bool
{ {
return match ($this) { return match ($this) {
self::WorkspaceScoped, self::OnboardingWorkflow, self::CanonicalWorkspaceRecordViewer => true, self::WorkspaceScoped, self::OnboardingWorkflow, self::CanonicalWorkspaceRecordViewer => true,
@ -76,7 +76,7 @@ public function allowsRememberedTenantRestore(): bool
}; };
} }
public function allowsTenantlessState(): bool public function allowsEnvironmentlessState(): bool
{ {
return match ($this) { return match ($this) {
self::WorkspaceWideSurface, self::WorkspaceWideSurface,
@ -88,7 +88,7 @@ public function allowsTenantlessState(): bool
}; };
} }
public function forcesTenantlessShellContext(): bool public function forcesEnvironmentlessShellContext(): bool
{ {
return match ($this) { return match ($this) {
self::WorkspaceWideSurface, self::WorkspaceWideSurface,
@ -98,17 +98,17 @@ public function forcesTenantlessShellContext(): bool
}; };
} }
public function requiresExplicitTenant(): bool public function requiresExplicitEnvironment(): bool
{ {
return match ($this) { return match ($this) {
self::TenantBound, self::TenantScopedEvidence => true, self::EnvironmentBound, self::EnvironmentScopedEvidence => true,
default => false, default => false,
}; };
} }
public function lane(): TenantInteractionLane public function lane(): TenantInteractionLane
{ {
return TenantInteractionLane::fromPageCategory($this); return TenantInteractionLane::fromSurfaceScope($this);
} }
private static function isWorkspaceWideSurfacePath(string $normalizedPath): bool private static function isWorkspaceWideSurfacePath(string $normalizedPath): bool

View File

@ -4,7 +4,6 @@
namespace App\Support\Navigation; namespace App\Support\Navigation;
use App\Support\Tenants\TenantPageCategory;
use Illuminate\Http\Request; use Illuminate\Http\Request;
final class NavigationScope final class NavigationScope
@ -17,8 +16,8 @@ public static function isWorkspaceSurface(?Request $request = null): bool
public static function isEnvironmentSurface(?Request $request = null): bool public static function isEnvironmentSurface(?Request $request = null): bool
{ {
return in_array(self::pageCategory($request), [ return in_array(self::pageCategory($request), [
TenantPageCategory::TenantBound, AdminSurfaceScope::EnvironmentBound,
TenantPageCategory::TenantScopedEvidence, AdminSurfaceScope::EnvironmentScopedEvidence,
], true); ], true);
} }
@ -27,9 +26,9 @@ public static function shouldRegisterEnvironmentNavigation(?Request $request = n
return self::isEnvironmentSurface($request); return self::isEnvironmentSurface($request);
} }
public static function pageCategory(?Request $request = null): TenantPageCategory public static function pageCategory(?Request $request = null): AdminSurfaceScope
{ {
return TenantPageCategory::fromPath(self::effectivePath($request)); return AdminSurfaceScope::fromPath(self::effectivePath($request));
} }
private static function effectivePath(?Request $request = null): string private static function effectivePath(?Request $request = null): string

View File

@ -20,11 +20,11 @@
use App\Models\BaselineSnapshotItem; use App\Models\BaselineSnapshotItem;
use App\Models\Finding; use App\Models\Finding;
use App\Models\FindingException; use App\Models\FindingException;
use App\Models\ManagedEnvironment;
use App\Models\OperationRun; use App\Models\OperationRun;
use App\Models\Policy; use App\Models\Policy;
use App\Models\PolicyVersion; use App\Models\PolicyVersion;
use App\Models\RestoreRun; use App\Models\RestoreRun;
use App\Models\ManagedEnvironment;
use App\Models\User; use App\Models\User;
use App\Models\Workspace; use App\Models\Workspace;
use App\Services\Auth\CapabilityResolver; use App\Services\Auth\CapabilityResolver;
@ -39,7 +39,6 @@
use App\Support\Ui\DerivedState\DerivedStateFamily; use App\Support\Ui\DerivedState\DerivedStateFamily;
use App\Support\Ui\DerivedState\DerivedStateKey; use App\Support\Ui\DerivedState\DerivedStateKey;
use App\Support\Ui\DerivedState\RequestScopedDerivedStateStore; use App\Support\Ui\DerivedState\RequestScopedDerivedStateStore;
use Filament\Facades\Filament;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
@ -367,7 +366,7 @@ private function memoizedEntries(
context: [ context: [
'source_type' => $sourceType, 'source_type' => $sourceType,
'surface' => $surface, 'surface' => $surface,
'active_tenant_id' => $this->activeTenantId(), 'active_tenant_id' => $this->activeEnvironmentId(),
'route_name' => request()?->route()?->getName(), 'route_name' => request()?->route()?->getName(),
'user_id' => auth()->id(), 'user_id' => auth()->id(),
], ],
@ -631,7 +630,7 @@ private function snapshotPolicyVersionEntry(NavigationMatrixRule $rule, Baseline
return $this->policyVersionEntry( return $this->policyVersionEntry(
rule: $rule, rule: $rule,
policyVersionId: is_numeric($policyVersionId) ? (int) $policyVersionId : null, policyVersionId: is_numeric($policyVersionId) ? (int) $policyVersionId : null,
tenantId: $this->activeTenantId(), tenantId: $this->activeEnvironmentId(),
); );
} }
@ -1094,7 +1093,7 @@ private function contextForOperationRun(OperationRun $run): CanonicalNavigationC
); );
} }
private function activeTenantId(): ?int private function activeEnvironmentId(): ?int
{ {
$tenant = app(OperateHubShell::class)->activeEntitledTenant(request()); $tenant = app(OperateHubShell::class)->activeEntitledTenant(request());

View File

@ -10,9 +10,9 @@
use App\Services\Auth\CapabilityResolver; use App\Services\Auth\CapabilityResolver;
use App\Services\Tenants\TenantOperabilityService; use App\Services\Tenants\TenantOperabilityService;
use App\Support\ManagedEnvironmentLinks; use App\Support\ManagedEnvironmentLinks;
use App\Support\Navigation\AdminSurfaceScope;
use App\Support\Navigation\WorkspaceHubRegistry; use App\Support\Navigation\WorkspaceHubRegistry;
use App\Support\Tenants\TenantOperabilityQuestion; use App\Support\Tenants\TenantOperabilityQuestion;
use App\Support\Tenants\TenantPageCategory;
use App\Support\Workspaces\WorkspaceContext; use App\Support\Workspaces\WorkspaceContext;
use Filament\Actions\Action; use Filament\Actions\Action;
use Filament\Facades\Filament; use Filament\Facades\Filament;
@ -30,10 +30,10 @@ public function __construct(
public function scopeLabel(?Request $request = null): string public function scopeLabel(?Request $request = null): string
{ {
$activeTenant = $this->activeEntitledTenant($request); $activeEnvironment = $this->activeEntitledTenant($request);
if ($activeTenant instanceof ManagedEnvironment) { if ($activeEnvironment instanceof ManagedEnvironment) {
return __('localization.shell.environment_scope').': '.$activeTenant->name; return __('localization.shell.environment_scope').': '.$activeEnvironment->name;
} }
return __('localization.shell.all_environments'); return __('localization.shell.all_environments');
@ -44,12 +44,12 @@ public function scopeLabel(?Request $request = null): string
*/ */
public function returnAffordance(?Request $request = null): ?array public function returnAffordance(?Request $request = null): ?array
{ {
$activeTenant = $this->activeEntitledTenant($request); $activeEnvironment = $this->activeEntitledTenant($request);
if ($activeTenant instanceof ManagedEnvironment) { if ($activeEnvironment instanceof ManagedEnvironment) {
return [ return [
'label' => 'Back to '.$activeTenant->name, 'label' => 'Back to '.$activeEnvironment->name,
'url' => ManagedEnvironmentLinks::viewUrl($activeTenant), 'url' => ManagedEnvironmentLinks::viewUrl($activeEnvironment),
]; ];
} }
@ -135,7 +135,7 @@ private function buildResolvedContext(?Request $request = null): ResolvedShellCo
pageCategory: $pageCategory, pageCategory: $pageCategory,
state: 'missing_workspace', state: 'missing_workspace',
displayMode: 'recovery', displayMode: 'recovery',
recoveryAction: $pageCategory === TenantPageCategory::WorkspaceChooserException ? 'none' : 'redirect_choose_workspace', recoveryAction: $pageCategory === AdminSurfaceScope::WorkspaceChooserException ? 'none' : 'redirect_choose_workspace',
recoveryDestination: '/admin/choose-workspace', recoveryDestination: '/admin/choose-workspace',
recoveryReason: 'missing_workspace', recoveryReason: 'missing_workspace',
); );
@ -157,7 +157,7 @@ private function buildResolvedContext(?Request $request = null): ResolvedShellCo
$recoveryReason = $routeTenant['reason']; $recoveryReason = $routeTenant['reason'];
if ($pageCategory === TenantPageCategory::TenantBound && $recoveryReason !== null) { if ($pageCategory === AdminSurfaceScope::EnvironmentBound && $recoveryReason !== null) {
return new ResolvedShellContext( return new ResolvedShellContext(
workspace: $workspace, workspace: $workspace,
tenant: null, tenant: null,
@ -170,7 +170,7 @@ private function buildResolvedContext(?Request $request = null): ResolvedShellCo
); );
} }
if ($pageCategory->forcesTenantlessShellContext()) { if ($pageCategory->forcesEnvironmentlessShellContext()) {
return new ResolvedShellContext( return new ResolvedShellContext(
workspace: $workspace, workspace: $workspace,
tenant: null, tenant: null,
@ -225,13 +225,13 @@ private function buildResolvedContext(?Request $request = null): ResolvedShellCo
); );
} }
if ($pageCategory->allowsRememberedTenantRestore()) { if ($pageCategory->allowsRememberedEnvironmentRestore()) {
$rememberedTenant = $this->workspaceContext->rememberedTenant($request); $rememberedEnvironment = $this->workspaceContext->rememberedEnvironment($request);
if ($rememberedTenant instanceof ManagedEnvironment) { if ($rememberedEnvironment instanceof ManagedEnvironment) {
return new ResolvedShellContext( return new ResolvedShellContext(
workspace: $workspace, workspace: $workspace,
tenant: $rememberedTenant, tenant: $rememberedEnvironment,
pageCategory: $pageCategory, pageCategory: $pageCategory,
state: 'tenant_scoped', state: 'tenant_scoped',
displayMode: 'tenant_scoped', displayMode: 'tenant_scoped',
@ -241,7 +241,7 @@ private function buildResolvedContext(?Request $request = null): ResolvedShellCo
} }
} }
if ($pageCategory->requiresExplicitTenant()) { if ($pageCategory->requiresExplicitEnvironment()) {
return new ResolvedShellContext( return new ResolvedShellContext(
workspace: $workspace, workspace: $workspace,
tenant: null, tenant: null,
@ -249,10 +249,10 @@ private function buildResolvedContext(?Request $request = null): ResolvedShellCo
state: 'missing_tenant', state: 'missing_tenant',
displayMode: 'recovery', displayMode: 'recovery',
workspaceSource: $workspaceSource, workspaceSource: $workspaceSource,
recoveryAction: $pageCategory === TenantPageCategory::TenantScopedEvidence recoveryAction: $pageCategory === AdminSurfaceScope::EnvironmentScopedEvidence
? 'redirect_evidence_overview' ? 'redirect_evidence_overview'
: 'abort_not_found', : 'abort_not_found',
recoveryDestination: $pageCategory === TenantPageCategory::TenantScopedEvidence recoveryDestination: $pageCategory === AdminSurfaceScope::EnvironmentScopedEvidence
? '/admin/evidence/overview' ? '/admin/evidence/overview'
: null, : null,
recoveryReason: $recoveryReason ?? 'missing_tenant', recoveryReason: $recoveryReason ?? 'missing_tenant',
@ -272,7 +272,7 @@ private function buildResolvedContext(?Request $request = null): ResolvedShellCo
private function resolveValidatedFilamentTenant( private function resolveValidatedFilamentTenant(
?Request $request = null, ?Request $request = null,
?TenantPageCategory $pageCategory = null, ?AdminSurfaceScope $pageCategory = null,
?Workspace $workspace = null, ?Workspace $workspace = null,
): ?ManagedEnvironment { ): ?ManagedEnvironment {
$tenant = Filament::getTenant(); $tenant = Filament::getTenant();
@ -297,7 +297,7 @@ private function resolveValidatedRouteTenant(
?ManagedEnvironment $tenant, ?ManagedEnvironment $tenant,
Workspace $workspace, Workspace $workspace,
?Request $request = null, ?Request $request = null,
?TenantPageCategory $pageCategory = null, ?AdminSurfaceScope $pageCategory = null,
): array { ): array {
$pageCategory ??= $this->pageCategory($request); $pageCategory ??= $this->pageCategory($request);
@ -316,22 +316,22 @@ private function resolveValidatedRouteTenant(
private function resolveWorkspaceForPageCategory( private function resolveWorkspaceForPageCategory(
?ManagedEnvironment $tenant, ?ManagedEnvironment $tenant,
TenantPageCategory $pageCategory, AdminSurfaceScope $pageCategory,
?Request $request = null, ?Request $request = null,
): ?Workspace { ): ?Workspace {
return match ($pageCategory) { return match ($pageCategory) {
TenantPageCategory::TenantScopedEvidence => $this->workspaceContext->currentWorkspace($request), AdminSurfaceScope::EnvironmentScopedEvidence => $this->workspaceContext->currentWorkspace($request),
TenantPageCategory::TenantBound => $this->workspaceContext->currentWorkspaceOrTenantWorkspace($tenant, $request), AdminSurfaceScope::EnvironmentBound => $this->workspaceContext->currentWorkspaceOrEnvironmentWorkspace($tenant, $request),
default => $this->workspaceContext->currentWorkspaceOrTenantWorkspace($tenant, $request), default => $this->workspaceContext->currentWorkspaceOrEnvironmentWorkspace($tenant, $request),
}; };
} }
private function resolveValidatedQueryHintTenant( private function resolveValidatedQueryHintTenant(
?Request $request, ?Request $request,
Workspace $workspace, Workspace $workspace,
TenantPageCategory $pageCategory, AdminSurfaceScope $pageCategory,
): array { ): array {
if (! $pageCategory->allowsQueryTenantHints()) { if (! $pageCategory->allowsQueryEnvironmentHints()) {
return ['tenant' => null, 'reason' => null]; return ['tenant' => null, 'reason' => null];
} }
@ -350,7 +350,7 @@ private function resolveValidatedQueryHintTenant(
return ['tenant' => $queryTenant, 'reason' => null]; return ['tenant' => $queryTenant, 'reason' => null];
} }
private function resolveRouteTenantCandidate(?Request $request = null, ?TenantPageCategory $pageCategory = null): ?ManagedEnvironment private function resolveRouteTenantCandidate(?Request $request = null, ?AdminSurfaceScope $pageCategory = null): ?ManagedEnvironment
{ {
$route = $request?->route(); $route = $request?->route();
$pageCategory ??= $this->pageCategory($request); $pageCategory ??= $this->pageCategory($request);
@ -445,7 +445,7 @@ private function tenantValidationReason(
ManagedEnvironment $tenant, ManagedEnvironment $tenant,
Workspace $workspace, Workspace $workspace,
?Request $request = null, ?Request $request = null,
?TenantPageCategory $pageCategory = null, ?AdminSurfaceScope $pageCategory = null,
): ?string { ): ?string {
$pageCategory ??= $this->pageCategory($request); $pageCategory ??= $this->pageCategory($request);
@ -463,8 +463,8 @@ private function tenantValidationReason(
return 'not_member'; return 'not_member';
} }
$question = $pageCategory === TenantPageCategory::TenantBound $question = $pageCategory === AdminSurfaceScope::EnvironmentBound
? TenantOperabilityQuestion::TenantBoundViewability ? TenantOperabilityQuestion::EnvironmentBoundViewability
: TenantOperabilityQuestion::AdministrativeDiscoverability; : TenantOperabilityQuestion::AdministrativeDiscoverability;
$allowed = $this->tenantOperabilityService->outcomeFor( $allowed = $this->tenantOperabilityService->outcomeFor(
@ -480,13 +480,13 @@ private function tenantValidationReason(
return null; return null;
} }
return $pageCategory === TenantPageCategory::TenantBound return $pageCategory === AdminSurfaceScope::EnvironmentBound
? 'inaccessible' ? 'inaccessible'
: 'not_operable'; : 'not_operable';
} }
private function pageCategory(?Request $request = null): TenantPageCategory private function pageCategory(?Request $request = null): AdminSurfaceScope
{ {
return TenantPageCategory::fromRequest($request); return AdminSurfaceScope::fromRequest($request);
} }
} }

View File

@ -6,14 +6,14 @@
use App\Models\ManagedEnvironment; use App\Models\ManagedEnvironment;
use App\Models\Workspace; use App\Models\Workspace;
use App\Support\Tenants\TenantPageCategory; use App\Support\Navigation\AdminSurfaceScope;
final readonly class ResolvedShellContext final readonly class ResolvedShellContext
{ {
public function __construct( public function __construct(
public ?Workspace $workspace, public ?Workspace $workspace,
public ?ManagedEnvironment $tenant, public ?ManagedEnvironment $tenant,
public TenantPageCategory $pageCategory, public AdminSurfaceScope $pageCategory,
public string $state, public string $state,
public string $displayMode, public string $displayMode,
public string $workspaceSource = 'none', public string $workspaceSource = 'none',

View File

@ -237,7 +237,7 @@ public static function related(OperationRun $run, ?ManagedEnvironment $tenant):
->first(); ->first();
if ($review instanceof EnvironmentReview) { if ($review instanceof EnvironmentReview) {
$links['ManagedEnvironment Review'] = EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $review], $tenant); $links['ManagedEnvironment Review'] = EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $review], $tenant);
} }
} }

View File

@ -4,18 +4,18 @@
namespace App\Support\SupportDiagnostics; namespace App\Support\SupportDiagnostics;
use App\Filament\Resources\EnvironmentReviewResource;
use App\Filament\Resources\FindingResource; use App\Filament\Resources\FindingResource;
use App\Filament\Resources\ProviderConnectionResource; use App\Filament\Resources\ProviderConnectionResource;
use App\Filament\Resources\ReviewPackResource; use App\Filament\Resources\ReviewPackResource;
use App\Filament\Resources\EnvironmentReviewResource;
use App\Models\AuditLog; use App\Models\AuditLog;
use App\Models\EnvironmentReview;
use App\Models\Finding; use App\Models\Finding;
use App\Models\ManagedEnvironment;
use App\Models\OperationRun; use App\Models\OperationRun;
use App\Models\ProviderConnection; use App\Models\ProviderConnection;
use App\Models\ReviewPack; use App\Models\ReviewPack;
use App\Models\StoredReport; use App\Models\StoredReport;
use App\Models\ManagedEnvironment;
use App\Models\EnvironmentReview;
use App\Models\User; use App\Models\User;
use App\Models\Workspace; use App\Models\Workspace;
use App\Support\Ai\AiDataClassification; use App\Support\Ai\AiDataClassification;
@ -741,7 +741,7 @@ private function environmentReviewSection(?EnvironmentReview $review, ?ManagedEn
label: 'ManagedEnvironment review #'.$review->getKey(), label: 'ManagedEnvironment review #'.$review->getKey(),
actionLabel: 'Open environment review', actionLabel: 'Open environment review',
url: $tenant instanceof ManagedEnvironment url: $tenant instanceof ManagedEnvironment
? EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $review], $tenant) ? EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $review], $tenant)
: null, : null,
freshnessAt: $review->generated_at, freshnessAt: $review->generated_at,
), ),

View File

@ -4,6 +4,8 @@
namespace App\Support\Tenants; namespace App\Support\Tenants;
use App\Support\Navigation\AdminSurfaceScope;
enum TenantInteractionLane: string enum TenantInteractionLane: string
{ {
case StandardActiveOperating = 'standard_active_operating'; case StandardActiveOperating = 'standard_active_operating';
@ -11,16 +13,16 @@ enum TenantInteractionLane: string
case AdministrativeManagement = 'administrative_management'; case AdministrativeManagement = 'administrative_management';
case CanonicalWorkspaceRecord = 'canonical_workspace_record'; case CanonicalWorkspaceRecord = 'canonical_workspace_record';
public static function fromPageCategory(TenantPageCategory $pageCategory): self public static function fromSurfaceScope(AdminSurfaceScope $pageCategory): self
{ {
return match ($pageCategory) { return match ($pageCategory) {
TenantPageCategory::OnboardingWorkflow => self::OnboardingWorkflow, AdminSurfaceScope::OnboardingWorkflow => self::OnboardingWorkflow,
TenantPageCategory::TenantBound, AdminSurfaceScope::EnvironmentBound,
TenantPageCategory::TenantScopedEvidence => self::AdministrativeManagement, AdminSurfaceScope::EnvironmentScopedEvidence => self::AdministrativeManagement,
TenantPageCategory::CanonicalWorkspaceRecordViewer => self::CanonicalWorkspaceRecord, AdminSurfaceScope::CanonicalWorkspaceRecordViewer => self::CanonicalWorkspaceRecord,
TenantPageCategory::WorkspaceWideSurface, AdminSurfaceScope::WorkspaceWideSurface,
TenantPageCategory::WorkspaceScoped, AdminSurfaceScope::WorkspaceScoped,
TenantPageCategory::WorkspaceChooserException => self::StandardActiveOperating, AdminSurfaceScope::WorkspaceChooserException => self::StandardActiveOperating,
}; };
} }
} }

View File

@ -105,7 +105,7 @@ public function supportsQuestion(TenantOperabilityQuestion $question, TenantInte
return match ($question) { return match ($question) {
TenantOperabilityQuestion::SelectorEligibility, TenantOperabilityQuestion::SelectorEligibility,
TenantOperabilityQuestion::RememberedContextValidity => $lane === TenantInteractionLane::StandardActiveOperating && $this->isSelectableInStandardLane(), TenantOperabilityQuestion::RememberedContextValidity => $lane === TenantInteractionLane::StandardActiveOperating && $this->isSelectableInStandardLane(),
TenantOperabilityQuestion::TenantBoundViewability => $lane === TenantInteractionLane::AdministrativeManagement, TenantOperabilityQuestion::EnvironmentBoundViewability => $lane === TenantInteractionLane::AdministrativeManagement,
TenantOperabilityQuestion::CanonicalLinkedRecordViewability => $lane === TenantInteractionLane::CanonicalWorkspaceRecord, TenantOperabilityQuestion::CanonicalLinkedRecordViewability => $lane === TenantInteractionLane::CanonicalWorkspaceRecord,
TenantOperabilityQuestion::ArchiveEligibility => $lane === TenantInteractionLane::AdministrativeManagement && $this->canArchive(), TenantOperabilityQuestion::ArchiveEligibility => $lane === TenantInteractionLane::AdministrativeManagement && $this->canArchive(),
TenantOperabilityQuestion::RestoreEligibility => $lane === TenantInteractionLane::AdministrativeManagement && $this->canRestore(), TenantOperabilityQuestion::RestoreEligibility => $lane === TenantInteractionLane::AdministrativeManagement && $this->canRestore(),

View File

@ -100,7 +100,7 @@ public static function invalid(?string $normalizedValue = null): self
badgeIcon: 'heroicon-m-exclamation-triangle', badgeIcon: 'heroicon-m-exclamation-triangle',
badgeIconColor: 'danger', badgeIconColor: 'danger',
shortDescription: 'Lifecycle data is invalid and requires review.', shortDescription: 'Lifecycle data is invalid and requires review.',
longDescription: 'The stored tenant lifecycle value is not canonical. Review the source data before treating this tenant as draft, onboarding, active, or archived.', longDescription: 'The stored environment lifecycle value is not canonical. Review the source data before treating this tenant as draft, onboarding, active, or archived.',
isInvalidFallback: true, isInvalidFallback: true,
lifecycle: null, lifecycle: null,
); );

View File

@ -7,6 +7,7 @@
use App\Models\ManagedEnvironment; use App\Models\ManagedEnvironment;
use App\Models\ManagedEnvironmentOnboardingSession; use App\Models\ManagedEnvironmentOnboardingSession;
use App\Models\User; use App\Models\User;
use App\Support\Navigation\AdminSurfaceScope;
final readonly class TenantOperabilityContext final readonly class TenantOperabilityContext
{ {
@ -15,7 +16,7 @@ public function __construct(
public ?User $actor, public ?User $actor,
public ?int $workspaceId, public ?int $workspaceId,
public TenantInteractionLane $lane, public TenantInteractionLane $lane,
public ?TenantPageCategory $pageCategory = null, public ?AdminSurfaceScope $pageCategory = null,
public ?string $linkedRecordType = null, public ?string $linkedRecordType = null,
public ?int $linkedRecordId = null, public ?int $linkedRecordId = null,
public ?ManagedEnvironmentOnboardingSession $onboardingDraft = null, public ?ManagedEnvironmentOnboardingSession $onboardingDraft = null,
@ -28,7 +29,7 @@ public static function forTenant(
?User $actor = null, ?User $actor = null,
?int $workspaceId = null, ?int $workspaceId = null,
TenantInteractionLane $lane = TenantInteractionLane::AdministrativeManagement, TenantInteractionLane $lane = TenantInteractionLane::AdministrativeManagement,
?TenantPageCategory $pageCategory = null, ?AdminSurfaceScope $pageCategory = null,
?ManagedEnvironmentOnboardingSession $onboardingDraft = null, ?ManagedEnvironmentOnboardingSession $onboardingDraft = null,
?string $requiredCapability = null, ?string $requiredCapability = null,
?ManagedEnvironment $selectedTenant = null, ?ManagedEnvironment $selectedTenant = null,

View File

@ -8,7 +8,7 @@ enum TenantOperabilityQuestion: string
{ {
case SelectorEligibility = 'selector_eligibility'; case SelectorEligibility = 'selector_eligibility';
case RememberedContextValidity = 'remembered_context_validity'; case RememberedContextValidity = 'remembered_context_validity';
case TenantBoundViewability = 'tenant_bound_viewability'; case EnvironmentBoundViewability = 'environment_bound_viewability';
case CanonicalLinkedRecordViewability = 'canonical_linked_record_viewability'; case CanonicalLinkedRecordViewability = 'canonical_linked_record_viewability';
case ArchiveEligibility = 'archive_eligibility'; case ArchiveEligibility = 'archive_eligibility';
case RestoreEligibility = 'restore_eligibility'; case RestoreEligibility = 'restore_eligibility';

View File

@ -55,7 +55,7 @@ public function shortExplanation(): string
self::TenantAlreadyArchived => 'The tenant is already archived, so there is nothing else to do for this action.', self::TenantAlreadyArchived => 'The tenant is already archived, so there is nothing else to do for this action.',
self::OnboardingNotResumable => 'This onboarding session can no longer be resumed from the current lifecycle state.', self::OnboardingNotResumable => 'This onboarding session can no longer be resumed from the current lifecycle state.',
self::CanonicalViewFollowupOnly => 'This canonical workspace view is informational only and cannot complete tenant follow-up directly.', self::CanonicalViewFollowupOnly => 'This canonical workspace view is informational only and cannot complete tenant follow-up directly.',
self::RememberedContextStale => 'The remembered tenant context is no longer valid for the current tenant selector state.', self::RememberedContextStale => 'The remembered environment context is no longer valid for the current tenant selector state.',
self::WorkspaceClosed => 'This workspace is closed and cannot be used for active tenant context or new tenant operations until it is reopened.', self::WorkspaceClosed => 'This workspace is closed and cannot be used for active tenant context or new tenant operations until it is reopened.',
self::TenantRemovedFromWorkspace => 'This tenant was removed from the workspace and cannot be selected or used for new tenant operations until it is restored.', self::TenantRemovedFromWorkspace => 'This tenant was removed from the workspace and cannot be selected or used for new tenant operations until it is restored.',
}; };

View File

@ -5,20 +5,22 @@
namespace App\Support\Ui\GovernanceArtifactTruth; namespace App\Support\Ui\GovernanceArtifactTruth;
use App\Filament\Resources\BaselineSnapshotResource; use App\Filament\Resources\BaselineSnapshotResource;
use App\Filament\Resources\EnvironmentReviewResource;
use App\Filament\Resources\EvidenceSnapshotResource; use App\Filament\Resources\EvidenceSnapshotResource;
use App\Filament\Resources\ReviewPackResource; use App\Filament\Resources\ReviewPackResource;
use App\Filament\Resources\EnvironmentReviewResource;
use App\Models\BaselineSnapshot; use App\Models\BaselineSnapshot;
use App\Models\EnvironmentReview;
use App\Models\EvidenceSnapshot; use App\Models\EvidenceSnapshot;
use App\Models\FindingExceptionDecision; use App\Models\FindingExceptionDecision;
use App\Models\OperationRun; use App\Models\OperationRun;
use App\Models\ReviewPack; use App\Models\ReviewPack;
use App\Models\StoredReport; use App\Models\StoredReport;
use App\Models\EnvironmentReview;
use App\Services\Baselines\BaselineSnapshotTruthResolver; use App\Services\Baselines\BaselineSnapshotTruthResolver;
use App\Services\Baselines\SnapshotRendering\FidelityState; use App\Services\Baselines\SnapshotRendering\FidelityState;
use App\Support\Badges\BadgeCatalog; use App\Support\Badges\BadgeCatalog;
use App\Support\Badges\BadgeDomain; use App\Support\Badges\BadgeDomain;
use App\Support\EnvironmentReviewCompletenessState;
use App\Support\EnvironmentReviewStatus;
use App\Support\Evidence\EvidenceCompletenessState; use App\Support\Evidence\EvidenceCompletenessState;
use App\Support\OperationCatalog; use App\Support\OperationCatalog;
use App\Support\OperationRunLinks; use App\Support\OperationRunLinks;
@ -28,8 +30,6 @@
use App\Support\ReasonTranslation\ReasonPresenter; use App\Support\ReasonTranslation\ReasonPresenter;
use App\Support\ReasonTranslation\ReasonResolutionEnvelope; use App\Support\ReasonTranslation\ReasonResolutionEnvelope;
use App\Support\ReviewPackStatus; use App\Support\ReviewPackStatus;
use App\Support\EnvironmentReviewCompletenessState;
use App\Support\EnvironmentReviewStatus;
use App\Support\Ui\DerivedState\DerivedStateFamily; use App\Support\Ui\DerivedState\DerivedStateFamily;
use App\Support\Ui\DerivedState\DerivedStateKey; use App\Support\Ui\DerivedState\DerivedStateKey;
use App\Support\Ui\DerivedState\RequestScopedDerivedStateStore; use App\Support\Ui\DerivedState\RequestScopedDerivedStateStore;
@ -600,7 +600,7 @@ private function buildEnvironmentReviewEnvelope(EnvironmentReview $review): Arti
if ($publishBlockers !== [] && $review->tenant !== null) { if ($publishBlockers !== [] && $review->tenant !== null) {
$nextActionUrl = $this->panelSafeTenantArtifactUrl( $nextActionUrl = $this->panelSafeTenantArtifactUrl(
fn (): string => EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $review], $review->tenant) fn (): string => EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $review], $review->tenant)
); );
} elseif (($freshnessState === 'stale' || $contentState === 'partial') && $review->tenant !== null && $review->evidenceSnapshot !== null) { } elseif (($freshnessState === 'stale' || $contentState === 'partial') && $review->tenant !== null && $review->evidenceSnapshot !== null) {
$nextActionUrl = $this->panelSafeTenantArtifactUrl( $nextActionUrl = $this->panelSafeTenantArtifactUrl(
@ -642,7 +642,7 @@ private function buildEnvironmentReviewEnvelope(EnvironmentReview $review): Arti
relatedRunId: $review->operation_run_id !== null ? (int) $review->operation_run_id : null, relatedRunId: $review->operation_run_id !== null ? (int) $review->operation_run_id : null,
relatedArtifactUrl: $review->tenant !== null relatedArtifactUrl: $review->tenant !== null
? $this->panelSafeTenantArtifactUrl( ? $this->panelSafeTenantArtifactUrl(
fn (): string => EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $review], $review->tenant) fn (): string => EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $review], $review->tenant)
) )
: null, : null,
includePublicationDimension: true, includePublicationDimension: true,
@ -785,7 +785,7 @@ private function buildReviewPackEnvelope(ReviewPack $pack): ArtifactTruthEnvelop
if ($sourceReview instanceof EnvironmentReview && $pack->tenant !== null) { if ($sourceReview instanceof EnvironmentReview && $pack->tenant !== null) {
$nextActionUrl = $this->panelSafeTenantArtifactUrl( $nextActionUrl = $this->panelSafeTenantArtifactUrl(
fn (): string => EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $sourceReview], $pack->tenant) fn (): string => EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $sourceReview], $pack->tenant)
); );
} elseif (($freshnessState === 'stale' || $contentState === 'partial') && $pack->tenant !== null && $pack->evidenceSnapshot !== null) { } elseif (($freshnessState === 'stale' || $contentState === 'partial') && $pack->tenant !== null && $pack->evidenceSnapshot !== null) {
$nextActionUrl = $this->panelSafeTenantArtifactUrl( $nextActionUrl = $this->panelSafeTenantArtifactUrl(

View File

@ -7,6 +7,7 @@
use App\Filament\Resources\BackupScheduleResource; use App\Filament\Resources\BackupScheduleResource;
use App\Filament\Resources\BackupSetResource; use App\Filament\Resources\BackupSetResource;
use App\Filament\Resources\EntraGroupResource; use App\Filament\Resources\EntraGroupResource;
use App\Filament\Resources\EnvironmentReviewResource;
use App\Filament\Resources\EvidenceSnapshotResource; use App\Filament\Resources\EvidenceSnapshotResource;
use App\Filament\Resources\FindingExceptionResource; use App\Filament\Resources\FindingExceptionResource;
use App\Filament\Resources\FindingResource; use App\Filament\Resources\FindingResource;
@ -15,10 +16,10 @@
use App\Filament\Resources\PolicyVersionResource; use App\Filament\Resources\PolicyVersionResource;
use App\Filament\Resources\RestoreRunResource; use App\Filament\Resources\RestoreRunResource;
use App\Filament\Resources\StoredReportResource; use App\Filament\Resources\StoredReportResource;
use App\Filament\Resources\EnvironmentReviewResource;
use App\Models\BackupSchedule; use App\Models\BackupSchedule;
use App\Models\BackupSet; use App\Models\BackupSet;
use App\Models\EntraGroup; use App\Models\EntraGroup;
use App\Models\EnvironmentReview;
use App\Models\EvidenceSnapshot; use App\Models\EvidenceSnapshot;
use App\Models\Finding; use App\Models\Finding;
use App\Models\FindingException; use App\Models\FindingException;
@ -27,7 +28,6 @@
use App\Models\PolicyVersion; use App\Models\PolicyVersion;
use App\Models\RestoreRun; use App\Models\RestoreRun;
use App\Models\StoredReport; use App\Models\StoredReport;
use App\Models\EnvironmentReview;
final class TenantOwnedModelFamilies final class TenantOwnedModelFamilies
{ {
@ -251,7 +251,7 @@ public static function scopeExceptions(): array
'why_excepted' => 'Workspace-admin tenant-default surface referencing tenant-owned data without being part of the mandatory first-slice canon.', 'why_excepted' => 'Workspace-admin tenant-default surface referencing tenant-owned data without being part of the mandatory first-slice canon.',
'still_required_checks' => [ 'still_required_checks' => [
'workspace membership', 'workspace membership',
'remembered tenant entitlement', 'remembered environment entitlement',
'capability gating on the destination action', 'capability gating on the destination action',
], ],
], ],

View File

@ -18,7 +18,7 @@ final class WorkspaceContext
public const INTENDED_URL_SESSION_KEY = 'workspace_intended_url'; public const INTENDED_URL_SESSION_KEY = 'workspace_intended_url';
public const LAST_TENANT_IDS_SESSION_KEY = 'workspace_last_tenant_ids'; public const LAST_ENVIRONMENT_IDS_SESSION_KEY = 'workspace_last_environment_ids';
public function __construct( public function __construct(
private WorkspaceResolver $resolver, private WorkspaceResolver $resolver,
@ -55,7 +55,7 @@ public function currentWorkspace(?Request $request = null): ?Workspace
return $workspace; return $workspace;
} }
public function currentWorkspaceOrTenantWorkspace(?ManagedEnvironment $tenant = null, ?Request $request = null): ?Workspace public function currentWorkspaceOrEnvironmentWorkspace(?ManagedEnvironment $tenant = null, ?Request $request = null): ?Workspace
{ {
$workspace = $this->currentWorkspace($request); $workspace = $this->currentWorkspace($request);
@ -96,19 +96,19 @@ public function setCurrentWorkspace(Workspace $workspace, ?User $user = null, ?R
} }
} }
public function rememberLastTenantId(int $workspaceId, int $tenantId, ?Request $request = null): void public function rememberLastEnvironmentId(int $workspaceId, int $tenantId, ?Request $request = null): void
{ {
$session = ($request && $request->hasSession()) ? $request->session() : session(); $session = ($request && $request->hasSession()) ? $request->session() : session();
$map = $session->get(self::LAST_TENANT_IDS_SESSION_KEY, []); $map = $session->get(self::LAST_ENVIRONMENT_IDS_SESSION_KEY, []);
$map = is_array($map) ? $map : []; $map = is_array($map) ? $map : [];
$map[(string) $workspaceId] = $tenantId; $map[(string) $workspaceId] = $tenantId;
$session->put(self::LAST_TENANT_IDS_SESSION_KEY, $map); $session->put(self::LAST_ENVIRONMENT_IDS_SESSION_KEY, $map);
} }
public function rememberTenantContext(ManagedEnvironment $tenant, ?Request $request = null): bool public function rememberEnvironmentContext(ManagedEnvironment $tenant, ?Request $request = null): bool
{ {
$workspaceId = $this->currentWorkspaceId($request); $workspaceId = $this->currentWorkspaceId($request);
@ -125,23 +125,23 @@ public function rememberTenantContext(ManagedEnvironment $tenant, ?Request $requ
); );
if (! $outcome->allowed) { if (! $outcome->allowed) {
$this->clearLastTenantId($request); $this->clearLastEnvironmentId($request);
return false; return false;
} }
if (! $this->userCanAccessTenant($tenant, $request)) { if (! $this->userCanAccessTenant($tenant, $request)) {
$this->clearRememberedTenantContext($request); $this->clearRememberedEnvironmentContext($request);
return false; return false;
} }
$this->rememberLastTenantId($workspaceId, (int) $tenant->getKey(), $request); $this->rememberLastEnvironmentId($workspaceId, (int) $tenant->getKey(), $request);
return true; return true;
} }
public function lastTenantId(?Request $request = null): ?int public function lastEnvironmentId(?Request $request = null): ?int
{ {
$workspaceId = $this->currentWorkspaceId($request); $workspaceId = $this->currentWorkspaceId($request);
@ -151,7 +151,7 @@ public function lastTenantId(?Request $request = null): ?int
$session = ($request && $request->hasSession()) ? $request->session() : session(); $session = ($request && $request->hasSession()) ? $request->session() : session();
$map = $session->get(self::LAST_TENANT_IDS_SESSION_KEY, []); $map = $session->get(self::LAST_ENVIRONMENT_IDS_SESSION_KEY, []);
$map = is_array($map) ? $map : []; $map = is_array($map) ? $map : [];
$id = $map[(string) $workspaceId] ?? null; $id = $map[(string) $workspaceId] ?? null;
@ -159,7 +159,7 @@ public function lastTenantId(?Request $request = null): ?int
return is_int($id) ? $id : (is_numeric($id) ? (int) $id : null); return is_int($id) ? $id : (is_numeric($id) ? (int) $id : null);
} }
public function clearLastTenantId(?Request $request = null): void public function clearLastEnvironmentId(?Request $request = null): void
{ {
$workspaceId = $this->currentWorkspaceId($request); $workspaceId = $this->currentWorkspaceId($request);
@ -169,20 +169,20 @@ public function clearLastTenantId(?Request $request = null): void
$session = ($request && $request->hasSession()) ? $request->session() : session(); $session = ($request && $request->hasSession()) ? $request->session() : session();
$map = $session->get(self::LAST_TENANT_IDS_SESSION_KEY, []); $map = $session->get(self::LAST_ENVIRONMENT_IDS_SESSION_KEY, []);
$map = is_array($map) ? $map : []; $map = is_array($map) ? $map : [];
unset($map[(string) $workspaceId]); unset($map[(string) $workspaceId]);
$session->put(self::LAST_TENANT_IDS_SESSION_KEY, $map); $session->put(self::LAST_ENVIRONMENT_IDS_SESSION_KEY, $map);
} }
public function clearRememberedTenantContext(?Request $request = null): void public function clearRememberedEnvironmentContext(?Request $request = null): void
{ {
$this->clearLastTenantId($request); $this->clearLastEnvironmentId($request);
} }
public function rememberedTenant(?Request $request = null): ?ManagedEnvironment public function rememberedEnvironment(?Request $request = null): ?ManagedEnvironment
{ {
$workspaceId = $this->currentWorkspaceId($request); $workspaceId = $this->currentWorkspaceId($request);
@ -190,31 +190,31 @@ public function rememberedTenant(?Request $request = null): ?ManagedEnvironment
return null; return null;
} }
$rememberedTenantId = $this->lastTenantId($request); $rememberedEnvironmentId = $this->lastEnvironmentId($request);
if ($rememberedTenantId === null) { if ($rememberedEnvironmentId === null) {
return null; return null;
} }
$tenant = ManagedEnvironment::query() $tenant = ManagedEnvironment::query()
->withTrashed() ->withTrashed()
->whereKey($rememberedTenantId) ->whereKey($rememberedEnvironmentId)
->first(); ->first();
if (! $tenant instanceof ManagedEnvironment) { if (! $tenant instanceof ManagedEnvironment) {
$this->clearRememberedTenantContext($request); $this->clearRememberedEnvironmentContext($request);
return null; return null;
} }
if ((int) $tenant->workspace_id !== $workspaceId) { if ((int) $tenant->workspace_id !== $workspaceId) {
$this->clearRememberedTenantContext($request); $this->clearRememberedEnvironmentContext($request);
return null; return null;
} }
if (! $this->userCanAccessTenant($tenant, $request)) { if (! $this->userCanAccessTenant($tenant, $request)) {
$this->clearRememberedTenantContext($request); $this->clearRememberedEnvironmentContext($request);
return null; return null;
} }
@ -228,7 +228,7 @@ public function rememberedTenant(?Request $request = null): ?ManagedEnvironment
); );
if (! $outcome->allowed) { if (! $outcome->allowed) {
$this->clearRememberedTenantContext($request); $this->clearRememberedEnvironmentContext($request);
return null; return null;
} }

View File

@ -1,12 +1,11 @@
<?php <?php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
use App\Http\Middleware\ApplyResolvedLocale; use App\Http\Middleware\ApplyResolvedLocale;
use App\Http\Middleware\SuppressDebugbarForSmokeRequests; use App\Http\Middleware\SuppressDebugbarForSmokeRequests;
use App\Http\Middleware\UseSystemSessionCookieForLivewireRequests; use App\Http\Middleware\UseSystemSessionCookieForLivewireRequests;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__)) return Application::configure(basePath: dirname(__DIR__))
->withRouting( ->withRouting(
@ -35,7 +34,7 @@
'ensure-platform-capability' => \App\Http\Middleware\EnsurePlatformCapability::class, 'ensure-platform-capability' => \App\Http\Middleware\EnsurePlatformCapability::class,
'ensure-workspace-member' => \App\Http\Middleware\EnsureWorkspaceMember::class, 'ensure-workspace-member' => \App\Http\Middleware\EnsureWorkspaceMember::class,
'ensure-workspace-selected' => \App\Http\Middleware\EnsureWorkspaceSelected::class, 'ensure-workspace-selected' => \App\Http\Middleware\EnsureWorkspaceSelected::class,
'ensure-filament-tenant-selected' => \App\Support\Middleware\EnsureFilamentTenantSelected::class, 'ensure-environment-context-selected' => \App\Support\Middleware\EnsureEnvironmentContextSelected::class,
]); ]);
$middleware->prependToPriorityList( $middleware->prependToPriorityList(

View File

@ -15,32 +15,32 @@
$user = auth()->user(); $user = auth()->user();
$tenants = collect(); $environments = collect();
if ($user instanceof User && $workspace) { if ($user instanceof User && $workspace) {
$tenants = collect($user->getTenants(Filament::getCurrentOrDefaultPanel())) $environments = collect($user->getTenants(Filament::getCurrentOrDefaultPanel()))
->filter(fn ($tenant): bool => $tenant instanceof ManagedEnvironment && (int) $tenant->workspace_id === (int) $workspace->getKey()) ->filter(fn ($environment): bool => $environment instanceof ManagedEnvironment && (int) $environment->workspace_id === (int) $workspace->getKey())
->values(); ->values();
} }
$currentTenant = $resolvedContext->tenant; $currentEnvironment = $resolvedContext->tenant;
$currentTenantId = $currentTenant instanceof ManagedEnvironment ? (int) $currentTenant->getKey() : null; $currentEnvironmentId = $currentEnvironment instanceof ManagedEnvironment ? (int) $currentEnvironment->getKey() : null;
$currentTenantName = $currentTenant instanceof ManagedEnvironment ? $currentTenant->getFilamentName() : null; $currentEnvironmentName = $currentEnvironment instanceof ManagedEnvironment ? $currentEnvironment->getFilamentName() : null;
$lastTenantId = $workspaceContext->lastTenantId(request()); $lastEnvironmentId = $workspaceContext->lastEnvironmentId(request());
$canClearEnvironmentContext = $currentTenant instanceof ManagedEnvironment || $lastTenantId !== null; $canClearEnvironmentContext = $currentEnvironment instanceof ManagedEnvironment || $lastEnvironmentId !== null;
@endphp @endphp
@php @php
$tenantLabel = $currentTenantName ?? __('localization.shell.no_environment_selected'); $environmentLabel = $currentEnvironmentName ?? __('localization.shell.no_environment_selected');
$workspaceLabel = $workspace?->name ?? __('localization.shell.choose_workspace'); $workspaceLabel = $workspace?->name ?? __('localization.shell.choose_workspace');
$hasActiveTenant = $currentTenantName !== null; $hasActiveEnvironment = $currentEnvironmentName !== null;
$managedEnvironmentsUrl = $workspace $managedEnvironmentsUrl = $workspace
? route('admin.workspace.managed-environments.index', ['workspace' => $workspace]) ? route('admin.workspace.managed-environments.index', ['workspace' => $workspace])
: route('admin.onboarding'); : route('admin.onboarding');
$workspaceUrl = $workspace $workspaceUrl = $workspace
? route('admin.home') ? route('admin.home')
: ChooseWorkspace::getUrl(panel: 'admin'); : ChooseWorkspace::getUrl(panel: 'admin');
$tenantTriggerLabel = $workspace ? $tenantLabel : __('localization.shell.choose_workspace'); $environmentTriggerLabel = $workspace ? $environmentLabel : __('localization.shell.choose_workspace');
$localePlane = 'admin'; $localePlane = 'admin';
@endphp @endphp
@ -59,7 +59,7 @@ class="inline-flex items-center gap-1.5 rounded-l-lg px-2.5 py-1.5 font-medium t
<x-filament::icon icon="heroicon-m-chevron-right" class="h-3 w-3 shrink-0 text-gray-300 dark:text-gray-600" /> <x-filament::icon icon="heroicon-m-chevron-right" class="h-3 w-3 shrink-0 text-gray-300 dark:text-gray-600" />
@endif @endif
{{-- Dropdown trigger: tenant label + chevron --}} {{-- Dropdown trigger: environment label + chevron --}}
<x-filament::dropdown placement="bottom-start" teleport width="xs"> <x-filament::dropdown placement="bottom-start" teleport width="xs">
<x-slot name="trigger"> <x-slot name="trigger">
<button <button
@ -67,8 +67,8 @@ class="inline-flex items-center gap-1.5 rounded-l-lg px-2.5 py-1.5 font-medium t
aria-label="{{ $workspace ? __('localization.shell.environment_scope') : __('localization.shell.select_environment') }}" aria-label="{{ $workspace ? __('localization.shell.environment_scope') : __('localization.shell.select_environment') }}"
class="inline-flex items-center gap-1.5 rounded-r-lg px-2 py-1.5 transition hover:bg-gray-50 dark:hover:bg-white/10" class="inline-flex items-center gap-1.5 rounded-r-lg px-2 py-1.5 transition hover:bg-gray-50 dark:hover:bg-white/10"
> >
<span class="{{ $workspace && $hasActiveTenant ? 'font-medium text-primary-600 dark:text-primary-400' : 'text-gray-500 dark:text-gray-400' }}"> <span class="{{ $workspace && $hasActiveEnvironment ? 'font-medium text-primary-600 dark:text-primary-400' : 'text-gray-500 dark:text-gray-400' }}">
{{ $tenantTriggerLabel }} {{ $environmentTriggerLabel }}
</span> </span>
<x-filament::icon icon="heroicon-m-chevron-down" class="h-3.5 w-3.5 text-gray-400 dark:text-gray-500" /> <x-filament::icon icon="heroicon-m-chevron-down" class="h-3.5 w-3.5 text-gray-400 dark:text-gray-500" />
@ -121,7 +121,7 @@ class="flex items-center gap-2 rounded-lg px-3 py-2 text-sm text-gray-700 transi
@if ($workspace) @if ($workspace)
<div class="border-t border-gray-200 dark:border-white/10"></div> <div class="border-t border-gray-200 dark:border-white/10"></div>
{{-- ManagedEnvironment section --}} {{-- Managed Environment section --}}
<div class="space-y-2"> <div class="space-y-2">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div class="text-xs font-semibold uppercase tracking-wider text-gray-400 dark:text-gray-500"> <div class="text-xs font-semibold uppercase tracking-wider text-gray-400 dark:text-gray-500">
@ -129,11 +129,11 @@ class="flex items-center gap-2 rounded-lg px-3 py-2 text-sm text-gray-700 transi
</div> </div>
</div> </div>
@if ($resolvedContext->pageCategory->requiresExplicitTenant() && $hasActiveTenant) @if ($resolvedContext->pageCategory->requiresExplicitEnvironment() && $hasActiveEnvironment)
<div class="space-y-2"> <div class="space-y-2">
<div class="flex items-center gap-2 rounded-lg bg-primary-50 px-3 py-2 dark:bg-primary-950/30"> <div class="flex items-center gap-2 rounded-lg bg-primary-50 px-3 py-2 dark:bg-primary-950/30">
<x-filament::icon icon="heroicon-o-building-office-2" class="h-4 w-4 text-primary-600 dark:text-primary-400" /> <x-filament::icon icon="heroicon-o-building-office-2" class="h-4 w-4 text-primary-600 dark:text-primary-400" />
<span class="text-sm font-medium text-primary-700 dark:text-primary-300">{{ $currentTenantName }}</span> <span class="text-sm font-medium text-primary-700 dark:text-primary-300">{{ $currentEnvironmentName }}</span>
<a <a
href="{{ ChooseEnvironment::getUrl(panel: 'admin') }}" href="{{ ChooseEnvironment::getUrl(panel: 'admin') }}"
class="ml-auto text-xs font-medium text-primary-600 hover:text-primary-500 dark:text-primary-400 dark:hover:text-primary-300" class="ml-auto text-xs font-medium text-primary-600 hover:text-primary-500 dark:text-primary-400 dark:hover:text-primary-300"
@ -153,7 +153,7 @@ class="ml-auto text-xs font-medium text-primary-600 hover:text-primary-500 dark:
@endif @endif
</div> </div>
@else @else
@if ($tenants->isEmpty()) @if ($environments->isEmpty())
<div class="space-y-2 rounded-lg border border-dashed border-gray-300 px-3 py-3 text-center text-xs text-gray-500 dark:border-gray-600 dark:text-gray-400"> <div class="space-y-2 rounded-lg border border-dashed border-gray-300 px-3 py-3 text-center text-xs text-gray-500 dark:border-gray-600 dark:text-gray-400">
<div>{{ __('localization.shell.no_active_environments') }}</div> <div>{{ __('localization.shell.no_active_environments') }}</div>
<a href="{{ $managedEnvironmentsUrl }}" class="inline-flex items-center gap-1 text-primary-600 hover:text-primary-500 dark:text-primary-400 dark:hover:text-primary-300"> <a href="{{ $managedEnvironmentsUrl }}" class="inline-flex items-center gap-1 text-primary-600 hover:text-primary-500 dark:text-primary-400 dark:hover:text-primary-300">
@ -162,7 +162,7 @@ class="ml-auto text-xs font-medium text-primary-600 hover:text-primary-500 dark:
</a> </a>
</div> </div>
@else @else
@if (! $hasActiveTenant) @if (! $hasActiveEnvironment)
<div class="rounded-lg bg-gray-50 px-3 py-2 text-xs text-gray-600 dark:bg-white/5 dark:text-gray-400"> <div class="rounded-lg bg-gray-50 px-3 py-2 text-xs text-gray-600 dark:bg-white/5 dark:text-gray-400">
{{ __('localization.shell.workspace_wide_available_without_environment') }} {{ __('localization.shell.workspace_wide_available_without_environment') }}
</div> </div>
@ -176,19 +176,19 @@ class="fi-input fi-text-input w-full"
/> />
<div class="max-h-48 overflow-auto rounded-lg border border-gray-200 dark:border-gray-700"> <div class="max-h-48 overflow-auto rounded-lg border border-gray-200 dark:border-gray-700">
@foreach ($tenants as $tenant) @foreach ($environments as $environment)
@php @php
$isActive = $currentTenantId !== null && (int) $tenant->getKey() === $currentTenantId; $isActive = $currentEnvironmentId !== null && (int) $environment->getKey() === $currentEnvironmentId;
@endphp @endphp
<form method="POST" action="{{ route('admin.select-environment') }}"> <form method="POST" action="{{ route('admin.select-environment') }}">
@csrf @csrf
<input type="hidden" name="managed_environment_id" value="{{ (int) $tenant->getKey() }}" /> <input type="hidden" name="managed_environment_id" value="{{ (int) $environment->getKey() }}" />
<button <button
type="submit" type="submit"
class="flex w-full items-center gap-2 px-3 py-2 text-left text-sm transition {{ $isActive ? 'bg-primary-50 font-medium text-primary-700 dark:bg-primary-950/30 dark:text-primary-300' : 'text-gray-700 hover:bg-gray-50 dark:text-gray-300 dark:hover:bg-white/5' }}" class="flex w-full items-center gap-2 px-3 py-2 text-left text-sm transition {{ $isActive ? 'bg-primary-50 font-medium text-primary-700 dark:bg-primary-950/30 dark:text-primary-300' : 'text-gray-700 hover:bg-gray-50 dark:text-gray-300 dark:hover:bg-white/5' }}"
data-search="{{ (string) str($tenant->getFilamentName())->lower() }}" data-search="{{ (string) str($environment->getFilamentName())->lower() }}"
x-show="query === '' || ($el.dataset.search ?? '').includes(query.toLowerCase())" x-show="query === '' || ($el.dataset.search ?? '').includes(query.toLowerCase())"
> >
@if ($isActive) @if ($isActive)
@ -197,7 +197,7 @@ class="flex w-full items-center gap-2 px-3 py-2 text-left text-sm transition {{
<x-filament::icon icon="heroicon-o-building-office-2" class="h-4 w-4 shrink-0 text-gray-400 dark:text-gray-500" /> <x-filament::icon icon="heroicon-o-building-office-2" class="h-4 w-4 shrink-0 text-gray-400 dark:text-gray-500" />
@endif @endif
{{ $tenant->getFilamentName() }} {{ $environment->getFilamentName() }}
</button> </button>
</form> </form>
@endforeach @endforeach

View File

@ -5,24 +5,23 @@
use App\Http\Controllers\Auth\EntraController; use App\Http\Controllers\Auth\EntraController;
use App\Http\Controllers\ClearEnvironmentContextController; use App\Http\Controllers\ClearEnvironmentContextController;
use App\Http\Controllers\LocalizationController; use App\Http\Controllers\LocalizationController;
use App\Http\Controllers\ManagedEnvironmentOnboardingController;
use App\Http\Controllers\OpenFindingExceptionsQueueController; use App\Http\Controllers\OpenFindingExceptionsQueueController;
use App\Http\Controllers\RbacDelegatedAuthController; use App\Http\Controllers\RbacDelegatedAuthController;
use App\Http\Controllers\ReviewPackDownloadController; use App\Http\Controllers\ReviewPackDownloadController;
use App\Http\Controllers\SelectEnvironmentController; use App\Http\Controllers\SelectEnvironmentController;
use App\Http\Controllers\SwitchWorkspaceController; use App\Http\Controllers\SwitchWorkspaceController;
use App\Http\Controllers\ManagedEnvironmentOnboardingController;
use App\Http\Middleware\SuppressDebugbarForSmokeRequests; use App\Http\Middleware\SuppressDebugbarForSmokeRequests;
use App\Models\ProviderConnection;
use App\Models\ManagedEnvironment; use App\Models\ManagedEnvironment;
use App\Models\ManagedEnvironmentOnboardingSession; use App\Models\ManagedEnvironmentOnboardingSession;
use App\Models\User; use App\Models\User;
use App\Models\Workspace; use App\Models\Workspace;
use App\Services\Onboarding\OnboardingDraftResolver; use App\Services\Onboarding\OnboardingDraftResolver;
use App\Services\Tenants\TenantOperabilityService;
use App\Support\Auth\WorkspaceRole; use App\Support\Auth\WorkspaceRole;
use App\Support\ManagedEnvironmentLinks; use App\Support\ManagedEnvironmentLinks;
use App\Services\Tenants\TenantOperabilityService; use App\Support\Navigation\AdminSurfaceScope;
use App\Support\Tenants\TenantOperabilityQuestion; use App\Support\Tenants\TenantOperabilityQuestion;
use App\Support\Tenants\TenantPageCategory;
use App\Support\Workspaces\WorkspaceContext; use App\Support\Workspaces\WorkspaceContext;
use App\Support\Workspaces\WorkspaceResolver; use App\Support\Workspaces\WorkspaceResolver;
use Filament\Http\Middleware\Authenticate as FilamentAuthenticate; use Filament\Http\Middleware\Authenticate as FilamentAuthenticate;
@ -268,7 +267,7 @@
$workspaceContext->setCurrentWorkspace($workspace, $user, $request); $workspaceContext->setCurrentWorkspace($workspace, $user, $request);
if ($tenant instanceof ManagedEnvironment) { if ($tenant instanceof ManagedEnvironment) {
$workspaceContext->rememberTenantContext($tenant, $request); $workspaceContext->rememberEnvironmentContext($tenant, $request);
} else { } else {
$workspaceContext->clearRememberedTenantContext($request); $workspaceContext->clearRememberedTenantContext($request);
} }
@ -377,10 +376,10 @@
$allowed = app(TenantOperabilityService::class)->outcomeFor( $allowed = app(TenantOperabilityService::class)->outcomeFor(
tenant: $tenant, tenant: $tenant,
question: TenantOperabilityQuestion::TenantBoundViewability, question: TenantOperabilityQuestion::EnvironmentBoundViewability,
actor: $user, actor: $user,
workspaceId: $workspaceId, workspaceId: $workspaceId,
lane: TenantPageCategory::TenantBound->lane(), lane: AdminSurfaceScope::EnvironmentBound->lane(),
)->allowed; )->allowed;
abort_unless($allowed, 404); abort_unless($allowed, 404);
@ -394,7 +393,7 @@
DispatchServingFilamentEvent::class, DispatchServingFilamentEvent::class,
FilamentAuthenticate::class, FilamentAuthenticate::class,
'ensure-workspace-member', 'ensure-workspace-member',
'ensure-filament-tenant-selected', 'ensure-environment-context-selected',
]) ])
->prefix('/admin/workspaces/{workspace}') ->prefix('/admin/workspaces/{workspace}')
->group(function (): void { ->group(function (): void {
@ -470,7 +469,7 @@
DispatchServingFilamentEvent::class, DispatchServingFilamentEvent::class,
FilamentAuthenticate::class, FilamentAuthenticate::class,
'ensure-workspace-selected', 'ensure-workspace-selected',
'ensure-filament-tenant-selected', 'ensure-environment-context-selected',
]) ])
->get('/admin/audit-log', \App\Filament\Pages\Monitoring\AuditLog::class) ->get('/admin/audit-log', \App\Filament\Pages\Monitoring\AuditLog::class)
->name('admin.monitoring.audit-log'); ->name('admin.monitoring.audit-log');
@ -507,7 +506,7 @@
DispatchServingFilamentEvent::class, DispatchServingFilamentEvent::class,
FilamentAuthenticate::class, FilamentAuthenticate::class,
'ensure-workspace-member', 'ensure-workspace-member',
'ensure-filament-tenant-selected', 'ensure-environment-context-selected',
]) ])
->get('/admin/workspaces/{workspace}/environments/{environment:slug}', \App\Filament\Pages\EnvironmentDashboard::class) ->get('/admin/workspaces/{workspace}/environments/{environment:slug}', \App\Filament\Pages\EnvironmentDashboard::class)
->name('admin.workspace.environments.show'); ->name('admin.workspace.environments.show');
@ -520,7 +519,7 @@
DispatchServingFilamentEvent::class, DispatchServingFilamentEvent::class,
FilamentAuthenticate::class, FilamentAuthenticate::class,
'ensure-workspace-member', 'ensure-workspace-member',
'ensure-filament-tenant-selected', 'ensure-environment-context-selected',
]) ])
->get('/admin/workspaces/{workspace}/environments/{environment:slug}/diagnostics', \App\Filament\Pages\EnvironmentDiagnostics::class) ->get('/admin/workspaces/{workspace}/environments/{environment:slug}/diagnostics', \App\Filament\Pages\EnvironmentDiagnostics::class)
->name('admin.workspace.environments.diagnostics'); ->name('admin.workspace.environments.diagnostics');
@ -533,7 +532,7 @@
DispatchServingFilamentEvent::class, DispatchServingFilamentEvent::class,
FilamentAuthenticate::class, FilamentAuthenticate::class,
'ensure-workspace-member', 'ensure-workspace-member',
'ensure-filament-tenant-selected', 'ensure-environment-context-selected',
]) ])
->get('/admin/workspaces/{workspace}/environments/{environment:slug}/access-scopes', \App\Filament\Resources\ManagedEnvironmentResource\Pages\ManageEnvironmentAccessScopes::class) ->get('/admin/workspaces/{workspace}/environments/{environment:slug}/access-scopes', \App\Filament\Resources\ManagedEnvironmentResource\Pages\ManageEnvironmentAccessScopes::class)
->name('admin.workspace.environments.access-scopes'); ->name('admin.workspace.environments.access-scopes');

View File

@ -59,7 +59,7 @@
$this->actingAs($user)->withSession([ $this->actingAs($user)->withSession([
WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id, WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id,
WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY => [ WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
(string) $tenant->workspace_id => (int) $tenant->getKey(), (string) $tenant->workspace_id => (int) $tenant->getKey(),
], ],
]); ]);

View File

@ -72,12 +72,12 @@
$this->actingAs($user)->withSession([ $this->actingAs($user)->withSession([
WorkspaceContext::SESSION_KEY => (int) $tenantPublished->workspace_id, WorkspaceContext::SESSION_KEY => (int) $tenantPublished->workspace_id,
WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY => [ WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
(string) $tenantPublished->workspace_id => (int) $tenantPublished->getKey(), (string) $tenantPublished->workspace_id => (int) $tenantPublished->getKey(),
], ],
]); ]);
visit(EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $publishedReview], $tenantPublished)) visit(EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $publishedReview], $tenantPublished))
->waitForText('Verwandter Kontext') ->waitForText('Verwandter Kontext')
->assertSee('Kunden-Workspace öffnen') ->assertSee('Kunden-Workspace öffnen')
->assertNoJavaScriptErrors() ->assertNoJavaScriptErrors()

View File

@ -2,9 +2,9 @@
declare(strict_types=1); declare(strict_types=1);
use App\Filament\Resources\EnvironmentReviewResource;
use App\Filament\Resources\EvidenceSnapshotResource; use App\Filament\Resources\EvidenceSnapshotResource;
use App\Filament\Resources\ReviewPackResource; use App\Filament\Resources\ReviewPackResource;
use App\Filament\Resources\EnvironmentReviewResource;
use App\Models\ManagedEnvironment; use App\Models\ManagedEnvironment;
use App\Support\EnvironmentReviewCompletenessState; use App\Support\EnvironmentReviewCompletenessState;
use App\Support\EnvironmentReviewStatus; use App\Support\EnvironmentReviewStatus;
@ -90,7 +90,7 @@
->assertSee('Stale') ->assertSee('Stale')
->assertSee('Refresh the stale evidence before relying on this snapshot'); ->assertSee('Refresh the stale evidence before relying on this snapshot');
visit(EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $partialReview], $partialTenant)) visit(EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $partialReview], $partialTenant))
->waitForText('Outcome summary') ->waitForText('Outcome summary')
->assertNoJavaScriptErrors() ->assertNoJavaScriptErrors()
->assertSee('Internal only') ->assertSee('Internal only')

View File

@ -38,7 +38,7 @@
$this->actingAs($fixture['user'])->withSession([ $this->actingAs($fixture['user'])->withSession([
WorkspaceContext::SESSION_KEY => (int) $fixture['workspace']->getKey(), WorkspaceContext::SESSION_KEY => (int) $fixture['workspace']->getKey(),
WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY => [ WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
(string) $fixture['workspace']->getKey() => (int) $fixture['visibleTenant']->getKey(), (string) $fixture['workspace']->getKey() => (int) $fixture['visibleTenant']->getKey(),
], ],
]); ]);
@ -97,7 +97,7 @@
$this->actingAs($viewer)->withSession([ $this->actingAs($viewer)->withSession([
WorkspaceContext::SESSION_KEY => (int) $fixture['workspace']->getKey(), WorkspaceContext::SESSION_KEY => (int) $fixture['workspace']->getKey(),
WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY => [ WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
(string) $fixture['workspace']->getKey() => (int) $fixture['visibleTenant']->getKey(), (string) $fixture['workspace']->getKey() => (int) $fixture['visibleTenant']->getKey(),
], ],
]); ]);
@ -134,7 +134,7 @@
$this->actingAs($fixture['user'])->withSession([ $this->actingAs($fixture['user'])->withSession([
WorkspaceContext::SESSION_KEY => (int) $fixture['workspace']->getKey(), WorkspaceContext::SESSION_KEY => (int) $fixture['workspace']->getKey(),
WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY => [ WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
(string) $fixture['workspace']->getKey() => (int) $fixture['visibleTenant']->getKey(), (string) $fixture['workspace']->getKey() => (int) $fixture['visibleTenant']->getKey(),
], ],
]); ]);

View File

@ -6,12 +6,12 @@
use App\Filament\Resources\BackupSetResource; use App\Filament\Resources\BackupSetResource;
use App\Filament\Resources\BaselineProfileResource; use App\Filament\Resources\BaselineProfileResource;
use App\Filament\Resources\BaselineSnapshotResource; use App\Filament\Resources\BaselineSnapshotResource;
use App\Filament\Resources\EnvironmentReviewResource;
use App\Filament\Resources\EvidenceSnapshotResource; use App\Filament\Resources\EvidenceSnapshotResource;
use App\Filament\Resources\FindingExceptionResource; use App\Filament\Resources\FindingExceptionResource;
use App\Filament\Resources\ManagedEnvironmentResource;
use App\Filament\Resources\PolicyVersionResource; use App\Filament\Resources\PolicyVersionResource;
use App\Filament\Resources\ReviewPackResource; use App\Filament\Resources\ReviewPackResource;
use App\Filament\Resources\ManagedEnvironmentResource;
use App\Filament\Resources\EnvironmentReviewResource;
use App\Filament\Resources\Workspaces\WorkspaceResource; use App\Filament\Resources\Workspaces\WorkspaceResource;
use App\Models\AlertDestination; use App\Models\AlertDestination;
use App\Models\BackupSet; use App\Models\BackupSet;
@ -20,11 +20,11 @@
use App\Models\BaselineSnapshotItem; use App\Models\BaselineSnapshotItem;
use App\Models\EvidenceSnapshot; use App\Models\EvidenceSnapshot;
use App\Models\Finding; use App\Models\Finding;
use App\Models\ManagedEnvironment;
use App\Models\OperationRun; use App\Models\OperationRun;
use App\Models\Policy; use App\Models\Policy;
use App\Models\PolicyVersion; use App\Models\PolicyVersion;
use App\Models\ReviewPack; use App\Models\ReviewPack;
use App\Models\ManagedEnvironment;
use App\Models\User; use App\Models\User;
use App\Services\Findings\FindingExceptionService; use App\Services\Findings\FindingExceptionService;
use App\Support\Evidence\EvidenceCompletenessState; use App\Support\Evidence\EvidenceCompletenessState;
@ -157,7 +157,7 @@ function spec192ApprovedFindingException(ManagedEnvironment $tenant, User $reque
->assertSee('Open finding') ->assertSee('Open finding')
->assertSee('Renew exception'); ->assertSee('Renew exception');
visit(EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $review], $tenant)) visit(EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $review], $tenant))
->waitForText('Related context') ->waitForText('Related context')
->assertNoJavaScriptErrors() ->assertNoJavaScriptErrors()
->assertScript("document.querySelectorAll('[data-supporting-group-kind]').length === 0", true) ->assertScript("document.querySelectorAll('[data-supporting-group-kind]').length === 0", true)

View File

@ -3,10 +3,10 @@
declare(strict_types=1); declare(strict_types=1);
use App\Filament\Pages\Monitoring\FindingExceptionsQueue; use App\Filament\Pages\Monitoring\FindingExceptionsQueue;
use App\Filament\Resources\EnvironmentReviewResource;
use App\Filament\Resources\EvidenceSnapshotResource; use App\Filament\Resources\EvidenceSnapshotResource;
use App\Filament\Resources\FindingExceptionResource; use App\Filament\Resources\FindingExceptionResource;
use App\Filament\Resources\ManagedEnvironmentResource; use App\Filament\Resources\ManagedEnvironmentResource;
use App\Filament\Resources\EnvironmentReviewResource;
use App\Models\EvidenceSnapshot; use App\Models\EvidenceSnapshot;
use App\Models\Finding; use App\Models\Finding;
use App\Models\FindingException; use App\Models\FindingException;
@ -17,13 +17,13 @@
use App\Models\User; use App\Models\User;
use App\Services\Findings\FindingExceptionService; use App\Services\Findings\FindingExceptionService;
use App\Support\Auth\PlatformCapabilities; use App\Support\Auth\PlatformCapabilities;
use App\Support\EnvironmentReviewCompletenessState;
use App\Support\EnvironmentReviewStatus;
use App\Support\Evidence\EvidenceCompletenessState; use App\Support\Evidence\EvidenceCompletenessState;
use App\Support\Evidence\EvidenceSnapshotStatus; use App\Support\Evidence\EvidenceSnapshotStatus;
use App\Support\OperationRunOutcome; use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus; use App\Support\OperationRunStatus;
use App\Support\System\SystemOperationRunLinks; use App\Support\System\SystemOperationRunLinks;
use App\Support\EnvironmentReviewCompletenessState;
use App\Support\EnvironmentReviewStatus;
use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\RefreshDatabase;
pest()->browser()->timeout(20_000); pest()->browser()->timeout(20_000);
@ -194,7 +194,7 @@ function spec194RelativeRedirect(string $redirect): string
->assertSee('Renew exception') ->assertSee('Renew exception')
->assertSee('Revoke exception'); ->assertSee('Revoke exception');
visit(EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $review], $tenant)) visit(EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $review], $tenant))
->waitForText('Related context') ->waitForText('Related context')
->assertNoJavaScriptErrors() ->assertNoJavaScriptErrors()
->assertNoConsoleLogs() ->assertNoConsoleLogs()

View File

@ -88,7 +88,7 @@
$this->actingAs($user)->withSession([ $this->actingAs($user)->withSession([
WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id, WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id,
WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY => [ WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
(string) $tenant->workspace_id => (int) $tenant->getKey(), (string) $tenant->workspace_id => (int) $tenant->getKey(),
], ],
]); ]);
@ -154,7 +154,7 @@
$this->actingAs($user)->withSession([ $this->actingAs($user)->withSession([
WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id, WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id,
WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY => [ WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
(string) $tenant->workspace_id => (int) $tenant->getKey(), (string) $tenant->workspace_id => (int) $tenant->getKey(),
], ],
]); ]);

View File

@ -36,7 +36,7 @@
$this->actingAs($user)->withSession([ $this->actingAs($user)->withSession([
WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id, WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id,
WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY => [ WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
(string) $tenant->workspace_id => (int) $tenant->getKey(), (string) $tenant->workspace_id => (int) $tenant->getKey(),
], ],
]); ]);

View File

@ -51,7 +51,7 @@
$this->actingAs($user)->withSession([ $this->actingAs($user)->withSession([
WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id, WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id,
WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY => [ WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
(string) $tenant->workspace_id => (int) $tenant->getKey(), (string) $tenant->workspace_id => (int) $tenant->getKey(),
], ],
]); ]);

View File

@ -15,7 +15,7 @@
$this->actingAs($user)->withSession([ $this->actingAs($user)->withSession([
WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id, WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id,
WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY => [ WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
(string) $tenant->workspace_id => (int) $tenant->getKey(), (string) $tenant->workspace_id => (int) $tenant->getKey(),
], ],
]); ]);

View File

@ -2,11 +2,11 @@
declare(strict_types=1); declare(strict_types=1);
use App\Filament\Resources\EnvironmentReviewResource;
use App\Filament\Resources\EvidenceSnapshotResource; use App\Filament\Resources\EvidenceSnapshotResource;
use App\Filament\Resources\FindingResource; use App\Filament\Resources\FindingResource;
use App\Filament\Resources\InventoryItemResource; use App\Filament\Resources\InventoryItemResource;
use App\Filament\Resources\StoredReportResource; use App\Filament\Resources\StoredReportResource;
use App\Filament\Resources\EnvironmentReviewResource;
use App\Models\Finding; use App\Models\Finding;
use App\Models\InventoryItem; use App\Models\InventoryItem;
use App\Models\StoredReport; use App\Models\StoredReport;
@ -41,7 +41,7 @@
$this->actingAs($user)->withSession([ $this->actingAs($user)->withSession([
WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id, WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id,
WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY => [ WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
(string) $tenant->workspace_id => (int) $tenant->getKey(), (string) $tenant->workspace_id => (int) $tenant->getKey(),
], ],
]); ]);

View File

@ -50,7 +50,7 @@
$this->actingAs($user)->withSession([ $this->actingAs($user)->withSession([
WorkspaceContext::SESSION_KEY => (int) $workspace->getKey(), WorkspaceContext::SESSION_KEY => (int) $workspace->getKey(),
WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY => [ WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
(string) $workspace->getKey() => (int) $environment->getKey(), (string) $workspace->getKey() => (int) $environment->getKey(),
], ],
]); ]);

View File

@ -20,13 +20,13 @@
$this->actingAs($user)->withSession([ $this->actingAs($user)->withSession([
WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id, WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id,
WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY => [ WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
(string) $tenant->workspace_id => (int) $tenant->getKey(), (string) $tenant->workspace_id => (int) $tenant->getKey(),
], ],
]); ]);
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id); session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
session()->put(WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY, [ session()->put(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY, [
(string) $tenant->workspace_id => (int) $tenant->getKey(), (string) $tenant->workspace_id => (int) $tenant->getKey(),
]); ]);

View File

@ -48,7 +48,7 @@
$this->actingAs($user)->withSession([ $this->actingAs($user)->withSession([
WorkspaceContext::SESSION_KEY => (int) $environment->workspace_id, WorkspaceContext::SESSION_KEY => (int) $environment->workspace_id,
WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY => [ WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
(string) $environment->workspace_id => (int) $environment->getKey(), (string) $environment->workspace_id => (int) $environment->getKey(),
], ],
]); ]);

View File

@ -24,13 +24,13 @@
$this->actingAs($user)->withSession([ $this->actingAs($user)->withSession([
WorkspaceContext::SESSION_KEY => (int) $workspace->getKey(), WorkspaceContext::SESSION_KEY => (int) $workspace->getKey(),
WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY => [ WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
(string) $workspace->getKey() => (int) $environment->getKey(), (string) $workspace->getKey() => (int) $environment->getKey(),
], ],
]); ]);
session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey()); session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey());
session()->put(WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY, [ session()->put(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY, [
(string) $workspace->getKey() => (int) $environment->getKey(), (string) $workspace->getKey() => (int) $environment->getKey(),
]); ]);

View File

@ -32,13 +32,13 @@
$this->actingAs($user)->withSession([ $this->actingAs($user)->withSession([
WorkspaceContext::SESSION_KEY => (int) $workspace->getKey(), WorkspaceContext::SESSION_KEY => (int) $workspace->getKey(),
WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY => [ WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
(string) $workspace->getKey() => (int) $environmentA->getKey(), (string) $workspace->getKey() => (int) $environmentA->getKey(),
], ],
]); ]);
session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey()); session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey());
session()->put(WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY, [ session()->put(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY, [
(string) $workspace->getKey() => (int) $environmentA->getKey(), (string) $workspace->getKey() => (int) $environmentA->getKey(),
]); ]);
@ -77,7 +77,7 @@
'wide_text' => $environmentB->name, 'wide_text' => $environmentB->name,
], ],
'customer review workspace' => [ 'customer review workspace' => [
'filtered_url' => CustomerReviewWorkspace::tenantPrefilterUrl($environmentA), 'filtered_url' => CustomerReviewWorkspace::environmentFilterUrl($environmentA),
'clean_url' => CustomerReviewWorkspace::getUrl(panel: 'admin'), 'clean_url' => CustomerReviewWorkspace::getUrl(panel: 'admin'),
'wide_text' => $environmentB->name, 'wide_text' => $environmentB->name,
], ],
@ -148,7 +148,7 @@
FindingExceptionsQueue::getUrl(panel: 'admin', parameters: [ FindingExceptionsQueue::getUrl(panel: 'admin', parameters: [
'environment_id' => (int) $environmentA->getKey(), 'environment_id' => (int) $environmentA->getKey(),
]), ]),
CustomerReviewWorkspace::tenantPrefilterUrl($environmentA), CustomerReviewWorkspace::environmentFilterUrl($environmentA),
route('admin.evidence.overview', [ route('admin.evidence.overview', [
'environment_id' => (int) $environmentA->getKey(), 'environment_id' => (int) $environmentA->getKey(),
]), ]),

Some files were not shown because too many files have changed in this diff Show More