// VV compare frontend
(() => {
const $ = (id) => document.getElementById(id);
const sections = {
upload: $("s-upload"),
processing: $("s-processing"),
result: $("s-result"),
};
const show = (n) => {
for (const [k, el] of Object.entries(sections)) el.classList.toggle("hidden", k !== n);
};
let state = { jobId: null, result: null, origFile: null, newFile: null };
function attachDrop(zoneId, inputId, textId, which) {
const zone = $(zoneId);
const input = $(inputId);
const text = $(textId);
zone.addEventListener("click", () => input.click());
input.addEventListener("change", (e) => {
if (e.target.files[0]) setFile(which, e.target.files[0], zone, text);
});
["dragenter", "dragover"].forEach((ev) =>
zone.addEventListener(ev, (e) => { e.preventDefault(); zone.classList.add("drag-over"); }));
["dragleave", "drop"].forEach((ev) =>
zone.addEventListener(ev, (e) => { e.preventDefault(); zone.classList.remove("drag-over"); }));
zone.addEventListener("drop", (e) => {
if (e.dataTransfer.files[0]) setFile(which, e.dataTransfer.files[0], zone, text);
});
}
function setFile(which, file, zone, textEl) {
if (which === "orig") state.origFile = file;
else state.newFile = file;
zone.classList.add("has-file");
const size = file.size > 1024 * 1024
? `${(file.size / 1024 / 1024).toFixed(1)} MB`
: `${(file.size / 1024).toFixed(0)} kB`;
textEl.innerHTML = `${escapeHtml(file.name)}
${size}`;
updateRunBtn();
}
attachDrop("drop-original", "orig-input", "orig-text", "orig");
attachDrop("drop-new", "new-input", "new-text", "new");
function updateRunBtn() {
const btn = $("compare-btn");
const hint = $("compare-hint");
if (!state.origFile && !state.newFile) {
btn.disabled = true; hint.textContent = "Nahrajte oba soubory.";
} else if (!state.origFile) {
btn.disabled = true; hint.textContent = "Chybí původní VV.";
} else if (!state.newFile) {
btn.disabled = true; hint.textContent = "Chybí nový VV.";
} else {
btn.disabled = false; hint.textContent = "Připraveno k porovnání.";
}
}
$("compare-btn").addEventListener("click", async () => {
if (!state.origFile || !state.newFile) return;
show("processing");
try {
const fd = new FormData();
fd.append("original", state.origFile);
fd.append("new", state.newFile);
const r = await fetch("/api/compare", { method: "POST", body: fd });
if (!r.ok) throw new Error((await r.json()).detail || r.statusText);
const json = await r.json();
state.jobId = json.job_id;
state.result = json.result;
renderResult();
show("result");
} catch (err) {
alert("Chyba: " + err.message);
show("upload");
}
});
function renderResult() {
const r = state.result;
const c = r.changes.length, a = r.added.length, d = r.removed.length;
$("cnt-changed").textContent = c;
$("cnt-added").textContent = a;
$("cnt-removed").textContent = d;
$("results-meta").textContent =
`Porovnání ${r.per_sheet.length} listů — ${c + a + d} celkových rozdílů.`;
const psHtml = r.per_sheet.map((ps) => `
| List | Hala | Pol. původní | Pol. nový | Změněné | Přidané | Odebrané |
|---|
Nepodařilo se najít listy VV v žádném ze souborů.
`; } $("download-btn").addEventListener("click", () => { if (state.jobId) window.location.href = `/api/report/${state.jobId}`; }); $("restart-btn").addEventListener("click", () => { state = { jobId: null, result: null, origFile: null, newFile: null }; $("orig-input").value = ""; $("new-input").value = ""; $("drop-original").classList.remove("has-file"); $("drop-new").classList.remove("has-file"); $("orig-text").textContent = "Přetáhněte soubor sem nebo klikněte"; $("new-text").textContent = "Přetáhněte soubor sem nebo klikněte"; updateRunBtn(); show("upload"); }); function escapeHtml(s) { return String(s ?? "").replace(/[&<>"']/g, (c) => ({"&":"&","<":"<",">":">",'"':""","'":"'"}[c])); } })();