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