feat: structure meal planning taxonomy and favorites
This commit is contained in:
@@ -12,6 +12,22 @@ import { str, num, collectErrors, MAX_TITLE, MAX_TEXT, MAX_SHORT } from '../midd
|
||||
const log = createLogger('Recipes');
|
||||
const router = express.Router();
|
||||
|
||||
const VALID_MEAL_CATEGORIES = ['meat', 'fish', 'pasta', 'rice', 'vegetarian', 'soup', 'leftovers', 'cozy', 'breakfast', 'snack', 'other'];
|
||||
const VALID_PROTEINS = ['mixed', 'chicken', 'beef', 'pork', 'fish', 'vegetarian', 'none', 'other'];
|
||||
const VALID_STYLES = ['family', 'quick', 'cozy', 'grill', 'vegetarian', 'kids', 'leftovers', 'other'];
|
||||
|
||||
function normalizeEnum(value, allowed, fallback = null) {
|
||||
const normalized = String(value || '').trim().toLowerCase();
|
||||
return allowed.includes(normalized) ? normalized : fallback;
|
||||
}
|
||||
|
||||
function normalizeTags(value) {
|
||||
const tags = Array.isArray(value)
|
||||
? value
|
||||
: String(value || '').split(',');
|
||||
return tags.map((tag) => String(tag || '').trim().toLowerCase()).filter(Boolean).slice(0, 12);
|
||||
}
|
||||
|
||||
function loadRecipeWithIngredients(id) {
|
||||
const recipe = db.get().prepare(`
|
||||
SELECT r.*, u.display_name AS creator_name, u.avatar_color AS creator_color
|
||||
@@ -75,11 +91,16 @@ router.post('/', (req, res) => {
|
||||
const errors = collectErrors([vTitle, vNotes, vRecipeUrl]);
|
||||
if (errors.length) return res.status(400).json({ error: errors.join(' '), code: 400 });
|
||||
|
||||
const mealCategory = normalizeEnum(req.body.meal_category ?? req.body.mealCategory, VALID_MEAL_CATEGORIES, 'other');
|
||||
const protein = normalizeEnum(req.body.protein, VALID_PROTEINS, 'mixed');
|
||||
const style = normalizeEnum(req.body.style, VALID_STYLES, 'family');
|
||||
const tagsJson = JSON.stringify(normalizeTags(req.body.tags ?? req.body.tags_json));
|
||||
|
||||
const recipeId = db.transaction(() => {
|
||||
const result = db.get().prepare(`
|
||||
INSERT INTO recipes (title, notes, recipe_url, created_by)
|
||||
VALUES (?, ?, ?, ?)
|
||||
`).run(vTitle.value, vNotes.value, vRecipeUrl.value, req.session.userId);
|
||||
INSERT INTO recipes (title, notes, recipe_url, meal_category, protein, style, tags_json, created_by)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`).run(vTitle.value, vNotes.value, vRecipeUrl.value, mealCategory, protein, style, tagsJson, req.session.userId);
|
||||
|
||||
const rid = Number(result.lastInsertRowid);
|
||||
const insertIng = db.get().prepare(`
|
||||
@@ -122,12 +143,17 @@ router.put('/:id', (req, res) => {
|
||||
const errors = collectErrors([vTitle, vNotes, vRecipeUrl]);
|
||||
if (errors.length) return res.status(400).json({ error: errors.join(' '), code: 400 });
|
||||
|
||||
const mealCategory = normalizeEnum(req.body.meal_category ?? req.body.mealCategory, VALID_MEAL_CATEGORIES, existing.meal_category || 'other');
|
||||
const protein = normalizeEnum(req.body.protein, VALID_PROTEINS, existing.protein || 'mixed');
|
||||
const style = normalizeEnum(req.body.style, VALID_STYLES, existing.style || 'family');
|
||||
const tagsJson = JSON.stringify(normalizeTags(req.body.tags ?? req.body.tags_json));
|
||||
|
||||
db.transaction(() => {
|
||||
db.get().prepare(`
|
||||
UPDATE recipes
|
||||
SET title = ?, notes = ?, recipe_url = ?
|
||||
SET title = ?, notes = ?, recipe_url = ?, meal_category = ?, protein = ?, style = ?, tags_json = ?
|
||||
WHERE id = ?
|
||||
`).run(vTitle.value, vNotes.value, vRecipeUrl.value, id);
|
||||
`).run(vTitle.value, vNotes.value, vRecipeUrl.value, mealCategory, protein, style, tagsJson, id);
|
||||
|
||||
db.get().prepare('DELETE FROM recipe_ingredients WHERE recipe_id = ?').run(id);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user