Generalised ingredients editor

This commit is contained in:
jableader 2024-05-12 17:00:26 +10:00
parent 6b3a87ebc8
commit 1663f3788d
5 changed files with 26 additions and 157 deletions

View file

@ -1,6 +1,8 @@
<template> <template>
<div> <div>
<button v-if="editing" @click="$emit('on-add')">Add Ingredient</button>
<button @click="toggleEditing">{{ editing ? 'Finished Editing' : 'Edit' }}</button>
<ul> <ul>
<li v-for="ingredient in ingredients" :key="ingredient.id"> <li v-for="ingredient in ingredients" :key="ingredient.id">
<div v-if="editing"> <div v-if="editing">
@ -19,8 +21,6 @@
</div> </div>
</li> </li>
</ul> </ul>
<button v-if="editing" @click="$emit('on-add')">Add Ingredient</button>
<button @click="editing = !editing">{{ editing ? 'Done' : 'Edit' }}</button>
</div> </div>
</template> </template>
@ -81,6 +81,12 @@ export default {
const newIngredients = await data.parseIngredients([line]); const newIngredients = await data.parseIngredients([line]);
this.$emit('on-update-ingredient', ingredient, newIngredients[0]); this.$emit('on-update-ingredient', ingredient, newIngredients[0]);
}, },
toggleEditing() {
this.editing = !this.editing;
if (this.editing && !this.ingredients.length) {
this.$emit('on-add');
}
}
} }
} }

View file

@ -1,89 +0,0 @@
<template>
<div class="ingredient-add-box" :class="{ 'adding-ingredient': showIngredientLine}">
<p class="ingredient-line" v-if="showIngredientLine">
<ingredient-line ref="line" :ingredient="ingredient"
@update-ingredient="updateIngredient"
@update-product-link="updateProduct" />
</p>
<p>
<button v-if="showIngredientLine" @click="addIngredient(ingredient)">
<img :src="require('@/assets/chevron-up.svg')" /> <br>
Add ingredient
</button>
<button v-if="showIngredientLine" @click="showIngredientLine = false">
<img :src="require('@/assets/close.svg')" /> <br>
Cancel
</button>
<button v-else @click="showIngredientLine = true">
<img :src="require('@/assets/create.svg')" /> <br>
Add ingredient
</button>
</p>
</div>
</template>
<script>
import IngredientLine from './IngredientLine.vue';
import data from '@/data.js'
export default {
name: 'IngredientAddBox',
components: { IngredientLine },
data() {
return {
ingredient: {},
showIngredientLine: false,
}
},
methods: {
addIngredient() {
this.$emit('add-ingredient', this.ingredient);
this.ingredient = {};
this.showIngredientLine = false;
},
async updateProduct(ingredient, product_link) {
this.ingredient.product = await data.parseProduct(ingredient, product_link);
},
async updateIngredient(ingredient, line) {
const newIngredients = await data.parseIngredients([line]);
this.ingredient = newIngredients[0];
},
}
}
</script>
<style scoped>
.ingredient-add-box {
display: flex;
flex-direction: column;
margin: 1ex 2em;
padding: 1em;
}
.adding-ingredient {
border: solid 1px #ccc;
border-radius: 5px;
}
.ingredient-line {
flex: 1;
}
button {
border: 0;
background: none;
cursor: pointer;
padding: 2ex 1em;
}
button img {
width: 3em;
}
button:hover {
background-color: #ccc;
}
</style>

View file

@ -29,18 +29,7 @@
</div> </div>
<div class="ingredients"> <div class="ingredients">
<h2>Sides & Additional Ingredients</h2> <h2>Sides & Additional Ingredients</h2>
<ul v-if="meal.extra_ingredients && meal.extra_ingredients.length"> <editable-ingredients-panel :ingredients="meal.extra_ingredients" @on-add="addIngredient" @on-delete="deleteIngredient" @on-update-ingredient="updateIngredient" />
<li v-for="ingredient in meal.extra_ingredients" :key="ingredient" class="saved-ingredient">
<compact-parsed-ingredient :ingredient="ingredient" />
<button @click="deleteIngredient(ingredient)">
<img class="icon" :src="require('@/assets/trash.svg')" />
</button>
</li>
</ul>
<div v-else>
<p>Add some ingredients using the search box</p>
</div>
<ingredient-add-box @add-ingredient="addIngredient" />
</div> </div>
<button @click="saveMeal">Save</button> <button @click="saveMeal">Save</button>
</div> </div>
@ -50,14 +39,13 @@
import data from '@/data.js' import data from '@/data.js'
import RecipeSearchBox from '@/components/recipes/RecipeSearchBox.vue'; import RecipeSearchBox from '@/components/recipes/RecipeSearchBox.vue';
import RecipeCard from '@/components/recipes/RecipeCard.vue'; import RecipeCard from '@/components/recipes/RecipeCard.vue';
import CompactParsedIngredient from '@/components/ingredients/CompactParsedIngredient.vue';
import DatePicker from './DatePicker.vue'; import DatePicker from './DatePicker.vue';
import IngredientAddBox from '@/components/ingredients/IngredientAddBox.vue'; import EditableIngredientsPanel from '../ingredients/EditableIngredientsPanel.vue';
import PersonList from './PersonList.vue'; import PersonList from './PersonList.vue';
export default { export default {
props: ['id'], props: ['id'],
components: { RecipeSearchBox, DatePicker, RecipeCard, IngredientAddBox, CompactParsedIngredient, PersonList }, components: { RecipeSearchBox, DatePicker, RecipeCard, EditableIngredientsPanel, PersonList },
data() { data() {
return { return {
meal: { meal: {
@ -98,12 +86,15 @@ export default {
removeRecipe(recipe) { removeRecipe(recipe) {
this.meal.recipes = this.meal.recipes.filter(r => r.id !== recipe.id); this.meal.recipes = this.meal.recipes.filter(r => r.id !== recipe.id);
}, },
addIngredient(ingredient) { addIngredient() {
this.meal.extra_ingredients.push(ingredient); this.meal.extra_ingredients = [{ line: '', product: null }, ...this.meal.extra_ingredients];
}, },
deleteIngredient(ingredient) { deleteIngredient(ingredient) {
this.meal.extra_ingredients = this.meal.extra_ingredients.filter(i => i != ingredient); this.meal.extra_ingredients = this.meal.extra_ingredients.filter(i => i != ingredient);
}, },
updateIngredient(ingredient, newIngredient) {
this.meal.extra_ingredients = this.meal.extra_ingredients.map(i => i == ingredient ? newIngredient : i);
},
removePerson(list, person) { removePerson(list, person) {
this.meal[list] = this.meal[list].filter(p => p.id !== person.id); this.meal[list] = this.meal[list].filter(p => p.id !== person.id);
}, },

View file

@ -15,19 +15,12 @@
<h1><input class="recipe-name" type="text" v-model="recipe.name" /></h1> <h1><input class="recipe-name" type="text" v-model="recipe.name" /></h1>
<h3 class="recipe-link"><a :href="recipe.link">View Recipe</a></h3> <h3 class="recipe-link"><a :href="recipe.link">View Recipe</a></h3>
<h2>Ingredients</h2> <h2>Ingredients</h2>
<ul> <editable-ingredients-panel
<li v-for="ingredient in recipe.ingredients" :key="ingredient.line"> :ingredients="recipe.ingredients"
<p class="ingredient-line"> :edit-only="true"
<ingredient-line @on-add="addIngredient" @on-delete="deleteIngredient" @on-update-ingredient="updateIngredient"
:ingredient="ingredient" />
@update-ingredient="updateIngredient"
@update-product-link="updateProduct" />
</p>
<button @click="deleteIngredient(ingredient)">Delete</button>
</li>
</ul>
<button @click="addIngredient">Add Ingredient</button>
<div> <div>
<button v-if="recipe.id" class="delete-btn" @click="deleteRecipe">Delete</button> <button v-if="recipe.id" class="delete-btn" @click="deleteRecipe">Delete</button>
<button class="submit-btn" @click="saveRecipe">{{ recipe.id ? "Save" : "Create" }}</button> <button class="submit-btn" @click="saveRecipe">{{ recipe.id ? "Save" : "Create" }}</button>
@ -65,40 +58,18 @@ input.recipe-name {
text-decoration: none; text-decoration: none;
} }
ul {
padding: 0;
}
li {
list-style: none;
display: flex;
flex-direction: row;
justify-content: space-between;
border: 1px solid #ccc;
border-radius: 5px;
padding: 5px;
}
.ingredient-line {
flex: 1;
margin: 0;
margin-right: 1em;
}
</style> </style>
<script> <script>
import data from '@/data.js' import data from '@/data.js'
import IngredientLine from '@/components/ingredients/IngredientLine.vue' import EditableIngredientsPanel from '@/components/ingredients/EditableIngredientsPanel.vue'
export default { export default {
props: { props: {
id: { type: Number, optional: true } id: { type: Number, optional: true }
}, },
components: { components: { EditableIngredientsPanel },
IngredientLine
},
data() { data() {
return { return {
link: this.$route.query.url ?? "", link: this.$route.query.url ?? "",
@ -139,17 +110,7 @@ export default {
this.recipe = null; this.recipe = null;
} }
}, },
async updateProduct(ingredient, product_link) { async updateIngredient(ingredient, newIngredient) {
const product = await data.parseProduct(ingredient, product_link);
this.recipe.ingredients = this.recipe.ingredients.map(i => i.name == ingredient.name ? { ...i, product, product_id: product.id } : i);
},
async updateIngredient(ingredient, line) {
const newIngredients = await data.parseIngredients([line]);
const newIngredient = newIngredients[0];
if (!newIngredient?.product) {
newIngredient.product = ingredient.product;
}
this.recipe.ingredients = this.recipe.ingredients.map(i => i == ingredient ? newIngredient : i); this.recipe.ingredients = this.recipe.ingredients.map(i => i == ingredient ? newIngredient : i);
}, },
deleteIngredient(ingredient) { deleteIngredient(ingredient) {
@ -174,7 +135,7 @@ export default {
} }
}, },
addIngredient() { addIngredient() {
this.recipe.ingredients.push({ line: '', product: null }); this.recipe.ingredients = [{ line: '', product: null }, ...this.recipe.ingredients];
}, },
async deleteRecipe() { async deleteRecipe() {
if (confirm('Are you sure you want to delete this recipe?')) { if (confirm('Are you sure you want to delete this recipe?')) {

View file

@ -90,7 +90,7 @@ export default {
const person = await data.currentUser(); const person = await data.currentUser();
if (!person) return; if (!person) return;
this.sources = this.sources.concat([{ ingredient: { id: 0 }, person }]); this.sources = [{ ingredient: { id: 0 }, person }, ...this.sources];
}, },
async deleteIngredient(ingredient) { async deleteIngredient(ingredient) {
const person = await data.currentUser(); const person = await data.currentUser();