База bj на своём PostgreSQL; «Не заполнено: …» для пустых полей; запись канала ИШ — в фоне, с ходом на странице

- шаг «База bj»: перенос на сервер PostgreSQL организации (проверка подключения, версии, прав, пустой базы;
  pg_dump → psql; сверка числа строк по таблицам; переключение и перезапуск); прежняя база — запасная копия;
  резервные копии: дамп внешней базы по её подключению, восстановление её не трогает
- все формы: пустые видимые обязательные поля называются по имени и подсвечиваются; DEPO — и на сервере
- «Обновить/Создать канал»: в фоне bj, ход по этапам с таймером (и после перезагрузки страницы), переходы
  заблокированы до конца, в конце проверка, что шлюз загрузил канал; состояние ИШ во время записи — без
  очереди к root-обработчику (страница больше не висит на «Проверяю…»)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
zuevav
2026-10-07 00:00:35 +03:00
parent 3a9bc5d7fa
commit 4adc1398f6
16 changed files with 832 additions and 50 deletions
@@ -763,6 +763,49 @@ details:has(> pre.copyable) { min-width:0; max-width:100%; }
});
})();
</script>
<style>.need-miss{border-color:var(--err)!important;box-shadow:0 0 0 3px color-mix(in srgb,var(--err) 18%,transparent)!important}
.need-msg{color:var(--err);font-size:13.5px;margin:8px 0 0;flex-basis:100%}</style>
<script>
/* Обязательные поля (06.10.2026): кнопка не уходит с пустыми видимыми полями — bj пишет, какие именно не заполнены,
подсвечивает их и ставит курсор в первое. Поля помечены required или data-need="как назвать"; скрытые (другая
вкладка, другой способ) не проверяются. window.bjNeed(form, btn) — то же для кнопок без отправки формы. */
(function(){
function visible(el){ return !el.disabled && el.type!=='hidden' && !!(el.offsetWidth||el.offsetHeight||el.getClientRects().length); }
function name(el){
var n=el.getAttribute('data-need')||el.getAttribute('aria-label')||'';
if(!n){ var l=el.closest('label'); if(l){ var c=l.cloneNode(true); c.querySelectorAll('input,select,textarea,button,.muted,.tooltip').forEach(function(x){x.remove()}); n=c.textContent.replace(/\s+/g,' ').replace(/\(.*?\)/g,'').trim(); } }
return n||el.placeholder||el.name;
}
function msgBox(f, btn){
var host=(btn&&btn.parentElement)||f, m=host.querySelector(':scope > .need-msg');
if(!m){ m=document.createElement('div'); m.className='need-msg'; m.setAttribute('role','alert'); host.appendChild(m); }
return m;
}
window.bjNeed=function(f, btn){
var miss=[], bad=[], first=null;
f.querySelectorAll('[required],[data-need]').forEach(function(el){
el.classList.remove('need-miss');
if(!visible(el)) return;
var empty = el.type==='file' ? !(el.files&&el.files.length) : (el.type==='checkbox'||el.type==='radio') ? false : !String(el.value||'').trim();
if(empty){ miss.push(name(el)); el.classList.add('need-miss'); first=first||el; }
else if(el.checkValidity && !el.checkValidity()){ bad.push(name(el)); el.classList.add('need-miss'); first=first||el; }
});
var old=f.querySelectorAll('.need-msg'); old.forEach(function(x){ x.remove(); });
if(!first) return true;
var t=[]; if(miss.length) t.push('Не заполнено: '+miss.join(', ')); if(bad.length) t.push('Заполнено неверно: '+bad.join(', '));
msgBox(f, btn).textContent=t.join('. ')+'.';
try{ first.focus({preventScroll:false}); first.scrollIntoView({block:'center',behavior:'smooth'}); }catch(e){}
return false;
};
document.addEventListener('DOMContentLoaded',function(){ document.querySelectorAll('form').forEach(function(f){ f.noValidate=true; }); });
document.addEventListener('submit',function(e){
var f=e.target; if(!(f instanceof HTMLFormElement)) return;
var b=e.submitter; if(b&&b.hasAttribute('formnovalidate')) return;
if(!window.bjNeed(f, b)){ e.preventDefault(); e.stopImmediatePropagation(); }
}, true);
document.addEventListener('input',function(e){ var el=e.target; if(el.classList&&el.classList.contains('need-miss')&&String(el.value||'').trim()) el.classList.remove('need-miss'); });
})();
</script>
</body>
</html>
{{end}}