From 52b4b1f05354b412c31571dae541b929f1494a1e Mon Sep 17 00:00:00 2001 From: Dave Date: Thu, 27 Aug 2026 23:42:16 +0200 Subject: [PATCH] feat(boss-dashboard): add windowRange helper with UTC boundaries --- src/lib/server/boss-dashboard-service.test.ts | 30 ++++++++++++ src/lib/server/boss-dashboard-service.ts | 47 +++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 src/lib/server/boss-dashboard-service.test.ts create mode 100644 src/lib/server/boss-dashboard-service.ts diff --git a/src/lib/server/boss-dashboard-service.test.ts b/src/lib/server/boss-dashboard-service.test.ts new file mode 100644 index 0000000..5d63f80 --- /dev/null +++ b/src/lib/server/boss-dashboard-service.test.ts @@ -0,0 +1,30 @@ +import { describe, it, expect } from "vitest"; +import { windowRange } from "./boss-dashboard-service"; + +describe("windowRange", () => { + it("today: start of UTC day to start of next UTC day", () => { + const r = windowRange("today", new Date("2026-08-27T14:32:11.000Z")); + expect(r.rangeStart).toBe("2026-08-27T00:00:00.000Z"); + expect(r.rangeEnd).toBe("2026-08-28T00:00:00.000Z"); + }); + + it("week: Monday 00:00 UTC to next Monday 00:00 UTC", () => { + // 2026-08-27 is a Thursday. Week starts Mon 2026-08-24. + const r = windowRange("week", new Date("2026-08-27T14:32:11.000Z")); + expect(r.rangeStart).toBe("2026-08-24T00:00:00.000Z"); + expect(r.rangeEnd).toBe("2026-08-31T00:00:00.000Z"); + }); + + it("month: first of month to first of next month", () => { + const r = windowRange("month", new Date("2026-08-27T14:32:11.000Z")); + expect(r.rangeStart).toBe("2026-08-01T00:00:00.000Z"); + expect(r.rangeEnd).toBe("2026-09-01T00:00:00.000Z"); + }); + + it("week across month boundary: still anchored to Monday", () => { + // 2026-09-02 is a Wednesday. Week starts Mon 2026-08-31, ends Mon 2026-09-07. + const r = windowRange("week", new Date("2026-09-02T09:00:00.000Z")); + expect(r.rangeStart).toBe("2026-08-31T00:00:00.000Z"); + expect(r.rangeEnd).toBe("2026-09-07T00:00:00.000Z"); + }); +}); diff --git a/src/lib/server/boss-dashboard-service.ts b/src/lib/server/boss-dashboard-service.ts new file mode 100644 index 0000000..f3dd3ed --- /dev/null +++ b/src/lib/server/boss-dashboard-service.ts @@ -0,0 +1,47 @@ +export type BossWindow = "today" | "week" | "month"; + +export interface WindowRange { + rangeStart: string; // ISO + rangeEnd: string; // ISO +} + +/** + * Computes the [start, end) UTC window for a boss-dashboard time tab. + * Today: midnight-to-midnight UTC. + * Week: Monday 00:00 UTC to next Monday 00:00 UTC. + * Month: day-1 00:00 UTC to day-1 of next month. + * + * All UTC because that's how PocketBase `created` timestamps compare. + */ +export function windowRange(window: BossWindow, now: Date = new Date()): WindowRange { + const start = new Date(now); + const end = new Date(now); + + switch (window) { + case "today": { + start.setUTCHours(0, 0, 0, 0); + end.setUTCDate(end.getUTCDate() + 1); + end.setUTCHours(0, 0, 0, 0); + break; + } + case "week": { + // Monday = 1, Sunday = 0. Shift so Monday is start. + const day = start.getUTCDay(); + const diffToMonday = day === 0 ? -6 : 1 - day; + start.setUTCDate(start.getUTCDate() + diffToMonday); + start.setUTCHours(0, 0, 0, 0); + end.setTime(start.getTime()); + end.setUTCDate(end.getUTCDate() + 7); + break; + } + case "month": { + start.setUTCDate(1); + start.setUTCHours(0, 0, 0, 0); + end.setUTCMonth(end.getUTCMonth() + 1, 1); + end.setUTCHours(0, 0, 0, 0); + break; + } + } + + return { rangeStart: start.toISOString(), rangeEnd: end.toISOString() }; +}