arshenoy commited on
Commit
9c1d653
·
verified ·
1 Parent(s): 66f104a

fixed streak logic

Browse files
Files changed (1) hide show
  1. components/MedicationTracker.tsx +331 -280
components/MedicationTracker.tsx CHANGED
@@ -1,322 +1,373 @@
1
  import React, { useState, useCallback } from 'react';
2
- import { Plus, Trash2, CheckCircle, Trophy, AlertTriangle, Quote, Calendar, Edit2, X, Save } from 'lucide-react';
3
  import { Medication, PatientProfile } from '../types';
4
 
5
  interface MedicationTrackerProps {
6
- medications: Medication[];
7
- setMedications: React.Dispatch<React.SetStateAction<Medication[]>>;
8
- profile: PatientProfile;
9
- setProfile: React.Dispatch<React.SetStateAction<PatientProfile>>;
10
  }
11
 
12
  const MOTIVATIONAL_QUOTES = [
13
- "Success is stumbling from failure to failure with no loss of enthusiasm. – Winston Churchill",
14
- "Fall seven times, stand up eight. – Japanese Proverb",
15
- "The only real mistake is the one from which we learn nothing. – Henry Ford",
16
- "It does not matter how slowly you go as long as you do not stop. – Confucius"
17
  ];
18
 
19
- // Memoized item to prevent lag when typing in form
20
- const MedicationItem = React.memo(({ med, editingId, onToggle, onEdit, onDelete }: {
21
- med: Medication;
22
- editingId: string | null;
23
- onToggle: (id: string) => void;
24
- onEdit: (med: Medication) => void;
25
- onDelete: (id: string) => void;
26
- }) => (
27
- <div className={`glass-card p-4 rounded-xl flex justify-between items-center group transition-all relative ${editingId === med.id ? 'border-neon-yellow/50 bg-neon-yellow/5' : (med.taken ? 'border-green-500/30 bg-green-500/5' : 'border-white/5')}`}>
28
- <div className="flex items-center gap-4">
29
- <button
30
- onClick={() => onToggle(med.id)}
31
- className={`w-8 h-8 rounded-full flex items-center justify-center border transition-all ${
32
- med.taken
33
- ? 'bg-green-500 border-green-500 text-black'
34
- : 'border-gray-500 text-transparent hover:border-green-400'
35
- }`}
36
- >
37
- <CheckCircle size={16} />
38
- </button>
39
- <div>
40
- <p className={`font-bold ${med.taken ? 'text-green-400 line-through' : 'text-white'}`}>{med.name}</p>
41
- <div className="flex flex-col gap-0.5 mt-1">
42
- <span className="text-xs text-gray-400">{med.dosage} {med.time}</span>
43
- {(med.startDate || med.endDate) && (
44
- <span className="text-[10px] text-gray-500 flex items-center gap-1">
45
- <Calendar size={10} /> {med.startDate || 'Now'} → {med.endDate || 'Ongoing'}
46
- </span>
47
- )}
48
- </div>
49
- </div>
50
- </div>
51
- <div className="flex items-center gap-2 opacity-0 group-hover:opacity-100 transition-opacity">
52
- <button
53
- onClick={() => onEdit(med)}
54
- className="p-2 rounded-lg text-gray-400 hover:text-neon-yellow hover:bg-neon-yellow/10 transition-colors"
55
- title="Edit"
56
- >
57
- <Edit2 size={16} />
58
- </button>
59
- <button
60
- onClick={() => onDelete(med.id)}
61
- className="p-2 rounded-lg text-gray-400 hover:text-red-400 hover:bg-red-400/10 transition-colors"
62
- title="Delete"
63
- >
64
- <Trash2 size={16} />
65
- </button>
66
- </div>
67
- </div>
68
- ));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
 
70
- const MedicationTracker: React.FC<MedicationTrackerProps> = ({
71
- medications,
72
- setMedications,
73
- profile,
74
- setProfile
75
  }) => {
76
- const [newMedName, setNewMedName] = useState('');
77
- const [newMedDosage, setNewMedDosage] = useState('');
78
- const [newMedTime, setNewMedTime] = useState('09:00');
79
- const [startDate, setStartDate] = useState('');
80
- const [endDate, setEndDate] = useState('');
81
- // Edit Mode State
82
- const [editingId, setEditingId] = useState<string | null>(null);
 
83
 
84
- const resetForm = () => {
85
- setNewMedName('');
86
- setNewMedDosage('');
87
- setNewMedTime('09:00');
88
- setStartDate('');
89
- setEndDate('');
90
- setEditingId(null);
91
- };
92
 
93
- const handleEdit = useCallback((med: Medication) => {
94
- setNewMedName(med.name);
95
- setNewMedDosage(med.dosage);
96
- setNewMedTime(med.time);
97
- setStartDate(med.startDate || '');
98
- setEndDate(med.endDate || '');
99
- setEditingId(med.id);
100
- }, []);
101
 
102
- const handleUpdate = () => {
103
- if (!editingId || !newMedName || !newMedDosage) return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
 
105
- setMedications(prev => prev.map(med => {
106
- if (med.id === editingId) {
107
- return {
108
- ...med,
109
  name: newMedName,
110
  dosage: newMedDosage,
111
  time: newMedTime,
 
112
  startDate: startDate || undefined,
113
  endDate: endDate || undefined
114
  };
115
- }
116
- return med;
117
- }));
118
- resetForm();
119
- };
120
 
121
- const addMedication = () => {
122
- if (!newMedName || !newMedDosage) return;
 
 
123
 
124
- const newMed: Medication = {
125
- id: Date.now().toString(),
126
- name: newMedName,
127
- dosage: newMedDosage,
128
- time: newMedTime,
129
- taken: false,
130
- startDate: startDate || undefined,
131
- endDate: endDate || undefined
132
- };
133
 
134
- setMedications(prev => [...prev, newMed]);
135
- resetForm();
136
- };
 
 
 
 
 
 
 
 
137
 
138
- const removeMedication = useCallback((id: string) => {
139
- setMedications(prev => prev.filter(m => m.id !== id));
140
- if (editingId === id) resetForm();
141
- }, [editingId, setMedications]);
142
 
143
- const toggleTaken = useCallback((id: string) => {
144
- // 1. Calculate new state immediately
145
- const updatedMeds = medications.map(m => {
146
- if (m.id === id) {
147
- return { ...m, taken: !m.taken };
148
- }
149
- return m;
150
- });
151
 
152
- // 2. Determine Streak Changes
153
- const allTakenNow = updatedMeds.length > 0 && updatedMeds.every(m => m.taken);
154
- const today = new Date();
155
- today.setHours(0,0,0,0);
156
- const todayStr = today.toISOString();
157
- const lastUpdate = new Date(profile.lastStreakUpdate);
158
- lastUpdate.setHours(0,0,0,0);
159
- const lastUpdateStr = lastUpdate.toISOString();
160
- const isToday = today.getTime() === lastUpdate.getTime();
 
 
 
 
 
 
 
 
161
 
162
- let newStreak = profile.streak;
163
- let newLastUpdate = profile.lastStreakUpdate;
 
 
 
 
 
 
 
 
164
 
165
- if (allTakenNow) {
166
- if (!isToday) {
167
- // First time finishing today -> Increment
168
- newStreak += 1;
169
- newLastUpdate = new Date().toISOString(); // store full timestamp for exactness
170
- }
171
- } else {
172
- // Not all taken
173
- if (isToday) {
174
- // If we previously marked today as done, we need to revert it.
175
- // Decrement streak (min 0)
176
- newStreak = Math.max(0, newStreak - 1);
177
- // Set update date to yesterday so if they re-complete it, it counts as "new" for today
178
- const yesterday = new Date(today);
179
- yesterday.setDate(yesterday.getDate() - 1);
180
- newLastUpdate = yesterday.toISOString();
181
- }
182
- }
183
 
184
- // 3. Batch Updates
185
- setMedications(updatedMeds);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
 
187
- if (newStreak !== profile.streak || newLastUpdate !== profile.lastStreakUpdate) {
188
- setProfile(prev => ({
189
- ...prev,
190
- streak: newStreak,
191
- lastStreakUpdate: newLastUpdate
192
- }));
193
- }
194
- }, [medications, profile.streak, profile.lastStreakUpdate, setMedications, setProfile]);
 
 
 
 
 
 
 
 
 
 
 
195
 
196
- const pendingMeds = medications.filter(m => !m.taken).length;
197
- const randomQuote = MOTIVATIONAL_QUOTES[Math.floor(Math.random() * MOTIVATIONAL_QUOTES.length)];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
 
 
 
 
 
 
199
 
200
- return (
201
- <div className="grid grid-cols-1 md:grid-cols-2 gap-8 animate-in fade-in slide-in-from-bottom-4 duration-500">
202
- <div className="space-y-6">
203
- <div className={`glass-panel p-6 rounded-2xl border-t ${editingId ? 'border-neon-yellow' : 'border-neon-blue'} transition-colors`}>
204
- <div className="flex justify-between items-center mb-4">
205
- <h2 className={`text-xl font-bold transition-colors flex items-center gap-2 ${editingId ? 'text-neon-yellow' : 'text-white'}`}>
206
- {editingId ? <Edit2 size={20} /> : <Plus className="text-neon-blue" size={20} />}
207
- {editingId ? 'Edit Medication' : 'Add Medication'}
208
- </h2>
209
- {editingId && (
210
- <button onClick={resetForm} className="text-xs text-gray-400 hover:text-white flex items-center gap-1">
211
- <X size={14} /> Cancel
212
- </button>
213
- )}
214
- </div>
215
-
216
- <div className="grid grid-cols-2 gap-4 mb-4">
217
- <input
218
- value={newMedName} onChange={e => setNewMedName(e.target.value)}
219
- placeholder="Medication Name"
220
- className="bg-black/40 border border-white/10 rounded-lg p-3 text-white text-sm focus:border-neon-blue outline-none col-span-2"
221
- />
222
- <input
223
- value={newMedDosage} onChange={e => setNewMedDosage(e.target.value)}
224
- placeholder="Dosage (e.g. 10mg)"
225
- className="bg-black/40 border border-white/10 rounded-lg p-3 text-white text-sm focus:border-neon-blue outline-none"
226
- />
227
- <input
228
- type="time"
229
- value={newMedTime} onChange={e => setNewMedTime(e.target.value)}
230
- className="bg-black/40 border border-white/10 rounded-lg p-3 text-white text-sm focus:border-neon-blue outline-none"
231
- />
232
-
233
- <div className="col-span-2 grid grid-cols-2 gap-4">
234
- <div>
235
- <label className="text-[10px] text-gray-500 uppercase font-bold mb-1 block">Start Date</label>
236
- <input
237
- type="date"
238
- value={startDate} onChange={e => setStartDate(e.target.value)}
239
- className="w-full bg-black/40 border border-white/10 rounded-lg p-3 text-white text-sm focus:border-neon-blue outline-none"
240
- />
241
- </div>
242
- <div>
243
- <label className="text-[10px] text-gray-500 uppercase font-bold mb-1 block">Finish Date (Opt)</label>
244
- <input
245
- type="date"
246
- value={endDate} onChange={e => setEndDate(e.target.value)}
247
- className="w-full bg-black/40 border border-white/10 rounded-lg p-3 text-white text-sm focus:border-neon-blue outline-none"
248
- />
249
- </div>
250
  </div>
251
- {editingId ? (
252
- <button
253
- onClick={handleUpdate}
254
- disabled={!newMedName}
255
- className="bg-neon-yellow text-black font-bold rounded-lg py-3 hover:bg-neon-yellow/90 transition-colors flex items-center justify-center gap-2 disabled:opacity-50 col-span-2 mt-2 shadow-[0_0_15px_rgba(255,195,0,0.2)]"
256
- >
257
- <Save size={18} /> Update Medication
258
- </button>
 
 
 
259
  ) : (
260
- <button
261
- onClick={addMedication}
262
- disabled={!newMedName}
263
- className="bg-neon-blue text-black font-bold rounded-lg py-3 hover:bg-neon-blue/90 transition-colors flex items-center justify-center gap-2 disabled:opacity-50 col-span-2 mt-2 shadow-[0_0_15px_rgba(0,204,255,0.2)]"
264
- >
265
- <Plus size={18} /> Add to Schedule
266
- </button>
 
 
267
  )}
268
  </div>
 
269
  </div>
270
- <div className="space-y-3">
271
- {medications.map(med => (
272
- <MedicationItem
273
- key={med.id}
274
- med={med}
275
- editingId={editingId}
276
- onToggle={toggleTaken}
277
- onEdit={handleEdit}
278
- onDelete={removeMedication}
279
- />
280
- ))}
281
- {medications.length === 0 && (
282
- <p className="text-center text-gray-500 text-sm py-4 italic">No medications tracked.</p>
283
- )}
284
- </div>
285
- </div>
286
- <div className="space-y-6">
287
- <div className="glass-panel p-8 rounded-2xl text-center relative overflow-hidden flex flex-col items-center justify-center min-h-[300px]">
288
- <div className={`absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-transparent ${profile.streak > 0 ? 'via-yellow-500' : 'via-gray-500'} to-transparent`}></div>
289
-
290
- <Trophy size={64} className={`${profile.streak > 0 ? 'text-yellow-400 drop-shadow-[0_0_10px_rgba(234,179,8,0.5)]' : 'text-gray-700'} mb-4 transition-colors duration-500`} />
291
- <h3 className={`text-4xl font-mono font-bold ${profile.streak > 0 ? 'text-white' : 'text-gray-500'} mb-2`}>{profile.streak} Days</h3>
292
- <p className="text-gray-400/80 font-bold uppercase tracking-widest text-xs">Current Streak</p>
293
-
294
- {profile.streak === 0 && (
295
- <div className="mt-8 bg-white/5 p-4 rounded-xl border border-white/5">
296
- <Quote size={16} className="text-gray-500 mx-auto mb-2" />
297
- <p className="text-sm text-gray-300 italic">"{randomQuote}"</p>
298
- </div>
299
- )}
300
- {profile.streak > 0 && (
301
- <p className="mt-6 text-gray-400 text-sm max-w-xs">
302
- Keep going! You are building a healthier future.
303
- </p>
304
- )}
305
- </div>
306
- {pendingMeds > 0 && (
307
- <div className="glass-card p-4 rounded-xl border-l-4 border-l-neon-red flex items-start gap-4">
308
- <div className="p-2 bg-neon-red/10 rounded-lg text-neon-red">
309
- <AlertTriangle size={24} />
310
- </div>
311
- <div>
312
- <h4 className="font-bold text-white">Missed Doses Pending</h4>
313
- <p className="text-sm text-gray-400 mt-1">You have <span className="text-neon-red font-bold">{pendingMeds}</span> medication(s) pending for today.</p>
314
- </div>
315
- </div>
316
- )}
317
- </div>
318
- </div>
319
- );
320
  };
321
 
322
  export default MedicationTracker;
 
1
  import React, { useState, useCallback } from 'react';
2
+ import { Plus, Trash2, CheckCircle, Trophy, AlertTriangle, Quote, Calendar, Edit2, X, Save, Clock } from 'lucide-react';
3
  import { Medication, PatientProfile } from '../types';
4
 
5
  interface MedicationTrackerProps {
6
+ medications: Medication[];
7
+ setMedications: React.Dispatch<React.SetStateAction<Medication[]>>;
8
+ profile: PatientProfile;
9
+ setProfile: React.Dispatch<React.SetStateAction<PatientProfile>>;
10
  }
11
 
12
  const MOTIVATIONAL_QUOTES = [
13
+ "Success is stumbling from failure to failure with no loss of enthusiasm. – Winston Churchill",
14
+ "Fall seven times, stand up eight. – Japanese Proverb",
15
+ "The only real mistake is the one from which we learn nothing. – Henry Ford",
16
+ "It does not matter how slowly you go as long as you do not stop. – Confucius"
17
  ];
18
 
19
+ // Helper to get local date string YYYY-MM-DD
20
+ const getLocalTodayString = () => {
21
+ const now = new Date();
22
+ const year = now.getFullYear();
23
+ const month = String(now.getMonth() + 1).padStart(2, '0');
24
+ const day = String(now.getDate()).padStart(2, '0');
25
+ return `${year}-${month}-${day}`;
26
+ };
27
+
28
+ // Check if med is active today
29
+ const isDueToday = (med: Medication) => {
30
+ const today = getLocalTodayString();
31
+ // If start date exists and is in the future, it's not due
32
+ if (med.startDate && med.startDate > today) return false;
33
+ // If end date exists and is in the past, it's not due
34
+ if (med.endDate && med.endDate < today) return false;
35
+ return true;
36
+ };
37
+
38
+ // Memoized item
39
+ const MedicationItem = React.memo(({ med, editingId, onToggle, onEdit, onDelete }: {
40
+ med: Medication;
41
+ editingId: string | null;
42
+ onToggle: (id: string) => void;
43
+ onEdit: (med: Medication) => void;
44
+ onDelete: (id: string) => void;
45
+ }) => {
46
+ const isDue = isDueToday(med);
47
+
48
+ return (
49
+ <div className={`glass-card p-4 rounded-xl flex justify-between items-center group transition-all relative
50
+ ${editingId === med.id ? 'border-neon-yellow/50 bg-neon-yellow/5' :
51
+ (med.taken ? 'border-green-500/30 bg-green-500/5' :
52
+ (!isDue ? 'border-white/5 opacity-50 grayscale' : 'border-white/5'))}`}>
53
+
54
+ <div className="flex items-center gap-4">
55
+ <button
56
+ onClick={() => onToggle(med.id)}
57
+ disabled={!isDue}
58
+ className={`w-8 h-8 rounded-full flex items-center justify-center border transition-all ${
59
+ med.taken
60
+ ? 'bg-green-500 border-green-500 text-black'
61
+ : (!isDue ? 'border-gray-700 text-gray-700 cursor-not-allowed' : 'border-gray-500 text-transparent hover:border-green-400')
62
+ }`}
63
+ >
64
+ {med.taken ? <CheckCircle size={16} /> : !isDue ? <Clock size={14}/> : null}
65
+ </button>
66
+ <div>
67
+ <p className={`font-bold ${med.taken ? 'text-green-400 line-through' : 'text-white'}`}>
68
+ {med.name}
69
+ {!isDue && <span className="text-[10px] ml-2 font-normal text-gray-500 bg-white/5 px-1.5 py-0.5 rounded border border-white/5">NOT DUE TODAY</span>}
70
+ </p>
71
+ <div className="flex flex-col gap-0.5 mt-1">
72
+ <span className="text-xs text-gray-400">{med.dosage} • {med.time}</span>
73
+ {(med.startDate || med.endDate) && (
74
+ <span className="text-[10px] text-gray-500 flex items-center gap-1">
75
+ <Calendar size={10} /> {med.startDate || 'Now'} → {med.endDate || 'Ongoing'}
76
+ </span>
77
+ )}
78
+ </div>
79
+ </div>
80
+ </div>
81
+
82
+ <div className="flex items-center gap-2 opacity-0 group-hover:opacity-100 transition-opacity">
83
+ <button
84
+ onClick={() => onEdit(med)}
85
+ className="p-2 rounded-lg text-gray-400 hover:text-neon-yellow hover:bg-neon-yellow/10 transition-colors"
86
+ title="Edit"
87
+ >
88
+ <Edit2 size={16} />
89
+ </button>
90
+ <button
91
+ onClick={() => onDelete(med.id)}
92
+ className="p-2 rounded-lg text-gray-400 hover:text-red-400 hover:bg-red-400/10 transition-colors"
93
+ title="Delete"
94
+ >
95
+ <Trash2 size={16} />
96
+ </button>
97
+ </div>
98
+ </div>
99
+ );
100
+ });
101
 
102
+ const MedicationTracker: React.FC<MedicationTrackerProps> = ({
103
+ medications,
104
+ setMedications,
105
+ profile,
106
+ setProfile
107
  }) => {
108
+ const [newMedName, setNewMedName] = useState('');
109
+ const [newMedDosage, setNewMedDosage] = useState('');
110
+ const [newMedTime, setNewMedTime] = useState('09:00');
111
+ const [startDate, setStartDate] = useState('');
112
+ const [endDate, setEndDate] = useState('');
113
+
114
+ // Edit Mode State
115
+ const [editingId, setEditingId] = useState<string | null>(null);
116
 
117
+ const resetForm = () => {
118
+ setNewMedName('');
119
+ setNewMedDosage('');
120
+ setNewMedTime('09:00');
121
+ setStartDate('');
122
+ setEndDate('');
123
+ setEditingId(null);
124
+ };
125
 
126
+ const handleEdit = useCallback((med: Medication) => {
127
+ setNewMedName(med.name);
128
+ setNewMedDosage(med.dosage);
129
+ setNewMedTime(med.time);
130
+ setStartDate(med.startDate || '');
131
+ setEndDate(med.endDate || '');
132
+ setEditingId(med.id);
133
+ }, []);
134
 
135
+ const handleUpdate = () => {
136
+ if (!editingId || !newMedName || !newMedDosage) return;
137
+
138
+ setMedications(prev => prev.map(med => {
139
+ if (med.id === editingId) {
140
+ return {
141
+ ...med,
142
+ name: newMedName,
143
+ dosage: newMedDosage,
144
+ time: newMedTime,
145
+ startDate: startDate || undefined,
146
+ endDate: endDate || undefined
147
+ };
148
+ }
149
+ return med;
150
+ }));
151
+ resetForm();
152
+ };
153
 
154
+ const addMedication = () => {
155
+ if (!newMedName || !newMedDosage) return;
156
+ const newMed: Medication = {
157
+ id: Date.now().toString(),
158
  name: newMedName,
159
  dosage: newMedDosage,
160
  time: newMedTime,
161
+ taken: false,
162
  startDate: startDate || undefined,
163
  endDate: endDate || undefined
164
  };
165
+ setMedications(prev => [...prev, newMed]);
166
+ resetForm();
167
+ };
 
 
168
 
169
+ const removeMedication = useCallback((id: string) => {
170
+ setMedications(prev => prev.filter(m => m.id !== id));
171
+ if (editingId === id) resetForm();
172
+ }, [editingId, setMedications]);
173
 
174
+ const toggleTaken = useCallback((id: string) => {
175
+ // 1. Calculate new state immediately
176
+ const updatedMeds = medications.map(m => {
177
+ if (m.id === id) {
178
+ return { ...m, taken: !m.taken };
179
+ }
180
+ return m;
181
+ });
 
182
 
183
+ // 2. Determine Streak Changes (LOGIC FIX: Filter ONLY meds active TODAY)
184
+ const todayMeds = updatedMeds.filter(isDueToday);
185
+
186
+ // Only check if active meds are taken. If no meds due today, streak logic is neutral (or paused).
187
+ const allActiveTaken = todayMeds.length > 0 && todayMeds.every(m => m.taken);
188
+
189
+ const today = new Date();
190
+ today.setHours(0,0,0,0);
191
+
192
+ const lastUpdate = new Date(profile.lastStreakUpdate);
193
+ lastUpdate.setHours(0,0,0,0);
194
 
195
+ const isToday = today.getTime() === lastUpdate.getTime();
 
 
 
196
 
197
+ let newStreak = profile.streak;
198
+ let newLastUpdate = profile.lastStreakUpdate;
 
 
 
 
 
 
199
 
200
+ if (allActiveTaken) {
201
+ if (!isToday) {
202
+ // First time finishing today -> Increment
203
+ newStreak += 1;
204
+ newLastUpdate = new Date().toISOString();
205
+ }
206
+ } else {
207
+ // Not all taken
208
+ if (isToday) {
209
+ // previously marked done today, now unchecked -> Revert
210
+ newStreak = Math.max(0, newStreak - 1);
211
+ // Reset date to yesterday to allow re-completion
212
+ const yesterday = new Date(today);
213
+ yesterday.setDate(yesterday.getDate() - 1);
214
+ newLastUpdate = yesterday.toISOString();
215
+ }
216
+ }
217
 
218
+ // 3. Batch Updates
219
+ setMedications(updatedMeds);
220
+ if (newStreak !== profile.streak || newLastUpdate !== profile.lastStreakUpdate) {
221
+ setProfile(prev => ({
222
+ ...prev,
223
+ streak: newStreak,
224
+ lastStreakUpdate: newLastUpdate
225
+ }));
226
+ }
227
+ }, [medications, profile.streak, profile.lastStreakUpdate, setMedications, setProfile]);
228
 
229
+ // LOGIC FIX: Pending count only looks at what is due today
230
+ const pendingMeds = medications.filter(m => isDueToday(m) && !m.taken).length;
231
+ const randomQuote = MOTIVATIONAL_QUOTES[Math.floor(Math.random() * MOTIVATIONAL_QUOTES.length)];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
 
233
+ return (
234
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-8 animate-in fade-in slide-in-from-bottom-4 duration-500">
235
+
236
+ <div className="space-y-6">
237
+ <div className={`glass-panel p-6 rounded-2xl border-t ${editingId ? 'border-neon-yellow' : 'border-neon-blue'} transition-colors`}>
238
+ <div className="flex justify-between items-center mb-4">
239
+ <h2 className={`text-xl font-bold transition-colors flex items-center gap-2 ${editingId ? 'text-neon-yellow' : 'text-white'}`}>
240
+ {editingId ? <Edit2 size={20} /> : <Plus className="text-neon-blue" size={20} />}
241
+ {editingId ? 'Edit Medication' : 'Add Medication'}
242
+ </h2>
243
+ {editingId && (
244
+ <button onClick={resetForm} className="text-xs text-gray-400 hover:text-white flex items-center gap-1">
245
+ <X size={14} /> Cancel
246
+ </button>
247
+ )}
248
+ </div>
249
+
250
+ <div className="grid grid-cols-2 gap-4 mb-4">
251
+ <input
252
+ value={newMedName} onChange={e => setNewMedName(e.target.value)}
253
+ placeholder="Medication Name"
254
+ className="bg-black/40 border border-white/10 rounded-lg p-3 text-white text-sm focus:border-neon-blue outline-none col-span-2"
255
+ />
256
+ <input
257
+ value={newMedDosage} onChange={e => setNewMedDosage(e.target.value)}
258
+ placeholder="Dosage (e.g. 10mg)"
259
+ className="bg-black/40 border border-white/10 rounded-lg p-3 text-white text-sm focus:border-neon-blue outline-none"
260
+ />
261
+ <input
262
+ type="time"
263
+ value={newMedTime} onChange={e => setNewMedTime(e.target.value)}
264
+ className="bg-black/40 border border-white/10 rounded-lg p-3 text-white text-sm focus:border-neon-blue outline-none"
265
+ />
266
+
267
+ <div className="col-span-2 grid grid-cols-2 gap-4">
268
+ <div>
269
+ <label className="text-[10px] text-gray-500 uppercase font-bold mb-1 block">Start Date</label>
270
+ <input
271
+ type="date"
272
+ value={startDate} onChange={e => setStartDate(e.target.value)}
273
+ className="w-full bg-black/40 border border-white/10 rounded-lg p-3 text-white text-sm focus:border-neon-blue outline-none"
274
+ />
275
+ </div>
276
+ <div>
277
+ <label className="text-[10px] text-gray-500 uppercase font-bold mb-1 block">Finish Date (Opt)</label>
278
+ <input
279
+ type="date"
280
+ value={endDate} onChange={e => setEndDate(e.target.value)}
281
+ className="w-full bg-black/40 border border-white/10 rounded-lg p-3 text-white text-sm focus:border-neon-blue outline-none"
282
+ />
283
+ </div>
284
+ </div>
285
 
286
+ {editingId ? (
287
+ <button
288
+ onClick={handleUpdate}
289
+ disabled={!newMedName}
290
+ className="bg-neon-yellow text-black font-bold rounded-lg py-3 hover:bg-neon-yellow/90 transition-colors flex items-center justify-center gap-2 disabled:opacity-50 col-span-2 mt-2 shadow-[0_0_15px_rgba(255,195,0,0.2)]"
291
+ >
292
+ <Save size={18} /> Update Medication
293
+ </button>
294
+ ) : (
295
+ <button
296
+ onClick={addMedication}
297
+ disabled={!newMedName}
298
+ className="bg-neon-blue text-black font-bold rounded-lg py-3 hover:bg-neon-blue/90 transition-colors flex items-center justify-center gap-2 disabled:opacity-50 col-span-2 mt-2 shadow-[0_0_15px_rgba(0,204,255,0.2)]"
299
+ >
300
+ <Plus size={18} /> Add to Schedule
301
+ </button>
302
+ )}
303
+ </div>
304
+ </div>
305
 
306
+ <div className="space-y-3">
307
+ {medications.map(med => (
308
+ <MedicationItem
309
+ key={med.id}
310
+ med={med}
311
+ editingId={editingId}
312
+ onToggle={toggleTaken}
313
+ onEdit={handleEdit}
314
+ onDelete={removeMedication}
315
+ />
316
+ ))}
317
+ {medications.length === 0 && (
318
+ <p className="text-center text-gray-500 text-sm py-4 italic">No medications tracked.</p>
319
+ )}
320
+ </div>
321
+ </div>
322
 
323
+ <div className="space-y-6">
324
+ <div className="glass-panel p-8 rounded-2xl text-center relative overflow-hidden flex flex-col items-center justify-center min-h-[300px]">
325
+ <div className={`absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-transparent ${profile.streak > 0 ? 'via-yellow-500' : 'via-gray-500'} to-transparent`}></div>
326
+
327
+ <Trophy size={64} className={`${profile.streak > 0 ? 'text-yellow-400 drop-shadow-[0_0_10px_rgba(234,179,8,0.5)]' : 'text-gray-700'} mb-4 transition-colors duration-500`} />
328
 
329
+ <h3 className={`text-4xl font-mono font-bold ${profile.streak > 0 ? 'text-white' : 'text-gray-500'} mb-2`}>{profile.streak} Days</h3>
330
+ <p className="text-gray-400/80 font-bold uppercase tracking-widest text-xs">Current Streak</p>
331
+
332
+ {profile.streak === 0 && (
333
+ <div className="mt-8 bg-white/5 p-4 rounded-xl border border-white/5">
334
+ <Quote size={16} className="text-gray-500 mx-auto mb-2" />
335
+ <p className="text-sm text-gray-300 italic">"{randomQuote}"</p>
336
+ </div>
337
+ )}
338
+
339
+ {profile.streak > 0 && (
340
+ <p className="mt-6 text-gray-400 text-sm max-w-xs">
341
+ Keep going! You are building a healthier future.
342
+ </p>
343
+ )}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
344
  </div>
345
+
346
+ {pendingMeds > 0 ? (
347
+ <div className="glass-card p-4 rounded-xl border-l-4 border-l-neon-red flex items-start gap-4">
348
+ <div className="p-2 bg-neon-red/10 rounded-lg text-neon-red">
349
+ <AlertTriangle size={24} />
350
+ </div>
351
+ <div>
352
+ <h4 className="font-bold text-white">Missed Doses Pending</h4>
353
+ <p className="text-sm text-gray-400 mt-1">You have <span className="text-neon-red font-bold">{pendingMeds}</span> medication(s) due today.</p>
354
+ </div>
355
+ </div>
356
  ) : (
357
+ <div className="glass-card p-4 rounded-xl border-l-4 border-l-green-500 flex items-start gap-4">
358
+ <div className="p-2 bg-green-500/10 rounded-lg text-green-500">
359
+ <CheckCircle size={24} />
360
+ </div>
361
+ <div>
362
+ <h4 className="font-bold text-white">All Caught Up!</h4>
363
+ <p className="text-sm text-gray-400 mt-1">You've taken all your medications for today.</p>
364
+ </div>
365
+ </div>
366
  )}
367
  </div>
368
+
369
  </div>
370
+ );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
371
  };
372
 
373
  export default MedicationTracker;