feat: implement filtered and paginated doctor appointments panel with status filtering

This commit is contained in:
hamed
2026-07-19 11:50:16 +03:30
parent 8420a1a6c2
commit 4e78a76824
7 changed files with 324 additions and 4 deletions
@@ -0,0 +1,107 @@
<?php
namespace App\Tests\Appointment;
use App\Appointment\Entity\Appointment;
use App\Appointment\Repository\AppointmentRepository;
use App\Doctor\Entity\Doctor;
use App\Tests\ApiTestCase;
/**
* Filtered + paginated doctor appointment list backing the dashboard filter bar
* (AppointmentRepository::searchByDoctor).
*/
class DoctorAppointmentFilterTest extends ApiTestCase
{
private function repo(): AppointmentRepository
{
return $this->em->getRepository(Appointment::class);
}
private function makeDoctor(): Doctor
{
$doctor = new Doctor($this->createUser(['ROLE_DOCTOR']), 'دکتر تست');
$this->em->persist($doctor);
$this->em->flush();
return $doctor;
}
private function booking(Doctor $doctor, int $start, ?string $status = null, ?string $name = null): Appointment
{
$a = new Appointment($doctor, $this->createUser(), $start, $start + 1_800);
if ($name !== null) {
$a->setPatientName($name);
}
if ($status === Appointment::STATUS_COMPLETED) {
$a->transitionTo(Appointment::STATUS_CONFIRMED);
$a->transitionTo(Appointment::STATUS_COMPLETED);
} elseif ($status !== null) {
$a->transitionTo($status);
}
$this->em->persist($a);
$this->em->flush();
return $a;
}
public function testStatusFilterExcludesVisited(): void
{
$doctor = $this->makeDoctor();
$base = time() + 86_400;
$this->booking($doctor, $base); // pending
$this->booking($doctor, $base + 3_600, Appointment::STATUS_CONFIRMED);
$this->booking($doctor, $base + 7_200, Appointment::STATUS_COMPLETED);
$res = $this->repo()->searchByDoctor(
$doctor,
[Appointment::STATUS_PENDING, Appointment::STATUS_CONFIRMED],
);
self::assertSame(2, $res['total']);
foreach ($res['items'] as $a) {
self::assertNotSame(Appointment::STATUS_COMPLETED, $a->getStatus());
}
}
public function testDateRangeAndNameSearch(): void
{
$doctor = $this->makeDoctor();
$base = time() + 86_400;
$this->booking($doctor, $base, null, 'علی رضایی');
$this->booking($doctor, $base + 200_000, null, 'مریم کاظمی');
$inRange = $this->repo()->searchByDoctor($doctor, [], null, $base - 60, $base + 60);
self::assertSame(1, $inRange['total']);
self::assertSame('علی رضایی', $inRange['items'][0]->getPatientName());
$byName = $this->repo()->searchByDoctor($doctor, [], null, null, null, 'کاظمی');
self::assertSame(1, $byName['total']);
self::assertSame('مریم کاظمی', $byName['items'][0]->getPatientName());
}
public function testPaginationSlicesAndReportsFullTotal(): void
{
$doctor = $this->makeDoctor();
$base = time() + 86_400;
for ($i = 0; $i < 5; $i++) {
$this->booking($doctor, $base + $i * 3_600);
}
$page2 = $this->repo()->searchByDoctor($doctor, [], null, null, null, null, null, 2, 2);
self::assertSame(5, $page2['total'], 'total باید کل نتایج باشد نه اندازهٔ صفحه');
self::assertCount(2, $page2['items']);
}
public function testEmptyResultForUnmatchedFilter(): void
{
$doctor = $this->makeDoctor();
$this->booking($doctor, time() + 86_400);
$res = $this->repo()->searchByDoctor($doctor, [Appointment::STATUS_NO_SHOW]);
self::assertSame(0, $res['total']);
self::assertSame([], $res['items']);
}
}