|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>MindBloom | Depression Cycle Tracker</title> |
|
|
<script src="https://cdn.tailwindcss.com"></script> |
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
|
|
<style> |
|
|
@keyframes fadeIn { |
|
|
from { opacity: 0; } |
|
|
to { opacity: 1; } |
|
|
} |
|
|
.fade-in { |
|
|
animation: fadeIn 0.5s ease-in-out; |
|
|
} |
|
|
.mood-1 { background-color: #f87171; } |
|
|
.mood-2 { background-color: #fb923c; } |
|
|
.mood-3 { background-color: #facc15; } |
|
|
.mood-4 { background-color: #a3e635; } |
|
|
.mood-5 { background-color: #4ade80; } |
|
|
.chart-bar { |
|
|
transition: height 0.5s ease-in-out; |
|
|
} |
|
|
.custom-scrollbar::-webkit-scrollbar { |
|
|
width: 6px; |
|
|
height: 6px; |
|
|
} |
|
|
.custom-scrollbar::-webkit-scrollbar-track { |
|
|
background: #f1f1f1; |
|
|
border-radius: 10px; |
|
|
} |
|
|
.custom-scrollbar::-webkit-scrollbar-thumb { |
|
|
background: #cbd5e1; |
|
|
border-radius: 10px; |
|
|
} |
|
|
.custom-scrollbar::-webkit-scrollbar-thumb:hover { |
|
|
background: #94a3b8; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body class="bg-gray-50 min-h-screen font-sans"> |
|
|
<div class="container mx-auto px-4 py-8 max-w-6xl"> |
|
|
|
|
|
<header class="flex justify-between items-center mb-8"> |
|
|
<div class="flex items-center"> |
|
|
<div class="w-10 h-10 rounded-full bg-indigo-600 flex items-center justify-center mr-3"> |
|
|
<i class="fas fa-brain text-white text-xl"></i> |
|
|
</div> |
|
|
<h1 class="text-2xl font-bold text-gray-800">MindBloom</h1> |
|
|
</div> |
|
|
<div class="flex items-center space-x-4"> |
|
|
<button id="theme-toggle" class="p-2 rounded-full hover:bg-gray-200 transition"> |
|
|
<i class="fas fa-moon text-gray-600"></i> |
|
|
</button> |
|
|
<div class="w-8 h-8 rounded-full bg-indigo-100 flex items-center justify-center"> |
|
|
<i class="fas fa-user text-indigo-600"></i> |
|
|
</div> |
|
|
</div> |
|
|
</header> |
|
|
|
|
|
|
|
|
<main class="grid grid-cols-1 lg:grid-cols-3 gap-6"> |
|
|
|
|
|
<div class="lg:col-span-2 space-y-6"> |
|
|
|
|
|
<div class="bg-white rounded-xl shadow-md p-6 fade-in"> |
|
|
<div class="flex justify-between items-center mb-4"> |
|
|
<h2 class="text-xl font-semibold text-gray-800">Today's Mood</h2> |
|
|
<span class="text-sm text-gray-500" id="current-date"></span> |
|
|
</div> |
|
|
|
|
|
<div class="mb-6"> |
|
|
<p class="text-gray-600 mb-4">How are you feeling today?</p> |
|
|
<div class="flex justify-between space-x-2"> |
|
|
<button class="mood-btn flex-1 py-3 rounded-lg flex flex-col items-center transition hover:opacity-90" data-value="1"> |
|
|
<i class="fas fa-face-sad-tear text-2xl mb-1"></i> |
|
|
<span class="text-xs">Very Low</span> |
|
|
</button> |
|
|
<button class="mood-btn flex-1 py-3 rounded-lg flex flex-col items-center transition hover:opacity-90" data-value="2"> |
|
|
<i class="fas fa-face-frown text-2xl mb-1"></i> |
|
|
<span class="text-xs">Low</span> |
|
|
</button> |
|
|
<button class="mood-btn flex-1 py-3 rounded-lg flex flex-col items-center transition hover:opacity-90" data-value="3"> |
|
|
<i class="fas fa-face-meh text-2xl mb-1"></i> |
|
|
<span class="text-xs">Neutral</span> |
|
|
</button> |
|
|
<button class="mood-btn flex-1 py-3 rounded-lg flex flex-col items-center transition hover:opacity-90" data-value="4"> |
|
|
<i class="fas fa-face-smile text-2xl mb-1"></i> |
|
|
<span class="text-xs">Good</span> |
|
|
</button> |
|
|
<button class="mood-btn flex-1 py-3 rounded-lg flex flex-col items-center transition hover:opacity-90" data-value="5"> |
|
|
<i class="fas fa-face-laugh-beam text-2xl mb-1"></i> |
|
|
<span class="text-xs">Very Good</span> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div id="mood-description" class="hidden"> |
|
|
<textarea id="mood-notes" class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500" rows="3" placeholder="Add any notes about how you're feeling..."></textarea> |
|
|
<div class="flex justify-end mt-3"> |
|
|
<button id="save-mood" class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition">Save</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="bg-white rounded-xl shadow-md p-6 fade-in"> |
|
|
<div class="flex justify-between items-center mb-4"> |
|
|
<h2 class="text-xl font-semibold text-gray-800">Mood History</h2> |
|
|
<div class="flex space-x-2"> |
|
|
<button class="time-btn px-3 py-1 text-sm rounded-md bg-indigo-100 text-indigo-700" data-range="7">Week</button> |
|
|
<button class="time-btn px-3 py-1 text-sm rounded-md hover:bg-indigo-100 hover:text-indigo-700" data-range="30">Month</button> |
|
|
<button class="time-btn px-3 py-1 text-sm rounded-md hover:bg-indigo-100 hover:text-indigo-700" data-range="90">3 Months</button> |
|
|
</div> |
|
|
</div> |
|
|
<div class="h-64"> |
|
|
<div id="mood-chart" class="h-full flex items-end justify-between pt-4"></div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="space-y-6"> |
|
|
|
|
|
<div class="bg-white rounded-xl shadow-md p-6 fade-in"> |
|
|
<h2 class="text-xl font-semibold text-gray-800 mb-4">Your Stats</h2> |
|
|
<div class="space-y-4"> |
|
|
<div> |
|
|
<p class="text-sm text-gray-500 mb-1">Current Mood Cycle</p> |
|
|
<div class="flex items-center"> |
|
|
<div class="w-3 h-3 rounded-full bg-indigo-600 mr-2"></div> |
|
|
<p class="font-medium" id="current-cycle">Neutral Phase</p> |
|
|
</div> |
|
|
</div> |
|
|
<div> |
|
|
<p class="text-sm text-gray-500 mb-1">Average Mood</p> |
|
|
<p class="font-medium text-2xl" id="avg-mood">3.4</p> |
|
|
</div> |
|
|
<div> |
|
|
<p class="text-sm text-gray-500 mb-1">Low Days This Month</p> |
|
|
<p class="font-medium" id="low-days">4</p> |
|
|
</div> |
|
|
<div> |
|
|
<p class="text-sm text-gray-500 mb-1">Current Streak</p> |
|
|
<p class="font-medium" id="current-streak">2 days</p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="bg-white rounded-xl shadow-md p-6 fade-in"> |
|
|
<div class="flex justify-between items-center mb-4"> |
|
|
<h2 class="text-xl font-semibold text-gray-800">Recent Journal</h2> |
|
|
<button class="text-indigo-600 hover:text-indigo-800"> |
|
|
<i class="fas fa-plus"></i> |
|
|
</button> |
|
|
</div> |
|
|
<div class="space-y-4 max-h-64 overflow-y-auto custom-scrollbar"> |
|
|
<div class="p-3 bg-indigo-50 rounded-lg"> |
|
|
<div class="flex justify-between items-start mb-2"> |
|
|
<p class="font-medium">Feeling better today</p> |
|
|
<span class="text-xs text-gray-500">Yesterday</span> |
|
|
</div> |
|
|
<p class="text-sm text-gray-700">Went for a walk in the park and it helped clear my mind. The sunshine felt nice.</p> |
|
|
</div> |
|
|
<div class="p-3 bg-amber-50 rounded-lg"> |
|
|
<div class="flex justify-between items-start mb-2"> |
|
|
<p class="font-medium">Struggling</p> |
|
|
<span class="text-xs text-gray-500">2 days ago</span> |
|
|
</div> |
|
|
<p class="text-sm text-gray-700">Hard to get out of bed today. Everything feels heavy.</p> |
|
|
</div> |
|
|
<div class="p-3 bg-indigo-50 rounded-lg"> |
|
|
<div class="flex justify-between items-start mb-2"> |
|
|
<p class="font-medium">Productive day</p> |
|
|
<span class="text-xs text-gray-500">4 days ago</span> |
|
|
</div> |
|
|
<p class="text-sm text-gray-700">Finished my project ahead of schedule. Feeling accomplished.</p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="bg-white rounded-xl shadow-md p-6 fade-in"> |
|
|
<h2 class="text-xl font-semibold text-gray-800 mb-4">Resources</h2> |
|
|
<div class="space-y-3"> |
|
|
<a href="#" class="flex items-center p-2 rounded-lg hover:bg-gray-50 transition"> |
|
|
<div class="w-8 h-8 rounded-full bg-green-100 flex items-center justify-center mr-3"> |
|
|
<i class="fas fa-head-side-virus text-green-600"></i> |
|
|
</div> |
|
|
<p class="text-sm">Coping Strategies</p> |
|
|
</a> |
|
|
<a href="#" class="flex items-center p-2 rounded-lg hover:bg-gray-50 transition"> |
|
|
<div class="w-8 h-8 rounded-full bg-blue-100 flex items-center justify-center mr-3"> |
|
|
<i class="fas fa-hands-holding-child text-blue-600"></i> |
|
|
</div> |
|
|
<p class="text-sm">Support Groups</p> |
|
|
</a> |
|
|
<a href="#" class="flex items-center p-2 rounded-lg hover:bg-gray-50 transition"> |
|
|
<div class="w-8 h-8 rounded-full bg-purple-100 flex items-center justify-center mr-3"> |
|
|
<i class="fas fa-book text-purple-600"></i> |
|
|
</div> |
|
|
<p class="text-sm">Depression Education</p> |
|
|
</a> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</main> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="toast" class="fixed bottom-4 right-4 bg-green-500 text-white px-4 py-2 rounded-lg shadow-lg hidden items-center"> |
|
|
<i class="fas fa-check-circle mr-2"></i> |
|
|
<span>Mood saved successfully!</span> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
|
|
|
const now = new Date(); |
|
|
document.getElementById('current-date').textContent = now.toLocaleDateString('en-US', { |
|
|
weekday: 'long', |
|
|
month: 'short', |
|
|
day: 'numeric' |
|
|
}); |
|
|
|
|
|
|
|
|
const themeToggle = document.getElementById('theme-toggle'); |
|
|
themeToggle.addEventListener('click', () => { |
|
|
document.documentElement.classList.toggle('dark'); |
|
|
const icon = themeToggle.querySelector('i'); |
|
|
if (document.documentElement.classList.contains('dark')) { |
|
|
icon.classList.replace('fa-moon', 'fa-sun'); |
|
|
document.body.classList.add('bg-gray-900'); |
|
|
document.body.classList.remove('bg-gray-50'); |
|
|
} else { |
|
|
icon.classList.replace('fa-sun', 'fa-moon'); |
|
|
document.body.classList.remove('bg-gray-900'); |
|
|
document.body.classList.add('bg-gray-50'); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
const moodBtns = document.querySelectorAll('.mood-btn'); |
|
|
const moodDescription = document.getElementById('mood-description'); |
|
|
let selectedMood = null; |
|
|
|
|
|
moodBtns.forEach(btn => { |
|
|
btn.addEventListener('click', () => { |
|
|
|
|
|
moodBtns.forEach(b => { |
|
|
b.classList.remove('mood-1', 'mood-2', 'mood-3', 'mood-4', 'mood-5'); |
|
|
b.classList.add('opacity-50'); |
|
|
}); |
|
|
|
|
|
|
|
|
selectedMood = parseInt(btn.dataset.value); |
|
|
btn.classList.add(`mood-${selectedMood}`); |
|
|
btn.classList.remove('opacity-50'); |
|
|
|
|
|
|
|
|
moodDescription.classList.remove('hidden'); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
const saveMoodBtn = document.getElementById('save-mood'); |
|
|
const moodNotes = document.getElementById('mood-notes'); |
|
|
const toast = document.getElementById('toast'); |
|
|
|
|
|
saveMoodBtn.addEventListener('click', () => { |
|
|
if (!selectedMood) return; |
|
|
|
|
|
|
|
|
const moodData = { |
|
|
date: now, |
|
|
mood: selectedMood, |
|
|
notes: moodNotes.value |
|
|
}; |
|
|
|
|
|
|
|
|
let moods = JSON.parse(localStorage.getItem('moods') || '[]'); |
|
|
moods.push(moodData); |
|
|
localStorage.setItem('moods', JSON.stringify(moods)); |
|
|
|
|
|
|
|
|
toast.classList.remove('hidden'); |
|
|
setTimeout(() => { |
|
|
toast.classList.add('hidden'); |
|
|
}, 3000); |
|
|
|
|
|
|
|
|
moodBtns.forEach(b => { |
|
|
b.classList.remove('mood-1', 'mood-2', 'mood-3', 'mood-4', 'mood-5'); |
|
|
b.classList.remove('opacity-50'); |
|
|
}); |
|
|
moodNotes.value = ''; |
|
|
moodDescription.classList.add('hidden'); |
|
|
selectedMood = null; |
|
|
|
|
|
|
|
|
updateChart(7); |
|
|
updateStats(); |
|
|
}); |
|
|
|
|
|
|
|
|
const timeBtns = document.querySelectorAll('.time-btn'); |
|
|
timeBtns.forEach(btn => { |
|
|
btn.addEventListener('click', () => { |
|
|
timeBtns.forEach(b => { |
|
|
b.classList.remove('bg-indigo-100', 'text-indigo-700'); |
|
|
b.classList.add('hover:bg-indigo-100', 'hover:text-indigo-700'); |
|
|
}); |
|
|
btn.classList.add('bg-indigo-100', 'text-indigo-700'); |
|
|
btn.classList.remove('hover:bg-indigo-100', 'hover:text-indigo-700'); |
|
|
|
|
|
const range = parseInt(btn.dataset.range); |
|
|
updateChart(range); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
function generateMockData() { |
|
|
if (localStorage.getItem('moods')) return; |
|
|
|
|
|
const moods = []; |
|
|
const today = new Date(); |
|
|
|
|
|
for (let i = 0; i < 90; i++) { |
|
|
const date = new Date(); |
|
|
date.setDate(today.getDate() - (90 - i)); |
|
|
|
|
|
|
|
|
const cyclePhase = Math.floor(i / 15) % 4; |
|
|
let mood; |
|
|
|
|
|
if (cyclePhase === 0) { |
|
|
mood = Math.floor(Math.random() * 2) + 4; |
|
|
} else if (cyclePhase === 1) { |
|
|
mood = 3; |
|
|
} else if (cyclePhase === 2) { |
|
|
mood = Math.floor(Math.random() * 2) + 1; |
|
|
} else { |
|
|
mood = 3; |
|
|
} |
|
|
|
|
|
moods.push({ |
|
|
date: date, |
|
|
mood: mood, |
|
|
notes: '' |
|
|
}); |
|
|
} |
|
|
|
|
|
localStorage.setItem('moods', JSON.stringify(moods)); |
|
|
} |
|
|
|
|
|
|
|
|
function updateChart(days) { |
|
|
generateMockData(); |
|
|
const moods = JSON.parse(localStorage.getItem('moods')); |
|
|
const chart = document.getElementById('mood-chart'); |
|
|
chart.innerHTML = ''; |
|
|
|
|
|
const today = new Date(); |
|
|
const startDate = new Date(); |
|
|
startDate.setDate(today.getDate() - days); |
|
|
|
|
|
const filteredMoods = moods.filter(mood => { |
|
|
const moodDate = new Date(mood.date); |
|
|
return moodDate >= startDate && moodDate <= today; |
|
|
}); |
|
|
|
|
|
|
|
|
const groupedMoods = {}; |
|
|
filteredMoods.forEach(mood => { |
|
|
const dateStr = new Date(mood.date).toLocaleDateString(); |
|
|
if (!groupedMoods[dateStr]) { |
|
|
groupedMoods[dateStr] = []; |
|
|
} |
|
|
groupedMoods[dateStr].push(mood.mood); |
|
|
}); |
|
|
|
|
|
|
|
|
const moodAverages = []; |
|
|
Object.keys(groupedMoods).forEach(dateStr => { |
|
|
const moods = groupedMoods[dateStr]; |
|
|
const avg = moods.reduce((a, b) => a + b, 0) / moods.length; |
|
|
moodAverages.push({ |
|
|
date: dateStr, |
|
|
avg: avg |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
moodAverages.sort((a, b) => new Date(a.date) - new Date(b.date)); |
|
|
|
|
|
|
|
|
const maxHeight = 180; |
|
|
moodAverages.forEach((day, index) => { |
|
|
const height = (day.avg / 5) * maxHeight; |
|
|
const date = new Date(day.date); |
|
|
const dayName = date.toLocaleDateString('en-US', { weekday: 'short' }).charAt(0); |
|
|
|
|
|
const bar = document.createElement('div'); |
|
|
bar.className = `chart-bar flex flex-col items-center w-8 mx-1`; |
|
|
|
|
|
const barFill = document.createElement('div'); |
|
|
barFill.className = `w-full rounded-t-md mood-${Math.round(day.avg)}`; |
|
|
barFill.style.height = `${height}px`; |
|
|
|
|
|
const dayLabel = document.createElement('span'); |
|
|
dayLabel.className = 'text-xs text-gray-500 mt-1'; |
|
|
dayLabel.textContent = dayName; |
|
|
|
|
|
bar.appendChild(barFill); |
|
|
bar.appendChild(dayLabel); |
|
|
chart.appendChild(bar); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
function updateStats() { |
|
|
generateMockData(); |
|
|
const moods = JSON.parse(localStorage.getItem('moods')); |
|
|
|
|
|
|
|
|
const avgMood = moods.reduce((sum, mood) => sum + mood.mood, 0) / moods.length; |
|
|
document.getElementById('avg-mood').textContent = avgMood.toFixed(1); |
|
|
|
|
|
|
|
|
const thisMonth = new Date().getMonth(); |
|
|
const lowDays = moods.filter(mood => { |
|
|
const moodDate = new Date(mood.date); |
|
|
return moodDate.getMonth() === thisMonth && mood.mood <= 2; |
|
|
}).length; |
|
|
document.getElementById('low-days').textContent = lowDays; |
|
|
|
|
|
|
|
|
const last7Days = moods.slice(-7).map(m => m.mood); |
|
|
const avgLast7 = last7Days.reduce((a, b) => a + b, 0) / last7Days.length; |
|
|
|
|
|
let cycle; |
|
|
if (avgLast7 <= 2) { |
|
|
cycle = "Low Phase"; |
|
|
} else if (avgLast7 >= 4) { |
|
|
cycle = "Good Phase"; |
|
|
} else { |
|
|
cycle = "Neutral Phase"; |
|
|
} |
|
|
document.getElementById('current-cycle').textContent = cycle; |
|
|
|
|
|
|
|
|
let streak = 0; |
|
|
let prevMood = moods[moods.length - 1].mood; |
|
|
for (let i = moods.length - 1; i >= 0; i--) { |
|
|
if (moods[i].mood === prevMood) { |
|
|
streak++; |
|
|
} else { |
|
|
break; |
|
|
} |
|
|
} |
|
|
document.getElementById('current-streak').textContent = `${streak} day${streak !== 1 ? 's' : ''}`; |
|
|
} |
|
|
|
|
|
|
|
|
updateChart(7); |
|
|
updateStats(); |
|
|
timeBtns[0].click(); |
|
|
</script> |
|
|
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=twnatelo/depression-cycle-tracker-prototype" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
|
|
</html> |