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,94 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Api\V1\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\AdminUpdateOrganisationRequest;
|
||||
use App\Http\Resources\Admin\AdminOrganisationResource;
|
||||
use App\Models\Organisation;
|
||||
use App\Models\Person;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
||||
|
||||
final class AdminOrganisationController extends Controller
|
||||
{
|
||||
public function index(): AnonymousResourceCollection
|
||||
{
|
||||
$query = Organisation::withoutGlobalScopes()
|
||||
->withCount(['events', 'users']);
|
||||
|
||||
if ($search = request('search')) {
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('name', 'like', "%{$search}%")
|
||||
->orWhere('slug', 'like', "%{$search}%");
|
||||
});
|
||||
}
|
||||
|
||||
if ($billingStatus = request('billing_status')) {
|
||||
$query->where('billing_status', $billingStatus);
|
||||
}
|
||||
|
||||
$sortBy = request('sort', 'name');
|
||||
$sortDirection = request('direction', 'asc');
|
||||
$query->orderBy(
|
||||
in_array($sortBy, ['name', 'created_at']) ? $sortBy : 'name',
|
||||
$sortDirection === 'desc' ? 'desc' : 'asc',
|
||||
);
|
||||
|
||||
return AdminOrganisationResource::collection($query->paginate());
|
||||
}
|
||||
|
||||
public function show(string $organisationId): JsonResponse
|
||||
{
|
||||
$organisation = Organisation::withoutGlobalScopes()
|
||||
->withCount(['events', 'users'])
|
||||
->findOrFail($organisationId);
|
||||
|
||||
$organisation->total_persons = Person::withoutGlobalScopes()
|
||||
->whereIn('event_id', $organisation->events()->select('id'))
|
||||
->count();
|
||||
|
||||
return $this->success(new AdminOrganisationResource($organisation));
|
||||
}
|
||||
|
||||
public function store(): void
|
||||
{
|
||||
// Organisations are created via the regular endpoint
|
||||
abort(405);
|
||||
}
|
||||
|
||||
public function update(AdminUpdateOrganisationRequest $request, string $organisationId): JsonResponse
|
||||
{
|
||||
$organisation = Organisation::withoutGlobalScopes()->findOrFail($organisationId);
|
||||
|
||||
$organisation->update($request->validated());
|
||||
|
||||
activity('admin')
|
||||
->causedBy(auth()->user())
|
||||
->performedOn($organisation)
|
||||
->event('admin.organisation.updated')
|
||||
->withProperties($request->validated())
|
||||
->log('Updated organisation ' . $organisation->name);
|
||||
|
||||
$organisation->loadCount(['events', 'users']);
|
||||
|
||||
return $this->success(new AdminOrganisationResource($organisation));
|
||||
}
|
||||
|
||||
public function destroy(string $organisationId): JsonResponse
|
||||
{
|
||||
$organisation = Organisation::withoutGlobalScopes()->findOrFail($organisationId);
|
||||
|
||||
activity('admin')
|
||||
->causedBy(auth()->user())
|
||||
->performedOn($organisation)
|
||||
->event('admin.organisation.deleted')
|
||||
->log('Deleted organisation ' . $organisation->name);
|
||||
|
||||
$organisation->delete();
|
||||
|
||||
return response()->json(null, 204);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user