Merge pull request #1512 from smilerz/additional_fixes

various fixes
This commit is contained in:
vabene1111 2022-02-11 09:01:56 +01:00 committed by GitHub
commit 6ae1365505
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 201 additions and 248 deletions

View File

@ -4,7 +4,7 @@ from datetime import timedelta
from django.contrib.postgres.search import SearchQuery, SearchRank, TrigramSimilarity
from django.core.cache import caches
from django.db.models import Avg, Case, Count, F, Func, Max, OuterRef, Q, Subquery, Sum, Value, When
from django.db.models.functions import Coalesce, Substr
from django.db.models.functions import Coalesce, Lower, Substr
from django.utils import timezone, translation
from django.utils.translation import gettext as _
@ -666,9 +666,9 @@ class RecipeFacet():
if not self._request.space.demo and self._request.space.show_facet_count:
return queryset.annotate(count=Coalesce(Subquery(self._recipe_count_queryset('keywords', depth, steplen)), 0)
).filter(depth=depth, count__gt=0
).values('id', 'name', 'count', 'numchild').order_by('name')[:200]
).values('id', 'name', 'count', 'numchild').order_by(Lower('name').asc())[:200]
else:
return queryset.filter(depth=depth).values('id', 'name', 'numchild').order_by('name')
return queryset.filter(depth=depth).values('id', 'name', 'numchild').order_by(Lower('name').asc())
def _food_queryset(self, queryset, food=None):
depth = getattr(food, 'depth', 0) + 1
@ -677,9 +677,9 @@ class RecipeFacet():
if not self._request.space.demo and self._request.space.show_facet_count:
return queryset.annotate(count=Coalesce(Subquery(self._recipe_count_queryset('steps__ingredients__food', depth, steplen)), 0)
).filter(depth__lte=depth, count__gt=0
).values('id', 'name', 'count', 'numchild').order_by('name')[:200]
).values('id', 'name', 'count', 'numchild').order_by(Lower('name').asc())[:200]
else:
return queryset.filter(depth__lte=depth).values('id', 'name', 'numchild').order_by('name')
return queryset.filter(depth__lte=depth).values('id', 'name', 'numchild').order_by(Lower('name').asc())
# # TODO: This might be faster https://github.com/django-treebeard/django-treebeard/issues/115
@ -909,7 +909,7 @@ def old_search(request):
params = dict(request.GET)
params['internal'] = None
f = RecipeFilter(params,
queryset=Recipe.objects.filter(space=request.user.userpreference.space).all().order_by('name'),
queryset=Recipe.objects.filter(space=request.user.userpreference.space).all().order_by(Lower('name').asc()),
space=request.space)
return f.qs

View File

@ -65,9 +65,13 @@ class RecipeShoppingEditor():
except (ValueError, TypeError):
self.servings = getattr(self._shopping_list_recipe, 'servings', None) or getattr(self.mealplan, 'servings', None) or getattr(self.recipe, 'servings', None)
@property
def _recipe_servings(self):
return getattr(self.recipe, 'servings', None) or getattr(getattr(self.mealplan, 'recipe', None), 'servings', None) or getattr(getattr(self._shopping_list_recipe, 'recipe', None), 'servings', None)
@property
def _servings_factor(self):
return self.servings / self.recipe.servings
return Decimal(self.servings)/Decimal(self._recipe_servings)
@property
def _shared_users(self):

View File

@ -14,8 +14,8 @@ from rest_framework.fields import empty
from cookbook.helper.HelperFunctions import str2bool
from cookbook.helper.shopping_helper import RecipeShoppingEditor
from cookbook.models import (Automation, BookmarkletImport, Comment, CookLog, Food,
FoodInheritField, ImportLog, ExportLog, Ingredient, Keyword, MealPlan, MealType,
from cookbook.models import (Automation, BookmarkletImport, Comment, CookLog, ExportLog, Food,
FoodInheritField, ImportLog, Ingredient, Keyword, MealPlan, MealType,
NutritionInformation, Recipe, RecipeBook, RecipeBookEntry,
RecipeImport, ShareLink, ShoppingList, ShoppingListEntry,
ShoppingListRecipe, Step, Storage, Supermarket, SupermarketCategory,
@ -677,7 +677,6 @@ class MealPlanSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
read_only_fields = ('created_by',)
# TODO deprecate
class ShoppingListRecipeSerializer(serializers.ModelSerializer):
name = serializers.SerializerMethodField('get_name') # should this be done at the front end?
recipe_name = serializers.ReadOnlyField(source='recipe.name')
@ -689,11 +688,11 @@ class ShoppingListRecipeSerializer(serializers.ModelSerializer):
value = Decimal(value)
value = value.quantize(Decimal(1)) if value == value.to_integral() else value.normalize() # strips trailing zero
return (
obj.name
or getattr(obj.mealplan, 'title', None)
or (d := getattr(obj.mealplan, 'date', None)) and ': '.join([obj.mealplan.recipe.name, str(d)])
or obj.recipe.name
) + f' ({value:.2g})'
obj.name
or getattr(obj.mealplan, 'title', None)
or (d := getattr(obj.mealplan, 'date', None)) and ': '.join([obj.mealplan.recipe.name, str(d)])
or obj.recipe.name
) + f' ({value:.2g})'
def update(self, instance, validated_data):
# TODO remove once old shopping list

View File

@ -75,7 +75,7 @@
<ul class="navbar-nav mr-auto">
<li class="nav-item {% if request.resolver_match.url_name in 'view_search' %}active{% endif %}">
<a class="nav-link" href="{% url 'view_search' %}"><i
class="fas fa-book"></i> {% trans 'Cookbook' %}</a>
class="fas fa-book"></i> {% trans 'Recipes' %}</a>
</li>
<li class="nav-item {% if request.resolver_match.url_name in 'view_plan' %}active{% endif %}">
<a class="nav-link" href="{% url 'view_plan' %}"><i

View File

@ -15,7 +15,7 @@ from django.core.files import File
from django.db.models import (Case, Count, Exists, F, IntegerField, OuterRef, ProtectedError, Q,
Subquery, Value, When)
from django.db.models.fields.related import ForeignObjectRel
from django.db.models.functions import Coalesce
from django.db.models.functions import Coalesce, Lower
from django.http import FileResponse, HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse
@ -42,19 +42,19 @@ from cookbook.helper.recipe_html_import import get_recipe_from_source
from cookbook.helper.recipe_search import RecipeFacet, RecipeSearch, old_search
from cookbook.helper.recipe_url_import import get_from_scraper
from cookbook.helper.shopping_helper import RecipeShoppingEditor, shopping_helper
from cookbook.models import (Automation, BookmarkletImport, CookLog, Food, FoodInheritField,
ImportLog, ExportLog, Ingredient, Keyword, MealPlan, MealType, Recipe, RecipeBook,
RecipeBookEntry, ShareLink, ShoppingList, ShoppingListEntry,
ShoppingListRecipe, Step, Storage, Supermarket, SupermarketCategory,
SupermarketCategoryRelation, Sync, SyncLog, Unit, UserFile,
UserPreference, ViewLog)
from cookbook.models import (Automation, BookmarkletImport, CookLog, ExportLog, Food,
FoodInheritField, ImportLog, Ingredient, Keyword, MealPlan, MealType,
Recipe, RecipeBook, RecipeBookEntry, ShareLink, ShoppingList,
ShoppingListEntry, ShoppingListRecipe, Step, Storage, Supermarket,
SupermarketCategory, SupermarketCategoryRelation, Sync, SyncLog, Unit,
UserFile, UserPreference, ViewLog)
from cookbook.provider.dropbox import Dropbox
from cookbook.provider.local import Local
from cookbook.provider.nextcloud import Nextcloud
from cookbook.schemas import FilterSchema, QueryParam, QueryParamAutoSchema, TreeSchema
from cookbook.serializer import (AutomationSerializer, BookmarkletImportSerializer,
CookLogSerializer, FoodInheritFieldSerializer, FoodSerializer,
FoodShoppingUpdateSerializer, ImportLogSerializer, ExportLogSerializer,
CookLogSerializer, ExportLogSerializer, FoodInheritFieldSerializer,
FoodSerializer, FoodShoppingUpdateSerializer, ImportLogSerializer,
IngredientSerializer, KeywordSerializer, MealPlanSerializer,
MealTypeSerializer, RecipeBookEntrySerializer,
RecipeBookSerializer, RecipeImageSerializer,
@ -138,7 +138,7 @@ class FuzzyFilterMixin(ViewSetMixin, ExtendedRecipeMixin):
schema = FilterSchema()
def get_queryset(self):
self.queryset = self.queryset.filter(space=self.request.space).order_by('name')
self.queryset = self.queryset.filter(space=self.request.space).order_by(Lower('name').asc())
query = self.request.query_params.get('query', None)
fuzzy = self.request.user.searchpreference.lookup
@ -161,7 +161,7 @@ class FuzzyFilterMixin(ViewSetMixin, ExtendedRecipeMixin):
self.queryset
.annotate(starts=Case(When(name__istartswith=query, then=(Value(100))),
default=Value(0))) # put exact matches at the top of the result set
.filter(filter).order_by('-starts', 'name')
.filter(filter).order_by('-starts', Lower('name').asc())
)
updated_at = self.request.query_params.get('updated_at', None)
@ -175,9 +175,9 @@ class FuzzyFilterMixin(ViewSetMixin, ExtendedRecipeMixin):
limit = self.request.query_params.get('limit', None)
random = self.request.query_params.get('random', False)
if random:
self.queryset = self.queryset.order_by("?")
if limit is not None:
if random:
self.queryset = self.queryset.order_by("?")
self.queryset = self.queryset[:int(limit)]
return self.annotate_recipe(queryset=self.queryset, request=self.request, serializer=self.serializer_class)
@ -276,7 +276,7 @@ class TreeMixin(MergeMixin, FuzzyFilterMixin, ExtendedRecipeMixin):
self.queryset = self.model.objects.none()
else:
return self.annotate_recipe(queryset=super().get_queryset(), request=self.request, serializer=self.serializer_class, tree=True)
self.queryset = self.queryset.filter(space=self.request.space).order_by('name')
self.queryset = self.queryset.filter(space=self.request.space).order_by(Lower('name').asc())
return self.annotate_recipe(queryset=self.queryset, request=self.request, serializer=self.serializer_class, tree=True)
@ -404,7 +404,7 @@ class SupermarketCategoryViewSet(viewsets.ModelViewSet, StandardFilterMixin):
permission_classes = [CustomIsUser]
def get_queryset(self):
self.queryset = self.queryset.filter(space=self.request.space).order_by('name')
self.queryset = self.queryset.filter(space=self.request.space).order_by(Lower('name').asc())
return super().get_queryset()
@ -873,7 +873,6 @@ class ExportLogViewSet(viewsets.ModelViewSet):
return self.queryset.filter(space=self.request.space)
class BookmarkletImportViewSet(viewsets.ModelViewSet):
queryset = BookmarkletImport.objects
serializer_class = BookmarkletImportSerializer

View File

@ -60,7 +60,7 @@ def search(request):
if request.user.userpreference.search_style == UserPreference.NEW:
return search_v2(request)
f = RecipeFilter(request.GET,
queryset=Recipe.objects.filter(space=request.user.userpreference.space).all().order_by('name'),
queryset=Recipe.objects.filter(space=request.user.userpreference.space).all().order_by(Lower('name').asc()),
space=request.space)
if request.user.userpreference.search_style == UserPreference.LARGE:
table = RecipeTable(f.qs)
@ -448,7 +448,7 @@ def history(request):
def system(request):
if not request.user.is_superuser:
return HttpResponseRedirect(reverse('index'))
postgres = False if (
settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql_psycopg2' # noqa: E501
or settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql' # noqa: E501

View File

@ -1,16 +1,17 @@
<template>
<div>
<h3><i class="fas fa-edit"></i> <span v-if="recipe !== undefined">{{ recipe.name }}</span></h3>
<h3>
<i class="fas fa-edit"></i> <span v-if="recipe !== undefined">{{ recipe.name }}</span>
</h3>
<loading-spinner :size="25" v-if="!recipe"></loading-spinner>
<div v-if="recipe !== undefined">
<!-- Title and description -->
<div class="row">
<div class="col-md-12">
<label for="id_name"> {{ $t("Name") }}</label>
<input class="form-control" id="id_name" v-model="recipe.name"/>
<input class="form-control" id="id_name" v-model="recipe.name" />
</div>
</div>
<div class="row pt-2">
@ -18,16 +19,14 @@
<label for="id_description">
{{ $t("Description") }}
</label>
<textarea id="id_description" class="form-control" v-model="recipe.description"
maxlength="512"></textarea>
<textarea id="id_description" class="form-control" v-model="recipe.description" maxlength="512"></textarea>
</div>
</div>
<!-- Image and misc properties -->
<div class="row pt-2">
<div class="col-md-6" style="max-height: 50vh; min-height: 30vh">
<input id="id_file_upload" ref="file_upload" type="file" hidden
@change="uploadImage($event.target.files[0])"/>
<input id="id_file_upload" ref="file_upload" type="file" hidden @change="uploadImage($event.target.files[0])" />
<div
class="h-100 w-100 border border-primary rounded"
@ -36,31 +35,26 @@
@dragover.prevent
@click="$refs.file_upload.click()"
>
<i class="far fa-image fa-10x text-primary"
style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%)"
v-if="!recipe.image"></i>
<i class="far fa-image fa-10x text-primary" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%)" v-if="!recipe.image"></i>
<img :src="recipe.image" id="id_image" class="img img-fluid img-responsive"
style="object-fit: cover; height: 100%" v-if="recipe.image"/>
<img :src="recipe.image" id="id_image" class="img img-fluid img-responsive" style="object-fit: cover; height: 100%" v-if="recipe.image" />
</div>
<button style="bottom: 10px; left: 30px; position: absolute" class="btn btn-danger"
@click="deleteImage" v-if="recipe.image">{{ $t("Delete") }}
</button>
<button style="bottom: 10px; left: 30px; position: absolute" class="btn btn-danger" @click="deleteImage" v-if="recipe.image">{{ $t("Delete") }}</button>
</div>
<div class="col-md-6 mt-1">
<label for="id_name"> {{ $t("Preparation") }} {{ $t("Time") }} ({{ $t("min") }})</label>
<input class="form-control" id="id_prep_time" v-model="recipe.working_time" type="number"/>
<br/>
<input class="form-control" id="id_prep_time" v-model="recipe.working_time" type="number" />
<br />
<label for="id_name"> {{ $t("Waiting") }} {{ $t("Time") }} ({{ $t("min") }})</label>
<input class="form-control" id="id_wait_time" v-model="recipe.waiting_time" type="number"/>
<br/>
<input class="form-control" id="id_wait_time" v-model="recipe.waiting_time" type="number" />
<br />
<label for="id_name"> {{ $t("Servings") }}</label>
<input class="form-control" id="id_servings" v-model="recipe.servings" type="number"/>
<br/>
<input class="form-control" id="id_servings" v-model="recipe.servings" type="number" />
<br />
<label for="id_name"> {{ $t("Servings") }} {{ $t("Text") }}</label>
<input class="form-control" id="id_servings_text" v-model="recipe.servings_text" maxlength="32"/>
<br/>
<input class="form-control" id="id_servings_text" v-model="recipe.servings_text" maxlength="32" />
<br />
<label for="id_name"> {{ $t("Keywords") }}</label>
<multiselect
v-model="recipe.keywords"
@ -123,26 +117,22 @@
<b-collapse id="id_nutrition_collapse" class="mt-2" v-model="nutrition_visible">
<div class="card-body" v-if="recipe.nutrition !== null">
<b-alert show>
There is currently only very basic support for tracking nutritional information.
A
<a href="https://github.com/vabene1111/recipes/issues/896" target="_blank"
rel="noreferrer nofollow">big update</a> is planned to improve on this in
many different areas.
There is currently only very basic support for tracking nutritional information. A
<a href="https://github.com/vabene1111/recipes/issues/896" target="_blank" rel="noreferrer nofollow">big update</a> is planned to improve on this in many different areas.
</b-alert>
<label for="id_name"> {{ $t(energy()) }}</label>
<input class="form-control" id="id_calories" v-model="recipe.nutrition.calories"/>
<input class="form-control" id="id_calories" v-model="recipe.nutrition.calories" />
<label for="id_name"> {{ $t("Carbohydrates") }}</label>
<input class="form-control" id="id_carbohydrates"
v-model="recipe.nutrition.carbohydrates"/>
<input class="form-control" id="id_carbohydrates" v-model="recipe.nutrition.carbohydrates" />
<label for="id_name"> {{ $t("Fats") }}</label>
<input class="form-control" id="id_fats" v-model="recipe.nutrition.fats"/>
<input class="form-control" id="id_fats" v-model="recipe.nutrition.fats" />
<label for="id_name"> {{ $t("Proteins") }}</label>
<input class="form-control" id="id_proteins" v-model="recipe.nutrition.proteins"/>
<input class="form-control" id="id_proteins" v-model="recipe.nutrition.proteins" />
</div>
</b-collapse>
</div>
@ -150,12 +140,10 @@
</div>
<!-- Steps -->
<draggable :list="recipe.steps" group="steps" :empty-insert-threshold="10" handle=".handle"
@sort="sortSteps()">
<draggable :list="recipe.steps" group="steps" :empty-insert-threshold="10" handle=".handle" @sort="sortSteps()">
<div v-for="(step, step_index) in recipe.steps" v-bind:key="step_index">
<div class="card mt-2 mb-2">
<div class="card-body pr-2 pl-2 pr-md-5 pl-md-5" :id="`id_card_step_${step_index}`">
<!-- step card header -->
<div class="row">
<div class="col-11">
@ -166,32 +154,26 @@
</h4>
</div>
<div class="col-1" style="text-align: right">
<a class="btn shadow-none btn-lg" href="#" role="button" id="dropdownMenuLink"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<a class="btn shadow-none btn-lg" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fas fa-ellipsis-v text-muted"></i>
</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuLink">
<button class="dropdown-item" @click="removeStep(step)"><i
class="fa fa-trash fa-fw"></i> {{ $t("Delete") }}
</button>
<button class="dropdown-item" @click="removeStep(step)"><i class="fa fa-trash fa-fw"></i> {{ $t("Delete") }}</button>
<button type="button" class="dropdown-item" v-if="!step.show_as_header"
@click="step.show_as_header = true">
<button type="button" class="dropdown-item" v-if="!step.show_as_header" @click="step.show_as_header = true">
<i class="fas fa-eye fa-fw"></i> {{ $t("Show_as_header") }}
</button>
<button type="button" class="dropdown-item" v-if="step.show_as_header"
@click="step.show_as_header = false">
<button type="button" class="dropdown-item" v-if="step.show_as_header" @click="step.show_as_header = false">
<i class="fas fa-eye-slash fa-fw"></i> {{ $t("Hide_as_header") }}
</button>
<button class="dropdown-item" @click="moveStep(step, step_index - 1)"
v-if="step_index > 0"><i class="fa fa-arrow-up fa-fw"></i>
<button class="dropdown-item" @click="moveStep(step, step_index - 1)" v-if="step_index > 0">
<i class="fa fa-arrow-up fa-fw"></i>
{{ $t("Move_Up") }}
</button>
<button class="dropdown-item" @click="moveStep(step, step_index + 1)"
v-if="step_index !== recipe.steps.length - 1">
<button class="dropdown-item" @click="moveStep(step, step_index + 1)" v-if="step_index !== recipe.steps.length - 1">
<i class="fa fa-arrow-down fa-fw"></i> {{ $t("Move_Down") }}
</button>
</div>
@ -202,36 +184,30 @@
<div class="row">
<div class="col-md-12">
<label :for="'id_step_' + step.id + 'name'">{{ $t("Step_Name") }}</label>
<input class="form-control" v-model="step.name"
:id="'id_step_' + step.id + 'name'"/>
<input class="form-control" v-model="step.name" :id="'id_step_' + step.id + 'name'" />
</div>
</div>
<!-- step data visibility controller -->
<div class="row pt-2">
<div class="col col-md-12">
<b-button pill variant="primary" size="sm" class="ml-1"
@click="step.time_visible = true" v-if="!step.time_visible">
<b-button pill variant="primary" size="sm" class="ml-1" @click="step.time_visible = true" v-if="!step.time_visible">
<i class="fas fa-plus-circle"></i> {{ $t("Time") }}
</b-button>
<b-button pill variant="primary" size="sm" class="ml-1"
@click="step.ingredients_visible = true" v-if="!step.ingredients_visible">
<b-button pill variant="primary" size="sm" class="ml-1" @click="step.ingredients_visible = true" v-if="!step.ingredients_visible">
<i class="fas fa-plus-circle"></i> {{ $t("Ingredients") }}
</b-button>
<b-button pill variant="primary" size="sm" class="ml-1"
@click="step.instruction_visible = true" v-if="!step.instruction_visible">
<b-button pill variant="primary" size="sm" class="ml-1" @click="step.instruction_visible = true" v-if="!step.instruction_visible">
<i class="fas fa-plus-circle"></i> {{ $t("Instructions") }}
</b-button>
<b-button pill variant="primary" size="sm" class="ml-1"
@click="step.step_recipe_visible = true" v-if="!step.step_recipe_visible">
<b-button pill variant="primary" size="sm" class="ml-1" @click="step.step_recipe_visible = true" v-if="!step.step_recipe_visible">
<i class="fas fa-plus-circle"></i> {{ $t("Recipe") }}
</b-button>
<b-button pill variant="primary" size="sm" class="ml-1"
@click="step.file_visible = true" v-if="!step.file_visible">
<b-button pill variant="primary" size="sm" class="ml-1" @click="step.file_visible = true" v-if="!step.file_visible">
<i class="fas fa-plus-circle"></i> {{ $t("File") }}
</b-button>
</div>
@ -240,8 +216,7 @@
<div class="row pt-2" v-if="step.time_visible">
<div class="col-md-12">
<label :for="'id_step_' + step.id + '_time'">{{ $t("step_time_minutes") }}</label>
<input class="form-control" v-model="step.time"
:id="'id_step_' + step.id + '_time'"/>
<input class="form-control" v-model="step.time" :id="'id_step_' + step.id + '_time'" />
</div>
</div>
@ -265,10 +240,19 @@
:multiple="false"
:loading="files_loading"
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
@search-change="searchFiles" >
@search-change="searchFiles"
>
</multiselect>
<b-input-group-append>
<b-button variant="primary" @click="step_for_file_create = step;show_file_create = true"> + </b-button>
<b-button
variant="primary"
@click="
step_for_file_create = step
show_file_create = true
"
>
+
</b-button>
</b-input-group-append>
</b-input-group>
</div>
@ -309,23 +293,16 @@
</div>
<div class="row">
<div class="col-md-12 pr-0 pl-0 pr-md-2 pl-md-2 mt-2">
<draggable :list="step.ingredients" group="ingredients"
:empty-insert-threshold="10" handle=".handle"
@sort="sortIngredients(step)">
<div v-for="(ingredient, index) in step.ingredients"
:key="ingredient.id">
<hr class="d-md-none"/>
<draggable :list="step.ingredients" group="ingredients" :empty-insert-threshold="10" handle=".handle" @sort="sortIngredients(step)">
<div v-for="(ingredient, index) in step.ingredients" :key="ingredient.id">
<hr class="d-md-none" />
<div class="d-flex">
<div class="flex-grow-0 handle align-self-start">
<button type="button"
class="btn btn-lg shadow-none pr-0 pl-1 pr-md-2 pl-md-2"><i
class="fas fa-arrows-alt-v"></i></button>
<button type="button" class="btn btn-lg shadow-none pr-0 pl-1 pr-md-2 pl-md-2"><i class="fas fa-arrows-alt-v"></i></button>
</div>
<div class="flex-fill row"
style="margin-left: 4px; margin-right: 4px">
<div class="col-lg-2 col-md-6 small-padding"
v-if="!ingredient.is_header">
<div class="flex-fill row" style="margin-left: 4px; margin-right: 4px">
<div class="col-lg-2 col-md-6 small-padding" v-if="!ingredient.is_header">
<input
class="form-control"
v-model="ingredient.amount"
@ -336,8 +313,7 @@
/>
</div>
<div class="col-lg-2 col-md-6 small-padding"
v-if="!ingredient.is_header">
<div class="col-lg-2 col-md-6 small-padding" v-if="!ingredient.is_header">
<!-- search set to false to allow API to drive results & order -->
<multiselect
v-if="!ingredient.no_amount"
@ -364,9 +340,9 @@
>
</multiselect>
</div>
<div class="col-lg-4 col-md-6 small-padding"
v-if="!ingredient.is_header">
<div class="col-lg-4 col-md-6 small-padding" v-if="!ingredient.is_header">
<!-- search set to false to allow API to drive results & order -->
<multiselect
ref="food"
v-model="ingredient.food"
@ -391,8 +367,7 @@
>
</multiselect>
</div>
<div class="small-padding"
v-bind:class="{ 'col-lg-4 col-md-6': !ingredient.is_header, 'col-lg-12 col-md-12': ingredient.is_header }">
<div class="small-padding" v-bind:class="{ 'col-lg-4 col-md-6': !ingredient.is_header, 'col-lg-12 col-md-12': ingredient.is_header }">
<input
class="form-control"
maxlength="256"
@ -411,50 +386,44 @@
</div>
<div class="flex-grow-0 small-padding">
<a class="btn shadow-none btn-lg pr-1 pl-0 pr-md-2 pl-md-2" href="#"
role="button" id="dropdownMenuLink2"
data-toggle="dropdown" aria-haspopup="true"
aria-expanded="false">
<a
class="btn shadow-none btn-lg pr-1 pl-0 pr-md-2 pl-md-2"
href="#"
role="button"
id="dropdownMenuLink2"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
<i class="fas fa-ellipsis-v text-muted"></i>
</a>
<div class="dropdown-menu dropdown-menu-right"
aria-labelledby="dropdownMenuLink2">
<button type="button" class="dropdown-item"
@click="removeIngredient(step, ingredient)">
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuLink2">
<button type="button" class="dropdown-item" @click="removeIngredient(step, ingredient)">
<i class="fa fa-trash fa-fw"></i>
{{ $t("Delete") }}
</button>
<button type="button" class="dropdown-item"
v-if="!ingredient.is_header"
@click="ingredient.is_header = true">
<button type="button" class="dropdown-item" v-if="!ingredient.is_header" @click="ingredient.is_header = true">
<i class="fas fa-heading fa-fw"></i>
{{ $t("Make_Header") }}
</button>
<button type="button" class="dropdown-item"
v-if="ingredient.is_header"
@click="ingredient.is_header = false">
<button type="button" class="dropdown-item" v-if="ingredient.is_header" @click="ingredient.is_header = false">
<i class="fas fa-leaf fa-fw"></i>
{{ $t("Make_Ingredient") }}
</button>
<button type="button" class="dropdown-item"
v-if="!ingredient.no_amount"
@click="ingredient.no_amount = true">
<button type="button" class="dropdown-item" v-if="!ingredient.no_amount" @click="ingredient.no_amount = true">
<i class="fas fa-balance-scale-right fa-fw"></i>
{{ $t("Disable_Amount") }}
</button>
<button type="button" class="dropdown-item"
v-if="ingredient.no_amount"
@click="ingredient.no_amount = false">
<button type="button" class="dropdown-item" v-if="ingredient.no_amount" @click="ingredient.no_amount = false">
<i class="fas fa-balance-scale-right fa-fw"></i>
{{ $t("Enable_Amount") }}
</button>
<button type="button" class="dropdown-item"
@click="copyTemplateReference(index, ingredient)">
<button type="button" class="dropdown-item" @click="copyTemplateReference(index, ingredient)">
<i class="fas fa-code"></i>
{{ $t("Copy_template_reference") }}
</button>
@ -466,11 +435,8 @@
</div>
</div>
<div class="row">
<div class="col-md-2 offset-md-5"
style="text-align: center; margin-top: 8px">
<button class="btn btn-success btn-block"
@click="addIngredient(step)"><i class="fa fa-plus"></i>
</button>
<div class="col-md-2 offset-md-5" style="text-align: center; margin-top: 8px">
<button class="btn btn-success btn-block" @click="addIngredient(step)"><i class="fa fa-plus"></i></button>
</div>
</div>
</div>
@ -503,29 +469,24 @@
{{ $t("Add_Step") }}
</button>
<button type="button" v-b-modal:id_modal_sort class="btn btn-warning shadow-none "><i
class="fas fa-sort-amount-down-alt fa-lg"></i></button>
<button type="button" v-b-modal:id_modal_sort class="btn btn-warning shadow-none"><i class="fas fa-sort-amount-down-alt fa-lg"></i></button>
</b-button-group>
</div>
</div>
</div>
</draggable>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br />
<br />
<br />
<br />
<br />
<br />
<!-- bottom buttons save/close/view -->
<div class="row fixed-bottom p-2 b-2 border-top text-center" style="background: white"
v-if="recipe !== undefined">
<div class="row fixed-bottom p-2 b-2 border-top text-center" style="background: white" v-if="recipe !== undefined">
<div class="col-md-3 col-6">
<a :href="resolveDjangoUrl('delete_recipe', recipe.id)"
class="btn btn-block btn-danger shadow-none">{{ $t("Delete") }}</a>
<a :href="resolveDjangoUrl('delete_recipe', recipe.id)" class="btn btn-block btn-danger shadow-none">{{ $t("Delete") }}</a>
</div>
<div class="col-md-3 col-6">
<a :href="resolveDjangoUrl('view_recipe', recipe.id)">
@ -533,15 +494,12 @@
</a>
</div>
<div class="col-md-3 col-6">
<button type="button" @click="updateRecipe(false)" v-b-tooltip.hover
:title="`${$t('Key_Ctrl')} + S`" class="btn btn-sm btn-block btn-info shadow-none">
<button type="button" @click="updateRecipe(false)" v-b-tooltip.hover :title="`${$t('Key_Ctrl')} + S`" class="btn btn-sm btn-block btn-info shadow-none">
{{ $t("Save") }}
</button>
</div>
<div class="col-md-3 col-6">
<button type="button" @click="updateRecipe(true)" v-b-tooltip.hover
:title="`${$t('Key_Ctrl')} + ${$t('Key_Shift')} + S`"
class="btn btn-sm btn-block btn-success shadow-none">
<button type="button" @click="updateRecipe(true)" v-b-tooltip.hover :title="`${$t('Key_Ctrl')} + ${$t('Key_Shift')} + S`" class="btn btn-sm btn-block btn-success shadow-none">
{{ $t("Save_and_View") }}
</button>
</div>
@ -549,11 +507,9 @@
<!-- modal for sorting steps -->
<b-modal id="id_modal_sort" v-bind:title="$t('Sort')" ok-only>
<draggable :list="recipe.steps" group="step_sorter" :empty-insert-threshold="10" handle=".handle"
@sort="sortSteps()" class="list-group" tag="ul">
<draggable :list="recipe.steps" group="step_sorter" :empty-insert-threshold="10" handle=".handle" @sort="sortSteps()" class="list-group" tag="ul">
<li class="list-group-item" v-for="(step, step_index) in recipe.steps" v-bind:key="step_index">
<button type="button" class="btn btn-lg shadow-none handle"><i class="fas fa-arrows-alt-v"></i>
</button>
<button type="button" class="btn btn-lg shadow-none handle"><i class="fas fa-arrows-alt-v"></i></button>
<template v-if="step.name !== ''">{{ step.name }}</template>
<template v-else>{{ $t("Step") }} {{ step_index + 1 }}</template>
</li>
@ -561,30 +517,21 @@
</b-modal>
<!-- form to create files on the fly -->
<generic-modal-form :model="Models.USERFILE" :action="Actions.CREATE" :show="show_file_create"
@finish-action="fileCreated"/>
<generic-modal-form :model="Models.USERFILE" :action="Actions.CREATE" :show="show_file_create" @finish-action="fileCreated" />
</div>
</div>
</template>
<script>
import Vue from "vue"
import {BootstrapVue} from "bootstrap-vue"
import { BootstrapVue } from "bootstrap-vue"
import "bootstrap-vue/dist/bootstrap-vue.css"
import draggable from "vuedraggable"
import {
ApiMixin,
resolveDjangoUrl,
ResolveUrlMixin,
StandardToasts,
convertEnergyToCalories,
energyHeading
} from "@/utils/utils"
import { ApiMixin, resolveDjangoUrl, ResolveUrlMixin, StandardToasts, convertEnergyToCalories, energyHeading } from "@/utils/utils"
import Multiselect from "vue-multiselect"
import {ApiApiFactory} from "@/utils/openapi/api"
import { ApiApiFactory } from "@/utils/openapi/api"
import LoadingSpinner from "@/components/LoadingSpinner"
import VueMarkdownEditor from "@kangc/v-md-editor"
@ -598,7 +545,7 @@ VueMarkdownEditor.use(vuepressTheme, {
})
import enUS from "@kangc/v-md-editor/lib/lang/en-US"
import GenericModalForm from "@/components/Modals/GenericModalForm";
import GenericModalForm from "@/components/Modals/GenericModalForm"
VueMarkdownEditor.lang.use("en-US", enUS)
@ -609,7 +556,7 @@ Vue.use(BootstrapVue)
export default {
name: "RecipeEditView",
mixins: [ResolveUrlMixin, ApiMixin],
components: {Multiselect, LoadingSpinner, draggable, GenericModalForm},
components: { Multiselect, LoadingSpinner, draggable, GenericModalForm },
data() {
return {
recipe_id: window.RECIPE_ID,
@ -639,11 +586,11 @@ export default {
},
mounted() {
this.loadRecipe()
// this.searchUnits("")
// this.searchFoods("")
// this.searchKeywords("")
this.searchUnits("")
this.searchFoods("")
this.searchKeywords("")
this.searchFiles("")
// this.searchRecipes("")
this.searchRecipes("")
this.$i18n.locale = window.CUSTOM_LOCALE
},
@ -692,26 +639,28 @@ export default {
loadRecipe: function () {
let apiFactory = new ApiApiFactory()
apiFactory.retrieveRecipe(this.recipe_id).then((response) => {
this.recipe = response.data
this.loading = false
apiFactory
.retrieveRecipe(this.recipe_id)
.then((response) => {
this.recipe = response.data
this.loading = false
// set default visibility style for each component of the step
this.recipe.steps.forEach((s) => {
this.$set(s, 'time_visible', (s.time !== 0))
this.$set(s, 'ingredients_visible', (s.ingredients.length > 0))
this.$set(s, 'instruction_visible', (s.instruction !== ''))
this.$set(s, 'step_recipe_visible', (s.step_recipe !== null))
this.$set(s, 'file_visible', (s.file !== null))
})
// set default visibility style for each component of the step
this.recipe.steps.forEach((s) => {
this.$set(s, "time_visible", s.time !== 0)
this.$set(s, "ingredients_visible", s.ingredients.length > 0)
this.$set(s, "instruction_visible", s.instruction !== "")
this.$set(s, "step_recipe_visible", s.step_recipe !== null)
this.$set(s, "file_visible", s.file !== null)
})
//TODO workaround function until view is properly refactored, loads name of selected sub recipe so the input can find its label
this.recipe.steps.forEach((s) => {
if (s.step_recipe != null) {
this.recipes.push(s.step_recipe_data)
}
//TODO workaround function until view is properly refactored, loads name of selected sub recipe so the input can find its label
this.recipe.steps.forEach((s) => {
if (s.step_recipe != null) {
this.recipes.push(s.step_recipe_data)
}
})
})
})
.catch((err) => {
this.loading = false
console.log(err)
@ -736,7 +685,7 @@ export default {
}
this.recipe.servings = Math.floor(this.recipe.servings) // temporary fix until a proper framework for frontend input validation is established
if (this.recipe.servings === "" || isNaN(this.recipe.servings) || this.recipe.servings===0 ) {
if (this.recipe.servings === "" || isNaN(this.recipe.servings) || this.recipe.servings === 0) {
this.recipe.servings = 1
}
@ -796,10 +745,10 @@ export default {
ingredients_visible: true,
instruction_visible: true,
step_recipe_visible: false,
file_visible: false
file_visible: false,
}
if (step_index !== undefined) {
console.log('adding at index', step_index)
console.log("adding at index", step_index)
this.recipe.steps.splice(step_index + 1, 0, empty_step)
} else {
this.recipe.steps.push(empty_step)
@ -832,12 +781,12 @@ export default {
this.$nextTick(() => document.getElementById(`amount_${this.recipe.steps.indexOf(step)}_${step.ingredients.length - 1}`).focus())
},
removeIngredient: function (step, ingredient) {
if (confirm(this.$t("confirm_delete", {object: this.$t("Ingredient")}))) {
if (confirm(this.$t("confirm_delete", { object: this.$t("Ingredient") }))) {
step.ingredients = step.ingredients.filter((item) => item !== ingredient)
}
},
removeStep: function (step) {
if (confirm(this.$t("confirm_delete", {object: this.$t("Step")}))) {
if (confirm(this.$t("confirm_delete", { object: this.$t("Step") }))) {
this.recipe.steps = this.recipe.steps.filter((item) => item !== step)
}
},
@ -850,7 +799,7 @@ export default {
let [tmp, step, id] = index.split("_")
let new_food = this.recipe.steps[step].ingredients[id]
new_food.food = {name: tag}
new_food.food = { name: tag }
this.foods.push(new_food.food)
this.recipe.steps[step].ingredients[id] = new_food
},
@ -858,12 +807,12 @@ export default {
let [tmp, step, id] = index.split("_")
let new_unit = this.recipe.steps[step].ingredients[id]
new_unit.unit = {name: tag}
new_unit.unit = { name: tag }
this.units.push(new_unit.unit)
this.recipe.steps[step].ingredients[id] = new_unit
},
addKeyword: function (tag) {
let new_keyword = {label: tag, name: tag}
let new_keyword = { label: tag, name: tag }
this.recipe.keywords.push(new_keyword)
},
searchKeywords: function (query) {
@ -886,7 +835,7 @@ export default {
this.files_loading = true
apiFactory
.listUserFiles({query: {query: query}})
.listUserFiles({ query: { query: query } })
.then((response) => {
this.files = response.data
this.files_loading = false
@ -898,7 +847,7 @@ export default {
},
searchRecipes: function (query) {
this.recipes_loading = true
this.genericAPI(this.Models.RECIPE, this.Actions.LIST, {query: query})
this.genericAPI(this.Models.RECIPE, this.Actions.LIST, { query: query })
.then((result) => {
this.recipes = result.data.results
this.recipes_loading = false
@ -958,13 +907,13 @@ export default {
})
},
fileCreated: function (data) {
if (data !== 'cancel') {
if (data !== "cancel") {
this.step_for_file_create.file = data.item
}
this.show_file_create = false
},
scrollToStep: function (step_index) {
document.getElementById("id_step_" + step_index).scrollIntoView({behavior: "smooth"})
document.getElementById("id_step_" + step_index).scrollIntoView({ behavior: "smooth" })
},
addNutrition: function () {
this.recipe.nutrition = {}

View File

@ -36,7 +36,7 @@
<!-- add to shopping form -->
<b-row class="justify-content-md-center align-items-center pl-1 pr-1" v-if="entrymode">
<b-col cols="12" md="3" v-if="!entry_mode_simple" class="d-none d-md-block mt-1">
<b-col cols="12" md="3" v-if="!ui.entry_mode_simple" class="d-none d-md-block mt-1">
<b-form-input
size="lg"
min="1"
@ -46,13 +46,13 @@
style="font-size: 16px; border-radius: 5px !important; border: 1px solid #e8e8e8 !important"
></b-form-input>
</b-col>
<b-col cols="12" md="4" v-if="!entry_mode_simple" class="mt-1">
<b-col cols="12" md="4" v-if="!ui.entry_mode_simple" class="mt-1">
<lookup-input :class_list="'mb-0'" :form="formUnit" :model="Models.UNIT" @change="new_item.unit = $event" :show_label="false" :clear="clear" />
</b-col>
<b-col cols="12" md="4" v-if="!entry_mode_simple" class="mt-1">
<b-col cols="12" md="4" v-if="!ui.entry_mode_simple" class="mt-1">
<lookup-input :class_list="'mb-0'" :form="formFood" :model="Models.FOOD" @change="new_item.food = $event" :show_label="false" :clear="clear" />
</b-col>
<b-col cols="12" md="11" v-if="entry_mode_simple" class="mt-1">
<b-col cols="12" md="11" v-if="ui.entry_mode_simple" class="mt-1">
<b-form-input size="lg" type="text" :placeholder="$t('QuickEntry')" v-model="new_item.ingredient" @keyup.enter="addItem"></b-form-input>
</b-col>
<b-col cols="12" md="1" class="d-none d-md-block mt-1">
@ -60,7 +60,7 @@
<i class="btn fas fa-cart-plus fa-lg px-0 text-success" @click="addItem" />
</b-button>
</b-col>
<b-col cols="12" md="3" v-if="!entry_mode_simple" class="d-block d-md-none mt-1">
<b-col cols="12" md="3" v-if="!ui.entry_mode_simple" class="d-block d-md-none mt-1">
<b-row>
<b-col cols="9">
<b-form-input
@ -82,10 +82,10 @@
</b-row>
<b-row class="row justify-content-around mt-2" v-if="entrymode">
<b-form-checkbox switch v-model="entry_mode_simple">
<b-form-checkbox switch v-model="ui.entry_mode_simple">
{{ $t("QuickEntry") }}
</b-form-checkbox>
<b-button variant="success" size="sm" class="d-flex d-md-none p-0" v-if="entry_mode_simple">
<b-button variant="success" size="sm" class="d-flex d-md-none p-0" v-if="ui.entry_mode_simple">
<i class="btn fas fa-cart-plus" @click="addItem" />
</b-button>
</b-row>
@ -156,9 +156,6 @@
<b-input-group-prepend is-text>
<input type="number" :min="1" v-model="add_recipe_servings" style="width: 3em" />
</b-input-group-prepend>
<!-- <b-input-group-prepend is-text>
<b>{{ $t("Recipe") }}</b>
</b-input-group-prepend> -->
<generic-multiselect
class="input-group-text m-0 p-0"
@change="new_recipe = $event.val"
@ -589,16 +586,16 @@
<b-form-select v-model="group_by" :options="group_by_choices" size="sm"></b-form-select>
</b-form-group>
<b-form-group v-bind:label="$t('Supermarket')" label-for="popover-input-2" label-cols="6" class="mb-1">
<b-form-select v-model="selected_supermarket" :options="supermarkets" text-field="name" value-field="id" size="sm"></b-form-select>
<b-form-select v-model="ui.selected_supermarket" :options="supermarkets" text-field="name" value-field="id" size="sm"></b-form-select>
</b-form-group>
<!-- TODO: shade filters red when they are actually filtering content -->
<b-form-group v-bind:label="$t('ShowDelayed')" label-for="popover-input-3" content-cols="1" class="mb-1">
<b-form-checkbox v-model="show_delay"></b-form-checkbox>
</b-form-group>
<b-form-group v-bind:label="$t('ShowUncategorizedFood')" label-for="popover-input-4" content-cols="1" class="mb-1" v-if="!selected_supermarket">
<b-form-group v-bind:label="$t('ShowUncategorizedFood')" label-for="popover-input-4" content-cols="1" class="mb-1" v-if="!ui.selected_supermarket">
<b-form-checkbox v-model="show_undefined_categories"></b-form-checkbox>
</b-form-group>
<b-form-group v-bind:label="$t('SupermarketCategoriesOnly')" label-for="popover-input-5" content-cols="1" class="mb-1" v-if="selected_supermarket">
<b-form-group v-bind:label="$t('SupermarketCategoriesOnly')" label-for="popover-input-5" content-cols="1" class="mb-1" v-if="ui.selected_supermarket">
<b-form-checkbox v-model="supermarket_categories_only"></b-form-checkbox>
</b-form-group>
</div>
@ -718,13 +715,15 @@ export default {
group_by_choices: ["created_by", "category", "recipe"],
supermarkets: [],
shopping_categories: [],
selected_supermarket: undefined,
show_undefined_categories: true,
supermarket_categories_only: false,
shopcat: null,
delay: 0,
clear: Math.random(),
entry_mode_simple: false,
ui: {
entry_mode_simple: false,
selected_supermarket: undefined,
},
settings: {
shopping_auto_sync: 0,
default_delay: 4,
@ -773,14 +772,15 @@ export default {
let shopping_list = this.items
// filter out list items that are delayed
if (!this.show_delay && shopping_list) {
shopping_list = shopping_list.filter((x) => !x.delay_until || !Date.parse(x?.delay_until) < new Date(Date.now()))
shopping_list = shopping_list.filter((x) => !x.delay_until || Date.parse(x?.delay_until) < new Date(Date.now()))
}
// if a supermarket is selected and filtered to only supermarket categories filter out everything else
if (this.selected_supermarket && this.supermarket_categories_only) {
if (this.ui.selected_supermarket && this.supermarket_categories_only) {
let shopping_categories = this.supermarkets // category IDs configured on supermarket
.filter((x) => x.id === this.selected_supermarket)
.filter((x) => x.id === this.ui.selected_supermarket)
.map((x) => x.category_to_supermarket)
.flat()
.map((x) => x.category.id)
@ -791,12 +791,12 @@ export default {
}
var groups = { false: {}, true: {} } // force unchecked to always be first
if (this.selected_supermarket) {
if (this.ui.selected_supermarket) {
// TODO: make nulls_first a user setting
groups.false[this.$t("Undefined")] = {}
groups.true[this.$t("Undefined")] = {}
let super_cats = this.supermarkets
.filter((x) => x.id === this.selected_supermarket)
.filter((x) => x.id === this.ui.selected_supermarket)
.map((x) => x.category_to_supermarket)
.flat()
.map((x) => x.category.name)
@ -850,7 +850,7 @@ export default {
return this.items.filter((x) => !x.delay_until || !Date.parse(x?.delay_until) > new Date(Date.now())).length < this.items.length
},
filterApplied() {
return (this.itemsDelayed && !this.show_delay) || !this.show_undefined_categories || (this.supermarket_categories_only && this.selected_supermarket)
return (this.itemsDelayed && !this.show_delay) || !this.show_undefined_categories || (this.supermarket_categories_only && this.ui.selected_supermarket)
},
Recipes() {
// hiding recipes associated with shopping list items that are complete
@ -876,10 +876,13 @@ export default {
},
},
watch: {
selected_supermarket(newVal, oldVal) {
this.supermarket_categories_only = this.settings.filter_to_supermarket
localStorage.setItem("shopping_v2_selected_supermarket", JSON.stringify(this.selected_supermarket))
ui: {
handler() {
this.$cookies.set(SETTINGS_COOKIE_NAME, this.ui)
},
deep: true,
},
new_recipe: {
handler() {
this.add_recipe_servings = this.new_recipe.servings
@ -910,12 +913,11 @@ export default {
"settings.default_delay": function (newVal, oldVal) {
this.delay = Number(newVal)
},
entry_mode_simple(newVal) {
this.$cookies.set(SETTINGS_COOKIE_NAME, newVal)
"ui.selected_supermarket": function (newVal, oldVal) {
this.supermarket_categories_only = this.settings.filter_to_supermarket
},
},
mounted() {
console.log(screen.height)
this.getShoppingList()
this.getSupermarkets()
this.getShoppingCategories()
@ -929,15 +931,14 @@ export default {
}
this.$nextTick(function () {
if (this.$cookies.isKey(SETTINGS_COOKIE_NAME)) {
this.entry_mode_simple = this.$cookies.get(SETTINGS_COOKIE_NAME)
this.ui = Object.assign({}, this.ui, this.$cookies.get(SETTINGS_COOKIE_NAME))
}
this.selected_supermarket = localStorage.getItem("shopping_v2_selected_supermarket") || undefined
})
},
methods: {
// this.genericAPI inherited from ApiMixin
addItem: function () {
if (this.entry_mode_simple) {
if (this.ui.entry_mode_simple) {
if (this.new_item.ingredient !== "" && this.new_item.ingredient !== undefined) {
this.genericPostAPI("api_ingredient_from_string", { text: this.new_item.ingredient }).then((result) => {
let unit = null
@ -1004,7 +1005,7 @@ export default {
})
},
resetFilters: function () {
this.selected_supermarket = undefined
this.ui.selected_supermarket = undefined
this.supermarket_categories_only = this.settings.filter_to_supermarket
this.show_undefined_categories = true
this.group_by = "category"
@ -1091,7 +1092,7 @@ export default {
},
getShoppingList: function (autosync = false) {
let params = {}
params.supermarket = this.selected_supermarket
params.supermarket = this.ui.selected_supermarket
params.options = { query: { recent: 1 } }
if (autosync) {

View File

@ -110,6 +110,7 @@ export default {
methods: {
// this.genericAPI inherited from ApiMixin
search: function (query) {
console.log("did the thing")
let options = {
page: 1,
pageSize: this.limit,