Initial user management.

This commit is contained in:
Jens Luedicke
2025-06-28 09:33:39 +02:00
parent dc4229e468
commit 452f3abd80
13 changed files with 766 additions and 37 deletions

9
templates/404.html Normal file
View File

@@ -0,0 +1,9 @@
{% extends "layout.html" %}
{% block content %}
<div class="error-container">
<h1>404 - Page Not Found</h1>
<p>The page you are looking for does not exist.</p>
<a href="{{ url_for('home') }}" class="btn">Return to Home</a>
</div>
{% endblock %}

0
templates/500.html Normal file
View File

View File

@@ -0,0 +1,88 @@
{% extends "layout.html" %}
{% block content %}
<div class="admin-container">
<h1>User Management</h1>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
<div class="admin-actions">
<a href="{{ url_for('create_user') }}" class="btn btn-success">Create New User</a>
</div>
<div class="user-list">
<table class="table">
<thead>
<tr>
<th>Username</th>
<th>Email</th>
<th>Role</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>{{ user.username }}</td>
<td>{{ user.email }}</td>
<td>{% if user.is_admin %}Admin{% else %}User{% endif %}</td>
<td>{{ user.created_at.strftime('%Y-%m-%d') }}</td>
<td>
<a href="{{ url_for('edit_user', user_id=user.id) }}" class="btn btn-sm btn-primary">Edit</a>
{% if user.id != g.user.id %}
<button class="btn btn-sm btn-danger" onclick="confirmDelete({{ user.id }}, '{{ user.username }}')">Delete</button>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Delete Confirmation Modal -->
<div id="delete-modal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<h2>Confirm Deletion</h2>
<p>Are you sure you want to delete user <span id="delete-username"></span>?</p>
<p>This action cannot be undone.</p>
<form id="delete-form" method="POST">
<button type="button" id="cancel-delete" class="btn btn-secondary">Cancel</button>
<button type="submit" class="btn btn-danger">Delete</button>
</form>
</div>
</div>
<script>
function confirmDelete(userId, username) {
document.getElementById('delete-username').textContent = username;
document.getElementById('delete-form').action = "{{ url_for('delete_user', user_id=0) }}".replace('0', userId);
document.getElementById('delete-modal').style.display = 'block';
}
// Close modal when clicking the X
document.querySelector('.close').addEventListener('click', function() {
document.getElementById('delete-modal').style.display = 'none';
});
// Close modal when clicking Cancel
document.getElementById('cancel-delete').addEventListener('click', function() {
document.getElementById('delete-modal').style.display = 'none';
});
// Close modal when clicking outside
window.addEventListener('click', function(event) {
if (event.target == document.getElementById('delete-modal')) {
document.getElementById('delete-modal').style.display = 'none';
}
});
</script>
</div>
{% endblock %}

View File

@@ -0,0 +1,44 @@
{% extends "layout.html" %}
{% block content %}
<div class="admin-container">
<h1>Create New User</h1>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
<form method="POST" action="{{ url_for('create_user') }}" class="user-form">
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" name="username" class="form-control" required autofocus>
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" class="form-control" required>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password" class="form-control" required>
</div>
<div class="form-group">
<label class="checkbox-container">
<input type="checkbox" name="is_admin"> Administrator privileges
<span class="checkmark"></span>
</label>
</div>
<div class="form-group">
<button type="submit" class="btn btn-success">Create User</button>
<a href="{{ url_for('admin_users') }}" class="btn btn-secondary">Cancel</a>
</div>
</form>
</div>
{% endblock %}

44
templates/edit_user.html Normal file
View File

@@ -0,0 +1,44 @@
{% extends "layout.html" %}
{% block content %}
<div class="admin-container">
<h1>Edit User: {{ user.username }}</h1>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
<form method="POST" action="{{ url_for('edit_user', user_id=user.id) }}" class="user-form">
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" name="username" class="form-control" value="{{ user.username }}" required>
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" class="form-control" value="{{ user.email }}" required>
</div>
<div class="form-group">
<label for="password">New Password (leave blank to keep current)</label>
<input type="password" id="password" name="password" class="form-control">
</div>
<div class="form-group">
<label class="checkbox-container">
<input type="checkbox" name="is_admin" {% if user.is_admin %}checked{% endif %}> Administrator privileges
<span class="checkmark"></span>
</label>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Update User</button>
<a href="{{ url_for('admin_users') }}" class="btn btn-secondary">Cancel</a>
</div>
</form>
</div>
{% endblock %}

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TimeTrack - {{ title }}</title>
<title>{{ title }} - TimeTrack</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
@@ -11,19 +11,48 @@
<nav>
<ul>
<li><a href="{{ url_for('home') }}">Home</a></li>
<li><a href="{{ url_for('history') }}">Complete History</a></li>
<li><a href="{{ url_for('config') }}" {% if title == 'Configuration' %}class="active"{% endif %}>Configuration</a></li>
<li><a href="{{ url_for('about') }}">About</a></li>
{% if g.user %}
<li><a href="{{ url_for('history') }}">History</a></li>
<li><a href="{{ url_for('config') }}">Config</a></li>
<li><a href="{{ url_for('about') }}">About</a></li>
<li><a href="{{ url_for('contact') }}">Contact</a></li>
<!-- Add Admin dropdown menu here -->
{% if g.user.is_admin %}
<li class="dropdown">
<a href="#" class="dropdown-toggle">Admin</a>
<ul class="dropdown-menu">
<li><a href="{{ url_for('admin_users') }}">User Management</a></li>
</ul>
</li>
{% endif %}
<li><a href="{{ url_for('profile') }}">Profile</a></li>
<li><a href="{{ url_for('logout') }}">Logout</a></li>
{% else %}
<li><a href="{{ url_for('login') }}">Login</a></li>
<li><a href="{{ url_for('register') }}">Register</a></li>
{% endif %}
</ul>
</nav>
</header>
<main>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<div class="flash-messages">
{% for category, message in messages %}
<div class="alert alert-{{ category }}">{{ message }}</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</main>
<footer>
<p>&copy; 2025 TimeTrack</p>
<p>&copy; {{ current_year }} TimeTrack. All rights reserved.</p>
</footer>
<script src="{{ url_for('static', filename='js/script.js') }}"></script>

42
templates/login.html Normal file
View File

@@ -0,0 +1,42 @@
{% extends "layout.html" %}
{% block content %}
<div class="auth-container">
<h1>Login to TimeTrack</h1>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
<form method="POST" action="{{ url_for('login') }}" class="auth-form">
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" name="username" class="form-control" required autofocus>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password" class="form-control" required>
</div>
<div class="form-group">
<label class="checkbox-container">
<input type="checkbox" name="remember"> Remember me
<span class="checkmark"></span>
</label>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Login</button>
</div>
<div class="auth-links">
<p>Don't have an account? <a href="{{ url_for('register') }}">Register here</a></p>
</div>
</form>
</div>
{% endblock %}

49
templates/profile.html Normal file
View File

@@ -0,0 +1,49 @@
{% extends "layout.html" %}
{% block content %}
<div class="profile-container">
<h1>My Profile</h1>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
<div class="profile-info">
<p><strong>Username:</strong> {{ user.username }}</p>
<p><strong>Account Type:</strong> {% if user.is_admin %}Administrator{% else %}User{% endif %}</p>
<p><strong>Member Since:</strong> {{ user.created_at.strftime('%Y-%m-%d') }}</p>
</div>
<h2>Update Profile</h2>
<form method="POST" action="{{ url_for('profile') }}" class="profile-form">
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" class="form-control" value="{{ user.email }}" required>
</div>
<h3>Change Password</h3>
<div class="form-group">
<label for="current_password">Current Password</label>
<input type="password" id="current_password" name="current_password" class="form-control">
</div>
<div class="form-group">
<label for="new_password">New Password</label>
<input type="password" id="new_password" name="new_password" class="form-control">
</div>
<div class="form-group">
<label for="confirm_password">Confirm New Password</label>
<input type="password" id="confirm_password" name="confirm_password" class="form-control">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Update Profile</button>
</div>
</form>
</div>
{% endblock %}

45
templates/register.html Normal file
View File

@@ -0,0 +1,45 @@
{% extends "layout.html" %}
{% block content %}
<div class="auth-container">
<h1>Register for TimeTrack</h1>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
<form method="POST" action="{{ url_for('register') }}" class="auth-form">
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" name="username" class="form-control" required autofocus>
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" class="form-control" required>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password" class="form-control" required>
</div>
<div class="form-group">
<label for="confirm_password">Confirm Password</label>
<input type="password" id="confirm_password" name="confirm_password" class="form-control" required>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Register</button>
</div>
<div class="auth-links">
<p>Already have an account? <a href="{{ url_for('login') }}">Login here</a></p>
</div>
</form>
</div>
{% endblock %}