feat: platform admin backend — controllers, services, routes, tests
Add cross-organisation admin API endpoints behind role:super_admin middleware: - AdminOrganisationController: CRUD with search, filter, billing_status management - AdminUserController: user management with role assignment across orgs - AdminStatsController: platform-wide aggregate statistics - AdminActivityLogController: filterable activity log viewer - AdminImpersonationController + ImpersonationService: user impersonation with token-based session management and activity logging - BillingStatus enum, form requests, API resources, 23 feature tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\AdminUpdateUserRequest;
|
||||
use App\Http\Resources\Admin\AdminUserResource;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
||||
|
||||
final class AdminUserController extends Controller
|
||||
{
|
||||
public function index(): AnonymousResourceCollection
|
||||
{
|
||||
$query = User::with('organisations');
|
||||
|
||||
if ($search = request('search')) {
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('first_name', 'like', "%{$search}%")
|
||||
->orWhere('last_name', 'like', "%{$search}%")
|
||||
->orWhere('email', 'like', "%{$search}%");
|
||||
});
|
||||
}
|
||||
|
||||
if ($organisationId = request('organisation_id')) {
|
||||
$query->whereHas('organisations', fn ($q) => $q->where('organisations.id', $organisationId));
|
||||
}
|
||||
|
||||
if ($role = request('role')) {
|
||||
$query->role($role);
|
||||
}
|
||||
|
||||
$query->orderBy('first_name')->orderBy('last_name');
|
||||
|
||||
return AdminUserResource::collection($query->paginate());
|
||||
}
|
||||
|
||||
public function show(User $user): JsonResponse
|
||||
{
|
||||
$user->load('organisations');
|
||||
|
||||
return $this->success(new AdminUserResource($user));
|
||||
}
|
||||
|
||||
public function update(AdminUpdateUserRequest $request, User $user): JsonResponse
|
||||
{
|
||||
$validated = $request->validated();
|
||||
$roles = $validated['roles'] ?? null;
|
||||
unset($validated['roles']);
|
||||
|
||||
if (! empty($validated)) {
|
||||
$user->update($validated);
|
||||
}
|
||||
|
||||
if ($roles !== null) {
|
||||
// Sync only platform-level roles, preserving org/event roles
|
||||
$platformRoles = ['super_admin', 'support_agent'];
|
||||
$currentRoles = $user->getRoleNames()->filter(fn ($r) => ! in_array($r, $platformRoles))->all();
|
||||
$user->syncRoles(array_merge($currentRoles, $roles));
|
||||
}
|
||||
|
||||
activity('admin')
|
||||
->causedBy(auth()->user())
|
||||
->performedOn($user)
|
||||
->event('admin.user.updated')
|
||||
->withProperties($request->validated())
|
||||
->log('Updated user ' . $user->full_name);
|
||||
|
||||
$user->load('organisations');
|
||||
|
||||
return $this->success(new AdminUserResource($user));
|
||||
}
|
||||
|
||||
public function destroy(User $user): JsonResponse
|
||||
{
|
||||
activity('admin')
|
||||
->causedBy(auth()->user())
|
||||
->performedOn($user)
|
||||
->event('admin.user.deleted')
|
||||
->log('Deleted user ' . $user->full_name);
|
||||
|
||||
$user->tokens()->delete();
|
||||
$user->delete();
|
||||
|
||||
return response()->json(null, 204);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user