diff --git a/cookbook/helper/recipe_search.py b/cookbook/helper/recipe_search.py
index 2d9641128..fb074b2ae 100644
--- a/cookbook/helper/recipe_search.py
+++ b/cookbook/helper/recipe_search.py
@@ -653,7 +653,7 @@ class RecipeFacet():
if not self._request.space.demo and self._request.space.show_facet_count:
return queryset.annotate(count=Coalesce(Subquery(self._recipe_count_queryset('keywords', depth, steplen)), 0)
).filter(depth=depth, count__gt=0
- ).values('id', 'name', 'count', 'numchild').order_by('name')
+ ).values('id', 'name', 'count', 'numchild').order_by('name')[:200]
else:
return queryset.filter(depth=depth).values('id', 'name', 'numchild').order_by('name')
@@ -664,7 +664,7 @@ class RecipeFacet():
if not self._request.space.demo and self._request.space.show_facet_count:
return queryset.annotate(count=Coalesce(Subquery(self._recipe_count_queryset('steps__ingredients__food', depth, steplen)), 0)
).filter(depth__lte=depth, count__gt=0
- ).values('id', 'name', 'count', 'numchild').order_by('name')
+ ).values('id', 'name', 'count', 'numchild').order_by('name')[:200]
else:
return queryset.filter(depth__lte=depth).values('id', 'name', 'numchild').order_by('name')
diff --git a/cookbook/templates/url_import.html b/cookbook/templates/url_import.html
index df03abf03..1b6de7102 100644
--- a/cookbook/templates/url_import.html
+++ b/cookbook/templates/url_import.html
@@ -668,7 +668,6 @@
Vue.component('vue-multiselect', window.VueMultiselect.default)
-
let app = new Vue({
components: {
Multiselect: window.VueMultiselect.default
diff --git a/vue/src/apps/RecipeSearchView/RecipeSearchView.vue b/vue/src/apps/RecipeSearchView/RecipeSearchView.vue
index 4172e7795..8eae19eda 100644
--- a/vue/src/apps/RecipeSearchView/RecipeSearchView.vue
+++ b/vue/src/apps/RecipeSearchView/RecipeSearchView.vue
@@ -8,25 +8,25 @@
-
+
@@ -41,12 +41,13 @@
class="btn btn-block text-uppercase"
v-b-tooltip.hover
:title="$t('show_only_internal')"
- v-bind:class="{ 'btn-success': settings.search_internal, 'btn-primary': !settings.search_internal }"
+ v-bind:class="{ 'btn-success': search.search_internal, 'btn-primary': !search.search_internal }"
@click="
- settings.search_internal = !settings.search_internal
+ search.search_internal = !search.search_internal
refreshData()
"
>
+
{{ $t("Internal") }}
@@ -56,33 +57,50 @@
-
-
-
-
-
+
+
+
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
{{ $t("Close") }}
@@ -95,7 +113,8 @@
+
-
- {{ $t("or") }}
+
+ {{ $t("or") }}
{{ $t("and") }}
@@ -124,7 +153,8 @@
+
-
- {{ $t("or") }}
+
+ {{ $t("or") }}
{{ $t("and") }}
@@ -155,7 +195,7 @@
-
- {{ $t("or") }}
+
+ {{ $t("or") }}
{{ $t("and") }}
@@ -178,7 +218,7 @@
- {{ $t("Page") }} {{ settings.pagination_page }}/{{ Math.ceil(pagination_count / settings.page_count) }}
+ {{ $t("Page") }} {{ search.pagination_page }}/{{ Math.ceil(pagination_count / ui.page_size) }}
{{ $t("Reset") }}
@@ -220,7 +260,7 @@
@@ -247,13 +287,14 @@ import LoadingSpinner from "@/components/LoadingSpinner" // TODO: is this deprec
import RecipeCard from "@/components/RecipeCard"
import GenericMultiselect from "@/components/GenericMultiselect"
-import { Treeselect, LOAD_CHILDREN_OPTIONS } from "@riophae/vue-treeselect"
-import "@riophae/vue-treeselect/dist/vue-treeselect.css"
+import { Treeselect, LOAD_CHILDREN_OPTIONS } from "@riophae/vue-treeselect" //TODO: delete
+import "@riophae/vue-treeselect/dist/vue-treeselect.css" //TODO: delete
import RecipeSwitcher from "@/components/Buttons/RecipeSwitcher"
Vue.use(BootstrapVue)
-let SETTINGS_COOKIE_NAME = "search_settings"
+let SEARCH_COOKIE_NAME = "search_settings"
+let UI_COOKIE_NAME = "_uisearch_settings"
export default {
name: "RecipeSearchView",
@@ -267,27 +308,30 @@ export default {
meal_plans: [],
last_viewed_recipes: [],
- settings_loaded: false,
- settings: {
+ search: {
search_input: "",
search_internal: false,
search_keywords: [],
search_foods: [],
search_books: [],
search_ratings: undefined,
-
search_keywords_or: true,
search_foods_or: true,
search_books_or: true,
+ pagination_page: 1,
+ },
+ ui: {
advanced_search_visible: false,
show_meal_plan: true,
meal_plan_days: 0,
recently_viewed: 5,
sort_by_new: true,
- pagination_page: 1,
- page_count: 25,
+ page_size: 25,
+ remember_search: true,
+ remember_hours: 4,
+ sql_debug: false,
+ tree_select: false,
},
-
pagination_count: 0,
random_search: false,
debug: false,
@@ -314,47 +358,35 @@ export default {
{ id: 0, label: this.$t("Unrated") + ratingCount(this.facets.Ratings?.["0.0"] ?? 0) },
]
},
- searchFiltered: function () {
- if (
- this.settings?.search_input === "" &&
- this.settings?.search_keywords?.length === 0 &&
- this.settings?.search_foods?.length === 0 &&
- this.settings?.search_books?.length === 0 &&
- // this.settings?.pagination_page === 1 &&
- !this.random_search &&
- this.settings?.search_ratings === undefined
- ) {
- return false
- } else {
- return true
- }
- },
},
mounted() {
this.$nextTick(function () {
- if (this.$cookies.isKey(SETTINGS_COOKIE_NAME)) {
- this.settings = Object.assign({}, this.settings, this.$cookies.get(SETTINGS_COOKIE_NAME))
+ if (this.$cookies.isKey(UI_COOKIE_NAME)) {
+ this.ui = Object.assign({}, this.ui, this.$cookies.get(UI_COOKIE_NAME))
+ }
+ if (this.ui.remember_search && this.$cookies.isKey(SEARCH_COOKIE_NAME)) {
+ this.search = Object.assign({}, this.search, this.$cookies.get(SEARCH_COOKIE_NAME), `${this.ui.remember_hours}h`)
}
let urlParams = new URLSearchParams(window.location.search)
if (urlParams.has("keyword")) {
- this.settings.search_keywords = []
+ this.search.search_keywords = []
this.facets.Keywords = []
for (let x of urlParams.getAll("keyword")) {
- this.settings.search_keywords.push(Number.parseInt(x))
+ this.search.search_keywords.push(Number.parseInt(x))
this.facets.Keywords.push({ id: x, name: "loading..." })
}
}
this.facets.Foods = []
- for (let x of this.settings.search_foods) {
+ for (let x of this.search.search_foods) {
this.facets.Foods.push({ id: x, name: "loading..." })
}
this.facets.Keywords = []
- for (let x of this.settings.search_keywords) {
+ for (let x of this.search.search_keywords) {
this.facets.Keywords.push({ id: x, name: "loading..." })
}
this.facets.Books = []
- for (let x of this.settings.search_books) {
+ for (let x of this.search.search_books) {
this.facets.Books.push({ id: x, name: "loading..." })
}
this.loadMealPlan()
@@ -364,27 +396,39 @@ export default {
this.debug = localStorage.getItem("DEBUG") == "True" || false
},
watch: {
- settings: {
+ search: {
handler() {
- this.$cookies.set(SETTINGS_COOKIE_NAME, this.settings, "4h")
+ this.$cookies.set(SEARCH_COOKIE_NAME, this.search, `${this.ui.remember_hours}h`)
},
deep: true,
},
- "settings.show_meal_plan": function () {
+ ui: {
+ handler() {
+ this.$cookies.set(UI_COOKIE_NAME, this.ui)
+ },
+ deep: true,
+ },
+ "ui.show_meal_plan": function () {
this.loadMealPlan()
},
- "settings.meal_plan_days": function () {
+ "ui.meal_plan_days": function () {
this.loadMealPlan()
},
- "settings.recently_viewed": function () {
+ "ui.recently_viewed": function () {
this.refreshData(false)
},
- "settings.search_input": _debounce(function () {
- this.settings.pagination_page = 1
+ "ui.tree_select": function () {
+ if (this.ui.tree_select && !this.facets?.Keywords && !this.facets?.Foods) {
+ console.log("i changed to true")
+ this.getFacets(this.facets?.hash)
+ }
+ },
+ "search.search_input": _debounce(function () {
+ this.search.pagination_page = 1
this.pagination_count = 0
this.refreshData(false)
}, 300),
- "settings.page_count": _debounce(function () {
+ "ui.page_size": _debounce(function () {
this.refreshData(false)
}, 300),
},
@@ -393,24 +437,24 @@ export default {
refreshData: function (random) {
this.random_search = random
let params = {
- query: this.settings.search_input,
- keywords: this.settings.search_keywords,
- foods: this.settings.search_foods,
- rating: this.settings.search_ratings,
- books: this.settings.search_books.map(function (A) {
+ query: this.search.search_input,
+ keywords: this.search.search_keywords,
+ foods: this.search.search_foods,
+ rating: this.search.search_ratings,
+ books: this.search.search_books.map(function (A) {
return A["id"]
}),
- keywordsOr: this.settings.search_keywords_or,
- foodsOr: this.settings.search_foods_or,
- booksOr: this.settings.search_books_or,
- internal: this.settings.search_internal,
+ keywordsOr: this.search.search_keywords_or,
+ foodsOr: this.search.search_foods_or,
+ booksOr: this.search.search_books_or,
+ internal: this.search.search_internal,
random: this.random_search,
- _new: this.settings.sort_by_new,
- page: this.settings.pagination_page,
- pageSize: this.settings.page_count,
+ _new: this.ui.sort_by_new,
+ page: this.search.pagination_page,
+ pageSize: this.search.page_size,
}
if (!this.searchFiltered) {
- params.options = { query: { last_viewed: this.settings.recently_viewed } }
+ params.options = { query: { last_viewed: this.ui.recently_viewed } }
}
this.genericAPI(this.Models.RECIPE, this.Actions.LIST, params)
.then((result) => {
@@ -418,9 +462,6 @@ export default {
this.pagination_count = result.data.count
this.facets = result.data.facets
- // if (this.facets?.cache_key) {
- // this.getFacets(this.facets.cache_key)
- // }
this.recipes = this.removeDuplicates(result.data.results, (recipe) => recipe.id)
if (!this.searchFiltered) {
// if meal plans are being shown - filter out any meal plan recipes from the recipe list
@@ -442,12 +483,12 @@ export default {
return [...new Map(data.map((item) => [key(item), item])).values()]
},
loadMealPlan: function () {
- if (this.settings.show_meal_plan) {
+ if (this.ui.show_meal_plan) {
let params = {
options: {
query: {
from_date: moment().format("YYYY-MM-DD"),
- to_date: moment().add(this.settings.meal_plan_days, "days").format("YYYY-MM-DD"),
+ to_date: moment().add(this.ui.meal_plan_days, "days").format("YYYY-MM-DD"),
},
},
}
@@ -459,26 +500,24 @@ export default {
}
},
genericSelectChanged: function (obj) {
- this.settings[obj.var] = obj.val
+ this.search[obj.var] = obj.val
this.refreshData(false)
},
resetSearch: function () {
- this.settings.search_input = ""
- this.settings.search_internal = false
- this.settings.search_keywords = []
- this.settings.search_foods = []
- this.settings.search_books = []
- this.settings.search_ratings = undefined
- this.settings.pagination_page = 1
+ this.search.search_input = ""
+ this.search.search_internal = false
+ this.search.search_keywords = []
+ this.search.search_foods = []
+ this.search.search_books = []
+ this.search.search_ratings = undefined
+ this.search.pagination_page = 1
this.refreshData(false)
},
pageChange: function (page) {
- this.settings.pagination_page = page
+ this.search.pagination_page = page
this.refreshData(false)
},
- isAdvancedSettingsSet() {
- return this.settings.search_keywords.length + this.settings.search_foods.length + this.settings.search_books.length > 0
- },
+
normalizer(node) {
let count = node?.count ? " (" + node.count + ")" : ""
return {
@@ -500,6 +539,9 @@ export default {
}
},
getFacets: function (hash, facet, id) {
+ if (!this.ui.tree_select) {
+ return
+ }
let params = { hash: hash }
if (facet) {
params[facet] = id
@@ -511,51 +553,59 @@ export default {
showSQL: function () {
// TODO refactor this so that it isn't a total copy of refreshData
let params = {
- query: this.settings.search_input,
- keywords: this.settings.search_keywords,
- foods: this.settings.search_foods,
- rating: this.settings.search_ratings,
- books: this.settings.search_books.map(function (A) {
+ query: this.search.search_input,
+ keywords: this.search.search_keywords,
+ foods: this.search.search_foods,
+ rating: this.search.search_ratings,
+ books: this.search.search_books.map(function (A) {
return A["id"]
}),
- keywordsOr: this.settings.search_keywords_or,
- foodsOr: this.settings.search_foods_or,
- booksOr: this.settings.search_books_or,
- internal: this.settings.search_internal,
+ keywordsOr: this.search.search_keywords_or,
+ foodsOr: this.search.search_foods_or,
+ booksOr: this.search.search_books_or,
+ internal: this.search.search_internal,
random: this.random_search,
- _new: this.settings.sort_by_new,
- page: this.settings.pagination_page,
- pageSize: this.settings.page_count,
+ _new: this.ui.sort_by_new,
+ page: this.search.pagination_page,
+ pageSize: this.ui.page_size,
}
if (!this.searchFiltered) {
- params.options = { query: { last_viewed: this.settings.recently_viewed, debug: true } }
+ params.options = { query: { last_viewed: this.ui.recently_viewed, debug: true } }
} else {
params.options = { query: { debug: true } }
}
this.genericAPI(this.Models.RECIPE, this.Actions.LIST, params).then((result) => {})
},
+ // TODO remove
loadFoodChildren({ action, parentNode, callback }) {
- // Typically, do the AJAX stuff here.
- // Once the server has responded,
- // assign children options to the parent node & call the callback.
-
if (action === LOAD_CHILDREN_OPTIONS) {
if (this.facets?.cache_key) {
this.getFacets(this.facets.cache_key, "food", parentNode.id).then(callback())
}
}
},
+ // TODO remove
loadKeywordChildren({ action, parentNode, callback }) {
- // Typically, do the AJAX stuff here.
- // Once the server has responded,
- // assign children options to the parent node & call the callback.
-
if (action === LOAD_CHILDREN_OPTIONS) {
if (this.facets?.cache_key) {
this.getFacets(this.facets.cache_key, "keyword", parentNode.id).then(callback())
}
}
},
+ searchFiltered: function (ignore_string = false) {
+ let filtered =
+ this.search?.search_keywords?.length === 0 &&
+ this.search?.search_foods?.length === 0 &&
+ this.search?.search_books?.length === 0 &&
+ // this.settings?.pagination_page === 1 &&
+ !this.random_search &&
+ this.search?.search_ratings === undefined
+ if (ignore_string) {
+ return filtered
+ } else {
+ return filtered && this.search?.search_input === ""
+ }
+ },
},
}
diff --git a/vue/src/components/GenericMultiselect.vue b/vue/src/components/GenericMultiselect.vue
index 125034407..7e11467e1 100644
--- a/vue/src/components/GenericMultiselect.vue
+++ b/vue/src/components/GenericMultiselect.vue
@@ -7,7 +7,7 @@
:hide-selected="multiple"
:preserve-search="true"
:internal-search="false"
- :limit="options_limit"
+ :limit="limit"
:placeholder="lookupPlaceholder"
:label="label"
track-by="id"
@@ -36,7 +36,6 @@ export default {
loading: false,
objects: [],
selected_objects: [],
- options_limit: 25,
}
},
props: {
@@ -90,9 +89,9 @@ export default {
search: function (query) {
let options = {
page: 1,
- pageSize: 10,
+ pageSize: this.limit,
query: query,
- limit: this.options_limit,
+ limit: this.limit,
}
this.genericAPI(this.model, this.Actions.LIST, options).then((result) => {
this.objects = this.sticky_options.concat(result.data?.results ?? result.data)
diff --git a/vue/src/locales/en.json b/vue/src/locales/en.json
index 180354df8..96b10040d 100644
--- a/vue/src/locales/en.json
+++ b/vue/src/locales/en.json
@@ -283,5 +283,9 @@
"shopping_add_onhand_desc": "Mark food 'On Hand' when checked off shopping list.",
"shopping_add_onhand": "Auto On Hand",
"related_recipes": "Related Recipes",
- "today_recipes": "Today's Recipes"
+ "today_recipes": "Today's Recipes",
+ "sql_debug": "SQL Debug",
+ "remember_search": "Remember Search",
+ "remember_hours": "Hours to Remember",
+ "tree_select": "Use Tree Selection"
}