Initial commit: Pireon Admin — headless admin panel and public API
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>
This commit is contained in:
147
app/Filament/Pages/SiteHealth.php
Normal file
147
app/Filament/Pages/SiteHealth.php
Normal file
@@ -0,0 +1,147 @@
|
||||
<?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';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user