diff --git a/susconecta/app/(main-routes)/consultas/page.tsx b/susconecta/app/(main-routes)/consultas/page.tsx index 8629de1..504decc 100644 --- a/susconecta/app/(main-routes)/consultas/page.tsx +++ b/susconecta/app/(main-routes)/consultas/page.tsx @@ -469,76 +469,78 @@ export default function ConsultasPage() { } return ( -
-
+
+ {/* Header responsivo */} +
-

Gerenciamento de Consultas

-

Visualize, filtre e gerencie todas as consultas da clínica.

+

Consultas

+

Gerencie todas as consultas da clínica

-
- {/* Pass origin so the Agenda page can return to Consultas when cancelling */} - - - + + + +
+ + {/* Filtros e busca responsivos */} +
+ {/* Linha 1: Busca */} +
+
+ + setSearchValue(e.target.value)} + onKeyDown={handleSearchKeyDown} + /> +
+
+ + {/* Linha 2: Selects responsivos */} +
+ + setFilterDate(e.target.value)} />
- - - Consultas Agendadas - Visualize, filtre e gerencie todas as consultas da clínica. -
-
-
- - setSearchValue(e.target.value)} - onKeyDown={handleSearchKeyDown} - /> -
-
- - setFilterDate(e.target.value)} /> -
-
- - {isLoading ? ( -
- - Carregando agendamentos... -
- ) : ( + {/* Loading state */} + {isLoading ? ( +
+ + Carregando agendamentos... +
+ ) : ( + <> + {/* Desktop Table - Hidden on mobile */} +
- Paciente - Médico - Status - Data e Hora - Ações + Paciente + Médico + Status + Data e Hora + Ações {paginatedAppointments.map((appointment) => { - // appointment.professional may now contain the doctor's name (resolved) const professionalLookup = mockProfessionals.find((p) => p.id === appointment.professional); const professionalName = typeof appointment.professional === "string" && appointment.professional && !professionalLookup ? appointment.professional @@ -546,8 +548,8 @@ export default function ConsultasPage() { return ( - {appointment.patient} - {professionalName} + {appointment.patient} + {professionalName} - {formatDate(appointment.scheduled_at ?? appointment.time)} + {formatDate(appointment.scheduled_at ?? appointment.time)} @@ -592,68 +594,146 @@ export default function ConsultasPage() { })}
- )} - - +
- {/* Controles de paginação */} -
-
- Itens por página: + {/* Mobile Cards - Hidden on desktop */} +
+ {paginatedAppointments.length > 0 ? ( + paginatedAppointments.map((appointment) => { + const professionalLookup = mockProfessionals.find((p) => p.id === appointment.professional); + const professionalName = typeof appointment.professional === "string" && appointment.professional && !professionalLookup + ? appointment.professional + : (professionalLookup ? professionalLookup.name : (appointment.professional || "Não encontrado")); + + return ( +
+
+
+
+
Paciente
+
{appointment.patient}
+
+ + + + + + handleView(appointment)}> + + Ver + + handleEdit(appointment)}> + + Editar + + handleDelete(appointment.id)} className="text-destructive"> + + Excluir + + + +
+
+
Médico
+
{professionalName}
+
+
+
Status
+ + {capitalize(appointment.status)} + +
+
+
Data e Hora
+
{formatDate(appointment.scheduled_at ?? appointment.time)}
+
+
+
+ ); + }) + ) : ( +
+ Nenhuma consulta encontrada +
+ )} +
+ + )} + + {/* Controles de paginação - Responsivos */} +
+
+ Itens por página: - + Mostrando {paginatedAppointments.length > 0 ? (currentPage - 1) * itemsPerPage + 1 : 0} a{" "} {Math.min(currentPage * itemsPerPage, appointments.length)} de {appointments.length}
-
+
- - Página {currentPage} de {totalPages || 1} + + Pág {currentPage} de {totalPages || 1}
diff --git a/susconecta/app/(main-routes)/dashboard/page.tsx b/susconecta/app/(main-routes)/dashboard/page.tsx index 8f90e09..67056ab 100644 --- a/susconecta/app/(main-routes)/dashboard/page.tsx +++ b/susconecta/app/(main-routes)/dashboard/page.tsx @@ -155,12 +155,12 @@ export default function DashboardPage() { if (loading) { return ( -
+
-
-
+
+
{[1, 2, 3, 4].map(i => ( -
+
))}
@@ -171,15 +171,15 @@ export default function DashboardPage() { // Se está exibindo formulário de paciente if (showPatientForm) { return ( -
-
- -

{editingPatientId ? "Editar paciente" : "Novo paciente"}

+

{editingPatientId ? "Editar paciente" : "Novo paciente"}

-
+
+
-

{editingDoctorId ? "Editar Médico" : "Novo Médico"}

+

{editingDoctorId ? "Editar Médico" : "Novo Médico"}

- {/* Header */} +
+ {/* Header - Responsivo */}
-

Dashboard

-

Bem-vindo ao painel de controle

+

Dashboard

+

Bem-vindo ao painel de controle

- {/* 1. CARDS RESUMO */} -
-
-
-
-

Total de Pacientes

-

{stats.totalPatients}

+ {/* 1. CARDS RESUMO - Responsivo com 1/2/4 colunas */} +
+
+
+
+

Total de Pacientes

+

{stats.totalPatients}

- +
-
-
-
-

Total de Médicos

-

{stats.totalDoctors}

+
+
+
+

Total de Médicos

+

{stats.totalDoctors}

- +
-
-
-
-

Consultas Hoje

-

{stats.appointmentsToday}

+
+
+
+

Consultas Hoje

+

{stats.appointmentsToday}

- +
-
-
-
-

Relatórios Pendentes

-

{pendingReports.length}

+
+
+
+

Relatórios Pendentes

+

{pendingReports.length}

- +
- {/* 6. AÇÕES RÁPIDAS */} -
-

Ações Rápidas

-
- - - -
{/* 2. PRÓXIMAS CONSULTAS */} -
-
-

Próximas Consultas (7 dias)

+
+
+

Próximas Consultas (7 dias)

{appointments.length > 0 ? ( -
+
{appointments.map(appt => ( -
-
-

+

+
+

{patients.get(appt.patient_id)?.full_name || 'Paciente desconhecido'}

-

+

Médico: {doctors.get(appt.doctor_id)?.full_name || 'Médico desconhecido'}

-

{formatDate(appt.scheduled_at)}

+

{formatDate(appt.scheduled_at)}

{getStatusBadge(appt.status)} @@ -322,64 +326,64 @@ export default function DashboardPage() { ))}
) : ( -

Nenhuma consulta agendada para os próximos 7 dias

+

Nenhuma consulta agendada para os próximos 7 dias

)}
{/* 5. RELATÓRIOS PENDENTES */} -
-

- - Relatórios Pendentes +
+

+ + Pendentes

{pendingReports.length > 0 ? (
{pendingReports.map(report => ( -
+

{report.order_number}

-

{report.exam || 'Sem descrição'}

+

{report.exam || 'Sem descrição'}

))} -
) : ( -

Sem relatórios pendentes

+

Sem relatórios pendentes

)}
{/* 4. NOVOS USUÁRIOS */} -
-

Novos Usuários (últimos 7 dias)

+
+

Novos Usuários (últimos 7 dias)

{newUsers.length > 0 ? ( -
+
{newUsers.map(user => ( -
-

{user.full_name || 'Sem nome'}

-

{user.email}

+
+

{user.full_name || 'Sem nome'}

+

{user.email}

))}
) : ( -

Nenhum novo usuário nos últimos 7 dias

+

Nenhum novo usuário nos últimos 7 dias

)}
{/* 8. ALERTAS */} {disabledUsers.length > 0 && ( -
-

- - Alertas - Usuários Desabilitados +
+

+ + Usuários Desabilitados

{disabledUsers.map(user => ( - - - - {user.full_name} ({user.email}) está desabilitado + + + + {user.full_name} ({user.email}) está desabilitado ))} @@ -388,12 +392,12 @@ export default function DashboardPage() { )} {/* 11. LINK PARA RELATÓRIOS */} -
-

Seção de Relatórios

-

+

+

Seção de Relatórios

+

Acesse a seção de relatórios médicos para gerenciar, visualizar e exportar documentos.

-
diff --git a/susconecta/app/(main-routes)/doutores/page.tsx b/susconecta/app/(main-routes)/doutores/page.tsx index 4adbd7d..2a52829 100644 --- a/susconecta/app/(main-routes)/doutores/page.tsx +++ b/susconecta/app/(main-routes)/doutores/page.tsx @@ -478,121 +478,124 @@ export default function DoutoresPage() { } return ( -
-
+
+
-

Médicos

-

Gerencie os médicos da sua clínica

+

Médicos

+

Gerencie os médicos da sua clínica

-
-
-
- - -
- - {searchMode && ( - - )} -
+ +
- {/* NOVO: Ordenar por */} + {/* Filtros e busca - Responsivos */} +
+ {/* Linha 1: Busca + Botão buscar */} +
+
+ + +
+ + {searchMode && ( + + )} +
+ + {/* Linha 2: Filtros */} +
- {/* NOVO: Especialidade */} - {/* NOVO: Estado (UF) */} - {/* NOVO: Cidade (dependente do estado) */} - -
-
+ {/* Tabela para desktop (md+) */} +
- Nome - Especialidade - CRM - Contato - Ações + Nome + Especialidade + CRM + Contato + Ações {loading ? ( - + Carregando… @@ -687,7 +690,7 @@ export default function DoutoresPage() { )) ) : ( - + Nenhum médico encontrado @@ -696,64 +699,126 @@ export default function DoutoresPage() {
- {/* Controles de paginação */} -
-
- Itens por página: + {/* Cards para mobile (md: hidden) */} +
+ {loading ? ( +
Carregando…
+ ) : paginatedDoctors.length > 0 ? ( + paginatedDoctors.map((doctor) => ( +
+
+
+

{doctor.full_name}

+

{doctor.crm || "Sem CRM"}

+
+ + + + + + handleView(doctor)}> + + Ver + + handleViewAssignedPatients(doctor)}> + + Pacientes + + handleEdit(String(doctor.id))}> + + Editar + + handleDelete(String(doctor.id))} className="text-destructive"> + + Excluir + + + +
+
+
+ Espec.: {doctor.especialidade || "—"} +
+
+ Email: {doctor.email} +
+
+ Tel.: {doctor.telefone || "—"} +
+
+
+ )) + ) : ( +
+ Nenhum médico encontrado +
+ )} +
+ + {/* Controles de paginação - Responsivos */} +
+
+ Itens por página: - + Mostrando {paginatedDoctors.length > 0 ? (currentPage - 1) * itemsPerPage + 1 : 0} a{" "} {Math.min(currentPage * itemsPerPage, displayedDoctors.length)} de {displayedDoctors.length}
-
+
- - Página {currentPage} de {totalPages || 1} + + Pág {currentPage} de {totalPages || 1}
diff --git a/susconecta/app/(main-routes)/pacientes/page.tsx b/susconecta/app/(main-routes)/pacientes/page.tsx index f43312d..f983e68 100644 --- a/susconecta/app/(main-routes)/pacientes/page.tsx +++ b/susconecta/app/(main-routes)/pacientes/page.tsx @@ -256,40 +256,53 @@ export default function PacientesPage() { } return ( -
-
+
+ {/* Header responsivo */} +
-

Pacientes

-

Gerencie os pacientes

+

Pacientes

+

Gerencie os pacientes

+ +
-
- {/* Busca */} -
+ {/* Filtros e busca responsivos */} +
+ {/* Linha 1: Busca */} +
+
setSearch(e.target.value)} onKeyDown={(e) => e.key === "Enter" && handleBuscarServidor()} />
- +
+ {/* Linha 2: Selects responsivos em grid */} +
{/* Ordenar por */} {/* Estado (UF) */} @@ -300,9 +313,9 @@ export default function PacientesPage() { setStateFilter(e.target.value); setCityFilter(""); }} - className="h-9 rounded-md border border-input bg-background px-3 py-1 text-sm shadow-sm focus:outline-none focus:ring-2 focus:ring-primary hover:border-primary transition-colors cursor-pointer" + className="h-8 sm:h-9 rounded-md border border-input bg-background px-2 sm:px-3 py-1 text-xs sm:text-sm shadow-sm focus:outline-none focus:ring-2 focus:ring-primary hover:border-primary transition-colors cursor-pointer" > - + {stateOptions.map((uf) => ( ))} @@ -313,42 +326,38 @@ export default function PacientesPage() { aria-label="Filtrar por cidade" value={cityFilter} onChange={(e) => setCityFilter(e.target.value)} - className="h-9 rounded-md border border-input bg-background px-3 py-1 text-sm shadow-sm focus:outline-none focus:ring-2 focus:ring-primary hover:border-primary transition-colors cursor-pointer" + className="h-8 sm:h-9 rounded-md border border-input bg-background px-2 sm:px-3 py-1 text-xs sm:text-sm shadow-sm focus:outline-none focus:ring-2 focus:ring-primary hover:border-primary transition-colors cursor-pointer" > - + {cityOptions.map((c) => ( ))} - -
-
+ {/* Desktop Table - Hidden on mobile */} +
- Nome - CPF - Telefone - Cidade - Estado - Ações + Nome + CPF + Telefone + Cidade + Estado + Ações {paginatedData.length > 0 ? ( paginatedData.map((p) => ( - {p.full_name || "(sem nome)"} - {p.cpf || "-"} - {p.phone_mobile || "-"} - {p.city || "-"} - {p.state || "-"} + {p.full_name || "(sem nome)"} + {p.cpf || "-"} + {p.phone_mobile || "-"} + {p.city || "-"} + {p.state || "-"} @@ -381,7 +390,7 @@ export default function PacientesPage() { )) ) : ( - + Nenhum paciente encontrado @@ -390,64 +399,132 @@ export default function PacientesPage() {
- {/* Controles de paginação */} -
-
- Itens por página: + {/* Mobile Cards - Hidden on desktop */} +
+ {paginatedData.length > 0 ? ( + paginatedData.map((p) => ( +
+
+
+
+
Nome
+
{p.full_name || "(sem nome)"}
+
+ + + + + + handleView(p)}> + + Ver + + handleEdit(String(p.id))}> + + Editar + + handleDelete(String(p.id))} className="text-destructive"> + + Excluir + + { setAssignPatientId(String(p.id)); setAssignDialogOpen(true); }}> + + Atribuir prof. + + + +
+
+
CPF
+
{p.cpf || "-"}
+
+
+
Telefone
+
{p.phone_mobile || "-"}
+
+
+
Cidade
+
{p.city || "-"}
+
+
+
Estado
+
{p.state || "-"}
+
+
+
+ )) + ) : ( +
+ Nenhum paciente encontrado +
+ )} +
+ + {/* Controles de paginação - Responsivos */} +
+
+ Itens por página: - + Mostrando {paginatedData.length > 0 ? (currentPage - 1) * itemsPerPage + 1 : 0} a{" "} {Math.min(currentPage * itemsPerPage, filtered.length)} de {filtered.length}
-
+
- - Página {currentPage} de {totalPages || 1} + + Pág {currentPage} de {totalPages || 1}
diff --git a/susconecta/app/profissional/page.tsx b/susconecta/app/profissional/page.tsx index b492a56..bb715ad 100644 --- a/susconecta/app/profissional/page.tsx +++ b/susconecta/app/profissional/page.tsx @@ -866,11 +866,15 @@ const ProfissionalPage = () => { {appointment.type}
-
-
-
- Ver informações do paciente -
+
+ {/* Tornar o trigger focusable/touchable para mobile: tabIndex + classes responsivas */} +
+ {/* Tooltip: em telas pequenas com scrollbar horizontal permanente (overflow-x-scroll); desktop sem scroll (hover) */} +
+
{appointment.title} • {appointment.type}
+
Agendamento para {appointment.title}. Status: {appointment.type === 'Rotina' ? 'requested' : appointment.type}.
+
{appointment.time} • {appointment.date}
+
diff --git a/susconecta/components/features/forms/calendar-registration-form.tsx b/susconecta/components/features/forms/calendar-registration-form.tsx index 0eacf75..1edba87 100644 --- a/susconecta/components/features/forms/calendar-registration-form.tsx +++ b/susconecta/components/features/forms/calendar-registration-form.tsx @@ -936,15 +936,29 @@ export function CalendarRegistrationForm({ formData, onFormChange, createMode =
{ + try { + const [y, m, d] = String(formData.appointmentDate).split('-').map(Number); + return new Date(y, m - 1, d); + } catch (e) { + return undefined; + } + })() : undefined} onSelect={(date) => { if (date) { - const dateStr = date.toISOString().split('T')[0]; + const y = date.getFullYear(); + const m = String(date.getMonth() + 1).padStart(2, '0'); + const d = String(date.getDate()).padStart(2, '0'); + const dateStr = `${y}-${m}-${d}`; onFormChange({ ...formData, appointmentDate: dateStr }); setShowDatePicker(false); } }} - disabled={(date) => date < new Date(new Date().toISOString().split('T')[0] + 'T00:00:00')} + disabled={(date) => { + const today = new Date(); + today.setHours(0, 0, 0, 0); + return date < today; + }} />
)} diff --git a/susconecta/components/features/forms/doctor-registration-form.tsx b/susconecta/components/features/forms/doctor-registration-form.tsx index 415070c..189f0b3 100644 --- a/susconecta/components/features/forms/doctor-registration-form.tsx +++ b/susconecta/components/features/forms/doctor-registration-form.tsx @@ -1,7 +1,7 @@ "use client"; import { useEffect, useMemo, useState } from "react"; -import { parse } from 'date-fns'; +import { parse, parseISO, format } from 'date-fns'; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; @@ -11,9 +11,11 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; -import { AlertCircle, ChevronDown, ChevronUp, FileImage, Loader2, Save, Upload, User, X, XCircle, Trash2 } from "lucide-react"; -import { Checkbox } from "@/components/ui/checkbox"; import { Popover, PopoverTrigger, PopoverContent } from "@/components/ui/popover"; +import { Calendar } from "@/components/ui/calendar"; +import { cn } from "@/lib/utils"; +import { AlertCircle, ChevronDown, ChevronUp, FileImage, Loader2, Save, Upload, User, X, XCircle, Trash2, CalendarIcon } from "lucide-react"; +import { Checkbox } from "@/components/ui/checkbox"; import { criarMedico, atualizarMedico, @@ -76,7 +78,7 @@ type FormData = { cpf: string; rg: string; sexo: string; - data_nascimento: string; + data_nascimento: Date | null; email: string; telefone: string; celular: string; @@ -109,7 +111,7 @@ const initial: FormData = { cpf: "", rg: "", sexo: "", - data_nascimento: "", + data_nascimento: null, email: "", telefone: "", celular: "", // Aqui, 'celular' pode ser 'phone_mobile' @@ -150,7 +152,7 @@ export function DoctorRegistrationForm({ }: DoctorRegistrationFormProps) { const [form, setForm] = useState(initial); const [errors, setErrors] = useState>({}); - const [expanded, setExpanded] = useState({ dados: true, contato: false, endereco: false, obs: false, formacao: false, admin: false }); + const [expanded, setExpanded] = useState({ dados: true, contato: false, endereco: false, obs: false, formacao: false }); const [isSubmitting, setSubmitting] = useState(false); const [isUploadingPhoto, setUploadingPhoto] = useState(false); const [isSearchingCEP, setSearchingCEP] = useState(false); @@ -257,7 +259,7 @@ export function DoctorRegistrationForm({ cpf: String(m.cpf || ""), rg: String(m.rg || m.document_number || ""), sexo: normalizeSex(m.sexo || m.sex || m.sexualidade || null) ?? "", - data_nascimento: String(formatBirth(m.data_nascimento || m.birth_date || m.birthDate || "")), + data_nascimento: m.data_nascimento ? parseISO(String(m.data_nascimento)) : m.birth_date ? parseISO(String(m.birth_date)) : null, email: String(m.email || ""), telefone: String(m.telefone || m.phone_mobile || m.phone || m.mobile || ""), celular: String(m.celular || m.phone2 || ""), @@ -430,36 +432,6 @@ function setField(k: T, v: FormData[T]) { } function toPayload(): MedicoInput { - // Converte data de nascimento para ISO (yyyy-MM-dd) tentando vários formatos - let isoDate: string | null = null; - try { - const raw = String(form.data_nascimento || '').trim(); - if (raw) { - const formats = ['dd/MM/yyyy', 'dd-MM-yyyy', 'yyyy-MM-dd', 'MM/dd/yyyy']; - for (const f of formats) { - try { - const d = parse(raw, f, new Date()); - if (!isNaN(d.getTime())) { - isoDate = d.toISOString().slice(0, 10); - break; - } - } catch (e) { - // ignore and try next - } - } - if (!isoDate) { - const parts = raw.split(/\D+/).filter(Boolean); - if (parts.length === 3) { - const [d, m, y] = parts; - const date = new Date(Number(y), Number(m) - 1, Number(d)); - if (!isNaN(date.getTime())) isoDate = date.toISOString().slice(0, 10); - } - } - } - } catch (err) { - console.debug('[DoctorForm] parse data_nascimento failed:', form.data_nascimento, err); - } - return { user_id: null, crm: form.crm || "", @@ -477,7 +449,7 @@ function toPayload(): MedicoInput { neighborhood: form.bairro || undefined, city: form.cidade || "", state: form.estado || "", - birth_date: isoDate, + birth_date: form.data_nascimento ? form.data_nascimento.toISOString().slice(0, 10) : null, rg: form.rg || null, active: true, created_by: null, @@ -796,7 +768,7 @@ async function handleSubmit(ev: React.FormEvent) {
-
+
- - { - const v = e.target.value.replace(/[^0-9\/]/g, "").slice(0, 10); - setField("data_nascimento", v); - }} - onBlur={() => { - const raw = form.data_nascimento; - const parts = raw.split(/\D+/).filter(Boolean); - if (parts.length === 3) { - const d = `${parts[0].padStart(2,'0')}/${parts[1].padStart(2,'0')}/${parts[2].padStart(4,'0')}`; - setField("data_nascimento", d); - } - }} - /> + + + + + + + setField("data_nascimento", date || null)} + initialFocus + /> + +
@@ -949,98 +927,6 @@ async function handleSubmit(ev: React.FormEvent) { - setExpanded((s) => ({ ...s, admin: !s.admin }))}> - - - - - - - Dados Administrativos e Financeiros - - {expanded.admin ? : } - - - - - -
-
- - -
-
- - setField("valor_consulta", e.target.value)} - placeholder="R$ 0,00" - /> -
-
- - {/* Agenda/Horário removido conforme solicitado */} - -
- -
-
- - setField("dados_bancarios", { ...form.dados_bancarios, banco: e.target.value })} - /> -
-
- - setField("dados_bancarios", { ...form.dados_bancarios, agencia: e.target.value })} - /> -
-
-
-
- - setField("dados_bancarios", { ...form.dados_bancarios, conta: e.target.value })} - /> -
-
- - -
-
-
- - -
-
-
-
- setExpanded((s) => ({ ...s, endereco: !s.endereco }))}> diff --git a/susconecta/components/features/forms/patient-registration-form.tsx b/susconecta/components/features/forms/patient-registration-form.tsx index 2e82998..feafa89 100644 --- a/susconecta/components/features/forms/patient-registration-form.tsx +++ b/susconecta/components/features/forms/patient-registration-form.tsx @@ -11,7 +11,10 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; -import { AlertCircle, ChevronDown, ChevronUp, FileImage, Loader2, Save, Upload, User, X, XCircle, Trash2 } from "lucide-react"; +import { Popover, PopoverTrigger, PopoverContent } from "@/components/ui/popover"; +import { Calendar } from "@/components/ui/calendar"; +import { cn } from "@/lib/utils"; +import { AlertCircle, ChevronDown, ChevronUp, FileImage, Loader2, Save, Upload, User, X, XCircle, Trash2, CalendarIcon } from "lucide-react"; import { Paciente, @@ -51,7 +54,7 @@ type FormData = { cpf: string; rg: string; sexo: string; - birth_date: string; + birth_date: Date | null; email: string; telefone: string; cep: string; @@ -72,7 +75,7 @@ const initial: FormData = { cpf: "", rg: "", sexo: "", - birth_date: "", + birth_date: null, email: "", telefone: "", cep: "", @@ -150,7 +153,7 @@ export function PatientRegistrationForm({ cpf: p.cpf || "", rg: p.rg || "", sexo: p.sex || "", - birth_date: p.birth_date ? (() => { try { return format(parseISO(String(p.birth_date)), 'dd/MM/yyyy'); } catch { return String(p.birth_date); } })() : "", + birth_date: p.birth_date ? parseISO(String(p.birth_date)) : null, telefone: p.phone_mobile || "", email: p.email || "", cep: p.cep || "", @@ -212,44 +215,13 @@ export function PatientRegistrationForm({ } function toPayload(): PacienteInput { - let isoDate: string | null = null; - try { - const raw = String(form.birth_date || '').trim(); - if (raw) { - // Try common formats first - const formats = ['dd/MM/yyyy', 'dd-MM-yyyy', 'yyyy-MM-dd', 'MM/dd/yyyy']; - for (const f of formats) { - try { - const d = parse(raw, f, new Date()); - if (!isNaN(d.getTime())) { - isoDate = d.toISOString().slice(0, 10); - break; - } - } catch (e) { - // ignore and try next format - } - } - - // Fallback: split numeric parts (handles 'dd mm yyyy' or 'ddmmyyyy' with separators) - if (!isoDate) { - const parts = raw.split(/\D+/).filter(Boolean); - if (parts.length === 3) { - const [d, m, y] = parts; - const date = new Date(Number(y), Number(m) - 1, Number(d)); - if (!isNaN(date.getTime())) isoDate = date.toISOString().slice(0, 10); - } - } - } - } catch (err) { - console.debug('[PatientForm] parse birth_date failed:', form.birth_date, err); - } return { full_name: form.nome, social_name: form.nome_social || null, cpf: form.cpf, rg: form.rg || null, sex: form.sexo || null, - birth_date: isoDate, + birth_date: form.birth_date ? form.birth_date.toISOString().slice(0, 10) : null, phone_mobile: form.telefone || null, email: form.email || null, cep: form.cep || null, @@ -376,7 +348,7 @@ export function PatientRegistrationForm({
- {photoPreview ? Preview : } + {photoPreview ? Preview : }
-
-
+
+
+
-
setField("birth_date", formatDataNascimento(e.target.value))} maxLength={10} />
+
+ + + + + + + setField("birth_date", date || null)} + initialFocus + /> + + +
diff --git a/susconecta/components/features/general/event-manager.tsx b/susconecta/components/features/general/event-manager.tsx index a31663b..7ffb8bd 100644 --- a/susconecta/components/features/general/event-manager.tsx +++ b/susconecta/components/features/general/event-manager.tsx @@ -872,7 +872,8 @@ function MonthView({ } return ( - + // Permitir que popovers absolutos saiam do grid do mês sem serem cortados +
{["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"].map((day) => (