ticket
This commit is contained in:
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
@@ -0,0 +1,45 @@
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 201.8 46.7" xml:space="preserve" class="logo-shape dark" style="enable-background:new 0 0 201.8 46.7;"><g><g><path d="M51,38.6h-1.9v5.1h-0.8v-5.1h-1.9V38H51L51,38.6L51,38.6z" class="st0"></path> <path d="M57.6,43.8c-0.6,0-1.1-0.2-1.4-0.6c-0.3-0.4-0.5-0.9-0.5-1.5v-0.2c0-0.6,0.2-1.1,0.5-1.5s0.8-0.6,1.3-0.6
|
||||
c0.6,0,1,0.2,1.3,0.5s0.4,0.8,0.4,1.4v0.4h-2.7l0,0c0,0.4,0.1,0.8,0.3,1c0.2,0.3,0.5,0.4,0.9,0.4c0.3,0,0.5,0,0.7-0.1
|
||||
c0.2-0.1,0.4-0.2,0.5-0.3l0.3,0.5c-0.1,0.1-0.4,0.3-0.6,0.4C58.3,43.7,58,43.8,57.6,43.8z M57.5,40c-0.3,0-0.5,0.1-0.7,0.3
|
||||
c-0.2,0.2-0.3,0.5-0.3,0.8l0,0h1.9V41c0-0.3-0.1-0.6-0.2-0.8S57.8,40,57.5,40z" class="st0"></path> <path d="M66.3,43.2c0.2,0,0.5-0.1,0.6-0.2s0.3-0.4,0.3-0.6h0.7l0,0c0,0.4-0.1,0.7-0.5,1c-0.3,0.3-0.7,0.4-1.1,0.4
|
||||
c-0.6,0-1.1-0.2-1.4-0.6c-0.3-0.4-0.5-0.9-0.5-1.5v-0.2c0-0.6,0.2-1.1,0.5-1.5s0.8-0.6,1.4-0.6c0.5,0,0.9,0.1,1.2,0.4
|
||||
c0.3,0.3,0.5,0.7,0.4,1.1l0,0h-0.7c0-0.3-0.1-0.5-0.3-0.7S66.5,40,66.3,40c-0.4,0-0.7,0.1-0.8,0.4c-0.2,0.3-0.3,0.6-0.3,1.1v0.2
|
||||
c0,0.4,0.1,0.8,0.3,1.1C65.6,43,65.9,43.2,66.3,43.2z" class="st0"></path> <path d="M74,40c0.1-0.2,0.3-0.4,0.5-0.5s0.5-0.2,0.7-0.2c0.5,0,0.8,0.1,1.1,0.4s0.4,0.7,0.4,1.3v2.7H76V41
|
||||
c0-0.4-0.1-0.6-0.2-0.8C75.6,40.1,75.3,40,75,40c-0.2,0-0.4,0-0.6,0.1c-0.2,0.1-0.3,0.2-0.4,0.4v3.2h-0.8v-6.1H74L74,40L74,40z" class="st0"></path> <path d="M83,39.4l0.1,0.6c0.1-0.2,0.3-0.4,0.5-0.5c0.2-0.1,0.5-0.2,0.7-0.2c0.5,0,0.8,0.1,1.1,0.4s0.4,0.7,0.4,1.3
|
||||
v2.7H85V41c0-0.4-0.1-0.6-0.2-0.8c-0.1-0.2-0.4-0.2-0.7-0.2c-0.2,0-0.4,0-0.6,0.1c-0.2,0.1-0.3,0.2-0.4,0.4v3.2h-0.8v-4.3L83,39.4
|
||||
L83,39.4z" class="st0"></path> <path d="M91.2,41.5c0-0.6,0.2-1.1,0.5-1.6s0.8-0.6,1.4-0.6c0.6,0,1.1,0.2,1.4,0.6s0.5,0.9,0.5,1.6v0.1
|
||||
c0,0.6-0.2,1.2-0.5,1.6s-0.8,0.6-1.4,0.6c-0.6,0-1-0.2-1.4-0.6C91.4,42.8,91.2,42.3,91.2,41.5L91.2,41.5z M92,41.6
|
||||
c0,0.5,0.1,0.8,0.3,1.1s0.5,0.4,0.9,0.4c0.4,0,0.6-0.1,0.8-0.4s0.3-0.7,0.3-1.1v-0.1c0-0.5-0.1-0.8-0.3-1.1
|
||||
c-0.2-0.3-0.5-0.5-0.9-0.5s-0.6,0.1-0.8,0.5S92,41.1,92,41.6L92,41.6z" class="st0"></path> <path d="M101.3,43.7h-0.8v-6.1h0.8V43.7z" class="st0"></path> <path d="M106.8,41.5c0-0.6,0.2-1.1,0.5-1.6s0.8-0.6,1.4-0.6c0.6,0,1.1,0.2,1.4,0.6s0.5,0.9,0.5,1.6v0.1
|
||||
c0,0.6-0.2,1.2-0.5,1.6s-0.8,0.6-1.4,0.6s-1-0.2-1.4-0.6C107,42.8,106.8,42.3,106.8,41.5L106.8,41.5z M107.6,41.6
|
||||
c0,0.5,0.1,0.8,0.3,1.1s0.5,0.4,0.9,0.4c0.4,0,0.6-0.1,0.8-0.4s0.3-0.7,0.3-1.1v-0.1c0-0.5-0.1-0.8-0.3-1.1s-0.5-0.5-0.9-0.5
|
||||
s-0.6,0.1-0.8,0.5S107.6,41.1,107.6,41.6L107.6,41.6z" class="st0"></path> <path d="M115.9,41.7c0-0.7,0.1-1.2,0.4-1.7s0.7-0.6,1.2-0.6c0.3,0,0.5,0,0.7,0.2c0.2,0.1,0.4,0.3,0.5,0.5l0.1-0.6h0.6
|
||||
v4.3c0,0.5-0.2,1-0.5,1.3s-0.8,0.4-1.4,0.4c-0.2,0-0.4,0-0.7-0.1c-0.2-0.1-0.5-0.1-0.6-0.2l0.1-0.6c0.1,0.1,0.3,0.1,0.5,0.2
|
||||
c0.2,0,0.4,0.1,0.6,0.1c0.4,0,0.6-0.1,0.8-0.3c0.2-0.2,0.3-0.4,0.3-0.8v-0.5c-0.1,0.2-0.3,0.3-0.5,0.4c-0.2,0.1-0.4,0.1-0.7,0.1
|
||||
c-0.5,0-0.9-0.2-1.2-0.6S115.9,42.4,115.9,41.7L115.9,41.7L115.9,41.7z M116.7,41.7c0,0.4,0.1,0.8,0.3,1s0.5,0.4,0.8,0.4
|
||||
c0.2,0,0.4,0,0.6-0.2c0.2-0.1,0.3-0.3,0.4-0.5v-2c-0.1-0.2-0.2-0.3-0.4-0.4c-0.2-0.1-0.4-0.2-0.6-0.2c-0.4,0-0.6,0.1-0.8,0.5
|
||||
S116.7,41.2,116.7,41.7L116.7,41.7L116.7,41.7z" class="st0"></path> <path d="M126.4,42.1l0.1,0.5h0l1-3.2h0.9l-1.8,4.9c-0.1,0.3-0.3,0.5-0.5,0.8c-0.2,0.2-0.5,0.3-0.8,0.3
|
||||
c-0.1,0-0.1,0-0.2,0c-0.1,0-0.2,0-0.2,0l0.1-0.6c0,0,0,0,0.1,0c0.1,0,0.2,0,0.2,0c0.2,0,0.3-0.1,0.4-0.2c0.1-0.1,0.2-0.3,0.3-0.5
|
||||
l0.2-0.5l-1.6-4.2h0.9L126.4,42.1z" class="st0"></path> <path d="M144.4,41.9L144.4,41.9c0,0.6-0.2,1-0.6,1.4s-0.9,0.5-1.5,0.5c-0.6,0-1.2-0.2-1.6-0.7s-0.6-1-0.6-1.8v-1
|
||||
c0-0.7,0.2-1.3,0.6-1.8s0.9-0.7,1.6-0.7s1.2,0.2,1.5,0.5c0.4,0.3,0.6,0.8,0.6,1.4l0,0h-0.8c0-0.4-0.1-0.7-0.4-1
|
||||
c-0.2-0.2-0.6-0.4-1-0.4s-0.8,0.2-1,0.5c-0.3,0.4-0.4,0.8-0.4,1.3v1c0,0.5,0.1,1,0.4,1.3s0.6,0.5,1,0.5c0.4,0,0.8-0.1,1-0.4
|
||||
s0.4-0.5,0.4-1L144.4,41.9L144.4,41.9z" class="st0"></path> <path d="M149.6,41.5c0-0.6,0.2-1.1,0.5-1.6s0.8-0.6,1.4-0.6c0.6,0,1.1,0.2,1.4,0.6s0.5,0.9,0.5,1.6v0.1
|
||||
c0,0.6-0.2,1.2-0.5,1.6s-0.8,0.6-1.4,0.6s-1-0.2-1.4-0.6C149.8,42.8,149.6,42.3,149.6,41.5L149.6,41.5z M150.4,41.6
|
||||
c0,0.5,0.1,0.8,0.3,1.1s0.5,0.4,0.9,0.4c0.4,0,0.6-0.1,0.8-0.4s0.3-0.7,0.3-1.1v-0.1c0-0.5-0.1-0.8-0.3-1.1s-0.5-0.5-0.9-0.5
|
||||
s-0.6,0.1-0.8,0.5S150.4,41.1,150.4,41.6L150.4,41.6z" class="st0"></path> <path d="M159.5,39.4l0,0.6c0.1-0.2,0.3-0.4,0.5-0.5c0.2-0.1,0.5-0.2,0.8-0.2s0.5,0.1,0.7,0.2s0.4,0.3,0.5,0.6
|
||||
c0.1-0.3,0.3-0.4,0.5-0.6c0.2-0.1,0.5-0.2,0.8-0.2c0.4,0,0.8,0.1,1,0.5c0.3,0.3,0.4,0.8,0.4,1.3v2.5H164v-2.6
|
||||
c0-0.4-0.1-0.7-0.2-0.9c-0.1-0.2-0.4-0.3-0.6-0.3c-0.3,0-0.5,0.1-0.6,0.3c-0.2,0.2-0.3,0.4-0.3,0.7v0v2.7h-0.8v-2.6
|
||||
c0-0.4-0.1-0.7-0.2-0.9s-0.4-0.3-0.6-0.3c-0.2,0-0.4,0-0.6,0.1s-0.3,0.2-0.4,0.4v3.2h-0.8v-4.3L159.5,39.4L159.5,39.4z" class="st0"></path> <path d="M174,41.7c0,0.6-0.1,1.1-0.4,1.5s-0.7,0.6-1.2,0.6c-0.3,0-0.5,0-0.7-0.1s-0.4-0.2-0.5-0.4v2.1h-0.8v-5.9h0.6
|
||||
l0.1,0.5c0.1-0.2,0.3-0.4,0.5-0.5c0.2-0.1,0.5-0.2,0.7-0.2c0.5,0,0.9,0.2,1.2,0.6S174,41,174,41.7L174,41.7L174,41.7z M173.2,41.7
|
||||
c0-0.5-0.1-0.9-0.3-1.2s-0.5-0.5-0.8-0.5c-0.2,0-0.4,0-0.6,0.1s-0.3,0.2-0.4,0.4v2.1c0.1,0.2,0.2,0.3,0.4,0.4s0.4,0.1,0.6,0.1
|
||||
c0.4,0,0.6-0.1,0.8-0.4C173.1,42.5,173.2,42.2,173.2,41.7L173.2,41.7L173.2,41.7z" class="st0"></path> <path d="M182.1,43.7c0-0.1,0-0.2-0.1-0.3s0-0.2,0-0.3c-0.1,0.2-0.3,0.4-0.6,0.5s-0.5,0.2-0.8,0.2
|
||||
c-0.4,0-0.8-0.1-1-0.3s-0.4-0.5-0.4-0.9c0-0.4,0.2-0.7,0.5-1c0.3-0.2,0.8-0.3,1.3-0.3h0.8v-0.4c0-0.3-0.1-0.5-0.2-0.6
|
||||
s-0.4-0.2-0.6-0.2c-0.3,0-0.5,0.1-0.6,0.2c-0.2,0.1-0.2,0.3-0.2,0.5l-0.7,0l0,0c0-0.3,0.1-0.6,0.4-0.9s0.7-0.4,1.2-0.4
|
||||
c0.5,0,0.9,0.1,1.2,0.4c0.3,0.3,0.4,0.6,0.4,1.1v2c0,0.1,0,0.3,0,0.4c0,0.1,0,0.3,0.1,0.4L182.1,43.7L182.1,43.7z M180.8,43.1
|
||||
c0.3,0,0.5-0.1,0.8-0.2c0.2-0.1,0.4-0.3,0.4-0.5v-0.7h-0.9c-0.3,0-0.6,0.1-0.8,0.2s-0.3,0.3-0.3,0.6c0,0.2,0.1,0.3,0.2,0.5
|
||||
S180.5,43.1,180.8,43.1z" class="st0"></path> <path d="M189.1,39.4l0.1,0.6c0.1-0.2,0.3-0.4,0.5-0.5c0.2-0.1,0.5-0.2,0.7-0.2c0.5,0,0.8,0.1,1.1,0.4s0.4,0.7,0.4,1.3
|
||||
v2.7H191V41c0-0.4-0.1-0.6-0.2-0.8c-0.1-0.2-0.4-0.2-0.7-0.2c-0.2,0-0.4,0-0.6,0.1c-0.2,0.1-0.3,0.2-0.4,0.4v3.2h-0.8v-4.3
|
||||
L189.1,39.4L189.1,39.4z" class="st0"></path> <path d="M198.7,42.1l0.1,0.5h0l1-3.2h0.9l-1.8,4.9c-0.1,0.3-0.3,0.5-0.5,0.8s-0.5,0.3-0.8,0.3c-0.1,0-0.1,0-0.2,0
|
||||
c-0.1,0-0.2,0-0.2,0l0.1-0.6c0,0,0,0,0.1,0s0.2,0,0.2,0c0.2,0,0.3-0.1,0.4-0.2s0.2-0.3,0.3-0.5l0.2-0.5l-1.6-4.2h0.9L198.7,42.1z" class="st0"></path></g></g> <g><path d="M46.4,31.9V9.9h6.2c1.9,0,3.6,0.4,5.1,1.3c1.5,0.9,2.6,2.1,3.4,3.6c0.8,1.6,1.2,3.4,1.2,5.4v1.4
|
||||
c0,2.1-0.4,3.9-1.2,5.5c-0.8,1.6-2,2.8-3.4,3.6s-3.2,1.3-5.2,1.3H46.4z M49.3,12.2v17.3h3.1c2.2,0,4-0.7,5.2-2.1
|
||||
c1.3-1.4,1.9-3.4,1.9-6v-1.3c0-2.5-0.6-4.5-1.8-5.9s-2.9-2.1-5-2.1L49.3,12.2L49.3,12.2z" class="st0"></path> <path d="M131.9,31.9H129l-11.1-17v17h-2.9V9.9h2.9L129,26.9V9.9h2.9L131.9,31.9L131.9,31.9z" class="st0"></path> <path d="M189.6,21.7l-2.7,2.8v7.5H184V9.8h2.9v10.9l9.8-10.9h3.5l-8.7,9.7l9.4,12.3h-3.5L189.6,21.7z" class="st0"></path> <polygon points="88.9,13.4 95.6,31.9 98.6,31.9 90.1,9.8 87.6,9.8 79.2,31.9 82.2,31.9 " class="st0"></polygon> <polygon points="157.9,13.4 164.7,31.9 167.6,31.9 159.2,9.8 156.7,9.8 148.2,31.9 151.2,31.9 " class="st0"></polygon></g> <path d="M27.5,30.2c0,0-5.5,5.3-10.1,9.8v6.1c-0.5,0.4,16.4-15.9,16.4-15.9L17.4,13v6.5L27.5,30.2z" class="st0"></path> <g><rect x="9.1" y="13.1" transform="matrix(0.7298 -0.6837 0.6837 0.7298 -14.5845 13.0725)" width="0.3" height="23.8" class="st0"></rect> <rect x="9.9" y="13.4" transform="matrix(0.7287 -0.6849 0.6849 0.7287 -13.8472 13.4064)" width="0.3" height="21.6" class="st0"></rect> <polygon points="17.3,30.5 4,16.4 4.2,16.2 17.5,30.3 " class="st0"></polygon> <polygon points="17.3,28.9 5.5,16.5 5.7,16.3 17.5,28.7 " class="st0"></polygon> <polygon points="17.2,27.2 7,16.5 7.2,16.3 17.5,27.1 " class="st0"></polygon></g> <g><polygon points="1.3,16.5 1.1,16.3 17.3,0.5 17.5,0.7 " class="st0"></polygon> <polygon points="2.8,16.5 2.6,16.3 17.3,2 17.4,2.2 " class="st0"></polygon> <polygon points="4.3,16.5 4.1,16.3 17.2,3.5 17.4,3.7 " class="st0"></polygon> <polygon points="5.8,16.5 5.6,16.3 17.2,5 17.4,5.2 " class="st0"></polygon> <polygon points="7.4,16.5 7.2,16.3 17.2,6.5 17.4,6.7 " class="st0"></polygon></g></svg>
|
||||
|
After Width: | Height: | Size: 8.2 KiB |
@@ -0,0 +1,68 @@
|
||||
import { FC, useState } from 'react'
|
||||
import DefaulModal from './DefaulModal'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from './Button'
|
||||
import Textarea from './Textarea'
|
||||
|
||||
type Props = {
|
||||
isLoading?: boolean,
|
||||
close: () => void,
|
||||
isOpen: boolean,
|
||||
onConfrim: (text?: string) => void,
|
||||
label?: string,
|
||||
isHasDescription?: boolean
|
||||
}
|
||||
|
||||
const ModalConfrim: FC<Props> = (props: Props) => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const [description, setDescription] = useState<string>('')
|
||||
|
||||
return (
|
||||
<DefaulModal
|
||||
open={props.isOpen}
|
||||
close={props.close}
|
||||
title_header={t('confrim.subject')}
|
||||
isHeader
|
||||
>
|
||||
<div className='mt-6'>
|
||||
<div className='text-sm text-center'>
|
||||
{
|
||||
props.label ?
|
||||
props.label
|
||||
:
|
||||
t('confrim.content')
|
||||
}
|
||||
|
||||
{
|
||||
props.isHasDescription &&
|
||||
<div className='mt-4'>
|
||||
<Textarea
|
||||
placeholder={t('description')}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
value={description}
|
||||
className='bg-transparent border border-gray-500 rounded-xl w-[300px] p-2'
|
||||
label=''
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div className='flex gap-4 justify-center mt-10'>
|
||||
<Button
|
||||
label={t('confrim.yes')}
|
||||
onClick={() => props.onConfrim(props.isHasDescription ? description : undefined)}
|
||||
isLoading={props.isLoading}
|
||||
/>
|
||||
<Button
|
||||
label={t('confrim.cancel')}
|
||||
className='bg-transparent text-black border border-primary'
|
||||
onClick={props.close}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</DefaulModal>
|
||||
)
|
||||
}
|
||||
|
||||
export default ModalConfrim
|
||||
@@ -0,0 +1,19 @@
|
||||
import { FC } from 'react'
|
||||
import Logo from '../assets/images/logo_orig.svg'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const PageLoading: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
|
||||
return (
|
||||
<div className='flex bg-transparent w-fit mx-auto flex-col gap-4 items-center'>
|
||||
<img src={Logo} alt='logo' className='w-28' />
|
||||
<div className='text-xs text-gray-700'>
|
||||
{t('loading')}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default PageLoading
|
||||
@@ -2,6 +2,7 @@ import { FC, InputHTMLAttributes } from 'react'
|
||||
|
||||
type Props = {
|
||||
label: string,
|
||||
error_text?: string,
|
||||
} & InputHTMLAttributes<HTMLTextAreaElement>
|
||||
|
||||
const Textarea: FC<Props> = (props: Props) => {
|
||||
|
||||
+70
-7
@@ -99,7 +99,10 @@
|
||||
"home": {
|
||||
"last_announcement": "آخرین اطلاعیه ها",
|
||||
"see_all": "دیدن همه",
|
||||
"companies": "شرکت ها"
|
||||
"companies": "شرکت ها",
|
||||
"request_pending": "درخواست شما برای ثبت شرکت، هنوز تایید نشده است",
|
||||
"request_approved": "درخواست شما برای ثبت شرکت، تایید شد.",
|
||||
"request_rejected": "درخواست شما برای ثبت شرکت، رد شده است."
|
||||
},
|
||||
"all": "همه",
|
||||
"service": {
|
||||
@@ -172,12 +175,27 @@
|
||||
"send_answer": "ارسال پاسخ",
|
||||
"close_ticket": "بستن تیکت",
|
||||
"info_ticket": "اطلاعات تیکت",
|
||||
"update_sms": "به روزرسانی این تیکت از طریق پیامک اطلاع داده میشود"
|
||||
"update_sms": "به روزرسانی این تیکت از طریق پیامک اطلاع داده میشود",
|
||||
"low": "پایین",
|
||||
"medium": "متوسط",
|
||||
"high": "بالا",
|
||||
"category": "دسته بندی",
|
||||
"asignto": "کارشناس پاسخگویی",
|
||||
"MEDIUM": "متوسط",
|
||||
"LOW": "پایین",
|
||||
"HIGH": "بالا",
|
||||
"PENDING": "در انتظار",
|
||||
"ANSWERED": "پاسخ داده شده",
|
||||
"CLOSED": "بسته شده",
|
||||
"service": "سرویس",
|
||||
"all": "همه"
|
||||
},
|
||||
"date": "تاریخ",
|
||||
"success": "عملیات با موفقیت انجام شد",
|
||||
"select": "انتخاب",
|
||||
"receip": {
|
||||
"receip": "صورتحساب",
|
||||
"receip_list": "لیست صورتحساب ها",
|
||||
"user_list": "لیست کاربران",
|
||||
"Language_learners_list": "لیست زبان آموزان",
|
||||
"dont_pay": "پرداخت نشده",
|
||||
"dont_confrim": "تایید نشده",
|
||||
"paid": "پرداخت شده",
|
||||
@@ -185,6 +203,8 @@
|
||||
"returned": "پس داده شده",
|
||||
"its_time": "سررسیده شده",
|
||||
"archive": "بایگانی شده",
|
||||
"overdue": "سررسید شده",
|
||||
"late_fee": "جریمه دیرکرد",
|
||||
"detail_account": "جزییات صورتحساب ",
|
||||
"personal_information": "اطلاعات شخصی",
|
||||
"type_person": "نوع شخص : ",
|
||||
@@ -193,11 +213,11 @@
|
||||
"company_name": "نام شرکت/ سازمان :",
|
||||
"registration_number": "شماره ثبت :",
|
||||
"tel_company": "تلفن شرکت :",
|
||||
"national_code": "کد ملی نماینده :",
|
||||
"national_code": " شناسه ملی :",
|
||||
"postal_code": "کد پستی :",
|
||||
"state": "استان :",
|
||||
"city": "شهر :",
|
||||
"address": "آدرس :",
|
||||
"address": "آدرس ثبتی :",
|
||||
"detail_receip": "مشخصات صورتحساب",
|
||||
"not_paid": "پرداخت نشده",
|
||||
"description": "توضیحات",
|
||||
@@ -206,8 +226,51 @@
|
||||
"date_factor": "تاریخ صدور فاکتور",
|
||||
"due_date": "تاریخ سررسید فاکتور",
|
||||
"factor_number": "شماره فاکتور",
|
||||
"status_factor": "وضعیت فاکتور"
|
||||
"status_factor": "وضعیت فاکتور",
|
||||
"customer": "مشتری",
|
||||
"receipt_information": "اطلاعات صورتحساب",
|
||||
"number": "شماره",
|
||||
"product_name": "نام کالا",
|
||||
"count": "تعداد",
|
||||
"unit_amount": "مبلغ واحد",
|
||||
"discount": "تخفیف(٪)",
|
||||
"total_amount": "مبلغ کل(تومان)",
|
||||
"tax": "مالیات بر ارزش افزوده (۰.۹ درصد)",
|
||||
"total": "جمع",
|
||||
"error_empty": "همه فیلد ها را پر کنید",
|
||||
"date_receipt": "تاریخ صورتحساب",
|
||||
"last_date_receipt": "آخرین مهلت پرداخت",
|
||||
"service": "سرویس",
|
||||
"status": "وضعیت ",
|
||||
"status_paid": "وضعیت پرداخت",
|
||||
"PENDING": "در انتظار تایید ",
|
||||
"PAID": "پرداخت شده",
|
||||
"CANCELED": "لغو شده",
|
||||
"EXPIRED": "منقضی شده",
|
||||
"WAIT_PAYMENT": "در انتظار پرداخت",
|
||||
"real_person": "شخص حقیقی",
|
||||
"fullName": "نام و نام خانوادگی",
|
||||
"compelete_financal_info_error": "برای دریافت فاکتور رسمی اطلاعات مالی خود را وارد کنید",
|
||||
"compelete_financal_info": "تکمیل اطلاعات مالی",
|
||||
"get_factor": "دریافت پیش فاکتور",
|
||||
"pay": "پرداخت",
|
||||
"confrim_factor": "تایید فاکتور",
|
||||
"sure_confrim_factor": "برای تایید فاکتور مطمئن هستید؟",
|
||||
"accepted": "تایید شده",
|
||||
"sure_confrim_pay": "آیا مطمئن هستید که مبلغ صورتحساب از کیف پول شما کسر شود؟",
|
||||
"thank_pay_1": "ضمن تشکر از خرید شما",
|
||||
"thank_pay_2": "مبلغ این صورت حساب پرداخت شده است.",
|
||||
"discount_code": "کد تخفیف",
|
||||
"apply": "اعمال",
|
||||
"discount_applied": "کد تخفیف با موفقیت اعمال شد",
|
||||
"discount_error": "کد تخفیف معتبر نیست",
|
||||
"pending": "در انتظار تایید",
|
||||
"discount_removed": "کد تخفیف با موفقیت حذف شد",
|
||||
"remove_discount": "حذف تخفیف",
|
||||
"charge_wallet": "شارژ کیف پول",
|
||||
"sure_charge_wallet": "آیا از شارژ کیف پول اطمینان دارید؟"
|
||||
},
|
||||
"loading": "در حال بارکذاری...",
|
||||
"edit": "ویرایش",
|
||||
"transaction": {
|
||||
"transaction": "تراکنش ها",
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import StatusWithText from '../../components/StatusWithText'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
import { ArrowLeft2 } from 'iconsax-react'
|
||||
import { useGetAnnoncementDetail } from './hooks/useAnnoncementData'
|
||||
import PageLoading from '../../components/PageLoading'
|
||||
import { Helmet } from 'react-helmet-async'
|
||||
|
||||
const AnnouncementDetail: FC = () => {
|
||||
|
||||
const { id } = useParams()
|
||||
const { t } = useTranslation('global')
|
||||
const navigate = useNavigate()
|
||||
const getAnnoncementDetail = useGetAnnoncementDetail(id ? id : '')
|
||||
|
||||
return (
|
||||
<div className='mt-4'>
|
||||
<Helmet>
|
||||
<title>
|
||||
داناک | اطلاعیه
|
||||
</title>
|
||||
</Helmet>
|
||||
<div className='flex justify-between'>
|
||||
<div>
|
||||
{t('announcement.text_announcement')}
|
||||
</div>
|
||||
<div onClick={() => navigate(-1)} className='text-xs flex gap-0.5 items-center font-extralight'>
|
||||
{t('back')}
|
||||
<ArrowLeft2 size={12} color='black' />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{
|
||||
getAnnoncementDetail.isPending ?
|
||||
<PageLoading />
|
||||
:
|
||||
<div className='flex-1 mt-4 bg-white py-6 px-4 rounded-2xl'>
|
||||
<div className='flex justify-between items-start'>
|
||||
<div className='text-sm max-w-[70%] leading-67'>
|
||||
{getAnnoncementDetail.data?.data?.announcement?.title}
|
||||
</div>
|
||||
{
|
||||
getAnnoncementDetail.data?.data?.announcement.isImportant &&
|
||||
<StatusWithText
|
||||
variant='error'
|
||||
text={t('announcement.important')}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div dangerouslySetInnerHTML={{ __html: getAnnoncementDetail.data?.data?.announcement?.content }} className='mt-5 leading-7 text-xs font-light'>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AnnouncementDetail
|
||||
@@ -0,0 +1,151 @@
|
||||
import { FC, Fragment, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Input from '../../components/Input'
|
||||
import StatusWithText from '../../components/StatusWithText'
|
||||
import { clx } from '../../helpers/utils'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { Pages } from '../../config/Pages'
|
||||
import { useGetAnnoncement } from './hooks/useAnnoncementData'
|
||||
import { AnnoncementItemType } from './types/AnnoncementTypes'
|
||||
import moment from 'moment-jalaali'
|
||||
import { Helmet } from 'react-helmet-async'
|
||||
import AnouncementSkeleton from './components/AnouncementSkeleton'
|
||||
|
||||
const AnnouncementtList: FC = () => {
|
||||
|
||||
const navigate = useNavigate()
|
||||
const { t } = useTranslation('global')
|
||||
const [search, setSearch] = useState<string>('')
|
||||
const [itemSelected, setItemSelected] = useState<AnnoncementItemType>()
|
||||
const getAnnoncements = useGetAnnoncement(search)
|
||||
|
||||
const handleChange = (item: AnnoncementItemType) => {
|
||||
if (window.innerWidth < 1280) {
|
||||
navigate(Pages.announcement.detail + item.announcement.id)
|
||||
} else {
|
||||
setItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='mt-4 text-sm'>
|
||||
<Helmet>
|
||||
<title>
|
||||
داناک | اطلاعیه ها
|
||||
</title>
|
||||
</Helmet>
|
||||
<div className='text-base'>
|
||||
{t('announcement.announcements')}
|
||||
</div>
|
||||
|
||||
<div className='mt-10 flex gap-8'>
|
||||
<div className='bg-white xl:w-[400px] w-full rounded-2xl p-6'>
|
||||
<div className='flex gap-3 items-center'>
|
||||
<div className='flex-1'>
|
||||
<Input
|
||||
variant='search'
|
||||
placeholder={t('announcement.search')}
|
||||
className='-mt-1'
|
||||
onChangeSearchFinal={(value) => setSearch(value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* <div>
|
||||
<Select
|
||||
items={[
|
||||
{ value: '1', label: 'همه' },
|
||||
{ value: '2', label: 'فعال' },
|
||||
{ value: '3', label: 'غیر فعال' },
|
||||
]}
|
||||
className='bg-[#EEF0F7] w-[68px]'
|
||||
/>
|
||||
</div> */}
|
||||
</div>
|
||||
|
||||
<Fragment>
|
||||
<div className='mt-8'>
|
||||
{
|
||||
getAnnoncements.isPending ?
|
||||
<Fragment>
|
||||
<AnouncementSkeleton />
|
||||
<AnouncementSkeleton />
|
||||
<AnouncementSkeleton />
|
||||
</Fragment>
|
||||
:
|
||||
getAnnoncements.data?.data?.announcements?.map((item: AnnoncementItemType) => {
|
||||
return (
|
||||
<div onClick={() => handleChange(item)} className={clx(
|
||||
'p-4 border-b cursor-pointer',
|
||||
item.id === itemSelected?.id && 'bg-[#F8F9FB]'
|
||||
)}>
|
||||
<div className='flex justify-between'>
|
||||
<div className='leading-6 text-xs xl:text-[12.5px] max-w-[70%]'>
|
||||
{item.announcement.title}
|
||||
</div>
|
||||
|
||||
<div className='flex gap-1'>
|
||||
{
|
||||
item.announcement.isImportant &&
|
||||
<StatusWithText
|
||||
variant='error'
|
||||
text={t('announcement.important')}
|
||||
/>
|
||||
}
|
||||
{
|
||||
!item.isRead &&
|
||||
<StatusWithText
|
||||
variant='success'
|
||||
text={t('announcement.new')}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='m-2 flex justify-between items-center text-[11px]'>
|
||||
<div dangerouslySetInnerHTML={{ __html: item.announcement.content }} className='max-w-[70%] truncate text-description'>
|
||||
</div>
|
||||
<div className='text-description'>
|
||||
{moment(item.announcement.publishAt).format('jYYYY/jMM/jDD')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
</div>
|
||||
</Fragment>
|
||||
</div>
|
||||
|
||||
{
|
||||
itemSelected &&
|
||||
<div className='flex-1 hidden xl:block bg-white py-7 px-5 rounded-2xl'>
|
||||
<div className='flex justify-between items-center'>
|
||||
<div className='text-lg'>
|
||||
متن اطلاعیه
|
||||
</div>
|
||||
{
|
||||
itemSelected.announcement.isImportant &&
|
||||
<StatusWithText
|
||||
variant='error'
|
||||
text={t('announcement.important')}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div className='mt-8'>
|
||||
{itemSelected.announcement.title}
|
||||
</div>
|
||||
|
||||
<div dangerouslySetInnerHTML={{ __html: itemSelected.announcement.content }} className='mt-5 leading-7 text-xs font-light'>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AnnouncementtList
|
||||
@@ -0,0 +1,41 @@
|
||||
import { FC } from 'react'
|
||||
import Skeleton from 'react-loading-skeleton'
|
||||
|
||||
const AnouncementSkeleton: FC = () => {
|
||||
return (
|
||||
<div className={'p-4 border-b'}>
|
||||
<div className='flex items-center justify-between'>
|
||||
<div className='leading-6 text-xs xl:text-[12.5px] max-w-[70%]'>
|
||||
<Skeleton
|
||||
height={7}
|
||||
width={100}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-1'>
|
||||
<Skeleton
|
||||
className='!w-10 !h-6 !rounded-full'
|
||||
/>
|
||||
<Skeleton
|
||||
className='!w-10 !h-6 !rounded-full'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='m-2 flex justify-between items-center text-[11px]'>
|
||||
<Skeleton
|
||||
height={7}
|
||||
width={150}
|
||||
/>
|
||||
<div className='text-description'>
|
||||
<Skeleton
|
||||
height={7}
|
||||
width={40}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AnouncementSkeleton
|
||||
@@ -0,0 +1,17 @@
|
||||
import * as api from "../service/AnnoncementService";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
export const useGetAnnoncement = (search: string) => {
|
||||
return useQuery({
|
||||
queryKey: ["announcements", search],
|
||||
queryFn: () => api.getAnnoncements(search),
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetAnnoncementDetail = (id: string) => {
|
||||
return useQuery({
|
||||
queryKey: ["announcement", id],
|
||||
queryFn: () => api.getAnnoncementDetail(id),
|
||||
enabled: !!id,
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
import axios from "../../../config/axios";
|
||||
|
||||
export const getAnnoncements = async (search: string) => {
|
||||
const { data } = await axios.get(`/announcements/user?q=${search}`);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const getAnnoncementDetail = async (id: string) => {
|
||||
const { data } = await axios.get(`/announcements/${id}`);
|
||||
return data;
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
export type AnnoncementItemType = {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
announcement: {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
title: string;
|
||||
content: string;
|
||||
publishAt: string;
|
||||
targetService: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
isImportant: boolean;
|
||||
};
|
||||
isRead: boolean;
|
||||
};
|
||||
+38
-10
@@ -1,31 +1,59 @@
|
||||
import { Building, InfoCircle } from 'iconsax-react'
|
||||
import { Building, CloseCircle, InfoCircle, TickCircle } from 'iconsax-react'
|
||||
import { FC, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { buildPath, clx } from '../../helpers/utils'
|
||||
import StatusWithText from '../../components/StatusWithText'
|
||||
import TitleSeeAll from '../../components/TitleSeeAll'
|
||||
import { useGetCompanies, useGetIndustry } from './hooks/useHomeData'
|
||||
import { useGetCompanies, useGetIndustry, useGetCompanyRequest } from './hooks/useHomeData'
|
||||
import { CompanyItemType, IndustryItemType } from './types/HomeTypes'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Pages } from '../../config/Pages'
|
||||
import { CompanyRequestStatus } from './enum/Enum'
|
||||
const Home: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const [activeIndustry, setActiveIndustry] = useState<string>('')
|
||||
const { data: industry } = useGetIndustry()
|
||||
const { data: companies } = useGetCompanies(activeIndustry)
|
||||
const { data: companyRequest } = useGetCompanyRequest()
|
||||
|
||||
return (
|
||||
<div className='mt-4'>
|
||||
<div className='w-full h-10 flex gap-2 items-center px-4 rounded-xl bg-[#FCE7CE] text-[#FF7B00]'>
|
||||
<InfoCircle
|
||||
size={24}
|
||||
color='#FF7B00'
|
||||
/>
|
||||
<div className='text-xs'>
|
||||
درخواست شما برای ثبت شرکت، هنوز تایید نشده است
|
||||
{
|
||||
companyRequest?.data?.requests &&
|
||||
<div className={clx(
|
||||
'w-full h-10 flex gap-2 items-center px-4 rounded-xl ',
|
||||
companyRequest?.data?.requests?.requestStatus === CompanyRequestStatus.PENDING ? 'bg-[#FCE7CE] text-[#FF7B00]'
|
||||
: companyRequest?.data?.requests?.requestStatus === CompanyRequestStatus.APPROVED ? 'bg-[#E6FFF4] text-[#039855]'
|
||||
: 'bg-[#FFE6E6] text-[#FF0000]'
|
||||
)}>
|
||||
{
|
||||
companyRequest?.data?.requests?.requestStatus === CompanyRequestStatus.PENDING ?
|
||||
<InfoCircle
|
||||
size={22}
|
||||
color='#FF7B00'
|
||||
/>
|
||||
: companyRequest?.data?.requests?.requestStatus === CompanyRequestStatus.APPROVED ?
|
||||
<TickCircle
|
||||
size={22}
|
||||
color='#039855'
|
||||
/>
|
||||
: companyRequest?.data?.requests?.requestStatus === CompanyRequestStatus.REJECTED ?
|
||||
<CloseCircle
|
||||
size={22}
|
||||
color='#FF0000'
|
||||
/>
|
||||
: null
|
||||
}
|
||||
<div className="text-xs">
|
||||
{companyRequest?.data?.requests?.requestStatus === CompanyRequestStatus.PENDING
|
||||
? t('home.request_pending')
|
||||
: companyRequest?.data?.requests?.requestStatus === CompanyRequestStatus.APPROVED
|
||||
? t('home.request_approved')
|
||||
: t('home.request_rejected')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div className='mt-6'>
|
||||
<TitleSeeAll
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
export enum CompanyStatus {
|
||||
APPROVED = "APPROVED",
|
||||
PENDING = "PENDING",
|
||||
REJECTED = "REJECTED",
|
||||
}
|
||||
|
||||
export enum CompanyRequestStatus {
|
||||
PENDING = "PENDING",
|
||||
APPROVED = "APPROVED",
|
||||
REJECTED = "REJECTED",
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
getIdWithSlug,
|
||||
getIndustry,
|
||||
getCompanyDetail,
|
||||
getCompanyRequest,
|
||||
} from "../service/HomeService";
|
||||
|
||||
export const useGetSlugId = () => {
|
||||
@@ -33,3 +34,10 @@ export const useGetCompanyDetail = (id: string) => {
|
||||
enabled: !!id,
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetCompanyRequest = () => {
|
||||
return useQuery({
|
||||
queryKey: ["company-request"],
|
||||
queryFn: () => getCompanyRequest(),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -25,3 +25,8 @@ export const getCompanyDetail = async (id: string) => {
|
||||
const { data } = await axios.get(`/companies/public/${id}`);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const getCompanyRequest = async () => {
|
||||
const { data } = await axios.get(`/companies/requests/user`);
|
||||
return data;
|
||||
};
|
||||
|
||||
+300
-225
@@ -1,255 +1,330 @@
|
||||
import { FC } from 'react'
|
||||
import { FC, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Edit } from 'iconsax-react'
|
||||
import StatusCircle from '../../components/StatusCircle'
|
||||
import SamanImage from '../../assets/images/saman.png'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { useApplyDiscount, useGetInvoiceDetail, useRemoveDiscount } from './hooks/useReceiptData'
|
||||
import { ApplyDiscountType, ReceiptDetailItemType } from './types/ReceiptTypes'
|
||||
import { NumberFormat } from '../../config/func'
|
||||
import moment from 'moment-jalaali'
|
||||
import LegalInfo from './components/LegalInfo'
|
||||
import RealInfo from './components/RealInfo'
|
||||
import RegisterInfo from './components/RegisterInfo'
|
||||
import { clx } from '../../helpers/utils'
|
||||
import Button from '../../components/Button'
|
||||
import ApproveInvoice from './components/ApproveInvoice'
|
||||
import PayInvoice from './components/PayInvoice'
|
||||
import Input from '../../components/Input'
|
||||
import { useGetProfile } from '../profile/hooks/useProfileData'
|
||||
import { toast } from 'react-toastify'
|
||||
import { ErrorType } from '../../helpers/types'
|
||||
// import PDFGenerator from '../../components/PDFGenerator'
|
||||
// import { useGetGetWays } from '../wallet/hooks/useWalletData'
|
||||
// import { GatewayItemType } from '../wallet/types/WalletTypes'
|
||||
|
||||
const ReceiptsDetail: FC = () => {
|
||||
|
||||
const { id } = useParams()
|
||||
const { t } = useTranslation('global')
|
||||
const [code, setCode] = useState<string>('')
|
||||
const [gateWayId, setGateWayId] = useState<string>('')
|
||||
const getProfile = useGetProfile()
|
||||
const getInvoce = useGetInvoiceDetail(id ? id : '')
|
||||
const applyDiscount = useApplyDiscount()
|
||||
const removeDiscount = useRemoveDiscount()
|
||||
// const getWays = useGetGetWays()
|
||||
|
||||
const handleApplyDiscount = () => {
|
||||
const params: ApplyDiscountType = {
|
||||
id: id ? id : '',
|
||||
code: code
|
||||
}
|
||||
applyDiscount.mutate(params, {
|
||||
onSuccess: () => {
|
||||
toast.success(t('receip.discount_applied'))
|
||||
getInvoce.refetch()
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast.error(error?.response?.data?.error?.message[0])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleRemoveDiscount = () => {
|
||||
removeDiscount.mutate({ id: id ? id : '' }, {
|
||||
onSuccess: () => {
|
||||
toast.success(t('receip.discount_removed'))
|
||||
getInvoce.refetch()
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast.error(error?.response?.data?.error?.message[0])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// useEffect(() => {
|
||||
|
||||
// if (getWays.data) {
|
||||
// setGateWayId(getWays.data?.data?.paymentGateways?.[0]?.id)
|
||||
// }
|
||||
|
||||
// }, [getWays.data])
|
||||
|
||||
|
||||
return (
|
||||
<div className='mt-4 pb-20'>
|
||||
<div>
|
||||
{t('receip.detail_account')}
|
||||
۱۲۳۱۲
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<div className='flex xl:flex-row flex-col gap-4 items-center'>
|
||||
<div className='flex gap-6 items-center'>
|
||||
<div className='whitespace-nowrap'>{t('receip.detail_account')}</div>
|
||||
|
||||
<div className='flex xl:flex-row flex-col mt-8 gap-6'>
|
||||
<div className='flex-1'>
|
||||
<div className='w-full bg-white rounded-3xl p-8'>
|
||||
<div className='flex justify-between items-center'>
|
||||
<div>
|
||||
{t('receip.personal_information')}
|
||||
</div>
|
||||
<div className='flex gap-1 items-center text-xs text-[#0047FF]'>
|
||||
<Edit size={16} color='#0047FF' />
|
||||
<div>
|
||||
{t('edit')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ApproveInvoice status={getInvoce.data?.data?.invoice?.status} id={id ? id : ''} refetch={getInvoce.refetch} />
|
||||
</div>
|
||||
|
||||
<div className='mt-8 flex xl:flex-row flex-col xl:gap-16 gap-4 xl:items-center text-xs'>
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.type_person')}
|
||||
</div>
|
||||
<div>
|
||||
{t('receip.company')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.representativeـname')}
|
||||
</div>
|
||||
<div>
|
||||
مهرداد مظفری
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.company_name')}
|
||||
</div>
|
||||
<div>
|
||||
داناک
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.registration_number')}
|
||||
</div>
|
||||
<div>
|
||||
۱۲۳۴
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='mt-8 border-t pt-7 flex xl:flex-row flex-col xl:gap-16 gap-4 xl:items-center text-xs'>
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.tel_company')}
|
||||
</div>
|
||||
<div>
|
||||
۰۰۱۲۳۴۵۶۷۸
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.national_code')}
|
||||
</div>
|
||||
<div>
|
||||
۰۰۱۲۳۴۵۶۷۸
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.postal_code')}
|
||||
</div>
|
||||
<div>
|
||||
۳۸۲۱۳۵۸۸۹۲
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div className='mt-6 flex xl:flex-row flex-col xl:gap-16 gap-4 xl:items-center text-xs'>
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.state')}
|
||||
</div>
|
||||
<div>
|
||||
مرکزی
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.state')}
|
||||
</div>
|
||||
<div>
|
||||
مرکزی
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.city')}
|
||||
</div>
|
||||
<div>
|
||||
اراک
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.address')}
|
||||
</div>
|
||||
<div>
|
||||
اراک -خیابان شریعتی-خیابان جنت
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div className='w-full bg-white rounded-3xl mt-8 p-8'>
|
||||
<div className='flex justify-between items-center'>
|
||||
<div>
|
||||
{t('receip.detail_receip')}
|
||||
</div>
|
||||
<div className='flex gap-2 text-xs items-center'>
|
||||
<StatusCircle
|
||||
color='#FF7B00'
|
||||
{
|
||||
!getInvoce.data?.data?.invoice?.discount && ['PENDING', 'APPROVED', 'WAIT_PAYMENT'].includes(getInvoce.data?.data?.invoice?.status) ?
|
||||
<div className='flex -mt-1 gap-2 items-center w-full'>
|
||||
<div className='w-full xl:w-fit'>
|
||||
<Input
|
||||
className='bg-transparent border border-border xl:max-w-40'
|
||||
placeholder={t('receip.discount_code')}
|
||||
value={code}
|
||||
onChange={(e) => setCode(e.target.value)}
|
||||
/>
|
||||
<div>
|
||||
{t('receip.not_paid')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='mt-7 text-sm'>
|
||||
<div className='flex justify-between items-center text-description h-14'>
|
||||
<div>
|
||||
{t('receip.description')}
|
||||
</div>
|
||||
<div>
|
||||
{t('receip.price')}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex justify-between items-center text-xs border-t border-[#EAEDF5] h-14'>
|
||||
<div>
|
||||
اکانت یکساله با دسترسی
|
||||
</div>
|
||||
<div>
|
||||
۱۰,۰۰۰,۰۰۰ ریال
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex justify-between items-center text-xs border-t border-[#EAEDF5] h-14'>
|
||||
<div>
|
||||
۱۰٪ ارزش افزوده
|
||||
</div>
|
||||
<div>
|
||||
۱۰,۰۰۰,۰۰۰ ریال
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex justify-between items-center text-xs border-t border-[#EAEDF5] h-14'>
|
||||
<div>
|
||||
جمع کل
|
||||
</div>
|
||||
<div>
|
||||
۱۰,۰۰۰,۰۰۰ ریال
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='bg-white xl:w-sidebar text-sm h-fit rounded-3xl p-8'>
|
||||
<div className='flex justify-between items-center'>
|
||||
<div>
|
||||
{t('receip.receip_information')}
|
||||
</div>
|
||||
<div className='flex gap-2 text-xs items-center'>
|
||||
<StatusCircle
|
||||
color='#FF7B00'
|
||||
<Button
|
||||
label={t('receip.apply')}
|
||||
className='bg-transparent text-black border border-black mt-1 px-5 w-fit'
|
||||
onClick={handleApplyDiscount}
|
||||
isLoading={applyDiscount.isPending}
|
||||
/>
|
||||
<div>پرداخت نشده</div>
|
||||
</div>
|
||||
</div>
|
||||
: getInvoce.data?.data?.invoice?.discount &&
|
||||
<div className='flex -mt-1 gap-2 items-center w-full'>
|
||||
<div className='w-full xl:w-fit'>
|
||||
<Button
|
||||
label={t('receip.remove_discount')}
|
||||
className='bg-transparent text-black border border-black mt-1 px-5 w-fit'
|
||||
onClick={handleRemoveDiscount}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div className='mt-6 text-xs font-extralight'>
|
||||
<div className='flex justify-between h-12 border-b items-center'>
|
||||
<div className='text-description'>
|
||||
{t('receip.date_factor')}
|
||||
{
|
||||
<div className='flex xl:flex-row flex-col mt-8 gap-6'>
|
||||
<div className='flex-1'>
|
||||
<div id="receipt-details" className='w-full bg-white rounded-3xl p-8'>
|
||||
<div className='flex justify-between items-center mb-6'>
|
||||
<div className='text-lg font-medium'>
|
||||
{t('receip.personal_information')}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
۱۴۰۲/۱۰/۰۴
|
||||
|
||||
{/* {
|
||||
getProfile.data?.data?.user?.financialType === 'LEGAL' ?
|
||||
<LegalInfo />
|
||||
:
|
||||
getProfile.data?.data?.user?.financialType === 'REAL' ?
|
||||
<RealInfo />
|
||||
: <RegisterInfo data={getProfile.data?.data} />
|
||||
} */}
|
||||
|
||||
<div className='mt-8'>
|
||||
<div className='flex justify-between items-center mb-6'>
|
||||
<div className='text-lg font-medium'>
|
||||
{t('receip.detail_receip')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='mt-7 text-sm'>
|
||||
<div className='flex justify-between items-center text-description h-14 font-medium'>
|
||||
<div>
|
||||
{t('receip.description')}
|
||||
</div>
|
||||
<div>
|
||||
{t('receip.price')}
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
getInvoce.data?.data?.invoice?.items?.map((item: ReceiptDetailItemType) => {
|
||||
return (
|
||||
<div key={item.id} className='flex justify-between items-center text-xs border-t border-[#EAEDF5] h-14'>
|
||||
<div className='flex gap-0.5'>
|
||||
<span>{item.name}</span>
|
||||
<span>{item.count}</span>
|
||||
<span>عدد</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>{NumberFormat(item.totalPrice)}</span> تومان
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
<div className='flex justify-between items-center text-xs border-t border-[#EAEDF5] h-14'>
|
||||
<div>
|
||||
۱۰٪ ارزش افزوده
|
||||
</div>
|
||||
<div>
|
||||
<span>{NumberFormat(getInvoce.data?.data?.invoice?.tax)}</span> تومان
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex justify-between gap-1 items-center text-xs border-t border-[#EAEDF5] h-14'>
|
||||
<div>
|
||||
جمع کل
|
||||
</div>
|
||||
<div className='flex gap-1'>
|
||||
{
|
||||
getInvoce.data?.data?.invoice?.originalPrice !== getInvoce.data?.data?.invoice?.totalPrice && (
|
||||
<><span className='line-through'>{NumberFormat(getInvoce.data?.data?.invoice?.originalPrice)}</span></>
|
||||
)
|
||||
}
|
||||
<span>{NumberFormat(getInvoce.data?.data?.invoice?.totalPrice)}</span> تومان
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex justify-between h-12 border-b items-center'>
|
||||
<div className='text-description'>
|
||||
{t('receip.due_date')}
|
||||
</div>
|
||||
<div>
|
||||
۱۴۰۲/۱۰/۰۴
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex justify-between h-12 border-b items-center'>
|
||||
<div className='text-description'>
|
||||
{t('receip.factor_number')}
|
||||
</div>
|
||||
<div>
|
||||
۱۲۳۱
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex justify-between h-12 border-b items-center'>
|
||||
<div className='text-description'>
|
||||
{t('receip.status_factor')}
|
||||
</div>
|
||||
<div>
|
||||
تایید شده
|
||||
|
||||
<div className='mt-8'>
|
||||
<div className='flex justify-between items-center mb-6'>
|
||||
<div className='text-lg font-medium'>
|
||||
{t('receip.receip_information')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='mt-6 text-xs font-extralight'>
|
||||
<div className='flex justify-between h-12 border-b items-center'>
|
||||
<div className='text-description'>
|
||||
{t('receip.date_factor')}
|
||||
</div>
|
||||
<div>
|
||||
{moment(getInvoce.data?.data?.invoice?.createdAt).format('jYYYY-jMM-jDD')}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex justify-between h-12 border-b items-center'>
|
||||
<div className='text-description'>
|
||||
{t('receip.due_date')}
|
||||
</div>
|
||||
<div>
|
||||
{moment(getInvoce.data?.data?.invoice?.dueDate).format('jYYYY-jMM-jDD')}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex justify-between h-12 border-b items-center'>
|
||||
<div className='text-description'>
|
||||
{t('receip.factor_number')}
|
||||
</div>
|
||||
<div>
|
||||
{moment(getInvoce.data?.data?.invoice.createdAt).format('jYYYY') + getInvoce.data?.data?.invoice.numericId}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex justify-between h-12 border-b items-center'>
|
||||
<div className='text-description'>
|
||||
{t('receip.status_factor')}
|
||||
</div>
|
||||
<div>
|
||||
{t(`receip.${getInvoce.data?.data?.invoice?.status}`)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='mt-5 flex justify-center gap-2'>
|
||||
<div className='border size-[72px] flex-col text-[10px] justify-center items-center rounded-xl border-border flex'>
|
||||
<img src={SamanImage} alt='saman' className='w-8 h-8' />
|
||||
<div className='mt-2'>
|
||||
بانک سامان
|
||||
<div className='bg-white xl:w-sidebar text-sm h-fit rounded-3xl p-8'>
|
||||
{
|
||||
getInvoce.data?.data?.invoice?.status === 'PAID' &&
|
||||
<div className='p-6 bg-green-50 rounded-2xl text-green-400 text-xs mb-6'>
|
||||
<p>{t('receip.thank_pay_1')}</p>
|
||||
<p className='mt-1'>{t('receip.thank_pay_2')}</p>
|
||||
</div>
|
||||
}
|
||||
<div className='flex justify-between items-center'>
|
||||
<div>
|
||||
{t('receip.receip_information')}
|
||||
</div>
|
||||
<div className={clx(
|
||||
'border border-orange-400 text-orange-400 rounded-lg text-[9px] h-6 flex items-center px-1',
|
||||
getInvoce.data?.data?.invoice?.status === 'PAID' ? 'border-green-400 text-green-400' : getInvoce.data?.data?.invoice?.status !== 'PENDING' ? 'border-red-400 text-red-400' : 'border-orange-400 text-orange-400'
|
||||
)}>
|
||||
<div>{t(`receip.${getInvoce.data?.data?.invoice?.status}`)}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='border size-[72px] flex-col text-[10px] justify-center items-center rounded-xl border-border flex'>
|
||||
<img src={SamanImage} alt='saman' className='w-8 h-8' />
|
||||
<div className='mt-2'>
|
||||
بانک سامان
|
||||
|
||||
<div className='mt-6 text-xs font-extralight'>
|
||||
<div className='flex justify-between h-12 border-b items-center'>
|
||||
<div className='text-description'>
|
||||
{t('receip.date_factor')}
|
||||
</div>
|
||||
<div>
|
||||
{moment(getInvoce.data?.data?.invoice?.createdAt).format('jYYYY-jMM-jDD')}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex justify-between h-12 border-b items-center'>
|
||||
<div className='text-description'>
|
||||
{t('receip.due_date')}
|
||||
</div>
|
||||
<div>
|
||||
{moment(getInvoce.data?.data?.invoice?.dueDate).format('jYYYY-jMM-jDD')}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex justify-between h-12 border-b items-center'>
|
||||
<div className='text-description'>
|
||||
{t('receip.factor_number')}
|
||||
</div>
|
||||
<div>
|
||||
{moment(getInvoce.data?.data?.invoice.createdAt).format('jYYYY') + getInvoce.data?.data?.invoice.numericId}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex justify-between h-12 border-b items-center'>
|
||||
<div className='text-description'>
|
||||
{t('receip.status_factor')}
|
||||
</div>
|
||||
<div>
|
||||
{t(`receip.${getInvoce.data?.data?.invoice?.status}`)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{
|
||||
getInvoce.data?.data?.invoice?.status === 'WAIT_PAYMENT' &&
|
||||
<div className='flex gap-4 items-center mt-8'>
|
||||
{/* <PDFGenerator
|
||||
elementId="receipt-details"
|
||||
fileName={`receipt-${getInvoce.data?.data?.invoice.numericId}`}
|
||||
/> */}
|
||||
<PayInvoice
|
||||
id={id ? id : ''}
|
||||
refetch={getInvoce.refetch}
|
||||
gateWayId={gateWayId}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
{/* {
|
||||
getInvoce.data?.data?.remainingToCharge > 0 &&
|
||||
<div className='flex mt-6 justify-center gap-2'>
|
||||
{
|
||||
getWays.data?.data?.paymentGateways?.map((item: GatewayItemType) => {
|
||||
return (
|
||||
<div key={item.id} className={clx(
|
||||
'size-[72px] rounded-xl border border-border flex flex-col justify-center items-center',
|
||||
gateWayId === item.id && 'border-black'
|
||||
)}>
|
||||
<img src={item.logoUrl} className='w-8' />
|
||||
<div className='mt-1 text-[10px]'>
|
||||
{item.name}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
} */}
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
)
|
||||
|
||||
+110
-92
@@ -1,109 +1,127 @@
|
||||
import { FC, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Tabs from '../../components/Tabs'
|
||||
import { Calendar, CloseCircle, Eye, FolderOpen, MoneyChange, WalletCheck, WalletRemove, WalletSearch } from 'iconsax-react'
|
||||
import { Eye, FolderOpen, WalletCheck, WalletMinus, WalletRemove, WalletSearch } from 'iconsax-react'
|
||||
import Td from '../../components/Td'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Pages } from '../../config/Pages'
|
||||
import { useGetInvoices } from './hooks/useReceiptData'
|
||||
import { NumberFormat } from '../../config/func'
|
||||
import { ReceiptItemType } from './types/ReceiptTypes'
|
||||
import moment from 'moment-jalaali'
|
||||
import { InvoiceStatus } from './types/ReceiptTypes'
|
||||
|
||||
|
||||
|
||||
const ReceiptsList: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const [activeTab, setActiveTab] = useState<string>('unpaid')
|
||||
const [activeTab, setActiveTab] = useState<InvoiceStatus>('PENDING')
|
||||
const getInvoices = useGetInvoices(activeTab)
|
||||
|
||||
return (
|
||||
<div className='flex justify-center'>
|
||||
<div className='mt-4 w-full max-w-[1200px]'>
|
||||
<div>
|
||||
{t('receip.Language_learners_list')}
|
||||
</div>
|
||||
<div className='mt-4'>
|
||||
<div>
|
||||
{t('receip.receip_list')}
|
||||
</div>
|
||||
|
||||
<div className='mt-14'>
|
||||
<Tabs
|
||||
active={activeTab}
|
||||
items={[
|
||||
{
|
||||
icon: <WalletRemove color={activeTab === 'unpaid' ? 'black' : '#8C90A3'} size={22} />,
|
||||
label: t('receip.dont_pay'),
|
||||
value: 'unpaid'
|
||||
},
|
||||
{
|
||||
icon: <WalletSearch color={activeTab === 'not_confimed' ? 'black' : '#8C90A3'} size={22} />,
|
||||
label: t('receip.dont_confrim'),
|
||||
value: 'not_confimed'
|
||||
},
|
||||
{
|
||||
icon: <WalletCheck color={activeTab === 'paid' ? 'black' : '#8C90A3'} size={22} />,
|
||||
label: t('receip.paid'),
|
||||
value: 'paid'
|
||||
},
|
||||
{
|
||||
icon: <CloseCircle color={activeTab === 'canceled' ? 'black' : '#8C90A3'} size={22} />,
|
||||
label: t('receip.canceled'),
|
||||
value: 'canceled'
|
||||
},
|
||||
{
|
||||
icon: <MoneyChange color={activeTab === 'returned' ? 'black' : '#8C90A3'} size={22} />,
|
||||
label: t('receip.returned'),
|
||||
value: 'returned'
|
||||
},
|
||||
{
|
||||
icon: <Calendar color={activeTab === 'time' ? 'black' : '#8C90A3'} size={22} />,
|
||||
label: t('receip.its_time'),
|
||||
value: 'time'
|
||||
},
|
||||
{
|
||||
icon: <FolderOpen color={activeTab === 'archive' ? 'black' : '#8C90A3'} size={22} />,
|
||||
label: t('receip.archive'),
|
||||
value: 'archive'
|
||||
},
|
||||
]}
|
||||
onChange={setActiveTab}
|
||||
/>
|
||||
</div>
|
||||
<div className='mt-14'>
|
||||
<Tabs
|
||||
active={activeTab}
|
||||
items={[
|
||||
{
|
||||
icon: <WalletSearch color={activeTab === 'PENDING' ? 'black' : '#8C90A3'} size={22} />,
|
||||
label: t('receip.pending'),
|
||||
value: 'PENDING'
|
||||
},
|
||||
{
|
||||
icon: <WalletRemove color={activeTab === 'WAIT_PAYMENT' ? 'black' : '#8C90A3'} size={22} />,
|
||||
label: t('receip.dont_pay'),
|
||||
value: 'WAIT_PAYMENT'
|
||||
},
|
||||
{
|
||||
icon: <WalletCheck color={activeTab === 'PAID' ? 'black' : '#8C90A3'} size={22} />,
|
||||
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'
|
||||
},
|
||||
]}
|
||||
onChange={(value) => setActiveTab(value as InvoiceStatus)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='relative overflow-x-auto rounded-3xl mt-9 w-full'>
|
||||
<table className='w-full text-sm '>
|
||||
<thead className='thead'>
|
||||
<tr>
|
||||
<Td text={t('ticket.number')} />
|
||||
<Td text={t('ticket.title')} />
|
||||
<Td text={t('ticket.team')} />
|
||||
<Td text={t('ticket.date')} />
|
||||
<Td text={t('ticket.status')} />
|
||||
<Td text={t('ticket.priority')} />
|
||||
<Td text={t('ticket.detail')} />
|
||||
<Td text={''} />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr className='tr'>
|
||||
<Td text={t('ticket.number')} />
|
||||
<Td text={t('ticket.title')} />
|
||||
<Td text={t('ticket.team')} />
|
||||
<Td text={t('ticket.date')} />
|
||||
<Td text={t('ticket.status')} />
|
||||
<Td text={t('ticket.priority')} />
|
||||
<Td text={t('ticket.detail')} />
|
||||
<Td text={''}>
|
||||
<Link to={Pages.receipts.detail + '1'}>
|
||||
<Eye size={20} color='black' />
|
||||
</Link>
|
||||
</Td>
|
||||
</tr>
|
||||
<tr className='tr'>
|
||||
<Td text={t('ticket.number')} />
|
||||
<Td text={t('ticket.title')} />
|
||||
<Td text={t('ticket.team')} />
|
||||
<Td text={t('ticket.date')} />
|
||||
<Td text={t('ticket.status')} />
|
||||
<Td text={t('ticket.priority')} />
|
||||
<Td text={t('ticket.detail')} />
|
||||
<Td text={''} />
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div className='relative overflow-x-auto rounded-3xl mt-9 w-full'>
|
||||
<table className='w-full text-sm '>
|
||||
<thead className='thead'>
|
||||
<tr>
|
||||
<Td text={t('ticket.number')} />
|
||||
{/* <Td text={t('receip.customer')} /> */}
|
||||
<Td text={t('receip.date_receipt')} />
|
||||
<Td text={t('receip.last_date_receipt')} />
|
||||
<Td text={t('receip.total')} />
|
||||
<Td text={t('receip.service')} />
|
||||
<Td text={t('receip.late_fee')} />
|
||||
<Td text={t('receip.status')} />
|
||||
<Td text={t('receip.status_paid')} />
|
||||
<Td text={''} />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
|
||||
getInvoices.data?.data?.invoices?.map((item: ReceiptItemType) => {
|
||||
return (
|
||||
<tr className='tr'>
|
||||
<Td text={moment(item.createdAt).format('jYYYY') + item.numericId} />
|
||||
{/* <Td text={item.user.firstName + ' ' + item.user.lastName} /> */}
|
||||
<Td text={''}>
|
||||
<div className='dltr text-right'>
|
||||
{moment(item.createdAt).format('jYYYY-jMM-jDD HH:mm')}
|
||||
</div>
|
||||
</Td>
|
||||
|
||||
<Td text={''}>
|
||||
<div className='dltr text-right'>
|
||||
{moment(item.dueDate).format('jYYYY-jMM-jDD HH:mm')}
|
||||
</div>
|
||||
</Td>
|
||||
<Td text={NumberFormat(item.totalPrice)} />
|
||||
<Td text={''}>
|
||||
<div className='flex flex-wrap gap-1.5'>
|
||||
{
|
||||
item.items.map((item => {
|
||||
return (
|
||||
<div className='bg-primary p-2 rounded-full text-white flex gap-1'><span>{item.name}</span> <span>({item.count})</span></div>
|
||||
)
|
||||
}))
|
||||
}
|
||||
</div>
|
||||
</Td>
|
||||
<Td text={NumberFormat(item.lateFee)} />
|
||||
<Td text={t(`receip.${item.status}`)} />
|
||||
<Td text={item.status === 'PAID' ? moment(item.paidAt).format('jYYYY-jMM-jDD') : t('receip.not_paid')} />
|
||||
<Td text={''}>
|
||||
<Link to={Pages.receipts.detail + item.id}>
|
||||
<Eye size={20} color='black' />
|
||||
</Link>
|
||||
</Td>
|
||||
</tr>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
import { Fragment, FC, useState } from 'react'
|
||||
import Button from '../../../components/Button'
|
||||
import { TickCircle } from 'iconsax-react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useApproveInvoice, useRequestApprove } from '../hooks/useReceiptData'
|
||||
import { ErrorType } from '../../../helpers/types'
|
||||
import { toast } from 'react-toastify'
|
||||
import { clx } from '../../../helpers/utils'
|
||||
import { useGetProfile } from '../../profile/hooks/useProfileData'
|
||||
import { RequestApproveInvoiceType } from '../types/ReceiptTypes'
|
||||
import DefaulModal from '../../../components/DefaulModal'
|
||||
import OTPInput from 'react-otp-input'
|
||||
import { useCountDown } from '../../../hooks/useCountDown'
|
||||
|
||||
type Props = {
|
||||
id: string,
|
||||
refetch: () => void,
|
||||
status: string
|
||||
}
|
||||
|
||||
const ApproveInvoice: FC<Props> = ({ id, refetch, status }) => {
|
||||
|
||||
const approveInvoce = useApproveInvoice()
|
||||
const requestApprove = useRequestApprove()
|
||||
const getProfile = useGetProfile()
|
||||
const { value, reset } = useCountDown(2, true)
|
||||
const [showModal, setShowModal] = useState<boolean>(false)
|
||||
const { t } = useTranslation('global')
|
||||
const [code, setCode] = useState<string>('')
|
||||
|
||||
const handleConfrim = () => {
|
||||
approveInvoce.mutate({ code: code, id: id, phone: getProfile.data?.data?.user?.phone }, {
|
||||
onSuccess: () => {
|
||||
toast.success(t('success'))
|
||||
refetch()
|
||||
setShowModal(false)
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast.error(error.response?.data?.error.message[0])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleShowModal = () => {
|
||||
const params: RequestApproveInvoiceType = {
|
||||
id: id,
|
||||
phone: getProfile.data?.data?.user?.phone
|
||||
}
|
||||
requestApprove.mutate(params, {
|
||||
onSuccess: () => {
|
||||
setShowModal(true)
|
||||
reset()
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast.error(error.response?.data?.error.message[0])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (status === 'PENDING' || status === 'WAIT_PAYMENT' && getProfile.isSuccess)
|
||||
return (
|
||||
<Fragment>
|
||||
<Button
|
||||
disabled={approveInvoce.isPending || requestApprove.isPending}
|
||||
onClick={() => status === 'PENDING' ? handleShowModal() : null}
|
||||
className={clx(
|
||||
'px-9 w-fit bg-white bg-opacity-70 text-description',
|
||||
status === 'WAIT_PAYMENT' && 'bg-green-300 text-[#00BA4B] bg-opacity-10'
|
||||
)}
|
||||
>
|
||||
<div className='flex gap-2.5'>
|
||||
<TickCircle
|
||||
size={20}
|
||||
color={status === 'PENDING' ? '#888888' : '#00BA4B'}
|
||||
/>
|
||||
<div className='whitespace-nowrap'>
|
||||
{
|
||||
status === 'WAIT_PAYMENT' ?
|
||||
t('receip.accepted')
|
||||
:
|
||||
t('receip.confrim_factor')
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
<DefaulModal
|
||||
open={showModal}
|
||||
close={() => setShowModal(false)}
|
||||
isHeader
|
||||
title_header={t('profile.confrim')}
|
||||
>
|
||||
<div className='mt-7'>
|
||||
<div className='text-xs text-center'>
|
||||
کد تایید به شماره {getProfile.data?.data?.user?.phone} ارسال شده است.برای تایید کد مربوطه را وارد کنید.
|
||||
</div>
|
||||
|
||||
<div className='mt-10'>کد تایید</div>
|
||||
<div className='mt-2 w-full flex justify-center dltr otp'>
|
||||
<OTPInput
|
||||
value={code}
|
||||
onChange={setCode}
|
||||
shouldAutoFocus
|
||||
numInputs={5}
|
||||
renderInput={(props) =>
|
||||
<input
|
||||
{...props}
|
||||
type='tel'
|
||||
autoComplete="one-time-code"
|
||||
inputMode="numeric"
|
||||
className='w-full h-[50px] flex-1 mx-2 bg-white bg-opacity-30 border rounded-2.5' />
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='flex mt-7 justify-center'>
|
||||
{
|
||||
value !== '00:00' ?
|
||||
<div className='flex gap-1 text-description text-xs'>
|
||||
<div>{value}</div>
|
||||
<div>{t('auth.counter_otp')}</div>
|
||||
</div>
|
||||
:
|
||||
<div onClick={handleShowModal} className='flex cursor-pointer gap-1 pl-2 text-black text-sm'>
|
||||
<div className=''>
|
||||
{t('auth.try_again')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
<div className='mt-10 flex justify-end border-t border-border pt-8'>
|
||||
<div className='flex gap-5'>
|
||||
<Button
|
||||
className='bg-white bg-opacity-40 text-description w-[150px] text-xs'
|
||||
label='لغو'
|
||||
onClick={() => setShowModal(false)}
|
||||
/>
|
||||
<Button
|
||||
className='w-[150px] text-xs'
|
||||
onClick={handleConfrim}
|
||||
disabled={code.length !== 5}
|
||||
isLoading={approveInvoce.isPending || requestApprove.isPending}
|
||||
>
|
||||
<div className='flex gap-2 items-center'>
|
||||
<TickCircle
|
||||
size={20}
|
||||
color='white'
|
||||
/>
|
||||
<div>{t('save')}</div>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</DefaulModal>
|
||||
</Fragment>
|
||||
)
|
||||
else return null
|
||||
}
|
||||
|
||||
export default ApproveInvoice
|
||||
@@ -0,0 +1,118 @@
|
||||
import { FC, Fragment } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useGetLegalInfo } from '../../financial/hooks/useFinancialData'
|
||||
import { useGetProfile } from '../../profile/hooks/useProfileData'
|
||||
|
||||
|
||||
const LegalInfo: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const { data: d, isSuccess } = useGetLegalInfo()
|
||||
const getProfile = useGetProfile()
|
||||
|
||||
const data = d?.data?.legalUser
|
||||
|
||||
if (isSuccess)
|
||||
return (
|
||||
<Fragment>
|
||||
<div className='mt-8 flex xl:flex-row flex-col xl:gap-16 gap-4 xl:items-center text-xs'>
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.type_person')}
|
||||
</div>
|
||||
<div>
|
||||
{
|
||||
t('receip.company')
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.representativeـname')}
|
||||
</div>
|
||||
<div>
|
||||
{getProfile.data?.data?.user?.firstName + ' ' + getProfile.data?.data?.user?.lastName}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.company_name')}
|
||||
</div>
|
||||
<div>
|
||||
{data.registrationName}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.registration_number')}
|
||||
</div>
|
||||
<div>
|
||||
{data.registrationCode}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='mt-8 border-t pt-7 flex xl:flex-row flex-col xl:gap-16 gap-4 xl:items-center text-xs'>
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.tel_company')}
|
||||
</div>
|
||||
<div>
|
||||
{data.phone}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.national_code')}
|
||||
</div>
|
||||
<div>
|
||||
{data?.nationalIdentity}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.postal_code')}
|
||||
</div>
|
||||
<div>
|
||||
{data?.address?.postalCode}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div className='mt-6 flex xl:flex-row flex-col xl:gap-16 gap-4 xl:items-center text-xs'>
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.state')}
|
||||
</div>
|
||||
<div>
|
||||
{data?.address?.city?.province?.name}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.city')}
|
||||
</div>
|
||||
<div>
|
||||
{data?.address?.city?.name}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.address')}
|
||||
</div>
|
||||
<div>
|
||||
{data?.address?.fullAddress}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
export default LegalInfo
|
||||
@@ -0,0 +1,68 @@
|
||||
import { FC, Fragment, useState } from 'react'
|
||||
import Button from '../../../components/Button'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useGetInvoiceDetail, usePayInvoice } from '../hooks/useReceiptData'
|
||||
import { ErrorType } from '../../../helpers/types'
|
||||
import ModalConfrim from '../../../components/ModalConfrim'
|
||||
// import { usePayment } from '../../wallet/hooks/useWalletData'
|
||||
import { toast } from '../../../components/Toast'
|
||||
|
||||
type Props = {
|
||||
id: string,
|
||||
refetch: () => void,
|
||||
gateWayId: string,
|
||||
}
|
||||
|
||||
const PayInvoice: FC<Props> = ({ id, refetch, gateWayId }) => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const [showModal, setShowModal] = useState<boolean>(false)
|
||||
const getInvoce = useGetInvoiceDetail(id)
|
||||
const payInvoice = usePayInvoice()
|
||||
// const payment = usePayment()
|
||||
|
||||
|
||||
const handleConfrim = () => {
|
||||
payInvoice.mutate(id, {
|
||||
onSuccess: () => {
|
||||
toast(t('success'), 'success')
|
||||
refetch()
|
||||
setShowModal(false)
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast(error.response?.data?.error.message[0], 'error')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleChartWallet = () => {
|
||||
// payment.mutate({ amount: getInvoce.data?.data?.remainingToCharge, gatewayId: gateWayId, invoiceId: id }, {
|
||||
// onSuccess: (data) => {
|
||||
// window.location.href = data?.data?.redirectUrl
|
||||
// toast(t('wallet.transfer_to_getway'), 'success')
|
||||
// },
|
||||
// onError: (error: ErrorType) => {
|
||||
// toast(error.response?.data?.error.message[0], 'error')
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Button
|
||||
label={getInvoce.data?.data?.remainingToCharge > 0 ? t('receip.charge_wallet') : t('receip.pay')}
|
||||
onClick={() => setShowModal(true)}
|
||||
/>
|
||||
|
||||
<ModalConfrim
|
||||
isOpen={showModal}
|
||||
close={() => setShowModal(false)}
|
||||
onConfrim={getInvoce.data?.data?.remainingToCharge > 0 ? handleChartWallet : handleConfrim}
|
||||
label={getInvoce.data?.data?.remainingToCharge > 0 ? t('receip.sure_charge_wallet') : t('receip.sure_confrim_pay')}
|
||||
isLoading={payInvoice.isPending}
|
||||
/>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
export default PayInvoice
|
||||
@@ -0,0 +1,107 @@
|
||||
import { FC, Fragment } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useGetRealInfo } from '../../financial/hooks/useFinancialData'
|
||||
|
||||
|
||||
|
||||
const RealInfo: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const { data: d } = useGetRealInfo()
|
||||
|
||||
const data = d?.data?.realUser
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div className='mt-8 flex xl:flex-row flex-col xl:gap-16 gap-4 xl:items-center text-xs'>
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.type_person')}
|
||||
</div>
|
||||
<div>
|
||||
{t('receip.real_person')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.fullName')}
|
||||
</div>
|
||||
<div>
|
||||
{data?.firstName + ' ' + data?.lastName}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('financial.father_name')}
|
||||
</div>
|
||||
<div>
|
||||
{data?.fatherName}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('financial.sex')}
|
||||
</div>
|
||||
<div>
|
||||
{data?.gender === 'MALE' ? 'مرد' : 'زن'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='mt-8 border-t pt-7 flex xl:flex-row flex-col xl:gap-16 gap-4 xl:items-center text-xs'>
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('financial.phone')}
|
||||
</div>
|
||||
<div>
|
||||
{data?.phone}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.postal_code')}
|
||||
</div>
|
||||
<div>
|
||||
{data?.address?.postalCode}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div className='mt-6 flex xl:flex-row flex-col xl:gap-16 gap-4 xl:items-center text-xs'>
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.state')}
|
||||
</div>
|
||||
<div>
|
||||
{data?.address?.city?.province?.name}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.city')}
|
||||
</div>
|
||||
<div>
|
||||
{data?.address?.city?.name}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.address')}
|
||||
</div>
|
||||
<div>
|
||||
{data?.address?.fullAddress}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
export default RealInfo
|
||||
@@ -0,0 +1,70 @@
|
||||
import { FC, Fragment } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { FinancialDataType } from '../../financial/types/FinancialTypes'
|
||||
import { InfoCircle } from 'iconsax-react'
|
||||
import Button from '../../../components/Button'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Pages } from '../../../config/Pages'
|
||||
|
||||
type Props = {
|
||||
data: FinancialDataType
|
||||
}
|
||||
|
||||
const RegisterInfo: FC<Props> = ({ data }) => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
|
||||
<div className='mt-8 xl:flex-row flex-col xl:h-10 py-4 xl:py-0 text-xs bg-[#FFF2F2] text-[#DB0105] rounded-xl px-4 flex gap-2 items-center'>
|
||||
<InfoCircle
|
||||
size={18}
|
||||
color='#DB0105'
|
||||
className='min-w-[18px]'
|
||||
/>
|
||||
<div className='text-center xl:text-right'>{t('receip.compelete_financal_info_error')}</div>
|
||||
|
||||
<div className='flex-1 flex xl:justify-end'>
|
||||
<Link to={Pages.financial}>
|
||||
<Button
|
||||
label={t('receip.compelete_financal_info')}
|
||||
className='w-fit whitespace-nowrap px-4 h-7 text-[10px]'
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className='mt-8 flex xl:flex-row flex-col xl:gap-16 gap-4 xl:items-center text-xs'>
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.type_person')}
|
||||
</div>
|
||||
<div>
|
||||
{t('receip.real_person')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('receip.fullName')}
|
||||
</div>
|
||||
<div>
|
||||
{data.user?.firstName + ' ' + data.user?.lastName}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-2'>
|
||||
<div className='text-description'>
|
||||
{t('financial.phone')}
|
||||
</div>
|
||||
<div>
|
||||
{data.user?.phone}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
export default RegisterInfo
|
||||
@@ -0,0 +1,62 @@
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import * as api from "../service/ReceiptService";
|
||||
import {
|
||||
ApplyDiscountType,
|
||||
ApproveInvoiceType,
|
||||
RemoveDiscountType,
|
||||
RequestApproveInvoiceType,
|
||||
} from "../types/ReceiptTypes";
|
||||
|
||||
export const useGetInvoices = (status: string) => {
|
||||
return useQuery({
|
||||
queryKey: ["invoices", status],
|
||||
queryFn: () => api.getInvoice(status),
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetInvoiceDetail = (id: string) => {
|
||||
return useQuery({
|
||||
queryKey: ["invoice", id],
|
||||
queryFn: () => api.getInvoiceDetail(id),
|
||||
enabled: !!id,
|
||||
});
|
||||
};
|
||||
|
||||
export const useApproveInvoice = () => {
|
||||
return useMutation({
|
||||
mutationFn: (variables: ApproveInvoiceType) =>
|
||||
api.approveInvoice(variables),
|
||||
});
|
||||
};
|
||||
|
||||
export const usePayInvoice = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (variables: string) => api.payInvoice(variables),
|
||||
onSuccess: () => {
|
||||
queryClient.refetchQueries({
|
||||
queryKey: ["wallet-balance"],
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useRequestApprove = () => {
|
||||
return useMutation({
|
||||
mutationFn: (variables: RequestApproveInvoiceType) =>
|
||||
api.requestApprove(variables),
|
||||
});
|
||||
};
|
||||
|
||||
export const useApplyDiscount = () => {
|
||||
return useMutation({
|
||||
mutationFn: (variables: ApplyDiscountType) => api.applyDiscount(variables),
|
||||
});
|
||||
};
|
||||
|
||||
export const useRemoveDiscount = () => {
|
||||
return useMutation({
|
||||
mutationFn: (variables: RemoveDiscountType) =>
|
||||
api.removeDiscount(variables.id),
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
import axios from "../../../config/axios";
|
||||
import {
|
||||
ApplyDiscountType,
|
||||
ApproveInvoiceType,
|
||||
RequestApproveInvoiceType,
|
||||
} from "../types/ReceiptTypes";
|
||||
|
||||
export const getInvoice = async (status: string) => {
|
||||
const { data } = await axios.get(`/invoices/user?status=${status}`);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const getInvoiceDetail = async (id: string) => {
|
||||
const { data } = await axios.get(`/invoices/${id}`);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const requestApprove = async (params: RequestApproveInvoiceType) => {
|
||||
const { data } = await axios.post(
|
||||
`/invoices/${params.id}/approve/request`,
|
||||
params
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const approveInvoice = async (params: ApproveInvoiceType) => {
|
||||
const { data } = await axios.patch(`/invoices/${params.id}/approve`, params);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const payInvoice = async (id: string) => {
|
||||
const { data } = await axios.post(`/invoices/${id}/pay`);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const applyDiscount = async (params: ApplyDiscountType) => {
|
||||
const { data } = await axios.post(
|
||||
`/invoices/${params.id}/apply-discount`,
|
||||
params
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const removeDiscount = async (id: string) => {
|
||||
const { data } = await axios.post(`/invoices/${id}/cancel-discount`);
|
||||
return data;
|
||||
};
|
||||
@@ -0,0 +1,60 @@
|
||||
export type InvoiceStatus =
|
||||
| "PENDING"
|
||||
| "WAIT_PAYMENT"
|
||||
| "PAID"
|
||||
| "ARCHIVED"
|
||||
| "OVERDUE";
|
||||
|
||||
export type ReceiptItemType = {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
name: string;
|
||||
count: string | number;
|
||||
unitPrice: string | number;
|
||||
discount: string | number;
|
||||
totalPrice: number;
|
||||
subscriptionPlan?: string | null;
|
||||
items: ReceiptItemType[];
|
||||
user: {
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
};
|
||||
dueDate: string;
|
||||
status: InvoiceStatus;
|
||||
paidAt: string;
|
||||
numericId: string;
|
||||
lateFee: number;
|
||||
};
|
||||
|
||||
export type ReceiptDetailItemType = {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
name: string;
|
||||
count: string | number;
|
||||
unitPrice: string | number;
|
||||
discount: string | number;
|
||||
totalPrice: number;
|
||||
subscriptionPlan?: string | null;
|
||||
};
|
||||
|
||||
export type RequestApproveInvoiceType = {
|
||||
phone: string;
|
||||
id: string;
|
||||
};
|
||||
|
||||
export type ApproveInvoiceType = {
|
||||
phone: string;
|
||||
code: string;
|
||||
id: string;
|
||||
};
|
||||
|
||||
export type ApplyDiscountType = {
|
||||
id: string;
|
||||
code: string;
|
||||
};
|
||||
|
||||
export type RemoveDiscountType = {
|
||||
id: string;
|
||||
};
|
||||
@@ -5,27 +5,33 @@ 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, useGetCategoriesTicket, useMultiUpload } from './hooks/useTicketData'
|
||||
import { useGetProfile } from '../profile/hooks/useProfileData'
|
||||
import PageLoading from '../../components/PageLoading'
|
||||
import { useFormik } from 'formik'
|
||||
import { CreateTicketType } from './types/TicketTypes'
|
||||
import * as Yup from 'yup'
|
||||
import { toast } from 'react-toastify'
|
||||
import { toast } from '../../components/Toast'
|
||||
import { ErrorType } from '../../helpers/types'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
import { Pages } from '../../config/Pages'
|
||||
import withAuth from '../../hoc/withAuth'
|
||||
import { buildPath } from '../../helpers/utils'
|
||||
// import { useGetMyServices } from '../service/hooks/useServiceData'
|
||||
// import { MyServicesItem } from '../service/types/ServiecTypes'
|
||||
|
||||
const CreateTicket: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const { slug } = useParams()
|
||||
const navigate = useNavigate()
|
||||
const [files, setFiles] = useState<File[]>([])
|
||||
const getProfile = useGetProfile()
|
||||
const getCategories = useGetCategoriesTicket()
|
||||
const multiUpload = useMultiUpload()
|
||||
const createTicket = useCreateTicket()
|
||||
// const getMyservice = useGetMyServices('')
|
||||
|
||||
const formik = useFormik<CreateTicketType>({
|
||||
initialValues: {
|
||||
@@ -33,7 +39,6 @@ const CreateTicket: FC = () => {
|
||||
message: '',
|
||||
categoryId: '',
|
||||
priority: 'LOW',
|
||||
danakServiceId: '',
|
||||
title: '',
|
||||
attachmentUrls: []
|
||||
},
|
||||
@@ -54,7 +59,7 @@ const CreateTicket: FC = () => {
|
||||
values.attachmentUrls = await data.data.map((item: { url: string }) => item?.url)
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast.error(error.response?.data?.error?.message[0])
|
||||
toast(error.response?.data?.error?.message[0], 'error')
|
||||
}
|
||||
})
|
||||
} else {
|
||||
@@ -62,24 +67,23 @@ const CreateTicket: FC = () => {
|
||||
}
|
||||
|
||||
|
||||
|
||||
createTicket.mutate(values, {
|
||||
onSuccess: (data) => {
|
||||
toast.success(t('success'))
|
||||
toast(t('success'), 'success')
|
||||
formik.resetForm()
|
||||
navigate(Pages.ticket.detail + data.data?.ticketId)
|
||||
navigate(buildPath(Pages.ticket.detail + data.data?.ticketId, slug))
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast.error(error.response?.data?.error?.message[0])
|
||||
toast(error.response?.data?.error?.message[0], 'error')
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
console.log(formik.errors);
|
||||
|
||||
|
||||
return (
|
||||
<div className='mt-4'>
|
||||
<div className='mt-4 pb-12'>
|
||||
<div>
|
||||
{t('ticket.new_ticket')}
|
||||
</div>
|
||||
@@ -143,18 +147,6 @@ const CreateTicket: FC = () => {
|
||||
</div>
|
||||
|
||||
<div className='mt-6 rowTwoInput'>
|
||||
<Select
|
||||
label={t('ticket.select_your_service')}
|
||||
items={[
|
||||
{
|
||||
label: '',
|
||||
value: ''
|
||||
}
|
||||
]}
|
||||
placeholder={t('ticket.not_once')}
|
||||
className='border'
|
||||
name='danakServiceId'
|
||||
/>
|
||||
<Select
|
||||
label={t('ticket.category')}
|
||||
items={getCategories.data?.data?.ticketCategories?.map((item: { id: string, title: string }) => ({
|
||||
@@ -191,11 +183,17 @@ const CreateTicket: FC = () => {
|
||||
|
||||
<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>
|
||||
|
||||
@@ -226,7 +224,7 @@ const CreateTicket: FC = () => {
|
||||
<TickSquare size={20} color='black' variant='Bold' />
|
||||
</div>
|
||||
<div className='text-description text-xs leading-5'>
|
||||
پاسخگویی 24 ساعته تلفنی را تنها از میهن وب هاست می توانید انتظار داشته باشید .بخش پشتیبانی در هر ساعتی حتی در روز های تعطیل آماده پیگیری سریع مشکلات کاربران است.
|
||||
پاسخگویی 24 ساعته تلفنی را تنها از داناک می توانید انتظار داشته باشید .بخش پشتیبانی در هر ساعتی حتی در روز های تعطیل آماده پیگیری سریع مشکلات کاربران است.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -240,4 +238,4 @@ const CreateTicket: FC = () => {
|
||||
)
|
||||
}
|
||||
|
||||
export default CreateTicket
|
||||
export default withAuth(CreateTicket)
|
||||
+36
-25
@@ -4,7 +4,7 @@ import Input from '../../components/Input'
|
||||
import Textarea from '../../components/Textarea'
|
||||
import UploadBox from '../../components/UploadBox'
|
||||
import Button from '../../components/Button'
|
||||
import { CloseCircle, InfoCircle, Paperclip2 } from 'iconsax-react'
|
||||
import { CloseCircle, InfoCircle, Paperclip2, Send2 } from 'iconsax-react'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
import { useAddMessageTicket, useCloseTicket, useGetMessages, useMultiUpload } from './hooks/useTicketData'
|
||||
import PageLoading from '../../components/PageLoading'
|
||||
@@ -12,7 +12,7 @@ import moment from 'moment-jalaali'
|
||||
import { useFormik } from 'formik'
|
||||
import { AddMessageTicketType } from './types/TicketTypes'
|
||||
import * as Yup from 'yup'
|
||||
import { toast } from 'react-toastify'
|
||||
import { toast } from '../../components/Toast'
|
||||
import { ErrorType } from '../../helpers/types'
|
||||
import { clx } from '../../helpers/utils'
|
||||
import { Pages } from '../../config/Pages'
|
||||
@@ -22,8 +22,8 @@ const TicketDetail: FC = () => {
|
||||
const { t } = useTranslation('global')
|
||||
const navigate = useNavigate()
|
||||
const { id } = useParams()
|
||||
const getMessages = useGetMessages(id ? id : '')
|
||||
const [files, setFiles] = useState<File[]>([])
|
||||
const getMessages = useGetMessages(id ? id : '')
|
||||
const addMessage = useAddMessageTicket(id ? id : '')
|
||||
const multiUpload = useMultiUpload()
|
||||
const [isResetFiles, setIsResetFiles] = useState<boolean>(false)
|
||||
@@ -48,7 +48,7 @@ const TicketDetail: FC = () => {
|
||||
values.attachmentUrls = await data.data.map((item: { url: string }) => item?.url)
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast.error(error.response?.data?.error?.message[0])
|
||||
toast(error.response?.data?.error?.message[0], 'error')
|
||||
}
|
||||
})
|
||||
} else {
|
||||
@@ -57,7 +57,7 @@ const TicketDetail: FC = () => {
|
||||
|
||||
addMessage.mutate(values, {
|
||||
onSuccess: () => {
|
||||
toast.success(t('success'))
|
||||
toast(t('success'), 'success')
|
||||
formik.resetForm()
|
||||
setFiles([])
|
||||
setIsResetFiles(true)
|
||||
@@ -66,7 +66,7 @@ const TicketDetail: FC = () => {
|
||||
}, 1000);
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast.error(error.response?.data?.error?.message[0])
|
||||
toast(error.response?.data?.error?.message[0], 'error')
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -75,20 +75,27 @@ const TicketDetail: FC = () => {
|
||||
const hadleCloseTicket = () => {
|
||||
closeTicket.mutate(id ? id : '', {
|
||||
onSuccess: () => {
|
||||
toast.success(t('success'))
|
||||
toast(t('success'), 'success')
|
||||
navigate(Pages.ticket.list)
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast.error(error.response?.data?.error?.message[0])
|
||||
toast(error.response?.data?.error?.message[0], 'error')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleSendAnswer = () => {
|
||||
const messageEndRef = document.getElementById('content');
|
||||
if (messageEndRef) {
|
||||
messageEndRef.scrollIntoView({ behavior: 'smooth' });
|
||||
messageEndRef.focus();
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='mt-4'>
|
||||
<div className='flex gap-1'>
|
||||
<div>{t('ticket.ticket_number')}</div>
|
||||
<div>12312</div>
|
||||
</div>
|
||||
{
|
||||
getMessages.isPending ?
|
||||
@@ -96,12 +103,12 @@ const TicketDetail: FC = () => {
|
||||
:
|
||||
<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.subject')}
|
||||
value={getMessages.data?.data?.ticket?.subject}
|
||||
readOnly
|
||||
/>
|
||||
<div className='gap-6 rowTwoInput mt-5'>
|
||||
<Input
|
||||
label={t('ticket.user_name')}
|
||||
value={getMessages.data?.data?.ticket?.user?.firstName + ' ' + getMessages.data?.data?.ticket?.user?.lastName}
|
||||
@@ -109,13 +116,9 @@ const TicketDetail: FC = () => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mt-8 xl:text-sm text-xs'>
|
||||
{getMessages.data?.data?.ticket?.subject}
|
||||
</div>
|
||||
|
||||
{
|
||||
getMessages.data?.data?.messages.map((item: any) => {
|
||||
if (item?.author?.role?.name === 'user') {
|
||||
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'>
|
||||
@@ -154,8 +157,8 @@ const TicketDetail: FC = () => {
|
||||
<div className='leading-7 mt-4'>
|
||||
{item.content}
|
||||
</div>
|
||||
<div className='flex justify-end mt-6 text-xs text-description'>
|
||||
10:07 | 1403/09/30
|
||||
<div className='flex dltr end mt-6 text-xs text-description'>
|
||||
{moment(item.createdAt).format('jYYYY-jMM-jDD HH:mm')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -186,6 +189,7 @@ const TicketDetail: FC = () => {
|
||||
<Fragment>
|
||||
<div className='mt-9'>
|
||||
<Textarea
|
||||
id='content'
|
||||
label={t('ticket.answer')}
|
||||
placeholder={t('ticket.your_description')}
|
||||
value={formik.values.content}
|
||||
@@ -206,11 +210,17 @@ const TicketDetail: FC = () => {
|
||||
|
||||
<div className='flex justify-end mt-6'>
|
||||
<Button
|
||||
label={t('ticket.send')}
|
||||
className='max-w-[100px]'
|
||||
className='xl:max-w-[100px]'
|
||||
onClick={() => formik.handleSubmit()}
|
||||
isLoading={addMessage.isPending || multiUpload.isPending}
|
||||
/>
|
||||
>
|
||||
<div className='flex gap-2 items-center'>
|
||||
<Send2 size={20} color='white' />
|
||||
<div>
|
||||
{t('ticket.send')}
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</Fragment>
|
||||
}
|
||||
@@ -223,6 +233,7 @@ const TicketDetail: FC = () => {
|
||||
<Button
|
||||
label={t('ticket.send_answer')}
|
||||
className='text-xs xl:h-10 h-8'
|
||||
onClick={handleSendAnswer}
|
||||
/>
|
||||
<Button
|
||||
className='bg-[#D52903] xl:h-10 h-8'
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import { FC, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '../../components/Button'
|
||||
import { Add, Eye, MessageRemove, MessageTick, MessageTime } from 'iconsax-react'
|
||||
import { Add, Eye, MessageRemove, Messages1, MessageTick, MessageTime } from 'iconsax-react'
|
||||
import Tabs from '../../components/Tabs'
|
||||
import Td from '../../components/Td'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Pages } from '../../config/Pages'
|
||||
import { useGetTickets } from './hooks/useTicketData'
|
||||
import moment from 'moment-jalaali'
|
||||
import { buildPath } from '../../helpers/utils'
|
||||
|
||||
const TicketList: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const [activeTab, setActiveTab] = useState<'ANSWERED' | 'PENDING' | 'CLOSED'>('ANSWERED')
|
||||
const [activeTab, setActiveTab] = useState<'ANSWERED' | 'PENDING' | 'CLOSED' | ''>('')
|
||||
const getTickets = useGetTickets(activeTab)
|
||||
|
||||
return (
|
||||
@@ -22,7 +23,7 @@ const TicketList: FC = () => {
|
||||
{t('ticket.tickets')}
|
||||
</div>
|
||||
|
||||
<Link to={Pages.ticket.create}>
|
||||
<Link to={buildPath(Pages.ticket.create)}>
|
||||
<Button
|
||||
className='w-[172px]'
|
||||
>
|
||||
@@ -41,20 +42,20 @@ const TicketList: FC = () => {
|
||||
active={activeTab}
|
||||
items={[
|
||||
{
|
||||
icon: <MessageTick color={activeTab === 'ANSWERED' ? 'black' : '#8C90A3'} size={22} />,
|
||||
label: t('ticket.answered'),
|
||||
value: 'ANSWERED'
|
||||
icon: <Messages1 color={activeTab === '' ? 'black' : '#8C90A3'} size={22} />,
|
||||
label: t('ticket.all'),
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
icon: <MessageTime color={activeTab === 'PENDING' ? 'black' : '#8C90A3'} size={22} />,
|
||||
label: t('ticket.checking'),
|
||||
value: 'PENDING'
|
||||
},
|
||||
// {
|
||||
// icon: <Message2 color={activeTab === 'wating' ? 'black' : '#8C90A3'} size={22} />,
|
||||
// label: t('ticket.wait_answered'),
|
||||
// value: 'wating'
|
||||
// },
|
||||
{
|
||||
icon: <MessageTick color={activeTab === 'ANSWERED' ? 'black' : '#8C90A3'} size={22} />,
|
||||
label: t('ticket.answered'),
|
||||
value: 'ANSWERED'
|
||||
},
|
||||
{
|
||||
icon: <MessageRemove color={activeTab === 'CLOSED' ? 'black' : '#8C90A3'} size={22} />,
|
||||
label: t('ticket.closed'),
|
||||
@@ -71,7 +72,8 @@ const TicketList: FC = () => {
|
||||
<tr>
|
||||
<Td text={t('ticket.number')} />
|
||||
<Td text={t('ticket.title')} />
|
||||
<Td text={t('ticket.team')} />
|
||||
<Td text={t('ticket.category')} />
|
||||
<Td text={t('ticket.service')} />
|
||||
<Td text={t('ticket.date')} />
|
||||
<Td text={t('ticket.status')} />
|
||||
<Td text={t('ticket.priority')} />
|
||||
@@ -86,6 +88,7 @@ const TicketList: FC = () => {
|
||||
<Td text={item.numericId} />
|
||||
<Td text={item.subject} />
|
||||
<Td text={item?.category?.title} />
|
||||
<Td text={item?.danakService?.name} />
|
||||
<Td text={''}>
|
||||
<div className='dltr text-right'>
|
||||
{moment(item.updatedAt).format('jYYYY-jMM-jDD HH:mm')}
|
||||
|
||||
@@ -22,14 +22,15 @@ export const useGetCategoriesTicket = () => {
|
||||
});
|
||||
};
|
||||
|
||||
export const useSingleUpload = () => {
|
||||
export const useSingleUpload = (callback?: (value: number) => void) => {
|
||||
return useMutation({
|
||||
mutationFn: (variables: FormData) => api.uploadSingle(variables),
|
||||
mutationFn: (variables: FormData) => api.uploadSingle(variables, callback),
|
||||
});
|
||||
};
|
||||
export const useMultiUpload = () => {
|
||||
export const useMultiUpload = (callback?: (value: number) => void) => {
|
||||
return useMutation({
|
||||
mutationFn: (variables: FormData) => api.uploadMultiple(variables),
|
||||
mutationFn: (variables: FormData) =>
|
||||
api.uploadMultiple(variables, callback),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -56,7 +57,16 @@ export const useAddMessageTicket = (id: string) => {
|
||||
};
|
||||
|
||||
export const useCloseTicket = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => api.closeTicket(id),
|
||||
onSuccess: () => {
|
||||
queryClient.refetchQueries({
|
||||
queryKey: ["tickets"],
|
||||
});
|
||||
queryClient.refetchQueries({
|
||||
queryKey: ["ticket-messages"],
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2,7 +2,11 @@ import axios from "../../../config/axios";
|
||||
import { AddMessageTicketType, CreateTicketType } from "../types/TicketTypes";
|
||||
|
||||
export const getTickets = async (status: string) => {
|
||||
const { data } = await axios.get(`/tickets?status=${status}`);
|
||||
let query = ``;
|
||||
if (status) {
|
||||
query = `?status=${status}`;
|
||||
}
|
||||
const { data } = await axios.get(`/tickets${query}`);
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -16,13 +20,45 @@ export const getCategoriesTicket = async () => {
|
||||
return data;
|
||||
};
|
||||
|
||||
export const uploadSingle = async (params: FormData) => {
|
||||
const { data } = await axios.post(`/uploader/single-file`, params);
|
||||
export const uploadSingle = async (
|
||||
params: FormData,
|
||||
callback?: (value: number) => void
|
||||
) => {
|
||||
const { data } = await axios.post(`/uploader/single-file`, params, {
|
||||
onUploadProgress: (progressEvent: any) => {
|
||||
if (progressEvent.bytes) {
|
||||
if (callback) {
|
||||
callback(
|
||||
Math.round((progressEvent.loaded / progressEvent.total) * 100)
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
if (callback) {
|
||||
callback(0);
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
export const uploadMultiple = async (params: FormData) => {
|
||||
const { data } = await axios.post(`/uploader/multi-file`, params);
|
||||
export const uploadMultiple = async (
|
||||
params: FormData,
|
||||
callback?: (value: number) => void
|
||||
) => {
|
||||
const { data } = await axios.post(`/uploader/multi-file`, params, {
|
||||
onUploadProgress: (progressEvent: any) => {
|
||||
if (progressEvent.bytes) {
|
||||
if (callback) {
|
||||
callback(
|
||||
Math.round((progressEvent.loaded / progressEvent.total) * 100)
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
if (callback) {
|
||||
callback(0);
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ import { useGetSlugId } from "../pages/home/hooks/useHomeData"
|
||||
import { ErrorType } from "../helpers/types"
|
||||
import { toast } from "react-toastify"
|
||||
import Request from "../pages/request/Request"
|
||||
import TicketList from "../pages/ticket/TicketList"
|
||||
import TicketCreate from "../pages/ticket/CreateTicket"
|
||||
import TicketDetail from "../pages/ticket/Detail"
|
||||
const MainRouter = () => {
|
||||
const { slug } = useParams()
|
||||
const getSlugId = useGetSlugId()
|
||||
@@ -71,6 +74,9 @@ const MainRouter = () => {
|
||||
<Route path="reportCard/list" element={<ReportCardList />} />
|
||||
<Route path="profile" element={<Profile />} />
|
||||
<Route path="request" element={<Request />} />
|
||||
<Route path="tickets" element={<TicketList />} />
|
||||
<Route path="tickets/create" element={<TicketCreate />} />
|
||||
<Route path="tickets/messages/:id" element={<TicketDetail />} />
|
||||
</Routes>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user