From e947a84dcbf9c82239cafbe29d84b089bceece77 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sat, 5 Apr 2025 06:02:31 +0000 Subject: [PATCH] Fix: Display 0 for expenses --- .../transactions/TransactionsHeader.tsx | 33 +++++++-- .../transactions/filterOperations/index.ts | 5 +- src/hooks/transactions/filterUtils.ts | 69 ++++++++++++------- src/utils/dateParser.ts | 33 +++++++-- 4 files changed, 103 insertions(+), 37 deletions(-) diff --git a/src/components/transactions/TransactionsHeader.tsx b/src/components/transactions/TransactionsHeader.tsx index 034fbe1..3275af1 100644 --- a/src/components/transactions/TransactionsHeader.tsx +++ b/src/components/transactions/TransactionsHeader.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Calendar, Search, ChevronLeft, ChevronRight } from 'lucide-react'; import { formatCurrency } from '@/utils/formatters'; + interface TransactionsHeaderProps { selectedMonth: string; searchQuery: string; @@ -12,6 +13,7 @@ interface TransactionsHeaderProps { totalExpenses: number; isDisabled: boolean; } + const TransactionsHeader: React.FC = ({ selectedMonth, searchQuery, @@ -29,18 +31,31 @@ const TransactionsHeader: React.FC = ({ // 예산 정보가 없는 경우 기본값 사용 const targetAmount = budgetData?.monthly?.targetAmount || 0; - return
+ + return ( +

지출 내역

{/* Search */}
- setSearchQuery(e.target.value)} disabled={isDisabled} /> + setSearchQuery(e.target.value)} + disabled={isDisabled} + />
{/* Month Selector */}
- @@ -49,7 +64,11 @@ const TransactionsHeader: React.FC = ({ {selectedMonth}
- @@ -69,6 +88,8 @@ const TransactionsHeader: React.FC = ({

-
; +
+ ); }; -export default TransactionsHeader; + +export default React.memo(TransactionsHeader); diff --git a/src/hooks/transactions/filterOperations/index.ts b/src/hooks/transactions/filterOperations/index.ts index 3e5fdb8..6bdbec9 100644 --- a/src/hooks/transactions/filterOperations/index.ts +++ b/src/hooks/transactions/filterOperations/index.ts @@ -3,6 +3,7 @@ import { useCallback, useEffect } from 'react'; import { Transaction } from '@/contexts/budget/types'; import { getCurrentMonth, getPrevMonth, getNextMonth } from '../dateUtils'; import { filterTransactionsByMonth, filterTransactionsByQuery, calculateTotalExpenses } from '../filterUtils'; +import { parseTransactionDate } from '@/utils/dateParser'; interface UseTransactionsFilteringProps { transactions: Transaction[]; @@ -28,7 +29,7 @@ export const useTransactionsFiltering = ({ console.log('트랜잭션 필터링 적용:', { 선택된월: selectedMonth, 검색어: searchQuery }); try { - // 먼저 월별 필터링 + // 먼저 월별 필터링 - 개선된 날짜 처리 기능 사용 const monthFiltered = filterTransactionsByMonth(transactions, selectedMonth); console.log('월별 필터링 결과:', monthFiltered.length); @@ -57,7 +58,7 @@ export const useTransactionsFiltering = ({ setSelectedMonth(getNextMonth(selectedMonth)); }, [selectedMonth, setSelectedMonth]); - // 총 지출 계산 - date-fns 없이도 사용 가능한 단순 버전 + // 총 지출 계산 - 개선된 계산 로직 사용 const getTotalExpenses = useCallback((filteredTransactions: Transaction[]): number => { console.log('총 지출 계산 중...', filteredTransactions.length); const total = calculateTotalExpenses(filteredTransactions); diff --git a/src/hooks/transactions/filterUtils.ts b/src/hooks/transactions/filterUtils.ts index 925b72f..87caff4 100644 --- a/src/hooks/transactions/filterUtils.ts +++ b/src/hooks/transactions/filterUtils.ts @@ -1,8 +1,11 @@ import { Transaction } from '@/contexts/budget/types'; +import { parseTransactionDate } from '@/utils/dateParser'; +import { format } from 'date-fns'; +import { ko } from 'date-fns/locale'; /** - * 월별로 트랜잭션 필터링 + * 월별로 트랜잭션 필터링 - 개선된 버전 */ export const filterTransactionsByMonth = ( transactions: Transaction[], @@ -15,6 +18,13 @@ export const filterTransactionsByMonth = ( console.log('샘플 트랜잭션 날짜:', transactions.slice(0, 3).map(t => t.date)); } + // 선택된 월의 숫자 추출 (ex: "4월" -> 4) + const selectedMonthNumber = parseInt(selectedMonth.replace('월', '')); + if (isNaN(selectedMonthNumber) || selectedMonthNumber < 1 || selectedMonthNumber > 12) { + console.error('잘못된 월 형식:', selectedMonth); + return []; + } + const filtered = transactions.filter(transaction => { // 트랜잭션 타입 확인 - 지출 항목만 포함 if (transaction.type !== 'expense') { @@ -28,27 +38,22 @@ export const filterTransactionsByMonth = ( return false; } - // 현재 월 포함 확인 (다양한 형식 지원) - const dateIncludes = transaction.date.includes(selectedMonth); - - // 월 이름으로 확인 (한글) - const monthNumberMatch = selectedMonth.match(/\d{4}-(\d{2})/); - let monthNameIncluded = false; - - if (monthNumberMatch) { - const monthNumber = parseInt(monthNumberMatch[1]); - const koreanMonths = [ - '1월', '2월', '3월', '4월', '5월', '6월', - '7월', '8월', '9월', '10월', '11월', '12월' - ]; - - // 해당 월의 한글 이름이 트랜잭션 날짜에 포함되어 있는지 확인 - if (monthNumber >= 1 && monthNumber <= 12) { - monthNameIncluded = transaction.date.includes(koreanMonths[monthNumber - 1]); - } + // 날짜 파싱 + const parsedDate = parseTransactionDate(transaction.date); + if (!parsedDate) { + console.warn('날짜 파싱 실패:', transaction.date); + return false; } - return dateIncludes || monthNameIncluded; + // 월 비교 + const transactionMonth = parsedDate.getMonth() + 1; // 0-based -> 1-based + const isMatchingMonth = transactionMonth === selectedMonthNumber; + + if (isMatchingMonth) { + console.log(`트랜잭션 매칭: ${transaction.title}, 날짜: ${transaction.date}, 월: ${transactionMonth}`); + } + + return isMatchingMonth; } catch (e) { console.error('트랜잭션 필터링 중 오류:', e, transaction); return false; @@ -89,17 +94,31 @@ export const filterTransactionsByQuery = ( }; /** - * 총 지출 금액 계산 + * 총 지출 금액 계산 - 개선된 버전 */ export const calculateTotalExpenses = (transactions: Transaction[]): number => { try { - const total = transactions.reduce((sum, t) => { - // 유효한 숫자인지 확인 - const amount = typeof t.amount === 'number' ? t.amount : 0; + // 유효한 트랜잭션만 필터링 (undefined, null 제외) + const validTransactions = transactions.filter(t => t && typeof t.amount !== 'undefined'); + console.log(`유효한 트랜잭션 수: ${validTransactions.length}/${transactions.length}`); + + // 디버깅용 로그 + if (validTransactions.length > 0) { + console.log('첫 번째 트랜잭션 정보:', { + title: validTransactions[0].title, + amount: validTransactions[0].amount, + type: validTransactions[0].type + }); + } + + const total = validTransactions.reduce((sum, t) => { + // 유효한 숫자인지 확인하고 기본값 처리 + const amount = typeof t.amount === 'number' ? t.amount : + parseInt(t.amount as any) || 0; return sum + amount; }, 0); - console.log(`총 지출 계산: ${total}원 (${transactions.length}개 항목)`); + console.log(`총 지출 계산: ${total}원 (${validTransactions.length}개 항목)`); return total; } catch (e) { console.error('총 지출 계산 중 오류:', e); diff --git a/src/utils/dateParser.ts b/src/utils/dateParser.ts index 855cf4a..5f97e43 100644 --- a/src/utils/dateParser.ts +++ b/src/utils/dateParser.ts @@ -7,17 +7,20 @@ import { ko } from 'date-fns/locale'; */ export const parseTransactionDate = (dateStr: string): Date | null => { // 빈 문자열 체크 - if (!dateStr) { + if (!dateStr || dateStr === '') { + console.log('빈 날짜 문자열'); return null; } try { // 특수 키워드 처리 - if (dateStr.includes('오늘')) { + if (dateStr.toLowerCase().includes('오늘')) { + console.log('오늘 날짜로 변환'); return new Date(); } - if (dateStr.includes('어제')) { + if (dateStr.toLowerCase().includes('어제')) { + console.log('어제 날짜로 변환'); const yesterday = new Date(); yesterday.setDate(yesterday.getDate() - 1); return yesterday; @@ -25,6 +28,7 @@ export const parseTransactionDate = (dateStr: string): Date | null => { // ISO 형식 (yyyy-MM-dd) 시도 if (/^\d{4}-\d{2}-\d{2}/.test(dateStr)) { + console.log('ISO 형식 날짜 감지'); const date = parseISO(dateStr); if (isValid(date)) { return date; @@ -36,6 +40,7 @@ export const parseTransactionDate = (dateStr: string): Date | null => { const koreanMatch = dateStr.match(koreanDatePattern); if (koreanMatch) { + console.log('한국어 날짜 형식 감지', koreanMatch); const month = parseInt(koreanMatch[1]) - 1; // 0-based month const day = parseInt(koreanMatch[2]); const date = new Date(); @@ -44,6 +49,21 @@ export const parseTransactionDate = (dateStr: string): Date | null => { return date; } + // 쉼표 구분 형식 처리 (예: "오늘, 14:52 PM") + const commaSplit = dateStr.split(','); + if (commaSplit.length > 1) { + console.log('쉼표 구분 날짜 감지'); + // 첫 부분이 오늘/어제 등의 키워드인 경우 처리 + const firstPart = commaSplit[0].trim().toLowerCase(); + if (firstPart === '오늘') { + return new Date(); + } else if (firstPart === '어제') { + const yesterday = new Date(); + yesterday.setDate(yesterday.getDate() - 1); + return yesterday; + } + } + // parse를 사용한 다양한 형식 시도 const formats = [ 'yyyy-MM-dd', @@ -51,13 +71,17 @@ export const parseTransactionDate = (dateStr: string): Date | null => { 'MM-dd-yyyy', 'MM/dd/yyyy', 'yyyy년 MM월 dd일', - 'MM월 dd일' + 'MM월 dd일', + 'yyyy년 M월 d일', + 'M월 d일' ]; for (const formatStr of formats) { try { + console.log(`날짜 형식 시도: ${formatStr}`); const date = parse(dateStr, formatStr, new Date(), { locale: ko }); if (isValid(date)) { + console.log('날짜 파싱 성공:', formatStr); return date; } } catch (e) { @@ -69,6 +93,7 @@ export const parseTransactionDate = (dateStr: string): Date | null => { // 위 모든 형식이 실패하면 마지막으로 Date 생성자 시도 const dateFromConstructor = new Date(dateStr); if (isValid(dateFromConstructor)) { + console.log('Date 생성자로 파싱 성공'); return dateFromConstructor; }