mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 21:10:59 +00:00
The page reads one window anchored on today, ninety days back and half a year forward, and the month grid navigates freely past it. A month outside the window came back with no days: the cells still rendered (they come from the month, not the read) and still opened the log sheet, and the write behind it still landed, because the period route is built for back-dating and re-anchors the neighbouring cycles for it. What did not happen is anything visible. Saving an April flow in August closed the sheet onto a month the read never carried, so the entry looked like it had not been made, and entering it again did not help. The grid now reports the month it moved to and gets its own bounded read for it. The anchored window is untouched, because every other surface on the page means today and the route caps a span at 400 days, so a union with a month two years back would simply be refused. A write also says so now. Saving, deleting and both period boundaries raise a confirmation; the sheet's inline strip already named a failure. Between the invisible month and the silent success there was no way to tell a save that worked from one that never happened. The sheet header carries the date it is writing to, and the one-tap boundaries drop the word today when the day is not today. It always wrote to the selected date; saying otherwise made a correction read as a mistake. The gear beside the log button is gone. It was a second door to the tab directly below it and no other page carries one in that corner. - calendar days carry isCycleStart, so the delete dialog can say that removing this day removes the period start with it - window arithmetic is a pure module with its own tests, including the 400-day span the anchor cannot be widened past - integration: a day 111 back is written, is absent from the anchored read, and comes back complete on a window over its month - new strings in all six languages
59 lines
2.4 KiB
TypeScript
59 lines
2.4 KiB
TypeScript
/**
|
|
* The date range the /cycle page reads, and what a month outside it needs.
|
|
*
|
|
* The page reads one anchored window — ninety days back, half a year forward —
|
|
* and that window drives the ring, the forecast panel and the temperature
|
|
* chart, all of which are about today. The month grid is not: it navigates
|
|
* freely, and a month the anchored read does not cover came back with no days
|
|
* at all. The cells still rendered (they come from the month, not the read) and
|
|
* still opened the log sheet, and the write behind it still landed — the period
|
|
* route is built for back-dating and re-anchors the neighbouring cycles for it.
|
|
* What did not happen is anything visible. Save an April flow in August and the
|
|
* sheet closes onto a month the read never carried, so the entry appears not to
|
|
* have been made, and entering it again does not help.
|
|
*
|
|
* So the month grid gets its own read when it steps outside the anchor. A month
|
|
* is a bounded thing to fetch, the anchored window stays exactly as it was for
|
|
* every surface that means "today", and the two grids merge by date.
|
|
*
|
|
* Pure and dateless — the caller supplies both the month and the window.
|
|
*/
|
|
|
|
/** An inclusive `YYYY-MM-DD` range, the shape the calendar read takes. */
|
|
export interface CalendarWindow {
|
|
from: string;
|
|
to: string;
|
|
}
|
|
|
|
/** Zero-padded `YYYY-MM-DD`. */
|
|
function ymd(year: number, month: number, day: number): string {
|
|
return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
|
}
|
|
|
|
/** First and last day of a `YYYY-MM` month. */
|
|
export function monthBounds(month: string): CalendarWindow {
|
|
const [year, monthIndex] = month.split("-").map(Number);
|
|
// Day 0 of the following month is the last day of this one, leap years and all.
|
|
const lastDay = new Date(year, monthIndex, 0).getDate();
|
|
return { from: ymd(year, monthIndex, 1), to: ymd(year, monthIndex, lastDay) };
|
|
}
|
|
|
|
/**
|
|
* The extra window a visible month needs, or null when `anchor` already covers
|
|
* it and the page should read nothing more. Day keys are zero-padded, so a
|
|
* plain string compare is a date compare.
|
|
*/
|
|
export function monthWindowOutside(
|
|
month: string,
|
|
anchor: CalendarWindow,
|
|
): CalendarWindow | null {
|
|
const bounds = monthBounds(month);
|
|
if (bounds.from >= anchor.from && bounds.to <= anchor.to) return null;
|
|
return bounds;
|
|
}
|
|
|
|
/** The `YYYY-MM` a `YYYY-MM-DD` day key belongs to. */
|
|
export function monthOf(date: string): string {
|
|
return date.slice(0, 7);
|
|
}
|