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 CategoryChart from '@/components/dashboard/CategoryChart';
|
||||||
import MonthlyChart from '@/components/dashboard/MonthlyChart';
|
import MonthlyChart from '@/components/dashboard/MonthlyChart';
|
||||||
import { DollarSign, TrendingUp, TrendingDown, PiggyBank } from 'lucide-react';
|
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 { useToast } from "@/components/ui/use-toast";
|
||||||
|
import { getTransacoes, getTransactionSummary, getCategorySummary, getMonthlyData } from '@/services/transacaoService';
|
||||||
|
|
||||||
const Dashboard = () => {
|
const Dashboard = () => {
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
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();
|
const { toast } = useToast();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Simulando carregamento de dados
|
async function loadData() {
|
||||||
const timer = setTimeout(() => {
|
try {
|
||||||
setIsLoading(false);
|
setIsLoading(true);
|
||||||
toast({
|
|
||||||
title: "Dados carregados com sucesso",
|
|
||||||
description: "Conecte ao Supabase para ver seus dados reais"
|
|
||||||
});
|
|
||||||
}, 1500);
|
|
||||||
|
|
||||||
return () => clearTimeout(timer);
|
// 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]);
|
}, [toast]);
|
||||||
|
|
||||||
const formatCurrency = (value: number) => {
|
const formatCurrency = (value: number) => {
|
||||||
@ -46,7 +75,7 @@ const Dashboard = () => {
|
|||||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||||||
<SummaryCard
|
<SummaryCard
|
||||||
title="Receitas"
|
title="Receitas"
|
||||||
value={formatCurrency(mockTotals.receitas)}
|
value={formatCurrency(totals.receitas)}
|
||||||
icon={<DollarSign className="h-4 w-4 text-finance-green" />}
|
icon={<DollarSign className="h-4 w-4 text-finance-green" />}
|
||||||
trend={5}
|
trend={5}
|
||||||
iconClass="bg-finance-green/10"
|
iconClass="bg-finance-green/10"
|
||||||
@ -54,7 +83,7 @@ const Dashboard = () => {
|
|||||||
/>
|
/>
|
||||||
<SummaryCard
|
<SummaryCard
|
||||||
title="Despesas"
|
title="Despesas"
|
||||||
value={formatCurrency(mockTotals.despesas)}
|
value={formatCurrency(totals.despesas)}
|
||||||
icon={<TrendingDown className="h-4 w-4 text-finance-red" />}
|
icon={<TrendingDown className="h-4 w-4 text-finance-red" />}
|
||||||
trend={-2}
|
trend={-2}
|
||||||
iconClass="bg-finance-red/10"
|
iconClass="bg-finance-red/10"
|
||||||
@ -62,14 +91,14 @@ const Dashboard = () => {
|
|||||||
/>
|
/>
|
||||||
<SummaryCard
|
<SummaryCard
|
||||||
title="Saldo"
|
title="Saldo"
|
||||||
value={formatCurrency(mockTotals.saldo)}
|
value={formatCurrency(totals.saldo)}
|
||||||
icon={<TrendingUp className="h-4 w-4 text-finance-blue" />}
|
icon={<TrendingUp className="h-4 w-4 text-finance-blue" />}
|
||||||
iconClass="bg-finance-blue/10"
|
iconClass="bg-finance-blue/10"
|
||||||
valueClass="text-finance-blue"
|
valueClass="text-finance-blue"
|
||||||
/>
|
/>
|
||||||
<SummaryCard
|
<SummaryCard
|
||||||
title="Economia"
|
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" />}
|
icon={<PiggyBank className="h-4 w-4 text-finance-purple" />}
|
||||||
iconClass="bg-finance-purple/10"
|
iconClass="bg-finance-purple/10"
|
||||||
valueClass="text-finance-purple"
|
valueClass="text-finance-purple"
|
||||||
@ -77,13 +106,13 @@ const Dashboard = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid gap-4 md:grid-cols-2">
|
<div className="grid gap-4 md:grid-cols-2">
|
||||||
<CategoryChart categories={mockCategories} isLoading={isLoading} />
|
<CategoryChart categories={categories} isLoading={isLoading} />
|
||||||
<MonthlyChart data={mockMonthlyData} isLoading={isLoading} />
|
<MonthlyChart data={monthlyData} isLoading={isLoading} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<h2 className="text-xl font-semibold">Transações Recentes</h2>
|
<h2 className="text-xl font-semibold">Transações Recentes</h2>
|
||||||
<TransactionsTable transactions={mockTransactions} isLoading={isLoading} />
|
<TransactionsTable transactions={transactions} isLoading={isLoading} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</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