334 lines
12 KiB
HTML
334 lines
12 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}Set New Password - Hospital Management System{% endblock %}
|
|
|
|
{% block css %}
|
|
<style>
|
|
.reset-confirm-container {
|
|
min-height: 100vh;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
}
|
|
|
|
.reset-confirm-card {
|
|
background: white;
|
|
border-radius: 15px;
|
|
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1);
|
|
overflow: hidden;
|
|
width: 100%;
|
|
max-width: 450px;
|
|
}
|
|
|
|
.reset-confirm-header {
|
|
background: linear-gradient(135deg, #348ac7 0%, #7474bf 100%);
|
|
color: white;
|
|
padding: 2rem;
|
|
text-align: center;
|
|
}
|
|
|
|
.reset-confirm-body {
|
|
padding: 2rem;
|
|
}
|
|
|
|
.btn-confirm {
|
|
background: linear-gradient(135deg, #348ac7 0%, #7474bf 100%);
|
|
border: none;
|
|
padding: 12px;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 1px;
|
|
}
|
|
|
|
.btn-confirm:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 5px 15px rgba(52, 138, 199, 0.4);
|
|
}
|
|
|
|
.password-requirements {
|
|
font-size: 0.875rem;
|
|
color: #6c757d;
|
|
}
|
|
|
|
.requirement {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.requirement.valid {
|
|
color: #28a745;
|
|
}
|
|
|
|
.requirement.invalid {
|
|
color: #dc3545;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="reset-confirm-container">
|
|
<div class="reset-confirm-card">
|
|
<div class="reset-confirm-header">
|
|
<h3 class="mb-0">
|
|
<i class="fas fa-hospital-alt me-2"></i>
|
|
Hospital Management
|
|
</h3>
|
|
<p class="mb-0 mt-2 opacity-75">Set your new password</p>
|
|
</div>
|
|
|
|
<div class="reset-confirm-body">
|
|
{% if validlink %}
|
|
<div class="text-center mb-4">
|
|
<div class="text-success mb-3">
|
|
<i class="fas fa-shield-alt fa-3x"></i>
|
|
</div>
|
|
<h4 class="mb-3">Create New Password</h4>
|
|
<p class="text-muted">
|
|
Please enter your new password below. Make sure it's strong and secure.
|
|
</p>
|
|
</div>
|
|
|
|
{% 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 %}
|
|
|
|
<form method="post" novalidate>
|
|
{% csrf_token %}
|
|
|
|
<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-lock"></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="password-requirements mt-2">
|
|
<small class="text-muted">Password requirements:</small>
|
|
<div id="password-requirements" class="mt-1">
|
|
<div class="requirement" id="req-length">
|
|
<i class="fas fa-times me-2"></i>At least 8 characters
|
|
</div>
|
|
<div class="requirement" id="req-uppercase">
|
|
<i class="fas fa-times me-2"></i>One uppercase letter
|
|
</div>
|
|
<div class="requirement" id="req-lowercase">
|
|
<i class="fas fa-times me-2"></i>One lowercase letter
|
|
</div>
|
|
<div class="requirement" id="req-number">
|
|
<i class="fas fa-times me-2"></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-lock"></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-info">
|
|
<i class="fas fa-info-circle me-2"></i>
|
|
After setting your new password, you'll be redirected to the login page.
|
|
</div>
|
|
|
|
<div class="d-grid">
|
|
<button type="submit" class="btn btn-primary btn-confirm" id="confirmBtn" disabled>
|
|
<i class="fas fa-save me-2"></i>Set New Password
|
|
</button>
|
|
</div>
|
|
</form>
|
|
|
|
{% else %}
|
|
<!-- Invalid or expired link -->
|
|
<div class="text-center">
|
|
<div class="text-danger mb-3">
|
|
<i class="fas fa-exclamation-triangle fa-3x"></i>
|
|
</div>
|
|
<h4 class="mb-3 text-danger">Invalid Reset Link</h4>
|
|
<p class="text-muted mb-4">
|
|
This password reset link is invalid or has expired.
|
|
Password reset links are only valid for 24 hours.
|
|
</p>
|
|
|
|
<div class="alert alert-warning">
|
|
<h6 class="alert-heading">
|
|
<i class="fas fa-clock me-2"></i>What happened?
|
|
</h6>
|
|
<ul class="mb-0 small">
|
|
<li>The link may have expired (links are valid for 24 hours)</li>
|
|
<li>The link may have already been used</li>
|
|
<li>The link may have been copied incorrectly</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="d-grid gap-2">
|
|
<a href="{% url 'accounts:password_reset' %}" class="btn btn-primary">
|
|
<i class="fas fa-redo me-2"></i>Request New Reset Link
|
|
</a>
|
|
<a href="{% url 'accounts:login' %}" class="btn btn-outline-secondary">
|
|
<i class="fas fa-arrow-left me-2"></i>Back to Login
|
|
</a>
|
|
</div>
|
|
|
|
<div class="text-center mt-3">
|
|
<small class="text-muted">
|
|
Need help?
|
|
<a href="mailto:support@hospital.com" class="text-decoration-none">Contact Support</a>
|
|
</small>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
{% if validlink %}
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const password1 = document.getElementById('id_new_password1');
|
|
const password2 = document.getElementById('id_new_password2');
|
|
const confirmBtn = document.getElementById('confirmBtn');
|
|
|
|
// Password validation
|
|
function validatePassword() {
|
|
const password = password1.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]) {
|
|
element.classList.add('valid');
|
|
element.classList.remove('invalid');
|
|
icon.className = 'fas fa-check me-2';
|
|
} else {
|
|
element.classList.add('invalid');
|
|
element.classList.remove('valid');
|
|
icon.className = 'fas fa-times me-2';
|
|
allValid = false;
|
|
}
|
|
});
|
|
|
|
return allValid;
|
|
}
|
|
|
|
function checkPasswordMatch() {
|
|
const matchDiv = document.getElementById('password-match');
|
|
|
|
if (password2.value === '') {
|
|
matchDiv.innerHTML = '';
|
|
return false;
|
|
}
|
|
|
|
if (password1.value === password2.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 updateConfirmButton() {
|
|
const passwordValid = validatePassword();
|
|
const passwordsMatch = checkPasswordMatch();
|
|
|
|
confirmBtn.disabled = !(passwordValid && passwordsMatch);
|
|
}
|
|
|
|
password1.addEventListener('input', function() {
|
|
validatePassword();
|
|
checkPasswordMatch();
|
|
updateConfirmButton();
|
|
});
|
|
|
|
password2.addEventListener('input', function() {
|
|
checkPasswordMatch();
|
|
updateConfirmButton();
|
|
});
|
|
|
|
// Form submission
|
|
const form = document.querySelector('form');
|
|
form.addEventListener('submit', function() {
|
|
confirmBtn.disabled = true;
|
|
confirmBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Setting Password...';
|
|
});
|
|
|
|
// Focus on first password field
|
|
password1.focus();
|
|
});
|
|
|
|
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';
|
|
}
|
|
}
|
|
{% endif %}
|
|
</script>
|
|
{% endblock %}
|
|
|