domovoy_bot/web/templates/digests.html

272 lines
13 KiB
HTML
Raw 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, #f093fb 0%, #f5576c 100%); color: white; padding: 30px; border-radius: 10px; margin-bottom: 30px; }
.header h1 { font-size: 2em; margin-bottom: 10px; }
.back-link { color: white; text-decoration: none; opacity: 0.9; }
.back-link:hover { opacity: 1; }
.tabs { display: flex; gap: 10px; margin-bottom: 20px; }
.tab { padding: 12px 24px; background: white; border: none; border-radius: 8px 8px 0 0; cursor: pointer; font-size: 1em; }
.tab.active { background: #f5576c; color: white; }
.tab-content { display: none; }
.tab-content.active { display: block; }
.pending-card { background: white; padding: 25px; border-radius: 10px; margin-bottom: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); border-left: 5px solid #f5576c; }
.pending-card h3 { color: #f5576c; margin-bottom: 10px; }
.pending-card .meta { color: #666; font-size: 0.9em; margin-bottom: 15px; }
.pending-card .text { background: #f8f9fa; padding: 15px; border-radius: 5px; margin: 15px 0; white-space: pre-wrap; max-height: 300px; overflow-y: auto; }
.btn { padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer; font-size: 0.95em; margin-right: 10px; transition: all 0.2s; }
.btn-approve { background: #48bb78; color: white; }
.btn-approve:hover { background: #38a169; }
.btn-reject { background: #f56565; color: white; }
.btn-reject:hover { background: #e53e3e; }
.btn-edit { background: #ed8936; color: white; }
.btn-edit:hover { background: #dd6b20; }
.btn-send { background: #4299e1; color: white; }
.btn-send: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: #f5576c; 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-pending { background: #feebc8; color: #7c2d12; }
.badge-approved { background: #c6f6d5; color: #22543d; }
.badge-rejected { background: #fed7d7; color: #742a2a; }
.badge-sent { background: #bee3f8; color: #2c5282; }
.modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 1000; }
.modal.active { display: flex; align-items: center; justify-content: center; }
.modal-content { background: white; padding: 30px; border-radius: 10px; max-width: 900px; 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; }
textarea { width: 100%; min-height: 300px; padding: 15px; border: 2px solid #e2e8f0; border-radius: 5px; font-family: inherit; font-size: 0.95em; }
textarea:focus { outline: none; border-color: #f5576c; }
</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 %}