From cde4c4230947272781adad260a3326c5d9c26147 Mon Sep 17 00:00:00 2001 From: M-Gabrielly Date: Tue, 30 Sep 2025 12:01:37 -0300 Subject: [PATCH] feat(auth): Creates user in patient registration - Adds automatic user creation in the new patient registration flow. - To avoid merge conflicts, the user and profile APIs have been separated from the main lib/api.ts file. --- .../forms/patient-registration-form.tsx | 31 ++++---- susconecta/lib/api/medicos.ts | 1 + susconecta/lib/api/pacientes.ts | 1 + susconecta/lib/api/perfis.ts | 64 +++++++++++++++ susconecta/lib/api/usuarios.ts | 79 +++++++++++++++++++ 5 files changed, 160 insertions(+), 16 deletions(-) create mode 100644 susconecta/lib/api/medicos.ts create mode 100644 susconecta/lib/api/pacientes.ts create mode 100644 susconecta/lib/api/perfis.ts create mode 100644 susconecta/lib/api/usuarios.ts diff --git a/susconecta/components/forms/patient-registration-form.tsx b/susconecta/components/forms/patient-registration-form.tsx index 06170eb..87b0d15 100644 --- a/susconecta/components/forms/patient-registration-form.tsx +++ b/susconecta/components/forms/patient-registration-form.tsx @@ -25,8 +25,9 @@ import { listarAnexos, removerAnexo, buscarPacientePorId, - listarPerfis, } from "@/lib/api"; +import { listarPerfis } from "@/lib/api/perfis"; +import { criarUsuario } from "@/lib/api/usuarios"; import { validarCPFLocal } from "@/lib/utils"; import { verificarCpfDuplicado } from "@/lib/api"; @@ -241,23 +242,21 @@ export function PatientRegistrationForm({ let saved: Paciente; if (mode === "create") { saved = await criarPaciente(payload); - - console.log("--- INÍCIO DO TESTE DE API ---"); - console.log("Paciente recém-criado:", saved); - try { - console.log("Buscando lista de perfis para verificar a criação..."); - const perfis = await listarPerfis(); - console.log("Lista de Perfis encontrada:", perfis); - const perfilCorrespondente = perfis.find(p => p.email === saved.email); - if (perfilCorrespondente) { - console.log("SUCESSO: Perfil correspondente foi encontrado!", perfilCorrespondente); - } else { - console.log("FALHA: Nenhum perfil correspondente ao email do paciente foi encontrado na lista de perfis."); + + if (saved.email && saved.nome) { + try { + await criarUsuario({ + email: saved.email, + full_name: saved.nome, + phone: saved.telefone || undefined, + role: 'paciente', + }); + } catch (userError) { + console.error("Falha ao criar usuário para o paciente:", userError); + // TODO: O que fazer se a criação do usuário falhar? + // Por enquanto, apenas logamos o erro. } - } catch (error) { - console.error("ERRO AO BUSCAR PERFIS:", error); } - console.log("--- FIM DO TESTE DE API ---"); } else { if (patientId == null) throw new Error("Paciente inexistente para edição"); diff --git a/susconecta/lib/api/medicos.ts b/susconecta/lib/api/medicos.ts new file mode 100644 index 0000000..3ae4724 --- /dev/null +++ b/susconecta/lib/api/medicos.ts @@ -0,0 +1 @@ +// Arquivo reservado para as APIs de Médicos diff --git a/susconecta/lib/api/pacientes.ts b/susconecta/lib/api/pacientes.ts new file mode 100644 index 0000000..461e86a --- /dev/null +++ b/susconecta/lib/api/pacientes.ts @@ -0,0 +1 @@ +// Arquivo reservado para as APIs de Pacientes diff --git a/susconecta/lib/api/perfis.ts b/susconecta/lib/api/perfis.ts new file mode 100644 index 0000000..6234253 --- /dev/null +++ b/susconecta/lib/api/perfis.ts @@ -0,0 +1,64 @@ +import { + API_BASE, + headers, + logAPI, + parse, +} from "../api"; +import type { ApiOk } from "../api"; + +// +// Perfis de Usuário (Profiles) +// + +export type UserProfile = { + id: string; + full_name?: string; + email?: string; + phone?: string; + avatar_url?: string; + disabled?: boolean; + created_at?: string; + updated_at?: string; +}; + +export type UserProfileInput = { + full_name?: string; + email?: string; + phone?: string; + avatar_url?: string; + disabled?: boolean; +}; + +export async function listarPerfis(params?: { page?: number; limit?: number; q?: string }): Promise { + const query = new URLSearchParams(); + if (params?.page) query.set("page", String(params.page)); + if (params?.limit) query.set("limit", String(params.limit)); + if (params?.q) query.set("q", params.q); + const url = `${API_BASE}/rest/v1/profiles${query.toString() ? `?${query.toString()}` : ""}`; + + const res = await fetch(url, { method: "GET", headers: headers("json") }); + const data = await parse>(res); + logAPI("listarPerfis", { url, result: data }); + return data?.data ?? (data as any); +} + +export async function buscarPerfilPorId(id: string | number): Promise { + const url = `${API_BASE}/rest/v1/profiles?id=eq.${id}`; + const res = await fetch(url, { method: "GET", headers: headers("json") }); + // A API da Supabase/PostgREST retorna um array mesmo pedindo um ID, então pegamos o primeiro. + const data = await parse(res); + const profile = data[0]; + logAPI("buscarPerfilPorId", { url, result: profile }); + return profile; +} + +export async function atualizarPerfil(id: string | number, input: UserProfileInput): Promise { + const url = `${API_BASE}/rest/v1/profiles?id=eq.${id}`; + const res = await fetch(url, { method: "PATCH", headers: headers("json"), body: JSON.stringify(input) }); + // O método PATCH no PostgREST retorna um array vazio por padrão. Para retornar os dados, precisa de um header `Prefer: return=representation` + // Por simplicidade, vamos assumir que se não deu erro, a operação foi um sucesso. + // Se a API estiver configurada para retornar o objeto, o parse vai funcionar. + const data = await parse>(res); + logAPI("atualizarPerfil", { url, payload: input, result: data }); + return data?.data ?? (data as any); +} \ No newline at end of file diff --git a/susconecta/lib/api/usuarios.ts b/susconecta/lib/api/usuarios.ts new file mode 100644 index 0000000..b7485e5 --- /dev/null +++ b/susconecta/lib/api/usuarios.ts @@ -0,0 +1,79 @@ +import { + API_BASE, + headers, + logAPI, + parse, +} from "../api"; +import type { ApiOk } from "../api"; +import type { UserProfile } from "./perfis"; + +// +// User Management APIs +// + +export type UserRole = { + id: string; + user_id: string; + role: 'admin' | 'medico' | 'paciente'; + created_at: string; +}; + +export type CreateUserInput = { + email: string; + password?: string; + full_name: string; + phone?: string; + role: 'admin' | 'medico' | 'paciente'; +}; + +export type CreatedUser = { + id: string; + email: string; + full_name: string; + phone?: string; + role: string; +}; + +export type CompleteUserInfo = { + user: { + id: string; + email: string; + email_confirmed_at: string; + created_at: string; + last_sign_in_at: string; + }; + profile: UserProfile; + roles: string[]; + permissions: { + isAdmin: boolean; + isManager: boolean; + isDoctor: boolean; + isSecretary: boolean; + isAdminOrManager: boolean; + }; +}; + +export async function criarUsuario(input: CreateUserInput): Promise<{ success: boolean; user: CreatedUser }> { + const url = `${API_BASE}/functions/v1/create-user`; + const res = await fetch(url, { method: "POST", headers: headers("json"), body: JSON.stringify(input) }); + const data = await parse(res); + logAPI("criarUsuario", { url, payload: input, result: data }); + return data; +} + +export async function listarUserRoles(): Promise { + const url = `${API_BASE}/rest/v1/user_roles`; + const res = await fetch(url, { method: "GET", headers: headers("json") }); + const data = await parse(res); + logAPI("listarUserRoles", { url, result: data }); + return data ?? []; +} + +export async function getCompleteUserInfo(userId: string): Promise { + const url = `${API_BASE}/functions/v1/user-info`; + // Assuming the function takes the user ID in the body of a POST request + const res = await fetch(url, { method: "POST", headers: headers("json"), body: JSON.stringify({ id: userId }) }); + const data = await parse(res); + logAPI("getCompleteUserInfo", { url, payload: { id: userId }, result: data }); + return data; +} \ No newline at end of file