in progress: associate ingredients to recipe on save

This commit is contained in:
Mikayla Dobson
2023-02-19 12:04:46 -06:00
parent 47360518ce
commit 63d0049450
8 changed files with 116 additions and 15 deletions

View File

@@ -1,7 +1,7 @@
import { Autocomplete, TextField } from "@mui/material"
import { ChangeEvent, useEffect, useRef, useState } from "react";
import { useAuthContext } from "../../context/AuthContext";
import { DropdownData, IIngredient } from "../../schemas";
import { DropdownData, IIngredient, RecipeIngredient } from "../../schemas";
import { IngredientFieldData } from "../../util/types";
import { Button } from "../ui";
@@ -17,7 +17,7 @@ const createIngredient = (name: string, userid: string | number) => {
return {
name: name,
createdbyid: userid
} as IIngredient
}
}
const quantityOptions: readonly any[] = [
@@ -149,7 +149,15 @@ function IngredientSelector({ position, ingredients, units, getRowState, destroy
if (typeof newValue == 'string') {
const newIngredient = createIngredient(newValue, user.id!);
setIngredientOptions((prev) => [...prev, newValue]);
setIngredientOptions((prev) => {
let shouldInsert = true;
for (let each of prev) {
if (each == newValue) shouldInsert = false;
}
return (shouldInsert ? [...prev, newValue] : prev);
});
setRowState((prev) => {
let shouldInsert = true;

View File

@@ -130,9 +130,17 @@ export default function AddRecipe() {
const handleCreate = async () => {
if (!user || !token) return;
// initialize API handlers
const recipeAPI = new API.Recipe(token);
const ingredientAPI = new API.Ingredient(token);
// array to aggregate error/success messages
let messages = new Array<string>();
// inject current user id into recipe entry
setInput({ ...input, authoruserid: user.id! });
// verify all required fields are set
for (let field of Object.keys(input)) {
// account for an edge case where this state may not have been set yet
if (field == 'authoruserid' as keyof IRecipe) {
@@ -140,16 +148,57 @@ export default function AddRecipe() {
}
if (!input[field as keyof IRecipe]) {
messages.push("Missing required field " + field);
return;
}
}
const recipe = new API.Recipe(token);
const result = await recipe.post(input);
let ingredientSelections = new Array<any>();
let newIngredientCount = 0;
// handle ingredient row data
for (let row of ingredientFieldData) {
if (row.ingredientSelection === undefined) {
messages.push("Please ensure you have included ingredient selections for all rows.");
}
ingredientSelections.push(row.ingredientSelection);
for (let ing of row.ingredients) {
// filter out recipes that already exist
if (ingredients.filter(x => x.name == ing.name).includes(ing)) {
continue;
}
// update the ingredient list
setIngredients((prev) => [...prev, ing]);
// post the new ingredient to the database
const newEntry = await ingredientAPI.post(ing);
messages.push(`Successfully created new ingredient: ${ing.name}!`);
console.log(newEntry);
newIngredientCount++;
}
}
// post recipe entry
const result = await recipeAPI.post(input);
// handle recipe post resolve/reject
if (result) {
const recipeID = result.recipe.id;
const recipeName = result.recipe.name;
let recipeIngredientCount = 0;
for (let ing of ingredientSelections) {
const ok = await recipeAPI.addIngredientToRecipe(ing, recipeID);
if (ok) recipeIngredientCount++;
}
messages.push(`Created recipe ${recipeName} with ${recipeIngredientCount} total ingredients!`)
if (newIngredientCount > 0) {
messages.push(`Successfully created ${newIngredientCount} new ingredients! Thanks for helping us grow.`);
}
setToast(
<Toast>

View File

@@ -1,5 +1,5 @@
import { AxiosError, AxiosHeaders, AxiosRequestHeaders, AxiosResponse } from "axios";
import { IUser, IUserAuth, IFriendship, IRecipe, IIngredient, ICollection, IGroceryList, DropdownData } from "../schemas";
import { IUser, IUserAuth, IFriendship, IRecipe, IIngredient, ICollection, IGroceryList, DropdownData, RecipeIngredient } from "../schemas";
import { default as _instance } from "./axiosInstance";
module API {
@@ -187,6 +187,11 @@ module API {
constructor(token: string) {
super(Settings.getAPISTRING() + "/app/recipe", token);
}
async addIngredientToRecipe(ingredient: RecipeIngredient, recipeid: string | number) {
const response = await this.instance.post(this.endpoint + `?addIngredients=true&recipeID=${recipeid}`, JSON.stringify(ingredient), this.headers);
return Promise.resolve(response.data);
}
}
export class Ingredient extends RestController<IIngredient> {
@@ -194,11 +199,6 @@ module API {
super(Settings.getAPISTRING() + "/app/ingredient", token);
}
async associateIngredientWithRecipe(recipeID: string | number, ingredientID: string | number) {
const response = await this.instance.post(this.endpoint + `/${ingredientID}?recipeID=${recipeID}`, this.headers);
return Promise.resolve(response.data);
}
async getAllForRecipe(recipeID: string | number) {
const response = await this.instance.get(this.endpoint + `?recipeID=${recipeID}`, this.headers);
return Promise.resolve(response.data);

View File

@@ -1,4 +1,4 @@
import { IRecipe } from "../schemas";
import { IRecipe, RecipeIngredient } from "../schemas";
import { Recipe } from "../models/recipe";
import ControllerResponse from "../util/ControllerResponse";
import { StatusCode } from "../util/types";
@@ -58,4 +58,15 @@ export default class RecipeCtl {
throw new Error(error);
}
}
async addIngredientToRecipe(ingredient: RecipeIngredient, recipeid: string | number) {
try {
const result = await RecipeInstance.addIngredientToRecipe(ingredient, recipeid);
const ok = result !== null;
const code = ok ? StatusCode.NewContent : StatusCode.BadRequest;
return new ControllerResponse(code, (result || "Something went wrong"));
} catch (error: any) {
throw new Error(error);
}
}
}

View File

@@ -1,4 +1,4 @@
import { IRecipe } from "../schemas";
import { IIngredient, IRecipe, RecipeIngredient } from "../schemas";
import fs from 'fs';
import pool from "../db";
import { CollectionCtl } from "../controllers";
@@ -114,4 +114,24 @@ export class Recipe {
throw new Error(error);
}
}
async addIngredientToRecipe(ingredient: RecipeIngredient, recipeid: string | number) {
const { quantity, unit, id } = ingredient;
try {
const statement = `
INSERT INTO recipin.cmp_recipeingredient
(quantity, unit, ingredientid, recipeid)
VALUES ($1, $2, $3, $4) RETURNING *
`
const result = await pool.query(statement, [quantity, unit, id, recipeid]);
if (result.rows) return result.rows[0];
return [];
} catch (e: any) {
throw new Error(e);
}
}
}

View File

@@ -51,4 +51,6 @@ export const ingredientRoute = (app: Express) => {
next(e);
}
})
return router;
}

View File

@@ -57,10 +57,16 @@ export const recipeRoute = (app: Express) => {
router.post('/', restrictAccess, async (req, res, next) => {
const user = req.user as IUser;
const data = req.body;
const { addIngredients, recipeID } = req.query;
try {
const result = await recipectl.post(user.id as number, data);
res.status(result.code).send(result.data);
if (addIngredients) {
const result = await recipectl.addIngredientToRecipe(data, recipeID as string);
res.status(result.code).send(result.data);
} else {
const result = await recipectl.post(user.id as number, data);
res.status(result.code).send(result.data);
}
} catch(e) {
next(e);
}

View File

@@ -44,6 +44,11 @@ export interface IIngredient extends HasHistory {
createdbyid: string | number
}
export interface RecipeIngredient extends Partial<IIngredient> {
unit: string
quantity: string | number
}
export interface ICollection extends HasHistory, CanDeactivate {
name: string
ismaincollection: boolean