develop #83

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

View File

@ -21,8 +21,10 @@ import {
listarAnexosMedico,
adicionarAnexoMedico,
removerAnexoMedico,
MedicoInput,
MedicoInput, // 👈 importado do lib/api
Medico, // 👈 adicionado import do tipo Medico
} from "@/lib/api";
;
import { buscarCepAPI } from "@/lib/api";
@ -39,32 +41,9 @@ type DadosBancarios = {
tipo_conta: string;
};
export type Medico = {
id: string;
nome?: string;
nome_social?: string | null;
cpf?: string;
rg?: string | null;
sexo?: string | null;
data_nascimento?: string | null;
telefone?: string;
celular?: string;
contato_emergencia?: string;
email?: string;
crm?: string;
estado_crm?: string;
rqe?: string;
formacao_academica?: FormacaoAcademica[];
curriculo_url?: string | null;
especialidade?: string;
observacoes?: string | null;
foto_url?: string | null;
tipo_vinculo?: string;
dados_bancarios?: DadosBancarios;
agenda_horario?: string;
valor_consulta?: number | string;
};
type Mode = "create" | "edit";
@ -80,7 +59,7 @@ export interface DoctorRegistrationFormProps {
type FormData = {
photo: File | null;
nome: string;
full_name: string; // Substitua 'nome' por 'full_name'
nome_social: string;
crm: string;
estado_crm: string;
@ -107,14 +86,13 @@ type FormData = {
anexos: File[];
tipo_vinculo: string;
dados_bancarios: DadosBancarios;
agenda_horario: string;
valor_consulta: string;
};
const initial: FormData = {
photo: null,
nome: "",
full_name: "",
nome_social: "",
crm: "",
estado_crm: "",
@ -128,7 +106,7 @@ const initial: FormData = {
data_nascimento: "",
email: "",
telefone: "",
celular: "",
celular: "", // Aqui, 'celular' pode ser 'phone_mobile'
contato_emergencia: "",
cep: "",
logradouro: "",
@ -152,6 +130,7 @@ const initial: FormData = {
export function DoctorRegistrationForm({
open = true,
onOpenChange,
@ -179,7 +158,7 @@ export function DoctorRegistrationForm({
if (!alive) return;
setForm({
photo: null,
nome: medico.nome ?? "",
full_name: medico.full_name ?? "",
nome_social: medico.nome_social ?? "",
crm: medico.crm ?? "",
estado_crm: medico.estado_crm ?? "",
@ -222,10 +201,11 @@ export function DoctorRegistrationForm({
}, [mode, doctorId]);
function setField<T extends keyof FormData>(k: T, v: FormData[T]) {
function setField<T extends keyof FormData>(k: T, v: FormData[T]) {
setForm((s) => ({ ...s, [k]: v }));
if (errors[k as string]) setErrors((e) => ({ ...e, [k]: "" }));
}
}
function addFormacao() {
@ -301,74 +281,79 @@ export function DoctorRegistrationForm({
function validateLocal(): boolean {
const e: Record<string, string> = {};
if (!form.nome.trim()) e.nome = "Nome é obrigatório";
if (!form.full_name.trim()) e.full_name = "Nome é obrigatório";
if (!form.cpf.trim()) e.cpf = "CPF é obrigatório";
if (!form.crm.trim()) e.crm = "CRM é obrigatório";
if (!form.especialidade.trim()) e.especialidade = "Especialidade é obrigatória";
if (!form.cep.trim()) e.cep = "CEP é obrigatório"; // Verifique se o CEP está preenchido
if (!form.bairro.trim()) e.bairro = "Bairro é obrigatório"; // Verifique se o bairro está preenchido
if (!form.cidade.trim()) e.cidade = "Cidade é obrigatória"; // Verifique se a cidade está preenchida
setErrors(e);
return Object.keys(e).length === 0;
}
}
async function handleSubmit(ev: React.FormEvent) {
async function handleSubmit(ev: React.FormEvent) {
ev.preventDefault();
if (!validateLocal()) return;
console.log("Submitting the form..."); // Verifique se a função está sendo chamada
if (!validateLocal()) {
console.log("Validation failed");
return; // Se a validação falhar, saia da função.
}
setSubmitting(true);
setErrors((e) => ({ ...e, submit: "" }));
try {
const payload: MedicoInput = {
nome: form.nome,
const payload: MedicoInput = {
full_name: form.full_name,
nome_social: form.nome_social || null,
cpf: form.cpf || null,
cpf: form.cpf || "",
rg: form.rg || null,
sexo: form.sexo || null,
data_nascimento: form.data_nascimento || null,
telefone: form.telefone || null,
celular: form.celular || null,
contato_emergencia: form.contato_emergencia || null,
email: form.email || null,
celular: form.celular || "",
email: form.email || undefined,
crm: form.crm,
estado_crm: form.estado_crm || null,
crm_uf: form.estado_crm || null,
rqe: form.rqe || null,
formacao_academica: form.formacao_academica ?? [],
curriculo_url: null,
especialidade: form.especialidade,
formacao_academica: form.formacao_academica,
especialidade: form.especialidade || "",
observacoes: form.observacoes || null,
tipo_vinculo: form.tipo_vinculo || null,
dados_bancarios: form.dados_bancarios ?? null,
agenda_horario: form.agenda_horario || null,
dados_bancarios: form.dados_bancarios || null, // Remova se não for necessário
valor_consulta: form.valor_consulta || null,
};
active: true,
cep: form.cep || null,
city: form.cidade || null,
complement: form.complemento || null,
neighborhood: form.bairro || null,
number: form.numero || null,
phone2: form.telefone || null, // Ajustar conforme necessário
state: form.estado || null,
street: form.logradouro || null,
created_by: 'user_id',
updated_by: 'user_id',
};
console.log("Payload being sent:", payload); // Verifique se o payload está correto
try {
const saved = mode === "create"
? await criarMedico(payload)
: await atualizarMedico(doctorId as number, payload);
const medicoId = saved.id;
if (form.photo) {
try {
await uploadFotoMedico(medicoId, form.photo);
} catch (e) {
console.warn("Falha ao enviar foto:", e);
}
}
if (form.anexos?.length) {
for (const f of form.anexos) {
try {
await adicionarAnexoMedico(medicoId, f);
} catch (e) {
console.warn("Falha ao enviar anexo:", f.name, e);
}
}
}
console.log("Médico salvo com sucesso", saved); // Verifique se o médico foi salvo
onSaved?.(saved);
if (inline) onClose?.();
else onOpenChange?.(false);
setSubmitting(false);
} catch (err: any) {
console.error("Erro ao salvar médico:", err);
setErrors((e) => ({ ...e, submit: err?.message || "Erro ao salvar médico" }));
} finally {
setSubmitting(false);
@ -376,6 +361,10 @@ export function DoctorRegistrationForm({
}
function handlePhoto(e: React.ChangeEvent<HTMLInputElement>) {
const f = e.target.files?.[0];
if (!f) return;
@ -449,8 +438,10 @@ export function DoctorRegistrationForm({
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label>Nome *</Label>
<Input value={form.nome} onChange={(e) => setField("nome", e.target.value)} className={errors.nome ? "border-destructive" : ""} />
{errors.nome && <p className="text-sm text-destructive">{errors.nome}</p>}
<Input value={form.full_name} onChange={(e) => setField("full_name", e.target.value)} />
{errors.full_name && <p className="text-sm text-destructive">{errors.full_name}</p>}
</div>
<div className="space-y-2">
<Label>Nome Social</Label>
@ -473,14 +464,19 @@ export function DoctorRegistrationForm({
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label>Especialidade *</Label>
<Input value={form.especialidade} onChange={(e) => setField("especialidade", e.target.value)} className={errors.especialidade ? "border-destructive" : ""} />
<Input
value={form.especialidade} // Mantenha o nome no form como 'especialidade'
onChange={(e) => setField("especialidade", e.target.value)} // Envia o valor correto
className={errors.especialidade ? "border-destructive" : ""}
/>
{errors.especialidade && <p className="text-sm text-destructive">{errors.especialidade}</p>}
</div>
<div className="space-y-2">
<Label>RQE</Label>
<Input value={form.rqe} onChange={(e) => setField("rqe", e.target.value)} />
</div>
</div>
</div>
<div className="space-y-2">
<Label>Currículo</Label>
@ -629,6 +625,7 @@ export function DoctorRegistrationForm({
<Label>E-mail</Label>
<Input value={form.email} onChange={(e) => setField("email", e.target.value)} />
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label>Telefone</Label>
<Input
@ -637,6 +634,16 @@ export function DoctorRegistrationForm({
placeholder="(XX) XXXXX-XXXX"
/>
</div>
<div className="space-y-2">
<Label>Celular</Label>
<Input
value={form.celular}
onChange={(e) => setField("celular", formatPhone(e.target.value))}
placeholder="(XX) XXXXX-XXXX"
/>
</div>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
@ -703,11 +710,14 @@ export function DoctorRegistrationForm({
<div className="space-y-2">
<Label>Agenda/Horário</Label>
<Textarea
// Dentro do form, apenas exiba o campo se precisar dele visualmente, mas não envie
<textarea
value={form.agenda_horario}
onChange={(e) => setField("agenda_horario", e.target.value)}
placeholder="Descreva os dias e horários de atendimento"
/>
disabled={true} // Torne o campo apenas visual, sem enviar
/>
</div>
<div className="space-y-4">

View File

@ -51,7 +51,7 @@ type FormData = {
cpf: string;
rg: string;
sexo: string;
data_nascimento: string;
birth_date: string; // 👈 corrigido
email: string;
telefone: string;
cep: string;
@ -72,7 +72,7 @@ const initial: FormData = {
cpf: "",
rg: "",
sexo: "",
data_nascimento: "",
birth_date: "", // 👈 corrigido
email: "",
telefone: "",
cep: "",
@ -86,6 +86,8 @@ const initial: FormData = {
anexos: [],
};
export function PatientRegistrationForm({
open = true,
onOpenChange,
@ -113,23 +115,27 @@ export function PatientRegistrationForm({
const p = await buscarPacientePorId(String(patientId));
setForm((s) => ({
...s,
nome: p.nome || "",
nome_social: p.nome_social || "",
nome: p.full_name || "", // 👈 trocar nome → full_name
nome_social: p.social_name || "",
cpf: p.cpf || "",
rg: p.rg || "",
sexo: p.sexo || "",
data_nascimento: (p.data_nascimento as string) || "",
telefone: p.telefone || "",
sexo: p.sex || "",
birth_date: p.birth_date || "", // 👈 trocar data_nascimento → birth_date
telefone: p.phone_mobile || "",
email: p.email || "",
cep: p.endereco?.cep || "",
logradouro: p.endereco?.logradouro || "",
numero: p.endereco?.numero || "",
complemento: p.endereco?.complemento || "",
bairro: p.endereco?.bairro || "",
cidade: p.endereco?.cidade || "",
estado: p.endereco?.estado || "",
observacoes: p.observacoes || "",
}));
cep: p.cep || "",
logradouro: p.street || "",
numero: p.number || "",
complemento: p.complement || "",
bairro: p.neighborhood || "",
cidade: p.city || "",
estado: p.state || "",
observacoes: p.notes || "",
}));
const ax = await listarAnexos(String(patientId)).catch(() => []);
setServerAnexos(Array.isArray(ax) ? ax : []);
} catch {
@ -187,26 +193,26 @@ export function PatientRegistrationForm({
function toPayload(): PacienteInput {
return {
nome: form.nome,
nome_social: form.nome_social || null,
full_name: form.nome, // 👈 troca 'nome' por 'full_name'
social_name: form.nome_social || null,
cpf: form.cpf,
rg: form.rg || null,
sexo: form.sexo || null,
data_nascimento: form.data_nascimento || null,
telefone: form.telefone || null,
sex: form.sexo || null,
birth_date: form.birth_date || null, // 👈 troca data_nascimento → birth_date
phone_mobile: form.telefone || null,
email: form.email || null,
endereco: {
cep: form.cep || undefined,
logradouro: form.logradouro || undefined,
numero: form.numero || undefined,
complemento: form.complemento || undefined,
bairro: form.bairro || undefined,
cidade: form.cidade || undefined,
estado: form.estado || undefined,
},
observacoes: form.observacoes || null,
cep: form.cep || null,
street: form.logradouro || null,
number: form.numero || null,
complement: form.complemento || null,
neighborhood: form.bairro || null,
city: form.cidade || null,
state: form.estado || null,
notes: form.observacoes || null,
};
}
}
async function handleSubmit(ev: React.FormEvent) {
ev.preventDefault();
@ -418,7 +424,8 @@ export function PatientRegistrationForm({
</div>
<div className="space-y-2">
<Label>Data de Nascimento</Label>
<Input type="date" value={form.data_nascimento} onChange={(e) => setField("data_nascimento", e.target.value)} />
<Input type="date" value={form.birth_date} onChange={(e) => setField("birth_date", e.target.value)} />
</div>
</div>
</CardContent>

View File

@ -26,32 +26,44 @@ export type Endereco = {
// ===== PACIENTES =====
export type Paciente = {
id: string;
nome?: string;
nome_social?: string | null;
full_name: string;
social_name?: string | null;
cpf?: string;
rg?: string | null;
sexo?: string | null;
data_nascimento?: string | null;
telefone?: string;
sex?: string | null;
birth_date?: string | null;
phone_mobile?: string;
email?: string;
endereco?: Endereco;
observacoes?: string | null;
foto_url?: string | null;
cep?: string | null;
street?: string | null;
number?: string | null;
complement?: string | null;
neighborhood?: string | null;
city?: string | null;
state?: string | null;
notes?: string | null;
};
export type PacienteInput = {
nome: string;
nome_social?: string | null;
full_name: string;
social_name?: string | null;
cpf: string;
rg?: string | null;
sexo?: string | null;
data_nascimento?: string | null;
telefone?: string | null;
sex?: string | null;
birth_date?: string | null;
phone_mobile?: string | null;
email?: string | null;
endereco?: Endereco;
observacoes?: string | null;
cep?: string | null;
street?: string | null;
number?: string | null;
complement?: string | null;
neighborhood?: string | null;
city?: string | null;
state?: string | null;
notes?: string | null;
};
// ===== MÉDICOS =====
export type FormacaoAcademica = {
instituicao: string;
@ -66,9 +78,10 @@ export type DadosBancarios = {
tipo_conta: string;
};
// ===== MÉDICOS =====
export type Medico = {
id: string;
nome?: string;
full_name: string; // Altere 'nome' para 'full_name'
nome_social?: string | null;
cpf?: string;
rg?: string | null;
@ -90,21 +103,37 @@ export type Medico = {
dados_bancarios?: DadosBancarios;
agenda_horario?: string;
valor_consulta?: number | string;
active?: boolean;
cep?: string;
city?: string;
complement?: string;
neighborhood?: string;
number?: string;
phone2?: string;
state?: string;
street?: string;
created_at?: string;
created_by?: string;
updated_at?: string;
updated_by?: string;
user_id?: string;
};
// ===== MÉDICOS =====
export type MedicoInput = {
nome: string;
full_name: string;
nome_social?: string | null;
cpf?: string | null;
cpf: string;
rg?: string | null;
sexo?: string | null;
data_nascimento?: string | null;
telefone?: string | null;
celular?: string | null;
contato_emergencia?: string | null;
email?: string | null;
telefone?: string;
celular?: string; // Este é o celular no seu código, mas talvez tenha que ser 'phone_mobile'
contato_emergencia?: string;
email?: string;
crm: string;
estado_crm?: string | null;
crm_uf?: string | null;
rqe?: string | null;
formacao_academica?: FormacaoAcademica[];
curriculo_url?: string | null;
@ -114,8 +143,25 @@ export type MedicoInput = {
dados_bancarios?: DadosBancarios | null;
agenda_horario?: string | null;
valor_consulta?: number | string | null;
active?: boolean;
cep?: string | null;
city?: string | null;
complement?: string | null;
neighborhood?: string | null;
number?: string | null;
phone2?: string | null; // Talvez seja o campo correto para o segundo telefone
state?: string | null;
street?: string | null;
created_at?: string;
created_by?: string | null;
updated_at?: string;
updated_by?: string | null;
user_id?: string | null;
};
// ===== CONFIG =====
const API_BASE =
process.env.NEXT_PUBLIC_API_BASE ?? "https://yuanqfswhberkoevtmfr.supabase.co";
@ -150,20 +196,26 @@ function withPrefer(h: Record<string, string>, prefer: string) {
}
// Parse genérico
// Dentro de lib/api.ts
async function parse<T>(res: Response): Promise<T> {
let json: any = null;
try {
json = await res.json();
} catch {}
} catch (err) {
console.error("Erro ao parsear a resposta:", err); // Coloque esse log aqui
}
if (!res.ok) {
console.error("[API ERROR]", res.url, res.status, json);
console.error("[API ERROR]", res.url, res.status, json); // Coloque esse log aqui
const code = (json && (json.error?.code || json.code)) ?? res.status;
const msg = (json && (json.error?.message || json.message)) ?? res.statusText;
throw new Error(`${code}: ${msg}`);
}
return (json?.data ?? json) as T;
}
// Helper de paginação (Range/Range-Unit)
function rangeHeaders(page?: number, limit?: number): Record<string, string> {
if (!page || !limit) return {};
@ -270,17 +322,24 @@ export async function buscarMedicoPorId(id: string | number): Promise<Medico> {
return arr[0];
}
// Dentro de lib/api.ts
export async function criarMedico(input: MedicoInput): Promise<Medico> {
const url = `${REST}/doctors`;
console.log("Enviando os dados para a API:", input); // Log para depuração
const url = `${REST}/doctors`; // Endpoint de médicos
const res = await fetch(url, {
method: "POST",
headers: withPrefer({ ...baseHeaders(), "Content-Type": "application/json" }, "return=representation"),
body: JSON.stringify(input),
body: JSON.stringify(input), // Enviando os dados padronizados
});
const arr = await parse<Medico[] | Medico>(res);
return Array.isArray(arr) ? arr[0] : (arr as Medico);
const arr = await parse<Medico[] | Medico>(res); // Resposta da API
return Array.isArray(arr) ? arr[0] : (arr as Medico); // Retorno do médico
}
export async function atualizarMedico(id: string | number, input: MedicoInput): Promise<Medico> {
const url = `${REST}/doctors?id=eq.${id}`;
const res = await fetch(url, {