From f0bbb3b0091df3a0a2588fee798c37be4e2db0e0 Mon Sep 17 00:00:00 2001 From: Marcel Raddatz Date: Thu, 9 Apr 2026 10:38:30 +0200 Subject: [PATCH] fix(planner): exclude current recipe from swap suggestions Adds excludeRecipeId prop to SwapSuggestionList so the meal being replaced is not offered as a swap candidate. Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/lib/planner/SwapSuggestionList.svelte | 10 ++++++++-- .../src/lib/planner/SwapSuggestionList.test.ts | 14 ++++++++++++++ frontend/src/routes/(app)/planner/+page.svelte | 2 ++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/frontend/src/lib/planner/SwapSuggestionList.svelte b/frontend/src/lib/planner/SwapSuggestionList.svelte index 3a928e9..98ee619 100644 --- a/frontend/src/lib/planner/SwapSuggestionList.svelte +++ b/frontend/src/lib/planner/SwapSuggestionList.svelte @@ -11,6 +11,7 @@ replacingMeta, recipes, currentWeekRecipeIds, + excludeRecipeId, isLoading = false, onpick, oncancel @@ -19,11 +20,16 @@ replacingMeta?: string; recipes: Recipe[]; currentWeekRecipeIds: Set; + excludeRecipeId?: string; isLoading?: boolean; onpick: (recipeId: string, recipeName: string) => void; oncancel?: () => void; } = $props(); + let visibleRecipes = $derived( + excludeRecipeId ? recipes.filter((r) => r.id !== excludeRecipeId) : recipes + ); + function recipeMeta(recipe: Recipe): string { return [ recipe.cookTimeMin != null ? `${recipe.cookTimeMin} min` : null, @@ -60,7 +66,7 @@

-{#if recipes.length === 0} +{#if visibleRecipes.length === 0}

{:else} - {#each recipes as recipe (recipe.id)} + {#each visibleRecipes as recipe (recipe.id)} {@const meta = recipeMeta(recipe)} {@const alreadyPlanned = currentWeekRecipeIds.has(recipe.id)}

{ expect(screen.getByTestId('swap-empty-state')).toBeTruthy(); }); + it('excludes the recipe being replaced when excludeRecipeId is provided', () => { + render(SwapSuggestionList, { props: { ...baseProps, excludeRecipeId: 'r2' } }); + expect(screen.queryByText('Chicken stir-fry')).toBeNull(); + expect(screen.getByText('Quick carbonara')).toBeTruthy(); + expect(screen.getByText('Mushroom risotto')).toBeTruthy(); + }); + + it('shows all recipes when excludeRecipeId is not provided', () => { + render(SwapSuggestionList, { props: baseProps }); + expect(screen.getByText('Quick carbonara')).toBeTruthy(); + expect(screen.getByText('Chicken stir-fry')).toBeTruthy(); + expect(screen.getByText('Mushroom risotto')).toBeTruthy(); + }); + it('disables all Wählen buttons when isLoading is true', () => { render(SwapSuggestionList, { props: { ...baseProps, isLoading: true } }); const buttons = screen.getAllByRole('button', { name: /Wählen/i }); diff --git a/frontend/src/routes/(app)/planner/+page.svelte b/frontend/src/routes/(app)/planner/+page.svelte index 769e219..ba03be8 100644 --- a/frontend/src/routes/(app)/planner/+page.svelte +++ b/frontend/src/routes/(app)/planner/+page.svelte @@ -309,6 +309,7 @@ replacingMeta={replacingMeta || undefined} recipes={sortedRecipes} {currentWeekRecipeIds} + excludeRecipeId={selectedSlot.recipe?.id} isLoading={swapLoading} onpick={handleSwapPick} oncancel={() => (swapSheetOpen = false)} @@ -549,6 +550,7 @@ replacingMeta={replacingMeta || undefined} recipes={sortedRecipes} {currentWeekRecipeIds} + excludeRecipeId={pickerSlot.recipe.id} onpick={handleRecipePick} />