with([ 'user:id,name,username,level,email', 'tags:id,name,slug', 'categories:id,name,slug', ]) ->where('artwork_status', 'review') ->where('is_approved', false) ->latest('created_at') ->limit(100) ->get([ 'id', 'user_id', 'title', 'description', 'hash', 'thumb_ext', 'artwork_status', 'moderation_note', 'created_at', 'slug', 'is_mature', 'maturity_status', 'visibility', ]) ->map(fn (Artwork $artwork): array => $this->present($artwork)) ->values(); return response()->json(['data' => $artworks], Response::HTTP_OK); } public function approve(int $id, Request $request, NotificationService $notifications): JsonResponse { $artwork = Artwork::query()->with('user')->where('artwork_status', 'review')->find($id); if (! $artwork) { return response()->json(['message' => 'Artwork not found in review queue.'], Response::HTTP_NOT_FOUND); } $artwork->forceFill([ 'is_approved' => true, 'is_public' => $artwork->visibility !== Artwork::VISIBILITY_PRIVATE, 'artwork_status' => 'published', 'published_at' => now(), 'approval_source' => 'moderator', 'moderated_at' => now(), 'moderated_by' => $request->user()->id, 'moderation_note' => $request->input('note'), ])->save(); IndexArtworkJob::dispatch((int) $artwork->id); if ($artwork->user) { $notifications->notifyArtworkApproved($artwork->user, $request->user(), $artwork); } return response()->json(['success' => true, 'id' => $artwork->id, 'status' => 'published']); } public function reject(int $id, Request $request, NotificationService $notifications): JsonResponse { $artwork = Artwork::query()->with('user')->where('artwork_status', 'review')->find($id); if (! $artwork) { return response()->json(['message' => 'Artwork not found in review queue.'], Response::HTTP_NOT_FOUND); } $note = (string) $request->input('note', 'Rejected during upload moderation.'); $artwork->forceFill([ 'is_approved' => false, 'is_public' => false, 'artwork_status' => 'rejected', 'published_at' => null, 'moderated_at' => now(), 'moderated_by' => $request->user()->id, 'moderation_note' => $note, ])->save(); IndexArtworkJob::dispatch((int) $artwork->id); if ($artwork->user) { $notifications->notifyArtworkRejected($artwork->user, $request->user(), $artwork, $note); } return response()->json(['success' => true, 'id' => $artwork->id, 'status' => 'rejected']); } private function present(Artwork $artwork): array { $previewUrl = $artwork->thumbUrl('md') ?: $artwork->thumbUrl('sm'); $previewLgUrl = $artwork->thumbUrl('lg') ?: $previewUrl; return [ 'id' => (int) $artwork->id, 'title' => (string) ($artwork->title ?: '(untitled artwork)'), 'description' => (string) ($artwork->description ?? ''), 'type' => 'artwork', 'preview_url' => $previewUrl, 'preview_lg_url' => $previewLgUrl, 'tags' => $artwork->tags ->map(fn ($tag): string => trim((string) ($tag->name ?: $tag->slug))) ->filter() ->values() ->all(), 'categories' => $artwork->categories ->map(fn ($category): string => trim((string) ($category->name ?: $category->slug))) ->filter() ->values() ->all(), 'user' => $artwork->user ? [ 'id' => (int) $artwork->user->id, 'name' => (string) $artwork->user->name, 'username' => $artwork->user->username, ] : null, 'slug' => $artwork->slug, 'is_mature' => (bool) $artwork->is_mature, 'maturity_status' => $artwork->maturity_status, 'visibility' => $artwork->visibility, 'moderation_note' => $artwork->moderation_note, 'created_at' => optional($artwork->created_at)?->toISOString(), ]; } }