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:
82
app/Support/ReviewTarget.php
Normal file
82
app/Support/ReviewTarget.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Support;
|
||||
|
||||
use App\Filament\Resources\Pages\PageResource;
|
||||
use App\Filament\Resources\Projects\ProjectResource;
|
||||
use App\Filament\Resources\Services\ServiceResource;
|
||||
use App\Models\Page;
|
||||
use App\Models\Project;
|
||||
use App\Models\Service;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
/**
|
||||
* Where a record under review lives, in words a notification can use.
|
||||
*
|
||||
* The panel-facing half of reviews, kept out of the models on purpose: a page
|
||||
* has no business knowing which Filament resource happens to edit it. Written
|
||||
* as an explicit map rather than resolved dynamically because there are three
|
||||
* of them and there is no honest way to guess the fourth.
|
||||
*/
|
||||
final class ReviewTarget
|
||||
{
|
||||
/**
|
||||
* @var array<class-string<Model>, class-string>
|
||||
*/
|
||||
protected const RESOURCES = [
|
||||
Page::class => PageResource::class,
|
||||
Project::class => ProjectResource::class,
|
||||
Service::class => ServiceResource::class,
|
||||
];
|
||||
|
||||
public static function url(Model $record): string
|
||||
{
|
||||
$resource = self::RESOURCES[$record::class] ?? null;
|
||||
|
||||
return $resource
|
||||
? $resource::getUrl('edit', ['record' => $record])
|
||||
: url('/admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* The permission that decides who can answer a review of this record.
|
||||
*
|
||||
* Derived from the table name, which matches the permission prefix for
|
||||
* every model that has a `publish` ability — the catalogue in
|
||||
* `Permissions::RESOURCES` is keyed the same way, and the unit test over
|
||||
* it is what keeps that true.
|
||||
*/
|
||||
public static function permission(Model $record): string
|
||||
{
|
||||
return $record->getTable().'.publish';
|
||||
}
|
||||
|
||||
/**
|
||||
* How many of these are waiting for the person reading the sidebar.
|
||||
*
|
||||
* Cached for a minute because Filament asks every resource for its badge
|
||||
* on every page render, and three counting queries to draw three numbers
|
||||
* would make the whole panel slower — the same reason the health badge is
|
||||
* cached. A minute is well inside the time it takes somebody to notice.
|
||||
*
|
||||
* @param class-string<Model> $model
|
||||
*/
|
||||
public static function waitingFor(string $model, ?User $user): ?int
|
||||
{
|
||||
if ($user === null || $user->cannot((new $model)->getTable().'.publish')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$count = Cache::remember(
|
||||
"reviews:waiting:{$model}:{$user->getAuthIdentifier()}",
|
||||
now()->addMinute(),
|
||||
fn () => $model::query()->awaitingReviewBy($user)->count(),
|
||||
);
|
||||
|
||||
return $count > 0 ? $count : null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user