forked from UKSOURCE/cms.hailearning.edu.vn
Merge branch 'main' of https://gits.techvanguard.vn/UKSOURCE/cms.hailearning.edu.vn into fea/bao-10022026-AuditLog
This commit is contained in:
@@ -172,12 +172,39 @@ exports.update = async (req, res) => {
|
||||
location: parsedData.contactInfo?.location || "",
|
||||
socialLinks: parsedData.socialLinks || [],
|
||||
};
|
||||
console.log("✓ Converted to top object:", top);
|
||||
} catch (e) {
|
||||
console.error("✗ Error parsing topbarJson:", e.message);
|
||||
return res.status(400).json({
|
||||
|
||||
if (logo) {
|
||||
updateData.logo = logoData;
|
||||
}
|
||||
|
||||
console.log(
|
||||
"Preparing to update header with data:",
|
||||
JSON.stringify(updateData, null, 2),
|
||||
);
|
||||
|
||||
const updatedHeader = await Header.findByIdAndUpdate(
|
||||
headerId,
|
||||
updateData,
|
||||
{ new: true, runValidators: true },
|
||||
);
|
||||
|
||||
if (!updatedHeader) {
|
||||
console.error("✗ Header not found with ID:", headerId);
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Header not found",
|
||||
});
|
||||
}
|
||||
res.json({
|
||||
success: true,
|
||||
message: "Header updated successfully",
|
||||
data: updatedHeader,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("✗ Error updating header:", error);
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: "Invalid JSON in topbarJson: " + e.message,
|
||||
message: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 135 KiB |
47
public/js/password-toggle.js
Normal file
47
public/js/password-toggle.js
Normal 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();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user