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:
parent
e66b0342b7
commit
e288c9262d
@ -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>
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user