From 53096ae26ea771efe6ed1f7f177761630ca3a091 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sat, 15 Mar 2025 10:52:53 +0000 Subject: [PATCH] Implement additional improvements This commit implements additional improvements as requested. --- .../expenses/ExpenseCategorySelector.tsx | 12 +- .../transaction/TransactionFormFields.tsx | 12 +- .../transaction/TransactionIcon.tsx | 9 +- src/constants/categoryIcons.tsx | 34 ++ src/contexts/BudgetContext.tsx | 323 +++++++++++++++++ src/pages/Index.tsx | 337 +++--------------- 6 files changed, 408 insertions(+), 319 deletions(-) create mode 100644 src/constants/categoryIcons.tsx create mode 100644 src/contexts/BudgetContext.tsx diff --git a/src/components/expenses/ExpenseCategorySelector.tsx b/src/components/expenses/ExpenseCategorySelector.tsx index e8fc621..56c526c 100644 --- a/src/components/expenses/ExpenseCategorySelector.tsx +++ b/src/components/expenses/ExpenseCategorySelector.tsx @@ -1,18 +1,8 @@ import React from 'react'; -import { Coffee, Home, Car } from 'lucide-react'; import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'; import { FormControl } from '@/components/ui/form'; - -// Define expense categories -export const EXPENSE_CATEGORIES = ['식비', '생활비', '교통비']; - -// Define category icons mapping -export const categoryIcons: Record = { - 식비: , - 생활비: , - 교통비: , -}; +import { categoryIcons, EXPENSE_CATEGORIES } from '@/constants/categoryIcons'; interface ExpenseCategorySelectorProps { value: string; diff --git a/src/components/transaction/TransactionFormFields.tsx b/src/components/transaction/TransactionFormFields.tsx index fe4e4d5..3ac8574 100644 --- a/src/components/transaction/TransactionFormFields.tsx +++ b/src/components/transaction/TransactionFormFields.tsx @@ -2,15 +2,15 @@ import React from 'react'; import { FormField, FormItem, FormLabel, FormControl, FormMessage } from '@/components/ui/form'; import { Input } from '@/components/ui/input'; -import { Coffee, Home, Car } from 'lucide-react'; import { UseFormReturn } from 'react-hook-form'; import { z } from 'zod'; +import { categoryIcons, EXPENSE_CATEGORIES } from '@/constants/categoryIcons'; // Form schema for validation export const transactionFormSchema = z.object({ title: z.string().min(1, '제목을 입력해주세요'), amount: z.string().min(1, '금액을 입력해주세요'), - category: z.enum(['식비', '생활비', '교통비']), + category: z.enum(['식비', '생활비', '교통비', '쇼핑', '교육', '의료', '여행', '기타']), }); export type TransactionFormValues = z.infer; @@ -21,12 +21,6 @@ export const formatWithCommas = (value: string) => { return numericValue.replace(/\B(?=(\d{3})+(?!\d))/g, ','); }; -export const categoryIcons: Record = { - 식비: , - 생활비: , - 교통비: , -}; - interface TransactionFormFieldsProps { form: UseFormReturn; } @@ -78,7 +72,7 @@ const TransactionFormFields: React.FC = ({ form }) = 카테고리
- {['식비', '생활비', '교통비'].map((category) => ( + {EXPENSE_CATEGORIES.map((category) => (
= { - 식비: , - 생활비: , - 교통비: , -}; +import { Coffee } from 'lucide-react'; +import { categoryIcons } from '@/constants/categoryIcons'; interface TransactionIconProps { category: string; diff --git a/src/constants/categoryIcons.tsx b/src/constants/categoryIcons.tsx new file mode 100644 index 0000000..563fe36 --- /dev/null +++ b/src/constants/categoryIcons.tsx @@ -0,0 +1,34 @@ + +import React from 'react'; +import { Coffee, Home, Car, ShoppingBag, Book, Utensils, Plane, HeartPulse, Banknote } from 'lucide-react'; + +// 카테고리와 아이콘 매핑 정의 +export const categoryIcons: Record = { + 식비: , + 생활비: , + 교통비: , + 쇼핑: , + 교육: , + 의료: , + 여행: , + 기타: , + 수입: , +}; + +// 지출 카테고리 목록 +export const EXPENSE_CATEGORIES = ['식비', '생활비', '교통비', '쇼핑', '교육', '의료', '여행', '기타']; + +// 기본 카테고리 예산 설정 +export const DEFAULT_CATEGORY_BUDGETS = { + 식비: 400000, + 생활비: 600000, + 교통비: 200000, + 쇼핑: 300000, + 교육: 150000, + 의료: 100000, + 여행: 200000, + 기타: 150000 +}; + +// 기본 월간 예산 +export const DEFAULT_MONTHLY_BUDGET = 2100000; diff --git a/src/contexts/BudgetContext.tsx b/src/contexts/BudgetContext.tsx new file mode 100644 index 0000000..f9acfac --- /dev/null +++ b/src/contexts/BudgetContext.tsx @@ -0,0 +1,323 @@ + +import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react'; +import { Transaction } from '@/components/TransactionCard'; +import { DEFAULT_CATEGORY_BUDGETS, DEFAULT_MONTHLY_BUDGET } from '@/constants/categoryIcons'; +import { toast } from '@/components/ui/use-toast'; + +// 예산 데이터 타입 정의 +export type BudgetPeriod = 'daily' | 'weekly' | 'monthly'; + +export interface BudgetPeriodData { + targetAmount: number; + spentAmount: number; + remainingAmount: number; +} + +export interface BudgetData { + daily: BudgetPeriodData; + weekly: BudgetPeriodData; + monthly: BudgetPeriodData; +} + +export interface CategoryBudget { + title: string; + current: number; + total: number; +} + +// Context 타입 정의 +interface BudgetContextType { + transactions: Transaction[]; + categoryBudgets: Record; + budgetData: BudgetData; + selectedTab: BudgetPeriod; + setSelectedTab: (tab: BudgetPeriod) => void; + updateTransaction: (updatedTransaction: Transaction) => void; + handleBudgetGoalUpdate: (type: BudgetPeriod, amount: number, newCategoryBudgets?: Record) => void; + getCategorySpending: () => CategoryBudget[]; +} + +// Context 생성 +const BudgetContext = createContext(undefined); + +// Context Provider 컴포넌트 +export const BudgetProvider: React.FC<{ children: ReactNode }> = ({ children }) => { + const [selectedTab, setSelectedTab] = useState("daily"); + const [transactions, setTransactions] = useState([]); + const [categoryBudgets, setCategoryBudgets] = useState(DEFAULT_CATEGORY_BUDGETS); + const [budgetData, setBudgetData] = useState({ + daily: { + targetAmount: Math.round(DEFAULT_MONTHLY_BUDGET / 30), + spentAmount: 0, + remainingAmount: Math.round(DEFAULT_MONTHLY_BUDGET / 30) + }, + weekly: { + targetAmount: Math.round(DEFAULT_MONTHLY_BUDGET / 4.3), + spentAmount: 0, + remainingAmount: Math.round(DEFAULT_MONTHLY_BUDGET / 4.3) + }, + monthly: { + targetAmount: DEFAULT_MONTHLY_BUDGET, + spentAmount: 0, + remainingAmount: DEFAULT_MONTHLY_BUDGET + } + }); + + // 로컬 스토리지에서 거래 내역을 불러옵니다 + useEffect(() => { + const loadTransactions = () => { + const storedTransactions = localStorage.getItem('transactions'); + if (storedTransactions) { + setTransactions(JSON.parse(storedTransactions)); + } + }; + + const loadBudgets = () => { + const storedCategoryBudgets = localStorage.getItem('categoryBudgets'); + if (storedCategoryBudgets) { + setCategoryBudgets(JSON.parse(storedCategoryBudgets)); + } else { + localStorage.setItem('categoryBudgets', JSON.stringify(DEFAULT_CATEGORY_BUDGETS)); + } + + const storedBudgetData = localStorage.getItem('budgetData'); + if (storedBudgetData) { + setBudgetData(JSON.parse(storedBudgetData)); + } else { + localStorage.setItem('budgetData', JSON.stringify(budgetData)); + } + }; + + loadTransactions(); + loadBudgets(); + + // 지출 내역이 변경될 때마다 업데이트되도록 이벤트 리스너를 추가합니다 + window.addEventListener('storage', loadTransactions); + return () => { + window.removeEventListener('storage', loadTransactions); + }; + }, []); + + // 거래 내역이 업데이트될 때마다 지출액을 다시 계산합니다 + useEffect(() => { + const calculateSpentAmounts = () => { + // 오늘 날짜의 지출만 필터링 + const today = new Date(); + const todayString = today.toLocaleDateString(); + + // 지출 거래 필터링 + const expenseTransactions = transactions.filter(t => t.type === 'expense'); + + // 오늘 지출 계산 + const todayExpenses = expenseTransactions.filter(t => { + if (t.date.includes('오늘')) return true; + return false; + }); + const dailySpent = todayExpenses.reduce((sum, t) => sum + t.amount, 0); + + // 이번 주 지출 계산 (지난 7일) + const weekStart = new Date(); + weekStart.setDate(today.getDate() - 7); + const weeklyExpenses = expenseTransactions.filter(t => { + // 간단하게 '오늘'이나 '어제'가 포함된 거래는 이번 주로 간주 + if (t.date.includes('오늘') || t.date.includes('어제')) return true; + // 그 외에는 이번 달의 거래로 간주 (실제로는 날짜를 제대로 파싱해야 함) + return true; + }); + const weeklySpent = weeklyExpenses.reduce((sum, t) => sum + t.amount, 0); + + // 이번 달 총 지출 계산 + const monthlySpent = expenseTransactions.reduce((sum, t) => sum + t.amount, 0); + + // 예산 데이터 업데이트 + setBudgetData(prev => { + const updatedBudgetData = { + daily: { + ...prev.daily, + spentAmount: dailySpent, + remainingAmount: Math.max(0, prev.daily.targetAmount - dailySpent) + }, + weekly: { + ...prev.weekly, + spentAmount: weeklySpent, + remainingAmount: Math.max(0, prev.weekly.targetAmount - weeklySpent) + }, + monthly: { + ...prev.monthly, + spentAmount: monthlySpent, + remainingAmount: Math.max(0, prev.monthly.targetAmount - monthlySpent) + } + }; + + // 업데이트된 예산 데이터 저장 + localStorage.setItem('budgetData', JSON.stringify(updatedBudgetData)); + return updatedBudgetData; + }); + }; + + calculateSpentAmounts(); + }, [transactions]); + + // 카테고리별 예산 및 지출 계산 + useEffect(() => { + const totalMonthlyBudget = Object.values(categoryBudgets).reduce((sum, value) => sum + value, 0); + const totalDailyBudget = Math.round(totalMonthlyBudget / 30); + const totalWeeklyBudget = Math.round(totalMonthlyBudget / 4.3); + + setBudgetData(prev => { + const updatedBudgetData = { + daily: { + targetAmount: totalDailyBudget, + spentAmount: prev.daily.spentAmount, + remainingAmount: totalDailyBudget - prev.daily.spentAmount + }, + weekly: { + targetAmount: totalWeeklyBudget, + spentAmount: prev.weekly.spentAmount, + remainingAmount: totalWeeklyBudget - prev.weekly.spentAmount + }, + monthly: { + targetAmount: totalMonthlyBudget, + spentAmount: prev.monthly.spentAmount, + remainingAmount: totalMonthlyBudget - prev.monthly.spentAmount + } + }; + + // 업데이트된 예산 데이터 저장 + localStorage.setItem('budgetData', JSON.stringify(updatedBudgetData)); + return updatedBudgetData; + }); + + // 카테고리 예산 저장 + localStorage.setItem('categoryBudgets', JSON.stringify(categoryBudgets)); + }, [categoryBudgets]); + + // 카테고리별 지출 계산 + const getCategorySpending = () => { + const expenseTransactions = transactions.filter(t => t.type === 'expense'); + const categorySpending: Record = { ...Object.fromEntries( + Object.keys(categoryBudgets).map(key => [key, 0]) + )}; + + expenseTransactions.forEach(t => { + if (t.category in categorySpending) { + categorySpending[t.category] += t.amount; + } + }); + + return Object.keys(categoryBudgets).map(category => ({ + title: category, + current: categorySpending[category] || 0, + total: categoryBudgets[category] + })); + }; + + // 예산 목표 업데이트 함수 + const handleBudgetGoalUpdate = (type: BudgetPeriod, amount: number, newCategoryBudgets?: Record) => { + // 카테고리 예산이 직접 업데이트된 경우 + if (newCategoryBudgets) { + setCategoryBudgets(newCategoryBudgets); + return; // 카테고리 예산이 변경되면 useEffect에서 자동으로 budgetData가 업데이트됩니다 + } + + // 월간 예산을 업데이트하고 일일, 주간도 자동 계산 + if (type === 'monthly') { + const dailyAmount = Math.round(amount / 30); + const weeklyAmount = Math.round(amount / 4.3); + + // 월간 총 예산이 변경되면 카테고리별 예산도 비례하여 조정 + const ratio = amount / budgetData.monthly.targetAmount; + const updatedCategoryBudgets = {...categoryBudgets}; + + Object.keys(updatedCategoryBudgets).forEach(category => { + updatedCategoryBudgets[category as keyof typeof categoryBudgets] = + Math.round(categoryBudgets[category as keyof typeof categoryBudgets] * ratio); + }); + + setCategoryBudgets(updatedCategoryBudgets); + + setBudgetData(prev => { + const updatedBudgetData = { + daily: { + targetAmount: dailyAmount, + spentAmount: prev.daily.spentAmount, + remainingAmount: Math.max(0, dailyAmount - prev.daily.spentAmount) + }, + weekly: { + targetAmount: weeklyAmount, + spentAmount: prev.weekly.spentAmount, + remainingAmount: Math.max(0, weeklyAmount - prev.weekly.spentAmount) + }, + monthly: { + targetAmount: amount, + spentAmount: prev.monthly.spentAmount, + remainingAmount: Math.max(0, amount - prev.monthly.spentAmount) + } + }; + + // 업데이트된 예산 데이터 저장 + localStorage.setItem('budgetData', JSON.stringify(updatedBudgetData)); + return updatedBudgetData; + }); + } else { + // 일일이나 주간 예산이 직접 업데이트되는 경우 + setBudgetData(prev => { + const remainingAmount = Math.max(0, amount - prev[type].spentAmount); + const updatedBudgetData = { + ...prev, + [type]: { + ...prev[type], + targetAmount: amount, + remainingAmount: remainingAmount + } + }; + + // 업데이트된 예산 데이터 저장 + localStorage.setItem('budgetData', JSON.stringify(updatedBudgetData)); + return updatedBudgetData; + }); + } + + toast({ + title: "목표 업데이트 완료", + description: `${type === 'daily' ? '일일' : type === 'weekly' ? '주간' : '월간'} 목표가 ${amount.toLocaleString()}원으로 설정되었습니다.` + }); + }; + + // 트랜잭션 업데이트 처리 + const updateTransaction = (updatedTransaction: Transaction) => { + setTransactions(prev => { + const updated = prev.map(transaction => + transaction.id === updatedTransaction.id ? updatedTransaction : transaction + ); + localStorage.setItem('transactions', JSON.stringify(updated)); + return updated; + }); + }; + + const contextValue: BudgetContextType = { + transactions, + categoryBudgets, + budgetData, + selectedTab, + setSelectedTab, + updateTransaction, + handleBudgetGoalUpdate, + getCategorySpending + }; + + return ( + + {children} + + ); +}; + +// Context 사용을 위한 Hook +export const useBudget = () => { + const context = useContext(BudgetContext); + if (!context) { + throw new Error('useBudget must be used within a BudgetProvider'); + } + return context; +}; diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx index 010add2..feb2220 100644 --- a/src/pages/Index.tsx +++ b/src/pages/Index.tsx @@ -1,5 +1,5 @@ -import React, { useState, useEffect } from 'react'; +import React from 'react'; import NavBar from '@/components/NavBar'; import AddTransactionButton from '@/components/AddTransactionButton'; import Header from '@/components/Header'; @@ -7,305 +7,58 @@ import BudgetProgressCard from '@/components/BudgetProgressCard'; import BudgetCategoriesSection from '@/components/BudgetCategoriesSection'; import RecentTransactionsSection from '@/components/RecentTransactionsSection'; import { formatCurrency, calculatePercentage } from '@/utils/formatters'; -import { toast } from '@/components/ui/use-toast'; -import { Transaction } from '@/components/TransactionCard'; +import { BudgetProvider, useBudget } from '@/contexts/BudgetContext'; -// 기본 카테고리 예산 설정 -const DEFAULT_CATEGORY_BUDGETS = { - 식비: 400000, - 생활비: 600000, - 교통비: 200000 -}; +// 메인 컴포넌트 +const IndexContent = () => { + const { + transactions, + budgetData, + selectedTab, + setSelectedTab, + handleBudgetGoalUpdate, + updateTransaction, + getCategorySpending + } = useBudget(); -// 기본 월간 예산 -const DEFAULT_MONTHLY_BUDGET = 1200000; - -const Index = () => { - const [selectedTab, setSelectedTab] = useState("daily"); - const [transactions, setTransactions] = useState([]); - const [categoryBudgets, setCategoryBudgets] = useState(DEFAULT_CATEGORY_BUDGETS); - const [budgetData, setBudgetData] = useState({ - daily: { - targetAmount: Math.round(DEFAULT_MONTHLY_BUDGET / 30), - spentAmount: 0, - remainingAmount: Math.round(DEFAULT_MONTHLY_BUDGET / 30) - }, - weekly: { - targetAmount: Math.round(DEFAULT_MONTHLY_BUDGET / 4.3), - spentAmount: 0, - remainingAmount: Math.round(DEFAULT_MONTHLY_BUDGET / 4.3) - }, - monthly: { - targetAmount: DEFAULT_MONTHLY_BUDGET, - spentAmount: 0, - remainingAmount: DEFAULT_MONTHLY_BUDGET - } - }); - - // 로컬 스토리지에서 거래 내역을 불러옵니다 - useEffect(() => { - const loadTransactions = () => { - const storedTransactions = localStorage.getItem('transactions'); - if (storedTransactions) { - setTransactions(JSON.parse(storedTransactions)); - } - }; - - const loadBudgets = () => { - const storedCategoryBudgets = localStorage.getItem('categoryBudgets'); - if (storedCategoryBudgets) { - setCategoryBudgets(JSON.parse(storedCategoryBudgets)); - } else { - localStorage.setItem('categoryBudgets', JSON.stringify(DEFAULT_CATEGORY_BUDGETS)); - } - - const storedBudgetData = localStorage.getItem('budgetData'); - if (storedBudgetData) { - setBudgetData(JSON.parse(storedBudgetData)); - } else { - localStorage.setItem('budgetData', JSON.stringify(budgetData)); - } - }; - - loadTransactions(); - loadBudgets(); - - // 지출 내역이 변경될 때마다 업데이트되도록 이벤트 리스너를 추가합니다 - window.addEventListener('storage', loadTransactions); - return () => { - window.removeEventListener('storage', loadTransactions); - }; - }, []); - - // 거래 내역이 업데이트될 때마다 지출액을 다시 계산합니다 - useEffect(() => { - const calculateSpentAmounts = () => { - // 오늘 날짜의 지출만 필터링 - const today = new Date(); - const todayString = today.toLocaleDateString(); - - // 지출 거래 필터링 - const expenseTransactions = transactions.filter(t => t.type === 'expense'); - - // 오늘 지출 계산 - const todayExpenses = expenseTransactions.filter(t => { - if (t.date.includes('오늘')) return true; - return false; - }); - const dailySpent = todayExpenses.reduce((sum, t) => sum + t.amount, 0); - - // 이번 주 지출 계산 (지난 7일) - const weekStart = new Date(); - weekStart.setDate(today.getDate() - 7); - const weeklyExpenses = expenseTransactions.filter(t => { - // 간단하게 '오늘'이나 '어제'가 포함된 거래는 이번 주로 간주 - if (t.date.includes('오늘') || t.date.includes('어제')) return true; - // 그 외에는 이번 달의 거래로 간주 (실제로는 날짜를 제대로 파싱해야 함) - return true; - }); - const weeklySpent = weeklyExpenses.reduce((sum, t) => sum + t.amount, 0); - - // 이번 달 총 지출 계산 - const monthlySpent = expenseTransactions.reduce((sum, t) => sum + t.amount, 0); - - // 예산 데이터 업데이트 - setBudgetData(prev => { - const updatedBudgetData = { - daily: { - ...prev.daily, - spentAmount: dailySpent, - remainingAmount: Math.max(0, prev.daily.targetAmount - dailySpent) - }, - weekly: { - ...prev.weekly, - spentAmount: weeklySpent, - remainingAmount: Math.max(0, prev.weekly.targetAmount - weeklySpent) - }, - monthly: { - ...prev.monthly, - spentAmount: monthlySpent, - remainingAmount: Math.max(0, prev.monthly.targetAmount - monthlySpent) - } - }; - - // 업데이트된 예산 데이터 저장 - localStorage.setItem('budgetData', JSON.stringify(updatedBudgetData)); - return updatedBudgetData; - }); - }; - - calculateSpentAmounts(); - }, [transactions]); - - // 카테고리별 예산 및 지출 계산 - useEffect(() => { - const totalMonthlyBudget = Object.values(categoryBudgets).reduce((sum, value) => sum + value, 0); - const totalDailyBudget = Math.round(totalMonthlyBudget / 30); - const totalWeeklyBudget = Math.round(totalMonthlyBudget / 4.3); - - setBudgetData(prev => { - const updatedBudgetData = { - daily: { - targetAmount: totalDailyBudget, - spentAmount: prev.daily.spentAmount, - remainingAmount: totalDailyBudget - prev.daily.spentAmount - }, - weekly: { - targetAmount: totalWeeklyBudget, - spentAmount: prev.weekly.spentAmount, - remainingAmount: totalWeeklyBudget - prev.weekly.spentAmount - }, - monthly: { - targetAmount: totalMonthlyBudget, - spentAmount: prev.monthly.spentAmount, - remainingAmount: totalMonthlyBudget - prev.monthly.spentAmount - } - }; - - // 업데이트된 예산 데이터 저장 - localStorage.setItem('budgetData', JSON.stringify(updatedBudgetData)); - return updatedBudgetData; - }); - - // 카테고리 예산 저장 - localStorage.setItem('categoryBudgets', JSON.stringify(categoryBudgets)); - }, [categoryBudgets]); - - // 카테고리별 지출 계산 - const getCategorySpending = () => { - const expenseTransactions = transactions.filter(t => t.type === 'expense'); - const categorySpending: Record = { - 식비: 0, - 생활비: 0, - 교통비: 0 - }; - - expenseTransactions.forEach(t => { - if (t.category in categorySpending) { - categorySpending[t.category] += t.amount; - } - }); - - return [ - { title: '식비', current: categorySpending.식비, total: categoryBudgets.식비 }, - { title: '생활비', current: categorySpending.생활비, total: categoryBudgets.생활비 }, - { title: '교통비', current: categorySpending.교통비, total: categoryBudgets.교통비 } - ]; - }; - - // 예산 목표 업데이트 함수 - const handleBudgetGoalUpdate = (type: 'daily' | 'weekly' | 'monthly', amount: number, newCategoryBudgets?: typeof categoryBudgets) => { - // 카테고리 예산이 직접 업데이트된 경우 - if (newCategoryBudgets) { - setCategoryBudgets(newCategoryBudgets); - return; // 카테고리 예산이 변경되면 useEffect에서 자동으로 budgetData가 업데이트됩니다 - } - - // 월간 예산을 업데이트하고 일일, 주간도 자동 계산 - if (type === 'monthly') { - const dailyAmount = Math.round(amount / 30); - const weeklyAmount = Math.round(amount / 4.3); - - // 월간 총 예산이 변경되면 카테고리별 예산도 비례하여 조정 - const ratio = amount / budgetData.monthly.targetAmount; - const updatedCategoryBudgets = {...categoryBudgets}; - - Object.keys(updatedCategoryBudgets).forEach(category => { - updatedCategoryBudgets[category as keyof typeof categoryBudgets] = - Math.round(categoryBudgets[category as keyof typeof categoryBudgets] * ratio); - }); - - setCategoryBudgets(updatedCategoryBudgets); - - setBudgetData(prev => { - const updatedBudgetData = { - daily: { - targetAmount: dailyAmount, - spentAmount: prev.daily.spentAmount, - remainingAmount: Math.max(0, dailyAmount - prev.daily.spentAmount) - }, - weekly: { - targetAmount: weeklyAmount, - spentAmount: prev.weekly.spentAmount, - remainingAmount: Math.max(0, weeklyAmount - prev.weekly.spentAmount) - }, - monthly: { - targetAmount: amount, - spentAmount: prev.monthly.spentAmount, - remainingAmount: Math.max(0, amount - prev.monthly.spentAmount) - } - }; - - // 업데이트된 예산 데이터 저장 - localStorage.setItem('budgetData', JSON.stringify(updatedBudgetData)); - return updatedBudgetData; - }); - } else { - // 일일이나 주간 예산이 직접 업데이트되는 경우 - setBudgetData(prev => { - const remainingAmount = Math.max(0, amount - prev[type].spentAmount); - const updatedBudgetData = { - ...prev, - [type]: { - ...prev[type], - targetAmount: amount, - remainingAmount: remainingAmount - } - }; - - // 업데이트된 예산 데이터 저장 - localStorage.setItem('budgetData', JSON.stringify(updatedBudgetData)); - return updatedBudgetData; - }); - } - - toast({ - title: "목표 업데이트 완료", - description: `${type === 'daily' ? '일일' : type === 'weekly' ? '주간' : '월간'} 목표가 ${amount.toLocaleString()}원으로 설정되었습니다.` - }); - }; - - // Handle transaction updates - const handleUpdateTransaction = (updatedTransaction: Transaction) => { - setTransactions(prev => { - const updated = prev.map(transaction => - transaction.id === updatedTransaction.id ? updatedTransaction : transaction - ); - localStorage.setItem('transactions', JSON.stringify(updated)); - return updated; - }); - }; - return ( -
-
-
+
+
- {/* 목표 진행 상황 */} -

예산과 지출

- + {/* 목표 진행 상황 */} +

예산과 지출

+ - {/* 지출 카테고리 */} - + {/* 지출 카테고리 */} + - {/* 최근 지출 */} - -
- - - + {/* 최근 지출 */} +
); }; +// Provider를 감싼 컨테이너 컴포넌트 +const Index = () => { + return ( + +
+ + + +
+
+ ); +}; + export default Index;