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.
This commit is contained in:
parent
ba89bc17f2
commit
b577a418e1
@ -26,8 +26,9 @@ import {
|
|||||||
listarAnexos,
|
listarAnexos,
|
||||||
removerAnexo,
|
removerAnexo,
|
||||||
buscarPacientePorId,
|
buscarPacientePorId,
|
||||||
listarPerfis,
|
|
||||||
} from "@/lib/api";
|
} from "@/lib/api";
|
||||||
|
import { listarPerfis } from "@/lib/api/perfis";
|
||||||
|
import { criarUsuario } from "@/lib/api/usuarios";
|
||||||
|
|
||||||
type Mode = "create" | "edit";
|
type Mode = "create" | "edit";
|
||||||
|
|
||||||
@ -231,23 +232,21 @@ export function PatientRegistrationForm({
|
|||||||
let saved: Paciente;
|
let saved: Paciente;
|
||||||
if (mode === "create") {
|
if (mode === "create") {
|
||||||
saved = await criarPaciente(payload);
|
saved = await criarPaciente(payload);
|
||||||
|
|
||||||
console.log("--- INÍCIO DO TESTE DE API ---");
|
if (saved.email && saved.nome) {
|
||||||
console.log("Paciente recém-criado:", saved);
|
try {
|
||||||
try {
|
await criarUsuario({
|
||||||
console.log("Buscando lista de perfis para verificar a criação...");
|
email: saved.email,
|
||||||
const perfis = await listarPerfis();
|
full_name: saved.nome,
|
||||||
console.log("Lista de Perfis encontrada:", perfis);
|
phone: saved.telefone || undefined,
|
||||||
const perfilCorrespondente = perfis.find(p => p.email === saved.email);
|
role: 'paciente',
|
||||||
if (perfilCorrespondente) {
|
});
|
||||||
console.log("SUCESSO: Perfil correspondente foi encontrado!", perfilCorrespondente);
|
} catch (userError) {
|
||||||
} else {
|
console.error("Falha ao criar usuário para o paciente:", userError);
|
||||||
console.log("FALHA: Nenhum perfil correspondente ao email do paciente foi encontrado na lista de perfis.");
|
// 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 {
|
} else {
|
||||||
if (patientId == null) throw new Error("Paciente inexistente para edição");
|
if (patientId == null) throw new Error("Paciente inexistente para edição");
|
||||||
|
|||||||
@ -60,7 +60,7 @@ export type PacienteInput = {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
const API_BASE = process.env.NEXT_PUBLIC_API_BASE ?? "https://mock.apidog.com/m1/1053378-0-default";
|
export const API_BASE = process.env.NEXT_PUBLIC_API_BASE ?? "https://mock.apidog.com/m1/1053378-0-default";
|
||||||
const MEDICOS_BASE = process.env.NEXT_PUBLIC_MEDICOS_BASE_PATH ?? "/medicos";
|
const MEDICOS_BASE = process.env.NEXT_PUBLIC_MEDICOS_BASE_PATH ?? "/medicos";
|
||||||
|
|
||||||
export const PATHS = {
|
export const PATHS = {
|
||||||
@ -88,7 +88,7 @@ function getAuthToken(): string | null {
|
|||||||
return localStorage.getItem('auth_token');
|
return localStorage.getItem('auth_token');
|
||||||
}
|
}
|
||||||
|
|
||||||
function headers(kind: "json" | "form" = "json"): Record<string, string> {
|
export function headers(kind: "json" | "form" = "json"): Record<string, string> {
|
||||||
const h: Record<string, string> = {};
|
const h: Record<string, string> = {};
|
||||||
|
|
||||||
// API Key da Supabase sempre necessária
|
// API Key da Supabase sempre necessária
|
||||||
@ -104,7 +104,7 @@ function headers(kind: "json" | "form" = "json"): Record<string, string> {
|
|||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
function logAPI(title: string, info: { url?: string; payload?: any; result?: any } = {}) {
|
export function logAPI(title: string, info: { url?: string; payload?: any; result?: any } = {}) {
|
||||||
try {
|
try {
|
||||||
console.group(`[API] ${title}`);
|
console.group(`[API] ${title}`);
|
||||||
if (info.url) console.log("url:", info.url);
|
if (info.url) console.log("url:", info.url);
|
||||||
@ -114,7 +114,7 @@ function logAPI(title: string, info: { url?: string; payload?: any; result?: any
|
|||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function parse<T>(res: Response): Promise<T> {
|
export async function parse<T>(res: Response): Promise<T> {
|
||||||
let json: any = null;
|
let json: any = null;
|
||||||
try {
|
try {
|
||||||
json = await res.json();
|
json = await res.json();
|
||||||
@ -344,135 +344,6 @@ export type MedicoInput = {
|
|||||||
valor_consulta?: number | string | null;
|
valor_consulta?: number | string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
|
||||||
// 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<UserProfile[]> {
|
|
||||||
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<ApiOk<UserProfile[]>>(res);
|
|
||||||
logAPI("listarPerfis", { url, result: data });
|
|
||||||
return data?.data ?? (data as any);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function buscarPerfilPorId(id: string | number): Promise<UserProfile> {
|
|
||||||
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<UserProfile[]>(res);
|
|
||||||
const profile = data[0];
|
|
||||||
logAPI("buscarPerfilPorId", { url, result: profile });
|
|
||||||
return profile;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function atualizarPerfil(id: string | number, input: UserProfileInput): Promise<UserProfile> {
|
|
||||||
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<ApiOk<UserProfile>>(res);
|
|
||||||
logAPI("atualizarPerfil", { url, payload: input, result: data });
|
|
||||||
return data?.data ?? (data as any);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// 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<any>(res);
|
|
||||||
logAPI("criarUsuario", { url, payload: input, result: data });
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function listarUserRoles(): Promise<UserRole[]> {
|
|
||||||
const url = `${API_BASE}/rest/v1/user_roles`;
|
|
||||||
const res = await fetch(url, { method: "GET", headers: headers("json") });
|
|
||||||
const data = await parse<UserRole[]>(res);
|
|
||||||
logAPI("listarUserRoles", { url, result: data });
|
|
||||||
return data ?? [];
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getCompleteUserInfo(userId: string): Promise<CompleteUserInfo> {
|
|
||||||
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<CompleteUserInfo>(res);
|
|
||||||
logAPI("getCompleteUserInfo", { url, payload: { id: userId }, result: data });
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// MÉDICOS (CRUD)
|
// MÉDICOS (CRUD)
|
||||||
|
|||||||
1
susconecta/lib/api/medicos.ts
Normal file
1
susconecta/lib/api/medicos.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
// Arquivo reservado para as APIs de Médicos
|
||||||
1
susconecta/lib/api/pacientes.ts
Normal file
1
susconecta/lib/api/pacientes.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
// Arquivo reservado para as APIs de Pacientes
|
||||||
64
susconecta/lib/api/perfis.ts
Normal file
64
susconecta/lib/api/perfis.ts
Normal file
@ -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<UserProfile[]> {
|
||||||
|
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<ApiOk<UserProfile[]>>(res);
|
||||||
|
logAPI("listarPerfis", { url, result: data });
|
||||||
|
return data?.data ?? (data as any);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function buscarPerfilPorId(id: string | number): Promise<UserProfile> {
|
||||||
|
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<UserProfile[]>(res);
|
||||||
|
const profile = data[0];
|
||||||
|
logAPI("buscarPerfilPorId", { url, result: profile });
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function atualizarPerfil(id: string | number, input: UserProfileInput): Promise<UserProfile> {
|
||||||
|
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<ApiOk<UserProfile>>(res);
|
||||||
|
logAPI("atualizarPerfil", { url, payload: input, result: data });
|
||||||
|
return data?.data ?? (data as any);
|
||||||
|
}
|
||||||
79
susconecta/lib/api/usuarios.ts
Normal file
79
susconecta/lib/api/usuarios.ts
Normal file
@ -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<any>(res);
|
||||||
|
logAPI("criarUsuario", { url, payload: input, result: data });
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listarUserRoles(): Promise<UserRole[]> {
|
||||||
|
const url = `${API_BASE}/rest/v1/user_roles`;
|
||||||
|
const res = await fetch(url, { method: "GET", headers: headers("json") });
|
||||||
|
const data = await parse<UserRole[]>(res);
|
||||||
|
logAPI("listarUserRoles", { url, result: data });
|
||||||
|
return data ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getCompleteUserInfo(userId: string): Promise<CompleteUserInfo> {
|
||||||
|
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<CompleteUserInfo>(res);
|
||||||
|
logAPI("getCompleteUserInfo", { url, payload: { id: userId }, result: data });
|
||||||
|
return data;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user