1+ <template >
2+     <div  class =" fixed inset-0 z-[9999] flex items-center justify-center bg-black/50 top-0 bottom-0 left-0 right-0" 
3+     v-if  =" modelShow"  >
4+       <div  class =" relative bg-white dark:bg-gray-700 rounded-lg shadow p-6 w-full max-w-md"  >
5+         <div  id =" mfaCode-label"   class =" mb-4 text-gray-700 dark:text-gray-100"  >
6+           {{ $t('Please enter your authenticator code') }}
7+         </div >
8+   
9+         <div  class =" my-4 w-full flex justify-center"   ref =" otpRoot"  >
10+           <v-otp-input 
11+             ref =" code" 
12+             container-class =" grid grid-cols-6 gap-3 w-full" 
13+             input-classes =" bg-gray-50 text-center flex justify-center otp-input border leading-none border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-10 h-10 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" 
14+             :num-inputs =" 6" 
15+             inputType =" number" 
16+             inputmode =" numeric" 
17+             :should-auto-focus =" true" 
18+             :should-focus-order =" true" 
19+             v-model:value =" bindValue" 
20+             @on-complete =" handleOnComplete" 
21+           />
22+         </div >
23+   
24+         <div  class =" mt-6 flex justify-center gap-3"  >
25+           <button 
26+             class =" px-4 py-2 rounded border bg-gray-100 dark:bg-gray-600" 
27+             @click =" onCancel" 
28+             :disabled =" inProgress" 
29+           >{{ $t('Cancel') }}</button >
30+         </div >
31+       </div >
32+     </div >
33+   </template >
34+   
35+   <script  setup lang="ts">
36+   import  VOtpInput  from  ' vue3-otp-input'  ; 
37+   import  { ref , nextTick , onMounted , onBeforeUnmount  } from  ' vue'  ; 
38+   import  { useUserStore  } from  ' @/stores/user'  ; 
39+   import  { useI18n  } from  ' vue-i18n'  ; 
40+   declare  global  { 
41+     interface  Window  { 
42+       adminforthTwoFaModal:  { 
43+         getCode:  () =>  Promise <any >; 
44+       }; 
45+     } 
46+   } 
47+   const   props =  defineProps <{ 
48+     autoFinishLogin? :  boolean  
49+   }>(); 
50+   const   emit =  defineEmits <{ 
51+     (e :  ' resolved'  , payload :  any ):  void  
52+     (e :  ' rejected'  , err ? :  any ):  void  
53+     (e :  ' closed'  ):  void  
54+   }>(); 
55+ 
56+   const   modelShow =  ref (false ); 
57+   let  resolveFn:  ((code :  string ) =>  void ) |  null  =  null ; 
58+   let  verifyingCallback:  ((code :  string ) =>  boolean ) |  null  =  null ; 
59+   let  verifyFn:  null  |  ((code :  string ) =>  Promise <boolean > |  boolean ) =  null ; 
60+ 
61+   window .adminforthTwoFaModal  =  { 
62+     getCode : (verifyingCallback ? :  () =>  Promise <boolean >) =>  new  Promise ((resolve ) =>  { 
63+       if  (modelShow .value ) { 
64+         throw  new  Error (' Modal is already open'  ); 
65+       } 
66+       modelShow .value  =  true ; 
67+       resolveFn  =  resolve ; 
68+       verifyFn  =  verifyingCallback  ??  null ; 
69+     }), 
70+   }; 
71+    
72+   const   { t } =  useI18n (); 
73+   const   user =  useUserStore (); 
74+    
75+   const   code =  ref <any >(null ); 
76+   const   otpRoot =  ref <HTMLElement  |  null >(null ); 
77+   const   bindValue =  ref (' '  ); 
78+    
79+   function  tagOtpInputs() { 
80+     const   root =  otpRoot .value ; 
81+     if  (! root ) return ; 
82+     root .querySelectorAll (' input.otp-input'  ).forEach ((el , idx ) =>  { 
83+       el .setAttribute (' name'  , ' mfaCode'  ); 
84+       el .setAttribute (' id'  , ` mfaCode-${idx  +  1 } ` ); 
85+       el .setAttribute (' autocomplete'  , ' one-time-code'  ); 
86+       el .setAttribute (' inputmode'  , ' numeric'  ); 
87+       el .setAttribute (' aria-labelledby'  , ' mfaCode-label'  ); 
88+     }); 
89+   } 
90+    
91+   function  handlePaste(event :  ClipboardEvent ) { 
92+     event .preventDefault (); 
93+     const   pastedText =  event .clipboardData ?.getData (' text'  ) ||  ' '  ; 
94+     if  (pastedText .length  ===  6 ) { 
95+       code .value ?.fillInput (pastedText ); 
96+     } 
97+   } 
98+    
99+   async  function  submit() { 
100+     if  (bindValue .value .length  !==  6 ) return ; 
101+     await  sendCode (bindValue .value ); 
102+   } 
103+    
104+   async  function  handleOnComplete(value :  string ) { 
105+     await  sendCode (value ); 
106+   } 
107+    
108+   async  function  sendCode(value :  string ) { 
109+     if  (! resolveFn ) { 
110+       throw  new  Error (' Modal is not initialized properly'  ); 
111+     } 
112+ 
113+     if  (verifyFn ) { 
114+       try  { 
115+         const   ok =  await  verifyFn (value ); 
116+         if  (! ok ) { 
117+           return ; 
118+         } 
119+       } catch  { 
120+         return ; 
121+       } 
122+     } 
123+ 
124+     modelShow .value  =  false ; 
125+     resolveFn (value ); 
126+   } 
127+    
128+    
129+   function  onCancel() { 
130+     modelShow .value  =  false ; 
131+     emit (' rejected'  , new  Error (' cancelled'  )); 
132+     emit (' closed'  ); 
133+   } 
134+    
135+   onMounted (async  () =>  { 
136+     await  nextTick (); 
137+     tagOtpInputs (); 
138+     window .addEventListener (' paste'  , handlePaste  as  any ); 
139+   }); 
140+   onBeforeUnmount (() =>  { 
141+     window .removeEventListener (' paste'  , handlePaste  as  any ); 
142+   }); 
143+    </script >
144+   
0 commit comments