Fixes: None; Extra: 1) Create Pinia stores for app, user, category, and company management; 2) Add utility functions for error handling and category slug lookups; 3) Include German locale file and robots.txt for improved SEO and accessibility; 4) Add SVG assets and improve general folder structure for better maintainability.
100 lines
No EOL
2.5 KiB
Vue
100 lines
No EOL
2.5 KiB
Vue
<template>
|
|
<div class="search">
|
|
<div class="container">
|
|
<div class="search__wrapper">
|
|
<div
|
|
v-for="(block, idx) in blocks"
|
|
:key="idx"
|
|
class="search__block"
|
|
>
|
|
<h6 class="search__block-title" v-if="hasData(block.key)">{{ t(block.title) }} {{ t('search.byRequest') }} "{{ q }}"</h6>
|
|
<div class="search__block-list" v-if="block.title.includes('products')">
|
|
<cards-product
|
|
v-for="product in data?.products.edges"
|
|
:key="product.node.uuid"
|
|
:product="product.node"
|
|
/>
|
|
</div>
|
|
<div class="search__block-list" v-if="block.title.includes('categories')">
|
|
<cards-category
|
|
v-for="category in data?.categories.edges"
|
|
:key="category.node.uuid"
|
|
:category="category.node"
|
|
/>
|
|
</div>
|
|
<div class="search__block-list" v-if="block.title.includes('brands')">
|
|
<cards-brand
|
|
v-for="brand in data?.brands.edges"
|
|
:key="brand.node.uuid"
|
|
:brand="brand.node"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import {useRouteQuery} from "@vueuse/router";
|
|
import {usePageTitle} from "~/composables/utils";
|
|
import {useSearchCombined} from "~/composables/search";
|
|
import type {ISearchCombinedResponse} from "~/types";
|
|
|
|
const {t} = useI18n();
|
|
|
|
const q = useRouteQuery('q', '');
|
|
|
|
const { setPageTitle } = usePageTitle();
|
|
|
|
setPageTitle(t('breadcrumbs.search'));
|
|
|
|
const { data } = await useSearchCombined(q.value);
|
|
|
|
type SearchResponseKey = keyof ISearchCombinedResponse;
|
|
|
|
const blocks = computed(() => {
|
|
if (!data.value) return [];
|
|
|
|
return (Object.keys(data.value) as SearchResponseKey[])
|
|
.map((key) => ({
|
|
key,
|
|
title: `search.${key}`
|
|
}));
|
|
});
|
|
|
|
const hasData = (blockKey: string): boolean => {
|
|
const validKey = blockKey as SearchResponseKey;
|
|
return (data.value?.[validKey]?.edges?.length ?? 0) > 0;
|
|
};
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.search {
|
|
padding-top: 75px;
|
|
|
|
&__wrapper {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 50px;
|
|
}
|
|
|
|
&__block {
|
|
&-title {
|
|
padding-bottom: 10px;
|
|
border-bottom: 2px solid $accentDark;
|
|
|
|
color: $accentDark;
|
|
font-size: 28px;
|
|
}
|
|
|
|
&-list {
|
|
margin-top: 25px;
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(275px, 1fr));
|
|
justify-content: space-between;
|
|
grid-gap: 75px;
|
|
}
|
|
}
|
|
}
|
|
</style> |