1- import React from 'react' ;
1+ import React , { useState , useEffect } from 'react' ;
22import TicketHostLayout from '../../../shared/ui/backgrounds/TicketHostLayout' ;
33import CreditCard from '../../../../public/assets/payment/CreditCard.svg' ;
44import DefaultTextField from '../../../../design-system/ui/textFields/DefaultTextField' ;
@@ -8,8 +8,7 @@ import { useForm } from 'react-hook-form';
88type CardFormValues = {
99 cardNumber : string ;
1010 cardHolder : string ;
11- expiryMonth : string ;
12- expiryYear : string ;
11+ expiry : string ;
1312 cvc : string ;
1413} ;
1514
@@ -18,10 +17,17 @@ const CardRegisterPage = () => {
1817 register,
1918 handleSubmit,
2019 setValue,
21- formState : { errors } ,
22- } = useForm < CardFormValues > ( ) ;
20+ watch,
21+ formState : { errors, isValid } ,
22+ } = useForm < CardFormValues > ( { mode : 'onChange' } ) ;
23+
24+ const [ isDisabled , setIsDisabled ] = useState ( true ) ;
25+
26+ // 입력값 변경 감지 및 버튼 활성화 여부 결정
27+ useEffect ( ( ) => {
28+ setIsDisabled ( ! isValid ) ;
29+ } , [ isValid ] ) ;
2330
24- // 공통 포맷 함수
2531 const formatValue = ( type : keyof CardFormValues , value : string ) : string => {
2632 switch ( type ) {
2733 case 'cardNumber' :
@@ -30,25 +36,26 @@ const CardRegisterPage = () => {
3036 . slice ( 0 , 16 )
3137 . replace ( / ( \d { 4 } ) / g, '$1 ' )
3238 . trim ( ) ;
33- case 'expiryMonth' :
34- return value . replace ( / \D / g, '' ) . slice ( 0 , 2 ) ;
35- case 'expiryYear' :
36- return value . replace ( / \D / g, '' ) . slice ( 0 , 4 ) ;
39+ case 'expiry' :
40+ return value
41+ . replace ( / \D / g, '' )
42+ . slice ( 0 , 4 )
43+ . replace ( / ( \d { 2 } ) ( \d { 0 , 2 } ) / , '$1 / $2' ) ;
3744 case 'cvc' :
38- return value . replace ( / \D / g, '' ) . slice ( 0 , 3 ) ;
45+ return value . replace ( / \D / g, '' ) . slice ( 0 , 4 ) ;
3946 default :
4047 return value ;
4148 }
4249 } ;
4350
44- // 공통 핸들러
4551 const handleInputChange = ( type : keyof CardFormValues ) => ( e : React . ChangeEvent < HTMLInputElement > ) => {
4652 const formattedValue = formatValue ( type , e . target . value ) ;
4753 setValue ( type , formattedValue , { shouldValidate : true } ) ;
4854 } ;
4955
5056 const onSubmit = ( data : CardFormValues ) => {
51- console . log ( '폼 전체 데이터 객체:' , data ) ;
57+ alert ( '카드 정보가 저장되었습니다!' ) ;
58+ console . log ( '폼 데이터:' , data ) ;
5259 } ;
5360
5461 return (
@@ -59,14 +66,11 @@ const CardRegisterPage = () => {
5966 < div >
6067 < DefaultTextField
6168 label = "카드 번호"
62- className = { `h-12 ${ errors . cardNumber ? 'border-2 border-red-500' : '' } ` }
6369 placeholder = "1234 5678 9012 3456"
70+ className = { `h-12 ${ errors . cardNumber ? 'border-2 border-red-500' : '' } ` }
6471 { ...register ( 'cardNumber' , {
6572 required : '카드 번호를 입력하세요.' ,
66- pattern : {
67- value : / ^ \d { 4 } \d { 4 } \d { 4 } \d { 4 } $ / ,
68- message : '올바른 카드 번호 형식이 아닙니다.' ,
69- } ,
73+ pattern : { value : / ^ \d { 4 } \d { 4 } \d { 4 } \d { 4 } $ / , message : '올바른 카드 번호 형식이 아닙니다.' } ,
7074 } ) }
7175 onChange = { handleInputChange ( 'cardNumber' ) }
7276 />
@@ -76,52 +80,42 @@ const CardRegisterPage = () => {
7680 < div >
7781 < DefaultTextField
7882 label = "카드 소지자 이름"
79- className = { `h-12 ${ errors . cardHolder ? 'border-2 border-red-500' : '' } ` }
8083 placeholder = "홍길동"
81- { ...register ( 'cardHolder' , { required : '카드 소지자 이름을 입력하세요. ' } ) }
84+ className = { `h-12 ${ errors . cardHolder ? 'border-2 border-red-500' : '' } ` }
85+ { ...register ( 'cardHolder' , { required : '카드 소지자 이름을 입력하세요.' } ) }
8286 />
8387 { errors . cardHolder && < p className = "text-red-500 text-xs md:text-sm" > { errors . cardHolder . message } </ p > }
8488 </ div >
8589
86- < div className = "flex justify-between w-full gap-2" >
87- < div >
88- < DefaultTextField
89- label = "만료 월"
90- className = { `h-10 ${ errors . expiryMonth ? 'border-2 border-red-500' : '' } ` }
91- placeholder = "월"
92- { ...register ( 'expiryMonth' , { required : '만료 월을 입력하세요.' } ) }
93- onChange = { handleInputChange ( 'expiryMonth' ) }
94- />
95- { errors . expiryMonth && < p className = "text-red-500 text-xs md:text-sm" > { errors . expiryMonth . message } </ p > }
96- </ div >
97- < div >
98- < DefaultTextField
99- label = "만료 년도"
100- className = { `h-10 ${ errors . expiryYear ? 'border-2 border-red-500' : '' } ` }
101- placeholder = "년도"
102- { ...register ( 'expiryYear' , { required : '만료 년도를 입력하세요.' } ) }
103- onChange = { handleInputChange ( 'expiryYear' ) }
104- />
105- { errors . expiryYear && < p className = "text-red-500 text-xs md:text-sm" > { errors . expiryYear . message } </ p > }
106- </ div >
107- < div >
108- < DefaultTextField
109- label = "CVC/CVV"
110- className = { `h-10 ${ errors . cvc ? 'border-2 border-red-500' : '' } ` }
111- placeholder = "123"
112- { ...register ( 'cvc' , { required : 'cvc를 입력하세요.' } ) }
113- onChange = { handleInputChange ( 'cvc' ) }
114- />
115- { errors . cvc && < p className = "text-red-500 text-xs md:text-sm" > { errors . cvc . message } </ p > }
116- </ div >
90+ < div >
91+ < DefaultTextField
92+ label = "만료 날짜 (MM / YY)"
93+ placeholder = "MM / YY"
94+ className = { `h-12 ${ errors . expiry ? 'border-2 border-red-500' : '' } ` }
95+ { ...register ( 'expiry' , { required : '만료 날짜를 입력하세요.' } ) }
96+ onChange = { handleInputChange ( 'expiry' ) }
97+ />
98+ { errors . expiry && < p className = "text-red-500 text-xs md:text-sm" > { errors . expiry . message } </ p > }
99+ </ div >
100+
101+ < div >
102+ < DefaultTextField
103+ label = "CVC / CVV"
104+ placeholder = "123"
105+ className = { `h-12 ${ errors . cvc ? 'border-2 border-red-500' : '' } ` }
106+ { ...register ( 'cvc' , { required : 'CVC를 입력하세요.' } ) }
107+ onChange = { handleInputChange ( 'cvc' ) }
108+ />
109+ { errors . cvc && < p className = "text-red-500 text-xs md:text-sm" > { errors . cvc . message } </ p > }
117110 </ div >
118- < div className = "flex flex-grow" />
111+
119112 < form onSubmit = { handleSubmit ( onSubmit ) } className = "py-5" >
120113 < Button
121114 label = "저장하기"
122- onClick = { ( ) => console . log ( '카드 정보 저장 버튼 클릭됨' ) }
123- className = "rounded-full w-full h-12"
124- /> { ' ' }
115+ disabled = { isDisabled }
116+ className = "rounded-full w-full h-12 bg-blue-500 disabled:bg-gray-400"
117+ onClick = { ( ) => console . log ( ) }
118+ />
125119 </ form >
126120 </ div >
127121 </ TicketHostLayout >
0 commit comments