fix(dashboard): make the staff dashboard show the operator's actual work

Every number on it was zero. "نوبت‌های امروز من" counted appointments where
appointments.staff_id matches — a column no booking path fills by default, and
which is NULL on every row in the database. "سرویس‌های من" read only direct
service assignment, so an operator whose whole job comes from a treatment
protocol was told they had no services. The landing page of the only role that
has one data page said, in effect, that they had nothing to do — while they had
two sessions booked that day.

Today's work now comes from TreatmentSessionRepository::findTodayForStaff, the
same queue rule the sessions page uses, so there is one definition of "my work
today" rather than two that disagree. Services are the union of direct
assignment and protocol authorisation.

The two stat cards are links to the pages they name; a number with no
destination made the user hunt the sidebar for a page the card had just
mentioned. Each row of the work list opens that session. The avatar moves from
its own full-width card into the header — two lines of text were costing a card
and pushing the day's work below the fold on mobile. The assigned-appointments
table renders only when it has rows.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
hamed
2026-08-07 14:16:00 +03:30
co-authored by Claude Opus 5
parent 384ea7b180
commit 5b3e80f92a
5 changed files with 249 additions and 24 deletions
+22 -2
View File
@@ -244,7 +244,16 @@ Returns stats for the authenticated secretary and (conditionally) today's appoin
## GET /api/v1/dashboard/staff
داشبورد پرسنل: سرویس‌هایی که به این پرسنل تخصیص یافته و نوبت‌های امروزِ خودش.
داشبورد پرسنل: کارِ امروزِ اپراتور و سرویس‌هایی که مجاز به انجامشان است.
> **کارِ امروز از جلسات درمان می‌آید، نه از `appointments.staff_id`.** آن ستون را هیچ
> مسیرِ نوبت‌دهی به‌صورت پیش‌فرض پر نمی‌کند، پس `today_appointments` برای اپراتوری که
> تمام روز جلسه دارد هم خالی است. `today_sessions` همان صفِ
> `TreatmentSessionRepository::findTodayForStaff` است — یک قاعده، نه دو تا. قواعدش در
> [treatment.md](treatment.md#جلسات-امروز-من-یک-صف-است).
>
> `services` هم اتحادِ دو منبع است: تخصیصِ مستقیمِ سرویس، و پروتکلِ «طول درمان» که این
> پرسنل را مجاز دانسته. بدون دومی، اپراتورِ لیزر «هیچ سرویسی ندارید» می‌دید.
**Auth:** `ROLE_STAFF` — و علاوه بر نقش، باید ردیف **فعالِ** `clinic_staff` در محیط فعال وجود
داشته باشد. توکن تا انقضا معتبر می‌ماند، پس غیرفعال‌کردن پرسنل همان لحظه با همین بررسی
@@ -270,7 +279,7 @@ Returns stats for the authenticated secretary and (conditionally) today's appoin
"version": 1,
"resources": { "services": { "view": true }, "appointments": { "view": true } }
},
"stats": { "today_appointments": 0, "services": 1 },
"stats": { "today_sessions": 2, "today_appointments": 0, "services": 1 },
"services": [
{
"uuid": "f9ffb607-f137-4ffb-8327-76427fd6fe55",
@@ -280,6 +289,17 @@ Returns stats for the authenticated secretary and (conditionally) today's appoin
"duration_minutes": null
}
],
"today_sessions": [
{
"uuid": "86300220-c8e3-4928-b5e2-ce54a75cd59b",
"session_number": 1,
"total_sessions": 3,
"status": "in_progress",
"service_name": "لیزر توتال",
"patient_name": "محمد رسولی",
"slot_start": 1786084200
}
],
"today_appointments": []
}
}