mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-31 10:18:55 +00:00
Update templates and controllers for 2fa
This commit is contained in:
@@ -89,14 +89,19 @@
|
||||
{{csrf_field()}}
|
||||
<input type="hidden" name="redirect_to_profile" value="1">
|
||||
<div class="mb-4">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-key"></i></span>
|
||||
<input id="verify-code" type="text" class="form-control" name="verify-code" required placeholder="Enter 6-digit code" autocomplete="off">
|
||||
<div class="d-flex justify-content-between gap-2 mb-2">
|
||||
<input type="text" class="form-control text-center otp-input" maxlength="1" pattern="[0-9]" inputmode="numeric" autocomplete="off" required>
|
||||
<input type="text" class="form-control text-center otp-input" maxlength="1" pattern="[0-9]" inputmode="numeric" autocomplete="off" required>
|
||||
<input type="text" class="form-control text-center otp-input" maxlength="1" pattern="[0-9]" inputmode="numeric" autocomplete="off" required>
|
||||
<input type="text" class="form-control text-center otp-input" maxlength="1" pattern="[0-9]" inputmode="numeric" autocomplete="off" required>
|
||||
<input type="text" class="form-control text-center otp-input" maxlength="1" pattern="[0-9]" inputmode="numeric" autocomplete="off" required>
|
||||
<input type="text" class="form-control text-center otp-input" maxlength="1" pattern="[0-9]" inputmode="numeric" autocomplete="off" required>
|
||||
</div>
|
||||
<input id="verify-code" type="hidden" name="verify-code" required>
|
||||
<div class="form-text">Enter the 6-digit code from your authenticator app</div>
|
||||
</div>
|
||||
<div class="d-grid gap-2">
|
||||
<button type="submit" class="btn btn-success">
|
||||
<button type="submit" class="btn btn-success" id="verify-button">
|
||||
<i class="fa fa-check-circle me-2"></i>Verify and Enable 2FA
|
||||
</button>
|
||||
<a href="{{url("/profileedit#security")}}" class="btn btn-outline-secondary">
|
||||
@@ -136,16 +141,111 @@
|
||||
.notification-fade.show {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.otp-input {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.otp-input {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Show notification alerts with animation
|
||||
setTimeout(function() {
|
||||
const alerts = document.querySelectorAll('.notification-fade');
|
||||
alerts.forEach(function(alert) {
|
||||
alert.classList.add('show');
|
||||
});
|
||||
}, 100);
|
||||
|
||||
// Handle OTP input functionality
|
||||
const otpInputs = document.querySelectorAll('.otp-input');
|
||||
const verifyCodeInput = document.getElementById('verify-code');
|
||||
const verifyButton = document.getElementById('verify-button');
|
||||
|
||||
if (otpInputs.length > 0) {
|
||||
// Set focus on first input on page load
|
||||
otpInputs[0].focus();
|
||||
|
||||
// Add event listeners to each OTP input
|
||||
otpInputs.forEach(function(input, index) {
|
||||
// Handle number input and auto-focus
|
||||
input.addEventListener('input', function(e) {
|
||||
const value = e.target.value;
|
||||
|
||||
// Only allow digits
|
||||
if (/^\d*$/.test(value)) {
|
||||
// Auto advance to next input
|
||||
if (value.length === 1 && index < otpInputs.length - 1) {
|
||||
otpInputs[index + 1].focus();
|
||||
}
|
||||
|
||||
// Combine all inputs into the hidden field
|
||||
updateHiddenField();
|
||||
} else {
|
||||
// Clear non-numeric input
|
||||
e.target.value = '';
|
||||
}
|
||||
});
|
||||
|
||||
// Handle backspace key
|
||||
input.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Backspace' && !e.target.value && index > 0) {
|
||||
// Move to previous input when backspace is pressed on empty input
|
||||
otpInputs[index - 1].focus();
|
||||
}
|
||||
});
|
||||
|
||||
// Handle paste event (allow pasting full 6-digit code)
|
||||
input.addEventListener('paste', function(e) {
|
||||
e.preventDefault();
|
||||
const pasteData = (e.clipboardData || window.clipboardData).getData('text');
|
||||
const digits = pasteData.replace(/\D/g, '').substring(0, 6).split('');
|
||||
|
||||
if (digits.length > 0) {
|
||||
// Fill inputs with pasted digits
|
||||
otpInputs.forEach((input, i) => {
|
||||
if (i < digits.length) {
|
||||
input.value = digits[i];
|
||||
}
|
||||
});
|
||||
|
||||
// Focus on appropriate field
|
||||
if (digits.length >= 6) {
|
||||
otpInputs[5].focus();
|
||||
} else {
|
||||
otpInputs[Math.min(digits.length, 5)].focus();
|
||||
}
|
||||
|
||||
// Update hidden field
|
||||
updateHiddenField();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Submit form on full code entry
|
||||
function updateHiddenField() {
|
||||
const code = Array.from(otpInputs).map(input => input.value).join('');
|
||||
verifyCodeInput.value = code;
|
||||
|
||||
// Auto submit if all 6 digits are entered
|
||||
if (code.length === 6 && /^\d{6}$/.test(code)) {
|
||||
setTimeout(() => {
|
||||
verifyButton.classList.add('btn-pulse');
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -488,6 +488,57 @@
|
||||
this.setAttribute('data-bs-original-title', 'Copy to clipboard');
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
// Check if there's a hash in the URL to activate the appropriate tab
|
||||
var hash = window.location.hash;
|
||||
if (hash) {
|
||||
var tabId = hash.substring(1);
|
||||
var tabElement = document.getElementById(tabId + '-tab');
|
||||
if (tabElement) {
|
||||
var tab = new bootstrap.Tab(tabElement);
|
||||
tab.show();
|
||||
|
||||
// If the security tab is opened and there are 2FA messages, animate them
|
||||
if (tabId === 'security') {
|
||||
animateAlerts();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Animate alerts when they become visible
|
||||
function animateAlerts() {
|
||||
var alerts = document.querySelectorAll('#security .alert');
|
||||
alerts.forEach(function(alert) {
|
||||
if (alert.style.opacity !== '1') {
|
||||
alert.style.opacity = '0';
|
||||
alert.style.transition = 'opacity 0.4s ease-in-out';
|
||||
setTimeout(function() {
|
||||
alert.style.opacity = '1';
|
||||
}, 100);
|
||||
|
||||
// Add auto-dismiss after 10 seconds for success alerts
|
||||
if (alert.classList.contains('alert-success')) {
|
||||
setTimeout(function() {
|
||||
alert.style.opacity = '0';
|
||||
setTimeout(function() {
|
||||
alert.style.display = 'none';
|
||||
}, 400);
|
||||
}, 10000);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Add event listener for tab changes
|
||||
var tabElements = document.querySelectorAll('a[data-bs-toggle="tab"]');
|
||||
tabElements.forEach(function(tabElement) {
|
||||
tabElement.addEventListener('shown.bs.tab', function(event) {
|
||||
// When security tab is shown, animate alerts
|
||||
if (event.target.getAttribute('href') === '#security') {
|
||||
animateAlerts();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
{/literal}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user