import React, { useState } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { ArrowRightIcon } from '@heroicons/react/24/outline';
import { toast } from 'sonner';
import { api } from '../lib/api';
import type { ApiResponse } from '../lib/api';
import type { Appointment, AppointmentStatus, AppointmentEvent } from '../types';
import { formatDate, formatDateTime, toDate } from '../lib/utils';
import PageHeader from '../components/ui/PageHeader';
import StatusBadge from '../components/ui/StatusBadge';
import ConfirmDialog from '../components/ui/ConfirmDialog';
import SearchableSelect from '../components/ui/SearchableSelect';
import ConfirmAppointmentModal from '../components/appointments/ConfirmAppointmentModal';
const ALL_STATUSES: { value: AppointmentStatus; label: string }[] = [
{ value: 'pending', label: 'رزرو شده' },
{ value: 'confirmed', label: 'تأیید شده' },
{ value: 'completed', label: 'تکمیل شده' },
{ value: 'cancelled_by_doctor', label: 'لغو پزشک' },
{ value: 'cancelled_by_user', label: 'لغو بیمار' },
{ value: 'no_show', label: 'غیبت' },
{ value: 'expired', label: 'منقضی' },
];
// روزِ محلیِ نوبت (YYYY-MM-DD) برای بازگشت به همان تاریخ در لیست.
const isoDay = (ts?: number | null) => {
if (!ts) return '';
const d = new Date(ts * 1000);
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
};
const timeOf = (ts?: number | null) => {
const d = toDate(ts ?? null);
return d
? new Intl.DateTimeFormat('fa-IR-u-nu-latn', { hour: '2-digit', minute: '2-digit', hour12: false }).format(d)
: '—';
};
function InfoRow({ label, value }: { label: string; value: React.ReactNode }) {
return (
{label}
{value ?? '—'}
);
}
export default function AppointmentDetailPage() {
const { uuid } = useParams<{ uuid: string }>();
const navigate = useNavigate();
const qc = useQueryClient();
const [cancelOpen, setCancelOpen] = useState(false);
const [confirmOpen, setConfirmOpen] = useState(false);
const [cancelReason, setCancelReason] = useState('');
const [newStatus, setNewStatus] = useState('');
const { data, isLoading } = useQuery({
queryKey: ['appointment', uuid],
queryFn: () => api.get>(`/api/v1/appointment/${uuid}`),
enabled: !!uuid,
});
const eventsQuery = useQuery({
queryKey: ['appointment-events', uuid],
queryFn: () => api.get>(`/api/v1/appointment/${uuid}/events`),
enabled: !!uuid,
});
const events: AppointmentEvent[] = (eventsQuery.data?.data as any) ?? [];
const statusMutation = useMutation({
mutationFn: (status: string) =>
api.patch>(`/api/v1/appointment/${uuid}/status`, { status, version: appt?.version }),
onSuccess: () => {
toast.success('وضعیت نوبت بروزرسانی شد');
qc.invalidateQueries({ queryKey: ['appointment', uuid] });
qc.invalidateQueries({ queryKey: ['appointment-events', uuid] });
},
onError: (err: Error) => toast.error(err.message),
});
const cancelMutation = useMutation({
mutationFn: () => api.patch>(`/api/v1/appointment/${uuid}/status`, {
status: 'cancelled_by_doctor',
version: appt?.version,
...(cancelReason.trim() ? { cancel_reason: cancelReason.trim() } : {}),
}),
onSuccess: () => {
toast.success('نوبت لغو شد');
setCancelOpen(false);
setCancelReason('');
qc.invalidateQueries({ queryKey: ['appointment', uuid] });
qc.invalidateQueries({ queryKey: ['appointment-events', uuid] });
},
onError: (err: Error) => toast.error(err.message),
});
// پاسخ single تودرتو است: { data: { data: {...} } }
const appt: any = (data?.data as any)?.data ?? data?.data;
// قطعیکردن هزینه و پرداخت دارد؛ از مسیر مودال میرود، نه PATCH وضعیت.
function applyStatus() {
if (!newStatus) return;
if (newStatus === 'confirmed') {
setConfirmOpen(true);
return;
}
statusMutation.mutate(newStatus);
}
// بازگشت به همان روزِ نوبت (نه امروز).
const day = isoDay(appt?.slot_start);
const backTo = day ? `/admin/appointments?date=${day}` : '/admin/appointments';
return (
navigate(backTo)}
className="flex items-center gap-2 text-sm text-slate-500 dark:text-slate-400 hover:text-slate-800 dark:hover:text-slate-100 transition-colors">
بازگشت
}
/>
{isLoading ? (
{Array.from({ length: 6 }).map((_, i) => (
))}
) : appt ? (
اطلاعات بیمار
{appt.patient_mobile}} />
{appt.patient_reason && }
{appt.note && }
وضعیت و اقدامات
{appt.status === 'pending' && (
)}
({ value: s.value, label: s.label }))}
value={newStatus || null}
onChange={(v) => setNewStatus(v ? String(v) : '')}
placeholder="انتخاب وضعیت..."
isClearable
/>
تاریخچه رویدادها
{eventsQuery.isLoading ? (
) : events.length === 0 ? (
رویدادی برای این نوبت ثبت نشده است.
) : (
{events.map((ev, i) => (
-
{ev.title}
{formatDateTime(ev.created_at)}
{ev.actor_name && (
توسط: {ev.actor_name}
)}
{ev.reason && (
دلیل: {ev.reason}
)}
))}
)}
) : (
نوبتی یافت نشد
)}
cancelMutation.mutate()}
onCancel={() => { setCancelOpen(false); setCancelReason(''); }}
>
setConfirmOpen(false)}
queryKey={['appointment', uuid]}
/>
);
}