Files
pikasTech-HWLAB/web/hwlab-cloud-web/app.mjs
T
2026-05-21 18:19:16 +00:00

149 lines
5.2 KiB
JavaScript

import { gateSummary } from "./gate-summary.mjs";
const status = document.querySelector("#status");
const metaGrid = document.querySelector("#meta-grid");
const milestoneGrid = document.querySelector("#milestone-grid");
const healthBody = document.querySelector("#health-body");
const artifactBody = document.querySelector("#artifact-body");
const topologyGrid = document.querySelector("#topology-grid");
const agentGrid = document.querySelector("#agent-grid");
const evidenceBody = document.querySelector("#evidence-body");
const blockerList = document.querySelector("#blocker-list");
const commandList = document.querySelector("#command-list");
function appendText(parent, tagName, text, className) {
const element = document.createElement(tagName);
element.textContent = text;
if (className) {
element.className = className;
}
parent.append(element);
return element;
}
function appendMetricGrid(parent, pairs) {
for (const [label, value] of pairs) {
const item = document.createElement("div");
item.className = "metric";
appendText(item, "span", label, "metric-label");
appendText(item, "strong", String(value ?? "n/a"), "metric-value");
parent.append(item);
}
}
function badge(text, tone = text) {
const span = document.createElement("span");
span.className = `badge tone-${String(tone).replace(/[^a-z0-9_-]/gi, "-").toLowerCase()}`;
span.textContent = text;
return span;
}
function cell(text, className) {
const td = document.createElement("td");
td.textContent = text ?? "n/a";
if (className) {
td.className = className;
}
return td;
}
status.replaceChildren(badge(gateSummary.gateStatus, gateSummary.gateStatus));
appendMetricGrid(metaGrid, [
["Environment", gateSummary.environment],
["Endpoint", gateSummary.endpoint],
["Namespace", gateSummary.namespace],
["Report commit", gateSummary.reportCommitId],
["Gate report", gateSummary.generatedFrom.gateReport],
["M5 plan", gateSummary.generatedFrom.m5Plan]
]);
for (const milestone of gateSummary.milestones) {
const item = document.createElement("article");
item.className = "milestone";
const head = document.createElement("div");
head.className = "milestone-head";
appendText(head, "strong", milestone.id);
head.append(badge(milestone.status, milestone.status));
item.append(head);
appendText(item, "p", milestone.summary);
appendText(item, "small", `${milestone.commandCount} commands / ${milestone.evidenceCount} evidence lines`);
milestoneGrid.append(item);
}
for (const row of gateSummary.health) {
const tr = document.createElement("tr");
tr.append(cell(row.component));
tr.append(cell(row.serviceId, "mono"));
const statusCell = document.createElement("td");
statusCell.append(badge(row.status, row.status));
tr.append(statusCell);
tr.append(cell(row.observedBy));
tr.append(cell(row.endpoint ?? gateSummary.endpoint, "mono wrap"));
healthBody.append(tr);
}
for (const artifact of gateSummary.artifacts) {
const tr = document.createElement("tr");
tr.append(cell(artifact.serviceId, "mono"));
tr.append(cell(artifact.commitId, "mono"));
tr.append(cell(`${artifact.image}:${artifact.tag}`, "mono wrap"));
tr.append(cell(artifact.digest, "mono wrap"));
tr.append(cell(artifact.healthTimestamp, "mono"));
artifactBody.append(tr);
}
appendMetricGrid(topologyGrid, [
["Project", gateSummary.topology.projectId],
["Gateways", gateSummary.gatewaySessionCount],
["Box resources", gateSummary.boxResourceCount],
["Patch panel", gateSummary.topology.patchPanel.patchPanelStatusId],
["Active links", gateSummary.topology.patchPanel.activeConnectionCount],
[
"Connection",
gateSummary.topology.patchPanel.activeConnections
.map((link) => `${link.fromResourceId}:${link.fromPort} -> ${link.toResourceId}:${link.toPort}`)
.join(", ")
]
]);
appendMetricGrid(agentGrid, [
["Agent session", gateSummary.agent.agentSessionId],
["Agent service", gateSummary.agent.agentServiceId],
["Worker session", gateSummary.agent.workerSessionId],
["Worker service", gateSummary.agent.workerServiceId],
["Trace events", gateSummary.traceCount],
["Audit events", gateSummary.auditCount],
["Cleanup records", gateSummary.cleanupCount],
["Cleanup id", gateSummary.agent.cleanupId]
]);
for (const record of gateSummary.evidenceRecords) {
const tr = document.createElement("tr");
tr.append(cell(record.evidenceId, "mono"));
tr.append(cell(record.operationId, "mono"));
tr.append(cell(record.serviceId, "mono"));
tr.append(cell(record.uri, "mono wrap"));
tr.append(cell(record.sha256, "mono wrap"));
evidenceBody.append(tr);
}
for (const blocker of gateSummary.blockers) {
const li = document.createElement("li");
const head = document.createElement("div");
head.className = "blocker-head";
head.append(badge(blocker.type, "blocked"));
appendText(head, "strong", blocker.scope);
li.append(head);
appendText(li, "p", blocker.summary);
blockerList.append(li);
}
for (const command of [
"node tools/hwlab-cli/bin/hwlab-cli.mjs test e2e --env dev --mvp",
"node tools/hwlab-cli/bin/hwlab-cli.mjs test e2e --env dev --mvp --live --confirm-dev --confirmed-non-production",
"node scripts/m5-mvp-e2e-dry-run.mjs"
]) {
appendText(commandList, "li", command, "mono");
}