refactor(admin): course cards with a detail view, and fill the banner's next date
The tab stacked every course's full session list on one page. A protocol allows sixty steps, so one open course was enough to bury the others. Courses are cards now — service, progress, supervising doctor, staff — and opening one replaces the list with its detail: a back button, two tabs (the whole course, or only what is still to come), search and paging inside each. The choice lives in the URL so browser-back returns to the same course. Booking stays where the work is: the button sits on the session card inside the upcoming tab, not on a separate page. The patient banner's 'نوبت بعدی' read '—' for anyone mid-course, because it only looked at booked appointments and a course's later sessions have none yet. It now falls back to the next session of the active course and relabels itself 'جلسهٔ بعدی' when it does — a planned session is not a booking, and the banner should not call it one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -171,6 +171,31 @@ export default function PatientDetailPage() {
|
||||
const hasDebt = sessions.some((s) => !s.is_paid);
|
||||
const nowSec = Math.floor(Date.now() / 1000);
|
||||
|
||||
/**
|
||||
* وقتی نوبتی رزرو نشده، جلسهٔ بعدیِ دورهٔ درمان را نشان میدهیم — با برچسبِ خودش.
|
||||
*
|
||||
* بیمارِ دورهای معمولاً نوبتِ بعدیاش هنوز گرفته نشده؛ خالی گذاشتنِ بنر یعنی
|
||||
* صفحه چیزی را که میداند نمیگوید.
|
||||
*/
|
||||
const treatmentQ = useQuery<ApiResponse<{ uuid: string; status: string }[]>>({
|
||||
queryKey: ['patient-treatment-cases', uuid],
|
||||
queryFn: () => api.get(`/api/v1/treatment-cases?record=${uuid}`),
|
||||
enabled: !!uuid,
|
||||
staleTime: 30_000,
|
||||
});
|
||||
const activeCase = (treatmentQ.data?.data ?? []).find((c) => c.status === 'active') ?? null;
|
||||
|
||||
const planQ = useQuery<ApiResponse<{ sessions: { status: string; planned_at: number | null }[] }>>({
|
||||
queryKey: ['treatment-case-plan', activeCase?.uuid],
|
||||
queryFn: () => api.get(`/api/v1/treatment-case/${activeCase!.uuid}/plan`),
|
||||
enabled: activeCase !== null,
|
||||
staleTime: 30_000,
|
||||
});
|
||||
|
||||
const nextSession = (planQ.data?.data?.sessions ?? [])
|
||||
.filter((s) => !['done', 'cancelled', 'no_show'].includes(s.status) && (s.planned_at ?? 0) >= nowSec)
|
||||
.sort((a, b) => (a.planned_at ?? 0) - (b.planned_at ?? 0))[0]?.planned_at ?? null;
|
||||
|
||||
const nextAppointment = (appointmentsQ.data?.data ?? [])
|
||||
.filter((a: any) => (a.starts_at ?? 0) >= nowSec && !String(a.status).startsWith('cancelled'))
|
||||
.sort((a: any, b: any) => a.starts_at - b.starts_at)[0]?.starts_at ?? null;
|
||||
@@ -186,6 +211,7 @@ export default function PatientDetailPage() {
|
||||
createdAt={(record as any)?.created_at}
|
||||
tags={(record as any)?.tags}
|
||||
nextAppointment={nextAppointment}
|
||||
nextSession={nextSession}
|
||||
hasDebt={hasDebt}
|
||||
onAddNote={() => setTab('notes')}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user