Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 14x 11x 5x 4x 3x 1x 1x | /**
* Shape of a workout row surfaced to the dashboard next-workout affordance.
* Mirrors the `+page.server.ts` select projection (issue #261).
*/
export interface UpcomingWorkout {
id: string;
scheduled_date: string; // 'YYYY-MM-DD'
session_type: string;
status: string;
location: { name: string } | null;
}
/**
* Select the next actionable workout to link from the dashboard.
*
* Per spec/programming.allium (`surface Dashboard` @guidance) the affordance
* targets the earliest workout whose `status === 'ready'`, ordered by
* `scheduled_date` ASC and tiebroken by `id` ASC. `scheduled` workouts are
* upcoming context only — linking one is a dead-end until MarkWorkoutReady
* promotes it — so they are not eligible. Returns `null` when no `ready`
* workout exists (the caller renders "no upcoming workout" copy).
*
* Pure: no `Date.now()`, no mutation of the input. "Today" is not computed —
* the DB query already scopes the rows; this re-applies the rule so it is
* independently unit-testable.
*/
export function selectNextWorkout(workouts: UpcomingWorkout[]): UpcomingWorkout | null {
const ready = workouts.filter((w) => w.status === 'ready');
if (ready.length === 0) return null;
return [...ready].sort((a, b) => {
if (a.scheduled_date !== b.scheduled_date) {
return a.scheduled_date < b.scheduled_date ? -1 : 1;
}
Eif (a.id !== b.id) {
return a.id < b.id ? -1 : 1;
}
return 0;
})[0];
}
|