156 lines
6.6 KiB
JavaScript
156 lines
6.6 KiB
JavaScript
(function () {
|
|
"use strict";
|
|
|
|
if (document.body.dataset.view !== "dashboard") return;
|
|
|
|
const ids = [
|
|
"dashboardLoading", "dashboardError", "dashboardErrorDetail",
|
|
"dashboardRetry", "dashboardEmpty", "dashboardContent",
|
|
"dashboardLoadStatus", "dashboardFleetTotal", "dashboardRuntimeOnline",
|
|
"dashboardRuntimeOffline", "dashboardRuntimeUnknown",
|
|
"dashboardLifecycleRepair", "dashboardLifecycleSpare",
|
|
"dashboardLifecycleRetired", "dashboardAttentionOffline",
|
|
"dashboardAttentionUnknown", "dashboardAttentionUnstable",
|
|
"dashboardAttentionRepair", "dashboardActiveRepairs",
|
|
"dashboardRepairStatuses", "dashboardSites", "dashboardRooms",
|
|
"dashboardPositions", "dashboardOccupied", "dashboardEmptyPositions",
|
|
"dashboardMappedMiners", "dashboardUnmappedMiners"
|
|
];
|
|
const el = {};
|
|
for (const id of ids) el[id] = document.getElementById(id);
|
|
let controller = null;
|
|
|
|
const STATUS_LABELS = {
|
|
OPEN: "Open",
|
|
IN_PROGRESS: "In progress",
|
|
WAITING_FOR_PARTS: "Waiting for parts",
|
|
READY_FOR_TEST: "Ready for test",
|
|
open: "Open",
|
|
diagnosing: "Diagnosing",
|
|
waiting_parts: "Waiting parts",
|
|
in_progress: "In progress",
|
|
testing: "Testing",
|
|
ready_to_shelf: "Ready to shelf",
|
|
needs_followup: "Needs follow-up",
|
|
repair: "In repair",
|
|
unknown: "Unknown"
|
|
};
|
|
|
|
function setCount(node, value) {
|
|
if (node) node.textContent = Number.isInteger(value) ? String(value) : "Unavailable";
|
|
}
|
|
|
|
function show(name) {
|
|
el.dashboardLoading.hidden = name !== "loading";
|
|
el.dashboardError.hidden = name !== "error";
|
|
el.dashboardEmpty.hidden = name !== "empty";
|
|
el.dashboardContent.hidden = name !== "ready";
|
|
}
|
|
|
|
function statusLabel(value) {
|
|
if (Object.prototype.hasOwnProperty.call(STATUS_LABELS, value)) {
|
|
return STATUS_LABELS[value];
|
|
}
|
|
return String(value || "Unknown").replaceAll("_", " ");
|
|
}
|
|
|
|
function renderFleet(fleet) {
|
|
if (!fleet || !el.dashboardFleetTotal) return;
|
|
setCount(el.dashboardFleetTotal, fleet.total);
|
|
setCount(el.dashboardRuntimeOnline, fleet.runtime_online);
|
|
setCount(el.dashboardRuntimeOffline, fleet.runtime_offline);
|
|
setCount(el.dashboardRuntimeUnknown, fleet.runtime_unknown);
|
|
setCount(el.dashboardLifecycleRepair, fleet.lifecycle_in_repair);
|
|
setCount(el.dashboardLifecycleSpare, fleet.lifecycle_spare);
|
|
setCount(el.dashboardLifecycleRetired, fleet.lifecycle_retired);
|
|
const attention = fleet.attention || {};
|
|
setCount(el.dashboardAttentionOffline, attention.offline);
|
|
setCount(el.dashboardAttentionUnknown, attention.unknown);
|
|
setCount(el.dashboardAttentionUnstable, attention.unstable);
|
|
setCount(el.dashboardAttentionRepair, attention.repair);
|
|
}
|
|
|
|
function renderRepairs(repairs) {
|
|
if (!repairs || !el.dashboardActiveRepairs) return;
|
|
setCount(el.dashboardActiveRepairs, repairs.active);
|
|
const entries = Object.entries(repairs.status_counts || {}).sort(function (left, right) {
|
|
return statusLabel(left[0]).localeCompare(statusLabel(right[0]));
|
|
});
|
|
el.dashboardRepairStatuses.replaceChildren();
|
|
if (!entries.length) {
|
|
const row = document.createElement("div");
|
|
const term = document.createElement("dt");
|
|
const value = document.createElement("dd");
|
|
term.textContent = "No active repair statuses";
|
|
value.textContent = "0";
|
|
row.append(term, value);
|
|
el.dashboardRepairStatuses.appendChild(row);
|
|
return;
|
|
}
|
|
for (const [status, count] of entries) {
|
|
const row = document.createElement("div");
|
|
const term = document.createElement("dt");
|
|
const link = document.createElement("a");
|
|
const value = document.createElement("dd");
|
|
const params = new URLSearchParams({ scope: "active", status: status });
|
|
link.href = "/operations/repairs?" + params.toString();
|
|
link.textContent = statusLabel(status);
|
|
term.appendChild(link);
|
|
value.textContent = Number.isInteger(count) ? String(count) : "Unavailable";
|
|
row.append(term, value);
|
|
el.dashboardRepairStatuses.appendChild(row);
|
|
}
|
|
}
|
|
|
|
function renderLocations(locations) {
|
|
if (!locations || !el.dashboardSites) return;
|
|
setCount(el.dashboardSites, locations.sites);
|
|
setCount(el.dashboardRooms, locations.rooms);
|
|
setCount(el.dashboardPositions, locations.positions);
|
|
setCount(el.dashboardOccupied, locations.occupied_positions);
|
|
setCount(el.dashboardEmptyPositions, locations.empty_positions);
|
|
setCount(el.dashboardMappedMiners, locations.mapped_miners);
|
|
setCount(el.dashboardUnmappedMiners, locations.unmapped_miners);
|
|
}
|
|
|
|
async function loadOverview() {
|
|
if (controller) controller.abort();
|
|
controller = new AbortController();
|
|
show("loading");
|
|
try {
|
|
const response = await fetch("/api/dashboard/overview", {
|
|
credentials: "same-origin",
|
|
headers: { Accept: "application/json" },
|
|
signal: controller.signal
|
|
});
|
|
if (response.status === 401) {
|
|
window.location.replace("/login?next=" + encodeURIComponent("/dashboard"));
|
|
return;
|
|
}
|
|
if (!response.ok) throw new Error("The overview service returned " + response.status + ".");
|
|
const payload = await response.json();
|
|
const data = payload && payload.data ? payload.data : {};
|
|
const available = payload && payload.meta ? payload.meta.available_sections || [] : [];
|
|
if (!available.length) {
|
|
show("empty");
|
|
return;
|
|
}
|
|
renderFleet(data.fleet);
|
|
renderRepairs(data.repairs);
|
|
renderLocations(data.locations);
|
|
const generated = new Date(data.generated_at);
|
|
el.dashboardLoadStatus.textContent = Number.isNaN(generated.valueOf())
|
|
? "Operational summary loaded"
|
|
: "Operational summary generated " + generated.toLocaleString();
|
|
show("ready");
|
|
} catch (error) {
|
|
if (error.name === "AbortError") return;
|
|
el.dashboardErrorDetail.textContent = error.message || "The operational overview could not be loaded.";
|
|
show("error");
|
|
}
|
|
}
|
|
|
|
el.dashboardRetry.addEventListener("click", loadOverview);
|
|
loadOverview();
|
|
}());
|