Merge branch 'develop' into remove_facets
This commit is contained in:
@ -1,219 +1,165 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-tabs content-class="mt-3" v-model="current_tab">
|
||||
<b-tab :title="$t('Planner')" active>
|
||||
<div class=" d-none d-lg-block">
|
||||
<div class="row">
|
||||
<div class="col col-2">
|
||||
<h4>{{ $t('Meal_Types') }}</h4>
|
||||
<div class="d-none d-lg-block">
|
||||
<div class="row ">
|
||||
<div class="col col-2">
|
||||
<h4>{{ $t('Meal_Types') }}</h4>
|
||||
|
||||
<b-form-checkbox :button-variant="danger" v-model="mt.checked" size="lg" v-for="mt in meal_types" v-bind:key="mt.id">
|
||||
|
||||
<b-badge variant="primary" :style="{'background-color':mt.color}">{{ mt.name }}</b-badge>
|
||||
</b-form-checkbox>
|
||||
|
||||
<hr/>
|
||||
<button class="btn btn-success shadow-none mt-1 btn-block" @click="createEntryClick(new Date())"><i
|
||||
class="fas fa-calendar-plus"></i> {{ $t("Create") }}
|
||||
</button>
|
||||
<button class="btn btn-primary shadow-none mt-1 btn-block" @click="createAutoPlan(new Date())"><i
|
||||
class="fas fa-calendar-plus"></i> {{ $t("Auto_Planner") }}
|
||||
</button>
|
||||
<a class="btn btn-primary shadow-none mt-1 btn-blockmt-1 btn-block" :href="iCalUrl"><i class="fas fa-download"></i>
|
||||
{{ $t("Export_To_ICal") }}
|
||||
</a>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="col col-10">
|
||||
<div class="row calender-row ">
|
||||
<div class="col-12 calender-parent">
|
||||
<calendar-view
|
||||
:show-date="showDate"
|
||||
:enable-date-selection="true"
|
||||
class="theme-default"
|
||||
:items="plan_items"
|
||||
:display-period-uom="settings.displayPeriodUom"
|
||||
:period-changed-callback="periodChangedCallback"
|
||||
:enable-drag-drop="true"
|
||||
:item-content-height="item_height"
|
||||
@click-date="createEntryClick"
|
||||
@drop-on-date="moveEntry"
|
||||
:display-period-count="settings.displayPeriodCount"
|
||||
:starting-day-of-week="settings.startingDayOfWeek"
|
||||
:display-week-numbers="settings.displayWeekNumbers"
|
||||
>
|
||||
<template #item="{ value, weekStartDate, top }">
|
||||
<meal-plan-card
|
||||
:value="value"
|
||||
:week-start-date="weekStartDate"
|
||||
:top="top"
|
||||
:detailed="detailed_items"
|
||||
:item_height="item_height"
|
||||
@dragstart="dragged_item = value"
|
||||
@click-item="entryClick"
|
||||
@open-context-menu="openContextMenu"
|
||||
/>
|
||||
</template>
|
||||
<template #header="{ headerProps }">
|
||||
<meal-plan-calender-header
|
||||
ref="header"
|
||||
:header-props="headerProps"
|
||||
@input="setShowDate"
|
||||
@delete-dragged="deleteEntry(dragged_item)"
|
||||
@create-new="createEntryClick(new Date())"
|
||||
@set-starting-day-back="setStartingDay(-1)"
|
||||
@set-starting-day-forward="setStartingDay(1)"
|
||||
:i-cal-url="iCalUrl"
|
||||
:options="options"
|
||||
:settings_prop="settings"
|
||||
/>
|
||||
</template>
|
||||
</calendar-view>
|
||||
</div>
|
||||
<div class="d-flex flex-row align-items-center border" v-for="mt in meal_types" v-bind:key="mt.id">
|
||||
<div class="flex-column" style="width: 2.5rem; height: 2.5rem;" :style="{'background-color': mt.color}"></div>
|
||||
<div class="flex-column flex-grow-1 align-middle justify-content-center">
|
||||
<div class="card-body p-2 align-middle">
|
||||
{{ mt.name }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="row d-block d-lg-none">
|
||||
<div>
|
||||
<div class="col-12">
|
||||
<div class="col-12 d-flex justify-content-center mt-2">
|
||||
<b-button-toolbar key-nav aria-label="Toolbar with button groups">
|
||||
<b-button-group class="mx-1">
|
||||
<b-button v-html="'<<'" class="p-2 pr-3 pl-3"
|
||||
@click="setShowDate($refs.header.headerProps.previousPeriod)"></b-button>
|
||||
</b-button-group>
|
||||
<b-button-group class="mx-1">
|
||||
<b-button @click="setShowDate($refs.header.headerProps.currentPeriod)"><i
|
||||
class="fas fa-home"></i></b-button>
|
||||
<b-form-datepicker right button-only button-variant="secondary" @context="datePickerChanged"></b-form-datepicker>
|
||||
</b-button-group>
|
||||
<b-button-group class="mx-1">
|
||||
<b-button v-html="'>>'" class="p-2 pr-3 pl-3"
|
||||
@click="setShowDate($refs.header.headerProps.nextPeriod)"></b-button>
|
||||
</b-button-group>
|
||||
</b-button-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 mt-2" style="padding-bottom: 60px">
|
||||
<div v-for="day in mobileSimpleGrid" v-bind:key="day.day">
|
||||
<b-list-group>
|
||||
<b-list-group-item>
|
||||
<div class="d-flex flex-row align-middle">
|
||||
<h6 class="mb-0 mt-1 align-middle">{{ day.date_label }}</h6>
|
||||
|
||||
<div class="flex-grow-1 text-right">
|
||||
<b-button class="btn-sm btn-outline-primary" @click="showMealPlanEditModal(null, day.create_default_date)"><i
|
||||
class="fa fa-plus"></i></b-button>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<button class="btn btn-success shadow-none mt-1 btn-block" @click="createEntryClick(new Date())"><i
|
||||
class="fas fa-calendar-plus"></i> {{ $t("Create") }}
|
||||
</button>
|
||||
<button class="btn btn-warning shadow-none mt-1 btn-block" @click="createAutoPlan(new Date())"><i
|
||||
class="fas fa-calendar-plus"></i> {{ $t("Auto_Planner") }}
|
||||
</button>
|
||||
<a class="btn btn-primary shadow-none mt-1 btn-blockmt-1 btn-block" :href="iCalUrl"><i class="fas fa-download"></i>
|
||||
{{ $t("Export_To_ICal") }}
|
||||
</a>
|
||||
|
||||
</b-list-group-item>
|
||||
<b-list-group-item v-for="plan in day.plan_entries" v-bind:key="plan.entry.id">
|
||||
<div class="d-flex flex-row align-items-center">
|
||||
<div>
|
||||
<b-img style="height: 50px; width: 50px; object-fit: cover"
|
||||
:src="plan.entry.recipe.image" rounded="circle" v-if="plan.entry.recipe?.image"></b-img>
|
||||
<b-img style="height: 50px; width: 50px; object-fit: cover"
|
||||
:src="image_placeholder" rounded="circle" v-else></b-img>
|
||||
</div>
|
||||
<div class="flex-grow-1 ml-2"
|
||||
style="text-overflow: ellipsis; overflow-wrap: anywhere;">
|
||||
<a class="btn btn-info shadow-none mt-1 btn-block" :href="resolveDjangoUrl('view_settings')">
|
||||
<i class="fas fa-cogs"></i> {{ $t("Settings") }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col col-10">
|
||||
<div class="row calender-row ">
|
||||
<div class="col-12 calender-parent">
|
||||
<calendar-view
|
||||
:show-date="showDate"
|
||||
:enable-date-selection="true"
|
||||
class="theme-default"
|
||||
:items="plan_items"
|
||||
:display-period-uom="settings.displayPeriodUom"
|
||||
:period-changed-callback="periodChangedCallback"
|
||||
:enable-drag-drop="true"
|
||||
:item-content-height="item_height"
|
||||
@click-date="createEntryClick"
|
||||
@drop-on-date="moveEntry"
|
||||
:display-period-count="settings.displayPeriodCount"
|
||||
:starting-day-of-week="settings.startingDayOfWeek"
|
||||
:display-week-numbers="settings.displayWeekNumbers"
|
||||
>
|
||||
<template #item="{ value, weekStartDate, top }">
|
||||
<meal-plan-card
|
||||
:value="value"
|
||||
:week-start-date="weekStartDate"
|
||||
:top="top"
|
||||
:detailed="detailed_items"
|
||||
:item_height="item_height"
|
||||
@dragstart="dragged_item = value"
|
||||
@click-item="entryClick"
|
||||
@open-context-menu="openContextMenu"
|
||||
/>
|
||||
</template>
|
||||
<template #header="{ headerProps }">
|
||||
<meal-plan-calender-header
|
||||
ref="header"
|
||||
:header-props="headerProps"
|
||||
@input="setShowDate"
|
||||
@delete-dragged="deleteEntry(dragged_item)"
|
||||
@create-new="createEntryClick(new Date())"
|
||||
@set-starting-day-back="setStartingDay(-1)"
|
||||
@set-starting-day-forward="setStartingDay(1)"
|
||||
:i-cal-url="iCalUrl"
|
||||
:options="options"
|
||||
:settings_prop="settings"
|
||||
/>
|
||||
</template>
|
||||
</calendar-view>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row d-block d-lg-none">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="">
|
||||
<div>
|
||||
<div class="col-12">
|
||||
<div class="col-12 d-flex justify-content-center mt-2">
|
||||
<b-button-toolbar key-nav aria-label="Toolbar with button groups">
|
||||
<b-button-group class="mx-1">
|
||||
<b-button v-html="'<<'" class="p-2 pr-3 pl-3"
|
||||
@click="setShowDate($refs.header.headerProps.previousPeriod)"></b-button>
|
||||
</b-button-group>
|
||||
<b-button-group class="mx-1">
|
||||
<b-button @click="setShowDate($refs.header.headerProps.currentPeriod)"><i
|
||||
class="fas fa-home"></i></b-button>
|
||||
<b-form-datepicker right button-only button-variant="secondary" @context="datePickerChanged"></b-form-datepicker>
|
||||
</b-button-group>
|
||||
<b-button-group class="mx-1">
|
||||
<b-button v-html="'>>'" class="p-2 pr-3 pl-3"
|
||||
@click="setShowDate($refs.header.headerProps.nextPeriod)"></b-button>
|
||||
</b-button-group>
|
||||
</b-button-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 mt-2" style="padding-bottom: 60px">
|
||||
<div v-for="day in mobileSimpleGrid" v-bind:key="day.day">
|
||||
<b-list-group>
|
||||
<b-list-group-item>
|
||||
<div class="d-flex flex-row align-middle">
|
||||
<h6 class="mb-0 mt-1 align-middle">{{ day.date_label }}</h6>
|
||||
|
||||
<div class="flex-grow-1 text-right">
|
||||
<b-button class="btn-sm btn-outline-primary" @click="showMealPlanEditModal(null, day.create_default_date)"><i
|
||||
class="fa fa-plus"></i></b-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</b-list-group-item>
|
||||
<b-list-group-item v-for="plan in day.plan_entries" v-bind:key="plan.entry.id">
|
||||
<div class="d-flex flex-row align-items-center">
|
||||
<div>
|
||||
<b-img style="height: 50px; width: 50px; object-fit: cover"
|
||||
:src="plan.entry.recipe.image" rounded="circle" v-if="plan.entry.recipe?.image"></b-img>
|
||||
<b-img style="height: 50px; width: 50px; object-fit: cover"
|
||||
:src="image_placeholder" rounded="circle" v-else></b-img>
|
||||
</div>
|
||||
<div class="flex-grow-1 ml-2"
|
||||
style="text-overflow: ellipsis; overflow-wrap: anywhere;">
|
||||
<span class="two-row-text">
|
||||
<a :href="resolveDjangoUrl('view_recipe', plan.entry.recipe.id)" v-if="plan.entry.recipe">{{ plan.entry.recipe.name }}</a>
|
||||
<span v-else>{{ plan.entry.title }}</span> <br/>
|
||||
</span>
|
||||
<span v-if="plan.entry.note" class="two-row-text">
|
||||
<span v-if="plan.entry.note" class="two-row-text">
|
||||
<small>{{ plan.entry.note }}</small> <br/>
|
||||
</span>
|
||||
<small class="text-muted">
|
||||
<span v-if="plan.entry.shopping" class="font-light"><i class="fas fa-shopping-cart fa-xs "/></span>
|
||||
{{ plan.entry.meal_type_name }}
|
||||
<span v-if="plan.entry.recipe">
|
||||
<small class="text-muted">
|
||||
<span v-if="plan.entry.shopping" class="font-light"><i class="fas fa-shopping-cart fa-xs "/></span>
|
||||
{{ plan.entry.meal_type_name }}
|
||||
<span v-if="plan.entry.recipe">
|
||||
- <i class="fa fa-clock"></i> {{ plan.entry.recipe.working_time + plan.entry.recipe.waiting_time }} {{ $t('min') }}
|
||||
</span>
|
||||
</small>
|
||||
</div>
|
||||
<div class="hover-button">
|
||||
<a class="pr-2" @click.stop="openContextMenu($event, {originalItem: plan})"><i class="fas fa-ellipsis-v"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</b-list-group-item>
|
||||
|
||||
</b-list-group>
|
||||
</div>
|
||||
</div>
|
||||
</small>
|
||||
</div>
|
||||
<div class="hover-button">
|
||||
<a class="pr-2" @click.stop="openContextMenu($event, {originalItem: plan})"><i class="fas fa-ellipsis-v"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</b-list-group-item>
|
||||
|
||||
</b-list-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</b-tab>
|
||||
<b-tab :title="$t('Settings')">
|
||||
<div class="row mt-3">
|
||||
<div class="col-12 col-md-9 col-lg-6">
|
||||
<h5>{{ $t("Meal_Types") }}</h5>
|
||||
<div>
|
||||
<draggable :list="meal_types" group="meal_types" :empty-insert-threshold="10"
|
||||
@sort="sortMealTypes()" ghost-class="ghost">
|
||||
<b-card no-body class="mt-1 list-group-item p-2" style="cursor: move"
|
||||
v-for="(meal_type, index) in meal_types" v-hover :key="meal_type.id">
|
||||
<b-card-header class="p-2 border-0">
|
||||
<div class="row">
|
||||
<div class="col-2">
|
||||
<button type="button" class="btn btn-lg shadow-none"><i
|
||||
class="fas fa-arrows-alt-v"></i></button>
|
||||
</div>
|
||||
<div class="col-10">
|
||||
<h5 class="mt-1 mb-1">
|
||||
{{ meal_type.name }}<span class="float-right text-primary" style="cursor: pointer"
|
||||
><i class="fa"
|
||||
v-bind:class="{ 'fa-pen': !meal_type.editing, 'fa-save': meal_type.editing }"
|
||||
@click="editOrSaveMealType(index)" aria-hidden="true"></i
|
||||
></span>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</b-card-header>
|
||||
<b-card-body class="p-4" v-if="meal_type.editing">
|
||||
<div class="form-group">
|
||||
<label>{{ $t("Name") }}</label>
|
||||
<input class="form-control" :placeholder="$t('Name')"
|
||||
v-model="meal_type.name"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{ $t("Color") }}</label>
|
||||
<input class="form-control" type="color" name="Name"
|
||||
:value="meal_type.color"
|
||||
@change="meal_type.color = $event.target.value"/>
|
||||
</div>
|
||||
<b-form-checkbox id="checkbox-1" v-model="meal_type.default"
|
||||
name="default_checkbox" class="mb-2">
|
||||
{{ $t("Default") }}
|
||||
</b-form-checkbox>
|
||||
<button class="btn btn-danger" @click="deleteMealType(index)">{{
|
||||
$t("Delete")
|
||||
}}
|
||||
</button>
|
||||
<button class="btn btn-primary float-right" @click="editOrSaveMealType(index)">
|
||||
{{ $t("Save") }}
|
||||
</button>
|
||||
</b-card-body>
|
||||
</b-card>
|
||||
</draggable>
|
||||
<button class="btn btn-success float-right mt-1" @click="newMealType">
|
||||
<i class="fas fa-plus"></i>
|
||||
{{ $t("New_Meal_Type") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</b-tab>
|
||||
</b-tabs>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<ContextMenu ref="menu">
|
||||
<template #menu="{ contextData }">
|
||||
<ContextMenuItem
|
||||
@ -336,7 +282,6 @@ export default {
|
||||
ContextMenu,
|
||||
ContextMenuItem,
|
||||
MealPlanCalenderHeader,
|
||||
draggable,
|
||||
BottomNavigationBar,
|
||||
},
|
||||
mixins: [CalendarMathMixin, ApiMixin, ResolveUrlMixin],
|
||||
@ -362,7 +307,6 @@ export default {
|
||||
displayWeekNumbers: true,
|
||||
},
|
||||
dragged_item: null,
|
||||
current_tab: 0,
|
||||
meal_types: [],
|
||||
current_context_menu_item: null,
|
||||
options: {
|
||||
@ -404,7 +348,7 @@ export default {
|
||||
},
|
||||
item_height: function () {
|
||||
if (this.settings.displayPeriodUom === "week") {
|
||||
return "10rem"
|
||||
return "3rem"
|
||||
} else {
|
||||
return "1.6rem"
|
||||
}
|
||||
@ -428,7 +372,7 @@ export default {
|
||||
date: moment_date,
|
||||
create_default_date: moment_date.format("YYYY-MM-DD"), // improve meal plan edit modal to do formatting itself and accept dates
|
||||
date_label: moment_date.format('ddd DD.MM'),
|
||||
plan_entries: this.plan_items.filter((m) => moment(m.startDate).isSame(moment_date, 'day'))
|
||||
plan_entries: this.plan_items.filter((m) => moment_date.isBetween(moment(m.startDate), moment(m.endDate), 'day', '[]'))
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -436,18 +380,14 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(function () {
|
||||
if (this.$cookies.isKey(SETTINGS_COOKIE_NAME)) {
|
||||
this.settings = Object.assign({}, this.settings, this.$cookies.get(SETTINGS_COOKIE_NAME))
|
||||
}
|
||||
})
|
||||
this.settings = useMealPlanStore().client_settings
|
||||
this.$i18n.locale = window.CUSTOM_LOCALE
|
||||
moment.locale(window.CUSTOM_LOCALE)
|
||||
},
|
||||
watch: {
|
||||
settings: {
|
||||
handler() {
|
||||
this.$cookies.set(SETTINGS_COOKIE_NAME, this.settings, "360d")
|
||||
useMealPlanStore().updateClientSettings(this.settings)
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
@ -554,12 +494,16 @@ export default {
|
||||
moveEntry(null_object, target_date, drag_event) {
|
||||
useMealPlanStore().plan_list.forEach((entry) => {
|
||||
if (entry.id === this.dragged_item.id) {
|
||||
let fromToDiff = Math.abs(moment(entry.to_date).diff(moment(entry.from_date), 'days'))
|
||||
|
||||
if (drag_event.ctrlKey) {
|
||||
let new_entry = Object.assign({}, entry)
|
||||
new_entry.date = target_date
|
||||
new_entry.from_date = target_date
|
||||
new_entry.to_date = moment(target_date).add(fromToDiff, 'd')
|
||||
this.createEntry(new_entry)
|
||||
} else {
|
||||
entry.date = target_date
|
||||
entry.from_date = target_date
|
||||
entry.to_date = moment(target_date).add(fromToDiff, 'd')
|
||||
this.saveEntry(entry)
|
||||
}
|
||||
}
|
||||
@ -568,7 +512,8 @@ export default {
|
||||
moveEntryLeft(data) {
|
||||
useMealPlanStore().plan_list.forEach((entry) => {
|
||||
if (entry.id === data.id) {
|
||||
entry.date = moment(entry.date).subtract(1, "d")
|
||||
entry.from_date = moment(entry.from_date).subtract(1, "d")
|
||||
entry.to_date = moment(entry.to_date).subtract(1, "d")
|
||||
this.saveEntry(entry)
|
||||
}
|
||||
})
|
||||
@ -576,7 +521,8 @@ export default {
|
||||
moveEntryRight(data) {
|
||||
useMealPlanStore().plan_list.forEach((entry) => {
|
||||
if (entry.id === data.id) {
|
||||
entry.date = moment(entry.date).add(1, "d")
|
||||
entry.from_date = moment(entry.from_date).add(1, "d")
|
||||
entry.to_date = moment(entry.to_date).add(1, "d")
|
||||
this.saveEntry(entry)
|
||||
}
|
||||
})
|
||||
@ -594,7 +540,8 @@ export default {
|
||||
openEntryEdit(entry) {
|
||||
this.$bvModal.show(`id_meal_plan_edit_modal`)
|
||||
this.entryEditing = entry
|
||||
this.entryEditing.date = moment(entry.date).format("YYYY-MM-DD")
|
||||
this.entryEditing.from_date = moment(entry.from_date).format("YYYY-MM-DD")
|
||||
this.entryEditing.to_date = moment(entry.to_date).format("YYYY-MM-DD")
|
||||
if (this.entryEditing.recipe != null) {
|
||||
this.entryEditing.title_placeholder = this.entryEditing.recipe.name
|
||||
}
|
||||
@ -617,21 +564,32 @@ export default {
|
||||
})
|
||||
},
|
||||
saveEntry(entry) {
|
||||
entry.date = moment(entry.date).format("YYYY-MM-DD")
|
||||
entry.from_date = moment(entry.from_date).format("YYYY-MM-DD")
|
||||
entry.to_date = moment(entry.to_date).format("YYYY-MM-DD")
|
||||
|
||||
if (entry.from_date > entry.to_date) {
|
||||
StandardToasts.makeStandardToast(this, StandardToasts.FAIL_UPDATE)
|
||||
entry.to_date = entry.from_date
|
||||
} else {
|
||||
useMealPlanStore().updateObject(entry)
|
||||
}
|
||||
|
||||
|
||||
useMealPlanStore().updateObject(entry)
|
||||
},
|
||||
createEntry(entry) {
|
||||
entry.date = moment(entry.date).format("YYYY-MM-DD")
|
||||
entry.from_date = moment(entry.from_date).format("YYYY-MM-DD")
|
||||
entry.to_date = moment(entry.to_date).format("YYYY-MM-DD")
|
||||
|
||||
useMealPlanStore().createObject(entry)
|
||||
},
|
||||
buildItem(plan_entry) {
|
||||
//dirty hack to order items within a day
|
||||
let date = moment(plan_entry.date).add(plan_entry.meal_type.order, "m")
|
||||
let from_date = moment(plan_entry.from_date).add(plan_entry.meal_type.order, "m")
|
||||
let to_date = moment(plan_entry.to_date).add(plan_entry.meal_type.order, "m")
|
||||
return {
|
||||
id: plan_entry.id,
|
||||
startDate: date,
|
||||
endDate: date,
|
||||
startDate: from_date,
|
||||
endDate: to_date,
|
||||
entry: plan_entry,
|
||||
}
|
||||
},
|
||||
@ -684,7 +642,7 @@ export default {
|
||||
}
|
||||
|
||||
.calender-row {
|
||||
height: calc(100vh - 240px);
|
||||
height: calc(100vh - 140px);
|
||||
}
|
||||
|
||||
.calender-parent {
|
||||
|
@ -723,9 +723,9 @@
|
||||
<div class="row">
|
||||
<div class="col col-md-12">
|
||||
<div
|
||||
style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); column-gap: 0.5rem; row-gap: 0.5rem; grid-auto-rows: max-content"
|
||||
>
|
||||
<div v-for="day in meal_plan_grid" v-bind:key="day.day" :class="{ 'd-none d-sm-block': day.plan_entries.length === 0 }">
|
||||
|
||||
style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); column-gap: 0.5rem;row-gap: 0.5rem; grid-auto-rows: max-content; ">
|
||||
<div v-for="day in meal_plan_grid" v-bind:key="day.day" :class="{'d-none d-sm-block': day.plan_entries.length === 0}">
|
||||
<b-list-group>
|
||||
<b-list-group-item class="hover-div pb-0">
|
||||
<div class="d-flex flex-row align-items-center">
|
||||
@ -739,16 +739,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</b-list-group-item>
|
||||
<b-list-group-item v-for="plan in day.plan_entries" v-bind:key="plan.id" class="hover-div">
|
||||
<b-list-group-item v-for="plan in day.plan_entries" v-bind:key="plan.id" class="hover-div p-0 pr-2">
|
||||
<div class="d-flex flex-row align-items-center">
|
||||
<div>
|
||||
<b-img
|
||||
style="height: 50px; width: 50px; object-fit: cover"
|
||||
:src="plan.recipe.image"
|
||||
rounded="circle"
|
||||
v-if="plan.recipe?.image"
|
||||
></b-img>
|
||||
<b-img style="height: 50px; width: 50px; object-fit: cover" :src="image_placeholder" rounded="circle" v-else></b-img>
|
||||
|
||||
<img style="height: 50px; width: 50px; object-fit: cover" :src="plan.recipe.image" v-if="plan.recipe?.image"/>
|
||||
<img style="height: 50px; width: 50px; object-fit: cover" :src="image_placeholder" v-else/>
|
||||
|
||||
</div>
|
||||
<div class="flex-grow-1 ml-2" style="text-overflow: ellipsis; overflow-wrap: anywhere">
|
||||
<span class="two-row-text">
|
||||
@ -986,8 +983,8 @@ export default {
|
||||
grid.push({
|
||||
date: moment_date,
|
||||
create_default_date: moment_date.format("YYYY-MM-DD"), // improve meal plan edit modal to do formatting itself and accept dates
|
||||
date_label: moment_date.format("ddd DD.MM"),
|
||||
plan_entries: this.meal_plan_store.plan_list.filter((m) => moment(m.date).isSame(moment_date, "day")),
|
||||
date_label: moment_date.format('ddd DD.MM'),
|
||||
plan_entries: this.meal_plan_store.plan_list.filter((m) => moment_date.isBetween(moment(m.from_date), moment(m.to_date), 'day', '[]'))
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1204,22 +1201,6 @@ export default {
|
||||
console.log("loadMealpLan")
|
||||
this.meal_plan_store = useMealPlanStore()
|
||||
this.meal_plan_store.refreshFromAPI(moment().format("YYYY-MM-DD"), moment().add(this.ui.meal_plan_days, "days").format("YYYY-MM-DD"))
|
||||
|
||||
// if (this.ui.show_meal_plan) {
|
||||
// let params = {
|
||||
// options: {
|
||||
// query: {
|
||||
// from_date: moment().format("YYYY-MM-DD"),
|
||||
// to_date: moment().add(this.ui.meal_plan_days, "days").format("YYYY-MM-DD"),
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
// this.genericAPI(this.Models.MEAL_PLAN, this.Actions.LIST, params).then((result) => {
|
||||
// this.meal_plans = result.data
|
||||
// })
|
||||
// } else {
|
||||
// this.meal_plans = []
|
||||
// }
|
||||
},
|
||||
genericSelectChanged: function (obj) {
|
||||
if (obj.var.includes("::")) {
|
||||
|
@ -29,7 +29,7 @@
|
||||
<b-form-select class="ml-1" id="UomInput" v-model="settings.displayPeriodCount" :options="options.displayPeriodCount"></b-form-select>
|
||||
</span>
|
||||
<span
|
||||
class="delete-area text-danger p-1 mr-2 ml-1 d-none d-sm-flex align-items-center"
|
||||
class="delete-area text-danger p-1 mr-2 ml-1 d-none d-sm-flex align-items-center rounded"
|
||||
@drop.prevent="onDeleteDrop($event)"
|
||||
@dragenter.prevent="onDeleteDragEnter($event)"
|
||||
@dragleave.prevent="onDeleteDragLeave($event)"
|
||||
|
@ -1,52 +1,34 @@
|
||||
<template>
|
||||
<div
|
||||
v-hover
|
||||
class="card cv-item meal-plan-card p-0"
|
||||
:key="value.id"
|
||||
:draggable="true"
|
||||
:style="`top:${top};max-height:${item_height}`"
|
||||
@dragstart="onDragItemStart(value, $event)"
|
||||
@click="onClickItem(value, $event)"
|
||||
:aria-grabbed="value == currentDragItem"
|
||||
:class="value.classes"
|
||||
@contextmenu.prevent="$emit('open-context-menu', $event, value)"
|
||||
>
|
||||
<div class="card-header p-1 text-center text-primary border-bottom-0" v-if="detailed" :style="`background-color: ${background_color}`">
|
||||
<span class="font-light text-center" v-if="entry.entry.meal_type.icon != null">{{ entry.entry.meal_type.icon }}</span>
|
||||
<span class="font-light d-none d-md-inline">{{ entry.entry.meal_type.name }}</span>
|
||||
<span v-if="entry.entry.shopping" class="font-light"><i class="fas fa-shopping-cart fa-xs float-left" v-b-tooltip.hover.top :title="$t('in_shopping')"/></span>
|
||||
</div>
|
||||
<div class="card-img-overlay h-100 d-flex flex-column justify-content-right float-right text-right p-0" v-if="detailed">
|
||||
<a>
|
||||
<div style="position: static">
|
||||
<div class="dropdown b-dropdown position-static btn-group">
|
||||
<button
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
type="button"
|
||||
class="btn btn-link text-decoration-none text-body pr-2 dropdown-toggle-no-caret"
|
||||
@click.stop="$emit('open-context-menu', $event, value)"
|
||||
>
|
||||
<i class="fas fa-ellipsis-v fa-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-header p-1 text-center" v-if="detailed" :style="`background-color: ${background_color}`">
|
||||
<span class="font-light">{{ title }}</span>
|
||||
</div>
|
||||
<b-img fluid class="card-img-bottom" :src="entry.entry.recipe.image" v-if="hasRecipe && detailed"></b-img>
|
||||
<b-img fluid class="card-img-bottom" :src="image_placeholder" v-if="detailed && ((!hasRecipe && entry.entry.note === '') || (hasRecipe && entry.entry.recipe.image === null))"></b-img>
|
||||
<div class="card-body p-1" v-if="detailed && entry.entry.recipe == null" :style="`background-color: ${background_color}`">
|
||||
<p>{{ entry.entry.note }}</p>
|
||||
</div>
|
||||
|
||||
<div class="row p-1 flex-nowrap" v-if="!detailed" :style="`background-color: ${background_color}`">
|
||||
<div class="col-10 d-inline-block text-truncate" :style="`max-height:${item_height}`">
|
||||
<span class="font-light">{{ title }}</span>
|
||||
<div v-hover
|
||||
class="card cv-item meal-plan-card p-0"
|
||||
:key="value.id"
|
||||
:draggable="true"
|
||||
:style="{'top': top, 'height': item_height, 'border-color': entry.entry.meal_type.color}"
|
||||
@dragstart="onDragItemStart(value, $event)"
|
||||
@click="onClickItem(value, $event)"
|
||||
:aria-grabbed="value == currentDragItem"
|
||||
:class="value.classes"
|
||||
@contextmenu.prevent="$emit('open-context-menu', $event, value)">
|
||||
|
||||
<div class="d-flex flex-row align-items-center">
|
||||
<div class="flex-column">
|
||||
<img class="" style="object-fit: cover" :style="{'height': item_height, 'width': item_height}" :src="entry.entry.recipe.image"
|
||||
v-if="hasRecipe && detailed"/>
|
||||
<img class="" style="object-fit: cover" :style="{'height': item_height, 'width': item_height}" :src="image_placeholder"
|
||||
v-if="detailed && ((!hasRecipe && entry.entry.note === '') || (hasRecipe && entry.entry.recipe.image === null))"/>
|
||||
</div>
|
||||
<div class="flex-column flex-grow-0 align-middle justify-content-center">
|
||||
<div class="card-body p-0 pl-1 align-middle">
|
||||
|
||||
<span class="font-light" :class="{'two-line-text': detailed,'one-line-text': !detailed,}">
|
||||
<i class="fas fa-shopping-cart fa-xs float-left" v-b-tooltip.hover.top :title="$t('in_shopping')" v-if="entry.entry.shopping"/>
|
||||
{{ title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -131,4 +113,22 @@ export default {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.two-line-text {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.one-line-text {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
|
@ -6,14 +6,13 @@
|
||||
<div class="row">
|
||||
<div class="col col-md-12">
|
||||
<div class="row">
|
||||
<div class="col-6 col-lg-9">
|
||||
<div class="col-12">
|
||||
<b-input-group>
|
||||
<b-form-input id="TitleInput" v-model="entryEditing.title"
|
||||
:placeholder="entryEditing.title_placeholder"
|
||||
@change="missing_recipe = false"></b-form-input>
|
||||
<b-input-group-append class="d-none d-lg-block">
|
||||
<b-button variant="primary" @click="entryEditing.title = ''"><i
|
||||
class="fa fa-eraser"></i></b-button>
|
||||
<b-button variant="primary" @click="entryEditing.title = ''"><i class="fa fa-eraser"></i></b-button>
|
||||
</b-input-group-append>
|
||||
</b-input-group>
|
||||
<span class="text-danger" v-if="missing_recipe">{{
|
||||
@ -23,13 +22,30 @@
|
||||
$t("Title")
|
||||
}}</small>
|
||||
</div>
|
||||
<div class="col-6 col-lg-3">
|
||||
<input type="date" id="DateInput" class="form-control" v-model="entryEditing.date"/>
|
||||
<small tabindex="-1" class="form-text text-muted">{{ $t("Date") }}</small>
|
||||
<div class="col-12 col-lg-6">
|
||||
<b-input-group>
|
||||
<b-form-input type="date" v-model="entryEditing.from_date"></b-form-input>
|
||||
<b-input-group-append>
|
||||
<b-button variant="secondary" @click="entryEditing.from_date = changeDate(entryEditing.from_date, -1)"><i class="fas fa-minus"></i></b-button>
|
||||
<b-button variant="primary" @click="entryEditing.from_date = changeDate(entryEditing.from_date, 1)"><i class="fas fa-plus"></i></b-button>
|
||||
</b-input-group-append>
|
||||
</b-input-group>
|
||||
|
||||
<small tabindex="-1" class="form-text text-muted">{{ $t("StartDate") }}</small>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6">
|
||||
<b-input-group>
|
||||
<b-form-input type="date" v-model="entryEditing.to_date"></b-form-input>
|
||||
<b-input-group-append>
|
||||
<b-button variant="secondary" @click="entryEditing.to_date = changeDate(entryEditing.to_date, -1)"><i class="fas fa-minus"></i></b-button>
|
||||
<b-button variant="primary" @click="entryEditing.to_date = changeDate(entryEditing.to_date, 1)"><i class="fas fa-plus"></i></b-button>
|
||||
</b-input-group-append>
|
||||
</b-input-group>
|
||||
<small tabindex="-1" class="form-text text-muted">{{ $t("EndDate") }}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-12 col-lg-6 col-xl-6">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<b-form-group>
|
||||
<generic-multiselect
|
||||
@change="selectRecipe"
|
||||
@ -43,7 +59,11 @@
|
||||
></generic-multiselect>
|
||||
<small tabindex="-1" class="form-text text-muted">{{ $t("Recipe") }}</small>
|
||||
</b-form-group>
|
||||
<b-form-group class="mt-3">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-6 col-xl-6">
|
||||
<b-form-group class="">
|
||||
<generic-multiselect
|
||||
required
|
||||
@change="selectMealType"
|
||||
@ -142,6 +162,7 @@ const {ApiApiFactory} = require("@/utils/openapi/api")
|
||||
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
|
||||
import {useMealPlanStore} from "@/stores/MealPlanStore";
|
||||
import ShoppingModal from "@/components/Modals/ShoppingModal.vue";
|
||||
import moment from "moment";
|
||||
|
||||
Vue.use(BootstrapVue)
|
||||
Vue.use(VueCookies)
|
||||
@ -221,7 +242,8 @@ export default {
|
||||
}
|
||||
|
||||
if (this.create_date) {
|
||||
this.entryEditing.date = this.create_date
|
||||
this.entryEditing.from_date = this.create_date
|
||||
this.entryEditing.to_date = this.create_date
|
||||
}
|
||||
|
||||
useUserPreferenceStore().getData().then(userPreference => {
|
||||
@ -290,6 +312,9 @@ export default {
|
||||
this.entryEditing.servings = 1
|
||||
}
|
||||
},
|
||||
changeDate(date, change){
|
||||
return moment(date).add(change, 'd').format("YYYY-MM-DD")
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@ -184,7 +184,7 @@ export default {
|
||||
localStorage.setItem("pinned_recipes", JSON.stringify(pinnedRecipes))
|
||||
},
|
||||
saveMealPlan: function (entry) {
|
||||
entry.date = moment(entry.date).format("YYYY-MM-DD")
|
||||
entry.from_date = moment(entry.from_date).format("YYYY-MM-DD")
|
||||
let reviewshopping = entry.addshopping && entry.reviewshopping
|
||||
entry.addshopping = entry.addshopping && !entry.reviewshopping
|
||||
|
||||
@ -208,7 +208,7 @@ export default {
|
||||
createMealPlan(data) {
|
||||
this.entryEditing = this.options.entryEditing
|
||||
this.entryEditing.recipe = this.recipe
|
||||
this.entryEditing.date = moment(new Date()).format("YYYY-MM-DD")
|
||||
this.entryEditing.from_date = moment(new Date()).format("YYYY-MM-DD")
|
||||
this.$nextTick(function () {
|
||||
this.$bvModal.show(`modal-meal-plan_${this.modal_id}`)
|
||||
})
|
||||
|
@ -14,24 +14,24 @@
|
||||
|
||||
<hr/>
|
||||
|
||||
<b-form v-if="meal_plan_store">
|
||||
<b-form v-if="settings">
|
||||
<b-form-group id="UomInput" :label="$t('Period')" :description="$t('Plan_Period_To_Show')"
|
||||
label-for="UomInput">
|
||||
<b-form-select id="UomInput" v-model="meal_plan_store.client_settings.displayPeriodUom"
|
||||
<b-form-select id="UomInput" v-model="settings.displayPeriodUom"
|
||||
:options="calendar_options.displayPeriodUom"></b-form-select>
|
||||
</b-form-group>
|
||||
<b-form-group id="PeriodInput" :label="$t('Periods')"
|
||||
:description="$t('Plan_Show_How_Many_Periods')" label-for="PeriodInput">
|
||||
<b-form-select id="PeriodInput" v-model="meal_plan_store.client_settings.displayPeriodCount"
|
||||
<b-form-select id="PeriodInput" v-model="settings.displayPeriodCount"
|
||||
:options="calendar_options.displayPeriodCount"></b-form-select>
|
||||
</b-form-group>
|
||||
<b-form-group id="DaysInput" :label="$t('Starting_Day')" :description="$t('Starting_Day')"
|
||||
label-for="DaysInput">
|
||||
<b-form-select id="DaysInput" v-model="meal_plan_store.client_settings.startingDayOfWeek"
|
||||
<b-form-select id="DaysInput" v-model="settings.startingDayOfWeek"
|
||||
:options="dayNames()"></b-form-select>
|
||||
</b-form-group>
|
||||
<b-form-group id="WeekNumInput" :label="$t('Week_Numbers')">
|
||||
<b-form-checkbox v-model="meal_plan_store.client_settings.displayWeekNumbers" name="week_num">
|
||||
<b-form-checkbox v-model="settings.displayWeekNumbers" name="week_num">
|
||||
{{ $t("Show_Week_Numbers") }}
|
||||
</b-form-checkbox>
|
||||
</b-form-group>
|
||||
@ -96,7 +96,7 @@ export default {
|
||||
return {
|
||||
user_preferences: undefined,
|
||||
languages: [],
|
||||
meal_plan_store: undefined,
|
||||
settings: undefined,
|
||||
calendar_options: {
|
||||
displayPeriodUom: [
|
||||
{text: this.$t("Week"), value: "week"},
|
||||
@ -108,14 +108,24 @@ export default {
|
||||
meal_types: [],
|
||||
generic_action: null,
|
||||
editing_meal_type: null,
|
||||
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
settings: {
|
||||
handler() {
|
||||
useMealPlanStore().updateClientSettings(this.settings)
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.user_preferences = this.preferences
|
||||
this.languages = window.AVAILABLE_LANGUAGES
|
||||
this.loadSettings()
|
||||
|
||||
this.meal_plan_store = useMealPlanStore()
|
||||
this.settings = useMealPlanStore().client_settings
|
||||
|
||||
|
||||
this.loadMealTypes()
|
||||
},
|
||||
|
@ -100,6 +100,8 @@
|
||||
"Energy": "Energy",
|
||||
"Nutrition": "Nutrition",
|
||||
"Date": "Date",
|
||||
"StartDate": "Start Date",
|
||||
"EndDate": "End Date",
|
||||
"Share": "Share",
|
||||
"Automation": "Automation",
|
||||
"Parameter": "Parameter",
|
||||
|
@ -478,5 +478,7 @@
|
||||
"Auto_Sort": "自动分类",
|
||||
"Auto_Sort_Help": "将所有食材移动到最恰当的步骤。",
|
||||
"Create Recipe": "创建食谱",
|
||||
"Import Recipe": "导入食谱"
|
||||
"Import Recipe": "导入食谱",
|
||||
"recipe_property_info": "您也可以为食物添加属性,以便根据食谱自动计算!",
|
||||
"per_serving": "每份"
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import {defineStore} from 'pinia'
|
||||
import {ApiApiFactory} from "@/utils/openapi/api";
|
||||
|
||||
const _STORE_ID = 'meal_plan_store'
|
||||
const _LOCAL_STORAGE_KEY = 'MEAL_PLAN_CLIENT_SETTINGS'
|
||||
import Vue from "vue"
|
||||
import {StandardToasts} from "@/utils/utils";
|
||||
/*
|
||||
@ -12,12 +13,7 @@ export const useMealPlanStore = defineStore(_STORE_ID, {
|
||||
state: () => ({
|
||||
plans: {},
|
||||
currently_updating: null,
|
||||
client_settings: {
|
||||
displayPeriodUom: "week",
|
||||
displayPeriodCount: 2,
|
||||
startingDayOfWeek: 1,
|
||||
displayWeekNumbers: true,
|
||||
},
|
||||
settings: null,
|
||||
}),
|
||||
getters: {
|
||||
plan_list: function () {
|
||||
@ -29,7 +25,8 @@ export const useMealPlanStore = defineStore(_STORE_ID, {
|
||||
},
|
||||
empty_meal_plan: function () {
|
||||
return {
|
||||
date: null,
|
||||
from_date: null,
|
||||
to_date: null,
|
||||
id: -1,
|
||||
meal_type: null,
|
||||
note: "",
|
||||
@ -40,6 +37,12 @@ export const useMealPlanStore = defineStore(_STORE_ID, {
|
||||
title: "",
|
||||
title_placeholder: 'Title', // meal plan edit modal should be improved to not need this
|
||||
}
|
||||
},
|
||||
client_settings: function () {
|
||||
if (this.settings === null) {
|
||||
this.settings = this.loadClientSettings()
|
||||
}
|
||||
return this.settings
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
@ -90,6 +93,23 @@ export const useMealPlanStore = defineStore(_STORE_ID, {
|
||||
}).catch(err => {
|
||||
StandardToasts.makeStandardToast(this, StandardToasts.FAIL_DELETE, err)
|
||||
})
|
||||
},
|
||||
updateClientSettings(settings) {
|
||||
this.settings = settings
|
||||
localStorage.setItem(_LOCAL_STORAGE_KEY, JSON.stringify(this.settings))
|
||||
},
|
||||
loadClientSettings() {
|
||||
let s = localStorage.getItem(_LOCAL_STORAGE_KEY)
|
||||
if (s === null || s === {}) {
|
||||
return {
|
||||
displayPeriodUom: "week",
|
||||
displayPeriodCount: 3,
|
||||
startingDayOfWeek: 1,
|
||||
displayWeekNumbers: true,
|
||||
}
|
||||
} else {
|
||||
return JSON.parse(s)
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
@ -1812,7 +1812,13 @@ export interface MealPlan {
|
||||
* @type {string}
|
||||
* @memberof MealPlan
|
||||
*/
|
||||
date: string;
|
||||
from_date: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof MealPlan
|
||||
*/
|
||||
to_date?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {MealPlanMealType}
|
||||
|
Reference in New Issue
Block a user