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'), ]; } }