// file: crates/ksp-app-store-desk/frontend/ts/main.ts // version: 2 import DataTable from "datatables.net-bs5"; import "datatables.net-bs5/css/dataTables.bootstrap5.css"; import "bootstrap"; import ResizeObserver from "resize-observer-polyfill"; import "simplebar"; import type { ShellStatusDto } from "./bindings/ksp_app_store_desk/dto_common/ShellStatusDto.ts"; import { frontendDebug, frontendError, frontendInfo, frontendTrace, installFrontendConsoleBridge } from "./frontend_log"; import { invokeKsp } from "./invoke"; (window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver; installFrontendConsoleBridge("main"); type ViewId = "overview" | "transactions" | "accounts" | "diagnostics"; const viewTitles: Record = { overview: "Overview", transactions: "RAW Transactions", accounts: "RAW Accounts", diagnostics: "Diagnostics", }; interface EmptyDataTable { columns: { adjust(): void }; } let transactionTable: EmptyDataTable | null = null; let accountTable: EmptyDataTable | null = null; function isViewId(value: string | undefined): value is ViewId { return value === "overview" || value === "transactions" || value === "accounts" || value === "diagnostics"; } function activateView(viewId: ViewId): 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 title = document.querySelector("#headerViewTitle"); if (title) { title.textContent = viewTitles[viewId]; } document.title = `Store Desk — ${viewTitles[viewId]}`; if (viewId === "transactions") { transactionTable?.columns.adjust(); } if (viewId === "accounts") { accountTable?.columns.adjust(); } frontendDebug("main", "Store Desk view activated", { viewId }); } function initializeEmptyTables(): void { const commonOptions = { data: [], paging: true, serverSide: false, pageLength: 25, lengthMenu: [25, 50, 100], searching: false, ordering: false, info: true, scrollX: true, autoWidth: false, language: { emptyTable: "Aucune donnée Store n'est chargée dans le scaffold pre.002.", }, }; transactionTable = new DataTable("#rawTransactionsTable", commonOptions) as unknown as EmptyDataTable; accountTable = new DataTable("#rawAccountsTable", commonOptions) as unknown as EmptyDataTable; frontendTrace("main", "Store Desk empty DataTables skeletons initialized", { pagingOwner: "datatables", serverSide: false }); } function renderShellStatus(status: ShellStatusDto): void { const versionBadge = document.querySelector("#appVersionBadge"); if (versionBadge) { versionBadge.textContent = status.applicationVersion; } const values: Record = { runtimeVersion: status.applicationVersion, runtimeShellPhase: status.shellPhase, runtimeConfigDocuments: status.configDocumentCount.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 ? `${status.startupDiagnostic.domain}/${status.startupDiagnostic.code}: ${status.startupDiagnostic.message}` : ""; } frontendTrace("main", "Store Desk shell status rendered", { fallbackLoggingActive: status.fallbackLoggingActive, shellPhase: status.shellPhase, }); } async function refreshDiagnostics(): Promise { frontendDebug("main", "Store Desk diagnostics refresh started"); try { const status = await invokeKsp("main", "get_shell_status"); renderShellStatus(status); frontendDebug("main", "Store Desk diagnostics refresh completed"); } catch { frontendError("main", "Store Desk diagnostics refresh failed"); } } function installInteractions(): void { document.querySelectorAll("[data-view]").forEach(button => { button.addEventListener("click", () => { const viewId = button.dataset.view; frontendDebug("main", "Store Desk navigation button clicked", { viewId: viewId ?? "missing" }); if (isViewId(viewId)) { activateView(viewId); } }); }); const refreshButton = document.querySelector("#refreshDiagnostics"); refreshButton?.addEventListener("click", () => { frontendDebug("main", "Store Desk diagnostics refresh button clicked"); void refreshDiagnostics(); }); document.addEventListener("click", event => { const eventTarget = event.target; if (!(eventTarget instanceof Element)) { return; } const control = eventTarget.closest("button, [role='tab'], [data-bs-toggle='tab']"); if (!control) { return; } if (control instanceof HTMLButtonElement && (control.dataset.view || control.id === "refreshDiagnostics")) { return; } const controlId = control.id || control.getAttribute("data-bs-target") || control.getAttribute("aria-controls") || "anonymous"; if (control.matches("[role='tab'], [data-bs-toggle='tab']")) { frontendDebug("main", "Store Desk tab control clicked", { controlId }); return; } frontendDebug("main", "Store Desk generic button clicked", { buttonId: controlId }); }); document.addEventListener("shown.bs.tab", event => { const target = event.target; const tabId = target instanceof HTMLElement ? target.id || target.getAttribute("data-bs-target") || target.textContent?.trim() || "anonymous" : "unknown"; frontendDebug("main", "Store Desk tab activated", { tabId }); }); document.addEventListener("change", event => { const target = event.target; if (!(target instanceof HTMLInputElement || target instanceof HTMLSelectElement || target instanceof HTMLTextAreaElement)) { return; } frontendDebug("main", "Store Desk interactive control changed", { controlId: target.id || target.getAttribute("name") || "anonymous", controlType: target instanceof HTMLSelectElement ? "select" : target.type || "text", }); }); frontendTrace("main", "Store Desk frontend interactions installed", { buttons: true, controls: true, tabs: true }); } async function initializeMain(): Promise { frontendInfo("main", "Store Desk main frontend loaded"); initializeEmptyTables(); installInteractions(); activateView("overview"); await refreshDiagnostics(); } document.addEventListener("DOMContentLoaded", () => { void initializeMain(); });