diff --git a/app/Filament/Admin/Resources/ApiKeyResource.php b/app/Filament/Admin/Resources/ApiKeyResource.php index 763eb3df7..0a8c244df 100644 --- a/app/Filament/Admin/Resources/ApiKeyResource.php +++ b/app/Filament/Admin/Resources/ApiKeyResource.php @@ -3,8 +3,20 @@ namespace App\Filament\Admin\Resources; use App\Filament\Admin\Resources\ApiKeyResource\Pages; +use App\Filament\Admin\Resources\UserResource\Pages\EditUser; +use App\Filament\Components\Tables\Columns\DateTimeColumn; use App\Models\ApiKey; +use Filament\Forms\Components\Fieldset; +use Filament\Forms\Components\TagsInput; +use Filament\Forms\Components\Textarea; +use Filament\Forms\Components\ToggleButtons; +use Filament\Forms\Form; use Filament\Resources\Resource; +use Filament\Tables\Actions\CreateAction; +use Filament\Tables\Actions\DeleteAction; +use Filament\Tables\Columns\TextColumn; +use Filament\Tables\Table; +use Illuminate\Database\Eloquent\Builder; class ApiKeyResource extends Resource { @@ -29,7 +41,14 @@ class ApiKeyResource extends Resource public static function getNavigationBadge(): ?string { - return static::getModel()::where('key_type', ApiKey::TYPE_APPLICATION)->count() ?: null; + return (string) static::getEloquentQuery()->count() ?: null; + } + + public static function getEloquentQuery(): Builder + { + $query = parent::getEloquentQuery(); + + return $query->where('key_type', ApiKey::TYPE_APPLICATION); } public static function getNavigationGroup(): ?string @@ -37,6 +56,92 @@ class ApiKeyResource extends Resource return trans('admin/dashboard.advanced'); } + public static function table(Table $table): Table + { + return $table + ->columns([ + TextColumn::make('key') + ->label(trans('admin/apikey.table.key')) + ->icon('tabler-clipboard-text') + ->state(fn (ApiKey $key) => $key->identifier . $key->token) + ->copyable(), + TextColumn::make('memo') + ->label(trans('admin/apikey.table.description')) + ->wrap() + ->limit(50), + DateTimeColumn::make('last_used_at') + ->label(trans('admin/apikey.table.last_used')) + ->placeholder(trans('admin/apikey.table.never_used')) + ->sortable(), + DateTimeColumn::make('created_at') + ->label(trans('admin/apikey.table.created')) + ->sortable(), + TextColumn::make('user.username') + ->label(trans('admin/apikey.table.created_by')) + ->icon('tabler-user') + ->url(fn (ApiKey $apiKey) => auth()->user()->can('update user', $apiKey->user) ? EditUser::getUrl(['record' => $apiKey->user]) : null), + ]) + ->actions([ + DeleteAction::make(), + ]) + ->emptyStateIcon('tabler-key') + ->emptyStateDescription('') + ->emptyStateHeading(trans('admin/apikey.empty_table')) + ->emptyStateActions([ + CreateAction::make(), + ]); + } + + public static function form(Form $form): Form + { + return $form + ->schema([ + Fieldset::make('Permissions') + ->columns([ + 'default' => 1, + 'sm' => 1, + 'md' => 2, + ]) + ->schema( + collect(ApiKey::getPermissionList())->map(fn ($resource) => ToggleButtons::make('permissions_' . $resource) + ->label(str($resource)->replace('_', ' ')->title())->inline() + ->options([ + 0 => 'None', + 1 => 'Read', + 3 => 'Read & Write', + ]) + ->icons([ + 0 => 'tabler-book-off', + 1 => 'tabler-book', + 3 => 'tabler-writing', + ]) + ->colors([ + 0 => 'success', + 1 => 'warning', + 3 => 'danger', + ]) + ->required() + ->columnSpan([ + 'default' => 1, + 'sm' => 1, + 'md' => 1, + ]) + ->default(0), + )->all(), + ), + TagsInput::make('allowed_ips') + ->placeholder(trans('admin/apikey.whitelist_placeholder')) + ->label(trans('admin/apikey.whitelist')) + ->helperText(trans('admin/apikey.whitelist_help')) + ->columnSpanFull(), + Textarea::make('memo') + ->required() + ->label(trans('admin/apikey.description')) + ->helperText(trans('admin/apikey.description_help')) + ->columnSpanFull(), + ]); + } + public static function getPages(): array { return [ diff --git a/app/Filament/Admin/Resources/ApiKeyResource/Pages/CreateApiKey.php b/app/Filament/Admin/Resources/ApiKeyResource/Pages/CreateApiKey.php index 645e90bb8..01a9fd4eb 100644 --- a/app/Filament/Admin/Resources/ApiKeyResource/Pages/CreateApiKey.php +++ b/app/Filament/Admin/Resources/ApiKeyResource/Pages/CreateApiKey.php @@ -4,12 +4,6 @@ namespace App\Filament\Admin\Resources\ApiKeyResource\Pages; use App\Filament\Admin\Resources\ApiKeyResource; use App\Models\ApiKey; -use Filament\Forms\Components\Fieldset; -use Filament\Forms\Components\Hidden; -use Filament\Forms\Components\TagsInput; -use Filament\Forms\Components\Textarea; -use Filament\Forms\Components\ToggleButtons; -use Filament\Forms\Form; use Filament\Resources\Pages\CreateRecord; use Illuminate\Database\Eloquent\Model; @@ -31,75 +25,13 @@ class CreateApiKey extends CreateRecord return []; } - public function form(Form $form): Form - { - return $form - ->schema([ - Hidden::make('identifier')->default(ApiKey::generateTokenIdentifier(ApiKey::TYPE_APPLICATION)), - Hidden::make('token')->default(str_random(ApiKey::KEY_LENGTH)), - - Hidden::make('user_id') - ->default(auth()->user()->id) - ->required(), - - Hidden::make('key_type') - ->inlineLabel() - ->default(ApiKey::TYPE_APPLICATION) - ->required(), - - Fieldset::make('Permissions') - ->columns([ - 'default' => 1, - 'sm' => 1, - 'md' => 2, - ]) - ->schema( - collect(ApiKey::getPermissionList())->map(fn ($resource) => ToggleButtons::make('permissions_' . $resource) - ->label(str($resource)->replace('_', ' ')->title())->inline() - ->options([ - 0 => trans('admin/apikey.permissions.none'), - 1 => trans('admin/apikey.permissions.read'), - // 2 => 'Write', // Makes no sense to have write-only permissions when you can't read it? - 3 => trans('admin/apikey.permissions.read_write'), - ]) - ->icons([ - 0 => 'tabler-book-off', - 1 => 'tabler-book', - 2 => 'tabler-writing', - 3 => 'tabler-writing', - ]) - ->colors([ - 0 => 'success', - 1 => 'warning', - 2 => 'danger', - 3 => 'danger', - ]) - ->required() - ->columnSpan([ - 'default' => 1, - 'sm' => 1, - 'md' => 1, - ]) - ->default(0), - )->all(), - ), - - TagsInput::make('allowed_ips') - ->placeholder('127.0.0.1 or 192.168.1.1') - ->label(trans('admin/apikey.whitelist')) - ->helperText(trans('admin/apikey.whitelist_help')) - ->columnSpanFull(), - - Textarea::make('memo') - ->required() - ->label(trans('admin/apikey.description')) - ->helperText(trans('admin/apikey.description_help')) - ->columnSpanFull(), - ]); - } - protected function handleRecordCreation(array $data): Model { + $data['identifier'] = ApiKey::generateTokenIdentifier(ApiKey::TYPE_APPLICATION); + $data['token'] = str_random(ApiKey::KEY_LENGTH); + $data['user_id'] = auth()->user()->id; + $data['key_type'] = ApiKey::TYPE_APPLICATION; + $permissions = []; foreach (ApiKey::getPermissionList() as $permission) { diff --git a/app/Filament/Admin/Resources/ApiKeyResource/Pages/ListApiKeys.php b/app/Filament/Admin/Resources/ApiKeyResource/Pages/ListApiKeys.php index 8aeae7305..a6814419a 100644 --- a/app/Filament/Admin/Resources/ApiKeyResource/Pages/ListApiKeys.php +++ b/app/Filament/Admin/Resources/ApiKeyResource/Pages/ListApiKeys.php @@ -3,69 +3,18 @@ namespace App\Filament\Admin\Resources\ApiKeyResource\Pages; use App\Filament\Admin\Resources\ApiKeyResource; -use App\Filament\Components\Tables\Columns\DateTimeColumn; use App\Models\ApiKey; -use Filament\Actions; +use Filament\Actions\CreateAction; use Filament\Resources\Pages\ListRecords; -use Filament\Tables\Actions\CreateAction; -use Filament\Tables\Actions\DeleteAction; -use Filament\Tables\Columns\TextColumn; -use Filament\Tables\Table; class ListApiKeys extends ListRecords { protected static string $resource = ApiKeyResource::class; - public function table(Table $table): Table - { - return $table - ->searchable(false) - ->modifyQueryUsing(fn ($query) => $query->where('key_type', ApiKey::TYPE_APPLICATION)) - ->columns([ - TextColumn::make('key') - ->label(trans('admin/apikey.table.key')) - ->copyable() - ->icon('tabler-clipboard-text') - ->state(fn (ApiKey $key) => $key->identifier . $key->token), - - TextColumn::make('memo') - ->label(trans('admin/apikey.table.description')) - ->wrap() - ->limit(50), - - TextColumn::make('identifier') - ->hidden() - ->searchable(), - - DateTimeColumn::make('last_used_at') - ->label(trans('admin/apikey.table.last_used')) - ->placeholder(trans('admin/apikey.table.never_used')) - ->sortable(), - - DateTimeColumn::make('created_at') - ->label(trans('admin/apikey.table.created')) - ->sortable(), - - TextColumn::make('user.username') - ->label(trans('admin/apikey.table.created_by')) - ->url(fn (ApiKey $apiKey): string => route('filament.admin.resources.users.edit', ['record' => $apiKey->user])), - ]) - ->actions([ - DeleteAction::make(), - ]) - ->emptyStateIcon('tabler-key') - ->emptyStateDescription('') - ->emptyStateHeading(trans('admin/apikey.empty_table')) - ->emptyStateActions([ - CreateAction::make('create') - ->button(), - ]); - } - protected function getHeaderActions(): array { return [ - Actions\CreateAction::make() + CreateAction::make() ->hidden(fn () => ApiKey::where('key_type', ApiKey::TYPE_APPLICATION)->count() <= 0), ]; } diff --git a/app/Filament/Admin/Resources/DatabaseHostResource.php b/app/Filament/Admin/Resources/DatabaseHostResource.php index 01643b854..a411e2a39 100644 --- a/app/Filament/Admin/Resources/DatabaseHostResource.php +++ b/app/Filament/Admin/Resources/DatabaseHostResource.php @@ -4,7 +4,18 @@ namespace App\Filament\Admin\Resources; use App\Filament\Admin\Resources\DatabaseHostResource\Pages; use App\Models\DatabaseHost; +use Filament\Forms\Components\Section; +use Filament\Forms\Components\Select; +use Filament\Forms\Components\TextInput; +use Filament\Forms\Form; +use Filament\Forms\Set; use Filament\Resources\Resource; +use Filament\Tables\Actions\CreateAction; +use Filament\Tables\Actions\DeleteBulkAction; +use Filament\Tables\Actions\EditAction; +use Filament\Tables\Actions\ViewAction; +use Filament\Tables\Columns\TextColumn; +use Filament\Tables\Table; class DatabaseHostResource extends Resource { @@ -39,11 +50,112 @@ class DatabaseHostResource extends Resource return trans('admin/dashboard.advanced'); } + public static function table(Table $table): Table + { + return $table + ->columns([ + TextColumn::make('name') + ->label(trans('admin/databasehost.table.name')), + TextColumn::make('host') + ->label(trans('admin/databasehost.table.host')), + TextColumn::make('port') + ->label(trans('admin/databasehost.table.port')), + TextColumn::make('username') + ->label(trans('admin/databasehost.table.username')), + TextColumn::make('databases_count') + ->counts('databases') + ->icon('tabler-database') + ->label(trans('admin/databasehost.databases')), + TextColumn::make('nodes.name') + ->icon('tabler-server-2') + ->badge() + ->placeholder(trans('admin/databasehost.no_nodes')), + ]) + ->checkIfRecordIsSelectableUsing(fn (DatabaseHost $databaseHost) => !$databaseHost->databases_count) + ->actions([ + ViewAction::make() + ->hidden(fn ($record) => static::canEdit($record)), + EditAction::make(), + ]) + ->groupedBulkActions([ + DeleteBulkAction::make() + ->authorize(fn () => auth()->user()->can('delete databasehost')), + ]) + ->emptyStateIcon('tabler-database') + ->emptyStateDescription('') + ->emptyStateHeading(trans('admin/databasehost.no_database_hosts')) + ->emptyStateActions([ + CreateAction::make(), + ]); + } + + public static function form(Form $form): Form + { + return $form + ->schema([ + Section::make() + ->columns([ + 'default' => 2, + 'sm' => 3, + 'md' => 3, + 'lg' => 4, + ]) + ->schema([ + TextInput::make('host') + ->columnSpan(2) + ->label(trans('admin/databasehost.host')) + ->helperText(trans('admin/databasehost.host_help')) + ->required() + ->live(onBlur: true) + ->afterStateUpdated(fn ($state, Set $set) => $set('name', $state)) + ->maxLength(255), + TextInput::make('port') + ->columnSpan(1) + ->label(trans('admin/databasehost.port')) + ->helperText(trans('admin/databasehost.port_help')) + ->required() + ->numeric() + ->default(3306) + ->minValue(0) + ->maxValue(65535), + TextInput::make('max_databases') + ->label(trans('admin/databasehost.max_database')) + ->helpertext(trans('admin/databasehost.max_databases_help')) + ->numeric(), + TextInput::make('name') + ->label(trans('admin/databasehost.display_name')) + ->helperText(trans('admin/databasehost.display_name_help')) + ->required() + ->maxLength(60), + TextInput::make('username') + ->label(trans('admin/databasehost.username')) + ->helperText(trans('admin/databasehost.username_help')) + ->required() + ->maxLength(255), + TextInput::make('password') + ->label(trans('admin/databasehost.password')) + ->helperText(trans('admin/databasehost.password_help')) + ->password() + ->revealable() + ->maxLength(255) + ->required(fn ($operation) => $operation === 'create'), + Select::make('node_ids') + ->multiple() + ->searchable() + ->preload() + ->helperText(trans('admin/databasehost.linked_nodes_help')) + ->label(trans('admin/databasehost.linked_nodes')) + ->relationship('nodes', 'name'), + ]), + ]); + } + public static function getPages(): array { return [ 'index' => Pages\ListDatabaseHosts::route('/'), 'create' => Pages\CreateDatabaseHost::route('/create'), + 'view' => Pages\ViewDatabaseHost::route('/{record}'), 'edit' => Pages\EditDatabaseHost::route('/{record}/edit'), ]; } diff --git a/app/Filament/Admin/Resources/DatabaseHostResource/Pages/CreateDatabaseHost.php b/app/Filament/Admin/Resources/DatabaseHostResource/Pages/CreateDatabaseHost.php index a33ce7900..eead5cf3b 100644 --- a/app/Filament/Admin/Resources/DatabaseHostResource/Pages/CreateDatabaseHost.php +++ b/app/Filament/Admin/Resources/DatabaseHostResource/Pages/CreateDatabaseHost.php @@ -4,11 +4,6 @@ namespace App\Filament\Admin\Resources\DatabaseHostResource\Pages; use App\Filament\Admin\Resources\DatabaseHostResource; use App\Services\Databases\Hosts\HostCreationService; -use Filament\Forms; -use Filament\Forms\Components\Section; -use Filament\Forms\Components\Select; -use Filament\Forms\Components\TextInput; -use Filament\Forms\Form; use Filament\Notifications\Notification; use Filament\Resources\Pages\CreateRecord; use Filament\Support\Exceptions\Halt; @@ -28,66 +23,6 @@ class CreateDatabaseHost extends CreateRecord $this->service = $service; } - public function form(Form $form): Form - { - return $form - ->schema([ - Section::make() - ->columns([ - 'default' => 2, - 'sm' => 3, - 'md' => 3, - 'lg' => 4, - ]) - ->schema([ - TextInput::make('host') - ->columnSpan(2) - ->label(trans('admin/databasehost.host')) - ->helperText(trans('admin/databasehost.host_help')) - ->required() - ->live(onBlur: true) - ->afterStateUpdated(fn ($state, Forms\Set $set) => $set('name', $state)) - ->maxLength(255), - TextInput::make('port') - ->columnSpan(1) - ->label(trans('admin/databasehost.port')) - ->helperText(trans('admin/databasehost.port_help')) - ->required() - ->numeric() - ->minValue(0) - ->maxValue(65535), - TextInput::make('max_databases') - ->label(trans('admin/databasehost.max_database')) - ->helpertext(trans('admin/databasehost.max_databases_help')) - ->numeric(), - TextInput::make('name') - ->label(trans('admin/databasehost.display_name')) - ->helperText(trans('admin/databasehost.display_name_help')) - ->required() - ->maxLength(60), - TextInput::make('username') - ->label(trans('admin/databasehost.username')) - ->helperText(trans('admin/databasehost.username_help')) - ->required() - ->maxLength(255), - TextInput::make('password') - ->label(trans('admin/databasehost.password')) - ->helperText(trans('admin/databasehost.password_help')) - ->password() - ->revealable() - ->required() - ->maxLength(255), - Select::make('nodes') - ->multiple() - ->searchable() - ->preload() - ->helperText(trans('admin/databasehost.linked_nodes_help')) - ->label(trans('admin/databasehost.linked_nodes')) - ->relationship('nodes', 'name'), - ]), - ]); - } - protected function getHeaderActions(): array { return [ @@ -106,7 +41,7 @@ class CreateDatabaseHost extends CreateRecord return $this->service->handle($data); } catch (PDOException $exception) { Notification::make() - ->title(trans('admin/databasehost.error')) + ->title('Error connecting to database host') ->body($exception->getMessage()) ->color('danger') ->icon('tabler-database') diff --git a/app/Filament/Admin/Resources/DatabaseHostResource/Pages/EditDatabaseHost.php b/app/Filament/Admin/Resources/DatabaseHostResource/Pages/EditDatabaseHost.php index 0c806ef71..b8e3de281 100644 --- a/app/Filament/Admin/Resources/DatabaseHostResource/Pages/EditDatabaseHost.php +++ b/app/Filament/Admin/Resources/DatabaseHostResource/Pages/EditDatabaseHost.php @@ -6,12 +6,7 @@ use App\Filament\Admin\Resources\DatabaseHostResource; use App\Filament\Admin\Resources\DatabaseHostResource\RelationManagers\DatabasesRelationManager; use App\Models\DatabaseHost; use App\Services\Databases\Hosts\HostUpdateService; -use Filament\Actions; -use Filament\Forms; -use Filament\Forms\Components\Section; -use Filament\Forms\Components\Select; -use Filament\Forms\Components\TextInput; -use Filament\Forms\Form; +use Filament\Actions\DeleteAction; use Filament\Notifications\Notification; use Filament\Resources\Pages\EditRecord; use Filament\Support\Exceptions\Halt; @@ -29,70 +24,11 @@ class EditDatabaseHost extends EditRecord $this->hostUpdateService = $hostUpdateService; } - public function form(Form $form): Form - { - return $form - ->schema([ - Section::make() - ->columns([ - 'default' => 2, - 'sm' => 3, - 'md' => 3, - 'lg' => 4, - ]) - ->schema([ - TextInput::make('host') - ->columnSpan(2) - ->label(trans('admin/databasehost.host')) - ->helperText(trans('admin/databasehost.host_help')) - ->required() - ->live(onBlur: true) - ->afterStateUpdated(fn ($state, Forms\Set $set) => $set('name', $state)) - ->maxLength(255), - TextInput::make('port') - ->columnSpan(1) - ->label(trans('admin/databasehost.port')) - ->helperText(trans('admin/databasehost.port_help')) - ->required() - ->numeric() - ->minValue(0) - ->maxValue(65535), - TextInput::make('max_databases') - ->label(trans('admin/databasehost.max_database')) - ->helpertext(trans('admin/databasehost.max_databases_help')) - ->numeric(), - TextInput::make('name') - ->label(trans('admin/databasehost.display_name')) - ->helperText(trans('admin/databasehost.display_name_help')) - ->required() - ->maxLength(60), - TextInput::make('username') - ->label(trans('admin/databasehost.username')) - ->helperText(trans('admin/databasehost.username_help')) - ->required() - ->maxLength(255), - TextInput::make('password') - ->label(trans('admin/databasehost.password')) - ->helperText(trans('admin/databasehost.password_help')) - ->password() - ->revealable() - ->maxLength(255), - Select::make('nodes') - ->multiple() - ->searchable() - ->preload() - ->helperText(trans('admin/databasehost.linked_nodes_help')) - ->label(trans('admin/databasehost.linked_nodes')) - ->relationship('nodes', 'name'), - ]), - ]); - } - protected function getHeaderActions(): array { return [ - Actions\DeleteAction::make() - ->label(fn (DatabaseHost $databaseHost) => $databaseHost->databases()->count() > 0 ? trans('admin/databasehost.delete_help') : trans('filament-actions::delete.single.modal.actions.delete.label')) + DeleteAction::make() + ->label(fn (DatabaseHost $databaseHost) => $databaseHost->databases()->count() > 0 ? 'Database Host Has Databases' : trans('filament-actions::delete.single.label')) ->disabled(fn (DatabaseHost $databaseHost) => $databaseHost->databases()->count() > 0), $this->getSaveFormAction()->formId('form'), ]; @@ -124,7 +60,7 @@ class EditDatabaseHost extends EditRecord return $this->hostUpdateService->handle($record, $data); } catch (PDOException $exception) { Notification::make() - ->title(trans('admin/databasehost.connection_error')) + ->title('Error connecting to database host') ->body($exception->getMessage()) ->color('danger') ->icon('tabler-database') diff --git a/app/Filament/Admin/Resources/DatabaseHostResource/Pages/ListDatabaseHosts.php b/app/Filament/Admin/Resources/DatabaseHostResource/Pages/ListDatabaseHosts.php index accce60c3..1c0f312d6 100644 --- a/app/Filament/Admin/Resources/DatabaseHostResource/Pages/ListDatabaseHosts.php +++ b/app/Filament/Admin/Resources/DatabaseHostResource/Pages/ListDatabaseHosts.php @@ -4,69 +4,17 @@ namespace App\Filament\Admin\Resources\DatabaseHostResource\Pages; use App\Filament\Admin\Resources\DatabaseHostResource; use App\Models\DatabaseHost; -use Filament\Actions; +use Filament\Actions\CreateAction; use Filament\Resources\Pages\ListRecords; -use Filament\Tables\Actions\BulkActionGroup; -use Filament\Tables\Actions\CreateAction; -use Filament\Tables\Actions\DeleteBulkAction; -use Filament\Tables\Actions\EditAction; -use Filament\Tables\Columns\TextColumn; -use Filament\Tables\Table; class ListDatabaseHosts extends ListRecords { protected static string $resource = DatabaseHostResource::class; - public function table(Table $table): Table - { - return $table - ->searchable(false) - ->columns([ - TextColumn::make('name') - ->label(trans('admin/databasehost.table.name')) - ->searchable(), - TextColumn::make('host') - ->label(trans('admin/databasehost.table.host')) - ->searchable(), - TextColumn::make('port') - ->label(trans('admin/databasehost.table.port')) - ->sortable(), - TextColumn::make('username') - ->label(trans('admin/databasehost.table.username')) - ->searchable(), - TextColumn::make('databases_count') - ->counts('databases') - ->icon('tabler-database') - ->label(trans('admin/databasehost.databases')), - TextColumn::make('nodes.name') - ->icon('tabler-server-2') - ->badge() - ->placeholder(trans('admin/databasehost.no_nodes')) - ->sortable(), - ]) - ->checkIfRecordIsSelectableUsing(fn (DatabaseHost $databaseHost) => !$databaseHost->databases_count) - ->actions([ - EditAction::make(), - ]) - ->bulkActions([ - BulkActionGroup::make([ - DeleteBulkAction::make() - ->authorize(fn () => auth()->user()->can('delete databasehost')), - ]), - ]) - ->emptyStateIcon('tabler-database') - ->emptyStateDescription('') - ->emptyStateHeading(trans('admin/databasehost.no_database_hosts')) - ->emptyStateActions([ - CreateAction::make('create') - ->button(), - ]); - } - protected function getHeaderActions(): array { return [ - Actions\CreateAction::make('create') + CreateAction::make() ->hidden(fn () => DatabaseHost::count() <= 0), ]; } diff --git a/app/Filament/Admin/Resources/DatabaseHostResource/Pages/ViewDatabaseHost.php b/app/Filament/Admin/Resources/DatabaseHostResource/Pages/ViewDatabaseHost.php new file mode 100644 index 000000000..a32a89438 --- /dev/null +++ b/app/Filament/Admin/Resources/DatabaseHostResource/Pages/ViewDatabaseHost.php @@ -0,0 +1,31 @@ +getRecord(), static::class)) { + return [ + DatabasesRelationManager::class, + ]; + } + + return []; + } +} diff --git a/app/Filament/Admin/Resources/MountResource.php b/app/Filament/Admin/Resources/MountResource.php index e9d6a35e9..7ff3f3623 100644 --- a/app/Filament/Admin/Resources/MountResource.php +++ b/app/Filament/Admin/Resources/MountResource.php @@ -4,7 +4,20 @@ namespace App\Filament\Admin\Resources; use App\Filament\Admin\Resources\MountResource\Pages; use App\Models\Mount; +use Filament\Forms\Components\Group; +use Filament\Forms\Components\Section; +use Filament\Forms\Components\Select; +use Filament\Forms\Components\Textarea; +use Filament\Forms\Components\TextInput; +use Filament\Forms\Components\ToggleButtons; +use Filament\Forms\Form; use Filament\Resources\Resource; +use Filament\Tables\Actions\CreateAction; +use Filament\Tables\Actions\DeleteBulkAction; +use Filament\Tables\Actions\EditAction; +use Filament\Tables\Actions\ViewAction; +use Filament\Tables\Columns\TextColumn; +use Filament\Tables\Table; class MountResource extends Resource { @@ -39,11 +52,140 @@ class MountResource extends Resource return trans('admin/dashboard.advanced'); } + public static function table(Table $table): Table + { + return $table + ->columns([ + TextColumn::make('name') + ->label(trans('admin/mount.table.name')) + ->description(fn (Mount $mount) => "$mount->source -> $mount->target") + ->sortable(), + TextColumn::make('eggs.name') + ->icon('tabler-eggs') + ->label(trans('admin/mount.eggs')) + ->badge() + ->placeholder(trans('admin/mount.table.all_eggs')), + TextColumn::make('nodes.name') + ->icon('tabler-server-2') + ->label(trans('admin/mount.nodes')) + ->badge() + ->placeholder(trans('admin/mount.table.all_nodes')), + TextColumn::make('read_only') + ->label(trans('admin/mount.table.read_only')) + ->badge() + ->icon(fn ($state) => $state ? 'tabler-writing-off' : 'tabler-writing') + ->color(fn ($state) => $state ? 'success' : 'warning') + ->formatStateUsing(fn ($state) => $state ? trans('admin/mount.toggles.read_only') : trans('admin/mount.toggles.writeable')), + ]) + ->actions([ + ViewAction::make() + ->hidden(fn ($record) => static::canEdit($record)), + EditAction::make(), + ]) + ->groupedBulkActions([ + DeleteBulkAction::make() + ->authorize(fn () => auth()->user()->can('delete mount')), + ]) + ->emptyStateIcon('tabler-layers-linked') + ->emptyStateDescription('') + ->emptyStateHeading(trans('admin/mount.no_mounts')) + ->emptyStateActions([ + CreateAction::make(), + ]); + } + + public static function form(Form $form): Form + { + return $form + ->schema([ + Section::make()->schema([ + TextInput::make('name') + ->label(trans('admin/mount.name')) + ->required() + ->helperText(trans('admin/mount.name_help')) + ->maxLength(64), + ToggleButtons::make('read_only') + ->label(trans('admin/mount.read_only')) + ->helperText(trans('admin/mount.read_only_help')) + ->options([ + false => trans('admin/mount.toggles.writable'), + true => trans('admin/mount.toggles.read_only'), + ]) + ->icons([ + false => 'tabler-writing', + true => 'tabler-writing-off', + ]) + ->colors([ + false => 'warning', + true => 'success', + ]) + ->inline() + ->default(false) + ->required(), + TextInput::make('source') + ->label(trans('admin/mount.source')) + ->required() + ->helperText(trans('admin/mount.source_help')) + ->maxLength(255), + TextInput::make('target') + ->label(trans('admin/mount.target')) + ->required() + ->helperText(trans('admin/mount.target_help')) + ->maxLength(255), + ToggleButtons::make('user_mountable') + ->hidden() + ->label('User mountable?') + ->options([ + false => 'No', + true => 'Yes', + ]) + ->icons([ + false => 'tabler-user-cancel', + true => 'tabler-user-bolt', + ]) + ->colors([ + false => 'success', + true => 'warning', + ]) + ->default(false) + ->inline() + ->required(), + Textarea::make('description') + ->label(trans('admin/mount.description')) + ->helperText(trans('admin/mount.description_help')) + ->columnSpanFull(), + ])->columnSpan(1)->columns([ + 'default' => 1, + 'lg' => 2, + ]), + Group::make()->schema([ + Section::make()->schema([ + Select::make('eggs')->multiple() + ->label(trans('admin/mount.eggs')) + ->relationship('eggs', 'name') + ->preload(), + Select::make('nodes')->multiple() + ->label(trans('admin/mount.nodes')) + ->relationship('nodes', 'name') + ->searchable(['name', 'fqdn']) + ->preload(), + ]), + ])->columns([ + 'default' => 1, + 'lg' => 2, + ]), + ])->columns([ + 'default' => 1, + 'lg' => 2, + ]); + } + public static function getPages(): array { return [ 'index' => Pages\ListMounts::route('/'), 'create' => Pages\CreateMount::route('/create'), + 'view' => Pages\ViewMount::route('/{record}'), 'edit' => Pages\EditMount::route('/{record}/edit'), ]; } diff --git a/app/Filament/Admin/Resources/MountResource/Pages/CreateMount.php b/app/Filament/Admin/Resources/MountResource/Pages/CreateMount.php index 026c5db5e..693ba4a0e 100644 --- a/app/Filament/Admin/Resources/MountResource/Pages/CreateMount.php +++ b/app/Filament/Admin/Resources/MountResource/Pages/CreateMount.php @@ -3,14 +3,6 @@ namespace App\Filament\Admin\Resources\MountResource\Pages; use App\Filament\Admin\Resources\MountResource; -use Filament\Forms\Components\Group; -use Filament\Forms\Components\Hidden; -use Filament\Forms\Components\Section; -use Filament\Forms\Components\Select; -use Filament\Forms\Components\Textarea; -use Filament\Forms\Components\TextInput; -use Filament\Forms\Components\ToggleButtons; -use Filament\Forms\Form; use Filament\Resources\Pages\CreateRecord; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Str; @@ -33,93 +25,6 @@ class CreateMount extends CreateRecord return []; } - public function form(Form $form): Form - { - return $form - ->schema([ - Section::make()->schema([ - TextInput::make('name') - ->label(trans('admin/mount.name')) - ->required() - ->helperText(trans('admin/mount.name_help')) - ->maxLength(64), - ToggleButtons::make('read_only') - ->label(trans('admin/mount.read_only')) - ->helperText(trans('admin/mount.read_only_help')) - ->options([ - false => trans('admin/mount.toggles.writable'), - true => trans('admin/mount.toggles.read_only'), - ]) - ->icons([ - false => 'tabler-writing', - true => 'tabler-writing-off', - ]) - ->colors([ - false => 'warning', - true => 'success', - ]) - ->inline() - ->default(false) - ->required(), - TextInput::make('source') - ->label(trans('admin/mount.source')) - ->required() - ->helperText(trans('admin/mount.source_help')) - ->maxLength(255), - TextInput::make('target') - ->label(trans('admin/mount.target')) - ->required() - ->helperText(trans('admin/mount.target_help')) - ->maxLength(255), - ToggleButtons::make('user_mountable') - ->hidden() - ->label('User mountable?') - ->options([ - false => 'No', - true => 'Yes', - ]) - ->icons([ - false => 'tabler-user-cancel', - true => 'tabler-user-bolt', - ]) - ->colors([ - false => 'success', - true => 'warning', - ]) - ->default(false) - ->inline() - ->required(), - Textarea::make('description') - ->label(trans('admin/mount.description')) - ->helperText(trans('admin/mount.description_help')) - ->columnSpanFull(), - Hidden::make('user_mountable')->default(1), - ])->columnSpan(1)->columns([ - 'default' => 1, - 'lg' => 2, - ]), - Group::make()->schema([ - Section::make()->schema([ - Select::make('eggs')->multiple() - ->label(trans('admin/mount.eggs')) - ->relationship('eggs', 'name') - ->preload(), - Select::make('nodes')->multiple() - ->label(trans('admin/mount.nodes')) - ->relationship('nodes', 'name') - ->searchable(['name', 'fqdn']) - ->preload(), - ]), - ])->columns([ - 'default' => 1, - 'lg' => 2, - ]), - ])->columns([ - 'default' => 1, - 'lg' => 2, - ]); - } - protected function handleRecordCreation(array $data): Model { $data['uuid'] ??= Str::uuid()->toString(); diff --git a/app/Filament/Admin/Resources/MountResource/Pages/EditMount.php b/app/Filament/Admin/Resources/MountResource/Pages/EditMount.php index bf359f775..d669bea59 100644 --- a/app/Filament/Admin/Resources/MountResource/Pages/EditMount.php +++ b/app/Filament/Admin/Resources/MountResource/Pages/EditMount.php @@ -3,108 +3,17 @@ namespace App\Filament\Admin\Resources\MountResource\Pages; use App\Filament\Admin\Resources\MountResource; -use Filament\Actions; -use Filament\Forms\Components\Group; -use Filament\Forms\Components\Section; -use Filament\Forms\Components\Select; -use Filament\Forms\Components\Textarea; -use Filament\Forms\Components\TextInput; -use Filament\Forms\Components\ToggleButtons; -use Filament\Forms\Form; +use Filament\Actions\DeleteAction; use Filament\Resources\Pages\EditRecord; class EditMount extends EditRecord { protected static string $resource = MountResource::class; - public function form(Form $form): Form - { - return $form - ->schema([ - Section::make()->schema([ - TextInput::make('name') - ->label(trans('admin/mount.name')) - ->required() - ->helperText(trans('admin/mount.name_help')) - ->maxLength(64), - ToggleButtons::make('read_only') - ->label(trans('admin/mount.read_only')) - ->helperText(trans('admin/mount.read_only_help')) - ->options([ - false => trans('admin/mount.toggles.writable'), - true => trans('admin/mount.toggles.read_only'), - ]) - ->icons([ - false => 'tabler-writing', - true => 'tabler-writing-off', - ]) - ->colors([ - false => 'warning', - true => 'success', - ]) - ->inline() - ->default(false) - ->required(), - TextInput::make('source') - ->label(trans('admin/mount.source')) - ->required() - ->helperText(trans('admin/mount.source_help')) - ->maxLength(255), - TextInput::make('target') - ->label(trans('admin/mount.target')) - ->required() - ->helperText(trans('admin/mount.target_help')) - ->maxLength(255), - ToggleButtons::make('user_mountable') - ->hidden() - ->label('User mountable?') - ->options([ - false => 'No', - true => 'Yes', - ]) - ->icons([ - false => 'tabler-user-cancel', - true => 'tabler-user-bolt', - ]) - ->colors([ - false => 'success', - true => 'warning', - ]) - ->default(false) - ->inline() - ->required(), - Textarea::make('description') - ->label(trans('admin/mount.description')) - ->helperText(trans('admin/mount.description_help')) - ->columnSpanFull(), - ])->columnSpan(1)->columns([ - 'default' => 1, - 'lg' => 2, - ]), - Group::make()->schema([ - Section::make()->schema([ - Select::make('eggs')->multiple() - ->relationship('eggs', 'name') - ->preload(), - Select::make('nodes')->multiple() - ->relationship('nodes', 'name') - ->searchable(['name', 'fqdn']) - ->preload(), - ]), - ])->columns([ - 'default' => 1, - 'lg' => 2, - ]), - ])->columns([ - 'default' => 1, - 'lg' => 2, - ]); - } - protected function getHeaderActions(): array { return [ - Actions\DeleteAction::make(), + DeleteAction::make(), $this->getSaveFormAction()->formId('form'), ]; } diff --git a/app/Filament/Admin/Resources/MountResource/Pages/ListMounts.php b/app/Filament/Admin/Resources/MountResource/Pages/ListMounts.php index 435687d7b..41b9e8b65 100644 --- a/app/Filament/Admin/Resources/MountResource/Pages/ListMounts.php +++ b/app/Filament/Admin/Resources/MountResource/Pages/ListMounts.php @@ -4,62 +4,17 @@ namespace App\Filament\Admin\Resources\MountResource\Pages; use App\Filament\Admin\Resources\MountResource; use App\Models\Mount; -use Filament\Actions; +use Filament\Actions\CreateAction; use Filament\Resources\Pages\ListRecords; -use Filament\Tables\Actions\BulkActionGroup; -use Filament\Tables\Actions\CreateAction; -use Filament\Tables\Actions\DeleteBulkAction; -use Filament\Tables\Actions\EditAction; -use Filament\Tables\Columns\IconColumn; -use Filament\Tables\Columns\TextColumn; -use Filament\Tables\Table; class ListMounts extends ListRecords { protected static string $resource = MountResource::class; - public function table(Table $table): Table - { - return $table - ->searchable(false) - ->columns([ - TextColumn::make('name') - ->label(trans('admin/mount.table.name')) - ->searchable(), - TextColumn::make('source') - ->label(trans('admin/mount.table.source')) - ->searchable(), - TextColumn::make('target') - ->label(trans('admin/mount.table.target')) - ->searchable(), - IconColumn::make('read_only') - ->label(trans('admin/mount.table.read_only')) - ->icon(fn (bool $state) => $state ? 'tabler-circle-check-filled' : 'tabler-circle-x-filled') - ->color(fn (bool $state) => $state ? 'success' : 'danger') - ->sortable(), - ]) - ->actions([ - EditAction::make(), - ]) - ->bulkActions([ - BulkActionGroup::make([ - DeleteBulkAction::make() - ->authorize(fn () => auth()->user()->can('delete mount')), - ]), - ]) - ->emptyStateIcon('tabler-layers-linked') - ->emptyStateDescription('') - ->emptyStateHeading(trans('admin/mount.no_mounts')) - ->emptyStateActions([ - CreateAction::make('create') - ->button(), - ]); - } - protected function getHeaderActions(): array { return [ - Actions\CreateAction::make() + CreateAction::make() ->hidden(fn () => Mount::count() <= 0), ]; } diff --git a/app/Filament/Admin/Resources/MountResource/Pages/ViewMount.php b/app/Filament/Admin/Resources/MountResource/Pages/ViewMount.php new file mode 100644 index 000000000..e7fe423c5 --- /dev/null +++ b/app/Filament/Admin/Resources/MountResource/Pages/ViewMount.php @@ -0,0 +1,19 @@ +emptyStateDescription('') ->emptyStateHeading(trans('admin/node.no_nodes')) ->emptyStateActions([ - CreateAction::make('create') - ->button(), + CreateAction::make(), ]); } diff --git a/app/Filament/Admin/Resources/RoleResource.php b/app/Filament/Admin/Resources/RoleResource.php index ea53adc1c..6df0f9187 100644 --- a/app/Filament/Admin/Resources/RoleResource.php +++ b/app/Filament/Admin/Resources/RoleResource.php @@ -16,6 +16,12 @@ use Filament\Forms\Components\TextInput; use Filament\Forms\Form; use Filament\Forms\Get; use Filament\Resources\Resource; +use Filament\Tables\Actions\CreateAction; +use Filament\Tables\Actions\DeleteBulkAction; +use Filament\Tables\Actions\EditAction; +use Filament\Tables\Actions\ViewAction; +use Filament\Tables\Columns\TextColumn; +use Filament\Tables\Table; use Illuminate\Support\Str; class RoleResource extends Resource @@ -51,6 +57,42 @@ class RoleResource extends Resource return static::getModel()::count() ?: null; } + public static function table(Table $table): Table + { + return $table + ->columns([ + TextColumn::make('name') + ->label(trans('admin/role.name')) + ->sortable() + ->searchable(), + TextColumn::make('permissions_count') + ->label(trans('admin/role.permissions')) + ->badge() + ->counts('permissions') + ->formatStateUsing(fn (Role $role, $state) => $role->isRootAdmin() ? trans('admin/role.all') : $state), + TextColumn::make('users_count') + ->label(trans('admin/role.users')) + ->counts('users') + ->icon('tabler-users'), + ]) + ->actions([ + ViewAction::make() + ->hidden(fn ($record) => static::canEdit($record)), + EditAction::make(), + ]) + ->checkIfRecordIsSelectableUsing(fn (Role $role) => !$role->isRootAdmin() && $role->users_count <= 0) + ->groupedBulkActions([ + DeleteBulkAction::make() + ->authorize(fn () => auth()->user()->can('delete role')), + ]) + ->emptyStateIcon('tabler-users-group') + ->emptyStateDescription('') + ->emptyStateHeading(trans('admin/role.no_roles')) + ->emptyStateActions([ + CreateAction::make(), + ]); + } + public static function form(Form $form): Form { $permissions = []; @@ -162,6 +204,7 @@ class RoleResource extends Resource return [ 'index' => Pages\ListRoles::route('/'), 'create' => Pages\CreateRole::route('/create'), + 'view' => Pages\ViewRole::route('/{record}'), 'edit' => Pages\EditRole::route('/{record}/edit'), ]; } diff --git a/app/Filament/Admin/Resources/RoleResource/Pages/EditRole.php b/app/Filament/Admin/Resources/RoleResource/Pages/EditRole.php index 2c4389ebb..90ce7fba1 100644 --- a/app/Filament/Admin/Resources/RoleResource/Pages/EditRole.php +++ b/app/Filament/Admin/Resources/RoleResource/Pages/EditRole.php @@ -50,7 +50,13 @@ class EditRole extends EditRecord return [ DeleteAction::make() ->disabled(fn (Role $role) => $role->isRootAdmin() || $role->users_count >= 1) - ->label(fn (Role $role) => $role->isRootAdmin() ? trans('admin/role.root_admin_delete') : ($role->users_count >= 1 ? trans('admin/role.in_use') : trans('filament-actions::delete.single.label'))), + ->label(fn (Role $role) => $role->isRootAdmin() ? 'Can\'t delete Root Admin' : ($role->users_count >= 1 ? 'In Use' : 'Delete')), + $this->getSaveFormAction()->formId('form'), ]; } + + protected function getFormActions(): array + { + return []; + } } diff --git a/app/Filament/Admin/Resources/RoleResource/Pages/ListRoles.php b/app/Filament/Admin/Resources/RoleResource/Pages/ListRoles.php index a9c4a26ba..5120509fa 100644 --- a/app/Filament/Admin/Resources/RoleResource/Pages/ListRoles.php +++ b/app/Filament/Admin/Resources/RoleResource/Pages/ListRoles.php @@ -3,53 +3,13 @@ namespace App\Filament\Admin\Resources\RoleResource\Pages; use App\Filament\Admin\Resources\RoleResource; -use App\Models\Role; use Filament\Actions\CreateAction; use Filament\Resources\Pages\ListRecords; -use Filament\Tables\Actions\BulkActionGroup; -use Filament\Tables\Actions\DeleteBulkAction; -use Filament\Tables\Actions\EditAction; -use Filament\Tables\Columns\TextColumn; -use Filament\Tables\Table; class ListRoles extends ListRecords { protected static string $resource = RoleResource::class; - public function table(Table $table): Table - { - return $table - ->columns([ - TextColumn::make('name') - ->label(trans('admin/role.name')) - ->sortable() - ->searchable(), - TextColumn::make('guard_name') - ->hidden() - ->sortable() - ->searchable(), - TextColumn::make('permissions_count') - ->label(trans('admin/role.permissions')) - ->badge() - ->counts('permissions') - ->formatStateUsing(fn (Role $role, $state) => $role->isRootAdmin() ? trans('admin/role.all') : $state), - TextColumn::make('users_count') - ->label(trans('admin/role.users')) - ->counts('users') - ->icon('tabler-users'), - ]) - ->actions([ - EditAction::make(), - ]) - ->checkIfRecordIsSelectableUsing(fn (Role $role) => !$role->isRootAdmin() && $role->users_count <= 0) - ->bulkActions([ - BulkActionGroup::make([ - DeleteBulkAction::make() - ->authorize(fn () => auth()->user()->can('delete role')), - ]), - ]); - } - protected function getHeaderActions(): array { return [ diff --git a/app/Filament/Admin/Resources/RoleResource/Pages/ViewRole.php b/app/Filament/Admin/Resources/RoleResource/Pages/ViewRole.php new file mode 100644 index 000000000..d8a41e058 --- /dev/null +++ b/app/Filament/Admin/Resources/RoleResource/Pages/ViewRole.php @@ -0,0 +1,19 @@ +emptyStateDescription('') ->emptyStateHeading(trans('admin/server.no_servers')) ->emptyStateActions([ - CreateAction::make('create') - ->button(), + CreateAction::make(), ]); } diff --git a/app/Filament/Admin/Resources/UserResource.php b/app/Filament/Admin/Resources/UserResource.php index e9eb0a781..9c7ba58bf 100644 --- a/app/Filament/Admin/Resources/UserResource.php +++ b/app/Filament/Admin/Resources/UserResource.php @@ -4,8 +4,19 @@ namespace App\Filament\Admin\Resources; use App\Filament\Admin\Resources\UserResource\Pages; use App\Filament\Admin\Resources\UserResource\RelationManagers; +use App\Models\Role; use App\Models\User; +use Filament\Forms\Components\CheckboxList; +use Filament\Forms\Components\TextInput; +use Filament\Forms\Form; use Filament\Resources\Resource; +use Filament\Tables\Actions\DeleteBulkAction; +use Filament\Tables\Actions\EditAction; +use Filament\Tables\Actions\ViewAction; +use Filament\Tables\Columns\IconColumn; +use Filament\Tables\Columns\ImageColumn; +use Filament\Tables\Columns\TextColumn; +use Filament\Tables\Table; class UserResource extends Resource { @@ -40,6 +51,85 @@ class UserResource extends Resource return static::getModel()::count() ?: null; } + public static function table(Table $table): Table + { + return $table + ->columns([ + ImageColumn::make('picture') + ->visibleFrom('lg') + ->label('') + ->extraImgAttributes(['class' => 'rounded-full']) + ->defaultImageUrl(fn (User $user) => 'https://gravatar.com/avatar/' . md5(strtolower($user->email))), + TextColumn::make('username') + ->label(trans('admin/user.username')), + TextColumn::make('email') + ->label(trans('admin/user.email')) + ->icon('tabler-mail'), + IconColumn::make('use_totp') + ->label('2FA') + ->visibleFrom('lg') + ->icon(fn (User $user) => $user->use_totp ? 'tabler-lock' : 'tabler-lock-open-off') + ->boolean(), + TextColumn::make('roles.name') + ->label(trans('admin/user.roles')) + ->badge() + ->icon('tabler-users-group') + ->placeholder(trans('admin/user.no_roles')), + TextColumn::make('servers_count') + ->counts('servers') + ->icon('tabler-server') + ->label(trans('admin/user.servers')), + TextColumn::make('subusers_count') + ->visibleFrom('sm') + ->label(trans('admin/user.subusers')) + ->counts('subusers') + ->icon('tabler-users'), + ]) + ->actions([ + ViewAction::make() + ->hidden(fn ($record) => static::canEdit($record)), + EditAction::make(), + ]) + ->checkIfRecordIsSelectableUsing(fn (User $user) => auth()->user()->id !== $user->id && !$user->servers_count) + ->groupedBulkActions([ + DeleteBulkAction::make() + ->authorize(fn () => auth()->user()->can('delete user')), + ]); + } + + public static function form(Form $form): Form + { + return $form + ->columns(['default' => 1, 'lg' => 3]) + ->schema([ + TextInput::make('username') + ->label(trans('admin/user.username')) + ->alphaNum() + ->required() + ->unique() + ->minLength(3) + ->maxLength(255), + TextInput::make('email') + ->label(trans('admin/user.email')) + ->email() + ->required() + ->unique() + ->maxLength(255), + TextInput::make('password') + ->label(trans('admin/user.password')) + ->hintIcon(fn ($operation) => $operation === 'create' ? 'tabler-question-mark' : null) + ->hintIconTooltip(fn ($operation) => $operation === 'create' ? trans('admin/user.password_help') : null) + ->password(), + CheckboxList::make('roles') + ->disableOptionWhen(fn (string $value): bool => $value == Role::getRootAdmin()->id) + ->relationship('roles', 'name') + ->dehydrated() + ->label(trans('admin/user.admin_roles')) + ->columnSpanFull() + ->bulkToggleable(false), + ]); + } + public static function getRelations(): array { return [ @@ -52,6 +142,7 @@ class UserResource extends Resource return [ 'index' => Pages\ListUsers::route('/'), 'create' => Pages\CreateUser::route('/create'), + 'view' => Pages\ViewUser::route('/{record}'), 'edit' => Pages\EditUser::route('/{record}/edit'), ]; } diff --git a/app/Filament/Admin/Resources/UserResource/Pages/CreateUser.php b/app/Filament/Admin/Resources/UserResource/Pages/CreateUser.php index 99322920f..44fdbdb1f 100644 --- a/app/Filament/Admin/Resources/UserResource/Pages/CreateUser.php +++ b/app/Filament/Admin/Resources/UserResource/Pages/CreateUser.php @@ -5,9 +5,6 @@ namespace App\Filament\Admin\Resources\UserResource\Pages; use App\Filament\Admin\Resources\UserResource; use App\Models\Role; use App\Services\Users\UserCreationService; -use Filament\Forms\Components\CheckboxList; -use Filament\Forms\Components\TextInput; -use Filament\Forms\Form; use Filament\Resources\Pages\CreateRecord; use Illuminate\Database\Eloquent\Model; @@ -24,39 +21,6 @@ class CreateUser extends CreateRecord $this->service = $service; } - public function form(Form $form): Form - { - return $form - ->columns(['default' => 1, 'lg' => 3]) - ->schema([ - TextInput::make('username') - ->label(trans('admin/user.username')) - ->alphaNum() - ->required() - ->unique() - ->minLength(3) - ->maxLength(255), - TextInput::make('email') - ->label(trans('admin/user.email')) - ->email() - ->required() - ->unique() - ->maxLength(255), - TextInput::make('password') - ->label(trans('admin/user.password')) - ->hintIcon('tabler-question-mark') - ->hintIconTooltip(trans('admin/user.password_help')) - ->password(), - CheckboxList::make('roles') - ->disableOptionWhen(fn (string $value): bool => $value == Role::getRootAdmin()->id) - ->relationship('roles', 'name') - ->dehydrated() - ->label(trans('admin/user.admin_roles')) - ->columnSpanFull() - ->bulkToggleable(false), - ]); - } - protected function getHeaderActions(): array { return [ diff --git a/app/Filament/Admin/Resources/UserResource/Pages/EditUser.php b/app/Filament/Admin/Resources/UserResource/Pages/EditUser.php index 62a0a10f9..652a04020 100644 --- a/app/Filament/Admin/Resources/UserResource/Pages/EditUser.php +++ b/app/Filament/Admin/Resources/UserResource/Pages/EditUser.php @@ -3,60 +3,28 @@ namespace App\Filament\Admin\Resources\UserResource\Pages; use App\Filament\Admin\Resources\UserResource; -use App\Models\Role; use App\Models\User; +use App\Services\Users\UserUpdateService; use Filament\Actions\DeleteAction; -use Filament\Forms\Components\CheckboxList; -use Filament\Forms\Components\Hidden; -use Filament\Forms\Components\Section; -use Filament\Forms\Components\TextInput; -use Filament\Forms\Form; use Filament\Resources\Pages\EditRecord; -use Illuminate\Support\Facades\Hash; +use Illuminate\Database\Eloquent\Model; class EditUser extends EditRecord { protected static string $resource = UserResource::class; - public function form(Form $form): Form + private UserUpdateService $service; + + public function boot(UserUpdateService $service): void { - return $form - ->schema([ - Section::make()->schema([ - TextInput::make('username') - ->label(trans('admin/user.username')) - ->required() - ->minLength(3) - ->maxLength(255), - TextInput::make('email') - ->label(trans('admin/user.email')) - ->email() - ->required() - ->maxLength(255), - TextInput::make('password') - ->label(trans('admin/user.password')) - ->dehydrateStateUsing(fn (string $state): string => Hash::make($state)) - ->dehydrated(fn (?string $state): bool => filled($state)) - ->password(), - Hidden::make('skipValidation') - ->default(true), - CheckboxList::make('roles') - ->disabled(fn (User $user) => $user->id === auth()->user()->id) - ->disableOptionWhen(fn (string $value): bool => $value == Role::getRootAdmin()->id) - ->relationship('roles', 'name') - ->label(trans('admin/user.admin_roles')) - ->columnSpanFull() - ->bulkToggleable(false), - ]) - ->columns(['default' => 1, 'lg' => 3]), - ]); + $this->service = $service; } protected function getHeaderActions(): array { return [ DeleteAction::make() - ->label(fn (User $user) => auth()->user()->id === $user->id ? trans('admin/user.self_delete') : ($user->servers()->count() > 0 ? trans('admin/user.has_servers') : trans('filament-actions::delete.single.modal.actions.delete.label'))) + ->label(fn (User $user) => auth()->user()->id === $user->id ? 'Can\'t Delete Yourself' : ($user->servers()->count() > 0 ? 'User Has Servers' : 'Delete')) ->disabled(fn (User $user) => auth()->user()->id === $user->id || $user->servers()->count() > 0), $this->getSaveFormAction()->formId('form'), ]; @@ -66,4 +34,13 @@ class EditUser extends EditRecord { return []; } + + protected function handleRecordUpdate(Model $record, array $data): Model + { + if (!$record instanceof User) { + return $record; + } + + return $this->service->handle($record, $data); + } } diff --git a/app/Filament/Admin/Resources/UserResource/Pages/ListUsers.php b/app/Filament/Admin/Resources/UserResource/Pages/ListUsers.php index 4fb959809..2ef32f2ab 100644 --- a/app/Filament/Admin/Resources/UserResource/Pages/ListUsers.php +++ b/app/Filament/Admin/Resources/UserResource/Pages/ListUsers.php @@ -3,78 +3,13 @@ namespace App\Filament\Admin\Resources\UserResource\Pages; use App\Filament\Admin\Resources\UserResource; -use App\Models\User; use Filament\Actions\CreateAction; use Filament\Resources\Pages\ListRecords; -use Filament\Tables\Actions\BulkActionGroup; -use Filament\Tables\Actions\DeleteBulkAction; -use Filament\Tables\Actions\EditAction; -use Filament\Tables\Columns\IconColumn; -use Filament\Tables\Columns\ImageColumn; -use Filament\Tables\Columns\TextColumn; -use Filament\Tables\Table; class ListUsers extends ListRecords { protected static string $resource = UserResource::class; - public function table(Table $table): Table - { - return $table - ->searchable(false) - ->columns([ - ImageColumn::make('picture') - ->visibleFrom('lg') - ->label('') - ->extraImgAttributes(['class' => 'rounded-full']) - ->defaultImageUrl(fn (User $user) => 'https://gravatar.com/avatar/' . md5(strtolower($user->email))), - TextColumn::make('external_id') - ->searchable() - ->hidden(), - TextColumn::make('uuid') - ->label('UUID') - ->hidden() - ->searchable(), - TextColumn::make('username') - ->label(trans('admin/user.username')) - ->searchable(), - TextColumn::make('email') - ->label(trans('admin/user.email')) - ->searchable() - ->icon('tabler-mail'), - IconColumn::make('use_totp') - ->label('2FA') - ->visibleFrom('lg') - ->icon(fn (User $user) => $user->use_totp ? 'tabler-lock' : 'tabler-lock-open-off') - ->boolean() - ->sortable(), - TextColumn::make('roles.name') - ->label(trans('admin/user.roles')) - ->badge() - ->icon('tabler-users-group') - ->placeholder(trans('admin/user.no_roles')), - TextColumn::make('servers_count') - ->counts('servers') - ->icon('tabler-server') - ->label(trans('admin/user.servers')), - TextColumn::make('subusers_count') - ->visibleFrom('sm') - ->label(trans('admin/user.subusers')) - ->counts('subusers') - ->icon('tabler-users'), - ]) - ->actions([ - EditAction::make(), - ]) - ->checkIfRecordIsSelectableUsing(fn (User $user) => auth()->user()->id !== $user->id && !$user->servers_count) - ->bulkActions([ - BulkActionGroup::make([ - DeleteBulkAction::make() - ->authorize(fn () => auth()->user()->can('delete user')), - ]), - ]); - } - protected function getHeaderActions(): array { return [ diff --git a/app/Filament/Admin/Resources/UserResource/Pages/ViewUser.php b/app/Filament/Admin/Resources/UserResource/Pages/ViewUser.php new file mode 100644 index 000000000..0c5933cb9 --- /dev/null +++ b/app/Filament/Admin/Resources/UserResource/Pages/ViewUser.php @@ -0,0 +1,19 @@ +columns([ + TextColumn::make('description') + ->label(trans('admin/webhook.table.description')), + TextColumn::make('endpoint') + ->label(trans('admin/webhook.table.endpoint')), + ]) + ->actions([ + ViewAction::make() + ->hidden(fn ($record) => static::canEdit($record)), + EditAction::make(), + DeleteAction::make(), + ]) + ->groupedBulkActions([ + DeleteBulkAction::make() + ->authorize(fn () => auth()->user()->can('delete webhook')), + ]) + ->emptyStateIcon('tabler-webhook') + ->emptyStateDescription('') + ->emptyStateHeading(trans('admin/webhook.no_webhooks')) + ->emptyStateActions([ + CreateAction::make(), + ]); + } + + public static function form(Form $form): Form + { + return $form + ->schema([ + TextInput::make('endpoint') + ->label(trans('admin/webhook.endpoint')) + ->activeUrl() + ->required(), + TextInput::make('description') + ->label(trans('admin/webhook.description')) + ->required(), + CheckboxList::make('events') + ->lazy() + ->options(fn () => WebhookConfiguration::filamentCheckboxList()) + ->searchable() + ->bulkToggleable() + ->columns(3) + ->columnSpanFull() + ->gridDirection('row') + ->required(), + ]); + } + public static function getPages(): array { return [ 'index' => Pages\ListWebhookConfigurations::route('/'), 'create' => Pages\CreateWebhookConfiguration::route('/create'), + 'view' => Pages\ViewWebhookConfiguration::route('/{record}'), 'edit' => Pages\EditWebhookConfiguration::route('/{record}/edit'), ]; } diff --git a/app/Filament/Admin/Resources/WebhookResource/Pages/CreateWebhookConfiguration.php b/app/Filament/Admin/Resources/WebhookResource/Pages/CreateWebhookConfiguration.php index 67e26447e..f38ebb7de 100644 --- a/app/Filament/Admin/Resources/WebhookResource/Pages/CreateWebhookConfiguration.php +++ b/app/Filament/Admin/Resources/WebhookResource/Pages/CreateWebhookConfiguration.php @@ -3,10 +3,6 @@ namespace App\Filament\Admin\Resources\WebhookResource\Pages; use App\Filament\Admin\Resources\WebhookResource; -use App\Models\WebhookConfiguration; -use Filament\Forms\Components\CheckboxList; -use Filament\Forms\Components\TextInput; -use Filament\Forms\Form; use Filament\Resources\Pages\CreateRecord; class CreateWebhookConfiguration extends CreateRecord @@ -26,28 +22,4 @@ class CreateWebhookConfiguration extends CreateRecord { return []; } - - public function form(Form $form): Form - { - return $form - ->schema([ - TextInput::make('endpoint') - ->label(trans('admin/webhook.endpoint')) - ->activeUrl() - ->required(), - TextInput::make('description') - ->label(trans('admin/webhook.description')) - ->required(), - CheckboxList::make('events') - ->label(trans('admin/webhook.events')) - ->lazy() - ->options(fn () => WebhookConfiguration::filamentCheckboxList()) - ->searchable() - ->bulkToggleable() - ->columns(3) - ->columnSpanFull() - ->gridDirection('row') - ->required(), - ]); - } } diff --git a/app/Filament/Admin/Resources/WebhookResource/Pages/EditWebhookConfiguration.php b/app/Filament/Admin/Resources/WebhookResource/Pages/EditWebhookConfiguration.php index 9070b5b15..07ba7e5b3 100644 --- a/app/Filament/Admin/Resources/WebhookResource/Pages/EditWebhookConfiguration.php +++ b/app/Filament/Admin/Resources/WebhookResource/Pages/EditWebhookConfiguration.php @@ -2,52 +2,28 @@ namespace App\Filament\Admin\Resources\WebhookResource\Pages; -use App\Models\WebhookConfiguration; use App\Filament\Admin\Resources\WebhookResource; -use Filament\Actions; -use Filament\Forms\Components\CheckboxList; -use Filament\Forms\Components\TextInput; -use Filament\Forms\Form; +use Filament\Actions\DeleteAction; use Filament\Resources\Pages\EditRecord; class EditWebhookConfiguration extends EditRecord { protected static string $resource = WebhookResource::class; - public function form(Form $form): Form + protected function getHeaderActions(): array { - return $form - ->schema([ - TextInput::make('endpoint') - ->label(trans('admin/webhook.endpoint')) - ->activeUrl() - ->required(), - TextInput::make('description') - ->label(trans('admin/webhook.description')) - ->required(), - CheckboxList::make('events') - ->label(trans('admin/webhook.events')) - ->lazy() - ->options(fn () => WebhookConfiguration::filamentCheckboxList()) - ->searchable() - ->bulkToggleable() - ->columns(3) - ->columnSpanFull() - ->gridDirection('row') - ->required(), - ]); + return [ + DeleteAction::make() + ->label('Delete') + ->modalHeading('Are you sure you want to delete this?') + ->modalDescription('') + ->modalSubmitActionLabel('Delete'), + $this->getSaveFormAction()->formId('form'), + ]; } protected function getFormActions(): array { return []; } - - protected function getHeaderActions(): array - { - return [ - Actions\DeleteAction::make(), - $this->getSaveFormAction()->formId('form'), - ]; - } } diff --git a/app/Filament/Admin/Resources/WebhookResource/Pages/ListWebhookConfigurations.php b/app/Filament/Admin/Resources/WebhookResource/Pages/ListWebhookConfigurations.php index ce1f3859b..722a0f167 100644 --- a/app/Filament/Admin/Resources/WebhookResource/Pages/ListWebhookConfigurations.php +++ b/app/Filament/Admin/Resources/WebhookResource/Pages/ListWebhookConfigurations.php @@ -4,44 +4,17 @@ namespace App\Filament\Admin\Resources\WebhookResource\Pages; use App\Filament\Admin\Resources\WebhookResource; use App\Models\WebhookConfiguration; -use Filament\Actions; +use Filament\Actions\CreateAction; use Filament\Resources\Pages\ListRecords; -use Filament\Tables\Actions\CreateAction; -use Filament\Tables\Columns\TextColumn; -use Filament\Tables\Table; -use Filament\Tables\Actions\EditAction; -use Filament\Tables\Actions\DeleteAction; class ListWebhookConfigurations extends ListRecords { protected static string $resource = WebhookResource::class; - public function table(Table $table): Table - { - return $table - ->columns([ - TextColumn::make('description') - ->label(trans('admin/webhook.table.description')), - TextColumn::make('endpoint') - ->label(trans('admin/webhook.table.endpoint')), - ]) - ->actions([ - DeleteAction::make(), - EditAction::make(), - ]) - ->emptyStateIcon('tabler-webhook') - ->emptyStateDescription('') - ->emptyStateHeading(trans('admin/webhook.no_webhooks')) - ->emptyStateActions([ - CreateAction::make('create') - ->button(), - ]); - } - protected function getHeaderActions(): array { return [ - Actions\CreateAction::make() + CreateAction::make() ->hidden(fn () => WebhookConfiguration::count() <= 0), ]; } diff --git a/app/Filament/Admin/Resources/WebhookResource/Pages/ViewWebhookConfiguration.php b/app/Filament/Admin/Resources/WebhookResource/Pages/ViewWebhookConfiguration.php new file mode 100644 index 000000000..cd7d32712 --- /dev/null +++ b/app/Filament/Admin/Resources/WebhookResource/Pages/ViewWebhookConfiguration.php @@ -0,0 +1,19 @@ + 'No API keys.', 'whitelist' => 'Whitelisted IPv4 Addresses', 'whitelist_help' => 'API keys can be restricted to only work from specific IPv4 addresses. Enter each address on a new line.', + 'whitelist_placeholder' => 'Example: 127.0.0.1 or 192.168.1.1', 'description' => 'Description', 'description_help' => 'A brief description of what this key is for.', 'nav_title' => 'API Keys', diff --git a/lang/en/admin/mount.php b/lang/en/admin/mount.php index ec6cef931..1f0c6f949 100644 --- a/lang/en/admin/mount.php +++ b/lang/en/admin/mount.php @@ -23,8 +23,8 @@ return [ ], 'table' => [ 'name' => 'Name', - 'source' => 'Source', - 'target' => 'Target', + 'all_eggs' => 'All Eggs', + 'all_nodes' => 'All Nodes', 'read_only' => 'Read Only', ], ];