feat(admin): 'next appointments' tab in the patient file

A patient's file had no view of their multi-session courses: which ones they
have, when the remaining sessions fall, or what was recorded in the sessions
already done. All of it lived on a tenant-wide page.

The tab lists the patient's courses and, per course, a card for every session
with its date, its status, and — for finished ones — the areas treated with the
device readings a staff member logged. Estimated dates are labelled as such, so
a projection is never read as a booking.

Each unbooked session carries a button that opens the same NewAppointmentModal
used elsewhere, seeded with that session's date, and now binds the resulting
appointment to that exact session via a new treatmentSessionUuid prop — a
patient can have several open courses, and without it the attachment falls back
to guessing from the service.

The modal opens in resource mode, not doctor mode: a course's service is booked
against the device's calendar, so useDoctorBookingServices returns nothing for
the supervising doctor and the picker would render 'no bookable services'. The
plan response now carries the course's device for exactly this. A course with no
device yet says so instead of offering a button that cannot work.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
hamed
2026-08-07 16:21:31 +03:30
co-authored by Claude Opus 5
parent fc50ac4b3b
commit de52d668c0
5 changed files with 295 additions and 1 deletions
+5
View File
@@ -296,6 +296,11 @@ single-session again. Idempotent: deleting a service that has no protocol still
| `is_estimate` | `false` یعنی به واقعیتی گره خورده، `true` یعنی محاسبهٔ لحظهٔ نمایش |
| `areas[]` | نواحی با `parameters` (خوانده‌های دستگاه)، `resource`، `note`، زمان‌ها |
پاسخ یک `resource` هم دارد: دستگاهی که جلسهٔ قبلِ همین دوره رویش انجام شده
(`NextSessionSlotFinder::preferredResource`). فرم ثبت نوبت بدونش کار نمی‌کند — سرویسِ
دوره روی تقویم منبع رزرو می‌شود نه روی برنامهٔ پزشک. `null` یعنی هنوز هیچ جلسه‌ای روی
دستگاهی انجام نشده.
**`planned_at` ذخیره نمی‌شود.** `TreatmentScheduler` فقط سررسید جلسهٔ بعدی را
می‌نویسد؛ بقیهٔ زنجیره را `TreatmentPlanProjector` در لحظهٔ خواندن می‌سازد. لنگرِ هر
جلسه به‌ترتیب: زمان اتمام، زمان نوبت، `due_at` نوشته‌شده. دوره‌ای که هیچ‌کدام را