export recipes from saved filter

This commit is contained in:
smilerz 2022-02-22 15:18:39 -06:00
parent c1605454dd
commit a7d66fa850
No known key found for this signature in database
GPG Key ID: 39444C7606D47126
5 changed files with 333 additions and 513 deletions

View File

@ -179,6 +179,7 @@ class ImportForm(ImportExportBase):
class ExportForm(ImportExportBase): class ExportForm(ImportExportBase):
recipes = forms.ModelMultipleChoiceField(widget=MultiSelectWidget, queryset=Recipe.objects.none(), required=False) recipes = forms.ModelMultipleChoiceField(widget=MultiSelectWidget, queryset=Recipe.objects.none(), required=False)
all = forms.BooleanField(required=False) all = forms.BooleanField(required=False)
filter = forms.IntegerField(required=False)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
space = kwargs.pop('space') space = kwargs.pop('space')

View File

@ -13,8 +13,8 @@ from cookbook.filters import RecipeFilter
from cookbook.helper.HelperFunctions import Round, str2bool from cookbook.helper.HelperFunctions import Round, str2bool
from cookbook.helper.permission_helper import has_group_permission from cookbook.helper.permission_helper import has_group_permission
from cookbook.managers import DICTIONARY from cookbook.managers import DICTIONARY
from cookbook.models import (CookLog, CustomFilter, Food, Keyword, Recipe, SearchFields, from cookbook.models import (CookLog, CustomFilter, Food, Keyword, Recipe, RecipeBook, SearchFields,
SearchPreference, ViewLog, RecipeBook) SearchPreference, ViewLog)
from recipes import settings from recipes import settings
@ -40,7 +40,7 @@ class RecipeSearch():
self._search_prefs = request.user.searchpreference self._search_prefs = request.user.searchpreference
else: else:
self._search_prefs = SearchPreference() self._search_prefs = SearchPreference()
self._string = params.get('query').strip() if params.get('query', None) else None self._string = self._params.get('query').strip() if self._params.get('query', None) else None
self._rating = self._params.get('rating', None) self._rating = self._params.get('rating', None)
self._keywords = { self._keywords = {
'or': self._params.get('keywords_or', None) or self._params.get('keywords', None), 'or': self._params.get('keywords_or', None) or self._params.get('keywords', None),
@ -205,7 +205,7 @@ class RecipeSearch():
else: else:
self._queryset = self._queryset.annotate(simularity=Coalesce(Subquery(simularity), 0.0)) self._queryset = self._queryset.annotate(simularity=Coalesce(Subquery(simularity), 0.0))
if self._sort_includes('score') and self._fulltext_include and self._fuzzy_match is not None: if self._sort_includes('score') and self._fulltext_include and self._fuzzy_match is not None:
self._queryset = self._queryset.annotate(score=Sum(F('rank')+F('simularity'))) self._queryset = self._queryset.annotate(score=F('rank')+F('simularity'))
else: else:
query_filter = Q() query_filter = Q()
for f in [x + '__unaccent__iexact' if x in self._unaccent_include else x + '__iexact' for x in SearchFields.objects.all().values_list('field', flat=True)]: for f in [x + '__unaccent__iexact' if x in self._unaccent_include else x + '__iexact' for x in SearchFields.objects.all().values_list('field', flat=True)]:

View File

@ -11,6 +11,7 @@ from django.utils.translation import gettext as _
from cookbook.forms import ExportForm, ImportExportBase, ImportForm from cookbook.forms import ExportForm, ImportExportBase, ImportForm
from cookbook.helper.permission_helper import group_required from cookbook.helper.permission_helper import group_required
from cookbook.helper.recipe_search import RecipeSearch
from cookbook.integration.cheftap import ChefTap from cookbook.integration.cheftap import ChefTap
from cookbook.integration.chowdown import Chowdown from cookbook.integration.chowdown import Chowdown
from cookbook.integration.cookbookapp import CookBookApp from cookbook.integration.cookbookapp import CookBookApp
@ -123,6 +124,9 @@ def export_recipe(request):
recipes = form.cleaned_data['recipes'] recipes = form.cleaned_data['recipes']
if form.cleaned_data['all']: if form.cleaned_data['all']:
recipes = Recipe.objects.filter(space=request.space, internal=True).all() recipes = Recipe.objects.filter(space=request.space, internal=True).all()
elif filter := form.cleaned_data['filter']:
search = RecipeSearch(request, filter=filter)
recipes = search.get_queryset(Recipe.objects.filter(space=request.space, internal=True))
integration = get_integration(request, form.cleaned_data['type']) integration = get_integration(request, form.cleaned_data['type'])

View File

@ -1,10 +1,8 @@
<template> <template>
<div id="app"> <div id="app">
<h2>{{ $t("Export") }}</h2>
<h2>{{ $t('Export') }}</h2>
<div class="row"> <div class="row">
<div class="col col-md-12"> <div class="col col-md-12">
<br /> <br />
<!-- TODO get option dynamicaly --> <!-- TODO get option dynamicaly -->
<select class="form-control" v-model="recipe_app"> <select class="form-control" v-model="recipe_app">
@ -16,7 +14,7 @@
<br /> <br />
<b-form-checkbox v-model="export_all" @change="disabled_multiselect = $event" name="check-button" switch style="margin-top: 1vh"> <b-form-checkbox v-model="export_all" @change="disabled_multiselect = $event" name="check-button" switch style="margin-top: 1vh">
{{ $t('All recipes') }} {{ $t("All recipes") }}
</b-form-checkbox> </b-form-checkbox>
<multiselect <multiselect
@ -35,140 +33,142 @@
id="id_recipes" id="id_recipes"
:multiple="true" :multiple="true"
:loading="recipes_loading" :loading="recipes_loading"
@search-change="searchRecipes"> @search-change="searchRecipes"
>
</multiselect> </multiselect>
<generic-multiselect
@change="filter = $event.val"
:model="Models.CUSTOM_FILTER"
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
:placeholder="$t('Custom Filter')"
:multiple="false"
:limit="50"
/>
<br /> <br />
<button @click="exportRecipe()" class="btn btn-primary shadow-none"><i class="fas fa-file-export"></i> {{ $t('Export') }} <button @click="exportRecipe()" class="btn btn-primary shadow-none"><i class="fas fa-file-export"></i> {{ $t("Export") }}</button>
</button>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import Vue from 'vue' import Vue from "vue"
import {BootstrapVue} from 'bootstrap-vue' import { BootstrapVue } from "bootstrap-vue"
import 'bootstrap-vue/dist/bootstrap-vue.css' import "bootstrap-vue/dist/bootstrap-vue.css"
import LoadingSpinner from "@/components/LoadingSpinner"
import LoadingSpinner from "@/components/LoadingSpinner"; import { StandardToasts, makeToast, resolveDjangoUrl, ApiMixin } from "@/utils/utils"
import Multiselect from "vue-multiselect"
import {StandardToasts, makeToast, resolveDjangoUrl} from "@/utils/utils"; import GenericMultiselect from "@/components/GenericMultiselect"
import Multiselect from "vue-multiselect"; import { ApiApiFactory } from "@/utils/openapi/api.ts"
import {ApiApiFactory} from "@/utils/openapi/api.ts"; import axios from "axios"
import axios from "axios";
Vue.use(BootstrapVue) Vue.use(BootstrapVue)
export default { export default {
name: 'ExportView', name: "ExportView",
/*mixins: [ /*mixins: [
ResolveUrlMixin, ResolveUrlMixin,
ToastMixin, ToastMixin,
],*/ ],*/
components: {Multiselect}, components: { Multiselect, GenericMultiselect },
mixins: [ApiMixin],
data() { data() {
return { return {
export_id: window.EXPORT_ID, export_id: window.EXPORT_ID,
loading: false, loading: false,
disabled_multiselect: false, disabled_multiselect: false,
recipe_app: 'DEFAULT', recipe_app: "DEFAULT",
recipe_list: [], recipe_list: [],
recipes_loading: false, recipes_loading: false,
recipes: [], recipes: [],
export_all: false, export_all: false,
filter: undefined,
} }
}, },
mounted() { mounted() {
if(this.export_id) if (this.export_id) this.insertRequested()
this.insertRequested() else this.searchRecipes("")
else
this.searchRecipes('')
}, },
methods: { methods: {
insertRequested: function () { insertRequested: function () {
let apiFactory = new ApiApiFactory() let apiFactory = new ApiApiFactory()
this.recipes_loading = true this.recipes_loading = true
apiFactory.retrieveRecipe(this.export_id).then((response) => { apiFactory
.retrieveRecipe(this.export_id)
.then((response) => {
this.recipes_loading = false this.recipes_loading = false
this.recipe_list.push(response.data) this.recipe_list.push(response.data)
})
}).catch((err) => { .catch((err) => {
console.log(err) console.log(err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH) StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH)
}).then(e => this.searchRecipes('')) })
.then((e) => this.searchRecipes(""))
}, },
searchRecipes: function (query) { searchRecipes: function (query) {
let apiFactory = new ApiApiFactory() let apiFactory = new ApiApiFactory()
this.recipes_loading = true this.recipes_loading = true
let maxResultLenght = 1000 let maxResultLenght = 1000
apiFactory.listRecipes(query, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, 1, maxResultLenght).then((response) => { apiFactory
this.recipes = response.data.results; .listRecipes(query, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, 1, maxResultLenght)
.then((response) => {
this.recipes = response.data.results
this.recipes_loading = false this.recipes_loading = false
})
}).catch((err) => { .catch((err) => {
console.log(err) console.log(err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH) StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH)
}) })
}, },
exportRecipe: function () { exportRecipe: function () {
if (this.recipe_list.length < 1 && this.export_all == false && this.filter === undefined) {
if (this.recipe_list.length < 1 && this.export_all == false) {
makeToast(this.$t("Error"), this.$t("Select at least one recipe"), "danger") makeToast(this.$t("Error"), this.$t("Select at least one recipe"), "danger")
return; return
} }
this.error = undefined this.error = undefined
this.loading = true this.loading = true
let formData = new FormData(); let formData = new FormData()
formData.append('type', this.recipe_app); formData.append("type", this.recipe_app)
formData.append('all', this.export_all) formData.append("all", this.export_all)
formData.append("filter", this.filter?.id)
for (var i = 0; i < this.recipe_list.length; i++) { for (var i = 0; i < this.recipe_list.length; i++) {
formData.append('recipes', this.recipe_list[i].id); formData.append("recipes", this.recipe_list[i].id)
} }
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'; axios.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded"
axios.post(resolveDjangoUrl('view_export',), formData).then((response) => { axios
if (response.data['error'] !== undefined){ .post(resolveDjangoUrl("view_export"), formData)
makeToast(this.$t("Error"), response.data['error'],"warning") .then((response) => {
if (response.data["error"] !== undefined) {
makeToast(this.$t("Error"), response.data["error"], "warning")
} else { } else {
window.location.href = resolveDjangoUrl('view_export_response', response.data['export_id']) window.location.href = resolveDjangoUrl("view_export_response", response.data["export_id"])
} }
})
}).catch((err) => { .catch((err) => {
this.error = err.data this.error = err.data
this.loading = false this.loading = false
console.log(err) console.log(err)
makeToast(this.$t("Error"), this.$t("There was an error loading a resource!"), "warning") makeToast(this.$t("Error"), this.$t("There was an error loading a resource!"), "warning")
}) })
}, },
},
} }
}
</script> </script>
<style src="vue-multiselect/dist/vue-multiselect.min.css"></style> <style src="vue-multiselect/dist/vue-multiselect.min.css"></style>
<style> <style></style>
</style>

View File

@ -8,21 +8,15 @@
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-12 col-lg-10 col-xl-8 mt-3 mb-3"> <div class="col-12 col-lg-10 col-xl-8 mt-3 mb-3">
<b-input-group> <b-input-group>
<b-input <b-input class="form-control form-control-lg form-control-borderless form-control-search" v-model="search.search_input" v-bind:placeholder="$t('Search')"></b-input>
class="form-control form-control-lg form-control-borderless form-control-search"
v-model="search.search_input" v-bind:placeholder="$t('Search')"></b-input>
<b-input-group-append> <b-input-group-append>
<b-button v-b-tooltip.hover :title="$t('show_sql')" @click="showSQL()" <b-button v-b-tooltip.hover :title="$t('show_sql')" @click="showSQL()" v-if="debug && ui.sql_debug">
v-if="debug && ui.sql_debug">
<i class="fas fa-bug" style="font-size: 1.5em"></i> <i class="fas fa-bug" style="font-size: 1.5em"></i>
</b-button> </b-button>
<b-button variant="light" v-b-tooltip.hover :title="$t('Random Recipes')" <b-button variant="light" v-b-tooltip.hover :title="$t('Random Recipes')" @click="openRandom()">
@click="openRandom()">
<i class="fas fa-dice-five" style="font-size: 1.5em"></i> <i class="fas fa-dice-five" style="font-size: 1.5em"></i>
</b-button> </b-button>
<b-button v-b-toggle.collapse_advanced_search v-b-tooltip.hover <b-button v-b-toggle.collapse_advanced_search v-b-tooltip.hover :title="$t('Advanced Settings')" v-bind:variant="searchFiltered(true) ? 'danger' : 'primary'">
:title="$t('Advanced Settings')"
v-bind:variant="searchFiltered(true) ? 'danger' : 'primary'">
<!-- TODO consider changing this icon to a filter --> <!-- TODO consider changing this icon to a filter -->
<i class="fas fa-caret-down" v-if="!search.advanced_search_visible"></i> <i class="fas fa-caret-down" v-if="!search.advanced_search_visible"></i>
<i class="fas fa-caret-up" v-if="search.advanced_search_visible"></i> <i class="fas fa-caret-up" v-if="search.advanced_search_visible"></i>
@ -32,18 +26,15 @@
</div> </div>
</div> </div>
<b-collapse id="collapse_advanced_search" class="mt-2 shadow-sm" <b-collapse id="collapse_advanced_search" class="mt-2 shadow-sm" v-model="search.advanced_search_visible">
v-model="search.advanced_search_visible">
<div class="card"> <div class="card">
<div class="card-body p-4"> <div class="card-body p-4">
<div class="row"> <div class="row">
<div class="col-md-3"> <div class="col-md-3">
<a class="btn btn-primary btn-block text-uppercase" <a class="btn btn-primary btn-block text-uppercase" :href="resolveDjangoUrl('new_recipe')">{{ $t("New_Recipe") }}</a>
:href="resolveDjangoUrl('new_recipe')">{{ $t("New_Recipe") }}</a>
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<a class="btn btn-primary btn-block text-uppercase" <a class="btn btn-primary btn-block text-uppercase" :href="resolveDjangoUrl('data_import_url')">{{ $t("Import") }}</a>
:href="resolveDjangoUrl('data_import_url')">{{ $t("Import") }}</a>
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<button <button
@ -62,186 +53,99 @@
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<button id="id_settings_button" <button id="id_settings_button" class="btn btn-primary btn-block text-uppercase"><i class="fas fa-cog fa-lg m-1"></i></button>
class="btn btn-primary btn-block text-uppercase"><i
class="fas fa-cog fa-lg m-1"></i></button>
</div> </div>
</div> </div>
<b-popover target="id_settings_button" triggers="click" placement="bottom"> <b-popover target="id_settings_button" triggers="click" placement="bottom">
<b-tabs content-class="mt-1 text-nowrap" small> <b-tabs content-class="mt-1 text-nowrap" small>
<b-tab :title="$t('Settings')" active :title-link-class="['mx-0']"> <b-tab :title="$t('Settings')" active :title-link-class="['mx-0']">
<b-form-group v-bind:label="$t('Recently_Viewed')" <b-form-group v-bind:label="$t('Recently_Viewed')" label-for="popover-input-1" label-cols="6" class="mb-1">
label-for="popover-input-1" label-cols="6" class="mb-1"> <b-form-input type="number" v-model="ui.recently_viewed" id="popover-input-1" size="sm"></b-form-input>
<b-form-input type="number" v-model="ui.recently_viewed"
id="popover-input-1" size="sm"></b-form-input>
</b-form-group> </b-form-group>
<b-form-group v-bind:label="$t('Recipes_per_page')" <b-form-group v-bind:label="$t('Recipes_per_page')" label-for="popover-input-page-count" label-cols="6" class="mb-1">
label-for="popover-input-page-count" label-cols="6" <b-form-input type="number" v-model="ui.page_size" id="popover-input-page-count" size="sm"></b-form-input>
class="mb-1">
<b-form-input type="number" v-model="ui.page_size"
id="popover-input-page-count"
size="sm"></b-form-input>
</b-form-group> </b-form-group>
<b-form-group v-bind:label="$t('Meal_Plan')" label-for="popover-input-2" <b-form-group v-bind:label="$t('Meal_Plan')" label-for="popover-input-2" label-cols="6" class="mb-1">
label-cols="6" class="mb-1"> <b-form-checkbox switch v-model="ui.show_meal_plan" id="popover-input-2" size="sm"></b-form-checkbox>
<b-form-checkbox switch v-model="ui.show_meal_plan"
id="popover-input-2" size="sm"></b-form-checkbox>
</b-form-group> </b-form-group>
<b-form-group v-if="ui.show_meal_plan" <b-form-group v-if="ui.show_meal_plan" v-bind:label="$t('Meal_Plan_Days')" label-for="popover-input-5" label-cols="6" class="mb-1">
v-bind:label="$t('Meal_Plan_Days')" <b-form-input type="number" v-model="ui.meal_plan_days" id="popover-input-5" size="sm"></b-form-input>
label-for="popover-input-5" label-cols="6" class="mb-1">
<b-form-input type="number" v-model="ui.meal_plan_days"
id="popover-input-5" size="sm"></b-form-input>
</b-form-group> </b-form-group>
<b-form-group v-bind:label="$t('Sort_by_new')" <b-form-group v-bind:label="$t('Sort_by_new')" label-for="popover-input-3" label-cols="6" class="mb-1">
label-for="popover-input-3" label-cols="6" class="mb-1"> <b-form-checkbox switch v-model="ui.sort_by_new" id="popover-input-3" size="sm"></b-form-checkbox>
<b-form-checkbox switch v-model="ui.sort_by_new"
id="popover-input-3" size="sm"></b-form-checkbox>
</b-form-group> </b-form-group>
<div class="row" style="margin-top: 1vh"> <div class="row" style="margin-top: 1vh">
<div class="col-12"> <div class="col-12">
<a :href="resolveDjangoUrl('view_settings') + '#search'">{{ <a :href="resolveDjangoUrl('view_settings') + '#search'">{{ $t("Search Settings") }}</a>
$t("Search Settings")
}}</a>
</div> </div>
</div> </div>
</b-tab> </b-tab>
<b-tab :title="$t('fields')" :title-link-class="['mx-0']"> <b-tab :title="$t('fields')" :title-link-class="['mx-0']">
<b-form-group v-bind:label="$t('show_keywords')" <b-form-group v-bind:label="$t('show_keywords')" label-for="popover-show_keywords" label-cols="8" class="mb-1">
label-for="popover-show_keywords" label-cols="8" <b-form-checkbox switch v-model="ui.show_keywords" id="popover-show_keywords" size="sm"></b-form-checkbox>
class="mb-1">
<b-form-checkbox switch v-model="ui.show_keywords"
id="popover-show_keywords"
size="sm"></b-form-checkbox>
</b-form-group> </b-form-group>
<b-form-group v-bind:label="$t('show_foods')" <b-form-group v-bind:label="$t('show_foods')" label-for="popover-show_foods" label-cols="8" class="mb-1">
label-for="popover-show_foods" label-cols="8" <b-form-checkbox switch v-model="ui.show_foods" id="popover-show_foods" size="sm"></b-form-checkbox>
class="mb-1">
<b-form-checkbox switch v-model="ui.show_foods"
id="popover-show_foods"
size="sm"></b-form-checkbox>
</b-form-group> </b-form-group>
<b-form-group v-bind:label="$t('show_books')" <b-form-group v-bind:label="$t('show_books')" label-for="popover-input-show_books" label-cols="8" class="mb-1">
label-for="popover-input-show_books" label-cols="8" <b-form-checkbox switch v-model="ui.show_books" id="popover-input-show_books" size="sm"></b-form-checkbox>
class="mb-1">
<b-form-checkbox switch v-model="ui.show_books"
id="popover-input-show_books"
size="sm"></b-form-checkbox>
</b-form-group> </b-form-group>
<b-form-group v-bind:label="$t('show_rating')" <b-form-group v-bind:label="$t('show_rating')" label-for="popover-show_rating" label-cols="8" class="mb-1">
label-for="popover-show_rating" label-cols="8" <b-form-checkbox switch v-model="ui.show_rating" id="popover-show_rating" size="sm"></b-form-checkbox>
class="mb-1">
<b-form-checkbox switch v-model="ui.show_rating"
id="popover-show_rating"
size="sm"></b-form-checkbox>
</b-form-group> </b-form-group>
<b-form-group v-bind:label="$t('show_units')" <b-form-group v-bind:label="$t('show_units')" label-for="popover-show_units" label-cols="8" class="mb-1">
label-for="popover-show_units" label-cols="8" <b-form-checkbox switch v-model="ui.show_units" id="popover-show_units" size="sm"></b-form-checkbox>
class="mb-1">
<b-form-checkbox switch v-model="ui.show_units"
id="popover-show_units"
size="sm"></b-form-checkbox>
</b-form-group> </b-form-group>
<b-form-group v-bind:label="$t('show_filters')" <b-form-group v-bind:label="$t('show_filters')" label-for="popover-show_filters" label-cols="8" class="mb-1">
label-for="popover-show_filters" label-cols="8" <b-form-checkbox switch v-model="ui.show_filters" id="popover-show_filters" size="sm"></b-form-checkbox>
class="mb-1">
<b-form-checkbox switch v-model="ui.show_filters"
id="popover-show_filters"
size="sm"></b-form-checkbox>
</b-form-group> </b-form-group>
<b-form-group v-bind:label="$t('show_sortby')" <b-form-group v-bind:label="$t('show_sortby')" label-for="popover-show_sortby" label-cols="8" class="mb-1">
label-for="popover-show_sortby" label-cols="8" <b-form-checkbox switch v-model="ui.show_sortby" id="popover-show_sortby" size="sm"></b-form-checkbox>
class="mb-1">
<b-form-checkbox switch v-model="ui.show_sortby"
id="popover-show_sortby"
size="sm"></b-form-checkbox>
</b-form-group> </b-form-group>
<b-form-group v-bind:label="$t('times_cooked')" <b-form-group v-bind:label="$t('times_cooked')" label-for="popover-show_timescooked" label-cols="8" class="mb-1">
label-for="popover-show_timescooked" label-cols="8" <b-form-checkbox switch v-model="ui.show_timescooked" id="popover-show_cooked" size="sm"></b-form-checkbox>
class="mb-1">
<b-form-checkbox switch v-model="ui.show_timescooked"
id="popover-show_cooked"
size="sm"></b-form-checkbox>
</b-form-group> </b-form-group>
<b-form-group v-bind:label="$t('make_now')" <b-form-group v-bind:label="$t('make_now')" label-for="popover-show_makenow" label-cols="8" class="mb-1">
label-for="popover-show_makenow" label-cols="8" <b-form-checkbox switch v-model="ui.show_makenow" id="popover-show_makenow" size="sm"></b-form-checkbox>
class="mb-1">
<b-form-checkbox switch v-model="ui.show_makenow"
id="popover-show_makenow"
size="sm"></b-form-checkbox>
</b-form-group> </b-form-group>
<b-form-group v-bind:label="$t('last_cooked')" <b-form-group v-bind:label="$t('last_cooked')" label-for="popover-show_cookedon" label-cols="8" class="mb-1">
label-for="popover-show_cookedon" label-cols="8" <b-form-checkbox switch v-model="ui.show_cookedon" id="popover-show_cookedon" size="sm"></b-form-checkbox>
class="mb-1">
<b-form-checkbox switch v-model="ui.show_cookedon"
id="popover-show_cookedon"
size="sm"></b-form-checkbox>
</b-form-group> </b-form-group>
<b-form-group v-bind:label="$t('last_viewed')" <b-form-group v-bind:label="$t('last_viewed')" label-for="popover-show_viewedon" label-cols="8" class="mb-1">
label-for="popover-show_viewedon" label-cols="8" <b-form-checkbox switch v-model="ui.show_viewedon" id="popover-show_viewedon" size="sm"></b-form-checkbox>
class="mb-1">
<b-form-checkbox switch v-model="ui.show_viewedon"
id="popover-show_viewedon"
size="sm"></b-form-checkbox>
</b-form-group> </b-form-group>
<b-form-group v-bind:label="$t('created_on')" <b-form-group v-bind:label="$t('created_on')" label-for="popover-show_createdon" label-cols="8" class="mb-1">
label-for="popover-show_createdon" label-cols="8" <b-form-checkbox switch v-model="ui.show_createdon" id="popover-show_createdon" size="sm"></b-form-checkbox>
class="mb-1">
<b-form-checkbox switch v-model="ui.show_createdon"
id="popover-show_createdon"
size="sm"></b-form-checkbox>
</b-form-group> </b-form-group>
<b-form-group v-bind:label="$t('updatedon')" <b-form-group v-bind:label="$t('updatedon')" label-for="popover-show_updatedon" label-cols="8" class="mb-1">
label-for="popover-show_updatedon" label-cols="8" <b-form-checkbox switch v-model="ui.show_updatedon" id="popover-show_updatedon" size="sm"></b-form-checkbox>
class="mb-1">
<b-form-checkbox switch v-model="ui.show_updatedon"
id="popover-show_updatedon"
size="sm"></b-form-checkbox>
</b-form-group> </b-form-group>
</b-tab> </b-tab>
<b-tab :title="$t('advanced')" :title-link-class="['mx-0']"> <b-tab :title="$t('advanced')" :title-link-class="['mx-0']">
<b-form-group v-bind:label="$t('remember_search')" <b-form-group v-bind:label="$t('remember_search')" label-for="popover-rem-search" label-cols="8" class="mb-1">
label-for="popover-rem-search" label-cols="8" <b-form-checkbox switch v-model="ui.remember_search" id="popover-rem-search" size="sm"></b-form-checkbox>
class="mb-1">
<b-form-checkbox switch v-model="ui.remember_search"
id="popover-rem-search"
size="sm"></b-form-checkbox>
</b-form-group> </b-form-group>
<b-form-group v-if="ui.remember_search" <b-form-group v-if="ui.remember_search" v-bind:label="$t('remember_hours')" label-for="popover-input-rem-hours" label-cols="8" class="mb-1">
v-bind:label="$t('remember_hours')" <b-form-input type="number" v-model="ui.remember_hours" id="popover-rem-hours" size="sm"></b-form-input>
label-for="popover-input-rem-hours" label-cols="8"
class="mb-1">
<b-form-input type="number" v-model="ui.remember_hours"
id="popover-rem-hours" size="sm"></b-form-input>
</b-form-group> </b-form-group>
<b-form-group v-bind:label="$t('tree_select')" <b-form-group v-bind:label="$t('tree_select')" label-for="popover-input-treeselect" label-cols="8" class="mb-1">
label-for="popover-input-treeselect" label-cols="8" <b-form-checkbox switch v-model="ui.tree_select" id="popover-input-treeselect" size="sm"></b-form-checkbox>
class="mb-1">
<b-form-checkbox switch v-model="ui.tree_select"
id="popover-input-treeselect"
size="sm"></b-form-checkbox>
</b-form-group> </b-form-group>
<b-form-group v-if="debug" v-bind:label="$t('sql_debug')" <b-form-group v-if="debug" v-bind:label="$t('sql_debug')" label-for="popover-input-sqldebug" label-cols="8" class="mb-1">
label-for="popover-input-sqldebug" label-cols="8" <b-form-checkbox switch v-model="ui.sql_debug" id="popover-input-sqldebug" size="sm"></b-form-checkbox>
class="mb-1">
<b-form-checkbox switch v-model="ui.sql_debug"
id="popover-input-sqldebug"
size="sm"></b-form-checkbox>
</b-form-group> </b-form-group>
</b-tab> </b-tab>
</b-tabs> </b-tabs>
<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" <b-button size="sm" variant="secondary" style="margin-right: 8px" @click="$root.$emit('bv::hide::popover')">{{ $t("Close") }} </b-button>
@click="$root.$emit('bv::hide::popover')">{{ $t("Close") }}
</b-button>
</div> </div>
</div> </div>
</b-popover> </b-popover>
@ -281,23 +185,18 @@
<h6 class="mb-0" v-if="ui.expert_mode && search.keywords_fields > 1"> <h6 class="mb-0" v-if="ui.expert_mode && search.keywords_fields > 1">
{{ $t("Keywords") }} {{ $t("Keywords") }}
</h6> </h6>
<span class="text-sm-left text-warning" <span 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") $t("warning_duplicate_filter")
}}</span> }}</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" <b-input-group-text style="width: 3em" @click="addField('keywords', k)">
@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" <b-input-group-text style="width: 3em" @click="removeField('keywords', k)">
@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>
<treeselect <treeselect
@ -334,20 +233,14 @@
switch switch
style="width: 4em" style="width: 4em"
> >
<span class="text-uppercase" <span class="text-uppercase" v-if="search.search_keywords[a].operator">{{ $t("or") }}</span>
v-if="search.search_keywords[a].operator">{{
$t("or")
}}</span>
<span class="text-uppercase" v-else>{{ $t("and") }}</span> <span class="text-uppercase" v-else>{{ $t("and") }}</span>
</b-form-checkbox> </b-form-checkbox>
</b-input-group-text> </b-input-group-text>
</b-input-group-append> </b-input-group-append>
<b-input-group-append v-if="ui.expert_mode"> <b-input-group-append v-if="ui.expert_mode">
<b-input-group-text> <b-input-group-text>
<b-form-checkbox v-model="search.search_keywords[a].not" <b-form-checkbox v-model="search.search_keywords[a].not" name="check-button" @change="refreshData(false)" class="shadow-none">
name="check-button"
@change="refreshData(false)"
class="shadow-none">
<span class="text-uppercase">{{ $t("not") }}</span> <span class="text-uppercase">{{ $t("not") }}</span>
</b-form-checkbox> </b-form-checkbox>
</b-input-group-text> </b-input-group-text>
@ -360,23 +253,18 @@
<h6 class="mt-2 mb-0" v-if="ui.expert_mode && search.foods_fields > 1"> <h6 class="mt-2 mb-0" v-if="ui.expert_mode && search.foods_fields > 1">
{{ $t("Foods") }} {{ $t("Foods") }}
</h6> </h6>
<span class="text-sm-left text-warning" <span 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") $t("warning_duplicate_filter")
}}</span> }}</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" <b-input-group-text style="width: 3em" @click="addField('foods', f)">
@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" <b-input-group-text style="width: 3em" @click="removeField('foods', f)">
@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>
<treeselect <treeselect
@ -405,24 +293,15 @@
/> />
<b-input-group-append> <b-input-group-append>
<b-input-group-text> <b-input-group-text>
<b-form-checkbox v-model="search.search_foods[i].operator" <b-form-checkbox v-model="search.search_foods[i].operator" name="check-button" @change="refreshData(false)" class="shadow-none" switch style="width: 4em">
name="check-button" <span class="text-uppercase" v-if="search.search_foods[i].operator">{{ $t("or") }}</span>
@change="refreshData(false)"
class="shadow-none" switch style="width: 4em">
<span class="text-uppercase"
v-if="search.search_foods[i].operator">{{
$t("or")
}}</span>
<span class="text-uppercase" v-else>{{ $t("and") }}</span> <span class="text-uppercase" v-else>{{ $t("and") }}</span>
</b-form-checkbox> </b-form-checkbox>
</b-input-group-text> </b-input-group-text>
</b-input-group-append> </b-input-group-append>
<b-input-group-append v-if="ui.expert_mode"> <b-input-group-append v-if="ui.expert_mode">
<b-input-group-text> <b-input-group-text>
<b-form-checkbox v-model="search.search_foods[i].not" <b-form-checkbox v-model="search.search_foods[i].not" name="check-button" @change="refreshData(false)" class="shadow-none">
name="check-button"
@change="refreshData(false)"
class="shadow-none">
<span class="text-uppercase">{{ $t("not") }}</span> <span class="text-uppercase">{{ $t("not") }}</span>
</b-form-checkbox> </b-form-checkbox>
</b-input-group-text> </b-input-group-text>
@ -435,23 +314,18 @@
<h6 class="mt-2 mb-0" v-if="ui.expert_mode && search.books_fields > 1"> <h6 class="mt-2 mb-0" v-if="ui.expert_mode && search.books_fields > 1">
{{ $t("Books") }} {{ $t("Books") }}
</h6> </h6>
<span class="text-sm-left text-warning" <span 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") $t("warning_duplicate_filter")
}}</span> }}</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" <b-input-group-text style="width: 3em" @click="addField('books', b)">
@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" <b-input-group-text style="width: 3em" @click="removeField('books', b)">
@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
@ -465,24 +339,15 @@
></generic-multiselect> ></generic-multiselect>
<b-input-group-append> <b-input-group-append>
<b-input-group-text> <b-input-group-text>
<b-form-checkbox v-model="search.search_books[i].operator" <b-form-checkbox v-model="search.search_books[i].operator" name="check-button" @change="refreshData(false)" class="shadow-none" style="width: 4em" switch>
name="check-button" <span class="text-uppercase" v-if="search.search_books[i].operator">{{ $t("or") }}</span>
@change="refreshData(false)"
class="shadow-none" style="width: 4em" switch>
<span class="text-uppercase"
v-if="search.search_books[i].operator">{{
$t("or")
}}</span>
<span class="text-uppercase" v-else>{{ $t("and") }}</span> <span class="text-uppercase" v-else>{{ $t("and") }}</span>
</b-form-checkbox> </b-form-checkbox>
</b-input-group-text> </b-input-group-text>
</b-input-group-append> </b-input-group-append>
<b-input-group-append v-if="ui.expert_mode"> <b-input-group-append v-if="ui.expert_mode">
<b-input-group-text> <b-input-group-text>
<b-form-checkbox v-model="search.search_books[i].not" <b-form-checkbox v-model="search.search_books[i].not" name="check-button" @change="refreshData(false)" class="shadow-none">
name="check-button"
@change="refreshData(false)"
class="shadow-none">
<span class="text-uppercase">{{ $t("not") }}</span> <span class="text-uppercase">{{ $t("not") }}</span>
</b-form-checkbox> </b-form-checkbox>
</b-input-group-text> </b-input-group-text>
@ -506,12 +371,8 @@
/> />
<b-input-group-append> <b-input-group-append>
<b-input-group-text> <b-input-group-text>
<b-form-checkbox v-model="search.search_rating_gte" <b-form-checkbox v-model="search.search_rating_gte" name="check-button" @change="refreshData(false)" class="shadow-none" switch style="width: 4em">
name="check-button" <span class="text-uppercase" v-if="search.search_rating_gte">&gt;=</span>
@change="refreshData(false)"
class="shadow-none" switch style="width: 4em">
<span class="text-uppercase"
v-if="search.search_rating_gte">&gt;=</span>
<span class="text-uppercase" v-else>&lt;=</span> <span class="text-uppercase" v-else>&lt;=</span>
</b-form-checkbox> </b-form-checkbox>
</b-input-group-text> </b-input-group-text>
@ -534,13 +395,8 @@
></generic-multiselect> ></generic-multiselect>
<b-input-group-append> <b-input-group-append>
<b-input-group-text> <b-input-group-text>
<b-form-checkbox v-model="search.search_units_or" <b-form-checkbox v-model="search.search_units_or" name="check-button" @change="refreshData(false)" class="shadow-none" style="width: 4em" switch>
name="check-button" <span class="text-uppercase" v-if="search.search_units_or">{{ $t("or") }}</span>
@change="refreshData(false)"
class="shadow-none" style="width: 4em" switch>
<span class="text-uppercase" v-if="search.search_units_or">{{
$t("or")
}}</span>
<span class="text-uppercase" v-else>{{ $t("and") }}</span> <span class="text-uppercase" v-else>{{ $t("and") }}</span>
</b-form-checkbox> </b-form-checkbox>
</b-input-group-text> </b-input-group-text>
@ -550,23 +406,17 @@
</div> </div>
<!-- special switches --> <!-- special switches -->
<div class="row g-0" <div class="row g-0" v-if="ui.show_timescooked || ui.show_makenow || ui.show_cookedon">
v-if="ui.show_timescooked || ui.show_makenow || ui.show_cookedon">
<div class="col-12"> <div class="col-12">
<b-input-group class="mt-2"> <b-input-group class="mt-2">
<!-- times cooked --> <!-- times cooked -->
<b-input-group-prepend is-text v-if="ui.show_timescooked"> <b-input-group-prepend is-text v-if="ui.show_timescooked">
{{ $t("times_cooked") }} {{ $t("times_cooked") }}
</b-input-group-prepend> </b-input-group-prepend>
<b-form-input id="timescooked" type="number" min="0" <b-form-input id="timescooked" type="number" min="0" v-model="search.timescooked" v-if="ui.show_timescooked"></b-form-input>
v-model="search.timescooked"
v-if="ui.show_timescooked"></b-form-input>
<b-input-group-append v-if="ui.show_timescooked"> <b-input-group-append v-if="ui.show_timescooked">
<b-input-group-text> <b-input-group-text>
<b-form-checkbox v-model="search.timescooked_gte" <b-form-checkbox v-model="search.timescooked_gte" name="check-button" @change="refreshData(false)" class="shadow-none" switch style="width: 4em">
name="check-button"
@change="refreshData(false)"
class="shadow-none" switch style="width: 4em">
<span class="text-uppercase" v-if="search.timescooked_gte">&gt;=</span> <span class="text-uppercase" v-if="search.timescooked_gte">&gt;=</span>
<span class="text-uppercase" v-else>&lt;=</span> <span class="text-uppercase" v-else>&lt;=</span>
</b-form-checkbox> </b-form-checkbox>
@ -585,10 +435,7 @@
@input="refreshData(false)" @input="refreshData(false)"
/> />
<b-input-group-text> <b-input-group-text>
<b-form-checkbox v-model="search.cookedon_gte" <b-form-checkbox v-model="search.cookedon_gte" name="check-button" @change="refreshData(false)" class="shadow-none" switch style="width: 4em">
name="check-button"
@change="refreshData(false)"
class="shadow-none" switch style="width: 4em">
<span class="text-uppercase" v-if="search.cookedon_gte">&gt;=</span> <span class="text-uppercase" v-if="search.cookedon_gte">&gt;=</span>
<span class="text-uppercase" v-else>&lt;=</span> <span class="text-uppercase" v-else>&lt;=</span>
</b-form-checkbox> </b-form-checkbox>
@ -608,10 +455,7 @@
@input="refreshData(false)" @input="refreshData(false)"
/> />
<b-input-group-text> <b-input-group-text>
<b-form-checkbox v-model="search.createdon_gte" <b-form-checkbox v-model="search.createdon_gte" name="check-button" @change="refreshData(false)" class="shadow-none" switch style="width: 4em">
name="check-button"
@change="refreshData(false)"
class="shadow-none" switch style="width: 4em">
<span class="text-uppercase" v-if="search.createdon_gte">&gt;=</span> <span class="text-uppercase" v-if="search.createdon_gte">&gt;=</span>
<span class="text-uppercase" v-else>&lt;=</span> <span class="text-uppercase" v-else>&lt;=</span>
</b-form-checkbox> </b-form-checkbox>
@ -629,10 +473,7 @@
@input="refreshData(false)" @input="refreshData(false)"
/> />
<b-input-group-text> <b-input-group-text>
<b-form-checkbox v-model="search.viewedon_gte" <b-form-checkbox v-model="search.viewedon_gte" name="check-button" @change="refreshData(false)" class="shadow-none" switch style="width: 4em">
name="check-button"
@change="refreshData(false)"
class="shadow-none" switch style="width: 4em">
<span class="text-uppercase" v-if="search.viewedon_gte">&gt;=</span> <span class="text-uppercase" v-if="search.viewedon_gte">&gt;=</span>
<span class="text-uppercase" v-else>&lt;=</span> <span class="text-uppercase" v-else>&lt;=</span>
</b-form-checkbox> </b-form-checkbox>
@ -650,10 +491,7 @@
@input="refreshData(false)" @input="refreshData(false)"
/> />
<b-input-group-text> <b-input-group-text>
<b-form-checkbox v-model="search.updatedon_gte" <b-form-checkbox v-model="search.updatedon_gte" name="check-button" @change="refreshData(false)" class="shadow-none" switch style="width: 4em">
name="check-button"
@change="refreshData(false)"
class="shadow-none" switch style="width: 4em">
<span class="text-uppercase" v-if="search.updatedon_gte">&gt;=</span> <span class="text-uppercase" v-if="search.updatedon_gte">&gt;=</span>
<span class="text-uppercase" v-else>&lt;=</span> <span class="text-uppercase" v-else>&lt;=</span>
</b-form-checkbox> </b-form-checkbox>
@ -662,9 +500,7 @@
<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 v-model="search.makenow" name="check-button" @change="refreshData(false)" class="shadow-none" switch style="width: 4em" />
@change="refreshData(false)"
class="shadow-none" switch style="width: 4em"/>
</b-input-group-text> </b-input-group-text>
</b-input-group-append> </b-input-group-append>
</b-input-group> </b-input-group>
@ -674,16 +510,14 @@
<!-- Buttons --> <!-- Buttons -->
<div class="row justify-content-end small"> <div class="row justify-content-end small">
<div class="col-auto"> <div class="col-auto">
<b-button class="my-0" variant="link" size="sm" <b-button class="my-0" variant="link" size="sm" @click="search.explain_visible = !search.explain_visible">
@click="search.explain_visible = !search.explain_visible">
<div v-if="!search.explain_visible"> <div v-if="!search.explain_visible">
<i class="far fa-eye"></i> <i class="far fa-eye"></i>
{{ $t("explain") }} {{ $t("explain") }}
</div> </div>
<div v-else><i class="far fa-eye-slash"></i> {{ $t("explain") }}</div> <div v-else><i class="far fa-eye-slash"></i> {{ $t("explain") }}</div>
</b-button> </b-button>
<b-button class="my-0" variant="link" size="sm" <b-button class="my-0" variant="link" size="sm" @click="ui.expert_mode = !ui.expert_mode">
@click="ui.expert_mode = !ui.expert_mode">
<div v-if="!ui.expert_mode"> <div v-if="!ui.expert_mode">
<i class="fas fa-circle"></i> <i class="fas fa-circle"></i>
{{ $t("expert_mode") }} {{ $t("expert_mode") }}
@ -717,8 +551,7 @@
and and
<b v-if="k.not">don't</b> <b v-if="k.not">don't</b>
contain contain
<b v-if="k.operator">any</b><b <b v-if="k.operator">any</b><b v-else>all</b> of the following <span class="text-success">keywords</span>:
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>
@ -729,9 +562,7 @@
and and
<b v-if="k.not">don't</b> <b v-if="k.not">don't</b>
contain contain
<b v-if="k.operator">any</b><b <b v-if="k.operator">any</b><b v-else>all</b> of the following <span class="text-success">foods</span>:
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>
@ -742,9 +573,7 @@
and and
<b v-if="k.not">don't</b> <b v-if="k.not">don't</b>
contain contain
<b v-if="k.operator">any</b><b <b v-if="k.operator">any</b><b v-else>all</b> of the following <span class="text-success">books</span>:
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>
@ -753,30 +582,24 @@
<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 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>:
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 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
v-if="search.search_rating_gte">greater than</template><template
v-else> less than</template> or
equal to {{ search.search_rating }}<br /> 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 and have been <span class="text-success">last cooked</span> <template v-if="search.lastcooked_gte"> after</template><template v-else> before</template>
v-if="search.lastcooked_gte"> after</template><template v-else> before</template>
<i>{{ search.lastcooked }}</i <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 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
v-if="search.timescooked_gte"> at least</template><template v-else> less than</template> or
equal to<i>{{ search.timescooked }}</i> times <br /> equal to<i>{{ search.timescooked }}</i> times <br />
</span> </span>
@ -797,8 +620,7 @@
<div v-if="recipes.length > 0"> <div v-if="recipes.length > 0">
<div class="row align-content-center"> <div class="row align-content-center">
<div class="col col-md-6" style="margin-top: 2vh"> <div class="col col-md-6" style="margin-top: 2vh">
<b-dropdown id="sortby" :text="sortByLabel" variant="link" <b-dropdown id="sortby" :text="sortByLabel" variant="link" toggle-class="text-decoration-none " class="m-0 p-0">
toggle-class="text-decoration-none " class="m-0 p-0">
<div v-for="o in sortOptions" :key="o.id"> <div v-for="o in sortOptions" :key="o.id">
<b-dropdown-item <b-dropdown-item
v-on:click=" v-on:click="
@ -813,9 +635,7 @@
</div> </div>
<div class="col col-md-6 text-right" style="margin-top: 2vh"> <div class="col col-md-6 text-right" style="margin-top: 2vh">
<span class="text-muted"> <span class="text-muted">
{{ $t("Page") }} {{ search.pagination_page }}/{{ {{ $t("Page") }} {{ search.pagination_page }}/{{ Math.ceil(pagination_count / ui.page_size) }}
Math.ceil(pagination_count / ui.page_size)
}}
<a href="#" @click="resetSearch()"><i class="fas fa-times-circle"></i> {{ $t("Reset") }}</a> <a href="#" @click="resetSearch()"><i class="fas fa-times-circle"></i> {{ $t("Reset") }}</a>
</span> </span>
</div> </div>
@ -823,24 +643,25 @@
<div class="row"> <div class="row">
<div class="col col-md-12"> <div class="col col-md-12">
<div <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); grid-gap: 0.8rem">
style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); grid-gap: 0.8rem">
<template v-if="!searchFiltered()"> <template v-if="!searchFiltered()">
<recipe-card v-bind:key="`mp_${m.id}`" v-for="m in meal_plans" :recipe="m.recipe" <recipe-card
:meal_plan="m" :footer_text="m.meal_type_name" v-bind:key="`mp_${m.id}`"
footer_icon="far fa-calendar-alt"></recipe-card> v-for="m in meal_plans"
:recipe="m.recipe"
:meal_plan="m"
:footer_text="m.meal_type_name"
footer_icon="far fa-calendar-alt"
></recipe-card>
</template> </template>
<recipe-card v-for="r in recipes" v-bind:key="r.id" :recipe="r" <recipe-card v-for="r in recipes" v-bind:key="r.id" :recipe="r" :footer_text="isRecentOrNew(r)[0]" :footer_icon="isRecentOrNew(r)[1]"></recipe-card>
:footer_text="isRecentOrNew(r)[0]"
:footer_icon="isRecentOrNew(r)[1]"></recipe-card>
</div> </div>
</div> </div>
</div> </div>
<div class="row" style="margin-top: 2vh" v-if="!random_search"> <div class="row" style="margin-top: 2vh" v-if="!random_search">
<div class="col col-md-12"> <div class="col col-md-12">
<b-pagination pills v-model="search.pagination_page" :total-rows="pagination_count" <b-pagination pills v-model="search.pagination_page" :total-rows="pagination_count" :per-page="ui.page_size" @change="pageChange" align="center"></b-pagination>
:per-page="ui.page_size" @change="pageChange" align="center"></b-pagination>
</div> </div>
</div> </div>
<div class="col-md-2 d-none d-md-block"></div> <div class="col-md-2 d-none d-md-block"></div>
@ -848,9 +669,8 @@
<div v-if="recipes.length < 1 && !recipes_loading"> <div v-if="recipes.length < 1 && !recipes_loading">
<div class="row mt-5"> <div class="row mt-5">
<div class="col col-md-12 text-center"> <div class="col col-md-12 text-center">
<h4 class="text-muted"><i class="far fa-eye"></i> {{ $t('search_no_recipes') }}</h4> <h4 class="text-muted"><i class="far fa-eye"></i> {{ $t("search_no_recipes") }}</h4>
</div> </div>
</div> </div>
<div class="row mt-2"> <div class="row mt-2">
@ -858,30 +678,24 @@
<b-card-group deck> <b-card-group deck>
<b-card v-bind:title="$t('Import')" class="text-center"> <b-card v-bind:title="$t('Import')" class="text-center">
<b-card-text> <b-card-text>
{{ $t('search_import_help_text') }} {{ $t("search_import_help_text") }}
</b-card-text> </b-card-text>
<b-button variant="primary" :href="resolveDjangoUrl('data_import_url')"><i <b-button variant="primary" :href="resolveDjangoUrl('data_import_url')"><i class="fas fa-file-import"></i> {{ $t("Import") }} </b-button>
class="fas fa-file-import"></i> {{ $t('Import') }}
</b-button>
</b-card> </b-card>
<b-card v-bind:title="$t('Create')" class="text-center"> <b-card v-bind:title="$t('Create')" class="text-center">
<b-card-text> <b-card-text>
{{ $t('search_create_help_text') }} {{ $t("search_create_help_text") }}
</b-card-text> </b-card-text>
<b-button variant="primary" :href="resolveDjangoUrl('new_recipe')"><i <b-button variant="primary" :href="resolveDjangoUrl('new_recipe')"><i class="fas fa-plus"></i> {{ $t("Create") }} </b-button>
class="fas fa-plus"></i> {{ $t('Create') }}
</b-button>
</b-card> </b-card>
</b-card-group> </b-card-group>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
@ -1334,8 +1148,7 @@ export default {
}, },
showSQL: function () { showSQL: function () {
let params = this.buildParams() let params = this.buildParams()
this.genericAPI(this.Models.RECIPE, this.Actions.LIST, params).then((result) => { this.genericAPI(this.Models.RECIPE, this.Actions.LIST, params).then((result) => {})
})
}, },
// TODO refactor to combine with load KeywordChildren // TODO refactor to combine with load KeywordChildren
loadFoodChildren({ action, parentNode, callback }) { loadFoodChildren({ action, parentNode, callback }) {
@ -1473,10 +1286,12 @@ export default {
} }
let search = this.buildParams(false) let search = this.buildParams(false)
console.log("after build", search)
;["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)
let params = { let params = {
name: filtername, name: filtername,
search: JSON.stringify(search), search: JSON.stringify(search),