- Исправлено 'плавающее' меню (дублирование кнопок при навигации)
- Создан base.html - единый базовый шаблон для всех страниц
- Добавлена кнопка '📅 Запланированные посты' во все 11 страниц
- Sticky sidebar с улучшенным дизайном
- Все страницы теперь имеют одинаковое меню (11 кнопок)
- Улучшенная подсветка активного пункта (border-left)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
190 lines
9.3 KiB
HTML
190 lines
9.3 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Верификация - Домовой Бот</title>
|
||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
|
||
<style>
|
||
.sidebar {
|
||
min-height: 100vh;
|
||
background: linear-gradient(180deg, #2c3e50 0%, #1a252f 100%);
|
||
}
|
||
.sidebar .nav-link { color: rgba(255,255,255,0.8); padding: 1rem; }
|
||
.sidebar .nav-link:hover, .sidebar .nav-link.active { color: white; background-color: rgba(255,255,255,0.1); }
|
||
.content { padding: 2rem; }
|
||
.status-pending { color: #f39c12; }
|
||
.status-approved { color: #27ae60; }
|
||
.status-rejected { color: #c0392b; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container-fluid">
|
||
<div class="row">
|
||
<div class="col-md-2 sidebar p-0">
|
||
<div class="p-3 text-white text-center">
|
||
<h4><i class="bi bi-house-door"></i> Домовой Бот</h4>
|
||
</div>
|
||
<nav class="nav flex-column">
|
||
<a class="nav-link" href="/"><i class="bi bi-speedometer2"></i> Дашборд</a>
|
||
<a class="nav-link" href="/users"><i class="bi bi-people"></i> Пользователи</a>
|
||
<a class="nav-link active" href="/verification"><i class="bi bi-shield-check"></i> Верификация</a>
|
||
<a class="nav-link" href="/phones"><i class="bi bi-telephone"></i> Телефоны</a>
|
||
<a class="nav-link" href="/export"><i class="bi bi-download"></i> Экспорт</a>
|
||
<a class="nav-link" href="/scheduled_posts"><i class="bi bi-calendar2-plus"></i> Запланированные посты</a>
|
||
</nav>
|
||
</div>
|
||
|
||
<div class="col-md-10 content">
|
||
<h2 class="mb-4"><i class="bi bi-shield-check"></i> Управление верификацией</h2>
|
||
|
||
<!-- Заявки на верификацию -->
|
||
<div class="card mb-4">
|
||
<div class="card-header bg-warning text-dark">
|
||
<h5 class="mb-0"><i class="bi bi-clock-history"></i> Заявки на проверку</h5>
|
||
</div>
|
||
<div class="card-body">
|
||
{% if pending_requests %}
|
||
<table class="table table-hover">
|
||
<thead>
|
||
<tr>
|
||
<th>ID заявки</th>
|
||
<th>User ID</th>
|
||
<th>Имя</th>
|
||
<th>Квартира</th>
|
||
<th>Дата</th>
|
||
<th>Действия</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for req in pending_requests %}
|
||
<tr>
|
||
<td>{{ req.id }}</td>
|
||
<td>{{ req.user_id }}</td>
|
||
<td>{{ req.comment or '—' }}</td>
|
||
<td>{{ req.apartment or '—' }}</td>
|
||
<td>{{ req.created_at.strftime('%d.%m.%Y %H:%M') if req.created_at else '—' }}</td>
|
||
<td>
|
||
<button class="btn btn-sm btn-success" onclick="approveVerification({{ req.id }})">
|
||
<i class="bi bi-check-lg"></i> Одобрить
|
||
</button>
|
||
<button class="btn btn-sm btn-danger" onclick="rejectVerification({{ req.id }})">
|
||
<i class="bi bi-x-lg"></i> Отклонить
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
{% else %}
|
||
<p class="text-muted text-center">Нет заявок на верификацию</p>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Непроверенные пользователи -->
|
||
<div class="card">
|
||
<div class="card-header bg-secondary text-white">
|
||
<h5 class="mb-0"><i class="bi bi-person-x"></i> Непроверенные пользователи</h5>
|
||
</div>
|
||
<div class="card-body">
|
||
{% if unverified_users %}
|
||
<table class="table table-hover">
|
||
<thead>
|
||
<tr>
|
||
<th>User ID</th>
|
||
<th>Имя</th>
|
||
<th>Квартира</th>
|
||
<th>Дата регистрации</th>
|
||
<th>Действия</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for user in unverified_users %}
|
||
<tr>
|
||
<td>{{ user.user_id }}</td>
|
||
<td>{{ user.full_name or '—' }}</td>
|
||
<td>{{ user.apartment or '—' }}</td>
|
||
<td>{{ user.join_date.strftime('%d.%m.%Y') if user.join_date else '—' }}</td>
|
||
<td>
|
||
<button class="btn btn-sm btn-success" onclick="verifyUser({{ user.user_id }})">
|
||
<i class="bi bi-check-lg"></i> Верифицировать
|
||
</button>
|
||
<button class="btn btn-sm btn-warning" onclick="unverifyUser({{ user.user_id }})">
|
||
<i class="bi bi-x-lg"></i> Отозвать
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
{% else %}
|
||
<p class="text-muted text-center">Все пользователи верифицированы</p>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||
<script>
|
||
async function approveVerification(requestId) {
|
||
if (!confirm('Одобрить верификацию?')) return;
|
||
|
||
const response = await fetch(`/api/verification/${requestId}/approve`, { method: 'POST' });
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
alert('✅ Верификация одобрена');
|
||
location.reload();
|
||
} else {
|
||
alert('❌ Ошибка: ' + result.error);
|
||
}
|
||
}
|
||
|
||
async function rejectVerification(requestId) {
|
||
if (!confirm('Отклонить верификацию?')) return;
|
||
|
||
const response = await fetch(`/api/verification/${requestId}/reject`, { method: 'POST' });
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
alert('Верификация отклонена');
|
||
location.reload();
|
||
} else {
|
||
alert('❌ Ошибка: ' + result.error);
|
||
}
|
||
}
|
||
|
||
async function verifyUser(userId) {
|
||
if (!confirm('Верифицировать пользователя?')) return;
|
||
|
||
const response = await fetch(`/api/user/${userId}/verify`, { method: 'POST' });
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
alert('✅ Пользователь верифицирован');
|
||
location.reload();
|
||
} else {
|
||
alert('❌ Ошибка: ' + result.error);
|
||
}
|
||
}
|
||
|
||
async function unverifyUser(userId) {
|
||
if (!confirm('Отозвать верификацию у пользователя?')) return;
|
||
|
||
const response = await fetch(`/api/user/${userId}/unverify`, { method: 'POST' });
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
alert('⚠️ Верификация отозвана');
|
||
location.reload();
|
||
} else {
|
||
alert('❌ Ошибка: ' + result.error);
|
||
}
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|