// VV price-check 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 }; // Upload const fileInput = $("file-input"); const dropZone = $("drop-zone"); $("browse-btn").addEventListener("click", () => fileInput.click()); fileInput.addEventListener("change", (e) => e.target.files[0] && upload(e.target.files[0])); ["dragenter", "dragover"].forEach((ev) => dropZone.addEventListener(ev, (e) => { e.preventDefault(); dropZone.classList.add("drag-over"); })); ["dragleave", "drop"].forEach((ev) => dropZone.addEventListener(ev, (e) => { e.preventDefault(); dropZone.classList.remove("drag-over"); })); dropZone.addEventListener("drop", (e) => e.dataTransfer.files[0] && upload(e.dataTransfer.files[0])); async function upload(file) { show("processing"); try { const fd = new FormData(); fd.append("file", file); const r = await fetch("/api/check", { 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 fmtPrice(v) { if (v == null || isNaN(v)) return "—"; return v.toLocaleString("cs-CZ", { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + " Kč"; } function renderResult() { const r = state.result; const vvCount = r.vv_sheet_count; const pricedCount = r.vv_sheets_with_prices || 0; const incCount = r.total_inconsistencies; const totalSheets = r.sheets.length; $("results-meta").textContent = `${totalSheets} listů v sešitu, ${vvCount} identifikováno jako VV, ${pricedCount} s vyplněnými cenami.`; const sheetsHtml = r.sheets.map((s) => { let cls = "not-vv"; let label = s.name; let icon = "·"; if (s.is_vv && s.priced_items > 0) { cls = "vv"; icon = "✓"; label = `${s.name} (${s.priced_items} s cenou)`; } else if (s.is_vv && s.items > 0) { cls = "vv-noprice"; icon = "⚠"; label = `${s.name} (${s.items} bez cen)`; } else if (s.is_vv) { cls = "not-vv"; label = `${s.name} (prázdné)`; } return `${icon} ${escapeHtml(label)}`; }).join(""); $("sheets-summary").innerHTML = `
V sešitu nebyl rozpoznán žádný výkaz výměr.
Aplikace hledá listy s hlavičkou „Popis / MJ / Výměra / Jedn. cena". Pokud váš sešit používá jiný formát, dejte vědět přes Návrh nového nástroje.
Nalezeno ${vvCount} listů VV, ale žádný nemá vyplněné jednotkové ceny.
„Kontrola cen ve VV" porovnává jednotkové ceny napříč listy — bez vyplněných cen není co porovnávat.
Pokud chcete porovnat tento sešit s předchozí verzí (změny ve výměrách, přidané/odebrané položky),
použijte Porovnání VV.
Všechny položky se stejným názvem mají shodnou jednotkovou cenu ve všech VV listech.
| List VV | Řádek | MJ | Jed. cena |
|---|