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,111 +84,157 @@ const CreateTicket: FC = () => {
|
|||||||
{t('ticket.new_ticket')}
|
{t('ticket.new_ticket')}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex gap-6 xl:mt-8 mt-4'>
|
{
|
||||||
<div className='flex-1 bg-white py-8 xl:px-10 px-4 rounded-3xl'>
|
getProfile.isPending || getCategories.isPending ?
|
||||||
<div>
|
<PageLoading />
|
||||||
{t('ticket.submit_your_message')}
|
:
|
||||||
</div>
|
<div className='flex gap-6 xl:mt-8 mt-4'>
|
||||||
|
<div className='flex-1 bg-white py-8 xl:px-10 px-4 rounded-3xl'>
|
||||||
<div className='mt-6 rowTwoInput'>
|
|
||||||
<Input
|
|
||||||
label={t('ticket.name')}
|
|
||||||
readOnly
|
|
||||||
placeholder='مهرداد مظفری'
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
label={t('ticket.email')}
|
|
||||||
readOnly
|
|
||||||
placeholder='Info@example.com'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div className='mt-6 rowTwoInput'>
|
|
||||||
<Input
|
|
||||||
label={t('ticket.subject')}
|
|
||||||
placeholder={t('ticket.enter_your_subject')}
|
|
||||||
/>
|
|
||||||
<Select
|
|
||||||
label={t('ticket.subject')}
|
|
||||||
items={[
|
|
||||||
{
|
|
||||||
label: '',
|
|
||||||
value: ''
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
placeholder={t('ticket.priority')}
|
|
||||||
className='border'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-6'>
|
|
||||||
<Select
|
|
||||||
label={t('ticket.select_your_service')}
|
|
||||||
items={[
|
|
||||||
{
|
|
||||||
label: '',
|
|
||||||
value: ''
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
placeholder={t('ticket.not_once')}
|
|
||||||
className='border'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className='mt-6'>
|
|
||||||
<Textarea
|
|
||||||
label={t('description')}
|
|
||||||
placeholder={t('ticket.your_description')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-6'>
|
|
||||||
<UploadBox
|
|
||||||
label={t('ticket.attachment')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-8 flex justify-end'>
|
|
||||||
<Button
|
|
||||||
label={t('ticket.send')}
|
|
||||||
className='max-w-[100px]'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='bg-white w-sidebar xl:block hidden py-10 px-5 h-fit rounded-3xl'>
|
|
||||||
<div className='text-sm'>
|
|
||||||
{t('ticket.title_hint')}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-6'>
|
|
||||||
<div className='flex items-start gap-2 border-b pb-5'>
|
|
||||||
<div>
|
<div>
|
||||||
<TickSquare size={20} color='black' variant='Bold' />
|
{t('ticket.submit_your_message')}
|
||||||
</div>
|
</div>
|
||||||
<div className='text-description text-xs leading-5'>
|
|
||||||
سوالات - مشکلاتی که به یک موضوع مربوط میشوند را در یک درخواست پشتیبانی پیگیر باشید و چند درخواست برای یک موضوع باز نکنید.
|
<div className='mt-6 rowTwoInput'>
|
||||||
|
<Input
|
||||||
|
label={t('ticket.name')}
|
||||||
|
readOnly
|
||||||
|
placeholder={getProfile?.data?.data?.user?.firstName + ' ' + getProfile?.data?.data?.user?.lastName}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
label={t('ticket.email')}
|
||||||
|
readOnly
|
||||||
|
placeholder={getProfile?.data?.data?.user?.email}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div className='mt-6 rowTwoInput'>
|
||||||
|
<Input
|
||||||
|
label={t('ticket.subject')}
|
||||||
|
placeholder={t('ticket.enter_your_subject')}
|
||||||
|
name='subject'
|
||||||
|
value={formik.values.subject}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
error_text={formik.touched.subject && formik.errors.subject ? formik.errors.subject : ''}
|
||||||
|
/>
|
||||||
|
<Select
|
||||||
|
label={t('ticket.priority')}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
label: t('ticket.low'),
|
||||||
|
value: 'LOW'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('ticket.medium'),
|
||||||
|
value: 'MEDIUM'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('ticket.high'),
|
||||||
|
value: 'HIGH'
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
placeholder={t('select')}
|
||||||
|
className='border'
|
||||||
|
name='priority'
|
||||||
|
value={formik.values.priority}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
error_text={formik.touched.priority && formik.errors.priority ? formik.errors.priority : ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='mt-6 rowTwoInput'>
|
||||||
|
<Select
|
||||||
|
label={t('ticket.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 }) => ({
|
||||||
|
label: item.title,
|
||||||
|
value: item.id
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
placeholder={t('select')}
|
||||||
|
className='border'
|
||||||
|
name='categoryId'
|
||||||
|
value={formik.values.categoryId}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
error_text={formik.touched.categoryId && formik.errors.categoryId ? formik.errors.categoryId : ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='mt-6'>
|
||||||
|
<Textarea
|
||||||
|
label={t('description')}
|
||||||
|
placeholder={t('ticket.your_description')}
|
||||||
|
name='message'
|
||||||
|
value={formik.values.message}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
error_text={formik.touched.message && formik.errors.message ? formik.errors.message : ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='mt-6'>
|
||||||
|
<UploadBox
|
||||||
|
label={t('ticket.attachment')}
|
||||||
|
isMultiple
|
||||||
|
onChange={setFiles}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='mt-8 flex justify-end'>
|
||||||
|
<Button
|
||||||
|
label={t('ticket.send')}
|
||||||
|
className='max-w-[100px]'
|
||||||
|
onClick={() => formik.handleSubmit()}
|
||||||
|
isLoading={createTicket.isPending || multiUpload.isPending}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-start gap-2 mt-6 border-b pb-5'>
|
|
||||||
<div>
|
<div className='bg-white w-sidebar xl:block hidden py-10 px-5 h-fit rounded-3xl'>
|
||||||
<TickSquare size={20} color='black' variant='Bold' />
|
<div className='text-sm'>
|
||||||
|
{t('ticket.title_hint')}
|
||||||
</div>
|
</div>
|
||||||
<div className='text-description text-xs leading-5'>
|
|
||||||
لطفاً برای بررسی و رفع مشکلات احتمالی صبور باشید بررسی و رفع مشکلات در برخی موارد زمان گیر است.
|
<div className='mt-6'>
|
||||||
</div>
|
<div className='flex items-start gap-2 border-b pb-5'>
|
||||||
</div>
|
<div>
|
||||||
<div className='flex items-start gap-2 mt-6'>
|
<TickSquare size={20} color='black' variant='Bold' />
|
||||||
<div>
|
</div>
|
||||||
<TickSquare size={20} color='black' variant='Bold' />
|
<div className='text-description text-xs leading-5'>
|
||||||
</div>
|
سوالات - مشکلاتی که به یک موضوع مربوط میشوند را در یک درخواست پشتیبانی پیگیر باشید و چند درخواست برای یک موضوع باز نکنید.
|
||||||
<div className='text-description text-xs leading-5'>
|
</div>
|
||||||
پاسخگویی 24 ساعته تلفنی را تنها از میهن وب هاست می توانید انتظار داشته باشید .بخش پشتیبانی در هر ساعتی حتی در روز های تعطیل آماده پیگیری سریع مشکلات کاربران است.
|
</div>
|
||||||
|
<div className='flex items-start gap-2 mt-6 border-b pb-5'>
|
||||||
|
<div>
|
||||||
|
<TickSquare size={20} color='black' variant='Bold' />
|
||||||
|
</div>
|
||||||
|
<div className='text-description text-xs leading-5'>
|
||||||
|
لطفاً برای بررسی و رفع مشکلات احتمالی صبور باشید بررسی و رفع مشکلات در برخی موارد زمان گیر است.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='flex items-start gap-2 mt-6'>
|
||||||
|
<div>
|
||||||
|
<TickSquare size={20} color='black' variant='Bold' />
|
||||||
|
</div>
|
||||||
|
<div className='text-description text-xs leading-5'>
|
||||||
|
پاسخگویی 24 ساعته تلفنی را تنها از میهن وب هاست می توانید انتظار داشته باشید .بخش پشتیبانی در هر ساعتی حتی در روز های تعطیل آماده پیگیری سریع مشکلات کاربران است.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
}
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
+247
-124
@@ -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,139 +90,188 @@ const TicketDetail: FC = () => {
|
|||||||
<div>{t('ticket.ticket_number')}</div>
|
<div>{t('ticket.ticket_number')}</div>
|
||||||
<div>12312</div>
|
<div>12312</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex xl:flex-row flex-col-reverse gap-6 xl:mt-8 mt-6'>
|
{
|
||||||
<div className='flex-1 bg-white py-8 xl:px-10 px-4 rounded-3xl'>
|
getMessages.isPending ?
|
||||||
<div className='gap-6 rowTwoInput'>
|
<PageLoading />
|
||||||
<Input
|
:
|
||||||
label={t('ticket.select_your_service')}
|
<div className='flex xl:flex-row flex-col-reverse gap-6 xl:mt-8 mt-6'>
|
||||||
value={'دی منو'}
|
<div className='flex-1 bg-white py-8 xl:px-10 px-4 rounded-3xl'>
|
||||||
readOnly
|
<div className='gap-6 rowTwoInput'>
|
||||||
/>
|
<Input
|
||||||
<Input
|
label={t('ticket.select_your_service')}
|
||||||
label={t('ticket.user_name')}
|
value={'دی منو'}
|
||||||
value={'مهرداد مظفری'}
|
readOnly
|
||||||
readOnly
|
/>
|
||||||
/>
|
<Input
|
||||||
</div>
|
label={t('ticket.user_name')}
|
||||||
|
value={getMessages.data?.data?.ticket?.user?.firstName + ' ' + getMessages.data?.data?.ticket?.user?.lastName}
|
||||||
<div className='mt-8 xl:text-sm text-xs'>
|
readOnly
|
||||||
موضوع درخواست پشتیبانی
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-6 xl:text-sm text-xs bg-[#F6F7FA] p-6 rounded-3xl rounded-tr-none xl:max-w-[70%] max-w-[90%]'>
|
|
||||||
<div className='leading-7'>
|
|
||||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است، چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است، و برای شرایط فعلی تکنولوژی مورد نیاز، و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد، کتابهای زیادی در شصت و سه درصد گذشته حال و آینده، شناخت فراوان جامعه و متخصصان را می طلبد،
|
|
||||||
</div>
|
|
||||||
<div className='flex justify-end mt-6 text-xs text-description'>
|
|
||||||
10:07 | 1403/09/30
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='w-full flex justify-end'>
|
|
||||||
<div className='mt-6 xl:text-sm text-xs bg-[#EBEDF5] p-6 rounded-3xl rounded-tl-none xl:max-w-[70%] max-w-[90%]'>
|
|
||||||
<div className='flex gap-1'>
|
|
||||||
<div className='font-bold'>
|
|
||||||
{t('ticket.expert')}
|
|
||||||
</div>
|
|
||||||
<div>سیما فرهادی</div>
|
|
||||||
</div>
|
|
||||||
<div className='leading-7 mt-4'>
|
|
||||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است، چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است، و برای شرایط فعلی تکنولوژی مورد نیاز، و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد، کتابهای زیادی در شصت و سه درصد گذشته حال و آینده، شناخت فراوان جامعه و متخصصان را می طلبد،
|
|
||||||
</div>
|
|
||||||
<div className='flex justify-end mt-6 text-xs text-description'>
|
|
||||||
10:07 | 1403/09/30
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-9'>
|
|
||||||
<Textarea
|
|
||||||
label={t('ticket.answer')}
|
|
||||||
placeholder={t('ticket.your_description')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-7'>
|
|
||||||
<UploadBox
|
|
||||||
label={t('ticket.attachment')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='flex justify-end mt-6'>
|
|
||||||
<Button
|
|
||||||
label={t('ticket.send')}
|
|
||||||
className='max-w-[100px]'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className='h-8'></div>
|
|
||||||
</div>
|
|
||||||
<div className={`h-fit xl:w-sidebar w-full`}>
|
|
||||||
<div className='bg-white xl:p-6 p-4 rounded-3xl flex gap-4'>
|
|
||||||
<Button
|
|
||||||
label={t('ticket.send_answer')}
|
|
||||||
className='text-xs xl:h-10 h-8'
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
className='bg-[#D52903] xl:h-10 h-8'
|
|
||||||
>
|
|
||||||
<div className='flex gap-2 text-xs items-center'>
|
|
||||||
<CloseCircle
|
|
||||||
color='white'
|
|
||||||
size={20}
|
|
||||||
/>
|
/>
|
||||||
<div>
|
|
||||||
{t('ticket.close_ticket')}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
|
||||||
</div>
|
<div className='mt-8 xl:text-sm text-xs'>
|
||||||
<div className='bg-white p-6 rounded-3xl mt-8'>
|
{getMessages.data?.data?.ticket?.subject}
|
||||||
<div className='flex justify-between'>
|
|
||||||
<div className='text-sm'>
|
|
||||||
{t('ticket.info_ticket')}
|
|
||||||
</div>
|
|
||||||
<div className='h-7 px-3 rounded-xl flex items-center text-xs bg-green-100 text-green-400'>
|
|
||||||
{t('ticket.answered')}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{
|
||||||
|
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'>
|
||||||
|
{item.content}
|
||||||
|
</div>
|
||||||
|
<div className='flex dltr end mt-6 text-xs text-description'>
|
||||||
|
{moment(item.createdAt).format('jYYYY-jMM-jDD HH:mm')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<div className='w-full flex justify-end'>
|
||||||
|
<div className='mt-6 xl:text-sm text-xs bg-[#EBEDF5] p-6 rounded-3xl rounded-tl-none xl:max-w-[70%] max-w-[90%]'>
|
||||||
|
<div className='flex gap-1'>
|
||||||
|
<div className='font-bold'>
|
||||||
|
{t('ticket.expert')}
|
||||||
|
</div>
|
||||||
|
<div>{item.author?.firstName + ' ' + item?.author?.lastName}</div>
|
||||||
|
</div>
|
||||||
|
<div className='leading-7 mt-4'>
|
||||||
|
{item.content}
|
||||||
|
</div>
|
||||||
|
<div className='flex justify-end mt-6 text-xs text-description'>
|
||||||
|
10:07 | 1403/09/30
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
getMessages.data?.data?.ticket?.status !== 'CLOSED' &&
|
||||||
|
<Fragment>
|
||||||
|
<div className='mt-9'>
|
||||||
|
<Textarea
|
||||||
|
label={t('ticket.answer')}
|
||||||
|
placeholder={t('ticket.your_description')}
|
||||||
|
value={formik.values.content}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
name='content'
|
||||||
|
error_text={formik.touched.content && formik.errors.content ? formik.errors.content : ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='mt-7'>
|
||||||
|
<UploadBox
|
||||||
|
label={t('ticket.attachment')}
|
||||||
|
onChange={setFiles}
|
||||||
|
isMultiple
|
||||||
|
isReset={isResetFiles}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='flex justify-end mt-6'>
|
||||||
|
<Button
|
||||||
|
label={t('ticket.send')}
|
||||||
|
className='max-w-[100px]'
|
||||||
|
onClick={() => formik.handleSubmit()}
|
||||||
|
isLoading={addMessage.isPending || multiUpload.isPending}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Fragment>
|
||||||
|
}
|
||||||
|
<div className='h-8'></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className={`h-fit lg:sticky left-0 lg:top-0 xl:w-sidebar w-full`}>
|
||||||
|
{
|
||||||
|
getMessages.data?.data?.ticket?.status !== 'CLOSED' &&
|
||||||
|
<div className='bg-white xl:p-6 p-4 rounded-3xl flex gap-4'>
|
||||||
|
<Button
|
||||||
|
label={t('ticket.send_answer')}
|
||||||
|
className='text-xs xl:h-10 h-8'
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
className='bg-[#D52903] xl:h-10 h-8'
|
||||||
|
onClick={hadleCloseTicket}
|
||||||
|
isLoading={closeTicket.isPending}
|
||||||
|
>
|
||||||
|
<div className='flex gap-2 text-xs items-center'>
|
||||||
|
<CloseCircle
|
||||||
|
color='white'
|
||||||
|
size={20}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
{t('ticket.close_ticket')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div className={clx(
|
||||||
|
'bg-white p-6 rounded-3xl mt-8',
|
||||||
|
getMessages.data?.data?.ticket?.status === 'CLOSED' && 'mt-0'
|
||||||
|
)}>
|
||||||
|
<div className='flex justify-between'>
|
||||||
|
<div className='text-sm'>
|
||||||
|
{t('ticket.info_ticket')}
|
||||||
|
</div>
|
||||||
|
<div className={clx(
|
||||||
|
'h-7 px-3 rounded-xl flex items-center text-xs bg-green-100 text-green-400',
|
||||||
|
getMessages.data?.data?.ticket?.status === 'PENDING' && 'bg-orange-100 text-orange-700',
|
||||||
|
getMessages.data?.data?.ticket?.status === 'CLOSED' && 'bg-red-100 text-red-400'
|
||||||
|
)}>
|
||||||
|
{t(`ticket.${getMessages.data?.data?.ticket?.status}`)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className='mt-14 text-xs w-full'>
|
<div className='mt-14 text-xs w-full'>
|
||||||
<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>
|
</div>
|
||||||
|
<div className='flex mt-6 justify-between text-description'>
|
||||||
|
<div>{t('ticket.priority')}</div>
|
||||||
|
<div>
|
||||||
|
{getMessages.data?.data?.ticket?.priority === 'low' ? t('ticket.low') : getMessages.data?.data?.ticket?.priority === 'medium' ? t('ticket.medium') : t('ticket.high')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-10 border-t text-xs pt-5 border-border items-center flex gap-2'>
|
<div className='mt-10 border-t text-xs pt-5 border-border items-center flex gap-2'>
|
||||||
<InfoCircle size={20} color='black' />
|
<InfoCircle size={20} color='black' />
|
||||||
<div>
|
<div>
|
||||||
{t('ticket.update_sms')}
|
{t('ticket.update_sms')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</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={''}>
|
<div className='dltr text-right'>
|
||||||
<Link to={Pages.ticket.detail + '1'}>
|
{moment(item.updatedAt).format('jYYYY-jMM-jDD HH:mm')}
|
||||||
<Eye size={20} color='#8C90A3' />
|
</div>
|
||||||
</Link>
|
</Td>
|
||||||
</Td>
|
<Td text={t(`ticket.${item.status}`)} />
|
||||||
<Td text={''} />
|
<Td text={t(`ticket.${item.priority}`)} />
|
||||||
</tr>
|
<Td text={''}>
|
||||||
<tr className='tr'>
|
<Link to={Pages.ticket.detail + item.id}>
|
||||||
<Td text={t('ticket.number')} />
|
<Eye size={20} color='#8C90A3' />
|
||||||
<Td text={t('ticket.title')} />
|
</Link>
|
||||||
<Td text={t('ticket.team')} />
|
</Td>
|
||||||
<Td text={t('ticket.date')} />
|
<Td text={''} />
|
||||||
<Td text={t('ticket.status')} />
|
</tr>
|
||||||
<Td text={t('ticket.priority')} />
|
))
|
||||||
<Td text={''}>
|
}
|
||||||
<Link to={Pages.ticket.detail + '1'}>
|
|
||||||
<Eye size={20} color='#8C90A3' />
|
|
||||||
</Link>
|
|
||||||
</Td>
|
|
||||||
<Td text={''} />
|
|
||||||
</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