feat: заменить прогресс-бар uptime на сегменты истории проверок
Build and deploy / build-deploy (push) Has been cancelled
Build and deploy / build-deploy (push) Has been cancelled
Каждая сегментная полоска — одна проверка модели за последние 24ч (зелёная = успех, красная = провал), слева старее, справа свежее. Бэкенд отдаёт историю проверок в /api/models и обновляет её после ручного теста.
This commit is contained in:
+35
-12
@@ -134,11 +134,17 @@
|
||||
.uptime-low { color: var(--red); }
|
||||
.uptime-none { color: var(--border); }
|
||||
|
||||
.uptime-bar-wrap { display: flex; align-items: center; gap: 6px; }
|
||||
.uptime-bar {
|
||||
width: 60px; height: 6px; background: var(--border); border-radius: 3px; overflow: hidden;
|
||||
.uptime-segs {
|
||||
display: flex; align-items: center; gap: 2px; flex-wrap: nowrap;
|
||||
}
|
||||
.uptime-bar-fill { height: 100%; border-radius: 3px; transition: width .3s; }
|
||||
.uptime-seg {
|
||||
width: 4px; height: 14px; border-radius: 1px; background: var(--border);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.uptime-seg.ok { background: var(--green); }
|
||||
.uptime-seg.fail { background: var(--red); }
|
||||
.uptime-seg.empty { background: transparent; border: 1px dashed var(--border); box-sizing: border-box; }
|
||||
.uptime-segs .uptime { margin-left: 6px; }
|
||||
|
||||
.check-btn {
|
||||
background: none; border: 1px solid var(--border); border-radius: 4px;
|
||||
@@ -234,13 +240,28 @@ function statusCell(ok) {
|
||||
return `<div class="status-cell"><span class="status-dot ${cls}"></span>${txt}</div>`;
|
||||
}
|
||||
|
||||
function uptimeCell(pct) {
|
||||
if (pct === null || pct === undefined) return `<span class="uptime uptime-none">—</span>`;
|
||||
const cls = pct >= 90 ? 'uptime-high' : pct >= 70 ? 'uptime-mid' : 'uptime-low';
|
||||
const barColor = pct >= 90 ? 'var(--green)' : pct >= 70 ? 'var(--yellow)' : 'var(--red)';
|
||||
return `<div class="uptime-bar-wrap">
|
||||
<div class="uptime-bar"><div class="uptime-bar-fill" style="width:${pct}%;background:${barColor}"></div></div>
|
||||
<span class="uptime ${cls}">${pct}%</span>
|
||||
function uptimeCell(pct, history) {
|
||||
const hist = Array.isArray(history) ? history : [];
|
||||
if (pct === null || pct === undefined) {
|
||||
if (!hist.length) return `<span class="uptime uptime-none">—</span>`;
|
||||
}
|
||||
const cls = pct === null || pct === undefined
|
||||
? 'uptime-none'
|
||||
: (pct >= 90 ? 'uptime-high' : pct >= 70 ? 'uptime-mid' : 'uptime-low');
|
||||
|
||||
// Сегменты: каждый — одна проверка. Слева старее, справа свежее.
|
||||
const segs = hist.map(ok =>
|
||||
`<span class="uptime-seg ${ok ? 'ok' : 'fail'}" title="${ok ? 'успех' : 'провал'}"></span>`
|
||||
).join('');
|
||||
|
||||
const label = pct === null || pct === undefined ? '—' : `${pct}%`;
|
||||
const tip = hist.length
|
||||
? `title="последние ${hist.length} проверок: ${hist.filter(Boolean).length} успех"`
|
||||
: '';
|
||||
|
||||
return `<div class="uptime-segs" ${tip}>
|
||||
${segs || '<span class="uptime-seg empty"></span>'}
|
||||
<span class="uptime ${cls}">${label}</span>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
@@ -258,7 +279,7 @@ function renderRow(m) {
|
||||
<td>${scoreCell(m.arena_text_score, m.arena_text_rank)}</td>
|
||||
<td>${scoreCell(m.arena_agent_score, m.arena_agent_rank)}</td>
|
||||
<td>${statusCell(m.last_ok)}</td>
|
||||
<td>${uptimeCell(m.uptime_pct)}</td>
|
||||
<td>${uptimeCell(m.uptime_pct, m.history)}</td>
|
||||
<td><button class="check-btn" onclick="checkOne('${m.id}', this)">▶ Тест</button></td>
|
||||
</tr>`;
|
||||
}
|
||||
@@ -373,6 +394,8 @@ async function checkOne(modelId, btn) {
|
||||
if (idx >= 0) {
|
||||
allData[idx].last_ok = data.ok;
|
||||
allData[idx].last_latency_ms = data.latency_ms;
|
||||
if (data.history) allData[idx].history = data.history;
|
||||
if (data.uptime_pct !== undefined) allData[idx].uptime_pct = data.uptime_pct;
|
||||
}
|
||||
applyFilters();
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user