modulo de agente pessoal de IA
This commit is contained in:
@@ -0,0 +1,435 @@
|
||||
/**
|
||||
* Serviço para integração com API do Asana
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface para workspace do Asana (resposta da API)
|
||||
*/
|
||||
export interface AsanaWorkspaceResponse {
|
||||
gid: string;
|
||||
resource_type: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface para resposta da API de workspaces
|
||||
*/
|
||||
export interface AsanaWorkspacesResponse {
|
||||
data: AsanaWorkspaceResponse[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface para workspace formatado (usado no componente)
|
||||
*/
|
||||
export interface AsanaWorkspace {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface para usuário do Asana (resposta da API)
|
||||
*/
|
||||
export interface AsanaUserResponse {
|
||||
gid: string;
|
||||
name: string;
|
||||
resource_type: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface para resposta da API de usuários
|
||||
*/
|
||||
export interface AsanaUsersResponse {
|
||||
data: AsanaUserResponse[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface para usuário formatado (usado no componente)
|
||||
*/
|
||||
export interface AsanaUser {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface para resposta da API de integração do Asana
|
||||
*/
|
||||
export interface AsanaIntegrationResponse {
|
||||
success: boolean;
|
||||
integracao_id?: string;
|
||||
id?: string;
|
||||
api_key?: string;
|
||||
workspace_gid?: string;
|
||||
workspace_nome?: string;
|
||||
usuario_asana_gid?: string;
|
||||
usuario_asana_nome?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface para request de criação/atualização de integração
|
||||
*/
|
||||
export interface AsanaIntegrationRequest {
|
||||
user_id?: string;
|
||||
api_key: string;
|
||||
workspace_gid: string;
|
||||
workspace_nome: string;
|
||||
usuario_asana_gid: string;
|
||||
usuario_asana_nome: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serviço para integração com Asana
|
||||
*/
|
||||
class AsanaService {
|
||||
private readonly BASE_URL = 'https://app.asana.com/api/1.0';
|
||||
private readonly INTEGRATION_BASE_URL = 'https://prod-hgtx-intelligence-n8n.hgtx.com.br';
|
||||
private readonly INTEGRATION_WEBHOOK_ID = 'c898beff-84cb-44df-a69c-6eff27ccd7aa';
|
||||
|
||||
/**
|
||||
* Lista os workspaces do Asana usando o token fornecido
|
||||
*
|
||||
* @param token - Token de acesso do Asana
|
||||
* @returns Promise com a lista de workspaces formatados
|
||||
*/
|
||||
async getWorkspaces(token: string): Promise<AsanaWorkspace[]> {
|
||||
if (!token || token.trim().length === 0) {
|
||||
throw {
|
||||
success: false,
|
||||
message: 'Token do Asana é obrigatório',
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const axios = (await import('axios')).default;
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
'accept': 'application/json',
|
||||
'authorization': `Bearer ${token.trim()}`,
|
||||
};
|
||||
|
||||
const response = await axios.get<AsanaWorkspacesResponse>(
|
||||
`${this.BASE_URL}/workspaces`,
|
||||
{ headers }
|
||||
);
|
||||
|
||||
// Converte a resposta da API para o formato esperado pelo componente
|
||||
// A API retorna gid, mas o componente espera id
|
||||
return response.data.data.map((workspace) => ({
|
||||
id: workspace.gid,
|
||||
name: workspace.name,
|
||||
}));
|
||||
} catch (error: unknown) {
|
||||
console.error('Erro ao buscar workspaces do Asana:', error);
|
||||
|
||||
// Retorna o erro da API se existir
|
||||
if (error && typeof error === 'object' && 'response' in error) {
|
||||
const axiosError = error as { response?: { data?: any; status?: number } };
|
||||
|
||||
if (axiosError.response?.status === 401) {
|
||||
throw {
|
||||
success: false,
|
||||
message: 'Token inválido ou expirado. Verifique sua chave de API.',
|
||||
};
|
||||
}
|
||||
|
||||
if (axiosError.response?.data) {
|
||||
throw {
|
||||
success: false,
|
||||
message: axiosError.response.data.message || 'Erro ao buscar workspaces do Asana',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
throw {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : 'Erro ao buscar workspaces do Asana',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lista os usuários de um workspace do Asana
|
||||
*
|
||||
* @param token - Token de acesso do Asana
|
||||
* @param workspaceId - ID do workspace (gid)
|
||||
* @returns Promise com a lista de usuários formatados
|
||||
*/
|
||||
async getUsers(token: string, workspaceId: string): Promise<AsanaUser[]> {
|
||||
if (!token || token.trim().length === 0) {
|
||||
throw {
|
||||
success: false,
|
||||
message: 'Token do Asana é obrigatório',
|
||||
};
|
||||
}
|
||||
|
||||
if (!workspaceId || workspaceId.trim().length === 0) {
|
||||
throw {
|
||||
success: false,
|
||||
message: 'ID do workspace é obrigatório',
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const axios = (await import('axios')).default;
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
'accept': 'application/json',
|
||||
'authorization': `Bearer ${token.trim()}`,
|
||||
};
|
||||
|
||||
const response = await axios.get<AsanaUsersResponse>(
|
||||
`${this.BASE_URL}/users?workspace=${workspaceId}`,
|
||||
{ headers }
|
||||
);
|
||||
|
||||
// Converte a resposta da API para o formato esperado pelo componente
|
||||
// A API retorna gid, mas o componente espera id
|
||||
return response.data.data.map((user) => ({
|
||||
id: user.gid,
|
||||
name: user.name,
|
||||
}));
|
||||
} catch (error: unknown) {
|
||||
console.error('Erro ao buscar usuários do Asana:', error);
|
||||
|
||||
// Retorna o erro da API se existir
|
||||
if (error && typeof error === 'object' && 'response' in error) {
|
||||
const axiosError = error as { response?: { data?: any; status?: number } };
|
||||
|
||||
if (axiosError.response?.status === 401) {
|
||||
throw {
|
||||
success: false,
|
||||
message: 'Token inválido ou expirado. Verifique sua chave de API.',
|
||||
};
|
||||
}
|
||||
|
||||
if (axiosError.response?.data) {
|
||||
throw {
|
||||
success: false,
|
||||
message: axiosError.response.data.message || 'Erro ao buscar usuários do Asana',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
throw {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : 'Erro ao buscar usuários do Asana',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtém os detalhes da integração do Asana do usuário
|
||||
*
|
||||
* @param userId - ID do usuário
|
||||
* @returns Promise com os detalhes da integração ou null se não houver
|
||||
*/
|
||||
async getIntegration(userId: string): Promise<AsanaIntegrationResponse | null> {
|
||||
if (!userId || userId.trim().length === 0) {
|
||||
throw {
|
||||
success: false,
|
||||
message: 'ID do usuário é obrigatório',
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const axios = (await import('axios')).default;
|
||||
const { GlobalFunctions } = await import('@/GlobalFunctions');
|
||||
|
||||
// Obtém o token JWT para autenticação
|
||||
const token = await GlobalFunctions.getToken();
|
||||
const apiKey = import.meta.env.VITE_API_KEY || '';
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
'accept': 'application/json',
|
||||
};
|
||||
|
||||
// Adiciona API key (obrigatória)
|
||||
if (apiKey) {
|
||||
headers['apikey'] = apiKey;
|
||||
}
|
||||
|
||||
// Adiciona token JWT se disponível
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
// Em desenvolvimento, usa proxy do Vite para evitar CORS
|
||||
const baseUrl = import.meta.env.DEV
|
||||
? '/api/intelligence'
|
||||
: this.INTEGRATION_BASE_URL;
|
||||
|
||||
const response = await axios.get<AsanaIntegrationResponse>(
|
||||
`${baseUrl}/webhook/${this.INTEGRATION_WEBHOOK_ID}/codex/agente-pessoal/integracoes/asana/${userId}`,
|
||||
{ headers }
|
||||
);
|
||||
|
||||
// Se success é false, retorna null (não tem integração)
|
||||
if (!response.data.success) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return response.data;
|
||||
} catch (error: unknown) {
|
||||
console.error('Erro ao buscar integração do Asana:', error);
|
||||
|
||||
// Se o erro for 404 ou similar, significa que não tem integração cadastrada
|
||||
if (error && typeof error === 'object' && 'response' in error) {
|
||||
const axiosError = error as { response?: { status?: number } };
|
||||
if (axiosError.response?.status === 404) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Para outros erros, retorna null silenciosamente (não quebra a aplicação)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cria uma nova integração do Asana
|
||||
*
|
||||
* @param request - Dados da integração a ser criada
|
||||
* @returns Promise com a resposta da criação
|
||||
*/
|
||||
async createIntegration(request: AsanaIntegrationRequest): Promise<AsanaIntegrationResponse> {
|
||||
if (!request.user_id || !request.api_key || !request.workspace_gid || !request.usuario_asana_gid) {
|
||||
throw {
|
||||
success: false,
|
||||
message: 'Dados incompletos para criar integração',
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const axios = (await import('axios')).default;
|
||||
const { GlobalFunctions } = await import('@/GlobalFunctions');
|
||||
|
||||
// Obtém o token JWT para autenticação
|
||||
const token = await GlobalFunctions.getToken();
|
||||
const apiKey = import.meta.env.VITE_API_KEY || '';
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
'accept': 'application/json',
|
||||
};
|
||||
|
||||
// Adiciona API key (obrigatória)
|
||||
if (apiKey) {
|
||||
headers['apikey'] = apiKey;
|
||||
}
|
||||
|
||||
// Adiciona token JWT se disponível
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
// Em desenvolvimento, usa proxy do Vite para evitar CORS
|
||||
const baseUrl = import.meta.env.DEV
|
||||
? '/api/intelligence'
|
||||
: this.INTEGRATION_BASE_URL;
|
||||
|
||||
const response = await axios.post<AsanaIntegrationResponse>(
|
||||
`${baseUrl}/webhook/codex/agente-pessoal/integracoes/asana`,
|
||||
request,
|
||||
{ headers }
|
||||
);
|
||||
|
||||
return response.data;
|
||||
} catch (error: unknown) {
|
||||
console.error('Erro ao criar integração do Asana:', error);
|
||||
|
||||
// Retorna o erro da API se existir
|
||||
if (error && typeof error === 'object' && 'response' in error) {
|
||||
const axiosError = error as { response?: { data?: any; status?: number } };
|
||||
|
||||
if (axiosError.response?.data) {
|
||||
const errorData = axiosError.response.data;
|
||||
throw {
|
||||
success: false,
|
||||
message: errorData.message || 'Erro ao criar integração do Asana',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
throw {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : 'Erro ao criar integração do Asana',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atualiza uma integração existente do Asana
|
||||
*
|
||||
* @param integracaoId - ID da integração a ser atualizada
|
||||
* @param request - Dados da integração a ser atualizada
|
||||
* @returns Promise com a resposta da atualização
|
||||
*/
|
||||
async updateIntegration(integracaoId: string, request: Omit<AsanaIntegrationRequest, 'user_id'>): Promise<AsanaIntegrationResponse> {
|
||||
if (!integracaoId || !request.api_key || !request.workspace_gid || !request.usuario_asana_gid) {
|
||||
throw {
|
||||
success: false,
|
||||
message: 'Dados incompletos para atualizar integração',
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const axios = (await import('axios')).default;
|
||||
const { GlobalFunctions } = await import('@/GlobalFunctions');
|
||||
|
||||
// Obtém o token JWT para autenticação
|
||||
const token = await GlobalFunctions.getToken();
|
||||
const apiKey = import.meta.env.VITE_API_KEY || '';
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
'accept': 'application/json',
|
||||
};
|
||||
|
||||
// Adiciona API key (obrigatória)
|
||||
if (apiKey) {
|
||||
headers['apikey'] = apiKey;
|
||||
}
|
||||
|
||||
// Adiciona token JWT se disponível
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
// Em desenvolvimento, usa proxy do Vite para evitar CORS
|
||||
const baseUrl = import.meta.env.DEV
|
||||
? '/api/intelligence'
|
||||
: this.INTEGRATION_BASE_URL;
|
||||
|
||||
const response = await axios.put<AsanaIntegrationResponse>(
|
||||
`${baseUrl}/webhook/${this.INTEGRATION_WEBHOOK_ID}/codex/agente-pessoal/integracoes/asana/${integracaoId}`,
|
||||
request,
|
||||
{ headers }
|
||||
);
|
||||
|
||||
return response.data;
|
||||
} catch (error: unknown) {
|
||||
console.error('Erro ao atualizar integração do Asana:', error);
|
||||
|
||||
// Retorna o erro da API se existir
|
||||
if (error && typeof error === 'object' && 'response' in error) {
|
||||
const axiosError = error as { response?: { data?: any; status?: number } };
|
||||
|
||||
if (axiosError.response?.data) {
|
||||
const errorData = axiosError.response.data;
|
||||
throw {
|
||||
success: false,
|
||||
message: errorData.message || 'Erro ao atualizar integração do Asana',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
throw {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : 'Erro ao atualizar integração do Asana',
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exporta instância única (Singleton)
|
||||
export const asanaService = new AsanaService();
|
||||
@@ -6,7 +6,28 @@ export { apiService } from './api';
|
||||
export { transcriptionService } from './transcription';
|
||||
export { audioGenerationService, VOICE_OPTIONS } from './audioGeneration';
|
||||
export { imageGenerationService, IMAGE_SIZE_OPTIONS } from './imageGeneration';
|
||||
export { userProfileService } from './userProfile';
|
||||
export { asanaService } from './asana';
|
||||
export type { TranscriptionResponse, TranscriptionRequest } from './transcription';
|
||||
export type { AudioGenerationResponse, AudioGenerationRequest, VoiceType } from './audioGeneration';
|
||||
export type { ImageGenerationResponse, ImageGenerationRequest, ImageSize } from './imageGeneration';
|
||||
export type {
|
||||
UserProfile,
|
||||
CreateUserRequest,
|
||||
UpdateUserRequest,
|
||||
FinancialIndicators,
|
||||
ExpenseItem,
|
||||
ExpensesResponse,
|
||||
ExpensesFilters,
|
||||
ExpenseCategory
|
||||
} from './userProfile';
|
||||
export type {
|
||||
AsanaWorkspace,
|
||||
AsanaWorkspaceResponse,
|
||||
AsanaWorkspacesResponse,
|
||||
AsanaUser,
|
||||
AsanaUserResponse,
|
||||
AsanaUsersResponse,
|
||||
AsanaIntegrationResponse
|
||||
} from './asana';
|
||||
export type { ApiResponse, UserConfig, ApiError } from './types';
|
||||
|
||||
@@ -0,0 +1,577 @@
|
||||
import { apiService } from './api';
|
||||
import { GlobalFunctions } from '@/GlobalFunctions';
|
||||
|
||||
/**
|
||||
* Interfaces para o serviço de perfil do usuário
|
||||
*/
|
||||
export interface UserProfile {
|
||||
success: boolean;
|
||||
id?: string;
|
||||
nome: string;
|
||||
email: string;
|
||||
whatsapp: string;
|
||||
followup: boolean;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface CreateUserRequest {
|
||||
nome: string;
|
||||
email: string;
|
||||
whatsapp: string;
|
||||
followup: boolean;
|
||||
}
|
||||
|
||||
export interface UpdateUserRequest {
|
||||
nome: string;
|
||||
whatsapp: string;
|
||||
followup: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface para indicadores financeiros
|
||||
*/
|
||||
export interface FinancialIndicators {
|
||||
success: boolean;
|
||||
id: string;
|
||||
nome: string;
|
||||
email: string;
|
||||
total_despesas: string;
|
||||
total_corporativo: string;
|
||||
total_pessoal: string;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface para despesa individual da API
|
||||
*/
|
||||
export interface ExpenseItem {
|
||||
id: string;
|
||||
data_hora: string;
|
||||
descricao: string;
|
||||
tipo: "pessoal" | "corporativo";
|
||||
valor: string;
|
||||
categoria_id: number;
|
||||
categoria_nome: string;
|
||||
usuario_nome: string;
|
||||
usuario_email: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface para resposta da API de despesas
|
||||
*/
|
||||
export interface ExpensesResponse {
|
||||
success: boolean;
|
||||
total_registros: number;
|
||||
total_paginas: number;
|
||||
per_page: number;
|
||||
pagina_atual: number;
|
||||
data: ExpenseItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface para filtros de busca de despesas
|
||||
*/
|
||||
export interface ExpensesFilters {
|
||||
page?: number;
|
||||
per_page?: number;
|
||||
descricao?: string;
|
||||
categoria_id?: number;
|
||||
data_inicial?: string;
|
||||
data_final?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface para categoria de despesas
|
||||
*/
|
||||
export interface ExpenseCategory {
|
||||
id: number;
|
||||
nome: string;
|
||||
descricao: string;
|
||||
criado_em: string;
|
||||
atualizado_em: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serviço para gerenciamento de perfil do usuário
|
||||
*/
|
||||
class UserProfileService {
|
||||
// Em desenvolvimento, usa proxy do Vite para evitar CORS
|
||||
// Em produção, usa URL direta (requer que servidor permita PUT no CORS)
|
||||
private readonly BASE_URL = import.meta.env.DEV
|
||||
? '/api/intelligence'
|
||||
: 'https://prod-hgtx-intelligence-n8n.hgtx.com.br';
|
||||
private readonly WEBHOOK_ID = '2299acaf-70ee-47e3-a6fc-56dcc678d651';
|
||||
private readonly GET_USER_ENDPOINT = `${this.BASE_URL}/webhook/${this.WEBHOOK_ID}/codex/agente-pessoal/user`;
|
||||
private readonly CREATE_USER_ENDPOINT = `${this.BASE_URL}/webhook/codex/agente-pessoal/user`;
|
||||
private readonly UPDATE_USER_ENDPOINT = `${this.BASE_URL}/webhook/${this.WEBHOOK_ID}/codex/agente-pessoal/user`;
|
||||
private readonly GET_FINANCIAL_INDICATORS_ENDPOINT = `${this.BASE_URL}/webhook/${this.WEBHOOK_ID}/codex/agente-pessoal/financas/indicadores`;
|
||||
private readonly GET_EXPENSES_ENDPOINT = `${this.BASE_URL}/webhook/${this.WEBHOOK_ID}/codex/agente-pessoal/financas`;
|
||||
private readonly GET_CATEGORIES_ENDPOINT = `${this.BASE_URL}/webhook/codex/agente-pessoal/categorias`;
|
||||
|
||||
/**
|
||||
* Obtém os detalhes do usuário pelo email
|
||||
*
|
||||
* @param userEmail - Email do usuário (opcional, usa do GlobalFunctions se não fornecido)
|
||||
* @returns Promise com os dados do usuário ou erro se não existir
|
||||
*/
|
||||
async getUserProfile(userEmail?: string): Promise<UserProfile> {
|
||||
const email = userEmail || GlobalFunctions.getUsuarioLogado().email;
|
||||
|
||||
if (!email) {
|
||||
throw {
|
||||
success: false,
|
||||
message: 'Email do usuário não fornecido',
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
// Usa axios diretamente pois a URL é absoluta e diferente do baseURL
|
||||
const axios = (await import('axios')).default;
|
||||
|
||||
// Obtém o token JWT para autenticação
|
||||
const token = await GlobalFunctions.getToken();
|
||||
const apiKey = import.meta.env.VITE_API_KEY || '';
|
||||
|
||||
// Prepara headers de autenticação
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
// Adiciona API key (obrigatória)
|
||||
if (apiKey) {
|
||||
headers['apikey'] = apiKey;
|
||||
}
|
||||
|
||||
// Adiciona token JWT se disponível
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
const response = await axios.get<UserProfile>(
|
||||
`${this.GET_USER_ENDPOINT}/${email}`,
|
||||
{ headers }
|
||||
);
|
||||
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
// Se o usuário não existir, retorna o erro da API
|
||||
if (error.response?.data) {
|
||||
return error.response.data;
|
||||
}
|
||||
|
||||
throw {
|
||||
success: false,
|
||||
message: error.message || 'Erro ao buscar perfil do usuário',
|
||||
status: error.response?.status,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cria um novo usuário
|
||||
*
|
||||
* @param request - Dados do usuário a ser criado
|
||||
* @returns Promise com os dados do usuário criado
|
||||
*/
|
||||
async createUser(request: CreateUserRequest): Promise<UserProfile> {
|
||||
// Validações
|
||||
if (!request.nome || request.nome.trim().length === 0) {
|
||||
throw {
|
||||
success: false,
|
||||
message: 'Nome é obrigatório',
|
||||
};
|
||||
}
|
||||
|
||||
if (!request.email || request.email.trim().length === 0) {
|
||||
throw {
|
||||
success: false,
|
||||
message: 'Email é obrigatório',
|
||||
};
|
||||
}
|
||||
|
||||
if (!request.whatsapp || request.whatsapp.trim().length === 0) {
|
||||
throw {
|
||||
success: false,
|
||||
message: 'WhatsApp é obrigatório',
|
||||
};
|
||||
}
|
||||
|
||||
// Remove formatação do WhatsApp (apenas números)
|
||||
const whatsappNumbers = request.whatsapp.replace(/\D/g, '');
|
||||
|
||||
try {
|
||||
const axios = (await import('axios')).default;
|
||||
|
||||
// Obtém o token JWT para autenticação
|
||||
const token = await GlobalFunctions.getToken();
|
||||
const apiKey = import.meta.env.VITE_API_KEY || '';
|
||||
|
||||
// Prepara headers de autenticação
|
||||
// A API pode precisar apenas da apikey, não do token JWT
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
// Adiciona API key (obrigatória)
|
||||
if (apiKey) {
|
||||
headers['apikey'] = apiKey;
|
||||
} else {
|
||||
console.warn('VITE_API_KEY não configurada');
|
||||
}
|
||||
|
||||
// Adiciona token JWT se disponível (pode ser necessário)
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
console.log('Criando usuário:', {
|
||||
url: this.CREATE_USER_ENDPOINT,
|
||||
hasApiKey: !!apiKey,
|
||||
hasToken: !!token,
|
||||
data: {
|
||||
nome: request.nome.trim(),
|
||||
email: request.email.trim(),
|
||||
whatsapp: whatsappNumbers,
|
||||
followup: request.followup,
|
||||
},
|
||||
});
|
||||
|
||||
const response = await axios.post<UserProfile>(
|
||||
this.CREATE_USER_ENDPOINT,
|
||||
{
|
||||
nome: request.nome.trim(),
|
||||
email: request.email.trim(),
|
||||
whatsapp: whatsappNumbers,
|
||||
followup: request.followup,
|
||||
},
|
||||
{ headers }
|
||||
);
|
||||
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
// Retorna o erro da API se existir
|
||||
if (error.response?.data) {
|
||||
throw error.response.data;
|
||||
}
|
||||
|
||||
throw {
|
||||
success: false,
|
||||
message: error.message || 'Erro ao criar usuário',
|
||||
status: error.response?.status,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atualiza os dados do usuário
|
||||
*
|
||||
* @param userId - ID do usuário
|
||||
* @param request - Dados a serem atualizados
|
||||
* @returns Promise com os dados atualizados do usuário
|
||||
*/
|
||||
async updateUser(userId: string, request: UpdateUserRequest): Promise<UserProfile> {
|
||||
// Validações
|
||||
if (!userId || userId.trim().length === 0) {
|
||||
throw {
|
||||
success: false,
|
||||
message: 'ID do usuário é obrigatório',
|
||||
};
|
||||
}
|
||||
|
||||
if (!request.nome || request.nome.trim().length === 0) {
|
||||
throw {
|
||||
success: false,
|
||||
message: 'Nome é obrigatório',
|
||||
};
|
||||
}
|
||||
|
||||
if (!request.whatsapp || request.whatsapp.trim().length === 0) {
|
||||
throw {
|
||||
success: false,
|
||||
message: 'WhatsApp é obrigatório',
|
||||
};
|
||||
}
|
||||
|
||||
// Remove formatação do WhatsApp (apenas números)
|
||||
const whatsappNumbers = request.whatsapp.replace(/\D/g, '');
|
||||
|
||||
try {
|
||||
const axios = (await import('axios')).default;
|
||||
|
||||
// Obtém o token JWT para autenticação
|
||||
const token = await GlobalFunctions.getToken();
|
||||
const apiKey = import.meta.env.VITE_API_KEY || '';
|
||||
|
||||
// Prepara headers de autenticação
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
// Adiciona API key (obrigatória)
|
||||
if (apiKey) {
|
||||
headers['apikey'] = apiKey;
|
||||
}
|
||||
|
||||
// Adiciona token JWT se disponível
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
console.log('Atualizando usuário:', {
|
||||
url: `${this.UPDATE_USER_ENDPOINT}/${userId}`,
|
||||
method: 'PUT',
|
||||
hasApiKey: !!apiKey,
|
||||
hasToken: !!token,
|
||||
isDev: import.meta.env.DEV,
|
||||
data: {
|
||||
nome: request.nome.trim(),
|
||||
whatsapp: whatsappNumbers,
|
||||
followup: request.followup,
|
||||
},
|
||||
});
|
||||
|
||||
// Usa PUT conforme documentação da API
|
||||
const response = await axios.put<UserProfile>(
|
||||
`${this.UPDATE_USER_ENDPOINT}/${userId}`,
|
||||
{
|
||||
nome: request.nome.trim(),
|
||||
whatsapp: whatsappNumbers,
|
||||
followup: request.followup,
|
||||
},
|
||||
{ headers }
|
||||
);
|
||||
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
// Retorna o erro da API se existir
|
||||
if (error.response?.data) {
|
||||
throw error.response.data;
|
||||
}
|
||||
|
||||
throw {
|
||||
success: false,
|
||||
message: error.message || 'Erro ao atualizar usuário',
|
||||
status: error.response?.status,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtém os indicadores financeiros do usuário
|
||||
*
|
||||
* @param userEmail - Email do usuário (opcional, usa do GlobalFunctions se não fornecido)
|
||||
* @returns Promise com os indicadores financeiros
|
||||
*/
|
||||
async getFinancialIndicators(userEmail?: string): Promise<FinancialIndicators> {
|
||||
const email = userEmail || GlobalFunctions.getUsuarioLogado().email;
|
||||
|
||||
if (!email) {
|
||||
throw {
|
||||
success: false,
|
||||
message: 'Email do usuário é obrigatório',
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const axios = (await import('axios')).default;
|
||||
|
||||
// Obtém o token JWT para autenticação
|
||||
const token = await GlobalFunctions.getToken();
|
||||
const apiKey = import.meta.env.VITE_API_KEY || '';
|
||||
|
||||
// Prepara headers de autenticação
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
// Adiciona API key (obrigatória)
|
||||
if (apiKey) {
|
||||
headers['apikey'] = apiKey;
|
||||
}
|
||||
|
||||
// Adiciona token JWT se disponível
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
const response = await axios.get<FinancialIndicators>(
|
||||
`${this.GET_FINANCIAL_INDICATORS_ENDPOINT}/${email}`,
|
||||
{ headers }
|
||||
);
|
||||
|
||||
return response.data;
|
||||
} catch (error: unknown) {
|
||||
// Retorna o erro da API se existir
|
||||
if (error && typeof error === 'object' && 'response' in error) {
|
||||
const axiosError = error as { response?: { data?: FinancialIndicators } };
|
||||
if (axiosError.response?.data) {
|
||||
throw axiosError.response.data;
|
||||
}
|
||||
}
|
||||
|
||||
throw {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : 'Erro ao buscar indicadores financeiros',
|
||||
status: error && typeof error === 'object' && 'response' in error
|
||||
? (error as { response?: { status?: number } }).response?.status
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtém a lista de despesas do usuário com paginação e filtros
|
||||
*
|
||||
* @param userEmail - Email do usuário (opcional, usa do GlobalFunctions se não fornecido)
|
||||
* @param filters - Filtros de busca (página, itens por página, descrição, categoria, datas)
|
||||
* @returns Promise com a lista de despesas paginada
|
||||
*/
|
||||
async getExpenses(userEmail?: string, filters?: ExpensesFilters): Promise<ExpensesResponse> {
|
||||
const email = userEmail || GlobalFunctions.getUsuarioLogado().email;
|
||||
|
||||
if (!email) {
|
||||
throw {
|
||||
success: false,
|
||||
message: 'Email do usuário é obrigatório',
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const axios = (await import('axios')).default;
|
||||
|
||||
// Obtém o token JWT para autenticação
|
||||
const token = await GlobalFunctions.getToken();
|
||||
const apiKey = import.meta.env.VITE_API_KEY || '';
|
||||
|
||||
// Prepara headers de autenticação
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
// Adiciona API key (obrigatória)
|
||||
if (apiKey) {
|
||||
headers['apikey'] = apiKey;
|
||||
}
|
||||
|
||||
// Adiciona token JWT se disponível
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
// Constrói query params
|
||||
const params = new URLSearchParams();
|
||||
if (filters?.page) params.append('page', filters.page.toString());
|
||||
if (filters?.per_page) params.append('per_page', filters.per_page.toString());
|
||||
if (filters?.descricao) params.append('descricao', filters.descricao);
|
||||
if (filters?.categoria_id) params.append('categoria_id', filters.categoria_id.toString());
|
||||
if (filters?.data_inicial) params.append('data_inicial', filters.data_inicial);
|
||||
if (filters?.data_final) params.append('data_final', filters.data_final);
|
||||
|
||||
const queryString = params.toString();
|
||||
const url = `${this.GET_EXPENSES_ENDPOINT}/${email}${queryString ? `?${queryString}` : ''}`;
|
||||
|
||||
const response = await axios.get<ExpensesResponse[]>(url, { headers });
|
||||
|
||||
// A API retorna um array com um único objeto
|
||||
if (Array.isArray(response.data) && response.data.length > 0) {
|
||||
const expensesResponse = response.data[0];
|
||||
|
||||
// Valida e limpa o array de dados, removendo objetos vazios
|
||||
if (expensesResponse.data && Array.isArray(expensesResponse.data)) {
|
||||
// Filtra objetos vazios (sem propriedades ou apenas com propriedades vazias)
|
||||
expensesResponse.data = expensesResponse.data.filter((item) => {
|
||||
// Verifica se o objeto tem pelo menos uma propriedade válida
|
||||
return item && typeof item === 'object' && Object.keys(item).length > 0 && item.id;
|
||||
});
|
||||
|
||||
// Se após filtrar não há dados, garante que data seja um array vazio
|
||||
if (expensesResponse.data.length === 0) {
|
||||
expensesResponse.data = [];
|
||||
expensesResponse.total_registros = 0;
|
||||
expensesResponse.total_paginas = 0;
|
||||
}
|
||||
} else {
|
||||
// Se data não é um array válido, inicializa como array vazio
|
||||
expensesResponse.data = [];
|
||||
expensesResponse.total_registros = 0;
|
||||
expensesResponse.total_paginas = 0;
|
||||
}
|
||||
|
||||
return expensesResponse;
|
||||
}
|
||||
|
||||
// Fallback caso a estrutura seja diferente - retorna resposta vazia
|
||||
return {
|
||||
success: true,
|
||||
total_registros: 0,
|
||||
total_paginas: 0,
|
||||
per_page: filters?.per_page || 10,
|
||||
pagina_atual: filters?.page || 1,
|
||||
data: [],
|
||||
};
|
||||
} catch (error: unknown) {
|
||||
// Retorna o erro da API se existir
|
||||
if (error && typeof error === 'object' && 'response' in error) {
|
||||
const axiosError = error as { response?: { data?: ExpensesResponse } };
|
||||
if (axiosError.response?.data) {
|
||||
throw axiosError.response.data;
|
||||
}
|
||||
}
|
||||
|
||||
throw {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : 'Erro ao buscar despesas',
|
||||
status: error && typeof error === 'object' && 'response' in error
|
||||
? (error as { response?: { status?: number } }).response?.status
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtém a lista de categorias de despesas
|
||||
*
|
||||
* @returns Promise com a lista de categorias
|
||||
*/
|
||||
async getCategories(): Promise<ExpenseCategory[]> {
|
||||
try {
|
||||
const axios = (await import('axios')).default;
|
||||
|
||||
// Obtém o token JWT para autenticação
|
||||
const token = await GlobalFunctions.getToken();
|
||||
const apiKey = import.meta.env.VITE_API_KEY || '';
|
||||
|
||||
// Prepara headers de autenticação
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
// Adiciona API key (obrigatória)
|
||||
if (apiKey) {
|
||||
headers['apikey'] = apiKey;
|
||||
}
|
||||
|
||||
// Adiciona token JWT se disponível
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
const response = await axios.get<ExpenseCategory[]>(this.GET_CATEGORIES_ENDPOINT, { headers });
|
||||
|
||||
// A API retorna um array de categorias
|
||||
if (Array.isArray(response.data)) {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
return [];
|
||||
} catch (error: unknown) {
|
||||
console.error('Erro ao buscar categorias:', error);
|
||||
|
||||
// Retorna array vazio em caso de erro para não quebrar a aplicação
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exporta instância única (Singleton)
|
||||
export const userProfileService = new UserProfileService();
|
||||
Reference in New Issue
Block a user