0.3.15-pre.011-fix.001

This commit is contained in:
2026-09-16 09:38:30 +02:00
parent a248be04ca
commit 2f587e22be
10 changed files with 266 additions and 15 deletions

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-raw-transaction-ingest-desk/frontend/ts/main.ts
// version: 7
// version: 8
import "bootstrap";
import ResizeObserver from "resize-observer-polyfill";
@@ -29,6 +29,7 @@ const viewTitles: Record<ViewId, string> = {
const activeRuntimes = new Map<string, RawIngestRouteRuntimeDto>();
const lastRuntimes = new Map<string, RawIngestRouteRuntimeDto>();
const routeMonitoring = new Map<string, RawIngestRouteMonitoringDto>();
const routeCards = new Map<string, HTMLElement>();
let routeInventory: RawIngestRouteInventoryDto | null = null;
let selectedProfileId: string | null = null;
let selectedMonitoringKey: string | null = null;
@@ -215,6 +216,7 @@ function renderSelectedProfile(): void {
text("#headerProfile", profile.profileId);
const routeRoot = document.querySelector<HTMLElement>("#routeFoundation");
if (routeRoot) {
routeCards.clear();
routeRoot.replaceChildren();
for (const route of profile.routes) {
const key = runtimeKey(profile.profileId, route.routeId);
@@ -246,6 +248,7 @@ function renderSelectedProfile(): void {
meta.className = "d-flex flex-wrap gap-2 mt-3 app-route-meta";
const state = document.createElement("span");
state.className = lifecycleBadgeClass(projectedState);
state.dataset.monitoringField = "state";
state.textContent = projectedState;
const network = document.createElement("span");
network.className = "badge text-bg-light border text-dark";
@@ -260,18 +263,23 @@ function renderSelectedProfile(): void {
summary.className = "app-route-monitoring-summary mt-3";
const health = document.createElement("span");
health.className = monitoring.health === "healthy" ? "badge text-bg-success" : "badge text-bg-warning";
health.dataset.monitoringField = "health";
health.textContent = `health: ${monitoring.health}`;
const activity = document.createElement("span");
activity.className = "badge text-bg-light border text-dark";
activity.dataset.monitoringField = "activity";
activity.textContent = `activity: ${monitoring.activity}`;
const persisted = document.createElement("span");
persisted.className = "badge text-bg-light border text-dark";
persisted.dataset.monitoringField = "persisted";
persisted.textContent = `persisted: ${monitoring.persistedTotal}`;
const sources = document.createElement("span");
sources.className = "badge text-bg-light border text-dark";
sources.dataset.monitoringField = "sources";
sources.textContent = `sources: ${monitoring.sourceActive}/${monitoring.sourceTotal}`;
const gaps = document.createElement("span");
gaps.className = monitoring.openGapCount === 0 ? "badge text-bg-light border text-dark" : "badge text-bg-warning";
gaps.dataset.monitoringField = "gaps";
gaps.textContent = `gaps: ${monitoring.openGapCount}`;
summary.append(health, activity, persisted, sources, gaps);
body.append(summary);
@@ -324,6 +332,7 @@ function renderSelectedProfile(): void {
body.append(actions);
}
card.append(body);
routeCards.set(key, card);
column.append(card);
routeRoot.append(column);
}
@@ -450,6 +459,34 @@ function renderMonitoringDetail(): void {
});
}
function renderRouteMonitoringCard(status: RawIngestRouteMonitoringDto): boolean {
const key = runtimeKey(status.profileId, status.routeId);
const card = routeCards.get(key);
if (!card) {
return false;
}
const field = (name: string): HTMLElement | null => card.querySelector<HTMLElement>(`[data-monitoring-field="${name}"]`);
const state = field("state");
const health = field("health");
const activity = field("activity");
const persisted = field("persisted");
const sources = field("sources");
const gaps = field("gaps");
if (!state || !health || !activity || !persisted || !sources || !gaps) {
return false;
}
state.className = lifecycleBadgeClass(status.state);
state.textContent = status.state;
health.className = status.health === "healthy" ? "badge text-bg-success" : "badge text-bg-warning";
health.textContent = `health: ${status.health}`;
activity.textContent = `activity: ${status.activity}`;
persisted.textContent = `persisted: ${status.persistedTotal}`;
sources.textContent = `sources: ${status.sourceActive}/${status.sourceTotal}`;
gaps.className = status.openGapCount === 0 ? "badge text-bg-light border text-dark" : "badge text-bg-warning";
gaps.textContent = `gaps: ${status.openGapCount}`;
return true;
}
function selectedCommitment(): RawIngestCommitment {
const selector = document.querySelector<HTMLSelectElement>("#routeCommitment");
return selector?.value === "finalized" ? "finalized" : "confirmed";
@@ -548,13 +585,21 @@ async function stopRoute(runtime: RawIngestRouteRuntimeDto): Promise<void> {
}
function applyRouteMonitoring(status: RawIngestRouteMonitoringDto, source: "event" | "startup" | "start" | "stop" | "user"): void {
const key = runtimeKey(status.profileId, status.routeId);
const previous = routeMonitoring.get(key) ?? null;
recordRouteMonitoring(status);
if (selectedMonitoringKey === null && status.profileId === selectedProfileId) {
selectedMonitoringKey = runtimeKey(status.profileId, status.routeId);
selectedMonitoringKey = key;
}
renderMultiRouteRuntime();
renderSelectedProfile();
const lifecycleChanged = previous === null || previous.state !== status.state;
if (lifecycleChanged || !renderRouteMonitoringCard(status)) {
renderSelectedProfile();
} else if (selectedMonitoringKey === key) {
renderMonitoringDetail();
}
frontendTrace("main", "Raw Transaction Ingest Desk route monitoring latest value applied", {
lifecycleChanged,
routeId: status.routeId,
sequence: status.sequence,
source,