add calculation of ingredients based on servings
This commit is contained in:
parent
fee47da55d
commit
e467ca7e92
5 changed files with 63 additions and 14 deletions
|
|
@ -59,7 +59,7 @@
|
|||
|
||||
/* input field */
|
||||
.input-field {
|
||||
@apply border p-2 w-full mb-2 rounded placeholder-gray-400;
|
||||
@apply border p-2 w-full rounded placeholder-gray-400;
|
||||
}
|
||||
|
||||
.text-area {
|
||||
|
|
@ -72,7 +72,7 @@
|
|||
}
|
||||
|
||||
/* lists */
|
||||
.default-list-item {
|
||||
.default-list {
|
||||
@apply list-disc pl-6 mb-6
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { useParams, Link } from "react-router-dom"
|
||||
import { recipes } from "../mock_data/recipes"
|
||||
import type { Recipe } from "../types/recipe"
|
||||
import { useState } from "react"
|
||||
|
||||
|
||||
/**
|
||||
* Displays the full detail of a single recipe,
|
||||
|
|
@ -10,28 +13,66 @@ export default function RecipeDetailView() {
|
|||
const { id } = useParams<{ id: string }>()
|
||||
const recipe = recipes.find((r) => r.id === id)
|
||||
|
||||
|
||||
if (!recipe) {
|
||||
return <p className="p-6">Recipe not found.</p>
|
||||
}
|
||||
|
||||
// Working copy for re-calculating ingredients
|
||||
const [recipeWorkingCopy, updateRecipeWorkingCopy] = useState<Recipe>(recipe)
|
||||
|
||||
const recalculateIngredients = (newAmount: number) => {
|
||||
// Always calculate factor from the *original recipe*, not the working copy
|
||||
const factor = newAmount / recipe.servings.amount
|
||||
|
||||
// Create a new ingredient list with updated amounts
|
||||
const updatedIngredients = recipe.ingredients.map((ing) => ({
|
||||
...ing,
|
||||
amount: ing.amount * factor,
|
||||
}))
|
||||
|
||||
// Update working copy with new servings + recalculated ingredients
|
||||
updateRecipeWorkingCopy({
|
||||
...recipeWorkingCopy,
|
||||
servings: {
|
||||
...recipeWorkingCopy.servings,
|
||||
amount: newAmount,
|
||||
},
|
||||
ingredients: updatedIngredients,
|
||||
})
|
||||
}
|
||||
// @todo add a feature to recalculate ingredients based on servings
|
||||
return (
|
||||
<div className="p-6 max-w-2xl mx-auto">
|
||||
<h1 className="content-title">{recipe.title}</h1>
|
||||
<h1 className="content-title">{recipeWorkingCopy.title}</h1>
|
||||
|
||||
{/* Recipe image */}
|
||||
{recipe.imageUrl && (
|
||||
{recipeWorkingCopy.imageUrl && (
|
||||
<img
|
||||
src={recipe.imageUrl}
|
||||
alt={recipe.title}
|
||||
className="w-full rounded-xl mb-4"
|
||||
src={recipeWorkingCopy.imageUrl}
|
||||
alt={recipeWorkingCopy.title}
|
||||
className="w-full rounded-xl mb-4 border"
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Servings */}
|
||||
<div className="flex flex-row items-center gap-2 bg-blue-100 columns-2 rounded p-2 mb-4">
|
||||
<p className="mb-2">For {recipeWorkingCopy.servings.amount} {recipeWorkingCopy.servings.unit}</p>
|
||||
<input
|
||||
type="number"
|
||||
className="input-field w-20 ml-auto"
|
||||
value={recipeWorkingCopy.servings.amount}
|
||||
onChange={
|
||||
e => {
|
||||
recalculateIngredients(Number(e.target.value))
|
||||
}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{/* Ingredients */}
|
||||
<h2 className="section-heading">Zutaten</h2>
|
||||
<p>For {recipe.servings.amount} {recipe.servings.unit}</p>
|
||||
<ul className="default-list-item">
|
||||
{recipe.ingredients.map((ing, i) => (
|
||||
<ul className="default-list">
|
||||
{recipeWorkingCopy.ingredients.map((ing, i) => (
|
||||
<li key={i}>
|
||||
{ing.amount} {ing.unit ?? ""} {ing.name}
|
||||
</li>
|
||||
|
|
@ -40,7 +81,7 @@ export default function RecipeDetailView() {
|
|||
|
||||
{/* Instructions */}
|
||||
<h2 className="section-heading">Zubereitung</h2>
|
||||
<p className="mb-6">{recipe.instructions}</p>
|
||||
<p className="mb-6">{recipeWorkingCopy.instructions}</p>
|
||||
|
||||
{/* Action buttons */}
|
||||
<div className="button-group">
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ export default function RecipeEditor({ recipe, onSave, onCancel }: RecipeEditorP
|
|||
setDraft({ ...draft, ingredients })
|
||||
}
|
||||
if (!recipe) return <div>Oops, there's no recipe in RecipeEditor...</div>
|
||||
// @todo add handling of images
|
||||
return (
|
||||
<div className="p-4 gap-10">
|
||||
<h2 className="content-title">
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export const recipes: Recipe[] = [
|
|||
{ name: "Tomato Sauce", amount: 400, unit: "ml" }
|
||||
],
|
||||
instructions: "Cook pasta. Prepare sauce. Mix together. Serve hot.",
|
||||
imageUrl: "https://source.unsplash.com/400x300/?spaghetti"
|
||||
//imageUrl: "https://source.unsplash.com/400x300/?spaghetti"
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
|
|
@ -30,6 +30,6 @@ export const recipes: Recipe[] = [
|
|||
{ name: "Olives", amount: 100, unit: "g"}
|
||||
],
|
||||
instructions: "Cook pasta. Prepare sauce. Mix together. Serve hot.",
|
||||
imageUrl: "https://source.unsplash.com/400x300/?spaghetti"
|
||||
//imageUrl: "https://source.unsplash.com/400x300/?spaghetti"
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -4,6 +4,13 @@ import type { Servings } from "./servings"
|
|||
/**
|
||||
* Represents a recipe object in the application.
|
||||
*/
|
||||
/*
|
||||
* @todo ingredient groups! There may be serveral ingredient lists, each with a title.
|
||||
* e.g. for the dough, for the filling, for the icing,...
|
||||
* - add type ingredient group with an optional title and a list of ingredients
|
||||
* - adapt RecipeDetailView
|
||||
* - add an IngredientGroupListEditor for handling IngredientGroups
|
||||
*/
|
||||
export interface Recipe {
|
||||
/** Unique identifier for the recipe */
|
||||
id: string
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue