refactor: consolidate internal tenant model naming #355

Merged
ahmido merged 1 commits from 300-internal-tenant-model-naming-consolidation into platform-dev 2026-05-14 11:13:32 +00:00
574 changed files with 6178 additions and 4846 deletions

View File

@ -40,8 +40,9 @@ public static function getUrl(?string $name = null, array $parameters = [], bool
return url('/admin');
}
$parameters['tenant'] ??= $resolvedTenant;
$parameters['environment'] ??= $resolvedTenant;
$parameters['workspace'] ??= $workspace;
unset($parameters['tenant']);
return parent::getUrl($name, $parameters, $isAbsolute, $panelId, null, $shouldGuessMissingParameters);
}
@ -52,7 +53,7 @@ protected static function workspaceScopedSlug(string $slug, ?Panel $panel = null
return $slug;
}
$prefix = 'workspaces/{workspace}/environments/{tenant}/';
$prefix = 'workspaces/{workspace}/environments/{environment}/';
return str_starts_with($slug, $prefix)
? $slug
@ -148,4 +149,4 @@ protected static function workspaceScopedTenantRelationshipName(): string
? $relationshipName
: 'tenant';
}
}
}

View File

@ -18,7 +18,7 @@
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\Schema;
class ChooseTenant extends Page
class ChooseEnvironment extends Page
{
protected static string $layout = 'filament-panels::components.layout.simple';
@ -26,9 +26,9 @@ class ChooseTenant extends Page
protected static bool $isDiscovered = false;
protected static ?string $slug = 'choose-tenant';
protected static ?string $slug = 'choose-environment';
protected string $view = 'filament.pages.choose-tenant';
protected string $view = 'filament.pages.choose-environment';
public function getTitle(): string
{
@ -66,7 +66,7 @@ public function getTenants(): Collection
return app(TenantOperabilityService::class)->filterSelectable(collect($tenants));
}
public function selectTenant(int $tenantId): void
public function selectEnvironment(int $tenantId): void
{
$user = auth()->user();

View File

@ -11,7 +11,7 @@
use App\Services\Audit\WorkspaceAuditLogger;
use App\Services\Auth\CapabilityResolver;
use App\Services\Auth\WorkspaceCapabilityResolver;
use App\Services\PortfolioCompare\CrossTenantPromotionExecutionService;
use App\Services\PortfolioCompare\CrossEnvironmentPromotionExecutionService;
use App\Support\Auth\Capabilities;
use App\Support\ManagedEnvironmentLinks;
use App\Support\Navigation\CanonicalNavigationContext;
@ -19,9 +19,9 @@
use App\Support\OperationRunLinks;
use App\Support\OpsUx\OpsUxBrowserEvents;
use App\Support\OpsUx\ProviderOperationStartResultPresenter;
use App\Support\PortfolioCompare\CrossTenantComparePreviewBuilder;
use App\Support\PortfolioCompare\CrossTenantCompareSelection;
use App\Support\PortfolioCompare\CrossTenantPromotionPreflight;
use App\Support\PortfolioCompare\CrossEnvironmentComparePreviewBuilder;
use App\Support\PortfolioCompare\CrossEnvironmentCompareSelection;
use App\Support\PortfolioCompare\CrossEnvironmentPromotionPreflight;
use App\Support\Rbac\WorkspaceUiEnforcement;
use App\Support\Ui\ActionSurface\ActionSurfaceDeclaration;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile;
@ -42,13 +42,13 @@
use Illuminate\Support\Str;
use UnitEnum;
class CrossTenantComparePage extends Page implements HasForms
class CrossEnvironmentComparePage extends Page implements HasForms
{
use InteractsWithForms;
private const string SOURCE_TENANT_QUERY_KEY = 'source_tenant_id';
private const string SOURCE_ENVIRONMENT_QUERY_KEY = 'source_environment_id';
private const string TARGET_TENANT_QUERY_KEY = 'target_tenant_id';
private const string TARGET_ENVIRONMENT_QUERY_KEY = 'target_environment_id';
private const string POLICY_TYPE_QUERY_KEY = 'policy_type';
@ -60,15 +60,15 @@ class CrossTenantComparePage extends Page implements HasForms
protected static string|UnitEnum|null $navigationGroup = 'Governance';
protected static ?string $title = 'Cross-ManagedEnvironment Compare';
protected static ?string $title = 'Cross-environment compare';
protected static ?string $slug = 'cross-tenant-compare';
protected static ?string $slug = 'cross-environment-compare';
protected string $view = 'filament.pages.cross-tenant-compare';
protected string $view = 'filament.pages.cross-environment-compare';
public ?string $sourceTenantId = null;
public ?string $sourceEnvironmentId = null;
public ?string $targetTenantId = null;
public ?string $targetEnvironmentId = null;
/**
* @var list<string>
@ -95,12 +95,12 @@ class CrossTenantComparePage extends Page implements HasForms
public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration
{
return ActionSurfaceDeclaration::forPage(ActionSurfaceProfile::ListOnlyReadOnly)
->satisfy(ActionSurfaceSlot::ListHeader, 'Header actions preserve return navigation, tenant drill-downs, and one dominant promotion-preflight action.')
->satisfy(ActionSurfaceSlot::ListHeader, 'Header actions preserve return navigation, environment drill-downs, and one dominant promotion-preflight action.')
->exempt(ActionSurfaceSlot::InspectAffordance, 'The compare page uses explicit selection controls instead of row-click inspection.')
->exempt(ActionSurfaceSlot::ListRowMoreMenu, 'Cross-tenant compare renders focused subject summaries instead of row-level overflow actions.')
->exempt(ActionSurfaceSlot::ListRowMoreMenu, 'Cross-environment compare renders focused subject summaries instead of row-level overflow actions.')
->exempt(ActionSurfaceSlot::ListBulkMoreGroup, 'The compare page has no bulk actions.')
->satisfy(ActionSurfaceSlot::ListEmptyState, 'The compare page explains when a selection is incomplete or invalid before any preview exists.')
->exempt(ActionSurfaceSlot::DetailHeader, 'Cross-tenant compare is a workspace decision page, not a record detail header.');
->exempt(ActionSurfaceSlot::DetailHeader, 'Cross-environment compare is a workspace decision page, not a record detail header.');
}
public function mount(): void
@ -124,24 +124,24 @@ public function form(Schema $schema): Schema
'xl' => 3,
])
->schema([
Select::make('sourceTenantId')
->label('Source tenant')
->options(fn (): array => $this->tenantOptions())
Select::make('sourceEnvironmentId')
->label('Source environment')
->options(fn (): array => $this->environmentOptions())
->searchable()
->preload()
->native(false)
->placeholder('Select a source tenant')
->extraFieldWrapperAttributes(['data-testid' => 'cross-tenant-source'])
->extraInputAttributes(['data-testid' => 'cross-tenant-source']),
Select::make('targetTenantId')
->label('Target tenant')
->options(fn (): array => $this->tenantOptions())
->placeholder('Select a source environment')
->extraFieldWrapperAttributes(['data-testid' => 'cross-environment-source'])
->extraInputAttributes(['data-testid' => 'cross-environment-source']),
Select::make('targetEnvironmentId')
->label('Target environment')
->options(fn (): array => $this->environmentOptions())
->searchable()
->preload()
->native(false)
->placeholder('Select a target tenant')
->extraFieldWrapperAttributes(['data-testid' => 'cross-tenant-target'])
->extraInputAttributes(['data-testid' => 'cross-tenant-target']),
->placeholder('Select a target environment')
->extraFieldWrapperAttributes(['data-testid' => 'cross-environment-target'])
->extraInputAttributes(['data-testid' => 'cross-environment-target']),
Select::make('selectedPolicyTypes')
->label('Governed subjects')
->options(fn (): array => $this->policyTypeOptions())
@ -151,10 +151,10 @@ public function form(Schema $schema): Schema
->native(false)
->placeholder('All governed subjects')
->helperText(fn (): ?string => $this->policyTypeOptions() === []
? 'Governed subject filters appear after authorized tenant inventory exists in the active workspace.'
? 'Governed subject filters appear after authorized environment inventory exists in the active workspace.'
: null)
->extraFieldWrapperAttributes(['data-testid' => 'cross-tenant-policy-types'])
->extraInputAttributes(['data-testid' => 'cross-tenant-policy-types']),
->extraFieldWrapperAttributes(['data-testid' => 'cross-environment-policy-types'])
->extraInputAttributes(['data-testid' => 'cross-environment-policy-types']),
]),
]);
}
@ -176,24 +176,24 @@ protected function getHeaderActions(): array
->url($navigationContext->backLinkUrl);
}
$sourceTenant = $this->selectedSourceTenant();
$sourceEnvironment = $this->selectedSourceEnvironment();
if ($sourceTenant instanceof ManagedEnvironment) {
$actions[] = Action::make('open_source_tenant')
->label('Open source tenant')
if ($sourceEnvironment instanceof ManagedEnvironment) {
$actions[] = Action::make('open_source_environment')
->label('Open source environment')
->icon('heroicon-o-arrow-top-right-on-square')
->color('gray')
->url(ManagedEnvironmentLinks::viewUrl($sourceTenant));
->url(ManagedEnvironmentLinks::viewUrl($sourceEnvironment));
}
$targetTenant = $this->selectedTargetTenant();
$targetEnvironment = $this->selectedTargetEnvironment();
if ($targetTenant instanceof ManagedEnvironment) {
$actions[] = Action::make('open_target_tenant')
->label('Open target tenant')
if ($targetEnvironment instanceof ManagedEnvironment) {
$actions[] = Action::make('open_target_environment')
->label('Open target environment')
->icon('heroicon-o-arrow-top-right-on-square')
->color('gray')
->url(ManagedEnvironmentLinks::viewUrl($targetTenant));
->url(ManagedEnvironmentLinks::viewUrl($targetEnvironment));
}
$preflightAction = Action::make('generatePromotionPreflight')
@ -254,15 +254,15 @@ public function applySelection(): void
$this->selectionMessage = null;
$this->preflight = null;
$this->sourceTenantId = $this->normalizeTenantIdentifier($this->sourceTenantId);
$this->targetTenantId = $this->normalizeTenantIdentifier($this->targetTenantId);
$this->sourceEnvironmentId = $this->normalizeEnvironmentIdentifier($this->sourceEnvironmentId);
$this->targetEnvironmentId = $this->normalizeEnvironmentIdentifier($this->targetEnvironmentId);
$this->selectedPolicyTypes = $this->normalizePolicyTypes($this->selectedPolicyTypes);
if ($this->sourceTenantId !== null
&& $this->targetTenantId !== null
&& $this->sourceTenantId === $this->targetTenantId) {
$this->selectionMessage = 'Choose two different tenants.';
$this->addError('targetTenantId', $this->selectionMessage);
if ($this->sourceEnvironmentId !== null
&& $this->targetEnvironmentId !== null
&& $this->sourceEnvironmentId === $this->targetEnvironmentId) {
$this->selectionMessage = 'Choose two different environments.';
$this->addError('targetEnvironmentId', $this->selectionMessage);
return;
}
@ -285,20 +285,20 @@ public function generatePromotionPreflight(): void
$selection = $this->compareSelection();
if (! $selection instanceof CrossTenantCompareSelection) {
if (! $selection instanceof CrossEnvironmentCompareSelection) {
return;
}
$this->preflight = app(CrossTenantPromotionPreflight::class)->build($this->preview);
$this->preflight = app(CrossEnvironmentPromotionPreflight::class)->build($this->preview);
$workspace = $this->workspace();
$user = auth()->user();
if ($workspace instanceof Workspace && $user instanceof User) {
app(WorkspaceAuditLogger::class)->logCrossTenantPromotionPreflightGenerated(
app(WorkspaceAuditLogger::class)->logCrossEnvironmentPromotionPreflightGenerated(
workspace: $workspace,
sourceTenant: $selection->sourceTenant,
targetTenant: $selection->targetTenant,
sourceEnvironment: $selection->sourceEnvironment,
targetEnvironment: $selection->targetEnvironment,
preflight: $this->preflight,
actor: $user,
);
@ -323,7 +323,7 @@ public function executePromotion(): void
$selection = $this->compareSelection();
$user = auth()->user();
if (! $selection instanceof CrossTenantCompareSelection || ! $user instanceof User) {
if (! $selection instanceof CrossEnvironmentCompareSelection || ! $user instanceof User) {
Notification::make()
->title('Promotion execution unavailable')
->body('Refresh the compare selection before executing promotion.')
@ -334,7 +334,7 @@ public function executePromotion(): void
}
try {
$result = app(CrossTenantPromotionExecutionService::class)->start(
$result = app(CrossEnvironmentPromotionExecutionService::class)->start(
selection: $selection,
preview: $this->preview,
preflight: $this->preflight,
@ -376,8 +376,8 @@ public function executePromotion(): void
public function clearSelectionUrl(): string
{
return static::getUrl($this->routeParameters([
self::SOURCE_TENANT_QUERY_KEY => null,
self::TARGET_TENANT_QUERY_KEY => null,
self::SOURCE_ENVIRONMENT_QUERY_KEY => null,
self::TARGET_ENVIRONMENT_QUERY_KEY => null,
self::POLICY_TYPE_QUERY_KEY => null,
]), panel: 'admin');
}
@ -388,18 +388,18 @@ public function selectionUrl(): string
}
public static function launchUrl(
?ManagedEnvironment $sourceTenant = null,
?ManagedEnvironment $targetTenant = null,
?ManagedEnvironment $sourceEnvironment = null,
?ManagedEnvironment $targetEnvironment = null,
?CanonicalNavigationContext $navigationContext = null,
): string {
$parameters = [];
if ($sourceTenant instanceof ManagedEnvironment) {
$parameters[self::SOURCE_TENANT_QUERY_KEY] = (int) $sourceTenant->getKey();
if ($sourceEnvironment instanceof ManagedEnvironment) {
$parameters[self::SOURCE_ENVIRONMENT_QUERY_KEY] = (int) $sourceEnvironment->getKey();
}
if ($targetTenant instanceof ManagedEnvironment) {
$parameters[self::TARGET_TENANT_QUERY_KEY] = (int) $targetTenant->getKey();
if ($targetEnvironment instanceof ManagedEnvironment) {
$parameters[self::TARGET_ENVIRONMENT_QUERY_KEY] = (int) $targetEnvironment->getKey();
}
if ($navigationContext instanceof CanonicalNavigationContext) {
@ -411,8 +411,8 @@ public static function launchUrl(
public function hasActiveSelection(): bool
{
return $this->sourceTenantId !== null
|| $this->targetTenantId !== null
return $this->sourceEnvironmentId !== null
|| $this->targetEnvironmentId !== null
|| $this->selectedPolicyTypes !== [];
}
@ -438,26 +438,26 @@ public function reasonLabel(string $reasonCode): string
return Str::headline(str_replace('_', ' ', $reasonCode));
}
public function sourceTenantUrl(): ?string
public function sourceEnvironmentUrl(): ?string
{
$tenant = $this->selectedSourceTenant();
$environment = $this->selectedSourceEnvironment();
if (! $tenant instanceof ManagedEnvironment) {
if (! $environment instanceof ManagedEnvironment) {
return null;
}
return ManagedEnvironmentLinks::viewUrl($tenant);
return ManagedEnvironmentLinks::viewUrl($environment);
}
public function targetTenantUrl(): ?string
public function targetEnvironmentUrl(): ?string
{
$tenant = $this->selectedTargetTenant();
$environment = $this->selectedTargetEnvironment();
if (! $tenant instanceof ManagedEnvironment) {
if (! $environment instanceof ManagedEnvironment) {
return null;
}
return ManagedEnvironmentLinks::viewUrl($tenant);
return ManagedEnvironmentLinks::viewUrl($environment);
}
/**
@ -466,16 +466,16 @@ public function targetTenantUrl(): ?string
private function formState(): array
{
return [
'sourceTenantId' => $this->sourceTenantId,
'targetTenantId' => $this->targetTenantId,
'sourceEnvironmentId' => $this->sourceEnvironmentId,
'targetEnvironmentId' => $this->targetEnvironmentId,
'selectedPolicyTypes' => $this->selectedPolicyTypes,
];
}
private function hydrateSelectionFromRequest(): void
{
$this->sourceTenantId = $this->normalizeTenantIdentifier(request()->query(self::SOURCE_TENANT_QUERY_KEY));
$this->targetTenantId = $this->normalizeTenantIdentifier(request()->query(self::TARGET_TENANT_QUERY_KEY));
$this->sourceEnvironmentId = $this->normalizeEnvironmentIdentifier(request()->query(self::SOURCE_ENVIRONMENT_QUERY_KEY));
$this->targetEnvironmentId = $this->normalizeEnvironmentIdentifier(request()->query(self::TARGET_ENVIRONMENT_QUERY_KEY));
$this->selectedPolicyTypes = $this->normalizePolicyTypes(request()->query(self::POLICY_TYPE_QUERY_KEY, []));
}
@ -487,11 +487,11 @@ private function refreshPreview(): void
$selection = $this->compareSelection();
if (! $selection instanceof CrossTenantCompareSelection) {
if (! $selection instanceof CrossEnvironmentCompareSelection) {
return;
}
$this->preview = app(CrossTenantComparePreviewBuilder::class)->build($selection);
$this->preview = app(CrossEnvironmentComparePreviewBuilder::class)->build($selection);
}
private function authorizePageAccess(): void
@ -554,61 +554,61 @@ private function authorizePromotionExecution(): void
abort(403);
}
$targetTenant = $this->selectedTargetTenant();
$targetEnvironment = $this->selectedTargetEnvironment();
if (! $targetTenant instanceof ManagedEnvironment) {
if (! $targetEnvironment instanceof ManagedEnvironment) {
abort(404);
}
/** @var CapabilityResolver $resolver */
$resolver = app(CapabilityResolver::class);
if (! $resolver->can($user, $targetTenant, Capabilities::TENANT_MANAGE)) {
if (! $resolver->can($user, $targetEnvironment, Capabilities::TENANT_MANAGE)) {
abort(403);
}
}
private function compareSelection(): ?CrossTenantCompareSelection
private function compareSelection(): ?CrossEnvironmentCompareSelection
{
$sourceTenant = $this->selectedSourceTenant();
$targetTenant = $this->selectedTargetTenant();
$sourceEnvironment = $this->selectedSourceEnvironment();
$targetEnvironment = $this->selectedTargetEnvironment();
if (! $sourceTenant instanceof ManagedEnvironment || ! $targetTenant instanceof ManagedEnvironment) {
if (! $sourceEnvironment instanceof ManagedEnvironment || ! $targetEnvironment instanceof ManagedEnvironment) {
return null;
}
if ((int) $sourceTenant->getKey() === (int) $targetTenant->getKey()) {
$this->selectionMessage = 'Choose two different tenants.';
if ((int) $sourceEnvironment->getKey() === (int) $targetEnvironment->getKey()) {
$this->selectionMessage = 'Choose two different environments.';
return null;
}
return new CrossTenantCompareSelection(
sourceTenant: $sourceTenant,
targetTenant: $targetTenant,
return new CrossEnvironmentCompareSelection(
sourceEnvironment: $sourceEnvironment,
targetEnvironment: $targetEnvironment,
policyTypes: $this->selectedPolicyTypes,
);
}
private function selectedSourceTenant(): ?ManagedEnvironment
private function selectedSourceEnvironment(): ?ManagedEnvironment
{
if ($this->sourceTenantId === null) {
if ($this->sourceEnvironmentId === null) {
return null;
}
return $this->resolveAuthorizedTenant($this->sourceTenantId);
return $this->resolveAuthorizedEnvironment($this->sourceEnvironmentId);
}
private function selectedTargetTenant(): ?ManagedEnvironment
private function selectedTargetEnvironment(): ?ManagedEnvironment
{
if ($this->targetTenantId === null) {
if ($this->targetEnvironmentId === null) {
return null;
}
return $this->resolveAuthorizedTenant($this->targetTenantId);
return $this->resolveAuthorizedEnvironment($this->targetEnvironmentId);
}
private function resolveAuthorizedTenant(string $tenantId): ManagedEnvironment
private function resolveAuthorizedEnvironment(string $environmentId): ManagedEnvironment
{
$workspace = $this->workspace();
$user = auth()->user();
@ -617,29 +617,29 @@ private function resolveAuthorizedTenant(string $tenantId): ManagedEnvironment
abort(404);
}
$tenant = ManagedEnvironment::query()
$environment = ManagedEnvironment::query()
->where('workspace_id', (int) $workspace->getKey())
->whereKey((int) $tenantId)
->whereKey((int) $environmentId)
->first();
if (! $tenant instanceof ManagedEnvironment) {
if (! $environment instanceof ManagedEnvironment) {
abort(404);
}
/** @var CapabilityResolver $resolver */
$resolver = app(CapabilityResolver::class);
if (! $user->canAccessTenant($tenant) || ! $resolver->can($user, $tenant, Capabilities::TENANT_VIEW)) {
if (! $user->canAccessTenant($environment) || ! $resolver->can($user, $environment, Capabilities::TENANT_VIEW)) {
abort(404);
}
return $tenant;
return $environment;
}
/**
* @return array<string, string>
*/
private function tenantOptions(): array
private function environmentOptions(): array
{
$workspace = $this->workspace();
$user = auth()->user();
@ -651,17 +651,17 @@ private function tenantOptions(): array
/** @var CapabilityResolver $resolver */
$resolver = app(CapabilityResolver::class);
$tenants = $user->accessibleManagedEnvironmentsQuery((int) $workspace->getKey())
$environments = $user->accessibleManagedEnvironmentsQuery((int) $workspace->getKey())
->select('managed_environments.*')
->orderBy('managed_environments.name')
->get();
$resolver->primeMemberships($user, $tenants->modelKeys());
$resolver->primeMemberships($user, $environments->modelKeys());
return $tenants
->filter(fn (ManagedEnvironment $tenant): bool => $resolver->can($user, $tenant, Capabilities::TENANT_VIEW))
->mapWithKeys(fn (ManagedEnvironment $tenant): array => [
(string) $tenant->getKey() => (string) $tenant->name,
return $environments
->filter(fn (ManagedEnvironment $environment): bool => $resolver->can($user, $environment, Capabilities::TENANT_VIEW))
->mapWithKeys(fn (ManagedEnvironment $environment): array => [
(string) $environment->getKey() => (string) $environment->name,
])
->all();
}
@ -671,14 +671,14 @@ private function tenantOptions(): array
*/
private function policyTypeOptions(): array
{
$tenantIds = array_map(static fn (string $tenantId): int => (int) $tenantId, array_keys($this->tenantOptions()));
$environmentIds = array_map(static fn (string $environmentId): int => (int) $environmentId, array_keys($this->environmentOptions()));
if ($tenantIds === []) {
if ($environmentIds === []) {
return [];
}
return InventoryItem::query()
->whereIn('managed_environment_id', $tenantIds)
->whereIn('managed_environment_id', $environmentIds)
->whereNotNull('policy_type')
->where('policy_type', '!=', '')
->distinct()
@ -697,7 +697,7 @@ private function preflightDisabledReason(): ?string
}
if (! is_array($this->preview)) {
return 'Select an authorized source and target tenant to generate a promotion preflight.';
return 'Select an authorized source and target environment to generate a promotion preflight.';
}
if ((int) data_get($this->preview, 'summary.total', 0) === 0) {
@ -737,14 +737,14 @@ private function executePromotionDisabledReason(): ?string
return 'You need workspace baseline manage access to execute promotion.';
}
$targetTenant = $this->selectedTargetTenant();
$targetEnvironment = $this->selectedTargetEnvironment();
if ($targetTenant instanceof ManagedEnvironment) {
if ($targetEnvironment instanceof ManagedEnvironment) {
/** @var CapabilityResolver $resolver */
$resolver = app(CapabilityResolver::class);
if (! $resolver->can($user, $targetTenant, Capabilities::TENANT_MANAGE)) {
return 'You need target tenant manage access to execute promotion.';
if (! $resolver->can($user, $targetEnvironment, Capabilities::TENANT_MANAGE)) {
return 'You need target environment manage access to execute promotion.';
}
}
}
@ -760,13 +760,13 @@ private function executePromotionConfirmationDescription(): string
$manualMappingRequired = (int) data_get($this->preflight, 'summary.manual_mapping_required', 0);
$excluded = $blocked + $manualMappingRequired;
$sourceTenantName = $selection?->sourceTenant->name ?? 'Source tenant';
$targetTenantName = $selection?->targetTenant->name ?? 'Target tenant';
$sourceEnvironmentName = $selection?->sourceEnvironment->name ?? 'Source environment';
$targetEnvironmentName = $selection?->targetEnvironment->name ?? 'Target environment';
return sprintf(
'Queue one promotion run from %s to %s for %d ready governed subject%s. %d subject%s remain excluded on the compare page.',
$sourceTenantName,
$targetTenantName,
$sourceEnvironmentName,
$targetEnvironmentName,
$ready,
$ready === 1 ? '' : 's',
$excluded,
@ -777,7 +777,7 @@ private function executePromotionConfirmationDescription(): string
/**
* @param mixed $value
*/
private function normalizeTenantIdentifier(mixed $value): ?string
private function normalizeEnvironmentIdentifier(mixed $value): ?string
{
if (! is_string($value) && ! is_int($value)) {
return null;
@ -815,8 +815,8 @@ private function normalizePolicyTypes(mixed $value): array
private function routeParameters(array $overrides = []): array
{
$parameters = [
self::SOURCE_TENANT_QUERY_KEY => $this->sourceTenantId,
self::TARGET_TENANT_QUERY_KEY => $this->targetTenantId,
self::SOURCE_ENVIRONMENT_QUERY_KEY => $this->sourceEnvironmentId,
self::TARGET_ENVIRONMENT_QUERY_KEY => $this->targetEnvironmentId,
self::POLICY_TYPE_QUERY_KEY => $this->selectedPolicyTypes,
];

View File

@ -5,9 +5,9 @@
namespace App\Filament\Pages;
use App\Filament\Pages\Governance\GovernanceInbox;
use App\Filament\Widgets\Tenant\TenantTriageArrivalContinuity;
use App\Filament\Widgets\Dashboard\TenantDashboardContextChips;
use App\Filament\Widgets\Dashboard\TenantDashboardOverview;
use App\Filament\Widgets\ManagedEnvironment\ManagedEnvironmentTriageArrivalContinuity;
use App\Filament\Widgets\Dashboard\EnvironmentDashboardContextChips;
use App\Filament\Widgets\Dashboard\EnvironmentDashboardOverview;
use App\Models\SupportRequest;
use App\Models\ManagedEnvironment;
use App\Models\User;
@ -21,8 +21,8 @@
use App\Support\SupportDiagnostics\SupportDiagnosticBundleBuilder;
use App\Support\SupportRequests\ExternalSupportDeskHandoffService;
use App\Support\SupportRequests\SupportRequestSubmissionService;
use App\Support\TenantDashboard\TenantDashboardSummary;
use App\Support\TenantDashboard\TenantDashboardSummaryBuilder;
use App\Support\EnvironmentDashboard\EnvironmentDashboardSummary;
use App\Support\EnvironmentDashboard\EnvironmentDashboardSummaryBuilder;
use Filament\Actions\Action;
use Filament\Actions\ActionGroup;
use Filament\Facades\Filament;
@ -43,7 +43,7 @@
use App\Filament\Widgets\Dashboard\DashboardKpis;
class TenantDashboard extends Dashboard
class EnvironmentDashboard extends Dashboard
{
protected Width|string|null $maxContentWidth = Width::Full;
@ -52,7 +52,7 @@ class TenantDashboard extends Dashboard
*/
public array $supportDiagnosticsAuditKeys = [];
private ?TenantDashboardSummary $dashboardSummary = null;
private ?EnvironmentDashboardSummary $dashboardSummary = null;
public static function getNavigationLabel(): string
{
@ -69,7 +69,7 @@ public function getTitle(): string | Htmlable
$summary = $this->dashboardSummary();
if (! $summary instanceof TenantDashboardSummary) {
if (! $summary instanceof EnvironmentDashboardSummary) {
return (string) $tenant->name;
}
@ -112,7 +112,7 @@ public static function getUrl(array $parameters = [], bool $isAbsolute = true, ?
protected function getHeaderWidgets(): array
{
return [
TenantDashboardContextChips::class,
EnvironmentDashboardContextChips::class,
];
}
@ -127,9 +127,9 @@ public function getHeaderWidgetsColumns(): int|array
public function getWidgets(): array
{
return [
TenantTriageArrivalContinuity::class,
ManagedEnvironmentTriageArrivalContinuity::class,
DashboardKpis::class,
TenantDashboardOverview::class,
EnvironmentDashboardOverview::class,
];
}
@ -204,7 +204,7 @@ private function primaryFollowUpHeaderPayload(): ?array
{
$summary = $this->dashboardSummary();
if (! $summary instanceof TenantDashboardSummary) {
if (! $summary instanceof EnvironmentDashboardSummary) {
return null;
}
@ -222,7 +222,7 @@ private function secondaryHeaderPayload(): ?array
{
$summary = $this->dashboardSummary();
if (! $summary instanceof TenantDashboardSummary) {
if (! $summary instanceof EnvironmentDashboardSummary) {
return null;
}
@ -304,9 +304,9 @@ private function summaryHeaderAction(string $name, array $payload, string $color
return $action;
}
private function dashboardSummary(): ?TenantDashboardSummary
private function dashboardSummary(): ?EnvironmentDashboardSummary
{
if ($this->dashboardSummary instanceof TenantDashboardSummary) {
if ($this->dashboardSummary instanceof EnvironmentDashboardSummary) {
return $this->dashboardSummary;
}
@ -317,7 +317,7 @@ private function dashboardSummary(): ?TenantDashboardSummary
return null;
}
$this->dashboardSummary = app(TenantDashboardSummaryBuilder::class)->build($tenant, $user);
$this->dashboardSummary = app(EnvironmentDashboardSummaryBuilder::class)->build($tenant, $user);
return $this->dashboardSummary;
}

View File

@ -6,8 +6,8 @@
use App\Filament\Concerns\ResolvesPanelTenantContext;
use App\Models\User;
use App\Services\Auth\TenantDiagnosticsService;
use App\Services\Auth\TenantMembershipManager;
use App\Services\Auth\ManagedEnvironmentDiagnosticsService;
use App\Services\Auth\ManagedEnvironmentMembershipManager;
use App\Support\Auth\Capabilities;
use App\Support\Rbac\UiEnforcement;
use App\Support\Rbac\UiTooltips;
@ -17,7 +17,7 @@
use Filament\Actions\Action;
use Filament\Pages\Page;
class TenantDiagnostics extends Page
class EnvironmentDiagnostics extends Page
{
use ResolvesPanelTenantContext;
@ -25,7 +25,7 @@ class TenantDiagnostics extends Page
protected static ?string $slug = 'diagnostics';
protected string $view = 'filament.pages.tenant-diagnostics';
protected string $view = 'filament.pages.environment-diagnostics';
public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration
{
@ -44,14 +44,14 @@ public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration
public function mount(): void
{
$tenant = static::resolveTenantContextForCurrentPanelOrFail();
$this->missingOwner = app(TenantDiagnosticsService::class)->tenantHasNoOwners($tenant);
$this->missingOwner = app(ManagedEnvironmentDiagnosticsService::class)->tenantHasNoOwners($tenant);
$user = auth()->user();
if (! $user instanceof User) {
abort(403, 'Not allowed');
}
$this->hasDuplicateMembershipsForCurrentUser = app(TenantDiagnosticsService::class)
$this->hasDuplicateMembershipsForCurrentUser = app(ManagedEnvironmentDiagnosticsService::class)
->userHasDuplicateMemberships($tenant, $user);
}
@ -96,7 +96,7 @@ public function bootstrapOwner(): void
abort(403, 'Not allowed');
}
app(TenantMembershipManager::class)->grantScope($tenant, $user, $user, sourceRef: 'diagnostic');
app(ManagedEnvironmentMembershipManager::class)->grantScope($tenant, $user, $user, sourceRef: 'diagnostic');
$this->mount();
}
@ -110,7 +110,7 @@ public function mergeDuplicateMemberships(): void
abort(403, 'Not allowed');
}
app(TenantDiagnosticsService::class)->mergeDuplicateMembershipsForUser($tenant, $user, $user);
app(ManagedEnvironmentDiagnosticsService::class)->mergeDuplicateMembershipsForUser($tenant, $user, $user);
$this->mount();
}

View File

@ -7,7 +7,7 @@
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Models\WorkspaceMembership;
use App\Services\Intune\TenantRequiredPermissionsViewModelBuilder;
use App\Services\Intune\ManagedEnvironmentRequiredPermissionsViewModelBuilder;
use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer;
use App\Support\Filament\TablePaginationProfiles;
@ -29,7 +29,7 @@
use Livewire\Attributes\Locked;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class TenantRequiredPermissions extends Page implements HasTable
class EnvironmentRequiredPermissions extends Page implements HasTable
{
use InteractsWithTable;
@ -37,11 +37,11 @@ class TenantRequiredPermissions extends Page implements HasTable
protected static bool $shouldRegisterNavigation = false;
protected static ?string $slug = 'workspaces/{workspace}/environments/{tenant}/required-permissions';
protected static ?string $slug = 'workspaces/{workspace}/environments/{environment}/required-permissions';
protected static ?string $title = 'Required permissions';
protected string $view = 'filament.pages.tenant-required-permissions';
protected string $view = 'filament.pages.environment-required-permissions';
public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration
{
@ -73,9 +73,9 @@ public function currentTenant(): ?ManagedEnvironment
return $this->trustedScopedTenant();
}
public function mount(ManagedEnvironment|string|null $tenant = null): void
public function mount(ManagedEnvironment|string|null $environment = null): void
{
$tenant = static::resolveScopedTenant($tenant);
$tenant = static::resolveScopedTenant($environment);
if (! $tenant instanceof ManagedEnvironment || ! static::hasScopedTenantAccess($tenant)) {
abort(404);
@ -151,10 +151,10 @@ public function table(Table $table): Table
TextColumn::make('status')
->label('Status')
->badge()
->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantPermissionStatus))
->color(BadgeRenderer::color(BadgeDomain::TenantPermissionStatus))
->icon(BadgeRenderer::icon(BadgeDomain::TenantPermissionStatus))
->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantPermissionStatus))
->formatStateUsing(BadgeRenderer::label(BadgeDomain::ManagedEnvironmentPermissionStatus))
->color(BadgeRenderer::color(BadgeDomain::ManagedEnvironmentPermissionStatus))
->icon(BadgeRenderer::icon(BadgeDomain::ManagedEnvironmentPermissionStatus))
->iconColor(BadgeRenderer::iconColor(BadgeDomain::ManagedEnvironmentPermissionStatus))
->sortable(),
TextColumn::make('features_label')
->label('Features')
@ -235,7 +235,7 @@ protected static function resolveScopedTenant(ManagedEnvironment|string|null $te
->first();
}
$routeTenant = request()->route('tenant');
$routeTenant = request()->route('environment') ?? request()->route('tenant');
if ($routeTenant instanceof ManagedEnvironment) {
return $routeTenant;
@ -333,7 +333,7 @@ private function trustedScopedTenant(): ?ManagedEnvironment
*/
private function filterState(array $filters = [], ?string $search = null): array
{
return TenantRequiredPermissionsViewModelBuilder::normalizeFilterState([
return ManagedEnvironmentRequiredPermissionsViewModelBuilder::normalizeFilterState([
'status' => $filters['status']['value'] ?? data_get($this->tableFilters, 'status.value'),
'type' => $filters['type']['value'] ?? data_get($this->tableFilters, 'type.value'),
'features' => $filters['features']['values'] ?? data_get($this->tableFilters, 'features.values', []),
@ -359,7 +359,7 @@ private function viewModelForState(array $state): array
return $this->cachedViewModel;
}
$builder = app(TenantRequiredPermissionsViewModelBuilder::class);
$builder = app(ManagedEnvironmentRequiredPermissionsViewModelBuilder::class);
$this->cachedViewModelStateKey = $stateKey ?: null;
$this->cachedViewModel = $builder->build($tenant, $state);
@ -514,7 +514,7 @@ private function seedTableStateFromQuery(): void
$queryFeatures = request()->query('features', []);
$state = TenantRequiredPermissionsViewModelBuilder::normalizeFilterState([
$state = ManagedEnvironmentRequiredPermissionsViewModelBuilder::normalizeFilterState([
'status' => request()->query('status', 'missing'),
'type' => request()->query('type', 'all'),
'features' => is_array($queryFeatures) ? $queryFeatures : [],

View File

@ -288,10 +288,10 @@ public function emptyState(): array
'title' => 'No visible assigned findings right now',
'body' => 'Nothing currently assigned to you needs attention across the visible environment scope. Choose an environment to continue working elsewhere in the workspace.',
'icon' => 'heroicon-o-clipboard-document-check',
'action_name' => 'choose_tenant_empty',
'action_name' => 'choose_environment_empty',
'action_label' => 'Choose an environment',
'action_kind' => 'url',
'action_url' => route('filament.admin.pages.choose-tenant'),
'action_url' => route('filament.admin.pages.choose-environment'),
];
}

View File

@ -11,7 +11,7 @@
use App\Models\Workspace;
use App\Services\Auth\CapabilityResolver;
use App\Services\Auth\WorkspaceCapabilityResolver;
use App\Services\TenantReviews\TenantReviewRegisterService;
use App\Services\EnvironmentReviews\EnvironmentReviewRegisterService;
use App\Support\Auth\Capabilities;
use App\Support\GovernanceInbox\GovernanceInboxSectionBuilder;
use App\Support\Navigation\CanonicalNavigationContext;
@ -333,7 +333,7 @@ private function reviewTenants(): array
return $this->reviewTenants = [];
}
$service = app(TenantReviewRegisterService::class);
$service = app(EnvironmentReviewRegisterService::class);
if (! $service->canAccessWorkspace($user, $workspace)) {
return $this->reviewTenants = [];

View File

@ -7,11 +7,11 @@
use App\Filament\Resources\EvidenceSnapshotResource;
use App\Models\EvidenceSnapshot;
use App\Models\ManagedEnvironment;
use App\Models\TenantReview;
use App\Models\EnvironmentReview;
use App\Models\User;
use App\Support\Badges\BadgeCatalog;
use App\Support\Badges\BadgeDomain;
use App\Support\TenantReviewStatus;
use App\Support\EnvironmentReviewStatus;
use App\Support\Ui\ActionSurface\ActionSurfaceDeclaration;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceInspectAffordance;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile;
@ -398,13 +398,13 @@ private function latestAccessibleSnapshots(): Collection
*/
private function currentReviewTenantIds(Collection $snapshots): array
{
return TenantReview::query()
return EnvironmentReview::query()
->where('workspace_id', $this->workspaceId())
->whereIn('managed_environment_id', $snapshots->pluck('managed_environment_id')->map(static fn (mixed $tenantId): int => (int) $tenantId)->all())
->whereIn('status', [
TenantReviewStatus::Draft->value,
TenantReviewStatus::Ready->value,
TenantReviewStatus::Published->value,
EnvironmentReviewStatus::Draft->value,
EnvironmentReviewStatus::Ready->value,
EnvironmentReviewStatus::Published->value,
])
->pluck('managed_environment_id')
->mapWithKeys(static fn (mixed $tenantId): array => [(int) $tenantId => true])

View File

@ -93,6 +93,6 @@ public function createWorkspace(array $data): void
->success()
->send();
$this->redirect(ChooseTenant::getUrl());
$this->redirect(ChooseEnvironment::getUrl());
}
}

View File

@ -4,16 +4,16 @@
namespace App\Filament\Pages\Reviews;
use App\Filament\Resources\TenantReviewResource;
use App\Filament\Resources\EnvironmentReviewResource;
use App\Models\EvidenceSnapshot;
use App\Models\FindingException;
use App\Models\ReviewPack;
use App\Models\ManagedEnvironment;
use App\Models\TenantReview;
use App\Models\EnvironmentReview;
use App\Models\User;
use App\Models\Workspace;
use App\Services\Audit\WorkspaceAuditLogger;
use App\Services\TenantReviews\TenantReviewRegisterService;
use App\Services\EnvironmentReviews\EnvironmentReviewRegisterService;
use App\Support\Audit\AuditActionId;
use App\Support\Auth\Capabilities;
use App\Support\Findings\FindingOutcomeSemantics;
@ -21,7 +21,7 @@
use App\Support\Governance\Controls\ComplianceEvidenceMappingV1;
use App\Support\Navigation\CanonicalNavigationContext;
use App\Support\ReviewPackStatus;
use App\Support\TenantReviewCompletenessState;
use App\Support\EnvironmentReviewCompletenessState;
use App\Support\Ui\ActionSurface\ActionSurfaceDeclaration;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceInspectAffordance;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile;
@ -236,7 +236,7 @@ public function authorizedTenants(): array
return $this->authorizedTenants = [];
}
return $this->authorizedTenants = app(TenantReviewRegisterService::class)->authorizedTenants($user, $workspace);
return $this->authorizedTenants = app(EnvironmentReviewRegisterService::class)->authorizedTenants($user, $workspace);
}
private function authorizePageAccess(): void
@ -252,7 +252,7 @@ private function authorizePageAccess(): void
throw new NotFoundHttpException;
}
$service = app(TenantReviewRegisterService::class);
$service = app(EnvironmentReviewRegisterService::class);
if (! $service->canAccessWorkspace($user, $workspace)) {
throw new NotFoundHttpException;
@ -300,7 +300,7 @@ private function workspaceQuery(): Builder
return ManagedEnvironment::query()->whereRaw('1 = 0');
}
return app(TenantReviewRegisterService::class)->customerWorkspaceTenantQuery($user, $workspace);
return app(EnvironmentReviewRegisterService::class)->customerWorkspaceTenantQuery($user, $workspace);
}
/**
@ -377,18 +377,18 @@ private function workspace(): ?Workspace
: null;
}
private function latestPublishedReview(ManagedEnvironment $tenant): ?TenantReview
private function latestPublishedReview(ManagedEnvironment $tenant): ?EnvironmentReview
{
$review = $tenant->tenantReviews->first();
$review = $tenant->environmentReviews->first();
return $review instanceof TenantReview ? $review : null;
return $review instanceof EnvironmentReview ? $review : null;
}
private function latestReviewUrl(ManagedEnvironment $tenant): ?string
{
$review = $this->latestPublishedReview($tenant);
if (! $review instanceof TenantReview) {
if (! $review instanceof EnvironmentReview) {
return null;
}
@ -404,7 +404,7 @@ private function latestReviewUrl(ManagedEnvironment $tenant): ?string
static fn (mixed $value): bool => $value !== null && $value !== '',
);
return $this->appendQuery(TenantReviewResource::tenantScopedUrl('view', ['record' => $review], $tenant), $query);
return $this->appendQuery(EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $review], $tenant), $query);
}
private function latestPublishedAt(ManagedEnvironment $tenant): ?\Illuminate\Support\Carbon
@ -416,8 +416,8 @@ private function reviewTruth(ManagedEnvironment $tenant): ?ArtifactTruthEnvelope
{
$review = $this->latestPublishedReview($tenant);
return $review instanceof TenantReview
? app(ArtifactTruthPresenter::class)->forTenantReview($review)
return $review instanceof EnvironmentReview
? app(ArtifactTruthPresenter::class)->forEnvironmentReview($review)
: null;
}
@ -427,7 +427,7 @@ private function reviewOutcome(ManagedEnvironment $tenant): ?CompressedGovernanc
$review = $this->latestPublishedReview($tenant);
$truth = $this->reviewTruth($tenant);
if (! $review instanceof TenantReview || ! $truth instanceof ArtifactTruthEnvelope) {
if (! $review instanceof EnvironmentReview || ! $truth instanceof ArtifactTruthEnvelope) {
return null;
}
@ -439,7 +439,7 @@ private function latestReviewStateLabel(ManagedEnvironment $tenant): string
{
$review = $this->latestPublishedReview($tenant);
if (! $review instanceof TenantReview) {
if (! $review instanceof EnvironmentReview) {
return __('localization.review.no_published_review');
}
@ -452,7 +452,7 @@ private function latestReviewStateColor(ManagedEnvironment $tenant): string
{
$review = $this->latestPublishedReview($tenant);
if (! $review instanceof TenantReview) {
if (! $review instanceof EnvironmentReview) {
return 'gray';
}
@ -481,7 +481,7 @@ private function reviewOutcomeDescription(ManagedEnvironment $tenant): ?string
{
$review = $this->latestPublishedReview($tenant);
if (! $review instanceof TenantReview) {
if (! $review instanceof EnvironmentReview) {
return __('localization.review.no_published_review_available');
}
@ -524,7 +524,7 @@ private function governancePackageSummary(ManagedEnvironment $tenant): array
{
$review = $this->latestPublishedReview($tenant);
if (! $review instanceof TenantReview) {
if (! $review instanceof EnvironmentReview) {
return [];
}
@ -541,7 +541,7 @@ private function governancePackageAvailability(ManagedEnvironment $tenant): arra
{
$review = $this->latestPublishedReview($tenant);
if (! $review instanceof TenantReview) {
if (! $review instanceof EnvironmentReview) {
return [
'state' => 'unavailable',
'label' => __('localization.review.governance_package_unavailable'),
@ -553,8 +553,8 @@ private function governancePackageAvailability(ManagedEnvironment $tenant): arra
$user = auth()->user();
$limitations = is_array($review->controlInterpretation()['limitations'] ?? null) ? $review->controlInterpretation()['limitations'] : [];
$isPartialReview = in_array((string) $review->completeness_state, [
TenantReviewCompletenessState::Partial->value,
TenantReviewCompletenessState::Stale->value,
EnvironmentReviewCompletenessState::Partial->value,
EnvironmentReviewCompletenessState::Stale->value,
], true) || $limitations !== [];
if (! $pack instanceof ReviewPack) {
@ -652,7 +652,7 @@ private function controlReadinessDescription(ManagedEnvironment $tenant): string
{
$review = $this->latestPublishedReview($tenant);
if (! $review instanceof TenantReview) {
if (! $review instanceof EnvironmentReview) {
return __('localization.review.no_published_review_available');
}
@ -720,7 +720,7 @@ private function workspaceReviewNeedsAttention(ManagedEnvironment $tenant): bool
{
$review = $this->latestPublishedReview($tenant);
if (! $review instanceof TenantReview) {
if (! $review instanceof EnvironmentReview) {
return true;
}
@ -739,7 +739,7 @@ private function evidenceStatusState(ManagedEnvironment $tenant): string
{
$review = $this->latestPublishedReview($tenant);
if (! $review instanceof TenantReview) {
if (! $review instanceof EnvironmentReview) {
return 'pending';
}
@ -802,7 +802,7 @@ private function primaryControlSummary(ManagedEnvironment $tenant): ?array
{
$review = $this->latestPublishedReview($tenant);
if (! $review instanceof TenantReview) {
if (! $review instanceof EnvironmentReview) {
return null;
}
@ -818,7 +818,7 @@ private function primaryControlSummary(ManagedEnvironment $tenant): ?array
->first();
}
private function controlLimitationSummary(TenantReview $review): ?string
private function controlLimitationSummary(EnvironmentReview $review): ?string
{
$counts = $review->controlInterpretationLimitationCounts();
@ -842,7 +842,7 @@ private function findingSummary(ManagedEnvironment $tenant): string
{
$review = $this->latestPublishedReview($tenant);
if (! $review instanceof TenantReview) {
if (! $review instanceof EnvironmentReview) {
return __('localization.review.no_published_review_available');
}
@ -869,7 +869,7 @@ private function acceptedRiskSummary(ManagedEnvironment $tenant): string
{
$review = $this->latestPublishedReview($tenant);
if (! $review instanceof TenantReview) {
if (! $review instanceof EnvironmentReview) {
return __('localization.review.no_published_review_available');
}
@ -897,7 +897,7 @@ private function evidenceProofAvailability(ManagedEnvironment $tenant): string
{
$review = $this->latestPublishedReview($tenant);
if (! $review instanceof TenantReview) {
if (! $review instanceof EnvironmentReview) {
return __('localization.review.no_published_review_available');
}
@ -941,10 +941,10 @@ private function visibleInterpretationVersions(): array
return [];
}
return app(TenantReviewRegisterService::class)
return app(EnvironmentReviewRegisterService::class)
->latestPublishedQuery($user, $workspace)
->get()
->map(static fn (TenantReview $review): ?string => $review->controlInterpretationVersion())
->map(static fn (EnvironmentReview $review): ?string => $review->controlInterpretationVersion())
->filter()
->unique()
->values()
@ -965,7 +965,7 @@ private function currentTenantFilterInterpretationVersion(): ?string
return null;
}
return $tenant->tenantReviews()->published()
return $tenant->environmentReviews()->published()
->latest('published_at')
->latest('generated_at')
->latest('id')

View File

@ -4,13 +4,13 @@
namespace App\Filament\Pages\Reviews;
use App\Filament\Resources\TenantReviewResource;
use App\Filament\Resources\EnvironmentReviewResource;
use App\Models\ManagedEnvironment;
use App\Models\TenantReview;
use App\Models\EnvironmentReview;
use App\Models\User;
use App\Models\Workspace;
use App\Services\ReviewPackService;
use App\Services\TenantReviews\TenantReviewRegisterService;
use App\Services\EnvironmentReviews\EnvironmentReviewRegisterService;
use App\Support\Auth\Capabilities;
use App\Support\Badges\BadgeCatalog;
use App\Support\Badges\BadgeDomain;
@ -19,7 +19,7 @@
use App\Support\Filament\CanonicalAdminTenantFilterState;
use App\Support\Filament\FilterPresets;
use App\Support\Filament\TablePaginationProfiles;
use App\Support\TenantReviewCompletenessState;
use App\Support\EnvironmentReviewCompletenessState;
use App\Support\Ui\ActionSurface\ActionSurfaceDeclaration;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceInspectAffordance;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile;
@ -112,15 +112,15 @@ public function table(Table $table): Table
->persistFiltersInSession()
->persistSearchInSession()
->persistSortInSession()
->recordUrl(fn (TenantReview $record): string => TenantReviewResource::tenantScopedUrl('view', ['record' => $record], $record->tenant))
->recordUrl(fn (EnvironmentReview $record): string => EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $record], $record->tenant))
->columns([
TextColumn::make('tenant.name')->label('ManagedEnvironment')->searchable(),
TextColumn::make('status')
->badge()
->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantReviewStatus))
->color(BadgeRenderer::color(BadgeDomain::TenantReviewStatus))
->icon(BadgeRenderer::icon(BadgeDomain::TenantReviewStatus))
->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantReviewStatus)),
->formatStateUsing(BadgeRenderer::label(BadgeDomain::EnvironmentReviewStatus))
->color(BadgeRenderer::color(BadgeDomain::EnvironmentReviewStatus))
->icon(BadgeRenderer::icon(BadgeDomain::EnvironmentReviewStatus))
->iconColor(BadgeRenderer::iconColor(BadgeDomain::EnvironmentReviewStatus)),
TextColumn::make('outcome')
->label('Outcome')
->badge()
@ -154,7 +154,7 @@ public function table(Table $table): Table
]),
SelectFilter::make('completeness_state')
->label('Completeness')
->options(BadgeCatalog::options(BadgeDomain::TenantReviewCompleteness, TenantReviewCompletenessState::values())),
->options(BadgeCatalog::options(BadgeDomain::EnvironmentReviewCompleteness, EnvironmentReviewCompletenessState::values())),
SelectFilter::make('published_state')
->label('Published state')
->options([
@ -174,11 +174,11 @@ public function table(Table $table): Table
Action::make('export_executive_pack')
->label('Export executive pack')
->icon('heroicon-o-arrow-down-tray')
->visible(fn (TenantReview $record): bool => auth()->user() instanceof User
&& auth()->user()->can(Capabilities::TENANT_REVIEW_MANAGE, $record->tenant)
->visible(fn (EnvironmentReview $record): bool => auth()->user() instanceof User
&& auth()->user()->can(Capabilities::ENVIRONMENT_REVIEW_MANAGE, $record->tenant)
&& in_array($record->status, ['ready', 'published'], true))
->disabled(fn (TenantReview $record): bool => (bool) (app(ReviewPackService::class)->reviewPackGenerationDecisionForTenant($record->tenant)['is_blocked'] ?? false))
->tooltip(function (TenantReview $record): ?string {
->disabled(fn (EnvironmentReview $record): bool => (bool) (app(ReviewPackService::class)->reviewPackGenerationDecisionForTenant($record->tenant)['is_blocked'] ?? false))
->tooltip(function (EnvironmentReview $record): ?string {
$decision = app(ReviewPackService::class)->reviewPackGenerationDecisionForTenant($record->tenant);
if ((bool) ($decision['is_blocked'] ?? false)) {
@ -195,7 +195,7 @@ public function table(Table $table): Table
return null;
})
->action(fn (TenantReview $record): mixed => TenantReviewResource::executeExport($record)),
->action(fn (EnvironmentReview $record): mixed => EnvironmentReviewResource::executeExport($record)),
])
->bulkActions([])
->emptyStateHeading('No review records match this view')
@ -225,7 +225,7 @@ public function authorizedTenants(): array
return $this->authorizedTenants = [];
}
return $this->authorizedTenants = app(TenantReviewRegisterService::class)->authorizedTenants($user, $workspace);
return $this->authorizedTenants = app(EnvironmentReviewRegisterService::class)->authorizedTenants($user, $workspace);
}
private function authorizePageAccess(): void
@ -241,7 +241,7 @@ private function authorizePageAccess(): void
throw new NotFoundHttpException;
}
$service = app(TenantReviewRegisterService::class);
$service = app(EnvironmentReviewRegisterService::class);
if (! $service->canAccessWorkspace($user, $workspace)) {
throw new NotFoundHttpException;
@ -258,10 +258,10 @@ private function registerQuery(): Builder
$workspace = $this->workspace();
if (! $user instanceof User || ! $workspace instanceof Workspace) {
return TenantReview::query()->whereRaw('1 = 0');
return EnvironmentReview::query()->whereRaw('1 = 0');
}
return app(TenantReviewRegisterService::class)->query($user, $workspace);
return app(EnvironmentReviewRegisterService::class)->query($user, $workspace);
}
/**
@ -341,36 +341,36 @@ private function workspace(): ?Workspace
: null;
}
private function reviewTruth(TenantReview $record, bool $fresh = false): ArtifactTruthEnvelope
private function reviewTruth(EnvironmentReview $record, bool $fresh = false): ArtifactTruthEnvelope
{
$presenter = app(ArtifactTruthPresenter::class);
return $fresh
? $presenter->forTenantReviewFresh($record)
: $presenter->forTenantReview($record);
? $presenter->forEnvironmentReviewFresh($record)
: $presenter->forEnvironmentReview($record);
}
private function reviewOutcomeLabel(TenantReview $record): string
private function reviewOutcomeLabel(EnvironmentReview $record): string
{
return $this->reviewOutcome($record)->primaryLabel;
}
private function reviewOutcomeBadgeColor(TenantReview $record): string
private function reviewOutcomeBadgeColor(EnvironmentReview $record): string
{
return $this->reviewOutcome($record)->primaryBadge->color;
}
private function reviewOutcomeBadgeIcon(TenantReview $record): ?string
private function reviewOutcomeBadgeIcon(EnvironmentReview $record): ?string
{
return $this->reviewOutcome($record)->primaryBadge->icon;
}
private function reviewOutcomeBadgeIconColor(TenantReview $record): ?string
private function reviewOutcomeBadgeIconColor(EnvironmentReview $record): ?string
{
return $this->reviewOutcome($record)->primaryBadge->iconColor;
}
private function reviewOutcomeDescription(TenantReview $record): ?string
private function reviewOutcomeDescription(EnvironmentReview $record): ?string
{
$primaryReason = $this->reviewOutcome($record)->primaryReason;
$findingOutcomeSummary = $this->findingOutcomeSummary($record);
@ -382,12 +382,12 @@ private function reviewOutcomeDescription(TenantReview $record): ?string
return trim($primaryReason.' Terminal outcomes: '.$findingOutcomeSummary.'.');
}
private function reviewOutcomeNextStep(TenantReview $record): string
private function reviewOutcomeNextStep(EnvironmentReview $record): string
{
return $this->reviewOutcome($record)->nextActionText;
}
private function reviewOutcome(TenantReview $record, bool $fresh = false): CompressedGovernanceOutcome
private function reviewOutcome(EnvironmentReview $record, bool $fresh = false): CompressedGovernanceOutcome
{
$presenter = app(ArtifactTruthPresenter::class);
$truth = $fresh
@ -401,7 +401,7 @@ private function reviewOutcome(TenantReview $record, bool $fresh = false): Compr
);
}
private function findingOutcomeSummary(TenantReview $record): ?string
private function findingOutcomeSummary(EnvironmentReview $record): ?string
{
$summary = is_array($record->summary) ? $record->summary : [];
$outcomeCounts = $summary['finding_outcomes'] ?? [];

View File

@ -6,7 +6,7 @@
use App\Models\User;
use App\Models\WorkspaceMembership;
use App\Services\Auth\ManagedEnvironmentAccessScopeResolver;
use App\Services\Auth\TenantMembershipManager;
use App\Services\Auth\ManagedEnvironmentMembershipManager;
use App\Support\Workspaces\WorkspaceContext;
use Filament\Forms;
use Filament\Pages\Tenancy\RegisterTenant as BaseRegisterTenant;
@ -105,7 +105,7 @@ protected function handleRegistration(array $data): Model
->allowedManagedEnvironmentIdsForWorkspace($user, $workspaceId);
if (is_array($explicitScopes)) {
app(TenantMembershipManager::class)->grantScope(
app(ManagedEnvironmentMembershipManager::class)->grantScope(
tenant: $tenant,
actor: $user,
member: $user,

View File

@ -4,7 +4,7 @@
namespace App\Filament\Pages\Workspaces;
use App\Filament\Pages\ChooseTenant;
use App\Filament\Pages\ChooseEnvironment;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Models\Workspace;
@ -17,7 +17,7 @@
use Filament\Pages\Page;
use Illuminate\Database\Eloquent\Collection;
class ManagedTenantsLanding extends Page
class ManagedEnvironmentsLanding extends Page
{
protected static bool $shouldRegisterNavigation = false;
@ -25,7 +25,7 @@ class ManagedTenantsLanding extends Page
protected static string $layout = 'filament-panels::components.layout.simple';
protected string $view = 'filament.pages.workspaces.managed-tenants-landing';
protected string $view = 'filament.pages.workspaces.managed-environments-landing';
public Workspace $workspace;
@ -90,9 +90,9 @@ public function getTenants(): Collection
->values();
}
public function goToChooseTenant(): void
public function goToChooseEnvironment(): void
{
$this->redirect(route('admin.workspace.managed-tenants.index', ['workspace' => $this->workspace]));
$this->redirect(route('admin.workspace.managed-environments.index', ['workspace' => $this->workspace]));
}
public function openTenant(int $tenantId): void

View File

@ -64,8 +64,8 @@ private function captureAction(): Action
: 'Capture baseline';
$modalDescription = $captureMode === BaselineCaptureMode::FullContent
? 'Select the source tenant. This will capture content evidence on demand (redacted) and may take longer depending on scope.'
: 'Select the source tenant whose current inventory will be captured as the baseline snapshot.';
? 'Select the source environment. This will capture content evidence on demand (redacted) and may take longer depending on scope.'
: 'Select the source environment whose current inventory will be captured as the baseline snapshot.';
$action = Action::make('capture')
->label($label)
@ -76,8 +76,8 @@ private function captureAction(): Action
->modalHeading($label)
->modalDescription($modalDescription)
->form([
Select::make('source_tenant_id')
->label('Source ManagedEnvironment')
Select::make('source_environment_id')
->label('Source managed environment')
->options(fn (): array => $this->getWorkspaceTenantOptions())
->required()
->searchable(),
@ -91,11 +91,11 @@ private function captureAction(): Action
/** @var BaselineProfile $profile */
$profile = $this->getRecord();
$sourceTenant = ManagedEnvironment::query()->find((int) $data['source_tenant_id']);
$sourceTenant = ManagedEnvironment::query()->find((int) $data['source_environment_id']);
if (! $sourceTenant instanceof ManagedEnvironment) {
Notification::make()
->title('Source tenant not found')
->title('Source environment not found')
->danger()
->send();
@ -175,8 +175,8 @@ private function compareNowAction(): Action
: 'Compare now';
$modalDescription = $captureMode === BaselineCaptureMode::FullContent
? 'Select the target tenant. This will refresh content evidence on demand (redacted) before comparing.'
: 'Select the target tenant to compare its current inventory against the effective current baseline snapshot.';
? 'Select the target environment. This will refresh content evidence on demand (redacted) before comparing.'
: 'Select the target environment to compare its current inventory against the effective current baseline snapshot.';
return Action::make('compareNow')
->label($label)
@ -187,8 +187,8 @@ private function compareNowAction(): Action
->modalHeading($label)
->modalDescription($modalDescription)
->form([
Select::make('target_tenant_id')
->label('Target ManagedEnvironment')
Select::make('target_environment_id')
->label('Target managed environment')
->options(fn (): array => $this->getEligibleCompareTenantOptions())
->required()
->searchable(),
@ -204,11 +204,11 @@ private function compareNowAction(): Action
/** @var BaselineProfile $profile */
$profile = $this->getRecord();
$targetTenant = ManagedEnvironment::query()->find((int) $data['target_tenant_id']);
$targetTenant = ManagedEnvironment::query()->find((int) $data['target_environment_id']);
if (! $targetTenant instanceof ManagedEnvironment || (int) $targetTenant->workspace_id !== (int) $profile->workspace_id) {
Notification::make()
->title('Target tenant not found')
->title('Target environment not found')
->danger()
->send();

View File

@ -8,16 +8,16 @@
use App\Filament\Concerns\ResolvesPanelTenantContext;
use App\Filament\Concerns\WorkspaceScopedTenantRoutes;
use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
use App\Filament\Resources\TenantReviewResource\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\TenantReview;
use App\Models\TenantReviewSection;
use App\Models\EnvironmentReview;
use App\Models\EnvironmentReviewSection;
use App\Models\User;
use App\Services\ReviewPackService;
use App\Services\TenantReviews\TenantReviewService;
use App\Services\EnvironmentReviews\EnvironmentReviewService;
use App\Support\Auth\Capabilities;
use App\Support\Auth\UiTooltips as AuthUiTooltips;
use App\Support\Badges\BadgeCatalog;
@ -31,8 +31,8 @@
use App\Support\ReasonTranslation\ReasonPresenter;
use App\Support\ReviewPackStatus;
use App\Support\Rbac\UiEnforcement;
use App\Support\TenantReviewCompletenessState;
use App\Support\TenantReviewStatus;
use App\Support\EnvironmentReviewCompletenessState;
use App\Support\EnvironmentReviewStatus;
use App\Support\Ui\ActionSurface\ActionSurfaceDeclaration;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceInspectAffordance;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile;
@ -62,7 +62,7 @@
use Illuminate\Support\Str;
use UnitEnum;
class TenantReviewResource extends Resource
class EnvironmentReviewResource extends Resource
{
use InteractsWithTenantOwnedRecords;
use ResolvesPanelTenantContext;
@ -70,7 +70,7 @@ class TenantReviewResource extends Resource
protected static bool $isDiscovered = false;
protected static ?string $model = TenantReview::class;
protected static ?string $model = EnvironmentReview::class;
protected static ?string $slug = 'reviews';
@ -95,7 +95,7 @@ public static function shouldRegisterNavigation(): bool
public static function getSlug(?Panel $panel = null): string
{
$slug = $panel?->getId() === 'admin'
? 'tenant-reviews'
? 'environment-reviews'
: parent::getSlug($panel);
return static::workspaceScopedSlug($slug, $panel);
@ -134,7 +134,7 @@ public static function canViewAny(): bool
return false;
}
return $user->can(Capabilities::TENANT_REVIEW_VIEW, $tenant);
return $user->can(Capabilities::ENVIRONMENT_REVIEW_VIEW, $tenant);
}
public static function canView(Model $record): bool
@ -142,7 +142,7 @@ public static function canView(Model $record): bool
$tenant = static::resolveTenantContextForCurrentPanel();
$user = auth()->user();
if (! $tenant instanceof ManagedEnvironment || ! $user instanceof User || ! $record instanceof TenantReview) {
if (! $tenant instanceof ManagedEnvironment || ! $user instanceof User || ! $record instanceof EnvironmentReview) {
return false;
}
@ -194,7 +194,7 @@ public static function infolist(Schema $schema): Schema
ViewEntry::make('artifact_truth')
->hiddenLabel()
->view('filament.infolists.entries.governance-artifact-truth')
->state(fn (TenantReview $record): array => static::truthState($record))
->state(fn (EnvironmentReview $record): array => static::truthState($record))
->columnSpanFull(),
])
->columnSpanFull(),
@ -202,30 +202,30 @@ public static function infolist(Schema $schema): Schema
->schema([
TextEntry::make('status')
->badge()
->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantReviewStatus))
->color(BadgeRenderer::color(BadgeDomain::TenantReviewStatus))
->icon(BadgeRenderer::icon(BadgeDomain::TenantReviewStatus))
->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantReviewStatus)),
->formatStateUsing(BadgeRenderer::label(BadgeDomain::EnvironmentReviewStatus))
->color(BadgeRenderer::color(BadgeDomain::EnvironmentReviewStatus))
->icon(BadgeRenderer::icon(BadgeDomain::EnvironmentReviewStatus))
->iconColor(BadgeRenderer::iconColor(BadgeDomain::EnvironmentReviewStatus)),
TextEntry::make('completeness_state')
->label(__('localization.review.completeness'))
->badge()
->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantReviewCompleteness))
->color(BadgeRenderer::color(BadgeDomain::TenantReviewCompleteness))
->icon(BadgeRenderer::icon(BadgeDomain::TenantReviewCompleteness))
->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantReviewCompleteness)),
->formatStateUsing(BadgeRenderer::label(BadgeDomain::EnvironmentReviewCompleteness))
->color(BadgeRenderer::color(BadgeDomain::EnvironmentReviewCompleteness))
->icon(BadgeRenderer::icon(BadgeDomain::EnvironmentReviewCompleteness))
->iconColor(BadgeRenderer::iconColor(BadgeDomain::EnvironmentReviewCompleteness)),
TextEntry::make('tenant.name')->label(__('localization.review.tenant')),
TextEntry::make('generated_at')->dateTime()->placeholder('—'),
TextEntry::make('published_at')->dateTime()->placeholder('—'),
TextEntry::make('evidenceSnapshot.id')
->label(__('localization.review.evidence_snapshot'))
->formatStateUsing(fn (?int $state): string => $state ? '#'.$state : '—')
->url(fn (TenantReview $record): ?string => $record->evidenceSnapshot
->url(fn (EnvironmentReview $record): ?string => $record->evidenceSnapshot
? EvidenceSnapshotResource::getUrl('view', ['record' => $record->evidenceSnapshot], tenant: $record->tenant)
: null),
TextEntry::make('currentExportReviewPack.id')
->label(__('localization.review.current_export'))
->formatStateUsing(fn (?int $state): string => $state ? '#'.$state : '—')
->url(fn (TenantReview $record): ?string => $record->currentExportReviewPack
->url(fn (EnvironmentReview $record): ?string => $record->currentExportReviewPack
? ReviewPackResource::getUrl('view', ['record' => $record->currentExportReviewPack], tenant: $record->tenant)
: null),
TextEntry::make('fingerprint')
@ -242,32 +242,32 @@ public static function infolist(Schema $schema): Schema
->schema([
ViewEntry::make('review_summary')
->hiddenLabel()
->view('filament.infolists.entries.tenant-review-summary')
->state(fn (TenantReview $record): array => static::summaryPresentation($record))
->view('filament.infolists.entries.environment-review-summary')
->state(fn (EnvironmentReview $record): array => static::summaryPresentation($record))
->columnSpanFull(),
])
->columnSpanFull(),
Section::make(__('localization.review.sections'))
->schema([
RepeatableEntry::make('sections')
->state(fn (TenantReview $record): array => static::visibleSections($record))
->state(fn (EnvironmentReview $record): array => static::visibleSections($record))
->hiddenLabel()
->schema([
TextEntry::make('title'),
TextEntry::make('completeness_state')
->label(__('localization.review.completeness'))
->badge()
->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantReviewCompleteness))
->color(BadgeRenderer::color(BadgeDomain::TenantReviewCompleteness))
->icon(BadgeRenderer::icon(BadgeDomain::TenantReviewCompleteness))
->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantReviewCompleteness)),
->formatStateUsing(BadgeRenderer::label(BadgeDomain::EnvironmentReviewCompleteness))
->color(BadgeRenderer::color(BadgeDomain::EnvironmentReviewCompleteness))
->icon(BadgeRenderer::icon(BadgeDomain::EnvironmentReviewCompleteness))
->iconColor(BadgeRenderer::iconColor(BadgeDomain::EnvironmentReviewCompleteness)),
TextEntry::make('measured_at')->dateTime()->placeholder('—'),
Section::make(__('localization.review.details'))
->schema([
ViewEntry::make('section_payload')
->hiddenLabel()
->view('filament.infolists.entries.tenant-review-section')
->state(fn (TenantReviewSection $record): array => static::sectionPresentation($record))
->view('filament.infolists.entries.environment-review-section')
->state(fn (EnvironmentReviewSection $record): array => static::sectionPresentation($record))
->columnSpanFull(),
])
->collapsible()
@ -281,12 +281,12 @@ public static function infolist(Schema $schema): Schema
}
/**
* @return array<int, TenantReviewSection>
* @return array<int, EnvironmentReviewSection>
*/
private static function visibleSections(TenantReview $record): array
private static function visibleSections(EnvironmentReview $record): array
{
return $record->sections
->reject(fn (TenantReviewSection $section): bool => static::isCustomerWorkspaceMode() && $section->isControlInterpretation())
->reject(fn (EnvironmentReviewSection $section): bool => static::isCustomerWorkspaceMode() && $section->isControlInterpretation())
->values()
->all();
}
@ -297,43 +297,43 @@ public static function table(Table $table): Table
Actions\Action::make('export_executive_pack')
->label(__('localization.review.export_executive_pack'))
->icon('heroicon-o-arrow-down-tray')
->visible(fn (TenantReview $record): bool => in_array($record->status, [
TenantReviewStatus::Ready->value,
TenantReviewStatus::Published->value,
->visible(fn (EnvironmentReview $record): bool => in_array($record->status, [
EnvironmentReviewStatus::Ready->value,
EnvironmentReviewStatus::Published->value,
], true))
->disabled(fn (TenantReview $record): bool => static::reviewPackGenerationBlocked($record->tenant))
->action(fn (TenantReview $record): mixed => static::executeExport($record)),
fn (TenantReview $record): TenantReview => $record,
->disabled(fn (EnvironmentReview $record): bool => static::reviewPackGenerationBlocked($record->tenant))
->action(fn (EnvironmentReview $record): mixed => static::executeExport($record)),
fn (EnvironmentReview $record): EnvironmentReview => $record,
)
->requireCapability(Capabilities::TENANT_REVIEW_MANAGE)
->requireCapability(Capabilities::ENVIRONMENT_REVIEW_MANAGE)
->preserveVisibility()
->preserveDisabled()
->apply();
$exportExecutivePackAction->tooltip(fn (TenantReview $record): ?string => static::reviewPackGenerationActionTooltip($record->tenant));
$exportExecutivePackAction->tooltip(fn (EnvironmentReview $record): ?string => static::reviewPackGenerationActionTooltip($record->tenant));
return $table
->defaultSort('generated_at', 'desc')
->persistFiltersInSession()
->persistSearchInSession()
->persistSortInSession()
->recordUrl(fn (TenantReview $record): string => static::tenantScopedUrl('view', ['record' => $record], $record->tenant))
->recordUrl(fn (EnvironmentReview $record): string => static::tenantScopedUrl('view', ['record' => $record], $record->tenant))
->columns([
Tables\Columns\TextColumn::make('status')
->badge()
->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantReviewStatus))
->color(BadgeRenderer::color(BadgeDomain::TenantReviewStatus))
->icon(BadgeRenderer::icon(BadgeDomain::TenantReviewStatus))
->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantReviewStatus))
->formatStateUsing(BadgeRenderer::label(BadgeDomain::EnvironmentReviewStatus))
->color(BadgeRenderer::color(BadgeDomain::EnvironmentReviewStatus))
->icon(BadgeRenderer::icon(BadgeDomain::EnvironmentReviewStatus))
->iconColor(BadgeRenderer::iconColor(BadgeDomain::EnvironmentReviewStatus))
->sortable(),
Tables\Columns\TextColumn::make('outcome')
->label(__('localization.review.outcome'))
->badge()
->getStateUsing(fn (TenantReview $record): string => static::compressedOutcome($record)->primaryLabel)
->color(fn (TenantReview $record): string => static::compressedOutcome($record)->primaryBadge->color)
->icon(fn (TenantReview $record): ?string => static::compressedOutcome($record)->primaryBadge->icon)
->iconColor(fn (TenantReview $record): ?string => static::compressedOutcome($record)->primaryBadge->iconColor)
->description(fn (TenantReview $record): ?string => static::compressedOutcome($record)->primaryReason)
->getStateUsing(fn (EnvironmentReview $record): string => static::compressedOutcome($record)->primaryLabel)
->color(fn (EnvironmentReview $record): string => static::compressedOutcome($record)->primaryBadge->color)
->icon(fn (EnvironmentReview $record): ?string => static::compressedOutcome($record)->primaryBadge->icon)
->iconColor(fn (EnvironmentReview $record): ?string => static::compressedOutcome($record)->primaryBadge->iconColor)
->description(fn (EnvironmentReview $record): ?string => static::compressedOutcome($record)->primaryReason)
->wrap(),
Tables\Columns\TextColumn::make('generated_at')->dateTime()->placeholder('—')->sortable(),
Tables\Columns\TextColumn::make('published_at')->dateTime()->placeholder('—')->sortable(),
@ -342,7 +342,7 @@ public static function table(Table $table): Table
->boolean(),
Tables\Columns\TextColumn::make('next_step')
->label(__('localization.review.next_step'))
->getStateUsing(fn (TenantReview $record): string => static::compressedOutcome($record)->nextActionText)
->getStateUsing(fn (EnvironmentReview $record): string => static::compressedOutcome($record)->nextActionText)
->wrap(),
Tables\Columns\TextColumn::make('fingerprint')
->toggleable(isToggledHiddenByDefault: true)
@ -350,18 +350,18 @@ public static function table(Table $table): Table
])
->filters([
Tables\Filters\SelectFilter::make('status')
->options(collect(TenantReviewStatus::cases())
->mapWithKeys(fn (TenantReviewStatus $status): array => [$status->value => Str::headline($status->value)])
->options(collect(EnvironmentReviewStatus::cases())
->mapWithKeys(fn (EnvironmentReviewStatus $status): array => [$status->value => Str::headline($status->value)])
->all()),
Tables\Filters\SelectFilter::make('completeness_state')
->options(BadgeCatalog::options(BadgeDomain::TenantReviewCompleteness, TenantReviewCompletenessState::values())),
->options(BadgeCatalog::options(BadgeDomain::EnvironmentReviewCompleteness, EnvironmentReviewCompletenessState::values())),
\App\Support\Filament\FilterPresets::dateRange('review_date', __('localization.review.review_date'), 'generated_at'),
])
->actions([
$exportExecutivePackAction,
])
->bulkActions([])
->emptyStateHeading(__('localization.review.no_tenant_reviews_yet'))
->emptyStateHeading(__('localization.review.no_environment_reviews_yet'))
->emptyStateDescription(__('localization.review.create_first_review_description'))
->emptyStateActions([
static::makeCreateReviewAction(
@ -375,8 +375,8 @@ public static function table(Table $table): Table
public static function getPages(): array
{
return [
'index' => Pages\ListTenantReviews::route('/'),
'view' => Pages\ViewTenantReview::route('/{record}'),
'index' => Pages\ListEnvironmentReviews::route('/'),
'view' => Pages\ViewEnvironmentReview::route('/{record}'),
];
}
@ -406,7 +406,7 @@ public static function makeCreateReviewAction(
])
->action(fn (array $data): mixed => static::executeCreateReview($data)),
)
->requireCapability(Capabilities::TENANT_REVIEW_MANAGE)
->requireCapability(Capabilities::ENVIRONMENT_REVIEW_MANAGE)
->apply();
}
@ -428,7 +428,7 @@ public static function executeCreateReview(array $data): void
abort(404);
}
if (! $user->can(Capabilities::TENANT_REVIEW_MANAGE, $tenant)) {
if (! $user->can(Capabilities::ENVIRONMENT_REVIEW_MANAGE, $tenant)) {
abort(403);
}
@ -447,7 +447,7 @@ public static function executeCreateReview(array $data): void
}
try {
$review = app(TenantReviewService::class)->create($tenant, $snapshot, $user);
$review = app(EnvironmentReviewService::class)->create($tenant, $snapshot, $user);
} catch (\Throwable $throwable) {
Notification::make()->danger()->title(__('localization.review.unable_create_review'))->body($throwable->getMessage())->send();
@ -471,7 +471,7 @@ public static function executeCreateReview(array $data): void
return;
}
$toast = OperationUxPresenter::queuedToast(OperationRunType::TenantReviewCompose->value)
$toast = OperationUxPresenter::queuedToast(OperationRunType::EnvironmentReviewCompose->value)
->body(__('localization.review.review_composing_background'));
if ($review->operation_run_id) {
@ -535,7 +535,7 @@ public static function reviewPackGenerationActionTooltip(?ManagedEnvironment $te
$tenant ??= static::panelTenantContext();
$user = auth()->user();
if ($tenant instanceof ManagedEnvironment && $user instanceof User && ! $user->can(Capabilities::TENANT_REVIEW_MANAGE, $tenant)) {
if ($tenant instanceof ManagedEnvironment && $user instanceof User && ! $user->can(Capabilities::ENVIRONMENT_REVIEW_MANAGE, $tenant)) {
return AuthUiTooltips::insufficientPermission();
}
@ -543,7 +543,7 @@ public static function reviewPackGenerationActionTooltip(?ManagedEnvironment $te
?? static::reviewPackGenerationWarningReason($tenant);
}
public static function executeExport(TenantReview $review): void
public static function executeExport(EnvironmentReview $review): void
{
$review->loadMissing(['tenant', 'currentExportReviewPack']);
$user = auth()->user();
@ -654,13 +654,13 @@ private static function evidenceSnapshotOptions(): array
private static function reviewCompletenessCountLabel(string $state): string
{
return BadgeCatalog::spec(BadgeDomain::TenantReviewCompleteness, $state)->label;
return BadgeCatalog::spec(BadgeDomain::EnvironmentReviewCompleteness, $state)->label;
}
/**
* @return array<string, mixed>
*/
private static function summaryPresentation(TenantReview $record): array
private static function summaryPresentation(EnvironmentReview $record): array
{
$summary = is_array($record->summary) ? $record->summary : [];
$truthEnvelope = static::truthEnvelope($record);
@ -706,7 +706,7 @@ private static function summaryPresentation(TenantReview $record): array
* @param array<string, mixed> $packagePresentation
* @return array<int, array{label:string,value:string}>
*/
private static function customerWorkspaceMetrics(TenantReview $record, array $summary, array $packagePresentation): array
private static function customerWorkspaceMetrics(EnvironmentReview $record, array $summary, array $packagePresentation): array
{
$acceptedRisk = is_array($summary['risk_acceptance'] ?? null) ? $summary['risk_acceptance'] : [];
@ -719,9 +719,9 @@ private static function customerWorkspaceMetrics(TenantReview $record, array $su
];
}
private static function customerReviewStatusLabel(TenantReview $record): string
private static function customerReviewStatusLabel(EnvironmentReview $record): string
{
if ($record->isPublished() && (string) $record->completeness_state === TenantReviewCompletenessState::Complete->value) {
if ($record->isPublished() && (string) $record->completeness_state === EnvironmentReviewCompletenessState::Complete->value) {
return __('localization.review.review_completed');
}
@ -732,7 +732,7 @@ private static function customerReviewStatusLabel(TenantReview $record): string
return Str::headline((string) $record->status);
}
private static function customerEvidenceStatusLabel(TenantReview $record): string
private static function customerEvidenceStatusLabel(EnvironmentReview $record): string
{
$snapshot = $record->evidenceSnapshot;
$tenant = $record->tenant;
@ -775,7 +775,7 @@ private static function customerAcceptedRiskStatusLabel(array $acceptedRisk): st
/**
* @return array<string, mixed>
*/
private static function governancePackagePresentation(TenantReview $record): array
private static function governancePackagePresentation(EnvironmentReview $record): array
{
$summary = is_array($record->summary) ? $record->summary : [];
$package = is_array($summary['governance_package'] ?? null) ? $summary['governance_package'] : [];
@ -793,7 +793,7 @@ private static function governancePackagePresentation(TenantReview $record): arr
/**
* @return array{state:string,label:string,description:string}
*/
private static function governancePackageAvailability(TenantReview $record): array
private static function governancePackageAvailability(EnvironmentReview $record): array
{
$pack = $record->currentExportReviewPack;
$tenant = $record->tenant;
@ -801,8 +801,8 @@ private static function governancePackageAvailability(TenantReview $record): arr
$controlInterpretation = $record->controlInterpretation();
$limitations = is_array($controlInterpretation['limitations'] ?? null) ? $controlInterpretation['limitations'] : [];
$isPartialReview = in_array((string) $record->completeness_state, [
TenantReviewCompletenessState::Partial->value,
TenantReviewCompletenessState::Stale->value,
EnvironmentReviewCompletenessState::Partial->value,
EnvironmentReviewCompletenessState::Stale->value,
], true) || $limitations !== [];
if (! $pack instanceof ReviewPack) {
@ -855,7 +855,7 @@ private static function governancePackageAvailability(TenantReview $record): arr
/**
* @return array<int, array{title:string,label:string,url:?string,description:string}>
*/
private static function summaryContextLinks(TenantReview $record, bool $customerWorkspaceMode = false): array
private static function summaryContextLinks(EnvironmentReview $record, bool $customerWorkspaceMode = false): array
{
$links = [];
@ -913,15 +913,15 @@ private static function summaryContextLinks(TenantReview $record, bool $customer
/**
* @return array<string, mixed>
*/
private static function sectionPresentation(TenantReviewSection $section): array
private static function sectionPresentation(EnvironmentReviewSection $section): array
{
$summary = is_array($section->summary_payload) ? $section->summary_payload : [];
$render = is_array($section->render_payload) ? $section->render_payload : [];
$review = $section->tenantReview;
$review = $section->environmentReview;
$tenant = $section->tenant;
$links = [];
if ($section->isControlInterpretation() && $review instanceof TenantReview && $tenant instanceof ManagedEnvironment && $review->evidenceSnapshot instanceof EvidenceSnapshot) {
if ($section->isControlInterpretation() && $review instanceof EnvironmentReview && $tenant instanceof ManagedEnvironment && $review->evidenceSnapshot instanceof EvidenceSnapshot) {
$user = auth()->user();
if ($user instanceof User && $user->can(Capabilities::EVIDENCE_VIEW, $tenant)) {
@ -960,34 +960,34 @@ private static function sectionPresentation(TenantReviewSection $section): array
];
}
private static function truthEnvelope(TenantReview $record, bool $fresh = false): ArtifactTruthEnvelope
private static function truthEnvelope(EnvironmentReview $record, bool $fresh = false): ArtifactTruthEnvelope
{
$presenter = app(ArtifactTruthPresenter::class);
return $fresh
? $presenter->forTenantReviewFresh($record)
: $presenter->forTenantReview($record);
? $presenter->forEnvironmentReviewFresh($record)
: $presenter->forEnvironmentReview($record);
}
/**
* @return array<string, mixed>
*/
private static function truthState(TenantReview $record, bool $fresh = false): array
private static function truthState(EnvironmentReview $record, bool $fresh = false): array
{
$presenter = app(ArtifactTruthPresenter::class);
return $presenter->surfaceStateFor($record, SurfaceCompressionContext::tenantReview(), $fresh)
return $presenter->surfaceStateFor($record, SurfaceCompressionContext::environmentReview(), $fresh)
?? static::truthEnvelope($record, $fresh)->toArray(static::compressedOutcome($record, $fresh));
}
private static function compressedOutcome(TenantReview $record, bool $fresh = false): CompressedGovernanceOutcome
private static function compressedOutcome(EnvironmentReview $record, bool $fresh = false): CompressedGovernanceOutcome
{
$presenter = app(ArtifactTruthPresenter::class);
return $presenter->compressedOutcomeFor($record, SurfaceCompressionContext::tenantReview(), $fresh)
return $presenter->compressedOutcomeFor($record, SurfaceCompressionContext::environmentReview(), $fresh)
?? $presenter->compressedOutcomeFromEnvelope(
static::truthEnvelope($record, $fresh),
SurfaceCompressionContext::tenantReview(),
SurfaceCompressionContext::environmentReview(),
);
}
@ -1013,7 +1013,7 @@ private static function isCustomerWorkspaceMode(): bool
/**
* @return array<string, mixed>
*/
private static function customerWorkspaceEvidenceQuery(TenantReview $record): array
private static function customerWorkspaceEvidenceQuery(EnvironmentReview $record): array
{
return array_filter([
'source_surface' => CustomerReviewWorkspace::SOURCE_SURFACE,

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace App\Filament\Resources\EnvironmentReviewResource\Pages;
use App\Filament\Resources\EnvironmentReviewResource;
use Filament\Resources\Pages\ListRecords;
class ListEnvironmentReviews extends ListRecords
{
protected static string $resource = EnvironmentReviewResource::class;
protected function getHeaderActions(): array
{
return [
EnvironmentReviewResource::makeCreateReviewAction(),
];
}
}

View File

@ -2,23 +2,23 @@
declare(strict_types=1);
namespace App\Filament\Resources\TenantReviewResource\Pages;
namespace App\Filament\Resources\EnvironmentReviewResource\Pages;
use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
use App\Filament\Resources\TenantReviewResource;
use App\Filament\Resources\EnvironmentReviewResource;
use App\Models\ReviewPack;
use App\Models\ManagedEnvironment;
use App\Models\TenantReview;
use App\Models\EnvironmentReview;
use App\Models\User;
use App\Services\ReviewPackService;
use App\Services\Audit\WorkspaceAuditLogger;
use App\Services\TenantReviews\TenantReviewLifecycleService;
use App\Services\TenantReviews\TenantReviewService;
use App\Services\EnvironmentReviews\EnvironmentReviewLifecycleService;
use App\Services\EnvironmentReviews\EnvironmentReviewService;
use App\Support\Audit\AuditActionId;
use App\Support\Auth\Capabilities;
use App\Support\Rbac\UiEnforcement;
use App\Support\ReviewPackStatus;
use App\Support\TenantReviewStatus;
use App\Support\EnvironmentReviewStatus;
use App\Support\Ui\GovernanceActions\GovernanceActionCatalog;
use Filament\Actions;
use Filament\Forms\Components\Textarea;
@ -26,9 +26,9 @@
use Filament\Resources\Pages\ViewRecord;
use Illuminate\Database\Eloquent\Model;
class ViewTenantReview extends ViewRecord
class ViewEnvironmentReview extends ViewRecord
{
protected static string $resource = TenantReviewResource::class;
protected static string $resource = EnvironmentReviewResource::class;
public function mount(int|string $record): void
{
@ -39,16 +39,16 @@ public function mount(int|string $record): void
protected function resolveRecord(int|string $key): Model
{
return TenantReviewResource::resolveScopedRecordOrFail($key);
return EnvironmentReviewResource::resolveScopedRecordOrFail($key);
}
protected function authorizeAccess(): void
{
$tenant = TenantReviewResource::panelTenantContext();
$tenant = EnvironmentReviewResource::panelTenantContext();
$record = $this->getRecord();
$user = auth()->user();
if (! $user instanceof User || ! $tenant instanceof ManagedEnvironment || ! $record instanceof TenantReview) {
if (! $user instanceof User || ! $tenant instanceof ManagedEnvironment || ! $record instanceof EnvironmentReview) {
abort(404);
}
@ -108,11 +108,11 @@ private function primaryLifecycleActionName(): ?string
return null;
}
if ((string) $this->record->status === TenantReviewStatus::Published->value) {
if ((string) $this->record->status === EnvironmentReviewStatus::Published->value) {
return 'export_executive_pack';
}
if ((string) $this->record->status === TenantReviewStatus::Ready->value) {
if ((string) $this->record->status === EnvironmentReviewStatus::Ready->value) {
return 'publish_review';
}
@ -157,8 +157,8 @@ private function secondaryLifecycleActionNames(): array
}
if (in_array((string) $this->record->status, [
TenantReviewStatus::Ready->value,
TenantReviewStatus::Published->value,
EnvironmentReviewStatus::Ready->value,
EnvironmentReviewStatus::Published->value,
], true)) {
$names[] = 'export_executive_pack';
}
@ -194,7 +194,7 @@ private function refreshReviewAction(): Actions\Action
}
try {
app(TenantReviewService::class)->refresh($this->record, $user);
app(EnvironmentReviewService::class)->refresh($this->record, $user);
} catch (\Throwable $throwable) {
Notification::make()->danger()->title('Unable to refresh review')->body($throwable->getMessage())->send();
@ -204,7 +204,7 @@ private function refreshReviewAction(): Actions\Action
Notification::make()->success()->title($rule->successTitle)->send();
}),
)
->requireCapability(Capabilities::TENANT_REVIEW_MANAGE)
->requireCapability(Capabilities::ENVIRONMENT_REVIEW_MANAGE)
->apply();
}
@ -236,7 +236,7 @@ private function publishReviewAction(): Actions\Action
}
try {
app(TenantReviewLifecycleService::class)->publish(
app(EnvironmentReviewLifecycleService::class)->publish(
$this->record,
$user,
(string) ($data['publish_reason'] ?? ''),
@ -251,7 +251,7 @@ private function publishReviewAction(): Actions\Action
Notification::make()->success()->title($rule->successTitle)->send();
}),
)
->requireCapability(Capabilities::TENANT_REVIEW_MANAGE)
->requireCapability(Capabilities::ENVIRONMENT_REVIEW_MANAGE)
->preserveVisibility()
->apply();
}
@ -264,18 +264,18 @@ private function exportExecutivePackAction(): Actions\Action
->icon('heroicon-o-arrow-down-tray')
->color('primary')
->hidden(fn (): bool => ! in_array((string) $this->record->status, [
TenantReviewStatus::Ready->value,
TenantReviewStatus::Published->value,
EnvironmentReviewStatus::Ready->value,
EnvironmentReviewStatus::Published->value,
], true))
->disabled(fn (): bool => TenantReviewResource::reviewPackGenerationBlocked($this->record->tenant))
->action(fn (): mixed => TenantReviewResource::executeExport($this->record)),
->disabled(fn (): bool => EnvironmentReviewResource::reviewPackGenerationBlocked($this->record->tenant))
->action(fn (): mixed => EnvironmentReviewResource::executeExport($this->record)),
)
->requireCapability(Capabilities::TENANT_REVIEW_MANAGE)
->requireCapability(Capabilities::ENVIRONMENT_REVIEW_MANAGE)
->preserveVisibility()
->preserveDisabled()
->apply();
$action->tooltip(fn (): ?string => TenantReviewResource::reviewPackGenerationActionTooltip($this->record->tenant));
$action->tooltip(fn (): ?string => EnvironmentReviewResource::reviewPackGenerationActionTooltip($this->record->tenant));
return $action;
}
@ -295,17 +295,17 @@ private function createNextReviewAction(): Actions\Action
}
try {
$nextReview = app(TenantReviewLifecycleService::class)->createNextReview($this->record, $user);
$nextReview = app(EnvironmentReviewLifecycleService::class)->createNextReview($this->record, $user);
} catch (\Throwable $throwable) {
Notification::make()->danger()->title('Unable to create next review')->body($throwable->getMessage())->send();
return;
}
$this->redirect(TenantReviewResource::tenantScopedUrl('view', ['record' => $nextReview], $nextReview->tenant));
$this->redirect(EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $nextReview], $nextReview->tenant));
}),
)
->requireCapability(Capabilities::TENANT_REVIEW_MANAGE)
->requireCapability(Capabilities::ENVIRONMENT_REVIEW_MANAGE)
->preserveVisibility()
->apply();
}
@ -337,7 +337,7 @@ private function archiveReviewAction(): Actions\Action
abort(403);
}
app(TenantReviewLifecycleService::class)->archive(
app(EnvironmentReviewLifecycleService::class)->archive(
$this->record,
$user,
(string) ($data['archive_reason'] ?? ''),
@ -347,7 +347,7 @@ private function archiveReviewAction(): Actions\Action
Notification::make()->success()->title($rule->successTitle)->send();
}),
)
->requireCapability(Capabilities::TENANT_REVIEW_MANAGE)
->requireCapability(Capabilities::ENVIRONMENT_REVIEW_MANAGE)
->preserveVisibility()
->apply();
}
@ -443,7 +443,7 @@ private function auditCustomerWorkspaceOpen(): void
app(WorkspaceAuditLogger::class)->log(
workspace: $tenant->workspace,
action: AuditActionId::TenantReviewOpened,
action: AuditActionId::EnvironmentReviewOpened,
context: [
'metadata' => [
'review_id' => (int) $this->record->getKey(),
@ -453,7 +453,7 @@ private function auditCustomerWorkspaceOpen(): void
],
],
actor: $user,
resourceType: 'tenant_review',
resourceType: 'environment_review',
resourceId: (string) $this->record->getKey(),
targetLabel: sprintf('ManagedEnvironment review #%d', (int) $this->record->getKey()),
tenant: $tenant,

View File

@ -681,7 +681,7 @@ public static function approvalQueueUrl(?ManagedEnvironment $tenant = null): ?st
}
return route('admin.finding-exceptions.open-queue', [
'tenant' => (string) $tenant->external_id,
'environment' => (string) $tenant->external_id,
]);
}
}

View File

@ -6,7 +6,7 @@
use App\Filament\Resources\FindingExceptionResource;
use App\Filament\Resources\FindingResource;
use App\Filament\Widgets\Tenant\FindingExceptionStatsOverview;
use App\Filament\Widgets\ManagedEnvironment\FindingExceptionStatsOverview;
use App\Models\FindingException;
use Filament\Actions\Action;
use Filament\Resources\Pages\ListRecords;

View File

@ -4,8 +4,8 @@
use App\Filament\Concerns\ResolvesPanelTenantContext;
use App\Filament\Resources\FindingResource;
use App\Filament\Widgets\Tenant\BaselineCompareCoverageBanner;
use App\Filament\Widgets\Tenant\FindingStatsOverview;
use App\Filament\Widgets\ManagedEnvironment\BaselineCompareCoverageBanner;
use App\Filament\Widgets\ManagedEnvironment\FindingStatsOverview;
use App\Models\Finding;
use App\Models\ManagedEnvironment;
use App\Models\User;

View File

@ -2,9 +2,9 @@
namespace App\Filament\Resources;
use App\Filament\Pages\CrossTenantComparePage;
use App\Filament\Resources\TenantResource\Pages;
use App\Filament\Resources\TenantResource\RelationManagers;
use App\Filament\Pages\CrossEnvironmentComparePage;
use App\Filament\Resources\ManagedEnvironmentResource\Pages;
use App\Filament\Resources\ManagedEnvironmentResource\RelationManagers;
use App\Http\Controllers\RbacDelegatedAuthController;
use App\Jobs\BulkTenantSyncJob;
use App\Jobs\SyncPoliciesJob;
@ -12,8 +12,8 @@
use App\Models\EntraRoleDefinition;
use App\Models\ProviderConnection;
use App\Models\ManagedEnvironment;
use App\Models\TenantOnboardingSession;
use App\Models\TenantTriageReview;
use App\Models\ManagedEnvironmentOnboardingSession;
use App\Models\ManagedEnvironmentTriageReview;
use App\Models\User;
use App\Models\Workspace;
use App\Services\Audit\WorkspaceAuditLogger;
@ -26,7 +26,7 @@
use App\Services\Intune\RbacOnboardingService;
use App\Services\OperationRunService;
use App\Services\Operations\BulkSelectionIdentity;
use App\Services\PortfolioTriage\TenantTriageReviewService;
use App\Services\PortfolioTriage\ManagedEnvironmentTriageReviewService;
use App\Services\Providers\AdminConsentUrlFactory;
use App\Services\Tenants\TenantActionPolicySurface;
use App\Services\Tenants\TenantOperabilityService;
@ -49,7 +49,7 @@
use App\Support\OpsUx\OpsUxBrowserEvents;
use App\Support\Navigation\CanonicalNavigationContext;
use App\Support\PortfolioTriage\PortfolioArrivalContextToken;
use App\Support\PortfolioTriage\TenantTriageReviewStateResolver;
use App\Support\PortfolioTriage\ManagedEnvironmentTriageReviewStateResolver;
use App\Support\Rbac\UiEnforcement;
use App\Support\Navigation\RelatedContextEntry;
use App\Support\Navigation\UnavailableRelationState;
@ -93,7 +93,7 @@
use Illuminate\Support\Str;
use UnitEnum;
class TenantResource extends Resource
class ManagedEnvironmentResource extends Resource
{
// ... [Properties Omitted for Brevity] ...
protected static ?string $model = ManagedEnvironment::class;
@ -119,7 +119,7 @@ class TenantResource extends Resource
private const string POSTURE_SNAPSHOT_REQUEST_KEY = 'tenant_resource.posture_snapshot';
private const string TRIAGE_REVIEW_SNAPSHOT_REQUEST_KEY = 'tenant_resource.triage_review_snapshot';
private const string TRIAGE_REVIEW_SNAPSHOT_REQUEST_KEY = 'managed_environment_resource.triage_review_snapshot';
/**
* @var array<string, true>
@ -297,7 +297,7 @@ public static function makeTenantViewMarkReviewedAction(): Actions\Action
->modalDescription(fn (ManagedEnvironment $record): string => static::triageReviewActionModalDescription(
$record,
static::tenantViewTriageState(),
TenantTriageReview::STATE_REVIEWED,
ManagedEnvironmentTriageReview::STATE_REVIEWED,
))
->visible(fn (ManagedEnvironment $record): bool => static::selectedActionTriageReviewRowForTenant(
$record,
@ -308,11 +308,11 @@ public static function makeTenantViewMarkReviewedAction(): Actions\Action
->before(function (ManagedEnvironment $record): void {
static::authorizeTriageReviewAction($record);
})
->action(function (ManagedEnvironment $record, TenantTriageReviewService $service): void {
->action(function (ManagedEnvironment $record, ManagedEnvironmentTriageReviewService $service): void {
static::handleTriageReviewMutation(
tenant: $record,
triageState: static::tenantViewTriageState(),
targetManualState: TenantTriageReview::STATE_REVIEWED,
targetManualState: ManagedEnvironmentTriageReview::STATE_REVIEWED,
service: $service,
);
});
@ -329,7 +329,7 @@ public static function makeTenantViewMarkFollowUpNeededAction(): Actions\Action
->modalDescription(fn (ManagedEnvironment $record): string => static::triageReviewActionModalDescription(
$record,
static::tenantViewTriageState(),
TenantTriageReview::STATE_FOLLOW_UP_NEEDED,
ManagedEnvironmentTriageReview::STATE_FOLLOW_UP_NEEDED,
))
->visible(fn (ManagedEnvironment $record): bool => static::selectedActionTriageReviewRowForTenant(
$record,
@ -340,11 +340,11 @@ public static function makeTenantViewMarkFollowUpNeededAction(): Actions\Action
->before(function (ManagedEnvironment $record): void {
static::authorizeTriageReviewAction($record);
})
->action(function (ManagedEnvironment $record, TenantTriageReviewService $service): void {
->action(function (ManagedEnvironment $record, ManagedEnvironmentTriageReviewService $service): void {
static::handleTriageReviewMutation(
tenant: $record,
triageState: static::tenantViewTriageState(),
targetManualState: TenantTriageReview::STATE_FOLLOW_UP_NEEDED,
targetManualState: ManagedEnvironmentTriageReview::STATE_FOLLOW_UP_NEEDED,
service: $service,
);
});
@ -826,10 +826,10 @@ public static function table(Table $table): Table
$record,
static::currentPortfolioTriageState($livewire),
)['derived_state'] ?? null)
->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantTriageReviewState))
->color(BadgeRenderer::color(BadgeDomain::TenantTriageReviewState))
->icon(BadgeRenderer::icon(BadgeDomain::TenantTriageReviewState))
->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantTriageReviewState))
->formatStateUsing(BadgeRenderer::label(BadgeDomain::ManagedEnvironmentTriageReviewState))
->color(BadgeRenderer::color(BadgeDomain::ManagedEnvironmentTriageReviewState))
->icon(BadgeRenderer::icon(BadgeDomain::ManagedEnvironmentTriageReviewState))
->iconColor(BadgeRenderer::iconColor(BadgeDomain::ManagedEnvironmentTriageReviewState))
->description(fn (ManagedEnvironment $record, mixed $livewire): ?string => static::triageReviewDescriptionForTenant(
$record,
static::currentPortfolioTriageState($livewire),
@ -953,7 +953,7 @@ public static function table(Table $table): Table
return '#';
}
$triageState = $livewire instanceof Pages\ListTenants
$triageState = $livewire instanceof Pages\ListManagedEnvironments
? static::currentPortfolioTriageState($livewire)
: [];
@ -976,12 +976,12 @@ public static function table(Table $table): Table
->url(fn (ManagedEnvironment $record): string => static::relatedOnboardingDraftUrl($record) ?? route('admin.onboarding'))
->visible(fn (ManagedEnvironment $record): bool => static::relatedOnboardingDraftAction($record, TenantActionSurface::TenantIndexRow) instanceof TenantActionDescriptor
&& static::tenantIndexPrimaryAction($record)?->key !== 'related_onboarding'),
Actions\Action::make('compareTenants')
Actions\Action::make('compareEnvironments')
->label('Compare tenants')
->icon('heroicon-o-scale')
->color('gray')
->url(function (ManagedEnvironment $record, mixed $livewire): string {
$triageState = $livewire instanceof Pages\ListTenants
$triageState = $livewire instanceof Pages\ListManagedEnvironments
? static::currentPortfolioTriageState($livewire)
: [];
@ -994,9 +994,9 @@ public static function table(Table $table): Table
$triageState = static::portfolioReturnFiltersFromRequest(request()->query());
}
return static::crossTenantCompareOpenUrl($record, $triageState);
return static::crossEnvironmentCompareOpenUrl($record, $triageState);
})
->visible(fn (ManagedEnvironment $record): bool => static::crossTenantCompareActionVisible($record)),
->visible(fn (ManagedEnvironment $record): bool => static::crossEnvironmentCompareActionVisible($record)),
UiEnforcement::forAction(
Actions\Action::make('edit')
->label('Edit')
@ -1019,7 +1019,7 @@ public static function table(Table $table): Table
->modalDescription(fn (ManagedEnvironment $record, mixed $livewire): string => static::triageReviewActionModalDescription(
$record,
static::currentPortfolioTriageState($livewire),
TenantTriageReview::STATE_REVIEWED,
ManagedEnvironmentTriageReview::STATE_REVIEWED,
))
->visible(fn (ManagedEnvironment $record, mixed $livewire): bool => static::selectedTriageReviewRowForTenant(
$record,
@ -1033,12 +1033,12 @@ public static function table(Table $table): Table
->action(function (
ManagedEnvironment $record,
mixed $livewire,
TenantTriageReviewService $service,
ManagedEnvironmentTriageReviewService $service,
): void {
static::handleTriageReviewMutation(
tenant: $record,
triageState: static::currentPortfolioTriageState($livewire),
targetManualState: TenantTriageReview::STATE_REVIEWED,
targetManualState: ManagedEnvironmentTriageReview::STATE_REVIEWED,
service: $service,
);
}),
@ -1051,7 +1051,7 @@ public static function table(Table $table): Table
->modalDescription(fn (ManagedEnvironment $record, mixed $livewire): string => static::triageReviewActionModalDescription(
$record,
static::currentPortfolioTriageState($livewire),
TenantTriageReview::STATE_FOLLOW_UP_NEEDED,
ManagedEnvironmentTriageReview::STATE_FOLLOW_UP_NEEDED,
))
->visible(fn (ManagedEnvironment $record, mixed $livewire): bool => static::selectedTriageReviewRowForTenant(
$record,
@ -1065,12 +1065,12 @@ public static function table(Table $table): Table
->action(function (
ManagedEnvironment $record,
mixed $livewire,
TenantTriageReviewService $service,
ManagedEnvironmentTriageReviewService $service,
): void {
static::handleTriageReviewMutation(
tenant: $record,
triageState: static::currentPortfolioTriageState($livewire),
targetManualState: TenantTriageReview::STATE_FOLLOW_UP_NEEDED,
targetManualState: ManagedEnvironmentTriageReview::STATE_FOLLOW_UP_NEEDED,
service: $service,
);
}),
@ -1149,13 +1149,13 @@ public static function table(Table $table): Table
->visible(fn (): bool => auth()->user() instanceof User)
->authorize(fn (): bool => auth()->user() instanceof User)
->extraAttributes(fn (mixed $livewire): array => [
'x-bind:aria-disabled' => static::crossTenantCompareBulkClientDisabledExpression($livewire).' ? true : null',
'x-bind:disabled' => static::crossTenantCompareBulkClientDisabledExpression($livewire),
'x-bind:title' => static::crossTenantCompareBulkClientTooltipExpression($livewire),
'x-bind:class' => "{ 'fi-disabled': ".static::crossTenantCompareBulkClientDisabledExpression($livewire).' }',
'x-bind:aria-disabled' => static::crossEnvironmentCompareBulkClientDisabledExpression($livewire).' ? true : null',
'x-bind:disabled' => static::crossEnvironmentCompareBulkClientDisabledExpression($livewire),
'x-bind:title' => static::crossEnvironmentCompareBulkClientTooltipExpression($livewire),
'x-bind:class' => "{ 'fi-disabled': ".static::crossEnvironmentCompareBulkClientDisabledExpression($livewire).' }',
])
->action(function (Collection $records, mixed $livewire): void {
$disabledReason = static::crossTenantCompareBulkDisabledReason($records);
$disabledReason = static::crossEnvironmentCompareBulkDisabledReason($records);
if ($disabledReason !== null) {
Notification::make()
@ -1167,7 +1167,7 @@ public static function table(Table $table): Table
}
if (method_exists($livewire, 'redirect')) {
$livewire->redirect(static::crossTenantCompareBulkOpenUrl($records, $livewire), navigate: true);
$livewire->redirect(static::crossEnvironmentCompareBulkOpenUrl($records, $livewire), navigate: true);
}
}),
Actions\BulkAction::make('syncSelected')
@ -1324,7 +1324,7 @@ public static function sanitizeReviewStates(mixed $value): array
{
return static::sanitizeRequestedValues(
$value,
TenantTriageReview::DERIVED_STATES,
ManagedEnvironmentTriageReview::DERIVED_STATES,
);
}
@ -1366,10 +1366,10 @@ public static function tenantDashboardOpenUrl(ManagedEnvironment $record, array
* triage_sort?: string|null
* } $triageState
*/
public static function crossTenantCompareOpenUrl(ManagedEnvironment $record, array $triageState = []): string
public static function crossEnvironmentCompareOpenUrl(ManagedEnvironment $record, array $triageState = []): string
{
return static::crossTenantCompareOpenUrlForSelection(
targetTenant: $record,
return static::crossEnvironmentCompareOpenUrlForSelection(
targetEnvironment: $record,
triageState: $triageState,
);
}
@ -1382,10 +1382,10 @@ public static function crossTenantCompareOpenUrl(ManagedEnvironment $record, arr
* triage_sort?: string|null
* } $triageState
*/
public static function crossTenantCompareOpenUrlForSelection(
ManagedEnvironment $targetTenant,
public static function crossEnvironmentCompareOpenUrlForSelection(
ManagedEnvironment $targetEnvironment,
array $triageState = [],
?ManagedEnvironment $sourceTenant = null,
?ManagedEnvironment $sourceEnvironment = null,
): string {
$normalizedState = static::portfolioReturnFilters(
static::sanitizeBackupPostures($triageState['backup_posture'] ?? []),
@ -1394,12 +1394,12 @@ public static function crossTenantCompareOpenUrlForSelection(
static::sanitizeTriageSort($triageState['triage_sort'] ?? null),
);
return CrossTenantComparePage::launchUrl(
sourceTenant: $sourceTenant,
targetTenant: $targetTenant,
return CrossEnvironmentComparePage::launchUrl(
sourceEnvironment: $sourceEnvironment,
targetEnvironment: $targetEnvironment,
navigationContext: CanonicalNavigationContext::forTenantRegistry(
backLinkUrl: static::getUrl(panel: 'admin', parameters: $normalizedState),
tenantId: $sourceTenant instanceof ManagedEnvironment ? null : (int) $targetTenant->getKey(),
tenantId: $sourceEnvironment instanceof ManagedEnvironment ? null : (int) $targetEnvironment->getKey(),
),
);
}
@ -1494,7 +1494,7 @@ private static function portfolioReturnFiltersFromRequest(array $query): array
);
}
private static function crossTenantCompareActionVisible(ManagedEnvironment $record): bool
private static function crossEnvironmentCompareActionVisible(ManagedEnvironment $record): bool
{
if (! $record->isActive()) {
return false;
@ -1533,7 +1533,7 @@ private static function crossTenantCompareActionVisible(ManagedEnvironment $reco
&& $tenantResolver->can($user, $record, Capabilities::TENANT_VIEW);
}
private static function crossTenantCompareBulkDisabledReason(Collection $records): ?string
private static function crossEnvironmentCompareBulkDisabledReason(Collection $records): ?string
{
$user = auth()->user();
@ -1541,20 +1541,20 @@ private static function crossTenantCompareBulkDisabledReason(Collection $records
return UiTooltips::insufficientPermission();
}
$tenants = $records
$environments = $records
->filter(fn ($record): bool => $record instanceof ManagedEnvironment)
->values();
if ($records->count() !== 2 || $tenants->count() !== 2) {
return 'Select exactly two tenants to compare.';
if ($records->count() !== 2 || $environments->count() !== 2) {
return 'Select exactly two environments to compare.';
}
if ($tenants->contains(fn (ManagedEnvironment $tenant): bool => ! $tenant->isActive())) {
return 'Only active tenants can be compared.';
if ($environments->contains(fn (ManagedEnvironment $environment): bool => ! $environment->isActive())) {
return 'Only active environments can be compared.';
}
$workspaceIds = $tenants
->map(fn (ManagedEnvironment $tenant): int => (int) $tenant->workspace_id)
$workspaceIds = $environments
->map(fn (ManagedEnvironment $environment): int => (int) $environment->workspace_id)
->unique()
->values();
@ -1578,32 +1578,32 @@ private static function crossTenantCompareBulkDisabledReason(Collection $records
return UiTooltips::insufficientPermission();
}
/** @var CapabilityResolver $tenantResolver */
$tenantResolver = app(CapabilityResolver::class);
/** @var CapabilityResolver $environmentResolver */
$environmentResolver = app(CapabilityResolver::class);
$isDenied = $tenants->contains(fn (ManagedEnvironment $tenant): bool => ! $user->canAccessTenant($tenant)
|| ! $tenantResolver->can($user, $tenant, Capabilities::TENANT_VIEW));
$isDenied = $environments->contains(fn (ManagedEnvironment $environment): bool => ! $user->canAccessTenant($environment)
|| ! $environmentResolver->can($user, $environment, Capabilities::TENANT_VIEW));
return $isDenied ? UiTooltips::insufficientPermission() : null;
}
private static function crossTenantCompareBulkClientDisabledExpression(mixed $livewire): string
private static function crossEnvironmentCompareBulkClientDisabledExpression(mixed $livewire): string
{
$containsInactiveSelection = static::crossTenantCompareBulkContainsInactiveSelectionExpression($livewire);
$containsInactiveSelection = static::crossEnvironmentCompareBulkContainsInactiveSelectionExpression($livewire);
return "getSelectedRecordsCount() !== 2 || {$containsInactiveSelection}";
}
private static function crossTenantCompareBulkClientTooltipExpression(mixed $livewire): string
private static function crossEnvironmentCompareBulkClientTooltipExpression(mixed $livewire): string
{
$containsInactiveSelection = static::crossTenantCompareBulkContainsInactiveSelectionExpression($livewire);
$containsInactiveSelection = static::crossEnvironmentCompareBulkContainsInactiveSelectionExpression($livewire);
return "getSelectedRecordsCount() !== 2 ? 'Select exactly two tenants to compare.' : ({$containsInactiveSelection} ? 'Only active tenants can be compared.' : null)";
return "getSelectedRecordsCount() !== 2 ? 'Select exactly two environments to compare.' : ({$containsInactiveSelection} ? 'Only active environments can be compared.' : null)";
}
private static function crossTenantCompareBulkContainsInactiveSelectionExpression(mixed $livewire): string
private static function crossEnvironmentCompareBulkContainsInactiveSelectionExpression(mixed $livewire): string
{
$inactiveRecordKeys = \Illuminate\Support\Js::from(static::crossTenantCompareInactiveSelectionRecordKeys($livewire));
$inactiveRecordKeys = \Illuminate\Support\Js::from(static::crossEnvironmentCompareInactiveSelectionRecordKeys($livewire));
return "[...selectedRecords].some((key) => {$inactiveRecordKeys}.includes(key))";
}
@ -1611,7 +1611,7 @@ private static function crossTenantCompareBulkContainsInactiveSelectionExpressio
/**
* @return list<string>
*/
private static function crossTenantCompareInactiveSelectionRecordKeys(mixed $livewire): array
private static function crossEnvironmentCompareInactiveSelectionRecordKeys(mixed $livewire): array
{
if (! $livewire instanceof HasTable || ! method_exists($livewire, 'getTableRecordKey')) {
return [];
@ -1630,9 +1630,9 @@ private static function crossTenantCompareInactiveSelectionRecordKeys(mixed $liv
->all();
}
private static function crossTenantCompareBulkOpenUrl(Collection $records, mixed $livewire): string
private static function crossEnvironmentCompareBulkOpenUrl(Collection $records, mixed $livewire): string
{
$triageState = $livewire instanceof Pages\ListTenants
$triageState = $livewire instanceof Pages\ListManagedEnvironments
? static::currentPortfolioTriageState($livewire)
: [];
@ -1649,10 +1649,10 @@ private static function crossTenantCompareBulkOpenUrl(Collection $records, mixed
->filter(fn ($record): bool => $record instanceof ManagedEnvironment)
->values();
return static::crossTenantCompareOpenUrlForSelection(
targetTenant: $tenants->get(1),
return static::crossEnvironmentCompareOpenUrlForSelection(
targetEnvironment: $tenants->get(1),
triageState: $triageState,
sourceTenant: $tenants->get(0),
sourceEnvironment: $tenants->get(0),
);
}
@ -1808,9 +1808,9 @@ private static function recoveryEvidenceForTenant(ManagedEnvironment $tenant): ?
*/
private static function reviewStateOptions(): array
{
return collect(TenantTriageReview::DERIVED_STATES)
return collect(ManagedEnvironmentTriageReview::DERIVED_STATES)
->mapWithKeys(static fn (string $state): array => [
$state => BadgeRenderer::spec(BadgeDomain::TenantTriageReviewState, $state)->label,
$state => BadgeRenderer::spec(BadgeDomain::ManagedEnvironmentTriageReviewState, $state)->label,
])
->all();
}
@ -1854,7 +1854,7 @@ private static function triageReviewSnapshot(): array
return $resolved;
}
$resolved = app(TenantTriageReviewStateResolver::class)->resolveMany(
$resolved = app(ManagedEnvironmentTriageReviewStateResolver::class)->resolveMany(
workspaceId: $workspaceId,
tenantIds: $snapshot['tenant_ids'],
backupHealthByTenant: $snapshot['backup_health'],
@ -2035,8 +2035,8 @@ private static function triageReviewActionModalDescription(
return 'This triage slice no longer points at a current visible concern.';
}
$currentLabel = BadgeRenderer::spec(BadgeDomain::TenantTriageReviewState, $row['derived_state'])->label;
$targetLabel = BadgeRenderer::spec(BadgeDomain::TenantTriageReviewState, $targetManualState)->label;
$currentLabel = BadgeRenderer::spec(BadgeDomain::ManagedEnvironmentTriageReviewState, $row['derived_state'])->label;
$targetLabel = BadgeRenderer::spec(BadgeDomain::ManagedEnvironmentTriageReviewState, $targetManualState)->label;
return implode("\n\n", [
'Concern family: '.$row['concern_family_label'],
@ -2071,7 +2071,7 @@ private static function triageReviewActionIsDisabled(ManagedEnvironment $tenant)
return true;
}
return ! $resolver->can($user, $tenant, Capabilities::TENANT_TRIAGE_REVIEW_MANAGE);
return ! $resolver->can($user, $tenant, Capabilities::MANAGED_ENVIRONMENT_TRIAGE_REVIEW_MANAGE);
}
private static function triageReviewActionTooltip(ManagedEnvironment $tenant): ?string
@ -2084,7 +2084,7 @@ private static function triageReviewActionTooltip(ManagedEnvironment $tenant): ?
$resolver = app(CapabilityResolver::class);
if ($resolver->isMember($user, $tenant) && ! $resolver->can($user, $tenant, Capabilities::TENANT_TRIAGE_REVIEW_MANAGE)) {
if ($resolver->isMember($user, $tenant) && ! $resolver->can($user, $tenant, Capabilities::MANAGED_ENVIRONMENT_TRIAGE_REVIEW_MANAGE)) {
return UiTooltips::insufficientPermission();
}
@ -2105,7 +2105,7 @@ private static function authorizeTriageReviewAction(ManagedEnvironment $tenant):
abort(404);
}
if (! $resolver->can($user, $tenant, Capabilities::TENANT_TRIAGE_REVIEW_MANAGE)) {
if (! $resolver->can($user, $tenant, Capabilities::MANAGED_ENVIRONMENT_TRIAGE_REVIEW_MANAGE)) {
abort(403);
}
}
@ -2122,7 +2122,7 @@ private static function handleTriageReviewMutation(
ManagedEnvironment $tenant,
array $triageState,
string $targetManualState,
TenantTriageReviewService $service,
ManagedEnvironmentTriageReviewService $service,
): void {
$row = static::selectedActionTriageReviewRowForTenant($tenant, $triageState);
@ -2141,14 +2141,14 @@ private static function handleTriageReviewMutation(
$recoveryEvidence = static::recoveryEvidenceForTenant($tenant);
$review = match ($targetManualState) {
TenantTriageReview::STATE_REVIEWED => $service->markReviewed(
ManagedEnvironmentTriageReview::STATE_REVIEWED => $service->markReviewed(
tenant: $tenant,
concernFamily: (string) $row['concern_family'],
backupHealth: $backupHealth,
recoveryEvidence: $recoveryEvidence,
actor: $actor instanceof User ? $actor : null,
),
TenantTriageReview::STATE_FOLLOW_UP_NEEDED => $service->markFollowUpNeeded(
ManagedEnvironmentTriageReview::STATE_FOLLOW_UP_NEEDED => $service->markFollowUpNeeded(
tenant: $tenant,
concernFamily: (string) $row['concern_family'],
backupHealth: $backupHealth,
@ -2158,7 +2158,7 @@ private static function handleTriageReviewMutation(
default => null,
};
if (! $review instanceof TenantTriageReview) {
if (! $review instanceof ManagedEnvironmentTriageReview) {
return;
}
@ -2169,7 +2169,7 @@ private static function handleTriageReviewMutation(
->body(sprintf(
'%s is now %s for %s.',
$tenant->name,
BadgeRenderer::spec(BadgeDomain::TenantTriageReviewState, $review->current_state)->label,
BadgeRenderer::spec(BadgeDomain::ManagedEnvironmentTriageReviewState, $review->current_state)->label,
static::triageConcernFamilyLabel((string) $review->concern_family),
))
->success()
@ -2197,7 +2197,7 @@ private static function selectedActionTriageReviewRowForTenant(ManagedEnvironmen
$backupHealth = app(TenantBackupHealthResolver::class)->assess($tenant);
$recoveryEvidence = app(RestoreSafetyResolver::class)->dashboardRecoveryEvidence($tenant);
$rows = app(TenantTriageReviewStateResolver::class)->resolveMany(
$rows = app(ManagedEnvironmentTriageReviewStateResolver::class)->resolveMany(
workspaceId: $workspaceId,
tenantIds: [$tenantId],
backupHealthByTenant: [$tenantId => $backupHealth],
@ -2640,10 +2640,10 @@ public static function infolist(Schema $schema): Schema
->formatStateUsing(fn ($state) => is_array($state) ? implode(', ', $state) : (string) $state),
Infolists\Components\TextEntry::make('status')
->badge()
->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantPermissionStatus))
->color(BadgeRenderer::color(BadgeDomain::TenantPermissionStatus))
->icon(BadgeRenderer::icon(BadgeDomain::TenantPermissionStatus))
->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantPermissionStatus)),
->formatStateUsing(BadgeRenderer::label(BadgeDomain::ManagedEnvironmentPermissionStatus))
->color(BadgeRenderer::color(BadgeDomain::ManagedEnvironmentPermissionStatus))
->icon(BadgeRenderer::icon(BadgeDomain::ManagedEnvironmentPermissionStatus))
->iconColor(BadgeRenderer::iconColor(BadgeDomain::ManagedEnvironmentPermissionStatus)),
])
->columnSpanFull(),
])
@ -2730,7 +2730,7 @@ public static function tenantIndexPrimaryAction(ManagedEnvironment $tenant): ?Te
return $catalog[1] ?? null;
}
public static function relatedOnboardingDraft(ManagedEnvironment $tenant): ?TenantOnboardingSession
public static function relatedOnboardingDraft(ManagedEnvironment $tenant): ?ManagedEnvironmentOnboardingSession
{
return static::tenantActionPolicy()->relatedOnboardingDraft($tenant);
}
@ -2749,7 +2749,7 @@ public static function relatedOnboardingDraftUrl(ManagedEnvironment $tenant): ?s
{
$draft = static::relatedOnboardingDraft($tenant);
if (! $draft instanceof TenantOnboardingSession) {
if (! $draft instanceof ManagedEnvironmentOnboardingSession) {
return null;
}
@ -3119,17 +3119,17 @@ private static function validatedLifecycleReason(string $reason, string $field):
public static function getPages(): array
{
return [
'index' => Pages\ListTenants::route('/'),
'view' => Pages\ViewTenant::route('/{record}'),
'edit' => Pages\EditTenant::route('/{record}/edit'),
'memberships' => Pages\ManageTenantMemberships::route('/{record}/memberships'),
'index' => Pages\ListManagedEnvironments::route('/'),
'view' => Pages\ViewManagedEnvironment::route('/{record}'),
'edit' => Pages\EditManagedEnvironment::route('/{record}/edit'),
'memberships' => Pages\ManageEnvironmentAccessScopes::route('/{record}/memberships'),
];
}
public static function getRelations(): array
{
return [
RelationManagers\TenantMembershipsRelationManager::class,
RelationManagers\ManagedEnvironmentMembershipsRelationManager::class,
];
}

View File

@ -1,26 +1,26 @@
<?php
namespace App\Filament\Resources\TenantResource\Pages;
namespace App\Filament\Resources\ManagedEnvironmentResource\Pages;
use App\Filament\Resources\TenantResource;
use App\Filament\Resources\ManagedEnvironmentResource;
use App\Models\ManagedEnvironment;
use App\Support\Tenants\TenantActionSurface;
use Filament\Actions;
use Filament\Resources\Pages\EditRecord;
class EditTenant extends EditRecord
class EditManagedEnvironment extends EditRecord
{
protected static string $resource = TenantResource::class;
protected static string $resource = ManagedEnvironmentResource::class;
protected function getHeaderActions(): array
{
return array_values(array_filter([
Actions\ActionGroup::make([
TenantResource::makeRestoreTenantAction(
ManagedEnvironmentResource::makeRestoreTenantAction(
TenantActionSurface::TenantEditHeader,
'You do not have permission to restore tenants.',
),
TenantResource::makeArchiveTenantAction(
ManagedEnvironmentResource::makeArchiveTenantAction(
TenantActionSurface::TenantEditHeader,
'You do not have permission to archive tenants.',
),
@ -30,7 +30,7 @@ protected function getHeaderActions(): array
->color('gray')
->visible(fn (): bool => $this->getRecord() instanceof ManagedEnvironment
&& in_array(
TenantResource::lifecycleActionDescriptor($this->getRecord(), TenantActionSurface::TenantEditHeader)?->key,
ManagedEnvironmentResource::lifecycleActionDescriptor($this->getRecord(), TenantActionSurface::TenantEditHeader)?->key,
['archive', 'restore'],
true,
)),

View File

@ -2,9 +2,9 @@
declare(strict_types=1);
namespace App\Filament\Resources\TenantResource\Pages;
namespace App\Filament\Resources\ManagedEnvironmentResource\Pages;
use App\Filament\Resources\TenantResource;
use App\Filament\Resources\ManagedEnvironmentResource;
use App\Models\User;
use App\Models\Workspace;
use App\Services\Onboarding\OnboardingDraftResolver;
@ -13,9 +13,9 @@
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
class ListTenants extends ListRecords
class ListManagedEnvironments extends ListRecords
{
protected static string $resource = TenantResource::class;
protected static string $resource = ManagedEnvironmentResource::class;
public function mount(): void
{
@ -64,7 +64,7 @@ protected function getTableEmptyStateDescription(): ?string
private function makeOnboardingEntryAction(): Actions\Action
{
$descriptor = TenantResource::tenantActionPolicy()->onboardingEntryDescriptor($this->accessibleResumableDraftCount());
$descriptor = ManagedEnvironmentResource::tenantActionPolicy()->onboardingEntryDescriptor($this->accessibleResumableDraftCount());
return Actions\Action::make('add_tenant')
->label($descriptor->label)
@ -92,10 +92,10 @@ private function applyRequestedTriageIntent(): void
return;
}
$backupPostures = TenantResource::sanitizeBackupPostures(request()->query('backup_posture'));
$recoveryEvidence = TenantResource::sanitizeRecoveryEvidenceStates(request()->query('recovery_evidence'));
$reviewStates = TenantResource::sanitizeReviewStates(request()->query('review_state'));
$triageSort = TenantResource::sanitizeTriageSort(request()->query('triage_sort'));
$backupPostures = ManagedEnvironmentResource::sanitizeBackupPostures(request()->query('backup_posture'));
$recoveryEvidence = ManagedEnvironmentResource::sanitizeRecoveryEvidenceStates(request()->query('recovery_evidence'));
$reviewStates = ManagedEnvironmentResource::sanitizeReviewStates(request()->query('review_state'));
$triageSort = ManagedEnvironmentResource::sanitizeTriageSort(request()->query('triage_sort'));
foreach (['backup_posture', 'recovery_evidence', 'review_state', 'triage_sort'] as $filterName) {
data_forget($this->tableFilters, $filterName);
@ -139,10 +139,10 @@ private function hasActiveTriageEmptyState(): bool
public function currentPortfolioTriageReturnState(): array
{
return [
'backup_posture' => TenantResource::sanitizeBackupPostures(data_get($this->tableFilters, 'backup_posture.values', [])),
'recovery_evidence' => TenantResource::sanitizeRecoveryEvidenceStates(data_get($this->tableFilters, 'recovery_evidence.values', [])),
'review_state' => TenantResource::sanitizeReviewStates(data_get($this->tableFilters, 'review_state.values', [])),
'triage_sort' => TenantResource::sanitizeTriageSort(data_get($this->tableFilters, 'triage_sort.value')),
'backup_posture' => ManagedEnvironmentResource::sanitizeBackupPostures(data_get($this->tableFilters, 'backup_posture.values', [])),
'recovery_evidence' => ManagedEnvironmentResource::sanitizeRecoveryEvidenceStates(data_get($this->tableFilters, 'recovery_evidence.values', [])),
'review_state' => ManagedEnvironmentResource::sanitizeReviewStates(data_get($this->tableFilters, 'review_state.values', [])),
'triage_sort' => ManagedEnvironmentResource::sanitizeTriageSort(data_get($this->tableFilters, 'triage_sort.value')),
];
}

View File

@ -1,18 +1,18 @@
<?php
namespace App\Filament\Resources\TenantResource\Pages;
namespace App\Filament\Resources\ManagedEnvironmentResource\Pages;
use App\Models\ManagedEnvironment;
use App\Support\ManagedEnvironmentLinks;
use Filament\Actions\Action;
class ManageTenantMemberships extends ViewTenant
class ManageEnvironmentAccessScopes extends ViewManagedEnvironment
{
protected static ?string $title = 'Manage environment access scope';
public function mount(int|string|ManagedEnvironment $tenant): void
public function mount(int|string|ManagedEnvironment $environment): void
{
parent::mount($tenant instanceof ManagedEnvironment ? (string) $tenant->getRouteKey() : $tenant);
parent::mount($environment instanceof ManagedEnvironment ? (string) $environment->getRouteKey() : $environment);
}
public function getSubheading(): ?string

View File

@ -1,12 +1,12 @@
<?php
namespace App\Filament\Resources\TenantResource\Pages;
namespace App\Filament\Resources\ManagedEnvironmentResource\Pages;
use App\Filament\Resources\TenantResource;
use App\Filament\Widgets\Tenant\AdminRolesSummaryWidget;
use App\Filament\Widgets\Tenant\RecentOperationsSummary;
use App\Filament\Widgets\Tenant\TenantArchivedBanner;
use App\Filament\Widgets\Tenant\TenantVerificationReport;
use App\Filament\Resources\ManagedEnvironmentResource;
use App\Filament\Widgets\ManagedEnvironment\AdminRolesSummaryWidget;
use App\Filament\Widgets\ManagedEnvironment\RecentOperationsSummary;
use App\Filament\Widgets\ManagedEnvironment\ManagedEnvironmentArchivedBanner;
use App\Filament\Widgets\ManagedEnvironment\ManagedEnvironmentVerificationReport;
use App\Jobs\RefreshTenantRbacHealthJob;
use App\Models\ManagedEnvironment;
use App\Models\User;
@ -22,9 +22,9 @@
use Filament\Notifications\Notification;
use Filament\Resources\Pages\ViewRecord;
class ViewTenant extends ViewRecord
class ViewManagedEnvironment extends ViewRecord
{
protected static string $resource = TenantResource::class;
protected static string $resource = ManagedEnvironmentResource::class;
public static function verificationHeaderActionLabel(): string
{
@ -44,9 +44,9 @@ public function getHeaderWidgetsColumns(): int|array
protected function getHeaderWidgets(): array
{
return [
TenantArchivedBanner::class,
ManagedEnvironmentArchivedBanner::class,
RecentOperationsSummary::class,
TenantVerificationReport::class,
ManagedEnvironmentVerificationReport::class,
AdminRolesSummaryWidget::class,
];
}
@ -54,27 +54,27 @@ protected function getHeaderWidgets(): array
protected function getHeaderActions(): array
{
return array_values(array_filter([
TenantResource::makeMembershipsAction(),
ManagedEnvironmentResource::makeMembershipsAction(),
Actions\ActionGroup::make([
TenantResource::makeAdminConsentAction(),
TenantResource::makeOpenInEntraAction(),
ManagedEnvironmentResource::makeAdminConsentAction(),
ManagedEnvironmentResource::makeOpenInEntraAction(),
])
->label('External links')
->icon('heroicon-o-arrow-top-right-on-square')
->color('gray')
->visible(fn (): bool => $this->getRecord() instanceof ManagedEnvironment
&& TenantResource::tenantViewExternalGroupVisible($this->getRecord())),
&& ManagedEnvironmentResource::tenantViewExternalGroupVisible($this->getRecord())),
Actions\ActionGroup::make([
TenantResource::makeSyncTenantAction(),
TenantResource::makeVerifyConfigurationAction('tenant_view_header'),
TenantResource::rbacAction(),
ManagedEnvironmentResource::makeSyncTenantAction(),
ManagedEnvironmentResource::makeVerifyConfigurationAction('tenant_view_header'),
ManagedEnvironmentResource::rbacAction(),
UiEnforcement::forAction(
Actions\Action::make('refresh_rbac')
->label('Refresh RBAC status')
->icon('heroicon-o-arrow-path')
->color('primary')
->requiresConfirmation()
->visible(fn (ManagedEnvironment $record): bool => TenantResource::tenantSetupMutationVisible($record))
->visible(fn (ManagedEnvironment $record): bool => ManagedEnvironmentResource::tenantSetupMutationVisible($record))
->action(function (ManagedEnvironment $record): void {
$user = auth()->user();
@ -140,27 +140,27 @@ protected function getHeaderActions(): array
->icon('heroicon-o-wrench-screwdriver')
->color('gray')
->visible(fn (): bool => $this->getRecord() instanceof ManagedEnvironment
&& TenantResource::tenantViewSetupGroupVisible($this->getRecord())),
&& ManagedEnvironmentResource::tenantViewSetupGroupVisible($this->getRecord())),
Actions\ActionGroup::make([
TenantResource::makeTenantViewMarkReviewedAction(),
TenantResource::makeTenantViewMarkFollowUpNeededAction(),
ManagedEnvironmentResource::makeTenantViewMarkReviewedAction(),
ManagedEnvironmentResource::makeTenantViewMarkFollowUpNeededAction(),
])
->label('Triage')
->icon('heroicon-o-check-circle')
->color('gray')
->visible(fn (): bool => $this->getRecord() instanceof ManagedEnvironment
&& TenantResource::tenantViewTriageGroupVisible($this->getRecord())),
&& ManagedEnvironmentResource::tenantViewTriageGroupVisible($this->getRecord())),
Actions\ActionGroup::make([
TenantResource::makeRestoreTenantAction(TenantActionSurface::TenantViewHeader),
TenantResource::makeRestoreTenantToWorkspaceAction(),
TenantResource::makeRemoveTenantFromWorkspaceAction(),
TenantResource::makeArchiveTenantAction(TenantActionSurface::TenantViewHeader),
ManagedEnvironmentResource::makeRestoreTenantAction(TenantActionSurface::TenantViewHeader),
ManagedEnvironmentResource::makeRestoreTenantToWorkspaceAction(),
ManagedEnvironmentResource::makeRemoveTenantFromWorkspaceAction(),
ManagedEnvironmentResource::makeArchiveTenantAction(TenantActionSurface::TenantViewHeader),
])
->label('Lifecycle')
->icon('heroicon-o-archive-box')
->color('gray')
->visible(fn (): bool => $this->getRecord() instanceof ManagedEnvironment
&& TenantResource::tenantViewLifecycleGroupVisible($this->getRecord())),
&& ManagedEnvironmentResource::tenantViewLifecycleGroupVisible($this->getRecord())),
]));
}
}

View File

@ -1,13 +1,13 @@
<?php
namespace App\Filament\Resources\TenantResource\RelationManagers;
namespace App\Filament\Resources\ManagedEnvironmentResource\RelationManagers;
use App\Filament\Resources\TenantResource\Pages\ManageTenantMemberships;
use App\Filament\Resources\ManagedEnvironmentResource\Pages\ManageEnvironmentAccessScopes;
use App\Models\ManagedEnvironment;
use App\Models\ManagedEnvironmentMembership;
use App\Models\User;
use App\Services\Auth\CapabilityResolver;
use App\Services\Auth\TenantMembershipManager;
use App\Services\Auth\ManagedEnvironmentMembershipManager;
use App\Support\Auth\Capabilities;
use App\Support\Rbac\UiEnforcement;
use App\Support\Ui\ActionSurface\ActionSurfaceDeclaration;
@ -22,7 +22,7 @@
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class TenantMembershipsRelationManager extends RelationManager
class ManagedEnvironmentMembershipsRelationManager extends RelationManager
{
protected static string $relationship = 'memberships';
@ -42,7 +42,7 @@ public static function canViewForRecord(Model $ownerRecord, string $pageClass):
return false;
}
if ($pageClass !== ManageTenantMemberships::class) {
if ($pageClass !== ManageEnvironmentAccessScopes::class) {
return false;
}
@ -103,7 +103,7 @@ public function table(Table $table): Table
->searchable()
->options(fn (): array => $this->workspaceMemberOptions()),
])
->action(function (array $data, TenantMembershipManager $manager): void {
->action(function (array $data, ManagedEnvironmentMembershipManager $manager): void {
$tenant = $this->getOwnerRecord();
if (! $tenant instanceof ManagedEnvironment) {
@ -155,7 +155,7 @@ public function table(Table $table): Table
->color('danger')
->icon('heroicon-o-x-mark')
->requiresConfirmation()
->action(function (ManagedEnvironmentMembership $record, TenantMembershipManager $manager): void {
->action(function (ManagedEnvironmentMembership $record, ManagedEnvironmentMembershipManager $manager): void {
$tenant = $this->getOwnerRecord();
if (! $tenant instanceof ManagedEnvironment) {

View File

@ -200,11 +200,11 @@ public static function infolist(Schema $schema): Schema
Section::make('Metadata')
->schema([
TextEntry::make('initiator.name')->label('Initiated by')->placeholder('—'),
TextEntry::make('tenantReview.id')
TextEntry::make('environmentReview.id')
->label('ManagedEnvironment review')
->formatStateUsing(fn (?int $state): string => $state ? '#'.$state : '—')
->url(fn (ReviewPack $record): ?string => $record->tenantReview && $record->tenant
? TenantReviewResource::tenantScopedUrl('view', ['record' => $record->tenantReview], $record->tenant)
->url(fn (ReviewPack $record): ?string => $record->environmentReview && $record->tenant
? EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $record->environmentReview], $record->tenant)
: null)
->placeholder('—'),
TextEntry::make('customer_workspace')
@ -217,10 +217,10 @@ public static function infolist(Schema $schema): Schema
TextEntry::make('summary.review_status')
->label('Review status')
->badge()
->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantReviewStatus))
->color(BadgeRenderer::color(BadgeDomain::TenantReviewStatus))
->icon(BadgeRenderer::icon(BadgeDomain::TenantReviewStatus))
->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantReviewStatus))
->formatStateUsing(BadgeRenderer::label(BadgeDomain::EnvironmentReviewStatus))
->color(BadgeRenderer::color(BadgeDomain::EnvironmentReviewStatus))
->icon(BadgeRenderer::icon(BadgeDomain::EnvironmentReviewStatus))
->iconColor(BadgeRenderer::iconColor(BadgeDomain::EnvironmentReviewStatus))
->placeholder('—'),
TextEntry::make('operationRun.id')
->label('Operation')
@ -306,7 +306,7 @@ public static function table(Table $table): Table
->dateTime()
->sortable()
->placeholder('—'),
Tables\Columns\TextColumn::make('tenantReview.id')
Tables\Columns\TextColumn::make('environmentReview.id')
->label('Review')
->formatStateUsing(fn (?int $state): string => $state ? '#'.$state : '—')
->toggleable(isToggledHiddenByDefault: true),
@ -429,7 +429,7 @@ public static function getEloquentQuery(): Builder
}
return parent::getEloquentQuery()
->with(['tenant', 'operationRun', 'evidenceSnapshot', 'tenantReview'])
->with(['tenant', 'operationRun', 'evidenceSnapshot', 'environmentReview'])
->where('managed_environment_id', (int) $tenant->getKey());
}
@ -575,7 +575,7 @@ public static function executeGeneration(array $data): void
return;
}
OperationUxPresenter::queuedToast('tenant.review_pack.generate')->send();
OperationUxPresenter::queuedToast('environment.review_pack.generate')->send();
}
/**

View File

@ -1,20 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Filament\Resources\TenantReviewResource\Pages;
use App\Filament\Resources\TenantReviewResource;
use Filament\Resources\Pages\ListRecords;
class ListTenantReviews extends ListRecords
{
protected static string $resource = TenantReviewResource::class;
protected function getHeaderActions(): array
{
return [
TenantReviewResource::makeCreateReviewAction(),
];
}
}

View File

@ -9,7 +9,7 @@
use App\Models\PlatformUser;
use App\Models\ProviderConnection;
use App\Models\ManagedEnvironment;
use App\Models\TenantPermission;
use App\Models\ManagedEnvironmentPermission;
use App\Support\Auth\PlatformCapabilities;
use App\Support\CustomerHealth\WorkspaceHealthSummaryQuery;
use App\Support\OperationCatalog;
@ -69,11 +69,11 @@ public function providerConnections(): Collection
}
/**
* @return Collection<int, TenantPermission>
* @return Collection<int, ManagedEnvironmentPermission>
*/
public function tenantPermissions(): Collection
public function managedEnvironmentPermissions(): Collection
{
return TenantPermission::query()
return ManagedEnvironmentPermission::query()
->where('managed_environment_id', (int) $this->tenant->getKey())
->orderBy('permission_key')
->limit(20)

View File

@ -6,7 +6,7 @@
use App\Models\ManagedEnvironment;
use App\Support\OpsUx\ActiveRuns;
use App\Support\TenantDashboard\TenantDashboardSummaryBuilder;
use App\Support\EnvironmentDashboard\EnvironmentDashboardSummaryBuilder;
use Filament\Facades\Filament;
use Filament\Widgets\StatsOverviewWidget;
use Filament\Widgets\StatsOverviewWidget\Stat;
@ -32,7 +32,7 @@ protected function getStats(): array
return [];
}
$summary = app(TenantDashboardSummaryBuilder::class)->build($tenant, auth()->user());
$summary = app(EnvironmentDashboardSummaryBuilder::class)->build($tenant, auth()->user());
$stats = [];

View File

@ -5,17 +5,17 @@
namespace App\Filament\Widgets\Dashboard;
use App\Models\ManagedEnvironment;
use App\Support\TenantDashboard\TenantDashboardSummaryBuilder;
use App\Support\EnvironmentDashboard\EnvironmentDashboardSummaryBuilder;
use Filament\Facades\Filament;
use Filament\Widgets\Widget;
class TenantDashboardContextChips extends Widget
class EnvironmentDashboardContextChips extends Widget
{
protected static bool $isLazy = false;
protected int|string|array $columnSpan = 'full';
protected string $view = 'filament.widgets.dashboard.tenant-dashboard-context-chips';
protected string $view = 'filament.widgets.dashboard.environment-dashboard-context-chips';
/**
* @return array<string, mixed>
@ -37,11 +37,11 @@ protected function getViewData(): array
];
}
$summary = app(TenantDashboardSummaryBuilder::class)->build($tenant, auth()->user());
$summary = app(EnvironmentDashboardSummaryBuilder::class)->build($tenant, auth()->user());
return [
'context' => $summary->context,
'pollingInterval' => $summary->pollingInterval,
];
}
}
}

View File

@ -5,17 +5,17 @@
namespace App\Filament\Widgets\Dashboard;
use App\Models\ManagedEnvironment;
use App\Support\TenantDashboard\TenantDashboardSummaryBuilder;
use App\Support\EnvironmentDashboard\EnvironmentDashboardSummaryBuilder;
use Filament\Facades\Filament;
use Filament\Widgets\Widget;
class TenantDashboardOverview extends Widget
class EnvironmentDashboardOverview extends Widget
{
protected static bool $isLazy = false;
protected int|string|array $columnSpan = 'full';
protected string $view = 'filament.widgets.dashboard.tenant-dashboard-overview';
protected string $view = 'filament.widgets.dashboard.environment-dashboard-overview';
/**
* @return array<string, mixed>
@ -49,8 +49,8 @@ protected function getViewData(): array
];
}
return app(TenantDashboardSummaryBuilder::class)
return app(EnvironmentDashboardSummaryBuilder::class)
->build($tenant)
->toArray();
}
}
}

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace App\Filament\Widgets\Tenant;
namespace App\Filament\Widgets\ManagedEnvironment;
use App\Filament\Resources\StoredReportResource;
use App\Jobs\ScanEntraAdminRolesJob;
@ -22,7 +22,7 @@ class AdminRolesSummaryWidget extends Widget
{
protected static bool $isLazy = false;
protected string $view = 'filament.widgets.tenant.admin-roles-summary';
protected string $view = 'filament.widgets.managed-environment.admin-roles-summary';
public ?ManagedEnvironment $record = null;

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace App\Filament\Widgets\Tenant;
namespace App\Filament\Widgets\ManagedEnvironment;
use App\Filament\Pages\BaselineCompareLanding;
use App\Models\ManagedEnvironment;
@ -16,7 +16,7 @@ class BaselineCompareCoverageBanner extends Widget
{
protected static bool $isLazy = false;
protected string $view = 'filament.widgets.tenant.baseline-compare-coverage-banner';
protected string $view = 'filament.widgets.managed-environment.baseline-compare-coverage-banner';
/**
* @return array<string, mixed>

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace App\Filament\Widgets\Tenant;
namespace App\Filament\Widgets\ManagedEnvironment;
use App\Filament\Resources\FindingExceptionResource;
use Filament\Widgets\StatsOverviewWidget as BaseWidget;

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace App\Filament\Widgets\Tenant;
namespace App\Filament\Widgets\ManagedEnvironment;
use App\Filament\Resources\FindingResource;
use Filament\Widgets\StatsOverviewWidget as BaseWidget;

View File

@ -2,18 +2,18 @@
declare(strict_types=1);
namespace App\Filament\Widgets\Tenant;
namespace App\Filament\Widgets\ManagedEnvironment;
use App\Models\ManagedEnvironment;
use App\Support\Tenants\TenantLifecyclePresentation;
use Filament\Facades\Filament;
use Filament\Widgets\Widget;
class TenantArchivedBanner extends Widget
class ManagedEnvironmentArchivedBanner extends Widget
{
protected static bool $isLazy = false;
protected string $view = 'filament.widgets.tenant.tenant-archived-banner';
protected string $view = 'filament.widgets.managed-environment.managed-environment-archived-banner';
/**
* @return array<string, mixed>

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace App\Filament\Widgets\Tenant;
namespace App\Filament\Widgets\ManagedEnvironment;
use App\Exceptions\Entitlements\WorkspaceEntitlementBlockedException;
use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
@ -23,13 +23,13 @@
use Filament\Notifications\Notification;
use Filament\Widgets\Widget;
class TenantReviewPackCard extends Widget
class ManagedEnvironmentReviewPackCard extends Widget
{
private const string ACTIVE_POLLING_INTERVAL = '10s';
protected static bool $isLazy = false;
protected string $view = 'filament.widgets.tenant.tenant-review-pack-card';
protected string $view = 'filament.widgets.managed-environment.managed-environment-review-pack-card';
public ?ManagedEnvironment $record = null;
@ -176,7 +176,7 @@ protected function getViewData(): array
: null;
$latestPack = ReviewPack::query()
->with(['tenantReview', 'operationRun'])
->with(['environmentReview', 'operationRun'])
->where('managed_environment_id', (int) $tenant->getKey())
->orderByDesc('created_at')
->orderByDesc('id')
@ -210,8 +210,8 @@ protected function getViewData(): array
}
$reviewUrl = null;
if ($latestPack->tenantReview && $canView) {
$reviewUrl = \App\Filament\Resources\TenantReviewResource::tenantScopedUrl('view', ['record' => $latestPack->tenantReview], $tenant);
if ($latestPack->environmentReview && $canView) {
$reviewUrl = \App\Filament\Resources\EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $latestPack->environmentReview], $tenant);
}
$failedReason = null;

View File

@ -2,12 +2,12 @@
declare(strict_types=1);
namespace App\Filament\Widgets\Tenant;
namespace App\Filament\Widgets\ManagedEnvironment;
use App\Models\ManagedEnvironment;
use App\Models\TenantTriageReview;
use App\Models\ManagedEnvironmentTriageReview;
use App\Models\User;
use App\Services\PortfolioTriage\TenantTriageReviewService;
use App\Services\PortfolioTriage\ManagedEnvironmentTriageReviewService;
use App\Support\Auth\Capabilities;
use App\Support\BackupHealth\TenantBackupHealthResolver;
use App\Support\Badges\BadgeDomain;
@ -15,7 +15,7 @@
use App\Support\PortfolioTriage\PortfolioArrivalContext;
use App\Support\PortfolioTriage\PortfolioArrivalContextResolver;
use App\Support\PortfolioTriage\PortfolioArrivalContextToken;
use App\Support\PortfolioTriage\TenantTriageReviewStateResolver;
use App\Support\PortfolioTriage\ManagedEnvironmentTriageReviewStateResolver;
use App\Support\Rbac\UiEnforcement;
use App\Support\RestoreSafety\RestoreSafetyResolver;
use Filament\Actions\Action;
@ -27,7 +27,7 @@
use Filament\Schemas\Contracts\HasSchemas;
use Filament\Widgets\Widget;
class TenantTriageArrivalContinuity extends Widget implements HasActions, HasSchemas
class ManagedEnvironmentTriageArrivalContinuity extends Widget implements HasActions, HasSchemas
{
use InteractsWithActions;
use InteractsWithSchemas;
@ -59,7 +59,7 @@ class TenantTriageArrivalContinuity extends Widget implements HasActions, HasSch
protected int|string|array $columnSpan = 'full';
protected string $view = 'filament.widgets.tenant.triage-arrival-continuity';
protected string $view = 'filament.widgets.managed-environment.triage-arrival-continuity';
public function mount(): void
{
@ -100,14 +100,14 @@ public function markReviewedAction(): Action
->color('success')
->requiresConfirmation()
->modalHeading('Mark reviewed')
->modalDescription($this->reviewModalDescription(TenantTriageReview::STATE_REVIEWED))
->modalDescription($this->reviewModalDescription(ManagedEnvironmentTriageReview::STATE_REVIEWED))
->visible(fn (): bool => $this->canShowReviewActions())
->action(function (TenantTriageReviewService $service): void {
$this->handleReviewMutation(TenantTriageReview::STATE_REVIEWED, $service);
->action(function (ManagedEnvironmentTriageReviewService $service): void {
$this->handleReviewMutation(ManagedEnvironmentTriageReview::STATE_REVIEWED, $service);
}),
)
->preserveVisibility()
->requireCapability(Capabilities::TENANT_TRIAGE_REVIEW_MANAGE)
->requireCapability(Capabilities::MANAGED_ENVIRONMENT_TRIAGE_REVIEW_MANAGE)
->apply();
}
@ -120,14 +120,14 @@ public function markFollowUpNeededAction(): Action
->color('warning')
->requiresConfirmation()
->modalHeading('Mark follow-up needed')
->modalDescription($this->reviewModalDescription(TenantTriageReview::STATE_FOLLOW_UP_NEEDED))
->modalDescription($this->reviewModalDescription(ManagedEnvironmentTriageReview::STATE_FOLLOW_UP_NEEDED))
->visible(fn (): bool => $this->canShowReviewActions())
->action(function (TenantTriageReviewService $service): void {
$this->handleReviewMutation(TenantTriageReview::STATE_FOLLOW_UP_NEEDED, $service);
->action(function (ManagedEnvironmentTriageReviewService $service): void {
$this->handleReviewMutation(ManagedEnvironmentTriageReview::STATE_FOLLOW_UP_NEEDED, $service);
}),
)
->preserveVisibility()
->requireCapability(Capabilities::TENANT_TRIAGE_REVIEW_MANAGE)
->requireCapability(Capabilities::MANAGED_ENVIRONMENT_TRIAGE_REVIEW_MANAGE)
->apply();
}
@ -170,10 +170,10 @@ private function reviewModalDescription(string $targetManualState): \Closure
}
$currentLabel = BadgeRenderer::spec(
BadgeDomain::TenantTriageReviewState,
(string) ($reviewState['derived_state'] ?? TenantTriageReview::DERIVED_STATE_NOT_REVIEWED),
BadgeDomain::ManagedEnvironmentTriageReviewState,
(string) ($reviewState['derived_state'] ?? ManagedEnvironmentTriageReview::DERIVED_STATE_NOT_REVIEWED),
)->label;
$targetLabel = BadgeRenderer::spec(BadgeDomain::TenantTriageReviewState, $targetManualState)->label;
$targetLabel = BadgeRenderer::spec(BadgeDomain::ManagedEnvironmentTriageReviewState, $targetManualState)->label;
return implode("\n\n", [
'Concern family: '.$this->concernFamilyLabel($context->concernFamily),
@ -184,7 +184,7 @@ private function reviewModalDescription(string $targetManualState): \Closure
};
}
private function handleReviewMutation(string $targetManualState, TenantTriageReviewService $service): void
private function handleReviewMutation(string $targetManualState, ManagedEnvironmentTriageReviewService $service): void
{
$tenant = Filament::getTenant();
@ -219,14 +219,14 @@ private function handleReviewMutation(string $targetManualState, TenantTriageRev
$actor = auth()->user();
$review = match ($targetManualState) {
TenantTriageReview::STATE_REVIEWED => $service->markReviewed(
ManagedEnvironmentTriageReview::STATE_REVIEWED => $service->markReviewed(
tenant: $tenant,
concernFamily: $context->concernFamily,
backupHealth: $concernTruth['backupHealth'],
recoveryEvidence: $concernTruth['recoveryEvidence'],
actor: $actor instanceof User ? $actor : null,
),
TenantTriageReview::STATE_FOLLOW_UP_NEEDED => $service->markFollowUpNeeded(
ManagedEnvironmentTriageReview::STATE_FOLLOW_UP_NEEDED => $service->markFollowUpNeeded(
tenant: $tenant,
concernFamily: $context->concernFamily,
backupHealth: $concernTruth['backupHealth'],
@ -236,7 +236,7 @@ private function handleReviewMutation(string $targetManualState, TenantTriageRev
default => null,
};
if (! $review instanceof TenantTriageReview) {
if (! $review instanceof ManagedEnvironmentTriageReview) {
return;
}
@ -247,7 +247,7 @@ private function handleReviewMutation(string $targetManualState, TenantTriageRev
->body(sprintf(
'%s is now %s for %s.',
$tenant->name,
BadgeRenderer::spec(BadgeDomain::TenantTriageReviewState, $review->current_state)->label,
BadgeRenderer::spec(BadgeDomain::ManagedEnvironmentTriageReviewState, $review->current_state)->label,
$this->concernFamilyLabel($context->concernFamily),
))
->success()
@ -268,7 +268,7 @@ private function currentReviewStateFor(ManagedEnvironment $tenant, string $conce
$concernTruth = $this->concernTruthFor($tenant);
$reviewState = app(TenantTriageReviewStateResolver::class)->resolveMany(
$reviewState = app(ManagedEnvironmentTriageReviewStateResolver::class)->resolveMany(
workspaceId: (int) $tenant->workspace_id,
tenantIds: [$tenantId],
backupHealthByTenant: [$tenantId => $concernTruth['backupHealth']],

View File

@ -2,9 +2,9 @@
declare(strict_types=1);
namespace App\Filament\Widgets\Tenant;
namespace App\Filament\Widgets\ManagedEnvironment;
use App\Filament\Resources\TenantResource\Pages\ViewTenant;
use App\Filament\Resources\ManagedEnvironmentResource\Pages\ViewManagedEnvironment;
use App\Filament\Support\VerificationReportChangeIndicator;
use App\Filament\Support\VerificationReportViewer;
use App\Models\OperationRun;
@ -24,11 +24,11 @@
use Filament\Notifications\Notification;
use Filament\Widgets\Widget;
class TenantVerificationReport extends Widget
class ManagedEnvironmentVerificationReport extends Widget
{
protected static bool $isLazy = false;
protected string $view = 'filament.widgets.tenant.tenant-verification-report';
protected string $view = 'filament.widgets.managed-environment.managed-environment-verification-report';
public ?ManagedEnvironment $record = null;
@ -203,7 +203,7 @@ protected function getViewData(): array
'startTooltip' => $isTenantMember && $canOperate && ! $canStart ? UiTooltips::insufficientPermission() : null,
'lifecycleNotice' => $lifecycleNotice,
'rerunHint' => $run instanceof OperationRun && $canStart
? ViewTenant::verificationHeaderActionHint()
? ViewManagedEnvironment::verificationHeaderActionHint()
: null,
];
}

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace App\Filament\Widgets\Tenant;
namespace App\Filament\Widgets\ManagedEnvironment;
use App\Models\OperationRun;
use App\Models\ManagedEnvironment;
@ -15,7 +15,7 @@ class RecentOperationsSummary extends Widget
{
protected static bool $isLazy = false;
protected string $view = 'filament.widgets.tenant.recent-operations-summary';
protected string $view = 'filament.widgets.managed-environment.recent-operations-summary';
public ?ManagedEnvironment $record = null;

View File

@ -4,7 +4,7 @@
use App\Models\ProviderConnection;
use App\Models\ManagedEnvironment;
use App\Models\TenantOnboardingSession;
use App\Models\ManagedEnvironmentOnboardingSession;
use App\Services\Intune\AuditLogger;
use App\Support\Providers\ProviderConnectionType;
use App\Support\Providers\ProviderConsentStatus;
@ -23,8 +23,8 @@ public function __invoke(
Request $request,
AuditLogger $auditLogger,
): View {
$expectedState = $request->session()->pull('tenant_onboard_state');
$workspaceId = $request->session()->pull('tenant_onboard_workspace_id');
$expectedState = $request->session()->pull('environment_onboard_state');
$workspaceId = $request->session()->pull('environment_onboard_workspace_id');
$tenantKey = $request->string('tenant')->toString();
$state = $request->string('state')->toString();
$tenantIdentifier = $tenantKey ?: $this->parseState($state);
@ -219,10 +219,10 @@ private function verificationStateLabel(ProviderConnection $connection): string
private function invalidateResumableOnboardingVerificationState(ManagedEnvironment $tenant, ProviderConnection $connection): void
{
TenantOnboardingSession::query()
ManagedEnvironmentOnboardingSession::query()
->where('managed_environment_id', (int) $tenant->getKey())
->resumable()
->each(function (TenantOnboardingSession $draft) use ($connection): void {
->each(function (ManagedEnvironmentOnboardingSession $draft) use ($connection): void {
$state = is_array($draft->state) ? $draft->state : [];
$providerConnectionId = $state['provider_connection_id'] ?? null;
$providerConnectionId = is_numeric($providerConnectionId) ? (int) $providerConnectionId : null;

View File

@ -11,7 +11,7 @@
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
final class ClearTenantContextController
final class ClearEnvironmentContextController
{
public function __invoke(Request $request): RedirectResponse
{
@ -38,13 +38,13 @@ public function __invoke(Request $request): RedirectResponse
$workspace = $workspaceContext->currentWorkspace($request);
if ($workspace !== null) {
return redirect()->route('admin.workspace.managed-tenants.index', ['workspace' => $workspace]);
return redirect()->route('admin.workspace.managed-environments.index', ['workspace' => $workspace]);
}
return redirect()->route('admin.home');
}
if ($previousPath === '' || $previousPath === '/admin/clear-tenant-context') {
if ($previousPath === '' || $previousPath === '/admin/clear-environment-context') {
return redirect()->to(OperationRunLinks::index());
}

View File

@ -16,7 +16,7 @@
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\Response as ResponseAlias;
class TenantOnboardingController extends Controller
class ManagedEnvironmentOnboardingController extends Controller
{
public function __invoke(
Request $request,
@ -27,12 +27,12 @@ public function __invoke(
abort_if($tenantIdentifier === '', ResponseAlias::HTTP_NOT_FOUND);
$state = Str::uuid()->toString();
$request->session()->put('tenant_onboard_state', $state);
$request->session()->put('environment_onboard_state', $state);
$workspaceId = app(WorkspaceContext::class)->currentWorkspaceId($request);
if ($workspaceId !== null) {
$request->session()->put('tenant_onboard_workspace_id', (int) $workspaceId);
$request->session()->put('environment_onboard_workspace_id', (int) $workspaceId);
}
$tenant = $this->resolveTenant($tenantIdentifier, is_numeric($workspaceId) ? (int) $workspaceId : null);

View File

@ -16,7 +16,7 @@
final class OpenFindingExceptionsQueueController extends Controller
{
public function __invoke(Request $request, ManagedEnvironment $tenant): RedirectResponse
public function __invoke(Request $request, ManagedEnvironment $environment): RedirectResponse
{
$user = auth()->user();
@ -24,13 +24,13 @@ public function __invoke(Request $request, ManagedEnvironment $tenant): Redirect
abort(403);
}
$workspace = Workspace::query()->whereKey($tenant->workspace_id)->first();
$workspace = Workspace::query()->whereKey($environment->workspace_id)->first();
if (! $workspace instanceof Workspace) {
abort(404);
}
if (! $user->canAccessTenant($tenant)) {
if (! $user->canAccessTenant($environment)) {
abort(404);
}
@ -49,12 +49,12 @@ public function __invoke(Request $request, ManagedEnvironment $tenant): Redirect
$workspaceContext->setCurrentWorkspace($workspace, $user, $request);
if (! $workspaceContext->rememberTenantContext($tenant, $request)) {
if (! $workspaceContext->rememberTenantContext($environment, $request)) {
abort(404);
}
return redirect()->to(FindingExceptionsQueue::getUrl([
'tenant' => (string) $tenant->external_id,
'tenant' => (string) $environment->external_id,
], panel: 'admin'));
}
}

View File

@ -55,8 +55,8 @@ public function __invoke(Request $request, ReviewPack $reviewPack): StreamedResp
context: [
'metadata' => [
'review_pack_id' => (int) $reviewPack->getKey(),
'tenant_review_id' => $reviewPack->tenant_review_id !== null
? (int) $reviewPack->tenant_review_id
'environment_review_id' => $reviewPack->environment_review_id !== null
? (int) $reviewPack->environment_review_id
: null,
'source_surface' => (string) $request->query('source_surface', 'review_pack'),
'review_id' => $request->query('review_id'),

View File

@ -16,7 +16,7 @@
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Schema;
final class SelectTenantController
final class SelectEnvironmentController
{
public function __invoke(Request $request): RedirectResponse
{

View File

@ -204,7 +204,7 @@ private function isLivewireUpdatePath(string $path): bool
private function isChooserFirstPath(string $path): bool
{
return in_array($path, ['/admin', '/admin/choose-tenant'], true);
return in_array($path, ['/admin', '/admin/choose-environment'], true);
}
private function requestHasExplicitTenantContext(Request $request): bool

View File

@ -69,9 +69,9 @@ public function handle(OperationRunService $runs): void
$chunkSize = max(1, $chunkSize);
foreach (array_chunk($ids, $chunkSize) as $chunk) {
foreach ($chunk as $targetTenantId) {
foreach ($chunk as $targetEnvironmentId) {
dispatch(new TenantSyncWorkerJob(
tenantId: $targetTenantId,
tenantId: $targetEnvironmentId,
userId: $this->userId,
operationRun: $this->operationRun,
context: $this->context,

View File

@ -100,7 +100,7 @@ public function handle(
$context = is_array($this->operationRun->context) ? $this->operationRun->context : [];
$profileId = (int) ($context['baseline_profile_id'] ?? 0);
$sourceTenantId = (int) ($context['source_tenant_id'] ?? 0);
$sourceEnvironmentId = (int) ($context['source_environment_id'] ?? 0);
$profile = BaselineProfile::query()->find($profileId);
@ -108,10 +108,10 @@ public function handle(
throw new RuntimeException("BaselineProfile #{$profileId} not found.");
}
$sourceTenant = ManagedEnvironment::query()->find($sourceTenantId);
$sourceTenant = ManagedEnvironment::query()->find($sourceEnvironmentId);
if (! $sourceTenant instanceof ManagedEnvironment) {
throw new RuntimeException("Source ManagedEnvironment #{$sourceTenantId} not found.");
throw new RuntimeException("Source ManagedEnvironment #{$sourceEnvironmentId} not found.");
}
$initiator = $this->operationRun->user_id

View File

@ -6,17 +6,17 @@
use App\Jobs\Concerns\BridgesFailedOperationRun;
use App\Models\OperationRun;
use App\Models\TenantReview;
use App\Models\EnvironmentReview;
use App\Services\OperationRunService;
use App\Services\TenantReviews\TenantReviewService;
use App\Services\EnvironmentReviews\EnvironmentReviewService;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use App\Support\TenantReviewStatus;
use App\Support\EnvironmentReviewStatus;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Throwable;
class ComposeTenantReviewJob implements ShouldQueue
class ComposeEnvironmentReviewJob implements ShouldQueue
{
use BridgesFailedOperationRun;
use Queueable;
@ -26,21 +26,21 @@ class ComposeTenantReviewJob implements ShouldQueue
public bool $failOnTimeout = true;
public function __construct(
public int $tenantReviewId,
public int $environmentReviewId,
public int $operationRunId,
) {}
public function handle(TenantReviewService $service, OperationRunService $operationRuns): void
public function handle(EnvironmentReviewService $service, OperationRunService $operationRuns): void
{
$review = TenantReview::query()->with(['tenant', 'evidenceSnapshot.items'])->find($this->tenantReviewId);
$review = EnvironmentReview::query()->with(['tenant', 'evidenceSnapshot.items'])->find($this->environmentReviewId);
$operationRun = OperationRun::query()->find($this->operationRunId);
if (! $review instanceof TenantReview || ! $operationRun instanceof OperationRun || ! $review->tenant) {
if (! $review instanceof EnvironmentReview || ! $operationRun instanceof OperationRun || ! $review->tenant) {
return;
}
$operationRuns->updateRun($operationRun, OperationRunStatus::Running->value, OperationRunOutcome::Pending->value);
$review->update(['status' => TenantReviewStatus::Draft->value]);
$review->update(['status' => EnvironmentReviewStatus::Draft->value]);
try {
$review = $service->compose($review);
@ -61,7 +61,7 @@ public function handle(TenantReviewService $service, OperationRunService $operat
);
} catch (Throwable $throwable) {
$review->update([
'status' => TenantReviewStatus::Failed->value,
'status' => EnvironmentReviewStatus::Failed->value,
'summary' => array_merge(is_array($review->summary) ? $review->summary : [], [
'error' => $throwable->getMessage(),
]),
@ -73,7 +73,7 @@ public function handle(TenantReviewService $service, OperationRunService $operat
outcome: OperationRunOutcome::Failed->value,
failures: [
[
'code' => 'tenant_review_compose.failed',
'code' => 'environment_review_compose.failed',
'message' => $throwable->getMessage(),
],
],

View File

@ -9,7 +9,7 @@
use App\Models\OperationRun;
use App\Models\ReviewPack;
use App\Models\ManagedEnvironment;
use App\Models\TenantReview;
use App\Models\EnvironmentReview;
use App\Services\Intune\SecretClassificationService;
use App\Services\OperationRunService;
use App\Services\ReviewPackService;
@ -39,7 +39,7 @@ public function __construct(
public function handle(OperationRunService $operationRunService): void
{
$reviewPack = ReviewPack::query()->with(['tenant', 'evidenceSnapshot.items', 'tenantReview.sections'])->find($this->reviewPackId);
$reviewPack = ReviewPack::query()->with(['tenant', 'evidenceSnapshot.items', 'environmentReview.sections'])->find($this->reviewPackId);
$operationRun = OperationRun::query()->find($this->operationRunId);
if (! $reviewPack instanceof ReviewPack || ! $operationRun instanceof OperationRun) {
@ -82,9 +82,9 @@ public function handle(OperationRunService $operationRunService): void
private function executeGeneration(ReviewPack $reviewPack, OperationRun $operationRun, ManagedEnvironment $tenant, EvidenceSnapshot $snapshot, OperationRunService $operationRunService): void
{
$review = $reviewPack->tenantReview;
$review = $reviewPack->environmentReview;
if ($review instanceof TenantReview) {
if ($review instanceof EnvironmentReview) {
$this->executeReviewDerivedGeneration($reviewPack, $review, $operationRun, $tenant, $snapshot, $operationRunService);
return;
@ -216,7 +216,7 @@ private function executeGeneration(ReviewPack $reviewPack, OperationRun $operati
private function executeReviewDerivedGeneration(
ReviewPack $reviewPack,
TenantReview $review,
EnvironmentReview $review,
OperationRun $operationRun,
ManagedEnvironment $tenant,
EvidenceSnapshot $snapshot,
@ -280,7 +280,7 @@ private function executeReviewDerivedGeneration(
$fingerprint = app(ReviewPackService::class)->computeFingerprintForReview($review, $options);
$reviewSummary = is_array($review->summary) ? $review->summary : [];
$summary = [
'tenant_review_id' => (int) $review->getKey(),
'environment_review_id' => (int) $review->getKey(),
'review_status' => (string) $review->status,
'review_completeness_state' => (string) $review->completeness_state,
'section_count' => $review->sections->count(),
@ -636,7 +636,7 @@ private function assembleZip(string $tempFile, array $fileMap, ?callable $afterW
*/
private function buildReviewDerivedFileMap(
ReviewPack $reviewPack,
TenantReview $review,
EnvironmentReview $review,
ManagedEnvironment $tenant,
EvidenceSnapshot $snapshot,
bool $includePii,
@ -662,7 +662,7 @@ private function buildReviewDerivedFileMap(
'tenant_name' => $includePii ? $tenant->name : '[REDACTED]',
'generated_at' => $generatedAt->toIso8601String(),
'delivery_bundle' => $deliveryMetadata,
'tenant_review' => [
'environment_review' => [
'id' => (int) $review->getKey(),
'status' => (string) $review->status,
'completeness_state' => (string) $review->completeness_state,
@ -686,7 +686,7 @@ private function buildReviewDerivedFileMap(
], JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR),
'summary.json' => json_encode($this->redactReportPayload(array_merge(
[
'tenant_review_id' => (int) $review->getKey(),
'environment_review_id' => (int) $review->getKey(),
'review_status' => (string) $review->status,
'review_completeness_state' => (string) $review->completeness_state,
],
@ -738,7 +738,7 @@ private function buildReviewDerivedFileMap(
/**
* @return array<string, mixed>
*/
private function deliveryBundleSummary(TenantReview $review): array
private function deliveryBundleSummary(EnvironmentReview $review): array
{
return [
'contract' => ReviewPackService::REVIEW_DERIVED_DELIVERY_CONTRACT,
@ -753,7 +753,7 @@ private function deliveryBundleSummary(TenantReview $review): array
*/
private function deliveryBundleMetadata(
ReviewPack $reviewPack,
TenantReview $review,
EnvironmentReview $review,
EvidenceSnapshot $snapshot,
\Carbon\CarbonInterface $generatedAt,
): array {
@ -805,7 +805,7 @@ private function deliveryBundleMetadata(
* @param array<string, mixed> $reviewSummary
*/
private function buildExecutiveEntrypoint(
TenantReview $review,
EnvironmentReview $review,
ManagedEnvironment $tenant,
EvidenceSnapshot $snapshot,
array $reviewSummary,

View File

@ -28,7 +28,7 @@
use RuntimeException;
use Throwable;
final class CrossTenantPromotionExecutionJob implements ShouldQueue
final class CrossEnvironmentPromotionExecutionJob implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
@ -73,16 +73,16 @@ public function handle(
return;
}
$tenant = $this->operationRun->tenant;
$targetEnvironment = $this->operationRun->tenant;
if (! $tenant instanceof ManagedEnvironment) {
throw new RuntimeException('Promotion execution target tenant is missing.');
if (! $targetEnvironment instanceof ManagedEnvironment) {
throw new RuntimeException('Promotion execution target environment is missing.');
}
$context = is_array($this->operationRun->context) ? $this->operationRun->context : [];
$targetScope = is_array($context['target_scope'] ?? null) ? $context['target_scope'] : [];
$lock = $limiter->acquireSlot((int) $tenant->getKey(), $targetScope);
$lock = $limiter->acquireSlot((int) $targetEnvironment->getKey(), $targetScope);
if (! $lock) {
$this->release(max(1, (int) config('tenantpilot.bulk_operations.poll_interval_seconds', 3)));
@ -114,7 +114,7 @@ public function handle(
$summary['total'] = count($items);
[$backupSet, $selectedItemIds, $preRestoreSummary, $preRestoreFailures] = $this->buildRestoreInputs(
tenant: $tenant,
targetEnvironment: $targetEnvironment,
operationRun: $this->operationRun,
items: $items,
);
@ -126,7 +126,7 @@ public function handle(
if ($selectedItemIds !== []) {
$restoreRun = RestoreRun::withoutEvents(fn (): RestoreRun => $restoreService->execute(
tenant: $tenant,
tenant: $targetEnvironment,
backupSet: $backupSet,
selectedItemIds: $selectedItemIds,
dryRun: false,
@ -160,10 +160,10 @@ public function handle(
failures: $failures,
);
$auditLogger->logCrossTenantPromotionExecutionCompleted(
$auditLogger->logCrossEnvironmentPromotionExecutionCompleted(
operationRun: $updated,
sourceTenantId: is_numeric($context['source_tenant_id'] ?? null) ? (int) $context['source_tenant_id'] : null,
targetTenant: $tenant,
sourceEnvironmentId: is_numeric($context['source_environment_id'] ?? null) ? (int) $context['source_environment_id'] : null,
targetEnvironment: $targetEnvironment,
summaryCounts: $summary,
restoreRun: $restoreRun,
);
@ -183,7 +183,7 @@ public function getOperationRun(): ?OperationRun
* @param list<array<string, mixed>> $items
* @return array{0: ?BackupSet, 1: list<int>, 2: array<string, int>, 3: list<array{code: string, message: string}>}
*/
private function buildRestoreInputs(ManagedEnvironment $tenant, OperationRun $operationRun, array $items): array
private function buildRestoreInputs(ManagedEnvironment $targetEnvironment, OperationRun $operationRun, array $items): array
{
$summary = [
'processed' => 0,
@ -195,14 +195,14 @@ private function buildRestoreInputs(ManagedEnvironment $tenant, OperationRun $op
];
$failures = [];
$backupSet = BackupSet::query()->create([
'managed_environment_id' => (int) $tenant->getKey(),
'name' => 'Cross-tenant promotion • Operation #'.$operationRun->getKey(),
'managed_environment_id' => (int) $targetEnvironment->getKey(),
'name' => 'Cross-environment promotion • Operation #'.$operationRun->getKey(),
'created_by' => $operationRun->user?->email,
'status' => 'completed',
'item_count' => 0,
'completed_at' => CarbonImmutable::now(),
'metadata' => [
'source' => 'cross_tenant_promotion',
'source' => 'cross_environment_promotion',
'operation_run_id' => (int) $operationRun->getKey(),
],
]);
@ -219,13 +219,13 @@ private function buildRestoreInputs(ManagedEnvironment $tenant, OperationRun $op
}
$versionId = data_get($item, 'source.policy_version_id');
$sourceTenantId = data_get($item, 'source.managed_environment_id');
$sourceEnvironmentId = data_get($item, 'source.managed_environment_id');
$version = is_numeric($versionId) && is_numeric($sourceTenantId)
$version = is_numeric($versionId) && is_numeric($sourceEnvironmentId)
? PolicyVersion::query()
->with('policy')
->whereKey((int) $versionId)
->where('managed_environment_id', (int) $sourceTenantId)
->where('managed_environment_id', (int) $sourceEnvironmentId)
->first()
: null;
@ -248,13 +248,13 @@ private function buildRestoreInputs(ManagedEnvironment $tenant, OperationRun $op
: (is_string($sourceExternalId) && trim($sourceExternalId) !== '' ? trim($sourceExternalId) : (string) $sourcePolicy->external_id);
$targetPolicy = Policy::query()
->where('managed_environment_id', (int) $tenant->getKey())
->where('managed_environment_id', (int) $targetEnvironment->getKey())
->where('policy_type', (string) $sourcePolicy->policy_type)
->where('external_id', $policyIdentifier)
->first();
$backupItem = BackupItem::query()->create([
'managed_environment_id' => (int) $tenant->getKey(),
'managed_environment_id' => (int) $targetEnvironment->getKey(),
'backup_set_id' => (int) $backupSet->getKey(),
'policy_id' => $targetPolicy?->getKey(),
'policy_identifier' => $policyIdentifier,
@ -263,10 +263,10 @@ private function buildRestoreInputs(ManagedEnvironment $tenant, OperationRun $op
'captured_at' => $version->captured_at ?? CarbonImmutable::now(),
'payload' => is_array($version->snapshot) ? $version->snapshot : [],
'metadata' => [
'source' => 'cross_tenant_promotion',
'source' => 'cross_environment_promotion',
'display_name' => (string) $sourcePolicy->display_name,
'operation_run_id' => (int) $operationRun->getKey(),
'source_tenant_id' => (int) $sourcePolicy->managed_environment_id,
'source_environment_id' => (int) $sourcePolicy->managed_environment_id,
'source_policy_id' => (int) $sourcePolicy->getKey(),
'source_policy_version_id' => (int) $version->getKey(),
'source_subject_key' => (string) ($item['subject_key'] ?? ''),

View File

@ -10,7 +10,7 @@
use App\Models\User;
use App\Services\Audit\WorkspaceAuditLogger;
use App\Services\Intune\AuditLogger as TenantAuditLogger;
use App\Services\Intune\TenantPermissionService;
use App\Services\Intune\ManagedEnvironmentPermissionService;
use App\Services\OperationRunService;
use App\Services\Providers\Contracts\HealthResult;
use App\Services\Providers\MicrosoftProviderHealthCheck;
@ -24,7 +24,7 @@
use App\Support\Providers\ProviderNextStepsRegistry;
use App\Support\Providers\ProviderReasonCodes;
use App\Support\Providers\TargetScope\ProviderConnectionTargetScopeNormalizer;
use App\Support\Verification\TenantPermissionCheckClusters;
use App\Support\Verification\ManagedEnvironmentPermissionCheckClusters;
use App\Support\Verification\VerificationReportWriter;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
@ -99,7 +99,7 @@ public function handle(
$this->updateRunTargetScope($this->operationRun, $connection, $entraTenantName);
$permissionService = app(TenantPermissionService::class);
$permissionService = app(ManagedEnvironmentPermissionService::class);
$graphOptions = null;
@ -178,7 +178,7 @@ public function handle(
];
}
$permissionChecks = TenantPermissionCheckClusters::buildChecks($tenant, $permissionRows, $inventory);
$permissionChecks = ManagedEnvironmentPermissionCheckClusters::buildChecks($tenant, $permissionRows, $inventory);
$targetScope = app(ProviderConnectionTargetScopeNormalizer::class)
->descriptorForConnection($connection)
->toArray();

View File

@ -6,15 +6,15 @@
use App\Support\Concerns\DerivesWorkspaceIdFromTenant;
use App\Support\Governance\Controls\ComplianceEvidenceMappingV1;
use App\Support\TenantReviewCompletenessState;
use App\Support\TenantReviewStatus;
use App\Support\EnvironmentReviewCompletenessState;
use App\Support\EnvironmentReviewStatus;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class TenantReview extends Model
class EnvironmentReview extends Model
{
use DerivesWorkspaceIdFromTenant;
use HasFactory;
@ -107,11 +107,11 @@ public function supersededReviews(): HasMany
}
/**
* @return HasMany<TenantReviewSection, $this>
* @return HasMany<EnvironmentReviewSection, $this>
*/
public function sections(): HasMany
{
return $this->hasMany(TenantReviewSection::class)->orderBy('sort_order')->orderBy('id');
return $this->hasMany(EnvironmentReviewSection::class)->orderBy('sort_order')->orderBy('id');
}
/**
@ -146,7 +146,7 @@ public function scopeForWorkspace(Builder $query, int $workspaceId): Builder
*/
public function scopePublished(Builder $query): Builder
{
return $query->where('status', TenantReviewStatus::Published->value);
return $query->where('status', EnvironmentReviewStatus::Published->value);
}
/**
@ -156,21 +156,21 @@ public function scopePublished(Builder $query): Builder
public function scopeMutable(Builder $query): Builder
{
return $query->whereIn('status', [
TenantReviewStatus::Draft->value,
TenantReviewStatus::Ready->value,
TenantReviewStatus::Failed->value,
EnvironmentReviewStatus::Draft->value,
EnvironmentReviewStatus::Ready->value,
EnvironmentReviewStatus::Failed->value,
]);
}
public function statusEnum(): TenantReviewStatus
public function statusEnum(): EnvironmentReviewStatus
{
return TenantReviewStatus::from((string) $this->status);
return EnvironmentReviewStatus::from((string) $this->status);
}
public function completenessEnum(): TenantReviewCompletenessState
public function completenessEnum(): EnvironmentReviewCompletenessState
{
return TenantReviewCompletenessState::tryFrom((string) $this->completeness_state)
?? TenantReviewCompletenessState::Missing;
return EnvironmentReviewCompletenessState::tryFrom((string) $this->completeness_state)
?? EnvironmentReviewCompletenessState::Missing;
}
public function isPublished(): bool
@ -253,12 +253,12 @@ public function controlInterpretationLimitationCounts(): array
->all();
}
public function controlInterpretationSection(): ?TenantReviewSection
public function controlInterpretationSection(): ?EnvironmentReviewSection
{
if ($this->relationLoaded('sections')) {
$section = $this->sections->firstWhere('section_key', ComplianceEvidenceMappingV1::SECTION_KEY);
return $section instanceof TenantReviewSection ? $section : null;
return $section instanceof EnvironmentReviewSection ? $section : null;
}
return $this->sections()

View File

@ -4,14 +4,14 @@
namespace App\Models;
use App\Support\TenantReviewCompletenessState;
use App\Support\EnvironmentReviewCompletenessState;
use App\Support\Governance\Controls\ComplianceEvidenceMappingV1;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class TenantReviewSection extends Model
class EnvironmentReviewSection extends Model
{
use HasFactory;
@ -31,11 +31,11 @@ protected function casts(): array
}
/**
* @return BelongsTo<TenantReview, $this>
* @return BelongsTo<EnvironmentReview, $this>
*/
public function tenantReview(): BelongsTo
public function environmentReview(): BelongsTo
{
return $this->belongsTo(TenantReview::class);
return $this->belongsTo(EnvironmentReview::class);
}
/**
@ -63,10 +63,10 @@ public function scopeRequired(Builder $query): Builder
return $query->where('required', true);
}
public function completenessEnum(): TenantReviewCompletenessState
public function completenessEnum(): EnvironmentReviewCompletenessState
{
return TenantReviewCompletenessState::tryFrom((string) $this->completeness_state)
?? TenantReviewCompletenessState::Missing;
return EnvironmentReviewCompletenessState::tryFrom((string) $this->completeness_state)
?? EnvironmentReviewCompletenessState::Missing;
}
public function isControlInterpretation(): bool

View File

@ -81,11 +81,11 @@ public function reviewPacks(): HasMany
}
/**
* @return HasMany<TenantReview, $this>
* @return HasMany<EnvironmentReview, $this>
*/
public function tenantReviews(): HasMany
public function environmentReviews(): HasMany
{
return $this->hasMany(TenantReview::class);
return $this->hasMany(EnvironmentReview::class);
}
/**

View File

@ -411,9 +411,9 @@ public function evidenceSnapshots(): HasMany
return $this->hasMany(EvidenceSnapshot::class);
}
public function tenantReviews(): HasMany
public function environmentReviews(): HasMany
{
return $this->hasMany(TenantReview::class);
return $this->hasMany(EnvironmentReview::class);
}
public function settings(): HasMany
@ -423,7 +423,7 @@ public function settings(): HasMany
public function permissions(): HasMany
{
return $this->hasMany(TenantPermission::class);
return $this->hasMany(ManagedEnvironmentPermission::class);
}
public function providerConnections(): HasMany

View File

@ -12,12 +12,12 @@
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class TenantOnboardingSession extends Model
class ManagedEnvironmentOnboardingSession extends Model
{
/** @use HasFactory<\Database\Factories\TenantOnboardingSessionFactory> */
/** @use HasFactory<\Database\Factories\ManagedEnvironmentOnboardingSessionFactory> */
use HasFactory;
protected $table = 'managed_tenant_onboarding_sessions';
protected $table = 'managed_environment_onboarding_sessions';
/**
* @var array<int, string>
@ -25,7 +25,7 @@ class TenantOnboardingSession extends Model
public const STATE_ALLOWED_KEYS = [
'entra_tenant_id',
'managed_environment_id',
'tenant_name',
'environment_name',
'environment',
'primary_domain',
'notes',
@ -80,11 +80,19 @@ public function workspace(): BelongsTo
/**
* @return BelongsTo<ManagedEnvironment, $this>
*/
public function tenant(): BelongsTo
public function managedEnvironment(): BelongsTo
{
return $this->belongsTo(ManagedEnvironment::class, 'managed_environment_id')->withTrashed();
}
/**
* @return BelongsTo<ManagedEnvironment, $this>
*/
public function tenant(): BelongsTo
{
return $this->managedEnvironment();
}
/**
* @return BelongsTo<User, $this>
*/

View File

@ -7,7 +7,7 @@
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class TenantPermission extends Model
class ManagedEnvironmentPermission extends Model
{
use DerivesWorkspaceIdFromTenant;
use HasFactory;

View File

@ -10,7 +10,7 @@
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class TenantTriageReview extends Model
class ManagedEnvironmentTriageReview extends Model
{
use HasFactory;

View File

@ -80,11 +80,11 @@ public function evidenceSnapshot(): BelongsTo
}
/**
* @return BelongsTo<TenantReview, $this>
* @return BelongsTo<EnvironmentReview, $this>
*/
public function tenantReview(): BelongsTo
public function environmentReview(): BelongsTo
{
return $this->belongsTo(TenantReview::class);
return $this->belongsTo(EnvironmentReview::class);
}
/**

View File

@ -5,14 +5,14 @@
namespace App\Policies;
use App\Models\ManagedEnvironment;
use App\Models\TenantReview;
use App\Models\EnvironmentReview;
use App\Models\User;
use App\Services\Auth\CapabilityResolver;
use App\Support\Auth\Capabilities;
use Illuminate\Auth\Access\HandlesAuthorization;
use Illuminate\Auth\Access\Response;
class TenantReviewPolicy
class EnvironmentReviewPolicy
{
use HandlesAuthorization;
@ -24,10 +24,10 @@ public function viewAny(User $user): bool
return false;
}
return app(CapabilityResolver::class)->can($user, $tenant, Capabilities::TENANT_REVIEW_VIEW);
return app(CapabilityResolver::class)->can($user, $tenant, Capabilities::ENVIRONMENT_REVIEW_VIEW);
}
public function view(User $user, TenantReview $review): Response|bool
public function view(User $user, EnvironmentReview $review): Response|bool
{
$tenant = $this->authorizedTenantOrNull($user, $review);
@ -35,7 +35,7 @@ public function view(User $user, TenantReview $review): Response|bool
return Response::denyAsNotFound();
}
return app(CapabilityResolver::class)->can($user, $tenant, Capabilities::TENANT_REVIEW_VIEW)
return app(CapabilityResolver::class)->can($user, $tenant, Capabilities::ENVIRONMENT_REVIEW_VIEW)
? true
: Response::deny();
}
@ -48,35 +48,35 @@ public function create(User $user): bool
return false;
}
return app(CapabilityResolver::class)->can($user, $tenant, Capabilities::TENANT_REVIEW_MANAGE);
return app(CapabilityResolver::class)->can($user, $tenant, Capabilities::ENVIRONMENT_REVIEW_MANAGE);
}
public function refresh(User $user, TenantReview $review): Response|bool
public function refresh(User $user, EnvironmentReview $review): Response|bool
{
return $this->authorizeManageAction($user, $review);
}
public function publish(User $user, TenantReview $review): Response|bool
public function publish(User $user, EnvironmentReview $review): Response|bool
{
return $this->authorizeManageAction($user, $review);
}
public function archive(User $user, TenantReview $review): Response|bool
public function archive(User $user, EnvironmentReview $review): Response|bool
{
return $this->authorizeManageAction($user, $review);
}
public function export(User $user, TenantReview $review): Response|bool
public function export(User $user, EnvironmentReview $review): Response|bool
{
return $this->authorizeManageAction($user, $review);
}
public function createNextReview(User $user, TenantReview $review): Response|bool
public function createNextReview(User $user, EnvironmentReview $review): Response|bool
{
return $this->authorizeManageAction($user, $review);
}
private function authorizeManageAction(User $user, TenantReview $review): Response|bool
private function authorizeManageAction(User $user, EnvironmentReview $review): Response|bool
{
$tenant = $this->authorizedTenantOrNull($user, $review);
@ -84,12 +84,12 @@ private function authorizeManageAction(User $user, TenantReview $review): Respon
return Response::denyAsNotFound();
}
return app(CapabilityResolver::class)->can($user, $tenant, Capabilities::TENANT_REVIEW_MANAGE)
return app(CapabilityResolver::class)->can($user, $tenant, Capabilities::ENVIRONMENT_REVIEW_MANAGE)
? true
: Response::deny();
}
private function authorizedTenantOrNull(User $user, TenantReview $review): ?ManagedEnvironment
private function authorizedTenantOrNull(User $user, EnvironmentReview $review): ?ManagedEnvironment
{
$tenant = $review->tenant;

View File

@ -5,7 +5,7 @@
namespace App\Policies;
use App\Models\ManagedEnvironment;
use App\Models\TenantOnboardingSession;
use App\Models\ManagedEnvironmentOnboardingSession;
use App\Models\User;
use App\Models\Workspace;
use App\Services\Auth\WorkspaceCapabilityResolver;
@ -17,7 +17,7 @@
use Illuminate\Auth\Access\Response;
use Illuminate\Support\Facades\Gate;
class TenantOnboardingSessionPolicy
class ManagedEnvironmentOnboardingSessionPolicy
{
public function viewAny(User $user): bool|Response
{
@ -27,33 +27,33 @@ public function viewAny(User $user): bool|Response
return Response::denyAsNotFound();
}
return $this->authorizeForWorkspace($user, $workspace, Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD);
return $this->authorizeForWorkspace($user, $workspace, Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD);
}
public function view(User $user, TenantOnboardingSession $tenantOnboardingSession): bool|Response
public function view(User $user, ManagedEnvironmentOnboardingSession $environmentOnboardingSession): bool|Response
{
return $this->authorizeForDraft(
user: $user,
tenantOnboardingSession: $tenantOnboardingSession,
capability: Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD,
environmentOnboardingSession: $environmentOnboardingSession,
capability: Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD,
);
}
public function update(User $user, TenantOnboardingSession $tenantOnboardingSession): bool|Response
public function update(User $user, ManagedEnvironmentOnboardingSession $environmentOnboardingSession): bool|Response
{
return $this->authorizeForDraft(
user: $user,
tenantOnboardingSession: $tenantOnboardingSession,
capability: Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD,
environmentOnboardingSession: $environmentOnboardingSession,
capability: Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD,
);
}
public function cancel(User $user, TenantOnboardingSession $tenantOnboardingSession): bool|Response
public function cancel(User $user, ManagedEnvironmentOnboardingSession $environmentOnboardingSession): bool|Response
{
return $this->authorizeForDraft(
user: $user,
tenantOnboardingSession: $tenantOnboardingSession,
capability: Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_CANCEL,
environmentOnboardingSession: $environmentOnboardingSession,
capability: Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CANCEL,
);
}
@ -83,7 +83,7 @@ private function currentWorkspace(User $user): ?Workspace
private function authorizeForDraft(
User $user,
TenantOnboardingSession $tenantOnboardingSession,
ManagedEnvironmentOnboardingSession $environmentOnboardingSession,
string $capability,
): bool|Response {
$workspace = $this->currentWorkspace($user);
@ -92,11 +92,11 @@ private function authorizeForDraft(
return Response::denyAsNotFound();
}
if ((int) $tenantOnboardingSession->workspace_id !== (int) $workspace->getKey()) {
if ((int) $environmentOnboardingSession->workspace_id !== (int) $workspace->getKey()) {
return Response::denyAsNotFound();
}
$tenant = $tenantOnboardingSession->tenant;
$tenant = $environmentOnboardingSession->managedEnvironment;
if ($tenant instanceof ManagedEnvironment) {
if ((int) $tenant->workspace_id !== (int) $workspace->getKey()) {
@ -140,7 +140,7 @@ private function authorizeForWorkspace(User $user, Workspace $workspace, string
private function forbiddenCapabilityMessage(string $capability): string
{
return match ($capability) {
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_CANCEL => 'You do not have permission to cancel this onboarding draft.',
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CANCEL => 'You do not have permission to cancel this onboarding draft.',
default => 'You do not have permission to continue this onboarding draft.',
};
}

View File

@ -8,8 +8,8 @@
use App\Models\PlatformUser;
use App\Models\ProviderConnection;
use App\Models\ManagedEnvironment;
use App\Models\TenantOnboardingSession;
use App\Models\TenantReview;
use App\Models\ManagedEnvironmentOnboardingSession;
use App\Models\EnvironmentReview;
use App\Models\User;
use App\Models\Workspace;
use App\Models\WorkspaceSetting;
@ -17,8 +17,8 @@
use App\Policies\AlertDestinationPolicy;
use App\Policies\AlertRulePolicy;
use App\Policies\ProviderConnectionPolicy;
use App\Policies\TenantOnboardingSessionPolicy;
use App\Policies\TenantReviewPolicy;
use App\Policies\ManagedEnvironmentOnboardingSessionPolicy;
use App\Policies\EnvironmentReviewPolicy;
use App\Policies\WorkspaceSettingPolicy;
use App\Services\Auth\CapabilityResolver;
use App\Services\Auth\WorkspaceCapabilityResolver;
@ -31,8 +31,8 @@ class AuthServiceProvider extends ServiceProvider
{
protected $policies = [
ProviderConnection::class => ProviderConnectionPolicy::class,
TenantOnboardingSession::class => TenantOnboardingSessionPolicy::class,
TenantReview::class => TenantReviewPolicy::class,
ManagedEnvironmentOnboardingSession::class => ManagedEnvironmentOnboardingSessionPolicy::class,
EnvironmentReview::class => EnvironmentReviewPolicy::class,
WorkspaceSetting::class => WorkspaceSettingPolicy::class,
AlertDestination::class => AlertDestinationPolicy::class,
AlertDelivery::class => AlertDeliveryPolicy::class,

View File

@ -4,9 +4,9 @@
use App\Filament\Pages\Auth\Login;
use App\Filament\Pages\BaselineCompareLanding;
use App\Filament\Pages\ChooseTenant;
use App\Filament\Pages\ChooseEnvironment;
use App\Filament\Pages\ChooseWorkspace;
use App\Filament\Pages\CrossTenantComparePage;
use App\Filament\Pages\CrossEnvironmentComparePage;
use App\Filament\Pages\Findings\FindingsHygieneReport;
use App\Filament\Pages\Findings\FindingsIntakeQueue;
use App\Filament\Pages\Governance\DecisionRegister;
@ -18,7 +18,7 @@
use App\Filament\Pages\Reviews\ReviewRegister;
use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
use App\Filament\Pages\Settings\WorkspaceSettings;
use App\Filament\Pages\TenantRequiredPermissions;
use App\Filament\Pages\EnvironmentRequiredPermissions;
use App\Filament\Pages\WorkspaceOverview;
use App\Filament\Resources\AlertDeliveryResource;
use App\Filament\Resources\AlertDestinationResource;
@ -28,7 +28,7 @@
use App\Filament\Resources\InventoryItemResource;
use App\Filament\Resources\PolicyResource;
use App\Filament\Resources\ProviderConnectionResource;
use App\Filament\Resources\TenantReviewResource;
use App\Filament\Resources\EnvironmentReviewResource;
use App\Filament\Resources\Workspaces\WorkspaceResource;
use App\Models\User;
use App\Models\Workspace;
@ -75,7 +75,7 @@ public function panel(Panel $panel): Panel
->font(null, provider: LocalFontProvider::class, preload: [])
->authenticatedRoutes(function (Panel $panel): void {
ChooseWorkspace::registerRoutes($panel);
ChooseTenant::registerRoutes($panel);
ChooseEnvironment::registerRoutes($panel);
NoAccess::registerRoutes($panel);
})
->colors([
@ -166,7 +166,7 @@ public function panel(Panel $panel): Panel
)
->renderHook(
PanelsRenderHook::PAGE_START,
fn (): string => request()->routeIs('admin.workspace.managed-tenants.index', 'admin.onboarding', 'admin.onboarding.draft', 'filament.admin.pages.choose-tenant')
fn (): string => request()->routeIs('admin.workspace.managed-environments.index', 'admin.onboarding', 'admin.onboarding.draft', 'filament.admin.pages.choose-environment')
? ''
: ((bool) config('tenantpilot.bulk_operations.progress_widget_enabled', true)
? view('livewire.bulk-operation-progress-wrapper')->render()
@ -182,16 +182,16 @@ public function panel(Panel $panel): Panel
WorkspaceResource::class,
BaselineProfileResource::class,
BaselineSnapshotResource::class,
TenantReviewResource::class,
EnvironmentReviewResource::class,
])
->discoverClusters(in: app_path('Filament/Clusters'), for: 'App\\Filament\\Clusters')
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
->pages([
BaselineCompareLanding::class,
InventoryCoverage::class,
TenantRequiredPermissions::class,
EnvironmentRequiredPermissions::class,
WorkspaceSettings::class,
CrossTenantComparePage::class,
CrossEnvironmentComparePage::class,
GovernanceInbox::class,
DecisionRegister::class,
FindingsHygieneReport::class,

View File

@ -143,10 +143,10 @@ public function logSupportDiagnosticsOpened(
/**
* @param array<string, mixed> $preflight
*/
public function logCrossTenantPromotionPreflightGenerated(
public function logCrossEnvironmentPromotionPreflightGenerated(
Workspace $workspace,
ManagedEnvironment $sourceTenant,
ManagedEnvironment $targetTenant,
ManagedEnvironment $sourceEnvironment,
ManagedEnvironment $targetEnvironment,
array $preflight,
User|PlatformUser|null $actor = null,
): \App\Models\AuditLog {
@ -154,12 +154,12 @@ public function logCrossTenantPromotionPreflightGenerated(
return $this->log(
workspace: $workspace,
action: AuditActionId::CrossTenantPromotionPreflightGenerated,
action: AuditActionId::CrossEnvironmentPromotionPreflightGenerated,
context: [
'source_tenant_id' => (int) $sourceTenant->getKey(),
'source_tenant_name' => (string) $sourceTenant->name,
'target_tenant_id' => (int) $targetTenant->getKey(),
'target_tenant_name' => (string) $targetTenant->name,
'source_environment_id' => (int) $sourceEnvironment->getKey(),
'source_environment_name' => (string) $sourceEnvironment->name,
'target_environment_id' => (int) $targetEnvironment->getKey(),
'target_environment_name' => (string) $targetEnvironment->name,
'ready_count' => (int) ($summary['ready'] ?? 0),
'blocked_count' => (int) ($summary['blocked'] ?? 0),
'manual_mapping_required_count' => (int) ($summary['manual_mapping_required'] ?? 0),
@ -170,20 +170,20 @@ public function logCrossTenantPromotionPreflightGenerated(
],
actor: $actor,
status: 'success',
resourceType: 'cross_tenant_promotion_preflight',
resourceId: sprintf('%s:%s', $sourceTenant->getKey(), $targetTenant->getKey()),
targetLabel: $sourceTenant->name.' -> '.$targetTenant->name,
summary: 'Cross-tenant promotion preflight generated for '.$sourceTenant->name.' -> '.$targetTenant->name,
resourceType: 'cross_environment_promotion_preflight',
resourceId: sprintf('%s:%s', $sourceEnvironment->getKey(), $targetEnvironment->getKey()),
targetLabel: $sourceEnvironment->name.' -> '.$targetEnvironment->name,
summary: 'Cross-environment promotion preflight generated for '.$sourceEnvironment->name.' -> '.$targetEnvironment->name,
);
}
/**
* @param array<string, mixed> $plan
*/
public function logCrossTenantPromotionExecutionQueued(
public function logCrossEnvironmentPromotionExecutionQueued(
Workspace $workspace,
ManagedEnvironment $sourceTenant,
ManagedEnvironment $targetTenant,
ManagedEnvironment $sourceEnvironment,
ManagedEnvironment $targetEnvironment,
OperationRun $operationRun,
array $plan,
User|PlatformUser|null $actor = null,
@ -192,12 +192,12 @@ public function logCrossTenantPromotionExecutionQueued(
return $this->log(
workspace: $workspace,
action: AuditActionId::CrossTenantPromotionExecutionQueued,
action: AuditActionId::CrossEnvironmentPromotionExecutionQueued,
context: [
'source_tenant_id' => (int) $sourceTenant->getKey(),
'source_tenant_name' => (string) $sourceTenant->name,
'target_tenant_id' => (int) $targetTenant->getKey(),
'target_tenant_name' => (string) $targetTenant->name,
'source_environment_id' => (int) $sourceEnvironment->getKey(),
'source_environment_name' => (string) $sourceEnvironment->name,
'target_environment_id' => (int) $targetEnvironment->getKey(),
'target_environment_name' => (string) $targetEnvironment->name,
'selection' => is_array($plan['selection'] ?? null) ? $plan['selection'] : [],
'ready_count' => (int) ($summary['ready'] ?? 0),
'excluded_count' => (int) ($summary['excluded'] ?? 0),
@ -208,36 +208,36 @@ public function logCrossTenantPromotionExecutionQueued(
status: 'queued',
resourceType: 'operation_run',
resourceId: (string) $operationRun->getKey(),
targetLabel: $sourceTenant->name.' -> '.$targetTenant->name,
summary: 'Cross-tenant promotion execution queued for '.$sourceTenant->name.' -> '.$targetTenant->name,
targetLabel: $sourceEnvironment->name.' -> '.$targetEnvironment->name,
summary: 'Cross-environment promotion execution queued for '.$sourceEnvironment->name.' -> '.$targetEnvironment->name,
operationRunId: (int) $operationRun->getKey(),
tenant: $targetTenant,
tenant: $targetEnvironment,
);
}
/**
* @param array<string, int> $summaryCounts
*/
public function logCrossTenantPromotionExecutionCompleted(
public function logCrossEnvironmentPromotionExecutionCompleted(
OperationRun $operationRun,
?int $sourceTenantId,
ManagedEnvironment $targetTenant,
?int $sourceEnvironmentId,
ManagedEnvironment $targetEnvironment,
array $summaryCounts,
?RestoreRun $restoreRun = null,
): \App\Models\AuditLog {
$context = is_array($operationRun->context) ? $operationRun->context : [];
$sourceTenantName = is_string($context['source_tenant_name'] ?? null)
? (string) $context['source_tenant_name']
$sourceEnvironmentName = is_string($context['source_environment_name'] ?? null)
? (string) $context['source_environment_name']
: null;
return $this->log(
workspace: $targetTenant->workspace,
action: AuditActionId::CrossTenantPromotionExecutionCompleted,
workspace: $targetEnvironment->workspace,
action: AuditActionId::CrossEnvironmentPromotionExecutionCompleted,
context: [
'source_tenant_id' => $sourceTenantId,
'source_tenant_name' => $sourceTenantName,
'target_tenant_id' => (int) $targetTenant->getKey(),
'target_tenant_name' => (string) $targetTenant->name,
'source_environment_id' => $sourceEnvironmentId,
'source_environment_name' => $sourceEnvironmentName,
'target_environment_id' => (int) $targetEnvironment->getKey(),
'target_environment_name' => (string) $targetEnvironment->name,
'summary_counts' => $summaryCounts,
'restore_run_id' => $restoreRun?->getKey(),
'operation_outcome' => (string) $operationRun->outcome,
@ -249,10 +249,10 @@ public function logCrossTenantPromotionExecutionCompleted(
},
resourceType: 'operation_run',
resourceId: (string) $operationRun->getKey(),
targetLabel: ($sourceTenantName !== null ? $sourceTenantName.' -> ' : '').$targetTenant->name,
summary: 'Cross-tenant promotion execution completed for '.(($sourceTenantName !== null ? $sourceTenantName.' -> ' : '')).$targetTenant->name,
targetLabel: ($sourceEnvironmentName !== null ? $sourceEnvironmentName.' -> ' : '').$targetEnvironment->name,
summary: 'Cross-environment promotion execution completed for '.(($sourceEnvironmentName !== null ? $sourceEnvironmentName.' -> ' : '')).$targetEnvironment->name,
operationRunId: (int) $operationRun->getKey(),
tenant: $targetTenant,
tenant: $targetEnvironment,
);
}

View File

@ -11,7 +11,7 @@
use App\Support\Audit\AuditActionId;
use Illuminate\Support\Facades\DB;
class TenantDiagnosticsService
class ManagedEnvironmentDiagnosticsService
{
public function __construct(public AuditLogger $auditLogger) {}

View File

@ -13,7 +13,7 @@
use DomainException;
use Illuminate\Support\Facades\DB;
class TenantMembershipManager
class ManagedEnvironmentMembershipManager
{
private const string SCOPE_PLACEHOLDER_ROLE = 'readonly';

View File

@ -55,9 +55,9 @@ class RoleCapabilityMap
Capabilities::REVIEW_PACK_VIEW,
Capabilities::REVIEW_PACK_MANAGE,
Capabilities::TENANT_REVIEW_VIEW,
Capabilities::TENANT_REVIEW_MANAGE,
Capabilities::TENANT_TRIAGE_REVIEW_MANAGE,
Capabilities::ENVIRONMENT_REVIEW_VIEW,
Capabilities::ENVIRONMENT_REVIEW_MANAGE,
Capabilities::MANAGED_ENVIRONMENT_TRIAGE_REVIEW_MANAGE,
Capabilities::EVIDENCE_VIEW,
Capabilities::EVIDENCE_MANAGE,
],
@ -99,9 +99,9 @@ class RoleCapabilityMap
Capabilities::REVIEW_PACK_VIEW,
Capabilities::REVIEW_PACK_MANAGE,
Capabilities::TENANT_REVIEW_VIEW,
Capabilities::TENANT_REVIEW_MANAGE,
Capabilities::TENANT_TRIAGE_REVIEW_MANAGE,
Capabilities::ENVIRONMENT_REVIEW_VIEW,
Capabilities::ENVIRONMENT_REVIEW_MANAGE,
Capabilities::MANAGED_ENVIRONMENT_TRIAGE_REVIEW_MANAGE,
Capabilities::EVIDENCE_VIEW,
Capabilities::EVIDENCE_MANAGE,
],
@ -131,8 +131,8 @@ class RoleCapabilityMap
Capabilities::PERMISSION_POSTURE_VIEW,
Capabilities::REVIEW_PACK_VIEW,
Capabilities::TENANT_REVIEW_VIEW,
Capabilities::TENANT_TRIAGE_REVIEW_MANAGE,
Capabilities::ENVIRONMENT_REVIEW_VIEW,
Capabilities::MANAGED_ENVIRONMENT_TRIAGE_REVIEW_MANAGE,
Capabilities::EVIDENCE_VIEW,
],
@ -153,7 +153,7 @@ class RoleCapabilityMap
Capabilities::PERMISSION_POSTURE_VIEW,
Capabilities::REVIEW_PACK_VIEW,
Capabilities::TENANT_REVIEW_VIEW,
Capabilities::ENVIRONMENT_REVIEW_VIEW,
Capabilities::EVIDENCE_VIEW,
],
];

View File

@ -23,17 +23,17 @@ class WorkspaceRoleCapabilityMap
Capabilities::WORKSPACE_ARCHIVE,
Capabilities::WORKSPACE_MEMBERSHIP_VIEW,
Capabilities::WORKSPACE_MEMBERSHIP_MANAGE,
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD,
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_IDENTIFY,
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_CANCEL,
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_CONNECTION_VIEW,
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_CONNECTION_MANAGE,
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_CONNECTION_MANAGE_DEDICATED,
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_VERIFICATION_START,
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_INVENTORY_SYNC,
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_POLICY_SYNC,
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_BACKUP_BOOTSTRAP,
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_ACTIVATE,
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD,
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_IDENTIFY,
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CANCEL,
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CONNECTION_VIEW,
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CONNECTION_MANAGE,
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CONNECTION_MANAGE_DEDICATED,
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_VERIFICATION_START,
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_BOOTSTRAP_INVENTORY_SYNC,
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_BOOTSTRAP_POLICY_SYNC,
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_BOOTSTRAP_BACKUP_BOOTSTRAP,
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_ACTIVATE,
Capabilities::WORKSPACE_SETTINGS_VIEW,
Capabilities::WORKSPACE_SETTINGS_MANAGE,
Capabilities::ALERTS_VIEW,
@ -48,15 +48,15 @@ class WorkspaceRoleCapabilityMap
Capabilities::WORKSPACE_VIEW,
Capabilities::WORKSPACE_MEMBERSHIP_VIEW,
Capabilities::WORKSPACE_MEMBERSHIP_MANAGE,
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD,
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_IDENTIFY,
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_CANCEL,
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_CONNECTION_VIEW,
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_CONNECTION_MANAGE,
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_VERIFICATION_START,
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_INVENTORY_SYNC,
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_POLICY_SYNC,
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_BACKUP_BOOTSTRAP,
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD,
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_IDENTIFY,
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CANCEL,
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CONNECTION_VIEW,
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CONNECTION_MANAGE,
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_VERIFICATION_START,
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_BOOTSTRAP_INVENTORY_SYNC,
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_BOOTSTRAP_POLICY_SYNC,
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_BOOTSTRAP_BACKUP_BOOTSTRAP,
Capabilities::WORKSPACE_SETTINGS_VIEW,
Capabilities::WORKSPACE_SETTINGS_MANAGE,
Capabilities::ALERTS_VIEW,
@ -70,11 +70,11 @@ class WorkspaceRoleCapabilityMap
WorkspaceRole::Operator->value => [
Capabilities::WORKSPACE_VIEW,
Capabilities::WORKSPACE_MEMBERSHIP_VIEW,
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_CONNECTION_VIEW,
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_VERIFICATION_START,
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_INVENTORY_SYNC,
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_POLICY_SYNC,
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_BACKUP_BOOTSTRAP,
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CONNECTION_VIEW,
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_VERIFICATION_START,
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_BOOTSTRAP_INVENTORY_SYNC,
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_BOOTSTRAP_POLICY_SYNC,
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_BOOTSTRAP_BACKUP_BOOTSTRAP,
Capabilities::WORKSPACE_SETTINGS_VIEW,
Capabilities::ALERTS_VIEW,
Capabilities::WORKSPACE_BASELINES_VIEW,

View File

@ -91,7 +91,7 @@ public function startCapture(
],
],
'baseline_profile_id' => (int) $profile->getKey(),
'source_tenant_id' => (int) $sourceTenant->getKey(),
'source_environment_id' => (int) $sourceTenant->getKey(),
'effective_scope' => $effectiveScope->toEffectiveScopeContext($this->capabilityGuard, 'capture'),
'capture_mode' => $captureMode->value,
'baseline_capture' => [

View File

@ -94,7 +94,7 @@ public function resume(OperationRun $priorRun, User $initiator): array
$newContext = [];
foreach (['target_scope', 'baseline_profile_id', 'baseline_snapshot_id', 'source_tenant_id', 'effective_scope', 'capture_mode'] as $key) {
foreach (['target_scope', 'baseline_profile_id', 'baseline_snapshot_id', 'source_environment_id', 'effective_scope', 'capture_mode'] as $key) {
if (array_key_exists($key, $context)) {
$newContext[$key] = $context[$key];
}

View File

@ -2,18 +2,18 @@
declare(strict_types=1);
namespace App\Services\TenantReviews;
namespace App\Services\EnvironmentReviews;
use App\Models\EvidenceSnapshot;
use App\Models\TenantReview;
use App\Support\TenantReviewStatus;
use App\Models\EnvironmentReview;
use App\Support\EnvironmentReviewStatus;
final class TenantReviewComposer
final class EnvironmentReviewComposer
{
public function __construct(
private readonly TenantReviewFingerprint $fingerprint,
private readonly TenantReviewSectionFactory $sectionFactory,
private readonly TenantReviewReadinessGate $readinessGate,
private readonly EnvironmentReviewFingerprint $fingerprint,
private readonly EnvironmentReviewSectionFactory $sectionFactory,
private readonly EnvironmentReviewReadinessGate $readinessGate,
) {}
/**
@ -25,7 +25,7 @@ public function __construct(
* sections: list<array<string, mixed>>
* }
*/
public function compose(EvidenceSnapshot $snapshot, ?TenantReview $review = null): array
public function compose(EvidenceSnapshot $snapshot, ?EnvironmentReview $review = null): array
{
$tenant = $snapshot->tenant;
@ -49,8 +49,8 @@ public function compose(EvidenceSnapshot $snapshot, ?TenantReview $review = null
$operationsSection = collect($sections)
->firstWhere('section_key', 'operations_health');
if ($review instanceof TenantReview && $review->isPublished()) {
$status = TenantReviewStatus::Published;
if ($review instanceof EnvironmentReview && $review->isPublished()) {
$status = EnvironmentReviewStatus::Published;
}
return [

View File

@ -2,13 +2,13 @@
declare(strict_types=1);
namespace App\Services\TenantReviews;
namespace App\Services\EnvironmentReviews;
use App\Models\EvidenceSnapshot;
use App\Models\ManagedEnvironment;
use Illuminate\Support\Arr;
final class TenantReviewFingerprint
final class EnvironmentReviewFingerprint
{
public function forSnapshot(ManagedEnvironment $tenant, EvidenceSnapshot $snapshot): string
{

View File

@ -2,31 +2,31 @@
declare(strict_types=1);
namespace App\Services\TenantReviews;
namespace App\Services\EnvironmentReviews;
use App\Models\EvidenceSnapshot;
use App\Models\ReviewPack;
use App\Models\ManagedEnvironment;
use App\Models\TenantReview;
use App\Models\EnvironmentReview;
use App\Models\User;
use App\Services\Audit\WorkspaceAuditLogger;
use App\Support\Audit\AuditActionId;
use App\Support\TenantReviewStatus;
use App\Support\EnvironmentReviewStatus;
use App\Support\Ui\DerivedState\DerivedStateFamily;
use App\Support\Ui\DerivedState\RequestScopedDerivedStateStore;
use Illuminate\Support\Facades\DB;
use InvalidArgumentException;
final class TenantReviewLifecycleService
final class EnvironmentReviewLifecycleService
{
public function __construct(
private readonly TenantReviewReadinessGate $readinessGate,
private readonly TenantReviewService $reviewService,
private readonly EnvironmentReviewReadinessGate $readinessGate,
private readonly EnvironmentReviewService $reviewService,
private readonly WorkspaceAuditLogger $auditLogger,
private readonly RequestScopedDerivedStateStore $derivedStateStore,
) {}
public function publish(TenantReview $review, User $user, string $reason): TenantReview
public function publish(EnvironmentReview $review, User $user, string $reason): EnvironmentReview
{
$review->loadMissing(['tenant', 'sections', 'currentExportReviewPack']);
$tenant = $review->tenant;
@ -44,7 +44,7 @@ public function publish(TenantReview $review, User $user, string $reason): Tenan
}
$review->forceFill([
'status' => TenantReviewStatus::Published->value,
'status' => EnvironmentReviewStatus::Published->value,
'published_at' => now(),
'published_by_user_id' => (int) $user->getKey(),
'summary' => array_merge(is_array($review->summary) ? $review->summary : [], [
@ -54,17 +54,17 @@ public function publish(TenantReview $review, User $user, string $reason): Tenan
$this->auditLogger->log(
workspace: $tenant->workspace,
action: AuditActionId::TenantReviewPublished,
action: AuditActionId::EnvironmentReviewPublished,
context: [
'metadata' => [
'review_id' => (int) $review->getKey(),
'before_status' => $beforeStatus,
'after_status' => TenantReviewStatus::Published->value,
'after_status' => EnvironmentReviewStatus::Published->value,
'reason' => $reason,
],
],
actor: $user,
resourceType: 'tenant_review',
resourceType: 'environment_review',
resourceId: (string) $review->getKey(),
targetLabel: sprintf('ManagedEnvironment review #%d', (int) $review->getKey()),
tenant: $tenant,
@ -75,7 +75,7 @@ public function publish(TenantReview $review, User $user, string $reason): Tenan
return $review->refresh()->load(['tenant', 'sections', 'currentExportReviewPack']);
}
public function archive(TenantReview $review, User $user, string $reason): TenantReview
public function archive(EnvironmentReview $review, User $user, string $reason): EnvironmentReview
{
$review->loadMissing('tenant');
$tenant = $review->tenant;
@ -92,23 +92,23 @@ public function archive(TenantReview $review, User $user, string $reason): Tenan
}
$review->forceFill([
'status' => TenantReviewStatus::Archived->value,
'status' => EnvironmentReviewStatus::Archived->value,
'archived_at' => now(),
])->save();
$this->auditLogger->log(
workspace: $tenant->workspace,
action: AuditActionId::TenantReviewArchived,
action: AuditActionId::EnvironmentReviewArchived,
context: [
'metadata' => [
'review_id' => (int) $review->getKey(),
'before_status' => $beforeStatus,
'after_status' => TenantReviewStatus::Archived->value,
'after_status' => EnvironmentReviewStatus::Archived->value,
'reason' => $reason,
],
],
actor: $user,
resourceType: 'tenant_review',
resourceType: 'environment_review',
resourceId: (string) $review->getKey(),
targetLabel: sprintf('ManagedEnvironment review #%d', (int) $review->getKey()),
tenant: $tenant,
@ -119,7 +119,7 @@ public function archive(TenantReview $review, User $user, string $reason): Tenan
return $review->refresh()->load(['tenant', 'sections', 'currentExportReviewPack']);
}
public function createNextReview(TenantReview $review, User $user, ?EvidenceSnapshot $snapshot = null): TenantReview
public function createNextReview(EnvironmentReview $review, User $user, ?EvidenceSnapshot $snapshot = null): EnvironmentReview
{
$review->loadMissing(['tenant', 'evidenceSnapshot']);
$tenant = $review->tenant;
@ -138,29 +138,29 @@ public function createNextReview(TenantReview $review, User $user, ?EvidenceSnap
throw new InvalidArgumentException('An eligible evidence snapshot is required to create the next review.');
}
$nextReview = DB::transaction(function () use ($review, $user, $snapshot, $tenant): TenantReview {
$nextReview = DB::transaction(function () use ($review, $user, $snapshot, $tenant): EnvironmentReview {
$nextReview = $this->reviewService->create($tenant, $snapshot, $user);
if ((int) $nextReview->getKey() !== (int) $review->getKey()) {
$review->forceFill([
'status' => TenantReviewStatus::Superseded->value,
'status' => EnvironmentReviewStatus::Superseded->value,
'superseded_by_review_id' => (int) $nextReview->getKey(),
])->save();
}
$this->auditLogger->log(
workspace: $tenant->workspace,
action: AuditActionId::TenantReviewSuccessorCreated,
action: AuditActionId::EnvironmentReviewSuccessorCreated,
context: [
'metadata' => [
'review_id' => (int) $review->getKey(),
'next_review_id' => (int) $nextReview->getKey(),
'before_status' => TenantReviewStatus::Published->value,
'after_status' => TenantReviewStatus::Superseded->value,
'before_status' => EnvironmentReviewStatus::Published->value,
'after_status' => EnvironmentReviewStatus::Superseded->value,
],
],
actor: $user,
resourceType: 'tenant_review',
resourceType: 'environment_review',
resourceId: (string) $nextReview->getKey(),
targetLabel: sprintf('ManagedEnvironment review #%d', (int) $nextReview->getKey()),
tenant: $tenant,
@ -194,9 +194,9 @@ private function validatedReason(mixed $reason, string $field): string
return $resolved;
}
private function invalidateArtifactTruthCache(TenantReview $review): void
private function invalidateArtifactTruthCache(EnvironmentReview $review): void
{
$this->derivedStateStore->invalidateModel(DerivedStateFamily::ArtifactTruth, $review, 'tenant_review');
$this->derivedStateStore->invalidateModel(DerivedStateFamily::ArtifactTruth, $review, 'environment_review');
$review->loadMissing('currentExportReviewPack');

View File

@ -2,14 +2,14 @@
declare(strict_types=1);
namespace App\Services\TenantReviews;
namespace App\Services\EnvironmentReviews;
use App\Models\TenantReview;
use App\Support\TenantReviewCompletenessState;
use App\Support\TenantReviewStatus;
use App\Models\EnvironmentReview;
use App\Support\EnvironmentReviewCompletenessState;
use App\Support\EnvironmentReviewStatus;
use Illuminate\Support\Collection;
final class TenantReviewReadinessGate
final class EnvironmentReviewReadinessGate
{
/**
* @param iterable<array<string, mixed>> $sections
@ -21,18 +21,18 @@ public function blockersForSections(iterable $sections): array
foreach ($sections as $section) {
$required = (bool) ($section['required'] ?? false);
$state = (string) ($section['completeness_state'] ?? TenantReviewCompletenessState::Missing->value);
$state = (string) ($section['completeness_state'] ?? EnvironmentReviewCompletenessState::Missing->value);
$title = (string) ($section['title'] ?? 'Review section');
if (! $required) {
continue;
}
if ($state === TenantReviewCompletenessState::Missing->value) {
if ($state === EnvironmentReviewCompletenessState::Missing->value) {
$blockers[] = sprintf('%s is missing.', $title);
}
if ($state === TenantReviewCompletenessState::Stale->value) {
if ($state === EnvironmentReviewCompletenessState::Stale->value) {
$blockers[] = sprintf('%s is stale and must be refreshed before publication.', $title);
}
}
@ -43,45 +43,45 @@ public function blockersForSections(iterable $sections): array
/**
* @param iterable<array<string, mixed>> $sections
*/
public function completenessForSections(iterable $sections): TenantReviewCompletenessState
public function completenessForSections(iterable $sections): EnvironmentReviewCompletenessState
{
$states = collect($sections)
->map(static fn (array $section): string => (string) ($section['completeness_state'] ?? TenantReviewCompletenessState::Missing->value))
->map(static fn (array $section): string => (string) ($section['completeness_state'] ?? EnvironmentReviewCompletenessState::Missing->value))
->values();
if ($states->isEmpty()) {
return TenantReviewCompletenessState::Missing;
return EnvironmentReviewCompletenessState::Missing;
}
if ($states->contains(TenantReviewCompletenessState::Missing->value)) {
return TenantReviewCompletenessState::Missing;
if ($states->contains(EnvironmentReviewCompletenessState::Missing->value)) {
return EnvironmentReviewCompletenessState::Missing;
}
if ($states->contains(TenantReviewCompletenessState::Stale->value)) {
return TenantReviewCompletenessState::Stale;
if ($states->contains(EnvironmentReviewCompletenessState::Stale->value)) {
return EnvironmentReviewCompletenessState::Stale;
}
if ($states->contains(TenantReviewCompletenessState::Partial->value)) {
return TenantReviewCompletenessState::Partial;
if ($states->contains(EnvironmentReviewCompletenessState::Partial->value)) {
return EnvironmentReviewCompletenessState::Partial;
}
return TenantReviewCompletenessState::Complete;
return EnvironmentReviewCompletenessState::Complete;
}
/**
* @param iterable<array<string, mixed>> $sections
*/
public function statusForSections(iterable $sections): TenantReviewStatus
public function statusForSections(iterable $sections): EnvironmentReviewStatus
{
return $this->blockersForSections($sections) === []
? TenantReviewStatus::Ready
: TenantReviewStatus::Draft;
? EnvironmentReviewStatus::Ready
: EnvironmentReviewStatus::Draft;
}
/**
* @return list<string>
*/
public function blockersForReview(TenantReview $review): array
public function blockersForReview(EnvironmentReview $review): array
{
$sections = $review->relationLoaded('sections')
? $review->sections
@ -96,7 +96,7 @@ public function blockersForReview(TenantReview $review): array
})->all());
}
public function canPublish(TenantReview $review): bool
public function canPublish(EnvironmentReview $review): bool
{
if (! $review->isMutable()) {
return false;
@ -105,11 +105,11 @@ public function canPublish(TenantReview $review): bool
return $this->blockersForReview($review) === [];
}
public function canExport(TenantReview $review): bool
public function canExport(EnvironmentReview $review): bool
{
if (! in_array($review->statusEnum(), [
TenantReviewStatus::Ready,
TenantReviewStatus::Published,
EnvironmentReviewStatus::Ready,
EnvironmentReviewStatus::Published,
], true)) {
return false;
}
@ -124,14 +124,14 @@ public function canExport(TenantReview $review): bool
public function sectionStateCounts(iterable $sections): array
{
$counts = collect($sections)
->groupBy(static fn (array $section): string => (string) ($section['completeness_state'] ?? TenantReviewCompletenessState::Missing->value))
->groupBy(static fn (array $section): string => (string) ($section['completeness_state'] ?? EnvironmentReviewCompletenessState::Missing->value))
->map(static fn (Collection $group): int => $group->count());
return [
'complete' => (int) ($counts[TenantReviewCompletenessState::Complete->value] ?? 0),
'partial' => (int) ($counts[TenantReviewCompletenessState::Partial->value] ?? 0),
'missing' => (int) ($counts[TenantReviewCompletenessState::Missing->value] ?? 0),
'stale' => (int) ($counts[TenantReviewCompletenessState::Stale->value] ?? 0),
'complete' => (int) ($counts[EnvironmentReviewCompletenessState::Complete->value] ?? 0),
'partial' => (int) ($counts[EnvironmentReviewCompletenessState::Partial->value] ?? 0),
'missing' => (int) ($counts[EnvironmentReviewCompletenessState::Missing->value] ?? 0),
'stale' => (int) ($counts[EnvironmentReviewCompletenessState::Stale->value] ?? 0),
];
}
}

View File

@ -2,10 +2,10 @@
declare(strict_types=1);
namespace App\Services\TenantReviews;
namespace App\Services\EnvironmentReviews;
use App\Models\ManagedEnvironment;
use App\Models\TenantReview;
use App\Models\EnvironmentReview;
use App\Models\User;
use App\Models\Workspace;
use App\Models\WorkspaceMembership;
@ -15,7 +15,7 @@
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
final class TenantReviewRegisterService
final class EnvironmentReviewRegisterService
{
public function __construct(
private readonly CapabilityResolver $capabilityResolver,
@ -44,7 +44,7 @@ public function authorizedTenants(User $user, Workspace $workspace): array
$this->capabilityResolver->primeMemberships($user, $tenants->modelKeys());
return $tenants
->filter(fn (ManagedEnvironment $tenant): bool => $this->capabilityResolver->can($user, $tenant, Capabilities::TENANT_REVIEW_VIEW))
->filter(fn (ManagedEnvironment $tenant): bool => $this->capabilityResolver->can($user, $tenant, Capabilities::ENVIRONMENT_REVIEW_VIEW))
->keyBy(static fn (ManagedEnvironment $tenant): int => (int) $tenant->getKey())
->all();
}
@ -53,7 +53,7 @@ public function query(User $user, Workspace $workspace): Builder
{
$tenantIds = array_keys($this->authorizedTenants($user, $workspace));
return TenantReview::query()
return EnvironmentReview::query()
->with(['tenant', 'evidenceSnapshot', 'currentExportReviewPack'])
->forWorkspace((int) $workspace->getKey())
->whereIn('managed_environment_id', $tenantIds === [] ? [-1] : $tenantIds)
@ -65,12 +65,12 @@ public function latestPublishedQuery(User $user, Workspace $workspace): Builder
{
$tenantIds = array_keys($this->authorizedTenants($user, $workspace));
$rankedReviews = TenantReview::query()
$rankedReviews = EnvironmentReview::query()
->select([
'tenant_reviews.id',
'tenant_reviews.managed_environment_id',
'tenant_reviews.published_at',
'tenant_reviews.generated_at',
'environment_reviews.id',
'environment_reviews.managed_environment_id',
'environment_reviews.published_at',
'environment_reviews.generated_at',
])
->selectRaw('ROW_NUMBER() OVER (PARTITION BY managed_environment_id ORDER BY published_at DESC, generated_at DESC, id DESC) as rn')
->forWorkspace((int) $workspace->getKey())
@ -78,14 +78,14 @@ public function latestPublishedQuery(User $user, Workspace $workspace): Builder
->published();
$latestPublishedIds = DB::query()
->fromSub($rankedReviews, 'ranked_tenant_reviews')
->fromSub($rankedReviews, 'ranked_environment_reviews')
->where('rn', 1)
->select('id');
return TenantReview::query()
return EnvironmentReview::query()
->with(['tenant', 'evidenceSnapshot', 'currentExportReviewPack'])
->forWorkspace((int) $workspace->getKey())
->whereIn('tenant_reviews.id', $latestPublishedIds)
->whereIn('environment_reviews.id', $latestPublishedIds)
->orderByDesc('published_at')
->orderByDesc('generated_at')
->orderByDesc('id');
@ -98,9 +98,9 @@ public function customerWorkspaceTenantQuery(User $user, Workspace $workspace):
return ManagedEnvironment::query()
->where('workspace_id', (int) $workspace->getKey())
->whereIn('id', $tenantIds === [] ? [-1] : $tenantIds)
->whereHas('tenantReviews', fn ($query) => $query->published())
->whereHas('environmentReviews', fn ($query) => $query->published())
->with([
'tenantReviews' => fn ($query) => $query
'environmentReviews' => fn ($query) => $query
->with(['tenant', 'evidenceSnapshot', 'currentExportReviewPack'])
->published()
->orderByDesc('published_at')

View File

@ -2,17 +2,17 @@
declare(strict_types=1);
namespace App\Services\TenantReviews;
namespace App\Services\EnvironmentReviews;
use App\Models\EvidenceSnapshot;
use App\Models\EvidenceSnapshotItem;
use App\Support\Findings\FindingOutcomeSemantics;
use App\Support\Governance\Controls\ComplianceEvidenceMappingV1;
use App\Support\TenantReviewCompletenessState;
use App\Support\EnvironmentReviewCompletenessState;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
final class TenantReviewSectionFactory
final class EnvironmentReviewSectionFactory
{
public function __construct(
private readonly FindingOutcomeSemantics $findingOutcomeSemantics,
@ -333,10 +333,10 @@ private function summary(?EvidenceSnapshotItem $item): array
return is_array($item?->summary_payload) ? $item->summary_payload : [];
}
private function state(?EvidenceSnapshotItem $item): TenantReviewCompletenessState
private function state(?EvidenceSnapshotItem $item): EnvironmentReviewCompletenessState
{
return TenantReviewCompletenessState::tryFrom((string) $item?->state)
?? TenantReviewCompletenessState::Missing;
return EnvironmentReviewCompletenessState::tryFrom((string) $item?->state)
?? EnvironmentReviewCompletenessState::Missing;
}
private function sourceFingerprint(?EvidenceSnapshotItem $item): ?string
@ -361,23 +361,23 @@ private function canonicalControlsFromEntries(array $entries): array
}
/**
* @param array<int, TenantReviewCompletenessState> $states
* @param array<int, EnvironmentReviewCompletenessState> $states
*/
private function maxState(array $states): TenantReviewCompletenessState
private function maxState(array $states): EnvironmentReviewCompletenessState
{
if (in_array(TenantReviewCompletenessState::Missing, $states, true)) {
return TenantReviewCompletenessState::Missing;
if (in_array(EnvironmentReviewCompletenessState::Missing, $states, true)) {
return EnvironmentReviewCompletenessState::Missing;
}
if (in_array(TenantReviewCompletenessState::Stale, $states, true)) {
return TenantReviewCompletenessState::Stale;
if (in_array(EnvironmentReviewCompletenessState::Stale, $states, true)) {
return EnvironmentReviewCompletenessState::Stale;
}
if (in_array(TenantReviewCompletenessState::Partial, $states, true)) {
return TenantReviewCompletenessState::Partial;
if (in_array(EnvironmentReviewCompletenessState::Partial, $states, true)) {
return EnvironmentReviewCompletenessState::Partial;
}
return TenantReviewCompletenessState::Complete;
return EnvironmentReviewCompletenessState::Complete;
}
/**

View File

@ -2,43 +2,43 @@
declare(strict_types=1);
namespace App\Services\TenantReviews;
namespace App\Services\EnvironmentReviews;
use App\Jobs\ComposeTenantReviewJob;
use App\Jobs\ComposeEnvironmentReviewJob;
use App\Models\EvidenceSnapshot;
use App\Models\OperationRun;
use App\Models\ManagedEnvironment;
use App\Models\TenantReview;
use App\Models\EnvironmentReview;
use App\Models\User;
use App\Services\Audit\WorkspaceAuditLogger;
use App\Services\OperationRunService;
use App\Support\Audit\AuditActionId;
use App\Support\OperationRunType;
use App\Support\TenantReviewStatus;
use App\Support\EnvironmentReviewStatus;
use Illuminate\Support\Facades\DB;
use InvalidArgumentException;
final class TenantReviewService
final class EnvironmentReviewService
{
public function __construct(
private readonly OperationRunService $operationRuns,
private readonly WorkspaceAuditLogger $auditLogger,
private readonly TenantReviewComposer $composer,
private readonly TenantReviewFingerprint $fingerprint,
private readonly EnvironmentReviewComposer $composer,
private readonly EnvironmentReviewFingerprint $fingerprint,
) {}
public function create(ManagedEnvironment $tenant, EvidenceSnapshot $snapshot, User $user): TenantReview
public function create(ManagedEnvironment $tenant, EvidenceSnapshot $snapshot, User $user): EnvironmentReview
{
return $this->queueComposition(
tenant: $tenant,
snapshot: $snapshot,
user: $user,
existingReview: null,
auditAction: AuditActionId::TenantReviewCreated,
auditAction: AuditActionId::EnvironmentReviewCreated,
);
}
public function refresh(TenantReview $review, User $user, ?EvidenceSnapshot $snapshot = null): TenantReview
public function refresh(EnvironmentReview $review, User $user, ?EvidenceSnapshot $snapshot = null): EnvironmentReview
{
$tenant = $review->tenant;
@ -53,11 +53,11 @@ public function refresh(TenantReview $review, User $user, ?EvidenceSnapshot $sna
snapshot: $snapshot,
user: $user,
existingReview: $review,
auditAction: AuditActionId::TenantReviewRefreshed,
auditAction: AuditActionId::EnvironmentReviewRefreshed,
);
}
public function compose(TenantReview $review): TenantReview
public function compose(EnvironmentReview $review): EnvironmentReview
{
$review->loadMissing(['tenant', 'evidenceSnapshot.items']);
@ -121,7 +121,7 @@ public function activeCompositionRun(ManagedEnvironment $tenant, ?EvidenceSnapsh
return $this->operationRuns->findCanonicalRunWithIdentity(
tenant: $tenant,
type: OperationRunType::TenantReviewCompose->value,
type: OperationRunType::EnvironmentReviewCompose->value,
identityInputs: [
'managed_environment_id' => (int) $tenant->getKey(),
'snapshot_id' => (int) $snapshot->getKey(),
@ -134,9 +134,9 @@ private function queueComposition(
ManagedEnvironment $tenant,
?EvidenceSnapshot $snapshot,
User $user,
?TenantReview $existingReview,
?EnvironmentReview $existingReview,
AuditActionId $auditAction,
): TenantReview {
): EnvironmentReview {
if (! $snapshot instanceof EvidenceSnapshot) {
throw new InvalidArgumentException('An eligible evidence snapshot is required.');
}
@ -148,17 +148,17 @@ private function queueComposition(
$fingerprint = $this->fingerprint->forSnapshot($tenant, $snapshot);
$review = $existingReview;
if (! $review instanceof TenantReview) {
if (! $review instanceof EnvironmentReview) {
$existing = $this->findExistingMutableReview($tenant, $fingerprint);
if ($existing instanceof TenantReview) {
if ($existing instanceof EnvironmentReview) {
return $existing->load(['tenant', 'evidenceSnapshot', 'sections', 'operationRun', 'initiator', 'publisher']);
}
}
$operationRun = $this->operationRuns->ensureRunWithIdentity(
tenant: $tenant,
type: OperationRunType::TenantReviewCompose->value,
type: OperationRunType::EnvironmentReviewCompose->value,
identityInputs: [
'managed_environment_id' => (int) $tenant->getKey(),
'snapshot_id' => (int) $snapshot->getKey(),
@ -177,14 +177,14 @@ private function queueComposition(
initiator: $user,
);
$review ??= TenantReview::query()->create([
$review ??= EnvironmentReview::query()->create([
'managed_environment_id' => (int) $tenant->getKey(),
'workspace_id' => (int) $tenant->workspace_id,
'evidence_snapshot_id' => (int) $snapshot->getKey(),
'operation_run_id' => (int) $operationRun->getKey(),
'initiated_by_user_id' => (int) $user->getKey(),
'fingerprint' => $fingerprint,
'status' => TenantReviewStatus::Draft->value,
'status' => EnvironmentReviewStatus::Draft->value,
'completeness_state' => (string) $snapshot->completeness_state,
'summary' => [
'evidence_basis' => [
@ -199,12 +199,12 @@ private function queueComposition(
],
]);
if ($existingReview instanceof TenantReview) {
if ($existingReview instanceof EnvironmentReview) {
$existingReview->forceFill([
'evidence_snapshot_id' => (int) $snapshot->getKey(),
'operation_run_id' => (int) $operationRun->getKey(),
'fingerprint' => $fingerprint,
'status' => TenantReviewStatus::Draft->value,
'status' => EnvironmentReviewStatus::Draft->value,
])->save();
$review = $existingReview->refresh();
@ -212,8 +212,8 @@ private function queueComposition(
if ($operationRun->wasRecentlyCreated) {
$this->operationRuns->dispatchOrFail($operationRun, function () use ($review, $operationRun): void {
ComposeTenantReviewJob::dispatch(
tenantReviewId: (int) $review->getKey(),
ComposeEnvironmentReviewJob::dispatch(
environmentReviewId: (int) $review->getKey(),
operationRunId: (int) $operationRun->getKey(),
);
});
@ -231,7 +231,7 @@ private function queueComposition(
],
],
actor: $user,
resourceType: 'tenant_review',
resourceType: 'environment_review',
resourceId: (string) $review->getKey(),
targetLabel: sprintf('ManagedEnvironment review #%d', (int) $review->getKey()),
operationRunId: (int) $operationRun->getKey(),
@ -241,9 +241,9 @@ private function queueComposition(
return $review->load(['tenant', 'evidenceSnapshot', 'sections', 'operationRun', 'initiator', 'publisher']);
}
private function findExistingMutableReview(ManagedEnvironment $tenant, string $fingerprint): ?TenantReview
private function findExistingMutableReview(ManagedEnvironment $tenant, string $fingerprint): ?EnvironmentReview
{
return TenantReview::query()
return EnvironmentReview::query()
->forTenant((int) $tenant->getKey())
->mutable()
->where('fingerprint', $fingerprint)

View File

@ -3,13 +3,13 @@
namespace App\Services\Intune;
use App\Models\ManagedEnvironment;
use App\Models\TenantPermission;
use App\Models\ManagedEnvironmentPermission;
use App\Services\Graph\GraphClientInterface;
use App\Services\Providers\MicrosoftGraphOptionsResolver;
use DateTimeInterface;
use Illuminate\Support\Carbon;
class TenantPermissionService
class ManagedEnvironmentPermissionService
{
public function __construct(
private readonly GraphClientInterface $graphClient,
@ -32,11 +32,11 @@ public function getRequiredPermissions(): array
*/
public function getGrantedPermissions(ManagedEnvironment $tenant): array
{
return TenantPermission::query()
return ManagedEnvironmentPermission::query()
->where('managed_environment_id', $tenant->id)
->get()
->keyBy('permission_key')
->map(fn (TenantPermission $permission) => [
->map(fn (ManagedEnvironmentPermission $permission) => [
'status' => $permission->status,
'details' => $permission->details,
'last_checked_at' => $permission->last_checked_at,
@ -46,7 +46,7 @@ public function getGrantedPermissions(ManagedEnvironment $tenant): array
/**
* @param array<string, array{status:string,details?:array<string,mixed>|null}|string>|null $grantedStatuses
* @param bool $persist Persist comparison results to tenant_permissions
* @param bool $persist Persist comparison results to managed_environment_permissions
* @param bool $liveCheck If true, fetch actual permissions from Graph API
* @param bool $useConfiguredStub Include configured stub permissions when no live check is used
* @param array{tenant?:string|null,client_id?:string|null,client_secret?:string|null,client_request_id?:string|null}|null $graphOptions
@ -165,7 +165,7 @@ public function compare(
if (! $shouldPersistErrorSnapshot) {
$canPersist = false;
} else {
$hasStoredStatuses = TenantPermission::query()
$hasStoredStatuses = ManagedEnvironmentPermission::query()
->where('managed_environment_id', $tenant->id)
->exists();
@ -189,7 +189,7 @@ public function compare(
: ($granted[$key]['details'] ?? null);
if ($canPersist) {
TenantPermission::updateOrCreate(
ManagedEnvironmentPermission::updateOrCreate(
[
'managed_environment_id' => $tenant->id,
'permission_key' => $key,
@ -433,7 +433,7 @@ private function fetchLivePermissions(ManagedEnvironment $tenant, ?array $graphO
private function lastRefreshedAtIso(ManagedEnvironment $tenant): ?string
{
$lastCheckedAt = TenantPermission::query()
$lastCheckedAt = ManagedEnvironmentPermission::query()
->where('managed_environment_id', (int) $tenant->getKey())
->max('last_checked_at');

View File

@ -6,15 +6,15 @@
use App\Support\Providers\Capabilities\ProviderCapabilityDefinition;
use App\Support\Providers\Capabilities\ProviderCapabilityRegistry;
use App\Support\Providers\Capabilities\ProviderCapabilityStatus;
use App\Support\Verification\TenantPermissionCheckClusters;
use App\Support\Verification\ManagedEnvironmentPermissionCheckClusters;
use App\Support\Verification\VerificationReportOverall;
use Carbon\CarbonInterface;
use Illuminate\Support\Carbon;
class TenantRequiredPermissionsViewModelBuilder
class ManagedEnvironmentRequiredPermissionsViewModelBuilder
{
/**
* @phpstan-type TenantPermissionRow array{key:string,type:'application'|'delegated',description:?string,features:array<int,string>,status:'granted'|'missing'|'error',details:array<string,mixed>|null}
* @phpstan-type ManagedEnvironmentPermissionRow array{key:string,type:'application'|'delegated',description:?string,features:array<int,string>,status:'granted'|'missing'|'error',details:array<string,mixed>|null}
* @phpstan-type FeatureImpact array{feature:string,missing:int,required_application:int,required_delegated:int,blocked:bool}
* @phpstan-type CapabilityGroup array{provider_capability_key:string,label:string,status:string,provider_requirement_keys:array<int,string>,missing_requirement_keys:array<int,string>,evidence_counts:array{requirements:int,missing:int,errors:int},message:string}
* @phpstan-type FilterState array{status:'missing'|'present'|'all',type:'application'|'delegated'|'all',features:array<int,string>,search:string}
@ -28,12 +28,12 @@ class TenantRequiredPermissionsViewModelBuilder
* primary_capability_group: CapabilityGroup|null,
* freshness: array{last_refreshed_at:?string,is_stale:bool}
* },
* permissions: array<int, TenantPermissionRow>,
* permissions: array<int, ManagedEnvironmentPermissionRow>,
* filters: FilterState,
* copy: array{application:string,delegated:string}
* }
*/
public function __construct(private readonly TenantPermissionService $permissionService) {}
public function __construct(private readonly ManagedEnvironmentPermissionService $permissionService) {}
/**
* @param array<string, mixed> $filters
@ -48,7 +48,7 @@ public function build(ManagedEnvironment $tenant, array $filters = []): array
useConfiguredStub: false,
);
/** @var array<int, TenantPermissionRow> $allPermissions */
/** @var array<int, ManagedEnvironmentPermissionRow> $allPermissions */
$allPermissions = collect($comparison['permissions'] ?? [])
->filter(fn (mixed $row): bool => is_array($row))
->map(fn (array $row): array => self::normalizePermissionRow($row))
@ -87,7 +87,7 @@ public function build(ManagedEnvironment $tenant, array $filters = []): array
}
/**
* @param array<int, TenantPermissionRow> $permissions
* @param array<int, ManagedEnvironmentPermissionRow> $permissions
* @param array{last_refreshed_at:?string,is_stale:bool} $freshness
* @return array<int, CapabilityGroup>
*/
@ -127,7 +127,7 @@ public static function primaryCapabilityGroup(array $groups): ?array
}
/**
* @param array<int, TenantPermissionRow> $permissions
* @param array<int, ManagedEnvironmentPermissionRow> $permissions
* @return CapabilityGroup
*/
private static function deriveCapabilityGroup(
@ -138,7 +138,7 @@ private static function deriveCapabilityGroup(
$rowsByRequirement = [];
foreach ($definition->providerRequirementKeys as $requirementKey) {
$rowsByRequirement[$requirementKey] = TenantPermissionCheckClusters::rowsForRequirementKey($permissions, $requirementKey);
$rowsByRequirement[$requirementKey] = ManagedEnvironmentPermissionCheckClusters::rowsForRequirementKey($permissions, $requirementKey);
}
$rows = array_values(array_merge(...array_values($rowsByRequirement ?: [[]])));
@ -193,7 +193,7 @@ private static function deriveCapabilityGroup(
}
/**
* @param array<int, TenantPermissionRow> $permissions
* @param array<int, ManagedEnvironmentPermissionRow> $permissions
*/
public static function deriveOverallStatus(array $permissions, bool $hasStaleFreshness = false): string
{
@ -243,7 +243,7 @@ public static function deriveFreshness(?CarbonInterface $lastRefreshedAt, ?Carbo
}
/**
* @param array<int, TenantPermissionRow> $permissions
* @param array<int, ManagedEnvironmentPermissionRow> $permissions
* @return array{missing_application:int,missing_delegated:int,present:int,error:int}
*/
public static function deriveCounts(array $permissions): array
@ -281,7 +281,7 @@ public static function deriveCounts(array $permissions): array
}
/**
* @param array<int, TenantPermissionRow> $permissions
* @param array<int, ManagedEnvironmentPermissionRow> $permissions
* @return array<int, FeatureImpact>
*/
public static function deriveFeatureImpacts(array $permissions): array
@ -345,7 +345,7 @@ public static function deriveFeatureImpacts(array $permissions): array
* - Respects Feature filter only
* - Ignores Search
*
* @param array<int, TenantPermissionRow> $permissions
* @param array<int, ManagedEnvironmentPermissionRow> $permissions
* @param 'application'|'delegated' $type
* @param array<int, string> $featureFilter
*/
@ -383,8 +383,8 @@ public static function deriveCopyPayload(array $permissions, string $type, array
}
/**
* @param array<int, TenantPermissionRow> $permissions
* @return array<int, TenantPermissionRow>
* @param array<int, ManagedEnvironmentPermissionRow> $permissions
* @return array<int, ManagedEnvironmentPermissionRow>
*/
public static function applyFilterState(array $permissions, array $state): array
{
@ -489,7 +489,7 @@ public static function normalizeFilterState(array $filters): array
/**
* @param array<string, mixed> $row
* @return TenantPermissionRow
* @return ManagedEnvironmentPermissionRow
*/
private static function normalizePermissionRow(array $row): array
{

View File

@ -6,7 +6,7 @@
use App\Exceptions\Onboarding\OnboardingDraftConflictException;
use App\Exceptions\Onboarding\OnboardingDraftImmutableException;
use App\Models\TenantOnboardingSession;
use App\Models\ManagedEnvironmentOnboardingSession;
use App\Models\User;
use App\Models\Workspace;
use Illuminate\Support\Facades\DB;
@ -19,25 +19,25 @@ public function __construct(
) {}
/**
* @param callable(TenantOnboardingSession):void $mutator
* @param callable(ManagedEnvironmentOnboardingSession):void $mutator
*/
public function createOrResume(
Workspace $workspace,
User $actor,
string $entraTenantId,
callable $mutator,
?TenantOnboardingSession $preferredDraft = null,
?ManagedEnvironmentOnboardingSession $preferredDraft = null,
?int $expectedVersion = null,
bool $incrementVersion = true,
?bool &$wasCreated = null,
): TenantOnboardingSession {
return DB::transaction(function () use ($workspace, $actor, $entraTenantId, $mutator, $preferredDraft, $expectedVersion, $incrementVersion, &$wasCreated): TenantOnboardingSession {
): ManagedEnvironmentOnboardingSession {
return DB::transaction(function () use ($workspace, $actor, $entraTenantId, $mutator, $preferredDraft, $expectedVersion, $incrementVersion, &$wasCreated): ManagedEnvironmentOnboardingSession {
$draft = $this->resolveDraftForIdentity($workspace, $entraTenantId, $preferredDraft);
$isNew = ! $draft instanceof TenantOnboardingSession;
$isNew = ! $draft instanceof ManagedEnvironmentOnboardingSession;
$wasCreated = $isNew;
if ($isNew) {
$draft = new TenantOnboardingSession;
$draft = new ManagedEnvironmentOnboardingSession;
$draft->workspace_id = (int) $workspace->getKey();
$draft->entra_tenant_id = $entraTenantId;
$draft->started_by_user_id = (int) $actor->getKey();
@ -61,18 +61,18 @@ public function createOrResume(
}
/**
* @param callable(TenantOnboardingSession):void $mutator
* @param callable(ManagedEnvironmentOnboardingSession):void $mutator
*/
public function mutate(
TenantOnboardingSession $draft,
ManagedEnvironmentOnboardingSession $draft,
User $actor,
callable $mutator,
?int $expectedVersion = null,
bool $incrementVersion = true,
bool $allowTerminal = false,
): TenantOnboardingSession {
return DB::transaction(function () use ($draft, $actor, $mutator, $expectedVersion, $incrementVersion, $allowTerminal): TenantOnboardingSession {
$lockedDraft = TenantOnboardingSession::query()
): ManagedEnvironmentOnboardingSession {
return DB::transaction(function () use ($draft, $actor, $mutator, $expectedVersion, $incrementVersion, $allowTerminal): ManagedEnvironmentOnboardingSession {
$lockedDraft = ManagedEnvironmentOnboardingSession::query()
->whereKey($draft->getKey())
->lockForUpdate()
->firstOrFail();
@ -101,19 +101,19 @@ public function mutate(
});
}
public function lockForTrustedMutation(TenantOnboardingSession|int|string $draft, Workspace $workspace): TenantOnboardingSession
public function lockForTrustedMutation(ManagedEnvironmentOnboardingSession|int|string $draft, Workspace $workspace): ManagedEnvironmentOnboardingSession
{
$draftId = $draft instanceof TenantOnboardingSession
$draftId = $draft instanceof ManagedEnvironmentOnboardingSession
? (int) $draft->getKey()
: (int) $draft;
$lockedDraft = TenantOnboardingSession::query()
$lockedDraft = ManagedEnvironmentOnboardingSession::query()
->whereKey($draftId)
->where('workspace_id', (int) $workspace->getKey())
->lockForUpdate()
->first();
if (! $lockedDraft instanceof TenantOnboardingSession) {
if (! $lockedDraft instanceof ManagedEnvironmentOnboardingSession) {
throw new NotFoundHttpException;
}
@ -123,16 +123,16 @@ public function lockForTrustedMutation(TenantOnboardingSession|int|string $draft
private function resolveDraftForIdentity(
Workspace $workspace,
string $entraTenantId,
?TenantOnboardingSession $preferredDraft = null,
): ?TenantOnboardingSession {
if ($preferredDraft instanceof TenantOnboardingSession) {
$lockedPreferredDraft = TenantOnboardingSession::query()
?ManagedEnvironmentOnboardingSession $preferredDraft = null,
): ?ManagedEnvironmentOnboardingSession {
if ($preferredDraft instanceof ManagedEnvironmentOnboardingSession) {
$lockedPreferredDraft = ManagedEnvironmentOnboardingSession::query()
->whereKey($preferredDraft->getKey())
->where('workspace_id', (int) $workspace->getKey())
->lockForUpdate()
->first();
if ($lockedPreferredDraft instanceof TenantOnboardingSession && $lockedPreferredDraft->entra_tenant_id === $entraTenantId) {
if ($lockedPreferredDraft instanceof ManagedEnvironmentOnboardingSession && $lockedPreferredDraft->entra_tenant_id === $entraTenantId) {
if ($lockedPreferredDraft->lifecycleState()->isTerminal()) {
throw new OnboardingDraftImmutableException(
draftId: (int) $lockedPreferredDraft->getKey(),
@ -144,7 +144,7 @@ private function resolveDraftForIdentity(
}
}
return TenantOnboardingSession::query()
return ManagedEnvironmentOnboardingSession::query()
->where('workspace_id', (int) $workspace->getKey())
->where('entra_tenant_id', $entraTenantId)
->resumable()
@ -153,7 +153,7 @@ private function resolveDraftForIdentity(
->first();
}
private function assertExpectedVersion(TenantOnboardingSession $draft, int $expectedVersion): void
private function assertExpectedVersion(ManagedEnvironmentOnboardingSession $draft, int $expectedVersion): void
{
$actualVersion = max(1, (int) ($draft->version ?? 1));
@ -168,7 +168,7 @@ private function assertExpectedVersion(TenantOnboardingSession $draft, int $expe
);
}
private function persistDraft(TenantOnboardingSession $draft, bool $incrementVersion): void
private function persistDraft(ManagedEnvironmentOnboardingSession $draft, bool $incrementVersion): void
{
$currentVersion = max(0, (int) ($draft->version ?? 0));

View File

@ -4,7 +4,7 @@
namespace App\Services\Onboarding;
use App\Models\TenantOnboardingSession;
use App\Models\ManagedEnvironmentOnboardingSession;
use App\Models\User;
use App\Models\Workspace;
use App\Services\Audit\WorkspaceAuditLogger;
@ -25,18 +25,18 @@ public function __construct(
* @throws AuthorizationException
* @throws NotFoundHttpException
*/
public function resolve(TenantOnboardingSession|int|string $draft, User $user, Workspace $workspace): TenantOnboardingSession
public function resolve(ManagedEnvironmentOnboardingSession|int|string $draft, User $user, Workspace $workspace): ManagedEnvironmentOnboardingSession
{
$draftId = $draft instanceof TenantOnboardingSession
$draftId = $draft instanceof ManagedEnvironmentOnboardingSession
? (int) $draft->getKey()
: (int) $draft;
$resolvedDraft = TenantOnboardingSession::query()
->with(['tenant', 'startedByUser', 'updatedByUser'])
$resolvedDraft = ManagedEnvironmentOnboardingSession::query()
->with(['managedEnvironment', 'startedByUser', 'updatedByUser'])
->whereKey($draftId)
->first();
if (! $resolvedDraft instanceof TenantOnboardingSession) {
if (! $resolvedDraft instanceof ManagedEnvironmentOnboardingSession) {
throw new NotFoundHttpException;
}
@ -48,7 +48,7 @@ public function resolve(TenantOnboardingSession|int|string $draft, User $user, W
$resolvedDraft = $this->lifecycleService
->syncPersistedLifecycle($resolvedDraft)
->loadMissing(['tenant', 'startedByUser', 'updatedByUser']);
->loadMissing(['managedEnvironment', 'startedByUser', 'updatedByUser']);
$normalizedTenant = $this->lifecycleService->syncLinkedTenantAfterCancellation($resolvedDraft);
@ -65,7 +65,7 @@ public function resolve(TenantOnboardingSession|int|string $draft, User $user, W
],
);
$resolvedDraft->setRelation('tenant', $normalizedTenant);
$resolvedDraft->setRelation('managedEnvironment', $normalizedTenant);
}
return $resolvedDraft;
@ -75,18 +75,18 @@ public function resolve(TenantOnboardingSession|int|string $draft, User $user, W
* @throws AuthorizationException
* @throws NotFoundHttpException
*/
public function resolveForTrustedAction(TenantOnboardingSession|int|string $draft, User $user, Workspace $workspace): TenantOnboardingSession
public function resolveForTrustedAction(ManagedEnvironmentOnboardingSession|int|string $draft, User $user, Workspace $workspace): ManagedEnvironmentOnboardingSession
{
return $this->resolve($draft, $user, $workspace);
}
/**
* @return Collection<int, TenantOnboardingSession>
* @return Collection<int, ManagedEnvironmentOnboardingSession>
*/
public function resumableDraftsFor(User $user, Workspace $workspace): Collection
{
$drafts = TenantOnboardingSession::query()
->with(['tenant', 'startedByUser', 'updatedByUser'])
$drafts = ManagedEnvironmentOnboardingSession::query()
->with(['managedEnvironment', 'startedByUser', 'updatedByUser'])
->where('workspace_id', (int) $workspace->getKey())
->resumable()
->orderByDesc('updated_at')
@ -103,7 +103,7 @@ public function resumableDraftsFor(User $user, Workspace $workspace): Collection
$resolvedDraft = $this->lifecycleService
->syncPersistedLifecycle($draft)
->loadMissing(['tenant', 'startedByUser', 'updatedByUser']);
->loadMissing(['managedEnvironment', 'startedByUser', 'updatedByUser']);
if (! $this->lifecycleService->canResumeDraft($resolvedDraft)) {
continue;

View File

@ -4,7 +4,7 @@
namespace App\Services\Onboarding;
use App\Models\TenantOnboardingSession;
use App\Models\ManagedEnvironmentOnboardingSession;
use App\Support\Onboarding\OnboardingCheckpoint;
use App\Support\Onboarding\OnboardingDraftStage;
use App\Support\Onboarding\OnboardingLifecycleState;
@ -15,9 +15,9 @@ public function __construct(
private readonly OnboardingLifecycleService $lifecycleService,
) {}
public function resolve(?TenantOnboardingSession $draft): OnboardingDraftStage
public function resolve(?ManagedEnvironmentOnboardingSession $draft): OnboardingDraftStage
{
if (! $draft instanceof TenantOnboardingSession) {
if (! $draft instanceof ManagedEnvironmentOnboardingSession) {
return OnboardingDraftStage::Identify;
}

View File

@ -8,7 +8,7 @@
use App\Models\OperationRun;
use App\Models\ProviderConnection;
use App\Models\ManagedEnvironment;
use App\Models\TenantOnboardingSession;
use App\Models\ManagedEnvironmentOnboardingSession;
use App\Services\Tenants\TenantOperabilityService;
use App\Support\Onboarding\OnboardingCheckpoint;
use App\Support\Onboarding\OnboardingLifecycleState;
@ -26,11 +26,11 @@ public function __construct(
private readonly ProductTelemetryRecorder $productTelemetryRecorder,
) {}
public function syncPersistedLifecycle(TenantOnboardingSession $draft, bool $incrementVersion = false): TenantOnboardingSession
public function syncPersistedLifecycle(ManagedEnvironmentOnboardingSession $draft, bool $incrementVersion = false): ManagedEnvironmentOnboardingSession
{
$freshDraft = TenantOnboardingSession::query()->whereKey($draft->getKey())->first();
$freshDraft = ManagedEnvironmentOnboardingSession::query()->whereKey($draft->getKey())->first();
if (! $freshDraft instanceof TenantOnboardingSession) {
if (! $freshDraft instanceof ManagedEnvironmentOnboardingSession) {
return $draft;
}
@ -44,7 +44,7 @@ public function syncPersistedLifecycle(TenantOnboardingSession $draft, bool $inc
return $freshDraft->refresh();
}
public function applySnapshot(TenantOnboardingSession $draft, bool $incrementVersion = false): bool
public function applySnapshot(ManagedEnvironmentOnboardingSession $draft, bool $incrementVersion = false): bool
{
$snapshot = $this->snapshot($draft);
$lifecycleState = $draft->lifecycle_state instanceof OnboardingLifecycleState
@ -98,7 +98,7 @@ public function applySnapshot(TenantOnboardingSession $draft, bool $incrementVer
return $changed;
}
public function recordCompletedCheckpointTelemetryIfNeeded(TenantOnboardingSession $draft): void
public function recordCompletedCheckpointTelemetryIfNeeded(ManagedEnvironmentOnboardingSession $draft): void
{
if (! $draft->wasChanged('last_completed_checkpoint')) {
return;
@ -127,7 +127,7 @@ public function recordCompletedCheckpointTelemetryIfNeeded(TenantOnboardingSessi
workspaceId: $workspaceId,
tenantId: $tenantId,
userId: $userId,
subjectType: 'tenant_onboarding_session',
subjectType: 'managed_environment_onboarding_session',
subjectId: (int) $draft->getKey(),
metadata: [
'checkpoint_key' => $checkpoint->value,
@ -147,7 +147,7 @@ public function recordCompletedCheckpointTelemetryIfNeeded(TenantOnboardingSessi
* blocking_reason_code: string|null
* }
*/
public function snapshot(TenantOnboardingSession $draft): array
public function snapshot(ManagedEnvironmentOnboardingSession $draft): array
{
$selectedProviderConnectionId = $this->selectedProviderConnectionId($draft);
$verificationRun = $this->verificationRun($draft);
@ -334,7 +334,7 @@ public function snapshot(TenantOnboardingSession $draft): array
];
}
public function verificationRun(TenantOnboardingSession $draft): ?OperationRun
public function verificationRun(ManagedEnvironmentOnboardingSession $draft): ?OperationRun
{
$state = is_array($draft->state) ? $draft->state : [];
$runId = $this->normalizeInteger($state['verification_operation_run_id'] ?? $state['verification_run_id'] ?? null);
@ -355,7 +355,7 @@ public function verificationRun(TenantOnboardingSession $draft): ?OperationRun
}
public function verificationStatus(
TenantOnboardingSession $draft,
ManagedEnvironmentOnboardingSession $draft,
?int $selectedProviderConnectionId = null,
?OperationRun $run = null,
): string {
@ -386,7 +386,7 @@ public function verificationStatus(
}
public function verificationCanProceed(
TenantOnboardingSession $draft,
ManagedEnvironmentOnboardingSession $draft,
?int $selectedProviderConnectionId = null,
?OperationRun $run = null,
): bool {
@ -408,7 +408,7 @@ public function verificationCanProceed(
return in_array($this->verificationStatus($draft, $selectedProviderConnectionId, $run), ['ready', 'needs_attention'], true);
}
public function verificationIsBlocked(TenantOnboardingSession $draft, ?int $selectedProviderConnectionId = null): bool
public function verificationIsBlocked(ManagedEnvironmentOnboardingSession $draft, ?int $selectedProviderConnectionId = null): bool
{
return $this->verificationStatus($draft, $selectedProviderConnectionId) === 'blocked';
}
@ -425,32 +425,32 @@ public function verificationIsBlocked(TenantOnboardingSession $draft, ?int $sele
* is_completed: bool
* }>
*/
public function bootstrapRunSummaries(TenantOnboardingSession $draft, ?int $selectedProviderConnectionId = null): array
public function bootstrapRunSummaries(ManagedEnvironmentOnboardingSession $draft, ?int $selectedProviderConnectionId = null): array
{
$selectedProviderConnectionId ??= $this->selectedProviderConnectionId($draft);
return $this->bootstrapState($draft, $selectedProviderConnectionId)['summaries'];
}
public function isReadyForActivation(TenantOnboardingSession $draft): bool
public function isReadyForActivation(ManagedEnvironmentOnboardingSession $draft): bool
{
return $this->snapshot($draft)['lifecycle_state'] === OnboardingLifecycleState::ReadyForActivation;
}
public function hasActiveCheckpoint(TenantOnboardingSession $draft): bool
public function hasActiveCheckpoint(ManagedEnvironmentOnboardingSession $draft): bool
{
$snapshot = $this->snapshot($draft);
return in_array($snapshot['lifecycle_state'], [OnboardingLifecycleState::Verifying, OnboardingLifecycleState::Bootstrapping], true);
}
public function canResumeDraft(TenantOnboardingSession $draft): bool
public function canResumeDraft(ManagedEnvironmentOnboardingSession $draft): bool
{
if (! $draft->isWorkflowResumable()) {
return false;
}
$tenant = $draft->tenant;
$tenant = $draft->managedEnvironment;
if (! $tenant instanceof ManagedEnvironment) {
return true;
@ -459,9 +459,9 @@ public function canResumeDraft(TenantOnboardingSession $draft): bool
return $this->tenantOperabilityService->canResumeOnboarding($tenant);
}
public function syncLinkedTenantAfterCancellation(TenantOnboardingSession $draft): ?ManagedEnvironment
public function syncLinkedTenantAfterCancellation(ManagedEnvironmentOnboardingSession $draft): ?ManagedEnvironment
{
$tenant = $draft->tenant;
$tenant = $draft->managedEnvironment;
if (! $tenant instanceof ManagedEnvironment) {
return null;
@ -471,7 +471,7 @@ public function syncLinkedTenantAfterCancellation(TenantOnboardingSession $draft
return null;
}
$hasOtherResumableDrafts = TenantOnboardingSession::query()
$hasOtherResumableDrafts = ManagedEnvironmentOnboardingSession::query()
->where('workspace_id', (int) $draft->workspace_id)
->where('managed_environment_id', (int) $tenant->getKey())
->whereKeyNot((int) $draft->getKey())
@ -487,7 +487,7 @@ public function syncLinkedTenantAfterCancellation(TenantOnboardingSession $draft
return $tenant->fresh();
}
private function hasTenantIdentity(TenantOnboardingSession $draft): bool
private function hasTenantIdentity(ManagedEnvironmentOnboardingSession $draft): bool
{
if ($draft->managed_environment_id !== null) {
return true;
@ -499,7 +499,7 @@ private function hasTenantIdentity(TenantOnboardingSession $draft): bool
return is_string($entraTenantId) && trim($entraTenantId) !== '';
}
private function selectedProviderConnectionId(TenantOnboardingSession $draft): ?int
private function selectedProviderConnectionId(ManagedEnvironmentOnboardingSession $draft): ?int
{
$state = is_array($draft->state) ? $draft->state : [];
@ -518,7 +518,7 @@ private function selectedProviderConnectionId(TenantOnboardingSession $draft): ?
return $exists ? $providerConnectionId : null;
}
private function connectionRecentlyUpdated(TenantOnboardingSession $draft): bool
private function connectionRecentlyUpdated(ManagedEnvironmentOnboardingSession $draft): bool
{
$state = is_array($draft->state) ? $draft->state : [];
@ -639,7 +639,7 @@ private function runReasonCodes(OperationRun $run): array
* }>
* }
*/
private function bootstrapState(TenantOnboardingSession $draft, ?int $selectedProviderConnectionId): array
private function bootstrapState(ManagedEnvironmentOnboardingSession $draft, ?int $selectedProviderConnectionId): array
{
$selectedTypes = $this->bootstrapOperationTypes($draft);
$runMap = $this->bootstrapRunMap($draft, $selectedTypes);
@ -717,7 +717,7 @@ private function bootstrapState(TenantOnboardingSession $draft, ?int $selectedPr
/**
* @return array<int, string>
*/
private function bootstrapOperationTypes(TenantOnboardingSession $draft): array
private function bootstrapOperationTypes(ManagedEnvironmentOnboardingSession $draft): array
{
$state = is_array($draft->state) ? $draft->state : [];
$types = $state['bootstrap_operation_types'] ?? [];
@ -736,7 +736,7 @@ private function bootstrapOperationTypes(TenantOnboardingSession $draft): array
* @param array<int, string> $selectedTypes
* @return array<string, int>
*/
private function bootstrapRunMap(TenantOnboardingSession $draft, array $selectedTypes): array
private function bootstrapRunMap(ManagedEnvironmentOnboardingSession $draft, array $selectedTypes): array
{
$state = is_array($draft->state) ? $draft->state : [];
$runs = $state['bootstrap_operation_runs'] ?? null;

View File

@ -342,7 +342,7 @@ private function laneForContext(QueuedExecutionContext $context): TenantInteract
$runContext = is_array($context->run->context) ? $context->run->context : [];
$wizardFlow = data_get($runContext, 'wizard.flow');
if (is_string($wizardFlow) && trim($wizardFlow) === 'managed_tenant_onboarding') {
if (is_string($wizardFlow) && trim($wizardFlow) === 'managed_environment_onboarding') {
return TenantInteractionLane::OnboardingWorkflow;
}

View File

@ -17,7 +17,7 @@
/**
* Generates, auto-resolves, and re-opens permission posture findings
* based on the output of TenantPermissionService::compare().
* based on the output of ManagedEnvironmentPermissionService::compare().
*/
final class PermissionPostureFindingGenerator implements FindingGeneratorContract
{

View File

@ -4,7 +4,7 @@
namespace App\Services\PortfolioCompare;
use App\Jobs\Operations\CrossTenantPromotionExecutionJob;
use App\Jobs\Operations\CrossEnvironmentPromotionExecutionJob;
use App\Models\OperationRun;
use App\Models\ProviderConnection;
use App\Models\User;
@ -17,14 +17,14 @@
use App\Support\OperationalControls\OperationalControlBlockedException;
use App\Support\OperationalControls\OperationalControlEvaluator;
use App\Support\OperationRunStatus;
use App\Support\PortfolioCompare\CrossTenantCompareSelection;
use App\Support\PortfolioCompare\CrossTenantPromotionExecutionPlanner;
use App\Support\PortfolioCompare\CrossEnvironmentCompareSelection;
use App\Support\PortfolioCompare\CrossEnvironmentPromotionExecutionPlanner;
use Carbon\CarbonImmutable;
final class CrossTenantPromotionExecutionService
final class CrossEnvironmentPromotionExecutionService
{
public function __construct(
private readonly CrossTenantPromotionExecutionPlanner $planner,
private readonly CrossEnvironmentPromotionExecutionPlanner $planner,
private readonly OperationRunService $operationRuns,
private readonly WorkspaceAuditLogger $auditLogger,
private readonly OperationalControlEvaluator $operationalControls,
@ -35,12 +35,12 @@ public function __construct(
* @param array<string, mixed> $preflight
*/
public function start(
CrossTenantCompareSelection $selection,
CrossEnvironmentCompareSelection $selection,
array $preview,
array $preflight,
User $actor,
): ProviderOperationStartResult {
$workspace = $selection->targetTenant->workspace;
$workspace = $selection->targetEnvironment->workspace;
if (! $workspace instanceof Workspace) {
throw new \RuntimeException('Promotion execution requires a workspace context.');
@ -60,8 +60,8 @@ public function start(
'reason_text' => $decision->reasonText,
'expires_at' => $decision->expiresAt?->toIso8601String(),
'actor_id' => (int) $actor->getKey(),
'source_tenant_id' => (int) $selection->sourceTenant->getKey(),
'target_tenant_id' => (int) $selection->targetTenant->getKey(),
'source_environment_id' => (int) $selection->sourceEnvironment->getKey(),
'target_environment_id' => (int) $selection->targetEnvironment->getKey(),
'requested_scope' => 'promotion.execute',
], static fn (mixed $value): bool => $value !== null && $value !== ''),
],
@ -71,14 +71,14 @@ public function start(
resourceId: $decision->sourceActivationId !== null ? (string) $decision->sourceActivationId : null,
targetLabel: 'Promotion execution',
summary: 'Promotion execution blocked by operational control',
tenant: $selection->targetTenant,
tenant: $selection->targetEnvironment,
);
throw OperationalControlBlockedException::forDecision($decision, 'Promotion execution');
}
$plan = $this->planner->build($preview, $preflight);
$providerConnection = $this->defaultProviderConnection((int) $selection->targetTenant->getKey());
$providerConnection = $this->defaultProviderConnection((int) $selection->targetEnvironment->getKey());
$now = CarbonImmutable::now();
$identity = array_replace($plan['identity'], [
@ -87,20 +87,20 @@ public function start(
$context = [
'operation_type' => 'promotion.execute',
'source_tenant_id' => (int) $selection->sourceTenant->getKey(),
'source_tenant_name' => (string) $selection->sourceTenant->name,
'target_tenant_id' => (int) $selection->targetTenant->getKey(),
'target_tenant_name' => (string) $selection->targetTenant->name,
'source_environment_id' => (int) $selection->sourceEnvironment->getKey(),
'source_environment_name' => (string) $selection->sourceEnvironment->name,
'target_environment_id' => (int) $selection->targetEnvironment->getKey(),
'target_environment_name' => (string) $selection->targetEnvironment->name,
'provider_connection_id' => $providerConnection instanceof ProviderConnection ? (int) $providerConnection->getKey() : null,
'required_capability' => Capabilities::TENANT_MANAGE,
'workspace_required_capability' => Capabilities::WORKSPACE_BASELINES_MANAGE,
'target_scope' => [
'workspace_id' => (int) $workspace->getKey(),
'managed_environment_id' => (int) $selection->targetTenant->getKey(),
'managed_environment_id' => (int) $selection->targetEnvironment->getKey(),
'provider_connection_id' => $providerConnection instanceof ProviderConnection ? (int) $providerConnection->getKey() : null,
'entra_tenant_id' => $providerConnection instanceof ProviderConnection
? (string) $providerConnection->entra_tenant_id
: (string) ($selection->targetTenant->managed_environment_id ?? $selection->targetTenant->external_id ?? $selection->targetTenant->getKey()),
: (string) ($selection->targetEnvironment->managed_environment_id ?? $selection->targetEnvironment->external_id ?? $selection->targetEnvironment->getKey()),
],
'promotion_execution' => [
'queued_at' => $now->toIso8601String(),
@ -111,7 +111,7 @@ public function start(
];
$run = $this->operationRuns->ensureRunWithIdentity(
tenant: $selection->targetTenant,
tenant: $selection->targetEnvironment,
type: 'promotion.execute',
identityInputs: $identity,
context: $context,
@ -134,13 +134,13 @@ public function start(
$this->operationRuns->dispatchOrFail(
$run,
fn (OperationRun $operationRun): mixed => CrossTenantPromotionExecutionJob::dispatch($operationRun),
fn (OperationRun $operationRun): mixed => CrossEnvironmentPromotionExecutionJob::dispatch($operationRun),
);
$this->auditLogger->logCrossTenantPromotionExecutionQueued(
$this->auditLogger->logCrossEnvironmentPromotionExecutionQueued(
workspace: $workspace,
sourceTenant: $selection->sourceTenant,
targetTenant: $selection->targetTenant,
sourceEnvironment: $selection->sourceEnvironment,
targetEnvironment: $selection->targetEnvironment,
operationRun: $run->fresh() ?? $run,
plan: $plan,
actor: $actor,
@ -149,10 +149,10 @@ public function start(
return ProviderOperationStartResult::started($run->fresh() ?? $run, true);
}
private function defaultProviderConnection(int $tenantId): ?ProviderConnection
private function defaultProviderConnection(int $environmentId): ?ProviderConnection
{
return ProviderConnection::query()
->where('managed_environment_id', $tenantId)
->where('managed_environment_id', $environmentId)
->where('provider', 'microsoft')
->where('is_default', true)
->orderBy('id')

View File

@ -5,19 +5,19 @@
namespace App\Services\PortfolioTriage;
use App\Models\ManagedEnvironment;
use App\Models\TenantTriageReview;
use App\Models\ManagedEnvironmentTriageReview;
use App\Models\User;
use App\Services\Audit\WorkspaceAuditLogger;
use App\Support\Audit\AuditActionId;
use App\Support\BackupHealth\TenantBackupHealthAssessment;
use App\Support\PortfolioTriage\TenantTriageReviewFingerprint;
use App\Support\PortfolioTriage\ManagedEnvironmentTriageReviewFingerprint;
use Illuminate\Support\Facades\DB;
use InvalidArgumentException;
final readonly class TenantTriageReviewService
final readonly class ManagedEnvironmentTriageReviewService
{
public function __construct(
private TenantTriageReviewFingerprint $fingerprints,
private ManagedEnvironmentTriageReviewFingerprint $fingerprints,
private WorkspaceAuditLogger $auditLogger,
) {}
@ -30,11 +30,11 @@ public function markReviewed(
?TenantBackupHealthAssessment $backupHealth = null,
?array $recoveryEvidence = null,
?User $actor = null,
): TenantTriageReview {
): ManagedEnvironmentTriageReview {
return $this->store(
tenant: $tenant,
concernFamily: $concernFamily,
manualState: TenantTriageReview::STATE_REVIEWED,
manualState: ManagedEnvironmentTriageReview::STATE_REVIEWED,
backupHealth: $backupHealth,
recoveryEvidence: $recoveryEvidence,
actor: $actor,
@ -50,11 +50,11 @@ public function markFollowUpNeeded(
?TenantBackupHealthAssessment $backupHealth = null,
?array $recoveryEvidence = null,
?User $actor = null,
): TenantTriageReview {
): ManagedEnvironmentTriageReview {
return $this->store(
tenant: $tenant,
concernFamily: $concernFamily,
manualState: TenantTriageReview::STATE_FOLLOW_UP_NEEDED,
manualState: ManagedEnvironmentTriageReview::STATE_FOLLOW_UP_NEEDED,
backupHealth: $backupHealth,
recoveryEvidence: $recoveryEvidence,
actor: $actor,
@ -71,8 +71,8 @@ private function store(
?TenantBackupHealthAssessment $backupHealth,
?array $recoveryEvidence,
?User $actor,
): TenantTriageReview {
if (! in_array($manualState, TenantTriageReview::MANUAL_STATES, true)) {
): ManagedEnvironmentTriageReview {
if (! in_array($manualState, ManagedEnvironmentTriageReview::MANUAL_STATES, true)) {
throw new InvalidArgumentException('Unsupported triage review state.');
}
@ -89,7 +89,7 @@ private function store(
$workspaceId = (int) $tenant->workspace_id;
$now = now();
/** @var TenantTriageReview $review */
/** @var ManagedEnvironmentTriageReview $review */
$review = DB::transaction(function () use (
$tenant,
$workspaceId,
@ -97,8 +97,8 @@ private function store(
$currentConcern,
$actor,
$now,
): TenantTriageReview {
TenantTriageReview::query()
): ManagedEnvironmentTriageReview {
ManagedEnvironmentTriageReview::query()
->forWorkspace($workspaceId)
->forTenant((int) $tenant->getKey())
->where('concern_family', $currentConcern['concern_family'])
@ -108,7 +108,7 @@ private function store(
'updated_at' => $now,
]);
return TenantTriageReview::query()->create([
return ManagedEnvironmentTriageReview::query()->create([
'workspace_id' => $workspaceId,
'managed_environment_id' => (int) $tenant->getKey(),
'concern_family' => $currentConcern['concern_family'],
@ -126,9 +126,9 @@ private function store(
$this->auditLogger->log(
workspace: $tenant->workspace,
action: $manualState === TenantTriageReview::STATE_REVIEWED
? AuditActionId::TenantTriageReviewMarkedReviewed
: AuditActionId::TenantTriageReviewMarkedFollowUpNeeded,
action: $manualState === ManagedEnvironmentTriageReview::STATE_REVIEWED
? AuditActionId::ManagedEnvironmentTriageReviewMarkedReviewed
: AuditActionId::ManagedEnvironmentTriageReviewMarkedFollowUpNeeded,
context: [
'metadata' => [
'concern_family' => $currentConcern['concern_family'],
@ -138,7 +138,7 @@ private function store(
],
],
actor: $actor,
resourceType: 'tenant_triage_review',
resourceType: 'managed_environment_triage_review',
resourceId: (string) $review->getKey(),
targetLabel: $tenant->name,
tenant: $tenant,
@ -156,7 +156,7 @@ private function summaryFor(string $concernFamily, string $manualState): string
default => 'Portfolio concern',
};
$state = $manualState === TenantTriageReview::STATE_REVIEWED
$state = $manualState === ManagedEnvironmentTriageReview::STATE_REVIEWED
? 'reviewed'
: 'follow-up needed';

View File

@ -11,7 +11,7 @@
use App\Models\OperationRun;
use App\Models\ReviewPack;
use App\Models\ManagedEnvironment;
use App\Models\TenantReview;
use App\Models\EnvironmentReview;
use App\Models\User;
use App\Services\Audit\WorkspaceAuditLogger;
use App\Services\Entitlements\WorkspaceCommercialLifecycleResolver;
@ -136,7 +136,7 @@ public function generate(ManagedEnvironment $tenant, User $user, array $options
*
* @param array<string, mixed> $options
*/
public function generateFromReview(TenantReview $review, User $user, array $options = []): ReviewPack
public function generateFromReview(EnvironmentReview $review, User $user, array $options = []): ReviewPack
{
$review->loadMissing(['tenant', 'evidenceSnapshot', 'sections']);
@ -155,7 +155,7 @@ public function generateFromReview(TenantReview $review, User $user, array $opti
if ($existing instanceof ReviewPack) {
$this->logReviewExport($review, $user, $existing, 'reused');
$this->recordReviewPackRequestTelemetry($existing, $user, 'tenant_review');
$this->recordReviewPackRequestTelemetry($existing, $user, 'environment_review');
return $existing;
}
@ -164,7 +164,7 @@ public function generateFromReview(TenantReview $review, User $user, array $opti
tenant: $tenant,
type: OperationRunType::ReviewPackGenerate->value,
inputs: [
'tenant_review_id' => (int) $review->getKey(),
'environment_review_id' => (int) $review->getKey(),
'include_pii' => $options['include_pii'],
'include_operations' => $options['include_operations'],
'evidence_snapshot_id' => (int) $snapshot->getKey(),
@ -177,7 +177,7 @@ public function generateFromReview(TenantReview $review, User $user, array $opti
if ($queuedPack instanceof ReviewPack) {
$this->logReviewExport($review, $user, $queuedPack, 'reused_active_run');
$this->recordReviewPackRequestTelemetry($queuedPack, $user, 'tenant_review');
$this->recordReviewPackRequestTelemetry($queuedPack, $user, 'environment_review');
return $queuedPack;
}
@ -186,14 +186,14 @@ public function generateFromReview(TenantReview $review, User $user, array $opti
$reviewPack = ReviewPack::create([
'managed_environment_id' => (int) $tenant->getKey(),
'workspace_id' => (int) $tenant->workspace_id,
'tenant_review_id' => (int) $review->getKey(),
'environment_review_id' => (int) $review->getKey(),
'operation_run_id' => (int) $operationRun->getKey(),
'evidence_snapshot_id' => (int) $snapshot->getKey(),
'initiated_by_user_id' => (int) $user->getKey(),
'status' => ReviewPackStatus::Queued->value,
'options' => $options,
'summary' => [
'tenant_review_id' => (int) $review->getKey(),
'environment_review_id' => (int) $review->getKey(),
'review_status' => (string) $review->status,
'review_completeness_state' => (string) $review->completeness_state,
'section_count' => $review->sections->count(),
@ -226,7 +226,7 @@ public function generateFromReview(TenantReview $review, User $user, array $opti
});
$this->logReviewExport($review, $user, $reviewPack, 'queued');
$this->recordReviewPackRequestTelemetry($reviewPack, $user, 'tenant_review');
$this->recordReviewPackRequestTelemetry($reviewPack, $user, 'environment_review');
return $reviewPack;
}
@ -308,10 +308,10 @@ public function findExistingPack(ManagedEnvironment $tenant, string $fingerprint
->first();
}
public function findExistingPackForReview(TenantReview $review, string $fingerprint): ?ReviewPack
public function findExistingPackForReview(EnvironmentReview $review, string $fingerprint): ?ReviewPack
{
return ReviewPack::query()
->where('tenant_review_id', (int) $review->getKey())
->where('environment_review_id', (int) $review->getKey())
->ready()
->where('fingerprint', $fingerprint)
->where('expires_at', '>', now())
@ -330,12 +330,12 @@ public function checkActiveRun(ManagedEnvironment $tenant): bool
->exists();
}
public function checkActiveRunForReview(TenantReview $review): bool
public function checkActiveRunForReview(EnvironmentReview $review): bool
{
return OperationRun::query()
->where('managed_environment_id', (int) $review->managed_environment_id)
->where('type', OperationRunType::ReviewPackGenerate->value)
->whereJsonContains('context->tenant_review_id', (int) $review->getKey())
->whereJsonContains('context->environment_review_id', (int) $review->getKey())
->active()
->exists();
}
@ -376,10 +376,10 @@ private function computeFingerprintForSnapshot(EvidenceSnapshot $snapshot, array
return hash('sha256', json_encode($data, JSON_THROW_ON_ERROR));
}
public function computeFingerprintForReview(TenantReview $review, array $options): string
public function computeFingerprintForReview(EnvironmentReview $review, array $options): string
{
$data = [
'tenant_review_id' => (int) $review->getKey(),
'environment_review_id' => (int) $review->getKey(),
'review_fingerprint' => (string) $review->fingerprint,
'review_status' => (string) $review->status,
'delivery_contract' => self::REVIEW_DERIVED_DELIVERY_CONTRACT,
@ -414,7 +414,7 @@ private function findPackForRun(ManagedEnvironment $tenant, OperationRun $operat
->first();
}
private function logReviewExport(TenantReview $review, User $user, ReviewPack $reviewPack, string $mode): void
private function logReviewExport(EnvironmentReview $review, User $user, ReviewPack $reviewPack, string $mode): void
{
$tenant = $review->tenant;
@ -424,7 +424,7 @@ private function logReviewExport(TenantReview $review, User $user, ReviewPack $r
$this->auditLogger->log(
workspace: $tenant->workspace,
action: AuditActionId::TenantReviewExported,
action: AuditActionId::EnvironmentReviewExported,
context: [
'metadata' => [
'review_id' => (int) $review->getKey(),
@ -434,7 +434,7 @@ private function logReviewExport(TenantReview $review, User $user, ReviewPack $r
],
],
actor: $user,
resourceType: 'tenant_review',
resourceType: 'environment_review',
resourceId: (string) $review->getKey(),
targetLabel: sprintf('ManagedEnvironment review #%d', (int) $review->getKey()),
operationRunId: $reviewPack->operation_run_id,

View File

@ -19,8 +19,8 @@ final class OperationRunTriageService
'directory.groups.sync',
'rbac.health_check',
'entra.admin_roles.scan',
'tenant.review_pack.generate',
'tenant.review.compose',
'environment.review_pack.generate',
'environment.review.compose',
];
private const CANCELABLE_TYPES = [
@ -29,8 +29,8 @@ final class OperationRunTriageService
'directory.groups.sync',
'rbac.health_check',
'entra.admin_roles.scan',
'tenant.review_pack.generate',
'tenant.review.compose',
'environment.review_pack.generate',
'environment.review.compose',
];
public function __construct(

View File

@ -5,7 +5,7 @@
namespace App\Services\Tenants;
use App\Models\ManagedEnvironment;
use App\Models\TenantOnboardingSession;
use App\Models\ManagedEnvironmentOnboardingSession;
use App\Models\User;
use App\Services\Onboarding\OnboardingLifecycleService;
use App\Support\Audit\AuditActionId;
@ -38,7 +38,7 @@ public function buildContext(ManagedEnvironment $tenant, TenantActionSurface $su
$workspaceId = request() !== null
? $this->workspaceContext->currentWorkspaceId(request())
: null;
$resumeOutcome = $draft instanceof TenantOnboardingSession
$resumeOutcome = $draft instanceof ManagedEnvironmentOnboardingSession
? $this->tenantOperabilityService->outcomeFor(
tenant: $tenant,
question: TenantOperabilityQuestion::ResumeOnboardingEligibility,
@ -58,7 +58,7 @@ public function buildContext(ManagedEnvironment $tenant, TenantActionSurface $su
lane: $lane,
relatedOnboardingDraft: $draft,
relatedOnboardingIsResumable: $resumeOutcome?->allowed ?? false,
hasRelatedOnboardingDraft: $draft instanceof TenantOnboardingSession,
hasRelatedOnboardingDraft: $draft instanceof ManagedEnvironmentOnboardingSession,
isArchived: $lifecycle->canRestore(),
);
}
@ -130,7 +130,7 @@ public function onboardingEntryDescriptor(int $resumableDraftCount): TenantActio
};
}
public function relatedOnboardingDraft(ManagedEnvironment $tenant, ?User $user = null): ?TenantOnboardingSession
public function relatedOnboardingDraft(ManagedEnvironment $tenant, ?User $user = null): ?ManagedEnvironmentOnboardingSession
{
$user ??= auth()->user();
@ -138,12 +138,12 @@ public function relatedOnboardingDraft(ManagedEnvironment $tenant, ?User $user =
return null;
}
return TenantOnboardingSession::query()
return ManagedEnvironmentOnboardingSession::query()
->where('workspace_id', (int) $tenant->workspace_id)
->where('managed_environment_id', (int) $tenant->getKey())
->orderByDesc('updated_at')
->get()
->first(fn (TenantOnboardingSession $draft): bool => Gate::forUser($user)->allows('view', $draft));
->first(fn (ManagedEnvironmentOnboardingSession $draft): bool => Gate::forUser($user)->allows('view', $draft));
}
private function viewAction(): TenantActionDescriptor
@ -267,7 +267,7 @@ private function relatedOnboardingActionForContext(
): ?TenantActionDescriptor {
$draft = $context->relatedOnboardingDraft;
if (! $draft instanceof TenantOnboardingSession) {
if (! $draft instanceof ManagedEnvironmentOnboardingSession) {
return null;
}

View File

@ -5,7 +5,7 @@
namespace App\Services\Tenants;
use App\Models\ManagedEnvironment;
use App\Models\TenantOnboardingSession;
use App\Models\ManagedEnvironmentOnboardingSession;
use App\Models\User;
use App\Services\Auth\CapabilityResolver;
use App\Support\ReasonTranslation\ReasonResolutionEnvelope;
@ -186,7 +186,7 @@ public function outcomeFor(
?User $actor = null,
?int $workspaceId = null,
TenantInteractionLane $lane = TenantInteractionLane::AdministrativeManagement,
?TenantOnboardingSession $onboardingDraft = null,
?ManagedEnvironmentOnboardingSession $onboardingDraft = null,
?string $requiredCapability = null,
?ManagedEnvironment $selectedTenant = null,
?string $linkedRecordType = null,
@ -478,7 +478,7 @@ private function resumeOnboardingOutcome(TenantOperabilityContext $context, Tena
);
}
if ($context->onboardingDraft instanceof TenantOnboardingSession && ! $context->onboardingDraft->isWorkflowResumable()) {
if ($context->onboardingDraft instanceof ManagedEnvironmentOnboardingSession && ! $context->onboardingDraft->isWorkflowResumable()) {
return TenantOperabilityOutcome::deny(
question: TenantOperabilityQuestion::ResumeOnboardingEligibility,
lifecycle: $lifecycle,

View File

@ -34,19 +34,19 @@ enum AuditActionId: string
case PolicyProviderMissingDetected = 'policy.provider_missing_detected';
case PolicyProviderMissingCleared = 'policy.provider_missing_cleared';
// Managed tenant onboarding wizard.
case ManagedTenantOnboardingStart = 'managed_tenant_onboarding.start';
case ManagedTenantOnboardingResume = 'managed_tenant_onboarding.resume';
case ManagedTenantOnboardingDraftSelected = 'managed_tenant_onboarding.draft_selected';
case ManagedTenantOnboardingDraftUpdated = 'managed_tenant_onboarding.draft_updated';
case ManagedTenantOnboardingProviderConnectionChanged = 'managed_tenant_onboarding.provider_connection_changed';
case ManagedTenantOnboardingVerificationStart = 'managed_tenant_onboarding.verification_start';
case ManagedTenantOnboardingVerificationPersisted = 'managed_tenant_onboarding.verification_persisted';
case ManagedTenantOnboardingBootstrapStarted = 'managed_tenant_onboarding.bootstrap_started';
case ManagedTenantOnboardingCancelled = 'managed_tenant_onboarding.cancelled';
case ManagedTenantOnboardingDeleted = 'managed_tenant_onboarding.deleted';
case ManagedTenantOnboardingActivationOverrideUsed = 'managed_tenant_onboarding.activation_override_used';
case ManagedTenantOnboardingActivation = 'managed_tenant_onboarding.activation';
// Managed environment onboarding wizard.
case ManagedEnvironmentOnboardingStart = 'managed_environment_onboarding.start';
case ManagedEnvironmentOnboardingResume = 'managed_environment_onboarding.resume';
case ManagedEnvironmentOnboardingDraftSelected = 'managed_environment_onboarding.draft_selected';
case ManagedEnvironmentOnboardingDraftUpdated = 'managed_environment_onboarding.draft_updated';
case ManagedEnvironmentOnboardingProviderConnectionChanged = 'managed_environment_onboarding.provider_connection_changed';
case ManagedEnvironmentOnboardingVerificationStart = 'managed_environment_onboarding.verification_start';
case ManagedEnvironmentOnboardingVerificationPersisted = 'managed_environment_onboarding.verification_persisted';
case ManagedEnvironmentOnboardingBootstrapStarted = 'managed_environment_onboarding.bootstrap_started';
case ManagedEnvironmentOnboardingCancelled = 'managed_environment_onboarding.cancelled';
case ManagedEnvironmentOnboardingDeleted = 'managed_environment_onboarding.deleted';
case ManagedEnvironmentOnboardingActivationOverrideUsed = 'managed_environment_onboarding.activation_override_used';
case ManagedEnvironmentOnboardingActivation = 'managed_environment_onboarding.activation';
case VerificationCompleted = 'verification.completed';
case VerificationCheckAcknowledged = 'verification.check_acknowledged';
@ -79,9 +79,9 @@ enum AuditActionId: string
case BaselineCompareStarted = 'baseline_compare.started';
case BaselineCompareCompleted = 'baseline_compare.completed';
case BaselineCompareFailed = 'baseline_compare.failed';
case CrossTenantPromotionPreflightGenerated = 'cross_tenant_promotion_preflight.generated';
case CrossTenantPromotionExecutionQueued = 'cross_tenant_promotion_execution.queued';
case CrossTenantPromotionExecutionCompleted = 'cross_tenant_promotion_execution.completed';
case CrossEnvironmentPromotionPreflightGenerated = 'cross_environment_promotion_preflight.generated';
case CrossEnvironmentPromotionExecutionQueued = 'cross_environment_promotion_execution.queued';
case CrossEnvironmentPromotionExecutionCompleted = 'cross_environment_promotion_execution.completed';
case BaselineAssignmentCreated = 'baseline_assignment.created';
case BaselineAssignmentUpdated = 'baseline_assignment.updated';
case BaselineAssignmentDeleted = 'baseline_assignment.deleted';
@ -104,17 +104,17 @@ enum AuditActionId: string
case EvidenceSnapshotRefreshed = 'evidence_snapshot.refreshed';
case EvidenceSnapshotExpired = 'evidence_snapshot.expired';
case EvidenceSnapshotOpened = 'evidence_snapshot.opened';
case TenantReviewCreated = 'tenant_review.created';
case TenantReviewRefreshed = 'tenant_review.refreshed';
case TenantReviewPublished = 'tenant_review.published';
case TenantReviewArchived = 'tenant_review.archived';
case TenantReviewOpened = 'tenant_review.opened';
case TenantReviewExported = 'tenant_review.exported';
case TenantReviewSuccessorCreated = 'tenant_review.successor_created';
case EnvironmentReviewCreated = 'environment_review.created';
case EnvironmentReviewRefreshed = 'environment_review.refreshed';
case EnvironmentReviewPublished = 'environment_review.published';
case EnvironmentReviewArchived = 'environment_review.archived';
case EnvironmentReviewOpened = 'environment_review.opened';
case EnvironmentReviewExported = 'environment_review.exported';
case EnvironmentReviewSuccessorCreated = 'environment_review.successor_created';
case CustomerReviewWorkspaceOpened = 'customer_review_workspace.opened';
case ReviewPackDownloaded = 'review_pack.downloaded';
case TenantTriageReviewMarkedReviewed = 'tenant_triage_review.marked_reviewed';
case TenantTriageReviewMarkedFollowUpNeeded = 'tenant_triage_review.marked_follow_up_needed';
case ManagedEnvironmentTriageReviewMarkedReviewed = 'managed_environment_triage_review.marked_reviewed';
case ManagedEnvironmentTriageReviewMarkedFollowUpNeeded = 'managed_environment_triage_review.marked_follow_up_needed';
case SupportDiagnosticsOpened = 'support_diagnostics.opened';
case SupportRequestCreated = 'support_request.created';
@ -209,18 +209,18 @@ private static function labels(): array
self::ManagedEnvironmentAccessScopeRemove->value => 'ManagedEnvironment access scope removal',
self::PolicyProviderMissingDetected->value => 'Policy provider missing detected',
self::PolicyProviderMissingCleared->value => 'Policy provider missing cleared',
self::ManagedTenantOnboardingStart->value => 'Managed tenant onboarding start',
self::ManagedTenantOnboardingResume->value => 'Managed tenant onboarding resume',
self::ManagedTenantOnboardingDraftSelected->value => 'Managed tenant onboarding draft selected',
self::ManagedTenantOnboardingDraftUpdated->value => 'Managed tenant onboarding draft updated',
self::ManagedTenantOnboardingProviderConnectionChanged->value => 'Managed tenant onboarding provider connection changed',
self::ManagedTenantOnboardingVerificationStart->value => 'Managed tenant onboarding verification start',
self::ManagedTenantOnboardingVerificationPersisted->value => 'Managed tenant onboarding verification persisted',
self::ManagedTenantOnboardingBootstrapStarted->value => 'Managed tenant onboarding bootstrap started',
self::ManagedTenantOnboardingCancelled->value => 'Managed tenant onboarding cancelled',
self::ManagedTenantOnboardingDeleted->value => 'Managed tenant onboarding deleted',
self::ManagedTenantOnboardingActivationOverrideUsed->value => 'Managed tenant onboarding activation override used',
self::ManagedTenantOnboardingActivation->value => 'Managed tenant onboarding activation',
self::ManagedEnvironmentOnboardingStart->value => 'Managed environment onboarding start',
self::ManagedEnvironmentOnboardingResume->value => 'Managed environment onboarding resume',
self::ManagedEnvironmentOnboardingDraftSelected->value => 'Managed environment onboarding draft selected',
self::ManagedEnvironmentOnboardingDraftUpdated->value => 'Managed environment onboarding draft updated',
self::ManagedEnvironmentOnboardingProviderConnectionChanged->value => 'Managed environment onboarding provider connection changed',
self::ManagedEnvironmentOnboardingVerificationStart->value => 'Managed environment onboarding verification start',
self::ManagedEnvironmentOnboardingVerificationPersisted->value => 'Managed environment onboarding verification persisted',
self::ManagedEnvironmentOnboardingBootstrapStarted->value => 'Managed environment onboarding bootstrap started',
self::ManagedEnvironmentOnboardingCancelled->value => 'Managed environment onboarding cancelled',
self::ManagedEnvironmentOnboardingDeleted->value => 'Managed environment onboarding deleted',
self::ManagedEnvironmentOnboardingActivationOverrideUsed->value => 'Managed environment onboarding activation override used',
self::ManagedEnvironmentOnboardingActivation->value => 'Managed environment onboarding activation',
self::VerificationCompleted->value => 'Verification completed',
self::VerificationCheckAcknowledged->value => 'Verification check acknowledged',
self::AlertDestinationCreated->value => 'Alert destination created',
@ -249,9 +249,9 @@ private static function labels(): array
self::BaselineCompareStarted->value => 'Baseline compare started',
self::BaselineCompareCompleted->value => 'Baseline compare completed',
self::BaselineCompareFailed->value => 'Baseline compare failed',
self::CrossTenantPromotionPreflightGenerated->value => 'Cross-tenant promotion preflight generated',
self::CrossTenantPromotionExecutionQueued->value => 'Cross-tenant promotion execution queued',
self::CrossTenantPromotionExecutionCompleted->value => 'Cross-tenant promotion execution completed',
self::CrossEnvironmentPromotionPreflightGenerated->value => 'Cross-environment promotion preflight generated',
self::CrossEnvironmentPromotionExecutionQueued->value => 'Cross-environment promotion execution queued',
self::CrossEnvironmentPromotionExecutionCompleted->value => 'Cross-environment promotion execution completed',
self::BaselineAssignmentCreated->value => 'Baseline assignment created',
self::BaselineAssignmentUpdated->value => 'Baseline assignment updated',
self::BaselineAssignmentDeleted->value => 'Baseline assignment deleted',
@ -274,17 +274,17 @@ private static function labels(): array
self::EvidenceSnapshotRefreshed->value => 'Evidence snapshot refreshed',
self::EvidenceSnapshotExpired->value => 'Evidence snapshot expired',
self::EvidenceSnapshotOpened->value => 'Evidence snapshot opened',
self::TenantReviewCreated->value => 'ManagedEnvironment review created',
self::TenantReviewRefreshed->value => 'ManagedEnvironment review refreshed',
self::TenantReviewPublished->value => 'ManagedEnvironment review published',
self::TenantReviewArchived->value => 'ManagedEnvironment review archived',
self::TenantReviewOpened->value => 'ManagedEnvironment review opened',
self::TenantReviewExported->value => 'ManagedEnvironment review exported',
self::TenantReviewSuccessorCreated->value => 'ManagedEnvironment review next cycle created',
self::EnvironmentReviewCreated->value => 'ManagedEnvironment review created',
self::EnvironmentReviewRefreshed->value => 'ManagedEnvironment review refreshed',
self::EnvironmentReviewPublished->value => 'ManagedEnvironment review published',
self::EnvironmentReviewArchived->value => 'ManagedEnvironment review archived',
self::EnvironmentReviewOpened->value => 'ManagedEnvironment review opened',
self::EnvironmentReviewExported->value => 'ManagedEnvironment review exported',
self::EnvironmentReviewSuccessorCreated->value => 'ManagedEnvironment review next cycle created',
self::CustomerReviewWorkspaceOpened->value => 'Customer review workspace opened',
self::ReviewPackDownloaded->value => 'Review pack downloaded',
self::TenantTriageReviewMarkedReviewed->value => 'Triage review marked reviewed',
self::TenantTriageReviewMarkedFollowUpNeeded->value => 'Triage review marked follow-up needed',
self::ManagedEnvironmentTriageReviewMarkedReviewed->value => 'Triage review marked reviewed',
self::ManagedEnvironmentTriageReviewMarkedFollowUpNeeded->value => 'Triage review marked follow-up needed',
self::SupportDiagnosticsOpened->value => 'Support diagnostics opened',
self::SupportRequestCreated->value => 'Support request created',
self::SupportRequestExternalTicketCreated->value => 'Support request external ticket created',
@ -362,9 +362,9 @@ private static function summaries(): array
self::BaselineProfileUpdated->value => 'Baseline profile updated',
self::BaselineProfileArchived->value => 'Baseline profile archived',
self::BaselineProfileScopeBackfilled->value => 'Baseline profile scope backfilled',
self::CrossTenantPromotionPreflightGenerated->value => 'Cross-tenant promotion preflight generated',
self::CrossTenantPromotionExecutionQueued->value => 'Cross-tenant promotion execution queued',
self::CrossTenantPromotionExecutionCompleted->value => 'Cross-tenant promotion execution completed',
self::CrossEnvironmentPromotionPreflightGenerated->value => 'Cross-environment promotion preflight generated',
self::CrossEnvironmentPromotionExecutionQueued->value => 'Cross-environment promotion execution queued',
self::CrossEnvironmentPromotionExecutionCompleted->value => 'Cross-environment promotion execution completed',
self::AlertDestinationCreated->value => 'Alert destination created',
self::AlertDestinationUpdated->value => 'Alert destination updated',
self::AlertDestinationDeleted->value => 'Alert destination deleted',
@ -388,13 +388,13 @@ private static function summaries(): array
self::EvidenceSnapshotRefreshed->value => 'Evidence snapshot refreshed',
self::EvidenceSnapshotExpired->value => 'Evidence snapshot expired',
self::EvidenceSnapshotOpened->value => 'Evidence snapshot opened',
self::TenantReviewCreated->value => 'ManagedEnvironment review created',
self::TenantReviewRefreshed->value => 'ManagedEnvironment review refreshed',
self::TenantReviewPublished->value => 'ManagedEnvironment review published',
self::TenantReviewArchived->value => 'ManagedEnvironment review archived',
self::TenantReviewOpened->value => 'ManagedEnvironment review opened',
self::TenantReviewExported->value => 'ManagedEnvironment review exported',
self::TenantReviewSuccessorCreated->value => 'ManagedEnvironment review next cycle created',
self::EnvironmentReviewCreated->value => 'ManagedEnvironment review created',
self::EnvironmentReviewRefreshed->value => 'ManagedEnvironment review refreshed',
self::EnvironmentReviewPublished->value => 'ManagedEnvironment review published',
self::EnvironmentReviewArchived->value => 'ManagedEnvironment review archived',
self::EnvironmentReviewOpened->value => 'ManagedEnvironment review opened',
self::EnvironmentReviewExported->value => 'ManagedEnvironment review exported',
self::EnvironmentReviewSuccessorCreated->value => 'ManagedEnvironment review next cycle created',
self::CustomerReviewWorkspaceOpened->value => 'Customer review workspace opened',
self::ReviewPackDownloaded->value => 'Review pack downloaded',
self::SupportDiagnosticsOpened->value => 'Support diagnostics opened',

View File

@ -27,28 +27,28 @@ class Capabilities
public const WORKSPACE_MEMBERSHIP_MANAGE = 'workspace_membership.manage';
// Managed tenant onboarding
public const WORKSPACE_MANAGED_TENANT_ONBOARD = 'workspace_managed_tenant.onboard';
// Managed environment onboarding
public const WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD = 'workspace_managed_environment.onboard';
public const WORKSPACE_MANAGED_TENANT_ONBOARD_IDENTIFY = 'workspace_managed_tenant.onboard.identify';
public const WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_IDENTIFY = 'workspace_managed_environment.onboard.identify';
public const WORKSPACE_MANAGED_TENANT_ONBOARD_CANCEL = 'workspace_managed_tenant.onboard.cancel';
public const WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CANCEL = 'workspace_managed_environment.onboard.cancel';
public const WORKSPACE_MANAGED_TENANT_ONBOARD_CONNECTION_VIEW = 'workspace_managed_tenant.onboard.connection.view';
public const WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CONNECTION_VIEW = 'workspace_managed_environment.onboard.connection.view';
public const WORKSPACE_MANAGED_TENANT_ONBOARD_CONNECTION_MANAGE = 'workspace_managed_tenant.onboard.connection.manage';
public const WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CONNECTION_MANAGE = 'workspace_managed_environment.onboard.connection.manage';
public const WORKSPACE_MANAGED_TENANT_ONBOARD_CONNECTION_MANAGE_DEDICATED = 'workspace_managed_tenant.onboard.connection.manage_dedicated';
public const WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CONNECTION_MANAGE_DEDICATED = 'workspace_managed_environment.onboard.connection.manage_dedicated';
public const WORKSPACE_MANAGED_TENANT_ONBOARD_VERIFICATION_START = 'workspace_managed_tenant.onboard.verification.start';
public const WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_VERIFICATION_START = 'workspace_managed_environment.onboard.verification.start';
public const WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_INVENTORY_SYNC = 'workspace_managed_tenant.onboard.bootstrap.inventory_sync';
public const WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_BOOTSTRAP_INVENTORY_SYNC = 'workspace_managed_environment.onboard.bootstrap.inventory_sync';
public const WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_POLICY_SYNC = 'workspace_managed_tenant.onboard.bootstrap.policy_sync';
public const WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_BOOTSTRAP_POLICY_SYNC = 'workspace_managed_environment.onboard.bootstrap.policy_sync';
public const WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_BACKUP_BOOTSTRAP = 'workspace_managed_tenant.onboard.bootstrap.backup_bootstrap';
public const WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_BOOTSTRAP_BACKUP_BOOTSTRAP = 'workspace_managed_environment.onboard.bootstrap.backup_bootstrap';
public const WORKSPACE_MANAGED_TENANT_ONBOARD_ACTIVATE = 'workspace_managed_tenant.onboard.activate';
public const WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_ACTIVATE = 'workspace_managed_environment.onboard.activate';
// Workspace settings
public const WORKSPACE_SETTINGS_VIEW = 'workspace_settings.view';
@ -146,12 +146,12 @@ class Capabilities
public const REVIEW_PACK_MANAGE = 'review_pack.manage';
// ManagedEnvironment reviews
public const TENANT_REVIEW_VIEW = 'tenant_review.view';
public const ENVIRONMENT_REVIEW_VIEW = 'environment_review.view';
public const TENANT_REVIEW_MANAGE = 'tenant_review.manage';
public const ENVIRONMENT_REVIEW_MANAGE = 'environment_review.manage';
// Portfolio triage review progress
public const TENANT_TRIAGE_REVIEW_MANAGE = 'tenant_triage_review.manage';
public const MANAGED_ENVIRONMENT_TRIAGE_REVIEW_MANAGE = 'managed_environment_triage_review.manage';
// Evidence snapshots
public const EVIDENCE_VIEW = 'evidence.view';

View File

@ -42,7 +42,7 @@ final class BadgeCatalog
BadgeDomain::TenantStatus->value => Domains\TenantStatusBadge::class,
BadgeDomain::TenantWorkspacePosture->value => Domains\TenantWorkspacePostureBadge::class,
BadgeDomain::TenantRbacStatus->value => Domains\TenantRbacStatusBadge::class,
BadgeDomain::TenantPermissionStatus->value => Domains\TenantPermissionStatusBadge::class,
BadgeDomain::ManagedEnvironmentPermissionStatus->value => Domains\ManagedEnvironmentPermissionStatusBadge::class,
BadgeDomain::PolicySnapshotMode->value => Domains\PolicySnapshotModeBadge::class,
BadgeDomain::PolicyRestoreMode->value => Domains\PolicyRestoreModeBadge::class,
BadgeDomain::PolicyRisk->value => Domains\PolicyRiskBadge::class,
@ -53,7 +53,7 @@ final class BadgeCatalog
BadgeDomain::ProviderConsentStatus->value => Domains\ProviderConsentStatusBadge::class,
BadgeDomain::ProviderVerificationStatus->value => Domains\ProviderVerificationStatusBadge::class,
BadgeDomain::ProviderCapabilityStatus->value => Domains\ProviderCapabilityStatusBadge::class,
BadgeDomain::ManagedTenantOnboardingVerificationStatus->value => Domains\ManagedTenantOnboardingVerificationStatusBadge::class,
BadgeDomain::ManagedEnvironmentOnboardingVerificationStatus->value => Domains\ManagedEnvironmentOnboardingVerificationStatusBadge::class,
BadgeDomain::VerificationCheckStatus->value => Domains\VerificationCheckStatusBadge::class,
BadgeDomain::VerificationCheckSeverity->value => Domains\VerificationCheckSeverityBadge::class,
BadgeDomain::VerificationReportOverall->value => Domains\VerificationReportOverallBadge::class,
@ -66,9 +66,9 @@ final class BadgeCatalog
BadgeDomain::CommercialLifecycleState->value => Domains\CommercialLifecycleStateBadge::class,
BadgeDomain::EvidenceSnapshotStatus->value => Domains\EvidenceSnapshotStatusBadge::class,
BadgeDomain::EvidenceCompleteness->value => Domains\EvidenceCompletenessBadge::class,
BadgeDomain::TenantReviewStatus->value => Domains\TenantReviewStatusBadge::class,
BadgeDomain::TenantReviewCompleteness->value => Domains\TenantReviewCompletenessStateBadge::class,
BadgeDomain::TenantTriageReviewState->value => Domains\TenantTriageReviewStateBadge::class,
BadgeDomain::EnvironmentReviewStatus->value => Domains\EnvironmentReviewStatusBadge::class,
BadgeDomain::EnvironmentReviewCompleteness->value => Domains\EnvironmentReviewCompletenessStateBadge::class,
BadgeDomain::ManagedEnvironmentTriageReviewState->value => Domains\ManagedEnvironmentTriageReviewStateBadge::class,
BadgeDomain::SystemHealth->value => Domains\SystemHealthBadge::class,
BadgeDomain::ReferenceResolutionState->value => Domains\ReferenceResolutionStateBadge::class,
BadgeDomain::DiffRowStatus->value => Domains\DiffRowStatusBadge::class,
@ -204,7 +204,7 @@ public static function normalizeProviderVerificationStatus(mixed $value): ?strin
};
}
public static function normalizeManagedTenantOnboardingVerificationStatus(mixed $value): ?string
public static function normalizeManagedEnvironmentOnboardingVerificationStatus(mixed $value): ?string
{
$state = self::normalizeState($value);

View File

@ -33,7 +33,7 @@ enum BadgeDomain: string
case TenantStatus = 'tenant_status';
case TenantWorkspacePosture = 'tenant_workspace_posture';
case TenantRbacStatus = 'tenant_rbac_status';
case TenantPermissionStatus = 'tenant_permission_status';
case ManagedEnvironmentPermissionStatus = 'managed_environment_permission_status';
case PolicySnapshotMode = 'policy_snapshot_mode';
case PolicyRestoreMode = 'policy_restore_mode';
case PolicyRisk = 'policy_risk';
@ -44,7 +44,7 @@ enum BadgeDomain: string
case ProviderConsentStatus = 'provider_connection.consent_status';
case ProviderVerificationStatus = 'provider_connection.verification_status';
case ProviderCapabilityStatus = 'provider_capability_status';
case ManagedTenantOnboardingVerificationStatus = 'managed_tenant_onboarding.verification_status';
case ManagedEnvironmentOnboardingVerificationStatus = 'managed_environment_onboarding.verification_status';
case VerificationCheckStatus = 'verification_check_status';
case VerificationCheckSeverity = 'verification_check_severity';
case VerificationReportOverall = 'verification_report_overall';
@ -57,9 +57,9 @@ enum BadgeDomain: string
case CommercialLifecycleState = 'commercial_lifecycle_state';
case EvidenceSnapshotStatus = 'evidence_snapshot_status';
case EvidenceCompleteness = 'evidence_completeness';
case TenantReviewStatus = 'tenant_review_status';
case TenantReviewCompleteness = 'tenant_review_completeness';
case TenantTriageReviewState = 'tenant_triage_review_state';
case EnvironmentReviewStatus = 'environment_review_status';
case EnvironmentReviewCompleteness = 'environment_review_completeness';
case ManagedEnvironmentTriageReviewState = 'managed_environment_triage_review_state';
case SystemHealth = 'system_health';
case ReferenceResolutionState = 'reference_resolution_state';
case DiffRowStatus = 'diff_row_status';

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