import React, { useEffect, useRef, useState } from 'react'; import { useQuery } from '@tanstack/react-query'; import ReactDOM from 'react-dom'; import { useNavigate } from 'react-router-dom'; import { PlusIcon, EllipsisHorizontalIcon, EyeIcon, PencilIcon, ArrowDownOnSquareIcon, UserCircleIcon, PhoneIcon, } from '@heroicons/react/24/outline'; import { api } from '../lib/api'; import type { PaginatedResponse } from '../lib/api'; import type { Appointment } from '../types'; import { formatDate } from '../lib/utils'; import { useAuthStore } from '../stores/authStore'; import AppointmentStatusDropdown from '../components/ui/AppointmentStatusDropdown'; import SearchableSelect from '../components/ui/SearchableSelect'; import Pagination from '../components/ui/Pagination'; import NewAppointmentDrawer from '../components/NewAppointmentDrawer'; import { AppointmentInfoModal, TransferReserveModal } from '../components/AppointmentActions'; const LIMIT = 20; /** Row menu for reserve entries — the design offers only مشاهده/ویرایش/انتقال. */ function ReserveRowMenu({ appointment, queryKey }: { appointment: Appointment; queryKey: unknown[] }) { const [open, setOpen] = useState(false); const [modal, setModal] = useState(null); const [pos, setPos] = useState<{ top: number; right: number } | null>(null); const btnRef = useRef(null); const menuRef = useRef(null); const navigate = useNavigate(); useEffect(() => { if (!open) return; const handler = (e: MouseEvent) => { const t = e.target as Node; if (!btnRef.current?.contains(t) && !menuRef.current?.contains(t)) setOpen(false); }; document.addEventListener('mousedown', handler); return () => document.removeEventListener('mousedown', handler); }, [open]); const items = [ { label: 'مشاهده', icon: EyeIcon, onClick: () => { setOpen(false); setModal('info'); } }, { label: 'ویرایش', icon: PencilIcon, onClick: () => { setOpen(false); navigate(`/admin/appointments/${appointment.uuid}/edit`); } }, { label: 'انتقال به لیست نوبت ها', icon: ArrowDownOnSquareIcon, onClick: () => { setOpen(false); setModal('transfer'); } }, ]; return ( <> {open && pos && ReactDOM.createPortal(
{items.map(({ label, icon: Icon, onClick }) => ( ))}
, document.body, )} {modal === 'info' && setModal(null)} />} {modal === 'transfer' && setModal(null)} />} ); } /** نوبت‌های رزرو شده (reserve-table.pdf) — day-level reserve entries with transfer back to the live list. */ export default function ReserveAppointmentsPage() { const [page, setPage] = useState(1); const [drawerOpen, setDrawerOpen] = useState(false); const primaryRole = useAuthStore(s => s.primaryRole); const dbUuid = useAuthStore(s => s.dbUuid); const isClinic = primaryRole === 'clinic'; // the doctor the new reserve is booked for (doctors book for themselves) const [doctorUuid, setDoctorUuid] = useState(primaryRole === 'doctor' && dbUuid ? dbUuid : ''); const clinicDoctorsQ = useQuery({ queryKey: ['clinic-doctors', dbUuid], queryFn: () => api.get(`/api/v1/clinic/doctor-list/${dbUuid}`), enabled: isClinic && !!dbUuid, }); const clinicDoctors: { uuid: string; name: string }[] = clinicDoctorsQ.data?.data?.data ?? []; const queryKey = ['reserve-appointments', page]; const { data, isLoading } = useQuery>({ queryKey, queryFn: () => api.get(`/api/v1/my/appointments?reserve=1&page=${page}&limit=${LIMIT}`), }); const items = data?.data ?? []; const total = data?.meta?.totalRecords ?? 0; const today = new Date(); const defaultDate = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`; const th: React.CSSProperties = { padding: '10px 14px', textAlign: 'right', fontWeight: 600, color: 'var(--text-2)', whiteSpace: 'nowrap' }; const td: React.CSSProperties = { padding: '10px 14px', textAlign: 'right', color: 'var(--text)', verticalAlign: 'middle' }; return (

نوبت های رزرو شده

{isClinic && (
({ value: d.uuid, label: d.name }))} value={doctorUuid || null} onChange={v => setDoctorUuid(v ? String(v) : '')} placeholder="انتخاب پزشک..." isClearable height={34} />
)} {primaryRole !== 'representation' && ( )}
{isLoading ? (
در حال بارگذاری...
) : items.length === 0 ? (
نوبت رزروی ثبت نشده است
) : (
{items.map((a, i) => ( ))}
ردیف مراجعه کننده شماره تماس تاریخ سرویس پرسنل وضعیت عملیات
{((page - 1) * LIMIT + i + 1).toLocaleString('fa-IR')}
{a.patient_name || '—'}
{a.patient_mobile}
{formatDate(a.slot_start)} {a.service_item?.name || '—'} {a.staff?.full_name || '—'}
)}
{total > LIMIT && (
)} {drawerOpen && ( setDrawerOpen(false)} isReserve /> )}
); }