This commit is contained in:
vabene1111
2024-10-27 14:40:33 +01:00
parent 77748a951b
commit ba401877e8
5 changed files with 109 additions and 29 deletions

View File

@@ -10,13 +10,13 @@
<v-row> <v-row>
<v-col class="pr-0"> <v-col class="pr-0">
<v-btn height="80px" color="info" density="compact" size="small" block stacked> <v-btn height="80px" color="info" density="compact" size="small" block stacked @click="useShoppingStore().delayEntries(entriesList, !isDelayed, true); ">
<i class="fa-solid fa-clock-rotate-left fa-2x mb-2"></i> <i class="fa-solid fa-clock-rotate-left fa-2x mb-2"></i>
{{ $t('Postpone') }} {{ $t('Postpone') }} {{isDelayed}}
</v-btn> </v-btn>
</v-col> </v-col>
<v-col> <v-col>
<v-btn height="80px" color="secondary" density="compact" size="small" block stacked> <v-btn height="80px" color="secondary" density="compact" size="small" block stacked @click="useShoppingStore().setFoodIgnoredState(entriesList,true, true); showDialog = false">
<i class="fa-solid fa-eye-slash fa-2x mb-2"></i> <i class="fa-solid fa-eye-slash fa-2x mb-2"></i>
{{ $t('Ignore_Shopping') }} {{ $t('Ignore_Shopping') }}
</v-btn> </v-btn>
@@ -24,7 +24,7 @@
</v-row> </v-row>
<v-row> <v-row>
<v-col class="pr-0 pt-0"> <v-col class="pr-0 pt-0">
<v-btn height="80px" color="primary" density="compact" size="small" block stacked> <v-btn height="80px" color="primary" density="compact" size="small" :to="{name: 'ModelEditPage', params: {model: 'Food', id: props.shoppingListFood?.food.id!}}" target="_blank" block stacked>
<i class="fa-solid fa-pencil fa-2x mb-2"></i> <i class="fa-solid fa-pencil fa-2x mb-2"></i>
{{ $t('Edit_Food') }} {{ $t('Edit_Food') }}
</v-btn> </v-btn>
@@ -67,7 +67,7 @@
</v-btn> </v-btn>
</template> </template>
<!-- TODO make properly reactive or delete from the food instance in this component as well--> <!-- TODO make properly reactive or delete from the food instance in this component as well | ADD functionality once reactive -->
<model-edit-dialog model="ShoppingListEntry" :item="e" @delete="useShoppingStore().entries.delete(e.id!);" v-if="!e.recipeMealplan"></model-edit-dialog> <model-edit-dialog model="ShoppingListEntry" :item="e" @delete="useShoppingStore().entries.delete(e.id!);" v-if="!e.recipeMealplan"></model-edit-dialog>
</v-list-item> </v-list-item>
</template> </template>
@@ -86,7 +86,7 @@
<script setup lang="ts"> <script setup lang="ts">
import {PropType} from "vue"; import {compile, computed, PropType, watch} from "vue";
import {ShoppingListEntry} from "@/openapi"; import {ShoppingListEntry} from "@/openapi";
import ModelSelect from "@/components/inputs/ModelSelect.vue"; import ModelSelect from "@/components/inputs/ModelSelect.vue";
import {IShoppingList, IShoppingListFood} from "@/types/Shopping"; import {IShoppingList, IShoppingListFood} from "@/types/Shopping";
@@ -96,6 +96,7 @@ import {DateTime} from "luxon";
import {useDisplay} from "vuetify"; import {useDisplay} from "vuetify";
import ModelEditDialog from "@/components/dialogs/ModelEditDialog.vue"; import ModelEditDialog from "@/components/dialogs/ModelEditDialog.vue";
import {useShoppingStore} from "@/stores/ShoppingStore"; import {useShoppingStore} from "@/stores/ShoppingStore";
import ShoppingListEntryEditor from "@/components/model_editors/ShoppingListEntryEditor.vue";
const {mobile} = useDisplay() const {mobile} = useDisplay()
@@ -105,6 +106,28 @@ const props = defineProps({
shoppingListFood: {type: {} as PropType<IShoppingListFood>, required: true}, shoppingListFood: {type: {} as PropType<IShoppingListFood>, required: true},
}) })
watch(() => {props.shoppingListFood}, () => {
console.log('PROP WATCH')
})
const entriesList = computed(() => {
let list = [] as ShoppingListEntry[]
props.shoppingListFood?.entries.forEach(e => {
list.push(e)
})
return list
})
const isDelayed = computed(() => {
let isDelayed = false
props.shoppingListFood.entries.forEach(e => {
isDelayed = isDelayed || e.delayUntil != null
})
console.log('computing is delayed', isDelayed)
return isDelayed
})
</script> </script>
<style scoped> <style scoped>

View File

@@ -6,20 +6,15 @@
<!-- <i class="swipe-icon fa-fw fas" :class="{'fa-check': !isChecked , 'fa-cart-plus': isChecked }"></i>--> <!-- <i class="swipe-icon fa-fw fas" :class="{'fa-check': !isChecked , 'fa-cart-plus': isChecked }"></i>-->
<!-- </div>--> <!-- </div>-->
<template #prepend>
<v-btn color="primary" v-if="isDelayed">
<i class="fa-fw fas fa-hourglass-half"></i>
</v-btn>
</template>
<div class="flex-grow-1 p-2"> <div class="flex-grow-1 p-2">
<div class="d-flex"> <div class="d-flex">
<div class="d-flex flex-column pr-2"> <div class="d-flex flex-column pr-2">
<span v-for="[i, a] in amounts" v-bind:key="a.key"> <span v-for="[i, a] in amounts" v-bind:key="a.key">
<span> <span>
<i class="fas fa-check" v-if="a.checked && !isChecked"></i> <i class="fas fa-check text-warning" v-if="a.checked && !isChecked"></i>
<i class="fas fa-hourglass-half" v-if="a.delayed && !a.checked"></i> <b> <i class="fas fa-hourglass-half text-primary" v-if="a.delayed && !a.checked"></i> <b>
{{ a.amount }} {{ a.amount }}
<span v-if="a.unit">{{ a.unit.name }}</span> <span v-if="a.unit">{{ a.unit.name }}</span>
</b> </b>
@@ -58,12 +53,13 @@ import {useShoppingStore} from "@/stores/ShoppingStore.js";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.js"; import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.js";
import {ApiApi, Food, ShoppingListEntry} from '@/openapi' import {ApiApi, Food, ShoppingListEntry} from '@/openapi'
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore"; import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
import {ShoppingLineAmount} from "@/types/Shopping"; import {IShoppingListFood, ShoppingLineAmount} from "@/types/Shopping";
const emit = defineEmits(['clicked']) const emit = defineEmits(['clicked'])
const props = defineProps({ const props = defineProps({
entries: {type: Array as PropType<Array<ShoppingListEntry>>, required: true}, entries: {type: Array as PropType<Array<ShoppingListEntry>>, required: true},
shoppingListFood: {type: {} as PropType<IShoppingListFood>, required: true},
}) })
const itemContainerId = computed(() => { const itemContainerId = computed(() => {

View File

@@ -1,7 +1,33 @@
<template> <template>
<v-tabs v-model="currentTab" > <v-tabs v-model="currentTab">
<v-tab value="shopping"><i class="fas fa-shopping-cart fa-fw"></i> <span class="d-none d-md-block ms-1">{{ $t('Shopping_list') }}</span></v-tab> <v-tab value="shopping"><i class="fas fa-shopping-cart fa-fw"></i> <span class="d-none d-md-block ms-1">{{ $t('Shopping_list') }}</span></v-tab>
<v-tab value="recipes"><i class="fas fa-book fa-fw"></i> <span class="d-none d-md-block ms-1">{{ $t('Recipes') }}</span></v-tab> <v-tab value="recipes"><i class="fas fa-book fa-fw"></i> <span class="d-none d-md-block ms-1">{{ $t('Recipes') }}</span></v-tab>
<v-menu :close-on-content-click="false">
<template v-slot:activator="{ props }">
<v-btn
class="me-4 float-right"
height="100%"
rounded="0"
variant="plain"
v-bind="props"
>
<i class="fa-solid fa-sliders"></i>
</v-btn>
</template>
<v-list density="compact">
<v-list-item @click="useShoppingStore().undoChange()" prepend-icon="fa-solid fa-arrow-rotate-left">{{ $t('Undo') }}</v-list-item>
<v-divider></v-divider>
<v-list-item>
<v-switch color="primary" hide-details :label="$t('ShowDelayed')" v-model="useUserPreferenceStore().deviceSettings.shopping_show_delayed_entries"></v-switch>
</v-list-item>
<v-list-item>
<v-switch color="primary" hide-details :label="$t('ShowRecentlyCompleted')" v-model="useUserPreferenceStore().deviceSettings.shopping_show_checked_entries"></v-switch>
</v-list-item>
</v-list>
</v-menu>
</v-tabs> </v-tabs>
<v-window v-model="currentTab"> <v-window v-model="currentTab">
@@ -31,7 +57,8 @@
<v-divider></v-divider> <v-divider></v-divider>
<template v-for="[i, value] in category.foods" :key="value.food.id"> <template v-for="[i, value] in category.foods" :key="value.food.id">
<shopping-line-item :entries="Array.from(value.entries.values())" @clicked="args => {shoppingLineItemDialog = true; shoppingLineItemDialogFood = value;}"></shopping-line-item> <shopping-line-item :shopping-list-food="value" :entries="Array.from(value.entries.values())"
@clicked="args => {shoppingLineItemDialog = true; shoppingLineItemDialogFood = value; console.log('SETTING ITEMS')}"></shopping-line-item>
</template> </template>
</template> </template>
@@ -52,7 +79,7 @@
<v-label>{{ $t('Recipes') }}</v-label> <v-label>{{ $t('Recipes') }}</v-label>
<v-list> <v-list>
<v-list-item v-for="r in useShoppingStore().getAssociatedRecipes()"> <v-list-item v-for="r in useShoppingStore().getAssociatedRecipes()">
{{r}} {{ r }}
</v-list-item> </v-list-item>
</v-list> </v-list>
</v-card-text> </v-card-text>

View File

@@ -1,5 +1,5 @@
import {acceptHMRUpdate, defineStore} from "pinia" import {acceptHMRUpdate, defineStore} from "pinia"
import {ApiApi, ShoppingListEntry, Supermarket, SupermarketCategory} from "@/openapi"; import {ApiApi, Food, ShoppingListEntry, Supermarket, SupermarketCategory} from "@/openapi";
import {computed, ref} from "vue"; import {computed, ref} from "vue";
import { import {
IShoppingExportEntry, IShoppingExportEntry,
@@ -7,11 +7,12 @@ import {
IShoppingListCategory, IShoppingListCategory,
IShoppingListFood, IShoppingListFood,
IShoppingSyncQueueEntry, IShoppingSyncQueueEntry,
ShoppingGroupingOptions, ShoppingListStats, ShoppingGroupingOptions,
ShoppingListStats,
ShoppingOperationHistoryEntry, ShoppingOperationHistoryEntry,
ShoppingOperationHistoryType ShoppingOperationHistoryType
} from "@/types/Shopping"; } from "@/types/Shopping";
import {ErrorMessageType, MessageType, useMessageStore} from "@/stores/MessageStore"; import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore"; import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
const _STORE_ID = "shopping_store" const _STORE_ID = "shopping_store"
@@ -72,7 +73,7 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
categoryStats.countChecked++ categoryStats.countChecked++
} else { } else {
categoryStats.countUnchecked++ categoryStats.countUnchecked++
if(entry.delayUntil != null) { if (entry.delayUntil != null) {
categoryStats.countUncheckedDelayed++ categoryStats.countUncheckedDelayed++
} }
} }
@@ -418,18 +419,48 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
* @param delay if entries should be delayed or if delay should be removed * @param delay if entries should be delayed or if delay should be removed
* @param undo if the user should be able to undo the change or not * @param undo if the user should be able to undo the change or not
*/ */
function delayEntries(entries: ShoppingListEntry[], delay: boolean, undo: boolean) { function setEntriesDelayedState(entries: ShoppingListEntry[], delay: boolean, undo: boolean) {
let delay_hours = useUserPreferenceStore().userSettings.defaultDelay! let delay_hours = useUserPreferenceStore().userSettings.defaultDelay!
let delayDate = new Date(Date.now() + delay_hours * (60 * 60 * 1000)) let delayDate = new Date(Date.now() + delay_hours * (60 * 60 * 1000))
if (undo) { if (undo) {
registerChange((delay ? 'DELAY' : 'UNDELAY'), entries) registerChange((delay ? 'DELAY' : 'UNDELAY'), entries)
} }
entries.forEach(entry => {
entry.delayUntil = (delay ? delayDate : null)
console.log('DELAY: ', delay, entry.delayUntil, entry)
updateObject(entry)
})
}
for (let i in entries) { /**
entries[i].delayUntil = (delay ? delayDate : null) * ignore all foods of the given entries for shopping in the future and check associated entries from the list
updateObject(entries[i]) * @param ignored if the food should be ignored or not ignored (for undo)
* @param {{}} entries set of entries associated with food to set checked
* @param undo if the user should be able to undo the change or not
*/
function setFoodIgnoredState(entries: ShoppingListEntry[], ignored: boolean, undo: boolean) {
const api = new ApiApi()
if (undo) {
registerChange((ignored ? 'IGNORE' : 'UNIGNORE'), entries)
} }
let foods = [] as Food[]
entries.forEach(e => {
if (!foods.includes(e.food!)) {
foods.push(e.food!)
}
})
setEntriesCheckedState(entries, ignored, false)
foods.forEach(food => {
food.ignoreShopping = ignored
api.apiFoodUpdate({food: food, id: food.id!}).catch(err => {
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err)
})
})
} }
/** /**
@@ -482,12 +513,14 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
if (type === 'CHECKED' || type === 'UNCHECKED') { if (type === 'CHECKED' || type === 'UNCHECKED') {
setEntriesCheckedState(entries, (type === 'UNCHECKED'), false) setEntriesCheckedState(entries, (type === 'UNCHECKED'), false)
} else if (type === 'DELAY' || type === 'UNDELAY') { } else if (type === 'DELAY' || type === 'UNDELAY') {
delayEntries(entries, (type === 'UNDELAY'), false) setEntriesDelayedState(entries, (type === 'UNDELAY'), false)
} else if (type === 'CREATED') { } else if (type === 'CREATED') {
for (let i in entries) { for (let i in entries) {
let e = entries[i] let e = entries[i]
deleteObject(e) deleteObject(e)
} }
} else if (type === 'IGNORE' || type === 'UNIGNORE') {
setFoodIgnoredState(entries, (type === 'UNIGNORE'), false)
} }
} else { } else {
// can use localization in store // can use localization in store
@@ -509,7 +542,8 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
updateObject, updateObject,
undoChange, undoChange,
setEntriesCheckedState, setEntriesCheckedState,
delayEntries, setFoodIgnoredState,
delayEntries: setEntriesDelayedState,
getAssociatedRecipes, getAssociatedRecipes,
} }

View File

@@ -79,7 +79,7 @@ export type ShoppingListStats = {
* DELAY: ShoppingListEntry was postponed/delayed * DELAY: ShoppingListEntry was postponed/delayed
* UNDELAY: ShoppingListEntry delay was removed * UNDELAY: ShoppingListEntry delay was removed
*/ */
export type ShoppingOperationHistoryType = 'CREATED' | 'CHECKED' | 'UNCHECKED' | 'DELAY' | 'UNDELAY' export type ShoppingOperationHistoryType = 'CREATED' | 'CHECKED' | 'UNCHECKED' | 'DELAY' | 'UNDELAY' | 'IGNORE' | 'UNIGNORE'
/** /**
* history event consisting of a type and affected entries * history event consisting of a type and affected entries