From c647c2eb7122cc243fd6be877bff2ddc8c413995 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sat, 16 Aug 2025 12:39:31 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B8=20Jav?= =?UTF-8?q?aScript=20=D0=B2=20=D1=81=D0=BE=D0=B2=D1=80=D0=B5=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D0=BD=D0=BE=D0=BC=20=D0=B8=D0=BD=D1=82=D0=B5=D1=80=D1=84?= =?UTF-8?q?=D0=B5=D0=B9=D1=81=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлены проверки на null для отсутствующих элементов - Исправлена ошибка 'Cannot set properties of null' - Добавлено логирование для отладки загрузки проектов и сервисов - Улучшена обработка ошибок в fetchProjects и fetchServices - Исправлена работа с layoutBadge, aggregate и groupBtn - Добавлены проверки существования элементов перед их использованием Автор: Сергей Антропов Сайт: https://devops.org.ru --- templates/index.html | 141 ++++++++++++++++++++++++++++--------------- 1 file changed, 92 insertions(+), 49 deletions(-) diff --git a/templates/index.html b/templates/index.html index a1d7b09..f65512c 100644 --- a/templates/index.html +++ b/templates/index.html @@ -601,11 +601,11 @@ const els = { lvlInfo: document.getElementById('lvlInfo'), lvlWarn: document.getElementById('lvlWarn'), lvlErr: document.getElementById('lvlErr'), - layoutBadge: document.getElementById('layoutBadge'), - aggregate: document.getElementById('aggregate'), + layoutBadge: document.getElementById('layoutBadge') || { textContent: '' }, + aggregate: document.getElementById('aggregate') || { checked: false }, themeSwitch: document.getElementById('themeSwitch'), copyFab: document.getElementById('copyFab'), - groupBtn: document.getElementById('groupBtn'), + groupBtn: document.getElementById('groupBtn') || { onclick: null }, // New modern elements containerList: document.getElementById('containerList'), @@ -792,51 +792,78 @@ function buildTabs(){ function setLayout(cls){ state.layout = cls; - els.layoutBadge.textContent = 'view: ' + (cls==='tabs'?'tabs':cls); + if (els.layoutBadge) { + els.layoutBadge.textContent = 'view: ' + (cls==='tabs'?'tabs':cls); + } els.grid.className = cls==='tabs' ? 'grid-1' : (cls==='grid2'?'grid-2':cls==='grid3'?'grid-3':'grid-4'); } async function fetchProjects(){ - const url = new URL(location.origin + '/api/projects'); - const res = await fetch(url); - if (!res.ok){ console.error('Failed to fetch projects'); return; } - const projects = await res.json(); - - // Обновляем селектор проектов - const select = els.projectSelect; - select.innerHTML = ''; - projects.forEach(project => { - const option = document.createElement('option'); - option.value = project; - option.textContent = project; - select.appendChild(option); - }); - - // Устанавливаем сохраненный проект - if (localStorage.lb_project && projects.includes(localStorage.lb_project)) { - select.value = localStorage.lb_project; + try { + console.log('Fetching projects...'); + const url = new URL(location.origin + '/api/projects'); + const res = await fetch(url); + if (!res.ok){ + console.error('Failed to fetch projects:', res.status, res.statusText); + return; + } + const projects = await res.json(); + console.log('Projects loaded:', projects); + + // Обновляем селектор проектов + const select = els.projectSelect; + if (select) { + select.innerHTML = ''; + projects.forEach(project => { + const option = document.createElement('option'); + option.value = project; + option.textContent = project; + select.appendChild(option); + }); + + // Устанавливаем сохраненный проект + if (localStorage.lb_project && projects.includes(localStorage.lb_project)) { + select.value = localStorage.lb_project; + } + } + } catch (error) { + console.error('Error fetching projects:', error); } } async function fetchServices(){ - const url = new URL(location.origin + '/api/services'); - const selectedProject = els.projectSelect.value; - - if (selectedProject && selectedProject !== 'all') { - url.searchParams.set('projects', selectedProject); - localStorage.lb_project = selectedProject; - } else { - localStorage.removeItem('lb_project'); + try { + console.log('Fetching services...'); + const url = new URL(location.origin + '/api/services'); + const selectedProject = els.projectSelect ? els.projectSelect.value : 'all'; + + if (selectedProject && selectedProject !== 'all') { + url.searchParams.set('projects', selectedProject); + localStorage.lb_project = selectedProject; + } else { + localStorage.removeItem('lb_project'); + } + + const res = await fetch(url); + if (!res.ok){ + console.error('Auth failed (HTTP):', res.status, res.statusText); + alert('Auth failed (HTTP)'); + return; + } + const data = await res.json(); + console.log('Services loaded:', data); + state.services = data; + const pj = selectedProject === 'all' ? 'all' : selectedProject; + + if (els.projectBadge) { + els.projectBadge.innerHTML = 'project: '+escapeHtml(pj)+''; + } + + buildTabs(); + if (!state.current && state.services.length) switchToSingle(state.services[0]); + } catch (error) { + console.error('Error fetching services:', error); } - - const res = await fetch(url); - if (!res.ok){ alert('Auth failed (HTTP)'); return; } - const data = await res.json(); - state.services = data; - const pj = selectedProject === 'all' ? 'all' : selectedProject; - els.projectBadge.innerHTML = 'project: '+escapeHtml(pj)+''; - buildTabs(); - if (!state.current && state.services.length) switchToSingle(state.services[0]); } function wsUrl(containerId, service, project){ @@ -966,8 +993,12 @@ function switchToSingle(svc){ for (const p of [...els.grid.children]) if (p!==panel) p.remove(); // Modern interface updates - els.logTitle.textContent = `${svc.name} (${svc.service || svc.name})`; - els.logContent.textContent = 'Connecting...'; + if (els.logTitle) { + els.logTitle.textContent = `${svc.name} (${svc.service || svc.name})`; + } + if (els.logContent) { + els.logContent.textContent = 'Connecting...'; + } // Update active state in container list document.querySelectorAll('.container-item').forEach(item => { @@ -1090,14 +1121,16 @@ function openFanGroup(services){ updateIdFiltersBar(); } -els.groupBtn.onclick = ()=>{ - const list = state.services.map(s=> `${s.service}`).filter((v,i,a)=> a.indexOf(v)===i).join(', '); - const ans = prompt('Введите имена сервисов через запятую:\n'+list); - if (ans){ - const services = ans.split(',').map(x=>x.trim()).filter(Boolean); - if (services.length) openFanGroup(services); - } -}; +if (els.groupBtn && els.groupBtn.onclick !== null) { + els.groupBtn.onclick = ()=>{ + const list = state.services.map(s=> `${s.service}`).filter((v,i,a)=> a.indexOf(v)===i).join(', '); + const ans = prompt('Введите имена сервисов через запятую:\n'+list); + if (ans){ + const services = ans.split(',').map(x=>x.trim()).filter(Boolean); + if (services.length) openFanGroup(services); + } + }; +} // Controls els.clearBtn.onclick = ()=> Object.values(state.open).forEach(o=> o.logEl.textContent=''); @@ -1156,6 +1189,16 @@ window.addEventListener('keydown', (e)=>{ // Инициализация (async function init() { + console.log('Initializing LogBoard+...'); + console.log('Elements found:', { + projectSelect: !!els.projectSelect, + containerList: !!els.containerList, + logTitle: !!els.logTitle, + logContent: !!els.logContent, + mobileToggle: !!els.mobileToggle, + themeSwitch: !!els.themeSwitch + }); + await fetchProjects(); await fetchServices(); })();