@@ -46,6 +46,7 @@ export class EmailVerificationModalComponent implements OnInit, OnDestroy {
4646 } ) ;
4747
4848 public submitting = signal ( false ) ;
49+ public errorMessage = signal < string | null > ( null ) ;
4950
5051 public ngOnInit ( ) : void {
5152 // Get email from dialog config
@@ -79,30 +80,123 @@ export class EmailVerificationModalComponent implements OnInit, OnDestroy {
7980 return ;
8081 }
8182
83+ // Clear any previous error message
84+ this . errorMessage . set ( null ) ;
85+
8286 // Combine all 6 digits into a single code
8387 const code = `${ this . verificationForm . value . digit1 } ${ this . verificationForm . value . digit2 } ${ this . verificationForm . value . digit3 } ${ this . verificationForm . value . digit4 } ${ this . verificationForm . value . digit5 } ${ this . verificationForm . value . digit6 } ` ;
8488 this . submitting . set ( true ) ;
8589
86- // Simulate submission - in real implementation this would call an API
87- setTimeout ( ( ) => {
88- this . submitting . set ( false ) ;
89- // Close the dialog and return the verification code
90- this . dialogRef . close ( { code, email : this . email } ) ;
91- } , 500 ) ;
90+ // Call the verification API
91+ this . userService
92+ . verifyAndLinkEmail ( this . email , code )
93+ . pipe ( finalize ( ( ) => this . submitting . set ( false ) ) )
94+ . subscribe ( {
95+ next : ( ) => {
96+ // On success, close the dialog and notify parent
97+ this . dialogRef . close ( { success : true , email : this . email } ) ;
98+ } ,
99+ error : ( error ) => {
100+ console . error ( 'Failed to verify code:' , error ) ;
101+
102+ // Check for specific error cases
103+ // ServiceValidationError structure: error.error.errors[0].message
104+ const specificMessage = error . error ?. errors ?. [ 0 ] ?. message || '' ;
105+ const genericMessage = error . error ?. message || error . message || '' ;
106+
107+ // Check both the specific validation message and generic message
108+ const errorText = ( specificMessage + ' ' + genericMessage ) . toLowerCase ( ) ;
109+
110+ let errorMsg = 'Invalid verification code. Please try again.' ;
111+
112+ if ( errorText . includes ( 'already linked' ) ) {
113+ // Email is already linked to another account - close modal and signal parent
114+ this . dialogRef . close ( {
115+ alreadyLinked : true ,
116+ email : this . email
117+ } ) ;
118+ return ;
119+ }
120+
121+ // Show error message in the modal for other errors
122+ this . errorMessage . set ( errorMsg ) ;
123+
124+ // Clear the form so user can try again
125+ this . verificationForm . reset ( ) ;
126+
127+ // Focus the first input
128+ setTimeout ( ( ) => {
129+ const firstInput = document . getElementById ( 'digit1' ) as HTMLInputElement ;
130+ if ( firstInput ) {
131+ firstInput . focus ( ) ;
132+ }
133+ } , 100 ) ;
134+ } ,
135+ } ) ;
92136 }
93137
94138 public onDigitInput ( event : Event , digitNumber : number ) : void {
95139 const input = event . target as HTMLInputElement ;
96140 const value = input . value ;
97141
98- // Only allow single digit numbers
99- if ( value && ! / ^ \d $ / . test ( value ) ) {
100- input . value = '' ;
142+ // Check if this is a paste event (multiple characters)
143+ if ( value . length > 1 ) {
144+ const digits = value . replace ( / \D / g, '' ) ;
145+
146+ if ( digits . length >= 6 ) {
147+ // This is a paste of 6 or more digits - fill all fields
148+ this . verificationForm . patchValue ( {
149+ digit1 : digits [ 0 ] || '' ,
150+ digit2 : digits [ 1 ] || '' ,
151+ digit3 : digits [ 2 ] || '' ,
152+ digit4 : digits [ 3 ] || '' ,
153+ digit5 : digits [ 4 ] || '' ,
154+ digit6 : digits [ 5 ] || '' ,
155+ } ) ;
156+
157+ // Clear the current input and focus the last field
158+ input . value = '' ;
159+ setTimeout ( ( ) => {
160+ const lastInput = document . getElementById ( 'digit6' ) as HTMLInputElement ;
161+ if ( lastInput ) {
162+ lastInput . focus ( ) ;
163+ }
164+ } , 0 ) ;
165+ return ;
166+ } else if ( digits . length > 0 ) {
167+ // Partial paste - just take the first digit
168+ const firstDigit = digits [ 0 ] ;
169+ input . value = firstDigit ;
170+ const controlName = `digit${ digitNumber } ` as 'digit1' | 'digit2' | 'digit3' | 'digit4' | 'digit5' | 'digit6' ;
171+ this . verificationForm . get ( controlName ) ?. setValue ( firstDigit ) ;
172+
173+ // Move to next field
174+ if ( digitNumber < 6 ) {
175+ const nextInput = document . getElementById ( `digit${ digitNumber + 1 } ` ) as HTMLInputElement ;
176+ if ( nextInput ) {
177+ nextInput . focus ( ) ;
178+ nextInput . select ( ) ;
179+ }
180+ }
181+ return ;
182+ }
183+ }
184+
185+ // Filter out non-digit characters
186+ const filteredValue = value . replace ( / \D / g, '' ) ;
187+ if ( filteredValue !== value ) {
188+ input . value = filteredValue ;
189+ return ;
190+ }
191+
192+ // Limit to single digit
193+ if ( filteredValue . length > 1 ) {
194+ input . value = filteredValue [ 0 ] ;
101195 return ;
102196 }
103197
104198 // If a digit was entered, move to next input
105- if ( value && digitNumber < 6 ) {
199+ if ( filteredValue && digitNumber < 6 ) {
106200 const nextInput = document . getElementById ( `digit${ digitNumber + 1 } ` ) as HTMLInputElement ;
107201 if ( nextInput ) {
108202 nextInput . focus ( ) ;
@@ -114,6 +208,13 @@ export class EmailVerificationModalComponent implements OnInit, OnDestroy {
114208 public onDigitKeyDown ( event : KeyboardEvent , digitNumber : number ) : void {
115209 const input = event . target as HTMLInputElement ;
116210
211+ // Handle Cmd+V (Mac) or Ctrl+V (Windows) paste
212+ if ( ( event . metaKey || event . ctrlKey ) && event . key === 'v' ) {
213+ // Don't prevent default - let the paste happen
214+ // The onDigitInput handler will process it
215+ return ;
216+ }
217+
117218 // Handle backspace - move to previous input if current is empty
118219 if ( event . key === 'Backspace' && ! input . value && digitNumber > 1 ) {
119220 event . preventDefault ( ) ;
@@ -152,23 +253,52 @@ export class EmailVerificationModalComponent implements OnInit, OnDestroy {
152253
153254 public onDigitPaste ( event : ClipboardEvent ) : void {
154255 event . preventDefault ( ) ;
256+ event . stopPropagation ( ) ;
257+
155258 const pastedData = event . clipboardData ?. getData ( 'text' ) || '' ;
156259 const digits = pastedData . replace ( / \D / g, '' ) . slice ( 0 , 6 ) ;
157260
158- if ( digits . length > 0 ) {
159- // Fill in the digits
261+ if ( digits . length >= 6 ) {
262+ // If we have 6 digits, fill all fields from the beginning
263+ this . verificationForm . patchValue ( {
264+ digit1 : digits [ 0 ] ,
265+ digit2 : digits [ 1 ] ,
266+ digit3 : digits [ 2 ] ,
267+ digit4 : digits [ 3 ] ,
268+ digit5 : digits [ 4 ] ,
269+ digit6 : digits [ 5 ] ,
270+ } ) ;
271+
272+ // Focus the last input after filling all
273+ setTimeout ( ( ) => {
274+ const lastInput = document . getElementById ( 'digit6' ) as HTMLInputElement ;
275+ if ( lastInput ) {
276+ lastInput . focus ( ) ;
277+ }
278+ } , 0 ) ;
279+ } else if ( digits . length > 0 ) {
280+ // For partial pastes, fill sequentially from digit 1
281+ const fieldMap : { [ key : number ] : 'digit1' | 'digit2' | 'digit3' | 'digit4' | 'digit5' | 'digit6' } = {
282+ 0 : 'digit1' ,
283+ 1 : 'digit2' ,
284+ 2 : 'digit3' ,
285+ 3 : 'digit4' ,
286+ 4 : 'digit5' ,
287+ 5 : 'digit6' ,
288+ } ;
289+
160290 for ( let i = 0 ; i < digits . length && i < 6 ; i ++ ) {
161- const controlName = `digit${ i + 1 } ` as 'digit1' | 'digit2' | 'digit3' | 'digit4' | 'digit5' | 'digit6' ;
162- this . verificationForm . get ( controlName ) ?. setValue ( digits [ i ] ) ;
291+ this . verificationForm . get ( fieldMap [ i ] ) ?. setValue ( digits [ i ] ) ;
163292 }
164293
165- // Focus the next empty input or the last one
166- const nextEmptyIndex = digits . length < 6 ? digits . length + 1 : 6 ;
167- const nextInput = document . getElementById ( `digit${ nextEmptyIndex } ` ) as HTMLInputElement ;
168- if ( nextInput ) {
169- nextInput . focus ( ) ;
170- nextInput . select ( ) ;
171- }
294+ // Focus the next empty input
295+ setTimeout ( ( ) => {
296+ const nextIndex = Math . min ( digits . length + 1 , 6 ) ;
297+ const nextInput = document . getElementById ( `digit${ nextIndex } ` ) as HTMLInputElement ;
298+ if ( nextInput ) {
299+ nextInput . focus ( ) ;
300+ }
301+ } , 0 ) ;
172302 }
173303 }
174304
0 commit comments