refactor(admin): collapse session cards in the course detail
A finished session prints three areas with their device readings, so a course with any history pushed the sessions that still need work off the screen. Each session is a collapsible card now, with the scannable facts kept in the head — number, status, date, staff, and an area count so opening is a decision rather than a guess. History starts collapsed; a session that still needs booking starts open, because its button is the reason it is on the page and should not sit behind an extra click. A session with neither areas nor an action has no body and renders as a plain row rather than an empty toggle. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import {
|
||||
ChevronLeftIcon, ChevronRightIcon, ClipboardDocumentListIcon,
|
||||
ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ClipboardDocumentListIcon,
|
||||
CpuChipIcon, MagnifyingGlassIcon, XMarkIcon,
|
||||
} from '@heroicons/react/24/outline';
|
||||
import { api } from '../../lib/api';
|
||||
@@ -312,6 +312,14 @@ function SessionCard({ session: s, summary, resource, nationalCode }: {
|
||||
nationalCode: string | null;
|
||||
}) {
|
||||
const [booking, setBooking] = useState(false);
|
||||
/**
|
||||
* سابقه بسته میآید و کارِ پیشِ رو باز.
|
||||
*
|
||||
* جلسهٔ انجامشده سه ناحیه با خواندههایشان دارد و بازِ پیشفرض، فهرست را دفن
|
||||
* میکند؛ ولی جلسهای که باید رزرو شود، دکمهاش دلیلِ وجودش است و پشت یک کلیک
|
||||
* اضافه نباید بماند.
|
||||
*/
|
||||
const [open, setOpen] = useState(!SETTLED.includes(s.status));
|
||||
const qc = useQueryClient();
|
||||
const clinicUuid = useClinicContext();
|
||||
|
||||
@@ -322,10 +330,12 @@ function SessionCard({ session: s, summary, resource, nationalCode }: {
|
||||
|
||||
const bookable = !SETTLED.includes(s.status) && s.appointment === null && summary.status === 'active';
|
||||
const areas = s.areas ?? [];
|
||||
// جلسهای که نه ناحیهای ثبت کرده و نه کاری برای انجام دارد، بدنهای ندارد که باز
|
||||
// شود؛ کلپسِ خالی فقط یک کلیک بینتیجه است.
|
||||
const hasBody = areas.length > 0 || bookable;
|
||||
|
||||
return (
|
||||
<div className="card card-pad" style={{ display: 'grid', gap: 8 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
|
||||
const head = (
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap', width: '100%' }}>
|
||||
<strong style={{ fontSize: 13.5 }}>
|
||||
جلسهٔ {formatNumber(s.session_number)} از {formatNumber(s.total_sessions)}
|
||||
</strong>
|
||||
@@ -338,10 +348,39 @@ function SessionCard({ session: s, summary, resource, nationalCode }: {
|
||||
{s.performed_by && (
|
||||
<span style={{ fontSize: 12.5, color: 'var(--text-3)' }}>پرسنل: {s.performed_by.name}</span>
|
||||
)}
|
||||
{/* شمارهٔ نواحی در سرِ بسته میماند تا باز کردن، حدس نباشد. */}
|
||||
{areas.length > 0 && !open && (
|
||||
<span style={{ fontSize: 12.5, color: 'var(--text-3)' }}>{formatNumber(areas.length)} ناحیه</span>
|
||||
)}
|
||||
{hasBody && (
|
||||
<ChevronDownIcon
|
||||
style={{
|
||||
width: 16, height: 16, marginInlineStart: 'auto', flexShrink: 0, color: 'var(--text-3)',
|
||||
transition: 'transform .2s var(--ease)', transform: open ? 'rotate(180deg)' : 'none',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="card card-pad" style={{ display: 'grid', gap: 8 }}>
|
||||
{hasBody ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen((o) => !o)}
|
||||
aria-expanded={open}
|
||||
style={{
|
||||
display: 'flex', width: '100%', padding: 0, background: 'none', border: 'none',
|
||||
cursor: 'pointer', font: 'inherit', color: 'var(--text)', textAlign: 'start',
|
||||
}}
|
||||
>
|
||||
{head}
|
||||
</button>
|
||||
) : head}
|
||||
|
||||
{/* آنچه واقعاً انجام شد: ناحیه، دستگاه و خواندههایش. */}
|
||||
{areas.length > 0 && (
|
||||
{open && areas.length > 0 && (
|
||||
<div style={{ display: 'grid', gap: 6 }}>
|
||||
{areas.map((a) => (
|
||||
<div key={a.uuid} style={{ display: 'flex', gap: 10, flexWrap: 'wrap', fontSize: 12.5 }}>
|
||||
@@ -364,7 +403,7 @@ function SessionCard({ session: s, summary, resource, nationalCode }: {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{bookable && (
|
||||
{open && bookable && (
|
||||
/* بدون دستگاه، فرم سرویسی برای انتخاب ندارد؛ دکمهٔ خاموشِ بیتوضیح بدتر از
|
||||
نبودنش است، پس دلیلش نوشته میشود. */
|
||||
resource === null ? (
|
||||
|
||||
Reference in New Issue
Block a user