create ticket admin
This commit is contained in:
@@ -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'
|
||||
@@ -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)}
|
||||
/>
|
||||
|
||||
+215
-101
@@ -1,127 +1,241 @@
|
||||
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>
|
||||
|
||||
<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>
|
||||
|
||||
|
||||
<div className='mt-6 rowTwoInput'>
|
||||
<Input
|
||||
label={t('ticket.subject')}
|
||||
placeholder={t('ticket.enter_your_subject')}
|
||||
/>
|
||||
<Select
|
||||
label={t('ticket.subject')}
|
||||
items={[
|
||||
{
|
||||
label: '',
|
||||
value: ''
|
||||
}
|
||||
]}
|
||||
placeholder={t('ticket.priority')}
|
||||
className='border'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mt-6'>
|
||||
<Select
|
||||
label={t('ticket.select_your_service')}
|
||||
items={[
|
||||
{
|
||||
label: '',
|
||||
value: ''
|
||||
}
|
||||
]}
|
||||
placeholder={t('ticket.not_once')}
|
||||
className='border'
|
||||
/>
|
||||
</div>
|
||||
<div className='mt-6'>
|
||||
<Textarea
|
||||
label={t('description')}
|
||||
placeholder={t('ticket.your_description')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mt-6'>
|
||||
<UploadBox
|
||||
label={t('ticket.attachment')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mt-8 flex justify-end'>
|
||||
<Button
|
||||
label={t('ticket.send')}
|
||||
className='max-w-[100px]'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='bg-white w-sidebar xl:block hidden py-10 px-5 h-fit rounded-3xl'>
|
||||
<div className='text-sm'>
|
||||
{t('ticket.title_hint')}
|
||||
</div>
|
||||
|
||||
<div className='mt-6'>
|
||||
<div className='flex items-start gap-2 border-b pb-5'>
|
||||
{
|
||||
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>
|
||||
<TickSquare size={20} color='black' variant='Bold' />
|
||||
{t('ticket.submit_your_message')}
|
||||
</div>
|
||||
<div className='text-description text-xs leading-5'>
|
||||
سوالات - مشکلاتی که به یک موضوع مربوط میشوند را در یک درخواست پشتیبانی پیگیر باشید و چند درخواست برای یک موضوع باز نکنید.
|
||||
|
||||
<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>
|
||||
|
||||
|
||||
<div className='mt-6 rowTwoInput'>
|
||||
<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.priority')}
|
||||
items={[
|
||||
{
|
||||
label: t('ticket.low'),
|
||||
value: 'LOW'
|
||||
},
|
||||
{
|
||||
label: t('ticket.medium'),
|
||||
value: 'MEDIUM'
|
||||
},
|
||||
{
|
||||
label: t('ticket.high'),
|
||||
value: 'HIGH'
|
||||
}
|
||||
]}
|
||||
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 rowTwoInput'>
|
||||
|
||||
<Select
|
||||
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
|
||||
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>
|
||||
<div className='flex items-start gap-2 mt-6 border-b pb-5'>
|
||||
<div>
|
||||
<TickSquare size={20} color='black' variant='Bold' />
|
||||
|
||||
<div className='bg-white w-sidebar xl:block hidden py-10 px-5 h-fit rounded-3xl'>
|
||||
<div className='text-sm'>
|
||||
{t('ticket.title_hint')}
|
||||
</div>
|
||||
<div className='text-description text-xs leading-5'>
|
||||
لطفاً برای بررسی و رفع مشکلات احتمالی صبور باشید بررسی و رفع مشکلات در برخی موارد زمان گیر است.
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-start gap-2 mt-6'>
|
||||
<div>
|
||||
<TickSquare size={20} color='black' variant='Bold' />
|
||||
</div>
|
||||
<div className='text-description text-xs leading-5'>
|
||||
پاسخگویی 24 ساعته تلفنی را تنها از میهن وب هاست می توانید انتظار داشته باشید .بخش پشتیبانی در هر ساعتی حتی در روز های تعطیل آماده پیگیری سریع مشکلات کاربران است.
|
||||
|
||||
<div className='mt-6'>
|
||||
<div className='flex items-start gap-2 border-b pb-5'>
|
||||
<div>
|
||||
<TickSquare size={20} color='black' variant='Bold' />
|
||||
</div>
|
||||
<div className='text-description text-xs leading-5'>
|
||||
سوالات - مشکلاتی که به یک موضوع مربوط میشوند را در یک درخواست پشتیبانی پیگیر باشید و چند درخواست برای یک موضوع باز نکنید.
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-start gap-2 mt-6 border-b pb-5'>
|
||||
<div>
|
||||
<TickSquare size={20} color='black' variant='Bold' />
|
||||
</div>
|
||||
<div className='text-description text-xs leading-5'>
|
||||
لطفاً برای بررسی و رفع مشکلات احتمالی صبور باشید بررسی و رفع مشکلات در برخی موارد زمان گیر است.
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-start gap-2 mt-6'>
|
||||
<div>
|
||||
<TickSquare size={20} color='black' variant='Bold' />
|
||||
</div>
|
||||
<div className='text-description text-xs leading-5'>
|
||||
پاسخگویی 24 ساعته تلفنی را تنها از داناک می توانید انتظار داشته باشید .بخش پشتیبانی در هر ساعتی حتی در روز های تعطیل آماده پیگیری سریع مشکلات کاربران است.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -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),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user