mirror of
https://github.com/wowlikon/LiB.git
synced 2026-02-04 04:31:09 +00:00
Улучшение безопасности
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
{% extends "base.html" %} {% block title %}LiB - Настройка 2FA{% endblock %} {%
|
||||
block content %}
|
||||
{% extends "base.html" %} {% block title %}LiB - Настройка 2FA{% endblock %}
|
||||
{% block content %}
|
||||
<div class="flex flex-1 items-center justify-center p-4 bg-gray-100">
|
||||
<div
|
||||
class="w-full max-w-4xl bg-white rounded-lg shadow-md overflow-hidden flex flex-col md:flex-row"
|
||||
@@ -11,7 +11,7 @@ block content %}
|
||||
Настройка 2FA
|
||||
</h2>
|
||||
<p class="text-sm text-gray-500 text-center mb-6">
|
||||
Отсканируйте код в Google Authenticator
|
||||
Отсканируйте код в приложении Аутентификатора
|
||||
</p>
|
||||
<div
|
||||
id="qr-container"
|
||||
@@ -155,5 +155,5 @@ block content %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script src="/static/2fa.js"></script>
|
||||
<script src="/static/page/2fa.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{% extends "base.html" %} {% block title %}Аналитика - LiB{% endblock %} {% block content %}
|
||||
{% extends "base.html" %} {% block title %}LiB - Аналитика{% endblock %}
|
||||
{% block content %}
|
||||
<div class="container mx-auto p-4 max-w-7xl">
|
||||
<div class="mb-8">
|
||||
<h1 class="text-2xl font-semibold text-gray-900 mb-1">Аналитика выдач и возвратов</h1>
|
||||
<p class="text-sm text-gray-500">Статистика и графики по выдачам книг</p>
|
||||
</div>
|
||||
|
||||
<!-- Период анализа -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-4 mb-6 border border-gray-100">
|
||||
<div class="flex items-center gap-4">
|
||||
<label class="text-sm font-medium text-gray-600">Период анализа:</label>
|
||||
@@ -22,7 +22,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Общая статистика -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-6">
|
||||
<div class="bg-white rounded-xl shadow-sm p-5 border border-gray-100">
|
||||
<div class="flex items-center justify-between">
|
||||
@@ -109,7 +108,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Графики -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
|
||||
<div class="bg-white rounded-xl shadow-sm p-6 border border-gray-100">
|
||||
<h2 class="text-base font-medium text-gray-700 mb-6">Выдачи по дням</h2>
|
||||
@@ -126,7 +124,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Топ книг -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-6 border border-gray-100">
|
||||
<h2 class="text-base font-medium text-gray-700 mb-6">Топ книг по выдачам</h2>
|
||||
<div id="top-books-container" class="space-y-2">
|
||||
@@ -137,6 +134,5 @@
|
||||
{% endblock %} {% block extra_head %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script src="/static/analytics.js"></script>
|
||||
<script src="/static/page/analytics.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
+268
-267
@@ -1,323 +1,324 @@
|
||||
{% extends "base.html" %} {% block title %}LiB - Авторизация{% endblock %} {%
|
||||
block content %}
|
||||
{% extends "base.html" %} {% block title %}LiB - Авторизация{% endblock %}
|
||||
{% block content %}
|
||||
<div class="flex flex-1 items-center justify-center p-4">
|
||||
<div class="w-full max-w-md">
|
||||
<div class="bg-white rounded-lg shadow-md overflow-hidden">
|
||||
<div class="flex border-b border-gray-200">
|
||||
<button
|
||||
type="button"
|
||||
id="login-tab"
|
||||
class="flex-1 py-4 text-center font-medium transition duration-200 text-gray-700 bg-gray-50 border-b-2 border-gray-500"
|
||||
>
|
||||
<button type="button" id="login-tab"
|
||||
class="flex-1 py-4 text-center font-medium transition duration-200 text-gray-700 bg-gray-50 border-b-2 border-gray-500">
|
||||
Вход
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
id="register-tab"
|
||||
class="flex-1 py-4 text-center font-medium transition duration-200 text-gray-400 hover:text-gray-600"
|
||||
>
|
||||
<button type="button" id="register-tab"
|
||||
class="flex-1 py-4 text-center font-medium transition duration-200 text-gray-400 hover:text-gray-600">
|
||||
Регистрация
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form id="login-form" class="p-6">
|
||||
<div class="mb-4">
|
||||
<label
|
||||
for="login-username"
|
||||
class="block text-sm font-medium text-gray-700 mb-2"
|
||||
>Имя пользователя</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
id="login-username"
|
||||
name="username"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-500 focus:border-transparent outline-none transition duration-200"
|
||||
placeholder="Введите имя пользователя"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label
|
||||
for="login-password"
|
||||
class="block text-sm font-medium text-gray-700 mb-2"
|
||||
>Пароль</label
|
||||
>
|
||||
<div class="relative">
|
||||
<input
|
||||
type="password"
|
||||
id="login-password"
|
||||
name="password"
|
||||
<div id="credentials-section">
|
||||
<div class="mb-4">
|
||||
<label for="login-username" class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Имя пользователя
|
||||
</label>
|
||||
<input type="text" id="login-username" name="username"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-500 focus:border-transparent outline-none transition duration-200"
|
||||
placeholder="Введите пароль"
|
||||
required
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="toggle-password absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
||||
>
|
||||
<svg
|
||||
class="eye-open w-5 h-5"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
||||
></path>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
|
||||
></path>
|
||||
</svg>
|
||||
<svg
|
||||
class="eye-closed w-5 h-5 hidden"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"
|
||||
></path>
|
||||
</svg>
|
||||
placeholder="Введите имя пользователя" required />
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="login-password" class="block text-sm font-medium text-gray-700 mb-2">Пароль</label>
|
||||
<div class="relative">
|
||||
<input type="password" id="login-password" name="password"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-500 focus:border-transparent outline-none transition duration-200"
|
||||
placeholder="Введите пароль" required />
|
||||
<button type="button"
|
||||
class="toggle-password absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600">
|
||||
<svg class="eye-open w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z">
|
||||
</path>
|
||||
</svg>
|
||||
<svg class="eye-closed w-5 h-5 hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<label class="custom-checkbox flex items-center text-sm text-gray-600">
|
||||
<input type="checkbox" id="remember-me" />
|
||||
<span class="checkmark"></span>Запомнить меня
|
||||
</label>
|
||||
<button type="button" id="forgot-password-btn"
|
||||
class="text-sm text-gray-500 hover:text-gray-700 transition">
|
||||
Забыли пароль?
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<label
|
||||
class="custom-checkbox flex items-center text-sm text-gray-600"
|
||||
>
|
||||
<input type="checkbox" id="remember-me" />
|
||||
<span class="checkmark"></span>Запомнить меня
|
||||
</label>
|
||||
<a
|
||||
href="#"
|
||||
class="text-sm text-gray-500 hover:text-gray-700 transition"
|
||||
>Забыли пароль?</a
|
||||
>
|
||||
|
||||
<div id="totp-section" class="hidden">
|
||||
<div class="text-center mb-4">
|
||||
<div class="w-20 h-20 mx-auto relative flex items-center justify-center mb-3">
|
||||
<svg class="absolute inset-0 w-full h-full -rotate-90" viewBox="0 0 80 80">
|
||||
<circle cx="40" cy="40" r="38" fill="none" stroke="#e5e7eb" stroke-width="2" />
|
||||
<circle id="lock-progress-circle" cx="40" cy="40" r="38" fill="none" stroke="#000000"
|
||||
stroke-width="2" stroke-linecap="round"
|
||||
style="stroke-dasharray: 238.761; stroke-dashoffset: 238.761;" />
|
||||
</svg>
|
||||
<div class="w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center z-10">
|
||||
<svg class="w-8 h-8 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="text-lg font-semibold text-gray-800">Двухфакторная аутентификация</h3>
|
||||
<p class="text-sm text-gray-500 mt-1">
|
||||
Введите код из приложения аутентификатора
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-6">
|
||||
<input type="text" id="login-totp" name="totp_code"
|
||||
class="w-full px-4 py-4 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-500 focus:border-transparent outline-none transition duration-200 text-center text-3xl tracking-[0.5em] font-mono"
|
||||
placeholder="000000" maxlength="6" inputmode="numeric" autocomplete="one-time-code" />
|
||||
</div>
|
||||
|
||||
<button type="button" id="back-to-credentials-btn"
|
||||
class="w-full mb-4 text-gray-500 hover:text-gray-700 text-sm flex items-center justify-center gap-1">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
|
||||
</svg>
|
||||
Назад
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
id="login-error"
|
||||
class="hidden mb-4 p-3 bg-red-100 border border-red-300 text-red-700 rounded-lg text-sm"
|
||||
></div>
|
||||
<button
|
||||
type="submit"
|
||||
id="login-submit"
|
||||
class="w-full bg-gray-500 text-white py-3 px-4 rounded-lg hover:bg-gray-600 transition duration-200 font-medium"
|
||||
>
|
||||
|
||||
<button type="submit" id="login-submit"
|
||||
class="w-full bg-gray-500 text-white py-3 px-4 rounded-lg hover:bg-gray-600 transition duration-200 font-medium">
|
||||
Войти
|
||||
</button>
|
||||
</form>
|
||||
<form
|
||||
id="register-form"
|
||||
class="p-6 hidden"
|
||||
onsubmit="return handleRegister(event);"
|
||||
>
|
||||
|
||||
<form id="register-form" class="p-6 hidden">
|
||||
<div class="mb-4">
|
||||
<label
|
||||
for="register-username"
|
||||
class="block text-sm font-medium text-gray-700 mb-2"
|
||||
>Имя пользователя</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
id="register-username"
|
||||
name="username"
|
||||
<label for="register-username" class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Имя пользователя
|
||||
</label>
|
||||
<input type="text" id="register-username" name="username"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-500 focus:border-transparent outline-none transition duration-200"
|
||||
placeholder="Придумайте имя пользователя (мин. 3 символа)"
|
||||
required
|
||||
minlength="3"
|
||||
maxlength="50"
|
||||
/>
|
||||
placeholder="Придумайте имя пользователя" required minlength="3" maxlength="50" />
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label
|
||||
for="register-email"
|
||||
class="block text-sm font-medium text-gray-700 mb-2"
|
||||
>Email</label
|
||||
>
|
||||
<input
|
||||
type="email"
|
||||
id="register-email"
|
||||
name="email"
|
||||
<label for="register-email" class="block text-sm font-medium text-gray-700 mb-2">Email</label>
|
||||
<input type="email" id="register-email" name="email"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-500 focus:border-transparent outline-none transition duration-200"
|
||||
placeholder="example@mail.com"
|
||||
required
|
||||
/>
|
||||
placeholder="example@mail.com" required />
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label
|
||||
for="register-fullname"
|
||||
class="block text-sm font-medium text-gray-700 mb-2"
|
||||
>Полное имя
|
||||
<span class="text-gray-400"
|
||||
>(необязательно)</span
|
||||
></label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
id="register-fullname"
|
||||
name="full_name"
|
||||
<label for="register-fullname" class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Полное имя <span class="text-gray-400">(необязательно)</span>
|
||||
</label>
|
||||
<input type="text" id="register-fullname" name="full_name"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-500 focus:border-transparent outline-none transition duration-200"
|
||||
placeholder="Иван Иванов"
|
||||
maxlength="100"
|
||||
/>
|
||||
placeholder="Иван Иванов" maxlength="100" />
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label
|
||||
for="register-password"
|
||||
class="block text-sm font-medium text-gray-700 mb-2"
|
||||
>Пароль</label
|
||||
>
|
||||
<label for="register-password" class="block text-sm font-medium text-gray-700 mb-2">Пароль</label>
|
||||
<div class="relative">
|
||||
<input
|
||||
type="password"
|
||||
id="register-password"
|
||||
name="password"
|
||||
<input type="password" id="register-password" name="password"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-500 focus:border-transparent outline-none transition duration-200"
|
||||
placeholder="Минимум 8 символов, A-Z, a-z, 0-9"
|
||||
required
|
||||
minlength="8"
|
||||
maxlength="100"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="toggle-password absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
||||
>
|
||||
<svg
|
||||
class="eye-open w-5 h-5"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
||||
></path>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
|
||||
></path>
|
||||
placeholder="Минимум 8 символов" required minlength="8" maxlength="100" />
|
||||
<button type="button"
|
||||
class="toggle-password absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600">
|
||||
<svg class="eye-open w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z">
|
||||
</path>
|
||||
</svg>
|
||||
<svg
|
||||
class="eye-closed w-5 h-5 hidden"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"
|
||||
></path>
|
||||
<svg class="eye-closed w-5 h-5 hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<div
|
||||
class="h-1 w-full bg-gray-200 rounded-full overflow-hidden"
|
||||
>
|
||||
<div
|
||||
id="password-strength-bar"
|
||||
class="h-full w-0 transition-all duration-300"
|
||||
></div>
|
||||
<div class="h-1 w-full bg-gray-200 rounded-full overflow-hidden">
|
||||
<div id="password-strength-bar" class="h-full w-0 transition-all duration-300"></div>
|
||||
</div>
|
||||
<p
|
||||
id="password-strength-text"
|
||||
class="text-xs mt-1 text-gray-500"
|
||||
></p>
|
||||
<p id="password-strength-text" class="text-xs mt-1 text-gray-500"></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label
|
||||
for="register-password-confirm"
|
||||
class="block text-sm font-medium text-gray-700 mb-2"
|
||||
>Подтвердите пароль</label
|
||||
>
|
||||
<label for="register-password-confirm" class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Подтвердите пароль
|
||||
</label>
|
||||
<div class="relative">
|
||||
<input
|
||||
type="password"
|
||||
id="register-password-confirm"
|
||||
name="password_confirm"
|
||||
<input type="password" id="register-password-confirm" name="password_confirm"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-500 focus:border-transparent outline-none transition duration-200"
|
||||
placeholder="Повторите пароль"
|
||||
required
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="toggle-password absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
||||
>
|
||||
<svg
|
||||
class="eye-open w-5 h-5"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
||||
></path>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
|
||||
></path>
|
||||
placeholder="Повторите пароль" required />
|
||||
<button type="button"
|
||||
class="toggle-password absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600">
|
||||
<svg class="eye-open w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z">
|
||||
</path>
|
||||
</svg>
|
||||
<svg
|
||||
class="eye-closed w-5 h-5 hidden"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"
|
||||
></path>
|
||||
<svg class="eye-closed w-5 h-5 hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<p
|
||||
id="password-match-error"
|
||||
class="text-xs mt-1 text-red-500 hidden"
|
||||
>
|
||||
<p id="password-match-error" class="text-xs mt-1 text-red-500 hidden">
|
||||
Пароли не совпадают
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
id="register-error"
|
||||
class="hidden mb-4 p-3 bg-red-100 border border-red-300 text-red-700 rounded-lg text-sm"
|
||||
></div>
|
||||
<div
|
||||
id="register-success"
|
||||
class="hidden mb-4 p-3 bg-green-100 border border-green-300 text-green-700 rounded-lg text-sm"
|
||||
></div>
|
||||
<button
|
||||
type="submit"
|
||||
id="register-submit"
|
||||
class="w-full bg-gray-500 text-white py-3 px-4 rounded-lg hover:bg-gray-600 transition duration-200 font-medium disabled:bg-gray-300 disabled:cursor-not-allowed"
|
||||
>
|
||||
|
||||
<button type="submit" id="register-submit"
|
||||
class="w-full bg-gray-500 text-white py-3 px-4 rounded-lg hover:bg-gray-600 transition duration-200 font-medium disabled:bg-gray-300 disabled:cursor-not-allowed">
|
||||
Зарегистрироваться
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<form id="reset-password-form" class="p-6 hidden">
|
||||
<div class="mb-4 text-center">
|
||||
<h3 class="text-lg font-semibold text-gray-800">Сброс пароля</h3>
|
||||
<p class="text-sm text-gray-500 mt-1">
|
||||
Используйте один из резервных кодов, полученных при регистрации
|
||||
</p>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="reset-username" class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Имя пользователя
|
||||
</label>
|
||||
<input type="text" id="reset-username" name="username"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-500 focus:border-transparent outline-none transition duration-200"
|
||||
placeholder="Введите имя пользователя" required />
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="reset-recovery-code" class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Резервный код
|
||||
</label>
|
||||
<input type="text" id="reset-recovery-code" name="recovery_code"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-500 focus:border-transparent outline-none transition duration-200 text-center font-mono uppercase"
|
||||
placeholder="XXXX-XXXX-XXXX-XXXX" maxlength="19" required
|
||||
pattern="[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}" />
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="reset-new-password" class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Новый пароль
|
||||
</label>
|
||||
<div class="relative">
|
||||
<input type="password" id="reset-new-password" name="new_password"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-500 focus:border-transparent outline-none transition duration-200"
|
||||
placeholder="Минимум 8 символов" required minlength="8" maxlength="100" />
|
||||
<button type="button"
|
||||
class="toggle-password absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600">
|
||||
<svg class="eye-open w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z">
|
||||
</path>
|
||||
</svg>
|
||||
<svg class="eye-closed w-5 h-5 hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-6">
|
||||
<label for="reset-confirm-password" class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Подтвердите новый пароль
|
||||
</label>
|
||||
<input type="password" id="reset-confirm-password" name="confirm_password"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-500 focus:border-transparent outline-none transition duration-200"
|
||||
placeholder="Повторите новый пароль" required />
|
||||
<p id="reset-password-match-error" class="text-xs mt-1 text-red-500 hidden">
|
||||
Пароли не совпадают
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button type="submit" id="reset-submit"
|
||||
class="w-full bg-gray-500 text-white py-3 px-4 rounded-lg hover:bg-gray-600 transition duration-200 font-medium">
|
||||
Сбросить пароль
|
||||
</button>
|
||||
<button type="button" id="back-to-login-btn"
|
||||
class="w-full mt-3 text-gray-500 hover:text-gray-700 text-sm">
|
||||
← Вернуться к входу
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="recovery-codes-modal"
|
||||
class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
|
||||
<div class="bg-white rounded-lg shadow-xl max-w-md w-full mx-4 max-h-[90vh] overflow-y-auto">
|
||||
<div class="p-6">
|
||||
<div class="flex items-center justify-center w-12 h-12 mx-auto bg-yellow-100 rounded-full mb-4">
|
||||
<svg class="w-6 h-6 text-yellow-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-lg font-semibold text-center text-gray-800 mb-2">
|
||||
Сохраните резервные коды!
|
||||
</h3>
|
||||
<p class="text-sm text-gray-500 text-center mb-4">
|
||||
Эти коды понадобятся для восстановления доступа к аккаунту.
|
||||
<strong class="text-red-600">Сохраните их в надёжном месте!</strong>
|
||||
</p>
|
||||
|
||||
<div id="recovery-codes-list"
|
||||
class="bg-gray-50 rounded-lg p-4 font-mono text-sm text-center space-y-2 mb-4">
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 mb-4">
|
||||
<button type="button" id="copy-codes-btn"
|
||||
class="flex-1 flex items-center justify-center gap-2 px-4 py-2 bg-gray-100 hover:bg-gray-200 rounded-lg text-sm font-medium transition">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z">
|
||||
</path>
|
||||
</svg>
|
||||
Копировать
|
||||
</button>
|
||||
<button type="button" id="download-codes-btn"
|
||||
class="flex-1 flex items-center justify-center gap-2 px-4 py-2 bg-gray-100 hover:bg-gray-200 rounded-lg text-sm font-medium transition">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path>
|
||||
</svg>
|
||||
Скачать
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<label class="flex items-center gap-2 text-sm text-gray-600 mb-4">
|
||||
<input type="checkbox" id="codes-saved-checkbox" class="rounded" />
|
||||
Я сохранил(а) коды в надёжном месте
|
||||
</label>
|
||||
|
||||
<button type="button" id="close-recovery-modal-btn" disabled
|
||||
class="w-full bg-gray-500 text-white py-3 px-4 rounded-lg hover:bg-gray-600 transition duration-200 font-medium disabled:bg-gray-300 disabled:cursor-not-allowed">
|
||||
Продолжить
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script type="text/javascript" src="/static/auth.js"></script>
|
||||
<script src="/static/page/auth.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends "base.html" %} {% block content %}
|
||||
{% extends "base.html" %} {% block title %}LiB - Автор{% endblock %}
|
||||
{% block content %}
|
||||
<div class="container mx-auto p-4 max-w-4xl">
|
||||
<div id="author-card" class="bg-white rounded-lg shadow-md p-6 mb-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
@@ -115,5 +116,5 @@
|
||||
</div>
|
||||
</template>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script src="/static/author.js"></script>
|
||||
<script src="/static/page/author.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends "base.html" %} {% block content %}
|
||||
{% extends "base.html" %} {% block title %}LiB - Авторы{% endblock %}
|
||||
{% block content %}
|
||||
<div class="container mx-auto p-4">
|
||||
<div
|
||||
class="flex flex-col md:flex-row justify-between items-center mb-6 gap-4"
|
||||
@@ -119,5 +120,5 @@
|
||||
</div>
|
||||
</template>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script src="/static/authors.js"></script>
|
||||
<script src="/static/page/authors.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -238,7 +238,9 @@
|
||||
|
||||
<footer class="bg-gray-800 text-white p-4 mt-8">
|
||||
<div class="container mx-auto text-center">
|
||||
<p>© 2025 LiB Library. All rights reserved.</p>
|
||||
<p>© 2026 LiB Library. Разработано в рамках дипломного проекта.
|
||||
Код открыт под лицензией <a href="https://github.com/wowlikon/LibraryAPI/blob/main/LICENSE">MIT</a>.
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
{% block scripts %}{% endblock %}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends "base.html" %} {% block content %}
|
||||
{% extends "base.html" %} {% block title %}LiB - Книга{% endblock %}
|
||||
{% block content %}
|
||||
<div class="container mx-auto p-4 max-w-6xl">
|
||||
<div id="book-card" class="bg-white rounded-lg shadow-md p-6 mb-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
@@ -143,7 +144,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Секция выдачи для библиотекарей и администраторов -->
|
||||
<div id="loans-section" class="hidden bg-white rounded-lg shadow-md p-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h2 class="text-xl font-bold text-gray-900">Выдачи книги</h2>
|
||||
@@ -175,7 +175,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Модальное окно для выдачи книги -->
|
||||
<div
|
||||
id="loan-modal"
|
||||
class="hidden fixed inset-0 z-50 overflow-y-auto"
|
||||
@@ -296,5 +295,5 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script src="/static/book.js"></script>
|
||||
<script src="/static/page/book.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends "base.html" %} {% block content %}
|
||||
{% extends "base.html" %} {% block title %}LiB - Книги{% endblock %}
|
||||
{% block content %}
|
||||
<div class="container mx-auto p-4 flex flex-col md:flex-row gap-6">
|
||||
<aside class="w-full md:w-1/4">
|
||||
<div
|
||||
@@ -186,5 +187,5 @@
|
||||
</div>
|
||||
</template>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script src="/static/books.js"></script>
|
||||
<script src="/static/page/books.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "base.html" %} {% block title %}Создание автора | LiB{% endblock %}
|
||||
{% extends "base.html" %} {% block title %}LiB - Создание автора{% endblock %}
|
||||
{% block content %}
|
||||
<div class="container mx-auto p-4 max-w-xl">
|
||||
<div class="bg-white rounded-lg shadow-md p-6 md:p-8">
|
||||
@@ -158,5 +158,5 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script src="/static/create_author.js"></script>
|
||||
<script src="/static/page/create_author.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "base.html" %} {% block title %}Создание книги | LiB{% endblock %} {%
|
||||
block content %}
|
||||
{% extends "base.html" %} {% block title %}LiB - Создание книги{% endblock %}
|
||||
{% block content %}
|
||||
<div class="container mx-auto p-4 max-w-3xl">
|
||||
<div class="bg-white rounded-lg shadow-md p-6 md:p-8">
|
||||
<div class="mb-8 border-b border-gray-100 pb-4">
|
||||
@@ -225,5 +225,5 @@ block content %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script src="/static/create_book.js"></script>
|
||||
<script src="/static/page/create_book.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "base.html" %} {% block title %}Создание жанра | LiB{% endblock %} {%
|
||||
block content %}
|
||||
{% extends "base.html" %} {% block title %}LiB - Создание жанра{% endblock %}
|
||||
{% block content %}
|
||||
<div class="container mx-auto p-4 max-w-xl">
|
||||
<div class="bg-white rounded-lg shadow-md p-6 md:p-8">
|
||||
<div class="mb-8 border-b border-gray-100 pb-4">
|
||||
@@ -158,5 +158,5 @@ block content %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script src="/static/create_genre.js"></script>
|
||||
<script src="/static/page/create_genre.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "base.html" %} {% block title %}Редактирование автора | LiB{%
|
||||
endblock %} {% block content %}
|
||||
{% extends "base.html" %} {% block title %}LiB - Редактирование автора{% endblock %}
|
||||
{% block content %}
|
||||
<div class="container mx-auto p-4 max-w-2xl">
|
||||
<div class="bg-white rounded-lg shadow-md p-6 md:p-8">
|
||||
<div class="mb-8 border-b border-gray-100 pb-4">
|
||||
@@ -312,5 +312,5 @@ endblock %} {% block content %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script src="/static/edit_author.js"></script>
|
||||
<script src="/static/page/edit_author.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "base.html" %} {% block title %}Редактирование книги | LiB{% endblock
|
||||
%} {% block content %}
|
||||
{% extends "base.html" %} {% block title %}LiB - Редактирование книги{% endblock %}
|
||||
{% block content %}
|
||||
<div class="container mx-auto p-4 max-w-3xl">
|
||||
<div class="bg-white rounded-lg shadow-md p-6 md:p-8">
|
||||
<div class="mb-8 border-b border-gray-100 pb-4">
|
||||
@@ -390,5 +390,5 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script src="/static/edit_book.js"></script>
|
||||
<script src="/static/page/edit_book.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "base.html" %} {% block title %}Редактирование жанра | LiB{% endblock
|
||||
%} {% block content %}
|
||||
{% extends "base.html" %} {% block title %}LiB - Редактирование жанра{% endblock %}
|
||||
{% block content %}
|
||||
<div class="container mx-auto p-4 max-w-2xl">
|
||||
<div class="bg-white rounded-lg shadow-md p-6 md:p-8">
|
||||
<div class="mb-8 border-b border-gray-100 pb-4">
|
||||
@@ -313,5 +313,5 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script src="/static/edit_genre.js"></script>
|
||||
<script src="/static/page/edit_genre.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "base.html" %} {% block title %}LiB - Библиотека{% endblock %} {%
|
||||
block content %}
|
||||
{% extends "base.html" %} {% block title %}LiB - Библиотека{% endblock %}
|
||||
{% block content %}
|
||||
<div class="flex flex-1 items-center justify-center p-4">
|
||||
<div class="w-full max-w-4xl">
|
||||
<div class="bg-white rounded-lg shadow-md overflow-hidden">
|
||||
@@ -186,10 +186,10 @@ block content %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-6 text-center text-gray-400 text-sm">
|
||||
<p>LiB — Библиотека. Создано с ❤️</p>
|
||||
<p>LiB — Библиотека. Красиво, функционально, безопасно.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script type="text/javascript" src="/static/index.js"></script>
|
||||
<script src="/static/page/index.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{% extends "base.html" %} {% block content %}
|
||||
{% extends "base.html" %} {% block title %}LiB - Мои книги{% endblock %}
|
||||
{% block content %}
|
||||
<div class="container mx-auto p-4 max-w-6xl">
|
||||
<div class="mb-6">
|
||||
<h1 class="text-3xl font-bold text-gray-900 mb-2">Мои книги</h1>
|
||||
<p class="text-gray-600">Управление вашими бронированиями и выдачами</p>
|
||||
</div>
|
||||
|
||||
<!-- Бронирования -->
|
||||
<div class="bg-white rounded-lg shadow-md p-6 mb-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h2 class="text-xl font-bold text-gray-900">Мои бронирования</h2>
|
||||
@@ -18,7 +18,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Активные выдачи -->
|
||||
<div class="bg-white rounded-lg shadow-md p-6 mb-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h2 class="text-xl font-bold text-gray-900">Активные выдачи</h2>
|
||||
@@ -31,7 +30,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Возвращенные книги -->
|
||||
<div class="bg-white rounded-lg shadow-md p-6 mb-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h2 class="text-xl font-bold text-gray-900">История возвратов</h2>
|
||||
@@ -45,6 +43,5 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script src="/static/my_books.js"></script>
|
||||
<script src="/static/page/my_books.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -1,159 +1,294 @@
|
||||
{% extends "base.html" %} {% block content %}
|
||||
<div
|
||||
class="container mx-auto p-4 max-w-2xl"
|
||||
x-data="{ showPasswordModal: false }"
|
||||
@close-modal.window="showPasswordModal = false"
|
||||
>
|
||||
{% extends "base.html" %} {% block title %}LiB - Профиль{% endblock %}
|
||||
{% block content %}
|
||||
<div class="container mx-auto p-4 max-w-2xl"
|
||||
x-data="{ showPasswordModal: false, showDisable2FAModal: false, showRecoveryCodesModal: false, is2FAEnabled: false, recoveryCodesRemaining: null }"
|
||||
@update-2fa.window="is2FAEnabled = $event.detail"
|
||||
@update-recovery-codes.window="recoveryCodesRemaining = $event.detail"
|
||||
@close-password-modal.window="showPasswordModal = false"
|
||||
@close-disable-2fa-modal.window="showDisable2FAModal = false"
|
||||
@close-recovery-codes-modal.window="showRecoveryCodesModal = false"
|
||||
@open-recovery-codes-modal.window="showRecoveryCodesModal = true">
|
||||
|
||||
<div id="profile-card" class="bg-white rounded-lg shadow-md p-6 mb-6">
|
||||
<div class="animate-pulse flex items-center">
|
||||
<div class="w-24 h-24 bg-gray-200 rounded-full mr-6"></div>
|
||||
<div class="h-6 bg-gray-200 w-48 rounded"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
id="account-section"
|
||||
class="bg-white rounded-lg shadow-md p-6 mb-6 hidden"
|
||||
>
|
||||
|
||||
<div id="account-section" class="bg-white rounded-lg shadow-md p-6 mb-6 hidden">
|
||||
<h2 class="text-xl font-bold mb-4 border-b pb-2">Информация</h2>
|
||||
<div id="account-info" class="space-y-4"></div>
|
||||
</div>
|
||||
<div
|
||||
id="roles-section"
|
||||
class="bg-white rounded-lg shadow-md p-6 mb-6 hidden"
|
||||
>
|
||||
|
||||
<div id="roles-section" class="bg-white rounded-lg shadow-md p-6 mb-6 hidden">
|
||||
<h2 class="text-xl font-bold mb-4 border-b pb-2">Роли</h2>
|
||||
<div id="roles-container" class="space-y-3"></div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-lg shadow-md p-6 mb-6">
|
||||
<h2 class="text-xl font-bold mb-4 border-b pb-2">Безопасность</h2>
|
||||
<div class="space-y-3">
|
||||
<button
|
||||
@click="showPasswordModal = true"
|
||||
class="w-full flex items-center justify-between p-4 bg-gray-50 hover:bg-gray-100 rounded-lg transition-colors"
|
||||
>
|
||||
<span class="text-gray-700 font-medium">Сменить пароль</span>
|
||||
<svg
|
||||
class="w-5 h-5 text-gray-400"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 5l7 7-7 7"
|
||||
/>
|
||||
<button type="button"
|
||||
@click="is2FAEnabled ? showDisable2FAModal = true : window.location.href = '/2fa'"
|
||||
class="w-full flex items-center justify-between p-4 bg-gray-50 hover:bg-gray-100 rounded-lg transition-colors">
|
||||
<div class="flex items-center gap-3">
|
||||
<svg class="w-5 h-5 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
||||
</svg>
|
||||
<span class="text-gray-700 font-medium">Двухфакторная аутентификация</span>
|
||||
</div>
|
||||
<span x-show="is2FAEnabled" class="px-2 py-1 text-xs font-medium rounded-full bg-green-100 text-green-800">
|
||||
Включена
|
||||
</span>
|
||||
<span x-show="!is2FAEnabled" class="px-2 py-1 text-xs font-medium rounded-full bg-gray-200 text-gray-600">
|
||||
Выключена
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button type="button" id="recovery-codes-btn"
|
||||
class="w-full flex items-center justify-between p-4 bg-gray-50 hover:bg-gray-100 rounded-lg transition-colors">
|
||||
<div class="flex items-center gap-3">
|
||||
<svg class="w-5 h-5 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z" />
|
||||
</svg>
|
||||
<span class="text-gray-700 font-medium">Резервные коды</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<template x-if="recoveryCodesRemaining !== null">
|
||||
<span :class="{
|
||||
'bg-green-100 text-green-800': recoveryCodesRemaining > 5,
|
||||
'bg-yellow-100 text-yellow-800': recoveryCodesRemaining > 2 && recoveryCodesRemaining <= 5,
|
||||
'bg-red-100 text-red-800': recoveryCodesRemaining <= 2
|
||||
}" class="px-2 py-1 text-xs font-medium rounded-full">
|
||||
<span x-text="recoveryCodesRemaining"></span> / 10
|
||||
</span>
|
||||
</template>
|
||||
<svg class="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button @click="showPasswordModal = true"
|
||||
class="w-full flex items-center justify-between p-4 bg-gray-50 hover:bg-gray-100 rounded-lg transition-colors">
|
||||
<div class="flex items-center gap-3">
|
||||
<svg class="w-5 h-5 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" />
|
||||
</svg>
|
||||
<span class="text-gray-700 font-medium">Сменить пароль</span>
|
||||
</div>
|
||||
<svg class="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
onclick="Auth.logout()"
|
||||
class="w-full flex items-center justify-between p-4 bg-red-50 hover:bg-red-100 rounded-lg transition-colors"
|
||||
>
|
||||
<span class="text-red-700 font-medium">Выйти из аккаунта</span>
|
||||
<svg
|
||||
class="w-5 h-5 text-red-400"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
<button onclick="Auth.logout()"
|
||||
class="w-full flex items-center justify-between p-4 bg-red-50 hover:bg-red-100 rounded-lg transition-colors">
|
||||
<div class="flex items-center gap-3">
|
||||
<svg class="w-5 h-5 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
|
||||
</svg>
|
||||
<span class="text-red-700 font-medium">Выйти из аккаунта</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
x-show="showPasswordModal"
|
||||
class="fixed inset-0 z-50 overflow-y-auto"
|
||||
style="display: none"
|
||||
>
|
||||
<div
|
||||
class="flex items-center justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0"
|
||||
>
|
||||
<div
|
||||
x-show="showPasswordModal"
|
||||
x-transition.opacity
|
||||
class="fixed inset-0 transition-opacity"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div
|
||||
class="absolute inset-0 bg-gray-500 opacity-75"
|
||||
@click="showPasswordModal = false"
|
||||
></div>
|
||||
|
||||
<div x-show="showPasswordModal" x-cloak class="fixed inset-0 z-50 overflow-y-auto">
|
||||
<div class="flex items-center justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div x-show="showPasswordModal" x-transition.opacity class="fixed inset-0 transition-opacity">
|
||||
<div class="absolute inset-0 bg-gray-500 opacity-75" @click="showPasswordModal = false"></div>
|
||||
</div>
|
||||
<div
|
||||
x-show="showPasswordModal"
|
||||
x-transition.scale
|
||||
class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg w-full"
|
||||
>
|
||||
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
||||
<h3
|
||||
class="text-lg leading-6 font-medium text-gray-900 mb-4"
|
||||
>
|
||||
Смена пароля
|
||||
</h3>
|
||||
<form id="change-password-form">
|
||||
<div class="mb-4">
|
||||
<label
|
||||
class="block text-gray-700 text-sm font-bold mb-2"
|
||||
>Текущий пароль</label
|
||||
>
|
||||
<input
|
||||
type="password"
|
||||
id="current-password"
|
||||
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
|
||||
/>
|
||||
<span class="hidden sm:inline-block sm:align-middle sm:h-screen">​</span>
|
||||
<div x-show="showPasswordModal" x-transition
|
||||
class="inline-block align-bottom bg-white rounded-xl text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg w-full">
|
||||
<div class="bg-white px-6 pt-6 pb-4">
|
||||
<h3 class="text-lg leading-6 font-semibold text-gray-900 mb-4">Смена пароля</h3>
|
||||
<form id="change-password-form" class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-gray-700 text-sm font-medium mb-2">Новый пароль</label>
|
||||
<input type="password" id="new-password"
|
||||
class="w-full px-4 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-500 focus:border-transparent outline-none transition"
|
||||
placeholder="Минимум 8 символов" />
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label
|
||||
class="block text-gray-700 text-sm font-bold mb-2"
|
||||
>Новый пароль</label
|
||||
>
|
||||
<input
|
||||
type="password"
|
||||
id="new-password"
|
||||
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label
|
||||
class="block text-gray-700 text-sm font-bold mb-2"
|
||||
>Подтвердите пароль</label
|
||||
>
|
||||
<input
|
||||
type="password"
|
||||
id="confirm-password"
|
||||
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
|
||||
/>
|
||||
<div>
|
||||
<label class="block text-gray-700 text-sm font-medium mb-2">Подтвердите пароль</label>
|
||||
<input type="password" id="confirm-password"
|
||||
class="w-full px-4 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-500 focus:border-transparent outline-none transition"
|
||||
placeholder="Повторите пароль" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div
|
||||
class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
id="submit-password-btn"
|
||||
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-600 text-base font-medium text-white hover:bg-gray-700 sm:ml-3 sm:w-auto sm:text-sm"
|
||||
>
|
||||
<div class="bg-gray-50 px-6 py-4 flex flex-row-reverse gap-3">
|
||||
<button type="button" id="submit-password-btn"
|
||||
class="px-5 py-2.5 bg-gray-600 text-white font-medium rounded-lg hover:bg-gray-700 transition">
|
||||
Сменить
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="showPasswordModal = false"
|
||||
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
|
||||
>
|
||||
<button type="button" @click="showPasswordModal = false"
|
||||
class="px-5 py-2.5 bg-white text-gray-700 font-medium rounded-lg border border-gray-300 hover:bg-gray-50 transition">
|
||||
Отмена
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div x-show="showDisable2FAModal" x-cloak class="fixed inset-0 z-50 overflow-y-auto">
|
||||
<div class="flex items-center justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div x-show="showDisable2FAModal" x-transition.opacity class="fixed inset-0 transition-opacity">
|
||||
<div class="absolute inset-0 bg-gray-500 opacity-75" @click="showDisable2FAModal = false"></div>
|
||||
</div>
|
||||
<span class="hidden sm:inline-block sm:align-middle sm:h-screen">​</span>
|
||||
<div x-show="showDisable2FAModal" x-transition
|
||||
class="inline-block align-bottom bg-white rounded-xl text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg w-full">
|
||||
<div class="bg-white px-6 pt-6 pb-4">
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<div class="w-10 h-10 bg-red-100 rounded-full flex items-center justify-center">
|
||||
<svg class="w-5 h-5 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-lg leading-6 font-semibold text-gray-900">Отключить 2FA</h3>
|
||||
</div>
|
||||
<p class="text-sm text-gray-500 mb-4">
|
||||
Это снизит безопасность вашего аккаунта. Для подтверждения введите пароль.
|
||||
</p>
|
||||
<form id="disable-2fa-form">
|
||||
<div>
|
||||
<label class="block text-gray-700 text-sm font-medium mb-2">Пароль</label>
|
||||
<input type="password" id="disable-2fa-password"
|
||||
class="w-full px-4 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-500 focus:border-transparent outline-none transition"
|
||||
placeholder="Введите ваш пароль" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="bg-gray-50 px-6 py-4 flex flex-row-reverse gap-3">
|
||||
<button type="button" id="submit-disable-2fa-btn"
|
||||
class="px-5 py-2.5 bg-red-600 text-white font-medium rounded-lg hover:bg-red-700 transition">
|
||||
Отключить
|
||||
</button>
|
||||
<button type="button" @click="showDisable2FAModal = false"
|
||||
class="px-5 py-2.5 bg-white text-gray-700 font-medium rounded-lg border border-gray-300 hover:bg-gray-50 transition">
|
||||
Отмена
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div x-show="showRecoveryCodesModal" x-cloak class="fixed inset-0 z-50 overflow-y-auto">
|
||||
<div class="flex items-center justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div x-show="showRecoveryCodesModal" x-transition.opacity class="fixed inset-0 transition-opacity">
|
||||
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
|
||||
</div>
|
||||
<span class="hidden sm:inline-block sm:align-middle sm:h-screen">​</span>
|
||||
<div x-show="showRecoveryCodesModal" x-transition
|
||||
class="inline-block align-bottom bg-white rounded-xl text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-md w-full">
|
||||
<div class="bg-white px-6 pt-6 pb-4">
|
||||
<div id="recovery-codes-loading" class="text-center py-8">
|
||||
<div class="animate-spin w-8 h-8 border-2 border-gray-300 border-t-gray-600 rounded-full mx-auto mb-4"></div>
|
||||
<p class="text-gray-500">Загрузка...</p>
|
||||
</div>
|
||||
|
||||
<div id="recovery-codes-status" class="hidden">
|
||||
<div class="flex items-center justify-center w-12 h-12 mx-auto rounded-full mb-4" id="status-icon-container">
|
||||
</div>
|
||||
<h3 class="text-lg font-semibold text-center text-gray-800 mb-2">
|
||||
Резервные коды
|
||||
</h3>
|
||||
|
||||
<div id="codes-status-summary" class="text-center mb-4"></div>
|
||||
|
||||
<div class="bg-gray-50 rounded-lg p-3 mb-4">
|
||||
<p class="text-xs text-gray-500 mb-2 text-center">Статус кодов:</p>
|
||||
<div id="codes-status-list" class="space-y-1 max-h-48 overflow-y-auto"></div>
|
||||
</div>
|
||||
|
||||
<div id="codes-warning" class="hidden bg-yellow-50 border border-yellow-200 rounded-lg p-4 mb-4">
|
||||
<p class="text-sm text-yellow-800 flex items-start gap-2">
|
||||
<svg class="w-5 h-5 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||
</svg>
|
||||
<span id="warning-text"></span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p id="codes-generated-at" class="text-xs text-gray-400 text-center mb-4"></p>
|
||||
|
||||
<button type="button" id="regenerate-codes-btn"
|
||||
class="w-full bg-gray-500 text-white py-3 px-4 rounded-lg hover:bg-gray-600 transition duration-200 font-medium mb-3">
|
||||
Сгенерировать новые коды
|
||||
</button>
|
||||
<button type="button" id="close-status-modal-btn"
|
||||
class="w-full text-gray-500 hover:text-gray-700 text-sm py-2">
|
||||
Закрыть
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="recovery-codes-display" class="hidden">
|
||||
<div class="flex items-center justify-center w-12 h-12 mx-auto bg-green-100 rounded-full mb-4">
|
||||
<svg class="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-lg font-semibold text-center text-gray-800 mb-2">
|
||||
Новые резервные коды
|
||||
</h3>
|
||||
<p class="text-sm text-gray-500 text-center mb-4">
|
||||
<strong class="text-red-600">Сохраните эти коды!</strong>
|
||||
Они понадобятся для восстановления доступа.
|
||||
</p>
|
||||
|
||||
<div id="recovery-codes-list"
|
||||
class="bg-gray-50 rounded-lg p-4 font-mono text-sm text-center space-y-2 mb-4 max-h-64 overflow-y-auto">
|
||||
</div>
|
||||
|
||||
<p id="recovery-codes-generated-at" class="text-xs text-gray-400 text-center mb-4"></p>
|
||||
|
||||
<div class="flex gap-2 mb-4">
|
||||
<button type="button" id="copy-codes-btn"
|
||||
class="flex-1 flex items-center justify-center gap-2 px-4 py-2 bg-gray-100 hover:bg-gray-200 rounded-lg text-sm font-medium transition">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
|
||||
</svg>
|
||||
<span>Копировать</span>
|
||||
</button>
|
||||
<button type="button" id="download-codes-btn"
|
||||
class="flex-1 flex items-center justify-center gap-2 px-4 py-2 bg-gray-100 hover:bg-gray-200 rounded-lg text-sm font-medium transition">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
||||
</svg>
|
||||
<span>Скачать</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<label class="flex items-center gap-2 text-sm text-gray-600 mb-4 cursor-pointer">
|
||||
<input type="checkbox" id="codes-saved-checkbox" class="rounded border-gray-300 text-gray-600 focus:ring-gray-500" />
|
||||
<span>Я сохранил(а) коды в надёжном месте</span>
|
||||
</label>
|
||||
|
||||
<button type="button" id="close-recovery-modal-btn" disabled
|
||||
class="w-full bg-gray-500 text-white py-3 px-4 rounded-lg hover:bg-gray-600 transition duration-200 font-medium disabled:bg-gray-300 disabled:cursor-not-allowed">
|
||||
Закрыть
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script src="/static/profile.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="/static/page/profile.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "base.html" %} {% block title %}Пользователи - LiB{% endblock %} {%
|
||||
block content %}
|
||||
{% extends "base.html" %} {% block title %}LiB - Пользователи{% endblock %}
|
||||
{% block content %}
|
||||
<div class="container mx-auto p-4">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h1 class="text-2xl font-bold text-gray-800">
|
||||
@@ -429,5 +429,5 @@ block content %}
|
||||
</div>
|
||||
|
||||
{% endblock %} {% block scripts %}
|
||||
<script src="/static/users.js"></script>
|
||||
<script src="/static/page/users.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user