Initial user management.
This commit is contained in:
9
templates/404.html
Normal file
9
templates/404.html
Normal 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
0
templates/500.html
Normal file
88
templates/admin_users.html
Normal file
88
templates/admin_users.html
Normal 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">×</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 %}
|
||||
44
templates/create_user.html
Normal file
44
templates/create_user.html
Normal 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
44
templates/edit_user.html
Normal 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 %}
|
||||
@@ -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>© 2025 TimeTrack</p>
|
||||
<p>© {{ current_year }} TimeTrack. All rights reserved.</p>
|
||||
</footer>
|
||||
|
||||
<script src="{{ url_for('static', filename='js/script.js') }}"></script>
|
||||
|
||||
42
templates/login.html
Normal file
42
templates/login.html
Normal 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
49
templates/profile.html
Normal 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
45
templates/register.html
Normal 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 %}
|
||||
Reference in New Issue
Block a user