Spaces:
Sleeping
Sleeping
<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> | |
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
<link rel="stylesheet" href="style.css" /> | |
</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"> | |
<!-- Logo --> | |
<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> | |
<!-- Hamburger Menu --> | |
<button | |
id="hamburger" | |
class="md:hidden text-[var(--latte-cream)] text-2xl" | |
> | |
☰ | |
</button> | |
</div> | |
<!-- Changes added for change marker for style change--> | |
<!-- Mobile Menu --> | |
<ul | |
id="mobile-menu" | |
class="hidden flex-col space-y-4 bg-white/30 backdrop-blur-lg border border-white/20 rounded-xl shadow-lg mt-2 p-4 mx-6 md:hidden" | |
> | |
<li> | |
<a | |
href="index.html" | |
class="block 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> | |
> | |
</li> | |
</ul> | |
</nav> | |
<script> | |
const hamburger = document.getElementById("hamburger"); | |
const mobileMenu = document.getElementById("mobile-menu"); | |
hamburger.addEventListener("click", () => { | |
mobileMenu.classList.toggle("hidden"); | |
}); | |
// Underline the active page only | |
const currentPath = window.location.pathname.split("/").pop(); | |
document.querySelectorAll(".nav-link").forEach((link) => { | |
if (link.getAttribute("href") === currentPath) { | |
link.classList.add("active-page"); // we'll style this in CSS | |
} | |
}); | |
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"> | |
<!-- VIEW MODE --> | |
<div id="profileViewSection"> | |
<div class="flex items-start space-x-6 mb-10"> | |
<!-- Avatar & Edit Button --> | |
<div class="flex flex-col items-center"> | |
<div class="w-32 h-32 bg-gray-300 rounded-full"></div> | |
<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> | |
<!-- User Info --> | |
<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> | |
<!-- EDIT MODE --> | |
<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"> | |
<div class="w-32 h-32 bg-gray-300 rounded-full"></div> | |
</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> | |
</div> | |
<!-- Chart Section --> | |
<div class="mt-10" id="chartSection"> | |
<div class="bg-white border border-gray-200 rounded-lg p-6 mb-6"> | |
<h2 class="text-xl font-semibold mb-4">Health Progress</h2> | |
<div | |
class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6" | |
> | |
<div class="flex flex-1 gap-2"> | |
<input | |
type="date" | |
id="entryDate" | |
class="flex-1 border rounded px-3 py-2 focus:ring-2 focus:ring-[#6B9080] focus:outline-none" | |
/> | |
<input | |
type="number" | |
id="entryScore" | |
min="0" | |
max="100" | |
placeholder="Score" | |
class="w-24 border rounded px-3 py-2 focus:ring-2 focus:ring-[#6B9080] focus:outline-none" | |
/> | |
</div> | |
<div class="flex gap-2"> | |
<button id="addEntryBtn" class="px-4 py-2 rounded"> | |
Add Entry | |
</button> | |
<button id="resetChartBtn" class="px-4 py-2 rounded"> | |
Reset Chart | |
</button> | |
</div> | |
</div> | |
<div class="overflow-x-auto"> | |
<canvas id="progressChart" class="w-full h-64"></canvas> | |
</div> | |
</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, | |
collection, | |
addDoc, | |
serverTimestamp, | |
} from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js"; | |
const firebaseConfig = { | |
apiKey: "AIzaSyAKWstCc7HqMgV8DmH2Sy6QwUP3JvZxu9g", | |
authDomain: "appchallenge-c8fe3.firebaseapp.com", | |
projectId: "appchallenge-c8fe3", | |
storageBucket: "appchallenge-c8fe3.firebasestorage.app", | |
messagingSenderId: "943791692455", | |
appId: "1:943791692455:web:ae04d06bf09669b6bdd982", | |
measurementId: "G-HJ7EHW8V4N" | |
}; | |
// 🔧 Init | |
const app = initializeApp(firebaseConfig); | |
const auth = getAuth(app); | |
const db = getFirestore(app); | |
// Expose Firestore helpers if you need them elsewhere | |
window.firebaseAuth = auth; | |
window.onAuthStateChanged = onAuthStateChanged; | |
window.firestoreDb = db; | |
window.firestoreHelpers = { collection, doc, addDoc, serverTimestamp }; | |
let currentUser = null; | |
// ✅ Auth state check | |
// ✅ Hide Home button when logged in | |
const homeNavDesktop = document.querySelector('ul.md\\:flex li a[href="index.html"]') | |
?.parentElement; // Desktop <li> | |
const homeNavMobile = document.querySelector('#mobile-menu a[href="index.html"]') | |
?.parentElement; // Mobile <li> | |
onAuthStateChanged(auth, async (user) => { | |
const authNavItem = document.getElementById("authNavItem"); | |
if (user) { | |
currentUser = user; | |
await loadUserProfile(); | |
// Swap login with logout | |
if (authNavItem) { | |
authNavItem.innerHTML = | |
'<button onclick="logout()" class="hover:text-[#6B9080] text-red-600">Logout</button>'; | |
} | |
// Hide Home button | |
if (homeNavDesktop) homeNavDesktop.style.display = "none"; | |
if (homeNavMobile) homeNavMobile.style.display = "none"; | |
} else { | |
if (authNavItem) { | |
authNavItem.innerHTML = | |
'<a href="login.html" class="hover:text-[#6B9080]">Login</a>'; | |
} | |
// Show Home button again | |
if (homeNavDesktop) homeNavDesktop.style.display = ""; | |
if (homeNavMobile) homeNavMobile.style.display = ""; | |
window.location.href = "login.html"; | |
} | |
}); | |
// 📄 Load user profile from Firestore | |
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 (error) { | |
console.error("Error loading user profile:", error); | |
} | |
} | |
// 💾 Save profile to Firestore | |
window.saveProfile = async (name, dob) => { | |
try { | |
await updateDoc(doc(db, "users", currentUser.uid), { | |
name: name, | |
dob: dob, | |
}); | |
localStorage.setItem("userName", name); | |
localStorage.setItem("userDOB", dob); | |
return true; | |
} catch (error) { | |
console.error("Error updating profile:", error); | |
return false; | |
} | |
}; | |
// 🚪 Logout | |
window.logout = async () => { | |
try { | |
await signOut(auth); | |
localStorage.clear(); | |
window.location.href = "login.html"; | |
} catch (error) { | |
console.error("Error signing out:", error); | |
} | |
}; | |
// 📊 LocalStorage for progress data | |
function loadProgressData() { | |
return JSON.parse(localStorage.getItem("progressData") || "[]"); | |
} | |
function saveProgressData(data) { | |
localStorage.setItem("progressData", JSON.stringify(data)); | |
} | |
function resetProgressData() { | |
localStorage.removeItem("progressData"); | |
} | |
// 🖼️ DOM Content Loaded logic | |
document.addEventListener("DOMContentLoaded", () => { | |
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"); | |
// Toggle to edit mode | |
editBtn.addEventListener("click", () => { | |
inputName.value = document.getElementById("userName").textContent; | |
inputDOB.value = document.getElementById("userDOB").textContent; | |
profileView.classList.add("hidden"); | |
profileEdit.classList.remove("hidden"); | |
}); | |
// Save profile | |
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."); | |
} | |
}); | |
// Cancel editing | |
cancelBtn.addEventListener("click", () => { | |
profileEdit.classList.add("hidden"); | |
profileView.classList.remove("hidden"); | |
}); | |
// Chart setup | |
const chartSection = document.getElementById("chartSection"); | |
const welcomeMessage = document.getElementById("welcomeMessage"); | |
const isNewUser = localStorage.getItem("isNewUser") === "true"; | |
if (isNewUser) { | |
welcomeMessage.classList.remove("hidden"); | |
chartSection.classList.add("hidden"); | |
} | |
const stored = loadProgressData(); | |
const labels = stored.map((e) => e.date); | |
const ctx = document.getElementById("progressChart").getContext("2d"); | |
const progressChart = new Chart(ctx, { | |
type: "line", | |
data: { | |
labels, | |
datasets: [ | |
{ | |
label: "Health Score", | |
data: stored.map((e) => e.score), | |
borderColor: "#6B9080", | |
fill: true, | |
tension: 0.4, | |
}, | |
], | |
}, | |
options: { | |
scales: { y: { beginAtZero: true, max: 100 } }, | |
}, | |
}); | |
// Add Entry | |
document.getElementById("addEntryBtn").addEventListener("click", () => { | |
const date = document.getElementById("entryDate").value; | |
const score = parseInt(document.getElementById("entryScore").value, 10); | |
if (!date || isNaN(score)) { | |
alert("Please select a date and enter a valid score."); | |
return; | |
} | |
stored.push({ date, score }); | |
saveProgressData(stored); | |
progressChart.data.labels.push(date); | |
progressChart.data.datasets[0].data.push(score); | |
progressChart.update(); | |
document.getElementById("entryDate").value = ""; | |
document.getElementById("entryScore").value = ""; | |
if (isNewUser) { | |
localStorage.setItem("isNewUser", "false"); | |
welcomeMessage.classList.add("hidden"); | |
chartSection.classList.remove("hidden"); | |
} | |
}); | |
// Reset Chart | |
document.getElementById("resetChartBtn").addEventListener("click", () => { | |
if (!confirm("Are you sure you want to clear all progress data?")) return; | |
resetProgressData(); | |
progressChart.data.labels = []; | |
progressChart.data.datasets[0].data = []; | |
progressChart.update(); | |
}); | |
}); | |
</script> | |
</body> | |
</html> |