Kyle93123 commited on
Commit
d8f7895
·
verified ·
1 Parent(s): 89ac138

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +877 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Weightloss Tracker
3
- emoji: 😻
4
- colorFrom: green
5
- colorTo: green
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: weightloss-tracker
3
+ emoji: 🐳
4
+ colorFrom: pink
5
+ colorTo: red
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,877 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Weight Loss Accountability Tracker</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
9
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
10
+ <style>
11
+ .gradient-bg {
12
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
13
+ }
14
+ .rule-card {
15
+ transition: all 0.3s ease;
16
+ }
17
+ .rule-card:hover {
18
+ transform: translateY(-5px);
19
+ box-shadow: 0 10px 20px rgba(0,0,0,0.1);
20
+ }
21
+ .progress-ring__circle {
22
+ transition: stroke-dashoffset 0.35s;
23
+ transform: rotate(-90deg);
24
+ transform-origin: 50% 50%;
25
+ }
26
+ .fade-in {
27
+ animation: fadeIn 0.5s ease-in;
28
+ }
29
+ @keyframes fadeIn {
30
+ from { opacity: 0; }
31
+ to { opacity: 1; }
32
+ }
33
+ </style>
34
+ </head>
35
+ <body class="gradient-bg min-h-screen font-sans">
36
+ <div class="container mx-auto px-4 py-8">
37
+ <!-- App Header -->
38
+ <header class="flex justify-between items-center mb-8">
39
+ <h1 class="text-3xl font-bold text-gray-800">
40
+ <i class="fas fa-weight-scale mr-2 text-blue-500"></i> WeightLossTracker
41
+ </h1>
42
+ <button id="settingsBtn" class="p-2 rounded-full hover:bg-gray-200 transition">
43
+ <i class="fas fa-cog text-gray-600 text-xl"></i>
44
+ </button>
45
+ </header>
46
+
47
+ <!-- Main Content Area -->
48
+ <main>
49
+ <!-- Onboarding Screen (shown first) -->
50
+ <div id="onboarding" class="bg-white rounded-xl shadow-md p-6 mb-6 fade-in">
51
+ <h2 class="text-2xl font-semibold text-gray-800 mb-4">Let's Get Started!</h2>
52
+ <p class="text-gray-600 mb-6">Set your weight loss goals and we'll help you stay accountable.</p>
53
+
54
+ <form id="goalForm" class="space-y-4">
55
+ <div>
56
+ <label for="startWeight" class="block text-sm font-medium text-gray-700 mb-1">Current Weight (kg)</label>
57
+ <input type="number" id="startWeight" step="0.1" required
58
+ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
59
+ </div>
60
+
61
+ <div>
62
+ <label for="targetWeight" class="block text-sm font-medium text-gray-700 mb-1">Target Weight (kg)</label>
63
+ <input type="number" id="targetWeight" step="0.1" required
64
+ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
65
+ </div>
66
+
67
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
68
+ <div>
69
+ <label for="startDate" class="block text-sm font-medium text-gray-700 mb-1">Start Date</label>
70
+ <input type="date" id="startDate" required
71
+ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
72
+ </div>
73
+ <div>
74
+ <label for="endDate" class="block text-sm font-medium text-gray-700 mb-1">Target Date</label>
75
+ <input type="date" id="endDate" required
76
+ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
77
+ </div>
78
+ </div>
79
+
80
+ <button type="submit" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-4 rounded-lg transition duration-300">
81
+ Start My Journey <i class="fas fa-arrow-right ml-2"></i>
82
+ </button>
83
+ </form>
84
+ </div>
85
+
86
+ <!-- Dashboard (hidden initially) -->
87
+ <div id="dashboard" class="hidden">
88
+ <!-- Daily Stats Card -->
89
+ <div class="bg-white rounded-xl shadow-md p-6 mb-6 fade-in">
90
+ <div class="flex justify-between items-center mb-4">
91
+ <h2 class="text-xl font-semibold text-gray-800">Today's Progress</h2>
92
+ <span id="todayDate" class="text-gray-500 text-sm"></span>
93
+ </div>
94
+
95
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
96
+ <div class="bg-blue-50 p-4 rounded-lg">
97
+ <p class="text-sm text-blue-600 font-medium mb-1">Target Weight</p>
98
+ <p id="targetWeightToday" class="text-2xl font-bold text-blue-800">-- kg</p>
99
+ </div>
100
+ <div class="bg-green-50 p-4 rounded-lg">
101
+ <p class="text-sm text-green-600 font-medium mb-1">Your Weight</p>
102
+ <p id="actualWeightToday" class="text-2xl font-bold text-green-800">-- kg</p>
103
+ </div>
104
+ <div class="bg-purple-50 p-4 rounded-lg">
105
+ <p class="text-sm text-purple-600 font-medium mb-1">Difference</p>
106
+ <p id="weightDifference" class="text-2xl font-bold text-purple-800">-- kg</p>
107
+ </div>
108
+ </div>
109
+
110
+ <div class="mb-4">
111
+ <label for="todayWeight" class="block text-sm font-medium text-gray-700 mb-2">Enter Today's Weight (kg)</label>
112
+ <div class="flex">
113
+ <input type="number" id="todayWeight" step="0.1"
114
+ class="flex-1 px-4 py-2 border border-gray-300 rounded-l-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
115
+ <button id="saveWeightBtn" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-r-lg transition duration-300">
116
+ Save <i class="fas fa-check ml-1"></i>
117
+ </button>
118
+ </div>
119
+ </div>
120
+
121
+ <div class="flex items-center justify-between bg-yellow-50 p-3 rounded-lg">
122
+ <div>
123
+ <p class="text-sm font-medium text-yellow-800">Current Accountability Level</p>
124
+ <p id="currentLevel" class="text-lg font-bold text-yellow-900">Not set</p>
125
+ </div>
126
+ <div id="levelIndicator" class="w-10 h-10 rounded-full flex items-center justify-center text-white font-bold">
127
+ <span>--</span>
128
+ </div>
129
+ </div>
130
+ </div>
131
+
132
+ <!-- Rules Card -->
133
+ <div id="rulesCard" class="bg-white rounded-xl shadow-md p-6 mb-6 fade-in">
134
+ <h2 class="text-xl font-semibold text-gray-800 mb-4">Today's Rules</h2>
135
+ <p id="noRulesMessage" class="text-gray-500 italic">Complete your setup and enter today's weight to see your rules.</p>
136
+
137
+ <div id="rulesContainer" class="hidden grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
138
+ <!-- Rules will be inserted here by JavaScript -->
139
+ </div>
140
+ </div>
141
+
142
+ <!-- Progress Chart -->
143
+ <div class="bg-white rounded-xl shadow-md p-6 mb-6 fade-in">
144
+ <h2 class="text-xl font-semibold text-gray-800 mb-4">Progress Chart</h2>
145
+ <div class="h-64">
146
+ <canvas id="progressChart"></canvas>
147
+ </div>
148
+ </div>
149
+
150
+ <!-- Streaks & Compliance -->
151
+ <div class="bg-white rounded-xl shadow-md p-6 fade-in">
152
+ <h2 class="text-xl font-semibold text-gray-800 mb-4">Compliance Streak</h2>
153
+ <div class="flex items-center">
154
+ <div class="relative w-24 h-24 mr-4">
155
+ <svg class="w-full h-full" viewBox="0 0 100 100">
156
+ <circle class="text-gray-200" stroke-width="8" stroke="currentColor" fill="transparent" r="40" cx="50" cy="50"/>
157
+ <circle id="streakCircle" class="text-green-500 progress-ring__circle" stroke-width="8" stroke-linecap="round" stroke="currentColor" fill="transparent" r="40" cx="50" cy="50"/>
158
+ </svg>
159
+ <div class="absolute inset-0 flex items-center justify-center">
160
+ <span id="streakCount" class="text-2xl font-bold text-gray-800">0</span>
161
+ </div>
162
+ </div>
163
+ <div>
164
+ <p class="text-gray-600">Current streak of following your rules</p>
165
+ <p id="streakMessage" class="text-sm text-gray-500 mt-1">Keep it going!</p>
166
+ </div>
167
+ </div>
168
+ </div>
169
+ </div>
170
+
171
+ <!-- History Modal -->
172
+ <div id="historyModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
173
+ <div class="bg-white rounded-xl shadow-xl w-full max-w-2xl max-h-[80vh] overflow-y-auto">
174
+ <div class="p-6">
175
+ <div class="flex justify-between items-center mb-4">
176
+ <h3 class="text-xl font-semibold text-gray-800">Weight History</h3>
177
+ <button id="closeHistoryModal" class="text-gray-500 hover:text-gray-700">
178
+ <i class="fas fa-times"></i>
179
+ </button>
180
+ </div>
181
+ <div class="overflow-x-auto">
182
+ <table class="min-w-full divide-y divide-gray-200">
183
+ <thead class="bg-gray-50">
184
+ <tr>
185
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date</th>
186
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Weight</th>
187
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Target</th>
188
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Difference</th>
189
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Compliant</th>
190
+ </tr>
191
+ </thead>
192
+ <tbody id="historyTableBody" class="bg-white divide-y divide-gray-200">
193
+ <!-- History data will be inserted here -->
194
+ </tbody>
195
+ </table>
196
+ </div>
197
+ </div>
198
+ </div>
199
+ </div>
200
+
201
+ <!-- Settings Modal -->
202
+ <div id="settingsModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
203
+ <div class="bg-white rounded-xl shadow-xl w-full max-w-md max-h-[80vh] overflow-y-auto">
204
+ <div class="p-6">
205
+ <div class="flex justify-between items-center mb-4">
206
+ <h3 class="text-xl font-semibold text-gray-800">Settings</h3>
207
+ <button id="closeSettingsModal" class="text-gray-500 hover:text-gray-700">
208
+ <i class="fas fa-times"></i>
209
+ </button>
210
+ </div>
211
+
212
+ <div class="space-y-4">
213
+ <div>
214
+ <h4 class="font-medium text-gray-700 mb-2">View History</h4>
215
+ <button id="viewHistoryBtn" class="w-full bg-gray-100 hover:bg-gray-200 text-gray-800 py-2 px-4 rounded-lg transition">
216
+ <i class="fas fa-history mr-2"></i> View Weight History
217
+ </button>
218
+ </div>
219
+
220
+ <div>
221
+ <h4 class="font-medium text-gray-700 mb-2">Export Data</h4>
222
+ <button id="exportDataBtn" class="w-full bg-gray-100 hover:bg-gray-200 text-gray-800 py-2 px-4 rounded-lg transition">
223
+ <i class="fas fa-file-export mr-2"></i> Export as CSV
224
+ </button>
225
+ </div>
226
+
227
+ <div>
228
+ <h4 class="font-medium text-gray-700 mb-2">Reset Progress</h4>
229
+ <button id="resetDataBtn" class="w-full bg-red-50 hover:bg-red-100 text-red-600 py-2 px-4 rounded-lg transition">
230
+ <i class="fas fa-trash-alt mr-2"></i> Reset All Data
231
+ </button>
232
+ </div>
233
+ </div>
234
+ </div>
235
+ </div>
236
+ </div>
237
+ </main>
238
+
239
+ <!-- Footer Navigation -->
240
+ <footer class="fixed bottom-0 left-0 right-0 bg-white shadow-lg">
241
+ <div class="container mx-auto px-4 py-2">
242
+ <div class="flex justify-around">
243
+ <button id="dashboardBtn" class="flex flex-col items-center text-blue-600">
244
+ <i class="fas fa-home text-xl"></i>
245
+ <span class="text-xs mt-1">Dashboard</span>
246
+ </button>
247
+ <button id="historyBtn" class="flex flex-col items-center text-gray-500">
248
+ <i class="fas fa-history text-xl"></i>
249
+ <span class="text-xs mt-1">History</span>
250
+ </button>
251
+ <button id="settingsFooterBtn" class="flex flex-col items-center text-gray-500">
252
+ <i class="fas fa-cog text-xl"></i>
253
+ <span class="text-xs mt-1">Settings</span>
254
+ </button>
255
+ </div>
256
+ </div>
257
+ </footer>
258
+ </div>
259
+
260
+ <script>
261
+ // App State
262
+ let appState = {
263
+ startWeight: null,
264
+ targetWeight: null,
265
+ startDate: null,
266
+ endDate: null,
267
+ dailyLoss: null,
268
+ entries: [],
269
+ currentStreak: 0,
270
+ rules: {
271
+ 0: [
272
+ "No extra rules - you're on track!",
273
+ "Enjoy your day as planned"
274
+ ],
275
+ 1: [
276
+ "No snacks between meals",
277
+ "30 minutes of extra exercise",
278
+ "No alcohol today"
279
+ ],
280
+ 2: [
281
+ "Strict 16:8 intermittent fasting",
282
+ "60 minutes of intense exercise",
283
+ "No processed foods",
284
+ "No desserts or sweets"
285
+ ],
286
+ 3: [
287
+ "Only 2 meals today (no snacks)",
288
+ "90 minutes of intense exercise",
289
+ "No eating out - home cooked meals only",
290
+ "No screens during meals",
291
+ "Cold shower in the morning"
292
+ ]
293
+ }
294
+ };
295
+
296
+ // DOM Elements
297
+ const onboarding = document.getElementById('onboarding');
298
+ const dashboard = document.getElementById('dashboard');
299
+ const goalForm = document.getElementById('goalForm');
300
+ const todayDate = document.getElementById('todayDate');
301
+ const targetWeightToday = document.getElementById('targetWeightToday');
302
+ const actualWeightToday = document.getElementById('actualWeightToday');
303
+ const weightDifference = document.getElementById('weightDifference');
304
+ const todayWeight = document.getElementById('todayWeight');
305
+ const saveWeightBtn = document.getElementById('saveWeightBtn');
306
+ const currentLevel = document.getElementById('currentLevel');
307
+ const levelIndicator = document.getElementById('levelIndicator');
308
+ const rulesContainer = document.getElementById('rulesContainer');
309
+ const noRulesMessage = document.getElementById('noRulesMessage');
310
+ const streakCount = document.getElementById('streakCount');
311
+ const streakMessage = document.getElementById('streakMessage');
312
+ const streakCircle = document.getElementById('streakCircle');
313
+ const progressChart = document.getElementById('progressChart');
314
+ const historyModal = document.getElementById('historyModal');
315
+ const closeHistoryModal = document.getElementById('closeHistoryModal');
316
+ const viewHistoryBtn = document.getElementById('viewHistoryBtn');
317
+ const historyTableBody = document.getElementById('historyTableBody');
318
+ const settingsModal = document.getElementById('settingsModal');
319
+ const closeSettingsModal = document.getElementById('closeSettingsModal');
320
+ const settingsBtn = document.getElementById('settingsBtn');
321
+ const settingsFooterBtn = document.getElementById('settingsFooterBtn');
322
+ const dashboardBtn = document.getElementById('dashboardBtn');
323
+ const historyBtn = document.getElementById('historyBtn');
324
+ const exportDataBtn = document.getElementById('exportDataBtn');
325
+ const resetDataBtn = document.getElementById('resetDataBtn');
326
+
327
+ // Initialize the app
328
+ document.addEventListener('DOMContentLoaded', () => {
329
+ loadState();
330
+ setupEventListeners();
331
+
332
+ // Show onboarding if no data exists
333
+ if (!appState.startWeight) {
334
+ onboarding.classList.remove('hidden');
335
+ dashboard.classList.add('hidden');
336
+ } else {
337
+ onboarding.classList.add('hidden');
338
+ dashboard.classList.remove('hidden');
339
+ updateDashboard();
340
+ }
341
+
342
+ // Set today's date
343
+ const today = new Date();
344
+ todayDate.textContent = today.toLocaleDateString('en-US', {
345
+ weekday: 'long',
346
+ year: 'numeric',
347
+ month: 'long',
348
+ day: 'numeric'
349
+ });
350
+ });
351
+
352
+ // Load state from localStorage
353
+ function loadState() {
354
+ const savedState = localStorage.getItem('weightLossTracker');
355
+ if (savedState) {
356
+ appState = JSON.parse(savedState);
357
+
358
+ // Convert string dates back to Date objects
359
+ if (appState.startDate) appState.startDate = new Date(appState.startDate);
360
+ if (appState.endDate) appState.endDate = new Date(appState.endDate);
361
+ if (appState.entries && appState.entries.length > 0) {
362
+ appState.entries.forEach(entry => {
363
+ entry.date = new Date(entry.date);
364
+ });
365
+ }
366
+ }
367
+ }
368
+
369
+ // Save state to localStorage
370
+ function saveState() {
371
+ // Convert Date objects to strings for storage
372
+ const stateToSave = {
373
+ ...appState,
374
+ startDate: appState.startDate ? appState.startDate.toISOString() : null,
375
+ endDate: appState.endDate ? appState.endDate.toISOString() : null,
376
+ entries: appState.entries.map(entry => ({
377
+ ...entry,
378
+ date: entry.date.toISOString()
379
+ }))
380
+ };
381
+
382
+ localStorage.setItem('weightLossTracker', JSON.stringify(stateToSave));
383
+ }
384
+
385
+ // Set up event listeners
386
+ function setupEventListeners() {
387
+ // Form submission
388
+ goalForm.addEventListener('submit', (e) => {
389
+ e.preventDefault();
390
+ setGoals();
391
+ });
392
+
393
+ // Save weight button
394
+ saveWeightBtn.addEventListener('click', () => {
395
+ saveTodayWeight();
396
+ });
397
+
398
+ // Settings buttons
399
+ settingsBtn.addEventListener('click', () => {
400
+ settingsModal.classList.remove('hidden');
401
+ });
402
+
403
+ settingsFooterBtn.addEventListener('click', () => {
404
+ settingsModal.classList.remove('hidden');
405
+ });
406
+
407
+ closeSettingsModal.addEventListener('click', () => {
408
+ settingsModal.classList.add('hidden');
409
+ });
410
+
411
+ // History buttons
412
+ viewHistoryBtn.addEventListener('click', () => {
413
+ settingsModal.classList.add('hidden');
414
+ showHistory();
415
+ });
416
+
417
+ historyBtn.addEventListener('click', () => {
418
+ showHistory();
419
+ });
420
+
421
+ closeHistoryModal.addEventListener('click', () => {
422
+ historyModal.classList.add('hidden');
423
+ });
424
+
425
+ // Dashboard button
426
+ dashboardBtn.addEventListener('click', () => {
427
+ historyModal.classList.add('hidden');
428
+ settingsModal.classList.add('hidden');
429
+ });
430
+
431
+ // Export data button
432
+ exportDataBtn.addEventListener('click', exportData);
433
+
434
+ // Reset data button
435
+ resetDataBtn.addEventListener('click', resetData);
436
+ }
437
+
438
+ // Set goals from form
439
+ function setGoals() {
440
+ appState.startWeight = parseFloat(document.getElementById('startWeight').value);
441
+ appState.targetWeight = parseFloat(document.getElementById('targetWeight').value);
442
+ appState.startDate = new Date(document.getElementById('startDate').value);
443
+ appState.endDate = new Date(document.getElementById('endDate').value);
444
+
445
+ // Calculate daily weight loss needed
446
+ const totalDays = Math.ceil((appState.endDate - appState.startDate) / (1000 * 60 * 60 * 24));
447
+ const totalLoss = appState.startWeight - appState.targetWeight;
448
+ appState.dailyLoss = totalLoss / totalDays;
449
+
450
+ // Save and update UI
451
+ saveState();
452
+ onboarding.classList.add('hidden');
453
+ dashboard.classList.remove('hidden');
454
+ updateDashboard();
455
+ }
456
+
457
+ // Save today's weight
458
+ function saveTodayWeight() {
459
+ const weight = parseFloat(todayWeight.value);
460
+ if (isNaN(weight)) return;
461
+
462
+ const today = new Date();
463
+ today.setHours(0, 0, 0, 0);
464
+
465
+ // Check if entry for today exists
466
+ const existingEntryIndex = appState.entries.findIndex(entry => {
467
+ const entryDate = new Date(entry.date);
468
+ entryDate.setHours(0, 0, 0, 0);
469
+ return entryDate.getTime() === today.getTime();
470
+ });
471
+
472
+ // Calculate target weight for today
473
+ const daysPassed = Math.ceil((today - appState.startDate) / (1000 * 60 * 60 * 24));
474
+ const targetWeight = appState.startWeight - (daysPassed * appState.dailyLoss);
475
+ const difference = weight - targetWeight;
476
+
477
+ // Check if compliant (difference <= 0)
478
+ const compliant = difference <= 0;
479
+
480
+ // Update or add entry
481
+ if (existingEntryIndex !== -1) {
482
+ appState.entries[existingEntryIndex] = {
483
+ date: today,
484
+ weight,
485
+ targetWeight,
486
+ difference,
487
+ compliant
488
+ };
489
+ } else {
490
+ appState.entries.push({
491
+ date: today,
492
+ weight,
493
+ targetWeight,
494
+ difference,
495
+ compliant
496
+ });
497
+ }
498
+
499
+ // Update streak
500
+ updateStreak();
501
+
502
+ // Save and update UI
503
+ saveState();
504
+ updateDashboard();
505
+ todayWeight.value = '';
506
+ }
507
+
508
+ // Update streak count
509
+ function updateStreak() {
510
+ // Sort entries by date
511
+ const sortedEntries = [...appState.entries].sort((a, b) => b.date - a.date);
512
+
513
+ let streak = 0;
514
+ let broken = false;
515
+
516
+ // Check each day in order (most recent first)
517
+ for (let i = 0; i < sortedEntries.length && !broken; i++) {
518
+ if (sortedEntries[i].compliant) {
519
+ streak++;
520
+ } else {
521
+ broken = true;
522
+ }
523
+ }
524
+
525
+ appState.currentStreak = streak;
526
+
527
+ // Update streak circle
528
+ const circumference = 2 * Math.PI * 40;
529
+ const offset = circumference - (streak / 7 * circumference);
530
+ streakCircle.style.strokeDasharray = circumference;
531
+ streakCircle.style.strokeDashoffset = offset;
532
+
533
+ // Update streak message
534
+ if (streak === 0) {
535
+ streakMessage.textContent = "Start a new streak today!";
536
+ } else if (streak < 3) {
537
+ streakMessage.textContent = "Good start! Keep going!";
538
+ } else if (streak < 7) {
539
+ streakMessage.textContent = "Nice streak! Don't break it!";
540
+ } else {
541
+ streakMessage.textContent = "Amazing discipline! Keep it up!";
542
+ }
543
+ }
544
+
545
+ // Update dashboard with current data
546
+ function updateDashboard() {
547
+ const today = new Date();
548
+ today.setHours(0, 0, 0, 0);
549
+
550
+ // Calculate target weight for today
551
+ const daysPassed = Math.ceil((today - appState.startDate) / (1000 * 60 * 60 * 24));
552
+ const targetWeight = appState.startWeight - (daysPassed * appState.dailyLoss);
553
+
554
+ // Find today's entry if it exists
555
+ const todayEntry = appState.entries.find(entry => {
556
+ const entryDate = new Date(entry.date);
557
+ entryDate.setHours(0, 0, 0, 0);
558
+ return entryDate.getTime() === today.getTime();
559
+ });
560
+
561
+ // Update target weight display
562
+ targetWeightToday.textContent = targetWeight.toFixed(1) + ' kg';
563
+
564
+ // Update actual weight and difference
565
+ if (todayEntry) {
566
+ actualWeightToday.textContent = todayEntry.weight.toFixed(1) + ' kg';
567
+ weightDifference.textContent = todayEntry.difference.toFixed(1) + ' kg';
568
+
569
+ // Set color based on difference
570
+ if (todayEntry.difference <= 0) {
571
+ weightDifference.classList.remove('text-purple-800', 'text-red-600');
572
+ weightDifference.classList.add('text-green-600');
573
+ } else {
574
+ weightDifference.classList.remove('text-purple-800', 'text-green-600');
575
+ weightDifference.classList.add('text-red-600');
576
+ }
577
+
578
+ // Update accountability level
579
+ updateAccountabilityLevel(todayEntry.difference);
580
+ } else {
581
+ actualWeightToday.textContent = '-- kg';
582
+ weightDifference.textContent = '-- kg';
583
+ currentLevel.textContent = 'Not set';
584
+ levelIndicator.textContent = '--';
585
+ levelIndicator.className = 'w-10 h-10 rounded-full flex items-center justify-center bg-gray-200 text-gray-600 font-bold';
586
+ noRulesMessage.classList.remove('hidden');
587
+ rulesContainer.classList.add('hidden');
588
+ }
589
+
590
+ // Update streak display
591
+ streakCount.textContent = appState.currentStreak;
592
+
593
+ // Update chart
594
+ updateChart();
595
+ }
596
+
597
+ // Update accountability level based on difference
598
+ function updateAccountabilityLevel(difference) {
599
+ let level = 0;
600
+ let levelText = "Level 0 - On Track";
601
+ let bgColor = "bg-green-500";
602
+
603
+ if (difference > 0 && difference <= 1) {
604
+ level = 1;
605
+ levelText = "Level 1 - Mild Accountability";
606
+ bgColor = "bg-yellow-500";
607
+ } else if (difference > 1 && difference <= 2) {
608
+ level = 2;
609
+ levelText = "Level 2 - Moderate Accountability";
610
+ bgColor = "bg-orange-500";
611
+ } else if (difference > 2) {
612
+ level = 3;
613
+ levelText = "Level 3 - Strict Accountability";
614
+ bgColor = "bg-red-500";
615
+ }
616
+
617
+ currentLevel.textContent = levelText;
618
+ levelIndicator.textContent = level;
619
+ levelIndicator.className = `w-10 h-10 rounded-full flex items-center justify-center ${bgColor} text-white font-bold`;
620
+
621
+ // Show rules
622
+ showRules(level);
623
+ }
624
+
625
+ // Show rules for the current level
626
+ function showRules(level) {
627
+ noRulesMessage.classList.add('hidden');
628
+ rulesContainer.classList.remove('hidden');
629
+ rulesContainer.innerHTML = '';
630
+
631
+ const rules = appState.rules[level] || [];
632
+
633
+ rules.forEach((rule, index) => {
634
+ const ruleCard = document.createElement('div');
635
+ ruleCard.className = 'rule-card bg-white border border-gray-200 rounded-lg p-4 shadow-sm';
636
+ ruleCard.innerHTML = `
637
+ <div class="flex items-start">
638
+ <div class="flex-shrink-0 h-6 w-6 rounded-full bg-blue-100 flex items-center justify-center mr-3">
639
+ <span class="text-blue-600 text-sm font-medium">${index + 1}</span>
640
+ </div>
641
+ <p class="text-gray-700">${rule}</p>
642
+ </div>
643
+ `;
644
+ rulesContainer.appendChild(ruleCard);
645
+ });
646
+ }
647
+
648
+ // Update progress chart
649
+ function updateChart() {
650
+ // Sort entries by date
651
+ const sortedEntries = [...appState.entries].sort((a, b) => a.date - b.date);
652
+
653
+ // Prepare data for chart
654
+ const labels = [];
655
+ const targetData = [];
656
+ const actualData = [];
657
+
658
+ // Add start weight if no entries exist yet
659
+ if (sortedEntries.length === 0 && appState.startWeight) {
660
+ labels.push(appState.startDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }));
661
+ targetData.push(appState.startWeight);
662
+ actualData.push(appState.startWeight);
663
+ }
664
+
665
+ // Add all entries
666
+ sortedEntries.forEach(entry => {
667
+ labels.push(entry.date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }));
668
+ targetData.push(entry.targetWeight);
669
+ actualData.push(entry.weight);
670
+ });
671
+
672
+ // Add today if no entry exists yet
673
+ const today = new Date();
674
+ today.setHours(0, 0, 0, 0);
675
+
676
+ const hasTodayEntry = sortedEntries.some(entry => {
677
+ const entryDate = new Date(entry.date);
678
+ entryDate.setHours(0, 0, 0, 0);
679
+ return entryDate.getTime() === today.getTime();
680
+ });
681
+
682
+ if (!hasTodayEntry && appState.startWeight) {
683
+ const daysPassed = Math.ceil((today - appState.startDate) / (1000 * 60 * 60 * 24));
684
+ const targetWeight = appState.startWeight - (daysPassed * appState.dailyLoss);
685
+
686
+ labels.push(today.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }));
687
+ targetData.push(targetWeight);
688
+ actualData.push(null); // No actual weight yet
689
+ }
690
+
691
+ // Add target date if not in the past
692
+ if (appState.endDate && appState.endDate >= today) {
693
+ labels.push(appState.endDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }));
694
+ targetData.push(appState.targetWeight);
695
+ actualData.push(null);
696
+ }
697
+
698
+ // Create or update chart
699
+ const ctx = progressChart.getContext('2d');
700
+
701
+ if (window.progressChartInstance) {
702
+ window.progressChartInstance.data.labels = labels;
703
+ window.progressChartInstance.data.datasets[0].data = targetData;
704
+ window.progressChartInstance.data.datasets[1].data = actualData;
705
+ window.progressChartInstance.update();
706
+ } else {
707
+ window.progressChartInstance = new Chart(ctx, {
708
+ type: 'line',
709
+ data: {
710
+ labels: labels,
711
+ datasets: [
712
+ {
713
+ label: 'Target Weight',
714
+ data: targetData,
715
+ borderColor: '#3B82F6',
716
+ backgroundColor: 'rgba(59, 130, 246, 0.05)',
717
+ borderWidth: 2,
718
+ fill: true,
719
+ tension: 0.4
720
+ },
721
+ {
722
+ label: 'Your Weight',
723
+ data: actualData,
724
+ borderColor: '#10B981',
725
+ backgroundColor: 'rgba(16, 185, 129, 0.05)',
726
+ borderWidth: 2,
727
+ pointBackgroundColor: '#10B981',
728
+ pointRadius: 5,
729
+ pointHoverRadius: 7,
730
+ tension: 0.4
731
+ }
732
+ ]
733
+ },
734
+ options: {
735
+ responsive: true,
736
+ maintainAspectRatio: false,
737
+ scales: {
738
+ y: {
739
+ beginAtZero: false,
740
+ title: {
741
+ display: true,
742
+ text: 'Weight (kg)'
743
+ }
744
+ },
745
+ x: {
746
+ title: {
747
+ display: true,
748
+ text: 'Date'
749
+ }
750
+ }
751
+ },
752
+ plugins: {
753
+ tooltip: {
754
+ callbacks: {
755
+ label: function(context) {
756
+ return context.dataset.label + ': ' + context.parsed.y.toFixed(1) + ' kg';
757
+ }
758
+ }
759
+ },
760
+ legend: {
761
+ position: 'top',
762
+ }
763
+ }
764
+ }
765
+ });
766
+ }
767
+ }
768
+
769
+ // Show history modal
770
+ function showHistory() {
771
+ historyModal.classList.remove('hidden');
772
+
773
+ // Clear existing rows
774
+ historyTableBody.innerHTML = '';
775
+
776
+ // Sort entries by date (newest first)
777
+ const sortedEntries = [...appState.entries].sort((a, b) => b.date - a.date);
778
+
779
+ // Add rows for each entry
780
+ sortedEntries.forEach(entry => {
781
+ const row = document.createElement('tr');
782
+
783
+ const compliantIcon = entry.compliant
784
+ ? '<i class="fas fa-check-circle text-green-500"></i>'
785
+ : '<i class="fas fa-times-circle text-red-500"></i>';
786
+
787
+ row.innerHTML = `
788
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
789
+ ${entry.date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' })}
790
+ </td>
791
+ <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
792
+ ${entry.weight.toFixed(1)} kg
793
+ </td>
794
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
795
+ ${entry.targetWeight.toFixed(1)} kg
796
+ </td>
797
+ <td class="px-6 py-4 whitespace-nowrap text-sm ${entry.difference <= 0 ? 'text-green-600' : 'text-red-600'}">
798
+ ${entry.difference.toFixed(1)} kg
799
+ </td>
800
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
801
+ ${compliantIcon}
802
+ </td>
803
+ `;
804
+
805
+ historyTableBody.appendChild(row);
806
+ });
807
+
808
+ // Add message if no entries
809
+ if (sortedEntries.length === 0) {
810
+ const row = document.createElement('tr');
811
+ row.innerHTML = `
812
+ <td colspan="5" class="px-6 py-4 text-center text-sm text-gray-500">
813
+ No weight entries yet. Start tracking to see your history here.
814
+ </td>
815
+ `;
816
+ historyTableBody.appendChild(row);
817
+ }
818
+ }
819
+
820
+ // Export data as CSV
821
+ function exportData() {
822
+ if (appState.entries.length === 0) {
823
+ alert('No data to export yet.');
824
+ return;
825
+ }
826
+
827
+ // Create CSV header
828
+ let csv = 'Date,Weight (kg),Target Weight (kg),Difference (kg),Compliant\n';
829
+
830
+ // Add data rows
831
+ appState.entries.forEach(entry => {
832
+ csv += `"${entry.date.toLocaleDateString('en-US')}",`;
833
+ csv += `${entry.weight.toFixed(1)},`;
834
+ csv += `${entry.targetWeight.toFixed(1)},`;
835
+ csv += `${entry.difference.toFixed(1)},`;
836
+ csv += `${entry.compliant ? 'Yes' : 'No'}\n`;
837
+ });
838
+
839
+ // Create download link
840
+ const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
841
+ const url = URL.createObjectURL(blob);
842
+ const link = document.createElement('a');
843
+ link.setAttribute('href', url);
844
+ link.setAttribute('download', 'weight_loss_tracker_data.csv');
845
+ link.style.visibility = 'hidden';
846
+ document.body.appendChild(link);
847
+ link.click();
848
+ document.body.removeChild(link);
849
+ }
850
+
851
+ // Reset all data
852
+ function resetData() {
853
+ if (confirm('Are you sure you want to reset all data? This cannot be undone.')) {
854
+ localStorage.removeItem('weightLossTracker');
855
+ appState = {
856
+ startWeight: null,
857
+ targetWeight: null,
858
+ startDate: null,
859
+ endDate: null,
860
+ dailyLoss: null,
861
+ entries: [],
862
+ currentStreak: 0,
863
+ rules: appState.rules // Keep rules structure
864
+ };
865
+
866
+ onboarding.classList.remove('hidden');
867
+ dashboard.classList.add('hidden');
868
+ historyModal.classList.add('hidden');
869
+ settingsModal.classList.add('hidden');
870
+
871
+ // Reset form
872
+ goalForm.reset();
873
+ }
874
+ }
875
+ </script>
876
+ <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=Kyle93123/weightloss-tracker" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
877
+ </html>