mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-01-21 03:50:01 -06:00
217 lines
9.9 KiB
HTML
217 lines
9.9 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}User Management - {{ app_name }}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid">
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<div>
|
|
<nav aria-label="breadcrumb">
|
|
<ol class="breadcrumb">
|
|
<li class="breadcrumb-item"><a href="{{ url_for('admin.admin_dashboard') }}">Admin</a></li>
|
|
<li class="breadcrumb-item active">Users</li>
|
|
</ol>
|
|
</nav>
|
|
<h1 class="h3 mb-0">
|
|
<i class="fas fa-users text-primary"></i> User Management
|
|
</h1>
|
|
</div>
|
|
<div>
|
|
<a href="{{ url_for('admin.create_user') }}" class="btn btn-primary">
|
|
<i class="fas fa-user-plus"></i> New User
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- User Statistics -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-3">
|
|
<div class="card text-center">
|
|
<div class="card-body">
|
|
<i class="fas fa-users fa-2x text-primary mb-2"></i>
|
|
<h4 class="text-primary">{{ stats.total_users }}</h4>
|
|
<p class="text-muted mb-0">Total Users</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card text-center">
|
|
<div class="card-body">
|
|
<i class="fas fa-user-check fa-2x text-success mb-2"></i>
|
|
<h4 class="text-success">{{ stats.active_users }}</h4>
|
|
<p class="text-muted mb-0">Active Users</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card text-center">
|
|
<div class="card-body">
|
|
<i class="fas fa-user-shield fa-2x text-warning mb-2"></i>
|
|
<h4 class="text-warning">{{ stats.admin_users }}</h4>
|
|
<p class="text-muted mb-0">Admin Users</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card text-center">
|
|
<div class="card-body">
|
|
<i class="fas fa-clock fa-2x text-info mb-2"></i>
|
|
<h4 class="text-info">{{ "%.1f"|format(stats.total_hours) }}h</h4>
|
|
<p class="text-muted mb-0">Total Hours</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Users List -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-list"></i> Users ({{ users|length }})
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if users %}
|
|
<div class="table-responsive">
|
|
<table class="table table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th>Username</th>
|
|
<th>Role</th>
|
|
<th>Status</th>
|
|
<th>Created</th>
|
|
<th>Last Login</th>
|
|
<th>Total Hours</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for user in users %}
|
|
<tr>
|
|
<td>
|
|
<div>
|
|
<strong>{{ user.username }}</strong>
|
|
{% if user.active_timer %}
|
|
<br><small class="text-warning">
|
|
<i class="fas fa-clock"></i> Timer Running
|
|
</small>
|
|
{% endif %}
|
|
</div>
|
|
</td>
|
|
<td>
|
|
{% if user.role == 'admin' %}
|
|
<span class="badge bg-warning">Admin</span>
|
|
{% else %}
|
|
<span class="badge bg-secondary">User</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
{% if user.is_active %}
|
|
<span class="badge bg-success">Active</span>
|
|
{% else %}
|
|
<span class="badge bg-danger">Inactive</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>{{ user.created_at.strftime('%Y-%m-%d') }}</td>
|
|
<td>
|
|
{% if user.last_login %}
|
|
{{ user.last_login.strftime('%Y-%m-%d %H:%M') }}
|
|
{% else %}
|
|
<span class="text-muted">Never</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<strong>{{ "%.1f"|format(user.total_hours) }}h</strong>
|
|
</td>
|
|
<td>
|
|
<div class="btn-group" role="group">
|
|
<a href="{{ url_for('admin.edit_user', user_id=user.id) }}"
|
|
class="btn btn-sm btn-outline-primary" title="Edit">
|
|
<i class="fas fa-edit"></i>
|
|
</a>
|
|
{% if user.id != current_user.id %}
|
|
<button type="button" class="btn btn-sm btn-outline-danger" title="Delete"
|
|
onclick="showDeleteUserModal('{{ user.id }}', '{{ user.username }}')">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-5">
|
|
<i class="fas fa-users fa-3x text-muted mb-3"></i>
|
|
<h4 class="text-muted">No Users Found</h4>
|
|
<p class="text-muted">No users have been created yet.</p>
|
|
<a href="{{ url_for('admin.create_user') }}" class="btn btn-primary">
|
|
<i class="fas fa-user-plus"></i> Create First User
|
|
</a>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Delete User Modal -->
|
|
<div class="modal fade" id="deleteUserModal" tabindex="-1">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">
|
|
<i class="fas fa-trash me-2 text-danger"></i>Delete User
|
|
</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="alert alert-danger">
|
|
<i class="fas fa-exclamation-triangle me-2"></i>
|
|
<strong>Warning:</strong> This action cannot be undone.
|
|
</div>
|
|
<p>Are you sure you want to delete the user <strong id="deleteUserName"></strong>?</p>
|
|
<p class="text-muted mb-0">This will permanently remove the user and all their data cannot be recovered.</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
|
|
<i class="fas fa-times me-1"></i>Cancel
|
|
</button>
|
|
<form method="POST" id="deleteUserForm" class="d-inline">
|
|
<button type="submit" class="btn btn-danger">
|
|
<i class="fas fa-trash me-2"></i>Delete User
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Function to show delete user modal
|
|
function showDeleteUserModal(userId, username) {
|
|
document.getElementById('deleteUserName').textContent = username;
|
|
document.getElementById('deleteUserForm').action = "{{ url_for('admin.delete_user', user_id=0) }}".replace('0', userId);
|
|
new bootstrap.Modal(document.getElementById('deleteUserModal')).show();
|
|
}
|
|
|
|
// Add loading state to delete user form
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
document.getElementById('deleteUserForm').addEventListener('submit', function(e) {
|
|
const submitBtn = this.querySelector('button[type="submit"]');
|
|
submitBtn.innerHTML = '<div class="loading-spinner me-2"></div>Deleting...';
|
|
submitBtn.disabled = true;
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %}
|