// NephroScreen frontend. Same-origin API by default; override API_BASE when the // frontend is hosted separately from the backend (e.g. Vercel + Render). const API_BASE = window.NEPHRO_API_BASE || ""; const EXAMPLE = { age: 62, bp: 80, sg: 1.01, al: 3, su: 0, bgr: 148, bu: 86, sc: 3.2, sod: 135, pot: 4.6, hemo: 9.5, pcv: 28, wbcc: 9800, rbcc: 3.4, rbc: "abnormal", pc: "abnormal", pcc: "present", ba: "notpresent", htn: "yes", dm: "yes", cad: "no", appet: "poor", pe: "yes", ane: "yes", }; const $ = (sel) => document.querySelector(sel); async function init() { try { const meta = await fetch(`${API_BASE}/api/metadata`).then((r) => r.json()); renderModelStrip(meta); renderFields(meta); } catch (e) { $("#modelStrip").innerHTML = 'API offline — start the backend to enable predictions.'; } $("#predictForm").addEventListener("submit", onSubmit); $("#exampleBtn").addEventListener("click", loadExample); } function renderModelStrip(meta) { const rf = (meta.metrics && meta.metrics["Random Forest"]) || {}; const chips = [ ["Model", "Random Forest"], ["Accuracy", rf.accuracy != null ? rf.accuracy + "%" : "—"], ["Recall", rf.recall != null ? rf.recall + "%" : "—"], ["ROC-AUC", rf.roc_auc != null ? rf.roc_auc : "—"], ["Threshold", meta.threshold], ]; $("#modelStrip").innerHTML = chips .map(([k, v]) => `${k} ${v}`) .join(""); } function renderFields(meta) { $("#numericFields").innerHTML = meta.numeric_fields .map( (f) => `
` ) .join(""); $("#categoricalFields").innerHTML = meta.categorical_fields .map((f) => { const opts = [''] .concat(f.choices.map((c) => ``)) .join(""); return `
`; }) .join(""); } function loadExample() { for (const [k, v] of Object.entries(EXAMPLE)) { const el = document.getElementById(k); if (el) el.value = v; } } function collect() { const payload = {}; new FormData($("#predictForm")).forEach((value, key) => { if (value === "" || value == null) return; const num = Number(value); payload[key] = Number.isNaN(num) || value.match(/[a-z]/i) ? value : num; }); return payload; } async function onSubmit(evt) { evt.preventDefault(); const btn = $("#submitBtn"); btn.disabled = true; btn.textContent = "Analyzing…"; try { const res = await fetch(`${API_BASE}/api/predict`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(collect()), }); if (!res.ok) throw new Error(await res.text()); renderResult(await res.json()); } catch (e) { alert("Prediction failed: " + e.message); } finally { btn.disabled = false; btn.textContent = "Estimate CKD risk"; } } function renderResult(r) { const pct = Math.round(r.probability * 100); const ring = r.risk_band === "High" ? "#dc2626" : r.risk_band === "Moderate" ? "#d97706" : "#16a34a"; $("#resultCard").innerHTML = `
${pct}%

${r.prediction}

${r.risk_band} risk

Model probability of CKD: ${pct}% · decision threshold ${r.threshold}

`; const inds = r.key_indicators || []; $("#indicators").innerHTML = inds.length ? inds .map( (i) => `
${i.label} — ${i.value} (normal ${i.normal_range}) ${i.flag.toUpperCase()}
` ) .join("") : '
No entered lab values fall outside typical reference ranges.
'; $("#disclaimer").textContent = r.disclaimer; $("#resultPanel").hidden = false; $("#resultPanel").scrollIntoView({ behavior: "smooth", block: "nearest" }); } init();