203 lines
8.8 KiB
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;
|
|
}
|
|
}
|