create ticket admin

This commit is contained in:
hamid zarghami
2025-05-25 10:33:22 +03:30
parent 08d66eec06
commit 4bb95b4d08
6 changed files with 253 additions and 109 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
VITE_TOKEN_NAME = 'admin_token'
VITE_REFRESH_TOKEN_NAME = 'admin_refresh_token'
VITE_BASE_URL = 'https://api.danakcorp.com'
# VITE_BASE_URL = 'http://192.168.1.108:4000'
# VITE_BASE_URL = 'http://192.168.1.113:4000'
+5 -5
View File
@@ -100,16 +100,16 @@ const ReceiptsList: FC = () => {
label: t('receip.paid'),
value: 'PAID'
},
{
icon: <WalletMinus color={activeTab === 'OVERDUE' ? 'black' : '#8C90A3'} size={22} />,
label: t('receip.overdue'),
value: 'OVERDUE'
},
{
icon: <FolderOpen color={activeTab === 'ARCHIVED' ? 'black' : '#8C90A3'} size={22} />,
label: t('receip.archive'),
value: 'ARCHIVED'
},
{
icon: <WalletMinus color={activeTab === 'OVERDUE' ? 'black' : '#8C90A3'} size={22} />,
label: t('receip.overdue'),
value: 'OVERDUE'
},
]}
onChange={(value) => setActiveTab(value as InvoiceStatus)}
/>
+145 -31
View File
@@ -1,38 +1,115 @@
import { FC } from 'react'
import { FC, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '../../components/Input'
import Select from '../../components/Select'
import Textarea from '../../components/Textarea'
import UploadBox from '../../components/UploadBox'
import Button from '../../components/Button'
import { TickSquare } from 'iconsax-react'
import { TickCircle, TickSquare } from 'iconsax-react'
import { useCreateTicket, useGetCategories } from './hooks/useTicketData'
import { useGetProfile } from '../profile/hooks/useProfileData'
import PageLoading from '../../components/PageLoading'
import { useFormik } from 'formik'
import * as Yup from 'yup'
import { ErrorType } from '../../helpers/types'
import { useNavigate } from 'react-router-dom'
import { Pages } from '../../config/Pages'
import { CreateTicketAdminType } from './types/TicketTypes'
import { toast } from 'react-toastify'
import { useMultiUpload } from '../service/hooks/useServiceData'
import { useGetCustomers } from '../customer/hooks/useCustomerData'
import { CustomerItemType } from '../customer/types/CustomerTypes'
const CreateTicket: FC = () => {
const { t } = useTranslation('global')
const navigate = useNavigate()
const [files, setFiles] = useState<File[]>([])
const getProfile = useGetProfile()
const getCategories = useGetCategories()
const multiUpload = useMultiUpload()
const createTicket = useCreateTicket()
const getCustomers = useGetCustomers(1, true)
const formik = useFormik<CreateTicketAdminType>({
initialValues: {
subject: '',
message: '',
categoryId: '',
priority: 'LOW',
attachmentUrls: [],
userId: ''
},
validationSchema: Yup.object({
subject: Yup.string().required(t('errors.required')),
message: Yup.string().required(t('errors.required')),
categoryId: Yup.string().required(t('errors.required')),
priority: Yup.string().required(t('errors.required')),
}),
onSubmit: async (values) => {
if (files.length > 0) {
const images = new FormData()
files.forEach(file => {
images.append('files', file)
})
await multiUpload.mutateAsync(images, {
onSuccess: async (data) => {
values.attachmentUrls = await data.data.map((item: { url: string }) => item?.url)
},
onError: (error: ErrorType) => {
toast.error(error.response?.data?.error?.message[0])
}
})
} else {
delete values.attachmentUrls
}
if (values.danakServiceId === '') {
delete values.danakServiceId
}
createTicket.mutate(values, {
onSuccess: (data) => {
toast.success(t('success'))
formik.resetForm()
navigate(Pages.ticket.detail + data.data?.ticketId)
},
onError: (error: ErrorType) => {
toast.error(error.response?.data?.error?.message[0])
}
})
}
})
return (
<div className='mt-4'>
<div className='mt-4 pb-12'>
<div>
{t('ticket.new_ticket')}
</div>
{
getProfile.isPending || getCategories.isPending ?
<PageLoading />
:
<div className='flex gap-6 xl:mt-8 mt-4'>
<div className='flex-1 bg-white py-8 xl:px-10 px-4 rounded-3xl'>
<div>
{t('ticket.submit_your_message')}
</div>
<div className='mt-6 rowTwoInput'>
<Input
label={t('ticket.name')}
readOnly
placeholder='مهرداد مظفری'
/>
<Input
label={t('ticket.email')}
readOnly
placeholder='Info@example.com'
<div className='mt-5'>
<Select
label={t('receip.customer')}
placeholder={t('select')}
items={getCustomers.data?.data?.customers?.map((item: CustomerItemType) => {
return {
label: item.firstName + ' ' + item.lastName,
value: item.id
}
})}
{...formik.getFieldProps('userId')}
/>
</div>
@@ -41,51 +118,85 @@ const CreateTicket: FC = () => {
<Input
label={t('ticket.subject')}
placeholder={t('ticket.enter_your_subject')}
name='subject'
value={formik.values.subject}
onChange={formik.handleChange}
error_text={formik.touched.subject && formik.errors.subject ? formik.errors.subject : ''}
/>
<Select
label={t('ticket.subject')}
label={t('ticket.priority')}
items={[
{
label: '',
value: ''
label: t('ticket.low'),
value: 'LOW'
},
{
label: t('ticket.medium'),
value: 'MEDIUM'
},
{
label: t('ticket.high'),
value: 'HIGH'
}
]}
placeholder={t('ticket.priority')}
placeholder={t('select')}
className='border'
name='priority'
value={formik.values.priority}
onChange={formik.handleChange}
error_text={formik.touched.priority && formik.errors.priority ? formik.errors.priority : ''}
/>
</div>
<div className='mt-6'>
<div className='mt-6 rowTwoInput'>
<Select
label={t('ticket.select_your_service')}
items={[
{
label: '',
value: ''
}
]}
placeholder={t('ticket.not_once')}
label={t('ticket.category')}
items={getCategories.data?.data?.ticketCategories?.map((item: { id: string, title: string }) => ({
label: item.title,
value: item.id
})
)}
placeholder={t('select')}
className='border'
name='categoryId'
value={formik.values.categoryId}
onChange={formik.handleChange}
error_text={formik.touched.categoryId && formik.errors.categoryId ? formik.errors.categoryId : ''}
/>
</div>
<div className='mt-6'>
<Textarea
label={t('description')}
placeholder={t('ticket.your_description')}
name='message'
value={formik.values.message}
onChange={formik.handleChange}
error_text={formik.touched.message && formik.errors.message ? formik.errors.message : ''}
/>
</div>
<div className='mt-6'>
<UploadBox
label={t('ticket.attachment')}
isMultiple
onChange={setFiles}
/>
</div>
<div className='mt-8 flex justify-end'>
<Button
label={t('ticket.send')}
className='max-w-[100px]'
/>
className='xl:max-w-[100px]'
onClick={() => formik.handleSubmit()}
isLoading={createTicket.isPending || multiUpload.isPending}
>
<div className='flex gap-2 items-center'>
<TickCircle size={20} color='white' />
<div>
{t('ticket.send')}
</div>
</div>
</Button>
</div>
</div>
@@ -116,12 +227,15 @@ const CreateTicket: FC = () => {
<TickSquare size={20} color='black' variant='Bold' />
</div>
<div className='text-description text-xs leading-5'>
پاسخگویی 24 ساعته تلفنی را تنها از میهن وب هاست می توانید انتظار داشته باشید .بخش پشتیبانی در هر ساعتی حتی در روز های تعطیل آماده پیگیری سریع مشکلات کاربران است.
پاسخگویی 24 ساعته تلفنی را تنها از داناک می توانید انتظار داشته باشید .بخش پشتیبانی در هر ساعتی حتی در روز های تعطیل آماده پیگیری سریع مشکلات کاربران است.
</div>
</div>
</div>
</div>
</div>
}
</div>
)
+12 -1
View File
@@ -1,6 +1,10 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import * as api from "../service/TicketServiec";
import { AddMessageTicketType, CreateCategoryType } from "../types/TicketTypes";
import {
AddMessageTicketType,
CreateCategoryType,
CreateTicketAdminType,
} from "../types/TicketTypes";
export const useGetTickets = (
status: string,
@@ -67,3 +71,10 @@ export const useOpenTicket = () => {
mutationFn: (id: string) => api.openTicket(id),
});
};
export const useCreateTicket = () => {
return useMutation({
mutationFn: (variables: CreateTicketAdminType) =>
api.createTicket(variables),
});
};
+10 -1
View File
@@ -1,5 +1,9 @@
import axios from "../../../config/axios";
import { AddMessageTicketType, CreateCategoryType } from "../types/TicketTypes";
import {
AddMessageTicketType,
CreateCategoryType,
CreateTicketAdminType,
} from "../types/TicketTypes";
export const getTickets = async (
status: string,
@@ -54,3 +58,8 @@ export const openTicket = async (id: string) => {
const { data } = await axios.post(`/tickets/${id}/open`);
return data;
};
export const createTicket = async (params: CreateTicketAdminType) => {
const { data } = await axios.post(`/tickets/admin`, params);
return data;
};
+10
View File
@@ -79,3 +79,13 @@ export type CategoriesItemType = {
createdAt: string;
updatedAt: string;
};
export type CreateTicketAdminType = {
subject: string;
priority: "LOW" | "MEDIUM" | "HIGH";
danakServiceId?: string;
categoryId: string;
message: string;
attachmentUrls?: string[];
userId: string;
};