Files
hossein-por-shop/frontend/pages/profile/purchases-and-orders/index.vue
T
2026-05-05 20:57:15 +03:30

253 lines
9.2 KiB
Vue

<script setup lang="ts">
import useGetAllOrders, { type GetAllOrdersRequest } from "~/composables/api/orders/useGetAllOrders";
import { useAppParams } from "~/composables/global/useAppParams";
// meta
useSeoMeta({
title: "پنل کاربری سفارشات",
});
definePageMeta({
middleware: "check-is-logged-in",
layout: "profile",
});
// state
const route = useRoute();
const { sort, status } = useAppParams();
const tableHeads = ref(["شماره سفارش", "تاریخ ثبت", "تعداد اقلام", "مبلغ", "وضعیت", "عملیات"]);
const sortFilters = ref([
{
title: "جدید ترین",
value: "-created_at",
},
{
title: "قدیمی ترین",
value: "created_at",
},
{
title: "گران ترین",
value: "-final_price",
},
{
title: "ارزان ترین",
value: "final_price",
},
]);
const statusFilters = ref([
{
title: "در انتظار تایید",
value: "ADMIN_PENDING",
},
{
title: "در حال پردازش",
value: "PENDING",
},
{
title: "ارسال شده",
value: "POSTED",
},
{
title: "تحویل داده شده",
value: "RECEIVED",
},
{
title: "لغو شده",
value: "CANCELED",
},
{
title: "مرجوع شده",
value: "REFUNDED",
},
]);
// queries
const { data, isPending: purchasesIsPending } = useGetAllOrders();
// computed
const purchases = computed(() => {
return data.value?.results.flat();
});
const hasPurchases = computed(() => data.value && data.value.count > 0);
const hasFilters = computed(() =>
Object.keys(route.query)
.filter((key) => key != "page")
.some((key) => (route.query as any)[key] != undefined)
);
const paginationData = computed(() => {
return data.value?.results.map((_, i: number) => {
return { type: "page", value: i };
});
});
// methods
const clearFilters = () => {
sort.value = undefined;
status.value = undefined;
};
</script>
<template>
<div class="w-full flex flex-col gap-5">
<ProfilePageTitle
title="خرید ها و سفارش های شما"
icon="ci:bi-cart"
/>
<div class="w-full flex flex-col gap-5">
<div class="w-full flex flex-col-reverse lg:flex-row items-center justify-between lg:px-5 gap-5">
<div class="max-lg:w-full flex items-center justify-start gap-8">
<div
class="flex flex-col lg:flex-row items-start lg:items-center justify-start gap-3 max-lg:w-full"
>
<span class="text-xs lg:text-sm">ترتیب بر اساس</span>
<Select
v-model="sort"
triggerRootClass="!py-2.5"
class="w-[6rem]"
>
<template #content>
<SelectGroup>
<SelectItem
v-for="(category, index) in sortFilters"
:key="index"
class="text-xs leading-none w-full rounded-sm py-4 lg:py-5 flex items-center justify-between h-[25px] pr-[12px] relative select-none data-[disabled]:pointer-events-none data-[highlighted]:outline-none data-[highlighted]:bg-slate-300 data-[highlighted]:text-black"
:value="category.value"
>
<SelectItemIndicator
class="absolute left-0 w-[25px] inline-flex items-center justify-center"
>
<Icon
name="ci:bi-check"
size="20"
/>
</SelectItemIndicator>
<SelectItemText class="text-end font-iran-yekan-x text-xs lg:text-sm">
{{ category.title }}
</SelectItemText>
</SelectItem>
</SelectGroup>
</template>
</Select>
</div>
<div
class="flex flex-col lg:flex-row items-start lg:items-center justify-start gap-3 max-lg:w-full"
>
<span class="text-xs lg:text-sm">وضعیت</span>
<Select
v-model="status"
triggerRootClass="!py-2.5"
class="w-[6rem]"
>
<template #content>
<SelectGroup>
<SelectItem
v-for="(category, index) in statusFilters"
:key="index"
class="text-xs leading-none w-full rounded-sm py-4 lg:py-5 flex items-center justify-between h-[25px] pr-[12px] relative select-none data-[disabled]:pointer-events-none data-[highlighted]:outline-none data-[highlighted]:bg-slate-300 data-[highlighted]:text-black"
:value="category.value"
>
<SelectItemIndicator
class="absolute left-0 w-[25px] inline-flex items-center justify-center"
>
<Icon
name="ci:bi-check"
size="20"
/>
</SelectItemIndicator>
<SelectItemText class="text-end font-iran-yekan-x text-xs lg:text-sm">
{{ category.title }}
</SelectItemText>
</SelectItem>
</SelectGroup>
</template>
</Select>
</div>
</div>
<div
class="flex items-center lg:justify-center gap-4 max-lg:w-full"
:class="hasFilters ? 'justify-between' : 'justify-end'"
>
<Button
v-if="hasFilters"
end-icon="ci:bi-x"
@click="clearFilters"
size="md"
class="rounded-full"
>
<span class="whitespace-pre"> حذف فیلتر ها </span>
</Button>
<NuxtLink to="/products">
<Button
end-icon="ci:bi-plus"
size="md"
class="rounded-full"
>
<span class="whitespace-pre"> خرید جدید </span>
</Button>
</NuxtLink>
</div>
</div>
<Placeholder
v-if="!hasPurchases && !purchasesIsPending"
class="!w-full !py-[5rem]"
icon="ci:bi-cart"
title="خرید یا سفارشی یافت نشد"
/>
<Table v-else>
<template #thead>
<th
v-for="(tableHead, index) in tableHeads"
:key="index"
scope="col"
:class="[0, 1].includes(index) ? 'w-3/12' : tableHeads.length - 1 == index ? 'w-1/2' : 'w-2/12'"
class="px-6 py-5 text-xs lg:text-sm font-normal shrink-0 whitespace-pre"
>
{{ tableHead }}
</th>
</template>
<template #tbody>
<template v-if="purchasesIsPending">
<PurchasesTableRowLoading v-for="i in 5" />
</template>
<template v-else>
<PurchasesTableRow
v-for="(purchase, index) in purchases"
:key="index"
:data="purchase"
/>
</template>
</template>
</Table>
<div
v-if="data && paginationData && data.count > 10"
class="w-full flex-center py-10"
>
<Pagination
:items="paginationData"
:total="data.count"
/>
</div>
</div>
</div>
</template>
<style scoped></style>