From 2c2dfba45357bbd205a59145ae55a2779c911ae0 Mon Sep 17 00:00:00 2001 From: Lucas Faria Mendes Date: Fri, 14 Aug 2026 15:06:35 -0300 Subject: remake Signed-off-by: Lucas Faria Mendes --- src/components/ui/expenses-chart.tsx | 187 +++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 src/components/ui/expenses-chart.tsx (limited to 'src/components/ui/expenses-chart.tsx') diff --git a/src/components/ui/expenses-chart.tsx b/src/components/ui/expenses-chart.tsx new file mode 100644 index 0000000..efd5dee --- /dev/null +++ b/src/components/ui/expenses-chart.tsx @@ -0,0 +1,187 @@ +import React from 'react'; +import { StyleSheet, Text, View } from 'react-native'; +import { PieChart } from 'react-native-gifted-charts'; +import { Colors, Radius, Spacing, Typography } from '@/constants/theme'; +import type { DashboardExpenseItem } from '@/types'; +import { Card, SectionLabel } from './primitives'; + +interface ExpensesChartProps { + recurring: DashboardExpenseItem[]; + oneTime: DashboardExpenseItem[]; +} + +const CATEGORY_COLORS: Record = { + moradia: '#4C6EF5', + alimentação: '#FA5252', + transporte: '#FAB005', + saúde: '#12B886', + educação: '#BE4BDB', + lazer: '#FF922B', + serviços: '#228BE6', + outros: '#868E96', +}; + +const CATEGORY_LABELS: Record = { + moradia: 'Moradia', + alimentação: 'Alimentação', + transporte: 'Transporte', + saúde: 'Saúde', + educação: 'Educação', + lazer: 'Lazer', + serviços: 'Serviços', + outros: 'Outros', +}; + +export function ExpensesChart({ recurring, oneTime }: ExpensesChartProps) { + const categoryTotals: Record = {}; + + const allExpenses = [ + ...recurring.filter(e => e.is_active && e.type === 'expense'), + ...oneTime.filter(e => e.type === 'expense') + ]; + + if (allExpenses.length === 0) { + return ( + + + + Não há despesas para analisar neste mês. + + + ); + } + + let totalExpenses = 0; + allExpenses.forEach(e => { + if (!categoryTotals[e.category]) { + categoryTotals[e.category] = 0; + } + categoryTotals[e.category] += e.amount; + totalExpenses += e.amount; + }); + + const pieData = Object.keys(categoryTotals) + .map(category => ({ + value: categoryTotals[category], + color: CATEGORY_COLORS[category] || CATEGORY_COLORS.outros, + label: CATEGORY_LABELS[category] || 'Outros', + percentage: ((categoryTotals[category] / totalExpenses) * 100).toFixed(0) + '%', + })) + .sort((a, b) => b.value - a.value); + + return ( + + + + + + { + return ( + + + {totalExpenses.toLocaleString('pt-BR', { + style: 'currency', + currency: 'BRL', + minimumFractionDigits: 0, + maximumFractionDigits: 0, + })} + + Total + + ); + }} + /> + + + + {pieData.map((item, index) => ( + + + + {item.label} + + {item.value.toLocaleString('pt-BR', { + style: 'currency', + currency: 'BRL', + })} ({item.percentage}) + + + + ))} + + + + ); +} + +const styles = StyleSheet.create({ + card: { + gap: Spacing.md, + }, + emptyContainer: { + paddingVertical: Spacing.xl, + alignItems: 'center', + justifyContent: 'center', + }, + emptyText: { + ...Typography.body, + color: Colors.textMuted, + }, + chartRow: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + }, + chartWrapper: { + alignItems: 'center', + justifyContent: 'center', + }, + centerLabel: { + alignItems: 'center', + justifyContent: 'center', + }, + centerLabelValue: { + ...Typography.body, + fontFamily: 'DMSans_700Bold', + color: Colors.textPrimary, + fontSize: 14, + }, + centerLabelText: { + ...Typography.caption, + color: Colors.textSecondary, + fontSize: 10, + }, + legendContainer: { + flex: 1, + marginLeft: Spacing.lg, + gap: Spacing.sm, + }, + legendItem: { + flexDirection: 'row', + alignItems: 'center', + }, + legendDot: { + width: 10, + height: 10, + borderRadius: 5, + marginRight: Spacing.xs, + }, + legendTextWrapper: { + flex: 1, + }, + legendLabel: { + ...Typography.caption, + color: Colors.textSecondary, + }, + legendValue: { + ...Typography.caption, + color: Colors.textPrimary, + fontFamily: 'DMSans_600SemiBold', + }, +}); -- cgit v1.2.3