use debounce search on search page

This commit is contained in:
Ruidy 2024-12-14 12:35:40 +01:00
parent 72824daf63
commit 6f5f66d02e
No known key found for this signature in database
GPG key ID: E00F51288CB857CC
4 changed files with 93 additions and 62 deletions

View file

@ -1,6 +1,8 @@
<script setup lang="ts">
const router = useRouter();
const route = useRoute();
const searchQuery = ref((route.query.q as string) || "");
const { execute } = useRecipeRandom();
const handleRandomClick = async () => {
@ -9,6 +11,31 @@ const handleRandomClick = async () => {
}
await execute();
};
const debouncedSearch = useDebounceFn(async (query: string) => {
if (searchQuery.value.trim()) {
router.push({
path: "/search",
query: { q: query.trim() },
});
}
}, 500);
const handleSubmit = () => {
if (searchQuery.value.trim()) {
router.push({
path: "/search",
query: { q: searchQuery.value.trim() },
});
}
};
if (route.path === "/search") {
// Watch for changes in searchQuery
watch(searchQuery, (newQuery) => {
debouncedSearch(newQuery);
});
}
</script>
<template>
@ -49,7 +76,7 @@ const handleRandomClick = async () => {
</ul>
</div>
<div class="navbar-end gap-2">
<recipe-search />
<recipe-search @search="handleSubmit" v-model="searchQuery" />
<button class="btn btn-primary" @click="handleRandomClick">Random</button>
</div>
</nav>

View file

@ -5,10 +5,11 @@
<input
type="text"
class="grow"
placeholder="Search"
v-model="searchQuery"
placeholder="Search recipes..."
v-model="model"
@focus="isFocused = true"
@blur="isFocused = false"
@keydown.enter="$emit('search')"
/>
<kbd class="kbd kbd-sm" :class="{ 'opacity-50': !isFocused }"></kbd>
<kbd class="kbd kbd-sm" :class="{ 'opacity-50': !isFocused }">K</kbd>
@ -16,21 +17,11 @@
</template>
<script setup lang="ts">
const searchQuery = ref("");
defineEmits(["search"]);
const model = defineModel<string>();
const isFocused = ref(false);
// Debounced search function
const debouncedSearch = useDebounceFn(async (query: string) => {
const { data, status, error } = await useRecipeSearch(query);
console.log("result", data.value, status.value, error.value);
}, 500);
// Watch for changes in searchQuery
watch(searchQuery, (newQuery) => {
debouncedSearch(newQuery);
});
// Optional: Handle keyboard shortcut
onMounted(() => {
const handleKeydown = (e: KeyboardEvent) => {
if ((e.metaKey || e.ctrlKey) && e.key === "k") {

View file

@ -1,13 +1,26 @@
<script setup lang="ts">
import type { Recipe } from "~/types/recipe";
const route = useRoute();
const searchQuery = computed(() => route.query.q as string);
const searchResults = ref<Recipe[]>([]);
const { data, status, error, execute } = await useRecipeSearch(
searchQuery.value || "",
);
const { data, status, error } = await useRecipeSearch(searchQuery.value || "");
searchResults.value = data.value;
watch(searchQuery, async (newQuery) => {
const { data, status, error, execute } = await useRecipeSearch(
newQuery.trim(),
);
searchResults.value = data.value;
});
</script>
<template>
<div class="container mx-auto px-4">
<div v-if="status" class="flex justify-center my-8">
<div v-if="false" class="flex justify-center my-8">
<span class="loading loading-spinner loading-lg text-primary"></span>
</div>
@ -33,7 +46,7 @@ const { data, status, error } = await useRecipeSearch(searchQuery.value || "");
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 my-8"
>
<div
v-for="recipe in data"
v-for="recipe in searchResults"
:key="recipe.id"
class="card bg-base-100 shadow-xl"
>
@ -42,8 +55,8 @@ const { data, status, error } = await useRecipeSearch(searchQuery.value || "");
<h2 class="card-title">{{ recipe.title }}</h2>
<p>{{ recipe.category }} {{ recipe.origin }}</p>
<div class="card-actions justify-end">
<NuxtLink :to="`/recipe/${recipe.id}`" class="btn btn-primary"
>View Recipe</NuxtLink
<nuxt-link :to="`/${recipe.id}`" class="btn btn-primary"
>View Recipe</nuxt-link
>
</div>
</div>

View file

@ -21,47 +21,47 @@ const mealSchema = z.object({
strMealThumb: z.string().url(),
strTags: z.string().nullable(),
strYoutube: z.string(),
strIngredient1: z.string().optional(),
strIngredient2: z.string().optional(),
strIngredient3: z.string().optional(),
strIngredient4: z.string().optional(),
strIngredient5: z.string().optional(),
strIngredient6: z.string().optional(),
strIngredient7: z.string().optional(),
strIngredient8: z.string().optional(),
strIngredient9: z.string().optional(),
strIngredient10: z.string().optional(),
strIngredient11: z.string().optional(),
strIngredient12: z.string().optional(),
strIngredient13: z.string().optional(),
strIngredient14: z.string().optional(),
strIngredient15: z.string().optional(),
strIngredient16: z.string().optional(),
strIngredient17: z.string().optional(),
strIngredient18: z.string().optional(),
strIngredient19: z.string().optional(),
strIngredient20: z.string().optional(),
strMeasure1: z.string().optional(),
strMeasure2: z.string().optional(),
strMeasure3: z.string().optional(),
strMeasure4: z.string().optional(),
strMeasure5: z.string().optional(),
strMeasure6: z.string().optional(),
strMeasure7: z.string().optional(),
strMeasure8: z.string().optional(),
strMeasure9: z.string().optional(),
strMeasure10: z.string().optional(),
strMeasure11: z.string().optional(),
strMeasure12: z.string().optional(),
strMeasure13: z.string().optional(),
strMeasure14: z.string().optional(),
strMeasure15: z.string().optional(),
strMeasure16: z.string().optional(),
strMeasure17: z.string().optional(),
strMeasure18: z.string().optional(),
strMeasure19: z.string().optional(),
strMeasure20: z.string().optional(),
strSource: z.string().optional(),
strIngredient1: z.string().nullish(),
strIngredient2: z.string().nullish(),
strIngredient3: z.string().nullish(),
strIngredient4: z.string().nullish(),
strIngredient5: z.string().nullish(),
strIngredient6: z.string().nullish(),
strIngredient7: z.string().nullish(),
strIngredient8: z.string().nullish(),
strIngredient9: z.string().nullish(),
strIngredient10: z.string().nullish(),
strIngredient11: z.string().nullish(),
strIngredient12: z.string().nullish(),
strIngredient13: z.string().nullish(),
strIngredient14: z.string().nullish(),
strIngredient15: z.string().nullish(),
strIngredient16: z.string().nullish(),
strIngredient17: z.string().nullish(),
strIngredient18: z.string().nullish(),
strIngredient19: z.string().nullish(),
strIngredient20: z.string().nullish(),
strMeasure1: z.string().nullish(),
strMeasure2: z.string().nullish(),
strMeasure3: z.string().nullish(),
strMeasure4: z.string().nullish(),
strMeasure5: z.string().nullish(),
strMeasure6: z.string().nullish(),
strMeasure7: z.string().nullish(),
strMeasure8: z.string().nullish(),
strMeasure9: z.string().nullish(),
strMeasure10: z.string().nullish(),
strMeasure11: z.string().nullish(),
strMeasure12: z.string().nullish(),
strMeasure13: z.string().nullish(),
strMeasure14: z.string().nullish(),
strMeasure15: z.string().nullish(),
strMeasure16: z.string().nullish(),
strMeasure17: z.string().nullish(),
strMeasure18: z.string().nullish(),
strMeasure19: z.string().nullish(),
strMeasure20: z.string().nullish(),
strSource: z.string().nullish(),
strImageSource: z.string().nullable(),
strCreativeCommonsConfirmed: z.string().nullable(),
dateModified: z.string().optional().nullable(),