Refactor: Split transacaoService.ts into smaller files
Refactors transacaoService.ts into smaller, more manageable files to improve code organization and maintainability.
This commit is contained in:
parent
1e9117cbd2
commit
049acd069e
@ -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' }),
|
||||
|
||||
@ -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 = () => {
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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 = () => {
|
||||
|
||||
55
src/services/transacao/baseService.ts
Normal file
55
src/services/transacao/baseService.ts
Normal file
@ -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<string[]> => {
|
||||
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);
|
||||
};
|
||||
13
src/services/transacao/index.ts
Normal file
13
src/services/transacao/index.ts
Normal file
@ -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';
|
||||
199
src/services/transacao/summaryService.ts
Normal file
199
src/services/transacao/summaryService.ts
Normal file
@ -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<CategorySummary[]> {
|
||||
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<string, number> = {};
|
||||
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<MonthlyData[]> {
|
||||
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<string, { receitas: number, despesas: number }> = {};
|
||||
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 [];
|
||||
}
|
||||
}
|
||||
88
src/services/transacao/transacaoCrudService.ts
Normal file
88
src/services/transacao/transacaoCrudService.ts
Normal file
@ -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<void> {
|
||||
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<Transaction> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
59
src/services/transacao/transacaoFetchService.ts
Normal file
59
src/services/transacao/transacaoFetchService.ts
Normal file
@ -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<Transaction[]> {
|
||||
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 [];
|
||||
}
|
||||
}
|
||||
@ -1,382 +1,11 @@
|
||||
import { supabase } from "@/integrations/supabase/client";
|
||||
import { Transaction, CategorySummary } from "@/types/financialTypes";
|
||||
|
||||
export async function getTransacoes(): Promise<Transaction[]> {
|
||||
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<string, number> = {};
|
||||
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<string, { receitas: number, despesas: number }> = {};
|
||||
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<void> {
|
||||
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<Transaction> {
|
||||
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';
|
||||
|
||||
Loading…
Reference in New Issue
Block a user