Skip to content

Commit

Permalink
various meal plan fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
vabene1111 committed Jan 2, 2025
1 parent f97d8ff commit 00ae511
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 55 deletions.
2 changes: 1 addition & 1 deletion vue3/src/components/display/HorizontalMealPlanWindow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
29 changes: 21 additions & 8 deletions vue3/src/components/display/MealPlanView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
86 changes: 44 additions & 42 deletions vue3/src/components/model_editors/MealPlanEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -53,50 +53,46 @@
<!-- <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>

<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>

</template>

<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";
Expand All @@ -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},
Expand All @@ -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()
Expand Down Expand Up @@ -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()
}, 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]
}
nextTick(() => {
editingObjChanged.value = false
})
}, existingItemFunction: () => {
initializeDateRange()
loadShoppingListEntries()
}
},)
})
Expand All @@ -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
*/
Expand All @@ -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>
Expand Down
9 changes: 7 additions & 2 deletions vue3/src/composables/useModelEditorFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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]
})
}
Expand Down Expand Up @@ -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)
Expand Down
18 changes: 16 additions & 2 deletions vue3/src/stores/MealPlanStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 00ae511

Please sign in to comment.