import { Link } from 'react-router'; import { useQuery } from '@tanstack/react-query'; import { ChevronLeftIcon, ClipboardDocumentListIcon, CpuChipIcon } from '@heroicons/react/24/outline'; import { api } from '../lib/api'; import type { ApiResponse } from '../lib/api'; import PageHeader from '../components/ui/PageHeader'; import StatusBadge from '../components/ui/StatusBadge'; import { formatNumber } from '../lib/utils'; import type { StaffTreatmentSession } from '../types'; /** * «جلسات امروز من» — نقطهٔ ورود پرسنل به کار روز. * * فهرست از نوبتِ متصل به جلسه می‌آید نه از سررسید تخمینی: کارِ امروز چیزی است که * برایش وقت گرفته شده. * * صفحه صف است نه گزارش، پس دو تصمیم را تحمیل می‌کند: کارِ باقی‌مانده بالای کارِ * تمام‌شده می‌نشیند، و فهرست خودش تازه می‌شود چون منشی وسط روز نوبت اضافه می‌کند و * اپراتور روی همین صفحه نشسته است. */ /** برای *امروز* بسته‌اند: غیبت هم دوباره برنامه‌ریزی می‌شود، نه همین امروز. */ const SETTLED_STATUSES = ['done', 'cancelled', 'no_show']; export default function StaffTreatmentSessionsPage() { const { data, isLoading, isError, refetch, isFetching } = useQuery({ queryKey: ['staff-treatment-sessions'], queryFn: () => api.get>('/api/v1/dashboard/staff/treatment-sessions'), staleTime: 30_000, refetchInterval: 60_000, }); const sessions = data?.data ?? []; const pending = sessions.filter((s) => !SETTLED_STATUSES.includes(s.status)); const done = sessions.filter((s) => SETTLED_STATUSES.includes(s.status)); return ( <> refetch()} disabled={isFetching}> {isFetching ? 'در حال به‌روزرسانی…' : 'به‌روزرسانی'} } /> {isLoading ? (
{[0, 1].map((i) =>
)}
) : isError ? ( /* بدون این شاخه، خطای سرور «امروز جلسه‌ای ندارید» خوانده می‌شد و اپراتور کارِ روزش را از دست می‌داد بی‌آنکه بداند چیزی خراب است. */
خواندن جلسات امروز ناموفق بود.
) : sessions.length === 0 ? (
امروز جلسه‌ای برای شما ثبت نشده است
) : (
{done.length > 0 && }
)} ); } function Group({ title, sessions, emptyText }: { title: string; sessions: StaffTreatmentSession[]; emptyText?: string; }) { return (

{title} ({formatNumber(sessions.length)})

{sessions.length === 0 ? (
{emptyText}
) : (
{sessions.map((s) => )}
)}
); } /** * کل ردیف لینک است، نه دکمه‌ای در گوشه‌اش. * * هر ردیف دقیقاً یک کار دارد — رفتن به همان جلسه — و تکرار یک دکمهٔ بلند در هر * کارت، هدف کلیک را کوچک‌تر می‌کرد نه بزرگ‌تر. */ function SessionRow({ session: s }: { session: StaffTreatmentSession }) { const settled = s.areas?.filter((a) => a.status === 'completed' || a.status === 'skipped').length ?? 0; const total = s.areas?.length ?? 0; const time = s.appointment ? new Date(s.appointment.slot_start * 1000).toLocaleTimeString('fa-IR', { hour: '2-digit', minute: '2-digit' }) : '—'; return ( {/* ساعت لنگر ردیف است: اپراتور اول «چه وقتی» را می‌خواند، بعد «برای که». */} {time}
{s.patient_name || 'بیمار نامشخص'}
{s.service_name} جلسهٔ {formatNumber(s.session_number)} از {formatNumber(s.total_sessions)} {s.resource_name && ( {s.resource_name} )} {total > 0 && ( {formatNumber(settled)} از {formatNumber(total)} ناحیه )}
); }