Compare commits

...

315 Commits

Author SHA1 Message Date
606ac68d85 Merge pull request 'update bug' (#20) from Yasin into release/sprint-8
Reviewed-on: #20
2024-12-02 06:55:15 +00:00
YasinShaikh123
537304f0fb update bug 2024-12-02 12:23:27 +05:30
YasinShaikh123
3b83b625c3 update Investment Details KYC 2024-11-25 20:24:39 +05:30
Swapnil Bendal
694dde0f14 [fixed] - view io details 2024-11-25 19:03:42 +05:30
Swapnil Bendal
4f8916036e [update] - condition on pending request for checkers 2024-11-25 16:45:52 +05:30
YasinShaikh123
0c21e99732 update comment changes 2024-11-25 16:41:19 +05:30
YasinShaikh123
5900f637be Merge branch 'release/sprint-8' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into Yasin 2024-11-25 15:52:33 +05:30
YasinShaikh123
2453b24d91 Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into Yasin 2024-11-25 12:21:02 +05:30
Swapnil Bendal
3db9488444 [update] - counter in pending tab 2024-11-22 20:25:21 +05:30
bb30a71a60 Merge pull request 'modal api useEffect' (#19) from Yasin into release/sprint-8
Reviewed-on: #19
2024-11-22 14:27:49 +00:00
YasinShaikh123
526d2aecca modal api useEffect 2024-11-22 19:57:05 +05:30
e742f6c18d Merge pull request 'update modal view' (#18) from Yasin into release/sprint-8
Reviewed-on: #18
2024-11-22 14:24:42 +00:00
YasinShaikh123
d4c9a5521f update modal view 2024-11-22 19:54:12 +05:30
7a04ee0abb Merge pull request 'updaste' (#17) from Yasin into release/sprint-8
Reviewed-on: #17
2024-11-22 13:53:09 +00:00
YasinShaikh123
a12b2c9a2c updaste 2024-11-22 19:22:06 +05:30
0b2e48200a Merge pull request 'token change' (#16) from Yasin into release/sprint-8
Reviewed-on: #16
2024-11-22 13:35:11 +00:00
YasinShaikh123
bf7b3e1596 token change 2024-11-22 19:04:31 +05:30
059b711bc1 Merge branch 'release/sprint-8' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into release/sprint-8 2024-11-22 19:00:15 +05:30
b0c01f3a0c updaate role 2024-11-22 19:00:14 +05:30
973ec656d0 Merge pull request 'update modal close' (#15) from Yasin into release/sprint-8
Reviewed-on: #15
2024-11-22 13:22:09 +00:00
YasinShaikh123
29a49150b7 update modal close 2024-11-22 18:51:05 +05:30
7a908a3284 Merge pull request 'updated' (#14) from Yasin into release/sprint-8
Reviewed-on: #14
2024-11-22 11:28:53 +00:00
YasinShaikh123
23c3997d06 updated 2024-11-22 16:56:31 +05:30
6779fdd672 Merge pull request 'Yasin' (#13) from Yasin into release/sprint-8
Reviewed-on: #13
2024-11-22 10:50:46 +00:00
YasinShaikh123
0ab898a3da update new bugs 2024-11-22 16:19:59 +05:30
YasinShaikh123
f3a8a80f4d update action 2024-11-22 13:32:28 +05:30
d1cbeee294 Merge pull request 'update bugs' (#12) from Yasin into release/sprint-8
Reviewed-on: #12
2024-11-22 07:22:50 +00:00
YasinShaikh123
1c4f975781 update bugs 2024-11-22 12:48:02 +05:30
17ffb864a3 Merge pull request 'Yasin' (#11) from Yasin into release/sprint-8
Reviewed-on: #11
2024-11-21 12:14:25 +00:00
YasinShaikh123
6ffcff58a2 update tabs 2024-11-21 17:42:27 +05:30
82612fa7f2 Merge pull request 'update color bugs' (#10) from main into release/sprint-8
Reviewed-on: #10
2024-11-21 10:36:20 +00:00
YasinShaikh123
470ba49c00 UPDATE invest modal 2024-11-21 16:05:51 +05:30
b29478a939 Merge pull request 'transaction panding' (#9) from Yasin into release/sprint-8
Reviewed-on: #9
2024-11-21 10:22:35 +00:00
YasinShaikh123
7ba524d2e4 transaction panding 2024-11-21 15:51:01 +05:30
cc58cdc9b7 Merge pull request 'update invest amount' (#8) from Yasin into release/sprint-8
Reviewed-on: #8
2024-11-21 10:17:31 +00:00
YasinShaikh123
9f54bfbc65 update invest amount 2024-11-21 15:46:10 +05:30
992cb13e1e Merge pull request 'update transaction status modal' (#7) from Yasin into release/sprint-8
Reviewed-on: #7
2024-11-19 13:53:15 +00:00
YasinShaikh123
85e3c34120 update transaction status modal 2024-11-19 19:19:45 +05:30
46672a34e2 Merge pull request 'update Io Cash Nav Transaction' (#6) from Yasin into release/sprint-8
Reviewed-on: #6
2024-11-18 13:36:45 +00:00
YasinShaikh123
61e49393fe update Io Cash Nav Transaction 2024-11-18 17:54:23 +05:30
YasinShaikh123
5b2efcd292 update color bugs 2024-11-14 16:28:55 +05:30
YasinShaikh123
5fc16b58ea working tabs👷‍♂️ 2024-11-14 16:04:20 +05:30
YasinShaikh123
06548abf1e update tabs 2024-11-14 12:08:17 +05:30
YasinShaikh123
f2023cf7b3 update bugs 2024-11-11 12:48:06 +05:30
YasinShaikh123
77fc645767 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-11-07 19:32:57 +05:30
YasinShaikh123
9740fff33c update bugs 2024-11-07 19:32:55 +05:30
0f678fefc1 update 2024-11-06 11:49:34 +05:30
YasinShaikh123
986e531896 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-11-05 13:25:51 +05:30
YasinShaikh123
734dff43a1 but update 2024-11-05 13:25:39 +05:30
d244df302b update 2024-11-05 12:12:38 +05:30
YasinShaikh123
eb14139bcf update 30/10 2024-10-30 12:30:20 +05:30
YasinShaikh123
f5e7217304 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-24 19:11:30 +05:30
YasinShaikh123
30c51a5b34 Update 2024-10-24 19:05:52 +05:30
1f89ca0a1c IO date fixed 2024-10-23 20:07:55 +05:30
YasinShaikh123
82215f8569 user form working 2024-10-23 19:27:52 +05:30
YasinShaikh123
0199a46ed5 Revert "Update changes"
This reverts commit 8cfccf656d.
2024-10-22 20:12:34 +05:30
YasinShaikh123
8cfccf656d Update changes 2024-10-22 20:11:04 +05:30
YasinShaikh123
2e06b52881 Update investment Amount 2024-10-22 20:11:00 +05:30
f99e78a8df updated 2024-10-22 16:28:14 +05:30
YasinShaikh123
6f5e83a4bc investment update 2024-10-21 19:31:18 +05:30
YasinShaikh123
fff3689aeb Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-18 20:12:21 +05:30
YasinShaikh123
184f42ef59 package install 2024-10-18 20:12:14 +05:30
ccc75f50e5 final Commit 2024-10-18 20:01:12 +05:30
751aa6c673 updatee 2024-10-18 19:16:21 +05:30
d44b8aea0d Risky Update ⚠️ 2024-10-18 18:43:28 +05:30
5fd9fda73b Email custom notification update ✉️ 2024-10-18 16:29:26 +05:30
6216367d21 export button fix 2024-10-18 15:08:38 +05:30
a1b9f1c507 update filters 2024-10-18 14:57:21 +05:30
5ff6d5d07b commit 2024-10-18 13:49:30 +05:30
7f1106449f updated 2024-10-18 13:19:23 +05:30
da55f00f73 update Amount Invested 2024-10-18 12:20:10 +05:30
0c56759251 update siquence 2024-10-18 11:57:33 +05:30
cd1cf86fc9 update 2024-10-17 20:28:21 +05:30
9cd0a4e9c6 updaate 2024-10-17 19:58:27 +05:30
0b080733fb upddateed 2024-10-17 15:58:21 +05:30
YasinShaikh123
94b8be1130 updated 2024-10-17 13:39:39 +05:30
YasinShaikh123
3079e7f269 updateIOStatus Changes 2024-10-16 17:01:36 +05:30
YasinShaikh123
2147914c81 IO Nav Details Update 2024-10-15 13:11:05 +05:30
YasinShaikh123
3696ec5e59 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-11 20:16:39 +05:30
YasinShaikh123
7f5d3065ca update fix 2024-10-11 20:16:36 +05:30
28990bb9d6 risky commit ⚠️ 2024-10-11 17:36:22 +05:30
2f816f3c45 update roles nd coma 2024-10-11 17:32:36 +05:30
YasinShaikh123
61c81b100c Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-11 17:31:51 +05:30
YasinShaikh123
fd22732648 create amount update 2024-10-11 17:31:46 +05:30
2df6ea41a4 update roles ⚠️ 2024-10-11 17:26:27 +05:30
YasinShaikh123
739b755ec1 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-11 16:03:52 +05:30
6c16d142d6 UPDATE 2024-10-11 16:03:39 +05:30
YasinShaikh123
4a54d5c80e Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-11 16:03:30 +05:30
YasinShaikh123
47d75371bc create update date 2024-10-11 16:03:26 +05:30
YasinShaikh123
bc8f78d8d6 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-11 16:01:53 +05:30
915242c1f3 update 2024-10-11 15:49:08 +05:30
YasinShaikh123
30a9226f86 routes change 2024-10-11 15:21:41 +05:30
a6dd60140a upddate 2024-10-11 14:58:29 +05:30
512e2936ad update fawateer 2024-10-11 14:52:17 +05:30
c9edca64c8 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-11 14:51:56 +05:30
054323978c update 2024-10-11 13:35:50 +05:30
YasinShaikh123
a12ab874cf Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-11 13:33:59 +05:30
YasinShaikh123
14b24ffa00 fawateer history maker 2024-10-11 13:33:27 +05:30
e47db6c840 uopdate 2024-10-10 17:41:41 +05:30
YasinShaikh123
48e53ea176 update status 2024-10-10 17:38:46 +05:30
36cf9fa610 update 2024-10-10 17:09:17 +05:30
8a6d9102cb Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-10 17:08:56 +05:30
YasinShaikh123
88bea104f6 approve Request api 2024-10-10 16:16:28 +05:30
417164cb16 update 2024-10-10 16:12:46 +05:30
YasinShaikh123
c0ce32219e Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-10 15:47:58 +05:30
YasinShaikh123
aff088c7b7 FawateerApprove modal 2024-10-10 15:47:15 +05:30
a7d3703244 checker maker update 2024-10-10 15:39:23 +05:30
c41deb0534 updaate 2024-10-10 12:11:27 +05:30
92ebf64223 upate 2024-10-09 23:23:50 +05:30
36a68e2169 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-09 17:57:16 +05:30
46aa0c4631 update 2024-10-09 17:57:11 +05:30
YasinShaikh123
bd48e3fb06 date update 2024-10-07 17:22:51 +05:30
YasinShaikh123
90a433e312 deleteion date 2024-10-07 17:10:26 +05:30
8cd6a65143 notification sr fix 2024-10-05 20:59:41 +05:30
105f103fda IsAppNotificationEnabled value refactored 2024-10-05 20:41:26 +05:30
aa6c61e4a4 kyc & notification fix 2024-10-05 20:37:17 +05:30
b08030a412 notification fix 2024-10-05 20:14:59 +05:30
YasinShaikh123
149667436e updated Kyc 2024-10-04 18:06:38 +05:30
YasinShaikh123
bb724d88d1 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-04 11:55:47 +05:30
YasinShaikh123
1fabbd9b77 mobile changes 2024-10-04 11:55:40 +05:30
c51ae9081f 0.7 2024-10-03 13:48:09 +05:30
cfd811708a 0.6 2024-10-02 18:59:42 +05:30
6630723092 0.5 2024-10-02 18:55:32 +05:30
2ae81ef032 upated 2024-10-02 18:41:38 +05:30
10221c03d9 0.3 2024-10-02 14:21:26 +05:30
084641c561 0.2 2024-10-02 14:02:14 +05:30
7b24db3e00 WFH update 0.1 2024-10-02 14:01:31 +05:30
f8b33e3eb5 update 2024-10-02 13:32:35 +05:30
9fa42845c7 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel 🐞 2024-10-01 17:48:42 +05:30
YasinShaikh123
4bded669bf update 2024-10-01 17:45:35 +05:30
bc8d986f29 update 2024-10-01 17:44:15 +05:30
f5497ea6ac update 2024-10-01 17:44:06 +05:30
YasinShaikh123
7f1ef99b6c update amount👍 2024-09-24 18:43:51 +05:30
28978622f3 update 2024-09-23 15:21:42 +05:30
YasinShaikh123
9dca99b1c1 update active bg 2024-09-23 12:25:30 +05:30
YasinShaikh123
8546ca3247 update bank 2024-09-20 18:04:58 +05:30
YasinShaikh123
ae1cdc8811 update Bank Details👍 2024-09-20 18:04:22 +05:30
YasinShaikh123
1a1910c58c changes admin 2024-09-19 16:45:05 +05:30
YasinShaikh123
fdb9ccefa9 update bank country name👍 2024-09-17 15:39:39 +05:30
YasinShaikh123
a126d6515d admin pages Working👷‍♂️ 2024-09-17 15:33:27 +05:30
YasinShaikh123
ab175b4c76 table changes👨‍⚕️ 2024-09-13 19:43:38 +05:30
YasinShaikh123
c9c7a7be69 investor💲 2024-09-11 16:39:51 +05:30
YasinShaikh123
da70893c2b remove investort Currency Toggle Btn 2024-09-10 19:15:05 +05:30
YasinShaikh123
a53069f6f6 investor portfolio 2024-09-10 17:55:48 +05:30
YasinShaikh123
8b43ff77a9 withdrawal changes 2024-09-10 15:54:58 +05:30
YasinShaikh123
8cb693a4c4 investor changes 2024-09-10 12:54:09 +05:30
YasinShaikh123
7b91ad2720 Merge branch 'main' of https://github.com/WDI-Ideas/tanami_admin into dev 2024-09-10 12:00:08 +05:30
YasinShaikh123
416c5ce8aa Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-09-10 11:51:08 +05:30
a0a1849a79 Investment name update in portfolio 2024-09-09 21:46:07 +05:30
YasinShaikh123
a1d7b1e9f9 modal comma 2024-09-09 17:49:49 +05:30
3a327d63e0 Exit update 2024-09-03 18:03:20 +05:30
753f3d40c3 Risky Commit ☠️ 2024-09-03 13:45:57 +05:30
YasinShaikh123
226b8a39bc portfolio switch btn 2024-09-03 13:14:49 +05:30
fdbdc61cac Commit before realese 🎰 2024-09-03 13:10:31 +05:30
d1cc24a43b Table mod 🎨 2024-09-02 18:02:19 +05:30
c766b29c4b merge 📌 2024-09-02 17:43:54 +05:30
4ee94c1261 Merge branch 'dev' 📌 2024-09-02 17:43:28 +05:30
YasinShaikh123
c2d75c340c investor bankDetails 2024-09-02 17:41:00 +05:30
8cb25e9189 Merge branch 'dev' 🕊️ 2024-09-02 16:22:07 +05:30
d74481e7f4 [ Refetch update ] 🎭 2024-09-02 16:20:51 +05:30
YasinShaikh123
2281550345 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-09-02 16:12:20 +05:30
YasinShaikh123
6ca97a7ed3 failed gif 2024-09-02 16:12:13 +05:30
4b3313b1ea Table Update 🎰 2024-09-02 15:48:46 +05:30
YasinShaikh123
7a8ea42832 transection amount 2024-09-02 15:44:37 +05:30
1be4a00ced Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-09-02 15:35:12 +05:30
YasinShaikh123
c5d9b32e8d Revert "update"
This reverts commit 4957bd5032.
2024-09-02 15:31:57 +05:30
YasinShaikh123
4957bd5032 update 2024-09-02 15:28:40 +05:30
YasinShaikh123
f151b67e49 Io nav details date 2024-09-02 15:28:34 +05:30
b92de5e990 [Risky Update Two] 2024-08-30 18:41:14 +05:30
33bbd5f3e6 [Risky Update] 2024-08-30 18:16:38 +05:30
YasinShaikh123
0ab8ac73aa bank details 2024-08-30 18:15:58 +05:30
410f18f512 exit resoved 2024-08-30 18:03:11 +05:30
29fdb99366 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel 2024-08-30 17:49:01 +05:30
YasinShaikh123
867ad9e7b9 doller update 2024-08-30 17:47:10 +05:30
92fe9f66f9 final fixes 2024-08-30 17:42:27 +05:30
d2d18d435f align updated 2024-08-30 16:53:31 +05:30
ede34c1b41 color update 2024-08-30 16:50:05 +05:30
639a6cfae9 [Exit resolved] 2024-08-30 16:26:30 +05:30
68026ddd66 updated final fixes 2024-08-30 16:09:07 +05:30
YasinShaikh123
be451086b9 Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-30 13:39:07 +05:30
YasinShaikh123
566d170226 update changes 2024-08-30 12:07:39 +05:30
60d3c7572d table updated 2024-08-30 12:07:10 +05:30
YasinShaikh123
dceec17c58 header ID 2024-08-29 17:02:43 +05:30
YasinShaikh123
a041cea256 Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-28 19:35:43 +05:30
YasinShaikh123
91e362d6c2 mobile changes 2024-08-28 19:35:39 +05:30
b3c0662e77 update payment routes 2024-08-28 19:35:07 +05:30
YasinShaikh123
86ce31e86c Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-26 20:28:41 +05:30
YasinShaikh123
0df81c1169 date formate 2024-08-26 20:28:39 +05:30
31eb1605bf Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-26 19:54:15 +05:30
8dc5eecad9 upmerge 2024-08-26 19:54:10 +05:30
YasinShaikh123
508d7fe5b5 changes date 2024-08-26 19:53:59 +05:30
YasinShaikh123
9208535613 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-26 18:59:39 +05:30
YasinShaikh123
9916d6618e amount changes 2024-08-26 18:59:29 +05:30
2f7f907a89 Updated Sr No 2024-08-26 18:58:58 +05:30
8b0454b16a updated 2024-08-26 16:20:54 +05:30
YasinShaikh123
f7b06bfd19 pagination 2024-08-26 16:05:03 +05:30
YasinShaikh123
64b881dd89 bugs 2024-08-26 14:12:56 +05:30
YasinShaikh123
fba3ad2212 Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-26 12:40:59 +05:30
YasinShaikh123
026a563d84 error message 2024-08-26 12:32:45 +05:30
b713845df6 update 2024-08-26 12:11:38 +05:30
79bcf851d9 [Coma update after sprint 5] 2024-08-23 22:18:50 +05:30
3a7d174766 coma updated after sprint 5 2024-08-23 22:01:17 +05:30
YasinShaikh123
6d315ba2ef comma changes 2024-08-23 20:46:55 +05:30
YasinShaikh123
7748872e29 Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-23 19:44:35 +05:30
YasinShaikh123
b5f7250691 comma 2024-08-23 19:44:26 +05:30
0db739c557 update 2024-08-23 19:44:03 +05:30
3165c11af7 update 2024-08-23 18:09:31 +05:30
c97de1314e updated 2024-08-23 18:06:32 +05:30
287fb80f9e Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel 2024-08-23 18:03:29 +05:30
b690a55ed3 updated 2024-08-23 18:03:18 +05:30
YasinShaikh123
b8390bfad9 profile update 2024-08-23 18:02:50 +05:30
YasinShaikh123
5e4ebe3fb5 Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-23 18:00:53 +05:30
YasinShaikh123
8b97add9e0 portpolio api 2024-08-23 18:00:44 +05:30
25226d5464 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-23 16:22:43 +05:30
3131ff8610 update 2024-08-23 16:22:40 +05:30
YasinShaikh123
187841b178 withdraw request 2024-08-23 16:22:04 +05:30
YasinShaikh123
849b89efc8 Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-23 13:06:23 +05:30
116db3b922 updated 2024-08-23 13:05:59 +05:30
YasinShaikh123
4313057012 conflict mrg 2024-08-23 12:49:43 +05:30
YasinShaikh123
cdd0d670ba Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-23 12:48:50 +05:30
7c3a6112d2 update delewtion 2024-08-23 12:24:34 +05:30
e3ccc3efbc Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel 2024-08-23 12:24:19 +05:30
YasinShaikh123
129a79c669 drawar history 2024-08-23 12:23:44 +05:30
b7ce3de128 update 2024-08-22 18:48:45 +05:30
YasinShaikh123
97cfd45545 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-22 18:47:49 +05:30
YasinShaikh123
e0664d8e21 drawal request ui 2024-08-22 18:47:45 +05:30
d009848068 update 2024-08-22 14:43:57 +05:30
YasinShaikh123
afb538cc22 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-22 12:10:44 +05:30
YasinShaikh123
e0b48abfd5 drawal request ui 2024-08-22 12:10:34 +05:30
9083d94813 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-22 12:10:11 +05:30
323d09f88d [ Displlay order updated ] 2024-08-22 12:10:07 +05:30
46d8031d4d deposite api 2024-08-21 16:04:05 +05:30
fb39e32b95 update endPoints 2024-08-21 13:03:50 +05:30
670e4d36a9 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-20 20:24:40 +05:30
YasinShaikh123
26777e2ada investor-amount 2024-08-20 20:22:37 +05:30
51f3b512a7 update 2024-08-20 20:22:19 +05:30
YasinShaikh123
d7012f2692 selected css 2024-08-20 19:10:58 +05:30
YasinShaikh123
e07a92ba03 mobile downloader 2024-08-20 15:55:06 +05:30
YasinShaikh123
adb4bd5d27 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into feature-dev 2024-08-20 14:53:05 +05:30
cfda3264fc [.env update] 2024-08-20 14:52:13 +05:30
YasinShaikh123
1569afe4f0 mobile height 2024-08-20 13:47:46 +05:30
YasinShaikh123
4a3072b4d3 io mobile 2024-08-20 13:00:22 +05:30
4516c70406 Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel 2024-08-20 12:19:06 +05:30
50f87869be Edit update 2024-08-20 12:19:02 +05:30
d69e4a203f Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel 2024-08-20 11:26:40 +05:30
6e4c794d2b update 2024-08-17 20:38:06 +05:30
dfbc1ad338 finalUp 2024-08-16 20:44:13 +05:30
aa1c0c994a updted 2024-08-16 20:30:42 +05:30
cecd8dd5e0 final Commit 2024-08-16 20:24:25 +05:30
620b365437 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-16 20:03:54 +05:30
2e5ecb967f update 2024-08-16 20:03:50 +05:30
3df4ed8df3 [Updated]=Final 2024-08-16 19:50:51 +05:30
6f514fe121 Merge branch 'feature-dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel 2024-08-16 19:44:55 +05:30
125d6e6ae3 [Update]=final 2024-08-16 19:44:29 +05:30
YasinShaikh123
68d6bd7c9e io header 2024-08-16 19:26:02 +05:30
e7aef39fa7 rixky update 2024-08-16 18:42:34 +05:30
71f8c8a98d update final 2024-08-16 18:31:04 +05:30
08559eb833 Merge branch 'feature-dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel 2024-08-16 18:30:43 +05:30
fc7d8ac0e9 updated final 2024-08-16 18:29:33 +05:30
YasinShaikh123
692e08abd6 investor view 2024-08-16 18:26:25 +05:30
a0b722bd12 final update 2024-08-16 18:23:55 +05:30
YasinShaikh123
34c51c4c4c Merge branch 'feature-dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into feature-dev 2024-08-16 17:59:44 +05:30
YasinShaikh123
b5a960f7df investor details 2024-08-16 17:59:39 +05:30
1f97c841f7 updated investor in IO 2024-08-16 16:33:20 +05:30
ba9efdd48b update sorting 2024-08-16 15:44:30 +05:30
YasinShaikh123
e103258995 tabs padding 2024-08-16 15:24:18 +05:30
YasinShaikh123
9d404a58f2 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into feature-dev 2024-08-16 15:23:51 +05:30
YasinShaikh123
24d4926f75 Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into feature-dev 2024-08-16 15:12:42 +05:30
YasinShaikh123
ee8f905d3a mobile view 2024-08-16 15:12:37 +05:30
568b2f716a finalUpdate Fri 16 Aug 2024-08-16 15:02:02 +05:30
27a975fe8a Merge branch 'feature-dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-14 20:48:27 +05:30
YasinShaikh123
395d60fd2f mobile modal 2024-08-14 20:48:12 +05:30
78c7f55673 Merge branch 'feature-dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-14 20:33:06 +05:30
YasinShaikh123
b308e22b6d updated 2024-08-14 20:31:04 +05:30
c9ad4a4377 update 2024-08-14 18:48:25 +05:30
2d641a9748 updated 2024-08-14 12:19:27 +05:30
83aa170c60 updated 2024-08-13 19:58:31 +05:30
YasinShaikh123
54e37a3703 mobile banner 2024-08-13 19:58:16 +05:30
8dae36daaf updated 13-Aug 2024-08-13 19:02:47 +05:30
84fb0d77cf Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-13 16:56:29 +05:30
94d3e25cce updated 2024-08-13 16:56:17 +05:30
YasinShaikh123
f17f77d409 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-13 13:47:52 +05:30
YasinShaikh123
51a4bc7917 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-13 13:47:29 +05:30
YasinShaikh123
78d573037d dashboard graph 2024-08-13 13:47:25 +05:30
5b1f89efc9 updateed 2024-08-13 13:46:41 +05:30
YasinShaikh123
7de0a679e0 dashboard charts 2024-08-12 17:23:56 +05:30
8cc16ddc8a updated 2024-08-12 17:22:04 +05:30
60b0263133 update 2024-08-12 16:19:47 +05:30
fd45d6aeef update nav 2024-08-12 15:48:55 +05:30
f7a8da789c update 2024-08-12 15:35:39 +05:30
YasinShaikh123
e0765514f5 deposite api changes 2024-08-12 12:34:56 +05:30
0393a18762 ionav updated 2024-08-12 12:22:01 +05:30
e8b9a4af40 update friday 2024-08-09 19:22:18 +05:30
dce1a09f98 dashboard update 2024-08-09 15:37:52 +05:30
1de2710cd1 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-09 12:24:48 +05:30
YasinShaikh123
66afc30a14 add switch btn 2024-08-09 12:24:29 +05:30
e657409edb update dashboard 2024-08-09 12:24:18 +05:30
9f6136935d Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-08 19:39:39 +05:30
26363e2b0c updatw 2024-08-08 19:38:17 +05:30
YasinShaikh123
0e7f954d20 sponser button 2024-08-08 19:37:14 +05:30
9da83b8297 token condition update 2024-08-07 20:27:34 +05:30
591e0c92a0 token update 2024-08-07 20:24:38 +05:30
YasinShaikh123
e234ce3181 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-07 20:19:18 +05:30
YasinShaikh123
4c6df2cc2f deposite reject 2024-08-07 20:19:15 +05:30
2a11fed4f0 UPDATED 070824 2024-08-07 20:18:36 +05:30
2b4896fa9b update 2024-08-06 13:10:24 +05:30
YasinShaikh123
2202305d28 deposit table 2024-08-05 20:11:13 +05:30
059275b310 contact updted 2024-08-05 20:10:07 +05:30
YasinShaikh123
d42cdf0a50 deposite table api 2024-08-05 17:58:01 +05:30
YasinShaikh123
5ca4dad151 Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-05 17:57:43 +05:30
eaf6394a4f update main 2024-08-05 17:56:07 +05:30
6b9dc86a5e Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-02 20:20:07 +05:30
60a6a053b3 updated 2024-08-02 20:19:41 +05:30
YasinShaikh123
8d5788ce02 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-02 20:19:19 +05:30
YasinShaikh123
3e3c47149a Deposit Table 2024-08-02 20:19:17 +05:30
192 changed files with 27400 additions and 5421 deletions

View File

@@ -2,7 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/src/assets/favicon.png" /> <link rel="icon" type="image/svg+xml" href="/src/assets/favicons.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tanami Admin</title> <title>Tanami Admin</title>
</head> </head>
@@ -22,7 +22,7 @@ const craftedMsg = "Crafted with ❤️ by WDI Team for a better web.";
</script> </script>
<script type="text/javascript"> <!-- <script type="text/javascript">
function googleTranslateElementInit() { function googleTranslateElementInit() {
new google.translate.TranslateElement({ new google.translate.TranslateElement({
pageLanguage: 'en', pageLanguage: 'en',
@@ -30,7 +30,10 @@ const craftedMsg = "Crafted with ❤️ by WDI Team for a better web.";
layout: google.translate.TranslateElement.InlineLayout.SIMPLE layout: google.translate.TranslateElement.InlineLayout.SIMPLE
}, 'google_translate_element'); }, 'google_translate_element');
} }
</script> </script> -->
<script type="text/javascript" src="//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script> <!-- <script type="text/javascript" src="//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script> -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/5.3.0/js/bootstrap.bundle.min.js"></script>
</body> </body>
</html> </html>

2367
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -16,16 +16,22 @@
"@emotion/styled": "^11.11.5", "@emotion/styled": "^11.11.5",
"@hookform/resolvers": "^3.3.4", "@hookform/resolvers": "^3.3.4",
"@reduxjs/toolkit": "^2.2.3", "@reduxjs/toolkit": "^2.2.3",
"apexcharts": "^3.52.0",
"axios": "^1.7.2", "axios": "^1.7.2",
"bootstrap": "5.3.3", "bootstrap": "5.3.3",
"chart.js": "^4.4.3",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"framer-motion": "^11.1.5", "framer-motion": "^11.1.5",
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
"quill": "^2.0.2",
"react": "^18.2.0", "react": "^18.2.0",
"react-apexcharts": "^1.4.1",
"react-beautiful-dnd": "^13.1.1", "react-beautiful-dnd": "^13.1.1",
"react-chartjs-2": "^5.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-hook-form": "^7.51.3", "react-hook-form": "^7.51.3",
"react-icons": "^5.1.0", "react-icons": "^5.1.0",
"react-phone-input-2": "^2.15.1",
"react-quill": "^2.0.0", "react-quill": "^2.0.0",
"react-redux": "^9.1.1", "react-redux": "^9.1.1",
"react-router-dom": "^6.22.3", "react-router-dom": "^6.22.3",

View File

@@ -14,6 +14,11 @@
/* font-family: "Lato", sans-serif !important; */ /* font-family: "Lato", sans-serif !important; */
} }
::selection {
background-color: #004717; /* Change this to your desired color */
color: white; /* Optional: Change the text color */
}
.pointer { .pointer {
cursor: pointer !important; cursor: pointer !important;
} }
@@ -336,3 +341,151 @@
font-size: 22px !important; font-size: 22px !important;
} }
} }
/* ========= [ switch BTN ============ */
/* From Uiverse.io by Nawsome */
.switch {
display: block;
background-color: black;
width: 85px;
height: 115px;
box-shadow: 0 0 10px 2px rgba(0, 0, 0, 0.2), 0 0 1px 2px black, inset 0 2px 2px -2px white, inset 0 0 2px 15px #47434c, inset 0 0 2px 22px black;
border-radius: 5px;
padding: 20px;
perspective: 700px;
}
.switch input {
display: none;
}
.switch input:checked + .button {
transform: translateZ(20px) rotateX(25deg);
box-shadow: 0 -10px 20px #ff1818;
}
.switch input:checked + .button .light {
animation: flicker 0.2s infinite 0.3s;
}
.switch input:checked + .button .shine {
opacity: 1;
}
.switch input:checked + .button .shadow {
opacity: 0;
}
.switch .button {
display: block;
transition: all 0.3s cubic-bezier(1, 0, 1, 1);
transform-origin: center center -20px;
transform: translateZ(20px) rotateX(-25deg);
transform-style: preserve-3d;
background-color: #9b0621;
height: 100%;
position: relative;
cursor: pointer;
background: linear-gradient(#980000 0%, #6f0000 30%, #6f0000 70%, #980000 100%);
background-repeat: no-repeat;
}
.switch .button::before {
content: "";
background: linear-gradient(rgba(255, 255, 255, 0.8) 10%, rgba(255, 255, 255, 0.3) 30%, #650000 75%, #320000) 50% 50%/97% 97%, #b10000;
background-repeat: no-repeat;
width: 100%;
height: 50px;
transform-origin: top;
transform: rotateX(-90deg);
position: absolute;
top: 0;
}
.switch .button::after {
content: "";
background-image: linear-gradient(#650000, #320000);
width: 100%;
height: 58px;
transform-origin: top;
transform: translateY(50px) rotateX(-90deg);
position: absolute;
bottom: 0;
box-shadow: 0 50px 8px 0px black, 0 80px 20px 0px rgba(0, 0, 0, 0.5);
}
.switch .light {
opacity: 0;
animation: light-off 1s;
position: absolute;
width: 100%;
height: 100%;
background-image: radial-gradient(#ffc97e, #ff1818 40%, transparent 70%);
}
.switch .dots {
position: absolute;
width: 100%;
height: 100%;
background-image: radial-gradient(transparent 30%, rgba(101, 0, 0, 0.7) 70%);
background-size: 10px 10px;
}
.switch .characters {
position: absolute;
width: 100%;
height: 100%;
background: linear-gradient(white, white) 50% 20%/5% 20%, radial-gradient(circle, transparent 50%, white 52%, white 70%, transparent 72%) 50% 80%/33% 25%;
background-repeat: no-repeat;
}
.switch .shine {
transition: all 0.3s cubic-bezier(1, 0, 1, 1);
opacity: 0.3;
position: absolute;
width: 100%;
height: 100%;
background: linear-gradient(white, transparent 3%) 50% 50%/97% 97%, linear-gradient(rgba(255, 255, 255, 0.5), transparent 50%, transparent 80%, rgba(255, 255, 255, 0.5)) 50% 50%/97% 97%;
background-repeat: no-repeat;
}
.switch .shadow {
transition: all 0.3s cubic-bezier(1, 0, 1, 1);
opacity: 1;
position: absolute;
width: 100%;
height: 100%;
background: linear-gradient(transparent 70%, rgba(0, 0, 0, 0.8));
background-repeat: no-repeat;
}
@keyframes flicker {
0% {
opacity: 1;
}
80% {
opacity: 0.8;
}
100% {
opacity: 1;
}
}
@keyframes light-off {
0% {
opacity: 1;
}
80% {
opacity: 0;
}
}

View File

@@ -6,6 +6,7 @@ import {
Routes, Routes,
Route, Route,
Navigate, Navigate,
useNavigate,
} from "react-router-dom"; } from "react-router-dom";
import "./App.css"; // Import CSS file import "./App.css"; // Import CSS file
import DefaultLayout from "./Layout/DefaultLayout"; import DefaultLayout from "./Layout/DefaultLayout";
@@ -14,6 +15,8 @@ import Login from "./Pages/Login";
import GlobalStateContext from "./Contexts/GlobalStateContext"; import GlobalStateContext from "./Contexts/GlobalStateContext";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import NoInternetScreen from "./Pages/NoInternetScreen"; import NoInternetScreen from "./Pages/NoInternetScreen";
import Welcome from "./Pages/PaymentSuccess";
import PaymentFailed from "./Pages/PaymentFailed";
const App = () => { const App = () => {
// const { isAuthenticate } = useSelector((state) => state?.auth); // const { isAuthenticate } = useSelector((state) => state?.auth);
@@ -24,6 +27,8 @@ const App = () => {
const [isOnline, setIsOnline] = useState(navigator.onLine); const [isOnline, setIsOnline] = useState(navigator.onLine);
useEffect(() => { useEffect(() => {
const handleOnlineStatusChange = () => { const handleOnlineStatusChange = () => {
setIsOnline(navigator.onLine); setIsOnline(navigator.onLine);
}; };
@@ -37,22 +42,30 @@ const App = () => {
}; };
}, []); }, []);
const PrivateRoute = ({ children }) => { // const token = localStorage.getItem('accessToken')
if (!isAuthenticate && isAuthenticatedInCookie !== "true") { // console.log(token);
return <Navigate to="/login" replace />;
}
return children; // const PrivateRoute = ({ children }) => {
}; // if (!isAuthenticate && isAuthenticatedInCookie !== "true") {
// return <Navigate to="/login" replace />;
// }
// return children;
// };
return ( return (
<Router> <Router>
<Routes> <Routes>
<Route path="/login" element={<Login />} /> <Route path="/login" element={<Login />} />
<Route path="/payment-success" element={<Welcome />} />
<Route path="/payment-failed" element={<PaymentFailed />} />
<Route <Route
path="/*" path="/*"
element={ element={
// isOnline ? ( // isOnline ? (
isAuthenticate || isAuthenticatedInCookie === "true" ? ( // isAuthenticate || isAuthenticatedInCookie === "true" ? (
localStorage.getItem('accessToken') && localStorage.getItem('refreshToken') ? (
// true ? (
<DefaultLayout isOnline={isOnline} /> <DefaultLayout isOnline={isOnline} />
) : ( ) : (
<Login /> <Login />

View File

@@ -151,7 +151,7 @@ const CreateIO = () => {
}, []); }, []);
const tableHeadRow = [ const tableHeadRow = [
"Sponsorer name", "Sponsor name",
"Address", "Address",
"Mobile no", "Mobile no",
"Created At", "Created At",
@@ -191,7 +191,7 @@ const CreateIO = () => {
const extractedArray = filteredData?.map((item) => ({ const extractedArray = filteredData?.map((item) => ({
id: item?.id, id: item?.id,
"Sponsorer name": ( "Sponsor name": (
<Text <Text
justifyContent={"left"} justifyContent={"left"}
as={"span"} as={"span"}
@@ -413,7 +413,7 @@ const CreateIO = () => {
], ],
}, },
{ {
label: "Sponsorer Name (English)", label: "Sponsor Name (English)",
placeHolder: " ", placeHolder: " ",
name: "sponserName", name: "sponserName",
type: "text", type: "text",

View File

@@ -0,0 +1,91 @@
import React, { forwardRef } from 'react';
import { Input } from "@chakra-ui/react";
export const formatCurrency = (value) => {
if (value === undefined || value === null) return '';
const [integer, decimal] = String(value).split('.');
const formattedInteger = integer.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return decimal !== undefined ? `${formattedInteger}.${decimal}` : formattedInteger;
};
const CurrencyInput = forwardRef(({ value, onChange, ...props }, ref) => {
const handleChange = (event) => {
let { value } = event?.target;
// Remove non-numeric characters except decimal point
value = value.replace(/[^0-9.]/g, '');
// Ensure only one decimal point
const parts = value.split('.');
if (parts.length > 2) {
value = parts[0] + '.' + parts.slice(1).join('');
}
// Restrict to two decimal places
if (parts[1]?.length > 2) {
value = parts[0] + '.' + parts[1].slice(0, 2);
}
onChange(value); // Pass the raw value to parent
};
return (
<Input
{...props}
ref={ref} // Forward ref here
type="text"
focusBorderColor="forestGreen.300"
value={formatCurrency(value)}
onChange={handleChange}
/>
);
});
export default CurrencyInput;
// import React, { forwardRef } from 'react';
// import { Input } from "@chakra-ui/react";
// export const formatCurrency = (value) => {
// if (value === undefined || value === null) return ''; // Handle undefined or null values
// const [integer, decimal] = String(value).split('.'); // Convert value to string before splitting
// const formattedInteger = integer.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
// return decimal ? `${formattedInteger}.${decimal}` : formattedInteger;
// };
// const CurrencyInput = forwardRef(({ value, onChange, ...props }, ref) => {
// const handleChange = (event) => {
// let { value } = event?.target;
// // Remove non-numeric characters except decimal point
// value = value?.replace(/[^0-9.]/g, '');
// // Ensure only one decimal point and restrict to two decimal places
// const parts = value?.split('.');
// if (parts.length > 2) {
// value = parts[0] + '.' + parts?.slice(1)?.join('');
// }
// if (parts[1]?.length > 2) {
// value = parts[0] + '.' + parts[1]?.slice(0, 2);
// }
// onChange(value); // Pass the raw value to parent or use it directly
// };
// return (
// <Input
// {...props}
// ref={ref} // Forward ref here
// type="text"
// value={formatCurrency(value)}
// onChange={handleChange}
// />
// );
// });
// export default CurrencyInput;

View File

@@ -50,7 +50,7 @@ const DataTable = ({
const [removed] = reorderedItems.splice(result.source.index, 1); const [removed] = reorderedItems.splice(result.source.index, 1);
reorderedItems.splice(result.destination.index, 0, removed); reorderedItems.splice(result.destination.index, 0, removed);
setData(reorderedItems) setData(reorderedItems)
console.log("New Order:", reorderedItems.map((item, index) => ({ index, item }))); // console.log("New Order:", reorderedItems.map((item, index) => ({ index, item })));
}; };
return ( return (
@@ -65,7 +65,7 @@ const DataTable = ({
{/* <Box mb={2}>{caption}</Box> */} {/* <Box mb={2}>{caption}</Box> */}
<Table size="sm" {...provided.droppableProps} ref={provided.innerRef}> <Table size="sm" {...provided.droppableProps} ref={provided.innerRef}>
<TableCaption p={0}>{caption}</TableCaption> <TableCaption p={0}>{caption}</TableCaption>
<Thead backgroundColor="gray.50"> <Thead backgroundColor="forestGreen.100">
<Tr> <Tr>
{tableHeadRow.map((heading, index) => ( {tableHeadRow.map((heading, index) => (
<Th <Th
@@ -97,7 +97,7 @@ const DataTable = ({
bg: "blue.50", bg: "blue.50",
cursor: "grab", cursor: "grab",
}} }}
bg={snapshot.isDragging ? "blue.100" : "white"} bg={snapshot.isDragging ? "blue.100" : index % 2 ? "white" : "forestGreen.50"}
boxShadow={snapshot.isDragging ? "0 0 1em rgba(0, 0, 0, 0.2)" : "none"} boxShadow={snapshot.isDragging ? "0 0 1em rgba(0, 0, 0, 0.2)" : "none"}
> >
@@ -121,7 +121,7 @@ const DataTable = ({
)} )}
</Draggable> </Draggable>
) : ( ) : (
<Tr key={index}> <Tr bg={index % 2 ? "forestGreen.50" : "white"} key={index}>
{tableHeadRow.map((heading, i) => ( {tableHeadRow.map((heading, i) => (
<Td <Td
textAlign={tableHeadRow.length - 1 === i || centered ? "center" : "left"} textAlign={tableHeadRow.length - 1 === i || centered ? "center" : "left"}

View File

@@ -9,70 +9,171 @@ import {
Tr, Tr,
Skeleton, Skeleton,
TableCaption, TableCaption,
Tfoot, Checkbox,
Radio,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import EmptySearchList from "../EmptySearchList"; import EmptySearchList from "../EmptySearchList";
import { TABLE_PAGINATION } from "../../Constants/Paginations";
const DataTable = ({ const NormalTable = ({
data, data,
isLoading, isLoading,
tableHeadRow, tableHeadRow,
emptyMessage, emptyMessage,
centered, centered,
total,
showRadioButton, // Prop for showing the checkboxes
selectedRadio,
setSelectedRadio, // State for managing selected checkboxes
handleCheckboxChange: radioChange,
radio
}) => { }) => {
const columnWidth = const columnWidth =
data && data[0] data && data[0]
? `${(100 / Object.keys(data[0]).length).toFixed(2)}%` ? `${(100 / Object.keys(data[0]).length).toFixed(2)}%`
: "auto"; : "auto";
// Toggle checkbox selection for individual rows
// const handleCheckboxChange = (value) => {
// setSelectedRadio((prev) => {
// if (prev.includes(value)) {
// // Remove if already selected
// return prev.filter((id) => id !== value);
// } else {
// // Add to selected
// return [...prev, value];
// }
// });
// };
// Handle "Check All" checkbox
const handleCheckAllChange = () => {
if (selectedRadio.length === data.length) {
setSelectedRadio([]); // Deselect all if already selected
} else {
const allIds = data.map((item) => item.id);
setSelectedRadio(allIds); // Select all
}
};
const handleCheckboxChange = (value) => {
if (radio) {
// If radio is true, select only one option
setSelectedRadio([value]); // Set the selected radio to this value only
} else {
// Handle multiple selection for checkboxes
setSelectedRadio((prev) => {
if (prev.includes(value)) {
// Remove if already selected
return prev.filter((id) => id !== value);
} else {
// Add to selected
return [...prev, value];
}
});
}
};
return ( return (
<TableContainer overflowX={"hidden"} className="h-auto mb-3 w-100"> <TableContainer overflowX={"auto"} className="h-auto w-100 table-scroll">
{data?.length === 0 ? ( {data?.length === 0 ? (
<EmptySearchList message={emptyMessage} /> <EmptySearchList message={emptyMessage} />
) : ( ) : (
<Table size="sm"> <Table size="sm">
<TableCaption>Tanami v1.0.0</TableCaption> <TableCaption p={total ? 0 : null}>
<Thead backgroundColor="gray.50"> {total ? total : "Tanami v1.0.0"}
</TableCaption>
<Thead bg="forestGreen.100">
<Tr> <Tr>
{showRadioButton &&(
<Th
color={"purple.900"}
textAlign={"center"}
p={4}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
textTransform={"none"}
>
{radio? "Select":<Checkbox
isChecked={selectedRadio?.length === data?.length}
onChange={handleCheckAllChange}
colorScheme="forestGreen"
/>}
</Th>
)}
{tableHeadRow.map((heading, index) => ( {tableHeadRow.map((heading, index) => (
<Th <Th
width={'fit-content'} color={"purple.900"}
textAlign={ textAlign={
tableHeadRow.length - 1 === index || centered tableHeadRow.length - 1 === index || centered
? "center" ? "center"
: "left" : "left"
} }
key={index} key={index}
p={3} p={4}
w={columnWidth} whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
textTransform={"none"}
> >
{isLoading ? <Skeleton height="20px" /> : heading} {isLoading ? <Skeleton height="20px" /> : heading}
{/* {heading} */}
</Th> </Th>
))} ))}
</Tr> </Tr>
</Thead> </Thead>
<Tbody className="web-text-small"> <Tbody className="web-text-small">
{isLoading {isLoading
? Array.from({ length: 12 }).map((_, index) => ( ? Array.from({ length: TABLE_PAGINATION?.size }).map(
<Tr key={index}> (_, index) => (
{tableHeadRow.map((_, i) => ( <Tr
<Td bg={index % 2 === 0 ? "white" : "forestGreen.50"}
width={'fit-content'} key={index}
key={i} >
style={{ {tableHeadRow.map((_, i) => (
whiteSpace: "nowrap", <Td
textOverflow: "ellipsis", key={i}
}} style={{
className="web-text-small" whiteSpace: "nowrap",
w={columnWidth} textOverflow: "ellipsis",
> }}
<Skeleton height="20px" mb={1} mt={1} /> className="web-text-small"
</Td> >
))} <Skeleton height="20px" mb={1} mt={1} />
</Tr> </Td>
)) ))}
</Tr>
)
)
: data?.map((item, index) => ( : data?.map((item, index) => (
<Tr key={index}> <Tr
cursor={"pointer"}
transition={"0.2s all"}
maxH={8}
bg={index % 2 === 0 ? "" : "forestGreen.50"}
key={index}
>
{showRadioButton && (
<Td textAlign={"center"}>
{radio ? <Radio
bg={"#fff"}
colorScheme="forestGreen"
value={item.id}
isChecked={selectedRadio.includes(item.id)}
onChange={() => radioChange(item.id, item)}
/>:<Checkbox
bg={"#fff"}
colorScheme="forestGreen"
value={item.id}
isChecked={selectedRadio.includes(item.id)}
onChange={() => handleCheckboxChange(item.id)}
/>}
</Td>
)}
{tableHeadRow.map((heading, i) => ( {tableHeadRow.map((heading, i) => (
<Td <Td
textAlign={ textAlign={
@@ -82,6 +183,7 @@ const DataTable = ({
} }
color={"gray.600"} color={"gray.600"}
key={i} key={i}
fontWeight={500}
style={{ style={{
whiteSpace: "nowrap", whiteSpace: "nowrap",
textOverflow: "ellipsis", textOverflow: "ellipsis",
@@ -100,4 +202,4 @@ const DataTable = ({
); );
}; };
export default DataTable; export default NormalTable;

View File

@@ -0,0 +1,55 @@
import React, { useState } from 'react';
import ApexCharts from 'react-apexcharts';
const ApexChart = ({ data }) => {
// Customize colors and series titles here
const [options] = useState({
chart: {
width: 600,
type: 'donut',
},
plotOptions: {
pie: {
startAngle: -90,
endAngle: 270,
donut: {
size: '45%' // Adjust the donut size here (percentage of chart size)
}
}
},
labels:data?.labels,
dataLabels: {
enabled: false
},
fill: {
type: 'gradient',
},
colors: data?.backgroundColor,
legend: {
show: false,
position: 'right',
labels: {
colors: ['#000'], // Customize the color of the legend labels
useSeriesColors: true
}
},
responsive: [{
breakpoint: 480,
options: {
chart: {
width: 500
},
legend: {
position: 'center'
}
}
}]
});
return (
<ApexCharts options={options} series={data?.values} type="donut" width={300} />
);
};
export default ApexChart;

View File

@@ -0,0 +1,66 @@
import React, { useState } from 'react';
import ReactApexChart from 'react-apexcharts';
function ApexLine() {
const [chartOptions, setChartOptions] = useState({
series: [{
name: 'Rate',
data: [45, 23, 70, 65, 5, 34, 32],
gradientToColors: ['#004017'],
}],
options: {
chart: {
height: 350,
type: 'line',
toolbar: {
show: false // Hide the action icons
}
},
stroke: {
width: 5,
curve: 'smooth',
colors: ['#598369'], // Customize the line color here
},
markers: {
size: 6, // Size of markers
colors: ['#004118'], // Marker (dot) color
strokeColor: '#fff', // Stroke color of the marker
strokeWidth: 2
},
xaxis: {
type: 'category', // Change from 'datetime' to 'category'
categories: ['BH', 'KW', 'OM', 'QA', 'SA', 'UAE', 'IND'],
tickAmount: 7
},
title: {
text: 'Exchange Rate Currency', // Adjust the title if needed
align: 'left',
style: {
fontSize: '15px',
color: '#000',
fontWeight: 400
}
},
fill: {
type: 'gradient',
gradient: {
shade: 'dark',
gradientToColors: ['#004017'],
shadeIntensity: 4,
type: 'horizontal',
opacityFrom: 1,
opacityTo: 1,
stops: [0, 100] // Gradient stops
},
}
}
});
return (
<div>
<ReactApexChart options={chartOptions.options} series={chartOptions.series} type="line" height={"100%"} width={"600"} />
</div>
);
}
export default ApexLine;

View File

@@ -0,0 +1,39 @@
// DonutChart.jsx
import React from 'react';
import { Doughnut } from 'react-chartjs-2';
import { Chart as ChartJS, Title, Tooltip, Legend, ArcElement } from 'chart.js';
ChartJS.register(Title, Tooltip, Legend, ArcElement);
const DonutChart = ({ data, width = 300, height = 250 }) => {
const chartData = {
labels: data.labels,
datasets: [
{
label: 'My Dataset',
data: data.values,
backgroundColor: [ '#3182ce', '#004118', '#D69E2E', '#E53E3E' ],
borderColor: ['#FFF'],
borderWidth: 2,
},
],
};
const options = {
responsive: true,
plugins: {
legend: {
display: false, // Hide the legend
},
tooltip: {
callbacks: {
label: (tooltipItem) => `${tooltipItem.label}: ${tooltipItem.raw}`,
},
},
},
};
return <Doughnut data={chartData} options={options} width={'100%'} />;
};
export default DonutChart;

View File

@@ -0,0 +1,80 @@
// LineChart.jsx
import React from 'react';
import { Line } from 'react-chartjs-2';
import { Chart as ChartJS, Title, Tooltip, Legend, LineElement, PointElement, LinearScale, CategoryScale } from 'chart.js';
// Register the necessary components
ChartJS.register( Title, Tooltip, Legend, LineElement, PointElement, LinearScale, CategoryScale );
// Sample options for the chart
// Sample options for the chart
const options = {
responsive: true,
plugins: {
legend: {
position: 'top',
},
tooltip: {
callbacks: {
label: function (context) {
let label = context.dataset.label || '';
if (label) {
label += ': ';
}
if (context.parsed.y !== null) {
label += `${context.parsed.y}`;
}
return label;
}
}
}
},
animation: {
tension: {
duration: 2000,
easing: 'linear',
from: 1,
to: 0,
loop: true
}
}
};
const Utils = {
numbers: ({ count, min, max }) => Array.from({ length: count }, () => Math.floor(Math.random() * (max - min + 1)) + min),
CHART_COLORS: {
red: 'rgba(255, 99, 132, 1)',
darkGreen: 'rgba(0, 65, 24, 1)' // Added color related to #004118
},
transparentize: (color, opacity) => {
// Use regex to replace the alpha value
return color.replace(/(rgba\(\d+, \d+, \d+, )\d+(\))/, `$1${opacity}$2`);
}
};
const LineChart = ({ width = 300, height = 250 }) => {
const data = {
labels: ['Bahrain', 'Kuwait', 'Oman', 'Qatar', 'Saudi Arabia', 'UAE', 'India'],
datasets: [
{
label: 'Exchange rate',
data: [45.9087, 23.8798, 99.9809, 65.8987, 65.8987, 34.9898, 32.8987],
borderColor: Utils.CHART_COLORS.darkGreen,
backgroundColor: Utils.transparentize(Utils.CHART_COLORS.darkGreen, 0.5),
pointStyle: 'rectRounded',
pointRadius: 10,
pointHoverRadius: 15
}
]
};
return (
<Line data={data} options={options} />
);
};
export default LineChart;

View File

@@ -0,0 +1,41 @@
import { Box, Input } from "@chakra-ui/react";
import React, { useRef, useState } from "react";
import audioClick from "../assets/click-151673.mp3";
const DummyComponent = () => {
// Define the state for the checkbox
const [isSwitchOn, setIsSwitchOn] = useState(false);
const audio = useRef();
// Function to toggle the switch
const handleToggle = () => {
setIsSwitchOn(!isSwitchOn);
if(audio.current){
audio.current.play();
}
};
return (
<Box display={"flex"} justifyContent={"right"} p={"2rem"}>
<label className="switch">
<Input
type="checkbox"
checked={isSwitchOn}
onChange={handleToggle} // Toggle the switch on change
/>
<Box className="button">
<div className="light"></div>
<div className="dots"></div>
<div className="characters"></div>
<div className="shine"></div>
<div className="shadow"></div>
</Box>
</label>
<audio ref={audio} src={audioClick} />
</Box>
);
};
export default DummyComponent;

View File

@@ -22,15 +22,18 @@ import {
Td, Td,
InputGroup, InputGroup,
InputRightAddon, InputRightAddon,
HStack,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { Controller } from "react-hook-form"; import { Controller } from "react-hook-form";
import { TiWarning } from "react-icons/ti"; import { TiWarning } from "react-icons/ti";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { AddIcon, CloseIcon } from "@chakra-ui/icons"; import { AddIcon, CloseIcon } from "@chakra-ui/icons";
import CurrencyInput from "./CurrencyInput";
import ShariahLogo from "../../src/assets/shariah-icon.png"
const today = new Date().toISOString().split("T")[0]; const today = new Date().toISOString().split("T")[0];
const formatDate = (dateString) => { export const formatDatee = (dateString) => {
const date = new Date(dateString); const date = new Date(dateString);
const year = date.getFullYear(); const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0"); // Months are zero-based const month = String(date.getMonth() + 1).padStart(2, "0"); // Months are zero-based
@@ -41,7 +44,7 @@ const formatDate = (dateString) => {
const defaultDate = "8/2/2024"; const defaultDate = "8/2/2024";
// Format the default date as YYYY-MM-DD // Format the default date as YYYY-MM-DD
const formattedDate = formatDate(defaultDate); const formattedDate = formatDatee(defaultDate);
const FormField = ({ const FormField = ({
label, label,
@@ -67,12 +70,14 @@ const FormField = ({
handleInputChange, handleInputChange,
align, align,
maxLength, maxLength,
dateValue,
closingDate,
...props ...props
}) => ( }) => (
<FormControl <FormControl
w={width ? width : "49%"} w={width ? width : "49%"}
isInvalid={errors[name]} isInvalid={errors[name]}
isRequired={isRequired} isRequired={type === "date" ? true: isRequired}
mb={2} mb={2}
> >
<FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}> <FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}>
@@ -82,6 +87,7 @@ const FormField = ({
control={control} control={control}
name={name} name={name}
defaultValue={value} defaultValue={value}
rules={rules} rules={rules}
render={({ field }) => { render={({ field }) => {
if (type === "select") { if (type === "select") {
@@ -402,8 +408,7 @@ const FormField = ({
w={6} w={6}
h={6} h={6}
src={ src={
" https://tanami.betadelivery.com/" + import.meta.env.VITE_IMAGE_URL+item?.logo
item?.logo
} }
/> />
{item.country === "United Arab Emirates" {item.country === "United Arab Emirates"
@@ -460,7 +465,65 @@ const FormField = ({
</Tbody> </Tbody>
</Table> </Table>
); );
} else { } else if(type === 'date'){
return (
<Input
position={'relative'}
bg={"#F5F8F6"}
focusBorderColor="forestGreen.300"
size={"sm"}
fontSize={"sm"}
rounded={"sm"}
type={"date"}
{...field}
{...props}
placeholder={placeHolder ? placeHolder : label}
textAlign={arabic ? "right" : align ? align : "left"}
_placeholder={{ fontSize: "sm" }}
// min={type === "date" ? today : undefined}
// maxLength={maxLength}
// defaultValue={type === "date" && "2023-07-26" : undefined}
// defaultValue={value}
// value={dateValue}
min={closingDate ? new Date().toISOString().split("T")[0] : undefined}
/>
);
}else if(type === 'number'){
return (
<CurrencyInput
position={'relative'}
bg={"#F5F8F6"}
focusBorderColor="forestGreen.300"
size={"sm"}
fontSize={"sm"}
rounded={"sm"}
{...field}
{...props}
placeholder={placeHolder ? placeHolder : label}
textAlign={"right"}
_placeholder={{ fontSize: "sm" }}
maxLength={maxLength}
// defaultValue={type === "date" && "2023-07-26" : undefined}
// defaultValue={value}
// value={dateValue}
/>
);}
else if(type === 'checkBox'){
return (
<HStack bg={"#F5F8F6"} p={"3px"} rounded={"2px"} border={'1px solid #E1E7EF'} justifyContent={"space-between"} mt={3}>
{/* <Image w={"38px"} aspectRatio={"1/1"} src={ShariahLogo} /> */}
<Checkbox
// isChecked={value}
isChecked={field.value}
ps={1}
{...field}
{...props} size='md' colorScheme='forestGreen'>
<Text as={"span"} fontSize={"sm"}>Is This Sharia Compliant</Text>
</Checkbox>
</HStack>
);} else{
return ( return (
<Input <Input
bg={"#F5F8F6"} bg={"#F5F8F6"}
@@ -474,7 +537,7 @@ const FormField = ({
placeholder={placeHolder ? placeHolder : label} placeholder={placeHolder ? placeHolder : label}
textAlign={arabic || type === "number" ? "right" : align ? align : "left"} textAlign={arabic || type === "number" ? "right" : align ? align : "left"}
_placeholder={{ fontSize: "sm" }} _placeholder={{ fontSize: "sm" }}
min={type === "date" ? today : undefined} // min={type === "date" ? today : undefined}
maxLength={maxLength} maxLength={maxLength}
// defaultValue={type === "date" && "2023-07-26" : undefined} // defaultValue={type === "date" && "2023-07-26" : undefined}
// value={"2023-07-26"} // value={"2023-07-26"}
@@ -490,7 +553,7 @@ const FormField = ({
</span> </span>
)} )}
{helperText && ( {helperText && (
<FormHelperText className="web-text-small">{helperText}</FormHelperText> <FormHelperText color={'gray.500'} className="web-text-small">{helperText}</FormHelperText>
)} )}
{type === "file" && ( {type === "file" && (
<FormHelperText className="web-text-small"> <FormHelperText className="web-text-small">

View File

@@ -59,7 +59,9 @@ const FormInputMain = ({
value, value,
handleInputChange, handleInputChange,
align, align,
maxLength maxLength,
dateValue,
closingDate
}, },
key key
) => ( ) => (
@@ -83,10 +85,12 @@ const FormInputMain = ({
handleImageChange={handleImageChange} handleImageChange={handleImageChange}
removeImage={removeImage} removeImage={removeImage}
width={width} width={width}
value={type === "date" ? null :value} value={value}
handleInputChange={handleInputChange} handleInputChange={handleInputChange}
align={align} align={align}
maxLength={maxLength} maxLength={maxLength}
dateValue={dateValue}
closingDate={closingDate}
/> />
) )
)} )}

View File

@@ -15,6 +15,7 @@ import {
Th, Th,
Thead, Thead,
Tr, Tr,
Checkbox, // Import Checkbox from Chakra UI
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import React from "react"; import React from "react";
@@ -30,16 +31,21 @@ const FormInputView = ({
<form> <form>
{Object?.entries(groupedFields, groupedFieldsTwo).map( {Object?.entries(groupedFields, groupedFieldsTwo).map(
([section, fields], index) => ( ([section, fields], index) => (
<Box key={section}> <Box key={index}>
<Heading as="h6" size="xs" mt={index === 0 ? 3 : 4}> <Heading as="h6" size="xs" mt={index === 0 ? 3 : 4}>
{section} {section}
</Heading> </Heading>
{/* <Box display={"flex"} gap={0}> */} <Box
<Box width={"100%"} display={"flex"} flexWrap={"wrap"} gap={4}> key={index}
width={"100%"}
display={"flex"}
flexWrap={"wrap"}
gap={4}
>
{fields.map( {fields.map(
({ value, label, id, width, btn, arabic, type, align }, key) => ({ value, label, id, width, btn, arabic, type, align }, key) =>
type === "table" ? ( type === "table" ? (
<Table w={"100%"} variant="simple"> <Table key={id} w={"100%"} variant="simple">
<Thead> <Thead>
<Tr> <Tr>
{value?.map((item, index) => ( {value?.map((item, index) => (
@@ -62,8 +68,7 @@ const FormInputView = ({
w={6} w={6}
h={6} h={6}
src={ src={
" https://tanami.betadelivery.com/" + import.meta.env.VITE_IMAGE_URL + item?.logo
item?.logo
} }
/> />
{item.country === "United Arab Emirates" {item.country === "United Arab Emirates"
@@ -94,7 +99,6 @@ const FormInputView = ({
isRequired={true} isRequired={true}
bg={"#F5F8F6"} bg={"#F5F8F6"}
focusBorderColor="forestGreen.300" focusBorderColor="forestGreen.300"
// border="1px solid #000"
size={"sm"} size={"sm"}
fontSize={"sm"} fontSize={"sm"}
rounded={"sm"} rounded={"sm"}
@@ -102,7 +106,6 @@ const FormInputView = ({
value={item.value} value={item.value}
textAlign={"right"} textAlign={"right"}
placeholder={"00.00"} placeholder={"00.00"}
// color={"#000"}
color={"#1A202C"} color={"#1A202C"}
fontWeight={500} fontWeight={500}
border={"1px solid #e2e8f0"} border={"1px solid #e2e8f0"}
@@ -120,8 +123,31 @@ const FormInputView = ({
</Tr> </Tr>
</Tbody> </Tbody>
</Table> </Table>
) : type === "checkBox" ? (
// <Box key={id} w={!width ? "49%" : width}>
<InputGroup
display={"flex"}
flexDirection={"column"}
width={"32%"}
size="sm"
>
<FormLabel key={id} color={"gray.500"} fontSize={"xs"}>
{label}
</FormLabel>
<Checkbox
isChecked={value}
colorScheme="green"
size="md"
fontSize="sm"
fontWeight="medium"
>
Is this shariah compliant
</Checkbox>
</InputGroup>
) : ( ) : (
<Box w={!width ? "49%" : width}> // </Box>
<Box key={id} w={!width ? "49%" : width}>
<FormLabel key={id} color={"gray.500"} fontSize={"xs"}> <FormLabel key={id} color={"gray.500"} fontSize={"xs"}>
{label} {label}
</FormLabel> </FormLabel>
@@ -141,7 +167,6 @@ const FormInputView = ({
) )
)} )}
</Box> </Box>
{/* </Box> */}
{index < {index <
Object.entries(groupedFields, groupedFieldsTwo).length - 1 && ( Object.entries(groupedFields, groupedFieldsTwo).length - 1 && (
<Divider /> <Divider />

View File

@@ -1,8 +1,9 @@
import { AddIcon } from "@chakra-ui/icons"; import { AddIcon, ArrowBackIcon } from "@chakra-ui/icons";
import { import {
Avatar, Avatar,
Box, Box,
Button, Button,
HStack,
Popover, Popover,
PopoverArrow, PopoverArrow,
PopoverBody, PopoverBody,
@@ -14,13 +15,14 @@ import {
useColorMode, useColorMode,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import React, { useContext } from "react"; import React, { useContext } from "react";
import { Link } from "react-router-dom"; import { Link, useNavigate } from "react-router-dom";
import { IoMdDownload } from "react-icons/io"; import { IoMdDownload } from "react-icons/io";
import * as XLSX from "xlsx"; import * as XLSX from "xlsx";
import profile from "../assets/proavatar.webp"; import profile from "../assets/proavatar.webp";
import GlobalStateContext from "../Contexts/GlobalStateContext"; import GlobalStateContext from "../Contexts/GlobalStateContext";
import { MdOutlineDarkMode, MdOutlineLightMode } from "react-icons/md"; import { MdOutlineDarkMode, MdOutlineLightMode } from "react-icons/md";
import logoMini from "../assets/propic.png" import logoMini from "../assets/propic.png";
import { BsBack } from "react-icons/bs";
const HeaderMain = ({ const HeaderMain = ({
link, link,
@@ -29,33 +31,39 @@ const HeaderMain = ({
icon, icon,
logOutHandler, logOutHandler,
slideDirecttion, slideDirecttion,
data,
}) => { }) => {
const navigate = useNavigate();
const { colorMode, toggleColorMode } = useContext(GlobalStateContext); const { colorMode, toggleColorMode } = useContext(GlobalStateContext);
return ( return (
<Box <Box
className={` pt-2 pb-2 fw-400 border-bottom d-flex ${ className={` pt-2 pb-2 fw-400 border-bottom d-flex ${
slideDirecttion ? "flex-row-reverse ps-2" : "" slideDirecttion ? "flex-row-reverse ps-2" : ""
} justify-content-between align-items-center`} } justify-content-between align-items-center`}
> // boxShadow={"0 0px 8px rgba(0, 0, 0, 0.2)"}
<Text
as={"span"} zIndex={999}
fontWeight={"500"} >
// color={"forestGreen.500"} <HStack>
className="fs-6 " {/* <ArrowBackIcon onClick={()=>navigate(-1)} /> */}
> <Text
{/* <icon /> */} as={"span"}
{title} fontWeight={"500"}
</Text> // color={"forestGreen.500"}
className="fs-6 "
>
{/* <icon /> */}
{title}
</Text>
</HStack>
<Box me={4} gap={2} className="d-flex justify-content-center "> <Box me={4} gap={2} className="d-flex justify-content-center ">
<Popover placement="bottom"> <Popover placement="bottom">
<Portal> <Portal>
<PopoverContent maxW="200px" className=""> <PopoverContent maxW="200px" className="">
<PopoverArrow /> <PopoverArrow />
<PopoverBody className="web-text-medium pointer link"> <PopoverBody onClick={()=> navigate('/profile')} className="web-text-medium pointer link">
Profile Profile
</PopoverBody> </PopoverBody>
<Link to={"/help-and-support"}> <Link to={"/help-and-support"}>
@@ -81,7 +89,7 @@ const HeaderMain = ({
boxSize={37} boxSize={37}
name="Tanami M" name="Tanami M"
src={logoMini} src={logoMini}
bg={'green.100'} bg={"green.100"}
// p={1} // p={1}
/> />
<Box <Box
@@ -91,10 +99,10 @@ const HeaderMain = ({
className=" overflow-hidden ms-3 flex-column " className=" overflow-hidden ms-3 flex-column "
> >
<Text as={"span"} className="web-text-small"> <Text as={"span"} className="web-text-small">
Hello, Tanami admin Hello, {data?.data?.firstName} {data?.data?.lastName}
</Text> </Text>
<Text as={"span"} className="web-text-xsmall"> <Text as={"span"} className="web-text-xsmall">
admin@tanami.com {data?.data?.emailAddress}
</Text> </Text>
</Box> </Box>
</Box> </Box>

View File

@@ -26,7 +26,7 @@ const ImageViewer = ({ src, isOpen, onClose }) => {
rounded={6} rounded={6}
w={"100%"} w={"100%"}
h={"100%"} h={"100%"}
src={"https://tanami.betadelivery.com/" + src} src={import.meta.env.VITE_IMAGE_URL + src}
/> />
</ModalBody> </ModalBody>
</ModalContent> </ModalContent>

View File

@@ -0,0 +1,105 @@
/* From Uiverse.io by abrahamcalsin */
.dot-spinner {
--uib-size: 2.8rem;
--uib-speed: .9s;
--uib-color: #004717;
position: relative;
display: flex;
align-items: center;
justify-content: flex-start;
height: var(--uib-size);
width: var(--uib-size);
}
.dot-spinner__dot {
position: absolute;
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: flex-start;
height: 100%;
width: 100%;
}
.dot-spinner__dot::before {
content: '';
height: 20%;
width: 20%;
border-radius: 50%;
background-color: var(--uib-color);
transform: scale(0);
opacity: 0.5;
animation: pulse0112 calc(var(--uib-speed) * 1.111) ease-in-out infinite;
box-shadow: 0 0 20px rgba(18, 31, 53, 0.3);
}
.dot-spinner__dot:nth-child(2) {
transform: rotate(45deg);
}
.dot-spinner__dot:nth-child(2)::before {
animation-delay: calc(var(--uib-speed) * -0.875);
}
.dot-spinner__dot:nth-child(3) {
transform: rotate(90deg);
}
.dot-spinner__dot:nth-child(3)::before {
animation-delay: calc(var(--uib-speed) * -0.75);
}
.dot-spinner__dot:nth-child(4) {
transform: rotate(135deg);
}
.dot-spinner__dot:nth-child(4)::before {
animation-delay: calc(var(--uib-speed) * -0.625);
}
.dot-spinner__dot:nth-child(5) {
transform: rotate(180deg);
}
.dot-spinner__dot:nth-child(5)::before {
animation-delay: calc(var(--uib-speed) * -0.5);
}
.dot-spinner__dot:nth-child(6) {
transform: rotate(225deg);
}
.dot-spinner__dot:nth-child(6)::before {
animation-delay: calc(var(--uib-speed) * -0.375);
}
.dot-spinner__dot:nth-child(7) {
transform: rotate(270deg);
}
.dot-spinner__dot:nth-child(7)::before {
animation-delay: calc(var(--uib-speed) * -0.25);
}
.dot-spinner__dot:nth-child(8) {
transform: rotate(315deg);
}
.dot-spinner__dot:nth-child(8)::before {
animation-delay: calc(var(--uib-speed) * -0.125);
}
@keyframes pulse0112 {
0%,
100% {
transform: scale(0);
opacity: 0.5;
}
50% {
transform: scale(1);
opacity: 1;
}
}

View File

@@ -1,7 +1,8 @@
import { Box, Spinner, Text } from "@chakra-ui/react"; import { Box, Spinner, Text } from "@chakra-ui/react";
import React from "react"; import React from "react";
import './FullscreenLoaders.css'
const FullscreenLoaders = () => { const FullscreenLoaders = ({height}) => {
return ( return (
<Box <Box
display={"flex"} display={"flex"}
@@ -9,16 +10,21 @@ const FullscreenLoaders = () => {
flexDirection={'column'} flexDirection={'column'}
alignItems={"center"} alignItems={"center"}
w={"100%"} w={"100%"}
h={"100vh"} h={height ? height: "100vh"}
gap={4} gap={4}
><Spinner >
thickness='3px' {/* <div className="dot-spinner">
speed='0.65s' <div className="dot-spinner__dot"></div>
emptyColor='green.100' <div className="dot-spinner__dot"></div>
color='#004717' <div className="dot-spinner__dot"></div>
size='lg' <div className="dot-spinner__dot"></div>
/> <div className="dot-spinner__dot"></div>
<Text color='#004717' fontSize={'md'} fontWeight={500}>Loading...</Text> <div className="dot-spinner__dot"></div>
<div className="dot-spinner__dot"></div>
<div className="dot-spinner__dot"></div>
</div> */}
{/* <Text color='#004717' fontSize={'md'} fontWeight={500}>Loading...</Text> */}
<Spinner />
</Box> </Box>
); );
}; };

View File

@@ -1,13 +1,21 @@
import React from "react"; import React from "react";
import './FullscreenLoaders.css'
import { Spinner } from "@chakra-ui/react";
const Loader01 = () => { const Loader01 = () => {
return ( return (
<div className="lds-ellipsis ">
<div></div> // <div className="dot-spinner">
<div></div> // <div className="dot-spinner__dot"></div>
<div></div> // <div className="dot-spinner__dot"></div>
<div></div> // <div className="dot-spinner__dot"></div>
</div> // <div className="dot-spinner__dot"></div>
// <div className="dot-spinner__dot"></div>
// <div className="dot-spinner__dot"></div>
// <div className="dot-spinner__dot"></div>
// <div className="dot-spinner__dot"></div>
// </div>
<Spinner color='green.900' />
); );
}; };

View File

@@ -0,0 +1,478 @@
import {
Box,
Button,
Heading,
HStack,
Image,
Modal,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Progress,
Spinner,
Stack,
Text,
} from "@chakra-ui/react";
import React, { useEffect, useState } from "react";
import Mobile from "../assets/mobileWing.png";
import mobileBanner from "../assets/welcome.avif";
import { GrDownload } from "react-icons/gr";
import { LuClock } from "react-icons/lu";
import { GiNetworkBars } from "react-icons/gi";
import { GrLinkedinOption } from "react-icons/gr";
import { FiInstagram } from "react-icons/fi";
import { IoBatteryHalf } from "react-icons/io5";
import { BiWifi } from "react-icons/bi";
import { useGetIOByIdQuery } from "../Services/io.service";
import { useNavigate, useParams } from "react-router-dom";
import FullscreenLoaders from "./Loaders/FullscreenLoaders";
import { calculatePercentage, formatDate } from "../Constants/Constants";
import { BsFileText } from "react-icons/bs";
const MobileView = ({ isOpen, onClose, finalRef, actionId }) => {
const [time, setTime] = useState(new Date());
const navigate = useNavigate();
const params = useParams();
const id = actionId;
const {
data: IObyID,
isLoading: IObyIDisLoading,
error: IObyIDerror,
} = useGetIOByIdQuery(id, { skip: !id });
console.log(IObyID);
const keyMerits = IObyID?.data?.keyMerits || [];
const artifactsImage = IObyID?.data?.artifactsImage || [];
useEffect(() => {
const timer = setInterval(() => {
setTime(new Date());
}, 1000);
return () => clearInterval(timer);
}, []);
const formatTime = (date) => {
return date.toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
hour12: true,
});
};
console.log(
calculatePercentage(
IObyID?.data?.totalAmtInvestmentInUSD,
IObyID?.data?.goalAmount
)
);
return (
<Modal
display={"flex"}
size={"xl"}
justifyContent={"center"}
isCentered
finalFocusRef={finalRef}
isOpen={isOpen}
onClose={onClose}
>
<ModalOverlay
backdropFilter="blur(5px)" // Add this line for backdrop blur
bg="rgba(0, 0, 0, 0.4)" // Optional: Adjust the overlay color and opacity
/>
<ModalContent backgroundColor={"transparent"} shadow={"none"}>
<HStack w={"100"} display={"flex"} justify={"center"}>
<Box
as="span"
boxShadow={"none"}
position={"relative"}
display={"flex"}
justifyContent={"center"}
h={"600px"}
w={"320px"}
sx={{
"@media (max-width: 2024px)": {
height: "695px",
width: "360px",
},
"@media (max-width: 1440px)": {
height: "600px",
width: "320px",
},
}}
>
<Image
h={"100%"}
w={"100%"}
src={Mobile}
position={"absolute"}
top={"0"}
left={"0"}
/>
<Box
backgroundColor={"#fff"}
h={"98%"}
w={"96%"}
// m={2}
borderRadius={"47px"}
pt={"36px"}
px={"15px"}
>
{IObyIDisLoading ? (
<Box
display={"flex"}
justifyContent={"center"}
alignItems={"center"}
h={"100%"}
>
<Spinner thickness="3px" color="purple.900" size="lg" />
</Box>
) : (
<>
<Box>
<Box
display={"flex"}
alignItems={"center"}
position={"absolute"}
left={"30px"}
top={"18px"}
>
<Text ml={1} mb={0}>
<GiNetworkBars fontSize={"10px"} />
</Text>
<Text ml={1} mb={0} fontSize={"10px"}>
{formatTime(time)}
</Text>
<Text ml={"5px"} mb={0}>
<GrLinkedinOption fontSize={"10px"} />
</Text>
{/* <Text ml={1} mb={0}><FiInstagram fontSize={"10px"} /></Text> */}
</Box>
<Box
display={"flex"}
alignItems={"center"}
position={"absolute"}
right={"36px"}
top={"17px"}
>
<Text mb={0}>
<BiWifi fontSize={"14px"} />
</Text>
<Text ml={1} mb={0}>
<IoBatteryHalf fontSize={"15px"} />
</Text>
</Box>
</Box>
<Box
p={"10px"}
overflowY={"scroll"}
h={"483px"}
zIndex={"99"}
position={"relative"}
borderBottomLeftRadius={"23px"}
borderBottomRightRadius={"23px"}
sx={{
"@media (max-width: 2024px)": {
height: "575px",
},
"@media (max-width: 1440px)": {
height: "483px",
},
}}
>
<Box
mb={4}
bg={"#f5f8f6"}
borderRadius={"20px"}
boxShadow={"rgba(0, 0, 0, 0.15) 0px 2px 8px"}
>
<Box position={"relative"}>
<Text
position={"absolute"}
top={"12px"}
left={"10px"}
backgroundColor={"#e4f6ea"}
fontSize={"10px"}
fontWeight={500}
color="green"
p={"7px 12px"}
borderRadius={"20px"}
>
Stock
</Text>
<Text
position={"absolute"}
top={"12px"}
right={"10px"}
fontSize={"10px"}
display={"flex"}
alignItems={"center"}
fontWeight={500}
backgroundColor={"#fff"}
p={"7px 12px"}
borderRadius={"20px"}
>
<LuClock color="#d8804e" />{" "}
<Text mb={0} ml={1}>
Closing Date {formatDate(IObyID?.data?.closingDate)}
</Text>
</Text>
{artifactsImage?.[0]?.artifactPathName && (
<Image
borderTopLeftRadius={"20px"}
borderTopRightRadius={"20px"}
h={"130px"}
w={"100%"}
src={
import.meta.env.VITE_IMAGE_URL+
artifactsImage[0]?.artifactPathName
}
/>
)}
</Box>
<Stack mt="3" bg={"#fff"} py={4} px={4}>
<Text
fontSize={"sm"}
fontWeight={"500"}
color={"#000"}
mb={0}
>
{IObyID?.data?.investmentType?.investmentTypeName}
</Text>
<Heading fontSize="16px" color={"#004717"}>
BHD {IObyID?.data?.goalAmount}
</Heading>
<Progress
colorScheme="green"
size="sm"
value={calculatePercentage(
IObyID?.data?.totalAmtInvestmentInUSD,
IObyID?.data?.goalAmount
)}
borderRadius={"3px"}
/>
<Text
color={"#4e4e4e"}
fontSize={"xs"}
fontWeight={600}
mb={0}
>
{parseFloat(
calculatePercentage(
IObyID?.data?.totalAmtInvestmentInUSD,
IObyID?.data?.goalAmount
) || 0
).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
% funded
</Text>
<Text
fontSize={"xs"}
fontWeight={500}
mb={0}
color={"#9d9d9d"}
>
{IObyID?.data?.descriptionEnglish}
</Text>
</Stack>
<Box py={4} px={4}>
<Box display={"flex"} justifyContent={"space-between"}>
<Text
fontSize={"xs"}
mb={1}
fontWeight={500}
color={"#616161"}
>
Sponsor name:
</Text>
<Text fontSize={"xs"} mb={1} fontWeight={500}>
{IObyID?.data?.sponsor?.sponsorName}
</Text>
</Box>
<Box display={"flex"} justifyContent={"space-between"}>
<Text
fontSize={"xs"}
mb={1}
fontWeight={500}
color={"#616161"}
>
Estimated return:
</Text>
<Text fontSize={"xs"} mb={1} fontWeight={500}>
{IObyID?.data?.expectedReturn}
</Text>
</Box>
<Box display={"flex"} justifyContent={"space-between"}>
<Text
fontSize={"xs"}
mb={1}
fontWeight={500}
color={"#616161"}
>
Hoiding period:
</Text>
<Text fontSize={"xs"} mb={1} fontWeight={500}>
{IObyID?.data?.holdingPeriod}
</Text>
</Box>
<Box display={"flex"} justifyContent={"space-between"}>
<Text
fontSize={"xs"}
mb={1}
fontWeight={500}
color={"#616161"}
>
Minimum investment:
</Text>
<Text fontSize={"xs"} mb={1} fontWeight={500}>
{
IObyID?.data?.minInvestmentAmt?.[0]?.country
?.minInvestmentAmt
}
</Text>
</Box>
</Box>
</Box>
<Box
mb={4}
p={5}
bg={"#f5f8f6"}
borderRadius={"20px"}
boxShadow={"rgba(0, 0, 0, 0.15) 0px 2px 8px"}
>
<Heading fontSize="14px" fontWeight={600}>
Key Merits
</Heading>
<Box>
{keyMerits &&
keyMerits.map((item, index) => (
<Box display={"flex"} alignItems={"center"}>
<Image
rounded={"md"}
display={"flex"}
p={1}
justifyContent={"center"}
alignItems={"center"}
src={
import.meta.env.VITE_IMAGE_URL+
item?.icon.iconFilePath
}
w={8}
h={8}
/>
<Text fontSize={"xs"} mb={0}>
{item?.meritsDescription}
</Text>
</Box>
))}
</Box>
</Box>
<Box
mb={4}
p={5}
borderRadius={"20px"}
boxShadow={"rgba(0, 0, 0, 0.15) 0px 2px 8px"}
>
<Heading fontSize="14px" fontWeight={600}>
Investment Documents
</Heading>
<Box
bg={"#f5f8f6"}
w={"150px"}
p={3}
borderRadius={"10px"}
>
<Box display={"flex"} alignItems={"center"} mb={2}>
{/* <Image
me={1}
src="https://tanami.betadelivery.com/public/icons/icon8.svg"
/> */}
<BsFileText color="forestGreen" fontSize="18px" />
<Text fontSize={"xs"} mb={0} ml={2}>
{IObyID?.data?.documents?.[0]?.documentName}
</Text>
</Box>
<Box
display={"flex"}
alignItems={"center"}
justifyContent={"space-between"}
>
<Text mb={0} fontSize={"sm"}>
{IObyID?.data?.documents?.[0]?.documentSize}
</Text>
<GrDownload fontSize={"15px"} />
</Box>
</Box>
</Box>
<Box
mb={4}
p={4}
borderRadius={"20px"}
boxShadow={"rgba(0, 0, 0, 0.15) 0px 2px 8px"}
>
<Heading fontSize="14px" fontWeight={600}>
Videos
</Heading>
<video
autoPlay
loop
controls
style={{
borderRadius: "18px",
width: "100%",
height: "auto",
}}
>
<source
src={
import.meta.env.VITE_IMAGE_URL+IObyID?.data?.artifactsVideo?.[0]
?.artifactStreamingURL
}
type="video/mp4"
style={{ height: "200px" }}
/>
Your browser does not support the video tag.
</video>
</Box>
</Box>
<Box
position={"relative"}
p={4}
background={"#fff"}
padding={"24px"}
paddingBottom={"3px"}
borderBottomLeftRadius={"30px"}
borderBottomRightRadius="30px"
>
<Button
margin={"auto"}
width={"85%"}
bottom="10px"
left="0"
colorScheme="forestGreen"
mr={3}
w={"100%"}
fontWeight={500}
borderRadius={"20px"}
>
Invest
</Button>
</Box>
</>
)}
</Box>
</Box>
</HStack>
</ModalContent>
</Modal>
);
};
export default MobileView;

View File

@@ -1,11 +1,17 @@
import React, { useState } from 'react'; import React, { useState } from "react";
import { Select, HStack, Text, Box, IconButton } from '@chakra-ui/react'; import { Select, HStack, Text, Box, IconButton } from "@chakra-ui/react";
import { ChevronLeftIcon, ChevronRightIcon } from '@chakra-ui/icons'; import { ChevronLeftIcon, ChevronRightIcon } from "@chakra-ui/icons";
const Pagination = ({ pageSize, setPageSize, totalItems,isLoading, setCurrentPage, currentPage }) => { const Pagination = ({
pageSize,
setPageSize,
totalItems,
isLoading,
setCurrentPage,
currentPage,
}) => {
// const [] = useState(itemsPerPageOptions[0]); // const [] = useState(itemsPerPageOptions[0]);
const totalPages = Math.ceil(totalItems / pageSize); const totalPages = Math.ceil(totalItems / pageSize);
const handlePageSizeChange = (e) => { const handlePageSizeChange = (e) => {
@@ -35,45 +41,54 @@ const Pagination = ({ pageSize, setPageSize, totalItems,isLoading, setCurrentPag
{/* <Text className='web-text-small'>Tanami v0.1</Text> */} {/* <Text className='web-text-small'>Tanami v0.1</Text> */}
<HStack> <HStack>
<Select <Select
className="pointer web-text-small"
className="pointer web-text-small" width={"90px"}
width={"90px"} rounded="sm"
rounded="sm" size="sm"
size="sm" value={pageSize}
value={pageSize} onChange={handlePageSizeChange}
onChange={handlePageSizeChange} >
> {[15, 20, 30]?.map((size) => (
{[ 15, 20, 30]?.map((size) => ( <option key={size} value={size}>
<option key={size} value={size}> {size}
{size} </option>
</option> ))}
))} </Select>
</Select>
<IconButton <IconButton
mt={1} mt={1}
size={'sm'} size={"sm"}
rounded="sm" rounded="sm"
icon={<ChevronLeftIcon />} icon={<ChevronLeftIcon />}
onClick={paginationPrev} onClick={paginationPrev}
className="link pointer" className="link pointer"
isDisabled={currentPage === 1} isDisabled={currentPage === 1}
aria-label="Previous Page"
/> />
<Text w={"81px"} display={'flex'} justifyContent={'center'} className="web-text-medium" as={"span"}> <Text
{isLoading ? "0": displayRange?.start} - {isLoading ? "00" :displayRange?.end} of {isLoading ? "00":totalItems} w={"81px"}
display={"flex"}
justifyContent={"center"}
className="web-text-medium"
as={"span"}
>
{isLoading ? "0" : displayRange?.start} -{" "}
{isLoading ? "00" : displayRange?.end} of{" "}
{isLoading ? "00" : totalItems}
</Text> </Text>
<IconButton <IconButton
mt={1} mt={1}
icon={<ChevronRightIcon />} icon={<ChevronRightIcon />}
size={'sm'} size={"sm"}
rounded="sm" rounded="sm"
onClick={paginationNext} onClick={paginationNext}
className="link pointer" className="link pointer"
isDisabled={currentPage === totalPages} isDisabled={currentPage === totalPages}
aria-label="Next Page"
/> />
</HStack> </HStack>
</HStack> </HStack>
); );
}; };
export default Pagination; export default Pagination;

View File

@@ -1,9 +1,18 @@
import { Box, Text } from '@chakra-ui/react'; import { Box, Text } from "@chakra-ui/react";
import React from 'react'; import React, { useRef } from "react";
import audioClick from "../assets/click-151673.mp3";
const SwitchButton = ({ isSwitchOn, setIsSwitchOn }) => { const SwitchButton = ({ isSwitchOn, setIsSwitchOn }) => {
// const [isSwitchOn, setIsSwitchOn] = useState(false);
const audio = useRef();
const switch_onChange_handle = () => { const switch_onChange_handle = () => {
setIsSwitchOn(!isSwitchOn); setIsSwitchOn(!isSwitchOn);
if (audio.current) {
audio.current.play();
}
}; };
return ( return (
@@ -15,7 +24,7 @@ const SwitchButton = ({ isSwitchOn, setIsSwitchOn }) => {
alignItems="center" alignItems="center"
// justifyContent={isSwitchOn ? "flex-end" : "flex-start"} // justifyContent={isSwitchOn ? "flex-end" : "flex-start"}
width="90px" width="90px"
height="24px" height="25px"
borderRadius="20px" borderRadius="20px"
backgroundColor={isSwitchOn ? "#004118" : "#ef0000"} backgroundColor={isSwitchOn ? "#004118" : "#ef0000"}
onClick={switch_onChange_handle} onClick={switch_onChange_handle}
@@ -24,16 +33,28 @@ const SwitchButton = ({ isSwitchOn, setIsSwitchOn }) => {
fontWeight="100" fontWeight="100"
transition="background-color 0.2s" transition="background-color 0.2s"
_before={{ _before={{
// content: '""',
// position: "absolute",
// width: "20px",
// height: "20px",
// borderRadius: "50%",
// backgroundColor: "#FFF",
// boxShadow: "0 2px 4px rgba(0, 0, 0, 0.2)",
// transform: isSwitchOn ? "translateX(65px)" : "translateX(0)",
// transition: "transform 0.2s",
// left:'2px'
content: '""', content: '""',
position: "absolute", position: "absolute",
width: "20px", height: "25px",
height: "20px", width: "25px",
left: "0px",
background:
"conic-gradient(rgb(104, 104, 104), white, rgb(104, 104, 104), white, rgb(104, 104, 104))",
borderRadius: "50%", borderRadius: "50%",
backgroundColor: "#FFF", transitionDuration: ".3s",
boxShadow: "0 2px 4px rgba(0, 0, 0, 0.2)", boxShadow: " 5px 2px 7px rgba(8, 8, 8, 0.308)",
transform: isSwitchOn ? "translateX(65px)" : "translateX(0)", transform: isSwitchOn ? "translateX(65px)" : "translateX(0)",
transition: "transform 0.2s",
left:'2px'
}} }}
> >
<Text <Text
@@ -46,9 +67,10 @@ const SwitchButton = ({ isSwitchOn, setIsSwitchOn }) => {
left={isSwitchOn ? "10px" : "auto"} left={isSwitchOn ? "10px" : "auto"}
right={isSwitchOn ? "auto" : "10px"} right={isSwitchOn ? "auto" : "10px"}
> >
{isSwitchOn ? 'Active' : 'InActive'} {isSwitchOn ? "Active" : "InActive"}
</Text> </Text>
</Box> </Box>
<audio ref={audio} src={audioClick} />
</Box> </Box>
); );
}; };

View File

@@ -1,6 +1,7 @@
import { CheckCircleIcon, WarningIcon } from "@chakra-ui/icons"; import { CheckCircleIcon, WarningIcon } from "@chakra-ui/icons";
import { Box, Text } from "@chakra-ui/react"; import { Box, Text } from "@chakra-ui/react";
import React from "react"; import React from "react";
import { PiWarningBold } from "react-icons/pi";
const ToastBox = ({ message, status }) => { const ToastBox = ({ message, status }) => {
return ( return (
@@ -9,10 +10,10 @@ const ToastBox = ({ message, status }) => {
rounded={"sm"} rounded={"sm"}
className="web-text-large d-flex gap-2 align-items-center" className="web-text-large d-flex gap-2 align-items-center"
p={3} p={3}
bg={status === "error" ? "red.500" : status === "warn" ? "blue.500" : "green.500"} bg={status === "error" ? "red.500" : status === "warn" ? "yellow.500" : status === "info" ? "blue.500" : "green.500"}
> >
{status === "error" || status === "warn" ? <WarningIcon/> : <CheckCircleIcon /> } {status === "error" || status === "warn" ? <PiWarningBold/> : status === "info" ? <WarningIcon/> : <CheckCircleIcon /> }
<Text as={"span"}>{message}</Text> <Text as={"span"}>{message}</Text>
</Box> </Box>
); );

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@@ -1,5 +1,83 @@
import dns from "node:dns" import dns from "node:dns"
import * as XLSX from 'xlsx';
export const generateSerialNumber = (index, currentPage, pageSize) => {
return (currentPage - 1) * pageSize + (index + 1);
};
export function getTomorrowDate() {
const today = new Date();
const tomorrow = new Date(today);
tomorrow.setDate(today.getDate() + 1);
// Format the date as YYYY-MM-DD (ISO 8601)
return tomorrow.toISOString().split('T')[0];
}
export function removeTrailingZeros(value) {
// Convert the value to a number and then to a string
let number = parseFloat(value);
let result = number.toString();
// Check if the result contains a decimal point
if (result.includes('.')) {
// Remove trailing zeros if the decimal part is 0 or 00
result = result.replace(/(\.\d*?)0+$/, '$1'); // Remove trailing zeros
result = result.replace(/\.$/, ''); // Remove the decimal point if it's the last character
}
return result;
}
export function getCountdownTimer(utcDateString) {
// Parse the UTC datetime string into a Date object
const targetDate = new Date(utcDateString);
const now = new Date();
// Calculate the difference in milliseconds
const difference = targetDate - now;
if (difference <= 0) {
return 'The time has passed or is now!';
}
// Convert the difference from milliseconds to a more readable format
const seconds = Math.floor(difference / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
const remainingDays = days;
const remainingHours = hours % 24;
const remainingMinutes = minutes % 60;
const remainingSeconds = seconds % 60;
return `${remainingDays === 0 ? "": remainingDays+"d"} ${remainingHours === 0 ? "": remainingHours+"h"} ${remainingMinutes}m ${remainingSeconds}s `;
}
export function bytesToMB(bytes) {
return (bytes / (1024 * 1024)).toFixed(2); // Convert bytes to MB and limit to 2 decimal places
}
export function startCountdown(utcDateString) {
// Function to update the countdown
const updateCountdown = () => {
const countdown = getCountdownTimer(utcDateString);
console.log(countdown);
};
// Update countdown immediately
updateCountdown();
// Set up interval to update countdown every minute (60000 milliseconds)
setInterval(updateCountdown, 60000);
}
export const getFileNameFromPath = (filePath) => { export const getFileNameFromPath = (filePath) => {
const parts = filePath?.split("/"); const parts = filePath?.split("/");
@@ -42,3 +120,102 @@ export async function checkEmailValidity(email) {
return false; // Error occurred return false; // Error occurred
} }
} }
// Function to convert timestamp to readable date format in Gulf timezone
export function formatTimestampInGulfTimezone(timestamp) {
const date = new Date(timestamp);
const options = {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
timeZone: 'Asia/Dubai', // Gulf Standard Time (GST) timezone
timeZoneName: 'short'
};
return date.toLocaleDateString('en-GB', options);
}
export function formatDate(dateString) {
const options = { year: 'numeric', month: 'short', day: 'numeric' };
const date = new Date(dateString);
return date.toLocaleDateString('en-US', options);
}
export function calculatePercentage(part, total) {
if (total === 0) {
return 0; // To avoid division by zero
}
return (part / total) * 100;
}
const getNestedValue = (obj, key) => {
return key.split('.').reduce((value, part) => {
return value && value[part] ? value[part] : null;
}, obj);
};
export const exportToExcel = (data, headers) => {
const flattenedData = data.map((item) => {
const newItem = {};
// Loop through customHeaders and get the correct values
headers.forEach((header) => {
newItem[header.label] = getNestedValue(item, header.key); // Use the helper function
});
return newItem; // Return the new flat object
});
// Now pass flattenedData to your Excel library to generate the file
// Assuming you're using a library like `xlsx` for this part:
const worksheet = XLSX.utils.json_to_sheet(flattenedData);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1");
// Generate file
XLSX.writeFile(workbook, "exported_data.xlsx");
};
export const exportToExcelNew = (data, fileName = "exported_data.xlsx") => {
console.log("Data to export:", data); // Log the data for debugging
// Ensure the data is not empty
if (!data || data.length === 0) {
console.error("No data provided for export.");
return;
}
// Convert the data to a worksheet
const worksheet = XLSX.utils.json_to_sheet(data);
// Create a new workbook and append the worksheet to it
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1");
// Ensure file has a valid .xlsx extension
const fileWithExtension = fileName.endsWith(".xlsx") ? fileName : `${fileName}.xlsx`;
// Write the workbook to a file
XLSX.writeFile(workbook, fileWithExtension);
};
export function formatDateToYYYYMMDD(dateString) {
const date = new Date(dateString);
// Extract individual date components
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0'); // Months are 0-based
const day = String(date.getDate()).padStart(2, '0');
// Combine the formatted parts
return `${year}-${month}-${day}`;
}

View File

@@ -46,7 +46,6 @@ const GlobalStateProvider = ({ children }) => {
const [slideFromRight, setSlideFormRight] = useState(false); const [slideFromRight, setSlideFormRight] = useState(false);
const { colorMode, toggleColorMode } = useColorMode(); const { colorMode, toggleColorMode } = useColorMode();
const [sponser, setSponser] = useState([]); const [sponser, setSponser] = useState([]);
const [ioStatus, setIoStatus] = useState([]);
const [investors, setInvestors] = useState([ const [investors, setInvestors] = useState([
{ {
@@ -520,7 +519,7 @@ const GlobalStateProvider = ({ children }) => {
rate: 2.66, rate: 2.66,
}, },
]); ]);
const [InvestorDetails, setInvestorDetails] = useState([ const [InvestorDetails, setInvestorDetails] = useState([
{ {
id: 1, id: 1,
clientId: "SA00000001", clientId: "SA00000001",
@@ -531,7 +530,7 @@ const GlobalStateProvider = ({ children }) => {
phoneNumber: "8940035906", phoneNumber: "8940035906",
address: "Saudi Arabia", address: "Saudi Arabia",
emailID: "john@gmail.com", emailID: "john@gmail.com",
InvestorType:"Retail", InvestorType: "Retail",
bankName: "Lorem Text", bankName: "Lorem Text",
branchAddress: "Hohenzollernring 58, 95444", branchAddress: "Hohenzollernring 58, 95444",
iban: "DE 1234 5678 9012 3456", iban: "DE 1234 5678 9012 3456",
@@ -550,7 +549,7 @@ const GlobalStateProvider = ({ children }) => {
phoneNumber: "8940035906", phoneNumber: "8940035906",
address: "Saudi Arabia", address: "Saudi Arabia",
emailID: "john@gmail.com", emailID: "john@gmail.com",
InvestorType:"Accredited Investors", InvestorType: "Accredited Investors",
bankName: "Lorem Text", bankName: "Lorem Text",
branchAddress: "Hohenzollernring 58, 95444", branchAddress: "Hohenzollernring 58, 95444",
iban: "DE 1234 5678 9012 3456", iban: "DE 1234 5678 9012 3456",
@@ -569,7 +568,7 @@ const GlobalStateProvider = ({ children }) => {
phoneNumber: "8940035906", phoneNumber: "8940035906",
address: "Saudi Arabia", address: "Saudi Arabia",
emailID: "john@gmail.com", emailID: "john@gmail.com",
InvestorType:"Retail", InvestorType: "Retail",
bankName: "Lorem Text", bankName: "Lorem Text",
branchAddress: "Hohenzollernring 58, 95444", branchAddress: "Hohenzollernring 58, 95444",
iban: "DE 1234 5678 9012 3456", iban: "DE 1234 5678 9012 3456",
@@ -588,7 +587,7 @@ const GlobalStateProvider = ({ children }) => {
phoneNumber: "8940035906", phoneNumber: "8940035906",
address: "Saudi Arabia", address: "Saudi Arabia",
emailID: "john@gmail.com", emailID: "john@gmail.com",
InvestorType:"Accredited Investors", InvestorType: "Accredited Investors",
bankName: "Lorem Text", bankName: "Lorem Text",
branchAddress: "Hohenzollernring 58, 95444", branchAddress: "Hohenzollernring 58, 95444",
iban: "DE 1234 5678 9012 3456", iban: "DE 1234 5678 9012 3456",
@@ -607,7 +606,7 @@ const GlobalStateProvider = ({ children }) => {
phoneNumber: "8940035906", phoneNumber: "8940035906",
address: "Saudi Arabia", address: "Saudi Arabia",
emailID: "john@gmail.com", emailID: "john@gmail.com",
InvestorType:"Retail", InvestorType: "Retail",
bankName: "Lorem Text", bankName: "Lorem Text",
branchAddress: "Hohenzollernring 58, 95444", branchAddress: "Hohenzollernring 58, 95444",
iban: "DE 1234 5678 9012 3456", iban: "DE 1234 5678 9012 3456",
@@ -626,7 +625,7 @@ const GlobalStateProvider = ({ children }) => {
phoneNumber: "8940035906", phoneNumber: "8940035906",
address: "Saudi Arabia", address: "Saudi Arabia",
emailID: "john@gmail.com", emailID: "john@gmail.com",
InvestorType:"Accredited Investors", InvestorType: "Accredited Investors",
bankName: "Lorem Text", bankName: "Lorem Text",
branchAddress: "Hohenzollernring 58, 95444", branchAddress: "Hohenzollernring 58, 95444",
iban: "DE 1234 5678 9012 3456", iban: "DE 1234 5678 9012 3456",
@@ -645,7 +644,7 @@ const GlobalStateProvider = ({ children }) => {
phoneNumber: "8940035906", phoneNumber: "8940035906",
address: "Saudi Arabia", address: "Saudi Arabia",
emailID: "john@gmail.com", emailID: "john@gmail.com",
InvestorType:"Retail", InvestorType: "Retail",
bankName: "Lorem Text", bankName: "Lorem Text",
branchAddress: "Hohenzollernring 58, 95444", branchAddress: "Hohenzollernring 58, 95444",
iban: "DE 1234 5678 9012 3456", iban: "DE 1234 5678 9012 3456",
@@ -664,7 +663,7 @@ const GlobalStateProvider = ({ children }) => {
phoneNumber: "8940035906", phoneNumber: "8940035906",
address: "Saudi Arabia", address: "Saudi Arabia",
emailID: "john@gmail.com", emailID: "john@gmail.com",
InvestorType:"Accredited Investors", InvestorType: "Accredited Investors",
bankName: "Lorem Text", bankName: "Lorem Text",
branchAddress: "Hohenzollernring 58, 95444", branchAddress: "Hohenzollernring 58, 95444",
iban: "DE 1234 5678 9012 3456", iban: "DE 1234 5678 9012 3456",
@@ -683,7 +682,7 @@ const GlobalStateProvider = ({ children }) => {
phoneNumber: "8940035906", phoneNumber: "8940035906",
address: "Saudi Arabia", address: "Saudi Arabia",
emailID: "john@gmail.com", emailID: "john@gmail.com",
InvestorType:"Retail", InvestorType: "Retail",
bankName: "Lorem Text", bankName: "Lorem Text",
branchAddress: "Hohenzollernring 58, 95444", branchAddress: "Hohenzollernring 58, 95444",
iban: "DE 1234 5678 9012 3456", iban: "DE 1234 5678 9012 3456",
@@ -702,7 +701,7 @@ const GlobalStateProvider = ({ children }) => {
phoneNumber: "8940035906", phoneNumber: "8940035906",
address: "Saudi Arabia", address: "Saudi Arabia",
emailID: "john@gmail.com", emailID: "john@gmail.com",
InvestorType:"Accredited Investors", InvestorType: "Accredited Investors",
bankName: "Lorem Text", bankName: "Lorem Text",
branchAddress: "Hohenzollernring 58, 95444", branchAddress: "Hohenzollernring 58, 95444",
iban: "DE 1234 5678 9012 3456", iban: "DE 1234 5678 9012 3456",
@@ -713,136 +712,8 @@ const GlobalStateProvider = ({ children }) => {
}, },
]); ]);
const [viewInvestor, setViewInvestor] = useState([ const [viewInvestor, setViewInvestor] = useState([]);
{ const [transaction, setTransaction] = useState([]);
id: 1,
dealId: "UWE3424992",
dealName: "KKR Private Equity Fund",
sponsorName: "KKR",
investAmount: "$100,000",
holdingPeriod: "4-5 years",
estimatedReturn: "50-60%",
kycStatus: "Open",
},
{
id: 2,
dealId: "UWE3424992",
dealName: "Blackstone Real Estate Income Trust",
sponsorName: "Blackstone",
investAmount: "$100,000",
holdingPeriod: "4-5 years",
estimatedReturn: "50-60%",
kycStatus: "Pending",
},
{
id: 3,
dealId: "UWE3424992",
dealName: "J.P. Morgan",
sponsorName: "J.P. Morgan",
investAmount: "$100,000",
holdingPeriod: "4-5 years",
estimatedReturn: "50-60%",
kycStatus: "Closed",
},
]);
const [transaction, setTransaction] = useState([
{
id: 1,
date: "2-Jan-24",
transaction: "Deposit",
currency: "BHD",
amount: "12000.00",
fromUSD: "",
toUSD: "2.6376",
USDamount: "31,651.20",
IOName:"",
paymentMethod:"Bank"
},
{
id: 2,
date: "6-Jan-24",
transaction: "Invested",
currency: "BHD",
amount: "-4000.00",
fromUSD: "",
toUSD: "2.6376",
USDamount: "-10,550.40",
IOName:"KKR Private Equity Fund",
paymentMethod:"-"
},
{
id: 3,
date: "7-Jan-24",
transaction: "Deposit",
currency: "BHD",
amount: "4000.00",
fromUSD: "",
toUSD: "2.6376",
USDamount: "10,550.40",
IOName:"-",
paymentMethod:"Apple Pay"
},
{
id: 4,
date: "8-Jan-24",
transaction: "Invested",
currency: "BHD",
amount: "-3000.00",
fromUSD: "",
toUSD: "2.6376",
USDamount: "-7,912.80",
IOName:"Black Stone Real Estate Income Trust",
paymentMethod:"-"
},
{
id: 5,
date: "2-Jan-24",
transaction: "Deposit",
currency: "BHD",
amount: "12000.00",
fromUSD: "0.3747",
toUSD: "",
USDamount: "31,651.20",
IOName:"KKR Private Equity Fund",
paymentMethod:"-"
},
{
id: 6,
date: "2-Jan-24",
transaction: "Deposit",
currency: "BHD",
amount: "12000.00",
fromUSD: "0.3747",
toUSD: "",
USDamount: "31,651.20",
IOName:"Black Stone Real Estate Income Trust",
paymentMethod:"-"
},
{
id: 7,
date: "2-Jan-24",
transaction: "Deposit",
currency: "BHD",
amount: "12000.00",
fromUSD: "0.3747",
toUSD: "",
USDamount: "31,651.20",
IOName:"KKR Private Equity Fund",
paymentMethod:"-"
},
{
id: 8,
date: "2-Jan-24",
transaction: "Deposit",
currency: "BHD",
amount: "12000.00",
fromUSD: "",
toUSD: "2.6376",
USDamount: "31,651.20",
IOName:"",
paymentMethod:"Bank"
},
]);
const [investorTransaction, setInvestorTransaction] = useState([ const [investorTransaction, setInvestorTransaction] = useState([
{ {
@@ -1091,112 +962,14 @@ const GlobalStateProvider = ({ children }) => {
]); ]);
const [deleteHistory, setDeleteHistory] = useState([ const [deleteHistory, setDeleteHistory] = useState([
{ {
id: uuidv4(), id: 1,
date: "2024-01-15", firstName: "satyam",
Distribution: "Office supplies", lastName: "Bendal",
charge: "200.50", clientId: "QA00000003",
year: "2024", RequestedOn: "2024-08-21T08:12:08.000Z",
quarter: "Q1", phoneNumber: "6387524874",
amount: 1500, country: "Qatar",
}, status: "Approved",
{
id: uuidv4(),
date: "2024-02-10",
Distribution: "Marketing expenses",
charge: "450.00",
year: "2024",
quarter: "Q1",
amount: 2000,
},
{
id: uuidv4(),
date: "2024-03-05",
Distribution: "Software subscription",
charge: "300.75",
year: "2024",
quarter: "Q1",
amount: 1200,
},
{
id: uuidv4(),
date: "2024-04-18",
Distribution: "Travel expenses",
charge: "600.30",
year: "2024",
quarter: "Q2",
amount: 2500,
},
{
id: uuidv4(),
date: "2024-05-22",
Distribution: "Consulting fees",
charge: "800.00",
year: "2024",
quarter: "Q2",
amount: 3000,
},
{
id: uuidv4(),
date: "2024-06-14",
Distribution: "Office rent",
charge: "1200.25",
year: "2024",
quarter: "Q2",
amount: 3500,
},
{
id: uuidv4(),
date: "2024-07-09",
Distribution: "Utilities",
charge: "150.40",
year: "2024",
quarter: "Q3",
amount: 1000,
},
{
id: uuidv4(),
date: "2024-08-29",
Distribution: "Employee training",
charge: "500.00",
year: "2024",
quarter: "Q3",
amount: 1800,
},
{
id: uuidv4(),
date: "2024-09-13",
Distribution: "Website maintenance",
charge: "350.65",
year: "2024",
quarter: "Q3",
amount: 1400,
},
{
id: uuidv4(),
date: "2024-10-23",
Distribution: "Advertising",
charge: "900.50",
year: "2024",
quarter: "Q4",
amount: 2200,
},
{
id: uuidv4(),
date: "2024-10-23",
Distribution: "Advertising",
charge: "900.50",
year: "2024",
quarter: "Q4",
amount: 2200,
},
{
id: uuidv4(),
date: "2024-10-23",
Distribution: "Advertising",
charge: "900.50",
year: "2024",
quarter: "Q4",
amount: 2200,
}, },
]); ]);
const [investorRequest, setInvestorRequest] = useState([ const [investorRequest, setInvestorRequest] = useState([
@@ -1311,114 +1084,26 @@ const GlobalStateProvider = ({ children }) => {
]); ]);
const [deleteRequest, setDeleteRequest] = useState([ const [deleteRequest, setDeleteRequest] = useState([
{ {
id: uuidv4(), "id": 2,
date: getRandomDate(startDate, endDate), "firstName": "satyam",
Distribution: "lorem ipsum dummy text", "lastName": "Bendal",
charge: "500", "clientId": "QA00000003",
year: "2024", "RequestedOn": "2024-08-21T09:44:21.000Z",
quater: "Q 1", "phoneNumber": "6387524874",
amount: 1000, "country": "Qatar",
"status": "Pending"
}, },
{ {
id: uuidv4(), "id": 3,
date: getRandomDate(startDate, endDate), "firstName": "satyam",
Distribution: "lorem ipsum dummy text", "lastName": "Bendal",
charge: "500", "clientId": "QA00000003",
year: "2024", "RequestedOn": "2024-08-21T09:53:03.000Z",
quater: "Q 1", "phoneNumber": "6387524874",
amount: 1000, "country": "Qatar",
}, "status": "Pending"
{ }
id: uuidv4(), ]);
date: getRandomDate(startDate, endDate),
Distribution: "lorem ipsum dummy text",
charge: "500",
year: "2024",
quater: "Q 1",
amount: 1000,
},
{
id: uuidv4(),
date: getRandomDate(startDate, endDate),
Distribution: "lorem ipsum dummy text",
charge: "500",
year: "2024",
quater: "Q 1",
amount: 1000,
},
{
id: uuidv4(),
date: getRandomDate(startDate, endDate),
Distribution: "lorem ipsum dummy text",
charge: "500",
year: "2024",
quater: "Q 1",
amount: 1000,
},
{
id: uuidv4(),
date: getRandomDate(startDate, endDate),
Distribution: "lorem ipsum dummy text",
charge: "500",
year: "2024",
quater: "Q 1",
amount: 1000,
},
{
id: uuidv4(),
date: getRandomDate(startDate, endDate),
Distribution: "lorem ipsum dummy text",
charge: "500",
year: "2024",
quater: "Q 1",
amount: 1000,
},
{
id: uuidv4(),
date: getRandomDate(startDate, endDate),
Distribution: "lorem ipsum dummy text",
charge: "500",
year: "2024",
quater: "Q 1",
amount: 1000,
},
{
id: uuidv4(),
date: getRandomDate(startDate, endDate),
Distribution: "lorem ipsum dummy text",
charge: "500",
year: "2024",
quater: "Q 1",
amount: 1000,
},
{
id: uuidv4(),
date: getRandomDate(startDate, endDate),
Distribution: "lorem ipsum dummy text",
charge: "500",
year: "2024",
quater: "Q 1",
amount: 1000,
},
{
id: uuidv4(),
date: getRandomDate(startDate, endDate),
Distribution: "lorem ipsum dummy text",
charge: "500",
year: "2024",
quater: "Q 1",
amount: 1000,
},
{
id: uuidv4(),
date: getRandomDate(startDate, endDate),
Distribution: "lorem ipsum dummy text",
charge: "500",
year: "2024",
quater: "Q 1",
amount: 1000,
},
]);
const [viewIO, setViewIO] = useState([ const [viewIO, setViewIO] = useState([
{ {
id: 1, id: 1,
@@ -1450,8 +1135,6 @@ const GlobalStateProvider = ({ children }) => {
}, },
]); ]);
const [IODetails, setIODetails] = useState({});
const [depositRequest, setDepositRequest] = useState([ const [depositRequest, setDepositRequest] = useState([
{ {
id: 1, id: 1,
@@ -1685,6 +1368,28 @@ const GlobalStateProvider = ({ children }) => {
mailId: "john@gmail.com", mailId: "john@gmail.com",
status: "Incompleted", status: "Incompleted",
}, },
{
id: 10,
date: "02-Jan-2024",
clientId: "SA00000001",
firstName: "John",
lastName: "David",
country: "Saudi Arabia",
phoneNumber: "8940035906",
mailId: "john@gmail.com",
status: "Incompleted",
},
{
id: 10,
date: "02-Jan-2024",
clientId: "SA00000001",
firstName: "John",
lastName: "David",
country: "Saudi Arabia",
phoneNumber: "8940035906",
mailId: "john@gmail.com",
status: "Incompleted",
},
]); ]);
const [manageAcademy, setManageAcademy] = useState([ const [manageAcademy, setManageAcademy] = useState([
@@ -1724,6 +1429,257 @@ const GlobalStateProvider = ({ children }) => {
}, },
]); ]);
const [users, setUsers] = useState([
{
id: 1,
firstName: "SA00000001",
lastName: "John David",
emailID: "John",
role: "David",
phoneNumber:"8940035906",
},
]);
const [fawateerRequest, setFawateerRequest] = useState([
{
id: 8,
principal_xid: 2,
transaction_date: "2024-10-07",
transaction_amount: "2000.00",
transactionStatus: "Approved",
makerComment: "This is a sample comment for the transactio",
checkerComment: "This is a sample comment for the transaction",
spportFile_path: "public\\spportFile_path\\screenshot_2024_02_15_152810.png",
clientReference_id: "BH00000001",
firstName: "jayesh",
lastName: "jain ",
mobileNumber: "+919819906537",
emailAddress: "jayeshkjain6@gmail.com",
maker: {
firstName: "Faisal",
lastName: "Aljalahma",
emailAddress: "admin@tanami.com",
ISDcode: "+973",
mobileNumber: "3633133"
}
},
{
id: 8,
principal_xid: 2,
transaction_date: "2024-10-07",
transaction_amount: "2000.00",
transactionStatus: "Approved",
makerComment: "This is a sample comment for the transactio",
checkerComment: "This is a sample comment for the transaction",
spportFile_path: "public\\spportFile_path\\screenshot_2024_02_15_152810.png",
clientReference_id: "BH00000001",
firstName: "jayesh",
lastName: "jain ",
mobileNumber: "+919819906537",
emailAddress: "jayeshkjain6@gmail.com",
maker: {
firstName: "Faisal",
lastName: "Aljalahma",
emailAddress: "admin@tanami.com",
ISDcode: "+973",
mobileNumber: "3633133"
}
},
{
id: 8,
principal_xid: 2,
transaction_date: "2024-10-07",
transaction_amount: "2000.00",
transactionStatus: "Approved",
makerComment: "This is a sample comment for the transactio",
checkerComment: "This is a sample comment for the transaction",
spportFile_path: "public\\spportFile_path\\screenshot_2024_02_15_152810.png",
clientReference_id: "BH00000001",
firstName: "jayesh",
lastName: "jain ",
mobileNumber: "+919819906537",
emailAddress: "jayeshkjain6@gmail.com",
maker: {
firstName: "Faisal",
lastName: "Aljalahma",
emailAddress: "admin@tanami.com",
ISDcode: "+973",
mobileNumber: "3633133"
}
},
{
id: 8,
principal_xid: 2,
transaction_date: "2024-10-07",
transaction_amount: "2000.00",
transactionStatus: "Approved",
makerComment: "This is a sample comment for the transactio",
checkerComment: "This is a sample comment for the transaction",
spportFile_path: "public\\spportFile_path\\screenshot_2024_02_15_152810.png",
clientReference_id: "BH00000001",
firstName: "jayesh",
lastName: "jain ",
mobileNumber: "+919819906537",
emailAddress: "jayeshkjain6@gmail.com",
maker: {
firstName: "Faisal",
lastName: "Aljalahma",
emailAddress: "admin@tanami.com",
ISDcode: "+973",
mobileNumber: "3633133"
}
},
]);
const [approveHistory, setApproveHistory] = useState([
{
"id": 1,
"principal_xid": 2,
"transaction_date": "2024-10-07",
"transaction_amount": "2000.00",
"transactionStatus": "Pending",
"makerComment": null,
"checkerComment": null,
"spportFile_path": null,
"clientReference_id": "BH00000001",
"firstName": "jayesh",
"lastName": "jain",
"mobileNumber": "+919819906537",
"emailAddress": "jayeshkjain6@gmail.com",
"maker": {
"firstName": "Faisal",
"lastName": "Aljalahma",
"emailAddress": "admin@tanami.com",
"ISDcode": "+973",
"mobileNumber": "3633133"
}
},
]);
const [approved, setApproved] = useState([
{
id: 1,
transactionDate: "02-Jan-24",
particulars: "Cash Reserve- Initated",
amount: "50,000.00",
Comments: "",
user: "Faisal",
entryDate: "02-Jan-24",
},
{
id: 2,
transactionDate: "12-Feb-24",
particulars: "Fees & Expense",
amount: "-22,000.00",
Comments: "",
user: "Faisal",
entryDate: "13-Feb-24",
},
{
id: 3,
transactionDate: "12-Feb-24",
particulars: "Distribution Received From Sponsor",
amount: "50,000.00",
Comments: "",
user: "Nawab",
entryDate: "24-Mar-24",
},
{
id: 4,
transactionDate: "28-Mar-24",
particulars: "Distribution Paid To Investors",
amount: "-40,000.00",
Comments: "",
user: "Faisal",
entryDate: "28-Mar-24",
},
{
id: 5,
transactionDate: "26-Jun-24",
particulars: "Distribution Received From Sponsor",
amount: "70,000.00",
Comments: "",
user: "Faisal",
entryDate: "27-Jun-24",
},
{
id: 6,
transactionDate: "28-Jun-24",
particulars: "Distribution Paid To Investors",
amount: "-60,000.00",
Comments: "",
user: "Nawab",
entryDate: "28-Jun-24",
},
]);
const [iONAVDetail, setIONAVDetail] = useState([
{
id: 1,
valuationDate: "01-Jul-24",
nav: "1,229,750.00 ",
lastUpdate: "12.56",
investmentClose: "29.45",
updatedBy: "Nawab",
updatedOn: "01-Jul-24",
},
{
id: 2,
valuationDate: "25-Apr-24",
nav: "1,092,500.00",
lastUpdate: "15.00",
investmentClose: "15.00",
updatedBy: "Faisal",
updatedOn: "25-Apr-24",
},
{
id: 3,
valuationDate: "02-Jan-24",
nav: "950,000.00",
lastUpdate: "",
investmentClose: "",
updatedBy: "Faisal",
updatedOn: "02-Jan-24",
},
]);
const [iOTransaction, setIOTransaction] = useState([
{
id: 1,
transactionName: "Amount Invested",
amount: "995,000",
createdBy: "Faisal",
createdOn: "27-Oct-24",
approvedBy: "Nawab",
approvedOn: "28-Oct-24",
},
{
id: 2,
transactionName: "Distribution To Sponser",
amount: "40,000",
createdBy: "Faisal",
createdOn: "30-Oct-24",
approvedBy: "Nawab",
approvedOn: "31-Oct-24",
},
{
id: 3,
transactionName: "Amount Invested",
amount: "995,000",
createdBy: "Faisal",
createdOn: "27-Oct-24",
approvedBy: "Nawab",
approvedOn: "28-Oct-24",
},
]);
const [InvestorWallet, setInvestorWallet] = useState(null);
// ==============[ prod state ]===============================
const [IODetails, setIODetails] = useState(null);
const [isIOloading, setIOloading] = useState(false);
return ( return (
<GlobalStateContext.Provider <GlobalStateContext.Provider
value={{ value={{
@@ -1795,8 +1751,22 @@ const GlobalStateProvider = ({ children }) => {
setAcademicDocuments, setAcademicDocuments,
iOArtifactsTwo, iOArtifactsTwo,
setIOArtifactsTwo, setIOArtifactsTwo,
ioStatus, InvestorWallet,
setIoStatus, setInvestorWallet,
isIOloading,
setIOloading,
users,
setUsers,
fawateerRequest,
setFawateerRequest,
approveHistory,
setApproveHistory,
approved,
setApproved,
iONAVDetail,
setIONAVDetail,
iOTransaction,
setIOTransaction,
}} }}
> >
{children} {children}

View File

@@ -6,6 +6,8 @@ import logoMiniDark from "../assets/favicon.png";
import { useDispatch } from "react-redux"; import { useDispatch } from "react-redux";
import { loginUser } from "../Redux/Slice/auth"; import { loginUser } from "../Redux/Slice/auth";
import Button02 from "../Components/Buttons/Button02"; import Button02 from "../Components/Buttons/Button02";
import { CgProfile } from "react-icons/cg";
import { import {
TbArrowBadgeLeftFilled, TbArrowBadgeLeftFilled,
TbListDetails, TbListDetails,
@@ -13,7 +15,7 @@ import {
TbTransactionDollar, TbTransactionDollar,
} from "react-icons/tb"; } from "react-icons/tb";
import { TbArrowBadgeRightFilled } from "react-icons/tb"; import { TbArrowBadgeRightFilled } from "react-icons/tb";
import { ArrowBackIcon, ArrowLeftIcon, ArrowRightIcon } from "@chakra-ui/icons"; import { ArrowBackIcon, ArrowLeftIcon, ArrowRightIcon, AtSignIcon } from "@chakra-ui/icons";
import { import {
Link, Link,
NavLink, NavLink,
@@ -51,6 +53,8 @@ import {
AlertIcon, AlertIcon,
Breadcrumb, Breadcrumb,
Divider, Divider,
Tooltip,
useRadio,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import GlobalStateContext from "../Contexts/GlobalStateContext"; import GlobalStateContext from "../Contexts/GlobalStateContext";
import Cookies from "js-cookie"; // Import the Cookies library import Cookies from "js-cookie"; // Import the Cookies library
@@ -72,8 +76,16 @@ import shield from "../assets/shield.png";
import SplashScreen from "../Pages/SplashScreen"; import SplashScreen from "../Pages/SplashScreen";
import CutomBreadcrumb from "../Components/CutomBreadcrumb"; import CutomBreadcrumb from "../Components/CutomBreadcrumb";
import CustomBreadcrumb from "../Components/CutomBreadcrumb"; import CustomBreadcrumb from "../Components/CutomBreadcrumb";
import { getCountdownTimer } from "../Constants/Constants";
import { useLogoutMutation } from "../Services/token.serivce";
import CreateRequest from "../Pages/Fawateer/CreateRequest";
import ApproveRequest from "../Pages/FawateerChecker/ApproveRequest/ApproveRequest";
import ApproveHistoryMaker from "../Pages/FawateerChecker/ApproveHistory/ApproveHistoryMaker";
import ApproveHistory from "../Pages/FawateerChecker/ApproveHistory/ApproveHistoryChecker";
import { useProfileQuery } from "../Services/io.service";
const DashboardLayout = ({ isOnline }) => { const DashboardLayout = ({ isOnline }) => {
const userRole = localStorage.getItem("role");
const navigate = useNavigate(); const navigate = useNavigate();
const dispach = useDispatch(); const dispach = useDispatch();
const location = useLocation(); const location = useLocation();
@@ -90,6 +102,18 @@ const DashboardLayout = ({ isOnline }) => {
const [isSplashVisible, setSplashVisible] = useState(true); const [isSplashVisible, setSplashVisible] = useState(true);
const [openIndex, setOpenIndex] = useState(null); const [openIndex, setOpenIndex] = useState(null);
const { data, refetch } = useProfileQuery();
useEffect(() => {
if (
!localStorage.getItem("accessToken") &&
!localStorage.getItem("refreshToken")
) {
logOutHandler();
return navigate("/login");
}
}, []);
useEffect(() => { useEffect(() => {
const savedIndex = localStorage.getItem("openAccordionIndex"); const savedIndex = localStorage.getItem("openAccordionIndex");
if (savedIndex !== null) { if (savedIndex !== null) {
@@ -107,8 +131,8 @@ const DashboardLayout = ({ isOnline }) => {
// Set a timer to hide the splash screen after 3 seconds // Set a timer to hide the splash screen after 3 seconds
const timer = setTimeout(() => { const timer = setTimeout(() => {
setSplashVisible(false); setSplashVisible(false);
},300); // 3000ms = 3 seconds }, 1000); // 3000ms = 3 seconds
refetch();
// Cleanup the timer // Cleanup the timer
return () => clearTimeout(timer); return () => clearTimeout(timer);
}, []); }, []);
@@ -117,11 +141,17 @@ const DashboardLayout = ({ isOnline }) => {
setOpenDrawerClick(!openDrawerClick); setOpenDrawerClick(!openDrawerClick);
}; };
const logOutHandler = () => { const [logout] = useLogoutMutation();
const logOutHandler = async () => {
// dispach(loginUser(false)); // dispach(loginUser(false));
setIsAuthenticate(false); setIsAuthenticate(false);
Cookies.remove("isAuthenticated"); Cookies.remove("isAuthenticated");
navigate("/login"); try {
await logout();
localStorage.clear();
navigate("/login");
} catch (error) {}
}; };
// // Function to get the title based on the route // // Function to get the title based on the route
@@ -132,15 +162,27 @@ const DashboardLayout = ({ isOnline }) => {
case path.startsWith("/sponser"): case path.startsWith("/sponser"):
return ( return (
<span className="d-flex align-items-end gap-2"> <span className="d-flex align-items-end gap-2">
<RiMoneyDollarBoxLine className="h4 m-0" /> Sponsorer <RiMoneyDollarBoxLine className="h4 m-0" /> Sponsor
</span> </span>
); );
case path.startsWith("/email"):
return (
<span className="d-flex align-items-end gap-2">
<AtSignIcon className="h4 m-0" /> Email Notifiation
</span>
);
case path.startsWith("/investment-type"): case path.startsWith("/investment-type"):
return ( return (
<span className="d-flex align-items-end gap-2"> <span className="d-flex align-items-end gap-2">
<VscSymbolClass className="h4 m-0" /> Investment Type <VscSymbolClass className="h4 m-0" /> Investment Type
</span> </span>
); );
case path.startsWith("/profile"):
return (
<span className="d-flex align-items-end gap-2">
<CgProfile className="h4 m-0" /> Profile
</span>
);
case path.startsWith("/exchange-rate"): case path.startsWith("/exchange-rate"):
return ( return (
<span className="d-flex align-items-end gap-2"> <span className="d-flex align-items-end gap-2">
@@ -184,6 +226,37 @@ const DashboardLayout = ({ isOnline }) => {
Investor Transactions Investor Transactions
</span> </span>
); );
case path.startsWith("/deposit-request"):
return (
<span className="d-flex align-items-end gap-2">
<RiMoneyDollarBoxLine className="h4 m-0 fw-normal" />
Deposit Pending Request
</span>
);
case path.startsWith("/deposit-history"):
return (
<span className="d-flex align-items-end gap-2">
<RiExchangeBoxLine className="h4 m-0 fw-normal" />
Deposite Request
</span>
);
case path.startsWith("/fawateer"):
return (
<span className="d-flex align-items-end gap-2">
<RiMoneyDollarBoxLine className="h4 m-0 fw-normal" />
Fawateer Deposit
</span>
);
case path.startsWith("/fawateer-history"):
return (
<span className="d-flex align-items-end gap-2">
<RiMoneyDollarBoxLine className="h4 m-0 fw-normal" />
Fawateer Deposit
</span>
);
case path.startsWith("/withdraw-request"): case path.startsWith("/withdraw-request"):
return ( return (
<span className="d-flex align-items-end gap-2"> <span className="d-flex align-items-end gap-2">
@@ -358,7 +431,7 @@ const DashboardLayout = ({ isOnline }) => {
bottom={4} bottom={4}
right={!slideFromRight ? 4 : "auto"} right={!slideFromRight ? 4 : "auto"}
left={slideFromRight ? 4 : "auto"} left={slideFromRight ? 4 : "auto"}
backgroundColor={"#fff"} backgroundColor={"#000"}
rounded={"full"} rounded={"full"}
p={2} p={2}
w={8} w={8}
@@ -383,11 +456,11 @@ const DashboardLayout = ({ isOnline }) => {
{slideFromRight ? null : ( {slideFromRight ? null : (
<aside <aside
className="h-100 position-relative sideBar " className="h-100 position-relative sideBar"
// onMouseOver={() => setIsDrawerOpen(true)} // onMouseOver={() => setIsDrawerOpen(true)}
// onMouseLeave={() => setIsDrawerOpen(false)} // onMouseLeave={() => setIsDrawerOpen(false)}
style={{ style={{
width: isDrawerOpen || openDrawerClick ? 232 : 74, width: isDrawerOpen || openDrawerClick ? 230 : 74,
transition: "width 0.3s ease-in-out", // Smooth transition for width change transition: "width 0.3s ease-in-out", // Smooth transition for width change
// overflow: "hidden", // overflow: "hidden",
backgroundColor: "#0041180A", backgroundColor: "#0041180A",
@@ -400,16 +473,18 @@ const DashboardLayout = ({ isOnline }) => {
isDrawerOpen || openDrawerClick isDrawerOpen || openDrawerClick
? "justify-content-start" ? "justify-content-start"
: "justify-content-center" : "justify-content-center"
} p-3 pt-3 pb-4 position-relative `} } p-3 pt-3 pb-3 position-relative `}
height={"10%"} height={"10%"}
> >
{isDrawerOpen || openDrawerClick ? ( {isDrawerOpen || openDrawerClick ? (
<Image <Image
style={{ style={{
width: 120, width: 110,
}} }}
src={colorMode === "light" ? logo : logoDark} src={colorMode === "light" ? logo : logoDark}
alt="Logo" alt="Logo"
onClick={() => navigate("/")}
cursor={"pointer"}
/> />
) : ( ) : (
<Image <Image
@@ -418,12 +493,14 @@ const DashboardLayout = ({ isOnline }) => {
}} }}
src={colorMode === "light" ? logoMini : logoMiniDark} src={colorMode === "light" ? logoMini : logoMiniDark}
alt="Logo" alt="Logo"
onClick={() => navigate("/")}
cursor={"pointer"}
/> />
)} )}
</div> </div>
<Box <Box
className="ps-2 scroll-bar pe-1" className="ps-2 scroll-bar pe-1 pt-3"
style={{ style={{
height: "90%", height: "90%",
overflowY: "scroll", overflowY: "scroll",
@@ -442,40 +519,53 @@ const DashboardLayout = ({ isOnline }) => {
if (type === "accordion") { if (type === "accordion") {
return ( return (
<AccordionItem key={index} border={"none"}> <AccordionItem key={index} border={"none"}>
<AccordionButton <Tooltip
style={{ height: "auto" }} isDisabled={isDrawerOpen || openDrawerClick}
className={`${ hasArrow
isDrawerOpen || openDrawerClick bg={"#fff"}
? "p-2 web-text-medium ps-3 justify-content-between" fontSize={"xs"}
: "p-2 ps-1 web-text-xlarge justify-content-center" label={title}
} rounded-1 link d-flex align-items-center gap-2 w-100 mb-1`} placement="top-start"
color={"blue.800"}
> >
<Box <AccordionButton
as="span" style={{ height: "auto" }}
display={"flex"} className={`${
gap={2} isDrawerOpen || openDrawerClick
alignItems={"center"} ? "p-2 web-text-medium ps-3 justify-content-between"
: "p-2 ps-1 web-text-xlarge justify-content-center"
} rounded-1 link d-flex align-items-center gap-2 w-100 mb-1`}
> >
{/* {Icon && title === "Admin" ? <Image w={15} src={shield} /> : <Icon className={`web-text-large`} />} */} <Box
{Icon && ( as="span"
<Icon display={"flex"}
fontSize={title === "Admin" ? "18px" : "15px"} gap={2}
/> alignItems={"center"}
)}
<Text
as={"span"}
display={
isDrawerOpen || openDrawerClick ? "flex" : "none"
}
alignItems="center"
overflow="hidden"
textAlign={"left"}
> >
{title} {/* {Icon && title === "Admin" ? <Image w={15} src={shield} /> : <Icon className={`web-text-large`} />} */}
</Text> {Icon && (
</Box> <Icon
<AccordionIcon /> fontSize={title === "Admin" ? "18px" : "15px"}
</AccordionButton> />
)}
<Text
as={"span"}
display={
isDrawerOpen || openDrawerClick
? "flex"
: "none"
}
alignItems="center"
overflow="hidden"
textAlign={"left"}
>
{title}
</Text>
</Box>
<AccordionIcon />
</AccordionButton>
</Tooltip>
<AccordionPanel <AccordionPanel
p={0} p={0}
pb={1} pb={1}
@@ -488,65 +578,81 @@ const DashboardLayout = ({ isOnline }) => {
{ title: subMenuTitle, path: link, icon: SubIcon }, { title: subMenuTitle, path: link, icon: SubIcon },
i i
) => ( ) => (
<Box <Tooltip
key={i} isDisabled={isDrawerOpen || openDrawerClick}
style={{ height: "auto", position: "relative" }} hasArrow
className={`${ bg={"#fff"}
isDrawerOpen || openDrawerClick fontSize={"xs"}
? " web-text-medium ps-4" label={subMenuTitle}
: " web-text-xlarge justify-content-center" placement="right"
} d-flex align-items-center p-0`} color={"blue.800"}
> >
<Box <Box
backgroundColor={"gray.300"} key={i}
style={{ style={{ height: "auto", position: "relative" }}
position: "absolute",
top: 0,
width: 2,
left: 22,
height:
i === submenu?.length - 1 ? "55%" : "120%",
borderRadius: "0 0 10px 10px",
}}
/>
<Box
backgroundColor={"gray.300"}
style={{
position: "absolute",
width: 10,
left: 22,
height: 2,
}}
/>
<NavLink
className={`${ className={`${
isDrawerOpen || openDrawerClick isDrawerOpen || openDrawerClick
? "p-2 ps-1 ms-2 web-text-medium " ? " web-text-medium ps-4"
: "p-2 ps-0 ms-0 zindex-3 ms-4 web-text-xlarge justify-content-center" : " web-text-xlarge justify-content-center"
} rounded-1 link d-flex align-items-center gap-2 w-100 `} } d-flex align-items-center p-0`}
to={link}
> >
{SubIcon && ( <Box
<SubIcon backgroundColor={"gray.300"}
className="web-text-large ms-2" style={{
style={{ zIndex: 111 }} position: "absolute",
/> top: 0,
)} width: 2,
<Text left: 22,
as={"span"} height:
display={ i === submenu?.length - 1
? "55%"
: "120%",
borderRadius: "0 0 10px 10px",
}}
/>
<Box
backgroundColor={"gray.300"}
style={{
position: "absolute",
width: 10,
left: 22,
height: 2,
}}
/>
<NavLink
className={`${
isDrawerOpen || openDrawerClick isDrawerOpen || openDrawerClick
? "flex" ? "p-2 ps-1 ms-2 web-text-medium "
: "none" : "p-2 ps-0 ms-0 zindex-3 ms-4 web-text-xlarge justify-content-center"
} } rounded-1 link d-flex align-items-center gap-2 w-100 `}
alignItems="center" to={link}
overflow="hidden"
> >
{subMenuTitle} {SubIcon && (
</Text> <SubIcon
</NavLink> className="web-text-large ms-2"
</Box> style={{ zIndex: 111 }}
/>
)}
<Text
as={"span"}
display={
isDrawerOpen || openDrawerClick
? "flex"
: "none"
}
alignItems="center"
overflow="hidden"
>
{subMenuTitle === "Aprover Request"
? data?.data?.role === "Maker"
? "Create Request"
: "Aprover Request"
: subMenuTitle}
</Text>
</NavLink>
</Box>
</Tooltip>
) )
)} )}
</AccordionPanel> </AccordionPanel>
@@ -564,28 +670,37 @@ const DashboardLayout = ({ isOnline }) => {
); );
} else if (type === "single") { } else if (type === "single") {
return ( return (
<NavLink <Tooltip
key={index} hasArrow
style={{ height: "auto", position: "relative" }} bg={"#fff"}
className={`${ fontSize={"xs"}
isDrawerOpen || openDrawerClick label={title}
? "p-2 web-text-medium" placement="top-start"
: "p-2 ps-0 web-text-xlarge justify-content-start" color={"blue.800"}
} rounded-1 link d-flex align-items-center gap-2 w-100`}
to={path}
> >
{Icon && <Icon className="web-text-large ms-2" />} <NavLink
<Text key={index}
as={"span"} style={{ height: "auto", position: "relative" }}
display={ className={`${
isDrawerOpen || openDrawerClick ? "flex" : "none" isDrawerOpen || openDrawerClick
} ? "p-2 web-text-medium"
alignItems="center" : "p-2 ps-0 web-text-xlarge justify-content-start"
overflow="hidden" } rounded-1 link d-flex align-items-center gap-2 w-100`}
to={path}
> >
{title} {Icon && <Icon className="web-text-large ms-2" />}
</Text> <Text
</NavLink> as={"span"}
display={
isDrawerOpen || openDrawerClick ? "flex" : "none"
}
alignItems="center"
overflow="hidden"
>
{title}
</Text>
</NavLink>
</Tooltip>
); );
} else { } else {
return null; return null;
@@ -615,21 +730,17 @@ const DashboardLayout = ({ isOnline }) => {
<ArrowRightIcon className="web-text-small " /> <ArrowRightIcon className="web-text-small " />
)} )}
</Button> </Button>
{/* <Text textAlign={'center'} fontWeight={500} fontSize={'xs'} color={"gray.600"}>{getCountdownTimer(localStorage.getItem('accessTokenExp'))}</Text> */}
<Box
id="google_translate_element"
display="block"
className="bg-danger"
/>
</aside> </aside>
)} )}
<main <main
className={`h-100 ${slideFromRight ? "pe-3" : "ps-3"} d-flex flex-column gap-0`} className={`h-100 ${
slideFromRight ? "pe-3" : "ps-3"
} d-flex flex-column gap-0`}
style={{ style={{
width: `calc(100% - ${isDrawerOpen || openDrawerClick ? 232 : 74}px)`, width: `calc(100% - ${isDrawerOpen || openDrawerClick ? 230 : 74}px)`,
transition: "width 0.3s ease-in-out", transition: "width 0.3s ease-in-out",
}} }}
> >
{/* <header className="p-2 ps-0 pt-3 fw-400 border-bottom"> {/* <header className="p-2 ps-0 pt-3 fw-400 border-bottom">
@@ -637,261 +748,46 @@ const DashboardLayout = ({ isOnline }) => {
</header> */} </header> */}
<HeaderMain <HeaderMain
data={data}
slideDirecttion={slideFromRight} slideDirecttion={slideFromRight}
logOutHandler={logOutHandler} logOutHandler={logOutHandler}
icon icon
title={getTitle()} title={getTitle()}
/> />
{/* <CustomBreadcrumb /> */}
<AppContent /> {/* <CustomBreadcrumb /> */}
<AppContent data={data} />
</main> </main>
{/* =======[ Left ]============ */}
{slideFromRight ? (
<aside
className="h-100 position-relative sideBar pe-1"
// onMouseOver={() => setIsDrawerOpen(true)}
// onMouseLeave={() => setIsDrawerOpen(false)}
style={{
width: isDrawerOpen || openDrawerClick ? 232 : 74,
transition: "width 0.3s ease-in-out", // Smooth transition for width change
// overflow: "hidden",
backgroundColor: "#0041180A",
position: "relative",
// backgroundColor: "#002F0F",
}}
>
<div
className={`d-flex ${
isDrawerOpen || openDrawerClick
? "justify-content-end"
: "justify-content-center"
} p-3 pt-3 pb-4 position-relative `}
height={"10%"}
>
{isDrawerOpen || openDrawerClick ? (
<img
style={{
width: 120,
}}
src={logo}
alt="Logo"
/>
) : (
<img
style={{
width: 30,
}}
src={logoMini}
alt="Logo"
/>
)}
</div>
<Box
className="ps-2 scroll-bar"
style={{ height: "80%", overflowY: "scroll", overflowX: "hidden" }}
>
<Accordion m={0} allowToggle>
{nav.map(({ title, type, Icon, submenu, path }, index) => {
if (type === "accordion") {
return (
<AccordionItem key={index} border={"none"}>
<AccordionButton
style={{ height: "auto" }}
className={`${
isDrawerOpen || openDrawerClick
? "p-2 web-text-medium ps-3 justify-content-between"
: "p-2 ps-1 web-text-xlarge justify-content-center"
} rounded-1 link d-flex align-items-center gap-2 w-100 mb-1`}
flexDirection={"row-reverse"}
>
<Box
as="span"
display={"flex"}
gap={2}
alignItems={"center"}
flexDirection={"row-reverse"}
>
{Icon && <Icon className="web-text-large" />}
<Text
as={"span"}
display={
isDrawerOpen || openDrawerClick ? "flex" : "none"
}
alignItems="center"
overflow="hidden"
>
{title}
</Text>
</Box>
<AccordionIcon />
</AccordionButton>
<AccordionPanel
p={0}
pb={1}
display={"flex"}
flexDirection={"column"}
gap={1}
>
{submenu?.map(
(
{ title: subMenuTitle, path: link, icon: SubIcon },
i
) => (
<Box
key={i}
style={{ height: "auto", position: "relative" }}
className={`${
isDrawerOpen || openDrawerClick
? " web-text-medium ps-0 pe-4"
: " web-text-xlarge justify-content-center"
} d-flex align-items-center p-0`}
>
<Box
backgroundColor={"gray.300"}
style={{
position: "absolute",
top: 0,
width: 2,
right: 20,
height:
i === submenu?.length - 1 ? "55%" : "120%",
borderRadius: "0 0 10px 10px",
}}
/>
<Box
backgroundColor={"gray.300"}
style={{
position: "absolute",
width: 8,
right: 20,
height: 2,
}}
/>
<NavLink
flexDirection={"row-reverse"}
className={`${
isDrawerOpen || openDrawerClick
? "p-2 ps-1 me-1 web-text-medium "
: "p-2 ps-0 ms-0 zindex-3 ms-4 web-text-xlarge justify-content-center"
} rounded-1 link d-flex align-items-center gap-2 w-100 flex-direction-row-reverse`}
to={link}
style={{ flexDirection: "row-reverse" }}
>
{SubIcon && (
<SubIcon
className="web-text-large ms-0"
style={{ zIndex: 111 }}
/>
)}
<Text
as={"span"}
display={
isDrawerOpen || openDrawerClick
? "flex"
: "none"
}
alignItems="center"
overflow="hidden"
>
{subMenuTitle}
</Text>
</NavLink>
</Box>
)
)}
</AccordionPanel>
</AccordionItem>
);
} else if (type === "title") {
return (
<Text
as={"span"}
key={index}
className="web-text-xxsmall fw-600 mt-1 text-secondary fw-bold me-2"
padding={0}
display={"flex"}
justifyContent={"end"}
>
{title}
</Text>
);
} else if (type === "single") {
return (
<NavLink
key={index}
style={{
height: "auto",
position: "relative",
flexDirection: "row-reverse",
}}
className={`${
isDrawerOpen || openDrawerClick
? "p-2 web-text-medium"
: "p-2 ps-0 web-text-xlarge justify-content-start"
} rounded-1 link d-flex align-items-center gap-2 w-100`}
to={path}
>
{Icon && <Icon className="web-text-large ms-2" />}
<Text
as={"span"}
display={
isDrawerOpen || openDrawerClick ? "flex" : "none"
}
alignItems="center"
overflow="hidden"
>
{title}
</Text>
</NavLink>
);
} else {
return null;
}
})}
</Accordion>
</Box>
<Button
colorScheme={"forestGreen"}
rounded={"lg"}
// onMouseOver={() => setIsDrawerOpen(true)}
// onMouseLeave={() => setIsDrawerOpen(false)}
onClick={openDrawerOnClick}
style={{
width: 18,
height: 26,
position: "absolute",
left: -18,
bottom: 28,
zIndex: 99,
}}
>
{isDrawerOpen || openDrawerClick ? (
<ArrowRightIcon className="web-text-small " />
) : (
<ArrowLeftIcon className="web-text-small" />
)}
</Button>
</aside>
) : null}
</Box> </Box>
); );
}; };
export default DashboardLayout; export default DashboardLayout;
const AppContent = () => { const AppContent = ({ data }) => {
return ( return (
<Routes> <Routes>
{RouteLink.map(({ path, Component }, index) => ( {RouteLink.map(({ path, Component }, index) => (
<Route key={index} path={path} element={<Component />} /> <Route key={index} path={path} element={<Component />} />
))} ))}
<Route
path="/fawateer"
element={
data?.data?.role === "Maker" ? <CreateRequest /> : <ApproveRequest />
}
/>
<Route
path="/fawateer-history"
element={
data?.data?.role === "Maker" ? (
<ApproveHistoryMaker />
) : (
<ApproveHistory />
)
}
/>
<Route path="*" element={<NotFound />} /> <Route path="*" element={<NotFound />} />
</Routes> </Routes>
); );

View File

@@ -11,66 +11,84 @@ import {
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import React, { useContext, useEffect, useState } from "react"; import React, { useContext, useEffect, useState } from "react";
import { OPACITY_ON_LOAD } from "../../Layout/animations"; import { OPACITY_ON_LOAD } from "../../Layout/animations";
import DataTable from "../../Components/DataTable/DataTable"; import NormalTable from "../../Components/DataTable/NormalTable";
import Pagination from "../../Components/Pagination"; import Pagination from "../../Components/Pagination";
import GlobalStateContext from "../../Contexts/GlobalStateContext"; import GlobalStateContext from "../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../Components/CustomAlertDialog"; import CustomAlertDialog from "../../Components/CustomAlertDialog";
import { formatDate } from "../../Components/Functions/UTCConvertor"; import { formatDate } from "../../Components/Functions/UTCConvertor";
import { CheckIcon, CloseIcon } from "@chakra-ui/icons"; import { CheckIcon, CloseIcon } from "@chakra-ui/icons";
import { useGetDeleteHistoryQuery } from "../../Services/delete.request.service";
import { TABLE_PAGINATION } from "../../Constants/Paginations";
// import { formatDate } from "../../Components/Functions/UTCConvertor"; // import { formatDate } from "../../Components/Functions/UTCConvertor";
const DeletionHistory = () => { const DeletionHistory = () => {
const toast = useToast(); const toast = useToast();
const { slideFromRight, deleteHistory, setDeleteHistory } = const { slideFromRight, setDeleteHistory } =
useContext(GlobalStateContext); useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false); const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false); const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false); const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState(""); const [mouseEnteredId, setMouseEnteredId] = useState("");
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
// Cleanup the timer on component unmount
return () => clearTimeout(timer);
}, []);
// ====================================================[Table Filter]================================================================ // =========================== [Use State] =============================
const filteredData = deleteHistory.filter((item) => { const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
// Filter by name (case insensitive) const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
const name = item.Distribution; const [searchTerm, setSearchTerm] = useState("");
const searchLower = searchTerm.toLowerCase(); const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
const nameMatches = name.toLowerCase().includes(searchLower);
// Filter by status // Debounce the search term to avoid making a request on every keystroke
// const status = item.status; useEffect(() => {
// const statusLower = status ? "active" : "inactive"; const handler = setTimeout(() => {
setDebouncedSearchTerm(searchTerm);
}, 500); // Adjust delay as needed
return () => {
clearTimeout(handler);
};
}, [searchTerm]);
// const statusMatches =
// statusFilter === "all" ||
// (statusFilter === "active" && status === true) ||
// (statusFilter === "inactive" && status === false);
return nameMatches; const {
}); data: deleteHistory,
isLoading,
refetch
} = useGetDeleteHistoryQuery({
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
search: debouncedSearchTerm,
},
{
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
})
const formatDate = (date) => {
return new Date(date).toLocaleDateString('en-GB', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
});
};
// Use useEffect to refetch data when the component mounts
useEffect(() => {
refetch();
}, [refetch]);
// ====================================================[Table Setup]================================================================ // ====================================================[Table Setup]================================================================
const tableHeadRow = [ const tableHeadRow = [
"Sr No.", "Sr No.",
"Date", "Request On",
"Distribution Amount", "Client ID",
"Charges (USD)", "First Name",
"Year", "Last Name",
"Quater", "Country",
"Action", "Phone Number",
"Status"
]; ];
const extractedArray = filteredData?.map((item, index) => ({ const extractedArray = deleteHistory?.data?.rows?.map((item, index) => ({
id: item?.id, id: item?.id,
"Sr No.": ( "Sr No.": (
<Text <Text
@@ -83,7 +101,7 @@ const DeletionHistory = () => {
{index + 1}. {index + 1}.
</Text> </Text>
), ),
"Date": ( "Request On": (
<Text <Text
justifyContent={slideFromRight ? "right" : "left"} justifyContent={slideFromRight ? "right" : "left"}
as={"span"} as={"span"}
@@ -91,10 +109,10 @@ const DeletionHistory = () => {
className="d-flex align-items-center web-text-small" className="d-flex align-items-center web-text-small"
fontWeight={'500'} fontWeight={'500'}
> >
{formatDate(item.date)} {formatDate(item.Requested_on)}
</Text> </Text>
), ),
"Distribution Amount": ( "Client ID": (
<Text <Text
justifyContent={slideFromRight ? "right" : "left"} justifyContent={slideFromRight ? "right" : "left"}
as={"span"} as={"span"}
@@ -102,10 +120,10 @@ const DeletionHistory = () => {
className="d-flex align-items-center web-text-small" className="d-flex align-items-center web-text-small"
fontWeight={'500'} fontWeight={'500'}
> >
{item.Distribution} {item.clientId}
</Text> </Text>
), ),
"Charges (USD)": ( "First Name": (
<Text <Text
justifyContent={slideFromRight ? "right" : "left"} justifyContent={slideFromRight ? "right" : "left"}
as={"span"} as={"span"}
@@ -113,11 +131,11 @@ const DeletionHistory = () => {
className="d-flex align-items-center web-text-small" className="d-flex align-items-center web-text-small"
fontWeight={'500'} fontWeight={'500'}
> >
{item.charge} {item.firstName}
{/* {formatDate(item.charge)} */} {/* {formatDate(item.charge)} */}
</Text> </Text>
), ),
Year: ( "Last Name": (
<Text <Text
justifyContent={slideFromRight ? "right" : "left"} justifyContent={slideFromRight ? "right" : "left"}
as={"span"} as={"span"}
@@ -125,10 +143,10 @@ const DeletionHistory = () => {
className="d-flex align-items-center web-text-small" className="d-flex align-items-center web-text-small"
fontWeight={'500'} fontWeight={'500'}
> >
{item.year} {item.lastName}
</Text> </Text>
), ),
Quater: ( "Country": (
<Text <Text
justifyContent={slideFromRight ? "right" : "left"} justifyContent={slideFromRight ? "right" : "left"}
as={"span"} as={"span"}
@@ -136,18 +154,31 @@ const DeletionHistory = () => {
className="d-flex align-items-center web-text-small" className="d-flex align-items-center web-text-small"
fontWeight={'500'} fontWeight={'500'}
> >
{item.quarter} {item.country}
</Text> </Text>
), ),
Action: ( "Phone Number": (
<Box display={'flex'} justifyContent={'space-around'}> <Text
<Tooltip rounded={'sm'} fontSize={'xs'} label='Accept' bg='#fff' color={'green.500'} placement="left-start"> justifyContent={slideFromRight ? "right" : "left"}
<Button color="green.500" rounded={'sm'} size={'xs'}> as={"span"}
<CheckIcon /></Button></Tooltip> color={"gray.600"}
<Tooltip rounded={'sm'} fontSize={'xs'} label='Reject' bg='#fff' color={'red.500'} placement="left-start"> className="d-flex align-items-center web-text-small"
<Button color="red.500" rounded={'sm'} size={'xs'}> fontWeight={'500'}
<CloseIcon /></Button></Tooltip> >
</Box> {item.phoneNumber}
</Text>
),
"Status": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
color={item?.deletionStatus? "red.500": "blue.500"}
className="d-flex align-items-center web-text-small"
fontWeight={'600'}
>
{item.deletionStatus}
</Text>
), ),
})); }));
@@ -187,13 +218,13 @@ const DeletionHistory = () => {
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
/> />
<HStack display={"flex"} alignItems={"center"}> {/* <HStack display={"flex"} alignItems={"center"}>
<Pagination totalItems={10} /> <Pagination totalItems={10} />
</HStack> </HStack> */}
</HStack> </HStack>
</Box> </Box>
<DataTable <NormalTable
emptyMessage={`We don't have any Sponers `} emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow} tableHeadRow={tableHeadRow}
data={extractedArray} data={extractedArray}

View File

@@ -6,15 +6,20 @@ import {
HStack, HStack,
Input, Input,
Text, Text,
Tooltip,
useDisclosure,
useToast, useToast,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import React, { useContext, useEffect, useState } from "react"; import React, { useContext, useEffect, useState } from "react";
import { OPACITY_ON_LOAD } from "../../Layout/animations"; import { OPACITY_ON_LOAD } from "../../Layout/animations";
import DataTable from "../../Components/DataTable/DataTable"; import NormalTable from "../../Components/DataTable/NormalTable";
import Pagination from "../../Components/Pagination"; import Pagination from "../../Components/Pagination";
import GlobalStateContext from "../../Contexts/GlobalStateContext"; import GlobalStateContext from "../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../Components/CustomAlertDialog"; import CustomAlertDialog from "../../Components/CustomAlertDialog";
import { formatDate } from "../../Components/Functions/UTCConvertor"; import { formatDate } from "../../Components/Functions/UTCConvertor";
import { CheckIcon, CloseIcon } from "@chakra-ui/icons";
import DeletionRequestApprove from "./DeletionRequestApprove";
import { useGetDeleteRequestQuery } from "../../Services/delete.request.service";
// import { formatDate } from "../../Components/Functions/UTCConvertor"; // import { formatDate } from "../../Components/Functions/UTCConvertor";
const DeletionRequest = () => { const DeletionRequest = () => {
@@ -28,6 +33,32 @@ const DeletionRequest = () => {
const [mouseEntered, setMouseEntered] = useState(false); const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState(""); const [mouseEnteredId, setMouseEnteredId] = useState("");
const formatDate = (date) => {
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
};
const {
data,
isLoading: DeletionLoading
} = useGetDeleteRequestQuery()
const {
isOpen: isConfirmOpen,
onOpen: onConfirmOpen,
onClose: onConfirmClose,
} = useDisclosure();
const {
isOpen: isRejectOpen,
onOpen: onRejectOpen,
onClose: onRejectClose,
} = useDisclosure();
useEffect(() => { useEffect(() => {
// Simulate loading // Simulate loading
const timer = setTimeout(() => { const timer = setTimeout(() => {
@@ -39,9 +70,9 @@ const DeletionRequest = () => {
}, []); }, []);
// ====================================================[Table Filter]================================================================ // ====================================================[Table Filter]================================================================
const filteredData = deleteRequest.filter((item) => { const filteredData = data?.data?.rows?.filter((item) => {
// Filter by name (case insensitive) // Filter by name (case insensitive)
const name = item.Distribution; const name = item?.firstName;
const searchLower = searchTerm.toLowerCase(); const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower); const nameMatches = name.toLowerCase().includes(searchLower);
@@ -60,19 +91,21 @@ const DeletionRequest = () => {
// ====================================================[Table Setup]================================================================ // ====================================================[Table Setup]================================================================
const tableHeadRow = [ const tableHeadRow = [
"Sr No.", "Sr No.",
"Date", "Requested on",
"Distribution Amount", "Client ID",
"Charges (USD)", "First name",
"Year", "Last name",
"Quater", "Country",
"Amount", "Phone number",
"Status",
"Action"
]; ];
const extractedArray = filteredData?.map((item, index) => ({ const extractedArray = filteredData?.map((item, index) => ({
id: item?.id, id: item?.id,
"Sr No.": ( "Sr No.": (
<Text <Text
justifyContent={slideFromRight ? "right" : "left"} justifyContent={"left"}
as={"span"} as={"span"}
color={"gray.800"} color={"gray.800"}
className="d-flex align-items-center web-text-small" className="d-flex align-items-center web-text-small"
@@ -81,74 +114,112 @@ const DeletionRequest = () => {
{index + 1}. {index + 1}.
</Text> </Text>
), ),
"Date": ( "Requested on": (
<Text <Text
justifyContent={slideFromRight ? "right" : "left"} justifyContent={"left"}
as={"span"} as={"span"}
color={"gray.600"} color={"gray.600"}
className="d-flex align-items-center web-text-small" className="d-flex align-items-center web-text-small"
fontWeight={'500'} fontWeight={'500'}
> >
{formatDate(item.date)} {formatDate(item?.Requested_on)}
</Text> </Text>
), ),
"Distribution Amount": ( "Client ID": (
<Text <Text
justifyContent={slideFromRight ? "right" : "left"} justifyContent={"left"}
as={"span"} as={"span"}
color={"gray.600"} color={"gray.600"}
className="d-flex align-items-center web-text-small" className="d-flex align-items-center web-text-small"
fontWeight={'500'} fontWeight={'500'}
> >
{item.Distribution} {item?.clientId}
</Text> </Text>
), ),
"Charges (USD)": ( "First name": (
<Text <Text
justifyContent={slideFromRight ? "right" : "left"} justifyContent={"left"}
as={"span"} as={"span"}
color={"gray.800"} color={"gray.800"}
className="d-flex align-items-center web-text-small" className="d-flex align-items-center web-text-small"
fontWeight={'500'} fontWeight={'500'}
> >
{item.charge} {item?.firstName}
{/* {formatDate(item.charge)} */} {/* {formatDate(item.charge)} */}
</Text> </Text>
), ),
Year: ( "Last name": (
<Text <Text
justifyContent={slideFromRight ? "right" : "left"} justifyContent={"left"}
as={"span"} as={"span"}
color={"gray.800"} color={"gray.800"}
className="d-flex align-items-center web-text-small" className="d-flex align-items-center web-text-small"
fontWeight={'500'} fontWeight={'500'}
> >
{item.year} {item?.lastName}
</Text> </Text>
), ),
Quater: ( Country: (
<Text <Text
justifyContent={slideFromRight ? "right" : "left"} justifyContent={"left"}
as={"span"} as={"span"}
color={"gray.600"} color={"gray.600"}
className="d-flex align-items-center web-text-small" className="d-flex align-items-center web-text-small"
fontWeight={'500'} fontWeight={'500'}
> >
{item.quater} {item?.country}
</Text> </Text>
), ),
Amount: ( "Phone number": (
<Text <Text
justifyContent={slideFromRight ? "right" : "left"} justifyContent={"left"}
as={"span"} as={"span"}
color={"gray.600"} color={"gray.600"}
className="d-flex align-items-center web-text-small" className="d-flex align-items-center web-text-small"
fontWeight={'500'} fontWeight={'500'}
> >
{item.amount} {item?.phoneNumber}
</Text> </Text>
), ),
Status:(<Box w={"auto"} display={'flex'} alignItems={'center'} isTruncated={true}>
<Text
as={'span'}
fontWeight={"700"}
textTransform={"none"}
color={item.kycStatus ? "blue.500" : "red.500"}
// px={2}
py={0.5}
variant={'solid'}
>
{item.KYCStatus ? "Completed" : "Not complete"}
</Text>
</Box>),
Action: (
<Box display={"flex"} justifyContent={"center"} gap={2}>
<Button
// colorScheme="forestGreen"
// color="green.500"
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={2}
py={1}
onClick={() => {
setActionId(item.id);
onConfirmOpen();
}}
colorScheme="green"
variant={"outline"}
cursor={"pointer"}
>
Review
</Button>
</Box>
),
})); }));
const handleDelete = () => { const handleDelete = () => {
@@ -187,17 +258,17 @@ const DeletionRequest = () => {
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
/> />
<HStack display={"flex"} alignItems={"center"}> {/* <HStack display={"flex"} alignItems={"center"}>
<Pagination totalItems={10} /> <Pagination totalItems={10} />
</HStack> </HStack> */}
</HStack> </HStack>
</Box> </Box>
<DataTable <NormalTable
emptyMessage={`We don't have any Sponers `} emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow} tableHeadRow={tableHeadRow}
data={extractedArray} data={extractedArray}
isLoading={isLoading} isLoading={DeletionLoading}
viewActionId={actionId} viewActionId={actionId}
setViewActionId={setActionId} setViewActionId={setActionId}
// totalPages={10} // totalPages={10}
@@ -213,6 +284,22 @@ const DeletionRequest = () => {
alertHandler={handleDelete} alertHandler={handleDelete}
isLoading={isLoading} isLoading={isLoading}
/> />
<DeletionRequestApprove
data={deleteRequest}
isOpen={isConfirmOpen}
onClose={onConfirmClose}
id={actionId}
// firstField={firstField}
/>
{/* <DepositRequestReject
isOpen={isRejectOpen}
onClose={onRejectClose}
id={actionId}
/> */}
</Box> </Box>
); );
}; };

View File

@@ -0,0 +1,207 @@
import {
Badge,
Box,
Button,
FormControl,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useEffect, useState } from "react";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
import { useGetDepositRequestByIdQuery, useGetDepositRequestQuery, useUpdateDepositRequestMutation } from "../../Services/deposit.request.service";
import FullscreenLoaders from "../../Components/Loaders/FullscreenLoaders";
import ToastBox from "../../Components/ToastBox";
import { useGetDrawalRequestQuery } from "../../Services/drawal.request.service";
import { useApproveDepositRequestMutation, useGetDeleteRequestByIdQuery } from "../../Services/delete.request.service";
const FILE_TYPES = ["image/jpeg", "image/png", "image/gif"];
export const conformModalSchema = yup.object().shape({
adminComment: yup.string().notRequired(),
});
const DeletionRequestApprove = ({ isOpen, onClose, firstField, id, data:requestData }) => {
const toast = useToast()
const [file, setFile] = useState();
const [isBtnLoading , setIsBtnLoading] = useState(false)
const [isBtnLoadingReject , setIsBtnLoadingReject] = useState(false)
const [isReject , setIsReject] = useState(false)
const fileredData = requestData?.find((item)=> item?.id === id)
const [ updateApproveRequest ] = useApproveDepositRequestMutation()
const { data, isLoading } = useGetDeleteRequestByIdQuery(id, {
skip: !id,
});
const {
register,
reset,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
useEffect(() => {
reset({
comment:fileredData?.comment
})
}, [requestData, id])
const onSubmit = async(data) => {
setIsBtnLoading(isReject?false:true)
setIsBtnLoadingReject(isReject)
const approveReq = {
adminComment:data?.adminComment,
deletionStatus: isReject?"Reject": "Approved"
}
try {
const res = await updateApproveRequest({ id ,data:approveReq })
if (res?.error) {
toast({
render: () => (
<ToastBox message={res?.error?.data?.message} status={"error"} />
),
});
heandleOnClose()
}else if(res?.data?.statusCode === 200) {
toast({
render: () => (
<ToastBox message={res?.data?.message} />
),
});
heandleOnClose()
}
} catch (error) {
}
};
const onReject = () => {
}
useEffect(() => {
if (data) {
reset({
comment: data?.data?.comment,
});
}
}, [data, reset]);
const heandleOnClose = () =>{
reset()
onClose()
setIsBtnLoading(false)
setIsReject(false)
setIsBtnLoadingReject(false)
}
return (
<Modal isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
<ModalOverlay />
<ModalContent pb={4}>
{/* <ModalHeader fontSize={"md"}>Confirm</ModalHeader> */}
<ModalCloseButton />
{isLoading ? (
<FullscreenLoaders height={"50vh"} />
) : (
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody>
<FormControl mt={6} mb={4}>
<FormLabel fontSize="sm">Investor Comment <Badge colorScheme="green">{fileredData?.currencyCode}</Badge></FormLabel>
{/* <Textarea
focusBorderColor="green.400"
name="comment"
{...register("comment")}
fontSize="sm"
type="text"
size="sm"
readOnly
rows={5}
/>
{errors.comment && (
<Text fontSize="xs" color="red">
{errors.comment.message}
</Text>
)} */}
<Text fontSize="sm" fontWeight={500} color={'gray.600'}>{data?.data?.comment}</Text>
</FormControl>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Admin Comment</FormLabel>
<Textarea
rows={5}
focusBorderColor="green.400"
name="adminComment"
{...register("adminComment")}
fontSize="sm"
type="textarea"
size="sm"
placeholder={"Enter your comments...."}
resize={"none"}
/>
{errors.adminComment && (
<Text fontSize="xs" color="red">
{errors.adminComment.message}
</Text>
)}
</FormControl>
</ModalBody>
<ModalFooter>
<Button
colorScheme="red"
mr={3}
type="submit"
size={"sm"}
rounded={"sm"}
variant={'ghost'}
onClick={()=> setIsReject(true)}
isLoading={isBtnLoadingReject}
>
Reject
</Button>
<Button
colorScheme="forestGreen"
variant="solid"
size={"sm"}
rounded={"sm"}
isLoading={isBtnLoading}
type="submit"
>
Approve
</Button>
</ModalFooter>
</Box>
)}
</ModalContent>
</Modal>
);
};
export default DeletionRequestApprove;

View File

@@ -0,0 +1,158 @@
import {
Box,
Button,
FormControl,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useEffect, useState } from "react";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
import { useDepositRejectMutation } from "../../../Services/deposit.request.service";
import ToastBox from "../../../Components/ToastBox";
export const conformModalSchema = yup.object().shape({
comments: yup.string().required("Comment is required"),
});
const DeletionRequestReject = ({ isOpen, onClose, firstField ,id}) => {
const [isBtnLoading , setIsBtnLoading] = useState(false)
const toast = useToast()
const {
register,
reset,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
const [ depositReject ] = useDepositRejectMutation()
const onSubmit = async(data) => {
setIsBtnLoading(true)
try {
const res = await depositReject({ id ,data})
if (res?.error) {
toast({
render: () => (
<ToastBox message={res?.error?.data?.message} status={"error"} />
),
});
setIsBtnLoading(false)
onClose();
}else if(res?.data?.statusCode === 200) {
toast({
render: () => (
<ToastBox message={res?.data?.message} />
),
});
setIsBtnLoading(false)
onClose();
}
} catch (error) {
console.log(error);
}
};
const handleFileChange = (event) => {
const selectedFile = event.target.files[0];
setFile(selectedFile);
};
const { data, isLoading } =
(id, {
skip: !id,
});
useEffect(() => {
if (data) {
reset({
investorAmount: data?.data?.investorAmount,
});
}
}, [data, reset]);
const heandleOnClose = () =>{
reset()
onClose()
}
return (
<Modal isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
<ModalOverlay />
<ModalContent pb={4}>
<ModalHeader fontSize={"md"}>Reject</ModalHeader>
<ModalCloseButton />
{isLoading ? (
<FullscreenLoaders height={"50vh"} />
) : (
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea
rows={6}
focusBorderColor="green.400"
name="comments"
{...register("comments")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your comments...."}
rounded={"md"}
resize={"none"}
/>
{errors.comments && (
<Text fontSize="xs" color="red">
{errors.comments.message}
</Text>
)}
</FormControl>
</ModalBody>
<ModalFooter>
<Button
colorScheme="gray"
mr={3}
onClick={onClose}
size={"sm"}
rounded={"sm"}
>
Cancel
</Button>
<Button
colorScheme="forestGreen"
variant="solid"
size={"sm"}
rounded={"sm"}
isLoading={isBtnLoading}
type="submit"
>
Send
</Button>
</ModalFooter>
</Box>
)}
</ModalContent>
</Modal>
);
};
export default DeletionRequestReject;

View File

@@ -0,0 +1,85 @@
import {
Box,
Button,
FormControl,
FormLabel,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Textarea,
} from "@chakra-ui/react";
import React from "react";
const InvestorComment = ({ isOpen, onClose }) => {
return (
<div>
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader fontSize={"md"}></ModalHeader>
<ModalCloseButton />
<Box as="form">
<ModalBody>
<FormControl mb={5}>
<FormLabel fontSize="sm">Investor Comment</FormLabel>
<Textarea
rows={6}
focusBorderColor="green.400"
name="fileName"
// {...register("fileName")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"The amount of investment is not updated... "}
rounded={"md"}
resize={"none"}
/>
</FormControl>
<FormControl mb={4}>
<FormLabel fontSize="sm">Admin Commment</FormLabel>
<Textarea
rows={6}
focusBorderColor="green.400"
name="fileName"
// {...register("fileName")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your comment...."}
rounded={"md"}
resize={"none"}
/>
</FormControl>
</ModalBody>
<ModalFooter pb={8}>
<Button
colorScheme="gray"
mr={3}
onClick={onClose}
size={"sm"}
rounded={"sm"}
>
Reject
</Button>
<Button
colorScheme="forestGreen"
variant="solid"
size={"sm"}
rounded={"sm"}
fontWeight={500}
>
Approve
</Button>
</ModalFooter>
</Box>
</ModalContent>
</Modal>
</div>
);
};
export default InvestorComment;

View File

@@ -5,14 +5,6 @@ import {
Button, Button,
HStack, HStack,
Input, Input,
Menu,
MenuButton,
MenuItem,
MenuList,
Portal,
Select,
Switch,
Tag,
Text, Text,
Tooltip, Tooltip,
useDisclosure, useDisclosure,
@@ -22,11 +14,7 @@ import React, { useContext, useEffect, useState, useRef } from "react";
import { HiDotsVertical } from "react-icons/hi"; import { HiDotsVertical } from "react-icons/hi";
import { Link, Link as RouterLink, useNavigate } from "react-router-dom"; import { Link, Link as RouterLink, useNavigate } from "react-router-dom";
import { import {
AddIcon,
DeleteIcon,
EditIcon, EditIcon,
EmailIcon,
ViewIcon,
} from "@chakra-ui/icons"; } from "@chakra-ui/icons";
import { OPACITY_ON_LOAD } from "../../../Layout/animations"; import { OPACITY_ON_LOAD } from "../../../Layout/animations";
import DataTable from "../../../Components/DataTable/DataTable"; import DataTable from "../../../Components/DataTable/DataTable";
@@ -36,6 +24,7 @@ import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import ToastBox from "../../../Components/ToastBox"; import ToastBox from "../../../Components/ToastBox";
import { debounce } from "../../Master/Sponser/AddSponser"; import { debounce } from "../../Master/Sponser/AddSponser";
import { useGetBankQuery } from "../../../Services/bank.details.service"; import { useGetBankQuery } from "../../../Services/bank.details.service";
import NormalTable from '../../../Components/DataTable/NormalTable'
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
@@ -64,7 +53,6 @@ const BankDetails = () => {
error, error,
} = useGetBankQuery({ page: 1, size: 10 }); } = useGetBankQuery({ page: 1, size: 10 });
console.log(bankDetails?.data);
useEffect(() => { useEffect(() => {
// Simulate loading // Simulate loading
@@ -115,10 +103,9 @@ const BankDetails = () => {
return nameMatches; return nameMatches;
}); });
console.log(bankDetails); const extractedArray = filteredData?.map((item,index) => ({
const extractedArray = filteredData?.map((item) => ({
id: item?.id, id: item?.id,
"Sr N/O": ( "Sr N/O": (
<Text <Text
@@ -133,18 +120,18 @@ const BankDetails = () => {
"Country name": ( "Country name": (
<Box w={"auto"} isTruncated={true}> <Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}> <Text as={"span"} color={"teal.900"}>
{item.country_xid} {item?.country?.countryName}
</Text> </Text>
</Box> </Box>
), ),
"Account Name": ( "Account Name": (
<Box w={"auto"} isTruncated={true}> <Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}> <Text as={"span"} color={"teal.900"}>
{item.accountName} {item?.accountName}
</Text> </Text>
</Box> </Box>
), ),
"Account No ": ( "Account No": (
<Box w={"auto"} isTruncated={true}> <Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}> <Text as={"span"} color={"teal.900"}>
{item?.accountNumber} {item?.accountNumber}
@@ -159,16 +146,17 @@ const BankDetails = () => {
</Box> </Box>
), ),
Action: ( Action: (
<Box display={"flex"} justifyContent={"space-between"} gap={2}> <Box display={"flex"} justifyContent={"space-between"}>
<Tooltip <Tooltip
rounded={"sm"} rounded={"sm"}
fontSize={"xs"} fontSize={"xs"}
label="View" label="Edit"
bg="#fff" bg="#fff"
color={"green.500"} color={"green.500"}
placement="top" placement="top"
> >
<Button <Button
bg={index % 2 === 0 ? "#6311cb14" : "#fff"}
onClick={() => { onClick={() => {
navigate(`/bank-details/edit-bank-details/${item.id}`); navigate(`/bank-details/edit-bank-details/${item.id}`);
}} }}
@@ -229,8 +217,8 @@ const BankDetails = () => {
</HStack> </HStack>
</Box> </Box>
<DataTable <NormalTable
emptyMessage={`We don't have any Sponers `} emptyMessage={`We don't have any Details`}
tableHeadRow={tableHeadRow} tableHeadRow={tableHeadRow}
data={extractedArray} data={extractedArray}
isLoading={isLoading} isLoading={isLoading}

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react"; import React, { useContext, useEffect, useState } from "react";
import { OPACITY_ON_LOAD } from "../../../Layout/animations"; import { OPACITY_ON_LOAD } from "../../../Layout/animations";
import { Box, useToast } from "@chakra-ui/react"; import { Box, useToast } from "@chakra-ui/react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
@@ -10,7 +10,8 @@ import FullscreenLoaders from "../../../Components/Loaders/FullscreenLoaders";
import CustomAlertDialog from "../../../Components/CustomAlertDialog"; import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import SwitchButton from "../../../Components/SwitchButton"; import SwitchButton from "../../../Components/SwitchButton";
import * as yup from "yup"; import * as yup from "yup";
import { useGetBankQuery, useUpdateBankDetailsMutation } from "../../../Services/bank.details.service"; import { useGetBankDetailsQuery,useUpdateBankDetailsMutation } from "../../../Services/bank.details.service";
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
// import { useUpdateBankDetailsMutation, useGetBankQuery } from "../../../Services/investorDetails.service"; // import { useUpdateBankDetailsMutation, useGetBankQuery } from "../../../Services/investorDetails.service";
const editBankSchema = yup.object().shape({ const editBankSchema = yup.object().shape({
@@ -28,12 +29,17 @@ const EditBankDetails = () => {
const id = params?.id; const id = params?.id;
const [isLoadingBtn, setIsLoadingBtn] = useState(false); const [isLoadingBtn, setIsLoadingBtn] = useState(false);
const { InvestorDetails, setInvestorDetails, slideFromRight } =
useContext(GlobalStateContext);
const [alert, setAlert] = useState(false); const [alert, setAlert] = useState(false);
const [form, setForm] = useState({}); const [form, setForm] = useState({});
const [isSwitchOn, setIsSwitchOn] = useState(false); const [isSwitchOn, setIsSwitchOn] = useState(false);
const [updateBankDetails] = useUpdateBankDetailsMutation(); const [updateBankDetails] = useUpdateBankDetailsMutation();
const { data: bankDetails, error, isLoading } = useGetBankQuery({ id }, { skip: !id }); const { data: bankDetails, error, isLoading, refetch } = useGetBankDetailsQuery({ id }, { skip: !id });
console.log(bankDetails);
const { const {
control, control,
@@ -45,16 +51,22 @@ const EditBankDetails = () => {
}); });
useEffect(() => { useEffect(() => {
if (bankDetails) { setInvestorDetails({
...bankDetails?.data,
});
// refetch()
if (bankDetails?.data) {
reset({ reset({
accountNumber: bankDetails.accountNumber, accountName: bankDetails?.data?.accountName,
swiftCode: bankDetails.swiftCode, countryName: bankDetails?.data?.countryName,
bankName: bankDetails.bankName, accountNumber: bankDetails?.data?.accountNumber,
bankAddress: bankDetails.bankAddress, swiftCode: bankDetails?.data?.swiftCode,
IBANnumber: bankDetails.IBANnumber, bankName: bankDetails?.data?.bankName,
bankAddress: bankDetails?.data?.bankAddress,
IBANnumber: bankDetails?.data?.IBANnumber,
}); });
} }
}, [bankDetails, reset]); }, [bankDetails, reset,id]);
if (isLoading) { if (isLoading) {
return <FullscreenLoaders />; return <FullscreenLoaders />;
@@ -62,7 +74,6 @@ const EditBankDetails = () => {
const handleConfirm = async () => { const handleConfirm = async () => {
setIsLoadingBtn(true); setIsLoadingBtn(true);
try { try {
const formData = { const formData = {
...form, ...form,
@@ -77,6 +88,7 @@ const EditBankDetails = () => {
setIsLoadingBtn(false); setIsLoadingBtn(false);
setAlert(false); setAlert(false);
refetch()
navigate("/bank-details"); navigate("/bank-details");
} else { } else {
throw new Error("Something went wrong"); throw new Error("Something went wrong");
@@ -91,66 +103,76 @@ const EditBankDetails = () => {
} }
}; };
console.log(bankDetails?.accountNumber);
const formEditFields = [ const formEditFields = [
{ // {
label: "Country Name", // label: "Country Name",
placeHolder: "Enter country name", // placeHolder:"",
name: "countryName", // name: "countryName",
type: "text", // type: "text",
isRequired: true, // isRequired: true,
section: "Add Details", // section: "Add Details",
maxLength: 50, // maxLength: 50,
width: "32%", // width: "32%",
}, // },
{ {
label: "Account Name", label: "Account Name",
// value:bankDetails?.accountName,
name: "accountName", name: "accountName",
placeHolder: "Enter account name", placeHolder: "",
type: "text", type: "text",
isRequired: true, isRequired: true,
section: "Add Details", section: "Add Details",
maxLength: 255, // maxLength: 255,
width: "32%", width: "32%",
}, },
{ {
label: "Account Number *", label: "Account Number",
name: "accountNumber", name: "accountNumber",
placeHolder: "Enter account number", placeHolder: "",
type: "text", type: "text",
section: "Add Details", section: "Add Details",
width: "32%", width: "32%",
isRequired: true,
}, },
{ {
label: "IBAN Number", label: "IBAN Number",
name: "IBANnumber", name: "IBANnumber",
placeHolder: "Enter IBAN number", placeHolder: "",
type: "text", type: "text",
section: "Add Details", section: "Add Details",
width: "32%", width: "32%",
isRequired: true,
}, },
{ {
label: "SWIFT Code", label: "SWIFT Code",
value: bankDetails?.data?.swiftCode,
name: "swiftCode", name: "swiftCode",
placeHolder: "Enter SWIFT code", placeHolder: "",
type: "text", type: "text",
section: "Add Details", section: "Add Details",
width: "32%", width: "32%",
isRequired: true,
}, },
{ {
label: "Bank Name", label: "Bank Name",
name: "bankName", name: "bankName",
placeHolder: "Enter bank name", placeHolder: "",
type: "text", type: "text",
section: "Add Details", section: "Add Details",
width: "32%", width: "32%",
isRequired: true,
}, },
{ {
label: "Bank Address", label: "Bank Address",
name: "bankAddress", name: "bankAddress",
placeHolder: "Enter bank address", placeHolder: "",
type: "text", type: "text",
section: "Add Details", section: "Add Details",
width: "32%", width: "32%",
isRequired: true,
}, },
]; ];
@@ -163,7 +185,7 @@ const EditBankDetails = () => {
return groups; return groups;
}, {}); }, {});
const onSubmit = (data) => { const onSubmit = async(data) => {
if (!Object.keys(errors).length) { if (!Object.keys(errors).length) {
setForm(data); setForm(data);
setAlert(true); setAlert(true);

View File

@@ -10,6 +10,7 @@ import {
Textarea, Textarea,
Button, Button,
Text, Text,
useToast,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup"; import { yupResolver } from "@hookform/resolvers/yup";
@@ -19,13 +20,35 @@ import { v4 as uuidv4 } from "uuid";
import GlobalStateContext from "../../Contexts/GlobalStateContext"; import GlobalStateContext from "../../Contexts/GlobalStateContext";
import { OPACITY_ON_LOAD } from "../../Layout/animations"; import { OPACITY_ON_LOAD } from "../../Layout/animations";
import FormInputMain from "../../Components/FormInputMain"; import FormInputMain from "../../Components/FormInputMain";
import { useGetContactQuery } from "../../Services/contact.service"; import {
useGetContactQuery,
useUpdateContactMutation,
} from "../../Services/contact.service";
import FullscreenLoaders from "../../Components/Loaders/FullscreenLoaders"; import FullscreenLoaders from "../../Components/Loaders/FullscreenLoaders";
import ToastBox from "../../Components/ToastBox";
export const addSponser = yup.object().shape({ export const addSponser = yup.object().shape({
phoneNumber: yup.string().required("Phone Number is required"), phoneNumber: yup
emailAddress: yup.string().required("E-mail ID is required"), .string()
websiteUrl: yup.string().required("Website URL is required"), .required("Phone Number is required"),
// .matches(
// /^\+?[1-9]\d{1,14}$/,
// "Phone Number must include a valid ISD code and be in E.164 format"
// ),
emailAddress: yup
.string()
.required("E-mail ID is required")
.matches(
/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
"Invalid email address"
),
websiteUrl: yup
.string()
.required("Website URL is required")
.matches(
/^(https?:\/\/)?([\w.-]+)+(:\d+)?(\/[\w.-]*)*\/?$/,
"Invalid URL format"
),
}); });
export function debounce(func, delay) { export function debounce(func, delay) {
@@ -37,8 +60,10 @@ export function debounce(func, delay) {
} }
const Contact = () => { const Contact = () => {
const toast = useToast();
const navigate = useNavigate(); const navigate = useNavigate();
const [form, setForm] = useState({}); const [form, setForm] = useState({});
const [isLoading, setIsLoading] = useState(false);
// const { sponser, setSponser } = useContext(GlobalStateContext); // const { sponser, setSponser } = useContext(GlobalStateContext);
const { const {
@@ -48,24 +73,24 @@ const Contact = () => {
formState: { errors }, formState: { errors },
} = useForm({ } = useForm({
resolver: yupResolver(addSponser), resolver: yupResolver(addSponser),
// mode: all
}); });
console.log(errors); console.log(errors);
const { const {
data: contact, data: contact,
isLoading: contactLoading, isLoading: contactLoading,
error, error,
} = useGetContactQuery({ page: 1, size: 10 }); } = useGetContactQuery();
const [updateContact] = useUpdateContactMutation();
console.log(contact?.data);
useEffect(() => { useEffect(() => {
if (contact) { if (contact) {
reset({ reset({
phoneNumber: contact.phoneNumber, phoneNumber: contact?.data[0]?.phoneNumber,
emailAddress: contact.emailAddress, emailAddress: contact?.data[0]?.emailAddress,
websiteUrl: contact.websiteUrl, websiteUrl: contact?.data[0]?.websiteUrl,
}); });
} }
}, [contact, reset]); }, [contact, reset]);
@@ -81,8 +106,8 @@ const Contact = () => {
name: "phoneNumber", name: "phoneNumber",
type: "text", type: "text",
isRequired: true, isRequired: true,
section: "Add Details", section:"",
defaultValue: contact?.phoneNumber || "", // value: contact?.phoneNumber || "",
}, },
{ {
label: "E-mail ID", label: "E-mail ID",
@@ -90,8 +115,8 @@ const Contact = () => {
placeHolder: " ", placeHolder: " ",
type: "text", type: "text",
isRequired: true, isRequired: true,
section: "Add Details", section:"",
defaultValue: contact?.emailAddress || "", // value: contact?.emailAddress || "",
}, },
{ {
label: "Website URL", label: "Website URL",
@@ -99,8 +124,8 @@ const Contact = () => {
placeHolder: " ", placeHolder: " ",
type: "text", type: "text",
isRequired: true, isRequired: true,
section: "Add Details", section:"",
defaultValue: contact?.websiteUrl || "", // value: contact?.websiteUrl || "",
}, },
]; ];
@@ -113,10 +138,20 @@ const Contact = () => {
return groups; return groups;
}, {}); }, {});
const onSubmit = (data) => { const onSubmit = async (data) => {
if (!Object.keys(errors).length) { setIsLoading(true);
setForm(data); try {
setAlert(true); const res = await updateContact(data);
if (res?.data?.statusCode === 200) {
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
setIsLoading(false);
}
} catch (error) {
console.log(error);
setIsLoading(false);
} }
}; };
@@ -127,6 +162,7 @@ const Contact = () => {
control={control} control={control}
errors={errors} errors={errors}
onSubmit={handleSubmit(onSubmit)} onSubmit={handleSubmit(onSubmit)}
btnLoading={isLoading}
/> />
</Box> </Box>
); );

View File

@@ -0,0 +1,360 @@
import {
Avatar,
Badge,
Box,
HStack,
Input,
Select,
Switch,
Text,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useState, useRef } from "react";
import { Link, Link as RouterLink, useNavigate } from "react-router-dom";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import { debounce } from "../../../Master/Sponser/AddSponser";
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
import NormalTable from "../../../../Components/DataTable/NormalTable";
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
import Pagination from "../../../../Components/Pagination";
import ToastBox from "../../../../Components/ToastBox";
import ReasonBanModal from "./ReasonBanModal";
import { useGetbanInvestorQuery } from "../../../../Services/ban.investor.service";
import { TABLE_PAGINATION } from "../../../../Constants/Paginations";
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
const BankInvestor = () => {
const navigate = useNavigate();
const toast = useToast();
const thirdField = useRef();
const { bankInvestor, setBankInvestor, slideFromRight } =
useContext(GlobalStateContext);
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
const { isOpen: isOpen, onOpen: onOpen, onClose: onClose } = useDisclosure();
const formatDate = (date) => {
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
};
// =========================== [Use State] =============================
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
const [searchTerm, setSearchTerm] = useState("");
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
const [status, setStatus] = useState("");
const [kyc, setKyc] = useState("");
const [country, setCountry] = useState("");
// Debounce the search term to avoid making a request on every keystroke
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedSearchTerm(searchTerm);
}, 500); // Adjust delay as needed
return () => {
clearTimeout(handler);
};
}, [searchTerm]);
const {
data,
isLoading: unbanLoading,
error,
refetch,
} = useGetbanInvestorQuery(
{
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
search: debouncedSearchTerm,
KYCStatus: kyc,
country_xid: country,
},
{
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
}
);
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
// Cleanup the timer on component unmount
return () => clearTimeout(timer);
}, []);
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
"Sr N/O",
"Date",
"Client ID",
"First Name",
"Last Name",
"Country",
"Phone Number",
"E-mail ID",
"KYC Status",
"Action",
];
const handleUpdateStatus = debounce((id) => {
setBankInvestor((prevData) =>
prevData.map((bankInvestor) =>
bankInvestor.id === id ? { ...bankInvestor } : bankInvestor
)
);
toast({
render: () => <ToastBox message={"Status changed succesfully.!"} />,
});
}, 300);
// ====================================================[Table Filter]================================================================
const filteredData = data?.data?.rows?.filter((item) => {
// Filter by name (case insensitive)
const name = item?.clientReference_id;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name?.toLowerCase().includes(searchLower);
// Filter by status
// const status = item.status;
// const statusLower = status ? "active" : "inactive";
// const statusMatches =
// statusFilter === "all" ||
// (statusFilter === "active" && status === true) ||
// (statusFilter === "inactive" && status === false);
return nameMatches;
});
const extractedArray = data?.data?.rows?.map((item) => ({
id: item?.id,
"Sr N/O": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"gray.600"}
className="d-flex align-items-center fw-bold web-text-small"
>
{item.id}
</Text>
),
Date: (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{formatDate(item?.date)}
</Text>
</Box>
),
"Client ID": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.clientReference_id}
</Text>
</Box>
),
"First Name": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.firstName}
</Text>
</Box>
),
"Last Name": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.lastName}
</Text>
</Box>
),
Country: (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.country}
</Text>
</Box>
),
"Phone Number": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.phoneNumber}
</Text>
</Box>
),
"E-mail ID": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.emailAddress}
</Text>
</Box>
),
"KYC Status": (
<Box w={"auto"} isTruncated={true}>
<Badge
fontWeight={"500"}
textTransform={"none"}
color={item?.KYCStatus === false ? "red" : "blue"}
px={2}
py={0.5}
variant={"ghost"}
>
{item?.KYCStatus === true ? "Completed" : "Incompleted"}
</Badge>
</Box>
),
Action: (
<Box w={"auto"} isTruncated={true}>
<Badge
cursor={"pointer"}
fontWeight={"500"}
textTransform={"none"}
colorScheme={"red"}
px={2}
py={0.5}
onClick={() => {
setActionId(item?.id);
onOpen();
}}
>
Ban Investor
</Badge>
</Box>
),
}));
console.log(extractedArray);
const handleDelete = () => {
const updatedInvestorDetails = InvestorDetails.filter(
(sponsor) => sponsor.id !== actionId
);
setTimeout(() => {
setInvestorDetails(updatedInvestorDetails);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
const handleEdit = (id) => {
setActionId(id);
onEditOpen();
};
return (
<Box {...OPACITY_ON_LOAD}>
<Box bg="white.500">
<HStack
display={"flex"}
justifyContent={"space-between"}
ps={1}
pe={1}
pb={4}
pt={4}
spacing="24px"
>
<Input
mt={1}
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<HStack display={"flex"} alignItems={"center"}>
{/* <Select
focusBorderColor="green.500"
size={"sm"}
fontSize={"xs"}
cursor={"pointer"}
onChange={(e) => setStatus(e.target.value)}
value={status}
>
<option value="" defaultValue={""} disabled hidden>
Status
</option>
<option value="">Status</option>
<option value="0">Ban</option>
<option value="1">UnBan</option>
</Select> */}
<Select
focusBorderColor="green.500"
size={"sm"}
fontSize={"xs"}
cursor={"pointer"}
onChange={(e) => setKyc(e.target.value)}
value={kyc}
>
<option value="" defaultValue={""} disabled hidden>
KYC Status
</option>
<option value="">KYC Status</option>
<option value="0">Incompleted</option>
<option value="1">Completed</option>
</Select>
<Select
focusBorderColor="green.500"
size={"sm"}
fontSize={"xs"}
cursor={"pointer"}
onChange={(e) => setCountry(e.target.value)}
value={country}
>
<option value="" defaultValue={""} disabled hidden>
Country
</option>
<option value="">All</option>
<option value="1">Behrain</option>
<option value="2">Kuwait</option>
<option value="3">Oman</option>
<option value="4">Qatar</option>
<option value="5">Saudi arabia</option>
<option value="6">United arab emirates</option>
</Select>
</HStack>
</HStack>
</Box>
<NormalTable
emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
setMouseEnteredId={setMouseEnteredId}
setMouseEntered={setMouseEntered}
/>
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>
<ReasonBanModal isOpen={isOpen} onClose={onClose} id={actionId} />
</Box>
);
};
export default BankInvestor;

View File

@@ -0,0 +1,27 @@
import { Box, Tab, TabList, TabPanel, TabPanels, Tabs } from "@chakra-ui/react";
import React from "react";
import BankInvestor from "./BankInvestor";
import UnbanInvestor from "../UnbanInvestor/UnbanInvestor";
const Investor = () => {
return (
<Box overflowY={"scroll"} height={"100vh"}>
<Tabs colorScheme="green" mt={3}>
<TabList>
<Tab fontSize={"sm"}>Unban Investor</Tab>
<Tab fontSize={"sm"}>Ban Investor</Tab>
</TabList>
<TabPanels>
<TabPanel>
<UnbanInvestor />
</TabPanel>
<TabPanel>
<BankInvestor />
</TabPanel>
</TabPanels>
</Tabs>
</Box>
);
};
export default Investor;

View File

@@ -0,0 +1,143 @@
import {
Box,
Button,
FormControl,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useEffect, useState } from "react";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
import ToastBox from "../../../../Components/ToastBox";
import { useDepositRejectMutation } from "../../../../Services/drawal.request.service";
import { useUpdateBanMutation } from "../../../../Services/ban.investor.service";
export const conformModalSchema = yup.object().shape({
comments: yup.string().required("Comment is required"),
});
const ReasonBanModal = ({ isOpen, onClose, firstField ,id}) => {
const [isBtnLoading , setIsBtnLoading] = useState(false)
const toast = useToast()
const {
register,
reset,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
const [ updateBanMutation ] = useUpdateBanMutation()
const onSubmit = async(data) => {
setIsBtnLoading(true)
try {
const res = await updateBanMutation({ id ,data})
console.log(res);
if (res?.error) {
toast({
render: () => (
<ToastBox message={res?.error?.data?.message} status={"error"} />
),
});
heandleOnClose()
}else if(res?.data?.statusCode === 200) {
toast({
render: () => (
<ToastBox message={res?.data?.message} />
),
});
heandleOnClose()
}
} catch (error) {
console.log(error);
}
};
const heandleOnClose = () =>{
reset()
onClose()
setIsBtnLoading(false)
}
return (
<Modal isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
<ModalOverlay />
<ModalContent pb={4}>
<ModalHeader fontSize={"md"}>Reason for Unban</ModalHeader>
<ModalCloseButton />
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea
rows={6}
focusBorderColor="green.400"
name="comments"
{...register("comments")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your comments...."}
rounded={"md"}
resize={"none"}
/>
{errors.comments && (
<Text fontSize="xs" color="red">
{errors.comments.message}
</Text>
)}
</FormControl>
</ModalBody>
<ModalFooter>
<Button
colorScheme="gray"
mr={3}
onClick={onClose}
size={"sm"}
rounded={"sm"}
>
Cancel
</Button>
<Button
colorScheme="forestGreen"
variant="solid"
size={"sm"}
rounded={"sm"}
isLoading={isBtnLoading}
type="submit"
fontWeight={400}
>
Save
</Button>
</ModalFooter>
</Box>
</ModalContent>
</Modal>
);
};
export default ReasonBanModal;

View File

@@ -0,0 +1,147 @@
import {
Box,
Button,
FormControl,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useEffect, useState } from "react";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
import ToastBox from "../../../../Components/ToastBox";
import { useDepositRejectMutation } from "../../../../Services/drawal.request.service";
import { useUpdateBanMutation, useUpdateUnbanMutation } from "../../../../Services/ban.investor.service";
export const conformModalSchema = yup.object().shape({
comments: yup.string().required("Comment is required"),
});
const ReasonBanModal = ({ isOpen, onClose, firstField ,id}) => {
const [isBtnLoading , setIsBtnLoading] = useState(false)
const toast = useToast()
const {
register,
reset,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
const [ updateBan ] = useUpdateUnbanMutation()
const onSubmit = async(data) => {
console.log(data);
setIsBtnLoading(true)
try {
const res = await updateBan({ id ,data})
console.log(res);
if (res?.error) {
toast({
render: () => (
<ToastBox message={res?.error?.data?.message} status={"error"} />
),
});
heandleOnClose()
}else if(res?.data) {
toast({
render: () => (
<ToastBox message={res?.data?.message} />
),
});
heandleOnClose()
}
} catch (error) {
console.log(error);
}
};
const heandleOnClose = () =>{
reset()
onClose()
setIsBtnLoading(false)
}
return (
<Modal isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
<ModalOverlay />
<ModalContent pb={4}>
<ModalHeader fontSize={"md"}>Reason for Ban</ModalHeader>
<ModalCloseButton />
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea
rows={6}
focusBorderColor="green.400"
name="comments"
{...register("comments")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your comments...."}
rounded={"md"}
resize={"none"}
/>
{errors.comments && (
<Text fontSize="xs" color="red">
{errors.comments.message}
</Text>
)}
</FormControl>
</ModalBody>
<ModalFooter>
<Button
colorScheme="gray"
mr={3}
onClick={onClose}
size={"sm"}
rounded={"sm"}
>
Cancel
</Button>
<Button
colorScheme="forestGreen"
variant="solid"
size={"sm"}
rounded={"sm"}
isLoading={isBtnLoading}
type="submit"
fontWeight={400}
>
Save
</Button>
</ModalFooter>
</Box>
</ModalContent>
</Modal>
);
};
export default ReasonBanModal;

View File

@@ -0,0 +1,352 @@
import {
Avatar,
Badge,
Box,
HStack,
Input,
Select,
Switch,
Text,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useState, useRef } from "react";
import { Link, Link as RouterLink, useNavigate } from "react-router-dom";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import { debounce } from "../../../Master/Sponser/AddSponser";
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
import DataTable from "../../../../Components/DataTable/NormalTable";
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
import Pagination from "../../../../Components/Pagination";
import ToastBox from "../../../../Components/ToastBox";
import ReasonBanModal from "./ReasonBanModal";
import {
useGetInvestorQuery,
useGetUnbanInvestorQuery,
} from "../../../../Services/ban.investor.service";
import { generateSerialNumber } from "../../../../Constants/Constants";
import { TABLE_PAGINATION } from "../../../../Constants/Paginations";
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
const UnbanInvestor = () => {
const navigate = useNavigate();
const toast = useToast();
const thirdField = useRef();
const { bankInvestor, setBankInvestor, slideFromRight } =
useContext(GlobalStateContext);
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState("");
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
const { isOpen: isOpen, onOpen: onOpen, onClose: onClose } = useDisclosure();
const formatDate = (date) => {
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
};
// =========================== [Use State] =============================
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
const [searchTerm, setSearchTerm] = useState("");
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
const [status, setStatus] = useState("");
const [kyc, setKyc] = useState("");
const [country, setCountry] = useState("");
// Debounce the search term to avoid making a request on every keystroke
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedSearchTerm(searchTerm);
}, 500); // Adjust delay as needed
return () => {
clearTimeout(handler);
};
}, [searchTerm]);
const {
data,
isLoading: unbanLoading,
error,
refetch,
} = useGetUnbanInvestorQuery(
{
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
searchTerm: debouncedSearchTerm,
KYCStatus: kyc,
country_xid: country,
},
{
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
}
);
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
// Cleanup the timer on component unmount
return () => clearTimeout(timer);
}, []);
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
"Sr N/O",
"Date",
"Client ID",
"First Name",
"Last Name",
"Country",
"Phone Number",
"E-mail ID",
"KYC Status",
"Action",
];
const handleUpdateStatus = debounce((id) => {
setBankInvestor((prevData) =>
prevData.map((bankInvestor) =>
bankInvestor.id === id ? { ...bankInvestor } : bankInvestor
)
);
toast({
render: () => <ToastBox message={"Status changed succesfully.!"} />,
});
}, 300);
// ====================================================[Table Filter]================================================================
const filteredData = data?.data?.rows?.filter((item) => {
// Filter by name (case insensitive)
const name = item?.clientReference_id;
const searchLower = searchTerm?.toLowerCase();
const nameMatches = name?.toLowerCase().includes(searchLower);
return nameMatches;
});
const extractedArray = data?.data?.rows?.map((item, index) => ({
id: item?.id,
"Sr N/O": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"gray.600"}
className="d-flex align-items-center fw-bold web-text-small"
>
{generateSerialNumber(index, currentPage, pageSize)}
</Text>
),
Date: (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{formatDate(item?.date)}
</Text>
</Box>
),
"Client ID": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.clientReference_id}
</Text>
</Box>
),
"First Name": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.firstName}
</Text>
</Box>
),
"Last Name": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.lastName}
</Text>
</Box>
),
Country: (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.country}
</Text>
</Box>
),
"Phone Number": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.phoneNumber}
</Text>
</Box>
),
"E-mail ID": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.emailAddress}
</Text>
</Box>
),
"KYC Status": (
<Box w={"auto"} isTruncated={true}>
<Badge
fontWeight={"500"}
textTransform={"none"}
color={item?.KYCStatus === false ? "red" : "blue"}
px={2}
py={0.5}
variant={"ghost"}
>
{item?.KYCStatus === true ? "Completed" : "Incompleted"}
</Badge>
</Box>
),
Action: (
<Box w={"auto"} isTruncated={true}>
<Badge
cursor={"pointer"}
fontWeight={"500"}
textTransform={"none"}
colorScheme={"red"}
px={2}
py={0.5}
onClick={() => {
setActionId(item?.id);
onOpen();
}}
>
Ban Investor
</Badge>
</Box>
),
}));
const handleDelete = () => {
const updatedInvestorDetails = InvestorDetails.filter(
(sponsor) => sponsor.id !== actionId
);
setTimeout(() => {
setInvestorDetails(updatedInvestorDetails);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
const handleEdit = (id) => {
setActionId(id);
onEditOpen();
};
return (
<Box {...OPACITY_ON_LOAD}>
<Box bg="white.500">
<HStack
display={"flex"}
justifyContent={"space-between"}
ps={1}
pe={1}
pb={4}
pt={4}
spacing="24px"
>
<Input
mt={1}
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<HStack display={"flex"} alignItems={"center"}>
{/* <Select
focusBorderColor="green.500"
size={"sm"}
fontSize={"xs"}
cursor={"pointer"}
onChange={(e) => setStatus(e.target.value)}
value={status}
>
<option value="" defaultValue={""} disabled hidden>
Status
</option>
<option value="">Status</option>
<option value="0">Ban</option>
<option value="1">UnBan</option>
</Select> */}
<Select
focusBorderColor="green.500"
size={"sm"}
fontSize={"xs"}
cursor={"pointer"}
onChange={(e) => setKyc(e.target.value)}
value={kyc}
>
<option value="" defaultValue={""} disabled hidden>
KYC Status
</option>
<option value="">KYC Status</option>
<option value="0">Incompleted</option>
<option value="1">Completed</option>
</Select>
<Select
focusBorderColor="green.500"
size={"sm"}
fontSize={"xs"}
cursor={"pointer"}
onChange={(e) => setCountry(e.target.value)}
value={country}
>
<option value="" defaultValue={""} disabled hidden>
Country
</option>
<option value="">All</option>
<option value="1">Behrain</option>
<option value="2">Kuwait</option>
<option value="3">Oman</option>
<option value="4">Qatar</option>
<option value="5">Saudi arabia</option>
<option value="6">United arab emirates</option>
</Select>
</HStack>
</HStack>
</Box>
<DataTable
emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
setMouseEnteredId={setMouseEnteredId}
setMouseEntered={setMouseEntered}
/>
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>
<ReasonBanModal isOpen={isOpen} onClose={onClose} id={actionId} />
</Box>
);
};
export default UnbanInvestor;

View File

@@ -1,22 +1,342 @@
import { Box, Image, Text } from "@chakra-ui/react" import React, { useContext, useEffect, useState } from "react";
// import error from "../assets/Error.svg" import {
import robot from "../../assets/robot.png" Badge,
// import robot from "../assets/robot.png" Box,
const Notification = () => { Button,
return ( Text,
Tooltip,
<Box useToast,
h={'100vh'} } from "@chakra-ui/react";
display={'flex'} import { useForm} from "react-hook-form";
justifyContent={'center'} import { yupResolver } from "@hookform/resolvers/yup";
alignItems={'center'} import * as yup from "yup";
flexDirection={'column'} import { useNavigate } from "react-router-dom";
gap={8} import { OPACITY_ON_LOAD } from "../../Layout/animations";
> import FormInputMain from "../../Components/FormInputMain";
<Image src={robot} w={"171px"} /> import {
{/* <Text color={'green.800'} as={'span'} fontSize={'small'}>The requested URL was not found on this server.</Text> */} useGetContactQuery,
</Box> useSendNotificationMutation,
) useUpdateContactMutation,
} } from "../../Services/contact.service";
import FullscreenLoaders from "../../Components/Loaders/FullscreenLoaders";
import ToastBox from "../../Components/ToastBox";
import NormalTable from "../../Components/DataTable/NormalTable";
import GlobalStateContext from "../../Contexts/GlobalStateContext";
import { useGetInvestorsQuery } from "../../Services/investor.details.service";
import { TABLE_PAGINATION } from "../../Constants/Paginations";
import { formatDate, generateSerialNumber } from "../../Constants/Constants";
import { ViewIcon } from "@chakra-ui/icons";
import { useGetUnbanInvestorQuery } from "../../Services/ban.investor.service";
export default Notification export const notification = yup.object().shape({
title: yup
.string()
.required("Investment Name is required"),
ManualDate: yup
.date()
.required("Manual Date is required")
.typeError("Invalid date format"),
ManualTime: yup
.string()
.required("Manual Time is required")
.matches(
/^([01]\d|2[0-3]):?([0-5]\d)$/,
"Invalid time format, must be in HH:mm"
),
expectedReturn: yup
.string()
.required("Expected Return is required"),
});
export const notificationNew = yup.object().shape({
title: yup
.string()
.required("Investment Name is required"),
message: yup
.string()
.required("Message is required"),
});
const Notification = () => {
const toast = useToast();
const navigate = useNavigate();
const [form, setForm] = useState({});
const [isLoading, setIsLoading] = useState(false);
const [ selectedRadio, setSelectedRadio] = useState([])
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
const [searchTerm, setSearchTerm] = useState("");
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
const {
control,
reset,
watch,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(notificationNew),
defaultValues: {
title: '',
message: '',
},
});
console.log(errors);
const {
data: contact,
isLoading: contactLoading,
error,
} = useGetContactQuery();
const formatDate = (date) => {
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
};
// const {
// data: investorDetails,
// isLoading: investorDetailsLoading,
// // error,
// } = useGetInvestorsQuery({ page: currentPage, size: pageSize });
const {
data : investorDetails,
isLoading: investorDetailsLoading,
refetch,
} = useGetUnbanInvestorQuery({
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
search: debouncedSearchTerm,
},
{
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
});;
console.log(investorDetails);
const [sendNotification] = useSendNotificationMutation();
if (contactLoading) {
return <FullscreenLoaders />;
}
const formFields = [
{
label: "Notification Header",
placeHolder: " ",
name: "title",
type: "text",
width:"100%",
maxLength:100,
helperText:`Maximum length should be 100 characters. You have entered ${watch()?.title?.length || 0} characters.`,
isRequired: true,
section: "Send Custom Push Notification",
// value: contact?.phoneNumber || "",
},
{
label: "Notification Message",
placeHolder: " ",
name: "message",
width:"100%",
type: "textarea",
isRequired: true,
maxLength:200,
helperText:`Maximum length should be 200 characters. You have entered ${watch()?.message?.length || 0} characters.`,
section: "Send Custom Push Notification",
// value: contact?.phoneNumber || "",
},
];
const groupedFields = formFields.reduce((groups, field) => {
const { section } = field;
if (!groups[section]) {
groups[section] = [];
}
groups[section].push(field);
return groups;
}, {});
const onSubmit = async (data) => {
const dataToPass = {
...data,
principal_xid:selectedRadio
}
setIsLoading(true);
try {
const res = await sendNotification(dataToPass);
console.log(res);
if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsLoading(false)
}else if(res?.data){
toast({
render: () => (
<ToastBox message={res?.data?.message} />
),
});
setIsLoading(false)
setSelectedRadio([])
reset({
title: '', // Resetting specific fields
message: '',
}); // Clears the form fields
}else{
toast({
render: () => (
<ToastBox status={'error'} message={"Something went wrong"} />
),
});
setIsLoading(false)
}
} catch (error) {
console.log(error);
setIsLoading(false);
}
};
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
"Sr N/O",
"Date",
"Client ID",
"First Name",
"Last Name",
"Country",
"Phone Number",
"E-mail ID",
"KYC Status",
];
const extractedArray = investorDetails?.data?.rows?.map((item, idx) => ({
id: item?.principal_xid,
"Sr N/O": (
<Text
justifyContent={"left"}
as={"span"}
color={"gray.600"}
className="d-flex align-items-center fw-bold web-text-small"
>
{generateSerialNumber(idx,currentPage, pageSize )}
</Text>
),
Date: (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{formatDate(item?.date)}
</Text>
</Box>
),
"Client ID": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.clientReference_id}
</Text>
</Box>
),
"First Name": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.firstName}
</Text>
</Box>
),
"Last Name": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.lastName}
</Text>
</Box>
),
Country: (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.country}
</Text>
</Box>
),
"Phone Number": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.phoneNumber}
</Text>
</Box>
),
"E-mail ID": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.emailAddress}
</Text>
</Box>
),
"KYC Status": (
<Box w={"auto"} isTruncated={true}>
<Badge
fontWeight={"500"}
textTransform={"none"}
color={item?.KYCStatus === false ? "red" : "blue"}
px={2}
py={0.5}
variant={'ghost'}
>
{item?.KYCStatus === true ? "Completed" : "Incompleted"}
</Badge>
</Box>
),
}));
return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={14}>
<FormInputMain
groupedFields={groupedFields}
control={control}
errors={errors}
onSubmit={handleSubmit(onSubmit)}
btnLoading={isLoading}
>
<Box overflow={'scroll'} h={'58vh'}>
<NormalTable
centered={true}
emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow}
data={extractedArray}
// isLoading={isLoading}
setSelectedRadio={setSelectedRadio}
selectedRadio={selectedRadio}
showRadioButton={true}
/>
</Box>
</FormInputMain>
</Box>
);
};
export default Notification;

View File

@@ -1,22 +1,241 @@
import { Box, Image, Text } from "@chakra-ui/react" import {
// import error from "../assets/Error.svg" Box,
import robot from "../../assets/robot.png" Button,
// import robot from "../assets/robot.png" HStack,
const Users = () => { Input,
return ( Text,
Tooltip,
<Box useToast,
h={'100vh'} } from "@chakra-ui/react";
display={'flex'} import React, { useContext, useEffect, useState, useRef } from "react";
justifyContent={'center'} import { Link, Link as RouterLink, useNavigate } from "react-router-dom";
alignItems={'center'} import {EditIcon,} from "@chakra-ui/icons";
flexDirection={'column'} import { OPACITY_ON_LOAD } from "../../Layout/animations";
gap={8} import DataTable from "../../Components/DataTable/NormalTable";
> import GlobalStateContext from "../../Contexts/GlobalStateContext";
<Image src={robot} w={"171px"} /> import CustomAlertDialog from "../../Components/CustomAlertDialog";
{/* <Text color={'green.800'} as={'span'} fontSize={'small'}>The requested URL was not found on this server.</Text> */} import ToastBox from "../../Components/ToastBox";
</Box> import { debounce } from "./Contact";
)
}
export default Users const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
const Users = () => {
const navigate = useNavigate();
const toast = useToast();
const thirdField = useRef();
const { users, setUsers, slideFromRight } =
useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
// const {
// isOpen: isEditOpen,
// onOpen: onEditOpen,
// onClose: onEditClose,
// } = useDisclosure();
const btnRef = React.useRef();
// const {
// data: bankDetails,
// isLoading: bankDetailsLoading,
// error,
// } = useGetBankQuery({ page: 1, size: 10 });
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
// Cleanup the timer on component unmount
return () => clearTimeout(timer);
}, []);
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
"Sr N/O",
"First Name",
"Last Name",
"E-mail ID",
"Role",
"Phone Number",
"Action",
];
const handleUpdateStatus = debounce((id) => {
setUsers((prevData) =>
prevData.map((users) =>
users.id === id ? { ...users } : users
)
);
toast({
render: () => <ToastBox message={"Status changed succesfully.!"} />,
});
}, 300);
// ====================================================[Table Filter]================================================================
const filteredData = users?.data?.filter((item) => {
// Filter by name (case insensitive)
const name = item.emailID;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name?.toLowerCase().includes(searchLower);
// Filter by status
// const status = item.status;
// const statusLower = status ? "active" : "inactive";
// const statusMatches =
// statusFilter === "all" ||
// (statusFilter === "active" && status === true) ||
// (statusFilter === "inactive" && status === false);
return nameMatches;
});
const extractedArray = filteredData?.map((item) => ({
id: item?.id,
"Sr N/O": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"gray.600"}
className="d-flex align-items-center fw-bold web-text-small"
>
{item.id}
</Text>
),
"First Name": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.firstName}
</Text>
</Box>
),
"Last Name": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.lastName}
</Text>
</Box>
),
"E-mail ID": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.emailID}
</Text>
</Box>
),
"Role": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.role}
</Text>
</Box>
),
"Phone Number": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.phoneNumber}
</Text>
</Box>
),
Action: (
<Box display={"flex"} justifyContent={"space-between"} gap={2}>
<Tooltip
rounded={"sm"}
fontSize={"xs"}
label="View"
bg="#fff"
color={"green.500"}
placement="top"
>
<Button
onClick={() => {
navigate(`/bank-details/edit-bank-details/${item.id}`);
}}
_hover={{ color: "green.500" }}
// transition={"0.5s all"}
color="green.300"
rounded={"sm"}
size={"xs"}
>
<EditIcon />
</Button>
</Tooltip>
</Box>
),
}));
const handleDelete = () => {
const updatedInvestorDetails = InvestorDetails.filter(
(sponsor) => sponsor.id !== actionId
);
setTimeout(() => {
setInvestorDetails(updatedInvestorDetails);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
const handleEdit = (id) => {
setActionId(id);
onEditOpen();
};
return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
<Box bg="white.500">
<HStack
display={"flex"}
justifyContent={"space-between"}
ps={1}
pe={1}
pb={4}
pt={4}
spacing="24px"
>
<Input
mt={1}
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</HStack>
</Box>
<DataTable
emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
setMouseEnteredId={setMouseEnteredId}
setMouseEntered={setMouseEntered}
/>
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>
</Box>
);
};
export default Users;

162
src/Pages/Dashbaord.jsx Normal file
View File

@@ -0,0 +1,162 @@
import { Box, HStack, Icon, position, Text, VStack } from '@chakra-ui/react'
import React from 'react'
import { HiOutlineChartSquareBar } from 'react-icons/hi'
import { RiMoneyDollarBoxLine } from 'react-icons/ri'
import { TbTransactionDollar } from 'react-icons/tb'
import { VscSymbolClass } from 'react-icons/vsc'
import { TABLE_PAGINATION } from '../Constants/Paginations'
import FullscreenLoaders from '../Components/Loaders/FullscreenLoaders'
import { useGetIOprepopulateDataQuery, useGetIOsQuery } from '../Services/io.service'
import { useGetInvestorsQuery } from '../Services/investor.details.service'
import DonutChart from '../Components/Doughnut/DonutChart'
import { GoDotFill } from "react-icons/go";
import { useNavigate } from 'react-router-dom'
import LineChart from '../Components/Doughnut/LineChart'
import { PiChartLineUpDuotone } from 'react-icons/pi'
import ApexChart from '../Components/Doughnut/ApexDonut'
import ApexLine from '../Components/Doughnut/ApexLine'
import ReactApexChart from 'react-apexcharts'
import { BsGraphUpArrow } from "react-icons/bs";
const Dashbaord = () => {
const navigate = useNavigate()
const { data, isLoading: isIoPreLoading } = useGetIOprepopulateDataQuery();
const { data: IO, isLoading: isIoLoading } = useGetIOsQuery({ page: TABLE_PAGINATION?.page, size: TABLE_PAGINATION?.size });
const { data: investorDetails, isInvestorLoading } = useGetInvestorsQuery({ page: TABLE_PAGINATION?.page, size: TABLE_PAGINATION?.size });
const sortArrayByStatus = () => {
const sortedArrays = {
open: [],
closed: [],
processing: [],
draft: []
};
IO?.data?.rows.forEach(item => {
const status = item.ioStatus?.statusAdmin;
if (status === 'Open') {
sortedArrays.open.push(item);
} else if (status === 'Closed') {
sortedArrays.closed.push(item);
} else if (status === 'Processing') {
sortedArrays.processing.push(item);
} else if (status === 'Draft') {
sortedArrays.draft.push(item);
}
});
return sortedArrays;
};
const statusData = sortArrayByStatus()
const chartData = {
labels: ['Draft', 'Open', 'Processing', 'Closed',],
backgroundColor: ['#3182ce', '#004118', '#D69E2E', '#E53E3E'],
values: [statusData?.draft?.length, statusData?.open?.length, statusData?.processing?.length, statusData?.closed?.length]
};
const series1= [{
data: [25, 66, 41, 89, 63, 25, 44, 12, 36, 9, 54]
}]
const options1= {
chart: {
type: 'line',
position:"absolute",
right:0,
width: 100,
height: 35,
sparkline: {
enabled: true
}
},
tooltip: {
fixed: {
enabled: false
},
x: {
show: false
},
y: {
title: {
formatter: function (seriesName) {
return ''
}
}
},
marker: {
show: false
}
}
}
return (
isIoPreLoading || isIoLoading || isInvestorLoading ? <FullscreenLoaders /> :
<Box height={'100vh'} bg={'#fff'} roundedTop={0} pt={5} overflowX={"hidden"}>
<Box display={'flex'} gap={6} w={'100%'} pt={3} pb={3} p={3} >
<Box position={'relative'} cursor={'pointer'} onClick={() => navigate("/investor-details")} boxShadow={'lg'} color={"#004118"} p={4} rounded={'xl'} w={'25%'} display={'flex'} bg={'#f5f8f6'} flexDirection={'column'} alignItems={'start'} >
<Icon left={"10px"} bg={'#004118'} rounded={9} p={2} color={"#fff"} as={TbTransactionDollar} mb={6} boxSize={12} />
<Text as={'span'} fontSize={'xs'} fontWeight={500}>Total Investors</Text>
<Text as={'span'} fontSize={'32px'} fontWeight={600}>{investorDetails?.data?.totalItems}</Text>
<Icon position={'absolute'} right={6} bottom={6} boxSize={8} as={BsGraphUpArrow} />
{/* <ReactApexChart position={'absolute'} right={6} bottom={6} options={options1} series={series1} type="line" height={35} width={100} /> */}
</Box>
<Box position={'relative'} cursor={'pointer'} onClick={() => navigate("/view-io")} boxShadow={'lg'} bg={'#f5f8f6'} color={"#004118"} p={3} rounded={'xl'} w={'25%'} display={'flex'} flexDirection={'column'} alignItems={'start'} >
<Icon bg={'#004118'} rounded={9} p={2} color={"#fff"} as={HiOutlineChartSquareBar} mb={6} boxSize={12} />
<Text as={'span'} fontSize={'xs'} fontWeight={500}>Total IO</Text>
<Text as={'span'} fontSize={'32px'} fontWeight={600}>{IO?.data?.totalItems}</Text>
<Icon position={'absolute'} right={6} bottom={6} boxSize={8} as={BsGraphUpArrow} />
</Box>
<Box position={'relative'} cursor={'pointer'} onClick={() => navigate("/sponser")} boxShadow={'lg'} bg={'#f5f8f6'} color={"#004118"} p={3} rounded={'xl'} w={'25%'} display={'flex'} flexDirection={'column'} alignItems={'start'} >
<Icon bg={'#004118'} rounded={9} p={2} color={"#fff"} as={RiMoneyDollarBoxLine} mb={6} boxSize={12} />
<Text as={'span'} fontSize={'xs'} fontWeight={500}>Total sponors</Text>
<Text as={'span'} fontSize={'32px'} fontWeight={600}>{data?.data?.sponsor?.length}</Text>
<Icon position={'absolute'} right={6} bottom={6} boxSize={8} as={BsGraphUpArrow} />
</Box>
<Box position={'relative'} cursor={'pointer'} onClick={() => navigate("/investment-type")} boxShadow={'lg'} bg={'#f5f8f6'} color={"#004118"} p={3} rounded={'xl'} w={'25%'} display={'flex'} flexDirection={'column'} alignItems={'start'} >
<Icon bg={'#004118'} rounded={9} p={2} color={"#fff"} as={VscSymbolClass} mb={6} boxSize={12} />
<Text as={'span'} fontSize={'xs'} fontWeight={500}>Total Investment Type</Text>
<Text as={'span'} fontSize={'32px'} fontWeight={600}>{data?.data?.investmentType?.length}</Text>
<Icon position={'absolute'} right={6} bottom={6} boxSize={8} as={BsGraphUpArrow} />
</Box>
</Box>
<Box h={'70%'} w={"100%"} display={'flex'} pe={4} mt={2}>
<Box w={'60%'} h={'100%'} p={4} pe={6} pt={1} >
<Box position={'relative'} h={'100%'} boxShadow={'lg'} display={'flex'} justifyContent={'center'} rounded={'xl'} p={5} ps={0} pe={0}>
{/* <Text position={'absolute'} top={0} left={6} as={'span'} fontSize={'sm'}>Exchange rate currency</Text> */}
{/* <LineChart /> */}
<ApexLine/>
</Box>
</Box>
<Box boxShadow={'lg'} position={"relative"} bg={'#fff'} rounded={'xl'} w={'40%'} display={'flex'} justifyContent={'space-between'} flexDirection={'column'} h={"95%"} mt={1} p={4}>
<Text as={'span'} fontSize={'sm'}>IO Status</Text>
<Box display={'flex'} w={'100%'} h={'100%'} alignItems={'center'} justifyContent={'space-around'} >
{/* <Box display={'flex'} w={'70%'} alignItems={'center'} h={325} p={6}> */}
{/* <DonutChart data={chartData} /> */}
<ApexChart data={chartData} />
{/* </Box> */}
<VStack alignItems={'start'} justifyContent={'center'} flexWrap={'wrap'}>
{chartData?.labels?.map((item, index) => <Text key={index} as={'span'} display={'flex'} gap={0.5} alignItems={'center'} fontSize={'sm'} fontWeight={600}><GoDotFill color={chartData?.backgroundColor[index]} fontSize={30} />{item}</Text>)}
</VStack>
</Box>
</Box>
</Box>
</Box>
)
}
export default Dashbaord

View File

@@ -31,18 +31,25 @@ import ToastBox from "../../../Components/ToastBox";
import DataTable from "../../../Components/DataTable/DataTable"; import DataTable from "../../../Components/DataTable/DataTable";
import DepositRequestApprove from "./DepositRequestApprove"; import DepositRequestApprove from "./DepositRequestApprove";
import DepositRequestReject from "./DepositRequestReject"; import DepositRequestReject from "./DepositRequestReject";
import NormalTable from "../../../Components/DataTable/NormalTable";
import { useGetDepositRequestQuery } from "../../../Services/deposit.request.service";
import { current } from "@reduxjs/toolkit";
import { TABLE_PAGINATION } from "../../../Constants/Paginations";
import {
generateSerialNumber,
removeTrailingZeros,
} from "../../../Constants/Constants";
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter export const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
const DepositRequest = () => { const DepositRequest = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const toast = useToast(); const toast = useToast();
const { depositRequest, setDepositRequest, slideFromRight } = const { depositRequest, setDepositRequest, slideFromRight } =
useContext(GlobalStateContext); useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false); const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false); const [actionId, setActionId] = useState("");
const [mouseEntered, setMouseEntered] = useState(false); const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState(""); const [mouseEnteredId, setMouseEnteredId] = useState("");
const { const {
@@ -55,31 +62,63 @@ const DepositRequest = () => {
onOpen: onRejectOpen, onOpen: onRejectOpen,
onClose: onRejectClose, onClose: onRejectClose,
} = useDisclosure(); } = useDisclosure();
// =========================== [Use State] =============================
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
const [searchTerm, setSearchTerm] = useState("");
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
// Debounce the search term to avoid making a request on every keystroke
useEffect(() => { useEffect(() => {
// Simulate loading const handler = setTimeout(() => {
const timer = setTimeout(() => { setDebouncedSearchTerm(searchTerm);
setIsLoading(false); }, 500); // Adjust delay as needed
}, 1500); return () => {
clearTimeout(handler);
};
}, [searchTerm]);
// Cleanup the timer on component unmount
return () => clearTimeout(timer); const formatDate = (date) => {
}, []); return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
};
const {
data,
isLoading: depositRequestLoading,
error,
refetch,
} = useGetDepositRequestQuery({
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
search: debouncedSearchTerm,
},
{
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
});
// Use useEffect to refetch data when the component mounts
useEffect(() => {
refetch();
}, [refetch]);
// ====================================================[Table Setup]================================================================ // ====================================================[Table Setup]================================================================
const tableHeadRow = [ const tableHeadRow = [
"Sr.no", "Sr.no",
"Date",
"Client ID", "Client ID",
"First Name", "First Name",
"Last Name", "Last Name",
"Country", "Country",
"Phone Number", "Phone Number",
"Currency",
"Deposit Amount", "Deposit Amount",
"Fees", "Deposit Date",
"Total Amount",
"Amount in Investor currency",
"Action", "Action",
]; ];
@@ -96,179 +135,172 @@ const DepositRequest = () => {
}); });
}, 300); }, 300);
// ====================================================[Table Filter]================================================================ const filteredData = data?.data?.rows
const filteredData = depositRequest.filter((item) => { .filter((item) => {
// Filter by name (case insensitive) // Filter by name (case insensitive)
const name = item.clientId; const name = [item.firstName, item.lastName, item.countryName]
const searchLower = searchTerm.toLowerCase(); .filter(Boolean)
const nameMatches = name.toLowerCase().includes(searchLower); .join(" ");
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
// Filter by status // Filter by status (Uncomment and use if needed)
// const status = item.status; // const status = item.status;
// const statusLower = status ? "active" : "inactive"; // const statusLower = status ? "active" : "inactive";
// const statusMatches = // const statusMatches =
// statusFilter === "all" || // statusFilter === "all" ||
// (statusFilter === "active" && status === true) || // (statusFilter === "active" && status === true) ||
// (statusFilter === "inactive" && status === false); // (statusFilter === "inactive" && status === false);
return nameMatches; return nameMatches;
}); })
.sort((b, a) => new Date(a.createdAt) - new Date(b.createdAt));
const [extractedArray, setExtractedArray] = useState( const extractedArray = data?.data?.rows?.map((item, idx) => ({
filteredData?.map((item, index) => ({ // id: item?.id,
// id: item?.id, "Sr.no": (
"Sr.no": ( <Text
<Text w={"20px"}
w={"30px"} justifyContent={slideFromRight ? "right" : "left"}
justifyContent={slideFromRight ? "right" : "left"} as={"span"}
as={"span"} color={"teal.900"}
color={"teal.900"} fontWeight={"500"}
fontWeight={"500"} className="d-flex align-items-center web-text-small"
className="d-flex align-items-center web-text-small" >
> {generateSerialNumber(idx, currentPage, pageSize)}
{index + 1} </Text>
),
"Client ID": (
<Text
w={"60px"}
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item?.clientReference_id}
</Text>
),
"First Name": (
<Box isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.firstName}
</Text> </Text>
), </Box>
Date: ( ),
<Text "Last Name": (
w={"60px"} <Box w={"70px"} isTruncated={true}>
justifyContent={slideFromRight ? "right" : "left"} <Text as={"span"} color={"teal.900"}>
as={"span"} {item?.lastName}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.date}
</Text> </Text>
), </Box>
"Client ID": ( ),
<Text Country: (
w={"60px"} <Box w={"100px"} isTruncated={true}>
justifyContent={slideFromRight ? "right" : "left"} <Text as={"span"} color={"teal.900"}>
as={"span"} {item?.countryName}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.clientId}
</Text> </Text>
), </Box>
"First Name": ( ),
<Box isTruncated={true} w={"50px"}> "Phone Number": (
<Text as={"span"} color={"teal.900"} fontWeight={"500"}> <Box w={"80px"} isTruncated={true}>
{item.firstName} <Text as={"span"} color={"teal.900"}>
</Text> {item?.mobileNumber}
</Box> </Text>
), </Box>
"Last Name": ( ),
<Box w={"50px"} isTruncated={true}> "Deposit Amount": (
<Text as={"span"} color={"teal.900"}> <Box
{item.lastName} display={"flex"}
</Text> justifyContent={"end"}
</Box> w={"130px"}
), isTruncated={true}
Country: ( >
<Box w={"70px"} isTruncated={true}> <Text as={"span"} color={"teal.900"}>
<Text as={"span"} color={"teal.900"}> {/* {formatCurrency(removeTrailingZeros(item?.investorAmount))} */}
{item.country} {parseFloat(item?.investorAmount || 0).toLocaleString(undefined, {
</Text> minimumFractionDigits: 2,
</Box> maximumFractionDigits: 2,
), })}
"Phone Number": ( <Badge ms={1} colorScheme="green">
<Box w={"70px"} isTruncated={true}> {item?.currencyCode}
<Text as={"span"} color={"teal.900"}> </Badge>
{item.phoneNumber} </Text>
</Text> </Box>
</Box> ),
), "Deposit Date": (
Currency: ( <Text
<Box w={"50px"} isTruncated={true}> w={"60px"}
<Text as={"span"} color={"teal.900"}> justifyContent={slideFromRight ? "right" : "left"}
<Badge px={2} py={1}> as={"span"}
{item.currency} color={"teal.900"}
</Badge> fontWeight={"500"}
</Text> className="d-flex align-items-center web-text-small"
</Box> >
), {formatDate(item?.createdAt)}
"Deposit Amount": ( </Text>
<Box w={"50px"} isTruncated={true}> ),
<Text as={"span"} color={"teal.900"}> Action: (
{item.depositAmount} <Box display={"flex"} justifyContent={"center"} gap={2}>
</Text> <Tooltip
</Box> rounded={"sm"}
), fontSize={"xs"}
Fees: ( label="Approve"
<Box w={"auto"} isTruncated={true}> bg="#fff"
<Text as={"span"} color={"teal.900"}> color={"green.500"}
{item.fees} placement="left-start"
</Text> >
</Box> <Button
), // colorScheme="forestGreen"
"Total Amount": ( // color="green.500"
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.totalAmount}
</Text>
</Box>
),
"Amount in Investor currency": (
<Box w={"70px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.amountcurrency}
</Text>
</Box>
),
Action: (
<Box display={"flex"} justifyContent={"space-around"} gap={2}>
<Tooltip
rounded={"sm"} rounded={"sm"}
fontSize={"xs"} size={"xs"}
label="Approve" textTransform={"inherit"}
bg="#fff" fontWeight={500}
color={"green.500"} px={2}
placement="left-start" py={1}
onClick={() => {
setActionId(item.id);
onConfirmOpen();
}}
colorScheme="green"
variant={"solid"}
cursor={"pointer"}
> >
<Badge <CheckIcon fontSize={"12px"} />
colorScheme="forestGreen" </Button>
color="green.500" </Tooltip>
rounded={"sm"} <Tooltip
size={"xs"} rounded={"sm"}
textTransform={"inherit"} fontSize={"xs"}
fontWeight={500} label="Reject"
px={2} bg="#fff"
py={1} color={"red.500"}
onClick={onConfirmOpen} placement="left-start"
> >
<CheckIcon fontSize={"12px"} /> <Button
</Badge> colorScheme="red"
</Tooltip> // color="red.500"
<Tooltip
rounded={"sm"} rounded={"sm"}
fontSize={"xs"} size={"xs"}
label="Reject" textTransform={"inherit"}
bg="#fff" fontWeight={500}
color={"red.500"} px={2}
placement="left-start" onClick={() => {
setActionId(item.id);
onRejectOpen();
}}
py={1}
// variant={"solid"}
> >
<Badge <CloseIcon fontSize={"10px"} />
colorScheme="red" </Button>
color="red.500" </Tooltip>
rounded={"sm"} </Box>
size={"xs"} ),
textTransform={"inherit"} }));
fontWeight={500}
px={2}
onClick={onRejectOpen}
py={1}
>
<CloseIcon fontSize={"10px"} />
</Badge>
</Tooltip>
</Box>
),
}))
);
const handleDelete = () => { const handleDelete = () => {
const IOtype = investmentType.filter( const IOtype = investmentType.filter(
@@ -307,17 +339,23 @@ const DepositRequest = () => {
/> />
<HStack display={"flex"} alignItems={"center"}> <HStack display={"flex"} alignItems={"center"}>
<Pagination totalItems={10} /> <Pagination
isLoading={depositRequestLoading}
pageSize={pageSize}
setPageSize={setPageSize}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
totalItems={data?.data?.totalItems}
/>
</HStack> </HStack>
</HStack> </HStack>
</Box> </Box>
<DataTable <NormalTable
emptyMessage={`We don't have any Investment type `} emptyMessage={`We don't have any Investment type `}
tableHeadRow={tableHeadRow} tableHeadRow={tableHeadRow}
setData={setExtractedArray}
data={extractedArray} data={extractedArray}
isLoading={isLoading} isLoading={depositRequestLoading}
viewActionId={actionId} viewActionId={actionId}
setViewActionId={setActionId} setViewActionId={setActionId}
// totalPages={10} // totalPages={10}
@@ -334,11 +372,17 @@ const DepositRequest = () => {
isLoading={isLoading} isLoading={isLoading}
/> />
<DepositRequestApprove <DepositRequestApprove
data={data?.data?.rows}
isOpen={isConfirmOpen} isOpen={isConfirmOpen}
onClose={onConfirmClose} onClose={onConfirmClose}
id={actionId}
// firstField={firstField} // firstField={firstField}
/> />
<DepositRequestReject isOpen={isRejectOpen} onClose={onRejectClose} /> <DepositRequestReject
isOpen={isRejectOpen}
onClose={onRejectClose}
id={actionId}
/>
</Box> </Box>
); );
}; };

View File

@@ -1,128 +1,248 @@
import { import {
Box, Badge,
Button, Box,
FormControl, Button,
FormLabel, FormControl,
Input, FormErrorMessage,
Modal, FormHelperText,
ModalBody, FormLabel,
ModalCloseButton, Input,
ModalContent, Modal,
ModalFooter, ModalBody,
ModalHeader, ModalCloseButton,
ModalOverlay, ModalContent,
Text, ModalFooter,
useDisclosure, ModalHeader,
} from "@chakra-ui/react"; ModalOverlay,
import React from "react"; Text,
import * as yup from "yup"; Textarea,
import { yupResolver } from "@hookform/resolvers/yup"; useDisclosure,
import { useForm } from "react-hook-form"; useToast,
} from "@chakra-ui/react";
export const conformModalSchema = yup.object().shape({ import React, { useEffect, useState } from "react";
fees: yup.string().required("File name is required"), import * as yup from "yup";
totalAmount: yup.string().required("File name is required"), import { yupResolver } from "@hookform/resolvers/yup";
import { Controller, useForm } from "react-hook-form";
import {
useGetDepositRequestByIdQuery,
useGetDepositRequestQuery,
useUpdateDepositRequestMutation,
} from "../../../Services/deposit.request.service";
import FullscreenLoaders from "../../../Components/Loaders/FullscreenLoaders";
import ToastBox from "../../../Components/ToastBox";
import CurrencyInput, { formatCurrency } from "../../../Components/CurrencyInput";
const FILE_TYPES = ["image/jpeg", "image/png", "image/gif"];
export const conformModalSchema = yup.object().shape({
investorAmount: yup.string().required("Investor amount is required"),
comment: yup.string().notRequired() .max(200, "Approve Comment cannot be more than 200 characters"),
supporting_FileName: yup.mixed().required("File is required"),
// .test("fileType", "Unsupported File Format", (value) => {
// return value && FILE_TYPES.includes(value.type);
// }),
});
const DepositRequestApprove = ({
isOpen,
onClose,
firstField,
id,
data: requestData,
}) => {
const toast = useToast();
const [file, setFile] = useState();
const [isBtnLoading, setIsBtnLoading] = useState(false);
const [updateDepositRequest] = useUpdateDepositRequestMutation();
const { data, isLoading } = useGetDepositRequestByIdQuery(id, {
skip: !id,
}); });
const DepositRequestApprove = ({ isOpen, onClose, firstField }) => { console.log(data?.data?.investorAmount);
const {
register, const {
handleSubmit, control,
formState: { errors }, register,
} = useForm({ reset,
resolver: yupResolver(conformModalSchema), watch,
}); handleSubmit,
formState: { errors },
const onSubmit = (data) => { } = useForm({
setFile(data.document[0]); resolver: yupResolver(conformModalSchema),
});
const newDocument = {
...data, useEffect(() => {
document: data.document[0].name, // Store the document name if (data) {
status: true, const investorAmount = parseFloat(data?.data?.investorAmount);
id: uuidv4(), reset({
createdAt: new Date().toISOString(), investorAmount: investorAmount,
Type: getFileIcon(file.type), accountName: data?.data?.accountName,
}; });
}
setCreate((prevCreate) => [...prevCreate, newDocument]); }, [id, data, reset]);
onClose();
}; const onSubmit = async (data) => {
setIsBtnLoading(true);
const handleFileChange = (event) => { const formData = new FormData();
const selectedFile = event.target.files[0];
setFile(selectedFile); formData.append("investorAmount", data.investorAmount);
}; formData.append("comment", data.comment);
const file = data.supporting_FileName["0"];
return ( formData.append("supporting_FileName", file);
<Modal isOpen={isOpen} onClose={onClose} initialFocusRef={firstField}>
<ModalOverlay /> try {
<ModalContent pb={4}> const res = await updateDepositRequest({ id, data: formData });
<ModalHeader fontSize={"md"}>Confirm</ModalHeader>
<ModalCloseButton /> if (res?.error) {
toast({
render: () => (
<ToastBox message={res?.error?.data?.message} status={"error"} />
),
});
setIsBtnLoading(false);
} else if (res?.data?.statusCode === 200) {
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
setIsBtnLoading(false);
}
} catch (error) {}
heandleOnClose();
};
const heandleOnClose = () => {
reset();
onClose();
};
return (
<Modal
isOpen={isOpen}
onClose={heandleOnClose}
initialFocusRef={firstField}
>
<ModalOverlay />
<ModalContent pb={4}>
<ModalHeader fontSize={"md"}>Confirm</ModalHeader>
<ModalCloseButton />
{isLoading ? (
<FullscreenLoaders height={"50vh"} />
) : (
<Box as="form" onSubmit={handleSubmit(onSubmit)}> <Box as="form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody> <ModalBody>
<FormControl mb={4}> {/* <FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Deposit Amount</FormLabel> <FormLabel fontSize="sm">
Deposit Amount
<Badge colorScheme="green">{data?.data?.currencyCode}</Badge>
</FormLabel>
<Input <Input
focusBorderColor='green.400' focusBorderColor="green.400"
name="fileName" name="investorAmount"
{...register("fileName")} {...register("investorAmount")}
value={formatCurrency(watch("investorAmount"))}
fontSize="sm" fontSize="sm"
type="text" type="number"
size="sm" size="sm"
placeholder={"$100,000"} placeholder={"100,000"}
readOnly textAlign={"right"}
// readOnly
/> />
</FormControl> {errors.investorAmount && (
<FormControl mb={4}>
<FormLabel fontSize="sm">Fees</FormLabel>
<Input
focusBorderColor='green.400'
name="fileName"
{...register("fileName")}
fontSize="sm"
type="text"
size="sm"
placeholder={"$100,000"}
/>
{errors.fees && (
<Text fontSize="xs" color="red"> <Text fontSize="xs" color="red">
{errors.fees.message} {errors.investorAmount.message}
</Text>
)}
</FormControl> */}
<FormControl mb={5} isRequired>
<FormLabel fontSize={"sm"}>Deposit Amount</FormLabel>
<Controller
name="investorAmount"
control={control}
render={({ field }) => (
<CurrencyInput {...field} textAlign={'right'} fontSize={"sm"} type="number" size={"sm"} />
)}
/>
{errors.investorAmount && (
<Text fontSize="xs" color="red">
{errors.investorAmount.message}
</Text>
)}
</FormControl>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Upload Supporting</FormLabel>
<Input
focusBorderColor="green.400"
name="supporting_FileName"
{...register("supporting_FileName")}
fontSize="sm"
type="file"
size="sm"
placeholder={"$100,000"}
className="form-control"
accept="image/*"
/>
{errors.supporting_FileName && (
<Text fontSize="xs" color="red">
{errors.supporting_FileName.message}
</Text> </Text>
)} )}
</FormControl> </FormControl>
<FormControl mb={4}> <FormControl mb={4}>
<FormLabel fontSize="sm">Total Amount</FormLabel> <FormLabel fontSize="sm">Comment</FormLabel>
<Input <Textarea
focusBorderColor='green.400' rows={5}
name="fileName" focusBorderColor="green.400"
{...register("fileName")} name="comment"
{...register("comment")}
fontSize="sm" fontSize="sm"
type="text" type="textarea"
size="sm" size="sm"
placeholder={"$100,000"} placeholder={"Enter your comment...."}
resize={"none"}
maxLength={200}
/> />
{errors.totalAmount && ( {errors.comment && (
<Text fontSize="xs" color="red"> <Text fontSize="xs" color="red">
{errors.totalAmount.message} {errors.comment.message}
</Text> </Text>
)} )}
<FormHelperText fontSize="xs" color="gray.500">
<Box as="span" me={1}>Maximum length should be 200 characters. You have entered </Box>
{watch("comment")?.length || 0} characters.
</FormHelperText>
</FormControl> </FormControl>
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>
<Button colorScheme="gray" mr={3} onClick={onClose} size={'sm'} rounded={'sm'}> <Button
colorScheme="gray"
mr={3}
onClick={onClose}
size={"sm"}
rounded={"sm"}
>
Cancel Cancel
</Button> </Button>
<Button colorScheme="forestGreen" variant="solid" size={'sm'} rounded={'sm'}> <Button
colorScheme="forestGreen"
variant="solid"
size={"sm"}
rounded={"sm"}
isLoading={isBtnLoading}
type="submit"
>
Confirm Confirm
</Button> </Button>
</ModalFooter> </ModalFooter>
</Box> </Box>
</ModalContent> )}
</Modal> </ModalContent>
); </Modal>
}; );
};
export default DepositRequestApprove;
export default DepositRequestApprove;

View File

@@ -1,99 +1,171 @@
import { import {
Box, Box,
Button, Button,
FormControl, FormControl,
FormLabel, FormHelperText,
Input, FormLabel,
Modal, Input,
ModalBody, Modal,
ModalCloseButton, ModalBody,
ModalContent, ModalCloseButton,
ModalFooter, ModalContent,
ModalHeader, ModalFooter,
ModalOverlay, ModalHeader,
Text, ModalOverlay,
Textarea, Text,
useDisclosure, Textarea,
} from "@chakra-ui/react"; useDisclosure,
import React from "react"; useToast,
import * as yup from "yup"; } from "@chakra-ui/react";
import { yupResolver } from "@hookform/resolvers/yup"; import React, { useEffect, useState } from "react";
import { useForm } from "react-hook-form"; import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
export const conformModalSchema = yup.object().shape({ import { useForm } from "react-hook-form";
comment: yup.string().required("Comment is required"), import { useDepositRejectMutation } from "../../../Services/deposit.request.service";
import ToastBox from "../../../Components/ToastBox";
export const conformModalSchema = yup.object().shape({
comments: yup.string().required("Comment is required")
.max(200, "Approve Comment cannot be more than 200 characters"),
});
const DepositRequestReject = ({ isOpen, onClose, firstField ,id}) => {
const [isBtnLoading , setIsBtnLoading] = useState(false)
const toast = useToast()
const {
register,
reset,
watch,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
}); });
const [ depositReject ] = useDepositRejectMutation()
const DepositRequestReject = ({ isOpen, onClose, firstField }) => {
const { const onSubmit = async(data) => {
register, setIsBtnLoading(true)
handleSubmit, try {
formState: { errors }, const res = await depositReject({ id ,data})
} = useForm({
resolver: yupResolver(conformModalSchema),
});
const onSubmit = (data) => { if (res?.error) {
setFile(data.document[0]); toast({
render: () => (
const newDocument = { <ToastBox message={res?.error?.data?.message} status={"error"} />
...data, ),
document: data.document[0].name, // Store the document name });
comment: true, setIsBtnLoading(false)
id: uuidv4(), onClose();
Type: getFileIcon(file.type),
}; }else if(res?.data?.statusCode === 200) {
toast({
setCreate((prevCreate) => [...prevCreate, newDocument]); render: () => (
onClose(); <ToastBox message={res?.data?.message} />
}; ),
});
const handleFileChange = (event) => { setIsBtnLoading(false)
const selectedFile = event.target.files[0]; onClose();
setFile(selectedFile);
}; }
return ( } catch (error) {
<Modal isOpen={isOpen} onClose={onClose} initialFocusRef={firstField}> console.log(error);
<ModalOverlay />
<ModalContent pb={4}> }
<ModalHeader fontSize={"md"}>Reject</ModalHeader>
<ModalCloseButton />
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody>
<FormControl mb={4}>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea rows={6}
focusBorderColor='green.400'
name="fileName"
{...register("fileName")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"$100,000"}
rounded={'md'}
resize={'none'}
/>
{errors.comment && (
<Text fontSize="xs" color="red">
{errors.comment.message}
</Text>
)}
</FormControl>
</ModalBody>
<ModalFooter>
<Button colorScheme="gray" mr={3} onClick={onClose} size={'sm'} rounded={'sm'}>
Cancel
</Button>
<Button colorScheme="forestGreen" variant="solid" size={'sm'} rounded={'sm'}>
Send
</Button>
</ModalFooter>
</Box>
</ModalContent>
</Modal>
);
}; };
export default DepositRequestReject; const handleFileChange = (event) => {
const selectedFile = event.target.files[0];
setFile(selectedFile);
};
const { data, isLoading } =
(id, {
skip: !id,
});
useEffect(() => {
if (data) {
reset({
investorAmount: data?.data?.investorAmount,
});
}
}, [data, reset]);
const heandleOnClose = () =>{
reset()
onClose()
}
return (
<Modal isCentered isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
<ModalOverlay />
<ModalContent pb={4}>
<ModalHeader fontSize={"md"}>Investor Comment</ModalHeader>
<ModalCloseButton />
{isLoading ? (
<FullscreenLoaders height={"50vh"} />
) : (
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea
rows={6}
focusBorderColor="green.400"
name="comments"
{...register("comments")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your comment...."}
rounded={"md"}
resize={"none"}
maxLength={200}
/>
{errors.comments && (
<Text fontSize="xs" color="red">
{errors.comments.message}
</Text>
)}
<FormHelperText fontSize="xs" color="gray.500">
<Box as="span" me={1}>Maximum length should be 200 characters. You have entered </Box>
{watch("comments")?.length || 0} characters.
</FormHelperText>
</FormControl>
</ModalBody>
<ModalFooter>
<Button
colorScheme="gray"
mr={3}
onClick={onClose}
size={"sm"}
rounded={"sm"}
>
Cancel
</Button>
<Button
colorScheme="forestGreen"
variant="solid"
size={"sm"}
rounded={"sm"}
isLoading={isBtnLoading}
type="submit"
>
Send
</Button>
</ModalFooter>
</Box>
)}
</ModalContent>
</Modal>
);
};
export default DepositRequestReject;

View File

@@ -8,70 +8,107 @@ import {
Text, Text,
Tooltip, Tooltip,
useToast, useToast,
useDisclosure useDisclosure,
Link,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import React, { useContext, useEffect, useState } from "react"; import React, { useContext, useEffect, useState } from "react";
import { HiDotsVertical } from "react-icons/hi"; import { HiDotsVertical } from "react-icons/hi";
import { Link, Link as RouterLink, useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { debounce } from "../../Master/Sponser/AddSponser"; import { debounce } from "../../Master/Sponser/AddSponser";
import { OPACITY_ON_LOAD } from "../../../Layout/animations"; import { OPACITY_ON_LOAD } from "../../../Layout/animations";
import Pagination from "../../../Components/Pagination"; import Pagination from "../../../Components/Pagination";
import GlobalStateContext from "../../../Contexts/GlobalStateContext"; import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../../Components/CustomAlertDialog"; import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import ToastBox from "../../../Components/ToastBox"; import ToastBox from "../../../Components/ToastBox";
import DataTable from "../../../Components/DataTable/DataTable"; import NormalTable from "../../../Components/DataTable/NormalTable";
import ConfirmModal from "./ConfirmModal"; import ConfirmModal from "./ConfirmModal";
import RejectModal from "./RejectModal"; import RejectModal from "./RejectModal";
import {
useDepositRejectMutation,
useGetDepositHistoryQuery,
} from "../../../Services/deposit.request.service";
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter import { ExternalLinkIcon } from "@chakra-ui/icons";
import { TABLE_PAGINATION } from "../../../Constants/Paginations";
import { generateSerialNumber } from "../../../Constants/Constants";
const formatDate = (date) => {
return new Date(date).toLocaleDateString('en-GB', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
});
}; // Simple date formatter
const DepositHistory = () => { const DepositHistory = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const toast = useToast(); const toast = useToast();
const { depositHistory, setDepositHistory, slideFromRight } = const { depositHistory, setDepositHistory, slideFromRight } =
useContext(GlobalStateContext); useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false); const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false); const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false); const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState(""); const [mouseEnteredId, setMouseEnteredId] = useState("");
const { // const {
isOpen: isConfirmOpen, // isOpen: isConfirmOpen,
onOpen: onConfirmOpen, // onOpen: onConfirmOpen,
onClose: onConfirmClose, // onClose: onConfirmClose,
} = useDisclosure(); // } = useDisclosure();
const { // const {
isOpen: isRejectOpen, // isOpen: isRejectOpen,
onOpen: onRejectOpen, // onOpen: onRejectOpen,
onClose: onRejectClose, // onClose: onRejectClose,
} = useDisclosure(); // } = useDisclosure();
// =========================== [Use State] =============================
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
const [searchTerm, setSearchTerm] = useState("");
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
// Debounce the search term to avoid making a request on every keystroke
useEffect(() => { useEffect(() => {
// Simulate loading const handler = setTimeout(() => {
const timer = setTimeout(() => { setDebouncedSearchTerm(searchTerm);
setIsLoading(false); }, 500); // Adjust delay as needed
}, 1500); return () => {
clearTimeout(handler);
};
}, [searchTerm]);
// Cleanup the timer on component unmount
return () => clearTimeout(timer); const {
}, []); data,
error,
refetch,
isLoading: depositHistoryLoading,
} = useGetDepositHistoryQuery({
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
search: debouncedSearchTerm,
},
{
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
});
// Use useEffect to refetch data when the component mounts
useEffect(() => {
refetch();
}, [refetch]);
// ====================================================[Table Setup]================================================================ // ====================================================[Table Setup]================================================================
const tableHeadRow = [ const tableHeadRow = [
"Sr.no", "Sr.no",
"Date",
"Client ID", "Client ID",
"First Name", "First Name",
"Last Name", "Last Name",
"Country", "Country",
"Phone Number", "Phone Number",
"Currency",
"Deposit Amount", "Deposit Amount",
"Fees", "Deposit Date",
"Total Amount",
"Amount in Investor currency",
"Status", "Status",
"Supporting's",
]; ];
const handleUpdateStatus = debounce((id) => { const handleUpdateStatus = debounce((id) => {
@@ -87,36 +124,130 @@ const DepositHistory = () => {
}); });
}, 300); }, 300);
// ====================================================[Table Filter]================================================================ const filteredData = data?.data?.rows
const filteredData = depositHistory.filter((item) => { .filter((item) => {
// Filter by name (case insensitive) // Filter by name (case insensitive)
const name = item.clientId; const name = [item.firstName, item.lastName, item.countryName]
const searchLower = searchTerm.toLowerCase(); .filter(Boolean)
const nameMatches = name.toLowerCase().includes(searchLower); .join(" ");
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
return nameMatches; // Filter by status (Uncomment and use if needed)
}); // const status = item.status;
// const statusLower = status ? "active" : "inactive";
// const statusMatches =
// statusFilter === "all" ||
// (statusFilter === "active" && status === true) ||
// (statusFilter === "inactive" && status === false);
return nameMatches;
})
.sort((b, a) => new Date(a.createdAt) - new Date(b.createdAt));
// const handleView = (id) => { // const handleView = (id) => {
// setActionId(id); // setActionId(id);
// onViewOpen(); // onViewOpen();
// }; // };
const [extractedArray, setExtractedArray] = useState( const extractedArray = data?.data?.rows?.map((item, idx) => ({
filteredData?.map((item, index) => ({ "Sr.no": (
"Sr.no": ( <Text
<Text w={"10px"}
w={"30px"} justifyContent={slideFromRight ? "right" : "left"}
justifyContent={slideFromRight ? "right" : "left"} as={"span"}
as={"span"} color={"teal.900"}
color={"teal.900"} fontWeight={"500"}
fontWeight={"500"} className="d-flex align-items-center web-text-small"
className="d-flex align-items-center web-text-small" >
> {generateSerialNumber(idx,currentPage, pageSize )}
{index + 1} </Text>
),
"Client ID": (
<Text
w={"60px"}
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item?.clientReference_id}
</Text>
),
"First Name": (
<Box isTruncated={true} w={"60px"}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.firstName}
</Text> </Text>
), </Box>
"Date": ( ),
"Last Name": (
<Box w={"70px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.lastName}
</Text>
</Box>
),
Country: (
<Box w={"80px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.countryName}
</Text>
</Box>
),
"Phone Number": (
<Box w={"80px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.mobileNumber}
</Text>
</Box>
),
"Deposit Amount": (
<Box
isTruncated={true}
display={"flex"}
justifyContent={"end"}
>
<Text as={"span"} color={"teal.900"} textAlign={"right"}>
{parseFloat(item?.investorAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
<Badge ms={1} colorScheme="green">
{item?.currencyCode}
</Badge>
</Text>
</Box>
),
"Deposit Date": (
<Text
w={"60px"}
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{formatDate(item?.createdAt)}
</Text>
),
Status: (
<Box w={"70px"} isTruncated={true} cursor={"pointer"}>
<Text
as={"span"}
color={
item.transactionStatus === "Approved" ? "green.500" : "red.500"
}
fontWeight={700}
>
{item.transactionStatus}
</Text>
</Box>
),
"Supporting's":
item.transactionStatus === "Approved" ? (
<Text <Text
w={"60px"} w={"60px"}
justifyContent={slideFromRight ? "right" : "left"} justifyContent={slideFromRight ? "right" : "left"}
@@ -125,110 +256,34 @@ const DepositHistory = () => {
fontWeight={"500"} fontWeight={"500"}
className="d-flex align-items-center web-text-small" className="d-flex align-items-center web-text-small"
> >
{item.date} {/* {item?.supporting_FileName} */}
</Text> <Badge
), px={2}
"Client ID": ( py={0.5}
<Text display={"flex"}
w={"60px"} alignItems={"center"}
justifyContent={slideFromRight ? "right" : "left"} textTransform={"inherit"}
as={"span"} fontWeight={500}
color={"teal.900"} colorScheme={"forestGreen"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.clientId}
</Text>
),
"First Name": (
<Box isTruncated={true} w={"50px"}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item.firstName}
</Text>
</Box>
),
"Last Name": (
<Box w={"50px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.lastName}
</Text>
</Box>
),
Country: (
<Box w={"70px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.country}
</Text>
</Box>
),
"Phone Number": (
<Box w={"70px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.phoneNumber}
</Text>
</Box>
),
Currency: (
<Box w={"50px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
<Badge px={2} py={1}>
{item.currency}
</Badge>
</Text>
</Box>
),
"Deposit Amount": (
<Box w={"50px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.depositAmount}
</Text>
</Box>
),
Fees: (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.fees}
</Text>
</Box>
),
"Total Amount": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.totalAmount}
</Text>
</Box>
),
"Amount in Investor currency": (
<Box w={"70px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.amountcurrency}
</Text>
</Box>
),
Status: (
<Box w={"70px"} isTruncated={true} cursor={'pointer'}>
<Text
// onClick={() => {
// setActionId(item.id);
// onConfirmOpen();
// }}
onClick={() => {
setActionId(item.id);
if (item.status === "Approved") {
onConfirmOpen();
} else {
onRejectOpen();
}
}}
as={"span"}
color={item.status === "Approved" ? "green" : "red"}
> >
{item.status} <Link
</Text> href={import.meta.env.VITE_IMAGE_URL + item?.supporting_FileName}
</Box> isExternal
>
<Box as="span" cursor={"pointer"}>
View
</Box>
<ExternalLinkIcon />
</Link>
{/* <Link to="www.google.com" isExternal>
<Box as="span">View</Box> <ExternalLinkIcon />
</Link> */}
</Badge>
</Text>
) : (
""
), ),
})) }));
);
const handleDelete = () => { const handleDelete = () => {
const IOtype = investmentType.filter( const IOtype = investmentType.filter(
@@ -245,15 +300,14 @@ const DepositHistory = () => {
return ( return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}> <Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
<ConfirmModal {/* <ConfirmModal
isOpen={isConfirmOpen} isOpen={isConfirmOpen}
onClose={onConfirmClose} onClose={onConfirmClose}
// firstField={firstField}
/> />
<RejectModal <RejectModal
isOpen={isRejectOpen} isOpen={isRejectOpen}
onClose={onRejectClose} onClose={onRejectClose}
/> /> */}
<Box bg="white.500"> <Box bg="white.500">
<HStack <HStack
display={"flex"} display={"flex"}
@@ -276,17 +330,24 @@ const DepositHistory = () => {
/> />
<HStack display={"flex"} alignItems={"center"}> <HStack display={"flex"} alignItems={"center"}>
<Pagination totalItems={10} /> <Pagination
isLoading={depositHistoryLoading}
pageSize={pageSize}
setPageSize={setPageSize}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
totalItems={data?.data?.totalItems}
/>
</HStack> </HStack>
</HStack> </HStack>
</Box> </Box>
<DataTable <NormalTable
emptyMessage={`We don't have any Investment type `} emptyMessage={`We don't have any Investment type `}
tableHeadRow={tableHeadRow} tableHeadRow={tableHeadRow}
setData={setExtractedArray} // setData={setExtractedArray}
data={extractedArray} data={extractedArray}
isLoading={isLoading} isLoading={depositHistoryLoading}
viewActionId={actionId} viewActionId={actionId}
setViewActionId={setActionId} setViewActionId={setActionId}
setMouseEnteredId={setMouseEnteredId} setMouseEnteredId={setMouseEnteredId}

View File

@@ -46,7 +46,7 @@ const RejectModal = ({ isOpen, onClose, firstField }) => {
setCreate((prevCreate) => [...prevCreate, newDocument]); setCreate((prevCreate) => [...prevCreate, newDocument]);
onClose(); onClose();
}; };
const handleFileChange = (event) => { const handleFileChange = (event) => {
const selectedFile = event.target.files[0]; const selectedFile = event.target.files[0];

View File

@@ -0,0 +1,303 @@
import {
Badge,
Box,
Button,
FormControl,
FormHelperText,
FormLabel,
HStack,
Input,
Text,
useToast,
} from "@chakra-ui/react";
import React, { useState } from "react";
import { OPACITY_ON_LOAD } from "../../Layout/animations";
import NormalTable from "../../Components/DataTable/NormalTable";
import { useGetUnbanInvestorQuery } from "../../Services/ban.investor.service";
import { formatDate, generateSerialNumber } from "../../Constants/Constants";
import { TABLE_PAGINATION } from "../../Constants/Paginations";
import ReactQuill from "react-quill";
import "react-quill/dist/quill.snow.css"; // Importing the Quill snow theme
import { useSendCustomEmailMutation } from "../../Services/contact.service";
import { EmailIcon } from "@chakra-ui/icons";
import ToastBox from "../../Components/ToastBox";
const EmailNotification = () => {
const [isLoading, setIsLoading] = useState(false);
const [selectedRadio, setSelectedRadio] = useState([]);
const [subject, setSubject] = useState("");
const [value, setValue] = useState(""); // Quill content (body)
const toast = useToast();
const [sendCustomNotification] = useSendCustomEmailMutation();
// ===========================[Table Setup]==============================
const tableHeadRow = [
"Sr N/O",
"Date",
"Client ID",
"First Name",
"Last Name",
"Country",
"Phone Number",
"E-mail ID",
"KYC Status",
];
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
const {
data: investorDetails,
isLoading: investorDetailsLoading,
refetch,
} = useGetUnbanInvestorQuery({
page: currentPage, // Omit pagination for search
size: 10000, // Omit pagination for search
});
const extractedArray = investorDetails?.data?.rows?.map((item, idx) => ({
id: item?.principal_xid,
"Sr N/O": (
<Text
justifyContent={"left"}
as={"span"}
color={"gray.600"}
className="d-flex align-items-center fw-bold web-text-small"
>
{generateSerialNumber(idx, currentPage, pageSize)}
</Text>
),
Date: (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{formatDate(item?.date)}
</Text>
</Box>
),
"Client ID": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.clientReference_id}
</Text>
</Box>
),
"First Name": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.firstName}
</Text>
</Box>
),
"Last Name": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.lastName}
</Text>
</Box>
),
Country: (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.country}
</Text>
</Box>
),
"Phone Number": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.phoneNumber}
</Text>
</Box>
),
"E-mail ID": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.emailAddress}
</Text>
</Box>
),
"KYC Status": (
<Box w={"auto"} isTruncated={true}>
<Badge
fontWeight={"500"}
textTransform={"none"}
color={item?.KYCStatus === false ? "red" : "blue"}
px={2}
py={0.5}
variant={"ghost"}
>
{item?.KYCStatus === true ? "Completed" : "Incompleted"}
</Badge>
</Box>
),
}));
const modules = {
toolbar: [
// [{ header: "1" }, { header: "2" },
// // { font: [] }
// ],
// [{ size: [] }],
["bold", "italic", "underline", "strike", "blockquote"],
[{ list: "ordered" }, { list: "bullet" }],
["clean"],
],
};
// Main submission logic
const handleSend = async (e) => {
e.preventDefault(); // Prevent default form submission
if (!subject || !value) {
toast({
render: () => (
<ToastBox status={"error"} message={"Subject or email body cannot be empty"} />
),
});
return;
}
if (selectedRadio.length === 0) {
toast({
render: () => (
<ToastBox status={"error"} message={"Please select at least one recipient"} />
),
});
return;
}
setIsLoading(true);
const emailPayload = {
subject,
body: value,
principal_xid: selectedRadio,
};
try {
const res = await sendCustomNotification(emailPayload)
console.log(res);
if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsLoading(false)
}else if(res?.data){
toast({
render: () => (
<ToastBox message={res?.data?.message} />
),
});
setIsLoading(false)
setSubject("")
setValue("")
setSelectedRadio([])
}else{
toast({
render: () => (
<ToastBox status={'error'} message={"Something went wrong"} />
),
});
setIsLoading(false)
}
} catch (error) {
}
};
return (
<Box
as={"form"}
{...OPACITY_ON_LOAD}
overflowY={"scroll"}
height={"100vh"}
pb={14}
pt={4}
>
<FormControl mb={0}>
{/* <HStack
py={4}
pb={3}
w={"100%"}
justifyContent={"space-between"}
alignItems={"center"}
>
<Text as={"span"} fontSize={"sm"}>
Customize your email
</Text>
</HStack> */}
<FormControl isRequired mb={3} p={1}>
<FormLabel fontSize={"sm"}>Subject</FormLabel>
<Input
size={"md"}
value={subject}
onChange={(e) => setSubject(e.target.value)}
focusBorderColor="forestGreen.300"
rounded={0.5}
type="text"
/>
{/* <FormHelperText>Entered subject will be reflected on emails subject body.</FormHelperText> */}
</FormControl>
<FormControl minH={400} isRequired mb={3} p={1}>
<FormLabel fontSize={"sm"}>Create Custom body</FormLabel>
<ReactQuill
theme="snow"
style={{
height:300
}}
value={value}
onChange={setValue}
modules={modules}
placeholder="Start typing here..."
/>
</FormControl>
{/* <FormHelperText fontSize={"xs"}>
We'll never share your email.
</FormHelperText> */}
</FormControl>
<Box overflow={'scroll'} h={'58vh'}>
<NormalTable
centered={true}
emptyMessage={`We don't have any Sponsors`}
tableHeadRow={tableHeadRow}
data={extractedArray}
setSelectedRadio={setSelectedRadio}
selectedRadio={selectedRadio}
showRadioButton={true}
/>
</Box>
<HStack justifyContent={"flex-end"} px={2}>
<Button
rightIcon={<EmailIcon />}
rounded={"sm"}
size={"sm"}
colorScheme="forestGreen"
type="submit"
isLoading={isLoading}
onClick={handleSend}
>
Send Email
</Button>
</HStack>
</Box>
);
};
export default EmailNotification;

View File

@@ -0,0 +1,440 @@
import React, { useState } from "react";
import { OPACITY_ON_LOAD } from "../../Layout/animations";
import {
Box,
Button,
HStack,
Input,
InputGroup,
InputRightAddon,
Textarea,
useDisclosure,
Image,
Icon,
VStack,
Text,
useToast,
} from "@chakra-ui/react";
import { FormControl, FormLabel, FormHelperText } from "@chakra-ui/react";
import { DeleteIcon, Search2Icon } from "@chakra-ui/icons";
import SelectInvestorModal from "./SelectInvestorModal";
import { Controller, useForm } from "react-hook-form"; // Import useForm
import { yupResolver } from "@hookform/resolvers/yup"; // Import resolver for Yup
import * as Yup from "yup"; // Import Yup for validation
import { motion } from "framer-motion"; // Import Framer Motion for animations
import { bytesToMB } from "../../Constants/Constants";
import { useCreateFawateerRequestMutation } from "../../Services/fawateer.maker.service";
import ToastBox from "../../Components/ToastBox";
import { useNavigate } from "react-router-dom";
import CurrencyInput from "../../Components/CurrencyInput";
// Validation schema using Yup
const validationSchema = Yup.object().shape({
investorName: Yup.string().required("Investor name is required"),
clientId: Yup.string().required("Client ID is required"),
transaction_date: Yup.date()
.required("Date is required")
.transform((value, originalValue) => {
return originalValue === "" ? null : value; // Convert empty strings to null
})
.typeError("Please enter a valid date")
.max(new Date(), "Date cannot be in the future"),
transaction_amount: Yup.number()
.required("Transaction amount is required")
.transform((value, originalValue) => (originalValue === "" ? null : value)) // Convert empty strings to null
.typeError("Transaction amount must be a number") // Custom error message if it's not a number
.positive("Transaction amount must be greater than zero"),
spportFile_path: Yup.mixed().required("Support file is required"),
makerComment: Yup.string() .max(200, "Approve Comment cannot be more than 50 characters"),
});
const CreateRequest = () => {
const toast = useToast();
const navigate = useNavigate();
const { isOpen, onOpen, onClose } = useDisclosure();
const [selectedInvestor, setSelectorInvestor] = useState({});
const [filePreview, setFilePreview] = useState(null); // State for previewing the file
const [fileType, setFileType] = useState(null); // State to store file type for conditional rendering
const [isLoading, setIsLoading] = useState(false);
const [id, setId] = useState(null);
// Initialize useForm with the resolver for Yup validation
const {
control,
register,
watch,
handleSubmit,
setValue,
reset,
formState: { errors },
} = useForm({
resolver: yupResolver(validationSchema),
});
const [creatFawaateerRequest] = useCreateFawateerRequestMutation();
const onSubmit = async (data) => {
console.log(data);
setIsLoading(true);
// Convert data to FormData
const formData = new FormData();
// Append each field from the data object to the FormData
Object.keys(data).forEach((key) => {
if (key === "spportFile_path" && data[key] instanceof FileList) {
// Append the first file from FileList (assuming single file input)
formData.append(key, data[key][0]); // Append the file
} else {
formData.append(key, data[key]); // Append other fields
}
});
try {
// Make the API call with formData
const res = await creatFawaateerRequest({ data: formData, id });
if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsLoading(false);
reset();
return;
} else if (res?.data) {
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
setIsLoading(false);
navigate("/fawateer-history");
return;
} else {
toast({
render: () => (
<ToastBox status={"error"} message={"Something went wrong"} />
),
});
setIsLoading(false);
return;
}
} catch (error) {
console.error("Error:", error);
toast({
render: () => (
<ToastBox status={"error"} message={"An error occurred"} />
),
});
setIsLoading(false);
return;
}
};
// Handle file change and preview
const handleFileChange = (e) => {
const file = e.target.files[0];
console.log(file);
setValue("spportFile_path", file); // Set the file value in the form
setFileType(file); // Set the file type
if (file && file.type.startsWith("image/")) {
// If the file is an image, generate a preview
const reader = new FileReader();
reader.onload = () => {
setFilePreview(reader.result); // Set the image preview
};
reader.readAsDataURL(file);
} else {
setFilePreview(null); // Clear preview if the file is not an image
}
};
return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
<Box
display={"flex"}
justifyContent={"space-between"}
flexWrap={"wrap"}
alignItems={"center"}
mt={5}
px={4}
as="form"
onSubmit={handleSubmit(onSubmit)}
>
{/* Investor Name Field */}
<FormControl
isRequired
w={"49%"}
mb={2}
isInvalid={errors.investorName}
>
<FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}>
Investor name
</FormLabel>
<InputGroup size="sm">
<Input
bg={"#F5F8F6"}
focusBorderColor="forestGreen.300"
size={"sm"}
fontSize={"sm"}
rounded={"sm"}
type={"text"}
readOnly={true}
placeholder={"Investor name"}
{...register("investorName")}
_placeholder={{ fontSize: "sm" }}
/>
<InputRightAddon
gap={2}
color={"forestgreen.400"}
onClick={onOpen}
cursor={"pointer"}
fontWeight={600}
fontSize={"xs"}
>
<Search2Icon /> Search
</InputRightAddon>
</InputGroup>
<FormHelperText
fontSize={"xs"}
fontWeight={500}
style={{ color: "red" }}
>
{errors.investorName?.message}
</FormHelperText>
</FormControl>
{/* Client ID Field */}
<FormControl isRequired w={"49%"} mb={2} isInvalid={errors.clientId}>
<FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}>
Client Id
</FormLabel>
<Input
bg={"#F5F8F6"}
focusBorderColor="forestGreen.300"
size={"sm"}
fontSize={"sm"}
rounded={"sm"}
type={"text"}
readOnly={true}
placeholder={"Client ID"}
{...register("clientId")}
/>
<FormHelperText
fontSize={"xs"}
fontWeight={500}
style={{ color: "red" }}
>
{errors.clientId?.message}
</FormHelperText>
</FormControl>
{/* Date Field */}
<FormControl isRequired w={"49%"} mb={2} isInvalid={errors.date}>
<FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}>
Date
</FormLabel>
<Input
bg={"#F5F8F6"}
focusBorderColor="forestGreen.300"
size={"sm"}
fontSize={"sm"}
rounded={"sm"}
type={"date"}
max={new Date().toLocaleDateString("en-CA")} // Ensures max is in local timezone
{...register("transaction_date", {
setValueAs: (value) => {
// Convert date string to local timezone Date object
return value ? new Date(value) : undefined;
},
})}
/>
<FormHelperText
fontSize={"xs"}
fontWeight={500}
style={{ color: "red" }}
>
{errors.transaction_date?.message}
</FormHelperText>
</FormControl>
{/* Amount Field */}
<FormControl isRequired w={"49%"} mb={2} isInvalid={errors.amount}>
<FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}>
Amount (BHD)
</FormLabel>
<Controller
name="transaction_amount"
control={control}
render={({ field }) => (
<CurrencyInput
bg={"#F5F8F6"}
{...field}
textAlign={"right"}
fontSize={"sm"}
type="number"
size={"sm"}
/>
)}
/>
<FormHelperText
fontSize={"xs"}
fontWeight={500}
style={{ color: "red" }}
>
{errors.transaction_amount?.message}
</FormHelperText>
</FormControl>
{/* Support File Field with Preview */}
<FormControl
isRequired
w={"49%"}
mb={2}
isInvalid={errors.spportFile_path}
>
<FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}>
Support file
</FormLabel>
<Input
bg={"#F5F8F6"}
focusBorderColor="forestGreen.300"
size={"sm"}
fontSize={"sm"}
rounded={"sm"}
type={"file"}
className="form-control"
name="spportFile_path"
placeholder={"Support file"}
{...register("spportFile_path")}
// onChange={handleFileChange}
/>
<FormHelperText
fontSize={"xs"}
fontWeight={500}
style={{ color: "red" }}
>
{errors.spportFile_path?.message}
</FormHelperText>
{/* Animated Preview */}
{filePreview && fileType?.type.startsWith("image/") && (
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.5 }}
style={{ marginTop: "10px" }}
>
<Box
position={"relative"}
display={"flex"}
alignContent={"flex-end"}
gap={3}
mt={2}
>
<Image
src={filePreview}
alt="File preview"
maxW={"150px"}
borderRadius="md"
boxShadow="md"
/>
<Icon
onClick={() => setFilePreview(null)}
className="link"
rounded={"md"}
color={"red.700"}
cursor={"pointer"}
p={1.5}
position={"absolute"}
top={0}
right={0}
as={DeleteIcon}
boxSize={7}
/>
<VStack justifyItems={"flex-end"} alignItems={"flex-start"}>
<Text as={"span"} color={"gray.600"} fontSize={"xs"}>
File Name:{" "}
<Text as={"span"} color={"GrayText"}>
{" "}
{fileType?.name}
</Text>
</Text>
<Text as={"span"} color={"gray.600"} fontSize={"xs"}>
File Size:{" "}
<Text as={"span"} color={"GrayText"}>
{" "}
{bytesToMB(fileType?.size)} Mb
</Text>
</Text>
<Text as={"span"} color={"gray.600"} fontSize={"xs"}>
File Type:{" "}
<Text as={"span"} color={"GrayText"}>
{" "}
{fileType?.type}
</Text>
</Text>
</VStack>
</Box>
</motion.div>
)}
</FormControl>
{/* Description Field */}
<FormControl w={"100%"} mb={2} isInvalid={errors.makerComment}>
<FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}>
Description
</FormLabel>
<Textarea
bg={"#F5F8F6"}
focusBorderColor="forestGreen.300"
size={"sm"}
fontSize={"sm"}
rounded={"sm"}
placeholder={"Description"}
{...register("makerComment")}
maxLength={200}
/>
<FormHelperText
fontSize={"xs"}
fontWeight={500}
style={{ color: "red" }}
>
{errors.makerComment?.message}
</FormHelperText>
<FormHelperText fontSize="xs" color="gray.500">
<Box as="span" me={1}>
Maximum length should be 200 characters. You have entered
</Box>
{watch("makerComment")?.length || 0} characters.
</FormHelperText>
</FormControl>
{/* Submit Button */}
<HStack mt={2} w={"100%"} justifyContent={"flex-end"}>
<Button
colorScheme="forestGreen"
size={"sm"}
rounded={"sm"}
fontSize={"xs"}
type="submit"
isLoading={isLoading}
>
Create request
</Button>
</HStack>
</Box>
<SelectInvestorModal
setId={setId}
setValue={setValue}
onClose={onClose}
isOpen={isOpen}
onOpen={onOpen}
/>
</Box>
);
};
export default CreateRequest;

View File

@@ -18,9 +18,11 @@ import {
useDisclosure, useDisclosure,
useToast, useToast,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import React, { useContext, useEffect, useState,useRef } from "react"; import React, { useContext, useEffect, useState, useRef } from "react";
import { OPACITY_ON_LOAD } from "../../Layout/animations";
import NormalTable from "../../Components/DataTable/NormalTable";
import { HiDotsVertical } from "react-icons/hi"; import { HiDotsVertical } from "react-icons/hi";
import { Link, Link as RouterLink,useNavigate } from "react-router-dom"; import { Link, Link as RouterLink, useNavigate } from "react-router-dom";
import { import {
AddIcon, AddIcon,
DeleteIcon, DeleteIcon,
@@ -28,30 +30,68 @@ import {
EmailIcon, EmailIcon,
ViewIcon, ViewIcon,
} from "@chakra-ui/icons"; } from "@chakra-ui/icons";
import GlobalStateContext from "../../Contexts/GlobalStateContext";
import { debounce } from "../Master/Sponser/AddSponser";
import { OPACITY_ON_LOAD } from "../../Layout/animations";
import DataTable from "../../Components/DataTable/DataTable";
import CustomAlertDialog from "../../Components/CustomAlertDialog";
import Pagination from "../../Components/Pagination"; import Pagination from "../../Components/Pagination";
import GlobalStateContext from "../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../Components/CustomAlertDialog";
import ToastBox from "../../Components/ToastBox"; import ToastBox from "../../Components/ToastBox";
import { debounce } from "../Master/Sponser/AddSponser";
// import InvestmentDetailsEdit from "./InvestmentDetailsEdit";
import { useGetInvestorsQuery } from "../../Services/investor.details.service";
import { TABLE_PAGINATION } from "../../Constants/Paginations";
import { exportToExcel, generateSerialNumber } from "../../Constants/Constants";
import { LuFileSpreadsheet } from "react-icons/lu";
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
const BankInvestor = () => { const FawateerRequest = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const toast = useToast(); const toast = useToast();
const thirdField = useRef(); const thirdField = useRef();
const { bankInvestor, setBankInvestor, slideFromRight } = const { InvestorDetails, setInvestorDetails, slideFromRight } =
useContext(GlobalStateContext); useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false); const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false); const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false); const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState(""); const [mouseEnteredId, setMouseEnteredId] = useState("");
const {isOpen: isEditOpen,onOpen: onEditOpen,onClose: onEditClose,} = useDisclosure(); const {
const btnRef = React.useRef() isOpen: isEditOpen,
onOpen: onEditOpen,
onClose: onEditClose,
} = useDisclosure();
const btnRef = React.useRef();
// =========================== [Use State] =============================
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
const [searchTerm, setSearchTerm] = useState("");
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
// Debounce the search term to avoid making a request on every keystroke
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedSearchTerm(searchTerm);
}, 500); // Adjust delay as needed
return () => {
clearTimeout(handler);
};
}, [searchTerm]);
const {
data: investorDetails,
isLoading: investorDetailsLoading,
error,
} = useGetInvestorsQuery({
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
search: debouncedSearchTerm,
},
{
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
}
);
useEffect(() => { useEffect(() => {
// Simulate loading // Simulate loading
@@ -65,21 +105,22 @@ const BankInvestor = () => {
// ====================================================[Table Setup]================================================================ // ====================================================[Table Setup]================================================================
const tableHeadRow = [ const tableHeadRow = [
"Sr N/O", "Sr No",
"Client ID", "Client ID",
"First Name", "First Name",
"Last Name", "Last Name",
"Country", "Country",
"Phone Number", "Phone Number",
"E-mail ID", "E-mail ID",
"KYC Status", // "Type",
"Action", // "KYC Status",
"Approval Status",
]; ];
const handleUpdateStatus = debounce((id) => { const handleUpdateStatus = debounce((id) => {
setBankInvestor((prevData) => setInvestorDetails((prevData) =>
prevData.map((bankInvestor) => prevData.map((InvestorDetails) =>
bankInvestor.id === id ? { ...bankInvestor } : bankInvestor InvestorDetails.id === id ? { ...InvestorDetails } : InvestorDetails
) )
); );
toast({ toast({
@@ -88,9 +129,9 @@ const BankInvestor = () => {
}, 300); }, 300);
// ====================================================[Table Filter]================================================================ // ====================================================[Table Filter]================================================================
const filteredData = bankInvestor?.filter((item) => { const filteredData = investorDetails?.data?.rows?.filter((item) => {
// Filter by name (case insensitive) // Filter by name (case insensitive)
const name = item.firstName; const name = [item?.principal?.firstName, item?.principal?.lastName, item?.country?.countryName, item?.principal?.mobileNumber, item?.principal?.emailAddress].filter(Boolean).join(' ');
const searchLower = searchTerm.toLowerCase(); const searchLower = searchTerm.toLowerCase();
const nameMatches = name?.toLowerCase().includes(searchLower); const nameMatches = name?.toLowerCase().includes(searchLower);
@@ -106,74 +147,100 @@ const BankInvestor = () => {
return nameMatches; return nameMatches;
}); });
const extractedArray = filteredData?.map((item) => ({
const customHeaders = [
{ label: "ID", key: "id" },
{ label: "Client ID", key: "clientReference_id" },
{ label: "First Name", key: "principal.firstName" }, // Nested property
{ label: "Last Name", key: "principal.lastName" }, // Nested property
{ label: "Country", key: "country.countryName" }, // Nested property
{ label: "Phone Number", key: "principal.mobileNumber" }, // Nested property
{ label: "E-mail ID", key: "principal.emailAddress" }, // Nested property
{ label: "Type", key: "investor_type.investorTypeName" }, // Nested property
{ label: "Status", key: "ioStatus" }, // Simple property
{ label: "KYC Status", key: "KYCStatus" }, // Simple property
];
const extractedArray = investorDetails?.data?.rows?.map((item, idx) => ({
id: item?.id, id: item?.id,
"Sr N/O": ( "Sr No": (
<Text <Text
w={'24px'}
justifyContent={slideFromRight ? "right" : "left"} justifyContent={slideFromRight ? "right" : "left"}
as={"span"} as={"span"}
color={"gray.600"} color={"gray.600"}
className="d-flex align-items-center fw-bold web-text-small" className="d-flex align-items-center fw-bold web-text-small"
> >
{item.id} {/* {item.id} */}
{generateSerialNumber(idx,currentPage, pageSize )}
</Text> </Text>
), ),
"Client ID": ( "Client ID": (
<Box w={"auto"} isTruncated={true}> <Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}> <Text as={"span"} color={"teal.900"}>
{item.clientId} {item.clientReference_id}
</Text> </Text>
</Box> </Box>
), ),
"First Name": ( "First Name": (
<Box w={"auto"} isTruncated={true}> <Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}> <Text as={"span"} color={"teal.900"}>
{item.firstName} {item?.principal?.firstName}
</Text> </Text>
</Box> </Box>
), ),
"Last Name": ( "Last Name": (
<Box w={"auto"} isTruncated={true}> <Box w={"70px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}> <Text as={"span"} color={"teal.900"}>
{item.lastName} {item?.principal?.lastName}
</Text> </Text>
</Box> </Box>
), ),
Country: ( Country: (
<Box w={"auto"} isTruncated={true}> <Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}> <Text as={"span"} color={"teal.900"}>
{item.country} {item?.country?.countryName}
</Text> </Text>
</Box> </Box>
), ),
"Phone Number": ( "Phone Number": (
<Box w={"auto"} isTruncated={true}> <Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}> <Text as={"span"} color={"teal.900"}>
{item.phoneNumber} {item?.principal?.mobileNumber}
</Text> </Text>
</Box> </Box>
), ),
"E-mail ID": ( "E-mail ID": (
<Box w={"auto"} isTruncated={true}> <Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}> <Text as={"span"} color={"teal.900"}>
{item.mailId} {item?.principal?.emailAddress}
</Text> </Text>
</Box> </Box>
), ),
"KYC Status": ( "Type": (
<Box w={"auto"} isTruncated={true}> <Box w={"auto"} isTruncated={true}>
<Badge fontWeight={'500'} textTransform={'none'} color={item.status === "Completed" ? "blue" : "red"} px={2} py={0.5}> <Text as={"span"} >
{item.status} <Badge color={"forestGreen.500"} variant={'ghost'} fontWeight={"700"} px={2} py={0.5}>
</Badge> {item?.investor_type?.investorTypeName}
</Badge>
</Text>
</Box> </Box>
), ),
Action: ( "Approval Status": (
<Switch <Box w={"auto"} isTruncated={true}>
size={"sm"} <Badge
colorScheme="forestGreen" fontWeight={"700"}
onChange={() => handleUpdateStatus(item.id)} textTransform={"none"}
isChecked={item.status} colorScheme={item.ioStatus ? "red" : "purple"}
/> px={2}
py={0.5}
>
Approved
</Badge>
</Box>
), ),
})); }));
@@ -195,6 +262,10 @@ const BankInvestor = () => {
onEditOpen(); onEditOpen();
}; };
console.log(investorDetails?.data?.totalItems);
return ( return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}> <Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
<Box bg="white.500"> <Box bg="white.500">
@@ -208,7 +279,7 @@ const BankInvestor = () => {
spacing="24px" spacing="24px"
> >
<Input <Input
mt={1} mt={1}
type="search" type="search"
width={300} width={300}
placeholder="Search..." placeholder="Search..."
@@ -218,17 +289,45 @@ const BankInvestor = () => {
value={searchTerm} value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
/> />
<HStack display={"flex"} alignItems={"center"}> <HStack display={"flex"} alignItems={"center"}>
<Pagination totalItems={10} />
<Pagination
isLoading={investorDetailsLoading}
pageSize={pageSize}
setPageSize={setPageSize}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
totalItems={investorDetails?.data?.totalItems}
/>
{/*
<Button
leftIcon={<AddIcon />}
colorScheme="forestGreen"
size={"sm"}
rounded={"sm"}
fontSize={"xs"}
onClick={()=> navigate('create-request')}
>
Create request
</Button> */}
</HStack> </HStack>
</HStack> </HStack>
</Box> </Box>
<DataTable <NormalTable
// centered={true}
emptyMessage={`We don't have any Sponers `} emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow} tableHeadRow={tableHeadRow}
data={extractedArray} data={extractedArray}
isLoading={isLoading} isLoading={investorDetailsLoading}
viewActionId={actionId} viewActionId={actionId}
setViewActionId={setActionId} setViewActionId={setActionId}
setMouseEnteredId={setMouseEnteredId} setMouseEnteredId={setMouseEnteredId}
@@ -246,4 +345,4 @@ const BankInvestor = () => {
); );
}; };
export default BankInvestor; export default FawateerRequest

View File

@@ -0,0 +1,247 @@
import {
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalFooter,
ModalBody,
ModalCloseButton,
Button,
Text,
Box,
Badge,
Select,
HStack,
Input,
useToast,
} from '@chakra-ui/react'
import NormalTable from '../../Components/DataTable/NormalTable'
import { generateSerialNumber } from '../../Constants/Constants';
import { useGetInvestorsQuery } from '../../Services/investor.details.service';
import { useEffect, useState } from 'react';
import { TABLE_PAGINATION } from '../../Constants/Paginations';
import Pagination from '../../Components/Pagination';
import { AddIcon } from '@chakra-ui/icons';
import { useGetFawateerInvestorsQuery } from '../../Services/fawateer.request.service';
import ToastBox from '../../Components/ToastBox';
const SelectInvestorModal = ({ isOpen, setValue, onClose, setId}) => {
const toast = useToast()
// =========================== [Use State] =============================
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
const [searchTerm, setSearchTerm] = useState("");
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
// Debounce the search term to avoid making a request on every keystroke
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedSearchTerm(searchTerm);
}, 500); // Adjust delay as needed
return () => {
clearTimeout(handler);
};
}, [searchTerm]);
const {
data: investorDetails,
isLoading: investorDetailsLoading,
error,
} = useGetFawateerInvestorsQuery({
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
search: debouncedSearchTerm,
},
{
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
}
);
const [ selectedRadio, setSelectedRadio] = useState([])
const [ selectedInvestor, setSelectorInvestor] = useState(null)
const handleCheckboxChange = (id) => {
setSelectedRadio([id]);
const investor = investorDetails?.data?.rows?.find((item)=> item?.id === id)
setSelectorInvestor(investor)
setId(investor?.principal_xid)
// setValue("investorName",`${selectedInvestor?.firstName} ${selectedInvestor?.lastName}`)
// setValue("clientId",selectedInvestor?.clientReference_id)
return
};
const handleAdd = () => {
if(selectedRadio?.length === 0){
toast({
render: () => (
<ToastBox status={'info'} message={"Please Select Investor"} />
),
});
return
}
setValue("investorName",`${selectedInvestor?.firstName} ${selectedInvestor?.lastName}`)
setValue("clientId",selectedInvestor?.clientReference_id)
return onClose()
}
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
"Sr No",
"Client ID",
"First Name",
"Last Name",
// "Country",
"Phone Number",
"E-mail ID",
// "Type",
// "KYC Status",
// "Approval Status",
];
const extractedArray = investorDetails?.data?.rows?.map((item, idx) => ({
id: item?.id,
"Sr No": (
<Text
w={'24px'}
as={"span"}
color={"gray.600"}
className="d-flex align-items-center fw-bold web-text-small"
>
{/* {item.id} */}
{generateSerialNumber(idx,currentPage, pageSize )}
</Text>
),
"Client ID": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.clientReference_id}
</Text>
</Box>
),
"First Name": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.firstName}
</Text>
</Box>
),
"Last Name": (
<Box w={"70px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.lastName}
</Text>
</Box>
),
"Phone Number": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.phoneNumber}
</Text>
</Box>
),
"E-mail ID": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.emailAddress}
</Text>
</Box>
),
"Type": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} >
<Badge color={"forestGreen.500"} variant={'ghost'} fontWeight={"700"} px={2} py={0.5}>
{item?.investor_type?.investorTypeName}
</Badge>
</Text>
</Box>
),
"Approval Status": (
<Box w={"auto"} isTruncated={true}>
<Badge
fontWeight={"700"}
textTransform={"none"}
colorScheme={item.ioStatus ? "red" : "purple"}
px={2}
py={0.5}
>
Approved
</Badge>
</Box>
),
}));
const handleClose = () => {
setSelectorInvestor(null)
handleClose()
}
return (
<Modal
isCentered
onClose={onClose}
isOpen={isOpen}
motionPreset='scale'
>
<ModalOverlay />
<ModalContent maxW={1200}>
<ModalHeader fontSize={'md'}>View Investor's</ModalHeader>
<ModalCloseButton />
<ModalBody>
{/* <Lorem count={2} /> */}
<HStack
display={"flex"}
justifyContent={"space-between"}
ps={1}
pe={1}
pb={4}
spacing="24px"
>
<Input
mt={1}
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<Button leftIcon={<AddIcon/>} fontSize={'xs'} colorScheme='forestGreen' size={'sm'} rounded={'sm'} onClick={handleAdd}>
Select Investor
</Button>
</HStack>
{investorDetailsLoading?"Loaading":<NormalTable
// centered={true}
emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={investorDetailsLoading}
handleCheckboxChange={handleCheckboxChange}
setSelectedRadio={setSelectedRadio}
selectedRadio={selectedRadio}
showRadioButton={true}
radio={true}
/>}
</ModalBody>
</ModalContent>
</Modal>
)
}
export default SelectInvestorModal

View File

@@ -0,0 +1,336 @@
import {
Avatar,
Badge,
Box,
Button,
HStack,
Input,
Link,
Text,
Tooltip,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useState } from "react";
import { CheckIcon, CloseIcon, ExternalLinkIcon } from "@chakra-ui/icons";
import Pagination from "../../../Components/Pagination";
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import DrawalRequestReject from "../../WithDrawal/DrawalRequest/DrawalRequestReject";
import NormalTable from "../../../Components/DataTable/NormalTable";
import DrawalRequestApprove from "../../WithDrawal/DrawalRequest/DrawalRequestApprove";
import { generateSerialNumber } from "../../../Constants/Constants";
import {
useGetApproveHistoryQuery,
useGetFawateerRequestQuery,
} from "../../../Services/fawateer.request.service";
import { TABLE_PAGINATION } from "../../../Constants/Paginations";
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
const ApproveHistory = () => {
const toast = useToast();
const { slideFromRight, approveHistory, setApproveHistory } =
useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
const formatDate = (date) => {
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
};
const {
isOpen: isConfirmOpen,
onOpen: onConfirmOpen,
onClose: onConfirmClose,
} = useDisclosure();
const {
isOpen: isRejectOpen,
onOpen: onRejectOpen,
onClose: onRejectClose,
} = useDisclosure();
const {
data,
isLoading: drawalRequestLoading,
error,
refetch,
} = useGetApproveHistoryQuery();
console.log(data?.data?.rows);
// Use useEffect to refetch data when the component mounts
useEffect(() => {
refetch();
}, [refetch]);
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
// Cleanup the timer on component unmount
return () => clearTimeout(timer);
}, []);
// ====================================================[Table Filter]================================================================
const filteredData = data?.data?.rows?.filter((item) => {
// Filter by name (case insensitive)
const name = item.firstName;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
// Filter by status
// const status = item.status;
// const statusLower = status ? "active" : "inactive";
// const statusMatches =
// statusFilter === "all" ||
// (statusFilter === "active" && status === true) ||
// (statusFilter === "inactive" && status === false);
return nameMatches;
});
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
"Sr.no",
"Client ID",
"First Name",
"Last Name",
"E-mail ID",
"Phone Number",
"Deposit Date",
"Deposit Amount (BHD)",
"Support Image",
"Status",
];
const extractedArray = filteredData?.map((item, idx) => ({
// id: item?.id,
"Sr.no": (
<Text
w={"auto"}
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{generateSerialNumber(idx, currentPage, pageSize)}
</Text>
),
"Client ID": (
<Text
w={"60px"}
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.clientReference_id}
</Text>
),
"First Name": (
<Box isTruncated={true} w={"80px"}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item.firstName}
</Text>
</Box>
),
"Last Name": (
<Box w={"50px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.lastName}
</Text>
</Box>
),
"E-mail ID": (
<Box isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.emailAddress}
</Text>
</Box>
),
"Phone Number": (
<Box w={"100px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.mobileNumber}
</Text>
</Box>
),
"Deposit Date": (
<Box
w={"100px"}
isTruncated={true}
display={"flex"}
>
<Text as={"span"} color={"teal.900"}>
{formatDate(item?.transaction_date)}
</Text>
</Box>
),
"Deposit Amount (BHD)": (
<Box w={"130px"} isTruncated={true} display={"flex"}>
<Text as={"span"} color={"teal.900"}>
{/* {item.investorAmount} */}
{parseFloat(item?.transaction_amount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
{/* <Badge ms={1} colorScheme="green">{item?.transaction_amount}</Badge> */}
</Text>
</Box>
),
"Support Image": (
<Text
color={"green.500"}
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item?.spportFile_path&&<Badge
px={2}
py={0.5}
textTransform={"inherit"}
fontWeight={500}
colorScheme={"forestGreen"}
>
<Link
href={import.meta.env.VITE_IMAGE_URL + item?.spportFile_path}
isExternal
display={"flex"}
alignItems={"center"}
>
<Box me={"1px"}
as="span"
cursor={"pointer"}
>
View
</Box>
<ExternalLinkIcon />
</Link>
</Badge>}
</Text>
),
Status: (
<Box isTruncated={true} display={"flex"}>
<Badge
my={1}
fontWeight={500}
px={2}
py={"2px"}
rounded={4}
colorScheme={
item?.transactionStatus === "Approved"
? "green"
: item?.transactionStatus === "Pending"
? "yellow"
: item?.transactionStatus === "Reject"
? "red"
: "gray" // default border color if status doesn't match any condition
}
>
{item.transactionStatus}
</Badge>
</Box>
),
}));
const handleDelete = () => {
const updatedSponsors = sponser.filter(
(sponsor) => sponsor.id !== actionId
);
setTimeout(() => {
setSponser(updatedSponsors);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
<Box bg="white.500">
<HStack
display={"flex"}
justifyContent={"space-between"}
ps={1}
pe={1}
pb={4}
pt={4}
spacing="24px"
>
<Input
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<Pagination
isLoading={drawalRequestLoading}
pageSize={pageSize}
setPageSize={setPageSize}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
totalItems={data?.data?.totalItems}
/>
</HStack>
</Box>
<NormalTable
isLoading={drawalRequestLoading}
emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow}
data={extractedArray}
viewActionId={actionId}
setViewActionId={setActionId}
// totalPages={10}
setMouseEnteredId={setMouseEnteredId}
setMouseEntered={setMouseEntered}
/>
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>
<DrawalRequestApprove
// data={data?.data?.rows}
isOpen={isConfirmOpen}
onClose={onConfirmClose}
id={actionId}
// firstField={firstField}
/>
<DrawalRequestReject
isOpen={isRejectOpen}
onClose={onRejectClose}
id={actionId}
/>
</Box>
);
};
export default ApproveHistory;

View File

@@ -0,0 +1,358 @@
import {
Avatar,
Badge,
Box,
Button,
HStack,
Input,
Link,
Text,
Tooltip,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useState } from "react";
import { CheckIcon, CloseIcon, ExternalLinkIcon } from "@chakra-ui/icons";
import Pagination from "../../../Components/Pagination";
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import DrawalRequestReject from "../../WithDrawal/DrawalRequest/DrawalRequestReject";
import NormalTable from "../../../Components/DataTable/NormalTable";
import DrawalRequestApprove from "../../WithDrawal/DrawalRequest/DrawalRequestApprove";
import { generateSerialNumber } from "../../../Constants/Constants";
import {
useGetApproveHistoryQuery,
useGetFawateerForMakerRequestQuery,
useGetFawateerRequestQuery,
} from "../../../Services/fawateer.request.service";
import { TABLE_PAGINATION } from "../../../Constants/Paginations";
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
const ApproveHistoryMaker = () => {
const toast = useToast();
const { slideFromRight, approveHistory, setApproveHistory } =
useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
const formatDate = (date) => {
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
};
const {
isOpen: isConfirmOpen,
onOpen: onConfirmOpen,
onClose: onConfirmClose,
} = useDisclosure();
const {
isOpen: isRejectOpen,
onOpen: onRejectOpen,
onClose: onRejectClose,
} = useDisclosure();
const {
data,
isLoading: drawalRequestLoading,
error,
refetch
} = useGetFawateerForMakerRequestQuery(
{
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
searchTerm: debouncedSearchTerm,
},
{
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
}
);
console.log(data);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedSearchTerm(searchTerm);
}, 500); // Adjust delay as needed
return () => {
clearTimeout(handler);
};
}, [searchTerm]);
// Use useEffect to refetch data when the component mounts
useEffect(() => {
refetch();
}, [refetch]);
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
// Cleanup the timer on component unmount
return () => clearTimeout(timer);
}, []);
// ====================================================[Table Filter]================================================================
const filteredData = data?.data?.rows?.filter((item) => {
// Filter by name (case insensitive)
const name = item.firstName;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
// Filter by status
// const status = item.status;
// const statusLower = status ? "active" : "inactive";
// const statusMatches =
// statusFilter === "all" ||
// (statusFilter === "active" && status === true) ||
// (statusFilter === "inactive" && status === false);
return nameMatches;
});
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
"Sr.no",
"Client ID",
"First Name",
"Last Name",
"E-mail ID",
"Phone Number",
"Deposit Date",
"Deposit Amount (BHD)",
"Support Image",
"Status",
];
const extractedArray = data?.data?.rows?.map((item, idx) => ({
// id: item?.id,
"Sr.no": (
<Text
w={"auto"}
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{generateSerialNumber(idx, currentPage, pageSize)}
</Text>
),
"Client ID": (
<Text
w={"60px"}
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.clientReference_id}
</Text>
),
"First Name": (
<Box isTruncated={true} w={"80px"}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item.firstName}
</Text>
</Box>
),
"Last Name": (
<Box w={"50px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.lastName}
</Text>
</Box>
),
"E-mail ID": (
<Box isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.emailAddress}
</Text>
</Box>
),
"Phone Number": (
<Box w={"100px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.mobileNumber}
</Text>
</Box>
),
"Deposit Date": (
<Box
w={"100px"}
isTruncated={true}
display={"flex"}
>
<Text as={"span"} color={"teal.900"}>
{formatDate(item?.transaction_date)}
</Text>
</Box>
),
"Deposit Amount (BHD)": (
<Box w={"130px"} isTruncated={true} display={"flex"}>
<Text as={"span"} color={"teal.900"}>
{/* {item.investorAmount} */}
{parseFloat(item?.transaction_amount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
{/* <Badge ms={1} colorScheme="green">{item?.transaction_amount}</Badge> */}
</Text>
</Box>
),
"Support Image": (
<Text
color={"green.500"}
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item?.spportFile_path&&<Badge
px={2}
py={0.5}
textTransform={"inherit"}
fontWeight={500}
colorScheme={"forestGreen"}
>
<Link
href={import.meta.env.VITE_IMAGE_URL + item?.spportFile_path}
isExternal
display={"flex"}
alignItems={"center"}
>
<Box me={"1px"}
as="span"
cursor={"pointer"}
>
View
</Box>
<ExternalLinkIcon />
</Link>
</Badge>}
</Text>
),
Status: (
<Box isTruncated={true} display={"flex"}>
<Badge
my={1}
fontWeight={500}
px={2}
py={"2px"}
rounded={4}
colorScheme={
item?.transactionStatus === "Approved"
? "green"
: item?.transactionStatus === "Pending"
? "yellow"
: item?.transactionStatus === "Reject"
? "red"
: "gray" // default border color if status doesn't match any condition
}
>
{item.transactionStatus}
</Badge>
</Box>
),
}));
const handleDelete = () => {
const updatedSponsors = sponser.filter(
(sponsor) => sponsor.id !== actionId
);
setTimeout(() => {
setSponser(updatedSponsors);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
<Box bg="white.500">
<HStack
display={"flex"}
justifyContent={"space-between"}
ps={1}
pe={1}
pb={4}
pt={4}
spacing="24px"
>
<Input
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<Pagination
isLoading={drawalRequestLoading}
pageSize={pageSize}
setPageSize={setPageSize}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
totalItems={data?.data?.totalItems}
/>
</HStack>
</Box>
<NormalTable
isLoading={drawalRequestLoading}
emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow}
data={extractedArray}
viewActionId={actionId}
setViewActionId={setActionId}
// totalPages={10}
setMouseEnteredId={setMouseEnteredId}
setMouseEntered={setMouseEntered}
/>
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>
<DrawalRequestApprove
// data={data?.data?.rows}
isOpen={isConfirmOpen}
onClose={onConfirmClose}
id={actionId}
// firstField={firstField}
/>
<DrawalRequestReject
isOpen={isRejectOpen}
onClose={onRejectClose}
id={actionId}
/>
</Box>
);
};
export default ApproveHistoryMaker;

View File

@@ -0,0 +1,95 @@
import {
Box,
Button,
FormControl,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
useDisclosure,
} from "@chakra-ui/react";
import React from "react";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
export const conformModalSchema = yup.object().shape({
fees: yup.string().required("File name is required"),
totalAmount: yup.string().required("File name is required"),
});
const ConfirmHistory = ({ isOpen, onClose, firstField }) => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
const onSubmit = (data) => {
console.log(data);
setFile(data.document[0]);
const newDocument = {
...data,
document: data.document[0].name, // Store the document name
status: true,
id: uuidv4(),
createdAt: new Date().toISOString(),
Type: getFileIcon(file.type),
};
setCreate((prevCreate) => [...prevCreate, newDocument]);
onClose();
};
const handleFileChange = (event) => {
const selectedFile = event.target.files[0];
setFile(selectedFile);
};
return (
<Modal isOpen={isOpen} onClose={onClose} initialFocusRef={firstField}>
<ModalOverlay />
<ModalContent pb={4}>
<ModalHeader fontSize={"md"}>Confirm</ModalHeader>
<ModalCloseButton />
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody>
<FormControl mb={4}>
<FormLabel fontSize="sm">Withdrawal Amount</FormLabel>
<Input
focusBorderColor='green.400'
name="fileName"
{...register("fileName")}
fontSize="sm"
type="text"
size="sm"
placeholder={"$100,000"}
readOnly
/>
</FormControl>
</ModalBody>
<ModalFooter>
<Button colorScheme="gray" mr={3} onClick={onClose} size={'sm'} rounded={'sm'}>
Cancel
</Button>
<Button colorScheme="forestGreen" variant="solid" size={'sm'} rounded={'sm'}>
Confirm
</Button>
</ModalFooter>
</Box>
</ModalContent>
</Modal>
);
};
export default ConfirmHistory;

View File

@@ -0,0 +1,100 @@
import {
Box,
Button,
FormControl,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
useDisclosure,
} from "@chakra-ui/react";
import React from "react";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
export const conformModalSchema = yup.object().shape({
comment: yup.string().required("Comment is required"),
});
const RejectHistory = ({ isOpen, onClose, firstField }) => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
const onSubmit = (data) => {
console.log(data);
setFile(data.document[0]);
const newDocument = {
...data,
document: data.document[0].name, // Store the document name
comment: true,
id: uuidv4(),
Type: getFileIcon(file.type),
};
setCreate((prevCreate) => [...prevCreate, newDocument]);
onClose();
};
const handleFileChange = (event) => {
const selectedFile = event.target.files[0];
setFile(selectedFile);
};
return (
<Modal isOpen={isOpen} onClose={onClose} initialFocusRef={firstField}>
<ModalOverlay />
<ModalContent pb={4}>
<ModalHeader fontSize={"md"}>Reject</ModalHeader>
<ModalCloseButton />
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody>
<FormControl mb={4}>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea rows={6}
focusBorderColor='green.400'
name="fileName"
{...register("fileName")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"$100,000"}
rounded={'md'}
resize={'none'}
/>
{errors.comment && (
<Text fontSize="xs" color="red">
{errors.comment.message}
</Text>
)}
</FormControl>
</ModalBody>
<ModalFooter>
<Button colorScheme="gray" mr={3} onClick={onClose} size={'sm'} rounded={'sm'}>
Cancel
</Button>
<Button colorScheme="forestGreen" variant="solid" size={'sm'} rounded={'sm'}>
Send
</Button>
</ModalFooter>
</Box>
</ModalContent>
</Modal>
);
};
export default RejectHistory;

View File

@@ -0,0 +1,416 @@
import {
Avatar,
Badge,
Box,
Button,
HStack,
Input,
Link,
Text,
Tooltip,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useState } from "react";
import { CheckIcon, CloseIcon, ExternalLinkIcon } from "@chakra-ui/icons";
import Pagination from "../../../Components/Pagination";
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import NormalTable from "../../../Components/DataTable/NormalTable";
import { generateSerialNumber } from "../../../Constants/Constants";
import { useGetFawateerRequestQuery } from "../../../Services/fawateer.request.service";
import { TABLE_PAGINATION } from "../../../Constants/Paginations";
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
import RequestApproveModal from "./RequestApproveModal";
import RequestRejectModal from "./RequestRejectModal";
const ApproveRequest = () => {
const toast = useToast();
const { slideFromRight, fawateerRequest, setFawateerRequest } =
useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
const formatDate = (date) => {
return new Date(date).toLocaleDateString('en-GB', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
});
};
// Debounce the search term to avoid making a request on every keystroke
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedSearchTerm(searchTerm);
}, 500); // Adjust delay as needed
return () => {
clearTimeout(handler);
};
}, [searchTerm]);
const {
isOpen: isConfirmOpen,
onOpen: onConfirmOpen,
onClose: onConfirmClose,
} = useDisclosure();
const {
isOpen: isRejectOpen,
onOpen: onRejectOpen,
onClose: onRejectClose,
} = useDisclosure();
const {
data,
isLoading: drawalRequestLoading,
error,
refetch
} = useGetFawateerRequestQuery(
{
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
searchTerm: debouncedSearchTerm,
},
{
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
}
);
console.log(data?.data?.rows);
// Use useEffect to refetch data when the component mounts
useEffect(() => {
refetch();
}, [refetch]);
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
// Cleanup the timer on component unmount
return () => clearTimeout(timer);
}, []);
// ====================================================[Table Filter]================================================================
const filteredData = data?.data?.rows?.filter((item) => {
// Filter by name (case insensitive)
const name = item.firstName;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
// Filter by status
// const status = item.status;
// const statusLower = status ? "active" : "inactive";
// const statusMatches =
// statusFilter === "all" ||
// (statusFilter === "active" && status === true) ||
// (statusFilter === "inactive" && status === false);
return nameMatches;
});
const role = localStorage?.getItem('role')
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
"Sr.no",
"Client ID",
"First Name",
"Last Name",
"E-mail ID",
"Phone Number",
"Deposit Date",
"Deposit Amount (BHD)",
"Support Image",
"Action",
];
const extractedArray = filteredData?.map((item, idx) => ({
// id: item?.id,
"Sr.no": (
<Text
w={"auto"}
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{generateSerialNumber(idx,currentPage, pageSize )}
</Text>
),
"Client ID": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.clientReference_id}
</Text>
),
"First Name": (
<Box isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item.firstName}
</Text>
</Box>
),
"Last Name": (
<Box isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.lastName}
</Text>
</Box>
),
"E-mail ID": (
<Box isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.emailAddress}
</Text>
</Box>
),
"Phone Number": (
<Box w={"100px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.mobileNumber}
</Text>
</Box>
),
"Deposit Date": (
<Box isTruncated={true} display={'flex'} >
<Text as={"span"} color={"teal.900"}>
{formatDate(item?.transaction_date)}
</Text>
</Box>
),
"Deposit Amount (BHD)": (
<Box isTruncated={true} display={'flex'} >
<Text as={"span"} color={"teal.900"}>
{/* {item.investorAmount} */}
{parseFloat(item?.transaction_amount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
{/* <Badge ms={1} colorScheme="green">{item?.transaction_amount}</Badge> */}
</Text>
</Box>
),
"Support Image": (
<Text
color={"green.500"}
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
<Badge
px={2}
py={0.5}
textTransform={"inherit"}
fontWeight={500}
colorScheme={"forestGreen"}
>
<Link
href={import.meta.env.VITE_IMAGE_URL + item?.spportFile_path}
isExternal
display={"flex"}
alignItems={"center"}
>
<Box me={"1px"}
as="span"
cursor={"pointer"}
>
View
</Box>
<ExternalLinkIcon />
</Link>
</Badge>
</Text>
),
"Status": (
<Box isTruncated={true} display={'flex'} >
<Badge
my={1}
fontWeight={500}
px={2}
py={"2px"}
rounded={4}
colorScheme={
item?.transactionStatus === "Approved"
? "green"
: item?.transactionStatus === "Pending"
? "yellow"
: item?.transactionStatus === "Reject"
? "red"
: "gray" // default border color if status doesn't match any condition
}
>
{item.transactionStatus}
</Badge>
</Box>
),
Action: (
<Box display={"flex"} justifyContent={"center"} gap={2}>
<Tooltip
rounded={"sm"}
fontSize={"xs"}
label="Approve"
bg="#fff"
color={"green.500"}
placement="left-start"
>
<Button
// colorScheme="forestGreen"
// color="green.500"
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={2}
py={1}
onClick={() => {
setActionId(item.id);
onConfirmOpen();
}}
colorScheme="green"
variant={"solid"}
cursor={"pointer"}
>
<CheckIcon fontSize={"12px"} />
</Button>
</Tooltip>
<Tooltip
rounded={"sm"}
fontSize={"xs"}
label="Reject"
bg="#fff"
color={"red.500"}
placement="left-start"
>
<Button
colorScheme="red"
// color="red.500"
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={2}
onClick={() => {
setActionId(item.id);
onRejectOpen();
}}
py={1}
// variant={"solid"}
>
<CloseIcon fontSize={"10px"} />
</Button>
</Tooltip>
</Box>
),
}));
const handleDelete = () => {
const updatedSponsors = sponser.filter(
(sponsor) => sponsor.id !== actionId
);
setTimeout(() => {
setSponser(updatedSponsors);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
<Box bg="white.500">
<HStack
display={"flex"}
justifyContent={"space-between"}
ps={1}
pe={1}
pb={4}
pt={4}
spacing="24px"
>
<Input
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<Pagination
isLoading={drawalRequestLoading}
pageSize={pageSize}
setPageSize={setPageSize}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
totalItems={data?.data?.totalItems}
/>
</HStack>
</Box>
<NormalTable
isLoading={drawalRequestLoading}
emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow}
data={extractedArray}
viewActionId={actionId}
setViewActionId={setActionId}
// totalPages={10}
setMouseEnteredId={setMouseEnteredId}
setMouseEntered={setMouseEntered}
/>
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>
<RequestApproveModal
// data={data?.data?.rows}
isOpen={isConfirmOpen}
onClose={onConfirmClose}
id={actionId}
// firstField={firstField}
/>
<RequestRejectModal
isOpen={isRejectOpen}
onClose={onRejectClose}
id={actionId}
/>
</Box>
);
};
export default ApproveRequest;

View File

@@ -0,0 +1,177 @@
import {
Box,
Button,
FormControl,
FormHelperText,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useEffect, useState } from "react";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
import { useDepositRejectMutation } from "../../../Services/deposit.request.service";
import ToastBox from "../../../Components/ToastBox";
import { useApproveCommentMutation } from "../../../Services/fawateer.request.service";
export const conformModalSchema = yup.object().shape({
// checkerComment: yup.string().required("Comment is required")
// .max(50, "Investment name cannot be more than 50 characters"),
checkerComment: yup
.string()
.required("Comment is required")
.max(200, "Approve Comment cannot be more than 200 characters"),
});
const RequestApproveModal = ({ isOpen, onClose, firstField ,id}) => {
const [isBtnLoading , setIsBtnLoading] = useState(false)
const toast = useToast()
const {
register,
reset,
watch,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
const [ approveFawateer ] = useApproveCommentMutation()
const onSubmit = async(data) => {
console.log(data, "tewxttttt");
setIsBtnLoading(true)
try {
const res = await approveFawateer({data,id})
if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsBtnLoading(false)
}else if(res?.data){
toast({
render: () => (
<ToastBox message={res?.data?.message} />
),
});
onClose()
setIsBtnLoading(false)
}else{
toast({
render: () => (
<ToastBox status={'error'} message={"Something went wrong"} />
),
});
setIsBtnLoading(false)
}
} catch (error) {
}
};
const handleFileChange = (event) => {
const selectedFile = event.target.files[0];
setFile(selectedFile);
};
const { data, isLoading } =
(id, {
skip: !id,
});
useEffect(() => {
if (data) {
reset({
investorAmount: data?.data?.investorAmount,
});
}
}, [data, reset]);
const heandleOnClose = () =>{
reset()
onClose()
}
return (
<Modal isCentered isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
<ModalOverlay />
<ModalContent pb={4}>
<ModalHeader fontSize={"md"}>Approve Comment</ModalHeader>
<ModalCloseButton />
{isLoading ? (
<FullscreenLoaders height={"50vh"} />
) : (
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea
rows={6}
focusBorderColor="green.400"
name="checkerComment"
{...register("checkerComment")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your checkerComment...."}
rounded={"md"}
resize={"none"}
maxLength={200}
/>
{errors.checkerComment && (
<Text fontSize="xs" color="red">
{errors.checkerComment.message}
</Text>
)}
<FormHelperText fontSize="xs" color="gray.500">
Maximum length should be 200 characters. You have entered
{watch("checkerComment")?.length || 0} characters.
</FormHelperText>
</FormControl>
</ModalBody>
<ModalFooter>
<Button
colorScheme="gray"
mr={3}
onClick={onClose}
size={"sm"}
rounded={"sm"}
>
Cancel
</Button>
<Button
colorScheme="forestGreen"
variant="solid"
size={"sm"}
rounded={"sm"}
isLoading={isBtnLoading}
type="submit"
>
Send
</Button>
</ModalFooter>
</Box>
)}
</ModalContent>
</Modal>
);
};
export default RequestApproveModal;

View File

@@ -0,0 +1,165 @@
import {
Box,
Button,
FormControl,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useEffect, useState } from "react";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
import { useDepositRejectMutation } from "../../../Services/deposit.request.service";
import ToastBox from "../../../Components/ToastBox";
import { useRejectCommentMutation } from "../../../Services/fawateer.request.service";
export const conformModalSchema = yup.object().shape({
comments: yup.string().required("Comment is required"),
});
const RequestRejectModal = ({ isOpen, onClose, firstField ,id}) => {
const [isBtnLoading , setIsBtnLoading] = useState(false)
const toast = useToast()
const {
register,
reset,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
const [ rejectFawateer ] = useRejectCommentMutation()
const onSubmit = async(data) => {
console.log(data, "tewxttttt");
setIsBtnLoading(true)
try {
const res = await rejectFawateer({data,id})
if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsBtnLoading(false)
}else if(res?.data){
toast({
render: () => (
<ToastBox message={res?.data?.message} />
),
});
onClose()
setIsBtnLoading(false)
}else{
toast({
render: () => (
<ToastBox status={'error'} message={"Something went wrong"} />
),
});
setIsBtnLoading(false)
}
} catch (error) {
}
};
const handleFileChange = (event) => {
const selectedFile = event.target.files[0];
setFile(selectedFile);
};
const { data, isLoading } =
(id, {
skip: !id,
});
useEffect(() => {
if (data) {
reset({
investorAmount: data?.data?.investorAmount,
});
}
}, [data, reset]);
const heandleOnClose = () =>{
reset()
onClose()
}
return (
<Modal isCentered isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
<ModalOverlay />
<ModalContent pb={4}>
<ModalHeader fontSize={"md"}>Reject Comment</ModalHeader>
<ModalCloseButton />
{isLoading ? (
<FullscreenLoaders height={"50vh"} />
) : (
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea
rows={6}
focusBorderColor="green.400"
name="comments"
{...register("comments")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your comments...."}
rounded={"md"}
resize={"none"}
/>
{errors.comments && (
<Text fontSize="xs" color="red">
{errors.comments.message}
</Text>
)}
</FormControl>
</ModalBody>
<ModalFooter>
<Button
colorScheme="gray"
mr={3}
onClick={onClose}
size={"sm"}
rounded={"sm"}
>
Cancel
</Button>
<Button
colorScheme="forestGreen"
variant="solid"
size={"sm"}
rounded={"sm"}
isLoading={isBtnLoading}
type="submit"
>
Send
</Button>
</ModalFooter>
</Box>
)}
</ModalContent>
</Modal>
);
};
export default RequestRejectModal;

View File

@@ -0,0 +1,253 @@
import {
Box,
Button,
Drawer,
DrawerBody,
DrawerCloseButton,
DrawerContent,
DrawerFooter,
DrawerHeader,
DrawerOverlay,
FormControl,
FormErrorMessage,
FormLabel,
Input,
Select,
Stack,
Textarea,
useToast,
} from "@chakra-ui/react";
import * as yup from "yup";
import React, { useState, useEffect, useContext } from "react";
import { useForm, Controller } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import { v4 as uuidv4 } from "uuid";
import { useCreateIoCashMutation, useCreateVideoArtifactsMutation, useUpdateVideoArtifactsMutation } from "../../../Services/io.service";
import { useParams } from "react-router-dom";
import ToastBox from "../../../Components/ToastBox";
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import CurrencyInput from "../../../Components/CurrencyInput";
const cashDetails = yup.object().shape({
transactionDate: yup.string().required("Date is required"),
ioTransType_xid: yup.number().required("Cash transaction is required"),
transactionAmount: yup.number().required("Transaction Amount is required"),
comments: yup.string().notRequired(),
});
const AddCashDetails = ({ isOpen, onClose, firstField, actionId, setActionId, data }) => {
const params = useParams()
const id = params?.id
const [file, setFile] = useState("");
const [fileName, setFileName] = useState("");
const [isLoading, setIsLoading] = useState(false)
const [alert, setAlert] = useState(false);
const toast = useToast();
// ======================[ Cotext Api ]
const { IODetails } = useContext(GlobalStateContext);
const found = data?.find((item) => item?.id === actionId);
const [createArtifactsVideo] = useCreateVideoArtifactsMutation()
const [updateVideoArtifacts] = useUpdateVideoArtifactsMutation()
// const {
// data
// } = useGetArtifactsQuery(id)
const {
control,
handleSubmit,
watch,
reset,
formState: { errors },
} = useForm({
resolver: yupResolver(cashDetails),
});
const [createIoCash] = useCreateIoCashMutation()
const onSubmit = async (data) => {
setIsLoading(true)
try {
const res = await createIoCash({ data, id })
if (res?.data?.statusCode === 200) {
setIsLoading(false);
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
handleClose()
}else if(res?.error?.status === 400){
setIsLoading(false);
toast({
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
});
}
} catch (error) {
console.log(error);
setIsLoading(false);
}
};
const handleConfirm = () => {
handleSubmit(onSubmit)();
};
const handleSave = () => {
handleSubmit(onSubmit)();
};
const handleClose = () => {
setAlert(false)
onClose()
reset({
transactionAmount:""
})
}
return (
<>
<Drawer
size={"md"}
isOpen={isOpen}
placement="right"
initialFocusRef={firstField}
onClose={handleClose}
>
<DrawerOverlay />
<DrawerContent>
<DrawerCloseButton />
<DrawerHeader fontSize={"sm"}>IO Cash Details</DrawerHeader>
<DrawerBody>
<Stack spacing={4}>
<FormControl isInvalid={errors.transactionDate} isRequired>
<FormLabel fontSize={"sm"}>Date Selection</FormLabel>
<Controller
name="transactionDate"
control={control}
render={({ field }) => (
<Input
focusBorderColor="forestGreen.300" {...field} fontSize={"sm"} type="date" size={"sm"} />
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.transactionDate?.message}
</FormErrorMessage>
</FormControl>
<FormControl isInvalid={errors.ioTransType_xid} isRequired>
<FormLabel fontSize={"sm"}>Cash transaction</FormLabel>
<Controller
name="ioTransType_xid"
control={control}
render={({ field }) => (
<Select
{...field}
placeholder="Select an option"
fontSize={"sm"}
size={"sm"}
focusBorderColor="forestGreen.300"
>
{IODetails?.ioCashTransaction?.map(({ id, transactionName }) => (
<option key={id} value={id}>
{transactionName}
</option>
))}
</Select>
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.ioTransType_xid?.message}
</FormErrorMessage>
</FormControl>
<FormControl isInvalid={errors.transactionAmount} isRequired>
<FormLabel fontSize={"sm"}>Transaction Amount</FormLabel>
<Controller
name="transactionAmount"
control={control}
render={({ field }) => (
<CurrencyInput {...field} textAlign={'right'} fontSize={"sm"} type="number" size={"sm"} />
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.transactionAmount?.message}
</FormErrorMessage>
</FormControl>
<FormControl isInvalid={errors.comments}>
<FormLabel fontSize={"sm"}>Comments</FormLabel>
<Controller
name="comments"
control={control}
render={({ field }) => (
<Textarea {...field} textAlign={'left'}
focusBorderColor="forestGreen.300" fontSize={"sm"} type="text" size={"sm"} />
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.comments?.message}
</FormErrorMessage>
</FormControl>
</Stack>
</DrawerBody>
<DrawerFooter>
<Button
variant="outline"
colorScheme={"forestGreen"}
rounded={"sm"}
size={"sm"}
mr={3}
onClick={handleClose}
>
Cancel
</Button>
<Button
colorScheme={"forestGreen"}
rounded={"sm"}
size={"sm"}
onClick={() => setAlert(true)}
>
Save
</Button>
</DrawerFooter>
</DrawerContent>
</Drawer>
<CustomAlertDialog
isOpen={alert}
onClose={() => setAlert(false)}
alertHandler={handleSave}
message={"Are you sure you want to add cash details?"}
isLoading={isLoading}
/>
</>
);
};
export default AddCashDetails;

View File

@@ -0,0 +1,265 @@
import {
Box,
Button,
Drawer,
DrawerBody,
DrawerCloseButton,
DrawerContent,
DrawerFooter,
DrawerHeader,
DrawerOverlay,
FormControl,
FormErrorMessage,
FormLabel,
HStack,
Input,
Select,
Stack,
Text,
Textarea,
VStack,
useToast,
} from "@chakra-ui/react";
import * as yup from "yup";
import React, { useState, useEffect, useContext } from "react";
import { useForm, Controller } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import { v4 as uuidv4 } from "uuid";
import { useCreateIoCashMutation, useCreateIoNavMutation, useCreateVideoArtifactsMutation, useUpdateVideoArtifactsMutation } from "../../../Services/io.service";
import { useParams } from "react-router-dom";
import ToastBox from "../../../Components/ToastBox";
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import CurrencyInput from "../../../Components/CurrencyInput";
import { formatDatee } from "../../../Components/FormField";
const ioNav = yup.object().shape({
transactionDate: yup.string().required("Date is required"),
transactionAmount: yup.number().required("New NAV is required"),
comments: yup.string().notRequired(),
});
const AddIONav = ({ isOpen, onClose, firstField, actionId, setActionId, data }) => {
const params = useParams()
const id = params?.id
const [file, setFile] = useState("");
const [fileName, setFileName] = useState("");
const [isLoading, setIsLoading] = useState(false)
const [alert, setAlert] = useState(false);
const toast = useToast();
// ======================[ Cotext Api ]
const { IODetails } = useContext(GlobalStateContext);
const found = data?.find((item) => item?.id === actionId);
const [createIoNav] = useCreateIoNavMutation()
// const {
// data
// } = useGetArtifactsQuery(id)
const {
control,
handleSubmit,
watch,
reset,
formState: { errors },
} = useForm({
resolver: yupResolver(ioNav),
});
const [createIoCash] = useCreateIoCashMutation()
const onSubmit = async (data) => {
setIsLoading(true)
try {
const res = await createIoNav({ data, id })
if (res?.data?.statusCode === 201) {
setIsLoading(false);
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
handleClose()
}else if(res?.error?.status === 400){
toast({
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
});
handleClose()
}
} catch (error) {
console.log(error);
}
};
const handleConfirm = () => {
handleSubmit(onSubmit)();
};
const handleSave = () => {
handleSubmit(onSubmit)();
};
const handleClose = () => {
setIsLoading(false);
setAlert(false)
onClose()
reset({
transactionDate:"",
transactionAmount:"",
comments:""
})
}
const today = formatDatee(new Date(), 'yyyy-MM-dd');
function calculatePercentage(newNav, currNav) {
const per = (newNav - currNav) / currNav * 100
return per.toFixed(2)
}
console.log(calculatePercentage(1092500, 976070));
return (
<>
<Drawer
size={"md"}
isOpen={isOpen}
placement="right"
initialFocusRef={firstField}
onClose={handleClose}
>
<DrawerOverlay />
<DrawerContent>
<DrawerCloseButton />
<DrawerHeader fontSize={"sm"}>IO Nav Details</DrawerHeader>
<DrawerBody>
<Stack spacing={4}>
<FormControl isInvalid={errors.transactionDate} isRequired>
<FormLabel fontSize={"sm"}>Date Selection</FormLabel>
<Controller
name="transactionDate"
control={control}
render={({ field }) => (
<Input {...field}
max={today} // Set max attribute to todays date
fontSize={"sm"} type="date" size={"sm"} />
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.transactionDate?.message}
</FormErrorMessage>
</FormControl>
<FormControl isInvalid={errors.transactionAmount} isRequired>
<FormLabel fontSize={"sm"}>New NAV</FormLabel>
<Controller
name="transactionAmount"
control={control}
render={({ field }) => (
<CurrencyInput {...field} textAlign={'right'} fontSize={"sm"} type="number" size={"sm"} />
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.transactionAmount?.message}
</FormErrorMessage>
</FormControl>
<HStack justify={'start'} gap={10} bg={'green.100'} p={3} rounded={'md'} shadow={'md'}>
<VStack align={'start'}>
<Text as={'span'} fontSize={'sm'} fontWeight={500}>Current nav</Text>
<Text as={'span'} fontSize={'sm'}>
{parseFloat(IODetails?.ioNAV || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
</VStack>
<VStack align={'start'}>
<Text as={'span'} fontSize={'sm'} fontWeight={500}>Live return %</Text>
<Text as={'span'} fontSize={'sm'}>{calculatePercentage(watch()?.transactionAmount||IODetails?.ioNAV,IODetails?.ioNAV)}</Text>
</VStack>
</HStack>
<FormControl isInvalid={errors.comments}>
<FormLabel fontSize={"sm"}>Comments</FormLabel>
<Controller
name="comments"
control={control}
render={({ field }) => (
<Textarea {...field} fontSize={"sm"} type="text" size={"sm"} />
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.comments?.message}
</FormErrorMessage>
</FormControl>
</Stack>
</DrawerBody>
<DrawerFooter>
<Button
variant="outline"
colorScheme={"forestGreen"}
rounded={"sm"}
size={"sm"}
mr={3}
onClick={handleClose}
>
Cancel
</Button>
<Button
colorScheme={"forestGreen"}
rounded={"sm"}
size={"sm"}
onClick={() => setAlert(true)}
>
Save
</Button>
</DrawerFooter>
</DrawerContent>
</Drawer>
<CustomAlertDialog
isOpen={alert}
onClose={() => setAlert(false)}
alertHandler={handleSave}
message={"Are you sure you want to add NAV details?"}
isLoading={isLoading}
/>
</>
);
};
export default AddIONav;

View File

@@ -8,20 +8,22 @@ import IODetails from "./IODetails";
import KeyMerits from "./KeyMerits"; import KeyMerits from "./KeyMerits";
import IOArtifacts from "./IOArtifacts"; import IOArtifacts from "./IOArtifacts";
import Investors from "./Investors"; import Investors from "./Investors";
import IOCashDetails from "./IOCashDetails"; // import IOCashDetails from "./IOCashDetailsold";
import IONAVDetails from "./IONAVDetails"; // import IONAVDetails from "./IONAVDetailsOld";
import InvestmentDocument from "./InvestmentDocument"; // Ensure this is the correct import import InvestmentDocument from "./InvestmentDocument"; // Ensure this is the correct import
import ViewIOdataHeader from "../ViewIO/ViewIOdataHeader"; import ViewIOdataHeader from "../ViewIO/ViewIOdataHeader";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import FullscreenLoaders from "../../../Components/Loaders/FullscreenLoaders"; import FullscreenLoaders from "../../../Components/Loaders/FullscreenLoaders";
import { useGetIOprepopulateDataQuery } from "../../../Services/io.service"; import { useGetIOprepopulateDataQuery } from "../../../Services/io.service";
import UnderConstruction from "../../UnderConstruction";
import Destribution from "./Destribution";
import IOCashDetails from "./IOCashDetails/IOCashDetails";
import IONAVDetails from "./IONAVDetails/IONAVDetails";
import IOTransaction from "./IOTransaction/IOTransaction";
const CreateIO = () => { const CreateIO = () => {
const id = useParams()?.id; const id = useParams()?.id;
const { data, error, isLoading } = useGetIOprepopulateDataQuery(); const { data, error, isLoading } = useGetIOprepopulateDataQuery();
// console.log(data?.data);
const enableNextTab = (index) => { const enableNextTab = (index) => {
setTabs((prevTabs) => { setTabs((prevTabs) => {
@@ -43,32 +45,43 @@ const CreateIO = () => {
{ {
label: "Investment documents", label: "Investment documents",
Content: InvestmentDocument, Content: InvestmentDocument,
isDisabled: id ? false : true, isDisabled: id ? true : true,
}, },
{ {
label: "Key merits", label: "Key merits",
Content: KeyMerits, Content: KeyMerits,
isDisabled: id ? false : true, isDisabled: id ? true : true,
}, },
{ {
label: "IO artifacts", label: "IO artifacts",
Content: IOArtifacts, Content: IOArtifacts,
isDisabled: id ? false : true, isDisabled: id ? true : true,
}, },
{ {
label: "Investors", label: "Investors",
Content: Investors, Content: Investors,
isDisabled: id ? false : false, // Content: UnderConstruction,
isDisabled: id ? true : true,
}, },
{ {
label: "IO Cash Details", label: "IO Cash Detail",
Content: IOCashDetails, Content: IOCashDetails,
isDisabled: id ? false : false, isDisabled: id ? true : true,
}, },
{ {
label: "IO NAV Details", label: "IO NAV Details",
Content: IONAVDetails, Content: IONAVDetails,
isDisabled: id ? false : false, isDisabled: id ? true : true,
},
{
label: "Distribution to Investors",
Content: Destribution,
isDisabled: id ? true : true,
},
{
label: "IO Transaction",
Content: IOTransaction,
isDisabled: id ? true : true,
}, },
]; ];
@@ -86,26 +99,31 @@ const CreateIO = () => {
height={"100vh"} height={"100vh"}
pb={10} pb={10}
> >
<Box paddingInline={"12px"} mt={2}> {id && <Box
ps={1}
pe={2} mt={2}>
{/* <span {/* <span
onClick={() => navigate(-1)} onClick={() => navigate(-1)}
style={{ fontSize: "15px", cursor: "pointer" }} style={{ fontSize: "15px", cursor: "pointer" }}
> >
<ArrowBackIcon cursor={"pointer"} /> Back <ArrowBackIcon cursor={"pointer"} /> Back
</span> */} </span> */}
<ViewIOdataHeader /> <ViewIOdataHeader isLoading={isLoading} data={data?.data} />
</Box> </Box>}
<Tabs <Tabs
index={activeIndex} index={activeIndex}
onChange={(index) => setActiveIndex(index)} onChange={(index) => setActiveIndex(index)}
mt={2} mt={2}
ps={1}
pe={2}
> >
<TabList> <TabList>
{tabs?.map(({ label, isDisabled }, index) => ( {tabs?.map(({ label, isDisabled }, index) => (
<Tab <Tab
isDisabled={isDisabled} isDisabled={isDisabled}
key={index} key={index}
fontSize={"sm"} fontSize={"xs"}
fontWeight={500}
_selected={{ _selected={{
color: "#004118", color: "#004118",
borderBottom: "2px solid #38a169", borderBottom: "2px solid #38a169",

View File

@@ -1,9 +1,238 @@
import React from 'react' import React, { useContext, useEffect, useRef, useState } from "react";
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import {
Box,
HStack,
Input,
Text,
Table,
Tbody,
Th,
Tr,
Avatar,
useDisclosure,
Button,
Badge,
} from "@chakra-ui/react";
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
import Pagination from "../../../Components/Pagination";
import NormalTable from "../../../Components/DataTable/NormalTable";
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import { formatDatee } from "../../../Components/FormField";
import { AddIcon } from "@chakra-ui/icons";
import AddIONav from "./AddIONav";
const Destribution = () => {
const { navDetails, setNavDetails, IODetails } =
useContext(GlobalStateContext);
const firstField = useRef();
const { isOpen, onOpen, onClose } = useDisclosure();
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
console.log(IODetails?.ioNAVHistory);
const formatDate = (date) => {
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
};
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
// Cleanup the timer on component unmount
return () => clearTimeout(timer);
}, []);
// Table setup
const tableHeadRow = [
// "Sr.No",
"Date",
"Amount",
"% of Investment",
];
// Table filter
const filteredData = IODetails?.distributionToInvestor
?.filter((item) => {
const name = item?.transactionAmount;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
return nameMatches;
})
.sort((b, a) => new Date(a.transactionDate) - new Date(b.transactionDate));
const extractedArray = filteredData?.map((item, index) => ({
id: item?.id,
"Sr.No": (
<Text
justifyContent={"start"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-start web-text-small"
>
{item?.id}
</Text>
),
Date: (
<Text
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{formatDate(item.transactionDate)}
</Text>
),
Amount: (
<Text
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{`${parseFloat(item.transactionAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
</Text>
),
"% of Investment": (
<Text
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{parseFloat(item.percentage).toFixed(2)}%
</Text>
),
}));
const handleDelete = () => {
const updatedNav = navDetails.filter((sponsor) => sponsor.id !== actionId);
setTimeout(() => {
setNavDetails(updatedNav);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
const Total = () => {
return (
<Table size="sm">
<Tbody>
<Tr backgroundColor="gray.50">
<Th
textAlign={"center"}
p={3}
width="60px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
Total
</Th>
<Th
textAlign={"center"}
p={3}
width="90px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{IODetails?.total_distributeToInvestor_amt?.toLocaleString(
undefined,
{ minimumFractionDigits: 2, maximumFractionDigits: 2 }
)}
</Th>
<Th
textAlign={"center"}
p={3}
width="100px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
{" "}
</Th>
</Tr>
</Tbody>
</Table>
);
};
const Distribution = () => {
return ( return (
<div>Distribution</div> <Box {...OPACITY_ON_LOAD} pb={0}>
) <Box bg="white.500">
} <HStack
display={"flex"}
justifyContent={"space-between"}
pb={3}
spacing="24px"
>
<Input
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</HStack>
</Box>
export default Distribution <NormalTable
centered={true}
emptyMessage={`We don't have any Sponers`}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
total={<Total />}
setMouseEnteredId={setMouseEnteredId}
setMouseEntered={setMouseEntered}
/>
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>
<AddIONav isOpen={isOpen} onClose={onClose} firstField={firstField} />
</Box>
);
};
export default Destribution;

View File

@@ -7,6 +7,7 @@ import {
Text, Text,
Tooltip, Tooltip,
useDisclosure, useDisclosure,
useToast,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import React, { useContext, useEffect, useRef, useState } from "react"; import React, { useContext, useEffect, useRef, useState } from "react";
import DataTable from "../../../Components/DataTable/DataTable"; import DataTable from "../../../Components/DataTable/DataTable";
@@ -21,7 +22,7 @@ import {
} from "@chakra-ui/icons"; } from "@chakra-ui/icons";
import IOArtifactsAdd from "../IOArtifactsAdd"; import IOArtifactsAdd from "../IOArtifactsAdd";
import IOArtifactsVideo from "./IOArtifactsVideo"; import IOArtifactsVideo from "./IOArtifactsVideo";
import SetDisplayOrder from "./SetDisplayOrder"; import SetDisplayOrder from "./SetDisplayOrderKeyMerits";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { import {
useDeleteImageArtifactsMutation, useDeleteImageArtifactsMutation,
@@ -30,8 +31,12 @@ import {
} from "../../../Services/io.service"; } from "../../../Services/io.service";
import { getFileNameFromPath } from "../../../Constants/Constants"; import { getFileNameFromPath } from "../../../Constants/Constants";
import ImageViewer from "../../../Components/ImageViewer"; import ImageViewer from "../../../Components/ImageViewer";
import ToastBox from "../../../Components/ToastBox";
import SetDisplayOrderIOArtifactsImages from "./SetDisplayOrderIOArtifactsImages";
import SetDisplayOrderIOArtifactsVideo from "./SetDisplayOrderIOArtifactsVideo";
const IOArtifacts = ({ enableNextTab, index, data }) => { const IOArtifacts = ({ enableNextTab, index, data }) => {
const toast = useToast()
const params = useParams(); const params = useParams();
const id = params?.id; const id = params?.id;
@@ -107,10 +112,12 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
setIsLoadingBtn(true); setIsLoadingBtn(true);
try { try {
const res = await deleteVideoArtifacts(id); const res = await deleteVideoArtifacts(id);
console.log(res?.data?.statusCode);
if (res?.data?.statusCode === 200) { if (res?.data?.statusCode === 200) {
setDeleteAlertVideo(false); setDeleteAlertVideo(false);
setIsLoadingBtn(false); setIsLoadingBtn(false);
toast({
render: () => <ToastBox message={res?.data?.message} status="success" />,
});
} }
} catch (error) { } catch (error) {
console.log(error); console.log(error);
@@ -121,10 +128,15 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
setIsLoadingBtn(true); setIsLoadingBtn(true);
try { try {
const res = await deleteImageArtifacts(id); const res = await deleteImageArtifacts(id);
console.log(res?.data?.statusCode); console.log(res);
if (res?.data?.statusCode === 200) { if (res?.data?.statusCode === 200) {
setDeleteAlertImage(false); setDeleteAlertImage(false);
setIsLoadingBtn(false); setIsLoadingBtn(false);
toast({
render: () => <ToastBox message={res?.data?.message} status="success" />,
});
} }
} catch (error) { } catch (error) {
console.log(error); console.log(error);
@@ -133,7 +145,14 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
const tableHeadRow = ["Sr.no", "File Name", "View image", "Action"]; const tableHeadRow = ["Sr.no", "File Name", "View image", "Action"];
const extractedArray = IObyID?.data?.artifactsImage?.map((item, index) => ({ console.log(IObyID?.data?.artifactsImage);
// console.log(filteredData);
const sortedDataImage = [...(IObyID?.data?.artifactsImage || [])]?.sort(
(a, b) => a?.displayOrder - b?.displayOrder
);
const extractedArray = sortedDataImage?.map((item, index) => ({
"Sr.no": ( "Sr.no": (
<Text <Text
justifyContent={slideFromRight ? "right" : "left"} justifyContent={slideFromRight ? "right" : "left"}
@@ -170,7 +189,7 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
colorScheme={"forestGreen"} colorScheme={"forestGreen"}
> >
<Link <Link
href={"https://tanami.betadelivery.com/" + item?.artifactPathName} href={import.meta.env.VITE_IMAGE_URL + item?.artifactPathName}
isExternal isExternal
> >
<Box <Box
@@ -236,6 +255,17 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
), ),
})); }));
console.log(IObyID?.data?.artifactsVideo);
// console.log(filteredData);
const sortedDataVideo = [...(IObyID?.data?.artifactsVideo || [])]?.sort(
(a, b) => a?.displayOrder - b?.displayOrder
);
const tableHeadRowTwo = [ const tableHeadRowTwo = [
"Sr.no", "Sr.no",
"File Name", "File Name",
@@ -243,7 +273,7 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
"Action", "Action",
]; ];
const extractedArrayTwo = IObyID?.data?.artifactsVideo?.map( const extractedArrayTwo = sortedDataVideo?.map(
(item, index) => ({ (item, index) => ({
"Sr.no": ( "Sr.no": (
<Text <Text
@@ -342,7 +372,9 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
Manage IO Images Manage IO Images
</Box> </Box>
<HStack> <HStack>
<SetDisplayOrder data={IObyID?.data?.artifactsImage} />
{IObyID?.data?.artifactsImage?.length !== 0 &&<SetDisplayOrderIOArtifactsImages data={sortedDataImage} />}
<Button <Button
leftIcon={<AddIcon />} leftIcon={<AddIcon />}
onClick={onOpen} onClick={onOpen}
@@ -378,7 +410,7 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
Manage IO videos Manage IO videos
</Box> </Box>
<HStack> <HStack>
<SetDisplayOrder data={IObyID?.data?.artifactsImage} /> {IObyID?.data?.artifactsVideo?.length !== 0 &&<SetDisplayOrderIOArtifactsVideo data={sortedDataVideo} />}
<Button <Button
leftIcon={<AddIcon />} leftIcon={<AddIcon />}
onClick={onOpenVideo} onClick={onOpenVideo}

View File

@@ -10,6 +10,7 @@ import {
DrawerOverlay, DrawerOverlay,
FormControl, FormControl,
FormErrorMessage, FormErrorMessage,
FormHelperText,
FormLabel, FormLabel,
Input, Input,
Stack, Stack,
@@ -26,25 +27,29 @@ import { useParams } from "react-router-dom";
import ToastBox from "../../../Components/ToastBox"; import ToastBox from "../../../Components/ToastBox";
const investmentVideoSchema = yup.object().shape({ const investmentVideoSchema = yup.object().shape({
artifactName: yup.string().required("Artifact name is required"), artifactName: yup.string().required("Artifact name is required")
artifactStreamingURL: yup.string().required("Artifact streaming URL is required").url("Invalid URL format"), .max(50, "Approve Comment cannot be more than 50 characters"),
artifactStreamingURL: yup.string()
.required("Artifact streaming URL is required")
.url("Invalid URL format"),
}); });
const IOArtifactsAdd = ({ isOpen, onClose, firstField, actionId, setActionId, data}) => { const IOArtifactsAdd = ({ isOpen, onClose, firstField, actionId, setActionId, data }) => {
const params = useParams() const params = useParams()
const id = params?.id const id = params?.id
const [file, setFile] = useState(""); const [file, setFile] = useState("");
const [fileName, setFileName] = useState(""); const [fileName, setFileName] = useState("");
const [ isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const [alert, setAlert] = useState(false); const [alert, setAlert] = useState(false);
const toast = useToast(); const toast = useToast();
const found = data?.find((item) => item?.id === actionId); const found = data?.find((item) => item?.id === actionId);
console.log(found); console.log(actionId);
const [ createArtifactsVideo ] = useCreateVideoArtifactsMutation()
const [ updateVideoArtifacts ] = useUpdateVideoArtifactsMutation() const [createArtifactsVideo] = useCreateVideoArtifactsMutation()
const [updateVideoArtifacts] = useUpdateVideoArtifactsMutation()
// const { // const {
// data // data
// } = useGetArtifactsQuery(id) // } = useGetArtifactsQuery(id)
@@ -63,7 +68,7 @@ const IOArtifactsAdd = ({ isOpen, onClose, firstField, actionId, setActionId, d
// useEffect to reset the form when `found` changes // useEffect to reset the form when `found` changes
useEffect(() => { useEffect(() => {
if (found) { if (found && actionId) {
reset({ reset({
artifactName: found?.artifactName, artifactName: found?.artifactName,
artifactStreamingURL: found?.artifactStreamingURL, artifactStreamingURL: found?.artifactStreamingURL,
@@ -71,8 +76,6 @@ const IOArtifactsAdd = ({ isOpen, onClose, firstField, actionId, setActionId, d
} }
}, [found, reset]); }, [found, reset]);
console.log(watch());
const onSubmit = async (data) => { const onSubmit = async (data) => {
setIsLoading(true) setIsLoading(true)
@@ -80,38 +83,39 @@ const IOArtifactsAdd = ({ isOpen, onClose, firstField, actionId, setActionId, d
try { try {
if (found) { if (found) {
const res = await updateVideoArtifacts({data, id: found?.id}) const res = await updateVideoArtifacts({ data, id: found?.id })
if (res?.data?.statusCode === 200) { if (res?.data?.statusCode === 200) {
toast({ toast({
render: () => <ToastBox message={res?.data?.message} />, render: () => <ToastBox message={res?.data?.message} />,
}); });
setAlert(false); setAlert(false);
setIsLoading(false) setIsLoading(false)
handleClose(); handleClose();
} }
} else { } else {
const res = await createArtifactsVideo({data, id}) const res = await createArtifactsVideo({ data, id })
if (res?.data?.statusCode === 200) { if (res?.data?.statusCode === 200) {
toast({ toast({
render: () => <ToastBox message={res?.data?.message} />, render: () => <ToastBox message={res?.data?.message} />,
}); });
setAlert(false); setAlert(false);
setIsLoading(false) setIsLoading(false)
handleClose(); setActionId(false);
handleClose();
} }
} }
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }
}; };
@@ -128,12 +132,16 @@ const IOArtifactsAdd = ({ isOpen, onClose, firstField, actionId, setActionId, d
onClose() onClose()
reset() reset()
setActionId(false); setActionId(false);
reset({
artifactName: "",
artifactStreamingURL: "",
});
} }
return ( return (
<> <>
<Drawer <Drawer
size={"md"} size={"md"}
isOpen={isOpen} isOpen={isOpen}
placement="right" placement="right"
initialFocusRef={firstField} initialFocusRef={firstField}
@@ -146,21 +154,25 @@ const IOArtifactsAdd = ({ isOpen, onClose, firstField, actionId, setActionId, d
<DrawerBody> <DrawerBody>
<Stack spacing={4}> <Stack spacing={4}>
<FormControl isInvalid={errors.artifactName}> <FormControl isInvalid={errors.artifactName} isRequired>
<FormLabel fontSize={"sm"}>Artifact Name</FormLabel> <FormLabel fontSize={"sm"}>Artifact Name</FormLabel>
<Controller <Controller
name="artifactName" name="artifactName"
control={control} control={control}
render={({ field }) => ( render={({ field }) => (
<Input {...field} fontSize={"sm"} type="text" size={"sm"} /> <Input {...field} fontSize={"sm"} type="text" size={"sm"} maxLength={50} />
)} )}
/> />
<FormErrorMessage fontSize={"xs"} fontWeight={500}> <FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.artifactName?.message} {errors.artifactName?.message}
</FormErrorMessage> </FormErrorMessage>
<FormHelperText fontSize="xs" color="gray.500">
<Box as="span" me={1}>Maximum length should be 50 characters. You have entered </Box>
{watch("artifactName")?.length || 0} characters.
</FormHelperText>
</FormControl> </FormControl>
<FormControl isInvalid={errors.artifactStreamingURL}> <FormControl isInvalid={errors.artifactStreamingURL} isRequired>
<FormLabel fontSize={"sm"}>Artifact Streaming URL</FormLabel> <FormLabel fontSize={"sm"}>Artifact Streaming URL</FormLabel>
<Controller <Controller
name="artifactStreamingURL" name="artifactStreamingURL"
@@ -200,12 +212,12 @@ const IOArtifactsAdd = ({ isOpen, onClose, firstField, actionId, setActionId, d
</DrawerContent> </DrawerContent>
</Drawer> </Drawer>
<CustomAlertDialog <CustomAlertDialog
isOpen={alert} isOpen={alert}
onClose={() => setAlert(false)} onClose={() => setAlert(false)}
alertHandler={handleSave} alertHandler={handleSave}
message={"Are you sure you want to add this artifact?"} message={"Are you sure you want to update this artifact?"}
isLoading={isLoading} isLoading={isLoading}
/> />
</> </>

View File

@@ -0,0 +1,263 @@
import {
Box,
Button,
Drawer,
DrawerBody,
DrawerCloseButton,
DrawerContent,
DrawerFooter,
DrawerHeader,
DrawerOverlay,
FormControl,
FormErrorMessage,
FormHelperText,
FormLabel,
Input,
Select,
Stack,
Textarea,
useToast,
} from "@chakra-ui/react";
import * as yup from "yup";
import React, { useState, useEffect, useContext } from "react";
import { useForm, Controller } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import { v4 as uuidv4 } from "uuid";
import { useParams } from "react-router-dom";
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
import { useCreateIoCashMutation, useCreateVideoArtifactsMutation, useUpdateVideoArtifactsMutation } from "../../../../Services/io.service";
import ToastBox from "../../../../Components/ToastBox";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import CurrencyInput from "../../../../Components/CurrencyInput";
const cashDetails = yup.object().shape({
transactionDate: yup.string().required("Date is required"),
ioTransType_xid: yup.number().required("Cash transaction is required"),
transactionAmount: yup.number().required("Transaction Amount is required"),
comments: yup.string().notRequired()
.max(200, "Approve Comment cannot be more than 200 characters"),
});
const AddCaseDetails = ({ isOpen, onClose, firstField, actionId, setActionId, data, setActiveTab }) => {
const params = useParams()
const id = params?.id
const [file, setFile] = useState("");
const [fileName, setFileName] = useState("");
const [isLoading, setIsLoading] = useState(false)
const [alert, setAlert] = useState(false);
const toast = useToast();
console.log(isOpen);
// ======================[ Cotext Api ]
const { IODetails } = useContext(GlobalStateContext);
const found = data?.find((item) => item?.id === actionId);
const [createArtifactsVideo] = useCreateVideoArtifactsMutation()
const [updateVideoArtifacts] = useUpdateVideoArtifactsMutation()
// const {
// data
// } = useGetArtifactsQuery(id)
const {
control,
handleSubmit,
watch,
reset,
formState: { errors },
} = useForm({
resolver: yupResolver(cashDetails),
});
const [createIoCash] = useCreateIoCashMutation()
const onSubmit = async (data) => {
setIsLoading(true)
try {
const res = await createIoCash({ data, id })
if (res?.data) {
setIsLoading(false);
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
handleClose()
setActiveTab(1)
}else if(res?.error){
setIsLoading(false);
toast({
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
});
}
} catch (error) {
console.log(error);
setIsLoading(false);
}
};
const handleConfirm = () => {
handleSubmit(onSubmit)();
};
const handleSave = () => {
handleSubmit(onSubmit)();
};
const handleClose = () => {
setAlert(false)
onClose()
reset({
transactionAmount:""
})
}
return (
<>
<Drawer
size={"md"}
isOpen={isOpen}
placement="right"
initialFocusRef={firstField}
onClose={handleClose}
>
<DrawerOverlay />
<DrawerContent>
<DrawerCloseButton />
<DrawerHeader fontSize={"sm"}>IO Cash Details</DrawerHeader>
<DrawerBody>
<Stack spacing={4}>
<FormControl isInvalid={errors.transactionDate} isRequired>
<FormLabel fontSize={"sm"}>Date Selection</FormLabel>
<Controller
name="transactionDate"
control={control}
render={({ field }) => (
<Input
focusBorderColor="forestGreen.300" {...field} fontSize={"sm"} type="date" size={"sm"} />
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.transactionDate?.message}
</FormErrorMessage>
</FormControl>
<FormControl isInvalid={errors.ioTransType_xid} isRequired>
<FormLabel fontSize={"sm"}>Cash transaction</FormLabel>
<Controller
name="ioTransType_xid"
control={control}
render={({ field }) => (
<Select
{...field}
placeholder="Select an option"
fontSize={"sm"}
size={"sm"}
focusBorderColor="forestGreen.300"
>
{IODetails?.ioCashTransaction?.map(({ id, transactionName }) => (
<option key={id} value={id}>
{transactionName}
</option>
))}
</Select>
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.ioTransType_xid?.message}
</FormErrorMessage>
</FormControl>
<FormControl isInvalid={errors.transactionAmount} isRequired>
<FormLabel fontSize={"sm"}>Transaction Amount</FormLabel>
<Controller
name="transactionAmount"
control={control}
render={({ field }) => (
<CurrencyInput {...field} textAlign={'right'} fontSize={"sm"} type="number" size={"sm"} />
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.transactionAmount?.message}
</FormErrorMessage>
</FormControl>
<FormControl isInvalid={errors.comments}>
<FormLabel fontSize={"sm"}>Comment</FormLabel>
<Controller
name="comments"
control={control}
render={({ field }) => (
<Textarea {...field} textAlign={'left'}
maxLength={200}
focusBorderColor="forestGreen.300" fontSize={"sm"} type="text" size={"sm"} />
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.comments?.message}
</FormErrorMessage>
<FormHelperText fontSize="xs" color="gray.500">
<Box as="span" me={1}>Maximum length should be 200 characters. You have entered </Box>
{watch("comments")?.length || 0} characters.
</FormHelperText>
</FormControl>
</Stack>
</DrawerBody>
<DrawerFooter>
<Button
variant="outline"
colorScheme={"forestGreen"}
rounded={"sm"}
size={"sm"}
mr={3}
onClick={handleClose}
>
Cancel
</Button>
<Button
colorScheme={"forestGreen"}
rounded={"sm"}
size={"sm"}
onClick={() => setAlert(true)}
>
Save
</Button>
</DrawerFooter>
</DrawerContent>
</Drawer>
<CustomAlertDialog
isOpen={alert}
onClose={() => setAlert(false)}
alertHandler={handleSave}
message={"Are you sure you want to add cash details?"}
isLoading={isLoading}
/>
</>
);
};
export default AddCaseDetails;

View File

@@ -0,0 +1,251 @@
import {
Box,
Button,
Drawer,
DrawerBody,
DrawerCloseButton,
DrawerContent,
DrawerFooter,
DrawerHeader,
DrawerOverlay,
FormControl,
FormErrorMessage,
FormLabel,
Input,
Select,
Stack,
Textarea,
useToast,
} from "@chakra-ui/react";
import * as yup from "yup";
import React, { useState, useEffect, useContext } from "react";
import { useForm, Controller } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import { v4 as uuidv4 } from "uuid";
import { useParams } from "react-router-dom";
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
import { useCreateIoCashMutation, useCreateVideoArtifactsMutation, useUpdateVideoArtifactsMutation } from "../../../../Services/io.service";
import ToastBox from "../../../../Components/ToastBox";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import CurrencyInput from "../../../../Components/CurrencyInput";
const cashDetails = yup.object().shape({
transactionDate: yup.string().required("Date is required"),
ioTransType_xid: yup.number().required("Cash transaction is required"),
transactionAmount: yup.number().required("Transaction Amount is required"),
comments: yup.string().notRequired(),
});
const AddPending = ({ isOpen, onClose, firstField, actionId, setActionId, data }) => {
const params = useParams()
const id = params?.id
const [file, setFile] = useState("");
const [fileName, setFileName] = useState("");
const [isLoading, setIsLoading] = useState(false)
const [alert, setAlert] = useState(false);
const toast = useToast();
// ======================[ Cotext Api ]
const { IODetails } = useContext(GlobalStateContext);
const found = data?.find((item) => item?.id === actionId);
const [createArtifactsVideo] = useCreateVideoArtifactsMutation()
const [updateVideoArtifacts] = useUpdateVideoArtifactsMutation()
// const {
// data
// } = useGetArtifactsQuery(id)
const {
control,
handleSubmit,
watch,
reset,
formState: { errors },
} = useForm({
resolver: yupResolver(cashDetails),
});
const [createIoCash] = useCreateIoCashMutation()
const onSubmit = async (data) => {
setIsLoading(true)
try {
const res = await createIoCash({ data, id })
if (res?.data?.statusCode === 200) {
setIsLoading(false);
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
handleClose()
}else if(res?.error?.status === 400){
setIsLoading(false);
toast({
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
});
}
} catch (error) {
console.log(error);
}
};
const handleConfirm = () => {
handleSubmit(onSubmit)();
};
const handleSave = () => {
handleSubmit(onSubmit)();
};
const handleClose = () => {
setAlert(false)
onClose()
reset({
transactionAmount:""
})
}
return (
<>
<Drawer
size={"md"}
isOpen={isOpen}
placement="right"
initialFocusRef={firstField}
onClose={handleClose}
>
<DrawerOverlay />
<DrawerContent>
<DrawerCloseButton />
<DrawerHeader fontSize={"sm"}>IO Cash Details</DrawerHeader>
<DrawerBody>
<Stack spacing={4}>
<FormControl isInvalid={errors.transactionDate} isRequired>
<FormLabel fontSize={"sm"}>Date Selection</FormLabel>
<Controller
name="transactionDate"
control={control}
render={({ field }) => (
<Input
focusBorderColor="forestGreen.300" {...field} fontSize={"sm"} type="date" size={"sm"} />
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.transactionDate?.message}
</FormErrorMessage>
</FormControl>
<FormControl isInvalid={errors.ioTransType_xid} isRequired>
<FormLabel fontSize={"sm"}>Cash transaction</FormLabel>
<Controller
name="ioTransType_xid"
control={control}
render={({ field }) => (
<Select
{...field}
placeholder="Select an option"
fontSize={"sm"}
size={"sm"}
focusBorderColor="forestGreen.300"
>
{IODetails?.ioCashTransaction?.map(({ id, transactionName }) => (
<option key={id} value={id}>
{transactionName}
</option>
))}
</Select>
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.ioTransType_xid?.message}
</FormErrorMessage>
</FormControl>
<FormControl isInvalid={errors.transactionAmount} isRequired>
<FormLabel fontSize={"sm"}>Transaction Amount</FormLabel>
<Controller
name="transactionAmount"
control={control}
render={({ field }) => (
<CurrencyInput {...field} textAlign={'right'} fontSize={"sm"} type="number" size={"sm"} />
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.transactionAmount?.message}
</FormErrorMessage>
</FormControl>
<FormControl isInvalid={errors.comments}>
<FormLabel fontSize={"sm"}>Comments</FormLabel>
<Controller
name="comments"
control={control}
render={({ field }) => (
<Textarea {...field} textAlign={'left'}
focusBorderColor="forestGreen.300" fontSize={"sm"} type="text" size={"sm"} />
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.comments?.message}
</FormErrorMessage>
</FormControl>
</Stack>
</DrawerBody>
<DrawerFooter>
<Button
variant="outline"
colorScheme={"forestGreen"}
rounded={"sm"}
size={"sm"}
mr={3}
onClick={handleClose}
>
Cancel
</Button>
<Button
colorScheme={"forestGreen"}
rounded={"sm"}
size={"sm"}
onClick={() => setAlert(true)}
>
Save
</Button>
</DrawerFooter>
</DrawerContent>
</Drawer>
<CustomAlertDialog
isOpen={alert}
onClose={() => setAlert(false)}
alertHandler={handleSave}
message={"Are you sure you want to add cash details?"}
isLoading={isLoading}
/>
</>
);
};
export default AddPending;

View File

@@ -0,0 +1,251 @@
import {
Box,
Button,
Drawer,
DrawerBody,
DrawerCloseButton,
DrawerContent,
DrawerFooter,
DrawerHeader,
DrawerOverlay,
FormControl,
FormErrorMessage,
FormLabel,
Input,
Select,
Stack,
Textarea,
useToast,
} from "@chakra-ui/react";
import * as yup from "yup";
import React, { useState, useEffect, useContext } from "react";
import { useForm, Controller } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import { v4 as uuidv4 } from "uuid";
import { useParams } from "react-router-dom";
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
import { useCreateIoCashMutation, useCreateVideoArtifactsMutation, useUpdateVideoArtifactsMutation } from "../../../../Services/io.service";
import ToastBox from "../../../../Components/ToastBox";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import CurrencyInput from "../../../../Components/CurrencyInput";
const cashDetails = yup.object().shape({
transactionDate: yup.string().required("Date is required"),
ioTransType_xid: yup.number().required("Cash transaction is required"),
transactionAmount: yup.number().required("Transaction Amount is required"),
comments: yup.string().notRequired(),
});
const AddRejected = ({ isOpen, onClose, firstField, actionId, setActionId, data }) => {
const params = useParams()
const id = params?.id
const [file, setFile] = useState("");
const [fileName, setFileName] = useState("");
const [isLoading, setIsLoading] = useState(false)
const [alert, setAlert] = useState(false);
const toast = useToast();
// ======================[ Cotext Api ]
const { IODetails } = useContext(GlobalStateContext);
const found = data?.find((item) => item?.id === actionId);
const [createArtifactsVideo] = useCreateVideoArtifactsMutation()
const [updateVideoArtifacts] = useUpdateVideoArtifactsMutation()
// const {
// data
// } = useGetArtifactsQuery(id)
const {
control,
handleSubmit,
watch,
reset,
formState: { errors },
} = useForm({
resolver: yupResolver(cashDetails),
});
const [createIoCash] = useCreateIoCashMutation()
const onSubmit = async (data) => {
setIsLoading(true)
try {
const res = await createIoCash({ data, id })
if (res?.data?.statusCode === 200) {
setIsLoading(false);
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
handleClose()
}else if(res?.error?.status === 400){
setIsLoading(false);
toast({
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
});
}
} catch (error) {
console.log(error);
}
};
const handleConfirm = () => {
handleSubmit(onSubmit)();
};
const handleSave = () => {
handleSubmit(onSubmit)();
};
const handleClose = () => {
setAlert(false)
onClose()
reset({
transactionAmount:""
})
}
return (
<>
<Drawer
size={"md"}
isOpen={isOpen}
placement="right"
initialFocusRef={firstField}
onClose={handleClose}
>
<DrawerOverlay />
<DrawerContent>
<DrawerCloseButton />
<DrawerHeader fontSize={"sm"}>IO Cash Details</DrawerHeader>
<DrawerBody>
<Stack spacing={4}>
<FormControl isInvalid={errors.transactionDate} isRequired>
<FormLabel fontSize={"sm"}>Date Selection</FormLabel>
<Controller
name="transactionDate"
control={control}
render={({ field }) => (
<Input
focusBorderColor="forestGreen.300" {...field} fontSize={"sm"} type="date" size={"sm"} />
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.transactionDate?.message}
</FormErrorMessage>
</FormControl>
<FormControl isInvalid={errors.ioTransType_xid} isRequired>
<FormLabel fontSize={"sm"}>Cash transaction</FormLabel>
<Controller
name="ioTransType_xid"
control={control}
render={({ field }) => (
<Select
{...field}
placeholder="Select an option"
fontSize={"sm"}
size={"sm"}
focusBorderColor="forestGreen.300"
>
{IODetails?.ioCashTransaction?.map(({ id, transactionName }) => (
<option key={id} value={id}>
{transactionName}
</option>
))}
</Select>
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.ioTransType_xid?.message}
</FormErrorMessage>
</FormControl>
<FormControl isInvalid={errors.transactionAmount} isRequired>
<FormLabel fontSize={"sm"}>Transaction Amount</FormLabel>
<Controller
name="transactionAmount"
control={control}
render={({ field }) => (
<CurrencyInput {...field} textAlign={'right'} fontSize={"sm"} type="number" size={"sm"} />
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.transactionAmount?.message}
</FormErrorMessage>
</FormControl>
<FormControl isInvalid={errors.comments}>
<FormLabel fontSize={"sm"}>Comments</FormLabel>
<Controller
name="comments"
control={control}
render={({ field }) => (
<Textarea {...field} textAlign={'left'}
focusBorderColor="forestGreen.300" fontSize={"sm"} type="text" size={"sm"} />
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.comments?.message}
</FormErrorMessage>
</FormControl>
</Stack>
</DrawerBody>
<DrawerFooter>
<Button
variant="outline"
colorScheme={"forestGreen"}
rounded={"sm"}
size={"sm"}
mr={3}
onClick={handleClose}
>
Cancel
</Button>
<Button
colorScheme={"forestGreen"}
rounded={"sm"}
size={"sm"}
onClick={() => setAlert(true)}
>
Save
</Button>
</DrawerFooter>
</DrawerContent>
</Drawer>
<CustomAlertDialog
isOpen={alert}
onClose={() => setAlert(false)}
alertHandler={handleSave}
message={"Are you sure you want to add cash details?"}
isLoading={isLoading}
/>
</>
);
};
export default AddRejected;

View File

@@ -0,0 +1,397 @@
import {
Avatar,
Badge,
Box,
Button,
HStack,
Input,
Table,
Tag,
Tbody,
Text,
Th,
Tooltip,
Tr,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useRef, useState } from "react";
import { AddIcon, DeleteIcon, EditIcon, ViewIcon } from "@chakra-ui/icons";
import { LuFileSpreadsheet } from "react-icons/lu";
import * as XLSX from "xlsx";
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
import NormalTable from "../../../../Components/DataTable/NormalTable";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
import ToastBox from "../../../../Components/ToastBox";
import AddCashDetails from "../AddCashDetails";
import { debounce } from "../../../Admin/Contact";
import AddApproved from "./AddCaseDetails";
import { useUpdateIOCaseMutation } from "../../../../Services/io.service";
import { useParams } from "react-router-dom";
import AddCaseDetails from "./AddCaseDetails";
const formatDate = (date) => new Date(date).toLocaleDateString();
const Approved = () => {
const firstField = useRef();
const params = useParams();
const toast = useToast();
const id = params?.id;
const { isOpen, onOpen, onClose } = useDisclosure();
const { IODetails, approved, setApproved } = useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
// Cleanup the timer on component unmount
return () => clearTimeout(timer);
}, []);
const formatDate = (date) => {
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
};
console.log("==============", IODetails?.ioCashStatusHistory?.Approved);
// Table filter
const filteredData = IODetails?.ioCashStatusHistory?.Approved?.filter(
(item) => {
// Filter by name (case insensitive)
const name = item.transactionDate;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
return nameMatches;
}
);
const [updateIOCase] = useUpdateIOCaseMutation();
const tableHeadRow = [
"Sr No.",
"Transaction Date",
"Transaction Type",
"Amount",
"Comments",
"Update By",
"Update On",
];
const extractedArray = filteredData?.map((item, index) => ({
id: item?.id,
"Sr No.": (
<Text as={"span"} color={"gray.800"} fontWeight={"500"}>
{index + 1}.
</Text>
),
"Transaction Date": (
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
{formatDate(item?.transactionDate)}
</Text>
),
"Transaction Type": (
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
{item?.transactionType}
</Text>
),
Amount: (
<Text as={"span"} color={"gray.800"} fontWeight={"500"}>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{parseFloat(item?.transactionAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
),
Comments: (
<Text w={"100px"} as={"span"} color={"gray.800"} fontWeight={"500"}>
{item?.comments ? item?.comments : "---"}
</Text>
),
"Update By": (
<Text
w={"100px"}
as={"span"}
color={"gray.800"}
fontWeight={"500"}
display={"flex"}
alignItems={"center"}
>
{/* <Avatar
mr={2}
size="sm"
name={item.creator?.firstName}
src={item.creator?.profilePhoto}
/> */}
{item?.modifier?.firstName}
</Text>
),
"Update On": (
<Text w={"100px"} as={"span"} color={"gray.800"} fontWeight={"500"}>
{formatDate(item.updatedAt)}
</Text>
),
}));
const handleDelete = () => {
const updatedSponsors = sponser.filter(
(sponsor) => sponsor.id !== actionId
);
setTimeout(() => {
setCaseDetails(updatedSponsors);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
const ioCashExporteDetails = IODetails?.ioCashStatusHistory?.Approved?.map(
(item, index) => ({
"Transaction date": item?.transactionDate,
"Transaction type": item?.transactionType,
Amount: parseFloat(item?.transactionAmount) || 0,
Comments: item?.comments,
})
);
const exportToExcelNew = (data, fileName) => {
const worksheet = XLSX.utils.json_to_sheet(data);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1");
// Export file
XLSX.writeFile(workbook, `${fileName}.xlsx`);
};
const Total = () => {
return (
<Table size="sm">
<Tbody backgroundColor="gray.50">
<Tr>
<Th
textAlign={"center"}
p={3}
width="200px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
Balance in IO Cash
</Th>
<Th
textAlign={"center"}
p={3}
width="120px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
{" "}
</Th>
<Th
textAlign={"center"}
p={3}
width="120px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
{" "}
</Th>
<Th
textAlign={"center"}
p={3}
width="140px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{/* {IODetails?.ioCash} */}
{parseFloat(IODetails?.ioCash || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Th>
<Th
textAlign={"center"}
p={3}
width="120px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
{}
</Th>
<Th
textAlign={"center"}
p={3}
width="100px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
{" "}
</Th>
<Th
textAlign={"center"}
p={3}
width="100px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
></Th>
<Th
textAlign={"center"}
p={3}
width="100px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
></Th>
</Tr>
</Tbody>
</Table>
);
};
const handleAdd = async () => {
try {
const res = await updateIOCase(id);
if (res?.data) {
// toast({
// render: () => (
// <ToastBox status={"success"} message={res?.data?.message} />
// ),
// });
setIsLoading(false);
onOpen();
} else if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsLoading(false);
}
} catch (error) {}
};
return (
<Box {...OPACITY_ON_LOAD} pb={0}>
<Box bg="white.500">
<HStack
display={"flex"}
justifyContent={"space-between"}
pb={3}
spacing="24px"
>
<Input
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<HStack display={"flex"} alignItems={"center"}>
<Button
onClick={() =>
exportToExcelNew(ioCashExporteDetails, "IO Cash History")
}
leftIcon={<LuFileSpreadsheet />}
colorScheme="forestGreen"
size={"sm"}
variant={"outline"}
rounded={"sm"}
fontSize={"xs"}
isDisabled={ioCashExporteDetails?.length === 0}
>
Export xls
</Button>
{/* <Button
onClick={onOpen}
leftIcon={<AddIcon />}
colorScheme={"forestGreen"}
rounded={"sm"}
fontSize={"xs"}
size={"sm"}
fontWeight={500}
>
Add
</Button> */}
{/* {IODetails?.isInvestedAmount ? (
localStorage?.getItem('role') ==="Maker" && <Button
onClick={handleAdd}
leftIcon={<AddIcon />}
colorScheme="forestGreen"
size={"sm"}
rounded={"sm"}
fontSize={"xs"}
>
Add
</Button>
) : null} */}
</HStack>
</HStack>
</Box>
<NormalTable
emptyMessage={`We don't have any Sponers`}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
total={<Total />}
setMouseEnteredId={setMouseEnteredId}
setMouseEntered={setMouseEntered}
/>
{/* <CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/> */}
<AddCaseDetails
isOpen={isOpen}
onClose={onClose}
firstField={firstField}
/>
</Box>
);
};
export default Approved;

View File

@@ -0,0 +1,144 @@
import {
Badge,
Box,
Button,
Tab,
TabList,
TabPanel,
TabPanels,
Tabs,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useRef, useState } from "react";
import Approved from "./Approved";
import Pending from "./Pending";
import Rejected from "./Rejected";
import { AddIcon } from "@chakra-ui/icons";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import AddCaseDetails from "./AddCaseDetails";
import { useUpdateIOCaseMutation } from "../../../../Services/io.service";
import ToastBox from "../../../../Components/ToastBox";
import { useParams } from "react-router-dom";
const IOCashDetails = () => {
const params = useParams();
const toast = useToast();
const id = params?.id;
const { IODetails } = useContext(GlobalStateContext);
const { isOpen, onOpen, onClose } = useDisclosure();
const firstField = useRef();
const [updateIOCase] = useUpdateIOCaseMutation();
const [activeTab, setActiveTab] = useState(0);
const handleAdd = async () => {
try {
const res = await updateIOCase(id);
if (res?.data) {
// toast({
// render: () => (
// <ToastBox status={"success"} message={"res?.data?.message"} />
// ),
// });
// setIsLoading(false);
onOpen();
} else if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsLoading(false);
}
} catch (error) {}
};
return (
<Box>
<Tabs
index={activeTab}
onChange={(index) => setActiveTab(index)}
variant="unstyled"
>
<Box
display={"flex"}
justifyContent={"space-between"}
alignItems={"center"}
borderBottom={"1px solid #ccc"}
>
<TabList>
<Tab
fontSize={"sm"}
_selected={{
color: "#004118",
borderBottom: "2px solid #38a169",
}}
>
Approved
</Tab>
<Tab
fontSize={"sm"}
_selected={{
color: "#004118",
borderBottom: "2px solid #38a169",
}}
>
Pending
{IODetails?.ioCashStatusHistory?.Pending.length > 0 && (
<Badge rounded={"sm"} colorScheme="forestGreen" ms={2}>
{IODetails?.ioCashStatusHistory?.Pending.length !== 0 && IODetails?.ioCashStatusHistory?.Pending.length}
</Badge>
)}
{/* <Badge rounded={"sm"} colorScheme="forestGreen" ms={2}>
{IODetails?.ioCashStatusHistory?.Pending.length || 0}
</Badge> */}
</Tab>
<Tab
fontSize={"sm"}
_selected={{
color: "#004118",
borderBottom: "2px solid #38a169",
}}
>
Rejected
</Tab>
</TabList>
{IODetails?.isInvestedAmount
? localStorage?.getItem("role") === "Maker" && (
<Button
onClick={handleAdd}
leftIcon={<AddIcon />}
colorScheme="forestGreen"
size={"sm"}
rounded={"sm"}
fontSize={"xs"}
>
Add
</Button>
)
: null}
</Box>
<TabPanels>
<TabPanel>
<Approved />
</TabPanel>
<TabPanel>
<Pending />
</TabPanel>
<TabPanel>
<Rejected />
</TabPanel>
</TabPanels>
</Tabs>
<AddCaseDetails
setActiveTab={setActiveTab}
isOpen={isOpen}
onClose={onClose}
firstField={firstField}
/>
</Box>
);
};
export default IOCashDetails;

View File

@@ -0,0 +1,465 @@
import {
Avatar,
Badge,
Box,
Button,
HStack,
Input,
Table,
Tag,
Tbody,
Text,
Th,
Tooltip,
Tr,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useRef, useState } from "react";
import {
AddIcon,
CheckIcon,
CloseIcon,
DeleteIcon,
EditIcon,
ViewIcon,
} from "@chakra-ui/icons";
import { LuFileSpreadsheet } from "react-icons/lu";
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
import NormalTable from "../../../../Components/DataTable/NormalTable";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
import ToastBox from "../../../../Components/ToastBox";
import AddCashDetails from "../AddCashDetails";
import { debounce } from "../../../Admin/Contact";
import AddPending from "./AddPending";
import { useParams } from "react-router-dom";
import { useUpdateIOCaseMutation } from "../../../../Services/io.service";
import RequestApproveModal from "./RequestApproveModal";
import RequestRejectModal from "./RequestRejectModal";
import AddCaseDetails from "./AddCaseDetails";
const formatDate = (date) => new Date(date).toLocaleDateString();
const Pending = () => {
const toast = useToast();
const params = useParams();
const id = params?.id;
const firstField = useRef();
const { isOpen, onOpen, onClose } = useDisclosure();
const { IODetails, approved, setApproved } = useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
const [updateIOCase] = useUpdateIOCaseMutation();
const {
isOpen: isConfirmOpen,
onOpen: onConfirmOpen,
onClose: onConfirmClose,
} = useDisclosure();
const {
isOpen: isRejectOpen,
onOpen: onRejectOpen,
onClose: onRejectClose,
} = useDisclosure();
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
// Cleanup the timer on component unmount
return () => clearTimeout(timer);
}, []);
const formatDate = (date) => {
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
};
// Table filter
const filteredData = IODetails?.ioCashStatusHistory?.Pending?.filter(
(item) => {
// Filter by name (case insensitive)
const name = item?.transactionDate;
const searchLower = searchTerm?.toLowerCase();
const nameMatches = name?.toLowerCase().includes(searchLower);
return nameMatches;
}
);
const tableHeadRow = [
"Sr No.",
"Transaction Date",
"Transaction Type",
"Amount",
"Comments",
"Update By",
"Update On",
...(localStorage?.getItem('role')!=="Maker" ? ["Actions"] : []),
];
const extractedArray = filteredData?.map((item, index) => ({
id: item?.id,
"Sr No.": (
<Text as={"span"} color={"gray.800"} fontWeight={"500"}>
{index + 1}.
</Text>
),
"Transaction Date": (
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
{formatDate(item?.transactionDate)}
</Text>
),
"Transaction Type": (
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
{item?.transactionType}
</Text>
),
Amount: (
<Text as={"span"} color={"gray.800"} fontWeight={"500"}>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{parseFloat(item?.transactionAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
),
Comments: (
<Text w={"100px"} as={"span"} color={"gray.800"} fontWeight={"500"}>
{item?.comments}
</Text>
),
"Update By": (
<Text
w={"100px"}
as={"span"}
color={"gray.800"}
fontWeight={"500"}
display={"flex"}
alignItems={"center"}
>
{/* <Avatar
mr={2}
size="sm"
name={item?.creator?.firstName}
src={item?.creator?.profilePhoto}
/> */}
{item?.modifier?.firstName}
</Text>
),
"Update On": (
<Text w={"100px"} as={"span"} color={"gray.800"} fontWeight={"500"}>
{formatDate(item.updatedAt)}
</Text>
),
Actions: (
<Box display={"flex"} justifyContent={"center"}>
{localStorage?.getItem("role") !== "Maker" ? <Box>
{index===0&&<Box display={"flex"} justifyContent={"center"} gap={2}>
<Tooltip
rounded={"sm"}
fontSize={"xs"}
label="Approve"
bg="#fff"
color={"green.500"}
placement="left-start"
>
<Button
// colorScheme="forestGreen"
// color="green.500"
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={2}
py={1}
onClick={() => {
setActionId(item.id);
onConfirmOpen();
}}
colorScheme="green"
variant={"solid"}
cursor={"pointer"}
>
<CheckIcon fontSize={"12px"} />
</Button>
</Tooltip>
<Tooltip
rounded={"sm"}
fontSize={"xs"}
label="Reject"
bg="#fff"
color={"red.500"}
placement="left-start"
>
<Button
colorScheme="red"
// color="red.500"
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={2}
onClick={() => {
setActionId(item.id);
onRejectOpen();
}}
py={1}
// variant={"solid"}
>
<CloseIcon fontSize={"10px"} />
</Button>
</Tooltip></Box>}
</Box> : <Button
colorScheme="green"
rounded={"sm"}
size={"xs"}
px={2}
py={1}
fontWeight={500}
onClick={() => {
setActionId(item.id);
}}
>
<ViewIcon me={"4px"} /> View
</Button>}
</Box>
),
}));
const handleDelete = () => {
const updatedSponsors = sponser.filter(
(sponsor) => sponsor.id !== actionId
);
setTimeout(() => {
setCaseDetails(updatedSponsors);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
const ioCashExporteDetails = IODetails?.ioCashStatusHistory?.Approved?.map(
(item, index) => ({
"Transaction date": item?.transactionDate,
"Transaction type": item?.transactionType,
Amount: parseFloat(item?.transactionAmount) || 0,
Comments: item?.comments,
})
);
const Total = () => {
return (
<Table size="sm">
<Tbody backgroundColor="gray.50">
<Tr>
<Th
textAlign={"center"}
p={3}
width="130px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
Balance in IO Cash
</Th>
<Th
textAlign={"center"}
p={3}
width="150px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
{" "}
</Th>
<Th
textAlign={"center"}
p={3}
width="150px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
{}
</Th>
<Th
textAlign={"center"}
p={3}
width="100px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{parseFloat(IODetails?.ioCash || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Th>
<Th
textAlign={"center"}
p={3}
width="100px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
{" "}
</Th>
<Th
textAlign={"center"}
p={3}
width="100px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
></Th>
<Th
textAlign={"center"}
p={3}
width="80px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
></Th>
<Th
textAlign={"center"}
p={3}
width="50px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
></Th>
</Tr>
</Tbody>
</Table>
);
};
const handleAdd = async () => {
try {
const res = await updateIOCase(id);
if (res?.data) {
// toast({
// render: () => (
// <ToastBox status={"success"} message={res?.data?.message} />
// ),
// });
setIsLoading(false);
onOpen();
} else if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsLoading(false);
}
} catch (error) {}
};
return (
<Box {...OPACITY_ON_LOAD} pb={0}>
<Box bg="white.500">
<HStack
display={"flex"}
justifyContent={"space-between"}
pb={3}
spacing="24px"
>
<Input
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
{/* <HStack display={"flex"} alignItems={"center"}>
{IODetails?.isInvestedAmount ? (
localStorage?.getItem('role') ==="Maker"&& <Button
onClick={handleAdd}
leftIcon={<AddIcon />}
colorScheme="forestGreen"
size={"sm"}
rounded={"sm"}
fontSize={"xs"}
>
Add
</Button>
) : null}
</HStack> */}
</HStack>
</Box>
<NormalTable
emptyMessage={`We don't have any Sponers`}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
// total={<Total />}
setMouseEnteredId={setMouseEnteredId}
setMouseEntered={setMouseEntered}
/>
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>
<AddCaseDetails
isOpen={isOpen}
onClose={onClose}
firstField={firstField}
/>
<RequestApproveModal
// data={data?.data?.rows}
isOpen={isConfirmOpen}
onClose={onConfirmClose}
id={actionId}
// firstField={firstField}
/>
<RequestRejectModal
isOpen={isRejectOpen}
onClose={onRejectClose}
id={actionId}
/>
</Box>
);
};
export default Pending;

View File

@@ -0,0 +1,357 @@
import {
Avatar,
Badge,
Box,
Button,
HStack,
Input,
Table,
Tag,
Tbody,
Text,
Th,
Tooltip,
Tr,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useRef, useState } from "react";
import { AddIcon} from "@chakra-ui/icons";
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
import NormalTable from "../../../../Components/DataTable/NormalTable";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
import AddCashDetails from "../AddCashDetails";
import AddRejected from "./AddRejected";
import { useUpdateIOCaseMutation } from "../../../../Services/io.service";
import { useParams } from "react-router-dom";
import ToastBox from "../../../../Components/ToastBox";
import AddCaseDetails from "./AddCaseDetails";
const formatDate = (date) => new Date(date).toLocaleDateString();
const Rejected = () => {
const params = useParams()
const id = params?.id
const toast = useToast();
const firstField = useRef();
const { isOpen, onOpen, onClose } = useDisclosure();
const { IODetails, approved, setApproved } =
useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
const [updateIOCase] = useUpdateIOCaseMutation()
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
// Cleanup the timer on component unmount
return () => clearTimeout(timer);
}, []);
const formatDate = (date) => {
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
};
// Table filter
const filteredData = IODetails?.ioCashStatusHistory?.Reject?.filter((item) => {
// Filter by name (case insensitive)
const name = item.transactionDate;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
return nameMatches;
});
const tableHeadRow = [
"Sr No.",
"Transaction Date",
"Transaction Type",
"Amount",
"Comments",
"Update By",
"Update On",
];
const extractedArray = filteredData?.map((item, index) => ({
id: item?.id,
"Sr No.": (
<Text
as={"span"}
color={"gray.800"}
fontWeight={"500"}
>
{index + 1}.
</Text>
),
"Transaction Date": (
<Text
as={"span"}
color={"gray.600"}
fontWeight={"500"}
>
{formatDate(item?.transactionDate)}
</Text>
),
"Transaction Type": (
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
{item?.transactionType}
</Text>
),
"Amount": (
<Text
as={"span"}
color={"gray.800"}
fontWeight={"500"}
>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{parseFloat(IODetails?.ioCash || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
),
"Comments": (
<Text
w={"100px"}
as={"span"}
color={"gray.800"}
fontWeight={"500"}
>
{item?.comments}
</Text>
),
"Update By": (
<Text
w={"100px"}
as={"span"}
color={"gray.800"}
fontWeight={"500"}
display={"flex"}
alignItems={"center"}
>
{/* <Avatar
mr={2}
size="sm"
name={item.creator?.firstName}
src={item.creator?.profilePhoto}
/> */}
{item?.modifier?.firstName}
</Text>
),
"Update On": (
<Text
w={"100px"}
as={"span"}
color={"gray.800"}
fontWeight={"500"}
>
{formatDate(item.updatedAt)}
</Text>
),
}));
const handleDelete = () => {
const updatedSponsors = sponser.filter(
(sponsor) => sponsor.id !== actionId
);
setTimeout(() => {
setCaseDetails(updatedSponsors);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
const Total = () => {
return (
<Table size="sm">
<Tbody backgroundColor="gray.50">
<Tr>
<Th
textAlign={"center"}
p={3}
width="130px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
Balance in IO Cash
</Th>
<Th
textAlign={"center"}
p={3}
width="150px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
{" "}
</Th>
<Th
textAlign={"center"}
p={3}
width="150px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
{}
</Th>
<Th
textAlign={"center"}
p={3}
width="100px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
{"48,000.00"}
</Th>
<Th
textAlign={"center"}
p={3}
width="100px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
{" "}
</Th>
<Th
textAlign={"center"}
p={3}
width="100px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
></Th>
<Th
textAlign={"center"}
p={3}
width="100px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
></Th>
</Tr>
</Tbody>
</Table>
);
};
const handleAdd = async () =>{
try {
const res = await updateIOCase(id)
if (res?.data) {
// toast({
// render: () => (
// <ToastBox status={"success"} message={res?.data?.message} />
// ),
// });
setIsLoading(false);
onOpen()
} else if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsLoading(false);
}
} catch (error) {
}
}
return (
<Box {...OPACITY_ON_LOAD} pb={0}>
<Box bg="white.500">
<HStack
display={"flex"}
justifyContent={"space-between"}
pb={3}
spacing="24px"
>
<Input
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
{/* <HStack display={"flex"} alignItems={"center"}>
{IODetails?.isInvestedAmount ? (
localStorage?.getItem('role') ==="Maker"&& <Button
onClick={handleAdd}
leftIcon={<AddIcon />}
colorScheme="forestGreen"
size={"sm"}
rounded={"sm"}
fontSize={"xs"}
>
Add
</Button>
) : null}
</HStack> */}
</HStack>
</Box>
<NormalTable
emptyMessage={`We don't have any Sponers`}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
// total={<Total/>}
setMouseEnteredId={setMouseEnteredId}
setMouseEntered={setMouseEntered}
/>
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>
<AddCaseDetails
isOpen={isOpen}
onClose={onClose}
firstField={firstField}
/>
</Box>
);
};
export default Rejected;

View File

@@ -0,0 +1,176 @@
import {
Box,
Button,
FormControl,
FormHelperText,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useEffect, useState } from "react";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
import ToastBox from "../../../../Components/ToastBox";
import { useApproveIOCaseMutation } from "../../../../Services/io.service";
export const conformModalSchema = yup.object().shape({
// comments: yup.string().required("Comment is required")
// .max(50, "Investment name cannot be more than 50 characters"),
comments: yup
.string()
.required("Comment is required")
.max(200, "Approve Comment cannot be more than 200 characters"),
});
const RequestApproveModal = ({ isOpen, onClose, firstField ,id}) => {
const [isBtnLoading , setIsBtnLoading] = useState(false)
const toast = useToast()
const {
register,
reset,
watch,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
const [ approveIOCase ] = useApproveIOCaseMutation()
const onSubmit = async(data) => {
console.log(data, "tewxttttt");
setIsBtnLoading(true)
try {
const res = await approveIOCase({data,id})
if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsBtnLoading(false)
}else if(res?.data){
toast({
render: () => (
<ToastBox message={res?.data?.message} />
),
});
onClose()
setIsBtnLoading(false)
}else{
toast({
render: () => (
<ToastBox status={'error'} message={"Something went wrong"} />
),
});
setIsBtnLoading(false)
}
} catch (error) {
}
};
const handleFileChange = (event) => {
const selectedFile = event.target.files[0];
setFile(selectedFile);
};
const { data, isLoading } =
(id, {
skip: !id,
});
useEffect(() => {
if (data) {
reset({
investorAmount: data?.data?.investorAmount,
});
}
}, [data, reset]);
const heandleOnClose = () =>{
reset()
onClose()
}
return (
<Modal isCentered isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
<ModalOverlay />
<ModalContent pb={4}>
<ModalHeader fontSize={"md"}>Approve Comment</ModalHeader>
<ModalCloseButton />
{isLoading ? (
<FullscreenLoaders height={"50vh"} />
) : (
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea
rows={6}
focusBorderColor="green.400"
name="comments"
{...register("comments")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your checker comment...."}
rounded={"md"}
resize={"none"}
maxLength={200}
/>
{errors.comments && (
<Text fontSize="xs" color="red">
{errors.comments.message}
</Text>
)}
<FormHelperText fontSize="xs" color="gray.500">
<Box as="span" me={1}>Maximum length should be 200 characters. You have entered </Box>
{watch("comments")?.length || 0} characters.
</FormHelperText>
</FormControl>
</ModalBody>
<ModalFooter>
<Button
colorScheme="gray"
mr={3}
onClick={onClose}
size={"sm"}
rounded={"sm"}
>
Cancel
</Button>
<Button
colorScheme="forestGreen"
variant="solid"
size={"sm"}
rounded={"sm"}
isLoading={isBtnLoading}
type="submit"
>
Send
</Button>
</ModalFooter>
</Box>
)}
</ModalContent>
</Modal>
);
};
export default RequestApproveModal;

View File

@@ -0,0 +1,172 @@
import {
Box,
Button,
FormControl,
FormHelperText,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useEffect, useState } from "react";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
import ToastBox from "../../../../Components/ToastBox";
import { useRejectIOCaseMutation } from "../../../../Services/io.service";
export const conformModalSchema = yup.object().shape({
comments: yup.string().required("Comment is required")
.max(200, "Approve Comment cannot be more than 200 characters"),
});
const RequestRejectModal = ({ isOpen, onClose, firstField ,id}) => {
const [isBtnLoading , setIsBtnLoading] = useState(false)
const toast = useToast()
const {
register,
reset,
watch,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
const [ rejectIOCase ] = useRejectIOCaseMutation()
const onSubmit = async(data) => {
console.log(data, "tewxttttt");
setIsBtnLoading(true)
try {
const res = await rejectIOCase({data,id})
if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsBtnLoading(false)
}else if(res?.data){
toast({
render: () => (
<ToastBox message={res?.data?.message} />
),
});
onClose()
setIsBtnLoading(false)
}else{
toast({
render: () => (
<ToastBox status={'error'} message={"Something went wrong"} />
),
});
setIsBtnLoading(false)
}
} catch (error) {
}
};
const handleFileChange = (event) => {
const selectedFile = event.target.files[0];
setFile(selectedFile);
};
const { data, isLoading } =
(id, {
skip: !id,
});
useEffect(() => {
if (data) {
reset({
investorAmount: data?.data?.investorAmount,
});
}
}, [data, reset]);
const heandleOnClose = () =>{
reset()
onClose()
}
return (
<Modal isCentered isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
<ModalOverlay />
<ModalContent pb={4}>
<ModalHeader fontSize={"md"}>Reject Comment</ModalHeader>
<ModalCloseButton />
{isLoading ? (
<FullscreenLoaders height={"50vh"} />
) : (
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea
rows={6}
focusBorderColor="green.400"
name="comments"
{...register("comments")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your comments...."}
rounded={"md"}
resize={"none"}
maxLength={200}
/>
{errors.comments && (
<Text fontSize="xs" color="red">
{errors.comments.message}
</Text>
)}
<FormHelperText fontSize="xs" color="gray.500">
<Box as="span" me={1}>Maximum length should be 200 characters. You have entered </Box>
{watch("comments")?.length || 0} characters.
</FormHelperText>
</FormControl>
</ModalBody>
<ModalFooter>
<Button
colorScheme="gray"
mr={3}
onClick={onClose}
size={"sm"}
rounded={"sm"}
>
Cancel
</Button>
<Button
colorScheme="forestGreen"
variant="solid"
size={"sm"}
rounded={"sm"}
isLoading={isBtnLoading}
type="submit"
>
Send
</Button>
</ModalFooter>
</Box>
)}
</ModalContent>
</Modal>
);
};
export default RequestRejectModal;

View File

@@ -1,4 +1,6 @@
import { import {
Avatar,
Badge,
Box, Box,
Button, Button,
HStack, HStack,
@@ -9,22 +11,29 @@ import {
Text, Text,
Th, Th,
Tr, Tr,
useDisclosure,
useToast, useToast,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import React, { useContext, useEffect, useState } from "react"; import React, { useContext, useEffect, useRef, useState } from "react";
import { OPACITY_ON_LOAD } from "../../../Layout/animations"; import { OPACITY_ON_LOAD } from "../../../Layout/animations";
import DataTable from "../../../Components/DataTable/DataTable"; import NormalTable from "../../../Components/DataTable/NormalTable";
import Pagination from "../../../Components/Pagination"; import Pagination from "../../../Components/Pagination";
import GlobalStateContext from "../../../Contexts/GlobalStateContext"; import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../../Components/CustomAlertDialog"; import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import ToastBox from "../../../Components/ToastBox"; import ToastBox from "../../../Components/ToastBox";
import { debounce } from "../../Master/Sponser/AddSponser"; import { debounce } from "../../Master/Sponser/AddSponser";
import { AddIcon } from "@chakra-ui/icons";
import AddCashDetails from "./AddCashDetails";
import { LuFileSpreadsheet } from "react-icons/lu";
import { exportToExcel, exportToExcelNew } from "../../../Constants/Constants";
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
const IOCashDetails = () => { const IOCashDetailsOld = () => {
const toast = useToast(); const toast = useToast();
const { caseDetails, setCaseDetails, slideFromRight } = const firstField = useRef();
const { isOpen, onOpen, onClose } = useDisclosure();
const { caseDetails, setCaseDetails, IODetails } =
useContext(GlobalStateContext); useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
@@ -43,6 +52,14 @@ const IOCashDetails = () => {
return () => clearTimeout(timer); return () => clearTimeout(timer);
}, []); }, []);
const formatDate = (date) => {
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
};
// Calculate totals // Calculate totals
const totalAmount = caseDetails.reduce( const totalAmount = caseDetails.reduce(
(acc, caseDetail) => acc + caseDetail.amount, (acc, caseDetail) => acc + caseDetail.amount,
@@ -52,7 +69,7 @@ const IOCashDetails = () => {
// Table setup // Table setup
const tableHeadRow = [ const tableHeadRow = [
"Date", "Date",
"Particulars", "Transaction type",
"Amount", "Amount",
"Comments", "Comments",
"Update by ", "Update by ",
@@ -71,52 +88,60 @@ const IOCashDetails = () => {
}, 300); }, 300);
// Table filter // Table filter
const filteredData = caseDetails.filter((item) => { const filteredData = IODetails?.ioCashHistory
const name = item.date; ?.filter((item) => {
const searchLower = searchTerm.toLowerCase(); const name = item.transactionType;
const nameMatches = name.toLowerCase().includes(searchLower); const searchLower = searchTerm.toLowerCase();
return nameMatches; const nameMatches = name.toLowerCase().includes(searchLower);
}); return nameMatches;
})
.sort((b, a) => new Date(a.createdAt) - new Date(b.createdAt));
const extractedArray = filteredData?.map((item, index) => ({
const [ extractedArray, setExtractedArray ] = useState(filteredData?.map((item, index) => ({
id: item?.id, id: item?.id,
"Date": ( Date: (
<Text <Text
justifyContent={slideFromRight ? "right" : "center"} justifyContent={"center"}
as={"span"} as={"span"}
color={"teal.900"} color={"teal.900"}
fontWeight={"500"} fontWeight={"500"}
className="d-flex align-items-center web-text-small" className="d-flex align-items-center web-text-small"
> >
{item.date} {formatDate(item?.transactionDate)}
</Text> </Text>
), ),
"Particulars": ( "Transaction type": (
<Text <Text
justifyContent={slideFromRight ? "right" : "center"} justifyContent={"center"}
as={"span"} as={"span"}
color={"teal.900"} color={"teal.900"}
fontWeight={"500"} fontWeight={"500"}
className="d-flex align-items-center web-text-small" className="d-flex align-items-center web-text-small"
> >
{item.particulars} {item?.transactionType}
</Text> </Text>
), ),
"Amount": ( Amount: (
<Text <Text
justifyContent={slideFromRight ? "right" : "center"} justifyContent={"left"}
as={"span"} as={"span"}
color={"teal.900"} color={"teal.900"}
fontWeight={"500"} fontWeight={"500"}
className="d-flex align-items-center web-text-small" className="d-flex align-items-center web-text-small"
> >
{item.amount} <Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{/* {parseFloat(item.transactionAmount || 0).toLocaleString()} */}
{`${parseFloat(item.transactionAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
</Text> </Text>
), ),
"Comments": ( Comments: (
<Text <Text
justifyContent={slideFromRight ? "right" : "center"} justifyContent={"center"}
as={"span"} as={"span"}
color={"teal.900"} color={"teal.900"}
fontWeight={"500"} fontWeight={"500"}
@@ -127,27 +152,52 @@ const IOCashDetails = () => {
), ),
"Update by ": ( "Update by ": (
<Text <Text
justifyContent={slideFromRight ? "right" : "center"} justifyContent={"center"}
as={"span"} as={"span"}
color={"teal.900"} color={"teal.900"}
fontWeight={"500"} fontWeight={"500"}
gap={2}
className="d-flex align-items-center web-text-small" className="d-flex align-items-center web-text-small"
> >
{item.updateBy} <Avatar
size="sm"
name={item.creator?.firstName}
src={item.creator?.profilePhoto}
/>
{item.creator?.firstName}
</Text> </Text>
), ),
"Update On": ( "Update On": (
<Text <Text
justifyContent={slideFromRight ? "right" : "center"} justifyContent={"center"}
as={"span"} as={"span"}
color={"teal.900"} color={"teal.900"}
fontWeight={"500"} fontWeight={"500"}
className="d-flex align-items-center web-text-small" className="d-flex align-items-center web-text-small"
> >
{item.updateOn} {formatDate(item.updatedAt)}
</Text> </Text>
), ),
}))); }));
const customHeaders = [
{ label: "Date", key: "transactionDate" },
{ label: "Transaction type", key: "transactionType" },
{ label: "Amount", key: "transactionAmount" },
{ label: "Comments", key: "comments" },
// { label: "Update by", key: "creator" },
// { label: "Update On", key: "updateOn" },
// Add more headers as needed
];
const ioCashExporteDetails = IODetails?.ioCashHistory?.map((item, index) => ({
Date: item?.transactionDate,
"Transaction type": item?.transactionType,
Amount: parseFloat(item?.transactionAmount) || 0,
Comments: item?.comments,
}));
console.log(ioCashExporteDetails);
const handleDelete = () => { const handleDelete = () => {
const updatedSponsors = sponser.filter( const updatedSponsors = sponser.filter(
@@ -166,7 +216,7 @@ const IOCashDetails = () => {
return ( return (
<Table size="sm"> <Table size="sm">
<Tbody backgroundColor="gray.50"> <Tbody backgroundColor="gray.50">
<Tr > <Tr>
<Th <Th
textAlign={"center"} textAlign={"center"}
p={3} p={3}
@@ -261,17 +311,42 @@ const IOCashDetails = () => {
/> />
<HStack display={"flex"} alignItems={"center"}> <HStack display={"flex"} alignItems={"center"}>
<Pagination totalItems={10} /> <Button
onClick={() =>
exportToExcelNew(ioCashExporteDetails, "IO Cash History")
}
leftIcon={<LuFileSpreadsheet />}
colorScheme="forestGreen"
size={"sm"}
variant={"outline"}
rounded={"sm"}
fontSize={"xs"}
isDisabled={ioCashExporteDetails?.length === 0}
>
Export xls
</Button>
{IODetails?.isInvestedAmount ? (
<Button
onClick={onOpen}
leftIcon={<AddIcon />}
colorScheme="forestGreen"
size={"sm"}
rounded={"sm"}
fontSize={"xs"}
>
Add IO Cash
</Button>
) : null}
</HStack> </HStack>
</HStack> </HStack>
</Box> </Box>
<DataTable <NormalTable
centered={true} centered={true}
emptyMessage={`We don't have any Sponers`} emptyMessage={`We don't have any Sponers`}
tableHeadRow={tableHeadRow} tableHeadRow={tableHeadRow}
data={extractedArray} data={extractedArray}
setData={setExtractedArray}
isLoading={isLoading} isLoading={isLoading}
viewActionId={actionId} viewActionId={actionId}
setViewActionId={setActionId} setViewActionId={setActionId}
@@ -287,8 +362,14 @@ const IOCashDetails = () => {
alertHandler={handleDelete} alertHandler={handleDelete}
isLoading={isLoading} isLoading={isLoading}
/> />
<AddCashDetails
isOpen={isOpen}
onClose={onClose}
firstField={firstField}
/>
</Box> </Box>
); );
}; };
export default IOCashDetails; export default IOCashDetailsOld;

View File

@@ -6,9 +6,6 @@ import { useForm } from "react-hook-form";
import * as yup from "yup"; import * as yup from "yup";
import GlobalStateContext from "../../../Contexts/GlobalStateContext"; import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import FullscreenLoaders from "../../../Components/Loaders/FullscreenLoaders"; import FullscreenLoaders from "../../../Components/Loaders/FullscreenLoaders";
import { generateUniqueId } from "../../../Contexts/GlobalStateProvider";
import { useGetInvestmentTypesQuery } from "../../../Services/investment.type.service";
import { useGetActiveSponserMasterQuery } from "../../../Services/sponser.service";
import { import {
useCreateIOMutation, useCreateIOMutation,
useGetIOByIdQuery, useGetIOByIdQuery,
@@ -16,32 +13,19 @@ import {
} from "../../../Services/io.service"; } from "../../../Services/io.service";
import ToastBox from "../../../Components/ToastBox"; import ToastBox from "../../../Components/ToastBox";
import { import {
Input,
Table,
Tbody,
Td,
Th,
Thead,
Tr,
useToast, useToast,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { formatDate } from "../../Master/Sponser/Sponsers"; import { formatDatee } from "../../../Components/FormField";
import { formatDateToYYYYMMDD, removeTrailingZeros } from "../../../Constants/Constants";
import behrain from "../../../assets/bahrain_flag.png";
import kuwait from "../../../assets/kuwait_flag.png";
import oman from "../../../assets/oman_flag.png";
import qatar from "../../../assets/qatar_flag.png";
import uae from "../../../assets/uae_flag.png";
import saudi from "../../../assets/saudi_arabia_flag.png";
const schema = yup.object().shape({ const schema = yup.object().shape({
investmentNameEnglish: yup investmentNameEnglish: yup
.string() .string()
.required("IO name in English is required") .required("IO name in English is required")
.min(3, "IO name in English must be at least 3 characters long") .min(3, "IO name in English must be at least 3 characters long")
.max(50, "IO name in English must be at most 50 characters long"), .max(150, "IO name in English must be at most 150 characters long"),
investmentNameArabic: yup investmentNameArabic: yup
.string() .string()
.required("IO name in Arabic is required") .required("IO name in Arabic is required")
.min(3, "IO name in Arabic must be at least 3 characters long") .min(3, "IO name in Arabic must be at least 3 characters long")
@@ -58,19 +42,22 @@ const schema = yup.object().shape({
.required("Description in Arabic is required") .required("Description in Arabic is required")
.min(10, "Description in Arabic must be at least 10 characters long") .min(10, "Description in Arabic must be at least 10 characters long")
.max(2000, "Description in Arabic must be at most 500 characters long"), .max(2000, "Description in Arabic must be at most 500 characters long"),
expectedReturnArabic: yup
.string()
.required("Expected return in Arabic is required"),
goalAmount: yup goalAmount: yup
.number() .number()
.required("Goal amount is required") .typeError("Goal Amount is must be number")
.positive("Goal amount must be a positive number") .required('Goal amount is required')
.min(1, "Goal amount must be at least 1"), .positive('Goal amount must be a positive number'),
closingDate: yup closingDate: yup
.date() .date()
.notRequired("Closing date is required") .notRequired("Closing date is required")
.min(new Date(), "Closing date cannot be in the past"), .min(new Date(), "Closing date cannot be in the past"),
holdingPeriod: yup.string().required("Holding period is required"), holdingPeriod: yup.string().required("Holding period is required"),
holdingPeriodArabic: yup.string().required("Holding period is required"),
// minInvestmentAmount: yup // minInvestmentAmount: yup
// .number() // .number()
@@ -83,7 +70,7 @@ const schema = yup.object().shape({
InvestmentDetails: yup.string().notRequired(), InvestmentDetails: yup.string().notRequired(),
comment: yup.string().notRequired() comment: yup.string().notRequired()
.min(10, "Comment must be at least 10 characters long") // .min(10, "Comment must be at least 10 characters long")
.max(100, "Comment must be at most 100 characters long"), .max(100, "Comment must be at most 100 characters long"),
expectedReturn: yup expectedReturn: yup
@@ -91,19 +78,29 @@ const schema = yup.object().shape({
.required("Expected return is required"), .required("Expected return is required"),
}); });
const IODetails = ({ enableNextTab, index, data }) => { const IODetails = ({ enableNextTab, index, data }) => {
const params = useParams(); const params = useParams();
const navigate = useNavigate(); const navigate = useNavigate();
const toast = useToast(); const toast = useToast();
const handleInputChange = (index, newValue) => { const handleInputChangeCreate = (index, newValue) => {
const updatedValues = [...values]; const updatedValues = [...values];
updatedValues[index].value = newValue; updatedValues[index].value = newValue;
setValues(updatedValues); setValues(updatedValues);
console.log(values);
};
const handleInputChangeEdit = (index, newValue) => {
// Allow only whole numbers using regex
if (/^\d*$/.test(newValue)) {
const updatedValues = [...values];
updatedValues[index].value = newValue;
setValues(updatedValues);
console.log(values);
}
}; };
// ======================[ States ] // ======================[ States ]
@@ -113,7 +110,7 @@ const IODetails = ({ enableNextTab, index, data }) => {
const id = params?.id; const id = params?.id;
// ======================[ Cotext Api ] // ======================[ Cotext Api ]
const { investmentType, sponser, IODetails, setIODetails } = const { investmentType, sponser, setIOStatus, setIODetails, setIOloading } =
useContext(GlobalStateContext); useContext(GlobalStateContext);
// ======================[ RTK Querry Api ] // ======================[ RTK Querry Api ]
@@ -123,9 +120,8 @@ const IODetails = ({ enableNextTab, index, data }) => {
isLoading: IObyIDisLoading, isLoading: IObyIDisLoading,
error: IObyIDerror, error: IObyIDerror,
} = useGetIOByIdQuery(id, { skip: !id }); } = useGetIOByIdQuery(id, { skip: !id });
console.log(IObyID);
const [creatIO] = useCreateIOMutation(); const [creatIO] = useCreateIOMutation();
const [updateIO] = useUpdateIOMutation(); const [updateIO] = useUpdateIOMutation();
@@ -147,43 +143,121 @@ const IODetails = ({ enableNextTab, index, data }) => {
}); });
const miniValue = data?.country?.map( const miniValue = data?.country?.map(
({ countryName, flagIcon, minInvestmentAmt, countryCode, id }, index) => { ({ countryName, flagIcon, minInvestmentAmt, countryCode, id, currency }, index) => {
return { return {
id:id, id:id,
country: countryName, country: countryName,
value: minInvestmentAmt, value: minInvestmentAmt,
logo: flagIcon, logo: flagIcon,
curr: countryCode, curr: currency?.currencyCode,
}; };
} }
); );
const minInvestmentById = IObyID?.data?.minInvestmentAmt?.map(({minInvestmentAmt, country, country_xid})=>{ const minInvestmentById = IObyID?.data?.minInvestmentAmt?.map(({minInvestmentAmt, country, currencyCode, country_xid,id })=>{
console.log(currencyCode);
return{ return{
_id:id,
id:country_xid, id:country_xid,
country: country?.countryName, country: country?.countryName,
value: minInvestmentAmt, value: removeTrailingZeros(minInvestmentAmt),
logo: country?.flagIcon, logo: country?.flagIcon,
curr: country?.countryCode, curr: currencyCode,
} }
}) })
const schemaEdit = yup.object().shape({
investmentNameEnglish: yup
.string()
.required("IO name in English is required")
.min(3, "IO name in English must be at least 3 characters long")
.max(150, "IO name in English must be at most 150 characters long"),
investmentNameArabic: yup
.string()
.required("IO name in Arabic is required")
.min(3, "IO name in Arabic must be at least 3 characters long")
.max(50, "IO name in Arabic must be at most 50 characters long"),
descriptionEnglish: yup
.string()
.required("Description in English is required")
.min(10, "Description in English must be at least 10 characters long")
.max(1000, "Description in English must be at most 1000 characters long"),
descriptionArabic: yup
.string()
.required("Description in Arabic is required")
.min(10, "Description in Arabic must be at least 10 characters long")
.max(2000, "Description in Arabic must be at most 500 characters long"),
expectedReturnArabic: yup
.string()
.required("Expected return in Arabic is required"),
goalAmount: yup
.number()
.typeError("Goal Amount is must be number")
.required('Goal amount is required')
.positive('Goal amount must be a positive number')
.min(IObyID?.data?.totalAmtInvestmentInUSD, `Goal amount should not be lesser then amount raised ${IObyID?.data?.totalAmtInvestmentInUSD}`),
closingDate: yup
.date()
.notRequired("Closing date is required")
.min(new Date(), "Closing date cannot be in the past"),
holdingPeriod: yup.string().required("Holding period is required"),
holdingPeriodArabic: yup.string().required("Holding period is required"),
isShariah: yup.string().required("CheckBox is required"),
// minInvestmentAmount: yup
// .number()
// .required("Minimum investment is required")
// .positive("Minimum investment must be a positive number")
// .min(1, "Minimum investment must be at least 1"),
ISIN: yup.string().notRequired(),
InvestmentDetails: yup.string().notRequired(),
comment: yup.string().notRequired()
.min(10, "Comment must be at least 10 characters long")
.max(100, "Comment must be at most 100 characters long"),
expectedReturn: yup
.string()
.required("Expected return is required"),
});
const [values, setValues] = useState(id?minInvestmentById:miniValue); const [values, setValues] = useState(id?minInvestmentById:miniValue);
console.log(values);
const formatNumber = (num) => {
// Remove non-numeric characters and format with commas
return num.replace(/\D/g, '')
.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
};
// console.log(values); // console.log(values);
// ======================[ Validator filter ] // ======================[ Validator filter ]
const { const {
control, control,
reset, reset,
watch,
setValue, setValue,
handleSubmit, handleSubmit,
formState: { errors }, formState: { errors },
} = useForm({ } = useForm({
resolver: yupResolver(schema), resolver: yupResolver(id ? schemaEdit : schema),
}); });
useEffect(() => { useEffect(() => {
setIOloading(IObyIDisLoading)
setIODetails({ setIODetails({
...IObyID?.data, ...IObyID?.data,
}); });
@@ -194,23 +268,22 @@ const IODetails = ({ enableNextTab, index, data }) => {
descriptionEnglish: IObyID?.data?.descriptionEnglish, descriptionEnglish: IObyID?.data?.descriptionEnglish,
descriptionArabic: IObyID?.data?.descriptionArabic, descriptionArabic: IObyID?.data?.descriptionArabic,
goalAmount: IObyID?.data?.goalAmount, goalAmount: IObyID?.data?.goalAmount,
closingDate: IObyID?.data?.closingDate, closingDate: formatDatee(IObyID?.data?.closingDate),
holdingPeriod: IObyID?.data?.holdingPeriod, holdingPeriod: IObyID?.data?.holdingPeriod,
ISIN: IObyID?.data?.ISIN, ISIN: IObyID?.data?.ISIN,
comment: IObyID?.data?.comment, comment: IObyID?.data?.comment,
expectedReturn: IObyID?.data?.expectedReturn, expectedReturn: IObyID?.data?.expectedReturn,
investmentType_xid: IObyID?.data?.investmentType_xid, investmentType_xid: IObyID?.data?.investmentType_xid,
investmentType_xid: IObyID?.data?.investmentType_xid,
InvestmentDetails: IObyID?.data?.InvestmentDetails, InvestmentDetails: IObyID?.data?.InvestmentDetails,
minInvestmentAmount: IObyID?.data?.minInvestmentAmount, minInvestmentAmount: IObyID?.data?.minInvestmentAmount,
holdingPeriodArabic: IObyID?.data?.minInvestmentAmount,
expectedReturnArabic: IObyID?.data?.minInvestmentAmount,
isShariah: IObyID?.data?.isShariah
}); });
} }
}, [id, IObyID]); }, [id, IObyID]);
// const minInvestmentById =
//=======================[ Creator ] //=======================[ Creator ]
const formFields = [ const formFields = [
{ {
@@ -221,6 +294,8 @@ const IODetails = ({ enableNextTab, index, data }) => {
isRequired: true, isRequired: true,
section: " ", section: " ",
width: "49%", width: "49%",
maxLength:150,
helperText:`Maximum length should be 150 characters. You have entered ${watch()?.investmentNameEnglish?.length || 0} characters.`
}, },
{ {
label: "IO Name (Arabic)", label: "IO Name (Arabic)",
@@ -231,6 +306,8 @@ const IODetails = ({ enableNextTab, index, data }) => {
arabic: true, arabic: true,
section: " ", section: " ",
width: "49%", width: "49%",
maxLength:150,
helperText:`Maximum length should be 150 characters. You have entered ${watch()?.investmentNameArabic?.length || 0} characters.`
}, },
{ {
label: "Description", label: "Description",
@@ -240,7 +317,8 @@ const IODetails = ({ enableNextTab, index, data }) => {
isRequired: true, isRequired: true,
section: " ", section: " ",
width: "49%", width: "49%",
maxLength:1000 maxLength:1000,
helperText:`Maximum length should be 1000 characters. You have entered ${watch()?.descriptionEnglish?.length || 0} characters.`
}, },
{ {
label: "Description (Arabic)", label: "Description (Arabic)",
@@ -251,8 +329,69 @@ const IODetails = ({ enableNextTab, index, data }) => {
arabic: true, arabic: true,
section: " ", section: " ",
width: "49%", width: "49%",
maxLength:1000,
helperText:`Maximum length should be 1000 characters. You have entered ${watch()?.descriptionArabic?.length || 0} characters.`
}, },
{
label: "Holding Period",
name: "holdingPeriod",
type: "text",
placeHolder: "1Y",
isRequired: true,
section: " ",
width: "49%",
value: IObyID?.data?.holdingPeriod,
maxLength:20,
helperText:`Maximum length should be 20 characters. You have entered ${watch()?.holdingPeriod?.length || 0} characters.`
},
{
label: "Holding Period (Arabic)",
name: "holdingPeriodArabic",
type: "text",
placeHolder: "1Y",
isRequired: true,
arabic: true,
section: " ",
width: "49%",
value: IObyID?.data?.holdingPeriodArabic,
maxLength:20,
helperText:`Maximum length should be 20 characters. You have entered ${watch()?.holdingPeriodArabic?.length || 0} characters.`
},
{
label: "Expected Return",
name: "expectedReturn",
type: "text",
isRequired: true,
section: " ",
width: "32.3%",
value: IObyID?.data?.expectedReturn,
},
{
label: "Expected Return (Arabic)",
name: "expectedReturnArabic",
type: "text",
isRequired: true,
arabic: true,
section: " ",
width: "32.3%",
value: IObyID?.data?.expectedReturnArabic,
},
{
label: "Shariah",
name: "isShariah",
type: "checkBox",
value:IObyID?.data?.isShariah,
// isRequired: true,
section: " ",
width: "32.3%",
value: IObyID?.data?.isShariah,
},
{ {
label: "Investment Type", label: "Investment Type",
placeHolder: "Select option", placeHolder: "Select option",
@@ -265,7 +404,7 @@ const IODetails = ({ enableNextTab, index, data }) => {
value: IObyID?.data?.investmentType_xid, value: IObyID?.data?.investmentType_xid,
}, },
{ {
label: "Sponsorer Name", label: "Sponsor Name",
placeHolder: "Select option", placeHolder: "Select option",
name: "sponserName", name: "sponserName",
type: "select", type: "select",
@@ -288,32 +427,14 @@ const IODetails = ({ enableNextTab, index, data }) => {
{ {
label: "Closing Date", label: "Closing Date",
name: "closingDate", name: "closingDate",
value: formatDate(IObyID?.data?.closingDate), // value: "IObyID?.data?.closingDate",
type: "date", type: "date",
isRequired: true, isRequired: true,
section: " ", section: " ",
width: "32.3%", width: "32.3%",
helperText: IObyID && `Current closing date is : ${formatDate(IObyID?.data?.closingDate)}` dateValue:formatDatee(IObyID?.data?.closingDate),
}, // helperText: IObyID && `Current closing date is : ${formatDate(IObyID?.data?.closingDate)}`
{ closingDate:true
label: "Holding Period",
name: "holdingPeriod",
type: "text",
placeHolder: "1Y",
isRequired: true,
section: " ",
width: "32.3%",
value: IObyID?.data?.holdingPeriod,
},
{
label: "Expected Return",
placeHolder: "$00.00",
name: "expectedReturn",
type: "text",
isRequired: true,
section: " ",
width: "32.3%",
value: IObyID?.data?.expectedReturn,
}, },
{ {
label: "ISIN", label: "ISIN",
@@ -332,6 +453,8 @@ const IODetails = ({ enableNextTab, index, data }) => {
section: " ", section: " ",
width: "32.3%", width: "32.3%",
value: IObyID?.data?.InvestmentDetails, value: IObyID?.data?.InvestmentDetails,
maxLength:20,
helperText:`Maximum length should be 20 characters. You have entered ${watch()?.InvestmentDetails?.length || 0} characters.`
}, },
{ {
@@ -340,11 +463,10 @@ const IODetails = ({ enableNextTab, index, data }) => {
name: "table", name: "table",
type: "table", type: "table",
section: " ", section: " ",
width: "100%", width: "100%",
isRequired: true, isRequired: true,
options: investmentTypeOptions, options: investmentTypeOptions,
type: "table", handleInputChange:id ? handleInputChangeEdit : handleInputChangeCreate,
handleInputChange: handleInputChange,
value: values, value: values,
}, },
@@ -357,154 +479,10 @@ const IODetails = ({ enableNextTab, index, data }) => {
width: "100%", width: "100%",
options: investmentTypeOptions, options: investmentTypeOptions,
value: IObyID?.data?.comment, value: IObyID?.data?.comment,
maxLength:100 maxLength:100,
helperText:`Maximum length should be 100 characters. You have entered ${watch()?.comment?.length || 0} characters.`
}, },
]; ];
//=======================[ Editor ]
// const formEditFields = [
// {
// label: "IO Name",
// value: IObyID?.data?.investmentNameEnglish,
// name: "investmentNameEnglish",
// type: "text",
// section: " ",
// width: "49%",
// isRequired: true,
// },
// {
// label: "IO Name (Arabic)",
// name: "investmentNameArabic",
// type: "text",
// value: IObyID?.data?.investmentNameArabic,
// isRequired: true,
// arabic: true,
// section: " ",
// width: "49%",
// },
// {
// label: "Description",
// name: "descriptionEnglish",
// value: IObyID?.data?.descriptionEnglish,
// type: "textarea",
// isRequired: true,
// section: " ",
// width: "49%",
// },
// {
// label: "Description (Arabic)",
// name: "descriptionArabic",
// value: IObyID?.data?.descriptionArabic,
// type: "textarea",
// isRequired: true,
// arabic: true,
// section: " ",
// width: "49%",
// },
// {
// label: "Goal Amount",
// placeHolder: "$00.00",
// value: IObyID?.data?.goalAmount,
// name: "goalAmount",
// type: "number",
// isRequired: true,
// section: " ",
// width: "32.3%",
// },
// {
// label: "Closing Date",
// name: "closingDate",
// type: "date",
// isRequired: true,
// value: IObyID?.data?.closingDate,
// section: " ",
// width: "32.3%",
// },
// {
// label: "Holding Period",
// name: "holdingPeriod",
// value: IObyID?.data?.holdingPeriod,
// type: "number",
// isRequired: true,
// placeHolder: "1Y",
// section: " ",
// width: "32.3%",
// },
// {
// label: "Minimum Investment Amount",
// placeHolder: "$00.00",
// name: "minInvestmentAmount",
// value: IObyID?.data?.minInvestmentAmount,
// type: "number",
// isRequired: true,
// section: " ",
// width: "32.3%",
// },
// {
// label: "ISIN",
// placeHolder: "$00.00",
// name: "ISIN",
// value: IObyID?.data?.ISIN,
// type: "number",
// section: " ",
// width: "32.3%",
// },
// {
// label: "Investment Details",
// placeHolder: "",
// name: "InvestmentDetails",
// value: IObyID?.data?.InvestmentDetails,
// type: "text",
// section: " ",
// width: "32.3%",
// },
// {
// label: "Expected Return Estimated",
// placeHolder: "$00.00",
// name: "expectedReturn",
// type: "number",
// isRequired: true,
// value: IObyID?.data?.expectedReturn,
// section: " ",
// width: "32.3%",
// },
// {
// label: "Investment Type",
// placeHolder: "Select option",
// value: IObyID?.data?.investmentType_xid,
// name: "investmentType_xid",
// type: "select",
// isRequired: true,
// section: " ",
// width: "32.3%",
// options: investmentTypeOptions,
// },
// {
// label: "Sponsorer Name",
// placeHolder: "Select option",
// name: "sponsor_xid",
// type: "select",
// options: sponserNameOption,
// value: IObyID?.data?.sponsor_xid,
// section: " ",
// isRequired: true,
// width: "32.3%",
// },
// {
// label: "Comment",
// placeHolder: "Enter comment here",
// name: "comment",
// type: "textarea",
// value: IObyID?.data?.comment,
// section: " ",
// width: "100%",
// // options: investmentTypeOptions,
// },
// ];
// ======================[ Form Contructor Filter ]
const groupedFields = formFields.reduce((groups, field) => { const groupedFields = formFields.reduce((groups, field) => {
const { section } = field; const { section } = field;
if (!groups[section]) { if (!groups[section]) {
@@ -515,27 +493,30 @@ const IODetails = ({ enableNextTab, index, data }) => {
}, {}); }, {});
const onSubmit = async (data) => { const onSubmit = async (data) => {
delete data.table;
setIsLoading(true);
// console.log(data);
const updatedMinAmount = values?.map(({id, value})=>{ delete data.table;
setIsLoading(true);
const updatedMinAmount = values?.map(({id, value, _id})=>{
return { return {
id:_id,
country_xid:id, country_xid:id,
minInvestmentAmt: Number(value) minInvestmentAmt: Number(value)
} }
}) })
// console.log(formatDateToYYYYMMDD(data.closingDate));
const formData = { const formData = {
...data, ...data,
investmentType_xid: Number(data.investmentType), investmentType_xid: Number(data.investmentType),
sponsor_xid: Number(data.sponserName), sponsor_xid: Number(data.sponserName),
minInvestmentAmt:updatedMinAmount minInvestmentAmt:updatedMinAmount,
closingDate: formatDateToYYYYMMDD(data.closingDate)
}; };
// console.log(formData);
// console.log(formData); // console.log(formData);
if (id) { if (id) {
console.log("========================",formData);
const res = await updateIO({ data: formData, id }); const res = await updateIO({ data: formData, id });
console.log(res); console.log(res);
if (res?.data?.statusCode === 200) { if (res?.data?.statusCode === 200) {
@@ -546,6 +527,11 @@ const IODetails = ({ enableNextTab, index, data }) => {
navigate(`/view-io/${id}`); navigate(`/view-io/${id}`);
enableNextTab(index); enableNextTab(index);
} else if(res?.error?.status === 400){ } else if(res?.error?.status === 400){
setIsLoading(false);
toast({
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
});
} else if(res?.error?.status === 500){
setIsLoading(false); setIsLoading(false);
toast({ toast({
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />, render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
@@ -553,6 +539,7 @@ const IODetails = ({ enableNextTab, index, data }) => {
} }
} else { } else {
try { try {
console.log("========================",formData);
const res = await creatIO(formData); const res = await creatIO(formData);
console.log(res?.error?.status); console.log(res?.error?.status);
if (res?.data?.statusCode === 200) { if (res?.data?.statusCode === 200) {
@@ -567,6 +554,11 @@ const IODetails = ({ enableNextTab, index, data }) => {
toast({ toast({
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />, render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
}); });
}else if(res?.error?.status === 500){
setIsLoading(false);
toast({
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
});
} }
} catch (error) { } catch (error) {
setIsLoading(false); setIsLoading(false);
@@ -592,8 +584,9 @@ const IODetails = ({ enableNextTab, index, data }) => {
}; };
return IObyIDisLoading ? ( return IObyIDisLoading ? (
<FullscreenLoaders /> <FullscreenLoaders height={'70vh'} />
) : ( ) : (
<FormInputMain <FormInputMain
p={0.1} p={0.1}
w={250} w={250}
@@ -604,7 +597,13 @@ const IODetails = ({ enableNextTab, index, data }) => {
onSubmit={handleSubmit(onSubmit)} onSubmit={handleSubmit(onSubmit)}
btnLoading={isLoading} btnLoading={isLoading}
submitTitle={id ? "Update" : "Submit"} submitTitle={id ? "Update" : "Submit"}
></FormInputMain> >
{/* <Box display={"flex"} justifyContent={"end"} mb={3} mt={4} me={3}>
<Checkbox colorScheme='forestGreen' display={"flex"} gap={3} flexDirection= {"row-reverse"}>
<Text as={"span"} fontWeight={500} fontSize={"sm"}>Shariah</Text>
</Checkbox>
</Box> */}
</FormInputMain>
); );
}; };

View File

@@ -1,176 +0,0 @@
import React, { useContext, useEffect, useState } from 'react'
import GlobalStateContext from '../../../Contexts/GlobalStateContext';
import { Box, HStack, Input,Text, Table, Tbody, Th, Tr } from '@chakra-ui/react';
import { OPACITY_ON_LOAD } from '../../../Layout/animations';
import Pagination from '../../../Components/Pagination';
import DataTable from '../../../Components/DataTable/DataTable';
import CustomAlertDialog from '../../../Components/CustomAlertDialog';
const IONAVDetails = () => {
const { navDetails, setNavDetails, slideFromRight } =
useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
// Cleanup the timer on component unmount
return () => clearTimeout(timer);
}, []);
// Table setup
const tableHeadRow = [
"Sr.No",
"As On Date",
"IO NAV Value",
"Comments",
"Update by ",
"Update On",
];
// Table filter
const filteredData = navDetails?.filter((item) => {
const name = item.updateBy;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
return nameMatches;
});
const [ extractedArray, setExtractedArray ] = useState(filteredData?.map((item, index) => ({
id: item?.id,
"Sr.No": index +1,
"As On Date": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.date}
</Text>
),
"IO NAV Value": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{`$${item.IONavValue}`}
</Text>
),
"Comments": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.comments}
</Text>
),
"Update by ": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.updateBy}
</Text>
),
"Update On": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.updateOn}
</Text>
),
})));
const handleDelete = () => {
const updatedNav = navDetails.filter(
(sponsor) => sponsor.id !== actionId
);
setTimeout(() => {
setNavDetails(updatedNav);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
return (<Box {...OPACITY_ON_LOAD} pb={0}>
<Box bg="white.500">
<HStack
display={"flex"}
justifyContent={"space-between"}
pb={3}
spacing="24px"
>
<Input
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<HStack display={"flex"} alignItems={"center"}>
<Pagination totalItems={10} />
</HStack>
</HStack>
</Box>
<DataTable
centered={true}
emptyMessage={`We don't have any Sponers`}
tableHeadRow={tableHeadRow}
data={extractedArray}
setData={setExtractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
caption={"Tanami v1.0"}
setMouseEnteredId={setMouseEnteredId}
setMouseEntered={setMouseEntered}
/>
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>
</Box>
)
}
export default IONAVDetails

View File

@@ -0,0 +1,266 @@
import {
Box,
Button,
Drawer,
DrawerBody,
DrawerCloseButton,
DrawerContent,
DrawerFooter,
DrawerHeader,
DrawerOverlay,
FormControl,
FormErrorMessage,
FormHelperText,
FormLabel,
HStack,
Input,
Select,
Stack,
Text,
Textarea,
VStack,
useToast,
} from "@chakra-ui/react";
import * as yup from "yup";
import React, { useState, useEffect, useContext } from "react";
import { useForm, Controller } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import { v4 as uuidv4 } from "uuid";
import { useParams } from "react-router-dom";
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
import ToastBox from "../../../../Components/ToastBox";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import CurrencyInput from "../../../../Components/CurrencyInput";
import { useAddNavDetailsMutation } from "../../../../Services/io.service";
import { formatDatee } from "../../../../Components/FormField";
const ioNav = yup.object().shape({
transactionDate: yup.string().required("Date is required"),
transactionAmount: yup.number().required("New NAV is required"),
comments: yup.string().notRequired()
.max(200, "Approve Comment cannot be more than 200 characters"),
});
const AddNavDetails = ({ isOpen, onClose, firstField, actionId, setActionId, data }) => {
const params = useParams()
const id = params?.id
const [file, setFile] = useState("");
const [fileName, setFileName] = useState("");
const [isLoading, setIsLoading] = useState(false)
const [alert, setAlert] = useState(false);
const toast = useToast();
// ======================[ Cotext Api ]
const { IODetails } = useContext(GlobalStateContext);
const found = data?.find((item) => item?.id === actionId);
const [addNavDetails] = useAddNavDetailsMutation()
// const {
// data
// } = useGetArtifactsQuery(id)
const {
control,
handleSubmit,
watch,
reset,
formState: { errors },
} = useForm({
resolver: yupResolver(ioNav),
});
const onSubmit = async (data) => {
setIsLoading(true)
try {
const res = await addNavDetails({ data, id })
if (res?.data?.statusCode === 201) {
setIsLoading(false);
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
handleClose()
}else if(res?.error?.status === 400){
toast({
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
});
handleClose()
}
} catch (error) {
console.log(error);
}
};
const handleConfirm = () => {
handleSubmit(onSubmit)();
};
const handleSave = () => {
handleSubmit(onSubmit)();
};
const handleClose = () => {
setIsLoading(false);
setAlert(false)
onClose()
reset({
transactionDate:"",
transactionAmount:"",
comments:""
})
}
const today = formatDatee(new Date(), 'yyyy-MM-dd');
function calculatePercentage(newNav, currNav) {
const per = (newNav - currNav) / currNav * 100
return per.toFixed(2)
}
console.log(calculatePercentage(1092500, 976070));
return (
<>
<Drawer
size={"md"}
isOpen={isOpen}
placement="right"
initialFocusRef={firstField}
onClose={handleClose}
>
<DrawerOverlay />
<DrawerContent>
<DrawerCloseButton />
<DrawerHeader fontSize={"sm"}>IO Nav Details</DrawerHeader>
<DrawerBody>
<Stack spacing={4}>
<FormControl isInvalid={errors.transactionDate} isRequired>
<FormLabel fontSize={"sm"}>Date Selection</FormLabel>
<Controller
name="transactionDate"
control={control}
render={({ field }) => (
<Input {...field}
max={today} // Set max attribute to todays date
fontSize={"sm"} type="date" size={"sm"} />
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.transactionDate?.message}
</FormErrorMessage>
</FormControl>
<FormControl isInvalid={errors.transactionAmount} isRequired>
<FormLabel fontSize={"sm"}>New NAV</FormLabel>
<Controller
name="transactionAmount"
control={control}
render={({ field }) => (
<CurrencyInput {...field} textAlign={'right'} fontSize={"sm"} type="number" size={"sm"} />
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.transactionAmount?.message}
</FormErrorMessage>
</FormControl>
<HStack justify={'start'} gap={10} bg={'green.100'} p={3} rounded={'md'} shadow={'md'}>
<VStack align={'start'}>
<Text as={'span'} fontSize={'sm'} fontWeight={500}>Current nav</Text>
<Text as={'span'} fontSize={'sm'}>
{parseFloat(IODetails?.ioNAV || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
</VStack>
<VStack align={'start'}>
<Text as={'span'} fontSize={'sm'} fontWeight={500}>Live return %</Text>
<Text as={'span'} fontSize={'sm'}>{calculatePercentage(watch()?.transactionAmount||IODetails?.ioNAV,IODetails?.ioNAV)}</Text>
</VStack>
</HStack>
<FormControl isInvalid={errors.comments}>
<FormLabel fontSize={"sm"}>Comment</FormLabel>
<Controller
name="comments"
control={control}
render={({ field }) => (
<Textarea {...field} maxLength={200} fontSize={"sm"} type="text" size={"sm"} />
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.comments?.message}
</FormErrorMessage>
<FormHelperText fontSize="xs" color="gray.500">
<Box as="span" me={1}>Maximum length should be 200 characters. You have entered </Box>
{watch("comments")?.length || 0} characters.
</FormHelperText>
</FormControl>
</Stack>
</DrawerBody>
<DrawerFooter>
<Button
variant="outline"
colorScheme={"forestGreen"}
rounded={"sm"}
size={"sm"}
mr={3}
onClick={handleClose}
>
Cancel
</Button>
<Button
colorScheme={"forestGreen"}
rounded={"sm"}
size={"sm"}
onClick={() => setAlert(true)}
>
Save
</Button>
</DrawerFooter>
</DrawerContent>
</Drawer>
<CustomAlertDialog
isOpen={alert}
onClose={() => setAlert(false)}
alertHandler={handleSave}
message={"Are you sure you want to add NAV details?"}
isLoading={isLoading}
/>
</>
);
};
export default AddNavDetails;

View File

@@ -0,0 +1,298 @@
import {
Avatar,
Badge,
Box,
Button,
HStack,
Input,
Table,
Tag,
Tbody,
Text,
Th,
Tooltip,
Tr,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useRef, useState } from "react";
import { AddIcon, DeleteIcon, EditIcon, ViewIcon } from "@chakra-ui/icons";
import { LuFileSpreadsheet } from "react-icons/lu";
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
import NormalTable from "../../../../Components/DataTable/NormalTable";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
import * as XLSX from "xlsx";
import ToastBox from "../../../../Components/ToastBox";
import AddCashDetails from "../AddCashDetails";
import { debounce } from "../../../Admin/Contact";
import { useParams } from "react-router-dom";
import { useUpdateIOCaseMutation } from "../../../../Services/io.service";
import AddApproved from "./AddNavDetails";
import AddNavDetails from "./AddNavDetails";
const formatDate = (date) => new Date(date).toLocaleDateString();
const Approved = () => {
const params = useParams();
const toast = useToast();
const id = params?.id;
const firstField = useRef();
const { isOpen, onOpen, onClose } = useDisclosure();
const { IODetails, iONAVDetail, setIONAVDetail } =
useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
const [updateIOCase] = useUpdateIOCaseMutation();
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
// Cleanup the timer on component unmount
return () => clearTimeout(timer);
}, []);
const formatDate = (date) => {
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
};
// Table filter
const filteredData = IODetails?.ioNAVStatusHistory?.Approved?.filter(
(item) => {
// Filter by name (case insensitive)
const name = item.transactionAmount;
const searchLower = searchTerm?.toLowerCase();
const nameMatches = name?.toLowerCase().includes(searchLower);
return nameMatches;
}
);
const tableHeadRow = [
"Sr No.",
"Valuation date",
"NAV",
"Last Nav Update",
"Investment Closed",
"Comments",
"Updated By",
];
const extractedArray = filteredData?.map((item, index) => ({
id: item?.id,
"Sr No.": (
<Text as={"span"} color={"gray.800"} fontWeight={"500"}>
{index + 1}.
</Text>
),
"Valuation date": (
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
{formatDate(item?.transactionDate)}
</Text>
),
NAV: (
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{parseFloat(item?.transactionAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
),
"Last Nav Update": (
<Text
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.previousNAVvalue && `${item.previousNAVvalue}`}
</Text>
),
"Investment Closed": (
<Text
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item?.initialNAVvalue && `${item?.initialNAVvalue}`}
</Text>
),
Comments: (
<Text w={"100px"} as={"span"} color={"gray.800"} fontWeight={"500"}>
{item?.comments ? item?.comments : "---"}
</Text>
),
"Updated By": (
<Text
w={"100px"}
as={"span"}
color={"gray.800"}
fontWeight={"500"}
display={"flex"}
alignItems={"center"}
>
{/* <Avatar
mr={2}
size="sm"
name={item.creator?.firstName}
src={item.creator?.profilePhoto}
/> */}
{item?.modifier?.firstName}
</Text>
),
}));
const handleAdd = async () => {
try {
const res = await updateIOCase(id);
if (res?.data) {
// toast({
// render: () => (
// <ToastBox status={"success"} message={res?.data?.message} />
// ),
// });
setIsLoading(false);
onOpen();
} else if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsLoading(false);
}
} catch (error) {}
};
const handleDelete = () => {
const updatedSponsors = sponser.filter(
(sponsor) => sponsor.id !== actionId
);
setTimeout(() => {
setCaseDetails(updatedSponsors);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
const exportToExcelNew = (data, fileName) => {
const worksheet = XLSX.utils.json_to_sheet(data);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1");
// Export file
XLSX.writeFile(workbook, `${fileName}.xlsx`);
};
const ioNavExport = IODetails?.ioNAVStatusHistory?.Approved?.map(
(item, index) => ({
ID: item?.id, // Keep as integer if it's already a number
"Valuation date": formatDate(item?.transactionDate), // Assuming this is a date, no conversion needed
NAV: parseFloat(item?.transactionAmount) || 0, // Convert to float
"Last Nav Update": parseFloat(item?.previousNAVvalue) || 0, // Convert to float
"Investment Closed": parseFloat(item?.initialNAVvalue) || 0, // Convert to float
Comments: item?.comments, // Keep as string
// "Transaction Type": item?.transactionType,
"Updated By": item?.creator?.firstName, // Keep as string
// "Update On": formatDate(item?.updatedAt) // Assuming this is a date, no conversion needed
})
);
return (
<Box {...OPACITY_ON_LOAD} pb={0}>
<Box bg="white.500">
<HStack
display={"flex"}
justifyContent={"space-between"}
pb={3}
spacing="24px"
>
<Input
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<HStack display={"flex"} alignItems={"center"}>
<Button
onClick={() => exportToExcelNew(ioNavExport, "Io Nav details")}
leftIcon={<LuFileSpreadsheet />}
colorScheme="forestGreen"
size={"sm"}
variant={"outline"}
rounded={"sm"}
fontSize={"xs"}
isDisabled={ioNavExport?.length === 0}
>
Export xls
</Button>
{/* {IODetails?.isInvestedAmount
? localStorage?.getItem("role") === "Maker" && (
<Button
onClick={handleAdd}
leftIcon={<AddIcon />}
colorScheme="forestGreen"
size={"sm"}
rounded={"sm"}
fontSize={"xs"}
>
Add
</Button>
)
: null} */}
</HStack>
</HStack>
</Box>
<NormalTable
emptyMessage={`We don't have any Sponers`}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
setMouseEnteredId={setMouseEnteredId}
setMouseEntered={setMouseEntered}
/>
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>
<AddNavDetails
isOpen={isOpen}
onClose={onClose}
firstField={firstField}
/>
</Box>
);
};
export default Approved;

View File

@@ -0,0 +1,190 @@
// import { Tab, TabList, TabPanel, TabPanels, Tabs } from "@chakra-ui/react";
// import React from "react";
// import Approved from "./Approved";
// import Pending from "./Pending";
// import Rejected from "./Rejected";
// const IONAVDetails = () => {
// return (
// <Tabs>
// <TabList>
// <Tab
// fontSize={"sm"}
// _selected={{
// color: "#004118",
// borderBottom: "2px solid #38a169",
// }}
// >
// Approved
// </Tab>
// <Tab
// fontSize={"sm"}
// _selected={{
// color: "#004118",
// borderBottom: "2px solid #38a169",
// }}
// >
// Pending
// </Tab>
// <Tab
// fontSize={"sm"}
// _selected={{
// color: "#004118",
// borderBottom: "2px solid #38a169",
// }}
// >
// Rejected
// </Tab>
// </TabList>
// <TabPanels>
// <TabPanel>
// <Approved />
// </TabPanel>
// <TabPanel>
// <Pending />
// </TabPanel>
// <TabPanel>
// <Rejected />
// </TabPanel>
// </TabPanels>
// </Tabs>
// );
// };
// export default IONAVDetails;
import {
Badge,
Box,
Button,
Tab,
TabList,
TabPanel,
TabPanels,
Tabs,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useRef } from "react";
import Approved from "./Approved";
import Pending from "./Pending";
import Rejected from "./Rejected";
import { AddIcon } from "@chakra-ui/icons";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import ToastBox from "../../../../Components/ToastBox";
import { useParams } from "react-router-dom";
import AddNavDetails from "./AddNavDetails";
import { useUpdateIOCaseMutation } from "../../../../Services/io.service";
const IONAVDetails = () => {
const params = useParams();
const toast = useToast();
const id = params?.id;
const { IODetails } = useContext(GlobalStateContext);
const { isOpen, onOpen, onClose } = useDisclosure();
const firstField = useRef();
const [updateIOCase] = useUpdateIOCaseMutation();
const handleAdd = async () => {
try {
const res = await updateIOCase(id);
if (res?.data) {
// toast({
// render: () => (
// <ToastBox status={"success"} message={res?.data?.message} />
// ),
// });
// setIsLoading(false);
onOpen();
} else if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsLoading(false);
}
} catch (error) {}
};
return (
<Box>
<Tabs variant="unstyled">
<Box
display={"flex"}
justifyContent={"space-between"}
alignItems={"center"}
borderBottom={"1px solid #ccc"}
>
<TabList>
<Tab
fontSize={"sm"}
_selected={{
color: "#004118",
borderBottom: "2px solid #38a169",
}}
>
Approved
</Tab>
<Tab
fontSize={"sm"}
_selected={{
color: "#004118",
borderBottom: "2px solid #38a169",
}}
>
Pending
{IODetails?.ioNAVStatusHistory?.Pending.length > 0 && (
<Badge rounded={"sm"} colorScheme="forestGreen" ms={2}>
{IODetails?.ioNAVStatusHistory?.Pending.length || 0}
</Badge>
)}
</Tab>
<Tab
fontSize={"sm"}
_selected={{
color: "#004118",
borderBottom: "2px solid #38a169",
}}
>
Rejected
</Tab>
</TabList>
{IODetails?.isInvestedAmount
? localStorage?.getItem("role") === "Maker" && (
<Button
onClick={handleAdd}
leftIcon={<AddIcon />}
colorScheme="forestGreen"
size={"sm"}
rounded={"sm"}
fontSize={"xs"}
>
Add
</Button>
)
: null}
</Box>
<TabPanels>
<TabPanel>
<Approved />
</TabPanel>
<TabPanel>
<Pending />
</TabPanel>
<TabPanel>
<Rejected />
</TabPanel>
</TabPanels>
</Tabs>
<AddNavDetails
isOpen={isOpen}
onClose={onClose}
firstField={firstField}
/>
</Box>
);
};
export default IONAVDetails;

View File

@@ -0,0 +1,338 @@
import {
Avatar,
Badge,
Box,
Button,
HStack,
Input,
Text,
Tooltip,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useRef, useState } from "react";
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
import NormalTable from "../../../../Components/DataTable/NormalTable";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
import { AddIcon, CheckIcon, CloseIcon, ViewIcon } from "@chakra-ui/icons";
import { useParams } from "react-router-dom";
import { useUpdateIOCaseMutation } from "../../../../Services/io.service";
import ToastBox from "../../../../Components/ToastBox";
import AddNavDetails from "./AddNavDetails";
import RequestApproveModal from "./RequestApproveModal";
import RequestRejectModal from "./RequestRejectModal";
const formatDate = (date) => new Date(date).toLocaleDateString();
const Pending = () => {
const params = useParams();
const toast = useToast();
const id = params?.id;
const firstField = useRef();
const { isOpen, onOpen, onClose } = useDisclosure();
const { IODetails, iONAVDetail, setIONAVDetail } =
useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
const [updateIOCase] = useUpdateIOCaseMutation();
const {
isOpen: isConfirmOpen,
onOpen: onConfirmOpen,
onClose: onConfirmClose,
} = useDisclosure();
const {
isOpen: isRejectOpen,
onOpen: onRejectOpen,
onClose: onRejectClose,
} = useDisclosure();
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
// Cleanup the timer on component unmount
return () => clearTimeout(timer);
}, []);
const formatDate = (date) => {
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
};
// Table filter
const filteredData = IODetails?.ioNAVStatusHistory?.Pending?.filter(
(item) => {
// Filter by name (case insensitive)
const name = item?.transactionDate;
const searchLower = searchTerm?.toLowerCase();
const nameMatches = name?.toLowerCase().includes(searchLower);
return nameMatches;
}
);
const tableHeadRow = [
"Sr No.",
"Valuation date",
"NAV",
"Last Nav Update",
"Investment Closed",
"Comments",
"Updated By",
...(localStorage?.getItem("role") !== "Maker" ? ["Status"] : []),
];
const extractedArray = filteredData?.map((item, index) => ({
id: item?.id,
"Sr No.": (
<Text as={"span"} color={"gray.800"} fontWeight={"500"}>
{index + 1}.
</Text>
),
"Valuation date": (
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
{formatDate(item?.transactionDate)}
</Text>
),
NAV: (
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{parseFloat(item?.transactionAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
),
"Last Nav Update": (
<Text
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.previousNAVvalue && `${item.previousNAVvalue}`}
</Text>
),
"Investment Closed": (
<Text
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item?.initialNAVvalue && `${item?.initialNAVvalue}`}
</Text>
),
Comments: (
<Text w={"100px"} as={"span"} color={"gray.800"} fontWeight={"500"}>
{item?.comments ? item?.comments : "---"}
</Text>
),
"Updated By": (
<Text
w={"100px"}
as={"span"}
color={"gray.800"}
fontWeight={"500"}
display={"flex"}
alignItems={"center"}
>
{/* <Avatar
mr={2}
size="sm"
name={item.creator?.firstName}
src={item.creator?.profilePhoto}
/> */}
{item?.modifier?.firstName}
</Text>
),
Status: (
<Box display={"flex"} justifyContent={"center"}>
<Box>
<Box display={"flex"} justifyContent={"center"} gap={2}>
<Tooltip
rounded={"sm"}
fontSize={"xs"}
label="Approve"
bg="#fff"
color={"green.500"}
placement="left-start"
>
<Button
// colorScheme="forestGreen"
// color="green.500"
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={2}
py={1}
onClick={() => {
setActionId(item.id);
onConfirmOpen();
}}
colorScheme="green"
variant={"solid"}
cursor={"pointer"}
>
<CheckIcon fontSize={"12px"} />
</Button>
</Tooltip>
<Tooltip
rounded={"sm"}
fontSize={"xs"}
label="Reject"
bg="#fff"
color={"red.500"}
placement="left-start"
>
<Button
colorScheme="red"
// color="red.500"
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={2}
onClick={() => {
setActionId(item.id);
onRejectOpen();
}}
py={1}
// variant={"solid"}
>
<CloseIcon fontSize={"10px"} />
</Button>
</Tooltip>
</Box>
</Box>
</Box>
),
}));
const handleDelete = () => {
const updatedSponsors = sponser.filter(
(sponsor) => sponsor.id !== actionId
);
setTimeout(() => {
setCaseDetails(updatedSponsors);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
const handleAdd = async () => {
try {
const res = await updateIOCase(id);
if (res?.data) {
// toast({
// render: () => (
// <ToastBox status={"success"} message={res?.data?.message} />
// ),
// });
setIsLoading(false);
onOpen();
} else if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsLoading(false);
}
} catch (error) {}
};
return (
<Box {...OPACITY_ON_LOAD} pb={0}>
<Box bg="white.500">
<HStack
display={"flex"}
justifyContent={"space-between"}
pb={3}
spacing="24px"
>
<Input
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
{/* {IODetails?.isInvestedAmount
? localStorage?.getItem("role") === "Maker" && (
<Button
onClick={handleAdd}
leftIcon={<AddIcon />}
colorScheme="forestGreen"
size={"sm"}
rounded={"sm"}
fontSize={"xs"}
>
Add
</Button>
)
: null} */}
</HStack>
</Box>
<NormalTable
emptyMessage={`We don't have any Sponers`}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
setMouseEnteredId={setMouseEnteredId}
setMouseEntered={setMouseEntered}
/>
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>
<AddNavDetails
isOpen={isOpen}
onClose={onClose}
firstField={firstField}
/>
<RequestApproveModal
// data={data?.data?.rows}
isOpen={isConfirmOpen}
onClose={onConfirmClose}
id={actionId}
// firstField={firstField}
/>
<RequestRejectModal
isOpen={isRejectOpen}
onClose={onRejectClose}
id={actionId}
/>
</Box>
);
};
export default Pending;

View File

@@ -0,0 +1,257 @@
import {
Avatar,
Badge,
Box,
Button,
HStack,
Input,
Table,
Tag,
Tbody,
Text,
Th,
Tooltip,
Tr,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useRef, useState } from "react";
import { AddIcon, DeleteIcon, EditIcon, ViewIcon } from "@chakra-ui/icons";
import { LuFileSpreadsheet } from "react-icons/lu";
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
import NormalTable from "../../../../Components/DataTable/NormalTable";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
import ToastBox from "../../../../Components/ToastBox";
import AddCashDetails from "../AddCashDetails";
import { debounce } from "../../../Admin/Contact";
import { useUpdateIOCaseMutation } from "../../../../Services/io.service";
import { useParams } from "react-router-dom";
import AddNavDetails from "./AddNavDetails";
const formatDate = (date) => new Date(date).toLocaleDateString();
const Rejected = () => {
const params = useParams();
const toast = useToast();
const id = params?.id;
const firstField = useRef();
const { isOpen, onOpen, onClose } = useDisclosure();
const { IODetails, iONAVDetail, setIONAVDetail } =
useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
const [updateIOCase] = useUpdateIOCaseMutation();
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
// Cleanup the timer on component unmount
return () => clearTimeout(timer);
}, []);
const formatDate = (date) => {
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
};
// Table filter
const filteredData = IODetails?.ioNAVStatusHistory?.Reject?.filter((item) => {
// Filter by name (case insensitive)
const name = item.transactionAmount;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
return nameMatches;
});
const tableHeadRow = [
"Sr No.",
"Valuation date",
"NAV",
"Last Nav Update",
"Investment Closed",
"Comments",
"Updated By",
];
const extractedArray = filteredData?.map((item, index) => ({
id: item?.id,
"Sr No.": (
<Text as={"span"} color={"gray.800"} fontWeight={"500"}>
{index + 1}.
</Text>
),
"Valuation date": (
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
{formatDate(item?.transactionDate)}
</Text>
),
NAV: (
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{parseFloat(item?.transactionAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
),
"Last Nav Update": (
<Text
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.previousNAVvalue && `${item.previousNAVvalue}`}
</Text>
),
"Investment Closed": (
<Text
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item?.initialNAVvalue && `${item?.initialNAVvalue}`}
</Text>
),
Comments: (
<Text w={"100px"} as={"span"} color={"gray.800"} fontWeight={"500"}>
{item?.comments ? item?.comments : "---"}
</Text>
),
"Updated By": (
<Text
w={"100px"}
as={"span"}
color={"gray.800"}
fontWeight={"500"}
display={"flex"}
alignItems={"center"}
>
{/* <Avatar
mr={2}
size="sm"
name={item.creator?.firstName}
src={item.creator?.profilePhoto}
/> */}
{item?.modifier?.firstName}
</Text>
),
}));
const handleDelete = () => {
const updatedSponsors = sponser.filter(
(sponsor) => sponsor.id !== actionId
);
setTimeout(() => {
setCaseDetails(updatedSponsors);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
const handleAdd = async () => {
try {
const res = await updateIOCase(id);
if (res?.data) {
// toast({
// render: () => (
// <ToastBox status={"success"} message={res?.data?.message} />
// ),
// });
setIsLoading(false);
onOpen();
} else if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsLoading(false);
}
} catch (error) {}
};
return (
<Box {...OPACITY_ON_LOAD} pb={0}>
<Box bg="white.500">
<HStack
display={"flex"}
justifyContent={"space-between"}
pb={3}
spacing="24px"
>
<Input
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
{/* {IODetails?.isInvestedAmount
? localStorage?.getItem("role") === "Maker" && (
<Button
onClick={handleAdd}
leftIcon={<AddIcon />}
colorScheme="forestGreen"
size={"sm"}
rounded={"sm"}
fontSize={"xs"}
>
Add
</Button>
)
: null} */}
</HStack>
</Box>
<NormalTable
emptyMessage={`We don't have any Sponers`}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
setMouseEnteredId={setMouseEnteredId}
setMouseEntered={setMouseEntered}
/>
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>
<AddNavDetails
isOpen={isOpen}
onClose={onClose}
firstField={firstField}
/>
</Box>
);
};
export default Rejected;

View File

@@ -0,0 +1,176 @@
import {
Box,
Button,
FormControl,
FormHelperText,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useEffect, useState } from "react";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
import ToastBox from "../../../../Components/ToastBox";
import { useApproveIOCaseMutation, useApproveIONavMutation } from "../../../../Services/io.service";
export const conformModalSchema = yup.object().shape({
// comments: yup.string().required("Comment is required")
// .max(50, "Investment name cannot be more than 50 characters"),
comments: yup
.string()
.required("Comment is required")
.max(200, "Approve Comment cannot be more than 200 characters"),
});
const RequestApproveModal = ({ isOpen, onClose, firstField ,id}) => {
const [isBtnLoading , setIsBtnLoading] = useState(false)
const toast = useToast()
const {
register,
reset,
watch,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
const [ approveIONav ] = useApproveIONavMutation()
const onSubmit = async(data) => {
console.log(data, "tewxttttt");
setIsBtnLoading(true)
try {
const res = await approveIONav({data,id})
if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsBtnLoading(false)
}else if(res?.data){
toast({
render: () => (
<ToastBox message={res?.data?.message} />
),
});
onClose()
setIsBtnLoading(false)
}else{
toast({
render: () => (
<ToastBox status={'error'} message={"Something went wrong"} />
),
});
setIsBtnLoading(false)
}
} catch (error) {
}
};
const handleFileChange = (event) => {
const selectedFile = event.target.files[0];
setFile(selectedFile);
};
const { data, isLoading } =
(id, {
skip: !id,
});
useEffect(() => {
if (data) {
reset({
investorAmount: data?.data?.investorAmount,
});
}
}, [data, reset]);
const heandleOnClose = () =>{
reset()
onClose()
}
return (
<Modal isCentered isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
<ModalOverlay />
<ModalContent pb={4}>
<ModalHeader fontSize={"md"}>Approve Comment</ModalHeader>
<ModalCloseButton />
{isLoading ? (
<FullscreenLoaders height={"50vh"} />
) : (
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea
rows={6}
focusBorderColor="green.400"
name="comments"
{...register("comments")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your checker comment...."}
rounded={"md"}
resize={"none"}
maxLength={200}
/>
{errors.comments && (
<Text fontSize="xs" color="red">
{errors.comments.message}
</Text>
)}
<FormHelperText fontSize="xs" color="gray.500">
<Box as="span" me={1}>Maximum length should be 200 characters. You have entered</Box>
{watch("comments")?.length || 0} characters.
</FormHelperText>
</FormControl>
</ModalBody>
<ModalFooter>
<Button
colorScheme="gray"
mr={3}
onClick={onClose}
size={"sm"}
rounded={"sm"}
>
Cancel
</Button>
<Button
colorScheme="forestGreen"
variant="solid"
size={"sm"}
rounded={"sm"}
isLoading={isBtnLoading}
type="submit"
>
Send
</Button>
</ModalFooter>
</Box>
)}
</ModalContent>
</Modal>
);
};
export default RequestApproveModal;

View File

@@ -0,0 +1,172 @@
import {
Box,
Button,
FormControl,
FormHelperText,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useEffect, useState } from "react";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
import ToastBox from "../../../../Components/ToastBox";
import { useRejectIOCaseMutation } from "../../../../Services/io.service";
export const conformModalSchema = yup.object().shape({
comments: yup.string().required("Comment is required")
.max(200, "Approve Comment cannot be more than 200 characters"),
});
const RequestRejectModal = ({ isOpen, onClose, firstField ,id}) => {
const [isBtnLoading , setIsBtnLoading] = useState(false)
const toast = useToast()
const {
register,
reset,
watch,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
const [ rejectIOCase ] = useRejectIOCaseMutation()
const onSubmit = async(data) => {
console.log(data, "tewxttttt");
setIsBtnLoading(true)
try {
const res = await rejectIOCase({data,id})
if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsBtnLoading(false)
}else if(res?.data){
toast({
render: () => (
<ToastBox message={res?.data?.message} />
),
});
onClose()
setIsBtnLoading(false)
}else{
toast({
render: () => (
<ToastBox status={'error'} message={"Something went wrong"} />
),
});
setIsBtnLoading(false)
}
} catch (error) {
}
};
const handleFileChange = (event) => {
const selectedFile = event.target.files[0];
setFile(selectedFile);
};
const { data, isLoading } =
(id, {
skip: !id,
});
useEffect(() => {
if (data) {
reset({
investorAmount: data?.data?.investorAmount,
});
}
}, [data, reset]);
const heandleOnClose = () =>{
reset()
onClose()
}
return (
<Modal isCentered isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
<ModalOverlay />
<ModalContent pb={4}>
<ModalHeader fontSize={"md"}>Reject Comment</ModalHeader>
<ModalCloseButton />
{isLoading ? (
<FullscreenLoaders height={"50vh"} />
) : (
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea
rows={6}
focusBorderColor="green.400"
name="comments"
{...register("comments")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your comments...."}
rounded={"md"}
resize={"none"}
maxLength={200}
/>
{errors.comments && (
<Text fontSize="xs" color="red">
{errors.comments.message}
</Text>
)}
<FormHelperText fontSize="xs" color="gray.500">
<Box as="span" me={1}>Maximum length should be 200 characters. You have entered </Box>
{watch("comments")?.length || 0} characters.
</FormHelperText>
</FormControl>
</ModalBody>
<ModalFooter>
<Button
colorScheme="gray"
mr={3}
onClick={onClose}
size={"sm"}
rounded={"sm"}
>
Cancel
</Button>
<Button
colorScheme="forestGreen"
variant="solid"
size={"sm"}
rounded={"sm"}
isLoading={isBtnLoading}
type="submit"
>
Send
</Button>
</ModalFooter>
</Box>
)}
</ModalContent>
</Modal>
);
};
export default RequestRejectModal;

View File

@@ -0,0 +1,298 @@
import React, { useContext, useEffect, useRef, useState } from "react";
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import {
Box,
HStack,
Input,
Text,
Table,
Tbody,
Th,
Tr,
Avatar,
useDisclosure,
Button,
Badge,
} from "@chakra-ui/react";
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
import Pagination from "../../../Components/Pagination";
import NormalTable from "../../../Components/DataTable/NormalTable";
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import { formatDatee } from "../../../Components/FormField";
import { AddIcon } from "@chakra-ui/icons";
import AddIONav from "./AddIONav";
import { formatDate } from "../../Master/Sponser/Sponsers";
import { LuFileSpreadsheet } from "react-icons/lu";
import { exportToExcel, exportToExcelNew } from "../../../Constants/Constants";
const IONAVDetails = () => {
const { navDetails, setNavDetails, IODetails } =
useContext(GlobalStateContext);
const firstField = useRef();
const { isOpen, onOpen, onClose } = useDisclosure();
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
console.log(IODetails?.ioNAVHistory);
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
// Cleanup the timer on component unmount
return () => clearTimeout(timer);
}, []);
// Table setup
const tableHeadRow = [
// "Sr.No",
"Valuation Date",
"NAV",
"Last NAV update",
"Investment Closed",
"Comments",
"Update by ",
// "Update On",
];
// Table filter
const filteredData = IODetails?.ioNAVHistory
?.filter((item) => {
const name = item.transactionType;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
return nameMatches;
})
.reverse()
// .sort((b, a) => new Date(a.transactionDate) - new Date(b.transactionDate));
const extractedArray = filteredData?.map((item, index) => ({
id: item?.id,
"Sr.No": index + 1,
"Valuation Date": (
<Text
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{/* {/ {formatDatee(item.transactionDate)} /} */}
{formatDate(item?.transactionDate)}
</Text>
),
NAV: (
<Text
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{/* {/ {`${item.transactionAmount}`} /} */}
<Badge ms={1} colorScheme="green" me={1}>$</Badge>
{`${parseFloat(item.transactionAmount || 0).toLocaleString()}`}
</Text>
),
"Last NAV update": (
<Text
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.previousNAVvalue && `${item.previousNAVvalue}`}
</Text>
),
"Investment Closed": (
<Text
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.initialNAVvalue && `${item.initialNAVvalue}`}
</Text>
),
Comments: (
// <Text
// justifyContent={"center"}
// as={"span"}
// color={"teal.900"}
// fontWeight={"500"}
// className="d-flex align-items-center web-text-small"
// >
// {item.comments}
// </Text>
<Text
as={"span"}
color={"teal.900"}
fontWeight={"500"}
maxWidth="200px" // Adjust width as needed
display="block" // Ensure block display for proper truncation
overflow="hidden"
isTruncated
textOverflow="ellipsis"
>
{item.comments}
</Text>
),
"Update by ": (
<Text
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
gap={2}
className="d-flex align-items-center web-text-small"
>
<Avatar
size="sm"
name={item.creator?.firstName}
src={item.creator?.profilePhoto}
/>
{item.creator?.firstName}
</Text>
),
"Update On": (
<Text
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{formatDate(item.updatedAt)}
</Text>
),
}));
const customHeaders = [
{ label: "ID", key: "id" },
{ label: "Valuation Date", key: "transactionDate" },
{ label: "NAV", key: "transactionAmount" },
{ label: "Last NAV update", key: "previousNAVvalue" },
{ label: "Investment Closed", key: "initialNAVvalue" },
{ label: "Comments", key: "comments" },
// { label: "Update by", key: "creator" },
{ label: "Transaction Type", key: "transactionType" },
// { label: "Comments", key: "comments" },
// Add more headers as needed
];
console.log(IODetails?.ioNAVHistory);
const ioNavExport = IODetails?.ioNAVHistory?.map((item, index) => ({
"ID": item?.id, // Keep as integer if it's already a number
"Valuation Date": formatDate(item?.transactionDate), // Assuming this is a date, no conversion needed
"NAV": parseFloat(item?.transactionAmount) || 0, // Convert to float
"Last NAV update": parseFloat(item?.previousNAVvalue) || 0, // Convert to float
"Investment Closed": parseFloat(item?.initialNAVvalue) || 0, // Convert to float
"Comments": item?.comments, // Keep as string
"Transaction Type": item?.transactionType, // Keep as string
"Update by ": item?.creator?.firstName, // Keep as string
// "Update On": formatDate(item?.updatedAt) // Assuming this is a date, no conversion needed
}));
const handleDelete = () => {
const updatedNav = navDetails.filter((sponsor) => sponsor.id !== actionId);
setTimeout(() => {
setNavDetails(updatedNav);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
const handleExport = () => {};
return (
<Box {...OPACITY_ON_LOAD} pb={0}>
<Box bg="white.500">
<HStack
display={"flex"}
justifyContent={"space-between"}
pb={3}
spacing="24px"
>
<Input
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<HStack display={"flex"} alignItems={"center"}>
<Button
onClick={() =>
exportToExcelNew(ioNavExport, "Io Nav details")
}
leftIcon={<LuFileSpreadsheet />}
colorScheme="forestGreen"
size={"sm"}
variant={"outline"}
rounded={"sm"}
fontSize={"xs"}
isDisabled={ioNavExport?.length === 0}
>
Export xls
</Button>
{IODetails?.isInvestedAmount ? (
<Button
onClick={onOpen}
leftIcon={<AddIcon />}
colorScheme="forestGreen"
size={"sm"}
rounded={"sm"}
fontSize={"xs"}
>
Add IO Nav
</Button>
) : null}
</HStack>
</HStack>
</Box>
<NormalTable
centered={true}
emptyMessage={`We don't have any Sponers`}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
caption={"Tanami v1.0"}
setMouseEnteredId={setMouseEnteredId}
setMouseEntered={setMouseEntered}
/>
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>
<AddIONav isOpen={isOpen} onClose={onClose} firstField={firstField} />
</Box>
);
};
export default IONAVDetails;

View File

@@ -0,0 +1,179 @@
import {
Box,
Button,
FormControl,
FormHelperText,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useEffect, useState } from "react";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
import ToastBox from "../../../../Components/ToastBox";
import { useApproveDistributedMutation } from "../../../../Services/io.service";
export const conformModalSchema = yup.object().shape({
// comments: yup.string().required("Comment is required")
// .max(50, "Investment name cannot be more than 50 characters"),
comments: yup
.string()
.required("Comment is required")
.max(200, "Approve Comment cannot be more than 200 characters"),
});
const ApproveDistrubationModal = ({ isOpen, onClose, firstField ,id, onBigModalClose}) => {
const [isBtnLoading , setIsBtnLoading] = useState(false)
const toast = useToast()
const {
register,
reset,
watch,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
const [ approveDistributed ] = useApproveDistributedMutation()
const onSubmit = async(data) => {
setIsBtnLoading(true)
try {
const res = await approveDistributed({data,id})
if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsBtnLoading(false)
}else if(res?.data){
toast({
render: () => (
<ToastBox message={res?.data?.message} />
),
});
onClose()
onBigModalClose()
setIsBtnLoading(false)
}else{
toast({
render: () => (
<ToastBox status={'error'} message={"Something went wrong"} />
),
});
setIsBtnLoading(false)
}
} catch (error) {
}
};
const handleFileChange = (event) => {
const selectedFile = event.target.files[0];
setFile(selectedFile);
};
const { data, isLoading } =
(id, {
skip: !id,
});
useEffect(() => {
if (data) {
reset({
investorAmount: data?.data?.investorAmount,
});
}
}, [data, reset]);
const heandleOnClose = () =>{
reset()
onClose()
}
return (
<Modal isCentered isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
<ModalOverlay />
<ModalContent pb={4}>
<ModalHeader fontSize={"md"}>Approve Comment</ModalHeader>
<ModalCloseButton />
{isLoading ? (
<FullscreenLoaders height={"50vh"} />
) : (
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea
rows={6}
focusBorderColor="green.400"
name="comments"
{...register("comments")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your checker comment...."}
rounded={"md"}
resize={"none"}
maxLength={200}
/>
{errors.comments && (
<Text fontSize="xs" color="red">
{errors.comments.message}
</Text>
)}
<FormHelperText fontSize="xs" color="gray.500">
<Box as="span" me={1}> Maximum length should be 200 characters. You have entered</Box>
{watch("comments")?.length || 0} characters.
</FormHelperText>
</FormControl>
</ModalBody>
<ModalFooter>
<Button
colorScheme="gray"
mr={3}
onClick={onClose}
size={"xs"}
rounded={"sm"}
>
Cancel
</Button>
<Button
colorScheme="forestGreen"
variant="solid"
size={"xs"}
rounded={"sm"}
isLoading={isBtnLoading}
type="submit"
>
Send
</Button>
</ModalFooter>
</Box>
)}
</ModalContent>
</Modal>
);
};
export default ApproveDistrubationModal;

View File

@@ -0,0 +1,177 @@
import {
Box,
Button,
FormControl,
FormHelperText,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useEffect, useState } from "react";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
import ToastBox from "../../../../Components/ToastBox";
import { useApproveInvestedMutation } from "../../../../Services/io.service";
export const conformModalSchema = yup.object().shape({
// comments: yup.string().required("Comment is required")
// .max(50, "Investment name cannot be more than 50 characters"),
comments: yup
.string()
.required("Comment is required")
.max(200, "Approve Comment cannot be more than 200 characters"),
});
const ApproveInvestedModal = ({ isOpen, onClose, firstField ,id,onBigModalClose}) => {
const [isBtnLoading , setIsBtnLoading] = useState(false)
const toast = useToast()
const {
register,
reset,
watch,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
const [ approveInvested ] = useApproveInvestedMutation()
const onSubmit = async(data) => {
setIsBtnLoading(true)
try {
const res = await approveInvested({data,id})
if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsBtnLoading(false)
}else if(res?.data){
toast({
render: () => (
<ToastBox message={res?.data?.message} />
),
});
onClose()
onBigModalClose()
setIsBtnLoading(false)
}else{
toast({
render: () => (
<ToastBox status={'error'} message={"Something went wrong"} />
),
});
setIsBtnLoading(false)
}
} catch (error) {
}
};
const handleFileChange = (event) => {
const selectedFile = event.target.files[0];
setFile(selectedFile);
};
const { data, isLoading } =
(id, {
skip: !id,
});
useEffect(() => {
if (data) {
reset({
investorAmount: data?.data?.investorAmount,
});
}
}, [data, reset]);
const heandleOnClose = () =>{
reset()
onClose()
}
return (
<Modal isCentered isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
<ModalOverlay />
<ModalContent pb={4}>
<ModalHeader fontSize={"md"}>Approve Comment</ModalHeader>
<ModalCloseButton />
{isLoading ? (
<FullscreenLoaders height={"50vh"} />
) : (
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea
rows={6}
focusBorderColor="green.400"
name="comments"
{...register("comments")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your checker comment...."}
rounded={"md"}
resize={"none"}
maxLength={200}
/>
{errors.comments && (
<Text fontSize="xs" color="red">
{errors.comments.message}
</Text>
)}
<FormHelperText fontSize="xs" color="gray.500">
<Box as="span" me={1}>Maximum length should be 200 characters. You have entered</Box>
{watch("comments")?.length || 0} characters.
</FormHelperText>
</FormControl>
</ModalBody>
<ModalFooter>
<Button
colorScheme="gray"
mr={3}
onClick={onClose}
size={"xs"}
rounded={"sm"}
>
Cancel
</Button>
<Button
colorScheme="forestGreen"
variant="solid"
size={"xs"}
rounded={"sm"}
isLoading={isBtnLoading}
type="submit"
>
Send
</Button>
</ModalFooter>
</Box>
)}
</ModalContent>
</Modal>
);
};
export default ApproveInvestedModal;

View File

@@ -0,0 +1,249 @@
import {
Avatar,
Badge,
Box,
Button,
HStack,
Input,
Table,
Tag,
Tbody,
Text,
Th,
Tooltip,
Tr,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useRef, useState } from "react";
import { AddIcon, DeleteIcon, EditIcon, ViewIcon } from "@chakra-ui/icons";
import { LuFileSpreadsheet } from "react-icons/lu";
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
import NormalTable from "../../../../Components/DataTable/NormalTable";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
import ToastBox from "../../../../Components/ToastBox";
import AddCashDetails from "../AddCashDetails";
import { debounce } from "../../../Admin/Contact";
const formatDate = (date) => new Date(date).toLocaleDateString();
const Approved = () => {
const toast = useToast();
const firstField = useRef();
const { isOpen, onOpen, onClose } = useDisclosure();
const { IODetails, iOTransaction, setIOTransaction } =
useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
// Cleanup the timer on component unmount
return () => clearTimeout(timer);
}, []);
const formatDate = (date) => {
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
};
// Table filter
const filteredData = IODetails?.ioTransactionRecords?.Approved?.filter((item) => {
// Filter by name (case insensitive)
const name = item?.transactionAmount;
const searchLower = searchTerm?.toLowerCase();
const nameMatches = name?.toLowerCase().includes(searchLower);
return nameMatches;
});
const tableHeadRow = [
"Sr No.",
"Transaction Date",
"Transaction Name",
"Amount",
"Created By",
"Created On",
"Approved By",
"Approved On",
];
const extractedArray = filteredData?.map((item, index) => ({
id: item?.id,
"Sr No.": (
<Text
as={"span"}
color={"gray.800"}
fontWeight={"500"}
>
{index + 1}.
</Text>
),
"Transaction Date": (
<Text
as={"span"}
color={"gray.600"}
fontWeight={"500"}
>
{formatDate(item?.transactionDate)}
</Text>
),
"Transaction Name": (
<Text
as={"span"}
color={"gray.600"}
fontWeight={"500"}
>
{item?.transactionType}
</Text>
),
"Amount": (
<Text
as={"span"}
color={"gray.600"}
fontWeight={"500"}
>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{/* {item?.transactionAmount} */}
{parseFloat(item?.transactionAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
),
"Created By": (
<Text
w={"100px"}
as={"span"}
color={"gray.800"}
fontWeight={"500"}
textTransform={'capitalize'}
>
{item?.creator?.firstName}
</Text>
),
"Created On": (
<Text
w={"100px"}
as={"span"}
color={"gray.800"}
fontWeight={"500"}
>
{formatDate(item?.createdAt)}
</Text>
),
"Approved By": (
<>
<Text
w={"100px"}
as={"span"}
color={"gray.800"}
fontWeight={"500"}
textTransform={'capitalize'}
>
{item?.modifier?.firstName}
</Text>
{/* <Text
w={"100px"}
as={"span"}
color={"gray.800"}
fontWeight={"500"}
display={"flex"}
alignItems={"center"}
>
<Avatar
mr={2}
size="sm"
name={item.creator?.firstName}
src={item.creator?.profilePhoto}
/>
{item?.creator?.firstName}
</Text> */}
</>
),
"Approved On": (
<Text
w={"100px"}
as={"span"}
color={"gray.800"}
fontWeight={"500"}
>
{item?.modifier ? formatDate(item?.updatedAt) : "---" }
{}
</Text>
),
}));
const handleDelete = () => {
const updatedSponsors = sponser.filter(
(sponsor) => sponsor.id !== actionId
);
setTimeout(() => {
setCaseDetails(updatedSponsors);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
return (
<Box {...OPACITY_ON_LOAD} pb={0}>
<Box bg="white.500">
<HStack
display={"flex"}
justifyContent={"space-between"}
pb={3}
spacing="24px"
>
<Input
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</HStack>
</Box>
<NormalTable
emptyMessage={`We don't have any Sponers`}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
setMouseEnteredId={setMouseEnteredId}
setMouseEntered={setMouseEntered}
/>
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>
</Box>
);
};
export default Approved;

View File

@@ -0,0 +1,161 @@
import {
Box,
Button,
FormControl,
FormHelperText,
FormLabel,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
useToast,
} from "@chakra-ui/react";
import React, { useEffect, useState } from "react";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
import ToastBox from "../../../../Components/ToastBox";
import {useApproveCancleTransactionMutation, useApproveExitTransactionMutation} from "../../../../Services/io.service";
const ApprovedCancelTransaction = ({ isOpen, onClose, firstField ,id,onBigModalClose}) => {
const [isBtnLoading , setIsBtnLoading] = useState(false)
const toast = useToast()
const {
register,
reset,
watch,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(),
});
const [ approveCancleTransaction ] = useApproveCancleTransactionMutation()
const onSubmit = async(data) => {
setIsBtnLoading(true)
try {
const res = await approveCancleTransaction({data,id})
if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsBtnLoading(false)
}else if(res?.data){
toast({
render: () => (
<ToastBox message={res?.data?.message} />
),
});
onClose()
onBigModalClose()
setIsBtnLoading(false)
}else{
toast({
render: () => (
<ToastBox status={'error'} message={"Something went wrong"} />
),
});
setIsBtnLoading(false)
}
} catch (error) {
}
};
const { data, isLoading } =
(id, {
skip: !id,
});
useEffect(() => {
if (data) {
reset({
investorAmount: data?.data?.investorAmount,
});
}
}, [data, reset]);
const heandleOnClose = () =>{
reset()
onClose()
}
return (
<Modal isCentered isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
<ModalOverlay />
<ModalContent pb={4}>
<ModalHeader fontSize={"md"}>Approve Comment</ModalHeader>
<ModalCloseButton />
{isLoading ? (
<FullscreenLoaders height={"50vh"} />
) : (
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea
rows={6}
focusBorderColor="green.400"
name="comments"
{...register("comments")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your checker comment...."}
rounded={"md"}
resize={"none"}
maxLength={200}
/>
{errors.comments && (
<Text fontSize="xs" color="red">
{errors.comments.message}
</Text>
)}
<FormHelperText fontSize="xs" color="gray.500">
<Box as="span" me={1}>Maximum length should be 200 characters. You have entered</Box>
{watch("comments")?.length || 0} characters.
</FormHelperText>
</FormControl>
</ModalBody>
<ModalFooter>
<Button
colorScheme="gray"
mr={3}
onClick={onClose}
size={"xs"}
rounded={"sm"}
>
Cancel
</Button>
<Button
colorScheme="forestGreen"
variant="solid"
size={"xs"}
rounded={"sm"}
isLoading={isBtnLoading}
type="submit"
>
Send
</Button>
</ModalFooter>
</Box>
)}
</ModalContent>
</Modal>
);
};
export default ApprovedCancelTransaction;

View File

@@ -0,0 +1,175 @@
import {
Box,
Button,
FormControl,
FormHelperText,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
useToast,
} from "@chakra-ui/react";
import React, { useEffect, useState } from "react";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
import ToastBox from "../../../../Components/ToastBox";
import { useApproveExitTransactionMutation } from "../../../../Services/io.service";
export const conformModalSchema = yup.object().shape({
comments: yup
.string()
.required("Comment is required")
.max(200, "Approve Comment cannot be more than 200 characters"),
});
const ApprovedExit = ({ isOpen, onClose, firstField ,id,onBigModalClose}) => {
const [isBtnLoading , setIsBtnLoading] = useState(false)
const toast = useToast()
const {
register,
reset,
watch,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
const [ approveExitTransaction ] = useApproveExitTransactionMutation()
const onSubmit = async(data) => {
setIsBtnLoading(true)
try {
const res = await approveExitTransaction({data,id})
if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsBtnLoading(false)
}else if(res?.data){
toast({
render: () => (
<ToastBox message={res?.data?.message} />
),
});
onClose()
onBigModalClose()
setIsBtnLoading(false)
}else{
toast({
render: () => (
<ToastBox status={'error'} message={"Something went wrong"} />
),
});
setIsBtnLoading(false)
}
} catch (error) {
}
};
const handleFileChange = (event) => {
const selectedFile = event.target.files[0];
setFile(selectedFile);
};
const { data, isLoading } =
(id, {
skip: !id,
});
useEffect(() => {
if (data) {
reset({
investorAmount: data?.data?.investorAmount,
});
}
}, [data, reset]);
const heandleOnClose = () =>{
reset()
onClose()
}
return (
<Modal isCentered isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
<ModalOverlay />
<ModalContent pb={4}>
<ModalHeader fontSize={"md"}>Approve Comment</ModalHeader>
<ModalCloseButton />
{isLoading ? (
<FullscreenLoaders height={"50vh"} />
) : (
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea
rows={6}
focusBorderColor="green.400"
name="comments"
{...register("comments")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your checker comment...."}
rounded={"md"}
resize={"none"}
maxLength={200}
/>
{errors.comments && (
<Text fontSize="xs" color="red">
{errors.comments.message}
</Text>
)}
<FormHelperText fontSize="xs" color="gray.500">
<Box as="span" me={1}>Maximum length should be 200 characters. You have entered</Box>
{watch("comments")?.length || 0} characters.
</FormHelperText>
</FormControl>
</ModalBody>
<ModalFooter>
<Button
colorScheme="gray"
mr={3}
onClick={onClose}
size={"xs"}
rounded={"sm"}
>
Cancel
</Button>
<Button
colorScheme="forestGreen"
variant="solid"
size={"xs"}
rounded={"sm"}
isLoading={isBtnLoading}
type="submit"
>
Send
</Button>
</ModalFooter>
</Box>
)}
</ModalContent>
</Modal>
);
};
export default ApprovedExit;

View File

@@ -0,0 +1,68 @@
import {
Badge,
Tab,
TabList,
TabPanel,
TabPanels,
Tabs,
} from "@chakra-ui/react";
import React, { useContext } from "react";
import Approved from "./Approved";
import Pending from "./Pending";
import Rejected from "./Rejected";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
const IOTransaction = () => {
const { IODetails } = useContext(GlobalStateContext);
return (
<Tabs>
<TabList>
<Tab
fontSize={"sm"}
_selected={{
color: "#004118",
borderBottom: "2px solid #38a169",
}}
>
Approved
</Tab>
<Tab
fontSize={"sm"}
_selected={{
color: "#004118",
borderBottom: "2px solid #38a169",
}}
>
Pending
{IODetails?.ioTransactionRecords?.Pending.length > 0 && (
<Badge rounded={"sm"} colorScheme="forestGreen" ms={2}>
{IODetails?.ioTransactionRecords?.Pending.length || 0}
</Badge>
)}
</Tab>
<Tab
fontSize={"sm"}
_selected={{
color: "#004118",
borderBottom: "2px solid #38a169",
}}
>
Rejected
</Tab>
</TabList>
<TabPanels>
<TabPanel>
<Approved />
</TabPanel>
<TabPanel>
<Pending />
</TabPanel>
<TabPanel>
<Rejected />
</TabPanel>
</TabPanels>
</Tabs>
);
};
export default IOTransaction;

View File

@@ -0,0 +1,295 @@
import {
Avatar,
Badge,
Box,
Button,
HStack,
Input,
Table,
Tag,
Tbody,
Text,
Th,
Tooltip,
Tr,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useRef, useState } from "react";
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
import NormalTable from "../../../../Components/DataTable/NormalTable";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
import { CheckIcon, CloseIcon, ViewIcon } from "@chakra-ui/icons";
import RequestApproveModal from "./RequestApproveModal";
import RequestRejectModal from "./RequestRejectModal";
import ViewAmountInvested from "./ViewAmountInvested";
import ViewDistributionInvestor from "./ViewDistributionInvestor";
import ViewExit from "./ViewExit";
import ViewCancel from "./ViewCancel";
const formatDate = (date) => new Date(date).toLocaleDateString();
const Pending = () => {
const toast = useToast();
const firstField = useRef();
const { isOpen, onOpen, onClose } = useDisclosure();
const { IODetails, iOTransaction, setIOTransaction } =
useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
const {
isOpen: isConfirmOpen,
onOpen: onConfirmOpen,
onClose: onConfirmClose,
} = useDisclosure();
const {
isOpen: isRejectOpen,
onOpen: onRejectOpen,
onClose: onRejectClose,
} = useDisclosure();
const {
isOpen: isInvestmentOpen,
onOpen: onInvestmentOpen,
onClose: onInvestmentClose,
} = useDisclosure();
const {
isOpen: isDistInvestorOpen,
onOpen: onDistInvestorOpen,
onClose: onDistInvestorClose,
} = useDisclosure();
const {
isOpen: isExitOpen,
onOpen: onExitOpen,
onClose: onExitClose,
} = useDisclosure();
const {
isOpen: isCancelOpen,
onOpen: onCancelOpen,
onClose: onCancelClose,
} = useDisclosure();
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
// Cleanup the timer on component unmount
return () => clearTimeout(timer);
}, []);
const formatDate = (date) => {
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
};
console.log(
"==============panding",
IODetails?.ioTransactionRecords?.Pending
);
// Table filter
// const filteredData = IODetails?.ioTransactionRecords?.Pending?.filter((item) => {
// // Filter by name (case insensitive)
// const name = item.transactionName;
// const searchLower = searchTerm?.toLowerCase();
// const nameMatches = name?.toLowerCase().includes(searchLower);
// return nameMatches;
// });
const tableHeadRow = [
"Sr No.",
"Transaction Date",
"Transaction Name",
"Amount",
"Created By",
"Created On",
"Approved By",
"Approved On",
"Actions",
];
const extractedArray = IODetails?.ioTransactionRecords?.Pending?.map(
(item, index) => ({
id: item?.id,
"Sr No.": (
<Text as={"span"} color={"gray.800"} fontWeight={"500"}>
{index + 1}.
</Text>
),
"Transaction Date": (
<Text
as={"span"}
color={"gray.600"}
fontWeight={"500"}
>
{formatDate(item?.transactionDate)}
</Text>
),
"Transaction Name": (
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
{item?.transactionType}
</Text>
),
Amount: (
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{parseFloat(item?.transactionAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
),
"Created By": (
<Text
textTransform={'capitalize'} w={"100px"} as={"span"} color={"gray.800"} fontWeight={"500"}>
{item?.creator?.firstName}
</Text>
),
"Created On": (
<Text w={"100px"} as={"span"} color={"gray.800"} fontWeight={"500"}>
{formatDate(item?.createdAt)}
</Text>
),
"Approved By": (
<Text
textTransform={'capitalize'} w={"100px"} as={"span"} color={"gray.800"} fontWeight={"500"}>
{item?.modifier?.firstName}
</Text>
),
"Approved On": (
<Text w={"100px"} as={"span"} color={"gray.800"} fontWeight={"500"}>
{item?.modifier ? formatDate(item?.updatedAt) : "---"}
</Text>
),
Actions: (
<Box display={"flex"} justifyContent={"center"} gap={2}>
<Button
colorScheme="forestGreen"
rounded={"sm"}
size={"xs"}
px={2}
py={1}
fontWeight={500}
onClick={() => {
setActionId(item.id); // Set the action ID for all cases
if (item?.transactionType === "Amount Invested") {
onInvestmentOpen();
} else if (item?.transactionType === "Distribution To Investor") {
onDistInvestorOpen();
} else if (item?.transactionType === "Exit") {
onExitOpen();
} else if (item?.transactionType === "Cancel") {
onCancelOpen();
}
}}
>
{localStorage?.getItem("role") === "Maker" ? <ViewIcon me={"4px"} /> : null} {localStorage?.getItem("role") === "Maker" ? "View" : "Approve / Reject"}
</Button>
</Box>
),
})
);
const handleDelete = () => {
const updatedSponsors = sponser.filter(
(sponsor) => sponsor.id !== actionId
);
setTimeout(() => {
setCaseDetails(updatedSponsors);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
return (
<Box {...OPACITY_ON_LOAD} pb={0}>
<Box bg="white.500">
<HStack
display={"flex"}
justifyContent={"space-between"}
pb={3}
spacing="24px"
>
<Input
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</HStack>
</Box>
<NormalTable
emptyMessage={`We don't have any Sponers`}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
setMouseEnteredId={setMouseEnteredId}
setMouseEntered={setMouseEntered}
/>
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>
<ViewAmountInvested
isOpen={isInvestmentOpen}
onClose={onInvestmentClose}
id={actionId}
/>
<ViewDistributionInvestor
isOpen={isDistInvestorOpen}
onClose={onDistInvestorClose}
id={actionId}
/>
<ViewExit
isOpen={isExitOpen}
onClose={onExitClose}
id={actionId}
/>
<ViewCancel
isOpen={isCancelOpen}
onClose={onCancelClose}
id={actionId}
/>
<RequestApproveModal
// data={data?.data?.rows}
isOpen={isConfirmOpen}
onClose={onConfirmClose}
id={actionId}
// firstField={firstField}
/>
<RequestRejectModal
isOpen={isRejectOpen}
onClose={onRejectClose}
id={actionId}
/>
</Box>
);
};
export default Pending;

View File

@@ -0,0 +1,219 @@
import {
Avatar,
Badge,
Box,
Button,
HStack,
Input,
Table,
Tag,
Tbody,
Text,
Th,
Tooltip,
Tr,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useRef, useState } from "react";
import { AddIcon, DeleteIcon, EditIcon, ViewIcon } from "@chakra-ui/icons";
import { LuFileSpreadsheet } from "react-icons/lu";
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
import NormalTable from "../../../../Components/DataTable/NormalTable";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
import ToastBox from "../../../../Components/ToastBox";
import AddCashDetails from "../AddCashDetails";
import { debounce } from "../../../Admin/Contact";
const formatDate = (date) => new Date(date).toLocaleDateString();
const Rejected = () => {
const toast = useToast();
const firstField = useRef();
const { isOpen, onOpen, onClose } = useDisclosure();
const { IODetails, iOTransaction, setIOTransaction } =
useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
// Cleanup the timer on component unmount
return () => clearTimeout(timer);
}, []);
const formatDate = (date) => {
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
};
// Table filter
// const filteredData = IODetails?.ioTransactionRecords?.Reject?.filter((item) => {
// // Filter by name (case insensitive)
// const name = item.transactionName;
// const searchLower = searchTerm.toLowerCase();
// const nameMatches = name.toLowerCase().includes(searchLower);
// return nameMatches;
// });
const tableHeadRow = [
"Sr No.",
"Transaction Name",
"Amount",
"Created By",
"Created On",
"Approved By",
"Approved On",
];
const extractedArray = IODetails?.ioTransactionRecords?.Reject?.map((item, index) => ({
id: item?.id,
"Sr No.": (
<Text
as={"span"}
color={"gray.800"}
fontWeight={"500"}
>
{index + 1}.
</Text>
),
"Transaction Name": (
<Text
as={"span"}
color={"gray.600"}
fontWeight={"500"}
>
{item?.transactionType}
</Text>
),
"Amount": (
<Text
as={"span"}
color={"gray.600"}
fontWeight={"500"}
>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{parseFloat(item?.transactionAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
),
"Created By": (
<Text
w={"100px"}
as={"span"}
color={"gray.800"}
fontWeight={"500"}
textTransform={'capitalize'}
>
{item?.creator?.firstName}
</Text>
),
"Created On": (
<Text
w={"100px"}
as={"span"}
color={"gray.800"}
fontWeight={"500"}
>
{formatDate(item?.createdAt)}
</Text>
),
"Approved By": (
<Text
w={"100px"}
as={"span"}
color={"gray.800"}
fontWeight={"500"}
textTransform={'capitalize'}
>
{item?.modifier?.firstName}
</Text>
),
"Approved On": (
<Text
w={"100px"}
as={"span"}
color={"gray.800"}
fontWeight={"500"}
>
{formatDate(item?.updatedAt)}
</Text>
),
}));
const handleDelete = () => {
const updatedSponsors = sponser.filter(
(sponsor) => sponsor.id !== actionId
);
setTimeout(() => {
setCaseDetails(updatedSponsors);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
return (
<Box {...OPACITY_ON_LOAD} pb={0}>
<Box bg="white.500">
<HStack
display={"flex"}
justifyContent={"space-between"}
pb={3}
spacing="24px"
>
<Input
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</HStack>
</Box>
<NormalTable
emptyMessage={`We don't have any Sponers`}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
setMouseEnteredId={setMouseEnteredId}
setMouseEntered={setMouseEntered}
/>
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>
</Box>
);
};
export default Rejected;

View File

@@ -0,0 +1,175 @@
import {
Box,
Button,
FormControl,
FormHelperText,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useEffect, useState } from "react";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
import ToastBox from "../../../../Components/ToastBox";
import { useApproveDistributionMutation, useApproveIOCaseMutation, useApproveIONavMutation } from "../../../../Services/io.service";
export const conformModalSchema = yup.object().shape({
// comments: yup.string().required("Comment is required")
// .max(50, "Investment name cannot be more than 50 characters"),
comments: yup
.string()
.required("Comment is required")
.max(200, "Approve Comment cannot be more than 200 characters"),
});
const RequestApproveModal = ({ isOpen, onClose, firstField ,id}) => {
const [isBtnLoading , setIsBtnLoading] = useState(false)
const toast = useToast()
const {
register,
reset,
watch,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
const [ approveDistribution ] = useApproveDistributionMutation()
const onSubmit = async(data) => {
setIsBtnLoading(true)
try {
const res = await approveDistribution({data,id})
if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsBtnLoading(false)
}else if(res?.data){
toast({
render: () => (
<ToastBox message={res?.data?.message} />
),
});
onClose()
setIsBtnLoading(false)
}else{
toast({
render: () => (
<ToastBox status={'error'} message={"Something went wrong"} />
),
});
setIsBtnLoading(false)
}
} catch (error) {
}
};
const handleFileChange = (event) => {
const selectedFile = event.target.files[0];
setFile(selectedFile);
};
const { data, isLoading } =
(id, {
skip: !id,
});
useEffect(() => {
if (data) {
reset({
investorAmount: data?.data?.investorAmount,
});
}
}, [data, reset]);
const heandleOnClose = () =>{
reset()
onClose()
}
return (
<Modal isCentered isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
<ModalOverlay />
<ModalContent pb={4}>
<ModalHeader fontSize={"md"}>Approve Comment</ModalHeader>
<ModalCloseButton />
{isLoading ? (
<FullscreenLoaders height={"50vh"} />
) : (
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea
rows={6}
focusBorderColor="green.400"
name="comments"
{...register("comments")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your checker comment...."}
rounded={"md"}
resize={"none"}
maxLength={200}
/>
{errors.comments && (
<Text fontSize="xs" color="red">
{errors.comments.message}
</Text>
)}
<FormHelperText fontSize="xs" color="gray.500">
<Box as="span" me={1}>Maximum length should be 200 characters. You have entered</Box>
{watch("comments")?.length || 0} characters.
</FormHelperText>
</FormControl>
</ModalBody>
<ModalFooter>
<Button
colorScheme="gray"
mr={3}
onClick={onClose}
size={"sm"}
rounded={"sm"}
>
Cancel
</Button>
<Button
colorScheme="forestGreen"
variant="solid"
size={"sm"}
rounded={"sm"}
isLoading={isBtnLoading}
type="submit"
>
Send
</Button>
</ModalFooter>
</Box>
)}
</ModalContent>
</Modal>
);
};
export default RequestApproveModal;

View File

@@ -0,0 +1,176 @@
import {
Box,
Button,
FormControl,
FormHelperText,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useEffect, useState } from "react";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
import ToastBox from "../../../../Components/ToastBox";
import { useApproveDistributionMutation, useApproveExitMutation, useApproveIOCaseMutation, useApproveIONavMutation } from "../../../../Services/io.service";
export const conformModalSchema = yup.object().shape({
// comments: yup.string().required("Comment is required")
// .max(50, "Investment name cannot be more than 50 characters"),
comments: yup
.string()
.required("Comment is required")
.max(200, "Approve Comment cannot be more than 200 characters"),
});
const RequestApproveModal = ({ isOpen, onClose, firstField ,id}) => {
const [isBtnLoading , setIsBtnLoading] = useState(false)
const toast = useToast()
const {
register,
reset,
watch,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
const [ approveExit ] = useApproveExitMutation()
const onSubmit = async(data) => {
setIsBtnLoading(true)
try {
const res = await approveExit({data,id})
if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsBtnLoading(false)
}else if(res?.data){
toast({
render: () => (
<ToastBox message={res?.data?.message} />
),
});
onClose()
setIsBtnLoading(false)
}else{
toast({
render: () => (
<ToastBox status={'error'} message={"Something went wrong"} />
),
});
setIsBtnLoading(false)
}
} catch (error) {
}
};
const handleFileChange = (event) => {
const selectedFile = event.target.files[0];
setFile(selectedFile);
};
const { data, isLoading } =
(id, {
skip: !id,
});
useEffect(() => {
if (data) {
reset({
investorAmount: data?.data?.investorAmount,
});
}
}, [data, reset]);
const heandleOnClose = () =>{
reset()
onClose()
}
return (
<Modal isCentered isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
<ModalOverlay />
<ModalContent pb={4}>
<ModalHeader fontSize={"md"}>Approve Comment</ModalHeader>
<ModalCloseButton />
{isLoading ? (
<FullscreenLoaders height={"50vh"} />
) : (
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea
rows={6}
focusBorderColor="green.400"
name="comments"
{...register("comments")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your checker comment...."}
rounded={"md"}
resize={"none"}
maxLength={200}
/>
{errors.comments && (
<Text fontSize="xs" color="red">
{errors.comments.message}
</Text>
)}
<FormHelperText fontSize="xs" color="gray.500">
<Box as="span" me={1}>Maximum length should be 200 characters. You have entered</Box>
{watch("comments")?.length || 0} characters.
</FormHelperText>
</FormControl>
</ModalBody>
<ModalFooter>
<Button
colorScheme="gray"
mr={3}
onClick={onClose}
size={"sm"}
rounded={"sm"}
>
Cancel
</Button>
<Button
colorScheme="forestGreen"
variant="solid"
size={"sm"}
rounded={"sm"}
isLoading={isBtnLoading}
type="submit"
>
Send
</Button>
</ModalFooter>
</Box>
)}
</ModalContent>
</Modal>
);
};
export default RequestApproveModal;

View File

@@ -0,0 +1,176 @@
import {
Box,
Button,
FormControl,
FormHelperText,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useEffect, useState } from "react";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
import ToastBox from "../../../../Components/ToastBox";
import { useRejectIOCaseMutation } from "../../../../Services/io.service";
export const conformModalSchema = yup.object().shape({
comments: yup.string().required("Comment is required")
.max(200, "Approve Comment cannot be more than 200 characters"),
});
const RequestRejectModal = ({ isOpen, onClose, firstField ,id, onBigModalClose}) => {
const [isBtnLoading , setIsBtnLoading] = useState(false)
const toast = useToast()
const {
register,
reset,
watch,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
const [ rejectIOCase ] = useRejectIOCaseMutation()
const handleFileChange = (event) => {
const selectedFile = event.target.files[0];
setFile(selectedFile);
};
const { data, isLoading } =
(id, {
skip: !id,
});
console.log("============data",data);
const onSubmit = async(data) => {
console.log(data, "tewxttttt");
setIsBtnLoading(true)
try {
const res = await rejectIOCase({data, id})
if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsBtnLoading(false)
}else if(res?.data){
toast({
render: () => (
<ToastBox message={res?.data?.message} />
),
});
onClose()
onBigModalClose()
setIsBtnLoading(false)
}else{
toast({
render: () => (
<ToastBox status={'error'} message={"Something went wrong"} />
),
});
setIsBtnLoading(false)
}
} catch (error) {
}
};
// useEffect(() => {
// if (data) {
// reset({
// investorAmount: data?.data?.investorAmount,
// });
// }
// }, [data, reset]);
const heandleOnClose = () =>{
reset()
onClose()
}
return (
<Modal isCentered isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
<ModalOverlay />
<ModalContent pb={4}>
<ModalHeader fontSize={"md"}>Reject Comment</ModalHeader>
<ModalCloseButton />
{isLoading ? (
<FullscreenLoaders height={"50vh"} />
) : (
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea
rows={6}
focusBorderColor="green.400"
name="comments"
{...register("comments")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your comments...."}
rounded={"md"}
resize={"none"}
/>
{errors.comments && (
<Text fontSize="xs" color="red">
{errors.comments.message}
</Text>
)}
<FormHelperText fontSize="xs" color="gray.500">
<Box as="span" me={1}>Maximum length should be 200 characters. You have entered</Box>
{watch("comments")?.length || 0} characters.
</FormHelperText>
</FormControl>
</ModalBody>
<ModalFooter>
<Button
colorScheme="gray"
mr={3}
onClick={onClose}
size={"xs"}
rounded={"sm"}
>
Cancel
</Button>
<Button
colorScheme="forestGreen"
variant="solid"
size={"xs"}
rounded={"sm"}
isLoading={isBtnLoading}
type="submit"
>
Send
</Button>
</ModalFooter>
</Box>
)}
</ModalContent>
</Modal>
);
};
export default RequestRejectModal;

View File

@@ -0,0 +1,293 @@
import React, { useContext, useEffect, useState } from "react";
import {
Box,
Button,
FormControl,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import { Controller, useForm } from "react-hook-form";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import { useParams } from "react-router-dom";
import { useAmountIvestmentMutation } from "../../../../Services/io.service";
import ToastBox from "../../../../Components/ToastBox";
import CurrencyInput from "../../../../Components/CurrencyInput";
import RequestRejectModal from "./RequestRejectModal";
import ApproveInvestedModal from "./ApproveInvestedModal";
import { formatDate } from "../../../Master/Sponser/Sponsers";
// Validation schema
const validationSchema = yup.object().shape({
transactionDate: yup.date().required("Date is required"),
Total_Amount: yup.number().required("Amount is required"),
amountInvested: yup.number().required("Amount to invest is required"),
IoCash: yup
.number()
.positive("IO Cash must be positive")
.required("IO Cash is required"),
});
// Function to format currency
const formatCurrency = (value) => {
if (isNaN(value)) return "";
const formatted = parseFloat(value).toFixed(2).toString();
const [integer, decimal] = formatted.split(".");
const formattedInteger = integer.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
return decimal ? `${formattedInteger}.${decimal}` : formattedInteger;
};
const ViewAmountInvested = ({ isOpen, onClose, id: investorId }) => {
const params = useParams();
const toast = useToast();
const id = params?.id;
const {
control,
register,
handleSubmit,
reset,
watch,
formState: { errors },
} = useForm({
resolver: yupResolver(validationSchema),
});
const [isLoading, setIsLoading] = useState(false);
const { IODetails } = useContext(GlobalStateContext);
const [amountInvested] = useAmountIvestmentMutation();
const [actionId, setActionId] = useState(false);
const {
isOpen: isConfirmOpen,
onOpen: onConfirmOpen,
onClose: onConfirmClose,
} = useDisclosure();
const {
isOpen: isRejectOpen,
onOpen: onRejectOpen,
onClose: onRejectClose,
} = useDisclosure();
useEffect(() => {
if (IODetails?.totalAmtInvestmentInUSD) {
const totalAmount = parseFloat(IODetails.totalAmtInvestmentInUSD);
const ioCashUpdate = parseFloat(IODetails.totalAmtInvestmentInUSD);
reset({
Total_Amount: totalAmount,
IoCash: ioCashUpdate,
});
}
}, [IODetails, reset]);
const onSubmit = async (data) => {
console.log(data);
setIsLoading(true);
try {
const res = await amountInvested({ data, id });
console.log(res);
if (res?.data?.statusCode === 200) {
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
setIsLoading(false);
onClose();
} else if (res?.error?.status === 400) {
toast({
render: () => (
<ToastBox message={res?.error?.data?.message} status={"error"} />
),
});
setIsLoading(false);
}
} catch (error) {
setIsLoading(false);
}
};
// const formatDate = (date) => new Date(date).toLocaleDateString();
const handleAmountChange = (e) => {
// e might be an object or just a value, handle both cases
const amount =
typeof e === "object" && e.target
? parseFloat(e.target.value) || 0
: parseFloat(e) || 0;
const totalAmount = parseFloat(IODetails?.totalAmtInvestmentInUSD) || 0;
const ioCash = (totalAmount - amount).toFixed(2);
reset({
amountInvested: parseFloat(amount),
IoCash: parseFloat(ioCash),
Total_Amount: IODetails?.totalAmtInvestmentInUSD,
});
};
console.log(
"=========hitttt",
IODetails?.ioTransactionRecords?.Pending?.[0]?.createdAt
);
return (
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader fontSize={"md"}>Amount Invested</ModalHeader>
<ModalCloseButton />
<ModalBody>
<form onSubmit={handleSubmit(onSubmit)}>
<FormControl
mb={"15px"}
isInvalid={!!errors.transactionDate}
isRequired
>
<FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>
Date
</FormLabel>
<Input
type="text"
value={formatDate(
IODetails?.ioTransactionRecords?.Pending?.[0]?.createdAt
)}
// value={IODetails?.ioTransactionRecords?.Pending?.[0]?.createdAt}
size="sm"
rounded={"sm"}
textAlign={"end"}
focusBorderColor="forestGreen.300"
fontSize={"sm"}
readOnly
/>
{/* <Text>
{IODetails?.ioTransactionRecords?.Approved?.[0]?.createdAt} dccd
</Text> */}
</FormControl>
<FormControl
mb={"15px"}
isInvalid={!!errors.Total_Amount}
isReadOnly
>
<FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>
Amount
</FormLabel>
<Input
type="text"
value={formatCurrency(watch("Total_Amount") || 0)}
size="sm"
rounded={"sm"}
textAlign={"end"}
focusBorderColor="forestGreen.300"
fontSize={"sm"}
readOnly
/>
</FormControl>
<FormControl
mb={"15px"}
isInvalid={!!errors.amountInvested}
isRequired
>
<FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>
Amount to invest
</FormLabel>
<Input
type="text"
value={formatCurrency(
IODetails?.ioTransactionRecords?.Pending?.[0]
?.transactionAmount || 0
)}
size="sm"
rounded={"sm"}
textAlign={"end"}
focusBorderColor="forestGreen.300"
fontSize={"sm"}
readOnly
/>
</FormControl>
<FormControl mb={"15px"} isInvalid={!!errors.IoCash}>
<FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>
IO Cash
</FormLabel>
<Input
type="text"
value={formatCurrency(
(watch("Total_Amount") || 0) -
(IODetails?.ioTransactionRecords?.Pending?.[0]
?.transactionAmount || 0)
)}
size="sm"
rounded={"sm"}
focusBorderColor="forestGreen.300"
fontSize={"sm"}
textAlign={"right"}
readOnly
/>
</FormControl>
{localStorage?.getItem("role") !== "Maker" && <ModalFooter>
<Box display={"flex"} justifyContent={"center"} gap={2}>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onConfirmOpen();
}}
colorScheme="forestGreen"
variant={"solid"}
cursor={"pointer"}
>
Approve
</Button>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onRejectOpen();
}}
>
Reject
</Button>
</Box>
</ModalFooter>}
</form>
</ModalBody>
</ModalContent>
<ApproveInvestedModal
isOpen={isConfirmOpen}
onClose={onConfirmClose}
onBigModalClose={onClose}
id={investorId}
/>
<RequestRejectModal
isOpen={isRejectOpen}
onClose={onRejectClose}
onBigModalClose={onClose}
id={investorId}
/>
</Modal>
);
};
export default ViewAmountInvested;

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