From b86c9d295a34adf74d53548b2a85210c6c9626a6 Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Mon, 13 Sep 2021 14:06:19 +0200 Subject: [PATCH] basic errors fixed --- .../templates/edit_internal_recipe_v2.html | 4 +- .../apps/RecipeEditView/RecipeEditView.vue | 722 +++++++++++++++++- vue/src/locales/en.json | 21 +- 3 files changed, 736 insertions(+), 11 deletions(-) diff --git a/cookbook/templates/edit_internal_recipe_v2.html b/cookbook/templates/edit_internal_recipe_v2.html index 564b96e8a..92f5f6796 100644 --- a/cookbook/templates/edit_internal_recipe_v2.html +++ b/cookbook/templates/edit_internal_recipe_v2.html @@ -12,7 +12,7 @@ {% block content %} -
+
@@ -30,7 +30,7 @@ diff --git a/vue/src/apps/RecipeEditView/RecipeEditView.vue b/vue/src/apps/RecipeEditView/RecipeEditView.vue index 47b62002f..00dfda207 100644 --- a/vue/src/apps/RecipeEditView/RecipeEditView.vue +++ b/vue/src/apps/RecipeEditView/RecipeEditView.vue @@ -1,5 +1,426 @@ @@ -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" }