Files
evo-sync/web/templates/admin/users.html
mguschin 52825f70de feat: admin can create users from /admin/users page
Adds a dialog form on the users list. Validates email uniqueness,
password length. Creates user as active with confirmed email.
System role can assign admin role; admin role can only create users.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 20:58:00 +03:00

172 lines
8.0 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}Пользователи — Администрирование — ЭВОСИНК{% endblock %}
{% block content %}
<div class="d-flex justify-between align-center mb-3">
<h1 style="font-size:1.3rem; margin:0;"><i class="bi bi-people me-2"></i>Пользователи</h1>
<button onclick="document.getElementById('create-user-dialog').showModal()" class="sm">
<i class="bi bi-person-plus me-1"></i>Создать пользователя
</button>
</div>
<dialog id="create-user-dialog">
<article>
<header>
<button aria-label="Закрыть" rel="prev" onclick="document.getElementById('create-user-dialog').close()"></button>
<h3>Создать пользователя</h3>
</header>
<form method="post" action="/admin/users/create">
<div class="row gap-2 mb-2">
<div class="col">
<label for="cu_first_name">Имя
<input type="text" id="cu_first_name" name="first_name" required>
</label>
</div>
<div class="col">
<label for="cu_last_name">Фамилия
<input type="text" id="cu_last_name" name="last_name">
</label>
</div>
</div>
<label for="cu_email">Email
<input type="email" id="cu_email" name="email" required>
</label>
<label for="cu_phone">Телефон
<input type="tel" id="cu_phone" name="phone" placeholder="+7 (999) 999-99-99">
</label>
<label for="cu_password">Пароль
<input type="password" id="cu_password" name="password" minlength="8" required>
</label>
{% if user.role == 'system' %}
<label for="cu_role">Роль
<select id="cu_role" name="role">
<option value="user" selected>Пользователь</option>
<option value="admin">Администратор</option>
</select>
</label>
{% endif %}
<footer class="d-flex gap-2 justify-end">
<button type="button" class="outline secondary" onclick="document.getElementById('create-user-dialog').close()">Отмена</button>
<button type="submit">Создать</button>
</footer>
</form>
</article>
</dialog>
{% if create_errors %}
<div role="alert" class="alert alert-danger mb-3">
{% for e in create_errors %}<p>{{ e }}</p>{% endfor %}
</div>
<script>document.addEventListener('DOMContentLoaded', () => document.getElementById('create-user-dialog').showModal());</script>
{% endif %}
<article class="card mb-3">
<div class="card-body">
<form method="get" action="/admin/users" class="d-flex gap-2 flex-wrap align-center">
<input type="text" name="search" value="{{ search }}" placeholder="Поиск по имени, email, телефону" style="flex:1; min-width:200px; margin:0;">
<select name="status" style="width:auto; margin:0;">
<option value="">Все статусы</option>
<option value="pending" {% if status_filter == 'pending' %}selected{% endif %}>Ожидает</option>
<option value="active" {% if status_filter == 'active' %}selected{% endif %}>Активен</option>
<option value="suspended" {% if status_filter == 'suspended' %}selected{% endif %}>Заблокирован</option>
</select>
<select name="role" style="width:auto; margin:0;">
<option value="">Все роли</option>
<option value="user" {% if role_filter == 'user' %}selected{% endif %}>Пользователь</option>
<option value="admin" {% if role_filter == 'admin' %}selected{% endif %}>Администратор</option>
<option value="system" {% if role_filter == 'system' %}selected{% endif %}>Системный</option>
</select>
<button type="submit" class="sm">Найти</button>
{% if search or status_filter or role_filter %}
<a href="/admin/users" role="button" class="outline secondary sm">Сбросить</a>
{% endif %}
</form>
</div>
</article>
<article class="card">
<div class="table-scroll">
<table class="align-middle">
<thead>
<tr>
<th>ID</th>
<th>Имя</th>
<th>Email</th>
<th>Телефон</th>
<th>Роль</th>
<th>Статус</th>
<th>Эвотор</th>
<th>Регистрация</th>
<th></th>
</tr>
</thead>
<tbody>
{% for u in users %}
<tr>
<td class="text-muted small">{{ u.id }}</td>
<td>{{ u.first_name }} {{ u.last_name }}</td>
<td>
{{ u.email }}
{% if not u.is_email_confirmed %}
<span class="badge badge-warning ms-1" title="Email не подтверждён"><i class="bi bi-exclamation-circle"></i></span>
{% endif %}
</td>
<td>{{ u.phone }}</td>
<td>
{% if u.role == 'system' %}<span class="badge badge-danger">Системный</span>
{% elif u.role == 'admin' %}<span class="badge badge-warning">Админ</span>
{% else %}<span class="badge badge-secondary">Польз.</span>
{% endif %}
</td>
<td>
{% if u.status == 'active' %}<span class="badge badge-success">Активен</span>
{% elif u.status == 'pending' %}<span class="badge badge-warning">Ожидает</span>
{% else %}<span class="badge badge-danger">Заблок.</span>
{% endif %}
</td>
<td>
{% if u.evotor_user_id %}
<i class="bi bi-check-circle text-success" title="{{ u.evotor_user_id }}"></i>
{% else %}
<span class="text-muted"></span>
{% endif %}
</td>
<td class="text-muted small">{{ u.created_at | datefmt }}</td>
<td>
<a href="/admin/users/{{ u.id }}" role="button" class="outline sm">
<i class="bi bi-eye"></i>
</a>
</td>
</tr>
{% else %}
<tr>
<td colspan="9" class="text-center text-muted py-4">Пользователи не найдены</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% if total_pages > 1 %}
<footer>
<div class="d-flex gap-2 justify-center align-center">
{% if page > 1 %}
<a href="?page={{ page - 1 }}&search={{ search }}&status={{ status_filter }}&role={{ role_filter }}" role="button" class="outline sm">«</a>
{% endif %}
<span class="text-muted small">Стр. {{ page }} из {{ total_pages }}</span>
{% if page < total_pages %}
<a href="?page={{ page + 1 }}&search={{ search }}&status={{ status_filter }}&role={{ role_filter }}" role="button" class="outline sm">»</a>
{% endif %}
</div>
</footer>
{% endif %}
</article>
{% if user.role == 'system' %}
<div class="mt-3 text-end">
<a href="/admin/roles" role="button" class="outline secondary sm">
<i class="bi bi-shield-lock me-1"></i>Управление ролями
</a>
</div>
{% endif %}
{% endblock %}