feat: add password toggle visibility and rename forgot-password to change-password

This commit is contained in:
Wini_Fy
2026-02-10 10:24:35 +07:00
parent 2ddf43c139
commit 5e7de227ec
8 changed files with 116 additions and 27 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 135 KiB

View File

@@ -0,0 +1,47 @@
document.addEventListener('DOMContentLoaded', function() {
const toggleButtons = document.querySelectorAll('.password-toggle-btn');
toggleButtons.forEach(button => {
// Hover effect logic
button.addEventListener('mouseenter', function() {
this.style.color = 'var(--primary-color)';
});
button.addEventListener('mouseleave', function() {
this.style.color = '#666';
});
button.addEventListener('click', function() {
// Find the input element within the same container
// We assume the structure is container > input + button
// or button is absolutely positioned relative to container
// The safest way is to look for the input in the parent container
const container = this.parentElement;
const input = container.querySelector('input');
if (input) {
const type = input.getAttribute('type') === 'password' ? 'text' : 'password';
input.setAttribute('type', type);
const icon = this.querySelector('i');
if (type === 'text') {
icon.classList.remove('fa-eye');
icon.classList.add('fa-eye-slash');
this.setAttribute('aria-label', 'Hide password');
} else {
icon.classList.remove('fa-eye-slash');
icon.classList.add('fa-eye');
this.setAttribute('aria-label', 'Show password');
}
}
});
// Add keyboard accessibility
button.addEventListener('keydown', function(e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
this.click();
}
});
});
});

View File

@@ -134,22 +134,22 @@ router.post('/register', async (req, res) => {
}
});
// Forgot Password Page
router.get('/forgot-password', (req, res) => {
res.render('auth/forgot-password', {
title: 'Forgot Password',
// Change Password Page
router.get('/change-password', (req, res) => {
res.render('auth/change-password', {
title: 'Change Password',
layout: false
});
});
// Forgot Password Handle
router.post('/forgot-password', async (req, res) => {
// Change Password Handle
router.post('/change-password', async (req, res) => {
const { email } = req.body;
try {
const user = await User.findOne({ email });
if (!user) {
req.flash('error_msg', 'No account with that email address exists.');
return res.redirect('/auth/forgot-password');
return res.redirect('/auth/change-password');
}
const token = crypto.randomBytes(20).toString('hex');
@@ -166,7 +166,7 @@ router.post('/forgot-password', async (req, res) => {
} catch (err) {
console.error(err);
req.flash('error_msg', 'Error processing request');
res.redirect('/auth/forgot-password');
res.redirect('/auth/change-password');
}
});
@@ -180,7 +180,7 @@ router.get('/reset-password/:token', async (req, res) => {
if (!user) {
req.flash('error_msg', 'Password reset token is invalid or has expired.');
return res.redirect('/auth/forgot-password');
return res.redirect('/auth/change-password');
}
res.render('auth/reset-password', {
@@ -190,7 +190,7 @@ router.get('/reset-password/:token', async (req, res) => {
});
} catch (err) {
console.error(err);
res.redirect('/auth/forgot-password');
res.redirect('/auth/change-password');
}
});
@@ -204,7 +204,7 @@ router.post('/reset-password/:token', async (req, res) => {
if (!user) {
req.flash('error_msg', 'Password reset token is invalid or has expired.');
return res.redirect('/auth/forgot-password');
return res.redirect('/auth/change-password');
}
if (req.body.password !== req.body.confirm_password) {

View File

@@ -170,11 +170,11 @@
</div>
<div style="text-align: center; margin-bottom: 20px;">
<h4 style="color: var(--primary-color); font-weight: 600; margin-bottom: 5px;">Forgot Password</h4>
<p style="color: var(--text-color); font-size: 13px;">Enter your email to reset password</p>
<h4 style="color: var(--primary-color); font-weight: 600; margin-bottom: 5px;">Change Password</h4>
<p style="color: var(--text-color); font-size: 13px;">Enter your email to change password</p>
</div>
<form action="/auth/forgot-password" method="POST" class="login-form">
<form action="/auth/change-password" method="POST" class="login-form">
<div class="form-group" style="margin-bottom: 12px;">
<label for="email" class="form-label">Email Address</label>
<input type="email" class="form-control" id="email" name="email" required autofocus>
@@ -182,7 +182,7 @@
<div style="text-align: center; margin-top: 20px;">
<button type="submit" class="btn-shine">
Reset Password
Continue
</button>
</div>

View File

@@ -170,12 +170,18 @@
<div class="form-group" style="margin-bottom: 12px;">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" name="password" required
autocomplete="current-password">
<div style="position: relative;">
<input type="password" class="form-control" id="password" name="password" required
autocomplete="current-password" style="padding-right: 40px;">
<button type="button" class="password-toggle-btn" aria-label="Show password"
style="position: absolute; right: 10px; top: 50%; transform: translateY(-50%); border: none; background: none; color: #666; cursor: pointer; padding: 0; z-index: 10;">
<i class="fas fa-eye"></i>
</button>
</div>
<a href="/auth/forgot-password"
<a href="/auth/change-password"
style="display: block; text-align: right; margin-top: 8px; color: #2563eb; text-decoration: none; font-size: 13px; font-weight: 600;">
Forgot Your Password?
Change Password?
</a>
</div>
@@ -209,6 +215,9 @@
<!-- Flash Handler JS -->
<script src="/js/flash-handler.js"></script>
<!-- Password Toggle JS -->
<script src="/js/password-toggle.js"></script>
</body>
</html>

View File

@@ -186,13 +186,26 @@
<div class="form-group" style="margin-bottom: 12px;">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" name="password" required>
<div style="position: relative;">
<input type="password" class="form-control" id="password" name="password" required
style="padding-right: 40px;">
<button type="button" class="password-toggle-btn" aria-label="Show password"
style="position: absolute; right: 10px; top: 50%; transform: translateY(-50%); border: none; background: none; color: #666; cursor: pointer; padding: 0; z-index: 10;">
<i class="fas fa-eye"></i>
</button>
</div>
</div>
<div class="form-group" style="margin-bottom: 12px;">
<label for="confirm_password" class="form-label">Confirm Password</label>
<input type="password" class="form-control" id="confirm_password" name="confirm_password"
required>
<div style="position: relative;">
<input type="password" class="form-control" id="confirm_password"
name="confirm_password" required style="padding-right: 40px;">
<button type="button" class="password-toggle-btn" aria-label="Show password"
style="position: absolute; right: 10px; top: 50%; transform: translateY(-50%); border: none; background: none; color: #666; cursor: pointer; padding: 0; z-index: 10;">
<i class="fas fa-eye"></i>
</button>
</div>
</div>
<div style="text-align: center; margin-top: 20px;">
@@ -251,6 +264,9 @@
}
});
</script>
<!-- Password Toggle JS -->
<script src="/js/password-toggle.js"></script>
</body>
</html>

View File

@@ -176,12 +176,26 @@
<form action="/auth/reset-password/<%= token %>" method="POST" class="login-form">
<div class="form-group" style="margin-bottom: 12px;">
<label for="password" class="form-label">New Password</label>
<input type="password" class="form-control" id="password" name="password" required autofocus>
<div style="position: relative;">
<input type="password" class="form-control" id="password" name="password" required autofocus
style="padding-right: 40px;">
<button type="button" class="password-toggle-btn" aria-label="Show password"
style="position: absolute; right: 10px; top: 50%; transform: translateY(-50%); border: none; background: none; color: #666; cursor: pointer; padding: 0; z-index: 10;">
<i class="fas fa-eye"></i>
</button>
</div>
</div>
<div class="form-group" style="margin-bottom: 12px;">
<label for="confirm_password" class="form-label">Confirm New Password</label>
<input type="password" class="form-control" id="confirm_password" name="confirm_password" required>
<div style="position: relative;">
<input type="password" class="form-control" id="confirm_password" name="confirm_password" required
style="padding-right: 40px;">
<button type="button" class="password-toggle-btn" aria-label="Show password"
style="position: absolute; right: 10px; top: 50%; transform: translateY(-50%); border: none; background: none; color: #666; cursor: pointer; padding: 0; z-index: 10;">
<i class="fas fa-eye"></i>
</button>
</div>
</div>
<div style="text-align: center; margin-top: 20px;">
@@ -234,6 +248,9 @@
}
});
</script>
<!-- Password Toggle JS -->
<script src="/js/password-toggle.js"></script>
</body>
</html>

View File

@@ -131,7 +131,7 @@
}
// Automatically clean up on every hide event
document.addEventListener('hidden.bs.modal', function() {
document.addEventListener('hidden.bs.modal', function () {
// Wait a tiny bit for the animation to finish
setTimeout(forceCleanupModals, 100);
});
@@ -146,11 +146,11 @@
}
}
}, 2000);
// Clean up on page load
window.addEventListener('load', forceCleanupModals);
</script>
<%- script %>
</body>
</html>
</html>