🎨 feat: Add sidebar navigation for Smart Broadcasts and Digests sections
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
parent
222c2ba521
commit
f7aaf96cfa
4 changed files with 183 additions and 46 deletions
|
|
@ -71,7 +71,7 @@
|
|||
<div class="col-md-2 sidebar p-0">
|
||||
<div class="p-3 text-white text-center border-bottom border-secondary">
|
||||
<h4 class="mb-1"><i class="bi bi-house-door"></i> Домовой</h4>
|
||||
<small class="text-white-50">Админ Панель v1.3</small>
|
||||
<small class="text-white-50">Админ Панель v3.1</small>
|
||||
</div>
|
||||
<nav class="nav flex-column mt-3">
|
||||
<a class="nav-link {% block nav_dashboard %}{% endblock %}" href="/">
|
||||
|
|
@ -86,15 +86,41 @@
|
|||
<span class="badge bg-danger">{{ pending_count }}</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
<a class="nav-link {% block nav_phones %}{% endblock %}" href="/phones">
|
||||
<i class="bi bi-telephone"></i> Телефоны
|
||||
</a>
|
||||
|
||||
<!-- РАССЫЛКИ - ПОДМЕНЮ -->
|
||||
<div class="nav-item mt-3 px-3">
|
||||
<small class="text-white-50 text-uppercase" style="font-size: 0.7em; letter-spacing: 1px;">РАССЫЛКИ</small>
|
||||
</div>
|
||||
<a class="nav-link {% block nav_broadcast %}{% endblock %}" href="/broadcast">
|
||||
<i class="bi bi-broadcast"></i> Рассылки
|
||||
<i class="bi bi-broadcast"></i> Обычная рассылка
|
||||
</a>
|
||||
<a class="nav-link {% block nav_smart_broadcast %}{% endblock %}" href="/broadcasts">
|
||||
<i class="bi bi-envelope-check"></i> Умная рассылка
|
||||
<span class="badge bg-success" style="font-size: 0.6em;">NEW</span>
|
||||
</a>
|
||||
<a class="nav-link {% block nav_scheduled_posts %}{% endblock %}" href="/scheduled_posts">
|
||||
<i class="bi bi-calendar2-plus"></i> Запланированные посты
|
||||
</a>
|
||||
|
||||
<!-- ДАЙДЖЕСТЫ -->
|
||||
<div class="nav-item mt-3 px-3">
|
||||
<small class="text-white-50 text-uppercase" style="font-size: 0.7em; letter-spacing: 1px;">ДАЙДЖЕСТЫ</small>
|
||||
</div>
|
||||
<a class="nav-link {% block nav_digests %}{% endblock %}" href="/digests">
|
||||
<i class="bi bi-newspaper"></i> Еженедельные дайджесты
|
||||
<span class="badge bg-success" style="font-size: 0.6em;">NEW</span>
|
||||
</a>
|
||||
<a class="nav-link {% block nav_digest_settings %}{% endblock %}" href="/digest_settings">
|
||||
<i class="bi bi-gear"></i> Настройки дайджеста
|
||||
</a>
|
||||
|
||||
<!-- ОСТАЛЬНОЕ МЕНЮ -->
|
||||
<div class="nav-item mt-3 px-3">
|
||||
<small class="text-white-50 text-uppercase" style="font-size: 0.7em; letter-spacing: 1px;">УПРАВЛЕНИЕ</small>
|
||||
</div>
|
||||
<a class="nav-link {% block nav_phones %}{% endblock %}" href="/phones">
|
||||
<i class="bi bi-telephone"></i> Телефоны
|
||||
</a>
|
||||
<a class="nav-link {% block nav_polls %}{% endblock %}" href="/polls">
|
||||
<i class="bi bi-bar-chart"></i> Опросы
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>📢 Умные рассылки - Домовой Бот</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f5f7fa; color: #333; }
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Умные рассылки - Домовой Бот{% endblock %}
|
||||
|
||||
{% block nav_smart_broadcast %}active{% endblock %}
|
||||
|
||||
{% block extra_style %}
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<style>
|
||||
.container { max-width: 1400px; margin: 0 auto; padding: 20px; }
|
||||
.header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; border-radius: 10px; margin-bottom: 30px; }
|
||||
.header h1 { font-size: 2em; margin-bottom: 10px; }
|
||||
|
|
@ -16,17 +19,28 @@
|
|||
.stat-card { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
|
||||
.stat-card h3 { font-size: 0.9em; color: #666; margin-bottom: 5px; }
|
||||
.stat-card .value { font-size: 2em; font-weight: bold; color: #667eea; }
|
||||
table { width: 100%; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
|
||||
th { background: #667eea; color: white; padding: 15px; text-align: left; }
|
||||
td { padding: 15px; border-bottom: 1px solid #eee; }
|
||||
tr:hover { background: #f8f9fa; }
|
||||
.btn { padding: 8px 16px; border: none; border-radius: 5px; cursor: pointer; font-size: 0.9em; transition: all 0.2s; }
|
||||
.create-form { background: white; padding: 25px; border-radius: 10px; margin-bottom: 30px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
|
||||
.create-form h2 { color: #667eea; margin-bottom: 15px; }
|
||||
.form-group { margin-bottom: 20px; }
|
||||
.form-group label { display: block; font-weight: 500; margin-bottom: 5px; }
|
||||
.form-group textarea { width: 100%; min-height: 120px; padding: 12px; border: 2px solid #e2e8f0; border-radius: 5px; font-family: inherit; font-size: 1em; resize: vertical; }
|
||||
.form-group textarea:focus { outline: none; border-color: #667eea; }
|
||||
.form-group select, .form-group input[type="file"] { width: 100%; padding: 10px; border: 2px solid #e2e8f0; border-radius: 5px; font-size: 1em; }
|
||||
.checkbox-group { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
|
||||
.checkbox-group input[type="checkbox"] { width: 20px; height: 20px; }
|
||||
.btn { padding: 12px 24px; border: none; border-radius: 5px; cursor: pointer; font-size: 1em; transition: all 0.2s; }
|
||||
.btn-primary { background: #667eea; color: white; }
|
||||
.btn-primary:hover { background: #5568d3; }
|
||||
.btn-success { background: #48bb78; color: white; }
|
||||
.btn-success:hover { background: #38a169; }
|
||||
.btn-warning { background: #ed8936; color: white; }
|
||||
.btn-warning:hover { background: #dd6b20; }
|
||||
.btn-info { background: #4299e1; color: white; }
|
||||
.btn-info:hover { background: #3182ce; }
|
||||
table { width: 100%; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
|
||||
th { background: #667eea; color: white; padding: 15px; text-align: left; }
|
||||
td { padding: 15px; border-bottom: 1px solid #eee; }
|
||||
tr:hover { background: #f8f9fa; }
|
||||
.badge { display: inline-block; padding: 4px 8px; border-radius: 4px; font-size: 0.85em; font-weight: 500; }
|
||||
.badge-success { background: #c6f6d5; color: #22543d; }
|
||||
.badge-warning { background: #feebc8; color: #7c2d12; }
|
||||
|
|
@ -38,6 +52,9 @@
|
|||
.modal-content { background: white; padding: 30px; border-radius: 10px; max-width: 800px; width: 90%; max-height: 80vh; overflow-y: auto; }
|
||||
.modal-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
|
||||
.close-btn { background: none; border: none; font-size: 1.5em; cursor: pointer; }
|
||||
.status-msg { padding: 15px; border-radius: 5px; margin: 15px 0; display: none; }
|
||||
.status-msg.success { background: #c6f6d5; color: #22543d; border: 1px solid #9ae6b4; }
|
||||
.status-msg.error { background: #fed7d7; color: #742a2a; border: 1px solid #feb2b2; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
|
@ -45,9 +62,42 @@
|
|||
<div class="header">
|
||||
<a href="/" class="back-link">← На главную</a>
|
||||
<h1>📢 Умные рассылки</h1>
|
||||
<p>Отслеживание прочтений и статистика</p>
|
||||
<p>Создание рассылок с отслеживанием прочтений</p>
|
||||
</div>
|
||||
|
||||
<!-- Форма создания рассылки -->
|
||||
<div class="create-form">
|
||||
<h2>✨ Создать новую рассылку</h2>
|
||||
<div id="statusMsg" class="status-msg"></div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="broadcastText">Текст рассылки *</label>
|
||||
<textarea id="broadcastText" placeholder="Введите текст рассылки. Можно использовать HTML: <b>жирный</b>, <i>курсив</i>, <code>код</code>"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="broadcastPhoto">Фото (необязательно)</label>
|
||||
<input type="file" id="broadcastPhoto" accept="image/*">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="broadcastRecipients">Получатели *</label>
|
||||
<select id="broadcastRecipients">
|
||||
<option value="all_and_chat">💬 В чат + всем верифицированным в личку</option>
|
||||
<option value="all_verified">✅ Только верифицированным в личку</option>
|
||||
<option value="ig_only">👥 Только инициативной группе</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" id="hasReadButton" checked>
|
||||
<label for="hasReadButton">Добавить кнопку "✅ Прочитал"</label>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary" onclick="sendBroadcast()">📢 Отправить рассылку</button>
|
||||
</div>
|
||||
|
||||
<!-- Статистика -->
|
||||
<div class="stats">
|
||||
<div class="stat-card">
|
||||
<h3>Всего рассылок</h3>
|
||||
|
|
@ -67,6 +117,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Таблица рассылок -->
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
|
@ -96,7 +147,7 @@
|
|||
<small>{{ b.read_percent }}%</small>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-primary" onclick="showReaders({{ b.id }})">👥 Кто прочитал</button>
|
||||
<button class="btn btn-info" onclick="showReaders({{ b.id }})">👥 Кто прочитал</button>
|
||||
<button class="btn btn-warning" onclick="showNonReaders({{ b.id }})">⚠️ Кто НЕ прочитал</button>
|
||||
{% if not b.is_reminder_sent %}
|
||||
<button class="btn btn-success" onclick="sendReminder({{ b.id }})">📬 Напоминание</button>
|
||||
|
|
@ -112,7 +163,7 @@
|
|||
{% if broadcasts|length == 0 %}
|
||||
<div style="text-align: center; padding: 50px; background: white; border-radius: 8px; margin-top: 20px;">
|
||||
<h2>📭 Рассылок пока нет</h2>
|
||||
<p style="margin-top: 10px;">Используйте команду /broadcast в Telegram для создания рассылки</p>
|
||||
<p style="margin-top: 10px;">Создайте первую рассылку формой выше!</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
|
@ -140,6 +191,62 @@
|
|||
</div>
|
||||
|
||||
<script>
|
||||
async function sendBroadcast() {
|
||||
const text = document.getElementById('broadcastText').value.trim();
|
||||
const photo = document.getElementById('broadcastPhoto').files[0];
|
||||
const recipients = document.getElementById('broadcastRecipients').value;
|
||||
const hasReadButton = document.getElementById('hasReadButton').checked;
|
||||
const statusMsg = document.getElementById('statusMsg');
|
||||
|
||||
if (!text) {
|
||||
statusMsg.textContent = '❌ Введите текст рассылки!';
|
||||
statusMsg.className = 'status-msg error';
|
||||
statusMsg.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('text', text);
|
||||
formData.append('recipients', recipients);
|
||||
formData.append('has_read_button', hasReadButton);
|
||||
if (photo) {
|
||||
formData.append('photo', photo);
|
||||
}
|
||||
|
||||
statusMsg.textContent = '⏳ Отправка рассылки...';
|
||||
statusMsg.className = 'status-msg';
|
||||
statusMsg.style.display = 'block';
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/broadcast/create', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
statusMsg.textContent = '✅ ' + result.message;
|
||||
statusMsg.className = 'status-msg success';
|
||||
statusMsg.style.display = 'block';
|
||||
|
||||
// Очистка формы
|
||||
document.getElementById('broadcastText').value = '';
|
||||
document.getElementById('broadcastPhoto').value = '';
|
||||
|
||||
// Перезагрузка через 2 секунды
|
||||
setTimeout(() => location.reload(), 2000);
|
||||
} else {
|
||||
statusMsg.textContent = '❌ ' + (result.error || 'Ошибка отправки');
|
||||
statusMsg.className = 'status-msg error';
|
||||
statusMsg.style.display = 'block';
|
||||
}
|
||||
} catch (error) {
|
||||
statusMsg.textContent = '❌ Ошибка: ' + error.message;
|
||||
statusMsg.className = 'status-msg error';
|
||||
statusMsg.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
function showReaders(broadcastId) {
|
||||
fetch(`/api/broadcast/readers/${broadcastId}`)
|
||||
.then(r => r.json())
|
||||
|
|
@ -191,5 +298,4 @@
|
|||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>⚙️ Настройки дайджеста - Домовой Бот</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f5f7fa; color: #333; }
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Настройки дайджеста - Домовой Бот{% endblock %}
|
||||
|
||||
{% block nav_digest_settings %}active{% endblock %}
|
||||
|
||||
{% block extra_style %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
</style>
|
||||
.container { max-width: 900px; margin: 0 auto; padding: 20px; }
|
||||
.header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; border-radius: 10px; margin-bottom: 30px; }
|
||||
.header h1 { font-size: 2em; margin-bottom: 10px; }
|
||||
|
|
@ -101,5 +105,4 @@
|
|||
alert('✅ Настройки сохранены! (Функционал в разработке)');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>📰 Дайджесты - Домовой Бот</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f5f7fa; color: #333; }
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Еженедельные дайджесты - Домовой Бот{% endblock %}
|
||||
|
||||
{% block nav_digests %}active{% endblock %}
|
||||
|
||||
{% block extra_style %}
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<style>
|
||||
.container { max-width: 1400px; margin: 0 auto; padding: 20px; }
|
||||
.header { background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); color: white; padding: 30px; border-radius: 10px; margin-bottom: 30px; }
|
||||
.header h1 { font-size: 2em; margin-bottom: 10px; }
|
||||
|
|
@ -246,5 +249,4 @@
|
|||
alert('Генерация дайджеста запущена! Проверьте Telegram - бот отправит превью.');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
{% endblock %}
|
||||
|
|
|
|||
Loading…
Reference in a new issue