mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-01 04:10:06 -05:00
test include async results in global search
This commit is contained in:
@@ -20,6 +20,7 @@
|
|||||||
<v-text-field
|
<v-text-field
|
||||||
id="id_global_search_input"
|
id="id_global_search_input"
|
||||||
v-model="searchQuery"
|
v-model="searchQuery"
|
||||||
|
@update:modelValue="debouncedAsyncSearch"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
clearable
|
clearable
|
||||||
placeholder="Search"
|
placeholder="Search"
|
||||||
@@ -30,7 +31,7 @@
|
|||||||
<v-card :variant="cardVariant(index)" v-for="(item, index) in searchResults" hover class="mt-1" @click="selectedResult = index" :key="index">
|
<v-card :variant="cardVariant(index)" v-for="(item, index) in searchResults" hover class="mt-1" @click="selectedResult = index" :key="index">
|
||||||
<v-card-title @click="goToSelectedRecipe(index)">
|
<v-card-title @click="goToSelectedRecipe(index)">
|
||||||
<v-avatar v-if="item.image" :image="item.image"></v-avatar>
|
<v-avatar v-if="item.image" :image="item.image"></v-avatar>
|
||||||
<v-avatar v-else-if="item.recipe_id !== undefined" color="tandoor">{{ item.name.charAt(0) }}</v-avatar>
|
<v-avatar v-else-if="item.recipeId !== undefined" color="tandoor">{{ item.name.charAt(0) }}</v-avatar>
|
||||||
<v-icon :icon="item.icon" v-if="item.icon"></v-icon>
|
<v-icon :icon="item.icon" v-if="item.icon"></v-icon>
|
||||||
{{ item.name }}
|
{{ item.name }}
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
@@ -50,7 +51,7 @@
|
|||||||
|
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
<v-card-actions>
|
<v-card-actions>
|
||||||
<v-btn @click="dialog=false" variant="plain">{{$t('Close')}}</v-btn>
|
<v-btn @click="dialog=false" variant="plain">{{ $t('Close') }}</v-btn>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
|
||||||
@@ -60,12 +61,14 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
import {computed, defineComponent, onMounted, ref, watch} from 'vue'
|
import {computed, onMounted, ref, watch} from 'vue'
|
||||||
import {SearchResult} from "@/types/SearchTypes";
|
import {SearchResult} from "@/types/SearchTypes";
|
||||||
import {ApiApi, Recipe, RecipeFlat} from "@/openapi";
|
import {ApiApi, Recipe, RecipeFlat, RecipeOverview} from "@/openapi";
|
||||||
import {useRouter} from "vue-router";
|
import {useRouter} from "vue-router";
|
||||||
import {useDisplay} from "vuetify";
|
import {useDisplay} from "vuetify";
|
||||||
import VClosableCardTitle from "@/components/dialogs/VClosableCardTitle.vue";
|
import VClosableCardTitle from "@/components/dialogs/VClosableCardTitle.vue";
|
||||||
|
import {useDebounceFn} from "@vueuse/core";
|
||||||
|
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const {mobile} = useDisplay()
|
const {mobile} = useDisplay()
|
||||||
@@ -75,32 +78,48 @@ const recipes = ref([] as Recipe[])
|
|||||||
const flatRecipes = ref([] as RecipeFlat[])
|
const flatRecipes = ref([] as RecipeFlat[])
|
||||||
const searchQuery = ref(null as string | null)
|
const searchQuery = ref(null as string | null)
|
||||||
const selectedResult = ref(0)
|
const selectedResult = ref(0)
|
||||||
|
const asyncSearchResults = ref([] as RecipeOverview[])
|
||||||
|
|
||||||
|
const flatListLoading = ref(false)
|
||||||
|
const asyncLoading = ref(false)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* build array of search results
|
* build array of search results
|
||||||
* uses custom type to be able to incorporate recent items, plans, books, ... at a later stage
|
* uses custom type to be able to incorporate recent items, plans, books, ... at a later stage
|
||||||
*/
|
*/
|
||||||
const searchResults = computed(() => {
|
const searchResults = computed(() => {
|
||||||
let search_results = [] as Array<SearchResult>
|
let searchResults = [] as Array<SearchResult>
|
||||||
|
|
||||||
if (searchQuery.value != '' && searchQuery.value != null) {
|
if (searchQuery.value != '' && searchQuery.value != null) {
|
||||||
search_results.push({name: searchQuery.value, icon: 'fas fa-search', suffix: 'Advanced Search'} as SearchResult)
|
// TODO add link to advanced search once it exists
|
||||||
|
//searchResults.push({name: searchQuery.value, icon: 'fas fa-search', suffix: 'Advanced Search'} as SearchResult)
|
||||||
|
|
||||||
flatRecipes.value.filter(fr => fr.name.toLowerCase().includes(searchQuery.value.toLowerCase())).slice(0, 10).forEach(r => {
|
flatRecipes.value.filter(fr => fr.name.toLowerCase().includes(searchQuery.value.toLowerCase())).slice(0, 10).forEach(r => {
|
||||||
search_results.push({name: r.name, image: r.image, recipe_id: r.id} as SearchResult)
|
searchResults.push({name: r.name, image: r.image, recipeId: r.id} as SearchResult)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (searchResults.length < 3) {
|
||||||
|
asyncSearchResults.value.slice(0, 5).forEach(r => {
|
||||||
|
if (searchResults.findIndex(x => x.recipeId == r.id) == -1) {
|
||||||
|
searchResults.push({name: r.name, image: r.image, recipeId: r.id, icon: 'fa-solid fa-globe'})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// search_results.push({name: 'Recent 1', icon: 'fas fa-history',} as SearchResult)
|
// show first 5 recipes by default
|
||||||
// search_results.push({name: 'Recent 2', icon: 'fas fa-history',} as SearchResult)
|
|
||||||
// search_results.push({name: 'Recent 3', icon: 'fas fa-history',} as SearchResult)
|
// TODO special "quick links" if applicable
|
||||||
|
// searchResults.push({name: 'Recent 1', icon: 'fas fa-history',} as SearchResult)
|
||||||
|
// searchResults.push({name: 'Recent 2', icon: 'fas fa-history',} as SearchResult)
|
||||||
|
// searchResults.push({name: 'Recent 3', icon: 'fas fa-history',} as SearchResult)
|
||||||
|
|
||||||
flatRecipes.value.slice(0, 5).forEach(r => {
|
flatRecipes.value.slice(0, 5).forEach(r => {
|
||||||
search_results.push({name: r.name, image: r.image, recipe_id: r.id} as SearchResult)
|
searchResults.push({name: r.name, image: r.image, recipeId: r.id} as SearchResult)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return search_results
|
return searchResults
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(dialog, (newValue) => {
|
watch(dialog, (newValue) => {
|
||||||
@@ -147,12 +166,34 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
flatListLoading.value = true
|
||||||
const api = new ApiApi()
|
const api = new ApiApi()
|
||||||
api.apiRecipeFlatList().then(r => {
|
api.apiRecipeFlatList().then(r => {
|
||||||
flatRecipes.value = r
|
flatRecipes.value = r
|
||||||
|
}).catch(err => {
|
||||||
|
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
|
||||||
|
}).finally(() => {
|
||||||
|
flatListLoading.value = false
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* search for query on server but debounce search so its not searched on every keypress
|
||||||
|
*/
|
||||||
|
const debouncedAsyncSearch = useDebounceFn(() => {
|
||||||
|
if (searchQuery.value != null && searchQuery.value != '') {
|
||||||
|
let api = new ApiApi()
|
||||||
|
asyncLoading.value = true
|
||||||
|
api.apiRecipeList({query: searchQuery.value}).then(r => {
|
||||||
|
asyncSearchResults.value = r.results
|
||||||
|
}).catch(err => {
|
||||||
|
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
|
||||||
|
}).finally(() => {
|
||||||
|
asyncLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, 300)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* determines the style for selected elements
|
* determines the style for selected elements
|
||||||
* @param index index of card to determine style for
|
* @param index index of card to determine style for
|
||||||
@@ -171,9 +212,9 @@ function cardVariant(index: number) {
|
|||||||
function goToSelectedRecipe(index: number) {
|
function goToSelectedRecipe(index: number) {
|
||||||
dialog.value = false
|
dialog.value = false
|
||||||
let searchResult = searchResults.value[index]
|
let searchResult = searchResults.value[index]
|
||||||
console.log('going to', searchResult.recipe_id)
|
console.log('going to', searchResult.recipeId)
|
||||||
if (searchResult.recipe_id != null) {
|
if (searchResult.recipeId != null) {
|
||||||
router.push({name: 'view_recipe', params: {'id': searchResult.recipe_id}})
|
router.push({name: 'view_recipe', params: {'id': searchResult.recipeId}})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
export interface SearchResult {
|
export interface SearchResult {
|
||||||
name: string,
|
name: string,
|
||||||
recipe_id?: number,
|
recipeId?: number,
|
||||||
suffix?: string,
|
suffix?: string,
|
||||||
description?: string,
|
description?: string,
|
||||||
icon?: string,
|
icon?: string,
|
||||||
image?: string,
|
image?: string|null,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user