Журнал обмена: автопоявление новых заявок; карточка не прыгает наверх при автообновлении
Журнал: новый эндпоинт /admin/journal/rows (строки новее since, рендер тем же шаблоном
journalrow) + JS раз в 8 с подставляет новые заявки сверху с мягкой подсветкой (row-new),
без перезагрузки и без дёрганья; счётчик обновляется, плашка «нет переводов» убирается.
Таблица теперь рендерится всегда (tbody#journal-body), строка вынесена в {{define journalrow}}.
Карточка заявки: автообновление по смене подписи раньше делало location.reload() и сбрасывало
прокрутку наверх — мешало читать. Теперь bjReloadKeepScroll() сохраняет позицию прокрутки
(sessionStorage) и восстанавливает после перезагрузки; автообновление сохранено.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -175,6 +175,10 @@ tbody tr:hover td { background:var(--accent-weak); }
|
||||
.live-meta.changed { color:var(--warn,#8a5a00); }
|
||||
.live-meta.lost { color:var(--err,#b3261e); }
|
||||
@keyframes livedot { 50% { opacity:.3 } }
|
||||
@keyframes bjRowIn { 0% { background:#fff3bf; } 100% { background:transparent; } }
|
||||
#journal-body tr.row-new > td { animation: bjRowIn 3.8s ease-out; }
|
||||
#journal-live-hint .dot { background:var(--ok,#2e7d32); animation:livedot 2s ease-in-out infinite; }
|
||||
@media (prefers-reduced-motion: reduce){ #journal-body tr.row-new > td { animation:none; } #journal-live-hint .dot{ animation:none; } }
|
||||
@media (prefers-reduced-motion: reduce) { .live-meta .dot { animation:none } }
|
||||
.badge.draft, .badge.validated, .badge.submitted_to_nsd { background:var(--accent-weak); color:var(--accent); }
|
||||
.badge.awaiting_decision, .badge.manual_approval { background:var(--warn-weak); color:var(--warn); }
|
||||
@@ -836,6 +840,21 @@ button.dq-link, .dq-link { background:none !important; border:0 !important; padd
|
||||
document.addEventListener('input', function(e){ if (e.target.closest('main form')) dirty = true; }, true);
|
||||
document.addEventListener('change', function(e){ if (e.target.closest('main form')) dirty = true; }, true);
|
||||
try { var u = new URL(location.href); if (u.searchParams.has('flash')) { u.searchParams.delete('flash'); history.replaceState(null, '', u.pathname + u.search + u.hash); } } catch(e){}
|
||||
// Автообновление карточки перерисовывает страницу через перезагрузку. Сохраняем позицию
|
||||
// прокрутки, чтобы при чтении заявку не отбрасывало наверх (автообновление остаётся).
|
||||
var scrollKey = 'bj-scroll:' + location.pathname;
|
||||
function bjReloadKeepScroll(){ try { sessionStorage.setItem(scrollKey, String(window.scrollY||window.pageYOffset||0)); } catch(e){} location.reload(); }
|
||||
try {
|
||||
var sy = sessionStorage.getItem(scrollKey);
|
||||
if (sy !== null) {
|
||||
sessionStorage.removeItem(scrollKey);
|
||||
var syY = parseInt(sy, 10) || 0;
|
||||
if ('scrollRestoration' in history) { try { history.scrollRestoration = 'manual'; } catch(e){} }
|
||||
window.scrollTo(0, syY);
|
||||
setTimeout(function(){ window.scrollTo(0, syY); }, 0);
|
||||
window.addEventListener('load', function(){ window.scrollTo(0, syY); });
|
||||
}
|
||||
} catch(e){}
|
||||
function hhmmss(){ var d = new Date(); return ('0'+d.getHours()).slice(-2)+':'+('0'+d.getMinutes()).slice(-2)+':'+('0'+d.getSeconds()).slice(-2); }
|
||||
function meta(e, text, cls){
|
||||
var m = e.nextElementSibling;
|
||||
@@ -857,7 +876,7 @@ button.dq-link, .dq-link { background:none !important; border:0 !important; padd
|
||||
if (!st) return;
|
||||
var big = e.classList.contains('live-big'), was = e.getAttribute('data-sig');
|
||||
if (big && was && st.sig && st.sig !== was) {
|
||||
if (!dirty) { location.reload(); return; }
|
||||
if (!dirty) { bjReloadKeepScroll(); return; }
|
||||
meta(e, 'Заявка изменилась. <a href="' + location.pathname + location.search + '">Обновить страницу</a> (отмеченное в форме не сохранится)', 'changed');
|
||||
e.setAttribute('data-sig', st.sig);
|
||||
}
|
||||
@@ -876,6 +895,51 @@ 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);
|
||||
})();
|
||||
// Журнал обмена: новые заявки подгружаются и всплывают сверху без перезагрузки страницы.
|
||||
(function(){
|
||||
var body = document.getElementById('journal-body');
|
||||
if (!body) return;
|
||||
var countEl = document.getElementById('journal-count');
|
||||
var empty = document.getElementById('journal-empty');
|
||||
function maxTime(){
|
||||
var mx = '0';
|
||||
body.querySelectorAll('tr[data-time]').forEach(function(tr){
|
||||
var t = tr.getAttribute('data-time') || '0';
|
||||
if (t.length > mx.length || (t.length === mx.length && t > mx)) mx = t;
|
||||
});
|
||||
return mx;
|
||||
}
|
||||
function seenKeys(){
|
||||
var s = {};
|
||||
body.querySelectorAll('tr[data-key]').forEach(function(tr){ s[tr.getAttribute('data-key')] = true; });
|
||||
return s;
|
||||
}
|
||||
function tick(){
|
||||
fetch('/admin/journal/rows?since=' + encodeURIComponent(maxTime()), {cache:'no-store'})
|
||||
.then(function(r){ return r.json(); })
|
||||
.then(function(d){
|
||||
if (d && typeof d.count === 'number' && countEl) countEl.textContent = d.count;
|
||||
if (!d || !d.rows || !d.rows.length) return;
|
||||
var seen = seenKeys();
|
||||
d.rows.sort(function(a,b){ return a.t < b.t ? -1 : a.t > b.t ? 1 : 0; }); // старые первыми — новые всплывают выше
|
||||
var added = 0;
|
||||
d.rows.forEach(function(row){
|
||||
if (!row.key || seen[row.key]) return;
|
||||
var tmp = document.createElement('tbody');
|
||||
tmp.innerHTML = (row.html || '').trim();
|
||||
var tr = tmp.firstElementChild;
|
||||
if (!tr) return;
|
||||
tr.classList.add('row-new');
|
||||
body.insertBefore(tr, body.firstChild);
|
||||
seen[row.key] = true; added++;
|
||||
setTimeout((function(el){ return function(){ el.classList.remove('row-new'); }; })(tr), 4000);
|
||||
});
|
||||
if (added && empty) empty.hidden = true;
|
||||
})
|
||||
.catch(function(){});
|
||||
}
|
||||
setInterval(tick, 8000);
|
||||
})();
|
||||
})();
|
||||
})();
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user