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>
77 lines
2.6 KiB
TypeScript
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'}`);
|
|
}
|
|
};
|