Skip to content

Commit f93dc53

Browse files
committed
refactor: streamline TwoFactor UI logic, improve recovery mode toggle, and standardize naming conventions
1 parent da99c25 commit f93dc53

File tree

4 files changed

+43
-50
lines changed

4 files changed

+43
-50
lines changed

app/Models/User.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
class User extends Authenticatable
1212
{
1313
/** @use HasFactory<\Database\Factories\UserFactory> */
14-
use HasFactory, Notifiable,TwoFactorAuthenticatable;
14+
use HasFactory, Notifiable, TwoFactorAuthenticatable;
1515

1616
/**
1717
* The attributes that are mass assignable.

resources/js/composables/useClipboard.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ interface ClipboardOptions {
66

77
export function useClipboard(options: ClipboardOptions = {}) {
88
const { timeout = 1500 } = options;
9-
9+
1010
const copied = ref(false);
11-
11+
1212
const copyToClipboard = async (text: string): Promise<void> => {
1313
if (typeof window === 'undefined' || !navigator.clipboard) {
1414
return;
1515
}
16-
16+
1717
try {
1818
await navigator.clipboard.writeText(text);
1919
copied.value = true;
@@ -22,9 +22,9 @@ export function useClipboard(options: ClipboardOptions = {}) {
2222
console.error('Failed to copy to clipboard:', error);
2323
}
2424
};
25-
25+
2626
return {
2727
copied,
2828
copyToClipboard,
2929
};
30-
}
30+
}

resources/js/composables/useTwoFactorAuth.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@ export const useTwoFactorAuth = () => {
1919
fetchManualSetupKey,
2020
fetchRecoveryCodes,
2121
};
22-
};
22+
};

resources/js/pages/auth/TwoFactorChallenge.vue

Lines changed: 36 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,58 @@
11
<script setup lang="ts">
2-
import { ref, watch, nextTick, computed } from 'vue';
2+
import { computed, ComputedRef, nextTick, ref, watch } from 'vue';
33
import { Head, Form } from '@inertiajs/vue3';
44
import { Button } from '@/components/ui/button';
55
import { Input } from '@/components/ui/input';
66
import InputError from '@/components/InputError.vue';
77
import AuthLayout from '@/layouts/AuthLayout.vue';
88
import { PinInput, PinInputGroup, PinInputSlot } from '@/components/ui/pin-input';
99
10-
const recovery = ref(false);
11-
const pinValue = ref<number[]>([]);
12-
const pinInputContainerRef = ref<HTMLElement | null>(null);
10+
const showRecoveryInput = ref(false);
11+
const code = ref<number[]>([]);
1312
const recoveryInputRef = ref<HTMLInputElement | null>(null);
1413
15-
const codeValue = computed(() => pinValue.value.join(''));
14+
const codeValue: ComputedRef<string> = computed(() => code.value.join(''));
1615
17-
watch(recovery, (value) => {
18-
if (value) {
19-
nextTick(() => {
20-
if (recoveryInputRef.value) {
21-
recoveryInputRef.value?.focus();
22-
}
23-
});
24-
} else {
16+
const authConfig = computed(() => {
17+
return {
18+
title: showRecoveryInput.value ? 'Recovery Code' : 'Authentication Code',
19+
description: showRecoveryInput.value
20+
? 'Please confirm access to your account by entering one of your emergency recovery codes.'
21+
: 'Enter the authentication code provided by your authenticator application.',
22+
toggleText: showRecoveryInput.value
23+
? 'login using an authentication code'
24+
: 'login using a recovery code',
25+
};
26+
});
27+
28+
watch(
29+
showRecoveryInput,
30+
(isRecoveryMode) => {
2531
nextTick(() => {
26-
if (pinInputContainerRef.value) {
27-
const firstInput = pinInputContainerRef.value.querySelector('input');
28-
if (firstInput) firstInput.focus();
32+
if (isRecoveryMode && recoveryInputRef.value) {
33+
recoveryInputRef.value.focus();
2934
}
3035
});
31-
}
32-
});
36+
},
37+
);
3338
34-
const toggleRecovery = (clearErrors: () => void) => {
35-
recovery.value = !recovery.value;
39+
const toggleRecoveryMode = (clearErrors: () => void): void => {
40+
showRecoveryInput.value = !showRecoveryInput.value;
3641
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 = '';
42-
};
43-
</script>
42+
code.value = [];
43+
};</script>
4444

4545
<template>
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-
>
46+
<AuthLayout :title="authConfig.title" :description="authConfig.description">
5447
<Head title="Two Factor Authentication" />
5548

5649
<div class="relative space-y-2">
57-
<template v-if="!recovery">
50+
<template v-if="!showRecoveryInput">
5851
<Form :action="route('two-factor.login')" method="post" class="space-y-4" #default="{ errors, processing, clearErrors }">
5952
<input type="hidden" name="code" :value="codeValue" />
6053
<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>
54+
<div class="flex w-full items-center justify-center">
55+
<PinInput id="otp" v-model="code" class="mt-1" type="number" otp>
6356
<PinInputGroup class="gap-2">
6457
<PinInputSlot
6558
v-for="(id, index) in 6"
@@ -73,16 +66,16 @@ const toggleRecovery = (clearErrors: () => void) => {
7366
</div>
7467
<InputError :message="errors.code" />
7568
</div>
76-
<Button type="submit" class="w-full" :disabled="processing || pinValue.length !== 6"> Continue </Button>
69+
<Button type="submit" class="w-full" :disabled="processing || code.length !== 6"> Continue </Button>
7770

7871
<div class="mt-4 space-x-0.5 text-center text-sm leading-5 text-muted-foreground">
7972
<span class="opacity-80">or you can </span>
8073
<button
8174
type="button"
8275
class="cursor-pointer border-0 bg-transparent p-0 font-medium underline opacity-80"
83-
@click="() => toggleRecovery(clearErrors)"
76+
@click="() => toggleRecoveryMode(clearErrors)"
8477
>
85-
login using a recovery code
78+
{{ authConfig.toggleText }}
8679
</button>
8780
</div>
8881
</Form>
@@ -107,9 +100,9 @@ const toggleRecovery = (clearErrors: () => void) => {
107100
<button
108101
type="button"
109102
class="cursor-pointer border-0 bg-transparent p-0 font-medium underline opacity-80"
110-
@click="() => toggleRecovery(clearErrors)"
103+
@click="() => toggleRecoveryMode(clearErrors)"
111104
>
112-
login using an authentication code
105+
{{ authConfig.toggleText }}
113106
</button>
114107
</div>
115108
</Form>

0 commit comments

Comments
 (0)