Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<title>Document Analyzer | CTRL + ALT + HEAL</title> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<link rel="stylesheet" href="style.css" /> | |
</head> | |
<body class="bg-[var(--latte-cream)] font-sans text-gray-800 min-h-screen"> | |
<!-- NAVBAR --> | |
<nav | |
class="fixed top-0 left-0 w-full z-50 backdrop-blur-md bg-white/20 border-b border-white/30 shadow-md" | |
> | |
<div class="flex items-center px-6 py-4 max-w-7xl mx-auto"> | |
<a | |
href="index.html" | |
class="text-2xl font-bold text-black hover:text-[var(--tropical-indigo)] transition" | |
>CTRL + ALT + HEAL</a | |
> | |
<!-- Desktop Links --> | |
<ul class="hidden md:flex space-x-6 font-medium text-gray-800 ml-auto"> | |
<li><a href="index.html" class="nav-link">Home</a></li> | |
<li><a href="profile.html" class="nav-link">Profile</a></li> | |
<li><a href="analyzer.html" class="nav-link">Analyzer</a></li> | |
<li><a href="past_data.html" class="nav-link">Past Reports</a></li> | |
<li id="authNavItem"> | |
<a href="login.html" class="nav-link">Login</a> | |
</li> | |
</ul> | |
<!-- Hamburger Menu --> | |
<button | |
id="hamburger" | |
class="md:hidden text-[var(--latte-cream)] text-2xl ml-auto" | |
> | |
☰ | |
</button> | |
</div> | |
<!-- Mobile Menu --> | |
<ul | |
id="mobile-menu" | |
class="hidden flex-col space-y-4 bg-white/30 backdrop-blur-lg border border-white/20 rounded-xl shadow-lg mt-2 p-4 mx-6 md:hidden" | |
> | |
<li><a href="index.html" class="block nav-link">Home</a></li> | |
<li><a href="analyzer.html" class="block nav-link">Analyzer</a></li> | |
<li><a href="profile.html" class="block nav-link">Profile</a></li> | |
<li><a href="login.html" class="block nav-link">Login</a></li> | |
<li><a href="about.html" class="block nav-link">About</a></li> | |
</ul> | |
</nav> | |
<!-- Shared helpers --> | |
<script src="script.js"></script> | |
<script> | |
const hamburger = document.getElementById("hamburger"); | |
const mobileMenu = document.getElementById("mobile-menu"); | |
hamburger.addEventListener("click", () => | |
mobileMenu.classList.toggle("hidden") | |
); | |
// Active page underline | |
const currentPath = window.location.pathname.split("/").pop(); | |
document.querySelectorAll(".nav-link").forEach((link) => { | |
if (link.getAttribute("href") === currentPath) | |
link.classList.add("active-page"); | |
}); | |
</script> | |
<main class="max-w-5xl mx-auto px-4 mb-16 pt-24"> | |
<!-- Upload Section --> | |
<div class="bg-white border border-gray-200 rounded-lg p-6 shadow mb-6"> | |
<h2 class="text-xl font-semibold mb-4"> | |
Upload & Analyze Your Medical PDF or Image | |
</h2> | |
<input | |
type="file" | |
id="pdf-upload" | |
accept=".pdf, image/*" | |
class="w-full mb-4 rounded px-3 py-2" | |
/> | |
<button id="analyze-btn" class="btn-primary px-4 py-2 rounded"> | |
Analyze with AI | |
</button> | |
<p id="loading" class="text-gray-600 mt-2">No file uploaded yet.</p> | |
<p id="auth-status" class="text-sm text-gray-500 mt-1"> | |
Sign in to save and view past analyses. | |
</p> | |
</div> | |
<!-- Extracted Text --> | |
<!-- <div | |
class="bg-[var(--latte-cream)] border border-[var(--wisteria)] rounded-lg p-6 mb-8" | |
> | |
<h3 class="text-lg font-semibold mb-3">Extracted Text</h3> | |
<div | |
id="text-output" | |
class="whitespace-pre-wrap h-60 overflow-auto bg-[#FAFBFC] text-sm border border-[var(--wisteria)] rounded p-4" | |
> | |
OCR results will appear here. | |
</div> | |
</div> --> | |
<!-- AI Findings --> | |
<div | |
class="bg-[var(--latte-cream)] border border-[var(--wisteria)] rounded-lg p-6 mb-8" | |
> | |
<h3 class="text-lg font-semibold mb-3">AI Findings</h3> | |
<div | |
id="recommendations-output" | |
class="bg-[#F9FAFB] text-sm border border-[var(--wisteria)] rounded p-4 space-y-4" | |
> | |
Findings and Recommendations will appear here. | |
</div> | |
</div> | |
</main> | |
<!-- Floating Chat Button --> | |
<button | |
id="chat-toggle" | |
class="fixed bottom-6 right-6 bg-[var(--tropical-indigo)] text-white px-5 py-3 rounded-full shadow-lg hover:scale-105 transition" | |
> | |
💬 Chat | |
</button> | |
<!-- Chatbot Drawer --> | |
<div | |
id="chat-drawer" | |
class="fixed top-16 right-0 w-96 max-w-full h-[calc(100%-4rem)] bg-white shadow-lg border-l border-gray-200 transform translate-x-full transition-transform duration-300 ease-in-out z-40 flex flex-col" | |
> | |
<div class="flex justify-between items-center p-4 border-b"> | |
<h3 class="text-lg font-semibold">Ask Chatbot</h3> | |
<button id="chat-close" class="text-gray-600 hover:text-black text-xl"> | |
✖ | |
</button> | |
</div> | |
<div id="chat-output" class="flex-1 overflow-auto p-4 space-y-2 text-sm"> | |
<p><strong>Chatbot:</strong> Ask me something about your report</p> | |
</div> | |
<div class="flex gap-2 p-4 border-t"> | |
<input | |
type="text" | |
id="user-question" | |
placeholder="Ask a question..." | |
class="flex-1 rounded px-3 py-2 focus:outline-none border" | |
/> | |
<button id="ask-btn" class="btn-primary px-4 py-2 rounded">Ask</button> | |
</div> | |
</div> | |
<script> | |
const chatDrawer = document.getElementById("chat-drawer"); | |
const chatToggle = document.getElementById("chat-toggle"); | |
const chatClose = document.getElementById("chat-close"); | |
chatToggle.addEventListener("click", () => | |
chatDrawer.classList.toggle("translate-x-full") | |
); | |
chatClose.addEventListener("click", () => | |
chatDrawer.classList.add("translate-x-full") | |
); | |
</script> | |
<!-- Firebase Auth + Firestore --> | |
<script type="module"> | |
import { | |
getFirestore, | |
collection, | |
doc, | |
addDoc, | |
serverTimestamp, | |
} from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js"; | |
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-app.js"; | |
import { | |
getAuth, | |
onAuthStateChanged, | |
signOut, | |
} from "https://www.gstatic.com/firebasejs/9.22.0/firebase-auth.js"; | |
const firebaseConfig = { | |
apiKey: "AIzaSyAPhM_Ee7cLzyKHs5zyFy8g5ZOk9-pubRI", | |
authDomain: "login-tutorial-7a9e1.firebaseapp.com", | |
projectId: "login-tutorial-7a9e1", | |
storageBucket: "login-tutorial-7a9e1.firebasestorage.app", | |
messagingSenderId: "491093197824", | |
appId: "1:491093197824:web:9f86659034af7e6a8244e5", | |
measurementId: "G-JM7T9N6ZLZ" | |
}; | |
const app = initializeApp(firebaseConfig); | |
const auth = getAuth(app); | |
const db = getFirestore(app); | |
window.firebaseAuth = auth; | |
window.onAuthStateChanged = onAuthStateChanged; | |
window.firestoreDb = db; | |
window.firestoreHelpers = { collection, doc, addDoc, serverTimestamp }; | |
onAuthStateChanged(auth, (user) => { | |
const authNavItem = document.getElementById("authNavItem"); | |
const profileNavDesktop = document.querySelector( | |
'ul.md\\:flex li a[href="profile.html"]' | |
)?.parentElement; | |
const pastNavDesktop = document.querySelector( | |
'ul.md\\:flex li a[href="past_data.html"]' | |
)?.parentElement; | |
const profileNavMobile = document.querySelector( | |
'#mobile-menu a[href="profile.html"]' | |
)?.parentElement; | |
const pastNavMobile = document.querySelector( | |
'#mobile-menu a[href="past_data.html"]' | |
)?.parentElement; | |
if (authNavItem) { | |
if (user) { | |
authNavItem.innerHTML = | |
'<button onclick="logout()" class="hover:text-[#6B9080] text-red-600">Logout</button>'; | |
if (profileNavDesktop) profileNavDesktop.style.display = "block"; | |
if (pastNavDesktop) pastNavDesktop.style.display = "block"; | |
if (profileNavMobile) profileNavMobile.style.display = "block"; | |
if (pastNavMobile) pastNavMobile.style.display = "block"; | |
} else { | |
authNavItem.innerHTML = | |
'<a href="login.html" class="hover:text-[#6B9080]">Login</a>'; | |
if (profileNavDesktop) profileNavDesktop.style.display = "none"; | |
if (pastNavDesktop) pastNavDesktop.style.display = "none"; | |
if (profileNavMobile) profileNavMobile.style.display = "none"; | |
if (pastNavMobile) pastNavMobile.style.display = "none"; | |
} | |
} | |
}); | |
window.logout = async () => { | |
try { | |
await signOut(auth); | |
localStorage.clear(); | |
window.location.href = "login.html"; | |
} catch (error) { | |
console.error("Error signing out:", error); | |
} | |
}; | |
</script> | |
<!-- Analyzer + Chatbot --> | |
<script type="module"> | |
import { | |
pipeline, | |
env, | |
} from "https://cdn.jsdelivr.net/npm/@xenova/[email protected]"; | |
const loadingEl = document.getElementById("loading"); | |
// const textOutput = document.getElementById("text-output"); | |
const recsOutput = document.getElementById("recommendations-output"); | |
const chat = document.getElementById("chat-output"); | |
const authStatus = document.getElementById("auth-status"); | |
let extractedText = ""; | |
let currentUser = null; | |
function renderRecCard(rec, idx) { | |
const sev = (rec.severity || "").toLowerCase(); | |
const sevClass = sev.includes("severe") | |
? "badge-high" | |
: sev.includes("moderate") | |
? "badge-medium" | |
: "badge-low"; | |
return ` | |
<div class="rec-card"> | |
<div class="flex items-center justify-between"> | |
<h4 class="rec-title">Finding ${idx + 1}: ${ | |
rec.findings || "N/A" | |
}</h4> | |
<span class="rec-badge ${sevClass}">${rec.severity || "—"}</span> | |
</div> | |
<ul class="rec-content space-y-1"> | |
<li><em>Recommendations:</em> | |
<ul class="list-disc list-inside ml-6">${( | |
rec.recommendations || [] | |
) | |
.map((r) => `<li>${r}</li>`) | |
.join("")}</ul> | |
</li> | |
<li><em>Treatment:</em> ${ | |
rec.treatment_suggestions || "Not available" | |
}</li> | |
<li><em>Home Care:</em> | |
<ul class="list-disc list-inside ml-6">${( | |
rec.home_care_guidance || [] | |
) | |
.map((r) => `<li>${r}</li>`) | |
.join("")}</ul> | |
</li> | |
${ | |
rec.info_link | |
? `<li><a href="${rec.info_link}" target="_blank" class="rec-link">Learn more</a></li>` | |
: "" | |
} | |
</ul> | |
</div>`; | |
} | |
window.onAuthStateChanged(window.firebaseAuth, (user) => { | |
currentUser = user; | |
authStatus.textContent = user | |
? `Signed in as ${user.email || user.uid}` | |
: "Not signed in. Sign in to save and view past analyses."; | |
}); | |
document | |
.getElementById("pdf-upload") | |
.addEventListener("change", function () { | |
loadingEl.textContent = this.files.length | |
? `File selected: ${this.files[0].name}` | |
: "No file uploaded yet."; | |
}); | |
async function postReportToBackend(report) { | |
try { | |
const response = await fetch(api("save_report/"), { | |
method: "POST", | |
headers: { "Content-Type": "application/json" }, | |
body: JSON.stringify(report), | |
}); | |
if (!response.ok) | |
throw new Error(`HTTP error! status: ${response.status}`); | |
const data = await response.json(); | |
console.log("Report sent to backend:", data); | |
} catch (error) { | |
console.error("Error sending report:", error); | |
} | |
} | |
document | |
.getElementById("analyze-btn") | |
.addEventListener("click", async () => { | |
const file = document.getElementById("pdf-upload").files[0]; | |
if (!file) { | |
loadingEl.textContent = "Please upload a file first."; | |
return; | |
} | |
loadingEl.textContent = "Processing with AI..."; | |
recsOutput.textContent = ""; | |
const formData = new FormData(); | |
formData.append("file", file); | |
formData.append("model", "bert"); | |
let data; | |
try { | |
const res = await fetch(api("analyze/"), { | |
method: "POST", | |
body: formData, | |
}); | |
if (!res.ok) throw new Error(await res.text()); | |
data = await res.json(); | |
} catch (err) { | |
console.error(err); | |
loadingEl.textContent = "Error during analysis: " + err.message; | |
return; | |
} | |
extractedText = data.ocr_text || ""; | |
const recs = Array.isArray(data.Detected_Anomolies) | |
? data.Detected_Anomolies | |
: data.Detected_Anomolies | |
? [data.Detected_Anomolies] | |
: []; | |
recsOutput.innerHTML = recs.length | |
? recs.map(renderRecCard).join("") | |
: "No recommendations found."; | |
if (currentUser) { | |
await postReportToBackend({ | |
user_id: currentUser.email, | |
report_date: new Date(), | |
ocr_text: extractedText, | |
anomalies: JSON.stringify(recs), | |
}); | |
} | |
loadingEl.textContent = "Analysis complete."; | |
}); | |
document.getElementById("ask-btn").onclick = async () => { | |
const q = document.getElementById("user-question").value.trim(); | |
if (!q) return; | |
if (!extractedText) { | |
alert("Please analyze a document first."); | |
return; | |
} | |
chat.innerHTML += `<p><strong>You:</strong> ${q}</p>`; | |
chat.innerHTML += `<p><strong>Chatbot:</strong> <em>Thinking...</em></p>`; | |
chat.scrollTop = chat.scrollHeight; | |
try { | |
const res = await fetch(api("chat/"), { | |
method: "POST", | |
headers: { "Content-Type": "application/json" }, | |
body: JSON.stringify({ | |
question: q, | |
user_id: currentUser ? currentUser.email : "anonymous", | |
}), | |
}); | |
if (!res.ok) throw new Error(await res.text()); | |
const data = await res.json(); | |
const last = chat.querySelectorAll("p"); | |
last[ | |
last.length - 1 | |
].innerHTML = `<strong>Chatbot:</strong> ${data.answer}`; | |
} catch (err) { | |
console.error("Error:", err); | |
const last = chat.querySelectorAll("p"); | |
last[ | |
last.length - 1 | |
].innerHTML = `<strong>Chatbot:</strong> Sorry, error: ${err.message}`; | |
} | |
document.getElementById("user-question").value = ""; | |
chat.scrollTop = chat.scrollHeight; | |
}; | |
</script> | |
</body> | |
</html> | |