// file: kb-app-demo-desktop/frontend/ts/demo_core_extraction.ts // version: 6 import * as bootstrap from "bootstrap"; import "simplebar"; import ResizeObserver from "resize-observer-polyfill"; import { invoke } from "@tauri-apps/api/core"; import { listen } from "@tauri-apps/api/event"; import { frontendDebug, frontendError, installFrontendConsoleBridge } from "./frontend_log.ts"; import type { DemoCoreExtractionOptionsPayload } from "./bindings/kb_app_demo_desktop/demo_core_extraction/DemoCoreExtractionOptionsPayload.ts"; import type { DemoCoreExtractionProgressPayload } from "./bindings/kb_app_demo_desktop/demo_core_extraction/DemoCoreExtractionProgressPayload.ts"; import type { DemoCoreExtractionRequest } from "./bindings/kb_app_demo_desktop/demo_core_extraction/DemoCoreExtractionRequest.ts"; import type { DemoCoreExtractionSummaryPayload } from "./bindings/kb_app_demo_desktop/demo_core_extraction/DemoCoreExtractionSummaryPayload.ts"; (window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap; (window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver; const logLines: string[] = []; const maximumLogLines = 1000; let running = false; function element(selector: string): T { const value = document.querySelector(selector); if (!value) { throw new Error(`Missing UI element: ${selector}`); } return value; } function inputValue(selector: string): string { return element(selector).value.trim(); } function integerValue(selector: string): number { const parsed = Number.parseInt(inputValue(selector), 10); if (!Number.isFinite(parsed)) { throw new Error(`Valeur numérique invalide pour ${selector}`); } return parsed; } function nullableIntegerValue(selector: string): number | null { const value = inputValue(selector); if (value.length === 0) { return null; } const parsed = Number.parseInt(value, 10); if (!Number.isFinite(parsed)) { throw new Error(`Valeur numérique invalide pour ${selector}`); } return parsed; } function appendLog(payload: DemoCoreExtractionProgressPayload | { timestamp: string; level: string; message: string }): void { const progress = "completed" in payload ? ` [${payload.completed}/${payload.total}]` : ""; logLines.push(`${payload.timestamp} ${payload.level.toUpperCase()}${progress} ${payload.message}`); while (logLines.length > maximumLogLines) { logLines.shift(); } const output = element("#coreExtractionLogOutput"); output.value = logLines.join("\n"); output.scrollTop = output.scrollHeight; } function setRunning(value: boolean): void { running = value; document.querySelectorAll(".core-extraction-start-button").forEach(button => { button.disabled = value; }); element("#cancelCoreExtractionButton").disabled = !value; const badge = element("#coreExtractionStatusBadge"); badge.textContent = value ? "Extraction en cours" : "Prêt"; badge.className = value ? "badge text-bg-warning" : "badge text-bg-success"; } function buildRequest(mode: string): DemoCoreExtractionRequest { return { mode, signaturesText: mode === "signatures" ? element("#coreExtractionSignaturesTextarea").value : null, programId: mode === "program_id" ? inputValue("#coreExtractionProgramIdInput") : null, minSlot: mode === "slot_range" ? nullableIntegerValue("#coreExtractionMinSlotInput") : null, maxSlot: mode === "slot_range" ? nullableIntegerValue("#coreExtractionMaxSlotInput") : null, limit: integerValue("#coreExtractionLimitInput"), maxConcurrentExtractions: integerValue("#coreExtractionConcurrencyInput"), forceReplay: element("#coreExtractionForceReplayInput").checked, }; } async function executeCoreExtraction(mode: string): Promise { if (running) { return; } try { const request = buildRequest(mode); setRunning(true); element("#coreExtractionSummaryOutput").value = "Exécution en cours..."; appendLog({ timestamp: new Date().toISOString(), level: "info", message: `Démarrage du mode ${mode}` }); const summary = await invoke("demo_core_extraction_execute", { request }); element("#coreExtractionSummaryOutput").value = JSON.stringify(summary, null, 2); appendLog({ timestamp: new Date().toISOString(), level: summary.cancelled || Number(summary.failed) > 0 ? "warn" : "info", message: `Campagne terminée : extracted=${summary.extracted}, skipped=${summary.skipped}, failed=${summary.failed}, cancelled=${summary.cancelledCandidates}, notStarted=${summary.notStarted}`, }); } catch (caughtError) { const message = caughtError instanceof Error ? caughtError.message : String(caughtError); element("#coreExtractionSummaryOutput").value = JSON.stringify({ error: message }, null, 2); appendLog({ timestamp: new Date().toISOString(), level: "error", message }); frontendError("kb_app_demo_desktop.frontend.demo_core_extraction", `Core extraction failed: ${message}`); } finally { setRunning(false); } } async function cancelCoreExtraction(): Promise { try { const accepted = await invoke("demo_core_extraction_cancel"); appendLog({ timestamp: new Date().toISOString(), level: accepted ? "warn" : "info", message: accepted ? "Demande d'arrêt envoyée." : "Aucune campagne active.", }); } catch (caughtError) { const message = caughtError instanceof Error ? caughtError.message : String(caughtError); frontendError("kb_app_demo_desktop.frontend.demo_core_extraction", `Cancellation failed: ${message}`); } } async function openDiagnostics(command: "open_demo_sql_pg_raw_window" | "open_demo_sql_pg_core_window" | "open_demo_sql_replay_candidates_window", label: string): Promise { try { await invoke(command); appendLog({ timestamp: new Date().toISOString(), level: "info", message: `${label} ouvert.` }); } catch (caughtError) { const message = caughtError instanceof Error ? caughtError.message : String(caughtError); appendLog({ timestamp: new Date().toISOString(), level: "error", message }); frontendError("kb_app_demo_desktop.frontend.demo_core_extraction", `${label} opening failed: ${message}`); } } async function loadOptions(): Promise { const options = await invoke("demo_core_extraction_options"); element("#coreExtractionVersionBadge").textContent = `Version ${options.processorVersion}`; element("#coreExtractionLimitInput").value = String(options.defaultLimit); element("#coreExtractionConcurrencyInput").value = String(options.defaultMaxConcurrentExtractions); const programOptions = element("#coreExtractionProgramIdOptions"); programOptions.replaceChildren(...options.programs.map(program => { const option = document.createElement("option"); option.value = program.programId; option.label = program.code; return option; })); setRunning(options.running); } document.addEventListener("DOMContentLoaded", () => { installFrontendConsoleBridge("kb_app_demo_desktop.frontend.demo_core_extraction"); frontendDebug("kb_app_demo_desktop.frontend.demo_core_extraction", "core extraction demo window loaded"); document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(item => new bootstrap.Tooltip(item)); document.querySelectorAll(".core-extraction-start-button").forEach(button => { button.addEventListener("click", () => { const mode = button.dataset.coreExtractionMode; if (mode) { void executeCoreExtraction(mode); } }); }); element("#openRawDiagnosticsButton").addEventListener("click", () => { void openDiagnostics("open_demo_sql_pg_raw_window", "Diagnostic raw"); }); element("#openCoreDiagnosticsButton").addEventListener("click", () => { void openDiagnostics("open_demo_sql_pg_core_window", "Diagnostic core"); }); element("#openReplayCandidatesButton").addEventListener("click", () => { void openDiagnostics("open_demo_sql_replay_candidates_window", "Sélecteur de candidats replay"); }); element("#cancelCoreExtractionButton").addEventListener("click", () => { void cancelCoreExtraction(); }); element("#coreExtractionLogOutput").addEventListener("app-log-clear", () => { logLines.length = 0; }); void listen("demo-core-extraction-progress", event => { appendLog(event.payload); }); void loadOptions().catch(caughtError => { const message = caughtError instanceof Error ? caughtError.message : String(caughtError); appendLog({ timestamp: new Date().toISOString(), level: "error", message }); frontendError("kb_app_demo_desktop.frontend.demo_core_extraction", `Options loading failed: ${message}`); }); });