import { useEffect, useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { toast } from 'sonner'; import { PlusIcon, TrashIcon } from '@heroicons/react/24/outline'; import Switch from './ui/Switch'; import SearchableSelect from './ui/SearchableSelect'; import { api, ApiError, type ApiResponse } from '../lib/api'; interface ProtocolStep { step_number: number; offset_days: number; } interface ProtocolStaff { uuid: string; name: string; } interface TreatmentProtocol { uuid: string; active: boolean; total_sessions: number; supervisor: { uuid: string; name: string } | null; steps: ProtocolStep[]; staff: ProtocolStaff[]; } interface StaffRow { uuid: string; full_name: string; active?: boolean; } interface DoctorRow { uuid: string; name: string; } /** پیش‌فرضِ روشن‌کردن سوییچ: کوتاه‌ترین دوره‌ای که معنی دارد. */ const DEFAULT_STEPS: ProtocolStep[] = [ { step_number: 1, offset_days: 0 }, { step_number: 2, offset_days: 30 }, ]; /** * «طول درمان» یک سرویس. * * وجودِ پروتکل خودش سوییچ است — سرویس بدون پروتکل تک‌جلسه‌ای است — پس روشن‌کردن یعنی * ساختن و خاموش‌کردن یعنی حذف. فاصلهٔ هر گام از **جلسهٔ قبل** است، نه از شروع دوره، * چون فاصلهٔ درمان به آخرین جلسه گره خورده نه به روز باز شدن پرونده. */ export default function TreatmentProtocolTab({ serviceUuid, canEdit }: { serviceUuid: string; canEdit: boolean; }) { const qc = useQueryClient(); const { data, isLoading } = useQuery({ queryKey: ['treatment-protocol', serviceUuid], queryFn: () => api.get>( `/api/v1/service-item/${serviceUuid}/treatment-protocol`, ), }); const { data: staffData } = useQuery({ queryKey: ['staff-list-for-protocol'], queryFn: () => api.get>('/api/v1/staff'), staleTime: 60_000, }); const { data: doctorData } = useQuery({ queryKey: ['my-clinic-doctors'], queryFn: () => api.get>('/api/v1/my/clinic-doctors'), staleTime: 60_000, }); const protocol = data?.data ?? null; const [enabled, setEnabled] = useState(false); const [steps, setSteps] = useState(DEFAULT_STEPS); const [staffUuids, setStaffUuids] = useState([]); const [supervisor, setSupervisor] = useState(null); useEffect(() => { setEnabled(protocol !== null); setSteps(protocol?.steps?.length ? protocol.steps : DEFAULT_STEPS); setStaffUuids(protocol?.staff?.map((s) => s.uuid) ?? []); setSupervisor(protocol?.supervisor?.uuid ?? null); }, [protocol]); const staffOptions = (staffData?.data ?? []) .filter((s) => s.active !== false) .map((s) => ({ value: s.uuid, label: s.full_name })); const doctorOptions = (doctorData?.data?.data ?? []).map((d) => ({ value: d.uuid, label: d.name })); const save = useMutation({ mutationFn: () => api.put>( `/api/v1/service-item/${serviceUuid}/treatment-protocol`, { steps: steps.map((s, i) => ({ step_number: i + 1, offset_days: s.offset_days })), staff_uuids: staffUuids, supervisor_doctor_uuid: supervisor, }, ), onSuccess: () => { toast.success('طول درمان ذخیره شد'); qc.invalidateQueries({ queryKey: ['treatment-protocol', serviceUuid] }); }, onError: (e) => toast.error(e instanceof ApiError ? e.message : 'ذخیرهٔ طول درمان ناموفق بود'), }); const remove = useMutation({ mutationFn: () => api.delete>(`/api/v1/service-item/${serviceUuid}/treatment-protocol`), onSuccess: () => { toast.success('طول درمان خاموش شد'); qc.invalidateQueries({ queryKey: ['treatment-protocol', serviceUuid] }); }, onError: (e) => toast.error(e instanceof ApiError ? e.message : 'خاموش‌کردن ناموفق بود'), }); const toggle = (on: boolean) => { setEnabled(on); if (!on && protocol !== null) remove.mutate(); }; const setOffset = (index: number, value: number) => { setSteps((prev) => prev.map((s, i) => (i === index ? { ...s, offset_days: value } : s))); }; const addStep = () => { setSteps((prev) => [ ...prev, { step_number: prev.length + 1, offset_days: prev[prev.length - 1]?.offset_days || 30 }, ]); }; const removeStep = (index: number) => { setSteps((prev) => (prev.length <= 2 ? prev : prev.filter((_, i) => i !== index))); }; const addStaff = (uuid: string | number | null) => { const id = uuid === null ? '' : String(uuid); if (id && !staffUuids.includes(id)) setStaffUuids((prev) => [...prev, id]); }; if (isLoading) { return
در حال بارگذاری...
; } return (
{enabled && ( <>

جلسات دوره ({steps.length} جلسه)

فاصلهٔ هر جلسه از جلسهٔ قبل حساب می‌شود، نه از شروع دوره. اگر بیمار دیر بیاید، بقیهٔ دوره هم جابه‌جا می‌شود.

{steps.map((step, index) => (
جلسهٔ {index + 1} {index === 0 ? ( شروع دوره — همان روز اولین نوبت ) : ( <> setOffset(index, Number(e.target.value))} aria-label={`فاصلهٔ جلسهٔ ${index + 1} از جلسهٔ قبل به روز`} style={{ width: 96 }} /> روز بعد از جلسهٔ قبل )} {canEdit && steps.length > 2 && ( )}
))} {canEdit && ( )}

پرسنل مجاز

منشی هنگام رزرو فقط از میان همین‌ها انتخاب می‌کند.

{canEdit && ( !staffUuids.includes(String(o.value)))} value={null} onChange={addStaff} placeholder="افزودن پرسنل..." ariaLabel="افزودن پرسنل مجاز" /> )}
{staffUuids.length === 0 && ( حداقل یک پرسنل الزامی است )} {staffUuids.map((uuid) => ( {staffOptions.find((o) => String(o.value) === uuid)?.label ?? uuid} {canEdit && ( )} ))}

پزشک ناظر

پاسخگوی بالینی دوره. لازم نیست خودش درمان را انجام دهد.

setSupervisor(v === null ? null : String(v))} placeholder="بدون پزشک ناظر" isClearable isDisabled={!canEdit} ariaLabel="پزشک ناظر دوره" />
{canEdit && ( )} )}
); }