From 049acd069ef8bc9f584ebae043f8863acb9da0a0 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Wed, 21 May 2025 10:49:36 +0000 Subject: [PATCH] Refactor: Split transacaoService.ts into smaller files Refactors transacaoService.ts into smaller, more manageable files to improve code organization and maintainability. --- src/hooks/useTransactionForm.ts | 3 +- src/hooks/useTransactions.ts | 2 +- src/pages/Categorias.tsx | 2 +- src/pages/Index.tsx | 7 +- src/services/transacao/baseService.ts | 55 +++ src/services/transacao/index.ts | 13 + src/services/transacao/summaryService.ts | 199 +++++++++ .../transacao/transacaoCrudService.ts | 88 ++++ .../transacao/transacaoFetchService.ts | 59 +++ src/services/transacaoService.ts | 391 +----------------- 10 files changed, 433 insertions(+), 386 deletions(-) create mode 100644 src/services/transacao/baseService.ts create mode 100644 src/services/transacao/index.ts create mode 100644 src/services/transacao/summaryService.ts create mode 100644 src/services/transacao/transacaoCrudService.ts create mode 100644 src/services/transacao/transacaoFetchService.ts diff --git a/src/hooks/useTransactionForm.ts b/src/hooks/useTransactionForm.ts index c8a1bbb..c02869e 100644 --- a/src/hooks/useTransactionForm.ts +++ b/src/hooks/useTransactionForm.ts @@ -1,4 +1,3 @@ - import { useState } from 'react'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; @@ -6,7 +5,7 @@ import * as z from 'zod'; import { useToast } from '@/hooks/use-toast'; import { supabase } from '@/integrations/supabase/client'; import { Transaction } from '@/types/financialTypes'; -import { updateTransacao } from '@/services/transacaoService'; +import { updateTransacao } from '@/services/transacao'; const transactionSchema = z.object({ estabelecimento: z.string().min(1, { message: 'Estabelecimento é obrigatório' }), diff --git a/src/hooks/useTransactions.ts b/src/hooks/useTransactions.ts index 3973774..9068a7e 100644 --- a/src/hooks/useTransactions.ts +++ b/src/hooks/useTransactions.ts @@ -3,7 +3,7 @@ import { useState, useEffect } from 'react'; import { Transaction } from '@/types/financialTypes'; import { CartaoCredito } from '@/types/cartaoTypes'; import { useToast } from '@/hooks/use-toast'; -import { getTransacoes } from '@/services/transacaoService'; +import { getTransacoes } from '@/services/transacao'; import { getCartoes } from '@/services/cartaoCreditoService'; export const useTransactions = () => { diff --git a/src/pages/Categorias.tsx b/src/pages/Categorias.tsx index e884d90..d659a88 100644 --- a/src/pages/Categorias.tsx +++ b/src/pages/Categorias.tsx @@ -2,7 +2,7 @@ import { useState, useEffect } from 'react'; import Layout from '@/components/layout/Layout'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { getCategorySummary } from '@/services/transacaoService'; +import { getCategorySummary } from '@/services/transacao'; import { CategorySummary } from '@/types/financialTypes'; import { useToast } from "@/components/ui/use-toast"; import { Progress } from "@/components/ui/progress"; diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx index af11e7e..e848e97 100644 --- a/src/pages/Index.tsx +++ b/src/pages/Index.tsx @@ -8,7 +8,12 @@ import MonthlyChart from '@/components/dashboard/MonthlyChart'; import { Wallet, ArrowUp, ArrowDown, PiggyBank, CreditCard } from 'lucide-react'; import { Transaction, CategorySummary, MonthlyData } from '@/types/financialTypes'; import { useToast } from "@/components/ui/use-toast"; -import { getTransacoes, getTransactionSummary, getCategorySummary, getMonthlyData } from '@/services/transacaoService'; +import { + getTransacoes, + getTransactionSummary, + getCategorySummary, + getMonthlyData +} from '@/services/transacao'; import { getCartoes } from '@/services/cartaoCreditoService'; const Dashboard = () => { diff --git a/src/services/transacao/baseService.ts b/src/services/transacao/baseService.ts new file mode 100644 index 0000000..2df45bd --- /dev/null +++ b/src/services/transacao/baseService.ts @@ -0,0 +1,55 @@ + +import { supabase } from "@/integrations/supabase/client"; + +/** + * Get user email from local storage and normalize it + * @returns Normalized user email or null if not found + */ +export const getUserEmail = (): string | null => { + const userEmail = localStorage.getItem('userEmail'); + + if (!userEmail) { + console.error('Email do usuário não encontrado no localStorage'); + return null; + } + + // Normalize the email (lowercase and trim spaces) + return userEmail.trim().toLowerCase(); +}; + +/** + * Get user groups by email + * @param userEmail Normalized user email + * @returns Array of group IDs or empty array if error + */ +export const getUserGroups = async (userEmail: string): Promise => { + try { + const { data: userGroups, error: groupsError } = await supabase + .from('grupos_whatsapp') + .select('remote_jid') + .eq('login', userEmail); + + if (groupsError) { + console.error('Erro ao buscar grupos do usuário:', groupsError); + return []; + } + + // Extract IDs of the groups to use in the filter + return userGroups ? userGroups.map(group => group.remote_jid) : []; + } catch (error) { + console.error('Erro ao obter grupos do usuário:', error); + return []; + } +}; + +/** + * Format currency value to Brazilian Real (BRL) + * @param value Number to format as currency + * @returns Formatted currency string + */ +export const formatCurrency = (value: number) => { + return new Intl.NumberFormat('pt-BR', { + style: 'currency', + currency: 'BRL', + }).format(value); +}; diff --git a/src/services/transacao/index.ts b/src/services/transacao/index.ts new file mode 100644 index 0000000..aa5921e --- /dev/null +++ b/src/services/transacao/index.ts @@ -0,0 +1,13 @@ + +// Re-export all transaction service functions +export { getTransacoes } from './transacaoFetchService'; +export { + getTransactionSummary, + getCategorySummary, + getMonthlyData +} from './summaryService'; +export { + deleteTransacao, + updateTransacao +} from './transacaoCrudService'; +export { formatCurrency } from './baseService'; diff --git a/src/services/transacao/summaryService.ts b/src/services/transacao/summaryService.ts new file mode 100644 index 0000000..48fed99 --- /dev/null +++ b/src/services/transacao/summaryService.ts @@ -0,0 +1,199 @@ + +import { supabase } from "@/integrations/supabase/client"; +import { CategorySummary, MonthlyData } from "@/types/financialTypes"; +import { getUserEmail, getUserGroups } from "./baseService"; + +/** + * Get transaction summary (totals for incomes and expenses) + * @returns Promise with summary data + */ +export async function getTransactionSummary() { + console.log("Buscando resumo das transações..."); + + const normalizedEmail = getUserEmail(); + + if (!normalizedEmail) { + return { receitas: 0, despesas: 0, saldo: 0 }; + } + + try { + // Get user groups by email + const groupIds = await getUserGroups(normalizedEmail); + + // Fetch transaction summary with enhanced filter based on email (login) or group_id + const { data, error } = await supabase + .from('transacoes') + .select('tipo, valor') + .or(`login.eq.${normalizedEmail},${groupIds.length > 0 ? `grupo_id.in.(${groupIds.map(id => `"${id}"`).join(',')})` : ''}`); + + if (error) { + console.error('Erro ao buscar resumo das transações:', error); + throw new Error('Não foi possível carregar o resumo das transações'); + } + + console.log("Dados para resumo encontrados:", data); + + const totalReceitas = data + .filter((item: any) => item.tipo?.toLowerCase() === 'receita') + .reduce((sum: number, item: any) => sum + Math.abs(item.valor || 0), 0); + + const totalDespesas = data + .filter((item: any) => (item.tipo?.toLowerCase() === 'despesa')) + .reduce((sum: number, item: any) => sum + Math.abs(item.valor || 0), 0); + + const resultado = { + receitas: totalReceitas, + despesas: totalDespesas, + saldo: totalReceitas - totalDespesas + }; + + console.log("Resumo calculado:", resultado); + return resultado; + } catch (error) { + console.error('Erro ao buscar resumo das transações:', error); + return { receitas: 0, despesas: 0, saldo: 0 }; + } +} + +/** + * Get category summary for transactions + * @param tipoFiltro Filter by transaction type ('receita', 'despesa', or 'all') + * @returns Promise with category summary data + */ +export async function getCategorySummary(tipoFiltro: string = 'despesa'): Promise { + console.log(`Buscando resumo de categorias para tipo: ${tipoFiltro}`); + + const normalizedEmail = getUserEmail(); + + if (!normalizedEmail) { + return []; + } + + try { + // Get user groups by email + const groupIds = await getUserGroups(normalizedEmail); + + // Fetch category summary with enhanced filter based on email (login) or group_id + const { data, error } = await supabase + .from('transacoes') + .select('categoria, valor, tipo') + .or(`login.eq.${normalizedEmail},${groupIds.length > 0 ? `grupo_id.in.(${groupIds.map(id => `"${id}"`).join(',')})` : ''}`); + + if (error) { + console.error('Erro ao buscar resumo de categorias:', error); + throw new Error('Não foi possível carregar o resumo por categoria'); + } + + console.log("Dados de categorias encontrados:", data); + + // Filter according to the requested type (incomes, expenses, or both) + let filteredData = data; + if (tipoFiltro.toLowerCase() === 'despesa') { + filteredData = data.filter((item: any) => item.tipo?.toLowerCase() === 'despesa'); + } else if (tipoFiltro.toLowerCase() === 'receita') { + filteredData = data.filter((item: any) => item.tipo?.toLowerCase() === 'receita'); + } + + console.log(`${filteredData.length} itens filtrados para tipo: ${tipoFiltro}`); + + // Group by category + const categorias: Record = {}; + filteredData.forEach((item: any) => { + if (item.valor) { + const categoriaKey = item.categoria || 'Outros'; + if (!categorias[categoriaKey]) { + categorias[categoriaKey] = 0; + } + categorias[categoriaKey] += Math.abs(item.valor); + } + }); + + // Calculate the total for percentages + const total = Object.values(categorias).reduce((sum, valor) => sum + valor, 0); + + // Colors for categories (reuse colors in mockData) + const cores = ["#F59E0B", "#60A5FA", "#8B5CF6", "#EF4444", "#10B981", "#6366F1", "#EC4899", "#14B8A6"]; + + // Map to the expected format + const resultado = Object.entries(categorias).map(([categoria, valor], index) => ({ + categoria, + valor, + percentage: total > 0 ? valor / total : 0, + color: cores[index % cores.length] + })); + + console.log("Resumo de categorias calculado:", resultado); + return resultado; + } catch (error) { + console.error('Erro ao buscar resumo de categorias:', error); + return []; + } +} + +/** + * Get monthly data for transactions + * @returns Promise with monthly data + */ +export async function getMonthlyData(): Promise { + console.log("Buscando dados mensais..."); + + const normalizedEmail = getUserEmail(); + + if (!normalizedEmail) { + return []; + } + + try { + // Get user groups by email + const groupIds = await getUserGroups(normalizedEmail); + + // Fetch monthly data with enhanced filter based on email (login) or group_id + const { data, error } = await supabase + .from('transacoes') + .select('quando, valor, tipo') + .or(`login.eq.${normalizedEmail},${groupIds.length > 0 ? `grupo_id.in.(${groupIds.map(id => `"${id}"`).join(',')})` : ''}`); + + if (error) { + console.error('Erro ao buscar dados mensais:', error); + throw new Error('Não foi possível carregar os dados mensais'); + } + + console.log("Dados mensais encontrados:", data); + + const meses: Record = {}; + const nomesMeses = ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez']; + + // Initialize months + nomesMeses.forEach(mes => { + meses[mes] = { receitas: 0, despesas: 0 }; + }); + + // Group by month, normalizing the type + data.forEach((item: any) => { + if (item.quando && item.valor) { + const data = new Date(item.quando); + const mesIndex = data.getMonth(); + const nomeMes = nomesMeses[mesIndex]; + + if (item.tipo?.toLowerCase() === 'receita') { + meses[nomeMes].receitas += Math.abs(item.valor); + } else { + meses[nomeMes].despesas += Math.abs(item.valor); + } + } + }); + + // Convert to the expected format + const resultado = Object.entries(meses).map(([month, values]) => ({ + month, + receitas: values.receitas, + despesas: values.despesas + })); + + console.log("Dados mensais calculados:", resultado); + return resultado; + } catch (error) { + console.error('Erro ao buscar dados mensais:', error); + return []; + } +} diff --git a/src/services/transacao/transacaoCrudService.ts b/src/services/transacao/transacaoCrudService.ts new file mode 100644 index 0000000..5585a84 --- /dev/null +++ b/src/services/transacao/transacaoCrudService.ts @@ -0,0 +1,88 @@ + +import { supabase } from "@/integrations/supabase/client"; +import { Transaction } from "@/types/financialTypes"; +import { getUserEmail } from "./baseService"; + +/** + * Delete a specific transaction + * @param id ID of the transaction to be deleted + */ +export async function deleteTransacao(id: string): Promise { + console.log(`Excluindo transação com ID: ${id}`); + + try { + const { error } = await supabase + .from('transacoes') + .delete() + .eq('id', parseInt(id)); // Convert string to number with parseInt + + if (error) { + console.error('Erro ao excluir transação:', error); + throw new Error('Não foi possível excluir a transação'); + } + + console.log('Transação excluída com sucesso'); + } catch (error) { + console.error('Erro ao excluir transação:', error); + throw error; + } +} + +/** + * Update an existing transaction + * @param transaction Transaction with updated data + * @returns Updated transaction + */ +export async function updateTransacao(transaction: Transaction): Promise { + console.log(`Atualizando transação com ID: ${transaction.id}`, transaction); + + const normalizedEmail = getUserEmail(); + + if (!normalizedEmail) { + throw new Error('Usuário não autenticado'); + } + + const transacaoData = { + login: normalizedEmail, + valor: transaction.tipo === 'receita' ? Math.abs(transaction.valor) : Math.abs(transaction.valor) * -1, + quando: transaction.quando, + detalhes: transaction.detalhes, + estabelecimento: transaction.estabelecimento, + tipo: transaction.tipo, + categoria: transaction.categoria + }; + + try { + const { data, error } = await supabase + .from('transacoes') + .update(transacaoData) + .eq('id', parseInt(transaction.id)) // Convert string to number with parseInt + .select('*') + .single(); + + if (error) { + console.error('Erro ao atualizar transação:', error); + throw new Error('Não foi possível atualizar a transação'); + } + + console.log('Transação atualizada com sucesso:', data); + + // Transform to the expected format + return { + id: data.id.toString(), + user: data.user || '', + login: data.login || normalizedEmail, + created_at: data.created_at, + valor: data.tipo === 'receita' ? Math.abs(data.valor || 0) : -Math.abs(data.valor || 0), + quando: data.quando || new Date().toISOString(), + detalhes: data.detalhes || '', + estabelecimento: data.estabelecimento || '', + tipo: data.tipo?.toLowerCase() || 'despesa', + categoria: data.categoria || 'Outros', + grupo_id: data.grupo_id || null + }; + } catch (error) { + console.error('Erro ao atualizar transação:', error); + throw error; + } +} diff --git a/src/services/transacao/transacaoFetchService.ts b/src/services/transacao/transacaoFetchService.ts new file mode 100644 index 0000000..af088d0 --- /dev/null +++ b/src/services/transacao/transacaoFetchService.ts @@ -0,0 +1,59 @@ + +import { supabase } from "@/integrations/supabase/client"; +import { Transaction } from "@/types/financialTypes"; +import { getUserEmail, getUserGroups } from "./baseService"; + +/** + * Fetch all transactions for the current user + * @returns Promise with array of transactions + */ +export async function getTransacoes(): Promise { + console.log("Buscando transações do Supabase..."); + + // Get the user's email from localStorage + const normalizedEmail = getUserEmail(); + + if (!normalizedEmail) { + return []; + } + + console.log("Buscando transações para o usuário com email:", normalizedEmail); + + try { + // First, get all user's groups by email + const groupIds = await getUserGroups(normalizedEmail); + console.log(`Encontrados ${groupIds.length} grupos vinculados ao usuário:`, groupIds); + + // Fetch transactions with filter based on email (login) or group_id + const { data, error } = await supabase + .from('transacoes') + .select('*') + .or(`login.eq.${normalizedEmail},${groupIds.length > 0 ? `grupo_id.in.(${groupIds.map(id => `"${id}"`).join(',')})` : ''}`) + .order('quando', { ascending: false }); + + if (error) { + console.error('Erro ao buscar transações:', error); + throw new Error('Não foi possível carregar as transações'); + } + + console.log("Transações encontradas:", data); + + // Transform the received data to the expected format, normalizing the types + return data.map((item: any) => ({ + id: item.id.toString(), + user: item.user || '', + login: item.login || normalizedEmail, // Ensure the login field is filled + created_at: item.created_at, + valor: item.tipo?.toLowerCase() === 'receita' ? Math.abs(item.valor || 0) : -Math.abs(item.valor || 0), + quando: item.quando || new Date().toISOString(), + detalhes: item.detalhes || '', + estabelecimento: item.estabelecimento || '', + tipo: item.tipo?.toLowerCase() || 'despesa', + categoria: item.categoria || 'Outros', + grupo_id: item.grupo_id || null + })); + } catch (error) { + console.error('Erro ao buscar transações:', error); + return []; + } +} diff --git a/src/services/transacaoService.ts b/src/services/transacaoService.ts index 27d54e0..7c7443a 100644 --- a/src/services/transacaoService.ts +++ b/src/services/transacaoService.ts @@ -1,382 +1,11 @@ -import { supabase } from "@/integrations/supabase/client"; -import { Transaction, CategorySummary } from "@/types/financialTypes"; -export async function getTransacoes(): Promise { - console.log("Buscando transações do Supabase..."); - - // Obter o email do usuário do localStorage - const userEmail = localStorage.getItem('userEmail'); - - if (!userEmail) { - console.error('Email do usuário não encontrado no localStorage'); - return []; - } - - // Normalizar o email (minúsculo e sem espaços) - const normalizedEmail = userEmail.trim().toLowerCase(); - console.log("Buscando transações para o usuário com email:", normalizedEmail); - - try { - // Primeiro, buscar todos os grupos do usuário pelo email - const { data: userGroups, error: groupsError } = await supabase - .from('grupos_whatsapp') - .select('remote_jid') - .eq('login', normalizedEmail); - - if (groupsError) { - console.error('Erro ao buscar grupos do usuário:', groupsError); - } - - // Extrair IDs dos grupos para usar no filtro - const groupIds = userGroups ? userGroups.map(group => group.remote_jid) : []; - console.log(`Encontrados ${groupIds.length} grupos vinculados ao usuário:`, groupIds); - - // Buscar transações com filtro baseado no email (login) ou grupo_id - const { data, error } = await supabase - .from('transacoes') - .select('*') - .or(`login.eq.${normalizedEmail},${groupIds.length > 0 ? `grupo_id.in.(${groupIds.map(id => `"${id}"`).join(',')})` : ''}`) - .order('quando', { ascending: false }); - - if (error) { - console.error('Erro ao buscar transações:', error); - throw new Error('Não foi possível carregar as transações'); - } - - console.log("Transações encontradas:", data); - - // Transformar os dados recebidos para o formato esperado, normalizando os tipos - return data.map((item: any) => ({ - id: item.id.toString(), - user: item.user || '', - login: item.login || normalizedEmail, // Garantir que o campo login esteja preenchido - created_at: item.created_at, - valor: item.tipo?.toLowerCase() === 'receita' ? Math.abs(item.valor || 0) : -Math.abs(item.valor || 0), - quando: item.quando || new Date().toISOString(), - detalhes: item.detalhes || '', - estabelecimento: item.estabelecimento || '', - tipo: item.tipo?.toLowerCase() || 'despesa', - categoria: item.categoria || 'Outros', - grupo_id: item.grupo_id || null - })); - } catch (error) { - console.error('Erro ao buscar transações:', error); - return []; - } -} - -export async function getTransactionSummary() { - console.log("Buscando resumo das transações..."); - - // Obter o email do usuário do localStorage - const userEmail = localStorage.getItem('userEmail'); - - if (!userEmail) { - console.error('Email do usuário não encontrado no localStorage'); - return { receitas: 0, despesas: 0, saldo: 0 }; - } - - // Normalizar o email (minúsculo e sem espaços) - const normalizedEmail = userEmail.trim().toLowerCase(); - - try { - // Primeiro, buscar todos os grupos do usuário pelo email - const { data: userGroups, error: groupsError } = await supabase - .from('grupos_whatsapp') - .select('remote_jid') - .eq('login', normalizedEmail); - - if (groupsError) { - console.error('Erro ao buscar grupos do usuário:', groupsError); - } - - // Extrair IDs dos grupos para usar no filtro - const groupIds = userGroups ? userGroups.map(group => group.remote_jid) : []; - - // Buscar resumo das transações com filtro aprimorado baseado no email (login) ou grupo_id - const { data, error } = await supabase - .from('transacoes') - .select('tipo, valor') - .or(`login.eq.${normalizedEmail},${groupIds.length > 0 ? `grupo_id.in.(${groupIds.map(id => `"${id}"`).join(',')})` : ''}`); - - if (error) { - console.error('Erro ao buscar resumo das transações:', error); - throw new Error('Não foi possível carregar o resumo das transações'); - } - - console.log("Dados para resumo encontrados:", data); - - const totalReceitas = data - .filter((item: any) => item.tipo?.toLowerCase() === 'receita') - .reduce((sum: number, item: any) => sum + Math.abs(item.valor || 0), 0); - - const totalDespesas = data - .filter((item: any) => (item.tipo?.toLowerCase() === 'despesa')) - .reduce((sum: number, item: any) => sum + Math.abs(item.valor || 0), 0); - - const resultado = { - receitas: totalReceitas, - despesas: totalDespesas, - saldo: totalReceitas - totalDespesas - }; - - console.log("Resumo calculado:", resultado); - return resultado; - } catch (error) { - console.error('Erro ao buscar resumo das transações:', error); - return { receitas: 0, despesas: 0, saldo: 0 }; - } -} - -export async function getCategorySummary(tipoFiltro: string = 'despesa') { - console.log(`Buscando resumo de categorias para tipo: ${tipoFiltro}`); - - // Obter o email do usuário do localStorage - const userEmail = localStorage.getItem('userEmail'); - - if (!userEmail) { - console.error('Email do usuário não encontrado no localStorage'); - return []; - } - - // Normalizar o email (minúsculo e sem espaços) - const normalizedEmail = userEmail.trim().toLowerCase(); - - try { - // Primeiro, buscar todos os grupos do usuário pelo email - const { data: userGroups, error: groupsError } = await supabase - .from('grupos_whatsapp') - .select('remote_jid') - .eq('login', normalizedEmail); - - if (groupsError) { - console.error('Erro ao buscar grupos do usuário:', groupsError); - } - - // Extrair IDs dos grupos para usar no filtro - const groupIds = userGroups ? userGroups.map(group => group.remote_jid) : []; - - // Buscar resumo de categorias com filtro aprimorado baseado no email (login) ou grupo_id - const { data, error } = await supabase - .from('transacoes') - .select('categoria, valor, tipo') - .or(`login.eq.${normalizedEmail},${groupIds.length > 0 ? `grupo_id.in.(${groupIds.map(id => `"${id}"`).join(',')})` : ''}`); - - if (error) { - console.error('Erro ao buscar resumo de categorias:', error); - throw new Error('Não foi possível carregar o resumo por categoria'); - } - - console.log("Dados de categorias encontrados:", data); - - // Filtrar conforme o tipo solicitado (receitas, despesas ou ambos) - let filteredData = data; - if (tipoFiltro.toLowerCase() === 'despesa') { - filteredData = data.filter((item: any) => item.tipo?.toLowerCase() === 'despesa'); - } else if (tipoFiltro.toLowerCase() === 'receita') { - filteredData = data.filter((item: any) => item.tipo?.toLowerCase() === 'receita'); - } - - console.log(`${filteredData.length} itens filtrados para tipo: ${tipoFiltro}`); - - // Agrupar por categoria - const categorias: Record = {}; - filteredData.forEach((item: any) => { - if (item.valor) { - const categoriaKey = item.categoria || 'Outros'; - if (!categorias[categoriaKey]) { - categorias[categoriaKey] = 0; - } - categorias[categoriaKey] += Math.abs(item.valor); - } - }); - - // Calcular o total para porcentagens - const total = Object.values(categorias).reduce((sum, valor) => sum + valor, 0); - - // Cores para categorias (reuse das cores no mockData) - const cores = ["#F59E0B", "#60A5FA", "#8B5CF6", "#EF4444", "#10B981", "#6366F1", "#EC4899", "#14B8A6"]; - - // Mapear para o formato esperado - const resultado = Object.entries(categorias).map(([categoria, valor], index) => ({ - categoria, - valor, - percentage: total > 0 ? valor / total : 0, - color: cores[index % cores.length] - })); - - console.log("Resumo de categorias calculado:", resultado); - return resultado; - } catch (error) { - console.error('Erro ao buscar resumo de categorias:', error); - return []; - } -} - -export async function getMonthlyData() { - console.log("Buscando dados mensais..."); - - // Obter o email do usuário do localStorage - const userEmail = localStorage.getItem('userEmail'); - - if (!userEmail) { - console.error('Email do usuário não encontrado no localStorage'); - return []; - } - - // Normalizar o email (minúsculo e sem espaços) - const normalizedEmail = userEmail.trim().toLowerCase(); - - try { - // Primeiro, buscar todos os grupos do usuário pelo email - const { data: userGroups, error: groupsError } = await supabase - .from('grupos_whatsapp') - .select('remote_jid') - .eq('login', normalizedEmail); - - if (groupsError) { - console.error('Erro ao buscar grupos do usuário:', groupsError); - } - - // Extrair IDs dos grupos para usar no filtro - const groupIds = userGroups ? userGroups.map(group => group.remote_jid) : []; - - // Buscar dados mensais com filtro aprimorado baseado no email (login) ou grupo_id - const { data, error } = await supabase - .from('transacoes') - .select('quando, valor, tipo') - .or(`login.eq.${normalizedEmail},${groupIds.length > 0 ? `grupo_id.in.(${groupIds.map(id => `"${id}"`).join(',')})` : ''}`); - - if (error) { - console.error('Erro ao buscar dados mensais:', error); - throw new Error('Não foi possível carregar os dados mensais'); - } - - console.log("Dados mensais encontrados:", data); - - const meses: Record = {}; - const nomesMeses = ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez']; - - // Inicializar meses - nomesMeses.forEach(mes => { - meses[mes] = { receitas: 0, despesas: 0 }; - }); - - // Agrupar por mês, normalizando o tipo - data.forEach((item: any) => { - if (item.quando && item.valor) { - const data = new Date(item.quando); - const mesIndex = data.getMonth(); - const nomeMes = nomesMeses[mesIndex]; - - if (item.tipo?.toLowerCase() === 'receita') { - meses[nomeMes].receitas += Math.abs(item.valor); - } else { - meses[nomeMes].despesas += Math.abs(item.valor); - } - } - }); - - // Converter para o formato esperado - const resultado = Object.entries(meses).map(([month, values]) => ({ - month, - receitas: values.receitas, - despesas: values.despesas - })); - - console.log("Dados mensais calculados:", resultado); - return resultado; - } catch (error) { - console.error('Erro ao buscar dados mensais:', error); - return []; - } -} - -/** - * Delete uma transação específica - * @param id ID da transação a ser excluída - */ -export async function deleteTransacao(id: string): Promise { - console.log(`Excluindo transação com ID: ${id}`); - - try { - const { error } = await supabase - .from('transacoes') - .delete() - .eq('id', parseInt(id)); // Convert string to number with parseInt - - if (error) { - console.error('Erro ao excluir transação:', error); - throw new Error('Não foi possível excluir a transação'); - } - - console.log('Transação excluída com sucesso'); - } catch (error) { - console.error('Erro ao excluir transação:', error); - throw error; - } -} - -/** - * Atualiza os dados de uma transação existente - * @param transaction A transação com os dados atualizados - * @returns A transação atualizada - */ -export async function updateTransacao(transaction: Transaction): Promise { - console.log(`Atualizando transação com ID: ${transaction.id}`, transaction); - - // Obter o email do usuário do localStorage - const userEmail = localStorage.getItem('userEmail'); - - if (!userEmail) { - console.error('Email do usuário não encontrado no localStorage'); - throw new Error('Usuário não autenticado'); - } - - // Normalizar o email (minúsculo e sem espaços) - const normalizedEmail = userEmail.trim().toLowerCase(); - - const transacaoData = { - login: normalizedEmail, - valor: transaction.tipo === 'receita' ? Math.abs(transaction.valor) : Math.abs(transaction.valor) * -1, - quando: transaction.quando, - detalhes: transaction.detalhes, - estabelecimento: transaction.estabelecimento, - tipo: transaction.tipo, - categoria: transaction.categoria - }; - - try { - const { data, error } = await supabase - .from('transacoes') - .update(transacaoData) - .eq('id', parseInt(transaction.id)) // Convert string to number with parseInt - .select('*') - .single(); - - if (error) { - console.error('Erro ao atualizar transação:', error); - throw new Error('Não foi possível atualizar a transação'); - } - - console.log('Transação atualizada com sucesso:', data); - - // Transformar para o formato esperado - return { - id: data.id.toString(), - user: data.user || '', - login: data.login || normalizedEmail, - created_at: data.created_at, - valor: data.tipo === 'receita' ? Math.abs(data.valor || 0) : -Math.abs(data.valor || 0), - quando: data.quando || new Date().toISOString(), - detalhes: data.detalhes || '', - estabelecimento: data.estabelecimento || '', - tipo: data.tipo?.toLowerCase() || 'despesa', - categoria: data.categoria || 'Outros', - grupo_id: data.grupo_id || null - }; - } catch (error) { - console.error('Erro ao atualizar transação:', error); - throw error; - } -} +// This file is now just a facade that re-exports from the modular services +export { + getTransacoes, + getTransactionSummary, + getCategorySummary, + getMonthlyData, + deleteTransacao, + updateTransacao, + formatCurrency +} from './transacao';