The recordDose server action was using INSERT to create new dose entries instead of UPDATE to mark existing scheduled doses as taken, creating duplicate records and breaking adherence tracking.
File: app/actions/medication-logs.ts
Changed from INSERT to UPDATE operation:
// BEFORE (WRONG)
const { medicationLogId, wasTaken, takenAt, notes } = validatedFields.data
const { data, error } = await supabase
.from('medication_doses')
.insert({
medication_log_id: medicationLogId,
user_id: user.id,
scheduled_time: takenAt || new Date().toISOString(),
taken_at: wasTaken ? (takenAt || new Date().toISOString()) : null,
was_taken: wasTaken,
notes: notes || null,
} as never)
// AFTER (CORRECT)
const { doseId, wasTaken, takenAt, notes } = validatedFields.data
// UPDATE the existing scheduled dose record
const { data, error } = await supabase
.from('medication_doses')
.update({
was_taken: wasTaken,
taken_at: wasTaken ? (takenAt || new Date().toISOString()) : null,
notes: notes || null,
} as never)
.eq('id', doseId)
.eq('user_id', user.id)File: app/actions/medication-logs.ts
Restored getTodaysPendingDoses() function that was missing:
export async function getTodaysPendingDoses(): Promise<
ActionResponse<Array<MedicationDose & { medication_name: string; dosage: string }>>
>File: lib/validations/medication-logs.ts
// Changed from medicationLogId to doseId
export const doseTrackingSchema = z.object({
doseId: z.string().uuid('Dose ID is required'),
wasTaken: z.boolean(),
takenAt: z.string().datetime().optional(),
notes: z.string().max(200, 'Notes must be less than 200 characters').optional(),
})components/medication/dose-quick-log.tsx- Passdose.idapp/(dashboard)/quick-log/take-all-button.tsx- Passdose.id
✅ Build Successful - No TypeScript errors
✅ All Exports Found - getTodaysPendingDoses is available
✅ Cache Cleared - .next directory removed
✅ recordDose uses UPDATE with doseId
✅ getTodaysPendingDoses function exists
✅ Validation schema uses doseId
✅ All callers pass dose.id
- No Duplicates: Updates existing scheduled doses instead of creating new ones
- Accurate Tracking: Adherence calculations work correctly
- Data Integrity: Maintains relationship between scheduled and taken doses
- Audit Trail: Can see when doses were scheduled vs actually taken
- Follows Best Practices: Uses Supabase UPDATE pattern from Context7 docs
The fix follows Supabase best practices from Context7 MCP server:
- ✅ Use
.update()for modifying existing records - ✅ Use
.eq('id', doseId)to target specific records - ✅ Include security checks with
.eq('user_id', user.id) - ✅ Return updated data with
.select().single() - ✅ Call
revalidatePath()to refresh UI
medication_doses table:
| id | medication_log_id | scheduled_time | was_taken | taken_at |
|----|-------------------|----------------|-----------|----------|
| 1 | med-123 | 08:00 | null | null | ← Scheduled
| 2 | med-123 | 08:15 | true | 08:15 | ← Duplicate!
| 3 | med-123 | 08:20 | true | 08:20 | ← Another duplicate!
medication_doses table:
| id | medication_log_id | scheduled_time | was_taken | taken_at |
|----|-------------------|----------------|-----------|----------|
| 1 | med-123 | 08:00 | true | 08:15 | ← Updated!
- ✅
app/actions/medication-logs.ts- FixedrecordDose, addedgetTodaysPendingDoses - ✅
lib/validations/medication-logs.ts- UpdateddoseTrackingSchema - ✅
components/medication/dose-quick-log.tsx- Passdose.id - ✅
app/(dashboard)/quick-log/take-all-button.tsx- Passdose.id
- ✅ Removed
.nextcache directory - ✅ Removed temporary documentation files
- ✅ Removed backup files
✅ COMPLETE - All fixes applied, build successful, cache cleared
Ready for testing:
- Create a medication (should generate scheduled doses)
- View pending doses in quick-log
- Click "Take" on a dose
- Verify in database:
was_taken= true,taken_athas timestamp - Verify NO duplicate records created
- Check adherence stats calculate correctly
- Test "Skip" functionality
- Test batch "Take All" button