Skip to content

Commit 939525f

Browse files
committed
rPing: Add refresh button and update indicator for device status management
Signed-off-by: KARTHIK LAL dev@karthiklal.in
1 parent fa6e617 commit 939525f

File tree

3 files changed

+234
-35
lines changed

3 files changed

+234
-35
lines changed

static/index.html

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,22 @@
1313
<body>
1414
<div class="app-container">
1515
<header class="header">
16-
<div class="logo">
16+
<div class="logo">
1717
<a href="javascript:void(0)" onclick="reloadCurrentPage()" class="logo-link">
1818
<img src="logo-light.png" alt="RustPing Logo" id="logoImage">
1919
<span class="app-name">RustPing</span>
20-
</a>
20+
</a>
2121
</div>
2222
<nav class="navigation">
2323
<input type="text" id="filterInput" placeholder="Filter by name/IP..." class="filter-input">
2424
<button id="filterButton" class="nav-button"><i class="fas fa-filter"></i></button>
2525
<button id="clearFilterButton" class="nav-button"><i class="fas fa-times"></i></button>
26+
<button id="refreshButton" class="nav-button" title="Refresh Device Status">
27+
<i class="fas fa-sync-alt"></i> Refresh
28+
</button>
29+
<div id="updateIndicator" class="update-indicator">
30+
<i class="fas fa-circle-notch fa-spin"></i> Updating...
31+
</div>
2632
<a href="log_view.html" class="nav-link" id="logViewButton"><i class="fas fa-file-alt"></i> Live Logs</a>
2733
<a href="failed_logs.html" class="nav-link" id="failedLogsButton"><i class="fas fa-exclamation-triangle"></i> Failed Logs</a>
2834
<a href="manage-devices.html" class="nav-link" id="manageDevicesButton"><i class="fas fa-server"></i> Manage Devices</a>

static/script.js

Lines changed: 125 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,32 @@ let categoryHealthData = {
2525
]
2626
};
2727

28+
// Device Status Management
29+
let updateInterval = 30000; // 30 seconds
30+
let isUpdating = false;
31+
let updateIndicator = document.getElementById('updateIndicator');
32+
let refreshButton = document.getElementById('refreshButton');
33+
34+
function showUpdateIndicator() {
35+
if (updateIndicator) {
36+
updateIndicator.style.display = 'inline-flex';
37+
if (refreshButton) {
38+
refreshButton.classList.add('updating');
39+
refreshButton.disabled = true;
40+
}
41+
}
42+
}
43+
44+
function hideUpdateIndicator() {
45+
if (updateIndicator) {
46+
updateIndicator.style.display = 'none';
47+
if (refreshButton) {
48+
refreshButton.classList.remove('updating');
49+
refreshButton.disabled = false;
50+
}
51+
}
52+
}
53+
2854
async function fetchDevices() {
2955
if (isFetching) return;
3056
isFetching = true;
@@ -581,48 +607,114 @@ function resetPassword() {
581607
}
582608
}
583609

584-
async function updateDeviceStatuses() {
585-
try {
586-
const response = await fetch('/devices');
587-
if (!response.ok) throw new Error('Failed to fetch devices');
588-
589-
const devices = await response.json();
590-
let statusChanged = false;
610+
async function fetchDeviceStatuses() {
611+
if (isUpdating) return;
612+
isUpdating = true;
613+
showUpdateIndicator();
614+
615+
try {
616+
const response = await fetch('/api/devices/status');
617+
if (!response.ok) throw new Error('Failed to fetch device statuses');
618+
const newStatuses = await response.json();
619+
620+
// Only update the UI if there are significant changes
621+
let hasChanges = false;
622+
for (const [deviceName, status] of Object.entries(newStatuses)) {
623+
const lastStatus = deviceStatuses.get(deviceName);
624+
const hasSignificantChange = !lastStatus ||
625+
lastStatus.ping_status !== status.ping_status ||
626+
lastStatus.http_status !== status.http_status ||
627+
lastStatus.down !== status.down;
628+
629+
if (hasSignificantChange) {
630+
hasChanges = true;
631+
deviceStatuses.set(deviceName, status);
632+
updateDeviceRow(deviceName, status);
633+
}
634+
}
591635

592-
devices.forEach(device => {
593-
const currentStatus = deviceStatuses.get(device.ip);
636+
// Only update charts if there are significant changes
637+
if (hasChanges) {
638+
updateBandwidthChart(devicesData);
639+
updateCategoryHealthChart(devicesData);
640+
}
641+
} catch (error) {
642+
console.error('Error fetching device statuses:', error);
643+
} finally {
644+
isUpdating = false;
645+
hideUpdateIndicator();
646+
}
647+
}
594648

595-
if (!currentStatus ||
596-
currentStatus.ping_status !== device.ping_status ||
597-
currentStatus.http_status !== device.http_status ||
598-
currentStatus.bandwidth_usage !== device.bandwidth_usage) {
649+
function updateDeviceRow(deviceName, status) {
650+
const row = document.querySelector(`tr[data-device="${deviceName}"]`);
651+
if (!row) return;
652+
653+
// Update status indicators
654+
const pingStatus = row.querySelector('.ping-status');
655+
const httpStatus = row.querySelector('.http-status');
656+
const bandwidthStatus = row.querySelector('.bandwidth-status');
657+
658+
// Only update if there's a significant change
659+
if (pingStatus) {
660+
const currentPingStatus = pingStatus.textContent.trim();
661+
if (currentPingStatus !== status.ping_status) {
662+
pingStatus.textContent = status.ping_status;
663+
pingStatus.className = `ping-status status-indicator ${status.ping_status.toLowerCase() === 'fail' ? 'status-fail' : 'status-success'}`;
664+
}
665+
}
599666

600-
deviceStatuses.set(device.ip, {
601-
ping_status: device.ping_status,
602-
http_status: device.http_status,
603-
bandwidth_usage: device.bandwidth_usage,
604-
lastChange: Date.now()
605-
});
606-
statusChanged = true;
607-
}
608-
});
667+
if (httpStatus) {
668+
const currentHttpStatus = httpStatus.textContent.trim();
669+
if (currentHttpStatus !== status.http_status) {
670+
httpStatus.textContent = status.http_status;
671+
httpStatus.className = `http-status status-indicator ${status.http_status.toLowerCase() === 'fail' ? 'status-fail' : 'status-success'}`;
672+
}
673+
}
609674

610-
// Only update UI if status actually changed
611-
if (statusChanged) {
612-
renderData(devices);
675+
if (bandwidthStatus) {
676+
const currentBandwidth = bandwidthStatus.textContent.trim();
677+
if (currentBandwidth !== status.bandwidth_usage) {
678+
bandwidthStatus.textContent = status.bandwidth_usage;
679+
}
613680
}
614-
} catch (error) {
615-
console.error('Error updating device statuses:', error);
616-
}
681+
682+
// Update row status
683+
row.classList.toggle('device-down', status.down);
617684
}
618685

619-
// Update status check interval
620-
const STATUS_CHECK_INTERVAL = 5000; // 5 seconds
621-
setInterval(updateDeviceStatuses, STATUS_CHECK_INTERVAL);
686+
// Initialize device statuses
687+
async function initializeDeviceStatuses() {
688+
try {
689+
const response = await fetch('/api/devices/status');
690+
if (!response.ok) throw new Error('Failed to fetch initial device statuses');
691+
const statuses = await response.json();
692+
693+
for (const [deviceName, status] of Object.entries(statuses)) {
694+
deviceStatuses.set(deviceName, status);
695+
updateDeviceRow(deviceName, status);
696+
}
697+
} catch (error) {
698+
console.error('Error initializing device statuses:', error);
699+
}
700+
}
622701

623-
// Initial load
702+
// Event Listeners
624703
document.addEventListener('DOMContentLoaded', () => {
625-
updateDeviceStatuses();
704+
// Initialize device statuses
705+
initializeDeviceStatuses();
706+
707+
// Set up automatic updates
708+
setInterval(fetchDeviceStatuses, updateInterval);
709+
710+
// Add click handler for refresh button
711+
if (refreshButton) {
712+
refreshButton.addEventListener('click', () => {
713+
if (!isUpdating) {
714+
fetchDeviceStatuses();
715+
}
716+
});
717+
}
626718
});
627719

628720
function initCategoryHealthChart() {

static/styles.css

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,4 +1066,105 @@ td.status-warning {
10661066

10671067
.icon-space {
10681068
padding-right: 1.2rem;
1069+
}
1070+
1071+
/* Update Indicator Styles */
1072+
.update-indicator {
1073+
display: none;
1074+
align-items: center;
1075+
gap: 8px;
1076+
color: var(--text-secondary);
1077+
font-size: 0.9em;
1078+
margin-left: 10px;
1079+
padding: 0.375rem 0.75rem;
1080+
background: var(--card-background-light);
1081+
border: 1px solid var(--border-color-light);
1082+
border-radius: var(--border-radius);
1083+
box-shadow: var(--box-shadow);
1084+
}
1085+
1086+
.dark-mode .update-indicator {
1087+
background: var(--card-background-dark);
1088+
border-color: var(--border-color-dark);
1089+
}
1090+
1091+
.update-indicator i {
1092+
color: var(--accent-color);
1093+
}
1094+
1095+
/* Refresh Button Styles */
1096+
#refreshButton {
1097+
display: inline-flex;
1098+
align-items: center;
1099+
gap: 8px;
1100+
padding: 0.375rem 0.75rem;
1101+
transition: all 0.2s ease;
1102+
background-color: var(--card-background-light);
1103+
border: 1px solid var(--border-color-light);
1104+
color: var(--text-color-light);
1105+
}
1106+
1107+
.dark-mode #refreshButton {
1108+
background-color: var(--card-background-dark);
1109+
border-color: var(--border-color-dark);
1110+
color: var(--text-color-dark);
1111+
}
1112+
1113+
#refreshButton i {
1114+
font-size: 1.1em;
1115+
transition: transform 0.2s ease;
1116+
}
1117+
1118+
#refreshButton:hover:not(:disabled) {
1119+
background-color: var(--table-row-hover-light);
1120+
border-color: var(--accent-color);
1121+
color: var(--accent-color);
1122+
}
1123+
1124+
.dark-mode #refreshButton:hover:not(:disabled) {
1125+
background-color: var(--table-row-hover-dark);
1126+
}
1127+
1128+
#refreshButton.updating {
1129+
pointer-events: none;
1130+
opacity: 0.7;
1131+
background-color: var(--table-row-hover-light);
1132+
}
1133+
1134+
.dark-mode #refreshButton.updating {
1135+
background-color: var(--table-row-hover-dark);
1136+
}
1137+
1138+
#refreshButton:disabled {
1139+
cursor: not-allowed;
1140+
opacity: 0.7;
1141+
}
1142+
1143+
/* Device Status Styles */
1144+
.device-row {
1145+
transition: background-color 0.3s ease;
1146+
}
1147+
1148+
.device-row.device-down {
1149+
background-color: rgba(207, 34, 46, 0.1);
1150+
}
1151+
1152+
.status-indicator {
1153+
display: inline-flex;
1154+
align-items: center;
1155+
gap: 6px;
1156+
padding: 4px 8px;
1157+
border-radius: 4px;
1158+
font-weight: 500;
1159+
transition: all 0.2s ease;
1160+
}
1161+
1162+
.status-success {
1163+
color: var(--success-color);
1164+
background-color: rgba(45, 164, 78, 0.1);
1165+
}
1166+
1167+
.status-fail {
1168+
color: var(--error-color);
1169+
background-color: rgba(207, 34, 46, 0.1);
10691170
}

0 commit comments

Comments
 (0)