api/app/Filament/Resources/ModerationCases/Tables/ModerationCasesTable.php

203 lines
8.8 KiB
PHP

<?php
namespace App\Filament\Resources\ModerationCases\Tables;
use App\Enums\ModerationCaseStatus;
use App\Enums\ReportStatus;
use App\Models\MealPosts;
use App\Models\ModerationCase;
use App\Models\PostReviews;
use App\Models\User;
use Filament\Actions\Action;
use Filament\Actions\EditAction;
use Filament\Actions\ViewAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
class ModerationCasesTable
{
public static function configure(Table $table): Table
{
return $table
->columns([
TextColumn::make('id')
->label(__('admin.moderation_cases.fields.id'))
->copyable()
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('target_type')
->label(__('admin.moderation_cases.fields.target_type'))
->state(fn (ModerationCase $record): string => $record->targetLabel())
->badge(),
TextColumn::make('target_title')
->label(__('admin.moderation_cases.fields.target_title'))
->state(fn (ModerationCase $record): string => $record->targetTitle())
->searchable(query: function (Builder $query, string $search): Builder {
return $query->where(function (Builder $query) use ($search): void {
$query
->whereHasMorph('caseable', [MealPosts::class], fn (Builder $query): Builder => $query->where('title', 'like', "%{$search}%"))
->orWhereHasMorph('caseable', [PostReviews::class], fn (Builder $query): Builder => $query->where('comment', 'like', "%{$search}%"))
->orWhereHasMorph('caseable', [User::class], fn (Builder $query): Builder => $query->where('name', 'like', "%{$search}%"));
});
}),
TextColumn::make('status')
->label(__('admin.moderation_cases.fields.status'))
->badge()
->sortable(),
TextColumn::make('reports_count')
->label(__('admin.moderation_cases.fields.reports_count'))
->numeric()
->sortable(),
TextColumn::make('threshold')
->label(__('admin.moderation_cases.fields.threshold'))
->numeric()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('threshold_notified_at')
->label(__('admin.moderation_cases.fields.threshold_notified_at'))
->dateTime()
->sortable()
->placeholder(__('admin.moderation_cases.placeholders.empty')),
TextColumn::make('assignedTo.name')
->label(__('admin.moderation_cases.fields.assigned_to'))
->placeholder(__('admin.moderation_cases.placeholders.empty'))
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('resolved_at')
->label(__('admin.moderation_cases.fields.resolved_at'))
->dateTime()
->sortable()
->placeholder(__('admin.moderation_cases.placeholders.empty')),
TextColumn::make('created_at')
->label(__('admin.moderation_cases.fields.created_at'))
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
SelectFilter::make('status')
->label(__('admin.moderation_cases.fields.status'))
->options(ModerationCaseStatus::class),
SelectFilter::make('caseable_type')
->label(__('admin.moderation_cases.filters.target_type'))
->options([
MealPosts::class => __('admin.moderation_cases.targets.meal_post'),
PostReviews::class => __('admin.moderation_cases.targets.post_review'),
User::class => __('admin.moderation_cases.targets.user'),
]),
])
->recordActions([
ViewAction::make(),
EditAction::make(),
Action::make('markReviewing')
->label(__('admin.moderation_cases.actions.mark_reviewing'))
->visible(fn (ModerationCase $record): bool => $record->status !== ModerationCaseStatus::REVIEWING)
->action(fn (ModerationCase $record): bool => self::markReviewing($record)),
Action::make('hideMeal')
->label(__('admin.moderation_cases.actions.hide_meal'))
->color('warning')
->requiresConfirmation()
->visible(fn (ModerationCase $record): bool => $record->caseable_type === MealPosts::class
&& $record->caseable instanceof MealPosts
&& ! $record->caseable->isHidden())
->action(fn (ModerationCase $record): bool => self::hideMeal($record)),
Action::make('suspendUser')
->label(__('admin.moderation_cases.actions.suspend_user'))
->color('danger')
->requiresConfirmation()
->visible(fn (ModerationCase $record): bool => $record->caseable_type === User::class
&& $record->caseable instanceof User
&& ! $record->caseable->isSuspended())
->action(fn (ModerationCase $record): bool => self::suspendUser($record)),
Action::make('resolve')
->label(__('admin.moderation_cases.actions.resolve'))
->color('success')
->requiresConfirmation()
->visible(fn (ModerationCase $record): bool => $record->status !== ModerationCaseStatus::RESOLVED)
->action(fn (ModerationCase $record): bool => self::closeCase($record, ModerationCaseStatus::RESOLVED, ReportStatus::RESOLVED)),
Action::make('reject')
->label(__('admin.moderation_cases.actions.reject'))
->color('gray')
->requiresConfirmation()
->visible(fn (ModerationCase $record): bool => $record->status !== ModerationCaseStatus::REJECTED)
->action(fn (ModerationCase $record): bool => self::closeCase($record, ModerationCaseStatus::REJECTED, ReportStatus::REJECTED)),
])
->toolbarActions([
//
]);
}
private static function markReviewing(ModerationCase $record): bool
{
$record->forceFill([
'status' => ModerationCaseStatus::REVIEWING,
'assigned_to' => $record->assigned_to ?: auth()->id(),
])->save();
$record->reports()
->where('status', ReportStatus::OPEN->value)
->update([
'status' => ReportStatus::REVIEWING->value,
'reviewed_by' => auth()->id(),
'reviewed_at' => now(),
]);
return true;
}
private static function hideMeal(ModerationCase $record): bool
{
$mealPost = $record->caseable;
if (! $mealPost instanceof MealPosts) {
return false;
}
$mealPost->forceFill([
'hidden_at' => now(),
'hidden_by' => auth()->id(),
'hidden_reason' => __('admin.moderation_cases.actions.hide_meal'),
])->save();
return self::markReviewing($record);
}
private static function suspendUser(ModerationCase $record): bool
{
$user = $record->caseable;
if (! $user instanceof User) {
return false;
}
$user->forceFill([
'suspended_at' => now(),
'suspended_by' => auth()->id(),
'suspended_reason' => __('admin.moderation_cases.actions.suspend_user'),
])->save();
return self::markReviewing($record);
}
private static function closeCase(
ModerationCase $record,
ModerationCaseStatus $caseStatus,
ReportStatus $reportStatus,
): bool {
$record->forceFill([
'status' => $caseStatus,
'assigned_to' => $record->assigned_to ?: auth()->id(),
'resolved_by' => auth()->id(),
'resolved_at' => now(),
])->save();
$record->reports()->update([
'status' => $reportStatus->value,
'reviewed_by' => auth()->id(),
'reviewed_at' => now(),
]);
return true;
}
}