Connect to transactions table

This commit is contained in:
gpt-engineer-app[bot] 2025-05-17 23:37:48 +00:00
parent cfabf6e503
commit a4f8efc420
2 changed files with 178 additions and 18 deletions

View File

@ -6,24 +6,53 @@ import TransactionsTable from '@/components/dashboard/TransactionsTable';
import CategoryChart from '@/components/dashboard/CategoryChart';
import MonthlyChart from '@/components/dashboard/MonthlyChart';
import { DollarSign, TrendingUp, TrendingDown, PiggyBank } from 'lucide-react';
import { mockTransactions, mockCategories, mockMonthlyData, mockTotals } from '@/data/mockData';
import { Transaction, CategorySummary, MonthlyData } from '@/types/financialTypes';
import { useToast } from "@/components/ui/use-toast";
import { getTransacoes, getTransactionSummary, getCategorySummary, getMonthlyData } from '@/services/transacaoService';
const Dashboard = () => {
const [isLoading, setIsLoading] = useState(true);
const [transactions, setTransactions] = useState<Transaction[]>([]);
const [categories, setCategories] = useState<CategorySummary[]>([]);
const [monthlyData, setMonthlyData] = useState<MonthlyData[]>([]);
const [totals, setTotals] = useState({ receitas: 0, despesas: 0, saldo: 0 });
const { toast } = useToast();
useEffect(() => {
// Simulando carregamento de dados
const timer = setTimeout(() => {
setIsLoading(false);
toast({
title: "Dados carregados com sucesso",
description: "Conecte ao Supabase para ver seus dados reais"
});
}, 1500);
return () => clearTimeout(timer);
async function loadData() {
try {
setIsLoading(true);
// Buscar todos os dados necessários
const [transacoesData, totalsData, categoriesData, monthlyDataResult] = await Promise.all([
getTransacoes(),
getTransactionSummary(),
getCategorySummary(),
getMonthlyData()
]);
setTransactions(transacoesData);
setTotals(totalsData);
setCategories(categoriesData);
setMonthlyData(monthlyDataResult);
toast({
title: "Dados carregados com sucesso",
description: "Seus dados financeiros foram atualizados"
});
} catch (error) {
console.error("Erro ao carregar dados:", error);
toast({
title: "Erro ao carregar dados",
description: "Verifique a conexão com o Supabase",
variant: "destructive"
});
} finally {
setIsLoading(false);
}
}
loadData();
}, [toast]);
const formatCurrency = (value: number) => {
@ -46,7 +75,7 @@ const Dashboard = () => {
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<SummaryCard
title="Receitas"
value={formatCurrency(mockTotals.receitas)}
value={formatCurrency(totals.receitas)}
icon={<DollarSign className="h-4 w-4 text-finance-green" />}
trend={5}
iconClass="bg-finance-green/10"
@ -54,7 +83,7 @@ const Dashboard = () => {
/>
<SummaryCard
title="Despesas"
value={formatCurrency(mockTotals.despesas)}
value={formatCurrency(totals.despesas)}
icon={<TrendingDown className="h-4 w-4 text-finance-red" />}
trend={-2}
iconClass="bg-finance-red/10"
@ -62,14 +91,14 @@ const Dashboard = () => {
/>
<SummaryCard
title="Saldo"
value={formatCurrency(mockTotals.saldo)}
value={formatCurrency(totals.saldo)}
icon={<TrendingUp className="h-4 w-4 text-finance-blue" />}
iconClass="bg-finance-blue/10"
valueClass="text-finance-blue"
/>
<SummaryCard
title="Economia"
value={`${((mockTotals.saldo / mockTotals.receitas) * 100).toFixed(1)}%`}
value={`${totals.receitas > 0 ? ((totals.saldo / totals.receitas) * 100).toFixed(1) : 0}%`}
icon={<PiggyBank className="h-4 w-4 text-finance-purple" />}
iconClass="bg-finance-purple/10"
valueClass="text-finance-purple"
@ -77,13 +106,13 @@ const Dashboard = () => {
</div>
<div className="grid gap-4 md:grid-cols-2">
<CategoryChart categories={mockCategories} isLoading={isLoading} />
<MonthlyChart data={mockMonthlyData} isLoading={isLoading} />
<CategoryChart categories={categories} isLoading={isLoading} />
<MonthlyChart data={monthlyData} isLoading={isLoading} />
</div>
<div className="space-y-2">
<h2 className="text-xl font-semibold">Transações Recentes</h2>
<TransactionsTable transactions={mockTransactions} isLoading={isLoading} />
<TransactionsTable transactions={transactions} isLoading={isLoading} />
</div>
</div>
</Layout>

View File

@ -0,0 +1,131 @@
import { supabase } from "@/integrations/supabase/client";
import { Transaction } from "@/types/financialTypes";
export async function getTransacoes(): Promise<Transaction[]> {
const { data, error } = await supabase
.from('transacoes')
.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');
}
// Transformar os dados recebidos para o formato esperado
return data.map((item) => ({
id: item.id.toString(),
user: item.user || '',
created_at: item.created_at,
valor: item.valor || 0,
quando: item.quando || new Date().toISOString(),
detalhes: item.detalhes || '',
estabelecimento: item.estabelecimento || '',
tipo: (item.tipo === 'entrada' || item.tipo === 'saida') ? item.tipo : 'saida',
categoria: item.categoria || 'Outros'
}));
}
export async function getTransactionSummary() {
const { data, error } = await supabase
.from('transacoes')
.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');
}
const totalReceitas = data
.filter(item => item.tipo === 'entrada')
.reduce((sum, item) => sum + (item.valor || 0), 0);
const totalDespesas = data
.filter(item => item.tipo === 'saida')
.reduce((sum, item) => sum + (item.valor || 0), 0);
return {
receitas: totalReceitas,
despesas: totalDespesas,
saldo: totalReceitas - totalDespesas
};
}
export async function getCategorySummary() {
const { data, error } = await supabase
.from('transacoes')
.select('categoria, valor, tipo')
.eq('tipo', 'saida');
if (error) {
console.error('Erro ao buscar resumo de categorias:', error);
throw new Error('Não foi possível carregar o resumo por categoria');
}
// Agrupar por categoria
const categorias: Record<string, number> = {};
data.forEach(item => {
if (item.categoria && item.valor) {
if (!categorias[item.categoria]) {
categorias[item.categoria] = 0;
}
categorias[item.categoria] += 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
return Object.entries(categorias).map(([categoria, valor], index) => ({
categoria,
valor,
percentage: total > 0 ? valor / total : 0,
color: cores[index % cores.length]
}));
}
export async function getMonthlyData() {
const { data, error } = await supabase
.from('transacoes')
.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');
}
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
data.forEach(item => {
if (item.quando && item.valor) {
const data = new Date(item.quando);
const mesIndex = data.getMonth();
const nomeMes = nomesMeses[mesIndex];
if (item.tipo === 'entrada') {
meses[nomeMes].receitas += item.valor;
} else {
meses[nomeMes].despesas += item.valor;
}
}
});
// Converter para o formato esperado
return Object.entries(meses).map(([month, values]) => ({
month,
receitas: values.receitas,
despesas: values.despesas
}));
}