Vineela Gampa commited on
Commit
e871e34
·
unverified ·
1 Parent(s): 6a15a6c

minor UI Fixes

Browse files
Files changed (6) hide show
  1. data/app.db +0 -0
  2. web/analyzer.html +5 -6
  3. web/index.html +5 -7
  4. web/login.html +0 -4
  5. web/past_data.html +343 -240
  6. web/profile.html +196 -188
data/app.db CHANGED
Binary files a/data/app.db and b/data/app.db differ
 
web/analyzer.html CHANGED
@@ -12,16 +12,15 @@
12
  <nav
13
  class="fixed top-0 left-0 w-full z-50 backdrop-blur-md bg-white/20 border-b border-white/30 shadow-md"
14
  >
15
- <div class="flex items-center px-6 py-4 max-w-7xl mx-auto">
16
  <a
17
  href="index.html"
18
- class="text-2xl font-bold text-black hover:text-[var(--tropical-indigo)] transition"
19
  >CTRL + ALT + HEAL</a
20
  >
21
 
22
  <!-- Desktop Links -->
23
  <ul class="hidden md:flex space-x-6 font-medium text-gray-800 ml-auto">
24
- <li><a href="index.html" class="nav-link">Home</a></li>
25
  <li><a href="profile.html" class="nav-link">Profile</a></li>
26
  <li><a href="analyzer.html" class="nav-link">Analyzer</a></li>
27
  <li><a href="past_data.html" class="nav-link">Past Reports</a></li>
@@ -69,10 +68,10 @@
69
  });
70
  </script>
71
 
72
- <main class="max-w-5xl mx-auto px-4 mb-16 pt-24">
73
  <!-- Upload Section -->
74
  <div class="bg-white border border-gray-200 rounded-lg p-6 shadow mb-6">
75
- <h2 class="text-xl font-semibold mb-4">
76
  Upload & Analyze Your Medical PDF or Image
77
  </h2>
78
  <input
@@ -107,7 +106,7 @@
107
  <div
108
  class="bg-[var(--latte-cream)] border border-[var(--wisteria)] rounded-lg p-6 mb-8"
109
  >
110
- <h3 class="text-lg font-semibold mb-3">AI Findings</h3>
111
  <div
112
  id="recommendations-output"
113
  class="bg-[#F9FAFB] text-sm border border-[var(--wisteria)] rounded p-4 space-y-4"
 
12
  <nav
13
  class="fixed top-0 left-0 w-full z-50 backdrop-blur-md bg-white/20 border-b border-white/30 shadow-md"
14
  >
15
+ <div class="flex justify-between items-center w-full px-6 py-4">
16
  <a
17
  href="index.html"
18
+ class="text-2xl font-bold text-black hover:text-sky-700 transition"
19
  >CTRL + ALT + HEAL</a
20
  >
21
 
22
  <!-- Desktop Links -->
23
  <ul class="hidden md:flex space-x-6 font-medium text-gray-800 ml-auto">
 
24
  <li><a href="profile.html" class="nav-link">Profile</a></li>
25
  <li><a href="analyzer.html" class="nav-link">Analyzer</a></li>
26
  <li><a href="past_data.html" class="nav-link">Past Reports</a></li>
 
68
  });
69
  </script>
70
 
71
+ <main class="container mx-auto px-6 pt-24">
72
  <!-- Upload Section -->
73
  <div class="bg-white border border-gray-200 rounded-lg p-6 shadow mb-6">
74
+ <h2 class="text-xl font-semibold mb-4 text-sky-700">
75
  Upload & Analyze Your Medical PDF or Image
76
  </h2>
77
  <input
 
106
  <div
107
  class="bg-[var(--latte-cream)] border border-[var(--wisteria)] rounded-lg p-6 mb-8"
108
  >
109
+ <h3 class="text-lg font-semibold mb-3 text-sky-700">AI Findings</h3>
110
  <div
111
  id="recommendations-output"
112
  class="bg-[#F9FAFB] text-sm border border-[var(--wisteria)] rounded p-4 space-y-4"
web/index.html CHANGED
@@ -7,14 +7,12 @@
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <link rel="stylesheet" href="style.css" />
9
  </head>
10
- <body>
11
  <!-- NAVBAR -->
12
  <nav
13
  class="fixed top-0 left-0 w-full z-50 backdrop-blur-md bg-white/20 border-b border-white/30 shadow-md"
14
  >
15
- <div
16
- class="max-w-6xl mx-auto px-6 py-4 flex justify-between items-center"
17
- >
18
  <!-- Logo -->
19
  <a
20
  href="index.html"
@@ -230,8 +228,8 @@
230
  storageBucket: "login-tutorial-7a9e1.firebasestorage.app",
231
  messagingSenderId: "491093197824",
232
  appId: "1:491093197824:web:9f86659034af7e6a8244e5",
233
- measurementId: "G-JM7T9N6ZLZ"
234
- };
235
 
236
  const app = initializeApp(firebaseConfig);
237
  const auth = getAuth(app);
@@ -311,4 +309,4 @@
311
  }
312
  </script>
313
  </body>
314
- </html>
 
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <link rel="stylesheet" href="style.css" />
9
  </head>
10
+ <body class="min-h-screen flex flex-col">
11
  <!-- NAVBAR -->
12
  <nav
13
  class="fixed top-0 left-0 w-full z-50 backdrop-blur-md bg-white/20 border-b border-white/30 shadow-md"
14
  >
15
+ <div class="flex justify-between items-center w-full px-6 py-4">
 
 
16
  <!-- Logo -->
17
  <a
18
  href="index.html"
 
228
  storageBucket: "login-tutorial-7a9e1.firebasestorage.app",
229
  messagingSenderId: "491093197824",
230
  appId: "1:491093197824:web:9f86659034af7e6a8244e5",
231
+ measurementId: "G-JM7T9N6ZLZ",
232
+ };
233
 
234
  const app = initializeApp(firebaseConfig);
235
  const auth = getAuth(app);
 
309
  }
310
  </script>
311
  </body>
312
+ </html>
web/login.html CHANGED
@@ -70,10 +70,6 @@
70
  <!-- Desktop Menu -->
71
  <ul class="hidden md:flex space-x-6 font-medium text-gray-800">
72
  <li><a href="index.html" class="nav-link">Home</a></li>
73
- <li><a href="profile.html" class="nav-link">Profile</a></li>
74
- <li><a href="analyzer.html" class="nav-link">Analyzer</a></li>
75
- <li><a href="past_data.html" class="nav-link">Past Report</a></li>
76
-
77
  <button
78
  id="hamburger"
79
  class="md:hidden text-[var(--latte-cream)] text-2xl"
 
70
  <!-- Desktop Menu -->
71
  <ul class="hidden md:flex space-x-6 font-medium text-gray-800">
72
  <li><a href="index.html" class="nav-link">Home</a></li>
 
 
 
 
73
  <button
74
  id="hamburger"
75
  class="md:hidden text-[var(--latte-cream)] text-2xl"
web/past_data.html CHANGED
@@ -1,140 +1,196 @@
1
  <!DOCTYPE html>
2
  <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <title>Past Analyzes - CTRL + ALT + HEAL</title>
6
- <script src="https://cdn.tailwindcss.com"></script>
7
- <script src="stylesheet" href="style.css"></script>
8
- <script src="script.js"></script>
9
- </head>
10
- <body class="bg-[#F7F8F9] min-h-screen">
11
- <nav class="bg-white border border-gray-200 px-6 py-4 mb-8">
12
- <div class="container mx-auto flex justify-between items-center">
13
- <a href="index.html" class="text-2xl font-bold text-[#6B9080]">CTRL + ALT + HEAL</a>
14
- <ul class="flex space-x-6 text-sm font-medium text-gray-700">
15
- <li><a href="index.html" class="hover:text-[#6B9080]">Home</a></li>
16
- <li><a href="analyzer.html" class="hover:text-[#6B9080]">Analyzer</a></li>
17
- <li><a href="past_data.html" class="hover:text-[#6B9080]">Past Reports</a></li>
18
- <li><a href="profile.html" class="hover:text-[#6B9080]">Profile</a></li>
19
- <li id="authNavItem"><a href="login.html" class="hover:text-[#6B9080]">Login</a></li>
20
- </ul>
21
- </div>
22
- </nav>
23
-
24
- <main class="max-w-4xl mx-auto px-4">
25
- <h1 class="text-2xl font-bold text-green-700 mb-4">Your Past Analyzes</h1>
26
- <p id="auth-status" class="text-sm text-gray-500 mb-6">Checking sign-in status…</p>
27
-
28
- <div id="recs-container" class="space-y-4">
29
- <p class="text-sm text-gray-500"></p>
30
- </div>
31
- </main>
32
-
33
-
34
-
35
-
36
- <script type="module">
37
- import { initializeApp } from 'https://www.gstatic.com/firebasejs/9.22.0/firebase-app.js';
38
- import { getAuth, onAuthStateChanged, signOut } from 'https://www.gstatic.com/firebasejs/9.22.0/firebase-auth.js';
39
- import { getFirestore, collection, query, orderBy, getDocs } from 'https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js';
40
-
41
- const firebaseConfig = {
42
- apiKey: "AIzaSyAPhM_Ee7cLzyKHs5zyFy8g5ZOk9-pubRI",
43
- authDomain: "login-tutorial-7a9e1.firebaseapp.com",
44
- projectId: "login-tutorial-7a9e1",
45
- storageBucket: "login-tutorial-7a9e1.firebasestorage.app",
46
- messagingSenderId: "491093197824",
47
- appId: "1:491093197824:web:9f86659034af7e6a8244e5",
48
- measurementId: "G-JM7T9N6ZLZ"
49
- };
50
- const app = initializeApp(firebaseConfig);
51
- const auth = getAuth(app);
52
- const db = getFirestore(app);
53
-
54
- let currentUser = null;
55
- onAuthStateChanged(auth, (user) => {
56
- const authNavItem = document.getElementById("authNavItem");
57
- if (authNavItem) {
58
- if (user) {
59
- authNavItem.innerHTML =
60
- '<button onclick="logout()" class="hover:text-[#6B9080] text-red-600">Logout</button>';
61
- } else {
62
- authNavItem.innerHTML =
63
- '<a href="login.html" class="hover:text-[#6B9080]">Login</a>';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  }
 
 
 
 
 
 
 
 
 
65
  }
66
- });
67
- window.logout = async () => {
68
- try {
69
- await signOut(auth);
70
- localStorage.clear();
71
- window.location.href = "login.html";
72
- } catch (error) {
73
- console.error("Error signing out:", error);
 
 
74
  }
75
- };
76
- const statusEl = document.getElementById("auth-status");
77
- const recsEl = document.getElementById("recs-container");
78
-
79
- function dbg(label, data) {
80
- console.log(`[PastReports][${new Date().toISOString()}] ${label}:`, data);
81
- }
82
- const SHOW_DEBUG = true;
83
- let debugBannerEl = null;
84
- if (SHOW_DEBUG) {
85
- debugBannerEl = document.createElement('div');
86
- debugBannerEl.style.cssText = 'font-size:12px;margin-top:6px;color:#6b7280;';
87
- statusEl.after(debugBannerEl);
88
- }
89
- function setDebugBanner(msg) {
90
- if (debugBannerEl) debugBannerEl.textContent = `Debug: ${msg}`;
91
- }
92
 
 
 
 
 
93
 
94
- window.toggleBox = function (id) {
95
- const content = document.getElementById(`box-content-${id}`);
96
- const arrow = document.getElementById(`box-arrow-${id}`);
97
- if (!content || !arrow) return;
 
 
 
 
 
 
98
 
99
- const open = content.style.maxHeight && content.style.maxHeight !== "0px";
100
- if (open) {
101
- content.style.maxHeight = "0px";
102
- arrow.style.transform = "rotate(0deg)";
103
- } else {
104
- content.style.maxHeight = content.scrollHeight + "px";
105
- arrow.textContent = "Close Full View";
106
  }
107
- };
108
-
109
- function safeJson(x) {
110
- try { return typeof x === "string" ? JSON.parse(x) : x; } catch { return x; }
111
- }
112
- function topAnomalyLabels(anoms, n = 3) {
113
- const arr = Array.isArray(anoms) ? anoms : [];
114
- return arr.slice(0, n).map(a =>
115
- (a?.findings ?? a?.condition ?? a?.measurement ?? "").toString()
116
- ).filter(Boolean);
117
- }
118
- function deriveReportName(row) {
119
- if (row.filename) return row.filename;
120
- if (row.report_date) return `Report ${new Date(row.report_date).toLocaleDateString()}`;
121
- if (row.ocr_text) {
122
- const first = row.ocr_text.split(/\r?\n/).find(l => l.trim());
123
- if (first) return first.slice(0, 50) + (first.length > 50 ? "…" : "");
 
124
  }
125
- return "Report";
126
- }
127
 
128
- function renderBackendReportCollapsible(row, idx) {
129
- const id = `r${row.id || idx}`;
130
- const created = row.created_at ? new Date(row.created_at).toLocaleString() : "";
131
- const reportName = deriveReportName(row);
 
 
132
 
133
- const anomaliesRaw = safeJson(row.anomalies);
134
- const anomalies = Array.isArray(anomaliesRaw) ? anomaliesRaw : [];
135
- const top3 = topAnomalyLabels(anomalies, 3);
136
 
137
- return `
138
  <div class="bg-white border border-gray-200 rounded-lg shadow">
139
  <!-- Header (clickable) -->
140
  <button
@@ -143,15 +199,19 @@
143
  >
144
  <div class="min-w-0">
145
  <div class="text-xs text-gray-500">${created || "—"}</div>
146
- <div class="font-semibold text-green-700 truncate">${reportName}</div>
147
  ${
148
  top3.length
149
  ? `<div class="flex flex-wrap gap-2 mt-2">
150
- ${top3.map(t => `
 
 
151
  <span class="text-xs bg-gray-100 border border-gray-200 px-2 py-1 rounded">
152
  ${t}
153
  </span>
154
- `).join("")}
 
 
155
  </div>`
156
  : `<div class="text-xs text-gray-400 mt-2">No anomalies detected</div>`
157
  }
@@ -165,25 +225,32 @@
165
  class="overflow-hidden transition-all duration-300 max-h-0 border-t border-gray-200"
166
  >
167
  <div class="p-4 space-y-3">
168
- <!-- OCR -->
169
- <div>
170
- <div class="font-medium mb-1">OCR Text</div>
171
- <pre class="whitespace-pre-wrap text-sm text-gray-700 bg-gray-50 border rounded p-3">${row.ocr_text || ""}</pre>
172
- </div>
173
-
174
  <!-- Full anomalies list -->
175
  ${
176
  anomalies.length
177
  ? `
178
  <div class="pt-2 border-t border-gray-100">
179
  <div class="font-medium mb-2">Findings</div>
180
- ${anomalies.map((a, i) => `
 
 
181
  <div class="border-t border-gray-200 pt-2 mt-2">
182
- <div class="font-medium">Finding ${i + 1}: ${a?.findings ?? ""}</div>
183
- <div class="text-sm text-gray-600">Severity: ${a?.severity ?? ""}</div>
184
- <div class="text-sm text-gray-600">Recommendations: ${(Array.isArray(a?.recommendations) ? a.recommendations : []).join(", ")}</div>
 
 
 
 
 
 
 
 
 
185
  </div>
186
- `).join("")}
 
 
187
  </div>
188
  `
189
  : ""
@@ -192,22 +259,24 @@
192
  </div>
193
  </div>
194
  `;
195
- }
196
-
197
- function renderFirestoreReportCollapsible(item, idx) {
198
- const id = `f${idx}`;
199
- const created =
200
- item?.createdAt && item.createdAt.toDate
201
- ? item.createdAt.toDate().toLocaleString()
202
- : "";
203
- const reportName = item?.reportDate
204
- ? `Report ${item.reportDate}`
205
- : "Report";
206
- const anomalies = Array.isArray(item?.resolutions) ? item.resolutions : [];
207
-
208
- const top3 = topAnomalyLabels(anomalies, 3);
209
 
210
- return `
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  <div class="bg-white border border-gray-200 rounded-lg shadow">
212
  <!-- Header (clickable) -->
213
  <button
@@ -216,7 +285,7 @@
216
  >
217
  <div class="min-w-0">
218
  <div class="text-xs text-gray-500">${created || "—"}</div>
219
- <div class="font-semibold text-green-700 truncate">${reportName}</div>
220
  ${
221
  top3.length
222
  ? `<div class="flex flex-wrap gap-2 mt-2">
@@ -242,12 +311,7 @@
242
  >
243
  <div class="p-4 space-y-3">
244
  <!-- OCR -->
245
- <div>
246
- <div class="font-medium mb-1">OCR Text</div>
247
- <pre class="whitespace-pre-wrap text-sm text-gray-700 bg-gray-50 border rounded p-3">${
248
- item?.ocr_text || ""
249
- }</pre>
250
- </div>
251
 
252
  <!-- Full anomalies list -->
253
  ${
@@ -259,8 +323,12 @@
259
  .map(
260
  (a, i) => `
261
  <div class="border-t border-gray-200 pt-2 mt-2">
262
- <div class="font-medium">Finding ${i + 1}: ${a?.findings ?? ""}</div>
263
- <div class="text-sm text-gray-600">Severity: ${a?.severity ?? ""}</div>
 
 
 
 
264
  <div class="text-sm text-gray-600">Recommendations: ${
265
  Array.isArray(a?.recommendations)
266
  ? a.recommendations.join(", ")
@@ -277,97 +345,132 @@
277
  </div>
278
  </div>
279
  `;
280
- }
281
-
282
-
283
- function renderBackendRow(row) {
284
- const created = row.created_at ? new Date(row.created_at).toLocaleString() : '';
285
- const reportDate = row.report_date || 'N/A';
286
- const anomalies = (() => {
287
- try { return Array.isArray(row.anomalies) ? row.anomalies : JSON.parse(row.anomalies || '[]'); }
288
- catch { return []; }
289
- })();
290
 
291
- return `
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292
  <div class="bg-white border border-gray-200 rounded-lg p-4 shadow">
293
  <div class="flex justify-between mb-2">
294
- <div class="font-semibold text-green-700">Report: ${reportDate}</div>
295
  <div class="text-xs text-gray-500">${created}</div>
296
  </div>
297
- <pre class="whitespace-pre-wrap text-sm text-gray-700 mb-2">${row.ocr_text || ''}</pre>
298
- ${anomalies.map((r,i)=>`
 
 
 
 
299
  <div class="border-t border-gray-200 pt-2 mt-2">
300
- <div class="font-medium">Finding ${i+1}: ${r?.findings ?? ''}</div>
301
- <div class="text-sm text-gray-600">Severity: ${r?.severity ?? ''}</div>
302
- <div class="text-sm text-gray-600">Recommendations: ${(Array.isArray(r?.recommendations)?r.recommendations:[]).join(', ')}</div>
 
 
 
 
 
 
 
 
 
303
  </div>
304
- `).join('')}
 
 
305
  </div>`;
306
- }
307
-
308
- function renderList(htmlItems) {
309
- recsEl.innerHTML = htmlItems && htmlItems.length
310
- ? htmlItems.join('')
311
- : '<p class="text-sm text-gray-500">No saved analyses yet.</p>';
312
- }
313
-
314
- async function loadFromBackend(user) {
315
- try {
316
- const userId = user.email || user.uid;
317
- const url = api('reports/', { user_id: userId });
318
- dbg('Backend URL', url);
319
- const res = await fetch(url, { method: 'GET' });
320
- dbg('Backend HTTP status', res.status);
321
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
322
- const data = await res.json();
323
- dbg('Backend payload', data);
324
- return Array.isArray(data) ? data : [];
325
- } catch (e) {
326
- console.error('Backend fetch failed:', e);
327
- dbg('Backend fetch error', e.message || e);
328
- return [];
329
  }
330
- }
331
 
332
- async function loadFromFirestore(user) {
333
- try {
334
- const q = query(collection(db, 'users', user.uid, 'analyses'), orderBy('createdAt', 'desc'));
335
- dbg('Firestore query uid', user.uid);
336
- const snap = await getDocs(q);
337
- const rows = snap.docs.map(d => d.data());
338
- dbg('Firestore count', rows.length);
339
- return rows;
340
- } catch (e) {
341
- console.error('Firestore fetch failed:', e);
342
- dbg('Firestore fetch error', e.message || e);
343
- return [];
344
  }
345
- }
346
 
347
- onAuthStateChanged(auth, async (user) => {
348
- if (!user) {
349
- statusEl.textContent = 'Not signed in.';
350
- renderList([]);
351
- setDebugBanner('User is not signed in.');
352
- return;
 
 
 
 
 
 
 
 
 
 
353
  }
354
 
355
- statusEl.textContent = `Signed in as ${user.email || user.uid}`;
356
-
357
- const backendRows = await loadFromBackend(user);
358
- const firestoreRows = await loadFromFirestore(user);
 
 
 
 
 
 
 
 
 
 
 
 
 
359
 
360
- setDebugBanner(`backend: ${backendRows.length} | firestore: ${firestoreRows.length}`);
361
- dbg('Summary', { backend: backendRows.length, firestore: firestoreRows.length });
 
 
 
 
 
362
 
363
- if (backendRows.length > 0) {
364
- recsEl.innerHTML = backendRows.map((row, i) => renderBackendReportCollapsible(row, i)).join("");
365
- } else if (firestoreRows.length > 0) {
366
- recsEl.innerHTML = firestoreRows.map((row, i) => renderFirestoreReportCollapsible(row, i)).join("");
367
- } else {
368
- recsEl.innerHTML = '<p class="text-sm text-gray-500">No saved analyses yet.</p>';
369
- }
370
- });
371
- </script>
372
- </body>
373
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <!DOCTYPE html>
2
  <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>Past Analyzes - CTRL + ALT + HEAL</title>
6
+ <script src="https://cdn.tailwindcss.com"></script>
7
+ <script src="stylesheet" href="style.css"></script>
8
+ <script src="script.js"></script>
9
+ </head>
10
+ <body class="bg-[#F7F8F9] min-h-screen">
11
+ <nav
12
+ class="fixed top-0 left-0 w-full z-50 backdrop-blur-md bg-white/20 border-b border-white/30 shadow-md"
13
+ >
14
+ <div class="flex justify-between items-center w-full px-6 py-4">
15
+ <a
16
+ href="index.html"
17
+ class="text-2xl font-bold text-black hover:text-[var(--tropical-indigo)] transition"
18
+ >
19
+ CTRL + ALT + HEAL
20
+ </a>
21
+ <ul class="hidden md:flex space-x-6 font-medium text-gray-800">
22
+ <li>
23
+ <a
24
+ href="profile.html"
25
+ class="block text-gray-800 hover:text-[var(--tropical-indigo)]"
26
+ >Profile</a
27
+ >
28
+ </li>
29
+ <li>
30
+ <a
31
+ href="analyzer.html"
32
+ class="block text-gray-800 hover:text-[var(--tropical-indigo)]"
33
+ >Analyzer</a
34
+ >
35
+ </li>
36
+ <li>
37
+ <a
38
+ href="past_data.html"
39
+ class="block text-gray-800 hover:text-[var(--tropical-indigo)]"
40
+ >Past Reports</a
41
+ >
42
+ </li>
43
+
44
+ <li id="authNavItem">
45
+ <a
46
+ href="login.html"
47
+ class="block text-gray-800 hover:text-[var(--tropical-indigo)]"
48
+ >Login</a
49
+ >
50
+ </li>
51
+ </ul>
52
+ </div>
53
+ </nav>
54
+
55
+ <main class="container mx-auto px-6 pt-24">
56
+ <h2 class="text-xl font-semibold mb-4 text-sky-700">
57
+ Your Past Analyzes
58
+ </h2>
59
+ <p id="auth-status" class="text-sm text-gray-500 mb-6">
60
+ Checking sign-in status…
61
+ </p>
62
+
63
+ <div id="recs-container" class="space-y-4">
64
+ <p class="text-sm text-gray-500"></p>
65
+ </div>
66
+ </main>
67
+
68
+ <script type="module">
69
+ import { initializeApp } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-app.js";
70
+ import {
71
+ getAuth,
72
+ onAuthStateChanged,
73
+ signOut,
74
+ } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-auth.js";
75
+ import {
76
+ getFirestore,
77
+ collection,
78
+ query,
79
+ orderBy,
80
+ getDocs,
81
+ } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";
82
+
83
+ const firebaseConfig = {
84
+ apiKey: "AIzaSyAPhM_Ee7cLzyKHs5zyFy8g5ZOk9-pubRI",
85
+ authDomain: "login-tutorial-7a9e1.firebaseapp.com",
86
+ projectId: "login-tutorial-7a9e1",
87
+ storageBucket: "login-tutorial-7a9e1.firebasestorage.app",
88
+ messagingSenderId: "491093197824",
89
+ appId: "1:491093197824:web:9f86659034af7e6a8244e5",
90
+ measurementId: "G-JM7T9N6ZLZ",
91
+ };
92
+ const app = initializeApp(firebaseConfig);
93
+ const auth = getAuth(app);
94
+ const db = getFirestore(app);
95
+
96
+ let currentUser = null;
97
+ onAuthStateChanged(auth, (user) => {
98
+ const authNavItem = document.getElementById("authNavItem");
99
+ if (authNavItem) {
100
+ if (user) {
101
+ authNavItem.innerHTML =
102
+ '<button onclick="logout()" class="hover:text-[#6B9080] text-red-600">Logout</button>';
103
+ } else {
104
+ authNavItem.innerHTML =
105
+ '<a href="login.html" class="hover:text-[#6B9080]">Login</a>';
106
+ }
107
+ }
108
+ });
109
+ window.logout = async () => {
110
+ try {
111
+ await signOut(auth);
112
+ localStorage.clear();
113
+ window.location.href = "login.html";
114
+ } catch (error) {
115
+ console.error("Error signing out:", error);
116
  }
117
+ };
118
+ const statusEl = document.getElementById("auth-status");
119
+ const recsEl = document.getElementById("recs-container");
120
+
121
+ function dbg(label, data) {
122
+ console.log(
123
+ `[PastReports][${new Date().toISOString()}] ${label}:`,
124
+ data
125
+ );
126
  }
127
+ const SHOW_DEBUG = true;
128
+ let debugBannerEl = null;
129
+ if (SHOW_DEBUG) {
130
+ debugBannerEl = document.createElement("div");
131
+ debugBannerEl.style.cssText =
132
+ "font-size:12px;margin-top:6px;color:#6b7280;";
133
+ statusEl.after(debugBannerEl);
134
+ }
135
+ function setDebugBanner(msg) {
136
+ if (debugBannerEl) debugBannerEl.textContent = `Debug: ${msg}`;
137
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
+ window.toggleBox = function (id) {
140
+ const content = document.getElementById(`box-content-${id}`);
141
+ const arrow = document.getElementById(`box-arrow-${id}`);
142
+ if (!content || !arrow) return;
143
 
144
+ const open =
145
+ content.style.maxHeight && content.style.maxHeight !== "0px";
146
+ if (open) {
147
+ content.style.maxHeight = "0px";
148
+ arrow.style.transform = "rotate(0deg)";
149
+ } else {
150
+ content.style.maxHeight = content.scrollHeight + "px";
151
+ arrow.textContent = "Close Full View";
152
+ }
153
+ };
154
 
155
+ function safeJson(x) {
156
+ try {
157
+ return typeof x === "string" ? JSON.parse(x) : x;
158
+ } catch {
159
+ return x;
160
+ }
 
161
  }
162
+ function topAnomalyLabels(anoms, n = 3) {
163
+ const arr = Array.isArray(anoms) ? anoms : [];
164
+ return arr
165
+ .slice(0, n)
166
+ .map((a) =>
167
+ (a?.findings ?? a?.condition ?? a?.measurement ?? "").toString()
168
+ )
169
+ .filter(Boolean);
170
+ }
171
+ function deriveReportName(row) {
172
+ if (row.filename) return row.filename;
173
+ if (row.report_date)
174
+ return `Report ${new Date(row.report_date).toLocaleDateString()}`;
175
+ if (row.ocr_text) {
176
+ const first = row.ocr_text.split(/\r?\n/).find((l) => l.trim());
177
+ if (first) return first.slice(0, 50) + (first.length > 50 ? "…" : "");
178
+ }
179
+ return "Report";
180
  }
 
 
181
 
182
+ function renderBackendReportCollapsible(row, idx) {
183
+ const id = `r${row.id || idx}`;
184
+ const created = row.created_at
185
+ ? new Date(row.created_at).toLocaleString()
186
+ : "";
187
+ const reportName = deriveReportName(row);
188
 
189
+ const anomaliesRaw = safeJson(row.anomalies);
190
+ const anomalies = Array.isArray(anomaliesRaw) ? anomaliesRaw : [];
191
+ const top3 = topAnomalyLabels(anomalies, 3);
192
 
193
+ return `
194
  <div class="bg-white border border-gray-200 rounded-lg shadow">
195
  <!-- Header (clickable) -->
196
  <button
 
199
  >
200
  <div class="min-w-0">
201
  <div class="text-xs text-gray-500">${created || "—"}</div>
202
+ <div class="font-semibold text-sky-700 truncate">${reportName}</div>
203
  ${
204
  top3.length
205
  ? `<div class="flex flex-wrap gap-2 mt-2">
206
+ ${top3
207
+ .map(
208
+ (t) => `
209
  <span class="text-xs bg-gray-100 border border-gray-200 px-2 py-1 rounded">
210
  ${t}
211
  </span>
212
+ `
213
+ )
214
+ .join("")}
215
  </div>`
216
  : `<div class="text-xs text-gray-400 mt-2">No anomalies detected</div>`
217
  }
 
225
  class="overflow-hidden transition-all duration-300 max-h-0 border-t border-gray-200"
226
  >
227
  <div class="p-4 space-y-3">
 
 
 
 
 
 
228
  <!-- Full anomalies list -->
229
  ${
230
  anomalies.length
231
  ? `
232
  <div class="pt-2 border-t border-gray-100">
233
  <div class="font-medium mb-2">Findings</div>
234
+ ${anomalies
235
+ .map(
236
+ (a, i) => `
237
  <div class="border-t border-gray-200 pt-2 mt-2">
238
+ <div class="font-medium">Finding ${i + 1}: ${
239
+ a?.findings ?? ""
240
+ }</div>
241
+ <div class="text-sm text-gray-600">Severity: ${
242
+ a?.severity ?? ""
243
+ }</div>
244
+ <div class="text-sm text-gray-600">Recommendations: ${(Array.isArray(
245
+ a?.recommendations
246
+ )
247
+ ? a.recommendations
248
+ : []
249
+ ).join(", ")}</div>
250
  </div>
251
+ `
252
+ )
253
+ .join("")}
254
  </div>
255
  `
256
  : ""
 
259
  </div>
260
  </div>
261
  `;
262
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
263
 
264
+ function renderFirestoreReportCollapsible(item, idx) {
265
+ const id = `f${idx}`;
266
+ const created =
267
+ item?.createdAt && item.createdAt.toDate
268
+ ? item.createdAt.toDate().toLocaleString()
269
+ : "";
270
+ const reportName = item?.reportDate
271
+ ? `Report ${item.reportDate}`
272
+ : "Report";
273
+ const anomalies = Array.isArray(item?.resolutions)
274
+ ? item.resolutions
275
+ : [];
276
+
277
+ const top3 = topAnomalyLabels(anomalies, 3);
278
+
279
+ return `
280
  <div class="bg-white border border-gray-200 rounded-lg shadow">
281
  <!-- Header (clickable) -->
282
  <button
 
285
  >
286
  <div class="min-w-0">
287
  <div class="text-xs text-gray-500">${created || "—"}</div>
288
+ <div class="font-semibold text-blue-700 truncate">${reportName}</div>
289
  ${
290
  top3.length
291
  ? `<div class="flex flex-wrap gap-2 mt-2">
 
311
  >
312
  <div class="p-4 space-y-3">
313
  <!-- OCR -->
314
+
 
 
 
 
 
315
 
316
  <!-- Full anomalies list -->
317
  ${
 
323
  .map(
324
  (a, i) => `
325
  <div class="border-t border-gray-200 pt-2 mt-2">
326
+ <div class="font-medium">Finding ${i + 1}: ${
327
+ a?.findings ?? ""
328
+ }</div>
329
+ <div class="text-sm text-gray-600">Severity: ${
330
+ a?.severity ?? ""
331
+ }</div>
332
  <div class="text-sm text-gray-600">Recommendations: ${
333
  Array.isArray(a?.recommendations)
334
  ? a.recommendations.join(", ")
 
345
  </div>
346
  </div>
347
  `;
348
+ }
 
 
 
 
 
 
 
 
 
349
 
350
+ function renderBackendRow(row) {
351
+ const created = row.created_at
352
+ ? new Date(row.created_at).toLocaleString()
353
+ : "";
354
+ const reportDate = row.report_date || "N/A";
355
+ const anomalies = (() => {
356
+ try {
357
+ return Array.isArray(row.anomalies)
358
+ ? row.anomalies
359
+ : JSON.parse(row.anomalies || "[]");
360
+ } catch {
361
+ return [];
362
+ }
363
+ })();
364
+
365
+ return `
366
  <div class="bg-white border border-gray-200 rounded-lg p-4 shadow">
367
  <div class="flex justify-between mb-2">
368
+ <div class="font-semibold text-blue-700">Report: ${reportDate}</div>
369
  <div class="text-xs text-gray-500">${created}</div>
370
  </div>
371
+ <pre class="whitespace-pre-wrap text-sm text-gray-700 mb-2">${
372
+ row.ocr_text || ""
373
+ }</pre>
374
+ ${anomalies
375
+ .map(
376
+ (r, i) => `
377
  <div class="border-t border-gray-200 pt-2 mt-2">
378
+ <div class="font-medium">Finding ${i + 1}: ${
379
+ r?.findings ?? ""
380
+ }</div>
381
+ <div class="text-sm text-gray-600">Severity: ${
382
+ r?.severity ?? ""
383
+ }</div>
384
+ <div class="text-sm text-gray-600">Recommendations: ${(Array.isArray(
385
+ r?.recommendations
386
+ )
387
+ ? r.recommendations
388
+ : []
389
+ ).join(", ")}</div>
390
  </div>
391
+ `
392
+ )
393
+ .join("")}
394
  </div>`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
395
  }
 
396
 
397
+ function renderList(htmlItems) {
398
+ recsEl.innerHTML =
399
+ htmlItems && htmlItems.length
400
+ ? htmlItems.join("")
401
+ : '<p class="text-sm text-gray-500">No saved analyses yet.</p>';
 
 
 
 
 
 
 
402
  }
 
403
 
404
+ async function loadFromBackend(user) {
405
+ try {
406
+ const userId = user.email || user.uid;
407
+ const url = api('reports/', { user_id: userId });
408
+ dbg("Backend URL", url);
409
+ const res = await fetch(url, { method: "GET" });
410
+ dbg("Backend HTTP status", res.status);
411
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
412
+ const data = await res.json();
413
+ dbg("Backend payload", data);
414
+ return Array.isArray(data) ? data : [];
415
+ } catch (e) {
416
+ console.error("Backend fetch failed:", e);
417
+ dbg("Backend fetch error", e.message || e);
418
+ return [];
419
+ }
420
  }
421
 
422
+ async function loadFromFirestore(user) {
423
+ try {
424
+ const q = query(
425
+ collection(db, "users", user.uid, "analyses"),
426
+ orderBy("createdAt", "desc")
427
+ );
428
+ dbg("Firestore query uid", user.uid);
429
+ const snap = await getDocs(q);
430
+ const rows = snap.docs.map((d) => d.data());
431
+ dbg("Firestore count", rows.length);
432
+ return rows;
433
+ } catch (e) {
434
+ console.error("Firestore fetch failed:", e);
435
+ dbg("Firestore fetch error", e.message || e);
436
+ return [];
437
+ }
438
+ }
439
 
440
+ onAuthStateChanged(auth, async (user) => {
441
+ if (!user) {
442
+ statusEl.textContent = "Not signed in.";
443
+ renderList([]);
444
+ setDebugBanner("User is not signed in.");
445
+ return;
446
+ }
447
 
448
+ statusEl.textContent = `Signed in as ${user.email || user.uid}`;
449
+
450
+ const backendRows = await loadFromBackend(user);
451
+ const firestoreRows = await loadFromFirestore(user);
452
+
453
+ setDebugBanner(
454
+ `backend: ${backendRows.length} | firestore: ${firestoreRows.length}`
455
+ );
456
+ dbg("Summary", {
457
+ backend: backendRows.length,
458
+ firestore: firestoreRows.length,
459
+ });
460
+
461
+ if (backendRows.length > 0) {
462
+ recsEl.innerHTML = backendRows
463
+ .map((row, i) => renderBackendReportCollapsible(row, i))
464
+ .join("");
465
+ } else if (firestoreRows.length > 0) {
466
+ recsEl.innerHTML = firestoreRows
467
+ .map((row, i) => renderFirestoreReportCollapsible(row, i))
468
+ .join("");
469
+ } else {
470
+ recsEl.innerHTML =
471
+ '<p class="text-sm text-gray-500">No saved analyses yet.</p>';
472
+ }
473
+ });
474
+ </script>
475
+ </body>
476
+ </html>
web/profile.html CHANGED
@@ -102,23 +102,16 @@
102
  <div class="bg-white border border-gray-200 rounded-lg p-6 shadow mb-6">
103
  <!-- PROFILE VIEW -->
104
  <div id="profileViewSection">
105
- <div class="flex items-start space-x-6 mb-10">
106
- <div class="flex flex-col items-center">
107
- <img src="https://cdn.mos.cms.futurecdn.net/ARWms77gyVZ5u2MZkQqcKc-1256-80.jpg.webp"
108
- alt="Profile Tree"
109
- class="w-32 h-32 object-cover rounded-full border border-gray-300">
110
-
111
- <button id="editProfileBtn" class="mt-2 px-4 py-2 rounded">
112
- Edit Profile
113
- </button>
114
- <a href="analyzer.html">
115
- <button id="analyzerBtn" class="mt-2 px-4 py-2 rounded">
116
- Go To Analyzer
117
- </button>
118
- </a>
119
  </div>
120
- <div>
121
- <h2 class="text-xl font-semibold mb-4">Your Profile</h2>
122
  <p class="mb-2">
123
  Name: <span id="userName" class="font-medium">Loading...</span>
124
  </p>
@@ -126,17 +119,26 @@
126
  DOB: <span id="userDOB" class="font-medium">Loading...</span>
127
  </p>
128
  </div>
 
 
 
 
 
 
 
 
129
  </div>
130
  </div>
131
 
132
  <!-- PROFILE EDIT -->
133
  <div id="profileEditSection" class="hidden mb-10">
134
  <div class="flex flex-col sm:flex-row items-start space-x-6">
135
- <div class="flex flex-col items-center mb-4 sm:mb-0">
136
- <img src="https://cdn.mos.cms.futurecdn.net/ARWms77gyVZ5u2MZkQqcKc-1256-80.jpg.webp"
137
- alt="Profile Tree"
138
- class="w-32 h-32 object-cover rounded-full border border-gray-300">
139
-
 
140
  </div>
141
  <div class="flex-1">
142
  <h1 class="text-3xl font-semibold mb-4">Edit Profile</h1>
@@ -175,7 +177,7 @@
175
  </div>
176
 
177
  <!-- RANGE BARS -->
178
- <h2 class="text-xl font-semibold mb-4">Your Current Health Summary</h2>
179
  <div class="tabs">
180
  <div class="tab active" data-tab="out-range">
181
  Out of Range <span class="count-badge">0</span>
@@ -210,8 +212,8 @@
210
  storageBucket: "login-tutorial-7a9e1.firebasestorage.app",
211
  messagingSenderId: "491093197824",
212
  appId: "1:491093197824:web:9f86659034af7e6a8244e5",
213
- measurementId: "G-JM7T9N6ZLZ"
214
- };
215
 
216
  const app = initializeApp(firebaseConfig);
217
  const auth = getAuth(app);
@@ -227,174 +229,183 @@
227
 
228
  // Fetch measurements from backend API
229
  async function getUserMeasurements(userId) {
230
- try {
231
- const url = api("user_measurements/", { user_id: userId });
232
- const res = await fetch(url);
233
- if (!res.ok) throw new Error("Failed to fetch measurements");
234
-
235
- const data = await res.json();
236
-
237
- return (data.measurements || []).map((m) => ({
238
- name: m.measurement_type,
239
- value: Number(m.value),
240
- min: Number(m.min),
241
- max: Number(m.max),
242
- tab: m.status === "HIGH" || m.status === "LOW" ? "out-range" : "in-range",
243
- impacts: m.impacts || [],
244
- }));
245
- } catch (e) {
246
- console.error("Error fetching user measurements:", e);
247
- return [];
248
- }
249
- }
 
 
 
 
250
  function getLatestByType(measurements) {
251
- const latest = {};
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
 
253
- measurements.forEach(m => {
254
- if (!latest[m.name] || new Date(m.timestamp) > new Date(latest[m.name].timestamp)) {
255
- latest[m.name] = m;
256
- }
257
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
 
259
- return Object.values(latest);
260
- }
 
 
261
 
 
 
 
 
 
 
262
 
263
- // Render bars in the UI based on measurement array
264
- function renderBars(measurements) {
265
- console.log("measurements",measurements);
266
- const counts = { "in-range": 0, "out-range": 0 };
267
- const tabs = document.querySelectorAll(".tab");
268
- const contents = document.querySelectorAll(".tab-content");
269
-
270
- // Clear existing content
271
- contents.forEach((c) => (c.innerHTML = ""));
272
-
273
- measurements.forEach((m) => {
274
- counts[m.tab]++;
275
- const container = document.getElementById(m.tab);
276
- const card = document.createElement("div");
277
- card.className = "range-card";
278
-
279
- const title = document.createElement("h3");
280
- title.className = "measurement-title";
281
- title.textContent = m.name;
282
- card.appendChild(title);
283
-
284
- if (m.impacts?.length) {
285
- const impactLabel = document.createElement("div");
286
- impactLabel.className = "impact-label";
287
- impactLabel.textContent = "Impacts: " + m.impacts.join(", ");
288
- card.appendChild(impactLabel);
289
- }
290
-
291
- const barContainer = document.createElement("div");
292
- barContainer.className = "range-bar-container";
293
- const bar = document.createElement("div");
294
- bar.className = "range-bar";
295
-
296
- let normalPercent = 100;
297
- let overflowPercent = 0;
298
- if (m.tab === "out-range" && m.value > m.max) {
299
- normalPercent = ((m.max - m.min) / (m.value - m.min)) * 100;
300
- overflowPercent = 100 - normalPercent;
301
- }
302
-
303
- const normalDiv = document.createElement("div");
304
- normalDiv.className = "normal-range";
305
- normalDiv.style.width = normalPercent + "%";
306
- bar.appendChild(normalDiv);
307
-
308
- if (overflowPercent > 0) {
309
- const overflowDiv = document.createElement("div");
310
- overflowDiv.className = "overflow-range";
311
- overflowDiv.style.left = normalPercent + "%";
312
- overflowDiv.style.width = overflowPercent + "%";
313
- bar.appendChild(overflowDiv);
314
- }
315
-
316
- let valuePercent = ((m.value - m.min) / (m.max - m.min)) * 100;
317
- valuePercent = Math.min(Math.max(valuePercent, 0), 100);
318
-
319
- const marker = document.createElement("div");
320
- marker.className = "marker";
321
- marker.style.left = valuePercent + "%";
322
- bar.appendChild(marker);
323
-
324
- const valueLabel = document.createElement("div");
325
- valueLabel.className = "value-label";
326
- valueLabel.style.left = valuePercent + "%";
327
- valueLabel.textContent = m.value;
328
- bar.appendChild(valueLabel);
329
-
330
- barContainer.appendChild(bar);
331
-
332
- const minMaxDiv = document.createElement("div");
333
- minMaxDiv.className = "min-max-labels relative w-full";
334
-
335
- const minLabel = document.createElement("span");
336
- minLabel.textContent = "Min: " + m.min;
337
- minLabel.style.position = "absolute";
338
- minLabel.style.left = "0";
339
-
340
- const maxLabel = document.createElement("span");
341
- maxLabel.textContent = "Max: " + m.max;
342
- maxLabel.style.position = "absolute";
343
-
344
- let maxPercent = 100;
345
- if (m.value > m.max) maxPercent = ((m.max - m.min) / (m.value - m.min)) * 100;
346
- maxLabel.style.left = maxPercent + "%";
347
- maxLabel.style.transform = "translateX(-100%)";
348
-
349
- minMaxDiv.appendChild(minLabel);
350
- minMaxDiv.appendChild(maxLabel);
351
- barContainer.appendChild(minMaxDiv);
352
-
353
- card.appendChild(barContainer);
354
- container.appendChild(card);
355
- });
356
-
357
- // Update tab badges
358
- tabs.forEach((tab) => {
359
- const t = tab.dataset.tab;
360
- const badge = tab.querySelector(".count-badge");
361
- badge.textContent = counts[t];
362
- tab.addEventListener("click", () => {
363
- tabs.forEach((t) => t.classList.remove("active"));
364
- tab.classList.add("active");
365
- contents.forEach((c) =>
366
- c.id === t ? c.classList.add("active") : c.classList.remove("active")
367
- );
368
- });
369
- });
370
- }
371
 
372
  // ✅ Auth State Handling
373
  onAuthStateChanged(auth, async (user) => {
374
- const authNavItem = document.getElementById("authNavItem");
375
- if (user) {
376
- currentUser = user;
377
- await loadUserProfile();
378
-
379
- // ✅ fetch and render bars after user is ready
380
- console.log("currentuser data",currentUser);
381
- // const measurements = await getUserMeasurements(currentUser.uid);
382
- let measurements = await getUserMeasurements(currentUser.email);
383
- measurements = getLatestByType(measurements);
384
-
385
- renderBars(measurements);
386
-
387
- authNavItem.innerHTML = '<button onclick="logout()" style="color:red">Logout</button>';
388
- if (homeNavDesktop) homeNavDesktop.style.display = "none";
389
- if (homeNavMobile) homeNavMobile.style.display = "none";
390
- } else {
391
- authNavItem.innerHTML = '<a href="login.html">Login</a>';
392
- if (homeNavDesktop) homeNavDesktop.style.display = "";
393
- if (homeNavMobile) homeNavMobile.style.display = "";
394
- window.location.href = "login.html";
395
- }
396
- });
397
-
398
 
399
  async function loadUserProfile() {
400
  try {
@@ -450,8 +461,6 @@ function renderBars(measurements) {
450
  profileView.classList.add("hidden");
451
  profileEdit.classList.remove("hidden");
452
  });
453
-
454
-
455
 
456
  saveBtn.addEventListener("click", async () => {
457
  const nameVal = inputName.value.trim();
@@ -477,8 +486,7 @@ function renderBars(measurements) {
477
  // RANGE BARS LOGIC
478
  const tabs = document.querySelectorAll(".tab");
479
  const contents = document.querySelectorAll(".tab-content");
480
-
481
  });
482
  </script>
483
  </body>
484
- </html>
 
102
  <div class="bg-white border border-gray-200 rounded-lg p-6 shadow mb-6">
103
  <!-- PROFILE VIEW -->
104
  <div id="profileViewSection">
105
+ <div class="grid grid-cols-10">
106
+ <div class="col-span-1">
107
+ <img
108
+ src="https://cdn.mos.cms.futurecdn.net/ARWms77gyVZ5u2MZkQqcKc-1256-80.jpg.webp"
109
+ alt="Profile Tree"
110
+ class="w-32 h-32 object-cover rounded-full border border-gray-300"
111
+ />
 
 
 
 
 
 
 
112
  </div>
113
+ <div class="col-span-5">
114
+ <h2 class="text-xl font-semibold mb-4 text-sky-700">Your Profile</h2>
115
  <p class="mb-2">
116
  Name: <span id="userName" class="font-medium">Loading...</span>
117
  </p>
 
119
  DOB: <span id="userDOB" class="font-medium">Loading...</span>
120
  </p>
121
  </div>
122
+ <div class="col-span-4">
123
+ <button id="editProfileBtn" class="mt-2 px-4 py-2 rounded">
124
+ Edit Profile
125
+ </button>
126
+ <button id="analyzerBtn" class="mt-2 px-4 py-2 rounded">
127
+ Go To Analyzer
128
+ </button>
129
+ </div>
130
  </div>
131
  </div>
132
 
133
  <!-- PROFILE EDIT -->
134
  <div id="profileEditSection" class="hidden mb-10">
135
  <div class="flex flex-col sm:flex-row items-start space-x-6">
136
+ <div>
137
+ <img
138
+ src="https://cdn.mos.cms.futurecdn.net/ARWms77gyVZ5u2MZkQqcKc-1256-80.jpg.webp"
139
+ alt="Profile Tree"
140
+ class="w-32 h-32 object-cover rounded-full border border-gray-300"
141
+ />
142
  </div>
143
  <div class="flex-1">
144
  <h1 class="text-3xl font-semibold mb-4">Edit Profile</h1>
 
177
  </div>
178
 
179
  <!-- RANGE BARS -->
180
+ <h2 class="text-xl font-semibold mb-4 text-sky-700">Your Current Health Summary</h2>
181
  <div class="tabs">
182
  <div class="tab active" data-tab="out-range">
183
  Out of Range <span class="count-badge">0</span>
 
212
  storageBucket: "login-tutorial-7a9e1.firebasestorage.app",
213
  messagingSenderId: "491093197824",
214
  appId: "1:491093197824:web:9f86659034af7e6a8244e5",
215
+ measurementId: "G-JM7T9N6ZLZ",
216
+ };
217
 
218
  const app = initializeApp(firebaseConfig);
219
  const auth = getAuth(app);
 
229
 
230
  // Fetch measurements from backend API
231
  async function getUserMeasurements(userId) {
232
+ try {
233
+ // const url = api("https://vineelagampa-prmschallenge.hf.space/user_measurements/", { user_id: userId });
234
+ const url = api("user_measurements/", { user_id: userId });
235
+ const res = await fetch(url);
236
+ if (!res.ok) throw new Error("Failed to fetch measurements");
237
+
238
+ const data = await res.json();
239
+
240
+ return (data.measurements || []).map((m) => ({
241
+ name: m.measurement_type,
242
+ value: Number(m.value),
243
+ min: Number(m.min),
244
+ max: Number(m.max),
245
+ tab:
246
+ m.status === "HIGH" || m.status === "LOW"
247
+ ? "out-range"
248
+ : "in-range",
249
+ impacts: m.impacts || [],
250
+ }));
251
+ } catch (e) {
252
+ console.error("Error fetching user measurements:", e);
253
+ return [];
254
+ }
255
+ }
256
  function getLatestByType(measurements) {
257
+ const latest = {};
258
+
259
+ measurements.forEach((m) => {
260
+ if (
261
+ !latest[m.name] ||
262
+ new Date(m.timestamp) > new Date(latest[m.name].timestamp)
263
+ ) {
264
+ latest[m.name] = m;
265
+ }
266
+ });
267
+
268
+ return Object.values(latest);
269
+ }
270
+
271
+ // Render bars in the UI based on measurement array
272
+ function renderBars(measurements) {
273
+ console.log("measurements", measurements);
274
+ const counts = { "in-range": 0, "out-range": 0 };
275
+ const tabs = document.querySelectorAll(".tab");
276
+ const contents = document.querySelectorAll(".tab-content");
277
 
278
+ // Clear existing content
279
+ contents.forEach((c) => (c.innerHTML = ""));
280
+
281
+ measurements.forEach((m) => {
282
+ counts[m.tab]++;
283
+ const container = document.getElementById(m.tab);
284
+ const card = document.createElement("div");
285
+ card.className = "range-card";
286
+
287
+ const title = document.createElement("h3");
288
+ title.className = "measurement-title";
289
+ title.textContent = m.name;
290
+ card.appendChild(title);
291
+
292
+ if (m.impacts?.length) {
293
+ const impactLabel = document.createElement("div");
294
+ impactLabel.className = "impact-label";
295
+ impactLabel.textContent = "Impacts: " + m.impacts.join(", ");
296
+ card.appendChild(impactLabel);
297
+ }
298
 
299
+ const barContainer = document.createElement("div");
300
+ barContainer.className = "range-bar-container";
301
+ const bar = document.createElement("div");
302
+ bar.className = "range-bar";
303
 
304
+ let normalPercent = 100;
305
+ let overflowPercent = 0;
306
+ if (m.tab === "out-range" && m.value > m.max) {
307
+ normalPercent = ((m.max - m.min) / (m.value - m.min)) * 100;
308
+ overflowPercent = 100 - normalPercent;
309
+ }
310
 
311
+ const normalDiv = document.createElement("div");
312
+ normalDiv.className = "normal-range";
313
+ normalDiv.style.width = normalPercent + "%";
314
+ bar.appendChild(normalDiv);
315
+
316
+ if (overflowPercent > 0) {
317
+ const overflowDiv = document.createElement("div");
318
+ overflowDiv.className = "overflow-range";
319
+ overflowDiv.style.left = normalPercent + "%";
320
+ overflowDiv.style.width = overflowPercent + "%";
321
+ bar.appendChild(overflowDiv);
322
+ }
323
+
324
+ let valuePercent = ((m.value - m.min) / (m.max - m.min)) * 100;
325
+ valuePercent = Math.min(Math.max(valuePercent, 0), 100);
326
+
327
+ const marker = document.createElement("div");
328
+ marker.className = "marker";
329
+ marker.style.left = valuePercent + "%";
330
+ bar.appendChild(marker);
331
+
332
+ const valueLabel = document.createElement("div");
333
+ valueLabel.className = "value-label";
334
+ valueLabel.style.left = valuePercent + "%";
335
+ valueLabel.textContent = m.value;
336
+ bar.appendChild(valueLabel);
337
+
338
+ barContainer.appendChild(bar);
339
+
340
+ const minMaxDiv = document.createElement("div");
341
+ minMaxDiv.className = "min-max-labels relative w-full";
342
+
343
+ const minLabel = document.createElement("span");
344
+ minLabel.textContent = "Min: " + m.min;
345
+ minLabel.style.position = "absolute";
346
+ minLabel.style.left = "0";
347
+
348
+ const maxLabel = document.createElement("span");
349
+ maxLabel.textContent = "Max: " + m.max;
350
+ maxLabel.style.position = "absolute";
351
+
352
+ let maxPercent = 100;
353
+ if (m.value > m.max)
354
+ maxPercent = ((m.max - m.min) / (m.value - m.min)) * 100;
355
+ maxLabel.style.left = maxPercent + "%";
356
+ maxLabel.style.transform = "translateX(-100%)";
357
+
358
+ minMaxDiv.appendChild(minLabel);
359
+ minMaxDiv.appendChild(maxLabel);
360
+ barContainer.appendChild(minMaxDiv);
361
+
362
+ card.appendChild(barContainer);
363
+ container.appendChild(card);
364
+ });
365
+
366
+ // Update tab badges
367
+ tabs.forEach((tab) => {
368
+ const t = tab.dataset.tab;
369
+ const badge = tab.querySelector(".count-badge");
370
+ badge.textContent = counts[t];
371
+ tab.addEventListener("click", () => {
372
+ tabs.forEach((t) => t.classList.remove("active"));
373
+ tab.classList.add("active");
374
+ contents.forEach((c) =>
375
+ c.id === t
376
+ ? c.classList.add("active")
377
+ : c.classList.remove("active")
378
+ );
379
+ });
380
+ });
381
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
382
 
383
  // ✅ Auth State Handling
384
  onAuthStateChanged(auth, async (user) => {
385
+ const authNavItem = document.getElementById("authNavItem");
386
+ if (user) {
387
+ currentUser = user;
388
+ await loadUserProfile();
389
+
390
+ // ✅ fetch and render bars after user is ready
391
+ console.log("currentuser data", currentUser);
392
+ // const measurements = await getUserMeasurements(currentUser.uid);
393
+ let measurements = await getUserMeasurements(currentUser.email);
394
+ measurements = getLatestByType(measurements);
395
+
396
+ renderBars(measurements);
397
+
398
+ authNavItem.innerHTML =
399
+ '<button onclick="logout()" style="color:red">Logout</button>';
400
+ if (homeNavDesktop) homeNavDesktop.style.display = "none";
401
+ if (homeNavMobile) homeNavMobile.style.display = "none";
402
+ } else {
403
+ authNavItem.innerHTML = '<a href="login.html">Login</a>';
404
+ if (homeNavDesktop) homeNavDesktop.style.display = "";
405
+ if (homeNavMobile) homeNavMobile.style.display = "";
406
+ window.location.href = "login.html";
407
+ }
408
+ });
409
 
410
  async function loadUserProfile() {
411
  try {
 
461
  profileView.classList.add("hidden");
462
  profileEdit.classList.remove("hidden");
463
  });
 
 
464
 
465
  saveBtn.addEventListener("click", async () => {
466
  const nameVal = inputName.value.trim();
 
486
  // RANGE BARS LOGIC
487
  const tabs = document.querySelectorAll(".tab");
488
  const contents = document.querySelectorAll(".tab-content");
 
489
  });
490
  </script>
491
  </body>
492
+ </html>