Add category filter to product view
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { ref, computed, watch, onMounted } from 'vue'
|
||||
import AppLayout from '@/layouts/AppLayout.vue'
|
||||
import AppHeader from '@/components/AppHeader.vue'
|
||||
import { Product } from '@/types';
|
||||
@@ -11,6 +11,7 @@ import PlaceholderPattern from '@/components/PlaceholderPattern.vue';
|
||||
import { toCurrency, toRoundedCurrency } from '@/lib/utils';
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import { Kbd, KbdGroup } from '@/components/ui/kbd'
|
||||
import { Toggle } from '@/components/ui/toggle'
|
||||
|
||||
interface Props {
|
||||
productsData: Product[];
|
||||
@@ -18,6 +19,19 @@ interface Props {
|
||||
const props = defineProps<Props>();
|
||||
const searchQuery = ref('')
|
||||
const searchField = ref()
|
||||
|
||||
const categoryVisibility = ref<Record<string, boolean>>({});
|
||||
|
||||
onMounted(() => {
|
||||
initializeCategoryVisibility();
|
||||
})
|
||||
|
||||
|
||||
// Watcher für Änderungen in productsData
|
||||
watch(() => props.productsData, () => {
|
||||
initializeCategoryVisibility();
|
||||
}, { deep: true });
|
||||
|
||||
const fuse = computed(() => {
|
||||
return new Fuse(props.productsData, {
|
||||
keys: ['title', 'description', 'notes'],
|
||||
@@ -26,13 +40,58 @@ const fuse = computed(() => {
|
||||
})
|
||||
|
||||
const filteredProducts = computed(() => {
|
||||
if (!searchQuery.value) {
|
||||
return props.productsData;
|
||||
const visibleCategories = Object.entries(categoryVisibility.value)
|
||||
.filter(([_, isVisible]) => isVisible)
|
||||
.map(([categoryName]) => categoryName);
|
||||
|
||||
if (visibleCategories.length == 0) return []
|
||||
|
||||
let products = props.productsData;
|
||||
|
||||
// Filter nach Suchanfrage
|
||||
if (searchQuery.value) {
|
||||
products = fuse.value.search(searchQuery.value).map(result => result.item);
|
||||
}
|
||||
|
||||
return fuse.value.search(searchQuery.value).map(result => result.item);
|
||||
})
|
||||
// Filter nach Kategorien
|
||||
products = products.filter(product =>
|
||||
product.category?.name && visibleCategories.includes(product.category.name)
|
||||
);
|
||||
|
||||
return products;
|
||||
});
|
||||
|
||||
const categories = computed(() => {
|
||||
const categorySet = new Set<string>();
|
||||
props.productsData.forEach(product => {
|
||||
if (product.category?.name) {
|
||||
categorySet.add(product.category.name);
|
||||
}
|
||||
});
|
||||
return Array.from(categorySet).sort();
|
||||
});
|
||||
|
||||
const initializeCategoryVisibility = () => {
|
||||
const visibility: Record<string, boolean> = {};
|
||||
props.productsData.forEach(product => {
|
||||
if (product.category?.name) {
|
||||
visibility[product.category.name] = true;
|
||||
}
|
||||
});
|
||||
categoryVisibility.value = visibility;
|
||||
};
|
||||
|
||||
const allCategoriesVisible = computed(() => {
|
||||
return Object.values(categoryVisibility.value).every(isVisible => isVisible);
|
||||
});
|
||||
|
||||
const toggleAllCategories = () => {
|
||||
const newVisibility = !allCategoriesVisible.value;
|
||||
|
||||
Object.keys(categoryVisibility.value).forEach(categoryName => {
|
||||
categoryVisibility.value[categoryName] = newVisibility;
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -76,6 +135,13 @@ const filteredProducts = computed(() => {
|
||||
</template>
|
||||
</AppHeader>
|
||||
|
||||
<div class="flex items-center justify-center mb-8">
|
||||
<Toggle :modelValue="allCategoriesVisible" variant="outline" size="sm" aria-label="Alle Kategorien"
|
||||
@click="toggleAllCategories">Alle</Toggle>
|
||||
<Toggle v-for="category in categories" v-model="categoryVisibility[category]" variant="outline" size="sm"
|
||||
:aria-label="'Kategorie ' + category">{{ category }}</Toggle>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-8 lg:gap-12">
|
||||
<div v-for="product in filteredProducts"
|
||||
class="w-[calc(50%-4*var(--spacing))] md:w-[calc(33%-5.333*var(--spacing))] lg:w-[calc(25%-9*var(--spacing))] xl:w-[calc(20%-9.6*var(--spacing))] flex flex-col gap-4">
|
||||
|
||||
Reference in New Issue
Block a user