@@ -5,134 +5,115 @@ import { Button } from '@/components/ui/button';
55import { Input } from ' @/components/ui/input' ;
66import InputError from ' @/components/InputError.vue' ;
77import AuthLayout from ' @/layouts/AuthLayout.vue' ;
8- import { PinInputRoot , PinInputInput } from ' reka-ui ' ;
8+ import { PinInput , PinInputGroup , PinInputSlot } from ' @/components/ui/pin-input ' ;
99
1010const recovery = ref (false );
11- const pinValue = ref <string []>([]);
11+ const pinValue = ref <number []>([]);
1212const pinInputContainerRef = ref <HTMLElement | null >(null );
1313const recoveryInputRef = ref <HTMLInputElement | null >(null );
1414
1515const codeValue = computed (() => pinValue .value .join (' ' ));
1616
1717watch (recovery , (value ) => {
18- if (value ) {
19- nextTick (() => {
20- if (recoveryInputRef .value ) {
21- recoveryInputRef .value ?.focus ();
22- }
23- });
24- } else {
25- nextTick (() => {
26- if (pinInputContainerRef .value ) {
27- const firstInput = pinInputContainerRef .value .querySelector (' input' );
28- if (firstInput ) firstInput .focus ();
29- }
30- });
31- }
32- })
18+ if (value ) {
19+ nextTick (() => {
20+ if (recoveryInputRef .value ) {
21+ recoveryInputRef .value ?.focus ();
22+ }
23+ });
24+ } else {
25+ nextTick (() => {
26+ if (pinInputContainerRef .value ) {
27+ const firstInput = pinInputContainerRef .value .querySelector (' input' );
28+ if (firstInput ) firstInput .focus ();
29+ }
30+ });
31+ }
32+ });
3333
3434const toggleRecovery = (clearErrors : () => void ) => {
35- recovery .value = ! recovery .value ;
36- clearErrors ();
37- pinValue .value = [];
38- const codeInput = document .querySelector (' input[name="code"]' ) as HTMLInputElement ;
39- const recoveryInput = document .querySelector (' input[name="recovery_code"]' ) as HTMLInputElement ;
40- if (codeInput ) codeInput .value = ' ' ;
41- if (recoveryInput ) recoveryInput .value = ' ' ;
35+ recovery .value = ! recovery .value ;
36+ clearErrors ();
37+ pinValue .value = [];
38+ const codeInput = document .querySelector (' input[name="code"]' ) as HTMLInputElement ;
39+ const recoveryInput = document .querySelector (' input[name="recovery_code"]' ) as HTMLInputElement ;
40+ if (codeInput ) codeInput .value = ' ' ;
41+ if (recoveryInput ) recoveryInput .value = ' ' ;
4242};
4343 </script >
4444
4545<template >
46- <AuthLayout
47- :title =" recovery ? 'Recovery Code' : 'Authentication Code'"
48- :description =" recovery
49- ? 'Please confirm access to your account by entering one of your emergency recovery codes.'
50- : 'Enter the authentication code provided by your authenticator application.'"
51- >
52- <Head title =" Two Factor Authentication" />
46+ <AuthLayout
47+ :title =" recovery ? 'Recovery Code' : 'Authentication Code'"
48+ :description ="
49+ recovery
50+ ? 'Please confirm access to your account by entering one of your emergency recovery codes.'
51+ : 'Enter the authentication code provided by your authenticator application.'
52+ "
53+ >
54+ <Head title =" Two Factor Authentication" />
5355
54- <div class =" relative space-y-2" >
55- <template v-if =" ! recovery " >
56- <Form
57- :action =" route('two-factor.login')"
58- method =" post"
59- class =" space-y-4"
60- #default =" { errors, processing, clearErrors }"
61- >
62- <input type =" hidden" name =" code" :value =" codeValue" />
63- <div class =" flex flex-col items-center justify-center space-y-3 text-center" >
64- <div ref =" pinInputContainerRef" class =" w-full flex items-center justify-center" >
65- <PinInputRoot
66- id =" otp"
67- v-model =" pinValue"
68- placeholder =" ○"
69- class =" flex gap-2 items-center mt-1"
70- >
71- <PinInputInput
72- v-for =" (id, index) in 6"
73- :key =" id"
74- :index =" index"
75- :autofocus =" index === 0"
76- class =" w-10 h-10 rounded-lg text-center shadow-sm border text-green10 placeholder:text-mauve8 focus:shadow-[0_0_0_2px] focus:shadow-stone-800 outline-none"
77- />
78- </PinInputRoot >
79- </div >
80- <InputError :message =" errors.code" />
81- </div >
82- <Button
83- type =" submit"
84- class =" w-full"
85- :disabled =" processing || pinValue.length !== 6"
86- >
87- Continue
88- </Button >
56+ <div class =" relative space-y-2" >
57+ <template v-if =" ! recovery " >
58+ <Form :action =" route('two-factor.login')" method =" post" class =" space-y-4" #default =" { errors, processing, clearErrors }" >
59+ <input type =" hidden" name =" code" :value =" codeValue" />
60+ <div class =" flex flex-col items-center justify-center space-y-3 text-center" >
61+ <div ref =" pinInputContainerRef" class =" flex w-full items-center justify-center" >
62+ <PinInput id =" otp" v-model =" pinValue" class =" mt-1" type =" number" otp >
63+ <PinInputGroup class =" gap-2" >
64+ <PinInputSlot
65+ v-for =" (id, index) in 6"
66+ :key =" id"
67+ :index =" index"
68+ :autofocus =" index === 0"
69+ class =" h-10 w-10 rounded-lg"
70+ />
71+ </PinInputGroup >
72+ </PinInput >
73+ </div >
74+ <InputError :message =" errors.code" />
75+ </div >
76+ <Button type =" submit" class =" w-full" :disabled =" processing || pinValue.length !== 6" > Continue </Button >
8977
90- <div class =" space-x-0.5 text-center text-sm leading-5 text-muted-foreground mt-4 " >
91- <span class =" opacity-80" >or you can </span >
92- <button
93- type =" button"
94- class = " font-medium underline opacity-80 cursor-pointer bg-transparent border -0 p-0 "
95- @click =" () => toggleRecovery(clearErrors)"
96- >
97- login using a recovery code
98- </button >
99- </div >
100- </Form >
101- </template >
78+ <div class =" mt-4 space-x-0.5 text-center text-sm leading-5 text-muted-foreground" >
79+ <span class =" opacity-80" >or you can </span >
80+ <button
81+ type =" button"
82+ class = " cursor-pointer border-0 bg-transparent p -0 font-medium underline opacity-80 "
83+ @click =" () => toggleRecovery(clearErrors)"
84+ >
85+ login using a recovery code
86+ </button >
87+ </div >
88+ </Form >
89+ </template >
10290
103- <template v-else >
104- <Form
105- :action =" route('two-factor.login')"
106- method =" post"
107- class =" space-y-4"
108- #default =" { errors, processing, clearErrors }"
109- >
110- <Input
111- ref =" recoveryInputRef"
112- name =" recovery_code"
113- type =" text"
114- class =" block w-full"
115- placeholder =" Enter recovery code"
116- autofocus
117- required
118- />
119- <InputError :message =" errors.recovery_code" />
120- <Button type =" submit" class =" w-full" :disabled =" processing" >
121- Continue
122- </Button >
91+ <template v-else >
92+ <Form :action =" route('two-factor.login')" method =" post" class =" space-y-4" #default =" { errors, processing, clearErrors }" >
93+ <Input
94+ ref =" recoveryInputRef"
95+ name =" recovery_code"
96+ type =" text"
97+ class =" block w-full"
98+ placeholder =" Enter recovery code"
99+ autofocus
100+ required
101+ />
102+ <InputError :message =" errors.recovery_code" />
103+ <Button type =" submit" class =" w-full" :disabled =" processing" > Continue </Button >
123104
124- <div class =" space-x-0.5 text-center text-sm leading-5 text-muted-foreground mt-4 " >
125- <span class =" opacity-80" >or you can </span >
126- <button
127- type =" button"
128- class = " font-medium underline opacity-80 cursor-pointer bg-transparent border -0 p-0 "
129- @click =" () => toggleRecovery(clearErrors)"
130- >
131- login using an authentication code
132- </button >
133- </div >
134- </Form >
135- </template >
136- </div >
137- </AuthLayout >
105+ <div class =" mt-4 space-x-0.5 text-center text-sm leading-5 text-muted-foreground" >
106+ <span class =" opacity-80" >or you can </span >
107+ <button
108+ type =" button"
109+ class = " cursor-pointer border-0 bg-transparent p -0 font-medium underline opacity-80 "
110+ @click =" () => toggleRecovery(clearErrors)"
111+ >
112+ login using an authentication code
113+ </button >
114+ </div >
115+ </Form >
116+ </template >
117+ </div >
118+ </AuthLayout >
138119</template >
0 commit comments