mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-03 21:37:49 -05:00
various meal plan fixes
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user