feat: модальное окно для логов проверок без сброса табличных фильтров
Build and deploy / build-deploy (push) Has been cancelled

This commit is contained in:
2026-10-22 09:20:09 +00:00
parent 463d80c4fe
commit 4ecae4c971
+80 -4
View File
@@ -173,6 +173,45 @@
.check-btn:hover { border-color: var(--accent); color: var(--accent); }
.check-btn.checking { opacity: .5; cursor: default; }
/* Модальное окно истории проверок */
.modal-overlay {
position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 2000;
display: flex; align-items: center; justify-content: center;
backdrop-filter: blur(2px); opacity: 0; pointer-events: none; transition: opacity .2s;
}
.modal-overlay.open { opacity: 1; pointer-events: auto; }
.modal-box {
background: var(--bg2); border: 1px solid var(--border); border-radius: 8px;
width: 90%; max-width: 650px; max-height: 80vh; display: flex; flex-direction: column;
box-shadow: 0 10px 30px rgba(0,0,0,.5); overflow: hidden;
}
.modal-header {
padding: 14px 18px; border-bottom: 1px solid var(--border);
display: flex; align-items: center; justify-content: space-between;
}
.modal-header h3 { margin: 0; font-size: 15px; color: var(--text); }
.modal-close {
background: none; border: none; color: var(--text2); font-size: 18px; cursor: pointer;
}
.modal-close:hover { color: var(--text); }
.modal-body {
padding: 14px 18px; overflow-y: auto; font-size: 12px;
}
.check-log-item {
padding: 8px 10px; border-bottom: 1px solid var(--border);
display: flex; flex-direction: column; gap: 4px;
}
.check-log-item:last-child { border-bottom: none; }
.log-line1 { display: flex; align-items: center; justify-content: space-between; }
.log-status.ok { color: var(--green); }
.log-status.fail { color: var(--red); }
.log-meta { color: var(--text2); font-size: 11px; }
.log-err {
color: var(--red); font-family: Consolas, 'Fira Mono', monospace;
font-size: 11px; background: rgba(248,81,73,.1); padding: 4px 6px;
border-radius: 4px; white-space: pre-wrap; word-break: break-word;
}
.empty { padding: 40px 24px; color: var(--text2); text-align: center; }
.loading { text-align: center; padding: 60px; color: var(--text2); }
@@ -491,11 +530,48 @@ async function checkOne(modelId, btn) {
async function openCheckHistory(modelId, btn) {
const res = await fetch(`/api/checks/${encodeURIComponent(modelId)}`);
const data = await res.json();
const idx = allData.findIndex(m => m.id === modelId);
if (idx >= 0) {
allData[idx].history = (data.checks || []).map(c => ({ ok: c.ok, latency_ms: c.latency_ms, at: c.checked_at, error: c.error }));
const checks = data.checks || [];
let modal = document.getElementById('history-modal');
if (!modal) {
modal = document.createElement('div');
modal.id = 'history-modal';
modal.className = 'modal-overlay';
modal.innerHTML = `
<div class="modal-box">
<div class="modal-header">
<h3 id="modal-title">Лог проверок</h3>
<button class="modal-close" onclick="closeModal()">&times;</button>
</div>
<div class="modal-body" id="modal-body"></div>
</div>
`;
modal.addEventListener('click', e => { if (e.target === modal) closeModal(); });
document.body.appendChild(modal);
}
applyFilters();
document.getElementById('modal-title').textContent = `Лог проверок: ${modelId}`;
const body = document.getElementById('modal-body');
if (!checks.length) {
body.innerHTML = '<div class="no-data" style="text-align:center;padding:20px;">Нет данных о проверках</div>';
} else {
body.innerHTML = checks.map(c => `
<div class="check-log-item">
<div class="log-line1">
<span class="log-status ${c.ok ? 'ok' : 'fail'}">${c.ok ? '✓ Успех' : '✗ Провал'}</span>
<span class="log-meta">${fmtTime(c.checked_at)} · ${c.latency_ms !== null && c.latency_ms !== undefined ? c.latency_ms + ' мс' : '—'}</span>
</div>
${c.error ? `<div class="log-err">${escapeHtml(c.error)}</div>` : ''}
</div>
`).join('');
}
modal.classList.add('open');
}
function closeModal() {
const modal = document.getElementById('history-modal');
if (modal) modal.classList.remove('open');
}
document.getElementById('btn-refresh').addEventListener('click', loadData);