From ba8bd237d19c6d09ab9bb3397da995267106757c Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sat, 22 Mar 2025 12:28:14 +0000 Subject: [PATCH] Refactor TransactionFormFields component Refactor TransactionFormFields component to improve maintainability. --- .../transaction/TransactionAmountInput.tsx | 41 ++++ .../TransactionCategorySelector.tsx | 46 +++++ .../transaction/TransactionFormFields.tsx | 178 +++--------------- .../transaction/TransactionPaymentMethod.tsx | 68 +++++++ .../transaction/TransactionTitleInput.tsx | 30 +++ .../TransactionTitleSuggestions.tsx | 64 +++++++ 6 files changed, 270 insertions(+), 157 deletions(-) create mode 100644 src/components/transaction/TransactionAmountInput.tsx create mode 100644 src/components/transaction/TransactionCategorySelector.tsx create mode 100644 src/components/transaction/TransactionPaymentMethod.tsx create mode 100644 src/components/transaction/TransactionTitleInput.tsx create mode 100644 src/components/transaction/TransactionTitleSuggestions.tsx diff --git a/src/components/transaction/TransactionAmountInput.tsx b/src/components/transaction/TransactionAmountInput.tsx new file mode 100644 index 0000000..fb08e3e --- /dev/null +++ b/src/components/transaction/TransactionAmountInput.tsx @@ -0,0 +1,41 @@ + +import React from 'react'; +import { FormField, FormItem, FormLabel, FormControl, FormMessage } from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { UseFormReturn } from 'react-hook-form'; +import { TransactionFormValues, formatWithCommas } from './TransactionFormFields'; + +interface TransactionAmountInputProps { + form: UseFormReturn; + onFocus: () => void; +} + +const TransactionAmountInput: React.FC = ({ form, onFocus }) => { + const handleAmountChange = (e: React.ChangeEvent) => { + const formattedValue = formatWithCommas(e.target.value); + form.setValue('amount', formattedValue); + }; + + return ( + ( + + 금액 + + + + + + )} + /> + ); +}; + +export default TransactionAmountInput; diff --git a/src/components/transaction/TransactionCategorySelector.tsx b/src/components/transaction/TransactionCategorySelector.tsx new file mode 100644 index 0000000..6cb28cd --- /dev/null +++ b/src/components/transaction/TransactionCategorySelector.tsx @@ -0,0 +1,46 @@ + +import React from 'react'; +import { FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form'; +import { UseFormReturn } from 'react-hook-form'; +import { TransactionFormValues } from './TransactionFormFields'; +import { EXPENSE_CATEGORIES } from '@/constants/categoryIcons'; +import { categoryIcons } from '@/constants/categoryIcons'; + +interface TransactionCategorySelectorProps { + form: UseFormReturn; +} + +const TransactionCategorySelector: React.FC = ({ form }) => { + return ( + ( + + 카테고리 +
+ {EXPENSE_CATEGORIES.map((category) => ( +
form.setValue('category', category as any)} + > +
+ {categoryIcons[category]} +
+ {category} +
+ ))} +
+ +
+ )} + /> + ); +}; + +export default TransactionCategorySelector; diff --git a/src/components/transaction/TransactionFormFields.tsx b/src/components/transaction/TransactionFormFields.tsx index 93e60c3..95848db 100644 --- a/src/components/transaction/TransactionFormFields.tsx +++ b/src/components/transaction/TransactionFormFields.tsx @@ -1,16 +1,14 @@ import React, { useState, useEffect } from 'react'; -import { FormField, FormItem, FormLabel, FormControl, FormMessage } from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; import { UseFormReturn } from 'react-hook-form'; import { z } from 'zod'; -import { categoryIcons, EXPENSE_CATEGORIES } from '@/constants/categoryIcons'; -import { Badge } from '@/components/ui/badge'; -import { getPersonalizedTitleSuggestions } from '@/utils/userTitlePreferences'; -import { Separator } from '@/components/ui/separator'; -import { CreditCard, Banknote } from 'lucide-react'; +import TransactionCategorySelector from './TransactionCategorySelector'; +import TransactionTitleSuggestions from './TransactionTitleSuggestions'; +import TransactionTitleInput from './TransactionTitleInput'; +import TransactionAmountInput from './TransactionAmountInput'; +import TransactionPaymentMethod from './TransactionPaymentMethod'; -// Form schema for validation - 카테고리를 4개로 확장 및 지출 방법 추가 +// Form schema for validation export const transactionFormSchema = z.object({ title: z.string().min(1, '제목을 입력해주세요'), amount: z.string().min(1, '금액을 입력해주세요'), @@ -35,25 +33,12 @@ const TransactionFormFields: React.FC = ({ form }) = const [showTitleSuggestions, setShowTitleSuggestions] = useState(false); const [showPaymentMethod, setShowPaymentMethod] = useState(false); - const handleAmountChange = (e: React.ChangeEvent) => { - const formattedValue = formatWithCommas(e.target.value); - form.setValue('amount', formattedValue); - }; - // 현재 선택된 카테고리 가져오기 const selectedCategory = form.watch('category'); - // 현재 선택된 지불 방법 가져오기 - const selectedPaymentMethod = form.watch('paymentMethod'); - - // 선택된 카테고리에 대한 개인화된 제목 제안 목록 상태 - const [titleSuggestions, setTitleSuggestions] = useState([]); - - // 카테고리가 변경될 때마다 개인화된 제목 목록 업데이트 및 제목 추천 표시 + // 카테고리가 변경될 때마다 제목 추천 표시 useEffect(() => { if (selectedCategory) { - const suggestions = getPersonalizedTitleSuggestions(selectedCategory); - setTitleSuggestions(suggestions); // 약간의 지연 후 제목 추천 표시 (애니메이션을 위해) setTimeout(() => { setShowTitleSuggestions(true); @@ -63,152 +48,31 @@ const TransactionFormFields: React.FC = ({ form }) = } }, [selectedCategory]); - // 제안된 제목 클릭 시 제목 필드에 설정 - const handleTitleSuggestionClick = (suggestion: string) => { - form.setValue('title', suggestion); - }; - return ( <> {/* 카테고리 필드를 첫 번째로 배치 */} - ( - - 카테고리 -
- {EXPENSE_CATEGORIES.map((category) => ( -
form.setValue('category', category as any)} - > -
- {categoryIcons[category]} -
- {category} -
- ))} -
- -
- )} - /> + {/* 카테고리별 제목 제안 - 카테고리 선택 후에만 표시 */} - {selectedCategory && ( -
- {titleSuggestions.length > 0 && ( -
- {titleSuggestions.map((suggestion) => ( - handleTitleSuggestionClick(suggestion)} - > - {suggestion} - - ))} -
- )} -
- )} + {/* 제목 필드를 두 번째로 배치 */} - ( - - 제목 - - - - - - )} - /> + {/* 금액 필드를 세 번째로 배치 */} - ( - - 금액 - - setShowPaymentMethod(true)} - /> - - - - )} + setShowPaymentMethod(true)} /> - {/* 구분선과 지출 방법 필드는 금액 입력 시에만 표시 */} -
- - - {/* 지출 방법 필드 - 버튼 형식으로 변경 및 텍스트 크기 축소 */} - ( - - 지출 방법 - -
-
form.setValue('paymentMethod', '신용카드')} - > - - 신용카드 -
-
form.setValue('paymentMethod', '현금')} - > - - 현금 -
-
-
- -
- )} - /> -
+ {/* 지출 방법 필드는 금액 입력 시에만 표시 */} + ); }; diff --git a/src/components/transaction/TransactionPaymentMethod.tsx b/src/components/transaction/TransactionPaymentMethod.tsx new file mode 100644 index 0000000..f5b1423 --- /dev/null +++ b/src/components/transaction/TransactionPaymentMethod.tsx @@ -0,0 +1,68 @@ + +import React from 'react'; +import { FormField, FormItem, FormLabel, FormControl, FormMessage } from '@/components/ui/form'; +import { UseFormReturn } from 'react-hook-form'; +import { TransactionFormValues } from './TransactionFormFields'; +import { Separator } from '@/components/ui/separator'; +import { CreditCard, Banknote } from 'lucide-react'; + +interface TransactionPaymentMethodProps { + form: UseFormReturn; + showPaymentMethod: boolean; +} + +const TransactionPaymentMethod: React.FC = ({ + form, + showPaymentMethod +}) => { + return ( +
+ + + ( + + 지출 방법 + +
+
form.setValue('paymentMethod', '신용카드')} + > + + 신용카드 +
+
form.setValue('paymentMethod', '현금')} + > + + 현금 +
+
+
+ +
+ )} + /> +
+ ); +}; + +export default TransactionPaymentMethod; diff --git a/src/components/transaction/TransactionTitleInput.tsx b/src/components/transaction/TransactionTitleInput.tsx new file mode 100644 index 0000000..886aa96 --- /dev/null +++ b/src/components/transaction/TransactionTitleInput.tsx @@ -0,0 +1,30 @@ + +import React from 'react'; +import { FormField, FormItem, FormLabel, FormControl, FormMessage } from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { UseFormReturn } from 'react-hook-form'; +import { TransactionFormValues } from './TransactionFormFields'; + +interface TransactionTitleInputProps { + form: UseFormReturn; +} + +const TransactionTitleInput: React.FC = ({ form }) => { + return ( + ( + + 제목 + + + + + + )} + /> + ); +}; + +export default TransactionTitleInput; diff --git a/src/components/transaction/TransactionTitleSuggestions.tsx b/src/components/transaction/TransactionTitleSuggestions.tsx new file mode 100644 index 0000000..e245463 --- /dev/null +++ b/src/components/transaction/TransactionTitleSuggestions.tsx @@ -0,0 +1,64 @@ + +import React, { useState, useEffect } from 'react'; +import { Badge } from '@/components/ui/badge'; +import { UseFormReturn } from 'react-hook-form'; +import { TransactionFormValues } from './TransactionFormFields'; +import { getPersonalizedTitleSuggestions } from '@/utils/userTitlePreferences'; + +interface TransactionTitleSuggestionsProps { + form: UseFormReturn; + showTitleSuggestions: boolean; +} + +const TransactionTitleSuggestions: React.FC = ({ + form, + showTitleSuggestions +}) => { + // 현재 선택된 카테고리 가져오기 + const selectedCategory = form.watch('category'); + + // 선택된 카테고리에 대한 개인화된 제목 제안 목록 상태 + const [titleSuggestions, setTitleSuggestions] = useState([]); + + // 카테고리가 변경될 때마다 개인화된 제목 목록 업데이트 + useEffect(() => { + if (selectedCategory) { + const suggestions = getPersonalizedTitleSuggestions(selectedCategory); + setTitleSuggestions(suggestions); + } + }, [selectedCategory]); + + // 제안된 제목 클릭 시 제목 필드에 설정 + const handleTitleSuggestionClick = (suggestion: string) => { + form.setValue('title', suggestion); + }; + + if (!selectedCategory || titleSuggestions.length === 0) { + return null; + } + + return ( +
+
+ {titleSuggestions.map((suggestion) => ( + handleTitleSuggestionClick(suggestion)} + > + {suggestion} + + ))} +
+
+ ); +}; + +export default TransactionTitleSuggestions;