mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-03 21:37:49 -05:00
Merge branch 'develop' into feature/2402-make-now-count
This commit is contained in:
@@ -24,9 +24,6 @@
|
||||
<div class="col-md-12">
|
||||
<b-card class="d-flex flex-column" v-hover v-on:click="openBook(book.id)">
|
||||
<b-row no-gutters style="height: inherit">
|
||||
<b-col no-gutters md="2" style="height: inherit">
|
||||
<h3>{{ book.icon }}</h3>
|
||||
</b-col>
|
||||
<b-col no-gutters md="10" style="height: inherit">
|
||||
<b-card-body class="m-0 py-0" style="height: inherit">
|
||||
<b-card-text class="h-100 my-0 d-flex flex-column" style="text-overflow: ellipsis">
|
||||
@@ -142,7 +139,7 @@ export default {
|
||||
let apiClient = new ApiApiFactory()
|
||||
|
||||
apiClient
|
||||
.createRecipeBook({ name: this.$t("New_Cookbook"), description: "", icon: "", shared: [] })
|
||||
.createRecipeBook({ name: this.$t("New_Cookbook"), description: "", shared: [] })
|
||||
.then((result) => {
|
||||
let new_book = result.data
|
||||
this.refreshData()
|
||||
|
||||
@@ -18,25 +18,6 @@
|
||||
{{ $t("All recipes") }}
|
||||
</b-form-checkbox>
|
||||
|
||||
<!-- <multiselect
|
||||
:searchable="true"
|
||||
:disabled="disabled_multiselect"
|
||||
v-model="recipe_list"
|
||||
:options="recipes"
|
||||
:close-on-select="false"
|
||||
:clear-on-select="true"
|
||||
:hide-selected="true"
|
||||
:preserve-search="true"
|
||||
placeholder="Select Recipes"
|
||||
:taggable="false"
|
||||
label="name"
|
||||
track-by="id"
|
||||
id="id_recipes"
|
||||
:multiple="true"
|
||||
:loading="recipes_loading"
|
||||
@search-change="searchRecipes"
|
||||
>
|
||||
</multiselect> -->
|
||||
<generic-multiselect
|
||||
class="input-group-text m-0 p-0"
|
||||
@change="recipe_list = $event.val"
|
||||
@@ -81,10 +62,6 @@ Vue.use(BootstrapVue)
|
||||
|
||||
export default {
|
||||
name: "ExportView",
|
||||
/*mixins: [
|
||||
ResolveUrlMixin,
|
||||
ToastMixin,
|
||||
],*/
|
||||
components: { GenericMultiselect },
|
||||
mixins: [ApiMixin],
|
||||
data() {
|
||||
@@ -92,7 +69,6 @@ export default {
|
||||
export_id: window.EXPORT_ID,
|
||||
loading: false,
|
||||
disabled_multiselect: false,
|
||||
|
||||
recipe_app: "DEFAULT",
|
||||
recipe_list: [],
|
||||
recipes_loading: false,
|
||||
@@ -103,7 +79,6 @@ export default {
|
||||
},
|
||||
mounted() {
|
||||
if (this.export_id) this.insertRequested()
|
||||
// else this.searchRecipes("")
|
||||
},
|
||||
methods: {
|
||||
insertRequested: function () {
|
||||
@@ -118,25 +93,10 @@ export default {
|
||||
this.recipe_list.push(response.data)
|
||||
})
|
||||
.catch((err) => {
|
||||
StandardToasts.makeStandardToast(this,StandardToasts.FAIL_FETCH, err)
|
||||
StandardToasts.makeStandardToast(this, StandardToasts.FAIL_FETCH, err)
|
||||
})
|
||||
// .then((e) => this.searchRecipes(""))
|
||||
},
|
||||
|
||||
// searchRecipes: function (query) {
|
||||
// this.recipes_loading = true
|
||||
|
||||
// this.genericAPI(this.Models.RECIPE, this.Actions.LIST, { query: query })
|
||||
// .then((response) => {
|
||||
// this.recipes = response.data.results
|
||||
// this.recipes_loading = false
|
||||
// })
|
||||
// .catch((err) => {
|
||||
// console.log(err)
|
||||
// StandardToasts.makeStandardToast(this,StandardToasts.FAIL_FETCH)
|
||||
// })
|
||||
// },
|
||||
|
||||
exportRecipe: function () {
|
||||
if (this.recipe_list.length < 1 && this.export_all == false && this.filter === undefined) {
|
||||
makeToast(this.$t("Error"), this.$t("Select at least one recipe"), "danger")
|
||||
@@ -148,7 +108,7 @@ export default {
|
||||
let formData = new FormData()
|
||||
formData.append("type", this.recipe_app)
|
||||
formData.append("all", this.export_all)
|
||||
formData.append("filter", this.filter?.id ?? null)
|
||||
formData.append("custom_filter", this.filter?.id ?? null)
|
||||
|
||||
for (var i = 0; i < this.recipe_list.length; i++) {
|
||||
formData.append("recipes", this.recipe_list[i].id)
|
||||
|
||||
@@ -511,6 +511,10 @@ export default {
|
||||
this.website_url = urlParams.get('url')
|
||||
this.loadRecipe(this.website_url)
|
||||
}
|
||||
if (urlParams.has("text")) {
|
||||
this.website_url = urlParams.get('text')
|
||||
this.loadRecipe(this.website_url)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
@@ -675,12 +679,26 @@ export default {
|
||||
*/
|
||||
autoImport: function () {
|
||||
this.collapse_visible.import = true
|
||||
this.website_url_list.split('\n').forEach(r => {
|
||||
this.loadRecipe(r, true, undefined).then((recipe_json) => {
|
||||
let url_list = this.website_url_list.split('\n').filter(x => x.trim() !== '')
|
||||
if (url_list.length > 0) {
|
||||
let url = url_list.shift()
|
||||
this.website_url_list = url_list.join('\n')
|
||||
|
||||
|
||||
this.loadRecipe(url, true, undefined).then((recipe_json) => {
|
||||
this.importRecipe('multi_import', recipe_json, true)
|
||||
setTimeout(() => {
|
||||
this.autoImport()
|
||||
}, 2000)
|
||||
}).catch((err) => {
|
||||
|
||||
})
|
||||
})
|
||||
this.website_url_list = ''
|
||||
} else {
|
||||
this.import_loading = false
|
||||
this.loading = false
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
/**
|
||||
* Import recipes with uploaded files and app integration
|
||||
|
||||
@@ -103,6 +103,7 @@
|
||||
|
||||
import draggable from "vuedraggable";
|
||||
import stringSimilarity from "string-similarity"
|
||||
import {getUserPreference} from "@/utils/utils"
|
||||
|
||||
export default {
|
||||
name: "ImportViewStepEditor",
|
||||
@@ -117,6 +118,7 @@ export default {
|
||||
recipe_json: undefined,
|
||||
current_edit_ingredient: null,
|
||||
current_edit_step: null,
|
||||
user_preferences: null,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -126,6 +128,7 @@ export default {
|
||||
},
|
||||
mounted() {
|
||||
this.recipe_json = this.recipe
|
||||
this.user_preferences = getUserPreference();
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
@@ -138,7 +141,7 @@ export default {
|
||||
let steps = []
|
||||
step.instruction.split(split_character).forEach(part => {
|
||||
if (part.trim() !== '') {
|
||||
steps.push({'instruction': part, 'ingredients': []})
|
||||
steps.push({'instruction': part, 'ingredients': [], 'show_ingredients_table': this.user_preferences.show_step_ingredients})
|
||||
}
|
||||
})
|
||||
steps[0].ingredients = step.ingredients // put all ingredients from the original step in the ingredients of the first step of the split step list
|
||||
|
||||
@@ -1,219 +1,165 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-tabs content-class="mt-3" v-model="current_tab">
|
||||
<b-tab :title="$t('Planner')" active>
|
||||
<div class="row calender-row d-none d-lg-block">
|
||||
<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 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 class="d-none d-lg-block">
|
||||
<div class="row ">
|
||||
<div class="col col-2">
|
||||
<h4>{{ $t('Meal_Types') }}</h4>
|
||||
|
||||
<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 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>
|
||||
</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>
|
||||
<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>
|
||||
|
||||
<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>
|
||||
<div class="flex-grow-1 ml-2"
|
||||
style="text-overflow: ellipsis; overflow-wrap: anywhere;">
|
||||
|
||||
</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>
|
||||
</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>
|
||||
<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-item>
|
||||
|
||||
</b-list-group>
|
||||
</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-3 calender-options">
|
||||
<h5>{{ $t("Planner_Settings") }}</h5>
|
||||
<b-form>
|
||||
<b-form-group id="UomInput" :label="$t('Period')" :description="$t('Plan_Period_To_Show')"
|
||||
label-for="UomInput">
|
||||
<b-form-select id="UomInput" v-model="settings.displayPeriodUom"
|
||||
:options="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="settings.displayPeriodCount"
|
||||
:options="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="settings.startingDayOfWeek"
|
||||
:options="dayNames"></b-form-select>
|
||||
</b-form-group>
|
||||
<b-form-group id="WeekNumInput" :label="$t('Week_Numbers')">
|
||||
<b-form-checkbox v-model="settings.displayWeekNumbers" name="week_num">
|
||||
{{ $t("Show_Week_Numbers") }}
|
||||
</b-form-checkbox>
|
||||
</b-form-group>
|
||||
</b-form>
|
||||
</div>
|
||||
<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.icon }} {{
|
||||
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">
|
||||
<emoji-input :field="'icon'" :label="$t('Icon')"
|
||||
:value="meal_type.icon"></emoji-input>
|
||||
</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
|
||||
@@ -279,24 +225,18 @@
|
||||
:create_date="mealplan_default_date"
|
||||
@reload-meal-types="refreshMealTypes"
|
||||
></meal-plan-edit-modal>
|
||||
|
||||
<div class="row d-none d-lg-block">
|
||||
<div class="col-12 float-right">
|
||||
<button class="btn btn-success shadow-none" @click="createEntryClick(new Date())"><i
|
||||
class="fas fa-calendar-plus"></i> {{ $t("Create") }}
|
||||
</button>
|
||||
<a class="btn btn-primary shadow-none" :href="iCalUrl"><i class="fas fa-download"></i>
|
||||
{{ $t("Export_To_ICal") }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<auto-meal-plan-modal
|
||||
:modal_title="'Auto create meal plan'"
|
||||
:current_period="current_period"
|
||||
></auto-meal-plan-modal>
|
||||
|
||||
<bottom-navigation-bar :create_links="[{label:$t('Export_To_ICal'), url: iCalUrl, icon:'fas fa-download'}]">
|
||||
<template #custom_create_functions>
|
||||
<h6 class="dropdown-header">{{ $t('Meal_Plan')}}</h6>
|
||||
<h6 class="dropdown-header">{{ $t('Meal_Plan') }}</h6>
|
||||
<a class="dropdown-item" @click="createEntryClick(new Date())"><i
|
||||
class="fas fa-calendar-plus fa-fw"></i> {{ $t("Create") }}</a>
|
||||
</template>
|
||||
|
||||
</bottom-navigation-bar>
|
||||
</div>
|
||||
</template>
|
||||
@@ -311,7 +251,6 @@ import ContextMenuItem from "@/components/ContextMenu/ContextMenuItem"
|
||||
import MealPlanCard from "@/components/MealPlanCard"
|
||||
import MealPlanEditModal from "@/components/MealPlanEditModal"
|
||||
import MealPlanCalenderHeader from "@/components/MealPlanCalenderHeader"
|
||||
import EmojiInput from "@/components/Modals/EmojiInput"
|
||||
|
||||
import moment from "moment"
|
||||
import draggable from "vuedraggable"
|
||||
@@ -322,6 +261,8 @@ import {CalendarView, CalendarMathMixin} from "vue-simple-calendar/src/component
|
||||
import {ApiApiFactory} from "@/utils/openapi/api"
|
||||
import BottomNavigationBar from "@/components/BottomNavigationBar.vue";
|
||||
import {useMealPlanStore} from "@/stores/MealPlanStore";
|
||||
import axios from "axios";
|
||||
import AutoMealPlanModal from "@/components/AutoMealPlanModal";
|
||||
|
||||
const {makeToast} = require("@/utils/utils")
|
||||
|
||||
@@ -334,19 +275,28 @@ let SETTINGS_COOKIE_NAME = "mealplan_settings"
|
||||
export default {
|
||||
name: "MealPlanView",
|
||||
components: {
|
||||
AutoMealPlanModal,
|
||||
MealPlanEditModal,
|
||||
MealPlanCard,
|
||||
CalendarView,
|
||||
ContextMenu,
|
||||
ContextMenuItem,
|
||||
MealPlanCalenderHeader,
|
||||
EmojiInput,
|
||||
draggable,
|
||||
BottomNavigationBar,
|
||||
},
|
||||
mixins: [CalendarMathMixin, ApiMixin, ResolveUrlMixin],
|
||||
data: function () {
|
||||
return {
|
||||
AutoPlan: {
|
||||
meal_types: [],
|
||||
keywords: [[]],
|
||||
servings: 1,
|
||||
date: Date.now(),
|
||||
startDay: null,
|
||||
endDay: null,
|
||||
shared: [],
|
||||
addshopping: false
|
||||
},
|
||||
showDate: new Date(),
|
||||
plan_entries: [],
|
||||
recipe_viewed: {},
|
||||
@@ -357,7 +307,6 @@ export default {
|
||||
displayWeekNumbers: true,
|
||||
},
|
||||
dragged_item: null,
|
||||
current_tab: 0,
|
||||
meal_types: [],
|
||||
current_context_menu_item: null,
|
||||
options: {
|
||||
@@ -397,19 +346,9 @@ export default {
|
||||
detailed_items: function () {
|
||||
return this.settings.displayPeriodUom === "week"
|
||||
},
|
||||
dayNames: function () {
|
||||
let options = []
|
||||
this.getFormattedWeekdayNames(this.userLocale, "long", 0).forEach((day, index) => {
|
||||
options.push({text: day, value: index})
|
||||
})
|
||||
return options
|
||||
},
|
||||
userLocale: function () {
|
||||
return this.getDefaultBrowserLocale
|
||||
},
|
||||
item_height: function () {
|
||||
if (this.settings.displayPeriodUom === "week") {
|
||||
return "10rem"
|
||||
return "3rem"
|
||||
} else {
|
||||
return "1.6rem"
|
||||
}
|
||||
@@ -432,8 +371,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.plan_items.filter((m) => moment(m.startDate).isSame(moment_date, 'day'))
|
||||
date_label: moment_date.format("dd") + " " + moment_date.format("ll"),
|
||||
plan_entries: this.plan_items.filter((m) => moment_date.isBetween(moment(m.startDate), moment(m.endDate), 'day', '[]'))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -441,19 +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.$root.$on("change", this.updateEmoji)
|
||||
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,
|
||||
},
|
||||
@@ -539,13 +473,6 @@ export default {
|
||||
StandardToasts.makeStandardToast(this, StandardToasts.FAIL_DELETE, err)
|
||||
})
|
||||
},
|
||||
updateEmoji: function (field, value) {
|
||||
this.meal_types.forEach((meal_type) => {
|
||||
if (meal_type.editing) {
|
||||
meal_type.icon = value
|
||||
}
|
||||
})
|
||||
},
|
||||
datePickerChanged(ctx) {
|
||||
this.setShowDate(ctx.selectedDate)
|
||||
},
|
||||
@@ -567,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)
|
||||
}
|
||||
}
|
||||
@@ -581,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)
|
||||
}
|
||||
})
|
||||
@@ -589,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)
|
||||
}
|
||||
})
|
||||
@@ -607,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
|
||||
}
|
||||
@@ -630,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,
|
||||
}
|
||||
},
|
||||
@@ -656,7 +601,11 @@ export default {
|
||||
this.$bvModal.show(`id_meal_plan_edit_modal`)
|
||||
})
|
||||
|
||||
}
|
||||
},
|
||||
createAutoPlan() {
|
||||
this.$bvModal.show(`autoplan-modal`)
|
||||
},
|
||||
|
||||
},
|
||||
directives: {
|
||||
hover: {
|
||||
@@ -693,7 +642,7 @@ export default {
|
||||
}
|
||||
|
||||
.calender-row {
|
||||
height: calc(100vh - 240px);
|
||||
height: calc(100vh - 140px);
|
||||
}
|
||||
|
||||
.calender-parent {
|
||||
|
||||
@@ -31,18 +31,18 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Image and misc properties -->
|
||||
<!-- Image and misc -->
|
||||
<div class="row pt-2">
|
||||
<div class="col-md-6" style="max-height: 50vh; min-height: 30vh">
|
||||
<input id="id_file_upload" ref="file_upload" type="file" hidden
|
||||
@change="uploadImage($event.target.files[0])"/>
|
||||
|
||||
<div
|
||||
class="h-100 w-100 border border-primary rounded"
|
||||
style="border-width: 2px !important; border-style: dashed !important"
|
||||
@drop.prevent="uploadImage($event.dataTransfer.files[0])"
|
||||
@dragover.prevent
|
||||
@click="$refs.file_upload.click()"
|
||||
class="h-100 w-100 border border-primary rounded"
|
||||
style="border-width: 2px !important; border-style: dashed !important"
|
||||
@drop.prevent="uploadImage($event.dataTransfer.files[0])"
|
||||
@dragover.prevent
|
||||
@click="$refs.file_upload.click()"
|
||||
>
|
||||
<i class="far fa-image fa-10x text-primary"
|
||||
style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%)"
|
||||
@@ -71,27 +71,27 @@
|
||||
<br/>
|
||||
<label for="id_name"> {{ $t("Keywords") }}</label>
|
||||
<multiselect
|
||||
v-model="recipe.keywords"
|
||||
:options="keywords"
|
||||
:close-on-select="false"
|
||||
:clear-on-select="true"
|
||||
:hide-selected="true"
|
||||
:preserve-search="true"
|
||||
:internal-search="false"
|
||||
:limit="options_limit"
|
||||
:placeholder="$t('select_keyword')"
|
||||
:tag-placeholder="$t('add_keyword')"
|
||||
:select-label="$t('Select')"
|
||||
:selected-label="$t('Selected')"
|
||||
:deselect-label="$t('remove_selection')"
|
||||
:taggable="true"
|
||||
@tag="addKeyword"
|
||||
label="label"
|
||||
track-by="id"
|
||||
id="id_keywords"
|
||||
:multiple="true"
|
||||
:loading="keywords_loading"
|
||||
@search-change="searchKeywords"
|
||||
v-model="recipe.keywords"
|
||||
:options="keywords"
|
||||
:close-on-select="false"
|
||||
:clear-on-select="true"
|
||||
:hide-selected="true"
|
||||
:preserve-search="true"
|
||||
:internal-search="false"
|
||||
:limit="options_limit"
|
||||
:placeholder="$t('select_keyword')"
|
||||
:tag-placeholder="$t('add_keyword')"
|
||||
:select-label="$t('Select')"
|
||||
:selected-label="$t('Selected')"
|
||||
:deselect-label="$t('remove_selection')"
|
||||
:taggable="true"
|
||||
@tag="addKeyword"
|
||||
label="label"
|
||||
track-by="id"
|
||||
id="id_keywords"
|
||||
:multiple="true"
|
||||
:loading="keywords_loading"
|
||||
@search-change="searchKeywords"
|
||||
>
|
||||
<template v-slot:noOptions>{{ $t("empty_list") }}</template>
|
||||
</multiselect>
|
||||
@@ -99,65 +99,53 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Nutrition -->
|
||||
<div class="row pt-2">
|
||||
<div class="col-md-12">
|
||||
<div class="card border-grey">
|
||||
<div class="card-header" style="display: table">
|
||||
<div class="row">
|
||||
<div class="col-md-9 d-table">
|
||||
<h5 class="d-table-cell align-middle">{{ $t("Nutrition") }}</h5>
|
||||
<div class="card mt-2 mb-2">
|
||||
<div class="card-body pr-2 pl-2 pr-md-5 pl-md-5 pt-3 pb-3">
|
||||
<h6>{{ $t('Properties') }} <small class="text-muted"> {{$t('per_serving')}}</small></h6>
|
||||
|
||||
<div class="alert alert-info" role="alert">
|
||||
{{ $t('recipe_property_info')}}
|
||||
</div>
|
||||
|
||||
<div class="d-flex mt-2" v-for="p in recipe.properties" v-bind:key="p.id">
|
||||
<div class="flex-fill w-50">
|
||||
<generic-multiselect
|
||||
@change="p.property_type = $event.val"
|
||||
:initial_single_selection="p.property_type"
|
||||
:label="'name'"
|
||||
:model="Models.PROPERTY_TYPE"
|
||||
:limit="25"
|
||||
:multiple="false"
|
||||
></generic-multiselect>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<button
|
||||
type="button"
|
||||
@click="addNutrition()"
|
||||
v-if="recipe.nutrition === null"
|
||||
v-b-tooltip.hover
|
||||
v-bind:title="$t('Add_nutrition_recipe')"
|
||||
class="btn btn-sm btn-success shadow-none float-right"
|
||||
>
|
||||
<i class="fas fa-plus-circle"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="removeNutrition()"
|
||||
v-if="recipe.nutrition !== null"
|
||||
v-b-tooltip.hover
|
||||
v-bind:title="$t('Remove_nutrition_recipe')"
|
||||
class="btn btn-sm btn-danger shadow-none float-right"
|
||||
>
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
</button>
|
||||
<div class="flex-fill w-50">
|
||||
<div class="input-group">
|
||||
<input type="number" class="form-control" v-model="p.property_amount">
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text" v-if="p.property_type !== null && p.property_type.unit !== ''">{{ p.property_type.unit }}</span>
|
||||
<button class="btn btn-danger" @click="deleteProperty(p)"><i class="fa fa-trash fa-fw"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-row mt-2">
|
||||
<div class="flex-column w-25 offset-4">
|
||||
<button class="btn btn-success btn-block" @click="addProperty()"><i class="fa fa-plus"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<b-collapse id="id_nutrition_collapse" class="mt-2" v-model="nutrition_visible">
|
||||
<div class="card-body" v-if="recipe.nutrition !== null">
|
||||
<b-alert show>
|
||||
There is currently only very basic support for tracking nutritional information. A
|
||||
<a href="https://github.com/vabene1111/recipes/issues/896" target="_blank"
|
||||
rel="noreferrer nofollow">big update</a> is planned to improve on this in many
|
||||
different areas.
|
||||
</b-alert>
|
||||
|
||||
<label for="id_name"> {{ $t(energy()) }}</label>
|
||||
|
||||
<input class="form-control" id="id_calories" v-model="recipe.nutrition.calories"/>
|
||||
|
||||
<label for="id_name"> {{ $t("Carbohydrates") }}</label>
|
||||
<input class="form-control" id="id_carbohydrates"
|
||||
v-model="recipe.nutrition.carbohydrates"/>
|
||||
|
||||
<label for="id_name"> {{ $t("Fats") }}</label>
|
||||
<input class="form-control" id="id_fats" v-model="recipe.nutrition.fats"/>
|
||||
|
||||
<label for="id_name"> {{ $t("Proteins") }}</label>
|
||||
<input class="form-control" id="id_proteins" v-model="recipe.nutrition.proteins"/>
|
||||
</div>
|
||||
</b-collapse>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row pt-2">
|
||||
<div class="col-md-12">
|
||||
|
||||
|
||||
<b-card-header header-tag="header" class="p-1" role="tab">
|
||||
<b-button squared block v-b-toggle.additional_collapse class="text-left"
|
||||
variant="outline-primary">{{ $t("additional_options") }}
|
||||
@@ -202,14 +190,14 @@
|
||||
<br/>
|
||||
<label> {{ $t("Share") }}</label>
|
||||
<generic-multiselect
|
||||
@change="recipe.shared = $event.val"
|
||||
parent_variable="recipe.shared"
|
||||
:initial_selection="recipe.shared"
|
||||
:label="'display_name'"
|
||||
:model="Models.USER_NAME"
|
||||
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
|
||||
v-bind:placeholder="$t('Share')"
|
||||
:limit="25"
|
||||
@change="recipe.shared = $event.val"
|
||||
parent_variable="recipe.shared"
|
||||
:initial_selection="recipe.shared"
|
||||
:label="'display_name'"
|
||||
:model="Models.USER_NAME"
|
||||
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
|
||||
v-bind:placeholder="$t('Share')"
|
||||
:limit="25"
|
||||
></generic-multiselect>
|
||||
|
||||
|
||||
@@ -240,7 +228,7 @@
|
||||
|
||||
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuLink">
|
||||
<button class="dropdown-item" @click="removeStep(step)"><i
|
||||
class="fa fa-trash fa-fw"></i> {{ $t("Delete") }}
|
||||
class="fa fa-trash fa-fw"></i> {{ $t("Delete") }}
|
||||
</button>
|
||||
|
||||
<button class="dropdown-item" @click="moveStep(step, step_index - 1)"
|
||||
@@ -252,6 +240,16 @@
|
||||
v-if="step_index !== recipe.steps.length - 1">
|
||||
<i class="fa fa-arrow-down fa-fw"></i> {{ $t("Move_Down") }}
|
||||
</button>
|
||||
<!-- Show "Hide step ingredients if state is currently set to shown" -->
|
||||
<button class="dropdown-item" @click="setStepShowIngredientsTable(step, false)"
|
||||
v-if="step.show_ingredients_table">
|
||||
<i class="op-icon fa fa-mavon-eye-slash"></i> {{ $t("hide_step_ingredients") }}
|
||||
</button>
|
||||
<!-- Show "Show step ingredients if state is currently set to hidden" -->
|
||||
<button class="dropdown-item" @click="setStepShowIngredientsTable(step, true)"
|
||||
v-if="! step.show_ingredients_table">
|
||||
<i class="op-icon fa fa-mavon-eye"></i> {{ $t("show_step_ingredients") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -282,7 +280,6 @@
|
||||
@click="step.time_visible = true" v-if="!step.time_visible">
|
||||
<i class="fas fa-plus-circle"></i> {{ $t("Time") }}
|
||||
</b-button>
|
||||
|
||||
<b-button pill variant="primary" size="sm" class="ml-1 mb-1 mb-md-0"
|
||||
@click="step.ingredients_visible = true" v-if="!step.ingredients_visible">
|
||||
<i class="fas fa-plus-circle"></i> {{ $t("Ingredients") }}
|
||||
@@ -303,11 +300,11 @@
|
||||
<i class="fas fa-plus-circle"></i> {{ $t("File") }}
|
||||
</b-button>
|
||||
<b-button
|
||||
pill
|
||||
variant="primary"
|
||||
size="sm"
|
||||
class="ml-1 mb-1 mb-md-0"
|
||||
@click="
|
||||
pill
|
||||
variant="primary"
|
||||
size="sm"
|
||||
class="ml-1 mb-1 mb-md-0"
|
||||
@click="
|
||||
paste_step = step
|
||||
$bvModal.show('id_modal_paste_ingredients')
|
||||
"
|
||||
@@ -330,31 +327,31 @@
|
||||
<label :for="'id_step_' + step.id + '_file'">{{ $t("File") }}</label>
|
||||
<b-input-group>
|
||||
<multiselect
|
||||
ref="file"
|
||||
v-model="step.file"
|
||||
:options="files"
|
||||
:close-on-select="true"
|
||||
:clear-on-select="true"
|
||||
:allow-empty="true"
|
||||
:preserve-search="true"
|
||||
:placeholder="$t('select_file')"
|
||||
:select-label="$t('Select')"
|
||||
:selected-label="$t('Selected')"
|
||||
:deselect-label="$t('remove_selection')"
|
||||
:id="'id_step_' + step.id + '_file'"
|
||||
label="name"
|
||||
track-by="name"
|
||||
:multiple="false"
|
||||
:loading="files_loading"
|
||||
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
|
||||
@search-change="searchFiles"
|
||||
ref="file"
|
||||
v-model="step.file"
|
||||
:options="files"
|
||||
:close-on-select="true"
|
||||
:clear-on-select="true"
|
||||
:allow-empty="true"
|
||||
:preserve-search="true"
|
||||
:placeholder="$t('select_file')"
|
||||
:select-label="$t('Select')"
|
||||
:selected-label="$t('Selected')"
|
||||
:deselect-label="$t('remove_selection')"
|
||||
:id="'id_step_' + step.id + '_file'"
|
||||
label="name"
|
||||
track-by="name"
|
||||
:multiple="false"
|
||||
:loading="files_loading"
|
||||
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
|
||||
@search-change="searchFiles"
|
||||
>
|
||||
<template v-slot:noOptions>{{ $t("empty_list") }}</template>
|
||||
</multiselect>
|
||||
<b-input-group-append>
|
||||
<b-button
|
||||
variant="primary"
|
||||
@click="
|
||||
variant="primary"
|
||||
@click="
|
||||
step_for_file_create = step
|
||||
show_file_create = true
|
||||
"
|
||||
@@ -370,24 +367,24 @@
|
||||
<div class="col-md-12">
|
||||
<label :for="'id_step_' + step.id + '_recipe'">{{ $t("Recipe") }}</label>
|
||||
<multiselect
|
||||
ref="step_recipe"
|
||||
v-model="step.step_recipe"
|
||||
:options="recipes.map((recipe) => recipe.id)"
|
||||
:close-on-select="true"
|
||||
:clear-on-select="true"
|
||||
:allow-empty="true"
|
||||
:preserve-search="true"
|
||||
:internal-search="false"
|
||||
:limit="options_limit"
|
||||
:placeholder="$t('select_recipe')"
|
||||
:select-label="$t('Select')"
|
||||
:selected-label="$t('Selected')"
|
||||
:deselect-label="$t('remove_selection')"
|
||||
:id="'id_step_' + step.id + '_recipe'"
|
||||
:custom-label="(opt) => recipes.find((x) => x.id === opt).name"
|
||||
:multiple="false"
|
||||
:loading="recipes_loading"
|
||||
@search-change="searchRecipes"
|
||||
ref="step_recipe"
|
||||
v-model="step.step_recipe"
|
||||
:options="recipes.map((recipe) => recipe.id)"
|
||||
:close-on-select="true"
|
||||
:clear-on-select="true"
|
||||
:allow-empty="true"
|
||||
:preserve-search="true"
|
||||
:internal-search="false"
|
||||
:limit="options_limit"
|
||||
:placeholder="$t('select_recipe')"
|
||||
:select-label="$t('Select')"
|
||||
:selected-label="$t('Selected')"
|
||||
:deselect-label="$t('remove_selection')"
|
||||
:id="'id_step_' + step.id + '_recipe'"
|
||||
:custom-label="(opt) => recipes.find((x) => x.id === opt).name"
|
||||
:multiple="false"
|
||||
:loading="recipes_loading"
|
||||
@search-change="searchRecipes"
|
||||
>
|
||||
<template v-slot:noOptions>{{ $t("empty_list") }}</template>
|
||||
</multiselect>
|
||||
@@ -427,12 +424,12 @@
|
||||
<div class="col-lg-2 col-md-6 small-padding"
|
||||
v-if="!ingredient.is_header">
|
||||
<input
|
||||
class="form-control"
|
||||
v-model="ingredient.amount"
|
||||
type="number"
|
||||
step="any"
|
||||
v-if="!ingredient.no_amount"
|
||||
:id="`amount_${step_index}_${index}`"
|
||||
class="form-control"
|
||||
v-model="ingredient.amount"
|
||||
type="number"
|
||||
step="any"
|
||||
v-if="!ingredient.no_amount"
|
||||
:id="`amount_${step_index}_${index}`"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -440,29 +437,29 @@
|
||||
v-if="!ingredient.is_header">
|
||||
<!-- search set to false to allow API to drive results & order -->
|
||||
<multiselect
|
||||
v-if="!ingredient.no_amount"
|
||||
ref="unit"
|
||||
v-model="ingredient.unit"
|
||||
:options="units"
|
||||
:close-on-select="true"
|
||||
:clear-on-select="true"
|
||||
:allow-empty="true"
|
||||
:preserve-search="true"
|
||||
:internal-search="false"
|
||||
:limit="options_limit"
|
||||
:placeholder="$t('select_unit')"
|
||||
:tag-placeholder="$t('Create')"
|
||||
:select-label="$t('Select')"
|
||||
:selected-label="$t('Selected')"
|
||||
:deselect-label="$t('remove_selection')"
|
||||
:taggable="true"
|
||||
@tag="addUnitType"
|
||||
:id="`unit_${step_index}_${index}`"
|
||||
label="name"
|
||||
track-by="name"
|
||||
:multiple="false"
|
||||
:loading="units_loading"
|
||||
@search-change="searchUnits"
|
||||
v-if="!ingredient.no_amount"
|
||||
ref="unit"
|
||||
v-model="ingredient.unit"
|
||||
:options="units"
|
||||
:close-on-select="true"
|
||||
:clear-on-select="true"
|
||||
:allow-empty="true"
|
||||
:preserve-search="true"
|
||||
:internal-search="false"
|
||||
:limit="options_limit"
|
||||
:placeholder="$t('select_unit')"
|
||||
:tag-placeholder="$t('Create')"
|
||||
:select-label="$t('Select')"
|
||||
:selected-label="$t('Selected')"
|
||||
:deselect-label="$t('remove_selection')"
|
||||
:taggable="true"
|
||||
@tag="addUnitType"
|
||||
:id="`unit_${step_index}_${index}`"
|
||||
label="name"
|
||||
track-by="name"
|
||||
:multiple="false"
|
||||
:loading="units_loading"
|
||||
@search-change="searchUnits"
|
||||
>
|
||||
<template v-slot:noOptions>{{
|
||||
$t("empty_list")
|
||||
@@ -475,28 +472,28 @@
|
||||
<!-- search set to false to allow API to drive results & order -->
|
||||
|
||||
<multiselect
|
||||
ref="food"
|
||||
v-model="ingredient.food"
|
||||
:options="foods"
|
||||
:close-on-select="true"
|
||||
:clear-on-select="true"
|
||||
:allow-empty="true"
|
||||
:preserve-search="true"
|
||||
:internal-search="false"
|
||||
:limit="options_limit"
|
||||
:placeholder="$t('select_food')"
|
||||
:tag-placeholder="$t('Create')"
|
||||
:select-label="$t('Select')"
|
||||
:selected-label="$t('Selected')"
|
||||
:deselect-label="$t('remove_selection')"
|
||||
:taggable="true"
|
||||
@tag="addFoodType"
|
||||
:id="`ingredient_${step_index}_${index}`"
|
||||
label="name"
|
||||
track-by="name"
|
||||
:multiple="false"
|
||||
:loading="foods_loading"
|
||||
@search-change="searchFoods"
|
||||
ref="food"
|
||||
v-model="ingredient.food"
|
||||
:options="foods"
|
||||
:close-on-select="true"
|
||||
:clear-on-select="true"
|
||||
:allow-empty="true"
|
||||
:preserve-search="true"
|
||||
:internal-search="false"
|
||||
:limit="options_limit"
|
||||
:placeholder="$t('select_food')"
|
||||
:tag-placeholder="$t('Create')"
|
||||
:select-label="$t('Select')"
|
||||
:selected-label="$t('Selected')"
|
||||
:deselect-label="$t('remove_selection')"
|
||||
:taggable="true"
|
||||
@tag="addFoodType"
|
||||
:id="`ingredient_${step_index}_${index}`"
|
||||
label="name"
|
||||
track-by="name"
|
||||
:multiple="false"
|
||||
:loading="foods_loading"
|
||||
@search-change="searchFoods"
|
||||
>
|
||||
<template v-slot:noOptions>{{
|
||||
$t("empty_list")
|
||||
@@ -507,11 +504,11 @@
|
||||
<div class="small-padding"
|
||||
v-bind:class="{ 'col-lg-4 col-md-6': !ingredient.is_header, 'col-lg-12 col-md-12': ingredient.is_header }">
|
||||
<input
|
||||
class="form-control"
|
||||
maxlength="256"
|
||||
v-model="ingredient.note"
|
||||
v-bind:placeholder="$t('Note')"
|
||||
v-on:keydown.tab="
|
||||
class="form-control"
|
||||
maxlength="256"
|
||||
v-model="ingredient.note"
|
||||
v-bind:placeholder="$t('Note')"
|
||||
v-on:keydown.tab="
|
||||
(event) => {
|
||||
if (step.ingredients.indexOf(ingredient) === step.ingredients.length - 1) {
|
||||
event.preventDefault()
|
||||
@@ -525,13 +522,13 @@
|
||||
|
||||
<div class="flex-grow-0 small-padding">
|
||||
<a
|
||||
class="btn shadow-none btn-lg pr-1 pl-0 pr-md-2 pl-md-2"
|
||||
href="#"
|
||||
role="button"
|
||||
id="dropdownMenuLink2"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
class="btn shadow-none btn-lg pr-1 pl-0 pr-md-2 pl-md-2"
|
||||
href="#"
|
||||
role="button"
|
||||
id="dropdownMenuLink2"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<i class="fas fa-ellipsis-v text-muted"></i>
|
||||
</a>
|
||||
@@ -573,35 +570,35 @@
|
||||
</button>
|
||||
|
||||
|
||||
<button type="button" class="dropdown-item"
|
||||
v-if="!ingredient.always_use_plural_unit"
|
||||
@click="ingredient.always_use_plural_unit = true">
|
||||
<i class="fas fa-filter fa-fw"></i>
|
||||
{{ $t("Use_Plural_Unit_Always") }}
|
||||
</button>
|
||||
<button type="button" class="dropdown-item"
|
||||
v-if="!ingredient.always_use_plural_unit"
|
||||
@click="ingredient.always_use_plural_unit = true">
|
||||
<i class="fas fa-filter fa-fw"></i>
|
||||
{{ $t("Use_Plural_Unit_Always") }}
|
||||
</button>
|
||||
|
||||
<button type="button" class="dropdown-item"
|
||||
v-if="ingredient.always_use_plural_unit"
|
||||
@click="ingredient.always_use_plural_unit = false">
|
||||
<i class="fas fa-filter fa-fw"></i>
|
||||
{{ $t("Use_Plural_Unit_Simple") }}
|
||||
</button>
|
||||
<button type="button" class="dropdown-item"
|
||||
v-if="ingredient.always_use_plural_unit"
|
||||
@click="ingredient.always_use_plural_unit = false">
|
||||
<i class="fas fa-filter fa-fw"></i>
|
||||
{{ $t("Use_Plural_Unit_Simple") }}
|
||||
</button>
|
||||
|
||||
<button type="button" class="dropdown-item"
|
||||
v-if="!ingredient.always_use_plural_food"
|
||||
@click="ingredient.always_use_plural_food = true">
|
||||
<i class="fas fa-filter fa-fw"></i>
|
||||
{{ $t("Use_Plural_Food_Always") }}
|
||||
</button>
|
||||
<button type="button" class="dropdown-item"
|
||||
v-if="!ingredient.always_use_plural_food"
|
||||
@click="ingredient.always_use_plural_food = true">
|
||||
<i class="fas fa-filter fa-fw"></i>
|
||||
{{ $t("Use_Plural_Food_Always") }}
|
||||
</button>
|
||||
|
||||
<button type="button" class="dropdown-item"
|
||||
v-if="ingredient.always_use_plural_food"
|
||||
@click="ingredient.always_use_plural_food = false">
|
||||
<i class="fas fa-filter fa-fw"></i>
|
||||
{{ $t("Use_Plural_Food_Simple") }}
|
||||
</button>
|
||||
|
||||
<button type="button" class="dropdown-item"
|
||||
v-if="ingredient.always_use_plural_food"
|
||||
@click="ingredient.always_use_plural_food = false">
|
||||
<i class="fas fa-filter fa-fw"></i>
|
||||
{{ $t("Use_Plural_Food_Simple") }}
|
||||
</button>
|
||||
|
||||
|
||||
<button type="button" class="dropdown-item"
|
||||
@click="copyTemplateReference(index, ingredient)">
|
||||
<i class="fas fa-code"></i>
|
||||
@@ -665,7 +662,7 @@
|
||||
</button>
|
||||
|
||||
<button type="button" v-b-modal:id_modal_sort class="btn btn-warning shadow-none"><i
|
||||
class="fas fa-sort-amount-down-alt fa-lg"></i></button>
|
||||
class="fas fa-sort-amount-down-alt fa-lg"></i></button>
|
||||
</b-button-group>
|
||||
</div>
|
||||
</div>
|
||||
@@ -697,7 +694,7 @@
|
||||
<div class="col-3 col-md-6 mb-1 mb-md-0 pr-2 pl-2">
|
||||
<a :href="resolveDjangoUrl('delete_recipe', recipe.id)"
|
||||
class="d-block d-md-none btn btn-block btn-danger shadow-none btn-sm"><i
|
||||
class="fa fa-trash fa-lg"></i></a>
|
||||
class="fa fa-trash fa-lg"></i></a>
|
||||
<a :href="resolveDjangoUrl('delete_recipe', recipe.id)"
|
||||
class="d-none d-md-block btn btn-block btn-danger shadow-none btn-sm">{{ $t("Delete") }}</a>
|
||||
</div>
|
||||
@@ -752,11 +749,11 @@
|
||||
|
||||
<!-- modal for pasting list of ingredients -->
|
||||
<b-modal
|
||||
id="id_modal_paste_ingredients"
|
||||
v-bind:title="$t('ingredient_list')"
|
||||
@ok="appendIngredients(paste_step)"
|
||||
@cancel="paste_ingredients = paste_step = undefined"
|
||||
@close="paste_ingredients = paste_step = undefined"
|
||||
id="id_modal_paste_ingredients"
|
||||
v-bind:title="$t('ingredient_list')"
|
||||
@ok="appendIngredients(paste_step)"
|
||||
@cancel="paste_ingredients = paste_step = undefined"
|
||||
@close="paste_ingredients = paste_step = undefined"
|
||||
>
|
||||
<b-form-textarea id="paste_ingredients" v-model="paste_ingredients"
|
||||
:placeholder="$t('paste_ingredients_placeholder')" rows="10"></b-form-textarea>
|
||||
@@ -782,7 +779,8 @@ import {
|
||||
ResolveUrlMixin,
|
||||
StandardToasts,
|
||||
convertEnergyToCalories,
|
||||
energyHeading
|
||||
energyHeading,
|
||||
getUserPreference
|
||||
} from "@/utils/utils"
|
||||
import Multiselect from "vue-multiselect"
|
||||
import {ApiApiFactory} from "@/utils/openapi/api"
|
||||
@@ -825,6 +823,7 @@ export default {
|
||||
show_file_create: false,
|
||||
step_for_file_create: undefined,
|
||||
use_plural: false,
|
||||
user_preferences: undefined,
|
||||
additional_visible: false,
|
||||
create_food: undefined,
|
||||
md_editor_toolbars: {
|
||||
@@ -870,9 +869,9 @@ export default {
|
||||
this.searchKeywords("")
|
||||
this.searchFiles("")
|
||||
this.searchRecipes("")
|
||||
|
||||
this.$i18n.locale = window.CUSTOM_LOCALE
|
||||
let apiClient = new ApiApiFactory()
|
||||
this.user_preferences = getUserPreference()
|
||||
apiClient.retrieveSpace(window.ACTIVE_SPACE_ID).then(r => {
|
||||
this.use_plural = r.data.use_plural
|
||||
})
|
||||
@@ -937,7 +936,10 @@ export default {
|
||||
// set default visibility style for each component of the step
|
||||
this.recipe.steps.forEach((s) => {
|
||||
this.$set(s, "time_visible", s.time !== 0)
|
||||
this.$set(s, "ingredients_visible", s.ingredients.length > 0 || this.recipe.steps.length === 1)
|
||||
// ingredients_visible determines whether or not the ingredients UI is shown in the edit view
|
||||
// show_ingredients_table determine whether the ingredients table is shown in the read view
|
||||
// these are seperate as one might want to add ingredients but not want the step-level view
|
||||
this.$set(s, "ingredients_visible", s.show_ingredients_table && (s.ingredients.length > 0 || this.recipe.steps.length === 1))
|
||||
this.$set(s, "instruction_visible", s.instruction !== "" || this.recipe.steps.length === 1)
|
||||
this.$set(s, "step_recipe_visible", s.step_recipe !== null)
|
||||
this.$set(s, "file_visible", s.file !== null)
|
||||
@@ -1040,6 +1042,7 @@ export default {
|
||||
show_as_header: false,
|
||||
time_visible: false,
|
||||
ingredients_visible: true,
|
||||
show_ingredients_table: this.user_preferences.show_step_ingredients,
|
||||
instruction_visible: true,
|
||||
step_recipe_visible: false,
|
||||
file_visible: false,
|
||||
@@ -1095,6 +1098,9 @@ export default {
|
||||
this.recipe.steps.splice(new_index < 0 ? 0 : new_index, 0, step)
|
||||
this.sortSteps()
|
||||
},
|
||||
setStepShowIngredientsTable: function (step, show_state) {
|
||||
step.show_ingredients_table = show_state
|
||||
},
|
||||
moveIngredient: function (step, ingredient, new_index) {
|
||||
step.ingredients.splice(step.ingredients.indexOf(ingredient), 1)
|
||||
step.ingredients.splice(new_index < 0 ? 0 : new_index, 0, ingredient)
|
||||
@@ -1121,6 +1127,14 @@ export default {
|
||||
let new_keyword = {label: tag, name: tag}
|
||||
this.recipe.keywords.push(new_keyword)
|
||||
},
|
||||
addProperty: function () {
|
||||
this.recipe.properties.push(
|
||||
{'property_amount': 0, 'property_type': null}
|
||||
)
|
||||
},
|
||||
deleteProperty: function (recipe_property) {
|
||||
this.recipe.properties = this.recipe.properties.filter(p => p.id !== recipe_property.id)
|
||||
},
|
||||
searchKeywords: function (query) {
|
||||
let apiFactory = new ApiApiFactory()
|
||||
|
||||
@@ -1258,28 +1272,34 @@ export default {
|
||||
ing_list.forEach((ing) => {
|
||||
if (ing.trim() !== "") {
|
||||
promises.push(this.genericPostAPI("api_ingredient_from_string", {text: ing}).then((result) => {
|
||||
|
||||
let unit = null
|
||||
if (result.data.unit !== "" && result.data.unit !== null) {
|
||||
unit = {name: result.data.unit}
|
||||
}
|
||||
parsed_ing_list.push({
|
||||
let new_ingredient = {
|
||||
amount: result.data.amount,
|
||||
unit: unit,
|
||||
food: {name: result.data.food},
|
||||
note: result.data.note,
|
||||
original_text: ing,
|
||||
})
|
||||
}
|
||||
console.log(ing, new_ingredient)
|
||||
parsed_ing_list.push(new_ingredient)
|
||||
}))
|
||||
}
|
||||
})
|
||||
Promise.allSettled(promises).then(() => {
|
||||
ing_list.forEach(ing => {
|
||||
step.ingredients.push(parsed_ing_list.find(x => x.original_text === ing))
|
||||
if(ing.trim() !== ""){
|
||||
step.ingredients.push(parsed_ing_list.find(x => x.original_text === ing))
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
duplicateIngredient: function (step, ingredient, new_index) {
|
||||
delete ingredient.id
|
||||
ingredient = JSON.parse(JSON.stringify(ingredient))
|
||||
step.ingredients.splice(new_index < 0 ? 0 : new_index, 0, ingredient)
|
||||
}
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,158 +1,6 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<template v-if="loading">
|
||||
<loading-spinner></loading-spinner>
|
||||
</template>
|
||||
|
||||
<div v-if="!loading" style="padding-bottom: 60px">
|
||||
<RecipeSwitcher ref="ref_recipe_switcher" @switch="quickSwitch($event)"/>
|
||||
<div class="row">
|
||||
<div class="col-12" style="text-align: center">
|
||||
<h3>{{ recipe.name }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row text-center">
|
||||
<div class="col col-md-12">
|
||||
<recipe-rating :recipe="recipe"></recipe-rating>
|
||||
<last-cooked :recipe="recipe" class="mt-2"></last-cooked>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-auto">
|
||||
<div class="col-12" style="text-align: center">
|
||||
<i>{{ recipe.description }}</i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="text-align: center">
|
||||
<keywords-component :recipe="recipe"></keywords-component>
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
<div class="row align-items-center">
|
||||
<div class="col col-md-3">
|
||||
<div class="d-flex">
|
||||
<div class="my-auto mr-1">
|
||||
<i class="fas fa-fw fa-user-clock fa-2x text-primary"></i>
|
||||
</div>
|
||||
<div class="my-auto mr-1">
|
||||
<span class="text-primary"><b>{{ $t("Preparation") }}</b></span><br/>
|
||||
{{ working_time }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col col-md-3">
|
||||
<div class="row d-flex">
|
||||
<div class="my-auto mr-1">
|
||||
<i class="far fa-fw fa-clock fa-2x text-primary"></i>
|
||||
</div>
|
||||
<div class="my-auto mr-1">
|
||||
<span class="text-primary"><b>{{ $t("Waiting") }}</b></span><br/>
|
||||
{{ waiting_time }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col col-md-4 col-10 mt-2 mt-md-0">
|
||||
<div class="d-flex">
|
||||
<div class="my-auto mr-1">
|
||||
<i class="fas fa-fw fa-pizza-slice fa-2x text-primary"></i>
|
||||
</div>
|
||||
<div class="my-auto mr-1">
|
||||
<CustomInputSpinButton v-model.number="servings"/>
|
||||
</div>
|
||||
<div class="my-auto mr-1">
|
||||
<span class="text-primary">
|
||||
<b>
|
||||
<template v-if="recipe.servings_text === ''">{{ $t("Servings") }}</template>
|
||||
<template v-else>{{ recipe.servings_text }}</template>
|
||||
</b>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col col-md-2 col-2 mt-2 mt-md-0 text-right">
|
||||
<recipe-context-menu v-bind:recipe="recipe" :servings="servings"
|
||||
:disabled_options="{print:false}"></recipe-context-menu>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 order-md-1 col-sm-12 order-sm-2 col-12 order-2"
|
||||
v-if="recipe && ingredient_count > 0 && (recipe.show_ingredient_overview || recipe.steps.length < 2)">
|
||||
<ingredients-card
|
||||
:recipe="recipe.id"
|
||||
:steps="recipe.steps"
|
||||
:ingredient_factor="ingredient_factor"
|
||||
:servings="servings"
|
||||
:header="true"
|
||||
id="ingredient_container"
|
||||
@checked-state-changed="updateIngredientCheckedState"
|
||||
@change-servings="servings = $event"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-12 order-1 col-sm-12 order-sm-1 col-md-6 order-md-2">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<img class="img img-fluid rounded" :src="recipe.image" :alt="$t('Recipe_Image')"
|
||||
v-if="recipe.image !== null" @load="onImgLoad"
|
||||
:style="{ 'max-height': ingredient_height }"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template v-if="!recipe.internal">
|
||||
<div v-if="recipe.file_path.includes('.pdf')">
|
||||
<PdfViewer :recipe="recipe"></PdfViewer>
|
||||
</div>
|
||||
<div
|
||||
v-if="recipe.file_path.includes('.png') || recipe.file_path.includes('.jpg') || recipe.file_path.includes('.jpeg') || recipe.file_path.includes('.gif')">
|
||||
<ImageViewer :recipe="recipe"></ImageViewer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-for="(s, index) in recipe.steps" v-bind:key="s.id" style="margin-top: 1vh">
|
||||
<step-component
|
||||
:recipe="recipe"
|
||||
:step="s"
|
||||
:ingredient_factor="ingredient_factor"
|
||||
:index="index"
|
||||
:start_time="start_time"
|
||||
@update-start-time="updateStartTime"
|
||||
@checked-state-changed="updateIngredientCheckedState"
|
||||
></step-component>
|
||||
</div>
|
||||
|
||||
<div v-if="recipe.source_url !== null">
|
||||
<h6 class="d-print-none"><i class="fas fa-file-import"></i> {{ $t("Imported_From") }}</h6>
|
||||
<span class="text-muted mt-1"><a style="overflow-wrap: break-word;"
|
||||
:href="recipe.source_url">{{ recipe.source_url }}</a></span>
|
||||
</div>
|
||||
|
||||
<div class="row" style="margin-top: 2vh; ">
|
||||
<div class="col-lg-6 offset-lg-3 col-12">
|
||||
<Nutrition-component :recipe="recipe" id="nutrition_container"
|
||||
:ingredient_factor="ingredient_factor"></Nutrition-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<add-recipe-to-book :recipe="recipe"></add-recipe-to-book>
|
||||
|
||||
<div class="row text-center d-print-none" style="margin-top: 3vh; margin-bottom: 3vh"
|
||||
v-if="share_uid !== 'None' && !loading">
|
||||
<div class="col col-md-12">
|
||||
<import-tandoor></import-tandoor> <br/>
|
||||
<a :href="resolveDjangoUrl('view_report_share_abuse', share_uid)" class="mt-3">{{ $t("Report Abuse") }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="app" v-if="recipe_id !== undefined">
|
||||
<recipe-view-component :recipe_id="recipe_id"></recipe-view-component>
|
||||
|
||||
<bottom-navigation-bar></bottom-navigation-bar>
|
||||
</div>
|
||||
@@ -163,192 +11,31 @@ import Vue from "vue"
|
||||
import {BootstrapVue} from "bootstrap-vue"
|
||||
import "bootstrap-vue/dist/bootstrap-vue.css"
|
||||
|
||||
import {apiLoadRecipe} from "@/utils/api"
|
||||
|
||||
import RecipeContextMenu from "@/components/RecipeContextMenu"
|
||||
import {ResolveUrlMixin, ToastMixin, calculateHourMinuteSplit} from "@/utils/utils"
|
||||
|
||||
import PdfViewer from "@/components/PdfViewer"
|
||||
import ImageViewer from "@/components/ImageViewer"
|
||||
|
||||
import moment from "moment"
|
||||
import LoadingSpinner from "@/components/LoadingSpinner"
|
||||
import AddRecipeToBook from "@/components/Modals/AddRecipeToBook"
|
||||
import RecipeRating from "@/components/RecipeRating"
|
||||
import LastCooked from "@/components/LastCooked"
|
||||
import IngredientsCard from "@/components/IngredientsCard"
|
||||
import StepComponent from "@/components/StepComponent"
|
||||
import KeywordsComponent from "@/components/KeywordsComponent"
|
||||
import NutritionComponent from "@/components/NutritionComponent"
|
||||
import RecipeSwitcher from "@/components/Buttons/RecipeSwitcher"
|
||||
import CustomInputSpinButton from "@/components/CustomInputSpinButton"
|
||||
import {ApiApiFactory} from "@/utils/openapi/api";
|
||||
import ImportTandoor from "@/components/Modals/ImportTandoor.vue";
|
||||
import RecipeViewComponent from "@/components/RecipeViewComponent.vue";
|
||||
import BottomNavigationBar from "@/components/BottomNavigationBar.vue";
|
||||
|
||||
Vue.prototype.moment = moment
|
||||
|
||||
Vue.use(BootstrapVue)
|
||||
|
||||
export default {
|
||||
name: "RecipeView",
|
||||
mixins: [ResolveUrlMixin, ToastMixin],
|
||||
mixins: [],
|
||||
components: {
|
||||
ImportTandoor,
|
||||
LastCooked,
|
||||
RecipeRating,
|
||||
PdfViewer,
|
||||
ImageViewer,
|
||||
IngredientsCard,
|
||||
StepComponent,
|
||||
RecipeContextMenu,
|
||||
NutritionComponent,
|
||||
KeywordsComponent,
|
||||
LoadingSpinner,
|
||||
AddRecipeToBook,
|
||||
RecipeSwitcher,
|
||||
CustomInputSpinButton,
|
||||
BottomNavigationBar,
|
||||
},
|
||||
computed: {
|
||||
ingredient_factor: function () {
|
||||
return this.servings / this.recipe.servings
|
||||
},
|
||||
ingredient_count() {
|
||||
return this.recipe?.steps.map((x) => x.ingredients).flat().length
|
||||
},
|
||||
working_time: function () {
|
||||
return calculateHourMinuteSplit(this.recipe.working_time)
|
||||
},
|
||||
waiting_time: function () {
|
||||
return calculateHourMinuteSplit(this.recipe.waiting_time)
|
||||
},
|
||||
RecipeViewComponent,
|
||||
BottomNavigationBar
|
||||
},
|
||||
computed: {},
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
recipe: undefined,
|
||||
rootrecipe: undefined,
|
||||
servings: 1,
|
||||
servings_cache: {},
|
||||
start_time: "",
|
||||
share_uid: window.SHARE_UID,
|
||||
wake_lock: null,
|
||||
ingredient_height: '250',
|
||||
recipe_id: window.RECIPE_ID
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
servings(newVal, oldVal) {
|
||||
this.servings_cache[this.recipe.id] = this.servings
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.loadRecipe(window.RECIPE_ID)
|
||||
this.$i18n.locale = window.CUSTOM_LOCALE
|
||||
this.requestWakeLock()
|
||||
window.addEventListener('resize', this.handleResize);
|
||||
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.destroyWakeLock()
|
||||
},
|
||||
methods: {
|
||||
requestWakeLock: async function () {
|
||||
if ('wakeLock' in navigator) {
|
||||
try {
|
||||
this.wake_lock = await navigator.wakeLock.request('screen')
|
||||
document.addEventListener('visibilitychange', this.visibilityChange)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
},
|
||||
handleResize: function () {
|
||||
if (document.getElementById('nutrition_container') !== null) {
|
||||
this.ingredient_height = document.getElementById('ingredient_container').clientHeight - document.getElementById('nutrition_container').clientHeight
|
||||
} else {
|
||||
this.ingredient_height = document.getElementById('ingredient_container').clientHeight
|
||||
}
|
||||
},
|
||||
destroyWakeLock: function () {
|
||||
if (this.wake_lock != null) {
|
||||
this.wake_lock.release()
|
||||
.then(() => {
|
||||
this.wake_lock = null
|
||||
});
|
||||
}
|
||||
|
||||
document.removeEventListener('visibilitychange', this.visibilityChange)
|
||||
},
|
||||
visibilityChange: async function () {
|
||||
if (this.wake_lock != null && document.visibilityState === 'visible') {
|
||||
await this.requestWakeLock()
|
||||
}
|
||||
},
|
||||
loadRecipe: function (recipe_id) {
|
||||
apiLoadRecipe(recipe_id).then((recipe) => {
|
||||
let total_time = 0
|
||||
for (let step of recipe.steps) {
|
||||
for (let ingredient of step.ingredients) {
|
||||
this.$set(ingredient, "checked", false)
|
||||
}
|
||||
|
||||
step.time_offset = total_time
|
||||
total_time += step.time
|
||||
}
|
||||
|
||||
// set start time only if there are any steps with timers (otherwise no timers are rendered)
|
||||
if (total_time > 0) {
|
||||
this.start_time = moment().format("yyyy-MM-DDTHH:mm")
|
||||
}
|
||||
|
||||
|
||||
if (recipe.image === null) this.printReady()
|
||||
|
||||
this.recipe = this.rootrecipe = recipe
|
||||
this.servings = this.servings_cache[this.rootrecipe.id] = recipe.servings
|
||||
this.loading = false
|
||||
|
||||
setTimeout(() => {
|
||||
this.handleResize()
|
||||
}, 100)
|
||||
})
|
||||
},
|
||||
updateStartTime: function (e) {
|
||||
this.start_time = e
|
||||
},
|
||||
updateIngredientCheckedState: function (e) {
|
||||
for (let step of this.recipe.steps) {
|
||||
for (let ingredient of step.ingredients) {
|
||||
if (ingredient.id === e.id) {
|
||||
this.$set(ingredient, "checked", !ingredient.checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
quickSwitch: function (e) {
|
||||
if (e === -1) {
|
||||
this.recipe = this.rootrecipe
|
||||
this.servings = this.servings_cache[this.rootrecipe?.id ?? 1]
|
||||
} else {
|
||||
this.recipe = e
|
||||
this.servings = this.servings_cache?.[e.id] ?? e.servings
|
||||
}
|
||||
},
|
||||
printReady: function () {
|
||||
const template = document.createElement("template");
|
||||
template.id = "printReady";
|
||||
document.body.appendChild(template);
|
||||
},
|
||||
onImgLoad: function () {
|
||||
this.printReady()
|
||||
},
|
||||
},
|
||||
methods: {},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#app > div > div {
|
||||
break-inside: avoid;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -48,17 +48,17 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="us in user_spaces" :key="us.id">
|
||||
<td>{{ us.user.username }}</td>
|
||||
<td>{{ us.user.display_name }}</td>
|
||||
<td>
|
||||
<generic-multiselect
|
||||
class="input-group-text m-0 p-0"
|
||||
@change="us.groups = $event.val; updateUserSpace(us)"
|
||||
label="name"
|
||||
:initial_selection="us.groups"
|
||||
:model="Models.GROUP"
|
||||
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
|
||||
:limit="10"
|
||||
:multiple="true"
|
||||
class="input-group-text m-0 p-0"
|
||||
@change="us.groups = $event.val; updateUserSpace(us)"
|
||||
label="name"
|
||||
:initial_selection="us.groups"
|
||||
:model="Models.GROUP"
|
||||
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
|
||||
:limit="10"
|
||||
:multiple="true"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
@@ -90,14 +90,14 @@
|
||||
<td>{{ il.email }}</td>
|
||||
<td>
|
||||
<generic-multiselect
|
||||
class="input-group-text m-0 p-0"
|
||||
@change="il.group = $event.val;"
|
||||
label="name"
|
||||
:initial_single_selection="il.group"
|
||||
:model="Models.GROUP"
|
||||
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
|
||||
:limit="10"
|
||||
:multiple="false"
|
||||
class="input-group-text m-0 p-0"
|
||||
@change="il.group = $event.val;"
|
||||
label="name"
|
||||
:initial_single_selection="il.group"
|
||||
:model="Models.GROUP"
|
||||
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
|
||||
:limit="10"
|
||||
:multiple="false"
|
||||
/>
|
||||
</td>
|
||||
<td><input type="date" v-model="il.valid_until" class="form-control"></td>
|
||||
@@ -164,6 +164,15 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h4>{{ $t('Open_Data_Import') }}</h4>
|
||||
<open-data-import-component></open-data-import-component>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col col-12">
|
||||
<h4 class="mt-2"><i class="fas fa-trash"></i> {{ $t('Delete') }}</h4>
|
||||
@@ -198,6 +207,7 @@ import GenericMultiselect from "@/components/GenericMultiselect";
|
||||
import GenericModalForm from "@/components/Modals/GenericModalForm";
|
||||
import axios from "axios";
|
||||
import VueClipboard from 'vue-clipboard2'
|
||||
import OpenDataImportComponent from "@/components/OpenDataImportComponent.vue";
|
||||
|
||||
Vue.use(VueClipboard)
|
||||
|
||||
@@ -206,7 +216,7 @@ Vue.use(BootstrapVue)
|
||||
export default {
|
||||
name: "SpaceManageView",
|
||||
mixins: [ResolveUrlMixin, ToastMixin, ApiMixin],
|
||||
components: {GenericMultiselect, GenericModalForm},
|
||||
components: {GenericMultiselect, GenericModalForm, OpenDataImportComponent},
|
||||
data() {
|
||||
return {
|
||||
ACTIVE_SPACE_ID: window.ACTIVE_SPACE_ID,
|
||||
@@ -228,8 +238,8 @@ export default {
|
||||
apiFactory.retrieveSpace(window.ACTIVE_SPACE_ID).then(r => {
|
||||
this.space = r.data
|
||||
})
|
||||
apiFactory.listUserSpaces().then(r => {
|
||||
this.user_spaces = r.data
|
||||
apiFactory.listUserSpaces(1, 25).then(r => { //TODO build proper pagination
|
||||
this.user_spaces = r.data.results
|
||||
})
|
||||
this.loadInviteLinks()
|
||||
},
|
||||
@@ -239,7 +249,7 @@ export default {
|
||||
if (link) {
|
||||
content = localStorage.BASE_PATH + this.resolveDjangoUrl('view_invite', inviteLink.uuid)
|
||||
}
|
||||
this.$copyText(content)
|
||||
this.$copyText(content)
|
||||
},
|
||||
loadInviteLinks: function () {
|
||||
let apiFactory = new ApiApiFactory()
|
||||
|
||||
@@ -1,103 +1,41 @@
|
||||
<template>
|
||||
|
||||
<div id="app">
|
||||
<div class="row" v-if="food">
|
||||
<div class="col-12">
|
||||
<h2>{{ food.name }}</h2>
|
||||
|
||||
<beta-warning></beta-warning>
|
||||
|
||||
<div v-if="metadata !== undefined">
|
||||
{{ $t('Data_Import_Info') }}
|
||||
|
||||
|
||||
<select class="form-control" v-model="selected_version">
|
||||
<option v-for="v in metadata.versions" v-bind:key="v">{{ v }}</option>
|
||||
</select>
|
||||
|
||||
<b-checkbox v-model="update_existing" class="mt-1">{{ $t('Update_Existing_Data') }}</b-checkbox>
|
||||
<b-checkbox v-model="use_metric" class="mt-1">{{ $t('Use_Metric') }}</b-checkbox>
|
||||
|
||||
|
||||
<div v-if="selected_version !== undefined" class="mt-3">
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th>{{ $t('Datatype') }}</th>
|
||||
<th>{{ $t('Number of Objects') }}</th>
|
||||
<th>{{ $t('Imported') }}</th>
|
||||
</tr>
|
||||
<tr v-for="d in metadata.datatypes" v-bind:key="d">
|
||||
<td>{{ $t(d.charAt(0).toUpperCase() + d.slice(1)) }}</td>
|
||||
<td>{{ metadata[selected_version][d] }}</td>
|
||||
<td>
|
||||
<template v-if="import_count !== undefined">{{ import_count[d] }}</template>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<button class="btn btn-success" @click="doImport">{{ $t('Import') }}</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<b-form v-if="food">
|
||||
<b-form-group :label="$t('Name')" description="">
|
||||
<b-form-input v-model="food.name"></b-form-input>
|
||||
</b-form-group>
|
||||
<b-form-group :label="$t('Plural')" description="">
|
||||
<b-form-input v-model="food.plural_name"></b-form-input>
|
||||
</b-form-group>
|
||||
|
||||
|
||||
<b-form-group :label="$t('Recipe')" :description="$t('food_recipe_help')">
|
||||
<generic-multiselect
|
||||
@change="food.recipe = $event.val;"
|
||||
:model="Models.RECIPE"
|
||||
:initial_selection="food.recipe"
|
||||
label="name"
|
||||
:multiple="false"
|
||||
:placeholder="$t('Recipe')"
|
||||
></generic-multiselect>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group :description="$t('OnHand_help')">
|
||||
<b-form-checkbox v-model="food.food_onhand">{{ $t('OnHand') }}</b-form-checkbox>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group :description="$t('ignore_shopping_help')">
|
||||
<b-form-checkbox v-model="food.ignore_shopping">{{ $t('Ignore_Shopping') }}</b-form-checkbox>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group :label="$t('Shopping_Category')" :description="$t('shopping_category_help')">
|
||||
<generic-multiselect
|
||||
@change="food.supermarket_category = $event.val;"
|
||||
:model="Models.SHOPPING_CATEGORY"
|
||||
:initial_selection="food.supermarket_category"
|
||||
label="name"
|
||||
:multiple="false"
|
||||
:placeholder="$t('Shopping_Category')"
|
||||
></generic-multiselect>
|
||||
</b-form-group>
|
||||
|
||||
<hr/>
|
||||
<!-- todo add conditions if false disable dont hide -->
|
||||
<b-form-group :label="$t('Substitutes')" :description="$t('substitute_help')">
|
||||
<generic-multiselect
|
||||
@change="food.substitute = $event.val;"
|
||||
:model="Models.FOOD"
|
||||
:initial_selection="food.substitute"
|
||||
label="name"
|
||||
:multiple="false"
|
||||
:placeholder="$t('Substitutes')"
|
||||
></generic-multiselect>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group :description="$t('substitute_siblings_help')">
|
||||
<b-form-checkbox v-model="food.substitute_siblings">{{ $t('substitute_siblings') }}</b-form-checkbox>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group :label="$t('InheritFields')" :description="$t('InheritFields_help')">
|
||||
<generic-multiselect
|
||||
@change="food.inherit_fields = $event.val;"
|
||||
:model="Models.FOOD_INHERIT_FIELDS"
|
||||
:initial_selection="food.inherit_fields"
|
||||
label="name"
|
||||
:multiple="false"
|
||||
:placeholder="$t('InheritFields')"
|
||||
></generic-multiselect>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group :label="$t('ChildInheritFields')" :description="$t('ChildInheritFields_help')">
|
||||
<generic-multiselect
|
||||
@change="food.child_inherit_fields = $event.val;"
|
||||
:model="Models.FOOD_INHERIT_FIELDS"
|
||||
:initial_selection="food.child_inherit_fields"
|
||||
label="name"
|
||||
:multiple="false"
|
||||
:placeholder="$t('ChildInheritFields')"
|
||||
></generic-multiselect>
|
||||
</b-form-group>
|
||||
|
||||
<!-- TODO change to a button -->
|
||||
<b-form-group :description="$t('reset_children_help')">
|
||||
<b-form-checkbox v-model="food.reset_inherit">{{ $t('reset_children') }}</b-form-checkbox>
|
||||
</b-form-group>
|
||||
|
||||
<b-button variant="primary" @click="updateFood">{{ $t('Save') }}</b-button>
|
||||
</b-form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -107,10 +45,9 @@ import Vue from "vue"
|
||||
import {BootstrapVue} from "bootstrap-vue"
|
||||
|
||||
import "bootstrap-vue/dist/bootstrap-vue.css"
|
||||
import {ApiApiFactory} from "@/utils/openapi/api";
|
||||
import RecipeCard from "@/components/RecipeCard.vue";
|
||||
import GenericMultiselect from "@/components/GenericMultiselect.vue";
|
||||
import {ApiMixin, StandardToasts} from "@/utils/utils";
|
||||
import {ApiMixin, resolveDjangoUrl, StandardToasts} from "@/utils/utils";
|
||||
import axios from "axios";
|
||||
import BetaWarning from "@/components/BetaWarning.vue";
|
||||
|
||||
|
||||
Vue.use(BootstrapVue)
|
||||
@@ -119,33 +56,39 @@ Vue.use(BootstrapVue)
|
||||
export default {
|
||||
name: "TestView",
|
||||
mixins: [ApiMixin],
|
||||
components: {
|
||||
GenericMultiselect
|
||||
},
|
||||
components: {BetaWarning},
|
||||
data() {
|
||||
return {
|
||||
food: undefined,
|
||||
|
||||
metadata: undefined,
|
||||
selected_version: undefined,
|
||||
update_existing: true,
|
||||
use_metric: true,
|
||||
import_count: undefined,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$i18n.locale = window.CUSTOM_LOCALE
|
||||
let apiClient = new ApiApiFactory()
|
||||
apiClient.retrieveFood('1').then((r) => {
|
||||
this.food = r.data
|
||||
})
|
||||
|
||||
axios.get(resolveDjangoUrl('api_import_open_data')).then(r => {
|
||||
this.metadata = r.data
|
||||
}).catch(err => {
|
||||
StandardToasts.makeStandardToast(this, StandardToasts.FAIL_FETCH, err)
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
updateFood: function () {
|
||||
let apiClient = new ApiApiFactory()
|
||||
apiClient.updateFood(this.food.id, this.food).then((r) => {
|
||||
this.food = r.data
|
||||
StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_UPDATE)
|
||||
doImport: function () {
|
||||
axios.post(resolveDjangoUrl('api_import_open_data'), {
|
||||
'selected_version': this.selected_version,
|
||||
'selected_datatypes': this.metadata.datatypes,
|
||||
'update_existing': this.update_existing,
|
||||
'use_metric': this.use_metric,
|
||||
}).then(r => {
|
||||
StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_CREATE)
|
||||
this.import_count = r.data
|
||||
}).catch(err => {
|
||||
StandardToasts.makeStandardToast(this, StandardToasts.FAIL_UPDATE, err)
|
||||
StandardToasts.makeStandardToast(this, StandardToasts.FAIL_CREATE, err)
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
155
vue/src/apps/TestView/TestViewBackup.vue
Normal file
155
vue/src/apps/TestView/TestViewBackup.vue
Normal file
@@ -0,0 +1,155 @@
|
||||
<template>
|
||||
|
||||
<div id="app">
|
||||
<div class="row" v-if="food">
|
||||
<div class="col-12">
|
||||
<h2>{{ food.name }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<b-form v-if="food">
|
||||
<b-form-group :label="$t('Name')" description="">
|
||||
<b-form-input v-model="food.name"></b-form-input>
|
||||
</b-form-group>
|
||||
<b-form-group :label="$t('Plural')" description="">
|
||||
<b-form-input v-model="food.plural_name"></b-form-input>
|
||||
</b-form-group>
|
||||
|
||||
|
||||
<b-form-group :label="$t('Recipe')" :description="$t('food_recipe_help')">
|
||||
<generic-multiselect
|
||||
@change="food.recipe = $event.val;"
|
||||
:model="Models.RECIPE"
|
||||
:initial_selection="food.recipe"
|
||||
label="name"
|
||||
:multiple="false"
|
||||
:placeholder="$t('Recipe')"
|
||||
></generic-multiselect>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group :description="$t('OnHand_help')">
|
||||
<b-form-checkbox v-model="food.food_onhand">{{ $t('OnHand') }}</b-form-checkbox>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group :description="$t('ignore_shopping_help')">
|
||||
<b-form-checkbox v-model="food.ignore_shopping">{{ $t('Ignore_Shopping') }}</b-form-checkbox>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group :label="$t('Shopping_Category')" :description="$t('shopping_category_help')">
|
||||
<generic-multiselect
|
||||
@change="food.supermarket_category = $event.val;"
|
||||
:model="Models.SHOPPING_CATEGORY"
|
||||
:initial_selection="food.supermarket_category"
|
||||
label="name"
|
||||
:multiple="false"
|
||||
:placeholder="$t('Shopping_Category')"
|
||||
></generic-multiselect>
|
||||
</b-form-group>
|
||||
|
||||
<hr/>
|
||||
<!-- todo add conditions if false disable dont hide -->
|
||||
<b-form-group :label="$t('Substitutes')" :description="$t('substitute_help')">
|
||||
<generic-multiselect
|
||||
@change="food.substitute = $event.val;"
|
||||
:model="Models.FOOD"
|
||||
:initial_selection="food.substitute"
|
||||
label="name"
|
||||
:multiple="false"
|
||||
:placeholder="$t('Substitutes')"
|
||||
></generic-multiselect>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group :description="$t('substitute_siblings_help')">
|
||||
<b-form-checkbox v-model="food.substitute_siblings">{{ $t('substitute_siblings') }}</b-form-checkbox>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group :label="$t('InheritFields')" :description="$t('InheritFields_help')">
|
||||
<generic-multiselect
|
||||
@change="food.inherit_fields = $event.val;"
|
||||
:model="Models.FOOD_INHERIT_FIELDS"
|
||||
:initial_selection="food.inherit_fields"
|
||||
label="name"
|
||||
:multiple="false"
|
||||
:placeholder="$t('InheritFields')"
|
||||
></generic-multiselect>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group :label="$t('ChildInheritFields')" :description="$t('ChildInheritFields_help')">
|
||||
<generic-multiselect
|
||||
@change="food.child_inherit_fields = $event.val;"
|
||||
:model="Models.FOOD_INHERIT_FIELDS"
|
||||
:initial_selection="food.child_inherit_fields"
|
||||
label="name"
|
||||
:multiple="false"
|
||||
:placeholder="$t('ChildInheritFields')"
|
||||
></generic-multiselect>
|
||||
</b-form-group>
|
||||
|
||||
<!-- TODO change to a button -->
|
||||
<b-form-group :description="$t('reset_children_help')">
|
||||
<b-form-checkbox v-model="food.reset_inherit">{{ $t('reset_children') }}</b-form-checkbox>
|
||||
</b-form-group>
|
||||
|
||||
<b-button variant="primary" @click="updateFood">{{ $t('Save') }}</b-button>
|
||||
</b-form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import Vue from "vue"
|
||||
import {BootstrapVue} from "bootstrap-vue"
|
||||
|
||||
import "bootstrap-vue/dist/bootstrap-vue.css"
|
||||
import {ApiApiFactory} from "@/utils/openapi/api";
|
||||
import RecipeCard from "@/components/RecipeCard.vue";
|
||||
import GenericMultiselect from "@/components/GenericMultiselect.vue";
|
||||
import {ApiMixin, StandardToasts} from "@/utils/utils";
|
||||
|
||||
|
||||
Vue.use(BootstrapVue)
|
||||
|
||||
|
||||
export default {
|
||||
name: "TestView",
|
||||
mixins: [ApiMixin],
|
||||
components: {
|
||||
GenericMultiselect
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
food: undefined,
|
||||
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$i18n.locale = window.CUSTOM_LOCALE
|
||||
let apiClient = new ApiApiFactory()
|
||||
apiClient.retrieveFood('1').then((r) => {
|
||||
this.food = r.data
|
||||
})
|
||||
|
||||
},
|
||||
methods: {
|
||||
updateFood: function () {
|
||||
let apiClient = new ApiApiFactory()
|
||||
apiClient.updateFood(this.food.id, this.food).then((r) => {
|
||||
this.food = r.data
|
||||
StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_UPDATE)
|
||||
}).catch(err => {
|
||||
StandardToasts.makeStandardToast(this, StandardToasts.FAIL_UPDATE, err)
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user