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:
2026-08-19 17:55:27 +02:00
commit 3897113313
534 changed files with 64050 additions and 0 deletions

View File

@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace App\Models\Concerns;
use Illuminate\Database\Eloquent\Builder;
/**
* A record that is only true between two dates.
*
* The window is enforced by each model's `published` scope, so nothing that
* reads public content has to remember it. What lives here are the questions
* the panel asks about that window — is this waiting for its date, has it
* already passed — because a panel that shows "Published" for something the
* site is not showing is lying to the person who wrote it.
*/
trait HasPublicationWindow
{
/**
* Waiting for its date to arrive.
*/
public function isScheduled(): bool
{
return $this->published_at !== null && $this->published_at->isFuture();
}
/**
* Past its end date, and therefore off the site.
*/
public function hasExpired(): bool
{
return $this->unpublish_at !== null && $this->unpublish_at->isPast();
}
/**
* About to go, so it can be flagged before somebody notices it vanished.
*/
public function expiresSoon(int $days = 7): bool
{
return $this->unpublish_at !== null
&& $this->unpublish_at->isFuture()
&& $this->unpublish_at->lt(now()->addDays($days));
}
/**
* @param Builder<static> $query
* @return Builder<static>
*/
public function scopeScheduled(Builder $query): Builder
{
return $query->whereNotNull('published_at')->where('published_at', '>', now());
}
/**
* @param Builder<static> $query
* @return Builder<static>
*/
public function scopeExpired(Builder $query): Builder
{
return $query->whereNotNull('unpublish_at')->where('unpublish_at', '<=', now());
}
}