Redesign TOTP/2FA page
This commit is contained in:
@ -7,10 +7,8 @@ import (
|
||||
|
||||
templ content(title string, msg types.Message) {
|
||||
@layout.Base(title){
|
||||
<main class="container mx-auto px-4 py-12 md:px-6 md:py-16 lg:py-10">
|
||||
<div class="flex min-h-screen items-center justify-center bg-background px-4 py-12 sm:px-6 lg:px-8">
|
||||
<div class="w-full max-w-md space-y-8">
|
||||
<div>
|
||||
<main class="bg-gray-100 flex items-center justify-center min-h-screen">
|
||||
<div class="bg-white shadow-md rounded-lg p-8 max-w-sm w-full">
|
||||
switch msg.Code {
|
||||
case 0:
|
||||
<div class="flex items-center p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50" role="alert">
|
||||
@ -23,42 +21,87 @@ templ content(title string, msg types.Message) {
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<h2 class="mt-6 text-center text-3xl font-bold tracking-tight text-foreground">Verify Your Identity</h2>
|
||||
<p class="mt-2 text-center text-sm text-muted-foreground">
|
||||
Please enter the 6-digit code generated by your authentication app to complete the login process.
|
||||
</p>
|
||||
<h1 class="text-2xl font-bold mb-2">Two-Factor Authentication</h1>
|
||||
<p class="text-gray-600 mb-6">Enter the 6-digit code from your authenticator app</p>
|
||||
<div class="flex justify-between mb-4" id="otpInputs">
|
||||
<input type="text" inputmode="numeric" pattern="\d{1}" maxlength="1"
|
||||
class="w-12 h-12 text-center text-2xl border-2 border-gray-300 rounded focus:border-blue-500 focus:outline-none"
|
||||
required>
|
||||
<input type="text" inputmode="numeric" pattern="\d{1}" maxlength="1"
|
||||
class="w-12 h-12 text-center text-2xl border-2 border-gray-300 rounded focus:border-blue-500 focus:outline-none"
|
||||
required>
|
||||
<input type="text" inputmode="numeric" pattern="\d{1}" maxlength="1"
|
||||
class="w-12 h-12 text-center text-2xl border-2 border-gray-300 rounded focus:border-blue-500 focus:outline-none"
|
||||
required>
|
||||
<input type="text" inputmode="numeric" pattern="\d{1}" maxlength="1"
|
||||
class="w-12 h-12 text-center text-2xl border-2 border-gray-300 rounded focus:border-blue-500 focus:outline-none"
|
||||
required>
|
||||
<input type="text" inputmode="numeric" pattern="\d{1}" maxlength="1"
|
||||
class="w-12 h-12 text-center text-2xl border-2 border-gray-300 rounded focus:border-blue-500 focus:outline-none"
|
||||
required>
|
||||
<input type="text" inputmode="numeric" pattern="\d{1}" maxlength="1"
|
||||
class="w-12 h-12 text-center text-2xl border-2 border-gray-300 rounded focus:border-blue-500 focus:outline-none"
|
||||
required>
|
||||
</div>
|
||||
<form class="space-y-6" method="POST">
|
||||
<div>
|
||||
<label for="code" class="block text-sm font-medium text-muted-foreground">
|
||||
Verification Code
|
||||
</label>
|
||||
<div class="mt-1">
|
||||
<input
|
||||
class="h-10 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 block w-full appearance-none rounded-md border border-input bg-background px-3 py-2 placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-primary sm:text-sm"
|
||||
id="code"
|
||||
autocomplete="one-time-code"
|
||||
required=""
|
||||
placeholder="123456"
|
||||
pattern="[0-9]{6}"
|
||||
maxlength="6"
|
||||
type="text"
|
||||
name="code"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
class="items-center whitespace-nowrap ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 h-10 flex w-full justify-center rounded-md bg-black py-2 px-4 text-sm font-medium text-primary-foreground shadow-sm text-white hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2"
|
||||
type="submit"
|
||||
>
|
||||
Verify Code
|
||||
<form id="otpForm" class="space-y-6" method="POST">
|
||||
<input type="hidden" id="otpValue" name="code">
|
||||
<button type="submit"
|
||||
class="w-full bg-blue-500 text-white py-2 px-4 rounded hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50 transition duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
id="submitButton" disabled>
|
||||
Verify
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</main>
|
||||
<script>
|
||||
const otpInputs = document.getElementById('otpInputs');
|
||||
const inputs = otpInputs.querySelectorAll('input[type="text"]');
|
||||
const form = document.getElementById('otpForm');
|
||||
const otpValue = document.getElementById('otpValue');
|
||||
const submitButton = document.getElementById('submitButton');
|
||||
|
||||
function updateOtpValue() {
|
||||
otpValue.value = Array.from(inputs).map(input => input.value).join('');
|
||||
submitButton.disabled = otpValue.value.length !== 6;
|
||||
}
|
||||
|
||||
inputs.forEach((input, index) => {
|
||||
input.addEventListener('input', function (e) {
|
||||
if (this.value.length === 1) {
|
||||
if (index < inputs.length - 1) {
|
||||
inputs[index + 1].focus();
|
||||
}
|
||||
} else if (this.value.length > 1) {
|
||||
this.value = this.value.slice(0, 1);
|
||||
}
|
||||
updateOtpValue();
|
||||
});
|
||||
|
||||
input.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Backspace' && this.value === '' && index > 0) {
|
||||
inputs[index - 1].focus();
|
||||
inputs[index - 1].value = '';
|
||||
updateOtpValue();
|
||||
}
|
||||
});
|
||||
|
||||
input.addEventListener('paste', function (e) {
|
||||
e.preventDefault();
|
||||
const pastedData = e.clipboardData.getData('text').slice(0, 6);
|
||||
for (let i = 0; i < pastedData.length; i++) {
|
||||
if (i + index < inputs.length) {
|
||||
inputs[i + index].value = pastedData[i];
|
||||
}
|
||||
}
|
||||
updateOtpValue();
|
||||
if (pastedData.length + index < inputs.length) {
|
||||
inputs[pastedData.length + index].focus();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ func content(title string, msg types.Message) templ.Component {
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<main class=\"container mx-auto px-4 py-12 md:px-6 md:py-16 lg:py-10\"><div class=\"flex min-h-screen items-center justify-center bg-background px-4 py-12 sm:px-6 lg:px-8\"><div class=\"w-full max-w-md space-y-8\"><div>")
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<main class=\"bg-gray-100 flex items-center justify-center min-h-screen\"><div class=\"bg-white shadow-md rounded-lg p-8 max-w-sm w-full\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -59,7 +59,7 @@ func content(title string, msg types.Message) templ.Component {
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(msg.Message)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/client/totp/totp.templ`, Line: 22, Col: 80}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/client/totp/totp.templ`, Line: 20, Col: 72}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -70,7 +70,7 @@ func content(title string, msg types.Message) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<h2 class=\"mt-6 text-center text-3xl font-bold tracking-tight text-foreground\">Verify Your Identity</h2><p class=\"mt-2 text-center text-sm text-muted-foreground\">Please enter the 6-digit code generated by your authentication app to complete the login process.</p></div><form class=\"space-y-6\" method=\"POST\"><div><label for=\"code\" class=\"block text-sm font-medium text-muted-foreground\">Verification Code</label><div class=\"mt-1\"><input class=\"h-10 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 block w-full appearance-none rounded-md border border-input bg-background px-3 py-2 placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-primary sm:text-sm\" id=\"code\" autocomplete=\"one-time-code\" required=\"\" placeholder=\"123456\" pattern=\"[0-9]{6}\" maxlength=\"6\" type=\"text\" name=\"code\"></div></div><div><button class=\"items-center whitespace-nowrap ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 h-10 flex w-full justify-center rounded-md bg-black py-2 px-4 text-sm font-medium text-primary-foreground shadow-sm text-white hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2\" type=\"submit\">Verify Code</button></div></form></div></div></main>")
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<h1 class=\"text-2xl font-bold mb-2\">Two-Factor Authentication</h1><p class=\"text-gray-600 mb-6\">Enter the 6-digit code from your authenticator app</p><div class=\"flex justify-between mb-4\" id=\"otpInputs\"><input type=\"text\" inputmode=\"numeric\" pattern=\"\\d{1}\" maxlength=\"1\" class=\"w-12 h-12 text-center text-2xl border-2 border-gray-300 rounded focus:border-blue-500 focus:outline-none\" required> <input type=\"text\" inputmode=\"numeric\" pattern=\"\\d{1}\" maxlength=\"1\" class=\"w-12 h-12 text-center text-2xl border-2 border-gray-300 rounded focus:border-blue-500 focus:outline-none\" required> <input type=\"text\" inputmode=\"numeric\" pattern=\"\\d{1}\" maxlength=\"1\" class=\"w-12 h-12 text-center text-2xl border-2 border-gray-300 rounded focus:border-blue-500 focus:outline-none\" required> <input type=\"text\" inputmode=\"numeric\" pattern=\"\\d{1}\" maxlength=\"1\" class=\"w-12 h-12 text-center text-2xl border-2 border-gray-300 rounded focus:border-blue-500 focus:outline-none\" required> <input type=\"text\" inputmode=\"numeric\" pattern=\"\\d{1}\" maxlength=\"1\" class=\"w-12 h-12 text-center text-2xl border-2 border-gray-300 rounded focus:border-blue-500 focus:outline-none\" required> <input type=\"text\" inputmode=\"numeric\" pattern=\"\\d{1}\" maxlength=\"1\" class=\"w-12 h-12 text-center text-2xl border-2 border-gray-300 rounded focus:border-blue-500 focus:outline-none\" required></div><form id=\"otpForm\" class=\"space-y-6\" method=\"POST\"><input type=\"hidden\" id=\"otpValue\" name=\"code\"> <button type=\"submit\" class=\"w-full bg-blue-500 text-white py-2 px-4 rounded hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50 transition duration-200 disabled:opacity-50 disabled:cursor-not-allowed\" id=\"submitButton\" disabled>Verify</button></form></div></main><script>\n const otpInputs = document.getElementById('otpInputs');\n const inputs = otpInputs.querySelectorAll('input[type=\"text\"]');\n const form = document.getElementById('otpForm');\n const otpValue = document.getElementById('otpValue');\n const submitButton = document.getElementById('submitButton');\n\n function updateOtpValue() {\n otpValue.value = Array.from(inputs).map(input => input.value).join('');\n submitButton.disabled = otpValue.value.length !== 6;\n }\n\n inputs.forEach((input, index) => {\n input.addEventListener('input', function (e) {\n if (this.value.length === 1) {\n if (index < inputs.length - 1) {\n inputs[index + 1].focus();\n }\n } else if (this.value.length > 1) {\n this.value = this.value.slice(0, 1);\n }\n updateOtpValue();\n });\n\n input.addEventListener('keydown', function (e) {\n if (e.key === 'Backspace' && this.value === '' && index > 0) {\n inputs[index - 1].focus();\n inputs[index - 1].value = '';\n updateOtpValue();\n }\n });\n\n input.addEventListener('paste', function (e) {\n e.preventDefault();\n const pastedData = e.clipboardData.getData('text').slice(0, 6);\n for (let i = 0; i < pastedData.length; i++) {\n if (i + index < inputs.length) {\n inputs[i + index].value = pastedData[i];\n }\n }\n updateOtpValue();\n if (pastedData.length + index < inputs.length) {\n inputs[pastedData.length + index].focus();\n }\n });\n });\n </script>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
Reference in New Issue
Block a user