import React, { useState } from 'react'; import { useParams } from 'react-router-dom'; import PageHeader from '../components/ui/PageHeader'; import DataTable, { type Column } from '../components/ui/DataTable'; import ConfirmDialog from '../components/ui/ConfirmDialog'; import SearchableSelect from '../components/ui/SearchableSelect'; import { formatDate } from '../lib/utils'; import { usePermissions } from '../hooks/usePermissions'; import { useBranches } from '../hooks/useBranches'; import { useNextSlotSuggestion, useTreatmentCourse } from '../hooks/useCourses'; import type { CourseSessionRow } from '../types'; const SESSION_STATUS: Record = { planned: { label: 'برنامه‌ریزی‌شده', className: 'badge' }, booked: { label: 'رزروشده', className: 'badge amber' }, completed: { label: 'انجام‌شده', className: 'badge green' }, skipped: { label: 'رد‌شده', className: 'badge red' }, }; /** * یک دورهٔ درمان: پیشرفت، جلسات، و پیشنهاد تاریخ جلسهٔ بعدی. * * پیشنهاد به شعبه وابسته است (ظرفیت هر شعبه فرق دارد)، پس تا شعبه انتخاب نشود چیزی * پرسیده نمی‌شود. */ export default function TreatmentCoursePage() { const { courseUuid } = useParams<{ courseUuid: string }>(); const { course, loading, abandon } = useTreatmentCourse(courseUuid); const { branches } = useBranches(); const { can } = usePermissions(); const canManage = can('appointment_settings', 'update'); const [branchUuid, setBranchUuid] = useState(''); const [abandoning, setAbandoning] = useState(false); const [reason, setReason] = useState(''); const { suggestion } = useNextSlotSuggestion(courseUuid, branchUuid || undefined); const columns: Column[] = [ { key: 'session_number', header: 'جلسه', render: (s) => {s.session_number}, }, { key: 'status', header: 'وضعیت', render: (s) => ( {SESSION_STATUS[s.status].label} ), }, { key: 'slot_start', header: 'تاریخ نوبت', render: (s) => ( {s.slot_start === null ? '—' : formatDate(s.slot_start)} ), }, { key: 'params', header: 'پارامتر', render: (s) => ( {Object.entries(s.params).length === 0 ? '—' : Object.entries(s.params) .map(([k, v]) => `${k}: ${v}`) .join('، ')} ), }, { key: 'completed_at', header: 'انجام‌شده در', render: (s) => ( {s.completed_at === null ? '—' : formatDate(s.completed_at)} ), }, ]; return (
{course && (
جلسهٔ {course.progress.completed} از {course.progress.total} انجام شده رزروشده: {course.progress.booked} · باقی‌مانده: {course.progress.planned} فاصله: حداقل {course.min_days} · ایده‌آل {course.ideal_days} · حداکثر {course.max_days} روز {course.status !== 'active' && ( {course.status === 'completed' ? 'تمام‌شده' : 'رهاشده'} )}
{course.abandon_reason && ( دلیل رهاکردن: {course.abandon_reason} )}
setBranchUuid(String(v ?? ''))} options={branches.map((b) => ({ value: b.uuid, label: b.name || 'بدون نام' }))} placeholder="انتخاب شعبه" />
{canManage && course.status === 'active' && ( )}
{suggestion && suggestion.session_number !== null && (
جلسهٔ بعدی: {suggestion.session_number} {suggestion.ideal_at !== undefined && ` · تاریخ ایده‌آل ${formatDate(suggestion.ideal_at)}`} {suggestion.range && ( بازهٔ مجاز: {formatDate(suggestion.range.min)} تا {formatDate(suggestion.range.max)} )} {suggestion.suggested_slots.length > 0 && ( نزدیک‌ترین وقت‌ها:{' '} {suggestion.suggested_slots.map((s) => formatDate(s.start)).join('، ')} )} {suggestion.warning && ( {suggestion.warning} )}
)}
)}
setAbandoning(false)} onConfirm={async () => { await abandon.mutateAsync(reason.trim() || 'رهاکردن دوره'); setAbandoning(false); }} >
setReason(e.target.value)} placeholder="مثلاً: انصراف بیمار" />
); }