feat(representation): add endpoints for doctor statistics and toggling doctor status
This commit is contained in:
@@ -114,10 +114,14 @@ export default function DoctorsPage() {
|
||||
// ── Queries ──
|
||||
|
||||
const statsQ = useQuery({
|
||||
queryKey: ['doctors-stats'],
|
||||
queryFn: () => api.get<ApiResponse<DoctorStats>>('/api/v1/admin/doctors/stats'),
|
||||
queryKey: ['doctors-stats', isRepresentation],
|
||||
queryFn: () => {
|
||||
const url = isRepresentation
|
||||
? '/api/v1/representation/doctors/stats'
|
||||
: '/api/v1/admin/doctors/stats';
|
||||
return api.get<ApiResponse<DoctorStats>>(url);
|
||||
},
|
||||
staleTime: 30_000,
|
||||
enabled: !isRepresentation,
|
||||
});
|
||||
|
||||
const specialtiesQ = useQuery({
|
||||
@@ -163,7 +167,10 @@ export default function DoctorsPage() {
|
||||
});
|
||||
|
||||
const toggleMut = useMutation({
|
||||
mutationFn: (uuid: string) => api.post<ApiResponse<{ is_active: boolean }>>(`/api/v1/admin/doctors/${uuid}/status`, {}),
|
||||
mutationFn: (uuid: string) => {
|
||||
const base = isRepresentation ? '/api/v1/representation/doctors' : '/api/v1/admin/doctors';
|
||||
return api.post<ApiResponse<{ is_active: boolean }>>(`${base}/${uuid}/status`, {});
|
||||
},
|
||||
onSuccess: () => {
|
||||
toast.success('وضعیت پزشک تغییر کرد');
|
||||
qc.invalidateQueries({ queryKey: ['admin-doctors'] });
|
||||
|
||||
@@ -398,6 +398,48 @@ Get yearly earnings dashboard for a representation.
|
||||
|
||||
---
|
||||
|
||||
### GET `/api/v1/representation/doctors/stats`
|
||||
|
||||
آمار پزشکانِ ثبتشده توسط نمایندهی جاری (فقط `representation_id = نمایندهی کاربر جاری`). شکل پاسخ سازگار با `GET /api/v1/admin/doctors/stats` (بدون `top_specialty`). فرانتاند کارتهای «کل پزشکان / فعال / غیرفعال / مرد / زن» را از این endpoint برای نقش نماینده پر میکند.
|
||||
|
||||
> **Permission:** `ROLE_REPRESENTATION` — id نماینده از `#[CurrentUser]`.
|
||||
|
||||
#### Response `200`
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": { "total": 12, "active": 9, "inactive": 3, "male": 7, "female": 5 }
|
||||
}
|
||||
```
|
||||
#### Errors
|
||||
| Code | HTTP | Description |
|
||||
|------|------|-------------|
|
||||
| `ERR_NOT_FOUND_001` | 404 | کاربر جاری نماینده نیست |
|
||||
|
||||
---
|
||||
|
||||
### POST `/api/v1/representation/doctors/{uuid}/status`
|
||||
|
||||
فعال/غیرفعال کردن پزشکِ زیرمجموعهی نمایندهی جاری (toggle `active_doctor_appointment`). فقط روی پزشکانی که `representation_id` آنها برابر نمایندهی کاربر جاری است؛ در غیر این صورت 404.
|
||||
|
||||
> **Permission:** `ROLE_REPRESENTATION` — مالکیت از `#[CurrentUser]` چک میشود.
|
||||
|
||||
#### Path Parameters
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `uuid` | string | uuid پزشک |
|
||||
|
||||
#### Response `200`
|
||||
```json
|
||||
{ "success": true, "data": { "is_active": false } }
|
||||
```
|
||||
#### Errors
|
||||
| Code | HTTP | Description |
|
||||
|------|------|-------------|
|
||||
| `ERR_NOT_FOUND_001` | 404 | کاربر جاری نماینده نیست، یا پزشک یافت نشد / متعلق به این نماینده نیست |
|
||||
|
||||
---
|
||||
|
||||
### GET `/api/v1/representation/clinics`
|
||||
|
||||
کلینیکهای ثبتشده توسط نمایندهی جاری (فقط `representation_id = نمایندهی کاربر جاری`). شکل آیتم سازگار با `GET /api/v1/admin/clinics`.
|
||||
|
||||
@@ -391,4 +391,64 @@ class RepresentationActionController extends BaseController
|
||||
|
||||
return $this->paginated($items, (int) $total, $page, $limit);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/api/v1/representation/doctors/stats',
|
||||
summary: 'آمار پزشکانِ ثبتشده توسط نمایندهی جاری (شکلِ یکسان با /admin/doctors/stats)',
|
||||
security: [['bearerAuth' => []]],
|
||||
responses: [new OA\Response(response: 200, description: 'آمار پزشکان نماینده')]
|
||||
)]
|
||||
#[Route('/api/v1/representation/doctors/stats', methods: ['GET'])]
|
||||
public function doctorStats(#[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$rep = $this->representationRepo->findByUser($user);
|
||||
if ($rep === null) {
|
||||
return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'نمایندهای برای این کاربر یافت نشد', 404);
|
||||
}
|
||||
|
||||
$conn = $this->em->getConnection();
|
||||
$repId = $rep->getId();
|
||||
|
||||
$total = (int) $conn->fetchOne('SELECT COUNT(*) FROM doctors WHERE representation_id = ?', [$repId]);
|
||||
$active = (int) $conn->fetchOne('SELECT COUNT(*) FROM doctors WHERE representation_id = ? AND active_doctor_appointment = 1', [$repId]);
|
||||
$male = (int) $conn->fetchOne("SELECT COUNT(*) FROM doctors WHERE representation_id = ? AND gender IN ('man','male')", [$repId]);
|
||||
$female = (int) $conn->fetchOne("SELECT COUNT(*) FROM doctors WHERE representation_id = ? AND gender IN ('woman','female')", [$repId]);
|
||||
|
||||
return $this->success([
|
||||
'total' => $total,
|
||||
'active' => $active,
|
||||
'inactive' => $total - $active,
|
||||
'male' => $male,
|
||||
'female' => $female,
|
||||
]);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/api/v1/representation/doctors/{uuid}/status',
|
||||
summary: 'فعال/غیرفعال کردن پزشکِ زیرمجموعهی نمایندهی جاری',
|
||||
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: 'وضعیت جدید پزشک'),
|
||||
new OA\Response(response: 404, description: 'پزشک یافت نشد یا متعلق به این نماینده نیست'),
|
||||
]
|
||||
)]
|
||||
#[Route('/api/v1/representation/doctors/{uuid}/status', methods: ['POST'])]
|
||||
public function toggleDoctorStatus(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$rep = $this->representationRepo->findByUser($user);
|
||||
if ($rep === null) {
|
||||
return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'نمایندهای برای این کاربر یافت نشد', 404);
|
||||
}
|
||||
|
||||
$doctor = $this->em->getRepository(Doctor::class)->findOneBy(['uuid' => $uuid]);
|
||||
if ($doctor === null || $doctor->getRepresentationId() !== $rep->getId()) {
|
||||
return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'پزشک یافت نشد', 404);
|
||||
}
|
||||
|
||||
$doctor->setActiveDoctorAppointment(!$doctor->isActiveDoctorAppointment());
|
||||
$this->em->flush();
|
||||
|
||||
return $this->success(['is_active' => $doctor->isActiveDoctorAppointment()]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user