Laravel 13 + Filament 5 admin panel and REST API for a non-profit developer collective, backed by the Pireon OAuth identity server. The public site is a separate, server-rendered application consuming /api/public/v1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
148 lines
4.1 KiB
PHP
148 lines
4.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Filament\Pages;
|
|
|
|
use App\Support\Health\Finding;
|
|
use App\Support\Health\HealthReport;
|
|
use App\Support\Health\Severity;
|
|
use BackedEnum;
|
|
use Filament\Actions\Action;
|
|
use Filament\Notifications\Notification;
|
|
use Filament\Pages\Page;
|
|
use Filament\Support\Icons\Heroicon;
|
|
use Illuminate\Contracts\Support\Htmlable;
|
|
use Illuminate\Support\Collection;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use UnitEnum;
|
|
|
|
/**
|
|
* Everything wrong with the public site, in one place.
|
|
*
|
|
* This page exists because the site is somewhere else. A missing translation,
|
|
* two records answering at the same URL, an image whose file is gone: none of
|
|
* it is visible where the content is edited. It shows up on the frontend,
|
|
* usually late, and usually to a visitor rather than to us.
|
|
*/
|
|
class SiteHealth extends Page
|
|
{
|
|
protected string $view = 'filament.pages.site-health';
|
|
|
|
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedShieldCheck;
|
|
|
|
protected static ?int $navigationSort = 88;
|
|
|
|
/**
|
|
* Not a public Livewire property: a Finding is a plain object, and
|
|
* Livewire can only carry types it knows how to serialise. It is
|
|
* recomputed per render instead, which is what opening this page is for.
|
|
*
|
|
* @var Collection<int, Finding>|null
|
|
*/
|
|
protected ?Collection $findings = null;
|
|
|
|
public static function canAccess(): bool
|
|
{
|
|
// Anyone who edits content benefits from seeing what is wrong with it.
|
|
// Individual checks hide themselves behind their own permission.
|
|
return Auth::user()?->can('panel.access') ?? false;
|
|
}
|
|
|
|
public static function getNavigationGroup(): string|UnitEnum|null
|
|
{
|
|
return __('panel.groups.administration');
|
|
}
|
|
|
|
public static function getNavigationLabel(): string
|
|
{
|
|
return __('health.title');
|
|
}
|
|
|
|
public function getTitle(): string|Htmlable
|
|
{
|
|
return __('health.title');
|
|
}
|
|
|
|
public function getSubheading(): string|Htmlable|null
|
|
{
|
|
return __('health.subheading');
|
|
}
|
|
|
|
/**
|
|
* @return Collection<int, Finding>
|
|
*/
|
|
public function findings(): Collection
|
|
{
|
|
return $this->findings ??= app(HealthReport::class)->runFor(Auth::user());
|
|
}
|
|
|
|
/**
|
|
* @return array<int, Action>
|
|
*/
|
|
protected function getHeaderActions(): array
|
|
{
|
|
return [
|
|
Action::make('refresh')
|
|
->label(__('health.actions.refresh'))
|
|
->icon('heroicon-o-arrow-path')
|
|
->color('gray')
|
|
->action(function (): void {
|
|
$this->refreshFindings();
|
|
|
|
Notification::make()
|
|
->title(__('health.refreshed'))
|
|
->success()
|
|
->send();
|
|
}),
|
|
];
|
|
}
|
|
|
|
public function refreshFindings(): void
|
|
{
|
|
// The badge is cached so that drawing it does not run ten checks on
|
|
// every page of the panel; opening this page is the one moment we
|
|
// definitely want fresh answers.
|
|
app(HealthReport::class)->forget(Auth::user());
|
|
|
|
$this->findings = null;
|
|
}
|
|
|
|
/**
|
|
* @return Collection<int, Finding>
|
|
*/
|
|
public function problems(): Collection
|
|
{
|
|
return $this->findings()->filter(fn (Finding $f) => $f->severity->isProblem());
|
|
}
|
|
|
|
/**
|
|
* @return Collection<int, Finding>
|
|
*/
|
|
public function passing(): Collection
|
|
{
|
|
return $this->findings()->reject(fn (Finding $f) => $f->severity->isProblem());
|
|
}
|
|
|
|
public function hasErrors(): bool
|
|
{
|
|
return $this->problems()->contains(fn (Finding $f) => $f->severity === Severity::Error);
|
|
}
|
|
|
|
/**
|
|
* A count in the sidebar, because a diagnostics page nobody opens
|
|
* diagnoses nothing.
|
|
*/
|
|
public static function getNavigationBadge(): ?string
|
|
{
|
|
$problems = app(HealthReport::class)->problemCount(Auth::user());
|
|
|
|
return $problems > 0 ? (string) $problems : null;
|
|
}
|
|
|
|
public static function getNavigationBadgeColor(): ?string
|
|
{
|
|
return 'warning';
|
|
}
|
|
}
|