edit recipe

This commit is contained in:
fred 2025-10-20 11:43:29 -07:00
parent 798e879863
commit 8b52d31dcf
10 changed files with 389 additions and 217 deletions

View file

@ -50,6 +50,7 @@ export const addRecipe = async (req: Request, res: Response): Promise<void> => {
return; return;
} }
try { try {
console.log(req.body);
const createdRecipe = await model.addRecipe(req.body); const createdRecipe = await model.addRecipe(req.body);
res.status(201).json(createdRecipe); res.status(201).json(createdRecipe);
} catch (error) { } catch (error) {
@ -61,6 +62,27 @@ export const addRecipe = async (req: Request, res: Response): Promise<void> => {
} }
}; };
export const updateRecipe = async (
req: Request,
res: Response,
): Promise<void> => {
console.log(req.body);
const id = parseInt(req.params.id, 10);
if (process.env.NODE_ENV === "demo") {
return;
}
try {
const updatedRecipe = await model.updateRecipe(req.body, id);
res.status(201).json(updatedRecipe);
} catch (error) {
res.status(500).json({
msg: "Failed to add recipe",
source: "recipeController",
error: error instanceof Error ? error.message : "Unknown error",
});
}
};
export const setStars = async (req: Request, res: Response): Promise<void> => { export const setStars = async (req: Request, res: Response): Promise<void> => {
if (process.env.NODE_ENV === "demo") { if (process.env.NODE_ENV === "demo") {
return; return;
@ -104,6 +126,7 @@ export default {
getAllRecipes, getAllRecipes,
getRecipeById, getRecipeById,
addRecipe, addRecipe,
updateRecipe,
setStars, setStars,
deleteRecipe, deleteRecipe,
}; };

View file

@ -12,7 +12,7 @@ class RecipeModel {
async getAllRecipes(): Promise<any[]> { async getAllRecipes(): Promise<any[]> {
try { try {
logger.info("index page view") logger.info("index page view");
return await this.prisma.recipes.findMany(); return await this.prisma.recipes.findMany();
} catch (err) { } catch (err) {
console.error("Error fetching all recipes:", err); console.error("Error fetching all recipes:", err);
@ -46,7 +46,10 @@ class RecipeModel {
instruction: step.instruction, instruction: step.instruction,
})), })),
}; };
logger.info("recipe page view", { recipe_id: data.details.id, recipe_name: data.details.name }) logger.info("recipe page view", {
recipe_id: data.details.id,
recipe_name: data.details.name,
});
return data; return data;
} catch (err) { } catch (err) {
console.log("Error finding recipe:", err); console.log("Error finding recipe:", err);
@ -58,25 +61,25 @@ class RecipeModel {
} }
async addRecipe(recipeData: { async addRecipe(recipeData: {
details: {
name: string; name: string;
author: string; author: string;
cuisine: string; cuisine: string;
stars: number; stars: number;
ingredients: string[];
steps: { [key: string]: string };
prep_minutes: number; prep_minutes: number;
cook_minutes: number; cook_minutes: number;
};
ingredients: string[];
steps: { [key: string]: string };
}): Promise<any> { }): Promise<any> {
const { const name = recipeData.details.name;
name, const author = recipeData.details.author;
author, const cuisine = recipeData.details.cuisine;
cuisine, const stars = recipeData.details.stars;
stars, const prep_minutes = recipeData.details.prep_minutes;
ingredients, const cook_minutes = recipeData.details.cook_minutes;
steps, const ingredients = recipeData.ingredients;
prep_minutes, const steps = recipeData.steps;
cook_minutes,
} = recipeData;
try { try {
const createdRecipe = await this.prisma.recipes.create({ const createdRecipe = await this.prisma.recipes.create({
data: { data: {
@ -90,10 +93,7 @@ class RecipeModel {
create: ingredients.map((ing) => ({ raw: ing })), create: ingredients.map((ing) => ({ raw: ing })),
}, },
recipeSteps: { recipeSteps: {
create: Object.keys(steps).map((stepNumber) => ({ create: steps,
step_number: parseInt(stepNumber),
instruction: steps[stepNumber],
})),
}, },
}, },
}); });
@ -112,6 +112,67 @@ class RecipeModel {
} }
} }
async updateRecipe(
recipeData: {
details: {
name: string;
author: string;
cuisine: string;
stars: number;
prep_minutes: number;
cook_minutes: number;
};
ingredients: string[];
steps: { [key: string]: string };
},
id: number,
): Promise<any> {
const name = recipeData.details.name;
const author = recipeData.details.author;
const cuisine = recipeData.details.cuisine;
const stars = recipeData.details.stars;
const prep_minutes = recipeData.details.prep_minutes;
const cook_minutes = recipeData.details.cook_minutes;
const ingredients = recipeData.ingredients;
const steps = recipeData.steps;
try {
await this.deleteRecipeData(id);
const updatedRecipe = await this.prisma.recipes.update({
where: { id },
data: {
name,
author,
cuisine,
prep_minutes,
cook_minutes,
stars,
recipeIngredients: {
create: ingredients.map((ing) => ({ raw: ing })),
},
recipeSteps: {
create: steps,
},
},
});
if (!updatedRecipe) {
logger.warn(`Recipe with id ${id} cannot be found`);
return null;
}
logger.info("Updated Recipe", {
id: updatedRecipe.id,
name: updatedRecipe.name,
});
return updatedRecipe;
} catch (err) {
console.log("Error updating recipe:", err);
logger.error("Error updating recipe", {
message: err instanceof Error ? err.message : "Unknown error",
});
throw new Error("Failed to update recipe");
}
}
async setStars(id: number, stars: number): Promise<{ message: string }> { async setStars(id: number, stars: number): Promise<{ message: string }> {
try { try {
await this.prisma.recipes.update({ await this.prisma.recipes.update({
@ -130,13 +191,7 @@ class RecipeModel {
async deleteRecipe(id: number): Promise<{ message: string }> { async deleteRecipe(id: number): Promise<{ message: string }> {
try { try {
await this.prisma.recipe_ingredients.deleteMany({ this.deleteRecipeData(id);
where: { recipe_id: id },
});
await this.prisma.recipe_steps.deleteMany({
where: { recipe_id: id },
});
const deletedRecipe = await this.prisma.recipes.delete({ const deletedRecipe = await this.prisma.recipes.delete({
where: { id }, where: { id },
}); });
@ -153,6 +208,25 @@ class RecipeModel {
throw new Error(err instanceof Error ? err.message : "Unknown error"); throw new Error(err instanceof Error ? err.message : "Unknown error");
} }
} }
async deleteRecipeData(id: number): Promise<{ message: string }> {
try {
await this.prisma.recipe_ingredients.deleteMany({
where: { recipe_id: id },
});
await this.prisma.recipe_steps.deleteMany({
where: { recipe_id: id },
});
return { message: "Recipe data deleted successfully" };
} catch (err) {
console.error("Error deleting recipe:", err);
logger.error("Error deleting recipe", {
message: err instanceof Error ? err.message : "Unknown error",
});
throw new Error(err instanceof Error ? err.message : "Unknown error");
}
}
} }
export default RecipeModel; export default RecipeModel;

View file

@ -11,6 +11,8 @@ router.get("/recipe/:id", recipeController.getRecipeById);
router.post("/add-recipe", recipeController.addRecipe); router.post("/add-recipe", recipeController.addRecipe);
router.post("/update-recipe/:id", recipeController.updateRecipe);
router.post("/set-stars", recipeController.setStars); router.post("/set-stars", recipeController.setStars);
router.delete("/delete-recipe", recipeController.deleteRecipe); router.delete("/delete-recipe", recipeController.deleteRecipe);

View file

@ -2,9 +2,10 @@ import "./App.css";
import Index from "./pages/Index.tsx"; import Index from "./pages/Index.tsx";
import RecipePage from "./pages/RecipePage.tsx"; import RecipePage from "./pages/RecipePage.tsx";
import AddRecipe from "./pages/AddRecipe.tsx"; import AddRecipe from "./pages/AddRecipe.tsx";
import About from "./pages/About.tsx" import About from "./pages/About.tsx";
import RecipeIngredients from "./pages/RecipeIngredients.tsx" import RecipeIngredients from "./pages/RecipeIngredients.tsx";
import RecipeSteps from "./pages/RecipeSteps.tsx" import RecipeSteps from "./pages/RecipeSteps.tsx";
import EditRecipe from "./pages/EditRecipe.tsx";
import RecipeBookTabs from "./components/RecipeBookTabs.tsx"; import RecipeBookTabs from "./components/RecipeBookTabs.tsx";
import { Routes, Route } from "react-router-dom"; import { Routes, Route } from "react-router-dom";
@ -18,6 +19,7 @@ function App() {
<Route path="/" element={<Index />} /> <Route path="/" element={<Index />} />
<Route path="/recipe/:id" element={<RecipePage />} /> <Route path="/recipe/:id" element={<RecipePage />} />
<Route path="/add-recipe" element={<AddRecipe />} /> <Route path="/add-recipe" element={<AddRecipe />} />
<Route path="/edit-recipe/:id" element={<EditRecipe />} />
<Route path="/about" element={<About />} /> <Route path="/about" element={<About />} />
<Route path="/recipe-ingredients" element={<RecipeIngredients />} /> <Route path="/recipe-ingredients" element={<RecipeIngredients />} />
<Route path="/recipe-steps" element={<RecipeSteps />} /> <Route path="/recipe-steps" element={<RecipeSteps />} />

View file

@ -0,0 +1,152 @@
import React, { useEffect, useState } from "react";
import AddBulkIngredients from "./AddBulkIngredients.tsx";
import AddBulkSteps from "./AddBulkSteps.tsx";
import StarRating from "./StarRating.tsx";
import { isProfane } from "../utils/profanityFilter";
import { type Recipe } from "../types/Recipe.ts";
interface Step {
step_number: number;
instruction: string;
}
interface RecipeFormProps {
onSubmit: (recipeData: Recipe) => Promise<void>;
initialData?: Recipe;
}
const RecipeForm: React.FC<RecipeFormProps> = ({ onSubmit, initialData }) => {
const [ingredients, setIngredients] = useState<string[]>([]);
const [steps, setSteps] = useState<Step[]>([]);
const [recipeName, setRecipeName] = useState("");
const [recipeCuisine, setRecipeCuisine] = useState("");
const [author, setAuthor] = useState("");
const [stars, setStars] = useState(0);
const [prepMinutes, setPrepMinutes] = useState(5);
const [cookMinutes, setCookMinutes] = useState(5);
useEffect(() => {
if (initialData) {
setRecipeName(initialData.details.name || "");
setRecipeCuisine(initialData.details.cuisine || "");
setAuthor(initialData.details.author || "");
setStars(initialData.details.stars || 0);
setPrepMinutes(initialData.details.prep_minutes || 5);
setCookMinutes(initialData.details.cook_minutes || 5);
setIngredients(initialData.ingredients);
setSteps(initialData.steps);
}
}, [initialData]);
const handleSubmit = async (event: React.FormEvent) => {
event.preventDefault();
if (
isProfane(recipeName) ||
isProfane(recipeCuisine) ||
ingredients.some(isProfane) ||
steps.some((step) => isProfane(step.instruction)) ||
isProfane(author)
) {
alert("Your submission contains inappropriate language.");
return;
}
const recipeData = {
details: {
name: recipeName,
cuisine: recipeCuisine.toLowerCase(),
author: author,
prep_minutes: prepMinutes,
cook_minutes: cookMinutes,
stars: stars,
},
ingredients: ingredients,
steps: steps.map((step) => ({
id: undefined,
step_number: step.step_number,
instruction: step.instruction,
})),
};
await onSubmit(recipeData);
};
return (
<form onSubmit={handleSubmit} className="add-recipe-form">
<input
type="text"
placeholder="Title"
className="recipe-name mb-4 p-2 border border-[var(--color-primaryBorder)] rounded w-full"
value={recipeName}
onChange={(e) => setRecipeName(e.target.value)}
/>
<input
type="text"
placeholder="Cuisine"
className="recipe-cusine mb-4 p-2 border border-[var(--color-primaryBorder)] rounded w-full"
value={recipeCuisine}
onChange={(e) => setRecipeCuisine(e.target.value)}
/>
<input
type="text"
placeholder="Author"
className="recipe-cusine mb-4 p-2 border border-[var(--color-primaryBorder)] rounded w-full"
value={author}
onChange={(e) => setAuthor(e.target.value)}
/>
<div className="flex items-center justify-between mb-4">
<div>
<label
htmlFor="prepTime"
className="mr-2 font-bold text-[var(--color-secondaryTextDark)]"
>
Prep Time:
</label>
<input
type="number"
placeholder="prep time in minutes"
className="recipe-cusine p-2 border border-[var(--color-primaryBorder)] rounded w-24"
value={prepMinutes}
onChange={(e) => setPrepMinutes(parseInt(e.target.value))}
/>
<span className="ml-2 text-[var(--color-secondaryTextDark)]">
minutes
</span>
</div>
<div>
<label
htmlFor="cookTime"
className="mr-2 font-bold text-[var(--color-secondaryTextDark)]"
>
Cook Time:
</label>
<input
type="number"
placeholder="cook time in minutes"
className="recipe-cusine p-2 border border-[var(--color-primaryBorder)] rounded w-24"
value={cookMinutes}
onChange={(e) => setCookMinutes(parseInt(e.target.value))}
/>
<span className="ml-2 text-[var(--color-secondaryTextDark)]">
minutes
</span>
</div>
<StarRating
rating={stars}
onRatingChange={(newRating: number) => setStars(newRating)}
/>
</div>
<AddBulkIngredients ingredients={ingredients} onChange={setIngredients} />
<AddBulkSteps steps={steps} onChange={setSteps} />
<button
type="submit"
className="ar-button bg-[var(--color-buttonBg)] text-[var(--color-textLight)] py-2 px-4 rounded hover:bg-[var(--color-buttonBgHover)]"
>
{initialData ? "Save" : "Add"} Recipe
</button>
</form>
);
};
export default RecipeForm;

View file

@ -1,191 +1,27 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { addRecipe } from "../services/frontendApi.js"; import { addRecipe } from "../services/frontendApi.js";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import AddBulkIngredients from "../components/AddBulkIngredients.tsx"; import RecipeForm from "../components/RecipeForm.tsx";
import AddBulkSteps from "../components/AddBulkSteps.tsx";
import StarRating from "../components/StarRating.tsx";
import DemoModal from "../components/DemoModal.tsx"; import DemoModal from "../components/DemoModal.tsx";
import "../css/colorTheme.css"; import { type Recipe } from "../types/Recipe.ts"
import { isProfane } from "../utils/profanityFilter";
interface Step {
step_number: number;
instruction: string;
}
function AddRecipe() { function AddRecipe() {
const [newRecipeId, setNewRecipeId] = useState<number | null>(null);
const [ingredients, setIngredients] = useState<string[]>([]);
const [steps, setSteps] = useState<Step[]>([]);
const [showBulkForm, setShowBulkForm] = useState(true);
const [recipeName, setRecipeName] = useState("");
const [recipeCuisine, setRecipeCuisine] = useState("");
const [author, setAuthor] = useState("");
const [stars, setStars] = useState(0);
const [prepMinutes, setPrepMinutes] = useState(5);
const [cookMinutes, setCookMinutes] = useState(5);
const [showDemoModal, setShowDemoModal] = useState(false); const [showDemoModal, setShowDemoModal] = useState(false);
const navigate = useNavigate(); const navigate = useNavigate();
const addRecipeForm = async (event: React.FormEvent) => { const addRecipeForm = async (recipeData: Recipe) => {
event.preventDefault();
if (
isProfane(recipeName) ||
isProfane(recipeCuisine) ||
ingredients.some((ingredient) => isProfane(ingredient)) ||
steps.some((step) => isProfane(step.instruction)) ||
isProfane(author)
) {
alert("Your submission contains inappropriate language.");
return;
}
if (process.env.NODE_ENV === "demo") { if (process.env.NODE_ENV === "demo") {
setShowDemoModal(true); setShowDemoModal(true);
return; return;
} }
const stepsHash = Object.fromEntries(
steps.map((step) => [step.step_number, step.instruction]),
);
if (
recipeName &&
recipeCuisine &&
Object.keys(stepsHash).length > 0 &&
ingredients.length > 0
) {
const recipeData = {
name: recipeName,
cuisine: recipeCuisine.toLowerCase(),
author: author,
prep_minutes: prepMinutes,
cook_minutes: cookMinutes,
stars: stars,
ingredients: ingredients,
steps: stepsHash,
};
console.log(recipeData);
const data = await addRecipe(recipeData);
setNewRecipeId(data.id);
} else {
alert("missing required data");
}
};
React.useEffect(() => { const data = await addRecipe(recipeData);
if (newRecipeId !== null && newRecipeId !== undefined) { navigate(`/recipe/${data.id}`);
navigate(`/recipe/${newRecipeId}`); };
}
}, [newRecipeId, navigate]);
return ( return (
<div className="add-recipe-card page-outer"> <div className="add-recipe-card page-outer">
<form onSubmit={addRecipeForm} className="add-recipe-form"> <RecipeForm onSubmit={addRecipeForm} />
<input
type="text"
placeholder="Title"
className="recipe-name mb-4 p-2 border border-[var(--color-primaryBorder)] rounded w-full"
value={recipeName}
maxLength={35}
onChange={(e) => setRecipeName(e.target.value)}
/>
<input
type="text"
placeholder="Cuisine"
className="recipe-cusine mb-4 p-2 border border-[var(--color-primaryBorder)] rounded w-full"
value={recipeCuisine}
maxLength={15}
onChange={(e) => setRecipeCuisine(e.target.value)}
/>
<input
type="text"
placeholder="Author"
className="recipe-cusine mb-4 p-2 border border-[var(--color-primaryBorder)] rounded w-full"
value={author}
onChange={(e) => setAuthor(e.target.value)}
/>
<div className="flex items-center justify-between mb-4">
<div>
<label
htmlFor="prepTime"
className="mr-2 font-bold text-[var(--color-secondaryTextDark)]"
>
Prep Time:
</label>
<input
type="number"
placeholder="prep time in minutes"
className="recipe-cusine p-2 border border-[var(--color-primaryBorder)] rounded w-24"
value={prepMinutes}
onChange={(e) => setPrepMinutes(parseInt(e.target.value))}
/>
<span className="ml-2 text-[var(--color-secondaryTextDark)]">
minutes
</span>
</div>
<div>
<label
htmlFor="cookTime"
className="mr-2 font-bold text-[var(--color-secondaryTextDark)]"
>
Cook Time:
</label>
<input
type="number"
placeholder="cook time in minutes"
className="recipe-cusine p-2 border border-[var(--color-primaryBorder)] rounded w-24"
value={cookMinutes}
onChange={(e) => setCookMinutes(parseInt(e.target.value))}
/>
<span className="ml-2 text-[var(--color-secondaryTextDark)]">
minutes
</span>
</div>
<div>
<StarRating
rating={stars}
onRatingChange={(newRating: number) => setStars(newRating)}
/>
</div>
</div>
<label className="mb-4 flex items-center cursor-pointer">
<div className="relative">
<input
type="checkbox"
checked={showBulkForm}
onChange={(e) => setShowBulkForm(e.target.checked)}
className="sr-only"
/>
</div>
</label>
<div>
<AddBulkIngredients
ingredients={ingredients}
onChange={setIngredients}
/>
</div>
{/*<ul className="mb-4">
{ingredients.map((ing, index) => (
<li key={index} className="text-gray-700 flex items-start mb-2">
<span>{ing}</span>
</li>
))}
</ul>*/}
<div>
<AddBulkSteps steps={steps} onChange={setSteps} />
</div>
{/*<ul className="mb-4">
{steps.map((step) => (
<li key={step.step_number} className="text-gray-700 flex items-start mb-2">
<span>{`${step.step_number}. ${step.instruction}`}</span>
</li>
))}
</ul>*/}
<button
type="submit"
className="ar-button bg-[var(--color-buttonBg)] text-[var(--color-textLight)] py-2 px-4 rounded hover:bg-[var(--color-buttonBgHover)]"
>
submit
</button>
</form>
{showDemoModal && ( {showDemoModal && (
<DemoModal <DemoModal
isOpen={showDemoModal} isOpen={showDemoModal}
@ -196,4 +32,5 @@ function AddRecipe() {
</div> </div>
); );
} }
export default AddRecipe; export default AddRecipe;

View file

@ -0,0 +1,50 @@
import React, { useEffect, useState } from "react";
import { useParams, useNavigate } from "react-router-dom";
import { getRecipeById, updateRecipe } from "../services/frontendApi.js";
import RecipeForm from "../components/RecipeForm.tsx";
import DemoModal from "../components/DemoModal.tsx";
import { type Recipe } from "../types/Recipe.ts"
function EditRecipe() {
const { id } = useParams();
const navigate = useNavigate();
const [recipe, setRecipe] = useState<Recipe>();
const [showDemoModal, setShowDemoModal] = useState(false);
useEffect(() => {
const fetchRecipe = async () => {
const recipeData = await getRecipeById(id);
setRecipe(recipeData);
};
fetchRecipe();
}, [id]);
const updateRecipeForm = async (recipeData: Recipe) => {
if (process.env.NODE_ENV === "demo") {
setShowDemoModal(true);
return;
}
await updateRecipe(id, recipeData);
navigate(`/recipe/${id}`);
};
return (
<div className="add-recipe-card page-outer">
{recipe && (
<RecipeForm onSubmit={updateRecipeForm} initialData={recipe} />
)}
{showDemoModal && (
<DemoModal
isOpen={showDemoModal}
onClose={() => setShowDemoModal(false)}
closeModal={() => setShowDemoModal(false)}
/>
)}
</div>
);
}
export default EditRecipe;

View file

@ -111,15 +111,30 @@ function RecipePage() {
<div className="border-b-2 border-[var(--color-primaryBorder)] pb-4"> <div className="border-b-2 border-[var(--color-primaryBorder)] pb-4">
<div className="recipe-card"> <div className="recipe-card">
<div className="flex relative justify-between"> <div className="flex relative justify-between">
<div className="invisible-buttons">
<button
onClick={() => {}}
className="invisible ar-button py-1 px-1 rounded mr-2 self-start"
>
🗑
</button>
<button <button
onClick={() => {}} onClick={() => {}}
className="invisible ar-button py-1 px-1 rounded self-start" className="invisible ar-button py-1 px-1 rounded self-start"
> >
🗑 🗑
</button> </button>
</div>
<h3 className="text-center max-w-lg px-4 text-2xl lg:text-3xl font-bold text-[var(--color-textDark)]"> <h3 className="text-center max-w-lg px-4 text-2xl lg:text-3xl font-bold text-[var(--color-textDark)]">
{recipe.details.name} {recipe.details.name}
</h3> </h3>
<div className="modify-buttons">
<button
onClick={() => navigate(`/edit-recipe/${recipe.details.id}`)}
className="ar-button bg-[var(--color-buttonBg)] text-[var(--color-textLight)] py-1 px-1 rounded hover:bg-[var(--color-buttonBgHover)] mr-2 self-start"
>
🔧
</button>
<button <button
onClick={openModal} onClick={openModal}
className="ar-button bg-[var(--color-buttonBg)] text-[var(--color-textLight)] py-1 px-1 rounded hover:bg-[var(--color-buttonBgHover)] self-start" className="ar-button bg-[var(--color-buttonBg)] text-[var(--color-textLight)] py-1 px-1 rounded hover:bg-[var(--color-buttonBgHover)] self-start"
@ -127,6 +142,7 @@ function RecipePage() {
🗑 🗑
</button> </button>
</div> </div>
</div>
<div className="mt-1"> <div className="mt-1">
<p className="text-[var(--color-textDark)] italic text-lg"> <p className="text-[var(--color-textDark)] italic text-lg">
{recipe.details.cuisine} {recipe.details.cuisine}
@ -146,7 +162,10 @@ function RecipePage() {
</h4> </h4>
<ul className="space-y-2"> <ul className="space-y-2">
{recipe.ingredients.map((ingredient: string, index) => ( {recipe.ingredients.map((ingredient: string, index) => (
<li key={index} className="text-[var(--color-secondaryTextDark)] flex items-start"> <li
key={index}
className="text-[var(--color-secondaryTextDark)] flex items-start"
>
<span className="w-1.5 h-1.5 bg-[var(--color-buttonBg)] rounded-full mt-2 mr-3 flex-shrink-0"></span> <span className="w-1.5 h-1.5 bg-[var(--color-buttonBg)] rounded-full mt-2 mr-3 flex-shrink-0"></span>
<span className="font-medium text-left">{ingredient}</span> <span className="font-medium text-left">{ingredient}</span>
</li> </li>

View file

@ -42,6 +42,18 @@ export const addRecipe = async (recipeData) => {
return data; return data;
}; };
export const updateRecipe = async (id, recipeData) => {
console.log("updateRecipe");
console.log(recipeData);
const response = await fetch(`${baseUrl}/update-recipe/${id}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(recipeData),
});
const data = await response.json();
return data;
};
export const setDBStars = async (id, stars) => { export const setDBStars = async (id, stars) => {
console.log(JSON.stringify({ id: id, stars: stars })); console.log(JSON.stringify({ id: id, stars: stars }));
// return // return

View file

@ -1,5 +1,5 @@
interface Step { interface Step {
id: number; id?: number;
step_number: number; step_number: number;
instruction: string; instruction: string;
} }
@ -17,6 +17,7 @@ interface Recipe {
ingredients: string[]; ingredients: string[];
steps: Step[]; steps: Step[];
} }
// smaller Recipe type returned by backend at /recipes for all // smaller Recipe type returned by backend at /recipes for all
interface RecipeSmall { interface RecipeSmall {
id: number; id: number;