Fix: Persist metas, fix category display, add category labels

- Fixed the issue where metas disappeared after navigating away and back.
- Fixed the category display in the dashboard and category menu.
- Added category names to the pie chart labels.
This commit is contained in:
gpt-engineer-app[bot] 2025-05-19 12:55:27 +00:00
parent e66b0342b7
commit e288c9262d
6 changed files with 97 additions and 43 deletions

View File

@ -1,7 +1,7 @@
import React from 'react';
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { PieChart, Pie, Cell, ResponsiveContainer, Legend, Tooltip } from 'recharts';
import { PieChart, Pie, Cell, ResponsiveContainer, Legend, Tooltip, Label } from 'recharts';
import { CategorySummary } from '@/types/financialTypes';
interface CategoryChartProps {
@ -13,10 +13,10 @@ const CategoryChart: React.FC<CategoryChartProps> = ({ categories, isLoading = f
// Filter out any categories with zero value to prevent rendering issues
const validCategories = categories.filter(cat => cat.valor > 0);
const renderCustomizedLabel = ({ cx, cy, midAngle, innerRadius, outerRadius, percent }: any) => {
if (percent === 0) return null;
const renderCustomizedLabel = ({ cx, cy, midAngle, innerRadius, outerRadius, percent, index, name }: any) => {
if (percent < 0.05) return null; // Não mostrar texto para fatias muito pequenas
const radius = innerRadius + (outerRadius - innerRadius) * 0.5;
const radius = innerRadius + (outerRadius - innerRadius) * 0.7;
const x = cx + radius * Math.cos(-midAngle * Math.PI / 180);
const y = cy + radius * Math.sin(-midAngle * Math.PI / 180);
@ -25,7 +25,7 @@ const CategoryChart: React.FC<CategoryChartProps> = ({ categories, isLoading = f
x={x}
y={y}
fill="#fff"
textAnchor="middle"
textAnchor={x > cx ? 'start' : 'end'}
dominantBaseline="central"
className="text-xs font-medium"
>
@ -41,6 +41,38 @@ const CategoryChart: React.FC<CategoryChartProps> = ({ categories, isLoading = f
}).format(value);
};
const renderCustomLegend = (props: any) => {
const { payload } = props;
return (
<div className="grid grid-cols-2 gap-x-4 gap-y-1 mt-2 text-xs">
{payload.map((entry: any, index: number) => (
<div key={`item-${index}`} className="flex items-center">
<div
className="w-3 h-3 mr-2"
style={{ backgroundColor: entry.color }}
/>
<span className="truncate">{entry.value}</span>
</div>
))}
</div>
);
};
const renderCustomTooltip = ({ active, payload }: any) => {
if (active && payload && payload.length) {
return (
<div className="bg-background border border-border p-2 rounded shadow-lg">
<p className="font-medium">{payload[0].name}</p>
<p>{formatCurrency(payload[0].value)}</p>
<p>{`${(payload[0].payload.percentage * 100).toFixed(1)}%`}</p>
</div>
);
}
return null;
};
return (
<Card className="dashboard-card h-full">
<CardHeader>
@ -69,16 +101,14 @@ const CategoryChart: React.FC<CategoryChartProps> = ({ categories, isLoading = f
outerRadius={80}
fill="#8884d8"
dataKey="valor"
nameKey="categoria"
>
{validCategories.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
<Tooltip
formatter={(value: number) => formatCurrency(value)}
labelFormatter={(index) => validCategories[index].categoria}
/>
<Legend />
<Tooltip content={renderCustomTooltip} />
<Legend content={renderCustomLegend} />
</PieChart>
</ResponsiveContainer>
</div>

View File

@ -28,6 +28,19 @@ const CategoriasPage = () => {
const [tipoFiltro, setTipoFiltro] = useState<string>('despesa');
const { toast } = useToast();
// Atualizar o userId no localStorage para garantir consistência
useEffect(() => {
const storedUserId = localStorage.getItem('userId');
const finDashUser = localStorage.getItem('finDashUser');
if (!storedUserId && finDashUser) {
localStorage.setItem('userId', finDashUser);
} else if (!storedUserId && !finDashUser) {
const defaultId = '9f267008-9128-4a2f-b730-de0a0b5602a9';
localStorage.setItem('userId', defaultId);
}
}, []);
const loadCategories = async (tipo: string) => {
try {
setIsLoading(true);

View File

@ -18,6 +18,19 @@ const Dashboard = () => {
const [totals, setTotals] = useState({ receitas: 0, despesas: 0, saldo: 0 });
const { toast } = useToast();
// Atualizar o userId no localStorage para garantir consistência
useEffect(() => {
const storedUserId = localStorage.getItem('userId');
const finDashUser = localStorage.getItem('finDashUser');
if (!storedUserId && finDashUser) {
localStorage.setItem('userId', finDashUser);
} else if (!storedUserId && !finDashUser) {
const defaultId = '9f267008-9128-4a2f-b730-de0a0b5602a9';
localStorage.setItem('userId', defaultId);
}
}, []);
useEffect(() => {
async function loadData() {
try {

View File

@ -10,7 +10,7 @@ import { Card } from '@/components/ui/card';
import { ResultadoMeta } from '@/types/financialTypes';
import { useToast } from '@/components/ui/use-toast';
import { calcularResultadosMetas, getMeta } from '@/services/metasService';
import { PlusCircle, Check } from 'lucide-react';
import { PlusCircle } from 'lucide-react';
import {
Dialog,
DialogContent,
@ -29,13 +29,24 @@ const MetasPage = () => {
const [valorMetaAtual, setValorMetaAtual] = useState(0);
useEffect(() => {
// Obter userId do localStorage (temporário até termos autenticação completa)
const user = localStorage.getItem('finDashUser');
if (user) {
setUserId(user);
loadData(user);
// Obter userId armazenado no localStorage
const storedUserId = localStorage.getItem('userId');
if (storedUserId) {
setUserId(storedUserId);
loadData(storedUserId);
} else {
setIsLoading(false);
// Tentar usar finDashUser como fallback
const finDashUser = localStorage.getItem('finDashUser');
if (finDashUser) {
setUserId(finDashUser);
loadData(finDashUser);
} else {
// Caso não encontre nenhum ID de usuário, usar um ID padrão temporário
const defaultId = '9f267008-9128-4a2f-b730-de0a0b5602a9';
localStorage.setItem('userId', defaultId);
setUserId(defaultId);
loadData(defaultId);
}
}
}, []);

View File

@ -1,23 +1,6 @@
import { supabase } from '@/integrations/supabase/client';
export interface Meta {
id: string;
user_id: string;
mes: number;
ano: number;
valor_meta: number;
created_at?: string;
}
export interface ResultadoMeta {
mes: number;
ano: number;
valor_meta: number;
economia_real: number;
percentual_atingido: number;
meta_batida: boolean;
}
import { Meta, ResultadoMeta } from '@/types/financialTypes';
// Obter todas as metas do usuário
export const getMetas = async (userId: string): Promise<Meta[]> => {
@ -55,9 +38,14 @@ export const getMeta = async (userId: string, mes: number, ano: number): Promise
};
// Criar ou atualizar meta
export const salvarMeta = async (meta: Partial<Meta>): Promise<Meta> => {
export const salvarMeta = async (meta: {
user_id: string,
mes: number,
ano: number,
valor_meta: number
}): Promise<Meta> => {
// Verificar se já existe uma meta para este mês/ano
const existingMeta = await getMeta(meta.user_id as string, meta.mes as number, meta.ano as number);
const existingMeta = await getMeta(meta.user_id, meta.mes, meta.ano);
if (existingMeta) {
// Atualizar meta existente
@ -128,11 +116,11 @@ export const calcularResultadosMetas = async (userId: string): Promise<Resultado
// Calcular receitas e despesas do mês
const receitas = transacoes
.filter(t => t.tipo === 'receita')
.filter(t => t.tipo?.toLowerCase() === 'receita')
.reduce((sum, t) => sum + Number(t.valor || 0), 0);
const despesas = transacoes
.filter(t => t.tipo === 'despesa')
.filter(t => t.tipo?.toLowerCase() === 'despesa')
.reduce((sum, t) => sum + Number(t.valor || 0), 0);
// Calcular economia real

View File

@ -1,6 +1,5 @@
import { supabase } from "@/integrations/supabase/client";
import { Transaction } from "@/types/financialTypes";
import { Transaction, CategorySummary } from "@/types/financialTypes";
export async function getTransacoes(): Promise<Transaction[]> {
console.log("Buscando transações do Supabase...");
@ -84,7 +83,7 @@ export async function getTransactionSummary() {
}
}
export async function getCategorySummary(tipoFiltro: string = 'all') {
export async function getCategorySummary(tipoFiltro: string = 'despesa') {
console.log(`Buscando resumo de categorias para tipo: ${tipoFiltro}`);
// Obter o ID do usuário atual do localStorage
@ -117,7 +116,7 @@ export async function getCategorySummary(tipoFiltro: string = 'all') {
// Agrupar por categoria
const categorias: Record<string, number> = {};
filteredData.forEach((item: any) => {
if (item.categoria && item.valor) {
if (item.valor) {
const categoriaKey = item.categoria || 'Outros';
if (!categorias[categoriaKey]) {
categorias[categoriaKey] = 0;