Alert banners (#892)

* add alert banner

* replace old server conflict banner with alert banner

* improve color and icon size

* add alert for websocket errors

* update file loading error to alert banner

* remove old events

* add back `console-status` event

* move @php block under @isset

* remove phpstan ignore

so I'm not getting force choked
This commit is contained in:
Boy132 2025-01-15 18:23:09 +01:00 committed by GitHub
parent 7c6b3a03db
commit 885e03ee06
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 133 additions and 70 deletions

View File

@ -3,11 +3,13 @@
namespace App\Filament\Server\Pages;
use App\Enums\ContainerStatus;
use App\Exceptions\Http\Server\ServerStateConflictException;
use App\Filament\Server\Widgets\ServerConsole;
use App\Filament\Server\Widgets\ServerCpuChart;
use App\Filament\Server\Widgets\ServerMemoryChart;
// use App\Filament\Server\Widgets\ServerNetworkChart;
use App\Filament\Server\Widgets\ServerOverview;
use App\Livewire\AlertBanner;
use App\Models\Server;
use Filament\Actions\Action;
use Filament\Facades\Filament;
@ -25,6 +27,22 @@ class Console extends Page
public ContainerStatus $status = ContainerStatus::Offline;
public function mount(): void
{
/** @var Server $server */
$server = Filament::getTenant();
try {
$server->validateCurrentState();
} catch (ServerStateConflictException $exception) {
AlertBanner::make()
->warning()
->title('Warning')
->body($exception->getMessage())
->send();
}
}
public function getWidgetData(): array
{
return [

View File

@ -96,7 +96,6 @@ class ListFiles extends ListRecords
->sortable(),
])
->recordUrl(function (File $file) use ($server) {
if ($file->is_directory) {
return self::getUrl(['path' => join_paths($this->path, $file->name)]);
}

View File

@ -0,0 +1,41 @@
<?php
namespace App\Livewire;
use Filament\Notifications\Concerns;
use Filament\Support\Concerns\EvaluatesClosures;
use Illuminate\Contracts\Support\Arrayable;
final class AlertBanner implements Arrayable
{
use Concerns\HasBody;
use Concerns\HasIcon;
use Concerns\HasStatus;
use Concerns\HasTitle;
use EvaluatesClosures;
public static function make(): static
{
return new self();
}
public function toArray(): array
{
return [
'title' => $this->getTitle(),
'body' => $this->getBody(),
'status' => $this->getStatus(),
'icon' => $this->getIcon(),
];
}
public function send(): static
{
$alerts = session()->get('alert-banners', []);
$alerts[] = $this->toArray();
session()->flash('alert-banners', $alerts);
return $this;
}
}

View File

@ -1,34 +0,0 @@
<?php
namespace App\Livewire;
use App\Models\Server;
use Filament\Facades\Filament;
use Illuminate\View\View;
use Livewire\Attributes\On;
use Livewire\Component;
class ServerConflictBanner extends Component
{
public ?Server $server = null;
public function mount(): void
{
/** @var Server $server */
$server = Filament::getTenant();
$this->server = $server;
}
#[On('console-install-completed')]
#[On('console-install-started')]
#[On('console-status')]
public function refresh(?string $state = null): void
{
$this->server->fresh();
}
public function render(): View
{
return view('livewire.server-conflict-banner');
}
}

View File

@ -2,10 +2,10 @@
namespace App\Models;
use App\Livewire\AlertBanner;
use App\Repositories\Daemon\DaemonFileRepository;
use Carbon\Carbon;
use Exception;
use Filament\Notifications\Notification;
use Illuminate\Database\Eloquent\Builder;
use Sushi\Sushi;
@ -160,9 +160,14 @@ class File extends Model
} catch (Exception $exception) {
report($exception);
Notification::make()
->title('Error loading files')
->body($exception->getMessage())
$message = str($exception->getMessage());
if ($message->startsWith('cURL error 7: ')) {
$message = $message->after('cURL error 7: ')->before(' after ');
}
AlertBanner::make()
->title('Could not load files')
->body($message->toString())
->danger()
->send();

View File

@ -5,7 +5,6 @@ namespace App\Providers;
use App\Checks\NodeVersionsCheck;
use App\Checks\PanelVersionCheck;
use App\Checks\UsedDiskSpaceCheck;
use App\Filament\Server\Pages\Console;
use App\Models;
use App\Models\ApiKey;
use App\Models\Node;
@ -117,8 +116,7 @@ class AppServiceProvider extends ServiceProvider
FilamentView::registerRenderHook(
PanelsRenderHook::CONTENT_START,
fn () => view('filament.server-conflict-banner'),
scopes: Console::class,
fn () => view('filament.alerts.alert-banner-container'),
);
FilamentView::registerRenderHook(

View File

@ -0,0 +1,11 @@
@php
$alertBanners = session()->get('alert-banners', []);
@endphp
@if (isset($alertBanners))
<div class="flex flex-col gap-4 p-3 mt-3 mb-3">
@foreach ($alertBanners as $alertBanner)
@include('filament.alerts.alert-banner', ['alertBanner' => $alertBanner])
@endforeach
</div>
@endif

View File

@ -0,0 +1,40 @@
@props(['alertBanner'])
@isset ($alertBanner)
@php
$status = $alertBanner['status'];
$title = $alertBanner['title'];
$body = $alertBanner['body'];
$icon = $alertBanner['icon'] ?? match ($status) {
"success" => "tabler-circle-check",
"warning" => "tabler-exclamation-circle",
"danger" => "tabler-circle-x",
default => "tabler-info-circle",
};
$colorClasses = match ($status) {
"success" => "text-success-600 dark:text-success-500",
"warning" => "text-warning-600 dark:text-warning-500",
"danger" => "text-danger-600 dark:text-danger-500",
default => "text-info-600 dark:text-info-500",
};
@endphp
<div class="{{$colorClasses}} flex p-4 rounded-xl shadow-lg bg-white dark:bg-gray-900 ring-1 ring-gray-950/5 dark:ring-white/10">
@if (filled($icon))
<x-filament::icon :icon="$icon" class="h-8 w-8 mr-2" color="{{$status}}" />
@endif
<div class="flex flex-col flex-grow">
@if (filled($title))
<p class="font-bold">{{str($title)->sanitizeHtml()->toHtmlString()}}</p>
@endif
@if (filled($body))
<p class="font-normal">{{str($body)->sanitizeHtml()->toHtmlString()}}</p>
@endif
</div>
</div>
@endisset

View File

@ -113,6 +113,19 @@
const socket = new WebSocket("{{ $this->getSocket() }}");
let token = '{{ $this->getToken() }}';
socket.onerror = function(websocketErrorEvent) {
@php
$alerts = session()->get('alert-banners', []);
$alerts[] = [
'title' => 'Could not connect to websocket!',
'body' => 'Check your browser console for more details.',
'status' => 'danger',
];
session()->flash('alert-banners', $alerts);
@endphp
};
socket.onmessage = function(websocketMessageEvent) {
let { event, args } = JSON.parse(websocketMessageEvent.data);
@ -141,12 +154,6 @@
'args': [null]
}));
break;
case 'install started':
$wire.dispatch('console-install-started');
break;
case 'install completed':
$wire.dispatch('console-install-completed');
break;
case 'token expiring':
case 'token expired':
token = '{{ $this->getToken() }}';

View File

@ -1 +0,0 @@
<livewire:server-conflict-banner />

View File

@ -1,21 +0,0 @@
@php
$shouldShow = false;
try {
$server->validateCurrentState();
} catch (\App\Exceptions\Http\Server\ServerStateConflictException $exception) {
$shouldShow = true;
$message = $exception->getMessage();
}
@endphp
<div id="server-conflict-banner">
@if ($shouldShow)
<div class="mt-2 p-2 rounded-lg text-white" style="background-color: #D97706;">
<div class="flex items-center">
<x-filament::icon icon="tabler-alert-triangle" class="h-6 w-6 mr-2 text-gray-500 dark:text-gray-400 text-white" />
<p>{!! $message !!}</p>
</div>
</div>
@endif
</div>