diff --git a/app/Filament/Resources/ApiKeyResource.php b/app/Filament/Resources/ApiKeyResource.php index 307799221..d50c23cf7 100644 --- a/app/Filament/Resources/ApiKeyResource.php +++ b/app/Filament/Resources/ApiKeyResource.php @@ -72,33 +72,44 @@ class ApiKeyResource extends Resource ->required() ->default(ApiKey::TYPE_APPLICATION), - Forms\Components\Fieldset::make('Permissions')->schema( - collect(ApiKey::RESOURCES)->map(fn ($resource) => Forms\Components\ToggleButtons::make("r_$resource") - ->label(str($resource)->replace('_', ' ')->title()) - ->options([ - 0 => 'None', - 1 => 'Read', - // 2 => 'Write', - 3 => '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', - ]) - ->inline() - ->required() - ->disabledOn('edit') - ->default(0), - )->all(), - ), + Forms\Components\Fieldset::make('Permissions') + ->columns([ + 'default' => 1, + 'sm' => 1, + 'md' => 2, + ]) + ->schema( + collect(ApiKey::RESOURCES)->map(fn ($resource) => Forms\Components\ToggleButtons::make("r_$resource") + ->label(str($resource)->replace('_', ' ')->title()) + ->options([ + 0 => 'None', + 1 => 'Read', + // 2 => 'Write', + 3 => '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', + ]) + ->inline() + ->required() + ->disabledOn('edit') + ->columnSpan([ + 'default' => 1, + 'sm' => 1, + 'md' => 1, + ]) + ->default(0), + )->all(), + ), Forms\Components\TagsInput::make('allowed_ips') ->placeholder('Example: 127.0.0.1 or 192.168.1.1') diff --git a/app/Filament/Resources/EggResource.php b/app/Filament/Resources/EggResource.php index 41d7d242b..019acd035 100644 --- a/app/Filament/Resources/EggResource.php +++ b/app/Filament/Resources/EggResource.php @@ -133,7 +133,6 @@ class EggResource extends Resource public static function table(Table $table): Table { return $table - ->searchable(false) ->defaultPaginationPageOption(25) ->columns([ Tables\Columns\TextColumn::make('id') @@ -142,13 +141,12 @@ class EggResource extends Resource ->searchable(), Tables\Columns\TextColumn::make('name') ->icon('tabler-egg') + ->description(fn ($record): string => $record->description) + ->wrap() ->searchable(), Tables\Columns\TextColumn::make('author') ->hidden() ->searchable(), - Tables\Columns\TextColumn::make('description') - ->words(50) - ->wrap(), Tables\Columns\TextColumn::make('servers_count') ->counts('servers') ->icon('tabler-server') diff --git a/app/Filament/Resources/MountResource.php b/app/Filament/Resources/MountResource.php index c415e753a..90fc9ca05 100644 --- a/app/Filament/Resources/MountResource.php +++ b/app/Filament/Resources/MountResource.php @@ -75,7 +75,7 @@ class MountResource extends Resource Forms\Components\Textarea::make('description') ->helperText('A longer description for this mount.') ->columnSpanFull(), - ])->columnSpan(2)->columns([ + ])->columnSpan(1)->columns([ 'default' => 1, 'lg' => 2, ]), @@ -91,10 +91,11 @@ class MountResource extends Resource ]), ])->columns([ 'default' => 1, + 'lg' => 2, ]), ])->columns([ 'default' => 1, - 'lg' => 3, + 'lg' => 2, ]); } diff --git a/app/Filament/Resources/NodeResource.php b/app/Filament/Resources/NodeResource.php index e09989123..f07b85fac 100644 --- a/app/Filament/Resources/NodeResource.php +++ b/app/Filament/Resources/NodeResource.php @@ -96,31 +96,37 @@ class NodeResource extends Resource ->sortable() ->searchable(), Tables\Columns\TextColumn::make('fqdn') + ->visibleFrom('md') ->label('Address') ->icon('tabler-network') ->sortable() ->searchable(), Tables\Columns\TextColumn::make('memory') + ->visibleFrom('sm') ->icon('tabler-device-desktop-analytics') ->numeric() ->suffix(' GB') ->formatStateUsing(fn ($state) => number_format($state / 1000, 2)) ->sortable(), Tables\Columns\TextColumn::make('disk') + ->visibleFrom('sm') ->icon('tabler-file') ->numeric() ->suffix(' GB') ->formatStateUsing(fn ($state) => number_format($state / 1000, 2)) ->sortable(), Tables\Columns\IconColumn::make('scheme') + ->visibleFrom('xl') ->label('SSL') ->trueIcon('tabler-lock') ->falseIcon('tabler-lock-open-off') ->state(fn (Node $node) => $node->scheme === 'https'), Tables\Columns\IconColumn::make('public') + ->visibleFrom('lg') ->trueIcon('tabler-eye-check') ->falseIcon('tabler-eye-cancel'), Tables\Columns\TextColumn::make('servers_count') + ->visibleFrom('sm') ->counts('servers') ->label('Servers') ->sortable() diff --git a/app/Filament/Resources/NodeResource/Pages/CreateNode.php b/app/Filament/Resources/NodeResource/Pages/CreateNode.php index 4f83ea126..4427bea4e 100644 --- a/app/Filament/Resources/NodeResource/Pages/CreateNode.php +++ b/app/Filament/Resources/NodeResource/Pages/CreateNode.php @@ -18,13 +18,18 @@ class CreateNode extends CreateRecord public function form(Forms\Form $form): Forms\Form { return $form - ->columns(4) + ->columns([ + 'default' => 2, + 'sm' => 3, + 'md' => 3, + 'lg' => 4, + ]) ->schema([ Forms\Components\TextInput::make('fqdn') ->columnSpan(2) ->required() ->autofocus() - ->live(debounce: 500) + ->live(debounce: 1500) ->rule('prohibited', fn ($state) => is_ip($state) && request()->isSecure()) ->label(fn ($state) => is_ip($state) ? 'IP Address' : 'Domain Name') ->placeholder(fn ($state) => is_ip($state) ? '192.168.1.1' : 'node.example.com') @@ -100,10 +105,21 @@ class CreateNode extends CreateRecord ->colors([ true => 'success', false => 'danger', + ]) + ->columnSpan([ + 'default' => 1, + 'sm' => 1, + 'md' => 1, + 'lg' => 1, ]), Forms\Components\TextInput::make('daemonListen') - ->columnSpan(1) + ->columnSpan([ + 'default' => 1, + 'sm' => 1, + 'md' => 1, + 'lg' => 1, + ]) ->label('Port') ->helperText('If you are running the daemon behind Cloudflare you should set the daemon port to 8443 to allow websocket proxying over SSL.') ->minValue(0) @@ -114,7 +130,12 @@ class CreateNode extends CreateRecord Forms\Components\TextInput::make('name') ->label('Display Name') - ->columnSpan(2) + ->columnSpan([ + 'default' => 1, + 'sm' => 1, + 'md' => 1, + 'lg' => 2, + ]) ->required() ->regex('/[a-zA-Z0-9_\.\- ]+/') ->helperText('This name is for display only and can be changed later.') @@ -122,7 +143,12 @@ class CreateNode extends CreateRecord Forms\Components\ToggleButtons::make('scheme') ->label('Communicate over SSL') - ->columnSpan(2) + ->columnSpan([ + 'default' => 1, + 'sm' => 1, + 'md' => 1, + 'lg' => 1, + ]) ->required() ->inline() ->helperText(function (Forms\Get $get) { @@ -153,7 +179,12 @@ class CreateNode extends CreateRecord Forms\Components\Textarea::make('description') ->hidden() - ->columnSpanFull() + ->columnSpan([ + 'default' => 1, + 'sm' => 1, + 'md' => 2, + 'lg' => 4, + ]) ->rows(5), Forms\Components\Hidden::make('skipValidation')->default(true), diff --git a/app/Filament/Resources/NodeResource/Pages/EditNode.php b/app/Filament/Resources/NodeResource/Pages/EditNode.php index 8a6cdacf6..e506ca9ab 100644 --- a/app/Filament/Resources/NodeResource/Pages/EditNode.php +++ b/app/Filament/Resources/NodeResource/Pages/EditNode.php @@ -19,7 +19,12 @@ class EditNode extends EditRecord { return $form->schema([ Tabs::make('Tabs') - ->columns(4) + ->columns([ + 'default' => 2, + 'sm' => 3, + 'md' => 3, + 'lg' => 4, + ]) ->persistTabInQueryString() ->columnSpanFull() ->tabs([ @@ -45,11 +50,11 @@ class EditNode extends EditRecord ]), Tabs\Tab::make('Allocations') ->icon('tabler-plug-connected') - ->columns(5) + ->columns(4) ->schema([ Forms\Components\Repeater::make('allocations') ->orderColumn('server_id') - ->columnSpan(3) + ->columnSpan(1) ->columns(4) ->relationship() ->addActionLabel('Create New Allocation') diff --git a/app/Filament/Resources/ServerResource.php b/app/Filament/Resources/ServerResource.php index d5fc4dfea..2e3ad09af 100644 --- a/app/Filament/Resources/ServerResource.php +++ b/app/Filament/Resources/ServerResource.php @@ -18,7 +18,6 @@ use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Arr; use Illuminate\Support\Facades\Validator; -use Illuminate\Support\Str; class ServerResource extends Resource { @@ -31,7 +30,12 @@ class ServerResource extends Resource public static function form(Form $form): Form { return $form - ->columns(6) + ->columns([ + 'default' => 2, + 'sm' => 2, + 'md' => 4, + 'lg' => 6, + ]) ->schema([ Forms\Components\ToggleButtons::make('docker') ->label('Container Status') @@ -110,7 +114,12 @@ class ServerResource extends Resource $set('name', $prefix . fake()->domainWord); })) - ->columnSpan(4) + ->columnSpan([ + 'default' => 2, + 'sm' => 4, + 'md' => 2, + 'lg' => 3, + ]) ->required() ->maxLength(191), @@ -118,7 +127,12 @@ class ServerResource extends Resource ->prefixIcon('tabler-user') ->default(auth()->user()->id) ->label('Owner') - ->columnSpan(2) + ->columnSpan([ + 'default' => 2, + 'sm' => 4, + 'md' => 2, + 'lg' => 3, + ]) ->relationship('user', 'username') ->searchable() ->preload() @@ -264,7 +278,12 @@ class ServerResource extends Resource Forms\Components\Select::make('egg_id') ->disabledOn('edit') ->prefixIcon('tabler-egg') - ->columnSpan(2) + ->columnSpan([ + 'default' => 2, + 'sm' => 2, + 'md' => 2, + 'lg' => 6, + ]) ->relationship('egg', 'name') ->searchable() ->preload() @@ -309,31 +328,6 @@ class ServerResource extends Resource ->inline() ->required(), - Forms\Components\Select::make('image') - ->hidden(fn (Forms\Get $get) => $get('custom_image')) - ->disabled(fn (Forms\Get $get) => $get('custom_image')) - ->label('Docker Image') - ->prefixIcon('tabler-brand-docker') - ->options(function (Forms\Get $get, Forms\Set $set) { - $images = Egg::find($get('egg_id'))->docker_images ?? []; - - $set('image', collect($images)->first()); - - return $images; - }) - ->disabled(fn (Forms\Components\Select $component) => empty($component->getOptions())) - ->selectablePlaceholder(false) - ->columnSpan(2) - ->required(), - - Forms\Components\TextInput::make('image') - ->hidden(fn (Forms\Get $get) => !$get('custom_image')) - ->disabled(fn (Forms\Get $get) => !$get('custom_image')) - ->label('Docker Image') - ->placeholder('Enter a custom Image') - ->columnSpan(2) - ->required(), - Forms\Components\ToggleButtons::make('custom_image') ->live() ->label('Custom Image?') @@ -352,6 +346,41 @@ class ServerResource extends Resource ]) ->inline(), + Forms\Components\TextInput::make('image') + ->hidden(fn (Forms\Get $get) => !$get('custom_image')) + ->disabled(fn (Forms\Get $get) => !$get('custom_image')) + ->label('Docker Image') + ->placeholder('Enter a custom Image') + ->columnSpan([ + 'default' => 2, + 'sm' => 2, + 'md' => 2, + 'lg' => 4, + ]) + ->required(), + + Forms\Components\Select::make('image') + ->hidden(fn (Forms\Get $get) => $get('custom_image')) + ->disabled(fn (Forms\Get $get) => $get('custom_image')) + ->label('Docker Image') + ->prefixIcon('tabler-brand-docker') + ->options(function (Forms\Get $get, Forms\Set $set) { + $images = Egg::find($get('egg_id'))->docker_images ?? []; + + $set('image', collect($images)->first()); + + return $images; + }) + ->disabled(fn (Forms\Components\Select $component) => empty($component->getOptions())) + ->selectablePlaceholder(false) + ->columnSpan([ + 'default' => 2, + 'sm' => 2, + 'md' => 2, + 'lg' => 4, + ]) + ->required(), + Forms\Components\Fieldset::make('Application Feature Limits') ->inlineLabel() ->hiddenOn('create') @@ -379,13 +408,18 @@ class ServerResource extends Resource ->label('Startup Command') ->required() ->live() + ->columnSpan([ + 'default' => 2, + 'sm' => 4, + 'md' => 4, + 'lg' => 6, + ]) ->rows(function ($state) { return str($state)->explode("\n")->reduce( fn (int $carry, $line) => $carry + floor(strlen($line) / 125), 0 ); - }) - ->columnSpanFull(), + }), Forms\Components\Hidden::make('environment')->default([]), @@ -396,6 +430,12 @@ class ServerResource extends Resource ->iconColor('primary') ->collapsible() ->collapsed() + ->columnSpan(([ + 'default' => 2, + 'sm' => 4, + 'md' => 4, + 'lg' => 6, + ])) ->schema([ Forms\Components\Placeholder::make('Select an egg first to show its variables!') ->hidden(fn (Forms\Get $get) => !empty($get('server_variables'))), @@ -424,14 +464,16 @@ class ServerResource extends Resource }, ]) ->label(fn (Forms\Get $get) => $get('name')) - ->hint(fn (Forms\Get $get) => $get('rules')) + //->hint('Rule') + ->hintIcon('tabler-code') + //->hintIconTooltip(fn (Forms\Get $get) => $get('rules')) ->prefix(fn (Forms\Get $get) => '{{' . $get('env_variable') . '}}') ->helperText(fn (Forms\Get $get) => empty($get('description')) ? '—' : $get('description')) ->maxLength(191), Forms\Components\Hidden::make('variable_id')->default(0), ]) - ->columnSpanFull(), + ->columnSpan(2), ]), Forms\Components\Section::make('Resource Management') @@ -439,7 +481,13 @@ class ServerResource extends Resource ->collapsed() ->icon('tabler-server-cog') ->iconColor('primary') - ->columns(3) + ->columns(2) + ->columnSpan(([ + 'default' => 2, + 'sm' => 4, + 'md' => 4, + 'lg' => 6, + ])) ->schema([ Forms\Components\TextInput::make('memory') ->default(0) diff --git a/app/Filament/Resources/UserResource.php b/app/Filament/Resources/UserResource.php index 86072742d..a2b55cc7b 100644 --- a/app/Filament/Resources/UserResource.php +++ b/app/Filament/Resources/UserResource.php @@ -84,6 +84,7 @@ class UserResource extends Resource ->searchable(false) ->columns([ Tables\Columns\ImageColumn::make('picture') + ->visibleFrom('lg') ->label('') ->extraImgAttributes(['class' => 'rounded-full']) ->defaultImageUrl(fn (User $user) => 'https://gravatar.com/avatar/' . md5(strtolower($user->email))), @@ -100,12 +101,14 @@ class UserResource extends Resource ->searchable() ->icon('tabler-mail'), Tables\Columns\IconColumn::make('root_admin') + ->visibleFrom('md') ->label('Admin') ->boolean() ->trueIcon('tabler-star') ->falseIcon('tabler-star-off') ->sortable(), Tables\Columns\IconColumn::make('use_totp')->label('2FA') + ->visibleFrom('lg') ->icon(fn (User $user) => $user->use_totp ? 'tabler-lock' : 'tabler-lock-open-off') ->boolean()->sortable(), Tables\Columns\TextColumn::make('servers_count') @@ -113,6 +116,7 @@ class UserResource extends Resource ->icon('tabler-server') ->label('Servers'), Tables\Columns\TextColumn::make('subusers_count') + ->visibleFrom('sm') ->counts('subusers') ->icon('tabler-users') // ->formatStateUsing(fn (string $state, $record): string => (string) ($record->servers_count + $record->subusers_count)) diff --git a/app/Filament/Resources/UserResource/Pages/EditProfile.php b/app/Filament/Resources/UserResource/Pages/EditProfile.php index 90c4a67c3..e215de6ca 100644 --- a/app/Filament/Resources/UserResource/Pages/EditProfile.php +++ b/app/Filament/Resources/UserResource/Pages/EditProfile.php @@ -8,6 +8,7 @@ use Filament\Forms\Components\Placeholder; use Filament\Forms\Components\Repeater; use Filament\Forms\Components\Select; use Filament\Forms\Components\Tabs; +use Filament\Forms\Components\TagsInput; use Filament\Forms\Components\Tabs\Tab; use Filament\Forms\Components\TextInput; use Filament\Forms\Get; @@ -73,8 +74,16 @@ class EditProfile extends \Filament\Pages\Auth\EditProfile ->icon('tabler-key') ->schema([ Placeholder::make('Coming soon!'), + TagsInput::make('allowed_ips') + ->placeholder('Example: 127.0.0.1 or 192.168.1.1') + ->label('Whitelisted IPv4 Addresses') + ->helperText('Press enter to add a new IP address or leave blank to allow any IP address') + ->columnSpanFull() + ->hidden() + ->default(null), ]), + Tab::make('SSH Keys') ->icon('tabler-lock-code') ->schema([