All files / src/routes/dashboard selectNextWorkout.ts

90% Statements 9/10
70% Branches 7/10
100% Functions 3/3
87.5% Lines 7/8

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];
}