From a37dbb4c75fd15f1a0b7b3f40861638e8368485a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Gustavo?= <166467972+JoaoGustavo-dev@users.noreply.github.com> Date: Mon, 27 Oct 2025 23:51:22 -0300 Subject: [PATCH] fix-visual-adjustments --- susconecta/app/paciente/page.tsx | 231 +++++++++++++++++++------------ 1 file changed, 142 insertions(+), 89 deletions(-) diff --git a/susconecta/app/paciente/page.tsx b/susconecta/app/paciente/page.tsx index df43035..47f4d95 100644 --- a/susconecta/app/paciente/page.tsx +++ b/susconecta/app/paciente/page.tsx @@ -17,7 +17,7 @@ import Link from 'next/link' import ProtectedRoute from '@/components/ProtectedRoute' import { useAuth } from '@/hooks/useAuth' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' -import { buscarPacientes, buscarPacientePorUserId, getUserInfo, listarMensagensPorPaciente, listarAgendamentos, buscarMedicosPorIds } from '@/lib/api' +import { buscarPacientes, buscarPacientePorUserId, getUserInfo, listarAgendamentos, buscarMedicosPorIds, atualizarPaciente, buscarPacientePorId } from '@/lib/api' import { ENV_CONFIG } from '@/lib/env-config' import { listarRelatoriosPorPaciente } from '@/lib/reports' // reports are rendered statically for now @@ -55,7 +55,7 @@ const strings = { export default function PacientePage() { const { logout, user } = useAuth() - const [tab, setTab] = useState<'dashboard'|'consultas'|'exames'|'mensagens'|'perfil'>('dashboard') + const [tab, setTab] = useState<'dashboard'|'consultas'|'exames'|'perfil'>('dashboard') // Simulação de loaders, empty states e erro const [loading, setLoading] = useState(false) @@ -238,32 +238,121 @@ export default function PacientePage() { const handleProfileChange = (field: string, value: string) => { setProfileData((prev: any) => ({ ...prev, [field]: value })) } - const handleSaveProfile = () => { - setIsEditingProfile(false) - setToast({ type: 'success', msg: strings.sucesso }) + const handleSaveProfile = async () => { + if (!patientId) { + setToast({ type: 'error', msg: 'Paciente não identificado. Não foi possível salvar.' }) + setIsEditingProfile(false) + return + } + setLoading(true) + try { + const payload: any = {} + if (profileData.email) payload.email = profileData.email + if (profileData.telefone) payload.phone_mobile = profileData.telefone + if (profileData.endereco) payload.street = profileData.endereco + if (profileData.cidade) payload.city = profileData.cidade + if (profileData.cep) payload.cep = profileData.cep + if (profileData.biografia) payload.notes = profileData.biografia + + await atualizarPaciente(String(patientId), payload) + + // refresh patient row + const refreshed = await buscarPacientePorId(String(patientId)).catch(() => null) + if (refreshed) { + const getFirst = (obj: any, keys: string[]) => { + if (!obj) return undefined + for (const k of keys) { + const v = obj[k] + if (v !== undefined && v !== null && String(v).trim() !== '') return String(v) + } + return undefined + } + const nome = getFirst(refreshed, ['full_name','fullName','name','nome','social_name']) || profileData.nome + const telefone = getFirst(refreshed, ['phone_mobile','phone','telefone','mobile']) || profileData.telefone + const rua = getFirst(refreshed, ['street','logradouro','endereco','address']) + const numero = getFirst(refreshed, ['number','numero']) + const bairro = getFirst(refreshed, ['neighborhood','bairro']) + const endereco = rua ? (numero ? `${rua}, ${numero}` : rua) + (bairro ? ` - ${bairro}` : '') : profileData.endereco + const cidade = getFirst(refreshed, ['city','cidade','localidade']) || profileData.cidade + const cep = getFirst(refreshed, ['cep','postal_code','zip']) || profileData.cep + const biografia = getFirst(refreshed, ['biography','bio','notes']) || profileData.biografia || '' + const emailFromRow = getFirst(refreshed, ['email']) || profileData.email + const foto = getFirst(refreshed, ['foto_url','avatar_url','fotoUrl']) || profileData.foto_url + setProfileData((prev:any) => ({ ...prev, nome, email: emailFromRow, telefone, endereco, cidade, cep, biografia, foto_url: foto })) + } + + setIsEditingProfile(false) + setToast({ type: 'success', msg: strings.sucesso }) + } catch (err: any) { + console.warn('[PacientePage] erro ao atualizar paciente', err) + setToast({ type: 'error', msg: err?.message || strings.erroSalvar }) + } finally { + setLoading(false) + } } const handleCancelEdit = () => { setIsEditingProfile(false) } function DashboardCards() { + const [nextAppt, setNextAppt] = useState(null) + const [examsCount, setExamsCount] = useState(null) + const [loading, setLoading] = useState(false) + + useEffect(() => { + let mounted = true + async function load() { + if (!patientId) { + setNextAppt(null) + setExamsCount(null) + return + } + setLoading(true) + try { + // Load appointments for this patient (upcoming) + const q = `patient_id=eq.${encodeURIComponent(String(patientId))}&order=scheduled_at.asc&limit=200` + const ags = await listarAgendamentos(q).catch(() => []) + if (!mounted) return + const now = Date.now() + // find the first appointment with scheduled_at >= now + const upcoming = (ags || []).map((a: any) => ({ ...a, _sched: a.scheduled_at ? new Date(a.scheduled_at).getTime() : null })) + .filter((a: any) => a._sched && a._sched >= now) + .sort((x: any, y: any) => Number(x._sched) - Number(y._sched)) + if (upcoming && upcoming.length) { + setNextAppt(new Date(upcoming[0]._sched).toLocaleDateString('pt-BR')) + } else { + setNextAppt(null) + } + + // Load reports/laudos count + const reports = await listarRelatoriosPorPaciente(String(patientId)).catch(() => []) + if (!mounted) return + setExamsCount(Array.isArray(reports) ? reports.length : 0) + } catch (e) { + console.warn('[DashboardCards] erro ao carregar dados', e) + if (!mounted) return + setNextAppt(null) + setExamsCount(null) + } finally { + if (mounted) setLoading(false) + } + } + load() + return () => { mounted = false } + }, [patientId]) + return ( -
+
{strings.proximaConsulta} - 12/10/2025 + {loading ? '...' : (nextAppt ?? '-')} {strings.ultimosExames} - 2 + {loading ? '...' : (examsCount !== null ? String(examsCount) : '-')} - - - {strings.mensagensNaoLidas} - 1 - -
+
) } @@ -729,7 +818,10 @@ export default function PacientePage() { try { const docs = await buscarMedicosPorIds([String(maybeDoctorId)]).catch(() => []) if (!mounted) return - if (docs && docs.length) setReportDoctorName(docs[0].full_name || docs[0].name || null) + if (docs && docs.length) { + const doc0: any = docs[0] + setReportDoctorName(doc0.full_name || doc0.name || doc0.fullName || null) + } } catch (e) { // ignore } @@ -777,35 +869,47 @@ export default function PacientePage() { {reportDoctorName &&
Profissional: {reportDoctorName}
} - {/* Prefer HTML content when available */} - {selectedReport.content_html ? ( -
- ) : ( -
- {selectedReport.exam && ( + {/* Standardized laudo sections: CID, Exame, Diagnóstico, Conclusão, Notas (prefer HTML when available) */} + {(() => { + const cid = selectedReport.cid ?? selectedReport.cid_code ?? selectedReport.cidCode ?? selectedReport.cie ?? '-' + const exam = selectedReport.exam ?? selectedReport.exame ?? selectedReport.especialidade ?? selectedReport.report_type ?? '-' + const diagnosis = selectedReport.diagnosis ?? selectedReport.diagnostico ?? selectedReport.diagnosis_text ?? selectedReport.diagnostico_text ?? '' + const conclusion = selectedReport.conclusion ?? selectedReport.conclusao ?? selectedReport.conclusion_text ?? selectedReport.conclusao_text ?? '' + const notesHtml = selectedReport.content_html ?? selectedReport.conteudo_html ?? selectedReport.contentHtml ?? null + const notesText = selectedReport.content ?? selectedReport.body ?? selectedReport.conteudo ?? selectedReport.notes ?? selectedReport.observacoes ?? '' + return ( +
+
+
CID
+
{cid || '-'}
+
+
Exame
-
{selectedReport.exam}
+
{exam || '-'}
- )} - {selectedReport.diagnosis && ( +
Diagnóstico
-
{selectedReport.diagnosis}
+
{diagnosis || '-'}
- )} - {selectedReport.conclusion && ( +
Conclusão
-
{selectedReport.conclusion}
+
{conclusion || '-'}
- )} - {/* fallback to generic content/body */} - {!(selectedReport.content_html || selectedReport.diagnosis || selectedReport.conclusion || selectedReport.content || selectedReport.body) && ( -
{JSON.stringify(selectedReport, null, 2)}
- )} -
- )} + +
+
Notas do Profissional
+ {notesHtml ? ( +
+ ) : ( +
{notesText || '-'}
+ )} +
+
+ ) + })()} {/* Optional: doctor signature or footer */} {selectedReport.doctor_signature && (
Assinatura: assinatura
@@ -823,58 +927,7 @@ export default function PacientePage() { ) } - function Mensagens() { - const [msgs, setMsgs] = useState([]) - const [loadingMsgs, setLoadingMsgs] = useState(false) - const [msgsError, setMsgsError] = useState(null) - - useEffect(() => { - let mounted = true - if (!patientId) return - setLoadingMsgs(true) - setMsgsError(null) - listarMensagensPorPaciente(String(patientId)) - .then(res => { - if (!mounted) return - setMsgs(Array.isArray(res) ? res : []) - }) - .catch(err => { - console.warn('[Mensagens] erro ao carregar mensagens', err) - if (!mounted) return - setMsgsError('Falha ao carregar mensagens.') - }) - .finally(() => { if (mounted) setLoadingMsgs(false) }) - - return () => { mounted = false } - }, [patientId]) - - return ( -
-

Mensagens Recebidas

-
- {loadingMsgs ? ( -
Carregando mensagens...
- ) : msgsError ? ( -
{msgsError}
- ) : (!msgs || msgs.length === 0) ? ( -
Nenhuma mensagem encontrada.
- ) : ( - msgs.map((msg: any) => ( -
-
- - {msg.sender_name || msg.from || msg.doctor_name || 'Remetente'} - {!msg.read && Nova} -
-
{new Date(msg.created_at || msg.data || Date.now()).toLocaleString('pt-BR')}
-
{msg.body || msg.content || msg.text || JSON.stringify(msg)}
-
- )) - )} -
-
- ) - } + function Perfil() { const hasAddress = Boolean(profileData.endereco || profileData.cidade || profileData.cep) @@ -992,7 +1045,7 @@ export default function PacientePage() { - + {/* Conteúdo principal */} @@ -1012,7 +1065,7 @@ export default function PacientePage() { {tab==='dashboard' && } {tab==='consultas' && } {tab==='exames' && } - {tab==='mensagens' && } + {tab==='perfil' && } )}