Merge remote-tracking branch 'origin/main'

This commit is contained in:
marzban-dev
2025-02-26 21:01:57 +03:30
64 changed files with 1766 additions and 547 deletions
+60 -8
View File
@@ -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>
+106 -11
View File
@@ -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>