mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 04:50:59 +00:00
Make the checkup due line say the same thing on both screens
The dashboard summary card and the checkups page each worked out "today", "tomorrow", "in N days" and "overdue by N days" from the same due date, and each did it with its own copy of the arithmetic. The page compares calendar days. The card subtracted one instant from the other and divided, so a checkup due Sunday at 01:00 read "tomorrow" when you looked on Friday evening, one due Saturday at 23:59 read "in 2 days" when it was in fact tomorrow, and one due at 09:00 quietly stopped saying "today" as the afternoon wore on. Both now read from one place, so they cannot drift apart again.
This commit is contained in:
1 parent
340d1db6d9
commit
a2497cf21d
3 files changed
+64
-63
No files matched your search
@@ -28,40 +28,15 @@ import { ResponsiveSheet } from "@/components/ui/responsive-sheet";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { MeasurementForm } from "@/components/measurements/measurement-form";
|
||||
import { isScreeningReminderType } from "@/lib/validations/measurement-reminders";
|
||||
import { relativeDueKey } from "@/lib/measurement-reminders/due-day";
|
||||
import {
|
||||
useMeasurementReminders,
|
||||
useMeasurementReminderMutations,
|
||||
type MeasurementReminder,
|
||||
} from "@/hooks/use-measurement-reminders";
|
||||
|
||||
const DAY_MS = 24 * 60 * 60 * 1000;
|
||||
const SUMMARY_LIMIT = 3;
|
||||
|
||||
/**
|
||||
* v1.32.36 — returns a FULLY-QUALIFIED i18n key, not a bare tail. A call site
|
||||
* that interpolates the tail onto the namespace root anchors the
|
||||
* reverse-coverage guard at that bare namespace, which marks every key under
|
||||
* it reachable and turns the guard into a no-op for the whole namespace. The
|
||||
* guard now refuses that shape outright, so the key is built whole here.
|
||||
*/
|
||||
function relativeDueKey(
|
||||
nextDueAt: string | null,
|
||||
now: number,
|
||||
): { key: string; days: number } {
|
||||
if (!nextDueAt) return { key: "measurementReminders.nextDue.none", days: 0 };
|
||||
const deltaDays = Math.round((new Date(nextDueAt).getTime() - now) / DAY_MS);
|
||||
if (deltaDays < 0)
|
||||
return {
|
||||
key: "measurementReminders.overdueByDays",
|
||||
days: Math.abs(deltaDays),
|
||||
};
|
||||
if (deltaDays === 0)
|
||||
return { key: "measurementReminders.nextDue.today", days: 0 };
|
||||
if (deltaDays === 1)
|
||||
return { key: "measurementReminders.nextDue.tomorrow", days: 1 };
|
||||
return { key: "measurementReminders.nextDue.inDays", days: deltaDays };
|
||||
}
|
||||
|
||||
function resolveLabel(
|
||||
reminder: MeasurementReminder,
|
||||
t: (key: string) => string,
|
||||
|
||||
@@ -46,7 +46,7 @@ import {
|
||||
useDisplayTimezone,
|
||||
} from "@/lib/i18n/context";
|
||||
import { relativeCalendarDate } from "@/lib/i18n/relative-time";
|
||||
import { startOfLocalDayInTz } from "@/lib/tz/local-day";
|
||||
import { relativeDueKey } from "@/lib/measurement-reminders/due-day";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { applyOrder, useModuleListPrefs } from "@/lib/module-list-prefs";
|
||||
import { SettingsCardHeader } from "@/components/settings/_card-header";
|
||||
@@ -143,42 +143,6 @@ const CADENCE_CUSTOM = "custom";
|
||||
const CADENCE_RRULE = "rrule";
|
||||
const DAY_MS = 24 * 60 * 60 * 1000;
|
||||
|
||||
/**
|
||||
* v1.32.36 — returns a FULLY-QUALIFIED i18n key, not a bare tail. A call site
|
||||
* that interpolates the tail onto the namespace root anchors the
|
||||
* reverse-coverage guard at that bare namespace, which marks every key under
|
||||
* it reachable and turns the guard into a no-op for the whole namespace. The
|
||||
* guard now refuses that shape outright, so the key is built whole here.
|
||||
*/
|
||||
function relativeDueKey(
|
||||
nextDueAt: string | null,
|
||||
now: number,
|
||||
): { key: string; days: number } {
|
||||
if (!nextDueAt) return { key: "measurementReminders.nextDue.none", days: 0 };
|
||||
const due = new Date(nextDueAt);
|
||||
if (Number.isNaN(due.getTime()))
|
||||
return { key: "measurementReminders.nextDue.none", days: 0 };
|
||||
// v1.18.9 (recon) — compare CALENDAR-day deltas in the user's local zone,
|
||||
// not a rolling-24h delta. A reminder due at 09:00 viewed the same evening
|
||||
// at 20:00 must still read "heute", and one whose due calendar-day is before
|
||||
// today must read "überfällig" — a raw `(due - now) / DAY_MS` round would
|
||||
// mis-bucket both. Floor each instant to its local day start (host-local zone
|
||||
// = the browser user's zone for this client component) before differencing.
|
||||
const dueDay = startOfLocalDayInTz(due, undefined).getTime();
|
||||
const nowDay = startOfLocalDayInTz(new Date(now), undefined).getTime();
|
||||
const deltaDays = Math.round((dueDay - nowDay) / DAY_MS);
|
||||
if (deltaDays < 0)
|
||||
return {
|
||||
key: "measurementReminders.overdueByDays",
|
||||
days: Math.abs(deltaDays),
|
||||
};
|
||||
if (deltaDays === 0)
|
||||
return { key: "measurementReminders.nextDue.today", days: 0 };
|
||||
if (deltaDays === 1)
|
||||
return { key: "measurementReminders.nextDue.tomorrow", days: 1 };
|
||||
return { key: "measurementReminders.nextDue.inDays", days: deltaDays };
|
||||
}
|
||||
|
||||
/**
|
||||
* v1.18.1 — a COACH-minted reminder stores an i18n KEY in `label` (the
|
||||
* cadence preset's `labelKey`), not free prose. Resolve it through `t()`;
|
||||
|
||||
Reference in new issue
Block a user