feat(planner): add sortEasiestFirst utility for J4 swap context
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,8 @@ import {
|
|||||||
weekDays,
|
weekDays,
|
||||||
isToday,
|
isToday,
|
||||||
formatWeekRange,
|
formatWeekRange,
|
||||||
formatDayLabel
|
formatDayLabel,
|
||||||
|
sortEasiestFirst
|
||||||
} from './week';
|
} from './week';
|
||||||
|
|
||||||
describe('getWeekStart', () => {
|
describe('getWeekStart', () => {
|
||||||
@@ -144,3 +145,52 @@ describe('formatDayLabel', () => {
|
|||||||
expect(formatDayLabel('2026-03-30')).toContain(',');
|
expect(formatDayLabel('2026-03-30')).toContain(',');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('sortEasiestFirst', () => {
|
||||||
|
it('sorts easy before medium before hard', () => {
|
||||||
|
const recipes = [
|
||||||
|
{ id: '1', name: 'Hard', effort: 'hard', cookTimeMin: 10 },
|
||||||
|
{ id: '2', name: 'Easy', effort: 'easy', cookTimeMin: 10 },
|
||||||
|
{ id: '3', name: 'Medium', effort: 'medium', cookTimeMin: 10 }
|
||||||
|
];
|
||||||
|
const sorted = sortEasiestFirst(recipes);
|
||||||
|
expect(sorted.map((r) => r.effort)).toEqual(['easy', 'medium', 'hard']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sorts by cookTimeMin ascending within same effort', () => {
|
||||||
|
const recipes = [
|
||||||
|
{ id: '1', name: 'Slow Easy', effort: 'easy', cookTimeMin: 60 },
|
||||||
|
{ id: '2', name: 'Fast Easy', effort: 'easy', cookTimeMin: 15 }
|
||||||
|
];
|
||||||
|
const sorted = sortEasiestFirst(recipes);
|
||||||
|
expect(sorted[0].name).toBe('Fast Easy');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('treats missing effort as after hard', () => {
|
||||||
|
const recipes = [
|
||||||
|
{ id: '1', name: 'No effort', effort: undefined, cookTimeMin: 5 },
|
||||||
|
{ id: '2', name: 'Hard', effort: 'hard', cookTimeMin: 5 }
|
||||||
|
];
|
||||||
|
const sorted = sortEasiestFirst(recipes);
|
||||||
|
expect(sorted[0].effort).toBe('hard');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('treats missing cookTimeMin as after defined values', () => {
|
||||||
|
const recipes = [
|
||||||
|
{ id: '1', name: 'No time', effort: 'easy', cookTimeMin: undefined },
|
||||||
|
{ id: '2', name: 'Has time', effort: 'easy', cookTimeMin: 30 }
|
||||||
|
];
|
||||||
|
const sorted = sortEasiestFirst(recipes);
|
||||||
|
expect(sorted[0].name).toBe('Has time');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not mutate the original array', () => {
|
||||||
|
const recipes = [
|
||||||
|
{ id: '1', name: 'Hard', effort: 'hard', cookTimeMin: 10 },
|
||||||
|
{ id: '2', name: 'Easy', effort: 'easy', cookTimeMin: 10 }
|
||||||
|
];
|
||||||
|
const original = [...recipes];
|
||||||
|
sortEasiestFirst(recipes);
|
||||||
|
expect(recipes[0].effort).toBe(original[0].effort);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -75,6 +75,25 @@ export function isToday(dateStr: string): boolean {
|
|||||||
return dateStr === todayStr;
|
return dateStr === todayStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const EFFORT_ORDER: Record<string, number> = { easy: 0, medium: 1, hard: 2 };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new array of recipes sorted easiest first (effort ASC, cookTimeMin ASC).
|
||||||
|
* Used for the J4 mid-week swap context — different from variety-first sorting in J2.
|
||||||
|
*/
|
||||||
|
export function sortEasiestFirst<T extends { effort?: string | null; cookTimeMin?: number | null }>(
|
||||||
|
recipes: T[]
|
||||||
|
): T[] {
|
||||||
|
return [...recipes].sort((a, b) => {
|
||||||
|
const ea = a.effort != null ? (EFFORT_ORDER[a.effort] ?? 99) : 99;
|
||||||
|
const eb = b.effort != null ? (EFFORT_ORDER[b.effort] ?? 99) : 99;
|
||||||
|
if (ea !== eb) return ea - eb;
|
||||||
|
const ta = a.cookTimeMin ?? Infinity;
|
||||||
|
const tb = b.cookTimeMin ?? Infinity;
|
||||||
|
return ta - tb;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats a week range: "30. Mär – 5. Apr 2026".
|
* Formats a week range: "30. Mär – 5. Apr 2026".
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user