Files
mealprep/frontend/src/routes/(app)/planner/suggestions/+page.server.ts
Marcel Raddatz 7c07bc443b feat(suggestions): C2 — Meal suggestions (variety-aware) (#40)
feat(suggestions): implement C2 meal suggestion screen (Issue #27)

Co-authored-by: Marcel Raddatz <marcel@raddatz.cloud>
Co-committed-by: Marcel Raddatz <marcel@raddatz.cloud>
2026-04-03 11:18:45 +02:00

77 lines
2.6 KiB
TypeScript

import type { PageServerLoad, Actions } from './$types';
import { redirect } from '@sveltejs/kit';
import { apiClient } from '$lib/server/api';
import { getWeekStart } from '$lib/planner/week';
export const load: PageServerLoad = async ({ fetch, url, locals: _locals }) => {
const weekParam = url.searchParams.get('week');
const weekStart = weekParam ?? getWeekStart(new Date());
const selectedDay = url.searchParams.get('day') ?? weekStart;
const api = apiClient(fetch);
// Load the week plan for context (week-so-far display)
const { data: weekPlan, error: weekPlanError } = await api.GET('/v1/week-plans', {
params: { query: { weekStart } }
});
if (weekPlanError || !weekPlan?.id) {
return { weekPlan: null, suggestions: [], selectedDay, weekStart };
}
// Load variety-aware suggestions for the selected day
const { data: suggestionsData } = await api.GET('/v1/week-plans/{id}/suggestions', {
params: { path: { id: weekPlan.id }, query: { slotDate: selectedDay } }
});
// Sort by simulatedScore descending (highest = best variety fit)
const suggestions = (suggestionsData?.suggestions ?? []).sort(
(a: any, b: any) => (b.simulatedScore ?? 0) - (a.simulatedScore ?? 0)
);
return { weekPlan, suggestions, selectedDay, weekStart };
};
export const actions: Actions = {
pickSuggestion: async ({ fetch, request, locals }) => {
// Role guard: only planners may assign meals
if (locals.benutzer?.rolle !== 'planer') {
return { success: false, error: 'Keine Berechtigung.' };
}
const formData = await request.formData();
const planId = formData.get('planId') as string;
const recipeId = formData.get('recipeId') as string;
const slotDate = formData.get('slotDate') as string;
const weekStart = formData.get('weekStart') as string;
// Validate slotDate format
if (!slotDate || !/^\d{4}-\d{2}-\d{2}$/.test(slotDate)) {
return { success: false, error: 'Ungültiges Datum.' };
}
// Validate planId is non-empty
if (!planId) {
return { success: false, error: 'Fehlende Plan-ID.' };
}
// Validate recipeId is UUID-like format
if (!recipeId || !/^[0-9a-f-]{36}$/.test(recipeId)) {
return { success: false, error: 'Ungültige Rezept-ID.' };
}
const api = apiClient(fetch);
const { data, error } = await api.POST('/v1/week-plans/{id}/slots', {
params: { path: { id: planId } },
body: { slotDate, recipeId }
});
if (error || !data) {
return { success: false, error: 'Gericht konnte nicht hinzugefügt werden.' };
}
// Redirect back to the planner after successful pick (spec: "returns to C1")
redirect(303, `/planner?week=${weekStart || slotDate.slice(0, 7) + '-01'}`);
}
};