import type { FC } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { Camera, ClipboardList, Clock, FileSignature, Send } from 'lucide-react'; import { Badge, Button, Modal, Skeleton, EmptyState, toast } from './ui'; import { odooApi, type FormularioFoto, type FormularioRespuesta } from '../services/odoo'; import EnviarFormularioModal from './formulario/EnviarFormularioModal'; import { MOMENTO_LABEL, valorLegible } from './formulario/tipos'; import { useAuth } from '../lib/auth'; import { useGlosario } from '../lib/glosario'; // Sección "Formularios" del expediente del paciente: respuestas (con quién y // cuándo), pendientes marcados, detalle con firma y fotos, y la galería de // fotos antes/después agrupada por fecha. interface Props { patientId: number; patientName?: string; } const estadoVariant = (estado: string) => estado === 'llenado' ? 'success' : estado === 'enviado' ? 'warning' : estado === 'cancelado' ? 'danger' : 'default'; const fechaCorta = (s: string | null) => { if (!s) return '—'; const d = new Date(`${s.slice(0, 10)}T12:00:00`); if (Number.isNaN(d.getTime())) return s.slice(0, 10); return d.toLocaleDateString('es-MX', { day: '2-digit', month: 'short', year: 'numeric' }); }; const FormulariosPaciente: FC = ({ patientId, patientName = '' }) => { const { can } = useAuth(); const { term } = useGlosario(); const [respuestas, setRespuestas] = useState([]); const [fotos, setFotos] = useState([]); const [loading, setLoading] = useState(true); const [detalle, setDetalle] = useState(null); const [enviarOpen, setEnviarOpen] = useState(false); const cargar = useCallback(async () => { setLoading(true); try { const [resR, resF] = await Promise.all([ odooApi.getFormularioRespuestas({ partner_id: patientId }), odooApi.getFormularioFotos({ partner_id: patientId }), ]); if (resR.status === 'success') setRespuestas(resR.respuestas); if (resF.status === 'success') setFotos(resF.fotos); } catch { toast.error('No se pudieron cargar los formularios'); } finally { setLoading(false); } }, [patientId]); useEffect(() => { cargar(); }, [cargar]); const pendientes = respuestas.filter((r) => r.estado === 'enviado'); const galeria = useMemo(() => { const grupos = new Map(); for (const f of fotos) { const dia = (f.create_date || '').slice(0, 10) || 'sin-fecha'; if (!grupos.has(dia)) grupos.set(dia, []); grupos.get(dia)!.push(f); } return Array.from(grupos.entries()).sort((a, b) => (a[0] < b[0] ? 1 : -1)); }, [fotos]); const abrirDetalle = async (id: number) => { try { const res = await odooApi.getFormularioRespuesta(id); if (res.status === 'success') setDetalle(res.respuesta); } catch { toast.error('No se pudo abrir el formulario'); } }; return (

Formularios y consentimientos

{can('formularios.enviar') && ( )}
{pendientes.length > 0 && (

{pendientes.length} formulario{pendientes.length > 1 ? 's' : ''} pendiente{pendientes.length > 1 ? 's' : ''} de llenar

    {pendientes.map((r) => (
  • {r.plantilla} · enviado {fechaCorta(r.enviado_at || r.create_date)}
  • ))}
)} {loading ? ( ) : respuestas.length === 0 ? ( } /> ) : (
    {respuestas.map((r) => (
  • ))}
)} {galeria.length > 0 && (

Galería antes / después

{galeria.map(([dia, items]) => (

{fechaCorta(dia)}

{items.map((f) => (
{f.name}
{MOMENTO_LABEL[f.kind] || f.kind}
))}
))}
)} setDetalle(null)} title={detalle ? `${detalle.plantilla} — ${detalle.name}` : 'Formulario'} maxWidth="2xl" > {detalle && (
{detalle.estado_label || detalle.estado} {fechaCorta(detalle.llenado_at || detalle.enviado_at || detalle.create_date)} · {detalle.creado_por_empleado ? 'Llenado por el personal' : `Llenado por el ${term('patient_singular').toLowerCase()}`}
{(detalle.campos || []).length > 0 && (
{(detalle.campos || []) .filter((c) => !['firma', 'foto', 'archivo'].includes(c.tipo)) .map((c) => (

{c.etiqueta}

{valorLegible(c, detalle.respuestas?.[c.clave])}

))}
)} {detalle.texto_legal && (
Texto legal aceptado

{detalle.texto_legal}

)} {detalle.tiene_firma && detalle.firma_url && (

Firma{detalle.firma_nombre ? ` de ${detalle.firma_nombre}` : ''}

Firma
)} {(detalle.fotos || []).length > 0 && (

Fotos y archivos

{(detalle.fotos || []).map((f) => (
{f.mimetype.startsWith('image/') ? ( {f.name} ) : ( {f.name} )}
{MOMENTO_LABEL[f.kind] || f.kind}
))}
)}
)}
setEnviarOpen(false)} patientId={patientId} patientName={patientName} onSent={cargar} />
); }; export default FormulariosPaciente;