Files
fotospiel-app/app/Filament/Resources/TenantFeedbackResource/Tables/TenantFeedbackTable.php
Codex Agent 117250879b
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled
Add superadmin moderation queues
2026-01-01 18:52:32 +01:00

209 lines
9.2 KiB
PHP

<?php
namespace App\Filament\Resources\TenantFeedbackResource\Tables;
use App\Models\TenantFeedback;
use Filament\Actions\Action;
use Filament\Actions\BulkAction;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\ViewAction;
use Filament\Facades\Filament;
use Filament\Forms\Components\Textarea;
use Filament\Tables;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Str;
class TenantFeedbackTable
{
public static function configure(Table $table): Table
{
return $table
->defaultSort('created_at', 'desc')
->columns([
Tables\Columns\TextColumn::make('created_at')
->label(__('Eingegangen'))
->since()
->sortable(),
Tables\Columns\TextColumn::make('status')
->label(__('admin.feedback.table.status'))
->badge()
->color(fn (?string $state) => match ($state) {
'resolved' => 'success',
'hidden' => 'gray',
'deleted' => 'danger',
default => 'warning',
})
->formatStateUsing(fn (?string $state) => self::statusLabels()[$state] ?? '—')
->sortable(),
Tables\Columns\TextColumn::make('tenant.name')
->label(__('Tenant'))
->searchable()
->limit(30),
Tables\Columns\TextColumn::make('event.name')
->label(__('Event'))
->limit(30)
->toggleable(),
Tables\Columns\TextColumn::make('category')
->label(__('Kategorie'))
->badge()
->formatStateUsing(fn (string $state) => Str::headline($state))
->sortable(),
Tables\Columns\TextColumn::make('sentiment')
->label(__('Stimmung'))
->badge()
->color(fn (?string $state) => match ($state) {
'positive' => 'success',
'neutral' => 'warning',
'negative' => 'danger',
default => 'gray',
})
->formatStateUsing(fn (?string $state) => $state ? Str::headline($state) : '—')
->sortable(),
Tables\Columns\TextColumn::make('rating')
->label(__('Rating'))
->formatStateUsing(fn (?int $state) => $state ? sprintf('%d/5', $state) : '—')
->sortable(),
Tables\Columns\TextColumn::make('message')
->label(__('Nachricht'))
->limit(60)
->toggleable(isToggledHiddenByDefault: true),
Tables\Columns\TextColumn::make('moderator.name')
->label(__('admin.feedback.table.moderated_by'))
->placeholder('—')
->toggleable(isToggledHiddenByDefault: true),
Tables\Columns\TextColumn::make('moderated_at')
->label(__('admin.feedback.table.moderated_at'))
->since()
->placeholder('—')
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
SelectFilter::make('status')
->label(__('admin.feedback.filters.status'))
->options(self::statusLabels())
->default('pending'),
SelectFilter::make('sentiment')
->label(__('Stimmung'))
->options([
'positive' => __('Positiv'),
'neutral' => __('Neutral'),
'negative' => __('Negativ'),
]),
SelectFilter::make('category')
->label(__('Kategorie'))
->options(fn () => TenantFeedback::query()
->whereNotNull('category')
->orderBy('category')
->pluck('category', 'category')
->toArray()),
])
->recordActions([
ViewAction::make(),
Action::make('resolve')
->label(__('admin.feedback.actions.resolve'))
->color('success')
->icon('heroicon-o-check-circle')
->visible(fn (TenantFeedback $record) => $record->status === 'pending')
->form([
self::moderationNotesField(false),
])
->requiresConfirmation()
->action(fn (TenantFeedback $record, array $data) => self::applyModeration($record, 'resolved', $data['moderation_notes'] ?? null)),
Action::make('hide')
->label(__('admin.feedback.actions.hide'))
->color('gray')
->icon('heroicon-o-eye-slash')
->visible(fn (TenantFeedback $record) => $record->status !== 'hidden' && $record->status !== 'deleted')
->form([
self::moderationNotesField(false),
])
->requiresConfirmation()
->action(fn (TenantFeedback $record, array $data) => self::applyModeration($record, 'hidden', $data['moderation_notes'] ?? null)),
Action::make('delete')
->label(__('admin.feedback.actions.delete'))
->color('danger')
->icon('heroicon-o-trash')
->visible(fn (TenantFeedback $record) => $record->status !== 'deleted')
->form([
self::moderationNotesField(true),
])
->requiresConfirmation()
->action(fn (TenantFeedback $record, array $data) => self::applyModeration($record, 'deleted', $data['moderation_notes'] ?? null)),
])
->toolbarActions([
BulkActionGroup::make([
BulkAction::make('resolve')
->label(__('admin.feedback.actions.resolve_selected'))
->icon('heroicon-o-check-circle')
->color('success')
->form([
self::moderationNotesField(false),
])
->requiresConfirmation()
->action(fn (Collection $records, array $data) => self::applyModerationToRecords($records, 'resolved', $data['moderation_notes'] ?? null)),
BulkAction::make('hide')
->label(__('admin.feedback.actions.hide_selected'))
->icon('heroicon-o-eye-slash')
->color('gray')
->form([
self::moderationNotesField(false),
])
->requiresConfirmation()
->action(fn (Collection $records, array $data) => self::applyModerationToRecords($records, 'hidden', $data['moderation_notes'] ?? null)),
BulkAction::make('delete')
->label(__('admin.feedback.actions.delete_selected'))
->icon('heroicon-o-trash')
->color('danger')
->form([
self::moderationNotesField(true),
])
->requiresConfirmation()
->action(fn (Collection $records, array $data) => self::applyModerationToRecords($records, 'deleted', $data['moderation_notes'] ?? null)),
]),
]);
}
private static function moderationNotesField(bool $required): Textarea
{
return Textarea::make('moderation_notes')
->label(__('admin.feedback.fields.moderation_notes'))
->maxLength(1000)
->rows(3)
->required($required);
}
private static function applyModeration(TenantFeedback $record, string $status, ?string $notes): void
{
$record->update([
'status' => $status,
'moderation_notes' => $notes,
'moderated_at' => now(),
'moderated_by' => Filament::auth()->id(),
]);
}
private static function applyModerationToRecords(Collection $records, string $status, ?string $notes): int
{
return TenantFeedback::query()
->whereIn('id', $records->pluck('id'))
->update([
'status' => $status,
'moderation_notes' => $notes,
'moderated_at' => now(),
'moderated_by' => Filament::auth()->id(),
]);
}
private static function statusLabels(): array
{
return [
'pending' => __('admin.feedback.status.pending'),
'resolved' => __('admin.feedback.status.resolved'),
'hidden' => __('admin.feedback.status.hidden'),
'deleted' => __('admin.feedback.status.deleted'),
];
}
}