From a5fac97a95721c923d8fbd34061fffdae3ddc65c 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 04:02:43 +0000 Subject: [PATCH] Implement budget input per category Implement budget input fields for each category in the monthly budget settings, and automatically calculate and populate daily, weekly, and monthly budgets based on the sum of the category inputs. --- src/components/BudgetProgressCard.tsx | 93 ++++++++++++++++++++++--- src/pages/Index.tsx | 99 +++++++++++++++++++++------ 2 files changed, 160 insertions(+), 32 deletions(-) diff --git a/src/components/BudgetProgressCard.tsx b/src/components/BudgetProgressCard.tsx index 5c17de2..a6d16cb 100644 --- a/src/components/BudgetProgressCard.tsx +++ b/src/components/BudgetProgressCard.tsx @@ -1,14 +1,23 @@ + import React, { useState } from 'react'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; -import { Check, ChevronDown, ChevronUp } from 'lucide-react'; +import { Check, ChevronDown, ChevronUp, Calculator } from 'lucide-react'; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'; + interface BudgetData { targetAmount: number; spentAmount: number; remainingAmount: number; } + +interface CategoryBudget { + 식비: number; + 생활비: number; + 교통비: number; +} + interface BudgetProgressCardProps { budgetData: { daily: BudgetData; @@ -21,6 +30,7 @@ interface BudgetProgressCardProps { calculatePercentage: (spent: number, target: number) => number; onSaveBudget: (type: 'daily' | 'weekly' | 'monthly', amount: number) => void; } + const BudgetProgressCard: React.FC = ({ budgetData, selectedTab, @@ -59,12 +69,14 @@ const BudgetProgressCard: React.FC = ({ ; }; + interface BudgetTabContentProps { data: BudgetData; formatCurrency: (amount: number) => string; calculatePercentage: (spent: number, target: number) => number; onSaveBudget: (amount: number) => void; } + const BudgetTabContent: React.FC = ({ data, formatCurrency, @@ -74,14 +86,33 @@ const BudgetTabContent: React.FC = ({ const percentage = calculatePercentage(data.spentAmount, data.targetAmount); const [isOpen, setIsOpen] = useState(false); const [budgetInput, setBudgetInput] = useState(data.targetAmount.toString()); + + const [categoryBudgets, setCategoryBudgets] = useState({ + 식비: Math.round(data.targetAmount * 0.4), + 생활비: Math.round(data.targetAmount * 0.4), + 교통비: Math.round(data.targetAmount * 0.2) + }); + const handleInputChange = (value: string) => { // Remove all non-numeric characters const numericValue = value.replace(/[^0-9]/g, ''); setBudgetInput(numericValue); }; + + const handleCategoryInputChange = (value: string, category: keyof CategoryBudget) => { + // Remove all non-numeric characters + const numericValue = value.replace(/[^0-9]/g, ''); + + setCategoryBudgets(prev => ({ + ...prev, + [category]: parseInt(numericValue) || 0 + })); + }; + const handleSave = () => { - const amount = parseInt(budgetInput, 10) || 0; - onSaveBudget(amount); + // Calculate total from all categories + const totalAmount = Object.values(categoryBudgets).reduce((sum, value) => sum + value, 0); + onSaveBudget(totalAmount); setIsOpen(false); }; @@ -89,6 +120,7 @@ const BudgetTabContent: React.FC = ({ const formatWithCommas = (amount: string) => { return amount.replace(/\B(?=(\d{3})+(?!\d))/g, ','); }; + return
@@ -117,21 +149,60 @@ const BudgetTabContent: React.FC = ({
- 월간 예산 설정하기 + 카테고리별 예산 설정하기 {isOpen ? : } - -
- handleInputChange(e.target.value)} placeholder="목표 금액 입력" className="neuro-pressed" /> -
-

월간 예산을 입력하면 일일, 주간 예산이 자동으로 입력됩니다.

+ +

총 예산을 설정하면 일일, 주간 예산이 자동으로 계산됩니다.

; }; -export default BudgetProgressCard; \ No newline at end of file + +export default BudgetProgressCard; diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx index 7bfbbd3..1444c30 100644 --- a/src/pages/Index.tsx +++ b/src/pages/Index.tsx @@ -1,5 +1,5 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import NavBar from '@/components/NavBar'; import AddTransactionButton from '@/components/AddTransactionButton'; import Header from '@/components/Header'; @@ -37,45 +37,102 @@ const Index = () => { type: 'income' }]; - // 예산 데이터 - 실제 앱에서는 백엔드에서 가져와야 함 + // 카테고리별 예산 + const [categoryBudgets, setCategoryBudgets] = useState({ + 식비: 400000, + 생활비: 600000, + 교통비: 200000 + }); + + // 예산 데이터 - 월간 예산을 기반으로 일일, 주간 계산 const [budgetData, setBudgetData] = useState({ daily: { - targetAmount: 30000, + targetAmount: 0, spentAmount: 15000, - remainingAmount: 15000 + remainingAmount: 0 }, weekly: { - targetAmount: 200000, + targetAmount: 0, spentAmount: 120000, - remainingAmount: 80000 + remainingAmount: 0 }, monthly: { - targetAmount: 1200000, + targetAmount: 0, spentAmount: 750000, - remainingAmount: 450000 + remainingAmount: 0 } }); - // Updated to only use the three specified categories + // 초기 로드 및 카테고리 예산 변경 시 전체 예산 업데이트 + 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({ + daily: { + targetAmount: totalDailyBudget, + spentAmount: budgetData.daily.spentAmount, + remainingAmount: totalDailyBudget - budgetData.daily.spentAmount + }, + weekly: { + targetAmount: totalWeeklyBudget, + spentAmount: budgetData.weekly.spentAmount, + remainingAmount: totalWeeklyBudget - budgetData.weekly.spentAmount + }, + monthly: { + targetAmount: totalMonthlyBudget, + spentAmount: budgetData.monthly.spentAmount, + remainingAmount: totalMonthlyBudget - budgetData.monthly.spentAmount + } + }); + }, [categoryBudgets]); + + // 카테고리 업데이트 const categories = [ - { title: '식비', current: 240000, total: 400000 }, - { title: '생활비', current: 350000, total: 600000 }, - { title: '교통비', current: 190000, total: 200000 }, + { title: '식비', current: budgetData.monthly.spentAmount * 0.32, total: categoryBudgets.식비 }, + { title: '생활비', current: budgetData.monthly.spentAmount * 0.47, total: categoryBudgets.생활비 }, + { title: '교통비', current: budgetData.monthly.spentAmount * 0.21, total: categoryBudgets.교통비 }, ]; // 예산 목표 업데이트 함수 const handleBudgetGoalUpdate = (type: 'daily' | 'weekly' | 'monthly', amount: number) => { - setBudgetData(prev => { - const remainingAmount = Math.max(0, amount - prev[type].spentAmount); - return { - ...prev, - [type]: { - ...prev[type], + // 월간 예산을 업데이트하고 일일, 주간도 자동 계산 + if (type === 'monthly') { + const dailyAmount = Math.round(amount / 30); + const weeklyAmount = Math.round(amount / 4.3); + + setBudgetData(prev => ({ + 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, - remainingAmount: remainingAmount + spentAmount: prev.monthly.spentAmount, + remainingAmount: Math.max(0, amount - prev.monthly.spentAmount) } - }; - }); + })); + } else { + // 일일이나 주간 예산이 직접 업데이트되는 경우 + setBudgetData(prev => { + const remainingAmount = Math.max(0, amount - prev[type].spentAmount); + return { + ...prev, + [type]: { + ...prev[type], + targetAmount: amount, + remainingAmount: remainingAmount + } + }; + }); + } toast({ title: "목표 업데이트 완료",