[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
Post a Comment