Add comprehensive project documentation for ClinicPro in CLAUDE.md and README.md

- Introduced CLAUDE.md for internal guidance on project structure, commands, and architecture.
- Created README.md with detailed project overview, technology stack, directory structure, setup instructions, API endpoints, authentication flow, and external services.
This commit is contained in:
hamed
2026-06-10 11:12:15 +03:30
parent c7ae591e49
commit e9075e8c92
18 changed files with 3533 additions and 142 deletions
+486
View File
@@ -17,11 +17,13 @@ use App\Sms\Entity\SmsLog;
use App\Sms\Entity\SmsTemplate;
use App\Shared\Controller\BaseController;
use Doctrine\ORM\EntityManagerInterface;
use OpenApi\Attributes as OA;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[OA\Tag(name: 'Admin')]
#[IsGranted('ROLE_ADMIN')]
class AdminApiController extends BaseController
{
@@ -31,6 +33,46 @@ class AdminApiController extends BaseController
// ── Users ─────────────────────────────────────────────────────────────────
#[OA\Get(
path: '/api/v1/admin/users',
summary: 'List all users (paginated)',
security: [['bearerAuth' => []]],
parameters: [
new OA\Parameter(name: 'page', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 1)),
new OA\Parameter(name: 'limit', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 15)),
new OA\Parameter(name: 'search', in: 'query', required: false, schema: new OA\Schema(type: 'string')),
],
responses: [
new OA\Response(
response: 200,
description: 'Paginated list of users',
content: new OA\JsonContent(
properties: [
new OA\Property(property: 'success', type: 'boolean', example: true),
new OA\Property(property: 'data', type: 'array', items: new OA\Items(
properties: [
new OA\Property(property: 'uuid', type: 'string'),
new OA\Property(property: 'id', type: 'integer'),
new OA\Property(property: 'mobile_number', type: 'string'),
new OA\Property(property: 'name', type: 'string'),
new OA\Property(property: 'email', type: 'string'),
new OA\Property(property: 'roles', type: 'array', items: new OA\Items(type: 'string')),
new OA\Property(property: 'is_active', type: 'boolean'),
new OA\Property(property: 'created_at', type: 'string', format: 'date-time'),
]
)),
new OA\Property(property: 'meta', properties: [
new OA\Property(property: 'totalRecords', type: 'integer'),
new OA\Property(property: 'totalPages', type: 'integer'),
new OA\Property(property: 'currentPage', type: 'integer'),
], type: 'object'),
]
)
),
new OA\Response(response: 401, description: 'Unauthorized'),
new OA\Response(response: 403, description: 'Forbidden ROLE_ADMIN required'),
]
)]
#[Route('/api/v1/admin/users', methods: ['GET'])]
public function users(Request $request): JsonResponse
{
@@ -69,6 +111,48 @@ class AdminApiController extends BaseController
// ── Appointments ──────────────────────────────────────────────────────────
#[OA\Get(
path: '/api/v1/admin/appointments',
summary: 'List all appointments (paginated)',
security: [['bearerAuth' => []]],
parameters: [
new OA\Parameter(name: 'page', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 1)),
new OA\Parameter(name: 'limit', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 15)),
new OA\Parameter(name: 'search', in: 'query', required: false, schema: new OA\Schema(type: 'string')),
new OA\Parameter(name: 'status', in: 'query', required: false, schema: new OA\Schema(type: 'string')),
],
responses: [
new OA\Response(
response: 200,
description: 'Paginated list of appointments',
content: new OA\JsonContent(
properties: [
new OA\Property(property: 'success', type: 'boolean', example: true),
new OA\Property(property: 'data', type: 'array', items: new OA\Items(
properties: [
new OA\Property(property: 'uuid', type: 'string'),
new OA\Property(property: 'patient_name', type: 'string'),
new OA\Property(property: 'patient_mobile', type: 'string'),
new OA\Property(property: 'doctor_name', type: 'string'),
new OA\Property(property: 'appointment_date', type: 'string', format: 'date'),
new OA\Property(property: 'appointment_time', type: 'string', example: '14:30'),
new OA\Property(property: 'status', type: 'string'),
new OA\Property(property: 'amount', type: 'integer'),
new OA\Property(property: 'created_at', type: 'string', format: 'date-time'),
]
)),
new OA\Property(property: 'meta', properties: [
new OA\Property(property: 'totalRecords', type: 'integer'),
new OA\Property(property: 'totalPages', type: 'integer'),
new OA\Property(property: 'currentPage', type: 'integer'),
], type: 'object'),
]
)
),
new OA\Response(response: 401, description: 'Unauthorized'),
new OA\Response(response: 403, description: 'Forbidden ROLE_ADMIN required'),
]
)]
#[Route('/api/v1/admin/appointments', methods: ['GET'])]
public function appointments(Request $request): JsonResponse
{
@@ -118,6 +202,46 @@ class AdminApiController extends BaseController
// ── Payments ──────────────────────────────────────────────────────────────
#[OA\Get(
path: '/api/v1/admin/payments',
summary: 'List all payments (paginated)',
security: [['bearerAuth' => []]],
parameters: [
new OA\Parameter(name: 'page', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 1)),
new OA\Parameter(name: 'limit', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 15)),
new OA\Parameter(name: 'status', in: 'query', required: false, schema: new OA\Schema(type: 'string', enum: ['pending', 'success', 'failed'])),
],
responses: [
new OA\Response(
response: 200,
description: 'Paginated list of payments',
content: new OA\JsonContent(
properties: [
new OA\Property(property: 'success', type: 'boolean', example: true),
new OA\Property(property: 'data', type: 'array', items: new OA\Items(
properties: [
new OA\Property(property: 'uuid', type: 'string'),
new OA\Property(property: 'amount', type: 'integer'),
new OA\Property(property: 'status', type: 'string'),
new OA\Property(property: 'gateway', type: 'string'),
new OA\Property(property: 'ref_id', type: 'string', nullable: true),
new OA\Property(property: 'patient_mobile', type: 'string'),
new OA\Property(property: 'paid_at', type: 'string', format: 'date-time', nullable: true),
new OA\Property(property: 'created_at', type: 'string', format: 'date-time'),
]
)),
new OA\Property(property: 'meta', properties: [
new OA\Property(property: 'totalRecords', type: 'integer'),
new OA\Property(property: 'totalPages', type: 'integer'),
new OA\Property(property: 'currentPage', type: 'integer'),
], type: 'object'),
]
)
),
new OA\Response(response: 401, description: 'Unauthorized'),
new OA\Response(response: 403, description: 'Forbidden ROLE_ADMIN required'),
]
)]
#[Route('/api/v1/admin/payments', methods: ['GET'])]
public function payments(Request $request): JsonResponse
{
@@ -164,6 +288,49 @@ class AdminApiController extends BaseController
// ── Representations ───────────────────────────────────────────────────────
#[OA\Get(
path: '/api/v1/admin/representations',
summary: 'List all representations (paginated)',
security: [['bearerAuth' => []]],
parameters: [
new OA\Parameter(name: 'page', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 1)),
new OA\Parameter(name: 'limit', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 15)),
new OA\Parameter(name: 'search', in: 'query', required: false, schema: new OA\Schema(type: 'string')),
new OA\Parameter(name: 'city_id', in: 'query', required: false, schema: new OA\Schema(type: 'integer')),
],
responses: [
new OA\Response(
response: 200,
description: 'Paginated list of representations',
content: new OA\JsonContent(
properties: [
new OA\Property(property: 'success', type: 'boolean', example: true),
new OA\Property(property: 'data', type: 'array', items: new OA\Items(
properties: [
new OA\Property(property: 'uuid', type: 'string'),
new OA\Property(property: 'domain', type: 'string'),
new OA\Property(property: 'full_name', type: 'string'),
new OA\Property(property: 'mobile_number', type: 'string'),
new OA\Property(property: 'city_id', type: 'integer', nullable: true),
new OA\Property(property: 'city', type: 'string', nullable: true),
new OA\Property(property: 'commission_percent', type: 'number', format: 'float'),
new OA\Property(property: 'wallet_balance', type: 'integer'),
new OA\Property(property: 'is_active', type: 'boolean'),
new OA\Property(property: 'created_at', type: 'string', format: 'date-time'),
]
)),
new OA\Property(property: 'meta', properties: [
new OA\Property(property: 'totalRecords', type: 'integer'),
new OA\Property(property: 'totalPages', type: 'integer'),
new OA\Property(property: 'currentPage', type: 'integer'),
], type: 'object'),
]
)
),
new OA\Response(response: 401, description: 'Unauthorized'),
new OA\Response(response: 403, description: 'Forbidden ROLE_ADMIN required'),
]
)]
#[Route('/api/v1/admin/representations', methods: ['GET'])]
public function representations(Request $request): JsonResponse
{
@@ -211,6 +378,46 @@ class AdminApiController extends BaseController
// ── Secretaries ───────────────────────────────────────────────────────────
#[OA\Get(
path: '/api/v1/admin/secretaries',
summary: 'List all secretaries (paginated)',
security: [['bearerAuth' => []]],
parameters: [
new OA\Parameter(name: 'page', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 1)),
new OA\Parameter(name: 'limit', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 15)),
new OA\Parameter(name: 'search', in: 'query', required: false, schema: new OA\Schema(type: 'string')),
],
responses: [
new OA\Response(
response: 200,
description: 'Paginated list of secretaries',
content: new OA\JsonContent(
properties: [
new OA\Property(property: 'success', type: 'boolean', example: true),
new OA\Property(property: 'data', type: 'array', items: new OA\Items(
properties: [
new OA\Property(property: 'uuid', type: 'string'),
new OA\Property(property: 'user_name', type: 'string'),
new OA\Property(property: 'mobile_number', type: 'string'),
new OA\Property(property: 'doctor_name', type: 'string'),
new OA\Property(property: 'doctor_uuid', type: 'string'),
new OA\Property(property: 'is_active', type: 'boolean'),
new OA\Property(property: 'permissions', type: 'array', items: new OA\Items(type: 'string')),
new OA\Property(property: 'created_at', type: 'string', format: 'date-time'),
]
)),
new OA\Property(property: 'meta', properties: [
new OA\Property(property: 'totalRecords', type: 'integer'),
new OA\Property(property: 'totalPages', type: 'integer'),
new OA\Property(property: 'currentPage', type: 'integer'),
], type: 'object'),
]
)
),
new OA\Response(response: 401, description: 'Unauthorized'),
new OA\Response(response: 403, description: 'Forbidden ROLE_ADMIN required'),
]
)]
#[Route('/api/v1/admin/secretaries', methods: ['GET'])]
public function secretaries(Request $request): JsonResponse
{
@@ -255,6 +462,44 @@ class AdminApiController extends BaseController
// ── Ratings ───────────────────────────────────────────────────────────────
#[OA\Get(
path: '/api/v1/admin/rates',
summary: 'List all ratings (paginated)',
security: [['bearerAuth' => []]],
parameters: [
new OA\Parameter(name: 'page', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 1)),
new OA\Parameter(name: 'limit', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 15)),
new OA\Parameter(name: 'search', in: 'query', required: false, schema: new OA\Schema(type: 'string')),
],
responses: [
new OA\Response(
response: 200,
description: 'Paginated list of ratings',
content: new OA\JsonContent(
properties: [
new OA\Property(property: 'success', type: 'boolean', example: true),
new OA\Property(property: 'data', type: 'array', items: new OA\Items(
properties: [
new OA\Property(property: 'uuid', type: 'string'),
new OA\Property(property: 'patient_name', type: 'string'),
new OA\Property(property: 'doctor_name', type: 'string'),
new OA\Property(property: 'overall', type: 'integer'),
new OA\Property(property: 'score', type: 'integer'),
new OA\Property(property: 'created_at', type: 'string', format: 'date-time'),
]
)),
new OA\Property(property: 'meta', properties: [
new OA\Property(property: 'totalRecords', type: 'integer'),
new OA\Property(property: 'totalPages', type: 'integer'),
new OA\Property(property: 'currentPage', type: 'integer'),
], type: 'object'),
]
)
),
new OA\Response(response: 401, description: 'Unauthorized'),
new OA\Response(response: 403, description: 'Forbidden ROLE_ADMIN required'),
]
)]
#[Route('/api/v1/admin/rates', methods: ['GET'])]
public function rates(Request $request): JsonResponse
{
@@ -297,6 +542,47 @@ class AdminApiController extends BaseController
// ── Comments ──────────────────────────────────────────────────────────────
#[OA\Get(
path: '/api/v1/admin/comments',
summary: 'List all comments (paginated)',
security: [['bearerAuth' => []]],
parameters: [
new OA\Parameter(name: 'page', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 1)),
new OA\Parameter(name: 'limit', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 15)),
new OA\Parameter(name: 'search', in: 'query', required: false, schema: new OA\Schema(type: 'string')),
new OA\Parameter(name: 'status', in: 'query', required: false, schema: new OA\Schema(type: 'string', enum: ['pending', 'approved', 'rejected'])),
],
responses: [
new OA\Response(
response: 200,
description: 'Paginated list of comments',
content: new OA\JsonContent(
properties: [
new OA\Property(property: 'success', type: 'boolean', example: true),
new OA\Property(property: 'data', type: 'array', items: new OA\Items(
properties: [
new OA\Property(property: 'uuid', type: 'string'),
new OA\Property(property: 'patient_name', type: 'string'),
new OA\Property(property: 'doctor_name', type: 'string'),
new OA\Property(property: 'title', type: 'string'),
new OA\Property(property: 'body', type: 'string'),
new OA\Property(property: 'is_approved', type: 'boolean'),
new OA\Property(property: 'status', type: 'string'),
new OA\Property(property: 'created_at', type: 'string', format: 'date-time'),
]
)),
new OA\Property(property: 'meta', properties: [
new OA\Property(property: 'totalRecords', type: 'integer'),
new OA\Property(property: 'totalPages', type: 'integer'),
new OA\Property(property: 'currentPage', type: 'integer'),
], type: 'object'),
]
)
),
new OA\Response(response: 401, description: 'Unauthorized'),
new OA\Response(response: 403, description: 'Forbidden ROLE_ADMIN required'),
]
)]
#[Route('/api/v1/admin/comments', methods: ['GET'])]
public function comments(Request $request): JsonResponse
{
@@ -345,6 +631,44 @@ class AdminApiController extends BaseController
// ── SMS Logs ──────────────────────────────────────────────────────────────
#[OA\Get(
path: '/api/v1/admin/sms/logs',
summary: 'List SMS send logs (paginated)',
security: [['bearerAuth' => []]],
parameters: [
new OA\Parameter(name: 'page', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 1)),
new OA\Parameter(name: 'limit', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 15)),
],
responses: [
new OA\Response(
response: 200,
description: 'Paginated list of SMS logs',
content: new OA\JsonContent(
properties: [
new OA\Property(property: 'success', type: 'boolean', example: true),
new OA\Property(property: 'data', type: 'array', items: new OA\Items(
properties: [
new OA\Property(property: 'uuid', type: 'string'),
new OA\Property(property: 'recipient', type: 'string'),
new OA\Property(property: 'message', type: 'string'),
new OA\Property(property: 'status', type: 'string', enum: ['sent', 'failed']),
new OA\Property(property: 'provider', type: 'string'),
new OA\Property(property: 'sent_at', type: 'string', format: 'date-time'),
new OA\Property(property: 'created_at', type: 'string', format: 'date-time'),
]
)),
new OA\Property(property: 'meta', properties: [
new OA\Property(property: 'totalRecords', type: 'integer'),
new OA\Property(property: 'totalPages', type: 'integer'),
new OA\Property(property: 'currentPage', type: 'integer'),
], type: 'object'),
]
)
),
new OA\Response(response: 401, description: 'Unauthorized'),
new OA\Response(response: 403, description: 'Forbidden ROLE_ADMIN required'),
]
)]
#[Route('/api/v1/admin/sms/logs', methods: ['GET'])]
public function smsLogs(Request $request): JsonResponse
{
@@ -376,6 +700,47 @@ class AdminApiController extends BaseController
// ── Settlements ───────────────────────────────────────────────────────────
#[OA\Get(
path: '/api/v1/admin/settlements',
summary: 'List all settlement requests (paginated)',
security: [['bearerAuth' => []]],
parameters: [
new OA\Parameter(name: 'page', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 1)),
new OA\Parameter(name: 'limit', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 15)),
new OA\Parameter(name: 'status', in: 'query', required: false, schema: new OA\Schema(type: 'string', enum: ['pending', 'approved', 'rejected'])),
],
responses: [
new OA\Response(
response: 200,
description: 'Paginated list of settlement requests',
content: new OA\JsonContent(
properties: [
new OA\Property(property: 'success', type: 'boolean', example: true),
new OA\Property(property: 'data', type: 'array', items: new OA\Items(
properties: [
new OA\Property(property: 'uuid', type: 'string'),
new OA\Property(property: 'representation_name', type: 'string'),
new OA\Property(property: 'amount', type: 'integer'),
new OA\Property(property: 'status', type: 'string'),
new OA\Property(property: 'bank_card', type: 'string', nullable: true),
new OA\Property(property: 'bank_name', type: 'string', nullable: true),
new OA\Property(property: 'reject_reason', type: 'string', nullable: true),
new OA\Property(property: 'requested_at', type: 'string', format: 'date-time'),
new OA\Property(property: 'processed_at', type: 'string', format: 'date-time', nullable: true),
]
)),
new OA\Property(property: 'meta', properties: [
new OA\Property(property: 'totalRecords', type: 'integer'),
new OA\Property(property: 'totalPages', type: 'integer'),
new OA\Property(property: 'currentPage', type: 'integer'),
], type: 'object'),
]
)
),
new OA\Response(response: 401, description: 'Unauthorized'),
new OA\Response(response: 403, description: 'Forbidden ROLE_ADMIN required'),
]
)]
#[Route('/api/v1/admin/settlements', methods: ['GET'])]
public function settlements(Request $request): JsonResponse
{
@@ -418,6 +783,45 @@ class AdminApiController extends BaseController
// ── SMS Templates (paginated list with optional status filter) ────────────
#[OA\Get(
path: '/api/v1/admin/sms/sample-templates',
summary: 'List SMS sample templates (paginated)',
security: [['bearerAuth' => []]],
parameters: [
new OA\Parameter(name: 'page', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 1)),
new OA\Parameter(name: 'limit', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 15)),
new OA\Parameter(name: 'status', in: 'query', required: false, schema: new OA\Schema(type: 'string', enum: ['approved', 'pending'], default: 'approved')),
],
responses: [
new OA\Response(
response: 200,
description: 'Paginated list of SMS templates',
content: new OA\JsonContent(
properties: [
new OA\Property(property: 'success', type: 'boolean', example: true),
new OA\Property(property: 'data', type: 'array', items: new OA\Items(
properties: [
new OA\Property(property: 'uuid', type: 'string'),
new OA\Property(property: 'name', type: 'string'),
new OA\Property(property: 'body', type: 'string'),
new OA\Property(property: 'provider_code', type: 'string', nullable: true),
new OA\Property(property: 'status', type: 'string'),
new OA\Property(property: 'admin_note', type: 'string', nullable: true),
new OA\Property(property: 'created_at', type: 'string', format: 'date-time'),
]
)),
new OA\Property(property: 'meta', properties: [
new OA\Property(property: 'totalRecords', type: 'integer'),
new OA\Property(property: 'totalPages', type: 'integer'),
new OA\Property(property: 'currentPage', type: 'integer'),
], type: 'object'),
]
)
),
new OA\Response(response: 401, description: 'Unauthorized'),
new OA\Response(response: 403, description: 'Forbidden ROLE_ADMIN required'),
]
)]
#[Route('/api/v1/admin/sms/sample-templates', methods: ['GET'])]
public function smsSampleTemplates(Request $request): JsonResponse
{
@@ -454,6 +858,57 @@ class AdminApiController extends BaseController
// ── Dashboard Recent ──────────────────────────────────────────────────────
#[OA\Get(
path: '/api/v1/admin/dashboard/recent',
summary: 'Get recent dashboard data (appointments, payments, users)',
security: [['bearerAuth' => []]],
responses: [
new OA\Response(
response: 200,
description: 'Recent dashboard data',
content: new OA\JsonContent(
properties: [
new OA\Property(property: 'success', type: 'boolean', example: true),
new OA\Property(property: 'data', properties: [
new OA\Property(property: 'appointments', type: 'array', items: new OA\Items(
properties: [
new OA\Property(property: 'uuid', type: 'string'),
new OA\Property(property: 'slot_start', type: 'string', format: 'date-time'),
new OA\Property(property: 'status', type: 'string'),
new OA\Property(property: 'doctor_name', type: 'string'),
new OA\Property(property: 'user_mobile', type: 'string'),
new OA\Property(property: 'user_name', type: 'string'),
new OA\Property(property: 'created_at', type: 'string', format: 'date-time'),
]
)),
new OA\Property(property: 'payments', type: 'array', items: new OA\Items(
properties: [
new OA\Property(property: 'uuid', type: 'string'),
new OA\Property(property: 'amount', type: 'integer'),
new OA\Property(property: 'status', type: 'string'),
new OA\Property(property: 'gateway', type: 'string'),
new OA\Property(property: 'user_mobile', type: 'string'),
new OA\Property(property: 'user_name', type: 'string'),
new OA\Property(property: 'created_at', type: 'string', format: 'date-time'),
]
)),
new OA\Property(property: 'users', type: 'array', items: new OA\Items(
properties: [
new OA\Property(property: 'uuid', type: 'string'),
new OA\Property(property: 'mobile', type: 'string'),
new OA\Property(property: 'name', type: 'string'),
new OA\Property(property: 'email', type: 'string', nullable: true),
new OA\Property(property: 'created_at', type: 'string', format: 'date-time'),
]
)),
], type: 'object'),
]
)
),
new OA\Response(response: 401, description: 'Unauthorized'),
new OA\Response(response: 403, description: 'Forbidden ROLE_ADMIN required'),
]
)]
#[Route('/api/v1/admin/dashboard/recent', methods: ['GET'])]
public function dashboardRecent(): JsonResponse
{
@@ -521,6 +976,37 @@ class AdminApiController extends BaseController
// ── Dashboard Stats ───────────────────────────────────────────────────────
#[OA\Get(
path: '/api/v1/admin/dashboard/stats',
summary: 'Get aggregated dashboard statistics',
security: [['bearerAuth' => []]],
responses: [
new OA\Response(
response: 200,
description: 'Dashboard statistics',
content: new OA\JsonContent(
properties: [
new OA\Property(property: 'success', type: 'boolean', example: true),
new OA\Property(property: 'data', properties: [
new OA\Property(property: 'total_users', type: 'integer'),
new OA\Property(property: 'active_doctors', type: 'integer'),
new OA\Property(property: 'total_doctors', type: 'integer'),
new OA\Property(property: 'total_clinics', type: 'integer'),
new OA\Property(property: 'today_appointments', type: 'integer'),
new OA\Property(property: 'total_appointments', type: 'integer'),
new OA\Property(property: 'today_payments_count', type: 'integer'),
new OA\Property(property: 'today_payments_amount', type: 'integer'),
new OA\Property(property: 'total_payments_amount', type: 'integer'),
new OA\Property(property: 'pending_comments', type: 'integer'),
new OA\Property(property: 'pending_settlements', type: 'integer'),
], type: 'object'),
]
)
),
new OA\Response(response: 401, description: 'Unauthorized'),
new OA\Response(response: 403, description: 'Forbidden ROLE_ADMIN required'),
]
)]
#[Route('/api/v1/admin/dashboard/stats', methods: ['GET'])]
public function dashboardStats(): JsonResponse
{