@@ -75,7 +101,8 @@ import ShoppingListItemEditor from "./ShoppingListItemEditor.vue";
import MultiPurposeLabel from "./MultiPurposeLabel.vue";
import { ShoppingListItemOut } from "~/lib/api/types/group";
import { MultiPurposeLabelOut, MultiPurposeLabelSummary } from "~/lib/api/types/labels";
-import { IngredientFood, IngredientUnit } from "~/lib/api/types/recipe";
+import { IngredientFood, IngredientUnit, RecipeSummary } from "~/lib/api/types/recipe";
+import RecipeList from "~/components/Domain/Recipe/RecipeList.vue";
interface actions {
text: string;
@@ -105,10 +132,15 @@ export default defineComponent({
type: Array as () => IngredientFood[],
required: true,
},
+ recipes: {
+ type: Map,
+ default: undefined,
+ }
},
setup(props, context) {
const { i18n } = useContext();
- const itemLabelCols = ref(props.value.checked ? "auto" : props.showLabel ? "6" : "8");
+ const displayRecipeRefs = ref(false);
+ const itemLabelCols = ref(props.value.checked ? "auto" : props.showLabel ? "4" : "6");
const contextMenu: actions[] = [
{
@@ -190,16 +222,34 @@ export default defineComponent({
return undefined;
});
+ const recipeList = computed(() => {
+ const recipeList: RecipeSummary[] = [];
+ if (!listItem.value.recipeReferences) {
+ return recipeList;
+ }
+
+ listItem.value.recipeReferences.forEach((ref) => {
+ const recipe = props.recipes.get(ref.recipeId)
+ if (recipe) {
+ recipeList.push(recipe);
+ }
+ });
+
+ return recipeList;
+ });
+
return {
updatedLabels,
save,
contextHandler,
+ displayRecipeRefs,
edit,
contextMenu,
itemLabelCols,
listItem,
localListItem,
label,
+ recipeList,
toggleEdit,
};
},
diff --git a/frontend/lib/api/types/group.ts b/frontend/lib/api/types/group.ts
index a0dd0517e00..42fb45a8546 100644
--- a/frontend/lib/api/types/group.ts
+++ b/frontend/lib/api/types/group.ts
@@ -359,6 +359,7 @@ export interface ShoppingListItemRecipeRefCreate {
recipeId: string;
recipeQuantity?: number;
recipeScale?: number;
+ recipeNote?: string;
}
export interface ShoppingListItemOut {
quantity?: number;
@@ -387,6 +388,7 @@ export interface ShoppingListItemRecipeRefOut {
recipeId: string;
recipeQuantity?: number;
recipeScale?: number;
+ recipeNote?: string;
id: string;
shoppingListItemId: string;
}
@@ -394,6 +396,7 @@ export interface ShoppingListItemRecipeRefUpdate {
recipeId: string;
recipeQuantity?: number;
recipeScale?: number;
+ recipeNote?: string;
id: string;
shoppingListItemId: string;
}
diff --git a/frontend/pages/shopping-lists/_id.vue b/frontend/pages/shopping-lists/_id.vue
index f80f1ff1a9d..7452ecc8fe3 100644
--- a/frontend/pages/shopping-lists/_id.vue
+++ b/frontend/pages/shopping-lists/_id.vue
@@ -19,6 +19,7 @@
:labels="allLabels || []"
:units="allUnits || []"
:foods="allFoods || []"
+ :recipes="recipeMap"
@checked="saveListItem"
@save="saveListItem"
@delete="deleteListItem(item)"
@@ -46,6 +47,7 @@
:labels="allLabels || []"
:units="allUnits || []"
:foods="allFoods || []"
+ :recipes="recipeMap"
@checked="saveListItem"
@save="saveListItem"
@delete="deleteListItem(item)"
@@ -175,8 +177,8 @@
{{ $tc('shopping-list.linked-recipes-count', shoppingList.recipeReferences ? shoppingList.recipeReferences.length : 0) }}
-
-
+
+
{{ $globals.icons.minus }}
@@ -530,9 +532,10 @@ export default defineComponent({
// =====================================
// Add/Remove Recipe References
- const listRecipes = computed>(() => {
- return shoppingList.value?.recipeReferences?.map((ref) => ref.recipe) ?? [];
- });
+ const recipeMap = computed(() => new Map(
+ (shoppingList.value?.recipeReferences?.map((ref) => ref.recipe) ?? [])
+ .map((recipe) => [recipe.id || "", recipe]))
+ );
async function addRecipeReferenceToList(recipeId: string) {
if (!shoppingList.value || recipeReferenceLoading.value) {
@@ -741,10 +744,10 @@ export default defineComponent({
getLabelColor,
itemsByLabel,
listItems,
- listRecipes,
loadingCounter,
preferences,
presentLabels,
+ recipeMap,
removeRecipeReferenceToList,
reorderLabelsDialog,
toggleReorderLabelsDialog,
diff --git a/mealie/db/models/group/shopping_list.py b/mealie/db/models/group/shopping_list.py
index 01c0086bdbd..e111537063e 100644
--- a/mealie/db/models/group/shopping_list.py
+++ b/mealie/db/models/group/shopping_list.py
@@ -5,11 +5,7 @@
from sqlalchemy.orm import Mapped, mapped_column
from mealie.db.models.labels import MultiPurposeLabel
-from mealie.db.models.recipe.api_extras import (
- ShoppingListExtras,
- ShoppingListItemExtras,
- api_extras,
-)
+from mealie.db.models.recipe.api_extras import ShoppingListExtras, ShoppingListItemExtras, api_extras
from .._model_base import BaseMixins, SqlAlchemyBase
from .._model_utils import GUID, auto_init
@@ -30,6 +26,7 @@ class ShoppingListItemRecipeReference(BaseMixins, SqlAlchemyBase):
recipe: Mapped[Optional["RecipeModel"]] = orm.relationship("RecipeModel", back_populates="shopping_list_item_refs")
recipe_quantity: Mapped[float] = mapped_column(Float, nullable=False)
recipe_scale: Mapped[float | None] = mapped_column(Float, default=1)
+ recipe_note: Mapped[str | None] = mapped_column(String)
@auto_init()
def __init__(self, **_) -> None:
diff --git a/mealie/schema/group/group_shopping_list.py b/mealie/schema/group/group_shopping_list.py
index 82d4c27d1b1..546b47a5c1d 100644
--- a/mealie/schema/group/group_shopping_list.py
+++ b/mealie/schema/group/group_shopping_list.py
@@ -35,6 +35,9 @@ class ShoppingListItemRecipeRefCreate(MealieModel):
recipe_scale: NoneFloat = 1
"""the number of times this recipe has been added"""
+ recipe_note: str | None = None
+ """the original note from the recipe"""
+
@validator("recipe_quantity", pre=True)
def default_none_to_zero(cls, v):
return 0 if v is None else v
diff --git a/mealie/services/group_services/shopping_lists.py b/mealie/services/group_services/shopping_lists.py
index 99e088d2b97..1315e796ef6 100644
--- a/mealie/services/group_services/shopping_lists.py
+++ b/mealie/services/group_services/shopping_lists.py
@@ -17,11 +17,7 @@
ShoppingListMultiPurposeLabelCreate,
ShoppingListSave,
)
-from mealie.schema.recipe.recipe_ingredient import (
- IngredientFood,
- IngredientUnit,
- RecipeIngredient,
-)
+from mealie.schema.recipe.recipe_ingredient import IngredientFood, IngredientUnit, RecipeIngredient
from mealie.schema.response.pagination import OrderDirection, PaginationQuery
from mealie.schema.user.user import GroupInDB, PrivateUser
@@ -68,6 +64,11 @@ def merge_items(
if to_item.note != from_item.note:
to_item.note = " | ".join([note for note in [to_item.note, from_item.note] if note])
+ if from_item.note and to_item.note != from_item.note:
+ notes: set[str] = set(to_item.note.split(" | ")) if to_item.note else set()
+ notes.add(from_item.note)
+ to_item.note = " | ".join([note for note in notes if note])
+
if to_item.extras and from_item.extras:
to_item.extras.update(from_item.extras)
@@ -318,7 +319,10 @@ def get_shopping_list_items_from_recipe(
unit_id=unit_id,
recipe_references=[
ShoppingListItemRecipeRefCreate(
- recipe_id=recipe_id, recipe_quantity=ingredient.quantity, recipe_scale=scale
+ recipe_id=recipe_id,
+ recipe_quantity=ingredient.quantity,
+ recipe_scale=scale,
+ recipe_note=ingredient.note or None,
)
],
)
@@ -336,8 +340,10 @@ def get_shopping_list_items_from_recipe(
existing_item.recipe_references[0].recipe_quantity += ingredient.quantity # type: ignore
# merge notes
- if existing_item.note != new_item.note:
- existing_item.note = " | ".join([note for note in [existing_item.note, new_item.note] if note])
+ if new_item.note and existing_item.note != new_item.note:
+ notes: set[str] = set(existing_item.note.split(" | ")) if existing_item.note else set()
+ notes.add(new_item.note)
+ existing_item.note = " | ".join([note for note in notes if note])
merged = True
break
From 1e693fdca64d51116e8afe3ae8d1f98d3381c150 Mon Sep 17 00:00:00 2001
From: Michael Genson <71845777+michael-genson@users.noreply.github.com>
Date: Mon, 21 Aug 2023 12:18:55 -0500
Subject: [PATCH 05/14] fix: refresh tag store when new tags are imported
(#2504)
---
frontend/pages/recipe/create/url.vue | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/frontend/pages/recipe/create/url.vue b/frontend/pages/recipe/create/url.vue
index 1040f3663f4..fb3f53bbfb3 100644
--- a/frontend/pages/recipe/create/url.vue
+++ b/frontend/pages/recipe/create/url.vue
@@ -74,6 +74,7 @@ import {
} from "@nuxtjs/composition-api";
import { AxiosResponse } from "axios";
import { useUserApi } from "~/composables/api";
+import { useTagStore } from "~/composables/store/use-tag-store";
import { validators } from "~/composables/use-validators";
import { VForm } from "~/types/vuetify";
@@ -87,13 +88,17 @@ export default defineComponent({
const api = useUserApi();
const route = useRoute();
const router = useRouter();
+ const tags = useTagStore();
- function handleResponse(response: AxiosResponse | null, edit = false) {
+ function handleResponse(response: AxiosResponse | null, edit = false, refreshTags = false) {
if (response?.status !== 201) {
state.error = true;
state.loading = false;
return;
}
+ if (refreshTags) {
+ tags.actions.refresh();
+ }
router.push(`/recipe/${response.data}?edit=${edit.toString()}`);
}
@@ -150,7 +155,7 @@ export default defineComponent({
}
state.loading = true;
const { response } = await api.recipes.createOneByUrl(url, importKeywordsAsTags);
- handleResponse(response, stayInEditMode);
+ handleResponse(response, stayInEditMode, importKeywordsAsTags);
}
return {
From 99e7717fec3495cf1e6c8476b8a1915a3d446233 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ars=C3=A8ne=20Reymond?=
<66876397+p0lycarpio@users.noreply.github.com>
Date: Mon, 21 Aug 2023 20:41:18 +0200
Subject: [PATCH 06/14] UI/UX improvements (#2423)
* disable autofocus and hide keyboard on enter
* improve a11y
* fix non-translated string
* improve recipeTimeline UI
* format
* fixes
---
.../Domain/Recipe/RecipeCardMobile.vue | 25 ++++--
.../Domain/Recipe/RecipeContextMenu.vue | 2 +-
.../Domain/Recipe/RecipePage/RecipePage.vue | 2 +-
.../Domain/Recipe/RecipeTimeline.vue | 7 +-
.../Recipe/RecipeTimelineContextMenu.vue | 6 +-
.../Domain/Recipe/RecipeTimelineItem.vue | 77 +++++++++++--------
frontend/components/Layout/AppHeader.vue | 4 +-
frontend/lang/messages/en-US.json | 3 +-
frontend/pages/index.vue | 13 +++-
frontend/pages/login.vue | 9 ++-
10 files changed, 91 insertions(+), 57 deletions(-)
diff --git a/frontend/components/Domain/Recipe/RecipeCardMobile.vue b/frontend/components/Domain/Recipe/RecipeCardMobile.vue
index c729b874c87..7cfcb91970a 100644
--- a/frontend/components/Domain/Recipe/RecipeCardMobile.vue
+++ b/frontend/components/Domain/Recipe/RecipeCardMobile.vue
@@ -2,24 +2,24 @@
-
+
-
+
-
+
-
- {{ name }}
+
+ {{ name }}
@@ -120,7 +120,11 @@ export default defineComponent({
vertical: {
type: Boolean,
default: false,
- }
+ },
+ isFlat: {
+ type: Boolean,
+ default: false,
+ },
},
setup() {
const { $auth } = useContext();
@@ -162,4 +166,9 @@ export default defineComponent({
.text-top {
align-self: start !important;
}
+
+.flat {
+ box-shadow: none!important;
+ background-color: transparent;
+}
diff --git a/frontend/components/Domain/Recipe/RecipeContextMenu.vue b/frontend/components/Domain/Recipe/RecipeContextMenu.vue
index 32ad0fb1e4f..9134e893470 100644
--- a/frontend/components/Domain/Recipe/RecipeContextMenu.vue
+++ b/frontend/components/Domain/Recipe/RecipeContextMenu.vue
@@ -149,7 +149,7 @@
:nudge-top="menuTop ? '5' : '0'"
allow-overflow
close-delay="125"
- open-on-hover
+ :open-on-hover="$vuetify.breakpoint.mdAndUp"
content-class="d-print-none"
>
diff --git a/frontend/components/Domain/Recipe/RecipePage/RecipePage.vue b/frontend/components/Domain/Recipe/RecipePage/RecipePage.vue
index 6e297a01d1e..2d0fc87b7dc 100644
--- a/frontend/components/Domain/Recipe/RecipePage/RecipePage.vue
+++ b/frontend/components/Domain/Recipe/RecipePage/RecipePage.vue
@@ -62,7 +62,7 @@
class="d-print-none d-flex px-2"
:class="$vuetify.breakpoint.smAndDown ? 'justify-center' : 'justify-end'"
>
-
+