Refactor Index page component
The Index page component was refactored into smaller, more manageable components to improve code readability and maintainability.
This commit is contained in:
21
src/components/home/EmptyState.tsx
Normal file
21
src/components/home/EmptyState.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface EmptyStateProps {
|
||||||
|
message?: string;
|
||||||
|
subMessage?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EmptyState: React.FC<EmptyStateProps> = ({
|
||||||
|
message = "아직 데이터가 없습니다",
|
||||||
|
subMessage = "예산을 설정하고 지출을 추가해 보세요"
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className="neuro-card py-8 text-center text-gray-400 mb-4">
|
||||||
|
<p>{message}</p>
|
||||||
|
<p className="text-sm mt-2">{subMessage}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EmptyState;
|
||||||
86
src/components/home/HomeContent.tsx
Normal file
86
src/components/home/HomeContent.tsx
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import BudgetProgressCard from '@/components/BudgetProgressCard';
|
||||||
|
import BudgetCategoriesSection from '@/components/BudgetCategoriesSection';
|
||||||
|
import RecentTransactionsSection from '@/components/RecentTransactionsSection';
|
||||||
|
import EmptyState from './EmptyState';
|
||||||
|
import { BudgetPeriod } from '@/contexts/BudgetContext';
|
||||||
|
import { formatCurrency, calculatePercentage } from '@/utils/formatters';
|
||||||
|
import { Transaction } from '@/components/TransactionCard';
|
||||||
|
|
||||||
|
interface HomeContentProps {
|
||||||
|
transactions: Transaction[];
|
||||||
|
budgetData: {
|
||||||
|
daily: {
|
||||||
|
targetAmount: number;
|
||||||
|
spentAmount: number;
|
||||||
|
remainingAmount: number;
|
||||||
|
};
|
||||||
|
weekly: {
|
||||||
|
targetAmount: number;
|
||||||
|
spentAmount: number;
|
||||||
|
remainingAmount: number;
|
||||||
|
};
|
||||||
|
monthly: {
|
||||||
|
targetAmount: number;
|
||||||
|
spentAmount: number;
|
||||||
|
remainingAmount: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
selectedTab: string;
|
||||||
|
setSelectedTab: (value: string) => void;
|
||||||
|
handleBudgetGoalUpdate: (type: BudgetPeriod, amount: number, newCategoryBudgets?: Record<string, number>) => void;
|
||||||
|
updateTransaction: (transaction: Transaction) => void;
|
||||||
|
getCategorySpending: () => Array<{
|
||||||
|
title: string;
|
||||||
|
current: number;
|
||||||
|
total: number;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const HomeContent: React.FC<HomeContentProps> = ({
|
||||||
|
transactions,
|
||||||
|
budgetData,
|
||||||
|
selectedTab,
|
||||||
|
setSelectedTab,
|
||||||
|
handleBudgetGoalUpdate,
|
||||||
|
updateTransaction,
|
||||||
|
getCategorySpending
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* 목표 진행 상황 */}
|
||||||
|
<h2 className="text-lg font-semibold mb-3">예산과 지출</h2>
|
||||||
|
<BudgetProgressCard
|
||||||
|
budgetData={budgetData}
|
||||||
|
selectedTab={selectedTab}
|
||||||
|
setSelectedTab={setSelectedTab}
|
||||||
|
formatCurrency={formatCurrency}
|
||||||
|
calculatePercentage={calculatePercentage}
|
||||||
|
onSaveBudget={handleBudgetGoalUpdate}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 지출 카테고리 */}
|
||||||
|
{getCategorySpending().some(cat => cat.current > 0 || cat.total > 0) ? (
|
||||||
|
<BudgetCategoriesSection categories={getCategorySpending()} />
|
||||||
|
) : (
|
||||||
|
<EmptyState />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 최근 지출 */}
|
||||||
|
{transactions.length > 0 ? (
|
||||||
|
<RecentTransactionsSection
|
||||||
|
transactions={transactions.slice(0, 5)}
|
||||||
|
onUpdateTransaction={updateTransaction}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="mt-4">
|
||||||
|
<h2 className="text-lg font-semibold mb-3">최근 지출</h2>
|
||||||
|
<EmptyState />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HomeContent;
|
||||||
94
src/hooks/useDataInitialization.ts
Normal file
94
src/hooks/useDataInitialization.ts
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
|
||||||
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
|
import { resetAllData } from '@/contexts/budget/storageUtils';
|
||||||
|
import { resetAllStorageData } from '@/utils/storageUtils';
|
||||||
|
|
||||||
|
export const useDataInitialization = (resetBudgetData?: () => void) => {
|
||||||
|
const [isInitialized, setIsInitialized] = useState(false);
|
||||||
|
|
||||||
|
// 모든 데이터 초기화 함수
|
||||||
|
const initializeAllData = useCallback(() => {
|
||||||
|
console.log('모든 데이터 초기화 시작');
|
||||||
|
|
||||||
|
// 현재 dontShowWelcome 값 백업
|
||||||
|
const dontShowWelcomeValue = localStorage.getItem('dontShowWelcome');
|
||||||
|
console.log('useDataInitialization - 초기화 전 dontShowWelcome 값:', dontShowWelcomeValue);
|
||||||
|
|
||||||
|
// 여러번 초기화 실행
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
// 모든 데이터 완전히 삭제 및 초기화
|
||||||
|
resetAllData();
|
||||||
|
resetAllStorageData();
|
||||||
|
|
||||||
|
// localStorage 직접 초기화 (추가 보호)
|
||||||
|
clearAllAnalyticsData();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 컨텍스트 데이터 리셋 (마지막에 한번 더)
|
||||||
|
if (resetBudgetData) {
|
||||||
|
resetBudgetData();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 초기화 후 dontShowWelcome 값 확인
|
||||||
|
const afterResetValue = localStorage.getItem('dontShowWelcome');
|
||||||
|
console.log('useDataInitialization - 초기화 후 dontShowWelcome 값:', afterResetValue);
|
||||||
|
|
||||||
|
// 값이 유지되지 않았다면 복원
|
||||||
|
if (dontShowWelcomeValue && afterResetValue !== dontShowWelcomeValue) {
|
||||||
|
console.log('useDataInitialization - dontShowWelcome 값 복원:', dontShowWelcomeValue);
|
||||||
|
localStorage.setItem('dontShowWelcome', dontShowWelcomeValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('모든 데이터 초기화 완료');
|
||||||
|
return true;
|
||||||
|
}, [resetBudgetData]);
|
||||||
|
|
||||||
|
// 분석 페이지 데이터 초기화 함수
|
||||||
|
const clearAllAnalyticsData = useCallback(() => {
|
||||||
|
// 분석 관련 데이터 강제 삭제
|
||||||
|
const analyticsKeys = [
|
||||||
|
'analytics', 'monthlyTotals', 'chartData',
|
||||||
|
'expenseHistory', 'budgetHistory', 'categorySpending',
|
||||||
|
'monthlyData', 'expenseData', 'analyticData'
|
||||||
|
];
|
||||||
|
|
||||||
|
analyticsKeys.forEach(key => {
|
||||||
|
localStorage.removeItem(key);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 모든 localStorage 순회하며 월간, 차트, 분석 관련 키워드 삭제
|
||||||
|
for (let i = localStorage.length - 1; i >= 0; i--) {
|
||||||
|
const key = localStorage.key(i);
|
||||||
|
if (key && (
|
||||||
|
key.includes('month') ||
|
||||||
|
key.includes('chart') ||
|
||||||
|
key.includes('analytics') ||
|
||||||
|
key.includes('expense') ||
|
||||||
|
key.includes('budget') ||
|
||||||
|
key.includes('total')
|
||||||
|
)) {
|
||||||
|
console.log(`분석 데이터 삭제: ${key}`);
|
||||||
|
localStorage.removeItem(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 데이터 초기화 실행
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isInitialized) {
|
||||||
|
const result = initializeAllData();
|
||||||
|
setIsInitialized(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 방문 기록 저장 (초기화 후에 저장)
|
||||||
|
localStorage.setItem('hasVisitedBefore', 'true');
|
||||||
|
}, [isInitialized, initializeAllData]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
isInitialized,
|
||||||
|
initializeAllData,
|
||||||
|
clearAllAnalyticsData
|
||||||
|
};
|
||||||
|
};
|
||||||
60
src/hooks/useWelcomeDialog.ts
Normal file
60
src/hooks/useWelcomeDialog.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
|
||||||
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
|
|
||||||
|
export const useWelcomeDialog = () => {
|
||||||
|
const [showWelcome, setShowWelcome] = useState(false);
|
||||||
|
|
||||||
|
// 환영 다이얼로그 표시 여부 확인
|
||||||
|
const checkWelcomeDialogState = useCallback(() => {
|
||||||
|
// 현재 세션에서 이미 환영 메시지를 닫았는지 확인
|
||||||
|
const sessionClosed = sessionStorage.getItem('welcomeClosedThisSession') === 'true';
|
||||||
|
|
||||||
|
if (sessionClosed) {
|
||||||
|
console.log('useWelcomeDialog - 이번 세션에서 이미 환영 메시지를 닫았습니다');
|
||||||
|
setShowWelcome(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dontShowWelcome = localStorage.getItem('dontShowWelcome');
|
||||||
|
console.log('useWelcomeDialog - dontShowWelcome 값:', dontShowWelcome);
|
||||||
|
|
||||||
|
// 명시적으로 'true' 문자열인 경우에만 숨김 처리
|
||||||
|
if (dontShowWelcome === 'true') {
|
||||||
|
console.log('useWelcomeDialog - 환영 메시지 표시하지 않음 (저장된 설정)');
|
||||||
|
setShowWelcome(false);
|
||||||
|
} else {
|
||||||
|
console.log('useWelcomeDialog - 환영 메시지 표시함');
|
||||||
|
setShowWelcome(true);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 환영 다이얼로그 닫기 핸들러
|
||||||
|
const handleCloseWelcome = useCallback((dontShowAgain: boolean) => {
|
||||||
|
setShowWelcome(false);
|
||||||
|
|
||||||
|
// 이번 세션에서 닫았음을 기록
|
||||||
|
sessionStorage.setItem('welcomeClosedThisSession', 'true');
|
||||||
|
|
||||||
|
// 사용자가 더 이상 보지 않기를 선택한 경우
|
||||||
|
if (dontShowAgain) {
|
||||||
|
localStorage.setItem('dontShowWelcome', 'true');
|
||||||
|
sessionStorage.setItem('dontShowWelcome', 'true');
|
||||||
|
console.log('useWelcomeDialog - 환영 팝업 더 이상 표시하지 않기 설정됨:', dontShowAgain);
|
||||||
|
|
||||||
|
// 설정 확인
|
||||||
|
const savedValue = localStorage.getItem('dontShowWelcome');
|
||||||
|
console.log('useWelcomeDialog - 설정 후 dontShowWelcome 저장값:', savedValue);
|
||||||
|
} else {
|
||||||
|
// 체크하지 않은 경우 명시적으로 false 저장
|
||||||
|
localStorage.setItem('dontShowWelcome', 'false');
|
||||||
|
sessionStorage.setItem('dontShowWelcome', 'false');
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
showWelcome,
|
||||||
|
setShowWelcome,
|
||||||
|
checkWelcomeDialogState,
|
||||||
|
handleCloseWelcome
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1,17 +1,14 @@
|
|||||||
|
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import NavBar from '@/components/NavBar';
|
import NavBar from '@/components/NavBar';
|
||||||
import AddTransactionButton from '@/components/AddTransactionButton';
|
import AddTransactionButton from '@/components/AddTransactionButton';
|
||||||
import Header from '@/components/Header';
|
import Header from '@/components/Header';
|
||||||
import BudgetProgressCard from '@/components/BudgetProgressCard';
|
|
||||||
import BudgetCategoriesSection from '@/components/BudgetCategoriesSection';
|
|
||||||
import RecentTransactionsSection from '@/components/RecentTransactionsSection';
|
|
||||||
import WelcomeDialog from '@/components/onboarding/WelcomeDialog';
|
import WelcomeDialog from '@/components/onboarding/WelcomeDialog';
|
||||||
import { formatCurrency, calculatePercentage } from '@/utils/formatters';
|
import HomeContent from '@/components/home/HomeContent';
|
||||||
import { useBudget } from '@/contexts/BudgetContext';
|
import { useBudget } from '@/contexts/BudgetContext';
|
||||||
import { useAuth } from '@/contexts/auth';
|
import { useAuth } from '@/contexts/auth';
|
||||||
import { resetAllData } from '@/contexts/budget/storageUtils';
|
import { useWelcomeDialog } from '@/hooks/useWelcomeDialog';
|
||||||
import { resetAllStorageData } from '@/utils/storageUtils';
|
import { useDataInitialization } from '@/hooks/useDataInitialization';
|
||||||
import { useIsMobile } from '@/hooks/use-mobile';
|
import { useIsMobile } from '@/hooks/use-mobile';
|
||||||
|
|
||||||
// 메인 컴포넌트
|
// 메인 컴포넌트
|
||||||
@@ -28,150 +25,17 @@ const Index = () => {
|
|||||||
} = useBudget();
|
} = useBudget();
|
||||||
|
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const [showWelcome, setShowWelcome] = useState(false);
|
const { showWelcome, checkWelcomeDialogState, handleCloseWelcome } = useWelcomeDialog();
|
||||||
const [isInitialized, setIsInitialized] = useState(false);
|
const { isInitialized } = useDataInitialization(resetBudgetData);
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
// 화면이 처음 로드될 때 데이터 초기화
|
// 초기화 후 0.5초 후에 환영 메시지 표시 상태 확인
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 초기화 함수 모음
|
if (isInitialized) {
|
||||||
const initializeAllData = () => {
|
|
||||||
console.log('모든 데이터 초기화 시작');
|
|
||||||
|
|
||||||
// 현재 dontShowWelcome 값 백업
|
|
||||||
const dontShowWelcomeValue = localStorage.getItem('dontShowWelcome');
|
|
||||||
console.log('Index - 초기화 전 dontShowWelcome 값:', dontShowWelcomeValue);
|
|
||||||
|
|
||||||
// 여러번 초기화 실행
|
|
||||||
for (let i = 0; i < 3; i++) {
|
|
||||||
// 모든 데이터 완전히 삭제 및 초기화
|
|
||||||
resetAllData();
|
|
||||||
resetAllStorageData();
|
|
||||||
|
|
||||||
// localStorage 직접 초기화 (추가 보호)
|
|
||||||
clearAllAnalyticsData();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 컨텍스트 데이터 리셋 (마지막에 한번 더)
|
|
||||||
if (resetBudgetData) {
|
|
||||||
resetBudgetData();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 초기화 후 dontShowWelcome 값 확인
|
|
||||||
const afterResetValue = localStorage.getItem('dontShowWelcome');
|
|
||||||
console.log('Index - 초기화 후 dontShowWelcome 값:', afterResetValue);
|
|
||||||
|
|
||||||
// 값이 유지되지 않았다면 복원
|
|
||||||
if (dontShowWelcomeValue && afterResetValue !== dontShowWelcomeValue) {
|
|
||||||
console.log('Index - dontShowWelcome 값 복원:', dontShowWelcomeValue);
|
|
||||||
localStorage.setItem('dontShowWelcome', dontShowWelcomeValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('모든 데이터 초기화 완료');
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 분석 페이지 데이터 초기화 함수
|
|
||||||
const clearAllAnalyticsData = () => {
|
|
||||||
// 분석 관련 데이터 강제 삭제
|
|
||||||
const analyticsKeys = [
|
|
||||||
'analytics', 'monthlyTotals', 'chartData',
|
|
||||||
'expenseHistory', 'budgetHistory', 'categorySpending',
|
|
||||||
'monthlyData', 'expenseData', 'analyticData'
|
|
||||||
];
|
|
||||||
|
|
||||||
analyticsKeys.forEach(key => {
|
|
||||||
localStorage.removeItem(key);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 모든 localStorage 순회하며 월간, 차트, 분석 관련 키워드 삭제
|
|
||||||
for (let i = localStorage.length - 1; i >= 0; i--) {
|
|
||||||
const key = localStorage.key(i);
|
|
||||||
if (key && (
|
|
||||||
key.includes('month') ||
|
|
||||||
key.includes('chart') ||
|
|
||||||
key.includes('analytics') ||
|
|
||||||
key.includes('expense') ||
|
|
||||||
key.includes('budget') ||
|
|
||||||
key.includes('total')
|
|
||||||
)) {
|
|
||||||
console.log(`분석 데이터 삭제: ${key}`);
|
|
||||||
localStorage.removeItem(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!isInitialized) {
|
|
||||||
// 데이터 초기화 실행
|
|
||||||
const result = initializeAllData();
|
|
||||||
setIsInitialized(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 환영 다이얼로그 표시 여부 결정 (데이터 초기화 후)
|
|
||||||
const checkWelcomeDialogState = () => {
|
|
||||||
// 현재 세션에서 이미 환영 메시지를 닫았는지 확인
|
|
||||||
const sessionClosed = sessionStorage.getItem('welcomeClosedThisSession') === 'true';
|
|
||||||
|
|
||||||
if (sessionClosed) {
|
|
||||||
console.log('Index - 이번 세션에서 이미 환영 메시지를 닫았습니다');
|
|
||||||
setShowWelcome(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dontShowWelcome = localStorage.getItem('dontShowWelcome');
|
|
||||||
console.log('Index - 페이지 로딩 시 dontShowWelcome 값:', dontShowWelcome);
|
|
||||||
|
|
||||||
// 명시적으로 'true' 문자열인 경우에만 숨김 처리
|
|
||||||
if (dontShowWelcome === 'true') {
|
|
||||||
console.log('Index - 환영 메시지 표시하지 않음 (저장된 설정)');
|
|
||||||
setShowWelcome(false);
|
|
||||||
} else {
|
|
||||||
console.log('Index - 환영 메시지 표시함');
|
|
||||||
setShowWelcome(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 0.5초 후 환영 메시지 표시 상태 확인 (데이터 초기화가 완료된 후)
|
|
||||||
const timeoutId = setTimeout(checkWelcomeDialogState, 500);
|
const timeoutId = setTimeout(checkWelcomeDialogState, 500);
|
||||||
|
|
||||||
// 방문 기록 저장 (초기화 후에 저장)
|
|
||||||
localStorage.setItem('hasVisitedBefore', 'true');
|
|
||||||
|
|
||||||
return () => clearTimeout(timeoutId);
|
return () => clearTimeout(timeoutId);
|
||||||
}, [isInitialized, resetBudgetData]);
|
|
||||||
|
|
||||||
// 환영 팝업 닫기
|
|
||||||
const handleCloseWelcome = (dontShowAgain: boolean) => {
|
|
||||||
setShowWelcome(false);
|
|
||||||
|
|
||||||
// 이번 세션에서 닫았음을 기록
|
|
||||||
sessionStorage.setItem('welcomeClosedThisSession', 'true');
|
|
||||||
|
|
||||||
// 사용자가 더 이상 보지 않기를 선택한 경우
|
|
||||||
if (dontShowAgain) {
|
|
||||||
localStorage.setItem('dontShowWelcome', 'true');
|
|
||||||
sessionStorage.setItem('dontShowWelcome', 'true');
|
|
||||||
console.log('Index - 환영 팝업 더 이상 표시하지 않기 설정됨:', dontShowAgain);
|
|
||||||
|
|
||||||
// 설정 확인
|
|
||||||
const savedValue = localStorage.getItem('dontShowWelcome');
|
|
||||||
console.log('Index - 설정 후 dontShowWelcome 저장값:', savedValue);
|
|
||||||
} else {
|
|
||||||
// 체크하지 않은 경우 명시적으로 false 저장
|
|
||||||
localStorage.setItem('dontShowWelcome', 'false');
|
|
||||||
sessionStorage.setItem('dontShowWelcome', 'false');
|
|
||||||
}
|
}
|
||||||
};
|
}, [isInitialized, checkWelcomeDialogState]);
|
||||||
|
|
||||||
// 빈 데이터 상태 메시지
|
|
||||||
const EmptyState = () => (
|
|
||||||
<div className="neuro-card py-8 text-center text-gray-400 mb-4">
|
|
||||||
<p>아직 데이터가 없습니다</p>
|
|
||||||
<p className="text-sm mt-2">예산을 설정하고 지출을 추가해 보세요</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
// 트랜잭션 변경 시 페이지 새로고침
|
// 트랜잭션 변경 시 페이지 새로고침
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -194,36 +58,15 @@ const Index = () => {
|
|||||||
<div className="max-w-md mx-auto px-6">
|
<div className="max-w-md mx-auto px-6">
|
||||||
<Header />
|
<Header />
|
||||||
|
|
||||||
{/* 목표 진행 상황 */}
|
<HomeContent
|
||||||
<h2 className="text-lg font-semibold mb-3">예산과 지출</h2>
|
transactions={transactions}
|
||||||
<BudgetProgressCard
|
|
||||||
budgetData={budgetData}
|
budgetData={budgetData}
|
||||||
selectedTab={selectedTab}
|
selectedTab={selectedTab}
|
||||||
setSelectedTab={setSelectedTab}
|
setSelectedTab={setSelectedTab}
|
||||||
formatCurrency={formatCurrency}
|
handleBudgetGoalUpdate={handleBudgetGoalUpdate}
|
||||||
calculatePercentage={calculatePercentage}
|
updateTransaction={updateTransaction}
|
||||||
onSaveBudget={handleBudgetGoalUpdate}
|
getCategorySpending={getCategorySpending}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* 지출 카테고리 */}
|
|
||||||
{getCategorySpending().some(cat => cat.current > 0 || cat.total > 0) ? (
|
|
||||||
<BudgetCategoriesSection categories={getCategorySpending()} />
|
|
||||||
) : (
|
|
||||||
<EmptyState />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 최근 지출 */}
|
|
||||||
{transactions.length > 0 ? (
|
|
||||||
<RecentTransactionsSection
|
|
||||||
transactions={transactions.slice(0, 5)}
|
|
||||||
onUpdateTransaction={updateTransaction}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div className="mt-4">
|
|
||||||
<h2 className="text-lg font-semibold mb-3">최근 지출</h2>
|
|
||||||
<EmptyState />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<AddTransactionButton />
|
<AddTransactionButton />
|
||||||
<NavBar />
|
<NavBar />
|
||||||
|
|||||||
Reference in New Issue
Block a user