Compare commits
9 Commits
7aa8d50cfd
...
1a78474120
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a78474120 | ||
|
|
b2ea58b19e | ||
|
|
4f944eab55 | ||
|
|
6e1e79465f | ||
|
|
084762e51e | ||
|
|
10af7490a1 | ||
|
|
c21796cfc1 | ||
|
|
f949c5eeb8 | ||
|
|
378d2fd7de |
|
|
@ -5,7 +5,7 @@
|
||||||
1. Clone the repository:
|
1. Clone the repository:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://git.point-dev.net/Karimaldeen/zaker.git
|
git clone https://git.point-dev.net/Karimaldeen/Quiz_dashboard.git
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
||||||
12
package.json
12
package.json
|
|
@ -4,28 +4,27 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^5.3.7",
|
"@ant-design/icons": "^5.3.7",
|
||||||
|
"@dnd-kit/core": "^6.1.0",
|
||||||
|
"@dnd-kit/modifiers": "^7.0.0",
|
||||||
|
"@dnd-kit/sortable": "^8.0.0",
|
||||||
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
"antd": "^5.17.4",
|
"antd": "^5.17.4",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
"bootstrap": "^5.3.3",
|
"bootstrap": "^5.3.3",
|
||||||
"dayjs": "^1.11.11",
|
"dayjs": "^1.11.11",
|
||||||
"formik": "^2.4.6",
|
"formik": "^2.4.6",
|
||||||
"html-to-image": "^1.11.11",
|
|
||||||
"i18next": "^23.11.5",
|
"i18next": "^23.11.5",
|
||||||
"path-to-regexp": "^6.2.2",
|
"lottie-react": "^2.4.0",
|
||||||
"pdf-lib": "^1.17.1",
|
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-beautiful-dnd": "^13.1.1",
|
"react-beautiful-dnd": "^13.1.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-i18next": "^13.5.0",
|
"react-i18next": "^13.5.0",
|
||||||
"react-icons": "^4.12.0",
|
"react-icons": "^4.12.0",
|
||||||
"react-mathjax": "^1.0.1",
|
|
||||||
"react-query": "^3.39.3",
|
"react-query": "^3.39.3",
|
||||||
"react-router-dom": "^6.23.1",
|
"react-router-dom": "^6.23.1",
|
||||||
"react-toastify": "^9.1.3",
|
"react-toastify": "^9.1.3",
|
||||||
"reactstrap": "^9.2.2",
|
"reactstrap": "^9.2.2",
|
||||||
"sass": "^1.77.4",
|
"sass": "^1.77.4",
|
||||||
"ts-node": "^10.9.2",
|
|
||||||
"vite-plugin-env-compatible": "^2.0.1",
|
|
||||||
"yup": "^1.4.0",
|
"yup": "^1.4.0",
|
||||||
"zustand": "^4.5.2"
|
"zustand": "^4.5.2"
|
||||||
},
|
},
|
||||||
|
|
@ -61,6 +60,7 @@
|
||||||
"@testing-library/user-event": "^13.5.0",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
"@types/node": "^20.14.0",
|
"@types/node": "^20.14.0",
|
||||||
"@types/react": "^18.3.3",
|
"@types/react": "^18.3.3",
|
||||||
|
"@types/react-beautiful-dnd": "^13.1.8",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
"@types/react-helmet": "^6.1.11",
|
"@types/react-helmet": "^6.1.11",
|
||||||
"@vitejs/plugin-legacy": "^5.4.1",
|
"@vitejs/plugin-legacy": "^5.4.1",
|
||||||
|
|
|
||||||
231
pnpm-lock.yaml
231
pnpm-lock.yaml
|
|
@ -11,6 +11,18 @@ importers:
|
||||||
'@ant-design/icons':
|
'@ant-design/icons':
|
||||||
specifier: ^5.3.7
|
specifier: ^5.3.7
|
||||||
version: 5.3.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
version: 5.3.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@dnd-kit/core':
|
||||||
|
specifier: ^6.1.0
|
||||||
|
version: 6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@dnd-kit/modifiers':
|
||||||
|
specifier: ^7.0.0
|
||||||
|
version: 7.0.0(@dnd-kit/core@6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
|
||||||
|
'@dnd-kit/sortable':
|
||||||
|
specifier: ^8.0.0
|
||||||
|
version: 8.0.0(@dnd-kit/core@6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
|
||||||
|
'@dnd-kit/utilities':
|
||||||
|
specifier: ^3.2.2
|
||||||
|
version: 3.2.2(react@18.3.1)
|
||||||
antd:
|
antd:
|
||||||
specifier: ^5.17.4
|
specifier: ^5.17.4
|
||||||
version: 5.17.4(date-fns@3.3.1)(luxon@3.4.4)(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
version: 5.17.4(date-fns@3.3.1)(luxon@3.4.4)(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
|
@ -26,18 +38,12 @@ importers:
|
||||||
formik:
|
formik:
|
||||||
specifier: ^2.4.6
|
specifier: ^2.4.6
|
||||||
version: 2.4.6(react@18.3.1)
|
version: 2.4.6(react@18.3.1)
|
||||||
html-to-image:
|
|
||||||
specifier: ^1.11.11
|
|
||||||
version: 1.11.11
|
|
||||||
i18next:
|
i18next:
|
||||||
specifier: ^23.11.5
|
specifier: ^23.11.5
|
||||||
version: 23.11.5
|
version: 23.11.5
|
||||||
path-to-regexp:
|
lottie-react:
|
||||||
specifier: ^6.2.2
|
specifier: ^2.4.0
|
||||||
version: 6.2.2
|
version: 2.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
pdf-lib:
|
|
||||||
specifier: ^1.17.1
|
|
||||||
version: 1.17.1
|
|
||||||
react:
|
react:
|
||||||
specifier: ^18.3.1
|
specifier: ^18.3.1
|
||||||
version: 18.3.1
|
version: 18.3.1
|
||||||
|
|
@ -53,9 +59,6 @@ importers:
|
||||||
react-icons:
|
react-icons:
|
||||||
specifier: ^4.12.0
|
specifier: ^4.12.0
|
||||||
version: 4.12.0(react@18.3.1)
|
version: 4.12.0(react@18.3.1)
|
||||||
react-mathjax:
|
|
||||||
specifier: ^1.0.1
|
|
||||||
version: 1.0.1(react@18.3.1)
|
|
||||||
react-query:
|
react-query:
|
||||||
specifier: ^3.39.3
|
specifier: ^3.39.3
|
||||||
version: 3.39.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
version: 3.39.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
|
@ -71,12 +74,6 @@ importers:
|
||||||
sass:
|
sass:
|
||||||
specifier: ^1.77.4
|
specifier: ^1.77.4
|
||||||
version: 1.77.4
|
version: 1.77.4
|
||||||
ts-node:
|
|
||||||
specifier: ^10.9.2
|
|
||||||
version: 10.9.2(@types/node@20.14.0)(typescript@4.9.5)
|
|
||||||
vite-plugin-env-compatible:
|
|
||||||
specifier: ^2.0.1
|
|
||||||
version: 2.0.1
|
|
||||||
yup:
|
yup:
|
||||||
specifier: ^1.4.0
|
specifier: ^1.4.0
|
||||||
version: 1.4.0
|
version: 1.4.0
|
||||||
|
|
@ -99,6 +96,9 @@ importers:
|
||||||
'@types/react':
|
'@types/react':
|
||||||
specifier: ^18.3.3
|
specifier: ^18.3.3
|
||||||
version: 18.3.3
|
version: 18.3.3
|
||||||
|
'@types/react-beautiful-dnd':
|
||||||
|
specifier: ^13.1.8
|
||||||
|
version: 13.1.8
|
||||||
'@types/react-dom':
|
'@types/react-dom':
|
||||||
specifier: ^18.3.0
|
specifier: ^18.3.0
|
||||||
version: 18.3.0
|
version: 18.3.0
|
||||||
|
|
@ -877,6 +877,34 @@ packages:
|
||||||
resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==}
|
resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==}
|
||||||
engines: {node: '>=10.0.0'}
|
engines: {node: '>=10.0.0'}
|
||||||
|
|
||||||
|
'@dnd-kit/accessibility@3.1.0':
|
||||||
|
resolution: {integrity: sha512-ea7IkhKvlJUv9iSHJOnxinBcoOI3ppGnnL+VDJ75O45Nss6HtZd8IdN8touXPDtASfeI2T2LImb8VOZcL47wjQ==}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=16.8.0'
|
||||||
|
|
||||||
|
'@dnd-kit/core@6.1.0':
|
||||||
|
resolution: {integrity: sha512-J3cQBClB4TVxwGo3KEjssGEXNJqGVWx17aRTZ1ob0FliR5IjYgTxl5YJbKTzA6IzrtelotH19v6y7uoIRUZPSg==}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=16.8.0'
|
||||||
|
react-dom: '>=16.8.0'
|
||||||
|
|
||||||
|
'@dnd-kit/modifiers@7.0.0':
|
||||||
|
resolution: {integrity: sha512-BG/ETy3eBjFap7+zIti53f0PCLGDzNXyTmn6fSdrudORf+OH04MxrW4p5+mPu4mgMk9kM41iYONjc3DOUWTcfg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@dnd-kit/core': ^6.1.0
|
||||||
|
react: '>=16.8.0'
|
||||||
|
|
||||||
|
'@dnd-kit/sortable@8.0.0':
|
||||||
|
resolution: {integrity: sha512-U3jk5ebVXe1Lr7c2wU7SBZjcWdQP+j7peHJfCspnA81enlu88Mgd7CC8Q+pub9ubP7eKVETzJW+IBAhsqbSu/g==}
|
||||||
|
peerDependencies:
|
||||||
|
'@dnd-kit/core': ^6.1.0
|
||||||
|
react: '>=16.8.0'
|
||||||
|
|
||||||
|
'@dnd-kit/utilities@3.2.2':
|
||||||
|
resolution: {integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=16.8.0'
|
||||||
|
|
||||||
'@emotion/hash@0.8.0':
|
'@emotion/hash@0.8.0':
|
||||||
resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==}
|
resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==}
|
||||||
|
|
||||||
|
|
@ -1122,12 +1150,6 @@ packages:
|
||||||
'@jridgewell/trace-mapping@0.3.9':
|
'@jridgewell/trace-mapping@0.3.9':
|
||||||
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
|
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
|
||||||
|
|
||||||
'@pdf-lib/standard-fonts@1.0.0':
|
|
||||||
resolution: {integrity: sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==}
|
|
||||||
|
|
||||||
'@pdf-lib/upng@1.0.1':
|
|
||||||
resolution: {integrity: sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==}
|
|
||||||
|
|
||||||
'@popperjs/core@2.11.8':
|
'@popperjs/core@2.11.8':
|
||||||
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
|
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
|
||||||
|
|
||||||
|
|
@ -1363,6 +1385,9 @@ packages:
|
||||||
'@types/prop-types@15.7.12':
|
'@types/prop-types@15.7.12':
|
||||||
resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==}
|
resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==}
|
||||||
|
|
||||||
|
'@types/react-beautiful-dnd@13.1.8':
|
||||||
|
resolution: {integrity: sha512-E3TyFsro9pQuK4r8S/OL6G99eq7p8v29sX0PM7oT8Z+PJfZvSQTx4zTQbUJ+QZXioAF0e7TGBEcA1XhYhCweyQ==}
|
||||||
|
|
||||||
'@types/react-dom@18.3.0':
|
'@types/react-dom@18.3.0':
|
||||||
resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==}
|
resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==}
|
||||||
|
|
||||||
|
|
@ -1883,13 +1908,6 @@ packages:
|
||||||
dom-helpers@5.2.1:
|
dom-helpers@5.2.1:
|
||||||
resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
|
resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
|
||||||
|
|
||||||
dotenv-expand@5.1.0:
|
|
||||||
resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==}
|
|
||||||
|
|
||||||
dotenv@8.2.0:
|
|
||||||
resolution: {integrity: sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==}
|
|
||||||
engines: {node: '>=8'}
|
|
||||||
|
|
||||||
electron-to-chromium@1.4.788:
|
electron-to-chromium@1.4.788:
|
||||||
resolution: {integrity: sha512-ubp5+Ev/VV8KuRoWnfP2QF2Bg+O2ZFdb49DiiNbz2VmgkIqrnyYaqIOqj8A6K/3p1xV0QcU5hBQ1+BmB6ot1OA==}
|
resolution: {integrity: sha512-ubp5+Ev/VV8KuRoWnfP2QF2Bg+O2ZFdb49DiiNbz2VmgkIqrnyYaqIOqj8A6K/3p1xV0QcU5hBQ1+BmB6ot1OA==}
|
||||||
|
|
||||||
|
|
@ -2139,9 +2157,6 @@ packages:
|
||||||
html-parse-stringify@3.0.1:
|
html-parse-stringify@3.0.1:
|
||||||
resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==}
|
resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==}
|
||||||
|
|
||||||
html-to-image@1.11.11:
|
|
||||||
resolution: {integrity: sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA==}
|
|
||||||
|
|
||||||
http-proxy-agent@7.0.2:
|
http-proxy-agent@7.0.2:
|
||||||
resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
|
resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
|
||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
|
|
@ -2535,9 +2550,6 @@ packages:
|
||||||
lines-and-columns@1.2.4:
|
lines-and-columns@1.2.4:
|
||||||
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
||||||
|
|
||||||
load-script@1.0.0:
|
|
||||||
resolution: {integrity: sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==}
|
|
||||||
|
|
||||||
loader-runner@4.3.0:
|
loader-runner@4.3.0:
|
||||||
resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==}
|
resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==}
|
||||||
engines: {node: '>=6.11.5'}
|
engines: {node: '>=6.11.5'}
|
||||||
|
|
@ -2562,6 +2574,15 @@ packages:
|
||||||
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
lottie-react@2.4.0:
|
||||||
|
resolution: {integrity: sha512-pDJGj+AQlnlyHvOHFK7vLdsDcvbuqvwPZdMlJ360wrzGFurXeKPr8SiRCjLf3LrNYKANQtSsh5dz9UYQHuqx4w==}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
|
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
|
|
||||||
|
lottie-web@5.12.2:
|
||||||
|
resolution: {integrity: sha512-uvhvYPC8kGPjXT3MyKMrL3JitEAmDMp30lVkuq/590Mw9ok6pWcFCwXJveo0t5uqYw1UREQHofD+jVpdjBv8wg==}
|
||||||
|
|
||||||
lru-cache@5.1.1:
|
lru-cache@5.1.1:
|
||||||
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
||||||
|
|
||||||
|
|
@ -2714,9 +2735,6 @@ packages:
|
||||||
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
|
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
pako@1.0.11:
|
|
||||||
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
|
|
||||||
|
|
||||||
parent-module@1.0.1:
|
parent-module@1.0.1:
|
||||||
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
@ -2743,16 +2761,10 @@ packages:
|
||||||
path-parse@1.0.7:
|
path-parse@1.0.7:
|
||||||
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
|
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
|
||||||
|
|
||||||
path-to-regexp@6.2.2:
|
|
||||||
resolution: {integrity: sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==}
|
|
||||||
|
|
||||||
path-type@4.0.0:
|
path-type@4.0.0:
|
||||||
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
|
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
pdf-lib@1.17.1:
|
|
||||||
resolution: {integrity: sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==}
|
|
||||||
|
|
||||||
picocolors@1.0.1:
|
picocolors@1.0.1:
|
||||||
resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
|
resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
|
||||||
|
|
||||||
|
|
@ -3098,11 +3110,6 @@ packages:
|
||||||
react-is@18.3.1:
|
react-is@18.3.1:
|
||||||
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
|
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
|
||||||
|
|
||||||
react-mathjax@1.0.1:
|
|
||||||
resolution: {integrity: sha512-+mjFcciZY3GQoqiQm3aRTyDjgBKuoaXpY+SCONX00jScuPpTKwnASeFMS5+pbTIzDf5zPT2Y9ZZfQ9U/d4CKtQ==}
|
|
||||||
peerDependencies:
|
|
||||||
react: ^16.3.0
|
|
||||||
|
|
||||||
react-popper@2.3.0:
|
react-popper@2.3.0:
|
||||||
resolution: {integrity: sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==}
|
resolution: {integrity: sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
|
@ -3540,9 +3547,6 @@ packages:
|
||||||
'@swc/wasm':
|
'@swc/wasm':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
tslib@1.14.1:
|
|
||||||
resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
|
|
||||||
|
|
||||||
tslib@2.6.2:
|
tslib@2.6.2:
|
||||||
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
|
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
|
||||||
|
|
||||||
|
|
@ -3633,9 +3637,6 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vite: '>=2.0.0'
|
vite: '>=2.0.0'
|
||||||
|
|
||||||
vite-plugin-env-compatible@2.0.1:
|
|
||||||
resolution: {integrity: sha512-DRrOZTg/W44ojVQQfGSMPEgYQGzp5TeIpt9cpaK35hTOC/b2D7Ffl8/RIgK8vQ0mlnDIUgETcA173bnMEkyzdw==}
|
|
||||||
|
|
||||||
vite@5.2.12:
|
vite@5.2.12:
|
||||||
resolution: {integrity: sha512-/gC8GxzxMK5ntBwb48pR32GGhENnjtY30G4A0jemunsBkiEZFw60s8InGpN8gkhHEkjnRK1aSAxeQgwvFhUHAA==}
|
resolution: {integrity: sha512-/gC8GxzxMK5ntBwb48pR32GGhENnjtY30G4A0jemunsBkiEZFw60s8InGpN8gkhHEkjnRK1aSAxeQgwvFhUHAA==}
|
||||||
engines: {node: ^18.0.0 || >=20.0.0}
|
engines: {node: ^18.0.0 || >=20.0.0}
|
||||||
|
|
@ -4764,11 +4765,44 @@ snapshots:
|
||||||
'@cspotcode/source-map-support@0.8.1':
|
'@cspotcode/source-map-support@0.8.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jridgewell/trace-mapping': 0.3.9
|
'@jridgewell/trace-mapping': 0.3.9
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@ctrl/tinycolor@3.6.1': {}
|
'@ctrl/tinycolor@3.6.1': {}
|
||||||
|
|
||||||
'@discoveryjs/json-ext@0.5.7': {}
|
'@discoveryjs/json-ext@0.5.7': {}
|
||||||
|
|
||||||
|
'@dnd-kit/accessibility@3.1.0(react@18.3.1)':
|
||||||
|
dependencies:
|
||||||
|
react: 18.3.1
|
||||||
|
tslib: 2.6.2
|
||||||
|
|
||||||
|
'@dnd-kit/core@6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||||
|
dependencies:
|
||||||
|
'@dnd-kit/accessibility': 3.1.0(react@18.3.1)
|
||||||
|
'@dnd-kit/utilities': 3.2.2(react@18.3.1)
|
||||||
|
react: 18.3.1
|
||||||
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
|
tslib: 2.6.2
|
||||||
|
|
||||||
|
'@dnd-kit/modifiers@7.0.0(@dnd-kit/core@6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)':
|
||||||
|
dependencies:
|
||||||
|
'@dnd-kit/core': 6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@dnd-kit/utilities': 3.2.2(react@18.3.1)
|
||||||
|
react: 18.3.1
|
||||||
|
tslib: 2.6.2
|
||||||
|
|
||||||
|
'@dnd-kit/sortable@8.0.0(@dnd-kit/core@6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)':
|
||||||
|
dependencies:
|
||||||
|
'@dnd-kit/core': 6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@dnd-kit/utilities': 3.2.2(react@18.3.1)
|
||||||
|
react: 18.3.1
|
||||||
|
tslib: 2.6.2
|
||||||
|
|
||||||
|
'@dnd-kit/utilities@3.2.2(react@18.3.1)':
|
||||||
|
dependencies:
|
||||||
|
react: 18.3.1
|
||||||
|
tslib: 2.6.2
|
||||||
|
|
||||||
'@emotion/hash@0.8.0': {}
|
'@emotion/hash@0.8.0': {}
|
||||||
|
|
||||||
'@emotion/unitless@0.7.5': {}
|
'@emotion/unitless@0.7.5': {}
|
||||||
|
|
@ -5041,15 +5075,8 @@ snapshots:
|
||||||
'@jridgewell/trace-mapping@0.3.9':
|
'@jridgewell/trace-mapping@0.3.9':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jridgewell/resolve-uri': 3.1.2
|
'@jridgewell/resolve-uri': 3.1.2
|
||||||
'@jridgewell/sourcemap-codec': 1.4.15
|
'@jridgewell/sourcemap-codec': 1.5.0
|
||||||
|
optional: true
|
||||||
'@pdf-lib/standard-fonts@1.0.0':
|
|
||||||
dependencies:
|
|
||||||
pako: 1.0.11
|
|
||||||
|
|
||||||
'@pdf-lib/upng@1.0.1':
|
|
||||||
dependencies:
|
|
||||||
pako: 1.0.11
|
|
||||||
|
|
||||||
'@popperjs/core@2.11.8': {}
|
'@popperjs/core@2.11.8': {}
|
||||||
|
|
||||||
|
|
@ -5221,13 +5248,17 @@ snapshots:
|
||||||
'@babel/runtime': 7.24.6
|
'@babel/runtime': 7.24.6
|
||||||
'@testing-library/dom': 9.3.3
|
'@testing-library/dom': 9.3.3
|
||||||
|
|
||||||
'@tsconfig/node10@1.0.11': {}
|
'@tsconfig/node10@1.0.11':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@tsconfig/node12@1.0.11': {}
|
'@tsconfig/node12@1.0.11':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@tsconfig/node14@1.0.3': {}
|
'@tsconfig/node14@1.0.3':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@tsconfig/node16@1.0.4': {}
|
'@tsconfig/node16@1.0.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@types/aria-query@5.0.4': {}
|
'@types/aria-query@5.0.4': {}
|
||||||
|
|
||||||
|
|
@ -5299,6 +5330,10 @@ snapshots:
|
||||||
|
|
||||||
'@types/prop-types@15.7.12': {}
|
'@types/prop-types@15.7.12': {}
|
||||||
|
|
||||||
|
'@types/react-beautiful-dnd@13.1.8':
|
||||||
|
dependencies:
|
||||||
|
'@types/react': 18.3.3
|
||||||
|
|
||||||
'@types/react-dom@18.3.0':
|
'@types/react-dom@18.3.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/react': 18.3.3
|
'@types/react': 18.3.3
|
||||||
|
|
@ -5459,6 +5494,7 @@ snapshots:
|
||||||
acorn-walk@8.3.3:
|
acorn-walk@8.3.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn: 8.12.1
|
acorn: 8.12.1
|
||||||
|
optional: true
|
||||||
|
|
||||||
acorn@8.12.1: {}
|
acorn@8.12.1: {}
|
||||||
|
|
||||||
|
|
@ -5557,7 +5593,8 @@ snapshots:
|
||||||
normalize-path: 3.0.0
|
normalize-path: 3.0.0
|
||||||
picomatch: 2.3.1
|
picomatch: 2.3.1
|
||||||
|
|
||||||
arg@4.1.3: {}
|
arg@4.1.3:
|
||||||
|
optional: true
|
||||||
|
|
||||||
argparse@1.0.10:
|
argparse@1.0.10:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -5866,7 +5903,8 @@ snapshots:
|
||||||
- supports-color
|
- supports-color
|
||||||
- ts-node
|
- ts-node
|
||||||
|
|
||||||
create-require@1.1.1: {}
|
create-require@1.1.1:
|
||||||
|
optional: true
|
||||||
|
|
||||||
cross-spawn@7.0.3:
|
cross-spawn@7.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -5953,7 +5991,8 @@ snapshots:
|
||||||
|
|
||||||
diff-sequences@29.6.3: {}
|
diff-sequences@29.6.3: {}
|
||||||
|
|
||||||
diff@4.0.2: {}
|
diff@4.0.2:
|
||||||
|
optional: true
|
||||||
|
|
||||||
dom-accessibility-api@0.5.16: {}
|
dom-accessibility-api@0.5.16: {}
|
||||||
|
|
||||||
|
|
@ -5962,10 +6001,6 @@ snapshots:
|
||||||
'@babel/runtime': 7.24.6
|
'@babel/runtime': 7.24.6
|
||||||
csstype: 3.1.3
|
csstype: 3.1.3
|
||||||
|
|
||||||
dotenv-expand@5.1.0: {}
|
|
||||||
|
|
||||||
dotenv@8.2.0: {}
|
|
||||||
|
|
||||||
electron-to-chromium@1.4.788: {}
|
electron-to-chromium@1.4.788: {}
|
||||||
|
|
||||||
electron-to-chromium@1.5.5: {}
|
electron-to-chromium@1.5.5: {}
|
||||||
|
|
@ -6215,8 +6250,6 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
void-elements: 3.1.0
|
void-elements: 3.1.0
|
||||||
|
|
||||||
html-to-image@1.11.11: {}
|
|
||||||
|
|
||||||
http-proxy-agent@7.0.2:
|
http-proxy-agent@7.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
agent-base: 7.1.1
|
agent-base: 7.1.1
|
||||||
|
|
@ -6796,8 +6829,6 @@ snapshots:
|
||||||
|
|
||||||
lines-and-columns@1.2.4: {}
|
lines-and-columns@1.2.4: {}
|
||||||
|
|
||||||
load-script@1.0.0: {}
|
|
||||||
|
|
||||||
loader-runner@4.3.0: {}
|
loader-runner@4.3.0: {}
|
||||||
|
|
||||||
locate-path@5.0.0:
|
locate-path@5.0.0:
|
||||||
|
|
@ -6816,6 +6847,14 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
js-tokens: 4.0.0
|
js-tokens: 4.0.0
|
||||||
|
|
||||||
|
lottie-react@2.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||||
|
dependencies:
|
||||||
|
lottie-web: 5.12.2
|
||||||
|
react: 18.3.1
|
||||||
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
|
|
||||||
|
lottie-web@5.12.2: {}
|
||||||
|
|
||||||
lru-cache@5.1.1:
|
lru-cache@5.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
yallist: 3.1.1
|
yallist: 3.1.1
|
||||||
|
|
@ -6948,8 +6987,6 @@ snapshots:
|
||||||
|
|
||||||
p-try@2.2.0: {}
|
p-try@2.2.0: {}
|
||||||
|
|
||||||
pako@1.0.11: {}
|
|
||||||
|
|
||||||
parent-module@1.0.1:
|
parent-module@1.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
callsites: 3.1.0
|
callsites: 3.1.0
|
||||||
|
|
@ -6974,18 +7011,9 @@ snapshots:
|
||||||
|
|
||||||
path-parse@1.0.7: {}
|
path-parse@1.0.7: {}
|
||||||
|
|
||||||
path-to-regexp@6.2.2: {}
|
|
||||||
|
|
||||||
path-type@4.0.0:
|
path-type@4.0.0:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
pdf-lib@1.17.1:
|
|
||||||
dependencies:
|
|
||||||
'@pdf-lib/standard-fonts': 1.0.0
|
|
||||||
'@pdf-lib/upng': 1.0.1
|
|
||||||
pako: 1.0.11
|
|
||||||
tslib: 1.14.1
|
|
||||||
|
|
||||||
picocolors@1.0.1: {}
|
picocolors@1.0.1: {}
|
||||||
|
|
||||||
picomatch@2.3.1: {}
|
picomatch@2.3.1: {}
|
||||||
|
|
@ -7416,11 +7444,6 @@ snapshots:
|
||||||
|
|
||||||
react-is@18.3.1: {}
|
react-is@18.3.1: {}
|
||||||
|
|
||||||
react-mathjax@1.0.1(react@18.3.1):
|
|
||||||
dependencies:
|
|
||||||
load-script: 1.0.0
|
|
||||||
react: 18.3.1
|
|
||||||
|
|
||||||
react-popper@2.3.0(@popperjs/core@2.11.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
react-popper@2.3.0(@popperjs/core@2.11.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@popperjs/core': 2.11.8
|
'@popperjs/core': 2.11.8
|
||||||
|
|
@ -7855,8 +7878,7 @@ snapshots:
|
||||||
typescript: 4.9.5
|
typescript: 4.9.5
|
||||||
v8-compile-cache-lib: 3.0.1
|
v8-compile-cache-lib: 3.0.1
|
||||||
yn: 3.1.1
|
yn: 3.1.1
|
||||||
|
optional: true
|
||||||
tslib@1.14.1: {}
|
|
||||||
|
|
||||||
tslib@2.6.2: {}
|
tslib@2.6.2: {}
|
||||||
|
|
||||||
|
|
@ -7919,7 +7941,8 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
|
|
||||||
v8-compile-cache-lib@3.0.1: {}
|
v8-compile-cache-lib@3.0.1:
|
||||||
|
optional: true
|
||||||
|
|
||||||
v8-to-istanbul@9.2.0:
|
v8-to-istanbul@9.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -7936,11 +7959,6 @@ snapshots:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
vite-plugin-env-compatible@2.0.1:
|
|
||||||
dependencies:
|
|
||||||
dotenv: 8.2.0
|
|
||||||
dotenv-expand: 5.1.0
|
|
||||||
|
|
||||||
vite@5.2.12(@types/node@20.14.0)(sass@1.77.4)(terser@5.31.4):
|
vite@5.2.12(@types/node@20.14.0)(sass@1.77.4)(terser@5.31.4):
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild: 0.20.2
|
esbuild: 0.20.2
|
||||||
|
|
@ -8109,7 +8127,8 @@ snapshots:
|
||||||
y18n: 5.0.8
|
y18n: 5.0.8
|
||||||
yargs-parser: 21.1.1
|
yargs-parser: 21.1.1
|
||||||
|
|
||||||
yn@3.1.1: {}
|
yn@3.1.1:
|
||||||
|
optional: true
|
||||||
|
|
||||||
yocto-queue@0.1.0: {}
|
yocto-queue@0.1.0: {}
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 698 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 66 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.4 KiB |
3
public/Icon/QuestionIcon.svg
Normal file
3
public/Icon/QuestionIcon.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M9.41177 10.8602C9.60628 10.8602 9.77726 10.7862 9.92471 10.6381C10.0722 10.49 10.1462 10.3191 10.1468 10.1252C10.1475 9.9313 10.0734 9.76031 9.92471 9.61224C9.776 9.46416 9.60502 9.39012 9.41177 9.39012C9.21851 9.39012 9.04753 9.46416 8.89882 9.61224C8.75012 9.76031 8.67608 9.9313 8.67671 10.1252C8.67733 10.3191 8.75137 10.49 8.89882 10.6381C9.04628 10.7862 9.21726 10.8609 9.41177 10.8602ZM8.99576 8.20988H9.82777C9.85224 7.81522 9.91467 7.51812 10.0151 7.31859C10.1148 7.11906 10.3555 6.84047 10.7369 6.48282C11.1347 6.12141 11.4121 5.79828 11.5689 5.51341C11.7258 5.22981 11.8042 4.90384 11.8042 4.53553C11.8042 3.90181 11.5784 3.37381 11.1266 2.95153C10.6748 2.52863 10.1032 2.31718 9.41177 2.31718C8.8891 2.31718 8.42479 2.45835 8.01882 2.74071C7.61286 3.02306 7.30384 3.408 7.09177 3.89553L7.85506 4.23153C8.03263 3.86384 8.2491 3.58777 8.50447 3.4033C8.75984 3.21883 9.06227 3.1269 9.41177 3.12753C9.86102 3.12753 10.2334 3.26055 10.5289 3.52659C10.8238 3.79263 10.9713 4.13616 10.9713 4.55718C10.9713 4.81318 10.8998 5.0513 10.7567 5.27153C10.613 5.49177 10.3661 5.75686 10.016 6.06682C9.61945 6.41318 9.35028 6.73098 9.20847 7.02024C9.06667 7.30949 8.99576 7.70604 8.99576 8.20988ZM4.34447 13.1765C3.9109 13.1765 3.54918 13.0315 3.2593 12.7416C2.96941 12.4518 2.82416 12.09 2.82353 11.6565V1.52C2.82353 1.08706 2.96879 0.725335 3.2593 0.434825C3.54981 0.144316 3.91153 -0.000625423 4.34447 2.0284e-06H14.48C14.9129 2.0284e-06 15.2747 0.144943 15.5652 0.434825C15.8557 0.724708 16.0006 1.08643 16 1.52V11.6565C16 12.0894 15.8551 12.4508 15.5652 12.7407C15.2753 13.0306 14.9133 13.1758 14.4791 13.1765H4.34447ZM4.34447 12.2353H14.48C14.6243 12.2353 14.757 12.1751 14.8781 12.0546C14.9992 11.9341 15.0595 11.8014 15.0588 11.6565V1.52C15.0588 1.37569 14.9986 1.24298 14.8781 1.12188C14.7576 1.00079 14.6246 0.940551 14.4791 0.941178H4.34447C4.19953 0.941178 4.06651 1.00141 3.94541 1.12188C3.82432 1.24235 3.76408 1.37506 3.76471 1.52V11.6565C3.76471 11.8008 3.82494 11.9335 3.94541 12.0546C4.06588 12.1757 4.19859 12.2359 4.34353 12.2353M1.52 16C1.08706 16 0.725335 15.8551 0.434825 15.5652C0.144316 15.2753 -0.000625423 14.9136 2.0284e-06 14.48V3.40235H0.941178V14.48C0.941178 14.6243 1.00141 14.757 1.12188 14.8781C1.24235 14.9992 1.37506 15.0595 1.52 15.0588H12.5976V16H1.52Z" fill="#202C4B"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.4 KiB |
42
src/Components/CustomFields/ImageBoxField/ImageBoxField.scss
Normal file
42
src/Components/CustomFields/ImageBoxField/ImageBoxField.scss
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
.ImageBoxField{
|
||||||
|
|
||||||
|
.ImageBox{
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: max(1.5px,.1vw) dashed #a9c3f1;
|
||||||
|
margin-block: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
.ImageBoxIcon{
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.imagePreview{
|
||||||
|
max-width: 99%;
|
||||||
|
height: auto;
|
||||||
|
max-height: 99%;
|
||||||
|
object-fit: contain;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
.ImageHeader{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.ImageCancelIcon{
|
||||||
|
width: 16px !important;
|
||||||
|
height: 16px !important;
|
||||||
|
}
|
||||||
|
.ImageBoxIcon{
|
||||||
|
width: 20px !important;
|
||||||
|
height: 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
88
src/Components/CustomFields/ImageBoxField/ImageBoxField.tsx
Normal file
88
src/Components/CustomFields/ImageBoxField/ImageBoxField.tsx
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
import { useFormikContext } from "formik";
|
||||||
|
import { useState, useRef, useEffect } from "react";
|
||||||
|
import './ImageBoxField.scss';
|
||||||
|
import ImageIcon from "./ImageIcon";
|
||||||
|
import ImageCancelIcon from "./ImageCancelIcon";
|
||||||
|
import { getNestedValue } from "../../../utils/getNestedValue";
|
||||||
|
import { generateImagePreview } from "./generateImagePreview";
|
||||||
|
|
||||||
|
// Helper function to generate image preview from a File
|
||||||
|
|
||||||
|
|
||||||
|
const ImageBoxField = ({ name }: any) => {
|
||||||
|
const formik = useFormikContext<any>();
|
||||||
|
const value = getNestedValue(formik.values, name);
|
||||||
|
const [imagePreview, setImagePreview] = useState<string | null>(null);
|
||||||
|
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
||||||
|
console.log(formik.values);
|
||||||
|
|
||||||
|
console.log(value,name);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (value instanceof File) {
|
||||||
|
generateImagePreview(value, setImagePreview);
|
||||||
|
} else if (typeof value === 'string') {
|
||||||
|
setImagePreview(value);
|
||||||
|
} else {
|
||||||
|
setImagePreview(null);
|
||||||
|
}
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
const handleFileChange = (event: any) => {
|
||||||
|
const file = event.target.files[0];
|
||||||
|
if (file) {
|
||||||
|
generateImagePreview(file, setImagePreview);
|
||||||
|
formik.setFieldValue(name, file);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleButtonClick = () => {
|
||||||
|
const fileInput = fileInputRef.current;
|
||||||
|
if (fileInput) {
|
||||||
|
fileInput.click();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
setImagePreview("");
|
||||||
|
formik.setFieldValue(name, "");
|
||||||
|
|
||||||
|
if (fileInputRef.current) {
|
||||||
|
fileInputRef.current.value = "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="ImageBoxField">
|
||||||
|
<div className="ImageHeader">
|
||||||
|
{imagePreview ? (
|
||||||
|
<>
|
||||||
|
<ImageCancelIcon onClick={handleCancel} className="ImageCancelIcon" />
|
||||||
|
<ImageIcon onClick={handleButtonClick} className="ImageBoxIcon" />
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className="VisibleHidden">
|
||||||
|
hidden
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="ImageBox">
|
||||||
|
{imagePreview ? (
|
||||||
|
<img src={imagePreview} alt="Preview" className="imagePreview" />
|
||||||
|
) : (
|
||||||
|
<ImageIcon onClick={handleButtonClick} className="ImageBoxIcon" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
id={`file-input-${name}`}
|
||||||
|
type="file"
|
||||||
|
accept="image/png, image/jpeg, image/webp"
|
||||||
|
style={{ display: "none" }}
|
||||||
|
onChange={handleFileChange}
|
||||||
|
ref={fileInputRef}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ImageBoxField;
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface ImageCancelIconProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||||
|
}
|
||||||
|
|
||||||
|
const ImageCancelIcon: React.FC<ImageCancelIconProps> = (props) => {
|
||||||
|
return (
|
||||||
|
<div {...props}>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 14 14"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M7 5.44469L12.4447 0L14 1.55531L8.55531 7L14 12.4447L12.4436 14L6.9989 8.55531L1.55531 14L0 12.4436L5.44469 6.9989L0 1.55421L1.55531 0.00109986L7 5.44469Z"
|
||||||
|
fill="#515B73"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ImageCancelIcon;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
27
src/Components/CustomFields/ImageBoxField/ImageIcon.tsx
Normal file
27
src/Components/CustomFields/ImageBoxField/ImageIcon.tsx
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface ImageIconProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||||
|
}
|
||||||
|
|
||||||
|
const ImageIcon: React.FC<ImageIconProps> = (props) => {
|
||||||
|
return (
|
||||||
|
<div {...props}>
|
||||||
|
<svg
|
||||||
|
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M11.25 5.625C11.25 7.11684 10.6574 8.54758 9.60248 9.60248C8.54758 10.6574 7.11684 11.25 5.625 11.25C4.13316 11.25 2.70242 10.6574 1.64752 9.60248C0.592632 8.54758 0 7.11684 0 5.625C0 4.13316 0.592632 2.70242 1.64752 1.64752C2.70242 0.592632 4.13316 0 5.625 0C7.11684 0 8.54758 0.592632 9.60248 1.64752C10.6574 2.70242 11.25 4.13316 11.25 5.625ZM6.25 3.125C6.25 2.95924 6.18415 2.80027 6.06694 2.68306C5.94973 2.56585 5.79076 2.5 5.625 2.5C5.45924 2.5 5.30027 2.56585 5.18306 2.68306C5.06585 2.80027 5 2.95924 5 3.125V5H3.125C2.95924 5 2.80027 5.06585 2.68306 5.18306C2.56585 5.30027 2.5 5.45924 2.5 5.625C2.5 5.79076 2.56585 5.94973 2.68306 6.06694C2.80027 6.18415 2.95924 6.25 3.125 6.25H5V8.125C5 8.29076 5.06585 8.44973 5.18306 8.56694C5.30027 8.68415 5.45924 8.75 5.625 8.75C5.79076 8.75 5.94973 8.68415 6.06694 8.56694C6.18415 8.44973 6.25 8.29076 6.25 8.125V6.25H8.125C8.29076 6.25 8.44973 6.18415 8.56694 6.06694C8.68415 5.94973 8.75 5.79076 8.75 5.625C8.75 5.45924 8.68415 5.30027 8.56694 5.18306C8.44973 5.06585 8.29076 5 8.125 5H6.25V3.125ZM16.25 3.75H12.2413C12.1187 3.3183 11.9542 2.89964 11.75 2.5H16.25C17.2446 2.5 18.1984 2.89509 18.9017 3.59835C19.6049 4.30161 20 5.25544 20 6.25V16.25C20 17.2446 19.6049 18.1984 18.9017 18.9017C18.1984 19.6049 17.2446 20 16.25 20H6.25C5.25544 20 4.30161 19.6049 3.59835 18.9017C2.89509 18.1984 2.5 17.2446 2.5 16.25V11.75C2.89667 11.9533 3.31333 12.1171 3.75 12.2413V16.25C3.75 16.7162 3.8775 17.1525 4.1 17.525L9.93625 11.79C10.2869 11.4457 10.7586 11.2528 11.25 11.2528C11.7414 11.2528 12.2131 11.4457 12.5637 11.79L18.4012 17.525C18.6298 17.139 18.7502 16.6986 18.75 16.25V6.25C18.75 5.58696 18.4866 4.95107 18.0178 4.48223C17.5489 4.01339 16.913 3.75 16.25 3.75ZM16.25 8.125C16.25 8.37123 16.2015 8.61505 16.1073 8.84253C16.013 9.07002 15.8749 9.27672 15.7008 9.45083C15.5267 9.62494 15.32 9.76305 15.0925 9.85727C14.865 9.9515 14.6212 10 14.375 10C14.1288 10 13.885 9.9515 13.6575 9.85727C13.43 9.76305 13.2233 9.62494 13.0492 9.45083C12.8751 9.27672 12.737 9.07002 12.6427 8.84253C12.5485 8.61505 12.5 8.37123 12.5 8.125C12.5 7.62772 12.6975 7.15081 13.0492 6.79917C13.4008 6.44754 13.8777 6.25 14.375 6.25C14.8723 6.25 15.3492 6.44754 15.7008 6.79917C16.0525 7.15081 16.25 7.62772 16.25 8.125ZM15 8.125C15 7.95924 14.9342 7.80027 14.8169 7.68306C14.6997 7.56585 14.5408 7.5 14.375 7.5C14.2092 7.5 14.0503 7.56585 13.9331 7.68306C13.8158 7.80027 13.75 7.95924 13.75 8.125C13.75 8.29076 13.8158 8.44973 13.9331 8.56694C14.0503 8.68415 14.2092 8.75 14.375 8.75C14.5408 8.75 14.6997 8.68415 14.8169 8.56694C14.9342 8.44973 15 8.29076 15 8.125ZM4.985 18.4075C5.36871 18.6321 5.80538 18.7504 6.25 18.75H16.25C16.7125 18.75 17.1437 18.625 17.515 18.4075L11.6875 12.6825C11.5707 12.568 11.4136 12.5038 11.25 12.5038C11.0864 12.5038 10.9293 12.568 10.8125 12.6825L4.985 18.4075Z"
|
||||||
|
fill="#515B73"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ImageIcon;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
export const generateImagePreview = (file: File, setImagePreview: (result: string) => void) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onloadend = () => {
|
||||||
|
setImagePreview(reader.result as string);
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
};
|
||||||
78
src/Components/CustomFields/SelectTag.tsx
Normal file
78
src/Components/CustomFields/SelectTag.tsx
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
import React, { useState, useMemo } from 'react';
|
||||||
|
import { Select, Spin } from 'antd';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useDebounce } from '../../utils/useDebounce';
|
||||||
|
import { useGetAllTag } from '../../api/tags';
|
||||||
|
import { useFormikContext } from 'formik';
|
||||||
|
|
||||||
|
const SelectTag: React.FC = () => {
|
||||||
|
const [searchValue, setSearchValue] = useState<string>('');
|
||||||
|
const [fieldValue, setFieldValue] = useState<string>('');
|
||||||
|
const formik = useFormikContext<any>()
|
||||||
|
const handleChange = (value: string[]) => {
|
||||||
|
console.log(value);
|
||||||
|
|
||||||
|
formik.setFieldValue("tags",value)
|
||||||
|
setSearchValue('');
|
||||||
|
setFieldValue('');
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSearch = useDebounce((value: string) => {
|
||||||
|
setSearchValue(value);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleFieldChange = (value: string) => {
|
||||||
|
setFieldValue(value);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBlur = () => {
|
||||||
|
setSearchValue('');
|
||||||
|
setFieldValue('');
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data, isLoading } = useGetAllTag({
|
||||||
|
name: searchValue,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [t] = useTranslation();
|
||||||
|
|
||||||
|
const options = data?.data ?? []
|
||||||
|
const additionalData = options?.length < 1 && searchValue.length > 1 && !isLoading ? [{id:`${searchValue}`,name:searchValue}] :[];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='SelectTag'>
|
||||||
|
|
||||||
|
<label htmlFor="">
|
||||||
|
{t("models.tag")}
|
||||||
|
</label>
|
||||||
|
<Select
|
||||||
|
mode="multiple"
|
||||||
|
allowClear
|
||||||
|
style={{ width: '100%' ,height:"40px"}}
|
||||||
|
placeholder=""
|
||||||
|
fieldNames={{ label: 'name', value: 'id' }}
|
||||||
|
onChange={handleChange}
|
||||||
|
options={[...options,...additionalData]}
|
||||||
|
filterOption={false}
|
||||||
|
loading={isLoading}
|
||||||
|
notFoundContent={isLoading ? <Spin /> : t("practical.not_found")}
|
||||||
|
onSearch={(value) => {
|
||||||
|
handleSearch(value);
|
||||||
|
handleFieldChange(value);
|
||||||
|
}}
|
||||||
|
searchValue={fieldValue}
|
||||||
|
onDropdownVisibleChange={(open) => {
|
||||||
|
if (!open) {
|
||||||
|
handleBlur();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
value={formik?.values?.tags ?? []}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SelectTag;
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import Image from "../Ui/Image";
|
|
||||||
|
|
||||||
const AddedSuccessfully = () => {
|
|
||||||
return (
|
|
||||||
<div className="AddedSuccessfully">
|
|
||||||
<Image src="/DataState/successfully.png" />
|
|
||||||
<h1>تمّت إضافة الطالب بنجاح!</h1>
|
|
||||||
<p>تمّت إضافة الطالب،هل تريد إضافة طالب آخر ؟</p>
|
|
||||||
|
|
||||||
<div className="TowButton">
|
|
||||||
<button>إضافة طالب جديد</button>
|
|
||||||
<button>تخطّي</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AddedSuccessfully;
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import Image from "../Ui/Image";
|
|
||||||
|
|
||||||
const EmptyData = ({
|
|
||||||
header,
|
|
||||||
info,
|
|
||||||
loading,
|
|
||||||
}: {
|
|
||||||
info: string;
|
|
||||||
header: string;
|
|
||||||
loading: boolean;
|
|
||||||
}) => {
|
|
||||||
if (loading) {
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div className="EmptyData">
|
|
||||||
<Image src="/DataState/EmptyData.gif" />
|
|
||||||
<h1>{header}</h1>
|
|
||||||
<p>{info}</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default EmptyData;
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import Image from "../Ui/Image";
|
|
||||||
|
|
||||||
const Loading = () => {
|
|
||||||
return (
|
|
||||||
<div className="Loading">
|
|
||||||
<Image src="/DataState/loading.gif" />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Loading;
|
|
||||||
|
|
@ -62,7 +62,7 @@ const SearchField: React.FC<Props> = ({ placeholder, searchBy }) => {
|
||||||
return (
|
return (
|
||||||
<div className={`search-field ${isOpen ? "open" : ""}`}>
|
<div className={`search-field ${isOpen ? "open" : ""}`}>
|
||||||
<div className="search-header" onClick={handleToggleDropdown}>
|
<div className="search-header" onClick={handleToggleDropdown}>
|
||||||
<IoSearch className="search__icon" />
|
{/* <IoSearch className="search__icon" /> */}
|
||||||
<input
|
<input
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
type="text"
|
type="text"
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,14 @@ interface Props {
|
||||||
options: Option[];
|
options: Option[];
|
||||||
placeholder: string;
|
placeholder: string;
|
||||||
onSelect?: (option: Option) => void;
|
onSelect?: (option: Option) => void;
|
||||||
|
withIcon?:boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const SearchFieldWithSelect: React.FC<Props> = ({
|
const SearchFieldWithSelect: React.FC<Props> = ({
|
||||||
options,
|
options,
|
||||||
placeholder,
|
placeholder,
|
||||||
onSelect,
|
onSelect,
|
||||||
|
withIcon=false
|
||||||
}) => {
|
}) => {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [selectedOption, setSelectedOption] = useState<Option | null>(null);
|
const [selectedOption, setSelectedOption] = useState<Option | null>(null);
|
||||||
|
|
@ -58,17 +60,16 @@ const SearchFieldWithSelect: React.FC<Props> = ({
|
||||||
return (
|
return (
|
||||||
<div ref={node} className={`search-field ${isOpen ? "open" : ""}`}>
|
<div ref={node} className={`search-field ${isOpen ? "open" : ""}`}>
|
||||||
<div className="search-header" onClick={toggleDropdown}>
|
<div className="search-header" onClick={toggleDropdown}>
|
||||||
<IoSearch className="search__icon" />
|
{withIcon && <IoSearch className="search__icon" />}
|
||||||
|
|
||||||
|
{/* <p className="search__input_text">{placeholder}</p> */}
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="search__input search__input_text"
|
className="search__input search__input_text"
|
||||||
placeholder={selectedOption ? selectedOption.label : ""}
|
placeholder={selectedOption ? selectedOption.label : placeholder}
|
||||||
// placeholder={selectedOption ? selectedOption.label : placeholder}
|
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<p className="search__input_text">{placeholder}</p>
|
|
||||||
{/* <IoMdArrowDropdown className={`search_select_icon ${isOpen ? 'open' : ''}`} /> */}
|
|
||||||
</div>
|
</div>
|
||||||
{(isOpen || (searchTerm !== "" && filteredOptions.length > 0)) && (
|
{(isOpen || (searchTerm !== "" && filteredOptions.length > 0)) && (
|
||||||
<div className="search-options">
|
<div className="search-options">
|
||||||
|
|
@ -80,7 +81,7 @@ const SearchFieldWithSelect: React.FC<Props> = ({
|
||||||
onClick={() => handleOptionClick(option)}
|
onClick={() => handleOptionClick(option)}
|
||||||
>
|
>
|
||||||
<div>{option.label}</div>
|
<div>{option.label}</div>
|
||||||
<IoSearch className="search__icon" />
|
{/* {withIcon && <IoSearch className="search__icon" />} */}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
1
src/Components/Lottie/Loading/Loading.json
Normal file
1
src/Components/Lottie/Loading/Loading.json
Normal file
File diff suppressed because one or more lines are too long
13
src/Components/Lottie/Loading/LoadingLottie.tsx
Normal file
13
src/Components/Lottie/Loading/LoadingLottie.tsx
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import React from 'react';
|
||||||
|
import Lottie from 'lottie-react'; // Default import
|
||||||
|
import animationData from './Loading.json'; // Import your Lottie JSON animation
|
||||||
|
|
||||||
|
const LoadingLottie = () => {
|
||||||
|
return (
|
||||||
|
<div className="cc">
|
||||||
|
<Lottie animationData={animationData} loop={true} style={{ width: 300, height: 300 }} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LoadingLottie;
|
||||||
1
src/Components/Lottie/NotFound/NotFound.json
Normal file
1
src/Components/Lottie/NotFound/NotFound.json
Normal file
File diff suppressed because one or more lines are too long
13
src/Components/Lottie/NotFound/NotFoundLottie.tsx
Normal file
13
src/Components/Lottie/NotFound/NotFoundLottie.tsx
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import React from 'react';
|
||||||
|
import Lottie from 'lottie-react'; // Default import
|
||||||
|
import animationData from './NotFound.json'; // Import your Lottie JSON animation
|
||||||
|
|
||||||
|
const NotFoundLottie = () => {
|
||||||
|
return (
|
||||||
|
<div className="cc">
|
||||||
|
<Lottie animationData={animationData} loop={true} style={{ width: 300, height: 300 }} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NotFoundLottie;
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import { Spin } from "antd";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
loading: boolean;
|
|
||||||
children: React.ReactNode;
|
|
||||||
className?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const KarimSpinner: React.FC<Props> = ({ loading, className, children }) => {
|
|
||||||
return (
|
|
||||||
<div className={className ?? ""}>
|
|
||||||
{loading ? (
|
|
||||||
<div className="text-center">
|
|
||||||
<Spin />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
children
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default KarimSpinner;
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
// .SearchBar {
|
|
||||||
// // margin-top: 20px;
|
|
||||||
// .group {
|
|
||||||
// display: flex;
|
|
||||||
// align-items: center;
|
|
||||||
// position: relative;
|
|
||||||
// max-width: 350px;
|
|
||||||
// width: 350px;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .input {
|
|
||||||
// width: 100%;
|
|
||||||
// height: 40px;
|
|
||||||
// padding: 0 1rem;
|
|
||||||
// padding-left: 2.5rem;
|
|
||||||
// border-radius: 8px;
|
|
||||||
// outline: none;
|
|
||||||
// font-weight: 500;
|
|
||||||
// background: var(--bg);
|
|
||||||
// color: var(--text);
|
|
||||||
// border: none;
|
|
||||||
// box-shadow: 2px 2px 7px 0 var(--bg);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .input::placeholder {
|
|
||||||
// color: var(--subtext);
|
|
||||||
// opacity: 0.4;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .icon {
|
|
||||||
// position: absolute;
|
|
||||||
// left: 1rem;
|
|
||||||
// fill: var(--subtext);
|
|
||||||
// width: 1rem;
|
|
||||||
// height: 1rem;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
import React, { useState } from "react";
|
|
||||||
import "./SearchBar.scss";
|
|
||||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
|
||||||
const SearchBar = () => {
|
|
||||||
const [searchQuery, setSearchQuery] = useState("");
|
|
||||||
const [searchParams] = useSearchParams();
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const handleChange = (event: any) => {
|
|
||||||
const { value } = event.target;
|
|
||||||
setSearchQuery(value);
|
|
||||||
updateUrlParams(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateUrlParams = (value: any) => {
|
|
||||||
navigate(`?search=${value}`, { replace: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="SearchBar">
|
|
||||||
<div className="group">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
|
||||||
className="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium css-1b5stb0 icon"
|
|
||||||
focusable="false"
|
|
||||||
aria-hidden="true"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
data-testid="SearchIcon"
|
|
||||||
>
|
|
||||||
<path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"></path>
|
|
||||||
</svg>
|
|
||||||
<input
|
|
||||||
placeholder="Search Product...."
|
|
||||||
type="search"
|
|
||||||
className="input"
|
|
||||||
value={searchQuery}
|
|
||||||
onChange={handleChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SearchBar;
|
|
||||||
|
|
@ -6,17 +6,16 @@ import {
|
||||||
File,
|
File,
|
||||||
DataRange,
|
DataRange,
|
||||||
SelectField,
|
SelectField,
|
||||||
Default,
|
|
||||||
CheckboxField,
|
CheckboxField,
|
||||||
MaltyFile,
|
MaltyFile,
|
||||||
SearchField,
|
SearchField,
|
||||||
TextField,
|
TextField,
|
||||||
DropFile,
|
DropFile,
|
||||||
|
Default,
|
||||||
} from "./View";
|
} from "./View";
|
||||||
import { ValidationFieldProps, ValidationFieldType } from "./utils/types";
|
import { ValidationFieldProps, ValidationFieldType } from "./utils/types";
|
||||||
import LocalSearchField from "./View/LocalSearch";
|
import LocalSearchField from "./View/LocalSearch";
|
||||||
import NumberFormate from "./View/NumberFormate";
|
import NumberFormate from "./View/NumberFormate";
|
||||||
import NumberField from "./View/NumberField";
|
|
||||||
|
|
||||||
const components: { [key: string]: React.FC<any> } = {
|
const components: { [key: string]: React.FC<any> } = {
|
||||||
Select: SelectField,
|
Select: SelectField,
|
||||||
|
|
@ -31,12 +30,11 @@ const components: { [key: string]: React.FC<any> } = {
|
||||||
MaltyFile: MaltyFile,
|
MaltyFile: MaltyFile,
|
||||||
Checkbox: CheckboxField,
|
Checkbox: CheckboxField,
|
||||||
NumberFormate: NumberFormate,
|
NumberFormate: NumberFormate,
|
||||||
Number: NumberField,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const ValidationField: React.FC<ValidationFieldProps> = React.memo(
|
const ValidationField: React.FC<ValidationFieldProps> = React.memo(
|
||||||
({ type, ...otherProps }: any) => {
|
({ type = "text", ...otherProps }) => {
|
||||||
const Component = components[type as ValidationFieldType];
|
const Component = components[type ?? ("text" as ValidationFieldType)];
|
||||||
|
|
||||||
if (!Component) {
|
if (!Component) {
|
||||||
return <Default {...otherProps} />;
|
return <Default {...otherProps} />;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import useFormField from "../../../Hooks/useFormField";
|
import useFormField from "../../../Hooks/useFormField";
|
||||||
import { Checkbox, Form } from "antd";
|
import { Checkbox } from "antd";
|
||||||
import { getNestedValue } from "../utils/getNestedValue";
|
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
|
||||||
const CheckboxField = ({
|
const CheckboxField = ({
|
||||||
name,
|
name,
|
||||||
label,
|
label,
|
||||||
|
|
@ -17,12 +17,8 @@ const CheckboxField = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={Group ? "d-inline mt-3 Checkboxs" : ``}>
|
<div className={Group ? "d-inline mt-3 Checkbox" : ``}>
|
||||||
<Form.Item
|
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||||
hasFeedback
|
|
||||||
validateStatus={isError ? "error" : ""}
|
|
||||||
help={isError ? errorMsg : ""}
|
|
||||||
>
|
|
||||||
<Checkbox
|
<Checkbox
|
||||||
onChange={onChange || CheckboxhandleChange}
|
onChange={onChange || CheckboxhandleChange}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
|
|
@ -31,7 +27,7 @@ const CheckboxField = ({
|
||||||
>
|
>
|
||||||
{t(`input.${label ? label : name}`)}
|
{t(`input.${label ? label : name}`)}
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
</Form.Item>
|
</ValidationFieldContainer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ import { Form, DatePicker } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import useFormField from "../../../Hooks/useFormField";
|
import useFormField from "../../../Hooks/useFormField";
|
||||||
import { MdOutlineEdit } from "react-icons/md";
|
import { MdOutlineEdit } from "react-icons/md";
|
||||||
|
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
|
||||||
|
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
|
||||||
|
|
||||||
const { RangePicker } = DatePicker;
|
const { RangePicker } = DatePicker;
|
||||||
|
|
||||||
|
|
@ -24,28 +26,16 @@ const DataRange = ({
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className="ValidationField w-100 ">
|
<div className="ValidationField w-100 ">
|
||||||
{no_label ? (
|
<ValidationFieldLabel
|
||||||
<label htmlFor={name} className="text">
|
name={name}
|
||||||
<span>empty</span>
|
label={label}
|
||||||
</label>
|
label_icon={label_icon}
|
||||||
) : label_icon ? (
|
no_label={no_label}
|
||||||
<div className="LabelWithIcon">
|
placeholder={placeholder}
|
||||||
<label htmlFor={name} className="text">
|
t={t}
|
||||||
{t(`input.${label ? label : name}`)}
|
/>
|
||||||
</label>
|
|
||||||
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<label htmlFor={name} className="text">
|
|
||||||
{t(`input.${label ? label : name}`)}
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form.Item
|
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||||
hasFeedback
|
|
||||||
validateStatus={isError ? "error" : ""}
|
|
||||||
help={isError ? errorMsg : ""}
|
|
||||||
>
|
|
||||||
<RangePicker
|
<RangePicker
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
size="large"
|
size="large"
|
||||||
|
|
@ -55,8 +45,9 @@ const DataRange = ({
|
||||||
onChange={onChange || onCalendarChange}
|
onChange={onChange || onCalendarChange}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
defaultValue={formik.values[name]}
|
defaultValue={formik.values[name]}
|
||||||
|
id={name}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</ValidationFieldContainer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@ import React from "react";
|
||||||
import useFormField from "../../../Hooks/useFormField";
|
import useFormField from "../../../Hooks/useFormField";
|
||||||
import { MdOutlineEdit } from "react-icons/md";
|
import { MdOutlineEdit } from "react-icons/md";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
import { DateEnum } from "../../../enums/Date";
|
||||||
|
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
|
||||||
|
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
|
||||||
|
|
||||||
const Date = ({
|
const Date = ({
|
||||||
name,
|
name,
|
||||||
|
|
@ -21,34 +24,21 @@ const Date = ({
|
||||||
const FormikValue = formik.values[name];
|
const FormikValue = formik.values[name];
|
||||||
const onCalendarChange = (value: any) => {
|
const onCalendarChange = (value: any) => {
|
||||||
formik.setFieldValue(name, value);
|
formik.setFieldValue(name, value);
|
||||||
// console.log(value,"value ");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Formater = "YYYY/MM/DD";
|
const Formatter = [DateEnum?.FORMATE];
|
||||||
return (
|
return (
|
||||||
<div className="ValidationField w-100 ">
|
<div className="ValidationField w-100 ">
|
||||||
{no_label ? (
|
<ValidationFieldLabel
|
||||||
<label htmlFor={name} className="text">
|
name={name}
|
||||||
<span>empty</span>
|
label={label}
|
||||||
</label>
|
label_icon={label_icon}
|
||||||
) : label_icon ? (
|
no_label={no_label}
|
||||||
<div className="LabelWithIcon">
|
placeholder={placeholder}
|
||||||
<label htmlFor={name} className="text">
|
t={t}
|
||||||
{t(`input.${label ? label : name}`)}
|
/>
|
||||||
</label>
|
|
||||||
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<label htmlFor={name} className="text">
|
|
||||||
{t(`input.${label ? label : name}`)}
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form.Item
|
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||||
hasFeedback
|
|
||||||
validateStatus={isError ? "error" : ""}
|
|
||||||
help={isError ? errorMsg : ""}
|
|
||||||
>
|
|
||||||
<DatePicker
|
<DatePicker
|
||||||
picker={picker}
|
picker={picker}
|
||||||
placeholder={t(`input.${placeholder}`)}
|
placeholder={t(`input.${placeholder}`)}
|
||||||
|
|
@ -58,10 +48,11 @@ const Date = ({
|
||||||
size="large"
|
size="large"
|
||||||
onChange={onChange || onCalendarChange}
|
onChange={onChange || onCalendarChange}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
format={Formater}
|
format={Formatter}
|
||||||
|
id={name}
|
||||||
/>
|
/>
|
||||||
{/* <DatePicker onChange={onChange} /> */}
|
{/* <DatePicker onChange={onChange} /> */}
|
||||||
</Form.Item>
|
</ValidationFieldContainer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import { Form, Input } from "antd";
|
import { Input } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import useFormField from "../../../Hooks/useFormField";
|
import useFormField from "../../../Hooks/useFormField";
|
||||||
import { MdOutlineEdit } from "react-icons/md";
|
|
||||||
import { Field } from "formik";
|
import { Field } from "formik";
|
||||||
import { ValidationFieldPropsInput } from "../utils/types";
|
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
|
||||||
|
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
|
||||||
|
import { FieldProps } from "../utils/types";
|
||||||
|
|
||||||
const Default = ({
|
const Default = ({
|
||||||
name,
|
name,
|
||||||
|
|
@ -14,52 +15,34 @@ const Default = ({
|
||||||
type,
|
type,
|
||||||
no_label,
|
no_label,
|
||||||
label_icon,
|
label_icon,
|
||||||
label2,
|
|
||||||
...props
|
...props
|
||||||
}: ValidationFieldPropsInput) => {
|
}: any) => {
|
||||||
const { errorMsg, isError, t } = useFormField(name, props);
|
const { errorMsg, isError, t } = useFormField(name, props);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ValidationField w-100">
|
<div className="ValidationField w-100">
|
||||||
{label2 ? (
|
<ValidationFieldLabel
|
||||||
<label htmlFor={name} className="text">
|
name={name}
|
||||||
{label2}
|
label={label}
|
||||||
</label>
|
label_icon={label_icon}
|
||||||
) : no_label ? (
|
no_label={no_label}
|
||||||
<label htmlFor={name} className="text">
|
placeholder={placeholder}
|
||||||
<span>empty</span>
|
t={t}
|
||||||
</label>
|
/>
|
||||||
) : label_icon ? (
|
|
||||||
<div className="LabelWithIcon">
|
|
||||||
<label htmlFor={name} className="text">
|
|
||||||
{t(`input.${label ? label : name}`)}
|
|
||||||
</label>
|
|
||||||
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<label htmlFor={name} className="text">
|
|
||||||
{t(`input.${label ? label : placeholder ? placeholder : name}`)}
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form.Item
|
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||||
hasFeedback
|
|
||||||
validateStatus={isError ? "error" : ""}
|
|
||||||
help={isError ? errorMsg : ""}
|
|
||||||
>
|
|
||||||
<Field
|
<Field
|
||||||
as={Input}
|
as={Input}
|
||||||
type={type ?? "text"}
|
type={type ?? "text"}
|
||||||
placeholder={t(
|
placeholder={t(`input.${placeholder || label || name}`)}
|
||||||
`input.${placeholder ? placeholder : label ? label : name}`,
|
|
||||||
)}
|
|
||||||
name={name}
|
name={name}
|
||||||
|
id={name}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
size="large"
|
size="large"
|
||||||
{...(type === "number" && { min: 0 })}
|
{...(type === "number" && { min: 0 })}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</ValidationFieldContainer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import { LoadingOutlined, PlusOutlined } from "@ant-design/icons";
|
||||||
import { message, Upload } from "antd";
|
import { message, Upload } from "antd";
|
||||||
import type { GetProp, UploadProps } from "antd";
|
import type { GetProp, UploadProps } from "antd";
|
||||||
import useFormField from "../../../Hooks/useFormField";
|
import useFormField from "../../../Hooks/useFormField";
|
||||||
import { ImageBaseURL } from "../../../api/config";
|
|
||||||
|
|
||||||
type FileType = Parameters<GetProp<UploadProps, "beforeUpload">>[0];
|
type FileType = Parameters<GetProp<UploadProps, "beforeUpload">>[0];
|
||||||
|
|
||||||
|
|
@ -12,7 +11,7 @@ const DropFile = ({
|
||||||
label,
|
label,
|
||||||
onChange,
|
onChange,
|
||||||
isDisabled,
|
isDisabled,
|
||||||
placholder,
|
placeholder,
|
||||||
className,
|
className,
|
||||||
props,
|
props,
|
||||||
no_label,
|
no_label,
|
||||||
|
|
@ -23,7 +22,7 @@ const DropFile = ({
|
||||||
|
|
||||||
const FormikValue =
|
const FormikValue =
|
||||||
typeof FormikName === "string"
|
typeof FormikName === "string"
|
||||||
? ImageBaseURL + FormikName
|
? FormikName
|
||||||
: FormikName instanceof File
|
: FormikName instanceof File
|
||||||
? URL.createObjectURL(FormikName)
|
? URL.createObjectURL(FormikName)
|
||||||
: "";
|
: "";
|
||||||
|
|
@ -71,6 +70,7 @@ const DropFile = ({
|
||||||
showUploadList={false}
|
showUploadList={false}
|
||||||
customRequest={customRequest}
|
customRequest={customRequest}
|
||||||
onChange={onChange || handleChange}
|
onChange={onChange || handleChange}
|
||||||
|
id={name}
|
||||||
>
|
>
|
||||||
{imageUrl ? (
|
{imageUrl ? (
|
||||||
<img
|
<img
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Button, Upload, UploadFile } from "antd";
|
import { Button, Upload, UploadFile } from "antd";
|
||||||
import useFormField from "../../../Hooks/useFormField";
|
import useFormField from "../../../Hooks/useFormField";
|
||||||
import { UploadOutlined } from "@ant-design/icons";
|
import { UploadOutlined } from "@ant-design/icons";
|
||||||
import { useMemo } from "react";
|
import React, { useMemo } from "react";
|
||||||
|
|
||||||
const File = ({
|
const File = ({
|
||||||
name,
|
name,
|
||||||
|
|
@ -11,37 +11,34 @@ const File = ({
|
||||||
placeholder,
|
placeholder,
|
||||||
className,
|
className,
|
||||||
props,
|
props,
|
||||||
icon,
|
|
||||||
}: any) => {
|
}: any) => {
|
||||||
const { formik, t, isError, errorMsg } = useFormField(name, props);
|
const { formik, t, isError, errorMsg } = useFormField(name, props);
|
||||||
let imageUrl = formik?.values?.[name] ?? null;
|
let imageUrl = formik?.values?.[name] ?? null;
|
||||||
console.log(imageUrl);
|
|
||||||
console.log(typeof imageUrl === "string");
|
|
||||||
|
|
||||||
const fileList: UploadFile[] = useMemo(() => {
|
const fileList: UploadFile[] = useMemo(() => {
|
||||||
if (!imageUrl) return [];
|
if (!imageUrl) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
typeof imageUrl === "string"
|
typeof imageUrl === "string"
|
||||||
? {
|
? {
|
||||||
uid: "-1",
|
uid: "-1",
|
||||||
name: "uploaded-image",
|
name: "",
|
||||||
status: "done",
|
status: "done",
|
||||||
url: imageUrl,
|
url: imageUrl,
|
||||||
thumbUrl: imageUrl,
|
thumbUrl: imageUrl,
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
uid: imageUrl.uid || "-1",
|
uid: imageUrl.uid || "-1",
|
||||||
name: imageUrl.name || "uploaded-image",
|
name: imageUrl.name || "",
|
||||||
status: "done",
|
status: "done",
|
||||||
originFileObj: imageUrl,
|
originFileObj: imageUrl,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}, [imageUrl]);
|
}, [imageUrl]);
|
||||||
// console.log(1);
|
|
||||||
|
|
||||||
const FilehandleChange = (value: any) => {
|
const FilehandleChange = (value: any) => {
|
||||||
// console.log(value,"filevalue");
|
|
||||||
if (value.fileList.length === 0) {
|
if (value.fileList.length === 0) {
|
||||||
formik.setFieldValue(name, null);
|
formik.setFieldValue(name, null);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -65,12 +62,15 @@ const File = ({
|
||||||
onChange={onChange || FilehandleChange}
|
onChange={onChange || FilehandleChange}
|
||||||
customRequest={customRequest}
|
customRequest={customRequest}
|
||||||
className={` w-100`}
|
className={` w-100`}
|
||||||
|
id={name}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
className={isError ? "isError w-100 " : " w-100"}
|
className={isError ? "isError w-100 " : " w-100"}
|
||||||
icon={icon ? icon : <UploadOutlined />}
|
icon={<UploadOutlined />}
|
||||||
>
|
>
|
||||||
{placeholder ?? t("input.Click_to_upload_the_image")}
|
{placeholder
|
||||||
|
? t(`input.${placeholder}`)
|
||||||
|
: t("input.Click_to_upload_the_image")}
|
||||||
</Button>
|
</Button>
|
||||||
<div className="Error_color"> {isError ? "required" : ""}</div>
|
<div className="Error_color"> {isError ? "required" : ""}</div>
|
||||||
{errorMsg}
|
{errorMsg}
|
||||||
|
|
@ -79,4 +79,4 @@ const File = ({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default File;
|
export default React.memo(File);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import { Form, Select } from "antd";
|
import { Select } from "antd";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import useFormField from "../../../Hooks/useFormField";
|
import useFormField from "../../../Hooks/useFormField";
|
||||||
import { MdOutlineEdit } from "react-icons/md";
|
|
||||||
import { translateOptions } from "../utils/translatedOptions";
|
import { translateOptions } from "../utils/translatedOptions";
|
||||||
|
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
|
||||||
|
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
|
||||||
|
|
||||||
const LocalSelectField = ({
|
const LocalSelectField = ({
|
||||||
name,
|
name,
|
||||||
|
|
@ -13,9 +14,9 @@ const LocalSelectField = ({
|
||||||
isMulti,
|
isMulti,
|
||||||
onChange,
|
onChange,
|
||||||
className,
|
className,
|
||||||
props,
|
|
||||||
no_label,
|
no_label,
|
||||||
label_icon,
|
label_icon,
|
||||||
|
...props
|
||||||
}: any) => {
|
}: any) => {
|
||||||
const { errorMsg, isError, t, formik } = useFormField(name, props);
|
const { errorMsg, isError, t, formik } = useFormField(name, props);
|
||||||
|
|
||||||
|
|
@ -29,13 +30,6 @@ const LocalSelectField = ({
|
||||||
option?.label?.toString().toLowerCase().includes(input.toLowerCase()) ||
|
option?.label?.toString().toLowerCase().includes(input.toLowerCase()) ||
|
||||||
option?.value?.toString().toLowerCase().includes(input.toLowerCase());
|
option?.value?.toString().toLowerCase().includes(input.toLowerCase());
|
||||||
|
|
||||||
const SelectableChange = (value: {
|
|
||||||
value: string;
|
|
||||||
label: React.ReactNode;
|
|
||||||
}) => {
|
|
||||||
formik.setFieldValue(name, value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSelectChange = (value: any) => {
|
const handleSelectChange = (value: any) => {
|
||||||
formik.setFieldValue(name, value);
|
formik.setFieldValue(name, value);
|
||||||
if (onChange) onChange(value);
|
if (onChange) onChange(value);
|
||||||
|
|
@ -47,35 +41,21 @@ const LocalSelectField = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ValidationField w-100">
|
<div className="ValidationField w-100">
|
||||||
{no_label ? (
|
<ValidationFieldLabel
|
||||||
<label htmlFor={name} className="text">
|
name={name}
|
||||||
<span>empty</span>
|
label={label}
|
||||||
</label>
|
label_icon={label_icon}
|
||||||
) : label_icon ? (
|
no_label={no_label}
|
||||||
<div className="LabelWithIcon">
|
placeholder={placeholder}
|
||||||
<label htmlFor={name} className="text">
|
t={t}
|
||||||
{t(`input.${label ? label : name}`)}
|
/>
|
||||||
</label>
|
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||||
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<label htmlFor={name} className="text">
|
|
||||||
{t(`input.${label ? label : name}`)}
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
<Form.Item
|
|
||||||
hasFeedback
|
|
||||||
validateStatus={isError ? "error" : ""}
|
|
||||||
help={isError ? errorMsg : ""}
|
|
||||||
>
|
|
||||||
<Select
|
<Select
|
||||||
placeholder={t(
|
placeholder={t(`input.${placeholder || label || name}`)}
|
||||||
`input.${placeholder ? placeholder : label ? label : name}`,
|
|
||||||
)}
|
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
options={translateOptions(option, t)}
|
options={translateOptions(option, t)}
|
||||||
size="large"
|
size="large"
|
||||||
className={`${className} ${isError ? "Select_error" : ""} w-100`}
|
className={`${className} ${isError ? "SelectError" : ""} w-100`}
|
||||||
value={formik.values[name]}
|
value={formik.values[name]}
|
||||||
allowClear
|
allowClear
|
||||||
{...(isMulti && { mode: "multiple" })}
|
{...(isMulti && { mode: "multiple" })}
|
||||||
|
|
@ -84,8 +64,11 @@ const LocalSelectField = ({
|
||||||
filterOption={handleSearch} // Custom filter function
|
filterOption={handleSearch} // Custom filter function
|
||||||
searchValue={searchValue} // Control the search input value
|
searchValue={searchValue} // Control the search input value
|
||||||
onSearch={handleSearchChange} // Update search input value on change
|
onSearch={handleSearchChange} // Update search input value on change
|
||||||
|
id={name}
|
||||||
|
fieldNames={{ label: "name", value: "id" }}
|
||||||
|
{...props}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</ValidationFieldContainer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
|
import React, { useMemo } from "react";
|
||||||
import { Button, Upload } from "antd";
|
import { Button, Upload } from "antd";
|
||||||
import { UploadOutlined } from "@ant-design/icons";
|
import { UploadOutlined } from "@ant-design/icons";
|
||||||
|
|
||||||
import useFormField from "../../../Hooks/useFormField";
|
import useFormField from "../../../Hooks/useFormField";
|
||||||
|
|
||||||
const MaltyFile = ({
|
const MaltyFile = ({
|
||||||
|
|
@ -15,11 +15,10 @@ const MaltyFile = ({
|
||||||
const { formik, t, isError } = useFormField(name, props);
|
const { formik, t, isError } = useFormField(name, props);
|
||||||
let imageUrl = formik?.values?.[name] ?? null;
|
let imageUrl = formik?.values?.[name] ?? null;
|
||||||
|
|
||||||
// Mapping formik values to fileList format
|
// Memoizing the fileList to prevent unnecessary recalculations
|
||||||
const fileList = imageUrl
|
const fileList = useMemo(() => {
|
||||||
|
return imageUrl
|
||||||
? imageUrl.map((file: any, index: number) => {
|
? imageUrl.map((file: any, index: number) => {
|
||||||
// console.log(file,"file");
|
|
||||||
|
|
||||||
return file instanceof File
|
return file instanceof File
|
||||||
? {
|
? {
|
||||||
uid: index,
|
uid: index,
|
||||||
|
|
@ -37,6 +36,7 @@ const MaltyFile = ({
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
: [];
|
: [];
|
||||||
|
}, [imageUrl]); // Dependency array ensures it recalculates only when imageUrl changes
|
||||||
|
|
||||||
const FilehandleChange = ({ fileList }: any) => {
|
const FilehandleChange = ({ fileList }: any) => {
|
||||||
if (fileList.length === 0) {
|
if (fileList.length === 0) {
|
||||||
|
|
@ -48,6 +48,7 @@ const MaltyFile = ({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Custom request function
|
// Custom request function
|
||||||
const customRequest = async ({ onSuccess }: any) => {
|
const customRequest = async ({ onSuccess }: any) => {
|
||||||
// Perform any necessary actions before onSuccess is called
|
// Perform any necessary actions before onSuccess is called
|
||||||
|
|
@ -63,11 +64,12 @@ const MaltyFile = ({
|
||||||
<Upload
|
<Upload
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
listType="picture"
|
listType="picture"
|
||||||
fileList={fileList} // Using fileList instead of defaultFileList
|
fileList={fileList} // Using memoized fileList
|
||||||
onChange={onChange || FilehandleChange}
|
onChange={onChange || FilehandleChange}
|
||||||
customRequest={customRequest}
|
customRequest={customRequest}
|
||||||
className={`${className} w-100`}
|
className={`${className} w-100`}
|
||||||
multiple // Allow multiple files to be selected
|
multiple // Allow multiple files to be selected
|
||||||
|
id={name}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
className={isError ? "isError w-100" : "w-100"}
|
className={isError ? "isError w-100" : "w-100"}
|
||||||
|
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
import { Form, Input, InputNumber } from "antd";
|
|
||||||
import React from "react";
|
|
||||||
import useFormField from "../../../Hooks/useFormField";
|
|
||||||
import { MdOutlineEdit } from "react-icons/md";
|
|
||||||
import { Field } from "formik";
|
|
||||||
import { ValidationFieldPropsInput } from "../utils/types";
|
|
||||||
|
|
||||||
const NumberField = ({
|
|
||||||
name,
|
|
||||||
label,
|
|
||||||
placeholder,
|
|
||||||
isDisabled,
|
|
||||||
onChange,
|
|
||||||
type,
|
|
||||||
no_label,
|
|
||||||
label_icon,
|
|
||||||
...props
|
|
||||||
}: ValidationFieldPropsInput) => {
|
|
||||||
const { errorMsg, isError, t, formik } = useFormField(name, props);
|
|
||||||
|
|
||||||
const handleChange = (
|
|
||||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
|
||||||
) => {
|
|
||||||
console.log("Change:", e);
|
|
||||||
formik.setFieldValue(name, e);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="ValidationField w-100">
|
|
||||||
{no_label ? (
|
|
||||||
<label htmlFor={name} className="text">
|
|
||||||
<span>empty</span>
|
|
||||||
</label>
|
|
||||||
) : label_icon ? (
|
|
||||||
<div className="LabelWithIcon">
|
|
||||||
<label htmlFor={name} className="text">
|
|
||||||
{t(`input.${label ? label : name}`)}
|
|
||||||
</label>
|
|
||||||
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<label htmlFor={name} className="text">
|
|
||||||
{t(`input.${label ? label : placeholder ? placeholder : name}`)}
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form.Item
|
|
||||||
hasFeedback
|
|
||||||
validateStatus={isError ? "error" : ""}
|
|
||||||
help={isError ? errorMsg : ""}
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
as={InputNumber}
|
|
||||||
type={type ?? "text"}
|
|
||||||
placeholder={t(
|
|
||||||
`input.${placeholder ? placeholder : label ? label : name}`,
|
|
||||||
)}
|
|
||||||
name={name}
|
|
||||||
disabled={isDisabled}
|
|
||||||
size="large"
|
|
||||||
onChange={handleChange}
|
|
||||||
{...(type === "number" && { min: 0 })}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default React.memo(NumberField);
|
|
||||||
|
|
@ -1,18 +1,19 @@
|
||||||
import { Form, Input, InputNumber } from "antd";
|
import { InputNumber } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import useFormField from "../../../Hooks/useFormField";
|
import useFormField from "../../../Hooks/useFormField";
|
||||||
import { MdOutlineEdit } from "react-icons/md";
|
|
||||||
import { Field } from "formik";
|
import { Field } from "formik";
|
||||||
|
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
|
||||||
|
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
|
||||||
|
|
||||||
const NumberFormate = ({
|
const NumberFormate = ({
|
||||||
name,
|
name,
|
||||||
label,
|
label,
|
||||||
placeholder,
|
placeholder,
|
||||||
isDisabled,
|
isDisabled,
|
||||||
props,
|
|
||||||
type,
|
type,
|
||||||
no_label,
|
no_label,
|
||||||
label_icon,
|
label_icon,
|
||||||
|
...props
|
||||||
}: any) => {
|
}: any) => {
|
||||||
const { errorMsg, isError, t, formik } = useFormField(name, props);
|
const { errorMsg, isError, t, formik } = useFormField(name, props);
|
||||||
const SelectableChange = (value: {
|
const SelectableChange = (value: {
|
||||||
|
|
@ -23,28 +24,15 @@ const NumberFormate = ({
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className="ValidationField w-100">
|
<div className="ValidationField w-100">
|
||||||
{no_label ? (
|
<ValidationFieldLabel
|
||||||
<label htmlFor={name} className="text">
|
name={name}
|
||||||
<span>empty</span>
|
label={label}
|
||||||
</label>
|
label_icon={label_icon}
|
||||||
) : label_icon ? (
|
no_label={no_label}
|
||||||
<div className="LabelWithIcon">
|
placeholder={placeholder}
|
||||||
<label htmlFor={name} className="text">
|
t={t}
|
||||||
{t(`input.${label ? label : name}`)}
|
/>
|
||||||
</label>
|
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||||
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<label htmlFor={name} className="text">
|
|
||||||
{t(`input.${label ? label : placeholder ? placeholder : name}`)}
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form.Item
|
|
||||||
hasFeedback
|
|
||||||
validateStatus={isError ? "error" : ""}
|
|
||||||
help={isError ? errorMsg : ""}
|
|
||||||
>
|
|
||||||
<Field
|
<Field
|
||||||
as={InputNumber}
|
as={InputNumber}
|
||||||
formatter={(value: any) =>
|
formatter={(value: any) =>
|
||||||
|
|
@ -57,16 +45,16 @@ const NumberFormate = ({
|
||||||
type={type ?? "text"}
|
type={type ?? "text"}
|
||||||
value={formik.values[name]}
|
value={formik.values[name]}
|
||||||
onChange={SelectableChange}
|
onChange={SelectableChange}
|
||||||
placeholder={t(
|
placeholder={t(`input.${placeholder || label || name}`)}
|
||||||
`input.${placeholder ? placeholder : label ? label : name}`,
|
|
||||||
)}
|
|
||||||
name={name}
|
name={name}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
size="large"
|
size="large"
|
||||||
|
id={name}
|
||||||
|
{...props}
|
||||||
|
|
||||||
// onChange={onChange ? onChange : handleChange}
|
// onChange={onChange ? onChange : handleChange}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</ValidationFieldContainer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,87 +1,143 @@
|
||||||
import { Form, Select, Spin } from "antd";
|
import { Select, Spin } from "antd";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import useFormField from "../../../Hooks/useFormField";
|
import useFormField from "../../../Hooks/useFormField";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
|
||||||
import { MdOutlineEdit } from "react-icons/md";
|
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
|
||||||
|
import { SearchFieldProps } from "../utils/types";
|
||||||
|
import { useValidationValidationParamState } from "../state/ValidationValidationParamState";
|
||||||
|
import { useDebounce } from "../../../utils/useDebounce";
|
||||||
|
|
||||||
const SearchField = ({
|
const SearchField = ({
|
||||||
name,
|
name,
|
||||||
label,
|
label,
|
||||||
placeholder,
|
placeholder,
|
||||||
isDisabled,
|
isDisabled,
|
||||||
searchBy,
|
searchBy = "search",
|
||||||
option,
|
option = [],
|
||||||
isMulti,
|
isMulti,
|
||||||
onChange,
|
onChange,
|
||||||
className,
|
className,
|
||||||
props,
|
|
||||||
no_label,
|
no_label,
|
||||||
label_icon,
|
label_icon,
|
||||||
isLoading,
|
isLoading,
|
||||||
}: any) => {
|
canChangePage,
|
||||||
|
PageName,
|
||||||
|
page,
|
||||||
|
...props
|
||||||
|
}: SearchFieldProps) => {
|
||||||
const { errorMsg, isError, t, formik } = useFormField(name, props);
|
const { errorMsg, isError, t, formik } = useFormField(name, props);
|
||||||
const [searchQuery, setSearchQuery] = useState<string>("");
|
const { pushValidationParamState, setValidationParamState } =
|
||||||
const navigate = useNavigate();
|
useValidationValidationParamState();
|
||||||
useEffect(() => {
|
|
||||||
const searchParams = new URLSearchParams(window?.location?.search);
|
|
||||||
setSearchQuery(searchParams?.get("search") || "");
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const SelectableChange = (value: {
|
const [AllPagesOption, setAllPagesOption] = useState<any>([]);
|
||||||
value: string;
|
|
||||||
label: React.ReactNode;
|
useEffect(() => {
|
||||||
}) => {
|
if (option?.length > 0) {
|
||||||
|
const NewOption = [...option, ...AllPagesOption];
|
||||||
|
const FilteredOption = NewOption.filter(
|
||||||
|
(value, index, self) =>
|
||||||
|
index === self.findIndex((t) => t.id === value.id),
|
||||||
|
);
|
||||||
|
const sortedNewOption = FilteredOption.sort((a, b) => a.id - b.id);
|
||||||
|
setAllPagesOption(sortedNewOption);
|
||||||
|
}
|
||||||
|
}, [option]);
|
||||||
|
useEffect(() => {
|
||||||
|
if (page === 1) {
|
||||||
|
setAllPagesOption(option);
|
||||||
|
}
|
||||||
|
}, [page]);
|
||||||
|
|
||||||
|
const SelectableChange = (value: any) => {
|
||||||
formik?.setFieldValue(name, value);
|
formik?.setFieldValue(name, value);
|
||||||
};
|
const isCleared = value?.length === 0 || !value;
|
||||||
const SearchHandleChange = (value: any) => {
|
|
||||||
navigate(`${window?.location?.pathname}?${searchBy}=${value}`, {
|
if (isCleared) {
|
||||||
replace: true,
|
if (PageName) {
|
||||||
|
setValidationParamState({
|
||||||
|
[PageName]: 1,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(value, "value");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleChange = useDebounce((value: string) => {
|
||||||
|
if (PageName) {
|
||||||
|
pushValidationParamState({
|
||||||
|
[PageName]: 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pushValidationParamState({
|
||||||
|
[searchBy]: value,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleBlur = () => {
|
||||||
|
if (PageName && page === 1) {
|
||||||
|
setValidationParamState({
|
||||||
|
[PageName]: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (PageName && page !== 1) {
|
||||||
|
setValidationParamState({
|
||||||
|
[PageName]: 1,
|
||||||
|
});
|
||||||
|
// setAllPagesOption([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleScroll = (event: any) => {
|
||||||
|
const target = event.target;
|
||||||
|
const isAtBottom =
|
||||||
|
target.scrollHeight === target.scrollTop + target.clientHeight;
|
||||||
|
|
||||||
|
if (isAtBottom && canChangePage && PageName && page) {
|
||||||
|
console.log("Scrolled to the last option!");
|
||||||
|
let newPage = page + 1;
|
||||||
|
pushValidationParamState({
|
||||||
|
[PageName]: newPage,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(AllPagesOption);
|
||||||
|
console.log(option,"option");
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ValidationField w-100">
|
<div className="ValidationField w-100">
|
||||||
{no_label ? (
|
<ValidationFieldLabel
|
||||||
<label htmlFor={name} className="text">
|
name={name}
|
||||||
<span>empty</span>
|
label={label}
|
||||||
</label>
|
label_icon={label_icon}
|
||||||
) : label_icon ? (
|
no_label={no_label}
|
||||||
<div className="LabelWithIcon">
|
placeholder={placeholder}
|
||||||
<label htmlFor={name} className="text">
|
t={t}
|
||||||
{t(`input.${label ? label : name}`)}
|
/>
|
||||||
</label>
|
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||||
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<label htmlFor={name} className="text">
|
|
||||||
{t(`input.${label ? label : name}`)}
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form.Item
|
|
||||||
hasFeedback
|
|
||||||
validateStatus={isError ? "error" : ""}
|
|
||||||
help={isError ? errorMsg : ""}
|
|
||||||
>
|
|
||||||
<Select
|
<Select
|
||||||
placeholder={t(
|
placeholder={t(`input.${placeholder || label || name}`)}
|
||||||
`input.${placeholder ? placeholder : label ? label : name}`,
|
|
||||||
)}
|
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
options={option}
|
options={AllPagesOption}
|
||||||
size="large"
|
size="large"
|
||||||
className={`${className} w-100`}
|
className={`${className} w-100`}
|
||||||
value={formik.values[name]}
|
value={formik.values[name]}
|
||||||
loading={isLoading}
|
// loading={isLoading}
|
||||||
allowClear
|
allowClear
|
||||||
{...(isMulti && { mode: "multiple" })}
|
{...(isMulti && { mode: "multiple" })}
|
||||||
onChange={onChange || SelectableChange}
|
onChange={onChange || SelectableChange}
|
||||||
showSearch
|
showSearch
|
||||||
optionFilterProp="label"
|
optionFilterProp="name"
|
||||||
notFoundContent={isLoading ? <Spin /> : "لا يوجد"}
|
notFoundContent={isLoading ? <Spin /> : t("validation.undefined")}
|
||||||
onSearch={SearchHandleChange}
|
onSearch={handleChange}
|
||||||
|
onBlur={handleBlur}
|
||||||
|
id={name}
|
||||||
|
onPopupScroll={handleScroll}
|
||||||
|
fieldNames={{ label: "name", value: "id" }}
|
||||||
|
{...props}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</ValidationFieldContainer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
import { Form, Select, Spin } from "antd";
|
import { Select } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import useFormField from "../../../Hooks/useFormField";
|
import useFormField from "../../../Hooks/useFormField";
|
||||||
import { MdOutlineEdit } from "react-icons/md";
|
|
||||||
import { translateOptions } from "../utils/translatedOptions";
|
import { translateOptions } from "../utils/translatedOptions";
|
||||||
|
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
|
||||||
|
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
|
||||||
|
import { SelectFieldProps } from "../utils/types";
|
||||||
|
|
||||||
const SelectField = ({
|
const SelectField = ({
|
||||||
name,
|
name,
|
||||||
|
|
@ -13,10 +15,11 @@ const SelectField = ({
|
||||||
isMulti,
|
isMulti,
|
||||||
onChange,
|
onChange,
|
||||||
className,
|
className,
|
||||||
props,
|
|
||||||
no_label,
|
no_label,
|
||||||
label_icon,
|
label_icon,
|
||||||
}: any) => {
|
isLoading,
|
||||||
|
...props
|
||||||
|
}: SelectFieldProps) => {
|
||||||
const { errorMsg, isError, t, formik } = useFormField(name, props);
|
const { errorMsg, isError, t, formik } = useFormField(name, props);
|
||||||
const SelectableChange = (value: {
|
const SelectableChange = (value: {
|
||||||
value: string;
|
value: string;
|
||||||
|
|
@ -24,47 +27,36 @@ const SelectField = ({
|
||||||
}) => {
|
}) => {
|
||||||
formik.setFieldValue(name, value);
|
formik.setFieldValue(name, value);
|
||||||
};
|
};
|
||||||
// console.log(name,"Select");
|
const options = translateOptions(option, t);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ValidationField w-100">
|
<div className="ValidationField w-100">
|
||||||
{no_label ? (
|
<ValidationFieldLabel
|
||||||
<label htmlFor={name} className="text">
|
name={name}
|
||||||
<span>empty</span>
|
label={label}
|
||||||
</label>
|
label_icon={label_icon}
|
||||||
) : label_icon ? (
|
no_label={no_label}
|
||||||
<div className="LabelWithIcon">
|
placeholder={placeholder}
|
||||||
<label htmlFor={name} className="text">
|
t={t}
|
||||||
{t(`input.${label ? label : name}`)}
|
/>
|
||||||
</label>
|
|
||||||
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<label htmlFor={name} className="text">
|
|
||||||
{t(`input.${label ? label : name}`)}
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form.Item
|
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||||
hasFeedback
|
|
||||||
validateStatus={isError ? "error" : ""}
|
|
||||||
help={isError ? errorMsg : ""}
|
|
||||||
>
|
|
||||||
<Select
|
<Select
|
||||||
placeholder={t(
|
placeholder={t(`input.${placeholder || label || name}`)}
|
||||||
`input.${placeholder ? placeholder : label ? label : name}`,
|
|
||||||
)}
|
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
options={translateOptions(option, t)}
|
options={options}
|
||||||
loading={option?.length < 1}
|
{...(isLoading && { loading: isLoading })}
|
||||||
size="large"
|
size="large"
|
||||||
className={`${className} ${isError ? "Select_error" : ""} w-100`}
|
className={`${className} ${isError ? "SelectError" : ""} w-100`}
|
||||||
value={formik.values[name]}
|
value={formik.values[name]}
|
||||||
allowClear
|
allowClear
|
||||||
{...(isMulti && { mode: "multiple" })}
|
{...(isMulti && { mode: "multiple" })}
|
||||||
onChange={onChange || SelectableChange}
|
onChange={onChange || SelectableChange}
|
||||||
|
showSearch={false}
|
||||||
|
id={name}
|
||||||
|
fieldNames={{label:"name",value:"id"}}
|
||||||
|
{...props}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</ValidationFieldContainer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import { Form, Input } from "antd";
|
import { Input } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import useFormField from "../../../Hooks/useFormField";
|
import useFormField from "../../../Hooks/useFormField";
|
||||||
import { Field } from "formik";
|
import { Field } from "formik";
|
||||||
|
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
|
||||||
const { TextArea } = Input;
|
const { TextArea } = Input;
|
||||||
|
|
||||||
const TextAreaField = ({
|
const TextAreaField = ({
|
||||||
|
|
@ -11,14 +12,12 @@ const TextAreaField = ({
|
||||||
isDisabled,
|
isDisabled,
|
||||||
onChange,
|
onChange,
|
||||||
props,
|
props,
|
||||||
type,
|
|
||||||
}: any) => {
|
}: any) => {
|
||||||
const { formik, isError, errorMsg, t } = useFormField(name, props);
|
const { formik, isError, errorMsg, t } = useFormField(name, props);
|
||||||
|
|
||||||
const handleChange = (
|
const handleChange = (
|
||||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||||
) => {
|
) => {
|
||||||
// console.log('Change:', e.target.value);
|
|
||||||
formik.setFieldValue(name, e.target.value);
|
formik.setFieldValue(name, e.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -27,11 +26,7 @@ const TextAreaField = ({
|
||||||
<label htmlFor={name} className="text">
|
<label htmlFor={name} className="text">
|
||||||
{t(`input.${label ? label : name}`)}
|
{t(`input.${label ? label : name}`)}
|
||||||
</label>
|
</label>
|
||||||
<Form.Item
|
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||||
hasFeedback
|
|
||||||
validateStatus={isError ? "error" : ""}
|
|
||||||
help={isError ? errorMsg : ""}
|
|
||||||
>
|
|
||||||
<Field
|
<Field
|
||||||
as={TextArea}
|
as={TextArea}
|
||||||
placeholder={t(`input.${placeholder ? placeholder : name}`)}
|
placeholder={t(`input.${placeholder ? placeholder : name}`)}
|
||||||
|
|
@ -39,10 +34,10 @@ const TextAreaField = ({
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
size="large"
|
size="large"
|
||||||
onChange={onChange || handleChange}
|
onChange={onChange || handleChange}
|
||||||
|
id={name}
|
||||||
// onChange={onChange ? onChange : handleChange}
|
// onChange={onChange ? onChange : handleChange}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</ValidationFieldContainer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ import React from "react";
|
||||||
import useFormField from "../../../Hooks/useFormField";
|
import useFormField from "../../../Hooks/useFormField";
|
||||||
import { MdOutlineEdit } from "react-icons/md";
|
import { MdOutlineEdit } from "react-icons/md";
|
||||||
import { Field } from "formik";
|
import { Field } from "formik";
|
||||||
|
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
|
||||||
|
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
|
||||||
const { TextArea } = Input;
|
const { TextArea } = Input;
|
||||||
|
|
||||||
const TextField = ({
|
const TextField = ({
|
||||||
|
|
@ -21,33 +23,20 @@ const TextField = ({
|
||||||
const TextFilehandleChange = (
|
const TextFilehandleChange = (
|
||||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||||
) => {
|
) => {
|
||||||
// console.log('Change:', e.target.value);
|
|
||||||
formik.setFieldValue(name, e.target.value);
|
formik.setFieldValue(name, e.target.value);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className={`ValidationField w-100 ${className ?? ""} `}>
|
<div className={`ValidationField w-100 ${className ?? ""} `}>
|
||||||
{no_label ? (
|
<ValidationFieldLabel
|
||||||
<label htmlFor={name} className="text">
|
name={name}
|
||||||
<span>empty</span>
|
label={label}
|
||||||
</label>
|
label_icon={label_icon}
|
||||||
) : label_icon ? (
|
no_label={no_label}
|
||||||
<div className="LabelWithIcon">
|
placeholder={placeholder}
|
||||||
<label htmlFor={name} className="text">
|
t={t}
|
||||||
{label2 ? label2 : t(`input.${label ? label : name}`)}
|
/>
|
||||||
</label>
|
|
||||||
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<label htmlFor={name} className="text">
|
|
||||||
{label2 ? label2 : t(`input.${label ? label : name}`)}
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form.Item
|
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||||
hasFeedback
|
|
||||||
validateStatus={isError ? "error" : ""}
|
|
||||||
help={isError ? errorMsg : ""}
|
|
||||||
>
|
|
||||||
<Field
|
<Field
|
||||||
as={TextArea}
|
as={TextArea}
|
||||||
placeholder={t(`input.${placeholder ? placeholder : name}`)}
|
placeholder={t(`input.${placeholder ? placeholder : name}`)}
|
||||||
|
|
@ -58,8 +47,9 @@ const TextField = ({
|
||||||
maxLength={1000}
|
maxLength={1000}
|
||||||
onChange={onChange || TextFilehandleChange}
|
onChange={onChange || TextFilehandleChange}
|
||||||
style={{ height: 120 }}
|
style={{ height: 120 }}
|
||||||
|
id={name}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</ValidationFieldContainer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import React from "react";
|
||||||
import useFormField from "../../../Hooks/useFormField";
|
import useFormField from "../../../Hooks/useFormField";
|
||||||
import { MdOutlineEdit } from "react-icons/md";
|
import { MdOutlineEdit } from "react-icons/md";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
|
||||||
|
|
||||||
const Time = ({
|
const Time = ({
|
||||||
name,
|
name,
|
||||||
|
|
@ -20,27 +21,19 @@ const Time = ({
|
||||||
formik.setFieldValue(name, value);
|
formik.setFieldValue(name, value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Formater = "H:mm";
|
const Formatter = "H:mm";
|
||||||
const FormikValue = formik.values[name];
|
const FormikValue = formik.values[name];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ValidationField w-100 ">
|
<div className="ValidationField w-100 ">
|
||||||
{no_label ? (
|
<ValidationFieldLabel
|
||||||
<label htmlFor={name} className="text">
|
name={name}
|
||||||
<span>empty</span>
|
label={label}
|
||||||
</label>
|
label_icon={label_icon}
|
||||||
) : label_icon ? (
|
no_label={no_label}
|
||||||
<div className="LabelWithIcon">
|
placeholder={placeholder}
|
||||||
<label htmlFor={name} className="text">
|
t={t}
|
||||||
{t(`input.${label ? label : name}`)}
|
/>
|
||||||
</label>
|
|
||||||
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<label htmlFor={name} className="text">
|
|
||||||
{t(`input.${label ? label : name}`)}
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
hasFeedback
|
hasFeedback
|
||||||
|
|
@ -51,14 +44,13 @@ const Time = ({
|
||||||
allowClear
|
allowClear
|
||||||
className={`${className} w-100`}
|
className={`${className} w-100`}
|
||||||
size="large"
|
size="large"
|
||||||
value={FormikValue ? dayjs(FormikValue, Formater) : null}
|
value={FormikValue ? dayjs(FormikValue, Formatter) : null}
|
||||||
onChange={onChange || onCalendarChange}
|
onChange={onChange || onCalendarChange}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
placeholder={t(
|
placeholder={t(`input.${placeholder || label || name}`)}
|
||||||
`input.${placeholder ? placeholder : label ? label : name}`,
|
format={Formatter}
|
||||||
)}
|
|
||||||
format={Formater}
|
|
||||||
needConfirm={false}
|
needConfirm={false}
|
||||||
|
id={name}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import React, { FC } from "react";
|
||||||
|
import { Form } from "antd";
|
||||||
|
|
||||||
|
interface ValidationFieldContainerProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
isError: boolean;
|
||||||
|
errorMsg: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ValidationFieldContainer: FC<ValidationFieldContainerProps> = ({
|
||||||
|
children,
|
||||||
|
isError,
|
||||||
|
errorMsg,
|
||||||
|
}) => (
|
||||||
|
<div className="ValidationFieldContainer">
|
||||||
|
<Form.Item
|
||||||
|
hasFeedback
|
||||||
|
validateStatus={isError ? "error" : ""}
|
||||||
|
help={isError ? errorMsg : ""}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
import React from "react";
|
||||||
|
import { MdOutlineEdit } from "react-icons/md";
|
||||||
|
|
||||||
|
interface ValidationFieldLabelProps {
|
||||||
|
name: string;
|
||||||
|
label?: string;
|
||||||
|
no_label?: boolean;
|
||||||
|
label_icon?: boolean;
|
||||||
|
placeholder?: string;
|
||||||
|
t: (key: string) => string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ValidationFieldLabel: React.FC<ValidationFieldLabelProps> = ({
|
||||||
|
name,
|
||||||
|
label,
|
||||||
|
placeholder,
|
||||||
|
no_label,
|
||||||
|
label_icon,
|
||||||
|
t,
|
||||||
|
}) => (
|
||||||
|
<>
|
||||||
|
{no_label ? (
|
||||||
|
<label htmlFor={name} className="text">
|
||||||
|
<span>empty</span>
|
||||||
|
</label>
|
||||||
|
) : label_icon ? (
|
||||||
|
<div className="LabelWithIcon">
|
||||||
|
<label htmlFor={name} className="text">
|
||||||
|
{t(`input.${label ? label : name}`)}
|
||||||
|
</label>
|
||||||
|
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<label htmlFor={name} className="text">
|
||||||
|
{t(`input.${label ? label : name}`)}
|
||||||
|
</label>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
@ -2,7 +2,6 @@ import { useState } from "react";
|
||||||
import { ErrorMessage, useField, Field, useFormikContext } from "formik";
|
import { ErrorMessage, useField, Field, useFormikContext } from "formik";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { FaExclamationCircle } from "react-icons/fa";
|
import { FaExclamationCircle } from "react-icons/fa";
|
||||||
import { convert_data_to_select } from "../../Layout/app/Const";
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
useState,
|
useState,
|
||||||
|
|
@ -12,5 +11,4 @@ export {
|
||||||
useFormikContext,
|
useFormikContext,
|
||||||
useTranslation,
|
useTranslation,
|
||||||
FaExclamationCircle,
|
FaExclamationCircle,
|
||||||
convert_data_to_select,
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { create } from "zustand";
|
||||||
|
|
||||||
|
interface ValidationParamState {
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ModalState {
|
||||||
|
ValidationParamState: ValidationParamState;
|
||||||
|
setValidationParamState: (validationParamState: ValidationParamState) => void;
|
||||||
|
pushValidationParamState: (
|
||||||
|
validationParamState: ValidationParamState,
|
||||||
|
) => void;
|
||||||
|
clearValidationParamState: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useValidationValidationParamState = create<ModalState>((set) => ({
|
||||||
|
ValidationParamState: {},
|
||||||
|
|
||||||
|
setValidationParamState: (validationParamState) =>
|
||||||
|
set(() => ({
|
||||||
|
ValidationParamState: validationParamState,
|
||||||
|
})),
|
||||||
|
|
||||||
|
pushValidationParamState: (validationParamState) =>
|
||||||
|
set((state) => ({
|
||||||
|
ValidationParamState: {
|
||||||
|
...state.ValidationParamState,
|
||||||
|
...validationParamState,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
|
||||||
|
clearValidationParamState: () =>
|
||||||
|
set({
|
||||||
|
ValidationParamState: {},
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
@ -4,10 +4,13 @@
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
.ValidationField {
|
.ValidationField {
|
||||||
margin-bottom: 1.3vw;
|
margin-bottom: 10px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
min-height: 80px;
|
||||||
|
padding-inline: 20px;
|
||||||
> * {
|
> * {
|
||||||
width: 100%;
|
width: 100% !important;
|
||||||
|
min-width: 150px;
|
||||||
}
|
}
|
||||||
.text,
|
.text,
|
||||||
.ant-form-item {
|
.ant-form-item {
|
||||||
|
|
@ -16,213 +19,55 @@
|
||||||
color: transparent;
|
color: transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-select-outlined:not(.ant-select-customize-input) .ant-select-selector {
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.Select_error {
|
|
||||||
.ant-select-selector {
|
|
||||||
border: 1px solid red !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// .ValidationField{
|
|
||||||
// .ant-select-selector{
|
|
||||||
// border: 1px solid var(--border-color) ;
|
|
||||||
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
> span {
|
|
||||||
margin-bottom: 0px !important;
|
|
||||||
&:focus-within {
|
|
||||||
border-color: var(--primary);
|
|
||||||
box-shadow: 0 0 0 1px var(--primary);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
&:has(.is-invalid) {
|
|
||||||
border-color: red !important ;
|
|
||||||
}
|
|
||||||
input {
|
|
||||||
color: var(--text);
|
|
||||||
background: var(--bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
input:-webkit-autofill,
|
|
||||||
input:-webkit-autofill:hover,
|
|
||||||
input:-webkit-autofill:focus,
|
|
||||||
input:-webkit-autofill:active {
|
|
||||||
-webkit-box-shadow: 0 0 0 30px white inset !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
//// upload
|
||||||
.ant-upload-select {
|
.ant-upload-select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.Checkboxs {
|
|
||||||
padding: 4%;
|
|
||||||
}
|
|
||||||
.ant-checkbox-wrapper {
|
|
||||||
min-width: 100px;
|
|
||||||
}
|
|
||||||
.SearchField {
|
|
||||||
button {
|
|
||||||
background: var(--primary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.text {
|
|
||||||
color: var(--text);
|
|
||||||
margin-bottom: 15px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:disabled {
|
|
||||||
color: var(--text) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.isError {
|
|
||||||
outline: red 1px solid;
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.Error_color {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
input:-webkit-autofill {
|
|
||||||
-webkit-box-shadow: 0 0 0 1000px white inset !important; /* Change the color to your desired background color */
|
|
||||||
}
|
|
||||||
input:-webkit-autofill:focus {
|
|
||||||
-webkit-box-shadow: 0 0 0 1000px white inset !important; /* Change the color to your desired background color */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove autofill background color on hover */
|
|
||||||
input:-webkit-autofill:hover {
|
|
||||||
-webkit-box-shadow: 0 0 0 1000px white inset !important; /* Change the color to your desired background color */
|
|
||||||
}
|
|
||||||
.upload_image_button {
|
|
||||||
.ant-btn {
|
|
||||||
min-height: 3vw !important;
|
|
||||||
border: 0.1vw solid var(--border-color);
|
|
||||||
|
|
||||||
|
.ant-btn-default{
|
||||||
|
padding: 7px 11px;
|
||||||
|
height: var(--fieldHeight);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
align-items: center;
|
||||||
}
|
|
||||||
.ant-select-outlined:not(.ant-select-customize-input) .ant-select-selector {
|
|
||||||
min-height: 3vw !important;
|
|
||||||
}
|
|
||||||
.ant-select-multiple.ant-select-lg .ant-select-selection-overflow {
|
|
||||||
min-height: 3vw !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-upload-list .ant-upload-list-item {
|
|
||||||
// height:3vw !important;
|
|
||||||
border: 1px solid var(--border-color) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.TowValidationItems {
|
|
||||||
display: flex;
|
|
||||||
gap: 3%;
|
|
||||||
> label {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-select .ant-select-arrow {
|
|
||||||
inset-inline-end: 1vw;
|
|
||||||
}
|
|
||||||
.ant-input-affix-wrapper-lg {
|
|
||||||
padding: 0.5vw 1vw;
|
|
||||||
font-size: 1vw;
|
|
||||||
min-height: 3vw;
|
|
||||||
border-radius: 0.6vw;
|
|
||||||
border: 1.5px solid var(--opacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-picker-outlined {
|
|
||||||
padding: 0.5vw 1vw;
|
|
||||||
font-size: 1vw;
|
|
||||||
min-height: 3vw;
|
|
||||||
border-radius: 0.6vw;
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-select-single.ant-select-lg .ant-select-selector {
|
|
||||||
min-height: 3vw;
|
|
||||||
border-radius: 0.6vw;
|
|
||||||
}
|
|
||||||
.ant-select-single .ant-select-selector .ant-select-selection-search-input {
|
|
||||||
min-height: 3vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-select-outlined .ant-select-selector {
|
|
||||||
min-height: 3vw !important;
|
|
||||||
border-radius: 0.6vw !important;
|
|
||||||
}
|
|
||||||
.ant-select-single.ant-select-lg {
|
|
||||||
min-height: 3vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-upload-wrapper .ant-upload-list .ant-upload-list-item {
|
|
||||||
width: 21.5vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-input-number-outlined {
|
|
||||||
width: 100%;
|
|
||||||
height: 3vw;
|
|
||||||
}
|
|
||||||
.ant-input-number-lg input.ant-input-number-input {
|
|
||||||
width: 100%;
|
|
||||||
height: 3vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bigRow {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
> *.w-100 {
|
|
||||||
width: 48% !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
//// number input
|
||||||
|
///
|
||||||
.ant-input-number-affix-wrapper-lg{
|
.ant-input-number-affix-wrapper-lg{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 3vw;
|
}
|
||||||
border-radius: 0.6vw;
|
.ValidationFieldCheckbox{
|
||||||
border: 1px solid var(--border-color);
|
// background-color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
.TwoSelectGroup {
|
/// input hight
|
||||||
display: flex;
|
.ant-form-item-control-input-content{
|
||||||
gap: 10px;
|
height: var(--fieldHeight);
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
.TwoSelectGroupbutton {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-checkbox-wrapper {
|
|
||||||
margin-top: 25px !important;
|
//// date picker
|
||||||
|
.ant-picker-large{
|
||||||
|
height: var(--fieldHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
.add_new_button {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
svg {
|
|
||||||
color: var(--primary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.ValidationField:has(.input_number) {
|
|
||||||
max-width: 100px;
|
|
||||||
|
|
||||||
.input_number {
|
/// text area
|
||||||
max-width: 100px;
|
///
|
||||||
}
|
|
||||||
|
.ValidationFieldTextArea{
|
||||||
|
.ant-form-item-control-input-content{
|
||||||
|
min-height: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flex {
|
}
|
||||||
display: flex;
|
|
||||||
gap: 30px;
|
.ant-input-textarea-affix-wrapper.ant-input-affix-wrapper {
|
||||||
max-width: 80% !important;
|
|
||||||
|
height: 120px;
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
export function getNestedValue(obj: any, path: any) {
|
|
||||||
return path
|
|
||||||
.replace(/\?.\[|\]\[|\]\.?/g, ".") // Replace question mark and square brackets
|
|
||||||
.split(".") // Split by dots
|
|
||||||
.filter(Boolean) // Remove empty strings
|
|
||||||
.reduce((acc: any, key: any) => acc && acc[key], obj); // Access nested properties
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
export const translateOptions = (options: any, t: any) => {
|
export const translateOptions = (options: any, t: any) => {
|
||||||
return options.map((opt: any) => ({
|
return options?.map((opt: any) => ({
|
||||||
...opt,
|
...opt,
|
||||||
label: t(`${opt.label}`),
|
label: t(`${opt?.label}`),
|
||||||
|
name: t(`${opt?.name}`),
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,149 +1,60 @@
|
||||||
import { InputProps } from "antd";
|
import { InputProps, SelectProps } from "antd";
|
||||||
|
|
||||||
export type ValidationFieldType =
|
// Common properties for all field types
|
||||||
| "text"
|
interface BaseFieldProps {
|
||||||
| "Select"
|
|
||||||
| "LocalSearch"
|
|
||||||
| "Search"
|
|
||||||
| "DataRange"
|
|
||||||
| "Date"
|
|
||||||
| "Time"
|
|
||||||
| "File"
|
|
||||||
| "MaltyFile"
|
|
||||||
| "DropFile"
|
|
||||||
| "Checkbox"
|
|
||||||
| "number"
|
|
||||||
| "password"
|
|
||||||
| "email"
|
|
||||||
| "TextArea";
|
|
||||||
|
|
||||||
export interface ValidationFieldPropsText {
|
|
||||||
name: string;
|
name: string;
|
||||||
no_label?: boolean;
|
|
||||||
label_icon?: boolean;
|
|
||||||
type: "text";
|
|
||||||
placeholder?: string;
|
|
||||||
label?: string;
|
label?: string;
|
||||||
|
placeholder?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
isDisabled?: boolean;
|
isDisabled?: boolean;
|
||||||
onChange?: (value: any) => void;
|
onChange?: (value: any) => void;
|
||||||
dir?: "ltr" | "rtl";
|
dir?: "ltr" | "rtl";
|
||||||
|
no_label?: boolean;
|
||||||
|
label_icon?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ValidationFieldPropsSelect {
|
// Specific field type properties
|
||||||
name: string;
|
export type SelectFieldProps = BaseFieldProps &
|
||||||
no_label?: boolean;
|
SelectProps & {
|
||||||
label_icon?: boolean;
|
type: "Select" | "LocalSearch";
|
||||||
type: "Select";
|
|
||||||
placeholder?: string;
|
|
||||||
label?: string;
|
|
||||||
className?: string;
|
|
||||||
isDisabled?: boolean;
|
|
||||||
onChange?: any;
|
|
||||||
dir?: "ltr" | "rtl";
|
|
||||||
option: any[];
|
option: any[];
|
||||||
isMulti?: boolean;
|
isMulti?: boolean;
|
||||||
}
|
isLoading?: boolean;
|
||||||
|
searchBy?: string;
|
||||||
|
canChangePage?: boolean;
|
||||||
|
PageName?: string;
|
||||||
|
page?: number;
|
||||||
|
};
|
||||||
|
|
||||||
export interface ValidationFieldPropsLocalSearch {
|
export type SearchFieldProps = BaseFieldProps &
|
||||||
name: string;
|
SelectProps & {
|
||||||
no_label?: boolean;
|
|
||||||
label_icon?: boolean;
|
|
||||||
type: "LocalSearch";
|
|
||||||
placeholder?: string;
|
|
||||||
label?: string;
|
|
||||||
className?: string;
|
|
||||||
isDisabled?: boolean;
|
|
||||||
onChange?: (value: any) => void;
|
|
||||||
dir?: "ltr" | "rtl";
|
|
||||||
option: any[];
|
|
||||||
isMulti?: boolean;
|
|
||||||
}
|
|
||||||
export interface ValidationFieldPropsSearch {
|
|
||||||
name: string;
|
|
||||||
no_label?: boolean;
|
|
||||||
label_icon?: boolean;
|
|
||||||
type: "Search";
|
type: "Search";
|
||||||
placeholder?: string;
|
|
||||||
label?: string;
|
|
||||||
className?: string;
|
|
||||||
isDisabled?: boolean;
|
|
||||||
onChange?: (value: any) => void;
|
|
||||||
dir?: "ltr" | "rtl";
|
|
||||||
option: any[];
|
option: any[];
|
||||||
isMulti?: boolean;
|
isMulti?: boolean;
|
||||||
|
isLoading: boolean;
|
||||||
searchBy: string;
|
searchBy: string;
|
||||||
isLoading?: any;
|
canChangePage: boolean;
|
||||||
}
|
PageName: string;
|
||||||
export interface ValidationFieldPropsDataRange {
|
page: number;
|
||||||
name: string;
|
};
|
||||||
no_label?: boolean;
|
|
||||||
label_icon?: boolean;
|
type DateFieldProps = BaseFieldProps & {
|
||||||
type: "DataRange";
|
type: "DataRange" | "Date" | "Time";
|
||||||
placeholder?: string;
|
|
||||||
label?: string;
|
|
||||||
className?: string;
|
|
||||||
isDisabled?: boolean;
|
|
||||||
onChange?: (value: any) => void;
|
|
||||||
dir?: "ltr" | "rtl";
|
|
||||||
Format?: "YYYY/MM/DD" | "MM/DD" | "YYYY/MM" | "YYYY-MM-DD HH:mm:ss.SSS";
|
Format?: "YYYY/MM/DD" | "MM/DD" | "YYYY/MM" | "YYYY-MM-DD HH:mm:ss.SSS";
|
||||||
}
|
|
||||||
export interface ValidationFieldPropsDate {
|
|
||||||
name: string;
|
|
||||||
no_label?: boolean;
|
|
||||||
label_icon?: boolean;
|
|
||||||
type: "Date";
|
|
||||||
placeholder?: string;
|
|
||||||
label?: string;
|
|
||||||
className?: string;
|
|
||||||
isDisabled?: boolean;
|
|
||||||
onChange?: (value: any) => void;
|
|
||||||
dir?: "ltr" | "rtl";
|
|
||||||
picker?: "data" | "week" | "month" | "quarter" | "year";
|
picker?: "data" | "week" | "month" | "quarter" | "year";
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface ValidationFieldPropsTime {
|
type FileFieldProps = BaseFieldProps & {
|
||||||
name: string;
|
|
||||||
no_label?: boolean;
|
|
||||||
label_icon?: boolean;
|
|
||||||
type: "Time";
|
|
||||||
label?: string;
|
|
||||||
placeholder?: string;
|
|
||||||
className?: string;
|
|
||||||
isDisabled?: boolean;
|
|
||||||
onChange?: (value: any) => void;
|
|
||||||
dir?: "ltr" | "rtl";
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ValidationFieldPropsFile {
|
|
||||||
name: string;
|
|
||||||
no_label?: boolean;
|
|
||||||
label_icon?: boolean;
|
|
||||||
type: "File" | "MaltyFile" | "DropFile";
|
type: "File" | "MaltyFile" | "DropFile";
|
||||||
placeholder?: string;
|
};
|
||||||
label?: string;
|
|
||||||
className?: string;
|
type CheckboxFieldProps = BaseFieldProps & {
|
||||||
isDisabled?: boolean;
|
|
||||||
onChange?: (value: any) => void;
|
|
||||||
dir?: "ltr" | "rtl";
|
|
||||||
icon?:any
|
|
||||||
}
|
|
||||||
export interface ValidationFieldPropsCheckbox {
|
|
||||||
name: string;
|
|
||||||
no_label?: boolean;
|
|
||||||
label_icon?: boolean;
|
|
||||||
type: "Checkbox";
|
type: "Checkbox";
|
||||||
label?: string;
|
|
||||||
className?: string;
|
|
||||||
isDisabled?: boolean;
|
|
||||||
onChange?: (value: any) => void;
|
|
||||||
dir?: "ltr" | "rtl";
|
|
||||||
Group?: boolean;
|
Group?: boolean;
|
||||||
}
|
};
|
||||||
export interface ValidationFieldPropstext {
|
|
||||||
name: string;
|
export type FieldProps = BaseFieldProps &
|
||||||
no_label?: boolean;
|
InputProps & {
|
||||||
label_icon?: boolean;
|
|
||||||
type?:
|
type?:
|
||||||
| "text"
|
| "text"
|
||||||
| "number"
|
| "number"
|
||||||
|
|
@ -151,45 +62,19 @@ export interface ValidationFieldPropstext {
|
||||||
| "email"
|
| "email"
|
||||||
| "TextArea"
|
| "TextArea"
|
||||||
| "NumberFormate";
|
| "NumberFormate";
|
||||||
label?: string;
|
|
||||||
label2?: string;
|
label2?: string;
|
||||||
className?: string;
|
|
||||||
placeholder?: string;
|
|
||||||
isDisabled?: boolean;
|
|
||||||
onChange?: (value: any) => void;
|
|
||||||
dir?: "ltr" | "rtl";
|
|
||||||
Group?: boolean;
|
Group?: boolean;
|
||||||
[key: string]: any; // Index signature to allow any additional props
|
[key: string]: any;
|
||||||
}
|
};
|
||||||
|
|
||||||
///// new
|
|
||||||
export interface BaseField {
|
|
||||||
name: string;
|
|
||||||
label?: string;
|
|
||||||
placeholder?: string;
|
|
||||||
}
|
|
||||||
export type OmitBaseType = "placeholder" | "name" | "label" | "type";
|
|
||||||
|
|
||||||
export type OmitPicker = OmitBaseType | "format";
|
|
||||||
|
|
||||||
export interface ValidationFieldPropsInput
|
|
||||||
extends Omit<InputProps, OmitBaseType>,
|
|
||||||
BaseField {
|
|
||||||
type: "text" | "number" | "password" | "email" | "Number";
|
|
||||||
isDisabled?: boolean;
|
|
||||||
no_label?: string;
|
|
||||||
label_icon?: string;
|
|
||||||
label2?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Union type for all field types
|
||||||
export type ValidationFieldProps =
|
export type ValidationFieldProps =
|
||||||
| ValidationFieldPropsInput
|
| SelectFieldProps
|
||||||
| ValidationFieldPropsSelect
|
| DateFieldProps
|
||||||
| ValidationFieldPropsLocalSearch
|
| FileFieldProps
|
||||||
| ValidationFieldPropsDataRange
|
| CheckboxFieldProps
|
||||||
| ValidationFieldPropsDate
|
| SearchFieldProps
|
||||||
| ValidationFieldPropsTime
|
| FieldProps;
|
||||||
| ValidationFieldPropsFile
|
|
||||||
| ValidationFieldPropsCheckbox
|
// Validation field type
|
||||||
| ValidationFieldPropstext
|
export type ValidationFieldType = ValidationFieldProps["type"];
|
||||||
| ValidationFieldPropsSearch;
|
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,12 @@ const Header = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="exercise_add_header mb-4">
|
<header className="exercise_add_header mb-4">
|
||||||
|
<article>
|
||||||
|
<img src="/Icon/QuestionIcon.svg" alt="" />
|
||||||
<div>
|
<div>
|
||||||
{t("practical.add")} {t("models.exercise")}{" "}
|
{t("practical.add")} {t("models.exercise")}{" "}
|
||||||
</div>
|
</div>
|
||||||
|
</article>
|
||||||
<div>
|
<div>
|
||||||
<GoArrowSwitch onClick={handleChange} className="m-2" />
|
<GoArrowSwitch onClick={handleChange} className="m-2" />
|
||||||
{isBseQuestion || values?.isBase === 1
|
{isBseQuestion || values?.isBase === 1
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import OrderBySelect from '../../Components/Filter/OrderBySelect';
|
||||||
import LayoutFilterModal from './LayoutFilterModal';
|
import LayoutFilterModal from './LayoutFilterModal';
|
||||||
import { BiFilterAlt } from 'react-icons/bi';
|
import { BiFilterAlt } from 'react-icons/bi';
|
||||||
import { MdKeyboardArrowDown } from "react-icons/md";
|
import { MdKeyboardArrowDown } from "react-icons/md";
|
||||||
|
import SearchField from '../../Components/DataTable/SearchField';
|
||||||
|
|
||||||
const FilterLayout = ({filterTitle, sub_children}:{filterTitle:string,sub_children:any}) => {
|
const FilterLayout = ({filterTitle, sub_children}:{filterTitle:string,sub_children:any}) => {
|
||||||
const {t} = useTranslation();
|
const {t} = useTranslation();
|
||||||
|
|
@ -21,7 +22,7 @@ const FilterLayout = ({filterTitle, sub_children}:{filterTitle:string,sub_childr
|
||||||
return (
|
return (
|
||||||
<div className='filter_header'>
|
<div className='filter_header'>
|
||||||
<div className='filter_header_top'>
|
<div className='filter_header_top'>
|
||||||
<h1>{t(filterTitle)}</h1>
|
<h4>{t(filterTitle)}</h4>
|
||||||
<div className='filter_and_order_by'>
|
<div className='filter_and_order_by'>
|
||||||
<span>
|
<span>
|
||||||
<LayoutFilterModal
|
<LayoutFilterModal
|
||||||
|
|
@ -48,8 +49,8 @@ const FilterLayout = ({filterTitle, sub_children}:{filterTitle:string,sub_childr
|
||||||
<p>{t("ادخالات")}</p>
|
<p>{t("ادخالات")}</p>
|
||||||
</span>
|
</span>
|
||||||
<div className="header_search">
|
<div className="header_search">
|
||||||
<SearchFieldWithSelect
|
<SearchField
|
||||||
options={translateArray}
|
searchBy=''
|
||||||
placeholder={t("practical.search_here")}
|
placeholder={t("practical.search_here")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { Formik, Form, useFormikContext } from "formik";
|
import { Formik, Form, useFormikContext } from "formik";
|
||||||
import React, { ReactNode, useEffect } from "react";
|
import React, { ReactNode, useEffect } from "react";
|
||||||
import { useValidationState } from "../../Components/ValidationField/utils/ValidationState";
|
|
||||||
import { useModalState } from "../../zustand/Modal";
|
import { useModalState } from "../../zustand/Modal";
|
||||||
|
|
||||||
interface FormValues {
|
interface FormValues {
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ const LayoutModel = ({
|
||||||
modelTitle,
|
modelTitle,
|
||||||
ModelEnum,
|
ModelEnum,
|
||||||
ModelClassName,
|
ModelClassName,
|
||||||
width = "45vw",
|
width = "800px",
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
}: LayoutModalProps) => {
|
}: LayoutModalProps) => {
|
||||||
const { isOpen, setIsOpen } = useModalState((state) => state);
|
const { isOpen, setIsOpen } = useModalState((state) => state);
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import React from "react";
|
import React, { lazy, Suspense } from "react";
|
||||||
import {
|
import {
|
||||||
useAddKeyToData,
|
useAddKeyToData,
|
||||||
Table,
|
Table,
|
||||||
useTranslation,
|
useTranslation,
|
||||||
usePagination,
|
usePagination,
|
||||||
Loading,
|
|
||||||
EmptyData,
|
|
||||||
} from ".";
|
} from ".";
|
||||||
import { DataTableProps } from "../../../types/Table";
|
import { DataTableProps } from "../../../types/Table";
|
||||||
|
const NotFoundLottie = React.lazy(() => import("../../../Components/Lottie/NotFound/NotFoundLottie"));
|
||||||
|
const LoadingLottie = React.lazy(() => import("../../../Components/Lottie/Loading/LoadingLottie"));
|
||||||
|
|
||||||
const DataTable: React.FC<DataTableProps> = ({
|
const DataTable: React.FC<DataTableProps> = ({
|
||||||
response,
|
response,
|
||||||
|
|
@ -22,29 +22,43 @@ const DataTable: React.FC<DataTableProps> = ({
|
||||||
const getRowClassName = (record: any, index: number): string => {
|
const getRowClassName = (record: any, index: number): string => {
|
||||||
return index % 2 === 0 ? "even-row" : "odd-row";
|
return index % 2 === 0 ? "even-row" : "odd-row";
|
||||||
};
|
};
|
||||||
|
const isRefetching = response?.isRefetching;
|
||||||
|
const isLoading = response?.isLoading;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table
|
<Table
|
||||||
|
style={{minHeight:"300px"}}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
dataSource={dataSource}
|
dataSource={dataSource}
|
||||||
rowClassName={(record, index) => getRowClassName(record, index)}
|
rowClassName={(record, index) => getRowClassName(record, index)}
|
||||||
className="DataTable"
|
className="DataTable"
|
||||||
loading={{
|
loading={{
|
||||||
spinning: response?.isLoading,
|
spinning: isLoading || isRefetching,
|
||||||
indicator: <Loading />,
|
indicator:<Suspense fallback={<></>}>
|
||||||
|
<LoadingLottie />
|
||||||
|
</Suspense> ,
|
||||||
size: "large",
|
size: "large",
|
||||||
}}
|
}}
|
||||||
locale={{
|
locale={{
|
||||||
emptyText: (
|
emptyText: (
|
||||||
<EmptyData
|
isLoading || isRefetching ?
|
||||||
loading={response?.isLoading}
|
<></>
|
||||||
header={t("Table.header")}
|
:
|
||||||
info={t("Table.info")}
|
|
||||||
|
<Suspense fallback={<></>}>
|
||||||
|
<NotFoundLottie
|
||||||
|
|
||||||
/>
|
/>
|
||||||
|
</Suspense>
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
pagination={{
|
pagination={{
|
||||||
...pagination,
|
...pagination,
|
||||||
onChange: handlePageChange,
|
onChange: handlePageChange,
|
||||||
|
nextIcon:<>{t("practical.next")}</>,
|
||||||
|
prevIcon:<> {t("practical.prev")} </>,
|
||||||
|
className:"pagination_antd"
|
||||||
|
|
||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,11 @@
|
||||||
import { Table } from "antd";
|
import { Table } from "antd";
|
||||||
import { useAddKeyToData } from "../../../Hooks/useAddKeyToData";
|
import { useAddKeyToData } from "../../../Hooks/useAddKeyToData";
|
||||||
import Loading from "../../../Components/DataState/Loading";
|
|
||||||
import EmptyData from "../../../Components/DataState/EmptyData";
|
|
||||||
import usePagination from "../usePagination";
|
import usePagination from "../usePagination";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Table,
|
Table,
|
||||||
useAddKeyToData,
|
useAddKeyToData,
|
||||||
Loading,
|
|
||||||
EmptyData,
|
|
||||||
usePagination,
|
usePagination,
|
||||||
useTranslation,
|
useTranslation,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,14 @@ const usePagination = (data: Data) => {
|
||||||
|
|
||||||
const [pagination, setPagination] = useState<PaginationAntd>({
|
const [pagination, setPagination] = useState<PaginationAntd>({
|
||||||
current: data?.meta?.current_page || 1,
|
current: data?.meta?.current_page || 1,
|
||||||
pageSize: data?.meta?.per_page || 10,
|
pageSize: data?.meta?.per_page || 2,
|
||||||
total: data?.meta?.total || 0,
|
total: data?.meta?.total || 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setPagination({
|
setPagination({
|
||||||
current: data?.meta?.current_page || 1,
|
current: data?.meta?.current_page || 1,
|
||||||
pageSize: data?.meta?.per_page || 10,
|
pageSize: data?.meta?.per_page || 2,
|
||||||
total: data?.meta?.total || 0,
|
total: data?.meta?.total || 0,
|
||||||
});
|
});
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ const NavBar = ({isOpen}:{isOpen:boolean}) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="NavBar">
|
<div className="NavBar">
|
||||||
<Suspense fallback={<SpinContainer />}>
|
<Suspense fallback={<></>}>
|
||||||
{/* <span className="navbar_link" onClick={handelNavigate}>
|
{/* <span className="navbar_link" onClick={handelNavigate}>
|
||||||
<MdOutlineArrowForwardIos /> {PageTitle}
|
<MdOutlineArrowForwardIos /> {PageTitle}
|
||||||
</span> */}
|
</span> */}
|
||||||
|
|
@ -40,6 +40,7 @@ const NavBar = ({isOpen}:{isOpen:boolean}) => {
|
||||||
<SearchFieldWithSelect
|
<SearchFieldWithSelect
|
||||||
options={translateArray}
|
options={translateArray}
|
||||||
placeholder={t("practical.search_here")}
|
placeholder={t("practical.search_here")}
|
||||||
|
withIcon
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<NavBarRightSide />
|
<NavBarRightSide />
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ const AddModel: React.FC = () => {
|
||||||
handleSubmit={handleSubmit}
|
handleSubmit={handleSubmit}
|
||||||
getInitialValues={getInitialValues({})}
|
getInitialValues={getInitialValues({})}
|
||||||
getValidationSchema={getValidationSchema}
|
getValidationSchema={getValidationSchema}
|
||||||
width="40vw"
|
width="500px"
|
||||||
>
|
>
|
||||||
<ModelForm />
|
<ModelForm />
|
||||||
</LayoutModel>
|
</LayoutModel>
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ const EditModel: React.FC = () => {
|
||||||
getInitialValues={getInitialValues(objectToEdit)}
|
getInitialValues={getInitialValues(objectToEdit)}
|
||||||
getValidationSchema={getValidationSchema}
|
getValidationSchema={getValidationSchema}
|
||||||
isAddModal={false}
|
isAddModal={false}
|
||||||
width="40vw"
|
width="500px"
|
||||||
>
|
>
|
||||||
<ModelForm />
|
<ModelForm />
|
||||||
</LayoutModel>
|
</LayoutModel>
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ const TableHeader = () => {
|
||||||
return (
|
return (
|
||||||
<div className="TableWithHeader">
|
<div className="TableWithHeader">
|
||||||
<Suspense fallback={<Spin />}>
|
<Suspense fallback={<Spin />}>
|
||||||
|
|
||||||
<PageHeader
|
<PageHeader
|
||||||
pageTitle="grade"
|
pageTitle="grade"
|
||||||
ModelAbility={ModalEnum?.GRADE_ADD}
|
ModelAbility={ModalEnum?.GRADE_ADD}
|
||||||
|
|
|
||||||
33
src/Pages/Admin/Student/Model/AddModel.tsx
Normal file
33
src/Pages/Admin/Student/Model/AddModel.tsx
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
import React from "react";
|
||||||
|
import { getInitialValues, getValidationSchema } from "./formUtil";
|
||||||
|
import { ModalEnum } from "../../../../enums/Model";
|
||||||
|
import LayoutModel from "../../../../Layout/Dashboard/LayoutModel";
|
||||||
|
import { QueryStatusEnum } from "../../../../enums/QueryStatus";
|
||||||
|
import ModelForm from "./ModelForm";
|
||||||
|
import { useAddStudent } from "../../../../api/student";
|
||||||
|
|
||||||
|
const AddModel: React.FC = () => {
|
||||||
|
const { mutate, status } = useAddStudent();
|
||||||
|
|
||||||
|
const handleSubmit = (values: any) => {
|
||||||
|
mutate({
|
||||||
|
...values,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<LayoutModel
|
||||||
|
status={status as QueryStatusEnum}
|
||||||
|
ModelEnum={ModalEnum.STUDENT_ADD}
|
||||||
|
modelTitle="student"
|
||||||
|
handleSubmit={handleSubmit}
|
||||||
|
getInitialValues={getInitialValues({})}
|
||||||
|
getValidationSchema={getValidationSchema}
|
||||||
|
>
|
||||||
|
<ModelForm />
|
||||||
|
</LayoutModel>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AddModel;
|
||||||
37
src/Pages/Admin/Student/Model/EditModel.tsx
Normal file
37
src/Pages/Admin/Student/Model/EditModel.tsx
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import React from "react";
|
||||||
|
import { getInitialValues, getValidationSchema } from "./formUtil";
|
||||||
|
import { ModalEnum } from "../../../../enums/Model";
|
||||||
|
import LayoutModel from "../../../../Layout/Dashboard/LayoutModel";
|
||||||
|
import ModelForm from "./ModelForm";
|
||||||
|
import { QueryStatusEnum } from "../../../../enums/QueryStatus";
|
||||||
|
import { useObjectToEdit } from "../../../../zustand/ObjectToEditState";
|
||||||
|
import { useUpdateStudent } from "../../../../api/student";
|
||||||
|
import { handelImageState } from "../../../../utils/DataToSendImageState";
|
||||||
|
|
||||||
|
const EditModel: React.FC = () => {
|
||||||
|
const { mutate, status } = useUpdateStudent();
|
||||||
|
const { objectToEdit } = useObjectToEdit((state) => state);
|
||||||
|
|
||||||
|
const handleSubmit = (values: any) => {
|
||||||
|
const Data_to_send = { ...values };
|
||||||
|
mutate(Data_to_send);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<LayoutModel
|
||||||
|
status={status as QueryStatusEnum}
|
||||||
|
ModelEnum={ModalEnum.STUDENT_EDIT}
|
||||||
|
modelTitle="student"
|
||||||
|
handleSubmit={handleSubmit}
|
||||||
|
getInitialValues={getInitialValues(objectToEdit)}
|
||||||
|
getValidationSchema={getValidationSchema}
|
||||||
|
isAddModal={false}
|
||||||
|
>
|
||||||
|
<ModelForm />
|
||||||
|
</LayoutModel>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EditModel;
|
||||||
58
src/Pages/Admin/Student/Model/ModelForm.tsx
Normal file
58
src/Pages/Admin/Student/Model/ModelForm.tsx
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { Col, Row } from "reactstrap";
|
||||||
|
import ValidationField from "../../../../Components/ValidationField/ValidationField";
|
||||||
|
import { useGetAllGrade } from "../../../../api/grade";
|
||||||
|
import { useValidationValidationParamState } from "../../../../Components/ValidationField/state/ValidationValidationParamState";
|
||||||
|
|
||||||
|
const Form = ({ isEdit = false }: { isEdit?: boolean }) => {
|
||||||
|
const { ValidationParamState } = useValidationValidationParamState();
|
||||||
|
const {
|
||||||
|
GradeName, GradeCurrentPage,
|
||||||
|
} = ValidationParamState;
|
||||||
|
|
||||||
|
|
||||||
|
const { data: Grade, isLoading: isLoadingGrade } = useGetAllGrade({
|
||||||
|
name: GradeName,
|
||||||
|
page: GradeCurrentPage
|
||||||
|
});
|
||||||
|
const GradeOption = Grade?.data ?? []
|
||||||
|
const canChangeGradePage = !!Grade?.links?.next;
|
||||||
|
const GradePage = Grade?.meta?.currentPage;
|
||||||
|
|
||||||
|
const sex = [
|
||||||
|
{name:"male" , id :"male"},
|
||||||
|
{name:"female" , id :"female"}
|
||||||
|
]
|
||||||
|
return (
|
||||||
|
<Row className="w-100">
|
||||||
|
<Col>
|
||||||
|
<ValidationField name="first_name" placeholder="first_name" label="first_name" />
|
||||||
|
<ValidationField name="last_name" placeholder="last_name" label="last_name" />
|
||||||
|
<ValidationField name="username" placeholder="username" label="username" />
|
||||||
|
{!isEdit &&
|
||||||
|
<ValidationField name="password" placeholder="password" label="password" />
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<ValidationField name="phone_number" placeholder="contact_number1" label="contact_number1" />
|
||||||
|
<ValidationField
|
||||||
|
searchBy="GradeName"
|
||||||
|
name="grade_id"
|
||||||
|
label="grade"
|
||||||
|
type="Search"
|
||||||
|
option={GradeOption}
|
||||||
|
isLoading={isLoadingGrade}
|
||||||
|
canChangePage={canChangeGradePage}
|
||||||
|
PageName={"GradeCurrentPage"}
|
||||||
|
page={GradePage}
|
||||||
|
|
||||||
|
/>
|
||||||
|
<ValidationField type="Select" name="sex" option={sex} />
|
||||||
|
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Form;
|
||||||
27
src/Pages/Admin/Student/Model/formUtil.ts
Normal file
27
src/Pages/Admin/Student/Model/formUtil.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
import * as Yup from "yup";
|
||||||
|
import { Student, StudentInitialValues } from "../../../../types/Student";
|
||||||
|
|
||||||
|
export const getInitialValues = (
|
||||||
|
objectToEdit: Partial<Student>,
|
||||||
|
): StudentInitialValues => {
|
||||||
|
return {
|
||||||
|
id: objectToEdit?.user_id,
|
||||||
|
first_name: objectToEdit?.first_name ?? "",
|
||||||
|
last_name: objectToEdit?.last_name ?? "",
|
||||||
|
// address: objectToEdit?.address ?? "",
|
||||||
|
// birthday: objectToEdit?.birthday ?? "",
|
||||||
|
// city: objectToEdit?.city ?? "",
|
||||||
|
grade_id: objectToEdit?.grade_id ,
|
||||||
|
// image: objectToEdit?.image ?? "",
|
||||||
|
sex: objectToEdit?.sex ,
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getValidationSchema = () => {
|
||||||
|
// validate input
|
||||||
|
return Yup.object().shape({
|
||||||
|
first_name: Yup.string().required("validation.required"),
|
||||||
|
last_name: Yup.string().required("validation.required"),
|
||||||
|
});
|
||||||
|
};
|
||||||
47
src/Pages/Admin/Student/Page.tsx
Normal file
47
src/Pages/Admin/Student/Page.tsx
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { lazy, Suspense } from "react";
|
||||||
|
import { Spin } from "antd";
|
||||||
|
import useSetPageTitle from "../../../Hooks/useSetPageTitle";
|
||||||
|
import { ModalEnum } from "../../../enums/Model";
|
||||||
|
import { useDeleteStudent } from "../../../api/student";
|
||||||
|
import PageHeader from "../../../Layout/Dashboard/PageHeader";
|
||||||
|
import { canAddStudent } from "../../../utils/hasAbilityFn";
|
||||||
|
import FilterLayout from "../../../Layout/Dashboard/FilterLayout";
|
||||||
|
import FilterForm from "./Model/FilterForm";
|
||||||
|
|
||||||
|
const Table = lazy(() => import("./Table"));
|
||||||
|
const AddModalForm = lazy(() => import("./Model/AddModel"));
|
||||||
|
const EditModalForm = lazy(() => import("./Model/EditModel"));
|
||||||
|
const DeleteModalForm = lazy(
|
||||||
|
() => import("../../../Layout/Dashboard/DeleteModels"),
|
||||||
|
);
|
||||||
|
|
||||||
|
const TableHeader = () => {
|
||||||
|
const [t] = useTranslation();
|
||||||
|
const deleteMutation = useDeleteStudent();
|
||||||
|
|
||||||
|
useSetPageTitle(t(`page_header.student`));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="TableWithHeader">
|
||||||
|
<Suspense fallback={<Spin />}>
|
||||||
|
<PageHeader
|
||||||
|
pageTitle="student"
|
||||||
|
ModelAbility={ModalEnum?.STUDENT_ADD}
|
||||||
|
canAdd={canAddStudent}/>
|
||||||
|
<FilterLayout
|
||||||
|
sub_children={<FilterForm/>}
|
||||||
|
filterTitle="table.student"/>
|
||||||
|
<Table />
|
||||||
|
<AddModalForm />
|
||||||
|
<EditModalForm />
|
||||||
|
<DeleteModalForm
|
||||||
|
deleteMutation={deleteMutation}
|
||||||
|
ModelEnum={ModalEnum?.STUDENT_DELETE}
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableHeader;
|
||||||
13
src/Pages/Admin/Student/Table.tsx
Normal file
13
src/Pages/Admin/Student/Table.tsx
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { useColumns } from "./useTableColumns";
|
||||||
|
import React from "react";
|
||||||
|
import DataTable from "../../../Layout/Dashboard/Table/DataTable";
|
||||||
|
import { useGetAllStudent } from "../../../api/student";
|
||||||
|
|
||||||
|
const App: React.FC = () => {
|
||||||
|
const response = useGetAllStudent({ pagination: true });
|
||||||
|
|
||||||
|
return <DataTable response={response} useColumns={useColumns} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
||||||
|
|
||||||
91
src/Pages/Admin/Student/useTableColumns.tsx
Normal file
91
src/Pages/Admin/Student/useTableColumns.tsx
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
import { TableColumnsType } from "antd";
|
||||||
|
import { Student } from "../../../types/Student";
|
||||||
|
import { FaPlus } from "react-icons/fa";
|
||||||
|
import useModalHandler from "../../../utils/useModalHandler";
|
||||||
|
import { ModalEnum } from "../../../enums/Model";
|
||||||
|
import { useObjectToEdit } from "../../../zustand/ObjectToEditState";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import {
|
||||||
|
canAddStudent,
|
||||||
|
canDeleteStudent,
|
||||||
|
canEditStudent,
|
||||||
|
canShowStudent,
|
||||||
|
} from "../../../utils/hasAbilityFn";
|
||||||
|
import ActionButtons from "../../../Components/Table/ActionButtons";
|
||||||
|
|
||||||
|
export const useColumns = () => {
|
||||||
|
const { handel_open_model } = useModalHandler();
|
||||||
|
|
||||||
|
const { setObjectToEdit } = useObjectToEdit((state) => state);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const handelShow = (record: Student) => {
|
||||||
|
navigate(`${record?.user_id}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handelDelete = (data: Student) => {
|
||||||
|
setObjectToEdit(data);
|
||||||
|
handel_open_model(ModalEnum?.STUDENT_DELETE);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEdit = (record: Student) => {
|
||||||
|
setObjectToEdit(record);
|
||||||
|
handel_open_model(ModalEnum?.STUDENT_EDIT);
|
||||||
|
};
|
||||||
|
const [t] = useTranslation();
|
||||||
|
|
||||||
|
const columns: TableColumnsType<Student> = [
|
||||||
|
{
|
||||||
|
title: t("columns.id"),
|
||||||
|
dataIndex: "id",
|
||||||
|
key: "id",
|
||||||
|
align: "center",
|
||||||
|
render: (_text, record) => record?.user_id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: `${t("columns.first_name")}`,
|
||||||
|
dataIndex: "first_name",
|
||||||
|
key: "first_name",
|
||||||
|
align: "center",
|
||||||
|
render: (_text, record) => record?.first_name,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: `${t("columns.last_name")}`,
|
||||||
|
dataIndex: "last_name",
|
||||||
|
key: "last_name",
|
||||||
|
align: "center",
|
||||||
|
render: (_text, record) => record?.last_name,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: `${t("columns.sex")}`,
|
||||||
|
dataIndex: "sex",
|
||||||
|
key: "sex",
|
||||||
|
align: "center",
|
||||||
|
render: (_text, record) => record?.sex,
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
title: "",
|
||||||
|
|
||||||
|
key: "actions",
|
||||||
|
align: "center",
|
||||||
|
render: (_text, record, index) => {
|
||||||
|
return (
|
||||||
|
<ActionButtons
|
||||||
|
canDelete={canDeleteStudent}
|
||||||
|
canEdit={canEditStudent}
|
||||||
|
canShow={canShowStudent}
|
||||||
|
index={index}
|
||||||
|
onDelete={() => handelDelete(record)}
|
||||||
|
onEdit={() => handleEdit(record)}
|
||||||
|
onShow={() => handelShow(record)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return columns;
|
||||||
|
};
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useContext, useEffect, useMemo } from "react";
|
import React, { Suspense, useContext, useEffect, useMemo } from "react";
|
||||||
import { HolderOutlined } from "@ant-design/icons";
|
import { HolderOutlined } from "@ant-design/icons";
|
||||||
import type { DragEndEvent } from "@dnd-kit/core";
|
import type { DragEndEvent } from "@dnd-kit/core";
|
||||||
import { DndContext } from "@dnd-kit/core";
|
import { DndContext } from "@dnd-kit/core";
|
||||||
|
|
@ -12,10 +12,16 @@ import {
|
||||||
} from "@dnd-kit/sortable";
|
} from "@dnd-kit/sortable";
|
||||||
import { CSS } from "@dnd-kit/utilities";
|
import { CSS } from "@dnd-kit/utilities";
|
||||||
import { Button, Table } from "antd";
|
import { Button, Table } from "antd";
|
||||||
import type { TableColumnsType } from "antd";
|
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { ParamsEnum } from "../../enums/params";
|
import { ParamsEnum } from "../../../enums/params";
|
||||||
import { useGetAllUnit } from "../../api/unit";
|
import { useGetAllUnit, useUpdateUnitOrder } from "../../../api/unit";
|
||||||
|
|
||||||
|
const NotFoundLottie = React.lazy(() => import("../../../Components/Lottie/NotFound/NotFoundLottie"));
|
||||||
|
const LoadingLottie = React.lazy(() => import("../../../Components/Lottie/Loading/LoadingLottie" ));
|
||||||
|
|
||||||
|
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useColumns } from "./useTableColumns";
|
||||||
|
|
||||||
interface DataType {
|
interface DataType {
|
||||||
id: string; // Unique identifier for each row
|
id: string; // Unique identifier for each row
|
||||||
|
|
@ -32,7 +38,7 @@ interface RowContextProps {
|
||||||
|
|
||||||
const RowContext = React.createContext<RowContextProps>({});
|
const RowContext = React.createContext<RowContextProps>({});
|
||||||
|
|
||||||
const DragHandle: React.FC = () => {
|
export const DragHandleUnit: React.FC = () => {
|
||||||
const { setActivatorNodeRef, listeners } = useContext(RowContext);
|
const { setActivatorNodeRef, listeners } = useContext(RowContext);
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -46,12 +52,6 @@ const DragHandle: React.FC = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const columns: TableColumnsType<DataType> = [
|
|
||||||
{ key: "sort", align: "center", width: 80, render: () => <DragHandle /> },
|
|
||||||
{ title: "Name", dataIndex: "name" },
|
|
||||||
{ title: "Age", dataIndex: "age" },
|
|
||||||
{ title: "Address", dataIndex: "address" },
|
|
||||||
];
|
|
||||||
|
|
||||||
interface RowProps extends React.HTMLAttributes<HTMLTableRowElement> {
|
interface RowProps extends React.HTMLAttributes<HTMLTableRowElement> {
|
||||||
"data-row-key": string;
|
"data-row-key": string;
|
||||||
|
|
@ -96,13 +96,10 @@ const DrapableTable: React.FC = () => {
|
||||||
response?.data?.data?.map((item: any, index: number) => ({
|
response?.data?.data?.map((item: any, index: number) => ({
|
||||||
id: item.id, // Ensure this is a unique identifier
|
id: item.id, // Ensure this is a unique identifier
|
||||||
order: index + 1, // Assign order based on index
|
order: index + 1, // Assign order based on index
|
||||||
name: item.name,
|
...item
|
||||||
age: item.age,
|
|
||||||
address: item.address,
|
|
||||||
})) ?? [];
|
})) ?? [];
|
||||||
|
|
||||||
const [dataSource, setDataSource] = React.useState<DataType[]>(data);
|
const [dataSource, setDataSource] = React.useState<DataType[]>(data);
|
||||||
console.log(dataSource, "dataSource");
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Update dataSource when the fetched data changes
|
// Update dataSource when the fetched data changes
|
||||||
|
|
@ -110,28 +107,51 @@ const DrapableTable: React.FC = () => {
|
||||||
setDataSource(sortedData);
|
setDataSource(sortedData);
|
||||||
}, [response?.data?.data]);
|
}, [response?.data?.data]);
|
||||||
|
|
||||||
|
const {mutate:orderUnit} = useUpdateUnitOrder({},{
|
||||||
|
retry:false
|
||||||
|
})
|
||||||
const onDragEnd = ({ active, over }: DragEndEvent) => {
|
const onDragEnd = ({ active, over }: DragEndEvent) => {
|
||||||
if (active.id !== over?.id) {
|
if (active.id !== over?.id) {
|
||||||
setDataSource((prevState) => {
|
setDataSource((prevState) => {
|
||||||
const activeIndex = prevState.findIndex(
|
const activeIndex = prevState.findIndex(
|
||||||
(record) => record.id === active.id,
|
(record) => record.id === active.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
const overIndex = prevState.findIndex(
|
const overIndex = prevState.findIndex(
|
||||||
|
//@ts-ignore
|
||||||
(record) => record.id === over.id,
|
(record) => record.id === over.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Move the items in the array
|
// Move the items in the array
|
||||||
const newState = arrayMove(prevState, activeIndex, overIndex);
|
const newState = arrayMove(prevState, activeIndex, overIndex);
|
||||||
|
const orderedNewState = newState.map((item, index) => ({
|
||||||
// Update the order based on the new positions
|
|
||||||
return newState.map((item, index) => ({
|
|
||||||
...item,
|
...item,
|
||||||
order: index + 1, // Update the order based on the new index
|
order: index + 1, // Update the order based on the new index
|
||||||
}));
|
}));
|
||||||
|
// Update the order based on the new positions
|
||||||
|
const orderedNewStateWithNewChape = orderedNewState?.map((item:any)=>{
|
||||||
|
return {
|
||||||
|
"unit_id":item?.id,
|
||||||
|
"order":item?.order
|
||||||
|
}
|
||||||
|
})
|
||||||
|
orderUnit({units: orderedNewStateWithNewChape, _method:"PUT"})
|
||||||
|
return orderedNewState
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getRowClassName = (record: any, index: number): string => {
|
||||||
|
return index % 2 === 0 ? "even-row" : "odd-row";
|
||||||
|
};
|
||||||
|
const isLoading = response?.isLoading;
|
||||||
|
const [t] = useTranslation()
|
||||||
|
const columns = useColumns();
|
||||||
|
const sortedDataSource = dataSource.sort((a, b) => a.order - b.order) ;
|
||||||
|
console.log(sortedDataSource,"sortedDataSource");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DndContext modifiers={[restrictToVerticalAxis]} onDragEnd={onDragEnd}>
|
<DndContext modifiers={[restrictToVerticalAxis]} onDragEnd={onDragEnd}>
|
||||||
<SortableContext
|
<SortableContext
|
||||||
|
|
@ -141,9 +161,32 @@ const DrapableTable: React.FC = () => {
|
||||||
<Table
|
<Table
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
components={{ body: { row: Row } }}
|
components={{ body: { row: Row } }}
|
||||||
|
//@ts-ignore
|
||||||
columns={columns}
|
columns={columns}
|
||||||
dataSource={dataSource.sort((a, b) => a.order - b.order)} // Sort by order for rendering
|
dataSource={sortedDataSource}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
|
rowClassName={(record, index) => getRowClassName(record, index)}
|
||||||
|
className="DataTable"
|
||||||
|
loading={{
|
||||||
|
spinning: isLoading,
|
||||||
|
indicator:<Suspense fallback={<></>}>
|
||||||
|
<LoadingLottie />
|
||||||
|
</Suspense> ,
|
||||||
|
size: "large",
|
||||||
|
}}
|
||||||
|
locale={{
|
||||||
|
emptyText: (
|
||||||
|
isLoading ?
|
||||||
|
<></>
|
||||||
|
:
|
||||||
|
|
||||||
|
<Suspense fallback={<></>}>
|
||||||
|
<NotFoundLottie
|
||||||
|
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
|
),
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</SortableContext>
|
</SortableContext>
|
||||||
</DndContext>
|
</DndContext>
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import { useObjectToEdit } from "../../../../zustand/ObjectToEditState";
|
||||||
|
|
||||||
const AddModel: React.FC = () => {
|
const AddModel: React.FC = () => {
|
||||||
const { mutate, status } = useAddUnit();
|
const { mutate, status } = useAddUnit();
|
||||||
const { curriculum_id } = useParams();
|
const { subject_id } = useParams();
|
||||||
const { OldObjectToEdit } = useObjectToEdit();
|
const { OldObjectToEdit } = useObjectToEdit();
|
||||||
|
|
||||||
const handleSubmit = (values: any) => {
|
const handleSubmit = (values: any) => {
|
||||||
|
|
@ -21,7 +21,7 @@ const AddModel: React.FC = () => {
|
||||||
|
|
||||||
mutate({
|
mutate({
|
||||||
...values,
|
...values,
|
||||||
curriculum_id: curriculum_id,
|
subject_id: subject_id,
|
||||||
order: order,
|
order: order,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
@ -34,7 +34,7 @@ const AddModel: React.FC = () => {
|
||||||
handleSubmit={handleSubmit}
|
handleSubmit={handleSubmit}
|
||||||
getInitialValues={getInitialValues({})}
|
getInitialValues={getInitialValues({})}
|
||||||
getValidationSchema={getValidationSchema}
|
getValidationSchema={getValidationSchema}
|
||||||
width="60vw"
|
|
||||||
>
|
>
|
||||||
<ModelForm />
|
<ModelForm />
|
||||||
</LayoutModel>
|
</LayoutModel>
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ const EditModel: React.FC = () => {
|
||||||
getInitialValues={getInitialValues(objectToEdit)}
|
getInitialValues={getInitialValues(objectToEdit)}
|
||||||
getValidationSchema={getValidationSchema}
|
getValidationSchema={getValidationSchema}
|
||||||
isAddModal={false}
|
isAddModal={false}
|
||||||
width="60vw"
|
|
||||||
>
|
>
|
||||||
<ModelForm />
|
<ModelForm />
|
||||||
</LayoutModel>
|
</LayoutModel>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { enumToArray } from "../../../../api/utils/enumToArray";
|
||||||
|
|
||||||
const Form = () => {
|
const Form = () => {
|
||||||
const termsArray = enumToArray(TermEnum);
|
const termsArray = enumToArray(TermEnum);
|
||||||
console.log(termsArray, "termsArray");
|
console.log(termsArray);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row className="w-100">
|
<Row className="w-100">
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import { useGetAllSubject } from "../../../api/subject";
|
||||||
import useSetPageTitle from "../../../Hooks/useSetPageTitle";
|
import useSetPageTitle from "../../../Hooks/useSetPageTitle";
|
||||||
import { ModalEnum } from "../../../enums/Model";
|
import { ModalEnum } from "../../../enums/Model";
|
||||||
import { useDeleteUnit } from "../../../api/unit";
|
import { useDeleteUnit } from "../../../api/unit";
|
||||||
import DrapableTable from "./DrapableTable";
|
|
||||||
import { useGetAllGrade } from "../../../api/grade";
|
import { useGetAllGrade } from "../../../api/grade";
|
||||||
import { useGetAllCurriculum } from "../../../api/curriculum";
|
import { useGetAllCurriculum } from "../../../api/curriculum";
|
||||||
import PageHeader from "../../../Layout/Dashboard/PageHeader";
|
import PageHeader from "../../../Layout/Dashboard/PageHeader";
|
||||||
|
|
@ -15,7 +14,7 @@ import FilterLayout from "../../../Layout/Dashboard/FilterLayout";
|
||||||
import FilterForm from "./Model/FilterForm";
|
import FilterForm from "./Model/FilterForm";
|
||||||
import { canAddUnit } from "../../../utils/hasAbilityFn";
|
import { canAddUnit } from "../../../utils/hasAbilityFn";
|
||||||
|
|
||||||
const Table = lazy(() => import("./Table"));
|
const Table = lazy(() => import("./DrapableTable"));
|
||||||
const AddModalForm = lazy(() => import("./Model/AddModel"));
|
const AddModalForm = lazy(() => import("./Model/AddModel"));
|
||||||
const EditModalForm = lazy(() => import("./Model/EditModel"));
|
const EditModalForm = lazy(() => import("./Model/EditModel"));
|
||||||
const DeleteModalForm = lazy(
|
const DeleteModalForm = lazy(
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ import {
|
||||||
} from "../../../utils/hasAbilityFn";
|
} from "../../../utils/hasAbilityFn";
|
||||||
import ActionButtons from "../../../Components/Table/ActionButtons";
|
import ActionButtons from "../../../Components/Table/ActionButtons";
|
||||||
import { Unit } from "../../../types/Unit";
|
import { Unit } from "../../../types/Unit";
|
||||||
|
import { ConvertEnumToTranslate } from "../../../utils/ConvertEnumToTranslate";
|
||||||
|
import { DragHandleUnit } from "./DrapableTable";
|
||||||
|
|
||||||
export const useColumns = () => {
|
export const useColumns = () => {
|
||||||
const { handel_open_model } = useModalHandler();
|
const { handel_open_model } = useModalHandler();
|
||||||
|
|
@ -40,6 +42,8 @@ export const useColumns = () => {
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
|
|
||||||
const columns: TableColumnsType<Unit> = [
|
const columns: TableColumnsType<Unit> = [
|
||||||
|
{ key: "sort", align: "center", width: 80, render: () => <DragHandleUnit /> },
|
||||||
|
|
||||||
{
|
{
|
||||||
title: t("columns.id"),
|
title: t("columns.id"),
|
||||||
dataIndex: "id",
|
dataIndex: "id",
|
||||||
|
|
@ -52,7 +56,7 @@ export const useColumns = () => {
|
||||||
dataIndex: "name",
|
dataIndex: "name",
|
||||||
key: "name",
|
key: "name",
|
||||||
align: "center",
|
align: "center",
|
||||||
render: (text, record) => record?.name,
|
render: (text) => text,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -60,20 +64,10 @@ export const useColumns = () => {
|
||||||
dataIndex: "term",
|
dataIndex: "term",
|
||||||
key: "term",
|
key: "term",
|
||||||
align: "center",
|
align: "center",
|
||||||
render: (text, record) => record?.term,
|
render: (text) => t(ConvertEnumToTranslate(text)),
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
// canAddUnit ? (
|
|
||||||
// <button
|
|
||||||
// onClick={() => handel_open_model(ModalEnum?.UNIT_ADD)}
|
|
||||||
// className="add_button"
|
|
||||||
// >
|
|
||||||
// {t("practical.add")} {t("models.unit")} <FaPlus />
|
|
||||||
// </button>
|
|
||||||
// ) : (
|
|
||||||
// ""
|
|
||||||
// ),
|
|
||||||
|
|
||||||
title: t("columns.procedure"),
|
title: t("columns.procedure"),
|
||||||
key: "actions",
|
key: "actions",
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,10 @@ const Form = () => {
|
||||||
return (
|
return (
|
||||||
<Row className="w-100">
|
<Row className="w-100">
|
||||||
<Col>
|
<Col>
|
||||||
<ValidationField placeholder="name" label="name" name="name" />
|
<ValidationField placeholder="name" label="name" name="name" type="Number" />
|
||||||
|
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
import { FaPlus } from "react-icons/fa";
|
|
||||||
import useModalHandler from "../../../utils/useModalHandler";
|
|
||||||
import { ModalEnum } from "../../../enums/Model";
|
import { ModalEnum } from "../../../enums/Model";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { lazy, Suspense } from "react";
|
import { lazy, Suspense } from "react";
|
||||||
import { Spin } from "antd";
|
import { Spin } from "antd";
|
||||||
import { canAddReport, canAddTags, canAddUser } from "../../../utils/hasAbilityFn";
|
import { canAddUser } from "../../../utils/hasAbilityFn";
|
||||||
import useSetPageTitle from "../../../Hooks/useSetPageTitle";
|
import useSetPageTitle from "../../../Hooks/useSetPageTitle";
|
||||||
import { useDeleteTag } from "../../../api/tags";
|
import { useDeleteTag } from "../../../api/tags";
|
||||||
import PageHeader from "../../../Layout/Dashboard/PageHeader";
|
import PageHeader from "../../../Layout/Dashboard/PageHeader";
|
||||||
|
|
@ -14,12 +12,8 @@ const Table = lazy(() => import("./Table"));
|
||||||
const AddModalForm = lazy(() => import("./Model/AddModel"));
|
const AddModalForm = lazy(() => import("./Model/AddModel"));
|
||||||
const EditModalForm = lazy(() => import("./Model/EditModel"));
|
const EditModalForm = lazy(() => import("./Model/EditModel"));
|
||||||
const DeleteModalForm = lazy(() => import("../../../Layout/Dashboard/DeleteModels"));
|
const DeleteModalForm = lazy(() => import("../../../Layout/Dashboard/DeleteModels"));
|
||||||
const SearchField = lazy(
|
|
||||||
() => import("../../../Components/DataTable/SearchField"),
|
|
||||||
);
|
|
||||||
|
|
||||||
const TableHeader = () => {
|
const TableHeader = () => {
|
||||||
const { handel_open_model } = useModalHandler();
|
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
useSetPageTitle(
|
useSetPageTitle(
|
||||||
t(`page_header.user`),
|
t(`page_header.user`),
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ const App: React.FC = () => {
|
||||||
const response = useGetAllUser({
|
const response = useGetAllUser({
|
||||||
name: searchQuery,
|
name: searchQuery,
|
||||||
pagination: true,
|
pagination: true,
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return <DataTable response={response} useColumns={useColumns} />;
|
return <DataTable response={response} useColumns={useColumns} />;
|
||||||
|
|
|
||||||
194
src/Pages/Admin/lesson/DrapableTable.tsx
Normal file
194
src/Pages/Admin/lesson/DrapableTable.tsx
Normal file
|
|
@ -0,0 +1,194 @@
|
||||||
|
import React, { Suspense, useContext, useEffect, useMemo } from "react";
|
||||||
|
import { HolderOutlined } from "@ant-design/icons";
|
||||||
|
import type { DragEndEvent } from "@dnd-kit/core";
|
||||||
|
import { DndContext } from "@dnd-kit/core";
|
||||||
|
import type { SyntheticListenerMap } from "@dnd-kit/core/dist/hooks/utilities";
|
||||||
|
import { restrictToVerticalAxis } from "@dnd-kit/modifiers";
|
||||||
|
import {
|
||||||
|
arrayMove,
|
||||||
|
SortableContext,
|
||||||
|
useSortable,
|
||||||
|
verticalListSortingStrategy,
|
||||||
|
} from "@dnd-kit/sortable";
|
||||||
|
import { CSS } from "@dnd-kit/utilities";
|
||||||
|
import { Button, Table } from "antd";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import { ParamsEnum } from "../../../enums/params";
|
||||||
|
import { useGetAllLesson, useUpdateLessonOrder } from "../../../api/lesson";
|
||||||
|
|
||||||
|
|
||||||
|
const NotFoundLottie = React.lazy(() => import("../../../Components/Lottie/NotFound/NotFoundLottie"));
|
||||||
|
const LoadingLottie = React.lazy(() => import("../../../Components/Lottie/Loading/LoadingLottie" ));
|
||||||
|
|
||||||
|
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useColumns } from "./useTableColumns";
|
||||||
|
|
||||||
|
interface DataType {
|
||||||
|
id: string; // Unique identifier for each row
|
||||||
|
order: number;
|
||||||
|
name: string;
|
||||||
|
age: number;
|
||||||
|
address: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RowContextProps {
|
||||||
|
setActivatorNodeRef?: (element: HTMLElement | null) => void;
|
||||||
|
listeners?: SyntheticListenerMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RowContext = React.createContext<RowContextProps>({});
|
||||||
|
|
||||||
|
export const DragHandleLesson: React.FC = () => {
|
||||||
|
const { setActivatorNodeRef, listeners } = useContext(RowContext);
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
size="small"
|
||||||
|
icon={<HolderOutlined />}
|
||||||
|
style={{ cursor: "move" }}
|
||||||
|
ref={setActivatorNodeRef}
|
||||||
|
{...listeners}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
interface RowProps extends React.HTMLAttributes<HTMLTableRowElement> {
|
||||||
|
"data-row-key": string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Row: React.FC<RowProps> = (props) => {
|
||||||
|
const {
|
||||||
|
attributes,
|
||||||
|
listeners,
|
||||||
|
setNodeRef,
|
||||||
|
setActivatorNodeRef,
|
||||||
|
transform,
|
||||||
|
transition,
|
||||||
|
isDragging,
|
||||||
|
} = useSortable({ id: props["data-row-key"] });
|
||||||
|
|
||||||
|
const style: React.CSSProperties = {
|
||||||
|
...props.style,
|
||||||
|
transform: CSS.Translate.toString(transform),
|
||||||
|
transition,
|
||||||
|
...(isDragging ? { position: "relative", zIndex: 9999 } : {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const contextValue = useMemo<RowContextProps>(
|
||||||
|
() => ({ setActivatorNodeRef, listeners }),
|
||||||
|
[setActivatorNodeRef, listeners],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RowContext.Provider value={contextValue}>
|
||||||
|
<tr {...props} ref={setNodeRef} style={style} {...attributes} />
|
||||||
|
</RowContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const DrapableTable: React.FC = () => {
|
||||||
|
const { subject_id } = useParams<ParamsEnum>();
|
||||||
|
const response = useGetAllLesson({ subject_id: subject_id, pagination: false });
|
||||||
|
|
||||||
|
// Assuming the response contains a unique id for each item
|
||||||
|
const data =
|
||||||
|
response?.data?.data?.map((item: any, index: number) => ({
|
||||||
|
id: item.id, // Ensure this is a unique identifier
|
||||||
|
order: index + 1, // Assign order based on index
|
||||||
|
...item
|
||||||
|
})) ?? [];
|
||||||
|
|
||||||
|
const [dataSource, setDataSource] = React.useState<DataType[]>(data);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Update dataSource when the fetched data changes
|
||||||
|
const sortedData = data.sort((a: any, b: any) => a.order - b.order);
|
||||||
|
setDataSource(sortedData);
|
||||||
|
}, [response?.data?.data]);
|
||||||
|
|
||||||
|
const {mutate:orderLesson} = useUpdateLessonOrder()
|
||||||
|
const onDragEnd = ({ active, over }: DragEndEvent) => {
|
||||||
|
if (active.id !== over?.id) {
|
||||||
|
setDataSource((prevState) => {
|
||||||
|
const activeIndex = prevState.findIndex(
|
||||||
|
(record) => record.id === active.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
const overIndex = prevState.findIndex(
|
||||||
|
//@ts-ignore
|
||||||
|
(record) => record.id === over.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Move the items in the array
|
||||||
|
const newState = arrayMove(prevState, activeIndex, overIndex);
|
||||||
|
const orderedNewState = newState.map((item, index) => ({
|
||||||
|
...item,
|
||||||
|
order: index + 1, // Update the order based on the new index
|
||||||
|
}));
|
||||||
|
// Update the order based on the new positions
|
||||||
|
const orderedNewStateWithNewChape = orderedNewState?.map((item:any)=>{
|
||||||
|
return {
|
||||||
|
"lesson_id":item?.id,
|
||||||
|
"order":item?.order
|
||||||
|
}
|
||||||
|
})
|
||||||
|
orderLesson({lessons: orderedNewStateWithNewChape, _method:"PUT"})
|
||||||
|
return orderedNewState
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getRowClassName = (record: any, index: number): string => {
|
||||||
|
return index % 2 === 0 ? "even-row" : "odd-row";
|
||||||
|
};
|
||||||
|
const [t] = useTranslation()
|
||||||
|
const columns = useColumns();
|
||||||
|
const sortedDataSource = dataSource.sort((a, b) => a.order - b.order) ;
|
||||||
|
console.log(sortedDataSource,"sortedDataSource");
|
||||||
|
const isLoading = response?.isLoading
|
||||||
|
return (
|
||||||
|
<DndContext modifiers={[restrictToVerticalAxis]} onDragEnd={onDragEnd}>
|
||||||
|
<SortableContext
|
||||||
|
items={dataSource.map((i) => i.id)}
|
||||||
|
strategy={verticalListSortingStrategy}
|
||||||
|
>
|
||||||
|
<Table
|
||||||
|
rowKey="id"
|
||||||
|
components={{ body: { row: Row } }}
|
||||||
|
//@ts-ignore
|
||||||
|
columns={columns}
|
||||||
|
dataSource={sortedDataSource}
|
||||||
|
pagination={false}
|
||||||
|
rowClassName={(record, index) => getRowClassName(record, index)}
|
||||||
|
className="DataTable"
|
||||||
|
loading={{
|
||||||
|
spinning: isLoading,
|
||||||
|
indicator:<Suspense fallback={<></>}>
|
||||||
|
<LoadingLottie />
|
||||||
|
</Suspense> ,
|
||||||
|
size: "large",
|
||||||
|
}}
|
||||||
|
locale={{
|
||||||
|
emptyText: (
|
||||||
|
isLoading ?
|
||||||
|
<></>
|
||||||
|
:
|
||||||
|
|
||||||
|
<Suspense fallback={<></>}>
|
||||||
|
<NotFoundLottie
|
||||||
|
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</SortableContext>
|
||||||
|
</DndContext>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DrapableTable;
|
||||||
|
|
@ -42,6 +42,7 @@ const AddModel: React.FC = () => {
|
||||||
handleSubmit={handleSubmit}
|
handleSubmit={handleSubmit}
|
||||||
getInitialValues={getInitialValues({})}
|
getInitialValues={getInitialValues({})}
|
||||||
getValidationSchema={getValidationSchema}
|
getValidationSchema={getValidationSchema}
|
||||||
|
width="500px"
|
||||||
>
|
>
|
||||||
<ModelForm />
|
<ModelForm />
|
||||||
</LayoutModel>
|
</LayoutModel>
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ const ModalForm: React.FC = () => {
|
||||||
getInitialValues={getInitialValues(objectToEdit)}
|
getInitialValues={getInitialValues(objectToEdit)}
|
||||||
getValidationSchema={getValidationSchema}
|
getValidationSchema={getValidationSchema}
|
||||||
isAddModal={false}
|
isAddModal={false}
|
||||||
|
width="500px"
|
||||||
>
|
>
|
||||||
<ModelForm />
|
<ModelForm />
|
||||||
</LayoutModel>
|
</LayoutModel>
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import FilterLayout from "../../../Layout/Dashboard/FilterLayout";
|
||||||
import FilterForm from "./Model/FilterForm";
|
import FilterForm from "./Model/FilterForm";
|
||||||
import { canAddLesson } from "../../../utils/hasAbilityFn";
|
import { canAddLesson } from "../../../utils/hasAbilityFn";
|
||||||
|
|
||||||
const Table = lazy(() => import("./Table"));
|
const Table = lazy(() => import("./DrapableTable"));
|
||||||
const AddModalForm = lazy(() => import("./Model/AddModel"));
|
const AddModalForm = lazy(() => import("./Model/AddModel"));
|
||||||
const EditModalForm = lazy(() => import("./Model/EditModel"));
|
const EditModalForm = lazy(() => import("./Model/EditModel"));
|
||||||
const DeleteModelsForm = lazy(
|
const DeleteModelsForm = lazy(
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import {
|
||||||
canShowLesson,
|
canShowLesson,
|
||||||
} from "../../../utils/hasAbilityFn";
|
} from "../../../utils/hasAbilityFn";
|
||||||
import ActionButtons from "../../../Components/Table/ActionButtons";
|
import ActionButtons from "../../../Components/Table/ActionButtons";
|
||||||
|
import { DragHandleLesson } from "./DrapableTable";
|
||||||
|
|
||||||
export const useColumns = () => {
|
export const useColumns = () => {
|
||||||
const { handel_open_model } = useModalHandler();
|
const { handel_open_model } = useModalHandler();
|
||||||
|
|
@ -37,6 +38,7 @@ export const useColumns = () => {
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
|
|
||||||
const columns: TableColumnsType<Lesson> = [
|
const columns: TableColumnsType<Lesson> = [
|
||||||
|
{ key: "sort", align: "center", width: 80, render: () => <DragHandleLesson /> },
|
||||||
{
|
{
|
||||||
title: t("columns.id"),
|
title: t("columns.id"),
|
||||||
dataIndex: "id",
|
dataIndex: "id",
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import { useObjectToEdit } from "../../../zustand/ObjectToEditState";
|
||||||
|
|
||||||
import Header from "../../../Components/exercise/Header";
|
import Header from "../../../Components/exercise/Header";
|
||||||
import { Question } from "../../../types/Item";
|
import { Question } from "../../../types/Item";
|
||||||
import BaseForm from "./Model/Malty/Add";
|
import BaseForm from "./Model/Malty/Form";
|
||||||
import ModelForm from "./Model/ModelForm";
|
import ModelForm from "./Model/ModelForm";
|
||||||
const AcceptModal = lazy(() => import("./Model/AcceptModal"));
|
const AcceptModal = lazy(() => import("./Model/AcceptModal"));
|
||||||
|
|
||||||
|
|
@ -42,6 +42,7 @@ const AddPage: React.FC = () => {
|
||||||
isBseQuestion,
|
isBseQuestion,
|
||||||
setTagsSearch,
|
setTagsSearch,
|
||||||
setObjectToEdit,
|
setObjectToEdit,
|
||||||
|
objectToEdit,
|
||||||
setSuccess,
|
setSuccess,
|
||||||
SavedQuestionData,
|
SavedQuestionData,
|
||||||
} = useObjectToEdit();
|
} = useObjectToEdit();
|
||||||
|
|
@ -97,20 +98,19 @@ const AddPage: React.FC = () => {
|
||||||
"/" +
|
"/" +
|
||||||
t("practical.add"),
|
t("practical.add"),
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSubmit = (
|
const handleSubmit = (
|
||||||
values: any,
|
values: any,
|
||||||
{ resetForm }: { resetForm: () => void },
|
{ resetForm }: { resetForm: () => void },
|
||||||
) => {
|
) => {
|
||||||
const DataToSend = structuredClone(values);
|
const DataToSend = structuredClone(values);
|
||||||
console.log(DataToSend);
|
|
||||||
|
|
||||||
setTagsSearch(null);
|
setTagsSearch(null);
|
||||||
const canAnswersBeShuffled = DataToSend?.canAnswersBeShuffled ? 1 : 0;
|
const canAnswersBeShuffled = DataToSend?.canAnswersBeShuffled ? 1 : 0;
|
||||||
if (isBseQuestion || DataToSend?.isBase === 1) {
|
if (isBseQuestion || DataToSend?.isBase === 1) {
|
||||||
const newBseQuestion = {
|
const newBseQuestion = {
|
||||||
subject_id: subject_id,
|
subject_id: subject_id,
|
||||||
content: DataToSend?.content,
|
content: DataToSend?.content,
|
||||||
image: DataToSend?.image ?? "",
|
content_image: DataToSend?.content_image ?? "",
|
||||||
isBase: 1,
|
isBase: 1,
|
||||||
lessons_ids: [lesson_id],
|
lessons_ids: [lesson_id],
|
||||||
canAnswersBeShuffled,
|
canAnswersBeShuffled,
|
||||||
|
|
@ -146,71 +146,77 @@ const AddPage: React.FC = () => {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const tags = processTags(DataToSend);
|
const tags = processTags(DataToSend);
|
||||||
console.log(values,"values");
|
|
||||||
const answers = values?.answers?.map((item:any,index:number)=>{
|
const answers = values?.answers?.map((item:any,index:number)=>{
|
||||||
return {
|
return {
|
||||||
order:index,
|
order:index,
|
||||||
...item
|
...item
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const NewQuestion = {
|
||||||
mutate({
|
|
||||||
...values,
|
...values,
|
||||||
subject_id: subject_id,
|
subject_id: subject_id,
|
||||||
tags,
|
tags,
|
||||||
lessons_ids: [lesson_id],
|
lessons_ids: [lesson_id],
|
||||||
canAnswersBeShuffled,
|
canAnswersBeShuffled,
|
||||||
answers
|
answers
|
||||||
});
|
}
|
||||||
|
console.clear()
|
||||||
|
console.log(NewQuestion,"NewQuestion");
|
||||||
|
|
||||||
|
mutate(NewQuestion);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
// console.log(SavedQuestionData);
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
if (
|
// if (
|
||||||
isSuccessAsync &&
|
// isSuccessAsync &&
|
||||||
(SavedQuestionData?.Questions?.length > 0 ? isSuccess : true)
|
// (SavedQuestionData?.Questions?.length > 0 ? isSuccess : true)
|
||||||
) {
|
// ) {
|
||||||
setObjectToEdit(null);
|
// setObjectToEdit(null);
|
||||||
setSuccess(true);
|
// setSuccess(true);
|
||||||
localStorage.removeItem(QUESTION_OBJECT_KEY);
|
// localStorage.removeItem(QUESTION_OBJECT_KEY);
|
||||||
}
|
// }
|
||||||
if (isSuccess && !SavedQuestionData?.Questions?.length) {
|
// if (isSuccess && !SavedQuestionData?.Questions?.length) {
|
||||||
toast.success(t("validation.the_possess_done_successful"));
|
// toast.success(t("validation.the_possess_done_successful"));
|
||||||
setObjectToEdit(null);
|
// setObjectToEdit(null);
|
||||||
setSuccess(true);
|
// setSuccess(true);
|
||||||
localStorage.removeItem(QUESTION_OBJECT_KEY);
|
// localStorage.removeItem(QUESTION_OBJECT_KEY);
|
||||||
}
|
// }
|
||||||
}, [isSuccess, isSuccessAsync]);
|
// }, [isSuccess, isSuccessAsync]);
|
||||||
|
|
||||||
let cleanedAnswers = cleanObject(SavedQuestionData);
|
// let cleanedAnswers = cleanObject(SavedQuestionData);
|
||||||
let noChange = hasItems(cleanedAnswers);
|
// let noChange = hasItems(cleanedAnswers);
|
||||||
|
// console.log(SavedQuestionData);
|
||||||
|
|
||||||
useSaveOnDisconnect(noChange, QUESTION_OBJECT_KEY, SavedQuestionData);
|
// useSaveOnDisconnect(noChange, QUESTION_OBJECT_KEY, SavedQuestionData);
|
||||||
|
|
||||||
const SavedData = getLocalStorageQuestions(QUESTION_OBJECT_KEY);
|
// const SavedData = getLocalStorageQuestions(QUESTION_OBJECT_KEY);
|
||||||
|
// console.log(SavedData);
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
if (!noChange) {
|
|
||||||
navigate(-1);
|
navigate(-1);
|
||||||
localStorage.removeItem(QUESTION_OBJECT_KEY);
|
// if (!noChange) {
|
||||||
} else {
|
// localStorage.removeItem(QUESTION_OBJECT_KEY);
|
||||||
setIsOpen(ModalEnum?.QUESTION_ACCEPT);
|
// } else {
|
||||||
}
|
// setIsOpen(ModalEnum?.QUESTION_ACCEPT);
|
||||||
|
// }
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isBseQuestion || SavedData?.isBase === 1) {
|
if (isBseQuestion ) {
|
||||||
return (
|
return (
|
||||||
<div className="exercise_add">
|
<div className="exercise_add">
|
||||||
<FormikForm
|
<FormikForm
|
||||||
handleSubmit={handleSubmit}
|
handleSubmit={handleSubmit}
|
||||||
initialValues={getInitialValuesBase(SavedData)}
|
initialValues={getInitialValuesBase(objectToEdit)}
|
||||||
validationSchema={getValidationSchemaBase}
|
validationSchema={getValidationSchemaBase}
|
||||||
>
|
>
|
||||||
<main className="w-100 exercise_add_main">
|
<main className="w-100 exercise_add_main">
|
||||||
<Header />
|
<Header />
|
||||||
<ModelForm/>
|
<BaseForm />
|
||||||
|
|
||||||
<div className="exercise_add_buttons">
|
<div className="exercise_add_buttons">
|
||||||
<div onClick={handleCancel}>{t("practical.back")}</div>
|
<div onClick={handleCancel}>{t("practical.back")}</div>
|
||||||
|
|
@ -236,7 +242,7 @@ const AddPage: React.FC = () => {
|
||||||
<div className="exercise_add">
|
<div className="exercise_add">
|
||||||
<FormikForm
|
<FormikForm
|
||||||
handleSubmit={handleSubmit}
|
handleSubmit={handleSubmit}
|
||||||
initialValues={getInitialValues(SavedData)}
|
initialValues={getInitialValues(objectToEdit)}
|
||||||
validationSchema={getValidationSchema}
|
validationSchema={getValidationSchema}
|
||||||
>
|
>
|
||||||
<main className="w-100 exercise_add_main">
|
<main className="w-100 exercise_add_main">
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import { useObjectToEdit } from "../../../zustand/ObjectToEditState";
|
||||||
import { removeStringKeys } from "../../../utils/removeStringKeys";
|
import { removeStringKeys } from "../../../utils/removeStringKeys";
|
||||||
import SpinContainer from "../../../Components/Layout/SpinContainer";
|
import SpinContainer from "../../../Components/Layout/SpinContainer";
|
||||||
import ModelForm from "./Model/ModelForm";
|
import ModelForm from "./Model/ModelForm";
|
||||||
import BaseForm from "./Model/Malty/Edit";
|
import BaseForm from "./Model/Malty/Form";
|
||||||
import { Question } from "../../../types/Item";
|
import { Question } from "../../../types/Item";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import useSetPageTitle from "../../../Hooks/useSetPageTitle";
|
import useSetPageTitle from "../../../Hooks/useSetPageTitle";
|
||||||
|
|
|
||||||
25
src/Pages/Admin/question/FilterForm.tsx
Normal file
25
src/Pages/Admin/question/FilterForm.tsx
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import React from 'react'
|
||||||
|
import ValidationField from '../../../Components/ValidationField/ValidationField'
|
||||||
|
import { Col, Row } from "reactstrap";
|
||||||
|
|
||||||
|
const FilterForm = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Row>
|
||||||
|
<Col>
|
||||||
|
<ValidationField placeholder="name" label="name" name="name" />
|
||||||
|
<ValidationField placeholder="name" label="name" name="name" />
|
||||||
|
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<ValidationField placeholder="name" label="name" name="name" />
|
||||||
|
<ValidationField placeholder="name" label="name" name="name" />
|
||||||
|
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FilterForm
|
||||||
|
|
@ -15,18 +15,18 @@ const CheckboxField = ({
|
||||||
const formik = useFormikContext<any>();
|
const formik = useFormikContext<any>();
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
const CheckboxhandleChange = (value: any, index: number) => {
|
const CheckboxhandleChange = (value: any, index: number) => {
|
||||||
const allAreZero = formik?.values?.QuestionOptions?.some(
|
const allAreZero = formik?.values?.answers?.some(
|
||||||
(item: any) => item.isCorrect === 1,
|
(item: any) => item.isCorrect === 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (allAreZero) {
|
if (allAreZero) {
|
||||||
formik?.values.QuestionOptions.forEach((item: any, index: number) => {
|
formik?.values.answers.forEach((item: any, index: number) => {
|
||||||
formik.setFieldValue(`QuestionOptions[${index}].isCorrect`, 0);
|
formik.setFieldValue(`answers[${index}].isCorrect`, 0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
formik.setFieldValue(
|
formik.setFieldValue(
|
||||||
`QuestionOptions[${name}].isCorrect`,
|
`answers[${name}].isCorrect`,
|
||||||
value?.target?.checked ? 1 : 0,
|
value?.target?.checked ? 1 : 0,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
@ -35,7 +35,7 @@ const CheckboxField = ({
|
||||||
<Checkbox
|
<Checkbox
|
||||||
onChange={onChange || CheckboxhandleChange}
|
onChange={onChange || CheckboxhandleChange}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
checked={formik.values?.QuestionOptions?.[name]?.isCorrect === 1}
|
checked={formik.values?.answers?.[name]?.isCorrect === 1}
|
||||||
className={className}
|
className={className}
|
||||||
>
|
>
|
||||||
{t(`input.${label ? label : name}`)}
|
{t(`input.${label ? label : name}`)}
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,8 @@ import { useTranslation } from "react-i18next";
|
||||||
import { getCharFromNumber } from "../../../../../utils/getCharFromNumber";
|
import { getCharFromNumber } from "../../../../../utils/getCharFromNumber";
|
||||||
import CheckboxField from "./CheckboxField";
|
import CheckboxField from "./CheckboxField";
|
||||||
import TextField from "./TextField";
|
import TextField from "./TextField";
|
||||||
import File from "./File";
|
import ImageBoxField from "../../../../../Components/CustomFields/ImageBoxField/ImageBoxField";
|
||||||
import { FaCirclePlus, FaDeleteLeft } from "react-icons/fa6";
|
|
||||||
import { GoTrash } from "react-icons/go";
|
import { GoTrash } from "react-icons/go";
|
||||||
import { LuImagePlus } from "react-icons/lu";
|
|
||||||
|
|
||||||
const ChoiceFields = ({ index, data }: { index: number; data: Choice }) => {
|
const ChoiceFields = ({ index, data }: { index: number; data: Choice }) => {
|
||||||
const formik = useFormikContext<any>();
|
const formik = useFormikContext<any>();
|
||||||
|
|
@ -18,46 +16,56 @@ const ChoiceFields = ({ index, data }: { index: number; data: Choice }) => {
|
||||||
|
|
||||||
const handleDeleteChoice = () => {
|
const handleDeleteChoice = () => {
|
||||||
console.log(index);
|
console.log(index);
|
||||||
console.log(formik.values.QuestionOptions[index]);
|
console.log(formik.values.answers[index]);
|
||||||
|
|
||||||
const updatedQuestionOptions = formik.values.QuestionOptions.filter(
|
const updatedAnswers = formik.values.answers.filter(
|
||||||
(_: any, i: any) => i !== index,
|
(_: any, i: any) => i !== index,
|
||||||
);
|
);
|
||||||
|
|
||||||
formik.setFieldValue("QuestionOptions", updatedQuestionOptions);
|
formik.setFieldValue("answers", updatedAnswers);
|
||||||
};
|
};
|
||||||
console.log(formik.values);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<div className="ChoiceFields">
|
<div className="ChoiceFields">
|
||||||
<TextField
|
<TextField
|
||||||
className="textarea_exercise"
|
className="textarea_exercise"
|
||||||
placeholder={"choice"}
|
placeholder={"choice"}
|
||||||
label2={t(`input.choice`) + ` ` + `(${getCharFromNumber(index)})`}
|
label2={t(`input.choice`) + ` ` +`( ${t(`alphabet.${getCharFromNumber(index)}`)} )`}
|
||||||
name={index}
|
name={index}
|
||||||
id={`choice_${index + 1}`}
|
id={`choice_${index + 1}`}
|
||||||
type="TextArea"
|
type="TextArea"
|
||||||
/>
|
/>
|
||||||
<File
|
|
||||||
className="file_exercise"
|
<ImageBoxField name={`answers.${index}.content_image`} />
|
||||||
label={"attachment"}
|
|
||||||
name={index}
|
<div className="answer_status">
|
||||||
type="File"
|
|
||||||
placeholder=""
|
|
||||||
icon={<LuImagePlus/>}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<CheckboxField
|
<CheckboxField
|
||||||
className=""
|
className=""
|
||||||
label="The_correct_answer"
|
label="The_correct_answer"
|
||||||
name={index}
|
name={index}
|
||||||
type="Checkbox"
|
type="Checkbox"
|
||||||
|
|
||||||
/>
|
/>
|
||||||
<p className="delete_question_options">
|
<p className="delete_question_options">
|
||||||
{t("header.delete_choice")}
|
{t("header.delete_choice")}
|
||||||
<GoTrash className="trash_icon" onClick={handleDeleteChoice} size={17} />
|
<GoTrash className="trash_icon" onClick={handleDeleteChoice} size={17} />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="exercise_form_width">
|
||||||
|
|
||||||
|
<ValidationField
|
||||||
|
className=" "
|
||||||
|
placeholder="_"
|
||||||
|
name={`answers.${index}.hint`}
|
||||||
|
label="hint"
|
||||||
|
type="text"
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ const Choices = () => {
|
||||||
return (
|
return (
|
||||||
<DragDropContext onDragEnd={handleDragEnd}>
|
<DragDropContext onDragEnd={handleDragEnd}>
|
||||||
<Droppable droppableId="choices">
|
<Droppable droppableId="choices">
|
||||||
{(provided:any) => (
|
{(provided) => (
|
||||||
<div {...provided.droppableProps} ref={provided.innerRef}>
|
<div {...provided.droppableProps} ref={provided.innerRef}>
|
||||||
{formik?.values?.answers?.map(
|
{formik?.values?.answers?.map(
|
||||||
(item: Choice, index: number) => {
|
(item: Choice, index: number) => {
|
||||||
|
|
@ -50,7 +50,7 @@ const Choices = () => {
|
||||||
draggableId={draggableId}
|
draggableId={draggableId}
|
||||||
index={index}
|
index={index}
|
||||||
>
|
>
|
||||||
{(provided:any) => (
|
{(provided) => (
|
||||||
<div
|
<div
|
||||||
ref={provided.innerRef}
|
ref={provided.innerRef}
|
||||||
{...provided.draggableProps}
|
{...provided.draggableProps}
|
||||||
|
|
|
||||||
|
|
@ -1,87 +0,0 @@
|
||||||
import { Button, Upload, UploadFile } from "antd";
|
|
||||||
import useFormField from "../../../../../Hooks/useFormField";
|
|
||||||
import { UploadOutlined } from "@ant-design/icons";
|
|
||||||
import { useMemo } from "react";
|
|
||||||
|
|
||||||
const File = ({
|
|
||||||
name,
|
|
||||||
label,
|
|
||||||
onChange,
|
|
||||||
isDisabled,
|
|
||||||
placeholder,
|
|
||||||
className,
|
|
||||||
props,
|
|
||||||
icon,
|
|
||||||
}: any) => {
|
|
||||||
const newName = `QuestionOptions[${name}].answer_image`;
|
|
||||||
|
|
||||||
const { formik, t, isError, errorMsg } = useFormField(newName, props);
|
|
||||||
let imageUrl = formik?.values?.QuestionOptions[name]?.answer_image ?? null;
|
|
||||||
// console.log(imageUrl);
|
|
||||||
console.log(imageUrl);
|
|
||||||
|
|
||||||
const fileList: UploadFile[] = useMemo(() => {
|
|
||||||
if (!imageUrl) return [];
|
|
||||||
|
|
||||||
return [
|
|
||||||
typeof imageUrl === "string"
|
|
||||||
? {
|
|
||||||
uid: "-1",
|
|
||||||
name: "uploaded-image",
|
|
||||||
status: "done",
|
|
||||||
url: imageUrl,
|
|
||||||
thumbUrl: imageUrl,
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
uid: imageUrl.uid || "-1",
|
|
||||||
name: imageUrl.name || "uploaded-image",
|
|
||||||
status: "done",
|
|
||||||
originFileObj: imageUrl,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}, [imageUrl]);
|
|
||||||
// console.log(1);
|
|
||||||
|
|
||||||
const FilehandleChange = (value: any) => {
|
|
||||||
// console.log(value,"filevalue");
|
|
||||||
if (value.fileList.length === 0) {
|
|
||||||
formik.setFieldValue(newName, null);
|
|
||||||
} else {
|
|
||||||
formik.setFieldValue(
|
|
||||||
`QuestionOptions[${name}].answer_image`,
|
|
||||||
value?.file?.originFileObj,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const customRequest = async ({ onSuccess, no_label, label_icon }: any) => {
|
|
||||||
onSuccess();
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<div className={`ValidationField upload_image_button ${className ?? ""} `}>
|
|
||||||
<label htmlFor={name} className="text">
|
|
||||||
{t(`input.${label || name}`)}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<Upload
|
|
||||||
disabled={isDisabled}
|
|
||||||
listType="picture"
|
|
||||||
maxCount={1}
|
|
||||||
fileList={[...fileList]}
|
|
||||||
onChange={onChange || FilehandleChange}
|
|
||||||
customRequest={customRequest}
|
|
||||||
className={` w-100`}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
className={isError ? "isError w-100 " : " w-100"}
|
|
||||||
icon={icon ? icon : <UploadOutlined />}
|
|
||||||
>
|
|
||||||
{placeholder ?? t("input.Click_to_upload_the_image")}
|
|
||||||
</Button>
|
|
||||||
<div className="Error_color"> {isError ? "required" : ""}</div>
|
|
||||||
{errorMsg}
|
|
||||||
</Upload>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default File;
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
import { Form, Input } from "antd";
|
|
||||||
import React from "react";
|
|
||||||
import useFormField from "../../../../../Hooks/useFormField";
|
|
||||||
import { MdOutlineEdit } from "react-icons/md";
|
|
||||||
import { Field } from "formik";
|
|
||||||
const { TextArea } = Input;
|
|
||||||
|
|
||||||
const HintField = ({
|
|
||||||
name,
|
|
||||||
label,
|
|
||||||
label2,
|
|
||||||
placeholder,
|
|
||||||
isDisabled,
|
|
||||||
onChange,
|
|
||||||
props,
|
|
||||||
no_label,
|
|
||||||
label_icon,
|
|
||||||
id,
|
|
||||||
className,
|
|
||||||
}: any) => {
|
|
||||||
const newName = `answers[${name}].hint`;
|
|
||||||
const { formik, isError, errorMsg, t } = useFormField(newName, props);
|
|
||||||
const TextFilehandleChange = (
|
|
||||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
|
||||||
) => {
|
|
||||||
// console.log('Change:', e.target.value);
|
|
||||||
formik.setFieldValue(newName, e.target.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={`ValidationField w-100 ${className ?? ""} `}>
|
|
||||||
{no_label ? (
|
|
||||||
<label htmlFor={name} className="text">
|
|
||||||
<span>empty</span>
|
|
||||||
</label>
|
|
||||||
) : label_icon ? (
|
|
||||||
<div className="LabelWithIcon">
|
|
||||||
<label htmlFor={name} className="text">
|
|
||||||
{label2 ? label2 : t(`input.${label ? label : name}`)}
|
|
||||||
</label>
|
|
||||||
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<label htmlFor={name} className="text">
|
|
||||||
{label2 ? label2 : t(`input.${label ? label : name}`)}
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form.Item
|
|
||||||
hasFeedback
|
|
||||||
validateStatus={isError ? "error" : ""}
|
|
||||||
help={isError ? errorMsg : ""}
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
as={Input}
|
|
||||||
placeholder={t(`input.${placeholder ? placeholder : name}`)}
|
|
||||||
name={newName}
|
|
||||||
disabled={isDisabled}
|
|
||||||
size="large"
|
|
||||||
onChange={onChange || TextFilehandleChange}
|
|
||||||
style={{ width: 200 }}
|
|
||||||
id={id}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default React.memo(HintField);
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { Form, Input } from "antd";
|
import { Form, Input } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import useFormField from "../../../../../Hooks/useFormField";
|
||||||
import { MdOutlineEdit } from "react-icons/md";
|
import { MdOutlineEdit } from "react-icons/md";
|
||||||
import { Field } from "formik";
|
import { Field } from "formik";
|
||||||
import useFormField from "../../../../../Hooks/useFormField";
|
|
||||||
const { TextArea } = Input;
|
const { TextArea } = Input;
|
||||||
|
|
||||||
const TextField = ({
|
const TextField = ({
|
||||||
|
|
@ -18,7 +18,7 @@ const TextField = ({
|
||||||
id,
|
id,
|
||||||
className,
|
className,
|
||||||
}: any) => {
|
}: any) => {
|
||||||
const newName = `QuestionOptions[${name}].answer`;
|
const newName = `answers[${name}].content`;
|
||||||
const { formik, isError, errorMsg, t } = useFormField(newName, props);
|
const { formik, isError, errorMsg, t } = useFormField(newName, props);
|
||||||
const TextFilehandleChange = (
|
const TextFilehandleChange = (
|
||||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||||
|
|
|
||||||
|
|
@ -10,17 +10,14 @@ import { useTranslation } from "react-i18next";
|
||||||
import DynamicTags from "./Tags/DynamicTags";
|
import DynamicTags from "./Tags/DynamicTags";
|
||||||
import QuestionFIeld from "./QuestionFIeld/QuestionFIeld";
|
import QuestionFIeld from "./QuestionFIeld/QuestionFIeld";
|
||||||
import { useObjectToEdit } from "../../../../../zustand/ObjectToEditState";
|
import { useObjectToEdit } from "../../../../../zustand/ObjectToEditState";
|
||||||
|
import Choices from "./ChoiceField/Choices";
|
||||||
|
|
||||||
const Form = () => {
|
const Form = () => {
|
||||||
const formik = useFormikContext<any>();
|
const formik = useFormikContext<any>();
|
||||||
const { isOpen } = useModalState((state) => state);
|
|
||||||
// const {data} = useGetAllQuestion();
|
|
||||||
const { setSuccess, Success, setSavedQuestionData } = useObjectToEdit();
|
const { setSuccess, Success, setSavedQuestionData } = useObjectToEdit();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (Success) {
|
if (Success) {
|
||||||
console.log(1);
|
|
||||||
|
|
||||||
formik.setErrors({});
|
formik.setErrors({});
|
||||||
formik.resetForm({ values: {} });
|
formik.resetForm({ values: {} });
|
||||||
setSuccess(false);
|
setSuccess(false);
|
||||||
|
|
@ -32,14 +29,13 @@ const Form = () => {
|
||||||
}, [formik?.values]);
|
}, [formik?.values]);
|
||||||
|
|
||||||
// console.log(formik?.errors);
|
// console.log(formik?.errors);
|
||||||
console.log(formik?.values?.Questions, "formik?.values?.Questions");
|
|
||||||
|
|
||||||
const handleAddChoice = (parent_index: number) => {
|
const handleAddChoice = (parent_index: number) => {
|
||||||
console.log(parent_index);
|
console.log(parent_index);
|
||||||
|
|
||||||
formik.setFieldValue(`Questions.[${parent_index}].QuestionOptions`, [
|
formik.setFieldValue(`Questions.[${parent_index}].answers`, [
|
||||||
...((formik?.values as any)?.Questions?.[parent_index]
|
...((formik?.values as any)?.Questions?.[parent_index]
|
||||||
.QuestionOptions as Choice[]),
|
.answers as Choice[]),
|
||||||
|
|
||||||
{
|
{
|
||||||
answer: null,
|
answer: null,
|
||||||
|
|
@ -58,9 +54,9 @@ const Form = () => {
|
||||||
image: "",
|
image: "",
|
||||||
parent: "",
|
parent: "",
|
||||||
isBase: 0,
|
isBase: 0,
|
||||||
max_mark: 1,
|
// max_mark: 1,
|
||||||
min_mark_to_pass: 1,
|
// min_mark_to_pass: 1,
|
||||||
QuestionOptions: [{ answer: null, answer_image: null, isCorrect: 0 }],
|
answers: [{ answer: null, answer_image: null, isCorrect: 0 }],
|
||||||
tags: [],
|
tags: [],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
@ -70,7 +66,6 @@ const Form = () => {
|
||||||
formik.setFieldValue("max_mark", max_mark);
|
formik.setFieldValue("max_mark", max_mark);
|
||||||
};
|
};
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
console.log(formik.errors);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row className="w-100">
|
<Row className="w-100">
|
||||||
|
|
@ -109,21 +104,9 @@ const Form = () => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{(
|
<Choices parent_index={parent_index} />
|
||||||
(formik?.values as any)?.Questions?.[parent_index]
|
|
||||||
?.QuestionOptions || []
|
|
||||||
).map((item: Choice, index: number) => {
|
|
||||||
return (
|
|
||||||
<ChoiceFields
|
|
||||||
key={index}
|
|
||||||
parent_index={parent_index}
|
|
||||||
index={index}
|
|
||||||
data={item}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
|
|
||||||
{formik?.values?.Questions?.[parent_index]?.QuestionOptions
|
{formik?.values?.Questions?.[parent_index]?.answers
|
||||||
?.length < 5 && (
|
?.length < 5 && (
|
||||||
<p className="add_new_button">
|
<p className="add_new_button">
|
||||||
<FaCirclePlus
|
<FaCirclePlus
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import useFormField from "../../../../../../Hooks/useFormField";
|
||||||
import { Checkbox, Form } from "antd";
|
import { Checkbox, Form } from "antd";
|
||||||
import { useFormik, useFormikContext } from "formik";
|
import { useFormik, useFormikContext } from "formik";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
@ -15,16 +16,15 @@ const CheckboxField = ({
|
||||||
const formik = useFormikContext<any>();
|
const formik = useFormikContext<any>();
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
const CheckboxhandleChange = (value: any) => {
|
const CheckboxhandleChange = (value: any) => {
|
||||||
console.log(value?.target?.checked);
|
|
||||||
|
|
||||||
const allAreZero = formik?.values?.Questions?.[
|
const allAreZero = formik?.values?.Questions?.[
|
||||||
parent_index
|
parent_index
|
||||||
]?.QuestionOptions?.some((item: any) => item.isCorrect === 1);
|
]?.answers?.some((item: any) => item.isCorrect === 1);
|
||||||
if (allAreZero) {
|
if (allAreZero) {
|
||||||
formik?.values?.Questions?.[parent_index]?.QuestionOptions.forEach(
|
formik?.values?.Questions?.[parent_index]?.answers.forEach(
|
||||||
(item: any, index: number) => {
|
(item: any, index: number) => {
|
||||||
formik.setFieldValue(
|
formik.setFieldValue(
|
||||||
`Questions[${parent_index}].QuestionOptions[${index}].isCorrect`,
|
`Questions[${parent_index}].answers[${index}].isCorrect`,
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -32,7 +32,7 @@ const CheckboxField = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
formik.setFieldValue(
|
formik.setFieldValue(
|
||||||
`Questions[${parent_index}].QuestionOptions[${name}].isCorrect`,
|
`Questions[${parent_index}].answers[${name}].isCorrect`,
|
||||||
value?.target?.checked ? 1 : 0,
|
value?.target?.checked ? 1 : 0,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
@ -42,7 +42,7 @@ const CheckboxField = ({
|
||||||
onChange={onChange || CheckboxhandleChange}
|
onChange={onChange || CheckboxhandleChange}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
checked={
|
checked={
|
||||||
formik.values?.Questions?.[parent_index]?.QuestionOptions?.[name]
|
formik.values?.Questions?.[parent_index]?.answers?.[name]
|
||||||
?.isCorrect === 1
|
?.isCorrect === 1
|
||||||
}
|
}
|
||||||
className={className}
|
className={className}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,9 @@ import TextField from "./TextField";
|
||||||
import File from "./File";
|
import File from "./File";
|
||||||
import { FaTrash } from "react-icons/fa";
|
import { FaTrash } from "react-icons/fa";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
|
import HintField from "./HintField";
|
||||||
|
import ImageBoxField from "../../../../../../Components/CustomFields/ImageBoxField/ImageBoxField";
|
||||||
|
import { GoTrash } from "react-icons/go";
|
||||||
|
|
||||||
const ChoiceFields = ({
|
const ChoiceFields = ({
|
||||||
index,
|
index,
|
||||||
|
|
@ -25,7 +28,7 @@ const ChoiceFields = ({
|
||||||
|
|
||||||
const handleDeleteChoice = () => {
|
const handleDeleteChoice = () => {
|
||||||
const arrayLength =
|
const arrayLength =
|
||||||
formik.values.Questions?.[parent_index].QuestionOptions?.length;
|
formik.values.Questions?.[parent_index].answers?.length;
|
||||||
|
|
||||||
console.log(arrayLength);
|
console.log(arrayLength);
|
||||||
|
|
||||||
|
|
@ -36,31 +39,29 @@ const ChoiceFields = ({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedQuestionOptions = formik.values.Questions?.[
|
const updatedAnswers = formik.values.Questions?.[
|
||||||
parent_index
|
parent_index
|
||||||
].QuestionOptions.filter((_: any, i: any) => i !== index);
|
].answers.filter((_: any, i: any) => i !== index);
|
||||||
formik.setFieldValue(
|
formik.setFieldValue(
|
||||||
`Questions[${parent_index}].QuestionOptions`,
|
`Questions[${parent_index}].answers`,
|
||||||
updatedQuestionOptions,
|
updatedAnswers,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<div className="ChoiceFields">
|
<div className="ChoiceFields">
|
||||||
<TextField
|
<TextField
|
||||||
className="textarea_exercise"
|
className="textarea_exercise"
|
||||||
placeholder={"choice"}
|
placeholder={"choice"}
|
||||||
label2={t(`input.choice`) + ` ` + `(${getCharFromNumber(index)})`}
|
label2={t(`input.choice`) + ` ` + `( ${t(`alphabet.${getCharFromNumber(index)}`)} )`}
|
||||||
name={index}
|
name={index}
|
||||||
parent_index={parent_index}
|
parent_index={parent_index}
|
||||||
type="TextArea"
|
type="TextArea"
|
||||||
/>
|
/>
|
||||||
<File
|
|
||||||
className="file_exercise"
|
<ImageBoxField name={`Questions.${parent_index}.answers.${index}.answer_image`} />
|
||||||
label={"attachment"}
|
|
||||||
name={index}
|
<div className="answer_status">
|
||||||
type="File"
|
|
||||||
parent_index={parent_index}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<CheckboxField
|
<CheckboxField
|
||||||
className=""
|
className=""
|
||||||
|
|
@ -70,9 +71,26 @@ const ChoiceFields = ({
|
||||||
parent_index={parent_index}
|
parent_index={parent_index}
|
||||||
/>
|
/>
|
||||||
<p className="delete_question_options">
|
<p className="delete_question_options">
|
||||||
<FaTrash onClick={handleDeleteChoice} size={17} />
|
{t("header.delete_choice")}
|
||||||
|
<GoTrash className="trash_icon" onClick={handleDeleteChoice} size={17} />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className="exercise_form_width">
|
||||||
|
<ValidationField
|
||||||
|
className=" "
|
||||||
|
placeholder="_"
|
||||||
|
name={`Questions.${parent_index}.answers.${index}.hint`}
|
||||||
|
label="hint"
|
||||||
|
type="text"
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,11 @@ const File = ({
|
||||||
parent_index,
|
parent_index,
|
||||||
props,
|
props,
|
||||||
}: any) => {
|
}: any) => {
|
||||||
const newName = `Questions[${parent_index}].QuestionOptions[${name}].answer_image`;
|
const newName = `Questions[${parent_index}].answers[${name}].answer_image`;
|
||||||
|
|
||||||
const { formik, t, isError, errorMsg } = useFormField(newName, props);
|
const { formik, t, isError, errorMsg } = useFormField(newName, props);
|
||||||
let imageUrl =
|
let imageUrl =
|
||||||
formik?.values?.Questions?.[parent_index]?.QuestionOptions[name]
|
formik?.values?.Questions?.[parent_index]?.answers[name]
|
||||||
?.answer_image ?? null;
|
?.answer_image ?? null;
|
||||||
// console.log(imageUrl);
|
// console.log(imageUrl);
|
||||||
|
|
||||||
|
|
@ -49,7 +49,7 @@ const File = ({
|
||||||
formik.setFieldValue(newName, null);
|
formik.setFieldValue(newName, null);
|
||||||
} else {
|
} else {
|
||||||
formik.setFieldValue(
|
formik.setFieldValue(
|
||||||
`Questions[${parent_index}].QuestionOptions[${name}].answer_image`,
|
`Questions[${parent_index}].answers[${name}].answer_image`,
|
||||||
value?.file?.originFileObj,
|
value?.file?.originFileObj,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ const TextField = ({
|
||||||
parent_index,
|
parent_index,
|
||||||
className,
|
className,
|
||||||
}: any) => {
|
}: any) => {
|
||||||
const newName = `Questions[${parent_index}].QuestionOptions[${name}].answer`;
|
const newName = `Questions[${parent_index}].answers[${name}].content`;
|
||||||
const { formik, isError, errorMsg, t } = useFormField(newName, props);
|
const { formik, isError, errorMsg, t } = useFormField(newName, props);
|
||||||
const TextFilehandleChange = (
|
const TextFilehandleChange = (
|
||||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,9 @@ const Form = () => {
|
||||||
const handleAddChoice = (parent_index: number) => {
|
const handleAddChoice = (parent_index: number) => {
|
||||||
console.log(parent_index);
|
console.log(parent_index);
|
||||||
console.log(formik?.values?.Questions);
|
console.log(formik?.values?.Questions);
|
||||||
formik.setFieldValue(`Questions.[${parent_index}].QuestionOptions`, [
|
formik.setFieldValue(`Questions.[${parent_index}].answers`, [
|
||||||
...((formik?.values as any)?.Questions?.[parent_index]
|
...((formik?.values as any)?.Questions?.[parent_index]
|
||||||
.QuestionOptions as Choice[]),
|
.answers as Choice[]),
|
||||||
|
|
||||||
{
|
{
|
||||||
answer: null,
|
answer: null,
|
||||||
|
|
@ -53,7 +53,7 @@ const Form = () => {
|
||||||
isBase: 0,
|
isBase: 0,
|
||||||
max_mark: 1,
|
max_mark: 1,
|
||||||
min_mark_to_pass: 1,
|
min_mark_to_pass: 1,
|
||||||
QuestionOptions: [{ answer: null, answer_image: null, isCorrect: 0 }],
|
answers: [{ answer: null, answer_image: null, isCorrect: 0 }],
|
||||||
tags: [],
|
tags: [],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
@ -103,7 +103,7 @@ const Form = () => {
|
||||||
|
|
||||||
{(
|
{(
|
||||||
(formik?.values as any)?.Questions?.[parent_index]
|
(formik?.values as any)?.Questions?.[parent_index]
|
||||||
?.QuestionOptions || []
|
?.answers || []
|
||||||
).map((item: Choice, index: number) => {
|
).map((item: Choice, index: number) => {
|
||||||
return (
|
return (
|
||||||
<ChoiceFields
|
<ChoiceFields
|
||||||
|
|
@ -114,7 +114,7 @@ const Form = () => {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{formik?.values?.Questions?.[parent_index]?.QuestionOptions
|
{formik?.values?.Questions?.[parent_index]?.answers
|
||||||
?.length < 5 && (
|
?.length < 5 && (
|
||||||
<p className="add_new_button">
|
<p className="add_new_button">
|
||||||
<FaCirclePlus
|
<FaCirclePlus
|
||||||
|
|
|
||||||
134
src/Pages/Admin/question/Model/Malty/Form.tsx
Normal file
134
src/Pages/Admin/question/Model/Malty/Form.tsx
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
import { Col, Row } from "reactstrap";
|
||||||
|
import React, { useEffect } from "react";
|
||||||
|
import ValidationField from "../../../../../Components/ValidationField/ValidationField";
|
||||||
|
import { useFormikContext } from "formik";
|
||||||
|
import { useModalState } from "../../../../../zustand/Modal";
|
||||||
|
import ChoiceFields from "./ChoiceField/ChoiceFields";
|
||||||
|
import { FaCirclePlus } from "react-icons/fa6";
|
||||||
|
import { Choice } from "../../../../../types/Item";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import DynamicTags from "./Tags/DynamicTags";
|
||||||
|
import QuestionFIeld from "./QuestionFIeld/QuestionFIeld";
|
||||||
|
import { useObjectToEdit } from "../../../../../zustand/ObjectToEditState";
|
||||||
|
import Choices from "./ChoiceField/Choices";
|
||||||
|
import ImageBoxField from "../../../../../Components/CustomFields/ImageBoxField/ImageBoxField";
|
||||||
|
import MaltySelectTag from "./Tags/MaltySelectTag";
|
||||||
|
|
||||||
|
const Form = () => {
|
||||||
|
const formik = useFormikContext<any>();
|
||||||
|
const { setSuccess, Success, setSavedQuestionData } = useObjectToEdit();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (Success) {
|
||||||
|
formik.setErrors({});
|
||||||
|
formik.resetForm({ values: {} });
|
||||||
|
setSuccess(false);
|
||||||
|
}
|
||||||
|
}, [Success]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSavedQuestionData(formik.values);
|
||||||
|
}, [formik?.values]);
|
||||||
|
|
||||||
|
// console.log(formik?.errors);
|
||||||
|
|
||||||
|
const handleAddChoice = (parent_index: number) => {
|
||||||
|
console.log(parent_index);
|
||||||
|
|
||||||
|
formik.setFieldValue(`Questions.[${parent_index}].answers`, [
|
||||||
|
...((formik?.values as any)?.Questions?.[parent_index]
|
||||||
|
.answers as Choice[]),
|
||||||
|
|
||||||
|
{
|
||||||
|
answer: null,
|
||||||
|
answer_image: null,
|
||||||
|
isCorrect: 0,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddQuestion = () => {
|
||||||
|
formik.setFieldValue("Questions", [
|
||||||
|
...((formik?.values as any)?.Questions as Choice[]),
|
||||||
|
|
||||||
|
{
|
||||||
|
content: "",
|
||||||
|
image: "",
|
||||||
|
parent: "",
|
||||||
|
isBase: 0,
|
||||||
|
// max_mark: 1,
|
||||||
|
// min_mark_to_pass: 1,
|
||||||
|
answers: [{ answer: null, answer_image: null, isCorrect: 0 }],
|
||||||
|
tags: [],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const max_mark = formik?.values?.max_mark + 1;
|
||||||
|
|
||||||
|
formik.setFieldValue("max_mark", max_mark);
|
||||||
|
};
|
||||||
|
const [t] = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Row className="w-100 exercise_form_container">
|
||||||
|
<div className="exercise_form">
|
||||||
|
|
||||||
|
<ValidationField className="textarea_exercise" name="content" label="main_question" type="TextArea" />
|
||||||
|
<ImageBoxField name="image" />
|
||||||
|
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
<div className=" flex "></div>
|
||||||
|
|
||||||
|
{((formik?.values as any)?.Questions || [])?.map(
|
||||||
|
(item: Choice, parent_index: number) => {
|
||||||
|
return (
|
||||||
|
<div key={parent_index}>
|
||||||
|
<div className="exercise_form">
|
||||||
|
<QuestionFIeld
|
||||||
|
key={parent_index}
|
||||||
|
index={parent_index}
|
||||||
|
data={item}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Choices parent_index={parent_index} />
|
||||||
|
|
||||||
|
{formik?.values?.Questions?.[parent_index]?.answers
|
||||||
|
?.length < 5 && (
|
||||||
|
<p className="add_new_button">
|
||||||
|
<FaCirclePlus
|
||||||
|
onClick={() => handleAddChoice(parent_index)}
|
||||||
|
size={23}
|
||||||
|
/>{" "}
|
||||||
|
{t("header.add_new_choice")}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
<div className="exercise_form_width">
|
||||||
|
|
||||||
|
<ValidationField
|
||||||
|
className=" "
|
||||||
|
placeholder="_"
|
||||||
|
name={`answers.${parent_index}.hint`}
|
||||||
|
label="hint_question"
|
||||||
|
type="text"
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
/>
|
||||||
|
<MaltySelectTag parent_index={parent_index} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
|
||||||
|
<p className="add_new_button">
|
||||||
|
<FaCirclePlus onClick={handleAddQuestion} size={23} />{" "}
|
||||||
|
{t("header.add_new_question")}
|
||||||
|
</p>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Form;
|
||||||
|
|
@ -9,10 +9,13 @@ import File from "./File";
|
||||||
import { FaTrash } from "react-icons/fa";
|
import { FaTrash } from "react-icons/fa";
|
||||||
import { useObjectToEdit } from "../../../../../../zustand/ObjectToEditState";
|
import { useObjectToEdit } from "../../../../../../zustand/ObjectToEditState";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
|
import CheckboxField from "./CheckboxField";
|
||||||
|
import HintField from "./HintField";
|
||||||
|
import ImageBoxField from "../../../../../../Components/CustomFields/ImageBoxField/ImageBoxField";
|
||||||
|
import { GoTrash } from "react-icons/go";
|
||||||
|
|
||||||
const QuestionFIeld = ({ index, data }: { index: number; data: Choice }) => {
|
const QuestionFIeld = ({ index, data }: { index: number; data: Choice }) => {
|
||||||
const formik = useFormikContext<any>();
|
const formik = useFormikContext<any>();
|
||||||
console.log(index);
|
|
||||||
const { setDeletedQuestions, DeletedQuestions } = useObjectToEdit();
|
const { setDeletedQuestions, DeletedQuestions } = useObjectToEdit();
|
||||||
|
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
|
|
@ -25,31 +28,43 @@ const QuestionFIeld = ({ index, data }: { index: number; data: Choice }) => {
|
||||||
if (DeleteQuestionId?.id) {
|
if (DeleteQuestionId?.id) {
|
||||||
setDeletedQuestions([...DeletedQuestions, DeleteQuestionId]);
|
setDeletedQuestions([...DeletedQuestions, DeleteQuestionId]);
|
||||||
}
|
}
|
||||||
const updatedQuestionOptions = formik.values.Questions.filter(
|
const updatedAnswers = formik.values.Questions.filter(
|
||||||
(_: any, i: any) => i !== index,
|
(_: any, i: any) => i !== index,
|
||||||
);
|
);
|
||||||
formik.setFieldValue(`Questions`, updatedQuestionOptions);
|
formik.setFieldValue(`Questions`, updatedAnswers);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
|
||||||
|
<div className="exercise_forms">
|
||||||
<div className="ChoiceFields">
|
<div className="ChoiceFields">
|
||||||
<TextField
|
<TextField
|
||||||
className="textarea_exercise"
|
className="textarea_exercise"
|
||||||
placeholder={"choice"}
|
placeholder={"question"}
|
||||||
label2={t(`input.question`) + ` ` + `${index + 1}`}
|
label2={t(`input.question`) + ` ` + `( ${t(`alphabet.${getCharFromNumber(index)}`)} )`}
|
||||||
name={index}
|
name={index}
|
||||||
|
id={`question_${index + 1}`}
|
||||||
type="TextArea"
|
type="TextArea"
|
||||||
/>
|
/>
|
||||||
<File
|
|
||||||
className="file_exercise"
|
<ImageBoxField name={`Questions.${index}.image`} />
|
||||||
label={"attachment"}
|
|
||||||
name={index}
|
<div className="answer_status">
|
||||||
type="File"
|
|
||||||
/>
|
|
||||||
<p className="delete_question_options">
|
<p className="delete_question_options">
|
||||||
<FaTrash onClick={handleDeleteQuestion} size={17} />
|
{t("header.delete_question")}
|
||||||
|
<GoTrash className="trash_icon" onClick={handleDeleteQuestion} size={17} />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
77
src/Pages/Admin/question/Model/Malty/Tags/MaltySelectTag.tsx
Normal file
77
src/Pages/Admin/question/Model/Malty/Tags/MaltySelectTag.tsx
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
import React, { useState, useMemo } from 'react';
|
||||||
|
import { Select, Spin } from 'antd';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useFormikContext } from 'formik';
|
||||||
|
import { useDebounce } from '../../../../../../utils/useDebounce';
|
||||||
|
import { useGetAllTag } from '../../../../../../api/tags';
|
||||||
|
|
||||||
|
const MaltySelectTag = ({parent_index}:{parent_index:number}) => {
|
||||||
|
const [searchValue, setSearchValue] = useState<string>('');
|
||||||
|
const [fieldValue, setFieldValue] = useState<string>('');
|
||||||
|
const formik = useFormikContext<any>();
|
||||||
|
const values = formik?.values?.Questions?.[parent_index]?.tags;
|
||||||
|
const handleChange = (value: string[]) => {
|
||||||
|
formik.setFieldValue(`Questions.[${parent_index}].tags`,value)
|
||||||
|
setSearchValue('');
|
||||||
|
setFieldValue('');
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSearch = useDebounce((value: string) => {
|
||||||
|
setSearchValue(value);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleFieldChange = (value: string) => {
|
||||||
|
setFieldValue(value);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBlur = () => {
|
||||||
|
setSearchValue('');
|
||||||
|
setFieldValue('');
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data, isLoading } = useGetAllTag({
|
||||||
|
name: searchValue,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [t] = useTranslation();
|
||||||
|
|
||||||
|
const options = data?.data ?? []
|
||||||
|
const additionalData = options?.length < 1 && searchValue.length > 1 && !isLoading ? [{id:`new_${searchValue}`,name:searchValue}] :[];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='SelectTag'>
|
||||||
|
|
||||||
|
<label htmlFor="">
|
||||||
|
{t("models.tag")}
|
||||||
|
</label>
|
||||||
|
<Select
|
||||||
|
mode="multiple"
|
||||||
|
allowClear
|
||||||
|
style={{ width: '100%' ,height:"40px"}}
|
||||||
|
placeholder=""
|
||||||
|
fieldNames={{ label: 'name', value: 'id' }}
|
||||||
|
onChange={handleChange}
|
||||||
|
options={[...options,...additionalData]}
|
||||||
|
filterOption={false}
|
||||||
|
loading={isLoading}
|
||||||
|
notFoundContent={isLoading ? <Spin /> : t("practical.not_found")}
|
||||||
|
onSearch={(value) => {
|
||||||
|
handleSearch(value);
|
||||||
|
handleFieldChange(value);
|
||||||
|
}}
|
||||||
|
searchValue={fieldValue}
|
||||||
|
onDropdownVisibleChange={(open) => {
|
||||||
|
if (!open) {
|
||||||
|
handleBlur();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
value={values ?? []}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MaltySelectTag;
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user