Files
TimeTracker/app/templates/admin/users.html
T
Dries Peeters 25b8d928b0 Update templates with new branding and logo integration
- Integrate new logo assets across all templates
- Update base template with improved favicon and meta tags
- Add logo support to login, admin, and inventory templates
- Update web manifest with new branding
- Enhance PDF layouts with logo support
2026-01-11 20:51:03 +01:00

134 lines
6.7 KiB
HTML

{% extends "base.html" %}
{% from "components/ui.html" import page_header, breadcrumb_nav, button, filter_badge %}
{% block content %}
{% set breadcrumbs = [
{'text': 'Admin', 'url': url_for('admin.admin_dashboard')},
{'text': 'Users'}
] %}
{{ page_header(
icon_class='fas fa-users-cog',
title_text='Manage Users',
subtitle_text='Add, edit, or remove user accounts',
breadcrumbs=breadcrumbs,
actions_html='<a href="' + url_for("admin.create_user") + '" class="bg-primary text-white px-4 py-2 rounded-lg hover:bg-primary/90 transition-colors"><i class="fas fa-plus mr-2"></i>Create User</a>' if (current_user.is_admin or has_permission('create_users')) else None
) }}
<div class="bg-card-light dark:bg-card-dark p-6 rounded-lg shadow overflow-x-auto">
<table class="w-full text-left">
<thead class="border-b border-border-light dark:border-border-dark">
<tr>
<th class="p-4">Username</th>
<th class="p-4">Roles & Permissions</th>
<th class="p-4">Status</th>
<th class="p-4">Actions</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr class="border-b border-border-light dark:border-border-dark">
<td class="p-4">
<div class="font-medium">{{ user.username }}</div>
{% if user.is_admin %}
<span class="text-xs text-primary">{{ _('Admin Access') }}</span>
{% endif %}
</td>
<td class="p-4">
{% if user.roles %}
<div class="flex flex-wrap gap-1">
{% for role in user.roles %}
<span class="px-2 py-0.5 inline-flex text-xs leading-5 font-semibold rounded-full
{% if role.is_system_role %}bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200
{% else %}bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200{% endif %}">
{{ role.name }}
</span>
{% endfor %}
</div>
{% else %}
{# Show legacy role if no new roles assigned yet #}
<div class="flex items-center gap-2">
<span class="px-2 py-0.5 inline-flex text-xs leading-5 font-semibold rounded-full bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-200">
{{ user.role | capitalize }} (legacy)
</span>
<a href="{{ url_for('permissions.manage_user_roles', user_id=user.id) }}"
class="text-xs text-primary hover:underline"
title="{{ _('Migrate to new role system') }}">
{{ _('Migrate') }} →
</a>
</div>
{% endif %}
</td>
<td class="p-4">
<div class="flex flex-col gap-1">
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full {{ 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200' if user.is_active else 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200' }}">
{{ 'Active' if user.is_active else 'Inactive' }}
</span>
{% if user.client_portal_enabled %}
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200" title="Client Portal: {{ user.client.name if user.client else 'No client assigned' }}">
<i class="fas fa-building mr-1"></i>Portal
</span>
{% endif %}
</div>
</td>
<td class="p-4">
<div class="flex gap-2">
{% if current_user.is_admin or has_permission('edit_users') %}
<a href="{{ url_for('admin.edit_user', user_id=user.id) }}" class="text-primary hover:underline text-sm">{{ _('Edit') }}</a>
{% endif %}
{% if current_user.is_admin or has_permission('manage_user_roles') %}
<a href="{{ url_for('permissions.manage_user_roles', user_id=user.id) }}" class="text-primary hover:underline text-sm">{{ _('Roles') }}</a>
{% endif %}
{% if user.id != current_user.id and (current_user.is_admin or has_permission('delete_users')) %}
<button type="button" onclick="confirmDeleteUser('{{ user.id }}', '{{ user.username }}', {{ user.time_entries.count() }})" class="text-red-600 hover:text-red-800 text-sm">{{ _('Delete') }}</button>
<form id="deleteUserForm-{{ user.id }}" method="POST" action="{{ url_for('admin.delete_user', user_id=user.id) }}" class="hidden">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
</form>
{% endif %}
</div>
</td>
</tr>
{% else %}
<tr>
<td colspan="4" class="p-4 text-center text-text-muted-light dark:text-text-muted-dark">{{ _('No users found.') }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<script>
function confirmDeleteUser(userId, username, timeEntriesCount) {
// Check if user has time entries
if (timeEntriesCount > 0) {
const msg = {{ _('Cannot delete user "{name}" because they have {count} time entries. Users with existing time entries cannot be deleted.')|tojson }}.replace('{name}', username).replace('{count}', timeEntriesCount);
if (window.showConfirm) {
window.showConfirm(msg, {
title: {{ _('Cannot Delete User')|tojson }},
confirmText: {{ _('OK')|tojson }},
variant: 'warning',
showCancel: false
});
} else {
alert(msg);
}
return false;
}
// Show delete confirmation
const msg = {{ _('Are you sure you want to delete user "{name}"? This action cannot be undone.')|tojson }}.replace('{name}', username);
window.showConfirm(msg, {
title: {{ _('Delete User')|tojson }},
confirmText: {{ _('Delete')|tojson }},
cancelText: {{ _('Cancel')|tojson }},
variant: 'danger'
}).then(function(ok) {
if (ok) {
document.getElementById('deleteUserForm-' + userId).submit();
}
});
}
</script>
{% endblock %}