ticket
This commit is contained in:
@@ -1,3 +1,3 @@
|
|||||||
VITE_TOKEN_NAME = 'dsc_token'
|
VITE_TOKEN_NAME = 'dsc_token'
|
||||||
VITE_REFRESH_TOKEN_NAME = 'dsc_refresh_token'
|
VITE_REFRESH_TOKEN_NAME = 'dsc_refresh_token'
|
||||||
VITE_BASE_URL = 'http://192.168.0.235:3500'
|
VITE_BASE_URL = 'http://192.168.0.221:3500'
|
||||||
Generated
+50
@@ -15,6 +15,7 @@
|
|||||||
"formik": "^2.4.6",
|
"formik": "^2.4.6",
|
||||||
"i18next": "^24.2.0",
|
"i18next": "^24.2.0",
|
||||||
"iconsax-react": "^0.0.8",
|
"iconsax-react": "^0.0.8",
|
||||||
|
"moment-jalaali": "^0.10.4",
|
||||||
"rc-rate": "^2.13.0",
|
"rc-rate": "^2.13.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
@@ -33,6 +34,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.17.0",
|
"@eslint/js": "^9.17.0",
|
||||||
|
"@types/moment-jalaali": "^0.7.9",
|
||||||
"@types/react": "^18.3.17",
|
"@types/react": "^18.3.17",
|
||||||
"@types/react-dom": "^18.3.5",
|
"@types/react-dom": "^18.3.5",
|
||||||
"@types/swiper": "^5.4.3",
|
"@types/swiper": "^5.4.3",
|
||||||
@@ -1841,6 +1843,16 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/moment-jalaali": {
|
||||||
|
"version": "0.7.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/moment-jalaali/-/moment-jalaali-0.7.9.tgz",
|
||||||
|
"integrity": "sha512-gsDOoAzRnCfQTbfdlUrCvX6R0wIto6CvwfvV2C3j4qJLK+DEiTK8Rl/xlOCBO9C6qeUfX8oyZ2UfjnXJTOvHSA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"moment": ">=2.14.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/prop-types": {
|
"node_modules/@types/prop-types": {
|
||||||
"version": "15.7.14",
|
"version": "15.7.14",
|
||||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz",
|
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz",
|
||||||
@@ -3492,6 +3504,12 @@
|
|||||||
"@pkgjs/parseargs": "^0.11.0"
|
"@pkgjs/parseargs": "^0.11.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jalaali-js": {
|
||||||
|
"version": "1.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/jalaali-js/-/jalaali-js-1.2.7.tgz",
|
||||||
|
"integrity": "sha512-gE+YHWSbygYAoJa+Xg8LWxGILqFOxZSBQQw39ghel01fVFUxV7bjL0x1JFsHcLQ3uPjvn81HQMa+kxwyPWnxGQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/jiti": {
|
"node_modules/jiti": {
|
||||||
"version": "1.21.7",
|
"version": "1.21.7",
|
||||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
|
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
|
||||||
@@ -3743,6 +3761,38 @@
|
|||||||
"node": ">=16 || 14 >=14.17"
|
"node": ">=16 || 14 >=14.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/moment": {
|
||||||
|
"version": "2.30.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||||
|
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moment-jalaali": {
|
||||||
|
"version": "0.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment-jalaali/-/moment-jalaali-0.10.4.tgz",
|
||||||
|
"integrity": "sha512-/eD0HeyvATznb5iE0G1BHjKRZAFEpJ9ZNUkcHwXhNgt1WJJVVzHD7+uDmqzZWVFLdbGme2gvIXKb3ezDYOXcZA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"jalaali-js": "^1.2.7",
|
||||||
|
"moment": "^2.29.4",
|
||||||
|
"moment-timezone": "^0.5.46"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moment-timezone": {
|
||||||
|
"version": "0.5.47",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.47.tgz",
|
||||||
|
"integrity": "sha512-UbNt/JAWS0m/NJOebR0QMRHBk0hu03r5dx9GK8Cs0AS3I81yDcOc9k+DytPItgVvBP7J6Mf6U2n3BPAacAV9oA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"moment": "^2.29.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
"formik": "^2.4.6",
|
"formik": "^2.4.6",
|
||||||
"i18next": "^24.2.0",
|
"i18next": "^24.2.0",
|
||||||
"iconsax-react": "^0.0.8",
|
"iconsax-react": "^0.0.8",
|
||||||
|
"moment-jalaali": "^0.10.4",
|
||||||
"rc-rate": "^2.13.0",
|
"rc-rate": "^2.13.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
@@ -35,6 +36,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.17.0",
|
"@eslint/js": "^9.17.0",
|
||||||
|
"@types/moment-jalaali": "^0.7.9",
|
||||||
"@types/react": "^18.3.17",
|
"@types/react": "^18.3.17",
|
||||||
"@types/react-dom": "^18.3.5",
|
"@types/react-dom": "^18.3.5",
|
||||||
"@types/swiper": "^5.4.3",
|
"@types/swiper": "^5.4.3",
|
||||||
|
|||||||
@@ -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 |
@@ -7,7 +7,7 @@ type Props = {
|
|||||||
className?: string;
|
className?: string;
|
||||||
isLoading?: boolean,
|
isLoading?: boolean,
|
||||||
} & ButtonHTMLAttributes<HTMLButtonElement> &
|
} & ButtonHTMLAttributes<HTMLButtonElement> &
|
||||||
XOR<{ children: ReactNode }, { label: string, isLoading?: boolean }>;
|
XOR<{ children: ReactNode }, { label: string }>;
|
||||||
|
|
||||||
const Button: FC<Props> = memo((props: Props) => {
|
const Button: FC<Props> = memo((props: Props) => {
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -45,7 +45,7 @@ const Select: FC<Props> = (props: Props) => {
|
|||||||
</select>
|
</select>
|
||||||
<ArrowDown2 size={16} color='black' className={clx(
|
<ArrowDown2 size={16} color='black' className={clx(
|
||||||
'absolute z-0 top-3 left-2',
|
'absolute z-0 top-3 left-2',
|
||||||
props.label && 'top-9'
|
props.label && 'top-10'
|
||||||
)} />
|
)} />
|
||||||
{
|
{
|
||||||
props.error_text && props.error_text !== '' ?
|
props.error_text && props.error_text !== '' ?
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { FC, InputHTMLAttributes } from 'react'
|
import { FC, InputHTMLAttributes } from 'react'
|
||||||
|
import Error from './Error'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
label: string,
|
label: string,
|
||||||
|
error_text?: string
|
||||||
} & InputHTMLAttributes<HTMLTextAreaElement>
|
} & InputHTMLAttributes<HTMLTextAreaElement>
|
||||||
|
|
||||||
const Textarea: FC<Props> = (props: Props) => {
|
const Textarea: FC<Props> = (props: Props) => {
|
||||||
@@ -17,6 +19,10 @@ const Textarea: FC<Props> = (props: Props) => {
|
|||||||
>
|
>
|
||||||
|
|
||||||
</textarea>
|
</textarea>
|
||||||
|
{
|
||||||
|
props.error_text &&
|
||||||
|
<Error errorText={props.error_text} />
|
||||||
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,22 +1,51 @@
|
|||||||
import { FC, useCallback, useState } from 'react'
|
import { FC, useCallback, useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useDropzone } from 'react-dropzone'
|
import { useDropzone } from 'react-dropzone'
|
||||||
|
import { CloseCircle } from 'iconsax-react'
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
label: string,
|
label: string,
|
||||||
|
isMultiple?: boolean,
|
||||||
|
onChange: (file: File[]) => void;
|
||||||
|
isReset?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const UploadBox: FC<Props> = (props: Props) => {
|
const UploadBox: FC<Props> = (props: Props) => {
|
||||||
|
|
||||||
const { t } = useTranslation('global')
|
const { t } = useTranslation('global')
|
||||||
const [file, setFile] = useState<File>()
|
const [files, setFiles] = useState<File[]>([])
|
||||||
|
|
||||||
const onDrop = useCallback((acceptedFiles: File[]) => {
|
const onDrop = useCallback((acceptedFiles: File[]) => {
|
||||||
setFile(acceptedFiles[0])
|
if (props.isMultiple) {
|
||||||
}, [])
|
const array = [...files]
|
||||||
|
array.push(acceptedFiles[0])
|
||||||
|
setFiles(array)
|
||||||
|
props.onChange(array)
|
||||||
|
} else {
|
||||||
|
setFiles([acceptedFiles[0]])
|
||||||
|
props.onChange([acceptedFiles[0]])
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [files])
|
||||||
const { getRootProps, getInputProps } = useDropzone({ onDrop })
|
const { getRootProps, getInputProps } = useDropzone({ onDrop })
|
||||||
|
|
||||||
|
const handleRemove = (index: number) => {
|
||||||
|
const array = [...files]
|
||||||
|
array.splice(index, 1)
|
||||||
|
setFiles(array)
|
||||||
|
props.onChange(array)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
|
||||||
|
if (props.isReset) {
|
||||||
|
setFiles([])
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [props.isReset])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -28,15 +57,27 @@ const UploadBox: FC<Props> = (props: Props) => {
|
|||||||
{t('select_file')}
|
{t('select_file')}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div className='text-description text-xs'>
|
<div className='lg:flex gap-2 hidden'>
|
||||||
{
|
{
|
||||||
file ?
|
files?.map((item, index) => (
|
||||||
file.name
|
<div key={index} className='flex bg-gray-200 py-1.5 px-2 rounded-lg items-center gap-1'>
|
||||||
:
|
<CloseCircle onClick={() => handleRemove(index)} size={16} color='red' />
|
||||||
t('no_select_file')
|
<div className='text-xs'>{item.name}</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className='lg:hidden gap-2 flex flex-wrap mt-4'>
|
||||||
|
{
|
||||||
|
files?.map((item, index) => (
|
||||||
|
<div key={index} className='flex bg-gray-200 py-1.5 px-2 rounded-lg items-center gap-1'>
|
||||||
|
<CloseCircle onClick={() => handleRemove(index)} size={16} color='red' />
|
||||||
|
<div className='text-xs'>{item.name}</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-2
@@ -148,8 +148,20 @@
|
|||||||
"send_answer": "ارسال پاسخ",
|
"send_answer": "ارسال پاسخ",
|
||||||
"close_ticket": "بستن تیکت",
|
"close_ticket": "بستن تیکت",
|
||||||
"info_ticket": "اطلاعات تیکت",
|
"info_ticket": "اطلاعات تیکت",
|
||||||
"update_sms": "به روزرسانی این تیکت از طریق پیامک اطلاع داده میشود"
|
"update_sms": "به روزرسانی این تیکت از طریق پیامک اطلاع داده میشود",
|
||||||
|
"low": "پایین",
|
||||||
|
"medium": "متوسط",
|
||||||
|
"high": "بالا",
|
||||||
|
"category": "دسته بندی",
|
||||||
|
"asignto": "کارشناس پاسخگویی",
|
||||||
|
"MEDIUM": "متوسط",
|
||||||
|
"LOW": "پایین",
|
||||||
|
"HIGH": "بالا",
|
||||||
|
"PENDING": "در انتظار",
|
||||||
|
"ANSWERED": "پاسخ داده شده",
|
||||||
|
"CLOSED": "بسته شده"
|
||||||
},
|
},
|
||||||
|
"date": "تاریخ",
|
||||||
"receip": {
|
"receip": {
|
||||||
"receip_list": "لیست صورتحساب ها",
|
"receip_list": "لیست صورتحساب ها",
|
||||||
"dont_pay": "پرداخت نشده",
|
"dont_pay": "پرداخت نشده",
|
||||||
@@ -288,5 +300,6 @@
|
|||||||
"username_save_success": "نام کاربری با موفقیت ذخیره شد"
|
"username_save_success": "نام کاربری با موفقیت ذخیره شد"
|
||||||
},
|
},
|
||||||
"email": "ایمیل",
|
"email": "ایمیل",
|
||||||
"save": "ذخیره"
|
"save": "ذخیره",
|
||||||
|
"select": "انتخاب"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { FC } from 'react'
|
import { FC, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import Input from '../../components/Input'
|
import Input from '../../components/Input'
|
||||||
import Select from '../../components/Select'
|
import Select from '../../components/Select'
|
||||||
@@ -6,10 +6,77 @@ import Textarea from '../../components/Textarea'
|
|||||||
import UploadBox from '../../components/UploadBox'
|
import UploadBox from '../../components/UploadBox'
|
||||||
import Button from '../../components/Button'
|
import Button from '../../components/Button'
|
||||||
import { TickSquare } from 'iconsax-react'
|
import { 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 { ErrorType } from '../../helpers/types'
|
||||||
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
import { Pages } from '../../config/Pages'
|
||||||
|
|
||||||
const CreateTicket: FC = () => {
|
const CreateTicket: FC = () => {
|
||||||
|
|
||||||
const { t } = useTranslation('global')
|
const { t } = useTranslation('global')
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const [files, setFiles] = useState<File[]>([])
|
||||||
|
const getProfile = useGetProfile()
|
||||||
|
const getCategories = useGetCategoriesTicket()
|
||||||
|
const multiUpload = useMultiUpload()
|
||||||
|
const createTicket = useCreateTicket()
|
||||||
|
|
||||||
|
const formik = useFormik<CreateTicketType>({
|
||||||
|
initialValues: {
|
||||||
|
subject: '',
|
||||||
|
message: '',
|
||||||
|
categoryId: '',
|
||||||
|
priority: 'LOW',
|
||||||
|
danakServiceId: '',
|
||||||
|
title: '',
|
||||||
|
attachmentUrls: []
|
||||||
|
},
|
||||||
|
validationSchema: Yup.object({
|
||||||
|
subject: Yup.string().required(t('errors.required')),
|
||||||
|
message: Yup.string().required(t('errors.required')),
|
||||||
|
categoryId: Yup.string().required(t('errors.required')),
|
||||||
|
priority: Yup.string().required(t('errors.required')),
|
||||||
|
}),
|
||||||
|
onSubmit: async (values) => {
|
||||||
|
if (files.length > 0) {
|
||||||
|
const images = new FormData()
|
||||||
|
files.forEach(file => {
|
||||||
|
images.append('files', file)
|
||||||
|
})
|
||||||
|
await multiUpload.mutateAsync(images, {
|
||||||
|
onSuccess: async (data) => {
|
||||||
|
values.attachmentUrls = await data.data.map((item: { url: string }) => item?.url)
|
||||||
|
},
|
||||||
|
onError: (error: ErrorType) => {
|
||||||
|
toast.error(error.response?.data?.error?.message[0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
delete values.attachmentUrls
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
createTicket.mutate(values, {
|
||||||
|
onSuccess: (data) => {
|
||||||
|
toast.success(t('success'))
|
||||||
|
formik.resetForm()
|
||||||
|
navigate(Pages.ticket.detail + data.data?.ticketId)
|
||||||
|
},
|
||||||
|
onError: (error: ErrorType) => {
|
||||||
|
toast.error(error.response?.data?.error?.message[0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(formik.errors);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mt-4'>
|
<div className='mt-4'>
|
||||||
@@ -17,6 +84,10 @@ const CreateTicket: FC = () => {
|
|||||||
{t('ticket.new_ticket')}
|
{t('ticket.new_ticket')}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{
|
||||||
|
getProfile.isPending || getCategories.isPending ?
|
||||||
|
<PageLoading />
|
||||||
|
:
|
||||||
<div className='flex gap-6 xl:mt-8 mt-4'>
|
<div className='flex gap-6 xl:mt-8 mt-4'>
|
||||||
<div className='flex-1 bg-white py-8 xl:px-10 px-4 rounded-3xl'>
|
<div className='flex-1 bg-white py-8 xl:px-10 px-4 rounded-3xl'>
|
||||||
<div>
|
<div>
|
||||||
@@ -27,12 +98,12 @@ const CreateTicket: FC = () => {
|
|||||||
<Input
|
<Input
|
||||||
label={t('ticket.name')}
|
label={t('ticket.name')}
|
||||||
readOnly
|
readOnly
|
||||||
placeholder='مهرداد مظفری'
|
placeholder={getProfile?.data?.data?.user?.firstName + ' ' + getProfile?.data?.data?.user?.lastName}
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
label={t('ticket.email')}
|
label={t('ticket.email')}
|
||||||
readOnly
|
readOnly
|
||||||
placeholder='Info@example.com'
|
placeholder={getProfile?.data?.data?.user?.email}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -41,21 +112,37 @@ const CreateTicket: FC = () => {
|
|||||||
<Input
|
<Input
|
||||||
label={t('ticket.subject')}
|
label={t('ticket.subject')}
|
||||||
placeholder={t('ticket.enter_your_subject')}
|
placeholder={t('ticket.enter_your_subject')}
|
||||||
|
name='subject'
|
||||||
|
value={formik.values.subject}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
error_text={formik.touched.subject && formik.errors.subject ? formik.errors.subject : ''}
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
label={t('ticket.subject')}
|
label={t('ticket.priority')}
|
||||||
items={[
|
items={[
|
||||||
{
|
{
|
||||||
label: '',
|
label: t('ticket.low'),
|
||||||
value: ''
|
value: 'LOW'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('ticket.medium'),
|
||||||
|
value: 'MEDIUM'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('ticket.high'),
|
||||||
|
value: 'HIGH'
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
placeholder={t('ticket.priority')}
|
placeholder={t('select')}
|
||||||
className='border'
|
className='border'
|
||||||
|
name='priority'
|
||||||
|
value={formik.values.priority}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
error_text={formik.touched.priority && formik.errors.priority ? formik.errors.priority : ''}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-6'>
|
<div className='mt-6 rowTwoInput'>
|
||||||
<Select
|
<Select
|
||||||
label={t('ticket.select_your_service')}
|
label={t('ticket.select_your_service')}
|
||||||
items={[
|
items={[
|
||||||
@@ -66,18 +153,39 @@ const CreateTicket: FC = () => {
|
|||||||
]}
|
]}
|
||||||
placeholder={t('ticket.not_once')}
|
placeholder={t('ticket.not_once')}
|
||||||
className='border'
|
className='border'
|
||||||
|
name='danakServiceId'
|
||||||
|
/>
|
||||||
|
<Select
|
||||||
|
label={t('ticket.category')}
|
||||||
|
items={getCategories.data?.data?.ticketCategories?.map((item: { id: string, title: string }) => ({
|
||||||
|
label: item.title,
|
||||||
|
value: item.id
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
placeholder={t('select')}
|
||||||
|
className='border'
|
||||||
|
name='categoryId'
|
||||||
|
value={formik.values.categoryId}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
error_text={formik.touched.categoryId && formik.errors.categoryId ? formik.errors.categoryId : ''}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='mt-6'>
|
<div className='mt-6'>
|
||||||
<Textarea
|
<Textarea
|
||||||
label={t('description')}
|
label={t('description')}
|
||||||
placeholder={t('ticket.your_description')}
|
placeholder={t('ticket.your_description')}
|
||||||
|
name='message'
|
||||||
|
value={formik.values.message}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
error_text={formik.touched.message && formik.errors.message ? formik.errors.message : ''}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-6'>
|
<div className='mt-6'>
|
||||||
<UploadBox
|
<UploadBox
|
||||||
label={t('ticket.attachment')}
|
label={t('ticket.attachment')}
|
||||||
|
isMultiple
|
||||||
|
onChange={setFiles}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -85,6 +193,8 @@ const CreateTicket: FC = () => {
|
|||||||
<Button
|
<Button
|
||||||
label={t('ticket.send')}
|
label={t('ticket.send')}
|
||||||
className='max-w-[100px]'
|
className='max-w-[100px]'
|
||||||
|
onClick={() => formik.handleSubmit()}
|
||||||
|
isLoading={createTicket.isPending || multiUpload.isPending}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -122,6 +232,9 @@ const CreateTicket: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
+145
-22
@@ -1,14 +1,88 @@
|
|||||||
import { FC } from 'react'
|
import { FC, Fragment, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import Input from '../../components/Input'
|
import Input from '../../components/Input'
|
||||||
import Textarea from '../../components/Textarea'
|
import Textarea from '../../components/Textarea'
|
||||||
import UploadBox from '../../components/UploadBox'
|
import UploadBox from '../../components/UploadBox'
|
||||||
import Button from '../../components/Button'
|
import Button from '../../components/Button'
|
||||||
import { CloseCircle, InfoCircle } from 'iconsax-react'
|
import { CloseCircle, InfoCircle } from 'iconsax-react'
|
||||||
|
import { useNavigate, useParams } from 'react-router-dom'
|
||||||
|
import { useAddMessageTicket, useCloseTicket, useGetMessages, useMultiUpload } from './hooks/useTicketData'
|
||||||
|
import PageLoading from '../../components/PageLoading'
|
||||||
|
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 { ErrorType } from '../../helpers/types'
|
||||||
|
import { clx } from '../../helpers/utils'
|
||||||
|
import { Pages } from '../../config/Pages'
|
||||||
|
|
||||||
const TicketDetail: FC = () => {
|
const TicketDetail: FC = () => {
|
||||||
|
|
||||||
const { t } = useTranslation('global')
|
const { t } = useTranslation('global')
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const { id } = useParams()
|
||||||
|
const getMessages = useGetMessages(id ? id : '')
|
||||||
|
const [files, setFiles] = useState<File[]>([])
|
||||||
|
const addMessage = useAddMessageTicket(id ? id : '')
|
||||||
|
const multiUpload = useMultiUpload()
|
||||||
|
const [isResetFiles, setIsResetFiles] = useState<boolean>(false)
|
||||||
|
const closeTicket = useCloseTicket()
|
||||||
|
|
||||||
|
const formik = useFormik<AddMessageTicketType>({
|
||||||
|
initialValues: {
|
||||||
|
content: '',
|
||||||
|
attachmentUrls: [],
|
||||||
|
},
|
||||||
|
validationSchema: Yup.object().shape({
|
||||||
|
content: Yup.string().required(t('errors.required')),
|
||||||
|
}),
|
||||||
|
onSubmit: async (values) => {
|
||||||
|
if (files.length > 0) {
|
||||||
|
const images = new FormData()
|
||||||
|
files.forEach(file => {
|
||||||
|
images.append('files', file)
|
||||||
|
})
|
||||||
|
await multiUpload.mutateAsync(images, {
|
||||||
|
onSuccess: async (data) => {
|
||||||
|
values.attachmentUrls = await data.data.map((item: { url: string }) => item?.url)
|
||||||
|
},
|
||||||
|
onError: (error: ErrorType) => {
|
||||||
|
toast.error(error.response?.data?.error?.message[0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
delete values.attachmentUrls
|
||||||
|
}
|
||||||
|
|
||||||
|
addMessage.mutate(values, {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.success(t('success'))
|
||||||
|
formik.resetForm()
|
||||||
|
setFiles([])
|
||||||
|
setIsResetFiles(true)
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsResetFiles(false)
|
||||||
|
}, 1000);
|
||||||
|
},
|
||||||
|
onError: (error: ErrorType) => {
|
||||||
|
toast.error(error.response?.data?.error?.message[0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const hadleCloseTicket = () => {
|
||||||
|
closeTicket.mutate(id ? id : '', {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.success(t('success'))
|
||||||
|
navigate(Pages.ticket.list)
|
||||||
|
},
|
||||||
|
onError: (error: ErrorType) => {
|
||||||
|
toast.error(error.response?.data?.error?.message[0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mt-4'>
|
<div className='mt-4'>
|
||||||
@@ -16,6 +90,10 @@ const TicketDetail: FC = () => {
|
|||||||
<div>{t('ticket.ticket_number')}</div>
|
<div>{t('ticket.ticket_number')}</div>
|
||||||
<div>12312</div>
|
<div>12312</div>
|
||||||
</div>
|
</div>
|
||||||
|
{
|
||||||
|
getMessages.isPending ?
|
||||||
|
<PageLoading />
|
||||||
|
:
|
||||||
<div className='flex xl:flex-row flex-col-reverse gap-6 xl:mt-8 mt-6'>
|
<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='flex-1 bg-white py-8 xl:px-10 px-4 rounded-3xl'>
|
||||||
<div className='gap-6 rowTwoInput'>
|
<div className='gap-6 rowTwoInput'>
|
||||||
@@ -26,51 +104,73 @@ const TicketDetail: FC = () => {
|
|||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
label={t('ticket.user_name')}
|
label={t('ticket.user_name')}
|
||||||
value={'مهرداد مظفری'}
|
value={getMessages.data?.data?.ticket?.user?.firstName + ' ' + getMessages.data?.data?.ticket?.user?.lastName}
|
||||||
readOnly
|
readOnly
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-8 xl:text-sm text-xs'>
|
<div className='mt-8 xl:text-sm text-xs'>
|
||||||
موضوع درخواست پشتیبانی
|
{getMessages.data?.data?.ticket?.subject}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-6 xl:text-sm text-xs bg-[#F6F7FA] p-6 rounded-3xl rounded-tr-none xl:max-w-[70%] max-w-[90%]'>
|
{
|
||||||
|
getMessages.data?.data?.messages.map((item: any) => {
|
||||||
|
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'>
|
<div className='leading-7'>
|
||||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است، چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است، و برای شرایط فعلی تکنولوژی مورد نیاز، و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد، کتابهای زیادی در شصت و سه درصد گذشته حال و آینده، شناخت فراوان جامعه و متخصصان را می طلبد،
|
{item.content}
|
||||||
</div>
|
</div>
|
||||||
<div className='flex justify-end mt-6 text-xs text-description'>
|
<div className='flex dltr end mt-6 text-xs text-description'>
|
||||||
10:07 | 1403/09/30
|
{moment(item.createdAt).format('jYYYY-jMM-jDD HH:mm')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
<div className='w-full flex justify-end'>
|
<div className='w-full flex justify-end'>
|
||||||
<div className='mt-6 xl:text-sm text-xs bg-[#EBEDF5] p-6 rounded-3xl rounded-tl-none xl:max-w-[70%] max-w-[90%]'>
|
<div className='mt-6 xl:text-sm text-xs bg-[#EBEDF5] p-6 rounded-3xl rounded-tl-none xl:max-w-[70%] max-w-[90%]'>
|
||||||
<div className='flex gap-1'>
|
<div className='flex gap-1'>
|
||||||
<div className='font-bold'>
|
<div className='font-bold'>
|
||||||
{t('ticket.expert')}
|
{t('ticket.expert')}
|
||||||
</div>
|
</div>
|
||||||
<div>سیما فرهادی</div>
|
<div>{item.author?.firstName + ' ' + item?.author?.lastName}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='leading-7 mt-4'>
|
<div className='leading-7 mt-4'>
|
||||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است، چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است، و برای شرایط فعلی تکنولوژی مورد نیاز، و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد، کتابهای زیادی در شصت و سه درصد گذشته حال و آینده، شناخت فراوان جامعه و متخصصان را می طلبد،
|
{item.content}
|
||||||
</div>
|
</div>
|
||||||
<div className='flex justify-end mt-6 text-xs text-description'>
|
<div className='flex justify-end mt-6 text-xs text-description'>
|
||||||
10:07 | 1403/09/30
|
10:07 | 1403/09/30
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
getMessages.data?.data?.ticket?.status !== 'CLOSED' &&
|
||||||
|
<Fragment>
|
||||||
<div className='mt-9'>
|
<div className='mt-9'>
|
||||||
<Textarea
|
<Textarea
|
||||||
label={t('ticket.answer')}
|
label={t('ticket.answer')}
|
||||||
placeholder={t('ticket.your_description')}
|
placeholder={t('ticket.your_description')}
|
||||||
|
value={formik.values.content}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
name='content'
|
||||||
|
error_text={formik.touched.content && formik.errors.content ? formik.errors.content : ''}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-7'>
|
<div className='mt-7'>
|
||||||
<UploadBox
|
<UploadBox
|
||||||
label={t('ticket.attachment')}
|
label={t('ticket.attachment')}
|
||||||
|
onChange={setFiles}
|
||||||
|
isMultiple
|
||||||
|
isReset={isResetFiles}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -78,11 +178,17 @@ const TicketDetail: FC = () => {
|
|||||||
<Button
|
<Button
|
||||||
label={t('ticket.send')}
|
label={t('ticket.send')}
|
||||||
className='max-w-[100px]'
|
className='max-w-[100px]'
|
||||||
|
onClick={() => formik.handleSubmit()}
|
||||||
|
isLoading={addMessage.isPending || multiUpload.isPending}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</Fragment>
|
||||||
|
}
|
||||||
<div className='h-8'></div>
|
<div className='h-8'></div>
|
||||||
</div>
|
</div>
|
||||||
<div className={`h-fit xl:w-sidebar w-full`}>
|
<div className={`h-fit lg:sticky left-0 lg:top-0 xl:w-sidebar w-full`}>
|
||||||
|
{
|
||||||
|
getMessages.data?.data?.ticket?.status !== 'CLOSED' &&
|
||||||
<div className='bg-white xl:p-6 p-4 rounded-3xl flex gap-4'>
|
<div className='bg-white xl:p-6 p-4 rounded-3xl flex gap-4'>
|
||||||
<Button
|
<Button
|
||||||
label={t('ticket.send_answer')}
|
label={t('ticket.send_answer')}
|
||||||
@@ -90,6 +196,8 @@ const TicketDetail: FC = () => {
|
|||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
className='bg-[#D52903] xl:h-10 h-8'
|
className='bg-[#D52903] xl:h-10 h-8'
|
||||||
|
onClick={hadleCloseTicket}
|
||||||
|
isLoading={closeTicket.isPending}
|
||||||
>
|
>
|
||||||
<div className='flex gap-2 text-xs items-center'>
|
<div className='flex gap-2 text-xs items-center'>
|
||||||
<CloseCircle
|
<CloseCircle
|
||||||
@@ -102,13 +210,21 @@ const TicketDetail: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className='bg-white p-6 rounded-3xl mt-8'>
|
}
|
||||||
|
<div className={clx(
|
||||||
|
'bg-white p-6 rounded-3xl mt-8',
|
||||||
|
getMessages.data?.data?.ticket?.status === 'CLOSED' && 'mt-0'
|
||||||
|
)}>
|
||||||
<div className='flex justify-between'>
|
<div className='flex justify-between'>
|
||||||
<div className='text-sm'>
|
<div className='text-sm'>
|
||||||
{t('ticket.info_ticket')}
|
{t('ticket.info_ticket')}
|
||||||
</div>
|
</div>
|
||||||
<div className='h-7 px-3 rounded-xl flex items-center text-xs bg-green-100 text-green-400'>
|
<div className={clx(
|
||||||
{t('ticket.answered')}
|
'h-7 px-3 rounded-xl flex items-center text-xs bg-green-100 text-green-400',
|
||||||
|
getMessages.data?.data?.ticket?.status === 'PENDING' && 'bg-orange-100 text-orange-700',
|
||||||
|
getMessages.data?.data?.ticket?.status === 'CLOSED' && 'bg-red-100 text-red-400'
|
||||||
|
)}>
|
||||||
|
{t(`ticket.${getMessages.data?.data?.ticket?.status}`)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -116,25 +232,31 @@ const TicketDetail: FC = () => {
|
|||||||
<div className='flex justify-between text-description'>
|
<div className='flex justify-between text-description'>
|
||||||
<div>{t('ticket.ticket_number')}</div>
|
<div>{t('ticket.ticket_number')}</div>
|
||||||
<div>
|
<div>
|
||||||
۱۲۳۴۵۵
|
{getMessages.data?.data?.ticket?.numericId}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex mt-6 justify-between text-description'>
|
<div className='flex mt-6 justify-between text-description'>
|
||||||
<div>{t('ticket.ticket_number')}</div>
|
<div>{t('ticket.asignto')}</div>
|
||||||
<div>
|
<div>
|
||||||
۱۲۳۴۵۵
|
{getMessages.data?.data?.ticket?.assignedTo ? getMessages.data?.data?.ticket?.assignedTo?.firstName + ' ' + getMessages.data?.data?.ticket?.assignedTo?.lastName : ''}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex mt-6 justify-between text-description'>
|
<div className='flex mt-6 justify-between text-description'>
|
||||||
<div>{t('ticket.ticket_number')}</div>
|
<div>{t('date')}</div>
|
||||||
<div>
|
<div className='dltr'>
|
||||||
۱۲۳۴۵۵
|
{moment(getMessages.data?.data?.ticket?.createdAt).format('jYYYY/jMM/jDD HH:mm')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex mt-6 justify-between text-description'>
|
<div className='flex mt-6 justify-between text-description'>
|
||||||
<div>{t('ticket.ticket_number')}</div>
|
<div>{t('ticket.category')}</div>
|
||||||
<div>
|
<div>
|
||||||
۱۲۳۴۵۵
|
{getMessages.data?.data?.ticket?.category?.title}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='flex mt-6 justify-between text-description'>
|
||||||
|
<div>{t('ticket.priority')}</div>
|
||||||
|
<div>
|
||||||
|
{getMessages.data?.data?.ticket?.priority === 'low' ? t('ticket.low') : getMessages.data?.data?.ticket?.priority === 'medium' ? t('ticket.medium') : t('ticket.high')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -149,6 +271,7 @@ const TicketDetail: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
import { FC, useState } from 'react'
|
import { FC, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import Button from '../../components/Button'
|
import Button from '../../components/Button'
|
||||||
import { Add, Eye, Message2, MessageRemove, MessageTick, MessageTime } from 'iconsax-react'
|
import { Add, Eye, MessageRemove, MessageTick, MessageTime } from 'iconsax-react'
|
||||||
import Tabs from '../../components/Tabs'
|
import Tabs from '../../components/Tabs'
|
||||||
import Td from '../../components/Td'
|
import Td from '../../components/Td'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { Pages } from '../../config/Pages'
|
import { Pages } from '../../config/Pages'
|
||||||
|
import { useGetTickets } from './hooks/useTicketData'
|
||||||
|
import moment from 'moment-jalaali'
|
||||||
|
|
||||||
const TicketList: FC = () => {
|
const TicketList: FC = () => {
|
||||||
|
|
||||||
const { t } = useTranslation('global')
|
const { t } = useTranslation('global')
|
||||||
const [activeTab, setActiveTab] = useState<string>('answered')
|
const [activeTab, setActiveTab] = useState<'ANSWERED' | 'PENDING' | 'CLOSED'>('ANSWERED')
|
||||||
|
const getTickets = useGetTickets(activeTab)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mt-4'>
|
<div className='mt-4'>
|
||||||
@@ -38,27 +41,27 @@ const TicketList: FC = () => {
|
|||||||
active={activeTab}
|
active={activeTab}
|
||||||
items={[
|
items={[
|
||||||
{
|
{
|
||||||
icon: <MessageTick color={activeTab === 'answered' ? 'black' : '#8C90A3'} size={22} />,
|
icon: <MessageTick color={activeTab === 'ANSWERED' ? 'black' : '#8C90A3'} size={22} />,
|
||||||
label: t('ticket.answered'),
|
label: t('ticket.answered'),
|
||||||
value: 'answered'
|
value: 'ANSWERED'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <MessageTime color={activeTab === 'checking' ? 'black' : '#8C90A3'} size={22} />,
|
icon: <MessageTime color={activeTab === 'PENDING' ? 'black' : '#8C90A3'} size={22} />,
|
||||||
label: t('ticket.checking'),
|
label: t('ticket.checking'),
|
||||||
value: 'checking'
|
value: 'PENDING'
|
||||||
},
|
},
|
||||||
|
// {
|
||||||
|
// icon: <Message2 color={activeTab === 'wating' ? 'black' : '#8C90A3'} size={22} />,
|
||||||
|
// label: t('ticket.wait_answered'),
|
||||||
|
// value: 'wating'
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
icon: <Message2 color={activeTab === 'wating' ? 'black' : '#8C90A3'} size={22} />,
|
icon: <MessageRemove color={activeTab === 'CLOSED' ? 'black' : '#8C90A3'} size={22} />,
|
||||||
label: t('ticket.wait_answered'),
|
|
||||||
value: 'wating'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: <MessageRemove color={activeTab === 'closed' ? 'black' : '#8C90A3'} size={22} />,
|
|
||||||
label: t('ticket.closed'),
|
label: t('ticket.closed'),
|
||||||
value: 'closed'
|
value: 'CLOSED'
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
onChange={setActiveTab}
|
onChange={(value) => setActiveTab(value as 'ANSWERED' | 'PENDING' | 'CLOSED')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -77,34 +80,28 @@ const TicketList: FC = () => {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr className='tr'>
|
{
|
||||||
<Td text={t('ticket.number')} />
|
getTickets.data?.data?.tickets?.map((item: any) => (
|
||||||
<Td text={t('ticket.title')} />
|
<tr key={item.id} className='tr'>
|
||||||
<Td text={t('ticket.team')} />
|
<Td text={item.numericId} />
|
||||||
<Td text={t('ticket.date')} />
|
<Td text={item.subject} />
|
||||||
<Td text={t('ticket.status')} />
|
<Td text={item?.category?.title} />
|
||||||
<Td text={t('ticket.priority')} />
|
|
||||||
<Td text={''}>
|
<Td text={''}>
|
||||||
<Link to={Pages.ticket.detail + '1'}>
|
<div className='dltr text-right'>
|
||||||
<Eye size={20} color='#8C90A3' />
|
{moment(item.updatedAt).format('jYYYY-jMM-jDD HH:mm')}
|
||||||
</Link>
|
</div>
|
||||||
</Td>
|
</Td>
|
||||||
<Td text={''} />
|
<Td text={t(`ticket.${item.status}`)} />
|
||||||
</tr>
|
<Td text={t(`ticket.${item.priority}`)} />
|
||||||
<tr className='tr'>
|
<Td text={''}>
|
||||||
<Td text={t('ticket.number')} />
|
<Link to={Pages.ticket.detail + item.id}>
|
||||||
<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={''}>
|
|
||||||
<Link to={Pages.ticket.detail + '1'}>
|
|
||||||
<Eye size={20} color='#8C90A3' />
|
<Eye size={20} color='#8C90A3' />
|
||||||
</Link>
|
</Link>
|
||||||
</Td>
|
</Td>
|
||||||
<Td text={''} />
|
<Td text={''} />
|
||||||
</tr>
|
</tr>
|
||||||
|
))
|
||||||
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import * as api from "../service/TicketService";
|
||||||
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
|
import { AddMessageTicketType, CreateTicketType } from "../types/TicketTypes";
|
||||||
|
|
||||||
|
export const useGetTickets = (status: string) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["tickets", status],
|
||||||
|
queryFn: () => api.getTickets(status),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useCreateTicket = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (variables: CreateTicketType) => api.createTicket(variables),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGetCategoriesTicket = () => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["category-tickets"],
|
||||||
|
queryFn: () => api.getCategoriesTicket(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSingleUpload = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (variables: FormData) => api.uploadSingle(variables),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const useMultiUpload = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (variables: FormData) => api.uploadMultiple(variables),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGetMessages = (id: string) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["ticket-messages", id],
|
||||||
|
queryFn: () => api.getMessages(id),
|
||||||
|
enabled: !!id,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAddMessageTicket = (id: string) => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (variables: AddMessageTicketType) =>
|
||||||
|
api.addMessageTicket(id, variables),
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.refetchQueries({
|
||||||
|
queryKey: ["ticket-messages", id],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useCloseTicket = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (id: string) => api.closeTicket(id),
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
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}`);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createTicket = async (params: CreateTicketType) => {
|
||||||
|
const { data } = await axios.post(`/tickets`, params);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCategoriesTicket = async () => {
|
||||||
|
const { data } = await axios.get(`/tickets/categories`);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const uploadSingle = async (params: FormData) => {
|
||||||
|
const { data } = await axios.post(`/uploader/single-file`, params);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const uploadMultiple = async (params: FormData) => {
|
||||||
|
const { data } = await axios.post(`/uploader/multi-file`, params);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getMessages = async (id: string) => {
|
||||||
|
const { data } = await axios.get(`/tickets/${id}/messages`);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const addMessageTicket = async (
|
||||||
|
id: string,
|
||||||
|
params: AddMessageTicketType
|
||||||
|
) => {
|
||||||
|
const { data } = await axios.post(`/tickets/${id}/messages`, params);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const closeTicket = async (id: string) => {
|
||||||
|
const { data } = await axios.post(`/tickets/${id}/close`);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
export type CreateTicketType = {
|
||||||
|
title: string;
|
||||||
|
subject: string;
|
||||||
|
priority: "LOW" | "MEDIUM" | "HIGH";
|
||||||
|
danakServiceId?: string;
|
||||||
|
categoryId: string;
|
||||||
|
message: string;
|
||||||
|
attachmentUrls?: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AddMessageTicketType = {
|
||||||
|
content: string;
|
||||||
|
attachmentUrls?: string[];
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user