partial shopping list saving

This commit is contained in:
vabene1111
2020-09-07 13:09:03 +02:00
parent 8239dc3604
commit 0ff65d35dc
5 changed files with 158 additions and 4 deletions

View File

@ -205,7 +205,8 @@ class ShoppingListRecipeSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = ShoppingListRecipe model = ShoppingListRecipe
fields = ('recipe', 'multiplier') fields = ('id', 'recipe', 'multiplier')
read_only_fields = ('id',)
class ShoppingListEntrySerializer(serializers.ModelSerializer): class ShoppingListEntrySerializer(serializers.ModelSerializer):

View File

@ -51,6 +51,56 @@
</div> </div>
</div> </div>
<div class="row">
<div class="col col-md-12">
<input class="form-control form-control-sm" type="number" placeholder="{% trans 'Amount' %}"
v-model="new_entry.amount" ref="new_entry_amount">
<multiselect
v-tabindex
ref="unit"
v-model="new_entry.unit"
:options="units"
:close-on-select="true"
:clear-on-select="true"
:allow-empty="true"
:preserve-search="true"
placeholder="{% trans 'Select Unit' %}"
tag-placeholder="{% trans 'Create' %}"
select-label="{% trans 'Select' %}"
:taggable="true"
@tag="addUnitType"
label="name"
track-by="name"
:multiple="false"
:loading="units_loading"
@search-change="searchUnits">
</multiselect>
<multiselect
v-tabindex
ref="food"
v-model="new_entry.food"
:options="foods"
:close-on-select="true"
:clear-on-select="true"
:allow-empty="true"
:preserve-search="true"
placeholder="{% trans 'Select Food' %}"
tag-placeholder="{% trans 'Create' %}"
select-label="{% trans 'Select' %}"
:taggable="true"
@tag="addFoodType"
label="name"
track-by="name"
:multiple="false"
:loading="foods_loading"
@search-change="searchFoods">
</multiselect>
<button class="btn btn-success btn-sm" @click="addEntry()"><i class="fa fa-plus"></i>
</button>
</div>
</div>
<div class="row" style="margin-top: 8px"> <div class="row" style="margin-top: 8px">
<div class="col col-12"> <div class="col col-12">
<table class="table table-sm table-striped"> <table class="table table-sm table-striped">
@ -66,6 +116,8 @@
<div v-else> <div v-else>
Non Edit Non Edit
</div> </div>
<button @click="updateShoppingList()">Save</button>
</template> </template>
@ -89,6 +141,15 @@
recipes: [], recipes: [],
shopping_list: undefined, shopping_list: undefined,
multiplier_cache: {}, multiplier_cache: {},
new_entry: {
unit: undefined,
amount: undefined,
food: undefined,
},
foods: [],
foods_loading: false,
units: [],
units_loading: false,
}, },
directives: { directives: {
tabindex: { tabindex: {
@ -134,6 +195,57 @@
} }
}, },
*/ */
makeToast: function (title, message, variant = null) {
//TODO remove duplicate function in favor of central one
this.$bvToast.toast(message, {
title: title,
variant: variant,
toaster: 'b-toaster-top-center',
solid: true
})
},
updateShoppingList: function () {
for (let r of this.shopping_list.recipes.filter(item => item.created === true)) {
let old_id = r.id
console.log('updating recipe', r)
this.$http.post("{% url 'api:shoppinglistrecipe-list' %}", r, {}).then((response) => {
r = response
for (let e of this.shopping_list.entries.filter(item => item.list_recipe === old_id)) {
e.list_recipe = r.id
}
}).catch((err) => {
console.log(err)
this.makeToast('{% trans 'Error' %}', '{% trans 'There was an error updating a resource!' %}' + err.bodyText, 'danger')
})
}
this.$http.put("{% url 'api:shoppinglist-detail' shopping_list_id %}", this.shopping_list, {}).then((response) => {
console.log(response)
this.makeToast('{% trans 'Updated' %}', '{% trans 'Changes saved successfully!' %}', 'success')
}).catch((err) => {
console.log(err)
this.makeToast('{% trans 'Error' %}', '{% trans 'There was an error updating a resource!' %}' + err.bodyText, 'danger')
})
},
addEntry: function () {
this.shopping_list.entries.push({
'list_recipe': null,
'food': this.new_entry.food,
'unit': ((this.new_entry.unit !== undefined) ? this.new_entry.unit : {'name': ''}),
'amount': parseFloat(this.new_entry.amount),
'order': 0
})
this.new_entry = {
unit: undefined,
amount: undefined,
food: undefined,
}
this.$refs.new_entry_amount.focus();
this.updateDisplay()
},
getRecipes: function () { getRecipes: function () {
let url = "{% url 'api:recipe-list' %}?limit=5" let url = "{% url 'api:recipe-list' %}?limit=5"
if (this.recipe_query !== '') { if (this.recipe_query !== '') {
@ -151,6 +263,7 @@
console.log(this.shopping_list) console.log(this.shopping_list)
let slr = { let slr = {
"created": true,
"id": Math.random() * 1000, "id": Math.random() * 1000,
"recipe": recipe, "recipe": recipe,
"multiplier": 1 "multiplier": 1
@ -188,7 +301,9 @@
this.shopping_list.entries.forEach(function (element) { this.shopping_list.entries.forEach(function (element) {
let item = {} let item = {}
Object.assign(item, element); Object.assign(item, element);
item.amount = item.amount * app.multiplier_cache[item.list_recipe] if (item.list_recipe !== null) {
item.amount = item.amount * app.multiplier_cache[item.list_recipe]
}
app.shopping_list.entries_display.push(item) app.shopping_list.entries_display.push(item)
}); });
}, },
@ -203,6 +318,35 @@
}) })
}, },
searchUnits: function (query) { //TODO move to central component
this.units_loading = true
this.$http.get("{% url 'api:unit-list' %}" + '?query=' + query + '&limit=10').then((response) => {
this.units = response.data;
this.units_loading = false
}).catch((err) => {
this.makeToast('{% trans 'Error' %}', '{% trans 'There was an error loading a resource!' %}' + err.bodyText, 'danger')
})
},
searchFoods: function (query) { //TODO move to central component
this.foods_loading = true
this.$http.get("{% url 'api:food-list' %}" + '?query=' + query + '&limit=10').then((response) => {
this.foods = response.data
this.foods_loading = false
}).catch((err) => {
this.makeToast('{% trans 'Error' %}', '{% trans 'There was an error loading a resource!' %}' + err.bodyText, 'danger')
})
},
addFoodType: function (tag, index) { //TODO move to central component
let new_food = {'name': tag}
this.foods.push(new_food)
this.new_entry.food = new_food
},
addUnitType: function (tag, index) { //TODO move to central component
let new_unit = {'name': tag}
this.units.push(new_unit)
this.new_entry.unit = new_unit
},
} }
}); });
</script> </script>

View File

@ -24,6 +24,7 @@ router.register(r'ingredient', api.IngredientViewSet)
router.register(r'meal-plan', api.MealPlanViewSet) router.register(r'meal-plan', api.MealPlanViewSet)
router.register(r'meal-type', api.MealTypeViewSet) router.register(r'meal-type', api.MealTypeViewSet)
router.register(r'shopping-list', api.ShoppingListViewSet) router.register(r'shopping-list', api.ShoppingListViewSet)
router.register(r'shopping-list-recipe', api.ShoppingListRecipeViewSet)
router.register(r'view-log', api.ViewLogViewSet) router.register(r'view-log', api.ViewLogViewSet)
urlpatterns = [ urlpatterns = [

View File

@ -32,7 +32,7 @@ from cookbook.models import Recipe, Sync, Storage, CookLog, MealPlan, MealType,
from cookbook.provider.dropbox import Dropbox from cookbook.provider.dropbox import Dropbox
from cookbook.provider.nextcloud import Nextcloud from cookbook.provider.nextcloud import Nextcloud
from cookbook.serializer import MealPlanSerializer, MealTypeSerializer, RecipeSerializer, ViewLogSerializer, UserNameSerializer, UserPreferenceSerializer, RecipeBookSerializer, IngredientSerializer, FoodSerializer, StepSerializer, \ from cookbook.serializer import MealPlanSerializer, MealTypeSerializer, RecipeSerializer, ViewLogSerializer, UserNameSerializer, UserPreferenceSerializer, RecipeBookSerializer, IngredientSerializer, FoodSerializer, StepSerializer, \
KeywordSerializer, RecipeImageSerializer, StorageSerializer, SyncSerializer, SyncLogSerializer, UnitSerializer, ShoppingListSerializer KeywordSerializer, RecipeImageSerializer, StorageSerializer, SyncSerializer, SyncLogSerializer, UnitSerializer, ShoppingListSerializer, ShoppingListRecipeSerializer
class UserNameViewSet(viewsets.ReadOnlyModelViewSet): class UserNameViewSet(viewsets.ReadOnlyModelViewSet):
@ -233,6 +233,14 @@ class RecipeViewSet(viewsets.ModelViewSet, StandardFilterMixin):
return Response(serializer.errors, 400) return Response(serializer.errors, 400)
class ShoppingListRecipeViewSet(viewsets.ModelViewSet):
queryset = ShoppingListRecipe.objects.all()
serializer_class = ShoppingListRecipeSerializer
permission_classes = [CustomIsUser] # TODO add custom validation
# TODO custom get qs
class ShoppingListViewSet(viewsets.ModelViewSet): class ShoppingListViewSet(viewsets.ModelViewSet):
queryset = ShoppingList.objects.all() queryset = ShoppingList.objects.all()
serializer_class = ShoppingListSerializer serializer_class = ShoppingListSerializer

View File

@ -165,7 +165,7 @@ def meal_plan_entry(request, pk):
@group_required('user') @group_required('user')
def shopping_list(request, pk=None): def shopping_list(request, pk=None):
return render(request, 'shopping_list.html', {}) return render(request, 'shopping_list.html', {'shopping_list_id': pk})
@group_required('guest') @group_required('guest')