127 lines
6.5 KiB
HTML
127 lines
6.5 KiB
HTML
{% extends "base.html" %}
|
|
{% block body %}
|
|
<div class="mb-8">
|
|
<h1 class="text-3xl font-bold tracking-tight text-white flex items-center gap-3 mb-2">
|
|
<i data-lucide="users" class="text-app-primary w-8 h-8"></i>
|
|
Пользователи
|
|
</h1>
|
|
<div class="text-app-textMuted text-sm">Управление доступом к админ-панели.</div>
|
|
</div>
|
|
|
|
<div class="card overflow-hidden mb-8">
|
|
<div class="overflow-x-auto">
|
|
<table class="w-full text-left text-sm whitespace-nowrap">
|
|
<thead class="bg-app-bg border-b border-app-border">
|
|
<tr class="text-app-textMuted font-semibold tracking-wide uppercase text-xs">
|
|
<th class="px-4 py-3 w-16">ID</th>
|
|
<th class="px-4 py-3">Логин</th>
|
|
<th class="px-4 py-3">Роль</th>
|
|
<th class="px-4 py-3">Создан</th>
|
|
<th class="px-4 py-3">Статус</th>
|
|
<th class="px-4 py-3 w-32 text-right">Действия</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y divide-app-border text-xs">
|
|
{% for u in users %}
|
|
<tr class="hover:bg-app-surfaceHover transition-colors">
|
|
<td class="px-4 py-3 font-mono text-app-textMuted">{{ u.id }}</td>
|
|
<td class="px-4 py-3 font-bold text-app-textMain">{{ u.login }}</td>
|
|
<td class="px-4 py-3">
|
|
<span class="badge badge-neutral px-2 py-0.5">{{ u.role }}</span>
|
|
</td>
|
|
<td class="px-4 py-3 text-app-textMuted">{{ u.created_at_fmt }}</td>
|
|
<td class="px-4 py-3">
|
|
{% if u.is_active %}
|
|
<span class="badge badge-success px-2 py-0.5 flex items-center gap-1.5 w-max">
|
|
<span class="w-1.5 h-1.5 rounded-full bg-app-success"></span> Активен
|
|
</span>
|
|
{% else %}
|
|
<span class="badge badge-error px-2 py-0.5 flex items-center gap-1.5 w-max">
|
|
<span class="w-1.5 h-1.5 rounded-full bg-app-error"></span> Заблокирован
|
|
</span>
|
|
{% endif %}
|
|
</td>
|
|
<td class="px-4 py-3 text-right">
|
|
{% if user.role == "admin" and user.id != u.id %}
|
|
<div class="flex items-center justify-end gap-2">
|
|
<form method="post" action="/users/{{ u.id }}/toggle" class="m-0 inline-block">
|
|
<input type="hidden" name="csrf_token" value="{{ user.csrf_token }}">
|
|
<button class="btn btn-xs {% if u.is_active %}btn-surface{% else %}btn-primary-outline{% endif %}" type="submit">
|
|
{% if u.is_active %}Блокировать{% else %}Разблокировать{% endif %}
|
|
</button>
|
|
</form>
|
|
<form method="post" action="/users/{{ u.id }}/delete" class="m-0 inline-block" onsubmit="return confirm('Точно удалить пользователя?')">
|
|
<input type="hidden" name="csrf_token" value="{{ user.csrf_token }}">
|
|
<button class="btn btn-xs text-app-error hover:bg-app-errorBg hover:text-app-error" type="submit">
|
|
Удалить
|
|
</button>
|
|
</form>
|
|
</div>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% if pagination.pages > 1 %}
|
|
<div class="p-4 border-t border-app-border flex justify-center bg-app-bg/50">
|
|
<div class="tabs p-1">
|
|
<a class="btn btn-sm btn-ghost w-10 h-10 p-0 rounded-md {% if pagination.page == 1 %}pointer-events-none opacity-50{% endif %}" href="?page=1"><i data-lucide="chevrons-left" class="w-4 h-4"></i></a>
|
|
<a class="btn btn-sm btn-ghost w-10 h-10 p-0 rounded-md {% if pagination.page == 1 %}pointer-events-none opacity-50{% endif %}" href="?page={{ pagination.page - 1 }}"><i data-lucide="chevron-left" class="w-4 h-4"></i></a>
|
|
|
|
<span class="flex items-center justify-center px-4 text-sm font-medium text-app-textMuted bg-app-bg rounded-md border border-app-border mx-1">
|
|
{{ pagination.page }} из {{ pagination.pages }}
|
|
</span>
|
|
|
|
<a class="btn btn-sm btn-ghost w-10 h-10 p-0 rounded-md {% if pagination.page >= pagination.pages %}pointer-events-none opacity-50{% endif %}" href="?page={{ pagination.page + 1 }}"><i data-lucide="chevron-right" class="w-4 h-4"></i></a>
|
|
<a class="btn btn-sm btn-ghost w-10 h-10 p-0 rounded-md {% if pagination.page >= pagination.pages %}pointer-events-none opacity-50{% endif %}" href="?page={{ pagination.pages }}"><i data-lucide="chevrons-right" class="w-4 h-4"></i></a>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% if user.role == "admin" %}
|
|
<details class="card group/details">
|
|
<summary class="p-4 flex items-center justify-between cursor-pointer select-none hover:bg-app-surfaceHover transition-colors list-none">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-8 h-8 rounded-lg bg-app-primary/20 text-app-primary flex items-center justify-center">
|
|
<i data-lucide="user-plus" class="w-5 h-5"></i>
|
|
</div>
|
|
<span class="text-lg font-bold text-white">Добавить пользователя</span>
|
|
</div>
|
|
<i data-lucide="chevron-down" class="w-5 h-5 text-app-textMuted transition-transform group-open/details:rotate-180"></i>
|
|
</summary>
|
|
|
|
<div class="p-6 border-t border-app-border bg-app-bg/30">
|
|
<form method="post" action="/users/create" class="flex flex-col md:flex-row gap-6 items-end">
|
|
<input type="hidden" name="csrf_token" value="{{ user.csrf_token }}">
|
|
|
|
<div class="w-full">
|
|
<label class="block text-xs font-bold text-app-textMuted mb-1">Логин</label>
|
|
<input name="login" class="input w-full" required>
|
|
</div>
|
|
|
|
<div class="w-full">
|
|
<label class="block text-xs font-bold text-app-textMuted mb-1">Пароль</label>
|
|
<input name="password" type="password" class="input w-full" required minlength="10" placeholder="Минимум 10 символов">
|
|
</div>
|
|
|
|
<div class="w-full md:max-w-xs">
|
|
<label class="block text-xs font-bold text-app-textMuted mb-1">Роль</label>
|
|
<select name="role" class="select w-full">
|
|
<option value="editor">Редактор (editor)</option>
|
|
<option value="viewer">Читатель (viewer)</option>
|
|
<option value="admin">Администратор (admin)</option>
|
|
</select>
|
|
</div>
|
|
|
|
<button class="btn btn-primary" type="submit">
|
|
<i data-lucide="plus" class="w-4 h-4"></i> Создать
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</details>
|
|
{% endif %}
|
|
{% endblock %}
|