import { useEffect, useMemo, useState } from 'react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useNavigate, useSearchParams } from 'react-router-dom'; import { PlusIcon, ChevronRightIcon } from '@heroicons/react/24/outline'; import { toast } from 'sonner'; import { api } from '../lib/api'; import type { ApiResponse } from '../lib/api'; import { useAuthStore } from '../stores/authStore'; import PersianDateInput from '../components/ui/PersianDateInput'; import PriceInput from '../components/ui/PriceInput'; import SearchableSelect from '../components/ui/SearchableSelect'; import { WalletChargeLink } from '../components/AppointmentActions'; /** * افزودن نوبت — صفحهٔ کامل (بازسازی `CreateTurn.jsx` طرح tauri). از همان endpointهای * موجود استفاده می‌کند: `POST /api/v1/my/appointment` (یا admin) + در صورت نیاز * `PATCH .../status`. هیچ endpoint جدیدی ساخته نشده است. */ interface Option { uuid: string; name?: string; full_name?: string } interface PatientRow { uuid: string; user_name?: string; user_mobile?: string } const toEpoch = (isoDate: string, time: string) => Math.floor(new Date(`${isoDate}T${time || '00:00'}`).getTime() / 1000); const addMinutes = (time: string, min: number) => { const [h, m] = time.split(':').map(Number); const t = h * 60 + m + min; return `${String(Math.floor(t / 60) % 24).padStart(2, '0')}:${String(t % 60).padStart(2, '0')}`; }; const label = { fontSize: 12.5, color: 'var(--text-3)', display: 'block' } as const; const sel: React.CSSProperties = { width: '100%', height: 38, borderRadius: 'var(--r-sm)', border: '1px solid var(--border)', background: 'var(--surface)', fontSize: 13, fontFamily: 'inherit', padding: '0 10px' }; const sectionTitle: React.CSSProperties = { fontSize: 14, fontWeight: 700, color: 'var(--text)', margin: '18px 0 12px' }; export default function AppointmentCreatePage() { const navigate = useNavigate(); const [params] = useSearchParams(); const qc = useQueryClient(); const primaryRole = useAuthStore(s => s.primaryRole); const dbUuid = useAuthStore(s => s.dbUuid); const isDoctor = primaryRole === 'doctor'; const today = new Date().toISOString().slice(0, 10); // ── پزشک const [doctorUuid, setDoctorUuid] = useState(isDoctor && dbUuid ? dbUuid : (params.get('doctor') ?? '')); const clinicDoctorsQuery = useQuery>({ queryKey: ['clinic-doctors', dbUuid], queryFn: () => api.get(`/api/v1/clinic/doctor-list/${dbUuid}`), enabled: !isDoctor && !!dbUuid, }); const doctorOptions = (clinicDoctorsQuery.data?.data?.data ?? []).map(d => ({ value: d.uuid, label: d.name })); // ── بیمار: جستجوی رکورد موجود یا ورود شخص جدید const [patientSearch, setPatientSearch] = useState(''); const [picked, setPicked] = useState(null); const [name, setName] = useState(''); const [mobile, setMobile] = useState(''); const patientsQ = useQuery>({ queryKey: ['create-patients', patientSearch], queryFn: () => api.get(`/api/v1/patient?search=${encodeURIComponent(patientSearch)}&limit=10`), enabled: patientSearch.trim().length >= 2, }); const patients = useMemo(() => patientsQ.data?.data ?? [], [patientsQ.data]); // ── مشخصات سرویس const [sectionUuid, setSectionUuid] = useState(''); const [itemUuid, setItemUuid] = useState(''); const [staffUuid, setStaffUuid] = useState(''); const sectionsQ = useQuery>({ queryKey: ['service-sections'], queryFn: () => api.get('/api/v1/service-sections') }); const itemsQ = useQuery>({ queryKey: ['service-items', sectionUuid], queryFn: () => api.get(`/api/v1/service-items/${sectionUuid}`), enabled: !!sectionUuid, }); const staffQ = useQuery>({ queryKey: ['staff-list'], queryFn: () => api.get('/api/v1/staff') }); // ── زمان نوبت const [date, setDate] = useState(params.get('date') || today); const [duration, setDuration] = useState(40); const [start, setStart] = useState('15:00'); const [end, setEnd] = useState(addMinutes('15:00', 40)); useEffect(() => { setEnd(addMinutes(start, duration)); }, [start, duration]); // ── بیعانه / وضعیت / توضیحات const [depositRequired, setDepositRequired] = useState(false); const [depositRials, setDepositRials] = useState(0); const [status, setStatus] = useState('pending'); const [note, setNote] = useState(''); const effectiveName = picked?.user_name || name.trim(); const effectiveMobile = picked?.user_mobile || mobile.trim(); const valid = !!doctorUuid && !!date && effectiveName.length >= 2 && effectiveMobile.length >= 10 && !!start && !!end; const create = useMutation({ mutationFn: async () => { const createEndpoint = primaryRole === 'admin' ? '/api/v1/admin/appointment' : '/api/v1/my/appointment'; const payload: Record = { doctor_uuid: doctorUuid, slot_start: toEpoch(date, start), slot_end: toEpoch(date, end), patient_name: effectiveName, patient_mobile: effectiveMobile, ...(sectionUuid ? { service_section_uuid: sectionUuid } : {}), ...(itemUuid ? { service_item_uuid: itemUuid } : {}), ...(staffUuid ? { staff_uuid: staffUuid } : {}), ...(depositRequired ? { deposit_required: true, deposit_amount_rials: depositRials } : {}), ...(note.trim() ? { note: note.trim() } : {}), }; const res: any = await api.post(createEndpoint, payload); if (status !== 'pending' && res?.data?.uuid) { await api.patch(`/api/v1/appointment/${res.data.uuid}/status`, { status, version: 1 }); } return res; }, onSuccess: () => { qc.invalidateQueries({ queryKey: ['appointments'] }); toast.success('نوبت با موفقیت ثبت شد'); navigate('/admin/appointments'); }, onError: (e: any) => toast.error(e.message || 'خطا در ثبت نوبت'), }); return (
{/* بردکرامب */}
نوبت ها ثبت نوبت جدید
{/* پزشک — فقط برای admin/clinic */} {!isDoctor && ( <>
پزشک:
setDoctorUuid(v ? String(v) : '')} placeholder="انتخاب پزشک..." isClearable height={38} />
)} {/* مراجعه کننده */}
اطلاعات مراجعه کننده:
{ setPicked(null); setPatientSearch(e.target.value); }} placeholder="جستجوی نام، شماره تماس، شماره پرونده..." />
{!picked && patients.length > 0 && (
{patients.map(p => ( ))}
)} {picked === null && ( <>
مراجعه کننده جدید
setName(e.target.value)} placeholder="نام و نام خانوادگی مراجعه کننده" />
setMobile(e.target.value)} placeholder="شماره تماس مراجعه کننده" dir="ltr" />
)} {/* مشخصات سرویس */}
مشخصات سرویس:
{/* زمان نوبت */}
زمان نوبت:
setDuration(Math.max(5, Number(e.target.value) || 0))} dir="ltr" />
setStart(e.target.value)} dir="ltr" />
setEnd(e.target.value)} dir="ltr" />
{/* بیعانه */}
بیعانه:
{depositRequired && }
{depositRequired && (
)}