172 lines
5.0 KiB
JavaScript
172 lines
5.0 KiB
JavaScript
function updateTime() {
|
||
const now = new Date();
|
||
const timeString = now
|
||
.toLocaleString("en-US", {
|
||
month: "2-digit",
|
||
day: "2-digit",
|
||
year: "numeric",
|
||
hour: "2-digit",
|
||
minute: "2-digit",
|
||
second: "2-digit",
|
||
fractionalSecondDigits: 3,
|
||
hour12: true,
|
||
})
|
||
.toUpperCase();
|
||
document.getElementById("current-time").textContent = timeString;
|
||
}
|
||
|
||
updateTime();
|
||
setInterval(updateTime, 1);
|
||
|
||
// projects
|
||
const projectItems = document.querySelectorAll(".project-item");
|
||
projectItems.forEach((item) => {
|
||
item.addEventListener("click", function () {
|
||
const content = this.querySelector(".project-content");
|
||
const icon = this.querySelector(".expand-icon");
|
||
|
||
// close other expanded items
|
||
projectItems.forEach((otherItem) => {
|
||
if (otherItem !== item) {
|
||
otherItem
|
||
.querySelector(".project-content")
|
||
.classList.remove("expanded");
|
||
otherItem.querySelector(".expand-icon").textContent = "+";
|
||
}
|
||
});
|
||
content.classList.toggle("expanded");
|
||
icon.textContent = content.classList.contains("expanded") ? "−" : "+";
|
||
});
|
||
});
|
||
|
||
// status updater
|
||
function formatLatency(latency) {
|
||
return (Math.round((latency / 1000) * 10) / 10).toFixed(1);
|
||
}
|
||
|
||
function formatUptime(uptime) {
|
||
return uptime.toFixed(2);
|
||
}
|
||
|
||
function getStatusClass(service) {
|
||
if (!service.online) return "status-offline";
|
||
if (service.latency > 500000) return "status-warning";
|
||
return "status-online";
|
||
}
|
||
|
||
function getGroupName(groupId) {
|
||
const groupNames = {
|
||
4: "INFRASTRUCTURE",
|
||
5: "SERVICES",
|
||
6: "API",
|
||
};
|
||
return groupNames[groupId] || `GROUP ${groupId}`;
|
||
}
|
||
|
||
function createStatusHTML(services) {
|
||
// Group services by group_id
|
||
const groups = services.reduce((acc, service) => {
|
||
const groupId = service.group_id;
|
||
if (!acc[groupId]) acc[groupId] = [];
|
||
acc[groupId].push(service);
|
||
return acc;
|
||
}, {});
|
||
|
||
return Object.entries(groups)
|
||
.map(
|
||
([groupId, groupServices]) => `
|
||
<div class="status-group">
|
||
<div class="subsection-title">${getGroupName(parseInt(groupId))}</div>
|
||
${groupServices
|
||
.map(
|
||
(service) => `
|
||
<div class="status-item">
|
||
<div class="entry" data-service="${service.permalink}">
|
||
<span class="status-indicator ${getStatusClass(service)}"></span>
|
||
<span class="service-name">${service.name.toUpperCase()}</span>
|
||
<span class="service-status">${service.online ? "OPERATIONAL" : "OFFLINE"}</span>
|
||
</div>
|
||
<div class="status-details">
|
||
<div class="status-metric">Latency: ${formatLatency(service.latency)}ms</div>
|
||
<div class="status-metric">Uptime 24h: ${formatUptime(service.online_24_hours)}%</div>
|
||
<div class="status-metric">7-day Uptime: ${formatUptime(service.online_7_days)}%</div>
|
||
</div>
|
||
</div>
|
||
`,
|
||
)
|
||
.join("")}
|
||
</div>
|
||
`,
|
||
)
|
||
.join("");
|
||
}
|
||
|
||
function updateStatus() {
|
||
const statusContainer = document.getElementById("status-container");
|
||
|
||
fetch("https://status.elia.network/api/services")
|
||
.then((response) => response.json())
|
||
.then((data) => {
|
||
statusContainer.innerHTML = createStatusHTML(data);
|
||
document.querySelectorAll(".status-item .entry").forEach((item) => {
|
||
item.addEventListener("click", function () {
|
||
const details = this.nextElementSibling;
|
||
details.classList.toggle("expanded");
|
||
});
|
||
});
|
||
})
|
||
.catch((error) => {
|
||
statusContainer.innerHTML = `
|
||
<div class="error-message">
|
||
UNABLE TO FETCH STATUS DATA
|
||
<div style="font-size: 0.8em; margin-top: 5px;">
|
||
${error.message}
|
||
</div>
|
||
</div>
|
||
`;
|
||
});
|
||
}
|
||
|
||
// initial update
|
||
updateStatus();
|
||
|
||
// update every minute
|
||
setInterval(updateStatus, 60000);
|
||
// status details toggle
|
||
document.querySelectorAll(".status-item .entry").forEach((item) => {
|
||
const details = item.nextElementSibling;
|
||
if (details && details.classList.contains("status-details")) {
|
||
item.addEventListener("click", function () {
|
||
details.classList.toggle("expanded");
|
||
});
|
||
}
|
||
});
|
||
|
||
// navigation
|
||
document.querySelectorAll('a[href^="#"]').forEach((anchor) => {
|
||
anchor.addEventListener("click", function (e) {
|
||
e.preventDefault();
|
||
document.querySelector(this.getAttribute("href")).scrollIntoView({
|
||
behavior: "smooth",
|
||
});
|
||
});
|
||
});
|
||
|
||
// update active nav icon
|
||
window.addEventListener("scroll", () => {
|
||
let current = "";
|
||
document.querySelectorAll("section").forEach((section) => {
|
||
const sectionTop = section.offsetTop;
|
||
if (scrollY >= sectionTop - 60) {
|
||
current = section.getAttribute("id");
|
||
}
|
||
});
|
||
|
||
document.querySelectorAll(".nav-icon").forEach((icon) => {
|
||
icon.classList.remove("active");
|
||
if (icon.getAttribute("href") === `#${current}`) {
|
||
icon.classList.add("active");
|
||
}
|
||
});
|
||
});
|