Skip to content

Commit

Permalink
Update product filters and store for enhanced functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
oussama.bensalah committed Jul 10, 2024
1 parent 06f90ec commit 2e476ca
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 107 deletions.
135 changes: 70 additions & 65 deletions src/components/Filters.vue
Original file line number Diff line number Diff line change
@@ -1,80 +1,85 @@
<template>
<div class="join join-vertical w-full">
<div class="collapse collapse-arrow join-item border-base-300 border">
<input type="radio" name="my-accordion-3" :checked="true" />
<div class="collapse-title text-xl font-medium">Price Filter</div>
<div class="collapse-content">
<div v-for="range in ranges" :key="range.lbl">
<label :for="range.lbl">
<input :id="range.lbl" type="radio" :value="{ min: range.min, max: range.max }" name="price"
v-model="selectPrice" class="custom-control-input" @change="filterRangePrice" />
{{ range.lbl }}</label>
</div>
<div class="mt-3">
<button class="btn btn-primary" @click="resetAllFilters">
Reset All
</button>
</div>
</div>
<div class="join join-vertical w-full">
<div class="collapse collapse-arrow join-item border-base-300 border">
<input type="radio" name="my-accordion-3" :checked="true" />
<div class="collapse-title text-xl font-medium">Price Filter</div>
<div class="collapse-content">
<div v-for="range in ranges" :key="range.lbl">
<label :for="range.lbl">
<input
:id="range.lbl"
type="radio"
:value="{ min: range.min, max: range.max }"
name="price"
v-model="selectPrice"
class="custom-control-input"
@change="filterByPriceOrCategory"
/>
{{ range.lbl }}</label
>
</div>
<div class="mt-3">
<button class="btn btn-primary" @click="resetAllFilters">
Reset All
</button>
</div>
</div>
</div>
</div>

<div class="collapse collapse-arrow join-item border-base-300 border">
<input type="radio" name="my-accordion-3" />
<div class="collapse-title text-xl font-medium">Category Filter</div>
<div class="collapse-content">
<div v-for="category in uniqueCategories" :key="category">
<label :for="category">
<input :id="category" type="radio" :value="category" name="category" v-model="selectCategory"
class="custom-control-input" @change="filterByCategory" />
{{ category }}</label>
</div>
<div class="mt-3">
<button class="btn btn-primary" @click="resetAllFilters">
Reset All
</button>
</div>
</div>
<div class="collapse collapse-arrow join-item border-base-300 border">
<input type="radio" name="my-accordion-3" />
<div class="collapse-title text-xl font-medium">Category Filter</div>
<div class="collapse-content">
<div v-for="category in uniqueCategories" :key="category">
<label :for="category">
<input
:id="category"
type="radio"
:value="category"
name="category"
v-model="selectCategory"
class="custom-control-input"
@change="filterByPriceOrCategory"
/>
{{ category }}</label
>
</div>
<div class="mt-3">
<button class="btn btn-primary" @click="resetAllFilters">
Reset All
</button>
</div>
</div>
</div>
</template>

<script setup lang="ts">
import { computed, ref, watch } from "vue"
import { useProductStore } from "../store/product"
import { computed, ref } from "vue";
import { useProductStore } from "../store/product";
const emit = defineEmits(["FilterByPrice", "FilterByCategory"])
const productStore = useProductStore();
const productStore = useProductStore()
const products = computed(() => productStore.list)
const uniqueCategories = computed(() => productStore.categoriesList);
const uniqueCategories = ref([...new Set(products.value.map(product => product.category))])
const selectPrice = ref({ min: 0, max: 1000000 })
const selectCategory = ref()
const selectPrice = ref({ min: 0, max: 1000000 });
const selectCategory = ref("");
const ranges = ref([
{ lbl: 'All', min: 0, max: 1000000 },
{ lbl: '<=10', min: 0, max: 10 },
{ lbl: '10 - 100', min: 10, max: 100 },
{ lbl: '100 - 500', min: 100, max: 500 },
{ lbl: '>=500', min: 500, max: 1000000 }
])
watch(() => productStore.loaded, () => {
uniqueCategories.value = [...new Set(products.value.map(product => product.category))]
})
const filterRangePrice = () => {
emit("FilterByPrice", selectPrice.value)
}
{ lbl: "All", min: 0, max: 1000000 },
{ lbl: "<=10", min: 0, max: 10 },
{ lbl: "10 - 100", min: 10, max: 100 },
{ lbl: "100 - 500", min: 100, max: 500 },
{ lbl: ">=500", min: 500, max: 1000000 },
]);
const filterByCategory = () => {
emit("FilterByCategory", selectCategory.value)
}
const filterByPriceOrCategory = () => {
productStore.filterAll(selectCategory.value, selectPrice.value);
};
const resetAllFilters = () => {
selectCategory.value = undefined
selectPrice.value = { min: 0, max: 1000000 }
emit("FilterByPrice", selectPrice.value)
emit("FilterByCategory", selectCategory.value)
}
</script>
selectCategory.value = ''
selectPrice.value = { min: 0, max: 1000000 }
productStore.filterAll("", { min: 0, max: 1000000 });
productStore.filtering = false
};
</script>
3 changes: 3 additions & 0 deletions src/models/product.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@ export interface Product {

export interface ProductState {
items: Record<string, Product>
filteredItems: Record<string, Product>
ids: number[]
categories: string[]
filtering: boolean
}
70 changes: 28 additions & 42 deletions src/pages/Home.vue
Original file line number Diff line number Diff line change
@@ -1,58 +1,44 @@
<script setup lang="ts">
import { computed, ref } from "vue"
import ProductCard from "../components/ProductCard.vue"
import Filters from "../components/Filters.vue"
import ProductCardSkeleton from "../components/ProductCardSkeleton.vue"
import { useProductStore } from "../store/product"
const productStore = useProductStore()
const priceFilter = ref()
const categoryFilter = ref()
const products = computed(() => productStore.list)
const filteredProducts = computed(() => {
let productsList = products.value
if (priceFilter.value) {
productsList = productsList.filter(product => {
return product.price >= priceFilter.value.min && product.price <= priceFilter.value.max
})
}
if (categoryFilter.value) {
productsList = productsList.filter(product => product.category === categoryFilter.value)
}
return productsList
})
const filterByPrice = (price: { min: number, max: number }) => {
priceFilter.value = price
}
const filterByCategory = (category: string) => {
categoryFilter.value = category
}
import { computed } from "vue";
import ProductCard from "../components/ProductCard.vue";
import Filters from "../components/Filters.vue";
import ProductCardSkeleton from "../components/ProductCardSkeleton.vue";
import { useProductStore } from "../store/product";
const productStore = useProductStore();
const products = computed(() => {
if (productStore.filtering) return productStore.filteredList;
return productStore.list;
});
</script>

<template>
<div class="flex flex-row">
<aside class="w-full p-6 sm:w-60 md:w-80 lg:w-96 dark:bg-gray-50 dark:text-gray-800">
<aside class="p-6 sm:w-60 dark:bg-gray-50 dark:text-gray-800">
<nav class="space-y-8 text-sm">
<div class="space-y-2">
<Filters @filter-by-price="filterByPrice" @filter-by-category="filterByCategory" />
<Filters />
</div>
</nav>
</aside>
<div class="p-4 max-w-7xl mx-auto">
<div class="grid gap-4 grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
<ProductCardSkeleton v-for="n in 10" v-show="!productStore.loaded" :key="n" />
<ProductCard v-if="!filteredProducts.length" v-for="product in products" :key="product.id" :product="product" />
<ProductCard v-if="filteredProducts.length" v-for="product in filteredProducts" :key="product.id"
:product="product" />
<ProductCardSkeleton
v-for="n in 10"
v-show="!productStore.loaded"
:key="n"
/>
<div v-if="!products.length">
<h1 class="text-xl">No item Found.</h1>
</div>
<ProductCard
v-if="products.length"
v-for="product in products"
:key="product.id"
:product="product"
/>
</div>
</div>
</div>

</template>
33 changes: 33 additions & 0 deletions src/store/product.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ export const useProductStore = defineStore({

state: (): ProductState => ({
items: {},
filteredItems: {},
ids: [],
categories: [],
filtering: false
}),

getters: {
Expand All @@ -19,6 +22,14 @@ export const useProductStore = defineStore({
loaded(): boolean {
return this.ids.length > 0
},

categoriesList(): string[] {
return [...new Set(this.list.map(product => product.category))]
},

filteredList(): Product[] {
return this.ids.map(i => this.filteredItems[i]).filter(product => product)
}
},

actions: {
Expand All @@ -37,5 +48,27 @@ export const useProductStore = defineStore({
return error
}
},

filterAll(category: string, price: {min: number, max: number}) {

this.filteredItems = {}
this.filtering = true
let productsList = this.list

if (price) {
productsList = productsList.filter(product => {
return product.price >= price.min && product.price <= price.max
})
}

if (category) {
productsList = productsList.filter(product => product.category === category)
}

productsList.forEach(product => {
this.filteredItems[product.id] = product
})

}
},
})

0 comments on commit 2e476ca

Please sign in to comment.