@@ -3,14 +3,15 @@ import HeadingSmall from '@/components/HeadingSmall.vue';
33import InputError from ' @/components/InputError.vue' ;
44import { Badge } from ' @/components/ui/badge' ;
55import { Button } from ' @/components/ui/button' ;
6+ import { Card , CardContent , CardDescription , CardHeader , CardTitle } from ' @/components/ui/card' ;
67import { Dialog , DialogContent , DialogDescription , DialogHeader , DialogTitle , DialogTrigger } from ' @/components/ui/dialog' ;
78import { PinInput , PinInputGroup , PinInputSlot } from ' @/components/ui/pin-input' ;
89import { useClipboard } from ' @/composables/useClipboard' ;
910import { useTwoFactorAuth } from ' @/composables/useTwoFactorAuth' ;
1011import AppLayout from ' @/layouts/AppLayout.vue' ;
1112import SettingsLayout from ' @/layouts/settings/Layout.vue' ;
1213import { Form , Head } from ' @inertiajs/vue3' ;
13- import { Check , Copy , Eye , EyeOff , Loader2 , LockKeyhole , ScanLine } from ' lucide-vue-next' ;
14+ import { Check , Copy , Eye , EyeOff , Loader2 , LockKeyhole , RefreshCw , ScanLine , ShieldBan } from ' lucide-vue-next' ;
1415import { computed , ComputedRef , nextTick , reactive , ref } from ' vue' ;
1516
1617const props = withDefaults (
@@ -85,22 +86,22 @@ const modalConfig = computed(() => {
8586 return {
8687 title: ' You have enabled two factor authentication.' ,
8788 description: ' Two factor authentication is now enabled, scan the QR code or enter the setup key' ,
88- buttonText: ' Close'
89+ buttonText: ' Close' ,
8990 };
9091 }
9192
9293 if (modalState .isInVerificationStep ) {
9394 return {
9495 title: ' Verify Authentication Code' ,
9596 description: ' Enter the 6-digit code from your authenticator app' ,
96- buttonText: ' Continue'
97+ buttonText: ' Continue' ,
9798 };
9899 }
99100
100101 return {
101102 title: ' Turn on 2-step Verification' ,
102103 description: ' To finish enabling two factor authentication, scan the QR code or enter the setup key' ,
103- buttonText: ' Continue'
104+ buttonText: ' Continue' ,
104105 };
105106});
106107
@@ -131,9 +132,9 @@ const handleSetupAction = (): void => {
131132 <SettingsLayout >
132133 <div class =" space-y-6" >
133134 <HeadingSmall title =" Two-Factor Authentication" description =" Manage your two-factor authentication settings" />
134- <div v-if =" !twoFactorEnabled" class =" flex flex-col items-start justify-start space-y-5 " >
135+ <div v-if =" !twoFactorEnabled" class =" flex flex-col items-start justify-start space-y-4 " >
135136 <Badge variant =" destructive" > Disabled </Badge >
136- <p class =" -translate-y-1 text-muted-foreground" >
137+ <p class =" text-muted-foreground" >
137138 When you enable 2FA, you'll be prompted for a secure code during login, which can be retrieved from your phone's TOTP
138139 supported app.
139140 </p >
@@ -153,38 +154,27 @@ const handleSetupAction = (): void => {
153154 </Dialog >
154155 </div >
155156
156- <div v-if =" twoFactorEnabled" class =" flex flex-col items-start justify-start space-y-5 " >
157+ <div v-if =" twoFactorEnabled" class =" flex flex-col items-start justify-start space-y-4 " >
157158 <Badge variant =" default" > Enabled </Badge >
158159 <p class =" text-muted-foreground" >
159160 With two factor authentication enabled, you'll be prompted for a secure, random token during login, which you can retrieve
160161 from your TOTP Authenticator app.
161162 </p >
162163 <Dialog v-model:open =" modalState.isOpen" ></Dialog >
163- <div >
164- <div class =" flex items-start rounded-t-xl border border-secondary p-4" >
165- <LockKeyhole class =" mr-2 size-5 text-muted-foreground" />
166- <div class =" space-y-1" >
167- <h3 class =" font-medium" >2FA Recovery Codes</h3 >
168- <p class =" text-sm text-muted-foreground" >
169- Recovery codes let you regain access if you lose your 2FA device. Store them in a secure password manager.
170- </p >
171- </div >
172- </div >
173-
174- <div class =" rounded-b-xl border border-t-0 text-sm" >
175- <div
176- @click =" toggleRecoveryCodesVisibility"
177- class =" group flex h-10 cursor-pointer items-center justify-between px-5 text-xs select-none"
164+ <Card >
165+ <CardHeader >
166+ <CardTitle class =" flex gap-3" ><LockKeyhole class =" size-4" /> 2FA Recovery Codes</CardTitle >
167+ <CardDescription
168+ >Recovery codes let you regain access if you lose your 2FA device. Store them in a secure password
169+ manager.</CardDescription
178170 >
179- <div :class =" `relative ${!recoveryCodes.isVisible ? 'opacity-40 hover:opacity-60' : 'opacity-60'}`" >
180- <span v-if =" !recoveryCodes.isVisible" class =" flex items-center space-x-1" >
181- <Eye class =" size-4" /> <span >View My Recovery Codes</span >
182- </span >
183- <span v-else class =" flex items-center space-x-1" >
184- <EyeOff class =" size-4" /> <span >Hide Recovery Codes</span >
185- </span >
186- </div >
187-
171+ </CardHeader >
172+ <CardContent >
173+ <div class =" group flex items-center justify-between select-none" >
174+ <Button @click =" toggleRecoveryCodesVisibility" >
175+ <component :is =" recoveryCodes.isVisible ? EyeOff : Eye" class =" size-4" />
176+ {{ recoveryCodes.isVisible ? 'Hide' : 'View' }} Recovery Codes
177+ </Button >
188178 <Form
189179 v-if =" recoveryCodes.isVisible"
190180 :action =" route('two-factor.recovery-codes')"
@@ -194,29 +184,31 @@ const handleSetupAction = (): void => {
194184 preserveScroll: true,
195185 }"
196186 >
197- <Button size =" sm" variant =" secondary" type =" submit" :disabled =" processing" @click.stop =" toggleRecoveryCodesVisibility" >
187+ <Button variant =" secondary" type =" submit" :disabled =" processing" @click.stop =" toggleRecoveryCodesVisibility" >
188+ <RefreshCw />
198189 {{ processing ? 'Regenerating...' : 'Regenerate Codes' }}
199190 </Button >
200191 </Form >
201192 </div >
202193
203194 <div
204- class =" relative overflow-hidden transition-all duration-300"
205- :style =" {
206- height: recoveryCodes.isVisible ? 'auto' : '0',
207- opacity: recoveryCodes.isVisible ? 1 : 0,
208- }"
195+ :class =" [
196+ 'relative overflow-hidden transition-all duration-300',
197+ recoveryCodes.isVisible ? 'h-auto opacity-100' : 'h-0 opacity-0',
198+ ]"
209199 >
210- <div class =" grid max-w-xl gap-1 bg-muted p-4 font-mono text-sm" >
211- <div v-for =" (code, index) in recoveryCodes.list" :key =" index" >{{ code }}</div >
200+ <div class =" space-y-3 mt-3" >
201+ <div class =" grid gap-1 rounded-lg bg-muted p-4 font-mono text-sm" >
202+ <div v-for =" (code, index) in recoveryCodes.list" :key =" index" >{{ code }}</div >
203+ </div >
204+ <p class =" text-xs text-muted-foreground select-none" >
205+ You have {{ recoveryCodes.list?.length }} recovery codes left. Each can be used once to access your account
206+ and will be removed after use. If you need more, click <span class =" font-bold" >Regenerate Codes</span > above.
207+ </p >
212208 </div >
213- <p class =" text-xs text-muted-foreground select-none" >
214- You have {{ recoveryCodes.list?.length }} recovery codes left. Each can be used once to access your account and
215- will be removed after use. If you need more, click <span class =" font-bold" >Regenerate Codes</span > above.
216- </p >
217209 </div >
218- </div >
219- </div >
210+ </CardContent >
211+ </Card >
220212
221213 <div class =" relative inline" >
222214 <Form
@@ -227,6 +219,7 @@ const handleSetupAction = (): void => {
227219 @success =" disableTwoFactorAuthenticationSuccess"
228220 >
229221 <Button variant =" destructive" type =" submit" :disabled =" processing" >
222+ <ShieldBan />
230223 {{ processing ? 'Disabling...' : 'Disable 2FA' }}
231224 </Button >
232225 </Form >
@@ -238,15 +231,11 @@ const handleSetupAction = (): void => {
238231 <DialogHeader class =" flex items-center justify-center" >
239232 <div class =" mb-3 w-auto rounded-full border border-border bg-card p-0.5 shadow-sm" >
240233 <div class =" relative overflow-hidden rounded-full border border-border bg-muted p-2.5" >
241- <div
242- class =" absolute inset-0 flex size-full items-stretch justify-around divide-x divide-border opacity-50 [& >div]:flex-1"
243- >
244- <div v-for =" i in 5" :key =" i" ></div >
234+ <div class =" absolute inset-0 grid grid-cols-5 opacity-50" >
235+ <div v-for =" i in 5" :key =" `col-${i}`" class =" border-r border-border last:border-r-0" ></div >
245236 </div >
246- <div
247- class =" absolute inset-0 flex size-full flex-col items-stretch justify-around divide-y divide-border opacity-50 [& >div]:flex-1"
248- >
249- <div v-for =" i in 5" :key =" i" ></div >
237+ <div class =" absolute inset-0 grid grid-rows-5 opacity-50" >
238+ <div v-for =" i in 5" :key =" `row-${i}`" class =" border-b border-border last:border-b-0" ></div >
250239 </div >
251240 <ScanLine class =" relative z-20 size-6 text-foreground" />
252241 </div >
0 commit comments