M-Gabrielly dea39d9421 refactor(api): Separates the API modules for patients and physicians
Moves the functions and types related to patients and physicians from the single file lib/api.ts to their own dedicated files in lib/api/pacientes.ts and lib/api/medicos.ts.
2025-10-01 23:21:51 -03:00

269 lines
7.6 KiB
TypeScript

// lib/api.ts
// Re-exporta as funções e tipos dos módulos específicos
export * from "./api/pacientes";
export * from "./api/medicos";
// Mantenha aqui apenas o código base da API que é compartilhado
export type ApiOk<T = any> = {
success?: boolean;
data: T;
message?: string;
pagination?: {
current_page?: number;
per_page?: number;
total_pages?: number;
total?: number;
};
};
// ===== TIPOS COMUNS =====
export type Endereco = {
cep?: string;
logradouro?: string;
numero?: string;
complemento?: string;
bairro?: string;
cidade?: string;
estado?: string;
};
// ===== PACIENTES =====
export type Paciente = {
id: string;
nome?: string;
nome_social?: string | null;
cpf?: string;
rg?: string | null;
sexo?: string | null;
data_nascimento?: string | null;
telefone?: string;
email?: string;
endereco?: Endereco;
observacoes?: string | null;
foto_url?: string | null;
};
export type PacienteInput = {
nome: string;
nome_social?: string | null;
cpf: string;
rg?: string | null;
sexo?: string | null;
data_nascimento?: string | null;
telefone?: string | null;
email?: string | null;
endereco?: Endereco;
observacoes?: string | null;
};
// ===== MÉDICOS =====
export type FormacaoAcademica = {
instituicao: string;
curso: string;
ano_conclusao: string;
};
export type DadosBancarios = {
banco: string;
agencia: string;
conta: string;
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;
};
export type MedicoInput = {
nome: string;
nome_social?: string | null;
cpf?: string | null;
rg?: string | null;
sexo?: string | null;
data_nascimento?: string | null;
telefone?: string | null;
celular?: string | null;
contato_emergencia?: string | null;
email?: string | null;
crm: string;
estado_crm?: string | null;
rqe?: string | null;
formacao_academica?: FormacaoAcademica[];
curriculo_url?: string | null;
especialidade: string;
observacoes?: string | null;
tipo_vinculo?: string | null;
dados_bancarios?: DadosBancarios | null;
agenda_horario?: 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);
}
// ===== CONFIG =====
export const API_BASE =
process.env.NEXT_PUBLIC_API_BASE ?? "https://yuanqfswhberkoevtmfr.supabase.co";
export const REST = `${API_BASE}/rest/v1`;
// Token salvo no browser (aceita auth_token ou token)
function getAuthToken(): string | null {
if (typeof window === "undefined") return null;
return (
localStorage.getItem("auth_token") ||
localStorage.getItem("token") ||
sessionStorage.getItem("auth_token") ||
sessionStorage.getItem("token")
);
}
// Cabeçalhos base
export function baseHeaders(): Record<string, string> {
const h: Record<string, string> = {
apikey:
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ",
Accept: "application/json",
};
const jwt = getAuthToken();
if (jwt) h.Authorization = `Bearer ${jwt}`;
return h;
}
// Para POST/PATCH/DELETE e para GET com count
export function withPrefer(h: Record<string, string>, prefer: string) {
return { ...h, Prefer: prefer };
}
// Parse genérico
export async function parse<T>(res: Response): Promise<T> {
let json: any = null;
try {
json = await res.json();
} catch {}
if (!res.ok) {
console.error("[API ERROR]", res.url, res.status, json);
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)
export function rangeHeaders(page?: number, limit?: number): Record<string, string> {
if (!page || !limit) return {};
const start = (page - 1) * limit;
const end = start + limit - 1;
return { Range: `${start}-${end}`, "Range-Unit": "items" };
}
// ===== CEP (usado nos formulários) =====
export async function buscarCepAPI(cep: string): Promise<{
logradouro?: string;
bairro?: string;
localidade?: string;
uf?: string;
erro?: boolean;
}> {
const clean = (cep || "").replace(/\D/g, "");
try {
const res = await fetch(`https://viacep.com.br/ws/${clean}/json/`);
const json = await res.json();
if (json?.erro) return { erro: true };
return {
logradouro: json.logradouro ?? "",
bairro: json.bairro ?? "",
localidade: json.localidade ?? "",
uf: json.uf ?? "",
erro: false,
};
} catch {
return { erro: true };
}
}
// Funções de log e outras utilidades podem ser mantidas aqui se necessário
export const logAPI = (name: string, details: any) => {
if (process.env.NODE_ENV === 'development') {
console.log(`[API Call: ${name}]`, details);
}
};