Merge pull request #2730 from smilerz/orphan_file_cleanup
view and delete orphaned files
This commit is contained in:
commit
6214176fe5
@ -7,7 +7,7 @@ class Round(Func):
|
|||||||
|
|
||||||
|
|
||||||
def str2bool(v):
|
def str2bool(v):
|
||||||
if type(v) == bool or v is None:
|
if isinstance(v, bool) or v is None:
|
||||||
return v
|
return v
|
||||||
else:
|
else:
|
||||||
return v.lower() in ("yes", "true", "1")
|
return v.lower() in ("yes", "true", "1")
|
||||||
|
@ -309,7 +309,7 @@ class RecipeSearch():
|
|||||||
|
|
||||||
def _favorite_recipes(self, times_cooked=None):
|
def _favorite_recipes(self, times_cooked=None):
|
||||||
if self._sort_includes('favorite') or times_cooked:
|
if self._sort_includes('favorite') or times_cooked:
|
||||||
less_than = '-' in (times_cooked or []) and not self._sort_includes('-favorite')
|
less_than = '-' in (str(times_cooked) or []) and not self._sort_includes('-favorite')
|
||||||
if less_than:
|
if less_than:
|
||||||
default = 1000
|
default = 1000
|
||||||
else:
|
else:
|
||||||
|
@ -83,22 +83,50 @@
|
|||||||
{% trans 'Everything is fine!' %}
|
{% trans 'Everything is fine!' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<h4 class="mt-3">{% trans 'Database' %} <span
|
<h4 class="mt-3">{% trans 'Database' %}
|
||||||
class="badge badge-{% if postgres %}warning{% else %}success{% endif %}">{% if postgres %}
|
<span class="badge badge-{{postgres_status}}">
|
||||||
{% trans 'Info' %}{% else %}{% trans 'Ok' %}{% endif %}</span></h4>
|
{% if postgres_status == 'warning' %}
|
||||||
{% if postgres %}
|
{% trans 'Info' %}
|
||||||
{% blocktrans %}
|
{% elif postgres_status == 'danger'%}
|
||||||
This application is not running with a Postgres database backend. This is ok but not recommended as some
|
{% trans 'Warning' %}
|
||||||
features only work with postgres databases.
|
{% else %}
|
||||||
{% endblocktrans %}
|
{% trans 'Ok' %}
|
||||||
{% else %}
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
</h4>
|
||||||
|
{{postgres_message}}
|
||||||
|
|
||||||
|
<h4 class="mt-3">
|
||||||
|
{% trans 'Orphaned Files' %}
|
||||||
|
|
||||||
|
<span class="badge badge-{% if orphans|length == 0 %}success{% elif orphans|length <= 25 %}warning{% else %}danger{% endif %}">
|
||||||
|
{% if orphans|length == 0 %}{% trans 'Success' %}
|
||||||
|
{% elif orphans|length <= 25 %}{% trans 'Warning' %}
|
||||||
|
{% else %}{% trans 'Danger' %}
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
{% if orphans|length == 0 %}
|
||||||
{% trans 'Everything is fine!' %}
|
{% trans 'Everything is fine!' %}
|
||||||
|
{% else %}
|
||||||
|
{% blocktrans with orphan_count=orphans|length %}
|
||||||
|
There are currently {{ orphan_count }} orphaned files.
|
||||||
|
{% endblocktrans %}
|
||||||
|
<br>
|
||||||
|
<button id="toggle-button" class="btn btn-info btn-sm" onclick="toggleOrphans()">{% trans 'Show' %}</button>
|
||||||
|
<button class="btn btn-info btn-sm" onclick="deleteOrphans()">{% trans 'Delete' %}</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<textarea id="orphans-list" style="display:none;" class="form-control" rows="20">
|
||||||
|
{% for orphan in orphans %}{{ orphan }}
|
||||||
|
{% endfor %}
|
||||||
|
</textarea>
|
||||||
|
|
||||||
<h4 class="mt-3">Debug</h4>
|
<h4 class="mt-3">Debug</h4>
|
||||||
<textarea class="form-control" rows="20">
|
<textarea class="form-control" rows="20">
|
||||||
Gunicorn Media: {{ gunicorn_media }}
|
Gunicorn Media: {{ gunicorn_media }}
|
||||||
Sqlite: {{ postgres }}
|
Sqlite: {% if postgres %} {% trans 'False' %} {% else %} {% trans 'True' %} {% endif %}
|
||||||
|
{% if postgres %}PostgreSQL: {{postgres_version}} {% endif %}
|
||||||
Debug: {{ debug }}
|
Debug: {{ debug }}
|
||||||
|
|
||||||
{% for key,value in request.META.items %}{% if key in 'SERVER_PORT,REMOTE_HOST,REMOTE_ADDR,SERVER_PROTOCOL' %}{{ key }}:{{ value }}
|
{% for key,value in request.META.items %}{% if key in 'SERVER_PORT,REMOTE_HOST,REMOTE_ADDR,SERVER_PROTOCOL' %}{{ key }}:{{ value }}
|
||||||
@ -110,4 +138,30 @@ Debug: {{ debug }}
|
|||||||
</textarea>
|
</textarea>
|
||||||
<br/>
|
<br/>
|
||||||
<br/>
|
<br/>
|
||||||
{% endblock %}
|
<form method="POST" id="delete-form">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="delete_orphans" value="false">
|
||||||
|
</form>
|
||||||
|
{% block script %}
|
||||||
|
<script>
|
||||||
|
function toggleOrphans() {
|
||||||
|
var orphansList = document.getElementById('orphans-list');
|
||||||
|
var button = document.getElementById('toggle-button');
|
||||||
|
|
||||||
|
if (orphansList.style.display === 'none') {
|
||||||
|
orphansList.style.display = 'block';
|
||||||
|
button.innerText = "{% trans 'Hide' %}";
|
||||||
|
} else {
|
||||||
|
orphansList.style.display = 'none';
|
||||||
|
button.innerText = "{% trans 'Show' %}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function deleteOrphans() {
|
||||||
|
document.getElementById('delete-form').delete_orphans.value = 'true';
|
||||||
|
document.getElementById('delete-form').submit();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{% endblock script %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
TANDOOR_VERSION = ""
|
TANDOOR_VERSION = ""
|
||||||
TANDOOR_REF = ""
|
TANDOOR_REF = "abf8f791360b2bc4a5c7d011877668679bcbb3f2"
|
||||||
VERSION_INFO = []
|
VERSION_INFO = [{'name': 'Tandoor ', 'version': "commit abf8f791360b2bc4a5c7d011877668679bcbb3f2\nMerge: 4723a7ec fd028047\nAuthor: vabene1111 \nDate: Sun Dec 3 14:10:28 2023 +0100\n\n Merge branch 'develop'\n \n # Conflicts:\n # docs/faq.md\n", 'website': 'https://github.com/TandoorRecipes/recipes', 'commit_link': 'https://github.com/TandoorRecipes/recipes/commit/abf8f791360b2bc4a5c7d011877668679bcbb3f2', 'ref': 'abf8f791360b2bc4a5c7d011877668679bcbb3f2', 'branch': 'HEAD', 'tag': ''}]
|
@ -3,12 +3,14 @@ import re
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
|
from django.apps import apps
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
from django.contrib.auth.password_validation import validate_password
|
from django.contrib.auth.password_validation import validate_password
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.db import models
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.shortcuts import get_object_or_404, redirect, render
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
@ -18,6 +20,7 @@ from django_scopes import scopes_disabled
|
|||||||
|
|
||||||
from cookbook.forms import (CommentForm, Recipe, SearchPreferenceForm, SpaceCreateForm,
|
from cookbook.forms import (CommentForm, Recipe, SearchPreferenceForm, SpaceCreateForm,
|
||||||
SpaceJoinForm, User, UserCreateForm, UserPreference)
|
SpaceJoinForm, User, UserCreateForm, UserPreference)
|
||||||
|
from cookbook.helper.HelperFunctions import str2bool
|
||||||
from cookbook.helper.permission_helper import (group_required, has_group_permission,
|
from cookbook.helper.permission_helper import (group_required, has_group_permission,
|
||||||
share_link_valid, switch_user_active_space)
|
share_link_valid, switch_user_active_space)
|
||||||
from cookbook.models import (Comment, CookLog, InviteLink, SearchFields, SearchPreference,
|
from cookbook.models import (Comment, CookLog, InviteLink, SearchFields, SearchPreference,
|
||||||
@ -225,10 +228,10 @@ def shopping_settings(request):
|
|||||||
if not sp:
|
if not sp:
|
||||||
sp = SearchPreferenceForm(user=request.user)
|
sp = SearchPreferenceForm(user=request.user)
|
||||||
fields_searched = (
|
fields_searched = (
|
||||||
len(search_form.cleaned_data['icontains'])
|
len(search_form.cleaned_data['icontains'])
|
||||||
+ len(search_form.cleaned_data['istartswith'])
|
+ len(search_form.cleaned_data['istartswith'])
|
||||||
+ len(search_form.cleaned_data['trigram'])
|
+ len(search_form.cleaned_data['trigram'])
|
||||||
+ len(search_form.cleaned_data['fulltext'])
|
+ len(search_form.cleaned_data['fulltext'])
|
||||||
)
|
)
|
||||||
if search_form.cleaned_data['preset'] == 'fuzzy':
|
if search_form.cleaned_data['preset'] == 'fuzzy':
|
||||||
sp.search = SearchPreference.SIMPLE
|
sp.search = SearchPreference.SIMPLE
|
||||||
@ -314,17 +317,48 @@ def system(request):
|
|||||||
if not request.user.is_superuser:
|
if not request.user.is_superuser:
|
||||||
return HttpResponseRedirect(reverse('index'))
|
return HttpResponseRedirect(reverse('index'))
|
||||||
|
|
||||||
|
postgres_ver = None
|
||||||
postgres = settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql'
|
postgres = settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql'
|
||||||
|
|
||||||
|
if postgres:
|
||||||
|
postgres_current = 16 # will need to be updated as PostgreSQL releases new major versions
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
from django.db import connection
|
||||||
|
|
||||||
|
postgres_ver = Decimal(str(connection.pg_version).replace('00', '.'))
|
||||||
|
if postgres_ver >= postgres_current:
|
||||||
|
database_status = 'success'
|
||||||
|
database_message = _('Everything is fine!')
|
||||||
|
elif postgres_ver < postgres_current - 2:
|
||||||
|
database_status = 'danger'
|
||||||
|
database_message = _('PostgreSQL %(v)s is deprecated. Upgrade to a fully supported version!') % {'v': postgres_ver}
|
||||||
|
else:
|
||||||
|
database_status = 'info'
|
||||||
|
database_message = _('You are running PostgreSQL %(v1)s. PostgreSQL %(v2)s is recommended') % {'v1': postgres_ver, 'v2': postgres_current}
|
||||||
|
else:
|
||||||
|
database_status = 'info'
|
||||||
|
database_message = _('This application is not running with a Postgres database backend. This is ok but not recommended as some features only work with postgres databases.')
|
||||||
|
|
||||||
secret_key = False if os.getenv('SECRET_KEY') else True
|
secret_key = False if os.getenv('SECRET_KEY') else True
|
||||||
|
|
||||||
|
if request.method == "POST":
|
||||||
|
del_orphans = request.POST.get('delete_orphans')
|
||||||
|
orphans = get_orphan_files(delete_orphans=str2bool(del_orphans))
|
||||||
|
else:
|
||||||
|
orphans = get_orphan_files()
|
||||||
|
|
||||||
return render(request, 'system.html', {
|
return render(request, 'system.html', {
|
||||||
'gunicorn_media': settings.GUNICORN_MEDIA,
|
'gunicorn_media': settings.GUNICORN_MEDIA,
|
||||||
'debug': settings.DEBUG,
|
'debug': settings.DEBUG,
|
||||||
'postgres': postgres,
|
'postgres': postgres,
|
||||||
|
'postgres_version': postgres_ver,
|
||||||
|
'postgres_status': database_status,
|
||||||
|
'postgres_message': database_message,
|
||||||
'version_info': VERSION_INFO,
|
'version_info': VERSION_INFO,
|
||||||
'plugins': PLUGINS,
|
'plugins': PLUGINS,
|
||||||
'secret_key': secret_key
|
'secret_key': secret_key,
|
||||||
|
'orphans': orphans
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@ -448,3 +482,47 @@ def test(request):
|
|||||||
def test2(request):
|
def test2(request):
|
||||||
if not settings.DEBUG:
|
if not settings.DEBUG:
|
||||||
return HttpResponseRedirect(reverse('index'))
|
return HttpResponseRedirect(reverse('index'))
|
||||||
|
|
||||||
|
|
||||||
|
def get_orphan_files(delete_orphans=False):
|
||||||
|
# Get list of all image files in media folder
|
||||||
|
media_dir = settings.MEDIA_ROOT
|
||||||
|
|
||||||
|
def find_orphans():
|
||||||
|
image_files = []
|
||||||
|
for root, dirs, files in os.walk(media_dir):
|
||||||
|
for file in files:
|
||||||
|
|
||||||
|
if not file.lower().endswith(('.db')) and not root.lower().endswith(('@eadir')):
|
||||||
|
full_path = os.path.join(root, file)
|
||||||
|
relative_path = os.path.relpath(full_path, media_dir)
|
||||||
|
image_files.append((relative_path, full_path))
|
||||||
|
|
||||||
|
# Get list of all image fields in models
|
||||||
|
image_fields = []
|
||||||
|
for model in apps.get_models():
|
||||||
|
for field in model._meta.get_fields():
|
||||||
|
if isinstance(field, models.ImageField) or isinstance(field, models.FileField):
|
||||||
|
image_fields.append((model, field.name))
|
||||||
|
|
||||||
|
# get all images in the database
|
||||||
|
# TODO I don't know why, but this completely bypasses scope limitations
|
||||||
|
image_paths = []
|
||||||
|
for model, field in image_fields:
|
||||||
|
image_field_paths = model.objects.values_list(field, flat=True)
|
||||||
|
image_paths.extend(image_field_paths)
|
||||||
|
|
||||||
|
# Check each image file against model image fields
|
||||||
|
return [img for img in image_files if img[0] not in image_paths]
|
||||||
|
orphans = find_orphans()
|
||||||
|
if delete_orphans:
|
||||||
|
for f in [img[1] for img in orphans]:
|
||||||
|
try:
|
||||||
|
os.remove(f)
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(f"File not found: {f}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error deleting file {f}: {e}")
|
||||||
|
orphans = find_orphans()
|
||||||
|
|
||||||
|
return [img[1] for img in orphans]
|
||||||
|
@ -438,7 +438,7 @@ for p in PLUGINS:
|
|||||||
if p['bundle_name'] != '':
|
if p['bundle_name'] != '':
|
||||||
WEBPACK_LOADER[p['bundle_name']] = {
|
WEBPACK_LOADER[p['bundle_name']] = {
|
||||||
'CACHE': not DEBUG,
|
'CACHE': not DEBUG,
|
||||||
'BUNDLE_DIR_NAME': f'vue/', # must end with slash
|
'BUNDLE_DIR_NAME': 'vue/', # must end with slash
|
||||||
'STATS_FILE': os.path.join(p["base_path"], 'vue', 'webpack-stats.json'),
|
'STATS_FILE': os.path.join(p["base_path"], 'vue', 'webpack-stats.json'),
|
||||||
'POLL_INTERVAL': 0.1,
|
'POLL_INTERVAL': 0.1,
|
||||||
'TIMEOUT': None,
|
'TIMEOUT': None,
|
||||||
|
14
version.py
14
version.py
@ -13,12 +13,11 @@ tandoor_hash = ''
|
|||||||
try:
|
try:
|
||||||
print('getting tandoor version')
|
print('getting tandoor version')
|
||||||
r = subprocess.check_output(['git', 'show', '-s'], cwd=BASE_DIR).decode()
|
r = subprocess.check_output(['git', 'show', '-s'], cwd=BASE_DIR).decode()
|
||||||
tandoor_branch = subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD'], cwd=BASE_DIR).decode()
|
tandoor_branch = subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD'], cwd=BASE_DIR).decode().replace('\n', '')
|
||||||
tandoor_hash = r.split('\n')[0].split(' ')[1]
|
tandoor_hash = r.split('\n')[0].split(' ')[1]
|
||||||
try:
|
try:
|
||||||
tandoor_tag = subprocess.check_output(['git', 'describe', '--exact-match', tandoor_hash], cwd=BASE_DIR).decode().replace('\n', '')
|
tandoor_tag = subprocess.check_output(['git', 'describe', '--exact-match', '--tags', tandoor_hash], cwd=BASE_DIR).decode().replace('\n', '')
|
||||||
except:
|
except BaseException:
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
version_info.append({
|
version_info.append({
|
||||||
@ -47,8 +46,9 @@ try:
|
|||||||
commit_hash = r.split('\n')[0].split(' ')[1]
|
commit_hash = r.split('\n')[0].split(' ')[1]
|
||||||
try:
|
try:
|
||||||
print('running describe')
|
print('running describe')
|
||||||
tag = subprocess.check_output(['git', 'describe', '--exact-match', commit_hash], cwd=os.path.join(BASE_DIR, 'recipes', 'plugins', d)).decode().replace('\n', '')
|
tag = subprocess.check_output(['git', 'describe', '--exact-match', commit_hash],
|
||||||
except:
|
cwd=os.path.join(BASE_DIR, 'recipes', 'plugins', d)).decode().replace('\n', '')
|
||||||
|
except BaseException:
|
||||||
tag = ''
|
tag = ''
|
||||||
|
|
||||||
version_info.append({
|
version_info.append({
|
||||||
@ -66,7 +66,7 @@ try:
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
print("command '{}' return with error (code {}): {}".format(e.cmd, e.returncode, e.output))
|
print("command '{}' return with error (code {}): {}".format(e.cmd, e.returncode, e.output))
|
||||||
except:
|
except BaseException:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
with open('cookbook/version_info.py', 'w+', encoding='UTF-8') as f:
|
with open('cookbook/version_info.py', 'w+', encoding='UTF-8') as f:
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="app" style="padding-bottom: 60px">
|
<div id="app" style="padding-bottom: 60px">
|
||||||
<RecipeSwitcher ref="ref_recipe_switcher"/>
|
<RecipeSwitcher ref="ref_recipe_switcher" />
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-xl-10 col-lg-10 offset-xl-1 offset-lg-1">
|
<div class="col-12 col-xl-10 col-lg-10 offset-xl-1 offset-lg-1">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@ -153,7 +153,7 @@
|
|||||||
<div class="row" style="margin-top: 1vh">
|
<div class="row" style="margin-top: 1vh">
|
||||||
<div class="col-12" style="text-align: right">
|
<div class="col-12" style="text-align: right">
|
||||||
<b-button size="sm" variant="secondary" style="margin-right: 8px" @click="$root.$emit('bv::hide::popover')"
|
<b-button size="sm" variant="secondary" style="margin-right: 8px" @click="$root.$emit('bv::hide::popover')"
|
||||||
>{{ $t("Close") }}
|
>{{ $t("Close") }}
|
||||||
</b-button>
|
</b-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -197,17 +197,17 @@
|
|||||||
<span
|
<span
|
||||||
class="text-sm-left text-warning"
|
class="text-sm-left text-warning"
|
||||||
v-if="ui.expert_mode && search.keywords_fields > 1 && hasDuplicateFilter(search.search_keywords, search.keywords_fields)"
|
v-if="ui.expert_mode && search.keywords_fields > 1 && hasDuplicateFilter(search.search_keywords, search.keywords_fields)"
|
||||||
>{{ $t("warning_duplicate_filter") }}</span
|
>{{ $t("warning_duplicate_filter") }}</span
|
||||||
>
|
>
|
||||||
<div class="row" v-if="ui.show_keywords">
|
<div class="row" v-if="ui.show_keywords">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<b-input-group class="mt-2" v-for="(k, a) in keywordFields" :key="a">
|
<b-input-group class="mt-2" v-for="(k, a) in keywordFields" :key="a">
|
||||||
<template #prepend v-if="ui.expert_mode">
|
<template #prepend v-if="ui.expert_mode">
|
||||||
<b-input-group-text style="width: 3em" @click="addField('keywords', k)">
|
<b-input-group-text style="width: 3em" @click="addField('keywords', k)">
|
||||||
<i class="fas fa-plus-circle text-primary" v-if="k == search.keywords_fields && k < 4"/>
|
<i class="fas fa-plus-circle text-primary" v-if="k == search.keywords_fields && k < 4" />
|
||||||
</b-input-group-text>
|
</b-input-group-text>
|
||||||
<b-input-group-text style="width: 3em" @click="removeField('keywords', k)">
|
<b-input-group-text style="width: 3em" @click="removeField('keywords', k)">
|
||||||
<i class="fas fa-minus-circle text-primary" v-if="k == search.keywords_fields && k > 1"/>
|
<i class="fas fa-minus-circle text-primary" v-if="k == search.keywords_fields && k > 1" />
|
||||||
</b-input-group-text>
|
</b-input-group-text>
|
||||||
</template>
|
</template>
|
||||||
<generic-multiselect
|
<generic-multiselect
|
||||||
@ -258,17 +258,17 @@
|
|||||||
<span
|
<span
|
||||||
class="text-sm-left text-warning"
|
class="text-sm-left text-warning"
|
||||||
v-if="ui.expert_mode && search.foods_fields > 1 && hasDuplicateFilter(search.search_foods, search.foods_fields)"
|
v-if="ui.expert_mode && search.foods_fields > 1 && hasDuplicateFilter(search.search_foods, search.foods_fields)"
|
||||||
>{{ $t("warning_duplicate_filter") }}</span
|
>{{ $t("warning_duplicate_filter") }}</span
|
||||||
>
|
>
|
||||||
<div class="row" v-if="ui.show_foods">
|
<div class="row" v-if="ui.show_foods">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<b-input-group class="mt-2" v-for="(f, i) in foodFields" :key="i">
|
<b-input-group class="mt-2" v-for="(f, i) in foodFields" :key="i">
|
||||||
<template #prepend v-if="ui.expert_mode">
|
<template #prepend v-if="ui.expert_mode">
|
||||||
<b-input-group-text style="width: 3em" @click="addField('foods', f)">
|
<b-input-group-text style="width: 3em" @click="addField('foods', f)">
|
||||||
<i class="fas fa-plus-circle text-primary" v-if="f == search.foods_fields && f < 4"/>
|
<i class="fas fa-plus-circle text-primary" v-if="f == search.foods_fields && f < 4" />
|
||||||
</b-input-group-text>
|
</b-input-group-text>
|
||||||
<b-input-group-text style="width: 3em" @click="removeField('foods', f)">
|
<b-input-group-text style="width: 3em" @click="removeField('foods', f)">
|
||||||
<i class="fas fa-minus-circle text-primary" v-if="f == search.foods_fields && f > 1"/>
|
<i class="fas fa-minus-circle text-primary" v-if="f == search.foods_fields && f > 1" />
|
||||||
</b-input-group-text>
|
</b-input-group-text>
|
||||||
</template>
|
</template>
|
||||||
<generic-multiselect
|
<generic-multiselect
|
||||||
@ -314,17 +314,17 @@
|
|||||||
<span
|
<span
|
||||||
class="text-sm-left text-warning"
|
class="text-sm-left text-warning"
|
||||||
v-if="ui.expert_mode && search.books_fields > 1 && hasDuplicateFilter(search.search_books, search.books_fields)"
|
v-if="ui.expert_mode && search.books_fields > 1 && hasDuplicateFilter(search.search_books, search.books_fields)"
|
||||||
>{{ $t("warning_duplicate_filter") }}</span
|
>{{ $t("warning_duplicate_filter") }}</span
|
||||||
>
|
>
|
||||||
<div class="row" v-if="ui.show_books">
|
<div class="row" v-if="ui.show_books">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<b-input-group class="mt-2" v-for="(b, i) in bookFields" :key="i">
|
<b-input-group class="mt-2" v-for="(b, i) in bookFields" :key="i">
|
||||||
<template #prepend v-if="ui.expert_mode">
|
<template #prepend v-if="ui.expert_mode">
|
||||||
<b-input-group-text style="width: 3em" @click="addField('books', b)">
|
<b-input-group-text style="width: 3em" @click="addField('books', b)">
|
||||||
<i class="fas fa-plus-circle text-primary" v-if="b == search.books_fields && b < 4"/>
|
<i class="fas fa-plus-circle text-primary" v-if="b == search.books_fields && b < 4" />
|
||||||
</b-input-group-text>
|
</b-input-group-text>
|
||||||
<b-input-group-text style="width: 3em" @click="removeField('books', b)">
|
<b-input-group-text style="width: 3em" @click="removeField('books', b)">
|
||||||
<i class="fas fa-minus-circle text-primary" v-if="b == search.books_fields && b > 1"/>
|
<i class="fas fa-minus-circle text-primary" v-if="b == search.books_fields && b > 1" />
|
||||||
</b-input-group-text>
|
</b-input-group-text>
|
||||||
</template>
|
</template>
|
||||||
<generic-multiselect
|
<generic-multiselect
|
||||||
@ -558,16 +558,26 @@
|
|||||||
<b-input-group-append v-if="ui.show_makenow">
|
<b-input-group-append v-if="ui.show_makenow">
|
||||||
<b-input-group-text>
|
<b-input-group-text>
|
||||||
{{ $t("make_now") }}
|
{{ $t("make_now") }}
|
||||||
<b-form-checkbox v-model="search.makenow" name="check-button"
|
<b-form-checkbox
|
||||||
@change="refreshData(false)"
|
v-model="search.makenow"
|
||||||
class="shadow-none" switch style="width: 4em"/>
|
name="check-button"
|
||||||
|
@change="refreshData(false)"
|
||||||
|
class="shadow-none"
|
||||||
|
switch
|
||||||
|
style="width: 4em"
|
||||||
|
/>
|
||||||
</b-input-group-text>
|
</b-input-group-text>
|
||||||
<b-input-group-text>
|
<b-input-group-text>
|
||||||
<span>{{ $t("make_now_count") }}</span>
|
<span>{{ $t("make_now_count") }}</span>
|
||||||
<b-form-input type="number" min="0" max="20" v-model="search.makenow_count"
|
<b-form-input
|
||||||
@change="refreshData(false)"
|
type="number"
|
||||||
size="sm" class="mt-1"></b-form-input>
|
min="0"
|
||||||
|
max="20"
|
||||||
|
v-model="search.makenow_count"
|
||||||
|
@change="refreshData(false)"
|
||||||
|
size="sm"
|
||||||
|
class="mt-1"
|
||||||
|
></b-form-input>
|
||||||
</b-input-group-text>
|
</b-input-group-text>
|
||||||
</b-input-group-append>
|
</b-input-group-append>
|
||||||
</b-input-group>
|
</b-input-group>
|
||||||
@ -607,11 +617,11 @@
|
|||||||
<!-- TODO find a way to localize this that works without explaining localization to each language translator -->
|
<!-- TODO find a way to localize this that works without explaining localization to each language translator -->
|
||||||
Show all recipes that are matched
|
Show all recipes that are matched
|
||||||
<span v-if="search.search_input">
|
<span v-if="search.search_input">
|
||||||
by <i>{{ search.search_input }}</i> <br/>
|
by <i>{{ search.search_input }}</i> <br />
|
||||||
</span>
|
</span>
|
||||||
<span v-else> without any search term <br/> </span>
|
<span v-else> without any search term <br /> </span>
|
||||||
|
|
||||||
<span v-if="search.search_internal"> and are <span class="text-success">internal</span> <br/></span>
|
<span v-if="search.search_internal"> and are <span class="text-success">internal</span> <br /></span>
|
||||||
|
|
||||||
<span v-for="k in search.search_keywords" v-bind:key="k.id">
|
<span v-for="k in search.search_keywords" v-bind:key="k.id">
|
||||||
<template v-if="k.items.length > 0">
|
<template v-if="k.items.length > 0">
|
||||||
@ -620,7 +630,7 @@
|
|||||||
contain
|
contain
|
||||||
<b v-if="k.operator">any</b><b v-else>all</b> of the following <span class="text-success">keywords</span>:
|
<b v-if="k.operator">any</b><b v-else>all</b> of the following <span class="text-success">keywords</span>:
|
||||||
<i>{{ k.items.flatMap((x) => x.name).join(", ") }}</i>
|
<i>{{ k.items.flatMap((x) => x.name).join(", ") }}</i>
|
||||||
<br/>
|
<br />
|
||||||
</template>
|
</template>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@ -631,7 +641,7 @@
|
|||||||
contain
|
contain
|
||||||
<b v-if="k.operator">any</b><b v-else>all</b> of the following <span class="text-success">foods</span>:
|
<b v-if="k.operator">any</b><b v-else>all</b> of the following <span class="text-success">foods</span>:
|
||||||
<i>{{ k.items.flatMap((x) => x.name).join(", ") }}</i>
|
<i>{{ k.items.flatMap((x) => x.name).join(", ") }}</i>
|
||||||
<br/>
|
<br />
|
||||||
</template>
|
</template>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@ -642,38 +652,38 @@
|
|||||||
contain
|
contain
|
||||||
<b v-if="k.operator">any</b><b v-else>all</b> of the following <span class="text-success">books</span>:
|
<b v-if="k.operator">any</b><b v-else>all</b> of the following <span class="text-success">books</span>:
|
||||||
<i>{{ k.items.flatMap((x) => x.name).join(", ") }}</i>
|
<i>{{ k.items.flatMap((x) => x.name).join(", ") }}</i>
|
||||||
<br/>
|
<br />
|
||||||
</template>
|
</template>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span v-if="search.makenow"> and you can <span class="text-success">make right now</span> (based on the on hand flag) <br/></span>
|
<span v-if="search.makenow"> and you can <span class="text-success">make right now</span> (based on the on hand flag) <br /></span>
|
||||||
|
|
||||||
<span v-if="search.search_units.length > 0">
|
<span v-if="search.search_units.length > 0">
|
||||||
and contain <b v-if="search.search_units_or">any</b><b v-else>all</b> of the following <span class="text-success">units</span>:
|
and contain <b v-if="search.search_units_or">any</b><b v-else>all</b> of the following <span class="text-success">units</span>:
|
||||||
<i>{{ search.search_units.flatMap((x) => x.name).join(", ") }}</i
|
<i>{{ search.search_units.flatMap((x) => x.name).join(", ") }}</i
|
||||||
><br/>
|
><br />
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span v-if="search.search_rating !== undefined">
|
<span v-if="search.search_rating !== undefined">
|
||||||
and have a <span class="text-success">rating</span> <template v-if="search.search_rating_gte">greater than</template
|
and have a <span class="text-success">rating</span> <template v-if="search.search_rating_gte">greater than</template
|
||||||
><template v-else> less than</template> or equal to {{ search.search_rating }}<br/>
|
><template v-else> less than</template> or equal to {{ search.search_rating }}<br />
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span v-if="search.lastcooked !== undefined">
|
<span v-if="search.lastcooked !== undefined">
|
||||||
and have been <span class="text-success">last cooked</span> <template v-if="search.lastcooked_gte"> after</template
|
and have been <span class="text-success">last cooked</span> <template v-if="search.lastcooked_gte"> after</template
|
||||||
><template v-else> before</template> <i>{{ search.lastcooked }}</i
|
><template v-else> before</template> <i>{{ search.lastcooked }}</i
|
||||||
><br/>
|
><br />
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span v-if="search.timescooked !== undefined">
|
<span v-if="search.timescooked !== undefined">
|
||||||
and have <span class="text-success">been cooked</span> <template v-if="search.timescooked_gte"> at least</template
|
and have <span class="text-success">been cooked</span> <template v-if="search.timescooked_gte"> at least</template
|
||||||
><template v-else> less than</template> or equal to<i>{{ search.timescooked }}</i> times <br/>
|
><template v-else> less than</template> or equal to<i>{{ search.timescooked }}</i> times <br />
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span v-if="search.sort_order.length > 0">
|
<span v-if="search.sort_order.length > 0">
|
||||||
<span class="text-success">order</span> by
|
<span class="text-success">order</span> by
|
||||||
<i>{{ search.sort_order.flatMap((x) => x.text).join(", ") }}</i>
|
<i>{{ search.sort_order.flatMap((x) => x.text).join(", ") }}</i>
|
||||||
<br/>
|
<br />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -709,19 +719,19 @@
|
|||||||
</b-dropdown>
|
</b-dropdown>
|
||||||
|
|
||||||
<b-button variant="outline-primary" size="sm" class="shadow-none ml-1" @click="resetSearch()" v-if="searchFiltered()"
|
<b-button variant="outline-primary" size="sm" class="shadow-none ml-1" @click="resetSearch()" v-if="searchFiltered()"
|
||||||
><i class="fas fa-file-alt"></i> {{ search.pagination_page }}/{{ Math.ceil(pagination_count / ui.page_size) }} {{ $t("Reset") }}
|
><i class="fas fa-file-alt"></i> {{ search.pagination_page }}/{{ Math.ceil(pagination_count / ui.page_size) }} {{ $t("Reset") }}
|
||||||
<i class="fas fa-times-circle"></i>
|
<i class="fas fa-times-circle"></i>
|
||||||
</b-button>
|
</b-button>
|
||||||
|
|
||||||
<b-button variant="outline-primary" size="sm" class="shadow-none ml-1" @click="openRandom()"
|
<b-button variant="outline-primary" size="sm" class="shadow-none ml-1" @click="openRandom()"
|
||||||
><i class="fas fa-dice-five"></i> {{ $t("Random") }}
|
><i class="fas fa-dice-five"></i> {{ $t("Random") }}
|
||||||
</b-button>
|
</b-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template v-if="!searchFiltered() && ui.show_meal_plan && meal_plan_grid.length > 0">
|
<template v-if="!searchFiltered() && ui.show_meal_plan && meal_plan_grid.length > 0">
|
||||||
<hr/>
|
<hr />
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col col-md-12">
|
<div class="col col-md-12">
|
||||||
<div
|
<div
|
||||||
@ -736,7 +746,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex-grow-1 text-right">
|
<div class="flex-grow-1 text-right">
|
||||||
<b-button class="hover-button btn-outline-primary btn-sm" @click="showMealPlanEditModal(null, day.create_default_date)"
|
<b-button class="hover-button btn-outline-primary btn-sm" @click="showMealPlanEditModal(null, day.create_default_date)"
|
||||||
><i class="fa fa-plus"></i
|
><i class="fa fa-plus"></i
|
||||||
></b-button>
|
></b-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -744,8 +754,8 @@
|
|||||||
<b-list-group-item v-for="plan in day.plan_entries" v-bind:key="plan.id" class="hover-div p-0 pr-2">
|
<b-list-group-item v-for="plan in day.plan_entries" v-bind:key="plan.id" class="hover-div p-0 pr-2">
|
||||||
<div class="d-flex flex-row align-items-center">
|
<div class="d-flex flex-row align-items-center">
|
||||||
<div>
|
<div>
|
||||||
<img style="height: 50px; width: 50px; object-fit: cover" :src="plan.recipe.image" v-if="plan.recipe?.image"/>
|
<img style="height: 50px; width: 50px; object-fit: cover" :src="plan.recipe.image" v-if="plan.recipe?.image" />
|
||||||
<img style="height: 50px; width: 50px; object-fit: cover" :src="image_placeholder" v-else/>
|
<img style="height: 50px; width: 50px; object-fit: cover" :src="image_placeholder" v-else />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-grow-1 ml-2" style="text-overflow: ellipsis; overflow-wrap: anywhere">
|
<div class="flex-grow-1 ml-2" style="text-overflow: ellipsis; overflow-wrap: anywhere">
|
||||||
<span class="two-row-text">
|
<span class="two-row-text">
|
||||||
@ -755,7 +765,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="hover-button">
|
<div class="hover-button">
|
||||||
<b-button @click="showMealPlanEditModal(plan, null)" class="btn-outline-primary btn-sm"
|
<b-button @click="showMealPlanEditModal(plan, null)" class="btn-outline-primary btn-sm"
|
||||||
><i class="fas fa-pencil-alt"></i
|
><i class="fas fa-pencil-alt"></i
|
||||||
></b-button>
|
></b-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -765,7 +775,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr/>
|
<hr />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div v-if="recipes.length > 0" class="mt-4">
|
<div v-if="recipes.length > 0" class="mt-4">
|
||||||
@ -853,24 +863,23 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Vue from "vue"
|
import { BootstrapVue } from "bootstrap-vue"
|
||||||
import {BootstrapVue} from "bootstrap-vue"
|
|
||||||
import VueCookies from "vue-cookies"
|
|
||||||
import "bootstrap-vue/dist/bootstrap-vue.css"
|
import "bootstrap-vue/dist/bootstrap-vue.css"
|
||||||
|
import Vue from "vue"
|
||||||
|
import VueCookies from "vue-cookies"
|
||||||
|
|
||||||
import moment from "moment"
|
|
||||||
import _debounce from "lodash/debounce"
|
import _debounce from "lodash/debounce"
|
||||||
|
import moment from "moment"
|
||||||
import Multiselect from "vue-multiselect"
|
import Multiselect from "vue-multiselect"
|
||||||
|
|
||||||
import {ApiMixin, ResolveUrlMixin, StandardToasts, ToastMixin} from "@/utils/utils"
|
|
||||||
import LoadingSpinner from "@/components/LoadingSpinner" // TODO: is this deprecated?
|
|
||||||
import RecipeCard from "@/components/RecipeCard"
|
|
||||||
import GenericMultiselect from "@/components/GenericMultiselect"
|
|
||||||
import RecipeSwitcher from "@/components/Buttons/RecipeSwitcher"
|
|
||||||
import {ApiApiFactory} from "@/utils/openapi/api"
|
|
||||||
import {useMealPlanStore} from "@/stores/MealPlanStore"
|
|
||||||
import BottomNavigationBar from "@/components/BottomNavigationBar.vue"
|
import BottomNavigationBar from "@/components/BottomNavigationBar.vue"
|
||||||
|
import RecipeSwitcher from "@/components/Buttons/RecipeSwitcher"
|
||||||
|
import GenericMultiselect from "@/components/GenericMultiselect"
|
||||||
import MealPlanEditModal from "@/components/MealPlanEditModal.vue"
|
import MealPlanEditModal from "@/components/MealPlanEditModal.vue"
|
||||||
|
import RecipeCard from "@/components/RecipeCard"
|
||||||
|
import { useMealPlanStore } from "@/stores/MealPlanStore"
|
||||||
|
import { ApiApiFactory } from "@/utils/openapi/api"
|
||||||
|
import { ApiMixin, ResolveUrlMixin, StandardToasts, ToastMixin } from "@/utils/utils"
|
||||||
|
|
||||||
Vue.use(VueCookies)
|
Vue.use(VueCookies)
|
||||||
Vue.use(BootstrapVue)
|
Vue.use(BootstrapVue)
|
||||||
@ -881,7 +890,7 @@ let UI_COOKIE_NAME = "ui_search_settings"
|
|||||||
export default {
|
export default {
|
||||||
name: "RecipeSearchView",
|
name: "RecipeSearchView",
|
||||||
mixins: [ResolveUrlMixin, ApiMixin, ToastMixin],
|
mixins: [ResolveUrlMixin, ApiMixin, ToastMixin],
|
||||||
components: {GenericMultiselect, RecipeCard, RecipeSwitcher, Multiselect, BottomNavigationBar, MealPlanEditModal},
|
components: { GenericMultiselect, RecipeCard, RecipeSwitcher, Multiselect, BottomNavigationBar, MealPlanEditModal },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
// this.Models and this.Actions inherited from ApiMixin
|
// this.Models and this.Actions inherited from ApiMixin
|
||||||
@ -898,22 +907,22 @@ export default {
|
|||||||
search_input: "",
|
search_input: "",
|
||||||
search_internal: false,
|
search_internal: false,
|
||||||
search_keywords: [
|
search_keywords: [
|
||||||
{items: [], operator: true, not: false},
|
{ items: [], operator: true, not: false },
|
||||||
{items: [], operator: false, not: false},
|
{ items: [], operator: false, not: false },
|
||||||
{items: [], operator: true, not: true},
|
{ items: [], operator: true, not: true },
|
||||||
{items: [], operator: false, not: true},
|
{ items: [], operator: false, not: true },
|
||||||
],
|
],
|
||||||
search_foods: [
|
search_foods: [
|
||||||
{items: [], operator: true, not: false},
|
{ items: [], operator: true, not: false },
|
||||||
{items: [], operator: false, not: false},
|
{ items: [], operator: false, not: false },
|
||||||
{items: [], operator: true, not: true},
|
{ items: [], operator: true, not: true },
|
||||||
{items: [], operator: false, not: true},
|
{ items: [], operator: false, not: true },
|
||||||
],
|
],
|
||||||
search_books: [
|
search_books: [
|
||||||
{items: [], operator: true, not: false},
|
{ items: [], operator: true, not: false },
|
||||||
{items: [], operator: false, not: false},
|
{ items: [], operator: false, not: false },
|
||||||
{items: [], operator: true, not: true},
|
{ items: [], operator: true, not: true },
|
||||||
{items: [], operator: false, not: true},
|
{ items: [], operator: false, not: true },
|
||||||
],
|
],
|
||||||
search_units: [],
|
search_units: [],
|
||||||
search_units_or: true,
|
search_units_or: true,
|
||||||
@ -986,7 +995,7 @@ export default {
|
|||||||
date: moment_date,
|
date: moment_date,
|
||||||
create_default_date: moment_date.format("YYYY-MM-DD"), // improve meal plan edit modal to do formatting itself and accept dates
|
create_default_date: moment_date.format("YYYY-MM-DD"), // improve meal plan edit modal to do formatting itself and accept dates
|
||||||
date_label: moment_date.format("dd") + " " + moment_date.format("ll"),
|
date_label: moment_date.format("dd") + " " + moment_date.format("ll"),
|
||||||
plan_entries: this.meal_plan_store.plan_list.filter((m) => moment_date.isBetween(moment(m.from_date), moment(m.to_date), 'day', '[]'))
|
plan_entries: this.meal_plan_store.plan_list.filter((m) => moment_date.isBetween(moment(m.from_date), moment(m.to_date), "day", "[]")),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1032,12 +1041,12 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{id: 5, label: "⭐⭐⭐⭐⭐ " + label(5)},
|
{ id: 5, label: "⭐⭐⭐⭐⭐ " + label(5) },
|
||||||
{id: 4, label: "⭐⭐⭐⭐ " + label()},
|
{ id: 4, label: "⭐⭐⭐⭐ " + label() },
|
||||||
{id: 3, label: "⭐⭐⭐ " + label()},
|
{ id: 3, label: "⭐⭐⭐ " + label() },
|
||||||
{id: 2, label: "⭐⭐ " + label()},
|
{ id: 2, label: "⭐⭐ " + label() },
|
||||||
{id: 1, label: "⭐ " + label(1)},
|
{ id: 1, label: "⭐ " + label(1) },
|
||||||
{id: 0, label: this.$t("Unrated")},
|
{ id: 0, label: this.$t("Unrated") },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
keywordFields: function () {
|
keywordFields: function () {
|
||||||
@ -1063,7 +1072,7 @@ export default {
|
|||||||
[this.$t("Name"), "name", "A-z", "Z-a"],
|
[this.$t("Name"), "name", "A-z", "Z-a"],
|
||||||
[this.$t("last_cooked"), "lastcooked", "↑", "↓"],
|
[this.$t("last_cooked"), "lastcooked", "↑", "↓"],
|
||||||
[this.$t("Rating"), "rating", "1-5", "5-1"],
|
[this.$t("Rating"), "rating", "1-5", "5-1"],
|
||||||
[this.$t("times_cooked"), "favorite", "*-x", "x-*"],
|
[this.$t("times_cooked"), "favorite", "x-X", "X-x"],
|
||||||
[this.$t("date_created"), "created_at", "↑", "↓"],
|
[this.$t("date_created"), "created_at", "↑", "↓"],
|
||||||
[this.$t("date_viewed"), "lastviewed", "↑", "↓"],
|
[this.$t("date_viewed"), "lastviewed", "↑", "↓"],
|
||||||
]
|
]
|
||||||
@ -1093,7 +1102,7 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.recipes = Array(this.ui.page_size).fill({loading: true})
|
this.recipes = Array(this.ui.page_size).fill({ loading: true })
|
||||||
|
|
||||||
this.$nextTick(function () {
|
this.$nextTick(function () {
|
||||||
if (this.$cookies.isKey(UI_COOKIE_NAME)) {
|
if (this.$cookies.isKey(UI_COOKIE_NAME)) {
|
||||||
@ -1162,13 +1171,13 @@ export default {
|
|||||||
"ui.expert_mode": function (newVal, oldVal) {
|
"ui.expert_mode": function (newVal, oldVal) {
|
||||||
if (!newVal) {
|
if (!newVal) {
|
||||||
this.search.search_keywords = this.search.search_keywords.map((x) => {
|
this.search.search_keywords = this.search.search_keywords.map((x) => {
|
||||||
return {...x, not: false}
|
return { ...x, not: false }
|
||||||
})
|
})
|
||||||
this.search.search_foods = this.search.search_foods.map((x) => {
|
this.search.search_foods = this.search.search_foods.map((x) => {
|
||||||
return {...x, not: false}
|
return { ...x, not: false }
|
||||||
})
|
})
|
||||||
this.search.search_books = this.search.search_books.map((x) => {
|
this.search.search_books = this.search.search_books.map((x) => {
|
||||||
return {...x, not: false}
|
return { ...x, not: false }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1177,7 +1186,7 @@ export default {
|
|||||||
// this.genericAPI inherited from ApiMixin
|
// this.genericAPI inherited from ApiMixin
|
||||||
refreshData: _debounce(function (random) {
|
refreshData: _debounce(function (random) {
|
||||||
this.recipes_loading = true
|
this.recipes_loading = true
|
||||||
this.recipes = Array(this.ui.page_size).fill({loading: true})
|
this.recipes = Array(this.ui.page_size).fill({ loading: true })
|
||||||
let params = this.buildParams(random)
|
let params = this.buildParams(random)
|
||||||
this.genericAPI(this.Models.RECIPE, this.Actions.LIST, params).then((result) => {
|
this.genericAPI(this.Models.RECIPE, this.Actions.LIST, params).then((result) => {
|
||||||
window.scrollTo(0, 0)
|
window.scrollTo(0, 0)
|
||||||
@ -1220,13 +1229,13 @@ export default {
|
|||||||
},
|
},
|
||||||
resetSearch: function (filter = undefined) {
|
resetSearch: function (filter = undefined) {
|
||||||
this.search.search_keywords = this.search.search_keywords.map((x) => {
|
this.search.search_keywords = this.search.search_keywords.map((x) => {
|
||||||
return {...x, items: []}
|
return { ...x, items: [] }
|
||||||
})
|
})
|
||||||
this.search.search_foods = this.search.search_foods.map((x) => {
|
this.search.search_foods = this.search.search_foods.map((x) => {
|
||||||
return {...x, items: []}
|
return { ...x, items: [] }
|
||||||
})
|
})
|
||||||
this.search.search_books = this.search.search_books.map((x) => {
|
this.search.search_books = this.search.search_books.map((x) => {
|
||||||
return {...x, items: []}
|
return { ...x, items: [] }
|
||||||
})
|
})
|
||||||
this.search.search_input = filter?.query ?? ""
|
this.search.search_input = filter?.query ?? ""
|
||||||
this.search.search_internal = filter?.internal ?? false
|
this.search.search_internal = filter?.internal ?? false
|
||||||
@ -1289,7 +1298,7 @@ export default {
|
|||||||
return
|
return
|
||||||
},
|
},
|
||||||
buildParams: function (random) {
|
buildParams: function (random) {
|
||||||
let params = {options: {query: {}}, page: this.search.pagination_page, pageSize: this.ui.page_size}
|
let params = { options: { query: {} }, page: this.search.pagination_page, pageSize: this.ui.page_size }
|
||||||
if (this.search.search_filter) {
|
if (this.search.search_filter) {
|
||||||
params.options.query.filter = this.search.search_filter.id
|
params.options.query.filter = this.search.search_filter.id
|
||||||
return params
|
return params
|
||||||
@ -1414,7 +1423,7 @@ export default {
|
|||||||
;["page", "pageSize"].forEach((key) => {
|
;["page", "pageSize"].forEach((key) => {
|
||||||
delete search[key]
|
delete search[key]
|
||||||
})
|
})
|
||||||
search = {...search, ...search.options.query}
|
search = { ...search, ...search.options.query }
|
||||||
console.log("after concat", search)
|
console.log("after concat", search)
|
||||||
let params = {
|
let params = {
|
||||||
name: filtername,
|
name: filtername,
|
||||||
|
Loading…
Reference in New Issue
Block a user