feat(UptimeMonitorPage): add current checking server ID state to improve UI feedback during uptime checks
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 2m56s

This commit is contained in:
2026-02-22 18:20:21 +07:00
parent 8c13ae9df4
commit 64d9178784
+28 -24
View File
@@ -90,6 +90,8 @@ export default function UptimeMonitorPage() {
const [historyMap, setHistoryMap] = useState(loadHistoryFromStorage); const [historyMap, setHistoryMap] = useState(loadHistoryFromStorage);
const [selectedServerId, setSelectedServerId] = useState(null); const [selectedServerId, setSelectedServerId] = useState(null);
const [checkIntervalSeconds, setCheckIntervalSeconds] = useState(DEFAULT_CHECK_INTERVAL_SEC); const [checkIntervalSeconds, setCheckIntervalSeconds] = useState(DEFAULT_CHECK_INTERVAL_SEC);
/** serverId, для которого сейчас идёт проверка — показываем «Проверка…» в строке */
const [currentCheckingServerId, setCurrentCheckingServerId] = useState(null);
const intervalRef = useRef(null); const intervalRef = useRef(null);
const checkingRef = useRef(false); const checkingRef = useRef(false);
@@ -133,11 +135,10 @@ export default function UptimeMonitorPage() {
if (checkingRef.current) return; if (checkingRef.current) return;
checkingRef.current = true; checkingRef.current = true;
setChecking(true); setChecking(true);
const results = {};
const newHistory = {};
for (const server of jumphosts) { for (const server of jumphosts) {
const serverId = server.id || server.dns || server.ip; const serverId = server.id || server.dns || server.ip;
if (!serverId) continue; if (!serverId) continue;
setCurrentCheckingServerId(serverId);
const t0 = Date.now(); const t0 = Date.now();
let ok = false; let ok = false;
let ms = null; let ms = null;
@@ -148,24 +149,19 @@ export default function UptimeMonitorPage() {
} catch (_) { } catch (_) {
ms = Date.now() - t0; ms = Date.now() - t0;
} }
results[serverId] = { ok, lastCheckTs: Date.now() }; const lastCheckTs = Date.now();
newHistory[serverId] = [{ ts: Date.now(), ok, ms }]; const entry = { ts: lastCheckTs, ok, ms };
// Обновляем UI сразу после каждой проверки — результат виден по мере прохождения тестов
setStatusMap((prev) => ({ ...prev, [serverId]: { ok, lastCheckTs } }));
setHistoryMap((prev) => {
const list = Array.isArray(prev[serverId]) ? prev[serverId] : [];
const merged = [...list, entry].slice(-HISTORY_MAX);
return { ...prev, [serverId]: merged };
});
setCurrentCheckingServerId(null);
await new Promise((r) => setTimeout(r, DELAY_BETWEEN_CHECKS_MS)); await new Promise((r) => setTimeout(r, DELAY_BETWEEN_CHECKS_MS));
} }
setStatusMap((prev) => { setCurrentCheckingServerId(null);
const next = { ...prev };
for (const [id, v] of Object.entries(results)) next[id] = v;
return next;
});
setHistoryMap((prev) => {
const next = { ...prev };
for (const [id, one] of Object.entries(newHistory)) {
const list = Array.isArray(next[id]) ? next[id] : [];
const merged = [...list, ...one].slice(-HISTORY_MAX);
next[id] = merged;
}
return next;
});
checkingRef.current = false; checkingRef.current = false;
setChecking(false); setChecking(false);
}, [jumphosts]); }, [jumphosts]);
@@ -282,24 +278,32 @@ export default function UptimeMonitorPage() {
</div> </div>
</td> </td>
<td> <td>
{status == null && ( {currentCheckingServerId === id && (
<span className="badge bg-blue-lt text-blue">
<span className="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true" />
Проверка…
</span>
)}
{currentCheckingServerId !== id && status == null && (
<span className="badge bg-secondary">—</span> <span className="badge bg-secondary">—</span>
)} )}
{status?.ok === true && ( {currentCheckingServerId !== id && status?.ok === true && (
<span className="badge bg-success-lt text-success"> <span className="badge bg-success-lt text-success">
<IconCircleCheck size={14} /> Доступен <IconCircleCheck size={14} /> Доступен
</span> </span>
)} )}
{status?.ok === false && ( {currentCheckingServerId !== id && status?.ok === false && (
<span className="badge bg-danger-lt text-danger"> <span className="badge bg-danger-lt text-danger">
<IconCircleX size={14} /> Недоступен <IconCircleX size={14} /> Недоступен
</span> </span>
)} )}
</td> </td>
<td className="text-muted"> <td className="text-muted">
{status?.lastCheckTs {currentCheckingServerId === id
? formatRelative(status.lastCheckTs) ? '…'
: '—'} : status?.lastCheckTs
? formatRelative(status.lastCheckTs)
: '—'}
</td> </td>
</tr> </tr>
); );