v0.3.7-pre.009
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<!-- file: crates/ksp-app-backfill-desk/frontend/main.html -->
|
||||
<!-- version: 4 -->
|
||||
<!-- version: 5 -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
|
||||
@@ -127,10 +127,36 @@
|
||||
<button id="startBackfillRequest" class="btn btn-primary" type="button" disabled>
|
||||
<i class="fa-solid fa-play me-2" aria-hidden="true"></i>Démarrer le backfill
|
||||
</button>
|
||||
<span class="text-body-secondary small">Un seul run peut être actif ; monitoring détaillé en pre.009.</span>
|
||||
<span class="text-body-secondary small">Un seul run peut être actif ; le monitoring latest-value est coalescé côté backend.</span>
|
||||
</div>
|
||||
</form>
|
||||
<div id="backfillRequestValidation" class="alert alert-secondary mt-4 mb-0" role="status" aria-live="polite" hidden></div>
|
||||
<div id="backfillRunStatusCard" class="card shadow-sm mt-4" hidden>
|
||||
<div class="card-header d-flex align-items-center justify-content-between gap-3 flex-wrap">
|
||||
<span class="fw-semibold">Monitoring latest-value</span>
|
||||
<button id="refreshBackfillStatus" class="btn btn-outline-primary btn-sm" type="button">
|
||||
<i class="fa-solid fa-rotate me-2" aria-hidden="true"></i>Resynchroniser
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<dl class="row mb-0 app-runtime-list">
|
||||
<dt class="col-sm-5">Job</dt><dd id="runStatusJobId" class="col-sm-7 font-monospace">—</dd>
|
||||
<dt class="col-sm-5">Lifecycle</dt><dd id="runStatusLifecycle" class="col-sm-7">—</dd>
|
||||
<dt class="col-sm-5">Phase</dt><dd id="runStatusPhase" class="col-sm-7">—</dd>
|
||||
<dt class="col-sm-5">Séquence</dt><dd id="runStatusSequence" class="col-sm-7 font-monospace">—</dd>
|
||||
<dt class="col-sm-5">Scope</dt><dd id="runStatusScope" class="col-sm-7">—</dd>
|
||||
<dt class="col-sm-5">Boundary</dt><dd id="runStatusBoundary" class="col-sm-7">—</dd>
|
||||
<dt class="col-sm-5">Candidates sel./adm./fin.</dt><dd id="runStatusCandidates" class="col-sm-7">—</dd>
|
||||
<dt class="col-sm-5">Frontier / holes</dt><dd id="runStatusFrontier" class="col-sm-7">—</dd>
|
||||
<dt class="col-sm-5">RAW ins./exist./purged</dt><dd id="runStatusEntities" class="col-sm-7">—</dd>
|
||||
<dt class="col-sm-5">Observations ins./exist.</dt><dd id="runStatusObservations" class="col-sm-7">—</dd>
|
||||
<dt class="col-sm-5">Anomalies</dt><dd id="runStatusAnomalies" class="col-sm-7">—</dd>
|
||||
<dt class="col-sm-5">Max in-flight</dt><dd id="runStatusConcurrency" class="col-sm-7">—</dd>
|
||||
<dt class="col-sm-5">Checkpoint</dt><dd id="runStatusCheckpoint" class="col-sm-7">—</dd>
|
||||
<dt class="col-sm-5">Failure</dt><dd id="runStatusFailure" class="col-sm-7">—</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<div id="backfillRequestPreview" class="card bg-body-tertiary border-0 mt-3" hidden>
|
||||
<div class="card-body">
|
||||
<h2 class="h6 mb-3">Projection backend sûre</h2>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
// file: crates/ksp-app-backfill-desk/frontend/ts/main.ts
|
||||
// version: 4
|
||||
// version: 5
|
||||
|
||||
import "bootstrap";
|
||||
import ResizeObserver from "resize-observer-polyfill";
|
||||
import "simplebar";
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
import { getCurrentWindow } from "@tauri-apps/api/window";
|
||||
import type { BackfillRunStatusDto } from "./bindings/ksp_app_backfill_desk/backfill_status/BackfillRunStatusDto.ts";
|
||||
import type { BackfillRequestPreviewDto } from "./bindings/ksp_app_backfill_desk/dto_backfill/BackfillRequestPreviewDto.ts";
|
||||
import type { BackfillStartRequestDto } from "./bindings/ksp_app_backfill_desk/dto_backfill/BackfillStartRequestDto.ts";
|
||||
import type { BackfillStartResponseDto } from "./bindings/ksp_app_backfill_desk/dto_backfill/BackfillStartResponseDto.ts";
|
||||
@@ -320,8 +322,9 @@ async function startBackfillRequest(): Promise<void> {
|
||||
status.hidden = false;
|
||||
status.classList.remove("alert-secondary", "alert-danger");
|
||||
status.classList.add("alert-success");
|
||||
status.textContent = `Job ${started.jobId} démarré (${started.state}). Le monitoring détaillé arrive en pre.009.`;
|
||||
status.textContent = `Job ${started.jobId} démarré (${started.state}).`;
|
||||
}
|
||||
await syncBackfillStatus("start");
|
||||
frontendInfo("main", "Backfill Desk campaign Start accepted", { jobId: started.jobId, state: started.state });
|
||||
} catch (caughtError) {
|
||||
renderRequestValidationError(caughtError);
|
||||
@@ -447,6 +450,76 @@ function bindBackfillRouteSelection(): void {
|
||||
frontendTrace("main", "Backfill Desk HTTP route selection handler installed");
|
||||
}
|
||||
|
||||
|
||||
function renderBackfillRunStatus(status: BackfillRunStatusDto | null, source: "event" | "startup" | "start" | "user"): void {
|
||||
const card = document.querySelector<HTMLElement>("#backfillRunStatusCard");
|
||||
if (!status) {
|
||||
runStartAccepted = false;
|
||||
refreshStartButton();
|
||||
if (card) {
|
||||
card.hidden = true;
|
||||
}
|
||||
frontendTrace("main", "Backfill Desk latest-value status cleared", { source });
|
||||
return;
|
||||
}
|
||||
runStartAccepted = status.active;
|
||||
refreshStartButton();
|
||||
if (card) {
|
||||
card.hidden = false;
|
||||
}
|
||||
const values: Record<string, string> = {
|
||||
runStatusJobId: status.jobId,
|
||||
runStatusLifecycle: status.completion === null ? status.state : `${status.state} / ${status.completion}`,
|
||||
runStatusPhase: status.phase,
|
||||
runStatusSequence: status.sequence,
|
||||
runStatusScope: status.scopeKind,
|
||||
runStatusBoundary: status.discoveryBoundary ?? "—",
|
||||
runStatusCandidates: `${status.candidatesSelected} / ${status.candidatesAdmitted} / ${status.candidatesFinished}`,
|
||||
runStatusFrontier: `${status.contiguousCompleted} (holes ${status.holes})`,
|
||||
runStatusEntities: `${status.entitiesInserted} / ${status.entitiesExisting} / ${status.entitiesPurged}`,
|
||||
runStatusObservations: `${status.observationsInserted} / ${status.observationsExisting}`,
|
||||
runStatusAnomalies: `missing ${status.missing} / conflits ${status.conflicts} / annulés ${status.cancelledCandidates}`,
|
||||
runStatusConcurrency: status.maximumInFlight.toString(),
|
||||
runStatusCheckpoint: status.checkpointPresent ? "présent" : "absent",
|
||||
runStatusFailure: status.failureDomain === null || status.failureCode === null ? "—" : `${status.failureDomain}/${status.failureCode}`,
|
||||
};
|
||||
for (const [id, value] of Object.entries(values)) {
|
||||
const element = document.querySelector<HTMLElement>(`#${id}`);
|
||||
if (element) {
|
||||
element.textContent = value;
|
||||
}
|
||||
}
|
||||
frontendTrace("main", "Backfill Desk latest-value status rendered", {
|
||||
active: status.active,
|
||||
phase: status.phase,
|
||||
sequence: status.sequence,
|
||||
source,
|
||||
state: status.state,
|
||||
terminal: status.terminal,
|
||||
});
|
||||
}
|
||||
|
||||
async function syncBackfillStatus(source: "startup" | "start" | "user"): Promise<void> {
|
||||
frontendDebug("main", "Backfill Desk latest-value status resynchronization started", { source });
|
||||
const status = await invokeKsp<BackfillRunStatusDto | null>("main", "backfill_status");
|
||||
renderBackfillRunStatus(status, source);
|
||||
frontendDebug("main", "Backfill Desk latest-value status resynchronization completed", { source, statusPresent: status !== null });
|
||||
}
|
||||
|
||||
async function bindBackfillStatusMonitoring(): Promise<void> {
|
||||
await listen<BackfillRunStatusDto>("ksp-backfill-status", event => {
|
||||
renderBackfillRunStatus(event.payload, "event");
|
||||
});
|
||||
const button = document.querySelector<HTMLButtonElement>("#refreshBackfillStatus");
|
||||
if (button) {
|
||||
button.addEventListener("click", () => {
|
||||
frontendDebug("main", "Backfill Desk latest-value status refresh button clicked");
|
||||
void syncBackfillStatus("user").catch(() => frontendWarn("main", "Backfill Desk latest-value status refresh failed"));
|
||||
});
|
||||
}
|
||||
frontendTrace("main", "Backfill Desk latest-value monitoring listener installed");
|
||||
}
|
||||
|
||||
function renderRuntimeStatus(status: ShellStatusDto): void {
|
||||
const values: Record<string, string> = {
|
||||
runtimeVersion: status.applicationVersion,
|
||||
@@ -503,9 +576,10 @@ async function initializeMain(): Promise<void> {
|
||||
bindRuntimeStatusRefresh();
|
||||
bindBackfillRouteSelection();
|
||||
bindCampaignForm();
|
||||
await bindBackfillStatusMonitoring();
|
||||
activateView("backfill", "startup");
|
||||
try {
|
||||
await Promise.all([loadRuntimeStatus("startup"), loadBackfillOptions("startup")]);
|
||||
await Promise.all([loadRuntimeStatus("startup"), loadBackfillOptions("startup"), syncBackfillStatus("startup")]);
|
||||
} catch {
|
||||
frontendWarn("main", "Backfill Desk startup runtime status load failed");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user