Skip to content

Commit bb56b5c

Browse files
committed
Site
1 parent 34efa9b commit bb56b5c

5 files changed

Lines changed: 1652 additions & 0 deletions

File tree

app.js

Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
// Main application logic
2+
const App = {
3+
currentDate: new Date(),
4+
currentMeal: null,
5+
currentEntryIndex: null,
6+
7+
// Initialize app
8+
init() {
9+
// Initialize storage
10+
Storage.init();
11+
12+
// Initialize chart
13+
Charts.init();
14+
15+
// Set up event listeners
16+
this.setupEventListeners();
17+
18+
// Load initial data
19+
this.loadCurrentDate();
20+
},
21+
22+
// Set up all event listeners
23+
setupEventListeners() {
24+
// Date navigation
25+
document.getElementById('prevDay').addEventListener('click', () => this.changeDate(-1));
26+
document.getElementById('nextDay').addEventListener('click', () => this.changeDate(1));
27+
28+
// Weight input
29+
const weightInput = document.getElementById('dailyWeight');
30+
weightInput.addEventListener('change', (e) => this.updateWeight(e.target.value));
31+
weightInput.addEventListener('blur', (e) => this.updateWeight(e.target.value));
32+
33+
// Targets met checkboxes
34+
document.getElementById('caloriesMetCheck').addEventListener('change', () => this.updateTargetsMet());
35+
document.getElementById('proteinMetCheck').addEventListener('change', () => this.updateTargetsMet());
36+
37+
// Add entry buttons
38+
document.querySelectorAll('.add-entry-btn').forEach(btn => {
39+
btn.addEventListener('click', (e) => {
40+
const meal = e.target.dataset.meal;
41+
this.openEntryModal(meal);
42+
});
43+
});
44+
45+
// Modal controls
46+
document.getElementById('closeModal').addEventListener('click', () => this.closeEntryModal());
47+
document.getElementById('cancelModal').addEventListener('click', () => this.closeEntryModal());
48+
document.getElementById('entryModal').addEventListener('click', (e) => {
49+
if (e.target.id === 'entryModal') {
50+
this.closeEntryModal();
51+
}
52+
});
53+
54+
// Entry form
55+
document.getElementById('entryForm').addEventListener('submit', (e) => {
56+
e.preventDefault();
57+
this.saveEntry();
58+
});
59+
60+
// Settings
61+
document.getElementById('settingsToggle').addEventListener('click', () => this.toggleSettings());
62+
document.getElementById('saveSettings').addEventListener('click', () => this.saveSettings());
63+
},
64+
65+
// Change date
66+
changeDate(days) {
67+
this.currentDate.setDate(this.currentDate.getDate() + days);
68+
this.loadCurrentDate();
69+
},
70+
71+
// Load data for current date
72+
loadCurrentDate() {
73+
// Update date display
74+
this.updateDateDisplay();
75+
76+
// Load profile
77+
const profile = Storage.getProfile();
78+
document.getElementById('targetCalories').textContent = profile.calorieTarget;
79+
document.getElementById('targetProtein').textContent = profile.proteinTarget;
80+
81+
// Load day entry
82+
const dayEntry = Storage.getDayEntry(this.currentDate);
83+
84+
// Update weight input
85+
const weightInput = document.getElementById('dailyWeight');
86+
weightInput.value = dayEntry.weight || '';
87+
88+
// Update targets met
89+
document.getElementById('caloriesMetCheck').checked = dayEntry.targetsMet?.calories || false;
90+
document.getElementById('proteinMetCheck').checked = dayEntry.targetsMet?.protein || false;
91+
92+
// Render meals
93+
this.renderMeal('breakfast', dayEntry.meals.breakfast);
94+
this.renderMeal('lunch', dayEntry.meals.lunch);
95+
this.renderMeal('dinner', dayEntry.meals.dinner);
96+
97+
// Update totals
98+
this.updateTotals(dayEntry);
99+
100+
// Update chart
101+
Charts.update(this.currentDate);
102+
103+
// Update goal progress
104+
this.updateGoalProgress();
105+
},
106+
107+
// Update date display
108+
updateDateDisplay() {
109+
const options = { weekday: 'short', month: 'short', day: 'numeric' };
110+
const dateStr = this.currentDate.toLocaleDateString('en-US', options);
111+
document.getElementById('currentDate').textContent = dateStr;
112+
},
113+
114+
// Render meal entries
115+
renderMeal(meal, entries) {
116+
const container = document.getElementById(`${meal}Entries`);
117+
118+
if (!entries || entries.length === 0) {
119+
container.innerHTML = '<div class="empty-state">No entries yet</div>';
120+
return;
121+
}
122+
123+
container.innerHTML = entries.map((entry, index) => `
124+
<div class="entry-item">
125+
<div class="entry-macros">
126+
<div class="entry-macro">
127+
<span class="entry-macro-label">Cal</span>
128+
<span class="entry-macro-value">${entry.calories}</span>
129+
</div>
130+
<div class="entry-macro">
131+
<span class="entry-macro-label">Prot</span>
132+
<span class="entry-macro-value">${entry.protein}g</span>
133+
</div>
134+
<div class="entry-macro">
135+
<span class="entry-macro-label">Carbs</span>
136+
<span class="entry-macro-value">${entry.carbs}g</span>
137+
</div>
138+
<div class="entry-macro">
139+
<span class="entry-macro-label">Fats</span>
140+
<span class="entry-macro-value">${entry.fats}g</span>
141+
</div>
142+
</div>
143+
<div class="entry-actions">
144+
<button class="edit-btn" onclick="App.editEntry('${meal}', ${index})">✏️</button>
145+
<button class="delete-btn" onclick="App.deleteEntry('${meal}', ${index})">🗑️</button>
146+
</div>
147+
</div>
148+
`).join('');
149+
},
150+
151+
// Update totals
152+
updateTotals(dayEntry) {
153+
const totals = Storage.calculateTotals(dayEntry);
154+
const profile = Storage.getProfile();
155+
156+
// Update displayed totals
157+
document.getElementById('totalCalories').textContent = totals.calories;
158+
document.getElementById('totalProtein').textContent = Math.round(totals.protein);
159+
document.getElementById('totalCarbs').textContent = Math.round(totals.carbs);
160+
document.getElementById('totalFats').textContent = Math.round(totals.fats);
161+
162+
// Update progress bars
163+
const caloriesPercent = Math.min((totals.calories / profile.calorieTarget) * 100, 100);
164+
const proteinPercent = Math.min((totals.protein / profile.proteinTarget) * 100, 100);
165+
166+
document.getElementById('caloriesProgress').style.width = `${caloriesPercent}%`;
167+
document.getElementById('proteinProgress').style.width = `${proteinPercent}%`;
168+
},
169+
170+
// Open entry modal
171+
openEntryModal(meal, entryIndex = null) {
172+
this.currentMeal = meal;
173+
this.currentEntryIndex = entryIndex;
174+
175+
const modal = document.getElementById('entryModal');
176+
const form = document.getElementById('entryForm');
177+
const title = document.getElementById('modalTitle');
178+
179+
// Set title
180+
title.textContent = entryIndex !== null ? 'Edit Entry' : 'Add Entry';
181+
182+
// Populate form if editing
183+
if (entryIndex !== null) {
184+
const dayEntry = Storage.getDayEntry(this.currentDate);
185+
const entry = dayEntry.meals[meal][entryIndex];
186+
187+
document.getElementById('entryCalories').value = entry.calories;
188+
document.getElementById('entryProtein').value = entry.protein;
189+
document.getElementById('entryCarbs').value = entry.carbs;
190+
document.getElementById('entryFats').value = entry.fats;
191+
} else {
192+
form.reset();
193+
}
194+
195+
// Show modal
196+
modal.classList.remove('hidden');
197+
198+
// Focus first input
199+
setTimeout(() => {
200+
document.getElementById('entryCalories').focus();
201+
}, 100);
202+
},
203+
204+
// Close entry modal
205+
closeEntryModal() {
206+
const modal = document.getElementById('entryModal');
207+
const form = document.getElementById('entryForm');
208+
209+
modal.classList.add('hidden');
210+
form.reset();
211+
this.currentMeal = null;
212+
this.currentEntryIndex = null;
213+
},
214+
215+
// Save entry
216+
saveEntry() {
217+
const entry = {
218+
calories: Number(document.getElementById('entryCalories').value),
219+
protein: Number(document.getElementById('entryProtein').value),
220+
carbs: Number(document.getElementById('entryCarbs').value),
221+
fats: Number(document.getElementById('entryFats').value)
222+
};
223+
224+
if (this.currentEntryIndex !== null) {
225+
// Update existing entry
226+
Storage.updateMealEntry(this.currentDate, this.currentMeal, this.currentEntryIndex, entry);
227+
} else {
228+
// Add new entry
229+
Storage.addMealEntry(this.currentDate, this.currentMeal, entry);
230+
}
231+
232+
this.closeEntryModal();
233+
this.loadCurrentDate();
234+
},
235+
236+
// Edit entry
237+
editEntry(meal, index) {
238+
this.openEntryModal(meal, index);
239+
},
240+
241+
// Delete entry
242+
deleteEntry(meal, index) {
243+
if (confirm('Delete this entry?')) {
244+
Storage.deleteMealEntry(this.currentDate, meal, index);
245+
this.loadCurrentDate();
246+
}
247+
},
248+
249+
// Update weight
250+
updateWeight(value) {
251+
if (value && value !== '') {
252+
const weight = parseFloat(value);
253+
if (!isNaN(weight) && weight > 0) {
254+
Storage.updateWeight(this.currentDate, weight);
255+
this.updateGoalProgress();
256+
Charts.update(this.currentDate);
257+
}
258+
}
259+
},
260+
261+
// Update targets met
262+
updateTargetsMet() {
263+
const caloriesMet = document.getElementById('caloriesMetCheck').checked;
264+
const proteinMet = document.getElementById('proteinMetCheck').checked;
265+
266+
Storage.updateTargetsMet(this.currentDate, {
267+
calories: caloriesMet,
268+
protein: proteinMet
269+
});
270+
271+
this.updateGoalProgress();
272+
},
273+
274+
// Update goal progress section
275+
updateGoalProgress() {
276+
const profile = Storage.getProfile();
277+
const dayEntry = Storage.getDayEntry(this.currentDate);
278+
const expectedWeight = Storage.calculateExpectedWeight(this.currentDate);
279+
const streak = Storage.calculateStreak(this.currentDate);
280+
const compliance = Storage.calculateWeeklyCompliance(this.currentDate);
281+
282+
// Update expected weight
283+
if (expectedWeight !== null) {
284+
document.getElementById('expectedWeight').textContent = `${expectedWeight.toFixed(1)} kg`;
285+
} else {
286+
document.getElementById('expectedWeight').textContent = '—';
287+
}
288+
289+
// Update current weight
290+
if (dayEntry.weight) {
291+
document.getElementById('currentWeight').textContent = `${dayEntry.weight.toFixed(1)} kg`;
292+
293+
// Calculate difference
294+
if (expectedWeight !== null) {
295+
const diff = dayEntry.weight - expectedWeight;
296+
const diffText = diff >= 0
297+
? `+${diff.toFixed(1)} kg (ahead)`
298+
: `${diff.toFixed(1)} kg (behind)`;
299+
document.getElementById('weightDifference').textContent = diffText;
300+
} else {
301+
document.getElementById('weightDifference').textContent = '—';
302+
}
303+
} else {
304+
document.getElementById('currentWeight').textContent = '—';
305+
document.getElementById('weightDifference').textContent = '—';
306+
}
307+
308+
// Update streak
309+
document.getElementById('streakCount').textContent = `${streak} day${streak !== 1 ? 's' : ''}`;
310+
311+
// Update compliance
312+
document.getElementById('weeklyCompliance').textContent = `${compliance}%`;
313+
},
314+
315+
// Toggle settings panel
316+
toggleSettings() {
317+
const panel = document.getElementById('settingsPanel');
318+
panel.classList.toggle('hidden');
319+
320+
if (!panel.classList.contains('hidden')) {
321+
// Load current settings
322+
const profile = Storage.getProfile();
323+
document.getElementById('settingAge').value = profile.age;
324+
document.getElementById('settingStartWeight').value = profile.weightStart;
325+
document.getElementById('settingTargetWeight').value = profile.weightTarget;
326+
document.getElementById('settingCalorieTarget').value = profile.calorieTarget;
327+
document.getElementById('settingProteinTarget').value = profile.proteinTarget;
328+
document.getElementById('settingDuration').value = profile.duration;
329+
}
330+
},
331+
332+
// Save settings
333+
saveSettings() {
334+
const updates = {
335+
age: Number(document.getElementById('settingAge').value),
336+
weightStart: Number(document.getElementById('settingStartWeight').value),
337+
weightTarget: Number(document.getElementById('settingTargetWeight').value),
338+
calorieTarget: Number(document.getElementById('settingCalorieTarget').value),
339+
proteinTarget: Number(document.getElementById('settingProteinTarget').value),
340+
duration: Number(document.getElementById('settingDuration').value)
341+
};
342+
343+
Storage.updateProfile(updates);
344+
345+
// Close settings panel
346+
document.getElementById('settingsPanel').classList.add('hidden');
347+
348+
// Reload current date to reflect new targets
349+
this.loadCurrentDate();
350+
}
351+
};
352+
353+
// Initialize app when DOM is ready
354+
document.addEventListener('DOMContentLoaded', () => {
355+
App.init();
356+
});

0 commit comments

Comments
 (0)