Wire admin studio SSR and search infrastructure
This commit is contained in:
160
app/Http/Controllers/Admin/AdminController.php
Normal file
160
app/Http/Controllers/Admin/AdminController.php
Normal file
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Enums\UserRole;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Artwork;
|
||||
use App\Models\Story;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
final class AdminController extends Controller
|
||||
{
|
||||
// ── Dashboard ────────────────────────────────────────────────────────────
|
||||
|
||||
public function dashboard(): Response
|
||||
{
|
||||
$stats = [
|
||||
'total_users' => User::count(),
|
||||
'new_users_today' => User::whereDate('created_at', today())->count(),
|
||||
'staff_count' => User::whereIn('role', ['admin', 'manager', 'editorial'])->count(),
|
||||
'moderator_count' => User::where('role', 'moderator')->count(),
|
||||
];
|
||||
|
||||
return Inertia::render('Admin/Dashboard', [
|
||||
'stats' => $stats,
|
||||
]);
|
||||
}
|
||||
|
||||
// ── Users ─────────────────────────────────────────────────────────────────
|
||||
|
||||
public function users(Request $request): Response
|
||||
{
|
||||
$search = $request->string('search')->trim()->toString();
|
||||
$roleFilter = $request->string('role')->trim()->toString();
|
||||
|
||||
$query = User::select('id', 'name', 'username', 'email', 'role', 'created_at', 'is_active')
|
||||
->orderByDesc('created_at');
|
||||
|
||||
if ($search !== '') {
|
||||
$query->where(function ($q) use ($search): void {
|
||||
$q->where('name', 'like', "%{$search}%")
|
||||
->orWhere('username', 'like', "%{$search}%")
|
||||
->orWhere('email', 'like', "%{$search}%");
|
||||
});
|
||||
}
|
||||
|
||||
if ($roleFilter !== '' && $roleFilter !== 'all') {
|
||||
$query->where('role', $roleFilter);
|
||||
}
|
||||
|
||||
$users = $query->paginate(50)->withQueryString();
|
||||
|
||||
return Inertia::render('Admin/Users/Index', [
|
||||
'users' => $users,
|
||||
'filters' => ['search' => $search, 'role' => $roleFilter],
|
||||
'roles' => collect(UserRole::cases())->map(fn ($r) => [
|
||||
'value' => $r->value,
|
||||
'label' => $r->label(),
|
||||
'badge' => $r->badgeClass(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
// ── Promote / Demote ──────────────────────────────────────────────────────
|
||||
|
||||
public function updateRole(Request $request, User $user): RedirectResponse
|
||||
{
|
||||
$request->validate([
|
||||
'role' => ['required', 'string', 'in:' . implode(',', array_column(UserRole::cases(), 'value'))],
|
||||
]);
|
||||
|
||||
/** @var \App\Models\User $actor */
|
||||
$actor = $request->user();
|
||||
|
||||
// Only admins can set the 'admin' role.
|
||||
if ($request->input('role') === UserRole::Admin->value && ! $actor->isAdmin()) {
|
||||
abort(403, 'Only admins can grant the Admin role.');
|
||||
}
|
||||
|
||||
// Prevent self-demotion.
|
||||
if ($actor->id === $user->id) {
|
||||
return back()->with('error', 'You cannot change your own role.');
|
||||
}
|
||||
|
||||
$user->update(['role' => $request->input('role')]);
|
||||
|
||||
return back()->with('success', "Role updated to \"{$request->input('role')}\" for {$user->name}.");
|
||||
}
|
||||
|
||||
// ── Stories ───────────────────────────────────────────────────────────────
|
||||
|
||||
public function stories(Request $request): Response
|
||||
{
|
||||
$stories = Story::with('creator:id,name,username')
|
||||
->select('id', 'title', 'status', 'published_at', 'creator_id')
|
||||
->orderByDesc('created_at')
|
||||
->paginate(50)
|
||||
->withQueryString();
|
||||
|
||||
return Inertia::render('Admin/Stories', [
|
||||
'stories' => $stories,
|
||||
]);
|
||||
}
|
||||
|
||||
// ── Artworks ──────────────────────────────────────────────────────────────
|
||||
|
||||
public function artworks(Request $request): Response
|
||||
{
|
||||
$artworks = Artwork::with('user:id,name,username')
|
||||
->select('id', 'title', 'artwork_status', 'created_at', 'user_id', 'hash', 'thumb_ext')
|
||||
->orderByDesc('created_at')
|
||||
->paginate(50)
|
||||
->withQueryString();
|
||||
|
||||
// Normalise status field and add thumb URL
|
||||
$artworks->getCollection()->transform(function ($artwork) {
|
||||
return [
|
||||
'id' => $artwork->id,
|
||||
'title' => $artwork->title,
|
||||
'status' => $artwork->artwork_status,
|
||||
'thumb' => $artwork->thumbUrl('sm') ?? null,
|
||||
'created_at' => $artwork->created_at,
|
||||
'user' => $artwork->user,
|
||||
];
|
||||
});
|
||||
|
||||
return Inertia::render('Admin/Artworks', [
|
||||
'artworks' => $artworks,
|
||||
]);
|
||||
}
|
||||
|
||||
// ── Username Queue ────────────────────────────────────────────────────────
|
||||
|
||||
public function usernameQueue(): Response
|
||||
{
|
||||
return Inertia::render('Admin/UsernameQueue');
|
||||
}
|
||||
|
||||
// ── Upload Queue ──────────────────────────────────────────────────────────
|
||||
|
||||
public function uploadQueue(): Response
|
||||
{
|
||||
return Inertia::render('Admin/UploadQueue');
|
||||
}
|
||||
|
||||
// ── Settings ──────────────────────────────────────────────────────────────
|
||||
|
||||
public function settings(): Response
|
||||
{
|
||||
return Inertia::render('Admin/Settings', [
|
||||
'settings' => [],
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user