mirror of
https://github.com/pelican-dev/panel.git
synced 2025-05-20 11:04:45 +02:00
Alert banner improvements: auto-refresh, fixes & "closeable" (#924)
* fix websocket error always displaying * use livewire component with polling for alert banner container * add id to alert banner * cleanup blade file and add "closeable" property
This commit is contained in:
parent
cbacc18e56
commit
61bdf0dcd7
@ -3,6 +3,7 @@
|
|||||||
namespace App\Filament\Server\Widgets;
|
namespace App\Filament\Server\Widgets;
|
||||||
|
|
||||||
use App\Exceptions\Http\HttpForbiddenException;
|
use App\Exceptions\Http\HttpForbiddenException;
|
||||||
|
use App\Livewire\AlertBanner;
|
||||||
use App\Models\Permission;
|
use App\Models\Permission;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
@ -113,4 +114,14 @@ class ServerConsole extends Widget
|
|||||||
cache()->put($cacheKey, $data, now()->addMinute());
|
cache()->put($cacheKey, $data, now()->addMinute());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[On('websocket-error')]
|
||||||
|
public function websocketError(): void
|
||||||
|
{
|
||||||
|
AlertBanner::make()
|
||||||
|
->title('Could not connect to websocket!')
|
||||||
|
->body('Check your browser console for more details.')
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,40 +2,83 @@
|
|||||||
|
|
||||||
namespace App\Livewire;
|
namespace App\Livewire;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
use Filament\Notifications\Concerns;
|
use Filament\Notifications\Concerns;
|
||||||
use Filament\Support\Concerns\EvaluatesClosures;
|
use Filament\Support\Concerns\EvaluatesClosures;
|
||||||
use Illuminate\Contracts\Support\Arrayable;
|
use Illuminate\Support\Str;
|
||||||
|
use Livewire\Wireable;
|
||||||
|
|
||||||
final class AlertBanner implements Arrayable
|
final class AlertBanner implements Wireable
|
||||||
{
|
{
|
||||||
use Concerns\HasBody;
|
use Concerns\HasBody;
|
||||||
use Concerns\HasIcon;
|
use Concerns\HasIcon;
|
||||||
|
use Concerns\HasId;
|
||||||
use Concerns\HasStatus;
|
use Concerns\HasStatus;
|
||||||
use Concerns\HasTitle;
|
use Concerns\HasTitle;
|
||||||
use EvaluatesClosures;
|
use EvaluatesClosures;
|
||||||
|
|
||||||
public static function make(): static
|
protected bool|Closure $closable = false;
|
||||||
|
|
||||||
|
public static function make(?string $id = null): AlertBanner
|
||||||
{
|
{
|
||||||
return new self();
|
$static = new self();
|
||||||
|
$static->id($id ?? Str::orderedUuid());
|
||||||
|
|
||||||
|
return $static;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toArray(): array
|
public function toLivewire(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
'id' => $this->getId(),
|
||||||
'title' => $this->getTitle(),
|
'title' => $this->getTitle(),
|
||||||
'body' => $this->getBody(),
|
'body' => $this->getBody(),
|
||||||
'status' => $this->getStatus(),
|
'status' => $this->getStatus(),
|
||||||
'icon' => $this->getIcon(),
|
'icon' => $this->getIcon(),
|
||||||
|
'closeable' => $this->isCloseable(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function send(): static
|
public static function fromLivewire(mixed $value): AlertBanner
|
||||||
{
|
{
|
||||||
$alerts = session()->get('alert-banners', []);
|
$static = AlertBanner::make();
|
||||||
$alerts[] = $this->toArray();
|
|
||||||
|
|
||||||
session()->flash('alert-banners', $alerts);
|
$static->id($value['id']);
|
||||||
|
$static->title($value['title']);
|
||||||
|
$static->body($value['body']);
|
||||||
|
$static->status($value['status']);
|
||||||
|
$static->icon($value['icon']);
|
||||||
|
$static->closable($value['closeable']);
|
||||||
|
|
||||||
|
return $static;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function closable(bool|Closure $closable = true): AlertBanner
|
||||||
|
{
|
||||||
|
$this->closable = $closable;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isCloseable(): bool
|
||||||
|
{
|
||||||
|
return $this->evaluate($this->closable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function send(): AlertBanner
|
||||||
|
{
|
||||||
|
session()->push('alert-banners', $this->toLivewire());
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getColorClasses(): string
|
||||||
|
{
|
||||||
|
return match ($this->getStatus()) {
|
||||||
|
'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',
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
35
app/Livewire/AlertBannerContainer.php
Normal file
35
app/Livewire/AlertBannerContainer.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class AlertBannerContainer extends Component
|
||||||
|
{
|
||||||
|
public array $alertBanners;
|
||||||
|
|
||||||
|
public function mount(): void
|
||||||
|
{
|
||||||
|
$this->alertBanners = [];
|
||||||
|
$this->pullFromSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pullFromSession(): void
|
||||||
|
{
|
||||||
|
foreach (session()->pull('alert-banners', []) as $alertBanner) {
|
||||||
|
$alertBanner = AlertBanner::fromLivewire($alertBanner);
|
||||||
|
$this->alertBanners[$alertBanner->getId()] = $alertBanner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function remove(string $id): void
|
||||||
|
{
|
||||||
|
unset($this->alertBanners[$id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render(): View
|
||||||
|
{
|
||||||
|
return view('livewire.alerts.alert-banner-container');
|
||||||
|
}
|
||||||
|
}
|
@ -118,8 +118,8 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
);
|
);
|
||||||
|
|
||||||
FilamentView::registerRenderHook(
|
FilamentView::registerRenderHook(
|
||||||
PanelsRenderHook::CONTENT_START,
|
PanelsRenderHook::PAGE_START,
|
||||||
fn () => view('filament.alerts.alert-banner-container'),
|
fn () => Blade::render('@livewire(\App\Livewire\AlertBannerContainer::class)'),
|
||||||
);
|
);
|
||||||
|
|
||||||
FilamentView::registerRenderHook(
|
FilamentView::registerRenderHook(
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
@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
|
|
@ -1,40 +0,0 @@
|
|||||||
@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
|
|
@ -113,17 +113,8 @@
|
|||||||
const socket = new WebSocket("{{ $this->getSocket() }}");
|
const socket = new WebSocket("{{ $this->getSocket() }}");
|
||||||
let token = '{{ $this->getToken() }}';
|
let token = '{{ $this->getToken() }}';
|
||||||
|
|
||||||
socket.onerror = function(websocketErrorEvent) {
|
socket.onerror = (event) => {
|
||||||
@php
|
$wire.dispatchSelf('websocket-error');
|
||||||
$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) {
|
socket.onmessage = function(websocketMessageEvent) {
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
<div wire:poll.1s="pullFromSession" id="alert-banner-container" class="flex flex-col gap-4">
|
||||||
|
@foreach (array_values($alertBanners) as $alertBanner)
|
||||||
|
@include('livewire.alerts.alert-banner', ['alertBanner' => $alertBanner])
|
||||||
|
@endforeach
|
||||||
|
</div>
|
27
resources/views/livewire/alerts/alert-banner.blade.php
Normal file
27
resources/views/livewire/alerts/alert-banner.blade.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
@props(['alertBanner'])
|
||||||
|
|
||||||
|
@php
|
||||||
|
$icon = $alertBanner->getIcon();
|
||||||
|
$title = $alertBanner->getTitle();
|
||||||
|
$body = $alertBanner->getBody();
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<div class="{{$alertBanner->getColorClasses()}} flex p-4 mt-3 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="{{$alertBanner->getStatus()}}" />
|
||||||
|
@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>
|
||||||
|
|
||||||
|
@if ($alertBanner->isCloseable())
|
||||||
|
<x-filament::icon-button color="gray" icon="tabler-x" wire:click="remove('{{$alertBanner->getID()}}')" />
|
||||||
|
@endif
|
||||||
|
</div>
|
Loading…
x
Reference in New Issue
Block a user