fix(workouts): order the list newest-first; show the year on past-year dates

The canonical picker re-sorts its output to startedAt ASC for its rollup callers, so the list route sliced the oldest page and recent workouts never surfaced on a long history — re-establish newest-first after the pick. And a workout from a previous year now shows its year so it is not read as this year's.
This commit is contained in:
Marc-André Bombeck committed 2026-07-17 16:53:06 +02:00
1 parent 546c5c7d8f
commit 05f80ad378
7 files changed
+48 -3

No files matched your search

@@ -147,6 +147,28 @@ describe("GET /api/workouts — canonical dedup", () => {
expect(body.data.meta.droppedDuplicates).toBe(1);
});
it("returns the most recent workouts first", async () => {
// The canonical picker re-sorts its output to startedAt ASC (its rollup
// contract), so the list route must re-establish newest-first AFTER the
// pick — otherwise offset/limit slice the OLDEST page and recent workouts
// never surface. These two never cluster (different day + slot).
const older = makeRow("w-old", "APPLE_HEALTH", "2026-01-01T07:00:00Z");
const newer = makeRow("w-new", "APPLE_HEALTH", "2026-07-01T07:00:00Z");
vi.mocked(prisma.workout.findMany).mockResolvedValueOnce([
newer,
older,
] as never);
const res = await GET(makeRequest());
const body = await res.json();
expect(res.status).toBe(200);
expect(body.data.workouts.map((w: { id: string }) => w.id)).toEqual([
"w-new",
"w-old",
]);
});
it("returns 401 without a session", async () => {
vi.mocked(getSession).mockResolvedValueOnce(null);
const res = await GET(makeRequest());
+11
View File
@@ -143,6 +143,17 @@ async function buildWorkoutsResponse(
userRow?.sourcePriorityJson ?? null,
);
// `pickCanonicalWorkoutRows` re-sorts its output to `startedAt ASC` (its
// documented contract for the rollup callers). This surface is a
// most-recent-first list, so the DB `orderBy startedAt desc` must be
// re-established AFTER the pick — otherwise `offset`/`limit` slice the
// OLDEST page and a user with a long history never sees recent workouts.
canonical.sort((a, b) => {
const delta = (b.startedAt?.getTime() ?? 0) - (a.startedAt?.getTime() ?? 0);
if (delta !== 0) return delta;
return a.id.localeCompare(b.id);
});
const page = canonical.slice(offset, offset + limit).map((row) => ({
id: row.id,
sportType: row.sportType,
+4
View File
@@ -87,10 +87,14 @@ function formatDate(
timeFormat: TimeFormatPreference,
): string {
const d = new Date(iso);
// Show the year whenever the workout is not from the current year, so a
// "Do., 16. Dez." from a previous year is never mistaken for this year's.
const showYear = d.getFullYear() !== new Date().getFullYear();
return getDateTimeFormat(locale, {
weekday: "short",
day: "2-digit",
month: "short",
...(showYear ? { year: "numeric" as const } : {}),
hour: "2-digit",
minute: "2-digit",
...hourCycleOptions(timeFormat),