import React, { useEffect, useRef, useState } from 'react'; import ReactDOM from 'react-dom'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { ChevronDownIcon } from '@heroicons/react/24/outline'; import { toast } from 'sonner'; import { api } from '../../lib/api'; const STATUS_META: Record = { pending: { label: 'رزرو شده', color: '#3b82f6' }, confirmed: { label: 'تأیید شده', color: '#22c55e' }, completed: { label: 'تکمیل شده', color: '#16a34a' }, cancelled_by_doctor: { label: 'لغو پزشک', color: '#ef4444' }, cancelled_by_user: { label: 'لغو بیمار', color: '#ef4444' }, no_show: { label: 'غیبت', color: '#9ca3af' }, expired: { label: 'منقضی شده', color: '#9ca3af' }, }; const TRANSITIONS: Record = { pending: ['confirmed', 'cancelled_by_doctor', 'cancelled_by_user', 'expired'], confirmed: ['completed', 'cancelled_by_doctor', 'cancelled_by_user', 'no_show'], }; interface Props { uuid: string; currentStatus: string; version: number; queryKey: unknown[]; } export default function AppointmentStatusDropdown({ uuid, currentStatus, version, queryKey }: Props) { const [open, setOpen] = useState(false); const [menuPos, setMenuPos] = useState<{ top: number; right: number } | null>(null); const btnRef = useRef(null); const menuRef = useRef(null); const qc = useQueryClient(); useEffect(() => { if (!open) return; function handler(e: MouseEvent) { const target = e.target as Node; if (!btnRef.current?.contains(target) && !menuRef.current?.contains(target)) { setOpen(false); } } document.addEventListener('mousedown', handler); return () => document.removeEventListener('mousedown', handler); }, [open]); // Reposition on scroll/resize while open useEffect(() => { if (!open) return; function reposition() { if (btnRef.current) { const rect = btnRef.current.getBoundingClientRect(); setMenuPos({ top: rect.bottom + 4, right: window.innerWidth - rect.right }); } } window.addEventListener('scroll', reposition, true); window.addEventListener('resize', reposition); return () => { window.removeEventListener('scroll', reposition, true); window.removeEventListener('resize', reposition); }; }, [open]); const mutation = useMutation({ mutationFn: (newStatus: string) => api.patch(`/api/v1/appointment/${uuid}/status`, { status: newStatus, version }), onSuccess: () => { qc.invalidateQueries({ queryKey }); setOpen(false); }, onError: () => toast.error('خطا در تغییر وضعیت'), }); const meta = STATUS_META[currentStatus] ?? { label: currentStatus, color: '#9ca3af' }; const nextStatuses = TRANSITIONS[currentStatus] ?? []; function handleToggle() { if (!open && btnRef.current) { const rect = btnRef.current.getBoundingClientRect(); setMenuPos({ top: rect.bottom + 4, right: window.innerWidth - rect.right }); } setOpen(o => !o); } return (
{open && menuPos && nextStatuses.length > 0 && ReactDOM.createPortal(
{nextStatuses.map(s => { const sm = STATUS_META[s] ?? { label: s, color: '#9ca3af' }; return ( ); })}
, document.body )}
); }