import { describe, expect, it } from "vitest"; import type { PatientSummary, UrgentAlert } from "../src/api/types"; import { checkInTab, clinicHoursLabel, dosesThisWeek, greeting, longDate, needsAttention, needsYou, personLine, recentCheckIns, summaryLine, todayStats, } from "../src/logic/today"; const quiet = { urgent: false, unreadNotes: 0, newSinceLastCheck: false }; const patient = (name: string, change: Partial = {}): PatientSummary => ({ id: name, kind: "connected", name, age: 60, pronoun: null, sharing: { status: "connected", sharedBy: [name.split(" ")[0]], scopes: ["medicines", "doses", "check_ins", "records"], allowsCheckInCalls: true, withheldSome: false, changedAt: "2026-10-30T09:00:00Z", }, medicines: { count: 2 }, doses: { taken: 12, expected: 14, onTime: 12 }, lastCheckIn: null, attention: quiet, lastActivityAt: "2026-10-29T09:00:00Z", ...change, }); const alert = (patientId: string, at: string, text = "Short of breath"): UrgentAlert => ({ id: `${patientId}-${at}`, checkInId: `call-${at}`, patientId, patientName: patientId, noteId: null, text, at, acknowledgedAt: null, }); const now = new Date("2026-10-31T10:00:00"); describe("the greeting", () => { it("says the part of the day and the date in words", () => { expect(greeting(new Date("2026-10-31T08:00:00"))).toBe("Good morning"); expect(greeting(new Date("2026-10-31T14:00:00"))).toBe("Good afternoon"); expect(greeting(new Date("2026-10-31T19:30:00"))).toBe("Good evening"); expect(longDate(now)).toBe("Saturday 31 October"); }); it("names only the age and pronouns the patient stated", () => { expect(personLine({ age: 64, pronoun: "she" })).toBe("64 years · she/her"); expect(personLine({ age: null, pronoun: null })).toBe(""); expect(personLine({ age: 41, pronoun: null })).toBe("41 years"); }); }); describe("Needs you", () => { const list = [ patient("Quiet One"), patient("New Info", { attention: { ...quiet, newSinceLastCheck: true }, sharing: { ...patient("x").sharing!, sharedBy: ["Funke"] } }), patient("Unread Notes", { attention: { ...quiet, unreadNotes: 2 }, lastCheckIn: { at: "2026-10-30T09:40:00", kind: "yasmar_call", status: "completed" } }), patient("Urgent Flag", { attention: { ...quiet, urgent: true, unreadNotes: 1 } }), ]; it("puts one card per patient in PAT-2 order and leaves quiet patients out", () => { const todos = needsYou(list, [], now); expect(todos.map((t) => [t.kind, t.patientName])).toEqual([ ["urgent", "Urgent Flag"], ["notes", "Unread Notes"], ["new", "New Info"], ]); expect(todos[1]).toMatchObject({ title: "2 unread notes · Unread Notes", detail: "From the Yasmar call · Yesterday", tab: "check_ins" }); expect(todos[2]).toMatchObject({ title: "New information · New Info", detail: "Shared by Funke since your last check.", tab: "check" }); expect(list.filter(needsAttention).map((p) => p.name)).toEqual(["New Info", "Unread Notes", "Urgent Flag"]); }); it("links an urgent card to the latest urgent note's call, even for a patient not in the list", () => { const todos = needsYou(list, [alert("Urgent Flag", "2026-10-31T08:00:00Z", "Older"), alert("Urgent Flag", "2026-10-31T09:42:00Z", "Ankles swollen"), alert("Someone Else", "2026-10-31T07:00:00Z")], now); expect(todos[0]).toMatchObject({ title: "2 urgent notes · Urgent Flag", detail: "Ankles swollen", checkInId: "call-2026-10-31T09:42:00Z", count: 2 }); expect(todos.filter((t) => t.kind === "urgent").map((t) => t.patientName)).toEqual(["Urgent Flag", "Someone Else"]); }); it("says what is waiting in one plain line", () => { const todos = needsYou(list, [alert("Urgent Flag", "2026-10-31T09:42:00Z")], now); expect(summaryLine(todos, [{ id: "inv" }])).toBe("1 urgent check-in note, unread notes for 1 patient, new information for 1 patient and 1 invitation to answer."); expect(summaryLine([], [])).toBe("Nothing needs you right now."); expect(summaryLine(needsYou([list[2], patient("More Notes", { attention: { ...quiet, unreadNotes: 1 } })], [], now), [])).toBe("Unread notes for 2 patients."); }); }); describe("doses and check-ins", () => { it("lists patients who share doses, lowest share taken first", () => { const rows = dosesThisWeek([ patient("Good Week", { doses: { taken: 14, expected: 14, onTime: 14 } }), patient("Hard Week", { doses: { taken: 3, expected: 14, onTime: 2 } }), patient("None Due", { doses: { taken: 0, expected: 0, onTime: 0 } }), patient("Paused", { sharing: { ...patient("x").sharing!, status: "paused" } }), patient("Not Shared", { sharing: { ...patient("x").sharing!, scopes: ["medicines"] } }), patient("Clinic Added", { kind: "clinic_added", sharing: null }), ]); expect(rows.map((r) => [r.patient.name, r.taken, r.expected])).toEqual([ ["Hard Week", 3, 14], ["Good Week", 14, 14], ]); }); it("shows the latest check-ins first and hides them while sharing is paused or stopped", () => { const at = (iso: string, kind: "yasmar_call" | "daily" = "yasmar_call") => ({ lastCheckIn: { at: iso, kind, status: kind === "daily" ? null : "completed" } }); const list = [ patient("Older", at("2026-10-29T09:00:00")), patient("Newest", at("2026-10-31T09:00:00", "daily")), patient("Paused", { ...at("2026-10-31T09:30:00"), sharing: { ...patient("x").sharing!, status: "paused" } }), patient("No Call"), ]; const recent = recentCheckIns(list); expect(recent.map((p) => p.name)).toEqual(["Newest", "Older"]); expect(checkInTab(recent[0])).toBe("shared"); expect(checkInTab(recent[1])).toBe("check_ins"); expect(todayStats(list, now)).toEqual({ total: 4, sharing: 3, checkedInToday: 1, unreadNotes: 0 }); }); }); describe("clinic hours in the clinic's clock", () => { const hours = [ { day: 6, open: "08:00", close: "13:00" }, { day: 6, open: "15:00", close: "18:00" }, { day: 5, open: "22:00", close: "06:00" }, ]; const lagos = 60; it("says when the clinic closes, or when it opens next", () => { expect(clinicHoursLabel(hours, new Date("2026-10-31T09:00:00Z"), lagos)).toEqual({ open: true, label: "Open until 13:00" }); expect(clinicHoursLabel(hours, new Date("2026-10-31T12:30:00Z"), lagos)).toEqual({ open: false, label: "Opens at 15:00" }); expect(clinicHoursLabel(hours, new Date("2026-10-31T18:00:00Z"), lagos)).toEqual({ open: false, label: "Closed now" }); }); it("keeps a night span open past midnight", () => { expect(clinicHoursLabel(hours, new Date("2026-10-31T03:00:00Z"), lagos)).toEqual({ open: true, label: "Open until 06:00" }); }); });