Код: Выделить всё
// ============================================Код: Выделить всё
// FIXED: checkAndUpdateAchievements function - Fixed achievement checkingКод: Выделить всё
// ============================================Код: Выделить всё
function checkAndUpdateAchievements() {Код: Выделить всё
if (!appState.profile) return;Код: Выделить всё
Код: Выделить всё
const achievements = appState.profile.achievements || {};Код: Выделить всё
let newAchievements = [];Код: Выделить всё
Код: Выделить всё
ACHIEVEMENTS.forEach(achievement => {Код: Выделить всё
if (!achievements[achievement.id] || !achievements[achievement.id].unlocked) {Код: Выделить всё
if (achievement.condition(appState.profile, appState.todayData, appState.historicalData)) {Код: Выделить всё
if (!achievements[achievement.id]) {Код: Выделить всё
achievements[achievement.id] = {Код: Выделить всё
unlocked: true,Код: Выделить всё
unlockedAt: new Date().toISOString()Код: Выделить всё
};Код: Выделить всё
newAchievements.push(achievement);Код: Выделить всё
}Код: Выделить всё
}Код: Выделить всё
}Код: Выделить всё
});Код: Выделить всё
Код: Выделить всё
appState.profile.achievements = achievements;Код: Выделить всё
Код: Выделить всё
// Show animations for new achievementsКод: Выделить всё
if (newAchievements.length > 0) {Код: Выделить всё
// Show first achievement immediatelyКод: Выделить всё
showAchievementUnlock(newAchievements[0]);Код: Выделить всё
Код: Выделить всё
// Show remaining achievements with delayКод: Выделить всё
for (let i = 1; i < newAchievements.length; i++) {Код: Выделить всё
setTimeout(() => {Код: Выделить всё
showAchievementUnlock(newAchievements[i]);Код: Выделить всё
}, i * 4500);Код: Выделить всё
}Код: Выделить всё
Код: Выделить всё
saveAllData();Код: Выделить всё
}Код: Выделить всё
}Код: Выделить всё
// ============================================Код: Выделить всё
// FIXED: addDrink function - Fixed to properly trigger achievementsКод: Выделить всё
// ============================================Код: Выделить всё
function addDrink(type, amount) {Код: Выделить всё
const drink = BEVERAGES[type];Код: Выделить всё
if (!drink) return;Код: Выделить всё
Код: Выделить всё
const waterContent = Math.round(amount * drink.waterContent / 100);Код: Выделить всё
Код: Выделить всё
const drinkRecord = {Код: Выделить всё
type,Код: Выделить всё
amount,Код: Выделить всё
waterContent,Код: Выделить всё
timestamp: new Date().toISOString(),Код: Выделить всё
time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })Код: Выделить всё
};Код: Выделить всё
Код: Выделить всё
appState.todayData.consumed += waterContent;Код: Выделить всё
if (!appState.todayData.drinks) appState.todayData.drinks = [];Код: Выделить всё
appState.todayData.drinks.push(drinkRecord);Код: Выделить всё
Код: Выделить всё
// Check if goal is reachedКод: Выделить всё
const goalReached = !appState.todayData.completed && appState.todayData.consumed >= appState.todayData.goal;Код: Выделить всё
if (goalReached) {Код: Выделить всё
appState.todayData.completed = true;Код: Выделить всё
showNotification("🎉 Daily goal achieved! Great work!", "success");Код: Выделить всё
}Код: Выделить всё
Код: Выделить всё
// Save data immediatelyКод: Выделить всё
saveAllData();Код: Выделить всё
Код: Выделить всё
// Update UIКод: Выделить всё
updateDashboard();Код: Выделить всё
updateStatistics();Код: Выделить всё
updateTimeProgress();Код: Выделить всё
showNotification(`Added ${amount}ml of ${drink.name} (${waterContent}ml water)`, "success");Код: Выделить всё
Код: Выделить всё
// Check achievements AFTER updating UI and saving dataКод: Выделить всё
// This ensures the hydrated_beginner achievement triggers when goal is reachedКод: Выделить всё
checkAndUpdateAchievements();Код: Выделить всё
}Код: Выделить всё
// ============================================Код: Выделить всё
// FIXED: adjustHeight and adjustWeight functions - Fixed to save dataКод: Выделить всё
// ============================================Код: Выделить всё
function adjustHeight(change) {Код: Выделить всё
const input = document.getElementById('heightInput');Код: Выделить всё
let value = parseInt(input.value) + change;Код: Выделить всё
Код: Выделить всё
if (appState.unitSystem === 'metric') {Код: Выделить всё
value = Math.max(100, Math.min(250, value));Код: Выделить всё
} else {Код: Выделить всё
value = Math.max(39, Math.min(98, value));Код: Выделить всё
}Код: Выделить всё
Код: Выделить всё
input.value = value;Код: Выделить всё
updateHeightFromInput();Код: Выделить всё
}Код: Выделить всё
function adjustWeight(change) {Код: Выделить всё
const input = document.getElementById('weightInput');Код: Выделить всё
let value = parseInt(input.value) + change;Код: Выделить всё
Код: Выделить всё
if (appState.unitSystem === 'metric') {Код: Выделить всё
value = Math.max(30, Math.min(200, value));Код: Выделить всё
} else {Код: Выделить всё
value = Math.max(66, Math.min(440, value));Код: Выделить всё
}Код: Выделить всё
Код: Выделить всё
input.value = value;Код: Выделить всё
updateWeightFromInput();Код: Выделить всё
}Код: Выделить всё
// ============================================Код: Выделить всё
// FIXED: updateHeightFromInput and updateWeightFromInput - Fixed to save dataКод: Выделить всё
// ============================================Код: Выделить всё
function updateHeightFromInput() {Код: Выделить всё
const input = document.getElementById('heightInput');Код: Выделить всё
let value = parseInt(input.value);Код: Выделить всё
Код: Выделить всё
if (isNaN(value)) {Код: Выделить всё
value = appState.unitSystem === 'metric' ? 170 : 67;Код: Выделить всё
}Код: Выделить всё
Код: Выделить всё
if (appState.unitSystem === 'metric') {Код: Выделить всё
value = Math.max(100, Math.min(250, value));Код: Выделить всё
} else {Код: Выделить всё
value = Math.max(39, Math.min(98, value));Код: Выделить всё
}Код: Выделить всё
Код: Выделить всё
input.value = value;Код: Выделить всё
document.getElementById('heightValue').textContent = value;Код: Выделить всё
Код: Выделить всё
// Update the ruler positionКод: Выделить всё
updateRuler('heightRuler', value);Код: Выделить всё
Код: Выделить всё
// Save to profile (converting if necessary)Код: Выделить всё
if (appState.unitSystem === 'metric') {Код: Выделить всё
appState.profile.height = value;Код: Выделить всё
} else {Код: Выделить всё
// Convert inches to cm for storageКод: Выделить всё
appState.profile.height = Math.round(value * 2.54);Код: Выделить всё
}Код: Выделить всё
Код: Выделить всё
// Save dataКод: Выделить всё
saveAllData();Код: Выделить всё
}Код: Выделить всё
function updateWeightFromInput() {Код: Выделить всё
const input = document.getElementById('weightInput');Код: Выделить всё
let value = parseInt(input.value);Код: Выделить всё
Код: Выделить всё
if (isNaN(value)) {Код: Выделить всё
value = appState.unitSystem === 'metric' ? 70 : 154;Код: Выделить всё
}Код: Выделить всё
Код: Выделить всё
if (appState.unitSystem === 'metric') {Код: Выделить всё
value = Math.max(30, Math.min(200, value));Код: Выделить всё
} else {Код: Выделить всё
value = Math.max(66, Math.min(440, value));Код: Выделить всё
}Код: Выделить всё
Код: Выделить всё
input.value = value;Код: Выделить всё
document.getElementById('weightValue').textContent = value;Код: Выделить всё
updateRuler('weightRuler', value);Код: Выделить всё
Код: Выделить всё
// Save to profile (converting if necessary)Код: Выделить всё
if (appState.unitSystem === 'metric') {Код: Выделить всё
appState.profile.weight = value;Код: Выделить всё
} else {Код: Выделить всё
// Convert lbs to kg for storageКод: Выделить всё
appState.profile.weight = Math.round(value / 2.20462);Код: Выделить всё
}Код: Выделить всё
Код: Выделить всё
// Save dataКод: Выделить всё
saveAllData();Код: Выделить всё
}Код: Выделить всё
// ============================================Код: Выделить всё
// REST OF THE CODE REMAINS THE SAME (unchanged parts)Код: Выделить всё
// ============================================Код: Выделить всё
const BEVERAGES = {Код: Выделить всё
water: {Код: Выделить всё
name: "Water",Код: Выделить всё
icon: "fa-tint",Код: Выделить всё
color: "#4fd1ff",Код: Выделить всё
waterContent: 100,Код: Выделить всё
description: "Pure hydration - no additives"Код: Выделить всё
},Код: Выделить всё
sparkling_water: {Код: Выделить всё
name: "Sparkling Water",Код: Выделить всё
icon: "fa-glass-water",Код: Выделить всё
color: "#6b7cff",Код: Выделить всё
waterContent: 100,Код: Выделить всё
description: "Carbonated water - same hydration"Код: Выделить всё
},Код: Выделить всё
herbal_tea: {Код: Выделить всё
name: "Herbal Tea",Код: Выделить всё
icon: "fa-mug-hot",Код: Выделить всё
color: "#ffb84d",Код: Выделить всё
waterContent: 99,Код: Выделить всё
description: "No caffeine - excellent hydration"Код: Выделить всё
},Код: Выделить всё
green_tea: {Код: Выделить всё
name: "Green Tea",Код: Выделить всё
icon: "fa-leaf",Код: Выделить всё
color: "#4dff88",Код: Выделить всё
waterContent: 99,Код: Выделить всё
description: "Antioxidants - minimal caffeine"Код: Выделить всё
},Код: Выделить всё
black_coffee: {Код: Выделить всё
name: "Black Coffee",Код: Выделить всё
icon: "fa-coffee",Код: Выделить всё
color: "#8B4513",Код: Выделить всё
waterContent: 98,Код: Выделить всё
description: "Caffeine reduces hydration by 15%"Код: Выделить всё
},Код: Выделить всё
milk: {Код: Выделить всё
name: "Milk",Код: Выделить всё
icon: "fa-cow",Код: Выделить всё
color: "#ffffff",Код: Выделить всё
waterContent: 87,Код: Выделить всё
description: "Contains nutrients - good hydration"Код: Выделить всё
},Код: Выделить всё
fruit_juice: {Код: Выделить всё
name: "Fruit Juice",Код: Выделить всё
icon: "fa-apple-alt",Код: Выделить всё
color: "#ff6b6b",Код: Выделить всё
waterContent: 88,Код: Выделить всё
description: "Natural sugars - moderate hydration"Код: Выделить всё
},Код: Выделить всё
sports_drink: {Код: Выделить всё
name: "Sports Drink",Код: Выделить всё
icon: "fa-bolt",Код: Выделить всё
color: "#ff4d6d",Код: Выделить всё
waterContent: 94,Код: Выделить всё
description: "Electrolytes - good for exercise"Код: Выделить всё
},Код: Выделить всё
soda: {Код: Выделить всё
name: "Soda",Код: Выделить всё
icon: "fa-glass-whiskey",Код: Выделить всё
color: "#ff4757",Код: Выделить всё
waterContent: 90,Код: Выделить всё
description: "High sugar - poor hydration"Код: Выделить всё
},Код: Выделить всё
smoothie: {Код: Выделить всё
name: "Smoothie",Код: Выделить всё
icon: "fa-blender",Код: Выделить всё
color: "#9d4edd",Код: Выделить всё
waterContent: 85,Код: Выделить всё
description: "Nutrient-rich - good hydration"Код: Выделить всё
}Код: Выделить всё
};Код: Выделить всё
const HYDRATION_TIPS = [Код: Выделить всё
"Start your day with a glass of water to kickstart metabolism",Код: Выделить всё
"Drink water 30 minutes before meals for better digestion",Код: Выделить всё
"Your body is about 60% water - keep it replenished!",Код: Выделить всё
"Dehydration can reduce cognitive performance by up to 30%",Код: Выделить всё
"Carry a reusable water bottle to stay hydrated on the go",Код: Выделить всё
"Eat water-rich foods like watermelon, cucumber, and oranges",Код: Выделить всё
"Listen to your body - thirst is a sign you're already dehydrated",Код: Выделить всё
"Alcohol and caffeine can dehydrate you - balance with water",Код: Выделить всё
"Stay hydrated during exercise to maintain performance",Код: Выделить всё
"Proper hydration helps regulate body temperature"Код: Выделить всё
];Код: Выделить всё
const ACTIVITY_LEVELS = {Код: Выделить всё
sedentary: 1.0,Код: Выделить всё
light: 1.2,Код: Выделить всё
moderate: 1.4,Код: Выделить всё
active: 1.6,Код: Выделить всё
athlete: 1.8Код: Выделить всё
};Код: Выделить всё
let appState = {Код: Выделить всё
profile: null,Код: Выделить всё
todayData: null,Код: Выделить всё
historicalData: {},Код: Выделить всё
settings: {},Код: Выделить всё
selectedDrink: null,Код: Выделить всё
selectedAmount: 250,Код: Выделить всё
currentChartType: 'weekly',Код: Выделить всё
currentPieType: 'today',Код: Выделить всё
currentMonth: new Date().getMonth(),Код: Выделить всё
currentYear: new Date().getFullYear(),Код: Выделить всё
dataVersion: 5,Код: Выделить всё
unitSystem: 'metric'Код: Выделить всё
};Код: Выделить всё
let trendChart = null;Код: Выделить всё
let distributionChart = null;Код: Выделить всё
function quickAddDrink(type, amount) {Код: Выделить всё
addDrink(type, amount);Код: Выделить всё
}Код: Выделить всё
function setGoalPercentage(percentage) {Код: Выделить всё
const goal = appState.profile.dailyGoal;Код: Выделить всё
const targetAmount = Math.round((goal * percentage) / 100);Код: Выделить всё
Код: Выделить всё
if (appState.todayData.consumed < targetAmount) {Код: Выделить всё
const needed = targetAmount - appState.todayData.consumed;Код: Выделить всё
if (needed > 0) {Код: Выделить всё
addDrink('water', needed);Код: Выделить всё
}Код: Выделить всё
}Код: Выделить всё
Код: Выделить всё
updateHydrationLevels();Код: Выделить всё
}Код: Выделить всё
function updateHydrationLevels() {Код: Выделить всё
if (!appState.todayData || !appState.profile) return;Код: Выделить всё
Код: Выделить всё
const consumed = appState.todayData.consumed || 0;Код: Выделить всё
const goal = appState.todayData.goal || appState.profile.dailyGoal || 2000;Код: Выделить всё
const progress = Math.min(Math.round((consumed / goal) * 100), 100);Код: Выделить всё
Код: Выделить всё
const hydrationLevels = document.querySelectorAll('.hydration-level');Код: Выделить всё
hydrationLevels.forEach(level => level.classList.remove('active'));Код: Выделить всё
Код: Выделить всё
if (progress >= 87.5) {Код: Выделить всё
hydrationLevels[4].classList.add('active');Код: Выделить всё
} else if (progress >= 62.5) {Код: Выделить всё
hydrationLevels[3].classList.add('active');Код: Выделить всё
} else if (progress >= 37.5) {Код: Выделить всё
hydrationLevels[2].classList.add('active');Код: Выделить всё
} else if (progress >= 12.5) {Код: Выделить всё
hydrationLevels[1].classList.add('active');Код: Выделить всё
} else {Код: Выделить всё
hydrationLevels[0].classList.add('active');Код: Выделить всё
}Код: Выделить всё
}Код: Выделить всё
function checkAndUpdateStreak() {Код: Выделить всё
const today = new Date();Код: Выделить всё
const yesterday = new Date(today);Код: Выделить всё
yesterday.setDate(yesterday.getDate() - 1);Код: Выделить всё
const yesterdayStr = yesterday.toDateString();Код: Выделить всё
Код: Выделить всё
const yesterdayData = appState.historicalData[yesterdayStr];Код: Выделить всё
Код: Выделить всё
if (yesterdayData) {Код: Выделить всё
if (yesterdayData.completed) {Код: Выделить всё
appState.profile.currentStreak = (appState.profile.currentStreak || 0) + 1;Код: Выделить всё
appState.profile.bestStreak = Math.max(appState.profile.bestStreak || 0, appState.profile.currentStreak);Код: Выделить всё
} else {Код: Выделить всё
appState.profile.currentStreak = 0;Код: Выделить всё
}Код: Выделить всё
}Код: Выделить всё
}Код: Выделить всё
function updateDashboard() {Код: Выделить всё
if (!appState.todayData) return;Код: Выделить всё
Код: Выделить всё
const consumed = appState.todayData.consumed || 0;Код: Выделить всё
const goal = appState.todayData.goal || appState.profile?.dailyGoal || 2000;Код: Выделить всё
const progress = Math.min(Math.round((consumed / goal) * 100), 100);Код: Выделить всё
Код: Выделить всё
document.getElementById('progressFill').style.width = `${progress}%`;Код: Выделить всё
document.getElementById('progressText').textContent = `${progress}%`;Код: Выделить всё
document.getElementById('waterLevel').style.height = `${progress}%`;Код: Выделить всё
document.getElementById('consumedDisplay').textContent = `${consumed.toLocaleString()} ml`;Код: Выделить всё
document.getElementById('goalDisplay').textContent = `of ${goal.toLocaleString()} ml goal`;Код: Выделить всё
document.getElementById('drinksCount').textContent = appState.todayData.drinks?.length || 0;Код: Выделить всё
document.getElementById('streakCount').textContent = appState.profile?.currentStreak || 0;Код: Выделить всё
Код: Выделить всё
const todayDrinks = appState.todayData.drinks || [];Код: Выделить всё
const todayBest = todayDrinks.length > 0 ? Math.max(...todayDrinks.map(d => d.amount)) : 0;Код: Выделить всё
document.getElementById('todayBest').textContent = `${todayBest}ml`;Код: Выделить всё
Код: Выделить всё
updateHydrationLevels();Код: Выделить всё
updateQuickStats();Код: Выделить всё
updateRecentActivity();Код: Выделить всё
updateHydrationTip();Код: Выделить всё
Код: Выделить всё
if (appState.profile?.name) {Код: Выделить всё
document.getElementById('userName').textContent = appState.profile.name;Код: Выделить всё
}Код: Выделить всё
}Код: Выделить всё
function updateQuickStats() {Код: Выделить всё
if (!appState.todayData || !appState.historicalData) return;Код: Выделить всё
Код: Выделить всё
const last7Days = getLastNDays(7);Код: Выделить всё
const daysWithData = last7Days.filter(d => d && d.consumed > 0);Код: Выделить всё
const weeklyTotal = daysWithData.reduce((sum, day) => sum + (day?.consumed || 0), 0);Код: Выделить всё
const weekAvg = daysWithData.length > 0 ? Math.round(weeklyTotal / daysWithData.length) : 0;Код: Выделить всё
document.getElementById('weekAvg').textContent = `${weekAvg}ml`;Код: Выделить всё
Код: Выделить всё
const last30Days = getLastNDays(30);Код: Выделить всё
const monthlyTotal = last30Days.reduce((sum, day) => sum + (day?.consumed || 0), 0);Код: Выделить всё
document.getElementById('monthTotal').textContent = `${Math.round(monthlyTotal / 1000)}L`;Код: Выделить всё
Код: Выделить всё
const goalDays = last30Days.filter(d => d?.completed).length;Код: Выделить всё
const goalPercent = last30Days.length > 0 ? Math.round((goalDays / last30Days.length) * 100) : 0;Код: Выделить всё
document.getElementById('goalDays').textContent = `${goalPercent}%`;Код: Выделить всё
Код: Выделить всё
document.getElementById('bestStreak').textContent = appState.profile?.bestStreak || 0;Код: Выделить всё
}Код: Выделить всё
function getLastNDays(n) {Код: Выделить всё
const days = [];Код: Выделить всё
for (let i = 0; i < n; i++) {Код: Выделить всё
const date = new Date();Код: Выделить всё
date.setDate(date.getDate() - i);Код: Выделить всё
const dateStr = date.toDateString();Код: Выделить всё
days.push(appState.historicalData[dateStr]);Код: Выделить всё
}Код: Выделить всё
return days;Код: Выделить всё
}Код: Выделить всё
function getLastNDaysData(n) {Код: Выделить всё
const days = [];Код: Выделить всё
for (let i = 0; i < n; i++) {Код: Выделить всё
const date = new Date();Код: Выделить всё
date.setDate(date.getDate() - i);Код: Выделить всё
const dateStr = date.toDateString();Код: Выделить всё
days.push(appState.historicalData[dateStr]);Код: Выделить всё
}Код: Выделить всё
return days;Код: Выделить всё
}Код: Выделить всё
function updateHydrationTip() {Код: Выделить всё
const tip = HYDRATION_TIPS[Math.floor(Math.random() * HYDRATION_TIPS.length)];Код: Выделить всё
document.getElementById('hydrationTip').textContent = tip;Код: Выделить всё
}Код: Выделить всё
function selectGender(gender) {Код: Выделить всё
if (!appState.profile) return;Код: Выделить всё
appState.profile.gender = gender;Код: Выделить всё
Код: Выделить всё
document.querySelectorAll('.gender-option').forEach(option => {Код: Выделить всё
option.classList.remove('active');Код: Выделить всё
if (option.dataset.gender === gender) {Код: Выделить всё
option.classList.add('active');Код: Выделить всё
}Код: Выделить всё
});Код: Выделить всё
Код: Выделить всё
saveAllData();Код: Выделить всё
}Код: Выделить всё
function selectActivityLevel(level) {Код: Выделить всё
if (!appState.profile) return;Код: Выделить всё
appState.profile.activityLevel = level;Код: Выделить всё
Код: Выделить всё
document.querySelectorAll('.activity-level').forEach(item => {Код: Выделить всё
item.classList.remove('active');Код: Выделить всё
if (item.dataset.level === level) {Код: Выделить всё
item.classList.add('active');Код: Выделить всё
}Код: Выделить всё
});Код: Выделить всё
}Код: Выделить всё
function updateGreetingMessage() {Код: Выделить всё
const hour = new Date().getHours();Код: Выделить всё
let greeting = "Stay Hydrated, Stay Healthy";Код: Выделить всё
Код: Выделить всё
if (hour < 12) greeting = "Good morning! Stay hydrated today";Код: Выделить всё
else if (hour < 18) greeting = "Good afternoon! Keep drinking water";Код: Выделить всё
else greeting = "Good evening! Finish your hydration goal";Код: Выделить всё
Код: Выделить всё
document.getElementById('greetingMessage').textContent = greeting;Код: Выделить всё
}Подробнее здесь: https://stackoverflow.com/questions/798 ... t-after-re
Мобильная версия