From e2a6b280803966febc19d0cbceb2c58c78ef8fc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Gustavo?= <166467972+JoaoGustavo-dev@users.noreply.github.com> Date: Thu, 6 Nov 2025 00:35:59 -0300 Subject: [PATCH] fixing-patient-page --- susconecta/app/paciente/page.tsx | 160 +++++++++++++++--- .../paciente/resultados/ResultadosClient.tsx | 66 +++++--- 2 files changed, 183 insertions(+), 43 deletions(-) diff --git a/susconecta/app/paciente/page.tsx b/susconecta/app/paciente/page.tsx index 697c02f..2fc841b 100644 --- a/susconecta/app/paciente/page.tsx +++ b/susconecta/app/paciente/page.tsx @@ -18,7 +18,8 @@ import Link from 'next/link' import ProtectedRoute from '@/components/shared/ProtectedRoute' import { useAuth } from '@/hooks/useAuth' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' -import { buscarPacientes, buscarPacientePorUserId, getUserInfo, listarAgendamentos, buscarMedicosPorIds, buscarMedicos, atualizarPaciente, buscarPacientePorId, getDoctorById } from '@/lib/api' +import { buscarPacientes, buscarPacientePorUserId, getUserInfo, listarAgendamentos, buscarMedicosPorIds, buscarMedicos, atualizarPaciente, buscarPacientePorId, getDoctorById, atualizarAgendamento, deletarAgendamento } from '@/lib/api' +import { CalendarRegistrationForm } from '@/components/features/forms/calendar-registration-form' import { buscarRelatorioPorId, listarRelatoriosPorMedico } from '@/lib/reports' import { ENV_CONFIG } from '@/lib/env-config' import { listarRelatoriosPorPaciente } from '@/lib/reports' @@ -35,7 +36,6 @@ const strings = { ultimosExames: 'Últimos Exames', mensagensNaoLidas: 'Mensagens Não Lidas', agendar: 'Agendar', - reagendar: 'Reagendar', cancelar: 'Cancelar', detalhes: 'Detalhes', adicionarCalendario: 'Adicionar ao calendário', @@ -445,11 +445,10 @@ export default function PacientePage() { - + ) } - // Consultas fictícias const [currentDate, setCurrentDate] = useState(new Date()) // helper: produce a local YYYY-MM-DD key (uses local timezone, not toISOString UTC) @@ -519,10 +518,15 @@ export default function PacientePage() { const selectedDate = new Date(currentDate); selectedDate.setHours(0, 0, 0, 0); const isSelectedDateToday = selectedDate.getTime() === today.getTime() - // Appointments state (loaded when component mounts) - const [appointments, setAppointments] = useState(null) - const [loadingAppointments, setLoadingAppointments] = useState(false) - const [appointmentsError, setAppointmentsError] = useState(null) + // Appointments state (loaded when component mounts) + const [appointments, setAppointments] = useState(null) + const [doctorsMap, setDoctorsMap] = useState>({}) // Store doctor info by ID + const [loadingAppointments, setLoadingAppointments] = useState(false) + const [appointmentsError, setAppointmentsError] = useState(null) + // expanded appointment id for inline details (kept for possible fallback) + const [expandedId, setExpandedId] = useState(null) + // selected appointment for modal details + const [selectedAppointment, setSelectedAppointment] = useState(null) useEffect(() => { let mounted = true @@ -608,6 +612,7 @@ export default function PacientePage() { } }) + setDoctorsMap(doctorsMap) setAppointments(mapped) } catch (err: any) { console.warn('[Consultas] falha ao carregar agendamentos', err) @@ -638,6 +643,60 @@ export default function PacientePage() { const _dialogSource = (appointments !== null ? appointments : consultasFicticias) const _todaysAppointments = (_dialogSource || []).filter((c: any) => c.data === todayStr) + // helper: present a localized label for appointment status + const statusLabel = (s: any) => { + const raw = (s === null || s === undefined) ? '' : String(s) + const key = raw.toLowerCase() + const map: Record = { + 'requested': 'Solicitado', + 'request': 'Solicitado', + 'confirmed': 'Confirmado', + 'confirmada': 'Confirmada', + 'confirmado': 'Confirmado', + 'completed': 'Concluído', + 'concluído': 'Concluído', + 'cancelled': 'Cancelado', + 'cancelada': 'Cancelada', + 'cancelado': 'Cancelado', + 'pending': 'Pendente', + 'pendente': 'Pendente', + 'checked_in': 'Registrado', + 'in_progress': 'Em andamento', + 'no_show': 'Não compareceu' + } + return map[key] || raw + } + + // map an appointment (row) to the CalendarRegistrationForm's formData shape + const mapAppointmentToFormData = (appointment: any) => { + // Use the raw appointment with all fields: doctor_id, scheduled_at, appointment_type, etc. + const schedIso = appointment.scheduled_at || (appointment.data && appointment.hora ? `${appointment.data}T${appointment.hora}` : null) || null + const baseDate = schedIso ? new Date(schedIso) : new Date() + const appointmentDate = schedIso ? baseDate.toISOString().split('T')[0] : '' + const startTime = schedIso ? baseDate.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' }) : (appointment.hora || '') + const duration = appointment.duration_minutes ?? appointment.duration ?? 30 + + // Get doctor name from doctorsMap if available + const docName = appointment.medico || (appointment.doctor_id ? doctorsMap[String(appointment.doctor_id)]?.full_name : null) || appointment.doctor_name || appointment.professional_name || '---' + + return { + id: appointment.id, + patientName: docName, + patientId: null, + doctorId: appointment.doctor_id ?? null, + professionalName: docName, + appointmentDate, + startTime, + endTime: '', + status: appointment.status || undefined, + appointmentType: appointment.appointment_type || appointment.type || (appointment.local ? 'presencial' : 'teleconsulta'), + duration_minutes: duration, + notes: appointment.notes || '', + } + } + + + return (
{/* Hero Section */} @@ -771,7 +830,7 @@ export default function PacientePage() { ? 'bg-linear-to-r from-amber-500 to-amber-600 shadow-amber-500/20' : 'bg-linear-to-r from-red-500 to-red-600 shadow-red-500/20' }`}> - {consulta.status} + {statusLabel(consulta.status)}
@@ -781,28 +840,43 @@ export default function PacientePage() { type="button" size="sm" className="border border-primary/30 text-primary bg-primary/5 hover:bg-primary! hover:text-white! hover:border-primary! transition-all duration-200 focus-visible:ring-2 focus-visible:ring-primary/40 active:scale-95 text-xs font-semibold flex-1" + onClick={() => setSelectedAppointment(consulta)} > Detalhes - {consulta.status !== 'Cancelada' && ( - - )} + {/* Reagendar removed by request */} {consulta.status !== 'Cancelada' && ( )} + + {/* Inline detalhes removed: modal will show details instead */} + )) @@ -811,6 +885,45 @@ export default function PacientePage() { + + + + !open && setSelectedAppointment(null)}> + + + Detalhes da Consulta + Detalhes da consulta +
+ {selectedAppointment ? ( + <> +
+
Profissional: {selectedAppointment.medico || '-'}
+
Especialidade: {selectedAppointment.especialidade || '-'}
+
+ +
+
Data: {(function(d:any,h:any){ try{ const dt = new Date(String(d) + 'T' + String(h||'00:00')); return formatDatePt(dt) }catch(e){ return String(d||'-') } })(selectedAppointment.data, selectedAppointment.hora)}
+
Hora: {selectedAppointment.hora || '-'}
+
Status: {statusLabel(selectedAppointment.status) || '-'}
+
+ + ) : ( +
Carregando...
+ )} +
+
+ +
+ +
+
+
+
+ + {/* Reagendar feature removed */} + ) } @@ -1262,7 +1375,7 @@ export default function PacientePage() { setReportsPage(1) }, [reports]) - return ( + return (<>

Laudos

@@ -1334,10 +1447,13 @@ export default function PacientePage() { )} +
+ + !open && setSelectedReport(null)}> - - + + {selectedReport && ( (() => { const looksLikeIdStr = (s: any) => { @@ -1422,7 +1538,7 @@ export default function PacientePage() { - + ) } diff --git a/susconecta/app/paciente/resultados/ResultadosClient.tsx b/susconecta/app/paciente/resultados/ResultadosClient.tsx index 1fa8519..b471740 100644 --- a/susconecta/app/paciente/resultados/ResultadosClient.tsx +++ b/susconecta/app/paciente/resultados/ResultadosClient.tsx @@ -148,7 +148,7 @@ export default function ResultadosClient() { try { setLoadingMedicos(true) console.log('[ResultadosClient] Initial doctors fetch starting') - const list = await buscarMedicos('medico').catch((err) => { + const list = await buscarMedicos('').catch((err) => { console.error('[ResultadosClient] Initial fetch error:', err) return [] }) @@ -175,7 +175,7 @@ export default function ResultadosClient() { setAgendaByDoctor({}) setAgendasExpandida({}) // termo de busca: usar a especialidade escolhida - const termo = (especialidadeHero && especialidadeHero !== 'Veja mais') ? especialidadeHero : 'medico' + const termo = (especialidadeHero && especialidadeHero !== 'Veja mais') ? especialidadeHero : '' console.log('[ResultadosClient] Fetching doctors with term:', termo) const list = await buscarMedicos(termo).catch((err) => { console.error('[ResultadosClient] buscarMedicos error:', err) @@ -219,9 +219,9 @@ export default function ResultadosClient() { }, [searchQuery]) // 3) Carregar horários disponíveis para um médico (próximos 7 dias) e agrupar por dia - async function loadAgenda(doctorId: string) { - if (!doctorId) return - if (agendaLoading[doctorId]) return + async function loadAgenda(doctorId: string): Promise<{ iso: string; label: string } | null> { + if (!doctorId) return null + if (agendaLoading[doctorId]) return null setAgendaLoading((s) => ({ ...s, [doctorId]: true })) try { // janela de 7 dias @@ -271,10 +271,12 @@ export default function ResultadosClient() { nearest = { iso: s.iso, label: s.label } } - setAgendaByDoctor((prev) => ({ ...prev, [doctorId]: days })) - setNearestSlotByDoctor((prev) => ({ ...prev, [doctorId]: nearest })) + setAgendaByDoctor((prev) => ({ ...prev, [doctorId]: days })) + setNearestSlotByDoctor((prev) => ({ ...prev, [doctorId]: nearest })) + return nearest } catch (e: any) { showToast('error', e?.message || 'Falha ao buscar horários') + return null } finally { setAgendaLoading((s) => ({ ...s, [doctorId]: false })) } @@ -752,19 +754,7 @@ export default function ResultadosClient() { - - - {/* Search input para buscar médico por nome */} + {/* Search input para buscar médico por nome (movido antes do Select de bairro para ficar ao lado visualmente) */}
+ +