develop #83

Merged
M-Gabrielly merged 426 commits from develop into main 2025-12-04 04:13:15 +00:00
3 changed files with 238 additions and 62 deletions
Showing only changes of commit 5d78e9f066 - Show all commits

View File

@ -1,6 +1,6 @@
"use client"; "use client";
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState, useRef } from "react";
import { parse, parseISO, format } from 'date-fns'; import { parse, parseISO, format } from 'date-fns';
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
@ -167,6 +167,7 @@ export function DoctorRegistrationForm({
userName: string; userName: string;
userType: 'médico' | 'paciente'; userType: 'médico' | 'paciente';
} | null>(null); } | null>(null);
const savedDoctorRef = useRef<any>(null);
const title = useMemo(() => (mode === "create" ? "Cadastro de Médico" : "Editar Médico"), [mode]); const title = useMemo(() => (mode === "create" ? "Cadastro de Médico" : "Editar Médico"), [mode]);
@ -504,6 +505,11 @@ async function handleSubmit(ev: React.FormEvent) {
// 1. Cria o perfil do médico na tabela doctors // 1. Cria o perfil do médico na tabela doctors
let savedDoctorProfile: any = await criarMedico(medicoPayload); let savedDoctorProfile: any = await criarMedico(medicoPayload);
console.log("✅ Perfil do médico criado:", savedDoctorProfile); console.log("✅ Perfil do médico criado:", savedDoctorProfile);
console.log("🔑 Senha no objeto retornado:", savedDoctorProfile?.password);
// Salvar a senha ANTES de qualquer operação que possa sobrescrever o objeto
const senhaGerada = savedDoctorProfile?.password;
console.log("💾 Senha salva em variável:", senhaGerada);
// Fallback: some create flows don't persist optional fields like birth_date/cep/sexo. // Fallback: some create flows don't persist optional fields like birth_date/cep/sexo.
// If the returned object is missing those but our payload included them, // If the returned object is missing those but our payload included them,
@ -531,7 +537,9 @@ async function handleSubmit(ev: React.FormEvent) {
const patched = await atualizarMedico(String(createdDoctorId), medicoPayload).catch((e) => { console.warn('[DoctorForm] fallback PATCH failed:', e); return null; }); const patched = await atualizarMedico(String(createdDoctorId), medicoPayload).catch((e) => { console.warn('[DoctorForm] fallback PATCH failed:', e); return null; });
if (patched) { if (patched) {
console.debug('[DoctorForm] fallback PATCH result:', patched); console.debug('[DoctorForm] fallback PATCH result:', patched);
savedDoctorProfile = patched; // Preservar a senha ao atualizar o objeto
savedDoctorProfile = { ...patched, password: senhaGerada };
console.log("🔄 Senha preservada após PATCH:", savedDoctorProfile?.password);
} }
} }
} catch (e) { } catch (e) {
@ -547,6 +555,7 @@ async function handleSubmit(ev: React.FormEvent) {
// { doctor, doctor_id, email, password, user_id } or similar shapes. // { doctor, doctor_id, email, password, user_id } or similar shapes.
const result = savedDoctorProfile as any; const result = savedDoctorProfile as any;
console.log('✅ Resultado de criarMedico:', result); console.log('✅ Resultado de criarMedico:', result);
console.log('🔑 Senha no resultado final:', result?.password);
// Determine the doctor id if available // Determine the doctor id if available
let createdDoctorId: string | null = null; let createdDoctorId: string | null = null;
@ -559,13 +568,36 @@ async function handleSubmit(ev: React.FormEvent) {
// If the function returned credentials, show them in the credentials dialog // If the function returned credentials, show them in the credentials dialog
if (result && (result.password || result.email || result.user)) { if (result && (result.password || result.email || result.user)) {
setCredentials({ console.log('📧 Credenciais recebidas - configurando dialog...');
console.log('📧 Email:', result.email || form.email);
console.log('🔑 Senha extraída:', result.password);
console.log('👤 Nome do usuário:', form.full_name);
const credenciaisParaExibir = {
email: result.email || form.email, email: result.email || form.email,
password: result.password || "", password: result.password || senhaGerada || "",
userName: form.full_name, userName: form.full_name,
userType: 'médico', userType: 'médico' as const,
}); };
console.log('📋 Credenciais a serem definidas:', credenciaisParaExibir);
// Salvar o médico no ref ANTES de abrir o dialog
savedDoctorRef.current = savedDoctorProfile;
setCredentials(credenciaisParaExibir);
setShowCredentialsDialog(true); setShowCredentialsDialog(true);
console.log('✅ Dialog de credenciais configurado e aberto');
// Verificar estados após 100ms
setTimeout(() => {
console.log('🔍 Verificando estados após 100ms:');
console.log('- showCredentialsDialog:', showCredentialsDialog);
console.log('- credentials:', credentials);
}, 100);
// NÃO fechar o formulário aqui - será fechado quando o usuário fechar o dialog de credenciais
return; // Sair da função para não executar o cleanup abaixo
} }
// Upload photo if provided and we have an id // Upload photo if provided and we have an id
@ -800,8 +832,8 @@ async function handleSubmit(ev: React.FormEvent) {
<PopoverContent className="w-auto p-0"> <PopoverContent className="w-auto p-0">
<Calendar <Calendar
mode="single" mode="single"
selected={form.data_nascimento} selected={form.data_nascimento ?? undefined}
onSelect={(date) => setField("data_nascimento", date || null)} onSelect={(date) => setField("data_nascimento", date ?? null)}
initialFocus initialFocus
/> />
</PopoverContent> </PopoverContent>
@ -1061,28 +1093,37 @@ async function handleSubmit(ev: React.FormEvent) {
<> <>
<div className="space-y-6">{content}</div> <div className="space-y-6">{content}</div>
{/* Dialog de credenciais */} <CredentialsDialog
{credentials && ( open={showCredentialsDialog}
<CredentialsDialog onOpenChange={(open) => {
open={showCredentialsDialog} console.log('🔄 CredentialsDialog (inline) onOpenChange chamado com:', open);
onOpenChange={(open) => { setShowCredentialsDialog(open);
setShowCredentialsDialog(open); if (!open) {
if (!open) { // Dialog foi fechado - limpar estados e fechar formulário
// Quando o dialog de credenciais fecha, fecha o formulário também console.log('✅ Dialog fechado - limpando formulário...');
setCredentials(null); setCredentials(null);
if (inline) {
onClose?.(); // Chamar onSaved se houver médico salvo
} else { if (savedDoctorRef.current) {
onOpenChange?.(false); onSaved?.(savedDoctorRef.current);
} savedDoctorRef.current = null;
} }
}}
email={credentials.email} // Limpar formulário
password={credentials.password} setForm(initial);
userName={credentials.userName} setPhotoPreview(null);
userType={credentials.userType} setServerAnexos([]);
/>
)} // Fechar formulário
if (inline) onClose?.();
else onOpenChange?.(false);
}
}}
email={credentials?.email || ''}
password={credentials?.password || ''}
userName={credentials?.userName || ''}
userType={credentials?.userType || 'médico'}
/>
</> </>
); );
} }
@ -1100,23 +1141,36 @@ async function handleSubmit(ev: React.FormEvent) {
</DialogContent> </DialogContent>
</Dialog> </Dialog>
{/* Dialog de credenciais */} <CredentialsDialog
{credentials && ( open={showCredentialsDialog}
<CredentialsDialog onOpenChange={(open) => {
open={showCredentialsDialog} console.log('🔄 CredentialsDialog (dialog) onOpenChange chamado com:', open);
onOpenChange={(open) => { setShowCredentialsDialog(open);
setShowCredentialsDialog(open); if (!open) {
if (!open) { // Dialog foi fechado - limpar estados e fechar formulário
setCredentials(null); console.log('✅ Dialog fechado - limpando formulário...');
onOpenChange?.(false); setCredentials(null);
// Chamar onSaved se houver médico salvo
if (savedDoctorRef.current) {
onSaved?.(savedDoctorRef.current);
savedDoctorRef.current = null;
} }
}}
email={credentials.email} // Limpar formulário
password={credentials.password} setForm(initial);
userName={credentials.userName} setPhotoPreview(null);
userType={credentials.userType} setServerAnexos([]);
/>
)} // Fechar formulário principal
onOpenChange?.(false);
}
}}
email={credentials?.email || ''}
password={credentials?.password || ''}
userName={credentials?.userName || ''}
userType={credentials?.userType || 'médico'}
/>
</> </>
); );
} }

View File

@ -1,6 +1,6 @@
"use client"; "use client";
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState, useRef } from "react";
import { format, parseISO, parse } from "date-fns"; import { format, parseISO, parse } from "date-fns";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
@ -139,6 +139,9 @@ export function PatientRegistrationForm({
userType: 'médico' | 'paciente'; userType: 'médico' | 'paciente';
} | null>(null); } | null>(null);
// Ref para guardar o paciente salvo para chamar onSaved quando o dialog fechar
const savedPatientRef = useRef<any>(null);
const title = useMemo(() => (mode === "create" ? "Cadastro de Paciente" : "Editar Paciente"), [mode]); const title = useMemo(() => (mode === "create" ? "Cadastro de Paciente" : "Editar Paciente"), [mode]);
useEffect(() => { useEffect(() => {
@ -276,7 +279,11 @@ export function PatientRegistrationForm({
setErrors((e) => ({ ...e, telefone: 'Telefone é obrigatório quando email é informado (fluxo de criação único).' })); setSubmitting(false); return; setErrors((e) => ({ ...e, telefone: 'Telefone é obrigatório quando email é informado (fluxo de criação único).' })); setSubmitting(false); return;
} }
let savedPatientProfile: any = await criarPaciente(patientPayload); let savedPatientProfile: any = await criarPaciente(patientPayload);
console.log('Perfil do paciente criado (via Function):', savedPatientProfile); console.log('🎯 Paciente criado! Resposta completa:', savedPatientProfile);
console.log('🔑 Senha no objeto:', savedPatientProfile?.password);
// Guardar a senha ANTES de qualquer operação que possa sobrescrever o objeto
const senhaGerada = savedPatientProfile?.password;
// Fallback: some backend create flows (create-user-with-password) do not // Fallback: some backend create flows (create-user-with-password) do not
// persist optional patient fields like sex/cep/birth_date. The edit flow // persist optional patient fields like sex/cep/birth_date. The edit flow
@ -295,17 +302,56 @@ export function PatientRegistrationForm({
const patched = await atualizarPaciente(String(pacienteId), patientPayload).catch((e) => { console.warn('[PatientForm] fallback PATCH falhou:', e); return null; }); const patched = await atualizarPaciente(String(pacienteId), patientPayload).catch((e) => { console.warn('[PatientForm] fallback PATCH falhou:', e); return null; });
if (patched) { if (patched) {
console.debug('[PatientForm] fallback PATCH result:', patched); console.debug('[PatientForm] fallback PATCH result:', patched);
savedPatientProfile = patched; // Preserva a senha ao fazer merge do patch
savedPatientProfile = { ...patched, password: senhaGerada };
} }
} }
} catch (e) { } catch (e) {
console.warn('[PatientForm] erro ao tentar fallback PATCH:', e); console.warn('[PatientForm] erro ao tentar fallback PATCH:', e);
} }
const maybePassword = (savedPatientProfile as any)?.password || (savedPatientProfile as any)?.generated_password; // Usar a senha que foi guardada ANTES do PATCH
if (maybePassword) { const emailToDisplay = savedPatientProfile?.email || form.email;
setCredentials({ email: (savedPatientProfile as any).email || form.email, password: String(maybePassword), userName: form.nome, userType: 'paciente' }); console.log('📧 Email para exibir:', emailToDisplay);
console.log('🔐 Senha para exibir:', senhaGerada);
if (senhaGerada && emailToDisplay) {
console.log('✅ Abrindo modal de credenciais...');
const credentialsToShow = {
email: emailToDisplay,
password: String(senhaGerada),
userName: form.nome,
userType: 'paciente' as const
};
console.log('📝 Credenciais a serem definidas:', credentialsToShow);
// Guardar o paciente salvo no ref para usar quando o dialog fechar
savedPatientRef.current = savedPatientProfile;
// Definir credenciais e abrir dialog
setCredentials(credentialsToShow);
setShowCredentialsDialog(true); setShowCredentialsDialog(true);
// NÃO limpar o formulário ou fechar ainda - aguardar o usuário fechar o dialog de credenciais
// O dialog de credenciais vai chamar onSaved e fechar quando o usuário clicar em "Fechar"
// Verificar se foi setado
setTimeout(() => {
console.log('🔍 Verificando estados após 100ms:');
console.log(' - showCredentialsDialog:', showCredentialsDialog);
console.log(' - credentials:', credentials);
}, 100);
} else {
console.error('❌ Não foi possível exibir credenciais:', { senhaGerada, emailToDisplay });
alert(`Paciente criado!\n\nEmail: ${emailToDisplay}\n\nAVISO: A senha não pôde ser recuperada. Entre em contato com o suporte.`);
// Se não há senha, limpar e fechar normalmente
onSaved?.(savedPatientProfile);
setForm(initial);
setPhotoPreview(null);
setServerAnexos([]);
if (inline) onClose?.();
else onOpenChange?.(false);
} }
if (form.photo) { if (form.photo) {
@ -313,8 +359,6 @@ export function PatientRegistrationForm({
catch (upErr) { console.warn('[PatientForm] Falha ao enviar foto do paciente após criação:', upErr); alert('Paciente criado, mas falha ao enviar a foto. Você pode tentar novamente no perfil.'); } catch (upErr) { console.warn('[PatientForm] Falha ao enviar foto do paciente após criação:', upErr); alert('Paciente criado, mas falha ao enviar a foto. Você pode tentar novamente no perfil.'); }
finally { setUploadingPhoto(false); } finally { setUploadingPhoto(false); }
} }
onSaved?.(savedPatientProfile); setForm(initial); setPhotoPreview(null); setServerAnexos([]); if (inline) onClose?.(); else onOpenChange?.(false);
} }
} catch (err: any) { console.error("❌ Erro no handleSubmit:", err); const userMessage = err?.message?.includes("toPayload") || err?.message?.includes("is not defined") ? "Erro ao processar os dados do formulário. Por favor, verifique os campos e tente novamente." : err?.message || "Erro ao salvar paciente. Por favor, tente novamente."; setErrors({ submit: userMessage }); } } catch (err: any) { console.error("❌ Erro no handleSubmit:", err); const userMessage = err?.message?.includes("toPayload") || err?.message?.includes("is not defined") ? "Erro ao processar os dados do formulário. Por favor, verifique os campos e tente novamente." : err?.message || "Erro ao salvar paciente. Por favor, tente novamente."; setErrors({ submit: userMessage }); }
finally { setSubmitting(false); } finally { setSubmitting(false); }
@ -519,8 +563,86 @@ export function PatientRegistrationForm({
); );
if (inline) { if (inline) {
return (<><div className="space-y-6">{content}</div>{credentials && (<CredentialsDialog open={showCredentialsDialog} onOpenChange={(open) => { setShowCredentialsDialog(open); if (!open) { setCredentials(null); if (inline) onClose?.(); else onOpenChange?.(false); } }} email={credentials.email} password={credentials.password} userName={credentials.userName} userType={credentials.userType} />)}</>); return (
<>
<div className="space-y-6">{content}</div>
<CredentialsDialog
open={showCredentialsDialog}
onOpenChange={(open) => {
console.log('🔄 CredentialsDialog onOpenChange chamado com:', open);
setShowCredentialsDialog(open);
if (!open) {
// Dialog foi fechado - limpar estados e fechar formulário
console.log('✅ Dialog fechado - limpando formulário...');
setCredentials(null);
// Chamar onSaved se houver paciente salvo
if (savedPatientRef.current) {
onSaved?.(savedPatientRef.current);
savedPatientRef.current = null;
}
// Limpar formulário
setForm(initial);
setPhotoPreview(null);
setServerAnexos([]);
// Fechar formulário
if (inline) onClose?.();
else onOpenChange?.(false);
}
}}
email={credentials?.email || ''}
password={credentials?.password || ''}
userName={credentials?.userName || ''}
userType={credentials?.userType || 'paciente'}
/>
</>
);
} }
return (<><Dialog open={open} onOpenChange={onOpenChange}><DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto"><DialogHeader><DialogTitle className="flex items-center gap-2"><User className="h-5 w-5" /> {title}</DialogTitle></DialogHeader>{content}</DialogContent></Dialog>{credentials && (<CredentialsDialog open={showCredentialsDialog} onOpenChange={(open) => { setShowCredentialsDialog(open); if (!open) { setCredentials(null); onOpenChange?.(false); } }} email={credentials.email} password={credentials.password} userName={credentials.userName} userType={credentials.userType} />)}</>); return (
<>
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<User className="h-5 w-5" /> {title}
</DialogTitle>
</DialogHeader>
{content}
</DialogContent>
</Dialog>
<CredentialsDialog
open={showCredentialsDialog}
onOpenChange={(open) => {
console.log('🔄 CredentialsDialog onOpenChange chamado com:', open);
setShowCredentialsDialog(open);
if (!open) {
// Dialog foi fechado - limpar estados e fechar formulário
console.log('✅ Dialog fechado - limpando formulário...');
setCredentials(null);
// Chamar onSaved se houver paciente salvo
if (savedPatientRef.current) {
onSaved?.(savedPatientRef.current);
savedPatientRef.current = null;
}
// Limpar formulário
setForm(initial);
setPhotoPreview(null);
setServerAnexos([]);
// Fechar formulário principal
onOpenChange?.(false);
}
}}
email={credentials?.email || ''}
password={credentials?.password || ''}
userName={credentials?.userName || ''}
userType={credentials?.userType || 'paciente'}
/>
</>
);
} }

View File

@ -2139,25 +2139,25 @@ export async function criarMedico(input: MedicoInput): Promise<Medico> {
// If server returned doctor_id, fetch the doctor // If server returned doctor_id, fetch the doctor
if (parsed && parsed.doctor_id) { if (parsed && parsed.doctor_id) {
const doc = await buscarMedicoPorId(String(parsed.doctor_id)).catch(() => null); const doc = await buscarMedicoPorId(String(parsed.doctor_id)).catch(() => null);
if (doc) return Object.assign(doc, { password }); if (doc) return { ...doc, password } as any;
if (parsed.doctor) return Object.assign(parsed.doctor, { password }); if (parsed.doctor) return { ...parsed.doctor, password } as any;
return Object.assign({ id: parsed.doctor_id, full_name: input.full_name, cpf: cleanCpf, email: input.email } as Medico, { password }); return { id: parsed.doctor_id, full_name: input.full_name, cpf: cleanCpf, email: input.email, password } as any;
} }
// If server returned doctor object directly // If server returned doctor object directly
if (parsed && (parsed.id || parsed.full_name || parsed.cpf)) { if (parsed && (parsed.id || parsed.full_name || parsed.cpf)) {
return Object.assign(parsed, { password }) as Medico; return { ...parsed, password } as any;
} }
// If server returned an envelope with user, try to locate doctor by email // If server returned an envelope with user, try to locate doctor by email
if (parsed && parsed.user && parsed.user.id) { if (parsed && parsed.user && parsed.user.id) {
const maybe = await fetch(`${REST}/doctors?email=eq.${encodeURIComponent(String(input.email))}&select=*`, { method: 'GET', headers: baseHeaders() }).then((r) => r.ok ? r.json().catch(() => []) : []); const maybe = await fetch(`${REST}/doctors?email=eq.${encodeURIComponent(String(input.email))}&select=*`, { method: 'GET', headers: baseHeaders() }).then((r) => r.ok ? r.json().catch(() => []) : []);
if (Array.isArray(maybe) && maybe.length) return Object.assign(maybe[0] as Medico, { password }); if (Array.isArray(maybe) && maybe.length) return { ...maybe[0], password } as any;
return Object.assign({ id: parsed.user.id, full_name: input.full_name, email: input.email } as Medico, { password }); return { id: parsed.user.id, full_name: input.full_name, email: input.email, password } as any;
} }
// otherwise return parsed with password as best-effort // otherwise return parsed with password as best-effort
return Object.assign(parsed || {}, { password }); return { ...(parsed || {}), password } as any;
} catch (err: any) { } catch (err: any) {
lastErr = err; lastErr = err;
const emsg = err && typeof err === 'object' && 'message' in err ? (err as any).message : String(err); const emsg = err && typeof err === 'object' && 'message' in err ? (err as any).message : String(err);