diff --git a/frontend/src/routes/(app)/recipes/[id]/edit/+page.server.ts b/frontend/src/routes/(app)/recipes/[id]/edit/+page.server.ts
new file mode 100644
index 0000000..aca6c2f
--- /dev/null
+++ b/frontend/src/routes/(app)/recipes/[id]/edit/+page.server.ts
@@ -0,0 +1,89 @@
+import { error, redirect, fail } from '@sveltejs/kit';
+import type { PageServerLoad, Actions } from './$types';
+import { apiClient } from '$lib/server/api';
+
+export const load: PageServerLoad = async ({ fetch, params }) => {
+ const api = apiClient(fetch);
+ const [recipeResult, tagsResult] = await Promise.all([
+ api.GET('/v1/recipes/{id}', { params: { path: { id: params.id } } }),
+ api.GET('/v1/tags', {})
+ ]);
+
+ if (recipeResult.error || !recipeResult.data) {
+ error(404, 'Recipe not found');
+ }
+
+ const recipe = recipeResult.data;
+ const allTags = tagsResult.data ?? [];
+ const categories = allTags
+ .filter((t) => t.tagType === 'category')
+ .map((t) => ({ id: t.id!, name: t.name!, tagType: t.tagType }));
+
+ return {
+ recipe: {
+ id: recipe.id!,
+ name: recipe.name!,
+ serves: recipe.serves,
+ cookTimeMin: recipe.cookTimeMin,
+ effort: recipe.effort,
+ heroImageUrl: recipe.heroImageUrl,
+ ingredients: (recipe.ingredients ?? []).map((ing) => ({
+ name: ing.name ?? '',
+ quantity: ing.quantity ?? 0,
+ unit: ing.unit ?? ''
+ })),
+ steps: (recipe.steps ?? [])
+ .sort((a, b) => (a.stepNumber ?? 0) - (b.stepNumber ?? 0))
+ .map((s) => ({ instruction: s.instruction ?? '' })),
+ tagIds: (recipe.tags ?? []).map((t) => t.id!)
+ },
+ categories
+ };
+};
+
+export const actions: Actions = {
+ update: async ({ request, fetch, params }) => {
+ const formData = await request.formData();
+ const name = formData.get('name') as string;
+ const serves = formData.get('serves');
+ const cookTimeMin = formData.get('cookTimeMin');
+ const effort = formData.get('effort') as string;
+ const ingredientsJson = formData.get('ingredientsJson') as string;
+ const stepsJson = formData.get('stepsJson') as string;
+ const tagIds = formData.getAll('tagIds') as string[];
+
+ if (!name?.trim()) return fail(422, { error: 'Name ist erforderlich' });
+ if (!effort) return fail(422, { error: 'Schwierigkeitsgrad ist erforderlich' });
+ if (!tagIds.length) return fail(422, { error: 'Mindestens eine Kategorie ist erforderlich' });
+
+ const parsedIngredients = JSON.parse(ingredientsJson || '[]');
+ const parsedSteps = JSON.parse(stepsJson || '[]');
+
+ const api = apiClient(fetch);
+ const { error: apiError } = await api.PUT('/v1/recipes/{id}', {
+ params: { path: { id: params.id } },
+ body: {
+ name: name.trim(),
+ serves: serves ? Number(serves) : undefined,
+ cookTimeMin: cookTimeMin ? Number(cookTimeMin) : undefined,
+ effort,
+ ingredients: parsedIngredients
+ .filter((ing: { name: string }) => ing.name?.trim())
+ .map((ing: { name: string; quantity: string; unit: string }, i: number) => ({
+ newIngredientName: ing.name.trim(),
+ quantity: Number(ing.quantity) || 0,
+ unit: ing.unit || '',
+ sortOrder: i
+ })),
+ steps: parsedSteps
+ .filter((s: string) => s?.trim())
+ .map((s: string, i: number) => ({ stepNumber: i + 1, instruction: s.trim() })),
+ tagIds
+ }
+ });
+
+ if (apiError) return fail(500, { error: 'Fehler beim Speichern' });
+
+ redirect(303, '/recipes');
+ }
+};
diff --git a/frontend/src/routes/(app)/recipes/[id]/edit/+page.svelte b/frontend/src/routes/(app)/recipes/[id]/edit/+page.svelte
new file mode 100644
index 0000000..440cb4d
--- /dev/null
+++ b/frontend/src/routes/(app)/recipes/[id]/edit/+page.svelte
@@ -0,0 +1,17 @@
+
+
+