@@ -9,49 +9,64 @@ import Button from '../../../common/Button';
99import apiClient from '../../../utils/apiClient' ;
1010import useSyncFormTranslations from '../../../common/useSyncFormTranslations' ;
1111
12- function asyncValidate ( fieldToValidate , value ) {
12+ // Debounce utility function
13+ const debounce = ( func , delay ) => {
14+ let timer ;
15+ return ( ...args ) =>
16+ new Promise ( ( resolve ) => {
17+ clearTimeout ( timer ) ;
18+ timer = setTimeout ( ( ) => resolve ( func ( ...args ) ) , delay ) ;
19+ } ) ;
20+ } ;
21+
22+ // API Validation Function
23+ async function asyncValidate ( fieldToValidate , value ) {
1324 if ( ! value || value . trim ( ) . length === 0 ) {
1425 return '' ;
1526 }
16- const queryParams = { } ;
17- queryParams [ fieldToValidate ] = value ;
18- queryParams . check_type = fieldToValidate ;
19- return apiClient
20- . get ( '/signup/duplicate_check' , { params : queryParams } )
21- . then ( ( response ) => {
22- if ( response . data . exists ) {
23- return response . data . message ;
24- }
25- return '' ;
27+ const queryParams = {
28+ [ fieldToValidate ] : value ,
29+ check_type : fieldToValidate
30+ } ;
31+
32+ try {
33+ const response = await apiClient . get ( '/signup/duplicate_check' , {
34+ params : queryParams
2635 } ) ;
36+ return response . data . exists ? response . data . message : '' ;
37+ } catch ( error ) {
38+ return 'Error validating field.' ;
39+ }
2740}
2841
42+ // Debounced Validators
43+ const debouncedAsyncValidate = debounce ( asyncValidate , 300 ) ;
44+
2945function validateUsername ( username ) {
30- return asyncValidate ( 'username' , username ) ;
46+ return debouncedAsyncValidate ( 'username' , username ) ;
3147}
3248
3349function validateEmail ( email ) {
34- return asyncValidate ( 'email' , email ) ;
50+ return debouncedAsyncValidate ( 'email' , email ) ;
3551}
3652
3753function SignupForm ( ) {
3854 const { t, i18n } = useTranslation ( ) ;
39-
4055 const dispatch = useDispatch ( ) ;
41- function onSubmit ( formProps ) {
42- return dispatch ( validateAndSignUpUser ( formProps ) ) ;
43- }
56+ const formRef = useRef ( null ) ;
4457 const [ showPassword , setShowPassword ] = useState ( false ) ;
4558 const [ showConfirmPassword , setShowConfirmPassword ] = useState ( false ) ;
46- const formRef = useRef ( null ) ;
47- const handleVisibility = ( ) => {
48- setShowPassword ( ! showPassword ) ;
49- } ;
50- const handleConfirmVisibility = ( ) => {
51- setShowConfirmPassword ( ! showConfirmPassword ) ;
52- } ;
59+
5360 useSyncFormTranslations ( formRef , i18n . language ) ;
5461
62+ const handleVisibility = ( ) => setShowPassword ( ! showPassword ) ;
63+ const handleConfirmVisibility = ( ) =>
64+ setShowConfirmPassword ( ! showConfirmPassword ) ;
65+
66+ function onSubmit ( formProps ) {
67+ return dispatch ( validateAndSignUpUser ( formProps ) ) ;
68+ }
69+
5570 return (
5671 < Form
5772 fields = { [ 'username' , 'email' , 'password' , 'confirmPassword' ] }
@@ -62,75 +77,72 @@ function SignupForm() {
6277 formRef . current = form ;
6378 return (
6479 < form className = "form" onSubmit = { handleSubmit } >
80+ { /* Username Field */ }
6581 < Field
6682 name = "username"
6783 validate = { validateUsername }
6884 validateFields = { [ ] }
6985 >
70- { ( field ) => (
86+ { ( { input , meta } ) => (
7187 < div className = "form__field" >
7288 < label htmlFor = "username" className = "form__label" >
7389 { t ( 'SignupForm.Title' ) }
7490 </ label >
7591 < input
7692 className = "form__input"
77- aria-label = { t ( 'SignupForm.TitleARIA' ) }
7893 type = "text"
7994 id = "username"
8095 autoComplete = "username"
8196 autoCapitalize = "none"
82- { ...field . input }
97+ { ...input }
8398 />
84- { field . meta . touched && field . meta . error && (
85- < span className = "form-error" aria-live = "polite" >
86- { field . meta . error }
87- </ span >
99+ { meta . touched && meta . error && (
100+ < span className = "form-error" > { meta . error } </ span >
88101 ) }
89102 </ div >
90103 ) }
91104 </ Field >
105+
106+ { /* Email Field */ }
92107 < Field name = "email" validate = { validateEmail } validateFields = { [ ] } >
93- { ( field ) => (
108+ { ( { input , meta } ) => (
94109 < div className = "form__field" >
95110 < label htmlFor = "email" className = "form__label" >
96111 { t ( 'SignupForm.Email' ) }
97112 </ label >
98113 < input
99114 className = "form__input"
100- aria-label = { t ( 'SignupForm.EmailARIA' ) }
101115 type = "email"
102116 id = "email"
103117 autoComplete = "email"
104- { ...field . input }
118+ { ...input }
105119 />
106- { field . meta . touched && field . meta . error && (
107- < span className = "form-error" aria-live = "polite" >
108- { field . meta . error }
109- </ span >
120+ { meta . touched && meta . error && (
121+ < span className = "form-error" > { meta . error } </ span >
110122 ) }
111123 </ div >
112124 ) }
113125 </ Field >
126+
127+ { /* Password Field */ }
114128 < Field name = "password" >
115- { ( field ) => (
129+ { ( { input , meta } ) => (
116130 < div className = "form__field" >
117131 < label htmlFor = "password" className = "form__label" >
118132 { t ( 'SignupForm.Password' ) }
119133 </ label >
120134 < div className = "form__field__password" >
121135 < input
122136 className = "form__input"
123- aria-label = { t ( 'SignupForm.PasswordARIA' ) }
124137 type = { showPassword ? 'text' : 'password' }
125138 id = "password"
126139 autoComplete = "new-password"
127- { ...field . input }
140+ { ...input }
128141 />
129142 < button
130143 className = "form__eye__icon"
131144 type = "button"
132145 onClick = { handleVisibility }
133- aria-hidden = "true"
134146 >
135147 { showPassword ? (
136148 < AiOutlineEyeInvisible />
@@ -139,16 +151,16 @@ function SignupForm() {
139151 ) }
140152 </ button >
141153 </ div >
142- { field . meta . touched && field . meta . error && (
143- < span className = "form-error" aria-live = "polite" >
144- { field . meta . error }
145- </ span >
154+ { meta . touched && meta . error && (
155+ < span className = "form-error" > { meta . error } </ span >
146156 ) }
147157 </ div >
148158 ) }
149159 </ Field >
160+
161+ { /* Confirm Password Field */ }
150162 < Field name = "confirmPassword" >
151- { ( field ) => (
163+ { ( { input , meta } ) => (
152164 < div className = "form__field" >
153165 < label htmlFor = "confirmPassword" className = "form__label" >
154166 { t ( 'SignupForm.ConfirmPassword' ) }
@@ -157,16 +169,14 @@ function SignupForm() {
157169 < input
158170 className = "form__input"
159171 type = { showConfirmPassword ? 'text' : 'password' }
160- aria-label = { t ( 'SignupForm.ConfirmPasswordARIA' ) }
161- id = "confirmPassword" // Match the id with htmlFor
172+ id = "confirmPassword"
162173 autoComplete = "new-password"
163- { ...field . input }
174+ { ...input }
164175 />
165176 < button
166177 className = "form__eye__icon"
167178 type = "button"
168179 onClick = { handleConfirmVisibility }
169- aria-hidden = "true"
170180 >
171181 { showConfirmPassword ? (
172182 < AiOutlineEyeInvisible />
@@ -175,14 +185,14 @@ function SignupForm() {
175185 ) }
176186 </ button >
177187 </ div >
178- { field . meta . touched && field . meta . error && (
179- < span className = "form-error" aria-live = "polite" >
180- { field . meta . error }
181- </ span >
188+ { meta . touched && meta . error && (
189+ < span className = "form-error" > { meta . error } </ span >
182190 ) }
183191 </ div >
184192 ) }
185193 </ Field >
194+
195+ { /* Submit Button */ }
186196 < Button type = "submit" disabled = { submitting || invalid || pristine } >
187197 { t ( 'SignupForm.SubmitSignup' ) }
188198 </ Button >
0 commit comments