250 lines
11 KiB
HTML
250 lines
11 KiB
HTML
{% 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()">×</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()">×</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 %}
|