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:
|
||||
|
||||
```bash
|
||||
git clone https://git.point-dev.net/Karimaldeen/zaker.git
|
||||
git clone https://git.point-dev.net/Karimaldeen/Quiz_dashboard.git
|
||||
```
|
||||
|
||||
```bash
|
||||
|
|
|
|||
12
package.json
12
package.json
|
|
@ -4,28 +4,27 @@
|
|||
"private": true,
|
||||
"dependencies": {
|
||||
"@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",
|
||||
"axios": "^1.7.2",
|
||||
"bootstrap": "^5.3.3",
|
||||
"dayjs": "^1.11.11",
|
||||
"formik": "^2.4.6",
|
||||
"html-to-image": "^1.11.11",
|
||||
"i18next": "^23.11.5",
|
||||
"path-to-regexp": "^6.2.2",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"lottie-react": "^2.4.0",
|
||||
"react": "^18.3.1",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-i18next": "^13.5.0",
|
||||
"react-icons": "^4.12.0",
|
||||
"react-mathjax": "^1.0.1",
|
||||
"react-query": "^3.39.3",
|
||||
"react-router-dom": "^6.23.1",
|
||||
"react-toastify": "^9.1.3",
|
||||
"reactstrap": "^9.2.2",
|
||||
"sass": "^1.77.4",
|
||||
"ts-node": "^10.9.2",
|
||||
"vite-plugin-env-compatible": "^2.0.1",
|
||||
"yup": "^1.4.0",
|
||||
"zustand": "^4.5.2"
|
||||
},
|
||||
|
|
@ -61,6 +60,7 @@
|
|||
"@testing-library/user-event": "^13.5.0",
|
||||
"@types/node": "^20.14.0",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-beautiful-dnd": "^13.1.8",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/react-helmet": "^6.1.11",
|
||||
"@vitejs/plugin-legacy": "^5.4.1",
|
||||
|
|
|
|||
231
pnpm-lock.yaml
231
pnpm-lock.yaml
|
|
@ -11,6 +11,18 @@ importers:
|
|||
'@ant-design/icons':
|
||||
specifier: ^5.3.7
|
||||
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:
|
||||
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)
|
||||
|
|
@ -26,18 +38,12 @@ importers:
|
|||
formik:
|
||||
specifier: ^2.4.6
|
||||
version: 2.4.6(react@18.3.1)
|
||||
html-to-image:
|
||||
specifier: ^1.11.11
|
||||
version: 1.11.11
|
||||
i18next:
|
||||
specifier: ^23.11.5
|
||||
version: 23.11.5
|
||||
path-to-regexp:
|
||||
specifier: ^6.2.2
|
||||
version: 6.2.2
|
||||
pdf-lib:
|
||||
specifier: ^1.17.1
|
||||
version: 1.17.1
|
||||
lottie-react:
|
||||
specifier: ^2.4.0
|
||||
version: 2.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
react:
|
||||
specifier: ^18.3.1
|
||||
version: 18.3.1
|
||||
|
|
@ -53,9 +59,6 @@ importers:
|
|||
react-icons:
|
||||
specifier: ^4.12.0
|
||||
version: 4.12.0(react@18.3.1)
|
||||
react-mathjax:
|
||||
specifier: ^1.0.1
|
||||
version: 1.0.1(react@18.3.1)
|
||||
react-query:
|
||||
specifier: ^3.39.3
|
||||
version: 3.39.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
|
|
@ -71,12 +74,6 @@ importers:
|
|||
sass:
|
||||
specifier: ^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:
|
||||
specifier: ^1.4.0
|
||||
version: 1.4.0
|
||||
|
|
@ -99,6 +96,9 @@ importers:
|
|||
'@types/react':
|
||||
specifier: ^18.3.3
|
||||
version: 18.3.3
|
||||
'@types/react-beautiful-dnd':
|
||||
specifier: ^13.1.8
|
||||
version: 13.1.8
|
||||
'@types/react-dom':
|
||||
specifier: ^18.3.0
|
||||
version: 18.3.0
|
||||
|
|
@ -877,6 +877,34 @@ packages:
|
|||
resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==}
|
||||
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':
|
||||
resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==}
|
||||
|
||||
|
|
@ -1122,12 +1150,6 @@ packages:
|
|||
'@jridgewell/trace-mapping@0.3.9':
|
||||
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':
|
||||
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
|
||||
|
||||
|
|
@ -1363,6 +1385,9 @@ packages:
|
|||
'@types/prop-types@15.7.12':
|
||||
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':
|
||||
resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==}
|
||||
|
||||
|
|
@ -1883,13 +1908,6 @@ packages:
|
|||
dom-helpers@5.2.1:
|
||||
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:
|
||||
resolution: {integrity: sha512-ubp5+Ev/VV8KuRoWnfP2QF2Bg+O2ZFdb49DiiNbz2VmgkIqrnyYaqIOqj8A6K/3p1xV0QcU5hBQ1+BmB6ot1OA==}
|
||||
|
||||
|
|
@ -2139,9 +2157,6 @@ packages:
|
|||
html-parse-stringify@3.0.1:
|
||||
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:
|
||||
resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
|
||||
engines: {node: '>= 14'}
|
||||
|
|
@ -2535,9 +2550,6 @@ packages:
|
|||
lines-and-columns@1.2.4:
|
||||
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
||||
|
||||
load-script@1.0.0:
|
||||
resolution: {integrity: sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==}
|
||||
|
||||
loader-runner@4.3.0:
|
||||
resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==}
|
||||
engines: {node: '>=6.11.5'}
|
||||
|
|
@ -2562,6 +2574,15 @@ packages:
|
|||
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
||||
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:
|
||||
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
||||
|
||||
|
|
@ -2714,9 +2735,6 @@ packages:
|
|||
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
pako@1.0.11:
|
||||
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
|
||||
|
||||
parent-module@1.0.1:
|
||||
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
||||
engines: {node: '>=6'}
|
||||
|
|
@ -2743,16 +2761,10 @@ packages:
|
|||
path-parse@1.0.7:
|
||||
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:
|
||||
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
pdf-lib@1.17.1:
|
||||
resolution: {integrity: sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==}
|
||||
|
||||
picocolors@1.0.1:
|
||||
resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
|
||||
|
||||
|
|
@ -3098,11 +3110,6 @@ packages:
|
|||
react-is@18.3.1:
|
||||
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:
|
||||
resolution: {integrity: sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==}
|
||||
peerDependencies:
|
||||
|
|
@ -3540,9 +3547,6 @@ packages:
|
|||
'@swc/wasm':
|
||||
optional: true
|
||||
|
||||
tslib@1.14.1:
|
||||
resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
|
||||
|
||||
tslib@2.6.2:
|
||||
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
|
||||
|
||||
|
|
@ -3633,9 +3637,6 @@ packages:
|
|||
peerDependencies:
|
||||
vite: '>=2.0.0'
|
||||
|
||||
vite-plugin-env-compatible@2.0.1:
|
||||
resolution: {integrity: sha512-DRrOZTg/W44ojVQQfGSMPEgYQGzp5TeIpt9cpaK35hTOC/b2D7Ffl8/RIgK8vQ0mlnDIUgETcA173bnMEkyzdw==}
|
||||
|
||||
vite@5.2.12:
|
||||
resolution: {integrity: sha512-/gC8GxzxMK5ntBwb48pR32GGhENnjtY30G4A0jemunsBkiEZFw60s8InGpN8gkhHEkjnRK1aSAxeQgwvFhUHAA==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
|
|
@ -4764,11 +4765,44 @@ snapshots:
|
|||
'@cspotcode/source-map-support@0.8.1':
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.9
|
||||
optional: true
|
||||
|
||||
'@ctrl/tinycolor@3.6.1': {}
|
||||
|
||||
'@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/unitless@0.7.5': {}
|
||||
|
|
@ -5041,15 +5075,8 @@ snapshots:
|
|||
'@jridgewell/trace-mapping@0.3.9':
|
||||
dependencies:
|
||||
'@jridgewell/resolve-uri': 3.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.4.15
|
||||
|
||||
'@pdf-lib/standard-fonts@1.0.0':
|
||||
dependencies:
|
||||
pako: 1.0.11
|
||||
|
||||
'@pdf-lib/upng@1.0.1':
|
||||
dependencies:
|
||||
pako: 1.0.11
|
||||
'@jridgewell/sourcemap-codec': 1.5.0
|
||||
optional: true
|
||||
|
||||
'@popperjs/core@2.11.8': {}
|
||||
|
||||
|
|
@ -5221,13 +5248,17 @@ snapshots:
|
|||
'@babel/runtime': 7.24.6
|
||||
'@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': {}
|
||||
|
||||
|
|
@ -5299,6 +5330,10 @@ snapshots:
|
|||
|
||||
'@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':
|
||||
dependencies:
|
||||
'@types/react': 18.3.3
|
||||
|
|
@ -5459,6 +5494,7 @@ snapshots:
|
|||
acorn-walk@8.3.3:
|
||||
dependencies:
|
||||
acorn: 8.12.1
|
||||
optional: true
|
||||
|
||||
acorn@8.12.1: {}
|
||||
|
||||
|
|
@ -5557,7 +5593,8 @@ snapshots:
|
|||
normalize-path: 3.0.0
|
||||
picomatch: 2.3.1
|
||||
|
||||
arg@4.1.3: {}
|
||||
arg@4.1.3:
|
||||
optional: true
|
||||
|
||||
argparse@1.0.10:
|
||||
dependencies:
|
||||
|
|
@ -5866,7 +5903,8 @@ snapshots:
|
|||
- supports-color
|
||||
- ts-node
|
||||
|
||||
create-require@1.1.1: {}
|
||||
create-require@1.1.1:
|
||||
optional: true
|
||||
|
||||
cross-spawn@7.0.3:
|
||||
dependencies:
|
||||
|
|
@ -5953,7 +5991,8 @@ snapshots:
|
|||
|
||||
diff-sequences@29.6.3: {}
|
||||
|
||||
diff@4.0.2: {}
|
||||
diff@4.0.2:
|
||||
optional: true
|
||||
|
||||
dom-accessibility-api@0.5.16: {}
|
||||
|
||||
|
|
@ -5962,10 +6001,6 @@ snapshots:
|
|||
'@babel/runtime': 7.24.6
|
||||
csstype: 3.1.3
|
||||
|
||||
dotenv-expand@5.1.0: {}
|
||||
|
||||
dotenv@8.2.0: {}
|
||||
|
||||
electron-to-chromium@1.4.788: {}
|
||||
|
||||
electron-to-chromium@1.5.5: {}
|
||||
|
|
@ -6215,8 +6250,6 @@ snapshots:
|
|||
dependencies:
|
||||
void-elements: 3.1.0
|
||||
|
||||
html-to-image@1.11.11: {}
|
||||
|
||||
http-proxy-agent@7.0.2:
|
||||
dependencies:
|
||||
agent-base: 7.1.1
|
||||
|
|
@ -6796,8 +6829,6 @@ snapshots:
|
|||
|
||||
lines-and-columns@1.2.4: {}
|
||||
|
||||
load-script@1.0.0: {}
|
||||
|
||||
loader-runner@4.3.0: {}
|
||||
|
||||
locate-path@5.0.0:
|
||||
|
|
@ -6816,6 +6847,14 @@ snapshots:
|
|||
dependencies:
|
||||
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:
|
||||
dependencies:
|
||||
yallist: 3.1.1
|
||||
|
|
@ -6948,8 +6987,6 @@ snapshots:
|
|||
|
||||
p-try@2.2.0: {}
|
||||
|
||||
pako@1.0.11: {}
|
||||
|
||||
parent-module@1.0.1:
|
||||
dependencies:
|
||||
callsites: 3.1.0
|
||||
|
|
@ -6974,18 +7011,9 @@ snapshots:
|
|||
|
||||
path-parse@1.0.7: {}
|
||||
|
||||
path-to-regexp@6.2.2: {}
|
||||
|
||||
path-type@4.0.0:
|
||||
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: {}
|
||||
|
||||
picomatch@2.3.1: {}
|
||||
|
|
@ -7416,11 +7444,6 @@ snapshots:
|
|||
|
||||
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):
|
||||
dependencies:
|
||||
'@popperjs/core': 2.11.8
|
||||
|
|
@ -7855,8 +7878,7 @@ snapshots:
|
|||
typescript: 4.9.5
|
||||
v8-compile-cache-lib: 3.0.1
|
||||
yn: 3.1.1
|
||||
|
||||
tslib@1.14.1: {}
|
||||
optional: true
|
||||
|
||||
tslib@2.6.2: {}
|
||||
|
||||
|
|
@ -7919,7 +7941,8 @@ snapshots:
|
|||
dependencies:
|
||||
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:
|
||||
dependencies:
|
||||
|
|
@ -7936,11 +7959,6 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- 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):
|
||||
dependencies:
|
||||
esbuild: 0.20.2
|
||||
|
|
@ -8109,7 +8127,8 @@ snapshots:
|
|||
y18n: 5.0.8
|
||||
yargs-parser: 21.1.1
|
||||
|
||||
yn@3.1.1: {}
|
||||
yn@3.1.1:
|
||||
optional: true
|
||||
|
||||
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 (
|
||||
<div className={`search-field ${isOpen ? "open" : ""}`}>
|
||||
<div className="search-header" onClick={handleToggleDropdown}>
|
||||
<IoSearch className="search__icon" />
|
||||
{/* <IoSearch className="search__icon" /> */}
|
||||
<input
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
|
|
|
|||
|
|
@ -11,12 +11,14 @@ interface Props {
|
|||
options: Option[];
|
||||
placeholder: string;
|
||||
onSelect?: (option: Option) => void;
|
||||
withIcon?:boolean
|
||||
}
|
||||
|
||||
const SearchFieldWithSelect: React.FC<Props> = ({
|
||||
options,
|
||||
placeholder,
|
||||
onSelect,
|
||||
withIcon=false
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [selectedOption, setSelectedOption] = useState<Option | null>(null);
|
||||
|
|
@ -58,17 +60,16 @@ const SearchFieldWithSelect: React.FC<Props> = ({
|
|||
return (
|
||||
<div ref={node} className={`search-field ${isOpen ? "open" : ""}`}>
|
||||
<div className="search-header" onClick={toggleDropdown}>
|
||||
<IoSearch className="search__icon" />
|
||||
{withIcon && <IoSearch className="search__icon" />}
|
||||
|
||||
{/* <p className="search__input_text">{placeholder}</p> */}
|
||||
<input
|
||||
type="text"
|
||||
className="search__input search__input_text"
|
||||
placeholder={selectedOption ? selectedOption.label : ""}
|
||||
// placeholder={selectedOption ? selectedOption.label : placeholder}
|
||||
placeholder={selectedOption ? selectedOption.label : placeholder}
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
<p className="search__input_text">{placeholder}</p>
|
||||
{/* <IoMdArrowDropdown className={`search_select_icon ${isOpen ? 'open' : ''}`} /> */}
|
||||
</div>
|
||||
{(isOpen || (searchTerm !== "" && filteredOptions.length > 0)) && (
|
||||
<div className="search-options">
|
||||
|
|
@ -80,7 +81,7 @@ const SearchFieldWithSelect: React.FC<Props> = ({
|
|||
onClick={() => handleOptionClick(option)}
|
||||
>
|
||||
<div>{option.label}</div>
|
||||
<IoSearch className="search__icon" />
|
||||
{/* {withIcon && <IoSearch className="search__icon" />} */}
|
||||
</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,
|
||||
DataRange,
|
||||
SelectField,
|
||||
Default,
|
||||
CheckboxField,
|
||||
MaltyFile,
|
||||
SearchField,
|
||||
TextField,
|
||||
DropFile,
|
||||
Default,
|
||||
} from "./View";
|
||||
import { ValidationFieldProps, ValidationFieldType } from "./utils/types";
|
||||
import LocalSearchField from "./View/LocalSearch";
|
||||
import NumberFormate from "./View/NumberFormate";
|
||||
import NumberField from "./View/NumberField";
|
||||
|
||||
const components: { [key: string]: React.FC<any> } = {
|
||||
Select: SelectField,
|
||||
|
|
@ -31,12 +30,11 @@ const components: { [key: string]: React.FC<any> } = {
|
|||
MaltyFile: MaltyFile,
|
||||
Checkbox: CheckboxField,
|
||||
NumberFormate: NumberFormate,
|
||||
Number: NumberField,
|
||||
};
|
||||
|
||||
const ValidationField: React.FC<ValidationFieldProps> = React.memo(
|
||||
({ type, ...otherProps }: any) => {
|
||||
const Component = components[type as ValidationFieldType];
|
||||
({ type = "text", ...otherProps }) => {
|
||||
const Component = components[type ?? ("text" as ValidationFieldType)];
|
||||
|
||||
if (!Component) {
|
||||
return <Default {...otherProps} />;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import useFormField from "../../../Hooks/useFormField";
|
||||
import { Checkbox, Form } from "antd";
|
||||
import { getNestedValue } from "../utils/getNestedValue";
|
||||
import { Checkbox } from "antd";
|
||||
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
|
||||
const CheckboxField = ({
|
||||
name,
|
||||
label,
|
||||
|
|
@ -17,12 +17,8 @@ const CheckboxField = ({
|
|||
};
|
||||
|
||||
return (
|
||||
<div className={Group ? "d-inline mt-3 Checkboxs" : ``}>
|
||||
<Form.Item
|
||||
hasFeedback
|
||||
validateStatus={isError ? "error" : ""}
|
||||
help={isError ? errorMsg : ""}
|
||||
>
|
||||
<div className={Group ? "d-inline mt-3 Checkbox" : ``}>
|
||||
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||
<Checkbox
|
||||
onChange={onChange || CheckboxhandleChange}
|
||||
disabled={isDisabled}
|
||||
|
|
@ -31,7 +27,7 @@ const CheckboxField = ({
|
|||
>
|
||||
{t(`input.${label ? label : name}`)}
|
||||
</Checkbox>
|
||||
</Form.Item>
|
||||
</ValidationFieldContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ import { Form, DatePicker } from "antd";
|
|||
import React from "react";
|
||||
import useFormField from "../../../Hooks/useFormField";
|
||||
import { MdOutlineEdit } from "react-icons/md";
|
||||
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
|
||||
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
|
|
@ -24,28 +26,16 @@ const DataRange = ({
|
|||
};
|
||||
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 : name}`)}
|
||||
</label>
|
||||
)}
|
||||
<ValidationFieldLabel
|
||||
name={name}
|
||||
label={label}
|
||||
label_icon={label_icon}
|
||||
no_label={no_label}
|
||||
placeholder={placeholder}
|
||||
t={t}
|
||||
/>
|
||||
|
||||
<Form.Item
|
||||
hasFeedback
|
||||
validateStatus={isError ? "error" : ""}
|
||||
help={isError ? errorMsg : ""}
|
||||
>
|
||||
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||
<RangePicker
|
||||
placeholder={placeholder}
|
||||
size="large"
|
||||
|
|
@ -55,8 +45,9 @@ const DataRange = ({
|
|||
onChange={onChange || onCalendarChange}
|
||||
disabled={isDisabled}
|
||||
defaultValue={formik.values[name]}
|
||||
id={name}
|
||||
/>
|
||||
</Form.Item>
|
||||
</ValidationFieldContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@ import React from "react";
|
|||
import useFormField from "../../../Hooks/useFormField";
|
||||
import { MdOutlineEdit } from "react-icons/md";
|
||||
import dayjs from "dayjs";
|
||||
import { DateEnum } from "../../../enums/Date";
|
||||
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
|
||||
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
|
||||
|
||||
const Date = ({
|
||||
name,
|
||||
|
|
@ -21,34 +24,21 @@ const Date = ({
|
|||
const FormikValue = formik.values[name];
|
||||
const onCalendarChange = (value: any) => {
|
||||
formik.setFieldValue(name, value);
|
||||
// console.log(value,"value ");
|
||||
};
|
||||
|
||||
const Formater = "YYYY/MM/DD";
|
||||
const Formatter = [DateEnum?.FORMATE];
|
||||
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 : name}`)}
|
||||
</label>
|
||||
)}
|
||||
<ValidationFieldLabel
|
||||
name={name}
|
||||
label={label}
|
||||
label_icon={label_icon}
|
||||
no_label={no_label}
|
||||
placeholder={placeholder}
|
||||
t={t}
|
||||
/>
|
||||
|
||||
<Form.Item
|
||||
hasFeedback
|
||||
validateStatus={isError ? "error" : ""}
|
||||
help={isError ? errorMsg : ""}
|
||||
>
|
||||
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||
<DatePicker
|
||||
picker={picker}
|
||||
placeholder={t(`input.${placeholder}`)}
|
||||
|
|
@ -58,10 +48,11 @@ const Date = ({
|
|||
size="large"
|
||||
onChange={onChange || onCalendarChange}
|
||||
disabled={isDisabled}
|
||||
format={Formater}
|
||||
format={Formatter}
|
||||
id={name}
|
||||
/>
|
||||
{/* <DatePicker onChange={onChange} /> */}
|
||||
</Form.Item>
|
||||
</ValidationFieldContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import { Form, Input } from "antd";
|
||||
import { Input } 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";
|
||||
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
|
||||
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
|
||||
import { FieldProps } from "../utils/types";
|
||||
|
||||
const Default = ({
|
||||
name,
|
||||
|
|
@ -14,52 +15,34 @@ const Default = ({
|
|||
type,
|
||||
no_label,
|
||||
label_icon,
|
||||
label2,
|
||||
...props
|
||||
}: ValidationFieldPropsInput) => {
|
||||
}: any) => {
|
||||
const { errorMsg, isError, t } = useFormField(name, props);
|
||||
|
||||
return (
|
||||
<div className="ValidationField w-100">
|
||||
{label2 ? (
|
||||
<label htmlFor={name} className="text">
|
||||
{label2}
|
||||
</label>
|
||||
) : 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>
|
||||
)}
|
||||
<ValidationFieldLabel
|
||||
name={name}
|
||||
label={label}
|
||||
label_icon={label_icon}
|
||||
no_label={no_label}
|
||||
placeholder={placeholder}
|
||||
t={t}
|
||||
/>
|
||||
|
||||
<Form.Item
|
||||
hasFeedback
|
||||
validateStatus={isError ? "error" : ""}
|
||||
help={isError ? errorMsg : ""}
|
||||
>
|
||||
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||
<Field
|
||||
as={Input}
|
||||
type={type ?? "text"}
|
||||
placeholder={t(
|
||||
`input.${placeholder ? placeholder : label ? label : name}`,
|
||||
)}
|
||||
placeholder={t(`input.${placeholder || label || name}`)}
|
||||
name={name}
|
||||
id={name}
|
||||
disabled={isDisabled}
|
||||
size="large"
|
||||
{...(type === "number" && { min: 0 })}
|
||||
{...props}
|
||||
/>
|
||||
</Form.Item>
|
||||
</ValidationFieldContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import { LoadingOutlined, PlusOutlined } from "@ant-design/icons";
|
|||
import { message, Upload } from "antd";
|
||||
import type { GetProp, UploadProps } from "antd";
|
||||
import useFormField from "../../../Hooks/useFormField";
|
||||
import { ImageBaseURL } from "../../../api/config";
|
||||
|
||||
type FileType = Parameters<GetProp<UploadProps, "beforeUpload">>[0];
|
||||
|
||||
|
|
@ -12,7 +11,7 @@ const DropFile = ({
|
|||
label,
|
||||
onChange,
|
||||
isDisabled,
|
||||
placholder,
|
||||
placeholder,
|
||||
className,
|
||||
props,
|
||||
no_label,
|
||||
|
|
@ -23,7 +22,7 @@ const DropFile = ({
|
|||
|
||||
const FormikValue =
|
||||
typeof FormikName === "string"
|
||||
? ImageBaseURL + FormikName
|
||||
? FormikName
|
||||
: FormikName instanceof File
|
||||
? URL.createObjectURL(FormikName)
|
||||
: "";
|
||||
|
|
@ -71,6 +70,7 @@ const DropFile = ({
|
|||
showUploadList={false}
|
||||
customRequest={customRequest}
|
||||
onChange={onChange || handleChange}
|
||||
id={name}
|
||||
>
|
||||
{imageUrl ? (
|
||||
<img
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Button, Upload, UploadFile } from "antd";
|
||||
import useFormField from "../../../Hooks/useFormField";
|
||||
import { UploadOutlined } from "@ant-design/icons";
|
||||
import { useMemo } from "react";
|
||||
import React, { useMemo } from "react";
|
||||
|
||||
const File = ({
|
||||
name,
|
||||
|
|
@ -11,37 +11,34 @@ const File = ({
|
|||
placeholder,
|
||||
className,
|
||||
props,
|
||||
icon,
|
||||
}: any) => {
|
||||
const { formik, t, isError, errorMsg } = useFormField(name, props);
|
||||
let imageUrl = formik?.values?.[name] ?? null;
|
||||
console.log(imageUrl);
|
||||
console.log(typeof imageUrl === "string");
|
||||
|
||||
const fileList: UploadFile[] = useMemo(() => {
|
||||
if (!imageUrl) return [];
|
||||
if (!imageUrl) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
typeof imageUrl === "string"
|
||||
? {
|
||||
uid: "-1",
|
||||
name: "uploaded-image",
|
||||
name: "",
|
||||
status: "done",
|
||||
url: imageUrl,
|
||||
thumbUrl: imageUrl,
|
||||
}
|
||||
: {
|
||||
uid: imageUrl.uid || "-1",
|
||||
name: imageUrl.name || "uploaded-image",
|
||||
name: imageUrl.name || "",
|
||||
status: "done",
|
||||
originFileObj: imageUrl,
|
||||
},
|
||||
];
|
||||
}, [imageUrl]);
|
||||
// console.log(1);
|
||||
|
||||
const FilehandleChange = (value: any) => {
|
||||
// console.log(value,"filevalue");
|
||||
if (value.fileList.length === 0) {
|
||||
formik.setFieldValue(name, null);
|
||||
} else {
|
||||
|
|
@ -65,12 +62,15 @@ const File = ({
|
|||
onChange={onChange || FilehandleChange}
|
||||
customRequest={customRequest}
|
||||
className={` w-100`}
|
||||
id={name}
|
||||
>
|
||||
<Button
|
||||
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>
|
||||
<div className="Error_color"> {isError ? "required" : ""}</div>
|
||||
{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 useFormField from "../../../Hooks/useFormField";
|
||||
import { MdOutlineEdit } from "react-icons/md";
|
||||
import { translateOptions } from "../utils/translatedOptions";
|
||||
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
|
||||
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
|
||||
|
||||
const LocalSelectField = ({
|
||||
name,
|
||||
|
|
@ -13,9 +14,9 @@ const LocalSelectField = ({
|
|||
isMulti,
|
||||
onChange,
|
||||
className,
|
||||
props,
|
||||
no_label,
|
||||
label_icon,
|
||||
...props
|
||||
}: any) => {
|
||||
const { errorMsg, isError, t, formik } = useFormField(name, props);
|
||||
|
||||
|
|
@ -29,13 +30,6 @@ const LocalSelectField = ({
|
|||
option?.label?.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) => {
|
||||
formik.setFieldValue(name, value);
|
||||
if (onChange) onChange(value);
|
||||
|
|
@ -47,35 +41,21 @@ const LocalSelectField = ({
|
|||
|
||||
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 : name}`)}
|
||||
</label>
|
||||
)}
|
||||
<Form.Item
|
||||
hasFeedback
|
||||
validateStatus={isError ? "error" : ""}
|
||||
help={isError ? errorMsg : ""}
|
||||
>
|
||||
<ValidationFieldLabel
|
||||
name={name}
|
||||
label={label}
|
||||
label_icon={label_icon}
|
||||
no_label={no_label}
|
||||
placeholder={placeholder}
|
||||
t={t}
|
||||
/>
|
||||
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||
<Select
|
||||
placeholder={t(
|
||||
`input.${placeholder ? placeholder : label ? label : name}`,
|
||||
)}
|
||||
placeholder={t(`input.${placeholder || label || name}`)}
|
||||
disabled={isDisabled}
|
||||
options={translateOptions(option, t)}
|
||||
size="large"
|
||||
className={`${className} ${isError ? "Select_error" : ""} w-100`}
|
||||
className={`${className} ${isError ? "SelectError" : ""} w-100`}
|
||||
value={formik.values[name]}
|
||||
allowClear
|
||||
{...(isMulti && { mode: "multiple" })}
|
||||
|
|
@ -84,8 +64,11 @@ const LocalSelectField = ({
|
|||
filterOption={handleSearch} // Custom filter function
|
||||
searchValue={searchValue} // Control the search input value
|
||||
onSearch={handleSearchChange} // Update search input value on change
|
||||
id={name}
|
||||
fieldNames={{ label: "name", value: "id" }}
|
||||
{...props}
|
||||
/>
|
||||
</Form.Item>
|
||||
</ValidationFieldContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useMemo } from "react";
|
||||
import { Button, Upload } from "antd";
|
||||
import { UploadOutlined } from "@ant-design/icons";
|
||||
|
||||
import useFormField from "../../../Hooks/useFormField";
|
||||
|
||||
const MaltyFile = ({
|
||||
|
|
@ -15,11 +15,10 @@ const MaltyFile = ({
|
|||
const { formik, t, isError } = useFormField(name, props);
|
||||
let imageUrl = formik?.values?.[name] ?? null;
|
||||
|
||||
// Mapping formik values to fileList format
|
||||
const fileList = imageUrl
|
||||
// Memoizing the fileList to prevent unnecessary recalculations
|
||||
const fileList = useMemo(() => {
|
||||
return imageUrl
|
||||
? imageUrl.map((file: any, index: number) => {
|
||||
// console.log(file,"file");
|
||||
|
||||
return file instanceof File
|
||||
? {
|
||||
uid: index,
|
||||
|
|
@ -37,6 +36,7 @@ const MaltyFile = ({
|
|||
};
|
||||
})
|
||||
: [];
|
||||
}, [imageUrl]); // Dependency array ensures it recalculates only when imageUrl changes
|
||||
|
||||
const FilehandleChange = ({ fileList }: any) => {
|
||||
if (fileList.length === 0) {
|
||||
|
|
@ -48,6 +48,7 @@ const MaltyFile = ({
|
|||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Custom request function
|
||||
const customRequest = async ({ onSuccess }: any) => {
|
||||
// Perform any necessary actions before onSuccess is called
|
||||
|
|
@ -63,11 +64,12 @@ const MaltyFile = ({
|
|||
<Upload
|
||||
disabled={isDisabled}
|
||||
listType="picture"
|
||||
fileList={fileList} // Using fileList instead of defaultFileList
|
||||
fileList={fileList} // Using memoized fileList
|
||||
onChange={onChange || FilehandleChange}
|
||||
customRequest={customRequest}
|
||||
className={`${className} w-100`}
|
||||
multiple // Allow multiple files to be selected
|
||||
id={name}
|
||||
>
|
||||
<Button
|
||||
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 useFormField from "../../../Hooks/useFormField";
|
||||
import { MdOutlineEdit } from "react-icons/md";
|
||||
import { Field } from "formik";
|
||||
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
|
||||
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
|
||||
|
||||
const NumberFormate = ({
|
||||
name,
|
||||
label,
|
||||
placeholder,
|
||||
isDisabled,
|
||||
props,
|
||||
type,
|
||||
no_label,
|
||||
label_icon,
|
||||
...props
|
||||
}: any) => {
|
||||
const { errorMsg, isError, t, formik } = useFormField(name, props);
|
||||
const SelectableChange = (value: {
|
||||
|
|
@ -23,28 +24,15 @@ const NumberFormate = ({
|
|||
};
|
||||
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 : ""}
|
||||
>
|
||||
<ValidationFieldLabel
|
||||
name={name}
|
||||
label={label}
|
||||
label_icon={label_icon}
|
||||
no_label={no_label}
|
||||
placeholder={placeholder}
|
||||
t={t}
|
||||
/>
|
||||
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||
<Field
|
||||
as={InputNumber}
|
||||
formatter={(value: any) =>
|
||||
|
|
@ -57,16 +45,16 @@ const NumberFormate = ({
|
|||
type={type ?? "text"}
|
||||
value={formik.values[name]}
|
||||
onChange={SelectableChange}
|
||||
placeholder={t(
|
||||
`input.${placeholder ? placeholder : label ? label : name}`,
|
||||
)}
|
||||
placeholder={t(`input.${placeholder || label || name}`)}
|
||||
name={name}
|
||||
disabled={isDisabled}
|
||||
size="large"
|
||||
id={name}
|
||||
{...props}
|
||||
|
||||
// onChange={onChange ? onChange : handleChange}
|
||||
/>
|
||||
</Form.Item>
|
||||
</ValidationFieldContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,87 +1,143 @@
|
|||
import { Form, Select, Spin } from "antd";
|
||||
import { Select, Spin } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import useFormField from "../../../Hooks/useFormField";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { MdOutlineEdit } from "react-icons/md";
|
||||
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
|
||||
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
|
||||
import { SearchFieldProps } from "../utils/types";
|
||||
import { useValidationValidationParamState } from "../state/ValidationValidationParamState";
|
||||
import { useDebounce } from "../../../utils/useDebounce";
|
||||
|
||||
const SearchField = ({
|
||||
name,
|
||||
label,
|
||||
placeholder,
|
||||
isDisabled,
|
||||
searchBy,
|
||||
option,
|
||||
searchBy = "search",
|
||||
option = [],
|
||||
isMulti,
|
||||
onChange,
|
||||
className,
|
||||
props,
|
||||
no_label,
|
||||
label_icon,
|
||||
isLoading,
|
||||
}: any) => {
|
||||
canChangePage,
|
||||
PageName,
|
||||
page,
|
||||
...props
|
||||
}: SearchFieldProps) => {
|
||||
const { errorMsg, isError, t, formik } = useFormField(name, props);
|
||||
const [searchQuery, setSearchQuery] = useState<string>("");
|
||||
const navigate = useNavigate();
|
||||
useEffect(() => {
|
||||
const searchParams = new URLSearchParams(window?.location?.search);
|
||||
setSearchQuery(searchParams?.get("search") || "");
|
||||
}, []);
|
||||
const { pushValidationParamState, setValidationParamState } =
|
||||
useValidationValidationParamState();
|
||||
|
||||
const SelectableChange = (value: {
|
||||
value: string;
|
||||
label: React.ReactNode;
|
||||
}) => {
|
||||
const [AllPagesOption, setAllPagesOption] = useState<any>([]);
|
||||
|
||||
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);
|
||||
};
|
||||
const SearchHandleChange = (value: any) => {
|
||||
navigate(`${window?.location?.pathname}?${searchBy}=${value}`, {
|
||||
replace: true,
|
||||
const isCleared = value?.length === 0 || !value;
|
||||
|
||||
if (isCleared) {
|
||||
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 (
|
||||
<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 : name}`)}
|
||||
</label>
|
||||
)}
|
||||
|
||||
<Form.Item
|
||||
hasFeedback
|
||||
validateStatus={isError ? "error" : ""}
|
||||
help={isError ? errorMsg : ""}
|
||||
>
|
||||
<ValidationFieldLabel
|
||||
name={name}
|
||||
label={label}
|
||||
label_icon={label_icon}
|
||||
no_label={no_label}
|
||||
placeholder={placeholder}
|
||||
t={t}
|
||||
/>
|
||||
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||
<Select
|
||||
placeholder={t(
|
||||
`input.${placeholder ? placeholder : label ? label : name}`,
|
||||
)}
|
||||
placeholder={t(`input.${placeholder || label || name}`)}
|
||||
disabled={isDisabled}
|
||||
options={option}
|
||||
options={AllPagesOption}
|
||||
size="large"
|
||||
className={`${className} w-100`}
|
||||
value={formik.values[name]}
|
||||
loading={isLoading}
|
||||
// loading={isLoading}
|
||||
allowClear
|
||||
{...(isMulti && { mode: "multiple" })}
|
||||
onChange={onChange || SelectableChange}
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
notFoundContent={isLoading ? <Spin /> : "لا يوجد"}
|
||||
onSearch={SearchHandleChange}
|
||||
optionFilterProp="name"
|
||||
notFoundContent={isLoading ? <Spin /> : t("validation.undefined")}
|
||||
onSearch={handleChange}
|
||||
onBlur={handleBlur}
|
||||
id={name}
|
||||
onPopupScroll={handleScroll}
|
||||
fieldNames={{ label: "name", value: "id" }}
|
||||
{...props}
|
||||
/>
|
||||
</Form.Item>
|
||||
</ValidationFieldContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import { Form, Select, Spin } from "antd";
|
||||
import { Select } from "antd";
|
||||
import React from "react";
|
||||
import useFormField from "../../../Hooks/useFormField";
|
||||
import { MdOutlineEdit } from "react-icons/md";
|
||||
import { translateOptions } from "../utils/translatedOptions";
|
||||
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
|
||||
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
|
||||
import { SelectFieldProps } from "../utils/types";
|
||||
|
||||
const SelectField = ({
|
||||
name,
|
||||
|
|
@ -13,10 +15,11 @@ const SelectField = ({
|
|||
isMulti,
|
||||
onChange,
|
||||
className,
|
||||
props,
|
||||
no_label,
|
||||
label_icon,
|
||||
}: any) => {
|
||||
isLoading,
|
||||
...props
|
||||
}: SelectFieldProps) => {
|
||||
const { errorMsg, isError, t, formik } = useFormField(name, props);
|
||||
const SelectableChange = (value: {
|
||||
value: string;
|
||||
|
|
@ -24,47 +27,36 @@ const SelectField = ({
|
|||
}) => {
|
||||
formik.setFieldValue(name, value);
|
||||
};
|
||||
// console.log(name,"Select");
|
||||
|
||||
const options = translateOptions(option, t);
|
||||
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 : name}`)}
|
||||
</label>
|
||||
)}
|
||||
<ValidationFieldLabel
|
||||
name={name}
|
||||
label={label}
|
||||
label_icon={label_icon}
|
||||
no_label={no_label}
|
||||
placeholder={placeholder}
|
||||
t={t}
|
||||
/>
|
||||
|
||||
<Form.Item
|
||||
hasFeedback
|
||||
validateStatus={isError ? "error" : ""}
|
||||
help={isError ? errorMsg : ""}
|
||||
>
|
||||
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||
<Select
|
||||
placeholder={t(
|
||||
`input.${placeholder ? placeholder : label ? label : name}`,
|
||||
)}
|
||||
placeholder={t(`input.${placeholder || label || name}`)}
|
||||
disabled={isDisabled}
|
||||
options={translateOptions(option, t)}
|
||||
loading={option?.length < 1}
|
||||
options={options}
|
||||
{...(isLoading && { loading: isLoading })}
|
||||
size="large"
|
||||
className={`${className} ${isError ? "Select_error" : ""} w-100`}
|
||||
className={`${className} ${isError ? "SelectError" : ""} w-100`}
|
||||
value={formik.values[name]}
|
||||
allowClear
|
||||
{...(isMulti && { mode: "multiple" })}
|
||||
onChange={onChange || SelectableChange}
|
||||
showSearch={false}
|
||||
id={name}
|
||||
fieldNames={{label:"name",value:"id"}}
|
||||
{...props}
|
||||
/>
|
||||
</Form.Item>
|
||||
</ValidationFieldContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { Form, Input } from "antd";
|
||||
import { Input } from "antd";
|
||||
import React from "react";
|
||||
import useFormField from "../../../Hooks/useFormField";
|
||||
import { Field } from "formik";
|
||||
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
|
||||
const { TextArea } = Input;
|
||||
|
||||
const TextAreaField = ({
|
||||
|
|
@ -11,14 +12,12 @@ const TextAreaField = ({
|
|||
isDisabled,
|
||||
onChange,
|
||||
props,
|
||||
type,
|
||||
}: any) => {
|
||||
const { formik, isError, errorMsg, t } = useFormField(name, props);
|
||||
|
||||
const handleChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||
) => {
|
||||
// console.log('Change:', e.target.value);
|
||||
formik.setFieldValue(name, e.target.value);
|
||||
};
|
||||
|
||||
|
|
@ -27,11 +26,7 @@ const TextAreaField = ({
|
|||
<label htmlFor={name} className="text">
|
||||
{t(`input.${label ? label : name}`)}
|
||||
</label>
|
||||
<Form.Item
|
||||
hasFeedback
|
||||
validateStatus={isError ? "error" : ""}
|
||||
help={isError ? errorMsg : ""}
|
||||
>
|
||||
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||
<Field
|
||||
as={TextArea}
|
||||
placeholder={t(`input.${placeholder ? placeholder : name}`)}
|
||||
|
|
@ -39,10 +34,10 @@ const TextAreaField = ({
|
|||
disabled={isDisabled}
|
||||
size="large"
|
||||
onChange={onChange || handleChange}
|
||||
|
||||
id={name}
|
||||
// onChange={onChange ? onChange : handleChange}
|
||||
/>
|
||||
</Form.Item>
|
||||
</ValidationFieldContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ import React from "react";
|
|||
import useFormField from "../../../Hooks/useFormField";
|
||||
import { MdOutlineEdit } from "react-icons/md";
|
||||
import { Field } from "formik";
|
||||
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
|
||||
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
|
||||
const { TextArea } = Input;
|
||||
|
||||
const TextField = ({
|
||||
|
|
@ -21,33 +23,20 @@ const TextField = ({
|
|||
const TextFilehandleChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||
) => {
|
||||
// console.log('Change:', e.target.value);
|
||||
formik.setFieldValue(name, 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>
|
||||
)}
|
||||
<ValidationFieldLabel
|
||||
name={name}
|
||||
label={label}
|
||||
label_icon={label_icon}
|
||||
no_label={no_label}
|
||||
placeholder={placeholder}
|
||||
t={t}
|
||||
/>
|
||||
|
||||
<Form.Item
|
||||
hasFeedback
|
||||
validateStatus={isError ? "error" : ""}
|
||||
help={isError ? errorMsg : ""}
|
||||
>
|
||||
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||
<Field
|
||||
as={TextArea}
|
||||
placeholder={t(`input.${placeholder ? placeholder : name}`)}
|
||||
|
|
@ -58,8 +47,9 @@ const TextField = ({
|
|||
maxLength={1000}
|
||||
onChange={onChange || TextFilehandleChange}
|
||||
style={{ height: 120 }}
|
||||
id={name}
|
||||
/>
|
||||
</Form.Item>
|
||||
</ValidationFieldContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import React from "react";
|
|||
import useFormField from "../../../Hooks/useFormField";
|
||||
import { MdOutlineEdit } from "react-icons/md";
|
||||
import dayjs from "dayjs";
|
||||
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
|
||||
|
||||
const Time = ({
|
||||
name,
|
||||
|
|
@ -20,27 +21,19 @@ const Time = ({
|
|||
formik.setFieldValue(name, value);
|
||||
};
|
||||
|
||||
const Formater = "H:mm";
|
||||
const Formatter = "H:mm";
|
||||
const FormikValue = formik.values[name];
|
||||
|
||||
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 : name}`)}
|
||||
</label>
|
||||
)}
|
||||
<ValidationFieldLabel
|
||||
name={name}
|
||||
label={label}
|
||||
label_icon={label_icon}
|
||||
no_label={no_label}
|
||||
placeholder={placeholder}
|
||||
t={t}
|
||||
/>
|
||||
|
||||
<Form.Item
|
||||
hasFeedback
|
||||
|
|
@ -51,14 +44,13 @@ const Time = ({
|
|||
allowClear
|
||||
className={`${className} w-100`}
|
||||
size="large"
|
||||
value={FormikValue ? dayjs(FormikValue, Formater) : null}
|
||||
value={FormikValue ? dayjs(FormikValue, Formatter) : null}
|
||||
onChange={onChange || onCalendarChange}
|
||||
disabled={isDisabled}
|
||||
placeholder={t(
|
||||
`input.${placeholder ? placeholder : label ? label : name}`,
|
||||
)}
|
||||
format={Formater}
|
||||
placeholder={t(`input.${placeholder || label || name}`)}
|
||||
format={Formatter}
|
||||
needConfirm={false}
|
||||
id={name}
|
||||
/>
|
||||
</Form.Item>
|
||||
</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 { useTranslation } from "react-i18next";
|
||||
import { FaExclamationCircle } from "react-icons/fa";
|
||||
import { convert_data_to_select } from "../../Layout/app/Const";
|
||||
|
||||
export {
|
||||
useState,
|
||||
|
|
@ -12,5 +11,4 @@ export {
|
|||
useFormikContext,
|
||||
useTranslation,
|
||||
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;
|
||||
}
|
||||
.ValidationField {
|
||||
margin-bottom: 1.3vw;
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
min-height: 80px;
|
||||
padding-inline: 20px;
|
||||
> * {
|
||||
width: 100%;
|
||||
width: 100% !important;
|
||||
min-width: 150px;
|
||||
}
|
||||
.text,
|
||||
.ant-form-item {
|
||||
|
|
@ -16,213 +19,55 @@
|
|||
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 {
|
||||
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;
|
||||
align-items: center;
|
||||
justify-content: 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;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.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{
|
||||
width: 100%;
|
||||
height: 3vw;
|
||||
border-radius: 0.6vw;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
.ValidationFieldCheckbox{
|
||||
// background-color: red;
|
||||
}
|
||||
|
||||
.TwoSelectGroup {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.TwoSelectGroupbutton {
|
||||
margin-bottom: 20px;
|
||||
/// input hight
|
||||
.ant-form-item-control-input-content{
|
||||
height: var(--fieldHeight);
|
||||
}
|
||||
|
||||
.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 {
|
||||
max-width: 100px;
|
||||
}
|
||||
/// text area
|
||||
///
|
||||
|
||||
.ValidationFieldTextArea{
|
||||
.ant-form-item-control-input-content{
|
||||
min-height: 120px;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
gap: 30px;
|
||||
max-width: 80% !important;
|
||||
}
|
||||
|
||||
.ant-input-textarea-affix-wrapper.ant-input-affix-wrapper {
|
||||
|
||||
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) => {
|
||||
return options.map((opt: any) => ({
|
||||
return options?.map((opt: any) => ({
|
||||
...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 =
|
||||
| "text"
|
||||
| "Select"
|
||||
| "LocalSearch"
|
||||
| "Search"
|
||||
| "DataRange"
|
||||
| "Date"
|
||||
| "Time"
|
||||
| "File"
|
||||
| "MaltyFile"
|
||||
| "DropFile"
|
||||
| "Checkbox"
|
||||
| "number"
|
||||
| "password"
|
||||
| "email"
|
||||
| "TextArea";
|
||||
|
||||
export interface ValidationFieldPropsText {
|
||||
// Common properties for all field types
|
||||
interface BaseFieldProps {
|
||||
name: string;
|
||||
no_label?: boolean;
|
||||
label_icon?: boolean;
|
||||
type: "text";
|
||||
placeholder?: string;
|
||||
label?: string;
|
||||
placeholder?: string;
|
||||
className?: string;
|
||||
isDisabled?: boolean;
|
||||
onChange?: (value: any) => void;
|
||||
dir?: "ltr" | "rtl";
|
||||
no_label?: boolean;
|
||||
label_icon?: boolean;
|
||||
}
|
||||
|
||||
export interface ValidationFieldPropsSelect {
|
||||
name: string;
|
||||
no_label?: boolean;
|
||||
label_icon?: boolean;
|
||||
type: "Select";
|
||||
placeholder?: string;
|
||||
label?: string;
|
||||
className?: string;
|
||||
isDisabled?: boolean;
|
||||
onChange?: any;
|
||||
dir?: "ltr" | "rtl";
|
||||
// Specific field type properties
|
||||
export type SelectFieldProps = BaseFieldProps &
|
||||
SelectProps & {
|
||||
type: "Select" | "LocalSearch";
|
||||
option: any[];
|
||||
isMulti?: boolean;
|
||||
}
|
||||
isLoading?: boolean;
|
||||
searchBy?: string;
|
||||
canChangePage?: boolean;
|
||||
PageName?: string;
|
||||
page?: number;
|
||||
};
|
||||
|
||||
export interface ValidationFieldPropsLocalSearch {
|
||||
name: string;
|
||||
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;
|
||||
export type SearchFieldProps = BaseFieldProps &
|
||||
SelectProps & {
|
||||
type: "Search";
|
||||
placeholder?: string;
|
||||
label?: string;
|
||||
className?: string;
|
||||
isDisabled?: boolean;
|
||||
onChange?: (value: any) => void;
|
||||
dir?: "ltr" | "rtl";
|
||||
option: any[];
|
||||
isMulti?: boolean;
|
||||
isLoading: boolean;
|
||||
searchBy: string;
|
||||
isLoading?: any;
|
||||
}
|
||||
export interface ValidationFieldPropsDataRange {
|
||||
name: string;
|
||||
no_label?: boolean;
|
||||
label_icon?: boolean;
|
||||
type: "DataRange";
|
||||
placeholder?: string;
|
||||
label?: string;
|
||||
className?: string;
|
||||
isDisabled?: boolean;
|
||||
onChange?: (value: any) => void;
|
||||
dir?: "ltr" | "rtl";
|
||||
canChangePage: boolean;
|
||||
PageName: string;
|
||||
page: number;
|
||||
};
|
||||
|
||||
type DateFieldProps = BaseFieldProps & {
|
||||
type: "DataRange" | "Date" | "Time";
|
||||
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";
|
||||
}
|
||||
};
|
||||
|
||||
export interface ValidationFieldPropsTime {
|
||||
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 FileFieldProps = BaseFieldProps & {
|
||||
type: "File" | "MaltyFile" | "DropFile";
|
||||
placeholder?: string;
|
||||
label?: string;
|
||||
className?: string;
|
||||
isDisabled?: boolean;
|
||||
onChange?: (value: any) => void;
|
||||
dir?: "ltr" | "rtl";
|
||||
icon?:any
|
||||
}
|
||||
export interface ValidationFieldPropsCheckbox {
|
||||
name: string;
|
||||
no_label?: boolean;
|
||||
label_icon?: boolean;
|
||||
};
|
||||
|
||||
type CheckboxFieldProps = BaseFieldProps & {
|
||||
type: "Checkbox";
|
||||
label?: string;
|
||||
className?: string;
|
||||
isDisabled?: boolean;
|
||||
onChange?: (value: any) => void;
|
||||
dir?: "ltr" | "rtl";
|
||||
Group?: boolean;
|
||||
}
|
||||
export interface ValidationFieldPropstext {
|
||||
name: string;
|
||||
no_label?: boolean;
|
||||
label_icon?: boolean;
|
||||
};
|
||||
|
||||
export type FieldProps = BaseFieldProps &
|
||||
InputProps & {
|
||||
type?:
|
||||
| "text"
|
||||
| "number"
|
||||
|
|
@ -151,45 +62,19 @@ export interface ValidationFieldPropstext {
|
|||
| "email"
|
||||
| "TextArea"
|
||||
| "NumberFormate";
|
||||
label?: string;
|
||||
label2?: string;
|
||||
className?: string;
|
||||
placeholder?: string;
|
||||
isDisabled?: boolean;
|
||||
onChange?: (value: any) => void;
|
||||
dir?: "ltr" | "rtl";
|
||||
Group?: boolean;
|
||||
[key: string]: any; // Index signature to allow any additional props
|
||||
}
|
||||
|
||||
///// 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;
|
||||
}
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
// Union type for all field types
|
||||
export type ValidationFieldProps =
|
||||
| ValidationFieldPropsInput
|
||||
| ValidationFieldPropsSelect
|
||||
| ValidationFieldPropsLocalSearch
|
||||
| ValidationFieldPropsDataRange
|
||||
| ValidationFieldPropsDate
|
||||
| ValidationFieldPropsTime
|
||||
| ValidationFieldPropsFile
|
||||
| ValidationFieldPropsCheckbox
|
||||
| ValidationFieldPropstext
|
||||
| ValidationFieldPropsSearch;
|
||||
| SelectFieldProps
|
||||
| DateFieldProps
|
||||
| FileFieldProps
|
||||
| CheckboxFieldProps
|
||||
| SearchFieldProps
|
||||
| FieldProps;
|
||||
|
||||
// Validation field type
|
||||
export type ValidationFieldType = ValidationFieldProps["type"];
|
||||
|
|
|
|||
|
|
@ -26,9 +26,12 @@ const Header = () => {
|
|||
|
||||
return (
|
||||
<header className="exercise_add_header mb-4">
|
||||
<article>
|
||||
<img src="/Icon/QuestionIcon.svg" alt="" />
|
||||
<div>
|
||||
{t("practical.add")} {t("models.exercise")}{" "}
|
||||
</div>
|
||||
</article>
|
||||
<div>
|
||||
<GoArrowSwitch onClick={handleChange} className="m-2" />
|
||||
{isBseQuestion || values?.isBase === 1
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import OrderBySelect from '../../Components/Filter/OrderBySelect';
|
|||
import LayoutFilterModal from './LayoutFilterModal';
|
||||
import { BiFilterAlt } from 'react-icons/bi';
|
||||
import { MdKeyboardArrowDown } from "react-icons/md";
|
||||
import SearchField from '../../Components/DataTable/SearchField';
|
||||
|
||||
const FilterLayout = ({filterTitle, sub_children}:{filterTitle:string,sub_children:any}) => {
|
||||
const {t} = useTranslation();
|
||||
|
|
@ -21,7 +22,7 @@ const FilterLayout = ({filterTitle, sub_children}:{filterTitle:string,sub_childr
|
|||
return (
|
||||
<div className='filter_header'>
|
||||
<div className='filter_header_top'>
|
||||
<h1>{t(filterTitle)}</h1>
|
||||
<h4>{t(filterTitle)}</h4>
|
||||
<div className='filter_and_order_by'>
|
||||
<span>
|
||||
<LayoutFilterModal
|
||||
|
|
@ -48,8 +49,8 @@ const FilterLayout = ({filterTitle, sub_children}:{filterTitle:string,sub_childr
|
|||
<p>{t("ادخالات")}</p>
|
||||
</span>
|
||||
<div className="header_search">
|
||||
<SearchFieldWithSelect
|
||||
options={translateArray}
|
||||
<SearchField
|
||||
searchBy=''
|
||||
placeholder={t("practical.search_here")}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { Formik, Form, useFormikContext } from "formik";
|
||||
import React, { ReactNode, useEffect } from "react";
|
||||
import { useValidationState } from "../../Components/ValidationField/utils/ValidationState";
|
||||
import { useModalState } from "../../zustand/Modal";
|
||||
|
||||
interface FormValues {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ const LayoutModel = ({
|
|||
modelTitle,
|
||||
ModelEnum,
|
||||
ModelClassName,
|
||||
width = "45vw",
|
||||
width = "800px",
|
||||
isLoading = false,
|
||||
}: LayoutModalProps) => {
|
||||
const { isOpen, setIsOpen } = useModalState((state) => state);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import React from "react";
|
||||
import React, { lazy, Suspense } from "react";
|
||||
import {
|
||||
useAddKeyToData,
|
||||
Table,
|
||||
useTranslation,
|
||||
usePagination,
|
||||
Loading,
|
||||
EmptyData,
|
||||
} from ".";
|
||||
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> = ({
|
||||
response,
|
||||
|
|
@ -22,29 +22,43 @@ const DataTable: React.FC<DataTableProps> = ({
|
|||
const getRowClassName = (record: any, index: number): string => {
|
||||
return index % 2 === 0 ? "even-row" : "odd-row";
|
||||
};
|
||||
const isRefetching = response?.isRefetching;
|
||||
const isLoading = response?.isLoading;
|
||||
|
||||
return (
|
||||
<Table
|
||||
style={{minHeight:"300px"}}
|
||||
columns={columns}
|
||||
dataSource={dataSource}
|
||||
rowClassName={(record, index) => getRowClassName(record, index)}
|
||||
className="DataTable"
|
||||
loading={{
|
||||
spinning: response?.isLoading,
|
||||
indicator: <Loading />,
|
||||
spinning: isLoading || isRefetching,
|
||||
indicator:<Suspense fallback={<></>}>
|
||||
<LoadingLottie />
|
||||
</Suspense> ,
|
||||
size: "large",
|
||||
}}
|
||||
locale={{
|
||||
emptyText: (
|
||||
<EmptyData
|
||||
loading={response?.isLoading}
|
||||
header={t("Table.header")}
|
||||
info={t("Table.info")}
|
||||
isLoading || isRefetching ?
|
||||
<></>
|
||||
:
|
||||
|
||||
<Suspense fallback={<></>}>
|
||||
<NotFoundLottie
|
||||
|
||||
/>
|
||||
</Suspense>
|
||||
),
|
||||
}}
|
||||
pagination={{
|
||||
...pagination,
|
||||
onChange: handlePageChange,
|
||||
nextIcon:<>{t("practical.next")}</>,
|
||||
prevIcon:<> {t("practical.prev")} </>,
|
||||
className:"pagination_antd"
|
||||
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,11 @@
|
|||
import { Table } from "antd";
|
||||
import { useAddKeyToData } from "../../../Hooks/useAddKeyToData";
|
||||
import Loading from "../../../Components/DataState/Loading";
|
||||
import EmptyData from "../../../Components/DataState/EmptyData";
|
||||
import usePagination from "../usePagination";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export {
|
||||
Table,
|
||||
useAddKeyToData,
|
||||
Loading,
|
||||
EmptyData,
|
||||
usePagination,
|
||||
useTranslation,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -12,14 +12,14 @@ const usePagination = (data: Data) => {
|
|||
|
||||
const [pagination, setPagination] = useState<PaginationAntd>({
|
||||
current: data?.meta?.current_page || 1,
|
||||
pageSize: data?.meta?.per_page || 10,
|
||||
pageSize: data?.meta?.per_page || 2,
|
||||
total: data?.meta?.total || 0,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setPagination({
|
||||
current: data?.meta?.current_page || 1,
|
||||
pageSize: data?.meta?.per_page || 10,
|
||||
pageSize: data?.meta?.per_page || 2,
|
||||
total: data?.meta?.total || 0,
|
||||
});
|
||||
}, [data]);
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ const NavBar = ({isOpen}:{isOpen:boolean}) => {
|
|||
|
||||
return (
|
||||
<div className="NavBar">
|
||||
<Suspense fallback={<SpinContainer />}>
|
||||
<Suspense fallback={<></>}>
|
||||
{/* <span className="navbar_link" onClick={handelNavigate}>
|
||||
<MdOutlineArrowForwardIos /> {PageTitle}
|
||||
</span> */}
|
||||
|
|
@ -40,6 +40,7 @@ const NavBar = ({isOpen}:{isOpen:boolean}) => {
|
|||
<SearchFieldWithSelect
|
||||
options={translateArray}
|
||||
placeholder={t("practical.search_here")}
|
||||
withIcon
|
||||
/>
|
||||
</div>
|
||||
<NavBarRightSide />
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ const AddModel: React.FC = () => {
|
|||
handleSubmit={handleSubmit}
|
||||
getInitialValues={getInitialValues({})}
|
||||
getValidationSchema={getValidationSchema}
|
||||
width="40vw"
|
||||
width="500px"
|
||||
>
|
||||
<ModelForm />
|
||||
</LayoutModel>
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ const EditModel: React.FC = () => {
|
|||
getInitialValues={getInitialValues(objectToEdit)}
|
||||
getValidationSchema={getValidationSchema}
|
||||
isAddModal={false}
|
||||
width="40vw"
|
||||
width="500px"
|
||||
>
|
||||
<ModelForm />
|
||||
</LayoutModel>
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ const TableHeader = () => {
|
|||
return (
|
||||
<div className="TableWithHeader">
|
||||
<Suspense fallback={<Spin />}>
|
||||
|
||||
<PageHeader
|
||||
pageTitle="grade"
|
||||
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 type { DragEndEvent } from "@dnd-kit/core";
|
||||
import { DndContext } from "@dnd-kit/core";
|
||||
|
|
@ -12,10 +12,16 @@ import {
|
|||
} from "@dnd-kit/sortable";
|
||||
import { CSS } from "@dnd-kit/utilities";
|
||||
import { Button, Table } from "antd";
|
||||
import type { TableColumnsType } from "antd";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { ParamsEnum } from "../../enums/params";
|
||||
import { useGetAllUnit } from "../../api/unit";
|
||||
import { ParamsEnum } from "../../../enums/params";
|
||||
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 {
|
||||
id: string; // Unique identifier for each row
|
||||
|
|
@ -32,7 +38,7 @@ interface RowContextProps {
|
|||
|
||||
const RowContext = React.createContext<RowContextProps>({});
|
||||
|
||||
const DragHandle: React.FC = () => {
|
||||
export const DragHandleUnit: React.FC = () => {
|
||||
const { setActivatorNodeRef, listeners } = useContext(RowContext);
|
||||
return (
|
||||
<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> {
|
||||
"data-row-key": string;
|
||||
|
|
@ -96,13 +96,10 @@ const DrapableTable: React.FC = () => {
|
|||
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
|
||||
name: item.name,
|
||||
age: item.age,
|
||||
address: item.address,
|
||||
...item
|
||||
})) ?? [];
|
||||
|
||||
const [dataSource, setDataSource] = React.useState<DataType[]>(data);
|
||||
console.log(dataSource, "dataSource");
|
||||
|
||||
useEffect(() => {
|
||||
// Update dataSource when the fetched data changes
|
||||
|
|
@ -110,28 +107,51 @@ const DrapableTable: React.FC = () => {
|
|||
setDataSource(sortedData);
|
||||
}, [response?.data?.data]);
|
||||
|
||||
const {mutate:orderUnit} = useUpdateUnitOrder({},{
|
||||
retry:false
|
||||
})
|
||||
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);
|
||||
|
||||
// Update the order based on the new positions
|
||||
return newState.map((item, index) => ({
|
||||
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 {
|
||||
"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 (
|
||||
<DndContext modifiers={[restrictToVerticalAxis]} onDragEnd={onDragEnd}>
|
||||
<SortableContext
|
||||
|
|
@ -141,9 +161,32 @@ const DrapableTable: React.FC = () => {
|
|||
<Table
|
||||
rowKey="id"
|
||||
components={{ body: { row: Row } }}
|
||||
//@ts-ignore
|
||||
columns={columns}
|
||||
dataSource={dataSource.sort((a, b) => a.order - b.order)} // Sort by order for rendering
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { useObjectToEdit } from "../../../../zustand/ObjectToEditState";
|
|||
|
||||
const AddModel: React.FC = () => {
|
||||
const { mutate, status } = useAddUnit();
|
||||
const { curriculum_id } = useParams();
|
||||
const { subject_id } = useParams();
|
||||
const { OldObjectToEdit } = useObjectToEdit();
|
||||
|
||||
const handleSubmit = (values: any) => {
|
||||
|
|
@ -21,7 +21,7 @@ const AddModel: React.FC = () => {
|
|||
|
||||
mutate({
|
||||
...values,
|
||||
curriculum_id: curriculum_id,
|
||||
subject_id: subject_id,
|
||||
order: order,
|
||||
});
|
||||
};
|
||||
|
|
@ -34,7 +34,7 @@ const AddModel: React.FC = () => {
|
|||
handleSubmit={handleSubmit}
|
||||
getInitialValues={getInitialValues({})}
|
||||
getValidationSchema={getValidationSchema}
|
||||
width="60vw"
|
||||
|
||||
>
|
||||
<ModelForm />
|
||||
</LayoutModel>
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ const EditModel: React.FC = () => {
|
|||
getInitialValues={getInitialValues(objectToEdit)}
|
||||
getValidationSchema={getValidationSchema}
|
||||
isAddModal={false}
|
||||
width="60vw"
|
||||
|
||||
>
|
||||
<ModelForm />
|
||||
</LayoutModel>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { enumToArray } from "../../../../api/utils/enumToArray";
|
|||
|
||||
const Form = () => {
|
||||
const termsArray = enumToArray(TermEnum);
|
||||
console.log(termsArray, "termsArray");
|
||||
console.log(termsArray);
|
||||
|
||||
return (
|
||||
<Row className="w-100">
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import { useGetAllSubject } from "../../../api/subject";
|
|||
import useSetPageTitle from "../../../Hooks/useSetPageTitle";
|
||||
import { ModalEnum } from "../../../enums/Model";
|
||||
import { useDeleteUnit } from "../../../api/unit";
|
||||
import DrapableTable from "./DrapableTable";
|
||||
import { useGetAllGrade } from "../../../api/grade";
|
||||
import { useGetAllCurriculum } from "../../../api/curriculum";
|
||||
import PageHeader from "../../../Layout/Dashboard/PageHeader";
|
||||
|
|
@ -15,7 +14,7 @@ import FilterLayout from "../../../Layout/Dashboard/FilterLayout";
|
|||
import FilterForm from "./Model/FilterForm";
|
||||
import { canAddUnit } from "../../../utils/hasAbilityFn";
|
||||
|
||||
const Table = lazy(() => import("./Table"));
|
||||
const Table = lazy(() => import("./DrapableTable"));
|
||||
const AddModalForm = lazy(() => import("./Model/AddModel"));
|
||||
const EditModalForm = lazy(() => import("./Model/EditModel"));
|
||||
const DeleteModalForm = lazy(
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ import {
|
|||
} from "../../../utils/hasAbilityFn";
|
||||
import ActionButtons from "../../../Components/Table/ActionButtons";
|
||||
import { Unit } from "../../../types/Unit";
|
||||
import { ConvertEnumToTranslate } from "../../../utils/ConvertEnumToTranslate";
|
||||
import { DragHandleUnit } from "./DrapableTable";
|
||||
|
||||
export const useColumns = () => {
|
||||
const { handel_open_model } = useModalHandler();
|
||||
|
|
@ -40,6 +42,8 @@ export const useColumns = () => {
|
|||
const [t] = useTranslation();
|
||||
|
||||
const columns: TableColumnsType<Unit> = [
|
||||
{ key: "sort", align: "center", width: 80, render: () => <DragHandleUnit /> },
|
||||
|
||||
{
|
||||
title: t("columns.id"),
|
||||
dataIndex: "id",
|
||||
|
|
@ -52,7 +56,7 @@ export const useColumns = () => {
|
|||
dataIndex: "name",
|
||||
key: "name",
|
||||
align: "center",
|
||||
render: (text, record) => record?.name,
|
||||
render: (text) => text,
|
||||
},
|
||||
|
||||
{
|
||||
|
|
@ -60,20 +64,10 @@ export const useColumns = () => {
|
|||
dataIndex: "term",
|
||||
key: "term",
|
||||
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"),
|
||||
key: "actions",
|
||||
|
|
|
|||
|
|
@ -5,8 +5,10 @@ const Form = () => {
|
|||
return (
|
||||
<Row className="w-100">
|
||||
<Col>
|
||||
<ValidationField placeholder="name" label="name" name="name" />
|
||||
<ValidationField placeholder="name" label="name" name="name" type="Number" />
|
||||
|
||||
</Col>
|
||||
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
import { FaPlus } from "react-icons/fa";
|
||||
import useModalHandler from "../../../utils/useModalHandler";
|
||||
import { ModalEnum } from "../../../enums/Model";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { lazy, Suspense } from "react";
|
||||
import { Spin } from "antd";
|
||||
import { canAddReport, canAddTags, canAddUser } from "../../../utils/hasAbilityFn";
|
||||
import { canAddUser } from "../../../utils/hasAbilityFn";
|
||||
import useSetPageTitle from "../../../Hooks/useSetPageTitle";
|
||||
import { useDeleteTag } from "../../../api/tags";
|
||||
import PageHeader from "../../../Layout/Dashboard/PageHeader";
|
||||
|
|
@ -14,12 +12,8 @@ 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 SearchField = lazy(
|
||||
() => import("../../../Components/DataTable/SearchField"),
|
||||
);
|
||||
|
||||
const TableHeader = () => {
|
||||
const { handel_open_model } = useModalHandler();
|
||||
const [t] = useTranslation();
|
||||
useSetPageTitle(
|
||||
t(`page_header.user`),
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ const App: React.FC = () => {
|
|||
const response = useGetAllUser({
|
||||
name: searchQuery,
|
||||
pagination: true,
|
||||
|
||||
});
|
||||
|
||||
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}
|
||||
getInitialValues={getInitialValues({})}
|
||||
getValidationSchema={getValidationSchema}
|
||||
width="500px"
|
||||
>
|
||||
<ModelForm />
|
||||
</LayoutModel>
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ const ModalForm: React.FC = () => {
|
|||
getInitialValues={getInitialValues(objectToEdit)}
|
||||
getValidationSchema={getValidationSchema}
|
||||
isAddModal={false}
|
||||
width="500px"
|
||||
>
|
||||
<ModelForm />
|
||||
</LayoutModel>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import FilterLayout from "../../../Layout/Dashboard/FilterLayout";
|
|||
import FilterForm from "./Model/FilterForm";
|
||||
import { canAddLesson } from "../../../utils/hasAbilityFn";
|
||||
|
||||
const Table = lazy(() => import("./Table"));
|
||||
const Table = lazy(() => import("./DrapableTable"));
|
||||
const AddModalForm = lazy(() => import("./Model/AddModel"));
|
||||
const EditModalForm = lazy(() => import("./Model/EditModel"));
|
||||
const DeleteModelsForm = lazy(
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
canShowLesson,
|
||||
} from "../../../utils/hasAbilityFn";
|
||||
import ActionButtons from "../../../Components/Table/ActionButtons";
|
||||
import { DragHandleLesson } from "./DrapableTable";
|
||||
|
||||
export const useColumns = () => {
|
||||
const { handel_open_model } = useModalHandler();
|
||||
|
|
@ -37,6 +38,7 @@ export const useColumns = () => {
|
|||
const [t] = useTranslation();
|
||||
|
||||
const columns: TableColumnsType<Lesson> = [
|
||||
{ key: "sort", align: "center", width: 80, render: () => <DragHandleLesson /> },
|
||||
{
|
||||
title: t("columns.id"),
|
||||
dataIndex: "id",
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import { useObjectToEdit } from "../../../zustand/ObjectToEditState";
|
|||
|
||||
import Header from "../../../Components/exercise/Header";
|
||||
import { Question } from "../../../types/Item";
|
||||
import BaseForm from "./Model/Malty/Add";
|
||||
import BaseForm from "./Model/Malty/Form";
|
||||
import ModelForm from "./Model/ModelForm";
|
||||
const AcceptModal = lazy(() => import("./Model/AcceptModal"));
|
||||
|
||||
|
|
@ -42,6 +42,7 @@ const AddPage: React.FC = () => {
|
|||
isBseQuestion,
|
||||
setTagsSearch,
|
||||
setObjectToEdit,
|
||||
objectToEdit,
|
||||
setSuccess,
|
||||
SavedQuestionData,
|
||||
} = useObjectToEdit();
|
||||
|
|
@ -97,20 +98,19 @@ const AddPage: React.FC = () => {
|
|||
"/" +
|
||||
t("practical.add"),
|
||||
);
|
||||
|
||||
const handleSubmit = (
|
||||
values: any,
|
||||
{ resetForm }: { resetForm: () => void },
|
||||
) => {
|
||||
const DataToSend = structuredClone(values);
|
||||
console.log(DataToSend);
|
||||
|
||||
setTagsSearch(null);
|
||||
const canAnswersBeShuffled = DataToSend?.canAnswersBeShuffled ? 1 : 0;
|
||||
if (isBseQuestion || DataToSend?.isBase === 1) {
|
||||
const newBseQuestion = {
|
||||
subject_id: subject_id,
|
||||
content: DataToSend?.content,
|
||||
image: DataToSend?.image ?? "",
|
||||
content_image: DataToSend?.content_image ?? "",
|
||||
isBase: 1,
|
||||
lessons_ids: [lesson_id],
|
||||
canAnswersBeShuffled,
|
||||
|
|
@ -146,71 +146,77 @@ const AddPage: React.FC = () => {
|
|||
});
|
||||
} else {
|
||||
const tags = processTags(DataToSend);
|
||||
console.log(values,"values");
|
||||
|
||||
const answers = values?.answers?.map((item:any,index:number)=>{
|
||||
return {
|
||||
order:index,
|
||||
...item
|
||||
}
|
||||
})
|
||||
|
||||
mutate({
|
||||
const NewQuestion = {
|
||||
...values,
|
||||
subject_id: subject_id,
|
||||
tags,
|
||||
lessons_ids: [lesson_id],
|
||||
canAnswersBeShuffled,
|
||||
answers
|
||||
});
|
||||
}
|
||||
console.clear()
|
||||
console.log(NewQuestion,"NewQuestion");
|
||||
|
||||
mutate(NewQuestion);
|
||||
}
|
||||
};
|
||||
|
||||
const navigate = useNavigate();
|
||||
// console.log(SavedQuestionData);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
isSuccessAsync &&
|
||||
(SavedQuestionData?.Questions?.length > 0 ? isSuccess : true)
|
||||
) {
|
||||
setObjectToEdit(null);
|
||||
setSuccess(true);
|
||||
localStorage.removeItem(QUESTION_OBJECT_KEY);
|
||||
}
|
||||
if (isSuccess && !SavedQuestionData?.Questions?.length) {
|
||||
toast.success(t("validation.the_possess_done_successful"));
|
||||
setObjectToEdit(null);
|
||||
setSuccess(true);
|
||||
localStorage.removeItem(QUESTION_OBJECT_KEY);
|
||||
}
|
||||
}, [isSuccess, isSuccessAsync]);
|
||||
// useEffect(() => {
|
||||
// if (
|
||||
// isSuccessAsync &&
|
||||
// (SavedQuestionData?.Questions?.length > 0 ? isSuccess : true)
|
||||
// ) {
|
||||
// setObjectToEdit(null);
|
||||
// setSuccess(true);
|
||||
// localStorage.removeItem(QUESTION_OBJECT_KEY);
|
||||
// }
|
||||
// if (isSuccess && !SavedQuestionData?.Questions?.length) {
|
||||
// toast.success(t("validation.the_possess_done_successful"));
|
||||
// setObjectToEdit(null);
|
||||
// setSuccess(true);
|
||||
// localStorage.removeItem(QUESTION_OBJECT_KEY);
|
||||
// }
|
||||
// }, [isSuccess, isSuccessAsync]);
|
||||
|
||||
let cleanedAnswers = cleanObject(SavedQuestionData);
|
||||
let noChange = hasItems(cleanedAnswers);
|
||||
// let cleanedAnswers = cleanObject(SavedQuestionData);
|
||||
// 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 = () => {
|
||||
if (!noChange) {
|
||||
navigate(-1);
|
||||
localStorage.removeItem(QUESTION_OBJECT_KEY);
|
||||
} else {
|
||||
setIsOpen(ModalEnum?.QUESTION_ACCEPT);
|
||||
}
|
||||
// if (!noChange) {
|
||||
// localStorage.removeItem(QUESTION_OBJECT_KEY);
|
||||
// } else {
|
||||
// setIsOpen(ModalEnum?.QUESTION_ACCEPT);
|
||||
// }
|
||||
};
|
||||
|
||||
if (isBseQuestion || SavedData?.isBase === 1) {
|
||||
if (isBseQuestion ) {
|
||||
return (
|
||||
<div className="exercise_add">
|
||||
<FormikForm
|
||||
handleSubmit={handleSubmit}
|
||||
initialValues={getInitialValuesBase(SavedData)}
|
||||
initialValues={getInitialValuesBase(objectToEdit)}
|
||||
validationSchema={getValidationSchemaBase}
|
||||
>
|
||||
<main className="w-100 exercise_add_main">
|
||||
<Header />
|
||||
<ModelForm/>
|
||||
<BaseForm />
|
||||
|
||||
<div className="exercise_add_buttons">
|
||||
<div onClick={handleCancel}>{t("practical.back")}</div>
|
||||
|
|
@ -236,7 +242,7 @@ const AddPage: React.FC = () => {
|
|||
<div className="exercise_add">
|
||||
<FormikForm
|
||||
handleSubmit={handleSubmit}
|
||||
initialValues={getInitialValues(SavedData)}
|
||||
initialValues={getInitialValues(objectToEdit)}
|
||||
validationSchema={getValidationSchema}
|
||||
>
|
||||
<main className="w-100 exercise_add_main">
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import { useObjectToEdit } from "../../../zustand/ObjectToEditState";
|
|||
import { removeStringKeys } from "../../../utils/removeStringKeys";
|
||||
import SpinContainer from "../../../Components/Layout/SpinContainer";
|
||||
import ModelForm from "./Model/ModelForm";
|
||||
import BaseForm from "./Model/Malty/Edit";
|
||||
import BaseForm from "./Model/Malty/Form";
|
||||
import { Question } from "../../../types/Item";
|
||||
import { toast } from "react-toastify";
|
||||
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 [t] = useTranslation();
|
||||
const CheckboxhandleChange = (value: any, index: number) => {
|
||||
const allAreZero = formik?.values?.QuestionOptions?.some(
|
||||
const allAreZero = formik?.values?.answers?.some(
|
||||
(item: any) => item.isCorrect === 1,
|
||||
);
|
||||
|
||||
if (allAreZero) {
|
||||
formik?.values.QuestionOptions.forEach((item: any, index: number) => {
|
||||
formik.setFieldValue(`QuestionOptions[${index}].isCorrect`, 0);
|
||||
formik?.values.answers.forEach((item: any, index: number) => {
|
||||
formik.setFieldValue(`answers[${index}].isCorrect`, 0);
|
||||
});
|
||||
}
|
||||
|
||||
formik.setFieldValue(
|
||||
`QuestionOptions[${name}].isCorrect`,
|
||||
`answers[${name}].isCorrect`,
|
||||
value?.target?.checked ? 1 : 0,
|
||||
);
|
||||
};
|
||||
|
|
@ -35,7 +35,7 @@ const CheckboxField = ({
|
|||
<Checkbox
|
||||
onChange={onChange || CheckboxhandleChange}
|
||||
disabled={isDisabled}
|
||||
checked={formik.values?.QuestionOptions?.[name]?.isCorrect === 1}
|
||||
checked={formik.values?.answers?.[name]?.isCorrect === 1}
|
||||
className={className}
|
||||
>
|
||||
{t(`input.${label ? label : name}`)}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,8 @@ import { useTranslation } from "react-i18next";
|
|||
import { getCharFromNumber } from "../../../../../utils/getCharFromNumber";
|
||||
import CheckboxField from "./CheckboxField";
|
||||
import TextField from "./TextField";
|
||||
import File from "./File";
|
||||
import { FaCirclePlus, FaDeleteLeft } from "react-icons/fa6";
|
||||
import ImageBoxField from "../../../../../Components/CustomFields/ImageBoxField/ImageBoxField";
|
||||
import { GoTrash } from "react-icons/go";
|
||||
import { LuImagePlus } from "react-icons/lu";
|
||||
|
||||
const ChoiceFields = ({ index, data }: { index: number; data: Choice }) => {
|
||||
const formik = useFormikContext<any>();
|
||||
|
|
@ -18,46 +16,56 @@ const ChoiceFields = ({ index, data }: { index: number; data: Choice }) => {
|
|||
|
||||
const handleDeleteChoice = () => {
|
||||
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,
|
||||
);
|
||||
|
||||
formik.setFieldValue("QuestionOptions", updatedQuestionOptions);
|
||||
formik.setFieldValue("answers", updatedAnswers);
|
||||
};
|
||||
console.log(formik.values);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="ChoiceFields">
|
||||
<TextField
|
||||
className="textarea_exercise"
|
||||
placeholder={"choice"}
|
||||
label2={t(`input.choice`) + ` ` + `(${getCharFromNumber(index)})`}
|
||||
label2={t(`input.choice`) + ` ` +`( ${t(`alphabet.${getCharFromNumber(index)}`)} )`}
|
||||
name={index}
|
||||
id={`choice_${index + 1}`}
|
||||
type="TextArea"
|
||||
/>
|
||||
<File
|
||||
className="file_exercise"
|
||||
label={"attachment"}
|
||||
name={index}
|
||||
type="File"
|
||||
placeholder=""
|
||||
icon={<LuImagePlus/>}
|
||||
/>
|
||||
|
||||
<ImageBoxField name={`answers.${index}.content_image`} />
|
||||
|
||||
<div className="answer_status">
|
||||
|
||||
<CheckboxField
|
||||
className=""
|
||||
label="The_correct_answer"
|
||||
name={index}
|
||||
type="Checkbox"
|
||||
|
||||
/>
|
||||
<p className="delete_question_options">
|
||||
{t("header.delete_choice")}
|
||||
<GoTrash className="trash_icon" onClick={handleDeleteChoice} size={17} />
|
||||
</p>
|
||||
</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 (
|
||||
<DragDropContext onDragEnd={handleDragEnd}>
|
||||
<Droppable droppableId="choices">
|
||||
{(provided:any) => (
|
||||
{(provided) => (
|
||||
<div {...provided.droppableProps} ref={provided.innerRef}>
|
||||
{formik?.values?.answers?.map(
|
||||
(item: Choice, index: number) => {
|
||||
|
|
@ -50,7 +50,7 @@ const Choices = () => {
|
|||
draggableId={draggableId}
|
||||
index={index}
|
||||
>
|
||||
{(provided:any) => (
|
||||
{(provided) => (
|
||||
<div
|
||||
ref={provided.innerRef}
|
||||
{...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 React from "react";
|
||||
import useFormField from "../../../../../Hooks/useFormField";
|
||||
import { MdOutlineEdit } from "react-icons/md";
|
||||
import { Field } from "formik";
|
||||
import useFormField from "../../../../../Hooks/useFormField";
|
||||
const { TextArea } = Input;
|
||||
|
||||
const TextField = ({
|
||||
|
|
@ -18,7 +18,7 @@ const TextField = ({
|
|||
id,
|
||||
className,
|
||||
}: any) => {
|
||||
const newName = `QuestionOptions[${name}].answer`;
|
||||
const newName = `answers[${name}].content`;
|
||||
const { formik, isError, errorMsg, t } = useFormField(newName, props);
|
||||
const TextFilehandleChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||
|
|
|
|||
|
|
@ -10,17 +10,14 @@ 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";
|
||||
|
||||
const Form = () => {
|
||||
const formik = useFormikContext<any>();
|
||||
const { isOpen } = useModalState((state) => state);
|
||||
// const {data} = useGetAllQuestion();
|
||||
const { setSuccess, Success, setSavedQuestionData } = useObjectToEdit();
|
||||
|
||||
useEffect(() => {
|
||||
if (Success) {
|
||||
console.log(1);
|
||||
|
||||
formik.setErrors({});
|
||||
formik.resetForm({ values: {} });
|
||||
setSuccess(false);
|
||||
|
|
@ -32,14 +29,13 @@ const Form = () => {
|
|||
}, [formik?.values]);
|
||||
|
||||
// console.log(formik?.errors);
|
||||
console.log(formik?.values?.Questions, "formik?.values?.Questions");
|
||||
|
||||
const handleAddChoice = (parent_index: number) => {
|
||||
console.log(parent_index);
|
||||
|
||||
formik.setFieldValue(`Questions.[${parent_index}].QuestionOptions`, [
|
||||
formik.setFieldValue(`Questions.[${parent_index}].answers`, [
|
||||
...((formik?.values as any)?.Questions?.[parent_index]
|
||||
.QuestionOptions as Choice[]),
|
||||
.answers as Choice[]),
|
||||
|
||||
{
|
||||
answer: null,
|
||||
|
|
@ -58,9 +54,9 @@ const Form = () => {
|
|||
image: "",
|
||||
parent: "",
|
||||
isBase: 0,
|
||||
max_mark: 1,
|
||||
min_mark_to_pass: 1,
|
||||
QuestionOptions: [{ answer: null, answer_image: null, isCorrect: 0 }],
|
||||
// max_mark: 1,
|
||||
// min_mark_to_pass: 1,
|
||||
answers: [{ answer: null, answer_image: null, isCorrect: 0 }],
|
||||
tags: [],
|
||||
},
|
||||
]);
|
||||
|
|
@ -70,7 +66,6 @@ const Form = () => {
|
|||
formik.setFieldValue("max_mark", max_mark);
|
||||
};
|
||||
const [t] = useTranslation();
|
||||
console.log(formik.errors);
|
||||
|
||||
return (
|
||||
<Row className="w-100">
|
||||
|
|
@ -109,21 +104,9 @@ const Form = () => {
|
|||
/>
|
||||
</div>
|
||||
|
||||
{(
|
||||
(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}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<Choices parent_index={parent_index} />
|
||||
|
||||
{formik?.values?.Questions?.[parent_index]?.QuestionOptions
|
||||
{formik?.values?.Questions?.[parent_index]?.answers
|
||||
?.length < 5 && (
|
||||
<p className="add_new_button">
|
||||
<FaCirclePlus
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React from "react";
|
||||
import useFormField from "../../../../../../Hooks/useFormField";
|
||||
import { Checkbox, Form } from "antd";
|
||||
import { useFormik, useFormikContext } from "formik";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
|
@ -15,16 +16,15 @@ const CheckboxField = ({
|
|||
const formik = useFormikContext<any>();
|
||||
const [t] = useTranslation();
|
||||
const CheckboxhandleChange = (value: any) => {
|
||||
console.log(value?.target?.checked);
|
||||
|
||||
const allAreZero = formik?.values?.Questions?.[
|
||||
parent_index
|
||||
]?.QuestionOptions?.some((item: any) => item.isCorrect === 1);
|
||||
]?.answers?.some((item: any) => item.isCorrect === 1);
|
||||
if (allAreZero) {
|
||||
formik?.values?.Questions?.[parent_index]?.QuestionOptions.forEach(
|
||||
formik?.values?.Questions?.[parent_index]?.answers.forEach(
|
||||
(item: any, index: number) => {
|
||||
formik.setFieldValue(
|
||||
`Questions[${parent_index}].QuestionOptions[${index}].isCorrect`,
|
||||
`Questions[${parent_index}].answers[${index}].isCorrect`,
|
||||
0,
|
||||
);
|
||||
},
|
||||
|
|
@ -32,7 +32,7 @@ const CheckboxField = ({
|
|||
}
|
||||
|
||||
formik.setFieldValue(
|
||||
`Questions[${parent_index}].QuestionOptions[${name}].isCorrect`,
|
||||
`Questions[${parent_index}].answers[${name}].isCorrect`,
|
||||
value?.target?.checked ? 1 : 0,
|
||||
);
|
||||
};
|
||||
|
|
@ -42,7 +42,7 @@ const CheckboxField = ({
|
|||
onChange={onChange || CheckboxhandleChange}
|
||||
disabled={isDisabled}
|
||||
checked={
|
||||
formik.values?.Questions?.[parent_index]?.QuestionOptions?.[name]
|
||||
formik.values?.Questions?.[parent_index]?.answers?.[name]
|
||||
?.isCorrect === 1
|
||||
}
|
||||
className={className}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ import TextField from "./TextField";
|
|||
import File from "./File";
|
||||
import { FaTrash } from "react-icons/fa";
|
||||
import { toast } from "react-toastify";
|
||||
import HintField from "./HintField";
|
||||
import ImageBoxField from "../../../../../../Components/CustomFields/ImageBoxField/ImageBoxField";
|
||||
import { GoTrash } from "react-icons/go";
|
||||
|
||||
const ChoiceFields = ({
|
||||
index,
|
||||
|
|
@ -25,7 +28,7 @@ const ChoiceFields = ({
|
|||
|
||||
const handleDeleteChoice = () => {
|
||||
const arrayLength =
|
||||
formik.values.Questions?.[parent_index].QuestionOptions?.length;
|
||||
formik.values.Questions?.[parent_index].answers?.length;
|
||||
|
||||
console.log(arrayLength);
|
||||
|
||||
|
|
@ -36,31 +39,29 @@ const ChoiceFields = ({
|
|||
return;
|
||||
}
|
||||
|
||||
const updatedQuestionOptions = formik.values.Questions?.[
|
||||
const updatedAnswers = formik.values.Questions?.[
|
||||
parent_index
|
||||
].QuestionOptions.filter((_: any, i: any) => i !== index);
|
||||
].answers.filter((_: any, i: any) => i !== index);
|
||||
formik.setFieldValue(
|
||||
`Questions[${parent_index}].QuestionOptions`,
|
||||
updatedQuestionOptions,
|
||||
`Questions[${parent_index}].answers`,
|
||||
updatedAnswers,
|
||||
);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div className="ChoiceFields">
|
||||
<TextField
|
||||
className="textarea_exercise"
|
||||
placeholder={"choice"}
|
||||
label2={t(`input.choice`) + ` ` + `(${getCharFromNumber(index)})`}
|
||||
label2={t(`input.choice`) + ` ` + `( ${t(`alphabet.${getCharFromNumber(index)}`)} )`}
|
||||
name={index}
|
||||
parent_index={parent_index}
|
||||
type="TextArea"
|
||||
/>
|
||||
<File
|
||||
className="file_exercise"
|
||||
label={"attachment"}
|
||||
name={index}
|
||||
type="File"
|
||||
parent_index={parent_index}
|
||||
/>
|
||||
|
||||
<ImageBoxField name={`Questions.${parent_index}.answers.${index}.answer_image`} />
|
||||
|
||||
<div className="answer_status">
|
||||
|
||||
<CheckboxField
|
||||
className=""
|
||||
|
|
@ -70,9 +71,26 @@ const ChoiceFields = ({
|
|||
parent_index={parent_index}
|
||||
/>
|
||||
<p className="delete_question_options">
|
||||
<FaTrash onClick={handleDeleteChoice} size={17} />
|
||||
{t("header.delete_choice")}
|
||||
<GoTrash className="trash_icon" onClick={handleDeleteChoice} size={17} />
|
||||
</p>
|
||||
</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,
|
||||
props,
|
||||
}: 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);
|
||||
let imageUrl =
|
||||
formik?.values?.Questions?.[parent_index]?.QuestionOptions[name]
|
||||
formik?.values?.Questions?.[parent_index]?.answers[name]
|
||||
?.answer_image ?? null;
|
||||
// console.log(imageUrl);
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ const File = ({
|
|||
formik.setFieldValue(newName, null);
|
||||
} else {
|
||||
formik.setFieldValue(
|
||||
`Questions[${parent_index}].QuestionOptions[${name}].answer_image`,
|
||||
`Questions[${parent_index}].answers[${name}].answer_image`,
|
||||
value?.file?.originFileObj,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ const TextField = ({
|
|||
parent_index,
|
||||
className,
|
||||
}: 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 TextFilehandleChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||
|
|
|
|||
|
|
@ -30,9 +30,9 @@ const Form = () => {
|
|||
const handleAddChoice = (parent_index: number) => {
|
||||
console.log(parent_index);
|
||||
console.log(formik?.values?.Questions);
|
||||
formik.setFieldValue(`Questions.[${parent_index}].QuestionOptions`, [
|
||||
formik.setFieldValue(`Questions.[${parent_index}].answers`, [
|
||||
...((formik?.values as any)?.Questions?.[parent_index]
|
||||
.QuestionOptions as Choice[]),
|
||||
.answers as Choice[]),
|
||||
|
||||
{
|
||||
answer: null,
|
||||
|
|
@ -53,7 +53,7 @@ const Form = () => {
|
|||
isBase: 0,
|
||||
max_mark: 1,
|
||||
min_mark_to_pass: 1,
|
||||
QuestionOptions: [{ answer: null, answer_image: null, isCorrect: 0 }],
|
||||
answers: [{ answer: null, answer_image: null, isCorrect: 0 }],
|
||||
tags: [],
|
||||
},
|
||||
]);
|
||||
|
|
@ -103,7 +103,7 @@ const Form = () => {
|
|||
|
||||
{(
|
||||
(formik?.values as any)?.Questions?.[parent_index]
|
||||
?.QuestionOptions || []
|
||||
?.answers || []
|
||||
).map((item: Choice, index: number) => {
|
||||
return (
|
||||
<ChoiceFields
|
||||
|
|
@ -114,7 +114,7 @@ const Form = () => {
|
|||
/>
|
||||
);
|
||||
})}
|
||||
{formik?.values?.Questions?.[parent_index]?.QuestionOptions
|
||||
{formik?.values?.Questions?.[parent_index]?.answers
|
||||
?.length < 5 && (
|
||||
<p className="add_new_button">
|
||||
<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 { useObjectToEdit } from "../../../../../../zustand/ObjectToEditState";
|
||||
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 formik = useFormikContext<any>();
|
||||
console.log(index);
|
||||
const { setDeletedQuestions, DeletedQuestions } = useObjectToEdit();
|
||||
|
||||
const [t] = useTranslation();
|
||||
|
|
@ -25,31 +28,43 @@ const QuestionFIeld = ({ index, data }: { index: number; data: Choice }) => {
|
|||
if (DeleteQuestionId?.id) {
|
||||
setDeletedQuestions([...DeletedQuestions, DeleteQuestionId]);
|
||||
}
|
||||
const updatedQuestionOptions = formik.values.Questions.filter(
|
||||
const updatedAnswers = formik.values.Questions.filter(
|
||||
(_: any, i: any) => i !== index,
|
||||
);
|
||||
formik.setFieldValue(`Questions`, updatedQuestionOptions);
|
||||
formik.setFieldValue(`Questions`, updatedAnswers);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<div className="exercise_forms">
|
||||
<div className="ChoiceFields">
|
||||
<TextField
|
||||
className="textarea_exercise"
|
||||
placeholder={"choice"}
|
||||
label2={t(`input.question`) + ` ` + `${index + 1}`}
|
||||
placeholder={"question"}
|
||||
label2={t(`input.question`) + ` ` + `( ${t(`alphabet.${getCharFromNumber(index)}`)} )`}
|
||||
name={index}
|
||||
id={`question_${index + 1}`}
|
||||
type="TextArea"
|
||||
/>
|
||||
<File
|
||||
className="file_exercise"
|
||||
label={"attachment"}
|
||||
name={index}
|
||||
type="File"
|
||||
/>
|
||||
|
||||
<ImageBoxField name={`Questions.${index}.image`} />
|
||||
|
||||
<div className="answer_status">
|
||||
|
||||
<p className="delete_question_options">
|
||||
<FaTrash onClick={handleDeleteQuestion} size={17} />
|
||||
{t("header.delete_question")}
|
||||
<GoTrash className="trash_icon" onClick={handleDeleteQuestion} size={17} />
|
||||
</p>
|
||||
|
||||
</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