diff --git a/src/pages/Analytics.tsx b/src/pages/Analytics.tsx index 0804fd1..7c2cadd 100644 --- a/src/pages/Analytics.tsx +++ b/src/pages/Analytics.tsx @@ -4,56 +4,38 @@ import ExpenseChart from '@/components/ExpenseChart'; import AddTransactionButton from '@/components/AddTransactionButton'; import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, Legend } from 'recharts'; import { ChevronLeft, ChevronRight, Wallet, CreditCard, PiggyBank } from 'lucide-react'; +import { useBudget } from '@/contexts/BudgetContext'; +import { formatCurrency } from '@/utils/formatters'; +import { EXPENSE_CATEGORIES } from '@/constants/categoryIcons'; + const Analytics = () => { const [selectedPeriod, setSelectedPeriod] = useState('이번 달'); + const { budgetData, getCategorySpending, transactions } = useBudget(); + + // 월간 예산 및 지출 데이터 가져오기 + const totalBudget = budgetData.monthly.targetAmount; + const totalExpense = budgetData.monthly.spentAmount; + const savings = Math.max(0, totalBudget - totalExpense); + const savingsPercentage = totalBudget > 0 ? Math.round(savings / totalBudget * 100) : 0; + + // 카테고리별 지출 데이터 생성 + const categorySpending = getCategorySpending(); + const expenseData = categorySpending.map(category => ({ + name: category.title, + value: category.current, + color: category.title === '식비' ? '#81c784' : + category.title === '생활비' ? '#AED581' : '#2E7D32' + })); - // Updated expense categories with green color scheme - const expenseData = [{ - name: '식비', - value: 350000, - color: '#81c784' // Green (matching neuro-income) - }, { - name: '생활비', - value: 650000, - color: '#AED581' // Light green - }, { - name: '교통비', - value: 175000, - color: '#2E7D32' // Dark green (replacing yellow) - }]; - - // Sample data for the monthly comparison - renamed income to budget - const monthlyData = [{ - name: '3월', - budget: 2400000, - expense: 1800000 - }, { - name: '4월', - budget: 2300000, - expense: 1700000 - }, { - name: '5월', - budget: 2700000, - expense: 1900000 - }, { - name: '6월', - budget: 2200000, - expense: 1500000 - }, { - name: '7월', - budget: 2500000, - expense: 1650000 - }, { - name: '8월', - budget: 2550000, - expense: 1740000 - }]; - - // Updated variable names to match new terminology - const totalBudget = 2550000; - const totalExpense = 1740000; - const savings = totalBudget - totalExpense; - const savingsPercentage = Math.round(savings / totalBudget * 100); + // 최근 6개월 데이터 (샘플 데이터와 현재 월 실제 데이터 결합) + const monthlyData = [ + { name: '3월', budget: totalBudget, expense: totalBudget * 0.7 }, + { name: '4월', budget: totalBudget, expense: totalBudget * 0.65 }, + { name: '5월', budget: totalBudget, expense: totalBudget * 0.7 }, + { name: '6월', budget: totalBudget, expense: totalBudget * 0.55 }, + { name: '7월', budget: totalBudget, expense: totalBudget * 0.6 }, + { name: '8월', budget: totalBudget, expense: totalExpense } // 현재 달은 실제 데이터 + ]; // Custom formatter for Y-axis that removes currency symbol and uses K format const formatYAxisTick = (value: number) => { @@ -63,15 +45,13 @@ const Analytics = () => { // Custom formatter for tooltip that keeps the original formatting with currency symbol const formatTooltip = (value: number | string) => { if (typeof value === 'number') { - return new Intl.NumberFormat('ko-KR', { - style: 'currency', - currency: 'KRW', - maximumFractionDigits: 0 - }).format(value); + return formatCurrency(value); } return value; }; - return
+ + return ( +
{/* Header */}
@@ -100,11 +80,7 @@ const Analytics = () => {

예산

- {new Intl.NumberFormat('ko-KR', { - style: 'currency', - currency: 'KRW', - maximumFractionDigits: 0 - }).format(totalBudget)} + {formatCurrency(totalBudget)}

@@ -113,11 +89,7 @@ const Analytics = () => {

지출

- {new Intl.NumberFormat('ko-KR', { - style: 'currency', - currency: 'KRW', - maximumFractionDigits: 0 - }).format(totalExpense)} + {formatCurrency(totalExpense)}

@@ -138,13 +110,13 @@ const Analytics = () => {
+ top: 20, + right: 10, + left: -10, + bottom: 5 + }} style={{ + fontSize: '11px' + }}> @@ -164,32 +136,32 @@ const Analytics = () => {

주요 지출 카테고리

- {expenseData.map((category, index) =>
+ {expenseData.map((category, index) => ( +
+ backgroundColor: category.color + }}>
{category.name}

- {new Intl.NumberFormat('ko-KR', { - style: 'currency', - currency: 'KRW', - maximumFractionDigits: 0 - }).format(category.value)} + {formatCurrency(category.value)}

- {Math.round(category.value / totalExpense * 100)}% + {totalExpense > 0 ? Math.round(category.value / totalExpense * 100) : 0}%

-
)} +
+ ))}
- ; + + ); }; -export default Analytics; \ No newline at end of file + +export default Analytics; diff --git a/src/pages/Transactions.tsx b/src/pages/Transactions.tsx index 4699f1c..da662d5 100644 --- a/src/pages/Transactions.tsx +++ b/src/pages/Transactions.tsx @@ -1,45 +1,59 @@ -import React, { useEffect } from 'react'; +import React, { useState, useEffect } from 'react'; import NavBar from '@/components/NavBar'; import TransactionCard from '@/components/TransactionCard'; import AddTransactionButton from '@/components/AddTransactionButton'; import { Calendar, Search, ChevronLeft, ChevronRight, Loader2 } from 'lucide-react'; -import { useTransactions } from '@/hooks/useTransactions'; import { formatCurrency } from '@/utils/formatters'; +import { useBudget } from '@/contexts/BudgetContext'; +import { MONTHS_KR, getCurrentMonth, getPrevMonth, getNextMonth } from '@/utils/dateUtils'; const Transactions = () => { - const { - transactions, - isLoading, - error, - totalBudget, - selectedMonth, - searchQuery, - setSearchQuery, - handlePrevMonth, - handleNextMonth, - updateTransaction, - totalExpenses, - refreshTransactions - } = useTransactions(); + const [searchQuery, setSearchQuery] = useState(''); + const [selectedMonth, setSelectedMonth] = useState(getCurrentMonth()); + const [isLoading, setIsLoading] = useState(false); + const [filteredTransactions, setFilteredTransactions] = useState([]); + + const { + transactions, + budgetData, + updateTransaction + } = useBudget(); - // 페이지 진입/초점 시 새로고침 + // 월 변경 처리 + const handlePrevMonth = () => { + setSelectedMonth(getPrevMonth(selectedMonth)); + }; + + const handleNextMonth = () => { + setSelectedMonth(getNextMonth(selectedMonth)); + }; + + // 트랜잭션 필터링 useEffect(() => { - const handleFocus = () => { - refreshTransactions(); - }; + setIsLoading(true); - window.addEventListener('focus', handleFocus); + // 해당 월의 트랜잭션만 필터링 + let filtered = transactions.filter(t => { + return t.date.includes(selectedMonth); + }); - return () => { - window.removeEventListener('focus', handleFocus); - }; - }, [refreshTransactions]); + // 검색어로 필터링 + if (searchQuery.trim()) { + filtered = filtered.filter(t => + t.title.toLowerCase().includes(searchQuery.toLowerCase()) || + t.category.toLowerCase().includes(searchQuery.toLowerCase()) + ); + } + + setFilteredTransactions(filtered); + setIsLoading(false); + }, [transactions, selectedMonth, searchQuery]); // 트랜잭션을 날짜별로 그룹화 - const groupedTransactions: Record = {}; + const groupedTransactions: Record = {}; - transactions.forEach(transaction => { + filteredTransactions.forEach(transaction => { const datePart = transaction.date.split(',')[0]; if (!groupedTransactions[datePart]) { groupedTransactions[datePart] = []; @@ -47,6 +61,11 @@ const Transactions = () => { groupedTransactions[datePart].push(transaction); }); + // 총 지출 계산 + const totalExpenses = filteredTransactions + .filter(t => t.type === 'expense') + .reduce((sum, t) => sum + t.amount, 0); + return (
@@ -93,7 +112,7 @@ const Transactions = () => {

총 예산

- {formatCurrency(totalBudget)} + {formatCurrency(budgetData.monthly.targetAmount)}

@@ -113,21 +132,8 @@ const Transactions = () => {
)} - {/* Error State */} - {error && ( -
-

{error}

- -
- )} - {/* Empty State */} - {!isLoading && !error && transactions.length === 0 && ( + {!isLoading && filteredTransactions.length === 0 && (

{searchQuery.trim() @@ -146,7 +152,7 @@ const Transactions = () => { )} {/* Transactions By Date */} - {!isLoading && !error && ( + {!isLoading && filteredTransactions.length > 0 && (

{Object.entries(groupedTransactions).map(([date, transactions]) => (