PRMSChallengeOct / web /profile.html
TheFrogGod's picture
Update web/profile.html
c1bf20e verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Profile - CTRL + ALT + HEAL</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body class="bg-[#F7F8F9] min-h-screen">
<!-- NAVBAR -->
<nav
class="fixed top-0 left-0 w-full z-50 backdrop-blur-md bg-white/20 border-b border-white/30 shadow-md"
>
<div class="flex justify-between items-center w-full px-6 py-4">
<a
href="index.html"
class="text-2xl font-bold text-black hover:text-[var(--tropical-indigo)] transition"
>
CTRL + ALT + HEAL
</a>
<ul class="hidden md:flex space-x-6 font-medium text-gray-800">
<li>
<a href="index.html" class="nav-link" style="display: none">Home</a>
</li>
<li><a href="profile.html" class="nav-link">Profile</a></li>
<li><a href="analyzer.html" class="nav-link">Analyzer</a></li>
<li><a href="past_data.html" class="nav-link">Past Reports</a></li>
<li id="authNavItem">
<a href="login.html" class="nav-link">Login</a>
</li>
</ul>
<button
id="hamburger"
class="md:hidden text-[var(--latte-cream)] text-2xl"
>
</button>
</div>
<ul
id="mobile-menu"
class="hidden flex-col space-y-4 bg-white/30 backdrop-blur-lg border border-white/20 rounded-xl shadow-lg mt-2 p-4 mx-6 md:hidden"
>
<li>
<a
href="index.html"
class="block text-gray-800 hover:text-[var(--tropical-indigo)]"
>Home</a
>
</li>
<li>
<a
href="analyzer.html"
class="block text-gray-800 hover:text-[var(--tropical-indigo)]"
>Analyzer</a
>
</li>
<li>
<a
href="profile.html"
class="block text-gray-800 hover:text-[var(--tropical-indigo)]"
>Profile</a
>
</li>
<li>
<a
href="login.html"
class="block text-gray-800 hover:text-[var(--tropical-indigo)]"
>Login</a
>
</li>
<li>
<a
href="about.html"
class="block text-gray-800 hover:text-[var(--tropical-indigo)]"
>About</a
>
</li>
</ul>
</nav>
<script>
const hamburger = document.getElementById("hamburger");
const mobileMenu = document.getElementById("mobile-menu");
hamburger.addEventListener("click", () =>
mobileMenu.classList.toggle("hidden")
);
const currentPath = window.location.pathname.split("/").pop();
document.querySelectorAll(".nav-link").forEach((link) => {
if (link.getAttribute("href") === currentPath)
link.classList.add("active-page");
});
document.querySelectorAll("#mobile-menu a").forEach((link) => {
if (link.getAttribute("href") === currentPath)
link.classList.add("active-page");
});
</script>
<div class="container mx-auto px-6 pt-24">
<div class="bg-white border border-gray-200 rounded-lg p-6 shadow mb-6">
<!-- PROFILE VIEW -->
<div id="profileViewSection">
<div class="flex items-start space-x-6 mb-10">
<div class="flex flex-col items-center">
<img src="https://cdn.mos.cms.futurecdn.net/ARWms77gyVZ5u2MZkQqcKc-1256-80.jpg.webp"
alt="Profile Tree"
class="w-32 h-32 object-cover rounded-full border border-gray-300">
<button id="editProfileBtn" class="mt-2 px-4 py-2 rounded">
Edit Profile
</button>
<a href="analyzer.html">
<button id="analyzerBtn" class="mt-2 px-4 py-2 rounded">
Go To Analyzer
</button>
</a>
</div>
<div>
<h2 class="text-xl font-semibold mb-4">Your Profile</h2>
<p class="mb-2">
Name: <span id="userName" class="font-medium">Loading...</span>
</p>
<p class="mb-2">
DOB: <span id="userDOB" class="font-medium">Loading...</span>
</p>
</div>
</div>
</div>
<!-- PROFILE EDIT -->
<div id="profileEditSection" class="hidden mb-10">
<div class="flex flex-col sm:flex-row items-start space-x-6">
<div class="flex flex-col items-center mb-4 sm:mb-0">
<img src="https://cdn.mos.cms.futurecdn.net/ARWms77gyVZ5u2MZkQqcKc-1256-80.jpg.webp"
alt="Profile Tree"
class="w-32 h-32 object-cover rounded-full border border-gray-300">
</div>
<div class="flex-1">
<h1 class="text-3xl font-semibold mb-4">Edit Profile</h1>
<div class="space-y-4">
<div>
<label for="inputName" class="block text-sm font-medium"
>Name</label
>
<input
type="text"
id="inputName"
class="w-full border rounded px-3 py-2 focus:ring-2 focus:ring-[#6B9080] focus:outline-none"
/>
</div>
<div>
<label for="inputDOB" class="block text-sm font-medium"
>Date of Birth</label
>
<input
type="date"
id="inputDOB"
class="w-full border rounded px-3 py-2 focus:ring-2 focus:ring-[#6B9080] focus:outline-none"
/>
</div>
</div>
<div class="flex space-x-2 mt-4">
<button id="saveProfileBtn" class="px-4 py-2 rounded">
Save
</button>
<button id="cancelProfileBtn" class="px-4 py-2 rounded">
Cancel
</button>
</div>
</div>
</div>
</div>
<!-- RANGE BARS -->
<h2 class="text-xl font-semibold mb-4">Your Current Health Summary</h2>
<div class="tabs">
<div class="tab active" data-tab="out-range">
Out of Range <span class="count-badge">0</span>
</div>
<div class="tab" data-tab="in-range">
In Range <span class="count-badge">0</span>
</div>
</div>
<div class="tab-content active" id="out-range"></div>
<div class="tab-content" id="in-range"></div>
</div>
</div>
<script type="module">
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-app.js";
import {
getAuth,
onAuthStateChanged,
signOut,
} from "https://www.gstatic.com/firebasejs/9.22.0/firebase-auth.js";
import {
getFirestore,
doc,
getDoc,
updateDoc,
} from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";
const firebaseConfig = {
apiKey: "AIzaSyAPhM_Ee7cLzyKHs5zyFy8g5ZOk9-pubRI",
authDomain: "login-tutorial-7a9e1.firebaseapp.com",
projectId: "login-tutorial-7a9e1",
storageBucket: "login-tutorial-7a9e1.firebasestorage.app",
messagingSenderId: "491093197824",
appId: "1:491093197824:web:9f86659034af7e6a8244e5",
measurementId: "G-JM7T9N6ZLZ"
};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);
let currentUser = null;
const homeNavDesktop = document.querySelector(
'ul.md\\:flex li a[href="index.html"]'
)?.parentElement;
const homeNavMobile = document.querySelector(
'#mobile-menu a[href="index.html"]'
)?.parentElement;
// Fetch measurements from backend API
async function getUserMeasurements(userId) {
try {
const url = api("user_measurements/", { user_id: userId });
const res = await fetch(url);
if (!res.ok) throw new Error("Failed to fetch measurements");
const data = await res.json();
return (data.measurements || []).map((m) => ({
name: m.measurement_type,
value: Number(m.value),
min: Number(m.min),
max: Number(m.max),
tab: m.status === "HIGH" || m.status === "LOW" ? "out-range" : "in-range",
impacts: m.impacts || [],
}));
} catch (e) {
console.error("Error fetching user measurements:", e);
return [];
}
}
function getLatestByType(measurements) {
const latest = {};
measurements.forEach(m => {
if (!latest[m.name] || new Date(m.timestamp) > new Date(latest[m.name].timestamp)) {
latest[m.name] = m;
}
});
return Object.values(latest);
}
// Render bars in the UI based on measurement array
function renderBars(measurements) {
console.log("measurements",measurements);
const counts = { "in-range": 0, "out-range": 0 };
const tabs = document.querySelectorAll(".tab");
const contents = document.querySelectorAll(".tab-content");
// Clear existing content
contents.forEach((c) => (c.innerHTML = ""));
measurements.forEach((m) => {
counts[m.tab]++;
const container = document.getElementById(m.tab);
const card = document.createElement("div");
card.className = "range-card";
const title = document.createElement("h3");
title.className = "measurement-title";
title.textContent = m.name;
card.appendChild(title);
if (m.impacts?.length) {
const impactLabel = document.createElement("div");
impactLabel.className = "impact-label";
impactLabel.textContent = "Impacts: " + m.impacts.join(", ");
card.appendChild(impactLabel);
}
const barContainer = document.createElement("div");
barContainer.className = "range-bar-container";
const bar = document.createElement("div");
bar.className = "range-bar";
let normalPercent = 100;
let overflowPercent = 0;
if (m.tab === "out-range" && m.value > m.max) {
normalPercent = ((m.max - m.min) / (m.value - m.min)) * 100;
overflowPercent = 100 - normalPercent;
}
const normalDiv = document.createElement("div");
normalDiv.className = "normal-range";
normalDiv.style.width = normalPercent + "%";
bar.appendChild(normalDiv);
if (overflowPercent > 0) {
const overflowDiv = document.createElement("div");
overflowDiv.className = "overflow-range";
overflowDiv.style.left = normalPercent + "%";
overflowDiv.style.width = overflowPercent + "%";
bar.appendChild(overflowDiv);
}
let valuePercent = ((m.value - m.min) / (m.max - m.min)) * 100;
valuePercent = Math.min(Math.max(valuePercent, 0), 100);
const marker = document.createElement("div");
marker.className = "marker";
marker.style.left = valuePercent + "%";
bar.appendChild(marker);
const valueLabel = document.createElement("div");
valueLabel.className = "value-label";
valueLabel.style.left = valuePercent + "%";
valueLabel.textContent = m.value;
bar.appendChild(valueLabel);
barContainer.appendChild(bar);
const minMaxDiv = document.createElement("div");
minMaxDiv.className = "min-max-labels relative w-full";
const minLabel = document.createElement("span");
minLabel.textContent = "Min: " + m.min;
minLabel.style.position = "absolute";
minLabel.style.left = "0";
const maxLabel = document.createElement("span");
maxLabel.textContent = "Max: " + m.max;
maxLabel.style.position = "absolute";
let maxPercent = 100;
if (m.value > m.max) maxPercent = ((m.max - m.min) / (m.value - m.min)) * 100;
maxLabel.style.left = maxPercent + "%";
maxLabel.style.transform = "translateX(-100%)";
minMaxDiv.appendChild(minLabel);
minMaxDiv.appendChild(maxLabel);
barContainer.appendChild(minMaxDiv);
card.appendChild(barContainer);
container.appendChild(card);
});
// Update tab badges
tabs.forEach((tab) => {
const t = tab.dataset.tab;
const badge = tab.querySelector(".count-badge");
badge.textContent = counts[t];
tab.addEventListener("click", () => {
tabs.forEach((t) => t.classList.remove("active"));
tab.classList.add("active");
contents.forEach((c) =>
c.id === t ? c.classList.add("active") : c.classList.remove("active")
);
});
});
}
// ✅ Auth State Handling
onAuthStateChanged(auth, async (user) => {
const authNavItem = document.getElementById("authNavItem");
if (user) {
currentUser = user;
await loadUserProfile();
// ✅ fetch and render bars after user is ready
console.log("currentuser data",currentUser);
// const measurements = await getUserMeasurements(currentUser.uid);
let measurements = await getUserMeasurements(currentUser.email);
measurements = getLatestByType(measurements);
renderBars(measurements);
authNavItem.innerHTML = '<button onclick="logout()" style="color:red">Logout</button>';
if (homeNavDesktop) homeNavDesktop.style.display = "none";
if (homeNavMobile) homeNavMobile.style.display = "none";
} else {
authNavItem.innerHTML = '<a href="login.html">Login</a>';
if (homeNavDesktop) homeNavDesktop.style.display = "";
if (homeNavMobile) homeNavMobile.style.display = "";
window.location.href = "login.html";
}
});
async function loadUserProfile() {
try {
const userDoc = await getDoc(doc(db, "users", currentUser.uid));
if (userDoc.exists()) {
const userData = userDoc.data();
document.getElementById("userName").textContent =
userData.name || "Name not set";
document.getElementById("userDOB").textContent =
userData.dob || "DOB not set";
localStorage.setItem("userName", userData.name || "");
localStorage.setItem("userDOB", userData.dob || "");
}
} catch (e) {
console.error("Error loading user profile:", e);
}
}
window.saveProfile = async (name, dob) => {
try {
await updateDoc(doc(db, "users", currentUser.uid), { name, dob });
localStorage.setItem("userName", name);
localStorage.setItem("userDOB", dob);
return true;
} catch (e) {
console.error("Error updating profile:", e);
return false;
}
};
window.logout = async () => {
try {
await signOut(auth);
localStorage.clear();
window.location.href = "login.html";
} catch (e) {
console.error("Error signing out:", e);
}
};
document.addEventListener("DOMContentLoaded", async () => {
const profileView = document.getElementById("profileViewSection");
const profileEdit = document.getElementById("profileEditSection");
const editBtn = document.getElementById("editProfileBtn");
const saveBtn = document.getElementById("saveProfileBtn");
const cancelBtn = document.getElementById("cancelProfileBtn");
const inputName = document.getElementById("inputName");
const inputDOB = document.getElementById("inputDOB");
editBtn.addEventListener("click", () => {
inputName.value = document.getElementById("userName").textContent;
inputDOB.value = document.getElementById("userDOB").textContent;
profileView.classList.add("hidden");
profileEdit.classList.remove("hidden");
});
saveBtn.addEventListener("click", async () => {
const nameVal = inputName.value.trim();
const dobVal = inputDOB.value;
const success = await window.saveProfile(nameVal, dobVal);
if (success) {
document.getElementById("userName").textContent =
nameVal || "Name not set";
document.getElementById("userDOB").textContent =
dobVal || "DOB not set";
profileEdit.classList.add("hidden");
profileView.classList.remove("hidden");
} else {
alert("Error saving profile. Please try again.");
}
});
cancelBtn.addEventListener("click", () => {
profileEdit.classList.add("hidden");
profileView.classList.remove("hidden");
});
// RANGE BARS LOGIC
const tabs = document.querySelectorAll(".tab");
const contents = document.querySelectorAll(".tab-content");
});
</script>
</body>
</html>