6. pagesss
{% block extra_js %}
<script>
document.addEventListener('DOMContentLoaded', function () {
const form = document.getElementById('drp-form');
const go = document.getElementById('drp-go');
const force = document.getElementById('drp-force');
const panel = document.getElementById('drp-panel');
const step = document.getElementById('drp-step');
const bar = document.getElementById('drp-bar');
const pf = document.getElementById('pf');
const pt = document.getElementById('pt');
const sp = document.getElementById('sp');
if (!form) return;
// every <option value> on the period selects, oldest first
const all = Array.from(pf.options).map(o => o.value).filter(Boolean);
function csrf() {
const i = form.querySelector('[name=csrfmiddlewaretoken]');
if (i) return i.value;
const m = document.cookie.match(/csrftoken=([^;]+)/);
return m ? m[1] : '';
}
function show(text, cls) {
panel.classList.add('on');
panel.classList.remove('err', 'ok');
if (cls) panel.classList.add(cls);
bar.style.display = cls ? 'none' : 'block';
step.innerHTML = text;
}
// Presets just set the three selects; the form is still the source of truth.
document.querySelectorAll('.preset').forEach(b => {
b.addEventListener('click', () => {
const n = parseInt(b.dataset.months, 10);
if (!all.length) return;
pf.value = n > 0 ? (all[Math.max(0, all.length - n)] || '') : '';
pt.value = '';
sp.value = '';
});
});
// The run can take minutes, so the page polls instead of waiting on the POST.
async function poll(id) {
try {
const r = await fetch(`status/${id}/`);
const s = await r.json();
if (s.status === 'done') {
const st = s.stats || {};
show(`Готово за ${s.seconds} с — ${st.n_charts || '—'} графиков, ` +
`${st.n_periods || 0} отчётных дат, ` +
`${(st.n_clients || 0).toLocaleString('ru-RU')} клиентов. ` +
`<a class="go-link" href="view/${id}/" target="_blank" rel="noopener">` +
`Открыть отчёт</a>`, 'ok');
window.open(`view/${id}/`, '_blank', 'noopener');
go.disabled = false;
setTimeout(() => window.location.reload(), 2000);
return;
}
if (s.status === 'failed') {
show('Ошибка: ' + (s.error || '').split('\n')[0], 'err');
go.disabled = false;
return;
}
show((s.step || 'выполняется') + ` … ${s.seconds} с`);
setTimeout(() => poll(id), 1200);
} catch (e) {
show('Потеряна связь с сервером: ' + e.message, 'err');
go.disabled = false;
}
}
form.addEventListener('submit', async (ev) => {
ev.preventDefault();
go.disabled = true;
show('Проверка готовых отчётов…');
try {
const r = await fetch('start/', {
method: 'POST',
headers: {'X-CSRFToken': csrf()},
body: new FormData(form),
});
const s = await r.json();
if (!r.ok) {
show('Ошибка: ' + (s.error || r.status), 'err');
go.disabled = false;
force.value = '0';
return;
}
if (s.cached) show('Такой отчёт уже построен — открываю…');
else show('Запуск расчёта…');
force.value = '0';
poll(s.id);
} catch (e) {
show('Не удалось запустить: ' + e.message, 'err');
go.disabled = false;
force.value = '0';
}
});
});
</script>
{% endblock %}