Files
filekeeper/view/client/totp/totp.templ

110 lines
4.9 KiB
Plaintext

package totpView
import (
"github.com/fossyy/filekeeper/view/client/layout"
"github.com/fossyy/filekeeper/types"
)
templ content(title string, msg types.Message) {
@layout.Base(title){
<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">
<svg class="flex-shrink-0 inline w-4 h-4 me-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
<path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"/>
</svg>
<span class="sr-only">Info</span>
<div>
<span class="font-medium">Error!</span> {msg.Message}
</div>
</div>
}
<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>
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>
}
}
templ Main(title string, msg types.Message) {
@content(title, msg)
}