БЫЛО: Каждая из 14 страниц имела своё собственное вшитое меню
с разным набором кнопок (от 4 до 10 пунктов)
СТАЛО: Все 14 страниц наследуют base.html с единым меню из 14 пунктов
в 4 разделах: Пользователи / Коммуникации / Контент / Система
Убрано 1949 строк дублирующегося кода, добавлено 1323 строки чистого контента.
Экономия: -626 строк (32% сокращение).
Переписанные страницы:
- dashboard.html, users.html, verification.html
- phones.html, polls.html, events.html
- schedules.html, ads.html, scheduled_posts.html, export.html
Результат: На ЛЮБОЙ странице пользователь видит ВСЕ 14 пунктов меню.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
84 lines
3.5 KiB
HTML
84 lines
3.5 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}Опросы - Домовой Бот{% endblock %}
|
||
|
||
{% block nav_polls %}active{% endblock %}
|
||
|
||
{% block 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>
|
||
{% endblock %}
|
||
|
||
{% block extra_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>
|
||
{% endblock %}
|