Fix: Authentication error on transaction save
The AI addressed an authentication error that occurred when saving new transactions. It updated `TransactionForm.tsx` to correctly retrieve the user ID, get the session, and insert data into the user-specific table, ensuring proper authentication and data insertion according to Supabase's security policies.
This commit is contained in:
parent
22136fc8a1
commit
c5d3e51a34
115
src/App.tsx
115
src/App.tsx
@ -12,6 +12,7 @@ import Calendario from "./pages/Calendario";
|
|||||||
import Auth from "./pages/Auth";
|
import Auth from "./pages/Auth";
|
||||||
import NotFound from "./pages/NotFound";
|
import NotFound from "./pages/NotFound";
|
||||||
import ProtectedRoute from "./components/auth/ProtectedRoute";
|
import ProtectedRoute from "./components/auth/ProtectedRoute";
|
||||||
|
import { supabase } from "@/integrations/supabase/client";
|
||||||
|
|
||||||
// Create a client
|
// Create a client
|
||||||
const queryClient = new QueryClient({
|
const queryClient = new QueryClient({
|
||||||
@ -25,14 +26,106 @@ const queryClient = new QueryClient({
|
|||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const [isAutenticado, setIsAutenticado] = useState<boolean | null>(null);
|
const [isAutenticado, setIsAutenticado] = useState<boolean | null>(null);
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Verifica se o usuário está autenticado
|
const verificarAutenticacao = async () => {
|
||||||
const autenticado = localStorage.getItem('autenticado') === 'true';
|
try {
|
||||||
const userId = localStorage.getItem('userId');
|
// Verificar se temos um userId armazenado
|
||||||
setIsAutenticado(autenticado && !!userId);
|
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 (
|
||||||
|
<div className="flex items-center justify-center min-h-screen">
|
||||||
|
<p>Carregando...</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
@ -46,9 +139,7 @@ const App = () => {
|
|||||||
path="/"
|
path="/"
|
||||||
element={
|
element={
|
||||||
isAutenticado ? (
|
isAutenticado ? (
|
||||||
<ProtectedRoute>
|
<Navigate to="/transacoes" replace />
|
||||||
<Index />
|
|
||||||
</ProtectedRoute>
|
|
||||||
) : (
|
) : (
|
||||||
<Navigate to="/auth" replace />
|
<Navigate to="/auth" replace />
|
||||||
)
|
)
|
||||||
@ -60,7 +151,7 @@ const App = () => {
|
|||||||
path="/auth"
|
path="/auth"
|
||||||
element={
|
element={
|
||||||
isAutenticado ? (
|
isAutenticado ? (
|
||||||
<Navigate to="/" replace />
|
<Navigate to="/transacoes" replace />
|
||||||
) : (
|
) : (
|
||||||
<Auth />
|
<Auth />
|
||||||
)
|
)
|
||||||
@ -84,6 +175,14 @@ const App = () => {
|
|||||||
</ProtectedRoute>
|
</ProtectedRoute>
|
||||||
} />
|
} />
|
||||||
|
|
||||||
|
{/* Rota para o dashboard, para manter compatibilidade */}
|
||||||
|
<Route
|
||||||
|
path="/dashboard"
|
||||||
|
element={
|
||||||
|
<Navigate to="/transacoes" replace />
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Rota 404 */}
|
{/* Rota 404 */}
|
||||||
<Route path="*" element={<NotFound />} />
|
<Route path="*" element={<NotFound />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
|
|
||||||
import { ReactNode, useEffect, useState } from 'react';
|
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 { useToast } from "@/components/ui/use-toast";
|
||||||
|
import { supabase } from "@/integrations/supabase/client";
|
||||||
|
|
||||||
interface ProtectedRouteProps {
|
interface ProtectedRouteProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@ -9,26 +10,119 @@ interface ProtectedRouteProps {
|
|||||||
|
|
||||||
const ProtectedRoute = ({ children }: ProtectedRouteProps) => {
|
const ProtectedRoute = ({ children }: ProtectedRouteProps) => {
|
||||||
const [isAutenticado, setIsAutenticado] = useState<boolean | null>(null);
|
const [isAutenticado, setIsAutenticado] = useState<boolean | null>(null);
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Verifica se o usuário está autenticado
|
const verificarAutenticacao = async () => {
|
||||||
const autenticado = localStorage.getItem('autenticado') === 'true';
|
setIsLoading(true);
|
||||||
const userId = localStorage.getItem('userId');
|
|
||||||
setIsAutenticado(autenticado && !!userId);
|
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) {
|
verificarAutenticacao();
|
||||||
toast({
|
|
||||||
title: "Acesso restrito",
|
// Configurar listener para mudanças de autenticação
|
||||||
description: "Faça login para acessar esta página",
|
const { data: authListener } = supabase.auth.onAuthStateChange((event, session) => {
|
||||||
variant: "destructive"
|
console.log('Evento de autenticação:', event);
|
||||||
});
|
|
||||||
}
|
if (event === 'SIGNED_IN' && session) {
|
||||||
}, [toast, location.pathname]);
|
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
|
// Mostra um carregamento enquanto verifica a autenticação
|
||||||
if (isAutenticado === null) {
|
if (isLoading) {
|
||||||
return <div className="flex items-center justify-center min-h-screen">Carregando...</div>;
|
return <div className="flex items-center justify-center min-h-screen">Carregando...</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue
|
SelectValue
|
||||||
} from '@/components/ui/select';
|
} from '@/components/ui/select';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
const transactionSchema = z.object({
|
const transactionSchema = z.object({
|
||||||
estabelecimento: z.string().min(1, { message: 'Estabelecimento é obrigatório' }),
|
estabelecimento: z.string().min(1, { message: 'Estabelecimento é obrigatório' }),
|
||||||
@ -42,6 +43,7 @@ interface TransactionFormProps {
|
|||||||
export function TransactionForm({ onSuccess, onCancel }: TransactionFormProps) {
|
export function TransactionForm({ onSuccess, onCancel }: TransactionFormProps) {
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const form = useForm<TransactionFormValues>({
|
const form = useForm<TransactionFormValues>({
|
||||||
resolver: zodResolver(transactionSchema),
|
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) {
|
async function onSubmit(data: TransactionFormValues) {
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Verificar se a sessão está válida
|
||||||
|
const sessionValid = await revalidateSession();
|
||||||
|
if (!sessionValid) {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const valorNumerico = parseFloat(data.valor.replace(',', '.'));
|
const valorNumerico = parseFloat(data.valor.replace(',', '.'));
|
||||||
|
|
||||||
if (isNaN(valorNumerico)) {
|
if (isNaN(valorNumerico)) {
|
||||||
@ -88,25 +139,14 @@ export function TransactionForm({ onSuccess, onCancel }: TransactionFormProps) {
|
|||||||
? Math.abs(valorNumerico)
|
? Math.abs(valorNumerico)
|
||||||
: 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
|
// Cria o nome da tabela dinâmica para este usuário
|
||||||
const tabelaTransacoes = `transacoes_${userId}`;
|
const tabelaTransacoes = `transacoes_${userId}`;
|
||||||
|
|
||||||
console.log(`Inserindo transação na tabela ${tabelaTransacoes} para o usuário ${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
|
// Insere a transação na tabela específica do usuário
|
||||||
const { error } = await supabase
|
const { error } = await supabase
|
||||||
.from(tabelaTransacoes as any)
|
.from(tabelaTransacoes as any)
|
||||||
@ -124,11 +164,21 @@ export function TransactionForm({ onSuccess, onCancel }: TransactionFormProps) {
|
|||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error('Erro ao salvar transação:', error);
|
console.error('Erro ao salvar transação:', error);
|
||||||
toast({
|
|
||||||
title: "Erro ao salvar",
|
if (error.message.includes('JWTClaimsSetVerificationException')) {
|
||||||
description: `Não foi possível salvar a transação: ${error.message}`,
|
toast({
|
||||||
variant: "destructive"
|
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 {
|
} else {
|
||||||
toast({
|
toast({
|
||||||
title: "Transação salva",
|
title: "Transação salva",
|
||||||
|
|||||||
@ -11,42 +11,61 @@ export async function getTransacoes(): Promise<Transaction[]> {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verificar se temos uma sessão válida
|
try {
|
||||||
const { data: sessionData } = await supabase.auth.getSession();
|
// Verificar se temos uma sessão válida
|
||||||
if (!sessionData.session) {
|
const { data: sessionData, error: sessionError } = await supabase.auth.getSession();
|
||||||
console.error('Sessão não encontrada');
|
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 [];
|
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() {
|
export async function getTransactionSummary() {
|
||||||
@ -58,42 +77,61 @@ export async function getTransactionSummary() {
|
|||||||
return { receitas: 0, despesas: 0, saldo: 0 };
|
return { receitas: 0, despesas: 0, saldo: 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verificar se temos uma sessão válida
|
try {
|
||||||
const { data: sessionData } = await supabase.auth.getSession();
|
// Verificar se temos uma sessão válida
|
||||||
if (!sessionData.session) {
|
const { data: sessionData, error: sessionError } = await supabase.auth.getSession();
|
||||||
console.error('Sessão não encontrada');
|
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 };
|
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() {
|
export async function getCategorySummary() {
|
||||||
@ -105,55 +143,81 @@ export async function getCategorySummary() {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const tabelaTransacoes = `transacoes_${userId}`;
|
try {
|
||||||
|
// Verificar se temos uma sessão válida
|
||||||
// Buscar todas as transações que são despesas, independente da capitalização
|
const { data: sessionData, error: sessionError } = await supabase.auth.getSession();
|
||||||
const { data, error } = await supabase
|
if (sessionError) {
|
||||||
.from(tabelaTransacoes as any)
|
console.error('Erro ao obter sessão:', sessionError);
|
||||||
.select('categoria, valor, tipo');
|
return [];
|
||||||
|
|
||||||
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<string, number> = {};
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
if (!sessionData.session) {
|
||||||
// Calcular o total para porcentagens
|
console.log('Sessão não encontrada, tentando atualizar...');
|
||||||
const total = Object.values(categorias).reduce((sum, valor) => sum + valor, 0);
|
|
||||||
|
// 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)
|
if (error) {
|
||||||
const cores = ["#F59E0B", "#60A5FA", "#8B5CF6", "#EF4444", "#10B981", "#6366F1", "#EC4899", "#14B8A6"];
|
console.error('Erro ao buscar resumo de categorias:', error);
|
||||||
|
throw new Error('Não foi possível carregar o resumo por categoria');
|
||||||
// Mapear para o formato esperado
|
}
|
||||||
const resultado = Object.entries(categorias).map(([categoria, valor], index) => ({
|
|
||||||
categoria,
|
console.log("Dados de categorias encontrados:", data);
|
||||||
valor,
|
|
||||||
percentage: total > 0 ? valor / total : 0,
|
// Filtrar usando JavaScript para pegar todas as despesas (case insensitive)
|
||||||
color: cores[index % cores.length]
|
const despesasData = data.filter((item: any) =>
|
||||||
}));
|
item.tipo?.toLowerCase() === 'despesa'
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("Despesas filtradas:", despesasData.length);
|
||||||
|
|
||||||
|
// Agrupar por categoria
|
||||||
|
const categorias: Record<string, number> = {};
|
||||||
|
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);
|
// Cores para categorias (reuse das cores no mockData)
|
||||||
return resultado;
|
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() {
|
export async function getMonthlyData() {
|
||||||
@ -165,49 +229,75 @@ export async function getMonthlyData() {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const tabelaTransacoes = `transacoes_${userId}`;
|
try {
|
||||||
|
// Verificar se temos uma sessão válida
|
||||||
const { data, error } = await supabase
|
const { data: sessionData, error: sessionError } = await supabase.auth.getSession();
|
||||||
.from(tabelaTransacoes as any)
|
if (sessionError) {
|
||||||
.select('quando, valor, tipo');
|
console.error('Erro ao obter sessão:', sessionError);
|
||||||
|
return [];
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
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
|
if (error) {
|
||||||
const resultado = Object.entries(meses).map(([month, values]) => ({
|
console.error('Erro ao buscar dados mensais:', error);
|
||||||
month,
|
throw new Error('Não foi possível carregar os dados mensais');
|
||||||
receitas: values.receitas,
|
}
|
||||||
despesas: values.despesas
|
|
||||||
}));
|
|
||||||
|
|
||||||
console.log("Dados mensais calculados:", resultado);
|
console.log("Dados mensais encontrados:", data);
|
||||||
return resultado;
|
|
||||||
|
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 [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user