// file: crates/ksp-app-solprices-desk/frontend/ts/main.ts // version: 4 import "bootstrap"; import ResizeObserver from "resize-observer-polyfill"; import "simplebar"; import { getCurrentWindow } from "@tauri-apps/api/window"; import type { MarketPriceRuntimeStatusDto } from "./bindings/ksp_app_solprices_desk/dto_common/MarketPriceRuntimeStatusDto.ts"; import type { MarketPriceProviderRowDto } from "./bindings/ksp_app_solprices_desk/market_price_runtime/MarketPriceProviderRowDto.ts"; import { frontendDebug, frontendInfo, frontendTrace, frontendWarn, installFrontendConsoleBridge } from "./frontend_log"; import { invokeKsp } from "./invoke"; (window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver; installFrontendConsoleBridge("main"); type ViewId = "prices" | "diagnostics"; const viewTitles: Record = { prices: "Prices", diagnostics: "Diagnostics", }; function isViewId(value: string | undefined): value is ViewId { return value === "prices" || value === "diagnostics"; } function activateView(viewId: ViewId, source: "startup" | "user"): void { document.querySelectorAll("[data-view-panel]").forEach(panel => { panel.hidden = panel.dataset.viewPanel !== viewId; }); document.querySelectorAll("[data-view]").forEach(button => { button.classList.toggle("active", button.dataset.view === viewId); }); const header = document.querySelector("#headerViewTitle"); if (header) { header.textContent = viewTitles[viewId]; } document.title = `SOL Prices Desk — ${viewTitles[viewId]}`; if (source !== "startup") { frontendDebug("main", "SOL Prices Desk view activated", { viewId, source }); } } function bindNavigation(): void { document.querySelectorAll("[data-view]").forEach(button => { button.addEventListener("click", () => { const viewId = button.dataset.view; frontendDebug("main", "SOL Prices Desk navigation control clicked", { viewId: viewId ?? "unknown" }); if (isViewId(viewId)) { activateView(viewId, "user"); } }); }); frontendTrace("main", "SOL Prices Desk navigation handlers installed"); } function appendMarketPriceCell(row: HTMLTableRowElement, value: string): void { const cell = document.createElement("td"); cell.textContent = value; row.append(cell); } function renderMarketPriceRows(rows: MarketPriceProviderRowDto[]): void { const loading = document.querySelector("#pricesLoading"); const empty = document.querySelector("#pricesEmpty"); const tableContainer = document.querySelector("#pricesTableContainer"); const tableBody = document.querySelector("#marketPriceRows"); if (loading) { loading.hidden = true; } if (!tableBody || !tableContainer || !empty) { frontendWarn("main", "SOL Prices Desk market-price table elements are missing"); return; } tableBody.replaceChildren(); empty.hidden = rows.length !== 0; tableContainer.hidden = rows.length === 0; for (const provider of rows) { const row = document.createElement("tr"); appendMarketPriceCell(row, provider.displayName); appendMarketPriceCell(row, provider.pair); appendMarketPriceCell(row, provider.semantics); appendMarketPriceCell(row, provider.authMode); appendMarketPriceCell(row, provider.loading ? "loading" : provider.availability); appendMarketPriceCell(row, provider.price ?? "—"); appendMarketPriceCell(row, provider.providerTimestampUnixMillis ?? "—"); appendMarketPriceCell(row, provider.receivedAtUnixMillis ?? "—"); appendMarketPriceCell(row, provider.retryAtUnixMillis ?? "—"); tableBody.append(row); } frontendTrace("main", "SOL Prices Desk market-price registry rows rendered", { rowCount: rows.length }); } function renderRuntimeStatus(status: MarketPriceRuntimeStatusDto): void { const values: Record = { runtimeCompositeProfile: status.activeCompositeProfile, runtimeOffchainProfile: status.activeOffchainProfile, runtimeVersion: status.applicationVersion, runtimeShellPhase: status.shellPhase, runtimeConfigDocuments: status.configDocumentCount.toString(), runtimeProviderCount: status.providerCount.toString(), runtimeReadyProviderCount: status.readyProviderCount.toString(), runtimeUnavailableProviderCount: status.unavailableProviderCount.toString(), runtimeLoggingProfile: status.activeLoggingProfile ?? "fallback", runtimeLoggingFallback: status.fallbackLoggingActive ? "oui" : "non", }; for (const [elementId, value] of Object.entries(values)) { const element = document.querySelector(`#${elementId}`); if (element) { element.textContent = value; } } const diagnostic = document.querySelector("#runtimeDiagnostic"); if (diagnostic) { diagnostic.hidden = status.startupDiagnostic === null; diagnostic.textContent = status.startupDiagnostic === null ? "" : `${status.startupDiagnostic.domain}:${status.startupDiagnostic.code} — ${status.startupDiagnostic.message}`; } frontendTrace("main", "SOL Prices Desk market-price runtime status rendered", { fallbackLoggingActive: status.fallbackLoggingActive, configDocumentCount: status.configDocumentCount, providerCount: status.providerCount, readyProviderCount: status.readyProviderCount, unavailableProviderCount: status.unavailableProviderCount, }); } async function loadMarketPrices(): Promise { frontendTrace("main", "SOL Prices Desk provider registry load started"); const rows = await invokeKsp("main", "list_market_prices"); renderMarketPriceRows(rows); frontendTrace("main", "SOL Prices Desk provider registry load completed", { rowCount: rows.length }); } async function loadRuntimeStatus(): Promise { const status = await invokeKsp("main", "get_runtime_status"); renderRuntimeStatus(status); } async function initializeMain(): Promise { const windowLabel = getCurrentWindow().label; frontendInfo("main", "SOL Prices Desk main frontend loaded", { windowLabel }); bindNavigation(); activateView("prices", "startup"); try { await loadRuntimeStatus(); } catch { const diagnostic = document.querySelector("#runtimeDiagnostic"); if (diagnostic) { diagnostic.hidden = false; diagnostic.textContent = "Le statut runtime initial n'a pas pu être chargé."; } frontendWarn("main", "SOL Prices Desk startup runtime status load failed"); } try { await loadMarketPrices(); } catch { const loading = document.querySelector("#pricesLoading"); const diagnostic = document.querySelector("#pricesDiagnostic"); if (loading) { loading.hidden = true; } if (diagnostic) { diagnostic.hidden = false; diagnostic.textContent = "L'inventaire provider-neutral n'a pas pu être chargé."; } frontendWarn("main", "SOL Prices Desk provider registry load failed"); } } document.addEventListener("DOMContentLoaded", () => { void initializeMain(); });