3 Commits

8 changed files with 313 additions and 86 deletions
+1 -17
View File
@@ -2911,7 +2911,6 @@
"integrity": "sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==", "integrity": "sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"undici-types": "~6.21.0" "undici-types": "~6.21.0"
} }
@@ -2929,7 +2928,6 @@
"integrity": "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==", "integrity": "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==",
"devOptional": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@types/prop-types": "*", "@types/prop-types": "*",
"csstype": "^3.0.2" "csstype": "^3.0.2"
@@ -2941,7 +2939,6 @@
"integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
"devOptional": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"peer": true,
"peerDependencies": { "peerDependencies": {
"@types/react": "^18.0.0" "@types/react": "^18.0.0"
} }
@@ -2992,7 +2989,6 @@
"integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "8.38.0", "@typescript-eslint/scope-manager": "8.38.0",
"@typescript-eslint/types": "8.38.0", "@typescript-eslint/types": "8.38.0",
@@ -3225,7 +3221,6 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
}, },
@@ -3447,7 +3442,6 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"caniuse-lite": "^1.0.30001726", "caniuse-lite": "^1.0.30001726",
"electron-to-chromium": "^1.5.173", "electron-to-chromium": "^1.5.173",
@@ -3806,7 +3800,6 @@
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
"integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
"license": "MIT", "license": "MIT",
"peer": true,
"funding": { "funding": {
"type": "github", "type": "github",
"url": "https://github.com/sponsors/kossnocorp" "url": "https://github.com/sponsors/kossnocorp"
@@ -3911,8 +3904,7 @@
"version": "8.6.0", "version": "8.6.0",
"resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz",
"integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==",
"license": "MIT", "license": "MIT"
"peer": true
}, },
"node_modules/embla-carousel-react": { "node_modules/embla-carousel-react": {
"version": "8.6.0", "version": "8.6.0",
@@ -4055,7 +4047,6 @@
"integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==", "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1", "@eslint-community/regexpp": "^4.12.1",
@@ -5751,7 +5742,6 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"nanoid": "^3.3.11", "nanoid": "^3.3.11",
"picocolors": "^1.1.1", "picocolors": "^1.1.1",
@@ -5944,7 +5934,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"loose-envify": "^1.1.0" "loose-envify": "^1.1.0"
}, },
@@ -5971,7 +5960,6 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"loose-envify": "^1.1.0", "loose-envify": "^1.1.0",
"scheduler": "^0.23.2" "scheduler": "^0.23.2"
@@ -5985,7 +5973,6 @@
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.61.1.tgz", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.61.1.tgz",
"integrity": "sha512-2vbXUFDYgqEgM2RcXcAT2PwDW/80QARi+PKmHy5q2KhuKvOlG8iIYgf7eIlIANR5trW9fJbP4r5aub3a4egsew==", "integrity": "sha512-2vbXUFDYgqEgM2RcXcAT2PwDW/80QARi+PKmHy5q2KhuKvOlG8iIYgf7eIlIANR5trW9fJbP4r5aub3a4egsew==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=18.0.0" "node": ">=18.0.0"
}, },
@@ -6539,7 +6526,6 @@
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
"integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@alloc/quick-lru": "^5.2.0", "@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2", "arg": "^5.0.2",
@@ -6664,7 +6650,6 @@
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
"tsserver": "bin/tsserver" "tsserver": "bin/tsserver"
@@ -6844,7 +6829,6 @@
"integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"esbuild": "^0.21.3", "esbuild": "^0.21.3",
"postcss": "^8.4.43", "postcss": "^8.4.43",
+1 -1
View File
@@ -21,7 +21,7 @@ export const Layout = ({ children, activeTab, onTabChange }: LayoutProps) => {
{ id: "images" as TabType, label: "Imagens", icon: Image }, { id: "images" as TabType, label: "Imagens", icon: Image },
{ id: "transcription" as TabType, label: "Transcrição de Áudio", icon: AudioLines }, { id: "transcription" as TabType, label: "Transcrição de Áudio", icon: AudioLines },
{ id: "generation" as TabType, label: "Geração de Áudio", icon: Mic }, { id: "generation" as TabType, label: "Geração de Áudio", icon: Mic },
{ id: "bots" as TabType, label: "Bots", icon: Bot }, //{ id: "bots" as TabType, label: "Bots", icon: Bot },
{ id: "agent" as TabType, label: "Agente de Parecer", icon: Brain }, { id: "agent" as TabType, label: "Agente de Parecer", icon: Brain },
]; ];
@@ -48,7 +48,7 @@ import {
PaginationPrevious, PaginationPrevious,
} from "@/components/ui/pagination"; } from "@/components/ui/pagination";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { personalAgent, FinancialIndicators, ExpenseItem, ExpensesResponse, ExpensesFilters, ExpenseCategory } from "@/services/personalAgent"; import { userProfileService, FinancialIndicators, ExpenseItem, ExpensesResponse, ExpensesFilters, ExpenseCategory } from "@/services/userProfile";
import { GlobalFunctions, TransferAreaProperties } from "@/GlobalFunctions"; import { GlobalFunctions, TransferAreaProperties } from "@/GlobalFunctions";
import { toast } from "@/hooks/use-toast"; import { toast } from "@/hooks/use-toast";
@@ -175,7 +175,7 @@ export default function Financas() {
} }
// Busca os indicadores financeiros da API // Busca os indicadores financeiros da API
const response = await personalAgent.getFinancialIndicators(userEmail); const response = await userProfileService.getFinancialIndicators(userEmail);
if (response.success) { if (response.success) {
setIndicators(response); setIndicators(response);
@@ -265,7 +265,7 @@ export default function Financas() {
filters.data_final = format(endDate, "yyyy-MM-dd"); filters.data_final = format(endDate, "yyyy-MM-dd");
} }
const response = await personalAgent.getExpenses(emailToUse, filters); const response = await userProfileService.getExpenses(emailToUse, filters);
if (response.success) { if (response.success) {
// Garante que a resposta tenha estrutura válida mesmo quando não há dados // Garante que a resposta tenha estrutura válida mesmo quando não há dados
@@ -308,7 +308,8 @@ export default function Financas() {
// Carrega categorias // Carrega categorias
try { try {
setLoadingCategories(true); setLoadingCategories(true);
const categoriesData = await personalAgent.getCategories(); const categoriesData = await userProfileService.getCategories();
console.log("Categorias carregadas:", categoriesData);
setCategories(categoriesData); setCategories(categoriesData);
} catch (error) { } catch (error) {
console.error("Erro ao carregar categorias:", error); console.error("Erro ao carregar categorias:", error);
@@ -4,7 +4,7 @@ import { GoogleCalendarCard } from "@/modules/intelligence-ia/components/integra
import { GoogleSheetsCard } from "@/modules/intelligence-ia/components/integrations/GoogleSheetsCard"; import { GoogleSheetsCard } from "@/modules/intelligence-ia/components/integrations/GoogleSheetsCard";
import { AsanaCard } from "@/modules/intelligence-ia/components/integrations/AsanaCard"; import { AsanaCard } from "@/modules/intelligence-ia/components/integrations/AsanaCard";
import { asanaService } from "@/services/asana"; import { asanaService } from "@/services/asana";
import { personalAgent } from "@/services/personalAgent"; import { userProfileService } from "@/services/userProfile";
import { GlobalFunctions, TransferAreaProperties } from "@/GlobalFunctions"; import { GlobalFunctions, TransferAreaProperties } from "@/GlobalFunctions";
interface Workspace { interface Workspace {
@@ -199,14 +199,16 @@ export default function Integrations() {
} }
if (!userEmail) { if (!userEmail) {
console.log("Integrations: Não foi possível obter email do usuário");
setLoadingIntegration(false); setLoadingIntegration(false);
return; return;
} }
// Busca o perfil do usuário para obter o ID // Busca o perfil do usuário para obter o ID
const userProfile = await personalAgent.getUserProfile(userEmail); const userProfile = await userProfileService.getUserProfile(userEmail);
if (!userProfile.success || !userProfile.id) { if (!userProfile.success || !userProfile.id) {
console.log("Integrations: Usuário não encontrado ou sem ID");
setLoadingIntegration(false); setLoadingIntegration(false);
return; return;
} }
@@ -14,7 +14,7 @@ import {
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { User, Mail, Phone, Save, Bell, Loader2, Plus } from "lucide-react"; import { User, Mail, Phone, Save, Bell, Loader2, Plus } from "lucide-react";
import { toast } from "@/hooks/use-toast"; import { toast } from "@/hooks/use-toast";
import { personalAgent, UserProfile } from "@/services/personalAgent"; import { userProfileService, UserProfile } from "@/services/userProfile";
import { GlobalFunctions, TransferAreaProperties } from "@/GlobalFunctions"; import { GlobalFunctions, TransferAreaProperties } from "@/GlobalFunctions";
const formatPhoneNumber = (value: string) => { const formatPhoneNumber = (value: string) => {
@@ -54,15 +54,20 @@ const MeuPerfil = () => {
// Verifica se há token no sessionStorage primeiro // Verifica se há token no sessionStorage primeiro
const jsonUsuario = sessionStorage.getItem('usuarioLogado'); const jsonUsuario = sessionStorage.getItem('usuarioLogado');
if (!jsonUsuario) { if (!jsonUsuario) {
console.log("MeuPerfil: Não há token no sessionStorage");
window.location.replace(import.meta.env.VITE_BASE_URL_HGTX_CORE || 'https://core.hgtx.com.br'); window.location.replace(import.meta.env.VITE_BASE_URL_HGTX_CORE || 'https://core.hgtx.com.br');
return; return;
} }
console.log("MeuPerfil: Token encontrado no sessionStorage");
// Tenta obter o email do usuário logado // Tenta obter o email do usuário logado
let userEmail = GlobalFunctions.getUsuarioLogado().email; let userEmail = GlobalFunctions.getUsuarioLogado().email;
console.log("MeuPerfil: Email obtido:", userEmail);
// Se não tem email, tenta fazer refresh do token // Se não tem email, tenta fazer refresh do token
if (!userEmail) { if (!userEmail) {
console.log("MeuPerfil: Email vazio, tentando fazer refresh do token");
try { try {
await GlobalFunctions.getToken(); await GlobalFunctions.getToken();
// Aguarda um pouco para o refresh ser processado (se necessário) // Aguarda um pouco para o refresh ser processado (se necessário)
@@ -70,6 +75,7 @@ const MeuPerfil = () => {
// Verifica novamente após refresh // Verifica novamente após refresh
userEmail = GlobalFunctions.getUsuarioLogado().email; userEmail = GlobalFunctions.getUsuarioLogado().email;
console.log("MeuPerfil: Email após refresh:", userEmail);
} catch (error) { } catch (error) {
console.error("MeuPerfil: Erro ao obter token:", error); console.error("MeuPerfil: Erro ao obter token:", error);
} }
@@ -79,17 +85,24 @@ const MeuPerfil = () => {
// Se o token existe mas não tem email, pode ser problema na decodificação // Se o token existe mas não tem email, pode ser problema na decodificação
if (!userEmail) { if (!userEmail) {
const usuarioData = GlobalFunctions.getUsuarioLogado(); const usuarioData = GlobalFunctions.getUsuarioLogado();
console.log("MeuPerfil: Dados do usuário após tentativas:", usuarioData);
// Se não tem email mas tem token no sessionStorage, tenta carregar mesmo assim // Se não tem email mas tem token no sessionStorage, tenta carregar mesmo assim
// O loadUserProfile vai tratar o erro adequadamente se o email for necessário // O loadUserProfile vai tratar o erro adequadamente se o email for necessário
// Só redireciona se o token estiver completamente inválido (sem UID e sem EID) // Só redireciona se o token estiver completamente inválido (sem UID e sem EID)
if (!usuarioData.email && usuarioData.UID === "0" && usuarioData.EID === "0") { if (!usuarioData.email && usuarioData.UID === "0" && usuarioData.EID === "0") {
console.log("MeuPerfil: Token completamente inválido (sem UID/EID), redirecionando");
window.location.replace(import.meta.env.VITE_BASE_URL_HGTX_CORE || 'https://core.hgtx.com.br'); window.location.replace(import.meta.env.VITE_BASE_URL_HGTX_CORE || 'https://core.hgtx.com.br');
return; return;
} }
// Se tem token mas não tem email, pode ser que o email esteja em outro campo
// ou o token precisa ser renovado. Tenta carregar o perfil mesmo assim.
console.log("MeuPerfil: Token existe mas email vazio, tentando carregar perfil mesmo assim");
} }
// Se passou na verificação (ou tem token válido), carrega o perfil // Se passou na verificação (ou tem token válido), carrega o perfil
console.log("MeuPerfil: Carregando perfil do usuário");
loadUserProfile(); loadUserProfile();
}; };
@@ -103,11 +116,13 @@ const MeuPerfil = () => {
// Se não tem email, tenta obter do Transfer Area como fallback // Se não tem email, tenta obter do Transfer Area como fallback
if (!userEmail) { if (!userEmail) {
console.log("MeuPerfil: Email não encontrado no token, tentando Transfer Area");
const transferEmail = GlobalFunctions.getTransferProperty( const transferEmail = GlobalFunctions.getTransferProperty(
TransferAreaProperties.UsuarioEmail TransferAreaProperties.UsuarioEmail
); );
if (transferEmail) { if (transferEmail) {
userEmail = transferEmail as string; userEmail = transferEmail as string;
console.log("MeuPerfil: Email do Transfer Area:", userEmail);
} }
} }
@@ -117,6 +132,7 @@ const MeuPerfil = () => {
await GlobalFunctions.getToken(); await GlobalFunctions.getToken();
await new Promise(resolve => setTimeout(resolve, 500)); await new Promise(resolve => setTimeout(resolve, 500));
userEmail = GlobalFunctions.getUsuarioLogado().email; userEmail = GlobalFunctions.getUsuarioLogado().email;
console.log("MeuPerfil: Email após refresh no loadUserProfile:", userEmail);
} catch (error) { } catch (error) {
console.error("MeuPerfil: Erro ao fazer refresh no loadUserProfile:", error); console.error("MeuPerfil: Erro ao fazer refresh no loadUserProfile:", error);
} }
@@ -135,7 +151,7 @@ const MeuPerfil = () => {
} }
setEmail(userEmail); setEmail(userEmail);
const response = await personalAgent.getUserProfile(userEmail); const response = await userProfileService.getUserProfile(userEmail);
if (response.success && response.id) { if (response.success && response.id) {
// Usuário existe // Usuário existe
@@ -187,7 +203,7 @@ const MeuPerfil = () => {
try { try {
setSaving(true); setSaving(true);
const response = await personalAgent.updateUser(userId, { const response = await userProfileService.updateUser(userId, {
nome: nomeCompleto, nome: nomeCompleto,
whatsapp: whatsapp, whatsapp: whatsapp,
followup: receberLembretes, followup: receberLembretes,
@@ -239,7 +255,7 @@ const MeuPerfil = () => {
try { try {
setCreating(true); setCreating(true);
const response = await personalAgent.createUser({ const response = await userProfileService.createUser({
nome: createNome.trim(), nome: createNome.trim(),
email: email, email: email,
whatsapp: createWhatsapp, whatsapp: createWhatsapp,
+100 -9
View File
@@ -1,32 +1,58 @@
/**
* Serviço para integração com API do Asana
*/
/**
* Interface para workspace do Asana (resposta da API)
*/
export interface AsanaWorkspaceResponse { export interface AsanaWorkspaceResponse {
gid: string; gid: string;
resource_type: string; resource_type: string;
name: string; name: string;
} }
/**
* Interface para resposta da API de workspaces
*/
export interface AsanaWorkspacesResponse { export interface AsanaWorkspacesResponse {
data: AsanaWorkspaceResponse[]; data: AsanaWorkspaceResponse[];
} }
/**
* Interface para workspace formatado (usado no componente)
*/
export interface AsanaWorkspace { export interface AsanaWorkspace {
id: string; id: string;
name: string; name: string;
} }
/**
* Interface para usuário do Asana (resposta da API)
*/
export interface AsanaUserResponse { export interface AsanaUserResponse {
gid: string; gid: string;
name: string; name: string;
resource_type: string; resource_type: string;
} }
/**
* Interface para resposta da API de usuários
*/
export interface AsanaUsersResponse { export interface AsanaUsersResponse {
data: AsanaUserResponse[]; data: AsanaUserResponse[];
} }
/**
* Interface para usuário formatado (usado no componente)
*/
export interface AsanaUser { export interface AsanaUser {
id: string; id: string;
name: string; name: string;
} }
/**
* Interface para resposta da API de integração do Asana
*/
export interface AsanaIntegrationResponse { export interface AsanaIntegrationResponse {
success: boolean; success: boolean;
integracao_id?: string; integracao_id?: string;
@@ -38,6 +64,9 @@ export interface AsanaIntegrationResponse {
usuario_asana_nome?: string; usuario_asana_nome?: string;
} }
/**
* Interface para request de criação/atualização de integração
*/
export interface AsanaIntegrationRequest { export interface AsanaIntegrationRequest {
user_id?: string; user_id?: string;
api_key: string; api_key: string;
@@ -47,9 +76,20 @@ export interface AsanaIntegrationRequest {
usuario_asana_nome: string; usuario_asana_nome: string;
} }
/**
* Serviço para integração com Asana
*/
class AsanaService { class AsanaService {
private readonly ASANA_BASE_URL = 'https://app.asana.com/api/1.0'; 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[]> { async getWorkspaces(token: string): Promise<AsanaWorkspace[]> {
if (!token || token.trim().length === 0) { if (!token || token.trim().length === 0) {
throw { throw {
@@ -67,10 +107,12 @@ class AsanaService {
}; };
const response = await axios.get<AsanaWorkspacesResponse>( const response = await axios.get<AsanaWorkspacesResponse>(
`${this.ASANA_BASE_URL}/workspaces`, `${this.BASE_URL}/workspaces`,
{ headers } { 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) => ({ return response.data.data.map((workspace) => ({
id: workspace.gid, id: workspace.gid,
name: workspace.name, name: workspace.name,
@@ -78,6 +120,7 @@ class AsanaService {
} catch (error: unknown) { } catch (error: unknown) {
console.error('Erro ao buscar workspaces do Asana:', error); console.error('Erro ao buscar workspaces do Asana:', error);
// Retorna o erro da API se existir
if (error && typeof error === 'object' && 'response' in error) { if (error && typeof error === 'object' && 'response' in error) {
const axiosError = error as { response?: { data?: any; status?: number } }; const axiosError = error as { response?: { data?: any; status?: number } };
@@ -103,6 +146,13 @@ class AsanaService {
} }
} }
/**
* 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[]> { async getUsers(token: string, workspaceId: string): Promise<AsanaUser[]> {
if (!token || token.trim().length === 0) { if (!token || token.trim().length === 0) {
throw { throw {
@@ -127,7 +177,7 @@ class AsanaService {
}; };
const response = await axios.get<AsanaUsersResponse>( const response = await axios.get<AsanaUsersResponse>(
`${this.ASANA_BASE_URL}/users?workspace=${workspaceId}`, `${this.BASE_URL}/users?workspace=${workspaceId}`,
{ headers } { headers }
); );
@@ -140,6 +190,7 @@ class AsanaService {
} catch (error: unknown) { } catch (error: unknown) {
console.error('Erro ao buscar usuários do Asana:', error); 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) { if (error && typeof error === 'object' && 'response' in error) {
const axiosError = error as { response?: { data?: any; status?: number } }; const axiosError = error as { response?: { data?: any; status?: number } };
@@ -165,6 +216,12 @@ class AsanaService {
} }
} }
/**
* 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> { async getIntegration(userId: string): Promise<AsanaIntegrationResponse | null> {
if (!userId || userId.trim().length === 0) { if (!userId || userId.trim().length === 0) {
throw { throw {
@@ -177,6 +234,7 @@ class AsanaService {
const axios = (await import('axios')).default; const axios = (await import('axios')).default;
const { GlobalFunctions } = await import('@/GlobalFunctions'); const { GlobalFunctions } = await import('@/GlobalFunctions');
// Obtém o token JWT para autenticação
const token = await GlobalFunctions.getToken(); const token = await GlobalFunctions.getToken();
const apiKey = import.meta.env.VITE_API_KEY || ''; const apiKey = import.meta.env.VITE_API_KEY || '';
@@ -185,19 +243,27 @@ class AsanaService {
'accept': 'application/json', 'accept': 'application/json',
}; };
// Adiciona API key (obrigatória)
if (apiKey) { if (apiKey) {
headers['apikey'] = apiKey; headers['apikey'] = apiKey;
} }
// Adiciona token JWT se disponível
if (token) { if (token) {
headers['Authorization'] = `Bearer ${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>( const response = await axios.get<AsanaIntegrationResponse>(
`https://prod-hgtx-intelligence-n8n.hgtx.com.br/webhook/c898beff-84cb-44df-a69c-6eff27ccd7aa/codex/agente-pessoal/integracoes/asana/${userId}`, `${baseUrl}/webhook/${this.INTEGRATION_WEBHOOK_ID}/codex/agente-pessoal/integracoes/asana/${userId}`,
{ headers } { headers }
); );
// Se success é false, retorna null (não tem integração)
if (!response.data.success) { if (!response.data.success) {
return null; return null;
} }
@@ -214,10 +280,17 @@ class AsanaService {
} }
} }
// Para outros erros, retorna null silenciosamente (não quebra a aplicação)
return null; 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> { async createIntegration(request: AsanaIntegrationRequest): Promise<AsanaIntegrationResponse> {
if (!request.user_id || !request.api_key || !request.workspace_gid || !request.usuario_asana_gid) { if (!request.user_id || !request.api_key || !request.workspace_gid || !request.usuario_asana_gid) {
throw { throw {
@@ -249,8 +322,13 @@ class AsanaService {
headers['Authorization'] = `Bearer ${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>( const response = await axios.post<AsanaIntegrationResponse>(
`https://prod-hgtx-intelligence-n8n.hgtx.com.br/webhook/codex/agente-pessoal/integracoes/asana`, `${baseUrl}/webhook/codex/agente-pessoal/integracoes/asana`,
request, request,
{ headers } { headers }
); );
@@ -279,6 +357,13 @@ class AsanaService {
} }
} }
/**
* 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> { async updateIntegration(integracaoId: string, request: Omit<AsanaIntegrationRequest, 'user_id'>): Promise<AsanaIntegrationResponse> {
if (!integracaoId || !request.api_key || !request.workspace_gid || !request.usuario_asana_gid) { if (!integracaoId || !request.api_key || !request.workspace_gid || !request.usuario_asana_gid) {
throw { throw {
@@ -291,6 +376,7 @@ class AsanaService {
const axios = (await import('axios')).default; const axios = (await import('axios')).default;
const { GlobalFunctions } = await import('@/GlobalFunctions'); const { GlobalFunctions } = await import('@/GlobalFunctions');
// Obtém o token JWT para autenticação
const token = await GlobalFunctions.getToken(); const token = await GlobalFunctions.getToken();
const apiKey = import.meta.env.VITE_API_KEY || ''; const apiKey = import.meta.env.VITE_API_KEY || '';
@@ -299,18 +385,23 @@ class AsanaService {
'accept': 'application/json', 'accept': 'application/json',
}; };
// Adiciona API key (obrigatória)
if (apiKey) { if (apiKey) {
headers['apikey'] = apiKey; headers['apikey'] = apiKey;
} }
// Adiciona token JWT se disponível
if (token) { if (token) {
headers['Authorization'] = `Bearer ${token}`; headers['Authorization'] = `Bearer ${token}`;
} }
const url = `https://prod-hgtx-intelligence-n8n.hgtx.com.br/webhook/c898beff-84cb-44df-a69c-6eff27ccd7aa/codex/agente-pessoal/integracoes/asana/${integracaoId}`; // Em desenvolvimento, usa proxy do Vite para evitar CORS
const baseUrl = import.meta.env.DEV
const response = await axios.post<AsanaIntegrationResponse>( ? '/api/intelligence'
url, : this.INTEGRATION_BASE_URL;
const response = await axios.put<AsanaIntegrationResponse>(
`${baseUrl}/webhook/${this.INTEGRATION_WEBHOOK_ID}/codex/agente-pessoal/integracoes/asana/${integracaoId}`,
request, request,
{ headers } { headers }
); );
+2 -2
View File
@@ -6,7 +6,7 @@ export { apiService } from './api';
export { transcriptionService } from './transcription'; export { transcriptionService } from './transcription';
export { audioGenerationService, VOICE_OPTIONS } from './audioGeneration'; export { audioGenerationService, VOICE_OPTIONS } from './audioGeneration';
export { imageGenerationService, IMAGE_SIZE_OPTIONS } from './imageGeneration'; export { imageGenerationService, IMAGE_SIZE_OPTIONS } from './imageGeneration';
export { personalAgent } from './personalAgent'; export { userProfileService } from './userProfile';
export { asanaService } from './asana'; export { asanaService } from './asana';
export type { TranscriptionResponse, TranscriptionRequest } from './transcription'; export type { TranscriptionResponse, TranscriptionRequest } from './transcription';
export type { AudioGenerationResponse, AudioGenerationRequest, VoiceType } from './audioGeneration'; export type { AudioGenerationResponse, AudioGenerationRequest, VoiceType } from './audioGeneration';
@@ -20,7 +20,7 @@ export type {
ExpensesResponse, ExpensesResponse,
ExpensesFilters, ExpensesFilters,
ExpenseCategory ExpenseCategory
} from './personalAgent'; } from './userProfile';
export type { export type {
AsanaWorkspace, AsanaWorkspace,
AsanaWorkspaceResponse, AsanaWorkspaceResponse,
@@ -1,4 +1,9 @@
import { apiService } from './api';
import { GlobalFunctions } from '@/GlobalFunctions'; import { GlobalFunctions } from '@/GlobalFunctions';
/**
* Interfaces para o serviço de perfil do usuário
*/
export interface UserProfile { export interface UserProfile {
success: boolean; success: boolean;
id?: string; id?: string;
@@ -22,6 +27,9 @@ export interface UpdateUserRequest {
followup: boolean; followup: boolean;
} }
/**
* Interface para indicadores financeiros
*/
export interface FinancialIndicators { export interface FinancialIndicators {
success: boolean; success: boolean;
id: string; id: string;
@@ -33,6 +41,9 @@ export interface FinancialIndicators {
message?: string; message?: string;
} }
/**
* Interface para despesa individual da API
*/
export interface ExpenseItem { export interface ExpenseItem {
id: string; id: string;
data_hora: string; data_hora: string;
@@ -45,6 +56,9 @@ export interface ExpenseItem {
usuario_email: string; usuario_email: string;
} }
/**
* Interface para resposta da API de despesas
*/
export interface ExpensesResponse { export interface ExpensesResponse {
success: boolean; success: boolean;
total_registros: number; total_registros: number;
@@ -54,6 +68,9 @@ export interface ExpensesResponse {
data: ExpenseItem[]; data: ExpenseItem[];
} }
/**
* Interface para filtros de busca de despesas
*/
export interface ExpensesFilters { export interface ExpensesFilters {
page?: number; page?: number;
per_page?: number; per_page?: number;
@@ -63,6 +80,9 @@ export interface ExpensesFilters {
data_final?: string; data_final?: string;
} }
/**
* Interface para categoria de despesas
*/
export interface ExpenseCategory { export interface ExpenseCategory {
id: number; id: number;
nome: string; nome: string;
@@ -71,8 +91,29 @@ export interface ExpenseCategory {
atualizado_em: string; atualizado_em: string;
} }
class PersonalAgent { /**
* 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> { async getUserProfile(userEmail?: string): Promise<UserProfile> {
const email = userEmail || GlobalFunctions.getUsuarioLogado().email; const email = userEmail || GlobalFunctions.getUsuarioLogado().email;
@@ -84,30 +125,36 @@ class PersonalAgent {
} }
try { try {
// Usa axios diretamente pois a URL é absoluta e diferente do baseURL
const axios = (await import('axios')).default; const axios = (await import('axios')).default;
// Obtém o token JWT para autenticação
const token = await GlobalFunctions.getToken(); const token = await GlobalFunctions.getToken();
const apiKey = import.meta.env.VITE_API_KEY || ''; const apiKey = import.meta.env.VITE_API_KEY || '';
// Prepara headers de autenticação
const headers: Record<string, string> = { const headers: Record<string, string> = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}; };
// Adiciona API key (obrigatória)
if (apiKey) { if (apiKey) {
headers['apikey'] = apiKey; headers['apikey'] = apiKey;
} }
// Adiciona token JWT se disponível
if (token) { if (token) {
headers['Authorization'] = `Bearer ${token}`; headers['Authorization'] = `Bearer ${token}`;
} }
const response = await axios.get<UserProfile>( const response = await axios.get<UserProfile>(
`https://prod-hgtx-intelligence-n8n.hgtx.com.br/webhook/2299acaf-70ee-47e3-a6fc-56dcc678d651/codex/agente-pessoal/user/${email}`, `${this.GET_USER_ENDPOINT}/${email}`,
{ headers } { headers }
); );
return response.data; return response.data;
} catch (error: any) { } catch (error: any) {
// Se o usuário não existir, retorna o erro da API
if (error.response?.data) { if (error.response?.data) {
return error.response.data; return error.response.data;
} }
@@ -120,8 +167,14 @@ class PersonalAgent {
} }
} }
/**
* 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> { async createUser(request: CreateUserRequest): Promise<UserProfile> {
// Validações
if (!request.nome || request.nome.trim().length === 0) { if (!request.nome || request.nome.trim().length === 0) {
throw { throw {
success: false, success: false,
@@ -143,30 +196,48 @@ class PersonalAgent {
}; };
} }
// Remove formatação do WhatsApp (apenas números)
const whatsappNumbers = request.whatsapp.replace(/\D/g, ''); const whatsappNumbers = request.whatsapp.replace(/\D/g, '');
try { try {
const axios = (await import('axios')).default; const axios = (await import('axios')).default;
// Obtém o token JWT para autenticação
const token = await GlobalFunctions.getToken(); const token = await GlobalFunctions.getToken();
const apiKey = import.meta.env.VITE_API_KEY || ''; 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> = { const headers: Record<string, string> = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}; };
// Adiciona API key (obrigatória)
if (apiKey) { if (apiKey) {
headers['apikey'] = apiKey; headers['apikey'] = apiKey;
} else { } else {
console.warn('API_KEY não configurada'); console.warn('VITE_API_KEY não configurada');
} }
// Adiciona token JWT se disponível (pode ser necessário)
if (token) { if (token) {
headers['Authorization'] = `Bearer ${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>( const response = await axios.post<UserProfile>(
'https://prod-hgtx-intelligence-n8n.hgtx.com.br/webhook/codex/agente-pessoal/user', this.CREATE_USER_ENDPOINT,
{ {
nome: request.nome.trim(), nome: request.nome.trim(),
email: request.email.trim(), email: request.email.trim(),
@@ -178,6 +249,7 @@ class PersonalAgent {
return response.data; return response.data;
} catch (error: any) { } catch (error: any) {
// Retorna o erro da API se existir
if (error.response?.data) { if (error.response?.data) {
throw error.response.data; throw error.response.data;
} }
@@ -190,8 +262,15 @@ class PersonalAgent {
} }
} }
/**
* 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> { async updateUser(userId: string, request: UpdateUserRequest): Promise<UserProfile> {
// Validações
if (!userId || userId.trim().length === 0) { if (!userId || userId.trim().length === 0) {
throw { throw {
success: false, success: false,
@@ -213,28 +292,47 @@ class PersonalAgent {
}; };
} }
// Remove formatação do WhatsApp (apenas números)
const whatsappNumbers = request.whatsapp.replace(/\D/g, ''); const whatsappNumbers = request.whatsapp.replace(/\D/g, '');
try { try {
const axios = (await import('axios')).default; const axios = (await import('axios')).default;
// Obtém o token JWT para autenticação
const token = await GlobalFunctions.getToken(); const token = await GlobalFunctions.getToken();
const apiKey = import.meta.env.VITE_API_KEY || ''; const apiKey = import.meta.env.VITE_API_KEY || '';
// Prepara headers de autenticação
const headers: Record<string, string> = { const headers: Record<string, string> = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}; };
// Adiciona API key (obrigatória)
if (apiKey) { if (apiKey) {
headers['apikey'] = apiKey; headers['apikey'] = apiKey;
} }
// Adiciona token JWT se disponível
if (token) { if (token) {
headers['Authorization'] = `Bearer ${token}`; headers['Authorization'] = `Bearer ${token}`;
} }
const response = await axios.post<UserProfile>( console.log('Atualizando usuário:', {
`https://prod-hgtx-intelligence-n8n.hgtx.com.br/webhook/2299acaf-70ee-47e3-a6fc-56dcc678d651/codex/agente-pessoal/user/${userId}`, 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(), nome: request.nome.trim(),
whatsapp: whatsappNumbers, whatsapp: whatsappNumbers,
@@ -245,6 +343,7 @@ class PersonalAgent {
return response.data; return response.data;
} catch (error: any) { } catch (error: any) {
// Retorna o erro da API se existir
if (error.response?.data) { if (error.response?.data) {
throw error.response.data; throw error.response.data;
} }
@@ -257,6 +356,12 @@ class PersonalAgent {
} }
} }
/**
* 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> { async getFinancialIndicators(userEmail?: string): Promise<FinancialIndicators> {
const email = userEmail || GlobalFunctions.getUsuarioLogado().email; const email = userEmail || GlobalFunctions.getUsuarioLogado().email;
@@ -269,29 +374,34 @@ class PersonalAgent {
try { try {
const axios = (await import('axios')).default; const axios = (await import('axios')).default;
// Obtém o token JWT para autenticação
const token = await GlobalFunctions.getToken(); const token = await GlobalFunctions.getToken();
const apiKey = import.meta.env.VITE_API_KEY || ''; const apiKey = import.meta.env.VITE_API_KEY || '';
// Prepara headers de autenticação
const headers: Record<string, string> = { const headers: Record<string, string> = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}; };
// Adiciona API key (obrigatória)
if (apiKey) { if (apiKey) {
headers['apikey'] = apiKey; headers['apikey'] = apiKey;
} }
// Adiciona token JWT se disponível
if (token) { if (token) {
headers['Authorization'] = `Bearer ${token}`; headers['Authorization'] = `Bearer ${token}`;
} }
const response = await axios.get<FinancialIndicators>( const response = await axios.get<FinancialIndicators>(
`https://prod-hgtx-intelligence-n8n.hgtx.com.br/webhook/2299acaf-70ee-47e3-a6fc-56dcc678d651/codex/agente-pessoal/financas/indicadores/${email}`, `${this.GET_FINANCIAL_INDICATORS_ENDPOINT}/${email}`,
{ headers } { headers }
); );
return response.data; return response.data;
} catch (error: unknown) { } catch (error: unknown) {
// Retorna o erro da API se existir
if (error && typeof error === 'object' && 'response' in error) { if (error && typeof error === 'object' && 'response' in error) {
const axiosError = error as { response?: { data?: FinancialIndicators } }; const axiosError = error as { response?: { data?: FinancialIndicators } };
if (axiosError.response?.data) { if (axiosError.response?.data) {
@@ -302,13 +412,20 @@ class PersonalAgent {
throw { throw {
success: false, success: false,
message: error instanceof Error ? error.message : 'Erro ao buscar indicadores financeiros', message: error instanceof Error ? error.message : 'Erro ao buscar indicadores financeiros',
status: error && typeof error === 'object' && 'response' in error status: error && typeof error === 'object' && 'response' in error
? (error as { response?: { status?: number } }).response?.status ? (error as { response?: { status?: number } }).response?.status
: undefined, : 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> { async getExpenses(userEmail?: string, filters?: ExpensesFilters): Promise<ExpensesResponse> {
const email = userEmail || GlobalFunctions.getUsuarioLogado().email; const email = userEmail || GlobalFunctions.getUsuarioLogado().email;
@@ -321,18 +438,22 @@ class PersonalAgent {
try { try {
const axios = (await import('axios')).default; const axios = (await import('axios')).default;
// Obtém o token JWT para autenticação
const token = await GlobalFunctions.getToken(); const token = await GlobalFunctions.getToken();
const apiKey = import.meta.env.VITE_API_KEY || ''; const apiKey = import.meta.env.VITE_API_KEY || '';
// Prepara headers de autenticação
const headers: Record<string, string> = { const headers: Record<string, string> = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}; };
// Adiciona API key (obrigatória)
if (apiKey) { if (apiKey) {
headers['apikey'] = apiKey; headers['apikey'] = apiKey;
} }
// Adiciona token JWT se disponível
if (token) { if (token) {
headers['Authorization'] = `Bearer ${token}`; headers['Authorization'] = `Bearer ${token}`;
} }
@@ -347,13 +468,14 @@ class PersonalAgent {
if (filters?.data_final) params.append('data_final', filters.data_final); if (filters?.data_final) params.append('data_final', filters.data_final);
const queryString = params.toString(); const queryString = params.toString();
const url = `https://prod-hgtx-intelligence-n8n.hgtx.com.br/webhook/2299acaf-70ee-47e3-a6fc-56dcc678d651/codex/agente-pessoal/financas/${email}${queryString ? `?${queryString}` : ''}`; const url = `${this.GET_EXPENSES_ENDPOINT}/${email}${queryString ? `?${queryString}` : ''}`;
const response = await axios.get<ExpensesResponse[]>(url, { headers }); 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) { if (Array.isArray(response.data) && response.data.length > 0) {
const expensesResponse = response.data[0]; const expensesResponse = response.data[0];
// Valida e limpa o array de dados, removendo objetos vazios // Valida e limpa o array de dados, removendo objetos vazios
if (expensesResponse.data && Array.isArray(expensesResponse.data)) { if (expensesResponse.data && Array.isArray(expensesResponse.data)) {
// Filtra objetos vazios (sem propriedades ou apenas com propriedades vazias) // Filtra objetos vazios (sem propriedades ou apenas com propriedades vazias)
@@ -361,7 +483,7 @@ class PersonalAgent {
// Verifica se o objeto tem pelo menos uma propriedade válida // Verifica se o objeto tem pelo menos uma propriedade válida
return item && typeof item === 'object' && Object.keys(item).length > 0 && item.id; 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 // Se após filtrar não há dados, garante que data seja um array vazio
if (expensesResponse.data.length === 0) { if (expensesResponse.data.length === 0) {
expensesResponse.data = []; expensesResponse.data = [];
@@ -374,7 +496,7 @@ class PersonalAgent {
expensesResponse.total_registros = 0; expensesResponse.total_registros = 0;
expensesResponse.total_paginas = 0; expensesResponse.total_paginas = 0;
} }
return expensesResponse; return expensesResponse;
} }
@@ -399,33 +521,42 @@ class PersonalAgent {
throw { throw {
success: false, success: false,
message: error instanceof Error ? error.message : 'Erro ao buscar despesas', message: error instanceof Error ? error.message : 'Erro ao buscar despesas',
status: error && typeof error === 'object' && 'response' in error status: error && typeof error === 'object' && 'response' in error
? (error as { response?: { status?: number } }).response?.status ? (error as { response?: { status?: number } }).response?.status
: undefined, : undefined,
}; };
} }
} }
/**
* Obtém a lista de categorias de despesas
*
* @returns Promise com a lista de categorias
*/
async getCategories(): Promise<ExpenseCategory[]> { async getCategories(): Promise<ExpenseCategory[]> {
try { try {
const axios = (await import('axios')).default; const axios = (await import('axios')).default;
// Obtém o token JWT para autenticação
const token = await GlobalFunctions.getToken(); const token = await GlobalFunctions.getToken();
const apiKey = import.meta.env.VITE_API_KEY || ''; const apiKey = import.meta.env.VITE_API_KEY || '';
// Prepara headers de autenticação
const headers: Record<string, string> = { const headers: Record<string, string> = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}; };
// Adiciona API key (obrigatória)
if (apiKey) { if (apiKey) {
headers['apikey'] = apiKey; headers['apikey'] = apiKey;
} }
// Adiciona token JWT se disponível
if (token) { if (token) {
headers['Authorization'] = `Bearer ${token}`; headers['Authorization'] = `Bearer ${token}`;
} }
const response = await axios.get<ExpenseCategory[]>('https://prod-hgtx-intelligence-n8n.hgtx.com.br/webhook/codex/agente-pessoal/categorias', { headers }); const response = await axios.get<ExpenseCategory[]>(this.GET_CATEGORIES_ENDPOINT, { headers });
// A API retorna um array de categorias // A API retorna um array de categorias
if (Array.isArray(response.data)) { if (Array.isArray(response.data)) {
@@ -435,10 +566,12 @@ class PersonalAgent {
return []; return [];
} catch (error: unknown) { } catch (error: unknown) {
console.error('Erro ao buscar categorias:', error); console.error('Erro ao buscar categorias:', error);
// Retorna array vazio em caso de erro para não quebrar a aplicação
return []; return [];
} }
} }
} }
export const personalAgent = new PersonalAgent(); // Exporta instância única (Singleton)
export const userProfileService = new UserProfileService();