Connect to transactions table
This commit is contained in:
parent
cfabf6e503
commit
a4f8efc420
@ -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>
|
||||
|
||||
131
src/services/transacaoService.ts
Normal file
131
src/services/transacaoService.ts
Normal 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
|
||||
}));
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user