feat(appointments): per-resource tabs backed by a resource_uuid list filter
Resources now get their own tabs on the appointments page, alongside doctors. An appointment on "Laser CO2" belongs to the device, not to whichever doctor happens to stand behind it, so selecting a resource tab replaces the doctor filter instead of stacking on top of it. GET /api/v1/my/appointments gains an optional resource_uuid filter and returns a `resource` object per row. The join is a leftJoin on purpose: appointments created before the resource-first model have no resource and must not drop out of the list. The resource tab lives in the URL so Back and refresh restore the same view, per the list-state rule in CLAUDE.md. The doctor tab is still useState; moving it is a separate refactor and was left untouched. Verified against the running app: filtering by a resource returns only its appointments, a resource from another tenant returns an empty list (TenantFilter, 200 not 403), and legacy rows still list with resource: null. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -317,6 +317,8 @@ class MyAppointmentsController extends BaseController
|
||||
$status = trim((string) $request->query->get('status', ''));
|
||||
$date = trim((string) $request->query->get('date', ''));
|
||||
$doctorUuid = trim((string) $request->query->get('doctor_uuid', ''));
|
||||
// تب منبع در صفحهٔ نوبتها: «نوبتهای همین دستگاه/اتاق». همان الگوی doctor_uuid.
|
||||
$resourceUuid = trim((string) $request->query->get('resource_uuid', ''));
|
||||
|
||||
// reserve=1 → only reserve-list entries; otherwise the regular slot list.
|
||||
$reserveOnly = $request->query->get('reserve') === '1';
|
||||
@@ -332,7 +334,8 @@ class MyAppointmentsController extends BaseController
|
||||
'u.uuid as patient_uuid, u.mobileNumber as patient_mobile, u.realName as patient_name',
|
||||
'ss.uuid as section_uuid, ss.name as section_name',
|
||||
'si.uuid as service_uuid, si.name as service_name',
|
||||
'st.uuid as staff_uuid, st.fullName as staff_name'
|
||||
'st.uuid as staff_uuid, st.fullName as staff_name',
|
||||
'r.uuid as resource_uuid, r.name as resource_name'
|
||||
)
|
||||
->from(Appointment::class, 'a')
|
||||
->join('a.doctor', 'd')
|
||||
@@ -341,6 +344,8 @@ class MyAppointmentsController extends BaseController
|
||||
->leftJoin('a.serviceItem', 'si')
|
||||
->leftJoin('a.staff', 'st')
|
||||
->leftJoin('a.clinic', 'cl')
|
||||
// leftJoin: نوبتهای پیش از مدل منبعمحور منبعی ندارند و نباید حذف شوند.
|
||||
->leftJoin('a.resource', 'r')
|
||||
->andWhere('a.isReserve = :reserveOnly')
|
||||
->setParameter('reserveOnly', $reserveOnly)
|
||||
->orderBy('a.slotStart', 'ASC');
|
||||
@@ -404,6 +409,10 @@ class MyAppointmentsController extends BaseController
|
||||
->setParameter('dayStart', $dayStart)
|
||||
->setParameter('dayEnd', $dayEnd);
|
||||
}
|
||||
if ($resourceUuid !== '') {
|
||||
$qb->andWhere('r.uuid = :resourceUuid')->setParameter('resourceUuid', $resourceUuid);
|
||||
}
|
||||
|
||||
if ($doctorUuid !== '') {
|
||||
$qb->andWhere('d.uuid = :doctorUuid')->setParameter('doctorUuid', $doctorUuid);
|
||||
}
|
||||
@@ -444,6 +453,9 @@ class MyAppointmentsController extends BaseController
|
||||
// تنها آن را بخواند بقیه را نشان نمیدهد.
|
||||
'service_items' => $serviceItemsByAppointment[$a['uuid']] ?? [],
|
||||
'staff' => $a['staff_uuid'] ? ['uuid' => $a['staff_uuid'], 'full_name' => $a['staff_name']] : null,
|
||||
// نوبتهای پیش از مدل منبعمحور منبعی ندارند؛ `null` یعنی «منبعی ثبت نشده»،
|
||||
// نه اینکه فیلد را نفرستاده باشیم.
|
||||
'resource' => $a['resource_uuid'] ? ['uuid' => $a['resource_uuid'], 'name' => $a['resource_name']] : null,
|
||||
'clinic_uuid' => $a['clinic_uuid'],
|
||||
'service_total_minutes' => $a['service_total_minutes'] !== null ? (int) $a['service_total_minutes'] : null,
|
||||
'service_buffer_minutes' => $a['service_buffer_minutes'] !== null ? (int) $a['service_buffer_minutes'] : null,
|
||||
|
||||
Reference in New Issue
Block a user