Adds Thumbnailator-based ImageCompressor that resizes uploaded images
to a 400px-wide JPEG preview stored in hero_image_preview. The recipe
list uses the preview instead of the full image URL.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Store hero image as base64 data URI in text column (V023 migration)
- Add file upload UI to RecipeForm with FileReader preview
- Remove isChildFriendly from RecipeCreateRequest (no form field)
- Fix 500 on save: effort values now lowercase, serves/cookTimeMin changed
from primitive short to nullable Integer to survive omitted fields
- Fix empty categories panel: removed stale tagType=category filter
- Group category tags by type with German headings in recipe form
- Split SuggestionResponse.SuggestionRecipe (no image) from SlotRecipe
- Seed 11 HelloFresh recipes with ingredients, steps and tags (V101)
- Add frontend e2e scaffold, specs and dev yml
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace SwapSuggestionList with RecipePicker in both mobile and desktop
swap contexts. RecipePicker now accepts excludeRecipeId, replacingRecipe,
and isDisabled props. Mobile swap sheet also triggers suggestion fetch
via activePickerDate so green/yellow/red score badges appear during swap.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- +server.ts: pass topN=100 so all recipes are scored in one request
- RecipePicker: Empfohlen keeps top 5 with scoreDelta > 0; builds a
scoreMap from all suggestions; shows green/yellow/red delta badge on
every recipe in Alle Rezepte that has a score entry
- Extracted scoreBadge snippet to avoid duplication between sections
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Neutral suggestions (no variety impact) now show "= 0.0 Punkte" in yellow
instead of no badge, making the three states explicit: green (improves),
yellow (neutral), red (worsens).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Avoids floating-point display like 6.199999999999999 by using
score.toFixed(1) in VarietyScoreCard.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Shows the actual score delta (e.g. "↓ -1.5 Punkte") in red instead of a
generic ⚠ Variationskonflikt label, letting users compare the cost of each
recipe to make an informed swap decision.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Button only renders when onremove callback is provided, keeping the
component usable in read-only contexts without the destructive action.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
"when backend returns error" → "when data is undefined (error response
without data)" — documents that the guard is data?.suggestions ?? [],
not error field inspection.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removes the inline interface from RecipePicker.svelte and replaces
any[] in +page.svelte with Suggestion[] — compile-time safety.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cancels the inflight request when activePickerDate changes or picker
closes, preventing stale responses from overwriting suggestions.
Adds page.test.ts covering fetch trigger, suggestion rendering,
and AbortSignal presence.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Defensive null-coalescing prevents crash when suggestion data arrives
without scoreDelta (e.g. stale backend or mismatched schema).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Derives activePickerDate from mobile pickerOpen/selectedDay and desktop
recipe-picker panel state, then uses $effect to fetch /planner?planId&date
on demand — wires suggestions and isLoading into both RecipePicker instances.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Suggestion interface: { recipe, scoreDelta, hasConflict } (no simulatedScore)
- Badge renders from hasConflict directly — no client-side delta computation needed
- New isLoading prop shows skeleton rows while suggestions fetch is in flight
- currentVarietyScore prop removed from component and both call sites follow in next commit
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Unused since the suggestions route was removed (commit 4333dc0).
RecipePicker.test.ts is the active coverage for suggestion rendering.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds excludeRecipeId prop to SwapSuggestionList so the meal being
replaced is not offered as a swap candidate.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- hooks.server.ts: replace type-cast with actual mapping so isPlanner works
- planner page: set min-h/min-w 40px on prev/next/heute week buttons
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix 7px → 11px font-size on section headers in RecipePicker
- Extract shared slotActions.ts with UUID validation for planId/slotId/recipeId
- Load full recipe list in planner page load (was placeholder current-week slots)
- Update planner/+page.svelte to pass data.recipes as allRecipes to RecipePicker
- Update planner and recipes page.server.ts to use shared slot action helpers
- Fix planner page.server tests: add recipes mock for parallel GET load
- Update action tests to use valid UUIDs (were 'plan-1'/'r1' style strings)
- Add validation-path tests for blank/invalid input on all slot actions
- Add tests for recipes/+server.ts GET endpoint (DayPicker week navigation)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Shows undo notification after slot add/replace. Rückgängig button
calls onundo, auto-dismisses after 4s via ondismiss callback.
Also patches test-setup for userEvent + fake timers compatibility.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
C4 sheet content: Empfohlen section with variety delta badges,
Alle Rezepte with client-side search filter. GET /planner endpoint
proxies suggestions to backend for lazy client-side loading.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>