(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(); }());