database and backend for recipe_ingredients and recipe_steps

This commit is contained in:
fred 2025-07-08 17:26:02 -07:00
parent 6af9d8619f
commit 5898ac7779
4 changed files with 61 additions and 11 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
examp_frontend/ examp_frontend/
postgres/db postgres/db
*/.env */.env
todo

View file

@ -21,8 +21,31 @@ app.get("/recipes", async (req, res) => {
app.get("/recipe/:id", async (req, res) => { app.get("/recipe/:id", async (req, res) => {
const id = req.params.id const id = req.params.id
try { try {
const recipe = await db('recipes').where('id', '=', id).select('id', 'name', 'cuisine').first(); const recipeQuery = db('recipes').where('id', '=', id).select('id', 'name', 'cuisine');
res.json(recipe);
const ingredientsQuery = db.from('recipe_ingredients as ri')
.join('ingredients as i', 'ri.ingredient_id', 'i.id')
.where('ri.recipe_id', id)
.select('i.name', 'ri.quantity', 'ri.unit');
const stepsQuery = db('recipe_steps').where('recipe_id', id).select('step_number', 'instruction');
const [recipe, ingredients, steps] = await Promise.all([recipeQuery, ingredientsQuery, stepsQuery]);
const result = {
recipe: recipe[0],
ingredients: ingredients.map(ingredient => ({
name: ingredient.name,
quantity: ingredient.quantity,
unit: ingredient.unit
})),
steps: steps.reduce((acc, step) => {
acc[step.step_number] = step.instruction;
return acc;
}, {})
};
res.json(result);
} catch (err) { } catch (err) {
console.log(err); console.log(err);
res.status(500).json({ error: err.message }); res.status(500).json({ error: err.message });
@ -30,12 +53,45 @@ app.get("/recipe/:id", async (req, res) => {
}); });
app.post("/add-recipe", async (req, res) => { app.post("/add-recipe", async (req, res) => {
const { name, cuisine } = req.body; const { name, cuisine, ingredients, steps } = req.body;
try { try {
const [id] = await db('recipes').insert({ const [id] = await db('recipes').insert({
name: name, name: name,
cuisine: cuisine cuisine: cuisine
}, ['id']) }, ['id'])
const existingIngredients = await db('ingredients').whereIn('name', ingredients.map(ing => ing.name));
let ingredientData = [];
for (let ingredient of ingredients) {
const existingIngredient = existingIngredients.find(ing => ing.name === ingredient.name);
if (!existingIngredient) {
// create the ingredient if there is no entry
const [ingredientId] = await db('ingredients').insert({
name: ingredient.name
}, ['id']);
ingredientData.push({ id: ingredientId, quantity: ingredient.quantity, unit: ingredient.unit });
} else {
// if the ingredient exists use existing entry
ingredientData.push({ id: existingIngredient.id, quantity: ingredient.quantity, unit: ingredient.unit });
}
}
const ingredientInserts = ingredientData.map(ing => ({
ingredient_id: ing.id,
quantity: ing.quantity,
unit: ing.unit,
recipe_id: id.id
}));
await db('recipe_ingredients').insert(ingredientInserts);
// Step 4: Insert steps into recipe_steps
const stepInserts = Object.keys(steps).map(stepNumber => ({
recipe_id: id.id,
step_number: parseInt(stepNumber),
instruction: steps[stepNumber]
}));
await db('recipe_steps').insert(stepInserts);
res.status(200).send({ message: "Recipe added", id: id.id }); res.status(200).send({ message: "Recipe added", id: id.id });
} catch (err) { } catch (err) {
console.log(err); console.log(err);

View file

@ -25,7 +25,7 @@ exports.up = function (knex) {
table.increments('id').primary(); table.increments('id').primary();
table.integer('recipe_id'); table.integer('recipe_id');
table.integer('step_number'); table.integer('step_number');
table.string('instructions'); table.string('instruction');
table.unique(['recipe_id', 'step_number']); table.unique(['recipe_id', 'step_number']);
table.index(['recipe_id']) table.index(['recipe_id'])
table.timestamps(true, true); table.timestamps(true, true);

7
todo
View file

@ -1,7 +0,0 @@
todo:
add recipe redirect to recipe page. id is getting returned now.
make naming consistent and sensical of pages, functions, vars etc.
add are you sure modal to delete.
add delete to recipe page
Ensure Accessibility: Make sure you include attributes like aria-labelledby and aria-describedby for better accessibility. # i dont know what this means, but it might be a good idea to look into it or other accessability before finishing the project