import type { FC, ReactNode } from 'react'; import { useEffect, useState } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { AlertTriangle, ArrowLeft, Check, Clock, Loader2, MapPin, User } from 'lucide-react'; import { Button, Input } from '../components/ui'; import { odooApi, type BookingCita } from '../services/odoo'; import { fechaLargaISO } from '../lib/publicBooking'; // Página de gestión de una reserva hecha en el portal (ver / cancelar) usando // el token HMAC del enlace. Anónima: el token es la única credencial. const ReservarCita: FC = () => { const { token = '' } = useParams(); const navigate = useNavigate(); const [cita, setCita] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [motivo, setMotivo] = useState(''); const [cancelando, setCancelando] = useState(false); const [confirmar, setConfirmar] = useState(false); const [cancelada, setCancelada] = useState(false); useEffect(() => { let alive = true; if (!token) { setLoading(false); setError('Enlace no válido'); return; } odooApi.getBookingCita(token) .then((res) => { if (alive) setCita(res); }) .catch((err) => { const msg = (err as { response?: { data?: { message?: string }; status?: number } }); if (alive) setError(msg?.response?.data?.message || 'No encontramos esta reserva.'); }) .finally(() => { if (alive) setLoading(false); }); return () => { alive = false; }; }, [token]); const cancelar = async () => { setCancelando(true); setError(null); try { await odooApi.cancelBooking(token, motivo.trim()); setCancelada(true); setCita((prev) => (prev ? { ...prev, estado: 'cancelled', estado_label: 'Canceló', puede_cancelar: false } : prev)); } catch (err) { const msg = (err as { response?: { data?: { message?: string } } })?.response?.data?.message; setError(msg || 'No se pudo cancelar la reserva.'); } finally { setCancelando(false); } }; if (loading) { return ( ); } if (!cita) { return (

No encontramos tu reserva

{error || 'El enlace no es válido o ya expiró.'}

); } return (

Tu reserva

{cita.estado_label}

Folio {cita.folio}

} texto={`${fechaLargaISO(cita.fecha)} · ${cita.hora}`} /> } texto={cita.servicio} /> {cita.medico && } texto={cita.medico} />} {cita.sucursal && } texto={cita.sucursal} />}
{cancelada && (

Tu reserva quedó cancelada. Si fue un error, puedes agendar de nuevo.

)} {!cancelada && cita.puede_cancelar && (
{!confirmar ? ( ) : (

¿Seguro que quieres cancelar? Puedes dejarnos un motivo (opcional).

setMotivo(e.target.value)} /> {error &&

{error}

}
)}
)} {!cancelada && !cita.puede_cancelar && cita.estado !== 'cancelled' && (

Esta reserva ya no se puede cancelar en línea {cita.permitir_cancelar ? ` (con menos de ${cita.cancelar_hasta_horas} h de anticipación)` : ''}. {cita.telefono_contacto ? ` Comunícate al ${cita.telefono_contacto}.` : ' Comunícate con la clínica.'}

)}
); }; const Centro: FC<{ children: ReactNode }> = ({ children }) => (
{children}
); const Linea: FC<{ icon: ReactNode; texto: string }> = ({ icon, texto }) => (
{icon} {texto}
); export default ReservarCita;