+
+
{{ $t('Edit') }} {{ $t('Recipe') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ step.name }}
+ {{ $t('Step') }} {{ step_index + 1 }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ $t('Ingredients') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -10,31 +431,316 @@ import {BootstrapVue} from 'bootstrap-vue'
import 'bootstrap-vue/dist/bootstrap-vue.css'
-import {ResolveUrlMixin} from "@/utils/utils";
+import {resolveDjangoUrl, ResolveUrlMixin, StandardToasts} from "@/utils/utils";
+import Multiselect from "vue-multiselect";
+import {ApiApiFactory} from "@/utils/openapi/api";
+import LoadingSpinner from "@/components/LoadingSpinner";
Vue.use(BootstrapVue)
export default {
name: 'RecipeSearchView',
mixins: [ResolveUrlMixin],
- components: {},
+ components: {Multiselect, LoadingSpinner},
data() {
return {
-
+ recipe_id: window.RECIPE_ID,
+ recipe: undefined,
+ recipe_changed: undefined,
+ keywords: [],
+ keywords_loading: false,
+ foods: [],
+ foods_loading: false,
+ units: [],
+ units_loading: false,
+ files: [],
+ files_loading: false,
+ recipes: [],
+ recipes_loading: false,
+ message: '',
}
},
- computed: {
-
- },
+ computed: {},
mounted() {
+ this.loadRecipe()
+ this.searchUnits('')
+ this.searchFoods('')
+ this.searchKeywords('')
+ this.searchFiles('')
+ this.searchRecipes('')
+
+
+ //TODO find out what this did and fix it
+ // this._keyListener = function (e) {
+ // if (e.code === "Space" && e.ctrlKey) {
+ // e.preventDefault(); // present "Save Page" from getting triggered.
+ //
+ // for (el of e.path) {
+ // if (el.id !== undefined && el.id.includes('id_card_step_')) {
+ // let step = this.recipe.steps[el.id.replace('id_card_step_', '')]
+ // this.addIngredient(step)
+ // }
+ // }
+ // }
+ // };
+ // document.addEventListener('keydown', this._keyListener.bind(this));
+
this.$i18n.locale = window.CUSTOM_LOCALE
},
+ created() {
+ window.addEventListener('beforeunload', this.warnPageLeave)
+ },
+ beforeUnmount() {
+ document.removeEventListener('keydown', this._keyListener);
+ },
watch: {
-
+ recipe: {
+ deep: true,
+ handler() {
+ this.recipe_changed = this.recipe_changed !== undefined;
+ }
+ }
},
methods: {
+ warnPageLeave: function (event) {
+ if (this.recipe_changed) {
+ event.returnValue = ''
+ return ''
+ }
+ },
+ loadRecipe: function () {
+ let apiFactory = new ApiApiFactory()
+
+ apiFactory.retrieveRecipe(this.recipe_id).then(response => {
+ this.recipe = response.data;
+ this.loading = false
+
+
+ //TODO workaround function until view is properly refactored, loads name of selected sub recipe so the input can find its label
+ this.recipe.steps.forEach(s => {
+ if (s.step_recipe != null) {
+ this.recipes.push(s.step_recipe_data)
+ }
+ })
+ console.log('after step loop')
+ }).catch((err) => {
+ this.loading = false
+ console.log(err)
+ StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH)
+ })
+ },
+ updateRecipe: function (view_after) {
+ let apiFactory = new ApiApiFactory()
+
+ this.sortSteps()
+ for (let s of this.recipe.steps) {
+ this.sortIngredients(s)
+
+ }
+ apiFactory.updateRecipe(this.recipe_id, this.recipe,
+ {}).then((response) => {
+ console.log(response)
+ StandardToasts.makeStandardToast(StandardToasts.SUCCESS_UPDATE)
+ this.recipe_changed = false
+ if (view_after) {
+ location.href = resolveDjangoUrl('view_recipe', this.recipe_id)
+ }
+ }).catch((err) => {
+ console.log(err)
+ StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE)
+ })
+ },
+ imageChanged: function (event) {
+ let apiFactory = new ApiApiFactory()
+
+ if (event.target.files && event.target.files[0]) {
+ let fd = new FormData()
+ fd.append('image', event.target.files[0])
+ apiFactory.imageRecipe("{% url 'api:recipe-detail' recipe.pk %}" + 'image/', fd, {headers: {'Content-Type': 'multipart/form-data'}}).then((response) => {
+
+ console.log(response)
+ StandardToasts.makeStandardToast(StandardToasts.SUCCESS_UPDATE)
+ }).catch((err) => {
+ console.log(err)
+ StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE)
+ })
+
+ let reader = new FileReader();
+ reader.onload = function (e) {
+ //TODO load new image to view
+ }
+ reader.readAsDataURL(event.target.files[0]);
+ }
+ },
+ addStep: function () { //TODO see if default can be generated from options request
+ this.recipe.steps.push(
+ {'instruction': '', ingredients: [], type: 'TEXT', show_as_header: true}
+ )
+ },
+ sortSteps: function () {
+ this.recipe.steps.forEach(function (element, index) {
+ element.order = index
+ });
+ },
+ sortIngredients: function (step) {
+ step.ingredients.forEach(function (element, index) {
+ element.order = index
+ });
+ },
+ addIngredient: function (step) { //TODO see if default can be generated from options request
+ step.ingredients.push({
+ 'food': null,
+ 'unit': {
+ 'name': '{{request.user.userpreference.default_unit}}'
+ },
+ 'amount': 0,
+ 'note': '',
+ 'order': 0,
+ 'is_header': false,
+ 'no_amount': false
+ })
+ this.sortIngredients(step)
+ this.$nextTick(() => document.getElementById(`amount_${this.recipe.steps.indexOf(step)}_${step.ingredients.length - 1}`).focus())
+
+ },
+ removeIngredient: function (step, ingredient) {
+ if (confirm(this.$t('confirm_delete', {object: this.$t('Ingredient')}))) {
+ step.ingredients = step.ingredients.filter(item => item !== ingredient)
+ }
+ },
+ removeStep: function (step) {
+ if (confirm(this.$t('confirm_delete', {object: this.$t('Step')}))) {
+ this.recipe.steps = this.recipe.steps.filter(item => item !== step)
+ }
+ },
+ moveStep: function (step, new_index) {
+ this.recipe.steps.splice(this.recipe.steps.indexOf(step), 1);
+ this.recipe.steps.splice((new_index < 0 ? 0 : new_index), 0, step);
+ this.sortSteps()
+ },
+ addFoodType: function (tag, index) {
+ let [tmp, step, id] = index.split('_')
+
+ let new_food = this.recipe.steps[step].ingredients[id]
+ new_food.food = {'name': tag}
+ this.foods.push(new_food.food)
+ this.recipe.steps[step].ingredients[id] = new_food
+ },
+ addUnitType: function (tag, index) {
+ let [tmp, step, id] = index.split('_')
+
+ let new_unit = this.recipe.steps[step].ingredients[id]
+ new_unit.unit = {'name': tag}
+ this.units.push(new_unit.unit)
+ this.recipe.steps[step].ingredients[id] = new_unit
+ },
+ addKeyword: function (tag) {
+ let new_keyword = {'label': tag, 'name': tag}
+ this.recipe.keywords.push(new_keyword)
+ },
+ searchKeywords: function (query) {
+ let apiFactory = new ApiApiFactory()
+
+ this.keywords_loading = true
+ apiFactory.listKeywords({query: {query: query}}).then((response) => {
+ this.keywords = response.data.results;
+ this.keywords_loading = false
+ }).catch((err) => {
+ console.log(err)
+ StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH)
+ })
+ },
+ searchFiles: function (query) {
+ let apiFactory = new ApiApiFactory()
+
+ this.files_loading = true
+ apiFactory.listUserFiles({query: {query: query}}).then((response) => {
+ this.files = response.data
+ this.files_loading = false
+ }).catch((err) => {
+ console.log(err)
+ StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH)
+ })
+ },
+ searchRecipes: function (query) {
+ let apiFactory = new ApiApiFactory()
+
+ this.recipes_loading = true
+ apiFactory.listRecipes({query: {query: query}}).then((response) => {
+ this.recipes = response.data.results
+ this.recipes_loading = false
+ }).catch((err) => {
+ console.log(err)
+ StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH)
+ })
+ },
+ searchUnits: function (query) {
+ let apiFactory = new ApiApiFactory()
+
+ this.units_loading = true
+ apiFactory.listUnits({query: {query: query}}).then((response) => {
+ this.units = response.data.results;
+
+ if (this.recipe !== undefined) {
+ for (let s of this.recipe.steps) {
+ for (let i of s.ingredients) {
+ if (i.unit !== null && i.unit.id === undefined) {
+ this.units.push(i.unit)
+ }
+ }
+ }
+ }
+ this.units_loading = false
+ }).catch((err) => {
+ StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH)
+ })
+ },
+ searchFoods: function (query) {
+ let apiFactory = new ApiApiFactory()
+
+ this.foods_loading = true
+ apiFactory.listFoods({query: {query: query}}).then((response) => {
+ this.foods = response.data.results
+
+ if (this.recipe !== undefined) {
+ for (let s of this.recipe.steps) {
+ for (let i of s.ingredients) {
+ if (i.food !== null && i.food.id === undefined) {
+ this.foods.push(i.food)
+ }
+ }
+ }
+ }
+
+ this.foods_loading = false
+ }).catch((err) => {
+ StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH)
+ })
+ },
+ scrollToStep: function (step_index) {
+ document.getElementById('id_step_' + step_index).scrollIntoView({behavior: 'smooth'});
+ },
+ addNutrition: function () {
+ this.recipe.nutrition = {}
+ },
+ removeNutrition: function () {
+ this.recipe.nutrition = null
+ },
+ copyTemplateReference: function (index, ingredient) {
+ const el = document.createElement('textarea');
+
+ let tag = `\u007B\u007B ingredients[${index}] \u007D\u007D`;
+ if (ingredient.food !== null) {
+ tag += `\u007B# ${ingredient.food.name} #\u007D`
+ }
+ el.value = tag
+ document.body.appendChild(el);
+ el.select();
+ document.execCommand('copy');
+ document.body.removeChild(el);
+ }
}
}
diff --git a/vue/src/locales/en.json b/vue/src/locales/en.json
index 896baa240..b463b12b2 100644
--- a/vue/src/locales/en.json
+++ b/vue/src/locales/en.json
@@ -7,6 +7,8 @@
"success_creating_resource": "Successfully created a resource!",
"success_updating_resource": "Successfully updated a resource!",
"success_deleting_resource": "Successfully deleted a resource!",
+ "step_time_minutes": "Step time in minutes",
+ "confirm_delete": "Are you sure you want to delete this {object}?",
"import_running": "Import running, please wait!",
"all_fields_optional": "All fields are optional and can be left empty.",
@@ -21,6 +23,10 @@
"Step_start_time": "Step start time",
"Sort_by_new": "Sort by new",
"Recipes_per_page": "Recipes per Page",
+ "Show_as_header": "Show as header",
+ "Hide_as_header": "Hide as header",
+ "Copy_template_reference": "Copy template reference",
+ "Save_and_View": "Save & View",
"Manage_Books": "Manage Books",
"Meal_Plan": "Meal Plan",
@@ -41,6 +47,15 @@
"Merge_Keyword": "Merge Keyword",
"Hide_Keywords": "Hide Keywords",
"Hide_Recipes": "Hide Recipes",
+ "Move_Up": "Move up",
+ "Move_Down": "Move down",
+ "Step_Name": "Step Name",
+ "Step_Type": "Step Type",
+ "Make_Header": "Make_Header",
+ "Make_Ingredient": "Make_Ingredient",
+ "Enable_Amount": "Enable Amount",
+ "Disable_Amount": "Disable Amount",
+ "Add_Step": "Add_Step",
"Keywords": "Keywords",
"Books": "Books",
@@ -59,6 +74,7 @@
"Link": "Link",
"Add": "Add",
"New": "New",
+ "Note": "Note",
"Success": "Success",
"Failure": "Failure",
"Ingredients": "Ingredients",
@@ -131,5 +147,8 @@
"Create_New_Keyword": "Add New Keyword",
"Create_New_Unit": "Add New Unit",
"and_up": "& Up",
- "Unrated": "Unrated"
+ "Instructions": "Instructions",
+ "Unrated": "Unrated",
+ "Time": "Time",
+ "Text": "Text"
}