Skip to content

Commit

Permalink
feat: improve predictive search logic and component separation
Browse files Browse the repository at this point in the history
  • Loading branch information
rylanharper committed Dec 3, 2024
1 parent 7ecf1c2 commit acbf3c4
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 99 deletions.
66 changes: 11 additions & 55 deletions app/components/search/search-menu-desktop.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,24 +43,6 @@ const closeSearch = () => {
emit('closeSearch');
};
// Helpers
const { getColorOption } = useProductHelpers();
// Computed
const productsWithOptions = computed(() =>
props.products.map((product) => {
const options = product.options;
const colorOption = getColorOption(options);
const colorOptionName = colorOption?.optionValues[0]?.name;
return {
...product,
colorOption,
colorOptionName
};
})
);
// Watchers
watch(
() => appStore.searchMenuOpen,
Expand Down Expand Up @@ -105,58 +87,32 @@ watch(
<div class="grid grid-cols-[280px_1fr] gap-12">
<div class="flex flex-col gap-4">
<h3 class="text-sm">Suggestions</h3>
<div v-if="searchQuery.length && products?.length" class="flex flex-col">
<NuxtLink
v-for="product in productsWithOptions"
<div v-if="props.searchQuery.length && props.products?.length" class="flex flex-col">
<SuggestedLink
v-for="product in products"
:key="product.id"
:to="`/products/${product.handle}`"
class="max-w-fit hover:text-gray-500"
>
<p v-if="product.colorOption" class="normal-case truncate ...">
{{ product.title }} ({{ product.colorOptionName }})
</p>
<span v-else>{{ product?.title }}</span>
</NuxtLink>
:product="product"
/>
</div>
<div v-else class="flex flex-col">
<NuxtLink
v-for="link in defaulSearchLinks"
:key="link.label"
:to="link.path"
class="max-w-fit normal-case truncate ... hover:text-gray-500"
class="max-w-fit normal-case hover:text-gray-500"
>
{{ link.label }}
</NuxtLink>
</div>
</div>
<div v-if="searchQuery.length && products?.length" class="flex flex-col gap-4">
<div v-if="props.searchQuery.length && props.products?.length" class="flex flex-col gap-4">
<h3 class="text-sm">Products</h3>
<div class="grid grid-cols-2 gap-8 w-full">
<NuxtLink
v-for="product in productsWithOptions"
<SuggestedProductCard
v-for="product in products"
:key="product.id"
:to="`/products/${product.handle}`"
class="flex gap-4"
>
<div class="w-24 aspect-square shrink-0">
<ShopifyImage
:image="product.featuredImage"
:alt="product.featuredImage?.altText || product.title"
/>
</div>
<div class="flex flex-col flex-1">
<div class="mb-1">
<h2 v-if="product.title">{{ product.title }}</h2>
<h3 v-if="product.colorOption" class="normal-case">
{{ product.colorOptionName }}
</h3>
</div>
<PriceDisplay
:price="product.priceRange?.minVariantPrice"
:compare-at-price-range="product.compareAtPriceRange?.minVariantPrice"
/>
</div>
</NuxtLink>
:product="product"
/>
</div>
</div>
</div>
Expand Down
48 changes: 5 additions & 43 deletions app/components/search/search-menu-mobile.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,6 @@ const closeSearch = () => {
emit('closeSearch');
};
// Helpers
const { getColorOption } = useProductHelpers();
// Computed
const productsWithOptions = computed(() =>
props.products.map((product) => {
const options = product.options;
const colorOption = getColorOption(options);
const colorOptionName = colorOption?.optionValues[0]?.name;
return {
...product,
colorOption,
colorOptionName
};
})
);
// Watchers
watch(
() => appStore.searchMenuOpen,
Expand Down Expand Up @@ -96,32 +78,12 @@ watch(
</button>
</div>
<div class="flex flex-col flex-1 overflow-y-scroll overflow-x-hidden no-scrollbar">
<div v-if="searchQuery.length && products?.length" class="grid grid-cols-2 gap-x-4 gap-y-8 w-full">
<NuxtLink
v-for="product in productsWithOptions"
<div v-if="props.searchQuery.length && props.products?.length" class="grid grid-cols-2 gap-x-4 gap-y-8 w-full">
<SuggestedProductCard
v-for="product in products"
:key="product.id"
:to="`/products/${product.handle}`"
class="flex gap-4"
>
<div class="size-24 aspect-square shrink-0">
<ShopifyImage
:image="product.featuredImage"
:alt="product.featuredImage?.altText || product.title"
/>
</div>
<div class="flex flex-col flex-1">
<div class="mb-1">
<h2 v-if="product.title">{{ product.title }}</h2>
<h3 v-if="product.colorOption" class="normal-case">
{{ product.colorOptionName }}
</h3>
</div>
<PriceDisplay
:price="product.priceRange?.minVariantPrice"
:compare-at-price-range="product.compareAtPriceRange?.minVariantPrice"
/>
</div>
</NuxtLink>
:product="product"
/>
</div>
</div>
</div>
Expand Down
9 changes: 8 additions & 1 deletion app/components/search/search-menu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,14 @@ const shopify = useShopify();
// Debounce query
const handleSearch = async (query: string) => {
searchQuery.value = query.trim();
const trimmedQuery = query.trim();
if (!trimmedQuery) {
searchResults.value = [];
return;
}
searchQuery.value = trimmedQuery;
const searchVars: PredictiveSearchQueryVariables = {
query: searchQuery.value,
Expand Down
28 changes: 28 additions & 0 deletions app/components/search/suggestions/suggested-link.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<script setup lang="ts">
import type { ProductFragment } from '@@/types/shopify';
// Props
const props = defineProps<{
product: ProductFragment;
}>();
// Helpers
const { getColorOption } = useProductHelpers();
// Computed
const options = computed(() => props.product?.options);
const colorOption = computed(() => getColorOption(options.value));
const colorOptionName = computed(() => colorOption.value?.optionValues[0]?.name);
</script>

<template>
<NuxtLink
:to="`/products/${product.handle}`"
class="max-w-fit hover:text-gray-500"
>
<p v-if="colorOptionName" class="normal-case truncate ...">
{{ product.title }} ({{ colorOptionName }})
</p>
<p v-else class="normal-case truncate ...">{{ product.title }}</p>
</NuxtLink>
</template>
42 changes: 42 additions & 0 deletions app/components/search/suggestions/suggested-product-card.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<script setup lang="ts">
import type { ProductFragment } from '@@/types/shopify';
// Props
const props = defineProps<{
product: ProductFragment;
}>();
// Helpers
const { getColorOption } = useProductHelpers();
// Computed
const options = computed(() => props.product?.options);
const colorOption = computed(() => getColorOption(options.value));
const colorOptionName = computed(() => colorOption.value?.optionValues[0]?.name);
</script>

<template>
<NuxtLink
:to="`/products/${product.handle}`"
class="flex gap-4"
>
<div class="w-24 aspect-square shrink-0">
<ShopifyImage
:image="product.featuredImage"
:alt="product.featuredImage?.altText || product.title"
/>
</div>
<div class="flex flex-col">
<div class="mb-2">
<h2 v-if="product.title">{{ product.title }}</h2>
<h3 v-if="colorOption" class="normal-case">
{{ colorOptionName }}
</h3>
</div>
<PriceDisplay
:price="product.priceRange.minVariantPrice"
:compare-at-price-range="product.compareAtPriceRange.minVariantPrice"
/>
</div>
</NuxtLink>
</template>

0 comments on commit acbf3c4

Please sign in to comment.