added validation of recipes to editor

This commit is contained in:
Anika Raemer 2025-09-07 17:35:05 +02:00
parent 69b7920f23
commit 37057f19f1

View file

@ -14,11 +14,47 @@ type RecipeEditorProps = {
* ingredients (with amount, unit, name), instructions, and image URL. * ingredients (with amount, unit, name), instructions, and image URL.
*/ */
export default function RecipeEditor({ recipe, onSave, onCancel }: RecipeEditorProps) { export default function RecipeEditor({ recipe, onSave, onCancel }: RecipeEditorProps) {
/** draft of the new recipe */
const [draft, setDraft] = useState<Recipe>(recipe) const [draft, setDraft] = useState<Recipe>(recipe)
/** Error list */
const [errors, setErrors] = useState<{ title?: boolean; ingredients?: boolean }>({})
/**
* Update ingredients
* @param ingredients new ingredients
*/
const updateIngredients = (ingredients: Ingredient[]) => { const updateIngredients = (ingredients: Ingredient[]) => {
setDraft({ ...draft, ingredients }) setDraft({ ...draft, ingredients })
} }
/**
* Validate recipe
* @returns Information on the errors the validation encountered
*/
const validate = () => {
const newErrors: { title?: boolean; ingredients?: boolean } = {}
// each recipe requires a title
if (!draft.title.trim()) {
newErrors.title = true
}
// the incredient list must not be empty
// @todo enhance validation and visualization of ingredient errors
if (!draft.ingredients || draft.ingredients.length === 0) {
newErrors.ingredients = true
}
setErrors(newErrors)
return Object.keys(newErrors).length === 0
}
/** Handles saving and ensures that the draft is only saved if valid */
const handleSave = (draft: Recipe) => {
if (validate()) {
onSave(draft)
}
}
// ensure that there is a recipe and show error otherwise
if (!recipe) return <div>Oops, there's no recipe in RecipeEditor...</div> if (!recipe) return <div>Oops, there's no recipe in RecipeEditor...</div>
// @todo add handling of images // @todo add handling of images
return ( return (
@ -30,7 +66,7 @@ export default function RecipeEditor({ recipe, onSave, onCancel }: RecipeEditorP
{/* Title */} {/* Title */}
<h3 className="subsection-heading">Title</h3> <h3 className="subsection-heading">Title</h3>
<input <input
className="input-field" className={`input-field ${errors.title ? "border-red-500" : ""}`}
placeholder="Title" placeholder="Title"
value={draft.title} value={draft.title}
onChange={e => setDraft({ ...draft, title: e.target.value })} onChange={e => setDraft({ ...draft, title: e.target.value })}
@ -61,11 +97,13 @@ export default function RecipeEditor({ recipe, onSave, onCancel }: RecipeEditorP
}} }}
/> />
</div> </div>
{/* Ingredient List */} {/* Ingredient List - @todo better visualization of errors! */}
<div className={errors.ingredients ? "border border-red-500 rounded p-2" : ""}>
<IngredientListEditor <IngredientListEditor
ingredients={draft.ingredients} ingredients={draft.ingredients}
onChange={updateIngredients} onChange={updateIngredients}
/> />
</div>
<h3 className="subsection-heading">Instructions</h3> <h3 className="subsection-heading">Instructions</h3>
{/* Instructions */} {/* Instructions */}
@ -80,7 +118,7 @@ export default function RecipeEditor({ recipe, onSave, onCancel }: RecipeEditorP
{/* Save Button */} {/* Save Button */}
<button <button
className="primary-button" className="primary-button"
onClick={() => onSave(draft)} onClick={() => handleSave(draft)}
> >
Save Save
</button> </button>