Refactor category budget setting
The category budget setting is now based on the monthly budget amount, which is then divided into daily and weekly budgets.
This commit is contained in:
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { CirclePlus, Save, Check } from 'lucide-react';
|
import { CirclePlus, Save, Check } from 'lucide-react';
|
||||||
import BudgetInputCard from './BudgetInputCard';
|
import BudgetInputCard from './BudgetInputCard';
|
||||||
@@ -94,17 +93,23 @@ const BudgetTabContent: React.FC<BudgetTabContentProps> = ({
|
|||||||
|
|
||||||
// 예산 여부에 따른 텍스트 결정
|
// 예산 여부에 따른 텍스트 결정
|
||||||
const budgetButtonText = targetAmount > 0 ? "예산 수정하기" : "예산 입력하기";
|
const budgetButtonText = targetAmount > 0 ? "예산 수정하기" : "예산 입력하기";
|
||||||
return <div>
|
|
||||||
{targetAmount > 0 ? <>
|
return (
|
||||||
|
<div>
|
||||||
|
{targetAmount > 0 ? (
|
||||||
|
<>
|
||||||
<div className="flex justify-between items-center mb-3">
|
<div className="flex justify-between items-center mb-3">
|
||||||
<div className="text-2xl font-bold">{formatCurrency(spentAmount)}</div>
|
<div className="text-2xl font-bold">{formatCurrency(spentAmount)}</div>
|
||||||
<div className="text-sm text-gray-500">/ {formatCurrency(targetAmount)}</div>
|
<div className="text-sm text-gray-500">/ {formatCurrency(targetAmount)}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="w-full h-2 neuro-pressed overflow-hidden mb-3">
|
<div className="w-full h-2 neuro-pressed overflow-hidden mb-3">
|
||||||
<div className={`h-full ${progressBarColor} transition-all duration-700 ease-out`} style={{
|
<div
|
||||||
width: `${Math.min(percentage, 100)}%`
|
className={`h-full ${progressBarColor} transition-all duration-700 ease-out`}
|
||||||
}} />
|
style={{
|
||||||
|
width: `${Math.min(percentage, 100)}%`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
@@ -125,15 +130,19 @@ const BudgetTabContent: React.FC<BudgetTabContentProps> = ({
|
|||||||
<span className="text-base font-semibold">{budgetButtonText}</span>
|
<span className="text-base font-semibold">{budgetButtonText}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</> : <div className="py-4 text-center">
|
</>
|
||||||
|
) : (
|
||||||
|
<div className="py-4 text-center">
|
||||||
<div className="text-gray-400 mb-4">아직 예산이 설정되지 않았습니다</div>
|
<div className="text-gray-400 mb-4">아직 예산이 설정되지 않았습니다</div>
|
||||||
<Button onClick={toggleBudgetInput} variant="default" className="bg-neuro-income hover:bg-neuro-income/90 animate-pulse shadow-lg">
|
<Button onClick={toggleBudgetInput} variant="default" className="bg-neuro-income hover:bg-neuro-income/90 animate-pulse shadow-lg">
|
||||||
<CirclePlus className="mr-2" size={24} />
|
<CirclePlus className="mr-2" size={24} />
|
||||||
<span className="animate-pulse">{budgetButtonText}</span>
|
<span className="animate-pulse">{budgetButtonText}</span>
|
||||||
</Button>
|
</Button>
|
||||||
</div>}
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{showBudgetInput && <div className="mt-4">
|
{showBudgetInput && (
|
||||||
|
<div className="mt-4">
|
||||||
<div className="neuro-card p-4">
|
<div className="neuro-card p-4">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium mb-3">카테고리별 예산 설정</h3>
|
<h3 className="text-base font-medium mb-3">카테고리별 예산 설정</h3>
|
||||||
@@ -155,7 +164,9 @@ const BudgetTabContent: React.FC<BudgetTabContentProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>}
|
</div>
|
||||||
</div>;
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
export default BudgetTabContent;
|
export default BudgetTabContent;
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ const DataResetSection = () => {
|
|||||||
await resetAllData();
|
await resetAllData();
|
||||||
setIsResetDialogOpen(false);
|
setIsResetDialogOpen(false);
|
||||||
|
|
||||||
|
// 데이터 초기화 후 애플리케이션 리로드
|
||||||
// toast 알림은 useDataReset.ts에서 처리하므로 여기서는 제거
|
// toast 알림은 useDataReset.ts에서 처리하므로 여기서는 제거
|
||||||
// 페이지 새로고침 코드 제거 (navigate 사용으로 대체)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import { BudgetData, BudgetPeriod, CategoryBudget, Transaction } from './types';
|
import { BudgetData, BudgetPeriod, CategoryBudget, Transaction } from './types';
|
||||||
import { EXPENSE_CATEGORIES } from '@/constants/categoryIcons';
|
import { EXPENSE_CATEGORIES } from '@/constants/categoryIcons';
|
||||||
|
|
||||||
@@ -53,7 +52,7 @@ export const calculateCategorySpending = (
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
// 예산 데이터 업데이트 계산
|
// 예산 데이터 업데이트 계산 - 수정된 함수
|
||||||
export const calculateUpdatedBudgetData = (
|
export const calculateUpdatedBudgetData = (
|
||||||
prevBudgetData: BudgetData,
|
prevBudgetData: BudgetData,
|
||||||
type: BudgetPeriod,
|
type: BudgetPeriod,
|
||||||
@@ -61,79 +60,35 @@ export const calculateUpdatedBudgetData = (
|
|||||||
): BudgetData => {
|
): BudgetData => {
|
||||||
console.log(`예산 업데이트 계산: 타입=${type}, 금액=${amount}`);
|
console.log(`예산 업데이트 계산: 타입=${type}, 금액=${amount}`);
|
||||||
|
|
||||||
if (type === 'monthly') {
|
// 카테고리 예산은 항상 월간 기준이므로, monthly 계산 방식 사용
|
||||||
// 문제 수정: 일일 예산은 월간/30, 주간 예산은 월간/4.3으로 계산
|
// 월간→주간→일간 순서로 변환
|
||||||
const dailyAmount = Math.round(amount / 30);
|
const monthlyAmount = type === 'monthly' ? amount :
|
||||||
const weeklyAmount = Math.round(amount / 4.3);
|
type === 'weekly' ? Math.round(amount * 4.3) :
|
||||||
|
Math.round(amount * 30);
|
||||||
|
|
||||||
console.log(`월간 예산 ${amount}원으로 설정 → 일일: ${dailyAmount}원, 주간: ${weeklyAmount}원`);
|
// 월간 금액에서 주간, 일간 계산
|
||||||
|
const weeklyAmount = Math.round(monthlyAmount / 4.3);
|
||||||
|
const dailyAmount = Math.round(monthlyAmount / 30);
|
||||||
|
|
||||||
return {
|
console.log(`예산 변환: 월간=${monthlyAmount}원, 주간=${weeklyAmount}원, 일간=${dailyAmount}원`);
|
||||||
daily: {
|
|
||||||
targetAmount: dailyAmount,
|
|
||||||
spentAmount: prevBudgetData.daily.spentAmount,
|
|
||||||
remainingAmount: Math.max(0, dailyAmount - prevBudgetData.daily.spentAmount)
|
|
||||||
},
|
|
||||||
weekly: {
|
|
||||||
targetAmount: weeklyAmount,
|
|
||||||
spentAmount: prevBudgetData.weekly.spentAmount,
|
|
||||||
remainingAmount: Math.max(0, weeklyAmount - prevBudgetData.weekly.spentAmount)
|
|
||||||
},
|
|
||||||
monthly: {
|
|
||||||
targetAmount: amount,
|
|
||||||
spentAmount: prevBudgetData.monthly.spentAmount,
|
|
||||||
remainingAmount: Math.max(0, amount - prevBudgetData.monthly.spentAmount)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else if (type === 'weekly') {
|
|
||||||
// 문제 수정: 월간 예산은 주간*4.3, 일일 예산은 주간/7로 계산
|
|
||||||
const monthlyAmount = Math.round(amount * 4.3);
|
|
||||||
const dailyAmount = Math.round(amount / 7);
|
|
||||||
|
|
||||||
console.log(`주간 예산 ${amount}원으로 설정 → 월간: ${monthlyAmount}원, 일일: ${dailyAmount}원`);
|
return {
|
||||||
|
daily: {
|
||||||
return {
|
targetAmount: dailyAmount,
|
||||||
daily: {
|
spentAmount: prevBudgetData.daily.spentAmount,
|
||||||
targetAmount: dailyAmount,
|
remainingAmount: Math.max(0, dailyAmount - prevBudgetData.daily.spentAmount)
|
||||||
spentAmount: prevBudgetData.daily.spentAmount,
|
},
|
||||||
remainingAmount: Math.max(0, dailyAmount - prevBudgetData.daily.spentAmount)
|
weekly: {
|
||||||
},
|
targetAmount: weeklyAmount,
|
||||||
weekly: {
|
spentAmount: prevBudgetData.weekly.spentAmount,
|
||||||
targetAmount: amount,
|
remainingAmount: Math.max(0, weeklyAmount - prevBudgetData.weekly.spentAmount)
|
||||||
spentAmount: prevBudgetData.weekly.spentAmount,
|
},
|
||||||
remainingAmount: Math.max(0, amount - prevBudgetData.weekly.spentAmount)
|
monthly: {
|
||||||
},
|
targetAmount: monthlyAmount,
|
||||||
monthly: {
|
spentAmount: prevBudgetData.monthly.spentAmount,
|
||||||
targetAmount: monthlyAmount,
|
remainingAmount: Math.max(0, monthlyAmount - prevBudgetData.monthly.spentAmount)
|
||||||
spentAmount: prevBudgetData.monthly.spentAmount,
|
}
|
||||||
remainingAmount: Math.max(0, monthlyAmount - prevBudgetData.monthly.spentAmount)
|
};
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// 문제 수정: 주간 예산은 일일*7, 월간 예산은 일일*30으로 계산
|
|
||||||
const weeklyAmount = Math.round(amount * 7);
|
|
||||||
const monthlyAmount = Math.round(amount * 30);
|
|
||||||
|
|
||||||
console.log(`일일 예산 ${amount}원으로 설정 → 주간: ${weeklyAmount}원, 월간: ${monthlyAmount}원`);
|
|
||||||
|
|
||||||
return {
|
|
||||||
daily: {
|
|
||||||
targetAmount: amount,
|
|
||||||
spentAmount: prevBudgetData.daily.spentAmount,
|
|
||||||
remainingAmount: Math.max(0, amount - prevBudgetData.daily.spentAmount)
|
|
||||||
},
|
|
||||||
weekly: {
|
|
||||||
targetAmount: weeklyAmount,
|
|
||||||
spentAmount: prevBudgetData.weekly.spentAmount,
|
|
||||||
remainingAmount: Math.max(0, weeklyAmount - prevBudgetData.weekly.spentAmount)
|
|
||||||
},
|
|
||||||
monthly: {
|
|
||||||
targetAmount: monthlyAmount,
|
|
||||||
spentAmount: prevBudgetData.monthly.spentAmount,
|
|
||||||
remainingAmount: Math.max(0, monthlyAmount - prevBudgetData.monthly.spentAmount)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 지출액 계산 (일일, 주간, 월간)
|
// 지출액 계산 (일일, 주간, 월간)
|
||||||
|
|||||||
@@ -23,18 +23,18 @@ export const resetAllData = (): void => {
|
|||||||
'categoryBudgets',
|
'categoryBudgets',
|
||||||
'budgetData',
|
'budgetData',
|
||||||
'budget',
|
'budget',
|
||||||
'monthlyExpenses', // 월간 지출 데이터
|
'monthlyExpenses',
|
||||||
'categorySpending', // 카테고리별 지출 데이터
|
'categorySpending',
|
||||||
'expenseAnalytics', // 지출 분석 데이터
|
'expenseAnalytics',
|
||||||
'expenseHistory', // 지출 이력
|
'expenseHistory',
|
||||||
'budgetHistory', // 예산 이력
|
'budgetHistory',
|
||||||
'analyticsCache', // 분석 캐시 데이터
|
'analyticsCache',
|
||||||
'monthlyTotals', // 월간 합계 데이터
|
'monthlyTotals',
|
||||||
'analytics', // 분석 페이지 데이터
|
'analytics',
|
||||||
'dailyBudget', // 일일 예산
|
'dailyBudget',
|
||||||
'weeklyBudget', // 주간 예산
|
'weeklyBudget',
|
||||||
'monthlyBudget', // 월간 예산
|
'monthlyBudget',
|
||||||
'chartData', // 차트 데이터
|
'chartData',
|
||||||
];
|
];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -42,6 +42,7 @@ export const resetAllData = (): void => {
|
|||||||
dataKeys.forEach(key => {
|
dataKeys.forEach(key => {
|
||||||
console.log(`삭제 중: ${key}`);
|
console.log(`삭제 중: ${key}`);
|
||||||
localStorage.removeItem(key);
|
localStorage.removeItem(key);
|
||||||
|
localStorage.removeItem(`${key}_backup`); // 백업 키도 함께 삭제
|
||||||
});
|
});
|
||||||
|
|
||||||
// 파일별 초기화 함수 호출
|
// 파일별 초기화 함수 호출
|
||||||
@@ -61,19 +62,15 @@ export const resetAllData = (): void => {
|
|||||||
localStorage.setItem('transactions_backup', JSON.stringify([]));
|
localStorage.setItem('transactions_backup', JSON.stringify([]));
|
||||||
|
|
||||||
// 이벤트 발생시켜 데이터 로드 트리거 - 이벤트 순서 최적화
|
// 이벤트 발생시켜 데이터 로드 트리거 - 이벤트 순서 최적화
|
||||||
try {
|
const events = [
|
||||||
// 한 번에 모든 이벤트 발생
|
new Event('transactionUpdated'),
|
||||||
const events = [
|
new Event('budgetDataUpdated'),
|
||||||
new Event('transactionUpdated'),
|
new Event('categoryBudgetsUpdated'),
|
||||||
new Event('budgetDataUpdated'),
|
new StorageEvent('storage')
|
||||||
new Event('categoryBudgetsUpdated'),
|
];
|
||||||
new StorageEvent('storage')
|
|
||||||
];
|
|
||||||
|
|
||||||
events.forEach(event => window.dispatchEvent(event));
|
// 모든 이벤트 동시에 발생
|
||||||
} catch (e) {
|
events.forEach(event => window.dispatchEvent(event));
|
||||||
console.error('이벤트 발생 오류:', e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 중요: 사용자 설정 값 복원 (백업한 값이 있는 경우)
|
// 중요: 사용자 설정 값 복원 (백업한 값이 있는 경우)
|
||||||
if (dontShowWelcomeValue) {
|
if (dontShowWelcomeValue) {
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export const loadBudgetFromStorage = (): number => {
|
|||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 모든 데이터 완전히 초기화
|
// 모든 데이터 완전히 초기화 - 성능 최적화
|
||||||
export const resetAllStorageData = (): void => {
|
export const resetAllStorageData = (): void => {
|
||||||
console.log('완전 초기화 시작 - resetAllStorageData');
|
console.log('완전 초기화 시작 - resetAllStorageData');
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ export const resetAllStorageData = (): void => {
|
|||||||
'budgetHistory',
|
'budgetHistory',
|
||||||
'transactionHistory',
|
'transactionHistory',
|
||||||
'lastSync',
|
'lastSync',
|
||||||
'syncEnabled', // 동기화 설정도 초기화
|
'syncEnabled'
|
||||||
];
|
];
|
||||||
|
|
||||||
// 키 동시에 삭제 (성능 최적화)
|
// 키 동시에 삭제 (성능 최적화)
|
||||||
|
|||||||
Reference in New Issue
Block a user