989 lines
27 KiB
TypeScript
989 lines
27 KiB
TypeScript
import { GlobalFunctions, TransferAreaProperties } from '@/GlobalFunctions';
|
|
import { apiService } from './api';
|
|
|
|
/**
|
|
* Interface para a resposta da API de chat
|
|
*/
|
|
export interface ChatResponse {
|
|
success: boolean;
|
|
response: string; // Mensagem da IA
|
|
chat_id: string; // ID do chat retornado pela API (importante para manter contexto)
|
|
}
|
|
|
|
/**
|
|
* Interface para os dados necessários para enviar mensagem
|
|
*/
|
|
export interface ChatMessageRequest {
|
|
chatId: string;
|
|
message: string;
|
|
modelId: string;
|
|
personalidade?: string;
|
|
anexos?: File[]; // Array de até 5 arquivos
|
|
userEmail?: string;
|
|
estabelecimentoId?: number;
|
|
}
|
|
|
|
/**
|
|
* Interface para armazenamento local de conversas
|
|
* Preparando para futura integração com banco de dados
|
|
*/
|
|
export interface StoredChat {
|
|
id: string;
|
|
title: string;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
model: string;
|
|
systemPrompt: string;
|
|
messages: Array<{
|
|
id: string;
|
|
role: 'user' | 'assistant';
|
|
content: string;
|
|
model?: string;
|
|
timestamp: Date;
|
|
attachments?: Array<{
|
|
name: string;
|
|
type: string;
|
|
size: number;
|
|
}>;
|
|
}>;
|
|
}
|
|
|
|
/**
|
|
* Interface para armazenamento de pastas
|
|
* Preparando para futura integração com banco de dados
|
|
*/
|
|
export interface StoredFolder {
|
|
id: string;
|
|
name: string;
|
|
createdAt: Date;
|
|
chatIds: string[];
|
|
}
|
|
|
|
/**
|
|
* Interface para Folder no banco de dados
|
|
*/
|
|
export interface FolderRecord {
|
|
id: string;
|
|
name: string;
|
|
created_at: string;
|
|
updated_at: string;
|
|
}
|
|
|
|
/**
|
|
* Interface para Chat no banco de dados
|
|
*/
|
|
export interface ChatRecord {
|
|
id: string;
|
|
title: string;
|
|
model_id: number;
|
|
folder_id: string | null;
|
|
created_at: string;
|
|
updated_at: string;
|
|
personalidade: string; // Personalidade/prompt do sistema salvo no banco
|
|
estabelecimento_id: number;
|
|
}
|
|
|
|
/**
|
|
* Interface para resposta do GET de chats e folders
|
|
*/
|
|
export interface GetChatsAndFoldersResponse {
|
|
chats: ChatRecord[];
|
|
folders: FolderRecord[];
|
|
}
|
|
|
|
/**
|
|
* Interface para Message no banco de dados
|
|
*/
|
|
export interface MessageRecord {
|
|
id: string;
|
|
chat_id: string;
|
|
role: 'user' | 'assistant';
|
|
content: string;
|
|
model_id: number;
|
|
model_name?: string; // Nome do modelo retornado pela API
|
|
has_attachments: number;
|
|
input_tokens: number;
|
|
output_tokens: number;
|
|
total_tokens: number;
|
|
cost_usd: string;
|
|
created_at: string;
|
|
}
|
|
|
|
/**
|
|
* Serviço de chat com IA
|
|
*/
|
|
class ChatService {
|
|
private readonly CHAT_ENDPOINT = '/webhook/codex/message';
|
|
private readonly STORAGE_KEY_CHATS = 'hgtx_chats';
|
|
private readonly STORAGE_KEY_FOLDERS = 'hgtx_folders';
|
|
|
|
// Endpoints para folders e chats
|
|
private readonly POST_FOLDER_ENDPOINT = '/webhook/codex/post_folders';
|
|
private readonly GET_CHATS_FOLDERS_ENDPOINT = '/webhook/fd073ec0-81ab-4681-8acd-b0da71f6ad30/codex/get_chat_folders';
|
|
private readonly PUT_CHAT_IN_FOLDER_ENDPOINT = '/webhook/fd073ec0-81ab-4681-8acd-b0da71f6ad30/codex/insert_chat_in_folder';
|
|
private readonly DELETE_FOLDER_ENDPOINT = '/webhook/fd073ec0-81ab-4681-8acd-b0da71f6ad30/codex/delete_chat_folder';
|
|
private readonly DELETE_CHAT_ENDPOINT = '/webhook/fd073ec0-81ab-4681-8acd-b0da71f6ad30/codex/delete_chat_messages';
|
|
private readonly GET_CHAT_MESSAGES_ENDPOINT = '/webhook/fd073ec0-81ab-4681-8acd-b0da71f6ad30/codex/get_chat_messages';
|
|
|
|
// Formatos de arquivo permitidos (atualmente)
|
|
private readonly ALLOWED_FILE_TYPES = {
|
|
// Formatos ativos
|
|
'application/pdf': { ext: '.pdf', label: 'PDF' },
|
|
'image/png': { ext: '.png', label: 'PNG' },
|
|
'image/jpeg': { ext: '.jpg, .jpeg', label: 'JPEG' },
|
|
'image/webp': { ext: '.webp', label: 'WebP' },
|
|
|
|
// Formatos futuros (desabilitados por enquanto)
|
|
// 'text/csv': { ext: '.csv', label: 'CSV' },
|
|
// 'text/plain': { ext: '.txt', label: 'TXT' },
|
|
// 'application/json': { ext: '.json', label: 'JSON' },
|
|
// 'application/vnd.ms-excel': { ext: '.xls', label: 'XLS' },
|
|
// 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': { ext: '.xlsx', label: 'XLSX' },
|
|
};
|
|
|
|
// Número máximo de anexos permitidos
|
|
private readonly MAX_ATTACHMENTS = 5;
|
|
|
|
/**
|
|
* Gera um chat_id único e seguro usando hash
|
|
* Formato: timestamp + random + hash
|
|
*
|
|
* NOTA: Esta função está mantida para compatibilidade e uso no localStorage,
|
|
* mas para comunicação com a API, o fluxo correto é:
|
|
* 1. Enviar chat_id: "0" na primeira mensagem
|
|
* 2. API retorna o chat_id real
|
|
* 3. Usar o chat_id retornado nas próximas mensagens
|
|
*
|
|
* @returns string - ID único para o chat (uso local)
|
|
*/
|
|
generateChatId(): string {
|
|
const timestamp = Date.now().toString(36);
|
|
const randomPart = Math.random().toString(36).substring(2, 15);
|
|
const randomPart2 = Math.random().toString(36).substring(2, 15);
|
|
|
|
// Combina timestamp e partes aleatórias para criar ID único
|
|
const chatId = `chat_${timestamp}_${randomPart}${randomPart2}`;
|
|
|
|
return chatId;
|
|
}
|
|
|
|
/**
|
|
* Envia mensagem para a API de chat
|
|
*
|
|
* @param request - Dados da requisição (mensagem, modelo, anexos, etc)
|
|
* @returns Promise com a resposta da API
|
|
*/
|
|
async sendMessage(request: ChatMessageRequest): Promise<ChatResponse> {
|
|
const {
|
|
chatId,
|
|
message,
|
|
modelId,
|
|
personalidade,
|
|
anexos,
|
|
userEmail,
|
|
estabelecimentoId
|
|
} = request;
|
|
|
|
// Usa valores do .env se não forem fornecidos
|
|
const email = GlobalFunctions.getTransferProperty(TransferAreaProperties.UsuarioEmail);//userEmail || import.meta.env.VITE_USER_EMAIL || '';
|
|
const estabId = GlobalFunctions.getTransferProperty(TransferAreaProperties.EstabelecimentoCodigo);//estabelecimentoId || import.meta.env.VITE_ESTABELECIMENTO_ID || '';
|
|
|
|
// Validações
|
|
if (!chatId) {
|
|
throw {
|
|
success: false,
|
|
message: 'ID do chat é obrigatório',
|
|
};
|
|
}
|
|
|
|
if (!message || message.trim().length === 0) {
|
|
throw {
|
|
success: false,
|
|
message: 'A mensagem não pode estar vazia',
|
|
};
|
|
}
|
|
|
|
if (!modelId) {
|
|
throw {
|
|
success: false,
|
|
message: 'Modelo da IA é obrigatório',
|
|
};
|
|
}
|
|
|
|
// Valida número de anexos
|
|
if (anexos && anexos.length > 5) {
|
|
throw {
|
|
success: false,
|
|
message: 'Máximo de 5 arquivos anexos permitidos',
|
|
};
|
|
}
|
|
|
|
// Cria FormData para envio multipart
|
|
const formData = new FormData();
|
|
formData.append('estabelecimento_id', estabId.toString());
|
|
formData.append('chat_id', chatId);
|
|
formData.append('user_email', email);
|
|
formData.append('model_id', modelId);
|
|
formData.append('message', message);
|
|
|
|
// Adiciona personalidade se fornecida
|
|
if (personalidade && personalidade.trim().length > 0) {
|
|
formData.append('personalidade', personalidade);
|
|
}
|
|
|
|
// Adiciona anexos (máximo 5)
|
|
if (anexos && anexos.length > 0) {
|
|
anexos.forEach((file, index) => {
|
|
if (index < 5) {
|
|
formData.append(`anexo${index + 1}`, file);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Log para debug (remover em produção se necessário)
|
|
console.log('Enviando mensagem:', {
|
|
chatId,
|
|
messageLength: message.length,
|
|
modelId,
|
|
hasPersonalidade: !!personalidade,
|
|
attachmentsCount: anexos?.length || 0,
|
|
userEmail: email,
|
|
estabelecimentoId: estabId,
|
|
});
|
|
|
|
try {
|
|
// Faz a requisição usando o serviço de API
|
|
const response = await apiService.postFormData<ChatResponse>(
|
|
this.CHAT_ENDPOINT,
|
|
formData
|
|
);
|
|
|
|
return response.data;
|
|
} catch (error: any) {
|
|
// Trata erros específicos
|
|
console.error('Erro ao enviar mensagem:', error);
|
|
|
|
throw {
|
|
success: false,
|
|
message: error.message || 'Erro ao comunicar com a IA',
|
|
status: error.status,
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Salva ou atualiza um chat no localStorage
|
|
* Preparado para futura migração para banco de dados
|
|
*
|
|
* @param chat - Chat a ser salvo
|
|
*/
|
|
saveChat(chat: StoredChat): void {
|
|
try {
|
|
const chats = this.getAllChats();
|
|
const existingIndex = chats.findIndex(c => c.id === chat.id);
|
|
|
|
if (existingIndex >= 0) {
|
|
chats[existingIndex] = {
|
|
...chat,
|
|
updatedAt: new Date(),
|
|
};
|
|
} else {
|
|
chats.push(chat);
|
|
}
|
|
|
|
localStorage.setItem(this.STORAGE_KEY_CHATS, JSON.stringify(chats));
|
|
} catch (error) {
|
|
console.error('Erro ao salvar chat:', error);
|
|
throw new Error('Não foi possível salvar o chat');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Busca um chat específico por ID
|
|
*
|
|
* @param chatId - ID do chat
|
|
* @returns Chat encontrado ou undefined
|
|
*/
|
|
getChat(chatId: string): StoredChat | undefined {
|
|
const chats = this.getAllChats();
|
|
return chats.find(c => c.id === chatId);
|
|
}
|
|
|
|
/**
|
|
* Retorna todos os chats salvos
|
|
*
|
|
* @returns Array de chats
|
|
*/
|
|
getAllChats(): StoredChat[] {
|
|
try {
|
|
const chatsJson = localStorage.getItem(this.STORAGE_KEY_CHATS);
|
|
if (!chatsJson) return [];
|
|
|
|
const chats = JSON.parse(chatsJson);
|
|
|
|
// Converte strings de data para objetos Date
|
|
return chats.map((chat: any) => ({
|
|
...chat,
|
|
createdAt: new Date(chat.createdAt),
|
|
updatedAt: new Date(chat.updatedAt),
|
|
messages: chat.messages.map((msg: any) => ({
|
|
...msg,
|
|
timestamp: new Date(msg.timestamp),
|
|
})),
|
|
}));
|
|
} catch (error) {
|
|
console.error('Erro ao carregar chats:', error);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deleta um chat do localStorage
|
|
*
|
|
* @param chatId - ID do chat a ser deletado
|
|
*/
|
|
deleteChatLocal(chatId: string): void {
|
|
try {
|
|
const chats = this.getAllChats();
|
|
const filteredChats = chats.filter(c => c.id !== chatId);
|
|
localStorage.setItem(this.STORAGE_KEY_CHATS, JSON.stringify(filteredChats));
|
|
} catch (error) {
|
|
console.error('Erro ao deletar chat:', error);
|
|
throw new Error('Não foi possível deletar o chat');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Salva uma pasta no localStorage
|
|
*
|
|
* @param folder - Pasta a ser salva
|
|
*/
|
|
saveFolder(folder: StoredFolder): void {
|
|
try {
|
|
const folders = this.getAllFolders();
|
|
const existingIndex = folders.findIndex(f => f.id === folder.id);
|
|
|
|
if (existingIndex >= 0) {
|
|
folders[existingIndex] = folder;
|
|
} else {
|
|
folders.push(folder);
|
|
}
|
|
|
|
localStorage.setItem(this.STORAGE_KEY_FOLDERS, JSON.stringify(folders));
|
|
} catch (error) {
|
|
console.error('Erro ao salvar pasta:', error);
|
|
throw new Error('Não foi possível salvar a pasta');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retorna todas as pastas salvas
|
|
*
|
|
* @returns Array de pastas
|
|
*/
|
|
getAllFolders(): StoredFolder[] {
|
|
try {
|
|
const foldersJson = localStorage.getItem(this.STORAGE_KEY_FOLDERS);
|
|
if (!foldersJson) return [];
|
|
|
|
const folders = JSON.parse(foldersJson);
|
|
|
|
// Converte strings de data para objetos Date
|
|
return folders.map((folder: any) => ({
|
|
...folder,
|
|
createdAt: new Date(folder.createdAt),
|
|
}));
|
|
} catch (error) {
|
|
console.error('Erro ao carregar pastas:', error);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deleta uma pasta do localStorage
|
|
*
|
|
* @param folderId - ID da pasta a ser deletada
|
|
*/
|
|
deleteFolderLocal(folderId: string): void {
|
|
try {
|
|
const folders = this.getAllFolders();
|
|
const filteredFolders = folders.filter(f => f.id !== folderId);
|
|
localStorage.setItem(this.STORAGE_KEY_FOLDERS, JSON.stringify(filteredFolders));
|
|
} catch (error) {
|
|
console.error('Erro ao deletar pasta:', error);
|
|
throw new Error('Não foi possível deletar a pasta');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gera título automático para o chat baseado na primeira mensagem
|
|
*
|
|
* @param message - Primeira mensagem do usuário
|
|
* @param maxLength - Comprimento máximo do título
|
|
* @returns Título gerado
|
|
*/
|
|
generateChatTitle(message: string, maxLength: number = 50): string {
|
|
if (!message || message.trim().length === 0) {
|
|
return 'Nova Conversa';
|
|
}
|
|
|
|
const trimmed = message.trim();
|
|
if (trimmed.length <= maxLength) {
|
|
return trimmed;
|
|
}
|
|
|
|
return trimmed.substring(0, maxLength) + '...';
|
|
}
|
|
|
|
/**
|
|
* Limpa todas as conversas (usar com cuidado)
|
|
*/
|
|
clearAllChats(): void {
|
|
localStorage.removeItem(this.STORAGE_KEY_CHATS);
|
|
}
|
|
|
|
/**
|
|
* Limpa todas as pastas (usar com cuidado)
|
|
*/
|
|
clearAllFolders(): void {
|
|
localStorage.removeItem(this.STORAGE_KEY_FOLDERS);
|
|
}
|
|
|
|
/**
|
|
* Valida anexos antes de enviar para a API
|
|
*
|
|
* @param files - Array de arquivos a serem validados
|
|
* @returns Objeto com resultado da validação
|
|
*/
|
|
validateAttachments(files: File[]): { valid: boolean; error?: string } {
|
|
// Valida número de anexos
|
|
if (files.length > this.MAX_ATTACHMENTS) {
|
|
return {
|
|
valid: false,
|
|
error: `Máximo de ${this.MAX_ATTACHMENTS} anexos permitidos. Você selecionou ${files.length}.`,
|
|
};
|
|
}
|
|
|
|
// Valida cada arquivo
|
|
for (let i = 0; i < files.length; i++) {
|
|
const file = files[i];
|
|
const fileType = file.type;
|
|
const fileName = file.name;
|
|
|
|
// Verifica se o tipo MIME é permitido
|
|
if (!this.ALLOWED_FILE_TYPES[fileType as keyof typeof this.ALLOWED_FILE_TYPES]) {
|
|
// Tenta validar pela extensão também
|
|
const extension = fileName.toLowerCase().substring(fileName.lastIndexOf('.'));
|
|
const isExtensionValid = Object.values(this.ALLOWED_FILE_TYPES).some(
|
|
type => type.ext.includes(extension)
|
|
);
|
|
|
|
if (!isExtensionValid) {
|
|
const allowedFormats = Object.values(this.ALLOWED_FILE_TYPES)
|
|
.map(t => t.label)
|
|
.join(', ');
|
|
|
|
return {
|
|
valid: false,
|
|
error: `Arquivo "${fileName}" não é permitido. Formatos aceitos: ${allowedFormats}`,
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
return { valid: true };
|
|
}
|
|
|
|
/**
|
|
* Retorna lista de formatos de arquivo permitidos
|
|
*
|
|
* @returns Array com informações dos formatos
|
|
*/
|
|
getAllowedFileTypes() {
|
|
return Object.entries(this.ALLOWED_FILE_TYPES).map(([mimeType, info]) => ({
|
|
mimeType,
|
|
...info,
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Retorna string formatada com formatos permitidos (para exibição)
|
|
*
|
|
* @returns String formatada (ex: "PDF, PNG, JPEG, WebP")
|
|
*/
|
|
getAllowedFileTypesLabel(): string {
|
|
return Object.values(this.ALLOWED_FILE_TYPES)
|
|
.map(t => t.label)
|
|
.join(', ');
|
|
}
|
|
|
|
/**
|
|
* Retorna string com extensões para usar no input file accept
|
|
*
|
|
* @returns String formatada (ex: ".pdf,.png,.jpg,.jpeg,.webp")
|
|
*/
|
|
getAllowedFileExtensions(): string {
|
|
return Object.values(this.ALLOWED_FILE_TYPES)
|
|
.map(t => t.ext)
|
|
.join(',');
|
|
}
|
|
|
|
/**
|
|
* Retorna número máximo de anexos permitidos
|
|
*/
|
|
getMaxAttachments(): number {
|
|
return this.MAX_ATTACHMENTS;
|
|
}
|
|
|
|
// ===== MÉTODOS DE INTEGRAÇÃO COM BANCO DE DADOS =====
|
|
|
|
/**
|
|
* Cria uma nova pasta no banco de dados
|
|
*
|
|
* @param name - Nome da pasta
|
|
* @param userEmail - Email do usuário (opcional)
|
|
* @returns Promise com sucesso ou erro
|
|
*/
|
|
async createFolder(name: string, userEmail?: string): Promise<{ success: boolean; folder?: FolderRecord }> {
|
|
const email = userEmail || GlobalFunctions.getTransferProperty(TransferAreaProperties.UsuarioEmail);
|
|
|
|
if (!email) {
|
|
throw {
|
|
success: false,
|
|
message: 'Email do usuário não fornecido',
|
|
};
|
|
}
|
|
|
|
if (!name || name.trim().length === 0) {
|
|
throw {
|
|
success: false,
|
|
message: 'Nome da pasta não pode estar vazio',
|
|
};
|
|
}
|
|
|
|
console.log('Criando pasta:', {
|
|
name,
|
|
userEmail: email,
|
|
});
|
|
|
|
try {
|
|
const response = await apiService.post<Array<{ success: boolean; user_email?: string; name?: string }>>(
|
|
this.POST_FOLDER_ENDPOINT,
|
|
{
|
|
user_email: email,
|
|
name: name.trim(),
|
|
}
|
|
);
|
|
|
|
console.log('Resposta completa do POST:', response);
|
|
console.log('response.data:', response.data);
|
|
|
|
// A API retorna um array com um objeto: [{"success":true, ...}]
|
|
let result: { success: boolean; user_email?: string; name?: string };
|
|
|
|
if (Array.isArray(response.data)) {
|
|
result = response.data[0];
|
|
console.log('API retornou array, usando primeiro elemento:', result);
|
|
} else {
|
|
result = response.data as any;
|
|
console.log('API retornou objeto direto:', result);
|
|
}
|
|
|
|
return {
|
|
success: result.success ?? true,
|
|
};
|
|
} catch (error: any) {
|
|
console.error('Erro ao criar pasta:', error);
|
|
|
|
throw {
|
|
success: false,
|
|
message: error.message || 'Erro ao criar pasta',
|
|
status: error.status,
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Renomeia uma pasta existente
|
|
*
|
|
* @param folderId - ID da pasta a ser renomeada
|
|
* @param newName - Novo nome da pasta
|
|
* @param userEmail - Email do usuário (opcional)
|
|
* @returns Promise com o resultado da operação
|
|
*/
|
|
async renameFolder(folderId: string, newName: string, userEmail?: string): Promise<{ success: boolean; folder?: FolderRecord }> {
|
|
const email = userEmail || GlobalFunctions.getTransferProperty(TransferAreaProperties.UsuarioEmail);
|
|
|
|
if (!email) {
|
|
throw {
|
|
success: false,
|
|
message: 'Email do usuário não fornecido',
|
|
};
|
|
}
|
|
|
|
if (!folderId || folderId.trim().length === 0) {
|
|
throw {
|
|
success: false,
|
|
message: 'ID da pasta não pode estar vazio',
|
|
};
|
|
}
|
|
|
|
if (!newName || newName.trim().length === 0) {
|
|
throw {
|
|
success: false,
|
|
message: 'Nome da pasta não pode estar vazio',
|
|
};
|
|
}
|
|
|
|
console.log('Renomeando pasta:', {
|
|
folderId,
|
|
newName,
|
|
userEmail: email,
|
|
});
|
|
|
|
try {
|
|
const response = await apiService.put<Array<{ success: boolean; user_email?: string; name?: string; id?: string }>>(
|
|
this.POST_FOLDER_ENDPOINT,
|
|
{
|
|
id: folderId,
|
|
user_email: email,
|
|
name: newName.trim(),
|
|
}
|
|
);
|
|
|
|
console.log('Resposta completa do PUT (renomear pasta):', response);
|
|
console.log('response.data:', response.data);
|
|
|
|
// A API retorna um array com um objeto: [{"success":true, "user_email": "...", "name": "...", "id": "..."}]
|
|
let result: { success: boolean; user_email?: string; name?: string; id?: string };
|
|
|
|
if (Array.isArray(response.data)) {
|
|
result = response.data[0];
|
|
console.log('API retornou array, usando primeiro elemento:', result);
|
|
} else {
|
|
result = response.data as any;
|
|
console.log('API retornou objeto direto:', result);
|
|
}
|
|
|
|
return {
|
|
success: result.success ?? true,
|
|
};
|
|
} catch (error: any) {
|
|
console.error('Erro ao renomear pasta:', error);
|
|
|
|
throw {
|
|
success: false,
|
|
message: error.message || 'Erro ao renomear pasta',
|
|
status: error.status,
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Busca todos os chats e pastas do usuário
|
|
*
|
|
* @param userEmail - Email do usuário (opcional)
|
|
* @returns Promise com chats e folders
|
|
*/
|
|
async getChatsAndFolders(userEmail?: string): Promise<GetChatsAndFoldersResponse> {
|
|
const email = userEmail || GlobalFunctions.getTransferProperty(TransferAreaProperties.UsuarioEmail);
|
|
|
|
if (!email) {
|
|
throw {
|
|
success: false,
|
|
message: 'Email do usuário não fornecido',
|
|
};
|
|
}
|
|
|
|
console.log('Buscando chats e pastas:', {
|
|
userEmail: email,
|
|
});
|
|
|
|
try {
|
|
const response = await apiService.get<Array<{ result: GetChatsAndFoldersResponse }>>(
|
|
`${this.GET_CHATS_FOLDERS_ENDPOINT}/${email}`
|
|
);
|
|
|
|
console.log('Resposta completa da API:', response);
|
|
console.log('response.data:', response.data);
|
|
|
|
// A API retorna um array com um objeto "result": [{"result": {"chats": [...], "folders": [...]}}]
|
|
let result: GetChatsAndFoldersResponse;
|
|
|
|
if (Array.isArray(response.data) && response.data.length > 0) {
|
|
result = response.data[0].result;
|
|
console.log('API retornou array com result:', result);
|
|
} else if ((response.data as any).result) {
|
|
result = (response.data as any).result;
|
|
console.log('API retornou objeto com result:', result);
|
|
} else {
|
|
// Fallback: retorna vazio
|
|
console.warn('Estrutura inesperada da resposta');
|
|
result = { chats: [], folders: [] };
|
|
}
|
|
|
|
// Garante que chats e folders são arrays
|
|
return {
|
|
chats: Array.isArray(result.chats) ? result.chats : [],
|
|
folders: Array.isArray(result.folders) ? result.folders : [],
|
|
};
|
|
} catch (error: any) {
|
|
console.error('Erro ao buscar chats e pastas:', error);
|
|
|
|
throw {
|
|
success: false,
|
|
message: error.message || 'Erro ao buscar chats e pastas',
|
|
status: error.status,
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Move um chat para dentro de uma pasta
|
|
*
|
|
* @param chatId - ID do chat
|
|
* @param folderId - ID da pasta
|
|
* @returns Promise com sucesso ou erro
|
|
*/
|
|
async moveChatToFolder(chatId: string, folderId: string): Promise<{ success: boolean }> {
|
|
if (!chatId || !folderId) {
|
|
throw {
|
|
success: false,
|
|
message: 'Chat ID e Folder ID são obrigatórios',
|
|
};
|
|
}
|
|
|
|
console.log('Movendo chat para pasta:', {
|
|
chatId,
|
|
folderId,
|
|
});
|
|
|
|
try {
|
|
const response = await apiService.put<Array<{ success: boolean }>>(
|
|
`${this.PUT_CHAT_IN_FOLDER_ENDPOINT}/${folderId}/${chatId}`
|
|
);
|
|
|
|
console.log('Resposta completa do PUT:', response);
|
|
console.log('response.data:', response.data);
|
|
|
|
// A API retorna um array com um objeto: [{"success":true}]
|
|
let result: { success: boolean };
|
|
|
|
if (Array.isArray(response.data)) {
|
|
result = response.data[0];
|
|
console.log('API retornou array, usando primeiro elemento:', result);
|
|
} else {
|
|
result = response.data;
|
|
console.log('API retornou objeto direto:', result);
|
|
}
|
|
|
|
return {
|
|
success: result.success ?? true,
|
|
};
|
|
} catch (error: any) {
|
|
console.error('Erro ao mover chat para pasta:', error);
|
|
|
|
throw {
|
|
success: false,
|
|
message: error.message || 'Erro ao mover chat para pasta',
|
|
status: error.status,
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deleta uma pasta do banco de dados
|
|
*
|
|
* @param folderId - ID da pasta
|
|
* @param userEmail - Email do usuário (opcional)
|
|
* @returns Promise com sucesso ou erro
|
|
*/
|
|
async deleteFolder(folderId: string, userEmail?: string): Promise<{ success: boolean }> {
|
|
const email = userEmail || GlobalFunctions.getTransferProperty(TransferAreaProperties.UsuarioEmail);
|
|
|
|
if (!email) {
|
|
throw {
|
|
success: false,
|
|
message: 'Email do usuário não fornecido',
|
|
};
|
|
}
|
|
|
|
if (!folderId) {
|
|
throw {
|
|
success: false,
|
|
message: 'ID da pasta não fornecido',
|
|
};
|
|
}
|
|
|
|
console.log('Deletando pasta:', {
|
|
folderId,
|
|
userEmail: email,
|
|
});
|
|
|
|
try {
|
|
const response = await apiService.delete<Array<{ success: boolean }>>(
|
|
`${this.DELETE_FOLDER_ENDPOINT}/${email}/${folderId}`
|
|
);
|
|
|
|
console.log('Resposta completa do DELETE:', response);
|
|
console.log('response.data:', response.data);
|
|
|
|
// A API retorna um array com um objeto: [{"success":true}]
|
|
let result: { success: boolean };
|
|
|
|
if (Array.isArray(response.data)) {
|
|
result = response.data[0];
|
|
console.log('API retornou array, usando primeiro elemento:', result);
|
|
} else {
|
|
result = response.data;
|
|
console.log('API retornou objeto direto:', result);
|
|
}
|
|
|
|
return {
|
|
success: result.success ?? true,
|
|
};
|
|
} catch (error: any) {
|
|
console.error('Erro ao deletar pasta:', error);
|
|
|
|
throw {
|
|
success: false,
|
|
message: error.message || 'Erro ao deletar pasta',
|
|
status: error.status,
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deleta um chat do banco de dados
|
|
*
|
|
* @param chatId - ID do chat
|
|
* @param userEmail - Email do usuário (opcional)
|
|
* @returns Promise com sucesso ou erro
|
|
*/
|
|
async deleteChat(chatId: string, userEmail?: string): Promise<{ success: boolean }> {
|
|
const email = userEmail || GlobalFunctions.getTransferProperty(TransferAreaProperties.UsuarioEmail);
|
|
|
|
if (!email) {
|
|
throw {
|
|
success: false,
|
|
message: 'Email do usuário não fornecido',
|
|
};
|
|
}
|
|
|
|
if (!chatId) {
|
|
throw {
|
|
success: false,
|
|
message: 'ID do chat não fornecido',
|
|
};
|
|
}
|
|
|
|
console.log('Deletando chat:', {
|
|
chatId,
|
|
userEmail: email,
|
|
});
|
|
|
|
try {
|
|
const response = await apiService.delete<Array<{ success: boolean }>>(
|
|
`${this.DELETE_CHAT_ENDPOINT}/${email}/${chatId}`
|
|
);
|
|
|
|
console.log('Resposta completa do DELETE:', response);
|
|
console.log('response.data:', response.data);
|
|
|
|
// A API retorna um array com um objeto: [{"success":true}]
|
|
let result: { success: boolean };
|
|
|
|
if (Array.isArray(response.data)) {
|
|
result = response.data[0];
|
|
console.log('API retornou array, usando primeiro elemento:', result);
|
|
} else {
|
|
result = response.data;
|
|
console.log('API retornou objeto direto:', result);
|
|
}
|
|
|
|
return {
|
|
success: result.success ?? true,
|
|
};
|
|
} catch (error: any) {
|
|
console.error('Erro ao deletar chat:', error);
|
|
|
|
throw {
|
|
success: false,
|
|
message: error.message || 'Erro ao deletar chat',
|
|
status: error.status,
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Busca todas as mensagens de um chat específico
|
|
*
|
|
* @param chatId - ID do chat
|
|
* @param userEmail - Email do usuário (opcional)
|
|
* @returns Promise com array de mensagens
|
|
*/
|
|
async getChatMessages(chatId: string, userEmail?: string): Promise<MessageRecord[]> {
|
|
const email = userEmail || GlobalFunctions.getTransferProperty(TransferAreaProperties.UsuarioEmail);
|
|
|
|
if (!email) {
|
|
throw {
|
|
success: false,
|
|
message: 'Email do usuário não fornecido',
|
|
};
|
|
}
|
|
|
|
if (!chatId) {
|
|
throw {
|
|
success: false,
|
|
message: 'ID do chat não fornecido',
|
|
};
|
|
}
|
|
|
|
console.log('Buscando mensagens do chat:', {
|
|
chatId,
|
|
userEmail: email,
|
|
});
|
|
|
|
try {
|
|
const response = await apiService.get<MessageRecord[]>(
|
|
`${this.GET_CHAT_MESSAGES_ENDPOINT}/${email}/${chatId}`
|
|
);
|
|
|
|
console.log('Resposta completa da API:', response);
|
|
console.log('response.data:', response.data);
|
|
console.log('É array?:', Array.isArray(response.data));
|
|
|
|
// A API retorna diretamente o array de mensagens
|
|
// Garante que sempre retorna um array
|
|
if (Array.isArray(response.data)) {
|
|
return response.data;
|
|
} else if (response.data && typeof response.data === 'object') {
|
|
// Se a resposta for um objeto com uma propriedade 'messages' ou similar
|
|
console.warn('API retornou objeto em vez de array:', response.data);
|
|
|
|
// Tenta encontrar o array dentro do objeto
|
|
if (Array.isArray((response.data as any).messages)) {
|
|
return (response.data as any).messages;
|
|
} else if (Array.isArray((response.data as any).data)) {
|
|
return (response.data as any).data;
|
|
}
|
|
}
|
|
|
|
// Se não conseguir extrair array, retorna vazio
|
|
console.warn('Não foi possível extrair array de mensagens da resposta');
|
|
return [];
|
|
} catch (error: any) {
|
|
console.error('Erro ao buscar mensagens:', error);
|
|
|
|
throw {
|
|
success: false,
|
|
message: error.message || 'Erro ao buscar mensagens',
|
|
status: error.status,
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
// Exporta instância única (Singleton)
|
|
export const chatService = new ChatService();
|