further improvements to search view

This commit is contained in:
vabene1111
2021-06-28 18:35:20 +02:00
parent 78885987f0
commit 272341f1dc
14 changed files with 208 additions and 52 deletions

View File

@@ -15,7 +15,9 @@
<b-input-group class="mt-3">
<b-input class="form-control" v-model="settings.search_input" v-bind:placeholder="$t('Search')"></b-input>
<b-input-group-append>
<b-button v-b-toggle.collapse_advanced_search v-bind:class="{'btn-primary': !isAdvancedSettingsSet(), 'btn-danger': isAdvancedSettingsSet()}" class="shadow-none btn"><i
<b-button v-b-toggle.collapse_advanced_search
v-bind:class="{'btn-primary': !isAdvancedSettingsSet(), 'btn-danger': isAdvancedSettingsSet()}"
class="shadow-none btn"><i
class="fas fa-caret-down" v-if="!settings.advanced_search_visible"></i><i class="fas fa-caret-up"
v-if="settings.advanced_search_visible"></i>
</b-button>
@@ -37,21 +39,16 @@
:href="resolveDjangoUrl('data_import_url')">{{ $t('Import') }}</a>
</div>
<div class="col-md-3" style="margin-top: 1vh">
<button class="btn btn-primary btn-block text-uppercase" @click="resetSearch">
{{ $t('Reset_Search') }}
<button 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}"
@click="settings.search_internal = !settings.search_internal;refreshData()">
{{ $t('Internal') }}
</button>
</div>
<div class="col-md-2" style="position: relative; margin-top: 1vh">
<b-form-checkbox v-model="settings.search_internal" name="check-button"
@change="refreshData(false)"
class="shadow-none"
style="position:relative;top: 50%; transform: translateY(-50%);" switch>
{{ $t('show_only_internal') }}
</b-form-checkbox>
</div>
<div class="col-md-1" style="position: relative; margin-top: 1vh">
<button id="id_settings_button" class="btn btn-primary btn-block"><i class="fas fa-cog"></i>
<div class="col-md-3" style="position: relative; margin-top: 1vh">
<button id="id_settings_button" class="btn btn-primary btn-block text-uppercase"><i
class="fas fa-cog"></i>
</button>
</div>
@@ -175,7 +172,16 @@
</div>
</div>
<div class="row" style="margin-top: 2vh">
<div class="row">
<div class="col col-md-12 text-right" style="margin-top: 2vh">
<span class="text-muted">
{{ $t('Page') }} {{ settings.pagination_page }}/{{ pagination_count }} <a href="#" @click="resetSearch"><i
class="fas fa-times-circle"></i> {{ $t('Reset') }}</a>
</span>
</div>
</div>
<div class="row">
<div class="col col-md-12">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));grid-gap: 1rem;">
@@ -194,10 +200,16 @@
</div>
</div>
<div class="row" style="margin-top: 2vh; text-align: center">
<div class="row" style="margin-top: 2vh">
<div class="col col-md-12">
<b-button @click="loadMore()" class="btn-block btn-success" v-if="pagination_more">{{ $t('Load_More') }}
</b-button>
<b-pagination pills
v-model="settings.pagination_page"
:total-rows="pagination_count"
per-page="25"
@change="pageChange"
align="center">
</b-pagination>
</div>
</div>
@@ -233,6 +245,8 @@ import GenericMultiselect from "@/components/GenericMultiselect";
Vue.use(BootstrapVue)
let SETTINGS_COOKIE_NAME = 'search_settings'
export default {
name: 'RecipeSearchView',
mixins: [ResolveUrlMixin],
@@ -243,6 +257,7 @@ export default {
meal_plans: [],
last_viewed_recipes: [],
settings_loaded: false,
settings: {
search_input: '',
search_internal: false,
@@ -256,19 +271,33 @@ export default {
advanced_search_visible: false,
show_meal_plan: true,
recently_viewed: 5,
pagination_page: 1,
},
pagination_more: true,
pagination_page: 1,
pagination_count: 0,
}
},
mounted() {
this.$nextTick(function () {
if (this.$cookies.isKey('search_settings_v2')) {
this.settings = this.$cookies.get("search_settings_v2")
if (this.$cookies.isKey(SETTINGS_COOKIE_NAME)) {
let cookie_val = this.$cookies.get(SETTINGS_COOKIE_NAME)
for (let i of Object.keys(cookie_val)) {
this.$set(this.settings, i, cookie_val[i])
}
//TODO i have no idea why the above code does not suffice to update the
//TODO pagination UI element as $set should update all values reactively but it does not
setTimeout(function () {
this.$set(this.settings, 'pagination_page', 0)
}.bind(this), 50)
setTimeout(function () {
this.$set(this.settings, 'pagination_page', cookie_val['pagination_page'])
}.bind(this), 51)
}
let urlParams = new URLSearchParams(window.location.search);
let apiClient = new ApiApiFactory()
@@ -278,7 +307,7 @@ export default {
let keyword = {id: x, name: 'loading'}
this.settings.search_keywords.push(keyword)
apiClient.retrieveKeyword(x).then(result => {
this.$set(this.settings.search_keywords,this.settings.search_keywords.indexOf(keyword), result.data)
this.$set(this.settings.search_keywords, this.settings.search_keywords.indexOf(keyword), result.data)
})
}
}
@@ -293,7 +322,7 @@ export default {
watch: {
settings: {
handler() {
this.$cookies.set("search_settings_v2", this.settings, -1)
this.$cookies.set(SETTINGS_COOKIE_NAME, this.settings, -1)
},
deep: true
},
@@ -327,16 +356,11 @@ export default {
this.settings.search_internal,
undefined,
this.pagination_page,
this.settings.pagination_page,
).then(result => {
this.pagination_more = (result.data.next !== null)
if (page_load) {
for (let x of result.data.results) {
this.recipes.push(x)
}
} else {
this.recipes = result.data.results
}
window.scrollTo(0, 0);
this.pagination_count = result.data.count
this.recipes = result.data.results
})
},
loadMealPlan: function () {
@@ -378,13 +402,14 @@ export default {
this.settings.search_keywords = []
this.settings.search_foods = []
this.settings.search_books = []
this.settings.pagination_page = 1
this.refreshData(false)
},
loadMore: function (page) {
this.pagination_page++
this.refreshData(true)
pageChange: function (page) {
this.settings.pagination_page = page
this.refreshData()
},
isAdvancedSettingsSet(){
isAdvancedSettingsSet() {
return ((this.settings.search_keywords.length + this.settings.search_foods.length + this.settings.search_books.length) > 0)
}
}

View File

@@ -11,6 +11,13 @@
</div>
</div>
<div class="row text-center">
<div class="col col-md-12">
<recipe-rating :recipe="recipe"></recipe-rating> <br/>
<last-cooked :recipe="recipe"></last-cooked>
</div>
</div>
<div class="my-auto">
<div class="col-12" style="text-align: center">
<i>{{ recipe.description }}</i>
@@ -166,6 +173,8 @@ import moment from 'moment'
import Keywords from "@/components/Keywords";
import LoadingSpinner from "@/components/LoadingSpinner";
import AddRecipeToBook from "@/components/AddRecipeToBook";
import RecipeRating from "@/components/RecipeRating";
import LastCooked from "@/components/LastCooked";
Vue.prototype.moment = moment
@@ -178,6 +187,8 @@ export default {
ToastMixin,
],
components: {
LastCooked,
RecipeRating,
PdfViewer,
ImageViewer,
Ingredient,

View File

@@ -0,0 +1,28 @@
<template>
<span>
<b-badge pill variant="primary" v-if="recipe.last_cooked !== null"><i class="fas fa-utensils"></i> {{
formatDate(recipe.last_cooked)
}}</b-badge>
</span>
</template>
<script>
import moment from "moment/moment";
export default {
name: "LastCooked",
props: {
recipe: Object
},
methods: {
formatDate: function (datetime) {
moment.locale(window.navigator.language);
return moment(datetime).format('L')
}
}
}
</script>
<style scoped>
</style>

View File

@@ -7,7 +7,9 @@
top></b-card-img-lazy>
<div class="card-img-overlay h-100 d-flex flex-column justify-content-right"
style="float:right; text-align: right; padding-top: 10px; padding-right: 5px">
<a><recipe-context-menu :recipe="recipe" style="float:right" v-if="recipe !== null"></recipe-context-menu></a>
<a>
<recipe-context-menu :recipe="recipe" style="float:right" v-if="recipe !== null"></recipe-context-menu>
</a>
</div>
</a>
@@ -21,12 +23,18 @@
<b-card-text style="text-overflow: ellipsis;">
<template v-if="recipe !== null">
{{ recipe.description }}
<recipe-rating :recipe="recipe"></recipe-rating> <br/> <!-- TODO UGLY! -->
<last-cooked :recipe="recipe"></last-cooked>
<keywords :recipe="recipe" style="margin-top: 4px"></keywords>
<b-badge pill variant="info" v-if="!recipe.internal">{{ $t('External') }}</b-badge>
<b-badge pill variant="success"
v-if="Date.parse(recipe.created_at) > new Date(Date.now() - (7 * (1000 * 60 * 60 * 24)))">
{{ $t('New') }}
</b-badge>
</template>
<template v-else>{{ meal_plan.note }}</template>
</b-card-text>
@@ -44,13 +52,19 @@
import RecipeContextMenu from "@/components/RecipeContextMenu";
import Keywords from "@/components/Keywords";
import {resolveDjangoUrl, ResolveUrlMixin} from "@/utils/utils";
import RecipeRating from "@/components/RecipeRating";
import moment from "moment/moment";
import Vue from "vue";
import LastCooked from "@/components/LastCooked";
Vue.prototype.moment = moment
export default {
name: "RecipeCard",
mixins: [
ResolveUrlMixin,
],
components: {Keywords, RecipeContextMenu},
components: {LastCooked, RecipeRating, Keywords, RecipeContextMenu},
props: {
recipe: Object,
meal_plan: Object,

View File

@@ -0,0 +1,24 @@
<template>
<div>
<span style="display: inline-block;" v-if="recipe.rating > 0">
<i class="fas fa-star fa-xs" v-for="i in Math.floor(recipe.rating)" v-bind:key="i"></i>
<i class="fas fa-star-half-alt fa-xs" v-if="recipe.rating % 1 > 0"></i>
</span>
</div>
</template>
<script>
export default {
name: "RecipeRating",
props: {
recipe: Object
}
}
</script>
<style scoped>
</style>

View File

@@ -564,7 +564,19 @@ export interface MealPlanRecipe {
* @type {string}
* @memberof MealPlanRecipe
*/
file_path?: string;
servings_text?: string;
/**
*
* @type {string}
* @memberof MealPlanRecipe
*/
rating?: string;
/**
*
* @type {string}
* @memberof MealPlanRecipe
*/
last_cooked?: string;
}
/**
*
@@ -699,6 +711,18 @@ export interface Recipe {
* @memberof Recipe
*/
servings_text?: string;
/**
*
* @type {string}
* @memberof Recipe
*/
rating?: string;
/**
*
* @type {string}
* @memberof Recipe
*/
last_cooked?: string;
}
/**
*
@@ -956,7 +980,19 @@ export interface RecipeOverview {
* @type {string}
* @memberof RecipeOverview
*/
file_path?: string;
servings_text?: string;
/**
*
* @type {string}
* @memberof RecipeOverview
*/
rating?: string;
/**
*
* @type {string}
* @memberof RecipeOverview
*/
last_cooked?: string;
}
/**
*