feat: Implement Jalali calendar support for dashboard charts
- Added support for Jalali calendar in the dashboard, allowing charts to display data based on the current Jalali month and year. - Updated the API to return `patients_year`, `patients_month`, and `revenue_year` parameters for the dashboard charts. - Refactored the dashboard controller to handle Jalali date calculations and queries. - Modified the frontend components to utilize the new Jalali date parameters and reflect changes in the UI. - Removed the status column from the NewAppointmentsTable as status management is now handled on the appointments page. - Added tests to ensure the correct functioning of the new Jalali chart period features.
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Dashboard;
|
||||
|
||||
use App\Appointment\Entity\Appointment;
|
||||
use App\Clinic\Entity\Clinic;
|
||||
use App\Doctor\Entity\Doctor;
|
||||
use App\Tests\ApiTestCase;
|
||||
|
||||
/**
|
||||
* GET /api/v1/dashboard/{clinic,doctor} — chart series periods.
|
||||
*
|
||||
* «نمودار تعداد بیماران» is one Jalali month (day-by-day) and «میزان درآمد» is
|
||||
* one Jalali year (month-by-month); both default to the current Jalali period
|
||||
* and are overridable via patients_year / patients_month / revenue_year.
|
||||
*/
|
||||
class DashboardChartPeriodTest extends ApiTestCase
|
||||
{
|
||||
/** @return array{0:int, 1:int} [jalaliYear, jalaliMonth] */
|
||||
private function currentJalali(): array
|
||||
{
|
||||
$cal = \IntlCalendar::createInstance(new \DateTimeZone('Asia/Tehran'), 'fa_IR@calendar=persian');
|
||||
|
||||
return [$cal->get(\IntlCalendar::FIELD_YEAR), $cal->get(\IntlCalendar::FIELD_MONTH) + 1];
|
||||
}
|
||||
|
||||
private function jalaliMonthLength(int $year, int $month): int
|
||||
{
|
||||
$cal = \IntlCalendar::createInstance(new \DateTimeZone('Asia/Tehran'), 'fa_IR@calendar=persian');
|
||||
$cal->set(\IntlCalendar::FIELD_YEAR, $year);
|
||||
$cal->set(\IntlCalendar::FIELD_MONTH, $month - 1);
|
||||
$cal->set(\IntlCalendar::FIELD_DAY_OF_MONTH, 1);
|
||||
|
||||
return $cal->getActualMaximum(\IntlCalendar::FIELD_DAY_OF_MONTH);
|
||||
}
|
||||
|
||||
private function clinicWithDoctor(): array
|
||||
{
|
||||
$clinicOwner = $this->createUser(['ROLE_CLINIC']);
|
||||
$clinic = new Clinic($clinicOwner);
|
||||
$clinic->setName('کلینیک نمودار');
|
||||
|
||||
$docOwner = $this->createUser(['ROLE_DOCTOR']);
|
||||
$doctor = new Doctor($docOwner, 'حمیدی');
|
||||
$clinic->getDoctors()->add($doctor);
|
||||
|
||||
$this->em->persist($doctor);
|
||||
$this->em->persist($clinic);
|
||||
$this->em->flush();
|
||||
|
||||
return [$clinicOwner, $doctor];
|
||||
}
|
||||
|
||||
public function testClinicChartsDefaultToCurrentJalaliPeriod(): void
|
||||
{
|
||||
[$owner] = $this->clinicWithDoctor();
|
||||
[$year, $month] = $this->currentJalali();
|
||||
|
||||
$res = $this->authJson('GET', '/api/v1/dashboard/clinic', $owner);
|
||||
self::assertSame(200, $this->responseCode());
|
||||
|
||||
self::assertSame(
|
||||
['patients_year' => $year, 'patients_month' => $month, 'revenue_year' => $year],
|
||||
$res['data']['charts_period']
|
||||
);
|
||||
self::assertCount($this->jalaliMonthLength($year, $month), $res['data']['charts']['appointments_by_day']);
|
||||
self::assertCount(12, $res['data']['charts']['revenue_by_month']);
|
||||
}
|
||||
|
||||
public function testClinicChartsHonourRequestedPeriod(): void
|
||||
{
|
||||
[$owner] = $this->clinicWithDoctor();
|
||||
|
||||
// اسفند ۱۴۰۳ سال کبیسه است → ۳۰ روز
|
||||
$res = $this->authJson('GET', '/api/v1/dashboard/clinic?patients_year=1403&patients_month=12&revenue_year=1402', $owner);
|
||||
self::assertSame(200, $this->responseCode());
|
||||
|
||||
self::assertSame(
|
||||
['patients_year' => 1403, 'patients_month' => 12, 'revenue_year' => 1402],
|
||||
$res['data']['charts_period']
|
||||
);
|
||||
self::assertCount(30, $res['data']['charts']['appointments_by_day']);
|
||||
}
|
||||
|
||||
public function testTodayAppointmentIsCountedOnItsJalaliDay(): void
|
||||
{
|
||||
[$owner, $doctor] = $this->clinicWithDoctor();
|
||||
$patient = $this->createUser(['ROLE_USER']);
|
||||
|
||||
$start = time();
|
||||
$this->em->persist(new Appointment($doctor, $patient, $start, $start + 1_800));
|
||||
$this->em->flush();
|
||||
|
||||
$res = $this->authJson('GET', '/api/v1/dashboard/clinic', $owner);
|
||||
$days = $res['data']['charts']['appointments_by_day'];
|
||||
|
||||
$cal = \IntlCalendar::createInstance(new \DateTimeZone('Asia/Tehran'), 'fa_IR@calendar=persian');
|
||||
$todayIndex = $cal->get(\IntlCalendar::FIELD_DAY_OF_MONTH) - 1;
|
||||
|
||||
self::assertGreaterThanOrEqual(1, $days[$todayIndex]['count'], 'today\'s slot must land on today\'s bar');
|
||||
}
|
||||
|
||||
public function testOutOfRangeParamsAreClamped(): void
|
||||
{
|
||||
[$owner] = $this->clinicWithDoctor();
|
||||
[$year] = $this->currentJalali();
|
||||
|
||||
$res = $this->authJson('GET', '/api/v1/dashboard/clinic?patients_month=99&patients_year=9999&revenue_year=0', $owner);
|
||||
self::assertSame(200, $this->responseCode());
|
||||
|
||||
$period = $res['data']['charts_period'];
|
||||
self::assertSame(12, $period['patients_month']);
|
||||
self::assertSame(1500, $period['patients_year']);
|
||||
// revenue_year=0 is falsy → falls back to the current Jalali year
|
||||
self::assertSame($year, $period['revenue_year']);
|
||||
}
|
||||
|
||||
public function testDoctorChartsUseSameJalaliPeriod(): void
|
||||
{
|
||||
$owner = $this->createUser(['ROLE_DOCTOR']);
|
||||
$doctor = new Doctor($owner, 'حمیدی');
|
||||
$this->em->persist($doctor);
|
||||
$this->em->flush();
|
||||
|
||||
$res = $this->authJson('GET', '/api/v1/dashboard/doctor?patients_year=1403&patients_month=1', $owner);
|
||||
self::assertSame(200, $this->responseCode());
|
||||
|
||||
self::assertSame(1403, $res['data']['charts_period']['patients_year']);
|
||||
self::assertCount(31, $res['data']['charts']['appointments_by_day']);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user