diff --git a/frontend/src/routes/(app)/planner/variety/+page.svelte b/frontend/src/routes/(app)/planner/variety/+page.svelte index 3a3b2f6..1950f57 100644 --- a/frontend/src/routes/(app)/planner/variety/+page.svelte +++ b/frontend/src/routes/(app)/planner/variety/+page.svelte @@ -3,7 +3,7 @@ import ScoreBreakdownList from '$lib/planner/ScoreBreakdownList.svelte'; import VarietyWarningCards from '$lib/planner/VarietyWarningCards.svelte'; import EffortBar from '$lib/planner/EffortBar.svelte'; - import { formatDayLabel } from '$lib/planner/week'; + import { computeSubScores, computeWarnings } from '$lib/planner/variety'; let { data } = $props(); @@ -14,7 +14,7 @@ let score = $derived(varietyScore?.score ?? 0); // Derive effort distribution from week plan slots - let effortCounts = $derived(() => { + let effortCounts = $derived.by(() => { const slots = weekPlan?.slots ?? []; let easy = 0, medium = 0, hard = 0; for (const slot of slots) { @@ -28,65 +28,21 @@ // Derive sub-scores from API data // TODO: replace with API-provided sub-scores once backend supports them. - let subScores = $derived(() => { - const proteinRepeats = (varietyScore?.tagRepeats ?? []).filter( - (t: any) => t.tagType === 'protein' - ).length; - const ingredientOverlapCount = (varietyScore?.ingredientOverlaps ?? []).length; - const { easy, medium, hard } = effortCounts(); - const total = easy + medium + hard; - // Effort balance: ideal is roughly equal split; penalise extreme distributions - const effortBalance = - total === 0 - ? 10 - : Math.round(Math.max(0, 10 - Math.abs(easy - hard) * 1.5)); - - return { - proteinDiversity: Math.max(0, Math.round(10 - proteinRepeats * 2)), - ingredientOverlap: Math.max(0, Math.round(10 - ingredientOverlapCount * 1.5)), - effortBalance: Math.min(10, effortBalance) - }; - }); + let subScores = $derived.by(() => computeSubScores({ + tagRepeats: varietyScore?.tagRepeats ?? [], + ingredientOverlaps: varietyScore?.ingredientOverlaps ?? [], + ...effortCounts + })); // Build warning list from API data - let warnings = $derived(() => { - const result: { title: string; explanation: string }[] = []; - - // Protein repeats - for (const repeat of varietyScore?.tagRepeats ?? []) { - if ((repeat.days?.length ?? 0) > 1) { - const days = (repeat.days ?? []).map((d: string) => d).join(', '); - result.push({ - title: `${repeat.tagName} mehrfach diese Woche`, - explanation: `${days} — erwäge einen Tausch für mehr Protein-Abwechslung.` - }); - } - } - - // Ingredient overlaps - for (const overlap of varietyScore?.ingredientOverlaps ?? []) { - if ((overlap.days?.length ?? 0) > 1) { - const days = (overlap.days ?? []).join(', '); - result.push({ - title: `${overlap.ingredientName} in mehreren Gerichten`, - explanation: `${days} — sorge für Zutaten-Abwechslung.` - }); - } - } - - // Duplicate recipes in plan - for (const name of varietyScore?.duplicatesInPlan ?? []) { - result.push({ - title: `${name} doppelt geplant`, - explanation: 'Dasselbe Rezept erscheint mehrfach — tausche eines aus.' - }); - } - - return result; - }); + let warnings = $derived.by(() => computeWarnings({ + tagRepeats: varietyScore?.tagRepeats ?? [], + ingredientOverlaps: varietyScore?.ingredientOverlaps ?? [], + duplicatesInPlan: varietyScore?.duplicatesInPlan ?? [] + })); // Protein grid: map protein tags to days of the week - let proteinByDay = $derived(() => { + let proteinByDay = $derived.by(() => { const map: Record = {}; for (const repeat of varietyScore?.tagRepeats ?? []) { if (repeat.tagType === 'protein') { @@ -147,16 +103,16 @@

Bewertung im Detail

- + - {#if warnings().length > 0} + {#if warnings.length > 0}

Hinweise

- +
{/if} {/if} @@ -208,7 +164,7 @@

Bewertung im Detail

- + @@ -220,10 +176,10 @@ Protein-Verteilung
- {#each weekDayAbbrs as abbr, i} + {#each weekDayAbbrs as abbr, i (weekDayKeys[i])} {@const key = weekDayKeys[i]} - {@const protein = proteinByDay()[key]} - {@const isRepeated = protein && Object.values(proteinByDay()).filter((p) => p === protein).length > 1} + {@const protein = proteinByDay[key]} + {@const isRepeated = protein && Object.values(proteinByDay).filter((p) => p === protein).length > 1}
{abbr}
Aufwandsverteilung - {#if (effortCounts().easy + effortCounts().medium + effortCounts().hard) > 0} + {#if (effortCounts.easy + effortCounts.medium + effortCounts.hard) > 0} {:else}

@@ -263,12 +219,12 @@

- {#if warnings().length > 0} + {#if warnings.length > 0}

Hinweise

- +
{/if} {/if}