mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-15 06:52:04 +00:00
Replace the day, timeline and life-event stubs with their implementations (#613) and drop the not-implemented helper, its error code and its OpenAPI response with the last stub. loadDay reads one local day for every reader: the readings live inside the record's own day window (never the UTC rollups), through the canonical-source engine the mood sheet already uses (resolveDayReadings in linked-context.ts), with the person's usual range over the 30 days before; the record sections from their own tables, each in the storage form it speaks (an instant in the local window, a date string with its own zone, a calendar date). A switched-off module's section is not read; a section the grant does not cover is named not_shared and not read. The day, the day index and the new visit preparation (GET /api/day/notable) are admitted on the readings and narrowed per section through actingDomainVisibility, so a scoped grant sees exactly its sections. The timeline and its readiness stay whole-record reads and narrow their lanes the same way. Notable days are deterministic keys: a three-month high or low with history behind it, a first reading, and in the visit preparation a regular reading that stopped. The timeline lays out spans and points from the owning tables, standing items without a date, and series by zoom: months from the rollup tier inside its five-year window and from one cached live query before it. The readiness inventory rates each lane with one link per gap. Life events are created, edited and soft-deleted as MANAGE verbs with audit rows, encrypted at rest, and join the sharing guard and the handler matrix. get_day reads a day for the Coach and the MCP endpoint without life events, notes or document names; its step carries the date it read.
61 lines
2.2 KiB
TypeScript
61 lines
2.2 KiB
TypeScript
/**
|
|
* `GET /api/day/notable?from=&to=` — what changed and what stood out in a
|
|
* window, for the preparation of the next visit (v1.42, #613). Without
|
|
* `from` the window starts at the last completed visit the caller may see;
|
|
* without `to` it ends today. The shape is `DayNotableWindowResponse` in
|
|
* `src/lib/day/contract.ts`.
|
|
*
|
|
* Observations and changes only, as keys and the record's own names: no
|
|
* value judgement, no cause. Same admission and section narrowing as the day.
|
|
*/
|
|
import { apiHandler, requireRecordAuth } from "@/lib/api-handler";
|
|
import { apiSuccess, returnAllZodIssues } from "@/lib/api-response";
|
|
import { resolveDayAccess } from "@/lib/day/sections";
|
|
import { loadSinceLastVisit } from "@/lib/day/visit-preparation";
|
|
import { dayNotableQuerySchema } from "@/lib/day/wire-schemas";
|
|
import { prisma } from "@/lib/db";
|
|
import { annotate } from "@/lib/logging/context";
|
|
import { actingDomainVisibility } from "@/lib/sharing/acting-domains";
|
|
import { userDayKey } from "@/lib/tz/format";
|
|
import { resolveUserTimezone } from "@/lib/tz/resolver";
|
|
|
|
export const GET = apiHandler(async (request: Request) => {
|
|
const { user, grantId } = await requireRecordAuth("read", "measurements");
|
|
const params = new URL(request.url).searchParams;
|
|
const parsed = dayNotableQuerySchema.safeParse({
|
|
from: params.get("from") ?? undefined,
|
|
to: params.get("to") ?? undefined,
|
|
});
|
|
if (!parsed.success) {
|
|
return returnAllZodIssues(parsed.error, 422, { errorCode: "day.invalid" });
|
|
}
|
|
const [access, tz] = await Promise.all([
|
|
actingDomainVisibility(prisma, grantId).then((domainVisible) =>
|
|
resolveDayAccess({
|
|
recordId: user.id,
|
|
domainVisible,
|
|
owner: grantId === null,
|
|
}),
|
|
),
|
|
resolveUserTimezone(user.id),
|
|
]);
|
|
const window = await loadSinceLastVisit({
|
|
recordId: user.id,
|
|
access,
|
|
tz,
|
|
today: userDayKey(new Date(), tz),
|
|
from: parsed.data.from,
|
|
to: parsed.data.to,
|
|
});
|
|
annotate({
|
|
action: { name: "day.notable.read" },
|
|
meta: {
|
|
anchor: window.anchor,
|
|
observations: window.observations.length,
|
|
changes: window.changes.length,
|
|
delegated: grantId !== null,
|
|
},
|
|
});
|
|
return apiSuccess(window);
|
|
});
|