import { useEffect, useMemo, useState } from 'react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { PlusIcon } from '@heroicons/react/24/outline'; import { toast } from 'sonner'; import { api } from '../lib/api'; import type { ApiResponse } from '../lib/api'; import Modal from './ui/Modal'; import PersianDateInput from './ui/PersianDateInput'; import PriceInput from './ui/PriceInput'; import { WalletChargeLink } from './AppointmentActions'; 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')}`; }; /** * اضافه کردن نوبت جدید (Figma add.pdf) — rich create form: patient * search-or-new, بخش/سرویس/پرسنل, date + default-duration + start/end time, * deposit toggle, status and notes. POSTs the extended /my/appointment. */ export default function NewAppointmentDrawer({ doctorUuid, defaultDate, queryKey, onClose, isReserve = false }: { doctorUuid: string; /** ISO Y-m-d — the currently viewed day. */ defaultDate: string; queryKey: unknown[]; onClose: () => void; /** true → «اضافه کردن نوبت رزرو» (day-level entry, no time fields). */ isReserve?: boolean; }) { const qc = useQueryClient(); // ── patient: pick an existing record or enter a new person ──────────────── const [patientSearch, setPatientSearch] = useState(''); const [pickedPatient, setPickedPatient] = useState(null); const [name, setName] = useState(''); const [mobile, setMobile] = useState(''); const patientsQ = useQuery>({ queryKey: ['drawer-patients', patientSearch], queryFn: () => api.get(`/api/v1/patient?search=${encodeURIComponent(patientSearch)}&limit=10`), enabled: patientSearch.trim().length >= 2, }); // ── service specs ────────────────────────────────────────────────────────── 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'), }); // ── timing ───────────────────────────────────────────────────────────────── const [date, setDate] = useState(defaultDate); 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]); // ── deposit / status / notes ─────────────────────────────────────────────── const [depositRequired, setDepositRequired] = useState(false); const [depositRials, setDepositRials] = useState(0); const [status, setStatus] = useState('pending'); const [note, setNote] = useState(''); const effectiveName = pickedPatient?.user_name || name.trim(); const effectiveMobile = pickedPatient?.user_mobile || mobile.trim(); const valid = !!doctorUuid && !!date && effectiveName.length >= 2 && effectiveMobile.length >= 10 && (isReserve || (!!start && !!end)); const create = useMutation({ mutationFn: async () => { const payload: Record = { doctor_uuid: doctorUuid, slot_start: isReserve ? toEpoch(date, '00:00') : toEpoch(date, start), slot_end: isReserve ? toEpoch(date, '00:00') : toEpoch(date, end), patient_name: effectiveName, patient_mobile: effectiveMobile, is_reserve: isReserve, ...(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('/api/v1/my/appointment', payload); // POST creates a pending booking; apply the picked status afterwards. 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 }); toast.success(isReserve ? 'نوبت رزرو ثبت شد' : 'نوبت با موفقیت ثبت شد'); onClose(); }, onError: (e: any) => toast.error(e.message || 'خطا در ثبت نوبت'), }); const label = { fontSize: 12.5, color: 'var(--text-3)' } as const; const sel = { width: '100%', height: 38, borderRadius: 'var(--r-sm)', border: '1px solid var(--border)', background: 'var(--surface)', fontSize: 13, fontFamily: 'inherit', padding: '0 10px' } as const; const patients = useMemo(() => patientsQ.data?.data ?? [], [patientsQ.data]); return (
اطلاعات مراجعه کننده:
{ setPickedPatient(null); setPatientSearch(e.target.value); }} placeholder="جستجوی نام، شماره تماس، شماره پرونده..." />
{!pickedPatient && patients.length > 0 && (
{patients.map(p => ( ))}
)} {pickedPatient === null && ( <>
مراجعه کننده جدید
setName(e.target.value)} placeholder="نام و نام خانوادگی مراجعه کننده را وارد نمایید" />
setMobile(e.target.value)} placeholder="شماره تماس مراجعه کننده را وارد نمایید" dir="ltr" />
)}
مشخصات سرویس:
زمان نوبت:
setDuration(Math.max(5, Number(e.target.value) || 0))} dir="ltr" />
{!isReserve && (
setStart(e.target.value)} dir="ltr" />
setEnd(e.target.value)} dir="ltr" />
)} {!isReserve && ( <>
بیعانه:
{depositRequired && (
)} )}