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:
@@ -44,13 +44,20 @@ const InfoLine = ({ icon, label, value }: { icon: React.ReactNode; label: string
|
||||
* FileServicesHeader (name + status chip, file number, tags, contact/date,
|
||||
* next appointment, یادداشت button).
|
||||
*/
|
||||
export default function PatientCaseBanner({ name, recordNumber, mobile, createdAt, tags, nextAppointment, hasDebt, noShows, onAddNote }: {
|
||||
export default function PatientCaseBanner({ name, recordNumber, mobile, createdAt, tags, nextAppointment, nextSession, hasDebt, noShows, onAddNote }: {
|
||||
name: string;
|
||||
recordNumber?: string | null;
|
||||
mobile?: string | null;
|
||||
createdAt?: number;
|
||||
tags?: Tag[];
|
||||
nextAppointment?: number | null;
|
||||
/**
|
||||
* جلسهٔ بعدیِ دوره وقتی نوبتی رزرو نشده.
|
||||
*
|
||||
* جدا از `nextAppointment` است و باید هم باشد: این هنوز نوبت نیست، برنامه است.
|
||||
* یکی کردنشان یعنی بنر چیزی را «نوبت» بنامد که کسی رزروش نکرده.
|
||||
*/
|
||||
nextSession?: number | null;
|
||||
hasDebt?: boolean;
|
||||
/** خلاصهٔ عدم حضور در پنجرهٔ سیاست — `null` یعنی هنوز نیامده. */
|
||||
noShows?: { count: number; threshold: number; window_days: number; at_risk: boolean } | null;
|
||||
@@ -106,7 +113,15 @@ export default function PatientCaseBanner({ name, recordNumber, mobile, createdA
|
||||
|
||||
{/* left — next appointment + note */}
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 28 }}>
|
||||
<InfoLine icon={<FilesServiceNotification />} label="نوبت بعدی:" value={nextAppointment ? formatDate(nextAppointment) : '—'} />
|
||||
<InfoLine
|
||||
icon={<FilesServiceNotification />}
|
||||
label={nextAppointment || !nextSession ? 'نوبت بعدی:' : 'جلسهٔ بعدی:'}
|
||||
value={
|
||||
nextAppointment ? formatDate(nextAppointment)
|
||||
: nextSession ? formatDate(nextSession)
|
||||
: '—'
|
||||
}
|
||||
/>
|
||||
<button
|
||||
type="button" onClick={onAddNote}
|
||||
style={{ display: 'inline-flex', alignItems: 'center', gap: 6, background: 'var(--accent)', color: 'var(--on-primary)', border: 'none', borderRadius: 10, padding: '0 18px', height: 36, fontSize: 13, fontWeight: 600, cursor: 'pointer' }}
|
||||
|
||||
Reference in New Issue
Block a user