admin ticket
This commit is contained in:
+281
-125
@@ -1,14 +1,89 @@
|
||||
import { FC } from 'react'
|
||||
import { FC, Fragment, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Input from '../../components/Input'
|
||||
import Textarea from '../../components/Textarea'
|
||||
import UploadBox from '../../components/UploadBox'
|
||||
import Button from '../../components/Button'
|
||||
import { CloseCircle, InfoCircle } from 'iconsax-react'
|
||||
import { CloseCircle, InfoCircle, Paperclip2 } from 'iconsax-react'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
import { useAddMessageTicket, useCloseTicket, useGetMessages } from './hooks/useTicketData'
|
||||
import PageLoading from '../../components/PageLoading'
|
||||
import moment from 'moment-jalaali'
|
||||
import { useFormik } from 'formik'
|
||||
import { AddMessageTicketType, TicketMessageType } from './types/TicketTypes'
|
||||
import * as Yup from 'yup'
|
||||
import { toast } from 'react-toastify'
|
||||
import { ErrorType } from '../../helpers/types'
|
||||
import { clx } from '../../helpers/utils'
|
||||
import { Pages } from '../../config/Pages'
|
||||
import { useMultiUpload } from '../service/hooks/useServiceData'
|
||||
|
||||
const TicketDetail: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const navigate = useNavigate()
|
||||
const { id } = useParams()
|
||||
const getMessages = useGetMessages(id ? id : '')
|
||||
const [files, setFiles] = useState<File[]>([])
|
||||
const addMessage = useAddMessageTicket(id ? id : '')
|
||||
const multiUpload = useMultiUpload()
|
||||
const [isResetFiles, setIsResetFiles] = useState<boolean>(false)
|
||||
const closeTicket = useCloseTicket()
|
||||
|
||||
const formik = useFormik<AddMessageTicketType>({
|
||||
initialValues: {
|
||||
content: '',
|
||||
attachmentUrls: [],
|
||||
},
|
||||
validationSchema: Yup.object().shape({
|
||||
content: 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
|
||||
}
|
||||
|
||||
addMessage.mutate(values, {
|
||||
onSuccess: () => {
|
||||
toast.success(t('success'))
|
||||
formik.resetForm()
|
||||
setFiles([])
|
||||
setIsResetFiles(true)
|
||||
setTimeout(() => {
|
||||
setIsResetFiles(false)
|
||||
}, 1000);
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast.error(error.response?.data?.error?.message[0])
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
const hadleCloseTicket = () => {
|
||||
closeTicket.mutate(id ? id : '', {
|
||||
onSuccess: () => {
|
||||
toast.success(t('success'))
|
||||
navigate(Pages.ticket.list)
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast.error(error.response?.data?.error?.message[0])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='mt-4'>
|
||||
@@ -16,139 +91,220 @@ const TicketDetail: FC = () => {
|
||||
<div>{t('ticket.ticket_number')}</div>
|
||||
<div>12312</div>
|
||||
</div>
|
||||
<div className='flex xl:flex-row flex-col-reverse gap-6 xl:mt-8 mt-6'>
|
||||
<div className='flex-1 bg-white py-8 xl:px-10 px-4 rounded-3xl'>
|
||||
<div className='gap-6 rowTwoInput'>
|
||||
<Input
|
||||
label={t('ticket.select_your_service')}
|
||||
value={'دی منو'}
|
||||
readOnly
|
||||
/>
|
||||
<Input
|
||||
label={t('ticket.user_name')}
|
||||
value={'مهرداد مظفری'}
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mt-8 xl:text-sm text-xs'>
|
||||
موضوع درخواست پشتیبانی
|
||||
</div>
|
||||
|
||||
<div className='mt-6 xl:text-sm text-xs bg-[#F6F7FA] p-6 rounded-3xl rounded-tr-none xl:max-w-[70%] max-w-[90%]'>
|
||||
<div className='leading-7'>
|
||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است، چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است، و برای شرایط فعلی تکنولوژی مورد نیاز، و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد، کتابهای زیادی در شصت و سه درصد گذشته حال و آینده، شناخت فراوان جامعه و متخصصان را می طلبد،
|
||||
</div>
|
||||
<div className='flex justify-end mt-6 text-xs text-description'>
|
||||
10:07 | 1403/09/30
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='w-full flex justify-end'>
|
||||
<div className='mt-6 xl:text-sm text-xs bg-[#EBEDF5] p-6 rounded-3xl rounded-tl-none xl:max-w-[70%] max-w-[90%]'>
|
||||
<div className='flex gap-1'>
|
||||
<div className='font-bold'>
|
||||
{t('ticket.expert')}
|
||||
</div>
|
||||
<div>سیما فرهادی</div>
|
||||
</div>
|
||||
<div className='leading-7 mt-4'>
|
||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است، چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است، و برای شرایط فعلی تکنولوژی مورد نیاز، و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد، کتابهای زیادی در شصت و سه درصد گذشته حال و آینده، شناخت فراوان جامعه و متخصصان را می طلبد،
|
||||
</div>
|
||||
<div className='flex justify-end mt-6 text-xs text-description'>
|
||||
10:07 | 1403/09/30
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='mt-9'>
|
||||
<Textarea
|
||||
label={t('ticket.answer')}
|
||||
placeholder={t('ticket.your_description')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mt-7'>
|
||||
<UploadBox
|
||||
label={t('ticket.attachment')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-end mt-6'>
|
||||
<Button
|
||||
label={t('ticket.send')}
|
||||
className='max-w-[100px]'
|
||||
/>
|
||||
</div>
|
||||
<div className='h-8'></div>
|
||||
</div>
|
||||
<div className={`h-fit xl:w-sidebar w-full`}>
|
||||
<div className='bg-white xl:p-6 p-4 rounded-3xl flex gap-4'>
|
||||
<Button
|
||||
label={t('ticket.send_answer')}
|
||||
className='text-xs xl:h-10 h-8'
|
||||
/>
|
||||
<Button
|
||||
className='bg-[#D52903] xl:h-10 h-8'
|
||||
>
|
||||
<div className='flex gap-2 text-xs items-center'>
|
||||
<CloseCircle
|
||||
color='white'
|
||||
size={20}
|
||||
{
|
||||
getMessages.isPending ?
|
||||
<PageLoading />
|
||||
:
|
||||
<div className='flex xl:flex-row flex-col-reverse gap-6 xl:mt-8 mt-6'>
|
||||
<div className='flex-1 bg-white py-8 xl:px-10 px-4 rounded-3xl'>
|
||||
<div className='gap-6 rowTwoInput'>
|
||||
<Input
|
||||
label={t('ticket.select_your_service')}
|
||||
value={'دی منو'}
|
||||
readOnly
|
||||
/>
|
||||
<Input
|
||||
label={t('ticket.user_name')}
|
||||
value={getMessages.data?.data?.ticket?.user?.firstName + ' ' + getMessages.data?.data?.ticket?.user?.lastName}
|
||||
readOnly
|
||||
/>
|
||||
<div>
|
||||
{t('ticket.close_ticket')}
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
<div className='bg-white p-6 rounded-3xl mt-8'>
|
||||
<div className='flex justify-between'>
|
||||
<div className='text-sm'>
|
||||
{t('ticket.info_ticket')}
|
||||
</div>
|
||||
<div className='h-7 px-3 rounded-xl flex items-center text-xs bg-green-100 text-green-400'>
|
||||
{t('ticket.answered')}
|
||||
|
||||
<div className='mt-8 xl:text-sm text-xs'>
|
||||
{getMessages.data?.data?.ticket?.subject}
|
||||
</div>
|
||||
|
||||
{
|
||||
getMessages.data?.data?.messages.map((item: TicketMessageType) => {
|
||||
if (item?.author?.role?.name === 'user') {
|
||||
return (
|
||||
<div key={item.id} className='mt-6 xl:text-sm text-xs bg-[#F6F7FA] p-6 rounded-3xl rounded-tr-none xl:max-w-[70%] max-w-[90%]'>
|
||||
<div className='leading-7'>
|
||||
{item.content}
|
||||
</div>
|
||||
<div className='flex dltr end mt-6 text-xs text-description'>
|
||||
{moment(item.createdAt).format('jYYYY-jMM-jDD HH:mm')}
|
||||
</div>
|
||||
|
||||
<div className='flex gap-2 flex-wrap text-sm'>
|
||||
{
|
||||
item.attachments && item?.attachments?.map((item, index: number) => {
|
||||
return (
|
||||
<div className='text-[#0047FF] mt-2 flex gap-1 items-center' key={item.id}>
|
||||
<Paperclip2 size={20} color='#0047FF' />
|
||||
<a href={item.attachmentUrl} target='_blank' rel='noreferrer'>
|
||||
{t('attach') + ' ' + Number(index + 1)}
|
||||
</a>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<div className='w-full flex justify-end'>
|
||||
<div className='mt-6 xl:text-sm text-xs bg-[#EBEDF5] p-6 rounded-3xl rounded-tl-none xl:max-w-[70%] max-w-[90%]'>
|
||||
<div className='flex gap-1'>
|
||||
<div className='font-bold'>
|
||||
{t('ticket.expert')}
|
||||
</div>
|
||||
<div>{item.author?.firstName + ' ' + item?.author?.lastName}</div>
|
||||
</div>
|
||||
<div className='leading-7 mt-4'>
|
||||
{item.content}
|
||||
</div>
|
||||
<div className='flex dltr mt-6 text-xs text-description'>
|
||||
{moment(item.createdAt).format('jYYYY-jMM-jDD HH:mm')}
|
||||
</div>
|
||||
|
||||
<div className='flex gap-2 flex-wrap text-sm'>
|
||||
{
|
||||
item.attachments && item?.attachments?.map((item, index: number) => {
|
||||
return (
|
||||
<div className='text-[#0047FF] mt-2 flex gap-1 items-center' key={item.id}>
|
||||
<Paperclip2 size={20} color='#0047FF' />
|
||||
<a href={item.attachmentUrl} target='_blank' rel='noreferrer'>
|
||||
{t('attach') + ' ' + Number(index + 1)}
|
||||
</a>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
|
||||
<div className='flex gap-2'></div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
{
|
||||
getMessages.data?.data?.ticket?.status !== 'CLOSED' &&
|
||||
<Fragment>
|
||||
<div className='mt-9'>
|
||||
<Textarea
|
||||
label={t('ticket.answer')}
|
||||
placeholder={t('ticket.your_description')}
|
||||
value={formik.values.content}
|
||||
onChange={formik.handleChange}
|
||||
name='content'
|
||||
error_text={formik.touched.content && formik.errors.content ? formik.errors.content : ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mt-7'>
|
||||
<UploadBox
|
||||
label={t('ticket.attachment')}
|
||||
onChange={setFiles}
|
||||
isMultiple
|
||||
isReset={isResetFiles}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-end mt-6'>
|
||||
<Button
|
||||
label={t('ticket.send')}
|
||||
className='max-w-[100px]'
|
||||
onClick={() => formik.handleSubmit()}
|
||||
isLoading={addMessage.isPending || multiUpload.isPending}
|
||||
/>
|
||||
</div>
|
||||
</Fragment>
|
||||
}
|
||||
<div className='h-8'></div>
|
||||
</div>
|
||||
<div className={`h-fit lg:sticky left-0 lg:top-0 xl:w-sidebar w-full`}>
|
||||
{
|
||||
getMessages.data?.data?.ticket?.status !== 'CLOSED' &&
|
||||
<div className='bg-white xl:p-6 p-4 rounded-3xl flex gap-4'>
|
||||
<Button
|
||||
label={t('ticket.send_answer')}
|
||||
className='text-xs xl:h-10 h-8'
|
||||
/>
|
||||
<Button
|
||||
className='bg-[#D52903] xl:h-10 h-8'
|
||||
onClick={hadleCloseTicket}
|
||||
isLoading={closeTicket.isPending}
|
||||
>
|
||||
<div className='flex gap-2 text-xs items-center'>
|
||||
<CloseCircle
|
||||
color='white'
|
||||
size={20}
|
||||
/>
|
||||
<div>
|
||||
{t('ticket.close_ticket')}
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
<div className={clx(
|
||||
'bg-white p-6 rounded-3xl mt-8',
|
||||
getMessages.data?.data?.ticket?.status === 'CLOSED' && 'mt-0'
|
||||
)}>
|
||||
<div className='flex justify-between'>
|
||||
<div className='text-sm'>
|
||||
{t('ticket.info_ticket')}
|
||||
</div>
|
||||
<div className={clx(
|
||||
'h-7 px-3 rounded-xl flex items-center text-xs bg-green-100 text-green-400',
|
||||
getMessages.data?.data?.ticket?.status === 'PENDING' && 'bg-orange-100 text-orange-700',
|
||||
getMessages.data?.data?.ticket?.status === 'CLOSED' && 'bg-red-100 text-red-400'
|
||||
)}>
|
||||
{t(`ticket.${getMessages.data?.data?.ticket?.status}`)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='mt-14 text-xs w-full'>
|
||||
<div className='flex justify-between text-description'>
|
||||
<div>{t('ticket.ticket_number')}</div>
|
||||
<div>
|
||||
۱۲۳۴۵۵
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex mt-6 justify-between text-description'>
|
||||
<div>{t('ticket.ticket_number')}</div>
|
||||
<div>
|
||||
۱۲۳۴۵۵
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex mt-6 justify-between text-description'>
|
||||
<div>{t('ticket.ticket_number')}</div>
|
||||
<div>
|
||||
۱۲۳۴۵۵
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex mt-6 justify-between text-description'>
|
||||
<div>{t('ticket.ticket_number')}</div>
|
||||
<div>
|
||||
۱۲۳۴۵۵
|
||||
</div>
|
||||
</div>
|
||||
<div className='mt-14 text-xs w-full'>
|
||||
<div className='flex justify-between text-description'>
|
||||
<div>{t('ticket.ticket_number')}</div>
|
||||
<div>
|
||||
{getMessages.data?.data?.ticket?.numericId}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex mt-6 justify-between text-description'>
|
||||
<div>{t('ticket.asignto')}</div>
|
||||
<div>
|
||||
{getMessages.data?.data?.ticket?.assignedTo ? getMessages.data?.data?.ticket?.assignedTo?.firstName + ' ' + getMessages.data?.data?.ticket?.assignedTo?.lastName : ''}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex mt-6 justify-between text-description'>
|
||||
<div>{t('date')}</div>
|
||||
<div className='dltr'>
|
||||
{moment(getMessages.data?.data?.ticket?.createdAt).format('jYYYY/jMM/jDD HH:mm')}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex mt-6 justify-between text-description'>
|
||||
<div>{t('ticket.category')}</div>
|
||||
<div>
|
||||
{getMessages.data?.data?.ticket?.category?.title}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex mt-6 justify-between text-description'>
|
||||
<div>{t('ticket.priority')}</div>
|
||||
<div>
|
||||
{getMessages.data?.data?.ticket?.priority === 'low' ? t('ticket.low') : getMessages.data?.data?.ticket?.priority === 'medium' ? t('ticket.medium') : t('ticket.high')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='mt-10 border-t text-xs pt-5 border-border items-center flex gap-2'>
|
||||
<InfoCircle size={20} color='black' />
|
||||
<div>
|
||||
{t('ticket.update_sms')}
|
||||
<div className='mt-10 border-t text-xs pt-5 border-border items-center flex gap-2'>
|
||||
<InfoCircle size={20} color='black' />
|
||||
<div>
|
||||
{t('ticket.update_sms')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user