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:
@@ -10,12 +10,14 @@ use App\Representation\Service\JalaliDateService;
|
||||
use App\Shared\Constant\ErrorCodes;
|
||||
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\CurrentUser;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
#[OA\Tag(name: 'Representations')]
|
||||
#[IsGranted('IS_AUTHENTICATED_FULLY')]
|
||||
class RepresentationController extends BaseController
|
||||
{
|
||||
@@ -28,6 +30,54 @@ class RepresentationController extends BaseController
|
||||
|
||||
// ── CRUD ──────────────────────────────────────────────────────────────────
|
||||
|
||||
#[OA\Post(
|
||||
path: '/api/v1/representation',
|
||||
summary: 'Create a new representation (ROLE_ADMIN)',
|
||||
security: [['bearerAuth' => []]],
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(
|
||||
required: ['full_name', 'mobile_number'],
|
||||
properties: [
|
||||
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: 'commission_percent', type: 'number', format: 'float', nullable: true),
|
||||
new OA\Property(property: 'bank_account', type: 'object', nullable: true),
|
||||
]
|
||||
)
|
||||
),
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 201,
|
||||
description: 'Representation created',
|
||||
content: new OA\JsonContent(
|
||||
properties: [
|
||||
new OA\Property(property: 'success', type: 'boolean', example: true),
|
||||
new OA\Property(property: 'data', type: 'object'),
|
||||
]
|
||||
)
|
||||
),
|
||||
new OA\Response(
|
||||
response: 409,
|
||||
description: 'Conflict – user is already a representation',
|
||||
content: new OA\JsonContent(
|
||||
properties: [
|
||||
new OA\Property(property: 'success', type: 'boolean', example: false),
|
||||
new OA\Property(property: 'errors', type: 'array', items: new OA\Items(
|
||||
properties: [
|
||||
new OA\Property(property: 'code', type: 'string'),
|
||||
new OA\Property(property: 'message', type: 'string'),
|
||||
]
|
||||
)),
|
||||
]
|
||||
)
|
||||
),
|
||||
new OA\Response(response: 401, description: 'Unauthorized'),
|
||||
new OA\Response(response: 403, description: 'Forbidden – ROLE_ADMIN required'),
|
||||
new OA\Response(response: 422, description: 'Validation error'),
|
||||
]
|
||||
)]
|
||||
#[IsGranted('ROLE_ADMIN')]
|
||||
#[Route('/api/v1/representation', methods: ['POST'])]
|
||||
public function create(Request $request): JsonResponse
|
||||
@@ -62,6 +112,29 @@ class RepresentationController extends BaseController
|
||||
return $this->success(['data' => $rep->toArray()], 201);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/api/v1/representation/{uuid}',
|
||||
summary: 'Get a representation by UUID (own or admin)',
|
||||
security: [['bearerAuth' => []]],
|
||||
parameters: [
|
||||
new OA\Parameter(name: 'uuid', in: 'path', required: true, schema: new OA\Schema(type: 'string')),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: 'Representation details',
|
||||
content: new OA\JsonContent(
|
||||
properties: [
|
||||
new OA\Property(property: 'success', type: 'boolean', example: true),
|
||||
new OA\Property(property: 'data', type: 'object'),
|
||||
]
|
||||
)
|
||||
),
|
||||
new OA\Response(response: 401, description: 'Unauthorized'),
|
||||
new OA\Response(response: 403, description: 'Forbidden'),
|
||||
new OA\Response(response: 404, description: 'Not found'),
|
||||
]
|
||||
)]
|
||||
#[Route('/api/v1/representation/{uuid}', methods: ['GET'])]
|
||||
public function get(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
@@ -77,6 +150,41 @@ class RepresentationController extends BaseController
|
||||
return $this->success(['data' => $rep->toArray()]);
|
||||
}
|
||||
|
||||
#[OA\Patch(
|
||||
path: '/api/v1/representation/{uuid}',
|
||||
summary: 'Update a representation (own or admin)',
|
||||
security: [['bearerAuth' => []]],
|
||||
requestBody: new OA\RequestBody(
|
||||
required: false,
|
||||
content: new OA\JsonContent(
|
||||
properties: [
|
||||
new OA\Property(property: 'full_name', type: 'string'),
|
||||
new OA\Property(property: 'city_id', type: 'integer', nullable: true),
|
||||
new OA\Property(property: 'bank_account', type: 'object', nullable: true),
|
||||
new OA\Property(property: 'commission_percent', type: 'number', format: 'float'),
|
||||
new OA\Property(property: 'active', type: 'boolean'),
|
||||
]
|
||||
)
|
||||
),
|
||||
parameters: [
|
||||
new OA\Parameter(name: 'uuid', in: 'path', required: true, schema: new OA\Schema(type: 'string')),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: 'Representation updated',
|
||||
content: new OA\JsonContent(
|
||||
properties: [
|
||||
new OA\Property(property: 'success', type: 'boolean', example: true),
|
||||
new OA\Property(property: 'data', type: 'object'),
|
||||
]
|
||||
)
|
||||
),
|
||||
new OA\Response(response: 401, description: 'Unauthorized'),
|
||||
new OA\Response(response: 403, description: 'Forbidden'),
|
||||
new OA\Response(response: 404, description: 'Not found'),
|
||||
]
|
||||
)]
|
||||
#[Route('/api/v1/representation/{uuid}', methods: ['PATCH'])]
|
||||
public function update(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
@@ -101,6 +209,31 @@ class RepresentationController extends BaseController
|
||||
return $this->success(['data' => $rep->toArray()]);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/api/v1/representation/{uuid}',
|
||||
summary: 'Delete a representation (ROLE_ADMIN)',
|
||||
security: [['bearerAuth' => []]],
|
||||
parameters: [
|
||||
new OA\Parameter(name: 'uuid', in: 'path', required: true, schema: new OA\Schema(type: 'string')),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: 'Representation deleted',
|
||||
content: new OA\JsonContent(
|
||||
properties: [
|
||||
new OA\Property(property: 'success', type: 'boolean', example: true),
|
||||
new OA\Property(property: 'data', properties: [
|
||||
new OA\Property(property: 'message', type: 'string'),
|
||||
], type: 'object'),
|
||||
]
|
||||
)
|
||||
),
|
||||
new OA\Response(response: 401, description: 'Unauthorized'),
|
||||
new OA\Response(response: 403, description: 'Forbidden – ROLE_ADMIN required'),
|
||||
new OA\Response(response: 404, description: 'Not found'),
|
||||
]
|
||||
)]
|
||||
#[IsGranted('ROLE_ADMIN')]
|
||||
#[Route('/api/v1/representation/{uuid}', methods: ['DELETE'])]
|
||||
public function delete(string $uuid): JsonResponse
|
||||
@@ -116,6 +249,41 @@ class RepresentationController extends BaseController
|
||||
|
||||
// ── Dashboard: monthly stats ──────────────────────────────────────────────
|
||||
|
||||
#[OA\Get(
|
||||
path: '/api/v1/representation/{uuid}/dashboard/monthly',
|
||||
summary: 'Get monthly dashboard stats for a representation',
|
||||
security: [['bearerAuth' => []]],
|
||||
parameters: [
|
||||
new OA\Parameter(name: 'uuid', in: 'path', required: true, schema: new OA\Schema(type: 'string')),
|
||||
new OA\Parameter(name: 'year', in: 'query', required: false, description: 'Jalali year', schema: new OA\Schema(type: 'integer')),
|
||||
new OA\Parameter(name: 'month', in: 'query', required: false, description: 'Jalali month (1–12)', schema: new OA\Schema(type: 'integer', minimum: 1, maximum: 12)),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: 'Monthly 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: 'period', properties: [
|
||||
new OA\Property(property: 'jalali_year', type: 'integer'),
|
||||
new OA\Property(property: 'jalali_month', type: 'integer'),
|
||||
], type: 'object'),
|
||||
new OA\Property(property: 'stats', properties: [
|
||||
new OA\Property(property: 'total_payments', type: 'integer'),
|
||||
new OA\Property(property: 'total_revenue_rials', type: 'integer'),
|
||||
new OA\Property(property: 'total_appointments', type: 'integer'),
|
||||
], type: 'object'),
|
||||
], type: 'object'),
|
||||
]
|
||||
)
|
||||
),
|
||||
new OA\Response(response: 401, description: 'Unauthorized'),
|
||||
new OA\Response(response: 403, description: 'Forbidden'),
|
||||
new OA\Response(response: 404, description: 'Not found'),
|
||||
]
|
||||
)]
|
||||
#[Route('/api/v1/representation/{uuid}/dashboard/monthly', methods: ['GET'])]
|
||||
public function dashboardMonthly(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
@@ -139,6 +307,49 @@ class RepresentationController extends BaseController
|
||||
]);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/api/v1/representation/{uuid}/dashboard/yearly',
|
||||
summary: 'Get yearly dashboard stats for a representation',
|
||||
security: [['bearerAuth' => []]],
|
||||
parameters: [
|
||||
new OA\Parameter(name: 'uuid', in: 'path', required: true, schema: new OA\Schema(type: 'string')),
|
||||
new OA\Parameter(name: 'year', in: 'query', required: false, description: 'Jalali year', schema: new OA\Schema(type: 'integer')),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: 'Yearly statistics broken down by month',
|
||||
content: new OA\JsonContent(
|
||||
properties: [
|
||||
new OA\Property(property: 'success', type: 'boolean', example: true),
|
||||
new OA\Property(property: 'data', properties: [
|
||||
new OA\Property(property: 'period', properties: [
|
||||
new OA\Property(property: 'jalali_year', type: 'integer'),
|
||||
], type: 'object'),
|
||||
new OA\Property(property: 'months', type: 'array', items: new OA\Items(
|
||||
properties: [
|
||||
new OA\Property(property: 'jalali_month', type: 'integer'),
|
||||
new OA\Property(property: 'stats', properties: [
|
||||
new OA\Property(property: 'total_payments', type: 'integer'),
|
||||
new OA\Property(property: 'total_revenue_rials', type: 'integer'),
|
||||
new OA\Property(property: 'total_appointments', type: 'integer'),
|
||||
], type: 'object'),
|
||||
]
|
||||
)),
|
||||
new OA\Property(property: 'totals', properties: [
|
||||
new OA\Property(property: 'total_payments', type: 'integer'),
|
||||
new OA\Property(property: 'total_revenue_rials', type: 'integer'),
|
||||
new OA\Property(property: 'total_appointments', type: 'integer'),
|
||||
], type: 'object'),
|
||||
], type: 'object'),
|
||||
]
|
||||
)
|
||||
),
|
||||
new OA\Response(response: 401, description: 'Unauthorized'),
|
||||
new OA\Response(response: 403, description: 'Forbidden'),
|
||||
new OA\Response(response: 404, description: 'Not found'),
|
||||
]
|
||||
)]
|
||||
#[Route('/api/v1/representation/{uuid}/dashboard/yearly', methods: ['GET'])]
|
||||
public function dashboardYearly(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user