PushStylechangestoVineelaGampa

#1
by TheFrogGod - opened
Files changed (5) hide show
  1. backend.py +249 -140
  2. web/analyzer.html +279 -189
  3. web/analyzer_update.html +0 -472
  4. web/past_data.html +0 -44
  5. web/profile.html +274 -242
backend.py CHANGED
@@ -1,36 +1,60 @@
1
- import os
2
  from ast import List
 
3
  from fastapi.middleware.cors import CORSMiddleware
4
  from pydantic import BaseModel
 
 
 
5
  import io
6
- import fitz
 
7
  import traceback
8
  import pandas as pd
9
-
10
- import base64
11
- import json
12
  import re
13
- import asyncio
14
- import functools
15
- from typing import Any, Optional
16
-
17
  import google.generativeai as genai
18
- from fastapi import FastAPI, UploadFile, File, Form, HTTPException, APIRouter, Request
19
- from fastapi.responses import JSONResponse, RedirectResponse
20
  from fastapi.staticfiles import StaticFiles
21
  import firebase_admin
22
  from firebase_admin import credentials, firestore
23
  from google.generativeai import generative_models
24
- from pydantic import BaseModel
 
 
 
 
 
25
  from past_reports import router as reports_router, db_fetch_reports
26
 
27
- from api_key import GEMINI_API_KEY
 
 
 
 
 
 
 
 
28
 
29
  app = FastAPI()
 
30
  api = APIRouter(prefix="/api")
31
  app.include_router(api)
32
 
33
- EXTRACTED_TEXT_CACHE = ""
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
  app.mount("/app", StaticFiles(directory="web", html=True), name="web")
36
  app.include_router(reports_router)
@@ -46,60 +70,38 @@ app.add_middleware(
46
  @app.get("/")
47
  def root():
48
  return RedirectResponse(url="/app/")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
- class AnalyzeRequest(BaseModel):
51
- image_base64: str
52
- prompt: Optional[str] = None
53
-
54
- GEMINI_API_KEY = os.getenv("GEMINI_API_KEY", GEMINI_API_KEY)
55
-
56
- if not GEMINI_API_KEY:
57
- raise RuntimeError(
58
- "No Gemini API key found. Put it in api_key.py as `GEMINI_API_KEY = '...'` or set env var GEMINI_API_KEY."
59
- )
60
-
61
- genai.configure(api_key=GEMINI_API_KEY)
62
-
63
- generation_config = {
64
- "temperature": 0.2,
65
- "top_p": 0.95,
66
- "top_k": 40,
67
- "max_output_tokens": 2048,
68
- }
69
-
70
- safety_settings = [
71
- {"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_MEDIUM_AND_ABOVE"},
72
- {"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_MEDIUM_AND_ABOVE"},
73
- {"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "BLOCK_MEDIUM_AND_ABOVE"},
74
- {"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_MEDIUM_AND_ABOVE"},
75
- ]
76
 
77
- # --- Pydantic Models for API Endpoints ---
78
  class ChatRequest(BaseModel):
79
  user_id: Optional[str] = "anonymous"
80
  question: str
81
-
82
  class ChatResponse(BaseModel):
83
  answer: str
84
-
85
- class TextRequest(BaseModel):
86
- text: str
87
-
88
- system_prompt = """ As a highly skilled medical practitioner specializing in image analysis, you are tasked with examining medical images for a renowned hospital. Your expertise is crucial in identifying any anomalies, diseases, or health issues that may be present in the images. Your responsibilities include:
89
- 1. Detailed Analysis: Thoroughly analyze each image, focusing on identifying any abnormal findings that may indicate underlying medical conditions.
90
- 2. Finding Report: Document all observed anomalies or signs of disease. Clearly articulate these findings in a structured report format, ensuring accuracy and clarity. Also include any measurement found such as trygliceride, HBa1c, and hdl levels. When presenting any found measurement, relay it in this format: {"findings": "Condition that this measurement could affect only if the value is in a risky rang: masurement type -- value with unit(current range). For example: {"findings": "Diabetes: Hba1c -- 8%(diabetic)", "ldl -- 80mg/dL(optimal)"}"
91
- At the beginning of every finding, if needed try to simplify the information given in 3 words.
92
- 3. Checking for Past: If a disease is considered family history or is a disease that the patient recovered from(anything anomoly that is historical), please mention this in severity using this format: "severity" : "severity of the anomoly (Past Anomoly but Still Under Risk)"
93
- 4. Recommendations and Next Steps: Provide detailed recommendations based on your findings. Outline the necessary follow-up actions or additional tests required to confirm diagnoses or assess treatment options.
94
- 5. Treatment Suggestions: Offer preliminary treatment suggestions or interventions based on the identified conditions, collaborating with the healthcare team to develop comprehensive patient care plans.
95
- 6. Output Format: Your output should be a JSON array (list) of objects, each describing one disease or medical finding using the structure below:
96
- [{"findings": "Description of the first disease or condition.", "severity": "MILD/SEVERE/CRITICAL", "recommendations": ["Follow-up test 1", "Follow-up test 2"], "treatment_suggestions": ["Treatment 1", "Treatment 2"], "home_care_guidance": ["Care tip 1", "Care tip 2"] }, { "findings": "Description of the second disease or condition.", "severity": "MILD/SEVERE/CRITICAL", "recommendations": ["Follow-up test A", "Follow-up test B"], "treatment_suggestions": ["Treatment A", "Treatment B"], "home_care_guidance": ["Care tip A", "Care tip B"] } ]
97
- Important Notes:
98
- 1. Scope of Response: Only respond if the image pertains to a human health issue.
99
- 2. Clarity of Image: Ensure the image is clear and suitable for accurate analysis.
100
- 3. As an AI, don't produce any output than doesn't follow this format and also remember that we need your input on the medical report for a team of renowned doctors to recheck your readings and determine AI's role in the medical industry.
101
- 4. Your Insights are Invaluable: Your insights play a crucial role in guiding clinical decisions. Please proceed with your analysis, adhering to the structured approach outlined above.
102
- 5. Completely UPPERCASE the main concern in the finding """
103
 
104
  system_prompt_chat = """
105
  *** Role: Medical Guidance Facilitator
@@ -110,9 +112,7 @@ Analyze medical data, provide concise, evidence-based insights, and recommend ac
110
  2. Historical Context – Compare current findings with any available previous reports.
111
  3. Medical Q&A – Answer specific questions about the report using trusted medical sources.
112
  4. Specialist Matching – Recommend relevant physician specialties for identified conditions.
113
- 5. Local Physician Recommendations List at least two real physician or clinic options within the user-specified mile radius (include name, specialty, address, distance from user, and contact info) based on the patient’s location and clinical need.
114
- 6. Insurance Guidance – If insurance/network information is provided, prioritize in-network physicians.
115
- 7. Safety Protocols – Include a brief disclaimer encouraging users to verify information, confirm insurance coverage, and consult providers directly.
116
  *** Response Structure:
117
  Start with a direct answer to the user’s primary question (maximum 4 concise sentences, each on a new line).
118
  If a physician/specialist is needed, recommend at least two local providers within the requested radius (include name, specialty, address, distance, and contact info).
@@ -124,56 +124,55 @@ User Question: {user_question}
124
  Assistant Answer:
125
  """
126
 
127
- # Initialize model
128
- model = genai.GenerativeModel(model_name="gemini-2.5-flash-lite")
129
-
130
- async def _call_model_blocking(request_inputs, generation_cfg, safety_cfg):
131
- """Run blocking model call in threadpool (so uvicorn's event loop isn't blocked)."""
132
- fn = functools.partial(
133
- model.generate_content,
134
- request_inputs,
135
- generation_config=generation_cfg,
136
- safety_settings=safety_cfg,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  )
138
- loop = asyncio.get_event_loop()
139
- return await loop.run_in_executor(None, fn)
140
 
 
 
141
 
142
- async def analyze_image(image_bytes: bytes, mime_type: str, prompt: Optional[str] = None) -> Any:
143
- base64_img = base64.b64encode(image_bytes).decode("utf-8")
144
- text_prompt = (prompt or system_prompt).strip()
145
-
146
- request_inputs = [
147
- {"inline_data": {"mime_type": mime_type, "data": base64_img}},
148
- {"text": text_prompt},
149
- ]
150
-
151
  try:
152
- response = await _call_model_blocking(request_inputs, generation_config, safety_settings)
 
 
 
 
 
 
153
  except Exception as e:
154
- raise RuntimeError(f"Model call failed: {e}")
155
-
156
- text = getattr(response, "text", None)
157
- if not text and isinstance(response, dict):
158
- candidates = response.get("candidates") or []
159
- if candidates:
160
- text = candidates[0].get("content") or candidates[0].get("text")
161
- if not text:
162
- text = str(response)
163
-
164
- clean = re.sub(r"```(?:json)?", "", text).strip()
165
-
166
- try:
167
- parsed = json.loads(clean)
168
- return parsed
169
- except json.JSONDecodeError:
170
- match = re.search(r"(\[.*\]|\{.*\})", clean, re.DOTALL)
171
- if match:
172
- try:
173
- return json.loads(match.group(1))
174
- except json.JSONDecodeError:
175
- return {"raw_found_json": match.group(1)}
176
- return {"raw_output": clean}
177
 
178
  def get_past_reports_from_sqllite(user_id: str):
179
  try:
@@ -188,46 +187,156 @@ def get_past_reports_from_sqllite(user_id: str):
188
 
189
  @app.post("/chat/", response_model=ChatResponse)
190
  async def chat_endpoint(request: ChatRequest):
191
- global result
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  try:
193
- document_text = json.dumps(result) if result else "No parsed text available"
194
  full_prompt = system_prompt_chat.format(
195
- document_text=document_text,
196
  user_question=request.question
197
  )
198
- data = await request.json()
199
- question = data.get("question")
200
- report_text = data.get("report_text")
201
- user_id = data.get("user_id")
202
-
203
  response = model.generate_content(full_prompt)
204
  return ChatResponse(answer=response.text)
205
  except Exception as e:
206
- raise HTTPException(status_code=500, detail=f"Chat error: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
 
208
- @app.post("/analyze")
209
- async def analyze_endpoint(file: UploadFile = File(...), prompt: str = Form(None)):
210
- global result
211
- """
212
- Upload an image file (field name `file`) and optional text `prompt`.
213
- Returns parsed JSON (or raw model output if JSON couldn't be parsed).
214
- """
215
- contents = await file.read() # <-- this gets the uploaded file bytes
216
- mime = file.content_type or "image/png"
217
- result = await analyze_image(contents, mime, prompt)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  try:
219
- result = await analyze_image(contents, mime, prompt)
220
  except Exception as e:
221
- raise HTTPException(status_code=500, detail=str(e))
222
- return JSONResponse(content={"Detected_Anomolies": result})
223
-
224
- @app.post("/analyze_json")
225
- async def analyze_json(req: AnalyzeRequest):
226
- import base64
227
- image_bytes = base64.b64decode(req.image_base64)
228
- result = await analyze_image(image_bytes, "image/png", req.prompt)
229
- return {"result": result}
230
-
 
 
231
  @app.get("/health/")
232
  def health():
233
  return {"response": "ok"}
@@ -238,4 +347,4 @@ def _log_routes():
238
  print("Mounted routes:")
239
  for r in app.routes:
240
  if isinstance(r, APIRoute):
241
- print(" ", r.path, r.methods)
 
 
1
  from ast import List
2
+ from fastapi import FastAPI, UploadFile, File, Form, HTTPException,APIRouter, Request
3
  from fastapi.middleware.cors import CORSMiddleware
4
  from pydantic import BaseModel
5
+ from typing import Optional
6
+ import pytesseract
7
+ from PIL import Image
8
  import io
9
+ import fitz
10
+ import base64
11
  import traceback
12
  import pandas as pd
 
 
 
13
  import re
14
+ import os
 
 
 
15
  import google.generativeai as genai
16
+ from dotenv import load_dotenv
17
+ from fastapi.responses import RedirectResponse
18
  from fastapi.staticfiles import StaticFiles
19
  import firebase_admin
20
  from firebase_admin import credentials, firestore
21
  from google.generativeai import generative_models
22
+
23
+ from api_key import GEMINI_API_KEY
24
+ from bert import analyze_with_clinicalBert, classify_disease_and_severity, extract_non_negated_keywords, analyze_measurements, detect_past_diseases
25
+ from disease_links import diseases as disease_links
26
+ from disease_steps import disease_next_steps
27
+ from disease_support import disease_doctor_specialty, disease_home_care
28
  from past_reports import router as reports_router, db_fetch_reports
29
 
30
+ model = genai.GenerativeModel('gemini-1.5-flash')
31
+ df = pd.read_csv("measurement.csv")
32
+ df.columns = df.columns.str.lower()
33
+ df['measurement'] = df['measurement'].str.lower()
34
+
35
+ disease_links = {"cholesterol": "https://www.webmd.com/cholesterol"}
36
+ disease_next_steps = {"cholesterol": ["Consult a doctor for a lipid panel."]}
37
+ disease_doctor_specialty = {"cholesterol": "Cardiologist"}
38
+ disease_home_care = {"cholesterol": ["Maintain a healthy diet."]}
39
 
40
  app = FastAPI()
41
+
42
  api = APIRouter(prefix="/api")
43
  app.include_router(api)
44
 
45
+
46
+ '''app.add_middleware(
47
+ CORSMiddleware,
48
+ allow_origins=[
49
+ "http://localhost:8002"
50
+ "http://localhost:9000"
51
+ "http://localhost:5501"
52
+ ],
53
+ allow_credentials=True,
54
+ allow_methods=["*"],
55
+ allow_headers=["*"],
56
+ )'''
57
+
58
 
59
  app.mount("/app", StaticFiles(directory="web", html=True), name="web")
60
  app.include_router(reports_router)
 
70
  @app.get("/")
71
  def root():
72
  return RedirectResponse(url="/app/")
73
+
74
+ EXTRACTED_TEXT_CACHE: str = ""
75
+
76
+ try:
77
+ gemini_api_key = os.environ.get("GEMINI_API_KEY", GEMINI_API_KEY)
78
+ if not gemini_api_key:
79
+ raise ValueError("No Gemini API key found in environment or api_key.py")
80
+ genai.configure(api_key=gemini_api_key)
81
+ except Exception as e:
82
+ raise RuntimeError(f"Failed to configure Gemini API: {e}")
83
+
84
+ try:
85
+ cred_path = os.environ.get("FIREBASE_SERVICE_ACCOUNT_KEY_PATH", "firebase_key.json")
86
+
87
+ if not os.path.exists(cred_path):
88
+ raise ValueError(
89
+ f"Firebase service account key not found. Looked for: {cred_path}. "
90
+ "Set FIREBASE_SERVICE_ACCOUNT_KEY_PATH or place firebase_key.json in project root."
91
+ )
92
 
93
+ cred = credentials.Certificate(cred_path)
94
+ firebase_admin.initialize_app(cred)
95
+ db = firestore.client()
96
+ except Exception as e:
97
+ raise RuntimeError(f"Failed to configure Firebase: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
 
99
  class ChatRequest(BaseModel):
100
  user_id: Optional[str] = "anonymous"
101
  question: str
102
+
103
  class ChatResponse(BaseModel):
104
  answer: str
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
 
106
  system_prompt_chat = """
107
  *** Role: Medical Guidance Facilitator
 
112
  2. Historical Context – Compare current findings with any available previous reports.
113
  3. Medical Q&A – Answer specific questions about the report using trusted medical sources.
114
  4. Specialist Matching – Recommend relevant physician specialties for identified conditions.
115
+ 5. Safety ProtocolsInclude a brief disclaimer encouraging users to verify information, confirm insurance coverage, and consult providers directly.
 
 
116
  *** Response Structure:
117
  Start with a direct answer to the user’s primary question (maximum 4 concise sentences, each on a new line).
118
  If a physician/specialist is needed, recommend at least two local providers within the requested radius (include name, specialty, address, distance, and contact info).
 
124
  Assistant Answer:
125
  """
126
 
127
+ def extract_images_from_pdf_bytes(pdf_bytes: bytes) -> list:
128
+ print("***Start of Code***")
129
+ doc = fitz.open(stream=pdf_bytes, filetype="pdf")
130
+ images = []
131
+ for page in doc:
132
+ pix = page.get_pixmap()
133
+ buf = io.BytesIO()
134
+ buf.write(pix.tobytes("png"))
135
+ images.append(buf.getvalue())
136
+ return images
137
+
138
+
139
+ def clean_ocr_text(text: str) -> str:
140
+ text = text.replace("\x0c", " ")
141
+ text = text.replace("\u00a0", " ")
142
+ text = re.sub(r'(\d)\s*\.\s*(\d)', r'\1.\2', text)
143
+ text = re.sub(r'\s+', ' ', text)
144
+ return text.strip()
145
+
146
+ def ocr_text_from_image(image_bytes: bytes) -> str:
147
+ base64_image = base64.b64encode(image_bytes).decode('utf-8')
148
+
149
+ image_content = {
150
+ 'mime_type': 'image/jpeg',
151
+ 'data': base64_image
152
+ }
153
+
154
+ prompt = "Could you read this document and just take all the text that is in it and just paste it back to me in text format. Open and read this document:"
155
+
156
+ response = model.generate_content(
157
+ [prompt, image_content]
158
  )
 
 
159
 
160
+ response_text = response.text
161
+ print(response_text)
162
 
163
+ return response_text
164
+ def get_past_reports_from_firestore(user_id: str):
 
 
 
 
 
 
 
165
  try:
166
+ reports_ref = db.collection('users').document(request.user_id).collection('reports')
167
+ docs = reports_ref.order_by('timestamp', direction=firestore.Query.DESCENDING).limit(10).stream()
168
+
169
+ history_text = ""
170
+ for doc in docs:
171
+ report_data = doc.to_dict()
172
+ history_text += f"Report from {report_data.get('timestamp', 'N/A')}:\n{report_data.get('ocr_text', 'No OCR text found')}\n\n"
173
  except Exception as e:
174
+ history_text = "No past reports found for this user."
175
+ return history_text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
 
177
  def get_past_reports_from_sqllite(user_id: str):
178
  try:
 
187
 
188
  @app.post("/chat/", response_model=ChatResponse)
189
  async def chat_endpoint(request: ChatRequest):
190
+ """
191
+ Chatbot endpoint that answers questions based on the last analyzed document and user history.
192
+ """
193
+ print("Received chat request for user:", request.user_id)
194
+ #history_text = get_past_reports_from_firestore(request.user_id)
195
+ history_text = get_past_reports_from_sqllite(request.user_id)
196
+
197
+ full_document_text = EXTRACTED_TEXT_CACHE + "\n\n" + "PAST REPORTS:\n" + history_text
198
+
199
+ if not full_document_text:
200
+ raise HTTPException(status_code=400, detail="No past reports or current data exists for this user")
201
+
202
+
203
+
204
+
205
  try:
 
206
  full_prompt = system_prompt_chat.format(
207
+ document_text=full_document_text,
208
  user_question=request.question
209
  )
 
 
 
 
 
210
  response = model.generate_content(full_prompt)
211
  return ChatResponse(answer=response.text)
212
  except Exception as e:
213
+ print(f"Gemini API error: {traceback.format_exc()}")
214
+ raise HTTPException(status_code=500, detail=f"An error occurred during chat response generation: {e}")
215
+
216
+ @app.post("/analyze/")
217
+ async def analyze(
218
+ file: UploadFile = File(...),
219
+ model: Optional[str] = Form("bert"),
220
+ mode: Optional[str] = Form(None)
221
+ ):
222
+ global resolution, EXTRACTED_TEXT_CACHE
223
+ if not file.filename:
224
+ raise HTTPException(status_code=400, detail="No file uploaded.")
225
+
226
+ filename = file.filename.lower()
227
+ detected_diseases = set()
228
+ ocr_full = ""
229
+ print("Received request for file:", filename)
230
+ if filename.endswith(".pdf"):
231
+ pdf_bytes = await file.read()
232
+ image_bytes_list = extract_images_from_pdf_bytes(pdf_bytes)
233
+ else:
234
+ content = await file.read()
235
+ image_bytes_list = [content]
236
+
237
+ for img_bytes in image_bytes_list:
238
+ ocr_text = ocr_text_from_image(img_bytes)
239
+ ocr_full += ocr_text + "\n\n"
240
+ ocr_full = clean_ocr_text(ocr_full)
241
+ print(f"CALLING OCR FULL: {ocr_full}")
242
+
243
+ EXTRACTED_TEXT_CACHE = ocr_full
244
 
245
+
246
+ if model.lower() == "gemini":
247
+ return {"message": "Gemini model not available; please use BERT model."}
248
+
249
+ found_diseases = extract_non_negated_keywords(ocr_full)
250
+ past = detect_past_diseases(ocr_full)
251
+
252
+ for disease in found_diseases:
253
+ if disease in past:
254
+ severity = classify_disease_and_severity(disease)
255
+ detected_diseases.add(((f"{disease}(detected as historical condition, but still under risk.)"), severity))
256
+ else:
257
+ severity = classify_disease_and_severity(disease)
258
+ detected_diseases.add((disease, severity))
259
+
260
+
261
+
262
+ print("Detected diseases:", detected_diseases)
263
+ ranges = analyze_measurements(ocr_full, df)
264
+
265
+
266
+ resolution = []
267
+ detected_ranges = []
268
+ for disease, severity in detected_diseases:
269
+ link = disease_links.get(disease.lower(), "https://www.webmd.com/")
270
+ next_steps = disease_next_steps.get(disease.lower(), ["Consult a doctor."])
271
+ specialist = disease_doctor_specialty.get(disease.lower(), "General Practitioner")
272
+ home_care = disease_home_care.get(disease.lower(), [])
273
+
274
+ resolution.append({
275
+ "findings": disease.upper(),
276
+ "severity": severity,
277
+ "recommendations": next_steps,
278
+ "treatment_suggestions": f"Consult a specialist: {specialist}",
279
+ "home_care_guidance": home_care,
280
+ "info_link": link
281
+
282
+ })
283
+
284
+ for i in ranges:
285
+ condition = i[0]
286
+ measurement = i[1]
287
+ unit = i[2]
288
+ severity = i[3]
289
+ value = i[4]
290
+ range_value = i[5] # renamed to avoid overwriting Python's built-in "range"
291
+
292
+ link_range = disease_links.get(condition.lower(), "https://www.webmd.com/")
293
+ next_steps_range = disease_next_steps.get(condition.lower(), ['Consult a doctor'])
294
+ specialist_range = disease_doctor_specialty.get(condition.lower(), "General Practitioner")
295
+ home_care_range = disease_home_care.get(condition.lower(), [])
296
+
297
+ condition_version = condition.upper()
298
+ severity_version = severity.upper()
299
+
300
+ resolution.append({
301
+ "findings": f"{condition_version} -- {measurement}",
302
+ "severity": f"{value} {unit} - {severity_version}",
303
+ "recommendations": next_steps_range,
304
+ "treatment_suggestions": f"Consult a specialist: {specialist_range}",
305
+ "home_care_guidance": home_care_range,
306
+ "info_link": link_range
307
+ })
308
+
309
+
310
+ ranges = analyze_measurements(ocr_full, df)
311
+ print(analyze_measurements(ocr_full, df))
312
+ # print ("Ranges is being printed", ranges)
313
+ historical_med_data = detect_past_diseases(ocr_full)
314
+
315
+ return {
316
+ "ocr_text": ocr_full.strip(),
317
+ "Detected_Anomolies": resolution,
318
+ }
319
+
320
+ class TextRequest(BaseModel):
321
+ text: str
322
+
323
+ @app.post("/analyze-text")
324
+ async def analyze_text_endpoint(request: TextRequest):
325
  try:
326
+ return analyze_text(request.text)
327
  except Exception as e:
328
+ print("ERROR in /analyze-text:", traceback.format_exc())
329
+ raise HTTPException(status_code=500, detail=f"Error analyzing text: {str(e)}")
330
+
331
+
332
+ def analyze_text(text):
333
+ severity, disease = classify_disease_and_severity(text)
334
+ return {
335
+ "extracted_text": text,
336
+ "summary": f"Detected Disease: {disease}, Severity: {severity}"
337
+ }
338
+
339
+
340
  @app.get("/health/")
341
  def health():
342
  return {"response": "ok"}
 
347
  print("Mounted routes:")
348
  for r in app.routes:
349
  if isinstance(r, APIRoute):
350
+ print(" ", r.path, r.methods)
web/analyzer.html CHANGED
@@ -1,4 +1,4 @@
1
- <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8" />
@@ -6,34 +6,34 @@
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <link rel="stylesheet" href="style.css" />
 
9
  </head>
10
  <body class="bg-[var(--latte-cream)] font-sans text-gray-800 min-h-screen">
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 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>
28
- <li id="authNavItem">
29
- <a href="login.html" class="nav-link">Login</a>
30
- </li>
31
  </ul>
32
 
33
  <!-- Hamburger Menu -->
34
  <button
35
  id="hamburger"
36
- class="md:hidden text-[var(--latte-cream)] text-2xl ml-auto"
37
  >
38
 
39
  </button>
@@ -44,31 +44,75 @@
44
  id="mobile-menu"
45
  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"
46
  >
47
- <li><a href="index.html" class="block nav-link">Home</a></li>
48
- <li><a href="analyzer.html" class="block nav-link">Analyzer</a></li>
49
- <li><a href="profile.html" class="block nav-link">Profile</a></li>
50
- <li><a href="login.html" class="block nav-link">Login</a></li>
51
- <li><a href="about.html" class="block nav-link">About</a></li>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  </ul>
53
  </nav>
54
 
55
- <!-- Shared helpers -->
56
  <script src="script.js"></script>
57
  <script>
58
  const hamburger = document.getElementById("hamburger");
59
  const mobileMenu = document.getElementById("mobile-menu");
60
- hamburger.addEventListener("click", () =>
61
- mobileMenu.classList.toggle("hidden")
62
- );
63
 
64
- // Active page underline
 
 
 
 
65
  const currentPath = window.location.pathname.split("/").pop();
 
66
  document.querySelectorAll(".nav-link").forEach((link) => {
67
- if (link.getAttribute("href") === currentPath)
 
 
 
 
 
 
68
  link.classList.add("active-page");
 
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">
@@ -115,54 +159,33 @@
115
  Findings and Recommendations will appear here.
116
  </div>
117
  </div>
118
- </main>
119
-
120
- <!-- Floating Chat Button -->
121
- <button
122
- id="chat-toggle"
123
- class="fixed bottom-6 right-6 bg-[var(--tropical-indigo)] text-white px-5 py-3 rounded-full shadow-lg hover:scale-105 transition"
124
- >
125
- 💬 Chat
126
- </button>
127
 
128
- <!-- Chatbot Drawer -->
129
- <div
130
- id="chat-drawer"
131
- 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"
132
- >
133
- <div class="flex justify-between items-center p-4 border-b">
134
- <h3 class="text-lg font-semibold">Ask Chatbot</h3>
135
- <button id="chat-close" class="text-gray-600 hover:text-black text-xl">
136
-
137
- </button>
138
- </div>
139
- <div id="chat-output" class="flex-1 overflow-auto p-4 space-y-2 text-sm">
140
- <p><strong>Chatbot:</strong> Ask me something about your report</p>
141
- </div>
142
- <div class="flex gap-2 p-4 border-t">
143
- <input
144
- type="text"
145
- id="user-question"
146
- placeholder="Ask a question..."
147
- class="flex-1 rounded px-3 py-2 focus:outline-none border"
148
- />
149
- <button id="ask-btn" class="btn-primary px-4 py-2 rounded">Ask</button>
150
  </div>
151
- </div>
152
-
153
- <script>
154
- const chatDrawer = document.getElementById("chat-drawer");
155
- const chatToggle = document.getElementById("chat-toggle");
156
- const chatClose = document.getElementById("chat-close");
157
- chatToggle.addEventListener("click", () =>
158
- chatDrawer.classList.toggle("translate-x-full")
159
- );
160
- chatClose.addEventListener("click", () =>
161
- chatDrawer.classList.add("translate-x-full")
162
- );
163
- </script>
164
 
165
- <!-- Firebase Auth + Firestore -->
166
  <script type="module">
167
  import {
168
  getFirestore,
@@ -179,12 +202,12 @@
179
  } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-auth.js";
180
 
181
  const firebaseConfig = {
182
- apiKey: "AIzaSyAKWstCc7HqMgV8DmH2Sy6QwUP3JvZxu9g",
183
- authDomain: "appchallenge-c8fe3.firebaseapp.com",
184
- projectId: "appchallenge-c8fe3",
185
- storageBucket: "appchallenge-c8fe3.firebasestorage.app",
186
- messagingSenderId: "943791692455",
187
- appId: "1:943791692455:web:ae04d06bf09669b6bdd982",
188
  };
189
 
190
  const app = initializeApp(firebaseConfig);
@@ -195,39 +218,47 @@
195
  window.firestoreDb = db;
196
  window.firestoreHelpers = { collection, doc, addDoc, serverTimestamp };
197
 
198
- onAuthStateChanged(auth, (user) => {
199
- const authNavItem = document.getElementById("authNavItem");
200
- const profileNavDesktop = document.querySelector(
201
- 'ul.md\\:flex li a[href="profile.html"]'
202
- )?.parentElement;
203
- const pastNavDesktop = document.querySelector(
204
- 'ul.md\\:flex li a[href="past_data.html"]'
205
- )?.parentElement;
206
- const profileNavMobile = document.querySelector(
207
- '#mobile-menu a[href="profile.html"]'
208
- )?.parentElement;
209
- const pastNavMobile = document.querySelector(
210
- '#mobile-menu a[href="past_data.html"]'
211
- )?.parentElement;
212
-
213
- if (authNavItem) {
214
- if (user) {
215
- authNavItem.innerHTML =
216
- '<button onclick="logout()" class="hover:text-[#6B9080] text-red-600">Logout</button>';
217
- if (profileNavDesktop) profileNavDesktop.style.display = "block";
218
- if (pastNavDesktop) pastNavDesktop.style.display = "block";
219
- if (profileNavMobile) profileNavMobile.style.display = "block";
220
- if (pastNavMobile) pastNavMobile.style.display = "block";
221
- } else {
222
- authNavItem.innerHTML =
223
- '<a href="login.html" class="hover:text-[#6B9080]">Login</a>';
224
- if (profileNavDesktop) profileNavDesktop.style.display = "none";
225
- if (pastNavDesktop) pastNavDesktop.style.display = "none";
226
- if (profileNavMobile) profileNavMobile.style.display = "none";
227
- if (pastNavMobile) pastNavMobile.style.display = "none";
228
- }
229
- }
230
- });
 
 
 
 
 
 
 
 
231
 
232
  window.logout = async () => {
233
  try {
@@ -240,7 +271,6 @@
240
  };
241
  </script>
242
 
243
- <!-- Analyzer + Chatbot -->
244
  <script type="module">
245
  import {
246
  pipeline,
@@ -250,12 +280,39 @@
250
  const loadingEl = document.getElementById("loading");
251
  const textOutput = document.getElementById("text-output");
252
  const recsOutput = document.getElementById("recommendations-output");
253
- const chat = document.getElementById("chat-output");
 
 
254
  const authStatus = document.getElementById("auth-status");
255
  let extractedText = "";
256
  let currentUser = null;
257
 
258
- function renderRecCard(rec, idx) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  const sev = (rec.severity || "").toLowerCase();
260
  const sevClass = sev.includes("severe")
261
  ? "badge-high"
@@ -272,21 +329,21 @@
272
  </div>
273
  <ul class="rec-content space-y-1">
274
  <li><em>Recommendations:</em>
275
- <ul class="list-disc list-inside ml-6">${(
276
- rec.recommendations || []
277
- )
278
- .map((r) => `<li>${r}</li>`)
279
- .join("")}</ul>
280
  </li>
281
  <li><em>Treatment:</em> ${
282
  rec.treatment_suggestions || "Not available"
283
  }</li>
284
  <li><em>Home Care:</em>
285
- <ul class="list-disc list-inside ml-6">${(
286
- rec.home_care_guidance || []
287
- )
288
- .map((r) => `<li>${r}</li>`)
289
- .join("")}</ul>
290
  </li>
291
  ${
292
  rec.info_link
@@ -294,39 +351,19 @@
294
  : ""
295
  }
296
  </ul>
297
- </div>`;
298
- }
 
299
 
300
  window.onAuthStateChanged(window.firebaseAuth, (user) => {
301
  currentUser = user;
302
- authStatus.textContent = user
303
- ? `Signed in as ${user.email || user.uid}`
304
- : "Not signed in. Sign in to save and view past analyses.";
305
- });
306
-
307
- document
308
- .getElementById("pdf-upload")
309
- .addEventListener("change", function () {
310
- loadingEl.textContent = this.files.length
311
- ? `File selected: ${this.files[0].name}`
312
- : "No file uploaded yet.";
313
- });
314
-
315
- async function postReportToBackend(report) {
316
- try {
317
- const response = await fetch(api("save_report/"), {
318
- method: "POST",
319
- headers: { "Content-Type": "application/json" },
320
- body: JSON.stringify(report),
321
- });
322
- if (!response.ok)
323
- throw new Error(`HTTP error! status: ${response.status}`);
324
- const data = await response.json();
325
- console.log("Report sent to backend:", data);
326
- } catch (error) {
327
- console.error("Error sending report:", error);
328
  }
329
- }
330
 
331
  document
332
  .getElementById("analyze-btn")
@@ -367,59 +404,112 @@
367
  : data.Detected_Anomolies
368
  ? [data.Detected_Anomolies]
369
  : [];
370
- recsOutput.innerHTML = recs.length
371
- ? recs.map(renderRecCard).join("")
372
- : "No recommendations found.";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
373
 
374
  if (currentUser) {
 
 
 
 
 
 
375
  await postReportToBackend({
376
  user_id: currentUser.email,
377
  report_date: new Date(),
378
  ocr_text: extractedText,
379
  anomalies: JSON.stringify(recs),
 
380
  });
381
  }
 
382
  loadingEl.textContent = "Analysis complete.";
383
  });
384
 
385
- document.getElementById("ask-btn").onclick = async () => {
386
- const q = document.getElementById("user-question").value.trim();
387
- if (!q) return;
388
- if (!extractedText) {
389
- alert("Please analyze a document first.");
390
- return;
391
- }
392
-
393
- chat.innerHTML += `<p><strong>You:</strong> ${q}</p>`;
394
- chat.innerHTML += `<p><strong>Chatbot:</strong> <em>Thinking...</em></p>`;
395
- chat.scrollTop = chat.scrollHeight;
396
-
397
- try {
398
- const res = await fetch(api("chat/"), {
399
- method: "POST",
400
- headers: { "Content-Type": "application/json" },
401
- body: JSON.stringify({
402
- question: q,
403
- user_id: currentUser ? currentUser.uid : "anonymous",
404
- }),
405
- });
406
- if (!res.ok) throw new Error(await res.text());
407
- const data = await res.json();
408
- const last = chat.querySelectorAll("p");
409
- last[
410
- last.length - 1
411
- ].innerHTML = `<strong>Chatbot:</strong> ${data.answer}`;
412
- } catch (err) {
413
- console.error("Error:", err);
414
- const last = chat.querySelectorAll("p");
415
- last[
416
- last.length - 1
417
- ].innerHTML = `<strong>Chatbot:</strong> Sorry, error: ${err.message}`;
418
  }
419
 
420
- document.getElementById("user-question").value = "";
421
- chat.scrollTop = chat.scrollHeight;
422
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
423
  </script>
424
  </body>
425
- </html>
 
1
+ web/analyzer.html<!DOCTYPE html>
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8" />
 
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <link rel="stylesheet" href="style.css" />
9
+
10
  </head>
11
  <body class="bg-[var(--latte-cream)] font-sans text-gray-800 min-h-screen">
12
  <!-- NAVBAR -->
13
  <nav
14
  class="fixed top-0 left-0 w-full z-50 backdrop-blur-md bg-white/20 border-b border-white/30 shadow-md"
15
  >
16
+ <div class="flex justify-between items-center w-full px-6 py-4">
17
+ <!-- Logo -->
18
  <a
19
  href="index.html"
20
  class="text-2xl font-bold text-black hover:text-[var(--tropical-indigo)] transition"
 
21
  >
22
+ CTRL + ALT + HEAL
23
+ </a>
24
 
25
+ <ul class="hidden md:flex space-x-6 font-medium text-gray-800">
26
+ <li><a href="index.html" class="nav-link ">Home</a></li>
27
+ <li><a href="profile.html" class="nav-link ">Profile</a></li>
 
28
  <li><a href="analyzer.html" class="nav-link">Analyzer</a></li>
29
+ <li><a href="past_data.html" class="nav-link ">Past Reports</a></li>
30
+ <li id="authNavItem"><a href="login.html" class="nav-link">Login</a></li>
 
 
31
  </ul>
32
 
33
  <!-- Hamburger Menu -->
34
  <button
35
  id="hamburger"
36
+ class="md:hidden text-[var(--latte-cream)] text-2xl"
37
  >
38
 
39
  </button>
 
44
  id="mobile-menu"
45
  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"
46
  >
47
+ <li>
48
+ <a
49
+ href="index.html"
50
+ class="block text-gray-800 hover:text-[var(--tropical-indigo)]"
51
+ >Home</a
52
+ >
53
+ </li>
54
+ <li>
55
+ <a
56
+ href="analyzer.html"
57
+ class="block text-gray-800 hover:text-[var(--tropical-indigo)]"
58
+ >Analyzer</a
59
+ >
60
+ </li>
61
+ <li>
62
+ <a
63
+ href="profile.html"
64
+ class="block text-gray-800 hover:text-[var(--tropical-indigo)]"
65
+ >Profile</a
66
+ >
67
+ </li>
68
+ <li>
69
+ <a
70
+ href="login.html"
71
+ class="block text-gray-800 hover:text-[var(--tropical-indigo)]"
72
+ >Login</a
73
+ >
74
+ </li>
75
+ <li>
76
+ <a
77
+ href="about.html"
78
+ class="block text-gray-800 hover:text-[var(--tropical-indigo)]"
79
+ >About</a
80
+ >
81
+ </li>
82
+
83
+ >
84
+ </li>
85
  </ul>
86
  </nav>
87
 
88
+ <!--Shared helpers (API base + query params) -->
89
  <script src="script.js"></script>
90
  <script>
91
  const hamburger = document.getElementById("hamburger");
92
  const mobileMenu = document.getElementById("mobile-menu");
 
 
 
93
 
94
+ hamburger.addEventListener("click", () => {
95
+ mobileMenu.classList.toggle("hidden");
96
+ });
97
+
98
+ // Underline the active page only
99
  const currentPath = window.location.pathname.split("/").pop();
100
+
101
  document.querySelectorAll(".nav-link").forEach((link) => {
102
+ if (link.getAttribute("href") === currentPath) {
103
+ link.classList.add("active-page"); // we'll style this in CSS
104
+ }
105
+ });
106
+
107
+ document.querySelectorAll("#mobile-menu a").forEach((link) => {
108
+ if (link.getAttribute("href") === currentPath) {
109
  link.classList.add("active-page");
110
+ }
111
  });
112
  </script>
113
 
114
+ <!-- CHANGE MARKER TO USE THIS -->
115
+ <!-- secound change marker-->
116
  <main class="max-w-5xl mx-auto px-4 mb-16 pt-24">
117
  <!-- Upload Section -->
118
  <div class="bg-white border border-gray-200 rounded-lg p-6 shadow mb-6">
 
159
  Findings and Recommendations will appear here.
160
  </div>
161
  </div>
 
 
 
 
 
 
 
 
 
162
 
163
+ <!-- Chatbot -->
164
+ <div
165
+ class="bg-[var(--latte-cream)] border border-[var(--wisteria)] rounded-lg p-6 mb-8"
166
+ >
167
+ <h3 class="text-lg font-semibold mb-3">Ask Chatbot</h3>
168
+ <div
169
+ id="chat-output"
170
+ class="space-y-2 h-48 overflow-auto text-sm rounded p-4"
171
+ >
172
+ <p><strong>Chatbot:</strong> Ask me something about your report</p>
173
+ </div>
174
+ <div class="flex mt-4 gap-2">
175
+ <input
176
+ type="text"
177
+ id="user-question"
178
+ placeholder="Ask a question..."
179
+ class="flex-1 rounded px-3 py-2 focus:outline-none"
180
+ />
181
+ <button id="ask-btn" class="btn-primary px-4 py-2 rounded">
182
+ Ask
183
+ </button>
184
+ </div>
185
  </div>
186
+ </main>
 
 
 
 
 
 
 
 
 
 
 
 
187
 
188
+ <!-- Firebase -->
189
  <script type="module">
190
  import {
191
  getFirestore,
 
202
  } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-auth.js";
203
 
204
  const firebaseConfig = {
205
+ apiKey: "AIzaSyAPhM_Ee7cLzyKHs5zyFy8g5ZOk9-pubRI",
206
+ authDomain: "login-tutorial-7a9e1.firebaseapp.com",
207
+ projectId: "login-tutorial-7a9e1",
208
+ storageBucket: "login-tutorial-7a9e1.firebasestorage.app",
209
+ messagingSenderId: "491093197824",
210
+ appId: "1:491093197824:web:9f866...",
211
  };
212
 
213
  const app = initializeApp(firebaseConfig);
 
218
  window.firestoreDb = db;
219
  window.firestoreHelpers = { collection, doc, addDoc, serverTimestamp };
220
 
221
+ onAuthStateChanged(auth, (user) => {
222
+ const authNavItem = document.getElementById("authNavItem");
223
+
224
+ // Desktop links
225
+ const homeNavDesktop = document.querySelector('ul.md\\:flex li a[href="index.html"]')?.parentElement;
226
+ const profileNavDesktop = document.querySelector('ul.md\\:flex li a[href="profile.html"]')?.parentElement;
227
+ const pastNavDesktop = document.querySelector('ul.md\\:flex li a[href="past_data.html"]')?.parentElement;
228
+
229
+ // Mobile links
230
+ const profileNavMobile = document.querySelector('#mobile-menu a[href="profile.html"]')?.parentElement;
231
+ const pastNavMobile = document.querySelector('#mobile-menu a[href="past_data.html"]')?.parentElement;
232
+ const homeNavMobile = document.querySelector('#mobile-menu a[href="index.html"]')?.parentElement;
233
+ if (authNavItem) {
234
+ if (user) {
235
+ authNavItem.innerHTML =
236
+ '<button onclick="logout()" class="hover:text-[#6B9080] text-red-600">Logout</button>';
237
+
238
+ // Hide Home
239
+ if (homeNavDesktop) homeNavDesktop.style.display = "none";
240
+ if (homeNavMobile) homeNavMobile.style.display = "none";
241
+ // Show Profile and Past Reports
242
+
243
+ if (profileNavDesktop) profileNavDesktop.style.display = "block";
244
+ if (pastNavDesktop) pastNavDesktop.style.display = "block";
245
+ if (profileNavMobile) profileNavMobile.style.display = "block";
246
+ if (pastNavMobile) pastNavMobile.style.display = "block";
247
+ } else {
248
+ authNavItem.innerHTML =
249
+ '<a href="login.html" class="hover:text-[#6B9080]">Login</a>';
250
+
251
+ // Show Home
252
+ if (homeNavDesktop) homeNavDesktop.style.display = "block";
253
+ if (homeNavMobile) homeNavMobile.style.display = "block";
254
+ // Hide Profile and Past Reports
255
+ if (profileNavDesktop) profileNavDesktop.style.display = "none";
256
+ if (pastNavDesktop) pastNavDesktop.style.display = "none";
257
+ if (profileNavMobile) profileNavMobile.style.display = "none";
258
+ if (pastNavMobile) pastNavMobile.style.display = "none";
259
+ }
260
+ }
261
+ });
262
 
263
  window.logout = async () => {
264
  try {
 
271
  };
272
  </script>
273
 
 
274
  <script type="module">
275
  import {
276
  pipeline,
 
280
  const loadingEl = document.getElementById("loading");
281
  const textOutput = document.getElementById("text-output");
282
  const recsOutput = document.getElementById("recommendations-output");
283
+ const findingsOutput = document.getElementById(
284
+ "detected-measurement-results"
285
+ );
286
  const authStatus = document.getElementById("auth-status");
287
  let extractedText = "";
288
  let currentUser = null;
289
 
290
+ document
291
+ .getElementById("pdf-upload")
292
+ .addEventListener("change", function () {
293
+ loadingEl.textContent = this.files.length
294
+ ? `File selected: ${this.files[0].name}`
295
+ : "No file uploaded yet.";
296
+ });
297
+
298
+ const saveAnalysis = async (uid, payload) => {
299
+ try {
300
+ const docRef = await firebase
301
+ .firestore()
302
+ .collection("users")
303
+ .doc(uid)
304
+ .collection("analyses")
305
+ .add({
306
+ ...payload,
307
+ createdAt: serverTimestamp(),
308
+ });
309
+ return docRef.id;
310
+ } catch (e) {
311
+ console.error("Failed to save analysis:", e);
312
+ }
313
+ };
314
+
315
+ const renderRecCard = (rec, idx) => {
316
  const sev = (rec.severity || "").toLowerCase();
317
  const sevClass = sev.includes("severe")
318
  ? "badge-high"
 
329
  </div>
330
  <ul class="rec-content space-y-1">
331
  <li><em>Recommendations:</em>
332
+ <ul class="list-disc list-inside ml-6">
333
+ ${(rec.recommendations || [])
334
+ .map((r) => `<li>${r}</li>`)
335
+ .join("")}
336
+ </ul>
337
  </li>
338
  <li><em>Treatment:</em> ${
339
  rec.treatment_suggestions || "Not available"
340
  }</li>
341
  <li><em>Home Care:</em>
342
+ <ul class="list-disc list-inside ml-6">
343
+ ${(rec.home_care_guidance || [])
344
+ .map((r) => `<li>${r}</li>`)
345
+ .join("")}
346
+ </ul>
347
  </li>
348
  ${
349
  rec.info_link
 
351
  : ""
352
  }
353
  </ul>
354
+ </div>
355
+ `;
356
+ };
357
 
358
  window.onAuthStateChanged(window.firebaseAuth, (user) => {
359
  currentUser = user;
360
+ if (user) {
361
+ authStatus.textContent = `Signed in as ${user.email || user.uid}`;
362
+ } else {
363
+ authStatus.textContent =
364
+ "Not signed in. Sign in to save and view past analyses.";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
365
  }
366
+ });
367
 
368
  document
369
  .getElementById("analyze-btn")
 
404
  : data.Detected_Anomolies
405
  ? [data.Detected_Anomolies]
406
  : [];
407
+ if (data.Detected_Anomolies) {
408
+ recsOutput.innerHTML = recs
409
+ .map((rec, i) => renderRecCard(rec, i))
410
+ .join("");
411
+ } else {
412
+ recsOutput.textContent = "No recommendations found.";
413
+ }
414
+
415
+ const findings = Array.isArray(data.Detected_Measurement_Values)
416
+ ? data.Detected_Measurement_Values
417
+ : data.Detected_Measurement_Values
418
+ ? [data.Detected_Measurement_Values]
419
+ : [];
420
+ if (data.Detected_Measurement_Values) {
421
+ findingsOutput.innerHTML = findings
422
+ .map((finding, i) => renderRecCard(finding, i))
423
+ .join("");
424
+ }
425
 
426
  if (currentUser) {
427
+ /*await saveAnalysis(currentUser.uid, {
428
+ reportDate: date,
429
+ ocr_text: extractedText,
430
+ resolutions: recs,
431
+ measurements: findings,
432
+ });*/
433
  await postReportToBackend({
434
  user_id: currentUser.email,
435
  report_date: new Date(),
436
  ocr_text: extractedText,
437
  anomalies: JSON.stringify(recs),
438
+ measurements: JSON.stringify(findings),
439
  });
440
  }
441
+
442
  loadingEl.textContent = "Analysis complete.";
443
  });
444
 
445
+ async function postReportToBackend(report) {
446
+ try {
447
+ const response = await fetch(api('save_report/'), {
448
+ method: 'POST',
449
+ headers: {
450
+ 'Content-Type': 'application/json',
451
+ },
452
+ body: JSON.stringify(report),
453
+ });
454
+ if (!response.ok) {
455
+ throw new Error(`HTTP error! status: ${response.status}`);
456
+ }
457
+ const data = await response.json();
458
+ console.log('Report successfully sent to backend:', data);
459
+ } catch (error) {
460
+ console.error('Error sending report to backend:', error);
461
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
462
  }
463
 
464
+ document.getElementById("ask-btn").onclick = async () => {
465
+ const q = document.getElementById("user-question").value.trim();
466
+ if (!q) return;
467
+
468
+ if (!extractedText) {
469
+ alert("Please analyze a document first before asking questions.");
470
+ return;
471
+ }
472
+
473
+ const chat = document.getElementById("chat-output");
474
+ chat.innerHTML += `<p><strong>You:</strong> ${q}</p>`;
475
+ chat.scrollTop = chat.scrollHeight;
476
+
477
+ chat.innerHTML += `<p><strong>Chatbot:</strong> <em>Thinking...</em></p>`;
478
+ chat.scrollTop = chat.scrollHeight;
479
+
480
+ try {
481
+ const response = await fetch(api('chat/'), {
482
+ method: "POST",
483
+ headers: {
484
+ "Content-Type": "application/json",
485
+ },
486
+ body: JSON.stringify({
487
+ question: q,
488
+ user_id: currentUser ? currentUser.uid : "anonymous"
489
+ }),
490
+ });
491
+
492
+ if (!response.ok) {
493
+ throw new Error(`HTTP error! status: ${response.status}`);
494
+ }
495
+
496
+ const data = await response.json();
497
+
498
+ //now addign acctual reposnse
499
+ const messages = chat.querySelectorAll('p');
500
+ const lastMessage = messages[messages.length - 1];
501
+ lastMessage.innerHTML = `<strong>Chatbot:</strong> ${data.answer}`;
502
+
503
+ } catch (error) {
504
+ console.error("Error:", error);
505
+ const messages = chat.querySelectorAll('p');
506
+ const lastMessage = messages[messages.length - 1];
507
+ lastMessage.innerHTML = `<strong>Chatbot:</strong> Sorry, I encountered an error: ${error.message}`;
508
+ }
509
+
510
+ document.getElementById("user-question").value = "";
511
+ chat.scrollTop = chat.scrollHeight;
512
+ };
513
  </script>
514
  </body>
515
+ </html>
web/analyzer_update.html DELETED
@@ -1,472 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <title>Document Analyzer | CTRL + ALT + HEAL</title>
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <script src="https://cdn.tailwindcss.com"></script>
8
- <link rel="stylesheet" href="style.css" />
9
- </head>
10
- <body class="bg-[var(--latte-cream)] font-sans text-gray-800 min-h-screen">
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 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
- >
20
- CTRL + ALT + HEAL
21
- </a>
22
-
23
- <!-- Desktop Links -->
24
- <ul class="hidden md:flex space-x-6 font-medium text-gray-800 ml-auto">
25
- <li><a href="index.html" class="nav-link">Home</a></li>
26
- <li><a href="profile.html" class="nav-link">Profile</a></li>
27
- <li><a href="analyzer.html" class="nav-link">Analyzer</a></li>
28
- <li><a href="past_data.html" class="nav-link">Past Reports</a></li>
29
- <li id="authNavItem">
30
- <a href="login.html" class="nav-link">Login</a>
31
- </li>
32
- </ul>
33
-
34
- <!-- Hamburger Menu -->
35
- <button
36
- id="hamburger"
37
- class="md:hidden text-[var(--latte-cream)] text-2xl ml-auto"
38
- >
39
-
40
- </button>
41
- </div>
42
-
43
- <!-- Mobile Menu -->
44
- <ul
45
- id="mobile-menu"
46
- 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"
47
- >
48
- <li><a href="index.html" class="block nav-link">Home</a></li>
49
- <li><a href="analyzer.html" class="block nav-link">Analyzer</a></li>
50
- <li><a href="profile.html" class="block nav-link">Profile</a></li>
51
- <li><a href="login.html" class="block nav-link">Login</a></li>
52
- <li><a href="about.html" class="block nav-link">About</a></li>
53
- </ul>
54
- </nav>
55
-
56
- <script src="script.js"></script>
57
- <script>
58
- const hamburger = document.getElementById("hamburger");
59
- const mobileMenu = document.getElementById("mobile-menu");
60
- hamburger.addEventListener("click", () =>
61
- mobileMenu.classList.toggle("hidden")
62
- );
63
-
64
- const currentPath = window.location.pathname.split("/").pop();
65
- document.querySelectorAll(".nav-link").forEach((link) => {
66
- if (link.getAttribute("href") === currentPath)
67
- link.classList.add("active-page");
68
- });
69
- </script>
70
-
71
- <main class="max-w-5xl mx-auto px-4 mb-16 pt-24">
72
- <div class="bg-white border border-gray-200 rounded-lg p-6 shadow mb-6">
73
- <h2 class="text-xl font-semibold mb-4">
74
- Upload & Analyze Your Medical PDF or Image
75
- </h2>
76
- <input
77
- type="file"
78
- id="pdf-upload"
79
- accept=".pdf, image/*"
80
- class="w-full mb-4 rounded px-3 py-2"
81
- />
82
- <button id="analyze-btn" class="btn-primary px-4 py-2 rounded">
83
- Analyze with AI
84
- </button>
85
- <p id="loading" class="text-gray-600 mt-2">No file uploaded yet.</p>
86
- <p id="auth-status" class="text-sm text-gray-500 mt-1">
87
- Sign in to save and view past analyses.
88
- </p>
89
- </div>
90
-
91
- <div
92
- class="bg-[var(--latte-cream)] border border-[var(--wisteria)] rounded-lg p-6 mb-8"
93
- >
94
- <h3 class="text-lg font-semibold mb-3">Extracted Text</h3>
95
- <div
96
- id="text-output"
97
- class="whitespace-pre-wrap h-60 overflow-auto bg-[#FAFBFC] text-sm border border-[var(--wisteria)] rounded p-4"
98
- >
99
- OCR results will appear here.
100
- </div>
101
- </div>
102
-
103
- <div
104
- class="bg-[var(--latte-cream)] border border-[var(--wisteria)] rounded-lg p-6 mb-8"
105
- >
106
- <h3 class="text-lg font-semibold mb-3">AI Findings</h3>
107
- <div
108
- id="recommendations-output"
109
- class="bg-[#F9FAFB] text-sm border border-[var(--wisteria)] rounded p-4 space-y-4"
110
- >
111
- Findings and Recommendations will appear here.
112
- </div>
113
- </div>
114
- </main>
115
-
116
- <button
117
- id="chat-toggle"
118
- class="fixed bottom-6 right-6 bg-[var(--tropical-indigo)] text-white px-5 py-3 rounded-full shadow-lg hover:scale-105 transition"
119
- >
120
- 💬 Chat
121
- </button>
122
-
123
- <div
124
- id="chat-drawer"
125
- 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"
126
- >
127
- <div class="flex justify-between items-center p-4 border-b">
128
- <h3 class="text-lg font-semibold">Ask Chatbot</h3>
129
- <button id="chat-close" class="text-gray-600 hover:text-black text-xl">
130
-
131
- </button>
132
- </div>
133
- <div id="chat-output" class="flex-1 overflow-auto p-4 space-y-2 text-sm">
134
- <p><strong>Chatbot:</strong> Ask me something about your report</p>
135
- </div>
136
- <div class="flex gap-2 p-4 border-t">
137
- <input
138
- type="text"
139
- id="user-question"
140
- placeholder="Ask a question..."
141
- class="flex-1 rounded px-3 py-2 focus:outline-none border"
142
- />
143
- <button id="ask-btn" class="btn-primary px-4 py-2 rounded">Ask</button>
144
- </div>
145
- </div>
146
-
147
- <script>
148
- const chatDrawer = document.getElementById("chat-drawer");
149
- const chatToggle = document.getElementById("chat-toggle");
150
- const chatClose = document.getElementById("chat-close");
151
- chatToggle.addEventListener("click", () =>
152
- chatDrawer.classList.toggle("translate-x-full")
153
- );
154
- chatClose.addEventListener("click", () =>
155
- chatDrawer.classList.add("translate-x-full")
156
- );
157
- </script>
158
-
159
- <script type="module">
160
- import {
161
- getFirestore,
162
- collection,
163
- doc,
164
- addDoc,
165
- setDoc,
166
- getDoc,
167
- serverTimestamp,
168
- } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";
169
- import { initializeApp } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-app.js";
170
- import {
171
- getAuth,
172
- onAuthStateChanged,
173
- signOut,
174
- } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-auth.js";
175
-
176
- const firebaseConfig = {
177
- apiKey: "AIzaSyAKWstCc7HqMgV8DmH2Sy6QwUP3JvZxu9g",
178
- authDomain: "appchallenge-c8fe3.firebaseapp.com",
179
- projectId: "appchallenge-c8fe3",
180
- storageBucket: "appchallenge-c8fe3.firebasestorage.app",
181
- messagingSenderId: "943791692455",
182
- appId: "1:943791692455:web:ae04d06bf09669b6bdd982",
183
- };
184
-
185
- const app = initializeApp(firebaseConfig);
186
- const auth = getAuth(app);
187
- const db = getFirestore(app);
188
- window.firebaseAuth = auth;
189
- window.firestoreDb = db;
190
- window.firestoreHelpers = {
191
- collection,
192
- doc,
193
- addDoc,
194
- setDoc,
195
- getDoc,
196
- serverTimestamp,
197
- };
198
-
199
- let currentUser = null;
200
-
201
- window.onAuthStateChanged(auth, (user) => {
202
- currentUser = user;
203
- const authNavItem = document.getElementById("authNavItem");
204
- const profileNavDesktop = document.querySelector(
205
- 'ul.md\\:flex li a[href="profile.html"]'
206
- )?.parentElement;
207
- const pastNavDesktop = document.querySelector(
208
- 'ul.md\\:flex li a[href="past_data.html"]'
209
- )?.parentElement;
210
- const profileNavMobile = document.querySelector(
211
- '#mobile-menu a[href="profile.html"]'
212
- )?.parentElement;
213
- const pastNavMobile = document.querySelector(
214
- '#mobile-menu a[href="past_data.html"]'
215
- )?.parentElement;
216
-
217
- if (authNavItem) {
218
- if (user) {
219
- authNavItem.innerHTML =
220
- '<button onclick="logout()" class="hover:text-[#6B9080] text-red-600">Logout</button>';
221
- if (profileNavDesktop) profileNavDesktop.style.display = "block";
222
- if (pastNavDesktop) pastNavDesktop.style.display = "block";
223
- if (profileNavMobile) profileNavMobile.style.display = "block";
224
- if (pastNavMobile) pastNavMobile.style.display = "block";
225
- } else {
226
- authNavItem.innerHTML =
227
- '<a href="login.html" class="hover:text-[#6B9080]">Login</a>';
228
- if (profileNavDesktop) profileNavDesktop.style.display = "none";
229
- if (pastNavDesktop) pastNavDesktop.style.display = "none";
230
- if (profileNavMobile) profileNavMobile.style.display = "none";
231
- if (pastNavMobile) pastNavMobile.style.display = "none";
232
- }
233
- }
234
- });
235
-
236
- window.logout = async () => {
237
- try {
238
- await signOut(auth);
239
- localStorage.clear();
240
- window.location.href = "login.html";
241
- } catch (error) {
242
- console.error("Error signing out:", error);
243
- }
244
- };
245
-
246
- // ---------------------- Analyzer ----------------------
247
- const loadingEl = document.getElementById("loading");
248
- const textOutput = document.getElementById("text-output");
249
- const recsOutput = document.getElementById("recommendations-output");
250
- const chat = document.getElementById("chat-output");
251
- const authStatus = document.getElementById("auth-status");
252
- let extractedText = "";
253
-
254
- function renderRecCard(rec, idx) {
255
- const sev = (rec.severity || "").toLowerCase();
256
- const sevClass = sev.includes("severe")
257
- ? "badge-high"
258
- : sev.includes("moderate")
259
- ? "badge-medium"
260
- : "badge-low";
261
- return `
262
- <div class="rec-card">
263
- <div class="flex items-center justify-between">
264
- <h4 class="rec-title">Finding ${idx + 1}: ${
265
- rec.findings || "N/A"
266
- }</h4>
267
- <span class="rec-badge ${sevClass}">${rec.severity || "—"}</span>
268
- </div>
269
- <ul class="rec-content space-y-1">
270
- <li><em>Recommendations:</em>
271
- <ul class="list-disc list-inside ml-6">${(
272
- rec.recommendations || []
273
- )
274
- .map((r) => `<li>${r}</li>`)
275
- .join("")}</ul>
276
- </li>
277
- <li><em>Treatment:</em> ${
278
- rec.treatment_suggestions || "Not available"
279
- }</li>
280
- <li><em>Home Care:</em>
281
- <ul class="list-disc list-inside ml-6">${(
282
- rec.home_care_guidance || []
283
- )
284
- .map((r) => `<li>${r}</li>`)
285
- .join("")}</ul>
286
- </li>
287
- ${
288
- rec.info_link
289
- ? `<li><a href="${rec.info_link}" target="_blank" class="rec-link">Learn more</a></li>`
290
- : ""
291
- }
292
- </ul>
293
- </div>`;
294
- }
295
-
296
- window.onAuthStateChanged(auth, (user) => {
297
- currentUser = user;
298
- authStatus.textContent = user
299
- ? `Signed in as ${user.email || user.uid}`
300
- : "Not signed in. Sign in to save and view past analyses.";
301
- });
302
-
303
- document
304
- .getElementById("pdf-upload")
305
- .addEventListener("change", function () {
306
- loadingEl.textContent = this.files.length
307
- ? `File selected: ${this.files[0].name}`
308
- : "No file uploaded yet.";
309
- });
310
-
311
- async function postReportToBackend(report) {
312
- try {
313
- const response = await fetch(api("save_report/"), {
314
- method: "POST",
315
- headers: { "Content-Type": "application/json" },
316
- body: JSON.stringify(report),
317
- });
318
- if (!response.ok)
319
- throw new Error(`HTTP error! status: ${response.status}`);
320
- const data = await response.json();
321
- console.log("Report sent to backend:", data);
322
- } catch (error) {
323
- console.error("Error sending report:", error);
324
- }
325
- }
326
-
327
- // -------- New: Save Latest Test Results with Smart Update --------
328
- async function saveLatestResults(csvData) {
329
- if (!currentUser) return;
330
-
331
- try {
332
- const docRef = doc(db, "latest_test_results", currentUser.email);
333
- const docSnap = await getDoc(docRef);
334
- let results = [];
335
-
336
- if (docSnap.exists()) {
337
- results = docSnap.data().results || [];
338
- }
339
-
340
- csvData.forEach((row) => {
341
- const idx = results.findIndex((r) => r.condition === row.Condition);
342
- if (idx >= 0) {
343
- results[idx].test_result = row["Test Result"];
344
- results[idx].unit = row.Unit;
345
- } else {
346
- results.push({
347
- condition: row.Condition,
348
- test_result: row["Test Result"],
349
- unit: row.Unit,
350
- });
351
- }
352
- });
353
-
354
- await setDoc(docRef, {
355
- user_id: currentUser.email,
356
- report_date: serverTimestamp(),
357
- results: results,
358
- });
359
-
360
- console.log("Latest test results updated with smart merge!");
361
- } catch (err) {
362
- console.error("Error saving latest results:", err);
363
- }
364
- }
365
-
366
- document
367
- .getElementById("analyze-btn")
368
- .addEventListener("click", async () => {
369
- const file = document.getElementById("pdf-upload").files[0];
370
- if (!file) {
371
- loadingEl.textContent = "Please upload a file first.";
372
- return;
373
- }
374
-
375
- loadingEl.textContent = "Processing with AI...";
376
- textOutput.textContent = "";
377
- recsOutput.textContent = "";
378
-
379
- const formData = new FormData();
380
- formData.append("file", file);
381
- formData.append("model", "bert");
382
-
383
- let data;
384
- try {
385
- const res = await fetch(api("analyze/"), {
386
- method: "POST",
387
- body: formData,
388
- });
389
- if (!res.ok) throw new Error(await res.text());
390
- data = await res.json();
391
- } catch (err) {
392
- console.error(err);
393
- loadingEl.textContent = "Error during analysis: " + err.message;
394
- return;
395
- }
396
-
397
- extractedText = data.ocr_text || "";
398
- textOutput.textContent = extractedText;
399
-
400
- const recs = Array.isArray(data.Detected_Anomolies)
401
- ? data.Detected_Anomolies
402
- : data.Detected_Anomolies
403
- ? [data.Detected_Anomolies]
404
- : [];
405
-
406
- recsOutput.innerHTML = recs.length
407
- ? recs.map(renderRecCard).join("")
408
- : "No recommendations found.";
409
-
410
- if (currentUser) {
411
- await postReportToBackend({
412
- user_id: currentUser.email,
413
- report_date: new Date(),
414
- ocr_text: extractedText,
415
- anomalies: JSON.stringify(recs),
416
- });
417
- }
418
-
419
- if (currentUser && recs.length) {
420
- const csvData = recs.map((rec) => ({
421
- User: currentUser.email,
422
- Condition: rec.findings || "N/A",
423
- "Test Result": rec.test_result || "N/A",
424
- Unit: rec.unit || "",
425
- }));
426
- saveLatestResults(csvData);
427
- }
428
-
429
- loadingEl.textContent = "Analysis complete.";
430
- });
431
-
432
- // ---------------------- Chatbot ----------------------
433
- document.getElementById("ask-btn").onclick = async () => {
434
- const q = document.getElementById("user-question").value.trim();
435
- if (!q) return;
436
- if (!extractedText) {
437
- alert("Please analyze a document first.");
438
- return;
439
- }
440
- chat.innerHTML += `<p><strong>You:</strong> ${q}</p>`;
441
- chat.innerHTML += `<p><strong>Chatbot:</strong> <em>Thinking...</em></p>`;
442
- chat.scrollTop = chat.scrollHeight;
443
-
444
- try {
445
- const res = await fetch(api("chat/"), {
446
- method: "POST",
447
- headers: { "Content-Type": "application/json" },
448
- body: JSON.stringify({
449
- question: q,
450
- user_id: currentUser ? currentUser.uid : "anonymous",
451
- }),
452
- });
453
- if (!res.ok) throw new Error(await res.text());
454
- const data = await res.json();
455
- const last = chat.querySelectorAll("p");
456
- last[
457
- last.length - 1
458
- ].innerHTML = `<strong>Chatbot:</strong> ${data.answer}`;
459
- } catch (err) {
460
- console.error("Error:", err);
461
- const last = chat.querySelectorAll("p");
462
- last[
463
- last.length - 1
464
- ].innerHTML = `<strong>Chatbot:</strong> Sorry, error: ${err.message}`;
465
- }
466
-
467
- document.getElementById("user-question").value = "";
468
- chat.scrollTop = chat.scrollHeight;
469
- };
470
- </script>
471
- </body>
472
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
web/past_data.html CHANGED
@@ -98,50 +98,6 @@
98
  </div>
99
  </div>
100
  </main>
101
- <!-- Floating Chat Button -->
102
- <button
103
- id="chat-toggle"
104
- class="fixed bottom-6 right-6 bg-[var(--tropical-indigo)] text-white px-5 py-3 rounded-full shadow-lg hover:scale-105 transition"
105
- >
106
- 💬 Chat
107
- </button>
108
-
109
- <!-- Chatbot Drawer -->
110
- <div
111
- id="chat-drawer"
112
- 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"
113
- >
114
- <div class="flex justify-between items-center p-4 border-b">
115
- <h3 class="text-lg font-semibold">Ask Chatbot</h3>
116
- <button id="chat-close" class="text-gray-600 hover:text-black text-xl">
117
-
118
- </button>
119
- </div>
120
- <div id="chat-output" class="flex-1 overflow-auto p-4 space-y-2 text-sm">
121
- <p><strong>Chatbot:</strong> Ask me something about your report</p>
122
- </div>
123
- <div class="flex gap-2 p-4 border-t">
124
- <input
125
- type="text"
126
- id="user-question"
127
- placeholder="Ask a question..."
128
- class="flex-1 rounded px-3 py-2 focus:outline-none border"
129
- />
130
- <button id="ask-btn" class="btn-primary px-4 py-2 rounded">Ask</button>
131
- </div>
132
- </div>
133
-
134
- <script>
135
- const chatDrawer = document.getElementById("chat-drawer");
136
- const chatToggle = document.getElementById("chat-toggle");
137
- const chatClose = document.getElementById("chat-close");
138
- chatToggle.addEventListener("click", () =>
139
- chatDrawer.classList.toggle("translate-x-full")
140
- );
141
- chatClose.addEventListener("click", () =>
142
- chatDrawer.classList.add("translate-x-full")
143
- );
144
- </script>
145
 
146
  <script type="module">
147
  import {
 
98
  </div>
99
  </div>
100
  </main>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
 
102
  <script type="module">
103
  import {
web/profile.html CHANGED
@@ -5,31 +5,32 @@
5
  <meta name="viewport" content="width=device-width,initial-scale=1" />
6
  <title>Profile - CTRL + ALT + HEAL</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
 
8
  <link rel="stylesheet" href="style.css" />
9
  </head>
10
  <body class="bg-[#F7F8F9] min-h-screen">
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
  <a
17
  href="index.html"
18
  class="text-2xl font-bold text-black hover:text-[var(--tropical-indigo)] transition"
19
  >
20
  CTRL + ALT + HEAL
21
  </a>
 
22
  <ul class="hidden md:flex space-x-6 font-medium text-gray-800">
23
- <li>
24
- <a href="index.html" class="nav-link" style="display: none">Home</a>
25
- </li>
26
  <li><a href="profile.html" class="nav-link">Profile</a></li>
27
  <li><a href="analyzer.html" class="nav-link">Analyzer</a></li>
28
  <li><a href="past_data.html" class="nav-link">Past Reports</a></li>
29
- <li id="authNavItem">
30
- <a href="login.html" class="nav-link">Login</a>
31
- </li>
32
  </ul>
 
 
33
  <button
34
  id="hamburger"
35
  class="md:hidden text-[var(--latte-cream)] text-2xl"
@@ -37,6 +38,8 @@
37
 
38
  </button>
39
  </div>
 
 
40
  <ul
41
  id="mobile-menu"
42
  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"
@@ -76,109 +79,141 @@
76
  >About</a
77
  >
78
  </li>
 
 
 
79
  </ul>
80
  </nav>
81
 
82
  <script>
83
  const hamburger = document.getElementById("hamburger");
84
  const mobileMenu = document.getElementById("mobile-menu");
85
- hamburger.addEventListener("click", () =>
86
- mobileMenu.classList.toggle("hidden")
87
- );
88
-
89
  const currentPath = window.location.pathname.split("/").pop();
90
  document.querySelectorAll(".nav-link").forEach((link) => {
91
- if (link.getAttribute("href") === currentPath)
92
- link.classList.add("active-page");
 
93
  });
94
  document.querySelectorAll("#mobile-menu a").forEach((link) => {
95
- if (link.getAttribute("href") === currentPath)
96
  link.classList.add("active-page");
 
97
  });
98
  </script>
99
 
100
  <div class="container mx-auto px-6 pt-24">
101
  <div class="bg-white border border-gray-200 rounded-lg p-6 shadow mb-6">
102
- <!-- PROFILE VIEW -->
103
- <div id="profileViewSection">
104
- <div class="flex items-start space-x-6 mb-10">
105
- <div class="flex flex-col items-center">
106
- <div class="w-32 h-32 bg-gray-300 rounded-full"></div>
107
- <button id="editProfileBtn" class="mt-2 px-4 py-2 rounded">
108
- Edit Profile
109
- </button>
110
- <a href="analyzer.html">
111
- <button id="analyzerBtn" class="mt-2 px-4 py-2 rounded">
112
- Go To Analyzer
113
- </button>
114
- </a>
115
- </div>
116
- <div>
117
- <h2 class="text-xl font-semibold mb-4">Your Profile</h2>
118
- <p class="mb-2">
119
- Name: <span id="userName" class="font-medium">Loading...</span>
120
- </p>
121
- <p class="mb-2">
122
- DOB: <span id="userDOB" class="font-medium">Loading...</span>
123
- </p>
124
- </div>
 
 
125
  </div>
126
  </div>
 
127
 
128
- <!-- PROFILE EDIT -->
129
- <div id="profileEditSection" class="hidden mb-10">
130
- <div class="flex flex-col sm:flex-row items-start space-x-6">
131
- <div class="flex flex-col items-center mb-4 sm:mb-0">
132
- <div class="w-32 h-32 bg-gray-300 rounded-full"></div>
133
- </div>
134
- <div class="flex-1">
135
- <h1 class="text-3xl font-semibold mb-4">Edit Profile</h1>
136
- <div class="space-y-4">
137
- <div>
138
- <label for="inputName" class="block text-sm font-medium"
139
- >Name</label
140
- >
141
- <input
142
- type="text"
143
- id="inputName"
144
- class="w-full border rounded px-3 py-2 focus:ring-2 focus:ring-[#6B9080] focus:outline-none"
145
- />
146
- </div>
147
- <div>
148
- <label for="inputDOB" class="block text-sm font-medium"
149
- >Date of Birth</label
150
- >
151
- <input
152
- type="date"
153
- id="inputDOB"
154
- class="w-full border rounded px-3 py-2 focus:ring-2 focus:ring-[#6B9080] focus:outline-none"
155
- />
156
- </div>
157
  </div>
158
- <div class="flex space-x-2 mt-4">
159
- <button id="saveProfileBtn" class="px-4 py-2 rounded">
160
- Save
161
- </button>
162
- <button id="cancelProfileBtn" class="px-4 py-2 rounded">
163
- Cancel
164
- </button>
 
 
165
  </div>
166
  </div>
 
 
 
 
 
 
 
 
167
  </div>
168
  </div>
 
 
169
 
170
- <!-- RANGE BARS -->
171
- <h2 class="text-xl font-semibold mb-4">Your Current Health Summary</h2>
172
- <div class="tabs">
173
- <div class="tab active" data-tab="out-range">
174
- Out of Range <span class="count-badge">0</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
  </div>
176
- <div class="tab" data-tab="in-range">
177
- In Range <span class="count-badge">0</span>
178
  </div>
179
  </div>
180
- <div class="tab-content active" id="out-range"></div>
181
- <div class="tab-content" id="in-range"></div>
182
  </div>
183
  </div>
184
 
@@ -194,47 +229,74 @@
194
  doc,
195
  getDoc,
196
  updateDoc,
 
 
 
197
  } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";
198
-
 
199
  const firebaseConfig = {
200
- apiKey: "AIzaSyAKWstCc7HqMgV8DmH2Sy6QwUP3JvZxu9g",
201
- authDomain: "appchallenge-c8fe3.firebaseapp.com",
202
- projectId: "appchallenge-c8fe3",
203
- storageBucket: "appchallenge-c8fe3.firebasestorage.app",
204
- messagingSenderId: "943791692455",
205
- appId: "1:943791692455:web:ae04d06bf09669b6bdd982",
206
- measurementId: "G-HJ7EHW8V4N",
207
  };
208
-
 
209
  const app = initializeApp(firebaseConfig);
210
  const auth = getAuth(app);
211
  const db = getFirestore(app);
212
-
 
 
 
 
 
 
213
  let currentUser = null;
214
- const homeNavDesktop = document.querySelector(
215
- 'ul.md\\:flex li a[href="index.html"]'
216
- )?.parentElement;
217
- const homeNavMobile = document.querySelector(
218
- '#mobile-menu a[href="index.html"]'
219
- )?.parentElement;
 
220
 
221
- // Auth State Handling
222
- onAuthStateChanged(auth, async (user) => {
223
- const authNavItem = document.getElementById("authNavItem");
224
- if (user) {
225
- currentUser = user;
226
- await loadUserProfile();
227
- authNavItem.innerHTML = '<button onclick="logout()">Logout</button>';
228
- if (homeNavDesktop) homeNavDesktop.style.display = "none";
229
- if (homeNavMobile) homeNavMobile.style.display = "none";
230
- } else {
231
- authNavItem.innerHTML = '<a href="login.html">Login</a>';
232
- if (homeNavDesktop) homeNavDesktop.style.display = "";
233
- if (homeNavMobile) homeNavMobile.style.display = "";
234
- window.location.href = "login.html";
235
- }
236
- });
237
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
  async function loadUserProfile() {
239
  try {
240
  const userDoc = await getDoc(doc(db, "users", currentUser.uid));
@@ -247,33 +309,50 @@
247
  localStorage.setItem("userName", userData.name || "");
248
  localStorage.setItem("userDOB", userData.dob || "");
249
  }
250
- } catch (e) {
251
- console.error("Error loading user profile:", e);
252
  }
253
  }
254
-
 
255
  window.saveProfile = async (name, dob) => {
256
  try {
257
- await updateDoc(doc(db, "users", currentUser.uid), { name, dob });
 
 
 
258
  localStorage.setItem("userName", name);
259
  localStorage.setItem("userDOB", dob);
260
  return true;
261
- } catch (e) {
262
- console.error("Error updating profile:", e);
263
  return false;
264
  }
265
  };
266
-
 
267
  window.logout = async () => {
268
  try {
269
  await signOut(auth);
270
  localStorage.clear();
271
  window.location.href = "login.html";
272
- } catch (e) {
273
- console.error("Error signing out:", e);
274
  }
275
  };
276
-
 
 
 
 
 
 
 
 
 
 
 
 
277
  document.addEventListener("DOMContentLoaded", () => {
278
  const profileView = document.getElementById("profileViewSection");
279
  const profileEdit = document.getElementById("profileEditSection");
@@ -282,18 +361,22 @@
282
  const cancelBtn = document.getElementById("cancelProfileBtn");
283
  const inputName = document.getElementById("inputName");
284
  const inputDOB = document.getElementById("inputDOB");
285
-
 
286
  editBtn.addEventListener("click", () => {
287
  inputName.value = document.getElementById("userName").textContent;
288
  inputDOB.value = document.getElementById("userDOB").textContent;
289
  profileView.classList.add("hidden");
290
  profileEdit.classList.remove("hidden");
291
  });
292
-
 
293
  saveBtn.addEventListener("click", async () => {
294
  const nameVal = inputName.value.trim();
295
  const dobVal = inputDOB.value;
 
296
  const success = await window.saveProfile(nameVal, dobVal);
 
297
  if (success) {
298
  document.getElementById("userName").textContent =
299
  nameVal || "Name not set";
@@ -305,133 +388,82 @@
305
  alert("Error saving profile. Please try again.");
306
  }
307
  });
308
-
 
309
  cancelBtn.addEventListener("click", () => {
310
  profileEdit.classList.add("hidden");
311
  profileView.classList.remove("hidden");
312
  });
313
-
314
- // RANGE BARS LOGIC
315
- const tabs = document.querySelectorAll(".tab");
316
- const contents = document.querySelectorAll(".tab-content");
317
- const measurements = [
318
- {
319
- name: "Blood Sugar",
320
- min: 70,
321
- max: 110,
322
- value: 95,
323
- tab: "in-range",
324
- impacts: ["Pancreas"],
325
- },
326
- {
327
- name: "Cholesterol",
328
- min: 120,
329
- max: 200,
330
- value: 180,
331
- tab: "in-range",
332
- impacts: ["Heart", "Arteries"],
333
- },
334
- {
335
- name: "Triglycerides",
336
- min: 50,
337
- max: 150,
338
- value: 180,
339
- tab: "out-range",
340
- impacts: ["Heart"],
341
  },
342
- {
343
- name: "Blood Pressure",
344
- min: 90,
345
- max: 120,
346
- value: 130,
347
- tab: "out-range",
348
- impacts: ["Heart", "Kidneys"],
349
  },
350
- ];
351
-
352
- const counts = { "in-range": 0, "out-range": 0 };
353
-
354
- measurements.forEach((m) => {
355
- counts[m.tab]++;
356
- const container = document.getElementById(m.tab);
357
- const card = document.createElement("div");
358
- card.className = "range-card";
359
- const title = document.createElement("h3");
360
- title.textContent = m.name;
361
- card.appendChild(title);
362
- if (m.impacts) {
363
- const impactLabel = document.createElement("div");
364
- impactLabel.className = "impact-label";
365
- impactLabel.textContent = "Impacts: " + m.impacts.join(", ");
366
- card.appendChild(impactLabel);
367
- }
368
- const barContainer = document.createElement("div");
369
- barContainer.className = "range-bar-container";
370
- const bar = document.createElement("div");
371
- bar.className = "range-bar";
372
-
373
- let normalPercent = 100;
374
- let overflowPercent = 0;
375
- if (m.tab === "out-range" && m.value > m.max) {
376
- normalPercent = ((m.max - m.min) / (m.value - m.min)) * 100;
377
- overflowPercent = 100 - normalPercent;
378
  }
379
-
380
- const normalDiv = document.createElement("div");
381
- normalDiv.className = "normal-range";
382
- normalDiv.style.width = normalPercent + "%";
383
- bar.appendChild(normalDiv);
384
- if (overflowPercent > 0) {
385
- const overflowDiv = document.createElement("div");
386
- overflowDiv.className = "overflow-range";
387
- overflowDiv.style.left = normalPercent + "%";
388
- overflowDiv.style.width = overflowPercent + "%";
389
- bar.appendChild(overflowDiv);
 
 
 
390
  }
391
-
392
- let valuePercent = ((m.value - m.min) / (m.max - m.min)) * 100;
393
- valuePercent = Math.min(Math.max(valuePercent, 0), 100);
394
- const marker = document.createElement("div");
395
- marker.className = "marker";
396
- marker.style.left = valuePercent + "%";
397
- bar.appendChild(marker);
398
- const valueLabel = document.createElement("div");
399
- valueLabel.className = "value-label";
400
- valueLabel.style.left = valuePercent + "%";
401
- valueLabel.textContent = m.value;
402
- bar.appendChild(valueLabel);
403
-
404
- barContainer.appendChild(bar);
405
-
406
- const minMaxDiv = document.createElement("div");
407
- minMaxDiv.className = "min-max-labels";
408
- const minLabel = document.createElement("span");
409
- minLabel.textContent = "Min: " + m.min;
410
- const maxLabel = document.createElement("span");
411
- maxLabel.textContent = "Max: " + m.max;
412
- minMaxDiv.appendChild(minLabel);
413
- minMaxDiv.appendChild(maxLabel);
414
- barContainer.appendChild(minMaxDiv);
415
-
416
- card.appendChild(barContainer);
417
- container.appendChild(card);
418
  });
419
-
420
- tabs.forEach((tab) => {
421
- const t = tab.dataset.tab;
422
- const badge = tab.querySelector(".count-badge");
423
- badge.textContent = counts[t];
424
- tab.addEventListener("click", () => {
425
- tabs.forEach((t) => t.classList.remove("active"));
426
- tab.classList.add("active");
427
- contents.forEach((c) =>
428
- c.id === t
429
- ? c.classList.add("active")
430
- : c.classList.remove("active")
431
- );
432
- });
433
  });
434
  });
 
435
  </script>
 
436
  </body>
437
  </html>
 
5
  <meta name="viewport" content="width=device-width,initial-scale=1" />
6
  <title>Profile - CTRL + ALT + HEAL</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
9
  <link rel="stylesheet" href="style.css" />
10
  </head>
11
  <body class="bg-[#F7F8F9] min-h-screen">
12
+ <!-- NAVBAR -->
13
  <nav
14
  class="fixed top-0 left-0 w-full z-50 backdrop-blur-md bg-white/20 border-b border-white/30 shadow-md"
15
  >
16
  <div class="flex justify-between items-center w-full px-6 py-4">
17
+ <!-- Logo -->
18
  <a
19
  href="index.html"
20
  class="text-2xl font-bold text-black hover:text-[var(--tropical-indigo)] transition"
21
  >
22
  CTRL + ALT + HEAL
23
  </a>
24
+
25
  <ul class="hidden md:flex space-x-6 font-medium text-gray-800">
26
+ <li><a href="index.html" class="nav-link" style="display:none;">Home</a></li>
 
 
27
  <li><a href="profile.html" class="nav-link">Profile</a></li>
28
  <li><a href="analyzer.html" class="nav-link">Analyzer</a></li>
29
  <li><a href="past_data.html" class="nav-link">Past Reports</a></li>
30
+ <li id="authNavItem"><a href="login.html" class="nav-link">Login</a></li>
 
 
31
  </ul>
32
+
33
+ <!-- Hamburger Menu -->
34
  <button
35
  id="hamburger"
36
  class="md:hidden text-[var(--latte-cream)] text-2xl"
 
38
 
39
  </button>
40
  </div>
41
+ <!-- Changes added for change marker for style change-->
42
+ <!-- Mobile Menu -->
43
  <ul
44
  id="mobile-menu"
45
  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"
 
79
  >About</a
80
  >
81
  </li>
82
+
83
+ >
84
+ </li>
85
  </ul>
86
  </nav>
87
 
88
  <script>
89
  const hamburger = document.getElementById("hamburger");
90
  const mobileMenu = document.getElementById("mobile-menu");
91
+ hamburger.addEventListener("click", () => {
92
+ mobileMenu.classList.toggle("hidden");
93
+ });
94
+ // Underline the active page only
95
  const currentPath = window.location.pathname.split("/").pop();
96
  document.querySelectorAll(".nav-link").forEach((link) => {
97
+ if (link.getAttribute("href") === currentPath) {
98
+ link.classList.add("active-page"); // we'll style this in CSS
99
+ }
100
  });
101
  document.querySelectorAll("#mobile-menu a").forEach((link) => {
102
+ if (link.getAttribute("href") === currentPath) {
103
  link.classList.add("active-page");
104
+ }
105
  });
106
  </script>
107
 
108
  <div class="container mx-auto px-6 pt-24">
109
  <div class="bg-white border border-gray-200 rounded-lg p-6 shadow mb-6">
110
+ <!-- VIEW MODE -->
111
+ <div id="profileViewSection">
112
+ <div class="flex items-start space-x-6 mb-10">
113
+ <!-- Avatar & Edit Button -->
114
+ <div class="flex flex-col items-center">
115
+ <div class="w-32 h-32 bg-gray-300 rounded-full"></div>
116
+ <button id="editProfileBtn" class="mt-2 px-4 py-2 rounded">
117
+ Edit Profile
118
+ </button>
119
+ <a href="analyzer.html"
120
+ ><button id="analyzerBtn" class="mt-2 px-4 py-2 rounded">
121
+ Go To Analyzer
122
+ </button></a
123
+ >
124
+ </div>
125
+
126
+ <!-- User Info -->
127
+ <div>
128
+ <h2 class="text-xl font-semibold mb-4">Your Profile</h2>
129
+ <p class="mb-2">
130
+ Name: <span id="userName" class="font-medium">Loading...</span>
131
+ </p>
132
+ <p class="mb-2">
133
+ DOB: <span id="userDOB" class="font-medium">Loading...</span>
134
+ </p>
135
  </div>
136
  </div>
137
+ </div>
138
 
139
+ <!-- EDIT MODE -->
140
+ <div id="profileEditSection" class="hidden mb-10">
141
+ <div class="flex flex-col sm:flex-row items-start space-x-6">
142
+ <div class="flex flex-col items-center mb-4 sm:mb-0">
143
+ <div class="w-32 h-32 bg-gray-300 rounded-full"></div>
144
+ </div>
145
+ <div class="flex-1">
146
+ <h1 class="text-3xl font-semibold mb-4">Edit Profile</h1>
147
+ <div class="space-y-4">
148
+ <div>
149
+ <label for="inputName" class="block text-sm font-medium"
150
+ >Name</label
151
+ >
152
+ <input
153
+ type="text"
154
+ id="inputName"
155
+ class="w-full border rounded px-3 py-2 focus:ring-2 focus:ring-[#6B9080] focus:outline-none"
156
+ />
 
 
 
 
 
 
 
 
 
 
 
157
  </div>
158
+ <div>
159
+ <label for="inputDOB" class="block text-sm font-medium"
160
+ >Date of Birth</label
161
+ >
162
+ <input
163
+ type="date"
164
+ id="inputDOB"
165
+ class="w-full border rounded px-3 py-2 focus:ring-2 focus:ring-[#6B9080] focus:outline-none"
166
+ />
167
  </div>
168
  </div>
169
+ <div class="flex space-x-2 mt-4">
170
+ <button id="saveProfileBtn" class="px-4 py-2 rounded">
171
+ Save
172
+ </button>
173
+ <button id="cancelProfileBtn" class="px-4 py-2 rounded">
174
+ Cancel
175
+ </button>
176
+ </div>
177
  </div>
178
  </div>
179
+ </div>
180
+ </div>
181
 
182
+ <!-- Chart Section -->
183
+ <div class="mt-10" id="chartSection">
184
+ <div class="bg-white border border-gray-200 rounded-lg p-6 mb-6">
185
+ <h2 class="text-xl font-semibold mb-4">Health Progress</h2>
186
+ <div
187
+ class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6"
188
+ >
189
+ <div class="flex flex-1 gap-2">
190
+ <input
191
+ type="date"
192
+ id="entryDate"
193
+ class="flex-1 border rounded px-3 py-2 focus:ring-2 focus:ring-[#6B9080] focus:outline-none"
194
+ />
195
+ <input
196
+ type="number"
197
+ id="entryScore"
198
+ min="0"
199
+ max="100"
200
+ placeholder="Score"
201
+ class="w-24 border rounded px-3 py-2 focus:ring-2 focus:ring-[#6B9080] focus:outline-none"
202
+ />
203
+ </div>
204
+ <div class="flex gap-2">
205
+ <button id="addEntryBtn" class="px-4 py-2 rounded">
206
+ Add Entry
207
+ </button>
208
+ <button id="resetChartBtn" class="px-4 py-2 rounded">
209
+ Reset Chart
210
+ </button>
211
+ </div>
212
  </div>
213
+ <div class="overflow-x-auto">
214
+ <canvas id="progressChart" class="w-full h-64"></canvas>
215
  </div>
216
  </div>
 
 
217
  </div>
218
  </div>
219
 
 
229
  doc,
230
  getDoc,
231
  updateDoc,
232
+ collection,
233
+ addDoc,
234
+ serverTimestamp,
235
  } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";
236
+
237
+ // 🔥 Firebase config
238
  const firebaseConfig = {
239
+ apiKey: "AIzaSyAPhM_Ee7cLzyKHs5zyFy8g5ZOk9-pubRI",
240
+ authDomain: "login-tutorial-7a9e1.firebaseapp.com",
241
+ projectId: "login-tutorial-7a9e1",
242
+ storageBucket: "login-tutorial-7a9e1.firebasestorage.app",
243
+ messagingSenderId: "491093197824",
244
+ appId: "1:491093197824:web:9f866...",
 
245
  };
246
+
247
+ // 🔧 Init
248
  const app = initializeApp(firebaseConfig);
249
  const auth = getAuth(app);
250
  const db = getFirestore(app);
251
+
252
+ // Expose Firestore helpers if you need them elsewhere
253
+ window.firebaseAuth = auth;
254
+ window.onAuthStateChanged = onAuthStateChanged;
255
+ window.firestoreDb = db;
256
+ window.firestoreHelpers = { collection, doc, addDoc, serverTimestamp };
257
+
258
  let currentUser = null;
259
+
260
+ // Auth state check
261
+ // ✅ Hide Home button when logged in
262
+ const homeNavDesktop = document.querySelector('ul.md\\:flex li a[href="index.html"]')
263
+ ?.parentElement; // Desktop <li>
264
+ const homeNavMobile = document.querySelector('#mobile-menu a[href="index.html"]')
265
+ ?.parentElement; // Mobile <li>
266
 
267
+ onAuthStateChanged(auth, async (user) => {
268
+ const authNavItem = document.getElementById("authNavItem");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
269
 
270
+ if (user) {
271
+ currentUser = user;
272
+ await loadUserProfile();
273
+
274
+ // Swap login with logout
275
+ if (authNavItem) {
276
+ authNavItem.innerHTML =
277
+ '<button onclick="logout()" class="hover:text-[#6B9080] text-red-600">Logout</button>';
278
+ }
279
+
280
+ // Hide Home button
281
+ if (homeNavDesktop) homeNavDesktop.style.display = "none";
282
+ if (homeNavMobile) homeNavMobile.style.display = "none";
283
+
284
+ } else {
285
+ if (authNavItem) {
286
+ authNavItem.innerHTML =
287
+ '<a href="login.html" class="hover:text-[#6B9080]">Login</a>';
288
+ }
289
+
290
+ // Show Home button again
291
+ if (homeNavDesktop) homeNavDesktop.style.display = "";
292
+ if (homeNavMobile) homeNavMobile.style.display = "";
293
+
294
+ window.location.href = "login.html";
295
+ }
296
+ });
297
+
298
+
299
+ // 📄 Load user profile from Firestore
300
  async function loadUserProfile() {
301
  try {
302
  const userDoc = await getDoc(doc(db, "users", currentUser.uid));
 
309
  localStorage.setItem("userName", userData.name || "");
310
  localStorage.setItem("userDOB", userData.dob || "");
311
  }
312
+ } catch (error) {
313
+ console.error("Error loading user profile:", error);
314
  }
315
  }
316
+
317
+ // 💾 Save profile to Firestore
318
  window.saveProfile = async (name, dob) => {
319
  try {
320
+ await updateDoc(doc(db, "users", currentUser.uid), {
321
+ name: name,
322
+ dob: dob,
323
+ });
324
  localStorage.setItem("userName", name);
325
  localStorage.setItem("userDOB", dob);
326
  return true;
327
+ } catch (error) {
328
+ console.error("Error updating profile:", error);
329
  return false;
330
  }
331
  };
332
+
333
+ // 🚪 Logout
334
  window.logout = async () => {
335
  try {
336
  await signOut(auth);
337
  localStorage.clear();
338
  window.location.href = "login.html";
339
+ } catch (error) {
340
+ console.error("Error signing out:", error);
341
  }
342
  };
343
+
344
+ // 📊 LocalStorage for progress data
345
+ function loadProgressData() {
346
+ return JSON.parse(localStorage.getItem("progressData") || "[]");
347
+ }
348
+ function saveProgressData(data) {
349
+ localStorage.setItem("progressData", JSON.stringify(data));
350
+ }
351
+ function resetProgressData() {
352
+ localStorage.removeItem("progressData");
353
+ }
354
+
355
+ // 🖼️ DOM Content Loaded logic
356
  document.addEventListener("DOMContentLoaded", () => {
357
  const profileView = document.getElementById("profileViewSection");
358
  const profileEdit = document.getElementById("profileEditSection");
 
361
  const cancelBtn = document.getElementById("cancelProfileBtn");
362
  const inputName = document.getElementById("inputName");
363
  const inputDOB = document.getElementById("inputDOB");
364
+
365
+ // Toggle to edit mode
366
  editBtn.addEventListener("click", () => {
367
  inputName.value = document.getElementById("userName").textContent;
368
  inputDOB.value = document.getElementById("userDOB").textContent;
369
  profileView.classList.add("hidden");
370
  profileEdit.classList.remove("hidden");
371
  });
372
+
373
+ // Save profile
374
  saveBtn.addEventListener("click", async () => {
375
  const nameVal = inputName.value.trim();
376
  const dobVal = inputDOB.value;
377
+
378
  const success = await window.saveProfile(nameVal, dobVal);
379
+
380
  if (success) {
381
  document.getElementById("userName").textContent =
382
  nameVal || "Name not set";
 
388
  alert("Error saving profile. Please try again.");
389
  }
390
  });
391
+
392
+ // Cancel editing
393
  cancelBtn.addEventListener("click", () => {
394
  profileEdit.classList.add("hidden");
395
  profileView.classList.remove("hidden");
396
  });
397
+
398
+ // Chart setup
399
+ const chartSection = document.getElementById("chartSection");
400
+ const welcomeMessage = document.getElementById("welcomeMessage");
401
+ const isNewUser = localStorage.getItem("isNewUser") === "true";
402
+
403
+ if (isNewUser) {
404
+ welcomeMessage.classList.remove("hidden");
405
+ chartSection.classList.add("hidden");
406
+ }
407
+
408
+ const stored = loadProgressData();
409
+ const labels = stored.map((e) => e.date);
410
+ const ctx = document.getElementById("progressChart").getContext("2d");
411
+ const progressChart = new Chart(ctx, {
412
+ type: "line",
413
+ data: {
414
+ labels,
415
+ datasets: [
416
+ {
417
+ label: "Health Score",
418
+ data: stored.map((e) => e.score),
419
+ borderColor: "#6B9080",
420
+ fill: true,
421
+ tension: 0.4,
422
+ },
423
+ ],
 
424
  },
425
+ options: {
426
+ scales: { y: { beginAtZero: true, max: 100 } },
 
 
 
 
 
427
  },
428
+ });
429
+
430
+ // Add Entry
431
+ document.getElementById("addEntryBtn").addEventListener("click", () => {
432
+ const date = document.getElementById("entryDate").value;
433
+ const score = parseInt(document.getElementById("entryScore").value, 10);
434
+
435
+ if (!date || isNaN(score)) {
436
+ alert("Please select a date and enter a valid score.");
437
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
438
  }
439
+
440
+ stored.push({ date, score });
441
+ saveProgressData(stored);
442
+ progressChart.data.labels.push(date);
443
+ progressChart.data.datasets[0].data.push(score);
444
+ progressChart.update();
445
+
446
+ document.getElementById("entryDate").value = "";
447
+ document.getElementById("entryScore").value = "";
448
+
449
+ if (isNewUser) {
450
+ localStorage.setItem("isNewUser", "false");
451
+ welcomeMessage.classList.add("hidden");
452
+ chartSection.classList.remove("hidden");
453
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
454
  });
455
+
456
+ // Reset Chart
457
+ document.getElementById("resetChartBtn").addEventListener("click", () => {
458
+ if (!confirm("Are you sure you want to clear all progress data?")) return;
459
+ resetProgressData();
460
+ progressChart.data.labels = [];
461
+ progressChart.data.datasets[0].data = [];
462
+ progressChart.update();
 
 
 
 
 
 
463
  });
464
  });
465
+
466
  </script>
467
+
468
  </body>
469
  </html>