217 lines
8.9 KiB
JavaScript
217 lines
8.9 KiB
JavaScript
(() => {
|
|
const menuController = window.WarpBoxUI?.bindMenuBar?.() || {
|
|
close() {
|
|
document.querySelectorAll(".menu-item.is-open").forEach((item) => {
|
|
item.classList.remove("is-open");
|
|
item.querySelector(".menu-button")?.setAttribute("aria-expanded", "false");
|
|
});
|
|
}
|
|
};
|
|
const toast = document.getElementById("toast");
|
|
const searchInput = document.getElementById("search-input");
|
|
const severityFilter = document.getElementById("severity-filter");
|
|
const statusFilter = document.getElementById("status-filter");
|
|
const sourceFilter = document.getElementById("source-filter");
|
|
const sortFilter = document.getElementById("sort-filter");
|
|
const alertsBody = document.getElementById("alerts-body");
|
|
const selectedCountEl = document.getElementById("selected-count");
|
|
const openCountEl = document.querySelector("[data-open-count]");
|
|
const highCountEl = document.querySelector("[data-high-count]");
|
|
const ackCountEl = document.querySelector("[data-ack-count]");
|
|
const closedCountEl = document.querySelector("[data-closed-count]");
|
|
const selectAll = document.getElementById("select-all");
|
|
|
|
const detailEls = {
|
|
title: document.getElementById("detail-title"),
|
|
severity: document.getElementById("detail-severity"),
|
|
status: document.getElementById("detail-status"),
|
|
code: document.getElementById("detail-code"),
|
|
trace: document.getElementById("detail-trace"),
|
|
time: document.getElementById("detail-time"),
|
|
description: document.getElementById("detail-description"),
|
|
metadata: document.getElementById("detail-metadata")
|
|
};
|
|
|
|
if (!alertsBody || !searchInput || !statusFilter || !selectedCountEl) return;
|
|
|
|
function showToast(message, type = "info", duration = 1800) {
|
|
if (window.WarpBoxUI) {
|
|
window.WarpBoxUI.toast(message, type, { target: toast, duration });
|
|
return;
|
|
}
|
|
if (!toast) return;
|
|
toast.textContent = message;
|
|
toast.classList.add("is-visible");
|
|
window.setTimeout(() => toast.classList.remove("is-visible"), duration);
|
|
}
|
|
|
|
function allRows() {
|
|
return Array.from(alertsBody.querySelectorAll("tr"));
|
|
}
|
|
|
|
function visibleRows() {
|
|
return allRows().filter((row) => row.style.display !== "none");
|
|
}
|
|
|
|
function selectedRows() {
|
|
return allRows().filter((row) => row.querySelector(".row-check")?.checked && row.style.display !== "none");
|
|
}
|
|
|
|
function updateSelectedCount() {
|
|
selectedCountEl.textContent = `Selected: ${selectedRows().length}`;
|
|
}
|
|
|
|
function updateSummaryCounts() {
|
|
const rows = visibleRows();
|
|
openCountEl.textContent = String(rows.filter((row) => row.dataset.status === "open").length);
|
|
highCountEl.textContent = String(rows.filter((row) => row.dataset.severity === "high" && row.dataset.status !== "closed").length);
|
|
ackCountEl.textContent = String(rows.filter((row) => row.dataset.status === "acked").length);
|
|
closedCountEl.textContent = String(rows.filter((row) => row.dataset.status === "closed").length);
|
|
}
|
|
|
|
function updateDetails(row) {
|
|
if (!row) return;
|
|
allRows().forEach((item) => item.classList.remove("is-selected"));
|
|
row.classList.add("is-selected");
|
|
detailEls.title.textContent = row.dataset.title || "";
|
|
detailEls.severity.textContent = row.dataset.severity || "";
|
|
detailEls.status.textContent = row.dataset.status || "";
|
|
detailEls.code.textContent = row.dataset.code || "";
|
|
detailEls.trace.textContent = row.dataset.trace || "";
|
|
detailEls.time.textContent = row.dataset.time || "";
|
|
detailEls.description.textContent = row.dataset.description || "";
|
|
try {
|
|
detailEls.metadata.textContent = JSON.stringify(JSON.parse(row.dataset.metadata || "{}"), null, 2);
|
|
} catch (_) {
|
|
detailEls.metadata.textContent = row.dataset.metadata || "{}";
|
|
}
|
|
}
|
|
|
|
function applyFilters() {
|
|
const search = searchInput.value.trim().toLowerCase();
|
|
const severity = severityFilter.value;
|
|
const status = statusFilter.value;
|
|
const group = sourceFilter.value;
|
|
|
|
allRows().forEach((row) => {
|
|
const haystack = [
|
|
row.dataset.title,
|
|
row.dataset.description,
|
|
row.dataset.code,
|
|
row.dataset.trace,
|
|
row.dataset.group
|
|
].join(" ").toLowerCase();
|
|
const matchesSearch = !search || haystack.includes(search);
|
|
const matchesSeverity = severity === "all" || row.dataset.severity === severity;
|
|
const matchesStatus = status === "all" || row.dataset.status === status;
|
|
const matchesGroup = group === "all" || row.dataset.group === group;
|
|
row.style.display = matchesSearch && matchesSeverity && matchesStatus && matchesGroup ? "" : "none";
|
|
});
|
|
|
|
const order = { high: 3, medium: 2, low: 1 };
|
|
visibleRows().sort((a, b) => {
|
|
if (sortFilter.value === "severity") return order[b.dataset.severity] - order[a.dataset.severity];
|
|
if (sortFilter.value === "oldest") return Number(a.dataset.id) - Number(b.dataset.id);
|
|
return Number(b.dataset.id) - Number(a.dataset.id);
|
|
}).forEach((row) => alertsBody.appendChild(row));
|
|
|
|
const selectedVisible = visibleRows().find((row) => row.classList.contains("is-selected"));
|
|
if (!selectedVisible && visibleRows()[0]) updateDetails(visibleRows()[0]);
|
|
updateSelectedCount();
|
|
updateSummaryCounts();
|
|
}
|
|
|
|
function setRowStatus(row, nextStatus) {
|
|
row.dataset.status = nextStatus;
|
|
const statusCell = row.children[3]?.querySelector(".alerts-pill");
|
|
if (!statusCell) return;
|
|
statusCell.className = `alerts-pill ${nextStatus}`;
|
|
statusCell.textContent = nextStatus;
|
|
}
|
|
|
|
function changeSelectedStatus(nextStatus) {
|
|
const rows = selectedRows();
|
|
if (!rows.length) {
|
|
showToast("Select one or more alerts first", "warning");
|
|
return;
|
|
}
|
|
|
|
rows.forEach((row) => {
|
|
setRowStatus(row, nextStatus);
|
|
row.querySelector(".row-check").checked = false;
|
|
});
|
|
if (selectAll) selectAll.checked = false;
|
|
updateSelectedCount();
|
|
updateSummaryCounts();
|
|
|
|
const currentRow = visibleRows().find((row) => row.classList.contains("is-selected")) || visibleRows()[0];
|
|
if (currentRow) updateDetails(currentRow);
|
|
showToast(nextStatus === "acked" ? "Selected alerts acknowledged" : "Selected alerts closed");
|
|
}
|
|
|
|
const commandMessages = {
|
|
refresh: "Alerts refreshed in mock view",
|
|
export: "Visible alerts exported in mock view",
|
|
"copy-meta": "Metadata copied in mock view",
|
|
"help-codes": "Each alert code maps to a unique trigger point and trace identifier.",
|
|
"help-meta": "Metadata explains why the alert happened and includes extra context."
|
|
};
|
|
|
|
function runCommand(command) {
|
|
switch (command) {
|
|
case "ack":
|
|
changeSelectedStatus("acked");
|
|
return;
|
|
case "close":
|
|
changeSelectedStatus("closed");
|
|
return;
|
|
case "open-only":
|
|
statusFilter.value = "open";
|
|
applyFilters();
|
|
showToast("Showing open alerts only");
|
|
return;
|
|
default:
|
|
showToast(commandMessages[command] || `Mock action: ${command}`);
|
|
}
|
|
}
|
|
|
|
[searchInput, severityFilter, statusFilter, sourceFilter, sortFilter].forEach((control) => {
|
|
control.addEventListener(control.tagName === "INPUT" ? "input" : "change", applyFilters);
|
|
});
|
|
|
|
allRows().forEach((row) => {
|
|
row.addEventListener("click", (event) => {
|
|
if (event.target.closest("button") || event.target.closest("input")) return;
|
|
updateDetails(row);
|
|
});
|
|
row.querySelector(".row-open")?.addEventListener("click", () => updateDetails(row));
|
|
row.querySelector(".row-check")?.addEventListener("change", updateSelectedCount);
|
|
});
|
|
|
|
selectAll?.addEventListener("change", () => {
|
|
visibleRows().forEach((row) => {
|
|
const checkbox = row.querySelector(".row-check");
|
|
if (checkbox) checkbox.checked = selectAll.checked;
|
|
});
|
|
updateSelectedCount();
|
|
});
|
|
|
|
document.querySelectorAll("[data-command]").forEach((button) => {
|
|
button.addEventListener("click", () => {
|
|
menuController.close();
|
|
runCommand(button.dataset.command);
|
|
});
|
|
});
|
|
|
|
document.addEventListener("keydown", (event) => {
|
|
if (event.key === "Escape") menuController.close();
|
|
if (event.key === "F5") {
|
|
event.preventDefault();
|
|
runCommand("refresh");
|
|
}
|
|
});
|
|
|
|
applyFilters();
|
|
updateDetails(allRows()[0]);
|
|
})();
|