Refactor: Split Index.tsx into smaller components
This commit refactors the `src/pages/Index.tsx` file into smaller, more manageable components to improve code organization and readability. Unused imports and files will be removed.
This commit is contained in:
parent
4e8972cb55
commit
47e301c9bf
30
src/components/dashboard/DashboardCharts.tsx
Normal file
30
src/components/dashboard/DashboardCharts.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
|
||||
import React from 'react';
|
||||
import { CategorySummary, MonthlyData } from "@/types/financialTypes";
|
||||
import CategoryChart from "@/components/dashboard/CategoryChart";
|
||||
import MonthlyChart from "@/components/dashboard/MonthlyChart";
|
||||
import { SimpleCard } from "@/components/ui/simple-card";
|
||||
|
||||
interface DashboardChartsProps {
|
||||
monthlyData: MonthlyData[];
|
||||
categories: CategorySummary[];
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
const DashboardCharts: React.FC<DashboardChartsProps> = ({ monthlyData, categories, isLoading }) => {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* 1. Receitas vs Despesas Chart */}
|
||||
<SimpleCard title="📈 Receitas vs Despesas" className="border-green-200">
|
||||
<MonthlyChart data={monthlyData} isLoading={isLoading} />
|
||||
</SimpleCard>
|
||||
|
||||
{/* 2. Gastos por Categoria */}
|
||||
<SimpleCard title="📊 Despesas por Categoria" className="border-orange-200">
|
||||
<CategoryChart categories={categories} isLoading={isLoading} />
|
||||
</SimpleCard>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardCharts;
|
||||
12
src/components/dashboard/DashboardHeader.tsx
Normal file
12
src/components/dashboard/DashboardHeader.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
import React from 'react';
|
||||
|
||||
const DashboardHeader = () => {
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<h1 className="text-2xl font-bold tracking-tight">Dashboard</h1>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardHeader;
|
||||
26
src/components/dashboard/DashboardLoadingState.tsx
Normal file
26
src/components/dashboard/DashboardLoadingState.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||
|
||||
const DashboardLoadingState = () => {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="grid gap-4 md:grid-cols-4">
|
||||
{[1, 2, 3, 4].map((i) => (
|
||||
<Card key={i}>
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<div className="h-4 bg-muted rounded w-1/2 animate-pulse"></div>
|
||||
<div className="h-4 w-4 bg-muted rounded animate-pulse"></div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="h-8 bg-muted rounded w-3/4 mb-2 animate-pulse"></div>
|
||||
<div className="h-3 bg-muted rounded w-1/2 animate-pulse"></div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardLoadingState;
|
||||
44
src/components/dashboard/DashboardRecentTransactions.tsx
Normal file
44
src/components/dashboard/DashboardRecentTransactions.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Transaction } from '@/types/financialTypes';
|
||||
import TransactionsTable from "@/components/dashboard/TransactionsTable";
|
||||
import { SimpleCard } from "@/components/ui/simple-card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
interface DashboardRecentTransactionsProps {
|
||||
transactions: Transaction[];
|
||||
transactionsLoading: boolean;
|
||||
}
|
||||
|
||||
const DashboardRecentTransactions: React.FC<DashboardRecentTransactionsProps> = ({
|
||||
transactions,
|
||||
transactionsLoading
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<SimpleCard title="📋 Últimas Transações" className="border-blue-200">
|
||||
<div className="space-y-4">
|
||||
<TransactionsTable
|
||||
transactions={transactions.slice(0, 5)}
|
||||
isLoading={transactionsLoading}
|
||||
showPagination={false}
|
||||
onEdit={() => {}}
|
||||
onDelete={() => {}}
|
||||
/>
|
||||
<div className="flex justify-center pt-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => navigate('/transacoes')}
|
||||
className="hover:bg-blue-50 hover:border-blue-300 transition-colors"
|
||||
>
|
||||
Ver todas as transações
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</SimpleCard>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardRecentTransactions;
|
||||
116
src/components/dashboard/DashboardSummaryCards.tsx
Normal file
116
src/components/dashboard/DashboardSummaryCards.tsx
Normal file
@ -0,0 +1,116 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { ArrowUpIcon, ArrowDownIcon, CreditCardIcon, Target } from "lucide-react";
|
||||
import { ResumoFinanceiro } from "@/types/financialTypes";
|
||||
import { useNavigateWithFilter } from "@/hooks/useNavigateWithFilter";
|
||||
|
||||
interface DashboardSummaryCardsProps {
|
||||
resumo: ResumoFinanceiro | null;
|
||||
formatCurrency: (value: number) => string;
|
||||
}
|
||||
|
||||
const DashboardSummaryCards: React.FC<DashboardSummaryCardsProps> = ({ resumo, formatCurrency }) => {
|
||||
const { navigateToTransactions } = useNavigateWithFilter();
|
||||
|
||||
const saldo = resumo ? resumo.totalReceitas - resumo.totalDespesas - (resumo.totalCartoes || 0) : 0;
|
||||
const totalDespesasGeral = resumo ? resumo.totalDespesas + (resumo.totalCartoes || 0) : 0;
|
||||
|
||||
return (
|
||||
<div className="grid gap-6 md:grid-cols-4">
|
||||
{/* Card Receitas - Clicável */}
|
||||
<Card
|
||||
className="relative overflow-hidden border-2 border-green-200 bg-gradient-to-br from-green-50 to-emerald-100 shadow-lg hover:shadow-xl transition-all duration-300 cursor-pointer group transform hover:scale-105"
|
||||
onClick={() => navigateToTransactions('receita')}
|
||||
>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-green-400/10 to-emerald-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
||||
<CardContent className="p-6 relative">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="p-3 bg-green-500 rounded-full shadow-lg">
|
||||
<ArrowUpIcon className="h-6 w-6 text-white" />
|
||||
</div>
|
||||
<div className="text-xs font-bold px-3 py-1 bg-green-500 text-white rounded-full shadow-sm">
|
||||
+5%
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm font-semibold text-green-700">Receitas</p>
|
||||
<p className="text-3xl font-bold text-green-600">
|
||||
{resumo ? formatCurrency(resumo.totalReceitas) : 'R$ 0,00'}
|
||||
</p>
|
||||
<p className="text-xs text-green-600 opacity-80">Clique para ver detalhes</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Card Despesas - Clicável */}
|
||||
<Card
|
||||
className="relative overflow-hidden border-2 border-red-200 bg-gradient-to-br from-red-50 to-rose-100 shadow-lg hover:shadow-xl transition-all duration-300 cursor-pointer group transform hover:scale-105"
|
||||
onClick={() => navigateToTransactions('despesa')}
|
||||
>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-red-400/10 to-rose-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
||||
<CardContent className="p-6 relative">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="p-3 bg-red-500 rounded-full shadow-lg">
|
||||
<ArrowDownIcon className="h-6 w-6 text-white" />
|
||||
</div>
|
||||
<div className="text-xs font-bold px-3 py-1 bg-red-500 text-white rounded-full shadow-sm">
|
||||
-2%
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm font-semibold text-red-700">Despesas</p>
|
||||
<p className="text-3xl font-bold text-red-600">
|
||||
{resumo ? formatCurrency(totalDespesasGeral) : 'R$ 0,00'}
|
||||
</p>
|
||||
{resumo && resumo.totalCartoes > 0 && (
|
||||
<p className="text-xs text-red-500">Cartões: {formatCurrency(resumo.totalCartoes)}</p>
|
||||
)}
|
||||
<p className="text-xs text-red-600 opacity-80">Clique para ver detalhes</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Card Saldo */}
|
||||
<Card className="relative overflow-hidden border-2 border-blue-200 bg-gradient-to-br from-blue-50 to-sky-100 shadow-lg hover:shadow-xl transition-all duration-300 group transform hover:scale-105">
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-blue-400/10 to-sky-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
||||
<CardContent className="p-6 relative">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="p-3 bg-blue-500 rounded-full shadow-lg">
|
||||
<CreditCardIcon className="h-6 w-6 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm font-semibold text-blue-700">Saldo</p>
|
||||
<p className={`text-3xl font-bold ${
|
||||
saldo >= 0 ? 'text-blue-600' : 'text-red-600'
|
||||
}`}>
|
||||
{resumo ? formatCurrency(saldo) : 'R$ 0,00'}
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Card Economia */}
|
||||
<Card className="relative overflow-hidden border-2 border-purple-200 bg-gradient-to-br from-purple-50 to-violet-100 shadow-lg hover:shadow-xl transition-all duration-300 group transform hover:scale-105">
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-purple-400/10 to-violet-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
||||
<CardContent className="p-6 relative">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="p-3 bg-purple-500 rounded-full shadow-lg">
|
||||
<Target className="h-6 w-6 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm font-semibold text-purple-700">Economia</p>
|
||||
<p className="text-3xl font-bold text-purple-600">
|
||||
-22.2%
|
||||
</p>
|
||||
<p className="text-xs text-purple-500">{resumo ? formatCurrency(Math.abs(saldo)) : 'R$ 0,00'}</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardSummaryCards;
|
||||
@ -1,15 +1,14 @@
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { ArrowUpIcon, ArrowDownIcon, CreditCardIcon, Target, TrendingUpIcon, TrendingDownIcon } from "lucide-react";
|
||||
import { getResumoFinanceiro, getCategorySummary, getMonthlyData } from "@/services/transacao";
|
||||
import { ResumoFinanceiro, CategorySummary, MonthlyData } from "@/types/financialTypes";
|
||||
import TransactionsTable from "@/components/dashboard/TransactionsTable";
|
||||
import { useTransactions } from "@/hooks/useTransactions";
|
||||
import CategoryChart from "@/components/dashboard/CategoryChart";
|
||||
import MonthlyChart from "@/components/dashboard/MonthlyChart";
|
||||
import { SimpleCard } from "@/components/ui/simple-card";
|
||||
import { useNavigateWithFilter } from "@/hooks/useNavigateWithFilter";
|
||||
import DashboardHeader from "@/components/dashboard/DashboardHeader";
|
||||
import DashboardLoadingState from "@/components/dashboard/DashboardLoadingState";
|
||||
import DashboardSummaryCards from "@/components/dashboard/DashboardSummaryCards";
|
||||
import DashboardCharts from "@/components/dashboard/DashboardCharts";
|
||||
import DashboardRecentTransactions from "@/components/dashboard/DashboardRecentTransactions";
|
||||
|
||||
const Dashboard = () => {
|
||||
const [resumo, setResumo] = useState<ResumoFinanceiro | null>(null);
|
||||
@ -17,7 +16,6 @@ const Dashboard = () => {
|
||||
const [monthlyData, setMonthlyData] = useState<MonthlyData[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const { toast } = useToast();
|
||||
const { navigateToTransactions } = useNavigateWithFilter();
|
||||
|
||||
const getCurrentMonth = () => {
|
||||
const now = new Date();
|
||||
@ -74,151 +72,28 @@ const Dashboard = () => {
|
||||
}, [toast]);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="grid gap-4 md:grid-cols-4">
|
||||
{[1, 2, 3, 4].map((i) => (
|
||||
<Card key={i}>
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<div className="h-4 bg-muted rounded w-1/2 animate-pulse"></div>
|
||||
<div className="h-4 w-4 bg-muted rounded animate-pulse"></div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="h-8 bg-muted rounded w-3/4 mb-2 animate-pulse"></div>
|
||||
<div className="h-3 bg-muted rounded w-1/2 animate-pulse"></div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return <DashboardLoadingState />;
|
||||
}
|
||||
|
||||
const saldo = resumo ? resumo.totalReceitas - resumo.totalDespesas - (resumo.totalCartoes || 0) : 0;
|
||||
const totalDespesasGeral = resumo ? resumo.totalDespesas + (resumo.totalCartoes || 0) : 0;
|
||||
|
||||
return (
|
||||
<div className="space-y-6" data-tour="dashboard-content">
|
||||
<div className="flex items-center justify-between">
|
||||
<h1 className="text-2xl font-bold tracking-tight">Dashboard</h1>
|
||||
</div>
|
||||
<DashboardHeader />
|
||||
|
||||
<DashboardSummaryCards
|
||||
resumo={resumo}
|
||||
formatCurrency={formatCurrency}
|
||||
/>
|
||||
|
||||
<div className="grid gap-6 md:grid-cols-4">
|
||||
{/* Card Receitas - Clicável */}
|
||||
<Card
|
||||
className="relative overflow-hidden border-0 bg-gradient-to-br from-green-50 to-emerald-100 shadow-lg hover:shadow-xl transition-all duration-300 cursor-pointer group transform hover:scale-105"
|
||||
onClick={() => navigateToTransactions('receita')}
|
||||
>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-green-400/10 to-emerald-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
||||
<CardContent className="p-6 relative">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="p-3 bg-green-500 rounded-full shadow-lg">
|
||||
<ArrowUpIcon className="h-6 w-6 text-white" />
|
||||
</div>
|
||||
<div className="text-xs font-bold px-3 py-1 bg-green-500 text-white rounded-full shadow-sm">
|
||||
+5%
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm font-semibold text-green-700">Receitas</p>
|
||||
<p className="text-3xl font-bold text-green-600">
|
||||
{resumo ? formatCurrency(resumo.totalReceitas) : 'R$ 0,00'}
|
||||
</p>
|
||||
<p className="text-xs text-green-600 opacity-80">Clique para ver detalhes</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<DashboardCharts
|
||||
monthlyData={monthlyData}
|
||||
categories={categories}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
|
||||
{/* Card Despesas - Clicável */}
|
||||
<Card
|
||||
className="relative overflow-hidden border-0 bg-gradient-to-br from-red-50 to-rose-100 shadow-lg hover:shadow-xl transition-all duration-300 cursor-pointer group transform hover:scale-105"
|
||||
onClick={() => navigateToTransactions('despesa')}
|
||||
>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-red-400/10 to-rose-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
||||
<CardContent className="p-6 relative">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="p-3 bg-red-500 rounded-full shadow-lg">
|
||||
<ArrowDownIcon className="h-6 w-6 text-white" />
|
||||
</div>
|
||||
<div className="text-xs font-bold px-3 py-1 bg-red-500 text-white rounded-full shadow-sm">
|
||||
-2%
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm font-semibold text-red-700">Despesas</p>
|
||||
<p className="text-3xl font-bold text-red-600">
|
||||
{resumo ? formatCurrency(totalDespesasGeral) : 'R$ 0,00'}
|
||||
</p>
|
||||
{resumo && resumo.totalCartoes > 0 && (
|
||||
<p className="text-xs text-red-500">Cartões: {formatCurrency(resumo.totalCartoes)}</p>
|
||||
)}
|
||||
<p className="text-xs text-red-600 opacity-80">Clique para ver detalhes</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Card Saldo */}
|
||||
<Card className="relative overflow-hidden border-0 bg-gradient-to-br from-blue-50 to-sky-100 shadow-lg hover:shadow-xl transition-all duration-300 group transform hover:scale-105">
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-blue-400/10 to-sky-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
||||
<CardContent className="p-6 relative">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="p-3 bg-blue-500 rounded-full shadow-lg">
|
||||
<CreditCardIcon className="h-6 w-6 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm font-semibold text-blue-700">Saldo</p>
|
||||
<p className={`text-3xl font-bold ${
|
||||
saldo >= 0 ? 'text-blue-600' : 'text-red-600'
|
||||
}`}>
|
||||
{resumo ? formatCurrency(saldo) : 'R$ 0,00'}
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Card Economia */}
|
||||
<Card className="relative overflow-hidden border-0 bg-gradient-to-br from-purple-50 to-violet-100 shadow-lg hover:shadow-xl transition-all duration-300 group transform hover:scale-105">
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-purple-400/10 to-violet-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
||||
<CardContent className="p-6 relative">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="p-3 bg-purple-500 rounded-full shadow-lg">
|
||||
<Target className="h-6 w-6 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm font-semibold text-purple-700">Economia</p>
|
||||
<p className="text-3xl font-bold text-purple-600">
|
||||
-22.2%
|
||||
</p>
|
||||
<p className="text-xs text-purple-500">{resumo ? formatCurrency(Math.abs(saldo)) : 'R$ 0,00'}</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
{/* 1. Receitas vs Despesas Chart */}
|
||||
<SimpleCard title="📈 Receitas vs Despesas" className="border-green-200">
|
||||
<MonthlyChart data={monthlyData} isLoading={isLoading} />
|
||||
</SimpleCard>
|
||||
|
||||
{/* 2. Gastos por Categoria */}
|
||||
<SimpleCard title="📊 Despesas por Categoria" className="border-orange-200">
|
||||
<CategoryChart categories={categories} isLoading={isLoading} />
|
||||
</SimpleCard>
|
||||
|
||||
{/* 3. Últimas Transações */}
|
||||
<SimpleCard title="📋 Últimas Transações" className="border-blue-200">
|
||||
<TransactionsTable
|
||||
transactions={transactions.slice(0, 5)}
|
||||
isLoading={transactionsLoading}
|
||||
showPagination={false}
|
||||
onEdit={() => {}}
|
||||
onDelete={() => {}}
|
||||
/>
|
||||
</SimpleCard>
|
||||
</div>
|
||||
<DashboardRecentTransactions
|
||||
transactions={transactions}
|
||||
transactionsLoading={transactionsLoading}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user