hospital-management/templates/core/user_management.html
2025-08-12 13:33:25 +03:00

579 lines
29 KiB
HTML

{% extends 'base.html' %}
{% load static %}
{% block title %}User Management{% endblock %}
{% block css %}
<link href="{% static 'assets/plugins/datatables.net-bs5/css/dataTables.bootstrap5.min.css' %}" rel="stylesheet" />
<link href="{% static 'assets/plugins/select2/dist/css/select2.min.css' %}" rel="stylesheet" />
{% endblock %}
{% block content %}
<div id="content" class="app-content">
<div class="container">
<ul class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
<li class="breadcrumb-item active">User Management</li>
</ul>
<div class="row align-items-center mb-3">
<div class="col">
<h1 class="page-header">User Management</h1>
<p class="text-muted">Manage system users, roles, and permissions</p>
</div>
<div class="col-auto">
<div class="btn-group">
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createUserModal">
<i class="fa fa-plus me-2"></i>Add User
</button>
<button type="button" class="btn btn-outline-primary" onclick="exportUsers()">
<i class="fa fa-download me-2"></i>Export
</button>
<button type="button" class="btn btn-outline-secondary" onclick="bulkActions()">
<i class="fa fa-cogs me-2"></i>Bulk Actions
</button>
</div>
</div>
</div>
<!-- User Statistics -->
<div class="row mb-4">
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<div class="w-60px h-60px bg-primary bg-opacity-20 d-flex align-items-center justify-content-center rounded-circle mx-auto mb-3">
<i class="fa fa-users fa-2x text-primary"></i>
</div>
<h5>Total Users</h5>
<div class="fs-24px fw-600 text-primary">{{ total_users }}</div>
<div class="text-muted small">All system users</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<div class="w-60px h-60px bg-success bg-opacity-20 d-flex align-items-center justify-content-center rounded-circle mx-auto mb-3">
<i class="fa fa-user-check fa-2x text-success"></i>
</div>
<h5>Active Users</h5>
<div class="fs-24px fw-600 text-success">{{ active_users }}</div>
<div class="text-muted small">Currently active</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<div class="w-60px h-60px bg-warning bg-opacity-20 d-flex align-items-center justify-content-center rounded-circle mx-auto mb-3">
<i class="fa fa-user-clock fa-2x text-warning"></i>
</div>
<h5>Pending Users</h5>
<div class="fs-24px fw-600 text-warning">{{ pending_users }}</div>
<div class="text-muted small">Awaiting approval</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<div class="w-60px h-60px bg-info bg-opacity-20 d-flex align-items-center justify-content-center rounded-circle mx-auto mb-3">
<i class="fa fa-user-tie fa-2x text-info"></i>
</div>
<h5>Administrators</h5>
<div class="fs-24px fw-600 text-info">{{ admin_users }}</div>
<div class="text-muted small">System admins</div>
</div>
</div>
</div>
</div>
<!-- Filters -->
<div class="card mb-4">
<div class="card-body">
<form method="get" class="row g-3 align-items-end">
<div class="col-md-3">
<label class="form-label">Search</label>
<input type="text" name="search" class="form-control" placeholder="Name, email, or username" value="{{ request.GET.search }}">
</div>
<div class="col-md-2">
<label class="form-label">Department</label>
<select name="department" class="form-select">
<option value="">All Departments</option>
{% for dept in departments %}
<option value="{{ dept.id }}" {% if request.GET.department == dept.id|stringformat:"s" %}selected{% endif %}>{{ dept.name }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-2">
<label class="form-label">Role</label>
<select name="role" class="form-select">
<option value="">All Roles</option>
{% for role in roles %}
<option value="{{ role.id }}" {% if request.GET.role == role.id|stringformat:"s" %}selected{% endif %}>{{ role.name }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-2">
<label class="form-label">Status</label>
<select name="status" class="form-select">
<option value="">All Status</option>
<option value="active" {% if request.GET.status == 'active' %}selected{% endif %}>Active</option>
<option value="inactive" {% if request.GET.status == 'inactive' %}selected{% endif %}>Inactive</option>
<option value="pending" {% if request.GET.status == 'pending' %}selected{% endif %}>Pending</option>
<option value="suspended" {% if request.GET.status == 'suspended' %}selected{% endif %}>Suspended</option>
</select>
</div>
<div class="col-md-3">
<button type="submit" class="btn btn-primary">
<i class="fa fa-search me-2"></i>Filter
</button>
<a href="{% url 'core:user_management' %}" class="btn btn-outline-secondary ms-2">
<i class="fa fa-times me-2"></i>Clear
</a>
</div>
</form>
</div>
</div>
<!-- Users Table -->
<div class="card">
<div class="card-header">
<h4 class="card-title">System Users</h4>
<div class="card-toolbar">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="selectAll">
<label class="form-check-label" for="selectAll">Select All</label>
</div>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped" id="usersTable">
<thead>
<tr>
<th width="30">
<input type="checkbox" class="form-check-input" id="selectAllHeader">
</th>
<th>User</th>
<th>Department</th>
<th>Role</th>
<th>Status</th>
<th>Last Login</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>
<input type="checkbox" class="form-check-input user-checkbox" value="{{ user.id }}">
</td>
<td>
<div class="d-flex align-items-center">
<div class="w-40px h-40px bg-{{ user.avatar_color|default:'primary' }} bg-opacity-20 d-flex align-items-center justify-content-center rounded-circle me-3">
{% if user.avatar %}
<img src="{{ user.avatar.url }}" class="w-40px h-40px rounded-circle" alt="">
{% else %}
<i class="fa fa-user text-{{ user.avatar_color|default:'primary' }}"></i>
{% endif %}
</div>
<div>
<div class="fw-bold">{{ user.get_full_name|default:user.username }}</div>
<div class="text-muted small">{{ user.email }}</div>
{% if user.phone %}
<div class="text-muted small">{{ user.phone }}</div>
{% endif %}
</div>
</div>
</td>
<td>
{% if user.department %}
<span class="badge bg-info">{{ user.department.name }}</span>
{% else %}
<span class="text-muted">Not assigned</span>
{% endif %}
</td>
<td>
{% for role in user.roles.all %}
<span class="badge bg-secondary me-1">{{ role.name }}</span>
{% empty %}
<span class="text-muted">No roles</span>
{% endfor %}
</td>
<td>
{% if user.is_active %}
{% if user.is_online %}
<span class="badge bg-success">
<i class="fa fa-circle me-1"></i>Online
</span>
{% else %}
<span class="badge bg-primary">Active</span>
{% endif %}
{% elif user.is_pending %}
<span class="badge bg-warning">Pending</span>
{% elif user.is_suspended %}
<span class="badge bg-danger">Suspended</span>
{% else %}
<span class="badge bg-secondary">Inactive</span>
{% endif %}
</td>
<td>
{% if user.last_login %}
<div>{{ user.last_login|date:"M d, Y" }}</div>
<div class="text-muted small">{{ user.last_login|date:"g:i A" }}</div>
{% else %}
<span class="text-muted">Never</span>
{% endif %}
</td>
<td>
<div>{{ user.date_joined|date:"M d, Y" }}</div>
<div class="text-muted small">{{ user.created_by.get_full_name|default:user.created_by.username }}</div>
</td>
<td>
<div class="btn-group">
<button type="button" class="btn btn-outline-primary btn-sm" onclick="viewUser('{{ user.id }}')">
<i class="fa fa-eye"></i>
</button>
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="editUser('{{ user.id }}')">
<i class="fa fa-edit"></i>
</button>
<div class="btn-group">
<button type="button" class="btn btn-outline-secondary btn-sm dropdown-toggle" data-bs-toggle="dropdown">
<i class="fa fa-cog"></i>
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" onclick="resetPassword('{{ user.id }}')">
<i class="fa fa-key me-2"></i>Reset Password
</a></li>
<li><a class="dropdown-item" href="#" onclick="toggleStatus('{{ user.id }}')">
<i class="fa fa-toggle-on me-2"></i>Toggle Status
</a></li>
<li><a class="dropdown-item" href="#" onclick="viewLoginHistory('{{ user.id }}')">
<i class="fa fa-history me-2"></i>Login History
</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item text-danger" href="#" onclick="deleteUser('{{ user.id }}')">
<i class="fa fa-trash me-2"></i>Delete User
</a></li>
</ul>
</div>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Pagination -->
{% if is_paginated %}
<nav aria-label="Users pagination">
<ul class="pagination justify-content-center">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page=1{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}{% if request.GET.department %}&department={{ request.GET.department }}{% endif %}{% if request.GET.role %}&role={{ request.GET.role }}{% endif %}{% if request.GET.status %}&status={{ request.GET.status }}{% endif %}">First</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}{% if request.GET.department %}&department={{ request.GET.department }}{% endif %}{% if request.GET.role %}&role={{ request.GET.role }}{% endif %}{% if request.GET.status %}&status={{ request.GET.status }}{% endif %}">Previous</a>
</li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="page-item active">
<span class="page-link">{{ num }}</span>
</li>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<li class="page-item">
<a class="page-link" href="?page={{ num }}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}{% if request.GET.department %}&department={{ request.GET.department }}{% endif %}{% if request.GET.role %}&role={{ request.GET.role }}{% endif %}{% if request.GET.status %}&status={{ request.GET.status }}{% endif %}">{{ num }}</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}{% if request.GET.department %}&department={{ request.GET.department }}{% endif %}{% if request.GET.role %}&role={{ request.GET.role }}{% endif %}{% if request.GET.status %}&status={{ request.GET.status }}{% endif %}">Next</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.paginator.num_pages }}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}{% if request.GET.department %}&department={{ request.GET.department }}{% endif %}{% if request.GET.role %}&role={{ request.GET.role }}{% endif %}{% if request.GET.status %}&status={{ request.GET.status }}{% endif %}">Last</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
</div>
</div>
</div>
</div>
<!-- Create User Modal -->
<div class="modal fade" id="createUserModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Create New User</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form id="createUserForm">
<div class="modal-body">
{% csrf_token %}
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">First Name *</label>
<input type="text" name="first_name" class="form-control" required>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Last Name *</label>
<input type="text" name="last_name" class="form-control" required>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Username *</label>
<input type="text" name="username" class="form-control" required>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Email *</label>
<input type="email" name="email" class="form-control" required>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Phone</label>
<input type="tel" name="phone" class="form-control">
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Department</label>
<select name="department" class="form-select">
<option value="">Select Department</option>
{% for dept in departments %}
<option value="{{ dept.id }}">{{ dept.name }}</option>
{% endfor %}
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Role</label>
<select name="roles" class="form-select" multiple>
{% for role in roles %}
<option value="{{ role.id }}">{{ role.name }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Status</label>
<select name="status" class="form-select">
<option value="active">Active</option>
<option value="pending">Pending</option>
<option value="inactive">Inactive</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Password *</label>
<input type="password" name="password" class="form-control" required>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Confirm Password *</label>
<input type="password" name="password_confirm" class="form-control" required>
</div>
</div>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="send_welcome_email" checked>
<label class="form-check-label">Send welcome email to user</label>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Create User</button>
</div>
</form>
</div>
</div>
</div>
{% endblock %}
{% block js %}
<script src="{% static 'assets/plugins/datatables.net/js/jquery.dataTables.min.js' %}"></script>
<script src="{% static 'assets/plugins/datatables.net-bs5/js/dataTables.bootstrap5.min.js' %}"></script>
<script src="{% static 'assets/plugins/select2/dist/js/select2.min.js' %}"></script>
<script>
$(document).ready(function() {
// Initialize DataTable
$('#usersTable').DataTable({
responsive: true,
pageLength: 25,
order: [[1, 'asc']],
columnDefs: [
{ orderable: false, targets: [0, 7] }
]
});
// Initialize Select2
$('select[multiple]').select2({
placeholder: 'Select roles',
allowClear: true
});
// Select all functionality
$('#selectAllHeader, #selectAll').change(function() {
$('.user-checkbox').prop('checked', this.checked);
});
$('.user-checkbox').change(function() {
var allChecked = $('.user-checkbox:checked').length === $('.user-checkbox').length;
$('#selectAllHeader, #selectAll').prop('checked', allChecked);
});
// Create user form submission
$('#createUserForm').submit(function(e) {
e.preventDefault();
var formData = new FormData(this);
$.post('{% url "core:create_user" %}', formData, function(response) {
if (response.success) {
$('#createUserModal').modal('hide');
toastr.success('User created successfully');
location.reload();
} else {
toastr.error('Failed to create user: ' + response.error);
}
}).fail(function() {
toastr.error('Failed to create user');
});
});
});
function viewUser(userId) {
window.open('{% url "accounts:user_detail" 0 %}'.replace('0', userId), '_blank');
}
function editUser(userId) {
window.location.href = '{% url "accounts:user_edit" 0 %}'.replace('0', userId);
}
function resetPassword(userId) {
if (confirm('Reset password for this user? They will receive an email with instructions.')) {
$.post('{% url "core:reset_user_password" %}', {
'user_id': userId,
'csrfmiddlewaretoken': '{{ csrf_token }}'
}, function(response) {
if (response.success) {
toastr.success('Password reset email sent');
} else {
toastr.error('Failed to reset password: ' + response.error);
}
}).fail(function() {
toastr.error('Failed to reset password');
});
}
}
function toggleStatus(userId) {
$.post('{% url "core:toggle_user_status" %}', {
'user_id': userId,
'csrfmiddlewaretoken': '{{ csrf_token }}'
}, function(response) {
if (response.success) {
toastr.success('User status updated');
location.reload();
} else {
toastr.error('Failed to update status: ' + response.error);
}
}).fail(function() {
toastr.error('Failed to update status');
});
}
function viewLoginHistory(userId) {
window.open('{% url "core:user_login_history" 0 %}'.replace('0', userId), '_blank');
}
function deleteUser(userId) {
if (confirm('Are you sure you want to delete this user? This action cannot be undone.')) {
$.post('{% url "core:delete_user" %}', {
'user_id': userId,
'csrfmiddlewaretoken': '{{ csrf_token }}'
}, function(response) {
if (response.success) {
toastr.success('User deleted successfully');
location.reload();
} else {
toastr.error('Failed to delete user: ' + response.error);
}
}).fail(function() {
toastr.error('Failed to delete user');
});
}
}
function exportUsers() {
var params = new URLSearchParams(window.location.search);
params.set('export', 'csv');
window.location.href = '{% url "core:user_management" %}?' + params.toString();
}
function bulkActions() {
var selectedUsers = $('.user-checkbox:checked').map(function() {
return this.value;
}).get();
if (selectedUsers.length === 0) {
toastr.warning('Please select users first');
return;
}
// Show bulk actions modal or dropdown
var action = prompt('Enter action (activate, deactivate, delete):');
if (action) {
$.post('{% url "core:bulk_user_actions" %}', {
'user_ids': selectedUsers,
'action': action,
'csrfmiddlewaretoken': '{{ csrf_token }}'
}, function(response) {
if (response.success) {
toastr.success('Bulk action completed');
location.reload();
} else {
toastr.error('Failed to perform bulk action: ' + response.error);
}
}).fail(function() {
toastr.error('Failed to perform bulk action');
});
}
}
</script>
{% endblock %}