diff --git a/src/App.tsx b/src/App.tsx index b2ad7bf..c87fce7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -12,6 +12,7 @@ import Calendario from "./pages/Calendario"; import Auth from "./pages/Auth"; import NotFound from "./pages/NotFound"; import ProtectedRoute from "./components/auth/ProtectedRoute"; +import { supabase } from "@/integrations/supabase/client"; // Create a client const queryClient = new QueryClient({ @@ -25,14 +26,106 @@ const queryClient = new QueryClient({ const App = () => { const [isAutenticado, setIsAutenticado] = useState(null); + const [isLoading, setIsLoading] = useState(true); useEffect(() => { - // Verifica se o usuário está autenticado - const autenticado = localStorage.getItem('autenticado') === 'true'; - const userId = localStorage.getItem('userId'); - setIsAutenticado(autenticado && !!userId); + const verificarAutenticacao = async () => { + try { + // Verificar se temos um userId armazenado + const userId = localStorage.getItem('userId'); + const autenticadoStorage = localStorage.getItem('autenticado') === 'true'; + + // Verificar se temos uma sessão válida com o Supabase + const { data: sessionData, error: sessionError } = await supabase.auth.getSession(); + + if (sessionError) { + console.error('Erro ao verificar sessão:', sessionError); + setIsAutenticado(false); + localStorage.setItem('autenticado', 'false'); + setIsLoading(false); + return; + } + + if (!sessionData.session) { + // Se não temos userId ou sessão, não está autenticado + if (!userId || !autenticadoStorage) { + setIsAutenticado(false); + localStorage.setItem('autenticado', 'false'); + setIsLoading(false); + return; + } + + // Tentar atualizar a sessão + const { data: refreshData, error: refreshError } = await supabase.auth.refreshSession(); + + if (refreshError || !refreshData.session) { + console.log('Não foi possível atualizar a sessão'); + setIsAutenticado(false); + localStorage.setItem('autenticado', 'false'); + setIsLoading(false); + return; + } + + console.log('Sessão atualizada com sucesso'); + setIsAutenticado(true); + localStorage.setItem('autenticado', 'true'); + setIsLoading(false); + return; + } + + // Se chegou aqui, temos uma sessão válida + setIsAutenticado(true); + localStorage.setItem('autenticado', 'true'); + + // Se não tivermos um userId, vamos usar o da sessão + if (!userId && sessionData.session.user.id) { + localStorage.setItem('userId', sessionData.session.user.id); + } + + // Se tivermos um userId, mas ele for diferente do da sessão, atualizamos + if (userId && sessionData.session.user.id && userId !== sessionData.session.user.id) { + localStorage.setItem('userId', sessionData.session.user.id); + } + } catch (error) { + console.error('Erro ao verificar autenticação:', error); + setIsAutenticado(false); + localStorage.setItem('autenticado', 'false'); + } finally { + setIsLoading(false); + } + }; + + verificarAutenticacao(); + + // Configurar listener para mudanças de autenticação + const { data: authListener } = supabase.auth.onAuthStateChange((event, session) => { + console.log('Evento de autenticação:', event); + + if (event === 'SIGNED_IN' && session) { + setIsAutenticado(true); + localStorage.setItem('autenticado', 'true'); + localStorage.setItem('userId', session.user.id); + } else if (event === 'SIGNED_OUT' || event === 'USER_DELETED') { + setIsAutenticado(false); + localStorage.setItem('autenticado', 'false'); + localStorage.removeItem('userId'); + } + }); + + // Limpar listener ao desmontar o componente + return () => { + authListener.subscription.unsubscribe(); + }; }, []); + if (isLoading) { + return ( +
+

Carregando...

+
+ ); + } + return ( @@ -46,9 +139,7 @@ const App = () => { path="/" element={ isAutenticado ? ( - - - + ) : ( ) @@ -60,7 +151,7 @@ const App = () => { path="/auth" element={ isAutenticado ? ( - + ) : ( ) @@ -84,6 +175,14 @@ const App = () => { } /> + {/* Rota para o dashboard, para manter compatibilidade */} + + } + /> + {/* Rota 404 */} } /> diff --git a/src/components/auth/ProtectedRoute.tsx b/src/components/auth/ProtectedRoute.tsx index 3f7391a..efb4731 100644 --- a/src/components/auth/ProtectedRoute.tsx +++ b/src/components/auth/ProtectedRoute.tsx @@ -1,7 +1,8 @@ import { ReactNode, useEffect, useState } from 'react'; -import { Navigate, useLocation } from 'react-router-dom'; +import { Navigate, useLocation, useNavigate } from 'react-router-dom'; import { useToast } from "@/components/ui/use-toast"; +import { supabase } from "@/integrations/supabase/client"; interface ProtectedRouteProps { children: ReactNode; @@ -9,26 +10,119 @@ interface ProtectedRouteProps { const ProtectedRoute = ({ children }: ProtectedRouteProps) => { const [isAutenticado, setIsAutenticado] = useState(null); + const [isLoading, setIsLoading] = useState(true); const { toast } = useToast(); const location = useLocation(); + const navigate = useNavigate(); useEffect(() => { - // Verifica se o usuário está autenticado - const autenticado = localStorage.getItem('autenticado') === 'true'; - const userId = localStorage.getItem('userId'); - setIsAutenticado(autenticado && !!userId); + const verificarAutenticacao = async () => { + setIsLoading(true); + + try { + // Verificar se temos um userId armazenado + const userId = localStorage.getItem('userId'); + + // Verificar se temos uma sessão válida com o Supabase + const { data: sessionData, error: sessionError } = await supabase.auth.getSession(); + + if (sessionError) { + console.error('Erro ao verificar sessão:', sessionError); + setIsAutenticado(false); + localStorage.setItem('autenticado', 'false'); + + toast({ + title: "Erro de autenticação", + description: "Não foi possível validar sua sessão", + variant: "destructive" + }); + + setIsLoading(false); + return; + } + + if (!sessionData.session) { + console.log('Sessão não encontrada, tentando atualizar...'); + + // Tentar atualizar a sessão + const { data: refreshData, error: refreshError } = await supabase.auth.refreshSession(); + + if (refreshError || !refreshData.session) { + console.log('Não foi possível atualizar a sessão ou usuário não está autenticado'); + setIsAutenticado(false); + localStorage.setItem('autenticado', 'false'); + + if (userId) { + toast({ + title: "Sessão expirada", + description: "Sua sessão expirou. Por favor, faça login novamente", + variant: "destructive" + }); + } + + setIsLoading(false); + return; + } + + console.log('Sessão atualizada com sucesso'); + setIsAutenticado(true); + localStorage.setItem('autenticado', 'true'); + setIsLoading(false); + return; + } + + // Se chegou aqui, temos uma sessão válida + setIsAutenticado(true); + localStorage.setItem('autenticado', 'true'); + + // Se não tivermos um userId, vamos usar o da sessão + if (!userId && sessionData.session.user.id) { + localStorage.setItem('userId', sessionData.session.user.id); + } + } catch (error) { + console.error('Erro ao verificar autenticação:', error); + setIsAutenticado(false); + localStorage.setItem('autenticado', 'false'); + + toast({ + title: "Erro inesperado", + description: "Ocorreu um erro ao verificar sua autenticação", + variant: "destructive" + }); + } finally { + setIsLoading(false); + } + }; - if (!autenticado || !userId) { - toast({ - title: "Acesso restrito", - description: "Faça login para acessar esta página", - variant: "destructive" - }); - } - }, [toast, location.pathname]); + verificarAutenticacao(); + + // Configurar listener para mudanças de autenticação + const { data: authListener } = supabase.auth.onAuthStateChange((event, session) => { + console.log('Evento de autenticação:', event); + + if (event === 'SIGNED_IN' && session) { + setIsAutenticado(true); + localStorage.setItem('autenticado', 'true'); + localStorage.setItem('userId', session.user.id); + } else if (event === 'SIGNED_OUT') { + setIsAutenticado(false); + localStorage.setItem('autenticado', 'false'); + localStorage.removeItem('userId'); + navigate('/auth'); + } else if (event === 'TOKEN_REFRESHED' && session) { + setIsAutenticado(true); + localStorage.setItem('autenticado', 'true'); + } + }); + + // Limpar listener ao desmontar o componente + return () => { + authListener.subscription.unsubscribe(); + }; + }, [toast, navigate, location.pathname]); // Mostra um carregamento enquanto verifica a autenticação - if (isAutenticado === null) { + if (isLoading) { return
Carregando...
; } diff --git a/src/components/dashboard/TransactionForm.tsx b/src/components/dashboard/TransactionForm.tsx index 3483266..0564dc5 100644 --- a/src/components/dashboard/TransactionForm.tsx +++ b/src/components/dashboard/TransactionForm.tsx @@ -22,6 +22,7 @@ import { SelectTrigger, SelectValue } from '@/components/ui/select'; +import { useNavigate } from 'react-router-dom'; const transactionSchema = z.object({ estabelecimento: z.string().min(1, { message: 'Estabelecimento é obrigatório' }), @@ -42,6 +43,7 @@ interface TransactionFormProps { export function TransactionForm({ onSuccess, onCancel }: TransactionFormProps) { const [isSubmitting, setIsSubmitting] = useState(false); const { toast } = useToast(); + const navigate = useNavigate(); const form = useForm({ resolver: zodResolver(transactionSchema), @@ -55,10 +57,59 @@ export function TransactionForm({ onSuccess, onCancel }: TransactionFormProps) { } }); + async function revalidateSession() { + try { + // Verificar sessão com Supabase + const { data: sessionData, error: sessionError } = await supabase.auth.getSession(); + + if (sessionError || !sessionData.session) { + console.error('Sessão inválida, tentando atualizar...'); + + // Tentar atualizar a sessão + const { data: refreshData, error: refreshError } = await supabase.auth.refreshSession(); + + if (refreshError || !refreshData.session) { + // Se não conseguir atualizar, redirecionar para login + console.error('Não foi possível atualizar a sessão:', refreshError); + toast({ + title: "Sessão expirada", + description: "Sua sessão expirou. Por favor, faça login novamente.", + variant: "destructive" + }); + + // Limpar o estado de autenticação + localStorage.setItem('autenticado', 'false'); + + // Redirecionar para a página de login após um pequeno atraso + setTimeout(() => { + navigate('/auth'); + }, 1000); + + return false; + } + + console.log('Sessão atualizada com sucesso'); + return true; + } + + return true; + } catch (error) { + console.error('Erro ao verificar sessão:', error); + return false; + } + } + async function onSubmit(data: TransactionFormValues) { setIsSubmitting(true); try { + // Verificar se a sessão está válida + const sessionValid = await revalidateSession(); + if (!sessionValid) { + setIsSubmitting(false); + return; + } + const valorNumerico = parseFloat(data.valor.replace(',', '.')); if (isNaN(valorNumerico)) { @@ -88,25 +139,14 @@ export function TransactionForm({ onSuccess, onCancel }: TransactionFormProps) { ? Math.abs(valorNumerico) : Math.abs(valorNumerico); - // Obtém a sessão atual para acessar o token JWT - const { data: sessionData, error: sessionError } = await supabase.auth.getSession(); - - if (sessionError || !sessionData.session) { - console.error('Erro ao obter sessão:', sessionError); - toast({ - title: "Erro de autenticação", - description: "Não foi possível validar sua sessão. Tente fazer login novamente.", - variant: "destructive" - }); - setIsSubmitting(false); - return; - } - // Cria o nome da tabela dinâmica para este usuário const tabelaTransacoes = `transacoes_${userId}`; console.log(`Inserindo transação na tabela ${tabelaTransacoes} para o usuário ${userId}`); + // Obtém a sessão atual para acessar o token JWT - nova solicitação após validação + const { data: sessionData } = await supabase.auth.getSession(); + // Insere a transação na tabela específica do usuário const { error } = await supabase .from(tabelaTransacoes as any) @@ -124,11 +164,21 @@ export function TransactionForm({ onSuccess, onCancel }: TransactionFormProps) { if (error) { console.error('Erro ao salvar transação:', error); - toast({ - title: "Erro ao salvar", - description: `Não foi possível salvar a transação: ${error.message}`, - variant: "destructive" - }); + + if (error.message.includes('JWTClaimsSetVerificationException')) { + toast({ + title: "Erro de autenticação", + description: "Sua sessão expirou. Por favor, faça login novamente.", + variant: "destructive" + }); + navigate('/auth'); + } else { + toast({ + title: "Erro ao salvar", + description: `Não foi possível salvar a transação: ${error.message}`, + variant: "destructive" + }); + } } else { toast({ title: "Transação salva", diff --git a/src/services/transacaoService.ts b/src/services/transacaoService.ts index 9648844..bdbb04f 100644 --- a/src/services/transacaoService.ts +++ b/src/services/transacaoService.ts @@ -11,42 +11,61 @@ export async function getTransacoes(): Promise { return []; } - // Verificar se temos uma sessão válida - const { data: sessionData } = await supabase.auth.getSession(); - if (!sessionData.session) { - console.error('Sessão não encontrada'); + try { + // Verificar se temos uma sessão válida + const { data: sessionData, error: sessionError } = await supabase.auth.getSession(); + if (sessionError) { + console.error('Erro ao obter sessão:', sessionError); + return []; + } + + if (!sessionData.session) { + console.log('Sessão não encontrada, tentando atualizar...'); + + // Tentar atualizar a sessão + const { data: refreshData, error: refreshError } = await supabase.auth.refreshSession(); + + if (refreshError || !refreshData.session) { + console.error('Não foi possível atualizar a sessão:', refreshError); + return []; + } + + console.log('Sessão atualizada com sucesso'); + } + + const tabelaTransacoes = `transacoes_${userId}`; + + console.log(`Consultando tabela: ${tabelaTransacoes}`); + + // Usando .from() com cast para any para contornar a checagem de tipos + const { data, error } = await supabase + .from(tabelaTransacoes as any) + .select('*') + .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.usuario_id || '', + 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' + })); + } catch (error) { + console.error('Erro ao buscar transações:', error); return []; } - - const tabelaTransacoes = `transacoes_${userId}`; - - console.log(`Consultando tabela: ${tabelaTransacoes}`); - - // Usando .rpc para chamar uma função SQL ou .from() com cast para any para contornar a checagem de tipos - const { data, error } = await supabase - .from(tabelaTransacoes as any) - .select('*') - .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.usuario_id || '', - 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' - })); } export async function getTransactionSummary() { @@ -58,42 +77,61 @@ export async function getTransactionSummary() { return { receitas: 0, despesas: 0, saldo: 0 }; } - // Verificar se temos uma sessão válida - const { data: sessionData } = await supabase.auth.getSession(); - if (!sessionData.session) { - console.error('Sessão não encontrada'); + try { + // Verificar se temos uma sessão válida + const { data: sessionData, error: sessionError } = await supabase.auth.getSession(); + if (sessionError) { + console.error('Erro ao obter sessão:', sessionError); + return { receitas: 0, despesas: 0, saldo: 0 }; + } + + if (!sessionData.session) { + console.log('Sessão não encontrada, tentando atualizar...'); + + // Tentar atualizar a sessão + const { data: refreshData, error: refreshError } = await supabase.auth.refreshSession(); + + if (refreshError || !refreshData.session) { + console.error('Não foi possível atualizar a sessão:', refreshError); + return { receitas: 0, despesas: 0, saldo: 0 }; + } + + console.log('Sessão atualizada com sucesso'); + } + + const tabelaTransacoes = `transacoes_${userId}`; + + const { data, error } = await supabase + .from(tabelaTransacoes as any) + .select('tipo, valor'); + + 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 }; } - - const tabelaTransacoes = `transacoes_${userId}`; - - const { data, error } = await supabase - .from(tabelaTransacoes as any) - .select('tipo, valor'); - - 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; } export async function getCategorySummary() { @@ -105,55 +143,81 @@ export async function getCategorySummary() { return []; } - const tabelaTransacoes = `transacoes_${userId}`; - - // Buscar todas as transações que são despesas, independente da capitalização - const { data, error } = await supabase - .from(tabelaTransacoes as any) - .select('categoria, valor, tipo'); - - 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 usando JavaScript para pegar todas as despesas (case insensitive) - const despesasData = data.filter((item: any) => - item.tipo?.toLowerCase() === 'despesa' - ); - - console.log("Despesas filtradas:", despesasData.length); - - // Agrupar por categoria - const categorias: Record = {}; - despesasData.forEach((item: any) => { - if (item.categoria && item.valor) { - const categoriaKey = item.categoria || 'Outros'; - if (!categorias[categoriaKey]) { - categorias[categoriaKey] = 0; - } - categorias[categoriaKey] += Math.abs(item.valor); + try { + // Verificar se temos uma sessão válida + const { data: sessionData, error: sessionError } = await supabase.auth.getSession(); + if (sessionError) { + console.error('Erro ao obter sessão:', sessionError); + return []; } - }); - - // Calcular o total para porcentagens - const total = Object.values(categorias).reduce((sum, valor) => sum + valor, 0); + + if (!sessionData.session) { + console.log('Sessão não encontrada, tentando atualizar...'); + + // Tentar atualizar a sessão + const { data: refreshData, error: refreshError } = await supabase.auth.refreshSession(); + + if (refreshError || !refreshData.session) { + console.error('Não foi possível atualizar a sessão:', refreshError); + return []; + } + + console.log('Sessão atualizada com sucesso'); + } + + const tabelaTransacoes = `transacoes_${userId}`; + + // Buscar todas as transações que são despesas, independente da capitalização + const { data, error } = await supabase + .from(tabelaTransacoes as any) + .select('categoria, valor, tipo'); - // 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] - })); + 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 usando JavaScript para pegar todas as despesas (case insensitive) + const despesasData = data.filter((item: any) => + item.tipo?.toLowerCase() === 'despesa' + ); + + console.log("Despesas filtradas:", despesasData.length); + + // Agrupar por categoria + const categorias: Record = {}; + despesasData.forEach((item: any) => { + if (item.categoria && 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); - console.log("Resumo de categorias calculado:", resultado); - return resultado; + // 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() { @@ -165,49 +229,75 @@ export async function getMonthlyData() { return []; } - const tabelaTransacoes = `transacoes_${userId}`; - - const { data, error } = await supabase - .from(tabelaTransacoes as any) - .select('quando, valor, tipo'); - - 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); - } + try { + // Verificar se temos uma sessão válida + const { data: sessionData, error: sessionError } = await supabase.auth.getSession(); + if (sessionError) { + console.error('Erro ao obter sessão:', sessionError); + return []; } - }); + + if (!sessionData.session) { + console.log('Sessão não encontrada, tentando atualizar...'); + + // Tentar atualizar a sessão + const { data: refreshData, error: refreshError } = await supabase.auth.refreshSession(); + + if (refreshError || !refreshData.session) { + console.error('Não foi possível atualizar a sessão:', refreshError); + return []; + } + + console.log('Sessão atualizada com sucesso'); + } + + const tabelaTransacoes = `transacoes_${userId}`; + + const { data, error } = await supabase + .from(tabelaTransacoes as any) + .select('quando, valor, tipo'); - // Converter para o formato esperado - const resultado = Object.entries(meses).map(([month, values]) => ({ - month, - receitas: values.receitas, - despesas: values.despesas - })); + 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 calculados:", resultado); - return resultado; + 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 []; + } }