fix: исправить ошибки JavaScript в современном интерфейсе

- Добавлены проверки на null для отсутствующих элементов
- Исправлена ошибка 'Cannot set properties of null'
- Добавлено логирование для отладки загрузки проектов и сервисов
- Улучшена обработка ошибок в fetchProjects и fetchServices
- Исправлена работа с layoutBadge, aggregate и groupBtn
- Добавлены проверки существования элементов перед их использованием

Автор: Сергей Антропов
Сайт: https://devops.org.ru
This commit is contained in:
Сергей Антропов 2025-08-16 12:39:31 +03:00
parent 43f19d32e1
commit c647c2eb71

View File

@ -601,11 +601,11 @@ const els = {
lvlInfo: document.getElementById('lvlInfo'), lvlInfo: document.getElementById('lvlInfo'),
lvlWarn: document.getElementById('lvlWarn'), lvlWarn: document.getElementById('lvlWarn'),
lvlErr: document.getElementById('lvlErr'), lvlErr: document.getElementById('lvlErr'),
layoutBadge: document.getElementById('layoutBadge'), layoutBadge: document.getElementById('layoutBadge') || { textContent: '' },
aggregate: document.getElementById('aggregate'), aggregate: document.getElementById('aggregate') || { checked: false },
themeSwitch: document.getElementById('themeSwitch'), themeSwitch: document.getElementById('themeSwitch'),
copyFab: document.getElementById('copyFab'), copyFab: document.getElementById('copyFab'),
groupBtn: document.getElementById('groupBtn'), groupBtn: document.getElementById('groupBtn') || { onclick: null },
// New modern elements // New modern elements
containerList: document.getElementById('containerList'), containerList: document.getElementById('containerList'),
@ -792,51 +792,78 @@ function buildTabs(){
function setLayout(cls){ function setLayout(cls){
state.layout = 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'); els.grid.className = cls==='tabs' ? 'grid-1' : (cls==='grid2'?'grid-2':cls==='grid3'?'grid-3':'grid-4');
} }
async function fetchProjects(){ async function fetchProjects(){
const url = new URL(location.origin + '/api/projects'); try {
const res = await fetch(url); console.log('Fetching projects...');
if (!res.ok){ console.error('Failed to fetch projects'); return; } const url = new URL(location.origin + '/api/projects');
const projects = await res.json(); const res = await fetch(url);
if (!res.ok){
// Обновляем селектор проектов console.error('Failed to fetch projects:', res.status, res.statusText);
const select = els.projectSelect; return;
select.innerHTML = '<option value="all">All Projects</option>'; }
projects.forEach(project => { const projects = await res.json();
const option = document.createElement('option'); console.log('Projects loaded:', projects);
option.value = project;
option.textContent = project; // Обновляем селектор проектов
select.appendChild(option); const select = els.projectSelect;
}); if (select) {
select.innerHTML = '<option value="all">All Projects</option>';
// Устанавливаем сохраненный проект projects.forEach(project => {
if (localStorage.lb_project && projects.includes(localStorage.lb_project)) { const option = document.createElement('option');
select.value = localStorage.lb_project; 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(){ async function fetchServices(){
const url = new URL(location.origin + '/api/services'); try {
const selectedProject = els.projectSelect.value; console.log('Fetching services...');
const url = new URL(location.origin + '/api/services');
if (selectedProject && selectedProject !== 'all') { const selectedProject = els.projectSelect ? els.projectSelect.value : 'all';
url.searchParams.set('projects', selectedProject);
localStorage.lb_project = selectedProject; if (selectedProject && selectedProject !== 'all') {
} else { url.searchParams.set('projects', selectedProject);
localStorage.removeItem('lb_project'); 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: <em>'+escapeHtml(pj)+'</em>';
}
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: <em>'+escapeHtml(pj)+'</em>';
buildTabs();
if (!state.current && state.services.length) switchToSingle(state.services[0]);
} }
function wsUrl(containerId, service, project){ function wsUrl(containerId, service, project){
@ -966,8 +993,12 @@ function switchToSingle(svc){
for (const p of [...els.grid.children]) if (p!==panel) p.remove(); for (const p of [...els.grid.children]) if (p!==panel) p.remove();
// Modern interface updates // Modern interface updates
els.logTitle.textContent = `${svc.name} (${svc.service || svc.name})`; if (els.logTitle) {
els.logContent.textContent = 'Connecting...'; els.logTitle.textContent = `${svc.name} (${svc.service || svc.name})`;
}
if (els.logContent) {
els.logContent.textContent = 'Connecting...';
}
// Update active state in container list // Update active state in container list
document.querySelectorAll('.container-item').forEach(item => { document.querySelectorAll('.container-item').forEach(item => {
@ -1090,14 +1121,16 @@ function openFanGroup(services){
updateIdFiltersBar(); updateIdFiltersBar();
} }
els.groupBtn.onclick = ()=>{ if (els.groupBtn && els.groupBtn.onclick !== null) {
const list = state.services.map(s=> `${s.service}`).filter((v,i,a)=> a.indexOf(v)===i).join(', '); els.groupBtn.onclick = ()=>{
const ans = prompt('Введите имена сервисов через запятую:\n'+list); const list = state.services.map(s=> `${s.service}`).filter((v,i,a)=> a.indexOf(v)===i).join(', ');
if (ans){ const ans = prompt('Введите имена сервисов через запятую:\n'+list);
const services = ans.split(',').map(x=>x.trim()).filter(Boolean); if (ans){
if (services.length) openFanGroup(services); const services = ans.split(',').map(x=>x.trim()).filter(Boolean);
} if (services.length) openFanGroup(services);
}; }
};
}
// Controls // Controls
els.clearBtn.onclick = ()=> Object.values(state.open).forEach(o=> o.logEl.textContent=''); els.clearBtn.onclick = ()=> Object.values(state.open).forEach(o=> o.logEl.textContent='');
@ -1156,6 +1189,16 @@ window.addEventListener('keydown', (e)=>{
// Инициализация // Инициализация
(async function init() { (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 fetchProjects();
await fetchServices(); await fetchServices();
})(); })();