mirror of
https://github.com/napnap75/multiarch-docker-images.git
synced 2026-11-14 18:01:47 +01:00
* Add tools page with find-lost-files consistency tool (US-40, US-41) Co-authored-by: napnap75 <napnap75@users.noreply.github.com> * tools page: hide the job panel when there is nothing to show, guard against undefined rendering Co-authored-by: napnap75 <napnap75@users.noreply.github.com> * inbox: leave non-PDF objects in place, never imported (US-12) Co-authored-by: napnap75 <napnap75@users.noreply.github.com> --------- Co-authored-by: Vibe Nuage Agent <vibe@mistral.ai> Co-authored-by: napnap75 <napnap75@users.noreply.github.com>
162 lines
5.3 KiB
JavaScript
162 lines
5.3 KiB
JavaScript
"use strict";
|
|
|
|
// Tools page (US-40, US-41): one card per tool with its description and a
|
|
// run button, live progress while the job runs, final summary afterwards.
|
|
// The job panel is restored from GET /api/archivist/tools on reload.
|
|
|
|
var pollTimer = null;
|
|
var currentJobId = null;
|
|
|
|
function $(id) { return document.getElementById(id); }
|
|
|
|
var SUMMARY_LABELS = {
|
|
scanned: "Fichiers analysés",
|
|
ok: "OK (à leur place)",
|
|
corrected: "Corrigés (déplacés)",
|
|
moved: "Déplacés vers inbox/",
|
|
duplicates: "Copies parasites",
|
|
missing: "Introuvables",
|
|
errors: "Erreurs"
|
|
};
|
|
|
|
var SUMMARY_ORDER = ["scanned", "ok", "corrected", "moved", "duplicates", "missing", "errors"];
|
|
|
|
function esc(v) {
|
|
return String(v == null ? "" : v)
|
|
.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")
|
|
.replace(/"/g, """).replace(/'/g, "'");
|
|
}
|
|
|
|
async function init() {
|
|
initTheme("themeBtn");
|
|
var res = await api("GET", "/api/archivist/tools");
|
|
if (res.status !== 200) return;
|
|
renderTools(res.data.tools || []);
|
|
var job = res.data && res.data.job;
|
|
if (job && job.id) {
|
|
renderJob(job);
|
|
if (job.status === "running") poll(job.id);
|
|
} else {
|
|
hideJob();
|
|
}
|
|
}
|
|
|
|
// hideJob clears and hides the progress/summary block: nothing to show
|
|
// when no job is running and no result is worth restoring.
|
|
function hideJob() {
|
|
stopPoll();
|
|
currentJobId = null;
|
|
var panel = $("jobpanel");
|
|
panel.hidden = true;
|
|
panel.innerHTML = "";
|
|
}
|
|
|
|
function renderTools(tools) {
|
|
var host = $("tools");
|
|
host.innerHTML = "";
|
|
tools.forEach(function (tool) {
|
|
var card = document.createElement("div");
|
|
card.className = "card toolcard";
|
|
card.innerHTML =
|
|
'<div class="toolhead"><h3>' + esc(tool.name) + "</h3>" +
|
|
'<button type="button" class="btn primary" data-tool="' + esc(tool.id) + '">Lancer</button></div>' +
|
|
'<p class="tooldesc">' + esc(tool.description) + "</p>";
|
|
card.querySelector("button").addEventListener("click", function () { run(tool.id); });
|
|
host.appendChild(card);
|
|
});
|
|
}
|
|
|
|
async function run(toolId) {
|
|
var res = await api("POST", "/api/archivist/tools/" + encodeURIComponent(toolId) + "/run");
|
|
if (res.status !== 200) {
|
|
$("jobpanel").hidden = false;
|
|
$("jobpanel").innerHTML = '<div class="card"><p class="errbox" style="display:block">' +
|
|
esc(errMsg(res)) + "</p></div>";
|
|
return;
|
|
}
|
|
renderJob(res.data);
|
|
poll(res.data.id);
|
|
}
|
|
|
|
function poll(jobId) {
|
|
currentJobId = jobId;
|
|
if (pollTimer) clearInterval(pollTimer);
|
|
pollTimer = setInterval(async function () {
|
|
var res = await api("GET", "/api/archivist/tools/jobs/" + encodeURIComponent(jobId));
|
|
if (res.status === 404) {
|
|
hideJob();
|
|
return;
|
|
}
|
|
if (res.status !== 200) {
|
|
stopPoll();
|
|
return;
|
|
}
|
|
renderJob(res.data);
|
|
if (res.data.status !== "running") stopPoll();
|
|
}, 1000);
|
|
}
|
|
|
|
function stopPoll() {
|
|
if (pollTimer) clearInterval(pollTimer);
|
|
pollTimer = null;
|
|
}
|
|
|
|
function renderJob(job) {
|
|
job = job || {};
|
|
var panel = $("jobpanel");
|
|
panel.hidden = false;
|
|
var running = job.status === "running";
|
|
var processed = job.processed || 0;
|
|
var total = job.total || 0;
|
|
var pct = total > 0 ? Math.round((processed / total) * 100) : 0;
|
|
|
|
var html = '<div class="card jobcard">';
|
|
if (running) {
|
|
html += '<div class="jobhead"><h3>Tâche en cours…</h3>' +
|
|
'<button type="button" id="cancelBtn" class="btn secondary">Annuler</button></div>';
|
|
} else {
|
|
var label = job.status === "done" ? "Tâche terminée"
|
|
: job.status === "cancelled" ? "Tâche annulée"
|
|
: job.status === "failed" ? "Tâche échouée"
|
|
: job.status ? "Tâche " + job.status : "Tâche";
|
|
html += '<div class="jobhead"><h3>' + esc(label) + "</h3></div>";
|
|
}
|
|
|
|
html += '<div class="progress" role="progressbar" aria-valuenow="' + pct + '" aria-valuemin="0" aria-valuemax="100">' +
|
|
'<div class="progressbar" style="width:' + pct + '%"></div></div>' +
|
|
'<div class="progressinfo">' + processed + " / " + total + " fichiers analysés" +
|
|
(running && job.current ? " — " + '<span class="mono">' + esc(job.current) + "</span>" : "") + "</div>";
|
|
|
|
var summary = job.summary || {};
|
|
html += '<div class="summarygrid">';
|
|
SUMMARY_ORDER.forEach(function (key) {
|
|
if (summary[key] === undefined) return;
|
|
html += '<div class="summaryitem"><span class="summarynum">' + summary[key] + "</span>" +
|
|
'<span class="summarylabel">' + esc(SUMMARY_LABELS[key] || key) + "</span></div>";
|
|
});
|
|
html += "</div>";
|
|
|
|
if ((job.missing || []).length) {
|
|
html += '<details class="jobdetails"><summary>Fichiers introuvables dans le bucket (' + job.missing.length + ')</summary><ul class="mono-list">';
|
|
job.missing.forEach(function (k) { html += '<li class="mono">' + esc(k) + "</li>"; });
|
|
html += "</ul></details>";
|
|
}
|
|
if ((job.errors || []).length) {
|
|
html += '<details class="jobdetails"><summary>Erreurs (' + job.errors.length + ')</summary><ul class="mono-list">';
|
|
job.errors.forEach(function (e) { html += '<li class="mono">' + esc(e) + "</li>"; });
|
|
html += "</ul></details>";
|
|
}
|
|
html += "</div>";
|
|
panel.innerHTML = html;
|
|
|
|
var cancelBtn = $("cancelBtn");
|
|
if (cancelBtn && job.status === "running") {
|
|
cancelBtn.addEventListener("click", async function () {
|
|
cancelBtn.disabled = true;
|
|
await api("POST", "/api/archivist/tools/jobs/" + encodeURIComponent(job.id) + "/cancel");
|
|
});
|
|
}
|
|
}
|
|
|
|
init();
|