[Debug] Fix Repeat Cadence Timeline Patient View

Date: June 9, 2026

Scope: 6 files changed (+264 / −65 lines) — bug fix + tests


The Problem

Rehab and survey assignments that repeat on a cadence (e.g. “every 4 days”) were showing up on the wrong days in two places:


Patient app — items appeared when they shouldn’t, or disappeared when they should be visible

Plan Builder timeline — recurring bars were drawn on incorrect dates

A concrete example: a PCSS symptom survey assigned to start on June 9 at midnight, with a patient day reset at 3:00 AM, was visible at 2:00 AM — one patient-day too early. It should only appear after the day reset.


Root Causes

1. Backend: patientHandler.ts

The API that builds the patient’s rehabArray had several date-handling issues:


The SQL MOD(...) repeat-cadence calculation applied the day-reset offset incorrectly to the assignment start date

Timestamps from Postgres were parsed with new Date(), which introduced timezone and midnight-boundary drift

No guard when repeat_every_x_days was 0

Items outside their start–end window could still be included

2. Frontend: BarsLayer.tsx (Plan Builder Gantt)

The timeline had an “anchor to today” hack: if today fell inside a bar, repeat segments were shifted so today always looked like an on-cycle day. That broke the rule that cadence must anchor to the assignment start date, not the current date.


Example with start date May 27 and a 4-day repeat — the buggy UI could include June 9; the correct cycle is May 27 → May 31 → June 4 → June 8 → June 12 → …


3. Minor: PCSS toggle in SimpleRtpView.tsx

Enabling PCSS built the end date on the client with new Date() + toISOString(), which is fragile across timezones. Switched to duration_days_override: 7 so the server owns the duration.


The Fix

Backend (patientHandler.ts)

Fetch timestamps as consistent strings via TO_CHAR(...) in SQL

Repeat cadence: MOD(today − start_date, repeat_every_x_days) = 0, anchored to the raw start date (no incorrect day-reset subtraction on start)

Enforce start ≤ today ≤ end before including repeating items

Treat repeat_every_x_days = 0 as 1 via COALESCE(NULLIF(...), 1)

New helpers — assignmentStartDayLabel, assignmentEndDayLabel, patientDayLabelFromNoZTimestamp — respect patient day reset without brittle Date parsing

Verified scenario (PCSS, America/New_York, day reset 3:00 AM):


Time Expected

June 9, 2:00 AM (NY)

Hidden — still previous patient-day

June 9, 9:00 AM (NY)

Visible — assignment day is active

Frontend (BarsLayer.tsx + repeatSegments.ts)

Removed the “anchor to today” logic

Extracted getRepeatSegmentDayOffsets() — segments at 0, N, 2N, 3N… days from assignment start

Added unit tests so segment dates stay locked to the start date

Tests

patientHandler.test.ts — repeat cadence + day-reset edge cases

repeatSegments.test.ts — timeline segments match backend date-level cadence

Takeaway

This wasn’t a one-line fix. Repeat cadence touches SQL filtering, timezone-aware patient-day boundaries, and timeline rendering — and all three had to agree on the same rule:


Cadence is anchored to the assignment start date, with patient day-reset applied consistently everywhere.


backend query logic, frontend visualization, a small PCSS API cleanup, and tests that lock the behavior in. Solid bug fix.

Comments

Popular posts from this blog

Headquarters SWE Internship

Open Source Project - Open Energy Dashboard

Summer Open Source Project - Eclipse PDE