diff --git a/frontend/src/components/SharedSchedule.tsx b/frontend/src/components/SharedSchedule.tsx index 6ec468f..76c1f16 100644 --- a/frontend/src/components/SharedSchedule.tsx +++ b/frontend/src/components/SharedSchedule.tsx @@ -535,6 +535,7 @@ export function SharedSchedule() { ) ); // Count missed doses (not taken AND not dismissed) + // Note: SharedSchedule doesn't have updatedAt info, so we only check dismissed status const missedPastDoses = totalPastDoses.filter((id) => { if (takenDoses.has(id)) return false; // Check if this dose is dismissed diff --git a/frontend/src/context/AppContext.tsx b/frontend/src/context/AppContext.tsx index 813cf7c..4a51fc3 100644 --- a/frontend/src/context/AppContext.tsx +++ b/frontend/src/context/AppContext.tsx @@ -419,12 +419,35 @@ export function AppProvider({ children }: { children: React.ReactNode }) { return doseDateStr <= dismissedUntilDate; }, []); + // Helper to check if a dose was scheduled BEFORE the medication was last updated + // If so, it's from a previous schedule configuration and shouldn't count as "missed" + const isDoseFromPreviousSchedule = useCallback( + (doseId: string, medUpdatedAt: string | number | null | undefined): boolean => { + if (!medUpdatedAt) return false; // No updatedAt means it was never changed, all doses are valid + + // Extract timestamp from dose ID (format: medId-blisterIdx-timestamp or medId-blisterIdx-timestamp-person) + const parts = doseId.split("-"); + if (parts.length < 3) return false; + const doseTimestamp = parseInt(parts[2], 10); + if (Number.isNaN(doseTimestamp)) return false; + + // Convert updatedAt to timestamp + const updatedAtTimestamp = typeof medUpdatedAt === "number" ? medUpdatedAt : new Date(medUpdatedAt).getTime(); + if (Number.isNaN(updatedAtTimestamp)) return false; + + // If the dose was scheduled before the medication was updated, it's from a previous schedule + return doseTimestamp < updatedAtTimestamp; + }, + [] + ); + const missedPastDoseIds = useMemo(() => { const totalPastDoses = pastDays.flatMap((d) => d.meds.flatMap((m) => { - // Find the medication to get its dismissedUntil + // Find the medication to get its dismissedUntil and updatedAt const med = medications.meds.find((med) => med.name === m.medName); const dismissedUntilDate = med?.dismissedUntil ?? undefined; + const medUpdatedAt = med?.updatedAt; return m.doses.flatMap((dose) => { // Check if this dose is on or before the dismissed date for this medication @@ -432,13 +455,19 @@ export function AppProvider({ children }: { children: React.ReactNode }) { return []; } + // Check if this dose is from a previous schedule configuration + // (scheduled before the medication was last updated) + if (isDoseFromPreviousSchedule(dose.id, medUpdatedAt)) { + return []; + } + return (dose.takenBy || []).length > 0 ? dose.takenBy.map((p: string) => `${dose.id}-${p}`) : [dose.id]; }); }) ); // Also filter out doses that are marked as taken or individually dismissed (legacy) return totalPastDoses.filter((id) => !doses.takenDoses.has(id) && !doses.dismissedDoses.has(id)); - }, [pastDays, medications.meds, doses.takenDoses, doses.dismissedDoses, isDoseDismissed]); + }, [pastDays, medications.meds, doses.takenDoses, doses.dismissedDoses, isDoseDismissed, isDoseFromPreviousSchedule]); // Modal helpers with browser history support const openMedDetail = useCallback( diff --git a/frontend/src/pages/SchedulePage.tsx b/frontend/src/pages/SchedulePage.tsx index 6f2012c..6b6c0bb 100644 --- a/frontend/src/pages/SchedulePage.tsx +++ b/frontend/src/pages/SchedulePage.tsx @@ -63,6 +63,7 @@ export function SchedulePage() { manuallyExpandedDays, toggleDayCollapse, openUserFilter, + missedPastDoseIds, } = useAppContext(); return ( @@ -88,17 +89,11 @@ export function SchedulePage() { {/* Past days toggle */} {pastDays.length > 0 && (() => { - const totalPastDoses = pastDays.flatMap((d) => - d.meds.flatMap((m) => - m.doses.flatMap((dose) => - (dose.takenBy || []).length > 0 ? dose.takenBy.map((p) => `${dose.id}-${p}`) : [dose.id] - ) - ) - ); - const missedPastDoses = totalPastDoses.filter((id) => !takenDoses.has(id)).length; + // Use context's missedPastDoseIds which handles dismissed doses and previous schedule detection + const missedCount = missedPastDoseIds.length; return (