schema([ Forms\Components\TextInput::make('name') ->label(__('admin.oauth.fields.name')) ->required() ->maxLength(255), Forms\Components\TextInput::make('client_id') ->label(__('admin.oauth.fields.client_id')) ->required() ->unique(ignoreRecord: true) ->maxLength(255), Forms\Components\TextInput::make('client_secret') ->label(__('admin.oauth.fields.client_secret')) ->password() ->revealable() ->helperText(__('admin.oauth.hints.client_secret')) ->dehydrated(fn (?string $state): bool => filled($state)) ->dehydrateStateUsing(fn (?string $state): ?string => filled($state) ? Hash::make($state) : null), Forms\Components\Select::make('tenant_id') ->label(__('admin.oauth.fields.tenant')) ->relationship('tenant', 'name') ->searchable() ->preload() ->nullable(), Forms\Components\Textarea::make('redirect_uris') ->label(__('admin.oauth.fields.redirect_uris')) ->rows(4) ->helperText(__('admin.oauth.hints.redirect_uris')) ->formatStateUsing(fn ($state): string => is_array($state) ? implode(PHP_EOL, $state) : (string) $state) ->dehydrateStateUsing(function (?string $state): array { $entries = collect(preg_split('/\r\n|\r|\n/', (string) $state)) ->map(fn ($uri) => trim($uri)) ->filter(); return $entries->values()->all(); }) ->required(), Forms\Components\TagsInput::make('scopes') ->label(__('admin.oauth.fields.scopes')) ->placeholder('tenant:read') ->suggestions([ 'tenant:read', 'tenant:write', 'tenant:admin', ]) ->separator(',') ->required(), Forms\Components\Toggle::make('is_active') ->label(__('admin.oauth.fields.is_active')) ->default(true), Forms\Components\Textarea::make('description') ->label(__('admin.oauth.fields.description')) ->rows(3) ->columnSpanFull(), ])->columns(2); } public static function table(Table $table): Table { return $table ->columns([ Tables\Columns\TextColumn::make('name') ->label(__('admin.oauth.fields.name')) ->searchable() ->sortable(), Tables\Columns\TextColumn::make('client_id') ->label(__('admin.oauth.fields.client_id')) ->copyable() ->sortable(), Tables\Columns\TextColumn::make('tenant.name') ->label(__('admin.oauth.fields.tenant')) ->toggleable(isToggledHiddenByDefault: true) ->sortable(), Tables\Columns\IconColumn::make('is_active') ->label(__('admin.oauth.fields.is_active')) ->boolean() ->color(fn (bool $state): string => $state ? 'success' : 'danger'), Tables\Columns\TextColumn::make('redirect_uris') ->label(__('admin.oauth.fields.redirect_uris')) ->formatStateUsing(fn ($state) => collect(Arr::wrap($state))->implode("\n")) ->limit(50) ->toggleable(), Tables\Columns\TagsColumn::make('scopes') ->label(__('admin.oauth.fields.scopes')) ->separator(', ') ->limit(4), Tables\Columns\TextColumn::make('updated_at') ->label(__('admin.oauth.fields.updated_at')) ->dateTime() ->sortable() ->toggleable(isToggledHiddenByDefault: true), ]) ->filters([ Tables\Filters\TernaryFilter::make('is_active') ->label(__('admin.oauth.filters.is_active')) ->placeholder(__('admin.oauth.filters.any')) ->trueLabel(__('admin.oauth.filters.active')) ->falseLabel(__('admin.oauth.filters.inactive')), Tables\Filters\SelectFilter::make('tenant_id') ->label(__('admin.oauth.fields.tenant')) ->relationship('tenant', 'name') ->searchable(), ]) ->actions([ Tables\Actions\ViewAction::make(), Tables\Actions\EditAction::make(), Tables\Actions\Action::make('regenerate_secret') ->label(__('admin.oauth.actions.regenerate_secret')) ->icon('heroicon-o-arrow-path') ->requiresConfirmation() ->action(function (OAuthClient $record): void { $plainSecret = Str::random(48); $record->forceFill([ 'client_secret' => Hash::make($plainSecret), ])->save(); Notification::make() ->title(__('admin.oauth.notifications.secret_regenerated_title')) ->body(__('admin.oauth.notifications.secret_regenerated_body', [ 'secret' => $plainSecret, ])) ->success() ->send(); }), Tables\Actions\DeleteAction::make() ->before(function (OAuthClient $record): void { RefreshToken::query() ->where('client_id', $record->client_id) ->delete(); }), ]) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ Tables\Actions\DeleteBulkAction::make() ->before(function (Collection $records): void { $records->each(function (OAuthClient $record) { RefreshToken::query() ->where('client_id', $record->client_id) ->delete(); }); }), ]), ]); } public static function getPages(): array { return [ 'index' => Pages\ListOAuthClients::route('/'), 'create' => Pages\CreateOAuthClient::route('/create'), 'view' => Pages\ViewOAuthClient::route('/{record}'), 'edit' => Pages\EditOAuthClient::route('/{record}/edit'), ]; } }