287 lines
12 KiB
HTML
287 lines
12 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}Change Password{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="d-flex align-items-center mb-3">
|
|
<div>
|
|
<ol class="breadcrumb">
|
|
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'accounts:user_profile' %}">Profile</a></li>
|
|
<li class="breadcrumb-item active">Change Password</li>
|
|
</ol>
|
|
<h1 class="page-header mb-0">Change Password</h1>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row justify-content-center">
|
|
<div class="col-xl-6 col-lg-8">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h4 class="card-title">
|
|
<i class="fas fa-key me-2"></i>
|
|
Change Your Password
|
|
</h4>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if messages %}
|
|
{% for message in messages %}
|
|
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
|
|
{{ message }}
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
</div>
|
|
{% endfor %}
|
|
{% endif %}
|
|
|
|
{% if form.non_field_errors %}
|
|
<div class="alert alert-danger">
|
|
{{ form.non_field_errors }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="alert alert-info">
|
|
<i class="fas fa-info-circle me-2"></i>
|
|
For security reasons, please enter your current password to confirm your identity.
|
|
</div>
|
|
|
|
<form method="post" novalidate>
|
|
{% csrf_token %}
|
|
|
|
<div class="mb-3">
|
|
<label for="id_old_password" class="form-label">Current Password</label>
|
|
<div class="input-group">
|
|
<span class="input-group-text">
|
|
<i class="fas fa-lock"></i>
|
|
</span>
|
|
<input type="password"
|
|
class="form-control {% if form.old_password.errors %}is-invalid{% endif %}"
|
|
id="id_old_password"
|
|
name="old_password"
|
|
placeholder="Enter your current password"
|
|
required>
|
|
<button type="button" class="btn btn-outline-secondary" onclick="togglePassword('id_old_password')">
|
|
<i class="fas fa-eye"></i>
|
|
</button>
|
|
</div>
|
|
{% if form.old_password.errors %}
|
|
<div class="invalid-feedback d-block">
|
|
{{ form.old_password.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="id_new_password1" class="form-label">New Password</label>
|
|
<div class="input-group">
|
|
<span class="input-group-text">
|
|
<i class="fas fa-key"></i>
|
|
</span>
|
|
<input type="password"
|
|
class="form-control {% if form.new_password1.errors %}is-invalid{% endif %}"
|
|
id="id_new_password1"
|
|
name="new_password1"
|
|
placeholder="Enter your new password"
|
|
required>
|
|
<button type="button" class="btn btn-outline-secondary" onclick="togglePassword('id_new_password1')">
|
|
<i class="fas fa-eye"></i>
|
|
</button>
|
|
</div>
|
|
{% if form.new_password1.errors %}
|
|
<div class="invalid-feedback d-block">
|
|
{{ form.new_password1.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Password Requirements -->
|
|
<div class="mt-2">
|
|
<small class="text-muted">Password requirements:</small>
|
|
<div id="password-requirements" class="mt-1">
|
|
<div class="requirement small" id="req-length">
|
|
<i class="fas fa-times text-danger me-1"></i>At least 8 characters
|
|
</div>
|
|
<div class="requirement small" id="req-uppercase">
|
|
<i class="fas fa-times text-danger me-1"></i>One uppercase letter
|
|
</div>
|
|
<div class="requirement small" id="req-lowercase">
|
|
<i class="fas fa-times text-danger me-1"></i>One lowercase letter
|
|
</div>
|
|
<div class="requirement small" id="req-number">
|
|
<i class="fas fa-times text-danger me-1"></i>One number
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="id_new_password2" class="form-label">Confirm New Password</label>
|
|
<div class="input-group">
|
|
<span class="input-group-text">
|
|
<i class="fas fa-key"></i>
|
|
</span>
|
|
<input type="password"
|
|
class="form-control {% if form.new_password2.errors %}is-invalid{% endif %}"
|
|
id="id_new_password2"
|
|
name="new_password2"
|
|
placeholder="Confirm your new password"
|
|
required>
|
|
<button type="button" class="btn btn-outline-secondary" onclick="togglePassword('id_new_password2')">
|
|
<i class="fas fa-eye"></i>
|
|
</button>
|
|
</div>
|
|
{% if form.new_password2.errors %}
|
|
<div class="invalid-feedback d-block">
|
|
{{ form.new_password2.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
<div id="password-match" class="mt-1"></div>
|
|
</div>
|
|
|
|
<div class="alert alert-warning">
|
|
<i class="fas fa-exclamation-triangle me-2"></i>
|
|
<strong>Important:</strong> After changing your password, you will be logged out of all devices and need to sign in again.
|
|
</div>
|
|
|
|
<div class="d-flex justify-content-between">
|
|
<a href="{% url 'accounts:user_profile' %}" class="btn btn-secondary">
|
|
<i class="fas fa-arrow-left me-2"></i>Cancel
|
|
</a>
|
|
<button type="submit" class="btn btn-primary" id="changePasswordBtn">
|
|
<i class="fas fa-save me-2"></i>Change Password
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Security Tips -->
|
|
<div class="card mt-4">
|
|
<div class="card-header">
|
|
<h5 class="card-title">
|
|
<i class="fas fa-shield-alt me-2"></i>
|
|
Security Tips
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<ul class="list-unstyled mb-0">
|
|
<li class="mb-2">
|
|
<i class="fas fa-check text-success me-2"></i>
|
|
Use a unique password that you don't use elsewhere
|
|
</li>
|
|
<li class="mb-2">
|
|
<i class="fas fa-check text-success me-2"></i>
|
|
Include a mix of letters, numbers, and special characters
|
|
</li>
|
|
<li class="mb-2">
|
|
<i class="fas fa-check text-success me-2"></i>
|
|
Avoid using personal information in your password
|
|
</li>
|
|
<li class="mb-2">
|
|
<i class="fas fa-check text-success me-2"></i>
|
|
Consider using a password manager
|
|
</li>
|
|
<li>
|
|
<i class="fas fa-check text-success me-2"></i>
|
|
Change your password regularly
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const newPassword1 = document.getElementById('id_new_password1');
|
|
const newPassword2 = document.getElementById('id_new_password2');
|
|
const changePasswordBtn = document.getElementById('changePasswordBtn');
|
|
|
|
// Password validation
|
|
function validatePassword() {
|
|
const password = newPassword1.value;
|
|
const requirements = {
|
|
'req-length': password.length >= 8,
|
|
'req-uppercase': /[A-Z]/.test(password),
|
|
'req-lowercase': /[a-z]/.test(password),
|
|
'req-number': /\d/.test(password)
|
|
};
|
|
|
|
let allValid = true;
|
|
Object.keys(requirements).forEach(req => {
|
|
const element = document.getElementById(req);
|
|
const icon = element.querySelector('i');
|
|
|
|
if (requirements[req]) {
|
|
icon.className = 'fas fa-check text-success me-1';
|
|
} else {
|
|
icon.className = 'fas fa-times text-danger me-1';
|
|
allValid = false;
|
|
}
|
|
});
|
|
|
|
return allValid;
|
|
}
|
|
|
|
function checkPasswordMatch() {
|
|
const matchDiv = document.getElementById('password-match');
|
|
|
|
if (newPassword2.value === '') {
|
|
matchDiv.innerHTML = '';
|
|
return false;
|
|
}
|
|
|
|
if (newPassword1.value === newPassword2.value) {
|
|
matchDiv.innerHTML = '<small class="text-success"><i class="fas fa-check me-1"></i>Passwords match</small>';
|
|
return true;
|
|
} else {
|
|
matchDiv.innerHTML = '<small class="text-danger"><i class="fas fa-times me-1"></i>Passwords do not match</small>';
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function updateSubmitButton() {
|
|
const passwordValid = validatePassword();
|
|
const passwordsMatch = checkPasswordMatch();
|
|
const oldPasswordFilled = document.getElementById('id_old_password').value !== '';
|
|
|
|
changePasswordBtn.disabled = !(passwordValid && passwordsMatch && oldPasswordFilled);
|
|
}
|
|
|
|
newPassword1.addEventListener('input', function() {
|
|
validatePassword();
|
|
checkPasswordMatch();
|
|
updateSubmitButton();
|
|
});
|
|
|
|
newPassword2.addEventListener('input', function() {
|
|
checkPasswordMatch();
|
|
updateSubmitButton();
|
|
});
|
|
|
|
document.getElementById('id_old_password').addEventListener('input', updateSubmitButton);
|
|
|
|
// Form submission
|
|
const form = document.querySelector('form');
|
|
form.addEventListener('submit', function() {
|
|
changePasswordBtn.disabled = true;
|
|
changePasswordBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Changing Password...';
|
|
});
|
|
});
|
|
|
|
function togglePassword(inputId) {
|
|
const input = document.getElementById(inputId);
|
|
const button = input.nextElementSibling;
|
|
const icon = button.querySelector('i');
|
|
|
|
if (input.type === 'password') {
|
|
input.type = 'text';
|
|
icon.className = 'fas fa-eye-slash';
|
|
} else {
|
|
input.type = 'password';
|
|
icon.className = 'fas fa-eye';
|
|
}
|
|
}
|
|
</script>
|
|
{% endblock %}
|
|
|