Update products page
This commit is contained in:
@@ -24,7 +24,7 @@ const { colorObject } = useImageColor(`#category-image-${id.value}`);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NuxtLink :to="`/products?category=${id}`">
|
||||
<NuxtLink :to="`/products/category/${id}`">
|
||||
<div class="group relative rounded-150 overflow-hidden w-full aspect-square bg-white brightness-[97%]">
|
||||
<Transition name="fade">
|
||||
<video
|
||||
|
||||
@@ -2,34 +2,39 @@
|
||||
// imports
|
||||
|
||||
import useGetCategories from "~/composables/api/product/useGetCategories";
|
||||
import useGetProducts, {
|
||||
type GetProductsFilters,
|
||||
} from "~/composables/api/products/useGetProducts";
|
||||
import useGetProducts, { type GetProductsFilters } from "~/composables/api/products/useGetProducts";
|
||||
import { PRODUCT_RANGE } from "~/constants";
|
||||
|
||||
// state
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const params = inject("params") as GetProductsFilters;
|
||||
|
||||
const currentCategory = computed({
|
||||
get: () => {
|
||||
return Array.isArray(route.params.slug) ? route.params.slug[1] ?? undefined : undefined;
|
||||
},
|
||||
set: (newValue) => {
|
||||
router.push({ path: `/products/category/${newValue}`, query: { ...route.query } });
|
||||
},
|
||||
});
|
||||
|
||||
const sort_filter = ref([
|
||||
{ title: "جدیدترین ها", value: "newest" },
|
||||
{ title: "گران ترین ها", value: "price" },
|
||||
{ title: "ارزان ترین ها", value: "-price" },
|
||||
]);
|
||||
|
||||
const sliderValue = ref([
|
||||
params.price_gte ?? PRODUCT_RANGE.min,
|
||||
params.price_lte ?? PRODUCT_RANGE.max,
|
||||
]);
|
||||
const sliderValue = ref([params.price_gte ?? PRODUCT_RANGE.min, params.price_lte ?? PRODUCT_RANGE.max]);
|
||||
|
||||
const has_discount = ref(Boolean(params.has_discount) ?? false);
|
||||
const in_stock = ref(Boolean(params.in_stock) ?? false);
|
||||
|
||||
const sliderValueDebounced = refDebounced(sliderValue, 1000);
|
||||
|
||||
const filtersSuccessMessage = ref<{ title: string; status: string } | null>(
|
||||
null
|
||||
);
|
||||
const filtersSuccessMessage = ref<{ title: string; status: string } | null>(null);
|
||||
|
||||
// queries
|
||||
|
||||
@@ -41,7 +46,7 @@ const filters = computed(() => {
|
||||
price_lte: params.price_lte ?? PRODUCT_RANGE.max,
|
||||
in_stock: params.in_stock ?? false,
|
||||
has_discount: params.has_discount ?? false,
|
||||
category: params.category ?? undefined,
|
||||
category: currentCategory.value,
|
||||
page: params.page ?? 1,
|
||||
};
|
||||
});
|
||||
@@ -50,8 +55,7 @@ const { data: categories, suspense } = useGetCategories();
|
||||
|
||||
await suspense();
|
||||
|
||||
const { isPending: productsIsPending, status: productsStatus } =
|
||||
useGetProducts(filters);
|
||||
const { isPending: productsIsPending, status: productsStatus } = useGetProducts(filters);
|
||||
|
||||
// computed
|
||||
|
||||
@@ -77,10 +81,9 @@ const resetFilters = () => {
|
||||
sliderValue.value = [PRODUCT_RANGE.min, PRODUCT_RANGE.max];
|
||||
has_discount.value = false;
|
||||
in_stock.value = false;
|
||||
params.category = undefined;
|
||||
};
|
||||
|
||||
// watch
|
||||
router.push({ path: `/products/`, query: { ...route.query } });
|
||||
};
|
||||
|
||||
watch(
|
||||
() => sliderValueDebounced.value,
|
||||
@@ -123,10 +126,11 @@ watch(
|
||||
<div class="size-full flex flex-col gap-14 justify-between">
|
||||
<div class="w-full flex flex-col gap-10">
|
||||
<div class="flex flex-col items-center w-full gap-5">
|
||||
<div
|
||||
class="flex items-center justify-start gap-2 text-lg w-full"
|
||||
>
|
||||
<Icon name="ci:filter-list" size="24" />
|
||||
<div class="flex items-center justify-start gap-2 text-lg w-full">
|
||||
<Icon
|
||||
name="ci:filter-list"
|
||||
size="24"
|
||||
/>
|
||||
ترتیب بر اساس
|
||||
</div>
|
||||
<div class="w-full flex items-center gap-2">
|
||||
@@ -134,11 +138,7 @@ watch(
|
||||
v-for="(sort, index) in sort_filter"
|
||||
:key="index"
|
||||
@click="params.sort = sort.value"
|
||||
:class="
|
||||
params.sort == sort.value
|
||||
? 'bg-black text-white'
|
||||
: 'bg-slate-100'
|
||||
"
|
||||
:class="params.sort == sort.value ? 'bg-black text-white' : 'bg-slate-100'"
|
||||
class="py-1 px-3 cursor-pointer text-nowrap transition-all rounded-md text-sm"
|
||||
>
|
||||
{{ sort.title }}
|
||||
@@ -147,20 +147,25 @@ watch(
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col w-full gap-5">
|
||||
<div
|
||||
class="flex items-center justify-start gap-2 text-lg w-full"
|
||||
>
|
||||
<Icon name="ci:grid" size="24" />
|
||||
<div class="flex items-center justify-start gap-2 text-lg w-full">
|
||||
<Icon
|
||||
name="ci:grid"
|
||||
size="24"
|
||||
/>
|
||||
دسته بندی
|
||||
</div>
|
||||
<ComboBox :options="allCategories" v-model="params.category" />
|
||||
<ComboBox
|
||||
:options="allCategories"
|
||||
v-model="currentCategory"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col w-full gap-5">
|
||||
<div
|
||||
class="flex items-center justify-start gap-2 text-lg w-full"
|
||||
>
|
||||
<Icon name="ci:scan-box" size="24" />
|
||||
<div class="flex items-center justify-start gap-2 text-lg w-full">
|
||||
<Icon
|
||||
name="ci:scan-box"
|
||||
size="24"
|
||||
/>
|
||||
محدوده قیمت
|
||||
</div>
|
||||
<SliderRoot
|
||||
@@ -171,12 +176,8 @@ watch(
|
||||
:max="PRODUCT_RANGE.max"
|
||||
:step="1000"
|
||||
>
|
||||
<SliderTrack
|
||||
class="bg-black/10 relative grow rounded-full h-[3px]"
|
||||
>
|
||||
<SliderRange
|
||||
class="absolute bg-black rounded-full h-full"
|
||||
/>
|
||||
<SliderTrack class="bg-black/10 relative grow rounded-full h-[3px]">
|
||||
<SliderRange class="absolute bg-black rounded-full h-full" />
|
||||
</SliderTrack>
|
||||
<SliderThumb
|
||||
v-for="thumb in Object.keys(PRODUCT_RANGE)"
|
||||
@@ -227,15 +228,9 @@ watch(
|
||||
: ' text-danger-600 bg-danger-100 border-danger-600'
|
||||
"
|
||||
>
|
||||
<span class="text-sm">{{
|
||||
filtersSuccessMessage.title
|
||||
}}</span>
|
||||
<span class="text-sm">{{ filtersSuccessMessage.title }}</span>
|
||||
<Icon
|
||||
:name="
|
||||
filtersSuccessMessage.status == 'success'
|
||||
? 'bi:check'
|
||||
: 'bi:x'
|
||||
"
|
||||
:name="filtersSuccessMessage.status == 'success' ? 'bi:check' : 'bi:x'"
|
||||
size="20"
|
||||
/>
|
||||
</div>
|
||||
@@ -246,14 +241,29 @@ watch(
|
||||
@click="resetFilters"
|
||||
class="w-full rounded-full py-4 !cursor-pointer disabled:pointer-events-none z-[3]"
|
||||
>
|
||||
<Transition name="fade" mode="out-in">
|
||||
<span v-if="productsIsPending" class="flex-center gap-3">
|
||||
<Transition
|
||||
name="fade"
|
||||
mode="out-in"
|
||||
>
|
||||
<span
|
||||
v-if="productsIsPending"
|
||||
class="flex-center gap-3"
|
||||
>
|
||||
در حال دریافت اطلاعات
|
||||
<Icon name="svg-spinners:3-dots-bounce" size="20" />
|
||||
<Icon
|
||||
name="svg-spinners:3-dots-bounce"
|
||||
size="20"
|
||||
/>
|
||||
</span>
|
||||
<span v-else class="flex-center gap-3">
|
||||
<span
|
||||
v-else
|
||||
class="flex-center gap-3"
|
||||
>
|
||||
بازنشانی به پیش فرض
|
||||
<Icon name="ci:close" size="20" />
|
||||
<Icon
|
||||
name="ci:close"
|
||||
size="20"
|
||||
/>
|
||||
</span>
|
||||
</Transition>
|
||||
</Button>
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
import useHomeData from "~/composables/api/home/useHomeData";
|
||||
import ProductsGrid from "~/components/global/ProductsGrid.vue";
|
||||
|
||||
import { useStorage } from "@vueuse/core";
|
||||
|
||||
// state
|
||||
|
||||
const { data: homeData, suspense } = useHomeData();
|
||||
|
||||
@@ -6,8 +6,20 @@ import { PRODUCT_RANGE } from "~/constants";
|
||||
|
||||
// state
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
useSeoMeta({
|
||||
title : "محصولات"
|
||||
title: "محصولات",
|
||||
});
|
||||
|
||||
definePageMeta({
|
||||
validate: (route) => {
|
||||
if (Array.isArray(route.params.slug)) {
|
||||
return route.params.slug.length === 2 && route.params.slug[0] === "category";
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
const params: GetProductsFilters = useUrlSearchParams("history", {
|
||||
@@ -23,7 +35,7 @@ const filters = computed(() => {
|
||||
price_lte: params.price_lte ?? PRODUCT_RANGE.max,
|
||||
in_stock: params.in_stock ?? false,
|
||||
has_discount: params.has_discount ?? false,
|
||||
category: params.category ?? undefined,
|
||||
category: Array.isArray(route.params.slug) ? route.params.slug[1] ?? undefined : undefined,
|
||||
page: params.page ?? 1,
|
||||
};
|
||||
});
|
||||
Reference in New Issue
Block a user