Fix: Dashboard and Transactions page improvements

- Fixed missing "Despesas" category in the pie chart.
- Re-added month/year filter to Dashboard and Transactions pages.
- Improved responsiveness of summary cards on smaller screens.
This commit is contained in:
gpt-engineer-app[bot] 2025-06-25 18:18:03 +00:00
parent 1d1135cbe4
commit 7b39bcd5aa
4 changed files with 56 additions and 27 deletions

View File

@ -16,7 +16,7 @@ const DashboardSummaryCards: React.FC<DashboardSummaryCardsProps> = ({ resumo, f
const saldo = resumo ? resumo.totalReceitas - resumo.totalDespesas - (resumo.totalCartoes || 0) : 0; const saldo = resumo ? resumo.totalReceitas - resumo.totalDespesas - (resumo.totalCartoes || 0) : 0;
return ( return (
<div className="grid gap-4 sm:gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-4"> <div className="grid gap-4 sm:gap-6 grid-cols-1 sm:grid-cols-2 xl:grid-cols-4">
{/* Card Receitas - Clicável */} {/* Card Receitas - Clicável */}
<Card <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" 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"
@ -34,7 +34,7 @@ const DashboardSummaryCards: React.FC<DashboardSummaryCardsProps> = ({ resumo, f
</div> </div>
<div className="space-y-1 sm:space-y-2"> <div className="space-y-1 sm:space-y-2">
<p className="text-xs sm:text-sm font-semibold text-green-700">Receitas</p> <p className="text-xs sm:text-sm font-semibold text-green-700">Receitas</p>
<p className="text-lg sm:text-2xl lg:text-3xl font-bold text-green-600 break-words"> <p className="text-base sm:text-xl xl:text-2xl font-bold text-green-600 leading-tight">
{resumo ? formatCurrency(resumo.totalReceitas) : 'R$ 0,00'} {resumo ? formatCurrency(resumo.totalReceitas) : 'R$ 0,00'}
</p> </p>
<p className="text-xs text-green-600 opacity-80">Clique para ver detalhes</p> <p className="text-xs text-green-600 opacity-80">Clique para ver detalhes</p>
@ -59,7 +59,7 @@ const DashboardSummaryCards: React.FC<DashboardSummaryCardsProps> = ({ resumo, f
</div> </div>
<div className="space-y-1 sm:space-y-2"> <div className="space-y-1 sm:space-y-2">
<p className="text-xs sm:text-sm font-semibold text-red-700">Despesas</p> <p className="text-xs sm:text-sm font-semibold text-red-700">Despesas</p>
<p className="text-lg sm:text-2xl lg:text-3xl font-bold text-red-600 break-words"> <p className="text-base sm:text-xl xl:text-2xl font-bold text-red-600 leading-tight">
{resumo ? formatCurrency(resumo.totalDespesas) : 'R$ 0,00'} {resumo ? formatCurrency(resumo.totalDespesas) : 'R$ 0,00'}
</p> </p>
{resumo && resumo.totalCartoes > 0 && ( {resumo && resumo.totalCartoes > 0 && (
@ -86,7 +86,7 @@ const DashboardSummaryCards: React.FC<DashboardSummaryCardsProps> = ({ resumo, f
</div> </div>
<div className="space-y-1 sm:space-y-2"> <div className="space-y-1 sm:space-y-2">
<p className="text-xs sm:text-sm font-semibold text-blue-700">Saldo</p> <p className="text-xs sm:text-sm font-semibold text-blue-700">Saldo</p>
<p className={`text-lg sm:text-2xl lg:text-3xl font-bold break-words ${ <p className={`text-base sm:text-xl xl:text-2xl font-bold leading-tight ${
saldo >= 0 ? 'text-blue-600' : 'text-red-600' saldo >= 0 ? 'text-blue-600' : 'text-red-600'
}`}> }`}>
{resumo ? formatCurrency(saldo) : 'R$ 0,00'} {resumo ? formatCurrency(saldo) : 'R$ 0,00'}
@ -106,10 +106,10 @@ const DashboardSummaryCards: React.FC<DashboardSummaryCardsProps> = ({ resumo, f
</div> </div>
<div className="space-y-1 sm:space-y-2"> <div className="space-y-1 sm:space-y-2">
<p className="text-xs sm:text-sm font-semibold text-purple-700">Economia</p> <p className="text-xs sm:text-sm font-semibold text-purple-700">Economia</p>
<p className="text-lg sm:text-2xl lg:text-3xl font-bold text-purple-600 break-words"> <p className="text-base sm:text-xl xl:text-2xl font-bold text-purple-600 leading-tight">
-22.2% -22.2%
</p> </p>
<p className="text-xs text-purple-500 break-words">{resumo ? formatCurrency(Math.abs(saldo)) : 'R$ 0,00'}</p> <p className="text-xs text-purple-500 leading-tight">{resumo ? formatCurrency(Math.abs(saldo)) : 'R$ 0,00'}</p>
</div> </div>
</CardContent> </CardContent>
</Card> </Card>

View File

@ -9,17 +9,21 @@ import DashboardLoadingState from "@/components/dashboard/DashboardLoadingState"
import DashboardSummaryCards from "@/components/dashboard/DashboardSummaryCards"; import DashboardSummaryCards from "@/components/dashboard/DashboardSummaryCards";
import DashboardCharts from "@/components/dashboard/DashboardCharts"; import DashboardCharts from "@/components/dashboard/DashboardCharts";
import DashboardRecentTransactions from "@/components/dashboard/DashboardRecentTransactions"; import DashboardRecentTransactions from "@/components/dashboard/DashboardRecentTransactions";
import { MonthFilter } from "@/components/filters/MonthFilter";
const Dashboard = () => { const Dashboard = () => {
const [resumo, setResumo] = useState<ResumoFinanceiro | null>(null); const [resumo, setResumo] = useState<ResumoFinanceiro | null>(null);
const [categories, setCategories] = useState<CategorySummary[]>([]); const [categories, setCategories] = useState<CategorySummary[]>([]);
const [monthlyData, setMonthlyData] = useState<MonthlyData[]>([]); const [monthlyData, setMonthlyData] = useState<MonthlyData[]>([]);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [selectedMonth, setSelectedMonth] = useState(() => {
const now = new Date();
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
});
const { toast } = useToast(); const { toast } = useToast();
const getCurrentMonth = () => { const getCurrentMonth = () => {
const now = new Date(); return selectedMonth;
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
}; };
const { const {
@ -69,7 +73,7 @@ const Dashboard = () => {
useEffect(() => { useEffect(() => {
loadResumo(); loadResumo();
}, [toast]); }, [toast, selectedMonth]);
if (isLoading) { if (isLoading) {
return <DashboardLoadingState />; return <DashboardLoadingState />;
@ -79,6 +83,15 @@ const Dashboard = () => {
<div className="space-y-6" data-tour="dashboard-content"> <div className="space-y-6" data-tour="dashboard-content">
<DashboardHeader /> <DashboardHeader />
{/* Filtro de Mês/Ano */}
<div className="flex justify-between items-center">
<h2 className="text-2xl font-bold">Dashboard</h2>
<MonthFilter
selectedMonth={selectedMonth}
onMonthChange={setSelectedMonth}
/>
</div>
<DashboardSummaryCards <DashboardSummaryCards
resumo={resumo} resumo={resumo}
formatCurrency={formatCurrency} formatCurrency={formatCurrency}

View File

@ -1,5 +1,5 @@
import { useEffect } from 'react'; import { useEffect, useState } from 'react';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import { useTransactions } from '@/hooks/useTransactions'; import { useTransactions } from '@/hooks/useTransactions';
import { TransactionSummaryCards } from '@/components/transacoes/TransactionSummaryCards'; import { TransactionSummaryCards } from '@/components/transacoes/TransactionSummaryCards';
@ -7,9 +7,15 @@ import { TransactionDialogs } from '@/components/transacoes/TransactionDialogs';
import { TransactionHeader } from '@/components/transacoes/TransactionHeader'; import { TransactionHeader } from '@/components/transacoes/TransactionHeader';
import TransactionsTable from '@/components/dashboard/TransactionsTable'; import TransactionsTable from '@/components/dashboard/TransactionsTable';
import { SimpleCard } from "@/components/ui/simple-card"; import { SimpleCard } from "@/components/ui/simple-card";
import { MonthFilter } from "@/components/filters/MonthFilter";
const Transacoes = () => { const Transacoes = () => {
const location = useLocation(); const location = useLocation();
const [selectedMonth, setSelectedMonth] = useState(() => {
const now = new Date();
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
});
const { const {
transactions, transactions,
isLoading, isLoading,
@ -31,7 +37,7 @@ const Transacoes = () => {
handleOpenDialog, handleOpenDialog,
handleOpenCartaoCreditoDialog, handleOpenCartaoCreditoDialog,
loadTransactions loadTransactions
} = useTransactions(); } = useTransactions({ monthFilter: selectedMonth });
// Aplicar filtro inicial se vier do dashboard // Aplicar filtro inicial se vier do dashboard
useEffect(() => { useEffect(() => {
@ -54,6 +60,14 @@ const Transacoes = () => {
return ( return (
<div className="space-y-6"> <div className="space-y-6">
<div className="flex justify-between items-center">
<h1 className="text-2xl font-bold">Transações</h1>
<MonthFilter
selectedMonth={selectedMonth}
onMonthChange={setSelectedMonth}
/>
</div>
<TransactionHeader <TransactionHeader
onOpenDialog={handleOpenDialog} onOpenDialog={handleOpenDialog}
onOpenCartaoCreditoDialog={handleOpenCartaoCreditoDialog} onOpenCartaoCreditoDialog={handleOpenCartaoCreditoDialog}

View File

@ -1,3 +1,4 @@
import { supabase } from "@/integrations/supabase/client"; import { supabase } from "@/integrations/supabase/client";
import { CategorySummary, MonthlyData, ResumoFinanceiro } from "@/types/financialTypes"; import { CategorySummary, MonthlyData, ResumoFinanceiro } from "@/types/financialTypes";
import { getUserEmail, getUserGroups } from "./baseService"; import { getUserEmail, getUserGroups } from "./baseService";
@ -164,37 +165,38 @@ export async function getCategorySummary(tipo: string = 'despesa'): Promise<Cate
return []; return [];
} }
console.log(`📋 [getCategorySummary] Dados recebidos:`, data.map(d => ({ categoria: d.categoria, valor: d.valor, tipo: d.tipo }))); console.log(`📋 [getCategorySummary] Dados recebidos (${data.length} registros):`, data.slice(0, 5));
// Group by category and sum values // Group by category and sum values
const categoryMap: { [key: string]: number } = {}; const categoryMap: { [key: string]: number } = {};
let total = 0; let total = 0;
data.forEach((transaction) => { data.forEach((transaction) => {
// Usar a categoria exata ou 'Outros' se for null/undefined/vazia // Tratar categorias vazias, nulas ou indefinidas
const categoria = transaction.categoria && transaction.categoria.trim() !== '' let categoria = 'Outros';
? transaction.categoria.trim()
: 'Outros'; if (transaction.categoria && typeof transaction.categoria === 'string') {
const cleanCategory = transaction.categoria.trim();
if (cleanCategory.length > 0) {
categoria = cleanCategory;
}
}
const valor = Math.abs(Number(transaction.valor || 0)); const valor = Math.abs(Number(transaction.valor || 0));
console.log(`📋 [getCategorySummary] Processando: categoria='${categoria}', valor=${valor}`); if (valor > 0) {
categoryMap[categoria] = (categoryMap[categoria] || 0) + valor; categoryMap[categoria] = (categoryMap[categoria] || 0) + valor;
total += valor; total += valor;
}
}); });
console.log(`📋 [getCategorySummary] Mapeamento final de categorias:`, categoryMap); console.log(`📋 [getCategorySummary] Categorias encontradas:`, Object.keys(categoryMap));
console.log(`📋 [getCategorySummary] Total geral: ${total}`); console.log(`📋 [getCategorySummary] Total geral: ${total}`);
// Enhanced color palette with more distinct colors - avoiding white/light colors // Enhanced color palette with more distinct colors - avoiding white/light colors
const colors = [ const colors = [
'#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7',
'#DDA0DD', '#98D8C8', '#F7DC6F', '#BB8FCE', '#85C1E9', '#DDA0DD', '#98D8C8', '#F7DC6F', '#BB8FCE', '#85C1E9'
'#F8C471', '#82E0AA', '#F1948A', '#85C1E9', '#D7BDE2',
'#A3E4D7', '#FAD7A0', '#D5A6BD', '#AED6F1', '#A9DFBF',
'#FF8A80', '#80CBC4', '#81C784', '#FFB74D', '#F48FB1',
'#CE93D8', '#90CAF9', '#A5D6A7', '#FFCC02', '#FFAB40'
]; ];
// Convert to CategorySummary array with colors // Convert to CategorySummary array with colors
@ -207,7 +209,7 @@ export async function getCategorySummary(tipo: string = 'despesa'): Promise<Cate
})) }))
.sort((a, b) => b.valor - a.valor); .sort((a, b) => b.valor - a.valor);
console.log(`📋 [getCategorySummary] ${categoryArray.length} categorias processadas:`, categoryArray); console.log(`📋 [getCategorySummary] Resultado final:`, categoryArray);
return categoryArray; return categoryArray;
} catch (error) { } catch (error) {
console.error('💥 [getCategorySummary] Erro geral:', error); console.error('💥 [getCategorySummary] Erro geral:', error);