import { useState } from 'react'; import { Link } from 'react-router-dom'; import { useQuery } from '@tanstack/react-query'; import { api } from '../lib/api'; import type { ApiResponse } from '../lib/api'; import PageHeader from '../components/ui/PageHeader'; import StatusBadge from '../components/ui/StatusBadge'; import { formatDate, formatDateTime } from '../lib/utils'; import { useUrlState } from '../hooks/useUrlState'; import type { TreatmentCaseSummary, StaffTreatmentSession, SlotSuggestionResponse } from '../types'; const TABS = [ { id: 'cases', label: 'پرونده‌های درمان' }, { id: 'unbooked', label: 'جلسات بدون نوبت' }, ] as const; type TabId = typeof TABS[number]['id']; const CASE_STATUS_LABEL: Record = { active: 'در جریان', completed: 'تمام شده', abandoned: 'رها شده', }; /** * پرونده‌های درمان و کارِ باقی‌ماندهٔ منشی. * * صفِ «جلسات بدون نوبت» عمداً کنار فهرست پرونده‌هاست نه صفحهٔ جدا: هر دو یک سؤال * را جواب می‌دهند — «کدام بیمار در چه مرحله‌ای است و چه کاری مانده». */ export default function TreatmentCasesPage() { const [urlState, setUrlState] = useUrlState({ tab: 'cases', status: '' }); const tab = (TABS.some((t) => t.id === urlState.tab) ? urlState.tab : 'cases') as TabId; return ( <>
{TABS.map((t) => ( ))}
{tab === 'cases' ? setUrlState({ status: s })} /> : } ); } function CasesTab({ status, onStatus }: { status: string; onStatus: (s: string) => void }) { const { data, isLoading } = useQuery({ queryKey: ['treatment-cases', status], queryFn: () => api.get>( `/api/v1/treatment-cases${status ? `?status=${status}` : ''}`, ), staleTime: 30_000, }); const cases = data?.data ?? []; return ( <>
{[['', 'همه'], ['active', 'در جریان'], ['completed', 'تمام شده'], ['abandoned', 'رها شده']].map(([v, label]) => ( ))}
{isLoading ? (
در حال بارگذاری...
) : cases.length === 0 ? (
پرونده‌ای یافت نشد. پرونده وقتی ساخته می‌شود که نوبتِ سرویسی با «طول درمان» قطعی شود.
) : (
{cases.map((c) => (
{c.service.name} {CASE_STATUS_LABEL[c.status]} {c.completed_sessions} از {c.total_sessions} جلسه
شروع: {formatDate(c.opened_at)} {c.supervisor && پزشک ناظر: {c.supervisor.name}} {c.areas.length > 0 && نواحی: {c.areas.map((a) => a.name).join('، ')}}
))}
)} ); } function UnbookedTab() { const { data, isLoading } = useQuery({ queryKey: ['treatment-sessions-unbooked'], queryFn: () => api.get>('/api/v1/treatment-sessions/unbooked?within_days=14'), staleTime: 30_000, }); const sessions = data?.data ?? []; return ( <>

جلساتی که سررسیدشان رسیده و هنوز نوبت نگرفته‌اند. رزرو عمداً خودکار نیست — وقتِ مناسب را باید با خود بیمار هماهنگ کرد.

{isLoading ? (
در حال بارگذاری...
) : sessions.length === 0 ? (
جلسه‌ای در انتظار رزرو نیست.
) : (
{sessions.map((s) => (
{s.service_name} جلسهٔ {s.session_number} از {s.total_sessions}
{s.due_at !== null && ( سررسید: {formatDate(s.due_at)} )}
))}
)} ); } /** * وقت‌های آزادِ همان دستگاهی که جلسهٔ قبلی رویش انجام شد. * * پیشنهاد است نه رزرو: منشی با بیمار هماهنگ می‌کند و بعد از فرم عادی نوبت ثبتش * می‌کند. خودکار رزرو کردن یعنی سیستم به‌جای بیمار تصمیم بگیرد و بعد او نیاید. */ function SlotSuggestions({ sessionUuid }: { sessionUuid: string }) { const [open, setOpen] = useState(false); const { data, isLoading, isError } = useQuery({ queryKey: ['session-slot-suggestions', sessionUuid], queryFn: () => api.get>( `/api/v1/treatment-session/${sessionUuid}/slot-suggestions?days=14`, ), enabled: open, staleTime: 60_000, }); if (!open) { return (
ثبت نوبت این جلسه
); } const days = data?.data?.days ?? []; return (
{isLoading && در حال جست‌وجوی وقت...} {isError && ( دستگاهی برای پیشنهاد وقت مشخص نیست — این جلسه هنوز روی هیچ دستگاهی انجام نشده. )} {!isLoading && !isError && days.length === 0 && ( در دو هفتهٔ آینده وقت آزادی روی این دستگاه نیست. )} {days.slice(0, 3).map((day) => (
{formatDate(Math.floor(new Date(day.date).getTime() / 1000))} {day.slots.slice(0, 6).map((slot) => ( {slot.start_time} ))}
))}
); }