238 lines
8.4 KiB
Vue
238 lines
8.4 KiB
Vue
<script setup lang="ts">
|
|
// imports
|
|
|
|
import useGetAllTickets, {
|
|
type GetAllTicketsRequest,
|
|
} from "~/composables/api/tickets/useGetAllTickets";
|
|
|
|
// meta
|
|
|
|
definePageMeta({
|
|
middleware: "check-is-logged-in",
|
|
layout: "profile",
|
|
});
|
|
|
|
// state
|
|
|
|
const params: GetAllTicketsRequest = useUrlSearchParams("history");
|
|
|
|
const filters = computed(() => {
|
|
return {
|
|
sort: params.sort ?? "created_at",
|
|
status: params.status ?? "",
|
|
page: params.page ?? 1,
|
|
};
|
|
});
|
|
|
|
const tableHeads = ref([
|
|
"دسته بندی",
|
|
"موضوع",
|
|
"تاریخ ایجاد و بروز رسانی",
|
|
"وضعیت",
|
|
"عملیات",
|
|
]);
|
|
|
|
const sortFilters = ref([
|
|
{
|
|
title: "جدید ترین",
|
|
value: "created_at",
|
|
},
|
|
{
|
|
title: "قدیمی ترین",
|
|
value: "-created_at",
|
|
},
|
|
]);
|
|
|
|
const statusFilters = ref([
|
|
{
|
|
title: "پاسخ داده شده",
|
|
value: "resolved",
|
|
},
|
|
{
|
|
title: "در حال پردازش",
|
|
value: "in_progress",
|
|
},
|
|
{
|
|
title: "بسته شده",
|
|
value: "closed",
|
|
},
|
|
]);
|
|
|
|
// provide / inject
|
|
|
|
provide("params", params);
|
|
|
|
// queries
|
|
|
|
const { data, isPending: ticketsIsPending } = useGetAllTickets(filters);
|
|
|
|
// computed
|
|
|
|
const tickets = computed(() => {
|
|
return data.value?.results.flat();
|
|
});
|
|
|
|
const hasTickets = computed(() => data.value?.count > 0);
|
|
|
|
const hasFilters = computed(() =>
|
|
Object.keys(params)
|
|
.filter((key) => key != "page")
|
|
.some((key) => params[key] != undefined)
|
|
);
|
|
|
|
const paginationData = computed(() => {
|
|
return data.value?.results.map((_, i: number) => {
|
|
return { type: "page", value: i };
|
|
});
|
|
});
|
|
|
|
// methods
|
|
|
|
const clearFilters = () => {
|
|
params.sort = undefined;
|
|
params.status = undefined;
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div class="w-full flex flex-col gap-5">
|
|
<ProfilePageTitle title="تیکت های شما" icon="bi:ticket" />
|
|
|
|
<div class="w-full flex flex-col gap-5">
|
|
<div class="w-full flex items-center justify-between px-5">
|
|
<div class="flex items-center justify-start gap-8">
|
|
<div class="flex items-center justify-start gap-3">
|
|
<span class="text-sm">ترتیب بر اساس</span>
|
|
<Select
|
|
v-model="params.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-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="bi:check" size="20" />
|
|
</SelectItemIndicator>
|
|
<SelectItemText
|
|
class="text-end font-iran-yekan-x text-sm"
|
|
>
|
|
{{ category.title }}
|
|
</SelectItemText>
|
|
</SelectItem>
|
|
</SelectGroup>
|
|
</template>
|
|
</Select>
|
|
</div>
|
|
<div class="flex items-center justify-start gap-3">
|
|
<span class="text-sm">وضعیت</span>
|
|
<Select
|
|
v-model="params.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-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="bi:check" size="20" />
|
|
</SelectItemIndicator>
|
|
<SelectItemText
|
|
class="text-end font-iran-yekan-x text-sm"
|
|
>
|
|
{{ category.title }}
|
|
</SelectItemText>
|
|
</SelectItem>
|
|
</SelectGroup>
|
|
</template>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex-center gap-4">
|
|
<Button
|
|
v-if="hasFilters"
|
|
end-icon="bi:x"
|
|
@click="clearFilters"
|
|
size="md"
|
|
class="rounded-full"
|
|
>
|
|
<span class="whitespace-pre"> حذف فیلتر ها </span>
|
|
</Button>
|
|
|
|
<NuxtLink :to="{ name: 'profile-tickets-new' }">
|
|
<Button
|
|
end-icon="bi:plus"
|
|
size="md"
|
|
class="rounded-full"
|
|
>
|
|
<span class="whitespace-pre"> تیکت جدید </span>
|
|
</Button>
|
|
</NuxtLink>
|
|
</div>
|
|
</div>
|
|
|
|
<Placeholder
|
|
v-if="!hasTickets && !ticketsIsPending"
|
|
class="!w-full !py-[5rem]"
|
|
icon="bi:ticket"
|
|
title="تیکتی یافت نشد"
|
|
/>
|
|
|
|
<Table v-else>
|
|
<template #thead>
|
|
<th
|
|
v-for="(tableHead, index) in tableHeads"
|
|
:key="index"
|
|
scope="col"
|
|
:class="
|
|
[0, 1, 2].includes(index)
|
|
? 'w-3/12'
|
|
: tableHeads.length - 1 == index
|
|
? 'w-1/2'
|
|
: 'w-2/12'
|
|
"
|
|
class="px-6 py-5 text-sm font-normal"
|
|
>
|
|
{{ tableHead }}
|
|
</th>
|
|
</template>
|
|
<template #tbody>
|
|
<template v-if="ticketsIsPending">
|
|
<TicketsTableRowLoading v-for="i in 5" />
|
|
</template>
|
|
<template v-else>
|
|
<TicketsTableRow
|
|
v-for="(ticket, index) in tickets"
|
|
:key="index"
|
|
:data="ticket"
|
|
/>
|
|
</template>
|
|
</template>
|
|
</Table>
|
|
|
|
<div v-if="data?.count > 7" class="w-full flex-center py-10">
|
|
<Pagination :items="paginationData" :total="data?.count" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped></style>
|