mirror of
https://github.com/pelican-dev/panel.git
synced 2025-05-19 23:24:46 +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;
|
||||
|
||||
use App\Exceptions\Http\HttpForbiddenException;
|
||||
use App\Livewire\AlertBanner;
|
||||
use App\Models\Permission;
|
||||
use App\Models\Server;
|
||||
use App\Models\User;
|
||||
@ -113,4 +114,14 @@ class ServerConsole extends Widget
|
||||
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;
|
||||
|
||||
use Closure;
|
||||
use Filament\Notifications\Concerns;
|
||||
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\HasIcon;
|
||||
use Concerns\HasId;
|
||||
use Concerns\HasStatus;
|
||||
use Concerns\HasTitle;
|
||||
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 [
|
||||
'id' => $this->getId(),
|
||||
'title' => $this->getTitle(),
|
||||
'body' => $this->getBody(),
|
||||
'status' => $this->getStatus(),
|
||||
'icon' => $this->getIcon(),
|
||||
'closeable' => $this->isCloseable(),
|
||||
];
|
||||
}
|
||||
|
||||
public function send(): static
|
||||
public static function fromLivewire(mixed $value): AlertBanner
|
||||
{
|
||||
$alerts = session()->get('alert-banners', []);
|
||||
$alerts[] = $this->toArray();
|
||||
$static = AlertBanner::make();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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(
|
||||
PanelsRenderHook::CONTENT_START,
|
||||
fn () => view('filament.alerts.alert-banner-container'),
|
||||
PanelsRenderHook::PAGE_START,
|
||||
fn () => Blade::render('@livewire(\App\Livewire\AlertBannerContainer::class)'),
|
||||
);
|
||||
|
||||
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() }}");
|
||||
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.onerror = (event) => {
|
||||
$wire.dispatchSelf('websocket-error');
|
||||
};
|
||||
|
||||
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