added simple edit modal to recipe import

This commit is contained in:
vabene1111
2023-01-16 21:06:29 +01:00
parent 3b9d221258
commit 97bcf1111b
2 changed files with 187 additions and 158 deletions

View File

@@ -1,63 +1,89 @@
<template> <template>
<div v-if="recipe_json !== undefined" class="mt-2 mt-md-0"> <div v-if="recipe_json !== undefined" class="mt-2 mt-md-0">
<h5>Steps</h5> <h5>Steps</h5>
<div class="row"> <div class="row">
<div class="col col-md-12 text-center"> <div class="col col-md-12 text-center">
<b-button @click="autoSortIngredients()" variant="secondary" v-b-tooltip.hover v-if="recipe_json.steps.length > 1" <b-button @click="autoSortIngredients()" variant="secondary" v-b-tooltip.hover v-if="recipe_json.steps.length > 1"
:title="$t('Auto_Sort_Help')"><i class="fas fa-random"></i> {{ $t('Auto_Sort') }} :title="$t('Auto_Sort_Help')"><i class="fas fa-random"></i> {{ $t('Auto_Sort') }}
</b-button> </b-button>
<b-button @click="splitAllSteps('\n')" variant="secondary" class="ml-1" v-b-tooltip.hover <b-button @click="splitAllSteps('\n')" variant="secondary" class="ml-1" v-b-tooltip.hover
:title="$t('Split_All_Steps')"><i :title="$t('Split_All_Steps')"><i
class="fas fa-expand-arrows-alt"></i> {{ $t('All') }} class="fas fa-expand-arrows-alt"></i> {{ $t('All') }}
</b-button> </b-button>
<b-button @click="mergeAllSteps()" variant="primary" class="ml-1" v-b-tooltip.hover <b-button @click="mergeAllSteps()" variant="primary" class="ml-1" v-b-tooltip.hover
:title="$t('Combine_All_Steps')"><i :title="$t('Combine_All_Steps')"><i
class="fas fa-compress-arrows-alt"></i> {{ $t('All') }} class="fas fa-compress-arrows-alt"></i> {{ $t('All') }}
</b-button> </b-button>
</div> </div>
</div> </div>
<div class="row mt-2" v-for="(s, index) in recipe_json.steps" <div class="row mt-2" v-for="(s, index) in recipe_json.steps"
v-bind:key="index"> v-bind:key="index">
<div class="col col-md-4 d-none d-md-block"> <div class="col col-md-4 d-none d-md-block">
<draggable :list="s.ingredients" group="ingredients" <draggable :list="s.ingredients" group="ingredients"
:empty-insert-threshold="10"> :empty-insert-threshold="10">
<b-list-group-item v-for="i in s.ingredients" <b-list-group-item v-for="i in s.ingredients"
v-bind:key="i.original_text"><i v-bind:key="i.original_text"><i
class="fas fa-arrows-alt"></i> {{ i.original_text }} class="fas fa-arrows-alt mr-2"></i>
</b-list-group-item> <b-badge variant="light">{{ i.amount }}</b-badge>
</draggable> <b-badge variant="secondary">{{ i.unit.name }}</b-badge>
</div> <b-badge variant="info">{{ i.food.name }}</b-badge>
<div class="col col-md-8 col-12"> ({{ i.original_text }})
<b-input-group> <b-button @click="current_edit_ingredient = i" v-b-modal.ingredient_edit_modal class="float-right btn-sm"><i class="fas fa-pencil-alt"></i></b-button>
<b-textarea </b-list-group-item>
style="white-space: pre-wrap" v-model="s.instruction" </draggable>
max-rows="10"></b-textarea> </div>
<b-input-group-append> <div class="col col-md-8 col-12">
<b-button variant="secondary" @click="splitStep(s,'\n')"><i <b-input-group>
class="fas fa-expand-arrows-alt"></i></b-button> <b-textarea
<b-button variant="danger" style="white-space: pre-wrap" v-model="s.instruction"
@click="recipe_json.steps.splice(recipe_json.steps.findIndex(x => x === s),1)"> max-rows="10"></b-textarea>
<i class="fas fa-trash-alt"></i> <b-input-group-append>
</b-button> <b-button variant="secondary" @click="splitStep(s,'\n')"><i
class="fas fa-expand-arrows-alt"></i></b-button>
</b-input-group-append> <b-button variant="danger"
</b-input-group> @click="recipe_json.steps.splice(recipe_json.steps.findIndex(x => x === s),1)">
<i class="fas fa-trash-alt"></i>
<div class="text-center mt-1"> </b-button>
<b-button @click="mergeStep(s)" variant="primary"
v-if="index + 1 < recipe_json.steps.length"><i </b-input-group-append>
class="fas fa-compress-arrows-alt"></i> </b-input-group>
</b-button>
<div class="text-center mt-1">
<b-button variant="success" <b-button @click="mergeStep(s)" variant="primary"
@click="recipe_json.steps.splice(recipe_json.steps.findIndex(x => x === s) +1,0,{ingredients:[], instruction: ''})"> v-if="index + 1 < recipe_json.steps.length"><i
<i class="fas fa-plus"></i> class="fas fa-compress-arrows-alt"></i>
</b-button> </b-button>
<b-button variant="success"
@click="recipe_json.steps.splice(recipe_json.steps.findIndex(x => x === s) +1,0,{ingredients:[], instruction: ''})">
<i class="fas fa-plus"></i>
</b-button>
</div>
</div>
<b-modal id="ingredient_edit_modal" :title="$t('Edit')" ok-only>
<div v-if="current_edit_ingredient !== null">
<b-form-group v-bind:label="$t('Original_Text')" class="mb-3">
<b-form-input v-model="current_edit_ingredient.original_text" type="text" disabled></b-form-input>
</b-form-group>
<b-form-group v-bind:label="$t('Amount')" class="mb-3">
<b-form-input v-model="current_edit_ingredient.amount" type="number" ></b-form-input>
</b-form-group>
<b-form-group v-bind:label="$t('Unit')" class="mb-3">
<b-form-input v-model="current_edit_ingredient.unit.name" type="text" ></b-form-input>
</b-form-group>
<b-form-group v-bind:label="$t('Food')" class="mb-3">
<b-form-input v-model="current_edit_ingredient.food.name" type="text" ></b-form-input>
</b-form-group>
</div>
</b-modal>
</div> </div>
</div>
</div> </div>
</div>
</template> </template>
<script> <script>
@@ -67,116 +93,117 @@ import draggable from "vuedraggable";
import stringSimilarity from "string-similarity" import stringSimilarity from "string-similarity"
export default { export default {
name: "ImportViewStepEditor", name: "ImportViewStepEditor",
components: { components: {
draggable draggable
},
props: {
recipe: undefined
},
data() {
return {
recipe_json: undefined
}
},
watch: {
recipe_json: function () {
this.$emit('change', this.recipe_json)
}, },
}, props: {
mounted() { recipe: undefined
this.recipe_json = this.recipe },
}, data() {
methods: { return {
/** recipe_json: undefined,
* utility function used by splitAllSteps and splitStep to split a single step object into multiple step objects current_edit_ingredient: null,
* @param step: single step
* @param split_character: character to split steps at
* @return array of step objects
*/
splitStepObject: function (step, split_character) {
let steps = []
step.instruction.split(split_character).forEach(part => {
if (part.trim() !== '') {
steps.push({'instruction': part, '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
return steps
}, },
/** watch: {
* Splits all steps of a given recipe_json at the split character (e.g. \n or \n\n) recipe_json: function () {
* @param split_character: character to split steps at this.$emit('change', this.recipe_json)
*/ },
splitAllSteps: function (split_character) {
let steps = []
this.recipe_json.steps.forEach(step => {
steps = steps.concat(this.splitStepObject(step, split_character))
})
this.recipe_json.steps = steps
}, },
/** mounted() {
* Splits the given step at the split character (e.g. \n or \n\n) this.recipe_json = this.recipe
* @param step: step ingredients to split
* @param split_character: character to split steps at
*/
splitStep: function (step, split_character) {
let old_index = this.recipe_json.steps.findIndex(x => x === step)
let new_steps = this.splitStepObject(step, split_character)
this.recipe_json.steps.splice(old_index, 1, ...new_steps)
}, },
/** methods: {
* Merge all steps of a given recipe_json into one /**
*/ * utility function used by splitAllSteps and splitStep to split a single step object into multiple step objects
mergeAllSteps: function () { * @param step: single step
let step = {'instruction': '', 'ingredients': []} * @param split_character: character to split steps at
this.recipe_json.steps.forEach(s => { * @return array of step objects
step.instruction += s.instruction + '\n' */
step.ingredients = step.ingredients.concat(s.ingredients) splitStepObject: function (step, split_character) {
}) let steps = []
this.recipe_json.steps = [step] step.instruction.split(split_character).forEach(part => {
}, if (part.trim() !== '') {
/** steps.push({'instruction': part, 'ingredients': []})
* Merge two steps (the given and next one) }
*/ })
mergeStep: function (step) { steps[0].ingredients = step.ingredients // put all ingredients from the original step in the ingredients of the first step of the split step list
let step_index = this.recipe_json.steps.findIndex(x => x === step) return steps
let removed_steps = this.recipe_json.steps.splice(step_index, 2) },
/**
* Splits all steps of a given recipe_json at the split character (e.g. \n or \n\n)
* @param split_character: character to split steps at
*/
splitAllSteps: function (split_character) {
let steps = []
this.recipe_json.steps.forEach(step => {
steps = steps.concat(this.splitStepObject(step, split_character))
})
this.recipe_json.steps = steps
},
/**
* Splits the given step at the split character (e.g. \n or \n\n)
* @param step: step ingredients to split
* @param split_character: character to split steps at
*/
splitStep: function (step, split_character) {
let old_index = this.recipe_json.steps.findIndex(x => x === step)
let new_steps = this.splitStepObject(step, split_character)
this.recipe_json.steps.splice(old_index, 1, ...new_steps)
},
/**
* Merge all steps of a given recipe_json into one
*/
mergeAllSteps: function () {
let step = {'instruction': '', 'ingredients': []}
this.recipe_json.steps.forEach(s => {
step.instruction += s.instruction + '\n'
step.ingredients = step.ingredients.concat(s.ingredients)
})
this.recipe_json.steps = [step]
},
/**
* Merge two steps (the given and next one)
*/
mergeStep: function (step) {
let step_index = this.recipe_json.steps.findIndex(x => x === step)
let removed_steps = this.recipe_json.steps.splice(step_index, 2)
this.recipe_json.steps.splice(step_index, 0, { this.recipe_json.steps.splice(step_index, 0, {
'instruction': removed_steps.flatMap(x => x.instruction).join('\n'), 'instruction': removed_steps.flatMap(x => x.instruction).join('\n'),
'ingredients': removed_steps.flatMap(x => x.ingredients) 'ingredients': removed_steps.flatMap(x => x.ingredients)
}) })
}, },
/** /**
* automatically assign ingredients to steps based on text matching * automatically assign ingredients to steps based on text matching
*/ */
autoSortIngredients: function () { autoSortIngredients: function () {
let ingredients = this.recipe_json.steps.flatMap(s => s.ingredients) let ingredients = this.recipe_json.steps.flatMap(s => s.ingredients)
this.recipe_json.steps.forEach(s => s.ingredients = []) this.recipe_json.steps.forEach(s => s.ingredients = [])
ingredients.forEach(i => { ingredients.forEach(i => {
let found = false let found = false
this.recipe_json.steps.forEach(s => { this.recipe_json.steps.forEach(s => {
if (s.instruction.includes(i.food.name.trim()) && !found) { if (s.instruction.includes(i.food.name.trim()) && !found) {
found = true found = true
s.ingredients.push(i) s.ingredients.push(i)
} }
}) })
if (!found) { if (!found) {
let best_match = {rating: 0, step: this.recipe_json.steps[0]} let best_match = {rating: 0, step: this.recipe_json.steps[0]}
this.recipe_json.steps.forEach(s => { this.recipe_json.steps.forEach(s => {
let match = stringSimilarity.findBestMatch(i.food.name.trim(), s.instruction.split(' ')) let match = stringSimilarity.findBestMatch(i.food.name.trim(), s.instruction.split(' '))
if (match.bestMatch.rating > best_match.rating) { if (match.bestMatch.rating > best_match.rating) {
best_match = {rating: match.bestMatch.rating, step: s} best_match = {rating: match.bestMatch.rating, step: s}
} }
}) })
best_match.step.ingredients.push(i) best_match.step.ingredients.push(i)
found = true found = true
}
})
} }
})
} }
}
} }
</script> </script>

View File

@@ -65,6 +65,7 @@
"Step_Type": "Step Type", "Step_Type": "Step Type",
"Make_Header": "Make Header", "Make_Header": "Make Header",
"Make_Ingredient": "Make Ingredient", "Make_Ingredient": "Make Ingredient",
"Amount": "Amount",
"Enable_Amount": "Enable Amount", "Enable_Amount": "Enable Amount",
"Disable_Amount": "Disable Amount", "Disable_Amount": "Disable Amount",
"Ingredient Editor": "Ingredient Editor", "Ingredient Editor": "Ingredient Editor",
@@ -160,6 +161,7 @@
"merge_title": "Merge {type}", "merge_title": "Merge {type}",
"move_title": "Move {type}", "move_title": "Move {type}",
"Food": "Food", "Food": "Food",
"Original_Text": "Original Text",
"Recipe_Book": "Recipe Book", "Recipe_Book": "Recipe Book",
"del_confirmation_tree": "Are you sure that you want to delete {source} and all of it's children?", "del_confirmation_tree": "Are you sure that you want to delete {source} and all of it's children?",
"delete_title": "Delete {type}", "delete_title": "Delete {type}",