satisfy(ActionSurfaceSlot::ListHeader, 'Add explicit access scope action is available in the relation header.') ->exempt(ActionSurfaceSlot::InspectAffordance, 'ManagedEnvironment access scope rows are managed inline and have no separate inspect destination.') ->exempt(ActionSurfaceSlot::ListRowMoreMenu, 'Remove stays direct for focused inline scope management.') ->exempt(ActionSurfaceSlot::ListBulkMoreGroup, 'Bulk access scope mutations are intentionally omitted.') ->exempt(ActionSurfaceSlot::ListEmptyState, 'No empty-state actions are exposed; add explicit access scope remains available in the header.'); } public static function canViewForRecord(Model $ownerRecord, string $pageClass): bool { if (! $ownerRecord instanceof ManagedEnvironment) { return false; } if ($pageClass !== ManageTenantMemberships::class) { return false; } $user = auth()->user(); if (! $user instanceof User) { return false; } if (! $user->canAccessTenant($ownerRecord)) { return false; } /** @var CapabilityResolver $resolver */ $resolver = app(CapabilityResolver::class); return $resolver->can($user, $ownerRecord, Capabilities::TENANT_MEMBERSHIP_VIEW); } public function table(Table $table): Table { return $table ->modifyQueryUsing(fn (Builder $query) => $query->with('user')) ->defaultSort('created_at', 'desc') ->paginated(\App\Support\Filament\TablePaginationProfiles::relationManager()) ->columns([ Tables\Columns\TextColumn::make('user.email') ->label(__('User')) ->searchable(), Tables\Columns\TextColumn::make('user_domain') ->label(__('Domain')) ->getStateUsing(function (ManagedEnvironmentMembership $record): ?string { $email = $record->user?->email; if (! is_string($email) || $email === '' || ! str_contains($email, '@')) { return null; } return (string) str($email)->after('@')->lower(); }), Tables\Columns\TextColumn::make('user.name') ->label(__('Name')) ->toggleable(isToggledHiddenByDefault: true), Tables\Columns\TextColumn::make('source') ->badge() ->toggleable(isToggledHiddenByDefault: true), Tables\Columns\TextColumn::make('created_at')->since()->sortable(), ]) ->headerActions([ UiEnforcement::forTableAction( Action::make('add_member') ->label(__('Add explicit access scope')) ->icon('heroicon-o-plus') ->form([ Forms\Components\Select::make('user_id') ->label(__('Workspace member')) ->required() ->searchable() ->options(fn (): array => $this->workspaceMemberOptions()), ]) ->action(function (array $data, TenantMembershipManager $manager): void { $tenant = $this->getOwnerRecord(); if (! $tenant instanceof ManagedEnvironment) { abort(404); } $actor = auth()->user(); if (! $actor instanceof User) { abort(403); } $member = User::query()->find((int) $data['user_id']); if (! $member) { Notification::make()->title(__('User not found'))->danger()->send(); return; } try { $manager->grantScope( tenant: $tenant, actor: $actor, member: $member, source: 'manual', ); } catch (\Throwable $throwable) { Notification::make() ->title(__('Failed to add explicit access scope')) ->body($throwable->getMessage()) ->danger() ->send(); return; } Notification::make()->title(__('Explicit access scope added'))->success()->send(); $this->resetTable(); }), fn () => $this->getOwnerRecord(), ) ->requireCapability(Capabilities::TENANT_MEMBERSHIP_MANAGE) ->tooltip('You do not have permission to manage environment access scopes.') ->apply(), ]) ->actions([ UiEnforcement::forTableAction( Action::make('remove') ->label(__('Remove explicit scope')) ->color('danger') ->icon('heroicon-o-x-mark') ->requiresConfirmation() ->action(function (ManagedEnvironmentMembership $record, TenantMembershipManager $manager): void { $tenant = $this->getOwnerRecord(); if (! $tenant instanceof ManagedEnvironment) { abort(404); } $actor = auth()->user(); if (! $actor instanceof User) { abort(403); } try { $manager->removeMember($tenant, $actor, $record); } catch (\Throwable $throwable) { Notification::make() ->title(__('Failed to remove explicit access scope')) ->body($throwable->getMessage()) ->danger() ->send(); return; } Notification::make()->title(__('Explicit access scope removed'))->success()->send(); $this->resetTable(); }), fn () => $this->getOwnerRecord(), ) ->requireCapability(Capabilities::TENANT_MEMBERSHIP_MANAGE) ->tooltip('You do not have permission to manage environment access scopes.') ->destructive() ->apply(), ]) ->bulkActions([]) ->emptyStateHeading(__('No explicit access scopes')) ->emptyStateDescription(__('Workspace members inherit access unless explicit scopes narrow that member to selected environments.')); } /** * @return array */ private function workspaceMemberOptions(): array { $tenant = $this->getOwnerRecord(); if (! $tenant instanceof ManagedEnvironment || ! is_numeric($tenant->workspace_id)) { return []; } return User::query() ->whereHas('workspaceMemberships', fn (Builder $query): Builder => $query->where('workspace_id', (int) $tenant->workspace_id)) ->whereDoesntHave('tenantMemberships', fn (Builder $query): Builder => $query->where('managed_environment_id', (int) $tenant->getKey())) ->orderBy('email') ->get(['id', 'name', 'email']) ->mapWithKeys(fn (User $user): array => [ (string) $user->id => trim((string) ($user->name ? "{$user->name} ({$user->email})" : $user->email)), ]) ->all(); } }