feat: улучшения UI/UX LogBoard+

- Добавлена кнопка Update для управления AJAX auto-update
- AJAX auto-update включен по умолчанию
- Улучшено управление видимостью кнопки Refresh
- Переупорядочены кнопки в header (Update, Refresh)
- Унифицированы стили кнопок (высота, шрифт, границы)
- Добавлен hover эффект для кнопки options с цветом warning
- Позиционирование help-btn в свернутом sidebar
- Уменьшена ширина свернутого sidebar на 30%
- Добавлена логика разворачивания sidebar при клике на options
- Отображение внешнего порта в статусе контейнера
- Показ 'standalone' для контейнеров без проекта
- Обновлена документация

Автор: Сергей Антропов
Сайт: https://devops.org.ru
This commit is contained in:
2025-08-18 20:32:46 +03:00
parent 6e51f00791
commit 749b40a494
20 changed files with 1162 additions and 1649 deletions

View File

@@ -42,7 +42,7 @@ a{color:var(--link)}
}
.sidebar.collapsed {
width: 60px;
width: 42px;
}
.sidebar.collapsed .sidebar-header h1,
@@ -358,7 +358,7 @@ a{color:var(--link)}
}
.sidebar.collapsed + .sidebar-toggle {
left: 60px;
left: 42px;
}
.sidebar-toggle:hover {
@@ -418,6 +418,16 @@ a{color:var(--link)}
.sidebar:not(.collapsed) .help-btn { display: none; }
.sidebar.collapsed .help-btn { display: flex; }
/* При свернутом сайдбаре фиксируем help-btn снизу по центру */
.sidebar.collapsed { position: relative; }
.sidebar.collapsed .help-btn {
position: absolute;
left: 50%;
bottom: 10px;
transform: translateX(-50%);
z-index: 2;
}
/* Компактные контролы в header: по умолчанию скрыты */
.header-compact-controls { display: none; align-items: center; gap: 6px; }
@@ -425,7 +435,6 @@ a{color:var(--link)}
.options-btn:hover,
.help-btn:hover,
.logout-btn:hover {
background: var(--tab-active);
@@ -433,11 +442,18 @@ a{color:var(--link)}
border-color: var(--accent);
}
/* Специальный hover эффект для кнопки options с цветом accent */
.options-btn:hover {
background: var(--accent) !important; /* Цвет логотипа */
color: #0b0d12 !important;
border-color: var(--accent) !important;
}
/* Кнопка options когда меню открыто (неактивное состояние) */
.options-btn:not(.active) {
background: var(--accent);
background: #e0a800; /* Цвет как у кнопки warning */
color: #0b0d12;
border-color: var(--accent);
border-color: #e0a800;
}
.options-btn.active {
@@ -492,6 +508,41 @@ a{color:var(--link)}
border-color: #e0af68;
}
/* Кнопка состояния AJAX Update */
.ajax-update-btn {
background: var(--chip);
color: var(--muted);
border: 1px solid var(--border);
border-radius: 6px;
padding: 6px 12px;
font-size: 11px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
font-family: inherit;
min-width: 60px;
text-align: center;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.ajax-update-btn.ajax-on {
background: #7ea855; /* Зеленый цвет */
color: white;
border-color: #7ea855;
}
.ajax-update-btn.ajax-off {
background: #f7768e; /* Красный цвет */
color: white;
border-color: #f7768e;
}
.ajax-update-btn:hover {
opacity: 0.8;
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
/* Sidebar Controls */
.sidebar-controls {
padding: 16px;
@@ -808,16 +859,18 @@ a{color:var(--link)}
.log-refresh-btn {
background: var(--accent);
color: white;
border: none;
border: 1px solid var(--accent);
border-radius: 6px;
transition: all 0.2s ease;
padding: 6px 24px;
padding: 6px 12px;
font-size: 11px;
font-weight: 500;
display: inline-flex;
align-items: center;
justify-content: center;
height: fit-content;
min-width: 60px;
text-align: center;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.log-refresh-btn:hover {
@@ -2140,14 +2193,15 @@ footer{position:fixed;right:10px;bottom:10px;opacity:.6;font-size:11px}
<span class="counter-value cother">0</span>
</button>
</div>
<button class="btn btn-small log-refresh-btn" title="Обновить логи и счетчики">
<i class="fas fa-sync-alt"></i> Refresh
</button>
<div class="theme-toggle">
<span>Theme</span>
<input id="themeSwitch" type="checkbox" />
</div>
<button id="wsstate" class="ws-status-btn">ws: off</button>
<button id="ajaxUpdateBtn" class="ajax-update-btn" title="AJAX Auto-update">update</button>
<button class="btn btn-small log-refresh-btn" title="Обновить логи и счетчики">
<i class="fas fa-sync-alt"></i> Refresh
</button>
</div>
</div>
@@ -2226,6 +2280,7 @@ const els = {
filter: document.getElementById('filter'),
wsstate: document.getElementById('wsstate'),
ajaxUpdateBtn: document.getElementById('ajaxUpdateBtn'),
projectBadge: document.getElementById('projectBadge'),
clearBtn: document.getElementById('clear'),
@@ -2288,6 +2343,28 @@ function setWsState(s){
}
}
function setAjaxUpdateState(enabled) {
console.log('setAjaxUpdateState: enabled =', enabled, 'els.ajaxUpdateBtn =', !!els.ajaxUpdateBtn);
if (els.ajaxUpdateBtn) {
// Удаляем все классы состояний
els.ajaxUpdateBtn.classList.remove('ajax-on', 'ajax-off');
// Добавляем соответствующий класс
if (enabled) {
els.ajaxUpdateBtn.classList.add('ajax-on');
els.ajaxUpdateBtn.textContent = 'update';
console.log('setAjaxUpdateState: Устанавливаем зеленый цвет (ajax-on)');
} else {
els.ajaxUpdateBtn.classList.add('ajax-off');
els.ajaxUpdateBtn.textContent = 'update';
console.log('setAjaxUpdateState: Устанавливаем красный цвет (ajax-off)');
}
} else {
console.error('setAjaxUpdateState: Кнопка ajaxUpdateBtn не найдена!');
}
}
// Функция для обновления всех логов при изменении фильтров
function refreshAllLogs() {
// Обновляем обычный просмотр
@@ -2784,11 +2861,12 @@ function buildTabs(){
</div>
<div class="container-service">
${escapeHtml(svc.service || svc.name)}
${svc.project ? `${escapeHtml(svc.project)}` : ''}
${escapeHtml(svc.project || 'standalone')}
</div>
<div class="container-status">
<span class="status-indicator ${statusClass}"></span>
${escapeHtml(svc.status)}
${svc.status === 'running' && svc.host_port ? ` on ${svc.host_port} port` : ''}
${svc.url ? `<a href="${svc.url}" target="_blank" class="container-link" title="Открыть сайт"><i class="fas fa-external-link-alt"></i></a>` : ''}
</div>
<div class="container-select">
@@ -5577,6 +5655,17 @@ els.refreshBtn.onclick = async () => {
btn.addEventListener('click', refreshLogsAndCounters);
});
// Обработчик для кнопки update (AJAX autoupdate toggle)
if (els.ajaxUpdateBtn) {
console.log('Инициализация обработчика клика для кнопки update');
els.ajaxUpdateBtn.addEventListener('click', () => {
console.log('Клик по кнопке update - вызываем toggleAjaxLogUpdate()');
toggleAjaxLogUpdate();
});
} else {
console.error('Кнопка ajaxUpdateBtn не найдена при инициализации обработчика!');
}
// Обработчики для счетчиков
function addCounterClickHandlers() {
// Назначаем обработчики на все дубликаты кнопок (и в log-header, и в header-compact-controls)
@@ -6046,6 +6135,10 @@ document.addEventListener('DOMContentLoaded', () => {
const isHidden = sidebarControls.classList.contains('hidden');
if (isHidden) {
// Если сайдбар свернут, сначала разворачиваем его
if (els.sidebar.classList.contains('collapsed')) {
toggleSidebar();
}
// Показываем настройки
sidebarControls.classList.remove('hidden');
els.optionsBtn.classList.remove('active');
@@ -6708,7 +6801,7 @@ window.addEventListener('keydown', async (e)=>{
// Глобальные переменные для AJAX обновления
let ajaxUpdateInterval = null;
let ajaxUpdateEnabled = false;
let ajaxUpdateEnabled = true; // По умолчанию включен
let ajaxUpdateIntervalMs = 2000; // 2 секунды по умолчанию (будет переопределено из env)
// Состояние для каждого контейнера (для multi-view)
@@ -6741,6 +6834,9 @@ window.addEventListener('keydown', async (e)=>{
// Обновляем UI
updateAjaxUpdateCheckbox();
// Обновляем видимость кнопки refresh и состояние кнопки update
updateRefreshButtonVisibility();
}
/**
@@ -6757,17 +6853,29 @@ window.addEventListener('keydown', async (e)=>{
// Обновляем UI
updateAjaxUpdateCheckbox();
// Обновляем видимость кнопки refresh и состояние кнопки update
updateRefreshButtonVisibility();
}
/**
* Переключить состояние AJAX обновления
*/
function toggleAjaxLogUpdate() {
console.log('toggleAjaxLogUpdate: Текущее состояние ajaxUpdateEnabled =', ajaxUpdateEnabled);
if (ajaxUpdateEnabled) {
console.log('toggleAjaxLogUpdate: Отключаем AJAX update');
disableAjaxLogUpdate();
} else {
console.log('toggleAjaxLogUpdate: Включаем AJAX update');
enableAjaxLogUpdate(ajaxUpdateIntervalMs);
}
console.log('toggleAjaxLogUpdate: Новое состояние ajaxUpdateEnabled =', ajaxUpdateEnabled);
// Обновляем видимость кнопки refresh и состояние кнопки update при переключении
updateRefreshButtonVisibility();
}
/**
@@ -6979,6 +7087,35 @@ window.addEventListener('keydown', async (e)=>{
if (checkbox) {
checkbox.checked = ajaxUpdateEnabled;
}
// Обновляем видимость кнопки refresh в зависимости от состояния ajax autoupdate
updateRefreshButtonVisibility();
}
/**
* Обновить видимость кнопки refresh в header и состояние кнопки update
*/
function updateRefreshButtonVisibility() {
console.log('updateRefreshButtonVisibility: ajaxUpdateEnabled =', ajaxUpdateEnabled);
const refreshButtons = document.querySelectorAll('.log-refresh-btn');
console.log('updateRefreshButtonVisibility: Найдено кнопок refresh =', refreshButtons.length);
refreshButtons.forEach(btn => {
if (ajaxUpdateEnabled) {
// Если ajax autoupdate включен, скрываем кнопку refresh
btn.style.display = 'none';
console.log('updateRefreshButtonVisibility: Скрываем кнопку refresh');
} else {
// Если ajax autoupdate выключен, показываем кнопку refresh
btn.style.display = 'inline-flex';
console.log('updateRefreshButtonVisibility: Показываем кнопку refresh');
}
});
// Обновляем состояние кнопки update
console.log('updateRefreshButtonVisibility: Обновляем состояние кнопки update');
setAjaxUpdateState(ajaxUpdateEnabled);
}
/**
@@ -7001,12 +7138,18 @@ window.addEventListener('keydown', async (e)=>{
} else {
disableAjaxLogUpdate();
}
// Обновляем видимость кнопки refresh и состояние кнопки update при изменении состояния чекбокса
updateRefreshButtonVisibility();
});
// Устанавливаем начальное состояние (включен по умолчанию)
checkbox.checked = true;
ajaxUpdateEnabled = true;
// Обновляем видимость кнопки refresh и состояние кнопки update при инициализации
updateRefreshButtonVisibility();
console.log('AJAX Update Checkbox initialized');
}
@@ -7064,6 +7207,9 @@ window.addEventListener('keydown', async (e)=>{
};
console.log('AJAX обновление логов инициализировано');
// Обновляем видимость кнопки refresh и состояние кнопки update после инициализации
updateRefreshButtonVisibility();
}
// Запускаем инициализацию AJAX обновления