domovoy_bot/web/templates/digests.html

250 lines
11 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 %}Еженедельные дайджесты - МКД ЛенКом 37{% endblock %}
{% block nav_digests %}active{% endblock %}
{% block content %}
<div class="d-flex justify-content-between align-items-center mb-3">
<div>
<h4 class="mb-1" style="font-size: 16px; font-weight: bold;">Еженедельные дайджесты дома</h4>
<div class="small text-muted">Утверждение проектов, ручная корректировка и архив публикаций</div>
</div>
<div>
<button class="btn-unix btn-unix-primary" onclick="generateNow()">
<i class="bi bi-arrow-repeat"></i> Сгенерировать сейчас
</button>
</div>
</div>
<div class="d-flex gap-2 mb-3">
<button class="btn-unix active" id="tabBtnPending" onclick="showTab('pending')">Ожидают утверждения ({{ pending_digests|length }})</button>
<button class="btn-unix" id="tabBtnArchive" onclick="showTab('archive')">Архив дайджестов</button>
</div>
<!-- ВКЛАДКА: НА УТВЕРЖДЕНИИ -->
<div id="pending" class="tab-content-panel">
{% if pending_digests|length > 0 %}
{% for digest in pending_digests %}
<div class="card mb-3">
<div class="card-header d-flex justify-content-between align-items-center">
<span>Дайджест #{{ digest.week_number }}/{{ digest.year }} (Период: {{ digest.period_label }})</span>
<span class="badge-status badge-warning">{{ digest.status_emoji }} {{ digest.status.upper() }}</span>
</div>
<div class="card-body">
<div class="small text-muted mb-2">Создан: {{ digest.created_at }}</div>
<pre style="white-space: pre-wrap; max-height: 250px; background-color: #ffffff; border: 1px solid var(--border-color);">{{ digest.digest_text }}</pre>
<div class="d-flex gap-2 mt-3">
<button class="btn-unix btn-unix-primary" onclick="approveDigest({{ digest.id }})">Утвердить</button>
<button class="btn-unix" onclick="editDigest({{ digest.id }})">Редактировать</button>
<button class="btn-unix" onclick="rejectDigest({{ digest.id }})">Отклонить</button>
<button class="btn-unix" onclick="sendDigest({{ digest.id }})" id="sendBtn{{ digest.id }}" style="display:none;">Отправить жильцам</button>
</div>
</div>
</div>
{% endfor %}
{% else %}
<div class="card p-4 text-center">
<div class="mb-2"><i class="bi bi-inbox text-muted" style="font-size: 32px;"></i></div>
<div style="font-weight: bold;">Нет дайджестов, ожидающих утверждения</div>
<div class="small text-muted mt-1">Автоматическая генерация запускается по расписанию каждое воскресенье в 19:00</div>
</div>
{% endif %}
</div>
<!-- ВКЛАДКА: АРХИВ -->
<div id="archive" class="tab-content-panel" style="display: none;">
{% if archive_digests|length > 0 %}
<div class="card">
<div class="card-header">АРХИВ ВЫПУСКОВ</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table mb-0">
<thead>
<tr>
<th style="width: 120px;">Выпуск</th>
<th>Период</th>
<th style="width: 140px;">Статус</th>
<th style="width: 180px;">Отправлен</th>
<th style="width: 100px;">Действия</th>
</tr>
</thead>
<tbody>
{% for digest in archive_digests %}
<tr>
<td><b>#{{ digest.week_number }}/{{ digest.year }}</b></td>
<td>{{ digest.period_label }}</td>
<td>
<span class="badge-status {% if digest.status == 'approved' or digest.status == 'sent' %}badge-success{% elif digest.status == 'rejected' %}badge-danger{% else %}badge-warning{% endif %}">
{{ digest.status_emoji }} {{ digest.status }}
</span>
</td>
<td>{{ digest.sent_at or '—' }}</td>
<td>
<button class="btn-unix" onclick="viewDigest({{ digest.id }})">Открыть</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% else %}
<div class="card p-4 text-center">
<div class="small text-muted">Архив пока пуст</div>
</div>
{% endif %}
</div>
<!-- Модальное окно для редактирования -->
<div id="editModal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.4); z-index: 1050; align-items: center; justify-content: center;">
<div class="card" style="width: 90%; max-width: 800px; max-height: 90vh; overflow-y: auto; margin: 40px auto; border: 2px solid var(--border-color);">
<div class="card-header d-flex justify-content-between align-items-center">
<span>РЕДАКТИРОВАНИЕ ДАЙДЖЕСТА</span>
<button class="btn-unix" onclick="closeEditModal()">&times;</button>
</div>
<div class="card-body">
<textarea id="editText" class="form-control" style="width: 100%; min-height: 350px; font-family: inherit; font-size: 12px;"></textarea>
<div class="d-flex gap-2 mt-3">
<button class="btn-unix btn-unix-primary" onclick="saveEdit()">Сохранить изменения</button>
<button class="btn-unix" onclick="closeEditModal()">Отмена</button>
</div>
</div>
</div>
</div>
<!-- Модальное окно для просмотра -->
<div id="viewModal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.4); z-index: 1050; align-items: center; justify-content: center;">
<div class="card" style="width: 90%; max-width: 800px; max-height: 90vh; overflow-y: auto; margin: 40px auto; border: 2px solid var(--border-color);">
<div class="card-header d-flex justify-content-between align-items-center">
<span id="viewTitle">ПРОСМОТР ДАЙДЖЕСТА</span>
<button class="btn-unix" onclick="closeViewModal()">&times;</button>
</div>
<div class="card-body">
<pre id="viewText" style="white-space: pre-wrap; background-color: #ffffff; border: 1px solid var(--border-color);"></pre>
<div class="mt-3">
<button class="btn-unix" onclick="closeViewModal()">Закрыть</button>
</div>
</div>
</div>
</div>
<script>
let currentDigestId = null;
function showTab(tabName) {
document.getElementById('pending').style.display = tabName === 'pending' ? 'block' : 'none';
document.getElementById('archive').style.display = tabName === 'archive' ? 'block' : 'none';
document.getElementById('tabBtnPending').className = tabName === 'pending' ? 'btn-unix active' : 'btn-unix';
document.getElementById('tabBtnArchive').className = tabName === 'archive' ? 'btn-unix active' : 'btn-unix';
}
function approveDigest(id) {
if (confirm('Утвердить этот дайджест?')) {
fetch(`/api/digest/${id}/approve`, { method: 'POST' })
.then(r => r.json())
.then(data => {
alert(data.message);
const sendBtn = document.getElementById(`sendBtn${id}`);
if (sendBtn) sendBtn.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').style.display = 'flex';
});
}
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').style.display = 'none';
}
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').style.display = 'flex';
});
}
function closeViewModal() {
document.getElementById('viewModal').style.display = 'none';
}
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;
const oldText = btn.innerHTML;
btn.innerHTML = '⏳ Генерация...';
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.innerHTML = oldText;
}
} catch (err) {
alert('Ошибка: ' + err.message);
btn.disabled = false;
btn.innerHTML = oldText;
}
}
</script>
{% endblock %}