feat(recipe): Edit-Modus für Zutaten, Schritte und Meta
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 1m18s
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 1m18s
Auf der Rezept-Detail-Seite ein neuer „Bearbeiten"-Button (Pencil-Icon) in der Action-Bar. Klick schaltet RecipeView auf RecipeEditor um. Im Editor: - Titel, Beschreibung, Portionen, Vorbereitungs-/Koch-/Gesamtzeit als inline-Inputs. - Zutaten: pro Zeile Menge, Einheit, Name, Notiz + Trash-Icon zum Entfernen. „+ Zutat hinzufügen"-Dashed-Button am Listenende. - Schritte: nummerierte Textareas, Trash-Icon, „+ Schritt hinzufügen". - Mengen akzeptieren Komma- oder Punkt-Dezimalen. - Empty-Items werden beim Speichern automatisch aussortiert. Backend: - Neue Repo-Funktionen updateRecipeMeta(id, patch), replaceIngredients, replaceSteps — letztere in einer Transaction mit delete+insert und FTS-Refresh. - PATCH /api/recipes/[id] akzeptiert jetzt zusätzlich description, servings_default, servings_unit, prep_time_min, cook_time_min, total_time_min, cuisine, category, ingredients[], steps[]. Vorher nur title/hidden_from_recent; diese beiden bleiben als Kurz-Fall erhalten, damit bestehende Aufrufer unverändert laufen. - Zod-Schema mit expliziten Grenzen (max-Länge, positive Mengen). Tests: 3 neue Cases für updateRecipeMeta, replaceIngredients (inkl. FTS-Update), replaceSteps. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,10 @@ import {
|
||||
insertRecipe,
|
||||
getRecipeById,
|
||||
getRecipeIdBySourceUrl,
|
||||
deleteRecipe
|
||||
deleteRecipe,
|
||||
updateRecipeMeta,
|
||||
replaceIngredients,
|
||||
replaceSteps
|
||||
} from '../../src/lib/server/recipes/repository';
|
||||
import { extractRecipeFromHtml } from '../../src/lib/server/parsers/json-ld-recipe';
|
||||
import type { Recipe } from '../../src/lib/types';
|
||||
@@ -97,4 +100,58 @@ describe('recipe repository', () => {
|
||||
deleteRecipe(db, id);
|
||||
expect(getRecipeById(db, id)).toBeNull();
|
||||
});
|
||||
|
||||
it('updateRecipeMeta patches only supplied fields', () => {
|
||||
const db = openInMemoryForTest();
|
||||
const id = insertRecipe(db, baseRecipe({ title: 'A', prep_time_min: 10 }));
|
||||
updateRecipeMeta(db, id, { description: 'neu', prep_time_min: 15 });
|
||||
const loaded = getRecipeById(db, id);
|
||||
expect(loaded?.title).toBe('A'); // unverändert
|
||||
expect(loaded?.description).toBe('neu');
|
||||
expect(loaded?.prep_time_min).toBe(15);
|
||||
});
|
||||
|
||||
it('replaceIngredients swaps full list and rebuilds FTS', () => {
|
||||
const db = openInMemoryForTest();
|
||||
const id = insertRecipe(
|
||||
db,
|
||||
baseRecipe({
|
||||
title: 'Pasta',
|
||||
ingredients: [
|
||||
{ position: 1, quantity: 200, unit: 'g', name: 'Pancetta', note: null, raw_text: '200 g Pancetta' }
|
||||
]
|
||||
})
|
||||
);
|
||||
replaceIngredients(db, id, [
|
||||
{ position: 1, quantity: 500, unit: 'g', name: 'Nudeln', note: null, raw_text: '500 g Nudeln' },
|
||||
{ position: 2, quantity: 2, unit: null, name: 'Eier', note: null, raw_text: '2 Eier' }
|
||||
]);
|
||||
const loaded = getRecipeById(db, id);
|
||||
expect(loaded?.ingredients.length).toBe(2);
|
||||
expect(loaded?.ingredients[0].name).toBe('Nudeln');
|
||||
// FTS index should reflect new ingredient
|
||||
const hit = db
|
||||
.prepare("SELECT rowid FROM recipe_fts WHERE recipe_fts MATCH 'nudeln'")
|
||||
.all();
|
||||
expect(hit.length).toBe(1);
|
||||
});
|
||||
|
||||
it('replaceSteps swaps full list', () => {
|
||||
const db = openInMemoryForTest();
|
||||
const id = insertRecipe(
|
||||
db,
|
||||
baseRecipe({
|
||||
title: 'S',
|
||||
steps: [
|
||||
{ position: 1, text: 'Alt' }
|
||||
]
|
||||
})
|
||||
);
|
||||
replaceSteps(db, id, [
|
||||
{ position: 1, text: 'Erst' },
|
||||
{ position: 2, text: 'Dann' }
|
||||
]);
|
||||
const loaded = getRecipeById(db, id);
|
||||
expect(loaded?.steps.map((s) => s.text)).toEqual(['Erst', 'Dann']);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user