Compare commits

...

9 Commits

Author SHA1 Message Date
karimaldeen
1a78474120 add karim field v4 2024-09-10 10:19:33 +03:00
karimaldeen
b2ea58b19e FIX PAGE 2024-09-09 16:23:12 +03:00
karimaldeen
4f944eab55 fix multy question 2024-09-09 14:36:47 +03:00
karimaldeen
6e1e79465f fix question 2024-09-09 13:46:31 +03:00
karimaldeen
084762e51e add tag select 2024-09-09 12:51:56 +03:00
karimaldeen
10af7490a1 change it 2024-09-08 22:24:57 +03:00
karimaldeen
c21796cfc1 fix scss and remove un nesasery code 2024-09-08 10:24:33 +03:00
karimaldeen
f949c5eeb8 fixx 2024-09-07 21:05:20 +03:00
karimaldeen
378d2fd7de fix readme 2024-09-07 12:18:02 +03:00
149 changed files with 2654 additions and 2533 deletions

View File

@ -5,7 +5,7 @@
1. Clone the repository:
```bash
git clone https://git.point-dev.net/Karimaldeen/zaker.git
git clone https://git.point-dev.net/Karimaldeen/Quiz_dashboard.git
```
```bash

View File

@ -4,28 +4,27 @@
"private": true,
"dependencies": {
"@ant-design/icons": "^5.3.7",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/modifiers": "^7.0.0",
"@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2",
"antd": "^5.17.4",
"axios": "^1.7.2",
"bootstrap": "^5.3.3",
"dayjs": "^1.11.11",
"formik": "^2.4.6",
"html-to-image": "^1.11.11",
"i18next": "^23.11.5",
"path-to-regexp": "^6.2.2",
"pdf-lib": "^1.17.1",
"lottie-react": "^2.4.0",
"react": "^18.3.1",
"react-beautiful-dnd": "^13.1.1",
"react-dom": "^18.3.1",
"react-i18next": "^13.5.0",
"react-icons": "^4.12.0",
"react-mathjax": "^1.0.1",
"react-query": "^3.39.3",
"react-router-dom": "^6.23.1",
"react-toastify": "^9.1.3",
"reactstrap": "^9.2.2",
"sass": "^1.77.4",
"ts-node": "^10.9.2",
"vite-plugin-env-compatible": "^2.0.1",
"yup": "^1.4.0",
"zustand": "^4.5.2"
},
@ -61,6 +60,7 @@
"@testing-library/user-event": "^13.5.0",
"@types/node": "^20.14.0",
"@types/react": "^18.3.3",
"@types/react-beautiful-dnd": "^13.1.8",
"@types/react-dom": "^18.3.0",
"@types/react-helmet": "^6.1.11",
"@vitejs/plugin-legacy": "^5.4.1",

View File

@ -11,6 +11,18 @@ importers:
'@ant-design/icons':
specifier: ^5.3.7
version: 5.3.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@dnd-kit/core':
specifier: ^6.1.0
version: 6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@dnd-kit/modifiers':
specifier: ^7.0.0
version: 7.0.0(@dnd-kit/core@6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
'@dnd-kit/sortable':
specifier: ^8.0.0
version: 8.0.0(@dnd-kit/core@6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
'@dnd-kit/utilities':
specifier: ^3.2.2
version: 3.2.2(react@18.3.1)
antd:
specifier: ^5.17.4
version: 5.17.4(date-fns@3.3.1)(luxon@3.4.4)(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@ -26,18 +38,12 @@ importers:
formik:
specifier: ^2.4.6
version: 2.4.6(react@18.3.1)
html-to-image:
specifier: ^1.11.11
version: 1.11.11
i18next:
specifier: ^23.11.5
version: 23.11.5
path-to-regexp:
specifier: ^6.2.2
version: 6.2.2
pdf-lib:
specifier: ^1.17.1
version: 1.17.1
lottie-react:
specifier: ^2.4.0
version: 2.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react:
specifier: ^18.3.1
version: 18.3.1
@ -53,9 +59,6 @@ importers:
react-icons:
specifier: ^4.12.0
version: 4.12.0(react@18.3.1)
react-mathjax:
specifier: ^1.0.1
version: 1.0.1(react@18.3.1)
react-query:
specifier: ^3.39.3
version: 3.39.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@ -71,12 +74,6 @@ importers:
sass:
specifier: ^1.77.4
version: 1.77.4
ts-node:
specifier: ^10.9.2
version: 10.9.2(@types/node@20.14.0)(typescript@4.9.5)
vite-plugin-env-compatible:
specifier: ^2.0.1
version: 2.0.1
yup:
specifier: ^1.4.0
version: 1.4.0
@ -99,6 +96,9 @@ importers:
'@types/react':
specifier: ^18.3.3
version: 18.3.3
'@types/react-beautiful-dnd':
specifier: ^13.1.8
version: 13.1.8
'@types/react-dom':
specifier: ^18.3.0
version: 18.3.0
@ -877,6 +877,34 @@ packages:
resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==}
engines: {node: '>=10.0.0'}
'@dnd-kit/accessibility@3.1.0':
resolution: {integrity: sha512-ea7IkhKvlJUv9iSHJOnxinBcoOI3ppGnnL+VDJ75O45Nss6HtZd8IdN8touXPDtASfeI2T2LImb8VOZcL47wjQ==}
peerDependencies:
react: '>=16.8.0'
'@dnd-kit/core@6.1.0':
resolution: {integrity: sha512-J3cQBClB4TVxwGo3KEjssGEXNJqGVWx17aRTZ1ob0FliR5IjYgTxl5YJbKTzA6IzrtelotH19v6y7uoIRUZPSg==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
'@dnd-kit/modifiers@7.0.0':
resolution: {integrity: sha512-BG/ETy3eBjFap7+zIti53f0PCLGDzNXyTmn6fSdrudORf+OH04MxrW4p5+mPu4mgMk9kM41iYONjc3DOUWTcfg==}
peerDependencies:
'@dnd-kit/core': ^6.1.0
react: '>=16.8.0'
'@dnd-kit/sortable@8.0.0':
resolution: {integrity: sha512-U3jk5ebVXe1Lr7c2wU7SBZjcWdQP+j7peHJfCspnA81enlu88Mgd7CC8Q+pub9ubP7eKVETzJW+IBAhsqbSu/g==}
peerDependencies:
'@dnd-kit/core': ^6.1.0
react: '>=16.8.0'
'@dnd-kit/utilities@3.2.2':
resolution: {integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==}
peerDependencies:
react: '>=16.8.0'
'@emotion/hash@0.8.0':
resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==}
@ -1122,12 +1150,6 @@ packages:
'@jridgewell/trace-mapping@0.3.9':
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
'@pdf-lib/standard-fonts@1.0.0':
resolution: {integrity: sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==}
'@pdf-lib/upng@1.0.1':
resolution: {integrity: sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==}
'@popperjs/core@2.11.8':
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
@ -1363,6 +1385,9 @@ packages:
'@types/prop-types@15.7.12':
resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==}
'@types/react-beautiful-dnd@13.1.8':
resolution: {integrity: sha512-E3TyFsro9pQuK4r8S/OL6G99eq7p8v29sX0PM7oT8Z+PJfZvSQTx4zTQbUJ+QZXioAF0e7TGBEcA1XhYhCweyQ==}
'@types/react-dom@18.3.0':
resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==}
@ -1883,13 +1908,6 @@ packages:
dom-helpers@5.2.1:
resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
dotenv-expand@5.1.0:
resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==}
dotenv@8.2.0:
resolution: {integrity: sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==}
engines: {node: '>=8'}
electron-to-chromium@1.4.788:
resolution: {integrity: sha512-ubp5+Ev/VV8KuRoWnfP2QF2Bg+O2ZFdb49DiiNbz2VmgkIqrnyYaqIOqj8A6K/3p1xV0QcU5hBQ1+BmB6ot1OA==}
@ -2139,9 +2157,6 @@ packages:
html-parse-stringify@3.0.1:
resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==}
html-to-image@1.11.11:
resolution: {integrity: sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA==}
http-proxy-agent@7.0.2:
resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
engines: {node: '>= 14'}
@ -2535,9 +2550,6 @@ packages:
lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
load-script@1.0.0:
resolution: {integrity: sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==}
loader-runner@4.3.0:
resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==}
engines: {node: '>=6.11.5'}
@ -2562,6 +2574,15 @@ packages:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
hasBin: true
lottie-react@2.4.0:
resolution: {integrity: sha512-pDJGj+AQlnlyHvOHFK7vLdsDcvbuqvwPZdMlJ360wrzGFurXeKPr8SiRCjLf3LrNYKANQtSsh5dz9UYQHuqx4w==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
lottie-web@5.12.2:
resolution: {integrity: sha512-uvhvYPC8kGPjXT3MyKMrL3JitEAmDMp30lVkuq/590Mw9ok6pWcFCwXJveo0t5uqYw1UREQHofD+jVpdjBv8wg==}
lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
@ -2714,9 +2735,6 @@ packages:
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
engines: {node: '>=6'}
pako@1.0.11:
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
parent-module@1.0.1:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
engines: {node: '>=6'}
@ -2743,16 +2761,10 @@ packages:
path-parse@1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
path-to-regexp@6.2.2:
resolution: {integrity: sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==}
path-type@4.0.0:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
engines: {node: '>=8'}
pdf-lib@1.17.1:
resolution: {integrity: sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==}
picocolors@1.0.1:
resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
@ -3098,11 +3110,6 @@ packages:
react-is@18.3.1:
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
react-mathjax@1.0.1:
resolution: {integrity: sha512-+mjFcciZY3GQoqiQm3aRTyDjgBKuoaXpY+SCONX00jScuPpTKwnASeFMS5+pbTIzDf5zPT2Y9ZZfQ9U/d4CKtQ==}
peerDependencies:
react: ^16.3.0
react-popper@2.3.0:
resolution: {integrity: sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==}
peerDependencies:
@ -3540,9 +3547,6 @@ packages:
'@swc/wasm':
optional: true
tslib@1.14.1:
resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
tslib@2.6.2:
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
@ -3633,9 +3637,6 @@ packages:
peerDependencies:
vite: '>=2.0.0'
vite-plugin-env-compatible@2.0.1:
resolution: {integrity: sha512-DRrOZTg/W44ojVQQfGSMPEgYQGzp5TeIpt9cpaK35hTOC/b2D7Ffl8/RIgK8vQ0mlnDIUgETcA173bnMEkyzdw==}
vite@5.2.12:
resolution: {integrity: sha512-/gC8GxzxMK5ntBwb48pR32GGhENnjtY30G4A0jemunsBkiEZFw60s8InGpN8gkhHEkjnRK1aSAxeQgwvFhUHAA==}
engines: {node: ^18.0.0 || >=20.0.0}
@ -4764,11 +4765,44 @@ snapshots:
'@cspotcode/source-map-support@0.8.1':
dependencies:
'@jridgewell/trace-mapping': 0.3.9
optional: true
'@ctrl/tinycolor@3.6.1': {}
'@discoveryjs/json-ext@0.5.7': {}
'@dnd-kit/accessibility@3.1.0(react@18.3.1)':
dependencies:
react: 18.3.1
tslib: 2.6.2
'@dnd-kit/core@6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@dnd-kit/accessibility': 3.1.0(react@18.3.1)
'@dnd-kit/utilities': 3.2.2(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
tslib: 2.6.2
'@dnd-kit/modifiers@7.0.0(@dnd-kit/core@6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)':
dependencies:
'@dnd-kit/core': 6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@dnd-kit/utilities': 3.2.2(react@18.3.1)
react: 18.3.1
tslib: 2.6.2
'@dnd-kit/sortable@8.0.0(@dnd-kit/core@6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)':
dependencies:
'@dnd-kit/core': 6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@dnd-kit/utilities': 3.2.2(react@18.3.1)
react: 18.3.1
tslib: 2.6.2
'@dnd-kit/utilities@3.2.2(react@18.3.1)':
dependencies:
react: 18.3.1
tslib: 2.6.2
'@emotion/hash@0.8.0': {}
'@emotion/unitless@0.7.5': {}
@ -5041,15 +5075,8 @@ snapshots:
'@jridgewell/trace-mapping@0.3.9':
dependencies:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.4.15
'@pdf-lib/standard-fonts@1.0.0':
dependencies:
pako: 1.0.11
'@pdf-lib/upng@1.0.1':
dependencies:
pako: 1.0.11
'@jridgewell/sourcemap-codec': 1.5.0
optional: true
'@popperjs/core@2.11.8': {}
@ -5221,13 +5248,17 @@ snapshots:
'@babel/runtime': 7.24.6
'@testing-library/dom': 9.3.3
'@tsconfig/node10@1.0.11': {}
'@tsconfig/node10@1.0.11':
optional: true
'@tsconfig/node12@1.0.11': {}
'@tsconfig/node12@1.0.11':
optional: true
'@tsconfig/node14@1.0.3': {}
'@tsconfig/node14@1.0.3':
optional: true
'@tsconfig/node16@1.0.4': {}
'@tsconfig/node16@1.0.4':
optional: true
'@types/aria-query@5.0.4': {}
@ -5299,6 +5330,10 @@ snapshots:
'@types/prop-types@15.7.12': {}
'@types/react-beautiful-dnd@13.1.8':
dependencies:
'@types/react': 18.3.3
'@types/react-dom@18.3.0':
dependencies:
'@types/react': 18.3.3
@ -5459,6 +5494,7 @@ snapshots:
acorn-walk@8.3.3:
dependencies:
acorn: 8.12.1
optional: true
acorn@8.12.1: {}
@ -5557,7 +5593,8 @@ snapshots:
normalize-path: 3.0.0
picomatch: 2.3.1
arg@4.1.3: {}
arg@4.1.3:
optional: true
argparse@1.0.10:
dependencies:
@ -5866,7 +5903,8 @@ snapshots:
- supports-color
- ts-node
create-require@1.1.1: {}
create-require@1.1.1:
optional: true
cross-spawn@7.0.3:
dependencies:
@ -5953,7 +5991,8 @@ snapshots:
diff-sequences@29.6.3: {}
diff@4.0.2: {}
diff@4.0.2:
optional: true
dom-accessibility-api@0.5.16: {}
@ -5962,10 +6001,6 @@ snapshots:
'@babel/runtime': 7.24.6
csstype: 3.1.3
dotenv-expand@5.1.0: {}
dotenv@8.2.0: {}
electron-to-chromium@1.4.788: {}
electron-to-chromium@1.5.5: {}
@ -6215,8 +6250,6 @@ snapshots:
dependencies:
void-elements: 3.1.0
html-to-image@1.11.11: {}
http-proxy-agent@7.0.2:
dependencies:
agent-base: 7.1.1
@ -6796,8 +6829,6 @@ snapshots:
lines-and-columns@1.2.4: {}
load-script@1.0.0: {}
loader-runner@4.3.0: {}
locate-path@5.0.0:
@ -6816,6 +6847,14 @@ snapshots:
dependencies:
js-tokens: 4.0.0
lottie-react@2.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
lottie-web: 5.12.2
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
lottie-web@5.12.2: {}
lru-cache@5.1.1:
dependencies:
yallist: 3.1.1
@ -6948,8 +6987,6 @@ snapshots:
p-try@2.2.0: {}
pako@1.0.11: {}
parent-module@1.0.1:
dependencies:
callsites: 3.1.0
@ -6974,18 +7011,9 @@ snapshots:
path-parse@1.0.7: {}
path-to-regexp@6.2.2: {}
path-type@4.0.0:
optional: true
pdf-lib@1.17.1:
dependencies:
'@pdf-lib/standard-fonts': 1.0.0
'@pdf-lib/upng': 1.0.1
pako: 1.0.11
tslib: 1.14.1
picocolors@1.0.1: {}
picomatch@2.3.1: {}
@ -7416,11 +7444,6 @@ snapshots:
react-is@18.3.1: {}
react-mathjax@1.0.1(react@18.3.1):
dependencies:
load-script: 1.0.0
react: 18.3.1
react-popper@2.3.0(@popperjs/core@2.11.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@popperjs/core': 2.11.8
@ -7855,8 +7878,7 @@ snapshots:
typescript: 4.9.5
v8-compile-cache-lib: 3.0.1
yn: 3.1.1
tslib@1.14.1: {}
optional: true
tslib@2.6.2: {}
@ -7919,7 +7941,8 @@ snapshots:
dependencies:
react: 18.3.1
v8-compile-cache-lib@3.0.1: {}
v8-compile-cache-lib@3.0.1:
optional: true
v8-to-istanbul@9.2.0:
dependencies:
@ -7936,11 +7959,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
vite-plugin-env-compatible@2.0.1:
dependencies:
dotenv: 8.2.0
dotenv-expand: 5.1.0
vite@5.2.12(@types/node@20.14.0)(sass@1.77.4)(terser@5.31.4):
dependencies:
esbuild: 0.20.2
@ -8109,7 +8127,8 @@ snapshots:
y18n: 5.0.8
yargs-parser: 21.1.1
yn@3.1.1: {}
yn@3.1.1:
optional: true
yocto-queue@0.1.0: {}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 698 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.41177 10.8602C9.60628 10.8602 9.77726 10.7862 9.92471 10.6381C10.0722 10.49 10.1462 10.3191 10.1468 10.1252C10.1475 9.9313 10.0734 9.76031 9.92471 9.61224C9.776 9.46416 9.60502 9.39012 9.41177 9.39012C9.21851 9.39012 9.04753 9.46416 8.89882 9.61224C8.75012 9.76031 8.67608 9.9313 8.67671 10.1252C8.67733 10.3191 8.75137 10.49 8.89882 10.6381C9.04628 10.7862 9.21726 10.8609 9.41177 10.8602ZM8.99576 8.20988H9.82777C9.85224 7.81522 9.91467 7.51812 10.0151 7.31859C10.1148 7.11906 10.3555 6.84047 10.7369 6.48282C11.1347 6.12141 11.4121 5.79828 11.5689 5.51341C11.7258 5.22981 11.8042 4.90384 11.8042 4.53553C11.8042 3.90181 11.5784 3.37381 11.1266 2.95153C10.6748 2.52863 10.1032 2.31718 9.41177 2.31718C8.8891 2.31718 8.42479 2.45835 8.01882 2.74071C7.61286 3.02306 7.30384 3.408 7.09177 3.89553L7.85506 4.23153C8.03263 3.86384 8.2491 3.58777 8.50447 3.4033C8.75984 3.21883 9.06227 3.1269 9.41177 3.12753C9.86102 3.12753 10.2334 3.26055 10.5289 3.52659C10.8238 3.79263 10.9713 4.13616 10.9713 4.55718C10.9713 4.81318 10.8998 5.0513 10.7567 5.27153C10.613 5.49177 10.3661 5.75686 10.016 6.06682C9.61945 6.41318 9.35028 6.73098 9.20847 7.02024C9.06667 7.30949 8.99576 7.70604 8.99576 8.20988ZM4.34447 13.1765C3.9109 13.1765 3.54918 13.0315 3.2593 12.7416C2.96941 12.4518 2.82416 12.09 2.82353 11.6565V1.52C2.82353 1.08706 2.96879 0.725335 3.2593 0.434825C3.54981 0.144316 3.91153 -0.000625423 4.34447 2.0284e-06H14.48C14.9129 2.0284e-06 15.2747 0.144943 15.5652 0.434825C15.8557 0.724708 16.0006 1.08643 16 1.52V11.6565C16 12.0894 15.8551 12.4508 15.5652 12.7407C15.2753 13.0306 14.9133 13.1758 14.4791 13.1765H4.34447ZM4.34447 12.2353H14.48C14.6243 12.2353 14.757 12.1751 14.8781 12.0546C14.9992 11.9341 15.0595 11.8014 15.0588 11.6565V1.52C15.0588 1.37569 14.9986 1.24298 14.8781 1.12188C14.7576 1.00079 14.6246 0.940551 14.4791 0.941178H4.34447C4.19953 0.941178 4.06651 1.00141 3.94541 1.12188C3.82432 1.24235 3.76408 1.37506 3.76471 1.52V11.6565C3.76471 11.8008 3.82494 11.9335 3.94541 12.0546C4.06588 12.1757 4.19859 12.2359 4.34353 12.2353M1.52 16C1.08706 16 0.725335 15.8551 0.434825 15.5652C0.144316 15.2753 -0.000625423 14.9136 2.0284e-06 14.48V3.40235H0.941178V14.48C0.941178 14.6243 1.00141 14.757 1.12188 14.8781C1.24235 14.9992 1.37506 15.0595 1.52 15.0588H12.5976V16H1.52Z" fill="#202C4B"/>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,42 @@
.ImageBoxField{
.ImageBox{
width: 120px;
height: 120px;
display: flex;
align-items: center;
justify-content: center;
border: max(1.5px,.1vw) dashed #a9c3f1;
margin-block: 10px;
border-radius: 5px;
.ImageBoxIcon{
cursor: pointer;
}
.imagePreview{
max-width: 99%;
height: auto;
max-height: 99%;
object-fit: contain;
border-radius: 5px;
}
}
.ImageHeader{
display: flex;
align-items: center;
justify-content: flex-end;
gap: 10px;
}
.ImageCancelIcon{
width: 16px !important;
height: 16px !important;
}
.ImageBoxIcon{
width: 20px !important;
height: 20px !important;
}
}

View File

@ -0,0 +1,88 @@
import { useFormikContext } from "formik";
import { useState, useRef, useEffect } from "react";
import './ImageBoxField.scss';
import ImageIcon from "./ImageIcon";
import ImageCancelIcon from "./ImageCancelIcon";
import { getNestedValue } from "../../../utils/getNestedValue";
import { generateImagePreview } from "./generateImagePreview";
// Helper function to generate image preview from a File
const ImageBoxField = ({ name }: any) => {
const formik = useFormikContext<any>();
const value = getNestedValue(formik.values, name);
const [imagePreview, setImagePreview] = useState<string | null>(null);
const fileInputRef = useRef<HTMLInputElement | null>(null);
console.log(formik.values);
console.log(value,name);
useEffect(() => {
if (value instanceof File) {
generateImagePreview(value, setImagePreview);
} else if (typeof value === 'string') {
setImagePreview(value);
} else {
setImagePreview(null);
}
}, [value]);
const handleFileChange = (event: any) => {
const file = event.target.files[0];
if (file) {
generateImagePreview(file, setImagePreview);
formik.setFieldValue(name, file);
}
};
const handleButtonClick = () => {
const fileInput = fileInputRef.current;
if (fileInput) {
fileInput.click();
}
};
const handleCancel = () => {
setImagePreview("");
formik.setFieldValue(name, "");
if (fileInputRef.current) {
fileInputRef.current.value = "";
}
};
return (
<div className="ImageBoxField">
<div className="ImageHeader">
{imagePreview ? (
<>
<ImageCancelIcon onClick={handleCancel} className="ImageCancelIcon" />
<ImageIcon onClick={handleButtonClick} className="ImageBoxIcon" />
</>
) : (
<div className="VisibleHidden">
hidden
</div>
)}
</div>
<div className="ImageBox">
{imagePreview ? (
<img src={imagePreview} alt="Preview" className="imagePreview" />
) : (
<ImageIcon onClick={handleButtonClick} className="ImageBoxIcon" />
)}
</div>
<input
id={`file-input-${name}`}
type="file"
accept="image/png, image/jpeg, image/webp"
style={{ display: "none" }}
onChange={handleFileChange}
ref={fileInputRef}
/>
</div>
);
};
export default ImageBoxField;

View File

@ -0,0 +1,27 @@
import React from 'react';
interface ImageCancelIconProps extends React.HTMLAttributes<HTMLDivElement> {
}
const ImageCancelIcon: React.FC<ImageCancelIconProps> = (props) => {
return (
<div {...props}>
<svg
viewBox="0 0 14 14"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7 5.44469L12.4447 0L14 1.55531L8.55531 7L14 12.4447L12.4436 14L6.9989 8.55531L1.55531 14L0 12.4436L5.44469 6.9989L0 1.55421L1.55531 0.00109986L7 5.44469Z"
fill="#515B73"
/>
</svg>
</div>
);
}
export default ImageCancelIcon;

View File

@ -0,0 +1,27 @@
import React from 'react';
interface ImageIconProps extends React.HTMLAttributes<HTMLDivElement> {
}
const ImageIcon: React.FC<ImageIconProps> = (props) => {
return (
<div {...props}>
<svg
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11.25 5.625C11.25 7.11684 10.6574 8.54758 9.60248 9.60248C8.54758 10.6574 7.11684 11.25 5.625 11.25C4.13316 11.25 2.70242 10.6574 1.64752 9.60248C0.592632 8.54758 0 7.11684 0 5.625C0 4.13316 0.592632 2.70242 1.64752 1.64752C2.70242 0.592632 4.13316 0 5.625 0C7.11684 0 8.54758 0.592632 9.60248 1.64752C10.6574 2.70242 11.25 4.13316 11.25 5.625ZM6.25 3.125C6.25 2.95924 6.18415 2.80027 6.06694 2.68306C5.94973 2.56585 5.79076 2.5 5.625 2.5C5.45924 2.5 5.30027 2.56585 5.18306 2.68306C5.06585 2.80027 5 2.95924 5 3.125V5H3.125C2.95924 5 2.80027 5.06585 2.68306 5.18306C2.56585 5.30027 2.5 5.45924 2.5 5.625C2.5 5.79076 2.56585 5.94973 2.68306 6.06694C2.80027 6.18415 2.95924 6.25 3.125 6.25H5V8.125C5 8.29076 5.06585 8.44973 5.18306 8.56694C5.30027 8.68415 5.45924 8.75 5.625 8.75C5.79076 8.75 5.94973 8.68415 6.06694 8.56694C6.18415 8.44973 6.25 8.29076 6.25 8.125V6.25H8.125C8.29076 6.25 8.44973 6.18415 8.56694 6.06694C8.68415 5.94973 8.75 5.79076 8.75 5.625C8.75 5.45924 8.68415 5.30027 8.56694 5.18306C8.44973 5.06585 8.29076 5 8.125 5H6.25V3.125ZM16.25 3.75H12.2413C12.1187 3.3183 11.9542 2.89964 11.75 2.5H16.25C17.2446 2.5 18.1984 2.89509 18.9017 3.59835C19.6049 4.30161 20 5.25544 20 6.25V16.25C20 17.2446 19.6049 18.1984 18.9017 18.9017C18.1984 19.6049 17.2446 20 16.25 20H6.25C5.25544 20 4.30161 19.6049 3.59835 18.9017C2.89509 18.1984 2.5 17.2446 2.5 16.25V11.75C2.89667 11.9533 3.31333 12.1171 3.75 12.2413V16.25C3.75 16.7162 3.8775 17.1525 4.1 17.525L9.93625 11.79C10.2869 11.4457 10.7586 11.2528 11.25 11.2528C11.7414 11.2528 12.2131 11.4457 12.5637 11.79L18.4012 17.525C18.6298 17.139 18.7502 16.6986 18.75 16.25V6.25C18.75 5.58696 18.4866 4.95107 18.0178 4.48223C17.5489 4.01339 16.913 3.75 16.25 3.75ZM16.25 8.125C16.25 8.37123 16.2015 8.61505 16.1073 8.84253C16.013 9.07002 15.8749 9.27672 15.7008 9.45083C15.5267 9.62494 15.32 9.76305 15.0925 9.85727C14.865 9.9515 14.6212 10 14.375 10C14.1288 10 13.885 9.9515 13.6575 9.85727C13.43 9.76305 13.2233 9.62494 13.0492 9.45083C12.8751 9.27672 12.737 9.07002 12.6427 8.84253C12.5485 8.61505 12.5 8.37123 12.5 8.125C12.5 7.62772 12.6975 7.15081 13.0492 6.79917C13.4008 6.44754 13.8777 6.25 14.375 6.25C14.8723 6.25 15.3492 6.44754 15.7008 6.79917C16.0525 7.15081 16.25 7.62772 16.25 8.125ZM15 8.125C15 7.95924 14.9342 7.80027 14.8169 7.68306C14.6997 7.56585 14.5408 7.5 14.375 7.5C14.2092 7.5 14.0503 7.56585 13.9331 7.68306C13.8158 7.80027 13.75 7.95924 13.75 8.125C13.75 8.29076 13.8158 8.44973 13.9331 8.56694C14.0503 8.68415 14.2092 8.75 14.375 8.75C14.5408 8.75 14.6997 8.68415 14.8169 8.56694C14.9342 8.44973 15 8.29076 15 8.125ZM4.985 18.4075C5.36871 18.6321 5.80538 18.7504 6.25 18.75H16.25C16.7125 18.75 17.1437 18.625 17.515 18.4075L11.6875 12.6825C11.5707 12.568 11.4136 12.5038 11.25 12.5038C11.0864 12.5038 10.9293 12.568 10.8125 12.6825L4.985 18.4075Z"
fill="#515B73"
/>
</svg>
</div>
);
}
export default ImageIcon;

View File

@ -0,0 +1,7 @@
export const generateImagePreview = (file: File, setImagePreview: (result: string) => void) => {
const reader = new FileReader();
reader.onloadend = () => {
setImagePreview(reader.result as string);
};
reader.readAsDataURL(file);
};

View File

@ -0,0 +1,78 @@
import React, { useState, useMemo } from 'react';
import { Select, Spin } from 'antd';
import { useTranslation } from 'react-i18next';
import { useDebounce } from '../../utils/useDebounce';
import { useGetAllTag } from '../../api/tags';
import { useFormikContext } from 'formik';
const SelectTag: React.FC = () => {
const [searchValue, setSearchValue] = useState<string>('');
const [fieldValue, setFieldValue] = useState<string>('');
const formik = useFormikContext<any>()
const handleChange = (value: string[]) => {
console.log(value);
formik.setFieldValue("tags",value)
setSearchValue('');
setFieldValue('');
};
const handleSearch = useDebounce((value: string) => {
setSearchValue(value);
});
const handleFieldChange = (value: string) => {
setFieldValue(value);
};
const handleBlur = () => {
setSearchValue('');
setFieldValue('');
};
const { data, isLoading } = useGetAllTag({
name: searchValue,
});
const [t] = useTranslation();
const options = data?.data ?? []
const additionalData = options?.length < 1 && searchValue.length > 1 && !isLoading ? [{id:`${searchValue}`,name:searchValue}] :[];
return (
<div className='SelectTag'>
<label htmlFor="">
{t("models.tag")}
</label>
<Select
mode="multiple"
allowClear
style={{ width: '100%' ,height:"40px"}}
placeholder=""
fieldNames={{ label: 'name', value: 'id' }}
onChange={handleChange}
options={[...options,...additionalData]}
filterOption={false}
loading={isLoading}
notFoundContent={isLoading ? <Spin /> : t("practical.not_found")}
onSearch={(value) => {
handleSearch(value);
handleFieldChange(value);
}}
searchValue={fieldValue}
onDropdownVisibleChange={(open) => {
if (!open) {
handleBlur();
}
}}
value={formik?.values?.tags ?? []}
/>
</div>
);
};
export default SelectTag;

View File

@ -1,19 +0,0 @@
import React from "react";
import Image from "../Ui/Image";
const AddedSuccessfully = () => {
return (
<div className="AddedSuccessfully">
<Image src="/DataState/successfully.png" />
<h1>تمّت إضافة الطالب بنجاح!</h1>
<p>تمّت إضافة الطالب،هل تريد إضافة طالب آخر ؟</p>
<div className="TowButton">
<button>إضافة طالب جديد</button>
<button>تخطّي</button>
</div>
</div>
);
};
export default AddedSuccessfully;

View File

@ -1,25 +0,0 @@
import React from "react";
import Image from "../Ui/Image";
const EmptyData = ({
header,
info,
loading,
}: {
info: string;
header: string;
loading: boolean;
}) => {
if (loading) {
return <></>;
}
return (
<div className="EmptyData">
<Image src="/DataState/EmptyData.gif" />
<h1>{header}</h1>
<p>{info}</p>
</div>
);
};
export default EmptyData;

View File

@ -1,12 +0,0 @@
import React from "react";
import Image from "../Ui/Image";
const Loading = () => {
return (
<div className="Loading">
<Image src="/DataState/loading.gif" />
</div>
);
};
export default Loading;

View File

@ -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"

View File

@ -11,12 +11,14 @@ interface Props {
options: Option[];
placeholder: string;
onSelect?: (option: Option) => void;
withIcon?:boolean
}
const SearchFieldWithSelect: React.FC<Props> = ({
options,
placeholder,
onSelect,
withIcon=false
}) => {
const [isOpen, setIsOpen] = useState(false);
const [selectedOption, setSelectedOption] = useState<Option | null>(null);
@ -58,17 +60,16 @@ const SearchFieldWithSelect: React.FC<Props> = ({
return (
<div ref={node} className={`search-field ${isOpen ? "open" : ""}`}>
<div className="search-header" onClick={toggleDropdown}>
<IoSearch className="search__icon" />
{withIcon && <IoSearch className="search__icon" />}
{/* <p className="search__input_text">{placeholder}</p> */}
<input
type="text"
className="search__input search__input_text"
placeholder={selectedOption ? selectedOption.label : ""}
// placeholder={selectedOption ? selectedOption.label : placeholder}
placeholder={selectedOption ? selectedOption.label : placeholder}
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<p className="search__input_text">{placeholder}</p>
{/* <IoMdArrowDropdown className={`search_select_icon ${isOpen ? 'open' : ''}`} /> */}
</div>
{(isOpen || (searchTerm !== "" && filteredOptions.length > 0)) && (
<div className="search-options">
@ -80,7 +81,7 @@ const SearchFieldWithSelect: React.FC<Props> = ({
onClick={() => handleOptionClick(option)}
>
<div>{option.label}</div>
<IoSearch className="search__icon" />
{/* {withIcon && <IoSearch className="search__icon" />} */}
</div>
))}
</div>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,13 @@
import React from 'react';
import Lottie from 'lottie-react'; // Default import
import animationData from './Loading.json'; // Import your Lottie JSON animation
const LoadingLottie = () => {
return (
<div className="cc">
<Lottie animationData={animationData} loop={true} style={{ width: 300, height: 300 }} />
</div>
);
};
export default LoadingLottie;

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,13 @@
import React from 'react';
import Lottie from 'lottie-react'; // Default import
import animationData from './NotFound.json'; // Import your Lottie JSON animation
const NotFoundLottie = () => {
return (
<div className="cc">
<Lottie animationData={animationData} loop={true} style={{ width: 300, height: 300 }} />
</div>
);
};
export default NotFoundLottie;

View File

@ -1,24 +0,0 @@
import React from "react";
import { Spin } from "antd";
interface Props {
loading: boolean;
children: React.ReactNode;
className?: string;
}
const KarimSpinner: React.FC<Props> = ({ loading, className, children }) => {
return (
<div className={className ?? ""}>
{loading ? (
<div className="text-center">
<Spin />
</div>
) : (
children
)}
</div>
);
};
export default KarimSpinner;

View File

@ -1,37 +0,0 @@
// .SearchBar {
// // margin-top: 20px;
// .group {
// display: flex;
// align-items: center;
// position: relative;
// max-width: 350px;
// width: 350px;
// }
// .input {
// width: 100%;
// height: 40px;
// padding: 0 1rem;
// padding-left: 2.5rem;
// border-radius: 8px;
// outline: none;
// font-weight: 500;
// background: var(--bg);
// color: var(--text);
// border: none;
// box-shadow: 2px 2px 7px 0 var(--bg);
// }
// .input::placeholder {
// color: var(--subtext);
// opacity: 0.4;
// }
// .icon {
// position: absolute;
// left: 1rem;
// fill: var(--subtext);
// width: 1rem;
// height: 1rem;
// }
// }

View File

@ -1,45 +0,0 @@
import React, { useState } from "react";
import "./SearchBar.scss";
import { useNavigate, useSearchParams } from "react-router-dom";
const SearchBar = () => {
const [searchQuery, setSearchQuery] = useState("");
const [searchParams] = useSearchParams();
const navigate = useNavigate();
const handleChange = (event: any) => {
const { value } = event.target;
setSearchQuery(value);
updateUrlParams(value);
};
const updateUrlParams = (value: any) => {
navigate(`?search=${value}`, { replace: true });
};
return (
<div className="SearchBar">
<div className="group">
<svg
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
className="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium css-1b5stb0 icon"
focusable="false"
aria-hidden="true"
viewBox="0 0 24 24"
data-testid="SearchIcon"
>
<path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"></path>
</svg>
<input
placeholder="Search Product...."
type="search"
className="input"
value={searchQuery}
onChange={handleChange}
/>
</div>
</div>
);
};
export default SearchBar;

View File

@ -6,17 +6,16 @@ import {
File,
DataRange,
SelectField,
Default,
CheckboxField,
MaltyFile,
SearchField,
TextField,
DropFile,
Default,
} from "./View";
import { ValidationFieldProps, ValidationFieldType } from "./utils/types";
import LocalSearchField from "./View/LocalSearch";
import NumberFormate from "./View/NumberFormate";
import NumberField from "./View/NumberField";
const components: { [key: string]: React.FC<any> } = {
Select: SelectField,
@ -31,12 +30,11 @@ const components: { [key: string]: React.FC<any> } = {
MaltyFile: MaltyFile,
Checkbox: CheckboxField,
NumberFormate: NumberFormate,
Number: NumberField,
};
const ValidationField: React.FC<ValidationFieldProps> = React.memo(
({ type, ...otherProps }: any) => {
const Component = components[type as ValidationFieldType];
({ type = "text", ...otherProps }) => {
const Component = components[type ?? ("text" as ValidationFieldType)];
if (!Component) {
return <Default {...otherProps} />;

View File

@ -1,7 +1,7 @@
import React from "react";
import useFormField from "../../../Hooks/useFormField";
import { Checkbox, Form } from "antd";
import { getNestedValue } from "../utils/getNestedValue";
import { Checkbox } from "antd";
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
const CheckboxField = ({
name,
label,
@ -17,12 +17,8 @@ const CheckboxField = ({
};
return (
<div className={Group ? "d-inline mt-3 Checkboxs" : ``}>
<Form.Item
hasFeedback
validateStatus={isError ? "error" : ""}
help={isError ? errorMsg : ""}
>
<div className={Group ? "d-inline mt-3 Checkbox" : ``}>
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
<Checkbox
onChange={onChange || CheckboxhandleChange}
disabled={isDisabled}
@ -31,7 +27,7 @@ const CheckboxField = ({
>
{t(`input.${label ? label : name}`)}
</Checkbox>
</Form.Item>
</ValidationFieldContainer>
</div>
);
};

View File

@ -3,6 +3,8 @@ import { Form, DatePicker } from "antd";
import React from "react";
import useFormField from "../../../Hooks/useFormField";
import { MdOutlineEdit } from "react-icons/md";
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
const { RangePicker } = DatePicker;
@ -24,28 +26,16 @@ const DataRange = ({
};
return (
<div className="ValidationField w-100 ">
{no_label ? (
<label htmlFor={name} className="text">
<span>empty</span>
</label>
) : label_icon ? (
<div className="LabelWithIcon">
<label htmlFor={name} className="text">
{t(`input.${label ? label : name}`)}
</label>
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
</div>
) : (
<label htmlFor={name} className="text">
{t(`input.${label ? label : name}`)}
</label>
)}
<ValidationFieldLabel
name={name}
label={label}
label_icon={label_icon}
no_label={no_label}
placeholder={placeholder}
t={t}
/>
<Form.Item
hasFeedback
validateStatus={isError ? "error" : ""}
help={isError ? errorMsg : ""}
>
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
<RangePicker
placeholder={placeholder}
size="large"
@ -55,8 +45,9 @@ const DataRange = ({
onChange={onChange || onCalendarChange}
disabled={isDisabled}
defaultValue={formik.values[name]}
id={name}
/>
</Form.Item>
</ValidationFieldContainer>
</div>
);
};

View File

@ -3,6 +3,9 @@ import React from "react";
import useFormField from "../../../Hooks/useFormField";
import { MdOutlineEdit } from "react-icons/md";
import dayjs from "dayjs";
import { DateEnum } from "../../../enums/Date";
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
const Date = ({
name,
@ -21,34 +24,21 @@ const Date = ({
const FormikValue = formik.values[name];
const onCalendarChange = (value: any) => {
formik.setFieldValue(name, value);
// console.log(value,"value ");
};
const Formater = "YYYY/MM/DD";
const Formatter = [DateEnum?.FORMATE];
return (
<div className="ValidationField w-100 ">
{no_label ? (
<label htmlFor={name} className="text">
<span>empty</span>
</label>
) : label_icon ? (
<div className="LabelWithIcon">
<label htmlFor={name} className="text">
{t(`input.${label ? label : name}`)}
</label>
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
</div>
) : (
<label htmlFor={name} className="text">
{t(`input.${label ? label : name}`)}
</label>
)}
<ValidationFieldLabel
name={name}
label={label}
label_icon={label_icon}
no_label={no_label}
placeholder={placeholder}
t={t}
/>
<Form.Item
hasFeedback
validateStatus={isError ? "error" : ""}
help={isError ? errorMsg : ""}
>
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
<DatePicker
picker={picker}
placeholder={t(`input.${placeholder}`)}
@ -58,10 +48,11 @@ const Date = ({
size="large"
onChange={onChange || onCalendarChange}
disabled={isDisabled}
format={Formater}
format={Formatter}
id={name}
/>
{/* <DatePicker onChange={onChange} /> */}
</Form.Item>
</ValidationFieldContainer>
</div>
);
};

View File

@ -1,9 +1,10 @@
import { Form, Input } from "antd";
import { Input } from "antd";
import React from "react";
import useFormField from "../../../Hooks/useFormField";
import { MdOutlineEdit } from "react-icons/md";
import { Field } from "formik";
import { ValidationFieldPropsInput } from "../utils/types";
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
import { FieldProps } from "../utils/types";
const Default = ({
name,
@ -14,52 +15,34 @@ const Default = ({
type,
no_label,
label_icon,
label2,
...props
}: ValidationFieldPropsInput) => {
}: any) => {
const { errorMsg, isError, t } = useFormField(name, props);
return (
<div className="ValidationField w-100">
{label2 ? (
<label htmlFor={name} className="text">
{label2}
</label>
) : no_label ? (
<label htmlFor={name} className="text">
<span>empty</span>
</label>
) : label_icon ? (
<div className="LabelWithIcon">
<label htmlFor={name} className="text">
{t(`input.${label ? label : name}`)}
</label>
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
</div>
) : (
<label htmlFor={name} className="text">
{t(`input.${label ? label : placeholder ? placeholder : name}`)}
</label>
)}
<ValidationFieldLabel
name={name}
label={label}
label_icon={label_icon}
no_label={no_label}
placeholder={placeholder}
t={t}
/>
<Form.Item
hasFeedback
validateStatus={isError ? "error" : ""}
help={isError ? errorMsg : ""}
>
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
<Field
as={Input}
type={type ?? "text"}
placeholder={t(
`input.${placeholder ? placeholder : label ? label : name}`,
)}
placeholder={t(`input.${placeholder || label || name}`)}
name={name}
id={name}
disabled={isDisabled}
size="large"
{...(type === "number" && { min: 0 })}
{...props}
/>
</Form.Item>
</ValidationFieldContainer>
</div>
);
};

View File

@ -3,7 +3,6 @@ import { LoadingOutlined, PlusOutlined } from "@ant-design/icons";
import { message, Upload } from "antd";
import type { GetProp, UploadProps } from "antd";
import useFormField from "../../../Hooks/useFormField";
import { ImageBaseURL } from "../../../api/config";
type FileType = Parameters<GetProp<UploadProps, "beforeUpload">>[0];
@ -12,7 +11,7 @@ const DropFile = ({
label,
onChange,
isDisabled,
placholder,
placeholder,
className,
props,
no_label,
@ -23,7 +22,7 @@ const DropFile = ({
const FormikValue =
typeof FormikName === "string"
? ImageBaseURL + FormikName
? FormikName
: FormikName instanceof File
? URL.createObjectURL(FormikName)
: "";
@ -71,6 +70,7 @@ const DropFile = ({
showUploadList={false}
customRequest={customRequest}
onChange={onChange || handleChange}
id={name}
>
{imageUrl ? (
<img

View File

@ -1,7 +1,7 @@
import { Button, Upload, UploadFile } from "antd";
import useFormField from "../../../Hooks/useFormField";
import { UploadOutlined } from "@ant-design/icons";
import { useMemo } from "react";
import React, { useMemo } from "react";
const File = ({
name,
@ -11,37 +11,34 @@ const File = ({
placeholder,
className,
props,
icon,
}: any) => {
const { formik, t, isError, errorMsg } = useFormField(name, props);
let imageUrl = formik?.values?.[name] ?? null;
console.log(imageUrl);
console.log(typeof imageUrl === "string");
const fileList: UploadFile[] = useMemo(() => {
if (!imageUrl) return [];
if (!imageUrl) {
return [];
}
return [
typeof imageUrl === "string"
? {
uid: "-1",
name: "uploaded-image",
name: "",
status: "done",
url: imageUrl,
thumbUrl: imageUrl,
}
: {
uid: imageUrl.uid || "-1",
name: imageUrl.name || "uploaded-image",
name: imageUrl.name || "",
status: "done",
originFileObj: imageUrl,
},
];
}, [imageUrl]);
// console.log(1);
const FilehandleChange = (value: any) => {
// console.log(value,"filevalue");
if (value.fileList.length === 0) {
formik.setFieldValue(name, null);
} else {
@ -65,12 +62,15 @@ const File = ({
onChange={onChange || FilehandleChange}
customRequest={customRequest}
className={` w-100`}
id={name}
>
<Button
className={isError ? "isError w-100 " : " w-100"}
icon={icon ? icon : <UploadOutlined />}
icon={<UploadOutlined />}
>
{placeholder ?? t("input.Click_to_upload_the_image")}
{placeholder
? t(`input.${placeholder}`)
: t("input.Click_to_upload_the_image")}
</Button>
<div className="Error_color"> {isError ? "required" : ""}</div>
{errorMsg}
@ -79,4 +79,4 @@ const File = ({
);
};
export default File;
export default React.memo(File);

View File

@ -1,8 +1,9 @@
import { Form, Select } from "antd";
import { Select } from "antd";
import React, { useState } from "react";
import useFormField from "../../../Hooks/useFormField";
import { MdOutlineEdit } from "react-icons/md";
import { translateOptions } from "../utils/translatedOptions";
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
const LocalSelectField = ({
name,
@ -13,9 +14,9 @@ const LocalSelectField = ({
isMulti,
onChange,
className,
props,
no_label,
label_icon,
...props
}: any) => {
const { errorMsg, isError, t, formik } = useFormField(name, props);
@ -29,13 +30,6 @@ const LocalSelectField = ({
option?.label?.toString().toLowerCase().includes(input.toLowerCase()) ||
option?.value?.toString().toLowerCase().includes(input.toLowerCase());
const SelectableChange = (value: {
value: string;
label: React.ReactNode;
}) => {
formik.setFieldValue(name, value);
};
const handleSelectChange = (value: any) => {
formik.setFieldValue(name, value);
if (onChange) onChange(value);
@ -47,35 +41,21 @@ const LocalSelectField = ({
return (
<div className="ValidationField w-100">
{no_label ? (
<label htmlFor={name} className="text">
<span>empty</span>
</label>
) : label_icon ? (
<div className="LabelWithIcon">
<label htmlFor={name} className="text">
{t(`input.${label ? label : name}`)}
</label>
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
</div>
) : (
<label htmlFor={name} className="text">
{t(`input.${label ? label : name}`)}
</label>
)}
<Form.Item
hasFeedback
validateStatus={isError ? "error" : ""}
help={isError ? errorMsg : ""}
>
<ValidationFieldLabel
name={name}
label={label}
label_icon={label_icon}
no_label={no_label}
placeholder={placeholder}
t={t}
/>
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
<Select
placeholder={t(
`input.${placeholder ? placeholder : label ? label : name}`,
)}
placeholder={t(`input.${placeholder || label || name}`)}
disabled={isDisabled}
options={translateOptions(option, t)}
size="large"
className={`${className} ${isError ? "Select_error" : ""} w-100`}
className={`${className} ${isError ? "SelectError" : ""} w-100`}
value={formik.values[name]}
allowClear
{...(isMulti && { mode: "multiple" })}
@ -84,8 +64,11 @@ const LocalSelectField = ({
filterOption={handleSearch} // Custom filter function
searchValue={searchValue} // Control the search input value
onSearch={handleSearchChange} // Update search input value on change
id={name}
fieldNames={{ label: "name", value: "id" }}
{...props}
/>
</Form.Item>
</ValidationFieldContainer>
</div>
);
};

View File

@ -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,28 +15,28 @@ const MaltyFile = ({
const { formik, t, isError } = useFormField(name, props);
let imageUrl = formik?.values?.[name] ?? null;
// 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,
name: file?.name,
status: "done",
originFileObj: file,
}
: {
uid: index,
id: file?.id,
name: file?.name,
status: "done",
url: file?.url || "",
thumbUrl: file?.url || "",
};
})
: [];
// Memoizing the fileList to prevent unnecessary recalculations
const fileList = useMemo(() => {
return imageUrl
? imageUrl.map((file: any, index: number) => {
return file instanceof File
? {
uid: index,
name: file?.name,
status: "done",
originFileObj: file,
}
: {
uid: index,
id: file?.id,
name: file?.name,
status: "done",
url: file?.url || "",
thumbUrl: file?.url || "",
};
})
: [];
}, [imageUrl]); // Dependency array ensures it recalculates only when imageUrl changes
const FilehandleChange = ({ fileList }: any) => {
if (fileList.length === 0) {
@ -48,6 +48,7 @@ const MaltyFile = ({
);
}
};
// Custom request function
const customRequest = async ({ onSuccess }: any) => {
// Perform any necessary actions before onSuccess is called
@ -63,19 +64,20 @@ const MaltyFile = ({
<Upload
disabled={isDisabled}
listType="picture"
fileList={fileList} // Using fileList instead of defaultFileList
fileList={fileList} // Using memoized fileList
onChange={onChange || FilehandleChange}
customRequest={customRequest}
className={`${className} w-100`}
multiple // Allow multiple files to be selected
id={name}
>
<Button
className={isError ? "isError w-100" : " w-100"}
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>
);

View File

@ -1,70 +0,0 @@
import { Form, Input, InputNumber } from "antd";
import React from "react";
import useFormField from "../../../Hooks/useFormField";
import { MdOutlineEdit } from "react-icons/md";
import { Field } from "formik";
import { ValidationFieldPropsInput } from "../utils/types";
const NumberField = ({
name,
label,
placeholder,
isDisabled,
onChange,
type,
no_label,
label_icon,
...props
}: ValidationFieldPropsInput) => {
const { errorMsg, isError, t, formik } = useFormField(name, props);
const handleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
) => {
console.log("Change:", e);
formik.setFieldValue(name, e);
};
return (
<div className="ValidationField w-100">
{no_label ? (
<label htmlFor={name} className="text">
<span>empty</span>
</label>
) : label_icon ? (
<div className="LabelWithIcon">
<label htmlFor={name} className="text">
{t(`input.${label ? label : name}`)}
</label>
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
</div>
) : (
<label htmlFor={name} className="text">
{t(`input.${label ? label : placeholder ? placeholder : name}`)}
</label>
)}
<Form.Item
hasFeedback
validateStatus={isError ? "error" : ""}
help={isError ? errorMsg : ""}
>
<Field
as={InputNumber}
type={type ?? "text"}
placeholder={t(
`input.${placeholder ? placeholder : label ? label : name}`,
)}
name={name}
disabled={isDisabled}
size="large"
onChange={handleChange}
{...(type === "number" && { min: 0 })}
{...props}
/>
</Form.Item>
</div>
);
};
export default React.memo(NumberField);

View File

@ -1,18 +1,19 @@
import { Form, Input, InputNumber } from "antd";
import { InputNumber } from "antd";
import React from "react";
import useFormField from "../../../Hooks/useFormField";
import { MdOutlineEdit } from "react-icons/md";
import { Field } from "formik";
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
const NumberFormate = ({
name,
label,
placeholder,
isDisabled,
props,
type,
no_label,
label_icon,
...props
}: any) => {
const { errorMsg, isError, t, formik } = useFormField(name, props);
const SelectableChange = (value: {
@ -23,28 +24,15 @@ const NumberFormate = ({
};
return (
<div className="ValidationField w-100">
{no_label ? (
<label htmlFor={name} className="text">
<span>empty</span>
</label>
) : label_icon ? (
<div className="LabelWithIcon">
<label htmlFor={name} className="text">
{t(`input.${label ? label : name}`)}
</label>
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
</div>
) : (
<label htmlFor={name} className="text">
{t(`input.${label ? label : placeholder ? placeholder : name}`)}
</label>
)}
<Form.Item
hasFeedback
validateStatus={isError ? "error" : ""}
help={isError ? errorMsg : ""}
>
<ValidationFieldLabel
name={name}
label={label}
label_icon={label_icon}
no_label={no_label}
placeholder={placeholder}
t={t}
/>
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
<Field
as={InputNumber}
formatter={(value: any) =>
@ -57,16 +45,16 @@ const NumberFormate = ({
type={type ?? "text"}
value={formik.values[name]}
onChange={SelectableChange}
placeholder={t(
`input.${placeholder ? placeholder : label ? label : name}`,
)}
placeholder={t(`input.${placeholder || label || name}`)}
name={name}
disabled={isDisabled}
size="large"
id={name}
{...props}
// onChange={onChange ? onChange : handleChange}
/>
</Form.Item>
</ValidationFieldContainer>
</div>
);
};

View File

@ -1,87 +1,143 @@
import { Form, Select, Spin } from "antd";
import { Select, Spin } from "antd";
import React, { useEffect, useState } from "react";
import useFormField from "../../../Hooks/useFormField";
import { useNavigate } from "react-router-dom";
import { MdOutlineEdit } from "react-icons/md";
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
import { SearchFieldProps } from "../utils/types";
import { useValidationValidationParamState } from "../state/ValidationValidationParamState";
import { useDebounce } from "../../../utils/useDebounce";
const SearchField = ({
name,
label,
placeholder,
isDisabled,
searchBy,
option,
searchBy = "search",
option = [],
isMulti,
onChange,
className,
props,
no_label,
label_icon,
isLoading,
}: any) => {
canChangePage,
PageName,
page,
...props
}: SearchFieldProps) => {
const { errorMsg, isError, t, formik } = useFormField(name, props);
const [searchQuery, setSearchQuery] = useState<string>("");
const navigate = useNavigate();
useEffect(() => {
const searchParams = new URLSearchParams(window?.location?.search);
setSearchQuery(searchParams?.get("search") || "");
}, []);
const { pushValidationParamState, setValidationParamState } =
useValidationValidationParamState();
const SelectableChange = (value: {
value: string;
label: React.ReactNode;
}) => {
const [AllPagesOption, setAllPagesOption] = useState<any>([]);
useEffect(() => {
if (option?.length > 0) {
const NewOption = [...option, ...AllPagesOption];
const FilteredOption = NewOption.filter(
(value, index, self) =>
index === self.findIndex((t) => t.id === value.id),
);
const sortedNewOption = FilteredOption.sort((a, b) => a.id - b.id);
setAllPagesOption(sortedNewOption);
}
}, [option]);
useEffect(() => {
if (page === 1) {
setAllPagesOption(option);
}
}, [page]);
const SelectableChange = (value: any) => {
formik?.setFieldValue(name, value);
const isCleared = value?.length === 0 || !value;
if (isCleared) {
if (PageName) {
setValidationParamState({
[PageName]: 1,
});
}
}
console.log(value, "value");
};
const SearchHandleChange = (value: any) => {
navigate(`${window?.location?.pathname}?${searchBy}=${value}`, {
replace: true,
const handleChange = useDebounce((value: string) => {
if (PageName) {
pushValidationParamState({
[PageName]: 1,
});
}
pushValidationParamState({
[searchBy]: value,
});
});
const handleBlur = () => {
if (PageName && page === 1) {
setValidationParamState({
[PageName]: null,
});
}
if (PageName && page !== 1) {
setValidationParamState({
[PageName]: 1,
});
// setAllPagesOption([]);
}
};
const handleScroll = (event: any) => {
const target = event.target;
const isAtBottom =
target.scrollHeight === target.scrollTop + target.clientHeight;
if (isAtBottom && canChangePage && PageName && page) {
console.log("Scrolled to the last option!");
let newPage = page + 1;
pushValidationParamState({
[PageName]: newPage,
});
}
};
console.log(AllPagesOption);
console.log(option,"option");
return (
<div className="ValidationField w-100 ">
{no_label ? (
<label htmlFor={name} className="text">
<span>empty</span>
</label>
) : label_icon ? (
<div className="LabelWithIcon">
<label htmlFor={name} className="text">
{t(`input.${label ? label : name}`)}
</label>
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
</div>
) : (
<label htmlFor={name} className="text">
{t(`input.${label ? label : name}`)}
</label>
)}
<Form.Item
hasFeedback
validateStatus={isError ? "error" : ""}
help={isError ? errorMsg : ""}
>
<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}>
<Select
placeholder={t(
`input.${placeholder ? placeholder : label ? label : name}`,
)}
placeholder={t(`input.${placeholder || label || name}`)}
disabled={isDisabled}
options={option}
options={AllPagesOption}
size="large"
className={`${className} w-100`}
value={formik.values[name]}
loading={isLoading}
// loading={isLoading}
allowClear
{...(isMulti && { mode: "multiple" })}
onChange={onChange || SelectableChange}
showSearch
optionFilterProp="label"
notFoundContent={isLoading ? <Spin /> : "لا يوجد"}
onSearch={SearchHandleChange}
optionFilterProp="name"
notFoundContent={isLoading ? <Spin /> : t("validation.undefined")}
onSearch={handleChange}
onBlur={handleBlur}
id={name}
onPopupScroll={handleScroll}
fieldNames={{ label: "name", value: "id" }}
{...props}
/>
</Form.Item>
</ValidationFieldContainer>
</div>
);
};

View File

@ -1,8 +1,10 @@
import { Form, Select, Spin } from "antd";
import { Select } from "antd";
import React from "react";
import useFormField from "../../../Hooks/useFormField";
import { MdOutlineEdit } from "react-icons/md";
import { translateOptions } from "../utils/translatedOptions";
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
import { SelectFieldProps } from "../utils/types";
const SelectField = ({
name,
@ -13,10 +15,11 @@ const SelectField = ({
isMulti,
onChange,
className,
props,
no_label,
label_icon,
}: any) => {
isLoading,
...props
}: SelectFieldProps) => {
const { errorMsg, isError, t, formik } = useFormField(name, props);
const SelectableChange = (value: {
value: string;
@ -24,47 +27,36 @@ const SelectField = ({
}) => {
formik.setFieldValue(name, value);
};
// console.log(name,"Select");
const options = translateOptions(option, t);
return (
<div className="ValidationField w-100">
{no_label ? (
<label htmlFor={name} className="text">
<span>empty</span>
</label>
) : label_icon ? (
<div className="LabelWithIcon">
<label htmlFor={name} className="text">
{t(`input.${label ? label : name}`)}
</label>
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
</div>
) : (
<label htmlFor={name} className="text">
{t(`input.${label ? label : name}`)}
</label>
)}
<ValidationFieldLabel
name={name}
label={label}
label_icon={label_icon}
no_label={no_label}
placeholder={placeholder}
t={t}
/>
<Form.Item
hasFeedback
validateStatus={isError ? "error" : ""}
help={isError ? errorMsg : ""}
>
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
<Select
placeholder={t(
`input.${placeholder ? placeholder : label ? label : name}`,
)}
placeholder={t(`input.${placeholder || label || name}`)}
disabled={isDisabled}
options={translateOptions(option, t)}
loading={option?.length < 1}
options={options}
{...(isLoading && { loading: isLoading })}
size="large"
className={`${className} ${isError ? "Select_error" : ""} w-100`}
className={`${className} ${isError ? "SelectError" : ""} w-100`}
value={formik.values[name]}
allowClear
{...(isMulti && { mode: "multiple" })}
onChange={onChange || SelectableChange}
showSearch={false}
id={name}
fieldNames={{label:"name",value:"id"}}
{...props}
/>
</Form.Item>
</ValidationFieldContainer>
</div>
);
};

View File

@ -1,7 +1,8 @@
import { Form, Input } from "antd";
import { Input } from "antd";
import React from "react";
import useFormField from "../../../Hooks/useFormField";
import { Field } from "formik";
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
const { TextArea } = Input;
const TextAreaField = ({
@ -11,14 +12,12 @@ const TextAreaField = ({
isDisabled,
onChange,
props,
type,
}: any) => {
const { formik, isError, errorMsg, t } = useFormField(name, props);
const handleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
) => {
// console.log('Change:', e.target.value);
formik.setFieldValue(name, e.target.value);
};
@ -27,11 +26,7 @@ const TextAreaField = ({
<label htmlFor={name} className="text">
{t(`input.${label ? label : name}`)}
</label>
<Form.Item
hasFeedback
validateStatus={isError ? "error" : ""}
help={isError ? errorMsg : ""}
>
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
<Field
as={TextArea}
placeholder={t(`input.${placeholder ? placeholder : name}`)}
@ -39,10 +34,10 @@ const TextAreaField = ({
disabled={isDisabled}
size="large"
onChange={onChange || handleChange}
id={name}
// onChange={onChange ? onChange : handleChange}
/>
</Form.Item>
</ValidationFieldContainer>
</div>
);
};

View File

@ -3,6 +3,8 @@ import React from "react";
import useFormField from "../../../Hooks/useFormField";
import { MdOutlineEdit } from "react-icons/md";
import { Field } from "formik";
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
const { TextArea } = Input;
const TextField = ({
@ -21,33 +23,20 @@ const TextField = ({
const TextFilehandleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
) => {
// console.log('Change:', e.target.value);
formik.setFieldValue(name, e.target.value);
};
return (
<div className={`ValidationField w-100 ${className ?? ""} `}>
{no_label ? (
<label htmlFor={name} className="text">
<span>empty</span>
</label>
) : label_icon ? (
<div className="LabelWithIcon">
<label htmlFor={name} className="text">
{label2 ? label2 : t(`input.${label ? label : name}`)}
</label>
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
</div>
) : (
<label htmlFor={name} className="text">
{label2 ? label2 : t(`input.${label ? label : name}`)}
</label>
)}
<ValidationFieldLabel
name={name}
label={label}
label_icon={label_icon}
no_label={no_label}
placeholder={placeholder}
t={t}
/>
<Form.Item
hasFeedback
validateStatus={isError ? "error" : ""}
help={isError ? errorMsg : ""}
>
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
<Field
as={TextArea}
placeholder={t(`input.${placeholder ? placeholder : name}`)}
@ -58,8 +47,9 @@ const TextField = ({
maxLength={1000}
onChange={onChange || TextFilehandleChange}
style={{ height: 120 }}
id={name}
/>
</Form.Item>
</ValidationFieldContainer>
</div>
);
};

View File

@ -3,6 +3,7 @@ import React from "react";
import useFormField from "../../../Hooks/useFormField";
import { MdOutlineEdit } from "react-icons/md";
import dayjs from "dayjs";
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
const Time = ({
name,
@ -20,27 +21,19 @@ const Time = ({
formik.setFieldValue(name, value);
};
const Formater = "H:mm";
const Formatter = "H:mm";
const FormikValue = formik.values[name];
return (
<div className="ValidationField w-100 ">
{no_label ? (
<label htmlFor={name} className="text">
<span>empty</span>
</label>
) : label_icon ? (
<div className="LabelWithIcon">
<label htmlFor={name} className="text">
{t(`input.${label ? label : name}`)}
</label>
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
</div>
) : (
<label htmlFor={name} className="text">
{t(`input.${label ? label : name}`)}
</label>
)}
<ValidationFieldLabel
name={name}
label={label}
label_icon={label_icon}
no_label={no_label}
placeholder={placeholder}
t={t}
/>
<Form.Item
hasFeedback
@ -51,14 +44,13 @@ const Time = ({
allowClear
className={`${className} w-100`}
size="large"
value={FormikValue ? dayjs(FormikValue, Formater) : null}
value={FormikValue ? dayjs(FormikValue, Formatter) : null}
onChange={onChange || onCalendarChange}
disabled={isDisabled}
placeholder={t(
`input.${placeholder ? placeholder : label ? label : name}`,
)}
format={Formater}
placeholder={t(`input.${placeholder || label || name}`)}
format={Formatter}
needConfirm={false}
id={name}
/>
</Form.Item>
</div>

View File

@ -0,0 +1,24 @@
import React, { FC } from "react";
import { Form } from "antd";
interface ValidationFieldContainerProps {
children: React.ReactNode;
isError: boolean;
errorMsg: string;
}
export const ValidationFieldContainer: FC<ValidationFieldContainerProps> = ({
children,
isError,
errorMsg,
}) => (
<div className="ValidationFieldContainer">
<Form.Item
hasFeedback
validateStatus={isError ? "error" : ""}
help={isError ? errorMsg : ""}
>
{children}
</Form.Item>
</div>
);

View File

@ -0,0 +1,39 @@
import React from "react";
import { MdOutlineEdit } from "react-icons/md";
interface ValidationFieldLabelProps {
name: string;
label?: string;
no_label?: boolean;
label_icon?: boolean;
placeholder?: string;
t: (key: string) => string;
}
export const ValidationFieldLabel: React.FC<ValidationFieldLabelProps> = ({
name,
label,
placeholder,
no_label,
label_icon,
t,
}) => (
<>
{no_label ? (
<label htmlFor={name} className="text">
<span>empty</span>
</label>
) : label_icon ? (
<div className="LabelWithIcon">
<label htmlFor={name} className="text">
{t(`input.${label ? label : name}`)}
</label>
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
</div>
) : (
<label htmlFor={name} className="text">
{t(`input.${label ? label : name}`)}
</label>
)}
</>
);

View File

@ -2,7 +2,6 @@ import { useState } from "react";
import { ErrorMessage, useField, Field, useFormikContext } from "formik";
import { useTranslation } from "react-i18next";
import { FaExclamationCircle } from "react-icons/fa";
import { convert_data_to_select } from "../../Layout/app/Const";
export {
useState,
@ -12,5 +11,4 @@ export {
useFormikContext,
useTranslation,
FaExclamationCircle,
convert_data_to_select,
};

View File

@ -0,0 +1,36 @@
import { create } from "zustand";
interface ValidationParamState {
[key: string]: any;
}
interface ModalState {
ValidationParamState: ValidationParamState;
setValidationParamState: (validationParamState: ValidationParamState) => void;
pushValidationParamState: (
validationParamState: ValidationParamState,
) => void;
clearValidationParamState: () => void;
}
export const useValidationValidationParamState = create<ModalState>((set) => ({
ValidationParamState: {},
setValidationParamState: (validationParamState) =>
set(() => ({
ValidationParamState: validationParamState,
})),
pushValidationParamState: (validationParamState) =>
set((state) => ({
ValidationParamState: {
...state.ValidationParamState,
...validationParamState,
},
})),
clearValidationParamState: () =>
set({
ValidationParamState: {},
}),
}));

View File

@ -4,10 +4,13 @@
justify-content: space-between;
}
.ValidationField {
margin-bottom: 1.3vw;
margin-bottom: 10px;
position: relative;
min-height: 80px;
padding-inline: 20px;
> * {
width: 100%;
width: 100% !important;
min-width: 150px;
}
.text,
.ant-form-item {
@ -16,213 +19,55 @@
color: transparent;
}
}
.ant-select-outlined:not(.ant-select-customize-input) .ant-select-selector {
border: 1px solid var(--border-color);
}
.Select_error {
.ant-select-selector {
border: 1px solid red !important;
}
}
// .ValidationField{
// .ant-select-selector{
// border: 1px solid var(--border-color) ;
// }
// }
> span {
margin-bottom: 0px !important;
&:focus-within {
border-color: var(--primary);
box-shadow: 0 0 0 1px var(--primary);
cursor: pointer;
}
&:has(.is-invalid) {
border-color: red !important ;
}
input {
color: var(--text);
background: var(--bg);
}
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active {
-webkit-box-shadow: 0 0 0 30px white inset !important;
}
}
}
//// upload
.ant-upload-select {
width: 100%;
}
.Checkboxs {
padding: 4%;
}
.ant-checkbox-wrapper {
min-width: 100px;
}
.SearchField {
button {
background: var(--primary);
}
}
.text {
color: var(--text);
margin-bottom: 15px;
font-weight: bold;
}
input:disabled {
color: var(--text) !important;
}
.isError {
outline: red 1px solid;
color: red;
}
.Error_color {
color: red;
}
input:-webkit-autofill {
-webkit-box-shadow: 0 0 0 1000px white inset !important; /* Change the color to your desired background color */
}
input:-webkit-autofill:focus {
-webkit-box-shadow: 0 0 0 1000px white inset !important; /* Change the color to your desired background color */
}
/* Remove autofill background color on hover */
input:-webkit-autofill:hover {
-webkit-box-shadow: 0 0 0 1000px white inset !important; /* Change the color to your desired background color */
}
.upload_image_button {
.ant-btn {
min-height: 3vw !important;
border: 0.1vw solid var(--border-color);
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 {
.ant-btn-default{
padding: 7px 11px;
height: var(--fieldHeight);
display: flex;
gap: 3%;
> label {
display: none;
}
justify-content: center;
align-items: center;
}
.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 {
//// number input
///
.ant-input-number-affix-wrapper-lg{
width: 100%;
height: 3vw;
}
.ant-input-number-lg input.ant-input-number-input {
width: 100%;
height: 3vw;
.ValidationFieldCheckbox{
// background-color: red;
}
.bigRow {
width: 100%;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
> *.w-100 {
width: 48% !important;
/// 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;
}
}
.ant-input-number-affix-wrapper-lg {
width: 100%;
height: 3vw;
border-radius: 0.6vw;
border: 1px solid var(--border-color);
}
.ant-input-textarea-affix-wrapper.ant-input-affix-wrapper {
.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;
height: 120px;
}

View File

@ -1,7 +0,0 @@
export function getNestedValue(obj: any, path: any) {
return path
.replace(/\?.\[|\]\[|\]\.?/g, ".") // Replace question mark and square brackets
.split(".") // Split by dots
.filter(Boolean) // Remove empty strings
.reduce((acc: any, key: any) => acc && acc[key], obj); // Access nested properties
}

View File

@ -1,6 +1,7 @@
export const translateOptions = (options: any, t: any) => {
return options.map((opt: any) => ({
return options?.map((opt: any) => ({
...opt,
label: t(`${opt.label}`),
label: t(`${opt?.label}`),
name: t(`${opt?.name}`),
}));
};

View File

@ -1,195 +1,80 @@
import { InputProps } from "antd";
import { InputProps, SelectProps } from "antd";
export type ValidationFieldType =
| "text"
| "Select"
| "LocalSearch"
| "Search"
| "DataRange"
| "Date"
| "Time"
| "File"
| "MaltyFile"
| "DropFile"
| "Checkbox"
| "number"
| "password"
| "email"
| "TextArea";
export interface ValidationFieldPropsText {
// Common properties for all field types
interface BaseFieldProps {
name: string;
no_label?: boolean;
label_icon?: boolean;
type: "text";
placeholder?: string;
label?: string;
placeholder?: string;
className?: string;
isDisabled?: boolean;
onChange?: (value: any) => void;
dir?: "ltr" | "rtl";
no_label?: boolean;
label_icon?: boolean;
}
export interface ValidationFieldPropsSelect {
name: string;
no_label?: boolean;
label_icon?: boolean;
type: "Select";
placeholder?: string;
label?: string;
className?: string;
isDisabled?: boolean;
onChange?: any;
dir?: "ltr" | "rtl";
option: any[];
isMulti?: 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 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";
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";
}
export interface ValidationFieldPropsDate {
name: string;
no_label?: boolean;
label_icon?: boolean;
type: "Date";
placeholder?: string;
label?: string;
className?: string;
isDisabled?: boolean;
onChange?: (value: any) => void;
dir?: "ltr" | "rtl";
picker?: "data" | "week" | "month" | "quarter" | "year";
}
};
export interface ValidationFieldPropsTime {
name: string;
no_label?: boolean;
label_icon?: boolean;
type: "Time";
label?: string;
placeholder?: string;
className?: string;
isDisabled?: boolean;
onChange?: (value: any) => void;
dir?: "ltr" | "rtl";
}
export interface ValidationFieldPropsFile {
name: string;
no_label?: boolean;
label_icon?: boolean;
type FileFieldProps = BaseFieldProps & {
type: "File" | "MaltyFile" | "DropFile";
placeholder?: string;
label?: string;
className?: string;
isDisabled?: boolean;
onChange?: (value: any) => void;
dir?: "ltr" | "rtl";
icon?:any
}
export interface ValidationFieldPropsCheckbox {
name: string;
no_label?: boolean;
label_icon?: boolean;
};
type CheckboxFieldProps = BaseFieldProps & {
type: "Checkbox";
label?: string;
className?: string;
isDisabled?: boolean;
onChange?: (value: any) => void;
dir?: "ltr" | "rtl";
Group?: boolean;
}
export interface ValidationFieldPropstext {
name: string;
no_label?: boolean;
label_icon?: boolean;
type?:
| "text"
| "number"
| "password"
| "email"
| "TextArea"
| "NumberFormate";
label?: string;
label2?: string;
className?: string;
placeholder?: string;
isDisabled?: boolean;
onChange?: (value: any) => void;
dir?: "ltr" | "rtl";
Group?: boolean;
[key: string]: any; // Index signature to allow any additional props
}
};
///// new
export interface BaseField {
name: string;
label?: string;
placeholder?: string;
}
export type OmitBaseType = "placeholder" | "name" | "label" | "type";
export type OmitPicker = OmitBaseType | "format";
export interface ValidationFieldPropsInput
extends Omit<InputProps, OmitBaseType>,
BaseField {
type: "text" | "number" | "password" | "email" | "Number";
isDisabled?: boolean;
no_label?: string;
label_icon?: string;
label2?: string;
}
export type FieldProps = BaseFieldProps &
InputProps & {
type?:
| "text"
| "number"
| "password"
| "email"
| "TextArea"
| "NumberFormate";
label2?: string;
Group?: boolean;
[key: string]: any;
};
// Union type for all field types
export type ValidationFieldProps =
| ValidationFieldPropsInput
| ValidationFieldPropsSelect
| ValidationFieldPropsLocalSearch
| ValidationFieldPropsDataRange
| ValidationFieldPropsDate
| ValidationFieldPropsTime
| ValidationFieldPropsFile
| ValidationFieldPropsCheckbox
| ValidationFieldPropstext
| ValidationFieldPropsSearch;
| SelectFieldProps
| DateFieldProps
| FileFieldProps
| CheckboxFieldProps
| SearchFieldProps
| FieldProps;
// Validation field type
export type ValidationFieldType = ValidationFieldProps["type"];

View File

@ -26,9 +26,12 @@ const Header = () => {
return (
<header className="exercise_add_header mb-4">
<article>
<img src="/Icon/QuestionIcon.svg" alt="" />
<div>
{t("practical.add")} {t("models.exercise")}{" "}
{t("practical.add")} {t("models.exercise")}{" "}
</div>
</article>
<div>
<GoArrowSwitch onClick={handleChange} className="m-2" />
{isBseQuestion || values?.isBase === 1

View File

@ -9,6 +9,7 @@ import OrderBySelect from '../../Components/Filter/OrderBySelect';
import LayoutFilterModal from './LayoutFilterModal';
import { BiFilterAlt } from 'react-icons/bi';
import { MdKeyboardArrowDown } from "react-icons/md";
import SearchField from '../../Components/DataTable/SearchField';
const FilterLayout = ({filterTitle, sub_children}:{filterTitle:string,sub_children:any}) => {
const {t} = useTranslation();
@ -21,7 +22,7 @@ const FilterLayout = ({filterTitle, sub_children}:{filterTitle:string,sub_childr
return (
<div className='filter_header'>
<div className='filter_header_top'>
<h1>{t(filterTitle)}</h1>
<h4>{t(filterTitle)}</h4>
<div className='filter_and_order_by'>
<span>
<LayoutFilterModal
@ -48,8 +49,8 @@ const FilterLayout = ({filterTitle, sub_children}:{filterTitle:string,sub_childr
<p>{t("ادخالات")}</p>
</span>
<div className="header_search">
<SearchFieldWithSelect
options={translateArray}
<SearchField
searchBy=''
placeholder={t("practical.search_here")}
/>
</div>

View File

@ -1,6 +1,5 @@
import { Formik, Form, useFormikContext } from "formik";
import React, { ReactNode, useEffect } from "react";
import { useValidationState } from "../../Components/ValidationField/utils/ValidationState";
import { useModalState } from "../../zustand/Modal";
interface FormValues {

View File

@ -32,7 +32,7 @@ const LayoutModel = ({
modelTitle,
ModelEnum,
ModelClassName,
width = "45vw",
width = "800px",
isLoading = false,
}: LayoutModalProps) => {
const { isOpen, setIsOpen } = useModalState((state) => state);

View File

@ -1,13 +1,13 @@
import React from "react";
import React, { lazy, Suspense } from "react";
import {
useAddKeyToData,
Table,
useTranslation,
usePagination,
Loading,
EmptyData,
} from ".";
import { DataTableProps } from "../../../types/Table";
const NotFoundLottie = React.lazy(() => import("../../../Components/Lottie/NotFound/NotFoundLottie"));
const LoadingLottie = React.lazy(() => import("../../../Components/Lottie/Loading/LoadingLottie"));
const DataTable: React.FC<DataTableProps> = ({
response,
@ -22,29 +22,43 @@ const DataTable: React.FC<DataTableProps> = ({
const getRowClassName = (record: any, index: number): string => {
return index % 2 === 0 ? "even-row" : "odd-row";
};
const isRefetching = response?.isRefetching;
const isLoading = response?.isLoading;
return (
<Table
style={{minHeight:"300px"}}
columns={columns}
dataSource={dataSource}
rowClassName={(record, index) => getRowClassName(record, index)}
className="DataTable"
loading={{
spinning: response?.isLoading,
indicator: <Loading />,
spinning: isLoading || isRefetching,
indicator:<Suspense fallback={<></>}>
<LoadingLottie />
</Suspense> ,
size: "large",
}}
locale={{
emptyText: (
<EmptyData
loading={response?.isLoading}
header={t("Table.header")}
info={t("Table.info")}
isLoading || isRefetching ?
<></>
:
<Suspense fallback={<></>}>
<NotFoundLottie
/>
</Suspense>
),
}}
pagination={{
...pagination,
onChange: handlePageChange,
nextIcon:<>{t("practical.next")}</>,
prevIcon:<> {t("practical.prev")} </>,
className:"pagination_antd"
}}
{...props}
/>

View File

@ -1,15 +1,11 @@
import { Table } from "antd";
import { useAddKeyToData } from "../../../Hooks/useAddKeyToData";
import Loading from "../../../Components/DataState/Loading";
import EmptyData from "../../../Components/DataState/EmptyData";
import usePagination from "../usePagination";
import { useTranslation } from "react-i18next";
export {
Table,
useAddKeyToData,
Loading,
EmptyData,
usePagination,
useTranslation,
};

View File

@ -12,14 +12,14 @@ const usePagination = (data: Data) => {
const [pagination, setPagination] = useState<PaginationAntd>({
current: data?.meta?.current_page || 1,
pageSize: data?.meta?.per_page || 10,
pageSize: data?.meta?.per_page || 2,
total: data?.meta?.total || 0,
});
useEffect(() => {
setPagination({
current: data?.meta?.current_page || 1,
pageSize: data?.meta?.per_page || 10,
pageSize: data?.meta?.per_page || 2,
total: data?.meta?.total || 0,
});
}, [data]);

View File

@ -32,7 +32,7 @@ const NavBar = ({isOpen}:{isOpen:boolean}) => {
return (
<div className="NavBar">
<Suspense fallback={<SpinContainer />}>
<Suspense fallback={<></>}>
{/* <span className="navbar_link" onClick={handelNavigate}>
<MdOutlineArrowForwardIos /> {PageTitle}
</span> */}
@ -40,6 +40,7 @@ const NavBar = ({isOpen}:{isOpen:boolean}) => {
<SearchFieldWithSelect
options={translateArray}
placeholder={t("practical.search_here")}
withIcon
/>
</div>
<NavBarRightSide />

View File

@ -26,7 +26,7 @@ const AddModel: React.FC = () => {
handleSubmit={handleSubmit}
getInitialValues={getInitialValues({})}
getValidationSchema={getValidationSchema}
width="40vw"
width="500px"
>
<ModelForm />
</LayoutModel>

View File

@ -32,7 +32,7 @@ const EditModel: React.FC = () => {
getInitialValues={getInitialValues(objectToEdit)}
getValidationSchema={getValidationSchema}
isAddModal={false}
width="40vw"
width="500px"
>
<ModelForm />
</LayoutModel>

View File

@ -25,6 +25,7 @@ const TableHeader = () => {
return (
<div className="TableWithHeader">
<Suspense fallback={<Spin />}>
<PageHeader
pageTitle="grade"
ModelAbility={ModalEnum?.GRADE_ADD}

View File

@ -0,0 +1,33 @@
import React from "react";
import { getInitialValues, getValidationSchema } from "./formUtil";
import { ModalEnum } from "../../../../enums/Model";
import LayoutModel from "../../../../Layout/Dashboard/LayoutModel";
import { QueryStatusEnum } from "../../../../enums/QueryStatus";
import ModelForm from "./ModelForm";
import { useAddStudent } from "../../../../api/student";
const AddModel: React.FC = () => {
const { mutate, status } = useAddStudent();
const handleSubmit = (values: any) => {
mutate({
...values,
});
};
return (
<>
<LayoutModel
status={status as QueryStatusEnum}
ModelEnum={ModalEnum.STUDENT_ADD}
modelTitle="student"
handleSubmit={handleSubmit}
getInitialValues={getInitialValues({})}
getValidationSchema={getValidationSchema}
>
<ModelForm />
</LayoutModel>
</>
);
};
export default AddModel;

View File

@ -0,0 +1,37 @@
import React from "react";
import { getInitialValues, getValidationSchema } from "./formUtil";
import { ModalEnum } from "../../../../enums/Model";
import LayoutModel from "../../../../Layout/Dashboard/LayoutModel";
import ModelForm from "./ModelForm";
import { QueryStatusEnum } from "../../../../enums/QueryStatus";
import { useObjectToEdit } from "../../../../zustand/ObjectToEditState";
import { useUpdateStudent } from "../../../../api/student";
import { handelImageState } from "../../../../utils/DataToSendImageState";
const EditModel: React.FC = () => {
const { mutate, status } = useUpdateStudent();
const { objectToEdit } = useObjectToEdit((state) => state);
const handleSubmit = (values: any) => {
const Data_to_send = { ...values };
mutate(Data_to_send);
};
return (
<>
<LayoutModel
status={status as QueryStatusEnum}
ModelEnum={ModalEnum.STUDENT_EDIT}
modelTitle="student"
handleSubmit={handleSubmit}
getInitialValues={getInitialValues(objectToEdit)}
getValidationSchema={getValidationSchema}
isAddModal={false}
>
<ModelForm />
</LayoutModel>
</>
);
};
export default EditModel;

View File

@ -0,0 +1,58 @@
import { Col, Row } from "reactstrap";
import ValidationField from "../../../../Components/ValidationField/ValidationField";
import { useGetAllGrade } from "../../../../api/grade";
import { useValidationValidationParamState } from "../../../../Components/ValidationField/state/ValidationValidationParamState";
const Form = ({ isEdit = false }: { isEdit?: boolean }) => {
const { ValidationParamState } = useValidationValidationParamState();
const {
GradeName, GradeCurrentPage,
} = ValidationParamState;
const { data: Grade, isLoading: isLoadingGrade } = useGetAllGrade({
name: GradeName,
page: GradeCurrentPage
});
const GradeOption = Grade?.data ?? []
const canChangeGradePage = !!Grade?.links?.next;
const GradePage = Grade?.meta?.currentPage;
const sex = [
{name:"male" , id :"male"},
{name:"female" , id :"female"}
]
return (
<Row className="w-100">
<Col>
<ValidationField name="first_name" placeholder="first_name" label="first_name" />
<ValidationField name="last_name" placeholder="last_name" label="last_name" />
<ValidationField name="username" placeholder="username" label="username" />
{!isEdit &&
<ValidationField name="password" placeholder="password" label="password" />
}
</Col>
<Col>
<ValidationField name="phone_number" placeholder="contact_number1" label="contact_number1" />
<ValidationField
searchBy="GradeName"
name="grade_id"
label="grade"
type="Search"
option={GradeOption}
isLoading={isLoadingGrade}
canChangePage={canChangeGradePage}
PageName={"GradeCurrentPage"}
page={GradePage}
/>
<ValidationField type="Select" name="sex" option={sex} />
</Col>
</Row>
);
};
export default Form;

View File

@ -0,0 +1,27 @@
import * as Yup from "yup";
import { Student, StudentInitialValues } from "../../../../types/Student";
export const getInitialValues = (
objectToEdit: Partial<Student>,
): StudentInitialValues => {
return {
id: objectToEdit?.user_id,
first_name: objectToEdit?.first_name ?? "",
last_name: objectToEdit?.last_name ?? "",
// address: objectToEdit?.address ?? "",
// birthday: objectToEdit?.birthday ?? "",
// city: objectToEdit?.city ?? "",
grade_id: objectToEdit?.grade_id ,
// image: objectToEdit?.image ?? "",
sex: objectToEdit?.sex ,
};
};
export const getValidationSchema = () => {
// validate input
return Yup.object().shape({
first_name: Yup.string().required("validation.required"),
last_name: Yup.string().required("validation.required"),
});
};

View File

@ -0,0 +1,47 @@
import { useTranslation } from "react-i18next";
import { lazy, Suspense } from "react";
import { Spin } from "antd";
import useSetPageTitle from "../../../Hooks/useSetPageTitle";
import { ModalEnum } from "../../../enums/Model";
import { useDeleteStudent } from "../../../api/student";
import PageHeader from "../../../Layout/Dashboard/PageHeader";
import { canAddStudent } from "../../../utils/hasAbilityFn";
import FilterLayout from "../../../Layout/Dashboard/FilterLayout";
import FilterForm from "./Model/FilterForm";
const Table = lazy(() => import("./Table"));
const AddModalForm = lazy(() => import("./Model/AddModel"));
const EditModalForm = lazy(() => import("./Model/EditModel"));
const DeleteModalForm = lazy(
() => import("../../../Layout/Dashboard/DeleteModels"),
);
const TableHeader = () => {
const [t] = useTranslation();
const deleteMutation = useDeleteStudent();
useSetPageTitle(t(`page_header.student`));
return (
<div className="TableWithHeader">
<Suspense fallback={<Spin />}>
<PageHeader
pageTitle="student"
ModelAbility={ModalEnum?.STUDENT_ADD}
canAdd={canAddStudent}/>
<FilterLayout
sub_children={<FilterForm/>}
filterTitle="table.student"/>
<Table />
<AddModalForm />
<EditModalForm />
<DeleteModalForm
deleteMutation={deleteMutation}
ModelEnum={ModalEnum?.STUDENT_DELETE}
/>
</Suspense>
</div>
);
};
export default TableHeader;

View File

@ -0,0 +1,13 @@
import { useColumns } from "./useTableColumns";
import React from "react";
import DataTable from "../../../Layout/Dashboard/Table/DataTable";
import { useGetAllStudent } from "../../../api/student";
const App: React.FC = () => {
const response = useGetAllStudent({ pagination: true });
return <DataTable response={response} useColumns={useColumns} />;
};
export default App;

View File

@ -0,0 +1,91 @@
import { TableColumnsType } from "antd";
import { Student } from "../../../types/Student";
import { FaPlus } from "react-icons/fa";
import useModalHandler from "../../../utils/useModalHandler";
import { ModalEnum } from "../../../enums/Model";
import { useObjectToEdit } from "../../../zustand/ObjectToEditState";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import {
canAddStudent,
canDeleteStudent,
canEditStudent,
canShowStudent,
} from "../../../utils/hasAbilityFn";
import ActionButtons from "../../../Components/Table/ActionButtons";
export const useColumns = () => {
const { handel_open_model } = useModalHandler();
const { setObjectToEdit } = useObjectToEdit((state) => state);
const navigate = useNavigate();
const handelShow = (record: Student) => {
navigate(`${record?.user_id}`);
};
const handelDelete = (data: Student) => {
setObjectToEdit(data);
handel_open_model(ModalEnum?.STUDENT_DELETE);
};
const handleEdit = (record: Student) => {
setObjectToEdit(record);
handel_open_model(ModalEnum?.STUDENT_EDIT);
};
const [t] = useTranslation();
const columns: TableColumnsType<Student> = [
{
title: t("columns.id"),
dataIndex: "id",
key: "id",
align: "center",
render: (_text, record) => record?.user_id,
},
{
title: `${t("columns.first_name")}`,
dataIndex: "first_name",
key: "first_name",
align: "center",
render: (_text, record) => record?.first_name,
},
{
title: `${t("columns.last_name")}`,
dataIndex: "last_name",
key: "last_name",
align: "center",
render: (_text, record) => record?.last_name,
},
{
title: `${t("columns.sex")}`,
dataIndex: "sex",
key: "sex",
align: "center",
render: (_text, record) => record?.sex,
},
{
title: "",
key: "actions",
align: "center",
render: (_text, record, index) => {
return (
<ActionButtons
canDelete={canDeleteStudent}
canEdit={canEditStudent}
canShow={canShowStudent}
index={index}
onDelete={() => handelDelete(record)}
onEdit={() => handleEdit(record)}
onShow={() => handelShow(record)}
/>
);
},
},
];
return columns;
};

View File

@ -1,4 +1,4 @@
import React, { useContext, useEffect, useMemo } from "react";
import React, { Suspense, useContext, useEffect, useMemo } from "react";
import { HolderOutlined } from "@ant-design/icons";
import type { DragEndEvent } from "@dnd-kit/core";
import { DndContext } from "@dnd-kit/core";
@ -12,10 +12,16 @@ import {
} from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { Button, Table } from "antd";
import type { TableColumnsType } from "antd";
import { useParams } from "react-router-dom";
import { ParamsEnum } from "../../enums/params";
import { useGetAllUnit } from "../../api/unit";
import { ParamsEnum } from "../../../enums/params";
import { useGetAllUnit, useUpdateUnitOrder } from "../../../api/unit";
const NotFoundLottie = React.lazy(() => import("../../../Components/Lottie/NotFound/NotFoundLottie"));
const LoadingLottie = React.lazy(() => import("../../../Components/Lottie/Loading/LoadingLottie" ));
import { useTranslation } from "react-i18next";
import { useColumns } from "./useTableColumns";
interface DataType {
id: string; // Unique identifier for each row
@ -32,7 +38,7 @@ interface RowContextProps {
const RowContext = React.createContext<RowContextProps>({});
const DragHandle: React.FC = () => {
export const DragHandleUnit: React.FC = () => {
const { setActivatorNodeRef, listeners } = useContext(RowContext);
return (
<Button
@ -46,12 +52,6 @@ const DragHandle: React.FC = () => {
);
};
const columns: TableColumnsType<DataType> = [
{ key: "sort", align: "center", width: 80, render: () => <DragHandle /> },
{ title: "Name", dataIndex: "name" },
{ title: "Age", dataIndex: "age" },
{ title: "Address", dataIndex: "address" },
];
interface RowProps extends React.HTMLAttributes<HTMLTableRowElement> {
"data-row-key": string;
@ -96,13 +96,10 @@ const DrapableTable: React.FC = () => {
response?.data?.data?.map((item: any, index: number) => ({
id: item.id, // Ensure this is a unique identifier
order: index + 1, // Assign order based on index
name: item.name,
age: item.age,
address: item.address,
...item
})) ?? [];
const [dataSource, setDataSource] = React.useState<DataType[]>(data);
console.log(dataSource, "dataSource");
useEffect(() => {
// Update dataSource when the fetched data changes
@ -110,28 +107,51 @@ const DrapableTable: React.FC = () => {
setDataSource(sortedData);
}, [response?.data?.data]);
const {mutate:orderUnit} = useUpdateUnitOrder({},{
retry:false
})
const onDragEnd = ({ active, over }: DragEndEvent) => {
if (active.id !== over?.id) {
setDataSource((prevState) => {
const activeIndex = prevState.findIndex(
(record) => record.id === active.id,
);
const overIndex = prevState.findIndex(
//@ts-ignore
(record) => record.id === over.id,
);
// Move the items in the array
const newState = arrayMove(prevState, activeIndex, overIndex);
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
return newState.map((item, index) => ({
...item,
order: index + 1, // Update the order based on the new index
}));
const orderedNewStateWithNewChape = orderedNewState?.map((item:any)=>{
return {
"unit_id":item?.id,
"order":item?.order
}
})
orderUnit({units: orderedNewStateWithNewChape, _method:"PUT"})
return orderedNewState
});
}
};
const getRowClassName = (record: any, index: number): string => {
return index % 2 === 0 ? "even-row" : "odd-row";
};
const isLoading = response?.isLoading;
const [t] = useTranslation()
const columns = useColumns();
const sortedDataSource = dataSource.sort((a, b) => a.order - b.order) ;
console.log(sortedDataSource,"sortedDataSource");
return (
<DndContext modifiers={[restrictToVerticalAxis]} onDragEnd={onDragEnd}>
<SortableContext
@ -141,9 +161,32 @@ const DrapableTable: React.FC = () => {
<Table
rowKey="id"
components={{ body: { row: Row } }}
//@ts-ignore
columns={columns}
dataSource={dataSource.sort((a, b) => a.order - b.order)} // Sort by order for rendering
dataSource={sortedDataSource}
pagination={false}
rowClassName={(record, index) => getRowClassName(record, index)}
className="DataTable"
loading={{
spinning: isLoading,
indicator:<Suspense fallback={<></>}>
<LoadingLottie />
</Suspense> ,
size: "large",
}}
locale={{
emptyText: (
isLoading ?
<></>
:
<Suspense fallback={<></>}>
<NotFoundLottie
/>
</Suspense>
),
}}
/>
</SortableContext>
</DndContext>

View File

@ -10,7 +10,7 @@ import { useObjectToEdit } from "../../../../zustand/ObjectToEditState";
const AddModel: React.FC = () => {
const { mutate, status } = useAddUnit();
const { curriculum_id } = useParams();
const { subject_id } = useParams();
const { OldObjectToEdit } = useObjectToEdit();
const handleSubmit = (values: any) => {
@ -21,7 +21,7 @@ const AddModel: React.FC = () => {
mutate({
...values,
curriculum_id: curriculum_id,
subject_id: subject_id,
order: order,
});
};
@ -34,7 +34,7 @@ const AddModel: React.FC = () => {
handleSubmit={handleSubmit}
getInitialValues={getInitialValues({})}
getValidationSchema={getValidationSchema}
width="60vw"
>
<ModelForm />
</LayoutModel>

View File

@ -27,7 +27,7 @@ const EditModel: React.FC = () => {
getInitialValues={getInitialValues(objectToEdit)}
getValidationSchema={getValidationSchema}
isAddModal={false}
width="60vw"
>
<ModelForm />
</LayoutModel>

View File

@ -5,7 +5,7 @@ import { enumToArray } from "../../../../api/utils/enumToArray";
const Form = () => {
const termsArray = enumToArray(TermEnum);
console.log(termsArray, "termsArray");
console.log(termsArray);
return (
<Row className="w-100">

View File

@ -7,7 +7,6 @@ import { useGetAllSubject } from "../../../api/subject";
import useSetPageTitle from "../../../Hooks/useSetPageTitle";
import { ModalEnum } from "../../../enums/Model";
import { useDeleteUnit } from "../../../api/unit";
import DrapableTable from "./DrapableTable";
import { useGetAllGrade } from "../../../api/grade";
import { useGetAllCurriculum } from "../../../api/curriculum";
import PageHeader from "../../../Layout/Dashboard/PageHeader";
@ -15,7 +14,7 @@ import FilterLayout from "../../../Layout/Dashboard/FilterLayout";
import FilterForm from "./Model/FilterForm";
import { canAddUnit } from "../../../utils/hasAbilityFn";
const Table = lazy(() => import("./Table"));
const Table = lazy(() => import("./DrapableTable"));
const AddModalForm = lazy(() => import("./Model/AddModel"));
const EditModalForm = lazy(() => import("./Model/EditModel"));
const DeleteModalForm = lazy(

View File

@ -17,6 +17,8 @@ import {
} from "../../../utils/hasAbilityFn";
import ActionButtons from "../../../Components/Table/ActionButtons";
import { Unit } from "../../../types/Unit";
import { ConvertEnumToTranslate } from "../../../utils/ConvertEnumToTranslate";
import { DragHandleUnit } from "./DrapableTable";
export const useColumns = () => {
const { handel_open_model } = useModalHandler();
@ -40,6 +42,8 @@ export const useColumns = () => {
const [t] = useTranslation();
const columns: TableColumnsType<Unit> = [
{ key: "sort", align: "center", width: 80, render: () => <DragHandleUnit /> },
{
title: t("columns.id"),
dataIndex: "id",
@ -52,7 +56,7 @@ export const useColumns = () => {
dataIndex: "name",
key: "name",
align: "center",
render: (text, record) => record?.name,
render: (text) => text,
},
{
@ -60,20 +64,10 @@ export const useColumns = () => {
dataIndex: "term",
key: "term",
align: "center",
render: (text, record) => record?.term,
render: (text) => t(ConvertEnumToTranslate(text)),
},
{
// canAddUnit ? (
// <button
// onClick={() => handel_open_model(ModalEnum?.UNIT_ADD)}
// className="add_button"
// >
// {t("practical.add")} {t("models.unit")} <FaPlus />
// </button>
// ) : (
// ""
// ),
title: t("columns.procedure"),
key: "actions",

View File

@ -5,8 +5,10 @@ const Form = () => {
return (
<Row className="w-100">
<Col>
<ValidationField placeholder="name" label="name" name="name" />
<ValidationField placeholder="name" label="name" name="name" type="Number" />
</Col>
</Row>
);
};

View File

@ -1,10 +1,8 @@
import { FaPlus } from "react-icons/fa";
import useModalHandler from "../../../utils/useModalHandler";
import { ModalEnum } from "../../../enums/Model";
import { useTranslation } from "react-i18next";
import { lazy, Suspense } from "react";
import { Spin } from "antd";
import { canAddReport, canAddTags, canAddUser } from "../../../utils/hasAbilityFn";
import { canAddUser } from "../../../utils/hasAbilityFn";
import useSetPageTitle from "../../../Hooks/useSetPageTitle";
import { useDeleteTag } from "../../../api/tags";
import PageHeader from "../../../Layout/Dashboard/PageHeader";
@ -14,12 +12,8 @@ const Table = lazy(() => import("./Table"));
const AddModalForm = lazy(() => import("./Model/AddModel"));
const EditModalForm = lazy(() => import("./Model/EditModel"));
const DeleteModalForm = lazy(() => import("../../../Layout/Dashboard/DeleteModels"));
const SearchField = lazy(
() => import("../../../Components/DataTable/SearchField"),
);
const TableHeader = () => {
const { handel_open_model } = useModalHandler();
const [t] = useTranslation();
useSetPageTitle(
t(`page_header.user`),

View File

@ -10,6 +10,7 @@ const App: React.FC = () => {
const response = useGetAllUser({
name: searchQuery,
pagination: true,
});
return <DataTable response={response} useColumns={useColumns} />;

View File

@ -0,0 +1,194 @@
import React, { Suspense, useContext, useEffect, useMemo } from "react";
import { HolderOutlined } from "@ant-design/icons";
import type { DragEndEvent } from "@dnd-kit/core";
import { DndContext } from "@dnd-kit/core";
import type { SyntheticListenerMap } from "@dnd-kit/core/dist/hooks/utilities";
import { restrictToVerticalAxis } from "@dnd-kit/modifiers";
import {
arrayMove,
SortableContext,
useSortable,
verticalListSortingStrategy,
} from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { Button, Table } from "antd";
import { useParams } from "react-router-dom";
import { ParamsEnum } from "../../../enums/params";
import { useGetAllLesson, useUpdateLessonOrder } from "../../../api/lesson";
const NotFoundLottie = React.lazy(() => import("../../../Components/Lottie/NotFound/NotFoundLottie"));
const LoadingLottie = React.lazy(() => import("../../../Components/Lottie/Loading/LoadingLottie" ));
import { useTranslation } from "react-i18next";
import { useColumns } from "./useTableColumns";
interface DataType {
id: string; // Unique identifier for each row
order: number;
name: string;
age: number;
address: string;
}
interface RowContextProps {
setActivatorNodeRef?: (element: HTMLElement | null) => void;
listeners?: SyntheticListenerMap;
}
const RowContext = React.createContext<RowContextProps>({});
export const DragHandleLesson: React.FC = () => {
const { setActivatorNodeRef, listeners } = useContext(RowContext);
return (
<Button
type="text"
size="small"
icon={<HolderOutlined />}
style={{ cursor: "move" }}
ref={setActivatorNodeRef}
{...listeners}
/>
);
};
interface RowProps extends React.HTMLAttributes<HTMLTableRowElement> {
"data-row-key": string;
}
const Row: React.FC<RowProps> = (props) => {
const {
attributes,
listeners,
setNodeRef,
setActivatorNodeRef,
transform,
transition,
isDragging,
} = useSortable({ id: props["data-row-key"] });
const style: React.CSSProperties = {
...props.style,
transform: CSS.Translate.toString(transform),
transition,
...(isDragging ? { position: "relative", zIndex: 9999 } : {}),
};
const contextValue = useMemo<RowContextProps>(
() => ({ setActivatorNodeRef, listeners }),
[setActivatorNodeRef, listeners],
);
return (
<RowContext.Provider value={contextValue}>
<tr {...props} ref={setNodeRef} style={style} {...attributes} />
</RowContext.Provider>
);
};
const DrapableTable: React.FC = () => {
const { subject_id } = useParams<ParamsEnum>();
const response = useGetAllLesson({ subject_id: subject_id, pagination: false });
// Assuming the response contains a unique id for each item
const data =
response?.data?.data?.map((item: any, index: number) => ({
id: item.id, // Ensure this is a unique identifier
order: index + 1, // Assign order based on index
...item
})) ?? [];
const [dataSource, setDataSource] = React.useState<DataType[]>(data);
useEffect(() => {
// Update dataSource when the fetched data changes
const sortedData = data.sort((a: any, b: any) => a.order - b.order);
setDataSource(sortedData);
}, [response?.data?.data]);
const {mutate:orderLesson} = useUpdateLessonOrder()
const onDragEnd = ({ active, over }: DragEndEvent) => {
if (active.id !== over?.id) {
setDataSource((prevState) => {
const activeIndex = prevState.findIndex(
(record) => record.id === active.id,
);
const overIndex = prevState.findIndex(
//@ts-ignore
(record) => record.id === over.id,
);
// Move the items in the array
const newState = arrayMove(prevState, activeIndex, overIndex);
const orderedNewState = newState.map((item, index) => ({
...item,
order: index + 1, // Update the order based on the new index
}));
// Update the order based on the new positions
const orderedNewStateWithNewChape = orderedNewState?.map((item:any)=>{
return {
"lesson_id":item?.id,
"order":item?.order
}
})
orderLesson({lessons: orderedNewStateWithNewChape, _method:"PUT"})
return orderedNewState
});
}
};
const getRowClassName = (record: any, index: number): string => {
return index % 2 === 0 ? "even-row" : "odd-row";
};
const [t] = useTranslation()
const columns = useColumns();
const sortedDataSource = dataSource.sort((a, b) => a.order - b.order) ;
console.log(sortedDataSource,"sortedDataSource");
const isLoading = response?.isLoading
return (
<DndContext modifiers={[restrictToVerticalAxis]} onDragEnd={onDragEnd}>
<SortableContext
items={dataSource.map((i) => i.id)}
strategy={verticalListSortingStrategy}
>
<Table
rowKey="id"
components={{ body: { row: Row } }}
//@ts-ignore
columns={columns}
dataSource={sortedDataSource}
pagination={false}
rowClassName={(record, index) => getRowClassName(record, index)}
className="DataTable"
loading={{
spinning: isLoading,
indicator:<Suspense fallback={<></>}>
<LoadingLottie />
</Suspense> ,
size: "large",
}}
locale={{
emptyText: (
isLoading ?
<></>
:
<Suspense fallback={<></>}>
<NotFoundLottie
/>
</Suspense>
),
}}
/>
</SortableContext>
</DndContext>
);
};
export default DrapableTable;

View File

@ -42,6 +42,7 @@ const AddModel: React.FC = () => {
handleSubmit={handleSubmit}
getInitialValues={getInitialValues({})}
getValidationSchema={getValidationSchema}
width="500px"
>
<ModelForm />
</LayoutModel>

View File

@ -43,6 +43,7 @@ const ModalForm: React.FC = () => {
getInitialValues={getInitialValues(objectToEdit)}
getValidationSchema={getValidationSchema}
isAddModal={false}
width="500px"
>
<ModelForm />
</LayoutModel>

View File

@ -15,7 +15,7 @@ import FilterLayout from "../../../Layout/Dashboard/FilterLayout";
import FilterForm from "./Model/FilterForm";
import { canAddLesson } from "../../../utils/hasAbilityFn";
const Table = lazy(() => import("./Table"));
const Table = lazy(() => import("./DrapableTable"));
const AddModalForm = lazy(() => import("./Model/AddModel"));
const EditModalForm = lazy(() => import("./Model/EditModel"));
const DeleteModelsForm = lazy(

View File

@ -14,6 +14,7 @@ import {
canShowLesson,
} from "../../../utils/hasAbilityFn";
import ActionButtons from "../../../Components/Table/ActionButtons";
import { DragHandleLesson } from "./DrapableTable";
export const useColumns = () => {
const { handel_open_model } = useModalHandler();
@ -37,6 +38,7 @@ export const useColumns = () => {
const [t] = useTranslation();
const columns: TableColumnsType<Lesson> = [
{ key: "sort", align: "center", width: 80, render: () => <DragHandleLesson /> },
{
title: t("columns.id"),
dataIndex: "id",

View File

@ -16,7 +16,7 @@ import { useObjectToEdit } from "../../../zustand/ObjectToEditState";
import Header from "../../../Components/exercise/Header";
import { Question } from "../../../types/Item";
import BaseForm from "./Model/Malty/Add";
import BaseForm from "./Model/Malty/Form";
import ModelForm from "./Model/ModelForm";
const AcceptModal = lazy(() => import("./Model/AcceptModal"));
@ -42,6 +42,7 @@ const AddPage: React.FC = () => {
isBseQuestion,
setTagsSearch,
setObjectToEdit,
objectToEdit,
setSuccess,
SavedQuestionData,
} = useObjectToEdit();
@ -97,20 +98,19 @@ const AddPage: React.FC = () => {
"/" +
t("practical.add"),
);
const handleSubmit = (
values: any,
{ resetForm }: { resetForm: () => void },
) => {
const DataToSend = structuredClone(values);
console.log(DataToSend);
setTagsSearch(null);
const canAnswersBeShuffled = DataToSend?.canAnswersBeShuffled ? 1 : 0;
if (isBseQuestion || DataToSend?.isBase === 1) {
const newBseQuestion = {
subject_id: subject_id,
content: DataToSend?.content,
image: DataToSend?.image ?? "",
content_image: DataToSend?.content_image ?? "",
isBase: 1,
lessons_ids: [lesson_id],
canAnswersBeShuffled,
@ -146,71 +146,77 @@ const AddPage: React.FC = () => {
});
} else {
const tags = processTags(DataToSend);
console.log(values,"values");
const answers = values?.answers?.map((item:any,index:number)=>{
return {
order:index,
...item
}
})
mutate({
const NewQuestion = {
...values,
subject_id: subject_id,
tags,
lessons_ids: [lesson_id],
canAnswersBeShuffled,
answers
});
}
console.clear()
console.log(NewQuestion,"NewQuestion");
mutate(NewQuestion);
}
};
const navigate = useNavigate();
// console.log(SavedQuestionData);
useEffect(() => {
if (
isSuccessAsync &&
(SavedQuestionData?.Questions?.length > 0 ? isSuccess : true)
) {
setObjectToEdit(null);
setSuccess(true);
localStorage.removeItem(QUESTION_OBJECT_KEY);
}
if (isSuccess && !SavedQuestionData?.Questions?.length) {
toast.success(t("validation.the_possess_done_successful"));
setObjectToEdit(null);
setSuccess(true);
localStorage.removeItem(QUESTION_OBJECT_KEY);
}
}, [isSuccess, isSuccessAsync]);
// useEffect(() => {
// if (
// isSuccessAsync &&
// (SavedQuestionData?.Questions?.length > 0 ? isSuccess : true)
// ) {
// setObjectToEdit(null);
// setSuccess(true);
// localStorage.removeItem(QUESTION_OBJECT_KEY);
// }
// if (isSuccess && !SavedQuestionData?.Questions?.length) {
// toast.success(t("validation.the_possess_done_successful"));
// setObjectToEdit(null);
// setSuccess(true);
// localStorage.removeItem(QUESTION_OBJECT_KEY);
// }
// }, [isSuccess, isSuccessAsync]);
let cleanedAnswers = cleanObject(SavedQuestionData);
let noChange = hasItems(cleanedAnswers);
// let cleanedAnswers = cleanObject(SavedQuestionData);
// let noChange = hasItems(cleanedAnswers);
// console.log(SavedQuestionData);
useSaveOnDisconnect(noChange, QUESTION_OBJECT_KEY, SavedQuestionData);
// useSaveOnDisconnect(noChange, QUESTION_OBJECT_KEY, SavedQuestionData);
const SavedData = getLocalStorageQuestions(QUESTION_OBJECT_KEY);
// const SavedData = getLocalStorageQuestions(QUESTION_OBJECT_KEY);
// console.log(SavedData);
const handleCancel = () => {
if (!noChange) {
navigate(-1);
localStorage.removeItem(QUESTION_OBJECT_KEY);
} else {
setIsOpen(ModalEnum?.QUESTION_ACCEPT);
}
navigate(-1);
// if (!noChange) {
// localStorage.removeItem(QUESTION_OBJECT_KEY);
// } else {
// setIsOpen(ModalEnum?.QUESTION_ACCEPT);
// }
};
if (isBseQuestion || SavedData?.isBase === 1) {
if (isBseQuestion ) {
return (
<div className="exercise_add">
<FormikForm
handleSubmit={handleSubmit}
initialValues={getInitialValuesBase(SavedData)}
initialValues={getInitialValuesBase(objectToEdit)}
validationSchema={getValidationSchemaBase}
>
<main className="w-100 exercise_add_main">
<Header/>
<ModelForm/>
<Header />
<BaseForm />
<div className="exercise_add_buttons">
<div onClick={handleCancel}>{t("practical.back")}</div>
@ -236,7 +242,7 @@ const AddPage: React.FC = () => {
<div className="exercise_add">
<FormikForm
handleSubmit={handleSubmit}
initialValues={getInitialValues(SavedData)}
initialValues={getInitialValues(objectToEdit)}
validationSchema={getValidationSchema}
>
<main className="w-100 exercise_add_main">

View File

@ -22,7 +22,7 @@ import { useObjectToEdit } from "../../../zustand/ObjectToEditState";
import { removeStringKeys } from "../../../utils/removeStringKeys";
import SpinContainer from "../../../Components/Layout/SpinContainer";
import ModelForm from "./Model/ModelForm";
import BaseForm from "./Model/Malty/Edit";
import BaseForm from "./Model/Malty/Form";
import { Question } from "../../../types/Item";
import { toast } from "react-toastify";
import useSetPageTitle from "../../../Hooks/useSetPageTitle";

View File

@ -0,0 +1,25 @@
import React from 'react'
import ValidationField from '../../../Components/ValidationField/ValidationField'
import { Col, Row } from "reactstrap";
const FilterForm = () => {
return (
<div>
<Row>
<Col>
<ValidationField placeholder="name" label="name" name="name" />
<ValidationField placeholder="name" label="name" name="name" />
</Col>
<Col>
<ValidationField placeholder="name" label="name" name="name" />
<ValidationField placeholder="name" label="name" name="name" />
</Col>
</Row>
</div>
)
}
export default FilterForm

View File

@ -15,18 +15,18 @@ const CheckboxField = ({
const formik = useFormikContext<any>();
const [t] = useTranslation();
const CheckboxhandleChange = (value: any, index: number) => {
const allAreZero = formik?.values?.QuestionOptions?.some(
const allAreZero = formik?.values?.answers?.some(
(item: any) => item.isCorrect === 1,
);
if (allAreZero) {
formik?.values.QuestionOptions.forEach((item: any, index: number) => {
formik.setFieldValue(`QuestionOptions[${index}].isCorrect`, 0);
formik?.values.answers.forEach((item: any, index: number) => {
formik.setFieldValue(`answers[${index}].isCorrect`, 0);
});
}
formik.setFieldValue(
`QuestionOptions[${name}].isCorrect`,
`answers[${name}].isCorrect`,
value?.target?.checked ? 1 : 0,
);
};
@ -35,7 +35,7 @@ const CheckboxField = ({
<Checkbox
onChange={onChange || CheckboxhandleChange}
disabled={isDisabled}
checked={formik.values?.QuestionOptions?.[name]?.isCorrect === 1}
checked={formik.values?.answers?.[name]?.isCorrect === 1}
className={className}
>
{t(`input.${label ? label : name}`)}

View File

@ -6,10 +6,8 @@ import { useTranslation } from "react-i18next";
import { getCharFromNumber } from "../../../../../utils/getCharFromNumber";
import CheckboxField from "./CheckboxField";
import TextField from "./TextField";
import File from "./File";
import { FaCirclePlus, FaDeleteLeft } from "react-icons/fa6";
import ImageBoxField from "../../../../../Components/CustomFields/ImageBoxField/ImageBoxField";
import { GoTrash } from "react-icons/go";
import { LuImagePlus } from "react-icons/lu";
const ChoiceFields = ({ index, data }: { index: number; data: Choice }) => {
const formik = useFormikContext<any>();
@ -18,46 +16,56 @@ const ChoiceFields = ({ index, data }: { index: number; data: Choice }) => {
const handleDeleteChoice = () => {
console.log(index);
console.log(formik.values.QuestionOptions[index]);
console.log(formik.values.answers[index]);
const updatedQuestionOptions = formik.values.QuestionOptions.filter(
const updatedAnswers = formik.values.answers.filter(
(_: any, i: any) => i !== index,
);
formik.setFieldValue("QuestionOptions", updatedQuestionOptions);
formik.setFieldValue("answers", updatedAnswers);
};
console.log(formik.values);
return (
<div className="ChoiceFields">
<TextField
className="textarea_exercise"
placeholder={"choice"}
label2={t(`input.choice`) + ` ` + `(${getCharFromNumber(index)})`}
name={index}
id={`choice_${index + 1}`}
type="TextArea"
/>
<File
className="file_exercise"
label={"attachment"}
name={index}
type="File"
placeholder=""
icon={<LuImagePlus/>}
/>
<>
<div className="ChoiceFields">
<TextField
className="textarea_exercise"
placeholder={"choice"}
label2={t(`input.choice`) + ` ` +`( ${t(`alphabet.${getCharFromNumber(index)}`)} )`}
name={index}
id={`choice_${index + 1}`}
type="TextArea"
/>
<CheckboxField
className=""
label="The_correct_answer"
name={index}
type="Checkbox"
/>
<p className="delete_question_options">
{t("header.delete_choice")}
<GoTrash className="trash_icon" onClick={handleDeleteChoice} size={17} />
</p>
</div>
<ImageBoxField name={`answers.${index}.content_image`} />
<div className="answer_status">
<CheckboxField
className=""
label="The_correct_answer"
name={index}
type="Checkbox"
/>
<p className="delete_question_options">
{t("header.delete_choice")}
<GoTrash className="trash_icon" onClick={handleDeleteChoice} size={17} />
</p>
</div>
</div>
<div className="exercise_form_width">
<ValidationField
className=" "
placeholder="_"
name={`answers.${index}.hint`}
label="hint"
type="text"
style={{ width: "100%" }}
/>
</div>
</>
);
};

View File

@ -35,7 +35,7 @@ const Choices = () => {
return (
<DragDropContext onDragEnd={handleDragEnd}>
<Droppable droppableId="choices">
{(provided:any) => (
{(provided) => (
<div {...provided.droppableProps} ref={provided.innerRef}>
{formik?.values?.answers?.map(
(item: Choice, index: number) => {
@ -50,7 +50,7 @@ const Choices = () => {
draggableId={draggableId}
index={index}
>
{(provided:any) => (
{(provided) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}

View File

@ -1,87 +0,0 @@
import { Button, Upload, UploadFile } from "antd";
import useFormField from "../../../../../Hooks/useFormField";
import { UploadOutlined } from "@ant-design/icons";
import { useMemo } from "react";
const File = ({
name,
label,
onChange,
isDisabled,
placeholder,
className,
props,
icon,
}: any) => {
const newName = `QuestionOptions[${name}].answer_image`;
const { formik, t, isError, errorMsg } = useFormField(newName, props);
let imageUrl = formik?.values?.QuestionOptions[name]?.answer_image ?? null;
// console.log(imageUrl);
console.log(imageUrl);
const fileList: UploadFile[] = useMemo(() => {
if (!imageUrl) return [];
return [
typeof imageUrl === "string"
? {
uid: "-1",
name: "uploaded-image",
status: "done",
url: imageUrl,
thumbUrl: imageUrl,
}
: {
uid: imageUrl.uid || "-1",
name: imageUrl.name || "uploaded-image",
status: "done",
originFileObj: imageUrl,
},
];
}, [imageUrl]);
// console.log(1);
const FilehandleChange = (value: any) => {
// console.log(value,"filevalue");
if (value.fileList.length === 0) {
formik.setFieldValue(newName, null);
} else {
formik.setFieldValue(
`QuestionOptions[${name}].answer_image`,
value?.file?.originFileObj,
);
}
};
const customRequest = async ({ onSuccess, no_label, label_icon }: any) => {
onSuccess();
};
return (
<div className={`ValidationField upload_image_button ${className ?? ""} `}>
<label htmlFor={name} className="text">
{t(`input.${label || name}`)}
</label>
<Upload
disabled={isDisabled}
listType="picture"
maxCount={1}
fileList={[...fileList]}
onChange={onChange || FilehandleChange}
customRequest={customRequest}
className={` w-100`}
>
<Button
className={isError ? "isError w-100 " : " w-100"}
icon={icon ? icon : <UploadOutlined />}
>
{placeholder ?? t("input.Click_to_upload_the_image")}
</Button>
<div className="Error_color"> {isError ? "required" : ""}</div>
{errorMsg}
</Upload>
</div>
);
};
export default File;

View File

@ -1,69 +0,0 @@
import { Form, Input } from "antd";
import React from "react";
import useFormField from "../../../../../Hooks/useFormField";
import { MdOutlineEdit } from "react-icons/md";
import { Field } from "formik";
const { TextArea } = Input;
const HintField = ({
name,
label,
label2,
placeholder,
isDisabled,
onChange,
props,
no_label,
label_icon,
id,
className,
}: any) => {
const newName = `answers[${name}].hint`;
const { formik, isError, errorMsg, t } = useFormField(newName, props);
const TextFilehandleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
) => {
// console.log('Change:', e.target.value);
formik.setFieldValue(newName, e.target.value);
};
return (
<div className={`ValidationField w-100 ${className ?? ""} `}>
{no_label ? (
<label htmlFor={name} className="text">
<span>empty</span>
</label>
) : label_icon ? (
<div className="LabelWithIcon">
<label htmlFor={name} className="text">
{label2 ? label2 : t(`input.${label ? label : name}`)}
</label>
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
</div>
) : (
<label htmlFor={name} className="text">
{label2 ? label2 : t(`input.${label ? label : name}`)}
</label>
)}
<Form.Item
hasFeedback
validateStatus={isError ? "error" : ""}
help={isError ? errorMsg : ""}
>
<Field
as={Input}
placeholder={t(`input.${placeholder ? placeholder : name}`)}
name={newName}
disabled={isDisabled}
size="large"
onChange={onChange || TextFilehandleChange}
style={{ width: 200 }}
id={id}
/>
</Form.Item>
</div>
);
};
export default React.memo(HintField);

View File

@ -1,8 +1,8 @@
import { Form, Input } from "antd";
import React from "react";
import useFormField from "../../../../../Hooks/useFormField";
import { MdOutlineEdit } from "react-icons/md";
import { Field } from "formik";
import useFormField from "../../../../../Hooks/useFormField";
const { TextArea } = Input;
const TextField = ({
@ -18,7 +18,7 @@ const TextField = ({
id,
className,
}: any) => {
const newName = `QuestionOptions[${name}].answer`;
const newName = `answers[${name}].content`;
const { formik, isError, errorMsg, t } = useFormField(newName, props);
const TextFilehandleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,

View File

@ -10,17 +10,14 @@ import { useTranslation } from "react-i18next";
import DynamicTags from "./Tags/DynamicTags";
import QuestionFIeld from "./QuestionFIeld/QuestionFIeld";
import { useObjectToEdit } from "../../../../../zustand/ObjectToEditState";
import Choices from "./ChoiceField/Choices";
const Form = () => {
const formik = useFormikContext<any>();
const { isOpen } = useModalState((state) => state);
// const {data} = useGetAllQuestion();
const { setSuccess, Success, setSavedQuestionData } = useObjectToEdit();
useEffect(() => {
if (Success) {
console.log(1);
formik.setErrors({});
formik.resetForm({ values: {} });
setSuccess(false);
@ -32,14 +29,13 @@ const Form = () => {
}, [formik?.values]);
// console.log(formik?.errors);
console.log(formik?.values?.Questions, "formik?.values?.Questions");
const handleAddChoice = (parent_index: number) => {
console.log(parent_index);
formik.setFieldValue(`Questions.[${parent_index}].QuestionOptions`, [
formik.setFieldValue(`Questions.[${parent_index}].answers`, [
...((formik?.values as any)?.Questions?.[parent_index]
.QuestionOptions as Choice[]),
.answers as Choice[]),
{
answer: null,
@ -58,9 +54,9 @@ const Form = () => {
image: "",
parent: "",
isBase: 0,
max_mark: 1,
min_mark_to_pass: 1,
QuestionOptions: [{ answer: null, answer_image: null, isCorrect: 0 }],
// max_mark: 1,
// min_mark_to_pass: 1,
answers: [{ answer: null, answer_image: null, isCorrect: 0 }],
tags: [],
},
]);
@ -70,7 +66,6 @@ const Form = () => {
formik.setFieldValue("max_mark", max_mark);
};
const [t] = useTranslation();
console.log(formik.errors);
return (
<Row className="w-100">
@ -109,21 +104,9 @@ const Form = () => {
/>
</div>
{(
(formik?.values as any)?.Questions?.[parent_index]
?.QuestionOptions || []
).map((item: Choice, index: number) => {
return (
<ChoiceFields
key={index}
parent_index={parent_index}
index={index}
data={item}
/>
);
})}
<Choices parent_index={parent_index} />
{formik?.values?.Questions?.[parent_index]?.QuestionOptions
{formik?.values?.Questions?.[parent_index]?.answers
?.length < 5 && (
<p className="add_new_button">
<FaCirclePlus

View File

@ -1,4 +1,5 @@
import React from "react";
import useFormField from "../../../../../../Hooks/useFormField";
import { Checkbox, Form } from "antd";
import { useFormik, useFormikContext } from "formik";
import { useTranslation } from "react-i18next";
@ -15,16 +16,15 @@ const CheckboxField = ({
const formik = useFormikContext<any>();
const [t] = useTranslation();
const CheckboxhandleChange = (value: any) => {
console.log(value?.target?.checked);
const allAreZero = formik?.values?.Questions?.[
parent_index
]?.QuestionOptions?.some((item: any) => item.isCorrect === 1);
]?.answers?.some((item: any) => item.isCorrect === 1);
if (allAreZero) {
formik?.values?.Questions?.[parent_index]?.QuestionOptions.forEach(
formik?.values?.Questions?.[parent_index]?.answers.forEach(
(item: any, index: number) => {
formik.setFieldValue(
`Questions[${parent_index}].QuestionOptions[${index}].isCorrect`,
`Questions[${parent_index}].answers[${index}].isCorrect`,
0,
);
},
@ -32,7 +32,7 @@ const CheckboxField = ({
}
formik.setFieldValue(
`Questions[${parent_index}].QuestionOptions[${name}].isCorrect`,
`Questions[${parent_index}].answers[${name}].isCorrect`,
value?.target?.checked ? 1 : 0,
);
};
@ -42,7 +42,7 @@ const CheckboxField = ({
onChange={onChange || CheckboxhandleChange}
disabled={isDisabled}
checked={
formik.values?.Questions?.[parent_index]?.QuestionOptions?.[name]
formik.values?.Questions?.[parent_index]?.answers?.[name]
?.isCorrect === 1
}
className={className}

View File

@ -9,6 +9,9 @@ import TextField from "./TextField";
import File from "./File";
import { FaTrash } from "react-icons/fa";
import { toast } from "react-toastify";
import HintField from "./HintField";
import ImageBoxField from "../../../../../../Components/CustomFields/ImageBoxField/ImageBoxField";
import { GoTrash } from "react-icons/go";
const ChoiceFields = ({
index,
@ -25,7 +28,7 @@ const ChoiceFields = ({
const handleDeleteChoice = () => {
const arrayLength =
formik.values.Questions?.[parent_index].QuestionOptions?.length;
formik.values.Questions?.[parent_index].answers?.length;
console.log(arrayLength);
@ -36,43 +39,58 @@ const ChoiceFields = ({
return;
}
const updatedQuestionOptions = formik.values.Questions?.[
const updatedAnswers = formik.values.Questions?.[
parent_index
].QuestionOptions.filter((_: any, i: any) => i !== index);
].answers.filter((_: any, i: any) => i !== index);
formik.setFieldValue(
`Questions[${parent_index}].QuestionOptions`,
updatedQuestionOptions,
`Questions[${parent_index}].answers`,
updatedAnswers,
);
};
return (
<>
<div className="ChoiceFields">
<TextField
className="textarea_exercise"
placeholder={"choice"}
label2={t(`input.choice`) + ` ` + `(${getCharFromNumber(index)})`}
label2={t(`input.choice`) + ` ` + `( ${t(`alphabet.${getCharFromNumber(index)}`)} )`}
name={index}
parent_index={parent_index}
type="TextArea"
/>
<File
className="file_exercise"
label={"attachment"}
name={index}
type="File"
parent_index={parent_index}
/>
<CheckboxField
className=""
label="The_correct_answer"
name={index}
type="Checkbox"
parent_index={parent_index}
/>
<p className="delete_question_options">
<FaTrash onClick={handleDeleteChoice} size={17} />
</p>
<ImageBoxField name={`Questions.${parent_index}.answers.${index}.answer_image`} />
<div className="answer_status">
<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>
</div>
<div className="exercise_form_width">
<ValidationField
className=" "
placeholder="_"
name={`Questions.${parent_index}.answers.${index}.hint`}
label="hint"
type="text"
style={{ width: "100%" }}
/>
</div>
</>
);
};

View File

@ -13,11 +13,11 @@ const File = ({
parent_index,
props,
}: any) => {
const newName = `Questions[${parent_index}].QuestionOptions[${name}].answer_image`;
const newName = `Questions[${parent_index}].answers[${name}].answer_image`;
const { formik, t, isError, errorMsg } = useFormField(newName, props);
let imageUrl =
formik?.values?.Questions?.[parent_index]?.QuestionOptions[name]
formik?.values?.Questions?.[parent_index]?.answers[name]
?.answer_image ?? null;
// console.log(imageUrl);
@ -49,7 +49,7 @@ const File = ({
formik.setFieldValue(newName, null);
} else {
formik.setFieldValue(
`Questions[${parent_index}].QuestionOptions[${name}].answer_image`,
`Questions[${parent_index}].answers[${name}].answer_image`,
value?.file?.originFileObj,
);
}

View File

@ -18,7 +18,7 @@ const TextField = ({
parent_index,
className,
}: any) => {
const newName = `Questions[${parent_index}].QuestionOptions[${name}].answer`;
const newName = `Questions[${parent_index}].answers[${name}].content`;
const { formik, isError, errorMsg, t } = useFormField(newName, props);
const TextFilehandleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,

View File

@ -30,9 +30,9 @@ const Form = () => {
const handleAddChoice = (parent_index: number) => {
console.log(parent_index);
console.log(formik?.values?.Questions);
formik.setFieldValue(`Questions.[${parent_index}].QuestionOptions`, [
formik.setFieldValue(`Questions.[${parent_index}].answers`, [
...((formik?.values as any)?.Questions?.[parent_index]
.QuestionOptions as Choice[]),
.answers as Choice[]),
{
answer: null,
@ -53,7 +53,7 @@ const Form = () => {
isBase: 0,
max_mark: 1,
min_mark_to_pass: 1,
QuestionOptions: [{ answer: null, answer_image: null, isCorrect: 0 }],
answers: [{ answer: null, answer_image: null, isCorrect: 0 }],
tags: [],
},
]);
@ -103,7 +103,7 @@ const Form = () => {
{(
(formik?.values as any)?.Questions?.[parent_index]
?.QuestionOptions || []
?.answers || []
).map((item: Choice, index: number) => {
return (
<ChoiceFields
@ -114,7 +114,7 @@ const Form = () => {
/>
);
})}
{formik?.values?.Questions?.[parent_index]?.QuestionOptions
{formik?.values?.Questions?.[parent_index]?.answers
?.length < 5 && (
<p className="add_new_button">
<FaCirclePlus

View File

@ -0,0 +1,134 @@
import { Col, Row } from "reactstrap";
import React, { useEffect } from "react";
import ValidationField from "../../../../../Components/ValidationField/ValidationField";
import { useFormikContext } from "formik";
import { useModalState } from "../../../../../zustand/Modal";
import ChoiceFields from "./ChoiceField/ChoiceFields";
import { FaCirclePlus } from "react-icons/fa6";
import { Choice } from "../../../../../types/Item";
import { useTranslation } from "react-i18next";
import DynamicTags from "./Tags/DynamicTags";
import QuestionFIeld from "./QuestionFIeld/QuestionFIeld";
import { useObjectToEdit } from "../../../../../zustand/ObjectToEditState";
import Choices from "./ChoiceField/Choices";
import ImageBoxField from "../../../../../Components/CustomFields/ImageBoxField/ImageBoxField";
import MaltySelectTag from "./Tags/MaltySelectTag";
const Form = () => {
const formik = useFormikContext<any>();
const { setSuccess, Success, setSavedQuestionData } = useObjectToEdit();
useEffect(() => {
if (Success) {
formik.setErrors({});
formik.resetForm({ values: {} });
setSuccess(false);
}
}, [Success]);
useEffect(() => {
setSavedQuestionData(formik.values);
}, [formik?.values]);
// console.log(formik?.errors);
const handleAddChoice = (parent_index: number) => {
console.log(parent_index);
formik.setFieldValue(`Questions.[${parent_index}].answers`, [
...((formik?.values as any)?.Questions?.[parent_index]
.answers as Choice[]),
{
answer: null,
answer_image: null,
isCorrect: 0,
},
]);
};
const handleAddQuestion = () => {
formik.setFieldValue("Questions", [
...((formik?.values as any)?.Questions as Choice[]),
{
content: "",
image: "",
parent: "",
isBase: 0,
// max_mark: 1,
// min_mark_to_pass: 1,
answers: [{ answer: null, answer_image: null, isCorrect: 0 }],
tags: [],
},
]);
const max_mark = formik?.values?.max_mark + 1;
formik.setFieldValue("max_mark", max_mark);
};
const [t] = useTranslation();
return (
<Row className="w-100 exercise_form_container">
<div className="exercise_form">
<ValidationField className="textarea_exercise" name="content" label="main_question" type="TextArea" />
<ImageBoxField name="image" />
<div></div>
</div>
<div className=" flex "></div>
{((formik?.values as any)?.Questions || [])?.map(
(item: Choice, parent_index: number) => {
return (
<div key={parent_index}>
<div className="exercise_form">
<QuestionFIeld
key={parent_index}
index={parent_index}
data={item}
/>
</div>
<Choices parent_index={parent_index} />
{formik?.values?.Questions?.[parent_index]?.answers
?.length < 5 && (
<p className="add_new_button">
<FaCirclePlus
onClick={() => handleAddChoice(parent_index)}
size={23}
/>{" "}
{t("header.add_new_choice")}
</p>
)}
<div className="exercise_form_width">
<ValidationField
className=" "
placeholder="_"
name={`answers.${parent_index}.hint`}
label="hint_question"
type="text"
style={{ width: "100%" }}
/>
<MaltySelectTag parent_index={parent_index} />
</div>
</div>
);
},
)}
<p className="add_new_button">
<FaCirclePlus onClick={handleAddQuestion} size={23} />{" "}
{t("header.add_new_question")}
</p>
</Row>
);
};
export default Form;

View File

@ -9,10 +9,13 @@ import File from "./File";
import { FaTrash } from "react-icons/fa";
import { useObjectToEdit } from "../../../../../../zustand/ObjectToEditState";
import { toast } from "react-toastify";
import CheckboxField from "./CheckboxField";
import HintField from "./HintField";
import ImageBoxField from "../../../../../../Components/CustomFields/ImageBoxField/ImageBoxField";
import { GoTrash } from "react-icons/go";
const QuestionFIeld = ({ index, data }: { index: number; data: Choice }) => {
const formik = useFormikContext<any>();
console.log(index);
const { setDeletedQuestions, DeletedQuestions } = useObjectToEdit();
const [t] = useTranslation();
@ -25,31 +28,43 @@ const QuestionFIeld = ({ index, data }: { index: number; data: Choice }) => {
if (DeleteQuestionId?.id) {
setDeletedQuestions([...DeletedQuestions, DeleteQuestionId]);
}
const updatedQuestionOptions = formik.values.Questions.filter(
const updatedAnswers = formik.values.Questions.filter(
(_: any, i: any) => i !== index,
);
formik.setFieldValue(`Questions`, updatedQuestionOptions);
formik.setFieldValue(`Questions`, updatedAnswers);
};
return (
<div className="ChoiceFields">
<TextField
className="textarea_exercise"
placeholder={"choice"}
label2={t(`input.question`) + ` ` + `${index + 1}`}
name={index}
type="TextArea"
/>
<File
className="file_exercise"
label={"attachment"}
name={index}
type="File"
/>
<p className="delete_question_options">
<FaTrash onClick={handleDeleteQuestion} size={17} />
</p>
</div>
<>
<div className="exercise_forms">
<div className="ChoiceFields">
<TextField
className="textarea_exercise"
placeholder={"question"}
label2={t(`input.question`) + ` ` + `( ${t(`alphabet.${getCharFromNumber(index)}`)} )`}
name={index}
id={`question_${index + 1}`}
type="TextArea"
/>
<ImageBoxField name={`Questions.${index}.image`} />
<div className="answer_status">
<p className="delete_question_options">
{t("header.delete_question")}
<GoTrash className="trash_icon" onClick={handleDeleteQuestion} size={17} />
</p>
</div>
</div>
</div>
</>
);
};

View File

@ -0,0 +1,77 @@
import React, { useState, useMemo } from 'react';
import { Select, Spin } from 'antd';
import { useTranslation } from 'react-i18next';
import { useFormikContext } from 'formik';
import { useDebounce } from '../../../../../../utils/useDebounce';
import { useGetAllTag } from '../../../../../../api/tags';
const MaltySelectTag = ({parent_index}:{parent_index:number}) => {
const [searchValue, setSearchValue] = useState<string>('');
const [fieldValue, setFieldValue] = useState<string>('');
const formik = useFormikContext<any>();
const values = formik?.values?.Questions?.[parent_index]?.tags;
const handleChange = (value: string[]) => {
formik.setFieldValue(`Questions.[${parent_index}].tags`,value)
setSearchValue('');
setFieldValue('');
};
const handleSearch = useDebounce((value: string) => {
setSearchValue(value);
});
const handleFieldChange = (value: string) => {
setFieldValue(value);
};
const handleBlur = () => {
setSearchValue('');
setFieldValue('');
};
const { data, isLoading } = useGetAllTag({
name: searchValue,
});
const [t] = useTranslation();
const options = data?.data ?? []
const additionalData = options?.length < 1 && searchValue.length > 1 && !isLoading ? [{id:`new_${searchValue}`,name:searchValue}] :[];
return (
<div className='SelectTag'>
<label htmlFor="">
{t("models.tag")}
</label>
<Select
mode="multiple"
allowClear
style={{ width: '100%' ,height:"40px"}}
placeholder=""
fieldNames={{ label: 'name', value: 'id' }}
onChange={handleChange}
options={[...options,...additionalData]}
filterOption={false}
loading={isLoading}
notFoundContent={isLoading ? <Spin /> : t("practical.not_found")}
onSearch={(value) => {
handleSearch(value);
handleFieldChange(value);
}}
searchValue={fieldValue}
onDropdownVisibleChange={(open) => {
if (!open) {
handleBlur();
}
}}
value={values ?? []}
/>
</div>
);
};
export default MaltySelectTag;

Some files were not shown because too many files have changed in this diff Show More