meal plan stuff

This commit is contained in:
vabene1111
2024-05-01 15:37:33 +02:00
parent f961413e94
commit 6d813ebb2f
4 changed files with 59 additions and 101 deletions

View File

@@ -24,7 +24,7 @@
</v-col> </v-col>
<v-col cols="12" md="6"> <v-col cols="12" md="6">
<ModelSelect model="recipe" v-model="mutableMealPlan.recipe"></ModelSelect> <ModelSelect model="recipe" v-model="mutableMealPlan.recipe"></ModelSelect>
<!-- <v-number-input label="Days" control-variant="split" :min="1"></v-number-input>--> <!--TODO create days input with +/- snyced to date --> <!-- <v-number-input label="Days" control-variant="split" :min="1"></v-number-input>--> <!--TODO create days input with +/- synced to date -->
<recipe-card :recipe="mutableMealPlan.recipe" v-if="mutableMealPlan && mutableMealPlan.recipe"></recipe-card> <recipe-card :recipe="mutableMealPlan.recipe" v-if="mutableMealPlan && mutableMealPlan.recipe"></recipe-card>
</v-col> </v-col>
</v-row> </v-row>
@@ -37,7 +37,7 @@
</v-card-text> </v-card-text>
<v-divider></v-divider> <v-divider></v-divider>
<v-card-actions> <v-card-actions>
<v-btn color="error"> <v-btn color="error" @click="useMealPlanStore().deleteObject(mutableMealPlan); dialog = false">
Delete Delete
</v-btn> </v-btn>
<v-btn color="success" class="ml-auto" @click="saveMealPlan"> <v-btn color="success" class="ml-auto" @click="saveMealPlan">
@@ -57,7 +57,6 @@ import RecipeCard from "@/components/display/RecipeCard.vue";
import {useMealPlanStore} from "@/stores/MealPlanStore"; import {useMealPlanStore} from "@/stores/MealPlanStore";
import {VNumberInput} from 'vuetify/labs/VNumberInput' //TODO remove once component is out of labs import {VNumberInput} from 'vuetify/labs/VNumberInput' //TODO remove once component is out of labs
import {VDateInput} from 'vuetify/labs/VDateInput' //TODO remove once component is out of labs import {VDateInput} from 'vuetify/labs/VDateInput' //TODO remove once component is out of labs
import Multiselect from '@vueform/multiselect'
import ModelSelect from "@/components/inputs/ModelSelect.vue"; import ModelSelect from "@/components/inputs/ModelSelect.vue";
import {useMessageStore} from "@/stores/MessageStore"; import {useMessageStore} from "@/stores/MessageStore";
@@ -78,6 +77,8 @@ if (props.mealPlan != undefined) {
watchEffect(() => { watchEffect(() => {
if (props.mealPlan != undefined) { if (props.mealPlan != undefined) {
mutableMealPlan.value = props.mealPlan mutableMealPlan.value = props.mealPlan
dateRangeValue.value.push(mutableMealPlan.value.fromDate)
dateRangeValue.value.push(mutableMealPlan.value.toDate)
} else { } else {
mutableMealPlan.value = newMealPlan() mutableMealPlan.value = newMealPlan()
} }
@@ -89,7 +90,7 @@ function saveMealPlan() {
mutableMealPlan.value.recipe = mutableMealPlan.value.recipe as RecipeOverview mutableMealPlan.value.recipe = mutableMealPlan.value.recipe as RecipeOverview
if (dateRangeValue.value != null) { if (dateRangeValue.value != null) {
mutableMealPlan.value.fromDate = dateRangeValue.value[0] mutableMealPlan.value.fromDate = dateRangeValue.value[0]
mutableMealPlan.value.toDate = dateRangeValue.value[dateRangeValue.value.length-1] mutableMealPlan.value.toDate = dateRangeValue.value[dateRangeValue.value.length - 1]
} else { } else {
useMessageStore().addError('Missing Dates') useMessageStore().addError('Missing Dates')
return return
@@ -108,6 +109,7 @@ function newMealPlan() {
return { return {
fromDate: DateTime.now().toJSDate(), fromDate: DateTime.now().toJSDate(),
toDate: DateTime.now().toJSDate(), toDate: DateTime.now().toJSDate(),
servings: 1,
} as MealPlan } as MealPlan
} }

View File

@@ -17,7 +17,7 @@
</span> </span>
</div> </div>
</div> </div>
<meal-plan-dialog :meal-plan="mealPlan"></meal-plan-dialog>
</v-card-text> </v-card-text>
</v-card> </v-card>
@@ -28,6 +28,7 @@
import {computed, PropType} from "vue"; import {computed, PropType} from "vue";
import {IMealPlanNormalizedCalendarItem} from "@/types/MealPlan"; import {IMealPlanNormalizedCalendarItem} from "@/types/MealPlan";
import RecipeImage from "@/components/display/RecipeImage.vue"; import RecipeImage from "@/components/display/RecipeImage.vue";
import MealPlanDialog from "@/components/dialogs/MealPlanDialog.vue";
const emit = defineEmits({ const emit = defineEmits({
onDragStart: (value: IMealPlanNormalizedCalendarItem, event: DragEvent) => { onDragStart: (value: IMealPlanNormalizedCalendarItem, event: DragEvent) => {

View File

@@ -1,6 +1,7 @@
<template> <template>
<v-row class="h-100"> <v-row class="h-100">
<v-col> <v-col>
<!-- TODO add hint about CTRL key while drag/drop -->
<CalendarView <CalendarView
:items="planItems" :items="planItems"
class="theme-default" class="theme-default"
@@ -28,16 +29,18 @@ import "vue-simple-calendar/dist/css/default.css"
import MealPlanCalendarItem from "@/components/display/MealPlanCalendarItem.vue"; import MealPlanCalendarItem from "@/components/display/MealPlanCalendarItem.vue";
import {IMealPlanCalendarItem, IMealPlanNormalizedCalendarItem} from "@/types/MealPlan"; import {IMealPlanCalendarItem, IMealPlanNormalizedCalendarItem} from "@/types/MealPlan";
import {computed, onMounted, ref} from "vue"; import {computed, onMounted, ref} from "vue";
import {ApiApi, MealPlan} from "@/openapi";
import {DateTime} from "luxon"; import {DateTime} from "luxon";
import {useDisplay} from "vuetify"; import {useDisplay} from "vuetify";
import {useMealPlanStore} from "@/stores/MealPlanStore"; import {useMealPlanStore} from "@/stores/MealPlanStore";
const {lgAndUp} = useDisplay() const {lgAndUp} = useDisplay()
const mealPlans = ref([] as MealPlan[])
const currentlyDraggedMealplan = ref({} as IMealPlanNormalizedCalendarItem) const currentlyDraggedMealplan = ref({} as IMealPlanNormalizedCalendarItem)
const clickedMealPlan = ref({} as IMealPlanNormalizedCalendarItem)
/**
* computed property that converts array of MealPlan object to
* array of CalendarItems (format required/extended from vue-simple-calendar)
*/
const planItems = computed(() => { const planItems = computed(() => {
let items = [] as IMealPlanCalendarItem[] let items = [] as IMealPlanCalendarItem[]
useMealPlanStore().planList.forEach(mp => { useMealPlanStore().planList.forEach(mp => {
@@ -67,18 +70,19 @@ function dropCalendarItemOnDate(undefinedItem: IMealPlanNormalizedCalendarItem,
//The item argument (first) is undefined because our custom calendar item cannot manipulate the calendar state so the item is unknown to the calendar (probably fixable by somehow binding state to the item) //The item argument (first) is undefined because our custom calendar item cannot manipulate the calendar state so the item is unknown to the calendar (probably fixable by somehow binding state to the item)
if (currentlyDraggedMealplan.value.originalItem.mealPlan.id != undefined) { if (currentlyDraggedMealplan.value.originalItem.mealPlan.id != undefined) {
let mealPlan = useMealPlanStore().plans.get(currentlyDraggedMealplan.value.originalItem.mealPlan.id) let mealPlan = useMealPlanStore().plans.get(currentlyDraggedMealplan.value.originalItem.mealPlan.id)
let fromToDiff = DateTime.fromJSDate(mealPlan.toDate).diff(DateTime.fromJSDate(mealPlan.fromDate), 'days') if (mealPlan != undefined) {
if (event.ctrlKey) { let fromToDiff = DateTime.fromJSDate(mealPlan.toDate).diff(DateTime.fromJSDate(mealPlan.fromDate), 'days')
let new_entry = Object.assign({}, mealPlan) if (event.ctrlKey) {
new_entry.fromDate = targetDate let new_entry = Object.assign({}, mealPlan)
new_entry.toDate = DateTime.fromJSDate(targetDate).plus(fromToDiff).toJSDate() new_entry.fromDate = targetDate
useMealPlanStore().createObject(new_entry) new_entry.toDate = DateTime.fromJSDate(targetDate).plus(fromToDiff).toJSDate()
useMealPlanStore().createObject(new_entry)
} else { } else {
mealPlan.fromDate = targetDate mealPlan.fromDate = targetDate
mealPlan.toDate = DateTime.fromJSDate(targetDate).plus(fromToDiff).toJSDate() mealPlan.toDate = DateTime.fromJSDate(targetDate).plus(fromToDiff).toJSDate()
console.log('UPDAAAATING: ', mealPlan.fromDate, mealPlan.toDate) useMealPlanStore().updateObject(mealPlan)
useMealPlanStore().updateObject(mealPlan) }
} }
} }
} }

View File

@@ -1,58 +1,39 @@
<template> <template>
<v-input> <v-input>
<!-- &lt;!&ndash;TODO Problems: 1. behind other cards when those are underneath the element, making card overflow visible breaks cards &ndash;&gt;-->
<!-- <VueMultiselect-->
<!-- :id="id"-->
<!-- v-model="selected_items"-->
<!-- :options="items"-->
<!-- :close-on-select="true"-->
<!-- :clear-on-select="true"-->
<!-- :hide-selected="multiple"-->
<!-- :preserve-search="true"-->
<!-- :internal-search="false"-->
<!-- :limit="limit"-->
<!-- :placeholder="model"-->
<!-- :label="label"-->
<!-- track-by="id"-->
<!-- :multiple="multiple"-->
<!-- :taggable="allowCreate"-->
<!-- tag-placeholder="TODO CREATE PLACEHOLDER"-->
<!-- :loading="search_loading"-->
<!-- @search-change="debouncedSearchFunction"-->
<!-- @input="selectionChanged"-->
<!-- @tag="addItem"-->
<!-- @open="search('')"-->
<!-- :disabled="disabled"-->
<!-- class="material-multiselect"-->
<!-- >-->
<!-- </VueMultiselect>-->
<!--TODO resolve-on-load false for now, race condition with model class, make prop once better solution is found -->
<!-- TODO strange behavior/layering issues with appendTo body, find soltion to make it work -->
<Multiselect <Multiselect
:id="id"
class="material-multiselect z-max" class="material-multiselect z-max"
:resolve-on-load="false"
v-model="model" v-model="model"
:options="search" :options="search"
:delay="300" :delay="300"
:object="true" :object="true"
valueProp="id" :valueProp="itemValue"
:label="label" :label="itemLabel"
:searchable="true" :searchable="true"
:strict="false" :strict="false"
:disabled="disabled" :disabled="disabled"
:mode="mode"
:can-clear="canClear"
:can-deselect="canClear"
:limit="limit"
placeholder="TODO ADD LOCALIZED PLACEHOLDER"
noOptionsText="TODO ADD LOCALIZED NO-OPTIONS"
noResultsText="TODO ADD LOCALIZED NO-RESULTS"
/> />
</v-input> </v-input>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import {computed, onMounted, PropType, ref, Ref} from "vue" import {onMounted, PropType, ref, Ref} from "vue"
import {ApiApi} from "@/openapi/index.js"
import {useDebounceFn} from "@vueuse/core" import {useDebounceFn} from "@vueuse/core"
import {GenericModel, getModelFromStr} from "@/types/Models" import {GenericModel, getModelFromStr} from "@/types/Models"
import Multiselect from '@vueform/multiselect' import Multiselect from '@vueform/multiselect'
import {ErrorMessageType, MessageType, useMessageStore} from "@/stores/MessageStore";
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue'])
@@ -61,22 +42,22 @@ const props = defineProps({
id: {type: String, required: false, default: Math.random().toString()}, id: {type: String, required: false, default: Math.random().toString()},
itemLabel: {type: String, default: "name"},
itemValue: {type: String, default: "id"},
limit: {type: Number, default: 25},
disabled: {type: Boolean, default: false},
canClear: {type: Boolean, default: true},
mode: {type: String as PropType<'single' | 'multiple' | 'tags'>, default: 'single'},
// not verified // not verified
multiple: {type: Boolean, default: true},
limit: {type: Number, default: 25},
allowCreate: {type: Boolean, default: false}, allowCreate: {type: Boolean, default: false},
search_on_load: {type: Boolean, default: false}, search_on_load: {type: Boolean, default: false},
clearable: {type: Boolean, default: false},
chips: {type: Boolean, default: undefined},
itemName: {type: String, default: "name"},
itemValue: {type: String, default: "id"},
placeholder: {type: String, default: undefined}, placeholder: {type: String, default: undefined},
label: {type: String, default: "name"},
parent_variable: {type: String, default: undefined}, parent_variable: {type: String, default: undefined},
sticky_options: { sticky_options: {
@@ -85,42 +66,15 @@ const props = defineProps({
return [] return []
}, },
}, },
initial_selection: {
type: Array,
default() {
return []
},
},
initial_single_selection: {
type: Object,
default: undefined,
},
disabled: {type: Boolean, default: false},
}) })
const model = defineModel() const model = defineModel()
const model_class = ref({} as GenericModel<any>) const model_class = ref({} as GenericModel<any>)
const items: Ref<Array<any>> = ref([]) const items: Ref<Array<any>> = ref([])
const selected_items: Ref<Array<any> | any> = ref(undefined) const selected_items: Ref<Array<any> | any> = ref(undefined)
const search_query = ref("")
const search_loading = ref(false)
const elementId = ref((Math.random() * 100000).toString())
onMounted(() => {
model_class.value = getModelFromStr(props.model)
if (props.search_on_load) {
debouncedSearchFunction("")
}
})
/**
* debounced search function bound to search input changing
*/
const debouncedSearchFunction = useDebounceFn((query: string) => {
search(query)
}, 300)
/** /**
* performs the API request to search for the selected input * performs the API request to search for the selected input
@@ -130,36 +84,33 @@ function search(query: string) {
return model_class.value.list(query).then((r) => { return model_class.value.list(query).then((r) => {
return r return r
}).catch((err) => { }).catch((err) => {
//useMessageStore().addMessage(MessageType.ERROR, err, 8000) useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
}).finally(() => { }).finally(() => {
search_loading.value = false
}) })
} }
// TODO refactor for new multiselect
function addItem(item: string) { function addItem(item: string) {
console.log("CREATEING NEW with -> ", item) console.log("CREATEING NEW with -> ", item)
const api = new ApiApi()
api.apiKeywordList()
model_class.value.create(item).then((createdObj) => { model_class.value.create(item).then((createdObj) => {
//StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_CREATE) useMessageStore().addMessage(MessageType.SUCCESS, 'Created', 5000)
if (selected_items.value instanceof Array) { if (selected_items.value instanceof Array) {
selected_items.value.push(createdObj) selected_items.value.push(createdObj)
} else { } else {
selected_items.value = createdObj selected_items.value = createdObj
} }
items.value.push(createdObj) items.value.push(createdObj)
selectionChanged()
}).catch((err) => { }).catch((err) => {
//StandardToasts.makeStandardToast(this, StandardToasts.FAIL_CREATE) useMessageStore().addError(ErrorMessageType.CREATE_ERROR, err)
}).finally(() => { }).finally(() => {
search_loading.value = false
}) })
} }
function selectionChanged() {
emit('update:modelValue', selected_items)
}
</script> </script>
<style src="@vueform/multiselect/themes/default.css"></style> <style src="@vueform/multiselect/themes/default.css"></style>