(() => { const dropZone = document.getElementById("drop-zone"); const fileInput = document.getElementById("file-input"); const fileList = document.getElementById("file-list"); const uploadBtn = document.getElementById("upload-btn"); const uploadSection = document.getElementById("upload-section"); const statusSection = document.getElementById("status-section"); const statusText = document.getElementById("status-text"); const resultsSection = document.getElementById("results-section"); const resultsContainer = document.getElementById("results-container"); let selectedFiles = []; // Drag & drop dropZone.addEventListener("click", () => fileInput.click()); dropZone.addEventListener("dragover", (e) => { e.preventDefault(); dropZone.classList.add("dragover"); }); dropZone.addEventListener("dragleave", () => dropZone.classList.remove("dragover")); dropZone.addEventListener("drop", (e) => { e.preventDefault(); dropZone.classList.remove("dragover"); addFiles(e.dataTransfer.files); }); fileInput.addEventListener("change", () => addFiles(fileInput.files)); function addFiles(files) { for (const f of files) { if (!selectedFiles.some((s) => s.name === f.name && s.size === f.size)) { selectedFiles.push(f); } } renderFileList(); } function renderFileList() { fileList.innerHTML = ""; selectedFiles.forEach((f, i) => { const tag = document.createElement("span"); tag.className = "file-tag"; tag.innerHTML = `${f.name} ×`; fileList.appendChild(tag); }); fileList.querySelectorAll(".remove").forEach((btn) => btn.addEventListener("click", (e) => { selectedFiles.splice(+e.target.dataset.idx, 1); renderFileList(); }) ); uploadBtn.disabled = selectedFiles.length === 0; } // Upload uploadBtn.addEventListener("click", async () => { if (!selectedFiles.length) return; uploadSection.hidden = true; statusSection.hidden = false; resultsSection.hidden = true; statusText.textContent = `Uploading ${selectedFiles.length} file(s)...`; const form = new FormData(); selectedFiles.forEach((f) => form.append("files", f)); try { const resp = await fetch("/api/upload", { method: "POST", body: form }); if (!resp.ok) { const err = await resp.json().catch(() => ({ detail: resp.statusText })); throw new Error(err.detail || "Upload failed"); } const { batch_id } = await resp.json(); statusText.textContent = "Analyzing..."; await pollResults(batch_id); } catch (err) { statusSection.hidden = true; uploadSection.hidden = false; alert("Error: " + err.message); } }); async function pollResults(batchId) { const maxAttempts = 60; for (let i = 0; i < maxAttempts; i++) { const resp = await fetch(`/api/results/${batchId}`); if (!resp.ok) { await sleep(1000); continue; } const data = await resp.json(); if (data.status === "complete") { renderResults(data); return; } statusText.textContent = `Analyzing... (${i + 1}s)`; await sleep(1000); } statusSection.hidden = true; uploadSection.hidden = false; alert("Timed out waiting for results"); } function sleep(ms) { return new Promise((r) => setTimeout(r, ms)); } // Render function renderResults(data) { statusSection.hidden = true; resultsSection.hidden = false; resultsContainer.innerHTML = ""; for (const file of data.files) { const div = document.createElement("div"); div.className = "file-result"; if (file.error) { div.innerHTML = `
Error: ${esc(file.error)}
`; resultsContainer.appendChild(div); continue; } let html = `| Feature | n | Mean | Std | Cp | Cpk | Pp | Ppk | USL | LSL | OOS |
|---|---|---|---|---|---|---|---|---|---|---|
| ${esc(s.feature_name)} | ${s.n} | ${s.mean} | ${s.std} | ${fmtIdx(s.cp)} | ${fmtIdx(s.cpk)} | ${fmtIdx(s.pp)} | ${fmtIdx(s.ppk)} | ${s.usl} | ${s.lsl} | ${s.out_of_spec_count} |
| Feature | Nominal | Actual | Dev | Tol+ | Tol- | USL | LSL | Status |
|---|---|---|---|---|---|---|---|---|
| ${esc(m.feature_name)} | ${m.nominal} | ${m.actual} | ${m.deviation.toFixed(4)} | +${m.tolerance_plus} | ${m.tolerance_minus} | ${m.usl} | ${m.lsl} | ${m.in_tolerance ? "OK" : "OOT"} |