Files
landing-api/app/Filament/RelationManagers/ReviewsRelationManager.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

99 lines
3.6 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Filament\RelationManagers;
use App\Filament\Support\ReviewActions;
use App\Models\ReviewComment;
use Filament\Actions\Action;
use Filament\Forms\Components\Textarea;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Model;
/**
* The conversation about one record, on the record.
*
* Sits beside the change history and answers a different question. A revision
* knows the title used to say something else; it cannot know that this went
* back because the photo is missing, because that sentence was never a field
* on the form. Kept together with the two review buttons so the reason and the
* decision are written in the same place, rather than the decision here and
* the reason in somebody's chat window.
*/
class ReviewsRelationManager extends RelationManager
{
protected static string $relationship = 'reviewComments';
public static function getTitle(Model $ownerRecord, string $pageClass): string
{
return __('panel.reviews.title');
}
/**
* The badge says "waiting", not "how many": a count of comments is
* trivia, and whether somebody is waiting for an answer is not.
*/
public static function getBadge(Model $ownerRecord, string $pageClass): ?string
{
return $ownerRecord->isAwaitingReview() ? '!' : null;
}
public static function getBadgeColor(Model $ownerRecord, string $pageClass): ?string
{
return 'warning';
}
public function table(Table $table): Table
{
return $table
->defaultSort('id', 'desc')
->modifyQueryUsing(fn ($query) => $query->with('user'))
->columns([
TextColumn::make('event')
->label(__('panel.reviews.fields.event'))
->badge(),
TextColumn::make('user_name')
->label(__('panel.reviews.fields.who'))
->state(fn (ReviewComment $record) => $record->author()),
TextColumn::make('body')
->label(__('panel.reviews.fields.note'))
->wrap()
->placeholder('—'),
TextColumn::make('created_at')
->label(__('panel.reviews.fields.when'))
->since()
->dateTimeTooltip()
->sortable(),
])
->headerActions([
ReviewActions::request(),
ReviewActions::returnForChanges(),
// Plain comments, for the half of a review that is neither a
// rejection nor an approval: a question, or an answer to one.
Action::make('comment')
->label(__('panel.reviews.actions.comment'))
->icon('heroicon-o-chat-bubble-left-ellipsis')
->color('gray')
->visible(fn () => $this->getOwnerRecord()->exists)
->schema([
Textarea::make('body')
->label(__('panel.reviews.fields.note'))
->rows(3)
->required()
->maxLength(2000),
])
->action(fn (array $data) => $this->getOwnerRecord()->comment($data['body'])),
])
->emptyStateHeading(__('panel.reviews.empty.heading'))
->emptyStateDescription(__('panel.reviews.empty.description'))
->emptyStateIcon('heroicon-o-chat-bubble-left-right');
}
}