Files
landing-api/app/Support/Health/Checks/StaleReviews.php
Josep Piguillem 3897113313 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>
2026-08-19 17:55:27 +02:00

85 lines
2.3 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Support\Health\Checks;
use App\Models\Page;
use App\Models\Project;
use App\Models\Service;
use App\Support\ContentTypes;
use App\Support\Health\Check;
use App\Support\Health\Finding;
use App\Support\Health\Severity;
use Illuminate\Database\Eloquent\Model;
/**
* Work that was handed over and never answered.
*
* The same shape as the unanswered-submissions check, and for the same reason:
* somebody is waiting, and nothing else in the panel would ever say so. It is
* the failure mode a review step introduces — before it existed, an editor who
* could not publish was stuck but knew it; now they are waiting and think
* something is happening.
*
* A review left for a week has stopped being a handover and become a piece of
* work nobody owns.
*/
class StaleReviews implements Check
{
protected const DAYS = 7;
/**
* The models where publishing is a permission of its own, which is exactly
* where a review can exist.
*
* @var array<int, class-string<Model>>
*/
protected const REVIEWABLE = [Page::class, Project::class, Service::class];
public function key(): string
{
return 'stale-reviews';
}
/**
* Whoever can publish something is who a stale review is waiting for, so
* seeing the count is the same question as being able to end it. Pages
* stand in for the three: a role that can publish none of them has no
* business being told what is queued.
*/
public function permission(): ?string
{
return 'pages.publish';
}
public function run(): Finding
{
$cutoff = now()->subDays(self::DAYS);
$samples = [];
$count = 0;
foreach (self::REVIEWABLE as $model) {
$stale = $model::query()
->awaitingReview()
->where('review_requested_at', '<', $cutoff)
->orderBy('review_requested_at')
->get();
$count += $stale->count();
foreach ($stale as $record) {
$samples[] = ContentTypes::title($record)
.' ('.$record->review_requested_at->diffForHumans().')';
}
}
return Finding::problem(
$this->key(),
$count,
Severity::Warning,
samples: $samples,
);
}
}