lots of shopping list improvements

This commit is contained in:
vabene1111
2025-02-06 17:51:42 +01:00
parent 75eff42329
commit a868860b7d
18 changed files with 2006 additions and 115 deletions

View File

@@ -99,6 +99,7 @@ function loadRecipeData() {
amount: ingredient.amount,
food: ingredient.food,
unit: ingredient.unit,
ingredient: ingredient,
checked: (ingredient.food ? !(ingredient.food.ignoreShopping || ingredient.food.foodOnhand) : true),
})
})
@@ -132,7 +133,8 @@ function createShoppingListRecipe() {
shoppingListEntries.entries.push({
amount: entry.amount * (servings.value / (recipe.value.servings ? recipe.value.servings : 1)),
foodId: entry.food ? entry.food.id! : null,
unitId: entry.unit ? entry.unit.id! : null
unitId: entry.unit ? entry.unit.id! : null,
ingredientId: entry.ingredient ? entry.ingredient.id! : null,
})
}
})

View File

@@ -1,6 +1,6 @@
<template>
<v-dialog max-width="1400" :activator="dialogActivator" v-model="model">
<component :is="editorComponent" :item="item" @create="createEvent" @save="saveEvent" @delete="deleteEvent" dialog @close="model = false" :itemDefaults="itemDefaults"></component>
<component :is="editorComponent" :item="props.item" :item-id="props.itemId" @create="createEvent" @save="saveEvent" @delete="deleteEvent" dialog @close="model = false" :itemDefaults="props.itemDefaults"></component>
</v-dialog>
</template>
@@ -20,6 +20,7 @@ const props = defineProps({
model: { type: String as PropType<EditorSupportedModels>, required: true, },
activator: {default: 'parent'},
item: {default: null},
itemId: {type: [Number, String], required: false, default: undefined},
itemDefaults: {required: false},
disabledFields: {default: []},
closeAfterCreate: {default: true},

View File

@@ -50,42 +50,33 @@
</div>
<v-list density="compact">
<template v-for="[i, e] in shoppingListFood.entries" :key="e.id">
<v-list-item border class="mt-1" :class="{'cursor-pointer': !e.recipeMealplan}">
<v-list-item border class="mt-1">
<v-list-item-title>
<b>
{{ $n(e.amount) }}
<span v-if="e.unit">{{ e.unit.name }}</span>
<span v-if="e.amount != 0">{{ $n(e.amount) }}&nbsp;</span>
<span v-if="e.unit">{{ e.unit.name }}&nbsp;</span>
</b>
{{ e.food.name }}
<span v-if="e.food">
{{ e.food.name }}
</span>
</v-list-item-title>
<v-list-item-subtitle v-if="e.completedAt">
<v-icon icon="fa-solid fa-check" size="small" color="success"></v-icon>
{{ $t('Completed') }} {{ DateTime.fromJSDate(e.completedAt).toLocaleString(DateTime.DATETIME_SHORT) }}
</v-list-item-subtitle>
<v-list-item-subtitle v-if="e.recipeMealplan && e.recipeMealplan.recipeName !== ''">
{{ e.recipeMealplan.servings }} x
<router-link :to="{name: 'view_recipe', params: {id: e.recipeMealplan.id}}" target="_blank" class="text-decoration-none"><b>
{{ e.recipeMealplan.recipeName }} </b>
</router-link>
<v-list-item-subtitle v-if="e.listRecipe && e.listRecipeData.recipe">
{{ e.listRecipeData.servings }} x {{ e.listRecipeData.recipeData.name }}
</v-list-item-subtitle>
<v-list-item-subtitle v-if="e.recipeMealplan && e.recipeMealplan.mealplanType !== undefined">
{{ e.recipeMealplan.mealplanType }} {{ DateTime.fromJSDate(e.recipeMealplan.mealplanFromDate).toLocaleString(DateTime.DATE_SHORT) }}
<v-list-item-subtitle v-if="e.listRecipe && e.listRecipeData.mealplan">
{{ e.listRecipeData.mealPlanData.mealType.name }} {{ DateTime.fromJSDate(e.listRecipeData.mealPlanData.fromDate).toLocaleString(DateTime.DATE_SHORT) }}
</v-list-item-subtitle>
<v-list-item-subtitle>
{{ e.createdBy.displayName }} - {{ DateTime.fromJSDate(e.createdAt).toLocaleString(DateTime.DATETIME_SHORT) }}
</v-list-item-subtitle>
<v-list-item-subtitle v-if="isDelayed(e)" class="text-info font-weight-bold">
{{ $t('PostponedUntil') }} {{ DateTime.fromJSDate(e.delayUntil).toLocaleString(DateTime.DATETIME_SHORT) }}
{{ $t('PostponedUntil') }} {{ DateTime.fromJSDate(e.delayUntil!).toLocaleString(DateTime.DATETIME_SHORT) }}
</v-list-item-subtitle>
<!-- <template #append>-->
<!-- <v-btn size="small" color="edit" icon="$edit" v-if="!e.recipeMealplan">-->
<!-- <v-icon icon="$edit"></v-icon>-->
<!-- <model-edit-dialog model="ShoppingListEntry" :item="e" @delete="useShoppingStore().entries.delete(e.id); shoppingListFood.entries.delete(e.id)"-->
<!-- @save="(args: ShoppingListEntry) => (shoppingListFood.entries.set(e.id, args))"></model-edit-dialog>-->
<!-- </v-btn>-->
<!-- </template>-->
<v-btn-group divided border>
<v-btn icon="" @click="e.amount = e.amount / 2; updateEntryAmount(e)" v-if="!e.listRecipeData">
<v-icon icon="fa-solid fa-divide"></v-icon>
@@ -102,13 +93,15 @@
</v-btn>
<v-btn color="edit" icon="$edit" v-if="!e.listRecipeData">
<v-icon icon="$edit"></v-icon>
<model-edit-dialog model="ShoppingListEntry" :item="e" @delete="useShoppingStore().entries.delete(e.id); shoppingListFood.entries.delete(e.id)"
@save="(args: ShoppingListEntry) => (shoppingListFood.entries.set(e.id, args))"></model-edit-dialog>
<model-edit-dialog model="ShoppingListEntry" :item="e"
@delete="useShoppingStore().entries.delete(e.id!); shoppingListFood.entries.delete(e.id!)"
@save="(args: ShoppingListEntry) => { useShoppingStore().entries.set(e.id!, args); shoppingListFood.entries.set(e.id!, args) }"></model-edit-dialog>
</v-btn>
<v-btn color="edit" icon="$recipes" v-if="e.listRecipe && e.listRecipeData.recipe" :to="{name: 'view_recipe', params: {id: e.listRecipeData.recipe}}">
<v-btn color="edit" icon="$recipes" v-if="e.listRecipe && e.listRecipeData.recipe"
:to="{name: 'view_recipe', params: {id: e.listRecipeData.recipe}}">
<v-icon icon="$recipes"></v-icon>
</v-btn>
<v-btn icon="" @click="useShoppingStore().deleteObject(e, true); shoppingListFood.entries.delete(e.id)" color="delete">
<v-btn icon="" @click="useShoppingStore().deleteObject(e, true); shoppingListFood.entries.delete(e.id!)" color="delete">
<v-icon icon="$delete"></v-icon>
</v-btn>
</v-btn-group>

View File

@@ -1,6 +1,6 @@
<template>
<v-list-item class="swipe-container" :id="itemContainerId" @touchend="handleSwipe()" @click="emit('clicked', entries)"
v-if="(useUserPreferenceStore().deviceSettings.shopping_show_checked_entries || !isChecked) && (useUserPreferenceStore().deviceSettings.shopping_show_delayed_entries || !isShoppingLineDelayed)"
<v-list-item class="swipe-container" :id="itemContainerId" @touchend="handleSwipe()" @click="dialog = true;"
v-if="isShoppingListFoodVisible(props.shoppingListFood, useUserPreferenceStore().deviceSettings)"
>
<!-- <div class="swipe-action" :class="{'bg-success': !isChecked , 'bg-warning': isChecked }">-->
<!-- <i class="swipe-icon fa-fw fas" :class="{'fa-check': !isChecked , 'fa-cart-plus': isChecked }"></i>-->
@@ -44,6 +44,7 @@
</v-list-item>
<shopping-line-item-dialog v-model="dialog" v-model:shopping-list-food="props.shoppingListFood"></shopping-line-item-dialog>
</template>
<script setup lang="ts">
@@ -56,7 +57,8 @@ import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.js";
import {ApiApi, Food, ShoppingListEntry} from '@/openapi'
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
import {IShoppingListFood, ShoppingLineAmount} from "@/types/Shopping";
import {isDelayed, isShoppingListFoodDelayed} from "@/utils/logic_utils";
import {isDelayed, isShoppingListFoodDelayed, isShoppingListFoodVisible} from "@/utils/logic_utils";
import ShoppingLineItemDialog from "@/components/dialogs/ShoppingLineItemDialog.vue";
const emit = defineEmits(['clicked'])
@@ -65,6 +67,8 @@ const props = defineProps({
hideInfoRow: {type: Boolean, default: false}
})
const dialog = ref(false)
const entries = computed(() => {
return Array.from(props.shoppingListFood.entries.values())
})
@@ -93,13 +97,6 @@ const isChecked = computed(() => {
return true
})
/**
* determine if any entry in a given IShoppingListFood is delayed, if so return true
*/
const isShoppingLineDelayed = computed(() => {
return isShoppingListFoodDelayed(props.shoppingListFood)
})
/**
* style action button depending on if all items are checked or not
*/

View File

@@ -100,8 +100,7 @@
<v-divider></v-divider>
<template v-for="[i, value] in category.foods" :key="value.food.id">
<shopping-line-item :shopping-list-food="value"
@clicked="() => {shoppingLineItemDialog = true; shoppingLineItemDialogFood = value;}"></shopping-line-item>
<shopping-line-item :shopping-list-food="value" ></shopping-line-item>
</template>
</template>
@@ -164,15 +163,20 @@
<v-list>
<v-list-item v-for="r in useShoppingStore().getAssociatedRecipes()">
<template #prepend>
<v-btn color="edit" icon :disabled="r.mealplan">
<v-btn color="edit" icon >
{{ r.servings }}
<number-scaler-dialog :number="r.servings"
@confirm="(servings: number) => {updateRecipeServings(r, servings)}"></number-scaler-dialog>
<number-scaler-dialog
v-if="r.mealplan == undefined"
:number="r.servings"
@confirm="(servings: number) => {updateRecipeServings(r, servings)}"
></number-scaler-dialog>
<model-edit-dialog model="MealPlan" :item-id="r.mealplan" v-if="r.mealplan != undefined" activator="parent"> </model-edit-dialog>
</v-btn>
</template>
<div class="ms-2">
<p v-if="r.recipe">{{ r.recipeData.name }} <br/></p>
<p v-if="r.recipe">{{ r.recipeData.name }}<br/></p>
<p v-if="r.mealplan">
{{ r.mealPlanData.mealType.name }} - {{ DateTime.fromJSDate(r.mealPlanData.fromDate).toLocaleString(DateTime.DATE_FULL) }}
</p>
@@ -181,7 +185,7 @@
<template #append>
<v-btn icon color="delete">
<v-icon icon="$delete"></v-icon>
<delete-confirm-dialog :object-name="r.recipeName" :model-name="$t('ShoppingListRecipe')"
<delete-confirm-dialog :object-name="r.name" :model-name="$t('ShoppingListRecipe')"
@delete="deleteListRecipe(r)"></delete-confirm-dialog>
</v-btn>
</template>
@@ -198,8 +202,8 @@
<v-container>
<v-row>
<v-col>
<SupermarketEditor :item="useUserPreferenceStore().deviceSettings.shopping_selected_supermarket"
@save="(args: Supermarket) => (useUserPreferenceStore().deviceSettings.shopping_selected_supermarket = args)"></SupermarketEditor>
<supermarket-editor :item="useUserPreferenceStore().deviceSettings.shopping_selected_supermarket"
@save="(args: Supermarket) => (useUserPreferenceStore().deviceSettings.shopping_selected_supermarket = args)"></supermarket-editor>
</v-col>
</v-row>
</v-container>
@@ -207,8 +211,6 @@
</v-window-item>
</v-window>
<shopping-line-item-dialog v-model="shoppingLineItemDialog" v-model:shopping-list-food="shoppingLineItemDialogFood"></shopping-line-item-dialog>
</template>
<script setup lang="ts">
@@ -228,14 +230,13 @@ import SupermarketEditor from "@/components/model_editors/SupermarketEditor.vue"
import DeleteConfirmDialog from "@/components/dialogs/DeleteConfirmDialog.vue";
import ShoppingListEntryInput from "@/components/inputs/ShoppingListEntryInput.vue";
import {DateTime} from "luxon";
import MealPlanEditor from "@/components/model_editors/MealPlanEditor.vue";
import ModelEditDialog from "@/components/dialogs/ModelEditDialog.vue";
const {t} = useI18n()
const currentTab = ref("shopping")
const shoppingLineItemDialog = ref(false)
const shoppingLineItemDialogFood = ref({} as IShoppingListFood)
/**
* VSelect items for shopping list grouping options with localized names
*/