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:
127
app/Filament/RelationManagers/RevisionsRelationManager.php
Normal file
127
app/Filament/RelationManagers/RevisionsRelationManager.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\RelationManagers;
|
||||
|
||||
use App\Models\Revision;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Resources\RelationManagers\RelationManager;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Filters\SelectFilter;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
/**
|
||||
* Who changed what, on the record it happened to.
|
||||
*
|
||||
* One class for every resource: the history of a page and the history of a
|
||||
* testimonial differ only in what is in them.
|
||||
*/
|
||||
class RevisionsRelationManager extends RelationManager
|
||||
{
|
||||
protected static string $relationship = 'revisions';
|
||||
|
||||
public static function getTitle(Model $ownerRecord, string $pageClass): string
|
||||
{
|
||||
return __('panel.revisions.title');
|
||||
}
|
||||
|
||||
public static function getBadge(Model $ownerRecord, string $pageClass): ?string
|
||||
{
|
||||
$count = $ownerRecord->revisions()->count();
|
||||
|
||||
return $count > 0 ? (string) $count : null;
|
||||
}
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->defaultSort('id', 'desc')
|
||||
->modifyQueryUsing(fn ($query) => $query->with('user'))
|
||||
->columns([
|
||||
TextColumn::make('event')
|
||||
->label(__('panel.revisions.fields.event'))
|
||||
->badge()
|
||||
->formatStateUsing(fn (string $state) => __("panel.revisions.events.{$state}"))
|
||||
// Publishing is a separate permission because it is a
|
||||
// bigger act than editing; it reads differently here too.
|
||||
->color(fn (string $state) => match ($state) {
|
||||
'published' => 'success',
|
||||
'unpublished' => 'warning',
|
||||
'created' => 'info',
|
||||
'deleted', 'erased' => 'danger',
|
||||
default => 'gray',
|
||||
}),
|
||||
|
||||
TextColumn::make('user_name')
|
||||
->label(__('panel.revisions.fields.author'))
|
||||
->state(fn (Revision $record) => $record->author()),
|
||||
|
||||
TextColumn::make('changeset')
|
||||
->label(__('panel.revisions.fields.fields'))
|
||||
->state(fn (Revision $record) => $record->fields())
|
||||
->badge()
|
||||
->color('gray')
|
||||
->limitList(4)
|
||||
->placeholder('—'),
|
||||
|
||||
TextColumn::make('created_at')
|
||||
->label(__('panel.revisions.fields.when'))
|
||||
->since()
|
||||
->dateTimeTooltip()
|
||||
->sortable(),
|
||||
])
|
||||
->filters([
|
||||
SelectFilter::make('event')
|
||||
->label(__('panel.revisions.fields.event'))
|
||||
->options(fn () => collect(['created', 'updated', 'published', 'unpublished', 'deleted', 'restored', 'erased'])
|
||||
->mapWithKeys(fn (string $e) => [$e => __("panel.revisions.events.{$e}")])
|
||||
->all()),
|
||||
])
|
||||
->recordActions([
|
||||
Action::make('diff')
|
||||
->label(__('panel.revisions.actions.diff'))
|
||||
->icon('heroicon-o-eye')
|
||||
->modalHeading(fn (Revision $record) => __('panel.revisions.diff_heading', [
|
||||
'author' => $record->author(),
|
||||
'when' => $record->created_at?->isoFormat('LLL'),
|
||||
]))
|
||||
->modalSubmitAction(false)
|
||||
->modalContent(fn (Revision $record) => view('filament.revisions.diff', [
|
||||
'revision' => $record,
|
||||
])),
|
||||
|
||||
Action::make('restore')
|
||||
->label(__('panel.revisions.actions.restore'))
|
||||
->icon('heroicon-o-arrow-uturn-left')
|
||||
->color('warning')
|
||||
->requiresConfirmation()
|
||||
->modalDescription(__('panel.revisions.restore_confirm'))
|
||||
->authorize(fn () => (bool) Auth::user()?->can('update', $this->getOwnerRecord()))
|
||||
// Nothing to put back for a creation or a deletion, and
|
||||
// the newest revision already is the current state.
|
||||
->visible(fn (Revision $record) => filled($record->changeset)
|
||||
&& $record->isNot($this->getOwnerRecord()->revisions()->first()))
|
||||
->action(function (Revision $record): void {
|
||||
$owner = $this->getOwnerRecord();
|
||||
|
||||
// Restoring is itself a change, so it lands in the
|
||||
// history like any other — the way back is never lost.
|
||||
$owner->forceFill($record->previousValues())->save();
|
||||
|
||||
Notification::make()
|
||||
->title(__('panel.revisions.restored'))
|
||||
->success()
|
||||
->send();
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
public function isReadOnly(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user