mirror of
https://github.com/pelican-dev/panel.git
synced 2025-05-19 21:04:44 +02:00
Update Overview, Again. Add some customization (#1200)
* wip * wip * wip * overview 2.1 * Combine 2 branches into one * updates * Fix 500 * use my friend JSON * Use switch
This commit is contained in:
parent
3639d7ccec
commit
befe6be80b
@ -62,7 +62,7 @@ enum ContainerStatus: string implements HasColor, HasIcon, HasLabel
|
||||
self::Removing => 'warning',
|
||||
self::Missing => 'danger',
|
||||
self::Stopping => 'warning',
|
||||
self::Offline => 'gray',
|
||||
self::Offline => 'danger',
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -2,13 +2,16 @@
|
||||
|
||||
namespace App\Filament\App\Resources\ServerResource\Pages;
|
||||
|
||||
use App\Enums\ServerResourceType;
|
||||
use App\Filament\App\Resources\ServerResource;
|
||||
use App\Filament\Components\Tables\Columns\ServerEntryColumn;
|
||||
use App\Filament\Server\Pages\Console;
|
||||
use App\Models\Server;
|
||||
use Filament\Resources\Components\Tab;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
use Filament\Tables\Columns\ColumnGroup;
|
||||
use Filament\Tables\Columns\Layout\Stack;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Filters\SelectFilter;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
@ -17,21 +20,89 @@ class ListServers extends ListRecords
|
||||
{
|
||||
protected static string $resource = ServerResource::class;
|
||||
|
||||
public const DANGER_THRESHOLD = 0.9;
|
||||
|
||||
public const WARNING_THRESHOLD = 0.7;
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
$baseQuery = auth()->user()->accessibleServers();
|
||||
|
||||
$viewOne = [
|
||||
TextColumn::make('condition')
|
||||
->label('')
|
||||
->default('unknown')
|
||||
->wrap()
|
||||
->badge()
|
||||
->alignCenter()
|
||||
->tooltip(fn (Server $server) => $server->formatResource('uptime', type: ServerResourceType::Time))
|
||||
->icon(fn (Server $server) => $server->condition->getIcon())
|
||||
->color(fn (Server $server) => $server->condition->getColor()),
|
||||
];
|
||||
|
||||
$viewTwo = [
|
||||
TextColumn::make('name')
|
||||
->label('')
|
||||
->size('md')
|
||||
->searchable(),
|
||||
TextColumn::make('')
|
||||
->label('')
|
||||
->badge()
|
||||
->copyable(request()->isSecure())
|
||||
->copyMessage(fn (Server $server, string $state) => 'Copied ' . $server->allocation->address)
|
||||
->state(fn (Server $server) => $server->allocation->address),
|
||||
];
|
||||
|
||||
$viewThree = [
|
||||
TextColumn::make('cpuUsage')
|
||||
->label('')
|
||||
->icon('tabler-cpu')
|
||||
->tooltip(fn (Server $server) => 'Usage Limit: ' . $server->formatResource('cpu', limit: true, type: ServerResourceType::Percentage, precision: 0))
|
||||
->state(fn (Server $server) => $server->formatResource('cpu_absolute', type: ServerResourceType::Percentage))
|
||||
->color(fn (Server $server) => $this->getResourceColor($server, 'cpu')),
|
||||
TextColumn::make('memoryUsage')
|
||||
->label('')
|
||||
->icon('tabler-memory')
|
||||
->tooltip(fn (Server $server) => 'Usage Limit: ' . $server->formatResource('memory', limit: true))
|
||||
->state(fn (Server $server) => $server->formatResource('memory_bytes'))
|
||||
->color(fn (Server $server) => $this->getResourceColor($server, 'memory')),
|
||||
TextColumn::make('diskUsage')
|
||||
->label('')
|
||||
->icon('tabler-device-floppy')
|
||||
->tooltip(fn (Server $server) => 'Usage Limit: ' . $server->formatResource('disk', limit: true))
|
||||
->state(fn (Server $server) => $server->formatResource('disk_bytes'))
|
||||
->color(fn (Server $server) => $this->getResourceColor($server, 'disk')),
|
||||
];
|
||||
|
||||
return $table
|
||||
->paginated(false)
|
||||
->query(fn () => $baseQuery)
|
||||
->poll('15s')
|
||||
->columns([
|
||||
->columns(
|
||||
(auth()->user()->getCustomization()['dashboard_layout'] ?? 'grid') === 'grid'
|
||||
? [
|
||||
Stack::make([
|
||||
ServerEntryColumn::make('server_entry')
|
||||
->searchable(['name']),
|
||||
]),
|
||||
])
|
||||
]
|
||||
: [
|
||||
ColumnGroup::make('Status')
|
||||
->label('Status')
|
||||
->columns($viewOne),
|
||||
ColumnGroup::make('Server')
|
||||
->label('Servers')
|
||||
->columns($viewTwo),
|
||||
ColumnGroup::make('Resources')
|
||||
->label('Resources')
|
||||
->columns($viewThree),
|
||||
]
|
||||
)
|
||||
->recordUrl(fn (Server $server) => Console::getUrl(panel: 'server', tenant: $server))
|
||||
->contentGrid([
|
||||
'default' => 1,
|
||||
'md' => 2,
|
||||
])
|
||||
->emptyStateIcon('tabler-brand-docker')
|
||||
->emptyStateDescription('')
|
||||
->emptyStateHeading(fn () => $this->activeTab === 'my' ? 'You don\'t own any servers!' : 'You don\'t have access to any servers!')
|
||||
@ -73,4 +144,50 @@ class ListServers extends ListRecords
|
||||
->badge($all->count()),
|
||||
];
|
||||
}
|
||||
|
||||
public function getResourceColor(Server $server, string $resource): ?string
|
||||
{
|
||||
$current = null;
|
||||
$limit = null;
|
||||
|
||||
switch ($resource) {
|
||||
case 'cpu':
|
||||
$current = $server->resources()['cpu_absolute'] ?? 0;
|
||||
$limit = $server->cpu;
|
||||
if ($server->cpu === 0) {
|
||||
return null;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'memory':
|
||||
$current = $server->resources()['memory_bytes'] ?? 0;
|
||||
$limit = $server->memory * 2 ** 20;
|
||||
if ($server->memory === 0) {
|
||||
return null;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'disk':
|
||||
$current = $server->resources()['disk_bytes'] ?? 0;
|
||||
$limit = $server->disk * 2 ** 20;
|
||||
if ($server->disk === 0) {
|
||||
return null;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($current >= $limit * self::DANGER_THRESHOLD) {
|
||||
return 'danger';
|
||||
}
|
||||
|
||||
if ($current >= $limit * self::WARNING_THRESHOLD) {
|
||||
return 'warning';
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ use Filament\Forms\Components\Tabs\Tab;
|
||||
use Filament\Forms\Components\TagsInput;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\ToggleButtons;
|
||||
use Filament\Forms\Get;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Pages\Auth\EditProfile as BaseEditProfile;
|
||||
@ -242,6 +243,7 @@ class EditProfile extends BaseEditProfile
|
||||
->password(),
|
||||
];
|
||||
}),
|
||||
|
||||
Tab::make(trans('profile.tabs.api_keys'))
|
||||
->icon('tabler-key')
|
||||
->schema([
|
||||
@ -308,9 +310,11 @@ class EditProfile extends BaseEditProfile
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
|
||||
Tab::make(trans('profile.tabs.ssh_keys'))
|
||||
->icon('tabler-lock-code')
|
||||
->hidden(),
|
||||
|
||||
Tab::make(trans('profile.tabs.activity'))
|
||||
->icon('tabler-history')
|
||||
->schema([
|
||||
@ -325,6 +329,47 @@ class EditProfile extends BaseEditProfile
|
||||
Placeholder::make('activity!')->label('')->content(fn (ActivityLog $log) => new HtmlString($log->htmlable())),
|
||||
]),
|
||||
]),
|
||||
|
||||
Tab::make(trans('profile.tabs.customization'))
|
||||
->icon('tabler-adjustments')
|
||||
->schema([
|
||||
Section::make(trans('profile.dashboard'))
|
||||
->collapsible()
|
||||
->icon('tabler-dashboard')
|
||||
->schema([
|
||||
ToggleButtons::make('dashboard_layout')
|
||||
->label(trans('profile.dashboard_layout'))
|
||||
->inline()
|
||||
->required()
|
||||
->options([
|
||||
'grid' => trans('profile.grid'),
|
||||
'table' => trans('profile.table'),
|
||||
]),
|
||||
]),
|
||||
Section::make(trans('profile.console'))
|
||||
->collapsible()
|
||||
->icon('tabler-brand-tabler')
|
||||
->schema([
|
||||
TextInput::make('console_rows')
|
||||
->label(trans('profile.rows'))
|
||||
->minValue(1)
|
||||
->numeric()
|
||||
->required()
|
||||
->columnSpan(1)
|
||||
->default(30),
|
||||
// Select::make('console_font')
|
||||
// ->label(trans('profile.font'))
|
||||
// ->hidden() //TODO
|
||||
// ->columnSpan(1),
|
||||
TextInput::make('console_font_size')
|
||||
->label(trans('profile.font_size'))
|
||||
->columnSpan(1)
|
||||
->minValue(1)
|
||||
->numeric()
|
||||
->required()
|
||||
->default(14),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
])
|
||||
->operation('edit')
|
||||
@ -381,4 +426,29 @@ class EditProfile extends BaseEditProfile
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
protected function mutateFormDataBeforeSave(array $data): array
|
||||
{
|
||||
$moarbetterdata = [
|
||||
'console_font_size' => $data['console_font_size'],
|
||||
'console_rows' => $data['console_rows'],
|
||||
'dashboard_layout' => $data['dashboard_layout'],
|
||||
];
|
||||
|
||||
unset($data['dashboard_layout'], $data['console_font_size'], $data['console_rows']);
|
||||
$data['customization'] = json_encode($moarbetterdata);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function mutateFormDataBeforeFill(array $data): array
|
||||
{
|
||||
$moarbetterdata = json_decode($data['customization'], true);
|
||||
|
||||
$data['console_font_size'] = $moarbetterdata['console_font_size'] ?? 14;
|
||||
$data['console_rows'] = $moarbetterdata['console_rows'] ?? 30;
|
||||
$data['dashboard_layout'] = $moarbetterdata['dashboard_layout'] ?? 'grid';
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
@ -481,7 +481,7 @@ class Server extends Model implements Validatable
|
||||
}
|
||||
|
||||
if ($resourceAmount === 0 & $limit) {
|
||||
return 'Unlimited';
|
||||
return "\u{221E}";
|
||||
}
|
||||
|
||||
if ($type === ServerResourceType::Percentage) {
|
||||
|
@ -68,6 +68,7 @@ use Spatie\Permission\Traits\HasRoles;
|
||||
* @property int|null $tokens_count
|
||||
* @property \Illuminate\Database\Eloquent\Collection|\App\Models\Role[] $roles
|
||||
* @property int|null $roles_count
|
||||
* @property string|null $customization
|
||||
*
|
||||
* @method static \Database\Factories\UserFactory factory(...$parameters)
|
||||
* @method static Builder|User newModelQuery()
|
||||
@ -125,6 +126,7 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
|
||||
'totp_authenticated_at',
|
||||
'gravatar',
|
||||
'oauth',
|
||||
'customization',
|
||||
];
|
||||
|
||||
/**
|
||||
@ -142,6 +144,7 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
|
||||
'use_totp' => false,
|
||||
'totp_secret' => null,
|
||||
'oauth' => '[]',
|
||||
'customization' => null,
|
||||
];
|
||||
|
||||
/** @var array<array-key, string[]> */
|
||||
@ -156,6 +159,10 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
|
||||
'use_totp' => ['boolean'],
|
||||
'totp_secret' => ['nullable', 'string'],
|
||||
'oauth' => ['array', 'nullable'],
|
||||
'customization' => ['array', 'nullable'],
|
||||
'customization.console_rows' => ['integer', 'min:1'],
|
||||
'customization.console_font' => ['string'],
|
||||
'customization.console_font_size' => ['integer', 'min:1'],
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
@ -166,6 +173,7 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
|
||||
'totp_authenticated_at' => 'datetime',
|
||||
'totp_secret' => 'encrypted',
|
||||
'oauth' => 'array',
|
||||
'customization' => 'array',
|
||||
];
|
||||
}
|
||||
|
||||
@ -402,4 +410,10 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @return array<mixed> */
|
||||
public function getCustomization(): array
|
||||
{
|
||||
return json_decode($this->customization, true) ?? [];
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->json('customization')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('customization');
|
||||
});
|
||||
}
|
||||
};
|
@ -9,6 +9,7 @@ return [
|
||||
'api_keys' => 'API Keys',
|
||||
'ssh_keys' => 'SSH Keys',
|
||||
'2fa' => '2FA',
|
||||
'customization' => 'Customization',
|
||||
],
|
||||
'username' => 'Username',
|
||||
'exit_admin' => 'Exit Admin',
|
||||
@ -38,4 +39,12 @@ return [
|
||||
'description' => 'Description',
|
||||
'allowed_ips' => 'Allowed IPs',
|
||||
'allowed_ips_help' => 'Press enter to add a new IP address or leave blank to allow any IP address',
|
||||
'dashboard' => 'Dashboard',
|
||||
'dashboard_layout' => 'Dashboard Layout',
|
||||
'console' => 'Console',
|
||||
'grid' => 'Grid',
|
||||
'table' => 'Table',
|
||||
'rows' => 'Rows',
|
||||
'font_size' => 'Font Size',
|
||||
'font' => 'Font',
|
||||
];
|
||||
|
@ -57,14 +57,14 @@
|
||||
};
|
||||
|
||||
let options = {
|
||||
fontSize: 14,
|
||||
fontSize: {{ auth()->user()->getCustomization()['console_font_size'] ?? 14 }},
|
||||
fontFamily: 'Comic Mono, monospace',
|
||||
lineHeight: 1.2,
|
||||
disableStdin: true,
|
||||
cursorStyle: 'underline',
|
||||
cursorInactiveStyle: 'underline',
|
||||
allowTransparency: true,
|
||||
rows: 30,
|
||||
rows: {{ auth()->user()->getCustomization()['console_rows'] ?? 30 }},
|
||||
theme: theme
|
||||
};
|
||||
|
||||
|
@ -19,57 +19,45 @@
|
||||
style="background-color: {{ $server->condition->getColor(true) }};">
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-800 dark:text-white overflow-hidden p-2">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-1 bg-gray-800 dark:text-white rounded-lg overflow-hidden p-3">
|
||||
<div class="flex items-center mb-5 gap-2">
|
||||
<x-filament::icon-button
|
||||
:icon="$server->condition->getIcon()"
|
||||
:color="$server->condition->getColor()"
|
||||
:tooltip="$server->condition->getLabel()"
|
||||
size="xl"
|
||||
/>
|
||||
<h2 class="text-2xl font-semibold p-2">
|
||||
<h2 class="text-xl font-bold">
|
||||
{{ $server->name }}
|
||||
<span class="dark:text-gray-400">
|
||||
({{ $server->formatResource('uptime', type: ServerResourceType::Time) }})
|
||||
</span>
|
||||
<span class="dark:text-gray-400">({{ $server->formatResource('uptime', type: ServerResourceType::Time) }})</span>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="flex w-1/2 justify-between text-center">
|
||||
<div class="w-1/4">
|
||||
<p class="text-md dark:text-gray-400">CPU</p>
|
||||
<div class="flex justify-between text-center">
|
||||
<div>
|
||||
<p class="text-sm dark:text-gray-400">CPU</p>
|
||||
<p class="text-md font-semibold">{{ $server->formatResource('cpu_absolute', type: ServerResourceType::Percentage) }}</p>
|
||||
<hr class="p-0.5">
|
||||
<p class="text-md font-semibold">
|
||||
{{ $server->formatResource('cpu_absolute', type: ServerResourceType::Percentage) }}
|
||||
</p>
|
||||
<p class="text-xs dark:text-gray-400">{{ $server->formatResource('cpu', type: ServerResourceType::Percentage, limit: true) }}</p>
|
||||
</div>
|
||||
<div class="w-1/4">
|
||||
<p class="text-md dark:text-gray-400">Memory</p>
|
||||
<div>
|
||||
<p class="text-sm dark:text-gray-400">Memory</p>
|
||||
<p class="text-md font-semibold">{{ $server->formatResource('memory_bytes') }}</p>
|
||||
<hr class="p-0.5">
|
||||
<p class="text-md font-semibold">
|
||||
{{ $server->formatResource('memory_bytes') }}
|
||||
</p>
|
||||
<p class="text-xs dark:text-gray-400">{{ $server->formatResource('memory', limit: true) }}</p>
|
||||
</div>
|
||||
<div class="w-1/4 hidden sm:block">
|
||||
<p class="text-md dark:text-gray-400">Disk</p>
|
||||
<div>
|
||||
<p class="text-sm dark:text-gray-400">Disk</p>
|
||||
<p class="text-md font-semibold">{{ $server->formatResource('disk_bytes') }}</p>
|
||||
<hr class="p-0.5">
|
||||
<p class="text-md font-semibold">
|
||||
{{ $server->formatResource('disk_bytes') }}
|
||||
</p>
|
||||
<p class="text-xs dark:text-gray-400">{{ $server->formatResource('disk', limit: true) }}</p>
|
||||
</div>
|
||||
<div class="w-1/4 hidden sm:block">
|
||||
<p class="text-md dark:text-gray-400">Network</p>
|
||||
<div class="hidden sm:block">
|
||||
<p class="text-sm dark:text-gray-400">Network</p>
|
||||
<hr class="p-0.5">
|
||||
<p class="text-md font-semibold">
|
||||
{{ $server->allocation->address }}
|
||||
</p>
|
||||
<p class="text-md font-semibold">{{ $server->allocation->address }} </p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user