'use client' import type { ReactNode } from 'react' import { useState, useEffect } from 'react' import { useRouter } from 'next/navigation' import { Button } from '@/components/ui/button' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from '@/components/ui/dialog' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Textarea } from '@/components/ui/textarea' import { Avatar, AvatarFallback } from '@/components/ui/avatar' import { User, LogOut, Calendar, FileText, MessageCircle, UserCog, Home, Clock, FolderOpen, ChevronLeft, ChevronRight, MapPin, Stethoscope } from 'lucide-react' import { SimpleThemeToggle } from '@/components/simple-theme-toggle' import { UploadAvatar } from '@/components/ui/upload-avatar' 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, 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 // Simulação de internacionalização básica const strings = { dashboard: 'Dashboard', consultas: 'Consultas', exames: 'Exames & Laudos', mensagens: 'Mensagens', perfil: 'Perfil', sair: 'Sair', proximaConsulta: 'Próxima Consulta', ultimosExames: 'Últimos Exames', mensagensNaoLidas: 'Mensagens Não Lidas', agendar: 'Agendar', reagendar: 'Reagendar', cancelar: 'Cancelar', detalhes: 'Detalhes', adicionarCalendario: 'Adicionar ao calendário', visualizarLaudo: 'Visualizar Laudo', download: 'Download', compartilhar: 'Compartilhar', inbox: 'Caixa de Entrada', enviarMensagem: 'Enviar Mensagem', salvar: 'Salvar', editarPerfil: 'Editar Perfil', consentimentos: 'Consentimentos', notificacoes: 'Preferências de Notificação', vazio: 'Nenhum dado encontrado.', erro: 'Ocorreu um erro. Tente novamente.', carregando: 'Carregando...', sucesso: 'Salvo com sucesso!', erroSalvar: 'Erro ao salvar.', } export default function PacientePage() { const { logout, user } = useAuth() const [tab, setTab] = useState<'dashboard'|'consultas'|'exames'|'perfil'>('dashboard') // Simulação de loaders, empty states e erro const [loading, setLoading] = useState(false) const [error, setError] = useState('') const [toast, setToast] = useState<{type: 'success'|'error', msg: string}|null>(null) const handleLogout = async () => { setLoading(true) setError('') try { await logout() } catch { setError(strings.erro) } finally { setLoading(false) } } // Estado para edição do perfil const [isEditingProfile, setIsEditingProfile] = useState(false) const [profileData, setProfileData] = useState({ nome: '', email: user?.email || '', telefone: '', endereco: '', cidade: '', cep: '', biografia: '', id: undefined, foto_url: undefined, }) const [patientId, setPatientId] = useState(null) // Load authoritative patient row for the logged-in user (prefer user_id lookup) useEffect(() => { let mounted = true const uid = user?.id ?? null const uemail = user?.email ?? null if (!uid && !uemail) return async function loadProfile() { try { setLoading(true) setError('') // 1) exact lookup by user_id on patients table let paciente: any = null if (uid) paciente = await buscarPacientePorUserId(uid) // 2) fallback: search patients by email and prefer a row that has user_id equal to auth id if (!paciente && uemail) { try { const results = await buscarPacientes(uemail) if (results && results.length) { paciente = results.find((r: any) => String(r.user_id) === String(uid)) || results[0] } } catch (e) { console.warn('[PacientePage] buscarPacientes falhou', e) } } // 3) fallback: use getUserInfo() (auth profile) if available if (!paciente) { try { const info = await getUserInfo().catch(() => null) const p = info?.profile ?? null if (p) { // map auth profile to our local shape (best-effort) paciente = { full_name: p.full_name ?? undefined, email: p.email ?? undefined, phone_mobile: p.phone ?? undefined, } } } catch (e) { // ignore } } if (paciente && mounted) { try { if ((paciente as any).id) setPatientId(String((paciente as any).id)) } catch {} 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(paciente, ['full_name','fullName','name','nome','social_name']) || '' const telefone = getFirst(paciente, ['phone_mobile','phone','telefone','mobile']) || '' const rua = getFirst(paciente, ['street','logradouro','endereco','address']) const numero = getFirst(paciente, ['number','numero']) const bairro = getFirst(paciente, ['neighborhood','bairro']) const endereco = rua ? (numero ? `${rua}, ${numero}` : rua) + (bairro ? ` - ${bairro}` : '') : '' const cidade = getFirst(paciente, ['city','cidade','localidade']) || '' const cep = getFirst(paciente, ['cep','postal_code','zip']) || '' const biografia = getFirst(paciente, ['biography','bio','notes']) || '' const emailFromRow = getFirst(paciente, ['email']) || uemail || '' if (process.env.NODE_ENV !== 'production') console.debug('[PacientePage] paciente row', paciente) setProfileData({ nome, email: emailFromRow, telefone, endereco, cidade, cep, biografia }) } } catch (err) { console.warn('[PacientePage] erro ao carregar paciente', err) } finally { if (mounted) setLoading(false) } } loadProfile() return () => { mounted = false } // eslint-disable-next-line react-hooks/exhaustive-deps }, [user?.id, user?.email]) // Load authoritative patient row for the logged-in user (prefer user_id lookup) useEffect(() => { let mounted = true const uid = user?.id ?? null const uemail = user?.email ?? null if (!uid && !uemail) return async function loadProfile() { try { setLoading(true) setError('') let paciente: any = null if (uid) paciente = await buscarPacientePorUserId(uid) if (!paciente && uemail) { try { const res = await buscarPacientes(uemail) if (res && res.length) paciente = res.find((r:any) => String((r as any).user_id) === String(uid)) || res[0] } catch (e) { console.warn('[PacientePage] busca por email falhou', e) } } if (paciente && mounted) { try { if ((paciente as any).id) setPatientId(String((paciente as any).id)) } catch {} 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(paciente, ['full_name','fullName','name','nome','social_name']) || profileData.nome const telefone = getFirst(paciente, ['phone_mobile','phone','telefone','mobile']) || profileData.telefone const rua = getFirst(paciente, ['street','logradouro','endereco','address']) const numero = getFirst(paciente, ['number','numero']) const bairro = getFirst(paciente, ['neighborhood','bairro']) const endereco = rua ? (numero ? `${rua}, ${numero}` : rua) + (bairro ? ` - ${bairro}` : '') : profileData.endereco const cidade = getFirst(paciente, ['city','cidade','localidade']) || profileData.cidade const cep = getFirst(paciente, ['cep','postal_code','zip']) || profileData.cep const biografia = getFirst(paciente, ['biography','bio','notes']) || profileData.biografia || '' const emailFromRow = getFirst(paciente, ['email']) || user?.email || profileData.email if (process.env.NODE_ENV !== 'production') console.debug('[PacientePage] paciente row', paciente) setProfileData((prev: any) => ({ ...prev, nome, email: emailFromRow, telefone, endereco, cidade, cep, biografia })) } } catch (err) { console.warn('[PacientePage] erro ao carregar paciente', err) } finally { if (mounted) setLoading(false) } } loadProfile() return () => { mounted = false } // eslint-disable-next-line react-hooks/exhaustive-deps }, [user?.id, user?.email]) const handleProfileChange = (field: string, value: string) => { setProfileData((prev: any) => ({ ...prev, [field]: value })) } 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} {loading ? '...' : (nextAppt ?? '-')} {strings.ultimosExames} {loading ? '...' : (examsCount !== null ? String(examsCount) : '-')}
) } // Consultas fictícias const [currentDate, setCurrentDate] = useState(new Date()) // helper: produce a local YYYY-MM-DD key (uses local timezone, not toISOString UTC) const localDateKey = (d: Date) => { const y = d.getFullYear() const m = String(d.getMonth() + 1).padStart(2, '0') const day = String(d.getDate()).padStart(2, '0') return `${y}-${m}-${day}` } const consultasFicticias = [ { id: 1, medico: "Dr. Carlos Andrade", especialidade: "Cardiologia", local: "Clínica Coração Feliz", data: localDateKey(new Date()), hora: "09:00", status: "Confirmada" }, { id: 2, medico: "Dra. Fernanda Lima", especialidade: "Dermatologia", local: "Clínica Pele Viva", data: localDateKey(new Date()), hora: "14:30", status: "Pendente" }, { id: 3, medico: "Dr. João Silva", especialidade: "Ortopedia", local: "Hospital Ortopédico", data: (() => { let d = new Date(); d.setDate(d.getDate()+1); return localDateKey(d) })(), hora: "11:00", status: "Cancelada" }, ]; function formatDatePt(date: Date) { return date.toLocaleDateString('pt-BR', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' }); } function navigateDate(direction: 'prev' | 'next') { const newDate = new Date(currentDate); newDate.setDate(newDate.getDate() + (direction === 'next' ? 1 : -1)); setCurrentDate(newDate); } function goToToday() { setCurrentDate(new Date()); } const todayStr = localDateKey(currentDate) const consultasDoDia = consultasFicticias.filter(c => c.data === todayStr); function Consultas() { const router = useRouter() const [tipoConsulta, setTipoConsulta] = useState<'teleconsulta' | 'presencial'>('teleconsulta') const [especialidade, setEspecialidade] = useState('cardiologia') const [localizacao, setLocalizacao] = useState('') const [mostrarAgendadas, setMostrarAgendadas] = useState(false) const hoverPrimaryClass = "transition duration-200 hover:bg-[#2563eb] hover:text-white focus-visible:ring-2 focus-visible:ring-[#2563eb]/60 active:scale-[0.97]" const activeToggleClass = "w-full transition duration-200 focus-visible:ring-2 focus-visible:ring-[#2563eb]/60 active:scale-[0.97] bg-[#2563eb] text-white hover:bg-[#2563eb] hover:text-white" const inactiveToggleClass = "w-full transition duration-200 bg-slate-50 text-[#2563eb] border border-[#2563eb]/30 hover:bg-slate-100 hover:text-[#2563eb] dark:bg-white/5 dark:text-white dark:hover:bg-white/10 dark:border-white/20" const hoverPrimaryIconClass = "rounded-xl bg-white text-[#1e293b] border border-black/10 shadow-[0_2px_8px_rgba(0,0,0,0.03)] transition duration-200 hover:bg-[#2563eb] hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#2563eb] dark:bg-slate-800 dark:text-slate-100 dark:border-white/10 dark:shadow-none dark:hover:bg-[#2563eb] dark:hover:text-white" const today = new Date(); today.setHours(0, 0, 0, 0); const selectedDate = new Date(currentDate); selectedDate.setHours(0, 0, 0, 0); const isSelectedDateToday = selectedDate.getTime() === today.getTime() // Appointments state (loaded when "Ver consultas agendadas" is opened) const [appointments, setAppointments] = useState(null) const [loadingAppointments, setLoadingAppointments] = useState(false) const [appointmentsError, setAppointmentsError] = useState(null) useEffect(() => { let mounted = true if (!mostrarAgendadas) return if (!patientId) { setAppointmentsError('Paciente não identificado. Faça login novamente.') return } async function loadAppointments() { try { setLoadingAppointments(true) setAppointmentsError(null) setAppointments(null) // Try `eq.` first, then fallback to `in.(id)` which some views expect const baseEncoded = encodeURIComponent(String(patientId)) const queriesToTry = [ `patient_id=eq.${baseEncoded}&order=scheduled_at.asc&limit=200`, `patient_id=in.(${baseEncoded})&order=scheduled_at.asc&limit=200`, ]; let rows: any[] = [] for (const q of queriesToTry) { try { // Debug: also fetch raw response to inspect headers/response body in the browser try { const token = (typeof window !== 'undefined') ? (localStorage.getItem('auth_token') || localStorage.getItem('token') || sessionStorage.getItem('auth_token') || sessionStorage.getItem('token')) : null const headers: Record = { apikey: ENV_CONFIG.SUPABASE_ANON_KEY, Accept: 'application/json', } if (token) headers.Authorization = `Bearer ${token}` const rawUrl = `${ENV_CONFIG.SUPABASE_URL}/rest/v1/appointments?${q}` console.debug('[Consultas][debug] GET', rawUrl, 'Headers(masked):', { ...headers, Authorization: headers.Authorization ? `${String(headers.Authorization).slice(0,6)}...${String(headers.Authorization).slice(-6)}` : undefined }) const rawRes = await fetch(rawUrl, { method: 'GET', headers }) const rawText = await rawRes.clone().text().catch(() => '') console.debug('[Consultas][debug] raw response', { url: rawUrl, status: rawRes.status, bodyPreview: (typeof rawText === 'string' && rawText.length > 0) ? rawText.slice(0, 200) : rawText }) } catch (dbgErr) { console.debug('[Consultas][debug] não foi possível capturar raw response', dbgErr) } const r = await listarAgendamentos(q) if (r && Array.isArray(r) && r.length) { rows = r break } // if r is empty array, continue to next query format } catch (e) { // keep trying next format console.debug('[Consultas] tentativa listarAgendamentos falhou para query', q, e) } } if (!mounted) return if (!rows || rows.length === 0) { // no appointments found for this patient using either filter setAppointments([]) return } const doctorIds = Array.from(new Set(rows.map((r: any) => r.doctor_id).filter(Boolean))) const doctorsMap: Record = {} if (doctorIds.length) { try { const docs = await buscarMedicosPorIds(doctorIds).catch(() => []) for (const d of docs || []) doctorsMap[d.id] = d } catch (e) { // ignore } } const mapped = (rows || []).map((a: any) => { const sched = a.scheduled_at ? new Date(a.scheduled_at) : null const doc = a.doctor_id ? doctorsMap[String(a.doctor_id)] : null return { id: a.id, medico: doc?.full_name || a.doctor_id || '---', especialidade: doc?.specialty || '', local: a.location || a.place || '', data: sched ? localDateKey(sched) : '', hora: sched ? sched.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' }) : '', status: a.status ? String(a.status) : 'Pendente', } }) setAppointments(mapped) } catch (err: any) { console.warn('[Consultas] falha ao carregar agendamentos', err) if (!mounted) return setAppointmentsError(err?.message ?? 'Falha ao carregar agendamentos.') setAppointments([]) } finally { if (mounted) setLoadingAppointments(false) } } loadAppointments() return () => { mounted = false } }, [mostrarAgendadas, patientId]) // Monta a URL de resultados com os filtros atuais const buildResultadosHref = () => { const qs = new URLSearchParams() qs.set('tipo', tipoConsulta) // 'teleconsulta' | 'presencial' if (especialidade) qs.set('especialidade', especialidade) if (localizacao) qs.set('local', localizacao) // indicate navigation origin so destination can alter UX (e.g., show modal instead of redirect) qs.set('origin', 'paciente') return `/resultados?${qs.toString()}` } // derived lists for the "Ver consultas agendadas" dialog (computed after appointments state is declared) const _dialogSource = (appointments !== null ? appointments : consultasFicticias) const _todaysAppointments = (_dialogSource || []).filter((c: any) => c.data === todayStr) return (

Agende sua próxima consulta

Escolha o formato ideal, selecione a especialidade e encontre o profissional perfeito para você.

{/* Remover campos de especialidade e localização, deixar só o botão centralizado */}
setMostrarAgendadas(open)}> Consultas agendadas Gerencie suas consultas confirmadas, pendentes ou canceladas.
{formatDatePt(currentDate)} {isSelectedDateToday && ( )}
{`${_todaysAppointments.length} consulta${_todaysAppointments.length !== 1 ? 's' : ''} agendada${_todaysAppointments.length !== 1 ? 's' : ''}`}
{loadingAppointments && mostrarAgendadas ? (
Carregando consultas...
) : appointmentsError ? (
{appointmentsError}
) : ( // prefer appointments (client-loaded) when present; fallback to fictitious list (() => { const todays = _todaysAppointments if (!todays || todays.length === 0) { return (

Nenhuma consulta agendada para este dia

Use a busca para marcar uma nova consulta.

) } return todays.map((consulta: any) => (
{consulta.medico}

{consulta.especialidade} • {consulta.local}

{consulta.hora}
{consulta.status}
{consulta.status !== 'Cancelada' && ( )} {consulta.status !== 'Cancelada' && ( )}
)) })() )}
) } // Selected report state const [selectedReport, setSelectedReport] = useState(null) function ExamesLaudos() { const [reports, setReports] = useState(null) const [loadingReports, setLoadingReports] = useState(false) const [reportsError, setReportsError] = useState(null) const [reportDoctorName, setReportDoctorName] = useState(null) useEffect(() => { let mounted = true if (!patientId) return setLoadingReports(true) setReportsError(null) listarRelatoriosPorPaciente(String(patientId)) .then(res => { if (!mounted) return setReports(Array.isArray(res) ? res : []) }) .catch(err => { console.warn('[ExamesLaudos] erro ao carregar laudos', err) if (!mounted) return setReportsError('Falha ao carregar laudos.') }) .finally(() => { if (mounted) setLoadingReports(false) }) return () => { mounted = false } }, [patientId]) // When a report is selected, try to fetch doctor name if we have an id useEffect(() => { let mounted = true if (!selectedReport) { setReportDoctorName(null) return } const maybeDoctorId = selectedReport.doctor_id || selectedReport.created_by || null if (!maybeDoctorId) { setReportDoctorName(null) return } (async () => { try { const docs = await buscarMedicosPorIds([String(maybeDoctorId)]).catch(() => []) if (!mounted) return if (docs && docs.length) { const doc0: any = docs[0] setReportDoctorName(doc0.full_name || doc0.name || doc0.fullName || null) } } catch (e) { // ignore } })() return () => { mounted = false } }, [selectedReport]) return (

Laudos

{loadingReports ? (
{strings.carregando}
) : reportsError ? (
{reportsError}
) : (!reports || reports.length === 0) ? (
Nenhum laudo encontrado para este paciente.
) : ( reports.map((r) => (
{r.title || r.name || r.report_name || 'Laudo'}
Data: {new Date(r.report_date || r.created_at || Date.now()).toLocaleDateString('pt-BR')}
)) )}
!open && setSelectedReport(null)}> Laudo Médico {selectedReport && ( <>
{selectedReport.title || selectedReport.name || 'Laudo'}
Data: {new Date(selectedReport.report_date || selectedReport.created_at || Date.now()).toLocaleDateString('pt-BR')}
{reportDoctorName &&
Profissional: {reportDoctorName}
}
{/* 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
{exam || '-'}
Diagnóstico
{diagnosis || '-'}
Conclusão
{conclusion || '-'}
Notas do Profissional
{notesHtml ? (
) : (
{notesText || '-'}
)}
) })()} {/* Optional: doctor signature or footer */} {selectedReport.doctor_signature && (
Assinatura: assinatura
)} )}
) } function Perfil() { const hasAddress = Boolean(profileData.endereco || profileData.cidade || profileData.cep) return (

Meu Perfil

{!isEditingProfile ? ( ) : (
)}
{/* Informações Pessoais */}

Informações Pessoais

{profileData.nome}

Este campo não pode ser alterado
{isEditingProfile ? ( handleProfileChange('email', e.target.value)} /> ) : (

{profileData.email}

)}
{isEditingProfile ? ( handleProfileChange('telefone', e.target.value)} /> ) : (

{profileData.telefone}

)}
{/* Endereço e Contato (render apenas se existir algum dado) */} {hasAddress && (

Endereço

{isEditingProfile ? ( handleProfileChange('endereco', e.target.value)} /> ) : (

{profileData.endereco}

)}
{isEditingProfile ? ( handleProfileChange('cidade', e.target.value)} /> ) : (

{profileData.cidade}

)}
{isEditingProfile ? ( handleProfileChange('cep', e.target.value)} /> ) : (

{profileData.cep}

)}
{/* Biografia removed: not used */}
)}
{/* Foto do Perfil */}

Foto do Perfil

handleProfileChange('foto_url', newUrl)} userName={profileData.nome} />
) } // Renderização principal return (
{/* Header só com título e botão de sair */}
Portal do Paciente
{/* Sidebar vertical */} {/* Conteúdo principal */}
{/* Toasts de feedback */} {toast && (
{toast.msg}
)} {/* Loader global */} {loading &&
{strings.carregando}
} {error &&
{error}
} {/* Conteúdo principal */} {!loading && !error && (
{tab==='dashboard' && } {tab==='consultas' && } {tab==='exames' && } {tab==='perfil' && }
)}
) }