import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { toast } from 'sonner'; import { api, ApiError, type ApiResponse } from '../lib/api'; import type { CancellationPolicy, CancellationPreview, CancellationResult, WaitlistEntry } from '../types'; /** * سیاست لغو و لیست انتظار. * * پیش‌نمایش لغو همیشه از سرور می‌آید — همان محاسبه‌ای که خودِ لغو انجام می‌دهد، تا عددی * که کاربر می‌بیند با آنچه کسر می‌شود یکی باشد. */ const POLICY_KEY = ['cancellation-policy']; const WAITLIST_KEY = ['waitlist']; function fail(e: unknown, fallback: string) { toast.error(e instanceof ApiError ? e.message : fallback); } interface PolicyResponse { default: CancellationPolicy | null; overrides: CancellationPolicy[]; } export function useCancellationPolicy() { const qc = useQueryClient(); const query = useQuery({ queryKey: POLICY_KEY, queryFn: () => api.get>('/api/v1/cancellation-policy'), }); const save = useMutation({ mutationFn: (body: Record) => api.put>('/api/v1/cancellation-policy', body), onSuccess: () => { toast.success('سیاست لغو ذخیره شد'); qc.invalidateQueries({ queryKey: POLICY_KEY }); }, onError: (e) => fail(e, 'ذخیرهٔ سیاست ناموفق بود'), }); return { policy: query.data?.data?.default ?? null, overrides: query.data?.data?.overrides ?? [], loading: query.isLoading, save, }; } export function useCancellationPreview(appointmentUuid: string | undefined, by: 'user' | 'doctor') { const query = useQuery({ queryKey: ['cancellation-preview', appointmentUuid, by], queryFn: () => api.get>( `/api/v1/appointment/${appointmentUuid}/cancellation-preview?by=${by}`, ), enabled: !!appointmentUuid, }); return { preview: query.data?.data, loading: query.isLoading }; } export function useCancelAppointment() { const qc = useQueryClient(); return useMutation({ mutationFn: ({ uuid, by, reason }: { uuid: string; by: 'user' | 'doctor'; reason?: string }) => api.post>(`/api/v1/appointment/${uuid}/cancel`, { by, ...(reason?.trim() ? { reason: reason.trim() } : {}), }), onSuccess: (res) => { const notified = res.data.waitlist_notified; toast.success( notified > 0 ? `نوبت لغو شد و ${notified} نفر از لیست انتظار خبر شدند` : 'نوبت لغو شد', ); qc.invalidateQueries({ queryKey: ['appointments'] }); qc.invalidateQueries({ queryKey: WAITLIST_KEY }); }, onError: (e) => fail(e, 'لغو نوبت ناموفق بود'), }); } export function useWaitlist(status?: string) { const qc = useQueryClient(); const key = [...WAITLIST_KEY, status ?? '']; const query = useQuery({ queryKey: key, queryFn: () => api.get>(`/api/v1/waitlist${status ? `?status=${status}` : ''}`), }); const remove = useMutation({ mutationFn: (uuid: string) => api.delete>(`/api/v1/waitlist/${uuid}`), onSuccess: () => { toast.success('از لیست انتظار حذف شد'); qc.invalidateQueries({ queryKey: WAITLIST_KEY }); }, onError: (e) => fail(e, 'حذف ناموفق بود'), }); return { entries: query.data?.data ?? [], loading: query.isLoading, remove }; } /** خلاصهٔ عدم حضور یک بیمار — نشان است نه مانع؛ مسدودسازی کارِ قانون `eligibility` است. */ export function usePatientNoShows(patientUuid: string | undefined) { const query = useQuery({ queryKey: ['patient-no-shows', patientUuid], queryFn: () => api.get>( `/api/v1/patient/${patientUuid}/no-shows`, ), enabled: !!patientUuid, }); return { noShows: query.data?.data ?? null }; }