diff --git a/web/src/components/forms/IngredientItem.tsx b/web/src/components/forms/IngredientItem.tsx
new file mode 100644
index 0000000..5a83c3e
--- /dev/null
+++ b/web/src/components/forms/IngredientItem.tsx
@@ -0,0 +1,17 @@
+import type { RecipeIngredient } from "../../types/recipe";
+
+interface IngredientItemProps {
+ ingredient: RecipeIngredient;
+ onChange: (id: string, name: string) => void;
+}
+
+export default function IngredientItem({ ingredient, onChange }: IngredientItemProps) {
+ return (
+ onChange(ingredient.Id, e.target.value)}
+ placeholder="Ingredient name"
+ />
+ );
+}
diff --git a/web/src/components/forms/IngredientList.tsx b/web/src/components/forms/IngredientList.tsx
new file mode 100644
index 0000000..160506b
--- /dev/null
+++ b/web/src/components/forms/IngredientList.tsx
@@ -0,0 +1,59 @@
+import type { Dispatch, SetStateAction } from "react";
+import { type IngredientId, type IngredientsById, type RecipeIngredient, type RecipeIngredientSection, type SectionsById } from "../../types/recipe";
+import IngredientSectionElement from "./IngredientSectionElement";
+import { Reorder } from "motion/react";
+
+
+interface IngredientListProps {
+}
+
+export default function IngredientList({ sectionOrder, setSectionOrder, sectionsById, ingredientsById, setIngredientsById }: IngredientListProps) {
+ const handleIngredientChange = (
+ ingredientId: IngredientId,
+ field: "Name" | "Amount" | "Unit",
+ value: string
+ ) => {
+ setIngredientsById(prev => {
+ const ing = prev[ingredientId];
+ if (!ing) return prev;
+
+ const updated: RecipeIngredient = {
+ ...ing,
+ [field]:
+ field === "Amount"
+ ? (value === "" ? 0 : Number(value))
+ : value,
+ };
+
+ // If nothing changed, keep same reference
+ if (updated === ing) return prev;
+
+ return { ...prev, [ingredientId]: updated };
+ });
+ };
+
+ return (
+ <>
+
+ {sectionOrder.map(sectionId => {
+ const section = sectionsById[sectionId];
+ console.log("@section", section);
+ return (
+
+
+
+
+ )
+ })}
+
+ >
+ );
+}
diff --git a/web/src/components/forms/IngredientSection.tsx b/web/src/components/forms/IngredientSection.tsx
new file mode 100644
index 0000000..522a8a4
--- /dev/null
+++ b/web/src/components/forms/IngredientSection.tsx
@@ -0,0 +1,36 @@
+import type { DragControls } from "motion/react";
+import type { RecipeIngredientSection } from "../../types/recipe";
+import DeleteIconSmall from "../icons/DeleteIconSmall";
+import DragIconSmall from "../icons/DragIconSmall";
+
+interface IngredientSectionProps {
+ section: RecipeIngredientSection;
+ onChange: (id: string, name: string) => void;
+ index: number;
+ controls: DragControls;
+};
+
+export default function IngredientSection({ section, onChange, index, controls }: IngredientSectionProps) {
+ return (
+
+
Group:
+
onChange(section.Id, e.target.value)}
+ placeholder="Section title"
+ className="mx-2 px-2 py-1 border border-gray-300 flex-grow rounded-sm"
+ />
+
+
+
+
controls.start(e)}>
+
+
+
+
+
+ );
+}
diff --git a/web/src/components/forms/InstructionElement.tsx b/web/src/components/forms/InstructionElement.tsx
index 933ca95..919143f 100644
--- a/web/src/components/forms/InstructionElement.tsx
+++ b/web/src/components/forms/InstructionElement.tsx
@@ -1,10 +1,11 @@
import { useEffect, useState, type ChangeEvent } from "react";
-import type { Instruction } from "../../pages/Create";
+import type { RecipeInstruction } from "../../types/recipe";
import { Reorder, useDragControls } from "motion/react";
import DragIconSmall from "../icons/DragIconSmall";
+import DeleteIconSmall from "../icons/DeleteIconSmall";
interface InstructionElementProps {
- instruction: Instruction;
+ instruction: RecipeInstruction;
index: number;
allowDelete: boolean;
onChange: (id: string, value: string) => void;
@@ -22,13 +23,13 @@ export default function InstructionElement({ instruction, index, allowDelete, on
// No need to set many times
if (!dirty) setDirty(true);
- onChange(instruction.id, e.target.value);
+ onChange(instruction.Id, e.target.value);
}
// EFFECTS
useEffect(() => {
if (dirty)
- setValid(instruction.content !== "");
+ setValid(instruction.Content !== "");
}, [dirty, instruction]);
return (
@@ -44,7 +45,7 @@ export default function InstructionElement({ instruction, index, allowDelete, on