From a2b6cbf6ab1c1c760b7debb2337114df0771520b Mon Sep 17 00:00:00 2001
From: Felix Schneider
Date: Fri, 1 Aug 2025 11:20:57 +0200
Subject: [PATCH] feat: group recipe ingredients by section titles
Group ingredients under their respective section titles in the recipe page, improving organization and clarity. Added logic to compute and map section titles for used and unused ingredients. Updated related components to display grouped ingredients accordingly.
---
.../RecipePageInstructions.vue | 110 +++++++++++++++---
.../recipes/use-recipe-ingredients.ts | 3 +-
frontend/lang/messages/en-US.json | 1 +
3 files changed, 99 insertions(+), 15 deletions(-)
diff --git a/frontend/components/Domain/Recipe/RecipePage/RecipePageParts/RecipePageInstructions.vue b/frontend/components/Domain/Recipe/RecipePage/RecipePageParts/RecipePageInstructions.vue
index 2867b9d62..9b2076b3f 100644
--- a/frontend/components/Domain/Recipe/RecipePage/RecipePageParts/RecipePageInstructions.vue
+++ b/frontend/components/Domain/Recipe/RecipePage/RecipePageParts/RecipePageInstructions.vue
@@ -29,32 +29,49 @@
{{ activeText }}
-
-
-
-
-
-
-
+
- {{ $t("recipe.linked-to-other-step") }}
+ {{ $t("recipe.unlinked") }}
+
+
+
+ {{ title }}
+
+
+
+
+ {{ $t("recipe.linked-to-other-step") }}
+
+
+
+ {{ title }}
+
+
+
+
+
+
+
+
@@ -563,6 +580,71 @@ const ingredientLookup = computed(() => {
}, results);
});
+// Map each ingredient's referenceId to its section title
+const ingredientSectionTitles = computed(() => {
+ const titleMap: { [key: string]: string } = {};
+ let currentTitle = "";
+
+ // Go through all ingredients in order
+ props.recipe.recipeIngredient.forEach((ingredient) => {
+ if (ingredient.referenceId === undefined) {
+ return;
+ }
+
+ // If this ingredient has a title, update the current title
+ if (ingredient.title) {
+ currentTitle = ingredient.title;
+ }
+
+ // Assign the current title to this ingredient
+ titleMap[ingredient.referenceId] = currentTitle;
+ });
+
+ return titleMap;
+});
+
+const groupedUnusedIngredients = computed(() => {
+ const groups: { [key: string]: RecipeIngredient[] } = {};
+
+ // Group ingredients by section title
+ unusedIngredients.value.forEach((ingredient) => {
+ if (ingredient.referenceId === undefined) {
+ return;
+ }
+
+ // Use the section title from the mapping, or fallback to the ingredient's own title
+ const title = ingredientSectionTitles.value[ingredient.referenceId] || ingredient.title || "";
+
+ if (!groups[title]) {
+ groups[title] = [];
+ }
+ groups[title].push(ingredient);
+ });
+
+ return groups;
+});
+
+const groupedUsedIngredients = computed(() => {
+ const groups: { [key: string]: RecipeIngredient[] } = {};
+
+ // Group ingredients by section title
+ usedIngredients.value.forEach((ingredient) => {
+ if (ingredient.referenceId === undefined) {
+ return;
+ }
+
+ // Use the section title from the mapping, or fallback to the ingredient's own title
+ const title = ingredientSectionTitles.value[ingredient.referenceId] || ingredient.title || "";
+
+ if (!groups[title]) {
+ groups[title] = [];
+ }
+ groups[title].push(ingredient);
+ });
+
+ return groups;
+});
+
function getIngredientByRefId(refId: string | undefined) {
if (refId === undefined) {
return "";
diff --git a/frontend/composables/recipes/use-recipe-ingredients.ts b/frontend/composables/recipes/use-recipe-ingredients.ts
index 867fed639..00ee9f55b 100644
--- a/frontend/composables/recipes/use-recipe-ingredients.ts
+++ b/frontend/composables/recipes/use-recipe-ingredients.ts
@@ -37,7 +37,7 @@ function useUnitName(unit: CreateIngredientUnit | IngredientUnit | undefined, us
}
export function useParsedIngredientText(ingredient: RecipeIngredient, scale = 1, includeFormating = true) {
- const { quantity, food, unit, note } = ingredient;
+ const { quantity, food, unit, note, title } = ingredient;
const usePluralUnit = quantity !== undefined && ((quantity || 0) * scale > 1 || (quantity || 0) * scale === 0);
const usePluralFood = (!quantity) || quantity * scale > 1;
@@ -66,6 +66,7 @@ export function useParsedIngredientText(ingredient: RecipeIngredient, scale = 1,
const foodName = useFoodName(food || undefined, usePluralFood);
return {
+ title: title ? sanitizeIngredientHTML(title) : undefined,
quantity: returnQty ? sanitizeIngredientHTML(returnQty) : undefined,
unit: unitName && quantity ? sanitizeIngredientHTML(unitName) : undefined,
name: foodName ? sanitizeIngredientHTML(foodName) : undefined,
diff --git a/frontend/lang/messages/en-US.json b/frontend/lang/messages/en-US.json
index 08b98b58c..4193f54e2 100644
--- a/frontend/lang/messages/en-US.json
+++ b/frontend/lang/messages/en-US.json
@@ -561,6 +561,7 @@
"see-original-text": "See Original Text",
"original-text-with-value": "Original Text: {originalText}",
"ingredient-linker": "Ingredient Linker",
+ "unlinked": "Not linked yet",
"linked-to-other-step": "Linked to other step",
"auto": "Auto",
"cook-mode": "Cook Mode",