Basic editing of users ingredients

This commit is contained in:
jableader 2024-05-12 16:32:28 +10:00
parent a4a4497dc4
commit 6a7dfbe460
3 changed files with 105 additions and 13 deletions

View file

@ -0,0 +1,56 @@
<template>
<div>
<ul>
<li v-for="ingredient in ingredients" :key="ingredient.id">
<div v-if="editing">
<ingredient-line :ingredient="ingredient" @update="updateIngredient" @update-ingredient="updateIngredient" @update-product-link="updateProduct" />
<button @click="$emit('on-delete', ingredient)">
<img class="icon" :src="require('@/assets/trash.svg')" />
</button>
</div>
<div v-else>
<compact-parsed-ingredient :ingredient="ingredient" />
</div>
</li>
</ul>
<button v-if="editing" @click="$emit('on-add')">Add Ingredient</button>
<button @click="editing = !editing">{{ editing ? 'Done' : 'Edit' }}</button>
</div>
</template>
<style>
.icon {
width: 1em;
height: 1em;
}
</style>
<script>
import data from '@/data.js'
import IngredientLine from './IngredientLine.vue'
import CompactParsedIngredient from './CompactParsedIngredient.vue'
export default {
name: 'EditableIngredientsPanel',
components: { IngredientLine, CompactParsedIngredient },
props: ['ingredients', 'editOnly'],
data() {
return {
editing: this.editOnly ?? false
}
},
methods: {
async updateProduct(ingredient, product_link) {
const product = await data.parseProduct(ingredient, product_link);
this.$emit('on-update-ingredient', ingredient, { ...ingredient, product });
},
async updateIngredient(ingredient, line) {
const newIngredients = await data.parseIngredients([line]);
this.$emit('on-update-ingredient', ingredient, newIngredients[0]);
},
}
}
</script>

View file

@ -3,7 +3,11 @@
<h1>Shopping List</h1> <h1>Shopping List</h1>
<h4>Included Meals</h4> <h4>Included Meals</h4>
<meal-selection-list @meal-selected="mealSelected" @meal-unselected="mealUnselected" /> <meal-selection-list @meal-selected="mealSelected" @meal-unselected="mealUnselected" />
<ul> <h4>My Requests</h4>
<editable-ingredients-panel @on-add="addIngredient" @on-delete="deleteIngredient" @on-update-ingredient="updateIngredient" :ingredients="mySources" />
<h4>Full shopping list</h4>
<ul class="full-shopping-list">
<li v-for="item in shoppingList" :key="item.id"> <li v-for="item in shoppingList" :key="item.id">
<shopping-list-item :product="item.product" :sources="item.sources" /> <shopping-list-item :product="item.product" :sources="item.sources" />
</li> </li>
@ -38,7 +42,7 @@
<style scoped> <style scoped>
li { .full-shopping-list li {
list-style-type: none; list-style-type: none;
border: 1px solid #ccc; border: 1px solid #ccc;
@ -46,28 +50,33 @@ li {
margin-bottom: 1em; margin-bottom: 1em;
} }
ul { .full-shopping-list {
padding: 0; padding: 0;
} }
</style> </style>
<script> <script>
import data from '@/data.js'
import EditableIngredientsPanel from '@/components/ingredients/EditableIngredientsPanel.vue'
import ShoppingListItem from './ShoppingListItem.vue' import ShoppingListItem from './ShoppingListItem.vue'
import MealSelectionList from './MealSelectionList.vue' import MealSelectionList from './MealSelectionList.vue'
import { mealToShoppingListSources, removeMealFromShoppingListSources, groupByProduct } from './shopping.js' import { mealToShoppingListSources, removeMealFromShoppingListSources, groupByProduct } from './shopping.js'
export default { export default {
name: 'ShoppingPage', name: 'ShoppingPage',
components: { components: { ShoppingListItem, MealSelectionList, EditableIngredientsPanel },
ShoppingListItem, MealSelectionList
},
data() { data() {
return { shoppingList: [], sources: [] } return { shoppingList: [], sources: [], mySources: [] }
}, },
watch: { watch: {
sources() { async sources() {
this.shoppingList = groupByProduct(this.sources); this.shoppingList = groupByProduct(this.sources);
const person = await data.currentUser();
this.mySources = this.sources.filter(source => source.person?.id === person?.id).map(source => source.ingredient);
} }
}, },
methods: { methods: {
@ -76,6 +85,25 @@ export default {
}, },
mealUnselected(meal) { mealUnselected(meal) {
this.sources = removeMealFromShoppingListSources(this.sources, meal); this.sources = removeMealFromShoppingListSources(this.sources, meal);
},
async addIngredient() {
const person = await data.currentUser();
if (!person) return;
this.sources = this.sources.concat([{ ingredient: { id: 0 }, person }]);
},
async deleteIngredient(ingredient) {
const person = await data.currentUser();
if (!person) return;
const isMatch = source => source.ingredient === ingredient && source.person?.id === person.id;
this.sources = this.sources.filter(source => !isMatch(source));
},
async updateIngredient(oldIngredient, newIngredient) {
const person = await data.currentUser();
if (!person) return;
this.sources = this.sources.map(source => source.ingredient === oldIngredient ? { ingredient: newIngredient, person } : source);
} }
} }
} }

View file

@ -14,20 +14,28 @@ export function mealToShoppingListSources(meal) {
} }
export function removeMealFromShoppingListSources(sources, meal) { export function removeMealFromShoppingListSources(sources, meal) {
return sources.filter(source => source.meal.id !== meal.id); return sources.filter(source => source.meal?.id !== meal.id);
} }
export function groupByProduct(sources) { export function groupByProduct(sources) {
const noProductKey = "no-product";
const noProduct = { name: "No product", };
const grouped = {}; const grouped = {};
for (const source of sources) { for (const source of sources) {
if (!grouped[source.ingredient.product.id]) { const key = source.ingredient?.product?.id ?? noProductKey;
grouped[source.ingredient.product.id] = { if (!key) {
product: source.ingredient.product, continue;
}
if (!grouped[key]) {
grouped[key] = {
product: source.ingredient.product ?? noProduct,
sources: [], sources: [],
}; };
} }
grouped[source.ingredient.product.id].sources.push(source); grouped[key].sources.push(source);
} }
return Object.values(grouped); return Object.values(grouped);