feat: add checkbox to include consumption from today until planner start date (#98)

- Add 'Include consumption from today until start date' checkbox to planner
- When checked, usage calculation starts from today instead of max(today, startDate)
- Persist checkbox state in localStorage per user
- Add i18n translations (EN + DE)
- Update planner tests to use dynamic future dates
This commit is contained in:
Daniel Volz
2026-02-06 22:01:01 +01:00
committed by GitHub
parent 01deea1fa0
commit 5818dcc00d
6 changed files with 215 additions and 43 deletions
+74 -25
View File
@@ -706,7 +706,16 @@ describe("Integration Tests", () => {
describe("Planner usage calculation", () => {
it("should calculate correct usage for daily medication", async () => {
// Create medication: 2 packs × 3 blisters × 10 pills = 60 pills total
// Schedule: 1 pill daily starting Jan 1
// Schedule: 1 pill daily starting tomorrow (future date)
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
tomorrow.setHours(8, 0, 0, 0);
const intakeStart = tomorrow.toISOString();
const planEnd = new Date(tomorrow);
planEnd.setDate(planEnd.getDate() + 10);
const planEndStr = planEnd.toISOString();
await app.inject({
method: "POST",
url: "/medications",
@@ -716,17 +725,17 @@ describe("Integration Tests", () => {
blistersPerPack: 3,
pillsPerBlister: 10,
looseTablets: 0,
blisters: [{ usage: 1, every: 1, start: "2025-01-01T08:00:00.000Z" }],
blisters: [{ usage: 1, every: 1, start: intakeStart }],
},
});
// Calculate usage for Jan 1-10 (10 days = 10 pills needed)
// Calculate usage for 10 days starting tomorrow
const response = await app.inject({
method: "POST",
url: "/medications/usage",
payload: {
startDate: "2025-01-01T00:00:00.000Z",
endDate: "2025-01-11T00:00:00.000Z", // 10 days
startDate: intakeStart,
endDate: planEndStr, // 10 days
},
});
@@ -735,13 +744,22 @@ describe("Integration Tests", () => {
expect(data).toHaveLength(1);
expect(data[0].medicationName).toBe("Daily Med");
expect(data[0].plannerUsage).toBe(10); // 10 days × 1 pill
// Note: 'enough' depends on current stock after consumption since start date
// Since test runs ~364 days after Jan 1, most pills are consumed
expect(data[0].totalPills).toBe(60); // Current stock is full (no consumption yet)
expect(data[0].enough).toBe(true);
});
it("should detect insufficient stock", async () => {
// Create medication: 1 pack × 1 blister × 5 pills = 5 pills total
// Schedule: 1 pill daily
// Schedule: 1 pill daily starting tomorrow
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
tomorrow.setHours(8, 0, 0, 0);
const intakeStart = tomorrow.toISOString();
const planEnd = new Date(tomorrow);
planEnd.setDate(planEnd.getDate() + 10);
const planEndStr = planEnd.toISOString();
await app.inject({
method: "POST",
url: "/medications",
@@ -751,17 +769,17 @@ describe("Integration Tests", () => {
blistersPerPack: 1,
pillsPerBlister: 5,
looseTablets: 0,
blisters: [{ usage: 1, every: 1, start: "2025-01-01T08:00:00.000Z" }],
blisters: [{ usage: 1, every: 1, start: intakeStart }],
},
});
// Calculate usage for 10 days (needs 10 pills, only have 5 originally)
// Calculate usage for 10 days (needs 10 pills, only have 5)
const response = await app.inject({
method: "POST",
url: "/medications/usage",
payload: {
startDate: "2025-01-01T00:00:00.000Z",
endDate: "2025-01-11T00:00:00.000Z",
startDate: intakeStart,
endDate: planEndStr,
},
});
@@ -773,7 +791,16 @@ describe("Integration Tests", () => {
it("should calculate weekly medication usage correctly", async () => {
// Create medication: 10 pills total
// Schedule: 1 pill every 7 days starting Jan 1
// Schedule: 1 pill every 7 days starting tomorrow
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
tomorrow.setHours(8, 0, 0, 0);
const intakeStart = tomorrow.toISOString();
const planEnd = new Date(tomorrow);
planEnd.setDate(planEnd.getDate() + 35); // 35 days to get 5 weekly doses
const planEndStr = planEnd.toISOString();
await app.inject({
method: "POST",
url: "/medications",
@@ -782,29 +809,42 @@ describe("Integration Tests", () => {
packCount: 1,
blistersPerPack: 1,
pillsPerBlister: 10,
blisters: [{ usage: 1, every: 7, start: "2025-01-01T08:00:00.000Z" }],
blisters: [{ usage: 1, every: 7, start: intakeStart }],
},
});
// Calculate usage for 30 days (should need ~4-5 pills)
// Calculate usage for 35 days (should need 5 pills)
const response = await app.inject({
method: "POST",
url: "/medications/usage",
payload: {
startDate: "2025-01-01T00:00:00.000Z",
endDate: "2025-01-31T00:00:00.000Z", // 30 days
startDate: intakeStart,
endDate: planEndStr,
},
});
expect(response.statusCode).toBe(200);
const data = response.json();
// Jan 1, 8, 15, 22, 29 = 5 doses
// Day 0, 7, 14, 21, 28 = 5 doses
expect(data[0].plannerUsage).toBe(5);
});
it("should handle multiple intake schedules per medication", async () => {
// Create medication with morning and evening doses
// 30 pills total, 1.5 pills per day (1 morning + 0.5 evening)
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
tomorrow.setHours(8, 0, 0, 0);
const morningStart = tomorrow.toISOString();
const eveningStart = new Date(tomorrow);
eveningStart.setHours(20, 0, 0, 0);
const eveningStartStr = eveningStart.toISOString();
const planEnd = new Date(tomorrow);
planEnd.setDate(planEnd.getDate() + 10);
const planEndStr = planEnd.toISOString();
await app.inject({
method: "POST",
url: "/medications",
@@ -814,8 +854,8 @@ describe("Integration Tests", () => {
blistersPerPack: 1,
pillsPerBlister: 30,
blisters: [
{ usage: 1, every: 1, start: "2025-01-01T08:00:00.000Z" }, // Morning: 1 pill
{ usage: 0.5, every: 1, start: "2025-01-01T20:00:00.000Z" }, // Evening: 0.5 pill
{ usage: 1, every: 1, start: morningStart }, // Morning: 1 pill
{ usage: 0.5, every: 1, start: eveningStartStr }, // Evening: 0.5 pill
],
},
});
@@ -825,8 +865,8 @@ describe("Integration Tests", () => {
method: "POST",
url: "/medications/usage",
payload: {
startDate: "2025-01-01T00:00:00.000Z",
endDate: "2025-01-11T00:00:00.000Z",
startDate: morningStart,
endDate: planEndStr,
},
});
@@ -838,6 +878,15 @@ describe("Integration Tests", () => {
it("should calculate correct blisters needed", async () => {
// 10 pills per blister, need 25 pills → need 3 blisters
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
tomorrow.setHours(8, 0, 0, 0);
const intakeStart = tomorrow.toISOString();
const planEnd = new Date(tomorrow);
planEnd.setDate(planEnd.getDate() + 10);
const planEndStr = planEnd.toISOString();
await app.inject({
method: "POST",
url: "/medications",
@@ -846,7 +895,7 @@ describe("Integration Tests", () => {
packCount: 5,
blistersPerPack: 1,
pillsPerBlister: 10,
blisters: [{ usage: 2.5, every: 1, start: "2025-01-01T08:00:00.000Z" }],
blisters: [{ usage: 2.5, every: 1, start: intakeStart }],
},
});
@@ -855,8 +904,8 @@ describe("Integration Tests", () => {
method: "POST",
url: "/medications/usage",
payload: {
startDate: "2025-01-01T00:00:00.000Z",
endDate: "2025-01-11T00:00:00.000Z",
startDate: intakeStart,
endDate: planEndStr,
},
});