mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
Merge pull request #742 from MBombeck/fix/backfill-taken-at-slot
Record a backfilled dose at its slot's own time
This commit is contained in:
5 files changed
+52
-11
No files matched your search
@@ -2,6 +2,15 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Marking an older missed dose as taken from the intake history now records
|
||||
the dose at the slot's own date and time. Marking Tuesday 09:00 from a
|
||||
Thursday evening used to land on the right slot but stamp the intake with
|
||||
the current moment; the recorded time now matches the claim being made. The
|
||||
optimistic paint had promised this all along, and the skipped-to-taken flip
|
||||
in the same view already behaved this way.
|
||||
|
||||
## [1.37.1] — 2026-08-08
|
||||
|
||||
### Added
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ try {
|
||||
// v1.4.38.4 → v1.4.42. Do not hand-edit; bump `package.json` and rebuild.
|
||||
const CACHE_VERSION =
|
||||
(typeof self !== "undefined" && self.__APP_VERSION__) ||
|
||||
/* @sw-version-fallback */ "v1.37.0";
|
||||
/* @sw-version-fallback */ "v1.37.1";
|
||||
const STATIC_CACHE = `healthlog-static-${CACHE_VERSION}`;
|
||||
const PAGE_CACHE = `healthlog-pages-${CACHE_VERSION}`;
|
||||
// v1.18.6 — read-only data cache for a curated allowlist of safe GET `/api/*`
|
||||
|
||||
@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
applyOptimisticSlotMark,
|
||||
slotMarkRequestBody,
|
||||
complianceFromLedger,
|
||||
formatSlotDelta,
|
||||
groupLedgerByDay,
|
||||
@@ -195,3 +196,23 @@ describe("applyOptimisticSlotMark", () => {
|
||||
expect(next.rows[0].status).toBe("taken_late");
|
||||
});
|
||||
});
|
||||
|
||||
describe("slotMarkRequestBody", () => {
|
||||
const row = { at: "2026-08-04T07:00:00.000Z" };
|
||||
|
||||
it("anchors takenAt on the slot instant, not on now", () => {
|
||||
const body = slotMarkRequestBody(row, "taken");
|
||||
expect(body).toEqual({
|
||||
skipped: false,
|
||||
scheduledFor: "2026-08-04T07:00:00.000Z",
|
||||
takenAt: "2026-08-04T07:00:00.000Z",
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps the skipped arm free of takenAt", () => {
|
||||
expect(slotMarkRequestBody(row, "skipped")).toEqual({
|
||||
skipped: true,
|
||||
scheduledFor: "2026-08-04T07:00:00.000Z",
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -204,3 +204,18 @@ export function applyOptimisticSlotMark(
|
||||
});
|
||||
return { ...payload, rows };
|
||||
}
|
||||
|
||||
/**
|
||||
* Request body for marking a ledger slot from the Verlauf. The taken arm
|
||||
* anchors `takenAt` on the slot instant: marking Tuesday 09:00 from a
|
||||
* Thursday evening records a dose taken Tuesday 09:00, which is the claim
|
||||
* the person is making. The optimistic paint above already promised this.
|
||||
*/
|
||||
export function slotMarkRequestBody(
|
||||
row: Pick<LedgerRow, "at">,
|
||||
action: "taken" | "skipped",
|
||||
): Record<string, unknown> {
|
||||
return action === "skipped"
|
||||
? { skipped: true, scheduledFor: row.at }
|
||||
: { skipped: false, scheduledFor: row.at, takenAt: row.at };
|
||||
}
|
||||
@@ -106,6 +106,7 @@ import {
|
||||
formatSlotDelta,
|
||||
groupLedgerByDay,
|
||||
isSlotActionable,
|
||||
slotMarkRequestBody,
|
||||
type LedgerPayload,
|
||||
type LedgerRow,
|
||||
} from "@/components/medications/dose-history-ledger-compute";
|
||||
@@ -273,18 +274,13 @@ export function DoseHistoryLedger({
|
||||
|
||||
try {
|
||||
// Pin the displayed slot so the server marks THAT dose (the canonical
|
||||
// slot upsert), not a now-snap to the nearest one. The POST returns
|
||||
// the created event; its id drives the Undo affordance (the same
|
||||
// soft-delete route the cards use).
|
||||
// slot upsert), not a now-snap to the nearest one — and anchor
|
||||
// `takenAt` on the same instant, matching the optimistic paint. The
|
||||
// POST returns the created event; its id drives the Undo affordance
|
||||
// (the same soft-delete route the cards use).
|
||||
const created = await apiPost<{ id?: string } | undefined>(
|
||||
`/api/medications/${medicationId}/intake`,
|
||||
action === "skipped"
|
||||
? { skipped: true, scheduledFor: row.at }
|
||||
: {
|
||||
skipped: false,
|
||||
scheduledFor: row.at,
|
||||
takenAt: new Date().toISOString(),
|
||||
},
|
||||
slotMarkRequestBody(row, action),
|
||||
);
|
||||
const eventId: string | undefined = created?.id;
|
||||
toast.success(
|
||||
|
||||
Reference in new issue
Block a user