Compare commits
No commits in common. "1a7847412019aeac4e9e19c8da7b6f77a624d8e4" and "7aa8d50cfd47973c8a7045d3fc8e92d9200e5e5a" have entirely different histories.
1a78474120
...
7aa8d50cfd
|
|
@ -5,7 +5,7 @@
|
|||
1. Clone the repository:
|
||||
|
||||
```bash
|
||||
git clone https://git.point-dev.net/Karimaldeen/Quiz_dashboard.git
|
||||
git clone https://git.point-dev.net/Karimaldeen/zaker.git
|
||||
```
|
||||
|
||||
```bash
|
||||
|
|
|
|||
12
package.json
12
package.json
|
|
@ -4,27 +4,28 @@
|
|||
"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",
|
||||
"lottie-react": "^2.4.0",
|
||||
"path-to-regexp": "^6.2.2",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"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"
|
||||
},
|
||||
|
|
@ -60,7 +61,6 @@
|
|||
"@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,18 +11,6 @@ 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)
|
||||
|
|
@ -38,12 +26,18 @@ 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
|
||||
lottie-react:
|
||||
specifier: ^2.4.0
|
||||
version: 2.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
path-to-regexp:
|
||||
specifier: ^6.2.2
|
||||
version: 6.2.2
|
||||
pdf-lib:
|
||||
specifier: ^1.17.1
|
||||
version: 1.17.1
|
||||
react:
|
||||
specifier: ^18.3.1
|
||||
version: 18.3.1
|
||||
|
|
@ -59,6 +53,9 @@ 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)
|
||||
|
|
@ -74,6 +71,12 @@ 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
|
||||
|
|
@ -96,9 +99,6 @@ 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,34 +877,6 @@ 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==}
|
||||
|
||||
|
|
@ -1150,6 +1122,12 @@ 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==}
|
||||
|
||||
|
|
@ -1385,9 +1363,6 @@ 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==}
|
||||
|
||||
|
|
@ -1908,6 +1883,13 @@ 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==}
|
||||
|
||||
|
|
@ -2157,6 +2139,9 @@ 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'}
|
||||
|
|
@ -2550,6 +2535,9 @@ 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'}
|
||||
|
|
@ -2574,15 +2562,6 @@ 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==}
|
||||
|
||||
|
|
@ -2735,6 +2714,9 @@ 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'}
|
||||
|
|
@ -2761,10 +2743,16 @@ 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==}
|
||||
|
||||
|
|
@ -3110,6 +3098,11 @@ 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:
|
||||
|
|
@ -3547,6 +3540,9 @@ 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==}
|
||||
|
||||
|
|
@ -3637,6 +3633,9 @@ 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}
|
||||
|
|
@ -4765,44 +4764,11 @@ 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': {}
|
||||
|
|
@ -5075,8 +5041,15 @@ snapshots:
|
|||
'@jridgewell/trace-mapping@0.3.9':
|
||||
dependencies:
|
||||
'@jridgewell/resolve-uri': 3.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.5.0
|
||||
optional: true
|
||||
'@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
|
||||
|
||||
'@popperjs/core@2.11.8': {}
|
||||
|
||||
|
|
@ -5248,17 +5221,13 @@ snapshots:
|
|||
'@babel/runtime': 7.24.6
|
||||
'@testing-library/dom': 9.3.3
|
||||
|
||||
'@tsconfig/node10@1.0.11':
|
||||
optional: true
|
||||
'@tsconfig/node10@1.0.11': {}
|
||||
|
||||
'@tsconfig/node12@1.0.11':
|
||||
optional: true
|
||||
'@tsconfig/node12@1.0.11': {}
|
||||
|
||||
'@tsconfig/node14@1.0.3':
|
||||
optional: true
|
||||
'@tsconfig/node14@1.0.3': {}
|
||||
|
||||
'@tsconfig/node16@1.0.4':
|
||||
optional: true
|
||||
'@tsconfig/node16@1.0.4': {}
|
||||
|
||||
'@types/aria-query@5.0.4': {}
|
||||
|
||||
|
|
@ -5330,10 +5299,6 @@ 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
|
||||
|
|
@ -5494,7 +5459,6 @@ snapshots:
|
|||
acorn-walk@8.3.3:
|
||||
dependencies:
|
||||
acorn: 8.12.1
|
||||
optional: true
|
||||
|
||||
acorn@8.12.1: {}
|
||||
|
||||
|
|
@ -5593,8 +5557,7 @@ snapshots:
|
|||
normalize-path: 3.0.0
|
||||
picomatch: 2.3.1
|
||||
|
||||
arg@4.1.3:
|
||||
optional: true
|
||||
arg@4.1.3: {}
|
||||
|
||||
argparse@1.0.10:
|
||||
dependencies:
|
||||
|
|
@ -5903,8 +5866,7 @@ snapshots:
|
|||
- supports-color
|
||||
- ts-node
|
||||
|
||||
create-require@1.1.1:
|
||||
optional: true
|
||||
create-require@1.1.1: {}
|
||||
|
||||
cross-spawn@7.0.3:
|
||||
dependencies:
|
||||
|
|
@ -5991,8 +5953,7 @@ snapshots:
|
|||
|
||||
diff-sequences@29.6.3: {}
|
||||
|
||||
diff@4.0.2:
|
||||
optional: true
|
||||
diff@4.0.2: {}
|
||||
|
||||
dom-accessibility-api@0.5.16: {}
|
||||
|
||||
|
|
@ -6001,6 +5962,10 @@ 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: {}
|
||||
|
|
@ -6250,6 +6215,8 @@ 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
|
||||
|
|
@ -6829,6 +6796,8 @@ snapshots:
|
|||
|
||||
lines-and-columns@1.2.4: {}
|
||||
|
||||
load-script@1.0.0: {}
|
||||
|
||||
loader-runner@4.3.0: {}
|
||||
|
||||
locate-path@5.0.0:
|
||||
|
|
@ -6847,14 +6816,6 @@ 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
|
||||
|
|
@ -6987,6 +6948,8 @@ snapshots:
|
|||
|
||||
p-try@2.2.0: {}
|
||||
|
||||
pako@1.0.11: {}
|
||||
|
||||
parent-module@1.0.1:
|
||||
dependencies:
|
||||
callsites: 3.1.0
|
||||
|
|
@ -7011,9 +6974,18 @@ 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: {}
|
||||
|
|
@ -7444,6 +7416,11 @@ 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
|
||||
|
|
@ -7878,7 +7855,8 @@ snapshots:
|
|||
typescript: 4.9.5
|
||||
v8-compile-cache-lib: 3.0.1
|
||||
yn: 3.1.1
|
||||
optional: true
|
||||
|
||||
tslib@1.14.1: {}
|
||||
|
||||
tslib@2.6.2: {}
|
||||
|
||||
|
|
@ -7941,8 +7919,7 @@ snapshots:
|
|||
dependencies:
|
||||
react: 18.3.1
|
||||
|
||||
v8-compile-cache-lib@3.0.1:
|
||||
optional: true
|
||||
v8-compile-cache-lib@3.0.1: {}
|
||||
|
||||
v8-to-istanbul@9.2.0:
|
||||
dependencies:
|
||||
|
|
@ -7959,6 +7936,11 @@ 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
|
||||
|
|
@ -8127,8 +8109,7 @@ snapshots:
|
|||
y18n: 5.0.8
|
||||
yargs-parser: 21.1.1
|
||||
|
||||
yn@3.1.1:
|
||||
optional: true
|
||||
yn@3.1.1: {}
|
||||
|
||||
yocto-queue@0.1.0: {}
|
||||
|
||||
|
|
|
|||
BIN
public/DataState/EmptyData.gif
Normal file
BIN
public/DataState/EmptyData.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 698 KiB |
BIN
public/DataState/loading.gif
Normal file
BIN
public/DataState/loading.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
BIN
public/DataState/successfully.png
Normal file
BIN
public/DataState/successfully.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
|
|
@ -1,3 +0,0 @@
|
|||
<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>
|
||||
|
Before Width: | Height: | Size: 2.4 KiB |
|
|
@ -1,42 +0,0 @@
|
|||
.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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
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;
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
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;
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
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;
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
export const generateImagePreview = (file: File, setImagePreview: (result: string) => void) => {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => {
|
||||
setImagePreview(reader.result as string);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
};
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
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;
|
||||
19
src/Components/DataState/AddedSuccessfully.tsx
Normal file
19
src/Components/DataState/AddedSuccessfully.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
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;
|
||||
25
src/Components/DataState/EmptyData.tsx
Normal file
25
src/Components/DataState/EmptyData.tsx
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
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;
|
||||
12
src/Components/DataState/Loading.tsx
Normal file
12
src/Components/DataState/Loading.tsx
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
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,14 +11,12 @@ 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);
|
||||
|
|
@ -60,16 +58,17 @@ const SearchFieldWithSelect: React.FC<Props> = ({
|
|||
return (
|
||||
<div ref={node} className={`search-field ${isOpen ? "open" : ""}`}>
|
||||
<div className="search-header" onClick={toggleDropdown}>
|
||||
{withIcon && <IoSearch className="search__icon" />}
|
||||
|
||||
{/* <p className="search__input_text">{placeholder}</p> */}
|
||||
<IoSearch className="search__icon" />
|
||||
<input
|
||||
type="text"
|
||||
className="search__input search__input_text"
|
||||
placeholder={selectedOption ? selectedOption.label : placeholder}
|
||||
placeholder={selectedOption ? selectedOption.label : ""}
|
||||
// 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">
|
||||
|
|
@ -81,7 +80,7 @@ const SearchFieldWithSelect: React.FC<Props> = ({
|
|||
onClick={() => handleOptionClick(option)}
|
||||
>
|
||||
<div>{option.label}</div>
|
||||
{/* {withIcon && <IoSearch className="search__icon" />} */}
|
||||
<IoSearch className="search__icon" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1,13 +0,0 @@
|
|||
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;
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1,13 +0,0 @@
|
|||
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;
|
||||
24
src/Components/ValidationField/Ui/KarimSpinner.tsx
Normal file
24
src/Components/ValidationField/Ui/KarimSpinner.tsx
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
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;
|
||||
37
src/Components/ValidationField/Ui/SearchBar.scss
Normal file
37
src/Components/ValidationField/Ui/SearchBar.scss
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// .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;
|
||||
// }
|
||||
// }
|
||||
45
src/Components/ValidationField/Ui/SearchBar.tsx
Normal file
45
src/Components/ValidationField/Ui/SearchBar.tsx
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
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,16 +6,17 @@ 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,
|
||||
|
|
@ -30,11 +31,12 @@ const components: { [key: string]: React.FC<any> } = {
|
|||
MaltyFile: MaltyFile,
|
||||
Checkbox: CheckboxField,
|
||||
NumberFormate: NumberFormate,
|
||||
Number: NumberField,
|
||||
};
|
||||
|
||||
const ValidationField: React.FC<ValidationFieldProps> = React.memo(
|
||||
({ type = "text", ...otherProps }) => {
|
||||
const Component = components[type ?? ("text" as ValidationFieldType)];
|
||||
({ type, ...otherProps }: any) => {
|
||||
const Component = components[type as ValidationFieldType];
|
||||
|
||||
if (!Component) {
|
||||
return <Default {...otherProps} />;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import useFormField from "../../../Hooks/useFormField";
|
||||
import { Checkbox } from "antd";
|
||||
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
|
||||
import { Checkbox, Form } from "antd";
|
||||
import { getNestedValue } from "../utils/getNestedValue";
|
||||
const CheckboxField = ({
|
||||
name,
|
||||
label,
|
||||
|
|
@ -17,8 +17,12 @@ const CheckboxField = ({
|
|||
};
|
||||
|
||||
return (
|
||||
<div className={Group ? "d-inline mt-3 Checkbox" : ``}>
|
||||
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||
<div className={Group ? "d-inline mt-3 Checkboxs" : ``}>
|
||||
<Form.Item
|
||||
hasFeedback
|
||||
validateStatus={isError ? "error" : ""}
|
||||
help={isError ? errorMsg : ""}
|
||||
>
|
||||
<Checkbox
|
||||
onChange={onChange || CheckboxhandleChange}
|
||||
disabled={isDisabled}
|
||||
|
|
@ -27,7 +31,7 @@ const CheckboxField = ({
|
|||
>
|
||||
{t(`input.${label ? label : name}`)}
|
||||
</Checkbox>
|
||||
</ValidationFieldContainer>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ 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;
|
||||
|
||||
|
|
@ -26,16 +24,28 @@ const DataRange = ({
|
|||
};
|
||||
return (
|
||||
<div className="ValidationField w-100 ">
|
||||
<ValidationFieldLabel
|
||||
name={name}
|
||||
label={label}
|
||||
label_icon={label_icon}
|
||||
no_label={no_label}
|
||||
placeholder={placeholder}
|
||||
t={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>
|
||||
)}
|
||||
|
||||
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||
<Form.Item
|
||||
hasFeedback
|
||||
validateStatus={isError ? "error" : ""}
|
||||
help={isError ? errorMsg : ""}
|
||||
>
|
||||
<RangePicker
|
||||
placeholder={placeholder}
|
||||
size="large"
|
||||
|
|
@ -45,9 +55,8 @@ const DataRange = ({
|
|||
onChange={onChange || onCalendarChange}
|
||||
disabled={isDisabled}
|
||||
defaultValue={formik.values[name]}
|
||||
id={name}
|
||||
/>
|
||||
</ValidationFieldContainer>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,9 +3,6 @@ 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,
|
||||
|
|
@ -24,21 +21,34 @@ const Date = ({
|
|||
const FormikValue = formik.values[name];
|
||||
const onCalendarChange = (value: any) => {
|
||||
formik.setFieldValue(name, value);
|
||||
// console.log(value,"value ");
|
||||
};
|
||||
|
||||
const Formatter = [DateEnum?.FORMATE];
|
||||
const Formater = "YYYY/MM/DD";
|
||||
return (
|
||||
<div className="ValidationField w-100 ">
|
||||
<ValidationFieldLabel
|
||||
name={name}
|
||||
label={label}
|
||||
label_icon={label_icon}
|
||||
no_label={no_label}
|
||||
placeholder={placeholder}
|
||||
t={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>
|
||||
)}
|
||||
|
||||
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||
<Form.Item
|
||||
hasFeedback
|
||||
validateStatus={isError ? "error" : ""}
|
||||
help={isError ? errorMsg : ""}
|
||||
>
|
||||
<DatePicker
|
||||
picker={picker}
|
||||
placeholder={t(`input.${placeholder}`)}
|
||||
|
|
@ -48,11 +58,10 @@ const Date = ({
|
|||
size="large"
|
||||
onChange={onChange || onCalendarChange}
|
||||
disabled={isDisabled}
|
||||
format={Formatter}
|
||||
id={name}
|
||||
format={Formater}
|
||||
/>
|
||||
{/* <DatePicker onChange={onChange} /> */}
|
||||
</ValidationFieldContainer>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import { Input } from "antd";
|
||||
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 { ValidationFieldLabel } from "../components/ValidationFieldLabel";
|
||||
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
|
||||
import { FieldProps } from "../utils/types";
|
||||
import { ValidationFieldPropsInput } from "../utils/types";
|
||||
|
||||
const Default = ({
|
||||
name,
|
||||
|
|
@ -15,34 +14,52 @@ const Default = ({
|
|||
type,
|
||||
no_label,
|
||||
label_icon,
|
||||
label2,
|
||||
...props
|
||||
}: any) => {
|
||||
}: ValidationFieldPropsInput) => {
|
||||
const { errorMsg, isError, t } = useFormField(name, props);
|
||||
|
||||
return (
|
||||
<div className="ValidationField w-100">
|
||||
<ValidationFieldLabel
|
||||
name={name}
|
||||
label={label}
|
||||
label_icon={label_icon}
|
||||
no_label={no_label}
|
||||
placeholder={placeholder}
|
||||
t={t}
|
||||
/>
|
||||
{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>
|
||||
)}
|
||||
|
||||
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||
<Form.Item
|
||||
hasFeedback
|
||||
validateStatus={isError ? "error" : ""}
|
||||
help={isError ? errorMsg : ""}
|
||||
>
|
||||
<Field
|
||||
as={Input}
|
||||
type={type ?? "text"}
|
||||
placeholder={t(`input.${placeholder || label || name}`)}
|
||||
placeholder={t(
|
||||
`input.${placeholder ? placeholder : label ? label : name}`,
|
||||
)}
|
||||
name={name}
|
||||
id={name}
|
||||
disabled={isDisabled}
|
||||
size="large"
|
||||
{...(type === "number" && { min: 0 })}
|
||||
{...props}
|
||||
/>
|
||||
</ValidationFieldContainer>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ 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];
|
||||
|
||||
|
|
@ -11,7 +12,7 @@ const DropFile = ({
|
|||
label,
|
||||
onChange,
|
||||
isDisabled,
|
||||
placeholder,
|
||||
placholder,
|
||||
className,
|
||||
props,
|
||||
no_label,
|
||||
|
|
@ -22,7 +23,7 @@ const DropFile = ({
|
|||
|
||||
const FormikValue =
|
||||
typeof FormikName === "string"
|
||||
? FormikName
|
||||
? ImageBaseURL + FormikName
|
||||
: FormikName instanceof File
|
||||
? URL.createObjectURL(FormikName)
|
||||
: "";
|
||||
|
|
@ -70,7 +71,6 @@ 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 React, { useMemo } from "react";
|
||||
import { useMemo } from "react";
|
||||
|
||||
const File = ({
|
||||
name,
|
||||
|
|
@ -11,34 +11,37 @@ 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: "",
|
||||
name: "uploaded-image",
|
||||
status: "done",
|
||||
url: imageUrl,
|
||||
thumbUrl: imageUrl,
|
||||
}
|
||||
: {
|
||||
uid: imageUrl.uid || "-1",
|
||||
name: imageUrl.name || "",
|
||||
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(name, null);
|
||||
} else {
|
||||
|
|
@ -62,15 +65,12 @@ const File = ({
|
|||
onChange={onChange || FilehandleChange}
|
||||
customRequest={customRequest}
|
||||
className={` w-100`}
|
||||
id={name}
|
||||
>
|
||||
<Button
|
||||
className={isError ? "isError w-100 " : " w-100"}
|
||||
icon={<UploadOutlined />}
|
||||
icon={icon ? icon : <UploadOutlined />}
|
||||
>
|
||||
{placeholder
|
||||
? t(`input.${placeholder}`)
|
||||
: t("input.Click_to_upload_the_image")}
|
||||
{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 React.memo(File);
|
||||
export default File;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import { Select } from "antd";
|
||||
import { Form, 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,
|
||||
|
|
@ -14,9 +13,9 @@ const LocalSelectField = ({
|
|||
isMulti,
|
||||
onChange,
|
||||
className,
|
||||
props,
|
||||
no_label,
|
||||
label_icon,
|
||||
...props
|
||||
}: any) => {
|
||||
const { errorMsg, isError, t, formik } = useFormField(name, props);
|
||||
|
||||
|
|
@ -30,6 +29,13 @@ 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);
|
||||
|
|
@ -41,21 +47,35 @@ const LocalSelectField = ({
|
|||
|
||||
return (
|
||||
<div className="ValidationField w-100">
|
||||
<ValidationFieldLabel
|
||||
name={name}
|
||||
label={label}
|
||||
label_icon={label_icon}
|
||||
no_label={no_label}
|
||||
placeholder={placeholder}
|
||||
t={t}
|
||||
/>
|
||||
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||
{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 : ""}
|
||||
>
|
||||
<Select
|
||||
placeholder={t(`input.${placeholder || label || name}`)}
|
||||
placeholder={t(
|
||||
`input.${placeholder ? placeholder : label ? label : name}`,
|
||||
)}
|
||||
disabled={isDisabled}
|
||||
options={translateOptions(option, t)}
|
||||
size="large"
|
||||
className={`${className} ${isError ? "SelectError" : ""} w-100`}
|
||||
className={`${className} ${isError ? "Select_error" : ""} w-100`}
|
||||
value={formik.values[name]}
|
||||
allowClear
|
||||
{...(isMulti && { mode: "multiple" })}
|
||||
|
|
@ -64,11 +84,8 @@ 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}
|
||||
/>
|
||||
</ValidationFieldContainer>
|
||||
</Form.Item>
|
||||
</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,10 +15,11 @@ const MaltyFile = ({
|
|||
const { formik, t, isError } = useFormField(name, props);
|
||||
let imageUrl = formik?.values?.[name] ?? null;
|
||||
|
||||
// Memoizing the fileList to prevent unnecessary recalculations
|
||||
const fileList = useMemo(() => {
|
||||
return imageUrl
|
||||
// Mapping formik values to fileList format
|
||||
const fileList = imageUrl
|
||||
? imageUrl.map((file: any, index: number) => {
|
||||
// console.log(file,"file");
|
||||
|
||||
return file instanceof File
|
||||
? {
|
||||
uid: index,
|
||||
|
|
@ -36,7 +37,6 @@ const MaltyFile = ({
|
|||
};
|
||||
})
|
||||
: [];
|
||||
}, [imageUrl]); // Dependency array ensures it recalculates only when imageUrl changes
|
||||
|
||||
const FilehandleChange = ({ fileList }: any) => {
|
||||
if (fileList.length === 0) {
|
||||
|
|
@ -48,7 +48,6 @@ const MaltyFile = ({
|
|||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Custom request function
|
||||
const customRequest = async ({ onSuccess }: any) => {
|
||||
// Perform any necessary actions before onSuccess is called
|
||||
|
|
@ -64,20 +63,19 @@ const MaltyFile = ({
|
|||
<Upload
|
||||
disabled={isDisabled}
|
||||
listType="picture"
|
||||
fileList={fileList} // Using memoized fileList
|
||||
fileList={fileList} // Using fileList instead of defaultFileList
|
||||
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"}
|
||||
className={isError ? "isError w-100" : " w-100"}
|
||||
icon={<UploadOutlined />}
|
||||
>
|
||||
{t(`input.` + placeholder) ?? t("input.upload_image")}
|
||||
</Button>
|
||||
<div className="Error_color">{isError ? "required" : ""}</div>
|
||||
<div className="Error_color"> {isError ? "required" : ""}</div>
|
||||
</Upload>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
70
src/Components/ValidationField/View/NumberField.tsx
Normal file
70
src/Components/ValidationField/View/NumberField.tsx
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
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,19 +1,18 @@
|
|||
import { InputNumber } from "antd";
|
||||
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 { 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: {
|
||||
|
|
@ -24,15 +23,28 @@ const NumberFormate = ({
|
|||
};
|
||||
return (
|
||||
<div className="ValidationField w-100">
|
||||
<ValidationFieldLabel
|
||||
name={name}
|
||||
label={label}
|
||||
label_icon={label_icon}
|
||||
no_label={no_label}
|
||||
placeholder={placeholder}
|
||||
t={t}
|
||||
/>
|
||||
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||
{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}
|
||||
formatter={(value: any) =>
|
||||
|
|
@ -45,16 +57,16 @@ const NumberFormate = ({
|
|||
type={type ?? "text"}
|
||||
value={formik.values[name]}
|
||||
onChange={SelectableChange}
|
||||
placeholder={t(`input.${placeholder || label || name}`)}
|
||||
placeholder={t(
|
||||
`input.${placeholder ? placeholder : label ? label : name}`,
|
||||
)}
|
||||
name={name}
|
||||
disabled={isDisabled}
|
||||
size="large"
|
||||
id={name}
|
||||
{...props}
|
||||
|
||||
// onChange={onChange ? onChange : handleChange}
|
||||
/>
|
||||
</ValidationFieldContainer>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,143 +1,87 @@
|
|||
import { Select, Spin } from "antd";
|
||||
import { Form, Select, Spin } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import useFormField from "../../../Hooks/useFormField";
|
||||
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";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { MdOutlineEdit } from "react-icons/md";
|
||||
|
||||
const SearchField = ({
|
||||
name,
|
||||
label,
|
||||
placeholder,
|
||||
isDisabled,
|
||||
searchBy = "search",
|
||||
option = [],
|
||||
searchBy,
|
||||
option,
|
||||
isMulti,
|
||||
onChange,
|
||||
className,
|
||||
props,
|
||||
no_label,
|
||||
label_icon,
|
||||
isLoading,
|
||||
canChangePage,
|
||||
PageName,
|
||||
page,
|
||||
...props
|
||||
}: SearchFieldProps) => {
|
||||
}: any) => {
|
||||
const { errorMsg, isError, t, formik } = useFormField(name, props);
|
||||
const { pushValidationParamState, setValidationParamState } =
|
||||
useValidationValidationParamState();
|
||||
|
||||
const [AllPagesOption, setAllPagesOption] = useState<any>([]);
|
||||
|
||||
const [searchQuery, setSearchQuery] = useState<string>("");
|
||||
const navigate = useNavigate();
|
||||
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 searchParams = new URLSearchParams(window?.location?.search);
|
||||
setSearchQuery(searchParams?.get("search") || "");
|
||||
}, []);
|
||||
|
||||
const SelectableChange = (value: any) => {
|
||||
const SelectableChange = (value: {
|
||||
value: string;
|
||||
label: React.ReactNode;
|
||||
}) => {
|
||||
formik?.setFieldValue(name, value);
|
||||
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,
|
||||
const SearchHandleChange = (value: any) => {
|
||||
navigate(`${window?.location?.pathname}?${searchBy}=${value}`, {
|
||||
replace: true,
|
||||
});
|
||||
}
|
||||
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">
|
||||
<ValidationFieldLabel
|
||||
name={name}
|
||||
label={label}
|
||||
label_icon={label_icon}
|
||||
no_label={no_label}
|
||||
placeholder={placeholder}
|
||||
t={t}
|
||||
/>
|
||||
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||
<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 : ""}
|
||||
>
|
||||
<Select
|
||||
placeholder={t(`input.${placeholder || label || name}`)}
|
||||
placeholder={t(
|
||||
`input.${placeholder ? placeholder : label ? label : name}`,
|
||||
)}
|
||||
disabled={isDisabled}
|
||||
options={AllPagesOption}
|
||||
options={option}
|
||||
size="large"
|
||||
className={`${className} w-100`}
|
||||
value={formik.values[name]}
|
||||
// loading={isLoading}
|
||||
loading={isLoading}
|
||||
allowClear
|
||||
{...(isMulti && { mode: "multiple" })}
|
||||
onChange={onChange || SelectableChange}
|
||||
showSearch
|
||||
optionFilterProp="name"
|
||||
notFoundContent={isLoading ? <Spin /> : t("validation.undefined")}
|
||||
onSearch={handleChange}
|
||||
onBlur={handleBlur}
|
||||
id={name}
|
||||
onPopupScroll={handleScroll}
|
||||
fieldNames={{ label: "name", value: "id" }}
|
||||
{...props}
|
||||
optionFilterProp="label"
|
||||
notFoundContent={isLoading ? <Spin /> : "لا يوجد"}
|
||||
onSearch={SearchHandleChange}
|
||||
/>
|
||||
</ValidationFieldContainer>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
import { Select } from "antd";
|
||||
import { Form, Select, Spin } 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,
|
||||
|
|
@ -15,11 +13,10 @@ const SelectField = ({
|
|||
isMulti,
|
||||
onChange,
|
||||
className,
|
||||
props,
|
||||
no_label,
|
||||
label_icon,
|
||||
isLoading,
|
||||
...props
|
||||
}: SelectFieldProps) => {
|
||||
}: any) => {
|
||||
const { errorMsg, isError, t, formik } = useFormField(name, props);
|
||||
const SelectableChange = (value: {
|
||||
value: string;
|
||||
|
|
@ -27,36 +24,47 @@ const SelectField = ({
|
|||
}) => {
|
||||
formik.setFieldValue(name, value);
|
||||
};
|
||||
const options = translateOptions(option, t);
|
||||
// console.log(name,"Select");
|
||||
|
||||
return (
|
||||
<div className="ValidationField w-100">
|
||||
<ValidationFieldLabel
|
||||
name={name}
|
||||
label={label}
|
||||
label_icon={label_icon}
|
||||
no_label={no_label}
|
||||
placeholder={placeholder}
|
||||
t={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>
|
||||
)}
|
||||
|
||||
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||
<Form.Item
|
||||
hasFeedback
|
||||
validateStatus={isError ? "error" : ""}
|
||||
help={isError ? errorMsg : ""}
|
||||
>
|
||||
<Select
|
||||
placeholder={t(`input.${placeholder || label || name}`)}
|
||||
placeholder={t(
|
||||
`input.${placeholder ? placeholder : label ? label : name}`,
|
||||
)}
|
||||
disabled={isDisabled}
|
||||
options={options}
|
||||
{...(isLoading && { loading: isLoading })}
|
||||
options={translateOptions(option, t)}
|
||||
loading={option?.length < 1}
|
||||
size="large"
|
||||
className={`${className} ${isError ? "SelectError" : ""} w-100`}
|
||||
className={`${className} ${isError ? "Select_error" : ""} w-100`}
|
||||
value={formik.values[name]}
|
||||
allowClear
|
||||
{...(isMulti && { mode: "multiple" })}
|
||||
onChange={onChange || SelectableChange}
|
||||
showSearch={false}
|
||||
id={name}
|
||||
fieldNames={{label:"name",value:"id"}}
|
||||
{...props}
|
||||
/>
|
||||
</ValidationFieldContainer>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import { Input } from "antd";
|
||||
import { Form, 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 = ({
|
||||
|
|
@ -12,12 +11,14 @@ 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);
|
||||
};
|
||||
|
||||
|
|
@ -26,7 +27,11 @@ const TextAreaField = ({
|
|||
<label htmlFor={name} className="text">
|
||||
{t(`input.${label ? label : name}`)}
|
||||
</label>
|
||||
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||
<Form.Item
|
||||
hasFeedback
|
||||
validateStatus={isError ? "error" : ""}
|
||||
help={isError ? errorMsg : ""}
|
||||
>
|
||||
<Field
|
||||
as={TextArea}
|
||||
placeholder={t(`input.${placeholder ? placeholder : name}`)}
|
||||
|
|
@ -34,10 +39,10 @@ const TextAreaField = ({
|
|||
disabled={isDisabled}
|
||||
size="large"
|
||||
onChange={onChange || handleChange}
|
||||
id={name}
|
||||
|
||||
// onChange={onChange ? onChange : handleChange}
|
||||
/>
|
||||
</ValidationFieldContainer>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ 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 = ({
|
||||
|
|
@ -23,20 +21,33 @@ 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 ?? ""} `}>
|
||||
<ValidationFieldLabel
|
||||
name={name}
|
||||
label={label}
|
||||
label_icon={label_icon}
|
||||
no_label={no_label}
|
||||
placeholder={placeholder}
|
||||
t={t}
|
||||
/>
|
||||
{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>
|
||||
)}
|
||||
|
||||
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
|
||||
<Form.Item
|
||||
hasFeedback
|
||||
validateStatus={isError ? "error" : ""}
|
||||
help={isError ? errorMsg : ""}
|
||||
>
|
||||
<Field
|
||||
as={TextArea}
|
||||
placeholder={t(`input.${placeholder ? placeholder : name}`)}
|
||||
|
|
@ -47,9 +58,8 @@ const TextField = ({
|
|||
maxLength={1000}
|
||||
onChange={onChange || TextFilehandleChange}
|
||||
style={{ height: 120 }}
|
||||
id={name}
|
||||
/>
|
||||
</ValidationFieldContainer>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ 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,
|
||||
|
|
@ -21,19 +20,27 @@ const Time = ({
|
|||
formik.setFieldValue(name, value);
|
||||
};
|
||||
|
||||
const Formatter = "H:mm";
|
||||
const Formater = "H:mm";
|
||||
const FormikValue = formik.values[name];
|
||||
|
||||
return (
|
||||
<div className="ValidationField w-100 ">
|
||||
<ValidationFieldLabel
|
||||
name={name}
|
||||
label={label}
|
||||
label_icon={label_icon}
|
||||
no_label={no_label}
|
||||
placeholder={placeholder}
|
||||
t={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>
|
||||
)}
|
||||
|
||||
<Form.Item
|
||||
hasFeedback
|
||||
|
|
@ -44,13 +51,14 @@ const Time = ({
|
|||
allowClear
|
||||
className={`${className} w-100`}
|
||||
size="large"
|
||||
value={FormikValue ? dayjs(FormikValue, Formatter) : null}
|
||||
value={FormikValue ? dayjs(FormikValue, Formater) : null}
|
||||
onChange={onChange || onCalendarChange}
|
||||
disabled={isDisabled}
|
||||
placeholder={t(`input.${placeholder || label || name}`)}
|
||||
format={Formatter}
|
||||
placeholder={t(
|
||||
`input.${placeholder ? placeholder : label ? label : name}`,
|
||||
)}
|
||||
format={Formater}
|
||||
needConfirm={false}
|
||||
id={name}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
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>
|
||||
);
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
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,6 +2,7 @@ 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,
|
||||
|
|
@ -11,4 +12,5 @@ export {
|
|||
useFormikContext,
|
||||
useTranslation,
|
||||
FaExclamationCircle,
|
||||
convert_data_to_select,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
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,13 +4,10 @@
|
|||
justify-content: space-between;
|
||||
}
|
||||
.ValidationField {
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 1.3vw;
|
||||
position: relative;
|
||||
min-height: 80px;
|
||||
padding-inline: 20px;
|
||||
> * {
|
||||
width: 100% !important;
|
||||
min-width: 150px;
|
||||
width: 100%;
|
||||
}
|
||||
.text,
|
||||
.ant-form-item {
|
||||
|
|
@ -19,55 +16,213 @@
|
|||
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%;
|
||||
}
|
||||
|
||||
.ant-btn-default{
|
||||
padding: 7px 11px;
|
||||
height: var(--fieldHeight);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
.Checkboxs {
|
||||
padding: 4%;
|
||||
}
|
||||
|
||||
|
||||
//// number input
|
||||
///
|
||||
.ant-input-number-affix-wrapper-lg{
|
||||
width: 100%;
|
||||
.ant-checkbox-wrapper {
|
||||
min-width: 100px;
|
||||
}
|
||||
.ValidationFieldCheckbox{
|
||||
// background-color: red;
|
||||
}
|
||||
|
||||
/// input hight
|
||||
.ant-form-item-control-input-content{
|
||||
height: var(--fieldHeight);
|
||||
}
|
||||
|
||||
|
||||
//// date picker
|
||||
.ant-picker-large{
|
||||
height: var(--fieldHeight);
|
||||
}
|
||||
|
||||
|
||||
/// text area
|
||||
///
|
||||
|
||||
.ValidationFieldTextArea{
|
||||
.ant-form-item-control-input-content{
|
||||
min-height: 120px;
|
||||
.SearchField {
|
||||
button {
|
||||
background: var(--primary);
|
||||
}
|
||||
|
||||
}
|
||||
.text {
|
||||
color: var(--text);
|
||||
margin-bottom: 15px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.ant-input-textarea-affix-wrapper.ant-input-affix-wrapper {
|
||||
|
||||
height: 120px;
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-input-number-affix-wrapper-lg {
|
||||
width: 100%;
|
||||
height: 3vw;
|
||||
border-radius: 0.6vw;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.TwoSelectGroup {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.TwoSelectGroupbutton {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.ant-checkbox-wrapper {
|
||||
margin-top: 25px !important;
|
||||
}
|
||||
|
||||
.add_new_button {
|
||||
margin-bottom: 20px;
|
||||
svg {
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
||||
.ValidationField:has(.input_number) {
|
||||
max-width: 100px;
|
||||
|
||||
.input_number {
|
||||
max-width: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
gap: 30px;
|
||||
max-width: 80% !important;
|
||||
}
|
||||
7
src/Components/ValidationField/utils/getNestedValue.ts
Normal file
7
src/Components/ValidationField/utils/getNestedValue.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
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,7 +1,6 @@
|
|||
export const translateOptions = (options: any, t: any) => {
|
||||
return options?.map((opt: any) => ({
|
||||
return options.map((opt: any) => ({
|
||||
...opt,
|
||||
label: t(`${opt?.label}`),
|
||||
name: t(`${opt?.name}`),
|
||||
label: t(`${opt.label}`),
|
||||
}));
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,60 +1,149 @@
|
|||
import { InputProps, SelectProps } from "antd";
|
||||
import { InputProps } from "antd";
|
||||
|
||||
// Common properties for all field types
|
||||
interface BaseFieldProps {
|
||||
export type ValidationFieldType =
|
||||
| "text"
|
||||
| "Select"
|
||||
| "LocalSearch"
|
||||
| "Search"
|
||||
| "DataRange"
|
||||
| "Date"
|
||||
| "Time"
|
||||
| "File"
|
||||
| "MaltyFile"
|
||||
| "DropFile"
|
||||
| "Checkbox"
|
||||
| "number"
|
||||
| "password"
|
||||
| "email"
|
||||
| "TextArea";
|
||||
|
||||
export interface ValidationFieldPropsText {
|
||||
name: string;
|
||||
no_label?: boolean;
|
||||
label_icon?: boolean;
|
||||
type: "text";
|
||||
placeholder?: string;
|
||||
label?: string;
|
||||
className?: string;
|
||||
isDisabled?: boolean;
|
||||
onChange?: (value: any) => void;
|
||||
dir?: "ltr" | "rtl";
|
||||
}
|
||||
|
||||
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";
|
||||
option: any[];
|
||||
isMulti?: boolean;
|
||||
}
|
||||
|
||||
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;
|
||||
type: "Search";
|
||||
placeholder?: string;
|
||||
label?: string;
|
||||
className?: string;
|
||||
isDisabled?: boolean;
|
||||
onChange?: (value: any) => void;
|
||||
dir?: "ltr" | "rtl";
|
||||
option: any[];
|
||||
isMulti?: 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";
|
||||
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";
|
||||
no_label?: boolean;
|
||||
label_icon?: boolean;
|
||||
}
|
||||
|
||||
// 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 type SearchFieldProps = BaseFieldProps &
|
||||
SelectProps & {
|
||||
type: "Search";
|
||||
option: any[];
|
||||
isMulti?: boolean;
|
||||
isLoading: boolean;
|
||||
searchBy: string;
|
||||
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";
|
||||
picker?: "data" | "week" | "month" | "quarter" | "year";
|
||||
};
|
||||
|
||||
type FileFieldProps = BaseFieldProps & {
|
||||
export interface ValidationFieldPropsFile {
|
||||
name: string;
|
||||
no_label?: boolean;
|
||||
label_icon?: boolean;
|
||||
type: "File" | "MaltyFile" | "DropFile";
|
||||
};
|
||||
|
||||
type CheckboxFieldProps = BaseFieldProps & {
|
||||
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: "Checkbox";
|
||||
label?: string;
|
||||
className?: string;
|
||||
isDisabled?: boolean;
|
||||
onChange?: (value: any) => void;
|
||||
dir?: "ltr" | "rtl";
|
||||
Group?: boolean;
|
||||
};
|
||||
|
||||
export type FieldProps = BaseFieldProps &
|
||||
InputProps & {
|
||||
}
|
||||
export interface ValidationFieldPropstext {
|
||||
name: string;
|
||||
no_label?: boolean;
|
||||
label_icon?: boolean;
|
||||
type?:
|
||||
| "text"
|
||||
| "number"
|
||||
|
|
@ -62,19 +151,45 @@ export type FieldProps = BaseFieldProps &
|
|||
| "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;
|
||||
};
|
||||
[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;
|
||||
}
|
||||
|
||||
// Union type for all field types
|
||||
export type ValidationFieldProps =
|
||||
| SelectFieldProps
|
||||
| DateFieldProps
|
||||
| FileFieldProps
|
||||
| CheckboxFieldProps
|
||||
| SearchFieldProps
|
||||
| FieldProps;
|
||||
|
||||
// Validation field type
|
||||
export type ValidationFieldType = ValidationFieldProps["type"];
|
||||
| ValidationFieldPropsInput
|
||||
| ValidationFieldPropsSelect
|
||||
| ValidationFieldPropsLocalSearch
|
||||
| ValidationFieldPropsDataRange
|
||||
| ValidationFieldPropsDate
|
||||
| ValidationFieldPropsTime
|
||||
| ValidationFieldPropsFile
|
||||
| ValidationFieldPropsCheckbox
|
||||
| ValidationFieldPropstext
|
||||
| ValidationFieldPropsSearch;
|
||||
|
|
|
|||
|
|
@ -26,12 +26,9 @@ 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,7 +9,6 @@ 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();
|
||||
|
|
@ -22,7 +21,7 @@ const FilterLayout = ({filterTitle, sub_children}:{filterTitle:string,sub_childr
|
|||
return (
|
||||
<div className='filter_header'>
|
||||
<div className='filter_header_top'>
|
||||
<h4>{t(filterTitle)}</h4>
|
||||
<h1>{t(filterTitle)}</h1>
|
||||
<div className='filter_and_order_by'>
|
||||
<span>
|
||||
<LayoutFilterModal
|
||||
|
|
@ -49,8 +48,8 @@ const FilterLayout = ({filterTitle, sub_children}:{filterTitle:string,sub_childr
|
|||
<p>{t("ادخالات")}</p>
|
||||
</span>
|
||||
<div className="header_search">
|
||||
<SearchField
|
||||
searchBy=''
|
||||
<SearchFieldWithSelect
|
||||
options={translateArray}
|
||||
placeholder={t("practical.search_here")}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
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 = "800px",
|
||||
width = "45vw",
|
||||
isLoading = false,
|
||||
}: LayoutModalProps) => {
|
||||
const { isOpen, setIsOpen } = useModalState((state) => state);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import React, { lazy, Suspense } from "react";
|
||||
import React 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,43 +22,29 @@ 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: isLoading || isRefetching,
|
||||
indicator:<Suspense fallback={<></>}>
|
||||
<LoadingLottie />
|
||||
</Suspense> ,
|
||||
spinning: response?.isLoading,
|
||||
indicator: <Loading />,
|
||||
size: "large",
|
||||
}}
|
||||
locale={{
|
||||
emptyText: (
|
||||
isLoading || isRefetching ?
|
||||
<></>
|
||||
:
|
||||
|
||||
<Suspense fallback={<></>}>
|
||||
<NotFoundLottie
|
||||
|
||||
<EmptyData
|
||||
loading={response?.isLoading}
|
||||
header={t("Table.header")}
|
||||
info={t("Table.info")}
|
||||
/>
|
||||
</Suspense>
|
||||
),
|
||||
}}
|
||||
pagination={{
|
||||
...pagination,
|
||||
onChange: handlePageChange,
|
||||
nextIcon:<>{t("practical.next")}</>,
|
||||
prevIcon:<> {t("practical.prev")} </>,
|
||||
className:"pagination_antd"
|
||||
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
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 || 2,
|
||||
pageSize: data?.meta?.per_page || 10,
|
||||
total: data?.meta?.total || 0,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setPagination({
|
||||
current: data?.meta?.current_page || 1,
|
||||
pageSize: data?.meta?.per_page || 2,
|
||||
pageSize: data?.meta?.per_page || 10,
|
||||
total: data?.meta?.total || 0,
|
||||
});
|
||||
}, [data]);
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ const NavBar = ({isOpen}:{isOpen:boolean}) => {
|
|||
|
||||
return (
|
||||
<div className="NavBar">
|
||||
<Suspense fallback={<></>}>
|
||||
<Suspense fallback={<SpinContainer />}>
|
||||
{/* <span className="navbar_link" onClick={handelNavigate}>
|
||||
<MdOutlineArrowForwardIos /> {PageTitle}
|
||||
</span> */}
|
||||
|
|
@ -40,7 +40,6 @@ 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="500px"
|
||||
width="40vw"
|
||||
>
|
||||
<ModelForm />
|
||||
</LayoutModel>
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ const EditModel: React.FC = () => {
|
|||
getInitialValues={getInitialValues(objectToEdit)}
|
||||
getValidationSchema={getValidationSchema}
|
||||
isAddModal={false}
|
||||
width="500px"
|
||||
width="40vw"
|
||||
>
|
||||
<ModelForm />
|
||||
</LayoutModel>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ const TableHeader = () => {
|
|||
return (
|
||||
<div className="TableWithHeader">
|
||||
<Suspense fallback={<Spin />}>
|
||||
|
||||
<PageHeader
|
||||
pageTitle="grade"
|
||||
ModelAbility={ModalEnum?.GRADE_ADD}
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
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;
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
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;
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
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;
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
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"),
|
||||
});
|
||||
};
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
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;
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
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;
|
||||
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
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, { Suspense, useContext, useEffect, useMemo } from "react";
|
||||
import React, { 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,16 +12,10 @@ 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, 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";
|
||||
import { ParamsEnum } from "../../enums/params";
|
||||
import { useGetAllUnit } from "../../api/unit";
|
||||
|
||||
interface DataType {
|
||||
id: string; // Unique identifier for each row
|
||||
|
|
@ -38,7 +32,7 @@ interface RowContextProps {
|
|||
|
||||
const RowContext = React.createContext<RowContextProps>({});
|
||||
|
||||
export const DragHandleUnit: React.FC = () => {
|
||||
const DragHandle: React.FC = () => {
|
||||
const { setActivatorNodeRef, listeners } = useContext(RowContext);
|
||||
return (
|
||||
<Button
|
||||
|
|
@ -52,6 +46,12 @@ export const DragHandleUnit: 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,10 +96,13 @@ 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
|
||||
...item
|
||||
name: item.name,
|
||||
age: item.age,
|
||||
address: item.address,
|
||||
})) ?? [];
|
||||
|
||||
const [dataSource, setDataSource] = React.useState<DataType[]>(data);
|
||||
console.log(dataSource, "dataSource");
|
||||
|
||||
useEffect(() => {
|
||||
// Update dataSource when the fetched data changes
|
||||
|
|
@ -107,51 +110,28 @@ 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);
|
||||
const orderedNewState = newState.map((item, index) => ({
|
||||
|
||||
// Update the order based on the new positions
|
||||
return 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
|
||||
|
|
@ -161,32 +141,9 @@ const DrapableTable: React.FC = () => {
|
|||
<Table
|
||||
rowKey="id"
|
||||
components={{ body: { row: Row } }}
|
||||
//@ts-ignore
|
||||
columns={columns}
|
||||
dataSource={sortedDataSource}
|
||||
dataSource={dataSource.sort((a, b) => a.order - b.order)} // Sort by order for rendering
|
||||
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 { subject_id } = useParams();
|
||||
const { curriculum_id } = useParams();
|
||||
const { OldObjectToEdit } = useObjectToEdit();
|
||||
|
||||
const handleSubmit = (values: any) => {
|
||||
|
|
@ -21,7 +21,7 @@ const AddModel: React.FC = () => {
|
|||
|
||||
mutate({
|
||||
...values,
|
||||
subject_id: subject_id,
|
||||
curriculum_id: curriculum_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);
|
||||
console.log(termsArray, "termsArray");
|
||||
|
||||
return (
|
||||
<Row className="w-100">
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ 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";
|
||||
|
|
@ -14,7 +15,7 @@ import FilterLayout from "../../../Layout/Dashboard/FilterLayout";
|
|||
import FilterForm from "./Model/FilterForm";
|
||||
import { canAddUnit } from "../../../utils/hasAbilityFn";
|
||||
|
||||
const Table = lazy(() => import("./DrapableTable"));
|
||||
const Table = lazy(() => import("./Table"));
|
||||
const AddModalForm = lazy(() => import("./Model/AddModel"));
|
||||
const EditModalForm = lazy(() => import("./Model/EditModel"));
|
||||
const DeleteModalForm = lazy(
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@ 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();
|
||||
|
|
@ -42,8 +40,6 @@ export const useColumns = () => {
|
|||
const [t] = useTranslation();
|
||||
|
||||
const columns: TableColumnsType<Unit> = [
|
||||
{ key: "sort", align: "center", width: 80, render: () => <DragHandleUnit /> },
|
||||
|
||||
{
|
||||
title: t("columns.id"),
|
||||
dataIndex: "id",
|
||||
|
|
@ -56,7 +52,7 @@ export const useColumns = () => {
|
|||
dataIndex: "name",
|
||||
key: "name",
|
||||
align: "center",
|
||||
render: (text) => text,
|
||||
render: (text, record) => record?.name,
|
||||
},
|
||||
|
||||
{
|
||||
|
|
@ -64,10 +60,20 @@ export const useColumns = () => {
|
|||
dataIndex: "term",
|
||||
key: "term",
|
||||
align: "center",
|
||||
render: (text) => t(ConvertEnumToTranslate(text)),
|
||||
render: (text, record) => record?.term,
|
||||
},
|
||||
|
||||
{
|
||||
// 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,10 +5,8 @@ const Form = () => {
|
|||
return (
|
||||
<Row className="w-100">
|
||||
<Col>
|
||||
<ValidationField placeholder="name" label="name" name="name" type="Number" />
|
||||
|
||||
<ValidationField placeholder="name" label="name" name="name" />
|
||||
</Col>
|
||||
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
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 { canAddUser } from "../../../utils/hasAbilityFn";
|
||||
import { canAddReport, canAddTags, canAddUser } from "../../../utils/hasAbilityFn";
|
||||
import useSetPageTitle from "../../../Hooks/useSetPageTitle";
|
||||
import { useDeleteTag } from "../../../api/tags";
|
||||
import PageHeader from "../../../Layout/Dashboard/PageHeader";
|
||||
|
|
@ -12,8 +14,12 @@ 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,7 +10,6 @@ const App: React.FC = () => {
|
|||
const response = useGetAllUser({
|
||||
name: searchQuery,
|
||||
pagination: true,
|
||||
|
||||
});
|
||||
|
||||
return <DataTable response={response} useColumns={useColumns} />;
|
||||
|
|
|
|||
|
|
@ -1,194 +0,0 @@
|
|||
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,7 +42,6 @@ const AddModel: React.FC = () => {
|
|||
handleSubmit={handleSubmit}
|
||||
getInitialValues={getInitialValues({})}
|
||||
getValidationSchema={getValidationSchema}
|
||||
width="500px"
|
||||
>
|
||||
<ModelForm />
|
||||
</LayoutModel>
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@ 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("./DrapableTable"));
|
||||
const Table = lazy(() => import("./Table"));
|
||||
const AddModalForm = lazy(() => import("./Model/AddModel"));
|
||||
const EditModalForm = lazy(() => import("./Model/EditModel"));
|
||||
const DeleteModelsForm = lazy(
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import {
|
|||
canShowLesson,
|
||||
} from "../../../utils/hasAbilityFn";
|
||||
import ActionButtons from "../../../Components/Table/ActionButtons";
|
||||
import { DragHandleLesson } from "./DrapableTable";
|
||||
|
||||
export const useColumns = () => {
|
||||
const { handel_open_model } = useModalHandler();
|
||||
|
|
@ -38,7 +37,6 @@ 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/Form";
|
||||
import BaseForm from "./Model/Malty/Add";
|
||||
import ModelForm from "./Model/ModelForm";
|
||||
const AcceptModal = lazy(() => import("./Model/AcceptModal"));
|
||||
|
||||
|
|
@ -42,7 +42,6 @@ const AddPage: React.FC = () => {
|
|||
isBseQuestion,
|
||||
setTagsSearch,
|
||||
setObjectToEdit,
|
||||
objectToEdit,
|
||||
setSuccess,
|
||||
SavedQuestionData,
|
||||
} = useObjectToEdit();
|
||||
|
|
@ -98,19 +97,20 @@ 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,
|
||||
content_image: DataToSend?.content_image ?? "",
|
||||
image: DataToSend?.image ?? "",
|
||||
isBase: 1,
|
||||
lessons_ids: [lesson_id],
|
||||
canAnswersBeShuffled,
|
||||
|
|
@ -146,77 +146,71 @@ 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
|
||||
}
|
||||
})
|
||||
const NewQuestion = {
|
||||
|
||||
mutate({
|
||||
...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);
|
||||
// console.log(SavedQuestionData);
|
||||
let cleanedAnswers = cleanObject(SavedQuestionData);
|
||||
let noChange = hasItems(cleanedAnswers);
|
||||
|
||||
// useSaveOnDisconnect(noChange, QUESTION_OBJECT_KEY, SavedQuestionData);
|
||||
useSaveOnDisconnect(noChange, QUESTION_OBJECT_KEY, SavedQuestionData);
|
||||
|
||||
// const SavedData = getLocalStorageQuestions(QUESTION_OBJECT_KEY);
|
||||
// console.log(SavedData);
|
||||
const SavedData = getLocalStorageQuestions(QUESTION_OBJECT_KEY);
|
||||
|
||||
const handleCancel = () => {
|
||||
if (!noChange) {
|
||||
navigate(-1);
|
||||
// if (!noChange) {
|
||||
// localStorage.removeItem(QUESTION_OBJECT_KEY);
|
||||
// } else {
|
||||
// setIsOpen(ModalEnum?.QUESTION_ACCEPT);
|
||||
// }
|
||||
localStorage.removeItem(QUESTION_OBJECT_KEY);
|
||||
} else {
|
||||
setIsOpen(ModalEnum?.QUESTION_ACCEPT);
|
||||
}
|
||||
};
|
||||
|
||||
if (isBseQuestion ) {
|
||||
if (isBseQuestion || SavedData?.isBase === 1) {
|
||||
return (
|
||||
<div className="exercise_add">
|
||||
<FormikForm
|
||||
handleSubmit={handleSubmit}
|
||||
initialValues={getInitialValuesBase(objectToEdit)}
|
||||
initialValues={getInitialValuesBase(SavedData)}
|
||||
validationSchema={getValidationSchemaBase}
|
||||
>
|
||||
<main className="w-100 exercise_add_main">
|
||||
<Header />
|
||||
<BaseForm />
|
||||
<Header/>
|
||||
<ModelForm/>
|
||||
|
||||
<div className="exercise_add_buttons">
|
||||
<div onClick={handleCancel}>{t("practical.back")}</div>
|
||||
|
|
@ -242,7 +236,7 @@ const AddPage: React.FC = () => {
|
|||
<div className="exercise_add">
|
||||
<FormikForm
|
||||
handleSubmit={handleSubmit}
|
||||
initialValues={getInitialValues(objectToEdit)}
|
||||
initialValues={getInitialValues(SavedData)}
|
||||
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/Form";
|
||||
import BaseForm from "./Model/Malty/Edit";
|
||||
import { Question } from "../../../types/Item";
|
||||
import { toast } from "react-toastify";
|
||||
import useSetPageTitle from "../../../Hooks/useSetPageTitle";
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
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?.answers?.some(
|
||||
const allAreZero = formik?.values?.QuestionOptions?.some(
|
||||
(item: any) => item.isCorrect === 1,
|
||||
);
|
||||
|
||||
if (allAreZero) {
|
||||
formik?.values.answers.forEach((item: any, index: number) => {
|
||||
formik.setFieldValue(`answers[${index}].isCorrect`, 0);
|
||||
formik?.values.QuestionOptions.forEach((item: any, index: number) => {
|
||||
formik.setFieldValue(`QuestionOptions[${index}].isCorrect`, 0);
|
||||
});
|
||||
}
|
||||
|
||||
formik.setFieldValue(
|
||||
`answers[${name}].isCorrect`,
|
||||
`QuestionOptions[${name}].isCorrect`,
|
||||
value?.target?.checked ? 1 : 0,
|
||||
);
|
||||
};
|
||||
|
|
@ -35,7 +35,7 @@ const CheckboxField = ({
|
|||
<Checkbox
|
||||
onChange={onChange || CheckboxhandleChange}
|
||||
disabled={isDisabled}
|
||||
checked={formik.values?.answers?.[name]?.isCorrect === 1}
|
||||
checked={formik.values?.QuestionOptions?.[name]?.isCorrect === 1}
|
||||
className={className}
|
||||
>
|
||||
{t(`input.${label ? label : name}`)}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,10 @@ import { useTranslation } from "react-i18next";
|
|||
import { getCharFromNumber } from "../../../../../utils/getCharFromNumber";
|
||||
import CheckboxField from "./CheckboxField";
|
||||
import TextField from "./TextField";
|
||||
import ImageBoxField from "../../../../../Components/CustomFields/ImageBoxField/ImageBoxField";
|
||||
import File from "./File";
|
||||
import { FaCirclePlus, FaDeleteLeft } from "react-icons/fa6";
|
||||
import { GoTrash } from "react-icons/go";
|
||||
import { LuImagePlus } from "react-icons/lu";
|
||||
|
||||
const ChoiceFields = ({ index, data }: { index: number; data: Choice }) => {
|
||||
const formik = useFormikContext<any>();
|
||||
|
|
@ -16,56 +18,46 @@ const ChoiceFields = ({ index, data }: { index: number; data: Choice }) => {
|
|||
|
||||
const handleDeleteChoice = () => {
|
||||
console.log(index);
|
||||
console.log(formik.values.answers[index]);
|
||||
console.log(formik.values.QuestionOptions[index]);
|
||||
|
||||
const updatedAnswers = formik.values.answers.filter(
|
||||
const updatedQuestionOptions = formik.values.QuestionOptions.filter(
|
||||
(_: any, i: any) => i !== index,
|
||||
);
|
||||
|
||||
formik.setFieldValue("answers", updatedAnswers);
|
||||
formik.setFieldValue("QuestionOptions", updatedQuestionOptions);
|
||||
};
|
||||
console.log(formik.values);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="ChoiceFields">
|
||||
<TextField
|
||||
className="textarea_exercise"
|
||||
placeholder={"choice"}
|
||||
label2={t(`input.choice`) + ` ` +`( ${t(`alphabet.${getCharFromNumber(index)}`)} )`}
|
||||
label2={t(`input.choice`) + ` ` + `(${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
|
||||
<CheckboxField
|
||||
className=""
|
||||
label="The_correct_answer"
|
||||
name={index}
|
||||
type="Checkbox"
|
||||
|
||||
/>
|
||||
<p className="delete_question_options">
|
||||
/>
|
||||
<p className="delete_question_options">
|
||||
{t("header.delete_choice")}
|
||||
<GoTrash className="trash_icon" onClick={handleDeleteChoice} size={17} />
|
||||
</p>
|
||||
</div>
|
||||
</p>
|
||||
</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) => (
|
||||
{(provided:any) => (
|
||||
<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) => (
|
||||
{(provided:any) => (
|
||||
<div
|
||||
ref={provided.innerRef}
|
||||
{...provided.draggableProps}
|
||||
|
|
|
|||
87
src/Pages/Admin/question/Model/Field/File.tsx
Normal file
87
src/Pages/Admin/question/Model/Field/File.tsx
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
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;
|
||||
69
src/Pages/Admin/question/Model/Field/HintField.tsx
Normal file
69
src/Pages/Admin/question/Model/Field/HintField.tsx
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
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 = `answers[${name}].content`;
|
||||
const newName = `QuestionOptions[${name}].answer`;
|
||||
const { formik, isError, errorMsg, t } = useFormField(newName, props);
|
||||
const TextFilehandleChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||
|
|
|
|||
|
|
@ -10,14 +10,17 @@ 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);
|
||||
|
|
@ -29,13 +32,14 @@ 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}].answers`, [
|
||||
formik.setFieldValue(`Questions.[${parent_index}].QuestionOptions`, [
|
||||
...((formik?.values as any)?.Questions?.[parent_index]
|
||||
.answers as Choice[]),
|
||||
.QuestionOptions as Choice[]),
|
||||
|
||||
{
|
||||
answer: null,
|
||||
|
|
@ -54,9 +58,9 @@ const Form = () => {
|
|||
image: "",
|
||||
parent: "",
|
||||
isBase: 0,
|
||||
// max_mark: 1,
|
||||
// min_mark_to_pass: 1,
|
||||
answers: [{ answer: null, answer_image: null, isCorrect: 0 }],
|
||||
max_mark: 1,
|
||||
min_mark_to_pass: 1,
|
||||
QuestionOptions: [{ answer: null, answer_image: null, isCorrect: 0 }],
|
||||
tags: [],
|
||||
},
|
||||
]);
|
||||
|
|
@ -66,6 +70,7 @@ const Form = () => {
|
|||
formik.setFieldValue("max_mark", max_mark);
|
||||
};
|
||||
const [t] = useTranslation();
|
||||
console.log(formik.errors);
|
||||
|
||||
return (
|
||||
<Row className="w-100">
|
||||
|
|
@ -104,9 +109,21 @@ const Form = () => {
|
|||
/>
|
||||
</div>
|
||||
|
||||
<Choices parent_index={parent_index} />
|
||||
{(
|
||||
(formik?.values as any)?.Questions?.[parent_index]
|
||||
?.QuestionOptions || []
|
||||
).map((item: Choice, index: number) => {
|
||||
return (
|
||||
<ChoiceFields
|
||||
key={index}
|
||||
parent_index={parent_index}
|
||||
index={index}
|
||||
data={item}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
{formik?.values?.Questions?.[parent_index]?.answers
|
||||
{formik?.values?.Questions?.[parent_index]?.QuestionOptions
|
||||
?.length < 5 && (
|
||||
<p className="add_new_button">
|
||||
<FaCirclePlus
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import React from "react";
|
||||
import useFormField from "../../../../../../Hooks/useFormField";
|
||||
import { Checkbox, Form } from "antd";
|
||||
import { useFormik, useFormikContext } from "formik";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
|
@ -16,15 +15,16 @@ 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
|
||||
]?.answers?.some((item: any) => item.isCorrect === 1);
|
||||
]?.QuestionOptions?.some((item: any) => item.isCorrect === 1);
|
||||
if (allAreZero) {
|
||||
formik?.values?.Questions?.[parent_index]?.answers.forEach(
|
||||
formik?.values?.Questions?.[parent_index]?.QuestionOptions.forEach(
|
||||
(item: any, index: number) => {
|
||||
formik.setFieldValue(
|
||||
`Questions[${parent_index}].answers[${index}].isCorrect`,
|
||||
`Questions[${parent_index}].QuestionOptions[${index}].isCorrect`,
|
||||
0,
|
||||
);
|
||||
},
|
||||
|
|
@ -32,7 +32,7 @@ const CheckboxField = ({
|
|||
}
|
||||
|
||||
formik.setFieldValue(
|
||||
`Questions[${parent_index}].answers[${name}].isCorrect`,
|
||||
`Questions[${parent_index}].QuestionOptions[${name}].isCorrect`,
|
||||
value?.target?.checked ? 1 : 0,
|
||||
);
|
||||
};
|
||||
|
|
@ -42,7 +42,7 @@ const CheckboxField = ({
|
|||
onChange={onChange || CheckboxhandleChange}
|
||||
disabled={isDisabled}
|
||||
checked={
|
||||
formik.values?.Questions?.[parent_index]?.answers?.[name]
|
||||
formik.values?.Questions?.[parent_index]?.QuestionOptions?.[name]
|
||||
?.isCorrect === 1
|
||||
}
|
||||
className={className}
|
||||
|
|
|
|||
|
|
@ -9,9 +9,6 @@ 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,
|
||||
|
|
@ -28,7 +25,7 @@ const ChoiceFields = ({
|
|||
|
||||
const handleDeleteChoice = () => {
|
||||
const arrayLength =
|
||||
formik.values.Questions?.[parent_index].answers?.length;
|
||||
formik.values.Questions?.[parent_index].QuestionOptions?.length;
|
||||
|
||||
console.log(arrayLength);
|
||||
|
||||
|
|
@ -39,58 +36,43 @@ const ChoiceFields = ({
|
|||
return;
|
||||
}
|
||||
|
||||
const updatedAnswers = formik.values.Questions?.[
|
||||
const updatedQuestionOptions = formik.values.Questions?.[
|
||||
parent_index
|
||||
].answers.filter((_: any, i: any) => i !== index);
|
||||
].QuestionOptions.filter((_: any, i: any) => i !== index);
|
||||
formik.setFieldValue(
|
||||
`Questions[${parent_index}].answers`,
|
||||
updatedAnswers,
|
||||
`Questions[${parent_index}].QuestionOptions`,
|
||||
updatedQuestionOptions,
|
||||
);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div className="ChoiceFields">
|
||||
<TextField
|
||||
className="textarea_exercise"
|
||||
placeholder={"choice"}
|
||||
label2={t(`input.choice`) + ` ` + `( ${t(`alphabet.${getCharFromNumber(index)}`)} )`}
|
||||
label2={t(`input.choice`) + ` ` + `(${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
|
||||
<CheckboxField
|
||||
className=""
|
||||
label="The_correct_answer"
|
||||
name={index}
|
||||
type="Checkbox"
|
||||
parent_index={parent_index}
|
||||
/>
|
||||
<p className="delete_question_options">
|
||||
{t("header.delete_choice")}
|
||||
<GoTrash className="trash_icon" onClick={handleDeleteChoice} size={17} />
|
||||
</p>
|
||||
</div>
|
||||
<p className="delete_question_options">
|
||||
<FaTrash onClick={handleDeleteChoice} size={17} />
|
||||
</p>
|
||||
</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}].answers[${name}].answer_image`;
|
||||
const newName = `Questions[${parent_index}].QuestionOptions[${name}].answer_image`;
|
||||
|
||||
const { formik, t, isError, errorMsg } = useFormField(newName, props);
|
||||
let imageUrl =
|
||||
formik?.values?.Questions?.[parent_index]?.answers[name]
|
||||
formik?.values?.Questions?.[parent_index]?.QuestionOptions[name]
|
||||
?.answer_image ?? null;
|
||||
// console.log(imageUrl);
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ const File = ({
|
|||
formik.setFieldValue(newName, null);
|
||||
} else {
|
||||
formik.setFieldValue(
|
||||
`Questions[${parent_index}].answers[${name}].answer_image`,
|
||||
`Questions[${parent_index}].QuestionOptions[${name}].answer_image`,
|
||||
value?.file?.originFileObj,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ const TextField = ({
|
|||
parent_index,
|
||||
className,
|
||||
}: any) => {
|
||||
const newName = `Questions[${parent_index}].answers[${name}].content`;
|
||||
const newName = `Questions[${parent_index}].QuestionOptions[${name}].answer`;
|
||||
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}].answers`, [
|
||||
formik.setFieldValue(`Questions.[${parent_index}].QuestionOptions`, [
|
||||
...((formik?.values as any)?.Questions?.[parent_index]
|
||||
.answers as Choice[]),
|
||||
.QuestionOptions as Choice[]),
|
||||
|
||||
{
|
||||
answer: null,
|
||||
|
|
@ -53,7 +53,7 @@ const Form = () => {
|
|||
isBase: 0,
|
||||
max_mark: 1,
|
||||
min_mark_to_pass: 1,
|
||||
answers: [{ answer: null, answer_image: null, isCorrect: 0 }],
|
||||
QuestionOptions: [{ answer: null, answer_image: null, isCorrect: 0 }],
|
||||
tags: [],
|
||||
},
|
||||
]);
|
||||
|
|
@ -103,7 +103,7 @@ const Form = () => {
|
|||
|
||||
{(
|
||||
(formik?.values as any)?.Questions?.[parent_index]
|
||||
?.answers || []
|
||||
?.QuestionOptions || []
|
||||
).map((item: Choice, index: number) => {
|
||||
return (
|
||||
<ChoiceFields
|
||||
|
|
@ -114,7 +114,7 @@ const Form = () => {
|
|||
/>
|
||||
);
|
||||
})}
|
||||
{formik?.values?.Questions?.[parent_index]?.answers
|
||||
{formik?.values?.Questions?.[parent_index]?.QuestionOptions
|
||||
?.length < 5 && (
|
||||
<p className="add_new_button">
|
||||
<FaCirclePlus
|
||||
|
|
|
|||
|
|
@ -1,134 +0,0 @@
|
|||
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,13 +9,10 @@ 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();
|
||||
|
|
@ -28,43 +25,31 @@ const QuestionFIeld = ({ index, data }: { index: number; data: Choice }) => {
|
|||
if (DeleteQuestionId?.id) {
|
||||
setDeletedQuestions([...DeletedQuestions, DeleteQuestionId]);
|
||||
}
|
||||
const updatedAnswers = formik.values.Questions.filter(
|
||||
const updatedQuestionOptions = formik.values.Questions.filter(
|
||||
(_: any, i: any) => i !== index,
|
||||
);
|
||||
formik.setFieldValue(`Questions`, updatedAnswers);
|
||||
formik.setFieldValue(`Questions`, updatedQuestionOptions);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<div className="exercise_forms">
|
||||
<div className="ChoiceFields">
|
||||
<TextField
|
||||
className="textarea_exercise"
|
||||
placeholder={"question"}
|
||||
label2={t(`input.question`) + ` ` + `( ${t(`alphabet.${getCharFromNumber(index)}`)} )`}
|
||||
placeholder={"choice"}
|
||||
label2={t(`input.question`) + ` ` + `${index + 1}`}
|
||||
name={index}
|
||||
id={`question_${index + 1}`}
|
||||
type="TextArea"
|
||||
/>
|
||||
|
||||
<ImageBoxField name={`Questions.${index}.image`} />
|
||||
|
||||
<div className="answer_status">
|
||||
|
||||
<File
|
||||
className="file_exercise"
|
||||
label={"attachment"}
|
||||
name={index}
|
||||
type="File"
|
||||
/>
|
||||
<p className="delete_question_options">
|
||||
{t("header.delete_question")}
|
||||
<GoTrash className="trash_icon" onClick={handleDeleteQuestion} size={17} />
|
||||
<FaTrash onClick={handleDeleteQuestion} size={17} />
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,77 +0,0 @@
|
|||
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