synced filters with url
This commit is contained in:
@@ -1,11 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
// imports
|
||||
|
||||
import useGetProducts, {
|
||||
type GetProductsFilters,
|
||||
} from "~/composables/api/products/useGetProducts";
|
||||
import { useParams } from "~/composables/global/useParams";
|
||||
import { PRODUCT_RANGE } from "~/constants";
|
||||
|
||||
// state
|
||||
|
||||
const params = useUrlSearchParams("history");
|
||||
const params: GetProductsFilters = inject("params");
|
||||
|
||||
const sort_filter = ref([
|
||||
{ title: "جدیدترین ها", value: "newest" },
|
||||
@@ -13,6 +17,13 @@ const sort_filter = ref([
|
||||
{ title: "ارزان ترین ها", value: "-price" },
|
||||
]);
|
||||
|
||||
const sliderValue = ref([PRODUCT_RANGE.min, PRODUCT_RANGE.max]);
|
||||
|
||||
const has_discount = ref(JSON.parse(params.has_discount) ?? false);
|
||||
const in_stock = ref(JSON.parse(params.in_stock) ?? false);
|
||||
|
||||
const sliderValueDebounced = refDebounced(sliderValue, 1000);
|
||||
|
||||
const options = [
|
||||
{
|
||||
name: "میوه",
|
||||
@@ -42,15 +53,47 @@ const options = [
|
||||
},
|
||||
];
|
||||
|
||||
const { isPending: productsIsPending } = useGetProducts(params);
|
||||
|
||||
// methods
|
||||
|
||||
const resetFilters = () => {
|
||||
params.sort = null;
|
||||
params.categories = [];
|
||||
params.range = [PRODUCT_RANGE.min, PRODUCT_RANGE.max];
|
||||
params.has_discount = false;
|
||||
params.in_stock = false;
|
||||
params.search = "";
|
||||
params.sort = "newest";
|
||||
sliderValue.value = [PRODUCT_RANGE.min, PRODUCT_RANGE.max];
|
||||
has_discount.value = false;
|
||||
in_stock.value = false;
|
||||
params.category = "";
|
||||
};
|
||||
|
||||
// watch
|
||||
|
||||
watch(
|
||||
() => sliderValueDebounced.value,
|
||||
(newValue) => {
|
||||
params.price_gte = newValue[0];
|
||||
params.price_lte = newValue[1];
|
||||
}
|
||||
);
|
||||
|
||||
watchOnce(
|
||||
() => [params.price_gte, params.price_lte],
|
||||
([newGte, newLte]) => {
|
||||
sliderValue.value[0] = newGte;
|
||||
sliderValue.value[1] = newLte;
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => [has_discount.value, in_stock.value],
|
||||
([newHasDiscount, newInStock]) => {
|
||||
params.has_discount = newHasDiscount;
|
||||
params.in_stock = newInStock;
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -87,14 +130,15 @@ const resetFilters = () => {
|
||||
<Icon name="ci:grid" size="24" />
|
||||
دسته بندی
|
||||
</div>
|
||||
<ComboBox :options="options" v-model="params.categories" />
|
||||
<div class="w-full flex flex-wrap gap-2 px-[1rem]">
|
||||
<ComboBox :options="options" v-model="params.category" />
|
||||
<div
|
||||
v-if="params.category"
|
||||
class="w-full flex flex-wrap gap-2 px-[1rem]"
|
||||
>
|
||||
<span
|
||||
v-for="(sort_param, index) in params.categories"
|
||||
:key="index"
|
||||
class="py-1 px-3 cursor-pointer text-nowrap bg-slate-100 rounded-full text-sm"
|
||||
>
|
||||
{{ sort_param }}
|
||||
{{ params.category }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -107,12 +151,12 @@ const resetFilters = () => {
|
||||
محدوده قیمت
|
||||
</div>
|
||||
<SliderRoot
|
||||
v-model="params.range"
|
||||
v-model="sliderValue"
|
||||
class="relative flex items-center select-none touch-none h-5"
|
||||
dir="rtl"
|
||||
:min="PRODUCT_RANGE.min"
|
||||
:max="PRODUCT_RANGE.max"
|
||||
:step="1"
|
||||
:step="1000"
|
||||
>
|
||||
<SliderTrack
|
||||
class="bg-black/10 relative grow rounded-full h-[3px]"
|
||||
@@ -132,8 +176,8 @@ const resetFilters = () => {
|
||||
<span class="text-sm text-black">حداقل</span>
|
||||
<span class="text-sm text-black">
|
||||
{{
|
||||
"range" in params
|
||||
? params.range[0].toLocaleString()
|
||||
"price_gte" in params
|
||||
? params.price_gte.toLocaleString()
|
||||
: PRODUCT_RANGE.min
|
||||
}}
|
||||
</span>
|
||||
@@ -142,8 +186,8 @@ const resetFilters = () => {
|
||||
<span class="text-sm text-black">حداکثر</span>
|
||||
<span class="text-sm text-black">
|
||||
{{
|
||||
"range" in params
|
||||
? params.range[1].toLocaleString()
|
||||
"price_lte" in params
|
||||
? params.price_lte.toLocaleString()
|
||||
: PRODUCT_RANGE.max
|
||||
}}
|
||||
</span>
|
||||
@@ -154,7 +198,7 @@ const resetFilters = () => {
|
||||
<div class="flex items-center justify-between w-full gap-5">
|
||||
<span class="text-black">فقط کالاهای تخفیف دار</span>
|
||||
<SwitchRoot
|
||||
v-model="params.has_discount"
|
||||
v-model="has_discount"
|
||||
class="w-[3rem] h-[1.8rem] flex data-[state=unchecked]:bg-slate-200 data-[state=checked]:bg-stone-800 border border-slate-300 data-[state=checked]:border-stone-700 rounded-full relative transition-all focus-within:outline-none"
|
||||
>
|
||||
<SwitchThumb
|
||||
@@ -167,7 +211,7 @@ const resetFilters = () => {
|
||||
<span class="text-black">فقط کالاهای موجود</span>
|
||||
|
||||
<SwitchRoot
|
||||
v-model="params.in_stock"
|
||||
v-model="in_stock"
|
||||
class="w-[3rem] h-[1.8rem] flex data-[state=unchecked]:bg-slate-200 data-[state=checked]:bg-stone-800 border border-slate-300 data-[state=checked]:border-stone-700 rounded-full relative transition-all focus-within:outline-none"
|
||||
>
|
||||
<SwitchThumb
|
||||
@@ -178,12 +222,21 @@ const resetFilters = () => {
|
||||
</div>
|
||||
|
||||
<Button
|
||||
end-icon="ci:close"
|
||||
:disabled="productsIsPending"
|
||||
variant="solid"
|
||||
@click="resetFilters"
|
||||
class="rounded-full py-4 !cursor-pointer"
|
||||
class="rounded-full py-4 !cursor-pointer disabled:pointer-events-none"
|
||||
>
|
||||
<Transition name="fade" mode="out-in">
|
||||
<span v-if="productsIsPending" class="flex-center gap-3">
|
||||
در حال دریافت اطلاعات
|
||||
<Icon name="svg-spinners:3-dots-bounce" size="20" />
|
||||
</span>
|
||||
<span v-else class="flex-center gap-3">
|
||||
بازنشانی به پیش فرض
|
||||
<Icon name="ci:close" size="20" />
|
||||
</span>
|
||||
</Transition>
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user