- Adiciona Edge Function para calcular slots disponíveis - Implementa método callFunction() no apiClient para Edge Functions - Atualiza appointmentService com getAvailableSlots() e create() - Simplifica AgendamentoConsulta removendo lógica manual de slots - Remove arquivos de teste e documentação temporária - Atualiza README com documentação completa - Adiciona AGENDAMENTO-SLOTS-API.md com detalhes da implementação - Corrige formatação de dados (telefone, CPF, nomes) - Melhora diálogos de confirmação e feedback visual - Otimiza performance e user experience
325 lines
8.6 KiB
TypeScript
325 lines
8.6 KiB
TypeScript
/**
|
|
* Serviço de Usuários
|
|
*/
|
|
|
|
import axios from "axios";
|
|
import { apiClient } from "../api/client";
|
|
import { API_CONFIG } from "../api/config";
|
|
import type {
|
|
UserRoleRecord,
|
|
UserInfo,
|
|
User,
|
|
CreateUserInput,
|
|
CreateUserResponse,
|
|
CreateDoctorInput,
|
|
CreateDoctorResponse,
|
|
CreatePatientInput,
|
|
CreatePatientResponse,
|
|
} from "./types";
|
|
|
|
class UserService {
|
|
/**
|
|
* Lista roles de usuários
|
|
*/
|
|
async listRoles(): Promise<UserRoleRecord[]> {
|
|
const response = await apiClient.get<UserRoleRecord[]>("/user_roles");
|
|
return response.data;
|
|
}
|
|
|
|
/**
|
|
* Obtém informações completas do usuário autenticado
|
|
* Inclui perfil, roles e permissões calculadas
|
|
*/
|
|
async getUserInfo(): Promise<UserInfo> {
|
|
const token = localStorage.getItem(API_CONFIG.STORAGE_KEYS.ACCESS_TOKEN);
|
|
|
|
if (!token) {
|
|
throw new Error("Token não encontrado");
|
|
}
|
|
|
|
const response = await axios.post<UserInfo>(
|
|
`${API_CONFIG.FUNCTIONS_URL}/user-info`,
|
|
{},
|
|
{
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
apikey: API_CONFIG.SUPABASE_ANON_KEY,
|
|
Authorization: `Bearer ${token}`,
|
|
},
|
|
}
|
|
);
|
|
|
|
return response.data;
|
|
}
|
|
|
|
/**
|
|
* Obtém dados básicos do usuário autenticado
|
|
*/
|
|
async getCurrentUser(): Promise<User> {
|
|
const token = localStorage.getItem(API_CONFIG.STORAGE_KEYS.ACCESS_TOKEN);
|
|
|
|
if (!token) {
|
|
throw new Error("Token não encontrado");
|
|
}
|
|
|
|
const response = await axios.get<User>(`${API_CONFIG.AUTH_URL}/user`, {
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
apikey: API_CONFIG.SUPABASE_ANON_KEY,
|
|
Authorization: `Bearer ${token}`,
|
|
},
|
|
});
|
|
|
|
return response.data;
|
|
}
|
|
|
|
/**
|
|
* Cria novo usuário no sistema
|
|
* Pode ser chamado COM ou SEM autenticação:
|
|
* - SEM autenticação: usa signup nativo (/auth/v1/signup) - PÚBLICO
|
|
* - COM autenticação: usa Edge Function (/functions/v1/create-user) - ADMIN
|
|
*/
|
|
async createUser(
|
|
data: CreateUserInput,
|
|
isPublicRegistration: boolean = true
|
|
): Promise<CreateUserResponse> {
|
|
// Registro público: usar endpoint nativo do Supabase
|
|
if (isPublicRegistration) {
|
|
const response = await axios.post<{
|
|
user: User;
|
|
session: { access_token: string; refresh_token: string };
|
|
}>(
|
|
`${API_CONFIG.AUTH_URL}/signup`,
|
|
{
|
|
email: data.email,
|
|
password: data.password,
|
|
options: {
|
|
data: {
|
|
full_name: data.full_name,
|
|
phone: data.phone,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
apikey: API_CONFIG.SUPABASE_ANON_KEY,
|
|
},
|
|
}
|
|
);
|
|
|
|
return {
|
|
success: true,
|
|
user: {
|
|
id: response.data.user.id,
|
|
email: response.data.user.email,
|
|
full_name: data.full_name,
|
|
phone: data.phone || null,
|
|
roles: [data.role],
|
|
},
|
|
message: "Usuário criado com sucesso",
|
|
};
|
|
}
|
|
|
|
// Criação por admin: usar Edge Function
|
|
const token = localStorage.getItem(API_CONFIG.STORAGE_KEYS.ACCESS_TOKEN);
|
|
const response = await axios.post<CreateUserResponse>(
|
|
`${API_CONFIG.FUNCTIONS_URL}/create-user`,
|
|
data,
|
|
{
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
apikey: API_CONFIG.SUPABASE_ANON_KEY,
|
|
Authorization: `Bearer ${token}`,
|
|
},
|
|
}
|
|
);
|
|
|
|
return response.data;
|
|
}
|
|
|
|
/**
|
|
* Adiciona uma role a um usuário
|
|
* Requer permissão de admin
|
|
*/
|
|
async addUserRole(userId: string, role: string): Promise<UserRoleRecord> {
|
|
const response = await apiClient.post<UserRoleRecord>("/user_roles", {
|
|
user_id: userId,
|
|
role: role,
|
|
});
|
|
return response.data;
|
|
}
|
|
|
|
/**
|
|
* Remove uma role de um usuário
|
|
* Requer permissão de admin
|
|
*/
|
|
async removeUserRole(userId: string, role: string): Promise<void> {
|
|
await apiClient.delete(`/user_roles?user_id=eq.${userId}&role=eq.${role}`);
|
|
}
|
|
|
|
/**
|
|
* Cria novo médico no sistema (endpoint especializado)
|
|
* Cria: Auth user + Profile + Role medico + Entrada completa em doctors
|
|
* Obrigatório: crm, crm_uf, cpf, full_name, email
|
|
* Validações: CRM válido, CPF válido, UF válido
|
|
* Use quando tiver TODOS os dados do médico
|
|
*/
|
|
async createDoctor(data: CreateDoctorInput): Promise<CreateDoctorResponse> {
|
|
const token = localStorage.getItem(API_CONFIG.STORAGE_KEYS.ACCESS_TOKEN);
|
|
const url = `${API_CONFIG.FUNCTIONS_URL}/create-doctor`;
|
|
|
|
console.log("[userService.createDoctor] URL:", url);
|
|
console.log("[userService.createDoctor] Data:", data);
|
|
|
|
const response = await axios.post<CreateDoctorResponse>(
|
|
url,
|
|
data,
|
|
{
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
apikey: API_CONFIG.SUPABASE_ANON_KEY,
|
|
Authorization: `Bearer ${token}`,
|
|
},
|
|
}
|
|
);
|
|
return response.data;
|
|
}
|
|
|
|
/**
|
|
* Cria novo paciente no sistema (endpoint especializado)
|
|
* Cria: Auth user + Profile + Role paciente + Entrada completa em patients
|
|
* Obrigatório: cpf, full_name, email, phone_mobile
|
|
* Validações: CPF válido, telefone válido
|
|
* Use quando tiver TODOS os dados do paciente
|
|
*/
|
|
async createPatient(
|
|
data: CreatePatientInput
|
|
): Promise<CreatePatientResponse> {
|
|
const token = localStorage.getItem(API_CONFIG.STORAGE_KEYS.ACCESS_TOKEN);
|
|
const url = `${API_CONFIG.FUNCTIONS_URL}/create-patient`;
|
|
|
|
console.log("[userService.createPatient] URL:", url);
|
|
console.log("[userService.createPatient] Data:", data);
|
|
|
|
const response = await axios.post<CreatePatientResponse>(
|
|
url,
|
|
data,
|
|
{
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
apikey: API_CONFIG.SUPABASE_ANON_KEY,
|
|
Authorization: `Bearer ${token}`,
|
|
},
|
|
}
|
|
);
|
|
return response.data;
|
|
}
|
|
|
|
/**
|
|
* Cria usuário com email e senha (alternativa ao Magic Link)
|
|
* POST /functions/v1/create-user-with-password
|
|
* Requer permissão de admin, gestor ou secretaria
|
|
* O usuário precisa confirmar o email antes de fazer login
|
|
*/
|
|
async createUserWithPassword(data: {
|
|
email: string;
|
|
password: string;
|
|
full_name: string;
|
|
phone?: string;
|
|
role: string;
|
|
create_patient_record?: boolean;
|
|
cpf?: string;
|
|
phone_mobile?: string;
|
|
}): Promise<{
|
|
success: boolean;
|
|
user: {
|
|
id: string;
|
|
email: string;
|
|
full_name: string;
|
|
roles: string[];
|
|
email_confirmed_at: string | null;
|
|
patient_id?: string;
|
|
};
|
|
message: string;
|
|
}> {
|
|
const token = localStorage.getItem(API_CONFIG.STORAGE_KEYS.ACCESS_TOKEN);
|
|
|
|
const response = await axios.post(
|
|
`${API_CONFIG.FUNCTIONS_URL}/create-user-with-password`,
|
|
data,
|
|
{
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
apikey: API_CONFIG.SUPABASE_ANON_KEY,
|
|
Authorization: `Bearer ${token}`,
|
|
},
|
|
}
|
|
);
|
|
|
|
return response.data;
|
|
}
|
|
|
|
/**
|
|
* Deleta usuário permanentemente (Hard Delete)
|
|
* POST /delete-user
|
|
* ⚠️ OPERAÇÃO IRREVERSÍVEL! Use apenas em desenvolvimento/QA
|
|
* Requer permissão de admin ou gestor
|
|
* Deleta em cascata: profiles, user_roles, doctors, patients, etc.
|
|
*/
|
|
async deleteUser(userId: string): Promise<{
|
|
success: boolean;
|
|
message: string;
|
|
userId: string;
|
|
}> {
|
|
const token = localStorage.getItem(API_CONFIG.STORAGE_KEYS.ACCESS_TOKEN);
|
|
|
|
const response = await axios.post<{
|
|
success: boolean;
|
|
message: string;
|
|
userId: string;
|
|
}>(
|
|
`${API_CONFIG.FUNCTIONS_URL}/delete-user`,
|
|
{ userId },
|
|
{
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
apikey: API_CONFIG.SUPABASE_ANON_KEY,
|
|
Authorization: `Bearer ${token}`,
|
|
},
|
|
}
|
|
);
|
|
|
|
return response.data;
|
|
}
|
|
|
|
/**
|
|
* Obtém informações de usuário por ID
|
|
* POST /functions/v1/user-info-by-id
|
|
* Requer permissão de admin ou gestor
|
|
*/
|
|
async getUserInfoById(userId: string): Promise<UserInfo> {
|
|
const token = localStorage.getItem(API_CONFIG.STORAGE_KEYS.ACCESS_TOKEN);
|
|
|
|
if (!token) {
|
|
throw new Error("Token não encontrado");
|
|
}
|
|
|
|
const response = await axios.post<UserInfo>(
|
|
`${API_CONFIG.FUNCTIONS_URL}/user-info-by-id`,
|
|
{ user_id: userId },
|
|
{
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
apikey: API_CONFIG.SUPABASE_ANON_KEY,
|
|
Authorization: `Bearer ${token}`,
|
|
},
|
|
}
|
|
);
|
|
|
|
return response.data;
|
|
}
|
|
}
|
|
|
|
export const userService = new UserService();
|