added resellers page
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
// import
|
// import
|
||||||
|
|
||||||
import useGetCategories from "~/composables/api/product/useGetCategories";
|
import useGetCategories from "~/composables/api/categories/useGetCategories";
|
||||||
|
|
||||||
// state
|
// state
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,164 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// import
|
||||||
|
|
||||||
|
import useGetResellersProducts, {
|
||||||
|
type GetResellersProductsFilters,
|
||||||
|
} from "~/composables/api/resellers/useGetResellersProducts";
|
||||||
|
import { PRODUCT_RANGE } from "~/constants";
|
||||||
|
|
||||||
|
// state
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
useSeoMeta({
|
||||||
|
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: GetResellersProductsFilters = useUrlSearchParams("history", {
|
||||||
|
removeFalsyValues: true,
|
||||||
|
removeNullishValues: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const filters = computed(() => {
|
||||||
|
return {
|
||||||
|
sort: params.sort ?? "newest",
|
||||||
|
search: params.search ?? "",
|
||||||
|
price_gte: params.price_gte ?? PRODUCT_RANGE.min,
|
||||||
|
price_lte: params.price_lte ?? PRODUCT_RANGE.max,
|
||||||
|
in_stock: params.in_stock ?? false,
|
||||||
|
has_discount: params.has_discount ?? false,
|
||||||
|
category: Array.isArray(route.params.slug) ? route.params.slug[1] ?? undefined : undefined,
|
||||||
|
page: params.page ?? 1,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const search = ref(params.search ?? "");
|
||||||
|
const searchDebounced = refDebounced(search, 1000);
|
||||||
|
|
||||||
|
// provide / inject
|
||||||
|
|
||||||
|
provide("params", params);
|
||||||
|
|
||||||
|
// queries
|
||||||
|
|
||||||
|
const { data, isLoading: productsIsLoading } = useGetResellersProducts(filters);
|
||||||
|
|
||||||
|
const products = computed(() => {
|
||||||
|
return data.value?.results.flat();
|
||||||
|
});
|
||||||
|
|
||||||
|
const paginationData = computed(() => {
|
||||||
|
return data!.value?.results.map((_, i: number) => {
|
||||||
|
return { type: "page", value: i };
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// watch
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => searchDebounced.value,
|
||||||
|
(newValue) => {
|
||||||
|
params.search = newValue;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="w-full container flex flex-col">
|
||||||
|
<div class="w-full flex flex-col lg:flex-row justify-end items-end py-[3.5rem] lg:py-[5rem] gap-10 lg:gap-5">
|
||||||
|
<div class="flex flex-col items-center lg:items-start gap-[1rem] lg:gap-[1.5rem] text-black w-full">
|
||||||
|
<h1 class="typo-h-5 lg:typo-h-4">لیست محصولات</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="w-full flex items-center justify-between lg:justify-end gap-4">
|
||||||
|
<Input
|
||||||
|
placeholder="جست و جو محصول ..."
|
||||||
|
v-model="search"
|
||||||
|
variant="outlined"
|
||||||
|
class="!rounded-xl w-full lg:w-8/12"
|
||||||
|
>
|
||||||
|
<template #endItem>
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<Icon
|
||||||
|
class="translate-y-[-1px] text-[20px] lg:text-[24px]"
|
||||||
|
name="ci:search"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Input>
|
||||||
|
<Suspense>
|
||||||
|
<FilterButton>
|
||||||
|
<template #content>
|
||||||
|
<FilterResellersProducts />
|
||||||
|
</template>
|
||||||
|
</FilterButton>
|
||||||
|
<template #fallback>
|
||||||
|
<Skeleton class="!size-11 lg:!w-[10.35rem] lg:!h-[3.35rem] shrink-0 !rounded-xl" />
|
||||||
|
</template>
|
||||||
|
</Suspense>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ul
|
||||||
|
v-if="productsIsLoading"
|
||||||
|
class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-y-8 gap-5 sm:gap-8 w-full"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="w-full flex flex-col gap-3"
|
||||||
|
v-for="i in 10"
|
||||||
|
:key="i"
|
||||||
|
>
|
||||||
|
<Skeleton
|
||||||
|
v-for="i in 3"
|
||||||
|
:key="i"
|
||||||
|
class="w-full"
|
||||||
|
:class="{
|
||||||
|
'!h-[11.9rem] lg:!h-[17.25rem] !rounded-2xl': i == 1,
|
||||||
|
'!h-[1.4rem] lg:!h-[1.5rem] !rounded-sm': [2, 3].includes(i),
|
||||||
|
'!w-1/2 lg:!w-full': i == 2,
|
||||||
|
'lg:!w-1/2': i == 3,
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ul>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="w-full h-max"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="!products!.length"
|
||||||
|
class="flex flex-grow w-full"
|
||||||
|
>
|
||||||
|
<Placeholder
|
||||||
|
title="محصولی یافت نشد :("
|
||||||
|
icon="bi:search"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<ProductsGrid
|
||||||
|
:with-header="false"
|
||||||
|
:products="products!"
|
||||||
|
class="!p-0"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-if="data && paginationData && data.count > 15"
|
||||||
|
class="w-full flex-center py-10 mt-5 lg:mt-10"
|
||||||
|
>
|
||||||
|
<Pagination
|
||||||
|
:items="paginationData"
|
||||||
|
:total="data.count"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
Reference in New Issue
Block a user