shopping list item checking
This commit is contained in:
@ -14,16 +14,17 @@
|
|||||||
|
|
||||||
<script src="{% static 'js/Sortable.min.js' %}"></script>
|
<script src="{% static 'js/Sortable.min.js' %}"></script>
|
||||||
<script src="{% static 'js/vuedraggable.umd.min.js' %}"></script>
|
<script src="{% static 'js/vuedraggable.umd.min.js' %}"></script>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="{% static 'css/pretty-checkbox.min.css' %}">
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col col-9">
|
<div class="col col-md-9">
|
||||||
<h2>{% trans 'Shopping List' %}</h2>
|
<h2>{% trans 'Shopping List' %}</h2>
|
||||||
</div>
|
</div>
|
||||||
<!--TODO proper alignment -->
|
<div class="col col-mdd-3 text-right">
|
||||||
<div class="col col-3 text-right">
|
|
||||||
<b-form-checkbox switch size="lg" v-model="edit_mode">{% trans 'Edit' %}</b-form-checkbox>
|
<b-form-checkbox switch size="lg" v-model="edit_mode">{% trans 'Edit' %}</b-form-checkbox>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -47,7 +48,10 @@
|
|||||||
<ul class="list-group" style="margin-top: 8px">
|
<ul class="list-group" style="margin-top: 8px">
|
||||||
<li class="list-group-item" v-for="x in recipes">
|
<li class="list-group-item" v-for="x in recipes">
|
||||||
<div class="row flex-row" style="padding-left: 0.5vw; padding-right: 0.5vw">
|
<div class="row flex-row" style="padding-left: 0.5vw; padding-right: 0.5vw">
|
||||||
<div class="flex-column flex-fill my-auto">[[x.name]]</div>
|
<div class="flex-column flex-fill my-auto"><a v-bind:href="getRecipeUrl(x.id)"
|
||||||
|
target="_blank"
|
||||||
|
rel="nofollow norefferer">[[x.name]]</a>
|
||||||
|
</div>
|
||||||
<div class="flex-column align-self-end">
|
<div class="flex-column align-self-end">
|
||||||
<button class="btn btn-outline-primary shadow-none"
|
<button class="btn btn-outline-primary shadow-none"
|
||||||
@click="addRecipeToList(x)"><i
|
@click="addRecipeToList(x)"><i
|
||||||
@ -72,7 +76,10 @@
|
|||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="row flex-row my-auto" v-for="x in shopping_list.recipes"
|
<div class="row flex-row my-auto" v-for="x in shopping_list.recipes"
|
||||||
style="margin-top: 0.5vh!important;">
|
style="margin-top: 0.5vh!important;">
|
||||||
<div class="flex-grow-1 flex-column my-auto">[[x.recipe_name]]</div>
|
<div class="flex-grow-1 flex-column my-auto"><a v-bind:href="getRecipeUrl(x.recipe)"
|
||||||
|
target="_blank"
|
||||||
|
rel="nofollow norefferer">[[x.recipe_name]]</a>
|
||||||
|
</div>
|
||||||
<div class="flex-column align-self-end ">
|
<div class="flex-column align-self-end ">
|
||||||
<div class="input-group input-group-sm my-auto">
|
<div class="input-group input-group-sm my-auto">
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
@ -159,12 +166,12 @@
|
|||||||
@search-change="searchFoods">
|
@search-change="searchFoods">
|
||||||
</multiselect>
|
</multiselect>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col col-md-1 my-auto text-right">
|
<div class="col col-md-1 my-auto text-right">
|
||||||
<button class="btn btn-success btn-lg" @click="addEntry()"><i class="fa fa-plus"></i>
|
<button class="btn btn-success btn-lg" @click="addEntry()"><i class="fa fa-plus"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row" style="margin-top: 4vh">
|
<div class="row" style="margin-top: 4vh">
|
||||||
<div class="col col-12 text-right">
|
<div class="col col-12 text-right">
|
||||||
@ -174,8 +181,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else="">
|
<div v-else>
|
||||||
Non Edit
|
|
||||||
|
<div class="row" style="margin-top: 8px">
|
||||||
|
<div class="col col-md-12">
|
||||||
|
<table class="table table table-striped">
|
||||||
|
<tr v-for="x in display_entries">
|
||||||
|
<td><input type="checkbox" style="zoom:1.4;" v-model="x.checked" @change="entryChecked(x)">
|
||||||
|
</td>
|
||||||
|
<td>[[x.amount]]</td>
|
||||||
|
<td>[[x.unit.name]]</td>
|
||||||
|
<td>[[x.food.name]]</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@ -199,7 +219,7 @@
|
|||||||
data: {
|
data: {
|
||||||
shopping_list_id: {% if shopping_list_id %}{{ shopping_list_id }}{% else %}null{% endif %},
|
shopping_list_id: {% if shopping_list_id %}{{ shopping_list_id }}{% else %}null{% endif %},
|
||||||
loading: true,
|
loading: true,
|
||||||
edit_mode: true,
|
edit_mode: false,
|
||||||
recipe_query: '',
|
recipe_query: '',
|
||||||
recipes: [],
|
recipes: [],
|
||||||
shopping_list: undefined,
|
shopping_list: undefined,
|
||||||
@ -353,6 +373,26 @@
|
|||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
entryChecked: function (entry) {
|
||||||
|
|
||||||
|
|
||||||
|
this.shopping_list.entries.forEach((item) => {
|
||||||
|
if (item.id === entry.id) { //TODO unwrap once same entries are merged
|
||||||
|
item.checked = entry.checked
|
||||||
|
console.log('updating ', item)
|
||||||
|
this.$http.put("{% url 'api:shoppinglistentry-detail' 123456 %}".replace('123456', item.id), item, {}).then((response) => {
|
||||||
|
console.log("YEHAA", response)
|
||||||
|
}).catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
this.makeToast('{% trans 'Error' %}', '{% trans 'There was an error updating a resource!' %}' + err.bodyText, 'danger')
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
addEntry: function () {
|
addEntry: function () {
|
||||||
this.shopping_list.entries.push({
|
this.shopping_list.entries.push({
|
||||||
@ -360,7 +400,8 @@
|
|||||||
'food': this.new_entry.food,
|
'food': this.new_entry.food,
|
||||||
'unit': ((this.new_entry.unit !== undefined) ? this.new_entry.unit : {'name': ''}),
|
'unit': ((this.new_entry.unit !== undefined) ? this.new_entry.unit : {'name': ''}),
|
||||||
'amount': parseFloat(this.new_entry.amount),
|
'amount': parseFloat(this.new_entry.amount),
|
||||||
'order': 0
|
'order': 0,
|
||||||
|
'checked': false
|
||||||
})
|
})
|
||||||
|
|
||||||
this.new_entry = {
|
this.new_entry = {
|
||||||
@ -373,9 +414,12 @@
|
|||||||
|
|
||||||
},
|
},
|
||||||
getRecipes: function () {
|
getRecipes: function () {
|
||||||
let url = "{% url 'api:recipe-list' %}?limit=5"
|
let url = "{% url 'api:recipe-list' %}?limit=5&internal=true"
|
||||||
if (this.recipe_query !== '') {
|
if (this.recipe_query !== '') {
|
||||||
url += '&query=' + this.recipe_query;
|
url += '&query=' + this.recipe_query;
|
||||||
|
} else {
|
||||||
|
this.recipes = []
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$http.get(url).then((response) => {
|
this.$http.get(url).then((response) => {
|
||||||
@ -385,6 +429,9 @@
|
|||||||
this.makeToast('{% trans 'Error' %}', '{% trans 'There was an error loading a resource!' %}' + err.bodyText, 'danger')
|
this.makeToast('{% trans 'Error' %}', '{% trans 'There was an error loading a resource!' %}' + err.bodyText, 'danger')
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
getRecipeUrl: function (id) { //TODO generic function that can be reused else were
|
||||||
|
return '{% url 'view_recipe' 123456 %}'.replace('123456', id)
|
||||||
|
},
|
||||||
addRecipeToList: function (recipe) {
|
addRecipeToList: function (recipe) {
|
||||||
console.log(this.shopping_list)
|
console.log(this.shopping_list)
|
||||||
|
|
||||||
|
@ -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-entry', api.ShoppingListEntryViewSet)
|
||||||
router.register(r'shopping-list-recipe', api.ShoppingListRecipeViewSet)
|
router.register(r'shopping-list-recipe', api.ShoppingListRecipeViewSet)
|
||||||
router.register(r'view-log', api.ViewLogViewSet)
|
router.register(r'view-log', api.ViewLogViewSet)
|
||||||
|
|
||||||
|
@ -28,11 +28,11 @@ from rest_framework.viewsets import ViewSetMixin
|
|||||||
|
|
||||||
from cookbook.helper.permission_helper import group_required, CustomIsOwner, CustomIsAdmin, CustomIsUser, CustomIsGuest, CustomIsShare
|
from cookbook.helper.permission_helper import group_required, CustomIsOwner, CustomIsAdmin, CustomIsUser, CustomIsGuest, CustomIsShare
|
||||||
from cookbook.helper.recipe_url_import import get_from_html
|
from cookbook.helper.recipe_url_import import get_from_html
|
||||||
from cookbook.models import Recipe, Sync, Storage, CookLog, MealPlan, MealType, ViewLog, UserPreference, RecipeBook, Ingredient, Food, Step, Keyword, Unit, SyncLog, ShoppingListRecipe, ShoppingList
|
from cookbook.models import Recipe, Sync, Storage, CookLog, MealPlan, MealType, ViewLog, UserPreference, RecipeBook, Ingredient, Food, Step, Keyword, Unit, SyncLog, ShoppingListRecipe, ShoppingList, ShoppingListEntry
|
||||||
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, ShoppingListRecipeSerializer
|
KeywordSerializer, RecipeImageSerializer, StorageSerializer, SyncSerializer, SyncLogSerializer, UnitSerializer, ShoppingListSerializer, ShoppingListRecipeSerializer, ShoppingListEntrySerializer
|
||||||
|
|
||||||
|
|
||||||
class UserNameViewSet(viewsets.ReadOnlyModelViewSet):
|
class UserNameViewSet(viewsets.ReadOnlyModelViewSet):
|
||||||
@ -203,6 +203,13 @@ class RecipeViewSet(viewsets.ModelViewSet, StandardFilterMixin):
|
|||||||
serializer_class = RecipeSerializer
|
serializer_class = RecipeSerializer
|
||||||
permission_classes = [CustomIsShare | CustomIsGuest] # TODO split read and write permission for meal plan guest
|
permission_classes = [CustomIsShare | CustomIsGuest] # TODO split read and write permission for meal plan guest
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
internal = self.request.query_params.get('internal', None)
|
||||||
|
if internal:
|
||||||
|
self.queryset = self.queryset.filter(internal=True)
|
||||||
|
|
||||||
|
return super(RecipeViewSet, self).get_queryset()
|
||||||
|
|
||||||
# TODO write extensive tests for permissions
|
# TODO write extensive tests for permissions
|
||||||
|
|
||||||
@decorators.action(
|
@decorators.action(
|
||||||
@ -241,6 +248,14 @@ class ShoppingListRecipeViewSet(viewsets.ModelViewSet):
|
|||||||
# TODO custom get qs
|
# TODO custom get qs
|
||||||
|
|
||||||
|
|
||||||
|
class ShoppingListEntryViewSet(viewsets.ModelViewSet):
|
||||||
|
queryset = ShoppingListEntry.objects.all()
|
||||||
|
serializer_class = ShoppingListEntrySerializer
|
||||||
|
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
|
||||||
|
Reference in New Issue
Block a user