various meal plan fixes

This commit is contained in:
vabene1111
2025-01-02 09:21:52 +01:00
parent f97d8ffdfd
commit 00ae511076
5 changed files with 89 additions and 55 deletions

View File

@@ -21,7 +21,7 @@
<div class="align-self-center">
<v-btn variant="flat" icon="">
<i class="fas fa-plus"></i>
<model-edit-dialog model="MealPlan"></model-edit-dialog>
<model-edit-dialog model="MealPlan" :item-defaults="{fromDate: mealPlanGridItem.date.toJSDate()}" :close-after-create="false" :close-after-save="false"></model-edit-dialog>
</v-btn>
</div>
</div>

View File

@@ -97,6 +97,18 @@ const calendarItemHeight = computed(() => {
* watch calendar date and load entries accordingly
*/
watch(calendarDate, () => {
refreshVisiblePeriod(false)
})
onMounted(() => {
refreshVisiblePeriod(true)
})
/**
* refresh data for the currently visible period
* @param startDateUnknown when the calendar initially loads the date is set to today but the visible period might be larger. If set loads the period day count for the past as well
*/
function refreshVisiblePeriod(startDateUnknown: boolean) {
let daysInPeriod = 7
if (useUserPreferenceStore().deviceSettings.mealplan_displayPeriod == 'month') {
daysInPeriod = 31
@@ -105,13 +117,14 @@ watch(calendarDate, () => {
}
let days = useUserPreferenceStore().deviceSettings.mealplan_displayPeriodCount * daysInPeriod
useMealPlanStore().refreshFromAPI(calendarDate.value, DateTime.now().plus({days: days}).toJSDate())
})
onMounted(() => {
// initial load for next 30 days
useMealPlanStore().refreshFromAPI(calendarDate.value, DateTime.now().plus({days: 30}).toJSDate())
})
// load backwards to as on initial
if (startDateUnknown) {
useMealPlanStore().refreshFromAPI(DateTime.fromJSDate(calendarDate.value).minus({days: days}).toJSDate(), DateTime.now().plus({days: days}).toJSDate())
} else {
useMealPlanStore().refreshFromAPI(calendarDate.value, DateTime.now().plus({days: days}).toJSDate())
}
}
/**
* handle drop event for calendar items on fields
@@ -124,8 +137,8 @@ function dropCalendarItemOnDate(undefinedItem: IMealPlanNormalizedCalendarItem,
if (currentlyDraggedMealplan.value.originalItem.mealPlan.id != undefined) {
let mealPlan = useMealPlanStore().plans.get(currentlyDraggedMealplan.value.originalItem.mealPlan.id)
if (mealPlan != undefined) {
let fromToDiff = {days: 1}
if (mealPlan.toDate) {
let fromToDiff = {days: 0}
if (mealPlan.toDate && mealPlan.toDate > mealPlan.fromDate) {
fromToDiff = DateTime.fromJSDate(mealPlan.toDate).diff(DateTime.fromJSDate(mealPlan.fromDate), 'days')
}
// create copy of item if control is pressed

View File

@@ -2,8 +2,8 @@
<model-editor-base
:loading="loading"
:dialog="dialog"
@save="saveObject"
@delete="deleteObject"
@save="saveObject().then((obj:MealPlan) => { useMealPlanStore().plans.set(obj.id, obj); loadShoppingListEntries()})"
@delete="useMealPlanStore().plans.delete(editingObj.id); deleteObject()"
@close="emit('close')"
:is-update="isUpdate()"
:is-changed="editingObjChanged"
@@ -53,16 +53,15 @@
<!-- <v-number-input label="Days" control-variant="split" :min="1"></v-number-input>-->
<!--TODO create days input with +/- synced to date -->
<recipe-card :recipe="editingObj.recipe" v-if="editingObj && editingObj.recipe"></recipe-card>
<v-checkbox :label="$t('AddToShopping')" v-model="editingObj.addshopping" hide-details v-if="editingObj.recipe && !isUpdate()"></v-checkbox>
<!-- TODO review shopping before add -->
</v-col>
</v-row>
<v-row dense>
<v-col cols="12" md="6">
<v-col cols="12">
<v-textarea :label="$t('Note')" v-model="editingObj.note" rows="3"></v-textarea>
</v-col>
<v-col cols="12" md="6" v-if="!isUpdate()">
<v-checkbox :label="$t('AddToShopping')" v-model="editingObj.addshopping" hide-details></v-checkbox>
<!-- <v-checkbox :label="$t('review_shopping')" v-model="addToShopping" hide-details></v-checkbox>-->
</v-col>
</v-row>
</v-form>
</v-tabs-window-item>
@@ -70,25 +69,22 @@
<v-tabs-window-item value="shopping">
<closable-help-alert class="mb-2" :text="$t('MealPlanShoppingHelp')"></closable-help-alert>
<v-row v-if="isUpdate()" dense style="max-height: 75vh" class="overflow-scroll">
<v-row v-if="isUpdate()" dense style="max-height: 75vh" class="overflow-y-scroll">
<v-col>
<shopping-list-entry-input :loading="useShoppingStore().currentlyUpdating" :meal-plan="editingObj"></shopping-list-entry-input>
<v-list v-if="editingObj.id">
<shopping-line-item
v-for="slf in useShoppingStore().getMealPlanEntries(editingObj.id)"
:shopping-list-food="slf"
hide-info-row
:key="slf.food.id"
></shopping-line-item>
</v-list>
</v-col>
</v-row>
</v-tabs-window-item>
</v-tabs-window>
</v-card-text>
</model-editor-base>
@@ -96,7 +92,7 @@
<script setup lang="ts">
import {onMounted, PropType, ref} from "vue";
import {nextTick, onMounted, PropType, ref} from "vue";
import {ApiApi, MealPlan, MealType, ShoppingListRecipe} from "@/openapi";
import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue";
import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions";
@@ -112,6 +108,7 @@ import ShoppingLineItem from "@/components/display/ShoppingLineItem.vue";
import {useShoppingStore} from "@/stores/ShoppingStore";
import ShoppingListEntryInput from "@/components/inputs/ShoppingListEntryInput.vue";
import ClosableHelpAlert from "@/components/display/ClosableHelpAlert.vue";
import {useMealPlanStore} from "@/stores/MealPlanStore";
const props = defineProps({
item: {type: {} as PropType<MealPlan>, required: false, default: null},
@@ -121,13 +118,23 @@ const props = defineProps({
})
const emit = defineEmits(['create', 'save', 'delete', 'close'])
const {setupState, deleteObject, saveObject, isUpdate, editingObjName, applyItemDefaults, loading, editingObj, editingObjChanged, modelClass} = useModelEditorFunctions<MealPlan>('MealPlan', emit)
const {
setupState,
deleteObject,
saveObject,
isUpdate,
editingObjName,
applyItemDefaults,
loading,
editingObj,
editingObjChanged,
modelClass
} = useModelEditorFunctions<MealPlan>('MealPlan', emit)
// object specific data (for selects/display)
const tab = ref('plan')
const dateRangeValue = ref([] as Date[])
const shoppingListRecipe = ref<ShoppingListRecipe | undefined>(undefined)
onMounted(() => {
const api = new ApiApi()
@@ -155,20 +162,22 @@ onMounted(() => {
editingObj.value.servings = 1
editingObj.value.mealType = defaultMealType
editingObj.value.addshopping = !!useUserPreferenceStore().userSettings.mealplanAutoaddShopping
editingObj.value.addshopping = useUserPreferenceStore().userSettings.mealplanAutoaddShopping
applyItemDefaults(props.itemDefaults)
if (editingObj.value.toDate < editingObj.value.fromDate) {
editingObj.value.toDate = editingObj.value.fromDate
}
initializeDateRange()
nextTick(() => {
editingObjChanged.value = false
})
}, existingItemFunction: () => {
initializeDateRange()
useShoppingStore().refreshFromAPI(editingObj.value.id!)
api.apiShoppingListRecipeList({mealplan: editingObj.value.id!}).then(r => {
if (r.results.length > 0) {
shoppingListRecipe.value = r.results[0]
}
})
loadShoppingListEntries()
}
},)
})
@@ -181,13 +190,24 @@ onMounted(() => {
function updateDate() {
if (dateRangeValue.value != null) {
editingObj.value.fromDate = dateRangeValue.value[0]
editingObj.value.toDate = dateRangeValue.value[dateRangeValue.value.length - 1]
if (dateRangeValue.value[dateRangeValue.value.length - 1] > editingObj.value.fromDate) {
editingObj.value.toDate = dateRangeValue.value[dateRangeValue.value.length - 1]
} else {
editingObj.value.toDate = editingObj.value.fromDate
}
} else {
useMessageStore().addMessage(MessageType.WARNING, 'Missing Date', 7000)
return
}
}
/**
* load all shopping list entries associated with meal plan
*/
function loadShoppingListEntries() {
useShoppingStore().refreshFromAPI(editingObj.value.id!)
}
/**
* initialize the dateRange selector when the editingObject is initialized
*/
@@ -204,24 +224,6 @@ function initializeDateRange() {
}
}
/**
* manually create a shopping list recipe (without a recipe) that links manually created entries to the shopping list
*/
function createShoppingListRecipe() {
let api = new ApiApi()
let slr = {
mealplan: editingObj.value.id,
servings: editingObj.value.servings,
} as ShoppingListRecipe
api.apiShoppingListRecipeCreate({shoppingListRecipe: slr}).then(r => {
shoppingListRecipe.value = r
}).catch(err => {
useMessageStore().addError(ErrorMessageType.CREATE_ERROR, err)
})
}
</script>
<style scoped>

View File

@@ -19,6 +19,10 @@ export function useModelEditorFunctions<T>(modelName: EditorSupportedModels, emi
const {t} = useI18n()
/**
* watch editing object to detect changes
* set editingObjChanged to true when a change is detected
*/
watch(() => editingObj.value, (newValue, oldValue) => {
if (Object.keys(oldValue).length > 0) {
editingObjChanged.value = true
@@ -55,7 +59,7 @@ export function useModelEditorFunctions<T>(modelName: EditorSupportedModels, emi
function applyItemDefaults(itemDefaults: T) {
if (Object.keys(itemDefaults).length > 0) {
Object.keys(itemDefaults).forEach(k => {
console.log('applying default ', k)
console.log('applying default ', k, itemDefaults[k])
editingObj.value[k] = itemDefaults[k]
})
}
@@ -209,8 +213,9 @@ export function useModelEditorFunctions<T>(modelName: EditorSupportedModels, emi
function deleteObject() {
loading.value = true
modelClass.value.destroy(editingObj.value.id).then((r: any) => {
return modelClass.value.destroy(editingObj.value.id).then((r: any) => {
emit('delete', editingObj.value)
console.log('deleted')
editingObj.value = {} as T
}).catch((err: any) => {
useMessageStore().addError(ErrorMessageType.DELETE_ERROR, err)

View File

@@ -55,10 +55,24 @@ export const useMealPlanStore = defineStore(_STORE_ID, () => {
currently_updating.value = [from_date, to_date] // certainly no perfect check but better than nothing
loading.value = true
const api = new ApiApi()
return api.apiMealPlanList({fromDate: DateTime.fromJSDate(from_date).toISODate() as string, toDate: DateTime.fromJSDate(to_date).toISODate() as string, pageSize: 100}).then(r => {
return api.apiMealPlanList({
fromDate: DateTime.fromJSDate(from_date).toISODate() as string,
toDate: DateTime.fromJSDate(to_date).toISODate() as string,
pageSize: 100
}).then(r => {
let foundIds: number[] = []
r.results.forEach((p) => {
plans.value.set(p.id, p)
plans.value.set(p.id!, p)
foundIds.push(p.id!)
})
// delete entries that no longer exist
plans.value.forEach(p => {
if (!foundIds.includes(p.id!)) {
plans.value.delete(p.id!)
}
})
currently_updating.value = [new Date(0), new Date(0)]
}).catch((err) => {
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)