Files
khadhroony-bot3/kb-app-demo-desktop/frontend/ts/demo_core_extraction.ts
2026-08-11 22:22:40 +02:00

192 lines
9.5 KiB
TypeScript

// file: kb-app-demo-desktop/frontend/ts/demo_core_extraction.ts
// version: 7
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<T extends HTMLElement>(selector: string): T {
const value = document.querySelector<T>(selector);
if (!value) {
throw new Error(`Missing UI element: ${selector}`);
}
return value;
}
function inputValue(selector: string): string {
return element<HTMLInputElement | HTMLTextAreaElement>(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<HTMLTextAreaElement>("#coreExtractionLogOutput");
output.value = logLines.join("\n");
output.scrollTop = output.scrollHeight;
}
function setRunning(value: boolean): void {
running = value;
document.querySelectorAll<HTMLButtonElement>(".core-extraction-start-button").forEach(button => {
button.disabled = value;
});
element<HTMLButtonElement>("#cancelCoreExtractionButton").disabled = !value;
const badge = element<HTMLElement>("#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<HTMLTextAreaElement>("#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<HTMLInputElement>("#coreExtractionForceReplayInput").checked,
};
}
async function executeCoreExtraction(mode: string): Promise<void> {
if (running) {
return;
}
try {
const request = buildRequest(mode);
setRunning(true);
element<HTMLTextAreaElement>("#coreExtractionSummaryOutput").value = "Exécution en cours...";
appendLog({ timestamp: new Date().toISOString(), level: "info", message: `Démarrage du mode ${mode}` });
const summary = await invoke<DemoCoreExtractionSummaryPayload>("demo_core_extraction_execute", { request });
element<HTMLTextAreaElement>("#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<HTMLTextAreaElement>("#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<void> {
try {
const accepted = await invoke<boolean>("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_store_raw_window" | "open_demo_store_core_window" | "open_demo_store_replay_candidates_window", label: string): Promise<void> {
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<void> {
const options = await invoke<DemoCoreExtractionOptionsPayload>("demo_core_extraction_options");
element<HTMLElement>("#coreExtractionVersionBadge").textContent = `Version ${options.processorVersion}`;
element<HTMLInputElement>("#coreExtractionLimitInput").value = String(options.defaultLimit);
element<HTMLInputElement>("#coreExtractionConcurrencyInput").value = String(options.defaultMaxConcurrentExtractions);
const programOptions = element<HTMLDataListElement>("#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<HTMLButtonElement>(".core-extraction-start-button").forEach(button => {
button.addEventListener("click", () => {
const mode = button.dataset.coreExtractionMode;
if (mode) {
void executeCoreExtraction(mode);
}
});
});
element<HTMLButtonElement>("#openRawDiagnosticsButton").addEventListener("click", () => {
void openDiagnostics("open_demo_store_raw_window", "Diagnostic raw");
});
element<HTMLButtonElement>("#openCoreDiagnosticsButton").addEventListener("click", () => {
void openDiagnostics("open_demo_store_core_window", "Diagnostic core");
});
element<HTMLButtonElement>("#openReplayCandidatesButton").addEventListener("click", () => {
void openDiagnostics("open_demo_store_replay_candidates_window", "Sélecteur de candidats replay");
});
element<HTMLButtonElement>("#cancelCoreExtractionButton").addEventListener("click", () => {
void cancelCoreExtraction();
});
element<HTMLTextAreaElement>("#coreExtractionLogOutput").addEventListener("app-log-clear", () => {
logLines.length = 0;
});
void listen<DemoCoreExtractionProgressPayload>("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}`);
});
});