domovoy_bot/web/templates/digests.html

275 lines
16 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% 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, var(--color-primary) 0%, rgba(10, 10, 15, 0.7) 100%); border: 1px solid var(--panel-border); color: white; padding: 30px; border-radius: 16px; margin-bottom: 30px; backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); box-shadow: 0 4px 20px rgba(0,0,0,0.3); }
.header h1 { font-size: 2em; margin-bottom: 10px; text-shadow: 0 0 10px var(--color-primary-glow); }
.back-link { color: var(--color-success); text-decoration: none; opacity: 0.9; }
.back-link:hover { opacity: 1; text-shadow: 0 0 8px var(--color-success-glow); }
.tabs { display: flex; gap: 10px; margin-bottom: 20px; }
.tab { padding: 12px 24px; background: rgba(255, 255, 255, 0.05); color: var(--color-text-muted); border: 1px solid var(--panel-border); border-radius: 8px 8px 0 0; cursor: pointer; font-size: 1em; transition: var(--transition-smooth); }
.tab:hover { color: var(--color-text-main); background: rgba(255, 255, 255, 0.08); }
.tab.active { background: var(--color-primary); color: white; border-color: var(--color-primary); box-shadow: 0 0 10px var(--color-primary-glow); }
.tab-content { display: none; }
.tab-content.active { display: block; }
.pending-card { background: var(--panel-bg); padding: 25px; border-radius: 16px; margin-bottom: 20px; box-shadow: 0 4px 20px rgba(0,0,0,0.2); border: 1px solid var(--panel-border); border-left: 5px solid var(--color-primary); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); transition: var(--transition-smooth); }
.pending-card:hover { border-color: var(--panel-border-hover); background: var(--panel-bg-hover); }
.pending-card h3 { color: var(--color-success); margin-bottom: 10px; text-shadow: 0 0 5px var(--color-success-glow); }
.pending-card .meta { color: var(--color-text-muted); font-size: 0.9em; margin-bottom: 15px; }
.pending-card .text { background: rgba(0, 0, 0, 0.3); color: var(--color-text-main); border: 1px solid var(--panel-border); padding: 15px; border-radius: 8px; margin: 15px 0; white-space: pre-wrap; max-height: 300px; overflow-y: auto; font-family: var(--font-mono); }
.btn { padding: 10px 20px; border: none; border-radius: 8px; cursor: pointer; font-size: 0.95em; margin-right: 10px; transition: var(--transition-smooth); }
.btn-approve { background: var(--color-success); color: #000; font-weight: 600; }
.btn-approve:hover { background: #00cca3; box-shadow: 0 0 10px var(--color-success-glow); }
.btn-reject { background: var(--color-danger); color: white; }
.btn-reject:hover { background: #e0244a; box-shadow: 0 0 10px var(--color-danger-glow); }
.btn-edit { background: var(--color-warning); color: #000; font-weight: 600; }
.btn-edit:hover { background: #d99800; box-shadow: 0 0 10px var(--color-warning-glow); }
.btn-send { background: var(--color-info); color: #000; font-weight: 600; }
.btn-send:hover { background: #0099cc; box-shadow: 0 0 10px var(--color-info-glow); }
table { width: 100%; background: var(--panel-bg); border-radius: 16px; overflow: hidden; box-shadow: 0 4px 20px rgba(0,0,0,0.2); border: 1px solid var(--panel-border); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); }
th { background: rgba(138, 43, 226, 0.2); color: var(--color-success); padding: 15px; text-align: left; border-bottom: 2px solid var(--panel-border); font-weight: 600; text-transform: uppercase; font-size: 0.85em; letter-spacing: 0.5px; }
td { padding: 15px; border-bottom: 1px solid var(--panel-border); color: var(--color-text-main); }
tr:hover { background: rgba(255, 255, 255, 0.02); }
.badge { display: inline-block; padding: 4px 8px; border-radius: 6px; font-size: 0.85em; font-weight: 500; border: 1px solid transparent; }
.badge-pending { background: rgba(255, 179, 0, 0.15); color: var(--color-warning); border-color: var(--color-warning); }
.badge-approved { background: rgba(0, 255, 204, 0.15); color: var(--color-success); border-color: var(--color-success); }
.badge-rejected { background: rgba(255, 45, 85, 0.15); color: var(--color-danger); border-color: var(--color-danger); }
.badge-sent { background: rgba(0, 191, 255, 0.15); color: var(--color-info); border-color: var(--color-info); }
.modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.7); z-index: 1000; backdrop-filter: blur(8px); }
.modal.active { display: flex; align-items: center; justify-content: center; }
.modal-content { background: var(--bg-main); border: 1px solid var(--panel-border); color: var(--color-text-main); padding: 30px; border-radius: 16px; max-width: 900px; width: 90%; max-height: 80vh; overflow-y: auto; box-shadow: 0 8px 32px rgba(0,0,0,0.5), 0 0 15px var(--color-primary-glow); }
.modal-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; border-bottom: 1px solid var(--panel-border); padding-bottom: 15px; }
.close-btn { background: none; border: none; font-size: 1.5em; cursor: pointer; color: var(--color-text-muted); }
.close-btn:hover { color: var(--color-text-main); }
textarea { width: 100%; min-height: 300px; padding: 15px; border: 1px solid var(--panel-border); border-radius: 8px; background: rgba(0, 0, 0, 0.3); color: var(--color-text-main); font-family: var(--font-mono); font-size: 0.95em; transition: var(--transition-smooth); }
textarea:focus { outline: none; border-color: var(--color-primary); box-shadow: 0 0 8px var(--color-primary-glow); }
</style>
</head>
<body>
<div class="container">
<div class="header">
<a href="/" class="back-link">На главную</a>
<h1>📰 Еженедельные дайджесты</h1>
<p>Утверждение, редактирование и архив</p>
</div>
<div class="tabs">
<button class="tab active" onclick="showTab('pending')">⏳ Ожидают утверждения</button>
<button class="tab" onclick="showTab('archive')">📚 Архив</button>
</div>
<div id="pending" class="tab-content active">
{% if pending_digests|length > 0 %}
{% for digest in pending_digests %}
<div class="pending-card">
<h3>📰 Дайджест #{{ digest.week_number }}/{{ digest.year }}</h3>
<div class="meta">
Период: {{ digest.period_label }} | Создан: {{ digest.created_at }} | Статус: <span class="badge badge-pending">{{ digest.status_emoji }} {{ digest.status }}</span>
</div>
<div class="text">{{ digest.digest_text }}</div>
<div style="margin-top: 15px;">
<button class="btn btn-approve" onclick="approveDigest({{ digest.id }})">✅ Утвердить</button>
<button class="btn btn-edit" onclick="editDigest({{ digest.id }})">✏️ Редактировать</button>
<button class="btn btn-reject" onclick="rejectDigest({{ digest.id }})">❌ Отклонить</button>
<button class="btn btn-send" onclick="sendDigest({{ digest.id }})" id="sendBtn{{ digest.id }}" style="display:none;">📨 Отправить</button>
</div>
</div>
{% endfor %}
{% else %}
<div style="text-align: center; padding: 50px; background: white; border-radius: 8px;">
<h2>📭 Нет ожидающих утверждения дайджестов</h2>
<p style="margin-top: 10px;">Следующий дайджест будет сгенерирован в воскресенье в 09:00</p>
<button class="btn btn-send" style="margin-top: 20px;" onclick="generateNow()">🔄 Сгенерировать сейчас</button>
</div>
{% endif %}
</div>
<div id="archive" class="tab-content">
{% if archive_digests|length > 0 %}
<table>
<thead>
<tr>
<th>Неделя</th>
<th>Период</th>
<th>Статус</th>
<th>Отправлен</th>
<th>Действия</th>
</tr>
</thead>
<tbody>
{% for digest in archive_digests %}
<tr>
<td>#{{ digest.week_number }}/{{ digest.year }}</td>
<td>{{ digest.period_label }}</td>
<td><span class="badge badge-{{ digest.status }}">{{ digest.status_emoji }} {{ digest.status }}</span></td>
<td>{{ digest.sent_at }}</td>
<td>
<button class="btn btn-edit" onclick="viewDigest({{ digest.id }})">👁️ Просмотр</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div style="text-align: center; padding: 50px; background: white; border-radius: 8px;">
<h2>📚 Архив пуст</h2>
<p style="margin-top: 10px;">Здесь будут отображаться все дайджесты</p>
</div>
{% endif %}
</div>
</div>
<!-- Модальное окно для редактирования -->
<div id="editModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2>✏️ Редактирование дайджеста</h2>
<button class="close-btn" onclick="closeEditModal()">&times;</button>
</div>
<textarea id="editText"></textarea>
<div style="margin-top: 15px;">
<button class="btn btn-approve" onclick="saveEdit()">💾 Сохранить</button>
<button class="btn btn-reject" onclick="closeEditModal()">Отмена</button>
</div>
</div>
</div>
<!-- Модальное окно для просмотра -->
<div id="viewModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2 id="viewTitle">📰 Дайджест</h2>
<button class="close-btn" onclick="closeViewModal()">&times;</button>
</div>
<div id="viewText" style="background: #f8f9fa; padding: 15px; border-radius: 5px; white-space: pre-wrap;"></div>
</div>
</div>
<script>
let currentDigestId = null;
function showTab(tabName) {
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
event.target.classList.add('active');
document.getElementById(tabName).classList.add('active');
}
function approveDigest(id) {
if (confirm('Утвердить этот дайджест?')) {
fetch(`/api/digest/${id}/approve`, { method: 'POST' })
.then(r => r.json())
.then(data => {
alert(data.message);
// Показываем кнопку "Отправить"
document.getElementById(`sendBtn${id}`).style.display = 'inline-block';
location.reload();
});
}
}
function rejectDigest(id) {
const reason = prompt('Причина отклонения:');
if (reason) {
fetch(`/api/digest/${id}/reject`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ reason: reason })
})
.then(r => r.json())
.then(data => {
alert(data.message);
location.reload();
});
}
}
function editDigest(id) {
currentDigestId = id;
fetch(`/api/digest/${id}`)
.then(r => r.json())
.then(data => {
document.getElementById('editText').value = data.digest.text;
document.getElementById('editModal').classList.add('active');
});
}
function saveEdit() {
const newText = document.getElementById('editText').value;
fetch(`/api/digest/${currentDigestId}/update_text`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: newText })
})
.then(r => r.json())
.then(data => {
alert(data.message);
closeEditModal();
location.reload();
});
}
function closeEditModal() {
document.getElementById('editModal').classList.remove('active');
}
function viewDigest(id) {
fetch(`/api/digest/${id}`)
.then(r => r.json())
.then(data => {
document.getElementById('viewTitle').textContent = `📰 Дайджест #${data.digest.week}/${data.digest.year}`;
document.getElementById('viewText').textContent = data.digest.text;
document.getElementById('viewModal').classList.add('active');
});
}
function closeViewModal() {
document.getElementById('viewModal').classList.remove('active');
}
function sendDigest(id) {
if (confirm('Отправить дайджест всем верифицированным жильцам?')) {
fetch(`/api/digest/${id}/send`, { method: 'POST' })
.then(r => r.json())
.then(data => {
alert(data.message);
location.reload();
});
}
}
async function generateNow() {
const btn = event.target;
btn.disabled = true;
btn.textContent = '⏳ Генерация...';
try {
const r = await fetch('/api/digest/generate', { method: 'POST' });
const result = await r.json();
if (result.success) {
alert('✅ ' + result.message);
location.reload();
} else {
alert('❌ Ошибка: ' + (result.error || 'Неизвестная'));
btn.disabled = false;
btn.textContent = '🔄 Сгенерировать сейчас';
}
} catch (err) {
alert('❌ Ошибка: ' + err.message);
btn.disabled = false;
btn.textContent = '🔄 Сгенерировать сейчас';
}
}
</script>
{% endblock %}