91 lines
3.9 KiB
Markdown
91 lines
3.9 KiB
Markdown
---
|
|
phase: 06-ux-polish
|
|
plan: "01"
|
|
subsystem: pwa/lib
|
|
tags: [tdd, date-math, event-form, d-04]
|
|
dependency_graph:
|
|
requires: []
|
|
provides:
|
|
- computeNewTimedEnd (apps/pwa/src/lib/eventDateTime.ts)
|
|
- computeNewAllDayEnd (apps/pwa/src/lib/eventDateTime.ts)
|
|
affects:
|
|
- Plan 06-06 (EventForm wires these helpers into onChange handlers)
|
|
tech_stack:
|
|
added: []
|
|
patterns:
|
|
- Local Date accessors (WR-05): getFullYear/getMonth/getDate/getHours/getMinutes — never toISOString().slice
|
|
- TDD RED→GREEN with Vitest in apps/pwa
|
|
key_files:
|
|
created: []
|
|
modified:
|
|
- apps/pwa/src/lib/eventDateTime.ts
|
|
- apps/pwa/src/lib/eventDateTime.test.ts
|
|
decisions:
|
|
- WR-05 enforced: all new date formatting uses local accessors; toISOString().slice banned for date strings
|
|
- Private helpers (localDateISO, localTimeHHMM, dateDiffDays, addDaysISO) added to eventDateTime.ts to support the two exports
|
|
metrics:
|
|
duration_minutes: 2
|
|
completed_date: "2026-06-10"
|
|
tasks_completed: 2
|
|
files_changed: 2
|
|
---
|
|
|
|
# Phase 06 Plan 01: End-Tracking Duration Math (D-04) Summary
|
|
|
|
**One-liner:** Pure duration-preservation helpers (`computeNewTimedEnd` + `computeNewAllDayEnd`) with 1h/same-day floor rules, TDD RED→GREEN in eventDateTime.ts.
|
|
|
|
## Tasks Completed
|
|
|
|
| Task | Name | Commit | Files |
|
|
|------|------|--------|-------|
|
|
| 1 (RED) | Failing tests for computeNewTimedEnd / computeNewAllDayEnd | `16cdbf3` | eventDateTime.test.ts |
|
|
| 2 (GREEN) | Implement the two end-tracking helpers | `605f543` | eventDateTime.ts |
|
|
|
|
## What Was Built
|
|
|
|
Two exported pure functions added to `apps/pwa/src/lib/eventDateTime.ts`:
|
|
|
|
- **`computeNewTimedEnd(newStartDate, newStartTime, oldStartDate, oldStartTime, oldEndDate, oldEndTime)`** — preserves a timed event's duration when the start moves. Returns `{ endDate: 'YYYY-MM-DD', endTime: 'HH:MM' }`. If the old end was already behind the old start (stale state), floors to newStart + 1 hour.
|
|
- **`computeNewAllDayEnd(newStartDate, oldStartDate, oldEndDate)`** — preserves an all-day event's inclusive day-span when the start moves. Returns `'YYYY-MM-DD'`. If the old span was negative (stale state), floors to 0 days (same day as newStart).
|
|
|
|
Four private helpers added to the same file: `localDateISO`, `localTimeHHMM`, `dateDiffDays`, `addDaysISO`. All use local Date accessors (WR-05 compliance).
|
|
|
|
## Test Coverage
|
|
|
|
Six new unit tests in `apps/pwa/src/lib/eventDateTime.test.ts`:
|
|
|
|
| Test | Behavior |
|
|
|------|----------|
|
|
| preserves a 1-hour timed delta | old 09:00→10:00; new start 11:00 → new end 12:00 |
|
|
| preserves a multi-day timed delta (26h) | old 08:00→+26h; new start same offset → correct |
|
|
| floors to 1h when old end was behind start | stale end → snaps to newStart+1h |
|
|
| preserves a 0-day span (single day all-day) | oldStart=oldEnd → newEnd=newStart |
|
|
| preserves a 3-day span | newEnd = newStart + 3 days |
|
|
| floors to same day when old end behind start | negative span → 0 → same day |
|
|
|
|
Full suite: **166/166 tests pass**. Pre-existing `serializeEventDateTime` / `localWallClockToUtcIso` tests unaffected.
|
|
|
|
## TDD Gate Compliance
|
|
|
|
- RED commit (`test(06-01): ...`): `16cdbf3` — 6 tests failing with `TypeError: computeNewTimedEnd is not a function`
|
|
- GREEN commit (`feat(06-01): ...`): `605f543` — all 166 tests pass
|
|
- RED→GREEN order confirmed via `git log`
|
|
|
|
## Deviations from Plan
|
|
|
|
None — plan executed exactly as written.
|
|
|
|
## Threat Flags
|
|
|
|
None. Pure client-side date arithmetic on already-trusted local form state. No network boundary, no new attack surface. T-06-01 accepted per threat model.
|
|
|
|
## Self-Check: PASSED
|
|
|
|
- `apps/pwa/src/lib/eventDateTime.ts` — FOUND (modified)
|
|
- `apps/pwa/src/lib/eventDateTime.test.ts` — FOUND (modified)
|
|
- Commit `16cdbf3` — FOUND (RED: test(06-01))
|
|
- Commit `605f543` — FOUND (GREEN: feat(06-01))
|
|
- `computeNewTimedEnd` export — FOUND in eventDateTime.ts
|
|
- `computeNewAllDayEnd` export — FOUND in eventDateTime.ts
|
|
- No `toISOString().slice` in helper code — CONFIRMED
|