Přidáno:
nová stránka /portal/incidents s title CSBot Services Portal - Incidenty menu Provoz -> Incidenty aktivní incidenty a historie posledních 100 incidentů barevné stavy: open červeně, resolved zeleně karta Aktivní incidenty a seznam Poslední incidenty na přehledu realtime napojení přes existující operations-live.js a snapshot sekce Incidenty služby v detailu služby, posledních 20 incidentů DB helpery pro service_incidents migrace/indexy pro lokální prostředí, kde tabulka ještě neexistuje
This commit is contained in:
@@ -59,6 +59,32 @@
|
||||
return `<span class="${className}">${escapeHtml(labels[normalized] || value)}</span>`;
|
||||
};
|
||||
|
||||
const incidentBadge = (status) => {
|
||||
const value = String(status || "");
|
||||
const normalized = value.toLowerCase();
|
||||
const labels = {
|
||||
open: "otevřený",
|
||||
resolved: "vyřešený",
|
||||
};
|
||||
let className = "pill pill-muted";
|
||||
if (normalized === "open") className = "pill pill-danger";
|
||||
if (normalized === "resolved") className = "pill pill-success";
|
||||
return `<span class="${className}">${escapeHtml(labels[normalized] || value)}</span>`;
|
||||
};
|
||||
|
||||
const formatDuration = (seconds) => {
|
||||
if (seconds === null || seconds === undefined || seconds === "") {
|
||||
return "";
|
||||
}
|
||||
const total = Math.max(0, Number.parseInt(seconds, 10) || 0);
|
||||
const hours = Math.floor(total / 3600);
|
||||
const minutes = Math.floor((total % 3600) / 60);
|
||||
const secs = total % 60;
|
||||
if (hours) return `${hours} h ${minutes} min`;
|
||||
if (minutes) return `${minutes} min ${secs} s`;
|
||||
return `${secs} s`;
|
||||
};
|
||||
|
||||
const setText = (selector, value) => {
|
||||
document.querySelectorAll(selector).forEach((el) => {
|
||||
el.textContent = value ?? 0;
|
||||
@@ -145,6 +171,39 @@
|
||||
return rows || '<tr><td colspan="5">Žádné služby nevyžadují pozornost.</td></tr>';
|
||||
};
|
||||
|
||||
const renderOpenIncidents = (items) => {
|
||||
const rows = (items || []).map((item) => {
|
||||
const serviceName = item.service_name || item.service_id || "";
|
||||
const duration = formatDuration(item.duration_seconds) || "probíhá";
|
||||
return `
|
||||
<tr>
|
||||
<td><a href="/portal/apps/${encodeURIComponent(item.service_id || "")}">${escapeHtml(serviceName)}</a></td>
|
||||
<td>${escapeHtml(item.title)}</td>
|
||||
<td>${escapeHtml(item.started_at)}</td>
|
||||
<td>${escapeHtml(duration)}</td>
|
||||
</tr>
|
||||
`;
|
||||
}).join("");
|
||||
return rows || '<tr><td colspan="4">Žádné aktivní incidenty.</td></tr>';
|
||||
};
|
||||
|
||||
const renderIncidents = (items) => {
|
||||
const rows = (items || []).map((item) => {
|
||||
const serviceName = item.service_name || item.service_id || "";
|
||||
return `
|
||||
<tr>
|
||||
<td><a href="/portal/apps/${encodeURIComponent(item.service_id || "")}">${escapeHtml(serviceName)}</a></td>
|
||||
<td>${escapeHtml(item.title)}</td>
|
||||
<td>${escapeHtml(item.started_at)}</td>
|
||||
<td>${escapeHtml(item.ended_at)}</td>
|
||||
<td>${escapeHtml(formatDuration(item.duration_seconds))}</td>
|
||||
<td>${incidentBadge(item.status)}</td>
|
||||
</tr>
|
||||
`;
|
||||
}).join("");
|
||||
return rows || '<tr><td colspan="6">Zatím nejsou evidované žádné incidenty.</td></tr>';
|
||||
};
|
||||
|
||||
const renderSnapshot = (snapshot) => {
|
||||
setText("[data-live-count='jobs.queued']", snapshot.jobs?.queued);
|
||||
setText("[data-live-count='jobs.running']", snapshot.jobs?.running);
|
||||
@@ -155,6 +214,7 @@
|
||||
setText("[data-live-count='health.healthy']", snapshot.health?.healthy);
|
||||
setText("[data-live-count='health.unhealthy']", snapshot.health?.unhealthy);
|
||||
setText("[data-live-count='health.unreachable']", snapshot.health?.unreachable);
|
||||
setText("[data-live-count='incidents.open']", snapshot.incidents?.open);
|
||||
setText("[data-live-count='workers.online']", snapshot.workers?.online);
|
||||
setText("[data-live-count='workers.offline']", snapshot.workers?.offline);
|
||||
setText("[data-live-count='workers.total']", (snapshot.workers?.online || 0) + (snapshot.workers?.offline || 0));
|
||||
@@ -170,6 +230,8 @@
|
||||
document.querySelectorAll("[data-live-table='deployments.failed']").forEach((el) => { el.innerHTML = renderDeployments(snapshot.deployments?.recent_failed); });
|
||||
document.querySelectorAll("[data-live-table='audit.recent']").forEach((el) => { el.innerHTML = renderAudit(snapshot.audit?.recent); });
|
||||
document.querySelectorAll("[data-live-table='health.problems']").forEach((el) => { el.innerHTML = renderHealthProblems(snapshot.health?.problems); });
|
||||
document.querySelectorAll("[data-live-table='incidents.active']").forEach((el) => { el.innerHTML = renderOpenIncidents(snapshot.incidents?.active); });
|
||||
document.querySelectorAll("[data-live-table='incidents.recent']").forEach((el) => { el.innerHTML = renderIncidents(snapshot.incidents?.recent); });
|
||||
|
||||
(snapshot.workers?.recent || []).forEach((worker) => {
|
||||
const row = Array.from(document.querySelectorAll("tr[data-worker-id]"))
|
||||
|
||||
Reference in New Issue
Block a user