satisfy(ActionSurfaceSlot::ListHeader, 'Header action: Assign tenant (manage-gated).') ->satisfy(ActionSurfaceSlot::InspectAffordance, ActionSurfaceInspectAffordance::ClickableRow->value) ->exempt(ActionSurfaceSlot::ListRowMoreMenu, 'v1 assignments have no row-level actions beyond delete.') ->exempt(ActionSurfaceSlot::ListBulkMoreGroup, 'No bulk mutations for assignments in v1.') ->satisfy(ActionSurfaceSlot::ListEmptyState, 'Empty state encourages assigning a tenant.'); } public function table(Table $table): Table { return $table ->columns([ Tables\Columns\TextColumn::make('tenant.display_name') ->label('Tenant') ->searchable(), Tables\Columns\TextColumn::make('assignedByUser.name') ->label('Assigned by') ->placeholder('—'), Tables\Columns\TextColumn::make('created_at') ->label('Assigned at') ->dateTime() ->sortable(), ]) ->headerActions([ $this->assignTenantAction(), ]) ->actions([ $this->removeAssignmentAction(), ]) ->emptyStateHeading('No tenants assigned') ->emptyStateDescription('Assign a tenant to compare its state against this baseline profile.') ->emptyStateActions([ $this->assignTenantAction(), ]); } private function assignTenantAction(): Action { return Action::make('assign') ->label('Assign Tenant') ->icon('heroicon-o-plus') ->visible(fn (): bool => $this->hasManageCapability()) ->form([ Select::make('tenant_id') ->label('Tenant') ->options(fn (): array => $this->getAvailableTenantOptions()) ->required() ->searchable(), ]) ->action(function (array $data): void { $user = auth()->user(); if (! $user instanceof User || ! $this->hasManageCapability()) { Notification::make() ->title('Permission denied') ->danger() ->send(); return; } /** @var BaselineProfile $profile */ $profile = $this->getOwnerRecord(); $tenantId = (int) $data['tenant_id']; $existing = BaselineTenantAssignment::query() ->where('workspace_id', $profile->workspace_id) ->where('tenant_id', $tenantId) ->first(); if ($existing instanceof BaselineTenantAssignment) { Notification::make() ->title('Tenant already assigned') ->body('This tenant already has a baseline assignment in this workspace.') ->warning() ->send(); return; } $assignment = BaselineTenantAssignment::create([ 'workspace_id' => (int) $profile->workspace_id, 'tenant_id' => $tenantId, 'baseline_profile_id' => (int) $profile->getKey(), 'assigned_by_user_id' => (int) $user->getKey(), ]); $this->auditAssignment($profile, $assignment, $user, 'created'); Notification::make() ->title('Tenant assigned') ->success() ->send(); }); } private function removeAssignmentAction(): Action { return Action::make('remove') ->label('Remove') ->icon('heroicon-o-trash') ->color('danger') ->visible(fn (): bool => $this->hasManageCapability()) ->requiresConfirmation() ->modalHeading('Remove tenant assignment') ->modalDescription('Are you sure you want to remove this tenant assignment? This will not delete any existing findings.') ->action(function (BaselineTenantAssignment $record): void { $user = auth()->user(); if (! $user instanceof User || ! $this->hasManageCapability()) { Notification::make() ->title('Permission denied') ->danger() ->send(); return; } /** @var BaselineProfile $profile */ $profile = $this->getOwnerRecord(); $this->auditAssignment($profile, $record, $user, 'removed'); $record->delete(); Notification::make() ->title('Assignment removed') ->success() ->send(); }); } /** * @return array */ private function getAvailableTenantOptions(): array { /** @var BaselineProfile $profile */ $profile = $this->getOwnerRecord(); $assignedTenantIds = BaselineTenantAssignment::query() ->where('workspace_id', $profile->workspace_id) ->pluck('tenant_id') ->all(); $query = Tenant::query() ->where('workspace_id', $profile->workspace_id) ->orderBy('display_name'); if (! empty($assignedTenantIds)) { $query->whereNotIn('id', $assignedTenantIds); } return $query->pluck('display_name', 'id')->all(); } private function auditAssignment( BaselineProfile $profile, BaselineTenantAssignment $assignment, User $user, string $action, ): void { $workspace = Workspace::query()->find($profile->workspace_id); if (! $workspace instanceof Workspace) { return; } $tenant = Tenant::query()->find($assignment->tenant_id); $auditLogger = app(WorkspaceAuditLogger::class); $auditLogger->log( workspace: $workspace, action: 'baseline.assignment.' . $action, context: [ 'baseline_profile_id' => (int) $profile->getKey(), 'baseline_profile_name' => (string) $profile->name, 'tenant_id' => (int) $assignment->tenant_id, 'tenant_name' => $tenant instanceof Tenant ? (string) $tenant->display_name : '—', ], actor: $user, resourceType: 'baseline_profile', resourceId: (string) $profile->getKey(), ); } private function hasManageCapability(): bool { $user = auth()->user(); if (! $user instanceof User) { return false; } $workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request()); if ($workspaceId === null) { return false; } $workspace = Workspace::query()->whereKey($workspaceId)->first(); if (! $workspace instanceof Workspace) { return false; } $resolver = app(\App\Services\Auth\WorkspaceCapabilityResolver::class); return $resolver->isMember($user, $workspace) && $resolver->can($user, $workspace, Capabilities::WORKSPACE_BASELINES_MANAGE); } }