✅ Проведено полное тестирование функционала: - Меню жильца (11 кнопок) - Меню админа (17 кнопок) - База данных (проверка записей) - Уникальность callback_data - Сообщения в личку (без спама) 📝 Созданные файлы: - tests/run_tests.py — автотесты - tests/TEST_REPORT.md — отчёт о тестировании - tests/COMPREHENSIVE_TEST_PLAN.md — план тестов - send_test_report.py — отправка отчёта 📊 Результаты: - Все кнопки работают - Участковский работает через БД (2 записи) - Сообщения только в личку - Веб-админка работает со всеми разделами 🎯 ВСЁ РАБОТАЕТ! Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
113 lines
6.5 KiB
HTML
113 lines
6.5 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<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; }
|
||
</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" 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="/ads"><i class="bi bi-megaphone"></i> Объявления</a>
|
||
<a class="nav-link" href="/broadcast"><i class="bi bi-broadcast"></i> Рассылки</a>
|
||
<a class="nav-link active" href="/polls"><i class="bi bi-bar-chart"></i> Опросы</a>
|
||
<a class="nav-link" href="/events"><i class="bi bi-calendar-event"></i> События</a>
|
||
<a class="nav-link" href="/schedules"><i class="bi bi-clock"></i> Расписания</a>
|
||
<a class="nav-link" href="/export"><i class="bi bi-download"></i> Экспорт</a>
|
||
<a class="nav-link" href="/settings"><i class="bi bi-gear"></i> Настройки</a>
|
||
</nav>
|
||
</div>
|
||
<div class="col-md-10 content">
|
||
<h2 class="mb-4"><i class="bi bi-bar-chart"></i> Опросы и голосования</h2>
|
||
|
||
<div class="card mb-4">
|
||
<div class="card-header bg-primary text-white">
|
||
<h5 class="mb-0"><i class="bi bi-plus-lg"></i> Создать опрос</h5>
|
||
</div>
|
||
<div class="card-body">
|
||
<form id="createPollForm" class="row g-3">
|
||
<div class="col-md-12">
|
||
<input type="text" class="form-control" id="pollQuestion" placeholder="Вопрос" required>
|
||
</div>
|
||
<div class="col-md-12">
|
||
<label class="form-label">Варианты ответов (каждый с новой строки):</label>
|
||
<textarea class="form-control" id="pollOptions" rows="4" placeholder="Вариант 1 Вариант 2 Вариант 3" required></textarea>
|
||
</div>
|
||
<div class="col-md-12">
|
||
<button type="submit" class="btn btn-primary">Создать опрос</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div class="card-body">
|
||
<h5>Активные опросы:</h5>
|
||
<table class="table table-hover">
|
||
<thead>
|
||
<tr><th>Вопрос</th><th>Варианты</th><th>Голосов</th><th>Дата</th><th>Действия</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for poll in polls %}
|
||
<tr>
|
||
<td>{{ poll.question }}</td>
|
||
<td>{{ (poll.options|length) if poll.options else 0 }} варианта</td>
|
||
<td>{{ poll.votes|length if poll.votes else 0 }}</td>
|
||
<td>{{ poll.created_at.strftime('%d.%m.%Y') if poll.created_at else '—' }}</td>
|
||
<td>
|
||
{% if poll.is_active %}
|
||
<button class="btn btn-sm btn-warning" onclick="closePoll({{ poll.poll_id }})">
|
||
<i class="bi bi-lock"></i> Закрыть
|
||
</button>
|
||
{% else %}
|
||
<span class="badge bg-secondary">Завершён</span>
|
||
{% endif %}
|
||
</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||
<script>
|
||
document.getElementById('createPollForm').addEventListener('submit', async (e) => {
|
||
e.preventDefault();
|
||
const question = document.getElementById('pollQuestion').value;
|
||
const optionsText = document.getElementById('pollOptions').value;
|
||
const options = optionsText.split('\n').filter(o => o.trim());
|
||
|
||
const r = await fetch('/api/poll/create', {
|
||
method: 'POST',
|
||
headers: {'Content-Type': 'application/json'},
|
||
body: JSON.stringify({ question, options, is_active: true })
|
||
});
|
||
const result = await r.json();
|
||
if (result.success) { alert('✅ Опрос создан'); location.reload(); }
|
||
});
|
||
async function closePoll(id) {
|
||
if (!confirm('Закрыть опрос?')) return;
|
||
const r = await fetch(`/api/poll/${id}/close`, { method: 'POST' });
|
||
const result = await r.json();
|
||
if (result.success) { alert('✅ Опрос закрыт'); location.reload(); }
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|