mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-09 08:08:24 -05:00
added simple edit modal to recipe import
This commit is contained in:
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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}",
|
||||||
|
|||||||
Reference in New Issue
Block a user