Списки: период, сортировка, подгрузка; анкета участника — строкой исходящих

«Исходящие», «Входящие», «Журнал обмена»: быстрые периоды (сегодня, 7, 30
дней, всё время), даты с–по, сортировка (время, инвестор/контрагент,
направление, статус), первые 50 строк и подгрузка при прокрутке
(part=rows&offset=N). Раньше показывались только последние 200/100.

Анкета участника — исходящий пакет в НРД: строка в «Исходящих» и
«Журнале» с ответом НРД (принята / отклонена с кодом / ждёт), вместо
плашки над списком. Отправки копятся в participant-regs.json (прежний
participant-reg.json подхватывается). userTemplate — функции текущего
пользователя и для порций строк.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q7eREaYwUt1i4fNLZcGnjn
This commit is contained in:
fontvielle
2026-10-22 10:50:43 +03:00
parent 890e575d3d
commit 60b4d46277
13 changed files with 522 additions and 92 deletions
@@ -184,6 +184,13 @@ tbody tr:hover td { background:var(--accent-weak); }
.badge.awaiting_decision, .badge.manual_approval { background:var(--warn-weak); color:var(--warn); }
.badge.confirmed, .badge.awaiting_sub16, .badge.done { background:var(--ok-weak); color:var(--ok); }
.badge.rejected, .badge.timed_out, .badge.err { background:var(--err-weak); color:var(--err); }
.list-tools{display:flex;flex-wrap:wrap;align-items:center;gap:8px 14px;margin:0 0 12px;font-size:13px}
.list-tools label{display:inline-flex;align-items:center;gap:6px;margin:0;color:var(--muted)}
.list-tools input[type=date],.list-tools select{padding:4px 8px;font-size:13px}
.lt-presets{display:inline-flex;gap:4px;flex-wrap:wrap}
.lt-p{padding:3px 10px;border:1px solid var(--border);border-radius:999px;text-decoration:none;color:var(--text)}
.lt-p.on{background:var(--accent);border-color:var(--accent);color:#fff}
.list-more{text-align:center;padding:12px;font-size:13px;cursor:pointer}
.badge.ok { background:var(--ok-weak); color:var(--ok); }
/* ---- Код / preformatted ---- */
@@ -981,6 +988,36 @@ button.dq-link, .dq-link { background:none !important; border:0 !important; padd
setInterval(tick, 8000);
if (document.querySelector('.live[data-live][data-active]')) setTimeout(tick, 1500);
})();
// Списки: следующие строки подгружаются при прокрутке (тем же адресом с part=rows&offset=N).
(function(){
var els = document.querySelectorAll('[data-list-more]');
if (!els.length || !window.fetch) return;
els.forEach(function(el){
var body = document.getElementById(el.getAttribute('data-body')), busy = false;
if (!body) return;
function near(){ return el.getBoundingClientRect().top < window.innerHeight + 300; }
function load(){
if (busy || el.hidden) return;
busy = true;
var q = el.getAttribute('data-q') || '';
fetch(location.pathname + '?' + (q ? q + '&' : '') + 'part=rows&offset=' + el.getAttribute('data-next'), {cache:'no-store', headers:{'X-Requested-With':'fetch'}})
.then(function(r){ if (!r.ok) throw new Error(r.status); return r.json(); })
.then(function(d){
var tmp = document.createElement('tbody');
tmp.innerHTML = (d.html || '').trim();
while (tmp.firstElementChild) body.appendChild(tmp.firstElementChild);
el.setAttribute('data-next', d.next);
if (!d.more) el.hidden = true;
busy = false;
if (!el.hidden && near()) load();
})
.catch(function(){ busy = false; el.textContent = 'Не удалось загрузить — нажмите, чтобы повторить'; });
}
el.addEventListener('click', load);
if ('IntersectionObserver' in window) new IntersectionObserver(function(es){ if (es[0].isIntersecting) load(); }, {rootMargin:'300px'}).observe(el);
else if (near()) load();
});
})();
// Журнал обмена: новые заявки подгружаются и всплывают сверху без перезагрузки страницы.
(function(){
var body = document.getElementById('journal-body');
@@ -1002,6 +1039,7 @@ button.dq-link, .dq-link { background:none !important; border:0 !important; padd
}
function tick(){
if (window.bjSelecting && window.bjSelecting()) return; // идёт выделение текста — новые строки подставим позже
if (location.search) return; // выбран период или сортировка — сверху не подставляем
fetch('/admin/journal/rows?since=' + encodeURIComponent(maxTime()), {cache:'no-store'})
.then(function(r){ return r.json(); })
.then(function(d){