Compare commits

...

175 Commits
main ... dev

Author SHA1 Message Date
e5443ba3fa fix generator 2024-12-03 15:56:49 +03:00
094b89bdd1 ability generator 2024-11-23 19:00:32 +03:00
3d98fc56d1 fix generator 2024-11-22 02:06:36 +03:00
1e5b935bb2 generator 2024-11-22 01:59:39 +03:00
MoazDawalibi
2335da5230 fix question bank response to be suitable with backend edit on response 2024-11-22 01:13:17 +03:00
Majd_dk
ca02e02c17 add edit to multi question page 2024-11-20 17:25:12 +03:00
Majd_dk
2d3613d1e3 add QRCode to EditQuestionPage 2024-11-19 16:55:19 +03:00
Majd_dk
7b966d93ed Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-11-19 16:23:12 +03:00
Moaz Dawalibi
dd2d6e65b9 fix notification filter 2024-11-12 17:12:43 +03:00
Moaz Dawalibi
e0a117d674 format 2024-11-12 17:02:08 +03:00
Moaz Dawalibi
eff39f9f1b put dirty to question 2024-11-12 15:15:06 +03:00
Moaz Dawalibi
8eeb3b6d03 fix add notification section 2024-11-12 15:13:59 +03:00
Moaz Dawalibi
60e4349628 add notification page 2024-11-12 14:10:49 +03:00
Moaz Dawalibi
eb36e9cebb fix qr code bug (return null) 2024-11-12 12:23:06 +03:00
Moaz Dawalibi
34d6d3affd Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-11-12 12:11:13 +03:00
Moaz Dawalibi
7bbd9e620e add env file , prepare notification add page ,fix loading page and fix error toast message 2024-11-12 11:54:35 +03:00
Majd_dk
4f30716fd7 add ScrollBar 2024-11-09 15:36:33 +03:00
Majd_dk
db3d01cf23 add QRCode to questionBank 2024-11-09 15:35:30 +03:00
Majd_dk
0541b7fcfb fixes QrCodeModels 2024-11-09 14:46:29 +03:00
Majd_dk
c6c0958a3f add ability to show QR code 2024-11-09 14:17:31 +03:00
Moaz Dawalibi
d04423db0f change url 2024-11-06 12:35:42 +03:00
Moaz Dawalibi
ecbcbf67cb report index page , show page and notification page 2024-11-06 12:09:23 +03:00
Moaz Dawalibi
547f71f840 fixes 2024-11-05 14:15:40 +03:00
Moaz Dawalibi
40b0ef3ab2 reseller sales and collections apis 2024-10-23 17:30:41 +03:00
Moaz Dawalibi
5b304c8952 fixes 2024-10-23 16:41:08 +03:00
Moaz Dawalibi
4675311fce admin apis and fixes 2024-10-22 16:56:30 +03:00
Moaz Dawalibi
e3c93c2330 fixes 2024-10-22 15:06:50 +03:00
Moaz Dawalibi
5f0624f20d fix cache bug in sales page and some fixes 2024-10-22 14:06:34 +03:00
Moaz Dawalibi
f053952b5f fixes 2024-10-21 17:39:15 +03:00
Moaz Dawalibi
a6553b23d8 fixes 2024-10-16 11:10:27 +03:00
Moaz Dawalibi
b1c7714e57 fix loading 2024-10-15 16:36:18 +03:00
Moaz Dawalibi
b6acf12431 fix area bug and some fixes 2024-10-15 16:30:25 +03:00
Moaz Dawalibi
a9aa5cbcef fixes 2024-10-15 09:26:18 +03:00
Moaz Dawalibi
8b1af0e7f1 Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-10-10 11:48:43 +03:00
Moaz Dawalibi
b2117bb0e4 clean 2024-10-10 11:48:36 +03:00
karimaldeen
9f05382d6c fix 2024-10-09 16:27:26 +03:00
karimaldeen
7c54d52d98 fix nullable content 2024-10-08 09:42:14 +03:00
karimaldeen
45a960bce3 fix 2024-10-08 09:19:37 +03:00
karimaldeen
1556648532 remove dummy 2024-10-02 17:15:08 +03:00
karimaldeen
ce1a1843a0 Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-10-02 17:14:14 +03:00
karimaldeen
865f785112 make fix 2024-10-02 17:13:42 +03:00
Moaz Dawalibi
f095644f25 fix sidebar state 2024-10-02 10:16:27 +03:00
karimaldeen
ccbebf99a1 fix side bar 2024-09-30 16:28:37 +03:00
karimaldeen
3ff4e0f846 Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-09-30 16:25:06 +03:00
karimaldeen
3525d8f667 add coupon 2024-09-30 16:20:58 +03:00
karimaldeen
862380b2b3 #187 && #189 2024-09-30 11:30:58 +03:00
karimaldeen
0d3856d725 fix 2024-09-29 12:20:44 +03:00
Moaz Dawalibi
7fc7258a24 fixes 2024-09-26 13:13:32 +03:00
karimaldeen
80dffd913e tyay math 2024-09-26 12:59:39 +03:00
Moaz Dawalibi
9b3c1fb49b fix react query bug 2024-09-26 10:47:22 +03:00
Moaz Dawalibi
81eb55e2e0 reseller : sales page and collection page
admin: collection and some fixes
2024-09-26 09:40:58 +03:00
karimaldeen
3bdfdf799e fixes 2024-09-25 00:24:26 +03:00
karimaldeen
53d3908f00 Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-09-25 00:06:22 +03:00
Moaz Dawalibi
b47626f950 sale api 2024-09-25 00:04:57 +03:00
karimaldeen
10e1e28c69 finish map#178 2024-09-24 15:43:20 +03:00
karimaldeen
d71e5c4fce Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-09-24 15:25:26 +03:00
Moaz Dawalibi
d6ae95fbd5 Merge branch 'dev' of into dev 2024-09-24 15:24:16 +03:00
Moaz Dawalibi
ea4bc5cfd7 reseller page ,fixes 2024-09-24 15:23:09 +03:00
karimaldeen
c3edef43d5 add map 2024-09-24 15:22:41 +03:00
karimaldeen
eb86869d89 add city and area pages 2024-09-24 15:19:48 +03:00
karimaldeen
3bf0308631 end role and permission # 166 2024-09-24 14:54:27 +03:00
karimaldeen
9a6e5bc53d end #166 2024-09-24 10:59:02 +03:00
karimaldeen
ba2b5b411c add permission and wait for api #166 2024-09-23 17:27:25 +03:00
karimaldeen
d603736a16 math 2024-09-23 15:12:24 +03:00
Moaz Dawalibi
daddeb12f8 show eye in password input and some fixes 2024-09-23 14:29:49 +03:00
karimaldeen
999573fa0c change logo and color 2024-09-22 15:06:15 +03:00
karimaldeen
470cae3b13 add check on reload 2024-09-22 11:09:15 +03:00
karimaldeen
ec4850cbb8 re make 2024-09-21 16:42:38 +03:00
karimaldeen
0412bccffb Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-09-21 16:10:42 +03:00
karimaldeen
34d8a821b0 add validation 2024-09-21 16:10:28 +03:00
Moaz Dawalibi
9644b4e6ba Merge branch 'dev' of into dev 2024-09-21 15:48:36 +03:00
karimaldeen
ca653967fa fixx 2024-09-21 15:39:59 +03:00
karimaldeen
fa8f705a75 fix question 2024-09-21 15:13:59 +03:00
Moaz Dawalibi
25161d4afa complete setting page 2024-09-21 14:43:27 +03:00
karimaldeen
cda04660bf add question VariableSizeList 2024-09-21 14:15:12 +03:00
karimaldeen
869e857f2c fix lag question 2024-09-21 12:52:06 +03:00
karimaldeen
efb687b742 end mml input 2024-09-21 09:12:01 +03:00
karimaldeen
c49f498309 add latex input 2024-09-19 10:51:37 +03:00
Moaz Dawalibi
887b3a5e75 Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-09-18 13:18:00 +03:00
Moaz Dawalibi
e0e2839cf3 settings 2024-09-18 13:17:48 +03:00
karimaldeen
42bbdebf6f Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-09-18 10:31:23 +03:00
karimaldeen
16f6507f00 make question one_of_image_and_content_should_be_enter_in_question 2024-09-18 10:30:46 +03:00
karimaldeen
b87a96e358 add animations 2024-09-18 09:20:55 +03:00
Moaz Dawalibi
3a877b5053 Merge branch 'dev' of into dev 2024-09-17 11:40:14 +03:00
Moaz Dawalibi
44989ff1dd manager page ,reseller and fixes 2024-09-17 11:39:19 +03:00
karimaldeen
9409c7256d add option 2024-09-17 11:38:06 +03:00
karimaldeen
af89180d07 fix 2024-09-17 10:41:04 +03:00
karimaldeen
c858803cb0 Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-09-16 20:22:58 +03:00
karimaldeen
de71606e88 fix 2024-09-16 20:22:07 +03:00
Moaz Dawalibi
edd50bafad manager page design and fixes 2024-09-16 17:05:10 +03:00
karimaldeen
05d0a12527 fix tags 2024-09-16 15:48:33 +03:00
karimaldeen
32f87910ea fix tag 2024-09-16 15:29:53 +03:00
karimaldeen
1d39a4cd4f fix navbar 2024-09-16 12:39:27 +03:00
Moaz Dawalibi
c47b78df76 Merge branch 'dev' of into dev 2024-09-16 12:35:12 +03:00
karimaldeen
251359e935 add validation question 2024-09-16 12:34:01 +03:00
Moaz Dawalibi
bbf290fee1 fixes 2024-09-16 12:33:53 +03:00
Moaz Dawalibi
a987ec7dad fixes and role page 2024-09-16 12:23:13 +03:00
karimaldeen
458319b49f fix page 2024-09-16 12:14:19 +03:00
karimaldeen
0b44bc69fb fix image 2mb and add delete ask 2024-09-16 12:01:35 +03:00
Moaz Dawalibi
c5c484d5c3 Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-09-16 09:54:57 +03:00
Moaz Dawalibi
f7d8e62b89 fixes 2024-09-16 09:54:50 +03:00
karimaldeen
82c1fb4944 fix shadow 2024-09-16 09:52:16 +03:00
karimaldeen
6c51c37270 dix shadow 2024-09-16 09:44:32 +03:00
Moaz Dawalibi
98473e250b Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-09-16 09:39:07 +03:00
Moaz Dawalibi
9e4c075231 profile page and fixes 2024-09-16 09:36:37 +03:00
karimaldeen
448186c025 fix filter 2024-09-16 09:34:10 +03:00
karimaldeen
9655e7da41 fix search 2024-09-15 17:13:37 +03:00
karimaldeen
cd6e47903b Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-09-15 16:28:50 +03:00
karimaldeen
cea1277edb dix text 2024-09-15 16:28:30 +03:00
Moaz Dawalibi
1d31d5eb34 Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-09-15 16:13:28 +03:00
Moaz Dawalibi
b7f970a00e notification page and error page 2024-09-15 16:13:19 +03:00
karimaldeen
fe6e8aae95 fix filter 2024-09-15 15:52:13 +03:00
karimaldeen
a05bb8b46d Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-09-15 14:45:28 +03:00
karimaldeen
d6c8ff6468 fix image 2024-09-15 14:44:33 +03:00
Moaz Dawalibi
e470e902e5 fixes 2024-09-15 14:21:11 +03:00
Moaz Dawalibi
b846d250bb change tabs icon 2024-09-15 14:19:27 +03:00
Moaz Dawalibi
f03af7d690 Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-09-15 14:14:04 +03:00
Moaz Dawalibi
2f81c4129f show reseller page 2024-09-15 14:13:15 +03:00
karimaldeen
e8e50d0526 add filter 2024-09-15 12:53:32 +03:00
karimaldeen
21cbcc91bb change tag 2024-09-15 11:28:51 +03:00
Moaz Dawalibi
2949b8b4be Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-09-15 10:16:19 +03:00
Moaz Dawalibi
9e35207841 show reseller 2024-09-15 10:15:30 +03:00
karimaldeen
0f54e21546 add new route navigater 2024-09-15 10:03:06 +03:00
karimaldeen
b7276a2c6a fix question column 2024-09-14 17:02:32 +03:00
karimaldeen
091c0bf15c add filter lessonsIds 2024-09-14 16:40:10 +03:00
karimaldeen
0b827ba990 add new page "QuestionBank" 2024-09-14 16:21:57 +03:00
karimaldeen
693c1cdb06 remove un used code 2024-09-14 16:00:20 +03:00
karimaldeen
e981ce7481 add icon sidebar 2024-09-14 15:21:43 +03:00
karimaldeen
b1c5405ff7 fix validation 2024-09-14 15:17:40 +03:00
karimaldeen
fc861e768f Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-09-14 15:10:54 +03:00
karimaldeen
5c41a13cb1 remove question drag and drop 2024-09-14 15:08:53 +03:00
karimaldeen
cd89ad405b add button for ux 2024-09-14 14:52:14 +03:00
Moaz Dawalibi
36feda0b43 fixes 2024-09-14 14:41:13 +03:00
Moaz Dawalibi
4b3246e6e8 Merge branch 'dev' of into dev 2024-09-14 14:41:07 +03:00
Moaz Dawalibi
0c7603ce94 single student 2024-09-14 14:39:13 +03:00
karimaldeen
dbdaa04fde fix 2024-09-14 13:01:18 +03:00
karimaldeen
a8967b62f2 dix button delete 2024-09-14 11:59:18 +03:00
karimaldeen
0586a88cab add validation 2024-09-14 11:55:13 +03:00
karimaldeen
6dc2e5fe7c done 2024-09-12 17:26:35 +03:00
karimaldeen
293f2c9d84 fix column 2024-09-12 17:12:15 +03:00
karimaldeen
9f433d3f9b formate 2024-09-12 16:54:48 +03:00
karimaldeen
bd59108fdc dix bug 2024-09-12 16:53:31 +03:00
karimaldeen
482c7341f8 Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-09-12 16:27:35 +03:00
karimaldeen
0e7b6d71a0 doen 2024-09-12 16:26:50 +03:00
karimaldeen
bcc86c7841 dix unit 2024-09-12 16:24:13 +03:00
Moaz Dawalibi
55f854129e Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-09-12 16:16:56 +03:00
karimaldeen
e17629a9ba Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-09-12 16:16:19 +03:00
karimaldeen
a55b2a4761 fix question 2024-09-12 16:15:59 +03:00
Moaz Dawalibi
2d05b5c302 fixes 2024-09-12 16:15:56 +03:00
Moaz Dawalibi
08bd2d245d Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-09-12 13:43:59 +03:00
Moaz Dawalibi
089160a4ae fixes 2024-09-12 13:43:45 +03:00
karimaldeen
491e3a88bd fix question 2024-09-12 13:41:25 +03:00
Moaz Dawalibi
d56c4a06bf Merge branch 'dev' of into dev 2024-09-12 11:58:41 +03:00
Moaz Dawalibi
1eccab7434 fixes 2024-09-12 11:57:50 +03:00
karimaldeen
c08de08790 add filter 2024-09-12 10:08:59 +03:00
karimaldeen
c1e2718e14 remove curruclumn 2024-09-11 17:02:49 +03:00
karimaldeen
39a910d9ca remove log 2024-09-11 17:00:14 +03:00
karimaldeen
d3ab2a8f7d Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-09-11 16:50:42 +03:00
karimaldeen
e13709e96d add route permistion by user type 2024-09-11 16:50:06 +03:00
Moaz Dawalibi
553d749633 Merge branch 'dev' of https://git.point-dev.net/Karimaldeen/Quiz_dashboard into dev 2024-09-11 16:47:42 +03:00
Moaz Dawalibi
d95e9a4397 student package page,add filter for all dash pages,and some fixes 2024-09-11 16:47:32 +03:00
karimaldeen
9ecb7ae551 fix question 2024-09-11 16:42:17 +03:00
karimaldeen
513ba6cb6a add CombinationKey 2024-09-11 14:55:59 +03:00
Moaz Dawalibi
e6df238ef0 user 2024-09-10 12:33:47 +03:00
karimaldeen
9509943a60 add reseller 2024-09-10 12:29:57 +03:00
karimaldeen
398d1e8f07 format 2024-09-10 10:20:21 +03:00
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
594 changed files with 39947 additions and 10962 deletions

1
.env Normal file
View File

@ -0,0 +1 @@
VITE_BASE_URL="https://nerd-back.point-dev.net/api/"

1
.env.example Normal file
View File

@ -0,0 +1 @@
REACT_APP_BASE_URL=

View File

@ -10,8 +10,13 @@
"Groupbutton",
"handelnavigate",
"Karim",
"katex",
"Latext",
"mathml",
"Popconfirm",
"queryqlent",
"registraion",
"Sellcast",
"SENDNOTIFICATION",
"setdateparams",
"szhsin",
@ -19,6 +24,8 @@
"toastify",
"Viewelement",
"webp",
"Xmark",
"ZAKER",
"zustand",
"مطلوب"
],

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,18 +4,17 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<link rel="apple-touch-icon" sizes="180x180" href="/App/Logo2.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/App/Logo2.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/App/Logo2.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/App/Logo.svg" />
<link rel="icon" type="image/png" sizes="32x32" href="/App/Logo.svg" />
<link rel="icon" type="image/png" sizes="16x16" href="/App/Logo.svg" />
<link rel="manifest" href="/site.webmanifest" />
<meta
name="description"
content="social networking platform with automated content moderation and context-based authentication system"
/>
<script type="module" src="/src/index.tsx"></script>
<title>NERD DASHBOARD</title>
<title>ZAKER DASHBOARD</title>
</head>
<body dir="rtl">
<noscript>You need to enable JavaScript to run this app.</noscript>

8879
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -3,31 +3,43 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@ant-design/icons": "^5.3.7",
"antd": "^5.17.4",
"axios": "^1.7.2",
"@ant-design/icons": "^5.5.1",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/modifiers": "^7.0.0",
"@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@types/katex": "^0.16.7",
"@uiw/react-markdown-preview": "^5.1.3",
"antd": "^5.21.1",
"axios": "^1.7.7",
"bootstrap": "^5.3.3",
"dayjs": "^1.11.11",
"dayjs": "^1.11.13",
"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",
"i18next": "^23.15.1",
"katex": "^0.16.11",
"leaflet": "^1.9.4",
"lottie-react": "^2.4.0",
"mathjs": "^13.1.1",
"mathml-to-latex": "^1.4.1",
"npm": "^10.8.3",
"qrcode.react": "^4.1.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-katex": "^3.0.1",
"react-leaflet": "^4.2.1",
"react-qr-code": "^2.0.15",
"react-query": "^3.39.3",
"react-router-dom": "^6.23.1",
"react-router-dom": "^6.26.2",
"react-toastify": "^9.1.3",
"reactstrap": "^9.2.2",
"sass": "^1.77.4",
"ts-node": "^10.9.2",
"react-window": "^1.8.10",
"reactstrap": "^9.2.3",
"sass": "^1.79.4",
"vite-plugin-env-compatible": "^2.0.1",
"yup": "^1.4.0",
"zustand": "^4.5.2"
"zustand": "^4.5.5"
},
"scripts": {
"start": "vite --port=3000",
@ -35,7 +47,12 @@
"test": "vite jest",
"preview": "vite preview",
"eject": "react-scripts eject",
"format": "prettier --write ."
"format": "prettier --write .",
"g:api": "node src/Extensions/FileGenerator/generateApi.js",
"g:column": "node src/Extensions/FileGenerator/generateColumn.js",
"g:formutil": "node src/Extensions/FileGenerator/generateformUtils.js",
"g:page": "node src/Extensions/FileGenerator/generatePage.js",
"g:dashboard": "node src/Extensions/FileGenerator/generateDashboard.js "
},
"eslintConfig": {
"extends": [
@ -59,22 +76,26 @@
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/node": "^20.14.0",
"@types/react": "^18.3.3",
"@types/node": "^20.16.10",
"@types/react": "^18.3.10",
"@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",
"@vitejs/plugin-react": "^4.3.0",
"@types/react-katex": "^3.0.4",
"@types/react-latex": "^2.0.3",
"@types/react-window": "^1.8.8",
"@vitejs/plugin-legacy": "^5.4.2",
"@vitejs/plugin-react": "^4.3.1",
"jest": "^29.7.0",
"jsdom": "^24.1.0",
"prettier": "^3.3.0",
"jsdom": "^24.1.3",
"prettier": "^3.3.3",
"rollup-plugin-visualizer": "^5.12.0",
"ts-jest": "^29.1.4",
"ts-jest": "^29.2.5",
"ts-loader": "^9.5.1",
"typescript": "^4.9.5",
"vite": "^5.2.12",
"vite": "^5.4.8",
"vite-plugin-compression": "^0.5.1",
"webpack": "^5.93.0",
"webpack": "^5.95.0",
"webpack-cli": "^5.1.4"
}
}

File diff suppressed because it is too large Load Diff

BIN
public/App/Error.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

38
public/App/Logo.svg Normal file
View File

@ -0,0 +1,38 @@
<svg width="157" height="119" viewBox="0 0 157 119" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M153.338 33.2514C154.044 33.5665 154.674 33.9951 155.216 34.5497C155.758 35.1043 156.174 35.7345 156.489 36.453C156.804 37.1715 156.956 37.9404 156.956 38.7723V68.9229C156.956 69.7548 156.804 70.5237 156.489 71.2422C156.174 71.9607 155.746 72.5909 155.216 73.1455C154.687 73.7001 154.056 74.1287 153.338 74.4438C152.62 74.7589 151.851 74.9102 151.019 74.9102H122.645C121.813 74.9102 121.045 74.7589 120.326 74.4438C119.608 74.1287 118.99 73.7001 118.448 73.1455C117.906 72.5909 117.49 71.9607 117.175 71.2422C116.86 70.5237 116.708 69.7548 116.708 68.9229V61.2214H143.317V46.5368H135.767C133.057 46.4612 130.687 45.9822 128.633 45.0747C126.578 44.1671 124.826 43.0579 123.376 41.747C122.028 40.5369 120.919 39.2513 120.074 37.8899C119.229 36.5286 118.612 35.3186 118.221 34.2219C117.83 33.1253 117.692 32.2556 117.78 31.6128C117.868 30.9699 118.158 30.7682 118.637 31.0077C119.923 31.5623 121.435 31.9657 123.162 32.2052C124.889 32.4447 126.691 32.6085 128.557 32.6716C130.422 32.7346 132.25 32.7724 134.053 32.7472C135.855 32.722 137.443 32.7472 138.792 32.7724" fill="white"/>
<path d="M134.67 20.8736C134.872 24.8819 131.822 28.3104 127.864 28.5121C123.855 28.7137 120.402 25.5121 120.25 21.4534C120.112 17.5837 123.212 14.2182 127.095 14.0166C131.09 13.8149 134.469 16.8778 134.67 20.8736Z" fill="#FDBB2E"/>
<path d="M109.393 69.4019C109.608 73.5741 106.431 77.1413 102.31 77.3556C98.1373 77.5698 94.5323 74.2296 94.3811 70.007C94.2298 65.9734 97.4566 62.4693 101.503 62.2676C105.662 62.066 109.179 65.2424 109.393 69.4145V69.4019Z" fill="white"/>
<path d="M109.406 15.9451C109.394 20.1299 106.028 23.5206 101.894 23.4954C97.709 23.4827 94.3057 19.9534 94.3814 15.7182C94.457 11.6847 97.8729 8.35701 101.919 8.38222C106.079 8.40743 109.419 11.7729 109.406 15.9451Z" fill="white"/>
<path d="M103.293 18.6173L102.953 17.3946H100.848L100.444 18.6173H98.9192L101.062 12.4409H102.738L104.869 18.6173H103.293ZM101.894 13.9787L101.201 16.2224H102.524L101.881 13.9787H101.894Z" fill="#0290D2"/>
<path d="M109.406 33.9069C109.394 38.0917 106.028 41.4824 101.894 41.4572C97.709 41.4446 94.3057 37.9153 94.3814 33.6801C94.457 29.6465 97.8729 26.3189 101.919 26.3441C106.079 26.3693 109.419 29.7348 109.406 33.9069Z" fill="white"/>
<path d="M99.3604 37.411V30.5162H102.827C104.289 30.5162 104.932 31.2599 104.932 32.3187C104.932 32.9615 104.541 33.4783 104.037 33.68C104.629 33.8817 105.133 34.3858 105.133 35.369C105.133 36.6043 104.339 37.411 102.524 37.411H99.3604ZM100.911 31.8397V33.201H102.587C103.091 33.201 103.369 32.9867 103.369 32.5203C103.369 32.054 103.104 31.8397 102.549 31.8397H100.923H100.911ZM100.911 34.3858V36.1001H102.638C103.23 36.1001 103.558 35.7472 103.558 35.2052C103.558 34.6631 103.23 34.3858 102.638 34.3858H100.898H100.911Z" fill="#0290D2"/>
<path d="M109.381 51.8057C109.406 55.9905 106.053 59.4064 101.932 59.419C97.7469 59.4316 94.3185 55.9401 94.3563 51.7049C94.3941 47.6714 97.7848 44.3185 101.831 44.3059C105.99 44.3059 109.356 47.6336 109.381 51.8057Z" fill="#FDBB2E"/>
<path d="M105.12 52.9779C104.818 54.6166 103.671 55.7006 101.969 55.7006C100.066 55.7006 98.6038 54.4149 98.6038 51.9317C98.6038 49.4486 99.902 48.0117 101.982 48.0117C103.81 48.0117 104.969 49.1587 105.108 50.6208H103.431C103.242 49.8772 102.675 49.4738 101.994 49.4738C100.822 49.4738 100.305 50.3688 100.305 51.8435C100.305 53.3183 100.81 54.2384 101.919 54.2384C102.763 54.2384 103.23 53.7846 103.431 52.9779H105.108H105.12Z" fill="#0290D2"/>
<path d="M99.5366 73.1456V66.4902H102.146C103.974 66.4902 105.272 67.7255 105.272 69.7423C105.272 71.759 104.1 73.1456 102.36 73.1456H99.5492H99.5366ZM102.045 71.8725C103.217 71.8725 103.684 71.1792 103.684 69.7423C103.684 68.3053 103.242 67.7633 101.932 67.7633H101.049V71.8725H102.058H102.045Z" fill="#0290D2"/>
<path d="M84.1211 39.1252C83.5665 38.5706 82.9362 38.1421 82.2178 37.8269C81.5119 37.5118 80.7304 37.3606 79.8985 37.3606H56.6805L67.7475 29.054L91.5957 11.2813C90.9025 10.8527 90.1588 11.1174 89.1252 11.9241C86.2009 14.2182 83.1757 16.3988 79.6086 19.0584C79.2935 16.6383 79.0918 16.4997 79.2683 14.4577C79.4447 12.4409 78.2977 12.0502 76.8986 11.483C75.2851 10.8275 75.1969 10.5628 73.1423 9.56703C73.2305 8.98721 73.2053 8.83595 73.2558 8.79814C75.4364 7.14691 77.6422 5.5335 79.7851 3.84446C82.9237 1.38653 83.9698 1.28569 83.264 0L62.882 15.1762L57.1973 19.2223C57.1973 19.2223 57.1216 19.2601 57.0712 19.2853C56.8948 19.3988 56.7813 19.4744 56.7057 19.5248C56.8065 19.4492 56.8443 19.4366 56.1763 19.8525C55.7855 20.1046 54.941 20.7475 53.8948 21.5542L46.1933 27.0246C41.164 30.5918 38.7943 42.6924 42.374 47.7091C42.8782 48.6166 43.5337 49.3099 44.3278 49.8141C45.1219 50.3183 46.0294 50.6586 47.0378 50.8351C48.0462 51.0115 49.0546 51.0746 50.063 51.0494H72.2348V61.9273H33.0339V62.1794C33.0339 61.3475 32.8826 60.5786 32.5675 59.8601C32.2524 59.1416 31.8239 58.5114 31.2945 57.9568C30.7525 57.4022 30.1348 56.9736 29.4163 56.6585C28.7105 56.3434 27.929 56.1921 27.0971 56.1921H19.3325V61.9399C19.4081 63.4651 19.4081 64.8894 19.3325 66.2129C19.2569 67.5364 19.156 68.7591 19.0174 69.9061C18.8787 71.154 18.614 72.2758 18.2359 73.259C17.8578 74.2422 17.3788 75.1371 16.799 75.9186C16.2318 76.7001 15.5889 77.406 14.8704 78.0488C14.1646 78.6916 13.4209 79.2841 12.652 79.8513C11.7192 80.5067 10.7487 81.1496 9.76548 81.7546C8.78231 82.3596 7.79915 83.0025 6.82859 83.6831C5.85802 84.3638 4.92524 85.1075 4.0429 85.9268C3.16057 86.7461 2.37908 87.6915 1.72363 88.7629C1.16902 89.6704 0.753072 90.4771 0.475767 91.2082C0.198461 91.9393 0.0472101 92.5569 0.0093958 93.0611C-0.0284185 93.5653 0.0472132 93.9056 0.24889 94.0821C0.437961 94.2585 0.740478 94.2081 1.15644 93.9308C2.54296 93.0233 3.99249 92.2544 5.50506 91.6115C7.01763 90.9687 8.55542 90.4015 10.1184 89.8973C11.6814 89.3931 13.2444 88.9141 14.8074 88.4603C16.3704 88.0066 17.8956 87.4898 19.3956 86.8974C20.6812 86.4058 21.9165 85.8512 23.1139 85.2083C24.3114 84.5655 25.4332 83.7966 26.4668 82.9142C27.5004 82.0319 28.4458 80.9983 29.2777 79.8135C30.1096 78.6286 30.8281 77.2421 31.4079 75.6539H79.8733C80.7052 75.6539 81.4741 75.5026 82.1925 75.1875C82.8984 74.8724 83.5413 74.4438 84.0959 73.8892C84.6505 73.3346 85.079 72.7044 85.3942 71.9859C85.7093 71.2674 85.8605 70.4985 85.8605 69.6666V43.4235C85.8605 42.5916 85.7093 41.8101 85.3942 41.079C85.079 40.3479 84.6505 39.7051 84.0959 39.1504L84.1211 39.1252Z" fill="white"/>
<path d="M85.4824 3.27722C84.4866 4.44947 82.5329 5.68474 81.3228 6.6427C80.0623 7.53764 78.3229 9.05021 76.9111 9.68045C77.9069 8.52081 79.8606 7.27294 81.0707 6.31497C82.3312 5.42003 84.0706 3.90746 85.4824 3.27722Z" fill="#FDBB2E"/>
<path d="M87.4614 5.59656C86.6168 6.64275 84.9026 7.71416 83.8438 8.54608C82.7346 9.32757 81.2346 10.6637 79.9741 11.1679C80.8186 10.1217 82.5329 9.05027 83.5917 8.21835C84.7009 7.43686 86.2009 6.10075 87.4614 5.59656Z" fill="#FDBB2E"/>
<path d="M88.5327 8.76031C87.726 9.7813 86.0622 10.8149 85.0412 11.6216C83.9698 12.3779 82.5203 13.6762 81.2976 14.1552C82.1043 13.1342 83.7682 12.1006 84.7892 11.2939C85.8606 10.5502 87.3101 9.2393 88.5327 8.76031Z" fill="#FDBB2E"/>
<path d="M59.7836 35.0245L51.1941 23.5049L49.9512 24.4317L58.5407 35.9512L59.7836 35.0245Z" fill="url(#paint0_linear_19_2009)"/>
<path d="M61.5795 33.7296L52.99 22.21L51.7471 23.1368L60.3366 34.6564L61.5795 33.7296Z" fill="url(#paint1_linear_19_2009)"/>
<path d="M24.141 109.118V113.353H11.0698V110.366L18.4814 102.488H11.1959V98.2525H23.9393V101.24L16.5025 109.118H24.141Z" fill="white"/>
<path d="M41.649 98.2399V113.353H38.2709V110.328C37.1491 112.408 35.2205 113.706 32.7626 113.706C28.6156 113.706 25.4771 110.303 25.4771 105.79C25.4771 101.278 28.6156 97.8743 32.7626 97.8743C35.2205 97.8743 37.1491 99.1726 38.2709 101.252V98.2273H41.649V98.2399ZM38.2709 105.803C38.2709 103.761 36.2793 102.223 33.6197 102.223C30.9601 102.223 28.9938 103.761 28.9938 105.803C28.9938 107.845 30.9475 109.383 33.6197 109.383C36.2919 109.383 38.2709 107.845 38.2709 105.803Z" fill="white"/>
<path d="M50.649 107.492L48.4557 109.093V113.366H45.0776V92.0383H48.4557V103.446H49.1112L54.2035 98.2651H59.8631L53.3464 105.135L60.0395 113.378H55.6531L50.649 107.517V107.492Z" fill="white"/>
<path d="M75.6819 107.076H62.9763C63.5939 108.702 65.346 109.748 67.7409 109.748C69.5182 109.748 71.0938 109.244 71.7744 108.588H75.5054C74.3836 111.639 71.3585 113.719 67.6527 113.719C62.9763 113.719 59.4722 110.315 59.4722 105.803C59.4722 101.29 62.9637 97.8869 67.6148 97.8869C72.266 97.8869 75.7576 101.265 75.7576 105.891C75.7576 106.282 75.7323 106.723 75.6693 107.076H75.6819ZM63.0015 104.492H72.3038C71.7114 102.866 69.8711 101.857 67.6275 101.857C65.3838 101.857 63.6317 102.891 63.0015 104.492Z" fill="white"/>
<path d="M87.1774 98.0003V103.332C83.5977 103.332 81.6691 104.643 81.6691 107V113.34H78.291V98.2272H81.6691V101.983C82.8539 99.5003 84.7825 97.9877 87.1774 97.9877V98.0003Z" fill="white"/>
<path d="M111.29 98.2399V113.353H107.912V110.328C106.79 112.408 104.862 113.706 102.404 113.706C98.2568 113.706 95.1182 110.303 95.1182 105.79C95.1182 101.278 98.2568 97.8743 102.404 97.8743C104.862 97.8743 106.79 99.1726 107.912 101.252V98.2273H111.29V98.2399ZM107.912 105.803C107.912 103.761 105.92 102.223 103.261 102.223C100.601 102.223 98.6349 103.761 98.6349 105.803C98.6349 107.845 100.589 109.383 103.261 109.383C105.933 109.383 107.912 107.845 107.912 105.803Z" fill="white"/>
<path d="M130.903 105.803C130.903 110.303 127.79 113.719 123.618 113.719C121.16 113.719 119.206 112.382 118.11 110.315V119H114.731V98.2651H118.11V101.315C119.206 99.2357 121.16 97.9122 123.618 97.9122C127.803 97.9122 130.903 101.315 130.903 105.828V105.803ZM127.399 105.803C127.399 103.761 125.42 102.223 122.748 102.223C120.076 102.223 118.097 103.761 118.097 105.803C118.097 107.845 120.088 109.383 122.748 109.383C125.408 109.383 127.399 107.845 127.399 105.803Z" fill="white"/>
<path d="M149.684 105.803C149.684 110.303 146.571 113.719 142.399 113.719C139.941 113.719 137.987 112.382 136.891 110.315V119H133.512V98.2651H136.891V101.315C137.987 99.2357 139.941 97.9122 142.399 97.9122C146.584 97.9122 149.684 101.315 149.684 105.828V105.803ZM146.18 105.803C146.18 103.761 144.201 102.223 141.529 102.223C138.857 102.223 136.878 103.761 136.878 105.803C136.878 107.845 138.87 109.383 141.529 109.383C144.189 109.383 146.18 107.845 146.18 105.803Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear_19_2009" x1="49.9496" y1="29.7083" x2="59.7813" y2="29.7083" gradientUnits="userSpaceOnUse">
<stop offset="0.02" stop-color="#FFD140"/>
<stop offset="0.94" stop-color="#F9A61C"/>
<stop offset="1" stop-color="#F7941D"/>
</linearGradient>
<linearGradient id="paint1_linear_19_2009" x1="51.7318" y1="28.416" x2="61.5761" y2="28.416" gradientUnits="userSpaceOnUse">
<stop offset="0.02" stop-color="#FFD140"/>
<stop offset="0.94" stop-color="#F9A61C"/>
<stop offset="1" stop-color="#F7941D"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

BIN
public/App/whiteLogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

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

@ -8,17 +8,17 @@
"theme_color": "#000000",
"icons": [
{
"src": "/App/Logo.png",
"src": "/App/Logo.svg",
"sizes": "81x113",
"type": "image/png"
},
{
"src": "/App/Logo.png",
"src": "/App/Logo.svg",
"sizes": "81x113",
"type": "image/png"
},
{
"src": "/App/Logo.png",
"src": "/App/Logo.svg",
"sizes": "81x113",
"type": "image/png",
"purpose": "maskable"

View File

@ -5,10 +5,11 @@ import { Spin } from "antd";
import { hasAbility } from "./utils/hasAbility";
import { renderRoutesRecursively } from "./Components/Routes/RenderRoutesRecursively";
import { RenderRouteElement } from "./Components/Routes/RenderRouteElement";
import { UserTypeEnum } from "./enums/UserType";
import { RoleByType } from "./utils/RoleByType";
const Page404 = lazy(() => import("./Layout/Ui/NotFoundPage"));
const Auth = lazy(() => import("./Pages/Auth/Page"));
const App = () => {
return (
<Routes>
@ -35,6 +36,9 @@ const App = () => {
{CrudRoute.map((route) => {
const useAbility = hasAbility(route.abilities, route.abilities_value);
if (!RoleByType(route)) {
return false;
}
if (!useAbility) {
return false;
}

View File

@ -0,0 +1,26 @@
import { Divider } from "antd";
import { useTranslation } from "react-i18next";
import { RxHome } from "react-icons/rx";
const AddressCard = ({ data }: { data: any }) => {
const { t } = useTranslation();
return (
<div className="address_card">
<h5>{t("practical.address")}</h5>
<Divider />
<div className="address_card_body">
{data?.map((address: any) => (
<div>
<RxHome />
<span>
<h6>{t(`practical.${address?.key}`)}</h6>
<p>{address?.value}</p>
</span>
</div>
))}
</div>
</div>
);
};
export default AddressCard;

View File

@ -0,0 +1,24 @@
import { Divider } from "antd";
import { useTranslation } from "react-i18next";
import ImageBoxField from "../CustomFields/ImageBoxField/ImageBoxField";
const AttachmentsCard = ({ data }: { data?: any }) => {
const { t } = useTranslation();
return (
<div className="attachments_card">
<h5>{t("practical.address")}</h5>
<Divider />
<div className="attachments_body">
<h5>{t("practical.id_photo")}</h5>
{/* {data?.map((address:any)=>( */}
<span>
<ImageBoxField name="image" />
</span>
{/* ))} */}
</div>
</div>
);
};
export default AttachmentsCard;

View File

@ -0,0 +1,49 @@
import { Button, Divider } from "antd";
import { useTranslation } from "react-i18next";
import { canAddReSeller } from "../../utils/hasAbilityFn";
const InfoCard = ({
data,
name,
status,
withButton = false,
handleClick,
}: {
data: any;
name: any;
status: any;
withButton?: boolean;
handleClick?: () => void;
}) => {
const { t } = useTranslation();
return (
<div className="info_card">
<div className="info_card_header">
<img src="/Image/faker_user.png " alt="" />
<div className="student_name_and_sub">
<span>{status}</span>
<h2>{name}</h2>
</div>
</div>
<Divider />
<div className="info_card_body">
{data?.map((student: any) => (
<span>
<h4>{student?.key}</h4>
<p>{student?.value}</p>
</span>
))}
{withButton
? canAddReSeller && (
<Button onClick={handleClick} className="info_card_button">
{t("practical.collecting_an_amount")}
</Button>
)
: ""}
</div>
</div>
);
};
export default InfoCard;

View File

@ -0,0 +1,30 @@
export const StudentParamInfo = [
{ key: "الحنس", value: "male" },
{ key: "sex", value: "male" },
{ key: "sex", value: "male" },
{ key: "sex", value: "male" },
{ key: "sex", value: "male" },
{ key: "sex", value: "male" },
];
export const ReSellerParamInfo = [
{ key: "رقم الهوية", value: "12i9128921019" },
{ key: "تاريخ الإضافة", value: "1/10/2010" },
{ key: "تاريخ الإضافة", value: "1/10/2010" },
{ key: "تاريخ الإضافة", value: "1/10/2010" },
{ key: "تاريخ الإضافة", value: "1/10/2010" },
{ key: "تاريخ الإضافة", value: "1/10/2010" },
{ key: "تاريخ الإضافة", value: "1/10/2010" },
];
export const StudentAddressInfo = [
{ key: "address", value: "moa moamasom aoms omaosm oasm oasm oasm asm aom" },
];
export const ReSellerAddressInfo = [
{
key: "governorate",
value: "moa moamasom aoms omaosm oasm oasm oasm asm aom",
},
{ key: "address", value: "moa moamasom aoms omaosm oasm oasm oasm asm aom" },
];

View File

@ -1,44 +1,33 @@
import {
DownloadOutlined,
RotateLeftOutlined,
RotateRightOutlined,
SwapOutlined,
ZoomInOutlined,
ZoomOutOutlined,
} from "@ant-design/icons";
import React from "react";
import React, { useState } from "react";
import { Image, Space } from "antd";
import { ImageBaseURL } from "../../api/config";
import useImageError from "../../Hooks/useImageError";
import { ErrorImage } from "../../Layout/app/Const";
import { CiImageOff } from "react-icons/ci"; // Assuming you're using this icon from react-icons
const ColumnsImage = ({ src }: any) => {
const imageUrl = src || ErrorImage;
const [hasError, setHasError] = useState(false); // Track if the image has an error
const handleError = useImageError;
// or you can download flipped and rotated image
// https://codesandbox.io/s/zi-ding-yi-gong-ju-lan-antd-5-7-0-forked-c9jvmp
const onDownload = () => {
fetch(src)
.then((response) => response.blob())
.then((blob) => {
const url = URL.createObjectURL(new Blob([blob]));
const link = document.createElement("a");
link.href = url;
link.download = "image.png";
document.body.appendChild(link);
link.click();
URL.revokeObjectURL(url);
link.remove();
});
};
const imageUrl = src;
if (hasError) {
// If there is an error, display the fallback icon
return <CiImageOff />;
}
if (!imageUrl) {
return <CiImageOff />;
}
return (
<Image
width={45}
height={45}
src={imageUrl}
className="p-1 mb-1 columnImage"
onError={() => setHasError(true)} // Triggered when image fails to load
preview={{
toolbarRender: (
_,
@ -55,7 +44,6 @@ const ColumnsImage = ({ src }: any) => {
},
) => (
<Space size={12} className="toolbar-wrapper">
{/* <DownloadOutlined onClick={onDownload} /> */}
<SwapOutlined rotate={90} onClick={onFlipY} />
<SwapOutlined onClick={onFlipX} />
<RotateLeftOutlined onClick={onRotateLeft} />
@ -65,19 +53,8 @@ const ColumnsImage = ({ src }: any) => {
</Space>
),
}}
onError={handleError}
/>
);
};
export default ColumnsImage;
// {
// name: t("image"),
// center: "true",
// cell: (row: any) => {
// return (
// <ColumnsImage src={row?.image} />
// )
// }
// },

View File

@ -0,0 +1,39 @@
.ImageBoxField {
.ImageBox {
width: 120px;
height: 120px;
display: flex;
align-items: center;
justify-content: center;
border: max(1.5px, 0.1vw) dashed #a9c3f1;
margin-block: 10px;
border-radius: 5px;
z-index: 9999999 !important;
cursor: pointer;
.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,101 @@
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";
import { useTranslation } from "react-i18next";
// 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);
const [t] = useTranslation();
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) {
const maxSize = 2 * 1024 * 1024;
if (file.size > maxSize) {
alert(t("validation.File_size_exceeds_2_MB_limit."));
event.target.value = "";
return;
}
// Process the file
}
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, null);
if (fileInputRef.current) {
fileInputRef.current.value = "";
}
};
console.log(name);
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" onClick={handleButtonClick}>
{imagePreview ? (
<img src={imagePreview} alt="Preview" className="imagePreview" />
) : (
<ImageIcon 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,108 @@
import "./ImageBoxField.scss";
import { useState, useRef, useEffect, memo } from "react";
import ImageIcon from "./ImageIcon";
import ImageCancelIcon from "./ImageCancelIcon";
import { getNestedValue } from "../../../utils/getNestedValue";
import { generateImagePreview } from "./generateImagePreview";
import { useTranslation } from "react-i18next";
import { areFieldPropsEqual } from "../../../utils/areFieldPropsEqual";
// Helper function to generate image preview from a File
const ImageBoxFieldMemo = memo(
({ form, field }: any) => {
const { values, setFieldValue } = form;
const { name } = field;
const value = getNestedValue(values, name);
const [imagePreview, setImagePreview] = useState<string | null>(null);
const fileInputRef = useRef<HTMLInputElement | null>(null);
const [t] = useTranslation();
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) {
const maxSize = 2 * 1024 * 1024;
if (file.size > maxSize) {
alert(t("validation.File_size_exceeds_2_MB_limit."));
event.target.value = "";
return;
}
// Process the file
}
if (file) {
generateImagePreview(file, setImagePreview);
setFieldValue(name, file);
}
};
const handleButtonClick = () => {
const fileInput = fileInputRef.current;
if (fileInput) {
fileInput.click();
}
};
const handleCancel = () => {
setImagePreview("");
setFieldValue(name, null);
if (fileInputRef.current) {
fileInputRef.current.value = "";
}
};
console.log(name);
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" onClick={handleButtonClick}>
{imagePreview ? (
<img src={imagePreview} alt="Preview" className="imagePreview" />
) : (
<ImageIcon 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>
);
},
(prevProps, nextProps) => {
return areFieldPropsEqual(prevProps, nextProps);
},
);
export default ImageBoxFieldMemo;

View File

@ -0,0 +1,18 @@
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,18 @@
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,10 @@
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,17 @@
import React from "react";
import { BlockMath } from "react-katex";
import "katex/dist/katex.min.css";
const LatexPreview = ({ latex }: { latex: string }) => {
// console.log(latex);
// const sanitizedLatex = latex.replace(/\\_/g, '_');
return (
<div>
<BlockMath>{latex}</BlockMath>
</div>
);
};
export default LatexPreview;

View File

@ -1,7 +1,6 @@
import { Button, Image, Upload, UploadFile } from "antd";
import useFormField from "../../Hooks/useFormField";
import { UploadOutlined } from "@ant-design/icons";
import { ImageBaseURL } from "../../api/config";
import { useTranslation } from "react-i18next";
import { FaRegFilePdf } from "react-icons/fa";
import { useState } from "react";
@ -17,7 +16,7 @@ const PdfUploader = ({
}: any) => {
const { formik, t, isError } = useFormField(name, props);
let FormikName = formik.values[name];
const imageUrl = formik.values[name] ? ImageBaseURL + FormikName : "";
const imageUrl = formik.values[name] ? FormikName : "";
const [Imageurl, setImageurl] = useState(null);
const FilehandleChange = (value: any) => {

View File

@ -0,0 +1,108 @@
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 [NewAdditionalData, setNewAdditionalData] = useState({});
const formik = useFormikContext<any>();
const handleChange = (value: any, option: any) => {
console.log(option);
console.log(value);
const newSelectedOption = option?.pop();
console.log(newSelectedOption);
const newObject = {
id: newSelectedOption?.id,
name: newSelectedOption?.name,
};
setNewAdditionalData(newObject);
formik.setFieldValue("tags", value);
setSearchValue("");
setFieldValue("");
};
const handleSearch = useDebounce((value: string) => {
console.log(value, "value");
setSearchValue(value);
});
const handleFieldChange = (value: string) => {
setFieldValue(value);
};
const handleBlur = () => {
setSearchValue("");
setFieldValue("");
};
const { data, isLoading } = useGetAllTag({
name: searchValue,
});
const [t] = useTranslation();
const initialData =
formik?.values?.tags?.filter((item: any) => {
return item?.id;
}) ?? [];
const options = data?.data ?? [];
const additionalData =
options.length < 1 && searchValue.length > 1 && !isLoading
? [{ id: searchValue, name: searchValue }]
: [];
const value =
formik?.values?.tags?.map((item: any) => item?.id ?? item) ?? [];
const AllOptions = [
...options,
...additionalData,
NewAdditionalData,
...initialData,
];
const uniqueOptions = Array.from(
new Map(
AllOptions.filter((item) => Object.keys(item).length > 0) // Filter out empty objects
.map((item) => [item.id, item]), // Create [id, item] pairs to ensure uniqueness
).values(),
);
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={uniqueOptions}
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={value}
/>
</div>
);
};
export default SelectTag;

View File

@ -0,0 +1,85 @@
import React, { useState } 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 SelectTagV2: React.FC = () => {
const [searchValue, setSearchValue] = useState<string>("");
const [fieldValue, setFieldValue] = useState<string>("");
const formik = useFormikContext<any>();
// Fetch tags based on search value
const { data, isLoading } = useGetAllTag({ name: searchValue });
const { t } = useTranslation();
// Get selected tags from Formik
const CurrentTags = formik.values.tags ?? []; // Assuming tags are stored as array of objects
console.log(CurrentTags, "CurrentTags");
const NewShapeTags = CurrentTags?.map((item: any) => {
return item?.name ?? item;
});
const handleChange = (_value: any[], option: any) => {
// console.log(option,"option");
console.log(_value);
// const NewShapeOption = option?.map((item:any)=> {return ({name:item?.name,id:item?.id})})
// console.log(NewShapeOption);
// formik.setFieldValue("tags", NewShapeOption);
// setSearchValue("");
// setFieldValue("");
};
const handleSearch = useDebounce((value: string) => {
setSearchValue(value);
});
const handleBlur = () => {
setSearchValue("");
setFieldValue("");
};
const options = data?.data ?? [];
const additionalData =
options.length < 1 && searchValue.length >= 1 && !isLoading
? [{ id: searchValue, name: searchValue }]
: options;
console.log(additionalData);
return (
<div className="SelectTag">
<label>{t("models.tag")}</label>
<Select
mode="multiple"
allowClear
style={{ width: "100%", height: "40px" }}
placeholder=""
fieldNames={{ label: "name", value: "name" }}
onChange={handleChange}
options={additionalData}
filterOption={false}
loading={isLoading}
notFoundContent={isLoading ? <Spin /> : t("practical.not_found")}
onSearch={(value) => {
handleSearch(value);
setFieldValue(value);
}}
searchValue={fieldValue}
onDropdownVisibleChange={(open) => {
if (!open) {
handleBlur();
}
}}
value={NewShapeTags}
/>
</div>
);
};
export default SelectTagV2;

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

@ -1,7 +1,8 @@
import React, { useState, useEffect, useRef } from "react";
import { useTranslation } from "react-i18next";
import { IoSearch } from "react-icons/io5";
import { useLocation, useNavigate } from "react-router-dom";
import { useFilterStateState } from "../../zustand/Filter";
import { useDebounce } from "../../utils/useDebounce";
import { PaginationParams } from "../../api/utils/PaginationParams";
interface Props {
placeholder: string;
@ -11,36 +12,23 @@ interface Props {
const SearchField: React.FC<Props> = ({ placeholder, searchBy }) => {
const [isOpen, setIsOpen] = useState(false);
const [searchQuery, setSearchQuery] = useState<string>("");
const location = useLocation();
const navigate = useNavigate();
const inputRef = useRef<HTMLInputElement>(null);
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
const DEBOUNCE_DELAY = 500; // Adjust the debounce delay as needed
useEffect(() => {
const searchParams = new URLSearchParams(location.search);
setSearchQuery(searchParams.get(searchBy) || "");
}, [location.search, searchBy]);
const { setFilter, Filter } = useFilterStateState();
const { page } = PaginationParams(location);
const handleInputChange = (value: string) => {
setSearchQuery(value);
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
timeoutRef.current = setTimeout(() => {
if (value.trim() !== "") {
navigate(`${location.pathname}?${searchBy}=${value.trim()}`);
} else {
const params = new URLSearchParams(location.search);
params.delete(searchBy);
navigate(`${location.pathname}`);
}
}, DEBOUNCE_DELAY);
};
const handleInputChangeWithDebounce = useDebounce((value: string) => {
if (Number(page) > 1) {
}
setFilter({
[searchBy]: value,
});
});
const handleToggleDropdown = () => {
setIsOpen(!isOpen);
};
@ -62,14 +50,17 @@ 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"
className="search__input"
placeholder={t(placeholder)}
value={searchQuery}
onChange={(e) => handleInputChange(e.target.value)}
onChange={(e) => {
handleInputChange(e.target.value);
handleInputChangeWithDebounce(e.target.value);
}}
/>
</div>
</div>

View File

@ -1,6 +1,7 @@
import React, { useState, useEffect, useRef } from "react";
import { IoSearch } from "react-icons/io5";
// import { IoSearch } from "react-icons/io5";
import { useNavigate } from "react-router-dom";
import { LuSearch } from "react-icons/lu";
interface Option {
label: string;
@ -11,12 +12,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);
@ -42,6 +45,7 @@ const SearchFieldWithSelect: React.FC<Props> = ({
const toggleDropdown = () => {
setIsOpen(!isOpen);
};
const navigate = useNavigate();
const handleOptionClick = (option: Option) => {
setSelectedOption(option);
@ -54,35 +58,37 @@ const SearchFieldWithSelect: React.FC<Props> = ({
const filteredOptions = options.filter((option) =>
option.label.toLowerCase().includes(searchTerm.toLowerCase()),
);
const Type = localStorage.getItem("type");
return (
<div ref={node} className={`search-field ${isOpen ? "open" : ""}`}>
<div className="search-header" onClick={toggleDropdown}>
<IoSearch className="search__icon" />
{withIcon && <LuSearch 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">
<div className="search-options-list">
{filteredOptions.map((option) => (
{filteredOptions.map((option) => {
console.log(option);
return (
<div
key={option.value}
className="search-option"
onClick={() => handleOptionClick(option)}
>
<div>{option.label}</div>
<IoSearch className="search__icon" />
{/* {withIcon && <IoSearch className="search__icon" />} */}
</div>
))}
);
})}
</div>
</div>
)}

View File

@ -1,54 +1,45 @@
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Divider, Select } from 'antd';
import SearchFieldWithSelect from '../../Components/DataTable/SearchFieldWithSelect';
import { translateOptions } from '../../utils/translatedOptions';
import { search_array } from '../../Routes';
import { useFilterStateState } from '../../zustand/Filter';
import { useSearchParams } from 'react-router-dom';
import { useTranslation } from "react-i18next";
import { Select } from "antd";
import { TbReorder } from "react-icons/tb";
import { useFilterStateState } from "../../zustand/Filter";
const OrderBySelect = () => {
const { t } = useTranslation();
const { Filter, setFilter } = useFilterStateState();
const [searchParams, setSearchParams] = useSearchParams();
const type_param = searchParams.get('type');
const [type, setType] = useState(type_param);
const translateArray = translateOptions(search_array, t);
const { setFilter } = useFilterStateState();
const handleChange = (value: string) => {
const newArray = Filter?.filter((item: any) => item.select !== true);
setFilter([
...newArray,
{ name: value, index: Filter.length, select: true },
]);
if (type_param) {
searchParams.delete('type');
setSearchParams(searchParams);
}
setType(value);
setFilter({
sort_by: value,
});
};
// send this with api request
// type: type,
// page: currentPage,
return (
<div className='order_by_filter'>
<div className="order_by_filter">
<Select
className='order_by_select'
className="order_by_select"
style={{ width: 200 }}
size="large"
placeholder={<div><TbReorder className='addition_select_icon'/> {t("ترتيب حسب")} </div>}
allowClear
placeholder={
<div>
<TbReorder className="addition_select_icon" /> {t("header.sort_by")}{" "}
</div>
}
onChange={handleChange}
options={[
{ value: "تصاعديا", label: t("تصاعديا") },
{ value: "تنازليا", label: t("تنازليا") },
{ value: "شوهدت مؤخرا", label: t("شوهدت مؤخرا") },
{ value: "وصلت مؤخرا", label: t("وصلت مؤخرا") },
{ value: "ascending", label: t("select.array.order.ascending") },
{ value: "descending", label: t("select.array.order.descending") },
// {
// value: "recently_viewed",
// label: t("select.array.order.recently_viewed"),
// },
// {
// value: "recently_arrived",
// label: t("select.array.order.recently_arrived"),
// },
]}
/>
</div>
)
}
);
};
export default OrderBySelect
export default OrderBySelect;

View File

@ -1,41 +1,27 @@
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Divider, Select } from 'antd';
import SearchFieldWithSelect from '../../Components/DataTable/SearchFieldWithSelect';
import { translateOptions } from '../../utils/translatedOptions';
import { search_array } from '../../Routes';
import { useFilterStateState } from '../../zustand/Filter';
import { useSearchParams } from 'react-router-dom';
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { Divider, Select } from "antd";
import usePagination from "../../Layout/Dashboard/usePagination";
import { useNavigate } from "react-router-dom";
import { useFilterStateState } from "../../zustand/Filter";
const PaginationColumn = () => {
const { t } = useTranslation();
const navigate = useNavigate();
const { Filter, setFilter } = useFilterStateState();
const [searchParams, setSearchParams] = useSearchParams();
const type_param = searchParams.get('type');
const [type, setType] = useState(type_param);
const translateArray = translateOptions(search_array, t);
const handleChange = (value: string) => {
const newArray = Filter?.filter((item: any) => item.select !== true);
setFilter([
...newArray,
{ name: value, index: Filter.length, select: true },
]);
if (type_param) {
searchParams.delete('type');
setSearchParams(searchParams);
}
setType(value);
navigate(`?per_page=${value}`);
setFilter({
per_page: value,
});
};
// send this with api request
// type: type,
// page: currentPage,
return (
<div className='pagination_column'>
<div className="pagination_column">
<Select
className='pagination_select'
className="pagination_select"
style={{ width: 70 }}
size="large"
defaultValue={"10"}
@ -49,7 +35,7 @@ const PaginationColumn = () => {
]}
/>
</div>
)
}
);
};
export default PaginationColumn
export default PaginationColumn;

View File

@ -0,0 +1,71 @@
import React, { useCallback, useState } from "react";
import "../styles/index.scss";
import CustomInput from "../design-system/CustomInput";
import { Button } from "antd";
import { useTranslation } from "react-i18next";
interface IFilterBody {
children: React.ReactNode;
}
const useFilter = () => {
const [isBodyVisible, setIsBodyVisible] = useState(true);
const toggleBodyVisibility = () => {
setIsBodyVisible((prev) => !prev);
};
const FilterButton = () => {
return (
<button onClick={toggleBodyVisibility}>
{isBodyVisible ? "Hide Filter" : "Show Filter"}
</button>
);
};
const FilterBody = ({ children }: IFilterBody) => {
const [values, setValues] = useState({ name1: "", name2: "" });
const handleChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setValues((prev) => ({ ...prev, [name]: value }));
},
[],
);
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
console.log(values, "values");
};
const [t] = useTranslation();
return (
<div className={`filter_body ${isBodyVisible ? "visible" : "hidden"}`}>
<form onSubmit={handleSubmit}>
{children}
<CustomInput
name="name1"
value={values.name1}
onChange={handleChange}
/>
<CustomInput
name="name2"
value={values.name2}
onChange={handleChange}
/>
<Button block htmlType="submit" type="primary">
{" "}
{t("practical.submit")}{" "}
</Button>
</form>
</div>
);
};
return {
FilterButton,
FilterBody,
};
};
export default useFilter;

View File

@ -0,0 +1,17 @@
import { Input } from "antd";
import React from "react";
interface CustomInputProps {
name: string;
value: string;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
}
const CustomInput: React.FC<CustomInputProps> = React.memo(
({ name, value, onChange }) => {
console.log(`Rendering ${name}`); // For debugging purposes
return <Input type="text" name={name} value={value} onChange={onChange} />;
},
);
export default CustomInput;

View File

@ -0,0 +1,3 @@
export enum FilterEnum {
FILTER = "FILTER",
}

View File

@ -0,0 +1,22 @@
import { create } from "zustand";
interface FilterState {
filterState: any[];
setFilterState: (data: any) => void;
clearFilterState: () => void;
setWithOldValue: (data: any) => void;
setInitialValue: (data: any) => void;
}
export const useFilterState = create<FilterState>((set, get) => ({
filterState: [],
setFilterState: (data) => set(() => ({ filterState: data })),
clearFilterState: () => set(() => ({ filterState: [] })),
setWithOldValue: (data) =>
set((state) => ({ filterState: [...state.filterState, data] })),
setInitialValue: (data) => {
if (get().filterState.length < 1) {
set(() => ({ filterState: data }));
}
},
}));

View File

@ -0,0 +1,34 @@
.filter_body {
max-height: 0;
overflow: hidden;
transition:
max-height 0.3s ease-out,
opacity 0.3s ease-out,
transform 0.3s ease-out;
opacity: 0;
transform: translateY(-20px);
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.filter_body.visible {
max-height: 200px;
opacity: 1;
transform: translateY(0);
}
.filter_body.hidden {
max-height: 0;
opacity: 0;
transform: translateY(-20px);
}
.DummyHomePage {
display: flex;
flex-direction: column;
align-items: center;
gap: 40px;
width: 70%;
padding: 50px;
}

View File

@ -0,0 +1,89 @@
import { Modal } from "antd";
import TextArea from "antd/es/input/TextArea";
import { useFormikContext } from "formik";
import React, { useState } from "react";
import { convertMathMLToLaTeX } from "../../utils/convertMathMLToLaTeX";
import { useTranslation } from "react-i18next";
import { toast } from "react-toastify";
const AddLaTexModal = ({
name,
setLatex,
Latex,
setIsModalOpen,
isModalOpen,
setCurrentValue,
}: {
name: string;
setLatex: (value: string) => void;
Latex: string;
setIsModalOpen: (value: boolean) => void;
isModalOpen: boolean;
setCurrentValue: (value: string) => void;
}) => {
const { values, setFieldValue, getFieldProps } = useFormikContext<any>();
const currentValue = getFieldProps(name).value;
const handleOk = () => {
const oldValue = currentValue ?? "";
const newLatex = convertMathMLToLaTeX(Latex);
console.log(oldValue);
if (newLatex) {
setFieldValue(name, oldValue + " $$ " + newLatex + " $$ ");
setCurrentValue(oldValue + " $$ " + newLatex + " $$ ");
setLatex("");
setIsModalOpen(false);
} else {
setFieldValue(name, oldValue + " $$ " + Latex + " $$ ");
setCurrentValue(oldValue + " $$ " + Latex + " $$ ");
setLatex("");
setIsModalOpen(false);
}
};
const handleCancel = () => {
setIsModalOpen(false);
setLatex("");
};
const handleChangeInputLatex = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
) => {
const newValue = e.target.value;
setLatex(newValue);
};
const [t] = useTranslation();
return (
<Modal
footer={false}
open={isModalOpen}
onOk={handleOk}
onCancel={handleCancel}
>
<div className="latexModal">
<label className="mb-3"> {t("header.past_your_MMl_text")} </label>
<TextArea
size="large"
showCount
maxLength={5000}
autoSize={{ minRows: 4, maxRows: 10 }}
style={{ height: "400px" }}
onChange={handleChangeInputLatex}
value={Latex}
/>
<div className="buttons">
<div className="back_button pointer" onClick={handleCancel}>
{t("practical.cancel")}
</div>
<div className="add_button" onClick={handleOk}>
{t(`practical.${"add"}`)}
</div>
</div>
</div>
</Modal>
);
};
export default AddLaTexModal;

View File

@ -0,0 +1,91 @@
import { Modal } from "antd";
import TextArea from "antd/es/input/TextArea";
import { useFormikContext } from "formik";
import React, { useState } from "react";
import { convertMathMLToLaTeX } from "../../utils/convertMathMLToLaTeX";
import { useTranslation } from "react-i18next";
import { toast } from "react-toastify";
import { parseTextAndLatex } from "../../utils/parseTextAndLatex";
const EditLaTexModal = ({
name,
setLatex,
Latex,
setIsModalOpen,
isModalOpen,
}: {
name: string;
setLatex: (value: string) => void;
Latex: any;
setIsModalOpen: (value: boolean) => void;
isModalOpen: boolean;
}) => {
const { values } = useFormikContext<any>();
const [Value, setValue] = useState(Latex?.text ?? Latex);
const handleOk = () => {
console.log(1);
const oldValue = values?.[name];
const currentKey = Latex?.key;
const Preview = parseTextAndLatex(oldValue ?? "");
console.log(Latex);
const newLatex = convertMathMLToLaTeX(Latex);
console.log(newLatex);
if (newLatex) {
const newArray = Preview?.map((item: any, index: number) => {
if (item?.key) return item;
});
} else {
toast.error(t("validation.that_is_not_a_valid_mml"));
}
};
const handleCancel = () => {
setIsModalOpen(false);
setLatex("");
};
const handleChangeInputLatex = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
) => {
const newValue = e.target.value;
console.log(newValue, "newValue");
setValue(newValue);
};
const [t] = useTranslation();
return (
<Modal
footer={false}
open={isModalOpen}
onOk={handleOk}
onCancel={handleCancel}
>
<div className="latexModal">
<label className="mb-3"> {t("header.past_your_MMl_text")} </label>
<TextArea
size="large"
showCount
maxLength={1000}
autoSize={{ minRows: 4, maxRows: 10 }}
style={{ height: "400px" }}
onChange={handleChangeInputLatex}
value={Value}
/>
<div className="buttons">
<div className="back_button pointer" onClick={handleCancel}>
{t("practical.cancel")}
</div>
<div className="add_button" onClick={handleOk}>
{t(`practical.${"edit"}`)}
</div>
</div>
</div>
</Modal>
);
};
export default EditLaTexModal;

View File

@ -0,0 +1,185 @@
import TextArea from "antd/es/input/TextArea";
import React, { Suspense, useEffect, useState } from "react";
import { parseTextAndLatex } from "../../utils/parseTextAndLatex";
import LatexPreview from "../CustomFields/MathComponent";
import { Checkbox } from "antd";
import { CheckboxProps } from "antd/lib";
import { useTranslation } from "react-i18next";
import { FaPlus } from "react-icons/fa";
import { useObjectToEdit } from "../../zustand/ObjectToEditState";
import SpinContainer from "../Layout/SpinContainer";
import { areFieldPropsEqual } from "./areFieldPropsEqual";
const AddLazyModal = React.lazy(() => import("./AddLaTexModal"));
const EditLazyModal = React.lazy(() => import("./EditLaTexModal"));
const LaTeXInputMemo: React.FC<any> = React.memo(
({ field, form, label, ...props }) => {
const { name, value } = field;
const { setFieldValue, touched, errors, getFieldProps, values } = form;
const { ShowLatexOption, Success } = useObjectToEdit();
const [showPreview, setShowPreview] = useState(false);
const Preview = parseTextAndLatex(value ?? "");
const onPreviewChange: CheckboxProps["onChange"] = (e) => {
setShowPreview(e.target.checked);
};
const [t] = useTranslation();
const [isModalOpen, setIsModalOpen] = useState(false);
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
const [Latex, setLatex] = useState<string>("");
const showModal = () => {
setIsModalOpen(true);
};
const handleEditModal = (item: any) => {
// console.log(item);
// setLatex(item);
// setIsEditModalOpen(true);
};
const [curCentValue, setCurrentValue] = useState(value);
const handleChangeInput = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
) => {
setCurrentValue(e.target.value);
};
const onBlur = () => {
if (curCentValue !== value) {
setFieldValue(name, curCentValue);
}
};
useEffect(() => {
if (Success) {
setCurrentValue(null);
}
}, [Success]);
useEffect(() => {
if (value) {
setCurrentValue(value);
}
}, [value]);
const isError = !!touched?.[name] && !!errors?.[name];
const errorMessage = isError ? ((errors?.[name] as string) ?? "") : "";
console.log(values);
let metaName = name.substring(0, name.lastIndexOf("."));
if (metaName.includes(".") || metaName.includes("[")) {
metaName += ".meta";
} else {
metaName += "meta";
}
const meta = getFieldProps(metaName).value;
console.log(metaName, meta);
const direction = meta?.direction === "ltr" ? "ltr" : "rtl";
const [Dir, setDir] = useState<"ltr" | "rtl">(direction);
const handleChangeDirection = () => {
if (Dir === "ltr") {
setDir("rtl");
setFieldValue(metaName, { ...(meta ?? {}), direction: "rtl" });
} else {
setDir("ltr");
setFieldValue(metaName, { ...(meta ?? {}), direction: "ltr" });
}
};
return (
<div className="LaTeXInput">
<label htmlFor={name} className="text">
<div>{t(label || name)}</div>{" "}
<div className="error_message">{t(errorMessage)}</div>
</label>
<div className="LaTeXInputArea">
<TextArea
size="large"
showCount
maxLength={5000}
autoSize={{ minRows: 6, maxRows: 10 }}
style={{ height: "400px" }}
onChange={handleChangeInput}
value={curCentValue}
onBlur={onBlur}
dir={Dir}
{...props}
/>
<div className="LaTeXInputOptions">
<Checkbox
onChange={handleChangeDirection}
checked={direction === "ltr"}
>
{t("header.change_direction")}
</Checkbox>
{ShowLatexOption && (
<>
<Checkbox onChange={onPreviewChange}>
{t("header.show_preview")}
</Checkbox>
<button type="button" className="addMML" onClick={showModal}>
<FaPlus /> {t("MML")}
</button>
</>
)}
</div>
{showPreview && (
<div className="showPreviewInput">
{Preview?.map((item: any, index: number) => {
if (item?.isLatex) {
console.log(item?.text);
return (
<span
dir="ltr"
key={index}
onClick={() => handleEditModal(item)}
className="LatexPreview"
>
<LatexPreview latex={item?.text} />
</span>
);
}
return <div key={index}>{item?.text}</div>;
})}
</div>
)}
</div>
<Suspense fallback={<SpinContainer />}>
<AddLazyModal
name={name}
Latex={Latex}
isModalOpen={isModalOpen}
setIsModalOpen={setIsModalOpen}
setLatex={setLatex}
setCurrentValue={setCurrentValue}
/>
<EditLazyModal
name={name}
Latex={Latex}
isModalOpen={isEditModalOpen}
setIsModalOpen={setIsEditModalOpen}
setLatex={setLatex}
/>
</Suspense>
</div>
);
},
(prevProps, nextProps) => {
return areFieldPropsEqual(prevProps, nextProps);
},
);
export default LaTeXInputMemo;

View File

@ -0,0 +1,15 @@
// utilityFunctions.ts
import { FieldProps } from "formik";
export const areFieldPropsEqual = (prevProps: any, nextProps: any): boolean => {
const prevError = prevProps.form.errors[prevProps.field.name];
const nextError = nextProps.form.errors[nextProps.field.name];
const prevTouched = prevProps.form.touched[prevProps.field.name];
const nextTouched = nextProps.form.touched[nextProps.field.name];
const prevValue = prevProps.field.value;
const nextValue = nextProps.field.value;
return false;
};

View File

@ -14,30 +14,52 @@ import { IoIosNotificationsOutline } from "react-icons/io";
import { CiCirclePlus } from "react-icons/ci";
import { TbWorld } from "react-icons/tb";
import TooltipComp from "./Tooltip";
import { useNavigate } from "react-router-dom";
const NavBarRightSide = () => {
const userData = getLocalStorage(USER_KEY);
const [t] = useTranslation();
const Navigate = useNavigate();
// const translateArray = translateOptions(search_array, t);
const { handel_open_model } = useModalHandler();
const handleEdit = () => {
handel_open_model(ModalEnum.CHANGE_PASSWORD);
};
return (
<article>
<span className="header_icons">
<TooltipComp note="change_language" color="#E0E0E0" icon={<TbWorld size={25}/>}/>
<TooltipComp note="add" color="#E0E0E0" icon={<CiCirclePlus size={25}/>}/>
<TooltipComp className="NotificationsIcon" note="notification" color="#E0E0E0" icon={< > <IoIosNotificationsOutline size={25} /> </>}/>
<TooltipComp
note="change_language"
color="#E0E0E0"
icon={<TbWorld size={25} />}
/>
<TooltipComp
note="add"
color="#E0E0E0"
icon={<CiCirclePlus size={25} />}
/>
<TooltipComp
onClick={() => Navigate("/notifications")}
className="NotificationsIcon"
note="notification"
color="#E0E0E0"
icon={
<>
{" "}
<IoIosNotificationsOutline size={25} />{" "}
</>
}
/>
</span>
<div className="header_profile">
{/* <span>
<h6>{userData?.username}</h6>
<p>{userData?.type}</p>
</span> */}
<Image src="/Image/faker_user.png" alt="Profile" />
<Image
// onClick={()=>(Navigate('/profile'))}
src="/Image/faker_user.png"
alt="Profile"
/>
</div>
</article>
);

View File

@ -1,35 +1,40 @@
import React from 'react'
import React from "react";
import { Tooltip } from "antd";
import { ModalEnum } from '../../../enums/Model';
import { useTranslation } from 'react-i18next';
import { CiCirclePlus } from 'react-icons/ci';
import useModalHandler from '../../../utils/useModalHandler';
const TooltipComp = ({note,color,icon,className=""}:{note:string,color:string,icon:any,className?:string}) => {
import { ModalEnum } from "../../../enums/Model";
import { useTranslation } from "react-i18next";
import { CiCirclePlus } from "react-icons/ci";
import useModalHandler from "../../../utils/useModalHandler";
const TooltipComp = ({
note,
color,
icon,
className = "",
onClick,
}: {
note: string;
color: string;
icon: any;
className?: string;
onClick?: () => void;
}) => {
const [t] = useTranslation();
const { handel_open_model } = useModalHandler();
const handleEdit = () => {
handel_open_model(ModalEnum.CHANGE_PASSWORD);
// handel_open_model(ModalEnum.CHANGE_PASSWORD);
};
return (
<div className={className}>
<div className={className} onClick={onClick}>
<Tooltip
placement="top"
title={
<div onClick={handleEdit}>
{t(`header.${note}`)}
</div>
}
title={<div onClick={handleEdit}>{t(`header.${note}`)}</div>}
color={color}
>
<div className={`gear `}>
{icon}
</div>
<div className={`gear `}>{icon}</div>
</Tooltip>
</div>
)
}
);
};
export default TooltipComp
export default TooltipComp;

View File

@ -2,6 +2,8 @@ import { useState } from "react";
import { useTranslation } from "react-i18next";
import { MdExpandLess, MdExpandMore } from "react-icons/md";
import { Link, useNavigate } from "react-router-dom";
import { useFilterStateState } from "../../../zustand/Filter";
import { Tooltip } from "antd";
export const MenuItem = ({ item, location, index, isOpen }: any) => {
const isActive = location.pathname.split("/")[1] === item.path?.slice(1);
@ -12,27 +14,40 @@ export const MenuItem = ({ item, location, index, isOpen }: any) => {
const isDropdownOpen = openDropdown === index;
const [t] = useTranslation();
const navigate = useNavigate();
const { setFilter } = useFilterStateState();
const handleNavigate = () => {
setFilter({});
navigate(item.path || "/");
};
return (
<>
<div
className={`link ${isActive ? "active" : ""} ${item?.children && "DropDownLink"}`}
onClick={() => navigate(item.path || "/")}
onClick={() => handleNavigate()}
>
<Tooltip placement="topLeft" title={t(item?.text)} color={"#E0E0E0"}>
<i>{item.icon}</i>
</Tooltip>
{/* Conditionally render the text based on sidebar width */}
<span style={{ display: isOpen === false ? 'none' : 'inline' }}>
<span style={{ display: isOpen === false ? "none" : "inline" }}>
{t(item.text)}
</span>
{item?.children && (
<>
{isDropdownOpen ? (
<div className="DropDownIcon" onClick={() => handleDropdown(index)}>
<MdExpandLess />
<div
className="DropDownIcon"
onClick={() => handleDropdown(index)}
>
<MdExpandLess className="sidebar_menu_icon" />
</div>
) : (
<div className="DropDownIcon" onClick={() => handleDropdown(index)}>
<MdExpandMore />
<div
className="DropDownIcon"
onClick={() => handleDropdown(index)}
>
<MdExpandMore className="sidebar_menu_icon" />
</div>
)}
</>

View File

@ -0,0 +1,980 @@
{
"v": "4.6.8",
"fr": 29.9700012207031,
"ip": 0,
"op": 69.0000028104276,
"w": 256,
"h": 256,
"nm": "Comp 1",
"ddd": 0,
"assets": [],
"layers": [
{
"ddd": 0,
"ind": 1,
"ty": 4,
"nm": "Glow ball",
"ks": {
"o": { "a": 0, "k": 100 },
"r": {
"a": 1,
"k": [
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"n": ["0p833_0p833_0p167_0p167"],
"t": 0,
"s": [0],
"e": [360]
},
{ "t": 69.0000028104276 }
]
},
"p": { "a": 0, "k": [127.984, 127.969, 0] },
"a": { "a": 0, "k": [-0.182, 32.385, 0] },
"s": { "a": 0, "k": [132, 132, 100] }
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"d": 1,
"ty": "el",
"s": { "a": 0, "k": [14.125, 14.125] },
"p": { "a": 0, "k": [0, 0] },
"nm": "Ellipse Path 1",
"mn": "ADBE Vector Shape - Ellipse"
},
{
"ty": "fl",
"c": { "a": 0, "k": [0.1635217, 0.8509804, 0.8105415, 1] },
"o": { "a": 0, "k": 100 },
"r": 1,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill"
},
{
"ty": "tr",
"p": { "a": 0, "k": [-0.063, 1.5], "ix": 2 },
"a": { "a": 0, "k": [0, 0], "ix": 1 },
"s": { "a": 0, "k": [100, 100], "ix": 3 },
"r": { "a": 0, "k": 0, "ix": 6 },
"o": { "a": 0, "k": 100, "ix": 7 },
"sk": { "a": 0, "k": 0, "ix": 4 },
"sa": { "a": 0, "k": 0, "ix": 5 },
"nm": "Transform"
}
],
"nm": "Ellipse 1",
"np": 3,
"cix": 2,
"ix": 1,
"mn": "ADBE Vector Group"
}
],
"ip": 0,
"op": 300.00001221925,
"st": 0,
"bm": 0,
"sr": 1
},
{
"ddd": 0,
"ind": 2,
"ty": 4,
"nm": "Shape Layer 8",
"ks": {
"o": { "a": 0, "k": 100 },
"r": { "a": 0, "k": 315 },
"p": { "a": 0, "k": [127.984, 127.969, 0] },
"a": { "a": 0, "k": [-0.182, 32.385, 0] },
"s": {
"a": 1,
"k": [
{
"i": { "x": [0.833, 0.833, 0.833], "y": [0.833, 0.833, 0.833] },
"o": { "x": [0.167, 0.167, 0.167], "y": [0.167, 0.167, 0.167] },
"n": [
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167"
],
"t": 56,
"s": [132, 132, 100],
"e": [145, 145, 100]
},
{
"i": { "x": [0.833, 0.833, 0.833], "y": [0.833, 0.833, 0.833] },
"o": { "x": [0.167, 0.167, 0.167], "y": [0.167, 0.167, 0.167] },
"n": [
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167"
],
"t": 59,
"s": [145, 145, 100],
"e": [132, 132, 100]
},
{ "t": 63.0000025660426 }
]
}
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"d": 1,
"ty": "el",
"s": { "a": 0, "k": [14.125, 14.125] },
"p": { "a": 0, "k": [0, 0] },
"nm": "Ellipse Path 1",
"mn": "ADBE Vector Shape - Ellipse"
},
{
"ty": "fl",
"c": {
"a": 1,
"k": [
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"n": ["0p833_0p833_0p167_0p167"],
"t": 0,
"s": [0.1647059, 0.6313726, 0.8509804, 1],
"e": [0.1647059, 0.6313726, 0.8509804, 1]
},
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"n": ["0p833_0p833_0p167_0p167"],
"t": 56,
"s": [0.1647059, 0.6313726, 0.8509804, 1],
"e": [0.1647059, 0.8509804, 0.8117647, 1]
},
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"n": ["0p833_0p833_0p167_0p167"],
"t": 62,
"s": [0.1647059, 0.8509804, 0.8117647, 1],
"e": [0.1647059, 0.6313726, 0.8509804, 1]
},
{ "t": 69.0000028104276 }
]
},
"o": { "a": 0, "k": 100 },
"r": 1,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill"
},
{
"ty": "tr",
"p": { "a": 0, "k": [-0.063, 1.5], "ix": 2 },
"a": { "a": 0, "k": [0, 0], "ix": 1 },
"s": { "a": 0, "k": [100, 100], "ix": 3 },
"r": { "a": 0, "k": 0, "ix": 6 },
"o": { "a": 0, "k": 100, "ix": 7 },
"sk": { "a": 0, "k": 0, "ix": 4 },
"sa": { "a": 0, "k": 0, "ix": 5 },
"nm": "Transform"
}
],
"nm": "Ellipse 1",
"np": 3,
"cix": 2,
"ix": 1,
"mn": "ADBE Vector Group"
}
],
"ip": 0,
"op": 300.00001221925,
"st": 0,
"bm": 0,
"sr": 1
},
{
"ddd": 0,
"ind": 3,
"ty": 4,
"nm": "Shape Layer 7",
"ks": {
"o": { "a": 0, "k": 100 },
"r": { "a": 0, "k": 270 },
"p": { "a": 0, "k": [127.984, 127.969, 0] },
"a": { "a": 0, "k": [-0.182, 32.385, 0] },
"s": {
"a": 1,
"k": [
{
"i": { "x": [0.833, 0.833, 0.833], "y": [0.833, 0.833, 0.833] },
"o": { "x": [0.167, 0.167, 0.167], "y": [0.167, 0.167, 0.167] },
"n": [
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167"
],
"t": 48,
"s": [132, 132, 100],
"e": [145, 145, 100]
},
{
"i": { "x": [0.833, 0.833, 0.833], "y": [0.833, 0.833, 0.833] },
"o": { "x": [0.167, 0.167, 0.167], "y": [0.167, 0.167, 0.167] },
"n": [
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167"
],
"t": 51,
"s": [145, 145, 100],
"e": [132, 132, 100]
},
{ "t": 55.0000022401959 }
]
}
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"d": 1,
"ty": "el",
"s": { "a": 0, "k": [14.125, 14.125] },
"p": { "a": 0, "k": [0, 0] },
"nm": "Ellipse Path 1",
"mn": "ADBE Vector Shape - Ellipse"
},
{
"ty": "fl",
"c": {
"a": 1,
"k": [
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"n": ["0p833_0p833_0p167_0p167"],
"t": 0,
"s": [0.1647059, 0.6313726, 0.8509804, 1],
"e": [0.1647059, 0.6745098, 0.8431373, 1]
},
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"n": ["0p833_0p833_0p167_0p167"],
"t": 47,
"s": [0.1647059, 0.6745098, 0.8431373, 1],
"e": [0.1647059, 0.8509804, 0.8117647, 1]
},
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"n": ["0p833_0p833_0p167_0p167"],
"t": 58,
"s": [0.1647059, 0.8509804, 0.8117647, 1],
"e": [0.1647059, 0.6313726, 0.8509804, 1]
},
{ "t": 69.0000028104276 }
]
},
"o": { "a": 0, "k": 100 },
"r": 1,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill"
},
{
"ty": "tr",
"p": { "a": 0, "k": [-0.063, 1.5], "ix": 2 },
"a": { "a": 0, "k": [0, 0], "ix": 1 },
"s": { "a": 0, "k": [100, 100], "ix": 3 },
"r": { "a": 0, "k": 0, "ix": 6 },
"o": { "a": 0, "k": 100, "ix": 7 },
"sk": { "a": 0, "k": 0, "ix": 4 },
"sa": { "a": 0, "k": 0, "ix": 5 },
"nm": "Transform"
}
],
"nm": "Ellipse 1",
"np": 3,
"cix": 2,
"ix": 1,
"mn": "ADBE Vector Group"
}
],
"ip": 0,
"op": 300.00001221925,
"st": 0,
"bm": 0,
"sr": 1
},
{
"ddd": 0,
"ind": 4,
"ty": 4,
"nm": "Shape Layer 6",
"ks": {
"o": { "a": 0, "k": 100 },
"r": { "a": 0, "k": 225 },
"p": { "a": 0, "k": [127.984, 127.969, 0] },
"a": { "a": 0, "k": [-0.182, 32.385, 0] },
"s": {
"a": 1,
"k": [
{
"i": { "x": [0.833, 0.833, 0.833], "y": [0.833, 0.833, 0.833] },
"o": { "x": [0.167, 0.167, 0.167], "y": [0.167, 0.167, 0.167] },
"n": [
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167"
],
"t": 39,
"s": [132, 132, 100],
"e": [145, 145, 100]
},
{
"i": { "x": [0.833, 0.833, 0.833], "y": [0.833, 0.833, 0.833] },
"o": { "x": [0.167, 0.167, 0.167], "y": [0.167, 0.167, 0.167] },
"n": [
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167"
],
"t": 42,
"s": [145, 145, 100],
"e": [132, 132, 100]
},
{ "t": 46.0000018736184 }
]
}
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"d": 1,
"ty": "el",
"s": { "a": 0, "k": [14.125, 14.125] },
"p": { "a": 0, "k": [0, 0] },
"nm": "Ellipse Path 1",
"mn": "ADBE Vector Shape - Ellipse"
},
{
"ty": "fl",
"c": {
"a": 1,
"k": [
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"n": ["0p833_0p833_0p167_0p167"],
"t": 0,
"s": [0.1647059, 0.6313726, 0.8509804, 1],
"e": [0.1647059, 0.6313726, 0.8509804, 1]
},
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"n": ["0p833_0p833_0p167_0p167"],
"t": 37,
"s": [0.1647059, 0.6313726, 0.8509804, 1],
"e": [0.1647059, 0.8509804, 0.8117647, 1]
},
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"n": ["0p833_0p833_0p167_0p167"],
"t": 43,
"s": [0.1647059, 0.8509804, 0.8117647, 1],
"e": [0.1647059, 0.6313726, 0.8509804, 1]
},
{ "t": 48.0000019550801 }
]
},
"o": { "a": 0, "k": 100 },
"r": 1,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill"
},
{
"ty": "tr",
"p": { "a": 0, "k": [-0.063, 1.5], "ix": 2 },
"a": { "a": 0, "k": [0, 0], "ix": 1 },
"s": { "a": 0, "k": [100, 100], "ix": 3 },
"r": { "a": 0, "k": 0, "ix": 6 },
"o": { "a": 0, "k": 100, "ix": 7 },
"sk": { "a": 0, "k": 0, "ix": 4 },
"sa": { "a": 0, "k": 0, "ix": 5 },
"nm": "Transform"
}
],
"nm": "Ellipse 1",
"np": 3,
"cix": 2,
"ix": 1,
"mn": "ADBE Vector Group"
}
],
"ip": 0,
"op": 300.00001221925,
"st": 0,
"bm": 0,
"sr": 1
},
{
"ddd": 0,
"ind": 5,
"ty": 4,
"nm": "Shape Layer 5",
"ks": {
"o": { "a": 0, "k": 100 },
"r": { "a": 0, "k": 180 },
"p": { "a": 0, "k": [127.984, 127.969, 0] },
"a": { "a": 0, "k": [-0.182, 32.385, 0] },
"s": {
"a": 1,
"k": [
{
"i": { "x": [0.833, 0.833, 0.833], "y": [0.833, 0.833, 0.833] },
"o": { "x": [0.167, 0.167, 0.167], "y": [0.167, 0.167, 0.167] },
"n": [
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167"
],
"t": 31,
"s": [132, 132, 100],
"e": [145, 145, 100]
},
{
"i": { "x": [0.833, 0.833, 0.833], "y": [0.833, 0.833, 0.833] },
"o": { "x": [0.167, 0.167, 0.167], "y": [0.167, 0.167, 0.167] },
"n": [
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167"
],
"t": 34,
"s": [145, 145, 100],
"e": [132, 132, 100]
},
{ "t": 38.0000015477717 }
]
}
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"d": 1,
"ty": "el",
"s": { "a": 0, "k": [14.125, 14.125] },
"p": { "a": 0, "k": [0, 0] },
"nm": "Ellipse Path 1",
"mn": "ADBE Vector Shape - Ellipse"
},
{
"ty": "fl",
"c": {
"a": 1,
"k": [
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"n": ["0p833_0p833_0p167_0p167"],
"t": 0,
"s": [0.1647059, 0.6313726, 0.8509804, 1],
"e": [0.1647059, 0.6313726, 0.8509804, 1]
},
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"n": ["0p833_0p833_0p167_0p167"],
"t": 26,
"s": [0.1647059, 0.6313726, 0.8509804, 1],
"e": [0.1647059, 0.8509804, 0.8117647, 1]
},
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"n": ["0p833_0p833_0p167_0p167"],
"t": 38,
"s": [0.1647059, 0.8509804, 0.8117647, 1],
"e": [0.1647059, 0.6313726, 0.8509804, 1]
},
{ "t": 42.0000017106951 }
]
},
"o": { "a": 0, "k": 100 },
"r": 1,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill"
},
{
"ty": "tr",
"p": { "a": 0, "k": [-0.063, 1.5], "ix": 2 },
"a": { "a": 0, "k": [0, 0], "ix": 1 },
"s": { "a": 0, "k": [100, 100], "ix": 3 },
"r": { "a": 0, "k": 0, "ix": 6 },
"o": { "a": 0, "k": 100, "ix": 7 },
"sk": { "a": 0, "k": 0, "ix": 4 },
"sa": { "a": 0, "k": 0, "ix": 5 },
"nm": "Transform"
}
],
"nm": "Ellipse 1",
"np": 3,
"cix": 2,
"ix": 1,
"mn": "ADBE Vector Group"
}
],
"ip": 0,
"op": 300.00001221925,
"st": 0,
"bm": 0,
"sr": 1
},
{
"ddd": 0,
"ind": 6,
"ty": 4,
"nm": "Shape Layer 4",
"ks": {
"o": { "a": 0, "k": 100 },
"r": { "a": 0, "k": 135 },
"p": { "a": 0, "k": [127.984, 127.969, 0] },
"a": { "a": 0, "k": [-0.182, 32.385, 0] },
"s": {
"a": 1,
"k": [
{
"i": { "x": [0.833, 0.833, 0.833], "y": [0.833, 0.833, 0.833] },
"o": { "x": [0.167, 0.167, 0.167], "y": [0.167, 0.167, 0.167] },
"n": [
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167"
],
"t": 23,
"s": [132, 132, 100],
"e": [145, 145, 100]
},
{
"i": { "x": [0.833, 0.833, 0.833], "y": [0.833, 0.833, 0.833] },
"o": { "x": [0.167, 0.167, 0.167], "y": [0.167, 0.167, 0.167] },
"n": [
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167"
],
"t": 26,
"s": [145, 145, 100],
"e": [132, 132, 100]
},
{ "t": 30.0000012219251 }
]
}
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"d": 1,
"ty": "el",
"s": { "a": 0, "k": [14.125, 14.125] },
"p": { "a": 0, "k": [0, 0] },
"nm": "Ellipse Path 1",
"mn": "ADBE Vector Shape - Ellipse"
},
{
"ty": "fl",
"c": {
"a": 1,
"k": [
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"n": ["0p833_0p833_0p167_0p167"],
"t": 0,
"s": [0.1647059, 0.6313726, 0.8509804, 1],
"e": [0.1647059, 0.8509804, 0.8117647, 1]
},
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"n": ["0p833_0p833_0p167_0p167"],
"t": 30,
"s": [0.1647059, 0.8509804, 0.8117647, 1],
"e": [0.1647059, 0.6313726, 0.8509804, 1]
},
{ "t": 38.0000015477717 }
]
},
"o": { "a": 0, "k": 100 },
"r": 1,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill"
},
{
"ty": "tr",
"p": { "a": 0, "k": [-0.063, 1.5], "ix": 2 },
"a": { "a": 0, "k": [0, 0], "ix": 1 },
"s": { "a": 0, "k": [100, 100], "ix": 3 },
"r": { "a": 0, "k": 0, "ix": 6 },
"o": { "a": 0, "k": 100, "ix": 7 },
"sk": { "a": 0, "k": 0, "ix": 4 },
"sa": { "a": 0, "k": 0, "ix": 5 },
"nm": "Transform"
}
],
"nm": "Ellipse 1",
"np": 3,
"cix": 2,
"ix": 1,
"mn": "ADBE Vector Group"
}
],
"ip": 0,
"op": 300.00001221925,
"st": 0,
"bm": 0,
"sr": 1
},
{
"ddd": 0,
"ind": 7,
"ty": 4,
"nm": "Shape Layer 3",
"ks": {
"o": { "a": 0, "k": 100 },
"r": { "a": 0, "k": 90 },
"p": { "a": 0, "k": [127.984, 127.969, 0] },
"a": { "a": 0, "k": [-0.182, 32.385, 0] },
"s": {
"a": 1,
"k": [
{
"i": { "x": [0.833, 0.833, 0.833], "y": [0.833, 0.833, 0.833] },
"o": { "x": [0.167, 0.167, 0.167], "y": [0.167, 0.167, 0.167] },
"n": [
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167"
],
"t": 14,
"s": [132, 132, 100],
"e": [145, 145, 100]
},
{
"i": { "x": [0.833, 0.833, 0.833], "y": [0.833, 0.833, 0.833] },
"o": { "x": [0.167, 0.167, 0.167], "y": [0.167, 0.167, 0.167] },
"n": [
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167"
],
"t": 17,
"s": [145, 145, 100],
"e": [132, 132, 100]
},
{ "t": 21.0000008553475 }
]
}
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"d": 1,
"ty": "el",
"s": { "a": 0, "k": [14.125, 14.125] },
"p": { "a": 0, "k": [0, 0] },
"nm": "Ellipse Path 1",
"mn": "ADBE Vector Shape - Ellipse"
},
{
"ty": "fl",
"c": {
"a": 1,
"k": [
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"n": ["0p833_0p833_0p167_0p167"],
"t": 0,
"s": [0.1647059, 0.6313726, 0.8509804, 1],
"e": [0.1647059, 0.8509804, 0.8117647, 1]
},
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"n": ["0p833_0p833_0p167_0p167"],
"t": 22,
"s": [0.1647059, 0.8509804, 0.8117647, 1],
"e": [0.1647059, 0.6313726, 0.8509804, 1]
},
{ "t": 28.0000011404634 }
]
},
"o": { "a": 0, "k": 100 },
"r": 1,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill"
},
{
"ty": "tr",
"p": { "a": 0, "k": [-0.063, 1.5], "ix": 2 },
"a": { "a": 0, "k": [0, 0], "ix": 1 },
"s": { "a": 0, "k": [100, 100], "ix": 3 },
"r": { "a": 0, "k": 0, "ix": 6 },
"o": { "a": 0, "k": 100, "ix": 7 },
"sk": { "a": 0, "k": 0, "ix": 4 },
"sa": { "a": 0, "k": 0, "ix": 5 },
"nm": "Transform"
}
],
"nm": "Ellipse 1",
"np": 3,
"cix": 2,
"ix": 1,
"mn": "ADBE Vector Group"
}
],
"ip": 0,
"op": 300.00001221925,
"st": 0,
"bm": 0,
"sr": 1
},
{
"ddd": 0,
"ind": 8,
"ty": 4,
"nm": "Shape Layer 2",
"ks": {
"o": { "a": 0, "k": 100 },
"r": { "a": 0, "k": 45 },
"p": { "a": 0, "k": [127.984, 127.969, 0] },
"a": { "a": 0, "k": [-0.182, 32.385, 0] },
"s": {
"a": 1,
"k": [
{
"i": { "x": [0.833, 0.833, 0.833], "y": [0.833, 0.833, 0.833] },
"o": { "x": [0.167, 0.167, 0.167], "y": [0.167, 0.167, 0.167] },
"n": [
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167"
],
"t": 7,
"s": [132, 132, 100],
"e": [145, 145, 100]
},
{
"i": { "x": [0.833, 0.833, 0.833], "y": [0.833, 0.833, 0.833] },
"o": { "x": [0.167, 0.167, 0.167], "y": [0.167, 0.167, 0.167] },
"n": [
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167"
],
"t": 10,
"s": [145, 145, 100],
"e": [132, 132, 100]
},
{ "t": 14.0000005702317 }
]
}
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"d": 1,
"ty": "el",
"s": { "a": 0, "k": [14.125, 14.125] },
"p": { "a": 0, "k": [0, 0] },
"nm": "Ellipse Path 1",
"mn": "ADBE Vector Shape - Ellipse"
},
{
"ty": "fl",
"c": {
"a": 1,
"k": [
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"n": ["0p833_0p833_0p167_0p167"],
"t": 0,
"s": [0.1647059, 0.6313726, 0.8509804, 1],
"e": [0.1647059, 0.8509804, 0.8117647, 1]
},
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"n": ["0p833_0p833_0p167_0p167"],
"t": 16,
"s": [0.1647059, 0.8509804, 0.8117647, 1],
"e": [0.1647059, 0.6313726, 0.8509804, 1]
},
{ "t": 22.0000008960784 }
]
},
"o": { "a": 0, "k": 100 },
"r": 1,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill"
},
{
"ty": "tr",
"p": { "a": 0, "k": [-0.063, 1.5], "ix": 2 },
"a": { "a": 0, "k": [0, 0], "ix": 1 },
"s": { "a": 0, "k": [100, 100], "ix": 3 },
"r": { "a": 0, "k": 0, "ix": 6 },
"o": { "a": 0, "k": 100, "ix": 7 },
"sk": { "a": 0, "k": 0, "ix": 4 },
"sa": { "a": 0, "k": 0, "ix": 5 },
"nm": "Transform"
}
],
"nm": "Ellipse 1",
"np": 3,
"cix": 2,
"ix": 1,
"mn": "ADBE Vector Group"
}
],
"ip": 0,
"op": 300.00001221925,
"st": 0,
"bm": 0,
"sr": 1
},
{
"ddd": 0,
"ind": 9,
"ty": 4,
"nm": "Shape Layer 1",
"ks": {
"o": { "a": 0, "k": 100 },
"r": { "a": 0, "k": 0 },
"p": { "a": 0, "k": [127.984, 127.969, 0] },
"a": { "a": 0, "k": [-0.182, 32.385, 0] },
"s": {
"a": 1,
"k": [
{
"i": { "x": [0.833, 0.833, 0.833], "y": [0.833, 0.833, 0.833] },
"o": { "x": [0.167, 0.167, 0.167], "y": [0.167, 0.167, 0.167] },
"n": [
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167"
],
"t": 0,
"s": [132, 132, 100],
"e": [145, 145, 100]
},
{
"i": { "x": [0.833, 0.833, 0.833], "y": [0.833, 0.833, 0.833] },
"o": { "x": [0.167, 0.167, 0.167], "y": [0.167, 0.167, 0.167] },
"n": [
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167",
"0p833_0p833_0p167_0p167"
],
"t": 3,
"s": [145, 145, 100],
"e": [132, 132, 100]
},
{ "t": 7.00000028511585 }
]
}
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"d": 1,
"ty": "el",
"s": { "a": 0, "k": [14.125, 14.125] },
"p": { "a": 0, "k": [0, 0] },
"nm": "Ellipse Path 1",
"mn": "ADBE Vector Shape - Ellipse"
},
{
"ty": "fl",
"c": {
"a": 1,
"k": [
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"n": ["0p833_0p833_0p167_0p167"],
"t": 0,
"s": [0.1647059, 0.6313726, 0.8509804, 1],
"e": [0.1647059, 0.8509804, 0.8117647, 1]
},
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"n": ["0p833_0p833_0p167_0p167"],
"t": 5,
"s": [0.1647059, 0.8509804, 0.8117647, 1],
"e": [0.1647059, 0.6313726, 0.8509804, 1]
},
{ "t": 16.0000006516934 }
]
},
"o": { "a": 0, "k": 100 },
"r": 1,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill"
},
{
"ty": "tr",
"p": { "a": 0, "k": [-0.063, 1.5], "ix": 2 },
"a": { "a": 0, "k": [0, 0], "ix": 1 },
"s": { "a": 0, "k": [100, 100], "ix": 3 },
"r": { "a": 0, "k": 0, "ix": 6 },
"o": { "a": 0, "k": 100, "ix": 7 },
"sk": { "a": 0, "k": 0, "ix": 4 },
"sa": { "a": 0, "k": 0, "ix": 5 },
"nm": "Transform"
}
],
"nm": "Ellipse 1",
"np": 3,
"cix": 2,
"ix": 1,
"mn": "ADBE Vector Group"
}
],
"ip": 0,
"op": 300.00001221925,
"st": 0,
"bm": 0,
"sr": 1
}
]
}

View File

@ -0,0 +1,17 @@
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 it is too large Load Diff

View File

@ -0,0 +1,17 @@
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

@ -10,6 +10,7 @@ export const renderRoutesRecursively = (routes: TMenuItem[]) =>
if (!useAbility) {
return false;
}
return (
<React.Fragment key={route.path}>
<Route path={route.path} element={RenderRouteElement(route)} />

View File

@ -0,0 +1,24 @@
import React from "react";
import { Switch } from "antd";
export interface SwitchProps {
onChange?: (checked: any, event: any) => any;
checked?: boolean;
}
const onSwitchChange = (checked: boolean) => {
console.log(`switch to ${checked}`);
};
const SwitchButton = ({ onChange, checked }: SwitchProps) => {
return (
<Switch
className="switch_button"
defaultChecked
onChange={(checked: any, event: any) =>
onChange ? onChange(checked, event) : onSwitchChange(checked)
}
// checked={checked}
/>
);
};
export default SwitchButton;

View File

@ -5,10 +5,11 @@ import { RiDeleteBin6Fill } from "react-icons/ri";
import { useTranslation } from "react-i18next";
import { BsEyeFill } from "react-icons/bs";
import { GoTrash } from "react-icons/go";
import { BsQrCode } from "react-icons/bs";
interface ActionButtonsProps {
canEdit: boolean;
canDelete: boolean;
canEdit?: boolean;
canDelete?: boolean;
canShow?: boolean;
editTooltipTitle?: string;
deleteTooltipTitle?: string;
@ -17,9 +18,13 @@ interface ActionButtonsProps {
onShow?: () => void;
index?: number;
className?: string;
canShowQr?: boolean;
onShoqQr?: () => void;
}
const ActionButtons: React.FC<ActionButtonsProps> = ({
canShowQr,
onShoqQr,
canEdit,
canDelete,
editTooltipTitle = "practical.edit",
@ -38,6 +43,10 @@ const ActionButtons: React.FC<ActionButtonsProps> = ({
: "buttonAction";
return (
<Space size="middle" className={`${CustomClassName} ${className} `}>
{canShowQr && (
<BsQrCode onClick={onShoqQr} size={22} style={{ color: "#A098AE" }} />
)}
{canEdit && (
<Tooltip placement="top" title={t(editTooltipTitle)} color="#E0E0E0">
<span onClick={onEdit}>
@ -48,11 +57,7 @@ const ActionButtons: React.FC<ActionButtonsProps> = ({
{canDelete && (
// <Tooltip placement="top" title={t(deleteTooltipTitle)} color="#E0E0E0">
<GoTrash
onClick={onDelete}
size={22}
style={{ color: "#A098AE" }}
/>
<GoTrash onClick={onDelete} size={22} style={{ color: "#A098AE" }} />
// </Tooltip>
)}
{canShow && (

View File

@ -0,0 +1,12 @@
import React from "react";
import QRCode from "react-qr-code";
const QRCodeGenerator = ({url}:any) => {
return (
<div style={{display:'flex',justifyContent:'center'}} >
<QRCode value={url} size={230} type='link' />
</div>
);
};
export default QRCodeGenerator;

View File

@ -0,0 +1,24 @@
import { Button } from "antd";
import { useTranslation } from "react-i18next";
import { CiEdit } from "react-icons/ci";
const EditSettingButton = ({
buttonName,
onClick,
}: {
buttonName?: string;
onClick?: () => void;
}) => {
const { t } = useTranslation();
return (
<div>
<Button className=" setting_edit_button" onClick={onClick}>
<CiEdit />
{t(`header.edit`) ?? `header.${buttonName}`}
</Button>
</div>
);
};
export default EditSettingButton;

View File

@ -0,0 +1,23 @@
import { Button } from "antd";
import { useTranslation } from "react-i18next";
const SecuritySettingButton = ({
name,
danger = false,
}: {
name: string;
danger?: boolean;
}) => {
const { t } = useTranslation();
return (
<div>
<Button
className={`security_setting_button ${danger ? "security_setting_button_danger" : ""}`}
>
{t(name)}
</Button>
</div>
);
};
export default SecuritySettingButton;

View File

@ -0,0 +1,22 @@
import { Tooltip } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { FaPaperclip } from "react-icons/fa";
interface ReportButtonsProps {
editTooltipTitle?: any;
onClick?: () => void;
}
const ReportTableIcon = ({ editTooltipTitle, onClick }: ReportButtonsProps) => {
const { t } = useTranslation();
return (
<Tooltip placement="top" title={t(editTooltipTitle)} color="#E0E0E0">
<span onClick={onClick}>
<FaPaperclip size={22} style={{ color: "#A098AE" }} />
</span>
</Tooltip>
);
};
export default ReportTableIcon;

View File

@ -0,0 +1,23 @@
import { Button } from "antd";
import { useTranslation } from "react-i18next";
import { HiOutlineTrash } from "react-icons/hi2";
const TrashButton = ({
name,
onClick,
icon = true,
}: {
name: string;
onClick?: () => void;
icon?: boolean;
}) => {
const { t } = useTranslation();
return (
<Button className="trash_button" onClick={onClick}>
{icon ? <HiOutlineTrash /> : ""}
{t(`header.${name}`)}
</Button>
);
};
export default TrashButton;

View File

@ -0,0 +1,58 @@
import { create } from "zustand";
interface FilterState {
filterState: any;
setFilterState: (data: any) => void;
clearFilterState: () => void;
setWithOldValue: (data: any) => void;
}
export const useFilterState = create<FilterState>((set, get) => ({
filterState: {},
setFilterState: (data) => set(() => ({ filterState: data })),
clearFilterState: () => set(() => ({ filterState: [] })),
setWithOldValue: (data) =>
set((state) => ({ filterState: [...state.filterState, data] })),
}));
// import { create } from "zustand";
// interface FilterState {
// filterState: any[];
// setFilterState: (data: any) => void;
// clearFilterState: () => void;
// setWithOldValue: (data: any) => void;
// setInitialValue: (data: any) => void;
// setFilterStateWithFormat:(data:any)=>void
// }
// export const useFilterState = create<FilterState>((set, get) => ({
// filterState: [],
// setFilterState: (data) => set(() => ({ filterState: data })),
// clearFilterState: () => set(() => ({ filterState: [] })),
// setWithOldValue: (data) =>
// set((state) => ({ filterState: [...state.filterState, data] })),
// setInitialValue: (data) => {
// if (get().filterState.length < 1) {
// set(() => ({ filterState: data }));
// }
// },
// // Inside your useFilterState store
// setFilterStateWithFormat: (data:any) => {
// const formatDate = (date:any) => {
// if (!date) return null;
// const d = new Date(date);
// const year = d.getFullYear();
// const month = String(d.getMonth() + 1).padStart(2, '0'); // Months are zero-based
// const day = String(d.getDate()).padStart(2, '0');
// return `${year}-${month}-${day}`;
// };
// const formattedData = {
// ...data,
// starting_date: formatDate(data.starting_date),
// };
// set(() => ({ filterState: formattedData }));
// },
// }));

View File

@ -0,0 +1,11 @@
import { create } from "zustand";
interface ModalState {
isOpen: string;
setIsOpen: (value: string) => void;
}
export const useModalState = create<ModalState>((set) => ({
isOpen: "",
setIsOpen: (value: string) => set((state) => ({ isOpen: value })),
}));

View File

@ -0,0 +1,192 @@
import React, { ReactNode, useEffect, useState } from "react";
import { FaFilter } from "react-icons/fa";
import {
Form,
Formik,
FormikConfig,
FormikHelpers,
useFormikContext,
} from "formik";
import { Button, ButtonProps, Divider, Modal } from "antd";
import { useTranslation } from "react-i18next";
import { useModalState } from "./Modal";
import { useFilterState } from "./FilterState";
import { ModalEnum } from "../../../enums/Model";
import { QueryStatusEnum } from "../../../enums/QueryStatus";
import SpinContainer from "../../Layout/SpinContainer";
import { FaXmark } from "react-icons/fa6";
type OmitFormikProps = "children" | "initialValues" | "onSubmit";
interface FormikFormProps extends Omit<FormikConfig<any>, OmitFormikProps> {
children: React.ReactNode;
onSubmit?: (
values: any,
formikHelpers?: FormikHelpers<any>,
) => void | Promise<any>;
getInitialValues?: any;
getValidationSchema?: any;
status?: QueryStatusEnum;
ModelClassName?: string;
width?: string;
isLoading?: boolean;
isOpen: any;
setIsOpen: any;
}
const useFilter = () => {
const { setIsOpen, isOpen } = useModalState((state) => state);
const { filterState, setFilterState, clearFilterState } = useFilterState();
const [t] = useTranslation();
const [formValues, setFormValues] = useState({});
// Define the type for the callback
type SubmitCallback = () => void;
const FilterButton = () => {
const handleState = () => {
if (isOpen === ModalEnum?.FILTER) {
setIsOpen("");
clearFilterState();
setFormValues({});
} else {
setIsOpen(ModalEnum?.FILTER);
}
};
return (
<span onClick={handleState} className="filter_menu_button">
<FaFilter />
{t("Filter")}
</span>
);
};
useEffect(() => {
setFormValues({});
setFilterState({});
}, []);
const FilterBody = ({
onSubmit,
children,
getInitialValues,
getValidationSchema,
status,
ModelClassName,
width = "500px",
isLoading = false,
setIsOpen,
isOpen,
...formikProps
}: FormikFormProps) => {
const handleSubmit = (values: any) => {
setFilterState(values);
setFormValues(values);
if (onSubmit) {
onSubmit(values);
}
Submit();
setIsOpen("");
};
const handleCancel = (isCancel = false) => {
if (isCancel) {
setIsOpen("");
return;
}
setIsOpen("");
clearFilterState();
setFormValues({});
};
const handleOpen = () => {
setIsOpen(true);
// setObjectToEdit({});
};
const [t] = useTranslation();
return (
<>
<Modal
className={"ModalForm " + ModelClassName}
centered
width={width}
footer={null}
open={isOpen}
onOk={handleOpen}
onCancel={() => {}}
mask={false}
style={{ position: "absolute", top: "31.4%", left: "16.7%" }}
>
<Formik
enableReinitialize={true}
onSubmit={handleSubmit}
initialValues={formValues}
onReset={() => {
handleCancel(false);
}}
{...formikProps}
>
{(formik) => {
return (
<Form>
<div>
<header>
{" "}
{t("models.filter")}{" "}
<FaXmark onClick={() => handleCancel(true)} />{" "}
</header>
<Divider />
<main className="main_modal">
{isLoading ? <SpinContainer /> : children}
<Divider />
</main>
</div>
</Form>
);
}}
</Formik>
</Modal>
</>
);
};
interface SubmitButtonProps extends Omit<ButtonProps, "loading"> {}
const FilterSubmit = ({ ...buttonProps }: SubmitButtonProps) => {
return (
<div className="filter-submit-buttons buttons">
<Button
type="primary"
className="back_button filter_modal_cancel_button"
htmlType="reset"
>
{t("practical.reset")}
</Button>
<Button
className="pointer filter_modal_add_button"
type="primary"
{...buttonProps}
htmlType="submit"
>
{t(`practical.submit`)}
</Button>
</div>
);
};
const Submit = (callback?: SubmitCallback): void => {
if (callback) {
callback();
}
};
return {
FilterButton,
FilterBody,
filterState,
setFilterState,
clearFilterState,
FilterSubmit,
Submit,
};
};
export default useFilter;

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,17 @@ import {
File,
DataRange,
SelectField,
Default,
CheckboxField,
MaltyFile,
SearchField,
TextField,
DropFile,
TextAreaMML,
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,
@ -24,6 +24,7 @@ const components: { [key: string]: React.FC<any> } = {
LocalSearch: LocalSearchField,
DataRange: DataRange,
TextArea: TextField,
TextAreaMML: TextAreaMML,
Date: Date,
Time: Time,
File: File,
@ -31,12 +32,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,52 +3,44 @@ 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,
label,
picker = "date",
isDisabled,
props,
onChange,
placeholder,
className,
no_label,
label_icon,
...props
}: any) => {
const { errorMsg, isError, t, formik } = useFormField(name, props);
const FormikValue = formik.values[name];
const onCalendarChange = (value: any) => {
formik.setFieldValue(name, value);
// console.log(value,"value ");
};
console.log(props);
console.log(FormikValue);
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 +50,13 @@ const Date = ({
size="large"
onChange={onChange || onCalendarChange}
disabled={isDisabled}
format={Formater}
format={props?.Format ?? Formatter}
id={name}
needConfirm={false}
{...props}
/>
{/* <DatePicker onChange={onChange} /> */}
</Form.Item>
</ValidationFieldContainer>
</div>
);
};

View File

@ -1,9 +1,9 @@
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";
const Default = ({
name,
@ -14,52 +14,35 @@ const Default = ({
type,
no_label,
label_icon,
label2,
...props
}: ValidationFieldPropsInput) => {
const { errorMsg, isError, t } = useFormField(name, props);
}: any) => {
const { errorMsg, isError, t, formik } = 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}
value={formik?.values?.[name]}
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 {
@ -51,6 +48,17 @@ const File = ({
const customRequest = async ({ onSuccess, no_label, label_icon }: any) => {
onSuccess();
};
const beforeUpload = (file: File) => {
const maxSize = 2 * 1024 * 1024; // 2 MB in bytes
if (file.size > maxSize) {
alert(t("validation.File_size_exceeds_2_MB_limit."));
return Upload.LIST_IGNORE; // Prevent the file from being uploaded
}
return true; // Allow the file to be uploaded
};
return (
<div className={`ValidationField upload_image_button ${className ?? ""} `}>
<label htmlFor={name} className="text">
@ -58,6 +66,7 @@ const File = ({
</label>
<Upload
beforeUpload={beforeUpload} // Set the beforeUpload function
disabled={isDisabled}
listType="picture"
maxCount={1}
@ -65,12 +74,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 +91,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,11 +15,10 @@ const MaltyFile = ({
const { formik, t, isError } = useFormField(name, props);
let imageUrl = formik?.values?.[name] ?? null;
// Mapping formik values to fileList format
const fileList = imageUrl
// Memoizing the fileList to prevent unnecessary recalculations
const fileList = useMemo(() => {
return imageUrl
? imageUrl.map((file: any, index: number) => {
// console.log(file,"file");
return file instanceof File
? {
uid: index,
@ -37,6 +36,7 @@ const MaltyFile = ({
};
})
: [];
}, [imageUrl]); // Dependency array ensures it recalculates only when imageUrl changes
const FilehandleChange = ({ fileList }: any) => {
if (fileList.length === 0) {
@ -48,6 +48,7 @@ const MaltyFile = ({
);
}
};
// Custom request function
const customRequest = async ({ onSuccess }: any) => {
// Perform any necessary actions before onSuccess is called
@ -63,11 +64,12 @@ const MaltyFile = ({
<Upload
disabled={isDisabled}
listType="picture"
fileList={fileList} // Using fileList instead of defaultFileList
fileList={fileList} // Using memoized fileList
onChange={onChange || FilehandleChange}
customRequest={customRequest}
className={`${className} w-100`}
multiple // Allow multiple files to be selected
id={name}
>
<Button
className={isError ? "isError w-100" : "w-100"}

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,154 @@
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;
}) => {
formik?.setFieldValue(name, value);
};
const SearchHandleChange = (value: any) => {
navigate(`${window?.location?.pathname}?${searchBy}=${value}`, {
replace: true,
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, option: any) => {
if (isMulti) {
formik?.setFieldValue(name, option ?? []);
} else {
formik?.setFieldValue(name, option ?? {});
}
const isCleared = value?.length === 0 || !value;
if (isCleared) {
if (PageName) {
setValidationParamState({
[PageName]: 1,
});
}
}
console.log(value, "value");
};
const handleChange = useDebounce((value: string) => {
if (PageName) {
pushValidationParamState({
[PageName]: 1,
});
}
pushValidationParamState({
[searchBy]: value,
});
});
const handleBlur = () => {
if (PageName && page === 1) {
setValidationParamState({
[PageName]: null,
});
}
if (PageName && page !== 1) {
setValidationParamState({
[PageName]: 1,
});
// setAllPagesOption([]);
}
};
const handleScroll = (event: any) => {
const target = event.target;
const isAtBottom =
target.scrollHeight - 10 <=
Math.floor(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");
const value = isMulti
? formik.values[name]?.map((item: any) => {
return item?.name ?? item;
})
: (formik.values[name]?.["name"] ?? "");
console.log(value);
return (
<div className="ValidationField w-100">
{no_label ? (
<label htmlFor={name} className="text">
<span>empty</span>
</label>
) : label_icon ? (
<div className="LabelWithIcon">
<label htmlFor={name} className="text">
{t(`input.${label ? label : name}`)}
</label>
<MdOutlineEdit size={22} style={{ color: "#A098AE" }} />
</div>
) : (
<label htmlFor={name} className="text">
{t(`input.${label ? label : name}`)}
</label>
)}
<Form.Item
hasFeedback
validateStatus={isError ? "error" : ""}
help={isError ? errorMsg : ""}
>
<ValidationFieldLabel
name={name}
label={label}
label_icon={label_icon}
no_label={no_label}
placeholder={placeholder}
t={t}
/>
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
<Select
placeholder={t(
`input.${placeholder ? placeholder : label ? label : name}`,
)}
placeholder={t(`input.${placeholder || label || name}`)}
disabled={isDisabled}
options={option}
options={AllPagesOption}
size="large"
className={`${className} w-100`}
value={formik.values[name]}
value={value}
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: "name" }}
{...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,37 @@ 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 = ({
@ -12,42 +14,30 @@ const TextField = ({
placeholder,
isDisabled,
onChange,
props,
no_label,
label_icon,
className,
...props
}: any) => {
const { formik, isError, errorMsg, t } = useFormField(name, props);
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}`)}
@ -56,10 +46,12 @@ const TextField = ({
size="large"
showCount
maxLength={1000}
autoSize={{ minRows: 4, maxRows: 10 }}
onChange={onChange || TextFilehandleChange}
style={{ height: 120 }}
id={name}
{...props}
/>
</Form.Item>
</ValidationFieldContainer>
</div>
);
};

View File

@ -0,0 +1,58 @@
import { Input } from "antd";
import React from "react";
import useFormField from "../../../Hooks/useFormField";
import { ValidationFieldLabel } from "../components/ValidationFieldLabel";
import { ValidationFieldContainer } from "../components/ValidationFieldContainer";
import LatexPreview from "../../CustomFields/MathComponent";
const { TextArea } = Input;
const TextFieldMML = ({
name,
label,
placeholder,
isDisabled,
onChange,
no_label,
label_icon,
className,
mathContent, // Add mathContent prop
...props
}: any) => {
const { formik, isError, errorMsg, t } = useFormField(name, props);
const TextFilehandleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
) => {
formik.setFieldValue(name, e.target.value);
};
return (
<div className={`ValidationField w-100 ${className ?? ""} `}>
<ValidationFieldLabel
name={name}
label={label}
label_icon={label_icon}
no_label={no_label}
placeholder={placeholder}
t={t}
/>
<ValidationFieldContainer isError={isError} errorMsg={errorMsg}>
<TextArea
placeholder={t(`input.${placeholder ? placeholder : name}`)}
name={name}
disabled={isDisabled}
size="large"
showCount
maxLength={1000}
autoSize={{ minRows: 4, maxRows: 10 }}
onChange={onChange || TextFilehandleChange}
id={name}
{...props}
/>
</ValidationFieldContainer>
</div>
);
};
export default React.memo(TextFieldMML);

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

@ -9,6 +9,7 @@ import MaltyFile from "./MaltyFile";
import SearchField from "./SearchField";
import TextField from "./TextField";
import DropFile from "./DropFile.tsx";
import TextAreaMML from "./TextFieldMML";
export {
Time,
@ -22,4 +23,5 @@ export {
SearchField,
TextField,
DropFile,
TextAreaMML,
};

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,225 +4,76 @@
justify-content: space-between;
}
.ValidationField {
margin-bottom: 1.3vw;
margin-bottom: 10px;
position: relative;
min-height: 80px;
> * {
width: 100%;
width: 100% !important;
min-width: 150px;
}
.text,
.ant-form-item {
margin-bottom: 7px !important;
font-weight: bold;
font-size: 19px;
> span {
color: transparent;
}
}
.ant-select-outlined:not(.ant-select-customize-input) .ant-select-selector {
border: 1px solid var(--border-color);
}
.Select_error {
.ant-select-selector {
border: 1px solid red !important;
}
}
// .ValidationField{
// .ant-select-selector{
// border: 1px solid var(--border-color) ;
// }
// }
> span {
margin-bottom: 0px !important;
&:focus-within {
border-color: var(--primary);
box-shadow: 0 0 0 1px var(--primary);
cursor: pointer;
}
&:has(.is-invalid) {
border-color: red !important ;
}
input {
color: var(--text);
background: var(--bg);
}
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active {
-webkit-box-shadow: 0 0 0 30px white inset !important;
}
}
}
//// upload
.ant-upload-select {
width: 100%;
}
.Checkboxs {
padding: 4%;
}
.ant-checkbox-wrapper {
min-width: 100px;
}
.SearchField {
button {
background: var(--primary);
}
}
.text {
color: var(--text);
margin-bottom: 15px;
font-weight: bold;
}
input:disabled {
color: var(--text) !important;
}
.isError {
outline: red 1px solid;
color: red;
}
.Error_color {
color: red;
}
input:-webkit-autofill {
-webkit-box-shadow: 0 0 0 1000px white inset !important; /* Change the color to your desired background color */
}
input:-webkit-autofill:focus {
-webkit-box-shadow: 0 0 0 1000px white inset !important; /* Change the color to your desired background color */
}
/* Remove autofill background color on hover */
input:-webkit-autofill:hover {
-webkit-box-shadow: 0 0 0 1000px white inset !important; /* Change the color to your desired background color */
}
.upload_image_button {
.ant-btn {
min-height: 3vw !important;
border: 0.1vw solid var(--border-color);
.ant-btn-default {
padding: 7px 11px;
min-height: var(--fieldHeight);
display: flex;
align-items: center;
justify-content: center;
}
}
.ant-select-outlined:not(.ant-select-customize-input) .ant-select-selector {
min-height: 3vw !important;
}
.ant-select-multiple.ant-select-lg .ant-select-selection-overflow {
min-height: 3vw !important;
}
.ant-upload-list .ant-upload-list-item {
// height:3vw !important;
border: 1px solid var(--border-color) !important;
}
.TowValidationItems {
display: flex;
gap: 3%;
> label {
display: none;
}
}
.ant-select .ant-select-arrow {
inset-inline-end: 1vw;
}
.ant-input-affix-wrapper-lg {
padding: 0.5vw 1vw;
font-size: 1vw;
min-height: 3vw;
border-radius: 0.6vw;
border: 1.5px solid var(--opacity);
}
.ant-picker-outlined {
padding: 0.5vw 1vw;
font-size: 1vw;
min-height: 3vw;
border-radius: 0.6vw;
border: 1px solid var(--border-color);
}
.ant-select-single.ant-select-lg .ant-select-selector {
min-height: 3vw;
border-radius: 0.6vw;
}
.ant-select-single .ant-select-selector .ant-select-selection-search-input {
min-height: 3vw;
}
.ant-select-outlined .ant-select-selector {
min-height: 3vw !important;
border-radius: 0.6vw !important;
}
.ant-select-single.ant-select-lg {
min-height: 3vw;
}
.ant-upload-wrapper .ant-upload-list .ant-upload-list-item {
width: 21.5vw;
}
.ant-input-number-outlined {
width: 100%;
height: 3vw;
}
.ant-input-number-lg input.ant-input-number-input {
width: 100%;
height: 3vw;
}
.bigRow {
width: 100%;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
> *.w-100 {
width: 48% !important;
}
align-items: center;
}
//// number input
///
.ant-input-number-affix-wrapper-lg {
width: 100%;
height: 3vw;
border-radius: 0.6vw;
border: 1px solid var(--border-color);
}
.ValidationFieldCheckbox {
// background-color: red;
}
.TwoSelectGroup {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.TwoSelectGroupbutton {
margin-bottom: 20px;
/// input hight
.ant-form-item-control-input-content {
min-height: var(--fieldHeight);
}
.ant-checkbox-wrapper {
margin-top: 25px !important;
//// date picker
.ant-picker-large {
min-height: var(--fieldHeight);
}
.add_new_button {
margin-bottom: 20px;
svg {
color: var(--primary);
}
}
.ValidationField:has(.input_number) {
max-width: 100px;
/// text area
///
.input_number {
max-width: 100px;
.ValidationFieldTextArea {
.ant-form-item-control-input-content {
min-height: 120px;
}
}
.flex {
display: flex;
gap: 30px;
max-width: 80% !important;
.ant-input-textarea-affix-wrapper.ant-input-affix-wrapper {
height: 120px;
}
//// malty select
///
.ant-select-multiple {
height: auto !important;
min-height: 40px;
.ant-select-selector {
min-height: 40px;
}
}

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

View File

@ -1,39 +1,152 @@
import { useFormikContext } from "formik";
import React from "react";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { GoArrowSwitch } from "react-icons/go";
import { useObjectToEdit } from "../../zustand/ObjectToEditState";
import { QUESTION_OBJECT_KEY } from "../../config/AppKey";
import { Checkbox, CheckboxProps, Popconfirm, Popover } from "antd";
import { CombinationKeyEnum } from "../../enums/CombinationKeyEnum";
import { PopconfirmProps } from "antd/lib";
import { SettingFilled } from "@ant-design/icons";
import { LocalStorageEnum } from "../../enums/LocalStorageEnum";
const Header = () => {
const [t] = useTranslation();
const { values, setFieldValue, setValues } = useFormikContext<any>();
const { isBseQuestion, setIsBseQuestion } = useObjectToEdit();
const { setSavedQuestionData } = useObjectToEdit();
const { values, setValues } = useFormikContext<any>();
const {
isBseQuestion,
setIsBseQuestion,
ShowHint,
setShowHint,
ShowLatexOption,
setShowLatexOption,
} = useObjectToEdit();
// const [Setting, setSetting] = useState(false)
const isEdited = () => {
if (isBseQuestion || values?.isBase === 1) {
const content = !values?.content;
const content_image = !values?.content_image;
const Questions =
values?.Questions?.length <= 1 &&
values?.Questions?.[0]?.answers?.length === 0;
const defaultQuestionHint =
Object.keys(values?.Questions?.[0] ?? {})?.length <= 1;
if (content && content_image && Questions && defaultQuestionHint) {
return false;
}
} else {
const content = !values?.content;
const content_image = !values?.content_image;
const hint = !values?.hint;
const answers = !(values?.answers?.length > 0);
const tags = !(values?.tags?.length > 0);
if (content && content_image && hint && answers && tags) {
return false;
}
}
return true;
};
const handleChange = () => {
setSavedQuestionData(null);
localStorage.removeItem(QUESTION_OBJECT_KEY);
if (isBseQuestion) {
setIsBseQuestion(false);
setValues(null);
setFieldValue("isBase", 0);
} else {
setIsBseQuestion(true);
setValues(null);
setFieldValue("isBase", 1);
}
};
const confirm: PopconfirmProps["onConfirm"] = (e) => {
setTimeout(() => {
handleChange();
}, 500);
};
const content = (
<div>
<p>
{" "}
(CTRL + SHIFT + {CombinationKeyEnum.CHOICE}) {t("header.add_choice")}{" "}
</p>
<p>
{" "}
(CTRL + SHIFT + {CombinationKeyEnum.QUESTION}){" "}
{t("header.add_question")}{" "}
</p>
</div>
);
const onChangeHint: CheckboxProps["onChange"] = (e) => {
setShowHint(e.target.checked);
localStorage?.setItem(
LocalStorageEnum.HINT_INPUT,
e.target.checked ? "true" : "false",
);
};
const onChangeLatexOption: CheckboxProps["onChange"] = (e) => {
setShowLatexOption(e.target.checked);
localStorage?.setItem(
LocalStorageEnum.LATEX_OPTION_INPUT,
e.target.checked ? "true" : "false",
);
};
const contentSetting = (
<div>
<Checkbox checked={ShowHint} onChange={onChangeHint}>
{t("header.show_hint")}
</Checkbox>
<Checkbox checked={ShowLatexOption} onChange={onChangeLatexOption}>
{t("header.show_MMl")}
</Checkbox>
</div>
);
return (
<header className="exercise_add_header mb-4">
<article>
<Popover content={content} title={t("practical.Abbreviations")}>
<img src="/Icon/QuestionIcon.svg" alt="" />
</Popover>
<div>
{t("practical.add")} {t("models.exercise")}{" "}
</div>
</article>
<div>
<GoArrowSwitch onClick={handleChange} className="m-2" />
<div className="question_header_setting">
<Popover trigger="click" content={contentSetting}>
<SettingFilled />
</Popover>
</div>
{isEdited() ? (
<Popconfirm
title={t("header.this_will_un_do_all_your_changes")}
okText={t("practical.yes")}
cancelText={t("practical.no")}
onConfirm={() => {
confirm();
}}
defaultOpen={false}
>
<GoArrowSwitch className="m-2" />
{isBseQuestion || values?.isBase === 1
? t("header.malty_exercise")
: t("header.exercise")}
</Popconfirm>
) : (
<>
<GoArrowSwitch onClick={() => confirm()} className="m-2" />
{isBseQuestion || values?.isBase === 1
? t("header.malty_exercise")
: t("header.exercise")}
</>
)}
</div>
</header>
);

View File

@ -0,0 +1,84 @@
const fs = require("fs");
const path = require("path");
// File path where the abilities are stored
const ABILITIES_FILE = path.join(__dirname, "./src/utils/hasAbilityFn.ts");
// Function to normalize the input name to UPPER_SNAKE_CASE (e.g., test_moaz => TEST_MOAZ, TestMoaz => TEST_MOAZ)
const normalizeToUpperSnakeCase = (input) => {
return input
.replace(/([a-z])([A-Z])/g, "$1_$2") // camelCase to snake_case
.replace(/[_\s]+/g, "_") // normalize multiple underscores or spaces
.toUpperCase(); // Convert to uppercase
};
// Function to capitalize the first letter of a word
const capitalizeFirstLetter = (word) => word.charAt(0).toUpperCase() + word.slice(1);
// Take the name dynamically from the terminal
const nameInput = process.argv[2];
if (!nameInput) {
console.error("Please provide a name as an argument.");
process.exit(1);
}
// Normalize input name (handle camelCase, snake_case, PascalCase)
const normalizedInput = normalizeToUpperSnakeCase(nameInput);
// Generate ability functions for the given name
const generateAbilityFunctions = (name) => {
const capitalized = capitalizeFirstLetter(name.toLowerCase());
return `
export const canAdd${capitalized} = hasAbility(
ABILITIES_ENUM.${name},
ABILITIES_VALUES_ENUM.STORE,
);
export const canEdit${capitalized} = hasAbility(
ABILITIES_ENUM.${name},
ABILITIES_VALUES_ENUM.UPDATE,
);
export const canDelete${capitalized} = hasAbility(
ABILITIES_ENUM.${name},
ABILITIES_VALUES_ENUM.DELETE,
);
export const canShow${capitalized} = hasAbility(
ABILITIES_ENUM.${name},
ABILITIES_VALUES_ENUM.SHOW,
);
export const canIndex${capitalized} = hasAbility(
ABILITIES_ENUM.${name},
ABILITIES_VALUES_ENUM.INDEX,
);
`.trim();
};
const updateAbilitiesFile = () => {
if (!fs.existsSync(ABILITIES_FILE)) {
console.error(`Error: File ${ABILITIES_FILE} does not exist.`);
return;
}
// Read the existing content of the file
const fileContent = fs.readFileSync(ABILITIES_FILE, "utf-8");
// Check for duplicates
const alreadyExists = fileContent.includes(`canAdd${capitalizeFirstLetter(normalizedInput)}`);
if (alreadyExists) {
console.log(`Abilities for "${normalizedInput}" already exist. No changes made.`);
return;
}
// Generate new abilities
const newAbilities = generateAbilityFunctions(normalizedInput);
// Append the new abilities at the end of the file
const updatedContent = `${fileContent.trim()}\n\n${newAbilities}\n`;
// Write the updated content back to the file
fs.writeFileSync(ABILITIES_FILE, updatedContent, "utf-8");
console.log(`Abilities for "${normalizedInput}" added successfully to hasAbilityFn.ts.`);
};
// Run the update
updateAbilitiesFile();

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