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>
99 lines
3.6 KiB
PHP
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');
|
|
}
|
|
}
|