Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -1,4 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
// imports
|
||||
|
||||
import useGetAllTickets, {
|
||||
type GetAllTicketsFilters,
|
||||
} from "~/composables/api/tickets/useGetAllTickets";
|
||||
|
||||
// meta
|
||||
|
||||
definePageMeta({
|
||||
@@ -8,10 +14,12 @@ definePageMeta({
|
||||
|
||||
// state
|
||||
|
||||
const filters = ref({
|
||||
const params = useUrlSearchParams();
|
||||
|
||||
const filters = ref<GetAllTicketsFilters>({
|
||||
sort: undefined,
|
||||
status: undefined,
|
||||
search: "",
|
||||
filter: undefined,
|
||||
page: params.page ?? 1,
|
||||
});
|
||||
|
||||
const tableHeads = ref([
|
||||
@@ -21,6 +29,24 @@ const tableHeads = ref([
|
||||
"وضعیت",
|
||||
"عملیات",
|
||||
]);
|
||||
|
||||
// queries
|
||||
|
||||
const { data, isLoading: ticketsIsLoading } = useGetAllTickets(filters);
|
||||
|
||||
// computed
|
||||
|
||||
const tickets = computed(() => {
|
||||
return data.value?.results.flat();
|
||||
});
|
||||
|
||||
const hasTickets = computed(() => tickets.value?.length > 0);
|
||||
|
||||
const paginationData = computed(() => {
|
||||
return tickets!.value?.results.map((_, i: number) => {
|
||||
return { type: "page", value: i };
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -30,7 +56,10 @@ const tableHeads = ref([
|
||||
<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">
|
||||
<div
|
||||
v-if="hasTickets"
|
||||
class="flex items-center justify-start gap-3"
|
||||
>
|
||||
<span class="text-sm">ترتیب بر اساس</span>
|
||||
<Select
|
||||
:options="['جدید ترین', 'قدیمی ترین']"
|
||||
@@ -39,7 +68,10 @@ const tableHeads = ref([
|
||||
class="w-[5rem]"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center justify-start gap-3">
|
||||
<div
|
||||
v-if="hasTickets"
|
||||
class="flex items-center justify-start gap-3"
|
||||
>
|
||||
<span class="text-sm">وضعیت پرداخت</span>
|
||||
<Select
|
||||
:options="[
|
||||
@@ -47,7 +79,7 @@ const tableHeads = ref([
|
||||
'در حال پردازش',
|
||||
'لغو شده',
|
||||
]"
|
||||
v-model="filters.status!"
|
||||
v-model="filters.filter!"
|
||||
triggerRootClass="!py-2.5"
|
||||
class="w-[5rem]"
|
||||
/>
|
||||
@@ -63,7 +95,14 @@ const tableHeads = ref([
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
<Table>
|
||||
<Placeholder
|
||||
v-if="!hasTickets && !ticketsIsLoading"
|
||||
class="!w-full !py-[5rem]"
|
||||
icon="bi:ticket"
|
||||
title="تیکتی یافت نشد"
|
||||
/>
|
||||
|
||||
<Table v-else>
|
||||
<template #thead>
|
||||
<th
|
||||
v-for="(tableHead, index) in tableHeads"
|
||||
@@ -82,9 +121,22 @@ const tableHeads = ref([
|
||||
</th>
|
||||
</template>
|
||||
<template #tbody>
|
||||
<TicketsTableRow v-for="i in 4" />
|
||||
<template v-if="ticketsIsLoading">
|
||||
<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="tickets?.length > 10" class="w-full flex-center py-10">
|
||||
<Pagination :items="paginationData" :total="data?.count" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
// imports
|
||||
|
||||
import useCreateTicket, {
|
||||
type CreateTicketRequest,
|
||||
} from "~/composables/api/tickets/useCreateTicket";
|
||||
import useUploadAttachment from "~/composables/api/tickets/useUploadAttachment";
|
||||
import { useToast } from "~/composables/global/useToast";
|
||||
import { QUERY_KEYS } from "~/constants";
|
||||
|
||||
// meta
|
||||
|
||||
definePageMeta({
|
||||
@@ -15,6 +24,12 @@ type TicketCategory = {
|
||||
|
||||
// state
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const { $queryClient: queryClient } = useNuxtApp();
|
||||
|
||||
const { addToast } = useToast();
|
||||
|
||||
const ticketCategories: TicketCategory[] = [
|
||||
{
|
||||
title: "مالی و حسابداری",
|
||||
@@ -46,12 +61,76 @@ const ticketCategories: TicketCategory[] = [
|
||||
},
|
||||
];
|
||||
|
||||
const ticketData = ref({
|
||||
category: undefined,
|
||||
const ticketData = ref<CreateTicketRequest>({
|
||||
ticket_category: undefined,
|
||||
order: undefined,
|
||||
message: "",
|
||||
files: [],
|
||||
subject: "",
|
||||
content: "",
|
||||
attachments: [],
|
||||
});
|
||||
|
||||
// queries
|
||||
|
||||
const { mutateAsync: createTicket, isPending: createTicketIsPending } =
|
||||
useCreateTicket();
|
||||
|
||||
const { mutate: uploadAttachment, isPending: uploadAttachmentIsPending } =
|
||||
useUploadAttachment();
|
||||
|
||||
// methods
|
||||
|
||||
const handleUploadAttachment = (file: File) => {
|
||||
uploadAttachment(
|
||||
{ file },
|
||||
{
|
||||
onSuccess: (data) => {
|
||||
ticketData.value.attachments.push({ ...data });
|
||||
},
|
||||
onError: (error) => {
|
||||
addToast({
|
||||
message: error.message
|
||||
? error.message
|
||||
: "خطایی در آپلود پیوست رخ داد",
|
||||
options: {
|
||||
status: "error",
|
||||
description: "لطفا مجدد تلاش کنید",
|
||||
},
|
||||
});
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
createTicket(
|
||||
{ ...ticketData.value },
|
||||
{
|
||||
onSuccess: () => {
|
||||
router.push({ name: "profile-tickets" });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [QUERY_KEYS.tickets],
|
||||
});
|
||||
addToast({
|
||||
message: "تیکت شما با موفقیت ثبت شد",
|
||||
options: {
|
||||
status: "success",
|
||||
description:
|
||||
"پس از بررسی پشتیبانی به شما اطلاع رسانی می شود",
|
||||
},
|
||||
});
|
||||
},
|
||||
onError: () => {
|
||||
addToast({
|
||||
message: "خطایی در ثبت تیکت رخ داد",
|
||||
options: {
|
||||
status: "success",
|
||||
description: "لطفا مجدد تلاش کنید",
|
||||
},
|
||||
});
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -75,7 +154,7 @@ const ticketData = ref({
|
||||
<Select
|
||||
placeholder="انتخاب کنید"
|
||||
variant="outlined"
|
||||
v-model="ticketData.category"
|
||||
v-model="ticketData.ticket_category"
|
||||
>
|
||||
<template #content>
|
||||
<SelectGroup>
|
||||
@@ -133,23 +212,39 @@ const ticketData = ref({
|
||||
</template>
|
||||
</Select>
|
||||
</DataField>
|
||||
<DataField
|
||||
id="subject"
|
||||
:required="true"
|
||||
label="عنوان تیکت"
|
||||
class="col-span-full"
|
||||
>
|
||||
<Input
|
||||
v-model="ticketData.subject"
|
||||
placeholder="عنوان تیکت را اینجا بنویسید ..."
|
||||
variant="outlined"
|
||||
/>
|
||||
</DataField>
|
||||
<DataField id="message" :required="true" label="متن تیکت">
|
||||
<textarea
|
||||
v-model="ticketData.message"
|
||||
v-model="ticketData.content"
|
||||
class="w-full bg-white border-[1.5px] border-slate-200 rounded-xl text-xs lg:text-sm p-5 text-black h-[10rem] lg:h-[20rem] transition resize-none outline-none focus:!border-black"
|
||||
placeholder="متن تیکت را اینجا بنویسید ..."
|
||||
/>
|
||||
</DataField>
|
||||
<FileInput v-model="ticketData.files" />
|
||||
<FileInput
|
||||
v-model="ticketData.attachments"
|
||||
@change="handleUploadAttachment"
|
||||
:loading="uploadAttachmentIsPending"
|
||||
/>
|
||||
</div>
|
||||
</ProfileSection>
|
||||
<div class="w-full flex-center py-5">
|
||||
<Button class="rounded-full px-20" end-icon="bi:send" size="md">
|
||||
<!-- <Icon
|
||||
v-if="createAddressIsPending"
|
||||
<Icon
|
||||
v-if="createTicketIsPending"
|
||||
name="svg-spinners:3-dots-bounce"
|
||||
/> -->
|
||||
<span>ارسال تیکت</span>
|
||||
/>
|
||||
<span v-else>ارسال تیکت</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user